Sequencerで再生しながらMIDIMessageを追加するときの注意

以下、import javax.sound.midi.*;


結論:Sequence.createTrackで所得したTrackオブジェクトにはデフォルトで終端記号が一つ含まれている。
これは、Trackに新しいメッセージを追加されたときに適宜伸びるので終了前に更新してしまえば止まらないが、一旦終端記号が読み出されたらSequencer全体として演奏が続いていてもそのTrackは無反応になる。


ちょっとした実験。

import javax.sound.midi.MidiEvent;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.Sequence;
import javax.sound.midi.Sequencer;
import javax.sound.midi.ShortMessage;
import javax.sound.midi.SysexMessage;
import javax.sound.midi.Track;


public class Hoge {

  public static void main(String[] args) {
    try {
      int TPB = 480;

      Sequencer seqr = MidiSystem.getSequencer();
      Sequence seq = new Sequence(Sequence.PPQ, TPB);
      seqr.setSequence(seq);

      // とりあえず2小節目まで全体は終了しない
      seq.createTrack().add(new MidiEvent(new SysexMessage(), TPB*8));

      // オンとオフ
      ShortMessage on = new ShortMessage();
      on.setMessage(ShortMessage.NOTE_ON, 0, 60, 127);
      ShortMessage off = new ShortMessage();
      off.setMessage(ShortMessage.NOTE_OFF, 0, 60, 0);

      // 1小節目の終わりにNoteOffを持ってくる
      Track track = seq.createTrack();
      track.add(new MidiEvent(on, 0));
      track.add(new MidiEvent(off, TPB*4));

      seqr.open();
      seqr.start();

      // 1秒休んで4拍目にon追加
      Thread.sleep(1000);
      track.add(new MidiEvent(on, TPB*3));
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

}

1小節目の始めと終わりにonとoffを配置して演奏開始した後、1秒後(だいたい3拍目)4拍目にonを追加する。この場合2小節目の先頭にoffメッセージがあるため、後から追加した音も鳴る。ここで、

      // 2拍目にNoteOffを持ってくる
      Track track = seq.createTrack();
      track.add(new MidiEvent(on, 0));
      track.add(new MidiEvent(off, TPB));

の様に、offを2拍目に配置する。すると、演奏開始時点での終端記号も2拍目になり、sleepが復帰した時点でtrackは終了しているので追加した音は読み込まれない。


終了前にダミーメッセージを入れるか、Track生成時点で終端記号を捕まえといてそれをいじると簡単に対処できる。