時間の管理ができるようになったところでMIDIメッセージをもっと簡単に設定できるようにしたいと思います。
現在の問題点
まず下のコードを見てください。一つの音を演奏させるためにトラックに設定する処理です。
track.add(new MidiEvent(new ShortMessage(ShortMessage.NOTE_ON, 0, 60, 127), current)); track.add(new MidiEvent(new ShortMessage(ShortMessage.NOTE_ON, 0, 60, 0), current + resolution));
音を鳴らすには音の開始と音の終了の二つのMIDIメッセージを送る必要があります。この二つを時間差をつけて設定する必要があります。これを必要な情報のみで簡単に設定できるようにしたいと思います。
実装内容の検討
音を鳴らすのに必要なのは以下の情報です。
- MIDIチャンネル
- ノート番号(どの鍵盤か?)
- ベロシティ(どのくらいの強さか?)
- 開始時間
- 終了時間
このうち開始時間については「何小節目の何拍目」という指定ができるようにしたいと思います。前回作ったMidiTimeSignクラスを利用すればこれは実現できます。
終了時間は開始時間と違い相対時間で指定したいと思います。「何小節目の何拍目」ではなく「開始時間の何拍後」という指定方法です。
MIDIチャンネルについては編集するトラックに対して固定にしたいと思います。コンストラクタでトラックとチャンネル番号を受け取ってそれを常に使い続けるようにします。
実装
では順に実装していきます。クラス名はTrackEditorとします。
コンストラクタは時間を管理するMidiTimeSignとトラックとチャンネルを引数に取りフィールド変数とします。
public class TrackEditor { MidiTimeSign timeSign; Track track; int channel; public TrackEditor(MidiTimeSign timeSign, Track track, int channel){ this.timeSign = timeSign; this.track = track; this.channel = channel; }
音の演奏時には小節と、拍、ノート番号、ベロシティ、音の長さを拍単位で引数に取るようにします。
public void play(int measure, double beat, int note, int velocity, double duration){
音の開始位置と終了位置をティック単位で取得します。終了位置の拍は開始位置の拍に拍単位の音の長さを足したものを使います。
long tickOn = timeSign.getTick(measure, beat); long tickOff = timeSign.getTick(measure, beat + duration);
トラックにメッセージを登録します。
track.add(new MidiEvent(new ShortMessage(ShortMessage.NOTE_ON, channel, note, velocity), tickOn)); track.add(new MidiEvent(new ShortMessage(ShortMessage.NOTE_ON, channel, note, 0), tickOff));
これで音を鳴らすコードの量をかなり減らすことができます。このように冗長な処理はなるべくメソッド化して楽曲に専念できるようにしていきたいと思います。
このトラックエディターについて次回はもう少し機能を追加します。