2012年11月22日

WAVEファイルに曲名、歌手名などの曲情報を書き込む



CDからリッピングした場合、MP3ファイルなどにエンコードせず、WAVEファイルで生の音を楽しみたいものです。一般のWAVEファイルには、メタデータは含まれていないようですが、CDリッピング時に得られたCDテキストFreeDBから得られた曲情報もWAVEファイルに書き込めば、より便利になります。
そこで、RIFF INFOチャンクを使ってWAVEファイルに曲名、歌手名などの曲情報の書き込むプログラムを作成してみました。




INFOチャンク読み込み/書き込みのためのインライン関数です。

//INFOチャンク文字列の取得
inline LPCTSTR CWaveFile::GetChunkTextByIndex(UINT index)
{
if (index>=GetChunkCount()) return FALSE;
return m_strChunkText[index].GetBuffer();
}

//INFOチャンク文字列の設定
inline void CWaveFile::SetChunkTextByIndex(UINT index,LPCTSTR lpszText)
{
if (index<GetChunkCount()) m_strChunkText[index]=lpszText;
}

//INFOチャンク文字列の削除
inline void CWaveFile::DeleteChunkByIndex(UINT index)
{
if (index<GetChunkCount()) m_strChunkText[index]=_T("");
}

//すべてのINFOチャンク文字列の削除
inline void CWaveFile::DeleteAllINFOChunk()
{
for(UINT i=0;i<GetChunkCount();i++) m_strChunkText[i]=_T("");
}


INFOチャンクタグ名及びタグ詳細記述の文字列定義です。

//INFOチャンク タグ名
static const char *const g_tagName[]={
"ISRC","ICRD","IPRD","INAM","IGNR","ICMT","IART","ICMS",
"ICOP","IENG","ISFT","IKEY","ITCH","ISBJ","ITRK"
};

//INFOチャンク タグの詳細記述
static const TCHAR *const g_tagDesc[]={
_T("メディア元"),_T("作成日"),_T("アルバム名"),_T("曲名"),
_T("ジャンル"),_T("コメント"),_T("アーティスト"),_T("著作権代理人"),
_T("著作権情報"),_T("録音エンジニア"),_T("ソフトウェア"),
_T("キーワード"),_T("エンコード技術者"),_T("タイトル"),_T("トラック番号")
};


INFOチャンク読み込み/書き込みのためのメンバー関数です。

//タグ文字列の取得
LPCSTR CWaveFile::GetChunkTagByIndex(UINT index)
{
if (index>=GetChunkCount()) return FALSE;
return g_tagName[index];
}

//INFOチャンク文字列の検索
LPCTSTR CWaveFile::FindChunkTextByTag(LPCSTR lpszTag)
{
for (UINT i=0;i<GetChunkCount();i++)
if (!::memcmp(g_tagName[i],lpszTag,4))
return m_strChunkText[i];
return FALSE;
}

//INFOチャンク文字列の設定
void CWaveFile::SetChunkTextByTag(LPCSTR lpszTag,LPCTSTR lpszText)
{
for (UINT i=0;i<GetChunkCount();i++)
if (!::memcmp(g_tagName[i],lpszTag,4)){
m_strChunkText[i]=lpszText;
break;
}
}

//タグ記述の取得
LPCTSTR CWaveFile::ChunkDescription(LPCSTR lpszTag)
{
for (UINT i=0;i<GetChunkCount();i++)
if (!::memcmp(g_tagName[i],lpszTag,4))
return g_tagDesc[i];
return FALSE;
}

//INFOチャンク文字列の削除
void CWaveFile::DeleteChunkByTag(LPCSTR lpszTag)
{
for (UINT i=0;i<GetChunkCount();i++)
if (!::memcmp(g_tagName[i],lpszTag,4)){
m_strChunkText[i]=_T("");
break;
}
}


WAVEファイルを開く際に、CWaveFile::ReadHeader関数から呼び出されて、INFOチャンク内のメタデータを読み込む関数です。

//INFOチャンク内のメタデータを読み込みます。
BOOL CWaveFile::ReadListINFO(HMMIO hmmio)
{
//すべてのINFOチャンク文字列を削除します。
DeleteAllINFOChunk();

//現在のファイルポインタの位置を保存します。
LONG mmioPos=::mmioSeek(hmmio,0,SEEK_CUR);

//LISTチャンクに進入して、INFOチャンクを探します。
MMCKINFO mmckInfo;
mmckInfo.fccType=::mmioStringToFOURCC("INFO", 0);
MMRESULT mmr=::mmioDescend(hmmio,&mmckInfo,NULL,MMIO_FINDLIST);
//INFOチャンクがなければ、エラー終了します。
if (mmr!=MMSYSERR_NOERROR){
//元のファイルポインタの位置に戻します。
::mmioSeek(hmmio,mmioPos,SEEK_SET);
return FALSE;
}

BOOL bResult=FALSE;
for(UINT i=0;i<GetChunkCount();i++){
//現在のファイルポインタの位置を保存します。
LONG mmioPos=::mmioSeek(hmmio,0,SEEK_CUR);

MMCKINFO mmckMeta;
//INFOチャンクタグ名定数の先頭のタグから順に、メタチャンクを探します。
mmckMeta.ckid=::mmioStringToFOURCC(g_tagName[i],0);
MMRESULT mmr=::mmioDescend(hmmio,&mmckMeta,NULL,MMIO_FINDCHUNK);
//メタチャンクがあった場合。
if (mmr==MMSYSERR_NOERROR) {
bResult=TRUE;
CStringA strA;
//メタデータを読み込みます。
LPTSTR lpsz=strA.GetBufferSetLength(mmckMeta.cksize);
::ZeroMemory(lpsz,mmckMeta.cksize);
LONG size=::mmioRead(hmmio,(HPSTR)lpsz,mmckMeta.cksize);
if (size!=mmckMeta.cksize) return FALSE;
//メタチャンクから退出します。
mmr=::mmioAscend(hmmio,&mmckMeta,0);
if (mmr!=MMSYSERR_NOERROR) return FALSE;
m_strChunkText[i]=strA;
}
else m_strChunkText[i].Empty();

//元のファイルポインタの位置に戻します。
::mmioSeek(hmmio,mmioPos,SEEK_SET);
}
//INFOチャンクから退出します。
mmr=::mmioAscend(hmmio,&mmckInfo,0);
if (mmr!=MMSYSERR_NOERROR) return FALSE;

//元のファイルポインタの位置に戻します。
::mmioSeek(hmmio,mmioPos,SEEK_SET);
return bResult;
}


WAVEファイルを保存する際に、CWaveFile::StreamOut関数から呼び出された後、INFOチャンクを新たに作成し、メタデータを書き込む関数です。

//INFOチャンクを新たに作成して、メタデータを書き込みます。
BOOL CWaveFile::WriteListINFO(HMMIO hmmio)
{
//INFOチャンク内に書き込む文字列があるかどうか?
BOOL bExist=FALSE;
for(UINT i=0;i<GetChunkCount();i++){
if (!m_strChunkText[i].IsEmpty()){
bExist=TRUE;
break;
}
}
if (bExist){
//INFOチャンクを作成して、その中に進入します。
MMCKINFO mmckInfo;
mmckInfo.fccType=::mmioStringToFOURCC("INFO", 0);
MMRESULT mmr=::mmioCreateChunk(hmmio,&mmckInfo,MMIO_CREATELIST);
//INFOチャンクの作成に失敗したら、エラー終了します。
if (mmr!=MMSYSERR_NOERROR) return FALSE;

for(UINT i=0;i<GetChunkCount();i++){
if (!m_strChunkText[i].IsEmpty()){
//メタチャンクを作成して、その中に進入します。
MMCKINFO mmckMeta;
mmckMeta.ckid=::mmioStringToFOURCC(g_tagName[i],0);
mmr=::mmioCreateChunk(hmmio,&mmckMeta,0);
//メタチャンクの作成に失敗したら、エラー終了します。
if (mmr!=MMSYSERR_NOERROR) return FALSE;

//メタデータを書き込みます。
CStringA strA=m_strChunkText[i];
strA.AppendChar('\0');
int sizeToWrite=strA.GetLength();
LONG size=::mmioWrite(hmmio,strA.GetBuffer(),sizeToWrite);
//書き込みに失敗したら、エラー終了します。
if (size<sizeToWrite) return FALSE;

//メタチャンクから退出します。
mmr=::mmioAscend(hmmio,&mmckMeta,0);
if (mmr!=MMSYSERR_NOERROR) return FALSE;
}
}
//INFOチャンクから退出します。
mmr=::mmioAscend(hmmio,&mmckInfo,0);
if (mmr!=MMSYSERR_NOERROR) return FALSE;
}
return TRUE;
}


WaveOut.h
WaveOut.cpp
WaveFile.h
WaveFile.cpp




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

メールアドレス:

ホームページアドレス:

コメント:

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


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

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