2011年04月16日

MP3ファイルの再生 第1部



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


続きを読む


posted by ひろし at 13:52| Comment(0) | MP3ファイルフォーマット | このブログの読者になる | 更新情報をチェックする

2011年04月20日

MP3ファイルの再生 第2部



CWaveFile::OpenFile関数から呼び出された後、MP3ファイルを開いて、CMP3File::ReadHeaderを呼び出します。

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

BOOL CMP3File::ReadFile(LPCTSTR lpszFileName)
{
//CDDAFile::ReadFile関数を呼び出し、CD-DA若しくはWAVEファイルとしてファイルを読み込みます。
//読み込みに成功した場合は、正常終了します。

if (CDDAFile::ReadFile(lpszFileName)) return TRUE;

//ファイルのオープンに失敗した場合は、エラー終了します。
CFile* infile=new CFile;
if (!infile->Open(lpszFileName,CFile::modeRead|CFile::shareDenyNone)){
delete infile;
return FALSE;
}

//ヘッダー読み込みに失敗した場合は、エラー終了します。
if (!ReadHeader(infile)){
infile->Close();
delete infile;
return FALSE;
}

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


MP3ファイルフレームヘッダからMPEGLAYER3WAVEFORMAT構造体を作成し、この構造体を元にACMストリームをオープンし、デコードと同時に演奏するための準備としてACMストリームヘッダを作成しておきます。

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

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

//フレームヘッダーを探します。
BYTE header[128];
FRAMEHEADER frameHeader={0};
while(TRUE){
//読み飛ばし位置に移動してから10バイト読みます。
infile->Seek(m_dwSkipOffset,CFile::begin);
if (infile->Read(header,10)!=10) return FALSE;
//フレームヘッダーがあった場合。
if (_ParseFrameHeader((PBYTE)&header,&frameHeader)){
//ファイルポインタ
DWORD offset=m_dwSkipOffset+frameHeader.frameSize;
//フレームサイズが正しいかどうか調べます。
FRAMEHEADER fh={0};
//読み飛ばし位置に移動してから4バイト読みます。
infile->Seek(offset,CFile::begin);
if (infile->Read(header,4)!=4) break;
//フレームヘッダーかどうか調べます。
if (_ParseFrameHeader((PBYTE)&header,&fh)) break;
m_dwSkipOffset++;
}
//"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{
//フレームヘッダーではなく、ID3v2タグでもない場合は、
//読み飛ばし位置を1バイト進めてから再び読み直します。

m_dwSkipOffset++;
//1メガバイト以上検索してもフレームヘッダーが見つからなかった場合は、エラー終了します。
if (m_dwSkipOffset>0x100000) return FALSE;
}
}

//フレームヘッダーから得られた値を、それぞれメンバー変数に保存します。
m_iVersion =frameHeader.version; //MPEGバージョン番号
m_iLayer =frameHeader.layer; //レイヤー番号
m_iBitRate =frameHeader.bitRate; //ビットレート
m_sizeFrame =frameHeader.frameSize; //MP3ブロックサイズ

//MPEGLAYER3WAVEFORMAT構造体を作成します。
MPEGLAYER3WAVEFORMAT mp3wf={0};

mp3wf.wfx.wFormatTag =WAVE_FORMAT_MPEGLAYER3;
mp3wf.wfx.nChannels =frameHeader.channel;
mp3wf.wfx.nSamplesPerSec =frameHeader.sampleRate;
mp3wf.wfx.nAvgBytesPerSec =(frameHeader.bitRate*1000)/8;
mp3wf.wfx.nBlockAlign =1;
mp3wf.wfx.wBitsPerSample =0;
mp3wf.wfx.cbSize =MPEGLAYER3_WFX_EXTRA_BYTES;

mp3wf.wID =MPEGLAYER3_ID_MPEG;
mp3wf.fdwFlags =(frameHeader.padding)?MPEGLAYER3_FLAG_PADDING_ON:MPEGLAYER3_FLAG_PADDING_OFF;
mp3wf.nBlockSize =frameHeader.frameSize;
mp3wf.nFramesPerBlock =1;
mp3wf.nCodecDelay =1393;

//Xingヘッダーがあるか調べます。
m_uFlagBitRate=CBR;
DWORD offset=(frameHeader.version==1)?
((frameHeader.channel==1)?17:32):((frameHeader.channel==1)?9:17);
infile->Seek(m_dwSkipOffset+offset+4,CFile::begin);
if (infile->Read(header,4)!=4) return FALSE;

//Xingヘッダーがある場合
if ((::memcmp(header,"Xing",4)==0)||
(::memcmp(header,"Info",4)==0))
{
m_uFlagBitRate=VBR;
if (infile->Read(header,4)!=4) return FALSE;
DWORD dwflags=(((((header[0]<<8)|header[1])<<8)|header[2])<<8)|header[3];
if (dwflags&1){//フレーム数の取得
if (infile->Read(header,4)!=4) return FALSE;
m_nFrames=(((((header[0]<<8)|header[1])<<8)|header[2])<<8)|header[3];
}
if (dwflags&2){//データ総数
if (infile->Read(header,4)!=4) return FALSE;
m_dwStreamSize=(((((header[0]<<8)|header[1])<<8)|header[2])<<8)|header[3];
}
if (dwflags&4){//TOCエントリー
if (infile->Read(m_vbrTOC,100)!=100) return FALSE;
}
if (dwflags&8){//VBR品質((高)0〜100(低))
if (infile->Read(header,4)!=4) return FALSE;
m_vbrQuality=(((((header[0]<<8)|header[1])<<8)|header[2])<<8)|header[3];
}
}

//すべてのフレームを調べて、フレーム総数と平均ビットレートを算出します。
int nFrames=0;
//ビットレートの合計
double ttlBitRate=0.0;
//データ総数
DWORD dwStreamSize=0;
//VBRではない場合は、既に読み込んだ第一フレームの分も加算します。
if (m_uFlagBitRate!=VBR){
nFrames++;
dwStreamSize+=m_sizeFrame;
}
//検索するファイル位置を算出します。
offset=m_dwSkipOffset+m_sizeFrame;

//すべてのフレームについて調べます。
while(TRUE){
//次のフレームの先頭位置を探します。
BOOL bFind=FALSE;
while(TRUE){
//読み飛ばし位置に移動してから4バイト読みます。
infile->Seek(offset,CFile::begin);
if (infile->Read(header,4)!=4) break;
//フレームヘッダーかどうか調べます。
if (_ParseFrameHeader((PBYTE)&header,&frameHeader)){
bFind=TRUE;
break;
}
//見つからない場合は、ファイルポインタを1つ進めます。
offset++;
}
if (!bFind) break;
//第一フレームのビットレートと違えば、ABRにします。
if ((m_uFlagBitRate==CBR)&&(m_iBitRate!=frameHeader.bitRate))
m_uFlagBitRate=ABR;

ttlBitRate+=(double)frameHeader.bitRate;
offset+=frameHeader.frameSize;
dwStreamSize+=frameHeader.frameSize;
nFrames++;
}
//平均ビットレートを算出します。
m_iAveBitRate=(int)(ttlBitRate/(double)nFrames);
if ((m_uFlagBitRate!=VBR)||(!m_nFrames)) m_nFrames=nFrames;
if (!m_dwStreamSize) m_dwStreamSize=dwStreamSize;

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

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

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

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

//MP3ストリームサイズは、ファイル長さから読み飛ばし位置と、
//ID3v1タグがあれば、そのサイズを差し引きます。

DWORD dwFileStreamSize=fileSize-m_dwSkipOffset-m_byteId3v1.GetSize();
if (dwFileStreamSize<m_dwStreamSize) m_dwStreamSize=dwFileStreamSize;

//ACMドライバーを開きます。
MMRESULT mmr=::acmDriverOpen(&m_hDriver,m_hDriverIdDec,0);
if (mmr!=MMSYSERR_NOERROR) m_hDriver=NULL;

//MPEGLAYER3WAVEFORMAT構造体からWAVEFORMATEX構造体を取得します。
m_wfx.wFormatTag=WAVE_FORMAT_PCM;
m_wfx.cbSize=sizeof(WAVEFORMATEX);
mmr=::acmFormatSuggest(m_hDriver,&mp3wf.wfx,&m_wfx,sizeof(WAVEFORMATEX),ACM_FORMATSUGGESTF_WFORMATTAG);
if (mmr!=MMSYSERR_NOERROR) return FALSE;

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

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

//MP3ストリームサイズからデコード後の総データサイズを取得します。
::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; //転送先バッファサイズ
//転送先バッファサイズは4608バイト以上になるようにします。(libmpg123対応)
if (dwDstSize<4608) dwDstSize=4608*2;

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

//第一フレームをドライバーに読み込ませます。
size=infile->Read(m_AcmStreamHeader.pbSrc,m_AcmStreamHeader.cbSrcLength);
if (!size) return FALSE;
//実際に読み込んだサイズを有効なサイズとします。
m_AcmStreamHeader.cbSrcLengthUsed=size;
//Mp3データから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);

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

//MPEGLAYER3WAVEFORMAT構造体を保存しておきます。
SetMpegLayer3WaveFormat(0,&mp3wf);

return TRUE;
}


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

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

//ACMストリームハンドルが無効な場合は、CDDAFile::StreamIn関数を呼び出します。
if (!IsMP3File()) return CDDAFile::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);

//フレームヘッダーを探します。
BYTE header[4];
FRAMEHEADER frameHeader;
while(TRUE){
//読み飛ばし位置に移動してから4バイト読みます。
m_infile->Seek(dwPosRead,CFile::begin);
if (m_infile->Read(header,4)!=4) return FALSE;
//フレームヘッダーかどうか調べます。
if (_ParseFrameHeader((PBYTE)&header,&frameHeader)) break;
//フレームヘッダーではない場合は、
//読み飛ばし位置を1バイト進めてから再び読み直します。

dwPosRead++;
}
//探し出したフレームヘッダーの先頭位置に移動します。
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;
//現在のファイル位置を取得します。
DWORD dwPos=(DWORD)m_infile->GetPosition();
//ID3v1タグを読み込んだ場合はその分を差し引きます。
if (dwPos>dwFileEnd){
DWORD dwTaiDust=dwPos-dwFileEnd;
if (m_AcmStreamHeader.cbSrcLengthUsed<dwTaiDust) break;
else m_AcmStreamHeader.cbSrcLengthUsed-=dwTaiDust;
fdwConvert|=ACM_STREAMCONVERTF_END;
}
//MP3データからWave音源データに変換します。
MMRESULT mmr=::acmStreamConvert(m_hAcmStream,&m_AcmStreamHeader,fdwConvert);

fdwConvert=ACM_STREAMCONVERTF_BLOCKALIGN;

//lameACM対応
#define MP3_NEED_MORE 0x200

if (mmr==MP3_NEED_MORE) continue;
//デコードエラーの場合は、エラー終了します。
else 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;
}


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

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

続きを読む
posted by ひろし at 00:57| Comment(0) | MP3ファイルフォーマット | このブログの読者になる | 更新情報をチェックする

2011年07月22日

libid3tagのインストール



MP3ファイルのid3タグを読み込むライブラリーですが、現在ウェブ上で入手できるものに、以下の二つのフリーライブラリーがあるようです。

  1. 「id3lib」.....id3v2.3.0対応、UNICODEはUTF16のみ対応。
  2. 「libid3tag」.id3v2.4.0対応、UNICODEはUTF16/UTF16BE/UTF8に対応。


上記二つのライブラリーを比較検討した結果、今回はid3v2.4.0にも対応した「libid3tag」を選びました。

続きを読む
posted by ひろし at 14:27| Comment(0) | MP3ファイルフォーマット | このブログの読者になる | 更新情報をチェックする

2011年08月03日

ID3タグの読み込み 第1部



今回は、MP3ファイルID3タグを読み込むために、libid3tag-0.15.1bというフリーライブラリを使用してみました。尚libid3tag-0.15.1bの扱いについては、前回のlibid3tagのインストールをご参照ください。


続きを読む
posted by ひろし at 13:05| Comment(0) | MP3ファイルフォーマット | このブログの読者になる | 更新情報をチェックする

ID3タグの読み込み 第2部



ID3タグを含んだ音楽ファイルからID3Fileクラスを使って、ID3タグを読み込む手順は以下の通りです。

  1. ID3FileクラスのインスタンスをID3タグを含んだ音楽ファイルのファイル名を指定して作成するか、或いはファイル内の音楽データを演奏しながら同時にID3タグを読み込む場合は、ファイル名を指定せずに作成します。
    libid3tag-0.15.1bにおいて、ファイル内の音楽データを演奏しながら、同時にファイル内のID3タグを読むというようなことは出来ないようです。)

  2. ファイル名を指定せずにID3Fileクラスのインスタンスを作成した場合は、ファイルから直接読み込んだID3タグの生データを、CID3File::Parse関数に入力してID3タグの内容を解析します。

  3. CID3File::Parse関数の戻り値や、CID3File::GetFrameCount関数で取得したフレーム数を元に、CID3File::GetFrame関数を使ってフレームの内容を順に読み出し、リストビュー等のコントロールを用いて結果を表示します。




続きを読む
posted by ひろし at 18:06| Comment(0) | MP3ファイルフォーマット | このブログの読者になる | 更新情報をチェックする

2011年08月06日

ID3タグの読み込み 第3部



「ID3タグの読み込み 第3部」では、MFCを使った用例とサンプルプログラムを紹介します。


続きを読む
posted by ひろし at 12:37| Comment(0) | MP3ファイルフォーマット | このブログの読者になる | 更新情報をチェックする

2011年08月27日

ID3タグの書き込み 第1部



今回は、ID3タグ編集用ライブラリ「libid3tag-0.15.1b」を使用して、ID3タグMP3ファイルに書き込む方法を解説します。尚ID3タグ編集用ライブラリ「libid3tag-0.15.1b」のインストールについては、libid3tagのインストールをご参照ください。


続きを読む
posted by ひろし at 03:31| Comment(0) | MP3ファイルフォーマット | このブログの読者になる | 更新情報をチェックする

2011年09月12日

ID3タグの書き込み 第2部



CIDFileクラスを使って、ID3タグへの編集/書き込みする手順は以下の通りです。

新たにフレームを作成して、ID3タグに追加/挿入する場合。
  1. ID3タグが存在しない場合は、CID3File::TagNew関数を呼び出して、ID3タグを新たに作成します。
  2. 追加するフレームIDを引数に入れてCID3File::InsertFrameNew関数を呼び出し、新規フレームの作成とID3タグにフレームへの追加/挿入をします。
  3. メンバー変数のCStringクラス文字列に必要なデータを設定した後、CID3File::SetFrame関数を呼び出してID3タグの内容を変更します。
  4. CID3File::Update関数を呼び出して、ID3タグの内容をメモリ或いはファイルに書き込みます。
ID3タグの読み込み 第2部」の手順に従って読み込んだ、ID3タグの内容を修正する場合。
  1. 修正するフレームの内容をCID3File::GetFrame関数で、メンバー変数のCStringクラス文字列に読み出します。
  2. メンバー変数のCStringAクラス文字列を編集後、CID3File::SetFrame関数を呼び出してID3タグの内容を変更します。
  3. CID3File::Update関数を呼び出して、ID3タグの内容をメモリ或いはファイルに書き込みます。
ID3タグの読み込み 第2部」の手順に従って読み込んだ、ID3タグのフレームを削除する場合。
  1. 削除するフレーム番号を引数に入れてCID3File::DeleteFrame関数を呼び出し、フレーム番号のフレームを削除します。
  2. CID3File::Update関数を呼び出して、ID3タグの内容をメモリ或いはファイルに書き込みます。


続きを読む
posted by ひろし at 18:15| Comment(0) | MP3ファイルフォーマット | このブログの読者になる | 更新情報をチェックする

2011年09月18日

ID3タグの書き込み 第3部



「ID3タグの書き込み 第3部」では、MFCを使った用例とサンプルプログラムを紹介します。

続きを読む
posted by ひろし at 18:58| Comment(0) | MP3ファイルフォーマット | このブログの読者になる | 更新情報をチェックする

2011年11月02日

LAME ACMのインストール



ウィンドウズXPで標準で入っているMP3エンコード/デコードのためのACMコーデックは、Fraunhofer Institut Integrierte Schaltungen IIS社Fraunhofer IIS MPEG Layer-3 Codecですが、エンコードする場合のビットレートが最高でも56kbps、サンプルレートが24000Hzと低品質で音楽メディアとして、ハイレベルな鑑賞に堪え得るものではありません。
そこで今回はlame-3.98.4.tar.gzをネットからダウンロードして、ACMドライバを作成するまでの過程を解説します。

続きを読む
posted by ひろし at 11:57| Comment(0) | MP3ファイルフォーマット | このブログの読者になる | 更新情報をチェックする

2011年11月18日

MP3ファイルの書き込み 第1部



ACM(Audio Compression Manager)を介してWAVE音源データMP3ストリームにエンコードしてMP3ファイルに書き込む手順は、以下の通りです。

  1. MP3エンコードに必要なLAME ACMをインストールします。
  2. プログラムから、目的のACMドライバーを探し出します。
  3. ACMドライバーを開いて使用可能なエンコードフォーマットの中から、目的の仕様を満たすMPEGLAYER3WAVEFORMAT構造体を一つ選択します。
  4. エンコードに特別な仕様を要求しない場合は、システムが推奨するACMドライバーMPEGLAYER3WAVEFORMAT構造体を取得します。
  5. WAVE音源データ読み込み用のバッファサイズから、MP3ストリームを一時格納するための作業用バッファサイズを取得します。
  6. 作業用バッファを確保して、ACMストリームヘッダーを作成します。
  7. 読み込んだWAVE音源データMP3ストリームにエンコードして、MP3ファイルに書き込みます。
  8. WAVE音源データの読み込みが終了したら、ACMストリームヘッダーを削除してから、ACMストリームをクローズします。
  9. 独自にACMドライバーを選択してオープンしている場合は、ACMドライバーをクローズします。



続きを読む
posted by ひろし at 13:39| Comment(0) | MP3ファイルフォーマット | このブログの読者になる | 更新情報をチェックする

2012年02月19日

id3libのインストール



MP3ファイルフォーマットカテゴリーではlibid3tagライブラリを使用してID3タグの読み書きを解説してきた訳ですが、「AACファイルフォーマットの読み込み」を書く段になって、このlibid3tagライブラリの限界が目立ってきたので、思い切って今一度id3libライブラリについても解説していきたいと思います。

今になって見えてきたlibid3tagライブラリの限界

  1. ID3v1タグの読み書きにおいては、ISO8859-1規格に厳密に従うので、シフトJISでの書き込みや読み込みができません。
  2. 書き込んだID3v2タグのバージョンはすべてID3v2.4タグになってしまう。つまりID3v2.2タグID3v2.3タグの書き込みが不可能なため、せっかく書き込んだ内容も、ウィンドウズのエクスプローラーや、ウィンドウズメディアプレーヤーSONYのMP3プレーヤーからはまったく読み込めません。
  3. バージョンID3v2.4で画像を書き込んだファイルを、ウィンドウズメディアプレーヤーで再生すると動作が不安定になり、場合によってはデッドロックを起こす場合があります。

以上の点からもバージョンID3v2.4以外は、id3libライブラリを使用したほうが無難のようです。

続きを読む
posted by ひろし at 21:10| Comment(0) | MP3ファイルフォーマット | このブログの読者になる | 更新情報をチェックする

2012年03月11日

ID3タグの読み込み(id3lib) 第1部



今回は、MP3ファイルID3タグを読み込むために、id3lib-3.8.3というフリーライブラリを使用してみました。尚id3lib-3.8.3の扱いについては、前回のid3libのインストールをご参照ください。


続きを読む
posted by ひろし at 03:16| Comment(0) | MP3ファイルフォーマット | このブログの読者になる | 更新情報をチェックする

ID3タグの読み込み(id3lib) 第2部



ID3タグを含んだ音楽ファイルからCID3Tagクラスを使って、ID3タグを読み込む手順は以下の通りです。

  1. CID3TagクラスのインスタンスをID3タグを含んだ音楽ファイルのファイル名を指定して作成するか、或いはファイル内のID3タグをバージョン毎に読み込む場合は、ファイル名を指定せずに作成します。

  2. ファイル名を指定せずにCID3Tagクラスのインスタンスを作成した場合は、ファイルから直接読み込んだID3タグの生データを、CID3Tag::Parse関数に入力してID3タグの内容を解析します。

  3. CID3Tag::Parse関数の戻り値や、CID3Tag::GetFrameCount関数で取得したフレーム数を元に、CID3Tag::GetFrame関数を使ってフレームの内容を順に読み出し、リストビュー等のコントロールを用いて結果を表示します。




続きを読む
posted by ひろし at 04:03| Comment(0) | MP3ファイルフォーマット | このブログの読者になる | 更新情報をチェックする

2012年04月09日

ID3タグの読み込み(id3lib) 第3部



「ID3タグの読み込み(id3lib) 第3部」では、MFCを使った用例とサンプルプログラムを紹介します。


続きを読む
posted by ひろし at 14:46| Comment(0) | MP3ファイルフォーマット | このブログの読者になる | 更新情報をチェックする

2012年04月19日

ID3タグの書き込み(id3lib) 第1部



ID3タグ編集用ライブラリ「id3lib-3.8.3」を使用して、ID3タグMP3ファイルに書き込む方法を解説します。尚ID3タグ編集用ライブラリ「id3lib-3.8.3」のインストールについては、id3libのインストールをご参照ください。


続きを読む
posted by ひろし at 23:10| Comment(0) | MP3ファイルフォーマット | このブログの読者になる | 更新情報をチェックする

2012年04月24日

ID3タグの書き込み(id3lib) 第2部



「ID3タグの書き込み(id3lib) 第2部」では、MFCを使った用例とサンプルプログラムを紹介します。

続きを読む
posted by ひろし at 21:43| Comment(0) | MP3ファイルフォーマット | このブログの読者になる | 更新情報をチェックする
×

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