2012年03月11日

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



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




id3lib-3.8.3をインクルードするための定義です。

#include <id3.h>
#include <id3\tag.h>


上記ヘッダーには含まれてはいませんが、ID3_FrameID取得のために必要となるプロトタイプの定義です。

ID3_FrameID ID3_FindFrameID(const char *id); //field_impl.hを参照してください。


ジャンル名文字列の取得/検索のためのstdcall関数です。

//ジャンル名文字列の取得
const char* __stdcall g_GenreName(UINT index)
{
if (index>=ID3_NR_OF_V1_GENRES) return FALSE;
return ID3_v1_genre_description[index];
}

//ジャンル名文字列の検索
//見つからなかった場合の戻り値:-1
int __stdcall g_FindGenreName(const char* pGenreName)
{
for(int i=0;i<ID3_NR_OF_V1_GENRES;i++)
if (!strcmpi(pGenreName,ID3_v1_genre_description[i]))
return i;
return -1;
}


画像名文字列の定義及び取得/検索のためのstdcall関数です。
ID3タグに固有の文字列定義です。

//画像名文字列
static const TCHAR *const pictureName[]={
_T("Other"), _T("32x32 pixels 'file icon' (PNG only)"), _T("Other file icon"),
_T("Cover (front)"), _T("Cover (back)"), _T("Leaflet page"), _T("Media (e.g. lable side of CD)"),
_T("Lead artist/lead performer/soloist"), _T("Artist/performer"), _T("Conductor"),
_T("Band/Orchestra"), _T("Composer"), _T("Lyricist/text writer"), _T("Recording Location"),
_T("During recording"), _T("During performance"), _T("Movie/video screen capture"),
_T("A bright coloured fish"), _T("Illustration"), _T("Band/artist logotype"),
_T("Publisher/Studio logotype")
};

//画像名の数(21)
#define PICTURE_NAME_COUNT ((int)(sizeof(pictureName)/sizeof(const char *const)))
//画像名文字列の取得
LPCTSTR __stdcall g_PictureName(UINT index)
{
if (index>=PICTURE_NAME_COUNT) return FALSE;
return pictureName[index];
}

//画像名文字列の検索
//見つからなかった場合の戻り値:-1

int __stdcall g_FindPictureName(LPCTSTR lpszPictureName)
{
for(int i=0;i<PICTURE_NAME_COUNT;i++)
if (!lstrcmpi(lpszPictureName,pictureName[i]))
return i;
return -1;
}


テキストエンコード文字列の定義及び取得/検索のためのstdcall関数です。

//テキストエンコード文字列
static const TCHAR *const fieldTextEncoding[]={
_T("ISO_8859_1"), //ID3TE_ISO8859_1 =0
_T("UTF_16"), //ID3TE_UTF16 =1
_T("UTF_16BE"), //ID3TE_UTF16BE =2
_T("UTF_8"), //ID3TE_UTF8 =3
_T("NUMENCODINGS") //ID3TE_NUMENCODINGS=4
};

//テキストエンコード文字列の取得
LPCTSTR __stdcall g_TextEncoding(UINT index)
{
if (index>3) return FALSE;
return fieldTextEncoding[index];
}

//テキストエンコード文字列の検索
//見つからなかった場合の戻り値:-1

int __stdcall g_FindTextEncoding(LPCTSTR lpszTextEncoding)
{
for(int i=0;i<4;i++)
if (!lstrcmpi(lpszTextEncoding,fieldTextEncoding[i]))
return i;
return -1;
}


タイムスタンプフォーマット文字列の定義及び取得/検索のためのstdcall関数です。

//タイムスタンプフォーマット文字列
static const TCHAR *const timeStampFormat[]={
_T("MPEG frames as unit"), //0x00
_T("milliseconds as unit") //0x01
};

//タイムスタンプフォーマット文字列の取得
//index=0:MPEG frame as unit, 1:milliSecond as unit

LPCTSTR __stdcall g_TimeStampFormat(UINT index)
{
if (index>1) return FALSE;
return timeStampFormat[index];
}

//タイムスタンプフォーマット文字列の検索
//戻り値:0=MPEG frame as unit, 1=milliSecond as unit
// :-1=見つからなかった。

int __stdcall g_FindTimeStampFormat(LPCTSTR lpszTimeStampFormat)
{
for (int i=0;i<2;i++)
if (!lstrcmpi(lpszTimeStampFormat,timeStampFormat[i]))
return i;
return -1;
}


内容タイプ文字列の定義及び取得/検索のためのstdcall関数です。

//内容タイプ文字列
static const TCHAR *const contentType[]={
_T("other"), //0x00
_T("lyrics"), //0x01
_T("text transcription"), //0x02
_T("movement/part name"), //0x03
_T("events"), //0x04
_T("chord"), //0x05
_T("trivia information"), //0x06
_T("URLs to webpages"), //0x07
_T("URLs to images") //0x08
};

//内容タイプ文字列の取得
LPCTSTR __stdcall g_ContentType(UINT index)
{
if (index>8) return FALSE;
return contentType[index];
}

//内容タイプ文字列の検索
//見つからなかった場合の戻り値:-1

int __stdcall g_FindContentType(LPCTSTR lpszContentType)
{
for (int i=0;i<9;i++)
if (!lstrcmpi(lpszContentType,contentType[i]))
return i;
return -1;
}


バージョン名文字列の定義及び取得/検索のためのstdcall関数です。

//バージョン名
static const TCHAR *const tagVersion[]={
_T("ID3v1.0"),
_T("ID3v1.1"),
_T("ID3v2.2.0"),
_T("ID3v2.2.1"),
_T("ID3v2.3.0"),
_T("ID3v2.4.0")
};

//バージョン名の取得
LPCTSTR __stdcall g_TagVersion(UINT index)
{
if (index>6) return FALSE;
return tagVersion[index];
}

//バージョン名の検索
//見つからなかった場合の戻り値:-1

int __stdcall g_FindTagVersion(LPCTSTR lpszTagVersion)
{
for(int i=0;i<6;i++)
if (!lstrcmpi(lpszTagVersion,tagVersion[i]))
return i;
return -1;
}


画像データの種類を取得するstdcall関数です。

//画像データの種類を取得します。
BOOL __stdcall g_GetFileType(CString& strFileType,PBYTE pHeader)
{
if (!AfxIsValidAddress(pHeader,16,FALSE)) return FALSE;
//PNG画像データの場合。
if (::memcmp(pHeader,"\x89\x50\x4E\x47\x0D\x0A\x1A\x0A",8)==0)
strFileType=_T("image/png");
//JPEG画像データの場合。
else if (((::memcmp(pHeader,"\xFF\xD8\xFF\xE0",4)==0)&&
(::memcmp(pHeader+6,"JFIF\0",5)==0))||
((::memcmp(pHeader,"\xFF\xD8\xFF\xE1",4)==0)&&
(::memcmp(pHeader+6,"Exif\0\0",6)==0)))

strFileType=_T("image/jpeg");
//GIF画像データの場合。
else if ((::memcmp(pHeader,"GIF87a",6)==0)||
(::memcmp(pHeader,"GIF89a",6)==0))
strFileType=_T("image/gif");
else return FALSE;

return TRUE;
}


CStringクラス内のDumpテキストから、バイナリーデータを取得する__stdcall関数です。

//Dumpテキストからバイナリーデータを取得します。
//buffer==NULL時の戻り値:バイナリーデータを格納するために必要なバイト数。

int __stdcall g_DumpTextToBinary(PBYTE buffer,CString& str)
{
int length=(str.GetLength()+1)/3;
if (length<=0) return FALSE;

if (!buffer) return length;

AfxIsValidAddress(buffer,length,TRUE);
if (!AfxIsValidAddress(buffer,length,TRUE)) return FALSE;

PBYTE dst=buffer;

TCHAR tszHex[]=_T("0x00");
LPTSTR pch=(LPTSTR)str.GetBuffer();
for (int i=0;i<length;i++){
tszHex[2]=*pch++;
tszHex[3]=*pch++;
pch++;
DWORD bin=0;
_stscanf(tszHex,_T("%X"),&bin);
*dst++=(BYTE)bin;
}
return length;
}

入力されたバイナリーデータから、CStringクラスにdumpテキストを取得する__stdcall関数です。

//バイナリーデータからCStringクラスにdumpテキストを取得します。
CString __stdcall g_BinaryToDumpText(PBYTE binary,UINT length)
{
ASSERT(AfxIsValidAddress(binary,length,FALSE));
if (!AfxIsValidAddress(binary,length,FALSE)) return FALSE;

CString str;
for(UINT i=0;i<length;i++){
if (i>0) str.AppendChar(' ');
str.AppendFormat(_T("%02X "),binary[i]);
}
str.ReleaseBuffer();
return str;
}



タグフィールド内の数値や、文字列を取得するstdcall関数です。

//画像名文字列の取得
BOOL __stdcall _GetPictureName(const ID3_Frame* frame,CString& str)
{
ASSERT(frame);
ASSERT(frame->GetID()==ID3FID_PICTURE);
ID3_Field* field=frame->GetField(ID3FN_PICTURETYPE);
ASSERT(field);
if (!field) return FALSE;
LPCTSTR lpszPictureName=g_PictureName(field->Get());
if (!lpszPictureName) return FALSE;
str=lpszPictureName;
return TRUE;
}

//テキストエンコード文字列の取得
BOOL __stdcall _GetTextEncoding(const ID3_Frame* frame,ID3_FieldID fieldID,CString& str)
{
ASSERT(frame);
ID3_Field* field=frame->GetField(fieldID);
ASSERT(field);
if (!field) return FALSE;
ID3_TextEnc textEnc=field->GetEncoding();
switch(textEnc){
case ID3TE_ISO8859_1:
case ID3TE_UTF16:
case ID3TE_UTF16BE:
case ID3TE_UTF8:
case ID3TE_NUMENCODINGS:
str=g_TextEncoding(textEnc);
break;
case ID3TE_NONE:
default:
return FALSE;
}
return TRUE;
}

//タイムスタンプフォーマット文字列の取得
BOOL __stdcall _GetTimeStampFormat(const ID3_Frame* frame,CString& str)
{
ASSERT(frame);
ID3_Field* field=frame->GetField(ID3FN_TIMESTAMPFORMAT);
ASSERT(field);
if (!field) return FALSE;

uint32 int32=field->Get();

LPCTSTR lpszTimeStampFormat=g_TimeStampFormat(int32-1);
if (!lpszTimeStampFormat) return FALSE;
str=lpszTimeStampFormat;

return TRUE;
}

//内容タイプ文字列の取得
BOOL __stdcall _GetContentType(const ID3_Frame* frame,CString& str)
{
ASSERT(frame);
ID3_Field* field=frame->GetField(ID3FN_CONTENTTYPE);
ASSERT(field);
if (!field) return FALSE;

uint32 int32=field->Get();

LPCTSTR lpszContentType=g_ContentType(int32);
if (!lpszContentType) return FALSE;
str=lpszContentType;

return TRUE;
}

//Binaryデータの取得
BOOL __stdcall _GetBinaryData(const ID3_Frame* frame,CString& str)
{
ASSERT(frame);
ID3_Field* field=frame->GetField(ID3FN_DATA);
ASSERT(field);
if (!field) return FALSE;

UINT length=(UINT)field->Size();
BYTE* bin=(BYTE*)field->GetRawBinary();
if ((!length)||(!bin)) return FALSE;
void* buffer=(void*)str.GetBufferSetLength(length/sizeof(TCHAR));
::CopyMemory(buffer,bin,length);
return TRUE;
}

//StringList文字列の取得
BOOL __stdcall _GetStringList(const ID3_Frame* frame,ID3_FieldID fieldID,CString& str,size_t index)
{
ASSERT(frame);
ID3_Field* field=frame->GetField(fieldID);
ASSERT(field);
if (!field) return FALSE;

CStringA strA;
ID3_TextEnc textEnc=field->GetEncoding();
if (textEnc==ID3TE_ISO8859_1){
//テキストエンコードがISO8859-1の場合。
size_t size=field->Size();
LPSTR lpsz=strA.GetBufferSetLength((int)size+1);
field->Get(lpsz,size+1,index);
}
else{
field->SetEncoding(ID3TE_ASCII);
size_t size=field->Size();
LPSTR lpsz=strA.GetBufferSetLength((int)size+1);
field->Get(lpsz,size+1,index);
field->SetEncoding(textEnc);
}
str=strA;
return TRUE;
}

//String文字列の取得
BOOL __stdcall _GetString(const ID3_Frame* frame,ID3_FieldID fieldID,CString& str)
{
return _GetStringList(frame,fieldID,str,0);
}



id3libライブラリーを使いやすくするため、CID3Tagクラスを独自に定義しました。

class ID3_Tag;
class ID3_Frame;

//CID3Tagクラス
class CID3Tag : public CObject
{
public:
//コンストラクタ
CID3Tag(const char* lpszFileName=NULL);
//デストラクタ
virtual ~CID3Tag();
//メンバー変数の初期化
void Init();
//メンバー変数の削除
void Delete();

//新規ID3タグの作成
virtual BOOL TagNew(LPCTSTR lpszVersion=NULL);
//id3タグの生データから、その内容を解析します。
virtual unsigned int Parse(BYTE* data,DWORD memSize);
//読み込んだタグのフレーム数を取得します。
virtual unsigned int GetFrameCount();
//指定FrameIDと同じフレームを探して、フレーム番号を返します。(エラー時:-1)
virtual int FindFrame(const char* FrameID);

//指定位置のフレームの内容を、メンバー変数文字列に書き出します。
virtual BOOL GetFrame(UINT indexFrame);

//メンバー変数文字列を、指定位置のフレームに書き込みます。
virtual BOOL SetFrame(UINT indexFrame);
//id3タグの内容をファイルまたは指定CByteArrayクラスに更新します。
virtual BOOL Update(CByteArray* pByteArray);
//画像をファイルから読み込みます。
virtual BOOL LoadImage(LPCTSTR lpszPathName);
//新規フレームの挿入
// indexFrame<0:末尾に追加します。

virtual BOOL InsertFrameNew(int indexFrame,const char* FrameID);
private:
//指定フレーム番号のフレームを取得します。(エラー時:-1)
ID3_Frame* _FindFrame(UINT indexFrame);
//指定フレームの挿入
// indexFrame<0:末尾に追加します。

BOOL _InsertFrame(int indexFrame,ID3_Frame *frame);
//現在のID3タグから指定番号のフレームを抜き出します。
ID3_Frame* _DetachFrame(UINT indexFrame);
public:
//フレームの削除
virtual BOOL DeleteFrame(UINT indexFrame);

//フレーム記述の取得(ASCII)
virtual const char* FrameDescriptionA(const char* frameID);
//フレーム記述の取得(CString)
virtual BOOL FrameDescription(CString& strDescription,const char* frameID);
//ID3v2.2タグ名からID3v2.3/4タグ名の取得
const char* ShortToLongName(const char* frameID);
private:
//現在のID3タグのバージョンの取得
enum ID3_V2Spec _TagVersion();
public:
//現在のID3タグのバージョンの取得
virtual BOOL TagVersion(CString& strVersion);
//現在のID3タグのバージョンを変更します。
BOOL SetTagVersion(LPCTSTR lpszVersion);

private:
ID3_Tag* m_pID3Tag; //読み込んだID3_Tagクラスへのポインタ
public:
//フレーム毎のデータ
CString m_strTitle; //フレーム名
CString m_strID; //フレームID
CString m_strBinary; //バイナリーデータ
CString m_strDesc; //フレーム見出し
CString m_strText; //フレームテキスト
CString m_strImmediate; //テキストの言語/属性
CString m_strTextEnc; //テキストのエンコード
CString m_strTimeStamp; //タイムスタンプフォーマット
CString m_strPictureName; //画像名
};


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

//コンストラクタ
CID3Tag::CID3Tag(const char* lpszFileName)
{
Init();
//ファイル名があれば、ファイルからタグを読みます。
if (lpszFileName) m_pID3Tag=new ID3_Tag(lpszFileName);
}

//デストラクタ
CID3Tag::~CID3Tag()
{
Delete();
}


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

//メンバー変数の初期化
void CID3Tag::Init()
{
m_pID3Tag=NULL;
}

//メンバー変数の削除
void CID3Tag::Delete()
{
if (m_pID3Tag) delete m_pID3Tag;
Init();
}

//新規ID3タグの作成
BOOL CID3Tag::TagNew(LPCTSTR lpszVersion)
{
Delete();

int index=g_FindTagVersion(lpszVersion);
//ID3v2.4以降の場合は、エラー終了します。
if (index>4) return FALSE;

m_pID3Tag=new ID3_Tag;
SetTagVersion(lpszVersion);
return TRUE;
}

//ID3タグの生データから、その内容を解析します。
unsigned int CID3Tag::Parse(BYTE* data,DWORD memSize)
{
ASSERT(AfxIsValidAddress(data,memSize,FALSE));
if (!AfxIsValidAddress(data,memSize,FALSE)) return FALSE;

if (!CID3Tag::TagNew()) return FALSE;
if (!m_pID3Tag) return FALSE;
//生データの内容を解析して、タグを作成します。
size_t size=m_pID3Tag->Parse(data,memSize);

//フレーム数を返して、終了します。
int nCount=CID3Tag::GetFrameCount();
if (!nCount){
Delete();
return FALSE;
}
return nCount;
}

//読み込んだタグのフレーム数を取得します。
unsigned int CID3Tag::GetFrameCount()
{
if (!m_pID3Tag) return FALSE;
return (unsigned int)m_pID3Tag->NumFrames();
}

//指定FrameIDと同じフレームを探して、フレーム番号を返します。(エラー時:-1)
int CID3Tag::FindFrame(const char* FrameID)
{
if (!m_pID3Tag) return -1;
size_t length=strlen(FrameID);
int index=0;
ID3_Tag::Iterator* iter=m_pID3Tag->CreateIterator();
const ID3_Frame* frame=NULL;
BOOL bFind=FALSE;
while (frame = iter->GetNext()){
if (::memcmp(frame->GetTextID(),FrameID,length)==0){
bFind=TRUE;
break;
}
index++;
}
delete iter;
if (!bFind) return -1;
return index;
}

//指定フレーム番号のフレームを取得します。(エラー時:-1)
ID3_Frame* CID3Tag::_FindFrame(UINT indexFrame)
{
if (!m_pID3Tag) return FALSE;
int index=0;
ID3_Tag::Iterator* iter=m_pID3Tag->CreateIterator();
ID3_Frame* frame=NULL;
BOOL bFind=FALSE;
while (frame = iter->GetNext()){
if (index==indexFrame){
bFind=TRUE;
break;
}
index++;
}
delete iter;
if (!bFind) return FALSE;
return frame;
}

//フレーム記述の取得(ASCII)
const char* CID3Tag::FrameDescriptionA(const char* frameID)
{
//引数のFrameIDを元に新規フレームを作成します。
ID3_FrameID eID=ID3_FindFrameID(frameID);
if (eID==ID3FID_NOFRAME) return FALSE;

ID3_FrameInfo FrameInfo;
return FrameInfo.Description(eID);
}

//ID3v2.2タグ名からID3v2.3/4タグ名の取得
const char* CID3Tag::ShortToLongName(const char* frameID)
{
//引数のFrameIDを元に新規フレームを作成します。
ID3_FrameID eID=ID3_FindFrameID(frameID);
if (eID==ID3FID_NOFRAME) return FALSE;

ID3_FrameInfo FrameInfo;
return FrameInfo.LongName(eID);
}

//フレーム記述の取得(CString)
BOOL CID3Tag::FrameDescription(CString& strDescription,const char* frameID)
{
const char* description=FrameDescriptionA(frameID);
if (!description) return FALSE;
#ifdef _UNICODE
CString strA=description;
strDescription=strA;
#else //_UNICODE
strDescription=description;
#endif//_UNICODE
return TRUE;
}

//現在のID3タグのバージョンの取得
enum ID3_V2Spec CID3Tag::_TagVersion()
{
if (!m_pID3Tag) return (ID3_V2Spec)-1;
return m_pID3Tag->GetSpec();
}

//現在のID3タグのバージョンの取得
BOOL CID3Tag::TagVersion(CString& strVersion)
{
//ID3v1タグの場合。
if (m_pID3Tag->HasV1Tag()){
//TRCKフレーム(トラック番号)があればID3v1.1とします。
int index=FindFrame("TRCK");
int indexVer=(index>-1)?1:0;
strVersion=g_TagVersion(indexVer);
}
//ID3v2タグの場合。
else{
enum ID3_V2Spec spec=_TagVersion();
if ((spec<0)||(spec>3)) return FALSE;
LPCTSTR lpszVersion=g_TagVersion(spec+2);
if (!lpszVersion) return FALSE;
strVersion=lpszVersion;
}
return TRUE;
}

//現在のID3タグのバージョンを変更します。
BOOL CID3Tag::SetTagVersion(LPCTSTR lpszVersion)
{
if (!m_pID3Tag) return FALSE;
int index=g_FindTagVersion(lpszVersion);
//ID3v2.4以降の場合は、エラー終了します。
if (index>4) return FALSE;

switch(index){
case 0: //ID3v1.0
case 1: //ID3v1.1
m_pID3Tag->SetTagType(ID3TT_ID3V1);
break;
case 2: //ID3v2.2.0
case 3: //ID3v2.2.1
case 4: //ID3v2.3.0
m_pID3Tag->SetSpec((ID3_V2Spec)(index-2));
break;
}
return TRUE;
}




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

メールアドレス:

ホームページアドレス:

コメント:

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


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

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