iTunes で複数の関連する CD を取り込んだのだが、曲のタイトルが統一されていなかった。 具体的には、「[CD 番号]-[トラック番号]」みたいになってほしかったのに、1 つの CD だけ「トラックxx」みたいな形式でインポートされてしまった。
ファイル名を変えても曲タイトルは変わらないので、メタ情報を変更する必要があると判断した。 ただ、CLI からサクッとメタ情報を触る方法がなさそうだったので、バイナリをいじって変更してみようと思いたった。
ベターな解決策
フリーソフト入れたらよかったんじゃない?
目次
本編
環境
$ zsh --version zsh 5.8 (x86_64-ubuntu-linux-gnu)
やったことまとめ
- mp3 のメタ情報について調べる
- Vim でバイナリファイルを開く方法を調べる
- 実際のファイルがどうなっているか調べる
- 書き換えてみる
- CLI で一括変更する
mp3 のメタ情報について調べる
超参考になったのはこちらの記事。
どうやら ID3 タグというものが mp3 のメタ情報らしい。
じゃあ実際のファイルでどうなってるんだろうか。
Vim でバイナリファイルを開く方法を調べる
ググったらサクッと出てきた。
-b
を付けてファイルを開き、:%!xxd
を実行すればいいらしい。
編集が終わったら :%!xxd -r
して保存。
実際のファイルがどうなっているか調べる
ということで中身を確認。
00000000: 4944 3302 0000 0000 107f 5454 3200 0011 ID3.......TT2... 00000010: 01ff fec8 30e9 30c3 30af 3030 0032 0000 ....0.0.0.00.2.. 00000020: 0054 524b 0000 0600 322f 3532 0043 4f4d .TRK....2/52.COM
さっきの記事などに照らし合わせて読んでみる。
ちなみに「長さ」はバイト数なので、例えば 4944
は長さ 2 になる。
最初の 3 文字が ID3、その次が 0200
なっているので、ID3v2.2 であることが分かる。
ID3v2 ヘッダ
ID3v2 ヘッダは以下のように定義されている(Wikipedia より)
オフセット | 長さ | 説明 |
---|---|---|
0 | 3 | “ID3” のマジックナンバー3文字 |
3 | 2 | バージョン |
5 | 1 | フラグ |
6 | 4 | サイズ |
バージョンまでは読んだのでそこから。
次の 1 バイトがフラグで、00
なので未使用。
次の 4 バイトがサイズで、0000 107f
となっている。
Syncsafe Integer というやつらしいので、いい感じに読んでみると以下の通り。
107f
→ 0001 0000 0111 1111
→ 0100001111111
→ 2175
合っているかは確認してない。
この後に拡張ヘッダが続くこともあるらしいが、v2.2 では存在しないので関係ない。
(ちなみに v2.3 以降で拡張ヘッダがある場合は、フラグで明示される)
フレーム
要するにデータの塊。 これが繰り返し指定される。
データの形式は以下(Wikipedia より)
オフセット | 長さ | 記述 |
---|---|---|
0 | 3 | フレームID |
3 | 3 | フレームサイズ |
6 | 可変長 | フレームデータ |
はい見ていきましょう。
5454 32
で、右側に TT2 って出ている。
Wikipedia でフレーム ID の一覧を見ると、「Title/songname/content description」とのこと。
今回の目的であるタイトルはここに記述されているようだ。
その次にはフレームサイズがある。
00 0011
なので 3……3?
17 だった気がするんだけどなぁ……
(ここはよくわからない、記事を書く前に試したときは 17 だったんだけどなんでだろう)
ということでスルーしてフレームデータへ。
3 行目に TRK
が見えているので、この前までだろう。
17 だよなぁ……?
01ff fec8 30e9 30c3 30af 3030 0032 0000 00
最初が 01
なので UTF-16。
fffe
はリトルエンディアンの BOM。
ということは、これ以降は 2 バイトごとに区切ってひっくり返して読む必要がある。
直したものがこちら。
30c8 30e9 30c3 30af 3000 3032 0000 00
ここで 30c8 unicode
でググると、「ト」に対応することが分かる。
全部対応付けると、「トラック02」となる。
実際にエクスプローラーに表示されているものと同じ。
ここまでで、どこにどんなふうにタイトル情報が入ってきているのかわかった。
書き換えてみる
最初の c8 30
を c7 30
に書き換えたら、1 文字目の「ト」が別の文字に変わるだろうと予想。
実際に書き換えてエクスプローラーで見てみると、「デラック02」になっていた。 Unicode は濁点付きの文字はなしの文字の直後に入っているんだな。
CLI で一括変更する
ここからが実際にやりたかったこと。
バイナリを一括置換してタイトルを変えていく。 今回は「トラックxx」を「1-xx」というものに変える。
xx 部分はそのままに、前半をサクッと置き換える。 全体の文字長が違うので、余った部分を 0 パディングする必要があるので、フレーム全体を置換する。
sed
でバイナリを扱えないか調べたけど、一回 xxd
コマンドでバイナリにしてから置き換える方法が書いてあった。
で、ここで -c
オプションに 68719476736
= 64 GB を指定しているが、自分の環境では指定できなかった。
10000000
= 10 MB なら問題なさそうなのでこちらに変更して実行。
xxd -p -c 10000000 "$filename" \ | sed -r -e 's/c830e930c330af30(....)(..)000/31002d00\1\200000000000/' \ | xxd -p -r \ > "_new-$filename"
今気付いたけど 0 足りなかったかも、支障はないけど。 あと数字の位置指定がいまいちだなぁ(両方 4 文字取るべきだった)。
ファイルサイズがでかいなら、-c
での指定をそこそこにして、最後に改行を消して書き込みすればよさそう。
sed -r -e 's/\r?\n//g'
みたいな。
実行すると、_new-ファイル名.mp3
みたいなやつが吐き出されるので、「タイトルが書き換わっているか」「再生できるか」を確認する。
問題なさそうなので全体に適用していく。
ls -1 \ | while read -r line; do \ xxd -p -c 10000000 "$line" \ | sed -r -e 's/c830e930c330af30(....)(..)000/31002d00\1\200000000000/' \ | xxd -p -r \ > "_new_$line"; done
これで全部のファイルに対して実行が完了したので、置換前のファイルを消して、置換後のファイルの名前を変える。 自分はエクスプローラーで古いファイルを削除、mmv コマンドで Vim の矩形選択でよきにした。 CLI でやるならこうやったかなぁ(未検証)
# 消す ls -1 | grep ".mp3" | grep -v "_new_" | rm # リネーム ls -1 | grep ".mp3" | xargs -i mv {} "$(echo {} | sed -r -e 's/_new_//g')"
おわりに
mp3 のメタ情報について詳しくなった。 詳しくなる必要があったかはわからないけど、多分なかった。
とはいえ、シェルでバイナリファイルを触ったことがあまりなかったから、いい経験になった。
xxd
を使えばこれまでの文字列とほぼ同じように扱えた。
ただ自動的に改行が入ってしまって、それをまたぐと sed
が効かなかったりしたので、若干のハマりどころがあるっぽい。