前回からの続きです。楽曲での拍子をプログラム上で保持するようにします。その次に小節と拍を指定すると曲の開始からの位置を拍のみで取得できるようにします。
拍子管理クラス
拍子記号は英語でtime signatureと言います。クラス名は少々略してTimeSignとします。鍵の順序を保持するTreeMapを継承します。小節を鍵としてIntegerで、拍を値としてDoubleで持つようにします。
package mocha.sound;
import java.util.TreeMap;
public class TimeSign extends TreeMap<Integer, Double> {
public TimeSign(double initial) {
put(0, initial);
}
}
拍子の管理
次にこのクラスに小節と拍を指定した時に拍のみで位置を返すメソッドを追加します。
public double getTotalBeat(int measure, double beat) {
まず指定の小節measureが負数の場合は処理を中断します。負数の場合にはその小節と同じか少ない値の鍵は存在しないからです。
if (floorEntry(measure) == null) {
throw new IllegalArgumentException("measure is too small:" + measure);
}
また拍数が大きすぎてその小節に収まりきれない場合は小節を1増やし拍数をその時の拍子の分差し引いて再度このメソッドを呼び直します。
double floorSign = floorEntry(measure).getValue();
if (beat > floorSign) {
return getTotalBeat(measure + 1, beat - floorSign);
}
ここから下は通常の計算をします。まず引数で指定された小節より前の情報を一括取得します。TreeMapのheadMapメソッドを利用します。このとき2つ目の引数にtrueを指定すると指定の小節と等しい鍵があればそれも含めたマップを返します。
NavigableMap<Integer, Double> headMap = headMap(measure, true);
取得したマップのエントリ分繰り返します。変数として現在の小節、現在の拍子、戻り値を定義します。
int currentMeasure = 0;
double currentSign = headMap.get(0);
double totalBeat = 0;
for (Integer nextMeasure : headMap.keySet()) {
現在の拍子記号を元に次の拍子記号のある小節までの拍数を足します。
totalBeat += (double) (nextMeasure - currentMeasure) * currentSign;
次の処理のために現在の小節、現在の拍子を更新します。
currentMeasure = nextMeasure; currentSign = headMap.get(nextMeasure);
繰り返しが終わったあとで残りの計算をします。
totalBeat += (double) (measure - currentMeasure) * currentSign; return totalBeat + beat;
動作確認
前回のユースケース通りに動くか確認します。
| 小節 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| 拍子設定 | 4/4 | 3/4 | 3/8 | ||||||||
| 位置(拍) | 0 | 4 | 8 | 12 | 16 | 19 | 22 | 25 | 28 | 29.5 | 31 |
確認用の処理を書きます。
TimeSign ts = new TimeSign(4);
ts.put(4, 3.0);
ts.put(8, 1.5);
for(int i = 0;i <= 10;i++){
System.out.println("measure=" + i + ", total beat=" + ts.getTotalBeat(i, 0));
}
動かしたところユースケースの想定と一致しました。
measure=0, total beat=0.0 measure=1, total beat=4.0 measure=2, total beat=8.0 measure=3, total beat=12.0 measure=4, total beat=16.0 measure=5, total beat=19.0 measure=6, total beat=22.0 measure=7, total beat=25.0 measure=8, total beat=28.0 measure=9, total beat=29.5 measure=10, total beat=31.0
ソース
今回作ったソースです。
package mocha.sound;
import java.util.NavigableMap;
import java.util.TreeMap;
public class TimeSign extends TreeMap<Integer, Double> {
public TimeSign(double initial) {
put(0, initial);
}
public double getTotalBeat(int measure, double beat) {
if (floorEntry(measure) == null) {
throw new IllegalArgumentException("measure is too small:" + measure);
}
double floorSign = floorEntry(measure).getValue();
if (beat > floorSign) {
return getTotalBeat(measure + 1, beat - floorSign);
}
NavigableMap<Integer, Double> headMap = headMap(measure, true);
int currentMeasure = 0;
double currentSign = headMap.get(0);
double totalBeat = 0;
for (Integer nextMeasure : headMap.keySet()) {
totalBeat += (double) (nextMeasure - currentMeasure) * currentSign;
currentMeasure = nextMeasure;
currentSign = headMap.get(nextMeasure);
}
totalBeat += (double) (measure - currentMeasure) * currentSign;
return totalBeat + beat;
}
}
次回はここから時間を取得する処理を作ります。