#!/usr/bin/env perl # # (c) 2002-2007 by Dirk Jagdmann # This software is provided 'as-is', without any express or implied # warranty. In no event will the authors be held liable for any damages # arising from the use of this software. # # Permission is granted to anyone to use this software for any purpose, # including commercial applications, and to alter it and redistribute it # freely, subject to the following restrictions: # # 1. The origin of this software must not be misrepresented; you # must not claim that you wrote the original software. If you use # this software in a product, an acknowledgment in the product # documentation would be appreciated but is not required. # # 2. Altered source versions must be plainly marked as such, and # must not be misrepresented as being the original software. # # 3. This notice may not be removed or altered from any source # distribution. use strict; use Audio::Wav; use MP3::Info; use_winamp_genres(); use Ogg::Vorbis::Header; my $recursion=0; my $mode="j"; # joint stereo my $bitrate=128; my $encode=0; my $totaltime=0; help() if $#ARGV < 0; parseoptions(\@ARGV); process(@ARGV); exit 0; sub parseoptions { my ($a) = @_; while ($_=shift @$a) { if ($_ eq "-m") { $mode="m"; next; } elsif ($_ eq "-s") { $mode="j"; next; } elsif ($_ eq "-r") { $recursion=1; next; } elsif ($_ eq "--encode") { $encode=1; next; } elsif (/^\d+$/) { $bitrate=$_; next; } elsif (/^-(\d+)$/) { $bitrate=$1; next; } # no parameters anymore else { unshift @$a, $_; last; } } } sub process { my @arg=@_; print "bitrate\tchannel\tfreq\ttime\tfilename\n" unless $encode; foreach my $old (@arg) { if (-d $old && $recursion) { chdir $old or next; print "---------> entering: $old\n"; process(glob("*")); chdir ".." or die "could not enter .."; print "---------> leaving: $old\n"; next; } next unless (-f $old); if (!$encode) { printinfo($old); } elsif (checkencode($old)) { encode($old) or die "could not encode $old"; } } if ($totaltime) { my $sec=$totaltime%60; $totaltime=int($totaltime/60); my $min=$totaltime%60; $totaltime=int($totaltime/60); print sprintf("totaltime %i:%02i:%02i\n", $totaltime, $min, $sec); } } #use Data::Dumper; sub printinfo { my ($fn) = @_; if($fn =~ /\.mp3$/i) { my $info=get_mp3info($fn); print $info->{BITRATE}*1000; print "\t"; print "stereo" if $info->{STEREO}; print "mono" unless $info->{STEREO}; print "\t$info->{FREQUENCY}\t$info->{TIME}\t$fn\n"; $info->{TIME} =~ /(\d+):(\d+)/; $totaltime+=$1*60+$2; } elsif($fn =~ /\.wav$/i) { eval { my $wav=new Audio::Wav; my $read=$wav->read($fn); my $details=$read->details(); # print Data::Dumper->Dump( [$details] ); print $details->{bytes_sec}*8 . "\t"; # bitrate if($details->{channels} == 1) { print "mono\t"; } elsif($details->{channels} == 2) { print "stereo\t"; } else { print $details->{channels} . "\t"; } print $details->{sample_rate} . "\t"; my $sec=int($details->{length}); $totaltime+=$sec; my $min=int($sec/60); $sec%=60; printf '%02i:%02i', $min, $sec; print "\t$fn\n"; }; if($@) { print "error\t\t\t\t$@\n"; } } elsif($fn =~ /\.ogg$/i) { my $ogg = Ogg::Vorbis::Header->new($fn); my $info=$ogg->info(); print $info->{bitrate_nominal} . "\t"; my $c=$info->{channels}; if($c == 1) { print "mono" } elsif($c == 2) { print "stereo" } else { print $c }; print "\t"; print $info->{rate} . "\t"; my $sec=int($info->{length}); $totaltime+=$sec; my $min=int($sec/60); $sec=$sec%60; print "$min:$sec\t"; print "$fn\n"; } } sub checkencode { return 0 unless $encode; my ($old) = @_; return 1 if $old =~ /\.wav$/i; return 1 if $old =~ /\.mpc$/i; return 1 if $old =~ /\.ogg$/i; return 1 if $old =~ /\.wma$/i; if ($old =~ /\.mp3$/i) { my $info=get_mp3info($old); return ($info->{BITRATE} > $bitrate) || ($info->{FREQUENCY}>44.1); } return 0; } sub encode { my ($old) = @_; return wav2mp3($old) if $old =~ /\.wav$/i; return mp32mp3($old) if $old =~ /\.mp3$/i; return mpc2mp3($old) if $old =~ /\.mpc$/i; return ogg2mp3($old) if $old =~ /\.ogg$/i; return wma2mp3($old) if $old =~ /\.wma$/i; return 0; } sub mp32mp3 { my ($filename) = @_; my $info=get_mp3info($filename); my $cmd="lame -h -b $bitrate -m $mode "; my $tag=get_mp3tag($filename); my $title=join(' ', $tag->{TITLE}); $title =~ s/"//g; #" $cmd.="--tt \"$title\" " if $title; my $genre=join(' ', $tag->{GENRE}); $genre = 'Other' if $genre =~ /Miscellaneous|Podcast|Hörspiel|Hörspiel/i; $cmd.="--tg \"$genre\" " if $genre; my $artist=join(' ', $tag->{ARTIST}); $artist =~ s/"//g; #" $cmd.="--ta \"$artist\" " if $artist; my $album=join(' ', $tag->{ALBUM}); $album =~ s/"//g; #" $cmd.="--tl \"$album\" " if $album; $tag->{YEAR}+=0; $cmd.="--ty \"$tag->{YEAR}\" " if $tag->{YEAR}; my $comment=join(' ', $tag->{COMMENT}); $comment =~ s/"//g; #" $cmd.="--tc \"$comment\" " if $comment; $tag->{TRACK}+=0; $cmd.="--tn \"$tag->{TRACK}\" " if $tag->{TRACKNUM}; $cmd.="--id3v1-only "; $cmd.="--resample 44.1 " if $info->{FREQUENCY} > 44.1; my $c="$cmd \"$filename\" \"$filename.new\""; my $r=system($c)/256; if ($r>0) { print "error while encoding $filename\n"; return 0; } rename($filename, "$filename.old") or die "could not rename $filename to $filename.old"; rename("$filename.new", $filename) or die "could not rename $filename.new to $filename"; return 1; } sub mpc2mp3 { my ($old) = @_; my $new=$old; $new =~ s/\.mpc$/\.mp3/i; my $tmp="/tmp/$$.wav"; print "analyzing $old\n"; my $cmd="mppdec \"$old\" $tmp 2>&1"; my $out=`$cmd`; # search for scale value $out =~ /--scale\s+([\d\.]+)\b/; my $scale=$1; if ($scale>0.0) { print "found scaling factor $scale\n"; # decode again $cmd="mppdec --scale $scale \"$old\" $tmp"; my $r=system($cmd)/256; if ($r>0) { print "error while decoding $old\n"; unlink $tmp; return 0; } } # get tags $cmd="apetag \"$old\""; my %tag; open(A, "$cmd |") or die "could not $cmd"; while () { chomp; /(.+?):(.+)/; my $key=lc $1; $tag{$key}=$2; } close(A); $cmd="lame -h -b $bitrate -m $mode $tmp \"$new\" "; $tag{title} =~ s/"//g; #" $cmd.="--tt \"$tag{title}\" " if $tag{title}; $tag{artist} =~ s/"//g; #" $cmd.="--ta \"$tag{artist}\" " if $tag{artist}; $tag{album} =~ s/"//g; #" $cmd.="--tl \"$tag{album}\" " if $tag{album}; $tag{year}+=0; $cmd.="--ty \"$tag{year}\" " if $tag{year}; $tag{comment} =~ s/"//g; #" $cmd.="--tc \"$tag{comment}\" " if $tag{comment}; $cmd.="--tg \"$tag{genre}\" " if $tag{genre}; $tag{track}+=0; $cmd.="--tn \"$tag{track}\" " if $tag{track}; $cmd.="--id3v1-only "; # encode my $r=system($cmd)/256; if ($r>0) { print "error while encoding $new\n"; unlink $tmp; return 0; } unlink $tmp; return 1; } sub wav2mp3 { my ($old) = @_; my $new=$old; $new =~ s/\.wav$/\.mp3/i; my $cmd="lame -h -b $bitrate -m $mode \"$old\" \"$new\""; my $r=system($cmd)/256; if ($r>0) { print "error while encoding $old\n"; return 0; } return 1; } sub wma2mp3 { my ($old) = @_; my $wav=$old; $wav =~ s/\.wma$/\.wav/i; my $cmd="mplayer -vc null -vo null -ao pcm:fast -ao pcm:waveheader '$old'"; print "$cmd\n"; my $r=system($cmd)/256; if ($r>0) { print "error while converting $old -> $wav\n"; return 0; } rename("audiodump.wav", $wav); my $r=wav2mp3($wav); unlink $wav; return $r; } sub ogg2mp3 { my ($old) = @_; my $new=$old; $new =~ s/\.ogg$/\.mp3/i; my $ogg = Ogg::Vorbis::Header->new($old); my $info=$ogg->info(); if($info->{channels}==2 && $mode eq 'm') { $mode="s -a" } my $cmd="oggdec -Q -o - -b 16 -e 0 -R -s 1 \"$old\" | lame -r -h -x -s $info->{rate} --bitwidth 16 -b $bitrate -m $mode - \"$new\""; # TODO: convert ogg tags to id3v1 print "$cmd\n"; my $r=system($cmd)/256; if ($r>0) { print "error while converting $old -> $new\n"; return 0; } return 1; } sub help { print "mp3recode.pl [-m] [-s|-j] [-000] [--encode] \n"; print "-m encode to mono\n"; print "-s encode to joint-stereo\n"; print "-000 bitrate to encode\n"; print "--encode give this switch to start reencoding\n"; print "-r recurse into subdirectories\n"; exit 1; }