/*
* Copyright (C) 2013 たんらる
*/
package fourthline.mmlTools;
import fourthline.mmlTools.core.MMLTicks;
import fourthline.mmlTools.core.MMLTokenizer;
import fourthline.mmlTools.core.MMLTools;
import fourthline.mmlTools.core.MelodyParser;
import fourthline.mmlTools.core.UndefinedTickException;
/**
* ドラムの和音分割
* 2011/12/15からマビさんの仕様が変わりました. (和音1、和音2が使えません)
* @author たんらる
*/
public class DrumTools extends MMLTools {
private boolean makiOption = false;
public DrumTools(String mml) {
super(mml);
}
public double getOverSec() throws UndefinedTickException {
return (getMabinogiTime() - getPlayTime());
}
public void setMakiOption(boolean value) {
makiOption = value;
}
/**
* メロディーパートを指定のランクの文字数でカットする(打楽器分割用)
* @param cutRank
*/
private void cutMMLDrum(ComposeRank cutRank) {
MMLCutter cutter = new MMLCutter(mml_melody);
mml_melody = cutter.cut(cutRank.getMelody(), makiOption);
mml_chord1 = cutter.cut(cutRank.getChord1(), makiOption);
mml_chord2 = cutter.cut(cutRank.getChord2()+1000, false);
}
/**
* ドラムの和音分割で、和音2を曲の長さ分以上になるように休符を挿入する(打楽器分割用)
* ほしいもの: 編集後のChord2とOversec
* @throws UndefinedTickException
*/
private void drumChord2InsertPlaylength(boolean minText) throws UndefinedTickException {
// total length - chord2 timing value
double total_length = getPlayTime(); // 分割前の演奏時間を計算しておく
double pre_chord2_length = chord2Parser.getPlayLengthByTempoList();
double sub_length = total_length - pre_chord2_length;
// add R to chord2
double beats = (sub_length*96.0) / MMLTicks.getTick("1.");
beats *= (32.0/60.0); // tempo / 60
StringBuffer sb = new StringBuffer(mml_chord2);
if (chord2Parser.getTempo() != 32) {
sb.append("t32");
}
if (!chord2Parser.getMmlL().equals("1.")) {
sb.append("l1.");
}
for (int i = 1; i < beats; i++) {
sb.append('r');
}
if (!minText) { // 終了調整モード
sb.append(tailLength(beats));
} else { // 文字数最小モード
sb.append('r');
}
System.out.println("beats: "+beats);
mml_chord2 = sb.toString();
chord2Parser = new MelodyParser(mml_chord2, "4", chord1Parser.getTempo());
}
/**
* OverTime分を少なくするコード
* @param time
* @return
*/
private String tailLength(double time) {
String tickCandidate[] = {
// "1", "2", "3", "4", "5", "6", "7", "8", "12", "16", "32"
"1", "2", "4", "8", "12", "16", "32"
};
double overTime = time - Math.floor(time);
String result = "";
try {
for (int i = 0; i < tickCandidate.length; i++) {
double overFollow = MMLTicks.getTick(tickCandidate[i])/51.20/11.25;
if (overFollow < overTime) {
overTime -= overFollow;
result += "r" + tickCandidate[i];
}
} // for
} catch (UndefinedTickException e) {
return "<Internal_ERROR>";
}
return result;
}
public String makeForMabiMML(ComposeRank cutRank) throws UndefinedTickException {
return makeForMabiMML(cutRank, true);
}
/**
* 指定ランクで、ドラムのメロディパートを和音に分割する
* @param cutRank 分割するランク
* @param minText 終端モード falseの場合、時間最適。trueの場合、文字数最小。
* @return 分割後のMML
* @throws UndefinedTickException
*/
public String makeForMabiMML(ComposeRank cutRank, boolean minText) throws UndefinedTickException {
cutMMLDrum(cutRank);
parseMMLforMabinogi();
parsePlayMode(true);
if (mml_chord1 != "") {
// 分割した場合、MMLを編集するため、再計算されるようにする
clearTimeCalc();
drumChord2InsertPlaylength(minText);
}
return getMML();
}
/**
* 打楽器音量調節テーブル
* @param vol
* @return
*/
int convDrumVol(int vol) {
int table[] = {
0, 1, 1, 2, 3, 4, 4, 5, 6, 7, 7, 8, 9, 10, 10, 11
};
if ( (vol < 0) || (vol >= table.length) ) {
return 0;
}
return table[vol];
}
/**
* ボリュームを 2/3 にする。
* 高速に処理するため、独自実装。
* 分割前に実行すること。
* @throws UndefinedTickException
*/
public String disDrumVolumn() throws UndefinedTickException {
MMLTokenizer mt = new MMLTokenizer(mml_melody);
StringBuffer sb = new StringBuffer(mml_melody.length());
while (mt.hasNext()) {
String item = mt.next();
if (item.startsWith("v") || item.startsWith("V")) {
String vol = item.substring(1);
int volInt = Integer.parseInt(vol);
volInt = convDrumVol(volInt);
sb.append(item.charAt(0));
sb.append(volInt);
} else {
sb.append(item);
}
}
String result = sb.toString();
mml_melody = result;
return result;
}
}
/**
* MMLを分割する(打楽器分割用)
*
*/
class MMLCutter {
private String mml_src;
public MMLCutter(String mml) {
mml_src = mml;
}
private int alignMaki(int cut) {
MMLTokenizer tokenizer = new MMLTokenizer(mml_src);
while (cut >= 0) {
int backIndex = tokenizer.backSearchToken(cut);
if (backIndex < 0) {
return -1;
} else if ( MMLTokenizer.isNote( mml_src.charAt(backIndex)) ) {
break;
} else {
cut = backIndex;
}
}
return cut;
}
public String cut(int cut, boolean maki) {
if (mml_src.length() <= cut) {
String result = mml_src;
mml_src = "";
return result;
}
while (!MMLTokenizer.isToken(mml_src.charAt(cut))) {
cut--;
}
if (maki) {
cut = alignMaki(cut);
}
String result = mml_src.substring(0, cut);
String strL = "";
if (maki) {
try {
MelodyParser parser = new MelodyParser(result);
String lastL = parser.getMmlL();
if ( !lastL.equals("4") ) { // not if 4 then, next part start L token
strL = "l" + lastL;
}
} catch (UndefinedTickException e) {}
}
mml_src = mml_src.substring(cut);
if ( (mml_src.charAt(0) != 'l') && (mml_src.charAt(0) != 'L') ) {
mml_src = strL + mml_src;
}
return result;
}
}