2012年05月25日

AACファイルの書き込み



faacエンコーダーを介してWAVE音源データAACストリームにエンコードしてAACファイルに書き込む手順は、以下の通りです。

  1. AACエンコードに必要なfaac 1.26をインストールします。
  2. faacエンコーダーを開いてfaacEncConfiguration構造体入力サンプル数最大出力バイト数等を取得し、faacEncConfiguration構造体の各値を、目的の使用を満たす値に再設定し直します。
  3. 取得した入力サンプル数から、WAVE音源データを一時格納するための入力バッファサイズを算出します。
  4. 取得した最大出力バイト数から、エンコード済みデータを格納する出力バッファサイズを算出します。
  5. 入力/出力バッファの領域を確保したら、WAVE音源データを読み込み、入力バッファの32ビットデータの上位ワードに書き込みます。
  6. 入力バッファの32ビットデータをAACストリームにエンコードして、AACファイルに書き込みます。
  7. WAVE音源データの読み込みが終了したら、エンコーダーに残ったすべてのデータを吐き出させてから、faacエンコーダーをクローズします。




faacEncConfiguration構造体を扱うためのメンバー関数です。

//faacEncConfiguration構造体の初期化
void CAACFile::InitEncConfiguration()
{
//faacバージョンの取得
int version=::faacEncGetVersion(&m_pszFaacId,&m_pszFaacCopyright);
ASSERT(version==FAAC_CFG_VERSION);

unsigned long inputSamples;
unsigned long maxOutputBytes;

//エンコーダーを開いてFaacエンコーダー構成構造体を取得します。
faacEncHandle hEncoder=::faacEncOpen(44100,2,&inputSamples,&maxOutputBytes);

m_pConfig=new faacEncConfiguration;
::ZeroMemory(m_pConfig,sizeof(faacEncConfiguration));
if (hEncoder){
//取得したエンコーダー構成構造体を初期化しておきます。
faacEncConfigurationPtr pConfig=::faacEncGetCurrentConfiguration(hEncoder);
::CopyMemory(m_pConfig,pConfig,sizeof(faacEncConfiguration));
m_pConfig->allowMidside =TRUE;
m_pConfig->useTns =TRUE; //AAC:TRUE,MP4:FALSE
m_pConfig->useLfe =FALSE;
m_pConfig->outputFormat =TRUE; //AAC:TRUE,MP4:FALSE
m_pConfig->mpegVersion =MPEG4;
m_pConfig->aacObjectType=LOW;
m_pConfig->quantqual =100;
m_pConfig->bandWidth =0;
m_pConfig->bitRate =0;
m_pConfig->inputFormat =FAAC_INPUT_32BIT;
m_pConfig->shortctl =SHORTCTL_NORMAL;
::faacEncClose(hEncoder);
}
}

//faacEncConfiguration構造体の取得
BOOL CAACFile::GetEncConfiguration(faacEncConfigurationPtr pConfigAAC,faacEncConfigurationPtr)
{
if (!AfxIsValidAddress(pConfigAAC,sizeof(faacEncConfiguration),FALSE)) return FALSE;
::CopyMemory(pConfigAAC,m_pConfig,sizeof(faacEncConfiguration));
return TRUE;
}

//faacEncConfiguration構造体の設定
BOOL CAACFile::SetEncConfiguration(faacEncConfigurationPtr pConfigAAC,faacEncConfigurationPtr)
{
if (!AfxIsValidAddress(pConfigAAC,sizeof(faacEncConfiguration),FALSE)) return FALSE;
::CopyMemory(m_pConfig,pConfigAAC,sizeof(faacEncConfiguration));
return TRUE;
}


CWaveFile::SaveFile関数から呼び出されるCWaveFile::WriteFile関数にオーバーライドして、現在演奏中のファイルをAACファイルに保存します。

//AACファイルの書き込み(オーバーライド)
BOOL CAACFile::WriteFile(LPCTSTR lpszFileName)
{
//拡張子名の取得
LPTSTR pExt=::PathFindExtension(lpszFileName);
//拡張子名が「.aac」で無い場合は、CMP3File::WriteFile関数を呼び出します。
if (::StrCmpNI(pExt,_T(".aac"),4)!=0) return CMP3File::WriteFile(lpszFileName);

//ファイルを開きます。
CFile outfile;
if (!outfile.Open(lpszFileName,CFile::modeCreate|CFile::modeReadWrite)) return FALSE;

//ID3v2.2タグがあれば、ファイルに書き込みます。
INT_PTR v22size=m_byteID3v22.GetSize();
BYTE* v22data=m_byteID3v22.GetData();
if ((v22data)&&(v22size)) outfile.Write(v22data,(UINT)v22size);

//ID3v2.3タグがあれば、ファイルに書き込みます。
INT_PTR v23size=m_byteID3v23.GetSize();
BYTE* v23data=m_byteID3v23.GetData();
if ((v23data)&&(v23size)) outfile.Write(v23data,(UINT)v23size);

//ID3v2.4タグがあれば、ファイルに書き込みます。
INT_PTR v24size=m_byteID3v24.GetSize();
BYTE* v24data=m_byteID3v24.GetData();
if ((v24data)&&(v24size)) outfile.Write(v24data,(UINT)v24size);

//AACファイルの場合は、AACデータの内容をそのまま書き写します。
if (IsAACFile()){
//AACデータを書き込む為のバッファを作成します。
CByteArray array;
UINT sizeBuf=GetSizeOfSaveBuffer();
array.SetSize(sizeBuf);
BYTE* buffer=array.GetData();

//AACデータを書き込みます。
UINT readSize=0;
m_infile->Seek(m_dwSkipOffset,CFile::begin);
while(readSize<m_dwStreamSize){
UINT sizeToRead=min(sizeBuf,m_dwStreamSize-readSize);
UINT readBytes=m_infile->Read(buffer,sizeToRead);
if (!readBytes) break;
outfile.Write(buffer,readBytes);
readSize+=readBytes;
}
}
//それ以外の場合は、エンコードし直します。
else if (!StreamOut(&outfile)){
outfile.Close();
::DeleteFile(lpszFileName);
return FALSE;
}

//最終ADTSヘッダー位置を探します。
ULONGLONG endOfFile=outfile.GetPosition();
ULONGLONG ptrFile=endOfFile-8;

BYTE header[8];
ADTS_HEADER ADTSHeader={0};
while(ptrFile>m_dwSkipOffset){
//読み飛ばし位置に移動してから8バイト読みます。
outfile.Seek(ptrFile,CFile::begin);
UINT size=outfile.Read(header,8);
ASSERT(size==8);
//ADTSヘッダーかどうか調べます。
if (_ParseADTSHeader((PBYTE)&header,&ADTSHeader)) break;
//ADTSヘッダーではない場合は、
//読み飛ばし位置を1バイト戻してから再び読み直します。

ptrFile--;
}
UINT sizeFrame=(UINT)(ULONGLONG)(endOfFile-ptrFile);
//実際の最終フレームサイズが足りない場合。
if (sizeFrame<(UINT)ADTSHeader.nFrameSize){
//足りないサイズ分、0xFFを書き足します。
outfile.SeekToEnd();
CByteArray byteBuf;
UINT sizePadding=ADTSHeader.nFrameSize-sizeFrame;
byteBuf.SetSize(sizePadding);
PBYTE lpBuf=byteBuf.GetData();
::memset(lpBuf,0xFF,sizePadding);
outfile.Write(lpBuf,sizePadding);
}
//実際の最終フレームサイズが多い場合。
else if (sizeFrame>(UINT)ADTSHeader.nFrameSize)
//フレームの末尾に移動します。
outfile.Seek(ptrFile+ADTSHeader.nFrameSize,CFile::begin);
//正常な場合。
else
//ファイル末尾に移動します。
outfile.SeekToEnd();

//ID3v1タグを書き込みます。
INT_PTR v1size=m_byteID3v1.GetSize();
BYTE* v1data=m_byteID3v1.GetData();
if ((v1data)&&(v1size)){
ASSERT(v1size==128);
outfile.Write(v1data,(UINT)v1size);
}
outfile.Close();

return TRUE;
}


引数で指定したfaacEncConfiguration構造体に従って、WAVE音源データAACエンコードする関数です。

//AACストリームの書き込み
BOOL CAACFile::StreamOut(CFile* outfile,faacEncConfigurationPtr pConfig)
{
//エンコーダーを開きます。
unsigned long inputSamples;
unsigned long maxOutputBytes;
faacEncHandle hEncoder=::faacEncOpen(m_wfx.nSamplesPerSec,m_wfx.nChannels,
&inputSamples,&maxOutputBytes);
if (!hEncoder) return FALSE;

//エンコーダー構成構造体の設定
faacEncConfigurationPtr config=::faacEncGetCurrentConfiguration(hEncoder);

//引数のエンコーダー構成構造体へのポインタが有効な値の場合。
if (AfxIsValidAddress(pConfig,sizeof(faacEncConfiguration),FALSE)){
m_pConfig->allowMidside =pConfig->allowMidside;
m_pConfig->useTns =pConfig->useTns;
m_pConfig->useLfe =pConfig->useLfe;
m_pConfig->outputFormat =pConfig->outputFormat;
m_pConfig->mpegVersion =pConfig->mpegVersion;
m_pConfig->aacObjectType=pConfig->aacObjectType;
m_pConfig->quantqual =pConfig->quantqual;
m_pConfig->bandWidth =pConfig->bandWidth;
m_pConfig->bitRate =pConfig->bitRate;
m_pConfig->shortctl =pConfig->shortctl;
}

if ((m_pConfig->shortctl!=SHORTCTL_NORMAL)&&
(m_pConfig->shortctl!=SHORTCTL_NOSHORT)&&
(m_pConfig->shortctl!=SHORTCTL_NOLONG))
m_pConfig->shortctl=SHORTCTL_NORMAL;
if (m_pConfig->bandWidth>(m_wfx.nSamplesPerSec/2))
m_pConfig->bandWidth=m_wfx.nSamplesPerSec/2;
m_pConfig->useLfe=(m_wfx.nChannels>=6);

//エンコーダー構成構造体を設定し直します。
config->allowMidside =m_pConfig->allowMidside;
config->useTns =m_pConfig->useTns;
config->useLfe =m_pConfig->useLfe;
config->outputFormat =m_pConfig->outputFormat;
config->mpegVersion =m_pConfig->mpegVersion;
config->aacObjectType =m_pConfig->aacObjectType;
config->quantqual =m_pConfig->quantqual;
config->bandWidth =m_pConfig->bandWidth;
config->bitRate =m_pConfig->bitRate;
config->shortctl =m_pConfig->shortctl;
config->inputFormat =FAAC_INPUT_32BIT;

if (!::faacEncSetConfiguration(hEncoder,config)){
::faacEncClose(hEncoder);
return FALSE;
}

//エンコードするファイルの保存用サイズを取得します。
DWORD cbSrcSize=GetSizeOfSaveBuffer();
//保存バッファサイズがサンプルバッファより小さい場合は、
//サンプルバッファより大きくなるようにします。

DWORD cbSampleSize=inputSamples*sizeof(int);
if (cbSrcSize<cbSampleSize)
cbSrcSize=(cbSampleSize/cbSrcSize+((cbSampleSize%cbSrcSize)!=0))*cbSrcSize;

//音源データを格納するバッファを作成します。
CByteArray byteSrcWav;
byteSrcWav.SetSize(cbSrcSize);
//バッファへのポインタを取得します。
PBYTE pSrcWav=byteSrcWav.GetData();

//エンコードデータを格納するバッファを作成します。
DWORD cbDstSize=maxOutputBytes;
CByteArray byteDstAAC;
byteDstAAC.SetSize(cbDstSize);
//バッファへのポインタを取得します。
PBYTE pDstAAC=byteDstAAC.GetData();

//サンプルバッファを作成します。
CByteArray byteSampleBuf;
byteSampleBuf.SetSize(cbSampleSize);
int32_t* pSampleBuf=(int32_t*)byteSampleBuf.GetData();

//読み込み用のインデックスをゼロにします。
UINT indexSample=0;
UINT nRemain=0;
BOOL bDrain=FALSE;
while(TRUE){
//バッファを初期化します。
::ZeroMemory(pSrcWav,cbSrcSize);
::ZeroMemory(pDstAAC,cbDstSize);

UINT writtenBytes=0;
//現在の演奏条件での音源データを取得します。
UINT readBytes=StreamIn(pSrcWav,cbSrcSize,&indexSample);
//最後まで読み込んだら、ループを抜けます。
if ((!readBytes)&&(!nRemain)&&(!writtenBytes)) break;
//最後のフレームの末尾を読み終えたら、払い出しフラグを立てます。
if (readBytes!=cbSrcSize) bDrain=TRUE;
PBYTE src=pSrcWav;
UINT bytesPerSample=m_wfx.wBitsPerSample/8;
UINT readSamples=readBytes/bytesPerSample;
while(readSamples>0){
UINT nSamples=min(readSamples,inputSamples);
if ((nRemain)&&(nSamples+nRemain>inputSamples))
nSamples=inputSamples-nRemain;
switch (bytesPerSample){
case 1://サンプルバイト数=1の場合は、符号付ワードに変換します。
for (UINT i=0;i<nSamples;i++)
pSampleBuf[i+nRemain]=(src[i]-128)*65536;
src+=nSamples;
break;
case 2://サンプルバイト数=2の場合は、ワード値の上位バイトに代入します。
for (UINT i=0;i<nSamples;i++)
pSampleBuf[i+nRemain]=(((short *)src)[i])<<8;
src+=(nSamples*2);
break;
default:
return FALSE;
}
readSamples-=nSamples;
if (nRemain){
nSamples+=nRemain;
nRemain=0;
}
if (nSamples==inputSamples){
//Wave音源データからAACデータに変換します。
writtenBytes=::faacEncEncode(hEncoder,pSampleBuf,nSamples,pDstAAC,cbDstSize);
if (writtenBytes<0) break;
//ファイルに音源データを書き込みます。
if (writtenBytes) outfile->Write(pDstAAC,writtenBytes);
}
else nRemain=nSamples;
}
//オーナーウィンドウがあれば、プログレスバーの位置を通知します。
if (m_pWndOwner) m_pWndOwner->PostMessage(PBM_SETPOS,indexSample,0);
//払い出しフラグがあるの場合は、残りデータを吐き出させます。
if (bDrain){
UINT nSamples=nRemain;
nRemain=0;
while(writtenBytes){
//Wave音源データからAACデータに変換します。
writtenBytes=::faacEncEncode(hEncoder,pSampleBuf,nSamples,pDstAAC,cbDstSize);
if (writtenBytes<=0) break;
//ファイルに音源データを書き込みます。
if (writtenBytes) outfile->Write(pDstAAC,writtenBytes);
if (nSamples) nSamples=0;
}
}
}
//エンコーダを閉じて終了します。
::faacEncClose(hEncoder);
return TRUE;
}



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




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

メールアドレス:

ホームページアドレス:

コメント:

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


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

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