エピ36.2 mp3の仕様を少し(続き)
***
エピ36.2 mp3の仕様を少し(続き)
***
mp3の仕様を調べています。やむを得ないことなのです。
前エピでは、mp3のフレームの構成を調べて、フレームヘッダの検出を確認しました。
今エピはサイド情報です。...CRCは無いです。
***
このmp3ファイルの最初のフレームのサイド情報です。
00 00 02 9F 48 41 81 01 12 E2 55 09 88 30 18 62
5E 0A 4C 05 12 04 80 00 09 2D 10 62 80 50 89 28
この32バイトを256ビットに変換すると、
000000000 000 00000000
001010011111 010010000 10000011 0000 0 /0100000001000100101110/ 0 0 1
001010101000 010011000 10000011 0000 0 /0011000011000100101111/ 0 0 0
001010010011 000000010 10001001 0000 0 /0100100000000000000000/ 0 0 1
001001011010 001000001 10001010 0000 0 /0010100001000100100101/ 0 0 0
となります。20bit + 59bit*4 という構成です。なお、モノラルだと 16bit + 59bit*2 = 136bit = 17byte です。
最初の 20bit はメインデータの各ブロックに共通の情報です。その構成は、
mainDataBegin :9bit
privateBits :3bit
scfsi[ch][band] :1bit *2*4 = 8bit
です。4つある 59bit は4つあるブロックに対応する情報です。その構成は、
part23Length :12bit
bigValues :9bit
globalGain :8bit
scalefacCompress :4bit
windowSwitchingFlag :1bit
REGION_OR_BLOCK :22bit # union
preFlag :1bit
scalefacScale :1bit
count1TableSelect :1bit
ここで REGION_OR_BLOCK は、
windowSwitchingFlag==0 なら REGION で
tableSelect[reg] :5bit *3 = 15bit
region0Count :4bit
region1Count :3bit
windowSwitchingFlag==1 なら BLOCK で
blockType :2bit
mixedBlockFlag :1bit
tableSelect[reg] :5bit *2 = 10bit # note "*2"
subblockGain[win] :3bit *3 = 9bit
です。先の256ビットをこの構成に従って数値に変換すると、
0 0 00000000
671 144 131 0 0 {(8 1 2) 5 6} 0 0 1
680 152 131 0 0 {(6 3 2) 5 7} 0 0 0
659 __2 137 0 0 {(9 0 0) 0 0} 0 0 1
602 _65 138 0 0 {(5 1 2) 4 5} 0 0 0
となります。
*
サイド情報は、...とりあえず必要なのは mainDataBegin と part23Length の2つです。
mainDataBegin : メインデータを切出す際のバイト位置
part23Length : 各ブロックのビット長
どちらも重要そうな雰囲気ありますね。
一方、最後まで必要なさそうなのは privateBits です。誰も参照しないのです。値も0だけ。ボッチです。
mixedBlockFlag もミックスブロックが無いので値は0のままですが、ミックスブロックを処理するコードは実装されていて、いちおう...、と言うかミックスブロックではないことを判定するために使用されます。そんな寂しいフラグ。
*
フレームの通番を f_num とします。フレームは 15298 個あるので 0〜15297 まで変化します。フレームデータとメインデータと共用です。
***
**
*
サイド情報の mainDataBegin(9bit)です。
メインデータは1つ前のフレームのフレームデータから始まることが多いです。その位置を決めるのが mainDataBegin です。
mainDataBegin は 9bit あるので最大 511 になる可能性があります(...そうなるmp3ファイルを作ることは可能という意味で)。
フレームデータは 128kbps & 44.1kHzなら、381 or 382バイトですから、2つ前のフレームデータから始まるかも知れないです(...、32kbps & 48kHz なら60or61バイトだから9フレーム前と言うこともあるな)。
mainDataBegin の最少は0です。最初のフレームは前のフレームはないから当然に0。最初のフレーム以外でも0になることはあるかも。
*
とりあえず mainDataBegin をグラフにして見ましょう。カセットテープの音の振幅と比べます。
上段は mainDataBegin で、下段は音の振幅です。
横軸は時間で、単位は秒です。mainDataBegin の横軸は f_num を時間に変換しました(t = f_num*(576*2/44100))。
mainDataBegin の最少は0ですが、最初(f_num=0)以外でも発生していますね。3.1 秒(f_num=122)と 396.1 秒(f_num=15166)の2つです。
0より大きな値は19で40点あります。1〜18は無いです。
最大は490です。次は489で1点、485で2点。...以下ボツボツです。381以上ならメインデータは2つ前のフレームデータから始まるのですが割と普通に発生してます。意外です。
mainDataBegin は音の振幅と関係があります。
無音とピー音では(音が単調なので?)メインデータの消費バイトが少なくなり、mainDataBegin の値は増加して425〜475程になる。
データ部分(ギャラギャラ音)になると消費バイトが増えて、mainDataBegin は減少して20〜40程になる。
mainDataBegin を決めているのは(ボイスレコーダの)エンコーダなのですけど、最少0と最大511の範囲から20程のマージンをとっている気配を感じます。
他と同じくピー音のはずの 185〜205 秒の区間は何かあるのかな?
***
**
*
次は part23Length (12bit * 4)です。
ブロックの長さはビット単位で可変長です。part23Length でそのビット数が分かります。
part23Length は 12bit で、4つあるので合計 (2^12)*4 = 16384 ビット(2048バイト)が最大です。フレーム長は一番長いと、320kbps, 32kHz のときに1440バイトになる。なるほど。
とりあえずグラフにします。
縦軸は part23Length です(値はブロックのビット長を表します)。横軸は時間で単位は秒です。
当然のことですけど。mainDataBegin と関係がありますね。
フレームデータから供給されるバイト数と、4ブロックで消費するバイト数によって増減するバッファ(ビットリザーバー?)の様子をグラフにして見ましょう。
縦軸は mainDataBegin とバッファ残量で、単位はバイトです。横軸は時間。
バッファが増減する様子は mainDataBegin の変化と似ているのですが、ズレが発生している箇所が2つあります。それは mainDataBegin が0となるところです。
拡大して0〜4秒を見ます。
3.18秒(f_num=122)でバッファに290バイト残っていますが、mainDataBegin が0なのでバッファは捨てて、f_num=122 のフレームデータの先頭からをメインデータとして使います。
もったいないよ。...、と言うか、ここって秘密のデータを保存するのに利用できるね。普通に再生できるmp3ファイルだけど、小さなデータを密かに配布できる。ヤバくない?
いや。ちゃんと仕様を公開して。フレーム通番とか経過時間を埋め込むと有益ではないか?
*
サイド情報の mainDataBegin と part23Length の動きを把握できましたね。
***
フレームデータからメインデータを取得するには次にようにします。
tmp = buf[-mainDataBegin:] if mainDataBegin >= 1 else []
buf = (buf+fd0)[-512:]
md0 = tmp+fd0
mainDataBegin が1以上ならバッファ(変数 buf)の後ろから mainDataBegin のバイト数だけ取得して、一時保存(変数 tmp)します。mainDataBegin が0なら tmp = [] です。
バッファとフレームデータ(変数 fd0)を加算して、後ろから512バイトを取り、バッファを更新します。
変数 tmp と fd0 を合わせたものがメインデータ(変数 md0)になります。
*
メインデータ(変数 md0)のうち、ブロックのデータとして使用するのは part23Length の分だけです。
p23s = (part23Length[0]+part23Length[1]
+part23Length[2]+part23Length[3])/8.0
md1 = md0[:int(p23s+0.999)]
ブロックで使用するデータは370〜390バイトくらいで、観察した範囲では最小315バイト、最大446バイトでした。平均は382前後。
使用しなかったデータは、次のフレームのメインデータとして(バッファを参照して)使用されるのです。
このmp3ファイルでは2回バッファを捨てているのですが一般的な処理なのかは不確かです。
***
**
*
ブロックを特定するところまで辿り着きました。
次はスケールファクタと、...ハフマン符号です! きっと楽しいです(主に自分が!)
***
間違いの指摘とか疑問とか、ご意見・ご感想とかありましたら、どうぞ感想欄に!
***
2026.9.14 微推敲
2026.9.15 微加筆




