EternalWindows
MP3 / MP3データ

今日、MP3プレイヤーと称されているほぼ全てのアプリケーションは、 標準MP3ファイルの再生をサポートしています。 この形式は、曲に関する情報をid3という規格に基づいて書き込めることから、 ファイルの加工性に優れ、多くのユーザーにとってのMPファイルとは、 この標準MP3ファイルのことを指していると言っても過言ではありません。 id3規格によって書き込まれた一連の情報はid3vタグと呼ばれ、 現在ではそのバージョンとしてid3v1とid3v2が存在していますが、 後述するように標準MP3ファイルの構造はこのバージョンによって変化します。

RIFF/WAVE形式や標準形式などといっても、それらはあくまで構造上の違いであり、 どちらのファイルにも、MP3の規格で圧縮された音声データが格納されていることを 忘れてはいけません。 たとえば、以前作成したDecodeWaveという関数はMP3データをACMを介して WAVEデータに変換していましたが、このような処理は標準形式においても同一であり、 最終的にwaveOutの関数でWAVEデータを再生するという部分も変わりありません。 このため、標準MP3ファイルを再生するための作業は、RIFF/WAVE形式のときと同じように ファイルからMP3データとMP3フォーマットを取得することであり、 異なるのはそれらをどのようにして取得するかという点のみなのです。 特にフォーマットに関しては、それ自体がファイルに実体として存在していないため、 データからフォーマットを導き出すという作業が必要になってきます。

それではまず、標準MP3ファイルからMP3データを取得するコードについて見てみましょう。 これまで利用してきたReadMP3File関数は、MP3のフォーマットとデータ、及びサイズを 呼び出し側に返しましたが、この仕様は標準MP3ファイルの読み取りにおいても適用できるため、 ReadMP3Fileのプロトタイプ自体は変更していません。

BOOL ReadMP3File(LPTSTR lpszFileName, LPMPEGLAYER3WAVEFORMAT lpmf, LPBYTE *lplpData, LPDWORD lpdwSize)
{
	HMMIO hmmio;
	
	hmmio = mmioOpen(lpszFileName, NULL, MMIO_READ);
	if (hmmio == NULL) {
		MessageBox(NULL, TEXT("ファイルのオープンに失敗しました。"), NULL, MB_ICONWARNING);
		return FALSE;
	}

	*lpdwSize = mmioSeek(hmmio, 0, SEEK_END);
	*lplpData = (LPBYTE)HeapAlloc(GetProcessHeap(), 0, *lpdwSize);
	mmioSeek(hmmio, 0, SEEK_SET);
	mmioRead(hmmio, (HPSTR)*lplpData, *lpdwSize);	

	mmioClose(hmmio, 0);
	
	// *lplpDataの中身を解析して、lpmfを初期化するコードを記述

	return TRUE;
}

標準MP3ファイルにおけるMP3データの格納位置は、id3vタグのバージョンによって変化します。 id3v1の場合、MP3データはファイルの先頭から格納されていますが、 id3v2ではタグフィールドの後にMP3データが格納されていることになっています。 また、標準MP3ファイルには、データのサイズ自体は格納されていません。 このことから、MP3データに該当する部分だけを読み取るのは困難だといえますが、 ファイル全体をデータと見立てれば、思いのほか簡単なコードになります。

*lpdwSize = mmioSeek(hmmio, 0, SEEK_END);
*lplpData = (LPBYTE)HeapAlloc(GetProcessHeap(), 0, *lpdwSize);
mmioSeek(hmmio, 0, SEEK_SET);
mmioRead(hmmio, (HPSTR)*lplpData, *lpdwSize);

mmioSeekの第2引数を0にしさらに第3引数にSEEK_ENDを指定した場合、 ファイルの先頭から終端までのバイト数、即ちファイルの全体のサイズが返ることになります。 このサイズ分だけメモリを確保すればファイル全体を読み取ることができるため、 そこには当然MP3データも含まれることになります。 id3v1の場合では、MP3データがファイルの先頭から格納されていますから、 ファイルデータをそのままMP3データとして利用することができるでしょう。 mmioReadでファイル全体を読み取る前にmmioSeekを呼び出しているのは、 先のサイズ取得のmmioSeekで移動したファイルポインタをリセットするためです。 次節から、*lplpDataの中身を解析する処理を見ていきます。


戻る