業務備忘録

備忘録です

バイナリの制御文字と'BOM'でハマった

2023-10-22 11:28:13 | 日記

最近部屋の片づけをしており、大量の本やCDを整理しています。
折角だから捨てる前に全部CDを取り込んでおこう…ということでアルバムを一つ一つasunderから取り込んでいるのですが、面倒な課題にぶつかってしまいました。

1. 曲・アルバム・作曲者の表記揺れが多すぎる問題

昔聞いていた曲には19世紀の古典的な曲が多く、再演・再奏された録音を収録している場合、アルバムが違うだけで「同じ曲なのにタイトルが違う」ということが頻発します。

J.Strauss Ⅱ Wo die Zitronen blühen

→ヨハンシュトラウス2世 シトロンの花咲くところ

→ヨハン・シュトラウスⅡ世 レモンの花咲くところ

→ヨハン・シュトラウス2世 シトロンの花咲く国

曲名が違うだけならそこまで神経質にならずともよいですが、作曲者名に揺れがあると「その作曲者の曲だけ聞きたい」という場合不便を極めます。

ということで、表記揺れを一括で直せるように楽曲ファイルのメタデータを編集できるようにしたいと思います。類似のMP3タグ編集ソフトで同等のことはできるのかもしれませんが、調べていません。どうせ勉強だし。

2. バイナリを読む

とりあえず、AACとかその他の音楽ファイルの形式は考えず、MP3のタグ(ID3V2)を編集することだけ考えます。
タグを編集する場合、バイナリ(0と1で構成された、テキスト以外のデータ)の読み書きを直接行うことになります。

ID3V2タグにはヘッダーも存在しますが、ここでは最初にタグの本体の中身を見ることにします。
バイナリエディタの左側のパネルの各セルには、1バイトの情報が表示されており、右側のパネルにはバイトに対応する文字がエンコードされて表示されています。

言わでものことかもしれませんが、1バイトはここでは16進表記されているので、'54'なら16 * 5 * 4で10進数の84となります。16進数をリテラル表記する際の'0x'という接頭辞をつけて表記するなら、0x54となります。
さて、上掲の画像の中で、エンコードされた文字列を見ていると、「TSSE」や「TRCK」、「TPE1」という大文字の文字列が目につきます。
この文字列からID3V2タグの「フレーム」と呼ばれる、楽曲のメタデータのより具体的な情報を格納する部分が開始します。「TSSE」などは各フレームの名称で、「TSSE」はエンコード設定に関する情報、「TRCK」はトラック番号、「TPE1」は主なるアーティストを指します。

今回は、「TRCK」について見ていきたいと思います。上掲の画像のうち、赤枠で囲った部分がフレーム名の「TRCK」を表す部分。
水色の枠で囲った部分が、フレームの本体サイズを表す部分で、今回は「00 00 00 05」なので、5バイトであることがわかります。本来はSyncsafe Integerという表記法に対応したサイズの計算が必要ですが、今回は割愛。
緑色の枠で囲った部分が、フラグを表す部分ですが、通常は使用されません。
そして、最後に、オレンジの枠で囲った部分が、フレームの本体で、ここにトラック番号が格納されています。サイズは先ほどフレームの本体サイズを表記していた箇所に従って5バイトとなります。

さて、フレームのサイズに従ってトラック番号を表す箇所を見てみると、「01 FF FE 31 00」と記されています。
31 = 0x31はUnicode(文字コードの標準規格)では'1'を指します。バイナリエディタで見ていた曲はアルバムの1曲目なので、正しそうですね。
ただ、0x31の前には、0x01,0xFF,0xFEというバイト列が並んでいます。0x01や0xFFや0xFEには対応する文字が無いようですが…。

3. 0x01,0xFFFEとは

Unicodeエンコーディング(UTF-16またはUTF-8)が使用されているデータ・ファイルには、ファイルの最初の数バイトにバイト順序マーク(BOM)が含まれている場合があります。キャラクタ・セットUTF-16が使用されているデータ・ファイルでは、ファイルの最初の2バイトの値{0xFE,0xFF}は、ファイルがビッグ・エンディアンのデータを含んでいることを示すBOMです。{0xFF,0xFE}という値は、ファイルにリトル・エンディアンのデータが含まれていることを示すBOMです。
https://docs.oracle.com/cd/E57425_01/121/SUTIL/GUID-CFEED713-D459-42F4-A777-7AAA654451AC.html

0xFF,0xFEが使用されている場合、続くデータはリトルエンディアン(=多バイトをメモリに格納する際の方式1つ。リトルエンディアンの場合下位バイトを先に格納する)ことを明示しています。このような記号をBOM(Byte Order Mark)と呼びます。
(ただし、UTF-8のBOMは0xEFBBBFで、これはUTF-8で書き込まれていることを明示するために使われるのであって、今見ているファイルがUTF-8で書き込まれているはずなのに0xFFEが書き込まれているのはなぜなのかはよくわかりません。)

せんずるに、0XFFFEは文字コードとしては定義されない値であり、今回はUTF-8のファイルとしてバイナリデータを扱うので、楽曲に関するメタデータとして考慮する必要はないということです。
ただし、読み込んだバイト列をエンコードする際に弾きだす必要はありますね。

0x01については、unicodeの制御文字のようで、意味としては'START OF HEADING'=ヘッダ開始を表す符号にすぎないようで、これも文字列として扱わないように弾く必要あり。そのほかunicodeには0x000~0x001Fまでの制御文字があるようです。