OSDN Git Service

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