1 #!/usr/bin/perl
  2 =head1 NAME
  3 
  4 reencoder - wrapper to reencode audio to different bitrates and format
  5 
  6 =head1 SYNOPSIS
  7 
  8 reencoder [--help] | [--debug] [--verbose] [[--test] | [ [--ogg-to-mp3] |
  9 [--mp3-to-ogg] | [--wma-to-mp3] | [--wma-to-ogg] | [--mp3-to-mp3]]
 10 [--bitrate=BITRATE] [--outputdir=DIR]]
 11 
 12 
 13 =head1 DESCRIPTION
 14 
 15 "reencoder" reencodes audio to a different format, and optionally a different
 16 bitrate.  It was originally designed to convert from ogg to mp3, but will also
 17 convert from wma, and will convert to both ogg and mp3.
 18 
 19 Use the --test option to check if the necessary applications are available.  Use
 20 --help to get examples of how to use.
 21 
 22 =head1 PREREQUISITES
 23 
 24 You will need the libogg-vorbis-header-perl and libmp3-tag-perl packages (and
 25 their dependencies) if using Debian/Ubuntu.  Everything else, grab the
 26 appropriate modules (Ogg::Vorbis::Header and MP3::Tag) from CPAN.
 27 
 28 =head1 BUGS
 29 
 30 The bitrate may only be setting the minimum bitrate for lame, not the target
 31 bitrate.
 32 
 33 =head1 AUTHOR INFORMATION
 34 
 35 Copyright 2005-2007, Michael Howe <michael@michaelhowe.org>.  All rights
 36 reserved.
 37 
 38 Originally written for Claire Millican.
 39 
 40 This script is free software; you can redistribute it and/or modify it under
 41 the same terms as Perl itself.
 42 
 43 Questions and comments are welcome.
 44 
 45 =over
 46 
 47 =head1 FUNCTIONS (REFERENCE ONLY)
 48 
 49 Below are general functions, as used in the script, for reference only.
 50 
 51 =cut
 52 
 53 use strict;
 54 use warnings;
 55 use Ogg::Vorbis::Header;
 56 use MP3::Tag;
 57 use IO::File;
 58 use Getopt::Long;
 59 use File::Basename;
 60 # Where we save the intermediate wav files
 61 my $tmpdir = "/tmp/oggconversion";
 62 
 63 # Name of the script (used to prefix debugging output).
 64 my $procname = 'reencoder';
 65 
 66 # May be redefined by options passed to the script later.
 67 my $help = 0;
 68 my $debug = 0;
 69 my $verbose = 0;
 70 my $test = 0;
 71 my $bitrate = 128;
 72 my $oggmp3 = 1;
 73 my $mp3ogg = 0;
 74 my $wmamp3 = 0;
 75 my $wmaogg = 0;
 76 my $mp3mp3 = 0;
 77 my $outputdir = `pwd`;
 78 
 79 =item debug( @debugstring )
 80 
 81 Takes an array @debugstring, which is printed to stderr if the verbose flag is
 82 passed.
 83 
 84 =cut
 85 
 86 sub debug(@) {
 87 	STDERR->print("$procname: ", @_, "\n") if ($verbose);
 88 }
 89 
 90 =item convert( $file, $cext, $next, $outputdir, $bitrate )
 91 
 92 Converts the given file to the given format.
 93 Accepts five arguments:
 94 File to convert
 95 Extension of file to convert
 96 Extension of file to be create
 97 Dir on which to create new file
 98 Bitrate of file
 99 
100 =cut
101 
102 sub convert($$$$$) {
103 	my $file = shift;
104 	my $cext = shift;
105 	my $next = shift;
106 	my $outputdir = shift;
107 	my $bitrate = shift;
108 	my $outname;
109 	my %comments = (
110 		artist => "",
111 		title => "",
112 		album => ""
113 	);
114 	
115 	# populate the comments hash:
116 	if ( 'ogg' eq $cext ) {
117 		my $ogg = Ogg::Vorbis::Header->new($file);
118 		%comments = (
119 			artist => $ogg->comment('artist'),
120 			title => $ogg->comment('title'),
121 			album => $ogg->comment('album')
122 		);
123 	} elsif ( 'mp3' eq $cext ) {
124 		my $mp3 = MP3::Tag->new($file);
125 		my ($title, $artist, $album) = ($mp3->autoinfo())[1,3,4];
126 		if ( $mp3->{ID3v2} ) {
127 			$title = ($mp3->{ID3v2}->get_frame("TIT2"))[0];
128 			$artist = ($mp3->{ID3v2}->get_frame("TPE1"))[0];
129 			$album = ($mp3->{ID3v2}->get_frame("TALB"))[0];
130 		} elsif ( $mp3->{ID3v1} ) {
131 			$title = $mp3->{ID3v1}->title;
132 			$artist = $mp3->{ID3v1}->artist;
133 			$album = $mp3->{ID3v1}->album;
134 		}
135 		%comments = (
136 			artist => $artist,
137 			title => $title,
138 			album => $album
139 		);
140 	}
141 	# we don't even like to _think_ about WMA files.
142 
143 	# This just stops the warnings about empty values, as they may get
144 	# set to undef if there's no value (obviously).
145 	$comments{artist} = "" unless ( $comments{artist} ) ;
146 	$comments{album} = "" unless ( $comments{album} );
147 	$comments{title} = "" unless ( $comments{title} );
148 
149 	print "Beginning encoding of $file\n";
150 
151 	# Mplayer magic - check it exists, then encode the initial file to
152 	# a wav file.
153 	my $mplayer = searchpath('mplayer');
154 	debug "Using mplayer at $mplayer.";
155 	( $mplayer ) || die("Can't find mplayer.  Try --help.");
156 	my $basename = basename($file);
157 	$basename =~ s{\.$cext$}{.wav};
158 	debug "Checking for existance of temporary directory.";
159 	( -d $tmpdir ) || mkdir $tmpdir || die( "Can't make directory $tmpdir: $!");
160 	
161 	debug "Encoding $file to $tmpdir/$basename using mplayer";
162 	system "$mplayer/mplayer -vc dummy -vo null -quiet -ao pcm:file=\"$tmpdir/$basename\" \"$file\" 1>/dev/null 2>&1";
163 	debug "Finished mplayer encoding";
164 
165 	# Sanity check - die if the file has not been created.
166 
167 	(-f "$tmpdir/$basename" ) || die( "Error - encoding of file $file failed
168 ($tmpdir/$basename does not exist).\nPlease try running the mplayer command
169 manually.\nCommand: mplayer -vc dummy -vo null -quiet -ao
170 pcm:file='$tmpdir/$basename' '$file'\n");
171 
172 
173 	# Check what format the output file should be, then encode
174 	# appropriately:
175 	if ( 'mp3' eq $next ) {
176 		$outname = $basename;
177 		$outname =~ s{\.wav$}{.mp3};
178 		# Whee!  Let's fire up lame:
179 		my $lame = searchpath('lame');
180 		( $lame) || die( "Error - you asked for mp3 encoding, but cannot find the lame binary in your path.  Please try --help.");
181 		debug "Encoding '$tmpdir/$basename' to '$outputdir/$outname' using lame";
182 		# Do the actual encoding:
183 		system "$lame/lame --quiet -h -b $bitrate --vbr-new --tt \"$comments{title}\" --ta \"$comments{artist}\" --tl \"$comments{album}\" --add-id3v2 \"$tmpdir/$basename\" \"$outputdir/$outname\"";
184 		debug "Encoding of '$outputdir/$outname' finished";
185 		# Clean up:
186 		debug "Removing $tmpdir/$basename";
187 		unlink "$tmpdir/$basename";
188 	} elsif ( 'ogg' eq $next ) {
189 		# We want an ogg file:
190 		$outname = $basename;
191 		$outname =~ s{\.wav$}{.ogg};
192 		my $oggenc = searchpath('oggenc');
193 		( $oggenc ) || die( "Error - you asked for ogg encoding, but cannot find the oggenc binary in your path.  Please try --help.");
194 		debug "Encoding '$tmpdir/$basename' to '$outputdir/$outname' using oggenc";
195 		system "$oggenc/oggenc --quiet --bitrate $bitrate --artist \"$comments{artist}\" --album \"$comments{album}\" --title \"$comments{title}\" --output=\"$outputdir/$outname\" \"$tmpdir/$basename\"";
196 		debug "Encoding of '$outputdir/$outname' finished";
197 		# Clean up:
198 		debug "Removing $tmpdir/$basename";
199 		unlink "$tmpdir/$basename";
200 	}
201 	print "File '$file' converted to '$outputdir/$outname'\n";
202 }
203 
204 =item usage( $output_object )
205 
206 Prints usage statement.
207 Accepts one argument, the thing that will be used to print the statement.
208 
209 =cut
210 
211 sub usage($) {
212 	my $f = shift;
213 	$f->print(<<EOF);
214 $0: Interconverts media files.
215 Note that these files are _lossy_, so the quality of the new file will be worse
216 than that of the original - there's nothing that can be done about this.
217 
218 Options:
219     --help	Display this message
220     --verbose	Display more info on what the script is doing
221     --test	Check that you have the appropriate programs installed
222 		(currently mplayer, lame and oggenc)
223     --bitrate	The bitrate that you want to encode the ogg/mp3 at (default
224     		128kB/s)
225     --outputdir	The directory to save the new files to (defaults to current
226     		directory).  Will attempt to create the directory if it doesn't
227 		exist
228     --ogg-to-mp3 (default)
229     		Convert from ogg to mp3
230     --mp3-to-ogg
231     		Convert from mp3 to ogg
232     --wma-to-mp3
233     		Convert from wma to mp3
234     --wma-to-ogg
235     		Convert from wma to ogg
236     --mp3-to-mp3
237 		Convert from mp3 to mp3
238 
239 Sample usage:
240 \$ cd /a/dir/with/ogg/files/
241 \$ mkdir mp3s
242 \$ $0 --outputdir=/a/dir/with/ogg/files/mp3s/ --ogg-to-mp3 *.ogg
243 Creates a mp3 copy in subdirectory /mp3s/ of all ogg files in
244 /a/dir/with/ogg/files.
245 
246 To convert a wma file to a mp3 file:
247 \$ $0 --wma-to-mp3 /a/random/wma/file.wma
248 Creates a file called 'file.mp3' in the current directory from the wma file
249 'file.wma' in directory '/a/random/wma/'.
250 
251 Copyright 2005 Michael Howe, written for Claire Millican.
252 
253 EOF
254 }
255 
256 =item dotest
257 
258 Do some tests to see if appropriate apps are on the system
259 
260 =cut
261 
262 sub dotest() {
263 	# Firstly, mplayer:
264 	my $mplayer = searchpath('mplayer');
265 	if ( $mplayer ) {
266 		print "Mplayer found at $mplayer\n"
267 	} else {
268 		warn( "Can't find mplayer in your path - do you have it installed?\nMplayer is REQUIRED for this script to run.");
269 	}
270 	# lame:	
271 	my $lame = searchpath('lame');
272 	if ( $lame ) {
273 		print "lame (mp3 encoder) found at $lame\n";
274 	} else {
275 		warn( "lame (mp3 encoder) not found in your path - do you have it installed?\nlame or oggenc are required for this script." );
276 	}
277 	# oggenc:
278 	my $oggenc = searchpath('oggenc');
279 	if ( $oggenc ) {
280 		print "oggenc (ogg encoder) found at $oggenc\n";
281 	} else {
282 		warn("oggenc (ogg encoder) not found in your path - do you have it installed?\nlame or oggenc are required for this script.");
283 	}
284 	if ( ($oggenc) && ($lame) ) {
285 		print "Oggenc and lame found.  There should be no encoding problems.\n";
286 	} else {
287 		warn("Neither lame nor oggenc were found in your path - you will not be able to use this script.\n");
288 	}
289 }
290 
291 
292 =item searchpath( $searchstring )
293 
294 Searches the PATH environment variable for the specified program.
295 Argument is the program to look for.
296 Returns the dir that it is in if it is found, 0 otherwise.
297 
298 =cut
299 
300 
301 sub searchpath($) {
302 	my $forwhat = shift;
303 	my @path = split( /:/, $ENV{"PATH"} );
304 
305 	my $foundit = 0;
306 	foreach (@path) {
307 #		debug "looking at $_/$forwhat";
308 		if ( -f "$_/$forwhat" ) {
309 			$foundit = $_;
310 			last;
311 		}
312 	}
313 	return $foundit;
314 }
315 
316 # start general code
317 GetOptions(
318 	'help|h' =>	\$help,
319 	'debug|d' =>	\$debug,
320 	'verbose|v' =>	\$verbose,
321 	'test|t' =>	\$test,
322 	'ogg-to-mp3' =>	\$oggmp3,
323 	'mp3-to-ogg' =>	\$mp3ogg,
324 	'wma-to-mp3' =>	\$wmamp3,
325 	'wma-to-ogg' =>	\$wmaogg,
326 	'mp3-to-mp3' => \$mp3mp3,
327 	'bitrate=i' =>	\$bitrate,
328 	'outputdir=s' =>	\$outputdir
329 );
330 
331 # Yeah, has stupid cr/lf/whatever from pwd
332 chomp $outputdir;
333 
334 if ($help) {
335 	usage(\*STDOUT);
336 	exit(0);
337 }
338 
339 if ($test) {
340 	dotest();
341 	exit(0);
342 }
343 
344 (@ARGV > 0 ) || die "Error - no files specified!  Try --help for help.\n";
345 
346 my @files = @ARGV;
347 
348 my ($ext, $newext);
349 if ( $oggmp3 ) { $ext = "ogg"; $newext = "mp3"; }
350 if ( $mp3ogg ) { $ext = "mp3"; $newext = "ogg"; }
351 if ( $wmamp3 ) { $ext = "wma"; $newext = "mp3"; }
352 if ( $wmaogg ) { $ext = "wma"; $newext = "ogg"; }
353 if ( $mp3mp3 ) { $ext = "mp3"; $newext = "mp3"; }
354 
355 foreach (@files) {
356 	chomp;
357 	unless (m{\.$ext$}) {
358 		debug "File $_ is not a $ext file.";
359 		next;
360 	}
361 	# Ok, we have a file to convert.  So what now?
362 	debug "Converting $_";
363 	convert($_, $ext, $newext, $outputdir, $bitrate);
364 }
365 
366 =back
367 
368 =cut
369 
370 # vim: tw=80
371 


syntax highlighted by Code2HTML, v. 0.9.1