flac+cue

Windows環境においてはcuesheetを利用した1CD1ファイルでの音楽ファイル管理がマニアの間では半ば常識となっている感があるけど、その流れを作ったEACfoobar2000のようなソフトが無いのでLinuxではなかなか難しい。というわけで、とりあえず作成の方法を模索してみようと思う。
CDから1つのwavファイルを作るのはcdda2wavやcdparanoiaで何も問題無いので、問題はcueファイルの作成だ。cuegen*1を知っていたので、これで枠組を作ってからruby-freedb*2で取得したトラックタイトルなどの情報を追加すれば完璧であろう、とはじめに考えた。
しかし、情報を集めていると、cuesheetを使っての音楽管理の利点は再生よりもむしろ、全く同じ形でCDに焼き直せることであり、プリギャップ*3の保存こそが最も重要なことらしい。前述のcuegenではINDEX 01のみでINDEX 00は作ってくれないので、これで作ったcuesheetからCDを焼き直した場合、プリギャップについての情報は完全に失われることになる。というわけでcuegenの使用はやめて、cuesheetをINDEX 00込みで作ってくれるソフトを探すことになった。
余談だが、そもそもcuesheetとはCDRWINがCDのトラック情報を記述するために使ったファイルであったのが、CDイメージのマウントとかそういうので対応するソフトが増えていくうちにデファクトスタンダードになっていったものである。だからかどうか知らないが、Windows以外の環境では対応しているソフトウェアがかなり少ない。
プリギャップを含んだcuesheetを直接吐くソフトウェアは見付からなかったが、試行錯誤しているうちにcdrdaoの生成したtoc*4ファイルをcdrdaoパッケージに含まれるtoc2cueに読ませれば、INDEX 00を使ってプリギャップを保存してくれることがわかった。

cdrdao read-toc --device /dev/cdrom hoge.toc

で問題なし。あとは

toc2cue hoge.toc hoge.cue

とすれば、

$ cat hoge.cue
FILE "data.wav" BINARY
  TRACK 01 AUDIO
    INDEX 01 00:00:00
  TRACK 02 AUDIO
    INDEX 01 00:45:05
  TRACK 03 AUDIO
    INDEX 01 03:52:17
  TRACK 04 AUDIO
    INDEX 00 08:05:12
    INDEX 01 08:06:42
  TRACK 05 AUDIO
    INDEX 00 11:15:40
    INDEX 01 11:16:70

見ての通りINDEX 00を含むcuesheetが作られる。
プリギャップを含むtocファイルを作るにはCD全体の走査が必要らしいので、実用を考える場合はCDのデータの取り込みも一緒にした方が良いだろう。

cdrdao read-cd --device /dev/cdrom hoge.toc

とするだけで、tocファイルだけでなくCDの内容の完全なコピーであるdata.binファイルが作成される。
さて、binファイルのままでは再生出来ないので、wavファイルに変換しなければならない。検索してみると、bchunk*5を見つけた。cuesheetからトラック情報を読んでトラックごとに分割するプログラムのようだが、1つのwavファイルにしたいならTRACK 01しかないcueファイルを作って1トラックのみだと思い込ませれば良い。そのまま

bchunk -w data.bin dummy.cue data

とするとノイズだけの砂嵐になったので、

If you only get loud noise on the tracks, try either -swab on cdrecord or -s on bchunk to swap the byte order.

とある通りに-sオプションを付けることで成功。
このbinからwavへの変換はわりとよくありそうな作業なのでbin2wav.shを書いて自動化する。

#!/bin/sh
CUE="/tmp/$$.cue"
cat <<EOS > "$CUE"
FILE "data.bin" DATA
  TRACK 01 AUDIO
    INDEX 01 00:00:00
EOS
bchunk -s -w "$1" "$CUE" "${1%.*}"
mv "${1%.*}01.wav" "${1%.*}.wav"
rm "$CUE"

ここまででwavファイルもcuesheetも揃ったのであとはエンコードするだけだが、せっかくなのでcuesheetのファイル名を引数に受け取って、それにcddbから得た情報を付けて標準出力に吐くRubyスクリプトも書いてみた。

#!/usr/bin/ruby
require "freedb"
freedb = Freedb.new
freedb.fetch_cgi("freedbtest.dyndns.org", 80, nil, nil, "/~cddb/cddbeuc.cgi") || die
freedb.get_result freedb.results.first.split.first
text = "REM GENRE \"#{freedb.genre}\"\n"
text << "REM DATE \"#{freedb.year}\"\n"
text << "REM DISCID \"#{freedb.discid.upcase}\"\n"
text << "TITLE \"#{freedb.title}\"\n"
text << "PERFORMER \"#{freedb.artist}\"\n"
open(ARGV.first,"r") {|f| text << f.read}
text.gsub!(/( *)TRACK (\d{2}).*$/){"#{$&}\n#{$1*2}TITLE \"#{freedb.tracks[$2.to_i-1]["title"]}\"\n#{$1*2}PERFORMER \"#{freedb.artist}\""}
open(ARGV.first,"w") {|f| f.puts text}

エラー処理などは全くしてないが、

$ cat hoge.cue
FILE "data.wav" BINARY
  TRACK 01 AUDIO
    INDEX 01 00:00:00
  TRACK 02 AUDIO
    INDEX 01 00:45:05
  TRACK 03 AUDIO
    INDEX 01 03:52:17
  TRACK 04 AUDIO
    INDEX 00 08:05:12
    INDEX 01 08:06:42
  TRACK 05 AUDIO
    INDEX 00 11:15:40
    INDEX 01 11:16:70
$ ruby hage.rb hoge.cue
REM GENRE "JPop"
REM DATE "2003"
REM DISCID "3F03A305"
TITLE "日本ブレイク工業 社歌"
PERFORMER "萬Z(量産型)"
FILE "data.wav" BINARY
  TRACK 01 AUDIO
    TITLE "ナレーション"
    PERFORMER "萬Z(量産型)"
    INDEX 01 00:00:00
  TRACK 02 AUDIO
    TITLE "日本ブレイク工業 社歌"
    PERFORMER "萬Z(量産型)"
    INDEX 01 00:45:05
  TRACK 03 AUDIO
    TITLE "希望の丘陵 (おか)"
    PERFORMER "萬Z(量産型)"
    INDEX 01 03:52:17
  TRACK 04 AUDIO
    TITLE "日本ブレイク工業 社歌 カラオケ"
    PERFORMER "萬Z(量産型)"
    INDEX 00 08:05:12
    INDEX 01 08:06:42
  TRACK 05 AUDIO
    TITLE "希望の丘陵 (おか) カラオケ"
    PERFORMER "萬Z(量産型)"
    INDEX 00 11:15:40
    INDEX 01 11:16:70

と言った感じに動作する。
あとはまあ適当に

$ flac --cuesheet=hoge.cue -o output.flac data.wav

を叩き台に色々付け加えれば良いわけです。