OSDN Git Service

ローカルリポジトリ作成→初コミット
[midichordhelper/MIDIChordHelper.git] / src / camidion / chordhelper / music / MIDISpec.java
1 package camidion.chordhelper.music;
2 import java.nio.charset.Charset;
3 import java.util.HashMap;
4 import java.util.Map;
5
6 import javax.sound.midi.InvalidMidiDataException;
7 import javax.sound.midi.MetaMessage;
8 import javax.sound.midi.MidiEvent;
9 import javax.sound.midi.MidiMessage;
10 import javax.sound.midi.Sequence;
11 import javax.sound.midi.ShortMessage;
12 import javax.sound.midi.SysexMessage;
13 import javax.sound.midi.Track;
14 /**
15  * MIDI仕様(システムエクスクルーシブ含む)
16  */
17 public class MIDISpec {
18         public static final int MAX_CHANNELS = 16;
19         public static final int PITCH_BEND_NONE = 8192;
20         /**
21          * メタメッセージタイプ名マップ
22          */
23         private static final Map<Integer,String>
24                 META_MESSAGE_TYPE_NAMES = new HashMap<Integer,String>() {
25                         {
26                                 put(0x00, "Seq Number");
27                                 put(0x01, "Text");
28                                 put(0x02, "Copyright");
29                                 put(0x03, "Seq/Track Name");
30                                 put(0x04, "Instrument Name");
31                                 put(0x05, "Lyric");
32                                 put(0x06, "Marker");
33                                 put(0x07, "Cue Point");
34                                 put(0x08, "Program Name");
35                                 put(0x09, "Device Name");
36                                 put(0x20, "MIDI Ch.Prefix");
37                                 put(0x21, "MIDI Output Port");
38                                 put(0x2F, "End Of Track");
39                                 put(0x51, "Tempo");
40                                 put(0x54, "SMPTE Offset");
41                                 put(0x58, "Time Signature");
42                                 put(0x59, "Key Signature");
43                                 put(0x7F, "Sequencer Specific");
44                         }
45                 };
46         /**
47          * メタメッセージタイプの名前を返します。
48          * @param metaMessageType メタメッセージタイプ
49          * @return メタメッセージタイプの名前
50          */
51         public static String getMetaName(int metaMessageType) {
52                 return META_MESSAGE_TYPE_NAMES.get(metaMessageType);
53         }
54         /**
55          * メタメッセージタイプがテキストのつくものか調べます。
56          * @param metaMessageType メタメッセージタイプ
57          * @return テキストがつくときtrue
58          */
59         public static boolean hasMetaText(int metaMessageType) {
60                 return (metaMessageType > 0 && metaMessageType < 10);
61         }
62         /**
63          * メタメッセージタイプが拍子記号か調べます。
64          * @param metaMessageType メタメッセージタイプ
65          * @return 拍子記号ならtrue
66          */
67         public static boolean isTimeSignature(int metaMessageType) {
68                 return metaMessageType == 0x58;
69         }
70         /**
71          * MIDIメッセージが拍子記号か調べます。
72          * @param msg MIDIメッセージ
73          * @return 拍子記号ならtrue
74          */
75         public static boolean isTimeSignature(MidiMessage midiMessage) {
76                 if ( !(midiMessage instanceof MetaMessage) )
77                         return false;
78                 return isTimeSignature( ((MetaMessage)midiMessage).getType() );
79         }
80         /**
81          * メタメッセージタイプが EOT (End Of Track) か調べます。
82          * @param metaMessageType メタメッセージタイプ
83          * @return EOTならtrue
84          */
85         public static boolean isEOT(int metaMessageType) {
86                 return metaMessageType == 0x2F;
87         }
88         /**
89          * MIDIメッセージが EOT (End Of Track) か調べます。
90          * @param midiMessage MIDIメッセージ
91          * @return EOTならtrue
92          */
93         public static boolean isEOT(MidiMessage midiMessage) {
94                 if ( !(midiMessage instanceof MetaMessage) )
95                         return false;
96                 return isEOT( ((MetaMessage)midiMessage).getType() );
97         }
98         /**
99          * 1分のマイクロ秒数
100          */
101         public static final int MICROSECOND_PER_MINUTE = (60 * 1000 * 1000);
102         /**
103          * MIDIのテンポメッセージについているバイト列をQPM単位のテンポに変換します。
104          * @param b バイト列
105          * @return テンポ[QPM]
106          */
107         public static int byteArrayToQpmTempo(byte[] b) {
108                 int tempoInUsPerQuarter
109                         = ((b[0] & 0xFF) << 16) | ((b[1] & 0xFF) << 8) | (b[2] & 0xFF);
110                 return MICROSECOND_PER_MINUTE / tempoInUsPerQuarter;
111         }
112         /**
113          * QPM単位のテンポをMIDIのテンポメッセージ用バイト列に変換します。
114          * @param qpm テンポ[QPM]
115          * @return MIDIのテンポメッセージ用バイト列
116          */
117         public static byte[] qpmTempoToByteArray(int qpm) {
118                 int tempoInUsPerQuarter = MICROSECOND_PER_MINUTE / qpm;
119                 byte[] b = new byte[3];
120                 b[0] = (byte)((tempoInUsPerQuarter >> 16) & 0xFF);
121                 b[1] = (byte)((tempoInUsPerQuarter >> 8) & 0xFF);
122                 b[2] = (byte)(tempoInUsPerQuarter & 0xFF);
123                 return b;
124         }
125         /**
126          * トラック名のバイト列を返します。
127          * @param track MIDIトラック
128          * @return トラック名のバイト列
129          */
130         public static byte[] getNameBytesOf(Track track) {
131                 MidiEvent midiEvent;
132                 MidiMessage message;
133                 MetaMessage metaMessage;
134                 for( int i=0; i<track.size(); i++ ) {
135                         midiEvent = track.get(i);
136                         if( midiEvent.getTick() > 0 ) { // No more event at top, try next track
137                                 break;
138                         }
139                         message = midiEvent.getMessage();
140                         if( ! (message instanceof MetaMessage) ) { // Not meta message
141                                 continue;
142                         }
143                         metaMessage = (MetaMessage)message;
144                         if( metaMessage.getType() != 0x03 ) { // Not sequence name
145                                 continue;
146                         }
147                         return metaMessage.getData();
148                 }
149                 return null;
150         }
151         /**
152          * トラック名のバイト列を設定します。
153          * @param track MIDIトラック
154          * @param name トラック名
155          * @return 成功:true、失敗:false
156          */
157         public static boolean setNameBytesOf(Track track, byte[] name) {
158                 MidiEvent midiEvent = null;
159                 MidiMessage msg = null;
160                 MetaMessage metaMsg = null;
161                 for( int i=0; i<track.size(); i++ ) {
162                         if(
163                                 (midiEvent = track.get(i)).getTick() > 0
164                                 ||
165                                 (msg = midiEvent.getMessage()) instanceof MetaMessage
166                                 &&
167                                 (metaMsg = (MetaMessage)msg).getType() == 0x03
168                         ) {
169                                 break;
170                         }
171                         metaMsg = null;
172                 }
173                 if( metaMsg == null ) {
174                         if( name.length == 0 ) return false;
175                         track.add(new MidiEvent(
176                                 (MidiMessage)(metaMsg = new MetaMessage()), 0
177                         ));
178                 }
179                 try {
180                         metaMsg.setMessage(0x03, name, name.length);
181                 }
182                 catch( InvalidMidiDataException e ) {
183                         e.printStackTrace();
184                         return false;
185                 }
186                 return true;
187         }
188         /**
189          * シーケンス名のバイト列を返します。
190          * <p>トラック名の入った最初のトラックにあるトラック名を
191          * シーケンス名として返します。
192          * </p>
193          * @param seq MIDIシーケンス
194          * @return シーケンス名のバイト列
195          */
196         public static byte[] getNameBytesOf(Sequence seq) {
197                 // Returns name of the MIDI sequence.
198                 // A sequence name is placed at top of first track of the sequence.
199                 //
200                 Track tracks[] = seq.getTracks();
201                 byte b[];
202                 for( Track track : tracks )
203                         if( (b = getNameBytesOf(track)) != null ) return b;
204                 return null;
205         }
206         /**
207          * シーケンス名のバイト列を設定します。
208          * <p>先頭のトラックに設定されます。
209          * 設定に失敗した場合、順に次のトラックへの設定を試みます。
210          * </p>
211          *
212          * @param seq MIDIシーケンス
213          * @param name シーケンス名のバイト列
214          * @return 成功:true、失敗:false
215          */
216         public static boolean setNameBytesOf(Sequence seq, byte[] name) {
217                 Track tracks[] = seq.getTracks();
218                 for( Track track : tracks )
219                         if( setNameBytesOf(track,name) ) return true;
220                 return false;
221         }
222         ///////////////////////////////////////////////////////////////////
223         //
224         // Channel Message / System Message
225         //
226         /**
227          * MIDIステータス名を返します。
228          * @param status MIDIステータス
229          * @return MIDIステータス名
230          */
231         public static String getStatusName( int status ) {
232                 if( status < 0x80 ) {
233                         // No such status
234                         return null;
235                 }
236                 else if ( status < 0xF0 ) {
237                         // Channel Message
238                         return ch_msg_status_names[ (status >> 4) - 0x08 ];
239                 }
240                 else if ( status <= 0xFF ) {
241                         // System Message
242                         return sys_msg_names[ status - 0xF0 ];
243                 }
244                 return null;
245         }
246         /**
247          * 指定のMIDIショートメッセージがチャンネルメッセージかどうか調べます。
248          * @param msg MIDIメッセージ
249          * @return MIDIチャンネルメッセージの場合true
250          */
251         public static boolean isChannelMessage( ShortMessage msg ) {
252                 return isChannelMessage( msg.getStatus() );
253         }
254         /**
255          * MIDIステータスがチャンネルメッセージかどうか調べます。
256          * @param status MIDIステータス
257          * @return MIDIチャンネルメッセージの場合true
258          */
259         public static boolean isChannelMessage( int status ) {
260                 return ( status < 0xF0 && status >= 0x80 );
261         }
262         private static final String ch_msg_status_names[] = {
263                 // 0x80 - 0xE0 : Channel Voice Message
264                 // 0xB0 : Channel Mode Message
265                 "NoteOFF", "NoteON",
266                 "Polyphonic Key Pressure", "Ctrl/Mode",
267                 "Program", "Ch.Pressure", "Pitch Bend"
268         };
269         private static final String sys_msg_names[] = {
270                 // 0xF0 : System Exclusive
271                 "SysEx",
272                 //
273                 // 0xF1 - 0xF7 : System Common Message
274                 "MIDI Time Code Quarter Frame",
275                 "Song Position Pointer", "Song Select",
276                 null, null, "Tune Request", "Special SysEx",
277                 //
278                 // 0xF8 - 0xFF : System Realtime Message
279                 // 0xFF : Meta Message (SMF only, Not for wired MIDI message)
280                 "Timing Clock", null, "Start", "Continue",
281                 "Stop", null, "Active Sensing", "Meta / Sys.Reset",
282         };
283         ///////////////////////////////////////////////////////////////////
284         //
285         // Control Change / Channel Mode Message
286         //
287         /**
288          * コントロールチェンジの名前を返します。
289          * @param controllerNumber コントローラ番号
290          * @return コントロールチェンジの名前
291          */
292         public static String getControllerName( int controllerNumber ) {
293                 if( controllerNumber < 0x00 ) {
294                         return null;
295                 }
296                 else if( controllerNumber < 0x20 ) {
297                         String s = controllerNames0[controllerNumber];
298                         if( s != null ) s += " (MSB)";
299                         return s;
300                 }
301                 else if( controllerNumber < 0x40 ) {
302                         String s = controllerNames0[controllerNumber - 0x20];
303                         if( s != null ) s += " (LSB)";
304                         return s;
305                 }
306                 else if( controllerNumber < 0x78 ) {
307                         return controllerMomentarySwitchNames[controllerNumber - 0x40];
308                 }
309                 else if( controllerNumber < 0x80 ) {
310                         return controllerModeMessageNames[controllerNumber - 0x78];
311                 }
312                 else {
313                         return null;
314                 }
315         }
316         private static final String controllerNames0[] = {
317                 //
318                 // 0x00-0x0F (MSB)
319                 "Bank Select", "Modulation Depth", "Breath Controller", null,
320                 "Foot Controller", "Portamento Time", "Data Entry", "Volume",
321                 "Balance", null, "Pan", "Expression",
322                 "Effect Control 1", "Effect Control 2", null, null,
323                 //
324                 // 0x10-0x1F (MSB)
325                 "General Purpose 1", "General Purpose 2",
326                 "General Purpose 3", "General Purpose 4",
327                 null, null, null, null,
328                 null, null, null, null,
329                 null, null, null, null,
330                 //
331                 // 0x20-0x3F (LSB)
332         };
333         private static final String controllerMomentarySwitchNames[] = {
334                 //
335                 // 0x40-0x4F
336                 "Damper Pedal (Sustain)", "Portamento",
337                 "Sustenuto", "Soft Pedal",
338                 "Legato Footswitch", "Hold 2",
339                 "Sound Controller 1 (Sound Variation)",
340                 "Sound Controller 2 (Timbre/Harmonic Intens)",
341                 "Sound Controller 3 (Release Time)",
342                 "Sound Controller 4 (Attack Time)",
343                 "Sound Controller 5 (Brightness)",
344                 "Sound Controller 6 (Decay Time)",
345                 "Sound Controller 7 (Vibrato Rate)",
346                 "Sound Controller 8 (Vibrato Depth)",
347                 "Sound Controller 9 (Vibrato Delay)",
348                 "Sound Controller 10 (Undefined)",
349                 //
350                 // 0x50-0x5F
351                 "General Purpose 5", "General Purpose 6 (Temp Change)",
352                 "General Purpose 7", "General Purpose 8",
353                 "Portamento Control", null, null, null,
354                 null, null, null, "Reverb (Ext.Effects Depth)",
355                 "Tremelo Depth", "Chorus Depth",
356                 "Celeste (Detune) Depth", "Phaser Depth",
357                 //
358                 // 0x60-0x6F
359                 "Data Increment", "Data Decrement",
360                 "NRPN (LSB)", "NRPN (MSB)",
361                 "RPN (LSB)", "RPN (MSB)", null, null,
362                 null, null, null, null,
363                 null, null, null, null,
364                 //
365                 // 0x70-0x77
366                 null, null, null, null,
367                 null, null, null, null
368         };
369         private static final String controllerModeMessageNames[] = {
370                 // 0x78-0x7F
371                 "All Sound OFF", "Reset All Controllers",
372                 "Local Control", "All Notes OFF",
373                 "Omni Mode OFF", "Omni Mode ON",
374                 "Mono Mode ON", "Poly Mode ON"
375         };
376         ///////////////////////////////////////////////////////////////////
377         //
378         // System Exclusive
379         //
380         /**
381          * システムエクスクルーシブの製造者IDをキーにして製造者名を返すマップ
382          */
383         public static final Map<Integer,String>
384                 SYSEX_MANUFACTURER_NAMES = new HashMap<Integer,String>() {
385                         {
386                                 put(0x40,"KAWAI");
387                                 put(0x41,"Roland");
388                                 put(0x42,"KORG");
389                                 put(0x43,"YAMAHA");
390                                 put(0x44,"CASIO");
391                                 put(0x7D,"Non-Commercial");
392                                 put(0x7E,"Universal: Non-RealTime");
393                                 put(0x7F,"Universal: RealTime");
394                         }
395                 };
396         /**
397          * MIDIノート番号の最大値
398          */
399         public static final int MAX_NOTE_NO = 127;
400         /**
401          * General MIDI の楽器ファミリー名の配列
402          */
403         public static final String instrumentFamilyNames[] = {
404
405                 "Piano",
406                 "Chrom.Percussion",
407                 "Organ",
408                 "Guitar",
409                 "Bass",
410                 "Strings",
411                 "Ensemble",
412                 "Brass",
413
414                 "Reed",
415                 "Pipe",
416                 "Synth Lead",
417                 "Synth Pad",
418                 "Synth Effects",
419                 "Ethnic",
420                 "Percussive",
421                 "Sound Effects",
422         };
423         /**
424          * General MIDI の楽器名(プログラムチェンジのプログラム名)の配列
425          */
426         public static final String instrumentNames[] = {
427                 "Acoustic Grand Piano",
428                 "Bright Acoustic Piano",
429                 "Electric Grand Piano",
430                 "Honky-tonk Piano",
431                 "Electric Piano 1",
432                 "Electric Piano 2",
433                 "Harpsichord",
434                 "Clavi",
435                 "Celesta",
436                 "Glockenspiel",
437                 "Music Box",
438                 "Vibraphone",
439                 "Marimba",
440                 "Xylophone",
441                 "Tubular Bells",
442                 "Dulcimer",
443                 "Drawbar Organ",
444                 "Percussive Organ",
445                 "Rock Organ",
446                 "Church Organ",
447                 "Reed Organ",
448                 "Accordion",
449                 "Harmonica",
450                 "Tango Accordion",
451                 "Acoustic Guitar (nylon)",
452                 "Acoustic Guitar (steel)",
453                 "Electric Guitar (jazz)",
454                 "Electric Guitar (clean)",
455                 "Electric Guitar (muted)",
456                 "Overdriven Guitar",
457                 "Distortion Guitar",
458                 "Guitar harmonics",
459                 "Acoustic Bass",
460                 "Electric Bass (finger)",
461                 "Electric Bass (pick)",
462                 "Fretless Bass",
463                 "Slap Bass 1",
464                 "Slap Bass 2",
465                 "Synth Bass 1",
466                 "Synth Bass 2",
467                 "Violin",
468                 "Viola",
469                 "Cello",
470                 "Contrabass",
471                 "Tremolo Strings",
472                 "Pizzicato Strings",
473                 "Orchestral Harp",
474                 "Timpani",
475                 "String Ensemble 1",
476                 "String Ensemble 2",
477                 "SynthStrings 1",
478                 "SynthStrings 2",
479                 "Choir Aahs",
480                 "Voice Oohs",
481                 "Synth Voice",
482                 "Orchestra Hit",
483                 "Trumpet",
484                 "Trombone",
485                 "Tuba",
486                 "Muted Trumpet",
487                 "French Horn",
488                 "Brass Section",
489                 "SynthBrass 1",
490                 "SynthBrass 2",
491                 "Soprano Sax",
492                 "Alto Sax",
493                 "Tenor Sax",
494                 "Baritone Sax",
495                 "Oboe",
496                 "English Horn",
497                 "Bassoon",
498                 "Clarinet",
499                 "Piccolo",
500                 "Flute",
501                 "Recorder",
502                 "Pan Flute",
503                 "Blown Bottle",
504                 "Shakuhachi",
505                 "Whistle",
506                 "Ocarina",
507                 "Lead 1 (square)",
508                 "Lead 2 (sawtooth)",
509                 "Lead 3 (calliope)",
510                 "Lead 4 (chiff)",
511                 "Lead 5 (charang)",
512                 "Lead 6 (voice)",
513                 "Lead 7 (fifths)",
514                 "Lead 8 (bass + lead)",
515                 "Pad 1 (new age)",
516                 "Pad 2 (warm)",
517                 "Pad 3 (polysynth)",
518                 "Pad 4 (choir)",
519                 "Pad 5 (bowed)",
520                 "Pad 6 (metallic)",
521                 "Pad 7 (halo)",
522                 "Pad 8 (sweep)",
523                 "FX 1 (rain)",
524                 "FX 2 (soundtrack)",
525                 "FX 3 (crystal)",
526                 "FX 4 (atmosphere)",
527                 "FX 5 (brightness)",
528                 "FX 6 (goblins)",
529                 "FX 7 (echoes)",
530                 "FX 8 (sci-fi)",
531                 "Sitar",
532                 "Banjo",
533                 "Shamisen",
534                 "Koto",
535                 "Kalimba",
536                 "Bag pipe",
537                 "Fiddle",
538                 "Shanai",
539                 "Tinkle Bell",
540                 "Agogo",
541                 "Steel Drums",
542                 "Woodblock",
543                 "Taiko Drum",
544                 "Melodic Tom",
545                 "Synth Drum",
546                 "Reverse Cymbal",
547                 "Guitar Fret Noise",
548                 "Breath Noise",
549                 "Seashore",
550                 "Bird Tweet",
551                 "Telephone Ring",
552                 "Helicopter",
553                 "Applause",
554                 "Gunshot",
555         };
556         /**
557          * パーカッション用MIDIノート番号の最小値
558          */
559         public static final int MIN_PERCUSSION_NUMBER = 35;
560         /**
561          * パーカッション用のMIDIチャンネル(通常はCH.10)における
562          * ノート番号からパーカッション名を返します。
563          *
564          * @param note_no ノート番号
565          * @return パーカッション名
566          */
567         public static String getPercussionName(int note_no) {
568                 int i = note_no - MIN_PERCUSSION_NUMBER ;
569                 return i>=0 && i < PERCUSSION_NAMES.length ? PERCUSSION_NAMES[i] : "(Unknown)" ;
570         }
571         public static final String      PERCUSSION_NAMES[] = {
572                 "Acoustic Bass Drum",
573                 "Bass Drum 1",
574                 "Side Stick",
575                 "Acoustic Snare",
576                 "Hand Clap",
577                 "Electric Snare",
578                 "Low Floor Tom",
579                 "Closed Hi Hat",
580                 "High Floor Tom",
581                 "Pedal Hi-Hat",
582                 "Low Tom",
583                 "Open Hi-Hat",
584                 "Low-Mid Tom",
585                 "Hi Mid Tom",
586                 "Crash Cymbal 1",
587                 "High Tom",
588                 "Ride Cymbal 1",
589                 "Chinese Cymbal",
590                 "Ride Bell",
591                 "Tambourine",
592                 "Splash Cymbal",
593                 "Cowbell",
594                 "Crash Cymbal 2",
595                 "Vibraslap",
596                 "Ride Cymbal 2",
597                 "Hi Bongo",
598                 "Low Bongo",
599                 "Mute Hi Conga",
600                 "Open Hi Conga",
601                 "Low Conga",
602                 "High Timbale",
603                 "Low Timbale",
604                 "High Agogo",
605                 "Low Agogo",
606                 "Cabasa",
607                 "Maracas",
608                 "Short Whistle",
609                 "Long Whistle",
610                 "Short Guiro",
611                 "Long Guiro",
612                 "Claves",
613                 "Hi Wood Block",
614                 "Low Wood Block",
615                 "Mute Cuica",
616                 "Open Cuica",
617                 "Mute Triangle",
618                 "Open Triangle",
619         };
620         public static final String nsx39LyricElements[] = {
621                 "あ","い","う","え","お",
622                 "か","き","く","け","こ",
623                 "が","ぎ","ぐ","げ","ご",
624             "きゃ","きゅ","きょ",
625             "ぎゃ","ぎゅ","ぎょ",
626                 "さ","すぃ","す","せ","そ",
627                 "ざ","ずぃ","ず","ぜ","ぞ",
628             "しゃ","し","しゅ","しぇ","しょ",
629             "じゃ","じ","じゅ","じぇ","じょ",
630                 "た","てぃ","とぅ","て","と",
631                 "だ","でぃ","どぅ","で","ど",
632                 "てゅ","でゅ",
633                 "ちゃ","ち","ちゅ","ちぇ","ちょ",
634                 "つぁ","つぃ","つ","つぇ","つぉ",
635                 "な","に","ぬ","ね","の",
636             "にゃ","にゅ","にょ",
637                 "は","ひ","ふ","へ","ほ",
638                 "ば","び","ぶ","べ","ぼ",
639                 "ぱ","ぴ","ぷ","ぺ","ぽ",
640                 "ひゃ","ひゅ","ひょ",
641                 "びゃ","びゅ","びょ",
642                 "ぴゃ","ぴゅ","ぴょ",
643                 "ふぁ","ふぃ","ふゅ","ふぇ","ふぉ",
644                 "ま","み","む","め","も",
645                 "みゃ","みゅ","みょ",
646                 "や","ゆ","よ",
647                 "ら","り","る","れ","ろ",
648                 "りゃ","りゅ","りょ",
649                 "わ","うぃ","うぇ","を",
650                 "ん","ん","ん","ん","ん",
651         };
652         /**
653          * MIDIメッセージの内容を文字列で返します。
654          * @param msg MIDIメッセージ
655          * @param charset MIDIメタメッセージに含まれるテキストデータの文字コード
656          * @return MIDIメッセージの内容を表す文字列
657          */
658         public static String msgToString(MidiMessage msg, Charset charset) {
659                 String str = "";
660                 if( msg instanceof ShortMessage ) {
661                         ShortMessage shortmsg = (ShortMessage)msg;
662                         int status = msg.getStatus();
663                         String statusName = getStatusName(status);
664                         int data1 = shortmsg.getData1();
665                         int data2 = shortmsg.getData2();
666                         if( isChannelMessage(status) ) {
667                                 int channel = shortmsg.getChannel();
668                                 String channelPrefix = "Ch."+(channel+1) + ": ";
669                                 String statusPrefix = (
670                                         statusName == null ? String.format("status=0x%02X",status) : statusName
671                                 ) + ": ";
672                                 int cmd = shortmsg.getCommand();
673                                 switch( cmd ) {
674                                 case ShortMessage.NOTE_OFF:
675                                 case ShortMessage.NOTE_ON:
676                                         str += channelPrefix + statusPrefix + data1;
677                                         str += ":[";
678                                         if( MIDISpec.isRhythmPart(channel) ) {
679                                                 str += getPercussionName(data1);
680                                         }
681                                         else {
682                                                 str += NoteSymbol.noteNoToSymbol(data1);
683                                         }
684                                         str +="] Velocity=" + data2;
685                                         break;
686                                 case ShortMessage.POLY_PRESSURE:
687                                         str += channelPrefix + statusPrefix + "Note=" + data1 + " Pressure=" + data2;
688                                         break;
689                                 case ShortMessage.PROGRAM_CHANGE:
690                                         str += channelPrefix + statusPrefix + data1 + ":[" + instrumentNames[data1] + "]";
691                                         if( data2 != 0 ) str += " data2=" + data2;
692                                         break;
693                                 case ShortMessage.CHANNEL_PRESSURE:
694                                         str += channelPrefix + statusPrefix + data1;
695                                         if( data2 != 0 ) str += " data2=" + data2;
696                                         break;
697                                 case ShortMessage.PITCH_BEND:
698                                 {
699                                         int val = ((data1 & 0x7F) | ((data2 & 0x7F) << 7));
700                                         str += channelPrefix + statusPrefix + ( (val-8192) * 100 / 8191) + "% (" + val + ")";
701                                 }
702                                 break;
703                                 case ShortMessage.CONTROL_CHANGE:
704                                 {
705                                         // Control / Mode message name
706                                         String ctrl_name = getControllerName(data1);
707                                         str += channelPrefix + (data1 < 0x78 ? "CtrlChg: " : "ModeMsg: ");
708                                         if( ctrl_name == null ) {
709                                                 str += " No.=" + data1 + " Value=" + data2;
710                                                 return str;
711                                         }
712                                         str += ctrl_name;
713                                         //
714                                         // Controller's value
715                                         switch( data1 ) {
716                                         case 0x40: case 0x41: case 0x42: case 0x43: case 0x45:
717                                                 str += " " + ( data2==0x3F?"OFF":data2==0x40?"ON":data2 );
718                                                 break;
719                                         case 0x44: // Legato Footswitch
720                                                 str += " " + ( data2==0x3F?"Normal":data2==0x40?"Legato":data2 );
721                                                 break;
722                                         case 0x7A: // Local Control
723                                                 str += " " + ( data2==0x00?"OFF":data2==0x7F?"ON":data2 );
724                                                 break;
725                                         default:
726                                                 str += " " + data2;
727                                                 break;
728                                         }
729                                 }
730                                 break;
731
732                                 default:
733                                         // Never reached here
734                                         break;
735                                 }
736                         }
737                         else { // System Message
738                                 str += (statusName == null ? ("status="+status) : statusName );
739                                 str += " (" + data1 + "," + data2 + ")";
740                         }
741                         return str;
742                 }
743                 else if( msg instanceof MetaMessage ) {
744                         MetaMessage metamsg = (MetaMessage)msg;
745                         byte[] msgdata = metamsg.getData();
746                         int msgtype = metamsg.getType();
747                         str += "Meta: ";
748                         String meta_name = getMetaName(msgtype);
749                         if( meta_name == null ) {
750                                 str += "Unknown MessageType="+msgtype + " Values=(";
751                                 for( byte b : msgdata ) str += String.format( " %02X", b );
752                                 str += " )";
753                                 return str;
754                         }
755                         // Add the message type name
756                         str += meta_name;
757                         //
758                         // Add the text data
759                         if( hasMetaText(msgtype) ) {
760                                 str +=" ["+(new String(msgdata,charset))+"]";
761                                 return str;
762                         }
763                         // Add the numeric data
764                         switch(msgtype) {
765                         case 0x00: // Sequence Number (for MIDI Format 2)
766                                 if( msgdata.length == 2 ) {
767                                         str += String.format(
768                                                 ": %04X",
769                                                 ((msgdata[0] & 0xFF) << 8) | (msgdata[1] & 0xFF)
770                                         );
771                                         break;
772                                 }
773                                 str += ": Size not 2 byte : data=(";
774                                 for( byte b : msgdata ) str += String.format( " %02X", b );
775                                 str += " )";
776                                 break;
777                         case 0x20: // MIDI Ch.Prefix
778                         case 0x21: // MIDI Output Port
779                                 if( msgdata.length == 1 ) {
780                                         str += String.format( ": %02X", msgdata[0] & 0xFF );
781                                         break;
782                                 }
783                                 str += ": Size not 1 byte : data=(";
784                                 for( byte b : msgdata ) str += String.format( " %02X", b );
785                                 str += " )";
786                                 break;
787                         case 0x51: // Tempo
788                                 str += ": " + byteArrayToQpmTempo( msgdata ) + "[QPM] (";
789                                 for( byte b : msgdata ) str += String.format( " %02X", b );
790                                 str += " )";
791                                 break;
792                         case 0x54: // SMPTE Offset
793                                 if( msgdata.length == 5 ) {
794                                         str += ": "
795                                                 + (msgdata[0] & 0xFF) + ":"
796                                                 + (msgdata[1] & 0xFF) + ":"
797                                                 + (msgdata[2] & 0xFF) + "."
798                                                 + (msgdata[3] & 0xFF) + "."
799                                                 + (msgdata[4] & 0xFF);
800                                         break;
801                                 }
802                                 str += ": Size not 5 byte : data=(";
803                                 for( byte b : msgdata ) str += String.format( " %02X", b );
804                                 str += " )";
805                                 break;
806                         case 0x58: // Time Signature
807                                 if( msgdata.length == 4 ) {
808                                         str +=": " + msgdata[0] + "/" + (1 << msgdata[1]);
809                                         str +=", "+msgdata[2]+"[clk/beat], "+msgdata[3]+"[32nds/24clk]";
810                                         break;
811                                 }
812                                 str += ": Size not 4 byte : data=(";
813                                 for( byte b : msgdata ) str += String.format( " %02X", b );
814                                 str += " )";
815                                 break;
816                         case 0x59: // Key Signature
817                                 if( msgdata.length == 2 ) {
818                                         Key key = new Key(msgdata);
819                                         str += ": " + key.signatureDescription();
820                                         str += " (" + key.toStringIn(SymbolLanguage.NAME) + ")";
821                                         break;
822                                 }
823                                 str += ": Size not 2 byte : data=(";
824                                 for( byte b : msgdata ) str += String.format( " %02X", b );
825                                 str += " )";
826                                 break;
827                         case 0x7F: // Sequencer Specific Meta Event
828                                 str += " (";
829                                 for( byte b : msgdata ) str += String.format( " %02X", b );
830                                 str += " )";
831                                 break;
832                         }
833                         return str;
834                 }
835                 else if( msg instanceof SysexMessage ) {
836                         SysexMessage sysexmsg = (SysexMessage)msg;
837                         int status = sysexmsg.getStatus();
838                         byte[] msgdata = sysexmsg.getData();
839                         int dataBytePos = 1;
840                         switch( status ) {
841                         case SysexMessage.SYSTEM_EXCLUSIVE:
842                                 str += "SysEx: ";
843                                 break;
844                         case SysexMessage.SPECIAL_SYSTEM_EXCLUSIVE:
845                                 str += "SysEx(Special): ";
846                                 break;
847                         default:
848                                 str += "SysEx: Invalid (status="+status+") ";
849                                 break;
850                         }
851                         if( msgdata.length < 1 ) {
852                                 str += " Invalid data size: " + msgdata.length;
853                                 return str;
854                         }
855                         int manufacturerId = (int)(msgdata[0] & 0xFF);
856                         int deviceId = (int)(msgdata[1] & 0xFF);
857                         int modelId = (int)(msgdata[2] & 0xFF);
858                         String manufacturerName = SYSEX_MANUFACTURER_NAMES.get(manufacturerId);
859                         if( manufacturerName == null ) {
860                                 manufacturerName = String.format("[Manufacturer code %02X]", msgdata[0]);
861                         }
862                         str += manufacturerName + String.format(" (DevID=0x%02X)", deviceId);
863                         switch( manufacturerId ) {
864                         case 0x7E: // Non-Realtime Universal
865                                 dataBytePos++;
866                                 int sub_id_1 = modelId;
867                                 int sub_id_2 = (int)(msgdata[3] & 0xFF);
868                                 switch( sub_id_1 ) {
869                                 case 0x09: // General MIDI (GM)
870                                         switch( sub_id_2 ) {
871                                         case 0x01: str += " GM System ON"; return str;
872                                         case 0x02: str += " GM System OFF"; return str;
873                                         }
874                                         break;
875                                 default:
876                                         break;
877                                 }
878                                 break;
879                                 // case 0x7F: // Realtime Universal
880                         case 0x41: // Roland
881                                 dataBytePos++;
882                                 switch( modelId ) {
883                                 case 0x42:
884                                         str += " [GS]"; dataBytePos++;
885                                         if( msgdata[3]==0x12 ) {
886                                                 str += "DT1:"; dataBytePos++;
887                                                 switch( msgdata[4] ) {
888                                                 case 0x00:
889                                                         if( msgdata[5]==0x00 ) {
890                                                                 if( msgdata[6]==0x7F ) {
891                                                                         if( msgdata[7]==0x00 ) {
892                                                                                 str += " [88] System Mode Set (Mode 1: Single Module)"; return str;
893                                                                         }
894                                                                         else if( msgdata[7]==0x01 ) {
895                                                                                 str += " [88] System Mode Set (Mode 2: Double Module)"; return str;
896                                                                         }
897                                                                 }
898                                                         }
899                                                         else if( msgdata[5]==0x01 ) {
900                                                                 int port = (msgdata[7] & 0xFF);
901                                                                 str += String.format(
902                                                                                 " [88] Ch.Msg Rx Port: Block=0x%02X, Port=%s",
903                                                                                 msgdata[6],
904                                                                                 port==0?"A":port==1?"B":String.format("0x%02X",port)
905                                                                                 );
906                                                                 return str;
907                                                         }
908                                                         break;
909                                                 case 0x40:
910                                                         if( msgdata[5]==0x00 ) {
911                                                                 switch( msgdata[6] ) {
912                                                                 case 0x00: str += " Master Tune: "; dataBytePos += 3; break;
913                                                                 case 0x04: str += " Master Volume: "; dataBytePos += 3; break;
914                                                                 case 0x05: str += " Master Key Shift: "; dataBytePos += 3; break;
915                                                                 case 0x06: str += " Master Pan: "; dataBytePos += 3; break;
916                                                                 case 0x7F:
917                                                                         switch( msgdata[7] ) {
918                                                                         case 0x00: str += " GS Reset"; return str;
919                                                                         case 0x7F: str += " Exit GS Mode"; return str;
920                                                                         }
921                                                                         break;
922                                                                 }
923                                                         }
924                                                         else if( msgdata[5]==0x01 ) {
925                                                                 switch( msgdata[6] ) {
926                                                                 // case 0x00: str += ""; break;
927                                                                 // case 0x10: str += ""; break;
928                                                                 case 0x30: str += " Reverb Macro: "; dataBytePos += 3; break;
929                                                                 case 0x31: str += " Reverb Character: "; dataBytePos += 3; break;
930                                                                 case 0x32: str += " Reverb Pre-LPF: "; dataBytePos += 3; break;
931                                                                 case 0x33: str += " Reverb Level: "; dataBytePos += 3; break;
932                                                                 case 0x34: str += " Reverb Time: "; dataBytePos += 3; break;
933                                                                 case 0x35: str += " Reverb Delay FB: "; dataBytePos += 3; break;
934                                                                 case 0x36: str += " Reverb Chorus Level: "; dataBytePos += 3; break;
935                                                                 case 0x37: str += " [88] Reverb Predelay Time: "; dataBytePos += 3; break;
936                                                                 case 0x38: str += " Chorus Macro: "; dataBytePos += 3; break;
937                                                                 case 0x39: str += " Chorus Pre-LPF: "; dataBytePos += 3; break;
938                                                                 case 0x3A: str += " Chorus Level: "; dataBytePos += 3; break;
939                                                                 case 0x3B: str += " Chorus FB: "; dataBytePos += 3; break;
940                                                                 case 0x3C: str += " Chorus Delay: "; dataBytePos += 3; break;
941                                                                 case 0x3D: str += " Chorus Rate: "; dataBytePos += 3; break;
942                                                                 case 0x3E: str += " Chorus Depth: "; dataBytePos += 3; break;
943                                                                 case 0x3F: str += " Chorus Send Level To Reverb: "; dataBytePos += 3; break;
944                                                                 case 0x40: str += " [88] Chorus Send Level To Delay: "; dataBytePos += 3; break;
945                                                                 case 0x50: str += " [88] Delay Macro: "; dataBytePos += 3; break;
946                                                                 case 0x51: str += " [88] Delay Pre-LPF: "; dataBytePos += 3; break;
947                                                                 case 0x52: str += " [88] Delay Time Center: "; dataBytePos += 3; break;
948                                                                 case 0x53: str += " [88] Delay Time Ratio Left: "; dataBytePos += 3; break;
949                                                                 case 0x54: str += " [88] Delay Time Ratio Right: "; dataBytePos += 3; break;
950                                                                 case 0x55: str += " [88] Delay Level Center: "; dataBytePos += 3; break;
951                                                                 case 0x56: str += " [88] Delay Level Left: "; dataBytePos += 3; break;
952                                                                 case 0x57: str += " [88] Delay Level Right: "; dataBytePos += 3; break;
953                                                                 case 0x58: str += " [88] Delay Level: "; dataBytePos += 3; break;
954                                                                 case 0x59: str += " [88] Delay FB: "; dataBytePos += 3; break;
955                                                                 case 0x5A: str += " [88] Delay Send Level To Reverb: "; dataBytePos += 3; break;
956                                                                 }
957                                                         }
958                                                         else if( msgdata[5]==0x02 ) {
959                                                                 switch( msgdata[6] ) {
960                                                                 case 0x00: str += " [88] EQ Low Freq: "; dataBytePos += 3; break;
961                                                                 case 0x01: str += " [88] EQ Low Gain: "; dataBytePos += 3; break;
962                                                                 case 0x02: str += " [88] EQ High Freq: "; dataBytePos += 3; break;
963                                                                 case 0x03: str += " [88] EQ High Gain: "; dataBytePos += 3; break;
964                                                                 }
965                                                         }
966                                                         else if( msgdata[5]==0x03 ) {
967                                                                 if( msgdata[6] == 0x00 ) {
968                                                                         str += " [Pro] EFX Type: "; dataBytePos += 3;
969                                                                 }
970                                                                 else if( msgdata[6] >= 0x03 && msgdata[6] <= 0x16 ) {
971                                                                         str += String.format(" [Pro] EFX Param %d", msgdata[6]-2 );
972                                                                         dataBytePos += 3;
973                                                                 }
974                                                                 else if( msgdata[6] == 0x17 ) {
975                                                                         str += " [Pro] EFX Send Level To Reverb: "; dataBytePos += 3;
976                                                                 }
977                                                                 else if( msgdata[6] == 0x18 ) {
978                                                                         str += " [Pro] EFX Send Level To Chorus: "; dataBytePos += 3;
979                                                                 }
980                                                                 else if( msgdata[6] == 0x19 ) {
981                                                                         str += " [Pro] EFX Send Level To Delay: "; dataBytePos += 3;
982                                                                 }
983                                                                 else if( msgdata[6] == 0x1B ) {
984                                                                         str += " [Pro] EFX Ctrl Src1: "; dataBytePos += 3;
985                                                                 }
986                                                                 else if( msgdata[6] == 0x1C ) {
987                                                                         str += " [Pro] EFX Ctrl Depth1: "; dataBytePos += 3;
988                                                                 }
989                                                                 else if( msgdata[6] == 0x1D ) {
990                                                                         str += " [Pro] EFX Ctrl Src2: "; dataBytePos += 3;
991                                                                 }
992                                                                 else if( msgdata[6] == 0x1E ) {
993                                                                         str += " [Pro] EFX Ctrl Depth2: "; dataBytePos += 3;
994                                                                 }
995                                                                 else if( msgdata[6] == 0x1F ) {
996                                                                         str += " [Pro] EFX Send EQ Switch: "; dataBytePos += 3;
997                                                                 }
998                                                         }
999                                                         else if( (msgdata[5] & 0xF0) == 0x10 ) {
1000                                                                 int ch = (msgdata[5] & 0x0F);
1001                                                                 if( ch <= 9 ) ch--; else if( ch == 0 ) ch = 9;
1002                                                                 if( msgdata[6]==0x02 ) {
1003                                                                         str += String.format(
1004                                                                                         " Rx Ch: Part=%d(0x%02X) Ch=0x%02X", (ch+1),  msgdata[5], msgdata[7]
1005                                                                                         );
1006                                                                         return str;
1007                                                                 }
1008                                                                 else if( msgdata[6]==0x15 ) {
1009                                                                         String map;
1010                                                                         switch( msgdata[7] ) {
1011                                                                         case 0: map = " NormalPart"; break;
1012                                                                         case 1: map = " DrumMap1"; break;
1013                                                                         case 2: map = " DrumMap2"; break;
1014                                                                         default: map = String.format("0x%02X",msgdata[7]); break;
1015                                                                         }
1016                                                                         str += String.format(
1017                                                                                 " Rhythm Part: Ch=%d(0x%02X) Map=%s",
1018                                                                                 (ch+1), msgdata[5],
1019                                                                                 map
1020                                                                         );
1021                                                                         return str;
1022                                                                 }
1023                                                         }
1024                                                         else if( (msgdata[5] & 0xF0) == 0x40 ) {
1025                                                                 int ch = (msgdata[5] & 0x0F);
1026                                                                 if( ch <= 9 ) ch--; else if( ch == 0 ) ch = 9;
1027                                                                 int dt = (msgdata[7] & 0xFF);
1028                                                                 if( msgdata[6]==0x20 ) {
1029                                                                         str += String.format(
1030                                                                                 " [88] EQ: Ch=%d(0x%02X) %s",
1031                                                                                 (ch+1), msgdata[5],
1032                                                                                 dt==0 ? "OFF" : dt==1 ? "ON" : String.format("0x%02X",dt)
1033                                                                         );
1034                                                                 }
1035                                                                 else if( msgdata[6]==0x22 ) {
1036                                                                         str += String.format(
1037                                                                                 " [Pro] Part EFX Assign: Ch=%d(0x%02X) %s",
1038                                                                                 (ch+1), msgdata[5],
1039                                                                                 dt==0 ? "ByPass" : dt==1 ? "EFX" : String.format("0x%02X",dt)
1040                                                                         );
1041                                                                 }
1042                                                         }
1043                                                         break;
1044                                                 } // [4]
1045                                         } // [3] [DT1]
1046                                         break; // [GS]
1047                                 case 0x45:
1048                                         str += " [GS-LCD]"; dataBytePos++;
1049                                         if( msgdata[3]==0x12 ) {
1050                                                 str += " [DT1]"; dataBytePos++;
1051                                                 if( msgdata[4]==0x10 && msgdata[5]==0x00 && msgdata[6]==0x00 ) {
1052                                                         dataBytePos += 3;
1053                                                         str += " Disp [" +(new String(
1054                                                                 msgdata, dataBytePos, msgdata.length - dataBytePos - 2
1055                                                         ))+ "]";
1056                                                 }
1057                                         } // [3] [DT1]
1058                                         break;
1059                                 case 0x14: str += " [D-50]"; dataBytePos++; break;
1060                                 case 0x16: str += " [MT-32]"; dataBytePos++; break;
1061                                 } // [2] model_id
1062                                 break;
1063                         case 0x43: // Yamaha
1064                                 if( (deviceId & 0xF0) == 0x10 && modelId == 0x4C ) {
1065                                         str += " [XG]Dev#="+(deviceId & 0x0F);
1066                                         dataBytePos += 2;
1067                                         if( msgdata[3]==0 && msgdata[4]==0 && msgdata[5]==0x7E && msgdata[6]==0 ) {
1068                                                 str += " System ON";
1069                                                 return str;
1070                                         }
1071
1072                                 }
1073                                 else if( deviceId == 0x79 && modelId == 9 ) {
1074                                         str += " [eVocaloid]";
1075                                         dataBytePos += 2;
1076                                         if( msgdata[3]==0x11 && msgdata[4]==0x0A && msgdata[5]==0 ) {
1077                                                 StringBuilder p = new StringBuilder();
1078                                                 for( int i=6; i<msgdata.length; i++ ) {
1079                                                         int b = (msgdata[i] & 0xFF);
1080                                                         if( b == 0xF7 ) break;
1081                                                         String s = (
1082                                                                 b < 0 || b >= MIDISpec.nsx39LyricElements.length ?
1083                                                                 "?": MIDISpec.nsx39LyricElements[b]
1084                                                         );
1085                                                         p.append(s);
1086                                                 }
1087                                                 str += " pronounce["+p+"]";
1088                                                 return str;
1089                                         }
1090                                 }
1091                                 break;
1092                         default:
1093                                 break;
1094                         }
1095                         int i;
1096                         str += " data=(";
1097                         for( i = dataBytePos; i<msgdata.length-1; i++ ) {
1098                                 str += String.format( " %02X", msgdata[i] );
1099                         }
1100                         if( i < msgdata.length && (int)(msgdata[i] & 0xFF) != 0xF7 ) {
1101                                 str+=" [ Invalid EOX " + String.format( "%02X", msgdata[i] ) + " ]";
1102                         }
1103                         str += " )";
1104                         return str;
1105                 }
1106                 byte[] msg_data = msg.getMessage();
1107                 str += "(";
1108                 for( byte b : msg_data ) {
1109                         str += String.format( " %02X", b );
1110                 }
1111                 str += " )";
1112                 return str;
1113         }
1114         public static boolean isRhythmPart(int ch) { return (ch == 9); }
1115 }