FC2ブログ

ホーム > MP3ファイルフォーマット > MP3ファイルの再生 第1部

MP3ファイルの再生 第1部



ウィンドウズのオーディオコーデックは、ACM(Audio Compression Manager)によって管理されており、標準でデコードのみ可能なMPEGLAYER3コーデックが装備されています。本章ではこのACMを介してMP3ファイルのデコードを行い、同時に再生するプログラムについて解説します。




MP3ファイルACMコーデックでデコードするために必要なインクルードファイルとライブラリです。

#include <mmreg.h>
#include <msacm.h>
#pragma comment(lib, "msacm32.lib")


MP3ファイルを再生するために必要なインクルードファイルです。

#include "CDDAFile.h"


上記インクルード定義で必要となるファイルです。

devioctl.h
ntddcdrm.h
ntddstor.h
WaveFile.h WaveFile.cpp
WaveOut.h WaveOut.cpp
CDDAFile.h CDDAFile.cpp


倍精度実数から四捨五入した整数を求めるマクロです。

//四捨五入した整数を求める
#define ROUND(data) ((int)((double)data+(double)0.5))


MP3ファイルビットレート及び周波数設定時に参照する定数テーブルです。

//ビットレートテーブル
static const int g_mp3BitRate[2][3][16]={
{{0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,-1}, //MPEG1-Layer1
{0,32,48,56, 64, 80, 96,112,128,160,192,224,256,320,384,-1}, //MPEG1-Layer2
{0,32,40,48, 56, 64, 80, 96,112,128,160,192,224,256,320,-1}}, //MPEG1-Layer3
{{0,32,48,56, 64, 80, 96,112,128,144,160,176,192,224,256,-1}, //MPEG2/2.5-Layer1
{0, 8,16,24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160,-1}, //MPEG2/2.5-Layer2
{0, 8,16,24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160,-1}} //MPEG2/2.5-Layer3
};

//周波数テーブル
static const long g_mp3Freq[3][4] = {
{44100,48000,32000,-1}, // MPEG1
{22050,24000,16000,-1}, // MPEG2
{11025,12000, 8000,-1} // MPEG2.5
};

//フレーム当たりのサンプル数テーブル
static const long g_mp3SamplesPerFrame[3][4]={
{ 384, 384, 384,-1}, //layer 1
{1152,1152,1152,-1}, //layer 2
{1152, 576, 576,-1} //layer 3
};


フレームヘッダーを解析して、デコードに必要な各値を格納する構造体とSTDCALL関数です。

//FRAMEHEADER構造体
typedef struct tagFRAMEHEADER{
int version; //MPEGバージョン番号
int layer; //レイヤー番号
int bitRate; //ビットレート
int sampleRate; //周波数
int padding; //パディングバイトの有無
int channel; //チャンネル数
int frameSize; //フレームサイズ
}FRAMEHEADER;

//フレームヘッダーを解析して、デコードに必要な各値をFRAMEHEADER構造体に格納します。
BOOL __stdcall _ParseFrameHeader(const PBYTE pHeader,FRAMEHEADER* pFrameHeader)
{
//先頭バイトは0xFF
if ((pHeader[0]&0xFF)!=0xFF) return FALSE;
//次のバイトの上位3ビットは1
if ((pHeader[1]&0xE0)!=0xE0) return FALSE;

//MPEGバージョン番号を取得します。
int version=4-((pHeader[1]>>3)&3);
if (version==3) return FALSE;
if (version==4) version=3;

//MPEGレイヤーを取得します。
int layer=4-((pHeader[1]>>1)&3);
if (layer==4) return FALSE;

//ビットレートを取得します。
int index=pHeader[2]>>4;
int bitRate=g_mp3BitRate[(version==1)?0:1][layer-1][index];
if (bitRate==-1) return FALSE;

//周波数を取得します。
index=(pHeader[2]>>2)&0x03;
int sampleRate=g_mp3Freq[version-1][index];
if (sampleRate==-1) return FALSE;

//パディング/チャンネル数を取得します。
int padding=(pHeader[2]>>1)&0x01;
int channel=(pHeader[3]>>6)==3?1:2;

//フレームサイズを算出します。
int frameSize=((g_mp3SamplesPerFrame[layer-1][version-1]/8*bitRate*1000)/sampleRate)+padding;

#define MIN_FRAME_SIZE 21
if (frameSize<MIN_FRAME_SIZE) return FALSE;

//強調モードが予約コードの場合は、FALSEを返して終了します。
if ((pHeader[3]&3)==2) return FALSE;

//データをFRAMEHEADER構造体に格納します。
pFrameHeader->version=version;
pFrameHeader->layer=layer;
pFrameHeader->bitRate=bitRate;
pFrameHeader->sampleRate=sampleRate;
pFrameHeader->padding=padding;
pFrameHeader->channel=channel;
pFrameHeader->frameSize=frameSize;

return TRUE;
}


MP3ファイルの再生をWAVEファイルと同様の操作にするために、CWaveFileクラスの派生クラスであるCDDAFileクラスを基底クラスにしたCMP3Fileクラスを独自に定義しました。

class CMP3File : public CDDAFile
{
public:
//コンストラクタ
CMP3File(CWnd* pWndOwner=NULL);
//デストラクタ
virtual ~CMP3File();

//メンバー変数の初期化
void Init();
//メンバー変数の削除
void Delete();

//MP3ファイルがオープンされているかどうか?
virtual BOOL IsMp3File(){return (m_hAcmStream!=NULL);};
//読み込み中のデバイスハンドルの有効/無効を基底クラスに返します。
virtual BOOL IsOpen(){return (IsMp3File())? TRUE:CDDAFile::IsOpen();};

//MP3Acmファイルを開く
// lpszFileName:ファイルのパス名

virtual BOOL OpenFile(LPCTSTR lpszFileName);
//MP3からWave音源に変換してストリーム読み込み
virtual UINT StreamIn(LPVOID pBuffer,UINT nBufSize,UINT* pIndexSample);
//ID3tagを現在のファイルに保存します。
BOOL SaveID3Tag();
//MP3ファイルの書き込み
virtual BOOL SaveFile(LPCTSTR lpszFileName);
//MP3ファイル(id3tag)の書き込み
BOOL SaveMp3File(LPCTSTR lpszFileName);
//MP3ストリームへの書き込み
BOOL StreamOut(CFile* outfile,HACMDRIVERID hadid=NULL,MPEGLAYER3WAVEFORMAT* pmp3wf=NULL);

//現在システムによって推奨されているドライバーIDの取得
void GetDriverIDSuggest(HACMDRIVERID* phadidEnc=NULL,HACMDRIVERID* phadidDec=NULL);
//ドライバーIDの取得
void GetDriverID(HACMDRIVERID* phadidEnc=NULL,HACMDRIVERID* phadidDec=NULL);
//ドライバーIDの設定
void SetDriverID(HACMDRIVERID hadidEnc=(HACMDRIVERID)-1,HACMDRIVERID hadidDec=(HACMDRIVERID)-1);
//MPEGLAYER3WAVEFORMAT構造体の取得
BOOL GetMpegLayer3WaveFormat(MPEGLAYER3WAVEFORMAT* pmp3wfEnc=NULL,
MPEGLAYER3WAVEFORMAT* pmp3wfDec=NULL);
//MPEGLAYER3WAVEFORMAT構造体の設定
void SetMpegLayer3WaveFormat(MPEGLAYER3WAVEFORMAT* pmp3wfEnc=NULL,
MPEGLAYER3WAVEFORMAT* pmp3wfDec=NULL);
private:
//MPEGLAYER3WAVEFORMAT構造体の設定
BOOL FillMpegLayer3WaveFormat(MPEGLAYER3WAVEFORMAT* pmp3wf);

protected:
//基底クラスに読み込み用のバッファサイズを戻します。
virtual UINT GetSizeOfLoadBuffer();
//基底クラスに書き込み用のバッファサイズを戻します。
virtual UINT GetSizeOfSaveBuffer();
//MP3ヘッダーの読み込み
BOOL ReadHeader(CFile* infile);

public:
CFile* m_infile; //Mp3ファイル読込みのためのCFileクラスへのポインタ
enum{CBR,ABR,VBR}; //エンコードビットレート形式(CBR/ABR/VBR)

private:
MPEGLAYER3WAVEFORMAT m_mp3wfEnc;//エンコード用MPEGLAYER3WAVEFORMAT
MPEGLAYER3WAVEFORMAT m_mp3wfDec;//デコード用MPEGLAYER3WAVEFORMAT
HACMDRIVERID m_hDriverIdEnc; //エンコード用ACMドライバーID
HACMDRIVERID m_hDriverIdDec; //デコード用ACMドライバーID
HACMDRIVER m_hDriver; //ACMドライバーハンドル
HACMSTREAM m_hAcmStream; //ACMストリームハンドル
ACMSTREAMHEADER m_AcmStreamHeader;//ACMSTREAMHEADER構造体

int m_iVersion; //MPEGバージョン番号
int m_iLayer; //MPEGレイヤー
int m_iBitRate; //ビットレート
int m_iAveBitRate; //平均ビットレート
UINT m_nFrames; //フレーム数
DWORD m_dwStreamSize; //MP3ストリームサイズ
UINT m_uFlagBitRate; //ビットレートフラグ(CBR/ABR/VBR)
UINT m_vbrQuality; //VBR品質((高)0~100(低))
BYTE m_vbrTOC[100]; //VBR TOCエントリー

DWORD m_mp3BlockSize; //Mp3ブロックサイズ
DWORD m_wavBlockSize; //Waveブロックサイズ
DWORD m_dwSkipOffset; //読み飛ばし位置(MP3ファイル上での音源データの開始位置)
DWORD m_dwSkipBottom; //ID3V1タグがある場合の無視すべきファイル末尾のバイト数
DWORD m_mp3StreamSize; //Mp3総データサイズ
DWORD m_nAveWaveBlockSize; //Waveブロックサイズの平均値
CByteArray m_byteSrcBuf; //デコード時の転送元バッファ
CByteArray m_byteDstBuf; //デコード時の転送先バッファ
CByteArray m_byteRemain; //音源データの端数を保持する残りバッファ
UINT m_nRemain; //音源データの端数

public:
//MPEGバージョン番号の取得
int GetVersion(){return m_iVersion;};
//MPEGレイヤーの取得
int GetLayer(){return m_iLayer;};
//ビットレートの取得
int GetBitRate(){return m_iBitRate;};
//フレームサイズの取得
DWORD GetFrameSize(){return m_mp3BlockSize;};
//平均ビットレートの取得
int GetAveBitRate(){return m_iAveBitRate;};
//フレーム数の取得
UINT GetFrameCount(){return m_nFrames;};
//MP3ストリームサイズの取得
DWORD GetStreamSize(){return m_dwStreamSize;};
//ビットレートフラグの取得(0:CBR,1:ABR,2:VBR)
UINT GetBitRateFlag(){return m_uFlagBitRate;};
//VBR品質の取得((高)0~100(低))
UINT GetVBRQuality(){return m_vbrQuality;};
//VBR TOCエントリーの取得
BYTE GetVBRTOC(int index){return m_vbrTOC[index];};

CByteArray m_byteID3v1; //ID3v1タグの生データ
CByteArray m_byteID3v22; //ID3v2.2タグの生データ
CByteArray m_byteID3v23; //ID3v2.3タグの生データ
CByteArray m_byteID3v24; //ID3v2.4タグの生データ

DWORD m_ID3v1Version; //ID3v1バージョン番号
BOOL m_bID3v22; //ID3v2.2タグの有無
BOOL m_bID3v23; //ID3v2.3タグの有無
BOOL m_bID3v24; //ID3v2.4タグの有無
};


CMP3Fileクラスのインライン関数です。

//基底クラスに読み込み用のバッファサイズを戻します。
inline UINT CMP3File::GetSizeOfLoadBuffer()
{
//MP3ファイルがオープンされている場合。
if (IsMp3File()){
//CWaveFileクラス(CWaveOutクラス)の読み込みバッファサイズを取得し、
//Waveブロックサイズで丸めたサイズ(剰余切り上げ)を戻します。

DWORD dwBufferLength=CWaveOut::GetSizeOfLoadBuffer();
return ((dwBufferLength/m_wavBlockSize)+((dwBufferLength%m_wavBlockSize)?1:0))*m_wavBlockSize;
}
//それ以外は既定クラスを呼び出します。
return CDDAFile::GetSizeOfLoadBuffer();
}

//基底クラスに書き込み用のバッファサイズを戻します。
inline UINT CMP3File::GetSizeOfSaveBuffer()
{
//MP3ファイルがオープンされている場合。
if (m_hAcmStream) return m_AcmStreamHeader.cbSrcLength;
//それ以外は既定クラスを呼び出します。
return CDDAFile::GetSizeOfSaveBuffer();
}

//MPEGLAYER3WAVEFORMAT構造体の設定
inline void CMP3File::SetMpegLayer3WaveFormat(MPEGLAYER3WAVEFORMAT* pmp3wfEnc,
MPEGLAYER3WAVEFORMAT* pmp3wfDec)
{
if (AfxIsValidAddress(pmp3wfEnc,sizeof(MPEGLAYER3WAVEFORMAT),TRUE))
::CopyMemory(&m_mp3wfEnc,pmp3wfEnc,sizeof(MPEGLAYER3WAVEFORMAT));
if (AfxIsValidAddress(pmp3wfDec,sizeof(MPEGLAYER3WAVEFORMAT),TRUE))
::CopyMemory(&m_mp3wfDec,pmp3wfDec,sizeof(MPEGLAYER3WAVEFORMAT));
}

//ドライバーIDの設定
inline void CMP3File::SetDriverID(HACMDRIVERID hadidEnc,HACMDRIVERID hadidDec)
{
if (hadidEnc!=(HACMDRIVERID)-1) m_hDriverIdEnc=hadidEnc;
if (hadidDec!=(HACMDRIVERID)-1) m_hDriverIdDec=hadidDec;
}


CMP3Fileクラスのコンストラクタ/デストラクタです。

//コンストラクタ
CMP3File::CMP3File(CWnd* pWndOwner)
:CDDAFile(pWndOwner)
{
m_hDriverIdEnc=NULL;
m_hDriverIdDec=NULL;
::ZeroMemory(&m_mp3wfEnc,sizeof(MPEGLAYER3WAVEFORMAT));
Init();
}

//デストラクタ
CMP3File::~CMP3File()
{
Delete();
}


CMP3Fileクラスのメンバ関数です。

//メンバー変数の初期化
void CMP3File::Init()
{
m_infile=0;
m_hDriver=NULL;
m_hAcmStream=0;

::ZeroMemory(&m_mp3wfDec,sizeof(MPEGLAYER3WAVEFORMAT));
::ZeroMemory(&m_AcmStreamHeader,sizeof(ACMSTREAMHEADER));
m_dwSkipOffset=0;
m_dwSkipBottom=0;

m_iVersion =0;
m_iLayer =0;
m_iBitRate =0;
m_iAveBitRate=0;

m_nFrames =0;
m_dwStreamSize =0;
m_uFlagBitRate =0;
m_vbrQuality =0;
::ZeroMemory(m_vbrTOC,100);

m_mp3BlockSize=0;
m_wavBlockSize=0;
m_mp3StreamSize=0;
m_nRemain=0;

m_ID3v1Version=0;
m_bID3v22=FALSE;
m_bID3v23=FALSE;
m_bID3v24=FALSE;

}

//メンバー変数の削除
void CMP3File::Delete()
{
CDDAFile::Delete();
//ACMストリームハンドルがある場合。
if (m_hAcmStream){
MMRESULT mmr;
//ACMストリームヘッダーがあれば削除します。
if (m_AcmStreamHeader.cbStruct==sizeof(ACMSTREAMHEADER)){
mmr=::acmStreamUnprepareHeader(m_hAcmStream,&m_AcmStreamHeader,0);
ASSERT(mmr==0);
}
//ACMストリームをクローズします。
mmr=::acmStreamClose(m_hAcmStream,0);
ASSERT(mmr==0);
}
//ACMドライバーハンドルがあればクローズします。
if (m_hDriver) ::acmDriverClose(m_hDriver,0);

//読み込みのためのCFileクラスへのポインタがあればクローズします。
if (m_infile){
m_infile->Close();
delete m_infile;
}

//ID3タグ生データの削除
m_byteID3v1.RemoveAll();
m_byteID3v22.RemoveAll();
m_byteID3v23.RemoveAll();
m_byteID3v24.RemoveAll();

//メンバー変数を初期化します。
Init();
}




<<CDDBの読み込みページの先頭MP3ファイルの再生 第2部>>



スポンサーサイト



コメント: 0

この記事へのコメント
ブログ著者にのみ知らせます。

Trackback+Pingback: 0

TrackBack URL for this entry
http://hiroshi0945.blog75.fc2.com/tb.php/52-9a30421e
Listed below are links to weblogs that reference
MP3ファイルの再生 第1部 from マルチメディアファイルフォーマット

Home > MP3ファイルフォーマット > MP3ファイルの再生 第1部

タグクラウド
ブロとも申請フォーム

この人とブロともになる

ブロとも一覧

このページの先頭へ戻る