2011年、明けましておめでとうございます。今年初記事は、しばらく続いていた SAP ではなく、Visual Studio ネタで。

多くの人が MP3 形式の音楽ファイルを利用していることでしょう。このファイル形式には、曲名やアーティスト情報、いつの間にやら画像も含めることができるようになっています。携帯音楽プレーヤーなどで再生すると、曲情報やらCD のジャケット写真やらが表示されていい感じです。iTunes, Winamp などのプレーヤーはもちろん、Windows などの OS でも標準で曲情報の編集機能がついて、便利になりました。

MP3 以外の音楽ファイルについては調べていませんが、このようなメタ情報をファイルに付加する形式には幾つかあり、MP3 の場合は ID3 という形式が有名だったりします。

http://ja.wikipedia.org/wiki/ID3%E3%82%BF%E3%82%B0

この情報をプログラムから使いたい場合、フォーマットを調べてがりがり編集するのも手ですが (ID3 は、思ったより複雑なフォーマットではないらしいが・・・)、世の中にはきっと便利なライブラリがあるに違いなく、そういうのを利用するのが手っ取り早いです。

ID3.org のサイトを真面目に読むと、以下のページで幾つかのライブラリが紹介されています。検索すると、他にも多数のライブラリが存在していそうですが、まあ本家の情報が一番信頼できると思いますので・・・

http://www.id3.org/Implementations

最終的には、Win32 コンソールのアプリケーションを作るのが目的なので、C++ のライブラリである ID3Lib を使います。それにしても Win32 から ID3 ライブラリを使うための情報が少ないこと少ないこと!

http://id3lib.sourceforge.net/

とりあえず、2011 年 1 月 2 日の最新バージョンは 3.8.3 らしい。というか 2003 年で更新がストップしている・・・。とりあえず id3lib-3.8.3.zip をダウンロード。ファイルを解凍すると、昔懐かしい dsw, dsp といった拡張子のファイルがある。Visual Studio 6.0 か!動くだろうか。

win32.readme.first.txt を参考にシナリオ B を試すが、スタティック リンクだと Unicode でプロジェクトをビルドするときに、シンボルが被ったり足りなくなったりでうまくいかないため、ダイナミック リンクに変更。シナリオ C は以下。

C)***Your project wants to link id3lib dynamic: (instructions below for vc) 
1) Rename config.h.win32 to config.h 
2) include prj/id3lib.dsp and zlib/prj/zlib.dsp to your workspace (*note this is a different id3lib than above) 
3) make your project dependend on id3lib, and make id3lib dependend on zlib 
4) Add /D ID3LIB_LINKOPTION=3 to your project options (settings, C/C++ tab) (*note this is a different option than above) 
5) Add the following include dirs to your program: 
   /I <path_to_id3lib>\\include /I <path_to_id3lib>\\include\\id3  
6) (add your code which uses id3lib) 
7) Compile. 
8 ) dump id3lib.dll in your programs project dir. 
9) distribute your program including id3lib.dll 
(MS recommend you distribute it in your programs dir and not in system(32) to avoid version conficts) 


(win32.readme.first.txt より抜粋)

作業はこんな感じ↓

  1. id3lib-3.8.3\config.h.win32 を config.h にリネーム
  2. id3lib-3.8.3\prj\id3lib.dsw を手元にある Visual Studio 2010 で開いてプロジェクトを変換
    (id3lib, zlib という 2 つのプロジェクトが含まれている)
  3. 作ろうとしているソリューション mosea に、変換したプロジェクト prj\id3lib.vcxproj と zlib\prj\zlib.vcxproj を追加
  4. ソリューション設定からプロジェクトの依存関係を設定 (mosea ← id3lib ← zlib)
  5. mosea プロジェクトの、コンパイラ設定で定数を定義 (/D ID3LIB_LINKOPTION=3)
  6. include の検索ディレクトリに id3lib の所定のディレクトリを追加
  7. インポートライブラリをリンク
  8. ソリューションをビルド!
    Debug も Release もエラーなし。
  9. 最後に、ビルド後のイベントとして DLL ファイルを mosea プロジェクト側にコピーするコマンドを追加しておきます。

    こうしておくと、DLL を所定の位置にコピーし忘れることがありません。

これでライブラリを使える環境が整いました。”Hello, ID3Lib” 的なプログラムとして、アーティスト情報を表示するコードを書いてみました。作りかけ感たっぷりですが、これでも 1 時間ぐらいかかったような。

// 
// main.cpp 
//

#include <Windows.h> 
#include <stdio.h> 
#include <iostream> 
#include <id3.h>

using namespace std;

bool ParseParameters(int argc, wchar_t *argv[]); 
bool ID3_Get(LPCSTR); 
bool ID3_Set(LPCSTR);

/*

Usage: 
mosea.exe [options] [set|get]

Options: 
-f <file>

Example: 
mosea.exe -f c:\hogehoge.mp3 get

*/

bool ParseParameters(int argc, wchar_t *argv[]) { 
    if ( argc<=1 || argc%2!=0 ) { 
        goto help; 
    } 
    
    int argCmd= 0; 
    if ( wcscmp(argv[argc-1], L"get")==0 ) { 
        argCmd= 0; 
    } 
    else if ( wcscmp(argv[argc-1], L"set")==0 ) { 
        argCmd= 1; 
    } 
    else { 
        goto help; 
    } 
    
    char argPath[MAX_PATH];

    for ( int i=1 ; i<argc-1 ; i+=2 ) { 
        if ( wcscmp(argv[i], L"-f")==0 ) { 
            WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, 
                argv[i+1], -1, argPath, MAX_PATH, NULL, NULL); 
        } 
    } 
    
    if ( argCmd==0 ) { 
        ID3_Get(argPath); 
    } 
    else { 
        ID3_Set(argPath); 
    }

    return true;

help: 
    wcout << endl << L"Usage:" << endl; 
    wcout << L"mosea.exe [options] [set|get]" << endl << endl;

    wcout << L"Options:" << endl; 
    wcout << L"-f <file>" << endl << endl;

    wcout << L"Example:" << endl; 
    wcout << L"mosea.exe -f c:\\hogehoge.mp3 get" << endl << endl; 
    
    return false; 
}

bool ID3_Get(LPCSTR file) { 
    ID3Tag *pITag= ID3Tag_New(); 
    if ( pITag==NULL ) 
        return false;

    ID3Tag_Link(pITag, file);

    ID3Frame *pIFrame= ID3Tag_FindFrameWithID(pITag, ID3FID_LEADARTIST); 
    if ( pIFrame==NULL ) 
        return false;

    ID3Field *pIField= ID3Frame_GetField(pIFrame, ID3FN_TEXT); 
    if ( pIField==NULL ) 
        return false;

    char title[1024]; 
    ID3Field_GetASCII(pIField, title, 1024);

    printf_s("Artist(ASC): %s\r\n", title);    
    
    return true; 
}

bool ID3_Set(LPCSTR file) { 
    return true; 
}

int wmain(int argc, wchar_t *argv[]) { 
    if ( !ParseParameters(argc, argv) ) 
        return 0;

    return 0; 
}

大きな問題点として、Unicode に対応していないので、アーティスト情報に Unicode 文字が入っていると正しく表示できません。ええ、試しに Beyoncé の曲を使うと見事に文字化けました。そのへんの対応は ID3Lib の仕様をちゃんと読まないといけないので、即興では無理でした。

>mosea -f 001a.mp3 get 
Artist(ASC): Beyonce

>mosea -f 001w.mp3 get 
Artist(ASC): フフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフ 
フフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフフ 
フフフフフp琮 ← Beyoncé だと文字化ける 

COM っぽい使い方をしていますが、id3\tag.h をインクルードすると C++ クラスも使えるので、後で書き換える予定です。実際 ID3Lib は COM サーバーとしても使えるようなことが書いてありましたが、Win32 からはそのまま使った方が楽なので、そのへんの検証はパス。