CDイメージのロスレス圧縮とmp3エンコード手順

  1. CDをドライブに入れて、EACで「アクション - CDイメージをコピーしCUEシートを作成」で hoge.wav + hoge.cue を作る。cueファイルにタグ情報も含まれるが、年は入らない。
  2. WavPackhoge.wavを hoge.wv に変換する。この状態でもfoobar2kで(compornentがあれば)聴ける。
  3. acdir で hoge.wv+hoge.cue をトラックごとのmp3に変換する。

必要なもの

コマンドライン詳細

…毎回叩くの面倒そうだったんでperlスクリプトにしますた。

#!perl -w
use strict;
use warnings;
use Getopt::Long;

############
# オプション類

our $verbose=0;
our $lame    = 'F:\\oyama\\bin\\razorlame111\\lame.exe --preset extreme --athaa-sensitivity 9 --lowpass 20 --lowpass-width 1 --add-id3v2 --pad-id3v2 --id3v2-only';
our $acdir   = 'F:\\oyama\\bin\\acdir-0.12\\acdir.exe';
our $wavpack = 'F:\\oyama\\bin\\wavpack\\wavpack.exe -h -m -q';
our $flac    = '"D:\\Program Files\\FLAC\\flac.exe" -s -6 -V ';

sub usage(@){
	@_ and warn join("\n",@_),"\n";
	die <<"END";
usage:
   $0 wv   *.wav \n\t.wav => .wv   (using wavpack)
   $0 flac *.wav \n\t.wav => .flac (using flac)
   $0 v2m  *.cue \n\t.cue + .wv   => .mp3 (using acdir,lame)
   $0 f2m  *.cue \n\t.cue + .flac => .mp3 (using acdir,lame)

options:
	--acdir <string>  \n\tset path and option. default: $acdir
	--lame <string>   \n\tset path and option. default: $lame
	--wavpack <string>\n\tset path and option. default: $wavpack
	--flac <string>   \n\tset path and option. default: $flac
END
}

sub parse_options(){
	GetOptions(
		"verbose:+",\$verbose,
		"wavpack=s",\$wavpack,
		"acdir=s"  ,\$acdir,
		"flac=s"   ,\$flac,
		"lame=s"   ,\$lame,
	) or usage "bad options.\n";
}

############

sub cmd($){
	my($cmd)=@_;
	warn "## $cmd\n";
	system $cmd;
	return 1 if $?==0;
	  if( $? == -1  ){ warn          "NG failed to execute: $!\n"; }
	elsif( $? & 127 ){ printf STDERR "NG child died with signal %d, %s coredump\n",($? & 127),  ($? & 128) ? &#39;with&#39; : &#39;without&#39;; }
	else             { printf STDERR "NG child exited with value %d\n", $? >> 8; }
	return 0;
}

our $count=0;
sub findfiles($$$){
	my($mask,$rList,$callback)=@_;
	for(@ARGV){
		for (glob (-d $_)?"$_\\$mask":$_){
			eval{ $callback->($_); };  $@ and warn $@;
			++$count;
		}
	}
}

sub wav_compress($$$){
	my($action,$rList,$ext,$exec)=@_;
	findfiles("*.wav",$rList,sub{
		my($src)=@_;
		my $dst=$src; $dst =~ s/\.wav$/\.$ext/;
		my $cue=$src; $cue =~ s/\.wav$/\.cue/;
		# 両方ある時は処理しない
		(-e $dst) and die "?? both $src and .$ext exists\n";
		# *.cue があれば--cuesheetオプションを付ける
		$cue = (-e $cue )?qq( --cuesheet "$cue"):&#39;&#39;;
		($ext eq &#39;flac&#39;) or $cue=&#39;&#39;; # flacのみ対応
		# 変換してみる
		(cmd qq($exec$cue "$src")) and die "OK convert $src to .$ext\n";
		# 失敗したら出力ファイルを消してみる
		if(-e $dst ){ unlink($dst) or warn "NG cannot delete $dst :$!\n"; }
	});
}

sub call_acdir{
	my($action,$rList,$srcext)=@_;
	my $tmp="tmp.cue";
	findfiles("*.cue",$rList,sub{
		my($cue)=@_;
		# 拡張子$srcext のファイル
		my $src=$cue; $src =~ s/\.cue$/\.$srcext/;
		# 存在確認
		(-f $cue ) or die "NG $cue is not regular file\n";
		(-f $src ) or die "NG $src is not regular file\n";
		warn qq(__ found "$cue" "$src"\n);

		# cue ファイルを一部変更する
		open(IN ,$cue)    or die "NG $cue $!\n";
		open(OUT,">$tmp") or die "NG $tmp $!\n";
		binmode IN;
		binmode OUT;
		while(<IN>){
			s/^FILE "([^"]*)" WAVE/FILE "$src" WAVE/;
			print OUT;
		}
		close(IN)  or die "NG $cue $!\n";
		close(OUT) or die "NG $tmp $!\n";

		#acdirを実行する
		my $cmd=qq($acdir --output "\$~a-\$~T-\$n\$~t.mp3" --pipe "$lame \$x --tt \$#t --ta \$#a --tl \$#T --tn \$#n - \$#o" $tmp);
		(cmd $cmd) and die "OK.\n";
	});
	if(-e $tmp){ (unlink $tmp) or warn "NG unlink $tmp :$!\n";}
}

############

if(not caller){
	parse_options;
	@ARGV or usage;
	my $action = lc shift;
	for(
		[&#39;wv&#39;   ,\&wav_compress,&#39;wv&#39;  ,$wavpack],
		[&#39;flac&#39; ,\&wav_compress,&#39;flac&#39;,$flac   ],
		[&#39;v2m&#39;  ,\&call_acdir  ,&#39;wv&#39;  ],
		[&#39;f2m&#39;  ,\&call_acdir  ,&#39;flac&#39;],
	){
		my($act,$func,@extra)=@$_;
		if($act eq $action){
			$func->($act,\@ARGV,@extra);
			warn "__ $count files proccessed.\n";
			exit;
		}
	}
	usage "NG unsupported action $action\n";
}