シーケンサと対比するのはレシーバである。レシーバではリアルタイムに指定したノートを送信できる。例えばユーザーの入力に合わせて音を鳴らしたいときにはレシーバを使う。
それに対してシーケンサはあらかじめ作成したノートの集合、つまり曲が記録されている。シーケンサではスタートするだけですべてのノートが再生される。レシーバと違いコードの中で再生のタイミングを図ったり、繰り返し同じノートの動作を命令する必要がない。
javax.sound APIではmidiシーケンサはMidiSystem.getSequencer()で取得できる。
シーケンサにおける時間単位はチックまたはマイクロ秒単位になる。
シーケンサにはノートのon/offを記録する。このときティック単位で時間を表す。例えば時間0でonにするならば、ティック=0でonとする。時間480でoffにするのならばティック=480でoffにする。ティックはシーケンサにおける時間単位になるが実際の絶対時間を示しているわけではない。
シーケンサにはPPQ: pulse per quater note, 1ティックが四分音符に対応するティック数を指定する。ティックが大きくなればテンポが遅くなるし、小さくなればテンポが速くなる。ただしシーケンサの中で1つのPPQしか取れない。
ティックに対する時間はマイクロ秒で指定することができる。四分音符 -> ティック数 -> 秒数という関係になる。四分音符自体には秒数が関連付けられていない。これによりマイクロ秒/ティックを変更するだけでテンポを操作できる。この単位はトラック単位で指定できる。
ノートを記録するのはトラックと呼ばれる。トラックは複数シーケンサに記録できるのでシーケンサでトラックを選ぶことで異なった曲を選択できる。
midiシーケンサを使って連続的に音を出してみた。
package midi06; import javax.sound.midi.*; import java.util.*; public class midi06main { public static void main(String[] args) throws Exception { /* 69, 74, 76, 79 */ int[] notenums = { 69, 74, 76, 79 }; Sequencer sequencer = null; try { sequencer = MidiSystem.getSequencer(); sequencer.open(); Sequence seq = new Sequence(Sequence.PPQ, 240); Track track = seq.createTrack(); MetaMessage tempo = new MetaMessage(); tempo.setMessage(0x51, new byte[] {0x07, (byte)0xa1, 0x20}, 3); track.add(new MidiEvent(tempo, 0)); /* for loopにする前のコード int duration = 0; int buffer = 120; ShortMessage note69on = new ShortMessage(); note69on.setMessage(ShortMessage.NOTE_ON, 69, 127); track.add(new MidiEvent(note69on, 0)); ShortMessage note69off = new ShortMessage(); note69off.setMessage(ShortMessage.NOTE_OFF, 69, 0); track.add(new MidiEvent(note69off, duration += buffer)); ShortMessage note74on = new ShortMessage(); note74on.setMessage(ShortMessage.NOTE_ON, 74, 127); track.add(new MidiEvent(note74on, duration += buffer)); ShortMessage note74off = new ShortMessage(); note74off.setMessage(ShortMessage.NOTE_OFF, 74, 0); track.add(new MidiEvent(note74off, duration += buffer)); ShortMessage note76on = new ShortMessage(); note76on.setMessage(ShortMessage.NOTE_ON, 76, 127); track.add(new MidiEvent(note76on, duration += buffer)); ShortMessage note76off = new ShortMessage(); note76off.setMessage(ShortMessage.NOTE_OFF, 76, 0); track.add(new MidiEvent(note76off, duration += buffer)); ShortMessage note79on = new ShortMessage(); note79on.setMessage(ShortMessage.NOTE_ON, 79, 127); track.add(new MidiEvent(note79on, duration += buffer)); ShortMessage note79off = new ShortMessage(); note79off.setMessage(ShortMessage.NOTE_OFF, 79, 0); track.add(new MidiEvent(note79off, duration += buffer)); */ int duration = 0; int buffer = 480; int index = 0; ShortMessage[] notes = new ShortMessage[notenums.length * 2]; for(int num : notenums) { notes[index] = new ShortMessage(); notes[index].setMessage(ShortMessage.NOTE_ON, num, 127); track.add(new MidiEvent(notes[index], duration)); duration += buffer; index++; notes[index] = new ShortMessage(); notes[index].setMessage(ShortMessage.NOTE_OFF, num, 0); track.add(new MidiEvent(notes[index], duration)); duration += buffer; index++; } sequencer.setSequence(seq); sequencer.start(); while (sequencer.isRunning()) Thread.sleep(10000); } finally { if (sequencer != null && sequencer.isOpen()) sequencer.close(); } } }