mp3cd-1.027/0000755000076500007650000000000011515337451011210 5ustar keeskeesmp3cd-1.027/scripts/0000755000076500007650000000000011515337451012677 5ustar keeskeesmp3cd-1.027/scripts/mp3cd0000755000076500007650000007272111515337412013641 0ustar keeskees#!/usr/bin/perl use strict; use warnings; use Getopt::Long qw(:config no_ignore_case bundling); use File::Path; use Pod::Usage; use Cwd ('abs_path'); our $VERSION = 1.027_000; =pod =head1 NAME mp3cd - Burns normalized audio CDs from lists of MP3s/WAVs/Oggs/FLACs =head1 SYNOPSIS mp3cd [OPTIONS] [playlist|files...] -s, --stage STAGE Start at a certain stage of processing: clean Start fresh (default, requires playlist) build Does not clean (requires playlist) decode Turns MP3s/Oggs/FLACs into WAVs correct Fix up any WAV formats norm Normalizes WAV volumes toc Builds a Table of Contents from WAVs toc_ok Checks TOC validity cdr_ok Checks for a CDR burn Burns from the TOC -q Quits after one stage of processing -t, --tempdir DIR Set working dir (default "/tmp/mp3cd-$USER") -d, --device PATH Look for CDR at "PATH" (default "/dev/cdrecorder") -r, --driver TYPE Use CDR driver TYPE (default up to cdrdao) -n, --simulate Don't actually burn a disc but do everything else. -E, --no-eject Don't eject drive after the burn. -L, --no-log Don't redirect output to "tool-output.txt" -T, --no-cd-text Don't attempt to write CD-TEXT tags to the audio CD -c, --cdrdao ARGS Pass the option string ARGS to cdrdao. -S, --skip STAGES Skip the comma-separated list of stages in STAGES. -V, --version Report which version of the script this is. -v, --verbose Shows commands as they are executed. -h, --usage Shows brief usage summary. --help Shows detailed help summary. --longhelp Shows complete help. =head1 OPTIONS =over 8 =item B<-s STAGE>, B<--stage STAGE> Starts processing at a given stage. This is used in case you had to stop processing, or a file was missing, or things generally blew up. It is especially useful if a burn fails because then you don't have to start totally over and re-WAV the files. If you just want to perform a single step, use B<--quit> to abort after the stage you request with B<--stage>. Also see B<--skip>. =over 8 =item B This is the default starting stage. The temp directory is cleared out. A playlist is required, since we expect to move to the B stage next, which requires it. =item B This stage examines the playlist from the command line, and tries to create a list of symlinks from the given playlist. So far, C can understand ".m3u" files, XMLPlaylist files, and lists of files. =item B All the files are converted into WAVs. So far, C knows how to decode MP3, Ogg, and FLAC files. (WAVs will be left as they are during this stage.) =item B The WAV files are corrected to have the correct bitrate and number of channels, as required for an audio CD. =item B The WAV files' volumes are normalized so any large differences in volume between records will be less noticeable. =item B Generates a Table of Contents for the audio CD. =item B Validates the TOC, just in case something went really wrong with the WAV files. =item B Verifies that there is a CDR ready for burning. =item B Actually performs the burn of all the WAV files to the waiting CDR. =back =item B<-q>, B<--quit> Aborts after one stage of processing. See B<--stage>. =item B<-t DIR>, B<--tempdir DIR> Use a working directory other than "/tmp/mp3cd-B". This is where all the file processing occurs. You will generally need at least 650M free here (or more depending on the recording length of your destination CD). =item B<-d PATH>, B<--device PATH> Use a device path other than "/dev/cdrecorder". =item B<-r TYPE>, B<--driver TYPE> Use a CDRDAO driver other than what cdrdao automatically detects. Note that some drivers may not support CD-TEXT mode. In this case, try "generic-mmc-raw". =item B<-c ARGS>, B<--cdrdao ARGS> Pass the given option string of ARGS to cdrdao during each command. =item B<-n>, B<--simulate> Do not actually write to the disc but simulate the process instead. =item B<-E>, B<--no-eject> Don't eject drive after the burn. =item B<-L>, B<--no-log> Don't redirect output to "tool-output.txt". All information will instead be redirected to the terminal via standard output (STDOUT). This will cause a lot of low-level detail to be displayed. =item B<-T>, B<--no-cd-text> Don't attempt to write CD-TEXT tags to the audio CD. Some devices and drivers do not support this mode. See B<--driver> for more details. =item B<-S STAGES>, B<--skip STAGES> While processing, skips the stages listed in the comma-separated list of stages given in STAGES. This would only be used if you really know what you're doing. For example, if the audio is already normalized and you didn't want to burn a CD, you could skip the normalizing and burning stages by giving "--skip norm,burn". See B<--stage> and B<--quit>. =item B<-V>, B<--version> Report which version of mp3cd this is. =item B<-v>, B<--verbose> Shows commands as they are executed. =item B<-h>, B<--usage> Show brief usage summary. =item B<--help> Show detailed help summary. =item B<--longhelp> Shows the full command line instructions. =back =head1 DESCRIPTION This script implements the suggested methods outlined in the Linux MP3 CD Burning mini-HOWTO: L This will burn a playlist (.m3u, XMLPlaylist or command line list) of MP3s, Oggs, FLACs, and/or WAVs to an audio CD. The ".m3u" format is really nothing more than a list of fully qualified filenames. The script handles making the WAVs sane by resampling if needed, and normalizing the volume across all tracks. If a failure happens, earlier stages can be skipped with the '-s' flag. The file "tool-output.txt" in the temp directory can be examined to see what went wrong during the stage. Some things are time-consuming (like decoding the audio into WAVs) and if the CD burn fails, it's much nicer not to have to start over from scratch. When doing this, you will not need the m3u file any more, since the files have already been built. See the list of stages using '-h'. =head1 PREREQUISITES Requires C, and that /dev/cdrecorder is a valid symlink to the /dev/sg device that cdrdao will use. Use .cdrdao to edit driver options. (See "man cdrdao" for details.) Requires C to decode MP3 and check/correct WAV formats. http://www.spies.com/Sox/ Requires C to process the audio. http://www.cs.columbia.edu/~cvaill/normalize/ Optionally requires C to decode Ogg to WAV files. http://www.gnu.org/directory/audio/ogg/OggEnc.html/ Optionally requires C to decode flac to WAV files. http://flac.sourceforge.net/ Optionally requires C Perl module if you want to use the .mp3cdrc file. http://search.cpan.org/~sherzodr/Config-Simple/ =head1 FILES =over 8 =item B<~/.mp3cdrc> Default options can be recorded in this file. The option names are the same as their command line long-name. Command line options will override these values. All options are run through perl's eval. For example: tempdir: /scratch/mp3cd/$ENV{'USER'} device: /dev/burner =back =head1 AUTHOR Kees Cook Contributors: J. Katz (Ogg support) Alex Rhomberg (XMLPlaylist support) Kevin C. Krinke (filelist inspiration, and countless many patches) James Greenhalgh (flac support) =head1 SEE ALSO perl(1), cdrdao(1), sox(1), oggdec(1), flac(1), sox(1), normalize(1). =head1 COPYRIGHT Copyright (C) 2003-2011 Kees Cook kees@outflux.net, http://outflux.net/ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. http://www.gnu.org/copyleft/gpl.html =cut # Change this to a location where you'll have at least a CD's worth of # disk space available. (For the WAVs) # Its contents will be deleted, so be careful. :) my $BURNDIR="/tmp/mp3cd-".getpwuid($<); # Filename to redirect sub-tool stdout/stderr my $LOG="tool-output.txt"; # Filename to write the TOC to my $CDTOC="cdda.toc"; # Filename to write tag info to my $TAGS="tag.data"; # List of audio files to burn (useful only for the "build" stage) my @FILES=(); my %stage_func = ( "clean" => \&Do_Clean, "build" => \&Do_Build, "decode" => \&Do_Decode, "correct" => \&Do_Correct, "norm" => \&Do_Normalize, "toc" => \&Do_TOC, "toc_ok" => \&Do_TOC_Verify, "cdr_ok" => \&Do_CDR_Check, "burn" => \&Do_Burn, ); my $UNKNOWN="unknown-format"; my %decoders = ( "flac" =>{ 'require' => 'flac', 'args' => '--silent -d -F $input -o $output', 'normal' => '--silent', 'verbose' => '', }, "ogg" => { 'require' => 'oggdec', 'args' => '$input -o $output', 'normal' => '--quiet', 'verbose' => '', }, "mp3" => { 'require' => 'sox', 'args' => '$input $output', 'normal' => '', 'verbose' => '-v', }, "m4a" => { 'require' => 'faad', 'args' => '-o $output $input', 'normal' => '--quiet', 'verbose' => '', }, $UNKNOWN => { 'require' => 'mplayer', 'args' => '-hardframedrop -vc null -vo null -ao pcm:fast:file=$output $input', 'normal' => '-quiet', 'verbose' => '', }, # Dummy entry to recognize WAVs "wav" => { 'require' => 'sox', }, ); my @stages; my %stages; my $count=0; my $stage; foreach $stage (qw(clean build decode correct norm toc toc_ok cdr_ok burn)) { push(@stages,$stage); $stages{$stage}=$count++; } our $opt_help=undef; our $opt_longhelp=undef; our $opt_usage=undef; our $opt_version=undef; our $opt_quit=undef; our $opt_stage="clean"; our $opt_tempdir=undef; our $opt_cdrdao=""; our $opt_device="/dev/cdrecorder"; our $opt_driver=undef; our $opt_simulate=undef; our $opt_no_eject=0; our $opt_no_log=0; our $opt_no_cd_text=0; our $opt_skip=""; our $opt_verbose=0; my @options=( 'help', 'longhelp', 'usage|h', 'version|V', 'verbose|v', 'stage|s=s', 'skip|S=s', 'quit|q', 'tempdir|t=s', 'device|d=s', 'driver|r=s', 'cdrdao|c=s', 'simulate|n', 'no-eject|E', 'no-log|L', 'no-cd-text|T', ); # Look for RC defaults my %rc; my $rcfile="$ENV{'HOME'}/.mp3cdrc"; if (-r $rcfile) { require Config::Simple; Config::Simple->import_from($rcfile,\%rc); } foreach my $opt (@options) { my ($name) = $opt =~ /^([^|]+)/; $name=~s/-/_/g; my $is_str = $opt =~ /=s$/ || 0; if (defined($rc{$name})) { eval "\$opt_$name = \"$rc{$name}\";"; if (!$is_str) { eval "\$opt_$name = \$opt_$name ? 1 : 0;"; } } } # Load command line options GetOptions(@options) or pod2usage( -exitval=>1, -verbose=>0 ); # Handle help/usage pod2usage( -exitval=>0, -verbose=>2 ) if ($opt_longhelp); pod2usage( -exitval=>0, -verbose=>1 ) if ($opt_help); pod2usage( -exitval=>0, -verbose=>0 ) if ($opt_usage); Version() if ($opt_version); # cdrdao needs to pick up device and driver from the command line $opt_cdrdao .= " --device $opt_device"; $opt_cdrdao .= " --driver $opt_driver" if (defined($opt_driver)); # Validate starting stage if (!defined($stages{$opt_stage})) { pod2usage( -exitval=>1, -verbose=>0, -msg=>"Unknown start stage '$opt_stage'!" ); } $stage=$opt_stage; # Check if we need (or do not need) a playlist/filelist if ($stage eq "clean" || $stage eq "build") { if (!defined($ARGV[0])) { pod2usage( -exitval=>1, -verbose=>0, -msg=>"Playlist/File list is required!" ); } } elsif (@ARGV) { pod2usage( -exitval=>1, -verbose=>0, -msg=> "Playlists/Files are ignored past stage 'build'!" ); } # Build a hash of the stages to skip my %skip_stage; foreach my $skip (split(/,/,$opt_skip)) { if (!defined($stages{$skip})) { pod2usage( -exitval=>1, -verbose=>0, -msg=>"Unknown stage to skip '$skip'!" ); } $skip_stage{$skip}=1; } # Skip all the stages after the selected one, in case of "--quit" my $cancel_rest = 0; foreach my $last (@stages) { if ($cancel_rest) { $skip_stage{$last}=1; } if ($opt_quit && $last eq $stage) { $cancel_rest = 1; } } # Figure out our burning directory $BURNDIR=$opt_tempdir if (defined($opt_tempdir)); # check for directory if (!opendir(DIR, $BURNDIR)) { eval { mkpath($BURNDIR) }; if ($@) { die "Can't create working directory '$BURNDIR': $@\n"; } opendir(DIR, $BURNDIR) || die "Can't open directory '$BURNDIR': $!\n"; } closedir DIR; # if no_log print all to stdout my $OUTPUT = ( $opt_no_log ) ? "" : ">>$LOG"; sub System { my $cmd = $_[0]; print STDERR $cmd."\n" if $opt_verbose; return system($cmd); } sub Backtick { my $cmd = $_[0]; print STDERR $cmd."\n" if $opt_verbose; # Cannot pipe to "tee" since it will mask exit codes my $output = `$cmd 2>&1`; my $rc = $?; my $logfile; open($logfile, ">>$LOG") or die "Cannot write to $LOG: $!\n"; print $logfile $output; close($logfile); print $output if ($opt_no_log); return $rc, $output; } # For-sure needed tools my %PREREQS = ( 'sox' => 'sox', 'cdrdao' => 'cdrdao', 'gst-launch' => 'gst-launch', ); $PREREQS{'normalize'} = 'normalize,normalize-audio' if (!defined($skip_stage{'norm'})); my %found; sub Lookup_tools { # check for required tools foreach my $requirement (sort keys %PREREQS) { foreach my $dir (split(/:/,$ENV{'PATH'})) { foreach my $prog (split(/,/,$PREREQS{$requirement})) { if (!defined($found{$requirement}) && -x "$dir/$prog") { $found{$requirement}="$dir/$prog"; last; } } } } my $abort=undef; foreach my $requirement (sort keys %PREREQS) { if (!defined($found{$requirement})) { my $tried = "Tried: ".$PREREQS{$requirement}; $tried =~ s/,/, /g; warn "Cannot find program to handle '$requirement'! $tried\n"; $abort=1; } } return $abort; } # Load file list, update needed tools Load_file_list(); pod2usage( -exitval => 1, -verbose => 0 ) if (Lookup_tools()); # check for CDR device my $skip_cdr = defined($skip_stage{'cdr_ok'}) && defined($skip_stage{'burn'}); if (!$skip_cdr && ! -w $opt_device) { pod2usage( -exitval=>1, -verbose=>0, -msg=> "Cannot write to '$opt_device'!" ); } # Run through all the stages we need to... for (; defined($stage) && defined($stages{$stage}); $stage=$stages[$stages{$stage}+1]) { if (defined($skip_stage{$stage})) { print "Skipping '$stage' stage...\n"; next; } $stage_func{$stage}->(); } # end of line exit(0); ### Functions sub require_extension($$) { my ($ext,$file) = @_; my $lookup = $ext; if (!defined($decoders{$lookup})) { # Unknown audio file format print STDERR "Not sure how to handle file type '$ext' ($file),\n"; print STDERR "falling back to ".$decoders{$UNKNOWN}->{'require'}.".\n"; $lookup = $UNKNOWN; } $PREREQS{"decoder:$lookup"}=$decoders{$lookup}->{'require'}; } sub Load_file_list { # Keep a count of how many files we've examined, and stop after, say, # 1000, in case an m3u lists itself (which is REALLY unlikely, but would # effectively put this code into a memory-eating endless loop). my $toomany=1000; while (my $file=shift @ARGV) { $file =~ m/\.([^\.]+)$/i; my $ext = lc($1 || ""); if ($ext eq "m3u" || $ext eq "pls" || $ext eq "xspf" || $ext eq "") { # Playlist open(M3U,$file) || die "Cannot open '$file': $!\n"; my @lines=; close(M3U); my @files; if (scalar(@lines) && $lines[0] =~ //i) { # kaffeine playlists require XML::Simple; my $contents = XML::Simple::XMLin($file); if (ref($contents->{entry}) eq 'ARRAY') { @files = map {$_->{url}} @{$contents->{entry}}; s/^file:// for @files; } else { @files = ($contents->{entry}->{url}); } } else { # regular list of files foreach (@lines) { chomp; next if (/^#/); push(@files,$_); } } unshift(@ARGV,@files); } else { require_extension($ext,$file); push(@FILES,$file); } die ">1000 files in the list?! I must have started looping forever.\n" if (--$toomany<0); } # Get absolute locations @FILES = map { abs_path($_) } @FILES; } sub Do_Clean { print "Cleaning up...\n"; # clear out burn dir my @list = ("$BURNDIR/$CDTOC","$BURNDIR/$LOG", "$BURNDIR/$TAGS"); foreach my $ext ("wav", sort keys %decoders) { push(@list,"$BURNDIR/*.$ext"); } System("rm -f ".join(" ",@list)); } sub append_tag_info($$$) { my ($media, $title, $path) = @_; my $artist = ""; my ($rc, $output) = Backtick("gst-launch -t filesrc location=$media ! decodebin"); die "Could not extract tags: $!\n" if ($rc != 0); my $tags = 0; # Parse gst-launch -t output # FOUND TAG : found by element "qtdemux0". # title: Just Dance # artist: Lady GaGa & Colby O'Donis foreach my $line (split("\n", $output)) { if ($line =~ /^FOUND TAG/) { $tags = 1; next; } next if ($tags != 1); if ($line =~ /^\S/) { $tags = 0; next; } my ($field, $value) = $line =~ /^\s*(\S*)\s*:\s*(.*)$/; next if (!defined($field)); $title=$value if ($field eq "title"); $artist=$value if ($field eq "artist"); } my $tagfile; open($tagfile,">>$TAGS") or die "Cannot write to $TAGS: $!\n"; print $tagfile "$title\n"; print $tagfile "$artist\n"; if ($opt_verbose) { print "\ttitle: $title\n"; print "\tartist: $artist\n"; } } sub Do_Build { # go there chdir($BURNDIR) || die "Cannot chdir('$BURNDIR'): $!\n"; # Clear the tag file, since we're regenerating it System("rm -f $TAGS"); my $error=undef; my $count=0; # make link for each file, and retain extension foreach my $file (@FILES) { chomp($file); next if ($file =~ /^#/); my @parts=split(/\./,$file); my $ext=lc(pop(@parts)); $ext=~tr/A-Z/a-z/; @parts=split(/\//,$file); my $name=pop(@parts); if (!defined($decoders{$ext}) && !defined($decoders{$UNKNOWN})) { warn "Error: '$file': unknown extension '$ext'!\n"; $error=1; next; } if (!-f $file) { warn "Error: '$file': $!\n"; $error=1; next; } $count++; my $track=sprintf("%02d",$count); print "$track: [...]/$name\n"; symlink($file,"$track.$ext") || die "symlink('$file','$count.$ext'): $!\n"; append_tag_info("$track.$ext", $name, $file); } die "Stopping due to errors...\n" if (defined($error)); # make sure we have some tracks die("No tracks?!\n") unless ($count>0); } sub Do_Decode { # go there chdir($BURNDIR) || die "Cannot chdir('$BURNDIR'): $!\n"; # leave any WAVs in playlist alone opendir(DIR, $BURNDIR) || die "Can't read directory '$BURNDIR': $!\n"; my @need_decode = grep { /^\d+\.[^\.]+$/i && !/\.wav$/ && -f "$BURNDIR/$_" } readdir(DIR); closedir DIR; # Re-check extensions and tools in case we're restarting foreach my $to_decode (sort {$a cmp $b} @need_decode) { my @parts=split(/\./,$to_decode); my $name=shift(@parts); my $ext=pop(@parts); require_extension($ext, $to_decode); } die "Cannot locate needed decoders\n" if (Lookup_tools()); # decode audio into WAV files foreach my $to_decode (sort {$a cmp $b} @need_decode) { my @parts=split(/\./,$to_decode); my $name=shift(@parts); my $ext=pop(@parts); my $file="${name}.wav"; if (-f $file) { print "Skipping track $name: $file exists.\n"; } else { print "Creating WAV for track $name ...\n"; my $lookup = $ext; if (!defined($decoders{$lookup})) { $lookup = $UNKNOWN; } my $decoder = $decoders{$lookup}; if (!defined($decoder)) { die("No decoder available for extension '$ext' - decoding failed!\n"); } my @cmd = ($found{"decoder:$lookup"}); # chose verbosity level if (!$opt_no_log) { push(@cmd,$decoder->{'normal'}); } else { push(@cmd,$decoder->{'verbose'}); } # set up arguments my $input = $to_decode; my $output = $file; push(@cmd,eval "return \"$decoder->{'args'}\""); # run decoder (don't need to worry about arg splits since we're # operating against symlinked files with known names, etc) my $cmd = join(" ",@cmd); # redirect logging $cmd="$cmd $OUTPUT 2>&1"; System($cmd) == 0 or die("Decoding failed!\n"); } } } sub Do_Correct { # go there chdir($BURNDIR) || die "Cannot chdir('$BURNDIR'): $!\n"; # get list of wavs from directory opendir(DIR, $BURNDIR) || die "Can't read directory '$BURNDIR': $!\n"; my @wavs = grep { /^\d+\.wav$/i && -f "$BURNDIR/$_" } readdir(DIR); closedir DIR; # correct any wav file formats foreach my $wav (sort {$a cmp $b} @wavs) { my @parts=split(/\./,$wav); my $name=shift(@parts); print "Checking WAV format for track $name ...\n"; my $report=`sox -V $wav $wav.raw trim 0.1 1 2>&1`; my ($channels, $frequency, $samples); if ($report =~ /^Input File/m) { # In version 13.0.0, the report format has changed # Sample Size : 8-bit (1 byte) # Channels : 1 # Sample Rate : 11025 $report =~ m/Sample (?:Size|Encoding)\s*:\s+(\d+)-bit/s or die "sox did not report sample size:\n$report"; $samples = $1; $report =~ m/Channels\s+:\s+(\d+)/s or die "sox did not report channel count:\n$report"; $channels = $1; $report =~ m/Sample Rate\s+:\s+(\d+)/s or die "sox did not report sample frequency:\n$report"; $frequency = $1; } else { # sox: Reading Wave file: Microsoft PCM format, 2 channels, # sox: 44100 samp/sec 176400 byte/sec, block align, 16 bits/samp, # sox: 44886528 data bytes $report =~ m|(\d+) channels?|s or die "sox did not report channel count:\n$report"; $channels = $1; $report =~ m|(\d+) samp/sec|s or die "sox did not report sample frequency:\n$report"; $frequency = $1; $report =~ m|(\d+) bits/samp|s or die "sox did not report sample size:\n$report"; $samples = $1; } unless ($channels == 2 && $frequency == 44100 && $samples == 16) { # only do a "resample" if frequency isn't correct my $resample="resample"; $resample="" if ($frequency == 44100); print "Correcting WAV format for track $name ...\n"; System("sox $wav -r 44100 -c 2 new-$wav $resample $OUTPUT 2>&1") == 0 or die("Correction failed!\n"); unlink($wav) || die "unlink('$wav'): $!\n"; rename("new-$wav",$wav) || die "rename('new-$wav','$wav'): $!\n"; } unlink("$wav.raw"); } } sub Do_Normalize { # go there chdir($BURNDIR) || die "Cannot chdir('$BURNDIR'): $!\n"; # normalize the volumes print "Normalizing volume levels...\n"; System("$found{'normalize'} -m [0-9]*.wav") == 0 or die("Normalizing failed!\n"); print "Normalizing finished.\n"; } sub encode_cd_text_data($) { my ($data) = @_; my $encoded = ""; # Handle backslash and quotes $data =~ s/\\/\\\\/g; $data =~ s/"/\\"/g; # Using the binary data method seems to fail (missing trailing 0?) # if ($data =~ /"/) { # $encoded = "{ " . join(", ",map(ord, split(//,$data))) . " }"; # } # else { $encoded = "\"" . $data . "\""; # } return $encoded; } sub cd_text($$) { my ($title, $artist) = @_; chomp($title); chomp($artist); my $text = "CD_TEXT {\n LANGUAGE 0 {\n"; $text .= " TITLE " . encode_cd_text_data($title) . "\n"; $text .= " PERFORMER " . encode_cd_text_data($artist) . "\n"; $text .= " }\n}\n"; return $text; } sub Do_TOC { # go there chdir($BURNDIR) || die "Cannot chdir('$BURNDIR'): $!\n"; print "Generating CDR Table of Contents...\n"; # Get ready to read tags my $tagfile; open($tagfile,"<$TAGS") || die "Cannot read $TAGS: $!\n"; # create a TOC for cdrdao open(TOC,">$CDTOC") || die("Cannot write to '$CDTOC': $!\n"); print TOC "CD_DA\n"; if (!$opt_no_cd_text) { # CDRDAO wants title/performer for the cd itself too, so leave them blank print TOC <), scalar(<$tagfile>)); } # The trailing space was (is?) needed for some versions of cdrdao print TOC "FILE \"$wav\" 0 \n\n"; } close TOC; close $tagfile; } sub Do_TOC_Verify { # go there chdir($BURNDIR) || die "Cannot chdir('$BURNDIR'): $!\n"; print "Verifying generated Table of Contents...\n"; System(cdrdao('read-test')." $CDTOC $OUTPUT 2>&1") == 0 or die "Failed to create CD Table of Contents?!\n"; } sub Do_CDR_Check { # go there chdir($BURNDIR) || die "Cannot chdir('$BURNDIR'): $!\n"; print "Checking for CDR...\n"; my ($rc, $report) = Backtick(cdrdao('disk-info')); die "CDR not loaded?!\n" if ($rc != 0); print "\tCDR found.\n"; if (!$opt_no_cd_text) { my $options = undef; my $driver_name = undef; foreach my $line (split("\n",$report)) { chomp($line); if ($line =~ /^Using driver: (.*)\(options (0x[0-9a-fA-F]+)\)$/) { $driver_name = $1; $options = hex($2); } } if (!defined($options)) { die "Could not determine driver options!\n"; } elsif ($opt_verbose) { printf("\tDriver name: %s\n", $driver_name); printf("\tDriver options: 0x%04x\n", $options); } # 0x10 == OPT_MMC_CD_TEXT /usr/share/cdrdao/drivers if (($driver_name =~ /raw writing/) || ($options & 0x10) == 0x10) { print "\tCD-TEXT supported.\n"; } else { print "ERROR: It seems that driver selected by cdrdao for $opt_device\n"; print " does not support CD-TEXT writing. Either disable CD-TEXT via\n"; print " '--no-cd-text' or select a different driver (e.g. try using\n"; print " '--driver generic-mmc-raw').\n"; exit(1); } } } sub Do_Burn { # go there chdir($BURNDIR) || die "Cannot chdir('$BURNDIR'): $!\n"; my $cmd = cdrdao('write'); $cmd.=" --eject" if (!$opt_no_eject); $cmd.=" -n $CDTOC"; System($cmd) == 0 or die "BURN FAILED!\n"; } sub Version { # Create human-readable version with un-human-readable code print "mp3cd version ". join(".",map{$_+0} (sprintf("%.6f",$VERSION) =~/^(\d+)\.?(\d{3})?(\d{3})?$/))."\n"; print <<'EOM'; Copyright 2003-2011 Kees Cook This program is free software; you may redistribute it under the terms of the GNU General Public License. This program has absolutely no warranty. EOM exit(0); } # return a good cdrdao command string prefix sub cdrdao { my $operation = $_[0] || 'simulate'; $operation = 'simulate' if ($opt_simulate && $operation eq 'write'); return "cdrdao $operation $opt_cdrdao"; } # /* vi:set ai ts=4 sw=4 expandtab: */ mp3cd-1.027/ChangeLog0000644000076500007650000000727111515336001012757 0ustar keeskeesRevision history for Perl script mp3cd. 1.27.0 Tue, 18 Jan, 2011 10:12:04 -0500 * Use mplayer as general fall-back when other decoders not known. * Add faad as m4a decoder. 1.26.1 Fri, 19 Jun 2009 18:20:12 -0700 * allow CD-TEXT when in recommended raw mode. 1.26.0 Sun, 14 Jun 2009 13:13:45 -0700 * added support for tag-reading and CD-TEXT writing via TOC. 1.25.6 Wed, 4 Dec 2008 16:42:33 -0700 * switch from mpg123 to sox for mp3 -> wav conversion 1.25.5 Thu, 2 Oct 2008 17:42:00 -0700 * fixed SoX 14.1.0 output handling (thanks to Joey Hess) * added --verbose flag to help with command debugging 1.25.4 Thu, 22 Mar 2007 17:46:18 -800 * fixed SoX 13 output handling (thanks to Christian von Essen) 1.25.3 Sat, 3 Mar 2007 11:11:32 -700 * fixed MP3 CD Burning HOWTO URL (thanks to Miguel de Val Borro) 1.25.2 Mon, 20 Nov 2006 14:43:33 -0800 * fixed Ogg capitalization (thanks to Filipus Klutiero). * fixed normalization path detection (thanks to Dave Allen Barker Jr). 1.25.1 Sun, 1 Oct 2006 09:44:18 -0700 * cleaned up prerequisite warnings * fixed typos 1.25.0 Mon Jun 12 23:33:12 PDT 2006 * generalized decoder support * allow multiple possible executable names for requirements 1.24.1 Sun Mar 5 12:32:22 PDT 2005 * patch from Richard Dawe for better sanity with stage skipping 1.24.0 Thu May 12 15:48:23 PDT 2005 * bugfix from Mike Grasso to populate the cdrdao options correctly 1.23.3 unreleased * Made Config::Simple less required 1.23.2 Fri Sep 24 23:05:32 PDT 2004 * Patch to clean up help via POD options (from Kevin) * Patch to finally cave into using /tmp :) (from Kevin) * Added support for .mp3cdrc to load option defaults 1.23.1 Tue Sep 14 07:46:48 PDT 2004 * Patch to increase --no-log verbosity (from Kevin) 1.23.0 Sun Sep 12 09:51:10 PDT 2004 * Added flac support (thanks to James Greenhalgh) * Added --no-log (thanks to Kevin) * Added --skip to implement stage skipping * Fixes bug where upper-case file extensions wouldn't be understood 1.22.3 Tue Sep 10 16:41:47 PDT 2004 * Added --no-eject option as suggested by Kevin 1.22.2 Tue Sep 7 21:23:14 PDT 2004 * Typo noticed by Kevin * moved to mpg321 instead of mpg123 (free software) 1.22.1 Sun Sep 5 23:57:33 PDT 2004 * Simulation was always on... oops! 1.22.0 Sat Sep 4 21:21:00 PDT 2004 * Fixed typo found by Kirsten Cook * handle file lists on the command line (thanks to Kevin C. Krinke!) * allow cdrdao options, device, simulation (thanks to Kevin again!) * re-arranged list building code * re-arranged tool verification code * handles non-fully-qualified files now * added note about "tool-output.txt" to man page 1.20 Fri Feb 13 19:42:15 PST 2003 * added XMLPlaylist support from Alex Rhomberg * cleaned up command line processor * improved and updated documentation 1.18 Wed Dec 31 09:16:04 PST 2003 * corrected program-finding logic bug pointed out by Michael Witrant 1.16.1 Sat Nov 29 17:38:08 PST 2003 * Added patch from J. Katz for OGG support and -t sanity * updated TODO list with some other ideas * cleaned up code a little * added --version 1.016 Thu Nov 20 22:11:38 PST 2003 * correcting "sox" commandline to work without the "nul" output 1.014 Fri Oct 17 12:14:44 PST 2003 * using "sox" to get wav info; looks like "file" isn't always sane * correcting temp dir path for sane username 1.012 Fri Oct 17 10:44:34 PST 2003 * private temp directory * try to make the path 1.01 Sun May 4 11:20:53 PST 2003 * initial public release. * thanks to sdist for making my package! # /* vi:set ai ts=4 sw=4 expandtab: */ mp3cd-1.027/INSTALL0000644000076500007650000000177511072242700012241 0ustar keeskees ------------ Installation ------------ To install the script and man pages in the standard areas, give the sequence of commands perl Makefile.PL make make test make install # you probably need to do this step as superuser If you want to install the script in your own private space, use perl Makefile.PL PREFIX=/home/joeuser INSTALLMAN1DIR=/home/joeuser/man/man1 INSTALLMAN3DIR=/home/joeuser/man/man3 make make test make install # can do this step as joeuser Note that `make test` does nothing interesting. -------------- Uninstallation -------------- Under a user with sufficient permissions and from the program distribution directory, execute perl Makefile.PL if there isn't a file called Makefile. Then execute make uninstall This sometimes works, and sometimes it does not. If it refuses to work, you can simply remove all files by hand. Look for the .packlist file which perl created when installing the software and remove all files you find in there. mp3cd-1.027/AUTHORS0000644000076500007650000000100211072242700012237 0ustar keeskeesKees Cook Contributors ------------ J. Katz (ogg support and fixes) Alex Rhomberg (XMLPlaylist support) Kevin C. Krinke (filelist inspiration, and countless many patches) James Greenhalgh (flac support) Mike Grasso (fixes) Richard Dawe (stage skipping fixes) Also thanks to Greg Wierzchowski for the MP3 burning HOWTO, and all the great software used in this script. This script was inspired by the "burnmp3" script that doesn't need to write out WAVs (but then it can't normalize either). mp3cd-1.027/MANIFEST0000644000076500007650000000024011072242700012323 0ustar keeskeesscripts/mp3cd Makefile.PL README INSTALL ChangeLog MANIFEST AUTHORS COPYING TODO META.yml Module meta-data (added by MakeMaker) mp3cd-1.027/Makefile.PL0000644000076500007650000000213111072242700013145 0ustar keeskeesuse ExtUtils::MakeMaker; # # See lib/ExtUtils/MakeMaker.pm for details of how to influence # the contents of the Makefile that is written. # # If any modules outside of the core perl distribution are required, # these should be included as a PREREQ_PM entry in WriteMakefile below, # as indicated in the example. This example requires the modules # MOD1 and MOD2 to be installed, with minimal versions 1 and 5, # respectively. If the version number is 0, any version is sufficient. # # As well, if you wish to force a minimal perl version to run the # script, insert a line, for example, # # require 5.004; # # below. my %opts = ( 'NAME' => 'mp3cd', 'VERSION_FROM' => 'scripts/mp3cd', # finds $VERSION 'EXE_FILES' => [ qw( scripts/mp3cd ) ], # scripts to install 'PREREQ_PM' => { 'Getopt::Long' => 0, 'Pod::Usage' => 0, 'File::Path' => 0, 'Config::Simple' => 0, 'Cwd' => 0, }, 'PREREQ_FATAL' => 1, ); if ($ExtUtils::MakeMaker::VERSION >= 5.43) { $opts{AUTHOR} = 'Kees Cook '; $opts{ABSTRACT_FROM} = 'scripts/mp3cd'; } WriteMakefile( %opts ) mp3cd-1.027/META.yml0000644000076500007650000000123511515337451012462 0ustar keeskees--- #YAML:1.0 name: mp3cd version: 1.027 abstract: Burns normalized audio CDs from lists of MP3s/WAVs/Oggs/FLACs author: - Kees Cook license: unknown distribution_type: module configure_requires: ExtUtils::MakeMaker: 0 build_requires: ExtUtils::MakeMaker: 0 requires: Config::Simple: 0 Cwd: 0 File::Path: 0 Getopt::Long: 0 Pod::Usage: 0 no_index: directory: - t - inc generated_by: ExtUtils::MakeMaker version 6.55_02 meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: 1.4 mp3cd-1.027/README0000644000076500007650000000121711515336052012065 0ustar keeskees This is the README file for mp3cd, a perl script that implements the suggested methods outlined in the Linux "MP3 CD Burning mini-HOWTO" http://tldp.org/HOWTO/mini/MP3-CD-Burning/ For more information on how to use the script, see the pod documentation via the command perldoc scripts/mp3cd or, after installation, view the man pages with man mp3cd For instructions on how to install the script, see the file INSTALL. Problems, questions, etc. may be sent to mp3cd@outflux.net mp3cd is Copyright (c) 2003-2011, by Kees Cook . All rights reserved. You may distribute this code under the terms of the GNU General Public License. mp3cd-1.027/COPYING0000644000076500007650000004311011072242700012230 0ustar keeskees GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. mp3cd-1.027/TODO0000644000076500007650000000060611215234541011673 0ustar keeskees - count up "blocks" of WAV (rounding up for each file), and compare against "disk-info" block size report to make sure it'll fit on a disk - use File::Spec/File::Basename - handle arbitrary filename exts (especially now that we have ogg/flac support) - tests for file and programs only if we expect to run a stage requiring them - improve "System", and dump tool output on failures, etc