前回音階の仕組みをみたところでこれをクラスにしたいと思います。
音階クラスの要件
まず音階クラスとは何かを決めたいと思います。
- 音程差の配列をもとにインスタンスを作る
- 必要な情報を与えるとMIDIノート番号を返す。
必要な情報は以下のように考えました。
- 基準となるMIDIノート番号
- オクターブのオフセット
- 音程
基準となるMIDIノート番号は例えば中央Cから始まるスケールの場合は60とします。オクターブのオフセットは通常0ですが上や下のオクターブ領域になる場合に調整するためのものです。音程は音程差の配列ののインデックスになります。音階を1オクターブ上まで順番に弾くには以下のような設定値になります。
1. 基準 | 60 | 60 | 60 | 60 | 60 | 60 | 60 | 60 |
---|---|---|---|---|---|---|---|---|
2. オクターブ | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
3. 音程 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 0 |
出力ノート番号 | 60 | 62 | 64 | 65 | 67 | 69 | 71 | 72 |
また入力する音程は配列のインデックスではありますが配列の長さ以上の値や0未満の値の場合自動でオクターブ上や下のノート番号を返すものとします。MIDIノート番号60は以下のような場合でも取得できるものとします。
1. 基準 | 60 | 60 | 60 | 60 | 60 | 72 |
---|---|---|---|---|---|---|
2. オクターブ | -2 | -1 | 0 | 1 | 2 | -1 |
3. 音程 | 14 | 7 | 0 | -7 | -14 | 0 |
出力ノート番号 | 60 | 60 | 60 | 60 | 60 | 60 |
実装
では実装していきます。コンストラクターは配列を受け取って保持するだけです。
public class Scale { int[] intervals; public Scale(int... intervals){ this.intervals = intervals; }
配列の長さを返すメソッドを用意します。
public int length(){ return intervals.length; }
最後にノート番号を返すメソッドを作ります。
public int note(int base, int octave, int index){
indexの正負で場合分けします。正の場合は配列をはみ出さないように調節します。
octave += index / intervals.length; index %= intervals.length;
負の場合は少々複雑です。まずindexが何オクターブ下になるのか計算します。
int offset = (index + 1) / intervals.length - 1;
次にindexを正の数にしてから剰余をとります。
index += Math.abs(offset) * intervals.length; index %= intervals.length;
最後に正負両方の場合で同じようにノート番号を計算して返します。
return base + octave * 12 + intervals[index];
最後にスケールを表示してみましょう。
Scale scale = new Scale(0, 2, 4, 5, 7, 9, 11); for(int i = 0;i < 8;i++){ System.out.println(scale.note(60, 0, i)); }
出力結果は以下のようになりました。
60 62 64 65 67 69 71 72
ソース
以下今回作ったソースです。次回は曲の打ち込みをします。
/* * mocha-java.com * */ package mocha.sound.note; /** * * @author minaberger */ public class Scale { int[] intervals; public Scale(int... intervals) { this.intervals = intervals; } public int length() { return intervals.length; } public int note(int base, int octave, int index) { if (index >= 0) { octave += index / intervals.length; index %= intervals.length; } else { int offset = (index + 1) / intervals.length - 1; octave += offset; index += Math.abs(offset) * intervals.length; index %= intervals.length; } return base + octave * 12 + intervals[index]; } }