前回シーケンスの内容を出力するメソッドを作ったのでこれを利用したいと思います。
出力先について
前回のメソッドでは引数にPrintStreamを指定していました。通常一曲のシーケンスは数百数千のメッセージでできているため標準出力に出力すると初めの情報は流れてしまい全て確認するのが難しいことが想定されます。PrintStreamにSystem.outを指定すれば標準出力になりますが以下のように初期化するとファイルに出力することができます。
new PrintStream(File file);
サンプルシーケンス
以前自前で作ったシーケンスを流用します。ただし複数トラックを確認したいのでトラックを3つ作ります。
Sequence sequence = new Sequence(Sequence.PPQ, 480); Track track; track = sequence.createTrack(); track.add(new MidiEvent(new ShortMessage(ShortMessage.NOTE_ON, 0, 60, 127), 480 * 4)); track.add(new MidiEvent(new ShortMessage(ShortMessage.NOTE_ON, 0, 60, 0), 480 * 5)); track.add(new MidiEvent(new ShortMessage(ShortMessage.NOTE_ON, 0, 60, 127), 480 * 7)); track.add(new MidiEvent(new ShortMessage(ShortMessage.NOTE_ON, 0, 60, 0), 480 * 8)); track = sequence.createTrack(); track.add(new MidiEvent(new ShortMessage(ShortMessage.NOTE_ON, 0, 64, 127), 480 * 5)); track.add(new MidiEvent(new ShortMessage(ShortMessage.NOTE_ON, 0, 64, 0), 480 * 6)); track.add(new MidiEvent(new ShortMessage(ShortMessage.NOTE_ON, 0, 64, 127), 480 * 7)); track.add(new MidiEvent(new ShortMessage(ShortMessage.NOTE_ON, 0, 64, 0), 480 * 8)); track = sequence.createTrack(); track.add(new MidiEvent(new ShortMessage(ShortMessage.NOTE_ON, 0, 67, 127), 480 * 6)); track.add(new MidiEvent(new ShortMessage(ShortMessage.NOTE_ON, 0, 67, 0), 480 * 7)); track.add(new MidiEvent(new ShortMessage(ShortMessage.NOTE_ON, 0, 67, 127), 480 * 7)); track.add(new MidiEvent(new ShortMessage(ShortMessage.NOTE_ON, 0, 67, 0), 480 * 8));
シーケンスをMIDIファイルに出力する
シーケンスは簡単にMIDIファイルに出力することができます。拡張子は.midです。二つ目の引数の数字はスタンダードMIDIファイルのフォーマットのことで0はトラックが一つだけの形式、1は複数の形式です。今回はトラックが複数あるので1にします。だいたいの場合1で良いと思います。
また2の形式もあるようですがあまり使われていないようです。
MidiSystem.write(sequence, 1, new File("midi/test.mid"));
シーケンス内容をテキストに出力する
前回作った出力用のメソッドを使って出力して見ます。
SequencePrinter.printSequence(sequence, System.out);
出力した結果です。
division type : PPQ resolution : 480 length in ms : 4000000 length in tick : 3840 track-0 length in tick :3840 track-0 size :5 track-0.event-0 tick : 1920 track-0.event-0.short command : NOTE_ON track-0.event-0.short channel : 0 track-0.event-0.short data : 60, 127 track-0.event-1 tick : 2400 track-0.event-1.short command : NOTE_ON track-0.event-1.short channel : 0 track-0.event-1.short data : 60, 0 track-0.event-2 tick : 3360 track-0.event-2.short command : NOTE_ON track-0.event-2.short channel : 0 track-0.event-2.short data : 60, 127 track-0.event-3 tick : 3840 track-0.event-3.short command : NOTE_ON track-0.event-3.short channel : 0 track-0.event-3.short data : 60, 0 track-0.event-4 tick : 3840 track-0.event-4.meta type : 47 track-1 length in tick :3840 track-1 size :5 track-1.event-0 tick : 2400 track-1.event-0.short command : NOTE_ON track-1.event-0.short channel : 0 track-1.event-0.short data : 64, 127 track-1.event-1 tick : 2880 track-1.event-1.short command : NOTE_ON track-1.event-1.short channel : 0 track-1.event-1.short data : 64, 0 track-1.event-2 tick : 3360 track-1.event-2.short command : NOTE_ON track-1.event-2.short channel : 0 track-1.event-2.short data : 64, 127 track-1.event-3 tick : 3840 track-1.event-3.short command : NOTE_ON track-1.event-3.short channel : 0 track-1.event-3.short data : 64, 0 track-1.event-4 tick : 3840 track-1.event-4.meta type : 47 track-2 length in tick :3840 track-2 size :5 track-2.event-0 tick : 2880 track-2.event-0.short command : NOTE_ON track-2.event-0.short channel : 0 track-2.event-0.short data : 67, 127 track-2.event-1 tick : 3360 track-2.event-1.short command : NOTE_ON track-2.event-1.short channel : 0 track-2.event-1.short data : 67, 0 track-2.event-2 tick : 3360 track-2.event-2.short command : NOTE_ON track-2.event-2.short channel : 0 track-2.event-2.short data : 67, 127 track-2.event-3 tick : 3840 track-2.event-3.short command : NOTE_ON track-2.event-3.short channel : 0 track-2.event-3.short data : 67, 0 track-2.event-4 tick : 3840 track-2.event-4.meta type : 47
自分では入れていないのですがメタメッセージがそれぞれのトラックの最後に入っています。これはEnd of Track「トラックの終了」を意味するメタメッセージです。スタンダードMIDIファイルの仕様上各トラックの終了位置に必要なためJavaのAPIで自動で追加しているようです。
ソース
最後に出力用のソースを載せておきます。
package mocha.sound.midi;
import java.io.PrintStream;
import javax.sound.midi.MetaMessage;
import javax.sound.midi.MidiEvent;
import javax.sound.midi.MidiMessage;
import javax.sound.midi.Sequence;
import javax.sound.midi.ShortMessage;
import javax.sound.midi.SysexMessage;
import javax.sound.midi.Track;
public class SequencePrinter {
public static void printSequence(Sequence sequence, PrintStream out) {
String divisionTypeName;
if (sequence.getDivisionType() == Sequence.PPQ) {
divisionTypeName = "PPQ";
} else if (sequence.getDivisionType() == Sequence.SMPTE_24) {
divisionTypeName = "SMPTE_24";
} else if (sequence.getDivisionType() == Sequence.SMPTE_25) {
divisionTypeName = "SMPTE_25";
} else if (sequence.getDivisionType() == Sequence.SMPTE_30) {
divisionTypeName = "SMPTE_30";
} else if (sequence.getDivisionType() == Sequence.SMPTE_30DROP) {
divisionTypeName = "SMPTE_30DROP";
} else {
divisionTypeName = "unknown";
}
out.println("division type : " + divisionTypeName);
out.println("resolution : " + sequence.getResolution());
out.println("length in ms : " + sequence.getMicrosecondLength());
out.println("length in tick : " + sequence.getTickLength());
int i = 0;
for (Track track : sequence.getTracks()) {
printTrack(track, "track-" + i++, out);
}
}
public static void printTrack(Track track, String name, PrintStream out) {
out.println(name + " length in tick :" + track.ticks());
out.println(name + " size :" + track.size());
for (int i = 0; i < track.size(); i++) {
printMidiEvent(track.get(i), name + ".event-" + i, out);
}
}
public static void printMidiEvent(MidiEvent event, String name, PrintStream out) {
out.println(name + " tick : " + event.getTick());
MidiMessage message = event.getMessage();
if (message instanceof ShortMessage) {
printShortMessage((ShortMessage) message, name + ".short", out);
} else if (message instanceof MetaMessage) {
printMetaMessage((MetaMessage) message, name + ".meta", out);
} else if (message instanceof SysexMessage) {
out.println(name + " sysex message");
} else {
out.println(name + " unknown");
}
}
public static void printMetaMessage(MetaMessage message, String name, PrintStream out) {
out.println(name + " type : " + message.getType());
}
public static void printShortMessage(ShortMessage message, String name, PrintStream out) {
String command;
switch (message.getCommand()) {
case ShortMessage.ACTIVE_SENSING:
command = "ACTIVE_SENSING";
break;
case ShortMessage.CHANNEL_PRESSURE:
command = "CHANNEL_PRESSURE";
break;
case ShortMessage.CONTINUE:
command = "CONTINUE";
break;
case ShortMessage.CONTROL_CHANGE:
command = "CONTROL_CHANGE";
break;
case ShortMessage.END_OF_EXCLUSIVE:
command = "END_OF_EXCLUSIVE";
break;
case ShortMessage.MIDI_TIME_CODE:
command = "MIDI_TIME_CODE";
break;
case ShortMessage.NOTE_OFF:
command = "NOTE_OFF";
break;
case ShortMessage.NOTE_ON:
command = "NOTE_ON";
break;
case ShortMessage.PITCH_BEND:
command = "PITCH_BEND";
break;
case ShortMessage.POLY_PRESSURE:
command = "POLY_PRESSURE";
break;
case ShortMessage.PROGRAM_CHANGE:
command = "PROGRAM_CHANGE";
break;
case ShortMessage.SONG_POSITION_POINTER:
command = "SONG_POSITION_POINTER";
break;
case ShortMessage.SONG_SELECT:
command = "SONG_SELECT";
break;
case ShortMessage.START:
command = "START";
break;
case ShortMessage.STOP:
command = "STOP";
break;
case ShortMessage.SYSTEM_RESET:
command = "SYSTEM_RESET";
break;
case ShortMessage.TIMING_CLOCK:
command = "TIMING_CLOCK";
break;
case ShortMessage.TUNE_REQUEST:
command = "TUNE_REQUEST";
break;
default:
command = "unknown(" + message.getCommand() + ")";
break;
}
out.println(name + " command : " + command);
out.println(name + " channel : " + message.getChannel());
out.println(name + " data : " + message.getData1() + ", " + message.getData2());
}
}