動的に演奏を生成するクラスSequencerManagerの使い方を解説する。
SequencerManagerはSequencerの再生を監視し、小節の変更時に登録オブジェクトのコールバックメソッドを呼び出すクラスである。
SequenceGeneratableを実装したクラスはSequencerManagerに登録することができ、実装したメソッドが小節の変更時に呼び出される。SequenceGeneratableは2つのメソッドを提供する。sendInitializingMessagesはSequencerManagerの演奏が開始したときに呼び出される。
public void sendInitializingMessages(Receiver r) { ShortMessage sm = new ShortMessage(); try { sm.setMessage(ShortMessage.SYSTEM_RESET); } catch (Exception e) { e.printStackTrace(); } r.send(sm, 0); }
引数には再生用デバイスのRecieverが渡される。ここでは、デバイスの状態を初期化するメッセージを送信する。
小節が変わったときの処理はchangeMeasureに記述する。
public boolean changeMeasure(Track track, long measureTick) { try { ShortMessage on = new ShortMessage(); on.setMessage(ShortMessage.NOTE_ON, 2, 60, 127); ShortMessage off = new ShortMessage(); off.setMessage(ShortMessage.NOTE_OFF, 2, 60, 0); track.add(new MidiEvent(on, measureTick)); track.add(new MidiEvent(off, measureTick + 480)); } catch (InvalidMidiDataException e) { e.printStackTrace(); } return true; }
引数にはそれぞれメッセージ追加用のTrackオブジェクトと、小節の開始tickが渡される。戻り値は処理の継続を表し、trueだと続行し、falseだとSequencerManagerの登録オブジェクトから外れる。ここでは小節の先頭で単音を生成し、処理を続行させる。
(※)ここでいう小節の変更時とは、厳密には小節変更の一拍前である。なので、小節開始ちょうどの時刻にイベントを指定しても無視される事は基本的に無い。ただし、登録している他のGeneratorがchangeMeasureメッソドで時間のかかる処理を行った場合、小節が始まってからメソッドが呼び出されてしまう可能性がある。なるべくchangeMeasureメソッドでは時間のかかる処理をしないようにする。
作ったSequenceGeneratableはSequencerManagerに登録して使う。
import jp.crestmuse.cmx.sound.SequencerManager; public class CMXTutorial4 { public static void main(String[] args) { try { SequencerManager sm = new SequencerManager(); NoteGen noteGen = new NoteGen(); sm.addGeneratable(noteGen); sm.start(); System.in.read(); sm.stop(); } catch (Exception e) { e.printStackTrace(); } System.exit(0); } }
SequencerManagerのコンストラクタを省いた場合デフォルトのRecieverが渡される。addGeneratableで登録するオブジェクトを指定する。
import javax.sound.midi.InvalidMidiDataException; import javax.sound.midi.MidiEvent; import javax.sound.midi.Receiver; import javax.sound.midi.ShortMessage; import javax.sound.midi.Track; import jp.crestmuse.cmx.sound.SequenceGeneratable; public class NoteGen implements SequenceGeneratable { public boolean changeMeasure(Track track, long measureTick) { try { ShortMessage on = new ShortMessage(); on.setMessage(ShortMessage.NOTE_ON, 2, 60, 127); ShortMessage off = new ShortMessage(); off.setMessage(ShortMessage.NOTE_OFF, 2, 60, 0); track.add(new MidiEvent(on, measureTick)); track.add(new MidiEvent(off, measureTick + 480)); } catch (InvalidMidiDataException e) { e.printStackTrace(); } return true; } public void sendInitializingMessages(Receiver r) { ShortMessage sm = new ShortMessage(); try { sm.setMessage(ShortMessage.SYSTEM_RESET); } catch (Exception e) { e.printStackTrace(); } r.send(sm, 0); } }
import jp.crestmuse.cmx.sound.SequencerManager; public class CMXTutorial4 { public static void main(String[] args) { try { SequencerManager sm = new SequencerManager(); NoteGen noteGen = new NoteGen(); sm.addGeneratable(noteGen); sm.start(); System.in.read(); sm.stop(); } catch (Exception e) { e.printStackTrace(); } System.exit(0); } }