C言語でMIDI(SMF)データを読んでみる!
(※1:当サイトの別記事「ファミコンやらゲームボーイやらのピコピコ音を作るぞ!(with C言語)」のために用意した記事です。)
(MIDIとかSMFについては、[ Wikipedia: MIDI ]が少しだけ参考になると思います。)
MIDI(SMF)のデータ構造については上の※1の記事内に参考サイトを掲載しているのでそちらをご覧下さい。この記事の中では説明を割愛します。^^;
この過程の結果(すなわち、解析プログラム)を知りたい方は記事の下の方を見てくださいね。
(この記事の内容にはミスがあるかもしれませんが、その時はご容赦ください。)
MIDIはバイナリデータなので少々扱いが面倒な気がしますが、恐れずにやってみましょう!
プログラムはちょっと長くなるので、表示/非表示を選択できるようにしました。
下のボタンを操作して、出したり消したりしてください。(えっ...?)
#include <stdio.h> int main(){ // ヘッダチャンク情報を格納する変数群 char header_chunk_type[4]; // チャンクタイプを示す文字列を格納。「MThd」が入るはず。[4byte] int header_chunk_size; // ヘッダチャンクデータのサイズ [4byte] short smf_format; // SMFのフォーマットタイプ(0か1か2) [2byte] short tracks; // トラックチャンク総数 [2byte] short division; // 四分音符あたりの分解能(ここではデルタタイム) [2byte] // データ読み取り開始! FILE *fp; // ファイルポインタ生成 if((fp = fopen("./sample.mid", "rb")) === NULL){ // バイナリ読み取りモードでファイルを開く printf("Error: Cannot open the file.\n"); // 失敗したらエラーを吐く return -1; } // ヘッダチャンク取得 fread(header_chunk_type, 1, 4, fp); // チャンクタイプ【※ア】 fread(&header_chunk_size, 4, 1, fp); // チャンクデータサイズ【※イ】 fread(&smf_format, 2, 1, fp); // SMFフォーマットタイプ fread(&tracks, 2, 1, fp); // トラックチャンク総数 fread(&division, 2, 1, fp); // 分解能(デルタタイム) // 読み取った情報を出力してみよう! printf("header_chunk_type : %c%c%c%c\n", header_chunk_type[0], header_chunk_type[1], header_chunk_type[2], header_chunk_type[3]); // 【※ウ】 printf("header_chunk_size : %d\n", header_chunk_size); printf("smf_format : %hd\n", smf_format); printf("tracks : %hd\n", tracks); printf("division : %hd\n", division); return 0; } |
このプログラムの概説です。
プログラムと同じディレクトリにある./sample.midというファイルを解析します。
念のために書いておきます。。。
#【※ア】文字配列header_chunk_typeの場所に、1byteデータを4つ取り込む...ということをしている。
# 配列の場合は、配列名自体がポインタ(先頭要素のアドレス)になっているので、「&」は不要!
#【※イ】int型変数header_chunk_sizeの場所に、4byteデータを1つ取り込む...ということをしている。
# 配列ではないので、変数名に「&」をつけることでそのアドレスを取得!
#【※ウ】header_chunk_typeの出力時に「%s」ではなく「%c%c%c%c」としているのは、
# 文字列header_chunk_typeには終端文字(\0)が含まれていないからです。
ちなみに、解析するMIDIデータの先頭部分はこんな感じです。
$ hexdump ./sample.mid |
00 00 00 06 : 「0x00000006」=「6」...チャンクデータ(smf_format, tracks, division)のサイズは6byte
00 01 : 「0x0001」=「1」...SMFのFormat1
00 12 : 「0x0012」=「18」...18Tracks
01 e0 : 「0x01e0」=「480」...分解能480
(「0x?????」というのは、?????が16進数であることを表します。)
さあ、これで読めるだろうとか思ってやってみたら、見事につまずいたわけです。^^;;
$ ./program header_chunk_type : MThd header_chunk_size : 100663296 smf_format : 256 tracks : 4608 division : -8191 |
なんじゃこりゃ...?(汗)
とんでもない嘘つきの解析プログラムでした。。。(^^;;
あっているのはheader_chunk_typeの「MThd」だけ。
一体、何故このようなことになったのでしょうか...
...にしてもsmf_formatの「256」というのは、あまりに綺麗な数字ですね。(2の8乗)
これは2進数で表すと「100000000」の9桁(=9ビット)ですよね。
データの最小単位はバイト(=8ビット)なので9ビットの情報は16ビットで表します。
すなわち、「00000001 00000000」です。
これを4桁ずつ区切って16進表記にすると「01 00」となります。
あれっ?MIDIのデータには「00 01」と書いてあるのに。。。...もしや!!(ひらめきっ!
そうなんです。おそらく、エンディアンの問題です。
MIDIのデータとして格納されている数値はビッグエンディアン形式ですが、
私はリトルエンディアンの環境(IntelプロセッサのMac)を使っていたのです。
正直、「そんなややこしい問題とか、勘弁してー!!!」と思いました。。。^^;
しかし、そんなことで嘆いてどうするんだ学生よ!と言われそうなので頑張ります。
とりあえず、エンディアンの問題さえ解決すればよいわけです。
...これについては、また込み入りそうなので、別の記事として書いておきました。
詳しく知りたい場合は是非エンディアン判定(別記事)を参考に!
先ほど取得した値はエンディアンが逆になっていたようなので、
エンディアンを変換してやればよいのです。
これもエンディアン変換(別記事)を参考に!
ということで、この2つの記事の内容を参考にしながら実装してみました!
...第3節へ〜^^;
とりあえずプログラムを書いてみました。
#include <stdio.h> #include <stdlib.h> int convertEndian(void *input, size_t s){ // エンディアン変換をおこなう関数 // stdlib.hをインクルードしてください。 // 【引数】: void *input...エンディアン変換対象へのポインタ // 【引数】: size_t s...変換対象のバイト数 int i; // カウンタ char *temp; // 変換時に用いる一時的配列 if((temp = (char *)calloc( s, sizeof(char))) == NULL){ return 0; // 領域確保できず(失敗) } for(i=0; i<s; i++){ // inputデータをtempに一時保管 temp[i] = ((char *)input)[i]; } for(i=1; i<=s; i++){ // tempデータを逆方向にしてinputへ代入 ((char *)input)[i-1] = temp[s-i]; } free(temp); // 確保した領域を解放 return 1; // 正常終了 } int main(){ // ヘッダチャンク情報 char header_chunk_type[4]; // チャンクタイプを示す文字列を格納。「MThd」が入るはず。[4byte] int header_chunk_size; // ヘッダチャンクデータのサイズ [4byte] short smf_format; // SMFのフォーマットタイプ(0か1か2) [2byte] short tracks; // トラックチャンク総数 [2byte] short division; // 四分音符あたりの分解能(ここではデルタタイム) [2byte] // MIDI(SMF)データ読み取り FILE *fp; // ファイルポインタ生成 int endian_check; // エンディアン判定に使用 if((fp = fopen("./sample.mid", "rb")) == NULL){ // バイナリ読み取りモードでファイルを開く printf("Error: Cannot open the file.\n"); // 失敗したらエラーを吐く return -1; } // ヘッダチャンク取得 fread(header_chunk_type, 1, 4, fp); // チャンクタイプ fread(&header_chunk_size, 4, 1, fp); // チャンクデータサイズ fread(&smf_format, 2, 1, fp); // SMFフォーマットタイプ fread(&tracks, 2, 1, fp); // トラックチャンク総数 fread(&division, 2, 1, fp); // 分解能(デルタタイム) // エンディアン判定&変換 endian_check = 1; if(*(char *)&endian_check){ // リトルエンディアンなら... // エンディアン変換処理 convertEndian(&header_chunk_size, sizeof(header_chunk_size)); convertEndian(&smf_format, sizeof(smf_format)); convertEndian(&tracks, sizeof(tracks)); convertEndian(&division, sizeof(division)); } else { // ビッグエンディアンなら... // 何も処理しない } // 読み取った情報を出力してみよう! printf("header_chunk_type : %c%c%c%c\n", header_chunk_type[0], header_chunk_type[1], header_chunk_type[2], header_chunk_type[3]); printf("header_chunk_size : %d\n", header_chunk_size); printf("smf_format : %hd\n", smf_format); printf("tracks : %hd\n", tracks); printf("division : %hd\n", division); return 0; } |
...というプログラムでしたが、さあ実行結果を見てみましょう。
$ ./program header_chunk_type : MThd header_chunk_size : 6 smf_format : 1 tracks : 18 division : 480 |
おぉっ、なんだかんだでヘッダチャンクは読み取れましたね!!
次はトラックチャンクの読取に挑戦です!
先ほどと同様にMIDIデータを眺めてみましょう。
$ hexdump ./sample.mid |
00 00 00 5d : 「0x0000005d」=「93」...チャンクのサイズは93byte
00 ff ...(中略)... 2f 00 ...93byte分のデータ(MIDIイベントの羅列)
こんな感じの『 MTrk チャンクサイズ イベントの羅列 』が、1つのトラックとして扱われます。
ヘッダチャンクを読み取ったとき、トラック数は18でしたね。
なので、このMIDIファイルはトラックが18個並んでいるということです。(下の表を参考に^^)
MIDI(SMF)データ | ||||||||||||||||||||||||||||||||||||||||
|
さて、実際にトラックチャンクを解析してみましょう。
ただし、まだMIDIイベントについては解析をおこないません。あとでやります。
#include <stdio.h> #include <stdlib.h> typedef struct { // トラックチャンクのデータを格納する構造体 char type[4]; // チャンクタイプを示す文字列を格納。「MTrk」が入るはず。[4byte] int size; // トラックチャンクデータのサイズ [4byte] char *data; // トラックデータ(MIDIイベントの羅列)へのポインタ } TrackChunk; int convertEndian(void *input, size_t s){ // エンディアン変換をおこなう関数 // stdlib.hをインクルードしてください。 // 【引数】: void *input...エンディアン変換対象へのポインタ // 【引数】: size_t s...変換対象のバイト数 int i; // カウンタ char *temp; // 変換時に用いる一時的配列 if((temp = (char *)calloc(s, sizeof(char))) == NULL){ perror("Error: Cannot get memory for temp."); return 0; // 領域確保できず(失敗) } for(i=0; i<s; i++){ // inputデータをtempに一時保管 temp[i] = ((char *)input)[i]; } for(i=1; i<=s; i++){ // tempデータを逆方向にしてinputへ代入 ((char *)input)[i-1] = temp[s-i]; } free(temp); // 確保した領域を解放 return 1; // 正常終了 } int main(){ int i, j; // カウンタ FILE *fp; // ファイルポインタ生成 int endian; // エンディアン判定にいろいろ使用(0:BigEndian, 1:LittleEndian) // ヘッダチャンク情報 char header_chunk_type[4]; // チャンクタイプを示す文字列を格納。「MThd」が入るはず。[4byte] int header_chunk_size; // ヘッダチャンクデータのサイズ [4byte] short smf_format; // SMFのフォーマットタイプ(0か1か2) [2byte] short tracks; // トラックチャンク総数 [2byte] short division; // 四分音符あたりの分解能(ここではデルタタイム) [2byte] // トラックチャンク情報 TrackChunk *track_chunks; // トラックチャンク情報を格納する配列のためのポインタ // エンディアン判定 endian = 1; if(*(char *)&endian){ // リトルエンディアンなら... endian = 1; // Little Endian } else { // ビッグエンディアンなら... endian = 0; // Big Endian } // MIDIファイルを開く if((fp = fopen("./sample.mid", "rb")) == NULL){ // バイナリ読み取りモードでファイルを開く perror("Error: Cannot open the file.\n"); // 失敗したらエラーを吐く return 0; } // ヘッダチャンク取得 fread(header_chunk_type, 1, 4, fp); // チャンクタイプ fread(&header_chunk_size, 4, 1, fp); // チャンクデータサイズ fread(&smf_format, 2, 1, fp); // SMFフォーマットタイプ fread(&tracks, 2, 1, fp); // トラックチャンク総数 fread(&division, 2, 1, fp); // 分解能(デルタタイム) // 必要ならエンディアン変換 if(endian){ // リトルエンディアンなら要変換 // エンディアン変換処理 convertEndian(&header_chunk_size, sizeof(header_chunk_size)); convertEndian(&smf_format, sizeof(smf_format)); convertEndian(&tracks, sizeof(tracks)); convertEndian(&division, sizeof(division)); } // 読み取ったヘッダチャンク情報を出力 printf("# Header ========================\n"); printf("header_chunk_type : %c%c%c%c\n", header_chunk_type[0], header_chunk_type[1], header_chunk_type[2], header_chunk_type[3]); printf("header_chunk_size : %d\n", header_chunk_size); printf("smf_format : %hd\n", smf_format); printf("tracks : %hd\n", tracks); printf("division : %hd\n", division); // トラックチャンク取得 if((track_chunks = (TrackChunk *)calloc(tracks, sizeof(TrackChunk))) == NULL){ // トラック数に応じて領域確保 perror("Error: Cannot get memory for track_chunks."); return 0; // 領域確保できず(失敗) } for(i=0; i<tracks; i++){ // トラック数だけ繰返し fread(track_chunks[i].type, 1, 4, fp); // チャンクタイプ fread(&track_chunks[i].size, 4, 1, fp); // チャンクデータサイズ if(endian){ // リトルエンディアンなら要変換 convertEndian(&track_chunks[i].size, sizeof(track_chunks[i].size)); } if((track_chunks[i].data = (char *)calloc(track_chunks[i].size, sizeof(char))) == NULL){ // データサイズに応じて領域確保 perror("Error: Cannot get memory for track_chunks[i].data ."); return 0; // 領域確保できず(失敗) } fread(track_chunks[i].data, track_chunks[i].size, sizeof(char), fp); // データ(MIDIイベントの羅列) } // 読み取ったトラックチャンク情報を出力 for(i=0; i<tracks; i++){ // トラック数だけ繰返し printf("# Track[%02d] =====================\n", i); printf("track_chunks[%d].type : %c%c%c%c\n", i, track_chunks[i].type[0], track_chunks[i].type[1], track_chunks[i].type[2], track_chunks[i].type[3]); printf("track_chunks[%d].size : %d\n", i, track_chunks[i].size); printf("track_chunks[%d].data\n : ", i); for(j=0; j<track_chunks[i].size; j++){ printf("%02x ", (unsigned char)track_chunks[i].data[j]); // 16進表記で出力 if(!((j+1)%10)) printf("\n : "); // 10バイト出力するたびに改行 } printf("\n"); } return 1; } |
いろいろと問題(例えばこんなこと)にぶつかったりして困りましたが、
無事にトラックチャンクを読み取ることができました。
さて、次こそ本題ですね。MIDIイベントの解析をしようと思います!
記事がここまで長いと、最初に紹介したサイトへのリンクまで行くのが面倒ですね...(^^;;
...なので改めて紹介します。ここからは以下のサイトを参考にします。
about Standard MIDI format
[ http://www2s.biglobe.ne.jp/~yyagi/material/smfspec.html ]
MIDI規格一覧表
[ http://www2.odn.ne.jp/~cbu69490/MIDI/MIDIlect2/MIDIlect_end.html ]
【資料】SMFの解説
[ http://members.jcom.home.ne.jp/0127953801/bubu/jukusui.htm ]
ファイル形式、MIDIシーケンスデータ
[ http://altered.s201.xrea.com/documentsdaw/m20070615fileformatmidi.html ]
小泉プロジェクト MIDIミニ知識
[ http://homepage3.nifty.com/koizumipro/miditowa.htm ]
StandardMIDI_document10
[ http://www.ne.jp/asahi/10/40/onptank/doc_smf10.html ]
結局、いろいろ妥協するはめになりました。。。
(メタイベント開始を示す「FF」とシステムリセット「FF」の判別がよくわからなかったので、システムリセットは見つけることができません。(システムリセットはほとんど使われないそうですが...^^;;))
(それから、日本語に対応していない...とか。ほかもいろいろ。)
...という感じで問題が多々あるような気がしますが、とりあえず重要そうなデータは取り出すことが出来るようになったので良しとします。(良しとさせてください...m(_ _;)m)
// // Reading MIDI(SMF) Data // 2010.08.07 - 2010.08.16 #include <stdio.h> #include <stdlib.h> typedef struct { // トラックチャンクのデータを格納する構造体 char type[4]; // チャンクタイプを示す文字列を格納。「MTrk」が入るはず。[4byte] int size; // トラックチャンクデータのサイズ [4byte] char *data; // トラックデータ(イベントの羅列)へのポインタ } TrackChunk; short mergeChar7bit(char x, char y){ // charの下7bitずつを結合してshort型で出力 // 【引数】:結合対象となる2つのchar型変数x, y short s; s = (unsigned char)x; // 上位バイトを先に入れておく s <<= 7; // 7bit左シフト s = (s | (unsigned char)(y & 0x7f)); // 下位バイトの下7bitを合成。。。 return s; } int convertEndian(void *input, size_t s){ // エンディアン変換をおこなう関数 // stdlib.hをインクルードしてください。 // 【引数】: void *input...エンディアン変換対象へのポインタ // 【引数】: size_t s...変換対象のバイト数 int i; // カウンタ char *temp; // 変換時に用いる一時的配列 if((temp = (char *)calloc(s, sizeof(char))) == NULL){ perror("Error: Cannot get memory for temp."); return 0; // 領域確保できず(失敗) } for(i=0; i<s; i++){ // inputデータをtempに一時保管 temp[i] = ((char *)input)[i]; } for(i=1; i<=s; i++){ // tempデータを逆方向にしてinputへ代入 ((char *)input)[i-1] = temp[s-i]; } free(temp); // 確保した領域を解放 return 1; // 正常終了 } int main(){ int i, j, k, cnt; // カウンタ FILE *fp; // ファイルポインタ生成 int endian; // エンディアン判定にいろいろ使用(0:BigEndian, 1:LittleEndian) // ヘッダチャンク情報 char header_chunk_type[4]; // チャンクタイプを示す文字列を格納。「MThd」が入るはず。[4byte] int header_chunk_size; // ヘッダチャンクデータのサイズ [4byte] short smf_format; // SMFのフォーマットタイプ(0か1か2) [2byte] short tracks; // トラックチャンク総数 [2byte] short division; // 四分音符あたりの分解能(ここではデルタタイム) [2byte] // トラックチャンク情報 TrackChunk *track_chunks; // トラックチャンク情報を格納する配列のためのポインタ unsigned char c; // イベント解析の際に使用する一時保存用変数 unsigned char status; // ステータスバイト用の一時変数 unsigned int delta; // デルタタイム // エンディアン判定 endian = 1; if(*(char *)&endian){ // リトルエンディアンなら... endian = 1; // Little Endian } else { // ビッグエンディアンなら... endian = 0; // Big Endian } // MIDIファイルを開く if((fp = fopen("./sample.mid", "rb")) == NULL){ // バイナリ読み取りモードでファイルを開く perror("Error: Cannot open the file.\n"); // 失敗したらエラーを吐く return 0; } // ヘッダチャンク取得 fread(header_chunk_type, 1, 4, fp); // チャンクタイプ fread(&header_chunk_size, 4, 1, fp); // チャンクデータサイズ fread(&smf_format, 2, 1, fp); // SMFフォーマットタイプ fread(&tracks, 2, 1, fp); // トラックチャンク総数 fread(&division, 2, 1, fp); // 分解能(デルタタイム) // 必要ならエンディアン変換 if(endian){ // リトルエンディアンなら要変換 // エンディアン変換処理 convertEndian(&header_chunk_size, sizeof(header_chunk_size)); convertEndian(&smf_format, sizeof(smf_format)); convertEndian(&tracks, sizeof(tracks)); convertEndian(&division, sizeof(division)); } // 読み取ったヘッダチャンク情報を出力 printf("# Header ========================\n"); printf("header_chunk_type : %c%c%c%c\n", header_chunk_type[0], header_chunk_type[1], header_chunk_type[2], header_chunk_type[3]); printf("header_chunk_size : %d\n", header_chunk_size); printf("smf_format : %hd\n", smf_format); printf("tracks : %hd\n", tracks); printf("division : %hd\n", division); // トラックチャンク取得 if((track_chunks = (TrackChunk *)calloc(tracks, sizeof(TrackChunk))) == NULL){ // トラック数に応じて領域確保 perror("Error: Cannot get memory for track_chunks."); return 0; // 領域確保できず(失敗) } for(i=0; i<tracks; i++){ // トラック数だけ繰返し fread(track_chunks[i].type, 1, 4, fp); // チャンクタイプ fread(&track_chunks[i].size, 4, 1, fp); // チャンクデータサイズ if(endian){ // リトルエンディアンなら要変換 convertEndian(&track_chunks[i].size, sizeof(track_chunks[i].size)); } if((track_chunks[i].data = (char *)calloc(track_chunks[i].size, sizeof(char))) == NULL){ // データサイズに応じて領域確保 perror("Error: Cannot get memory for track_chunks[i].data ."); return 0; // 領域確保できず(失敗) } fread(track_chunks[i].data, track_chunks[i].size, sizeof(char), fp); // データ(イベントの羅列) } // 読み取ったトラックチャンク情報を出力 for(i=0; i<tracks; i++){ // トラック数だけ繰返し printf("# Track[%02d] =====================\n", i+1); printf("track_chunks[%d].type : %c%c%c%c\n", i, track_chunks[i].type[0], track_chunks[i].type[1], track_chunks[i].type[2], track_chunks[i].type[3]); printf("track_chunks[%d].size : %d\n", i, track_chunks[i].size); printf("track_chunks[%d].data\n", i); for(j=0; j<track_chunks[i].size; j++){ // データ分だけ繰返し delta = 0; // 初期化 while((c = (unsigned char)track_chunks[i].data[j++]) & 0x80){ delta = delta | (c & 0x7F); // 合成 delta <<= 7; // 7bit左シフト } delta = delta | c; // 合成 printf("%7d:: ", delta); // デルタタイム出力 // ランニングステータスルールに対する処理 if((track_chunks[i].data[j] & 0x80) == 0x80){ // ランニングステータスルールが適用されない場合は、ステータスバイト用変数を更新。 status = (unsigned char)track_chunks[i].data[j]; // ステータスバイトを保持しておく } else { printf("\b@"); // ランニングステータスルール適用のしるし j--; // データバイトの直前のバイト(デルタタイムかな?)を指すようにしておく。 // 次の処理でj++するはずなので、そうすればデータバイトにアクセスできる。 } // データ判別 if((status & 0xf0) == 0x80){ // ノート・オフ【ボイスメッセージ】 printf("Note Off [%02dch] : ", (status & 0x0f)); j++; printf("Note%d", (unsigned char)track_chunks[i].data[j]); printf("[0x%02x] ", (unsigned char)track_chunks[i].data[j++]); printf("Velocity=%d\n", (unsigned char)track_chunks[i].data[j]); } else if((status & 0xf0) == 0x90){ // ノート・オン【ボイスメッセージ】 printf("Note On [%02dch] : ", (status & 0x0f)); j++; printf("Note%d", (unsigned char)track_chunks[i].data[j]); printf("[0x%02x] ", (unsigned char)track_chunks[i].data[j++]); printf("Velocity=%d\n", (unsigned char)track_chunks[i].data[j]); } else if((status & 0xf0) == 0xa0){ // ポリフォニック・キー・プレッシャー【ボイスメッセージ】 printf("Polyphonic Key Pressure [%02dch] : ", (status & 0x0f)); j++; printf("Note%d", (unsigned char)track_chunks[i].data[j]); printf("[0x%02x] ", (unsigned char)track_chunks[i].data[j++]); printf("Pressure=%d\n", (unsigned char)track_chunks[i].data[j]); } else if((status & 0xf0) == 0xb0){ // コントロールチェンジ【ボイスメッセージ】、【モードメッセージ】 printf("Control Change [%02dch] : ", (status & 0x0f)); j++; c = (unsigned char)track_chunks[i].data[j++]; if(c<=63){ // 連続可変タイプのエフェクトに関するコントロール情報(MSBもLSBも) // (ホントは「0<=c && c<=63」と書きたいけど、warningが出るので「c<=63」にする) printf("VariableEffect("); switch(c){ case 0: // 上位バイト[MSB] case 32: // 下位バイト[LSB] printf("BankSelect[%s]", (c==0)?"MSB":"LSB"); // バンク・セレクト break; case 1: case 33: printf("ModulationDepth[%s]", (c==1)?"MSB":"LSB"); // モジュレーション・デプス break; case 2: case 34: printf("BreathType[%s]", (c==2)?"MSB":"LSB"); // ブレス・タイプ break; case 4: case 36: printf("FootType[%s]", (c==4)?"MSB":"LSB"); // フット・タイプ break; case 5: case 37: printf("PortamentoTime[%s]", (c==5)?"MSB":"LSB"); // ポルタメント・タイム break; case 6: case 38: printf("DataEntry[%s]", (c==6)?"MSB":"LSB"); // データ・エントリー break; case 7: case 39: printf("MainVolume[%s]", (c==7)?"MSB":"LSB"); // メイン・ボリューム break; case 8: case 40: printf("BalanceControl[%s]", (c==8)?"MSB":"LSB"); // バランス・コントロール break; case 10: case 42: printf("Panpot[%s]", (c==10)?"MSB":"LSB"); // パンポット break; case 11: case 43: printf("Expression[%s]", (c==11)?"MSB":"LSB"); // エクスプレッション break; case 16: case 17: case 18: case 19: case 48: case 49: case 50: case 51: printf("SomethingElse_No.%d[%s]", c, (c==16)?"MSB":"LSB"); // 汎用操作子 break; default: printf("##UndefinedType_No.%d[%s]", (c<32)?c:c-32, (c<32)?"MSB":"LSB"); // よくわからないコントロール } printf(")=%d", (unsigned char)track_chunks[i].data[j]); } else if(64<=c && c<=95){ // 連続可変でないタイプのエフェクトに関するコントロール情報 printf("InvariableEffect("); switch(c){ case 64: printf("Hold1(Damper)"); // ホールド1(ダンパー) break; case 65: printf("Portamento"); // ポルタメント break; case 66: printf("Sostenuto"); // ソステヌート break; case 67: printf("SoftPedal"); // ソフト・ペダル break; case 69: printf("Hold2(Freeze)"); // ホールド2(フリーズ) break; case 71: printf("HarmonicIntensity"); // ハーモニック・インテンシティ break; case 72: printf("ReleaseTime"); // リリース・タイム break; case 73: printf("AttackTime"); // アタック・タイム break; case 74: printf("Brightness"); // ブライトネス break; case 80: case 81: case 82: case 83: printf("SomethingElse_No.%d", c); // 汎用操作子 break; case 91: printf("ExternalEffect"); // 外部エフェクト break; case 92: printf("Tremolo"); // トレモロ break; case 93: printf("Chorus"); // コーラス break; case 94: printf("Celeste"); // セレステ break; case 95: printf("Phaser"); // フェイザー break; default: printf("##UndefinedType_No.%d", c); // よくわからないコントロール } printf(")=%d", (unsigned char)track_chunks[i].data[j]); } else if(96<=c && c<=119){ // 特殊な情報 printf("SpecialPurpose("); switch(c){ case 96: printf("DataIncrement"); // データ・インクリメント break; case 97: printf("DataDecrement"); // デクリメント break; case 98: printf("NRPN[LSB]"); // NRPNのLSB break; case 99: printf("NRPN[MSB]"); // NRPNのMSB break; case 100: printf("RPN[LSB]"); // RPNのLSB break; case 101: printf("RPN[MSB]"); // RPNのMSB break; default: printf("##UndefinedType_No.%d", c); // よくわからないコントロール } printf(")=%d", (unsigned char)track_chunks[i].data[j]); } else if(120<=c && c<=127){ // モード・メッセージ printf("ModeMessage("); switch(c){ case 120: printf("AllSoundOff"); // オール・サウンド・オフ break; case 121: printf("ResetAllController"); // リセット・オール・コントローラー break; case 122: printf("LocalControl"); // ローカル・コントロール break; case 123: printf("AllNoteOff"); // オール・ノート・オフ break; case 124: printf("OmniOn"); // オムニ・オン break; case 125: printf("OmniOff"); // オムニ・オフ break; case 126: printf("MonoModeOn"); // モノモード・オン(ポリモード・オフ) break; case 127: printf("PolyModeOn"); // ポリモード・オン(モノモード・オフ) break; default: printf("##UndefinedType_No.%d", c); // よくわからないコントロール } printf(")=%d", c, (unsigned char)track_chunks[i].data[j]); } printf("\n"); } else if((status & 0xf0) == 0xc0){ // プログラム・チェンジ【ボイスメッセージ】 printf("Program Change [%02dch] : ", (status & 0x0f)); j++; printf("ToneNo=%d\n", (unsigned char)track_chunks[i].data[j]); } else if((status & 0xf0) == 0xd0){ // チャンネル・プレッシャー【ボイスメッセージ】 printf("Channel Pressure [%02dch] : ", (status & 0x0f)); j++; printf("Pressure=%d\n", (unsigned char)track_chunks[i].data[j]); } else if((status & 0xf0) == 0xe0){ // ピッチ・ベンド・チェンジ【ボイスメッセージ】 printf("Pitch Bend Change [%02dch] : ", (status & 0x0f)); j++; printf("Bend=%hd", mergeChar7bit(track_chunks[i].data[j+1], track_chunks[i].data[j]) - 8192); printf(" (LSB:%d", (unsigned char)track_chunks[i].data[j++]); printf(", MSB:%d)\n", (unsigned char)track_chunks[i].data[j]); } else if((status & 0xf0) == 0xf0){ // 【システム・メッセージ】 switch(status & 0x0f){ case 0x00: // エクスクルーシブ・メッセージ printf("F0 Exclusive Message : "); j++; // SysExのデータ長を取得 cnt = 0; // 初期化 while((c = (unsigned char)track_chunks[i].data[j++]) & 0x80){ // フラグビットが1の間はループ cnt = cnt | c; // 合成 cnt <<= 7; // 7bit左シフト } cnt = cnt | c; // 合成 printf(" Length=%u", (unsigned int)cnt); // SysExのデータ長を取得 for(k=1; k<=cnt; k++){ // 長さの分だけデータ取得 printf("[%02x]", (unsigned char)track_chunks[i].data[j++]); } j--; // 行き過ぎた分をデクリメント break; case 0x01: // MIDIタイムコード printf("MIDI Time Code : "); j++; printf("(frame/sec/min/hour)=%d", (unsigned char)track_chunks[i].data[j]); break; case 0x02: // ソング・ポジション・ポインタ printf("Song Position Pointer : "); j++; printf("Position=%hd[MIDI beat]", mergeChar7bit(track_chunks[i].data[j+1], track_chunks[i].data[j])); printf(" (LSB:%d", (unsigned char)track_chunks[i].data[j++]); printf(", MSB:%d)", (unsigned char)track_chunks[i].data[j]); break; case 0x03: // ソング・セレクト printf("Song Select : "); j++; printf("SelectNo=%d", (unsigned char)track_chunks[i].data[j]); break; case 0x06: // チューン・リクエスト printf("Tune Request"); break; case 0x07: // エンド・オブ・エクスクルーシブでもあるけども... // F7ステータスの場合のエクスクルーシブ・メッセージ //printf("@End of Exclusive"); printf("F7 Exclusive Message : "); j++; // SysExのデータ長を取得 cnt = 0; // 初期化 while((c = (unsigned char)track_chunks[i].data[j++]) & 0x80){ // フラグビットが1の間はループ cnt = cnt | c; // 合成 cnt <<= 7; // 7bit左シフト } cnt = cnt | c; // 合成 printf(" Length=%u", (unsigned int)cnt); // SysExのデータ長を取得 for(k=1; k<=cnt; k++){ // 長さの分だけデータ取得 printf("[%02x]", (unsigned char)track_chunks[i].data[j++]); } j--; // 行き過ぎた分をデクリメント break; case 0x08: // タイミング・クロック printf("Timing Clock"); break; case 0x0A: // スタート printf("Start"); break; case 0x0B: // コンティニュー printf("Continue"); break; case 0x0C: // ストップ printf("Stop"); break; case 0x0E: // アクティブ・センシング printf("Active Sensing"); break; //case 0x0F: // システムリセット(何か間違っている気がする。。。) // printf("System Reset"); case 0x0F: // メタイベント printf("Meta Ivent : "); j++; switch((unsigned char)track_chunks[i].data[j]){ case 0x00: // シーケンス番号 printf("Sequence Number="); j += 2; // データ長の分を通り越す cnt = (unsigned char)track_chunks[i].data[j++]; cnt <<= 8; // 8bit左シフト cnt = cnt | (unsigned char)track_chunks[i].data[j]; printf("%d", cnt); break; case 0x01: // テキスト[可変長] printf("Text="); cnt = 0; j += 1; while((c = (unsigned char)track_chunks[i].data[j++]) & 0x80){ cnt = cnt | (c & 0x7F); // 合成 cnt <<= 7; // 7bit左シフト } cnt = cnt | (c & 0x7F); // 合成 for(k=1; k<=cnt; k++){ printf("%c", track_chunks[i].data[j++]); } j--; // 行き過ぎた分をデクリメント break; case 0x02: // 著作権表示[可変長] printf("Copyright="); cnt = 0; j += 1; while((c = (unsigned char)track_chunks[i].data[j++]) & 0x80){ cnt = cnt | (c & 0x7F); // 合成 cnt <<= 7; // 7bit左シフト } cnt = cnt | (c & 0x7F); // 合成 for(k=1; k<=cnt; k++){ printf("%c", track_chunks[i].data[j++]); } j--; // 行き過ぎた分をデクリメント break; case 0x03: // シーケンス名(曲タイトル)・トラック名[可変長] printf("Title="); cnt = 0; j += 1; while((c = (unsigned char)track_chunks[i].data[j++]) & 0x80){ cnt = cnt | (c & 0x7F); // 合成 cnt <<= 7; // 7bit左シフト } cnt = cnt | (c & 0x7F); // 合成 for(k=1; k<=cnt; k++){ printf("%c", track_chunks[i].data[j++]); } j--; // 行き過ぎた分をデクリメント break; case 0x04: // 楽器名[可変長] printf("InstName="); cnt = 0; j += 1; while((c = (unsigned char)track_chunks[i].data[j++]) & 0x80){ cnt = cnt | (c & 0x7F); // 合成 cnt <<= 7; // 7bit左シフト } cnt = cnt | (c & 0x7F); // 合成 for(k=1; k<=cnt; k++){ printf("%c", track_chunks[i].data[j++]); } j--; // 行き過ぎた分をデクリメント break; case 0x05: // 歌詞[可変長] printf("Words="); cnt = 0; j += 1; while((c = (unsigned char)track_chunks[i].data[j++]) & 0x80){ cnt = cnt | (c & 0x7F); // 合成 cnt <<= 7; // 7bit左シフト } cnt = cnt | (c & 0x7F); // 合成 for(k=1; k<=cnt; k++){ printf("%c", track_chunks[i].data[j++]); } j--; // 行き過ぎた分をデクリメント break; case 0x06: // マーカー[可変長] printf("Marker="); cnt = 0; j += 1; while((c = (unsigned char)track_chunks[i].data[j++]) & 0x80){ cnt = cnt | (c & 0x7F); // 合成 cnt <<= 7; // 7bit左シフト } cnt = cnt | (c & 0x7F); // 合成 for(k=1; k<=cnt; k++){ printf("%c", track_chunks[i].data[j++]); } j--; // 行き過ぎた分をデクリメント break; case 0x07: // キューポイント[可変長] printf("CuePoint="); cnt = 0; j += 1; while((c = (unsigned char)track_chunks[i].data[j++]) & 0x80){ cnt = cnt | (c & 0x7F); // 合成 cnt <<= 7; // 7bit左シフト } cnt = cnt | (c & 0x7F); // 合成 for(k=1; k<=cnt; k++){ printf("%c", track_chunks[i].data[j++]); } j--; // 行き過ぎた分をデクリメント break; case 0x08: // プログラム名(音色名)[可変長] printf("ProgramName="); cnt = 0; j += 1; while((c = (unsigned char)track_chunks[i].data[j++]) & 0x80){ cnt = cnt | (c & 0x7F); // 合成 cnt <<= 7; // 7bit左シフト } cnt = cnt | (c & 0x7F); // 合成 for(k=1; k<=cnt; k++){ printf("%c", track_chunks[i].data[j++]); } j--; // 行き過ぎた分をデクリメント break; case 0x09: // デバイス名(音源名)[可変長] printf("DeviceName="); cnt = 0; j += 1; while((c = (unsigned char)track_chunks[i].data[j++]) & 0x80){ cnt = cnt | (c & 0x7F); // 合成 cnt <<= 7; // 7bit左シフト } cnt = cnt | (c & 0x7F); // 合成 for(k=1; k<=cnt; k++){ printf("%c", track_chunks[i].data[j++]); } j--; // 行き過ぎた分をデクリメント break; case 0x20: // MIDIチャンネルプリフィックス[1byte] printf("MidiChannelPrefix="); j += 2; // データ長の分を通り越す cnt = (unsigned char)track_chunks[i].data[j]; printf("%d", cnt); break; case 0x21: // ポート指定[1byte] printf("Port="); j += 2; // データ長の分を通り越す cnt = (unsigned char)track_chunks[i].data[j]; printf("%d", cnt); break; case 0x2F: // トラック終端[0byte] printf("End of Track"); j += 1; // データ長の分を通り越す break; case 0x51: // テンポ設定[3byte] printf("Temp="); j += 2; // データ長の分を通り越す cnt = (unsigned char)track_chunks[i].data[j++]; cnt <<= 8; // 8bit左シフト cnt = cnt | (unsigned char)track_chunks[i].data[j++]; cnt <<= 8; // 8bit左シフト cnt = cnt | (unsigned char)track_chunks[i].data[j]; printf("%d", cnt); break; case 0x54: // SMPTEオフセット[5byte] printf("SMPTE_Offset="); j += 2; // データ長の分を通り越す cnt = (unsigned char)track_chunks[i].data[j++]; switch(cnt & 0xC0){ // フレーム速度 case 0x00: printf("24fps"); break; case 0x01: printf("25fps"); break; case 0x10: printf("30fps(DropFrame)"); break; case 0x11: printf("30fps"); break; } printf(",Hour:%d", (cnt & 0x3F)); // 時間 printf(",Min:%d", (unsigned char)track_chunks[i].data[j++]); // 分 printf(",Sec:%d", (unsigned char)track_chunks[i].data[j++]); // 秒 printf(",Frame:%d", (unsigned char)track_chunks[i].data[j++]); // フレーム printf(",SubFrame:%d", (unsigned char)track_chunks[i].data[j]); // サブフレーム break; case 0x58: // 拍子設定[4byte] printf("Rhythm="); j += 2; // データ長の分を通り越す printf("%d", (unsigned char)track_chunks[i].data[j++]); cnt = 1; for(k=0; k<=(int)track_chunks[i].data[j]; k++){ // 拍子の分母を算出する cnt *= 2; // 2の累乗 } printf("/%d", cnt); j++; printf(" ClockPerBeat=%d", (unsigned char)track_chunks[i].data[j++]); printf(" 32NotePer4Note=%d", (unsigned char)track_chunks[i].data[j]); break; case 0x59: // 調設定[2byte] printf("Key="); j += 2; // データ長の分を通り越す cnt = (int)track_chunks[i].data[j++]; if(c > 0){ printf("Sharp[%d]", c); } else if(c == 0){ printf("C"); } else { printf("Flat[%d]", c); } cnt = (int)track_chunks[i].data[j]; if(c == 0){ printf("_Major"); } else { printf("_Minor"); } break; case 0x7F: // シーケンサ特定メタイベント printf("SpecialIvent="); cnt = 0; j += 1; while((c = (unsigned char)track_chunks[i].data[j++]) & 0x80){ cnt = cnt | (c & 0x7F); // 合成 cnt <<= 7; // 7bit左シフト } cnt = cnt | (c & 0x7F); // 合成 for(k=1; k<=cnt; k++){ printf("[%02x]", (unsigned char)track_chunks[i].data[j++]); } j--; // 行き過ぎた分をデクリメント break; default : ; } break; default: printf("# SysEx (Something else...[Status:%02x])", status); // 見知らぬステータスなら } printf("\n"); } else { printf("## Something else...[Status:%02x]\n", status); // 見知らぬステータスなら } } printf("\n"); } // track_chunks,track_chunks[i].dataはcalloc()で領域確保しているので解放し忘れないように! return 1; } |
関数にしてまとめる...なんてこともしないグダグダなプログラムですが...(汗)
このプログラムをコンパイルして実行すると、同一ディレクトリ内に存在する「sample.mid」というMIDI(SMF)ファイルを解析してくれます。
(ちなみにgccでコンパイルしました。)
(MacOS Xで、Intel Macです。きっとリトルエンディアンなのです。)
ちなみに、ランニングステータスルールにも対応しています。(実験済み)
実際に「sample.mid」を解析した結果出力を以下に示します。(長いので一部省略)
「結果出力を表示する」のボタンを押すと表示されます。
$ gcc -o program ./program.c $ ./program # Header ======================== header_chunk_type : MThd header_chunk_size : 6 smf_format : 1 tracks : 18 division : 480 # Track[01] ===================== track_chunks[0].type : MTrk track_chunks[0].size : 93 track_chunks[0].data 0:: Meta Ivent : Title= 0:: Meta Ivent : Copyright=Copyright (C) 2010 0:: Meta Ivent : Text=?y?T?C?g???z URL 0:: Meta Ivent : Temp=500000 0:: Meta Ivent : Marker=Setup 0:: Meta Ivent : Rhythm=4/8 ClockPerBeat=24 32NotePer4Note=8 480:: Meta Ivent : Temp=500000 1440:: Meta Ivent : Marker=Start 0:: Meta Ivent : End of Track # Track[02] ===================== track_chunks[1].type : MTrk track_chunks[1].size : 265 track_chunks[1].data 0:: Meta Ivent : Title=System Setup 0:: Meta Ivent : Port=0 0:: F0 Exclusive Message : Length=5[7e][7f][09][01][f7] 60:: F0 Exclusive Message : Length=10[41][10][42][12][40][00][7f][00][41][f7] 60:: F0 Exclusive Message : Length=7[7f][7f][04][01][00][7f][f7] 10:: F0 Exclusive Message : Length=10[41][10][42][12][40][11][15][00][1a][f7] 10:: F0 Exclusive Message : Length=10[41][10][42][12][40][12][15][00][19][f7] 10:: F0 Exclusive Message : Length=10[41][10][42][12][40][13][15][00][18][f7] 10:: F0 Exclusive Message : Length=10[41][10][42][12][40][14][15][00][17][f7] 10:: F0 Exclusive Message : Length=10[41][10][42][12][40][15][15][00][16][f7] 10:: F0 Exclusive Message : Length=10[41][10][42][12][40][16][15][00][15][f7] 10:: F0 Exclusive Message : Length=10[41][10][42][12][40][17][15][00][14][f7] 10:: F0 Exclusive Message : Length=10[41][10][42][12][40][18][15][00][13][f7] 10:: F0 Exclusive Message : Length=10[41][10][42][12][40][19][15][00][12][f7] 10:: F0 Exclusive Message : Length=10[41][10][42][12][40][10][15][01][1a][f7] 10:: F0 Exclusive Message : Length=10[41][10][42][12][40][1a][15][00][11][f7] 10:: F0 Exclusive Message : Length=10[41][10][42][12][40][1b][15][00][10][f7] 10:: F0 Exclusive Message : Length=10[41][10][42][12][40][1c][15][00][0f][f7] 10:: F0 Exclusive Message : Length=10[41][10][42][12][40][1d][15][00][0e][f7] 10:: F0 Exclusive Message : Length=10[41][10][42][12][40][1e][15][00][0d][f7] 10:: F0 Exclusive Message : Length=10[41][10][42][12][40][1f][15][00][0c][f7] 200:: Meta Ivent : End of Track # Track[03] ===================== track_chunks[2].type : MTrk track_chunks[2].size : 219 track_chunks[2].data 0:: Meta Ivent : Title=CH01 0:: Meta Ivent : Port=0 290:: Control Change [00ch] : ModeMessage(ResetAllController)=121 4:: Control Change [00ch] : VariableEffect(BankSelect[MSB])=0 0:: Control Change [00ch] : VariableEffect(BankSelect[LSB])=0 0:: Program Change [00ch] : ToneNo=0 4:: Control Change [00ch] : VariableEffect(Panpot[MSB])=64 4:: Control Change [00ch] : VariableEffect(MainVolume[MSB])=100 4:: Control Change [00ch] : InvariableEffect(ExternalEffect)=0 4:: Control Change [00ch] : VariableEffect(Expression[MSB])=127 4:: Control Change [00ch] : VariableEffect(ModulationDepth[MSB])=0 4:: Control Change [00ch] : SpecialPurpose(RPN[MSB])=0 0:: Control Change [00ch] : SpecialPurpose(RPN[LSB])=1 0:: Control Change [00ch] : VariableEffect(DataEntry[MSB])=64 0:: Control Change [00ch] : VariableEffect(DataEntry[LSB])=0 4:: Control Change [00ch] : SpecialPurpose(RPN[MSB])=0 0:: Control Change [00ch] : SpecialPurpose(RPN[LSB])=2 0:: Control Change [00ch] : VariableEffect(DataEntry[MSB])=64 4:: Control Change [00ch] : SpecialPurpose(RPN[MSB])=0 0:: Control Change [00ch] : SpecialPurpose(RPN[LSB])=0 0:: Control Change [00ch] : VariableEffect(DataEntry[MSB])=12 4:: Pitch Bend Change [00ch] : Bend=0 (LSB:0, MSB:64) 4:: Control Change [00ch] : SpecialPurpose(RPN[MSB])=127 0:: Control Change [00ch] : SpecialPurpose(RPN[LSB])=127 146:: Note On [00ch] : Note60[0x3c] Velocity=100 480:: Note Off [00ch] : Note60[0x3c] Velocity=0 0:: Note On [00ch] : Note62[0x3e] Velocity=100 480:: Note Off [00ch] : Note62[0x3e] Velocity=0 0:: Note On [00ch] : Note64[0x40] Velocity=100 480:: Note Off [00ch] : Note64[0x40] Velocity=0 0:: Note On [00ch] : Note65[0x41] Velocity=100 480:: Note Off [00ch] : Note65[0x41] Velocity=0 0:: Note On [00ch] : Note67[0x43] Velocity=100 0:: Note On [00ch] : Note64[0x40] Velocity=100 480:: Note Off [00ch] : Note67[0x43] Velocity=0 0:: Note Off [00ch] : Note64[0x40] Velocity=0 0:: Note On [00ch] : Note69[0x45] Velocity=100 0:: Note On [00ch] : Note62[0x3e] Velocity=100 480:: Note Off [00ch] : Note69[0x45] Velocity=0 0:: Note Off [00ch] : Note62[0x3e] Velocity=0 0:: Note On [00ch] : Note71[0x47] Velocity=100 0:: Note On [00ch] : Note67[0x43] Velocity=100 480:: Note Off [00ch] : Note71[0x47] Velocity=0 0:: Note Off [00ch] : Note67[0x43] Velocity=0 0:: Note On [00ch] : Note72[0x48] Velocity=100 0:: Note On [00ch] : Note60[0x3c] Velocity=100 0:: Note On [00ch] : Note64[0x40] Velocity=100 480:: Note Off [00ch] : Note72[0x48] Velocity=0 0:: Note Off [00ch] : Note60[0x3c] Velocity=0 0:: Note Off [00ch] : Note64[0x40] Velocity=0 480:: Meta Ivent : End of Track # Track[04] ===================== track_chunks[3].type : MTrk track_chunks[3].size : 106 track_chunks[3].data 0:: Meta Ivent : Title=CH02 0:: Meta Ivent : Port=0 290:: Control Change [01ch] : ModeMessage(ResetAllController)=121 4:: Control Change [01ch] : VariableEffect(BankSelect[MSB])=0 0:: Control Change [01ch] : VariableEffect(BankSelect[LSB])=0 0:: Program Change [01ch] : ToneNo=0 4:: Control Change [01ch] : VariableEffect(Panpot[MSB])=64 4:: Control Change [01ch] : VariableEffect(MainVolume[MSB])=100 4:: Control Change [01ch] : InvariableEffect(ExternalEffect)=0 4:: Control Change [01ch] : VariableEffect(Expression[MSB])=127 4:: Control Change [01ch] : VariableEffect(ModulationDepth[MSB])=0 4:: Control Change [01ch] : SpecialPurpose(RPN[MSB])=0 0:: Control Change [01ch] : SpecialPurpose(RPN[LSB])=1 0:: Control Change [01ch] : VariableEffect(DataEntry[MSB])=64 0:: Control Change [01ch] : VariableEffect(DataEntry[LSB])=0 4:: Control Change [01ch] : SpecialPurpose(RPN[MSB])=0 0:: Control Change [01ch] : SpecialPurpose(RPN[LSB])=2 0:: Control Change [01ch] : VariableEffect(DataEntry[MSB])=64 4:: Control Change [01ch] : SpecialPurpose(RPN[MSB])=0 0:: Control Change [01ch] : SpecialPurpose(RPN[LSB])=0 0:: Control Change [01ch] : VariableEffect(DataEntry[MSB])=12 4:: Pitch Bend Change [01ch] : Bend=0 (LSB:0, MSB:64) 4:: Control Change [01ch] : SpecialPurpose(RPN[MSB])=127 0:: Control Change [01ch] : SpecialPurpose(RPN[LSB])=127 146:: Meta Ivent : End of Track # Track[05] ===================== track_chunks[4].type : MTrk track_chunks[4].size : 106 track_chunks[4].data 0:: Meta Ivent : Title=CH03 0:: Meta Ivent : Port=0 290:: Control Change [02ch] : ModeMessage(ResetAllController)=121 4:: Control Change [02ch] : VariableEffect(BankSelect[MSB])=0 0:: Control Change [02ch] : VariableEffect(BankSelect[LSB])=0 0:: Program Change [02ch] : ToneNo=0 4:: Control Change [02ch] : VariableEffect(Panpot[MSB])=64 【...中略...】 0:: Control Change [14ch] : SpecialPurpose(RPN[LSB])=0 0:: Control Change [14ch] : VariableEffect(DataEntry[MSB])=12 4:: Pitch Bend Change [14ch] : Bend=0 (LSB:0, MSB:64) 4:: Control Change [14ch] : SpecialPurpose(RPN[MSB])=127 0:: Control Change [14ch] : SpecialPurpose(RPN[LSB])=127 146:: Meta Ivent : End of Track # Track[18] ===================== track_chunks[17].type : MTrk track_chunks[17].size : 106 track_chunks[17].data 0:: Meta Ivent : Title=CH16 0:: Meta Ivent : Port=0 290:: Control Change [15ch] : ModeMessage(ResetAllController)=121 4:: Control Change [15ch] : VariableEffect(BankSelect[MSB])=0 0:: Control Change [15ch] : VariableEffect(BankSelect[LSB])=0 0:: Program Change [15ch] : ToneNo=0 4:: Control Change [15ch] : VariableEffect(Panpot[MSB])=64 4:: Control Change [15ch] : VariableEffect(MainVolume[MSB])=100 4:: Control Change [15ch] : InvariableEffect(ExternalEffect)=0 4:: Control Change [15ch] : VariableEffect(Expression[MSB])=127 4:: Control Change [15ch] : VariableEffect(ModulationDepth[MSB])=0 4:: Control Change [15ch] : SpecialPurpose(RPN[MSB])=0 0:: Control Change [15ch] : SpecialPurpose(RPN[LSB])=1 0:: Control Change [15ch] : VariableEffect(DataEntry[MSB])=64 0:: Control Change [15ch] : VariableEffect(DataEntry[LSB])=0 4:: Control Change [15ch] : SpecialPurpose(RPN[MSB])=0 0:: Control Change [15ch] : SpecialPurpose(RPN[LSB])=2 0:: Control Change [15ch] : VariableEffect(DataEntry[MSB])=64 4:: Control Change [15ch] : SpecialPurpose(RPN[MSB])=0 0:: Control Change [15ch] : SpecialPurpose(RPN[LSB])=0 0:: Control Change [15ch] : VariableEffect(DataEntry[MSB])=12 4:: Pitch Bend Change [15ch] : Bend=0 (LSB:0, MSB:64) 4:: Control Change [15ch] : SpecialPurpose(RPN[MSB])=127 0:: Control Change [15ch] : SpecialPurpose(RPN[LSB])=127 146:: Meta Ivent : End of Track |
ちなみに「sample.mid」はWindows環境で作られました。
MIDI音楽編集ソフトDomino[サイトはコチラ]を利用しました。
あと、ランニングステータスルールに対応するために、まずルールを適用したMIDIファイルを生成するため、SMFランナー 1.0[サイトはコチラ]を利用しました。
また、解析の正しさを確認するために、SMFView[Vectorにてダウンロード可能]にて解析を行い、比較・検証しました。
(これらの環境及びこれまで紹介したサイトの存在無しに、この解析プログラムは存在し得ません。勝手ではありますが、この場で御礼申し上げます。)
一応、そのsample.midもアップロードしておきます。
あぁ、長かった。。。
個人的にはNoteOnとNoteOffさえ取り出せれば良かったんですが。。。
「やるなら全部読みたいやん!」という衝動に駆られて作ってみました。
(が、結局中途半端ですけどね^^;;)
もし活用できる方がいらっしゃれば是非使ってくださいまし。嬉しい限りです。
(使えるものとは到底思えないけど。。。orz)
(もう一度...この記事の内容にはミスがあるかもしれませんが、その時はどうぞご容赦ください。^^;)
# 2010.10.16 : 語句修正
以後の更新内容の改善のために、是非ともご評価のほどよろしくお願いします!→ |
テーマ : サウンド・プログラミング / ジャンル : コンピュータ