↑ページトップへ
表示調整
閉じる
挿絵表示切替ボタン
▼配色
▼行間
▼文字サイズ
▼メニューバー
×閉じる

ブックマークに追加しました

設定
0/400
設定を保存しました
エラーが発生しました
※文字以内
ブックマークを解除しました。

エラーが発生しました。

エラーの原因がわからない場合はヘルプセンターをご確認ください。

ブックマーク機能を使うにはログインしてください。
PR
50/50

エピ36.3 mp3の仕様を少し(続き)


 ***


 エピ36.3 mp3の仕様を少し(続き)


 ***


 前エピでは、サイド情報を解釈してブロックの特定へと進みました。


 今エピではブロックの内容に進みます。スケールファクタとハフマン符号です。


 ***


 最初のフレームのサイド情報です。


   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


 スケールファクタは scfsi = 00000000 と scalefacCompress = [0,0,0,0] の2つを参照します。

 ハフマン符号では、


   bigValues = [144,152,2,65]

   windowSwitchingFlag = [0,0,0,0](及び blockType = [0,0,0,0])

   tableSelect = [(8,1,2),(6,3,2),(9,0,0),(5,1,2)]

   region0Count = [5,5,0,4]

   region1Count = [6,7,0,5]

   count1TableSelect = [1,0,1,0]


 を参照します。残るサイド情報は globalGain, preFlag, scalefacScale の3つです。あと少しです。


 最初のフレームのフレームデータです。382バイトあります。


           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

  (中略)

  1150 : D1 A1 12 E4 41 84 03 97 9D 5C 44 35 E5 05 C5 2C

  1160 : 51 73 F6 B4 79 F2 A5 94 04 41 AA/0E 4E 71 81 65 <-- '/'

  1170 : 6D 39 59 2D E4 2E D7 18 29 ED BD 8E DA 26 82 5C

  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


 サイド情報の mainDataBegin は0です。最初のフレームですからね。

 part23Lengthは 671, 680, 659, 602 です。671+680+659+602 = 2612 で、2612/8 = 326.5 です。

 よって、フレームデータの0〜326の327バイトがこのフレームで使用するメインデータです。このフレームで使用しない327〜381の55バイトは次のフレームで使います。つまり、次のフレームの mainDataBegin は55です。


 *


 メインデータを二進数に変換してブロックに分けます。ブロック0(グラニュール0のチャネル0)は次の 671bit です。


  00000000100010110110111101111111001101111010010101

  10011000110110001100000110011000111111000110001110

  00110001100011000110111111100000010001000000010101

  (中略)

  11111111110000110000111111000010001110000000011111

  11110001111111111010000111101001000011001111000011

  110000111111101011011


 1つのブロックを処理すると、ロングブロックなら 22 個、ショートブロックなら 13*3 個のスケールファクタと、(ロング、ショートとも)ハフマン符号から 576 個のサンプルができます。


 ロングかショートかは、windowSwitchingFlag 及び blockType で判定します(なおミックスは無いので考えないことにする)。


 *


 なお、スケールファクタの変数名は sf にしました。ロングブロックは sf.L[22] で、ショートブロックは sf.S[3][13] な感じです。ハフマン符号のサンブルは is[576] です。変数名は後で出てきます。


 ***


 余談になりますが。メインデータの端数のビットが 1010 となっているのは何故でしょう? 普通に処理したら 0000 にならないかな?


   1914 - 0101010

   1779 - _101010

   1859 - __01010

   1950 - ___1010

   1870 - ____010

   1920 - _____10

   2010 - ______0

   1995 - _______


 気になって他のブロックも調べてみたら、01010101 = 0xAA になっているようだ。...、エンコーダはブラックボックス。


 ***

 **

 *


 スケールファクタです。


 スケールファクタは、ロングブロックなら 22 個、ショートブロックなら 13*3 個できます。

 値の範囲は0〜15です。ハフマン符号のサンブル is[] を


   Pow43(is[i]) * 2.0^(-sf.L[sfb] + ...)


 といった感じで調整します。レベル(音量?)が低いときのレンジ切り換え用ですね(詳しくは後述します)。


 参照するサイド情報は scfsi (2*4bit) と scalefacCompress(4bit *4)の2つです。

 scfsi はチャネル0とチャネル1で各 4bit(各ビットがフラグ)あり、ロングブロックの場合に参照します。

 scfsi の各ビットは、グラニュール0のスケールファクタをグラニュール1にコピーするか(or ブロックのビットを使用して値を得るか)を決めます。各ビットはスケールファクタの0〜5, 6〜10, 11〜15, 16〜20を決めます。21は scfsi の対象外。謎。


 scalefacCompress はブロック毎に 4bit(=0〜15)あり、1個のスケールファクタが使用するビット数である slen0 と slen1 を次のように決めます。


   slen0 = ([0,0,0,0,3,1,1,1,2,2,2,3,3,3,4,4])[scalefacCompress]

   slen1 = ([0,1,2,3,0,1,2,3,1,2,3,1,2,3,2,3])[scalefacCompress]


 ロングブロックの場合、22個のスケールファクタのうち、0〜10の11個の使用ビット数は slen0 で、11〜20の10個は slen1 です。...、21は常に0。謎。

 つまり、(slen0*11 + slen1*10) ビットを使用します。グラニュール1は scfsi によりグラニュール0からコピーすると、その分だけ使用ビット数が減ります。

 使用ビット数は最少0で、最大は 4*11 + 3*10 = 74bit ですね。


 ショートブロックの場合は、0〜17は slen0 で、18〜35は slen1 です。36〜38は常に0です。

 全部で (slen0*18 + slen1*18) ビットを使用します。最大で 4*18 + 3*18 = 126bit です。


 スケールファクタの値は使用ビット数が 0bit なら0で、4bit 使用するなら0〜15です。


 *

 *

 *


 では、最初のフレームのブロック0(グラニュール0のチャネル0)のスケールファクタを特定しましょう。


 グラニュール0なので scfsi は参照せず。ロングブロックなので (slen0*11 + slen*10) ビットを使用して22個の値を得る。


 ...、のだけど...、あれ? scalefacCompress が0で slen0 も slen1 も0だ。つまり、使用ビット数0で、スケールファクタは全部0。...。


 あれ?


 ***

 **

 *


 えと。ハフマン符号です。


 576 個のサンブルができます。サンプルは周波数軸の値で、0〜576 が 0〜22.05Hz に対応します。周波数軸はロングブロックなら 22 個のバンドに区分して、これに 22 個のスケールファクタが対応します。ショートブロックなら 13 個のバンドです。...、このバンドはスケールファクタバンドと言うそうです。長いよ。


 ロングブロックの 22 個のバンドです。最初のバンドは0〜3です。


  sfbIdxL = [0,4,8,12,16,20,24,30,36,44,52,62,74,90,110,134,

       162,196,238,288,342,418,576]


 ショートブロックの 13 個のバンドです。


  sfbIdxS = [0,4,8,12,16,22,30,40,52,66,84,106,136,192]


 使用するビット数は可変です。ハフマン符号ですからね。Deflate のハフマン符号と同様な付属ビットがあります。1ビットの符号サインと最大13ビットのオフセットです。符号は0以外の場合に付属して、オフセットは15の場合に付属します。


 値の範囲は、ハフマン符号では 0〜15 です。これに符号を付加して -15〜15、13ビットのオフセットを付けて -8207〜8207 になります。


 *


 ハフマン符号の符号表は固定で、0〜33の34個あります。


 576 個のサンプルを得るためにどの符号表を使用するかはサイド情報が決めます。...、これが複雑に見えて最初は混乱してました。


 576 個のサンプルを連続する4つの領域リージョンに分けます。領域0〜領域3 です。

 各領域で使用する符号表は、


   領域0: tableSelect[0]

   領域1: tableSelect[1]

   領域2: tableSelect[2]

   領域3: (count1TableSelect+32)


 により決めます。そして領域(の開始と終了)を次のように決めます。


   bv2 = bigValues*2

   if(windowSwitchingFlag==1):

    region0_S = 0

    region1_S = min(bv2,36) # note

    region2_S = bv2

    region3_S = bv2

    region3_E = 576

   elif(windowSwitchingFlag==0):

    region0_S = 0

    region1_S = min(bv2,sfbIdxL[region0Count+1])

    region2_S = min(bv2,sfbIdxL[region0Count+1+region1Count+1])

    region3_S = bv2

    region3_E = 576


 ショートブロックの領域2は常に0個である点に注意かな。


 region0Count(4bit)の最大は15なので region1_S は最大 162 で、region1Count(3bit)の最大は7で、region2_S の最大は 576 です。


 *


 dist10 のコードをそのままスクリプトにしたらサイド情報の暗黙値の設定で困惑。region0Count が、blockType in [1,3] のブロックは region1_S が 36 になる値なのに、blockType==2のブロックは 44 となる値でね。そしてハフマン符号のデコード処理で blockType==2 のブロックは region1_S=36 と設定し直している。ナニコレ。どいういうこと?

 bigValues*2 は 36 未満になることもあるから注意ね。dist10 はデコード処理の for ループにて bigValues*2 を上限にしているからバグらないけど。...、ブツブツ、...。


 ***


 各領域の個数をグラフにしましょう。


 縦軸は各領域の開始/終了です。横軸は時間。

挿絵(By みてみん)

 0〜5秒の区間です。

挿絵(By みてみん)

 青色〜橙色(r0s〜r1s)が領域0、橙色〜緑色(r1s〜r2s)が領域1、緑色〜赤色(r2s〜r3s)が 領域2、そして 赤色〜紫色(r3s〜r3e)が領域3です。


 うん。符号表を選択しながら 576 個のサンブルができるイメージができましたね。


 *


 最初のフレームのブロック0のデコードで使用する符号表と領域を確認します。


 参照するサイド情報は、


   tableSelect = (8,1,2)

   count1TableSelect = 1

   windowSwitchingFlag = 0

   bigValues = 144

   region0Count = 5

   region1Count = 6


 です。これらを参照して、符号表と領域は、


   |領域|符号|_開始〜_終了|_個数|

   |_0|_8|__0〜_23|_24|

   |_1|_1|_24〜_89|_66|

   |_2|_2|_90〜287|198|

   |_3|33|288〜575|288|


 となります。


 *


 早速。ブロック0をデコードしました。...、まあ、ハフマン符号ですからね。


  領域0 - 24 [5, 3, 1, -1, -1, 0, -1, -1, -1, 0, 0, 0, 0, 0, 0, -1, -1, 0, 0, 0, 1, 1, 1, 0]

  領域1 - 66 [0, 0, 0, 0, 0, -1, -1, -1, -1, 0, -1, -1, 1, 1, 0, 0, 0, 0, 0, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0]

  領域2 - 198 [0, 0, 1, 0, 0, 0, 0, -1, -1, 0, 0, 0, -1, -1, -1, -1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, -1, -1, 0, 0, 0, 0, -1, -1, -1, -1, -1, 0, 0, 0, -1, -1, -1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, -1, -1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, -1, -1, -1, -1, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 1, 1, 1, 2, 2, 1, 1]

  領域3 - 164 [-1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, 0, -1, -1, 0, 0, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 0, 0, 0, 0, 0, -1, -1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, -1, -1, -1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, -1, -1, 0, 0, -1]


 領域0〜領域2は事前確認したのと同じ個数のサンプルができましたが、領域3は足りません。ブロック0のビットは全部を使っています。...。


 実は領域3の後には(576 サンプルになるまでの)0の連続が続くのです。...領域4とでも言うべきでしょうか。


 デコーダのソースコードには「Zero out rest.」とか「zero part」等とコメントがあります。...、なお。領域0〜2は「bigvalues area」「long hash table values」「low frequencies (called big values)」とか、領域3は「count1 area」「short (count1table) values」「high frequencies」とかあります。ご参考?までに。


 *


 0や±1ばかりなのは録音開始時で、ほぼ無音だからです。


 スクリプトでデコードした結果は、dist10 と lame3x の出力と比較して一致することを確認しました。15298 個の全部ね。


 *


 領域0だけですけど、ハフマン符号でエンコードしてみます。


   [5, 3, 1, -1, -1, 0, -1, -1, -1, 0, 0, 0, 0, 0, 0, -1, -1, 0, 0, 0, 1, 1, 1, 0]


 サンプル2個を1組にして、符号サインは無視して、符号表の8で変換します。

   +5,+3 ⇒ 000000001

   +1,-1 ⇒ 01

   -1, 0 ⇒ 101

   -1,-1 ⇒ 01

   -1, 0 ⇒ 101

    0, 0 ⇒ 11

    0, 0 ⇒ 11

    0,-1 ⇒ 01

   -1, 0 ⇒ 101

    0, 0 ⇒ 11

   +1,+1 ⇒ 01

   +1, 0 ⇒ 101


 1つ目のサンプルに15があったらオフセットを付けるのですが、ここには無いですね。それからサンプルが0以外なら符号サインを付けします。プラスなら0、マイナスなら1です。

 同様に2つ目のサンプルの付加をします。


   +5,+3 ⇒ 000000001 0 0

   +1,-1 ⇒ 01 0 1

   -1, 0 ⇒ 101 1 _

   -1,-1 ⇒ 01 1 1

   -1, 0 ⇒ 101 1 _

    0, 0 ⇒ 11 _ _

    0, 0 ⇒ 11 _ _

    0,-1 ⇒ 100 _ 1

   -1, 0 ⇒ 101 1 _

    0, 0 ⇒ 11 _ _

   +1,+1 ⇒ 01 0 0

   +1, 0 ⇒ 101 0 _


 「⇒ 」の右側を集めます。


   000000001 00 01 01 101 1 01 11 101 1 11 11 100 1 101 1 11 01 00 101 0


 ブロック0の先頭部分と比べます。


  エンコード 0000000010001011011011110111111100110111101001010

  ブロック0 0000000010001011011011110111111100110111101001010...


 エンコードとブロック0の先頭は一致しますね。OKです。


 *


 参考までに。符号表の8です。


   tbl_8 = [

      [(5,5), "00000000000"],

      [(5,4), "00000000001"],

      [(4,5), "0000000001"],

      [(3,5), "0000000100"],

      [(4,4), "0000000101"],

      [(5,3), "000000001"],

      [(2,5), "000000011"],

      [(5,2), "000000100"],

      [(0,5), "000000101"],

      [(5,0), "000001100"],

      [(3,4), "000001010"],

      [(4,3), "000001011"],

      [(3,3), "000001101"], # ?

      [(1,5), "00000011"],

      [(5,1), "00000100"],

      [(2,4), "00000111"],

      [(4,2), "00001000"],

      [(1,4), "00001001"],

      [(0,4), "00001100"],

      [(4,0), "00001101"],

      [(2,3), "00001110"],

      [(3,2), "00001111"],

      [(1,3), "00010000"],

      [(3,1), "00010001"],

      [(0,3), "00010010"],

      [(3,0), "00010011"],

      [(4,1), "0000101"], # --- ?

      [(2,2), "000101"],

      [(0,2), "000110"],

      [(2,0), "000111"],

      [(1,2), "0010"],

      [(2,1), "0011"],

      [(0,1), "100"],

      [(1,0), "101"],

      [(1,1), "01"],

      [(0,0), "11"],

   ]


 カノニカル・ハフマン符号かなと思って確認したら正規化カノニカルされていなくて残念。

 ビットが乱れているのを見ると整頓したくなりますよね。


 サンプル (4,1) を 7bit のコードにしているは不思議。発生頻度の見積り方を知りたいな。


 ***

 **

 *


 メインデータを4つのブロックに分けて、スケールファクタとハフマン符号を確認しました。


 整数あるいはビット単位の処理はここまでです。誤差のない処理なのでどのデコーダでも同じ結果になるはずです。dist10 と lame3x は一致しました。


 次は実数演算が入ります。


 ***


 間違いの指摘とか疑問とか、ご意見・ご感想とかありましたら、どうぞ感想欄に!


 ***

評価をするにはログインしてください。
ブックマークに追加
ブックマーク機能を使うにはログインしてください。
― 新着の感想 ―
このエピソードに感想はまだ書かれていません。
感想一覧
+注意+

特に記載なき場合、掲載されている作品はすべてフィクションであり実在の人物・団体等とは一切関係ありません。
特に記載なき場合、掲載されている作品の著作権は作者にあります(一部作品除く)。
作者以外の方による作品の引用を超える無断転載は禁止しており、行った場合、著作権法の違反となります。