2013年03月29日

AC3ファイルの再生



AC3ACMのインストールで紹介したAC3ACMドライバーを使用して、AC3ファイルをデコード/再生する方法を解説します。



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

#include "MP4File.h"


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

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


AC3ストリームを扱うためのフォーマットタグと、DOLBYAC3WAVEFORMAT構造体を定義します。

#define WAVE_FORMAT_DVD_DTS 0x2001
#define WAVE_FORMAT_DOLBY_AC3 0x2000

#pragma pack(push, 1)
typedef struct tagDOLBYAC3WAVEFORMAT
{
WAVEFORMATEX wfx;
BYTE bBigEndian;
BYTE bsid;
BYTE lfeon;
BYTE copyrightb;
BYTE nAuxBitsCode;
}DOLBYAC3WAVEFORMAT;
#pragma pack(pop)


ビットストリームからのビットデータの取得を簡便にするために、CBitStreamクラスを独自に定義しました。

class CBitStream
{
public:
//コンスラクタ
CBitStream(const PBYTE pBytes);
//ビットストリームを取得します。
DWORD GetBitStream(int nBits);
private:
PBYTE m_pBytes;
int m_nBitsLeft;
DWORD m_dwCurrent;
};


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

//コンスラクタ
CBitStream::CBitStream(const PBYTE pBytes)
{
m_pBytes=pBytes;
m_nBitsLeft=0;
m_dwCurrent=0;
}

//ビットストリームを取得します。
DWORD CBitStream::GetBitStream(int nBits)
{
DWORD dwResult=0;
if (nBits<m_nBitsLeft){
dwResult=(m_dwCurrent<<(32-m_nBitsLeft))>>(32-nBits);
m_nBitsLeft-=nBits;
}
else{
nBits-=m_nBitsLeft;
dwResult=(m_dwCurrent<<(32-m_nBitsLeft))>>(32-m_nBitsLeft);
PBYTE pBytes=m_pBytes;
m_dwCurrent=(pBytes[0]<<24)+(pBytes[1]<<16)+(pBytes[2]<<8)+pBytes[3];
m_pBytes+=4;
if(nBits) dwResult=(dwResult<<nBits)+(m_dwCurrent>>(32-nBits));
m_nBitsLeft=32-nBits;
}
return dwResult;
}


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

class CAC3File : public CMP4File
{
public:
//コンストラクタ
CAC3File(CWnd* pWndCallBack=NULL);
//デストラクタ
virtual ~CAC3File();

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

//AC3ファイルがオープンされているかどうか?(オーバーライド)
virtual BOOL IsAC3File() const{return ((IsAcmDecode())&&(m_streamid>0));};
//読み込み中のデバイスハンドルの有効/無効を基底クラスに返します。(オーバーライド)
virtual BOOL IsOpen() const{return (IsAC3File()())? TRUE:CAACFile::IsOpen();};

//AC3ファイルを開きます。
BOOL OpenFile(LPCTSTR lpszFileName,BOOL bDeleteMemberParam=TRUE);

//AC3からWave音源に変換してストリーム読み込み(オーバーライド)
virtual UINT StreamIn(LPVOID pBuffer,UINT nBufSize,UINT* pIndexSample);

//AC3ファイルの書き込み(重ね書き不可&オーバーライド)
virtual BOOL WriteFile(LPCTSTR lpszFileName);

protected:
//AC3ヘッダーの読み込み
BOOL ReadHeader(CFile* infile);
//AC3ストリームの書き込み(オーバーライド)
virtual BOOL AC3StreamOut(CFile* outfile,DOLBYAC3WAVEFORMAT* pac3wf=NULL);

private:
UINT m_streamid;
UINT m_aveTimesPerFrame;
};


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

//コンストラクタ
CAC3File::CAC3File(CWnd* pWndCallBack)
:CMP4File(pWndCallBack)
{
Init();
}

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

//メンバー変数の初期化
void CAC3File::Init()
{
m_streamid=0;
m_aveTimesPerFrame=0;
}

//メンバー変数の削除
void CAC3File::Delete(BOOL bDeleteBaseClass)
{
if (bDeleteBaseClass)
//基底クラスを削除します。
CMP4File::Delete(bDeleteBaseClass);

else if (IsPlaying())
//WAVE出力デバイスを停止します。
Stop();

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


AC3ファイルを開いた後、CAC3File::ReadHeader関数を呼び出します。

//AC3ファイルを開きます。(オーバーライド)
// lpszFileName:オープンするファイル名。

BOOL CAC3File::OpenFile(LPCTSTR lpszFileName,BOOL bDeleteMemberParam)
{
if (bDeleteMemberParam) Delete(TRUE);

//ファイルのオープンに失敗した場合は、CDDAFile::OpenFile関数を呼び出します。
CFile* infile=new CFile;
if (!infile->Open(lpszFileName,CFile::modeRead|CFile::shareDenyNone)){
delete infile;
return CDDAFile::OpenFile(lpszFileName,FALSE);
}

//ヘッダー読み込みに失敗した場合は、CMP4File::OpenFile関数を呼び出します。
if (!ReadHeader(infile)){
infile->Close();
delete infile;
return CMP4File::OpenFile(lpszFileName,FALSE);
}

//CFileクラスへのポインタを保存します。
m_infile=infile;

//ファイルのパス名を保存しておきます。
m_strPathName=lpszFileName;
//ファイル形式を書き込んでおきます。
m_strFileType=_T("AC3");

return TRUE;
}


AC3ファイルのヘッダー情報からDOLBYAC3WAVEFORMAT構造体を作成し、この構造体を元にACMストリームを作成して、再生と同時にデコードするためのACMストリームヘッダを作成します。

//AC3ヘッダーの読み込み
BOOL CAC3File::ReadHeader(CFile* infile)
{
ASSERT(infile->IsKindOf(RUNTIME_CLASS(CFile)));

//読み飛ばし位置は、現在のファイル位置とします。
m_dwSkipOffset=(DWORD)infile->GetPosition();
//ファイルサイズを取得します。
DWORD fileSize=(DWORD)(DWORD_PTR)infile->GetLength();

DOLBYAC3WAVEFORMAT ac3wf={0};

BYTE header[10];
if (infile->Read(header,10)<10) return FALSE;
int id=*((DWORD*)(*(&header)));
if(id==0x0180FE7F){

//h4[b7]
//indicates the frame type, 1 = normal, 0 = termination.

int ftype=(header[4]>>7);
//h4[b6b5b4b3b2]
//the number of core samples by which a termination frame falls short of the normal length of 32 samples.
//For a normal frame the value must be 31.

int deficitsamplecount=(header[4]>>2)&0x1F;
// h4[b1]
//A flag that indicates if a CRC is present if TRUE (1).
//This will be FALSE for DVD.

int cpf=(header[4]>>1)&1; //crcpresent
// h4[b1]+h5[b7b6b5b4b3b2]
//The number less 1 of sample blocks.
//This will always be 15 for DVD. This means each frame is 16*32 (512) samples.
//At a sampling rate of 48K this translates to 10 and 2/3 milliseconds, or 960 ticks of the 90KHz clock.

int nblks=((header[4]&1)<<6)|(header[5]>>2);//npcmsampleblocks
//h5[b1b0]+h6[b7b6b5b4b3b2b1b0]+h7[b7b6b5b4]
//For DVD this will be 1005 (0x3ED) for 768 Kbps streams or 2012 (0x7DC) for 1536 Kbps streams.

int fsize=(((header[5]&3)<<12)|(header[6]<<4)|(header[7]>>4))+1; //framebytes
//h7[b3b2b1b0]+h8[b7b6]
//value channels arrangement
//00 1 A
//01 2 A + B (dual mono)
//02 2 L + R (stereo)
//03 2 (L+R) + (L-R) (sum and difference)
//04 2 LT + RT (left and right total)
//05 3 C + L + R
//06 3 L + R + S
//07 4 C + L + R + S
//08 4 L + R + SL + SR
//09 5 C + L + R + SL + SR
//0A 6 CL + CR + L + R + SL + SR
//0B 6 C + L + R + LR + RR + OV
//0C 6 CF + CR + LF + RF + LR + RR
//0D 7 CL + C + CR + L + R + SL + SR
//0E 8 CL + CR + L + R + SL1 + SL2 + SR1 + SR2
//0F 8 CL + C + CR + L + R + SL + S + SR
//10-3F user defined

int amode=(header[7]&0x0F)<<2|(header[8]>>6);
//h8[b5b4b3b2]
int freq=(header[8]>>2)&0x0F;
//h8[b1b0]+h9[b7b6b5]
//This is a coded value for the targeted bitrate of the encoded stream.
//For DVD only 2 values are possible:
//0x0F - targeted rate is 768 Kbps, actual rate is 754.5 Kbps
//18 - targeted rate is 1536 Kbps, actual rate is 1509.75 Kbps

int transbitrate=((header[8]&3)<<3)|(header[9]>>5);
//h9[b4]
int mix=(header[9]>>4)&1;
//h9[b3]
int dynf=(header[9]>>3)&1;
//h9[b2]
int timef=(header[9]>>2)&1;
//h9[b1]
int auxf=(header[9]>>1)&1;
//h9[b0]
int hdcd=(header[9]&1);

int freqtbl[]={0,
8000,16000,32000,0,0,
11025,22050,44100,0,0,
12000,24000,48000,0,0
};

int bitratetbl[]={
32000, 56000, 64000, 96000, 112000, 128000, 192000, 224000,
256000, 320000, 384000, 448000, 512000, 576000, 640000, 754500,
960000,1024000,1152000,1280000,1344000,1408000,1411200,1472000,
1509750,1920000,2048000,3072000,3840000,0,0,0
};

int channelTbl[]={1,2,2,2,2,3,3,4,4,5,6,6,6,7,8,8};

ac3wf.wfx.cbSize=sizeof(DOLBYAC3WAVEFORMAT)-sizeof(WAVEFORMATEX);
ac3wf.wfx.nChannels=(amode<0x10)?channelTbl[amode]:2;
ac3wf.wfx.nSamplesPerSec=freqtbl[freq];
ac3wf.wfx.nAvgBytesPerSec=(bitratetbl[transbitrate]+4)/8;

m_sizeFrame=fsize;
m_aveTimesPerFrame=(UINT)(UINT_PTR)((m_sizeFrame==2012)?
10000000i64*2013*8/bitratetbl[transbitrate]:
10000000i64*m_sizeFrame*8/bitratetbl[transbitrate]);

ac3wf.wfx.wFormatTag=WAVE_FORMAT_DVD_DTS;
m_streamid =0x88;
}

else if ((WORD)id==0x770b){

//最初から読み直します。
BYTE syncInfo[5]={0};
infile->Seek(m_dwSkipOffset,CFile::begin);
if (infile->Read(syncInfo,5)<5) return FALSE;

BYTE bsiBuf[128]={0};
if (infile->Read(bsiBuf,128)<128) return FALSE;

BYTE fscod=syncInfo[4]>>6;

CBitStream bsi(bsiBuf);
//bsi0[b7b6b5b4b3]
BYTE bsid=(BYTE)bsi.GetBitStream(5);
//bsi0[b2b1b0];
BYTE bsmod=(BYTE)bsi.GetBitStream(3);
//bsi1[b7b6b5];
BYTE acmod=(BYTE)bsi.GetBitStream(3);

//cmixlev - center mix level (acmod 3, 5, 7)
double centerMixLevel=0.0;
if ((acmod&0x1)&&(acmod!=0x1)){
//value center mix level
//00 0.707 (-3.0 dB)
//01 0.595 (-4.5 dB)
//10 0.500 (-6.0 dB)
//11 reserved

double cmixlevTable[]={0.707,0.595,0.500,0};
BYTE cmixlev=(BYTE)bsi.GetBitStream(2);
centerMixLevel=cmixlevTable[cmixlev];
}

//surmixlev - surround mix level (acmod 4, 5, 6, 7)
double surroundMixLevel=0.0;
if (acmod&0x4){
//value surround mix level
//00 0.707 (-3.0 dB)
//01 0.500 (-6.0 dB)
//10 0
//11 reserved

double surmixlevTable[]={0.707,0.500,0,0};
BYTE surmixlev=(BYTE)bsi.GetBitStream(2);
surroundMixLevel=surmixlevTable[surmixlev];
}

//dsurmod - dolby surround mode (acmod 2)
//value indication
//00 not indicated
//01 Not Dolby Surround encoded
//10 Dolby Surround encoded
//11 reserved

BYTE dsurmod=(BYTE)bsi.GetBitStream(2);

//lfeon - Low frequency effects (LFE) on, 0=off, 1=on
BYTE lfeon=(BYTE)bsi.GetBitStream(1);

//acmod audio coding mode
//mode audio coding mode nfchans Channel Array Order
//000 1+1 2 Ch1, Ch2
//001 1/0 1 C
//010 2/0 2 L, R
//011 3/0 3 L, C, R
//100 2/1 3 L, R, S
//101 3/1 4 L, C, R, S
//110 2/2 4 L, R, SL, SR
//111 3/2 5 L, C, R, SL, SR

int channelTable[]={2,1,2,3,3,4,4,5};
int nChannels=channelTable[acmod]+lfeon;

if ((bsid>=0)&&(bsid<=8)){
int freqTable[]={48000,44100,32000,48000};
int bitrateTable[38]={
32000, 32000, 40000, 40000, 48000, 48000, 56000, 56000, 64000, 64000,
80000, 80000, 96000, 96000,112000,112000,128000,128000,160000,160000,
192000,192000,224000,224000,256000,256000,320000,320000,384000,384000,
448000,448000,512000,512000,576000,576000,640000,640000
};

int frameSizeTable[3][38]={
{ 64, 64, 80, 80, 96, 96, 112, 112, 128, 128, 160, 160, 192, 192,//48000Hz
224, 224, 256, 256, 320, 320, 384, 384, 448, 448, 512, 512, 640, 640,
768, 768, 896, 896,1024,1024,1152,1152,1280,1280},
{ 69, 70, 87, 88, 104, 105, 121, 122, 139, 140, 174, 175, 208, 209,//44100Hz
243, 244, 278, 279, 348, 349, 417, 418, 487, 488, 557, 558, 696, 697,
835, 836, 975, 976,1114,1115,1253,1254,1393,1394},
{ 96, 96, 120, 120, 144, 144, 168, 168, 192, 192, 240, 240, 288, 288,//32000Hz
336, 336, 384, 384, 480, 480, 576, 576, 672, 672, 768, 768, 960, 960,
1152,1152,1344,1344,1536,1536,1728,1728,1920,1920}
};

if (fscod==3) return FALSE;
int frmsizcod=syncInfo[4]&0x3F;
if(frmsizcod>=38) return FALSE;
int sampleRate=freqTable[fscod];
int bitRate=bitrateTable[frmsizcod];
int frameSize=frameSizeTable[fscod][frmsizcod]*2;

ac3wf.wfx.cbSize=sizeof(DOLBYAC3WAVEFORMAT)-sizeof(WAVEFORMATEX);
ac3wf.wfx.nChannels=nChannels;
ac3wf.wfx.nSamplesPerSec =sampleRate;
ac3wf.wfx.nAvgBytesPerSec=(bitrateTable[frmsizcod]+4)/8;
ac3wf.bBigEndian=FALSE;

m_sizeFrame=frameSize;
m_aveTimesPerFrame=10000000i64*m_sizeFrame*8/bitrateTable[frmsizcod];

ac3wf.wfx.wFormatTag=WAVE_FORMAT_DOLBY_AC3;
ac3wf.bsid =bsid;
ac3wf.lfeon =lfeon;
m_streamid =0x80;
}
else if((bsid>=11)&&(bsid<=16)){ //DD+
BYTE numblkscod=(syncInfo[4]&0x30)>>4;
if(fscod==3){
fscod=numblkscod+3;
numblkscod=3;
}

int freqtbl[]={48000,44100,32000,22400,22050,16000,48000};

ac3wf.wfx.cbSize=sizeof(DOLBYAC3WAVEFORMAT)-sizeof(WAVEFORMATEX);
ac3wf.wfx.nChannels=nChannels;
ac3wf.wfx.nSamplesPerSec =freqtbl[fscod];
ac3wf.wfx.nAvgBytesPerSec=ac3wf.wfx.nSamplesPerSec*4;
ac3wf.bBigEndian=FALSE;

m_sizeFrame=2+(id>>23)+((id&0x00070000)>>7);
int timetbl[]={53333,106667,160000,320000};
m_aveTimesPerFrame=timetbl[numblkscod];

ac3wf.wfx.wFormatTag=WAVE_FORMAT_DOLBY_AC3;
ac3wf.bsid =bsid;
ac3wf.lfeon =lfeon;
m_streamid =0xC0;
}
else return FALSE;
}
else return FALSE;

ac3wf.wfx.wBitsPerSample=16;
ac3wf.wfx.nBlockAlign=(ac3wf.wfx.wBitsPerSample/8)*ac3wf.wfx.nChannels;

m_dwStreamSize=fileSize;
m_nFrameCount=m_dwStreamSize/m_sizeFrame;
UINT totalTime=m_aveTimesPerFrame*m_nFrameCount;
m_nDataSize=UINT((double)ac3wf.wfx.nAvgBytesPerSec*(double)totalTime/1000.0);

//DOLBYAC3WAVEFORMAT構造体からWAVEFORMATEX構造体を取得します。
::ZeroMemory(&m_wfx,sizeof(WAVEFORMATEX));
m_wfx.wFormatTag=WAVE_FORMAT_PCM;
MMRESULT mmr=::acmFormatSuggest(NULL,&ac3wf.wfx,&m_wfx,sizeof(WAVEFORMATEX),ACM_FORMATSUGGESTF_WFORMATTAG);
if (mmr!=MMSYSERR_NOERROR) return FALSE;

//AC-3 ACM Codec ver 2.2対応
if (m_wfx.nChannels>2){
m_wfx.nChannels=2;
m_wfx.nBlockAlign=m_wfx.wBitsPerSample/8*m_wfx.nChannels;
m_wfx.nAvgBytesPerSec=m_wfx.nSamplesPerSec*m_wfx.nBlockAlign;
}

//ACMストリームをオープンします。
mmr=::acmStreamOpen(&m_hAcmStream,NULL,&ac3wf.wfx,&m_wfx,NULL,0,0,ACM_STREAMOPENF_NONREALTIME);
if (mmr!=MMSYSERR_NOERROR) return FALSE;

//AC3フレームサイズに相当するデコード後のデータサイズを取得します。
DWORD sizeFrameDecoded;
::acmStreamSize(m_hAcmStream,m_sizeFrame,&sizeFrameDecoded,ACM_STREAMSIZEF_SOURCE);

//AC3総データサイズからデコード後の総データサイズを取得します。
::acmStreamSize(m_hAcmStream,m_dwStreamSize,(LPDWORD)&m_nDataSize,ACM_STREAMSIZEF_SOURCE);
//デコード後のデータサイズの平均値を算出します。
m_sizeAveFrameDecoded=ROUND((double)m_sizeFrame*(double)m_nDataSize/(double)m_dwStreamSize);

//作業用バッファを確保します。
DWORD dwSrcSize=m_sizeFrame; //転送元バッファサイズ
DWORD dwDstSize=sizeFrameDecoded; //転送先バッファサイズ

m_byteSrcBuf.SetSize(dwSrcSize); //転送元バッファ
m_byteDstBuf.SetSize(dwDstSize); //転送先バッファ

//ACMストリームヘッダーを作成します。
::ZeroMemory(&m_AcmStreamHeader,sizeof(ACMSTREAMHEADER));
m_AcmStreamHeader.cbStruct = sizeof(ACMSTREAMHEADER);
m_AcmStreamHeader.pbSrc = m_byteSrcBuf.GetData();
m_AcmStreamHeader.cbSrcLength = (DWORD)(DWORD_PTR)m_byteSrcBuf.GetSize();
m_AcmStreamHeader.pbDst = m_byteDstBuf.GetData();
m_AcmStreamHeader.cbDstLength = (DWORD)(DWORD_PTR)m_byteDstBuf.GetSize();

::acmStreamPrepareHeader(m_hAcmStream,&m_AcmStreamHeader,0);

//現在のファイル位置を、読み飛ばし位置に移動します。
infile->Seek(m_dwSkipOffset,CFile::begin);

//第一フレームをドライバーに読み込ませます。
UINT size=infile->Read(m_AcmStreamHeader.pbSrc,m_AcmStreamHeader.cbSrcLength);
if (!size) return FALSE;
//実際に読み込んだサイズを有効なサイズとします。
m_AcmStreamHeader.cbSrcLengthUsed=size;
//AC3データからWave音源データに変換します。
mmr=::acmStreamConvert(m_hAcmStream,&m_AcmStreamHeader,ACM_STREAMCONVERTF_BLOCKALIGN|ACM_STREAMCONVERTF_START);
//デコード済みデータがある場合。
if ((mmr==MMSYSERR_NOERROR)&&(m_AcmStreamHeader.cbDstLengthUsed>0)){
//キャッシュに、デコード済みデータをコピーします。
m_byteCache.SetSize(m_AcmStreamHeader.cbDstLengthUsed);
::CopyMemory(m_byteCache.GetData(),m_AcmStreamHeader.pbDst,m_AcmStreamHeader.cbDstLengthUsed);
}
else
//キャッシュを初期化します。
m_byteCache.SetSize(0);

//指定の演奏位置を、読み込んだAC3データに相当する音源データ分進めます。
DWORD dwPosRecent=(DWORD)infile->GetPosition();
UINT indexFrame=(dwPosRecent-m_dwSkipOffset)/m_sizeFrame;
m_indexSample=(m_sizeAveFrameDecoded*indexFrame)/m_wfx.nBlockAlign;

//DOLBYAC3WAVEFORMAT構造体を保存しておきます。
SetDecodeFormat((LPWAVEFORMATEX)&ac3wf);

return TRUE;
}


AC3ファイル再生時に音源データ読み込みのために、基底クラスから呼び出されるオーバーライド関数です。

//AC3からWave音源に変換してストリーム読み込み
UINT CAC3File::StreamIn(LPVOID pBuffer,UINT nBufSize,UINT* pIndexSample)
{
ASSERT(!::pBuffer,nBufSize));
ASSERT(AfxIsValidAddress(pIndexSample,sizeof(UINT)));

//ACMストリームハンドルが無効な場合は、
//CMP4File::StreamIn関数を呼び出します。

if (!IsAC3File()()) return CMP4File::StreamIn(pBuffer,nBufSize,pIndexSample);

//現在位置と指定位置を算出します。
DWORD dwPosRecent=(DWORD)m_infile->GetPosition();
UINT indexFrame=ROUND((double)(*pIndexSample*m_wfx.nBlockAlign)/(double)m_sizeAveFrameDecoded);
DWORD dwPosRead=m_dwSkipOffset+(m_sizeFrame*indexFrame);

DWORD fdwConvert=ACM_STREAMCONVERTF_BLOCKALIGN;
if (indexFrame==0) fdwConvert|=ACM_STREAMCONVERTF_START;
//現在演奏中で現在位置と指定位置が100_秒以上違えば、指定位置にシークします。
if ((IsPlaying())&&
((DWORD)abs((int)(dwPosRead-dwPosRecent))>(m_wfx.nAvgBytesPerSec/10))){
//前回の残りの音源データ数を0にします。
m_byteCache.SetSize(0);
//探し出したフレームヘッダーの先頭位置に移動します。
m_infile->Seek(dwPosRead,CFile::begin);
}

//キャッシュ内のデータサイズを取得します。
UINT nCaches=(UINT)(UINT_PTR)m_byteCache.GetSize();
//MP3ストリーム最後尾の位置
DWORD dwFileEnd=m_dwStreamSize+m_dwSkipOffset;

//キャッシュ内のデータサイズがバッファサイズに満たない場合。
while(nCaches<nBufSize){

UINT size=m_infile->Read(m_AcmStreamHeader.pbSrc,m_AcmStreamHeader.cbSrcLength);
if (!size) break;
if (size!=m_AcmStreamHeader.cbSrcLength) fdwConvert|=ACM_STREAMCONVERTF_END;
//実際に読み込んだサイズを有効なサイズとします。
m_AcmStreamHeader.cbSrcLengthUsed=size;
//AC3データからWave音源データに変換します。
MMRESULT mmr=::acmStreamConvert(m_hAcmStream,&m_AcmStreamHeader,fdwConvert);

fdwConvert=ACM_STREAMCONVERTF_BLOCKALIGN;

//デコードエラーの場合は、エラー終了します。
if (mmr!=MMSYSERR_NOERROR) return FALSE;
//デコード済みデータがある場合。
else if (m_AcmStreamHeader.cbDstLengthUsed>0){
//キャッシュの末尾に、デコード済みデータを追加します。
nCaches=(UINT)(UINT_PTR)m_byteCache.GetSize();
m_byteCache.SetSize(nCaches+m_AcmStreamHeader.cbDstLengthUsed);
::CopyMemory(m_byteCache.GetData()+nCaches,m_AcmStreamHeader.pbDst,
m_AcmStreamHeader.cbDstLengthUsed);
nCaches=(UINT)(UINT_PTR)m_byteCache.GetSize();
}
}
//バッファに取り込むサイズを算定して、キャッシュからデータをコピーします。
UINT readBytes=min(nBufSize,nCaches);
::CopyMemory(pBuffer,m_byteCache.GetData(),readBytes);

//キャッシュ内のデータの内、バッファに取り込んだデータを削除してから、
//残りのデータを左シフトします。

m_byteCache.RemoveAt(0,readBytes);

//指定の演奏位置を、読み込んだMp3データに相当する音源データ分進めます。
dwPosRecent=(DWORD)m_infile->GetPosition();
indexFrame=(dwPosRecent-m_dwSkipOffset)/m_sizeFrame;
*pIndexSample=(m_sizeAveFrameDecoded*indexFrame)/m_wfx.nBlockAlign;

//読み込んだバイト数を返して終了します。
return readBytes;
}



CAC3Fileクラスのインスタンスを作成した後、CAC3File::OpenFile関数でAC3ファイルを開きます。
エラーがなければCWaveFile::Play関数で演奏を開始します。演奏を中断したり、演奏終了後WAVE出力デバイスを閉じる場合は、CWaveFile::Stop関数を呼び出します。




posted by ひろし at 14:27| Comment(0) | AC3ファイルフォーマット | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
×

この広告は180日以上新しい記事の投稿がないブログに表示されております。