2012年05月12日

AACファイルの再生



faad2ライブラリーを使用してAACファイルのデコードを行い、同時に再生するプログラムについて解説します。



AACファイルをデコードするために必要なインクルードファイルとライブラリです。
尚ライブラリーについては前回のfaad2-2.7のインストールで作成したものです。

extern "C"
{
#include "..\\faad2-2.7\include\neaacdec.h"
}
#pragma comment(lib,"SoundLib\\libfaad-vc7.lib")


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

#include "SoundLib\MP3File.h"


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

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


AACファイル周波数設定時に参照する定数テーブルです。

//周波数テーブル
static const long g_aacFreq[]={
96000,88200,64000,48000,44100,32000,
24000,22050,16000,12000,11025, 8000,
-1, -1, -1, -1
};


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

//ADTS_HEADER構造体
typedef struct tagADTS_HEADER{
int iVersion; //4:MPEG4,2:MPEG2
BOOL bProtect; //保護属性 0:なし,1:有り
int iProfile; //音質と圧縮率 0:main,01:LC,2:SSR,3:(予約)
DWORD sampleRate; //周波数
WORD nChannel; //チャンネル数
BOOL bCopy; //0:オリジナル,1:コピー
DWORD nFrameSize; //AACフレーム長(バイト、ヘッダ込み)
DWORD nFullness; //ADTSバッファ残量 0x7FF VBR
}ADTS_HEADER;

//ADTSヘッダーを解析して、デコードに必要な各値をADTS_HEADER構造体に格納します。
BOOL __stdcall _ParseADTSHeader(const PBYTE pHeader,ADTS_HEADER* pADTSHeader)
{<
ASSERT(!::IsBadReadPtr(pHeader,7));
ASSERT(!::IsBadWritePtr(pADTSHeader,sizeof(ADTS_HEADER)));

//先頭バイトは0xFF
if ((pHeader[0]&0xFF)!=0xFF) return FALSE;
//次のバイトの上位3ビットは1
if ((pHeader[1]&0xF0)!=0xF0) return FALSE;

//MPEGバージョン番号を取得します。
//header[1]:b3=0:MPEG4,1:MPEG2

int version=4-((pHeader[1]>>3)&1)*2;

//layer (常に00)
//header[1]:b2b1=00

int layer=(pHeader[1]>>1)&3;
if (layer!=0) return FALSE;

//保護属性を取得します。
//header[1]:b0

BOOL bProtect=pHeader[1]&1;

//音質と圧縮率を取得します。
//header[2]:b7b6

int profile=(pHeader[2]>>6)&3;


//周波数を取得します。
//header[2]:b5b4b3b2

int index=(pHeader[2]>>2)&0x0F;
int sampleRate=g_aacFreq[index];
if (sampleRate==-1) return FALSE;

//private bit
//header[2]:b1

BOOL bPrivateBit=(pHeader[2]>>1)&1;

//チャンネル数を取得します。
//header[2]:b0,header[3]:b7b6

int channel=((pHeader[2]&1)<<2)|((pHeader[3]>>6)&3);
if (channel==0) return FALSE;

//0:オリジナル,1:コピー
//header[3]:b5

BOOL bCopy=(pHeader[3]>>5)&1;

//home
//header[3]:b4

BOOL bHome=(pHeader[3]>>4)&1;

//copyright identification bit
//header[3]:b3

BOOL bCopyrightIDbit=(pHeader[3]>>3)&1;

//copyright identification start
//header[3]:b2

BOOL bCopyrightIDstart=(pHeader[3]>>2)&1;

//フレームサイズ
//header[3]:b1b0
//header[4]:b7~b0
//header[5]:b7b6b5

DWORD frameSize=((((DWORD)pHeader[3])<<11)&0x1800)|
((((DWORD)pHeader[4])<<3)&0x7F8)|
((((DWORD)pHeader[5])>>5)&0x007);

//ADTSバッファ残量
//header[5]:b4~b0
//header[6]:b7~b2

DWORD nFullness=((((DWORD)pHeader[5])<<6)&0x7c0)|
((((DWORD)pHeader[6])>>2)&0x03F);

//no raw data blocks in frame
//header[6]:b1b2

int remainBlocks=pHeader[6]&3;

//データをADTS_HEADER構造体に格納します。
pADTSHeader->iVersion=version;
pADTSHeader->bProtect=bProtect;
pADTSHeader->iProfile=profile;
pADTSHeader->sampleRate=sampleRate;
pADTSHeader->nChannel=channel;
pADTSHeader->bCopy=bCopy;
pADTSHeader->nFrameSize=frameSize;
pADTSHeader->nFullness=nFullness;
return TRUE;
}


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

//このヘッダーでneaacdec.hのインクルードは避けたいので、構造体の型のみ宣言しておきます。
typedef struct faacEncConfiguration faacEncConfiguration;
typedef faacEncConfiguration *faacEncConfigurationPtr;
typedef void *NeAACDecHandle;

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

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

enum streamFormat {
RAW_STREAM=0,
ADTS_STREAM=1,
};

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

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

//faacEncConfiguration構造体の初期化
void InitEncConfiguration();
//faacEncConfiguration構造体の取得(オーバーライド)
virtual BOOL GetEncConfiguration(faacEncConfigurationPtr pConfigAAC,faacEncConfigurationPtr pConfigMP4=NULL);
//faacEncConfiguration構造体の設定(オーバーライド)
virtual BOOL SetEncConfiguration(faacEncConfigurationPtr pConfigAAC,faacEncConfigurationPtr pConfigMP4=NULL);

//faacバージョンの取得
const char* GetFaacVersion(){return m_pszFaacId;};
//faacエンコーダー名の取得
const char* GetFaacCopyright(){return m_pszFaacCopyright;};

protected:
//AACファイルを開く(オーバーライド)
// lpszFileName:ファイルのパス名
virtual BOOL ReadFile(LPCTSTR lpszFileName);
//ヘッダーの読み込み
BOOL ReadHeader(CFile* infile);
//AACファイルの書き込み(オーバーライド)
virtual BOOL WriteFile(LPCTSTR lpszFileName);
//AACストリームの書き込み
BOOL StreamOut(CFile* outfile,faacEncConfigurationPtr pConfig=NULL);

//デコーダー
NeAACDecHandle m_hDecoder;
int m_headerType; //0:RAW,1:ADIF,2:ADTS
CByteArray m_byteInCache; //入力側キャッシュ

//エンコーダー
char* m_pszFaacId; //バージョン
char* m_pszFaacCopyright; //著作権
faacEncConfiguration* m_pConfig;//エンコーダー構成
};


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

//コンストラクタ
CAACFile::CAACFile(CWnd* pWndOwner)
:CMP3File(pWndOwner)
{
Init();
//faacEncConfiguration構造体の初期化
InitEncConfiguration();
}

//デストラクタ
CAACFile::~CAACFile()
{
delete m_pConfig;
Delete();
}



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

//メンバー変数の初期化
void CAACFile::Init()
{
//デコーダー
m_hDecoder=0;
m_headerType=RAW;
m_byteInCache.RemoveAll();
}

//メンバー変数の削除
void CAACFile::Delete()
{
CMP3File::Delete();
if (m_hDecoder) ::NeAACDecClose(m_hDecoder);
Init();
}


CWaveFile::OpenFile関数から呼び出されるCWaveFile::ReadFile関数にオーバーライドして、AACファイルを開いた後、CAACFile::ReadHeader関数を呼び出します。

//AACファイルをオープンします。
// lpszFileName:オープンするファイル名。

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

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


AACファイルのヘッダ情報を読み取り、その情報を元にデコーダを開き、AACファイル再生のため準備をします。

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

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

//ADTSヘッダーを探します。
BYTE header[128];
ADTS_HEADER ADTSHeader={0};
int bitrate =0; //ビットレート

while(TRUE){
//読み飛ばし位置に移動してから10バイト読みます。
infile->Seek(m_dwSkipOffset,CFile::begin);
if (infile->Read(header,10)!=10) return FALSE;

//ADTSヘッダーがある場合。
if (_ParseADTSHeader((PBYTE)&header,&ADTSHeader)){
m_headerType=ADTS;
break;
}
//ADIFヘッダーがある場合。
else if (::memcmp(header,"ADIF",4)==0){
int skipSize=(header[4]&0x80)?9:0;
bitrate=((DWORD)(header[4+skipSize]&0x0F)<<19)|
((DWORD) header[5+skipSize]<<11)|
((DWORD) header[6+skipSize]<<3 )|
((DWORD) header[7+skipSize]&0xE0);
float length=(float)fileSize;
if (length) length=((float)length*8.f)/((float)bitrate)+0.5f;
bitrate=(int)((float)bitrate/1000.0f+0.5f);
m_headerType=ADIF;
break;
}
//"ID3"タグがある場合。
else if (::memcmp(header,"ID3",3)==0){
//ID3V2タグサイズを算出します。
DWORD sizeTag=((header[6]&0x7F)<<21)|((header[7]&0x7F)<<14)|
((header[8]&0x7F)<< 7)|(header[9]&0x7F);
//読み飛ばし位置を算出します。
m_dwSkipOffset+=(sizeTag+10);
//バッファに生データを保存します。
BYTE* pBuffer=NULL;
switch(header[3]){
case 2: //ID3v2.2タグの場合
if (!m_byteID3v22.GetSize()){
m_byteID3v22.SetSize(sizeTag+10);
pBuffer=m_byteID3v22.GetData();
m_ID3v22Version=(header[3]<<8)|(header[4]);
}
break;
case 3: //ID3v2.3タグの場合
if (!m_byteID3v23.GetSize()){
m_byteID3v23.SetSize(sizeTag+10);
pBuffer=m_byteID3v23.GetData();
m_ID3v23Version=(header[3]<<8)|(header[4]);
}
break;
case 4: //ID3v2.4タグの場合
if (!m_byteID3v24.GetSize()){
m_byteID3v24.SetSize(sizeTag+10);
pBuffer=m_byteID3v24.GetData();
m_ID3v24Version=(header[3]<<8)|(header[4]);
}
break;
default:
pBuffer=NULL;
}
//ID3v2タグバッファが確保されている場合は、
//ID3v2タグの内容をバッファに読み込みます。

if (pBuffer){
::CopyMemory(pBuffer,&header,10);
UINT size=infile->Read(pBuffer+10,sizeTag);
if (size<sizeTag) return FALSE;
}
}
//生データの場合
else if (::memcmp(header,"\xDE\x36\0\0",4)==0){
m_headerType=RAW;
break;
}
//ADTSヘッダー/ADIFヘッダー/ID3v2タグ/生データでもない場合。
else
//エラー終了します。
return FALSE;
}

BYTE channels=2;
int sampleRate=44100;
//ADTSヘッダーがある場合。
if (m_headerType==ADTS){
//フレームヘッダーから得られた値をメンバー変数に保存します。
m_iVersion =ADTSHeader.iVersion; //MPEGバージョン番号
m_iLayer =0; //MPEGレイヤー
m_iBitRate =bitrate; //ビットレート
channels=(BYTE)ADTSHeader.nChannel;
if ((channels!=1)&&(channels!=2)) return FALSE;

sampleRate=ADTSHeader.sampleRate;
m_uFlagBitRate=(ADTSHeader.nFullness==0x7FF)?VBR:CBR;
//フレーム数
int nFrames=1;
//データ総数
m_dwStreamSize=ADTSHeader.nFrameSize;
//ファイルポインタ
DWORD offset=m_dwSkipOffset+ADTSHeader.nFrameSize;
//すべてのフレームについて調べます。
while(TRUE){
//次のフレームの先頭位置を探します。
BOOL bFind=FALSE;
while(TRUE){
//読み飛ばし位置に移動してから8バイト読みます。
infile->Seek(offset,CFile::begin);
if (infile->Read(header,8)!=8) break;
//ADTSヘッダーかどうか調べます。
if (_ParseADTSHeader((PBYTE)&header,&ADTSHeader)){
bFind=TRUE;
break;
}
//見つからない場合は、ファイルポインタを1つ進めます。
offset++;
}
if (!bFind) break;
if (!ADTSHeader.nFrameSize) break;

offset+=ADTSHeader.nFrameSize;
m_dwStreamSize+=ADTSHeader.nFrameSize;
nFrames++;
}
m_nFrames=nFrames;
}
//生データの場合。
else
//AACデータストリームサイズは、ファイル長さから読み飛ばし位置を差し引きます。
m_dwStreamSize=fileSize-m_dwSkipOffset;

//ID3v1タグがあるか調べます。
//ファイル末尾から128バイト戻った位置にシークします。

infile->Seek(fileSize-128,CFile::begin);

UINT size=infile->Read(header,128);
if (size<128) return FALSE;

//"TAG"がある場合。
if (::memcmp(header,"TAG",3)==0){
//バージョン番号を保存します。
m_id3v1Version=0x100|((header[125]=='\0')?1:0);
//バッファに生データを保存します。
m_byteId3v1.SetSize(128);
::CopyMemory(m_byteId3v1.GetData(),&header,128);
}

//総演奏時間を算出します。
float totalTime=0;
//ADTSヘッダーがある場合のみ。
if (m_headerType==ADTS){
float framesPerSec=(float)sampleRate/1024.0f;
float bytesPerFrame=(m_nFrames)? (float)m_dwStreamSize/(float)(m_nFrames*1000):0;
//平均ビットレート
m_iAveBitRate=(int)(8.0*bytesPerFrame*framesPerSec+0.5);
totalTime=(framesPerSec)? (float)m_nFrames/framesPerSec:1;
}
//現在のファイル位置を、読み飛ばし位置に移動します。
infile->Seek(m_dwSkipOffset,CFile::begin);

//デコーダーを開きます。
NeAACDecHandle hDecoder=::NeAACDecOpen();
if (!hDecoder) return FALSE;

//現在のデコーダー構成構造体を設定します。
NeAACDecConfigurationPtr config=::NeAACDecGetCurrentConfiguration(hDecoder);
config->defObjectType=MAIN;
config->outputFormat =1;
::NeAACDecSetConfiguration(hDecoder, config);

//ファイル読み込み用バッファを確保します。
DWORD cbSrcSize=FAAD_MIN_STREAMSIZE*channels;
m_byteSrcBuf.SetSize(cbSrcSize);
BYTE *pbSrc=m_byteSrcBuf.GetData();
::ZeroMemory(pbSrc,cbSrcSize);
//バッファにファイルを読み込みます。
UINT bytesInBuffer=infile->Read(pbSrc,cbSrcSize);

//入力側キャッシュにバッファの内容をコピーします。
m_byteInCache.Copy(m_byteSrcBuf);
m_byteInCache.SetSize(bytesInBuffer);
//出力側キャッシュを初期化します。
m_byteCache.SetSize(0);

//入力側キャッシュの内容でデコーダーを初期化します。
BYTE* buffer=m_byteInCache.GetData();
DWORD samplerate;
int bytesconsumed=::NeAACDecInit(hDecoder,buffer,bytesInBuffer,&samplerate, &channels);
if (bytesconsumed<0){
::NeAACDecClose(hDecoder);
return FALSE;
}
//デコーダーが消費した分のデータを削除して、残りデータを左シフトします。
m_byteInCache.RemoveAt(0,bytesConsumed);

//Wave再生の為のWAVEFOTMATEX構造体を作成します。
m_wfx.cbSize=sizeof(WAVEFORMATEX);
m_wfx.wFormatTag=WAVE_FORMAT_PCM;
m_wfx.nSamplesPerSec=samplerate;
m_wfx.nChannels=channels;
m_wfx.wBitsPerSample=16;
m_wfx.nBlockAlign=channels*2;
m_wfx.nAvgBytesPerSec=m_wfx.nBlockAlign*m_wfx.nSamplesPerSec;

m_uFlagBitRate=VBR;
m_hDecoder=hDecoder;

//ADTSヘッダーがある場合。
if (m_headerType==ADTS){
//AACの総演奏時間から変換後の総データサイズを取得します。
m_nDataSize=(int)(totalTime*(float)m_wfx.nAvgBytesPerSec+0.5);
//フレームサイズに相当するデコード後のデータサイズの平均値を算出します。
m_sizeAveFrameDecoded=m_nDataSize/m_nFrames;
m_sizeFrame=m_dwStreamSize/m_nFrames;
}
//生データの場合。
else{
m_nDataSize=m_dwStreamSize*16;
m_sizeFrame=cbSrcSize;
}
return TRUE;
}



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

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

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

if (!IsAACFile()) return CMP3File::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);
//現在演奏中のADTSヘッダーのあるAACファイルで、
//現在位置と指定位置が100_秒以上違えば、指定位置にシークします。

if ((IsPlaying())&&(m_headerType==ADTS)&&
((DWORD)abs((int)(dwPosRead-dwPosRecent))>(m_wfx.nAvgBytesPerSec/10))){
//キャッシュを初期化します。
m_byteCache.SetSize(0);
m_byteInCache.SetSize(0);

//指定位置から末尾に向かって、一番近い位置にあるADTSヘッダーを探します。
BYTE header[8];
ADTS_HEADER ADTSHeader={0};
while(TRUE){
//読み飛ばし位置に移動してから8バイト読みます。
m_infile->Seek(dwPosRead,CFile::begin);
if (m_infile->Read(header,8)!=8) return FALSE;
//ADTSヘッダーかどうか調べます。
if (_ParseADTSHeader((PBYTE)&header,&ADTSHeader)) break;
//ADTSヘッダーではない場合は、
//読み飛ばし位置を1バイト進めてから再び読み直します。

dwPosRead++;
}
//探し出したADTSヘッダーの先頭位置に移動します。
m_infile->Seek(dwPosRead,CFile::begin);
}

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

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

//入力側キャッシュ内のバッファの先頭アドレスを取得します。
BYTE* buffer=m_byteInCache.GetData();
//入力側キャッシュ内のバッファのデータサイズを取得します。
UINT bytesInBuffer=(UINT)(UINT_PTR)m_byteInCache.GetSize();

//ファイル読み込み用バッファの先頭アドレスを取得します。
BYTE* pbSrc=m_byteSrcBuf.GetData();
//ファイル読み込み用バッファのデータサイズを取得します。
UINT cbSrcLength=(UINT)(UINT_PTR)m_byteSrcBuf.GetSize();

//入力側キャッシュ内のデータサイズがストリームサイズに満たない場合は、
//ファイルから読み込みます。

if (bytesInBuffer<cbSrcLength){
UINT size=m_infile->Read(pbSrc,cbSrcLength);
//読み込んだデータがある場合。
if(size>0){
//入力側キャッシュの末尾に、読み込んだデータを追加します。
m_byteInCache.SetSize(bytesInBuffer+size);
buffer=m_byteInCache.GetData();
::CopyMemory(buffer+bytesInBuffer,pbSrc,size);
bytesInBuffer=(UINT)(UINT_PTR)m_byteInCache.GetSize();
}
//ファイルの末尾に来た場合。
else{
TRACE1("Reach EOF bytesInBuffer=%08x\n",bytesInBuffer);
}
}

//デコードします。
NeAACDecFrameInfo frameInfo={0};
const void* pDecoded=(const void*)::NeAACDecDecode(m_hDecoder,&frameInfo,buffer,bytesInBuffer);

//入力側キャッシュ内のデータのうち、デコーダーが読み込んだデータを削除して、
//残りのデータを左シフトします。

m_byteInCache.RemoveAt(0,frameInfo.bytesconsumed);

//デコードエラー時の場合。
if (frameInfo.error){
TRACE1("Decode Error=%d\n",frameInfo.error);
if (!bytesInBuffer) return FALSE;
m_byteInCache.SetSize(0);
}
//デコード済みデータがある場合
if (frameInfo.samples>0){
//出力側キャッシュの末尾に、デコード済みデータを追加します。
nCaches=(UINT)(UINT_PTR)m_byteCache.GetSize();
DWORD sampleBytes=frameInfo.samples*m_wfx.wBitsPerSample/8;
m_byteCache.SetSize(nCaches+sampleBytes);
::CopyMemory(m_byteCache.GetData()+nCaches,pDecoded,sampleBytes);
nCaches=(UINT)(UINT_PTR)m_byteCache.GetSize();
}
//デコード済みデータがなく、入力側キャッシュにもデータが残っていない場合は、
//エラー終了します。

else if (!bytesInBuffer){
TRACE0("Not Find Decoded Data\n");
return FALSE;
}
}
//バッファに取り込むサイズを算定して、キャッシュからデータをコピーします。
UINT readBytes=min(nBufSize,nCaches);
::CopyMemory(pBuffer,m_byteCache.GetData(),readBytes);

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

m_byteCache.RemoveAt(0,readBytes);

//指定の演奏位置を、読み込んだAACデータに相当する音源データ分進めます。
dwPosRecent=(DWORD)m_infile->GetPosition();
//ADTSヘッダーがある場合。
if (m_headerType==ADTS){
indexFrame=(dwPosRecent-m_dwSkipOffset)/m_sizeFrame;
*pIndexSample=(m_sizeAveFrameDecoded*indexFrame)/m_wfx.nBlockAlign;
}
//生データの場合。
else{
*pIndexSample=(UINT)((float)m_nDataSize*(float)(dwPosRecent-m_dwSkipOffset)/
(float)m_dwStreamSize)/m_wfx.nBlockAlign;
}
//読み込んだバイト数を返して終了します。
return readBytes;
}



WaveOut.h
WaveOut.cpp
WaveFile.h
WaveFile.cpp
CDDAFile.h
CDDAFile.cpp
MP3File.h
MP3File.cpp
AACFile.h
AACFile.cpp




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

メールアドレス:

ホームページアドレス:

コメント:

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


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

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