#!/usr/bin/perl =head1 NAME reencoder - wrapper to reencode audio to different bitrates and format =head1 SYNOPSIS reencoder [--help] | [--debug] [--verbose] [[--test] | [ [--ogg-to-mp3] | [--mp3-to-ogg] | [--wma-to-mp3] | [--wma-to-ogg] | [--mp3-to-mp3]] [--bitrate=BITRATE] [--outputdir=DIR]] =head1 DESCRIPTION "reencoder" reencodes audio to a different format, and optionally a different bitrate. It was originally designed to convert from ogg to mp3, but will also convert from wma, and will convert to both ogg and mp3. Use the --test option to check if the necessary applications are available. Use --help to get examples of how to use. =head1 PREREQUISITES You will need the libogg-vorbis-header-perl and libmp3-tag-perl packages (and their dependencies) if using Debian/Ubuntu. Everything else, grab the appropriate modules (Ogg::Vorbis::Header and MP3::Tag) from CPAN. =head1 BUGS The bitrate may only be setting the minimum bitrate for lame, not the target bitrate. =head1 AUTHOR INFORMATION Copyright 2005-2007, Michael Howe . All rights reserved. Originally written for Claire Millican. This script is free software; you can redistribute it and/or modify it under the same terms as Perl itself. Questions and comments are welcome. =over =head1 FUNCTIONS (REFERENCE ONLY) Below are general functions, as used in the script, for reference only. =cut use strict; use warnings; use Ogg::Vorbis::Header; use MP3::Tag; use IO::File; use Getopt::Long; use File::Basename; # Where we save the intermediate wav files my $tmpdir = "/tmp/oggconversion"; # Name of the script (used to prefix debugging output). my $procname = 'reencoder'; # May be redefined by options passed to the script later. my $help = 0; my $debug = 0; my $verbose = 0; my $test = 0; my $bitrate = 128; my $oggmp3 = 1; my $mp3ogg = 0; my $wmamp3 = 0; my $wmaogg = 0; my $mp3mp3 = 0; my $outputdir = `pwd`; =item debug( @debugstring ) Takes an array @debugstring, which is printed to stderr if the verbose flag is passed. =cut sub debug(@) { STDERR->print("$procname: ", @_, "\n") if ($verbose); } =item convert( $file, $cext, $next, $outputdir, $bitrate ) Converts the given file to the given format. Accepts five arguments: File to convert Extension of file to convert Extension of file to be create Dir on which to create new file Bitrate of file =cut sub convert($$$$$) { my $file = shift; my $cext = shift; my $next = shift; my $outputdir = shift; my $bitrate = shift; my $outname; my %comments = ( artist => "", title => "", album => "" ); # populate the comments hash: if ( 'ogg' eq $cext ) { my $ogg = Ogg::Vorbis::Header->new($file); %comments = ( artist => $ogg->comment('artist'), title => $ogg->comment('title'), album => $ogg->comment('album') ); } elsif ( 'mp3' eq $cext ) { my $mp3 = MP3::Tag->new($file); my ($title, $artist, $album) = ($mp3->autoinfo())[1,3,4]; if ( $mp3->{ID3v2} ) { $title = ($mp3->{ID3v2}->get_frame("TIT2"))[0]; $artist = ($mp3->{ID3v2}->get_frame("TPE1"))[0]; $album = ($mp3->{ID3v2}->get_frame("TALB"))[0]; } elsif ( $mp3->{ID3v1} ) { $title = $mp3->{ID3v1}->title; $artist = $mp3->{ID3v1}->artist; $album = $mp3->{ID3v1}->album; } %comments = ( artist => $artist, title => $title, album => $album ); } # we don't even like to _think_ about WMA files. # This just stops the warnings about empty values, as they may get # set to undef if there's no value (obviously). $comments{artist} = "" unless ( $comments{artist} ) ; $comments{album} = "" unless ( $comments{album} ); $comments{title} = "" unless ( $comments{title} ); print "Beginning encoding of $file\n"; # Mplayer magic - check it exists, then encode the initial file to # a wav file. my $mplayer = searchpath('mplayer'); debug "Using mplayer at $mplayer."; ( $mplayer ) || die("Can't find mplayer. Try --help."); my $basename = basename($file); $basename =~ s{\.$cext$}{.wav}; debug "Checking for existance of temporary directory."; ( -d $tmpdir ) || mkdir $tmpdir || die( "Can't make directory $tmpdir: $!"); debug "Encoding $file to $tmpdir/$basename using mplayer"; system "$mplayer/mplayer -vc dummy -vo null -quiet -ao pcm:file=\"$tmpdir/$basename\" \"$file\" 1>/dev/null 2>&1"; debug "Finished mplayer encoding"; # Sanity check - die if the file has not been created. (-f "$tmpdir/$basename" ) || die( "Error - encoding of file $file failed ($tmpdir/$basename does not exist).\nPlease try running the mplayer command manually.\nCommand: mplayer -vc dummy -vo null -quiet -ao pcm:file='$tmpdir/$basename' '$file'\n"); # Check what format the output file should be, then encode # appropriately: if ( 'mp3' eq $next ) { $outname = $basename; $outname =~ s{\.wav$}{.mp3}; # Whee! Let's fire up lame: my $lame = searchpath('lame'); ( $lame) || die( "Error - you asked for mp3 encoding, but cannot find the lame binary in your path. Please try --help."); debug "Encoding '$tmpdir/$basename' to '$outputdir/$outname' using lame"; # Do the actual encoding: system "$lame/lame --quiet -h -b $bitrate --vbr-new --tt \"$comments{title}\" --ta \"$comments{artist}\" --tl \"$comments{album}\" --add-id3v2 \"$tmpdir/$basename\" \"$outputdir/$outname\""; debug "Encoding of '$outputdir/$outname' finished"; # Clean up: debug "Removing $tmpdir/$basename"; unlink "$tmpdir/$basename"; } elsif ( 'ogg' eq $next ) { # We want an ogg file: $outname = $basename; $outname =~ s{\.wav$}{.ogg}; my $oggenc = searchpath('oggenc'); ( $oggenc ) || die( "Error - you asked for ogg encoding, but cannot find the oggenc binary in your path. Please try --help."); debug "Encoding '$tmpdir/$basename' to '$outputdir/$outname' using oggenc"; system "$oggenc/oggenc --quiet --bitrate $bitrate --artist \"$comments{artist}\" --album \"$comments{album}\" --title \"$comments{title}\" --output=\"$outputdir/$outname\" \"$tmpdir/$basename\""; debug "Encoding of '$outputdir/$outname' finished"; # Clean up: debug "Removing $tmpdir/$basename"; unlink "$tmpdir/$basename"; } print "File '$file' converted to '$outputdir/$outname'\n"; } =item usage( $output_object ) Prints usage statement. Accepts one argument, the thing that will be used to print the statement. =cut sub usage($) { my $f = shift; $f->print(< \$help, 'debug|d' => \$debug, 'verbose|v' => \$verbose, 'test|t' => \$test, 'ogg-to-mp3' => \$oggmp3, 'mp3-to-ogg' => \$mp3ogg, 'wma-to-mp3' => \$wmamp3, 'wma-to-ogg' => \$wmaogg, 'mp3-to-mp3' => \$mp3mp3, 'bitrate=i' => \$bitrate, 'outputdir=s' => \$outputdir ); # Yeah, has stupid cr/lf/whatever from pwd chomp $outputdir; if ($help) { usage(\*STDOUT); exit(0); } if ($test) { dotest(); exit(0); } (@ARGV > 0 ) || die "Error - no files specified! Try --help for help.\n"; my @files = @ARGV; my ($ext, $newext); if ( $oggmp3 ) { $ext = "ogg"; $newext = "mp3"; } if ( $mp3ogg ) { $ext = "mp3"; $newext = "ogg"; } if ( $wmamp3 ) { $ext = "wma"; $newext = "mp3"; } if ( $wmaogg ) { $ext = "wma"; $newext = "ogg"; } if ( $mp3mp3 ) { $ext = "mp3"; $newext = "mp3"; } foreach (@files) { chomp; unless (m{\.$ext$}) { debug "File $_ is not a $ext file."; next; } # Ok, we have a file to convert. So what now? debug "Converting $_"; convert($_, $ext, $newext, $outputdir, $bitrate); } =back =cut # vim: tw=80