1#!/usr/local/bin/perl
2#
3# mp3burn $Revision: 0.13 $ $Date: 2008/10/04 10:23:21 $
4# based upon mp3burn-0.1 - see http://sourceforge.net/projects/mp3burn/
5#
6#Copyright 2000 Ryan Richter <bobort@bigfoot.com>
7#With help from Dan Lark <dlark@pcisys.net>
8#Copyright 2003 Alexander Wirt <formorer@formorer.de>
9#
10#You may fold, spindle, and mutilate this software under the terms of the GPL
11#
12# $Log: mp3burn,v $
13# Revision 0.13  2008/10/04 10:23:21  formorer
14# Fix swab detection
15#
16# Revision 0.12  2006/09/24 09:38:28  formorer
17# Add swap support for ppc
18#
19# Revision 0.11  2005/02/09 21:10:51  formorer
20# Added swab detection for amd64
21#
22# Revision 0.10  2004/07/08 20:33:50  formorer
23# - Fixed some small typo - thanks to sdelafond@lika.fr.st
24#
25# Revision 0.9  2004/06/20 15:22:18  formorer
26# Added Support for length detection of FLAC files.
27# (There must be someone outside really using them ;))
28#
29# Revision 0.8  2004/06/05 06:40:34  formorer
30# I'm bored from any locale problems or changes in the output of ogginfo...
31# So I decided to switch to Ogg::Vorbis::Header and it work like a charme..
32# Here it is.
33#
34# Revision 0.7  2004/06/05 05:56:18  formorer
35# the decection of the correct mp3decoder is now much smarter :)
36#
37# Revision 0.6  2004/04/28 08:06:37  formorer
38# Fixed Flac Detection - Thanks to: Georg Wittmann <debian-bug@pbrz.de>
39#
40# Revision 0.5  2004/04/14 10:06:39  formorer
41# Fixed get_atip function, so that $cdrecord_opts are recognized
42#
43# Revision 0.4  2004/01/02 09:12:48  formorer
44# Fixed Podparsing
45#
46# Revision 0.3.1.1  2004/01/01 21:43:57  formorer
47# Initial Import
48#
49######################
50
51
52=head1 NAME
53
54mp3burn - burn audio CDs from MP3, Ogg Vorbis, or FLAC files
55
56=cut
57
58use MP3::Info;
59use File::Basename;
60use Pod::Usage;
61use String::ShellQuote;
62use Getopt::Long;
63use Ogg::Vorbis::Header;
64
65sub get_audio_info {
66    my  ($filename) =@_;
67    my $hash = {};
68    my $fileinfo=`file -b "$filename"`;
69    if ($fileinfo =~ m/FLAC/i) {
70	#If the FLAC decoder is not installed we just exit
71	$flac = `which flac`;
72	chomp $flac;
73	if (! -x $flac) { print "FLAC decoder is not available\n"; return; }
74
75        # FLAC file
76	# we don't know the length, so just set it to 1 second
77	$hash->{DECODER}=["flac", "-d", "-F", "-s", "-c"];
78        eval "require Audio::FLAC";
79	if ($@) {
80		if ($DEBUG) { print "No Audio::FLAC available\n"; }
81		$hash->{SECS} = 1;
82	} else {
83		require Audio::FLAC;
84		my $flac = Audio::FLAC->new("$filename");
85		$hash->{SECS} = $flac->{trackTotalLengthSeconds};
86	if ($DEBUG) { print "Flac Length: $hash->{SECS}\n"; }
87
88	}
89
90    } elsif ($fileinfo =~ m/Ogg data/i) {
91        # Ogg/Vorbis processing
92	$hash->{DECODER}=["ogg123", "-d", "raw", "-f", "-", "-q"];
93	my $ogginfo = Ogg::Vorbis::Header->new("$filename");
94	if (! defined $ogginfo) {
95	undef $hash;
96	next;
97	}
98
99	$hash->{SECS} = $ogginfo->info('length');
100	$hash->{MODE} = $ogginfo->info('channels');
101	$hash->{FREQUENCY} = $ogginfo->info('rate');
102	if ($DEBUG) {
103		print "ogg parsing:\n";
104		print "\tchannels=$hash->{MODE}\n";
105		print "\trate=$hash->{FREQUENCY}\n";
106		print "\tlength=$hash->{SECS}\n";
107	}
108    } elsif ($hash = MP3::Info::get_mp3info($filename)) {
109        if ($mp3decoder) {
110	    $hash->{DECODER} = ["$mp3decoder", "--rate", "44100", "--stereo", "-s", "-q"];
111	} else {
112	    if ($hash->{FREQUENCY} != 44.1) {
113	       print "*** unable to continue ***\n";
114	       print "  mpg321 cannot handle files with sample rates != 44.1kHz\n";
115	       print "  sample rate is $hash->{FREQUENCY} for file: $filename\n";
116	       print "  please either burn without this file or see the mp3burn manpage\n";
117	       print "  about using -M or setting \$mp3decoder in \~/.mp3burnrc\n";
118	       &Cleanup;
119	       exit 1;
120	    }
121  	    $hash->{DECODER} = ["mpg321", "--rate", "44100", "--stereo", "-s", "-q"];
122	}
123    }
124    # else we don't know what type of file this is...
125    $hash;
126}
127
128
129sub get_ATIP_info () {
130	#die "No CDR device information is available. Please specify the device." unless ($1);
131	#eject doesn't work with the atip switch... also it would be counterproductive to eject
132	#the cd before burning... so just remove it
133
134	$atipoptions = $cdrecord_opts;
135	$atipoptions =~ s/-eject//;
136
137	if ($DEBUG) { print "Call cdrecord with: cdrecord -atip $atipoptions. \n"; }
138	open(CDINFO,"cdrecord -atip $atipoptions 2>&1 |"); #Use cdrecord -atip to get ATIP info
139	while (<CDINFO>) {
140	        if (/cdrecord: No CD\/DVD-Recorder device specified/) {
141		   print "No CDR device specified. It must be specified via on of the following:\n";
142		   print "\tdev=\$device in \~/.mp3burnrc\n";
143		   print "\t-o \"dev=\$device\" on the command-line\n";
144		   print "\tthe value of \$CDR_DEVICE in the environment\n";
145		   print "\tin the file /etc/default/cdrecord (man cdrecord)\n";
146		   exit 1;
147		}
148		next unless (/out:.+\((\d+):(\d+)/); #The lead out time is what we want
149		$min=$1; $sec=$2;
150	}
151	close CDINFO;
152	die "No CD-R in CD Writer." unless ($sec && $min);
153	printf "ATIP reports available time: [%d:%.2d]\n",$min,$sec;
154}
155
156
157sub Cleanup {
158
159   # kill off any children that might still be around
160   if (@children) {
161      print "cleaning up children: @children\n";
162      kill 'TERM', @children;
163   }
164
165   # remove any fifos we created
166   if (@fifo) {
167      unlink @fifo;
168   }
169}
170
171=head1 SYNOPSIS
172
173B<mp3burn> [OPTION] [mp3,ogg, and flac files]
174
175=cut
176
177
178#Very much better and more intuitivly to work :)
179Getopt::Long::Configure ("bundling");
180$files = GetOptions('help|h' => \$help, #Help function
181					'swap|m' => \$swap, #Manual swap
182					'playlist|p=s' => \$playlist, #Load a playliste
183					'tmpdir|t=s' => \$tempdir, #tempdir
184					'check|c=s' => \$check, #timecheck
185					'cdrecord|o=s' => \$manual, #cdrecord
186					'dummy|d' => \$dummy, #debugfunction
187					'atip|a' => \$atip, #show atip infos
188					'encoder|M=s' => \$encoder, #external mp3 encoder
189					'debug|D' => \$DEBUG); #debugging
190
191
192
193=head1 DESCRIPTION
194
195B<mp3burn>
196is a simple command line tool for making audio CDs from
197MP3s without filling up your disk with .wav files.  It uses
198Perl(1), ogg123(1), mpg321(1) or mpg123(1), cdrecord(1),
199flac(1), and the L<MP3::Info(3)> Perl module.
200
201=cut
202
203=head1 OPTIONS
204
205=over 4
206
207=cut
208
209=item B<-h, --help>
210
211Prints out a brief help
212
213=item B<-m, --swap>
214
215Manual C<cdrecord -swab> option mode.
216Use this to disable the automatic detection for swab mode in case it is
217not working correctly on your system.  (Also, please send email to
218<formorer@formorer.de> or file a bug against the L<mp3burn> package if you
219encounter this problem.)
220
221=cut
222
223$manual_cdrecord_opts = $swap;
224
225=item B<-p, --playlist> ".m3u playlist"
226
227Use a playlist to specify audio files to burn.
228Instead of (or in addition to) listing mp3/ogg/flac files,
229supply a .m3u playlist (e.g., from xmms) that contains the
230audio files for your CD.
231
232Note:  If you specify both a playlist and audio files, the
233files specified on the command-line will be appended to the list
234of audio files listed in the playlist.  If a file referenced
235in a playlist cannot be read, it will be skipped.  Be wary of
236playlist editors that use relative paths - mp3burn cannot know
237what path the playlist editor assumed.
238
239=cut
240
241
242=item B<-t, --tmpdir> "tmpdir"
243
244Put temporary files in F<tmpdir>.
245Default is to use the current directory.
246
247=cut
248
249if ($tempdir) {
250	$tmpdir=$tempdir ."/";
251	die "Cannot write to temp. dir -> $tmpdir" unless  ( -d $tmpdir &&  -w $tmpdir);
252}
253
254
255
256
257if (-r "$ENV{'HOME'}/.mp3burnrc") {		#process ~/.mp3burnrc
258	if ((stat("$ENV{'HOME'}/.mp3burnrc"))[2] & 02) {
259		die "$ENV{'HOME'}/.mp3burnrc should not be world-writable";
260	}
261	open(RC, "$ENV{'HOME'}/.mp3burnrc");
262	$oldRS = $/;
263	undef $/;
264	$rc = <RC>;
265	close(RC);
266	unless(defined eval $rc) {
267		die "Error in .mp3burnrc:\n$@";
268	}
269	$/ = $oldRS;
270}
271
272#cdrecord_opts must be determined before -c or -a options are processed.
273#The value of $CDR_DEVICE must be explicitly added to $cdrecord_opts
274#in order to get past our checks.  Other cdrecord env vars need not be processed by us.
275if(exists $ENV{'CDR_DEVICE'}) {
276   $cdrecord_opts .= " dev=" . $ENV{'CDR_DEVICE'} . " ";
277   if ($DEBUG) {
278      print "adding the value of environment variable CDR_DEVICE to the cdrecord_opts\n";
279   }
280}
281
282$cdrecord_opts = $manual if $manual; 	# -o overrides .mp3burnrc
283
284=item B<-c, --check> "MMM:SS" | ATIP
285
286Time check:  compute the total length of files to be burned
287and warn if greater than I<MMM:SS> minutes and seconds.  If
288the value ATIP is supplied, the total length is checked
289against the length available on the CDR[W] as reported by
290ATIP.
291
292Note that FLAC-encoded files are assumed to be 1 second long
293(until there is an easy way to get the file duration).  You
294will need to calculate burn-length on your own with FLAC files.
295
296=cut
297
298if ($check) {
299	die "Time check not available without MP3::Info module" if $no_mp3info;
300	if ($check =~ /ATIP/i) { #If the user trusts ATIP info use that for our time check
301		get_ATIP_info();
302	} else { #Otherwise a time is supplied
303		die "Time check needs to be in the form of MMM:SS or 'ATIP'"
304			unless ($check =~ /\d{0,3}\:\d{2}$/);
305		($min,$sec)=split(/\:/,$check);
306	}
307}
308
309# this is no longer necessarily a condition to die...
310#if ($cdrecord_opts eq '') {
311#	die "Need to specify cdrecord options through -o or .mp3burnrc\n" .
312#		"Usage:  mp3burn [-c MMM:SS] [-d] [-t tmpdir] [-o cdrecord_opts] [mp3 files]\n";
313#}
314
315
316=item B<-d, --dummy>
317
318Perform a "dummy" run: do everything except actually burn the CD
319(uses L<cdrecord(1)> C<-dummy> option).
320
321=cut
322
323if ($dummy) {
324   $cdrecord_opts .= " -dummy";
325}
326
327
328=item B<-o, --cdrecord>  "cdrecord_opts"
329
330Specify the command line options for cdrecord.
331The quotes are required to prevent B<mp3burn> from parsing cdrecord(1) options.
332Overrides options specified in F<~/.mp3burnrc>.
333
334Example: B<-o> "-v dev=1,0 speed=4 -swab"
335
336=item B<Note:>
337
338The options I<-pad> and I<-audio> are added automatically, since they are
339always necessary.  The script also tries to detect if I<-swab>  is needed (for
340example on x86 and other little-Endian platforms).  cdrecord is supposed
341to take care of any byte-ordering requirements specific to your burner.
342(If you end up with a CD that merely sounds like static, you most likely
343need to toggle use of I<-swab>.)  You should also consider using I<-v> so
344that you can watch the burn in progress. This goes for F<~/.mp3burnrc> also.
345
346=cut
347
348$cdrecord_opts .= " -tao -pad -audio";
349
350unless ($manual_cdrecord_opts) {
351   # if the datastream is in little-endian order, we need to
352   # add the swab flag to cdrecord if it's not already present
353
354   if (!($cdrecord_opts =~ /.*-swab.*/)) {
355      # assert: swab wasn't set
356      # check to see if it's needed
357      chop ($arch = `/usr/bin/uname -m`);
358      if ($DEBUG) { print "arch=$arch\n"; }
359
360      if ($arch =~ /i[3456]86/ || $arch =~ /amd64/ || $arch =~ /ppc/ ) {	# ia32 - we need to swab
361	 $cdrecord_opts .= " -swab";
362         if ($DEBUG) { print "-swab flag automatically added\n"; }
363      } #elsif () {}			# what other arch's need this?
364   }
365}
366
367=item B<-a, --atip>
368
369Lookup the ATIP info for the device in the cdburner
370(using L<cdrecord(1)> C<-atip>) and then exits.  This
371option can only be used (successfully) in conjuction with B<-o>.
372
373=cut 
374
375# process -a (ATIP) flag
376if ($atip) { 	# We just want to see how much time the disk has
377	if ($check || $dummy || $tempdir) {
378		#The -a option is mutually exclusive of all but the -o
379		#"cdrecord options" switch
380		$errmsg = "The '-a' ATIP check cannot be used with any other switches\n";
381		$errmsg .= "You may use '-c ATIP' to automatically use disk ATIP info, however.";
382		die $errmsg;
383	} else {
384		get_ATIP_info(); #Let's get the ATIP info and bail
385		exit 0;
386	}
387}
388
389
390unless ($playlist) {
391   # display usage if there is no playlist and no audio filename args or if -h is ommitted
392   if (! @ARGV || $help) {
393	pod2usage(1);
394   }
395}
396
397# check to see if mpg123 is present
398#
399# since this package depends on mpg321, we can count on
400# /usr/bin/mpg123 being a link to /etc/alternatives and then mpg321
401# by default - for the time being check for the debian install of
402# mpg123 in mpg123-oss <grumble>
403
404
405=item B<-M, --encoder> "MP3 decoder"
406
407Use an MP3 decoder other than the default, which is mpg321.  This is
408imperative when burning tracks that have sample rates other than 44.1kHz,
409and the current version of mpg321 will not decode these files.  Specify
410the name of the decoder to be used, e.g. F<mpg123-oss-3dnow>;  you can
411also specify this in  your .mp3burnrc file with B<$mp3decoder
412=> F<mp3decoder>.  I<(Note:  Currently, the MP3 decoder must be
413able to accept mpg123-style command-line arguments.)>
414
415=cut
416
417$mp3decoder = $encoder if $encoder;		# -M overrides .mp3burnrc
418if ($mp3decoder) {
419   $mp3decoder = `which $mp3decoder`;
420   chop $mp3decoder;
421   die "Cannot locate MP3 decoder -> $encoder" unless (-x $mp3decoder);
422}
423#No mp3decoder choosed ? We use our default
424
425if (! $mp3decoder) { $mp3decoder = "mpg123"; }
426if (! `which $mp3decoder`) {
427	print "$mp3decoder not found...\n";
428	print "Try mpg123: ";
429	if (`which mpg123`) {
430		print "found\n";
431		$mp3decoder = `which mpg123`;
432	} else {
433		print "not found\n";
434		print "Try mpg321: ";
435		if (`which mpg321`){
436			print "found\n";
437			$mp3decoder = `which mpg321`;
438		} else {
439			print "not found\n";
440			die "No mpg123 compatible player found";
441		}
442	print "Using $mp3decoder as mp3 decoder\n";
443	}
444} else {
445	$mp3decoder = `which $mp3decoder`;
446}
447chop $mp3decoder;
448
449# process the playlist - push these files onto ARGV
450if ($playlist) {
451   shift
452   my @playlist_files;
453   open (PL, $playlist) || die "cannot open playlist $playlist";
454
455   while (<PL>) {
456      # skip over comments/headers, others lines should be filenames
457      next if ($_ =~ /^#/);
458      chomp;
459      if (-r $_) {
460         unshift (@playlist_files, $_);
461      } else {
462         print "file not found - skipping playlist file $_";
463      }
464   }
465   close (PL);
466
467   foreach $element (@playlist_files) {
468      quotemeta($element);
469      unshift (@ARGV, $element);
470   }
471}
472
473#############################################
474# loop over the audio filenames in ARGV
475#############################################
476for ($i = 0; $i <= $#ARGV; $i++) {
477	die "$ARGV[$i] does not exist or invalid audio file" unless (-f $ARGV[$i]); #Check to see if file exists
478	if (-l $ARGV[$i]) {		#mp3info doesn't work on symlinks
479	    $file = readlink $ARGV[$i];
480	} else {
481	    $file = $ARGV[$i];
482	}
483
484	# 2002/11/10 <tmancill@debian.org>
485	# moved get_audio_info up to avoid creating FIFO when we don't
486	# have a valid audio file to work with
487	$info = get_audio_info $file; #Let's get the mp3's time
488	unless ($info) {
489	   print "skipping file: $file - not a valid MP3, OGG, or FLAC file, or decoder is not installed!\n";
490	   next;
491	}
492
493
494        if ($DEBUG) {
495	   print "creating FIFO for audio file: $file\n";
496	}
497
498	$fifo[$i] = $tmpdir . basename $ARGV[$i];	#set the names of the fifos
499	$fifo[$i] =~ s/$/.cdr/i;		#foo.mp3 -> foo.mp3.cdr
500
501	if ($sec) {
502	    $totsecs += $info->{SECS} + 2; # total time + 2 for padding
503	}
504	system "mkfifo", $fifo[$i];	#Make our fifos (optionally to the tempdir)
505 	# 2000/11/21 <tmancill@debian.org>
506	# beef up the fork() code - example taken from camel book
507	FORK:
508        if ($pid = fork) {
509           # we're in the parent here, child pid in $pid
510           # we could use a list, but we're lazy and know how many procs
511           # there will be, so use an array
512           push @children, $pid;
513        } elsif (defined $pid) {
514           # if $pid is defined, it's == 0
515           #start decoder processes
516
517	   if ($DEBUG) {
518	   	print "Decoder: @{$info->{DECODER}} File: $ARGV[$i]\n";
519	   }
520	   close(STDOUT);
521	   open(STDOUT, ">$fifo[$i]");	#this to avoid using the shell
522	   exec(@{$info->{DECODER}},  $ARGV[$i]);
523	   die "Failed to exec \`".join(" ",@{$info->{DECODER}})."\': $!";
524	} elsif ($! =~ /No more process/) {
525           # EAGAIN, supposedly recoverable fork error
526           sleep 5;
527           redo FORK;
528        } else {
529           # weird fork error
530           die "Can't fork: $!\n";
531        }
532}
533
534#If we have no valid files left we should die
535
536die "No valid files to burn left. Exiting\n" unless @fifo;
537
538$totmin=int $totsecs/60;
539$totsec=$totsecs % 60;
540if (($totsecs > (($min*60)+$sec)) && $sec) {
541	printf "The max time allocated was [$min:%02d].\n", $sec;
542	printf "The total time came to [$totmin:%02d]\n", $totsec;
543	print "Do you wish to continue? (Y/N) ";
544	while (1) {
545		$key=uc(getc);
546		if ($key eq 'N') {
547			unlink @fifo;
548   			print "cleaning up children: @children\n";
549   			kill 'TERM', @children;
550			exit 1;
551		}
552		last if ($key eq 'Y');
553	}
554}
555if ($sec){
556	printf "\nTotal time is [$totmin:%02d] of [$min:%02d] available calculated\n\n", $totsec;
557	sleep 3;
558}
559
560# prepare the command line
561# We use now the shellquote module for escaping the filenames
562$cdrecordcmd = "cdrecord " . join (" ", split(/\s+/, $cdrecord_opts)) . " " . shell_quote @fifo;
563
564if ($DEBUG) {
565   print "invoking cdrecord with:\n";
566   print "$cdrecordcmd\n";
567}
568
569# burn!
570$rc = system "$cdrecordcmd";
571
572# check the return code from cdrecord
573if ($rc != 0) {
574   # cdrecord exited non-zero
575   print "warning:  cdrecord exited non-zero!\n";
576}
577
578&Cleanup;
579
580exit 0;
581
582
583=head1 RETURN VALUE
584
585B<mp3burn> returns 0 on success.
586
587=head1 DIAGNOSTICS
588
589=item Error in .mp3burnrc:
590
591Perl(1) cannot parse the F<.mp3burnrc> file.
592The following example occurs when a double quote is not terminated:
593
594  bash-2.05$ sudo mp3burn -d ~/bell.ogg
595  String found where operator expected at (eval 10) line 7, at end of line
596	  (Missing operator before ?)
597  Error in .mp3burnrc:
598  Can't find string terminator '"' anywhere before EOF at (eval 10) line 7.
599  bash-2.05$
600
601You will experience this error if you define both $cdrecord_opts and
602$mp3decoder without terminating the variable assignments with the ';'
603character:
604
605  $ mp3burn -d -t /tmp Theodor_Storm_Aquis_submersus_1.mp3
606  Scalar found where operator expected at (eval 10) line 2, near ""-v speed=2 dev=0,3,0"
607  $mp3decoder"
608        (Missing operator before
609  $mp3decoder?)
610  Error in .mp3burnrc:
611  syntax error at (eval 10) line 2, near ""-v speed=2 dev=0,3,0"
612  $mp3decoder"
613
614=back
615
616=head1 EXAMPLES
617
618Write an Ogg Vorbis file from a CD-R drive, F</dev/scd0>,
619mounted at F</mnt/scd0/> to a
620CD-RW drive, F</dev/scd1>, called C<0,1,0> in cdrecord(1) SCSI notation.
621Ensure that file is no longer than 50 minutes.
622L<sudo(1)> is used to get root permissions for cdrecord(1).
623
624  % sudo mp3burn -c 050:00 -o "-v speed=2 dev=0,1,0" /mnt/scd0/bell.ogg
625
626Create a F<~/.mp3burnrc> that prints a message before writing and uses
627a different MP3 decoder than the default of mpg321.
628
629  # This is an example.
630  $cdrecord_opts="-v speed=2 dev=0,1,0";
631  $mp3decoder = "mpg123-oss-3dnow";
632
633  print "Nine seconds to slap a CD-R in the drive!\n" ;
634
635  #
636  # See mp3burn(3).
637  #
638
639Specify an mp3decoder other than mpg321.
640
641  $ sudo mp3burn -M mpg123-esd ./rush/*mp3
642
643=head1 FILES
644
645=over 4
646
647=item F<~/.mp3burnrc>
648
649In this file, you may permanently specify the cdrecord options and
650MP3 decoder you want to use.  The format is:
651
652  $cdrecord_opts = "cdrecord options";
653  $mp3decoder = "some mp3 decoder";
654
655You may place comments in this file by beginning a line with C<#>.
656
657=item B<Note:>
658
659The values of $cdrecord_opts and $mp3decoder in F<~/.mp3burnrc> are ignored
660if the C<-o> or C<-<>command-line options are used, respectively.
661
662=back
663
664=head1 CAVEATS
665
666Has not been tested extensively with Ogg Vorbis files.
667Ogg Vorbis files must be in CD-DA format:
668i.e. 44100 samples/channel x 16 bits/sample x 2 channels.
669
670=head1 BUGS
671
672If you execute B<mp3burn> with root permissions,
673the F<~/.mp3burnrc> will also be executed with root permissions.
674
675=head1 NOTES
676
677There are a number of GUI frontends for B<mp3burn>:
678
679=over
680
681=item Xmp3Burn
682
683http://perso.wanadoo.es/ja_recio/xmp3burn/xmp3burn.html
684
685=item Kmp3burn
686
687http://computer.freepage.de/kmp3burn/index.htm
688
689=item GtkMp3Burn
690
691http://gtkmp3burn.sourceforge.net/
692
693=back
694
695=head1 SEE ALSO
696
697cdrecord(1), mpg321(1), ogg123(1), ogginfo(1), flac(1), L<MP3::Info(3)>
698The B<mp3burn> web page is http://mp3burn.sourceforge.net/.
699The Ogg Vorbis web page is http://www.xiph.org/ogg/vorbis/.
700The FLAC web page is http://flac.sourceforge.net/.
701
702=head1 AUTHOR
703
704Copyright (c) 2000 Ryan Richter.
705Copyright (c) 2003 Alexander Wirt
706
707This script was written by Ryan Richter <bobort@bigfoot.com> with much code
708contributed by Dan Lark <dlark@spinn.net>.
709
710I would like to thank Dan Lark <dlark@spinn.net> for
711contributing the ideas and code for most of the new features,
712and Tony Mancill <tony@mancill.com> for making Debian packages
713and helping with debugging.
714
715Later in 2003 Alexander Wirt continued to write this program.
716
717This program is licensed under the GNU General Public License.
718You may fold, spindle, and mutilate this software under the terms of the GPL.
719
720=head1 HISTORY
721
722=over
723
724=item  20031014 <formorer@formorer.de>
725
726Switches from standard getopt to GetOpts::Long
727
728Updated Manpage for the new option format
729
730=item  20031012 <formorer@formorer.de>
731
732Updated mp3burn to use pod2usage
733
734Added -h switch for getting help
735
736Fixed a bug with the output of file in conduction with FLAC files
737
738Added a gracefully exit if there are no valid files left
739
740Use String::Shellquote to avoid problems with the shell
741
742=item  20030203 <tmancill@debian.org>
743
744hacked in support for FLAC, as per suggested by <ldm@apartia.org>
745
746=item  20021110 <tmancill@debian.org>
747
748updated to work with new ogginfo output format in vorbis-tools 1.0;
749modified slightly to not create FIFOs for invalid MP3/OGG files
750
751=item  20020728 <tmancill@debian.org>
752
753added I<-M $mp3decoder> switch to support MP3 decoders other than
754mpg321 and mpg123-oss
755
756=item  20010917	<tmancill@debian.org>
757
758added check to automatically add -swab on ia32 platform
759
760L<MP3::Info(3)> replaces MPEG::MP3Info
761
762=item B<mp3burn> 0.02 10/28/00
763
764Changes since 0.01:
765
766Bugfixes:
767
768Spaces, quotes, and other shell metacharacters in filenames
769should no longer problematic, since we use Shell:QuoteString
770to avoid problems with that (Feedback for that is wished).
771
772Mono MP3s and MP3s not sampled at 44.1kHz are no longer
773problematic.
774
775New Features:
776
777Editing the executable is no longer necessary; cdrecord
778options can be specified on the command line or in F<~/.mp3burnrc>.
779
780Temp dir for FIFOs may be set to other than the current dir.
781
782Dummy runs now supported from the command line.
783
784A time check is now available: B<mp3burn> can abort if total
785time exceeds a threshold.  Requires L<MPEG::MP3Info>.
786
787Playlist support.
788
789=back
790
791=cut
792
793