My Works
- LEDチェッカ
- 太陽電池式回路テスタ
- TTL Pong
- DSP プリセットラジオ
- 電動式 CVCC 電源装置
- OLED MP3 プレーヤ
- etc.
Report
- MIDI のハードウェア
- aitendo DSPラジオ一覧表
- etc.
Parts Collection
- Logic IC
- Calculator LSI
- CMOS Melody IC
- etc.
主な更新のみ
24/09/11
- MCU-based Melody ICs
24/08/12
- MCU-based Melody ICs
24/03/10
- Logic IC Collection
初版公開:2013/08/20
最終更新: 2015/01/18
デジタルオーディオの再生機器において、MP3 形式(MPEG 1 Layer III audio)の圧縮オーディオファイルは広く使われています。DRM 等の面倒な制約が無く、インターネット配信等で手軽に MP3 形式のオーディオファイルが手に入ることから、自作機器で MP3 形式のファイルを扱う機会も多く存在します。実用的な機器のファームウェアを製作する上で、メタ情報を記述した id3 タグ等のタグ情報を解析することは必要不可欠です。本稿では解析すべきタグとして、 ID3v1、ID3v2タグについての概要を記述します。また、VBR ファイルの再生時間を得るために解析すべき XING ヘッダ、MPEG フレームヘッダ の概要もあわせて記述します。電子工作のみならず、オーディオ関係のソフトウェアを製作する上でも参考になると思います。
[15/01/18] MP4 ファイルのタグ解説も追加
一般的な MP3ファイルは MPEG オーディオフレームの前後に ID3 タグが挟み込む形になっています。現在一般的に使われている ID3 タグには ID3v1、ID3v2 と呼ばれる 2 種類のタグが存在します。
ID3v1 タグは 128 バイトの固定長で、ファイル末尾に配置されます。ID3v1 タグには歌手名・曲名・アルバム名などが記述されますが、サイズが固定長なのでそれほど大きい長さのテキストは入りきりません。また、文字コード等の問題も存在します。
ID3v2 タグは可変長で、ファイル先頭に配置されます。ID3v1 タグを完全に包含するものとなっていて、テキストのメタ情報以外にも、画像、歌詞などさまざまな情報を埋め込むことができるよう、たくさんの種類(id3v2.3 で 74 種類)のタグが規定されています。
他の MPEG 形式ファイルと同じように、MP3 形式ファイルに必須なものは MPEG フレームのみです。タグ情報がある場合とない場合があります。WAVE ファイルのように、オーディオデコーダにヘッダ情報を渡す必要はありません。MPEG オーディオのデコードには MPEG オーディオフレームヘッダ内の情報だけで事足りるので、ID3 タグなどのメタ情報タグは存在しなくても成立します。しかしながら、ほとんどの MP3 形式オーディオファイルにはエンコード時の時点で ID3 タグが付加されています。ID3 タグの付いた一般的な MP3ファイルのデータ構造について下図に示します。
歌手名等の情報は ID3 タグの解析によって得られますが、楽曲の長さは ID3 タグには存在しません。楽曲の長さは総フレーム数、もしくはビットレートとファイルサイズによってのみ規定されるからです。しかしながら、VBR (Variable Bit Rate) 形式の MP3 ファイルにおいては、全体としてのビットレート情報が存在しないため、正確な曲長を得るためにはファイルに存在する全ての MPEG フレームを走査する必要があります。これはアプリケーションによっては時間がかかるので、ファイルを走査することなく楽曲長やシークを可能にするために考えられたのが XING ヘッダと呼ばれるヘッダです。XING ヘッダが存在する場合には、最初の MPEG オーディオフレームの先頭に格納されています。XING ヘッダには総フレーム数を保持するフィールドがあり、これから楽曲全体の長さを計算することができます。また XING ヘッダ の TOC エントリの情報を元に、より正確な VBR ファイルのシーク処理を実現できます。
そのほかに MP3 ファイルに存在する(かもしれない)タグ情報やヘッダに、ID3v1 拡張タグや VBRI ヘッダが存在しますが、これらはめったに使われていません。これらを扱うべき機会はかなり少ないと思われるので、ここでは解説しません。
ID3v1 タグが存在する場合、"TAG" の 3 バイトから始まる MP3 ファイル末尾 128 バイトが ID3v1 タグです。内容は以下の通りです。
ID3v1 タグ:128 バイト
フィールド名 | オフセット | 長さ | 種類 | 説明 |
ヘッダ識別子 | 0 | 3 | 文字列 | "TAG" (54h, 41h, 47h) |
曲名 | 3 | 30 | 文字列 | - |
アーティスト名 | 33 | 30 | 文字列 | - |
アルバム名 | 63 | 30 | 文字列 | - |
年 | 93 | 4 | 文字列 | e.g. "1999" |
コメント | 97 | 28 / 30 | 文字列 | 以下参照 |
トラックの有無 | 125 | 1 | バイナリ | 00h ⇒ トラック有効、コメントは 28 バイト長 それ以外 ⇒ トラック無効、コメントは 28 バイト長 |
トラック | 126 | 1 | バイナリ | 不使用なら 00h |
ジャンル | 127 | 1 | バイナリ | 不使用なら FFh |
注意すべきはすべての文字列は NULL 終端文字列ではないという点です。不使用バイトは NULL (00h) もしくは空白 (20h) で埋められるように規定されています。したがって上表での長さ 30 の文字列をプログラムで処理するためにバッファに格納するためには、少なくとも 31 バイト長の NULL 終端文字列バッファが必要になります。
エンコーディングについての規定はない(と思います)。日本語テキストを格納する場合では、デファクトスタンダードとして Shift-JIS で設定されているか、無効なデータで埋まっている(ID3v2 タグのみが有効)場合が多いようです。
長さ 30 文字までの情報しか埋め込めないという致命的な欠点があります。この点を少し改善するものとして、追加の 30 文字を格納するための ID3v1 拡張タグというものが存在しますが、ID3v2 タグが存在する現在では不要のものであり、ほとんど使われることがありません。
(ID3v2 タグが存在する場合、)"ID3" の 3 バイトから始まる MP3 ファイル先頭からの領域が ID3v2 タグです。
いくつかのサブバージョンが存在しますが、ID3v2.3 と ID3v2.4 が広く使われています。Windows のシェルやいくつかのメディアプレーヤなどでは ID3v2.4 には対応していない場合が多いです。これらの主な違いは、規定されているタグの数と規定されている文字エンコーディングの種類です。とりあえず ID3v2.3 をサポートしていれば、実用的には大きな問題は生じないと思います。以下は v2.3 と v2.4 について解説します。
ファイルの先頭領域 10 バイト が ID3v2 ヘッダです。場合によってその後 6 バイト以上の ID3v2 拡張ヘッダが続く場合があります。これらの内容は下表の通りです。
なお、基本的に ID3v2 ヘッダにおける数値表現は ビッグエンディアン (MSB first) です。
ID3v2 ヘッダ:10 バイト
フィールド名 | オフセット | 長さ | 種類 | 説明 |
ヘッダ識別子 | 0 | 3 | 文字列 | "ID3" (54h, 41h, 47h) |
バージョン | 3 | 2 | バイナリ | タグのバージョンを表す e.g. 03h, 00h -> ID3v2.3.0 |
フラグ | 5 | 1 | フラグ | Bit 7: 非同期化(通常0) Bit 6: 拡張ヘッダ有 Bit 5: 実験中(通常0) Bit 4: (v2.4) ID3v2 フッタ有 |
サイズ | 6 | 4 | Syncsafe Integer |
ID3v2 ヘッダ以降の ID3v2
タグのサイズを示す 注:各ビットは下位 7bit のみが有効 全体として 7x4 = 28 bit の整数を表す |
複数バイト数値の扱いですが、ID3v2 タグにおいては各バイトは 7bit のみが有効であるケースが多いです。読み込み処理は以下の例のように行います。
uint32_t val = (((uint8_t)p[0])<<21) + (((uint8_t)p[1])<<14) + (((uint8_t)p[2])<<7) + (uint8_t)p[3];
このような ID3 タグで使用される数値表現を Syncsafe Integer と呼びます。ID3v2.3 以前ではヘッダ部分にのみ Syncsafe 形式が用いられますが、一方 v2.4 ではほとんどの 4 バイト数値にこの Syncsafe 表現を使用することになっています。
フラグフィールドのビット6 が 1 の場合、次に以下の形式の ID3v2 拡張ヘッダが続きますが、この内容を使うことは少ないですので、詳しくは割愛します。
ID3v2 拡張 ヘッダ:最低 6 バイト
フィールド名 | オフセット | 長さ | 種類 | 説明 |
サイズ | 0 | 4 | (v2.3) 32bit 数値 (v2.4) Syncsafe 数値 |
この拡張ヘッダのサイズを示す (v2.4)注:各ビットは下位 7bit のみが有効 |
拡張フラグなど | 4 | >2 | ? | 使わなくても大丈夫 (ID3v2.3 と ID3v2.4 で異なる) |
ID3v2 ヘッダ / 拡張ヘッダの後に、実際のタグを格納した ID3v2 フレームが続きます。
ID3v2 フレームは最低 1 つ存在しなければいけません。また、すべてのフレームは ID3v2 フレームヘッダ部分を除き最低 1 バイトのデータが存在しなければいけません。
ID3v2 フレームヘッダ:10 バイト(ID3v2.3 以降)
フィールド名 | オフセット | 長さ | 種類 | 説明 |
フレーム ID | 0 | 4 | テキスト | フレーム内容を表す 4 文字の ID |
サイズ | 4 | 4 | (v2.3) 32bit 数値 (v2.4) Syncsafe 数値 |
フレームヘッダを除いたこのフレームのサイズを示す (v2.4) 注:各ビットは下位 7bit のみが有効 frameSize = size + 10 |
フラグ | 8 | 2 | フラグ | 使わなくても大丈夫 |
ID3v2 フレームヘッダ:6 バイト(ID3v2.2)
フィールド名 | オフセット | 長さ | 種類 | 説明 |
フレーム ID | 0 | 3 | テキスト | フレーム内容を表す 3 文字の ID |
サイズ | 3 | 3 | 24bit 数値 | フレームヘッダを除いたこのフレームのサイズさを示す frameSize = size + 6 |
各フレームが何を記述するかは、4 文字のフレーム ID によって決定します。ID3v2.3 でフレーム ID が 3 文字から 4 文字に拡張されました。したがって、現在主流の ID3v2.3 や ID3v2.4 を対象としたプログラムでは、互換性の無い ID3v2.2 タグを扱うことはできません。
各フレームヘッダにはフラグが存在しますが、あまり使いませんので詳しくは割愛します。zlib による圧縮などが定義されていますが、対応しているソフトウェアは少ないでしょう。
フレーム本体はフレームヘッダの後に続く、ヘッダで指定されたサイズ数分のブロックです。フレームサイズの数値表現はバージョンによって異なるので注意が必要です。
ID3v2 フレームは任意の個数連続して存在します。連続するフレームの終了は、フレームヘッダのフレーム ID が有効な文字('A' - 'Z', '0' - '9')のみで構成されているかどうかによって確認します。
フレーム ID は多数存在します(一覧は参考資料にあります)が、このうち使われるフレームID はごくわずかです。ID の最初の文字がおおまかなタグの形式を表します(例えば'T' で始まればテキストなど)。ID3v1 タグで定義されたタグは ID3v2 タグでは以下の通りに対応します。ID3v1 タグで数字によって表現されていたタグは ID3v2 では可変長の文字列によって表現されます。
ID3v2 主なフレーム ID
名前 | ID3v1 | ID3v2.2 | ID3v2.3 |
曲名 | O | TT2 | TIT2 |
アーティスト名 | O | TP1 | TPE1 |
アルバム名 | O | TAL | TALB |
年 | O | TYE | TYER |
コメント | O | COM | COMM |
トラック | O | TRK | TRCK |
ジャンル | O | TCO | TCON |
テキストを表現する各フレームは、下表のようにテキストエンコーディングを示す 1 バイトとフレーム内容を示す NULL 終端テキストからなります。ただし、"COMM" や "SYLT" などの一部タグは下表とは形式が異なります。
文字列フレームの内容
フィールド名 | オフセット | 長さ | 種類 | 説明 |
エンコーディング | 0 | 1 | バイナリ | テキストのエンコーディングを表す 00h: ISO-8859-1 01h: UTF-16 / BOMあり (id3v2.4) 02h: UTF-16BE / BOMなし (id3v2.4) 03h: UTF-8 |
フレーム内容 | 1 | ? | NULL 終端テキスト | フレーム ID で指定された内容を表すテキスト 終端文字: "00h" (ISO-8859-1, UTF-8) "00h 00h" (UTF-16) |
これを見てわかるように、ID3v2 タグでは Shift-JIS のエンコーディングは一切規定されていません。ISO-8859-1 はいわゆる英数字のみを規定したエンコーディングです。したがって、日本語などマルチバイト文字を使う場合は Unicode を用いるべきであり、Shift-JIS を使ってタグを付けることはフォーマット違反となります。しかしながら、大多数のソフトウェアではエンコーディングフィールドを 00h に設定した上で Shift-JIS でタグ付けを行う事を許容しています(Windows のシェルに至っては UTF-16 で書かれたタグを全て Shift-JIS に書き換えてしまいます)。したがって、実用的なソフトウェアを作るためには、このような不正なフォーマットのタグを正常に扱えるように(といっても日本語環境でプログラミングを行う限り、自然に対応できるのですが)するべきです。
ここまでの説明で、ID3 タグを扱うプログラムを書くことができると思います。
ここまで ID3 タグについて説明してきましたが、楽曲の長さの情報は ID3 タグには存在しません。楽曲の長さは MPEG フレーム数、もしくはビットレートとファイルサイズによってのみ規定されるべきだからです。MPEG フレームについては次のセクションで説明します。曲長の計算式を 2 つ示します。
MPEG フレーム数はこれまでに述べたタグから得られます。
VBR (Variable Bit Rate) 形式の MP3 ファイルにおいては、全体としてのビットレート情報が存在しないため、正確な曲長を得るためにはファイルに存在する全ての MPEG フレームを走査する必要があります。これは時間がかかりすぎるので、考えられたのが XING ヘッダと呼ばれるヘッダです。サンプリングレートはファイル内で変わらないので、上記の計算式 1. を適用して、XING ヘッダ内の総フレーム数と適当な MPEG フレームヘッダ内のサンプリングレートを使用すれば、楽曲の曲長が求まります。
XING ヘッダは "Xing" の 4 バイトから始まる、最初の MPEG オーディオフレームのオフセット 32 バイト(ステレオ) または 17 バイト(モノラル) から続く、120 バイトの領域です。もちろん CBR でエンコードされた MP3 ファイルには存在しません。
すべての数値表現は ビッグエンディアン (MSB first) です。
フィールド名 | オフセット(byte) | 長さ(byte) | 説明 |
ヘッダ識別子 | 0 | 4 | "Xing" |
フラグ | 4 | 4 | Bit 1(00 00 00 01): フレーム数が有効 Bit 2: ファイルサイズが有効 Bit 3: TOC エントリが有効 Bit 4: Quality Indicator が有効 |
フレーム数 | 8 | 4 | この MPEG フレームを含む、総フレーム数 |
ファイルサイズ [byte] | 12(標準) | 4 | このファイルの長さ |
TOC エントリ | 16(標準) | 100 | 100個の TOC エントリ(テキスト参照) |
Quality Indicator | 116(標準) | 4 | 品質を示す(あまり使われない) |
フレーム数などフィールドの有効・無効はフラグによって指定されます。もしあるフィールドが無効な場合、そのフィールドはパディングされるのではなく、それ以降のフィールドが前倒しされて配置されます。つまり、フレーム数が無効な場合、ファイルサイズのオフセットは 8 となります。
もうひとつの重要なデータは TOC エントリ (Table Of Contents) です。このTOC エントリは VBR ファイルのシークに利用します。具体的には、ファイルの x [%] の時間位置にシークするとき、ファイルポインタを次式
にセットすることで、時間ベースのシークが可能になります。
前述の通り、MP3 ファイルの基本構造はこの MPEG フレームヘッダです。このヘッダ情報を解析することで、ファイル全体の楽曲長さやシーク位置などが計算できます。
MPEG オーディオフレームヘッダは 各 MPEG オーディオフレーム の先頭 32 bit (4 byte)を占めるヘッダ領域で、デコードに必要な情報を与えます。1 つの MPEG フレームのサイズは次の計算式によって求まります(MPEG 1 Layer III の場合)。
フィールド名 | オフセット(bit) | 長さ(bit) | 説明 |
フレーム同期 | 0 | 11 | 全て1 |
バージョンID | 11 | 2 | 11 |
レイヤー | 13 | 2 | 01 |
保護ビット | 15 | 1 | 1 |
ビットレート | 16 | 4 | 0000 - 1111 : {free, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 予約} |
サンプリングレート | 20 | 2 | 00: 44100 01: 48000 10: 32000 11: 予約 |
パディングビット | 22 | 1 | 0: パディングしない 1: パディングする(フレームサイズに +1 byteする) |
チャンネル | 24 | 2 | 00: ステレオ 01: ジョイントステレオ 10: デュアルチャンネル 11: モノラル |
以下省略 | 26 | 6 | 省略 |
表中「オフセット」は先頭からみたビット数です。
MPEG フレームの例: {FF, FB, 90, 64}
FFh FBh 90h 64h
1111 1111 1111 1011 1001 0000 0011
0010
aaaa aaaa aaab bccd eeee ffg- hh-- ----
a: フレーム同期
b: バージョンID
c: レイヤー
d: 保護ビット
e:
ビットレート(128kbps)
f: サンプリングレート(44,100Hz)
g: パディング(しない)
h:
チャンネル(ステレオ)
習うより慣れろで、実際の MP3 ファイルのタグを見るのは、アプリケーション開発の参考になるかもしれません。YMP3TagViewer は私の製作したソフトウェアで、ファイルに存在するすべての ID3v1、ID3v2 タグを見ることができます。
下図にソフトウェアの実行例を示します。この画像では、 amazon から購入した MP3 ファイルを表示しています。通常のプレーヤでは見えない情報タグが存在することや、2バイト文字の扱いの問題によって ID3v1 タグが無意味な内容となってしまっています。
個人的にはあまり使う機会が少ないのですが、最近は楽曲配信などで MP3 形式のほかに AAC 形式が普及しています。このフォーマットは通常、.mp4 や .m4a の拡張子を持つ MP4 形式のファイルフォーマット (MP4 フォーマット) に格納されます。このセクションでは、 MP4 (MP4v2) フォーマットのタグ(メタ情報)について簡単に触れます。
MP4 フォーマットのファイルは、box と呼ばれる基本ブロックからのみ構成されます。この box はメディアとメタ情報の両方を格納でき、RIFF における チャンクのようなものですが、最大の違いは box は木構造として表現できるという点です。つまり、ある box は複数の box を子として持つことができ、全ての box は 1 つの親(根ノード)からたどれるということです。ただし実際には、根ノードはファイル全体を示す暗黙の box であり、ファイル中に box として定義されているわけではないです。
各 box は先頭に 4 バイトの データサイズ、4 バイトの box 名からなるヘッダを持ちます。この box ヘッダ以降が box の本体となります。なお、データサイズは ビッグエンディアン (MSB First) です。データサイズはヘッダ自体を含むその box が占めるファイルサイズを示します。
1 つの box は複数の子 box を持つ場合があり、こういう box を コンテナ (container box) と呼びます。コンテナかどうかは box 名で判断します。コンテナである場合、box 本体は(子どもを持つ場合)複数の子 box の配列となり、アトリビュートは持ちません。コンテナでない場合、box 本体はコンテナが示す種類のデータそのものです。
それではメタ情報はどこに存在するのでしょうか。MP4 フォーマットでは、ファイル中に親を持たない(=階層の最も高い)box には以下のものが存在する決まりになっています。
("ftyp" box の例)
メタ情報は "moov" box の子どもを辿っていけば見つけることができます。上述の木構造をディレクトリ風に表すならば、有益なメタ情報の box は /moov/udta/ 直下 や /moov/udta/meta/ilst/ 以下に存在するようです。ただし、アプリケーションによっては異なる場所に異なる形式で存在するかもしれません。以下に有益な box 名を示します。
box 名 | 説明 |
perf | アーティスト名 |
titl | タイトル(曲名) |
albm | 親タイトル(アルバム名) |
dscp | コメント |
gnre | ジャンル |
/moov/udta/ 直下 にあるこれらの box 本体のオフセット 2 byte 以降が、メタ情報の NULL 終端文字列です。最初の 2 byte は言語コードを示し、大抵の場合 3 文字の ISO 言語コードを 2 バイトにパックしたバイト列となります。例えば日本語("jpn")なら [0x2A, 0x0E] となります。手っ取り早く実装するならこれは読み飛ばして構わないと思います。言語コードが ISO 言語コードならば最初の 1 byte は必ず 0x40 未満になり、そのときの文字コードは基本的に UTF-8 ですが、BOM が存在する場合は UTF-16 も許容しています。
/moov/udta/meta/ilst/ 以下のメタ情報はもう少し複雑なので、Overview of QTFF の「Metadata」の項を参照してください。/moov/udta/ 直下の情報ともオーバーラップする情報もありますが、アルバムアートなどはこちらの box にのみ格納されます。ちゃんと読み込み処理を実装するとちょっと面倒そうです。
WAVE ファイルにもメタ情報を入れるタグ(LIST チャンク)が存在します。このタグに対応しているソフトウェアは少ないのですが、WAVE ファイルを処理するアプリケーションを開発する上で、このタグを考慮する必要があります。このタグ情報は、RIFF 構造で言うところの LIST チャンク内に存在します。(参考:上画像)
LIST チャンクのヘッダ部分は次のような構造です。なお、他の Web ページの解説では、全てのチャンク ID は小文字の文字列で説明されていることが多いですが、大文字小文字の区別を問わない実装とする必要があります。
フィールド名 | オフセット(bytes) | 長さ(bytes) | 説明 |
チャンクID | 0 | 4 | "LIST" or "list" |
サイズ | 4 | 4 | チャンクID、サイズを除く LIST チャンク全体のサイズ (bytes) |
種類ID | 8 | 4 | リストの種類を示す文字列 ex. "ADTL", "INFO" |
このヘッダに続き、任意個の文字列のリストが続きます。それぞれのリストのサイズは 4 の倍数になるようにアラインメントされなけらばならない点に注意が必要です。
フィールド名 | オフセット(bytes) | 長さ(bytes) | 説明 |
情報ID | 0 | 4 | 下表参照 |
サイズ | 4 | 4 | 情報 ID、サイズを除く LIST チャンク全体のサイズ (bytes) |
文字列 | 8 | 可変 | word-aligned 文字列 |
このページによると、代表的な情報 ID として以下のものがあるようです。規格化されているものではないようなので、これ以外にも様々なタグが使われているかもしれません。
情報ID | 説明 |
IART | アーティスト名 |
INAM | タイトル(曲名) |
IPRD | 親タイトル(アルバム名) |
ICMT | コメント |
ICRD | 作成日 |
ISFT | 使用ソフトウェア名 |
IGNR | ジャンル |