自动CD唱片存入电脑程序浅谈
自动CD唱片存入电脑程序浅谈
由于版权问题在网上听高音质音乐都收费,自己家里正版CD唱片出外听不到,加上有时不小心搞到CD唱片损坏,
那就惨重,这就有需要保存在电脑里。如果一个一个输入命令去抓取,那就影响效率了,所以需要编写一个运行脚位去自动抓取了,这是一个运行脚位程序,是用perl语言编写的,调用slackware linux系统的cdparanoia的CD
抓取命令和flac命令将wav格式改为无损格式flac,用flac格式可以减小空间解压时不影响音质。这个比较好选择。
程序如下:
#! /usr/bin/perl -w -I .
open(STDIN, "< /dev/null");
open(STDOUT, "> /dev/null");
open(STDERR, "> /dev/null");
fork && exit;
my $cdparanoia = `which cdparanoia 2>/dev/null` || "/usr/bin/cdparanoia";
my @cdparanoia_opts = ();
my $encoder = "/usr/bin/flac";
my @encoder_opts = qw/-m 4 -q 6 /;
my $encoder_ext = "flac";
my $eject = "/usr/bin/eject";
my $flacdir = "/usr/local/share/music";
my $tmpdir = "/tmp";
my @cddab_server_list = (
"127.0.0.1:8880",
);
chomp($cdparanoia);
chomp($encoder);
use MPEG::MP3Info;
use Getopt::Long;
$| = 1;
print <
License: GPL
Author: jmayer\@earthling.net
Simple five-step usage guide:
1. configure by editing $0
2. run $0
3. Drop audio CD into the CD-ROM tray.
4. Do something else until autorip ejects your CD.
5. Repeat steps 3-4 until CD supply is exhausted.
HEADER
my $justid = 0;
my $ret = GetOptions ('info', \$justid);
sub escape
{
my $a = shift;
# massage the data:
$a =~ s/[^a-zA-Z0-9\-]+/_/g;
# whee!
$a;
}
my $tmpwav = $tmpdir . "/tmp.wav";
while (1) {
# wait for a cd:
print "Waiting for next disk...\n";
{
my $gotcd = 0;
while (!$gotcd) {
system("$cdparanoia -qQ &> /dev/null");
if (($? >> 8) == 0) { $gotcd = 1; }
else { sleep(2); }
}
}
my $info = CDDB::get();
if (!defined($info->{DTITLE})) {
print "ERROR: Could not identify disk!\n";
system($eject);
sleep(2);
next;
}
my ($artist,$title) = split(/\s+\/\s+/, $info->{DTITLE});
my @track = ();
while (my($key,$val) = each %$info) {
if ($key =~ m/^TTITLE(\d+)/) {
$track[$1] = $val;
}
}
print "-" x 75,"\n";
print "Title: $title\n";
print "Artist: $artist\n";
print "Tracks:\n\to ",join("\n\to ",@track),"\n\n";
if ($justid) {
system($eject);
next;
}
unlink($tmpwav);
my $subdir = escape($artist) . "-" . escape($title);
my $dir = $tmpdir."/".$subdir;
system ("mkdir -p $dir");
my @tracks_pending = ( 1 .. ($#track+1) );
my @encodes_pending = ();
my $rip_pid = undef;
my $enc_pid = undef;
my $ripping = undef;
my $started_rip = time();
while(1) {
if (@tracks_pending && ($#encodes_pending < 1) && !defined($rip_pid))
{
my $track = shift(@tracks_pending);
$rip_pid = rip($track);
$ripping = $track;
}
if (@encodes_pending && !defined($enc_pid))
{
my $track = shift(@encodes_pending);
my $tmpwav = $tmpdir . "/tmp.$$.$track.wav";
my $mp3file = sprintf ("%s/%02d-%s.%s", $dir, $track,
escape($track[$track-1]), $encoder_ext);
$enc_pid = enc($tmpwav, $mp3file,
$track, $track[$track-1], $artist, $title);
}
last if ((!defined($enc_pid)) && (!defined($rip_pid)));
my $pid = wait();
if (defined($enc_pid) && $pid == $enc_pid) {
$enc_pid = undef;
}
if (defined($rip_pid) && $pid == $rip_pid) {
push (@encodes_pending, $ripping);
$ripping = undef;
$rip_pid = undef;
}
}
my $dur_rip = time() - $started_rip;
my $min_rip = int($dur_rip / 60);
my $sec_rip = $dur_rip % 60;
print "Files moved to: ${oggdir}/${subdir}\n";
print "Finished disk (time elapsed: $min_rip:$sec_rip)\n";
system ("mv $dir ${oggdir}/${subdir}");
system ($eject);
print "\n";
}
sub rip
{
my $track = shift;
my $tmpwav = $tmpdir . "/tmp.$$.$track.wav";
my $pid = fork();
return $pid if ($pid);
# I'm a child:
my $cmd = "$cdparanoia -qw $track $tmpwav";
print "Started ripping track #$track\n";
my $start =time();
system ("$cmd");
my $dur = time() - $start;
print "Finished ripping track #$track ($dur secs)\n";
exit(0);
}
sub enc
{
my $inputwav = shift || die;
my $outputmp3 = shift || die;
my ($tracknum, $track, $artist, $title) = (shift,shift,shift,shift);
my $pid = fork();
return $pid if ($pid);
# I'm a child:
print "Started encoding $track\n";
my $start = time();
if ($encoder_ext eq 'mp3') {
# mp3
system ("$encoder @encoder_opts $inputwav \"${outputmp3}\" 2>&1 > /dev/null");
set_mp3tag ($outputmp3,
$track,
$artist,
$title,
"",
"",
"",
int($tracknum));
my $dur = time()-$start;
} else {
# ogg vorbis
system ($encoder, @encoder_opts,
"-o", $outputmp3,
"-t", $track,
"-l", $title,
"-a", $artist,
$inputwav);
}
unlink($inputwav);
my $dur = time()-$start;
print "Finished encoding ${outputmp3} ($dur secs)\n";
exit(0);
}
exit(0);
package CDDB;
use FileHandle;
use Data::Dumper;
use IO::Socket;
sub cddb_sum
{
my $n = shift;
my $ret = 0;
while ($n > 0) {
$ret += ($n % 10);
$n = int($n / 10);
}
return $ret;
}
sub cddb_discid
{
my $offset = shift;
my $length = shift;
# print Data::Dumper->Dump([ $offset, $length ]);
my $t = 0;
my $n = 0;
my $ref;
my $track = 1;
my @o = ();
foreach $ref (@$offset)
{
my $this_off = $ref->[0] * 60 + $ref->[1] + 2;
# $this_off *= 75;
# print STDERR "$track: $this_off\n"; $track++;
$n = $n + cddb_sum( $this_off );
push (@o, $this_off * 75);
}
my $lastoff =$offset->[$#$offset]->[3];
my $lastlen =$length->[$#$length]->[3];
$t = $lastoff + $lastlen;
# print STDERR "$lastoff + $lastlen: t = $t / 75...\n";
$t = int($t / 75);
# $t = $t & 0xFFFFFF;
# print STDERR sprintf("n=%x(%d) t=%x(%d)\n",$n,$n,$t,$t);
my $id = (($n & 0xFF) << 24 | $t << 8 | ($#$length+1));
return sprintf("%08x",$id), ($#$length+1), @o, $t;
}
sub get_toc_cdparanoia
{
my $ripper = shift;
# print STDERR "Running $ripper:\n";
my $fh = new FileHandle("$ripper -Q 2>&1 |");
my @length = ();
my @offset = ();
while (defined($_ = <$fh>)) {
if ( /(\d+)\s+\[(\d+):(\d+)\.(\d+)\].+?(\d+)\s+\[(\d+):(\d+)\.(\d+)\]/ ) {
# print STDERR $_;
push (@length, [ $2, $3, $4, $1 ]); # min, sec, frame, $tracks
push (@offset, [ $6, $7, $8, $5 ]); # min, sec, frame, $tracks
}
}
$fh->close();
# print STDERR "done with $ripper\n";
return \@length, \@offset;
}
sub get_cddb
{
my @id = @_;
my $status;
my %entry;
my $server;
foreach $server (@cddb_server_list)
{
($status, %entry) = get_cddb_entry ($server, @id);
last if ($status != 0);
}
return \%entry;
}
sub myprint
{
my $fh = shift;
# print STDERR @_;
print $fh @_;
}
sub get_cddb_entry
{
my $server = shift;
my @id = @_;
my $sock = new IO::Socket::INET (
PeerAddr => $server,
Proto => 'tcp') || return 0;
$_ = <$sock>; # print STDERR $_;
return 0 unless (m/^2/);
#my $user = $ENV{USER};
my $user = 'root';
my $hostname = `hostname`; chomp($hostname);
myprint ($sock, "cddb hello $user $hostname CDDB.pm 0.1\n");
$_ = <$sock>; # print STDERR $_;
return 0 unless (m/^2/);
myprint ($sock, "cddb query @id\n");
$_ = <$sock>; print STDERR $_;
return 0 if (m/^202/); # no match for disc
return 0 unless (m/^2/);
my $cat; my $real_id;
if (m/^200\s+(\S+)\s+(\S+)/) {
$cat = $1; $real_id = $2;
} else {
$_ = <$sock>; # print STDERR $_;
return 0 unless (m/^(\S+)\s+(\S+)/);
$cat = $1; $real_id = $2;
while (defined($_ = <$sock>))
{ # print STDERR $_;
last if (m/^\./);
}
}
print STDERR "found real id: $cat $real_id\n";
myprint ($sock,"cddb read $cat $real_id\n");
$_ = <$sock>; # print STDERR $_;
return 0 unless (m/^2/);
my $info = {};
while (defined ($_ = <$sock>)) { # print STDERR $_;
last if (m/^\./);
next if (m/^#/);
if (m/^(\S+?)=(.+)/) {
$info->{$1} = $2;
$info->{$1} =~ s/\s*$//;
}
}
myprint ($sock,"quit\n");
$sock->close();
return (1, %$info);
}
sub get
{
my ($len, $off) = get_toc_cdparanoia($cdparanoia);
my @id = cddb_discid($off, $len);
my $info = get_cddb(@id);
return $info;
}
sub test
{
my ($len, $off) = get_toc_cdparanoia($cdparanoia);
my @id = cddb_discid($off, $len);
print "Disc ID: @id\n";
my $info = get_cddb(@id);
print Data::Dumper->Dump([ $info ]),"\n";
}