C言語でMIDI(SMF)データを読んでみる!

今回は、MIDI(SMF)データをC言語で解析しましょー!という、ちょい長めのお話です。

※1:当サイトの別記事「ファミコンやらゲームボーイやらのピコピコ音を作るぞ!(with C言語)」のために用意した記事です。)
(MIDIとかSMFについては、[ Wikipedia: MIDI ]が少しだけ参考になると思います。)

MIDI(SMF)のデータ構造については上の※1の記事内に参考サイトを掲載しているのでそちらをご覧下さい。この記事の中では説明を割愛します。^^;


この過程の結果(すなわち、解析プログラム)を知りたい方は記事の下の方を見てくださいね。
(この記事の内容にはミスがあるかもしれませんが、その時はご容赦ください。)

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
00000000  4d 54 68 64 00 00 00 06  00 01 00 12 01 e0 4d 54
00000010  72 6b 00 00 00 5d 00 ff  03 00 00 ff 02 13 43 6f
...(省略)...

4d 54 68 64 : 「MThd」...チャンクタイプ(「MThd」なのでヘッダ!)
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. エンディアンの問題を片付ける!

...これについては、また込み入りそうなので、別の記事として書いておきました。
詳しく知りたい場合は是非エンディアン判定(別記事)を参考に!

先ほど取得した値はエンディアンが逆になっていたようなので、
エンディアンを変換してやればよいのです。
これもエンディアン変換(別記事)を参考に!

ということで、この2つの記事の内容を参考にしながら実装してみました!
...第3節へ〜^^;

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


おぉっ、なんだかんだでヘッダチャンクは読み取れましたね!!
次はトラックチャンクの読取に挑戦です!

4. トラックチャンクも読み込もう!(ただし、MIDIイベントはスルー)

先ほどと同様にMIDIデータを眺めてみましょう。

$ hexdump ./sample.mid
00000000  4d 54 68 64 00 00 00 06  00 01 00 12 01 e0 4d 54
00000010  72 6b 00 00 00 5d 00 ff  03 00 00 ff 02 13 43 6f
00000020  70 79 72 69 67 68 74 20  28 43 29 20 32 30 31 30
00000030  20 00 ff 01 10 81 79 83  54 83 43 83 67 96 bc 81
00000040  7a 20 55 52 4c 00 ff 51  03 07 a1 20 00 ff 06 05
00000050  53 65 74 75 70 00 ff 58  04 04 02 18 08 83 60 ff
00000060  51 03 07 a1 20 8b 20 ff  06 05 53 74 61 72 74 00
00000070  ff 2f 00
                   4d 54 72 6b 00  00 01 09 00 ff 03 0c 53
00000080  79 73 74 65 6d 20 53 65  74 75 70 00 ff 21 01 00
00000090  00 f0 05 7e 7f 09 01 f7  3c f0 0a 41 10 42 12 40
...(省略)...

4d 54 72 6b : 「MTrk」...チャンクタイプ(「MTrk」なのでトラック!)
00 00 00 5d : 「0x0000005d」=「93」...チャンクのサイズは93byte
00 ff ...(中略)... 2f 00 ...93byte分のデータ(MIDIイベントの羅列

こんな感じの『 MTrk チャンクサイズ イベントの羅列 』が、1つのトラックとして扱われます。
ヘッダチャンクを読み取ったとき、トラック数は18でしたね。
なので、このMIDIファイルはトラックが18個並んでいるということです。(下の表を参考に^^)

MIDI(SMF)データ
 MThd  サイズは 6byte  Format 1  18Tracks  分解能480 
←ヘッダチャンク
 MTrk  サイズは 93byte  [ 93byte分のMIDIイベント] 
←1個目のトラックチャンク
 MTrk  サイズは 265byte  [265byte分のMIDIイベント] 
←2個目のトラックチャンク
 MTrk  サイズは ?? byte  [?? byte分のMIDIイベント] 
←3個目のトラ...
 MTrk  サイズは ?? byte  [?? byte分のMIDIイベント] 
←4個目
 ・・・ 
(5...16個目)
 MTrk  サイズは ?? byte  [?? byte分のMIDIイベント] 
←17個目
 MTrk  サイズは ?? byte  [?? byte分のMIDIイベント] 
←18個目


さて、実際にトラックチャンクを解析してみましょう。
ただし、まだ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イベントの解析をしようと思います!

5. いよいよ、本体データ(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 : 語句修正
以後の更新内容の改善のために、是非ともご評価のほどよろしくお願いします!→

テーマ : サウンド・プログラミング /  ジャンル : コンピュータ

コメントの投稿

非公開コメント

いきなりすいません

誠に図々しいおはなしでありますが、もしよければノートオンとノートオフだけ表示プログラムにするには、このプログラムをどういじればいいかを教えてほしいです。。。
よろしく御願します

Re: いきなりすいません

とらすけです。
ご訪問&ご質問ありがとうございます!
変更する部分を説明しだすと、おそらく私自身が混乱するので、
実際に動かしながら変更してみたプログラムを新しい記事として掲載しました。
是非そちらを御覧ください^^

「C言語でMIDI(SMF)データのNoteOn/NoteOffだけ表示する!」
URL: http://torasukenote.blog120.fc2.com/blog-entry-114.html

掲載したものでご期待に添えたかわかりませんが、
必要な部分が抜けていたりした場合はまたコメントをお送りください!
カテゴリー
ようこそ!
Author: Torasuke
Profile: 地元大学の情報系学部に息をひそめる二回生。
   SA SW


ブログ内検索
最近のコメント
オススメ
京都の大学生のラボブログ
Python,Java,Objective-C,GAE,Macなど
Python独習中の大学生のブログ


ltzz.info
ここの管理者さんには謁見済み!(えっ

 Use OpenOffice.org
無補償でも良いなら、MSOfficeよりOpenOfficeで十分です。

Mozilla Firefox ブラウザ無料ダウンロード
当サイトは、Firefoxというブラウザで動作確認しています。私は以前、IE派でしたが、一度乗り換えて慣れてしまうと、Firefoxのほうが便利だということを実感しました。

是非よろしくお願いします。