EternalWindows
MP3 / フレームヘッダ

標準MP3ファイルでは、フォーマットがファイルに直接格納されていないことから、 MP3データを解析してフォーマットを初期化することになります。 このとき、大きなヒントとなるのがMP3が定義している規格上の値です。 そもそも、MP3とは映像データ圧縮方式であるMPEGの音声圧縮部分(MPEG Audio Layer3)を 独立させたものであって、元々はCD-ROMへの保存を意図して作られた規格でした。 このため、MP3におけるサンプリングレートというのはCDクオリティを意識してか、 32000Hz、44100Hz、48000Hzという3つの高い値をサポートしているだけで、 それ以外の値を取り得る事は基本的にはありません。 このように、MP3の定義を理解していれば比較的簡単にフォーマットの構成要素を 特定することができるため、まずはその定義について注目していきたいと思います。

ビット MPEG1 MPEG2
004410022050
014800024000
103200016000
11------

上の表は、MPEGバージョンとサンプリングレートの関係を示しています。 ビットの列に書かれている2進数は実際にMP3データに含まれ、 たとえば、この値が01でバージョンがMPEG1だった場合、 サンプリングレートが48000Hzであるということを特定できることになります。 MPEG2は、MPEG1より高い圧縮率をサポートする規格でDVDなどに利用されています。 一般にMP3といえばそのバージョンはMPEG1を指しますが、 MPEG2のアルゴリズムに準じた場合でも拡張子はmp3となります。 ただし、動画における音声ではMPEG1の場合はMP3で圧縮が施されますが、 MPEG2の場合はAACという別の音声圧縮規格が利用されています。

MPEG Audioでは、圧縮に関わるもう1つの要素としてレイヤーが存在します。 レイヤーは1から3段階に別れ、Layer3が最も高い圧縮率を誇っています。 MP3の3はこのLayer3からきており、レイヤーが3でないMPEG AudioはMP3ではありません。 それらは、mpg(Layer1)やmp2(Layer1)といった固有の拡張子を持ち、 定義されているビットレートもMP3のそれとは異なっています。 次に、MPEG1とMPEG2におけるLayer3のビットレートを示します。

ビット MPEG1 MPEG2
0000------
0001328
00104016
00114824
01005632
01016440
01108048
01119656
100011264
100112880
101016096
101119264
1100224128
1101256144
1110320160
1111------

これらのことから分かるように、MP3データにはフォーマットの構成要素は直接格納されていません。 格納されているのはMP3の規格上特定の意味を持つ値であり、 それを先のように予め定義されているテーブルなどと照合することによって、 始めてフォーマットの構成要素を取得することができるのです。 これら規格上の値は、MP3データを構成するフレームの先頭32ビットに存在し、 それは特別にフレームヘッダと呼ばれています。

Layer3における1フレームは、MP3データ内の約1152サンプルに相当します。 たとえば、サンプリングレートが44.1HzであるMP3ならば、 1秒間の音は約40(44100 / 1152)フレームで構成されていると考えれるでしょう。 このような考え方は、MP3データを小刻みにWAVEデータに変換したいような場合には 重要な指針となりますが、そうでない場合は特にフレームについて意識する必要はありません。 個々のフレームのフレームヘッダは主として同一の値であるため、 先頭にあるフレームのフレームヘッダを調べるだけで十分なのです。 次に、フレームヘッダのフォーマットを二進数表記で示します。

IIJJKLMM EEEEFFGH AAABBCCD AAAAAAAA

個々の文字に相当するビットの意味は、次のようになります。

文字 意味
A 同期ヘッダ。 11ビット全てが1となる。
B バージョン。 00 = MPEG2.5, 01 = 予約, 10 = MPEG2, 11 = MPEG1
C レイヤー。 00 = 予約, 01 = Layer3, 10 = Layer2, 11 = Layer1
D エラー保護。 0 = あり, 1 = なし
E ビットレート。 定義されているテーブルのインデックスとして利用する。
F サンプリングレート。 定義されているテーブルのインデックスとして利用する。
G パディング。 0 = なし, 1 = あり
H 拡張。
I チャンネルモード。 00 = ステレオ, 01 = ジョイントステレオ, 10 = デュアルモノラル, 11 = シングルチャネル
J 拡張モード。 チャンネルモードがジョイントステレオの場合利用する。
K 著作権。 0 = なし, 1 = あり
L オリジナル。 0 = コピー, 1 = オリジナル
M エンファシス。 00 = なし, 01 = 50/15ms, 10 = 予約, 11 = CCITTj.17

このように多くのビットに意味が込められていますが、 あくまで本来の目的はMP3フォーマットを初期化することでした。 そのため、基本的にはフォーマットの構成要素となるビットレートとサンプリングレート、 及びチャンネルに関するビットを走査するだけで十分だといえるでしょう。 ビットレートやサンプリングレートはMP3のバージョンによって定義が異なりますから、 バージョンも忘れずに取得しておかなければなりません。

次に示すGetFrameHeaderは、MP3データからFRAMEHEADER構造体を初期化します。 この構造体がフレームヘッダの情報を格納することになります。 引数のlpDataがMP3データの先頭を指しており、 それはまた最初のフレームのフレームヘッダを指していることも意味します。 lpDataの型がLPBYTEであることから、0から3までの添え字を指定することで、 4バイト中の任意のバイトにアクセスできます。

BOOL GetFrameHeader(LPBYTE lpData, LPFRAMEHEADER lpfh)
{
	BYTE  index;
	BYTE  version;
	BYTE  channel;
	BYTE  padding;
	WORD  wFrameSize;
	DWORD dwBitRate;
	DWORD dwSampleRate;
	DWORD dwBitTableLayer3[][16] = {
		{0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0}, // MPEG1
		{0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0} // MPEG2
	};
	DWORD dwSampleTable[][3] = {
		{44100, 48000, 32000}, // MPEG1
		{22050, 24000, 16000} // MPEG2
	};

	if (lpData[0] != 0xff || lpData[1] >> 5 != 0x07)
		return FALSE;
	
	switch (lpData[1] >> 3 & 0x03) {

	case 3:
		version = 1;
		break;

	case 2:
		version = 2;
		break;

	default:
		return FALSE;

	}

	if ((lpData[1] >> 1 & 0x03) != 1)
		return FALSE;

	index     = lpData[2] >> 4;
	dwBitRate = dwBitTableLayer3[version - 1][index];
	
	index        = lpData[2] >> 2 & 0x03;
	dwSampleRate = dwSampleTable[version - 1][index];

	padding = lpData[2] >> 1 & 0x01;
	channel = (lpData[3] >> 6) == 3 ? 1 : 2;
	
	wFrameSize = (WORD)((1152 * dwBitRate * 1000 / dwSampleRate) / 8) + padding;

	lpfh->version      = version;
	lpfh->dwBitRate    = dwBitRate;
	lpfh->dwSampleRate = dwSampleRate;
	lpfh->padding      = padding;
	lpfh->channel      = channel;
	lpfh->wFrameSize   = wFrameSize;

	return TRUE;
}

フレームヘッダから特定の値を抜き出すには、まずその値がヘッダの何バイト目に あるのかを把握し、その上で特定の値をビット演算することになります。 しかし、単純にビット演算の式を見ただけでは何を意味しているのか分かりませんから、 ここでは0x6490fbffという値をフレームヘッダと仮定して話を進めたいと思います。 なお、0x6490fbffの二進数表記は次のようになります。

01000100 10010010 11111011 11111111

では、コードを順に見ていきましょう。

if (lpData[0] != 0xff || lpData[1] >> 5 != 0x07)
	return FALSE;

このコードは、lpDataが指すデータがフレームヘッダかどうかを調べています。 フレームヘッダの第1バイト、及び第2バイトの上位3ビットは必ず1でなければならないため、 2つの条件式を用いてそれらを判定しています。 後者の式が本当に正しいかどうかを確認してみましょう。

  11111011 // 0xfb
>>       5
-----------
  00000111

よって、確かに上位3ビットを取り出しているといえます。 0x07は、0111ですから確かに上位3ビットが1であるかどうかの判定が成立しています。

switch (lpData[1] >> 3 & 0x03) {

case 3:
	version = 1;
	break;

case 2:
	version = 2;
	break;

default:
	return FALSE;

}

このコードは、MP3のバージョンを取得する部分です。 バージョンは第2バイトの4ビット目と5ビット目に相当しますから、 その部分を適切に取り出します。

  11111011 // 0xfb
>>       3
-----------
  00011111

そして、この結果に0x03との論理積を取れば、

  00011111
& 00000011
-----------
  00000011

確かに目的のビットのみを取り出せたといえます。 上記の例ではバージョンの値は3であり、この3という値はバージョンの定義において 1と定義されているため、このMP3ファイルのバージョンは1であることが分かります。

if ((lpData[1] >> 1 & 0x03) != 1)
	return FALSE;

このコードは、MP3のレイヤーが3であるかどうかを調べています。 全てのレイヤーをサポートする場合はこのような判定は不要ですが、 所定のビットを取り出す作業はどのような場合でも必要です。 レイヤーは、第2バイトの2ビット目と3ビット目に相当します。

  11111011 // 0xfb
>>       1
-----------
  01111101

これで後は先のように0x03との論理積を取れば、 最初の2ビット以外は0にすることができるため、 目的の値を取り出せたことになります。

index     = lpData[2] >> 4;
dwBitRate = dwBitTableLayer3[version - 1][index];

ここからは、いよいよフォーマットの初期化に必要なデータの取得に入ります。 まず、ビットレートですが、取り得る値自体はMP3の定義に基づいて dwBitTableLayer3という二次元配列で宣言されています。 1つ目の添え字は、バージョン1か2かどちらのビットレートを利用するのかを表し、 2つ目の添え字は配列のインデックスとして機能します。 このインデックスは、フレームヘッダの第3バイトの上位4ビットに格納されています。

  10010010 // 0x90
>>       4
-----------
  00001001

よって、確かに4ビットを取り出すことができたといえます。 実際にdwBitTableLayer3にアクセスする際はバージョンの値を-1としていますが、 これは単純に配列の添え字が0から始まるということに起因しているだけで、 それ以外に意味があるわけではありません。

index        = lpData[2] >> 2 & 0x03;
dwSampleRate = dwSampleTable[version - 1][index];

サンプリングレートを取得する部分です。 dwSampleTableの構成はビットレートのときと同じように 1つ目の添え字がバージョンを表し、2つ目の添え字が配列のインデックスとなります。 インデックスは、フレームヘッダの第3バイトの3ビット目と4ビット目に相当します。

  10010010 // 0x90
>>       2
-----------
  01000100

そして、この結果に0x03との論理積を取れば、

  01000100
& 00000011
-----------
  00000000

よって、確かに目的の2ビットを取り出すことができたといえます。 この0という値と先で取得したがバージョンが1だったことから、 サンプリングレートが44100Hzになることが分かります。

padding = lpData[2] >> 1 & 0x01;

パディングを取得する部分です。 これは、フレームヘッダの第3バイトの2ビット目に相当します。

  10010010 // 0x90
>>       1
-----------
  01001001

そして、0x01との論理積を取れば、

  01001001
& 00000001
-----------
  00000001

確かに目的のビットを取り出すことができたといえます。

channel = (lpData[3] >> 6) == 3 ? 1 : 2;

チャンネルを取得する部分です。 3という値はMP3の定義においてモノラルとされていますから、 そのときには1(モノラル)を代入し、そうでない場合は2(ステレオ)を代入します。 チャンネルは、フレームヘッダの第4バイトの上位2ビットに相当します。

  01000100 // 0x64
>>       6
-----------
  00000001

よって、確かに目的のビットを取り出すことができたといえます。

既に示したGetFrameHeaderは、前節で示したReadMP3Fileの中で呼ばれていますが、 ReadMP3FileではMPEGLAYER3WAVEFORMAT構造体を初期化するという目的がありました。 しかし、このためには、1フレームのサイズというものを求めておく必要があります。

wFrameSize = (WORD)((1152 / 8 * dwBitRate * 1000) / dwSampleRate) + padding;

この計算式は計算の順序をいくつか入れ替えても同じ答えになるため、 式ではなく、考え方について述べていきたいと思います。 まず、求めるべき最終的単位がバイトであることから、 ビットレート(キロビット)をバイト単位に変換します。 これは、以下のような式で算出することができます。

(dwBitRate * 1000) / 8)

キロビットを1000で掛ければビット単位となり、 それをさらに8で割ればバイト単位になります。 ビットレートは1秒間の音を再生するのに必要なビット数ですから、 今ここで算出した値は1秒間の音を再生するのに必要なバイト数です。 後は、このバイト数に対してフレームサイズがどれほどの割合を占めるのかですが、 これは次の計算式から求めることができます。

1152 / dwSampleRate

Layer3のMP3は1フレームが1152サンプルの音を含んでいるため、 これを1秒間のサンプル数を表すサンプリングレートで割れば、 1152サンプル再生するために必要な秒数を算出することができます。 そして、この値を先の計算式で算出したバイト数に掛ければ、 1152サンプル再生するために必要なバイト数、 即ちフレームサイズを取得できたということになります。 最初に示したwFrameSizeの計算式はこれらの計算を上手にまとめたもので、 パディングを付加しているのはサイズを一定の倍数に保つためです。

wFrameSizeを初期化できれば、各種データをFRAMEHEADER構造体に設定します。 フレームサイズは、フレームヘッダに直接含まれる情報ではないわけですが、 便宜上この構造体に含めておきます。 そして、FRAMEHEADER構造体の初期化が完了すれば、 いよいよMPEGLAYER3WAVEFORMAT構造体の初期化に入ります。

lpmf->wfx.wFormatTag      = WAVE_FORMAT_MPEGLAYER3;
lpmf->wfx.nChannels       = channel;
lpmf->wfx.nSamplesPerSec  = dwSampleRate;
lpmf->wfx.nAvgBytesPerSec = (dwBitRate * 1000) / 8;
lpmf->wfx.nBlockAlign     = 1;
lpmf->wfx.wBitsPerSample  = 0;
lpmf->wfx.cbSize          = MPEGLAYER3_WFX_EXTRA_BYTES;

lpmf->wID             = MPEGLAYER3_ID_MPEG;
lpmf->fdwFlags        = padding ? MPEGLAYER3_FLAG_PADDING_ON : MPEGLAYER3_FLAG_PADDING_OFF;
lpmf->nBlockSize      = wFrameSize;
lpmf->nFramesPerBlock = 1;
lpmf->nCodecDelay     = 0x571;

cbSizeとwFormatTagはRIFF/WAVE形式の説明の際に示したように、 上記で代入している値を常に取ることになります。 nChannelsとnSamplesPerSecは、それぞれフレームヘッダから解析した値を代入し、 nAvgBytesPerSecは1秒間の音を再生するのに必要なバイト数ですから、 ビットレートをバイト単位に変換すればよいことになります。 nBlockAlignとwBitsPerSampleは、それぞれ1と0にするべきとされています。 また、MP3固有のメンバについても上記のように初期化すべきとされています。


戻る