前回シーケンスを作りましたがその情報を確認できるようにします。
シーケンスの構造
保持するSequenceオブジェクトは次のような構造になっています。
- シーケンスは複数のトラックTrackオブジェクトを保持する。
- トラックは複数のイベントMidiEventを保持する。
- イベントは一つのメッセージMidiMessageと時間情報をtickで保持する。
- メッセージにはショートメッセージShortMessage、メタメッセージMetaMessage、システムエクスクルーシブメッセージSysexMessageの三種類がある。
一番下部の要素から出力処理を実装していきます。
ショートメッセージの出力
https://docs.oracle.com/javase/jp/8/docs/api/javax/sound/midi/ShortMessage.html
ショートメッセージはコマンドとチャンネルと二つのデータを保持します。以下のように出力するようにします。コマンドは意味がわかるようにswitch文を使って文字列に変換しています。ショートメッセージはデータが二つと決まっているのでチャンネルと合わせて出力しています。
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 + " datta : " + message.getData1() + ", " + message.getData2());
}
メタメッセージの出力
https://docs.oracle.com/javase/jp/8/docs/api/javax/sound/midi/MetaMessage.html
メタメッセージは定数がないのでとりあえずtypeだけ出力します。シーケンスの情報として色々使われていることが判明した時に追加で実装していくことにします。
public static void printMetaMessage(MetaMessage message, String name, PrintStream out){
out.println(name + " type : " + message.getType());
}
イベントの出力
https://docs.oracle.com/javase/jp/8/docs/api/javax/sound/midi/MidiEvent.html
イベントの時間位置を出力したあとメッセージを出力します。メッセージはinstanceofを使ってどのサブクラスか判定します。ショートメッセージあるいはメタメッセージの場合はキャストつまり型変換してそれぞれの出力メソッドを利用します。
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");
}
}
トラックの出力
https://docs.oracle.com/javase/jp/8/docs/api/javax/sound/midi/Track.html
トラックの長さと保持するイベントの数を出力したあとfor文でイベントをすべて出力します。
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);
}
}
シーケンスの出力
https://docs.oracle.com/javase/jp/8/docs/api/javax/sound/midi/Sequence.html
最後にシーケンスを出力します。タイミング形式divisionTypeは文字列で出力しています。その後トラックをfor文で全て出力していきます。
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);
}
}
次回実際のシーケンスを使って出力します。