diff options
author | Kees Cook <kees@debian.org> | 2011-01-18 17:17:41 +0100 |
---|---|---|
committer | Kees Cook <kees@debian.org> | 2011-01-18 17:17:41 +0100 |
commit | 3adb2fec9ada32a626871fee3ebf9fd513b128ac (patch) | |
tree | ae7ee92ebdba68feb892b6a5c7598da7a2acaab4 |
Import mp3cd_1.27.0.orig.tar.gz
[dgit import orig mp3cd_1.27.0.orig.tar.gz]
-rw-r--r-- | AUTHORS | 15 | ||||
-rw-r--r-- | COPYING | 340 | ||||
-rw-r--r-- | ChangeLog | 109 | ||||
-rw-r--r-- | INSTALL | 42 | ||||
-rw-r--r-- | MANIFEST | 10 | ||||
-rw-r--r-- | META.yml | 26 | ||||
-rw-r--r-- | Makefile.PL | 38 | ||||
-rw-r--r-- | README | 20 | ||||
-rw-r--r-- | TODO | 7 | ||||
-rwxr-xr-x | scripts/mp3cd | 1032 |
10 files changed, 1639 insertions, 0 deletions
@@ -0,0 +1,15 @@ +Kees Cook <kees@outflux.net> + +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). + @@ -0,0 +1,340 @@ + 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. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + 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. + + <signature of Ty Coon>, 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. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..860e848 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,109 @@ +Revision 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: */ @@ -0,0 +1,42 @@ + +------------ +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. + diff --git a/MANIFEST b/MANIFEST new file mode 100644 index 0000000..2a3a5f8 --- /dev/null +++ b/MANIFEST @@ -0,0 +1,10 @@ +scripts/mp3cd +Makefile.PL +README +INSTALL +ChangeLog +MANIFEST +AUTHORS +COPYING +TODO +META.yml Module meta-data (added by MakeMaker) diff --git a/META.yml b/META.yml new file mode 100644 index 0000000..2b1b499 --- /dev/null +++ b/META.yml @@ -0,0 +1,26 @@ +--- #YAML:1.0 +name: mp3cd +version: 1.027 +abstract: Burns normalized audio CDs from lists of MP3s/WAVs/Oggs/FLACs +author: + - Kees Cook <kees@outflux.net> +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 diff --git a/Makefile.PL b/Makefile.PL new file mode 100644 index 0000000..a2d7d87 --- /dev/null +++ b/Makefile.PL @@ -0,0 +1,38 @@ +use 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 <kees@outflux.net>'; + $opts{ABSTRACT_FROM} = 'scripts/mp3cd'; +} + +WriteMakefile( %opts ) @@ -0,0 +1,20 @@ + +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 <kees@outflux.net>. +All rights reserved. You may distribute this code under the terms of +the GNU General Public License. + @@ -0,0 +1,7 @@ + +- 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 diff --git a/scripts/mp3cd b/scripts/mp3cd new file mode 100755 index 0000000..89b328d --- /dev/null +++ b/scripts/mp3cd @@ -0,0 +1,1032 @@ +#!/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<clean> + +This is the default starting stage. The temp directory is cleared out. +A playlist is required, since we expect to move to the B<build> stage +next, which requires it. + +=item B<build> + +This stage examines the playlist from the command line, and tries to +create a list of symlinks from the given playlist. So far, C<mp3cd> +can understand ".m3u" files, XMLPlaylist files, and lists of files. + +=item B<decode> + +All the files are converted into WAVs. So far, C<mp3cd> knows how to +decode MP3, Ogg, and FLAC files. (WAVs will be left as they are during +this stage.) + +=item B<correct> + +The WAV files are corrected to have the correct bitrate and number of +channels, as required for an audio CD. + +=item B<norm> + +The WAV files' volumes are normalized so any large differences in volume +between records will be less noticeable. + +=item B<toc> + +Generates a Table of Contents for the audio CD. + +=item B<toc_ok> + +Validates the TOC, just in case something went really wrong with +the WAV files. + +=item B<cdr_ok> + +Verifies that there is a CDR ready for burning. + +=item B<burn> + +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<username>". 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<http://tldp.org/HOWTO/MP3-CD-Burning/> + +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<cdrdao>, 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<sox> to decode MP3 and check/correct WAV formats. + http://www.spies.com/Sox/ + +Requires C<normalize> to process the audio. + http://www.cs.columbia.edu/~cvaill/normalize/ + +Optionally requires C<oggdec> to decode Ogg to WAV files. + http://www.gnu.org/directory/audio/ogg/OggEnc.html/ + +Optionally requires C<flac> to decode flac to WAV files. + http://flac.sourceforge.net/ + +Optionally requires C<Config::Simple> 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 <kees@outflux.net> + + 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=<M3U>; + close(M3U); + + my @files; + if (scalar(@lines) && $lines[0] =~ /<!DOCTYPE\s+XMLPlaylist>/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 <<EOM; +CD_TEXT { + LANGUAGE_MAP { + 0 : EN + } + LANGUAGE 0 { + TITLE "" + PERFORMER "" + } +} +EOM + } + print TOC "\n"; + + # get list of wavs + opendir(DIR, $BURNDIR) || die "Can't read directory '$BURNDIR': $!\n"; + my @wavs = grep { /^\d+\.wav$/i && -f "$BURNDIR/$_" } readdir(DIR); + closedir DIR; + + foreach my $wav (sort {$a cmp $b} @wavs) + { + die ("Yikes! What happened to '$wav'?!\n") unless (-f $wav); + print TOC "TRACK AUDIO\n"; + if (!$opt_no_cd_text) { + print TOC cd_text(scalar(<$tagfile>), 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 <kees@outflux.net> +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: */ |