2021年6月14日月曜日

C# TagLib#でwavファイルのタグを設定する方法

TagLibでmp3のタグ編集アプリを作りつつ、ついでにwavのタグ設定を試してみました。

mp3で文字化けに悩まされたましたが、wavでもやはり発生しました。

wavの場合の問題は、WindowsはwavのタグをShift-JISで読み書きするのに対し、TagLibはUTF8で読み書きすることです。

TagLibはファイルから読み込んだバイト列をByteVectorオブジェクトに格納し、これをAPIを介してStringにしています。

なので、そのAPIを介さずに、TagLibが保持しているバイト列をShift-JIS EncodingでString化し、Saveする前にバイト列を直接セットすれば文字化けを回避できます。

TagFileを作るときはファイルの種類を意識する必要はありません。

    TagLib.File TagFile = TagLib.File.Create(FilePath);

TagFileからTagを取得するときは、TagLib.TagTypes.RiffInfoを使います。

    TagLib.Riff.InfoTag tag = TagFile.GetTag(TagLib.Riff.InfoTag, true);

次の要領でShift-JIS Endodingでバイト列をString化します。

    Encoding SjisEnc = System.Text.Encoding.GetEncoding("shift-jis");
    String GetStringFromSjisPropery(TagLib.Riff.InfoTag tag, TagLib.ByteVector id)
    {
        TagLib.ByteVectorCollection vals = tag.GetValues(id);
        foreach (TagLib.ByteVector v in vals)
        {
            byte[] b = v.Data;
            if (b.Length > 0)
            {
            return SjisEnc.GetString(b);
            }
        }
        return null;
    }

Artist, Album, Titleのidは次のようになります。

    ByteVector idArtist = new ByteVector(Encoding.ASCII.GetBytes("IART"));
    ByteVector idAlbum = new ByteVector(Encoding.ASCII.GetBytes("IPRD"));
    ByteVector idTitle = new ByteVector(Encoding.ASCII.GetBytes("INAM"));

出力時は、save直前にShift-JISでバイト列化したByteVectorを当該項目にセットします。

    ByteVector ToByteVector(ByteVector id, String str)
    {
        return new ByteVector(SjisEnc.GetBytes(str+ " "));
    }

なぜか文字化けるので、strにスペースを一文字足してからバイト列化しています。
(スペースではなくnull('\0')が正しいのかも?)

StringをByteVectorにしたものを、次の要領でtagにセットします。

    ((TagLib.Riff.InfoTag)tag).SetValue(id, ToByteVector(id, str));

uintのTrackなど、String以外の項目はTagLibのAPIで直接操作できます。

Windows内だけ利用する場合は、これでOKでしょう。他のOS環境や、アプリによっては文字化けるかもしれません。

[追記]

Wavのヘッダー仕様はRIFFで定義されていますが、これのTRACKのIDが曖昧です
IPRTITRK, TRCKの3つが使われる場合があるようです。
TagLibは
IPRTで読み書きしていますが、WindowsのエクスプローラはITRKの値を表示します。

TagLibでITRKに値のRead/Writeは次のようにします。 

  ByteVector idTrack = new ByteVector(Encoding.ASCII.GetBytes("ITRK"));
  //Read
  uint trackNum = ((TagLib.Riff.InfoTag)tag).GetValueAsUInt(idTrack);
  //Write
  ((TagLib.Riff.InfoTag)tag).SetValue(idTracktrackNum);

-------

ついでながら、TagLibは様々なフォーマットに対応しています。
wma、m4a(aac)を試したところ、これらはutf8で問題ないようで、基本的にmp3と同じ要領で読み書きできます。

TagFile.GetTag(TagLib.TagTypes)の引数に使う定数は次のようになります。

    wma: TagLib.TagTypes.Asf
    m4a: TagLib.TagTypes.Apple