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ライブラリを使用したほうが無難のようです。



id3lib-3.8.3のインストール

今回もsourceforge.netというサイトからダウンロードしました。



上図のサイトを開いて、「Download id3lib-3.8.3.zip (1.1 MB)」と書いてあるハイパーリンクをクリックすると「id3lib-3.8.3-1.zip」ファイルのダウンロードが自動的に開始されます。

次にダウンロードした「id3lib-3.8.3-1.zip」ファイルを所定のフォルダー内にWinRAR等を使って解凍すると、「id3lib-3.8.3」と言う名のフォルダーが展開されます。



「libid3tag」ライブラリーとは違って「zlib」ライブラリーも一緒になっているので個別にインストールする必要はありません。

修正事項

フルサイズのXingヘッダーの付いたVBR MP3ファイルを読み込むと、メモリーエラーになる不具合の修正。

「mp3_parse.cpp」の468行目

using namespace dami;

bool Mp3Info::Parse(ID3_Reader& reader, size_t mp3size)
{




// read xing/vbr header if present
// derived from code in vbrheadersdk.zip
// from http://www.xingtech.com/developer/mp3/


const size_t VBR_HEADER_MIN_SIZE = 8; // "xing" + flags are fixed
// const size_t VBR_HEADER_MAX_SIZE = 116; // frames, bytes, toc and scale are optional ←コメントアウト
const size_t VBR_HEADER_MAX_SIZE = 120; // frames, bytes, toc and scale are optional ←追加する行

if (mp3size >= vbr_header_offest + VBR_HEADER_MIN_SIZE)
{







UTF-16BEで書かれたStringList文字列を読み込むための不具合の修正
※field_string_ascii.cpp
ID3_FieldImpl::AddText_i関数の228行目をコメントアウトして、その直ぐ下にif文を1行挿入します。

/** For fields which support this feature, adds a string to the list of
** strings currently in the field.
**
** This is useful for using id3v2 frames such as the involved people list,
** composer, and part of setp. You can use the GetNumTextItems() method to
** find out how many such items are in a list.
**
** \code
** myFrame.GetField(ID3FN_TEXT)->Add("this is a test");
** \endcode
**
** \param string The string to add to the field
**/

size_t ID3_FieldImpl::AddText_i(String data)
{
size_t len = 0; // how much of str we copied into this field (max is strLen)
ID3D_NOTICE ("ID3_FieldImpl::AddText_i: Adding \"" << data << "\"" );
if (this->GetNumTextItems() == 0)
{
// there aren't any text items in the field so just assign the string to
// the field

len = this->SetText_i(data);
}
else
{

// ASSERT(_fixed_size == 0)
_text += '\0';
// if (this->GetEncoding() == ID3TE_UNICODE) ←コメントアウト
if ((this->GetEncoding()==ID3TE_UTF16)||(this->GetEncoding()==ID3TE_UTF16BE)) ←追加する行

{
_text += '\0';
}
_text.append(data);
len = data.size();
_num_items++;
}

return len;
}


UTF-16シフトJIS入力と、StringList文字列のUNICODE入力時における不具合の修正

※io_helpers.cpp
io::writeUnicodeText関数の365行目から371行目までコメントアウトして、その直ぐ下に以下挿入します。

size_t io::writeUnicodeText(ID3_Writer& writer, String data, bool bom)
{
ID3_Writer::pos_type beg = writer.getCur();
/* size_t size = (data.size() / 2) * 2; ←ここからコメントアウト
if (size == 0)
{
return 0;
}
if (bom)
{
// Write the BOM: 0xFEFF
unicode_t BOM = 0xFEFF;
writer.writeChars((const unsigned char*) &BOM, 2);
for (size_t i = 0; i < size; i += 2)
{
unicode_t ch = (data[i] << 8) | data[i+1];
writer.writeChars((const unsigned char*) &ch, 2);
}
}*/
←ここまで
//StringList文字列数を算出します。 ←ここから挿入
dami::String ucs=data;
size_t size=ucs.size()/2;
if (!size) return 0;
wchar_t* ucstr=(wchar_t*)ucs.c_str();
char* src=(char*)ucstr;
char* dst=src;
size_t nStrings=0;
for(size_t i=0;i<size;i++){
char ch=*src++;
*dst++=*src++;
*dst++=ch;
if (ucstr[i]=='\0') nStrings++;
if ((i==size-1)&&(ucstr[i]!='\0')){
ucs+='\0';
ucs+='\0';
ucstr=(wchar_t*)ucs.c_str();
nStrings++;
}
}

//StringList文字列毎に書き込みます。
for(size_t i=0;i<nStrings;i++){
//2つ目の文字列から、ヌル文字を書き込みます。
if (i>0){
unicode_t null=NULL_UNICODE;
writer.writeChars((const unsigned char*)&null,2);
}
//bom=trueの場合はUTF-16LE、falseの場合はUTF-16BEと見做します。
unicode_t BOM=(bom)?0xFEFF:0xFFFE;
writer.writeChars((const unsigned char*)&BOM,2);

size_t length=wcslen(ucstr);
for(size_t j=0;j<length;j++){
unicode_t wch=ucstr[j];
if (!bom) wch=((wch>>8)&0x00FF)|((wch<<8)&0xFF00);
writer.writeChars((const unsigned char*)&wch,2);
}
ucstr+=length+1;
} ←ここまで
return writer.getCur() - beg;
}


※utils.cpp
dami::String mbstoucs関数の53行目から58行目までをコメントアウトして、その直ぐ下に以下挿入します。

// converts an ASCII string into a Unicode one
dami::String mbstoucs(dami::String data)
{
/* size_t size = data.size(); ←ここからコメントアウト
dami::String unicode(size * 2, '\0');
for (size_t i = 0; i < size; ++i)
{
unicode[i*2+1] = toascii(data[i]);
}*/
←ここまで
//StringList文字列数を算出します。 ←ここから挿入
dami::String mbs=data;
size_t size=mbs.size();
char* mbstr=(char*)mbs.c_str();
size_t nStrings=0;
for (size_t i=0;i<size;i++){
if (mbstr[i]=='\0') nStrings++;
if ((i==size-1)&&(mbstr[i]!='\0')){
mbs+='\0';
mbstr=(char*)mbs.c_str();
nStrings++;
}
}

//StringList文字列毎に変換します。
char* localeInfo = setlocale(LC_ALL, ".ACP");
dami::String unicode;
for (size_t i=0;i<nStrings;i++){
size_t length=strlen(mbstr);
size_t count=mbstowcs(NULL,mbstr,length);
if(count==-1) break;

dami::String wcs(count*2,'\0');

wchar_t* wcstr=(wchar_t*)wcs.c_str();
size_t size=mbstowcs(wcstr,mbstr,length);

char* src=(char*)wcstr;
char* dst=src;
for(size_t i=0;i<size;i++){
char b=*src++;
*dst++=*src++;
*dst++=b;
}
if (i>0){
unicode+='\0';
unicode+='\0';
}
unicode.append(wcs);
mbstr+=length+1;
} ←ここまで
return unicode;
}


※utils.cpp
dami::String ucstombs関数の65行目から70行目までをコメントアウトして、その直ぐ下に以下挿入します。

// converts a Unicode string into ASCII
dami::String ucstombs(dami::String data)
{
/* size_t size = data.size() / 2; ←ここからコメントアウト
dami::String ascii(size, '\0');
for (size_t i = 0; i < size; ++i)
{
ascii[i] = toascii(data[i*2+1]);
}*/
←ここまで
//StringList文字列数を算出します。 ←ここから挿入
dami::String ucs=data;
size_t size=ucs.size()/2;
wchar_t* ucstr=(wchar_t*)ucs.c_str();
char* src=(char*)ucstr;
char* dst=src;
size_t nStrings=0;
for(size_t i=0;i<size;i++){
char ch=*src++;
*dst++=*src++;
*dst++=ch;
if (ucstr[i]=='\0') nStrings++;
if ((i==size-1)&&(ucstr[i]!='\0')){
ucs+='\0';
ucs+='\0';
ucstr=(wchar_t*)ucs.c_str();
nStrings++;
}
}

//StringList文字列毎に変換します。
char* localeInfo = setlocale(LC_ALL, ".ACP");
dami::String ascii;
for (size_t i=0;i<nStrings;i++){
size_t length=wcslen(ucstr);
size_t count=wcstombs(NULL,ucstr,NULL);
if (count==-1) break;

dami::String mbs(count,'\0');
size_t size=wcstombs((char*)mbs.c_str(),ucstr,count);

if (i>0) ascii+='\0';
ascii.append(mbs);
ucstr+=length+1;
} ←ここまで
return ascii;
}



ID3v2.2を書き込むための修正

「tag_impl.cpp」のID3_TagImpl::operator=関数の308行目に、以下の行を追加します。

ID3_TagImpl &
ID3_TagImpl::operator=( const ID3_Tag &rTag )
{
this->Clear();
this->SetSpec(rTag.GetSpec()); ←追加する行
this->SetUnsync(rTag.GetUnsync());
this->SetExtended(rTag.GetExtendedHeader());
this->SetExperimental(rTag.GetExperimental());







「frame_render.cpp」のID3_FrameImpl::Render関数の81行目に、以下の行を追加します。

void ID3_FrameImpl::Render(ID3_Writer& writer) const
{
// Return immediately if we have no fields, which (usually) means we're
// trying to render a frame which has been Cleared or hasn't been initialized

if (!this->NumFields())
{
return;
}

ID3_FrameHeader hdr;
hdr.SetSpec(this->GetSpec()); ←追加する行
const size_t hdr_size = hdr.Size();

// 1. Write out the field data to the buffer, with the assumption that
// we won't be decompressing, since this is the usual behavior








「header_tag.cpp」のID3_TagHeader::Render関数の70行目、71行目を削除して、以下の行を追加します。

void ID3_TagHeader::Render(ID3_Writer& writer) const
{
writer.writeChars((uchar *) ID, strlen(ID));

// writer.writeChar(ID3_V2SpecToVer(ID3V2_LATEST)); ←コメントアウト
writer.writeChar(ID3_V2SpecToVer(GetSpec())); ←追加する行
// writer.writeChar(ID3_V2SpecToRev(ID3V2_LATEST)); ←コメントアウト
writer.writeChar(ID3_V2SpecToRev(GetSpec())); ←追加する行

// set the flags byte in the header







ID3v1をID3_Tag::Parse関数で読み込むための修正

「tag.h」のID3_Tagクラスの116行目に以下プロトタイプ追加。

class ID3_CPP_EXPORT ID3_Tag
{
ID3_TagImpl* _impl;
char _tmp_filename[ID3_PATH_LENGTH];
public:




bool HasTagType(ID3_TagType tt) const;
ID3_V2Spec GetSpec() const;
bool SetSpec(ID3_V2Spec);
bool SetTagType(enum ID3_TagType tt); ←追加する行

static size_t IsV2Tag(const uchar*);




「tag.cpp」のID3_Tag::Parse関数の561行目コメントアウトして、その直ぐ後に以下の行を追加します。

size_t ID3_Tag::Parse(const uchar* buffer, size_t bytes)
{
ID3_MemoryReader mr(buffer, bytes);
ID3_Reader::pos_type beg = mr.getCur();
// id3::v2::parse(*_impl, mr); ←コメントアウト
if (!id3::v2::parse(*_impl, mr)){ ←ここから追加
mr.setCur(mr.getEnd());
if (id3::v1::parse(*_impl, mr))
SetTagType(ID3TT_ID3V1);
} ←ここまで
return mr.getEnd() - beg;
}



「tag.cpp」の951行目に以下メンバー関数追加。

bool ID3_Tag::SetTagType(enum ID3_TagType tt)
{
return _impl->SetTagType(tt);
}



「tag_impl.h」のID3_TagImplクラスの127行目にメンバー関数のプロトタイプとマクロを追加。

class ID3_TagImpl
{
typedef std::list<ID3_Frame *> Frames;
public:
typedef Frames::iterator iterator;
typedef Frames::const_iterator const_iterator;
public:




bool HasTagType(ID3_TagType tt) const { return _file_tags.test(tt); }
ID3_V2Spec GetSpec() const;
bool SetSpec(ID3_V2Spec);
bool SetTagType(ID3_TagType tt) {return _file_tags.add(tt);} ←追加する行

static size_t IsV2Tag(ID3_Reader&);









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

メールアドレス:

ホームページアドレス:

コメント:

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


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

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