エピ36.1 mp3の仕様を少し
***
エピ36.1 mp3の仕様を少し
***
mp3のデコーダを評価していたら、デコーダによって結果が割と違うことが分かりました。
特に ffmpeg ですけど。RMSレベルが他より10倍ほど大きくて、それに周期的なノイズが入っているのです。
何がどこで違うのか。気になりますね。
でも詳しく調べるには、....mp3のデコード処理のことを理解しないと難しいです。
それで、mp3の仕様を少しだけ調べてみます。
***
mp3ファイルとは
ここではカセットテープの音をボイスレコーダで録音して出来たファイルのことです(笑)。一般的なことはググってね。
mp3というファイル形式は前世紀の末にできたもので、とても古いのです。最初は「フレーム」として定義された数百バイトのデータの連続でした。mp3の本体は今でもフレームの連続という点は同じ。
大きく違うのはフレーム連続の前や末尾に「タグ」が追加されたこと。タグにより曲名やアルバム名、演者名、演奏時間、年月日、ジャンル?、その他色々な情報を付加できるのです。
歌詞や画像を埋め込んでmp3プレーヤーで表示することもmp3プレーヤーが対応していれば可能らしい。これってカラオケ用?
タグには幾つかのバージョンがあるけど、現在では「ID3v2」が多いらしいです。前に付けるタイプのダグです。タグは最大256MBですって。
ID3v2ならファイル先頭の10バイトで識別できます。このmp3ファイルの先頭10バイトは、
49 44 33 03 00 00 00 00 1F 76
ですから、
49 44 33 __ __ : "ID3" -- ID3v2を意味するファイル識別子
03 00 __ __ __ : 3.0 -- ID3v2 のバージョン
00 __ __ __ __ : 0,0,0 -- 各フラグはオフ
00 00 1F 76 __ : 0x0FF6 -- タグサイズ
...、なので「ID3v2.3」であり、タグは(先頭10バイトを含めて)10 + 0x0FF6 = 0x1000 バイトあることが分かります。
つまり、mp3のフレームは 0x1000 バイトから始まります。
***
フレームとは
■フレーム=|フレームヘッダ|CRC|サイド情報|フレームデータ|
です。
「フレームヘッダ」は4バイトです。
「CRC」は0 or 2バイトです。このmp3ファイルはフレームヘッダから0バイト(CRC無し)です。
「サイド情報」は17 or 32バイトです。このmp3ファイルはフレームヘッダから32バイトです。
残りは「フレームデータ」です。このmp3ファイルの場合、1フレームは417 or 418バイトで、フレームデータは381 or 382バイトです。
フレームの長さ(バイト数)はフレームヘッダで決まります。
*
フレームデータから「メインデータ」を切り出します。
1フレームで処理するデータはフレーム内のデータなのが普通?だと思いますが、...mp3で処理するメインデータは可変長で、フレームから供給されるフレームデータから必要分を取得してメインデータとするのです。
メインデータは同フレームのフレームデータの先頭から始まるか、1つ前のフレームデータの途中から始まる感じです(正確なところは後述します)。
┃フレーム0┃フレーム1┃フレーム2┃フレーム3┃フレーム4┃・・
↑ ↑ ↑ ↑ ↑ ↑
┃メイン0┃メイン1┃メイン2_┃メイン3┃メイン4___┃・・・
何故ならば。メインデータは音声の複雑さによって消費するバイト数が違うからです。消費バイト数に応じてフレーム長を可変にすると、メディアからの読み出し速度に影響して、...都合が悪いのです。多分。
それでフレームデータはビットレートで決めた一定速度で供給することにして、そのギャップを埋めるための工夫をしたのです(...、ビットリザーバーというらしい)。
*
メインデータは4つの「ブロック」に別れます。...2chの場合ね。
ここで「グラニュール(granule)」という謎の言葉が登場します。1つのメインデータには2つのグラニュールが含まれるのです。そう決まっています。
そしてステレオなら、1つのグラニュールに2つのチャネルが含まれます。
■メインデータ=|グラニュール0|グラニュール1|
■グラニュール=|チャネル0|チャネル1|
1チャネルが1ブロックになります。グラニュール0のチャネル0をブロック0、...グラニュール1のチャネル1をブロック3としますね。
メインデータはバイト単位です。最初も最後もバイト境界です。ブロックはビット単位です。ブロックのビット数はサイド情報で分かります。
ブロック0の最初のビットは、メインデータの最初のバイトのMSBです。当り前ですね。
ブロック3の最後のビットは、メインデータの最後のバイトなのでしょうけど、...LSBとは限りません。最後の数ビットは捨てる場合があるようです。
*
グラニュールって、お砂糖と関係があるの? ...、あるよー
グラニュールが2個入りって、コーヒーにお砂糖は2つだから? ...、そうだよー
えー、本当なのー! ...、全てウソさ〜♪
*
ブロックは3種類あります。ロングとショート、それとミックスです。
「ロングブロック」が通常です。音声が短時間で激しく変化するときに「ショートブロック」が登場します。
「ミックスブロック」はロングとショートの中間的なブロックとして仕様に定義されていますが幽霊的なものらしいです。このmp3ファイルには登場しません。
*
ブロックは2つの領域に別れます。
■ブロック=|スケールファクタ|ハフマン符号|
「スケールファクタ」と「ハフマン符号」から、576サンプルのデータができます。
各領域はビット単位の長さで、そのビット数はサイド情報で分かります。
*
ここまでの処理には誤差が発生する余地はなくて、どのmp3デコーダでも同じです。きっとね。
*
*
*
このmp3ファイルの最初のフレームを見てみましょう。0x1000 から始まるフレームです。
1000 : FF FB 92 04 <--- フレームヘッダ
00 00 02 9F 48 41 81 01 12 E2 55 09
1010 : 88 30 18 62 5E 0A 4C 05 12 04 80 00 09 2D 10 62
1020 : 80 50 89 28 <--- サイド情報
00 8B 6F 7F 37 A5 66 36 30 66 3F 18
1030 : E3 18 C6 FE 04 40 56 AB CE 7D 1B E4 3E 73 BC EF
1040 : 42 37 CF CF 46 E7 FD 4E F6 FA 12 73 9C E7 F3 9D
(中略)
1180 : 40 C5 C8 BA D2 E0 C2 97 FA 62 79 7C 16 F6 17 35
1190 : 37 CA 1D 03 26 3A 7B 70 B1 E7 B0 A9 67 F1 76 4F
11A0 : C1 1B <--- フレームデータ
*
フレームヘッダです。FF FB 92 04 の4バイトをビットに変換して各ビットを解釈します。
# 0,1バイト
SYNC_WORD ________ : 111111111111 = フレーム同期
VERSION __________ : 1 = MPEG1
LAY ______________ : 01 = Layer3
ERROR_PROTECTION _ : 1 = CRCなし
# 2バイト
BITRATE_INDEX ____ : 1001 = ビットレート 128kbps
SAMPLING_FREQUENCY : 00 = サンプリング周波数 44.1kHz
PADDING __________ : 1 = パディングあり
EXTENSION ________ : 0 = 拡張なし
# 3バイト
MODE _____________ : 00 = ステレオ(左右独立)
MODE_EXT _________ : 00 = 非適用
COPYRIGHT ________ : 0 = 著作権保護なし
ORIGINAL _________ : 1 = オリジナル
EMPHASIS _________ : 00 = 強調なし
フレームヘッダから分かることは、
・MPEG1 Layer3 である。mp3 とは MPEG-1 Audio Layer-3 ですから
・ビットレートは 128kbps で、サンプリング周波数は 44.1kHz
・ステレオで左右独立である
・CRCなし、パディングあり
です。
なお、MODE は 00=ステレオの他に、01=ジョイント, 10=デュアル, 11=シングルがあります。11 以外は2チャネルです。
更に MODE_EXT で 01=Intensityステレオ, 10=M/Sステレオ, 11=Intensityステレオ&M/Sステレオがあります。けど 00 & 00 でよかったヨ。
なお、上記は MPEG1 での定義で、MPEG2 以降は SYNC_WORD を 11bit、VERSION を 2bit にしてます。MPEG 2 の VERSION は 10 なので SYNC_WORD の 11bit に1が続きますが、MPEG 2.5 の VERSION は 01 なので1の12連続でフレームヘッダを検出しようとすると見付からないです。
*
1フレームの長さ(バイト数)は、ビットレートとサンプリング周波数(とパディングの有無)で決まります。
128kbps と 44.1kHz なら、417バイト(か418バイト)です。1バイトの違いはパディングの有無です。
フレーム長の計算式は
144byte * 128kbps/44.1kHz = 417.959... = 417 + PAD
です。144byte*8 = 1152bit で、1152 = 576*2 で、576 は1ブロックのサンプル数ですけど。どういう意味かな?
ビットレート 128kbps とは、1秒毎に 128kbit をメディアから読み出して、供給することと解釈します。フレーム長(バイト数)を f_len とすると1秒間のフレーム数は 128k/(f_len*8) となります。
1フレームには2グラニュール(= 576*2 サンプル)が入っています。毎秒 44.1k サンプルを出力するには、1秒間に 44.1k/(576*2) フレームが必要です。
よって、
128k/(f_len*8) = 44.1k/(576*2)
となることが必要です。正確には「=」にはならなくて「≒」か、...あるいは「≧」で差分は小さいことが望ましい、...でしょうか。
この式から
f_len = (576*2/8)*128k/44.1k = 417.95918...
が導かれます。小数点以下のバイト数を調整するためにパディングが入るのですね。...、128kbps と 44.1kHz の場合なら、常時パディング有りで余裕があったら省略する、...感じかな。
***
**
*
では、このmp3ファイルからフレームヘッダを検出してみましょう。
フレームヘッダの最初は 0xFF で、次のバイトは 0xFA か 0xFB ですから、これでサーチしてみると、...。
全然ダメです。1フレームは417バイト(か418バイト)のはずのところ、6バイトや37バイト、19バイトで検出されます。
なお、最初のフレームは 0x1000 で見つかりました。実はID3v2は、ID3v2を知らない旧式のmp3デコーダでもフレームヘッダを誤検出しないように配慮しているのです(SYNC_WORD である1の連続が発生しないように回避する。これを unsynchronisation scheme と言うらしい)。
フレームヘッダの検出を、
・BITRATE_INDEX は 1111 にならない
・SAMPLING_FREQUENCY は 11 にならない
・EXTENSION は 1 にならない
...、などの条件を追加しても誤検出は避けられないです。フレームデータにはハフマン符号が含まれているからですね。...。うーむ。
*
最初のフレームは正しく検出できているので、ここでフレーム長を計算して次のフレームはフレーム長以降で検出するガードを追加しましょう。以降のフレームヘッダも同様にガードしながら検出します。
すると、
パディング無(417バイト) 623 個
パディング有(418バイト) 14675 個
のフレームが検出されました。計 15298 個です。
このmp3ファイルは 44.1kHz ですから1フレームで (576*2)/44.1 = 26.122 ミリ秒で、15298 フレームなら 399.621 秒になります。録音時間と合ってますね。
パディング有りのフレームの割合は、
14674/15298 = 0.95927...
でフレーム長(バイト数)の計算式の小数点以下とほぼ同じ(微かに大きい)です。
パディング有23連続→パディング無1個→パディング有24連続→パディング無1個→(元に戻る)というサイクルです。例外は、...パディング有25連続と同48連続が各1つ。最後は9連続だけど除外ね。
フレーム長の計算やパディングの役割は想定通りかな。
*
あれ?
(23+24)/(23+1+24+1) = 47/49 = 0.959183673...
これだと、フレーム長の計算式と一致するな。
f_len = 144*128k/44.1k = 2048/49 = 417 + 47/49
有理数で計算したら分かったのだけど。mp3のビットレートとサンプリング周波数は、
ビットレート:32,40,48,56,64,80,96,112,128,160,192,224,256,320 [kbps]
サンプリング周波数:32.0, 44.1, 48.0 [kHz]
ビットレートはどれも8の倍数だから、32.0kHz と 48.0kHz のフレーム長の計算式は整数になる。
44.1kHz は N+M/49 になるので 49 フレーム中の M フレームをパディング有、他はパディング無にすればよいのでした。
44.1k って色々割り切れてよい数ですけど分母に回ると、...なヤツです。
***
ところでmp3プレーヤーならmp3ファイルを途中からでも再生できますけど。どういう仕組みなのでしょう?
1つにはファイルをオープンしたときに全データを読み出してフレームヘッダを特定する方法が考えられますけど。大きなファイルだと遅いよね。
すると最初のフレームを読んでフレーム長を求めて目的のフレームの位置を概算してシークした後、フレームヘッダを検出することになるかな。1フレームで 26.122 ミリ秒ですから数フレーム 〜 数十フレームくらいズレても大丈夫かな。
ファイルの途中のフレームヘッダを検出する場合は、フレーム長を加算した先が次のフレームヘッダであるかをチェックすれば誤検出を防げそう。心配なら前後の数フレームを調べればよい。
***
**
*
フレームの特定までですが、mp3の仕様を調べました。
力尽きたので、続きは次エピで。
***
間違いの指摘とか疑問とか、ご意見・ご感想とかありましたら、どうぞ感想欄に!
***
2026.9.11 データ差替、推敲、加筆
2026.9.12 微推敲
2026.9.16 微推敲




