#!/usr/bin/perl -w # Convert a set of input files to MP#, filling MP3 ID tags # Copyright 2001, Aaron Birenboim, aaron@boim.com # You are free to modify and distribute this at will, as long # as the above credit stays in tact. ##################################################################### &parseArgs(@ARGV); # get inputs into globals for ($i=0;$i<=$#inFile;$i++) { @cmd = ("nice","-20","lame","-h","-v", "-V" ,$qual[$i], "-b" ,$minRate[$i]); @cmd = (@cmd,"--ty",$year[$i]) if (&inRange($year[$i],1800,2100)); @cmd = (@cmd,"--tn",$trackNo[$i])if (&inRange($trackNo[$i],1,99)); @cmd = (@cmd,"--tt",$title[$i]) if ($title[$i] ne "UNKNOWN"); @cmd = (@cmd,"--ta",$artist[$i]) if ($artist[$i] ne "UNKNOWN"); @cmd = (@cmd,"--tl",$album[$i]) if ($album[$i] ne "UNKNOWN"); @cmd = (@cmd,"--tc",$comment[$i])if ($comment[$i] ne "NONE"); @cmd = (@cmd,$inFile[$i],$outFile[$i]); print '-------------------',`date`,join(' ',@cmd,"\n"); system(@cmd); chmod 0666,$outFile[$i]; } print '-------------------',`date`,"******** Done.\n"; ##################################################### sub inRange { local ($val,$lo,$hi) = @_; return 0 if ($val < $lo); return 0 if ($val > $hi); return 1; } sub parseArgs { local $tmpNam = sprintf("/tmp/wav2mp3_%s-%d.cfg",$ENV{'USER'},$$); &writeDefaultCfg($tmpNam,@_); # allow user to edit file local $editor = defined($ENV{"EDITOR"}) ? $ENV{"EDITOR"} : "pico"; local $cmd = defined($ENV{"DISPLAY"}) ? "xterm -e " : ""; $cmd .= " $editor $tmpNam"; print $cmd,"\n"; system($cmd); &readCfg($tmpNam); # makes globals with all settings. #unlink $tmpNam; } sub readCfg { local ($nam) = @_; open(F,"< $nam") or die "Could not open \"$nam\" for read.\n"; local $Year = 9999; local $Title = "UNKNOWN"; local $Artist = "UNKNOWN"; local $Album = "UNKNOWN"; local $Comment = "NONE"; local $Qual = 1; local $MinRate = 112; local $AppendArtist = 1; local $AppendAlbum = 1; local $OutBase = "./"; local $i = -1; local @outBase; local @appendArtist; local @appendAlbum; while () { chomp; s/#.*//; s/^\s*//; s/\s+$//; #print "Processing <$_>\n"; if (m/^InFile/) { $i++; # set defaults for new file entry #print "File entry $i\n"; $inFile[$i] = "UNKNOWN"; $year[$i] = $Year; $trackNo[$i] = 0; $title[$i] = $Title; $artist[$i] = $Artist; $album[$i] = $Album; $comment[$i] = $Comment; $qual[$i] = $Qual; $minRate[$i] = $MinRate; $outBase[$i] = $OutBase; $appendArtist[$i] = $AppendArtist; $appendAlbum[$i] = $AppendAlbum; } if ($i < 0) { # no files yet. these must be default settings $Year = &parseLine("Year",$Year,$_); $Title = &parseLine("Title",$Title,$_); $Artist = &parseLine("Artist",$Artist,$_); $Album = &parseLine("Album",$Album,$_); $Comment = &parseLine("Comment",$Comment,$_); $Qual = &parseLine("CompressionQuality",$Qual,$_); $OutBase = &parseLine("OutputDirectoryBase",$OutBase,$_); $MinRate = &parseLine("MinRate",$MinRate,$_); $AppendArtist = &parseBoolean("AppendArtist",$AppendArtist,$_); $AppendAlbum = &parseBoolean("AppendAlbum",$AppendAlbum,$_); } else { $inFile[$i] = &parseLine("InFile",$inFile[$i],$_); $year[$i] = &parseLine("Year",$year[$i],$_); $trackNo[$i] = &parseLine("TrackNumber",$trackNo[$i],$_); $title[$i] = &parseLine("Title",$title[$i],$_); $artist[$i] = &parseLine("Artist",$artist[$i],$_); $album[$i] = &parseLine("Album",$album[$i],$_); $comment[$i] = &parseLine("Comment",$comment[$i],$_); $qual[$i] = &parseLine("CompressionQuality",$qual[$i],$_); $outBase[$i] = &parseLine("OutputDirectoryBase",$outBase[$i],$_); $minRate[$i] = &parseLine("MinRate",$minRate[$i],$_); $appendArtist[$i] = &parseBoolean("AppendArtist",$appendArtist[$i],$_); $appendAlbum[$i] = &parseBoolean("AppendAlbum",$appendAlbum[$i],$_); } } close F; # construst output filename, and create directory if necessary for ($i=0;$i<=$#inFile;$i++) { $appendArtist[$i] = 0 if ($artist[$i] eq "UNKNOWN"); $appendAlbum[$i] = 0 if ($album[$i] eq "UNKNOWN"); $outFile[$i] = $outBase[$i]; -d $outFile[$i] or mkdir $outFile[$i],0755; $outFile[$i] .= &safeFileName($artist[$i]) . "/" if $appendArtist[$i]; -d $outFile[$i] or mkdir $outFile[$i],0755; $outFile[$i] .= &safeFileName($album[$i]) . "/" if $appendAlbum[$i]; -d $outFile[$i] or mkdir $outFile[$i],0755; $outFile[$i] .= sprintf("%02d_",$trackNo[$i]) if ($trackNo[$i] > 0); # set base name to input name if no title was given #print "in<$inFile[$i]> tit<$title[$i]> out<$outFile[$i]>\n"; $outFile[$i] .= ($title[$i] eq "UNKNOWN") ? &baseName($inFile[$i]) : &safeFileName($title[$i]); $outFile[$i] .= ".mp3"; } } sub writeDefaultCfg { local ($tmpNam,@files) = @_; open(F,"> $tmpNam") or die "Could not open \"$tmpNam\" for write.\n"; local ($artist,$album,@trackName) = &guessFromCDDB(@files); print F "# wav2mp3 parameter settings ######################################################################### # The following are defaults, and can be over-ridden on a per-track basis OutputDirectoryBase ./ # Artist and Album may be appended to this Year 9999 # Year recording was made (none if 9999) TrackNumber 0 # None if 0 Artist $artist # Artist name, skipped if UNKNOWN Album $album # Album name, skipped if UNKNOWN Comment NONE # Comment field, skipped if NONE CompressionQuality 2 # 0 is best quality, 9 is worst (most compressed) # Allowable compression rates (kBits/sec): # 32,40,48,56,64,80,96,112,128,160,192,224,256,320 MinRate 112 # Min allowed compression bit rate AppendArtist TRUE # \"Artist\" is appended to OutputDirectoryBase if available AppendAlbum TRUE # \"Album\" is appended to OutputDirectoryBase if available ################################################################## # Individual conversion job requests\n\n"; local $i; for ($i=0;$i<=$#files;$i++) { print F "InFile $files[$i] TrackNumber ",$i+1," Title $trackName[$i]\n\n"; } close F; } # if cd is in drive, try to guess info from CDDB # MyCDDB is just the standard cddb.pl, but with # options set to just print out results from first match, # with no user interaction #require "MyCDDBget.pm"; sub guessFromCDDB { use MyCDDBget (); local (@trk) = @_; local ($artist,$album,@trkNam) = &MyCDDBget(); local $i; for($i=0; $i < $#trk; $i++) { if (defined($trkNam[$i])) { $trk[$i] = $trkNam[$i]; } else { $trk[$i] = &guessTitleFromFileName($trk[$i]); } } return($artist,$album,@trk); } sub guessTitleFromFileName { $_ = `basename $_[0]`; chomp; s/\..*$//; s/^[0-9]+_//; $_ = "UNK" if (length($_) <= 0); s/_/ /g; return ($_); } sub parseLine { local ($key,$default,$_) = @_; defined($default) or die "No default set for $key"; defined($key) or return $default; defined($_) or return $default; #print "Parse $key,$default,<$_>\n"; s/^\s+//; m/^$key/ or return $default; s/$key\s+//; s/\s+$//; #print "Found<$_>\n"; return $_; } sub parseBoolean { local ($key,$default,$_) = @_; defined($key) or return $default; defined($default) or die "No default set"; defined($_) or return $default; #print "ParseB $key,$default,<$_>\n"; s/^\s+//; m/^$key/ or return $default; s/$key\s+//; s/\s+$//; #print "Found <$_>\n"; m/TRUE/i and return 1; m/YES/i and return 1; m/1/ and return 1; return 0; } # make substitutions in a string to create something more appropriate # for a UNIX filename sub safeFileName { ($_) = @_; chomp; s/\s/_/g; # replace white space with underscores #s/[\'";:\[\]\(\)\*&#`\/]/~/g; # replace odd characters with ~ #print "SafeFile <$_>\n"; return $_; } sub baseName { ($_) = @_; #print "<$_> last<"; local @a = split '/'; $_ = $a[$#a]; #print "$_> Base <"; @a = split '\.'; #print "$a[0]>\n"; return $a[0]; } __END__ # Stuff below here is dead code from earlier versions. # If i ever feel that this script is mature, I'll remove this. # For now, i leave it in case I ever need to revive some of it. #################################### aaron #$compLvl = &getInteger( # "Compression rate [0..9], 0 is least compression === highest quality:" # ,0,9); # $minRate = &getInteger("Min Encoding Rate [32,40,48,56,64,80,96,112,128,160,192,224,256,320]:",32,40,48,56,64,80,96,112,128,160,192,224,256,320); # $title[$i] = &updateEntry("Name",$title[$i],30); sub updateEntry{ # local ($prompt,$default,$len) = @_; local ($prompt,$default) = @_; print "$prompt [$default] : "; $_ = ; chomp; if (length($_) < 1){ # use default return $default; } #$_ = substr($_,0,$len); return ($_); } sub getInteger{ local ($msg,@rng) = @_; while (1) { print $msg; $_ = ; chomp; # just positive for now if ((substr($_,0,1) ge '0') and (substr($_,0,1) le '9')) { if ($#rng == 1) { if (($_ >= $rng[0]) and ($_ <= $rng[1])) { return $_; } } else { local $x; foreach $x (@rng) { if ($_ == $x) { return $_; } } } } } } #sub getGenre{ # here is the list of acceptable genre codes I may want to implement in the # future # 00 - Blues # 01 - Classic Rock # 02 - Country # 03 - Dance # 04 - Disco # 05 - Funk # 06 - Grunge # 07 - Hip-Hop # 08 - Jazz # 09 - Metal # 10 - New Age # 11 - Oldies # 12 - Other # 13 - Pop # 14 - R&B # 15 - Rap # 16 - Reggae # 17 - Rock # 18 - Techno # 19 - Industrial # 20 - Alternative # 21 - Ska # 22 - Death Metal # 23 - Pranks # 24 - Soundtrack # 25 - Euro-Techno # 26 - Ambient # 27 - Trip-Hop # 28 - Vocal # 29 - Jazz+Funk # 30 - Fusion # 31 - Trance # 32 - Classical # 33 - Instrumental # 34 - Acid # 35 - House # 36 - Game # 37 - Sound Clip # 38 - Gospel # 39 - Noise # 40 - Alternative Rock # 41 - Bass # 43 - Punk # 44 - Space # 45 - Meditative # 46 - Instrumental Pop # 47 - Instrumental Rock # 48 - Ethnic # 49 - Gothic # 50 - Darkwave # 51 - Techno-Industrial # 52 - Electronic # 53 - Pop-Folk # 54 - Eurodance # 55 - Dream # 56 - Southern Rock # 57 - Comedy # 58 - Cult # 59 - Gangsta # 60 - Top 40 # 61 - Christian Rap # 62 - Pop/Funk # 63 - Jungle # 64 - Native US # 65 - Cabaret # 66 - New Wave # 67 - Psychadelic # 68 - Rave # 69 - Showtunes # 70 - Trailer # 71 - Lo-Fi # 72 - Tribal # 73 - Acid Punk # 74 - Acid Jazz # 75 - Polka # 76 - Retro # 77 - Musical # 78 - Rock & Roll # 79 - Hard Rock # 80 - Folk # 81 - Folk-Rock # 82 - National Folk # 83 - Swing # 84 - Fast Fusion # 85 - Bebob # 86 - Latin # 87 - Revival # 88 - Celtic # 89 - Bluegrass # 90 - Avantgarde # 91 - Gothic Rock # 92 - Progressive Rock # 93 - Psychedelic Rock # 94 - Symphonic Rock # 95 - Slow Rock # 96 - Big Band # 97 - Chorus # 98 - Easy Listening # 99 - Acoustic # 100 - Humour # 101 - Speech # 102 - Chanson # 103 - Opera # 104 - Chamber Music # 105 - Sonata # 106 - Symphony # 107 - Booty Bass # 108 - Primus # 109 - Porn Groove # 110 - Satire # 111 - Slow Jam # 112 - Club # 113 - Tango # 114 - Samba # 115 - Folklore # 116 - Ballad # 117 - Power Ballad # 118 - Rhytmic Soul # 119 - Freestyle # 120 - Duet # 121 - Punk Rock # 122 - Drum Solo # 123 - Acapella # 124 - Euro-House # 125 - Dance Hall # 126 - Goa # 127 - Drum & Bass # 128 - Club-House # 129 - Hardcore # 130 - Terror # 131 - Indie # 132 - BritPop # 133 - Negerpunk # 134 - Polsk Punk # 135 - Beat # 136 - Christian Gangsta # 137 - Heavy Metal # 138 - Black Metal # 139 - Crossover # 140 - Contemporary C # 141 - Christian Rock # 142 - Merengue # 143 - Salsa # 144 - Thrash Metal # 145 - Anime # 146 - JPop # 147 - SynthPop