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