summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKees Cook <kees@debian.org>2011-01-18 17:17:41 +0100
committerKees Cook <kees@debian.org>2011-01-18 17:17:41 +0100
commit3adb2fec9ada32a626871fee3ebf9fd513b128ac (patch)
treeae7ee92ebdba68feb892b6a5c7598da7a2acaab4
Import mp3cd_1.27.0.orig.tar.gz
[dgit import orig mp3cd_1.27.0.orig.tar.gz]
-rw-r--r--AUTHORS15
-rw-r--r--COPYING340
-rw-r--r--ChangeLog109
-rw-r--r--INSTALL42
-rw-r--r--MANIFEST10
-rw-r--r--META.yml26
-rw-r--r--Makefile.PL38
-rw-r--r--README20
-rw-r--r--TODO7
-rwxr-xr-xscripts/mp3cd1032
10 files changed, 1639 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..01ca4a9
--- /dev/null
+++ b/AUTHORS
@@ -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).
+
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..d60c31a
--- /dev/null
+++ b/COPYING
@@ -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: */
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..cabdca7
--- /dev/null
+++ b/INSTALL
@@ -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 )
diff --git a/README b/README
new file mode 100644
index 0000000..ed9e6d9
--- /dev/null
+++ b/README
@@ -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.
+
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..9fd8be7
--- /dev/null
+++ b/TODO
@@ -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: */