OSDN Git Service

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