ホーム >プログラム >Delphi 6 ローテクTips >ID3タグの取得と書き込み

TWindowsMediaPlayerを利用するとID3タグを取得できるだけでなく書き込むこともできます。
メディアを読み込んで再生できる状態になってからでないと読み書きできないので時間がかかるのが難点ですが、工夫次第で大いに活用できるのではないかと思います。


以下はWMP10を取り込んで試した結果です。
WMP9やWMP11などだと違う結果になるところもあるかも知れません。

取得

WindowsMediaPlayerのOpenStateChangeイベントで状態を判定してMediaOpen(wmposMediaOpen)になれば取得できます。
Shell オブジェクトを利用する場合と違って取得できる項目も多く、またHDDのフォーマットがNTFSに縛られないことからけっこう使えると思います(時間がかかることを許容できれば)

procedure TForm1.WindowsMediaPlayer1OpenStateChange(Sender: TObject; NewState: Integer);
var
  i: Integer;
  ls_Name, ls_Value, ls_ReadOnly: WideString;
begin
  if (NewState = wmposMediaOpen) then begin
    WindowsMediaPlayer1.controls.stop;  //再生を続ける場合は削除

    //取得できる項目の数だけ処理
    for i := 0 to WindowsMediaPlayer1.currentMedia.attributeCount -1 do begin
      //項目名を取得
      ls_Name := WindowsMediaPlayer1.currentMedia.getAttributeName(i);  //項目名
      //項目が読み込み専用かどうかの判定
      if (WindowsMediaPlayer1.currentMedia.isReadOnlyItem(ls_Name)) then begin
        //読み込み専用の項目
        ls_ReadOnly := 'R';
      end else begin
        //書き込み可能かもしれない項目
        ls_ReadOnly := ' ';
      end;
      //項目の値を取得
      ls_Value := WindowsMediaPlayer1.currentMedia.getItemInfo(ls_Name);  //値を取得
      //読み込み専用かどうか、項目名、値を表示
      myDebug.gpcDebugAdd(WideFormat('%s %s %s', [ls_ReadOnly, ls_Name, ls_Value]));
    end;
  end;
end;
結果

Stage6からダウンロードしたファイルをTMPGEnc XPressでwmaに変換したファイルの例。
タイトルやアーティストなどを書き込んでいない状態だとこういう感じです。
Titleに何も設定していない状態だとTilteタグはファイルの主ファイル名を返すようです。

ちなみにこれらのファイルがVBR(可変ビットレート)である場合WindowsMediaPlayer(コンポーネントではなくMicroSoftの製品の方、、ややこしい)に読み込ませるともっと色々な情報が書き込まれる場合もあります。

行頭にRがある項目は読み込み専用でこれに書き込もうとするとエラーが発生します。
ただしメディアによって読み込み専用と判定されたりされなかったりする項目もありますし、書き込みできる項目であっても読み込み専用であると判定される場合もありました。
またwmaファイルでは書き込み可能であってもmp3では読み込み専用で書き込もうとするとエラーが発生する項目もありました。
書き込むときには読み込み専用かどうか事前にチェックしてから書き込む、try〜exceptで囲む、などした方が良いでしょう。


書き込み

書き込みはURLプロパティにファイル名を代入して開いてから WindowsMediaPlayer1.currentMedia.setItemInfo(ls_Name, ls_Value)) としてcloseメソッドを呼ぶか別のファイルを読み込むだけでできます。
わざわざ更新処理をする必要はありません。
というか更新処理のプロパティや手続きがないので再生を続けている間は更新できません。
また他のプレーヤーで同じファイルを開いていたりすると(エラーが発生することもなく黙ったまま)更新に失敗します。
また更新しようとするファイルがメディアコレクションに含まれる場合ハングアップします(実際は2-3分待っていれば戻ってきますがタグ情報の更新はされません)
メディアコレクションというのはWMP10を起動して「ライブラリ」をクリックすると一覧で表示されるメディアライブラリのことです。
ここに登録されているファイルのタグを書き込もうとするとハングアップしたようになり失敗してしまうということです。

プログラムからメディアコレクション中のメディアを削除すると、このライブラリからも削除されます。

ということで書き込みにはメディアコレクションからファイルを削除しなければなりません。ただメディアコレクションを操作するとWMPのライブラリに影響してしまいます。
WMPのメディアライブラリなんて関係ないよ、という場合はいいのですがWMPをプレーヤーとして利用している場合はライブラリから削除したままでは不都合なので、更新しようとするファイルがライブラリに登録されているかを確認してから削除・更新を行い登録されていたのなら登録し直すという処理が必要になります。
私は必要ないので削除するだけの処理しかしていませんが。


procedure TForm1.Button2Click(Sender: TObject);
begin
  if (Assigned(WindowsMediaPlayer1.currentMedia)) then begin
    //メディアライブラリからファイルの登録を削除
    //必要ならこの前後にライブラリに存在するかの確認と書き込み後の再登録を行う。
    try
      WindowsMediaPlayer1.mediaCollection.setDeleted(WindowsMediaPlayer1.currentMedia, True);
    except
      //「パラメータが間違っています」というエラーが出るが無視。
    end;

    //タグ情報書き込み。
    try
      WindowsMediaPlayer1.currentMedia.setItemInfo('Author',      'ドクロちゃん(千葉紗子)');
      WindowsMediaPlayer1.currentMedia.setItemInfo('Title',       '撲殺天使ドクロちゃん');
      WindowsMediaPlayer1.currentMedia.setItemInfo('WM/Composer', '高木隆次');
      WindowsMediaPlayer1.currentMedia.setItemInfo('WM/Writer',   '水島努');
    except
      //必要ならここにエラーの時の処理。
    end;
    WindowsMediaPlayer1.close;
  end;
end;
結果

Author、Title、WM/Composer、WM/Writerの四項目に書き込みできているのが分かります。

setDeletedメソッドの引数にcurrentMediaを指定すると「パラメータが違います」というエラーが出てしまうのでそれを無視するためにtry 〜 except end;で囲んでしまいます。
本来エラーが出るのだから何かしら良くない結果になっているのではなかろうかとも思うのですが、ライブラリからはちゃんと削除されていますしタグも書き込まれているのでこれで良いのではと思います。

以前はsetDeleteの引数にcurrentMediaではなくmediaCollection.addメソッドを使って取得したメディアオブジェクトを渡していました。
こうするとエラーは出ないのですが、書き込みができるようになるまで少し時間がかかるためcurrentMediaがnilでなくなるまで待つための処理が必要になりますし、そのため時間もかかります。
そもそもメディアコレクションから削除するためにメディアコレクションに追加する処理を行うというのも妙なことです。
ということでエラーは出るけれども無視しても問題なさそうなので短時間で処理できる方法に切り替えました。

数値に変換できる文字列の認識は以下のような結果になります。
全角のアラビア数字の後にある半角の数字は認識されます。
'54' (一文字目は全角の5で二文字目は半角の4)は 4 と書き込まれます。
'5' (全角の5)は認識されず書き込まれません。書き込まれるのは半角の数字だけです。
'四4' (二文字目は半角の4)は認識されず書き込まれません。漢数字はNGです。
'456' (一文字目は半角の4で二文字目が全角の5三文字目が半角の6)は 4 と書き込まれます。

なんだか中途半端で変ですね。WMP9やWMP11だと違うんでしょうか。

項目

とりあえず使えそうな項目を列挙してみます。

AttributeName 内容 備考
W Author アーティスト 「作成者」あるいは「出演者」という表現をしている場合もあり。
R Bitrate ビット レート 単位はbps。
通常使用されるkpbsにするには1000で割って小数点以下切捨て(のようです)
R Duration 長さ(時間) 単位は秒。
小数点以下切捨てで表示していることが多いようです。
R FileSize ファイルサイズ 単位はByte。
KBにするには1024で割って小数点以下切り上げ。
R FrameRate FPS フレームレート。
1000で割るようです。
R IsVBR 可変ビットレートかどうか ビデオ、オーディオどちらか一方、あるいは両方ともVBRであればTrue(という文字列)が返ります。
W Title タイトル
W WM/AlbumArtist アルバムのアーティスト
W WM/AlbumTitle アルバム
W WM/Composer 作曲者
W WM/Conductor 指揮者
W WM/Director 監督
W WM/Genre ジャンル
W WM/Lyrics 歌詞
W WM/MediaOriginalChannel チャンネル TVのチャンネル?
W WM/MediaStationName 局名 放送局?
W WM/PartOfSet セット 二枚組みアルバムのうちの何枚目かとか。
W WM/Producer プロデューサ
W WM/Publisher スタジオ スタジオジブリとか?
W WM/SubTitle サブタイトル アニメやドラマのサブタイトル?
W WM/SubTitleDescription この回の説明 アニメやドラマの第〜話の説明とか?
N WM/TrackNumber トラック 数値に変換できる文字列のみ書き込み可能。
よく似たAttributeNameに WM/Track というのがあるがそちらは書き込みできないので注意(書き込んでもエラーは発生しない)
W WM/Writer ライター 作詞者。
映画やドラマなどのシナリオライターもここに書き込むのだろうか?
W WM/Year
他に WM/VideoHeight と WM/VideoWidth というビデオの高さと幅を返す項目もありますが書き込まれていない場合もあり、currentMedia.imageSourceHeight や currentMedia.imageSourceWidth を使った方が確実なので省きました。

R(灰色)は読み込み専用(書き込み禁止)の項目。書き込もうとするとエラーが発生します。
N(薄いオレンジ)は数値に変換できる文字列のみ書き込み可能(そうでない文字列を与えてもエラーは発生しないが書き込みは反映されない)
その他は任意の文字列を書き込み可能。ウムラウトのようなUnicode文字を含んだ文字列もOK。

ただし元々のメディアに書き込まれている文字のコードページと整合性を取らないといけないというようなことが書かれてあったような気もしますが、その辺のことは正直良く分かりません。

以前書き込む値はWideStringでないとハングアップするとかmp3infpをインストールした環境でもハングアップするなどと書いていましたが、間違いでした

ハングアップする(ように見える)のはURLプロパティを使って書き込もうとしたファイルがWMPのメディアライブラリに登録してある場合でした。

解決策としてURLプロパティに代入するのではなくプレイリストに登録して書き込むというのもあるのですが、プレイリストに登録して書き込む場合はファイルに直接書き込むというのではないようで、メディアライブラリ中のタグ情報に書き込むことでいずれファイルにもそれが反映される、という流れのようで変更がすぐには反映されません。
また場合によっては反映されないこともあります。
メディアライブラリ中のタグは更新されているけれどもエクスプローラで見てみると更新されていないということがありました。
正直これは使い難いなぁ、、という感じです。

ちなみにURLプロパティに代入して書き込む時に必要なWindowsMediaPlayer1.close;は必要ないようです。

procedure TForm1.Button1Click(Sender: TObject);
var
  ls_WFile: WideString;
begin
  if (gfnbOpenFileDialog(ls_WFile)) then begin
    WindowsMediaPlayer1.currentPlaylist.clear;
    try
      //プレイリストに登録しないとうまく書き込みできない
      WindowsMediaPlayer1.currentPlaylist.appendItem(WindowsMediaPlayer1.mediaCollection.add(ls_WFile));
      //プレイリストの場合はcontrols.playではだめ
      WindowsMediaPlayer1.controls.playItem(WindowsMediaPlayer1.currentMedia);
    except
      //対応していないファイルだとエラーが発生する
      Application.MessageBox('対応していないファイルです', 'エラー');
    end;
  end;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  if (Assigned(WindowsMediaPlayer1.currentMedia)) then begin
    try
      WindowsMediaPlayer1.currentMedia.setItemInfo('Author',      'ドクロちゃん(千葉紗子)');
      WindowsMediaPlayer1.currentMedia.setItemInfo('Title',       '撲殺天使ドクロちゃん');
      WindowsMediaPlayer1.currentMedia.setItemInfo('WM/Composer', '高木隆次');
      WindowsMediaPlayer1.currentMedia.setItemInfo('WM/Writer',   '水島努');
    except
      //必要ならここにエラーの時の処理
    end;
  end;
end;

プレイリストに登録するメリットとしては開けないファイルであったらエラーが発生することです。
URLプロパティに代入するやり方だとエラーは発生せず変わりにいちいち「この拡張子には対応していないが開くか」と聞いてくるダイアログが開いてしまいます。(ここで二回「はい」を選ぶとOnMediaErrorイベントが起きます)

とはいえ反映が直接ファイルに行われるわけではなく反映されるタイミングも今ひとつよく分からないという欠点があります。