EternalWindows
MP3 / RIFF/WAVE形式

Winows標準の音声方式であるWAVEは、デジタル化された波形データで構成され、 それはサウンドボードに対する唯一の入力値となっています。 このため、アプリケーションが扱うマルチメディアAPIの水準を問わず、 システム内部では最終的にWAVEデータが扱われることなっていますが、 このデータがファイルとして保存されること自体は、あまり好ましいことではありません。 ご存知のように、WAVEデータのサイズというのは非常に重く、 これが及ぼすハードディスク容量やネットワーク転送への負荷を考えた場合、 せめてファイルという媒体で扱う際には音声データを何らかの形式で圧縮し、 データの持ち運びを容易にするなどの工夫は不可欠だといえるはずです。 音声圧縮方式のひとつであるMP3は、その圧縮率としてWAVEの約10分の1を誇り、 この形式に準拠したMP3ファイルは現在でも数多く普及しています。

MP3ファイルは、俗に標準MP3ファイルと呼ばれる形式と、 RIFF/WAVE形式のMP3ファイルの2種類に分かれます。 前者の形式は、id3タグ呼ばれる領域に曲に関する情報を埋め込むことができ、 それらの情報を格納しないにしても、多くのMP3ファイルがこの形式を採用しています。 一方、後者の形式は厳密には拡張子が.wavであるWAVEファイルであり、 通常のWAVEファイルと同じようにRIFF形式で構成されています。 このため、RIFF/WAVE形式のMP3ファイルの読み取りは、 WAVEファイルの読み取りとほぼ同じように行うことができ、 MP3ファイルを再生するための方法を習得するにあたっては、 まずこちらの形式から対処していくのが近道だといえるでしょう。 そのうえで、現在普及している標準MP3ファイルの内容について見てきたいと思います。

基本的にはRIFF/WAVE形式のMP3ファイルは、通常のWAVEファイルに格納されているデータを MP3固有のものに置き換えたものであると考えることができます。 RIFF/WAVE形式のMP3ファイルがRIFF形式で構成されていることから、 チャンクという単位でデータを格納するという仕組みは変わりませんが、 そのデータの形式自体は、通常のWAVEファイルとは異なります。 具体的には、fmt チャンクにはMPEGLAYER3WAVEFORMAT構造体が格納されています。

typedef struct mpeglayer3waveformat_tag {
  WAVEFORMATEX  wfx;
  WORD          wID;
  DWORD         fdwFlags;
  WORD          nBlockSize;
  WORD          nFramesPerBlock;
  WORD          nCodecDelay;
} MPEGLAYER3WAVEFORMAT;

この構造体の詳細については現在、マイクロソフトのリファレンスからは参照できませんが、 基本的に開発者が必要とするのはwfxメンバのみです。 MP3のフォーマットにも関わらず、WAVEFORMATEX構造体が含まれるのは不思議に思えますが、 MP3にもWAVEと同じようにサンプリングレートやチャンネルなどの概念が存在するため、 これらを表すのにはWAVEFORMATEX構造体が適しているのです。 また、wfxメンバがMPEGLAYER3WAVEFORMAT構造体の先頭に定義されていることから、 誤って通常のWAVEファイルを読み取って場合でも、 WAVEFORMATEX構造体は正しく初期化されることになりますから、 wFormatTagメンバで読み取ったフォーマットの形式を確認することができます。

今回のプログラムは、RIFF/WAVE形式のMP3ファイルからMPEGLAYER3WAVEFORMAT構造体を取得し、 wfxメンバに含まれる一部のメンバの値を表示します。

#include <windows.h>
#include <mmreg.h>

#pragma comment (lib, "winmm.lib")

BOOL ReadMP3File(LPTSTR lpszFileName, LPMPEGLAYER3WAVEFORMAT lpmf);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	TCHAR                szBuf[256];
	MPEGLAYER3WAVEFORMAT mf;

	if (!ReadMP3File(TEXT("sample.wav"), &mf))
		return 0;
	
	wsprintf(szBuf, TEXT("wFormatTag %x, cbSize %d, nSamplesPerSec %d, nChannels %d"),
		mf.wfx.wFormatTag, mf.wfx.cbSize, mf.wfx.nSamplesPerSec, mf.wfx.nChannels);

	MessageBox(NULL, szBuf, TEXT("OK"), MB_OK);
	
	return 0;
}

BOOL ReadMP3File(LPTSTR lpszFileName, LPMPEGLAYER3WAVEFORMAT lpmf)
{
	HMMIO    hmmio;
	MMRESULT mmr;
	MMCKINFO mmckRiff;
	MMCKINFO mmckFmt;
	
	hmmio = mmioOpen(lpszFileName, NULL, MMIO_READ);
	if (hmmio == NULL) {
		MessageBox(NULL, TEXT("ファイルのオープンに失敗しました。"), NULL, MB_ICONWARNING);
		return FALSE;
	}
	
	mmckRiff.fccType = mmioStringToFOURCC(TEXT("WAVE"), 0);
	mmr = mmioDescend(hmmio, &mmckRiff, NULL, MMIO_FINDRIFF);
	if (mmr != MMSYSERR_NOERROR) {
		mmioClose(hmmio, 0);
		return FALSE;
	}
	
	mmckFmt.ckid = mmioStringToFOURCC(TEXT("fmt "), 0);
	mmioDescend(hmmio, &mmckFmt, &mmckRiff, MMIO_FINDCHUNK);
	mmioRead(hmmio, (HPSTR)lpmf, mmckFmt.cksize);
	mmioAscend(hmmio, &mmckFmt, 0);
	if (lpmf->wfx.wFormatTag != WAVE_FORMAT_MPEGLAYER3) {
		MessageBox(NULL, TEXT("RIFF/WAVE形式のMP3ファイルではありません。"), NULL, MB_ICONWARNING);
		mmioClose(hmmio, 0);
		return FALSE;
	}

	mmioAscend(hmmio, &mmckRiff, 0);
	mmioClose(hmmio, 0);

	return TRUE;
}

MPEGLAYER3WAVEFORMATを扱うときには、mmreg.hをインクルードします。 ReadMP3Fileの処理は、通常のWAVEファイルの読み取りとほぼ同様です。 まず、ファイルにRIFFチャンクが存在することとフォームタイプがWAVEであることを調べ、 次にfmt チャンクからフォーマットを取得します。 このフォーマットは、場合によっては通常のWAVEファイルのフォーマット、 つまりフォーマットタグがWAVE_FORMAT_PCMである可能性がありますから、 MP3のフォーマットであるかを確認しなければなりません。

if (lpmf->wfx.wFormatTag != WAVE_FORMAT_MPEGLAYER3) {
	MessageBox(NULL, TEXT("RIFF/WAVE形式のMP3ファイルではありません。"), NULL, MB_ICONWARNING);
	mmioClose(hmmio, 0);
	return FALSE;
}

WAVE_FORMAT_MPEGLAYER3はMP3のフォーマットタグであるため、 MP3かどうかの判定はこの定数を利用するのが適切だといえるでしょう。 より確実な判定を求めるならcbSizeメンバがMPEGLAYER3_WFX_EXTRA_BYTESであるかを 確認する方法もありますが、概念的には少々複雑です。 MPEGLAYER3_WFX_EXTRA_BYTESは、mmreg.hにて次のように定義されています。

#define MPEGLAYER3_WFX_EXTRA_BYTES   12

この12という値は、MPEGLAYER3WAVEFORMAT構造体のwIDからnCodecDelayまでのサイズの合計です。 WAVEFORMATEX構造体の視点からすれば、これらのメンバは一種の拡張バイトであり、 そのサイズを参照することで、フォーマット固有のデータの有無を確認することができます。 ReadMP3Fileが成功した後、MP3におけるWAVEFORMATEX構造体のメンバを確認します。 WAVE_FORMAT_PCMを除く全てのフォーマットタグは16進数で定義されているため、 そのようなフォーマットタグは16進数で表示することを心掛けるべきでしょう。 cbSizeは、先に述べたようにMPEGLAYER3_WFX_EXTRA_BYTESの12という値になります。

RIFF/WAVE形式のMP3ファイルの特徴

音声圧縮にMP3を利用する場合は、標準MP3ファイルとして保存することが多いため、 一般にはRIFF/WAVE形式のMP3ファイルはそれほど多く見かけることはないと思います。 そもそも、この形式はWAVEの再生のみに対応しているアプリケーションに対してMP3を 再生させることが目的であって、拡張子が.wavでファイルの形式がRIFFであることから、 一部のアプリケーションならば再生に対応している可能性があるのです。 しかし、現在の多くのアプリケーションはどちらの形式の再生にも対応していますから、 RIFF/WAVE形式よりもタグ情報を格納できる標準MP3ファイルのほうが優れているといえるでしょう。 とはいえ、既に述べたようにRIFF/WAVE形式のMP3ファイルと通常のWAVEファイルの相違は、 fmt チャンクとdataチャンクに格納されているデータがMP3固有であるという点のみですから、 RIFF/WAVE形式のMP3ファイルの作成は標準MP3ファイルと比べて非常に簡単となっています。 標準MP3ファイルの作成では専用のアプリケーションをインストールする必要があるのに対し、 RIFF/WAVE形式のMP3ファイルに関してはWindows付属のサウンドレコーダーで作成することが できるため、圧縮後のサイズを知るための指針となるかもしれません。



戻る