FC2ブログ

ホーム > AACファイルフォーマット > AACファイルの書き込み

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;
}


現在演奏中のファイルをAACファイルに保存します。

//AACファイルの書き込み
BOOL CAACFile::SaveFile(LPCTSTR lpszFileName)
{
//書き込み対象のファイルがない場合は、FALSEを戻して終了します。
if (!IsOpen()) return FALSE;

//拡張子名の取得
LPTSTR pExt=::PathFindExtension(lpszFileName);
//拡張子名が「.aac」で無い場合は、CMP3File::SaveFile関数を呼び出します。
if (::StrCmpNI(pExt,_T(".aac"),4)!=0) return CMP3File::SaveFile(lpszFileName);

//現在演奏中のファイルがAACファイルの場合。
if (IsAACFile()){
//現在演奏中のファイルに上書きする場合。
if (m_strPathName==lpszFileName){
//テンポラリフォルダーのディレクトリパスを取得
DWORD sizeTempPath=::GetTempPath(0,0);
CString strTempPath;
LPTSTR lpszPath=strTempPath.GetBufferSetLength(sizeTempPath);
sizeTempPath=::GetTempPath(sizeTempPath,lpszPath);
if (!sizeTempPath) return FALSE;

//テンポラリファイルを作成し、ファイル名を取得します。
CStringA strPathA=(CStringA)strTempPath;
CStringA strPrefix=(CStringA)m_infile->GetFileName();
CStringA strTempFile;
LPTSTR lpszFile=(LPTSTR)strTempFile.GetBufferSetLength(sizeTempPath+16);
if (!::GetTempFileName((LPCTSTR)strPathA.GetBuffer(),(LPCTSTR)strPrefix.GetBuffer(),0,lpszFile))
return FALSE;

//テンポラリファイルにAACファイルを書き込みます。
if (!SaveAACFile(strTempFile)) return FALSE;

//AACファイルにテンポラリファイルを重ね書きします。
Delete();
::CopyFile(strTempFile,m_strPathName,FALSE);

//テンポラリファイルの削除
::remove(strTempFile);
return TRUE;
}
}
//ID3タグの内容を保存しておきます。
CByteArray byteId3v1Safe,byteId3v22Safe;
CByteArray byteId3v23Safe,byteId3v24Safe;

byteId3v1Safe.Copy(m_byteID3v1);
byteId3v22Safe.Copy(m_byteID3v22);
byteId3v23Safe.Copy(m_byteID3v23);
byteId3v24Safe.Copy(m_byteID3v24);

//ドライバー内に残ったデコードデータの残りが
//保存ファイルデータに混じらないようにするため、
//念のため現在演奏中のファイルを、再度開き直しておきます。

if (!OpenFile(m_strPathName)) return FALSE;

//保存しておいたID3タグの内容を戻します。
m_byteID3v1.Copy(byteId3v1Safe);
m_byteID3v22.Copy(byteId3v22Safe);
m_byteID3v23.Copy(byteId3v23Safe);
m_byteID3v24.Copy(byteId3v24Safe);

return SaveAACFile(lpszFileName);
}

//AACファイル(ID3tag)の書き込み
BOOL CAACFile::SaveAACFile(LPCTSTR lpszFileName)
{
if (!IsOpen()) return FALSE;
//ファイルを開きます。
CFile outfile;
if (!outfile.Open(lpszFileName,CFile::modeCreate|CFile::modeWrite)) 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.2タグがあれば、ファイルに書き込みます。
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;
}

//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 readTotalSize=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;
}
readTotalSize+=readBytes;
//オーナーウィンドウがあれば、プログレスバーの位置を通知します。
if (m_pWndOwner) m_pWndOwner->PostMessage(PBM_SETPOS,readTotalSize,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;
}


現在オープンされているファイル形式に適した、出力バッファサイズを返します。

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



サンプルプログラム(VisualC++net2003ソリューション)AACFileTest02B.zip

上記のzipファイルをダウンロードして、faad2-2.7のインストールで作成した「faad2-2.7」及び、「libfaad-vc7」フォルダー、faac 1.26のインストールで作成した「faac」及び、「libfaac-vc7」フォルダーがある同じフォルダー内にWinRAR等を使って解凍すると、「AACFileTest02B」という名のフォルダーが作成されます。



「AACFileTest02」フォルダー内にある「AACFileTest.sln」を開いて「F5」キーを押すと、ビルド確認のダイヤログが表示されるので「Yes」を選択してソリューションをビルドします。



ビルドが終わると直ちにプログラムが自動起動して下の図にあるダイヤログが表示されます。



パソコン本体のCDROMドライブに音楽CDを入れてCDROMドライブのドアをクローズし、ダイヤログにある「CDDA」ボタンをクリックするとCDが読み込まれてトラック1から再生されます。
(※この時ウィンドウズのCDROMドライブのAUTOPLAY機能は解除しておいて下さい。)

CDテキストがない場合、ダイヤログのタイトルバーは「CDDAFileTest」のままで、曲名はトラック1から順に「Track??.cdda」と表示されますが、freeDBからのCDDBの読み込みに成功すると、ドロップダウンリストにCD内の全ての曲名が登録され、その内容からID3v1/v2タグを自動作成し、「CDDAのプロパティ」ダイヤログの下段にあるリストビューに表示します。



メニューから「ファイル」/「CDの一括保存...」を選択し、「書き込みの設定」ダイヤログを表示させます。



「書き込む場所」、「フォルダー名」、「ID3v1/v2.3タグ作成チェックボックス」、「曲名チェックボックス」、「曲名」等を確認/変更してから、「保存ファイル形式」は「AACファイル」を選択して、「適用する」ボタンを押すと、「AACエンコードスペックの設定」ダイヤログが表示されます。



必要に応じて各値を設定して「適用する」ボタンを押すと、一括保存シーケンスが開始されます。



「曲名チェックボックス」にチェックのあった全てのトラックの保存が終了すると、一番最初に保存したAACファイルに戻ってから、また順に再生が始まります。



「CDDAのプロパティ」ダイヤログで、作成されたAACファイルのMpegバージョン/平均ビットレート等のスペック値と、作成されたID3タグの内容を確認してください。



スポンサーサイト



コメント: 0

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

Trackback+Pingback: 1

TrackBack URL for this entry
http://hiroshi0945.blog75.fc2.com/tb.php/76-d6c70ddd
Listed below are links to weblogs that reference
AACファイルの書き込み from マルチメディアファイルフォーマット
Trackback from まとめwoネタ速neo 2012-05-25 Fri 21:20:22

まとめtyaiました【AACファイルの書き込み】

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

Home > AACファイルフォーマット > AACファイルの書き込み

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

この人とブロともになる

ブロとも一覧

このページの先頭へ戻る