/* *** midtotxt.c: SMF ファイルをテキスト形式に変換する *** * (1999.8.12: YH) * (1999.8.17 (revised)) * (2017.10.31 (revised)) * * * 使用法: midtotxt [-options] [SMF-file] * 出力は stdout へ。 * * options * b: バイト情報のみを出力 * n: ノート情報のみを出力 * *** このプログラムはあくまで SMF ファイルのバイト列を可視化するだけの * ものであり、実用的価値は低い(PMML の逆コンパイラなどを利用すること)。 * * 入力の MIDI ファイルにはエラーはないものとし、ファイル内容についての * エラー処理は省略してある。 */ #include #include #include /* option flags */ int byteflag = 0; /* 1 ならバイト情報のみを出力 */ int noteflag = 0; /* 1 ならノート情報のみを出力 */ #define MAXLEN 1000000 /* SMF ファイル読み込みバッファの大きさ */ int format; /* フォーマット: 0/1/2 */ int tracks; /* トラック数 */ int tqtm; /* 時間単位 */ int ntrack; /* 現在のトラック番号 (0, 1, 2, ...) */ int ttime; /* 現在のトラック中の通算時間 */ int delta; /* イベントのデルタ・タイム */ int status; /* イベントのステータス・バイト */ int start; /* イベント(ないしチャンク)の先頭バイト */ int trackbytes; /* トラック・データセクションのバイト数 */ int events; /* 総イベント数 */ int tevents; /* トラックのイベント数 */ /* *** mid ファイル読み込み *** */ FILE *fin; char filename[100]; /* 入力 SMF ファイル名 */ unsigned char mbuf[MAXLEN]; /* SMF ファイル読み込みバッファ */ int mlen; /* SMF ファイルの長さ(バイト数) */ int mp; /* 現在のバイト位置 */ void readfile() /* SMF ファイルを内部バッファに読み込む */ { int c; mlen = 0; while ((c = getc(fin)) != EOF) { if (mlen >= MAXLEN) { fprintf(stderr, "*** SMF file too large ***\n"); exit(1); } mbuf[mlen++] = c; } mp = 0; } /* *** 数値入力 *** */ #define getb() mbuf[mp++] /* 1バイトの読み込み */ #define ungetb() mp-- int getn(b) /* 固定長数値の読み込み b: バイト数 */ int b; { int n, i; for (i=0, n=0; i from) printf(" "); printf("%02x", mbuf[i]); if ((mbuf[i] & 0x80) == 0) break; } from = i+1; printf("]+"); if (runstatus) printf("<%02x>+", runstatus); printf("["); } for (i=from; i from) printf(" "); printf("%02x", mbuf[i]); } if (textflag) { printf(" \""); for (i=xto; i 2)) { /* error: shouldn't happen */ } tracks = getn(2); /* error check omitted */ tqtm = getn(2); /* error check omitted */ putheader(start, mp, 1, 0); if (byteflag) return; printf("\t\t*** Header Chunk ***\n"); printf("\t\t Format : %d\n", format); printf("\t\t # of Tracks : %d\n", tracks); printf("\t\t Time quantum: %d\n", tqtm); printf("\n"); } /* *** トラック読み込み *** */ static int textflag; static char obuf[1000]; static int op; void event_error() /* 解析不能イベント:異常終了 */ { putheader(start, start+10, 0, 0); printf("\t*** unknown event, aborting ***\n"); exit(1); } static char *notenames[12] = { /* 音名(note-number % 12) */ "C ", "C#/Db", "D ", "D#/Eb", "E ", "F ", "F#/Gb", "G ", "G#/Ab", "A ", "A#/Bb", "B ", }; #define print_note(note) \ op += sprintf(obuf+op, " [%d%s]", note/12-1, notenames[note % 12]) #define Controlmsg(str) \ op += sprintf(obuf+op, " (%s (%s))", str, (d1 & 0x20)? "LSB": "MSB") void Control_event(d1, d2) /* control change (0xBx) メッセージの詳細 */ int d1, d2; { switch(d1) { case 0x00: case 0x20: Controlmsg("bank select"); break; case 0x01: case 0x21: Controlmsg("modulation"); break; case 0x02: case 0x22: Controlmsg("breath control"); break; case 0x04: case 0x24: Controlmsg("foot control"); break; case 0x05: case 0x25: Controlmsg("portamento time"); break; case 0x06: case 0x26: Controlmsg("data entry"); break; case 0x07: case 0x27: Controlmsg("main volume"); break; case 0x08: case 0x28: Controlmsg("balance control"); break; case 0x0a: case 0x2a: Controlmsg("panpot"); break; case 0x0b: case 0x2b: Controlmsg("expression"); break; case 0x0c: op += sprintf(obuf+op, " (effect control (KORG))"); break; case 0x0d: op += sprintf(obuf+op, " (effect control (KORG))"); break; case 0x40: op += sprintf(obuf+op, " (hold 1 (damper) on/off)"); break; case 0x41: op += sprintf(obuf+op, " (portamento)"); break; case 0x42: op += sprintf(obuf+op, " (sostenuto)"); break; case 0x43: op += sprintf(obuf+op, " (soft)"); break; case 0x45: op += sprintf(obuf+op, " (hold 2 (freeze))"); break; case 0x48: op += sprintf(obuf+op, " (release time (KORG))"); break; case 0x49: op += sprintf(obuf+op, " (attack time (KORG))"); break; case 0x4a: op += sprintf(obuf+op, " (brightness (KORG))"); break; case 0x54: op += sprintf(obuf+op, " (portamento control (Roland))"); break; case 0x5b: op += sprintf(obuf+op, " (external effect depth)"); break; case 0x5c: op += sprintf(obuf+op, " (tremolo depth)"); break; case 0x5d: op += sprintf(obuf+op, " (chorus depth)"); break; case 0x5e: op += sprintf(obuf+op, " (celeste depth)"); break; case 0x5f: op += sprintf(obuf+op, " (phaser depth)"); break; case 0x60: op += sprintf(obuf+op, " (data increment)"); break; case 0x61: op += sprintf(obuf+op, " (data decrement)"); break; case 0x62: op += sprintf(obuf+op, " (NRPN LSB)"); break; case 0x63: op += sprintf(obuf+op, " (NRPN MSB)"); break; case 0x64: op += sprintf(obuf+op, " (RPN LSB)"); break; case 0x65: op += sprintf(obuf+op, " (RPN MSB)"); break; case 0x78: op += sprintf(obuf+op, " (all sound off)"); break; case 0x79: op += sprintf(obuf+op, " (reset all controllers)"); break; case 0x7a: op += sprintf(obuf+op, " (local control)"); break; case 0x7b: op += sprintf(obuf+op, " (all notes off)"); break; case 0x7c: op += sprintf(obuf+op, " (omni mode off)"); break; case 0x7d: op += sprintf(obuf+op, " (omni mode on)"); break; case 0x7e: op += sprintf(obuf+op, " (mono mode on)"); break; case 0x7f: op += sprintf(obuf+op, " (poly mode on)"); break; default: break; } } void MIDI_event() { int message, channel, d1, d2; op += sprintf(obuf+op, "\t\t "); message = status & 0xf0; channel = status & 0x0f; switch(message) { case 0x80: /* note off */ d1 = getn(1); d2 = getn(1); op += sprintf(obuf+op, "#%02d note off %3d %3d", channel, d1, d2); print_note(d1); break; case 0x90: /* note on */ d1 = getn(1); d2 = getn(1); op += sprintf(obuf+op, "#%02d note on %3d %3d", channel, d1, d2); print_note(d1); break; case 0xa0: /* polyphonic key pressure */ d1 = getn(1); d2 = getn(1); op += sprintf(obuf+op, "#%02d poly. key pressure %3d %3d", channel, d1, d2); print_note(d1); break; case 0xb0: /* control change */ d1 = getn(1); d2 = getn(1); op += sprintf(obuf+op, "#%02d control change %3d %3d", channel, d1, d2); /* 個別内容については以下で追記出力 */ Control_event(d1, d2); break; case 0xc0: /* program change */ d1 = getn(1); op += sprintf(obuf+op, "#%02d program change %d", channel, d1); break; case 0xd0: /* channel pressure */ d1 = getn(1); op += sprintf(obuf+op, "#%02d channel pressure %d", channel, d1); break; case 0xe0: /* pitch bend */ d1 = getn(1); d2 = getn(1); op += sprintf(obuf+op, "#%02d pitch bend %d", channel, ((d2 << 7) | d1)-0x2000); break; case 0xf0: switch(channel) { /* common messages */ case 0x01: d1 = getn(1); op += sprintf(obuf+op, "quarter frame %d", d1); break; case 0x02: d1 = getn(1); d2 = getn(1); d1 |= d2 << 7; op += sprintf(obuf+op, "song position pointer %d", d1); break; case 0x03: d1 = getn(1); op += sprintf(obuf+op, "song select %d", d1); break; case 0x06: op += sprintf(obuf+op, "tune request"); break; /* real-time messages */ case 0x08: op += sprintf(obuf+op, "timing clock"); break; case 0x0a: op += sprintf(obuf+op, "start"); break; case 0x0b: op += sprintf(obuf+op, "continue"); break; case 0x0c: op += sprintf(obuf+op, "stop"); break; case 0x0e: op += sprintf(obuf+op, "active sensing"); break; default: event_error(); } break; default: event_error(); /* shouldn't happen */ } if (runstatus) { op += sprintf(obuf+op, " *"); } op += sprintf(obuf+op, "\n"); } void SysEx_event() { int len = getv(); op += sprintf(obuf+op, "\t\t system exclusive\n"); mp += len; } static char *sharpkey[8][2] = { /* 調名:嬰(シャープ)系 */ { "C major", "A minor" }, { "G major", "E minor" }, { "D major", "B minor" }, { "A major", "F# minor" }, { "E major", "C# minor" }, { "B major", "G# minor" }, { "F# major", "D# minor" }, { "C# major", "A# minor" }, }; static char *flatkey[8][2] = { /* 調名:変(フラット)系 */ { "C major", "A minor" }, { "F major", "D minor" }, { "Bb major", "G minor" }, { "Eb major", "C minor" }, { "Ab major", "F minor" }, { "Db major", "Bb minor" }, { "Gb major", "Eb minor" }, { "Cb major", "Ab minor" }, }; void Meta_event() { int n, event_type; op += sprintf(obuf+op, "\t\t "); switch(event_type = getb()) { case 0x00: if (getb() != 0x02) event_error(); op += sprintf(obuf+op, "sequence number: %d\n", getn(2)); break; case 0x01: op += sprintf(obuf+op, "text"); goto TEXT; case 0x02: op += sprintf(obuf+op, "copyright notice"); goto TEXT; case 0x03: op += sprintf(obuf+op, "sequence/track name"); goto TEXT; case 0x04: op += sprintf(obuf+op, "instrument name"); goto TEXT; case 0x05: op += sprintf(obuf+op, "lyric"); goto TEXT; case 0x06: op += sprintf(obuf+op, "marker"); goto TEXT; case 0x07: op += sprintf(obuf+op, "cue point"); case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f: op += sprintf(obuf+op, "other texts"); TEXT: n = getv(); textflag = mp; op += sprintf(obuf+op, " (%d bytes)\n", n); mp += n; break; case 0x20: case 0x21: /* patch */ if (getb() != 0x01) event_error(); op += sprintf(obuf+op, "MIDI channel prefix: %d\n", getn(1)); break; case 0x2f: if (getb() != 0x00) event_error(); op += sprintf(obuf+op, "end of track (%d events)\n", tevents); break; case 0x51: if (getb() != 0x03) event_error(); op += sprintf(obuf+op, "set tempo: %d\n", getn(3)); break; case 0x54: if (getb() != 0x05) event_error(); op += sprintf(obuf+op, "SMPTE offset"); n = getn(1); op += sprintf(obuf+op, " %d - %02d", n >> 5, n & 0x1f); n = getn(1); op += sprintf(obuf+op, ":%02d", n); n = getn(1); op += sprintf(obuf+op, ":%02d", n); n = getn(1); op += sprintf(obuf+op, ".%02d", n); n = getn(1); op += sprintf(obuf+op, " %3d\n", n); break; case 0x58: if (getb() != 0x04) event_error(); op += sprintf(obuf+op, "time signature:"); n = getn(1); op += sprintf(obuf+op, " %d", n); n = getn(1); op += sprintf(obuf+op, "/%d", 1 << n); n = getn(1); op += sprintf(obuf+op, " %d", n); n = getn(1); op += sprintf(obuf+op, " %d\n", n); break; case 0x59: if (getb() != 0x02) event_error(); op += sprintf(obuf+op, "key signature:"); n = getn(1); if (n & 0x80) { n = 0x100 - n; op += sprintf(obuf+op, " %s\n", flatkey[n][getn(1)]); } else { op += sprintf(obuf+op, " %s\n", sharpkey[n][getn(1)]); } break; case 0x7f: n = getv(); sprintf(obuf+op, "sequencer specific (%d bytes)\n", n); mp += n; break; default: event_error(); } } void get_event() /* イベント1コの処理 */ { int b, savstatus; start = mp; textflag = 0; op = 0; runstatus = 0; tevents++; delta = getv(); ttime += delta; b = getb(); if (b & 0x80) { savstatus = status; switch(status = b) { default: MIDI_event(); if (status >= 0xf8) status = savstatus; break; case 0xf0: case 0xf7: SysEx_event(); break; case 0xff: Meta_event(); status = savstatus; break; } } else { /* running status */ ungetb(); runstatus = status; MIDI_event(); } b = status & 0xf0; if (noteflag && (b != 0x80) && (b != 0x90)) return; putheader(start, mp, 0, textflag); if (byteflag) return; printf("%s", obuf); } void TrackChunk() /* トラック・チャンク処理 */ { int endtrack; start = mp; ttime = 0; tevents = 0; /* header part */ if (strncmp((char *)(mbuf+mp), "MTrk", 4)) { /* first 4 bytes are "MTrk"? */ /* error: shouldn't happen */ } mp += 4; trackbytes = getn(4); endtrack = mp + trackbytes; putheader(start, mp, 1, 0); if (!byteflag) printf("\t\t*** Track Chunk %d (%d bytes) ***\n\n", ntrack, trackbytes); while (mp < endtrack) get_event(); events += tevents; if (!byteflag) printf("\n"); } /* *** トップレベル *** */ void main(argc, argv) int argc; char **argv; { int i, j; fin = stdin; strcpy(filename, "(stdin)"); for (i=1; i