diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..cbee875 --- /dev/null +++ b/COPYING @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 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. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +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 and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, 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 library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete 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 distribute a copy of this License along with the +Library. + + 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 Library or any portion +of it, thus forming a work based on the Library, 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) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +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 Library, 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 Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you 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. + + If distribution of 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 satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be 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. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library 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. + + 9. 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 Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +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 with +this License. + + 11. 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 Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library 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 Library. + +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. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library 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. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser 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 Library +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 Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +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 + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "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 +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. 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 LIBRARY 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 +LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; 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. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/Makefile.debian b/Makefile.debian new file mode 100644 index 0000000..cb5e426 --- /dev/null +++ b/Makefile.debian @@ -0,0 +1,11 @@ +DESTDIR=`pwd`/debian/wavegain + +all: + gcc *.c -o wavegain -DHAVE_CONFIG_H -lm -lsndfile + +install: + install -d $(DESTDIR)/usr/bin/ + install -m 0755 wavegain $(DESTDIR)/usr/bin/ + +clean: + rm -Rf wavegain *.o diff --git a/Makefile.linux b/Makefile.linux new file mode 100644 index 0000000..17235c6 --- /dev/null +++ b/Makefile.linux @@ -0,0 +1,9 @@ +all: + gcc *.c -o wavegain -DHAVE_CONFIG_H -lm -lsndfile + +install: + install -d /usr/bin/ + install -m 0755 wavegain /usr/bin/ + +clean: + rm -Rf wavegain *.o diff --git a/WaveGain/WaveGain.dsp b/WaveGain/WaveGain.dsp new file mode 100644 index 0000000..eb3aec4 --- /dev/null +++ b/WaveGain/WaveGain.dsp @@ -0,0 +1,146 @@ +# Microsoft Developer Studio Project File - Name="WaveGain" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=WaveGain - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "WaveGain.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "WaveGain.mak" CFG="WaveGain - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "WaveGain - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "WaveGain - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=xicl6.exe +RSC=rc.exe + +!IF "$(CFG)" == "WaveGain - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /Ox /Ot /Og /Oi /Ob2 /D "_CONSOLE" /D "_MBCS" /D "WIN32" /D "NDEBUG" /D "HAVE_CONFIG_H" /D "_USE_NON_INTEL_COMPILER" /FD /c +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x809 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=xilink6.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Desc=Compressing using UPX... +PostBuild_Cmds=upx --best .\release\WaveGain.exe +# End Special Build Tool + +!ELSEIF "$(CFG)" == "WaveGain - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "HAVE_CONFIG_H" /D "_USE_NON_INTEL_COMPILER" /YX /FD /GZ /c +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x809 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=xilink6.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "WaveGain - Win32 Release" +# Name "WaveGain - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\audio.c +# End Source File +# Begin Source File + +SOURCE=..\dither.c +# End Source File +# Begin Source File + +SOURCE=..\gain_analysis.c +# End Source File +# Begin Source File + +SOURCE=..\getopt.c +# End Source File +# Begin Source File + +SOURCE=..\getopt1.c +# End Source File +# Begin Source File + +SOURCE=..\main.c +# End Source File +# Begin Source File + +SOURCE=..\misc.c +# End Source File +# Begin Source File + +SOURCE=..\recurse.c +# End Source File +# Begin Source File + +SOURCE=..\wavegain.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\gain_analysis.h +# End Source File +# Begin Source File + +SOURCE=..\getopt.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/WaveGain/WaveGain.dsw b/WaveGain/WaveGain.dsw new file mode 100644 index 0000000..2f37608 --- /dev/null +++ b/WaveGain/WaveGain.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "WaveGain"=".\WaveGain.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/WaveGain/upx.exe b/WaveGain/upx.exe new file mode 100644 index 0000000..5f37de5 Binary files /dev/null and b/WaveGain/upx.exe differ diff --git a/audio.c b/audio.c new file mode 100644 index 0000000..346d434 --- /dev/null +++ b/audio.c @@ -0,0 +1,1152 @@ +/* WaveGain - Filename: AUDIO.C + * + * Function: Essentially provides all the wave input and output routines. + * + * Copyright (c) 2002 - 2005 John Edwards + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Portions, (c) Michael Smith + * Portions from Vorbize, (c) Kenneth Arnold + * and libvorbis examples, (c) Monty + * + * AIFF/AIFC support from OggSquish, (c) 1994-1996 Monty + */ + + +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#else +# ifndef __MACOSX__ +# include +# endif +#endif + +#include + +#ifndef __MACOSX__ +#include +#endif + +#include +#include "config.h" +#include "audio.h" +#include "i18n.h" +#include "misc.h" + +/* Macros to read header data */ +#define READ_U32_LE(buf) \ + (((buf)[3]<<24)|((buf)[2]<<16)|((buf)[1]<<8)|((buf)[0]&0xff)) + +#define READ_U16_LE(buf) \ + (((buf)[1]<<8)|((buf)[0]&0xff)) + +#define READ_U32_BE(buf) \ + (((buf)[0]<<24)|((buf)[1]<<16)|((buf)[2]<<8)|((buf)[3]&0xff)) + +#define READ_U16_BE(buf) \ + (((buf)[0]<<8)|((buf)[1]&0xff)) + +/* Define the supported formats here */ +input_format formats[] = { + {wav_id, 12, wav_open, wav_close, "wav", N_("WAV file reader")}, + {aiff_id, 12, aiff_open, wav_close, "aiff", N_("AIFF/AIFC file reader")}, + {NULL, 0, NULL, NULL, NULL, NULL} +}; + +/* Global */ +unsigned int current_pos_t; + +input_format *open_audio_file(FILE *in, wavegain_opt *opt) +{ + int j=0; + unsigned char *buf=NULL; + int buf_size=0, buf_filled=0; + int size,ret; + + while (formats[j].id_func) { + size = formats[j].id_data_len; + if (size >= buf_size) { + buf = realloc(buf, size); + buf_size = size; + } + + if (size > buf_filled) { + ret = fread(buf+buf_filled, 1, buf_size-buf_filled, in); + buf_filled += ret; + + if (buf_filled < size) { + /* File truncated */ + j++; + continue; + } + } + + if (formats[j].id_func(buf, buf_filled)) { + /* ok, we now have something that can handle the file */ + if (formats[j].open_func(in, opt, buf, buf_filled)) { + free(buf); + return &formats[j]; + } + } + j++; + } + + free(buf); + + return NULL; +} + +static int seek_forward(FILE *in, int length) +{ + if (fseek(in, length, SEEK_CUR)) { + /* Failed. Do it the hard way. */ + unsigned char buf[1024]; + int seek_needed = length, seeked; + while (seek_needed > 0) { + seeked = fread(buf, 1, seek_needed>1024?1024:seek_needed, in); + if (!seeked) + return 0; /* Couldn't read more, can't read file */ + else + seek_needed -= seeked; + } + } + return 1; +} + + +static int find_wav_chunk(FILE *in, char *type, unsigned int *len) +{ + unsigned char buf[8]; + + while (1) { + if (fread(buf,1,8,in) < 8) { + /* Suck down a chunk specifier */ + fprintf(stderr, "Warning: Unexpected EOF in reading WAV header (1)\n"); + return 0; /* EOF before reaching the appropriate chunk */ + } + + if (memcmp(buf, type, 4)) { + *len = READ_U32_LE(buf+4); + if (!seek_forward(in, *len)) + return 0; + + buf[4] = 0; + } + else { + *len = READ_U32_LE(buf+4); + return 1; + } + } +} + +static int find_aiff_chunk(FILE *in, char *type, unsigned int *len) +{ + unsigned char buf[8]; + + while (1) { + if (fread(buf,1,8,in) <8) { + fprintf(stderr, "Warning: Unexpected EOF in AIFF chunk\n"); + return 0; + } + + *len = READ_U32_BE(buf+4); + + if (memcmp(buf,type,4)) { + if ((*len) & 0x1) + (*len)++; + + if (!seek_forward(in, *len)) + return 0; + } + else + return 1; + } +} + + + +double read_IEEE80(unsigned char *buf) +{ + int s=buf[0]&0xff; + int e=((buf[0]&0x7f)<<8)|(buf[1]&0xff); + double f=((unsigned long)(buf[2]&0xff)<<24)| + ((buf[3]&0xff)<<16)| + ((buf[4]&0xff)<<8) | + (buf[5]&0xff); + + if (e==32767) { + if (buf[2]&0x80) + return HUGE_VAL; /* Really NaN, but this won't happen in reality */ + else { + if (s) + return -HUGE_VAL; + else + return HUGE_VAL; + } + } + + f=ldexp(f,32); + f+= ((buf[6]&0xff)<<24)| + ((buf[7]&0xff)<<16)| + ((buf[8]&0xff)<<8) | + (buf[9]&0xff); + + return ldexp(f, e-16446); +} + +/* AIFF/AIFC support adapted from the old OggSQUISH application */ +int aiff_id(unsigned char *buf, int len) +{ + if (len<12) return 0; /* Truncated file, probably */ + + if (memcmp(buf, "FORM", 4)) + return 0; + + if (memcmp(buf+8, "AIF",3)) + return 0; + + if (buf[11]!='C' && buf[11]!='F') + return 0; + + return 1; +} + +int aiff_open(FILE *in, wavegain_opt *opt, unsigned char *buf, int buflen) +{ + int aifc; /* AIFC or AIFF? */ + unsigned int len; + unsigned char *buffer; + unsigned char buf2[8]; + aiff_fmt format; + aifffile *aiff = malloc(sizeof(aifffile)); + + if (buf[11]=='C') + aifc=1; + else { + aifc=0; + opt->format = WAV_FMT_AIFF; + } + + if (!find_aiff_chunk(in, "COMM", &len)) { + fprintf(stderr, "Warning: No common chunk found in AIFF file\n"); + return 0; /* EOF before COMM chunk */ + } + + if (len < 18) { + fprintf(stderr, "Warning: Truncated common chunk in AIFF header\n"); + return 0; /* Weird common chunk */ + } + + buffer = alloca(len); + + if (fread(buffer,1,len,in) < len) { + fprintf(stderr, "Warning: Unexpected EOF in reading AIFF header\n"); + return 0; + } + + format.channels = READ_U16_BE(buffer); + format.totalframes = READ_U32_BE(buffer+2); + format.samplesize = READ_U16_BE(buffer+6); + format.rate = (int)read_IEEE80(buffer+8); + + aiff->bigendian = BIG; + opt->endianness = BIG; + + if (aifc) { + if (len < 22) { + fprintf(stderr, "Warning: AIFF-C header truncated.\n"); + return 0; + } + + if (!memcmp(buffer+18, "NONE", 4)) { + aiff->bigendian = BIG; + opt->endianness = BIG; + } + else if (!memcmp(buffer+18, "sowt", 4)) { + aiff->bigendian = LITTLE; + opt->endianness = LITTLE; + } + else { + fprintf(stderr, "Warning: Can't handle compressed AIFF-C (%c%c%c%c)\n", *(buffer+18), *(buffer+19), *(buffer+20), *(buffer+21)); + return 0; /* Compressed. Can't handle */ + } + } + + if (!find_aiff_chunk(in, "SSND", &len)) { + fprintf(stderr, "Warning: No SSND chunk found in AIFF file\n"); + return 0; /* No SSND chunk -> no actual audio */ + } + + if (len < 8) { + fprintf(stderr, "Warning: Corrupted SSND chunk in AIFF header\n"); + return 0; + } + + if (fread(buf2,1,8, in) < 8) { + fprintf(stderr, "Warning: Unexpected EOF reading AIFF header\n"); + return 0; + } + + format.offset = READ_U32_BE(buf2); + format.blocksize = READ_U32_BE(buf2+4); + + if ( format.blocksize == 0 && (format.samplesize == 16 || format.samplesize == 8)) { + /* From here on, this is very similar to the wav code. Oh well. */ + + opt->rate = format.rate; + opt->channels = format.channels; + opt->read_samples = wav_read; /* Similar enough, so we use the same */ + opt->total_samples_per_channel = format.totalframes; + opt->samplesize = format.samplesize; + if (aifc && format.samplesize == 8) + opt->format = WAV_FMT_AIFC8; + else if (aifc && format.samplesize == 16) + opt->format = WAV_FMT_AIFC16; + opt->header_size = 0; + opt->header = NULL; + opt->rate = format.rate; + aiff->f = in; + aiff->samplesread = 0; + aiff->channels = format.channels; + aiff->samplesize = format.samplesize; + aiff->totalsamples = format.totalframes; + + opt->readdata = (void *)aiff; + + seek_forward(in, format.offset); /* Swallow some data */ + return 1; + } + else { + fprintf(stderr, "Warning: OggEnc does not support this type of AIFF/AIFC file\n" + " Must be 8 or 16 bit PCM.\n"); + return 0; + } +} + +int wav_id(unsigned char *buf, int len) +{ + unsigned int flen; + + if (len<12) return 0; /* Something screwed up */ + + if (memcmp(buf, "RIFF", 4)) + return 0; /* Not wave */ + + flen = READ_U32_LE(buf+4); /* We don't use this */ + + if (memcmp(buf+8, "WAVE",4)) + return 0; /* RIFF, but not wave */ + + return 1; +} + +int wav_open(FILE *in, wavegain_opt *opt, unsigned char *oldbuf, int buflen) +{ + unsigned char buf[16]; + unsigned int len; + long current_pos; + int samplesize; + wav_fmt format; + wavfile *wav = malloc(sizeof(wavfile)); + + /* Ok. At this point, we know we have a WAV file. Now we have to detect + * whether we support the subtype, and we have to find the actual data + * We don't (for the wav reader) need to use the buffer we used to id this + * as a wav file (oldbuf) + */ + + if (!find_wav_chunk(in, "fmt ", &len)) { + fprintf(stderr, "Warning: Failed to find fmt chunk in reading WAV header\n"); + return 0; /* EOF */ + } + + if (len < 16) { + fprintf(stderr, "Warning: Unrecognised format chunk in WAV header\n"); + return 0; /* Weird format chunk */ + } + + /* A common error is to have a format chunk that is not 16 or 18 bytes + * in size. This is incorrect, but not fatal, so we only warn about + * it instead of refusing to work with the file. Please, if you + * have a program that's creating format chunks of sizes other than + * 16 or 18 bytes in size, report a bug to the author. + */ + if (len!=16 && len!=18) + fprintf(stderr, "Warning: INVALID format chunk in wav header.\n" + " Trying to read anyway (may not work)...\n"); + + if (fread(buf,1,16,in) < 16) { + fprintf(stderr, "Warning: Unexpected EOF in reading WAV header (2)\n"); + return 0; + } + + /* Deal with stupid broken apps. Don't use these programs. + */ + if (len - 16 > 0 && !seek_forward(in, len-16)) + return 0; + + format.format = READ_U16_LE(buf); + format.channels = READ_U16_LE(buf+2); + format.samplerate = READ_U32_LE(buf+4); + format.bytespersec = READ_U32_LE(buf+8); + format.align = READ_U16_LE(buf+12); + format.samplesize = READ_U16_LE(buf+14); + + if (!find_wav_chunk(in, "data", &len)) { + fprintf(stderr, "Warning: Failed to find data chunk in reading WAV header\n"); + return 0; /* EOF */ + } + + if (opt->apply_gain) { + current_pos = ftell(in); + current_pos_t = current_pos + len; + fseek(in, 0, SEEK_SET); + if ((opt->header = malloc(sizeof(char) * current_pos)) == NULL) + fprintf(stderr, "Error: unable to allocate memory for header\n"); + else { + opt->header_size = current_pos; + fread(opt->header, 1, opt->header_size, in); + } + fseek(in, current_pos, SEEK_SET); + } + + if (format.format == 1) { + samplesize = format.samplesize/8; + opt->read_samples = wav_read; + /* works with current enum */ + opt->format = samplesize; + } + else if (format.format == 3) { + samplesize = 4; + opt->read_samples = wav_ieee_read; + opt->endianness = LITTLE; + opt->format = WAV_FMT_FLOAT; + } + else { + fprintf(stderr, "ERROR: Wav file is unsupported type (must be standard PCM\n" + " or type 3 floating point PCM)\n"); + return 0; + } + + opt->samplesize = format.samplesize; + + if ( format.align == format.channels*samplesize && + format.samplesize == samplesize*8 && + (format.samplesize == 32 || format.samplesize == 24 || + format.samplesize == 16 || format.samplesize == 8)) { + /* OK, good - we have the one supported format, + now we want to find the size of the file */ + opt->rate = format.samplerate; + opt->channels = format.channels; + + wav->f = in; + wav->samplesread = 0; + wav->bigendian = 0; + opt->endianness = LITTLE; + wav->channels = format.channels; /* This is in several places. The price + of trying to abstract stuff. */ + wav->samplesize = format.samplesize; + + if (len) + opt->total_samples_per_channel = len/(format.channels*samplesize); + else { + long pos; + pos = ftell(in); + if (fseek(in, 0, SEEK_END) == -1) + opt->total_samples_per_channel = 0; /* Give up */ + else { + opt->total_samples_per_channel = (ftell(in) - pos)/(format.channels*samplesize); + fseek(in,pos, SEEK_SET); + } + } + wav->totalsamples = opt->total_samples_per_channel; + + opt->readdata = (void *)wav; + return 1; + } + else { + fprintf(stderr, "ERROR: Wav file is unsupported subformat (must be 8, 16, 24 or 32 bit PCM\n" + "or floating point PCM)\n"); + return 0; + } +} + +long wav_read(void *in, double **buffer, int samples, int fast, int chunk) +{ + wavfile *f = (wavfile *)in; + int sampbyte = f->samplesize / 8; + signed char *buf = alloca(samples*sampbyte*f->channels); + long bytes_read; + int i,j; + long realsamples; + + if (fast) { + chunk /= (sampbyte * f->channels); + chunk *= (sampbyte * f->channels); + fseek(f->f, chunk, SEEK_SET); + } + + bytes_read = fread(buf, 1, samples*sampbyte*f->channels, f->f); + + if (f->totalsamples && f->samplesread + bytes_read/(sampbyte*f->channels) > f->totalsamples) { + bytes_read = sampbyte*f->channels*(f->totalsamples - f->samplesread); + } + + realsamples = bytes_read/(sampbyte*f->channels); + f->samplesread += realsamples; + + if (f->samplesize==8) { + unsigned char *bufu = (unsigned char *)buf; + for (i = 0; i < realsamples; i++) { + for (j=0; j < f->channels; j++) + buffer[j][i]=((int)(bufu[i*f->channels + j])-128)/128.0f; + } + } + else if (f->samplesize==16) { +#ifdef __MACOSX__ + if (f->bigendian != machine_endianness) { +#else + if (f->bigendian == machine_endianness) { +#endif + for (i = 0; i < realsamples; i++) { + for (j=0; j < f->channels; j++) + buffer[j][i] = ((buf[i*2*f->channels + 2*j + 1]<<8) | + (buf[i*2*f->channels + 2*j] & 0xff))/32768.0f; + } + } + else { + for (i = 0; i < realsamples; i++) { + for (j=0; j < f->channels; j++) + buffer[j][i]=((buf[i*2*f->channels + 2*j]<<8) | + (buf[i*2*f->channels + 2*j + 1] & 0xff))/32768.0f; + } + } + } + else if (f->samplesize==24) { +#ifdef __MACOSX__ + if (f->bigendian != machine_endianness) { +#else + if (f->bigendian == machine_endianness) { +#endif + for (i = 0; i < realsamples; i++) { + for (j=0; j < f->channels; j++) + buffer[j][i] = ((buf[i*3*f->channels + 3*j + 2] << 16) | + (((unsigned char *)buf)[i*3*f->channels + 3*j + 1] << 8) | + (((unsigned char *)buf)[i*3*f->channels + 3*j] & 0xff)) + / 8388608.0f; + } + } + else { + fprintf(stderr, "Big endian 24 bit PCM data is not currently " + "supported, aborting.\n"); + return 0; + } + } + else if (f->samplesize==32) { +#ifdef __MACOSX__ + if (f->bigendian != machine_endianness) { +#else + if (f->bigendian == machine_endianness) { +#endif + for (i = 0; i < realsamples; i++) { + for (j=0; j < f->channels; j++) + buffer[j][i] = ((buf[i*4*f->channels + 4*j + 3] << 24) | + (((unsigned char *)buf)[i*4*f->channels + 4*j + 2] << 16) | + (((unsigned char *)buf)[i*4*f->channels + 4*j + 1] << 8) | + (((unsigned char *)buf)[i*4*f->channels + 4*j] & 0xff)) + / 2147483648.0f; + } + } + else { + fprintf(stderr, "Big endian 32 bit PCM data is not currently " + "supported, aborting.\n"); + return 0; + } + } + else { + fprintf(stderr, "Internal error: attempt to read unsupported " + "bitdepth %d\n", f->samplesize); + return 0; + } + + return realsamples; +} + +long wav_ieee_read(void *in, double **buffer, int samples, int fast, int chunk) +{ + wavfile *f = (wavfile *)in; + float *buf = alloca(samples*4*f->channels); /* de-interleave buffer */ + long bytes_read; + int i,j; + long realsamples; + + if (fast) { + chunk /= (sizeof(float) * f->channels); + chunk *= (sizeof(float) * f->channels); + fseek(f->f, chunk, SEEK_SET); + } + + bytes_read = fread(buf,1,samples*4*f->channels, f->f); + + if (f->totalsamples && f->samplesread + bytes_read/(4*f->channels) > f->totalsamples) + bytes_read = 4*f->channels*(f->totalsamples - f->samplesread); + realsamples = bytes_read/(4*f->channels); + f->samplesread += realsamples; + + for (i=0; i < realsamples; i++) + for (j=0; j < f->channels; j++) + buffer[j][i] = buf[i*f->channels + j]; + + return realsamples; +} + + +void wav_close(void *info) +{ + wavfile *f = (wavfile *)info; + + free(f); +} + +int raw_open(FILE *in, wavegain_opt *opt) +{ + wav_fmt format; /* fake wave header ;) */ + wavfile *wav = malloc(sizeof(wavfile)); + + /* construct fake wav header ;) */ + format.format = 2; + format.channels = opt->channels; + format.samplerate = opt->rate; + format.samplesize = opt->samplesize; + format.bytespersec = opt->channels * opt->rate * opt->samplesize / 8; + format.align = format.bytespersec; + wav->f = in; + wav->samplesread = 0; + wav->bigendian = opt->endianness; + wav->channels = format.channels; + wav->samplesize = opt->samplesize; + wav->totalsamples = 0; + + opt->read_samples = wav_read; + opt->readdata = (void *)wav; + opt->total_samples_per_channel = 0; /* raw mode, don't bother */ + return 1; +} + +/* + * W A V E O U T P U T + */ + +audio_file *open_output_audio_file(char *infile, wavegain_opt *opt) +{ + audio_file *aufile = malloc(sizeof(audio_file)); + + aufile->outputFormat = opt->format; + + aufile->samplerate = opt->rate; + aufile->channels = opt->channels; + aufile->samples = 0; + aufile->endianness = opt->endianness; + aufile->bits_per_sample = opt->samplesize; + + if (opt->std_out) { + aufile->sndfile = stdout; +#ifdef _WIN32 + _setmode( _fileno(stdout), _O_BINARY ); +#endif + } + else + aufile->sndfile = fopen(infile, "wb"); + + if (aufile->sndfile == NULL) { + if (aufile) + free(aufile); + return NULL; + } + + switch (aufile->outputFormat) { + case WAV_FMT_AIFF: + write_aiff_header(aufile); + break; + case WAV_FMT_8BIT: + case WAV_FMT_16BIT: + case WAV_FMT_24BIT: + case WAV_FMT_32BIT: + case WAV_FMT_FLOAT: { + unsigned int file_size = 0x7fffffff; + write_wav_header(aufile, opt, file_size); + } + break; + } + + return aufile; +} + +int write_audio_file(audio_file *aufile, void *sample_buffer, int samples) +{ + switch (aufile->outputFormat) { + case WAV_FMT_8BIT: + return write_audio_8bit(aufile, sample_buffer, samples); + case WAV_FMT_16BIT: + case WAV_FMT_AIFF: + return write_audio_16bit(aufile, sample_buffer, samples); + case WAV_FMT_24BIT: + return write_audio_24bit(aufile, sample_buffer, samples); + case WAV_FMT_32BIT: + return write_audio_32bit(aufile, sample_buffer, samples); + case WAV_FMT_FLOAT: + return write_audio_float(aufile, sample_buffer, samples); + default: + return 0; + } + + return 0; +} + +void close_audio_file( FILE *in, audio_file *aufile, wavegain_opt *opt) +{ + unsigned char *ch; + unsigned int pos; + + if (!opt->std_out) { + + switch (aufile->outputFormat) { + case WAV_FMT_AIFF: + fseek(aufile->sndfile, 0, SEEK_SET); + write_aiff_header(aufile); + break; + case WAV_FMT_8BIT: + case WAV_FMT_16BIT: + case WAV_FMT_24BIT: + case WAV_FMT_32BIT: + case WAV_FMT_FLOAT: { + fseek(in, 0, SEEK_END); + pos = ftell (in); + if ((pos - current_pos_t) > 0) { + fseek (in, current_pos_t, SEEK_SET); + ch = malloc (sizeof(char) * (pos - current_pos_t)); + + fread (ch, 1, pos - current_pos_t, in); + fwrite (ch, pos - current_pos_t, 1, aufile->sndfile); + + if (ch) + free (ch); + } + fseek(aufile->sndfile, 0, SEEK_END); + pos = ftell (aufile->sndfile); + fseek(aufile->sndfile, 0, SEEK_SET); + write_wav_header(aufile, opt, pos - 8); + break; + } + } + } + + if(opt->header) + free(opt->header); + if(opt) + free(opt); + + fclose(aufile->sndfile); + + if (aufile) + free(aufile); +} + +#define WRITE_U32(buf, x) *(buf) = (unsigned char)((x)&0xff);\ + *((buf)+1) = (unsigned char)(((x)>>8)&0xff);\ + *((buf)+2) = (unsigned char)(((x)>>16)&0xff);\ + *((buf)+3) = (unsigned char)(((x)>>24)&0xff); + +#define WRITE_U16(buf, x) *(buf) = (unsigned char)((x)&0xff);\ + *((buf)+1) = (unsigned char)(((x)>>8)&0xff); + +static int write_wav_header(audio_file *aufile, wavegain_opt *opt, unsigned int file_size) +{ + unsigned short channels = opt->channels; + unsigned int samplerate = opt->rate; + unsigned int bytespersec = opt->channels * opt->rate * opt->samplesize / 8; + unsigned short align = opt->channels * opt->samplesize / 8; + unsigned short samplesize = opt->samplesize; + unsigned int size = file_size; + unsigned int data_size = aufile->samples * (opt->samplesize / 8) < 0x7fffffff ? + aufile->samples * (opt->samplesize / 8) : 0x7fffffff; + + unsigned char *p = opt->header; + int no_of_chunks_processed = 0; + for (;;) { + if (!memcmp(p, "RIFF", 4)) { + p += 4; + WRITE_U32(p, size); + p += 4; + no_of_chunks_processed++; + } + if (!memcmp(p, "fmt ", 4)) { + p += 8; + if (aufile->outputFormat == WAV_FMT_FLOAT) { + WRITE_U16(p, 3); + } + else { + WRITE_U16(p, 1); + } + p += 2; + WRITE_U16(p, channels); + p += 2; + WRITE_U32(p, samplerate); + p += 4; + WRITE_U32(p, bytespersec); + p += 4; + WRITE_U16(p, align); + p += 2; + WRITE_U16(p, samplesize); + p += 2; + no_of_chunks_processed++; + } + if (no_of_chunks_processed == 2) { + break; + } + else + p++; + } + + p = opt->header + opt->header_size - 8; + if (!memcmp(p, "data", 4)) { + p += 4; + WRITE_U32(p, data_size); + p += 4; + fwrite(opt->header, opt->header_size, 1, aufile->sndfile); + } + return 1; +} + +/* + * Write a 80 bit IEEE854 big endian number as 10 octets. Destination is passed as pointer, + * End of destination (p+10) is returned. + */ + +static unsigned char* Convert_to_80bit_BE_IEEE854_Float(unsigned char* p, long double val ) +{ + unsigned long word32 = 0x401E; + + if (val > 0.L) + while (val < (long double)0x80000000) // scales value in the range 2^31...2^32 + word32--, val *= 2.L; // so you have the exponent + + *p++ = (unsigned char)(word32 >> 8); + *p++ = (unsigned char)(word32 >> 0); // write exponent, sign is assumed as '+' + word32 = (unsigned long) val; + *p++ = (unsigned char)(word32 >> 24); + *p++ = (unsigned char)(word32 >> 16); + *p++ = (unsigned char)(word32 >> 8); + *p++ = (unsigned char)(word32 >> 0); // write the upper 32 bit of the mantissa + word32 = (unsigned long) ((val - word32) * 4294967296.L); + *p++ = (unsigned char)(word32 >> 24); + *p++ = (unsigned char)(word32 >> 16); + *p++ = (unsigned char)(word32 >> 8); + *p++ = (unsigned char)(word32 >> 0); // write the lower 32 bit of the mantissa + + return p; +} + +static int write_aiff_header(audio_file *aufile) +{ + unsigned char header[54], + *p = header; + unsigned int bytes = (aufile->bits_per_sample + 7) / 8; + unsigned long data_size = aufile->samples * bytes; + unsigned long word32; + + // FORM chunk + *p++ = 'F'; + *p++ = 'O'; + *p++ = 'R'; + *p++ = 'M'; + + word32 = data_size + 0x2E; // size of the AIFF chunk + *p++ = (unsigned char)(word32 >> 24); + *p++ = (unsigned char)(word32 >> 16); + *p++ = (unsigned char)(word32 >> 8); + *p++ = (unsigned char)(word32 >> 0); + + *p++ = 'A'; + *p++ = 'I'; + *p++ = 'F'; + *p++ = 'F'; + // end of FORM chunk + + // COMM chunk + *p++ = 'C'; + *p++ = 'O'; + *p++ = 'M'; + *p++ = 'M'; + + word32 = 0x12; // size of this chunk + *p++ = (unsigned char)(word32 >> 24); + *p++ = (unsigned char)(word32 >> 16); + *p++ = (unsigned char)(word32 >> 8); + *p++ = (unsigned char)(word32 >> 0); + + word32 = aufile->channels; // channels + *p++ = (unsigned char)(word32 >> 8); + *p++ = (unsigned char)(word32 >> 0); + + word32 = aufile->samples / aufile->channels; // no. of sample frames + *p++ = (unsigned char)(word32 >> 24); + *p++ = (unsigned char)(word32 >> 16); + *p++ = (unsigned char)(word32 >> 8); + *p++ = (unsigned char)(word32 >> 0); + + word32 = aufile->bits_per_sample; // bits + *p++ = (unsigned char)(word32 >> 8); + *p++ = (unsigned char)(word32 >> 0); + + p = Convert_to_80bit_BE_IEEE854_Float (p, (long double)aufile->samplerate); // sample frequency as big endian 80 bit IEEE854 float + // End of COMM chunk + + // SSND chunk + *p++ = 'S'; + *p++ = 'S'; + *p++ = 'N'; + *p++ = 'D'; + + word32 = data_size + 0x08; // chunk length + *p++ = (unsigned char)(word32 >> 24); + *p++ = (unsigned char)(word32 >> 16); + *p++ = (unsigned char)(word32 >> 8); + *p++ = (unsigned char)(word32 >> 0); + + *p++ = 0; // offset + *p++ = 0; + *p++ = 0; + *p++ = 0; + + *p++ = 0; // block size + *p++ = 0; + *p++ = 0; + *p++ = 0; + + return fwrite(header, sizeof(header), 1, aufile->sndfile); +} + +static int write_audio_8bit(audio_file *aufile, void *sample_buffer, unsigned int samples) +{ + int ret; + unsigned int i; + unsigned char *sample_buffer8 = (unsigned char*)sample_buffer; + unsigned char *data = malloc(samples*aufile->bits_per_sample*sizeof(char)/8); + + aufile->samples += samples; + + for (i = 0; i < samples; i++) + data[i] = (sample_buffer8[i]+128) & 0xFF; + + ret = fwrite(data, samples*aufile->bits_per_sample/8, 1, aufile->sndfile); + + if (data) + free(data); + + return ret; +} + +static int write_audio_16bit(audio_file *aufile, void *sample_buffer, unsigned int samples) +{ + int ret; + unsigned int i; + short *sample_buffer16 = (short*)sample_buffer; + char *data = malloc(samples*aufile->bits_per_sample*sizeof(char)/8); + + aufile->samples += samples; + +#ifdef __MACOSX__ + if (aufile->endianness != machine_endianness) { +#else + if (aufile->endianness == machine_endianness) { +#endif + for (i = 0; i < samples; i++) { + data[i*2] = (char)(sample_buffer16[i] & 0xFF); + data[i*2+1] = (char)((sample_buffer16[i] >> 8) & 0xFF); + } + } + else { + for (i = 0; i < samples; i++) { + data[i*2+1] = (char)(sample_buffer16[i] & 0xFF); + data[i*2] = (char)((sample_buffer16[i] >> 8) & 0xFF); + } + } + + ret = fwrite(data, samples*aufile->bits_per_sample/8, 1, aufile->sndfile); + + if (data) + free(data); + + return ret; +} + +static int write_audio_24bit(audio_file *aufile, void *sample_buffer, unsigned int samples) +{ + int ret; + unsigned int i; + long *sample_buffer24 = (long*)sample_buffer; + char *data = malloc(samples*aufile->bits_per_sample*sizeof(char)/8); + + aufile->samples += samples; + + for (i = 0; i < samples; i++) { + data[i*3] = (char)(sample_buffer24[i] & 0xFF); + data[i*3+1] = (char)((sample_buffer24[i] >> 8) & 0xFF); + data[i*3+2] = (char)((sample_buffer24[i] >> 16) & 0xFF); + } + + ret = fwrite(data, samples*aufile->bits_per_sample/8, 1, aufile->sndfile); + + if (data) + free(data); + + return ret; +} + +static int write_audio_32bit(audio_file *aufile, void *sample_buffer, unsigned int samples) +{ + int ret; + unsigned int i; + long *sample_buffer32 = (long*)sample_buffer; + char *data = malloc(samples*aufile->bits_per_sample*sizeof(char)/8); + + aufile->samples += samples; + + for (i = 0; i < samples; i++) { + data[i*4] = (char)(sample_buffer32[i] & 0xFF); + data[i*4+1] = (char)((sample_buffer32[i] >> 8) & 0xFF); + data[i*4+2] = (char)((sample_buffer32[i] >> 16) & 0xFF); + data[i*4+3] = (char)((sample_buffer32[i] >> 24) & 0xFF); + } + + ret = fwrite(data, samples*aufile->bits_per_sample/8, 1, aufile->sndfile); + + if (data) + free(data); + + return ret; +} + +static int write_audio_float(audio_file *aufile, void *sample_buffer, unsigned int samples) +{ + int ret; + unsigned int i; + float *sample_buffer_f = (float*)sample_buffer; + unsigned char *data = malloc(samples*aufile->bits_per_sample*sizeof(char)/8); + + aufile->samples += samples; + + for (i = 0; i < samples; i++) { + int exponent, + mantissa, + negative = 0; + float in = sample_buffer_f[i]; + + data[i*4] = 0; + data[i*4+1] = 0; + data[i*4+2] = 0; + data[i*4+3] = 0; + if (in == 0.f) + continue; + + if (in < 0.0) { + in *= -1.0; + negative = 1; + } + in = (float)frexp(in, &exponent); + exponent += 126; + in *= (float)0x1000000; + mantissa = (((int)in) & 0x7FFFFF); + + if (negative) + data[i*4+3] |= 0x80; + + if (exponent & 0x01) + data[i*4+2] |= 0x80; + + data[i*4] = mantissa & 0xFF; + data[i*4+1] = (mantissa >> 8) & 0xFF; + data[i*4+2] |= (mantissa >> 16) & 0x7F; + data[i*4+3] |= (exponent >> 1) & 0x7F; + } + + ret = fwrite(data, samples*aufile->bits_per_sample/8, 1, aufile->sndfile); + + if (data) + free(data); + + return ret; +} + +void* output_to_PCM(double **input, void *sample_buffer, int channels, + int samples, int format) +{ + unsigned char ch; + int i; + + char *char_sample_buffer = (char*)sample_buffer; + short *short_sample_buffer = (short*)sample_buffer; + int *int_sample_buffer = (int*)sample_buffer; + float *float_sample_buffer = (float*)sample_buffer; + + /* + * Copy output to a standard PCM buffer + */ + switch (format) { + case WAV_FMT_8BIT: + for (ch = 0; ch < channels; ch++) { + for (i = 0; i < samples; i++) { + char_sample_buffer[(i*channels)+ch] = (char)input[ch][i]; + } + } + break; + case WAV_FMT_AIFF: + case WAV_FMT_16BIT: + for (ch = 0; ch < channels; ch++) { + for (i = 0; i < samples; i++) { + short_sample_buffer[(i*channels)+ch] = (short)input[ch][i]; + } + } + break; + case WAV_FMT_24BIT: + case WAV_FMT_32BIT: + for (ch = 0; ch < channels; ch++) { + for (i = 0; i < samples; i++) { + int_sample_buffer[(i*channels)+ch] = (int)input[ch][i]; + } + } + break; + case WAV_FMT_FLOAT: + for (ch = 0; ch < channels; ch++) { + for (i = 0; i < samples; i++) { + float_sample_buffer[(i*channels)+ch] = (float)input[ch][i]; + } + } + break; + } + + return sample_buffer; +} + +/* + * end of audio.c + */ diff --git a/audio.h b/audio.h new file mode 100644 index 0000000..3eef7e4 --- /dev/null +++ b/audio.h @@ -0,0 +1,153 @@ +/* WaveGain - Filename: AUDIO.H + * + * Copyright (c) 2002 - 2005 John Edwards + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Portions Copyright 2000-2002, Michael Smith + * + * AIFF/AIFC support from OggSquish, (c) 1994-1996 Monty + */ + + +#ifndef AUDIO_H_INCLUDED +#define AUDIO_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef long (*audio_read_func)(void *src, + double **buffer, + int samples, + int fast, + int chunk); + +typedef struct +{ + audio_read_func read_samples; + + void *readdata; + + long total_samples_per_channel; + int channels; + long rate; + int samplesize; + int endianness; + int format; + int std_out; + int apply_gain; + int header_size; + unsigned char *header; + + FILE *out; + char *filename; +} wavegain_opt; + +typedef struct +{ + int (*id_func)(unsigned char *buf, int len); /* Returns true if can load file */ + int id_data_len; /* Amount of data needed to id whether this can load the file */ + int (*open_func)(FILE *in, wavegain_opt *opt, unsigned char *buf, int buflen); + void (*close_func)(void *); + char *format; + char *description; +} input_format; + + +typedef struct { + short format; + short channels; + int samplerate; + int bytespersec; + short align; + short samplesize; +} wav_fmt; + +typedef struct { + short channels; + short samplesize; + long totalsamples; + long samplesread; + FILE *f; + short bigendian; +} wavfile; + +typedef struct { + short channels; + int totalframes; + short samplesize; + int rate; + int offset; + int blocksize; +} aiff_fmt; + +typedef wavfile aifffile; /* They're the same */ + +input_format *open_audio_file(FILE *in, wavegain_opt *opt); + +int raw_open(FILE *in, wavegain_opt *opt); +int wav_open(FILE *in, wavegain_opt *opt, unsigned char *buf, int buflen); +int aiff_open(FILE *in, wavegain_opt *opt, unsigned char *buf, int buflen); +int wav_id(unsigned char *buf, int len); +int aiff_id(unsigned char *buf, int len); +void wav_close(void *); +void raw_close(void *); + +long wav_read(void *, double **buffer, int samples, int fast, int chunk); +long wav_ieee_read(void *, double **buffer, int samples, int fast, int chunk); + +enum { + WAV_NO_FMT = 0, + WAV_FMT_8BIT, + WAV_FMT_16BIT, + WAV_FMT_24BIT, + WAV_FMT_32BIT, + WAV_FMT_FLOAT, + WAV_FMT_AIFF, + WAV_FMT_AIFC8, + WAV_FMT_AIFC16 +} file_formats; + +typedef struct +{ + int outputFormat; + FILE *sndfile; + unsigned long samplerate; + unsigned int bits_per_sample; + unsigned int channels; + unsigned long samples; + int endianness; + int format; +} audio_file; + +audio_file *open_output_audio_file(char *infile, wavegain_opt *opt); +int write_audio_file(audio_file *aufile, void *sample_buffer, int samples); +void close_audio_file(FILE *in, audio_file *aufile, wavegain_opt *opt); +static int write_wav_header(audio_file *aufile, wavegain_opt *opt, unsigned int file_size); +static int write_aiff_header(audio_file *aufile); +static int write_audio_8bit(audio_file *aufile, void *sample_buffer, unsigned int samples); +static int write_audio_16bit(audio_file *aufile, void *sample_buffer, unsigned int samples); +static int write_audio_24bit(audio_file *aufile, void *sample_buffer, unsigned int samples); +static int write_audio_32bit(audio_file *aufile, void *sample_buffer, unsigned int samples); +static int write_audio_float(audio_file *aufile, void *sample_buffer, unsigned int samples); +void* output_to_PCM(double **input, void *samplebuffer, int channels, int samples, int format); + +#ifdef __cplusplus +} +#endif +#endif /* AUDIO_H_INCLUDED */ diff --git a/config.h b/config.h new file mode 100644 index 0000000..885281c --- /dev/null +++ b/config.h @@ -0,0 +1,117 @@ +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + +/* config.h You may need to edit this to match your system. */ + +/* Enable recursive processing and pattern matching */ +#define ENABLE_RECURSIVE + +/* Define if you have the header file, and it defines `DIR'. */ +#undef HAVE_DIRENT_H + +/* Define if you don't have `vprintf' but do have `_doprnt.' */ +#undef HAVE_DOPRNT + +/* Define if you have the header file. */ +#define HAVE_ERRNO_H + +/* Define if you have the `floor' function. */ +#define HAVE_FLOOR + +/* Define if you have the `getcwd' function. */ +#define HAVE_GETCWD + +/* Define if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define if your system has a working `malloc' function. */ +#define HAVE_MALLOC + +/* Define if you have the `memmove' function. */ +#define HAVE_MEMMOVE + +/* Define if you have the header file. */ +#define HAVE_MEMORY_H + +/* Define if you have the `memset' function. */ +#define HAVE_MEMSET + +/* Define if you have the header file, and it defines `DIR'. */ +#undef HAVE_NDIR_H + +/* Define if you have the `pow' function. */ +#define HAVE_POW + +/* Define if `stat' has the bug that it succeeds when given the zero-length + file name argument. */ +#undef HAVE_STAT_EMPTY_STRING_BUG + +/* Define if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define if you have the header file. */ +#define HAVE_STDLIB_H + +/* Define if you have the `strchr' function. */ +#define HAVE_STRCHR + +/* Define if you have the `strdup' function. */ +#define HAVE_STRDUP + +/* Define if you have the `strerror' function. */ +#define HAVE_STRERROR + +/* Define if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define if you have the header file. */ +#define HAVE_STRING_H + +/* Define if you have the header file, and it defines `DIR'. */ +#undef HAVE_SYS_DIR_H + +/* Define if you have the header file, and it defines `DIR'. */ +#undef HAVE_SYS_NDIR_H + +/* Define if you have the header file. */ +#define HAVE_SYS_STAT_H + +/* Define if you have the header file. */ +#define HAVE_SYS_TYPES_H + +/* Define if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define if you have the header file. */ +#define HAVE_UTIME_H + +/* Define if you have the `vprintf' function. */ +#define HAVE_VPRINTF + +/* Define if `lstat' dereferences a symlink specified with a trailing slash. */ +#undef LSTAT_FOLLOWS_SLASHED_SYMLINK + +/* Define if you have the ANSI C header files. */ +#define STDC_HEADERS + +/* Define to empty if `const' does not conform to ANSI C. */ +//#undef const + +/* Define as `__inline' if that's what the C compiler calls it, or to nothing + if it is not supported. */ +#define inline __inline + +/* Define to `unsigned' if does not define. */ +//#undef size_t + +/* Define according to endianness of CPU - PC = LITTLE, APPLE/MAC = BIG, etc. */ +#define LITTLE 0 +#define BIG 1 +#ifdef __MACOSX__ +# define machine_endianness BIG +#else +# define machine_endianness LITTLE +#endif + +#endif /* _CONFIG_H_ */ + diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..49d7514 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,27 @@ +wavegain (1.2.6-0rarewares1) unstable; urgency=low + + * Upstream Release. + * Thanx john33! + + -- mike gan Mon, 22 Nov 2005 00:00:00 -0800 + +wavegain (1.2.5-0.1) unstable; urgency=low + + * Upstream Release. + * -0.1 erm... actually fix it + + -- mike gan Thu, 3 Oct 2005 23:00:00 -0700 + +wavegain (1.2.1-0.1) unstable; urgency=low + + * Upstream Release. + * -0.1 changes build environment + + -- mike gan Thu, 14 Apr 2005 00:00:00 -0700 + +wavegain (1.0.4-1) unstable; urgency=low + + * Initial Release. + + -- mike gan Sat, 15 May 2004 14:04:39 -0700 + diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..b8626c4 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +4 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..03459db --- /dev/null +++ b/debian/control @@ -0,0 +1,12 @@ +Source: wavegain +Section: sound +Priority: optional +Maintainer: mike gan +Build-Depends: debhelper (>= 4.0.0) +Standards-Version: 3.6.0 + +Package: wavegain +Architecture: any +Depends: ${shlibs:Depends} +Description: provides replaygain functionality for wav audio files + provides replaygain functionality for wav audio files diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..75b6796 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,8 @@ +This package was debianized by mike gan on +Sat, 15 May 2004 14:04:39 -0700. + +It was downloaded from: http://rarewares.org + +Upstream Author(s): John Edwards + +Copyright: GPL 2.1 diff --git a/debian/dirs b/debian/dirs new file mode 100644 index 0000000..e772481 --- /dev/null +++ b/debian/dirs @@ -0,0 +1 @@ +usr/bin diff --git a/debian/files b/debian/files new file mode 100644 index 0000000..58c7695 --- /dev/null +++ b/debian/files @@ -0,0 +1 @@ +wavegain_1.2.6-0rarewares1_i386.deb sound optional diff --git a/debian/rules b/debian/rules new file mode 100644 index 0000000..cf146c1 --- /dev/null +++ b/debian/rules @@ -0,0 +1,95 @@ +#!/usr/bin/make -f +# -*- makefile -*- +# Sample debian/rules that uses debhelper. +# GNU copyright 1997 to 1999 by Joey Hess. + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +CFLAGS = -Wall -g + +ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) + CFLAGS += -O0 +else + CFLAGS += -O2 +endif +ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS))) + INSTALL_PROGRAM += -s +endif + +configure: configure-stamp +configure-stamp: + dh_testdir + # Add here commands to configure the package. + + touch configure-stamp + + +build: build-stamp + +build-stamp: configure-stamp + dh_testdir + + # Add here commands to compile the package. + $(MAKE) -f Makefile.debian + #/usr/bin/docbook-to-man debian/wavegain.sgml > wavegain.1 + + touch build-stamp + +clean: + dh_testdir + dh_testroot + rm -f build-stamp configure-stamp + + # Add here commands to clean up after the build process. + -$(MAKE) -f Makefile.debian clean + + dh_clean + +install: build + dh_testdir + dh_testroot + dh_clean -k + dh_installdirs + + # Add here commands to install the package into debian/wavegain. + $(MAKE) -f Makefile.debian install DESTDIR=$(CURDIR)/debian/wavegain + + +# Build architecture-independent files here. +binary-indep: build install +# We have nothing to do by default. + +# Build architecture-dependent files here. +binary-arch: build install + dh_testdir + dh_testroot + dh_installchangelogs + dh_installdocs + dh_installexamples +# dh_install +# dh_installmenu +# dh_installdebconf +# dh_installlogrotate +# dh_installemacsen +# dh_installpam +# dh_installmime +# dh_installinit +# dh_installcron +# dh_installinfo + dh_installman + dh_link + dh_strip + dh_compress + dh_fixperms +# dh_perl +# dh_python +# dh_makeshlibs + dh_installdeb + dh_shlibdeps + dh_gencontrol + dh_md5sums + dh_builddeb + +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary install configure diff --git a/debian/wavegain.substvars b/debian/wavegain.substvars new file mode 100644 index 0000000..eee81af --- /dev/null +++ b/debian/wavegain.substvars @@ -0,0 +1 @@ +shlibs:Depends=libc6 (>= 2.3.5-1), libsndfile1 diff --git a/dither.c b/dither.c new file mode 100644 index 0000000..230cd22 --- /dev/null +++ b/dither.c @@ -0,0 +1,184 @@ +/* This program is licensed under the GNU Library General Public License, version 2, + * a copy of which is included with this program (with filename LICENSE.LGPL). + * + * (c) 2002 John Edwards + * mostly lifted from work by Frank Klemm + * random functions for dithering. + * + * last modified: $ID:$ + */ +#include "dither.h" +#include + +static const unsigned char Parity [256] = { // parity + 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1, + 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0, + 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0, + 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1, + 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0, + 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1, + 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1, + 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0 +}; + +static unsigned int __r1 = 1; +static unsigned int __r2 = 1; + + +/* + * This is a simple random number generator with good quality for audio purposes. + * It consists of two polycounters with opposite rotation direction and different + * periods. The periods are coprime, so the total period is the product of both. + * + * ------------------------------------------------------------------------------------------------- + * +-> |31:30:29:28:27:26:25:24:23:22:21:20:19:18:17:16:15:14:13:12:11:10: 9: 8: 7: 6: 5: 4: 3: 2: 1: 0| + * | ------------------------------------------------------------------------------------------------- + * | | | | | | | + * | +--+--+--+-XOR-+--------+ + * | | + * +--------------------------------------------------------------------------------------+ + * + * ------------------------------------------------------------------------------------------------- + * |31:30:29:28:27:26:25:24:23:22:21:20:19:18:17:16:15:14:13:12:11:10: 9: 8: 7: 6: 5: 4: 3: 2: 1: 0| <-+ + * ------------------------------------------------------------------------------------------------- | + * | | | | | + * +--+----XOR----+--+ | + * | | + * +----------------------------------------------------------------------------------------+ + * + * + * The first has an period of 3*5*17*257*65537, the second of 7*47*73*178481, + * which gives a period of 18.410.713.077.675.721.215. The result is the + * XORed values of both generators. + */ + + +unsigned int +random_int ( void ) +{ + unsigned int t1, t2, t3, t4; + + t3 = t1 = __r1; t4 = t2 = __r2; // Parity calculation is done via table lookup, this is also available + t1 &= 0xF5; t2 >>= 25; // on CPUs without parity, can be implemented in C and avoid unpredictable + t1 = Parity [t1]; t2 &= 0x63; // jumps and slow rotate through the carry flag operations. + t1 <<= 31; t2 = Parity [t2]; + + return (__r1 = (t3 >> 1) | t1 ) ^ (__r2 = (t4 + t4) | t2 ); +} + + + +double +Random_Equi ( double mult ) // gives a equal distributed random number +{ // between -2^31*mult and +2^31*mult + return mult * (int) random_int (); +} + +double +Random_Triangular ( double mult ) // gives a triangular distributed random number +{ // between -2^32*mult and +2^32*mult + return mult * ( (double) (int) random_int () + (double) (int) random_int () ); +} + +/*********************************************************************************************************************/ + +static const float F44_0 [16 + 32] = { + (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, + (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, + + (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, + (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, + + (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, + (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0, (float)0 +}; + + +static const float F44_1 [16 + 32] = { /* SNR(w) = 4.843163 dB, SNR = -3.192134 dB */ + (float) 0.85018292704024355931, (float) 0.29089597350995344721, (float)-0.05021866022121039450, (float)-0.23545456294599161833, + (float)-0.58362726442227032096, (float)-0.67038978965193036429, (float)-0.38566861572833459221, (float)-0.15218663390367969967, + (float)-0.02577543084864530676, (float) 0.14119295297688728127, (float) 0.22398848581628781612, (float) 0.15401727203382084116, + (float) 0.05216161232906000929, (float)-0.00282237820999675451, (float)-0.03042794608323867363, (float)-0.03109780942998826024, + + (float) 0.85018292704024355931, (float) 0.29089597350995344721, (float)-0.05021866022121039450, (float)-0.23545456294599161833, + (float)-0.58362726442227032096, (float)-0.67038978965193036429, (float)-0.38566861572833459221, (float)-0.15218663390367969967, + (float)-0.02577543084864530676, (float) 0.14119295297688728127, (float) 0.22398848581628781612, (float) 0.15401727203382084116, + (float) 0.05216161232906000929, (float)-0.00282237820999675451, (float)-0.03042794608323867363, (float)-0.03109780942998826024, + + (float) 0.85018292704024355931, (float) 0.29089597350995344721, (float)-0.05021866022121039450, (float)-0.23545456294599161833, + (float)-0.58362726442227032096, (float)-0.67038978965193036429, (float)-0.38566861572833459221, (float)-0.15218663390367969967, + (float)-0.02577543084864530676, (float) 0.14119295297688728127, (float) 0.22398848581628781612, (float) 0.15401727203382084116, + (float) 0.05216161232906000929, (float)-0.00282237820999675451, (float)-0.03042794608323867363, (float)-0.03109780942998826024, +}; + + +static const float F44_2 [16 + 32] = { /* SNR(w) = 10.060213 dB, SNR = -12.766730 dB */ + (float) 1.78827593892108555290, (float) 0.95508210637394326553, (float)-0.18447626783899924429, (float)-0.44198126506275016437, + (float)-0.88404052492547413497, (float)-1.42218907262407452967, (float)-1.02037566838362314995, (float)-0.34861755756425577264, + (float)-0.11490230170431934434, (float) 0.12498899339968611803, (float) 0.38065885268563131927, (float) 0.31883491321310506562, + (float) 0.10486838686563442765, (float)-0.03105361685110374845, (float)-0.06450524884075370758, (float)-0.02939198261121969816, + + (float) 1.78827593892108555290, (float) 0.95508210637394326553, (float)-0.18447626783899924429, (float)-0.44198126506275016437, + (float)-0.88404052492547413497, (float)-1.42218907262407452967, (float)-1.02037566838362314995, (float)-0.34861755756425577264, + (float)-0.11490230170431934434, (float) 0.12498899339968611803, (float) 0.38065885268563131927, (float) 0.31883491321310506562, + (float) 0.10486838686563442765, (float)-0.03105361685110374845, (float)-0.06450524884075370758, (float)-0.02939198261121969816, + + (float) 1.78827593892108555290, (float) 0.95508210637394326553, (float)-0.18447626783899924429, (float)-0.44198126506275016437, + (float)-0.88404052492547413497, (float)-1.42218907262407452967, (float)-1.02037566838362314995, (float)-0.34861755756425577264, + (float)-0.11490230170431934434, (float) 0.12498899339968611803, (float) 0.38065885268563131927, (float) 0.31883491321310506562, + (float) 0.10486838686563442765, (float)-0.03105361685110374845, (float)-0.06450524884075370758, (float)-0.02939198261121969816, +}; + + +static const float F44_3 [16 + 32] = { /* SNR(w) = 15.382598 dB, SNR = -29.402334 dB */ + (float) 2.89072132015058161445, (float) 2.68932810943698754106, (float) 0.21083359339410251227, (float)-0.98385073324997617515, + (float)-1.11047823227097316719, (float)-2.18954076314139673147, (float)-2.36498032881953056225, (float)-0.95484132880101140785, + (float)-0.23924057925542965158, (float)-0.13865235703915925642, (float) 0.43587843191057992846, (float) 0.65903257226026665927, + (float) 0.24361815372443152787, (float)-0.00235974960154720097, (float) 0.01844166574603346289, (float) 0.01722945988740875099, + + (float) 2.89072132015058161445, (float) 2.68932810943698754106, (float) 0.21083359339410251227, (float)-0.98385073324997617515, + (float)-1.11047823227097316719, (float)-2.18954076314139673147, (float)-2.36498032881953056225, (float)-0.95484132880101140785, + (float)-0.23924057925542965158, (float)-0.13865235703915925642, (float) 0.43587843191057992846, (float) 0.65903257226026665927, + (float) 0.24361815372443152787, (float)-0.00235974960154720097, (float) 0.01844166574603346289, (float) 0.01722945988740875099, + + (float) 2.89072132015058161445, (float) 2.68932810943698754106, (float) 0.21083359339410251227, (float)-0.98385073324997617515, + (float)-1.11047823227097316719, (float)-2.18954076314139673147, (float)-2.36498032881953056225, (float)-0.95484132880101140785, + (float)-0.23924057925542965158, (float)-0.13865235703915925642, (float) 0.43587843191057992846, (float) 0.65903257226026665927, + (float) 0.24361815372443152787, (float)-0.00235974960154720097, (float) 0.01844166574603346289, (float) 0.01722945988740875099 +}; + + +double +scalar16 ( const float* x, const float* y ) +{ + return x[ 0]*y[ 0] + x[ 1]*y[ 1] + x[ 2]*y[ 2] + x[ 3]*y[ 3] + + x[ 4]*y[ 4] + x[ 5]*y[ 5] + x[ 6]*y[ 6] + x[ 7]*y[ 7] + + x[ 8]*y[ 8] + x[ 9]*y[ 9] + x[10]*y[10] + x[11]*y[11] + + x[12]*y[12] + x[13]*y[13] + x[14]*y[14] + x[15]*y[15]; +} + + +void +Init_Dither ( int bits, int shapingtype ) +{ + static unsigned char default_dither [] = { 92, 92, 88, 84, 81, 78, 74, 67, 0, 0 }; + static const float* F [] = { F44_0, F44_1, F44_2, F44_3 }; + int index; + + if (shapingtype < 0) shapingtype = 0; + if (shapingtype > 3) shapingtype = 3; + index = bits - 11 - shapingtype; + if (index < 0) index = 0; + if (index > 9) index = 9; + + memset ( Dither.ErrorHistory , 0, sizeof (Dither.ErrorHistory ) ); + memset ( Dither.DitherHistory, 0, sizeof (Dither.DitherHistory) ); + + Dither.FilterCoeff = F [shapingtype]; + Dither.Mask = ((Uint64_t)-1) << (32 - bits); + Dither.Add = 0.5 * ((1L << (32 - bits)) - 1); + Dither.Dither = 0.01*default_dither[index] / (((Int64_t)1) << bits); +} + + + diff --git a/dither.h b/dither.h new file mode 100644 index 0000000..dd2784b --- /dev/null +++ b/dither.h @@ -0,0 +1,44 @@ +/* This program is licensed under the GNU Library General Public License, version 2, + * a copy of which is included with this program (with filename LICENSE.LGPL). + * + * (c) 2002 John Edwards + * + * rand_t header. + * + * last modified: $ID:$ + */ + +#ifndef __RAND_T_H +#define __RAND_T_H + +#include "misc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + const float* FilterCoeff; + Uint64_t Mask; + double Add; + float Dither; + float ErrorHistory [2] [16]; // max. 2 channels, 16th order Noise shaping + float DitherHistory [2] [16]; + int LastRandomNumber [2]; +} dither_t; + +extern dither_t Dither; +extern double doubletmp; +static const unsigned char Parity [256]; +unsigned int random_int ( void ); +extern double scalar16 ( const float* x, const float* y ); +extern double Random_Equi ( double mult ); +extern double Random_Triangular ( double mult ); +void Init_Dither ( int bits, int shapingtype ); + +#ifdef __cplusplus +} +#endif + +#endif /* __RAND_T_H */ + diff --git a/gain_analysis.c b/gain_analysis.c new file mode 100644 index 0000000..cbef014 --- /dev/null +++ b/gain_analysis.c @@ -0,0 +1,472 @@ +/* + * ReplayGainAnalysis - analyzes input samples and give the recommended dB change + * Copyright (C) 2001 David Robinson and Glen Sawyer + * Improvements and optimizations added by Frank Klemm, and by Marcel Müller + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * concept and filter values by David Robinson (David@Robinson.org) + * -- blame him if you think the idea is flawed + * original coding by Glen Sawyer (mp3gain@hotmail.com) + * -- blame him if you think this runs too slowly, or the coding is otherwise flawed + * + * lots of code improvements by Frank Klemm ( http://www.uni-jena.de/~pfk/mpp/ ) + * -- credit him for all the _good_ programming ;) + * + * + * For an explanation of the concepts and the basic algorithms involved, go to: + * http://www.replaygain.org/ + */ + +/* + * Here's the deal. Call + * + * InitGainAnalysis ( long samplefreq ); + * + * to initialize everything. Call + * + * AnalyzeSamples ( const Float_t* left_samples, + * const Float_t* right_samples, + * size_t num_samples, + * int num_channels ); + * + * as many times as you want, with as many or as few samples as you want. + * If mono, pass the sample buffer in through left_samples, leave + * right_samples NULL, and make sure num_channels = 1. + * + * GetTitleGain() + * + * will return the recommended dB level change for all samples analyzed + * SINCE THE LAST TIME you called GetTitleGain() OR InitGainAnalysis(). + * + * GetAlbumGain() + * + * will return the recommended dB level change for all samples analyzed + * since InitGainAnalysis() was called and finalized with GetTitleGain(). + * + * Pseudo-code to process an album: + * + * Float_t l_samples [4096]; + * Float_t r_samples [4096]; + * size_t num_samples; + * unsigned int num_songs; + * unsigned int i; + * + * InitGainAnalysis ( 44100 ); + * for ( i = 1; i <= num_songs; i++ ) { + * while ( ( num_samples = getSongSamples ( song[i], left_samples, right_samples ) ) > 0 ) + * AnalyzeSamples ( left_samples, right_samples, num_samples, 2 ); + * fprintf ("Recommended dB change for song %2d: %+6.2f dB\n", i, GetTitleGain() ); + * } + * fprintf ("Recommended dB change for whole album: %+6.2f dB\n", GetAlbumGain() ); + */ + +/* + * So here's the main source of potential code confusion: + * + * The filters applied to the incoming samples are IIR filters, + * meaning they rely on up to number of previous samples + * AND up to number of previous filtered samples. + * + * I set up the AnalyzeSamples routine to minimize memory usage and interface + * complexity. The speed isn't compromised too much (I don't think), but the + * internal complexity is higher than it should be for such a relatively + * simple routine. + * + * Optimization/clarity suggestions are welcome. + */ + +#include +#include +#include +#include + +#include "gain_analysis.h" + +typedef unsigned short Uint16_t; +typedef signed short Int16_t; +typedef unsigned int Uint32_t; +typedef signed int Int32_t; + +#define YULE_ORDER 10 +#define BUTTER_ORDER 2 +#define YULE_FILTER filterYule +#define BUTTER_FILTER filterButter +#define RMS_PERCENTILE 0.95 // percentile which is louder than the proposed level +#define MAX_SAMP_FREQ 48000. // maximum allowed sample frequency [Hz] +#define RMS_WINDOW_TIME 0.050 // Time slice size [s] +#define STEPS_per_dB 100. // Table entries per dB +#define MAX_dB 120. // Table entries for 0...MAX_dB (normal max. values are 70...80 dB) + +#define MAX_ORDER (BUTTER_ORDER > YULE_ORDER ? BUTTER_ORDER : YULE_ORDER) +#define MAX_SAMPLES_PER_WINDOW (size_t) (MAX_SAMP_FREQ * RMS_WINDOW_TIME) // max. Samples per Time slice +#define PINK_REF 64.82 //298640883795 // calibration value + +Float_t linprebuf [MAX_ORDER * 2]; +Float_t* linpre; // left input samples, with pre-buffer +Float_t lstepbuf [MAX_SAMPLES_PER_WINDOW + MAX_ORDER]; +Float_t* lstep; // left "first step" (i.e. post first filter) samples +Float_t loutbuf [MAX_SAMPLES_PER_WINDOW + MAX_ORDER]; +Float_t* lout; // left "out" (i.e. post second filter) samples +Float_t rinprebuf [MAX_ORDER * 2]; +Float_t* rinpre; // right input samples ... +Float_t rstepbuf [MAX_SAMPLES_PER_WINDOW + MAX_ORDER]; +Float_t* rstep; +Float_t routbuf [MAX_SAMPLES_PER_WINDOW + MAX_ORDER]; +Float_t* rout; +long sampleWindow; // number of samples required to reach number of milliseconds required for RMS window +long totsamp; +double lsum; +double rsum; +int freqindex; +int first; +static Uint32_t A [(size_t)(STEPS_per_dB * MAX_dB)]; +static Uint32_t B [(size_t)(STEPS_per_dB * MAX_dB)]; + +// for each filter: +// [0] 48 kHz, [1] 44.1 kHz, [2] 32 kHz, [3] 24 kHz, [4] 22050 Hz, [5] 16 kHz, [6] 12 kHz, [7] is 11025 Hz, [8] 8 kHz + +#ifdef WIN32 +#ifndef __GNUC__ +#pragma warning ( disable : 4305 ) +#endif +#endif + +static const Float_t ABYule[9][2*YULE_ORDER + 1] = { + {0.03857599435200, -3.84664617118067, -0.02160367184185, 7.81501653005538, -0.00123395316851,-11.34170355132042, -0.00009291677959, 13.05504219327545, -0.01655260341619,-12.28759895145294, 0.02161526843274, 9.48293806319790, -0.02074045215285, -5.87257861775999, 0.00594298065125, 2.75465861874613, 0.00306428023191, -0.86984376593551, 0.00012025322027, 0.13919314567432, 0.00288463683916 }, + {0.05418656406430, -3.47845948550071, -0.02911007808948, 6.36317777566148, -0.00848709379851, -8.54751527471874, -0.00851165645469, 9.47693607801280, -0.00834990904936, -8.81498681370155, 0.02245293253339, 6.85401540936998, -0.02596338512915, -4.39470996079559, 0.01624864962975, 2.19611684890774, -0.00240879051584, -0.75104302451432, 0.00674613682247, 0.13149317958808, -0.00187763777362 }, + {0.15457299681924, -2.37898834973084, -0.09331049056315, 2.84868151156327, -0.06247880153653, -2.64577170229825, 0.02163541888798, 2.23697657451713, -0.05588393329856, -1.67148153367602, 0.04781476674921, 1.00595954808547, 0.00222312597743, -0.45953458054983, 0.03174092540049, 0.16378164858596, -0.01390589421898, -0.05032077717131, 0.00651420667831, 0.02347897407020, -0.00881362733839 }, + {0.30296907319327, -1.61273165137247, -0.22613988682123, 1.07977492259970, -0.08587323730772, -0.25656257754070, 0.03282930172664, -0.16276719120440, -0.00915702933434, -0.22638893773906, -0.02364141202522, 0.39120800788284, -0.00584456039913, -0.22138138954925, 0.06276101321749, 0.04500235387352, -0.00000828086748, 0.02005851806501, 0.00205861885564, 0.00302439095741, -0.02950134983287 }, + {0.33642304856132, -1.49858979367799, -0.25572241425570, 0.87350271418188, -0.11828570177555, 0.12205022308084, 0.11921148675203, -0.80774944671438, -0.07834489609479, 0.47854794562326, -0.00469977914380, -0.12453458140019, -0.00589500224440, -0.04067510197014, 0.05724228140351, 0.08333755284107, 0.00832043980773, -0.04237348025746, -0.01635381384540, 0.02977207319925, -0.01760176568150 }, + {0.44915256608450, -0.62820619233671, -0.14351757464547, 0.29661783706366, -0.22784394429749, -0.37256372942400, -0.01419140100551, 0.00213767857124, 0.04078262797139, -0.42029820170918, -0.12398163381748, 0.22199650564824, 0.04097565135648, 0.00613424350682, 0.10478503600251, 0.06747620744683, -0.01863887810927, 0.05784820375801, -0.03193428438915, 0.03222754072173, 0.00541907748707 }, + {0.56619470757641, -1.04800335126349, -0.75464456939302, 0.29156311971249, 0.16242137742230, -0.26806001042947, 0.16744243493672, 0.00819999645858, -0.18901604199609, 0.45054734505008, 0.30931782841830, -0.33032403314006, -0.27562961986224, 0.06739368333110, 0.00647310677246, -0.04784254229033, 0.08647503780351, 0.01639907836189, -0.03788984554840, 0.01807364323573, -0.00588215443421 }, + {0.58100494960553, -0.51035327095184, -0.53174909058578, -0.31863563325245, -0.14289799034253, -0.20256413484477, 0.17520704835522, 0.14728154134330, 0.02377945217615, 0.38952639978999, 0.15558449135573, -0.23313271880868, -0.25344790059353, -0.05246019024463, 0.01628462406333, -0.02505961724053, 0.06920467763959, 0.02442357316099, -0.03721611395801, 0.01818801111503, -0.00749618797172 }, + {0.53648789255105, -0.25049871956020, -0.42163034350696, -0.43193942311114, -0.00275953611929, -0.03424681017675, 0.04267842219415, -0.04678328784242, -0.10214864179676, 0.26408300200955, 0.14590772289388, 0.15113130533216, -0.02459864859345, -0.17556493366449, -0.11202315195388, -0.18823009262115, -0.04060034127000, 0.05477720428674, 0.04788665548180, 0.04704409688120, -0.02217936801134 } +}; + +static const Float_t ABButter[9][2*BUTTER_ORDER + 1] = { + {0.98621192462708, -1.97223372919527, -1.97242384925416, 0.97261396931306, 0.98621192462708 }, + {0.98500175787242, -1.96977855582618, -1.97000351574484, 0.97022847566350, 0.98500175787242 }, + {0.97938932735214, -1.95835380975398, -1.95877865470428, 0.95920349965459, 0.97938932735214 }, + {0.97531843204928, -1.95002759149878, -1.95063686409857, 0.95124613669835, 0.97531843204928 }, + {0.97316523498161, -1.94561023566527, -1.94633046996323, 0.94705070426118, 0.97316523498161 }, + {0.96454515552826, -1.92783286977036, -1.92909031105652, 0.93034775234268, 0.96454515552826 }, + {0.96009142950541, -1.91858953033784, -1.92018285901082, 0.92177618768381, 0.96009142950541 }, + {0.95856916599601, -1.91542108074780, -1.91713833199203, 0.91885558323625, 0.95856916599601 }, + {0.94597685600279, -1.88903307939452, -1.89195371200558, 0.89487434461664, 0.94597685600279 } +}; + + +#ifdef WIN32 +#ifndef __GNUC__ +#pragma warning ( default : 4305 ) +#endif +#endif + +// When calling these filter procedures, make sure that ip[-order] and op[-order] point to real data! + +// If your compiler complains that "'operation on 'output' may be undefined", you can +// either ignore the warnings or uncomment the three "y" lines (and comment out the indicated line) + +static void +filterYule (const Float_t* input, Float_t* output, size_t nSamples, const Float_t* kernel) +{ + + while (nSamples--) { + *output = 1e-10 /* 1e-10 is a hack to avoid slowdown because of denormals */ + + input [0] * kernel[0] + - output[-1] * kernel[1] + + input [-1] * kernel[2] + - output[-2] * kernel[3] + + input [-2] * kernel[4] + - output[-3] * kernel[5] + + input [-3] * kernel[6] + - output[-4] * kernel[7] + + input [-4] * kernel[8] + - output[-5] * kernel[9] + + input [-5] * kernel[10] + - output[-6] * kernel[11] + + input [-6] * kernel[12] + - output[-7] * kernel[13] + + input [-7] * kernel[14] + - output[-8] * kernel[15] + + input [-8] * kernel[16] + - output[-9] * kernel[17] + + input [-9] * kernel[18] + - output[-10]* kernel[19] + + input [-10]* kernel[20]; + ++output; + ++input; + } +} + +static void +filterButter (const Float_t* input, Float_t* output, size_t nSamples, const Float_t* kernel) +{ + + while (nSamples--) { + *output = + input [0] * kernel[0] + - output[-1] * kernel[1] + + input [-1] * kernel[2] + - output[-2] * kernel[3] + + input [-2] * kernel[4]; + ++output; + ++input; + } +} + + +// returns a INIT_GAIN_ANALYSIS_OK if successful, INIT_GAIN_ANALYSIS_ERROR if not + +int +ResetSampleFrequency ( long samplefreq ) { + int i; + + // zero out initial values + for ( i = 0; i < MAX_ORDER; i++ ) + linprebuf[i] = lstepbuf[i] = loutbuf[i] = rinprebuf[i] = rstepbuf[i] = routbuf[i] = 0.; + + switch ( (int)(samplefreq) ) { + case 48000: freqindex = 0; break; + case 44100: freqindex = 1; break; + case 32000: freqindex = 2; break; + case 24000: freqindex = 3; break; + case 22050: freqindex = 4; break; + case 16000: freqindex = 5; break; + case 12000: freqindex = 6; break; + case 11025: freqindex = 7; break; + case 8000: freqindex = 8; break; + default: return INIT_GAIN_ANALYSIS_ERROR; + } + + sampleWindow = (int) ceil (samplefreq * RMS_WINDOW_TIME); + + lsum = 0.; + rsum = 0.; + totsamp = 0; + + memset ( A, 0, sizeof(A) ); + + return INIT_GAIN_ANALYSIS_OK; +} + +int +InitGainAnalysis ( long samplefreq ) +{ + if (ResetSampleFrequency(samplefreq) != INIT_GAIN_ANALYSIS_OK) { + return INIT_GAIN_ANALYSIS_ERROR; + } + + linpre = linprebuf + MAX_ORDER; + rinpre = rinprebuf + MAX_ORDER; + lstep = lstepbuf + MAX_ORDER; + rstep = rstepbuf + MAX_ORDER; + lout = loutbuf + MAX_ORDER; + rout = routbuf + MAX_ORDER; + + memset ( B, 0, sizeof(B) ); + + return INIT_GAIN_ANALYSIS_OK; +} + +// returns GAIN_ANALYSIS_OK if successful, GAIN_ANALYSIS_ERROR if not + +static __inline double fsqr(const double d) +{ return d*d; +} + +int +AnalyzeSamples ( const Float_t* left_samples, const Float_t* right_samples, size_t num_samples, int num_channels ) +{ + const Float_t* curleft; + const Float_t* curright; + long batchsamples; + long cursamples; + long cursamplepos; + int i; + + if ( num_samples == 0 ) + return GAIN_ANALYSIS_OK; + + cursamplepos = 0; + batchsamples = num_samples; + + switch ( num_channels) { + case 1: right_samples = left_samples; + case 2: break; + default: return GAIN_ANALYSIS_ERROR; + } + + if ( num_samples < MAX_ORDER ) { + memcpy ( linprebuf + MAX_ORDER, left_samples , num_samples * sizeof(Float_t) ); + memcpy ( rinprebuf + MAX_ORDER, right_samples, num_samples * sizeof(Float_t) ); + } + else { + memcpy ( linprebuf + MAX_ORDER, left_samples, MAX_ORDER * sizeof(Float_t) ); + memcpy ( rinprebuf + MAX_ORDER, right_samples, MAX_ORDER * sizeof(Float_t) ); + } + + while ( batchsamples > 0 ) { + cursamples = batchsamples > sampleWindow-totsamp ? sampleWindow - totsamp : batchsamples; + if ( cursamplepos < MAX_ORDER ) { + curleft = linpre+cursamplepos; + curright = rinpre+cursamplepos; + if (cursamples > MAX_ORDER - cursamplepos ) + cursamples = MAX_ORDER - cursamplepos; + } + else { + curleft = left_samples + cursamplepos; + curright = right_samples + cursamplepos; + } + + YULE_FILTER ( curleft , lstep + totsamp, cursamples, ABYule[freqindex]); + YULE_FILTER ( curright, rstep + totsamp, cursamples, ABYule[freqindex]); + + BUTTER_FILTER ( lstep + totsamp, lout + totsamp, cursamples, ABButter[freqindex]); + BUTTER_FILTER ( rstep + totsamp, rout + totsamp, cursamples, ABButter[freqindex]); + + curleft = lout + totsamp; // Get the squared values + curright = rout + totsamp; + + i = cursamples % 16; + while (i--) + { lsum += fsqr(*curleft++); + rsum += fsqr(*curright++); + } + i = cursamples / 16; + while (i--) + { lsum += fsqr(curleft[0]) + + fsqr(curleft[1]) + + fsqr(curleft[2]) + + fsqr(curleft[3]) + + fsqr(curleft[4]) + + fsqr(curleft[5]) + + fsqr(curleft[6]) + + fsqr(curleft[7]) + + fsqr(curleft[8]) + + fsqr(curleft[9]) + + fsqr(curleft[10]) + + fsqr(curleft[11]) + + fsqr(curleft[12]) + + fsqr(curleft[13]) + + fsqr(curleft[14]) + + fsqr(curleft[15]); + curleft += 16; + rsum += fsqr(curright[0]) + + fsqr(curright[1]) + + fsqr(curright[2]) + + fsqr(curright[3]) + + fsqr(curright[4]) + + fsqr(curright[5]) + + fsqr(curright[6]) + + fsqr(curright[7]) + + fsqr(curright[8]) + + fsqr(curright[9]) + + fsqr(curright[10]) + + fsqr(curright[11]) + + fsqr(curright[12]) + + fsqr(curright[13]) + + fsqr(curright[14]) + + fsqr(curright[15]); + curright += 16; + } + + batchsamples -= cursamples; + cursamplepos += cursamples; + totsamp += cursamples; + if ( totsamp == sampleWindow ) { // Get the Root Mean Square (RMS) for this set of samples + double val = STEPS_per_dB * 10. * log10 ( (lsum+rsum) / totsamp * 0.5 + 1.e-37 ); + int ival = (int) val; + if ( ival < 0 ) ival = 0; + if ( ival >= (int)(sizeof(A)/sizeof(*A)) ) ival = sizeof(A)/sizeof(*A) - 1; + A [ival]++; + lsum = rsum = 0.; + memmove ( loutbuf , loutbuf + totsamp, MAX_ORDER * sizeof(Float_t) ); + memmove ( routbuf , routbuf + totsamp, MAX_ORDER * sizeof(Float_t) ); + memmove ( lstepbuf, lstepbuf + totsamp, MAX_ORDER * sizeof(Float_t) ); + memmove ( rstepbuf, rstepbuf + totsamp, MAX_ORDER * sizeof(Float_t) ); + totsamp = 0; + } + if ( totsamp > sampleWindow ) // somehow I really screwed up: Error in programming! Contact author about totsamp > sampleWindow + return GAIN_ANALYSIS_ERROR; + } + if ( num_samples < MAX_ORDER ) { + memmove ( linprebuf, linprebuf + num_samples, (MAX_ORDER-num_samples) * sizeof(Float_t) ); + memmove ( rinprebuf, rinprebuf + num_samples, (MAX_ORDER-num_samples) * sizeof(Float_t) ); + memcpy ( linprebuf + MAX_ORDER - num_samples, left_samples, num_samples * sizeof(Float_t) ); + memcpy ( rinprebuf + MAX_ORDER - num_samples, right_samples, num_samples * sizeof(Float_t) ); + } + else { + memcpy ( linprebuf, left_samples + num_samples - MAX_ORDER, MAX_ORDER * sizeof(Float_t) ); + memcpy ( rinprebuf, right_samples + num_samples - MAX_ORDER, MAX_ORDER * sizeof(Float_t) ); + } + + return GAIN_ANALYSIS_OK; +} + + +static Float_t +analyzeResult ( Uint32_t* Array, size_t len ) +{ + Uint32_t elems; + Int32_t upper; + size_t i; + + elems = 0; + for ( i = 0; i < len; i++ ) + elems += Array[i]; + if ( elems == 0 ) + return GAIN_NOT_ENOUGH_SAMPLES; + + upper = (Int32_t) ceil (elems * (1. - RMS_PERCENTILE)); + for ( i = len; i-- > 0; ) { + if ( (upper -= Array[i]) <= 0 ) + break; + } + + return (Float_t) ((Float_t)PINK_REF - (Float_t)i / (Float_t)STEPS_per_dB); +} + + +Float_t +GetTitleGain ( void ) +{ + Float_t retval; + int i; + + retval = analyzeResult ( A, sizeof(A)/sizeof(*A) ); + + for ( i = 0; i < (int)(sizeof(A)/sizeof(*A)); i++ ) { + B[i] += A[i]; + A[i] = 0; + } + + for ( i = 0; i < MAX_ORDER; i++ ) + linprebuf[i] = lstepbuf[i] = loutbuf[i] = rinprebuf[i] = rstepbuf[i] = routbuf[i] = 0.f; + + totsamp = 0; + lsum = rsum = 0.; + return retval; +} + + +Float_t +GetAlbumGain ( void ) +{ + return analyzeResult ( B, sizeof(B)/sizeof(*B) ); +} + +/* end of gain_analysis.c */ diff --git a/gain_analysis.h b/gain_analysis.h new file mode 100644 index 0000000..2f17984 --- /dev/null +++ b/gain_analysis.h @@ -0,0 +1,56 @@ +/* + * ReplayGainAnalysis - analyzes input samples and give the recommended dB change + * Copyright (C) 2001 David Robinson and Glen Sawyer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * concept and filter values by David Robinson (David@Robinson.org) + * -- blame him if you think the idea is flawed + * coding by Glen Sawyer (mp3gain@hotmail.com) 735 W 255 N, Orem, UT 84057-4505 USA + * -- blame him if you think this runs too slowly, or the coding is otherwise flawed + * + * For an explanation of the concepts and the basic algorithms involved, go to: + * http://www.replaygain.org/ + */ + +#ifndef GAIN_ANALYSIS_H +#define GAIN_ANALYSIS_H + +#include + +#define GAIN_NOT_ENOUGH_SAMPLES -24601 +#define GAIN_ANALYSIS_ERROR 0 +#define GAIN_ANALYSIS_OK 1 + +#define INIT_GAIN_ANALYSIS_ERROR 0 +#define INIT_GAIN_ANALYSIS_OK 1 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef double Float_t; // Type used for filtering + +int InitGainAnalysis ( long samplefreq ); +int AnalyzeSamples ( const Float_t* left_samples, const Float_t* right_samples, size_t num_samples, int num_channels ); +int ResetSampleFrequency ( long samplefreq ); +Float_t GetTitleGain ( void ); +Float_t GetAlbumGain ( void ); + +#ifdef __cplusplus +} +#endif + +#endif /* GAIN_ANALYSIS_H */ diff --git a/getopt.c b/getopt.c new file mode 100644 index 0000000..bb4e893 --- /dev/null +++ b/getopt.c @@ -0,0 +1,1047 @@ +/* Getopt for GNU. + NOTE: getopt is now part of the C library, so if you don't know what + "Keep this file name-space clean" means, talk to drepper@gnu.org + before changing it! + + Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99 + Free Software Foundation, Inc. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* This tells Alpha OSF/1 not to define a getopt prototype in . + Ditto for AIX 3.2 and . */ +#ifndef _NO_PROTO +# define _NO_PROTO +#endif + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#if !defined __STDC__ || !__STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +# ifndef const +# define const +# endif +#endif + +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 +# include +# if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +# define ELIDE_CODE +# endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +/* Don't include stdlib.h for non-GNU C libraries because some of them + contain conflicting prototypes for getopt. */ +# include +# include +#endif /* GNU C library. */ + +#ifdef VMS +# include +# if HAVE_STRING_H - 0 +# include +# endif +#endif + +#ifndef _ +/* This is for other GNU distributions with internationalized messages. + When compiling libc, the _ macro is predefined. */ +# ifdef HAVE_LIBINTL_H +# include +# define _(msgid) gettext (msgid) +# else +# define _(msgid) (msgid) +# endif +#endif + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable POSIXLY_CORRECT disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "getopt.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* 1003.2 says this must be 1 before any call. */ +int optind = 1; + +/* Formerly, initialization of getopt depended on optind==0, which + causes problems with re-calling getopt as programs generally don't + know that. */ + +int __getopt_initialized; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +int optopt = '?'; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return -1 with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +/* Value of POSIXLY_CORRECT environment variable. */ +static char *posixly_correct; + +#ifdef __GNU_LIBRARY__ +/* We want to avoid inclusion of string.h with non-GNU libraries + because there are many ways it can cause trouble. + On some systems, it contains special magic macros that don't work + in GCC. */ +# include +# define my_index strchr +#else + +#include + +/* Avoid depending on library functions or files + whose names are inconsistent. */ + +#ifndef getenv +extern char *getenv (); +#endif + +static char * +my_index (str, chr) + const char *str; + int chr; +{ + while (*str) + { + if (*str == chr) + return (char *) str; + str++; + } + return 0; +} + +/* If using GCC, we can safely declare strlen this way. + If not using GCC, it is ok not to declare it. */ +#ifdef __GNUC__ +/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. + That was relevant to code that was here before. */ +# if (!defined __STDC__ || !__STDC__) && !defined strlen +/* gcc with -traditional declares the built-in strlen to return int, + and has done so at least since version 2.4.5. -- rms. */ +extern int strlen (const char *); +# endif /* not __STDC__ */ +#endif /* __GNUC__ */ + +#endif /* not __GNU_LIBRARY__ */ + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +#ifdef _LIBC +/* Bash 2.0 gives us an environment variable containing flags + indicating ARGV elements that should not be considered arguments. */ + +/* Defined in getopt_init.c */ +extern char *__getopt_nonoption_flags; + +static int nonoption_flags_max_len; +static int nonoption_flags_len; + +static int original_argc; +static char *const *original_argv; + +/* Make sure the environment variable bash 2.0 puts in the environment + is valid for the getopt call we must make sure that the ARGV passed + to getopt is that one passed to the process. */ +static void +__attribute__ ((unused)) +store_args_and_env (int argc, char *const *argv) +{ + /* XXX This is no good solution. We should rather copy the args so + that we can compare them later. But we must not use malloc(3). */ + original_argc = argc; + original_argv = argv; +} +# ifdef text_set_element +text_set_element (__libc_subinit, store_args_and_env); +# endif /* text_set_element */ + +# define SWAP_FLAGS(ch1, ch2) \ + if (nonoption_flags_len > 0) \ + { \ + char __tmp = __getopt_nonoption_flags[ch1]; \ + __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ + __getopt_nonoption_flags[ch2] = __tmp; \ + } +#else /* !_LIBC */ +# define SWAP_FLAGS(ch1, ch2) +#endif /* _LIBC */ + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +#if defined __STDC__ && __STDC__ +static void exchange (char **); +#endif + +static void +exchange (argv) + char **argv; +{ + int bottom = first_nonopt; + int middle = last_nonopt; + int top = optind; + char *tem; + + /* Exchange the shorter segment with the far end of the longer segment. + That puts the shorter segment into the right place. + It leaves the longer segment in the right place overall, + but it consists of two parts that need to be swapped next. */ + +#ifdef _LIBC + /* First make sure the handling of the `__getopt_nonoption_flags' + string can work normally. Our top argument must be in the range + of the string. */ + if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) + { + /* We must extend the array. The user plays games with us and + presents new arguments. */ + char *new_str = malloc (top + 1); + if (new_str == NULL) + nonoption_flags_len = nonoption_flags_max_len = 0; + else + { + memset (__mempcpy (new_str, __getopt_nonoption_flags, + nonoption_flags_max_len), + '\0', top + 1 - nonoption_flags_max_len); + nonoption_flags_max_len = top + 1; + __getopt_nonoption_flags = new_str; + } + } +#endif + + while (top > middle && middle > bottom) + { + if (top - middle > middle - bottom) + { + /* Bottom segment is the short one. */ + int len = middle - bottom; + register int i; + + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } + else + { + /* Top segment is the short one. */ + int len = top - middle; + register int i; + + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + SWAP_FLAGS (bottom + i, middle + i); + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } + } + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (optind - last_nonopt); + last_nonopt = optind; +} + +/* Initialize the internal data when the first call is made. */ + +#if defined __STDC__ && __STDC__ +static const char *_getopt_initialize (int, char *const *, const char *); +#endif +static const char * +_getopt_initialize (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + /* Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + first_nonopt = last_nonopt = optind; + + nextchar = NULL; + + posixly_correct = getenv ("POSIXLY_CORRECT"); + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (posixly_correct != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + +#ifdef _LIBC + if (posixly_correct == NULL + && argc == original_argc && argv == original_argv) + { + if (nonoption_flags_max_len == 0) + { + if (__getopt_nonoption_flags == NULL + || __getopt_nonoption_flags[0] == '\0') + nonoption_flags_max_len = -1; + else + { + const char *orig_str = __getopt_nonoption_flags; + int len = nonoption_flags_max_len = strlen (orig_str); + if (nonoption_flags_max_len < argc) + nonoption_flags_max_len = argc; + __getopt_nonoption_flags = + (char *) malloc (nonoption_flags_max_len); + if (__getopt_nonoption_flags == NULL) + nonoption_flags_max_len = -1; + else + memset (__mempcpy (__getopt_nonoption_flags, orig_str, len), + '\0', nonoption_flags_max_len - len); + } + } + nonoption_flags_len = nonoption_flags_max_len; + } + else + nonoption_flags_len = 0; +#endif + + return optstring; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns -1. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +int +_getopt_internal (argc, argv, optstring, longopts, longind, long_only) + int argc; + char *const *argv; + const char *optstring; + const struct option *longopts; + int *longind; + int long_only; +{ + optarg = NULL; + + if (optind == 0 || !__getopt_initialized) + { + if (optind == 0) + optind = 1; /* Don't scan ARGV[0], the program name. */ + optstring = _getopt_initialize (argc, argv, optstring); + __getopt_initialized = 1; + } + + /* Test whether ARGV[optind] points to a non-option argument. + Either it does not have option syntax, or there is an environment flag + from the shell indicating it is not an option. The later information + is only used when the used in the GNU libc. */ +#ifdef _LIBC +# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \ + || (optind < nonoption_flags_len \ + && __getopt_nonoption_flags[optind] == '1')) +#else +# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') +#endif + + if (nextchar == NULL || *nextchar == '\0') + { + /* Advance to the next ARGV-element. */ + + /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been + moved back by the user (who may also have changed the arguments). */ + if (last_nonopt > optind) + last_nonopt = optind; + if (first_nonopt > optind) + first_nonopt = optind; + + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc && NONOPTION_P) + optind++; + last_nonopt = optind; + } + + /* The special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !strcmp (argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return -1; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if (NONOPTION_P) + { + if (ordering == REQUIRE_ORDER) + return -1; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Skip the initial punctuation. */ + + nextchar = (argv[optind] + 1 + + (longopts != NULL && argv[optind][1] == '-')); + } + + /* Decode the current option-ARGV-element. */ + + /* Check whether the ARGV-element is a long option. + + If long_only and the ARGV-element has the form "-f", where f is + a valid short option, don't consider it an abbreviated form of + a long option that starts with f. Otherwise there would be no + way to give the -f short option. + + On the other hand, if there's a long option "fubar" and + the ARGV-element is "-fu", do consider that an abbreviation of + the long option, just like "--fu", and not "-f" with arg "u". + + This distinction seems to be the most useful approach. */ + + if (longopts != NULL + && (argv[optind][1] == '-' + || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1]))))) + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = -1; + int option_index; + + for (nameend = nextchar; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) + == (unsigned int) strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, _("%s: option `%s' is ambiguous\n"), + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + optopt = 0; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + optind++; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (opterr) + { + if (argv[optind - 1][1] == '-') + /* --option */ + fprintf (stderr, + _("%s: option `--%s' doesn't allow an argument\n"), + argv[0], pfound->name); + else + /* +option or -option */ + fprintf (stderr, + _("%s: option `%c%s' doesn't allow an argument\n"), + argv[0], argv[optind - 1][0], pfound->name); + } + + nextchar += strlen (nextchar); + + optopt = pfound->val; + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + optopt = pfound->val; + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' + || my_index (optstring, *nextchar) == NULL) + { + if (opterr) + { + if (argv[optind][1] == '-') + /* --option */ + fprintf (stderr, _("%s: unrecognized option `--%s'\n"), + argv[0], nextchar); + else + /* +option or -option */ + fprintf (stderr, _("%s: unrecognized option `%c%s'\n"), + argv[0], argv[optind][0], nextchar); + } + nextchar = (char *) ""; + optind++; + optopt = 0; + return '?'; + } + } + + /* Look at and handle the next short option-character. */ + + { + char c = *nextchar++; + char *temp = my_index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') + { + if (opterr) + { + if (posixly_correct) + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, _("%s: illegal option -- %c\n"), + argv[0], c); + else + fprintf (stderr, _("%s: invalid option -- %c\n"), + argv[0], c); + } + optopt = c; + return '?'; + } + /* Convenience. Treat POSIX -W foo same as long option --foo */ + if (temp[0] == 'W' && temp[1] == ';') + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = 0; + int option_index; + + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + return c; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + + /* optarg is now the argument, see if it's in the + table of longopts. */ + + for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) == strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"), + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + return '?'; + } + if (pfound != NULL) + { + option_index = indfound; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (opterr) + fprintf (stderr, _("\ +%s: option `-W %s' doesn't allow an argument\n"), + argv[0], pfound->name); + + nextchar += strlen (nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + nextchar = NULL; + return 'W'; /* Let the application handle it. */ + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else + optarg = NULL; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, + _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + +int +getopt (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + return _getopt_internal (argc, argv, optstring, + (const struct option *) 0, + (int *) 0, + 0); +} + +#endif /* Not ELIDE_CODE. */ + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt (argc, argv, "abc:d:0123456789"); + if (c == -1) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/getopt.h b/getopt.h new file mode 100644 index 0000000..91eb54e --- /dev/null +++ b/getopt.h @@ -0,0 +1,169 @@ +/* Declarations for getopt. + Copyright (C) 1989,90,91,92,93,94,96,97,98 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#ifndef _GETOPT_H + +#ifndef __need_getopt +# define _GETOPT_H 1 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int opterr; + +/* Set to an option character which was unrecognized. */ + +extern int optopt; + +#ifndef __need_getopt +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +struct option +{ +# if defined __STDC__ && __STDC__ + const char *name; +# else + char *name; +# endif + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +# define no_argument 0 +# define required_argument 1 +# define optional_argument 2 +#endif /* need getopt */ + + +/* Get definitions and prototypes for functions to process the + arguments in ARGV (ARGC of them, minus the program name) for + options given in OPTS. + + Return the option character from OPTS just read. Return -1 when + there are no more options. For unrecognized options, or options + missing arguments, `optopt' is set to the option letter, and '?' is + returned. + + The OPTS string is a list of characters which are recognized option + letters, optionally followed by colons, specifying that that letter + takes an argument, to be placed in `optarg'. + + If a letter in OPTS is followed by two colons, its argument is + optional. This behavior is specific to the GNU `getopt'. + + The argument `--' causes premature termination of argument + scanning, explicitly telling `getopt' that there are no more + options. + + If OPTS begins with `--', then non-option arguments are treated as + arguments to the option '\0'. This behavior is specific to the GNU + `getopt'. */ + +#if defined __STDC__ && __STDC__ +# ifdef __GNU_LIBRARY__ +/* Many other libraries have conflicting prototypes for getopt, with + differences in the consts, in stdlib.h. To avoid compilation + errors, only prototype getopt for the GNU C library. */ +extern int getopt (int __argc, char *const *__argv, const char *__shortopts); +# else /* not __GNU_LIBRARY__ */ +extern int getopt (); +# endif /* __GNU_LIBRARY__ */ + +# ifndef __need_getopt +extern int getopt_long (int __argc, char *const *__argv, const char *__shortopts, + const struct option *__longopts, int *__longind); +extern int getopt_long_only (int __argc, char *const *__argv, + const char *__shortopts, + const struct option *__longopts, int *__longind); + +/* Internal only. Users should not call this directly. */ +extern int _getopt_internal (int __argc, char *const *__argv, + const char *__shortopts, + const struct option *__longopts, int *__longind, + int __long_only); +# endif +#else /* not __STDC__ */ +extern int getopt (); +# ifndef __need_getopt +extern int getopt_long (); +extern int getopt_long_only (); + +extern int _getopt_internal (); +# endif +#endif /* __STDC__ */ + +#ifdef __cplusplus +} +#endif + +/* Make sure we later can get all the definitions and declarations. */ +#undef __need_getopt + +#endif /* getopt.h */ diff --git a/getopt1.c b/getopt1.c new file mode 100644 index 0000000..9cd0644 --- /dev/null +++ b/getopt1.c @@ -0,0 +1,188 @@ +/* getopt_long and getopt_long_only entry points for GNU getopt. + Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98 + Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "getopt.h" + +#if !defined __STDC__ || !__STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +#ifndef const +#define const +#endif +#endif + +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 +#include +#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +#define ELIDE_CODE +#endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +#include +#endif + +#ifndef NULL +#define NULL 0 +#endif + +int +getopt_long (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 0); +} + +/* Like getopt_long, but '-' as well as '--' can indicate a long option. + If an option that starts with '-' (not '--') doesn't match a long option, + but does match a short option, it is parsed as a short option + instead. */ + +int +getopt_long_only (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 1); +} + + +#endif /* Not ELIDE_CODE. */ + +#ifdef TEST + +#include + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = + { + {"add", 1, 0, 0}, + {"append", 0, 0, 0}, + {"delete", 1, 0, 0}, + {"verbose", 0, 0, 0}, + {"create", 0, 0, 0}, + {"file", 1, 0, 0}, + {0, 0, 0, 0} + }; + + c = getopt_long (argc, argv, "abc:d:0123456789", + long_options, &option_index); + if (c == -1) + break; + + switch (c) + { + case 0: + printf ("option %s", long_options[option_index].name); + if (optarg) + printf (" with arg %s", optarg); + printf ("\n"); + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case 'd': + printf ("option d with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/i18n.h b/i18n.h new file mode 100644 index 0000000..535101d --- /dev/null +++ b/i18n.h @@ -0,0 +1,18 @@ +#ifndef I18N_H +#define I18N_H + +#ifdef ENABLE_NLS +#include +#define _(X) gettext(X) +#else +#define _(X) (X) +#define textdomain(X) +#define bindtextdomain(X, Y) +#endif +#ifdef gettext_noop +#define N_(X) gettext_noop(X) +#else +#define N_(X) (X) +#endif + +#endif diff --git a/main.c b/main.c new file mode 100644 index 0000000..02883b6 --- /dev/null +++ b/main.c @@ -0,0 +1,682 @@ +/* + * function: ReplayGain support for Wave files (http://www.replaygain.org) + * + * Reads in a Vorbis file, figures out the peak and ReplayGain levels and + * applies the Gain tags to the Wave file + * + * This program is distributed under the GNU General Public License, version + * 2.1. A copy of this license is included with this source. + * + * Copyright (C) 2002-2004 John Edwards + * Additional code by Magnus Holmgren and Gian-Carlo Pascutto + * Linux patch by Marc Brooker + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "getopt.h" +#include +#include +#include +#include +#include "config.h" +#include "gain_analysis.h" +#include "i18n.h" +#include "audio.h" +#include "misc.h" +#include "wavegain.h" +#include "main.h" +#include "dither.h" + +#ifdef _WIN32 +#include +#endif + +#ifdef ENABLE_RECURSIVE +#include "recurse.h" +#endif + +/*This is an ugly way of doing this - but it works for the moment*/ +#ifndef MAX_PATH +#define MAX_PATH 512 +#endif + +int write_log = 0; +char log_file_name[MAX_PATH]; +extern double total_samples; +extern double total_files; + +/** + * \brief Allocate a FILE_LIST node. + * + * Allocate a FILE_LIST node. filename is set to file while track_peak and + * track_gain are set to NO_PEAK and NO_GAIN respectively. The rest of the + * fields are set to zero. + * + * \param file name of file to get a node for. + * \return the allocated node or NULL (in which case a message has been + * printed). + */ +static FILE_LIST* alloc_node(const char* file) +{ + FILE_LIST* node; + + node = calloc(1, sizeof(*node)); + + if (node != NULL) { + node->filename = strdup(file); +// node->filename = _strdup(file); + + if (node->filename != NULL) { + node->track_peak = NO_PEAK; + node->track_gain = NO_GAIN; + node->dc_offset[0] = 0.; + node->dc_offset[1] = 0.; + node->offset[0] = 0.; + node->offset[1] = 0.; + return node; + } + free(node); + } + + fprintf(stderr, _("Out of memory\n")); + return NULL; +} + + +/** + * \brief Allocate a new FILE_LIST node and add it to the end of a list. + * + * Allocate a new FILE_LIST node and add it to the end of a list. + * + * \param list pointer to the list (first node) pointer. If the list + * pointer contains NULL, it is set to the new node. Otherwise + * the new node is added the last node in the list. + * \param file name of the file to get a node for. + * \return 0 if successful and -1 if the node couldn't be allocated (in + * which case a message has been printed). + */ +int add_to_list(FILE_LIST** list, const char* file) +{ + if (*list == NULL) { + *list = alloc_node(file); + + if (*list == NULL) + return -1; + } + else { + FILE_LIST* node = *list; + + while (node->next_file != NULL) + node = node->next_file; + + node->next_file = alloc_node(file); + + if (node->next_file == NULL) + return -1; + } + + return 0; +} + + +/** + * Free all nodes in a FILE_LIST list. + * + * \param list pointer to first node in list. Can be NULL, in which case + * nothing is done. + */ +void free_list(FILE_LIST* list) +{ + FILE_LIST* next; + + while (list != NULL) { + next = list->next_file; + free((void *) list->filename); + free(list); + list = next; + } +} + +static char s_floatToAscii[256]; +const char* ftos(float f, const char* format) +{ + sprintf(s_floatToAscii, format, f); + return s_floatToAscii; +} + +/** + * \brief Processs the file in file_list. + * + * Process the files in file_list. If settings->album is set, the files are + * considered to belong to one album. + * + * \param file_list list of files to process. + * \param settings settings and global variables. + * \return 0 if successful and -1 if an error occured (in which case a + * message has been printed). + */ +int process_files(FILE_LIST* file_list, SETTINGS* settings, const char* dir) +{ + FILE_LIST* file; + float factor_clip, + audiophile_gain; + double Gain, + scale, + dB, + album_dc_offset[2] = {0., 0.}; + int need_to_process = 0; + + settings->album_peak = NO_PEAK; + + if (file_list == NULL) + return 0; + + settings->first_file = 1; + + /* Analyze the files */ + for (file = file_list; file; file = file->next_file) { + if (file->filename == '\0') + continue; + + if (!get_gain(file->filename, &file->track_peak, &file->track_gain, + file->dc_offset, file->offset, settings)) { + file->filename = 0; + continue; + } + if (file->dc_offset[0] * 32768 <= -1.0 || file->dc_offset[0] * 32768 >= 1.0 || + file->dc_offset[1] * 32768 <= -1.0 || file->dc_offset[1] * 32768 >= 1.0) + need_to_process = 1; + album_dc_offset[0] += file->offset[0]; + album_dc_offset[1] += file->offset[1]; + } + + album_dc_offset[0] /= total_samples; + album_dc_offset[1] /= total_samples; + + if (!settings->no_offset && settings->adc) { + int dc_l = (int)(album_dc_offset[0] * 32768 * -1); + int dc_r = (int)(album_dc_offset[1] * 32768 * -1); + fprintf(stderr, " ********************* Album DC Offset | %4d | %4d | ***************\n", + dc_l, dc_r); + if(write_log) { + log_error(" ********************* Album DC Offset | %4d | %4d | ***************\n", + dc_l, dc_r); + } + } + + fprintf(stderr, "\n"); + if(write_log) + log_error("\n"); + + if (settings->audiophile) { + Gain = GetAlbumGain() + settings->man_gain; + scale = (float) pow(10., Gain * 0.05); + settings->set_album_gain = 0; + if(settings->clip_prev) { + factor_clip = (float) (32767./( settings->album_peak + 1)); + if(scale < factor_clip) + factor_clip = 1.f; + else + factor_clip /= (float)scale; + scale *= factor_clip; + } + if (settings->scale) { + fprintf(stdout, "%8.6lf", scale); + } + + dB = 20. * log10(scale); + audiophile_gain = (float) dB; + if (audiophile_gain < 0.01 && audiophile_gain > -0.01 && !need_to_process) { + fprintf(stderr, " No Album Gain adjustment or DC Offset correction required, exiting.\n"); + if(write_log) + log_error(" No Album Gain adjustment or DC Offset correction required, exiting.\n"); + settings->set_album_gain = 1; + } + else { + fprintf(stderr, " Recommended Album Gain: %+6.2f dB\tScale: %6.4f\n\n", audiophile_gain, scale); + if(write_log) + log_error(" Recommended Album Gain: %+6.2f dB\tScale: %6.4f\n\n", audiophile_gain, scale); + } + } + + if(settings->apply_gain) { /* Write radio and audiophile gains. */ + total_files = 0.0; + for (file = file_list; file; file = file->next_file) { + if (settings->audiophile && settings->set_album_gain == 1) + break; + if (settings->radio && (file->track_gain < 0.01 && file->track_gain > -0.01) && !need_to_process) { + fprintf(stderr, " No Title Gain adjustment or DC Offset correction required for file: %s, skipping.\n", file->filename); + if(write_log) + log_error(" No Title Gain adjustment or DC Offset correction required for file: %s, skipping.\n", file->filename); + } + else if (!write_gains(file->filename, file->track_gain, audiophile_gain, file->track_peak, + file->dc_offset, album_dc_offset, settings)) { + fprintf(stderr, " Error processing GAIN for file - %s\n", file->filename); + continue; + } + } + } + + fprintf(stderr, "\n WaveGain Processing completed normally\n"); + if(write_log) + log_error("\n WaveGain Processing completed normally\n\n"); + +#ifdef _WIN32 + if (settings->cmd) { /* execute user command */ + FILE_LIST* file; + char buff[MAX_PATH]; + char *b, *p, *q; + float track_scale = 1.0; + + SetEnvironmentVariable("ALBUM_GAIN", ftos(audiophile_gain, "%.2f")); + SetEnvironmentVariable("ALBUM_SCALE", ftos((float)scale, "%.5f")); + SetEnvironmentVariable("ALBUM_NEW_PEAK", ftos(settings->album_peak * (float)scale, "%.0f")); + SetEnvironmentVariable("ALBUM_PEAK", ftos(settings->album_peak, "%.0f")); + + for (file = file_list; file; file = file->next_file) { + if (file->filename == '\0') + continue; + + if (dir[0] == '.' && dir[1] == '\\') dir += 2; + + strcpy(buff, file->filename); + b = (buff[0] == '.' && buff[1] == '\\') ? buff+2 : buff; + SetEnvironmentVariable("INPUT_FILE", b); + + p = strrchr(b, '\\'); + if (p) { + p[0] = '\0'; ++p; + SetEnvironmentVariable("INPUT_FDIR", b); + SetEnvironmentVariable("INPUT_RDIR", b); // assume dir = "." + } + else { + p = b; + SetEnvironmentVariable("INPUT_FDIR", "."); + SetEnvironmentVariable("INPUT_RDIR", dir); + } + + q = strrchr(p, '.'); + if (q) q[0] = '\0'; + SetEnvironmentVariable("INPUT_NAME", p); + + track_scale = (float)(pow(10., file->track_gain * 0.05)); + SetEnvironmentVariable("TRACK_SCALE", ftos(track_scale, "%.5f")); + SetEnvironmentVariable("TRACK_GAIN", ftos(file->track_gain, "%.2f")); + SetEnvironmentVariable("TRACK_NEW_PEAK", ftos(file->track_peak, "%.0f")); + SetEnvironmentVariable("TRACK_PEAK", ftos(file->track_peak / track_scale, "%.0f")); + + SetEnvironmentVariable("DC_OFFSET_L", ftos((int)(file->dc_offset[0]*(-32768)), "%.0f")); + SetEnvironmentVariable("DC_OFFSET_R", ftos((int)(file->dc_offset[1]*(-32768)), "%.0f")); + + fprintf(stderr, "\n Executing command on \"%s\":\n\n", file->filename); + system(settings->cmd); + } + } +#endif + + return 0; +} + + +/** + * Print out a list of options and the command line syntax. + */ +static void usage(void) +{ + fprintf(stdout, _("WaveGain v" WAVEGAIN_VERSION " Compiled " __DATE__ ".\n")); + fprintf(stdout, _("Copyright (c) 2002-2005 John Edwards \n")); + fprintf(stdout, _("Additional code by Magnus Holmgren, Gian-Carlo Pascutto, and Tycho\n\n")); +#ifdef _WIN32 + fprintf(stdout, " Usage: wavegain [options] input.wav [...] [-e cmd [args]]\n\n"); +#else + fprintf(stdout, " Usage: wavegain [options] input.wav [...]\n\n"); +#endif + fprintf(stdout, " OPTIONS\n"); + fprintf(stdout, " -h, --help Prints this help information.\n"); + fprintf(stdout, " -a, --album Use ReplayGain Audiophile/Album gain setting, or\n"); + fprintf(stdout, " -r, --radio Use ReplayGain Radio/Single Track gain setting(DEFAULT).\n"); + fprintf(stdout, " -q, --adc Apply Album based DC Offset correction.\n"); + fprintf(stdout, " DEFAULT is Track based DC Offset correction.\n"); + fprintf(stdout, " -p, --no_offset Do NOT apply DC Offset correction.\n"); + fprintf(stdout, " -c, --calculate Calculates and prints gain settings, and DC Offsets BUT\n"); + fprintf(stdout, " DOES NOT APPLY THEM - This is the DEFAULT.\n"); + fprintf(stdout, " -x, --scale Writes scale values to stdout in the format: n.nnnnnn\n"); + fprintf(stdout, " In Album mode it only writes the Album Scale value, and\n"); + fprintf(stdout, " in Title mode it only writes the Title Scale values.\n"); + fprintf(stdout, " ONLY works in Calculation mode.\n"); + fprintf(stdout, " -y, --apply Calculates and APPLIES gain settings, and applies\n"); + fprintf(stdout, " DC Offset correction.\n"); +#ifdef ENABLE_RECURSIVE + fprintf(stdout, " -z, --recursive Search for files recursively, each folder as an album\n"); +#endif + fprintf(stdout, " -l, --log Write log file.(Default filename = WGLog.txt)\n"); + fprintf(stdout, " -f, --logfile Specify log filename. (Assumes -l if present.)\n"); + fprintf(stdout, " -n, --noclip NO Clipping Prevention.\n"); + fprintf(stdout, " -d, --dither X Dither output, where X =\n"); + fprintf(stdout, " 0 for dither OFF (default).\n"); + fprintf(stdout, " 1 for dither without Noise Shaping.\n"); + fprintf(stdout, " 2 for dither with Light Noise Shaping.\n"); + fprintf(stdout, " 3 for dither with Medium Noise Shaping.\n"); + fprintf(stdout, " 4 for dither with Heavy Noise Shaping.\n"); + fprintf(stdout, " -t, --limiter Apply 6dB Hard Limiter to output.\n"); + fprintf(stdout, " -g, --gain X Apply additional Manual Gain adjustment in decibels, where\n"); + fprintf(stdout, " X = any floating point number between -12.0 and +12.0.\n"); + fprintf(stdout, " Clipping Prevention WILL be applied UNLESS '-n' is used.\n"); + fprintf(stdout, " -s, --fast Calculates and prints gain settings - DOES NOT APPLY THEM.\n"); + fprintf(stdout, " NOTE: This method does NOT process all samples, it only\n"); + fprintf(stdout, " processes 200 x 16k chunks of samples. Results will\n"); + fprintf(stdout, " NOT be as accurate as a full analysis but, with most\n"); + fprintf(stdout, " material, will be within +/- 0.5db. Files of 8,192,000\n"); + fprintf(stdout, " real samples, or less, will be analysed in full.\n"); + fprintf(stdout, " DC Offset is neither calculated nor corrected in\n"); + fprintf(stdout, " FAST mode.\n"); + fprintf(stdout, " -o, --stdout Write output file to stdout.\n"); + fprintf(stdout, " FORMAT OPTIONS (One option ONLY may be used)\n"); + fprintf(stdout, " -b, --bits X Set output sample format, where X =\n"); + fprintf(stdout, " 1 for 8 bit unsigned PCM data.\n"); + fprintf(stdout, " 2 for 16 bit signed PCM data.\n"); + fprintf(stdout, " 3 for 24 bit signed PCM data.\n"); + fprintf(stdout, " 4 for 32 bit signed PCM data.\n"); + fprintf(stdout, " 5 for 32 bit floats.\n"); + fprintf(stdout, " 6 for 16 bit 'aiff' format.\n"); + fprintf(stdout, " NOTE: By default, the output file will be of the same bitwidth\n"); + fprintf(stdout, " and type as the input file.\n"); +#ifdef _WIN32 + fprintf(stdout, " -e, --exec Cmd Execute a command after wavegain.\n"); + fprintf(stdout, " The following environment variables are available:\n"); + fprintf(stdout, " INPUT_FILE, INPUT_FDIR, INPUT_RDIR, INPUT_NAME,\n"); + fprintf(stdout, " TRACK_GAIN, TRACK_PEAK, TRACK_SCALE, TRACK_NEW_PEAK,\n"); + fprintf(stdout, " ALBUM_GAIN, ALBUM_PEAK, ALBUM_SCALE, ALBUM_NEW_PEAK,\n"); + fprintf(stdout, " DC_OFFSET_L, DC_OFFSET_R\n"); +#endif + fprintf(stdout, " INPUT FILES\n"); + fprintf(stdout, " WaveGain input files may be 8, 16, 24 or 32 bit integer, or floating point\n"); + fprintf(stdout, " wave files with 1 or 2 channels and a sample rate of 48000Hz, 44100Hz,\n"); + fprintf(stdout, " 32000Hz, 24000Hz, 22050Hz, 16000Hz, 12000Hz, 11025Hz or 8000Hz.\n"); + fprintf(stdout, " 16 bit integer 'aiff' files are also supported.\n"); + fprintf(stdout, " Wildcards (?, *) can be used in the filename, or '-' for stdin.\n"); + + return; +} + + +const static struct option long_options[] = { + {"help", 0, NULL, 'h'}, + {"album", 0, NULL, 'a'}, + {"radio", 0, NULL, 'r'}, + {"adc", 0, NULL, 'q'}, + {"no_offset", 0, NULL, 'p'}, + {"calculate", 0, NULL, 'c'}, + {"scale", 0, NULL, 'x'}, + {"apply", 0, NULL, 'y'}, + {"fast", 0, NULL, 's'}, + {"stdout", 0, NULL, 'o'}, +#ifdef ENABLE_RECURSIVE + {"recursive", 0, NULL, 'z'}, +#endif + {"log", 0, NULL, 'l'}, + {"logfile", 1, NULL, 'f'}, + {"noclip", 0, NULL, 'n'}, + {"dither", 1, NULL, 'd'}, + {"limiter", 0, NULL, 't'}, + {"gain", 1, NULL, 'g'}, + {"bits", 1, NULL, 'b'}, +#ifdef _WIN32 + {"exec", 1, NULL, 'e'}, +#endif + {NULL, 0, NULL , 0} +}; + + +#ifdef ENABLE_RECURSIVE +#define ARG_STRING "harqpcxysozlf:nd:tg:b:e:" +#else +#define ARG_STRING "harqpcxysolf:nd:tg:b:e:" +#endif + + +int main(int argc, char** argv) +{ + SETTINGS settings; + int option_index = 1, + ret, + i, + bits; + + char CmdDir[MAX_PATH]; + char *p; + + memset(&settings, 0, sizeof(settings)); + settings.first_file = 1; + settings.clip_prev = 1; + settings.outbitwidth = 16; + settings.format = WAV_NO_FMT; + +#ifdef _WIN32 + /* Is this good enough? Or do we need to consider multi-byte codepages as + * well? + */ + SetConsoleOutputCP(GetACP()); + + GetModuleFileName(NULL, CmdDir, MAX_PATH); + p = strrchr(CmdDir, '\\') + 1; + p[0] = '\0'; +#endif + + while ((ret = getopt_long(argc, argv, ARG_STRING, long_options, &option_index)) != -1) { + switch(ret) { + case 0: + fprintf(stderr, "Internal error parsing command line options\n"); + exit(1); + break; + case 'h': + usage(); + exit(0); + break; + case 'a': + settings.audiophile = 1; + break; + case 'r': + settings.radio = 1; + break; + case 'q': + settings.adc = 1; + break; + case 'p': + settings.no_offset = 1; + break; + case 'c': + settings.apply_gain = 0; + break; + case 'x': + settings.scale = 1; + break; + case 'y': + settings.apply_gain = 1; + settings.scale = 0; + break; + case 's': + settings.fast = 1; + break; + case 'o': + settings.std_out = 1; + break; +#ifdef ENABLE_RECURSIVE + case 'z': + settings.recursive = 1; + break; +#endif + case 'l': + write_log = 1; +#ifdef _WIN32 + strcpy(log_file_name, CmdDir); + strcat(log_file_name, LOG_NAME); +#else + strcpy(log_file_name, LOG_NAME); +#endif + break; + case 'f': + write_log = 1; + strcpy(log_file_name, optarg); + break; + case 'n': + settings.clip_prev = 0; + break; + case 'd': + settings.dithering = 1; + if(sscanf(optarg, "%d", &settings.shapingtype) != 1) { + fprintf(stderr, "Warning: dither type %s not recognised, using default\n", optarg); + break; + } + if (settings.shapingtype == 0) + settings.dithering = 0; + else if (settings.shapingtype == 1) + settings.shapingtype = 0; + else if (settings.shapingtype == 2) + settings.shapingtype = 1; + else if (settings.shapingtype == 3) + settings.shapingtype = 2; + else if (settings.shapingtype == 4) + settings.shapingtype = 3; + break; + case 't': + settings.limiter = 1; + break; + case 'g': + if(sscanf(optarg, "%f", &settings.man_gain) != 1) { + fprintf(stderr, "Warning: manual gain %s not recognised, ignoring\n", optarg); + break; + } + if(settings.man_gain < -12.0) { + fprintf(stderr, "Warning: manual gain %s is out of range, " + "applying gain of -12.0dB\n", optarg); + settings.man_gain = -12.0; + } + else if(settings.man_gain > 12.0) { + fprintf(stderr, "Warning: manual gain %s is out of range, " + "applying gain of +12.0dB\n", optarg); + settings.man_gain = -12.0; + } + break; + case 'b': + if(sscanf(optarg, "%d", &bits) != 1) { + fprintf(stderr, "Warning: output format %s not recognised, using default\n", optarg); + break; + } + if (bits == 1) { + settings.outbitwidth = 8; + settings.format = WAV_FMT_8BIT; + break; + } + else if (bits == 2) { + settings.outbitwidth = 16; + settings.format = WAV_FMT_16BIT; + break; + } + else if (bits == 3) { + settings.outbitwidth = 24; + settings.format = WAV_FMT_24BIT; + break; + } + else if (bits == 4) { + settings.outbitwidth = 32; + settings.format = WAV_FMT_32BIT; + break; + } + else if (bits == 5) { + settings.outbitwidth = 32; + settings.format = WAV_FMT_FLOAT; + break; + } + else if (bits == 6) { + settings.outbitwidth = 16; + settings.format = WAV_FMT_AIFF; + break; + } + else { + fprintf(stderr, "Warning: output format %s not recognised, using default\n", optarg); + break; + } + break; +#ifdef _WIN32 + case 'e': { + char *p; + int k; + char * lpPart[MAX_PATH]={NULL}; + + settings.cmd = (char *) malloc(1024*8); /* 8Kb is XP's limit */ + if (settings.cmd == NULL) { + fprintf(stderr, "Failed to allocate memory for cmd...\n"); + return 1; + } + + p = settings.cmd; + + k = GetFullPathName(argv[optind - 1], 1024*8, p, lpPart); + k = GetShortPathName(p, p, MAX_PATH); + if (k == 0) { + p += sprintf (p, "%s", argv[optind - 1]); + } else { + p += k; + } + + for (k = optind; k < argc; ++k) { + p += sprintf(p, " \"%s\"", argv[k]); + } + + argc = optind; + break; + } +#endif + } + } + + + if (optind >= argc) { + fprintf(stderr, _("No files specified.\n")); + usage(); + return EXIT_SUCCESS; + } + + if (write_log) { + log_error("Command line:\n\t"); + for(i = 0; i < argc; i++) + log_error("%s ", argv[i]); + log_error("\n"); + } + + if (!strcmp(argv[optind], "-")) { + float track_peak, track_gain; + double dc_offset; + double offset; + if (!get_gain("-", &track_peak, &track_gain, + &dc_offset, &offset, &settings)) + return -1; + } + else { + for (i = optind; i < argc; ++i) { +#ifdef ENABLE_RECURSIVE + if (process_argument(argv[i], &settings) < 0) { + free_list(settings.file_list); + return EXIT_FAILURE; + } +#else + if (add_to_list(&settings.file_list, argv[i]) < 0) + return EXIT_FAILURE; +#endif + } + + /* Process files (perhaps remaining) in list */ + ret = process_files(settings.file_list, &settings, "."); + free_list(settings.file_list); + settings.file_list = NULL; + if (settings.cmd) free(settings.cmd); + settings.cmd = NULL; + } + + return (ret < 0) ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/main.h b/main.h new file mode 100644 index 0000000..13f8860 --- /dev/null +++ b/main.h @@ -0,0 +1,67 @@ +#ifndef MAIN_H +#define MAIN_H + +/* Some version numbers */ +#define WAVEGAIN_VERSION "1.2.6" + +#define BUFFER_LEN 16384 +#define TEMP_NAME "wavegain.tmp" +#define LOG_NAME "WGLog.txt" + +#define NO_GAIN -10000.f + +#ifdef _WIN32 +#include +#endif + +/** Information about a file to process */ +typedef struct file_list +{ + struct file_list* next_file; + const char* filename; + float track_gain; + float track_peak; + double dc_offset[2]; + double offset[2]; +} FILE_LIST; + + +/** Settings and misc other global data */ +typedef struct settings +{ + FILE_LIST* file_list; /**< Files to process (possibly as an album) */ +#ifdef ENABLE_RECURSIVE + char* pattern; /**< Pattern to match file names against */ +#endif + int first_file; /**< About to process first file in directory */ + float man_gain; /**< Apply Manual Gain entered by user */ + float album_peak; /**< Will end up storing the highest value of the tracks analyzed */ + int audiophile; /**< Calculate Album gain */ + int scale; /**< write Scale values to stdout */ + int apply_gain; /**< Apply the calculated gain - album or track */ + int set_album_gain; /**< Don't apply the calculated album gain if set */ + int fast; /**< Use the fast routines for RG analysis */ + int std_out; /**< Write output file to stdout */ + int radio; /**< Calculate Title gain */ + int adc; /**< Apply Album based DC Offset correction (default is Track based) */ + int no_offset; /**< Do NOT apply any DC Offset */ + int write_log; /**< Write a log of gain calculations, etc */ + char *log_data; /**< Pointer to data to be written to log file */ + int clip_prev; /**< Whether, or not, to apply clipping prevention */ + int dithering; /**< Apply dithering to output */ + int shapingtype; /**< Noise shaping to use in dithering */ + int limiter; /**< Apply Hard limiter */ + unsigned int outbitwidth; /**< bitwidth of desired output */ + unsigned int format; /**< libsndfile format of desired output */ +#ifdef ENABLE_RECURSIVE + int recursive; +#endif + char* cmd; +} SETTINGS; + + +extern int add_to_list(FILE_LIST** list, const char* file); +extern void free_list(FILE_LIST* list); +extern int process_files(FILE_LIST* file_list, SETTINGS* settings, const char* dir); + +#endif /* MAIN_H */ diff --git a/misc.c b/misc.c new file mode 100644 index 0000000..45759a5 --- /dev/null +++ b/misc.c @@ -0,0 +1,111 @@ +/* + * Misc utility functions + * + * This program is distributed under the GNU General Public License, version + * 2.1. A copy of this license is included with this source. + * + * Copyright (C) 2002 Gian-Carlo Pascutto and Magnus Holmgren + */ +#include +#include +#include +#include +#include "config.h" +#include "i18n.h" +#include "misc.h" + +#ifndef _WIN32 +#include +#include +#endif + +extern char log_file_name[]; + +/** + * \brief Display a file (or other I/O) error message. + * + * Display a file (or other I/O) error message. First a message is formatted + * and printed, followed by the error text, terminated by a line feed. + * + * \param message message format to display. + * \param ... printf-arguments used to format the message. + */ +void file_error(const char* message, ...) +{ + int err_num = errno; + va_list args; + + va_start(args, message); + vfprintf(stderr, message, args); + va_end(args); + + fprintf(stderr, strerror(err_num)); + fprintf(stderr, "\n"); +} + + +/** + * \brief Get the last component in a path. + * + * Get the last component in a path. If no directory separator is found, + * return the path as is. + * + * \param path path to get last component of. + * \return the last path component, or path. + */ +char* last_path(const char* path) +{ + int i; + + for (i = strlen(path) - 1; i >= 0; i--) { +#ifdef _WIN32 + if ((path[i] == '\\') || (path[i] == ':')) { +#else + if (path[i] == '/') { +#endif + return (char*) &path[i + 1]; + } + } + + return (char*) path; +} + +void log_error(const char *fmt, ...) +{ + va_list ap; + FILE *fp; + char msgbuf[1024]; + char *bufp = msgbuf; + + /* + * A really rough sanity check to protect against blatant buffer overrun + */ + if (strlen(fmt) > 750) + sprintf(msgbuf, "%s %s", " ", fmt); + else { + va_start(ap, fmt); + + vsprintf(bufp, fmt, ap); + + va_end(ap); + + if (errno != 0) { + bufp = msgbuf + strlen(msgbuf); + sprintf(bufp, " error is: %s (%d)", strerror(errno), errno); + errno = 0; + } + } + + va_start(ap, fmt); + + if ((fp = fopen(log_file_name, "a")) == (FILE *)NULL) + return; + + fprintf(fp, "%s", msgbuf); + fflush(fp); + fclose(fp); + + va_end(ap); +} + + diff --git a/misc.h b/misc.h new file mode 100644 index 0000000..f75b5c2 --- /dev/null +++ b/misc.h @@ -0,0 +1,16 @@ +#ifndef MISC_H +#define MISC_H + +#if defined _WIN32 && !defined __MINGW32__ // Microsoft and Intel call it __int64 +typedef signed __int64 Int64_t; +typedef unsigned __int64 Uint64_t; +#else +typedef signed long long Int64_t; +typedef unsigned long long Uint64_t; +#endif + +void file_error(const char* message, ...); +char* last_path(const char* path); +extern void log_error(const char *fmt, ...); + +#endif /* MISC_H */ diff --git a/recurse.c b/recurse.c new file mode 100644 index 0000000..4016423 --- /dev/null +++ b/recurse.c @@ -0,0 +1,596 @@ +/* + * Process files recursively, using an optional pattern + * + * This program is distributed under the GNU General Public License, version + * 2.1. A copy of this license is included with this source. + * + * Copyright (C) 2002 Magnus Holmgren + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef ENABLE_RECURSIVE + +#include +#include +#include +#include +#include "i18n.h" +#include "misc.h" +#include "recurse.h" + +#ifdef _WIN32 +#include +#include +#else +#include +#include +#include +#include +#endif + + +#ifdef _WIN32 +#define PATH_SEPARATOR "\\" +#define PATH_SEPARATOR_CHAR '\\' +#else +#define PATH_SEPARATOR "/" +#define PATH_SEPARATOR_CHAR '/' +#endif + + +typedef struct directory +{ + const char* name; /* Name of the read file (or directory) */ + int read_error; /* True if an error has occured */ + + /* The following stuff is used internally */ +#ifdef _WIN32 + WIN32_FIND_DATA find_data; + HANDLE find_handle; +#else + DIR* dir; + struct dirent* entry; +#endif + const char* full_path; +} DIRECTORY; + + +/** + * See if a path refers to a directory. + * + * \param path path to examine. + * \returns 1 if path is a directory, 0 if it isn't a directory, and -1 for + * any errors. + */ +static int is_dir(const char* path) +{ + struct stat stat_buf; + + if (stat(path, &stat_buf) != 0) { + return -1; + } + + /* Is this the proper way to check for a directory? */ + return (stat_buf.st_mode & S_IFDIR) ? 1 : 0; +} + + +/** + * \brief See if a string contains wildcard characters. + * + * See if a string contains wildcard characters, as used by match(). + * + * \param string text string to examine. + * \return 1 if the string contains pattern characters, 0 otherwise. + */ +static int contains_pattern(const char* string) +{ + while (*string) { + switch (*string) { + case '?': + case '*': + return 1; + + case '\\': + /* Accept a terminating \ as a literal \ */ + if (string[1]) + string++; + /* Fall through */ + + default: + string++; + break; + } + } + + return 0; +} + + +/** + * \brief Compare two characters for equality. + * + * Compare two characters for equality. Placed as a separate function since + * case should be considered on some platforms. + * + * \param c1 first character to compare. + * \param c2 second character to compare. + * \return 1 if the characters are equal, 0 otherwise. + */ +inline static int equal(const char c1, const char c2) +{ +#ifdef _WIN32 + return (toupper(c1) == toupper(c2)) ? 1 : 0; +#else + return (c1 == c2) ? 1 : 0; +#endif +} + + +/** + * \brief Match a text against a pattern. + * + * Match a text against a pattern. The pattern may contain the wildcards '*' + * and '?'. '*' matches zero or more characters while '?' matches exactly one + * character. Wildcards can be escaped by preceding them with a '\'. To match + * a '\' character, escape it (i.e., "\\"). + * + * Using '\' as an escape character makes this function unsuitable to match + * full pathnames on Win32 (or DOS) platforms. Matching the last part (i.e., + * after the last '\' (or '/', depending on platform)) works fine though. + * + * This function is case sensitive on some platforms. + * + * \param pattern the pattern strings will be matched against. + * \param text string to match against pattern. + * \return 1 if the text matches the pattern and 0 otherwise. + */ +static int match(const char* pattern, const char* text) +{ + const char* last_pattern = NULL; + const char* last_text = NULL; + + while (*text) { + switch (*pattern) { + case '?': + /* Just accept any difference */ + ++pattern; + ++text; + break; + + case '*': + /* Search for the text following the '*' */ + last_pattern = ++pattern; + last_text = text; + break; + + case '\\': + /* Accept a terminating \ as a literal \ */ + if (pattern[1]) + ++pattern; + /* Fall through */ + + default: + if (!equal(*pattern++, *text++)) { + if (last_pattern != NULL) { + /* Accept difference and repeat search for the text + * following the '*' + */ + pattern = last_pattern; + text = ++last_text; + } + else + return 0; + } + break; + } + } + + /* Only a match if pattern is exhausted (we know text is) */ + return (*pattern) ? 0 : 1; +} + + +#ifdef _WIN32 +/** + * \brief Display a file (or other I/O) Win32 error message. + * + * Display a file (or other I/O) error message that was caused by a + * Win32-specific function call. First a message is formatted + * and printed, followed by the error text, terminated by a line feed. + * + * \param message message format to display. + * \param ... printf-arguments used to format the message. + */ +static void file_error_win(const char *message, ...) +{ + char *error; + va_list args; + + va_start(args, message); + vfprintf(stderr, message, args); + va_end(args); + + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &error, 0, + NULL); + + fprintf(stderr, error); + fprintf(stderr, "\n"); + LocalFree(error); +} + + +/** + * Open the current directory for scanning. + * + * \param full_path the directory name to dislpay in case of errors. + * \return directory structure, where the name field contains the name of + * the first entry in the directory. If an error occured, NULL is + * returned (and a message has been printed). + */ +static DIRECTORY *open_dir(const char *full_path) +{ + DIRECTORY *result; + + result = (DIRECTORY *) calloc(1, sizeof(DIRECTORY)); + + if (result != NULL) { + result->find_handle = FindFirstFile("*", &result->find_data); + + if (result->find_handle != INVALID_HANDLE_VALUE) { + result->full_path = full_path; + result->name = result->find_data.cFileName; + return result; + } + + /* Could get "no more files" here, but not likely, due to "." and ".." */ + + free(result); + file_error_win(_("Couldn't scan directory '%s': "), full_path); + return NULL; + } + + fprintf(stderr, "Out of memory\n"); + return NULL; +} + + +/** + * Get the next file or folder in the directory. + * + * \param directory pointer returned by open_dir. If the call is successful, + * the name field is updated with the new name. + * \return 0 for success and -1 for failure (in which case a message has been + * printed). + */ +static int read_dir(DIRECTORY *directory) +{ + if (FindNextFile(directory->find_handle, &directory->find_data)) { + /* Probably not needed, but... */ + directory->name = directory->find_data.cFileName; + return 0; + } + else { + if (GetLastError() != ERROR_NO_MORE_FILES) { + file_error_win(_("Couldn't scan directory '%s': "), directory->full_path); + directory->read_error = 1; + } + } + + return -1; +} + + +/** + * \brief Close the scanning of a directory. + * + * Close the scanning of a directory. No more calls to read_dir() can be made + * after this call. + * + * \param directory directory to stop scanning. + * \return 0 for success and -1 for failure (in which case a message has been + * printed). + */ +static int close_dir(DIRECTORY *directory) +{ + int result = -1; + + if (directory != NULL) { + if (FindClose(directory->find_handle) == 0) { + /* What could cause this? Do we need to take some action here? */ + file_error_win(_("Couldn't close directory '%s': "), directory->full_path); + } + else + result = 0; + free(directory); + } + + return result; +} + +#else /* WIN32 */ + +/** + * Open the current directory for scanning. + * + * \param full_path the directory name to dislpay in case of errors. + * \return directory structure, where the name field contains the name of + * the first entry in the directory. If an error occured, NULL is + * returned (and a message has been printed). + */ +static DIRECTORY *open_dir(const char *full_path) +{ + DIRECTORY *result; + + result = (DIRECTORY *) calloc(1, sizeof(DIRECTORY)); + + if (result != NULL) { + result->full_path = full_path; + result->dir = opendir("."); + + if (result->dir != NULL) { + result->entry = readdir(result->dir); + + if (result->entry != NULL) { + result->name = result->entry->d_name; + return result; + } + } + + /* Could get "no more files" here, but not likely, due to "." and ".." */ + + free(result); + file_error(_("Couldn't scan directory '%s': "), result->full_path); + return NULL; + } + + fprintf(stderr, _("Out of memory\n")); + return NULL; +} + + +/** + * Get the next file or folder in the directory. + * + * \param directory pointer returned by open_dir. If the call is successful, + * the name field is updated with the new name. + * \return 0 for success and -1 for failure (in which case a message has been + * printed). + */ +static int read_dir(DIRECTORY *directory) +{ + directory->entry = readdir(directory->dir); + + if (directory->entry != NULL) { + directory->name = directory->entry->d_name; + return 0; + } + + /* I think this is the right way to check for errors... */ + directory->read_error = (errno != 0); + + if (directory->read_error) + file_error(_("Couldn't scan directory '%s': "), directory->full_path); + + return -1; +} + + +/** + * \brief Close the scanning of a directory. + * + * Close the scanning of a directory. No more calls to read_dir() can be made + * after this call. + * + * \param directory directory to stop scanning. + * \return 0 for success and -1 for failure (in which case a message has been + * printed). + */ +static int close_dir(DIRECTORY *directory) +{ + int result = -1; + + if (directory != NULL) { + if (closedir(directory->dir) != 0) + file_error(_("Couldn't close directory '%s': "), directory->full_path); + else + result = 0; + + free(directory); + } + + return result; +} +#endif /* WIN32 */ + + +/** + * \brief Process all files in a directory. + * + * Process all files in a directory. If settings->album is set, assume the + * files make up one album. If settings->recursive is set, process all + * subdirectories as well. + * + * \param current "display name" (i.e., not neccessarily the full name) of + * the current path, used to display the name of the + * directory being processed. path will be appended to + * current and passed on recursively, if needed. + * \param path name of folder to process. + * \param settings settings and global variables. + * \return 0 if successful and -1 if an error occured (in which case a + * message has been printed). + */ +static int process_directory(const char* current, const char* path, SETTINGS* settings) +{ + char* full_path; + char* old_path; + int result = -1; + + old_path = getcwd(NULL, 1024); +// old_path = _getcwd(NULL, 1024); + + if (old_path == NULL) { + file_error(_("Couldn't get name of current directory: ")); + return result; + } + + full_path = malloc(strlen(current) + strlen(path) + 2); + + if (full_path == NULL) { + free(old_path); + fprintf(stderr, _("Out of memory")); + return result; + } + + strcpy(full_path, current); + + if (strlen(full_path) > 0) + strcat(full_path, PATH_SEPARATOR); + + strcat(full_path, path); + + if (chdir(path) == 0) { +// if (_chdir(path) == 0) { + DIRECTORY* directory; + FILE_LIST* file_list = NULL; + + directory = open_dir("."); + + if (directory != NULL) { + result = 0; + + do { + int dir; + + /* Skip "special" directories */ + if (!strcmp(directory->name, ".") || !strcmp(directory->name, "..")) + continue; + + dir = is_dir(directory->name); + + if (dir > 0) { + if (settings->recursive) + result = process_directory(full_path, directory->name, settings); + } + else if (!dir) { + if(match(settings->pattern, directory->name) && (add_to_list(&file_list, directory->name) < 0)) + result = -1; + } + else { + file_error(_("Couldn't find '%s': "), path); + result = -1; + } + } while ((result == 0) && (read_dir(directory) == 0)); + + if (directory->read_error) + result = -1; + + close_dir(directory); + } + + if ((result == 0) && (file_list != NULL)) { + fprintf(stderr, _("\nProcessing directory '%s':\n"), full_path); + + result = process_files(file_list, settings, full_path); + } + + free_list(file_list); + + if ((result == 0) && (chdir(old_path) != 0)) { +// if ((result == 0) && (_chdir(old_path) != 0)) { + file_error(_("Couldn't go back to folder '%s': "), old_path); + result = 0; + } + } + else + file_error(_("Couldn't go to folder '%s': "), full_path); + + free(old_path); + free(full_path); + return result; +} + + +/** + * \brief Process an argument. + * + * Process an argument. Check for wildcard at end of path, then process the + * file or folder specified. + * + * \param path path argument to process. + * \param settings settings and global variables. + * \return 0 if successful and -1 if an error occured (in which case a + * message has been printed). + */ +int process_argument(const char* path, SETTINGS* settings) +{ + char* buffer = strdup(path); +// char* buffer = _strdup(path); + char* my_path; + int my_path_len; + int dir; + int result = -1; + + if (buffer == NULL) { + fprintf(stderr, _("Out of memory\n")); + return result; + } + + my_path = buffer; + /* Check for wildcards */ + settings->pattern = last_path(my_path); + + if (contains_pattern(settings->pattern)) { + /* Strip last part of path */ + if (settings->pattern > my_path) { + /* Not using [-1] to avoid compiler warning */ + settings->pattern--; + *settings->pattern++ = '\0'; + } + else + my_path = ""; + } + else + settings->pattern = NULL; + + my_path_len = strlen(my_path); + + if (my_path_len == 0) + my_path = "."; + else if (my_path[my_path_len - 1] == PATH_SEPARATOR_CHAR) + /* On Win32, "path" and "path\." are okay, but not "path\"... */ + my_path[my_path_len - 1] = '\0'; + + dir = is_dir(my_path); + + if (dir > 0) { + /* Finish off any files before processing folder */ + if (process_files(settings->file_list, settings, ".") == 0) { + if (settings->pattern == NULL) + settings->pattern = "*"; + + result = process_directory("", my_path, settings); + } + + free_list(settings->file_list); + settings->file_list = NULL; + } + else if (dir == 0) { + if (settings->pattern) + /* A pattern was specified, but a file was found as "folder part" */ + fprintf(stderr, _("'%s' is a file, not a folder\n"), my_path); + else + result = add_to_list(&settings->file_list, my_path); + } + else + file_error(_("Couldn't find '%s': "), my_path); + + free(buffer); + return result; +} + +#endif /* ENABLE_RECURSIVE */ diff --git a/recurse.h b/recurse.h new file mode 100644 index 0000000..3eef81c --- /dev/null +++ b/recurse.h @@ -0,0 +1,12 @@ +#ifndef RECURSE_H +#define RECURSE_H + +#ifdef ENABLE_RECURSIVE + +#include "main.h" + +extern int process_argument(const char* path, SETTINGS* settings); + +#endif /* ENABLE_RECURSIVE */ + +#endif /* RECURSE_H */ diff --git a/wavegain.c b/wavegain.c new file mode 100644 index 0000000..0bf2e19 --- /dev/null +++ b/wavegain.c @@ -0,0 +1,605 @@ +/* + * function: ReplayGain support for Wave files (http://www.replaygain.org) + * + * Reads in a Vorbis file, figures out the peak and ReplayGain levels and + * applies the Gain tags to the Wave file + * + * This program is distributed under the GNU General Public License, version + * 2.1. A copy of this license is included with this source. + * + * Copyright (C) 2002-2004 John Edwards + * Additional code by Magnus Holmgren and Gian-Carlo Pascutto + * Linux patch by Marc Brooker + */ + +#include "config.h" +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#else +# ifndef __MACOSX__ +# include +# endif +#endif + +#include + +#ifndef __MACOSX__ +#include +#endif + +#include "gain_analysis.h" +#include "i18n.h" +#include "config.h" +#include "getopt.h" +#include "misc.h" +#include "audio.h" +#include "dither.h" +#include "main.h" +#include "wavegain.h" + +#ifdef _WIN32 +#include +#endif + +#ifdef ENABLE_RECURSIVE +#include "recurse.h" +#endif + +/*Gcc uses LL as a suffix for long long int (64 bit) types - Marc Brooker 8/4/2004*/ +#ifdef __GNUC__ +#define ROUND64(x) ( doubletmp = (x) + Dither.Add + (Int64_t)0x001FFFFD80000000LL, *(Int64_t*)(&doubletmp) - (Int64_t)0x433FFFFD80000000LL ) +#else +#define ROUND64(x) ( doubletmp = (x) + Dither.Add + (Int64_t)0x001FFFFD80000000L, *(Int64_t*)(&doubletmp) - (Int64_t)0x433FFFFD80000000L ) +#endif + +extern int write_log; +dither_t Dither; +double doubletmp; +double total_samples; +double total_files; + +/* Replaced with a double based function for consistency 2005-11-17 +static float FABS(float x) +{ + unsigned int *ix=(unsigned int *)&x; + *ix&=0x7fffffffUL; + return(x); +} +*/ +static double DABS(double x) +{ + Uint64_t *ix=(Uint64_t *)&x; +#ifdef __GNUC__ + *ix&=0x7fffffffffffffffULL; +#else + *ix&=0x7fffffffffffffff; +#endif + return(x); +} + +/* Dither output */ +Int64_t dither_output(int dithering, int shapingtype, int i, double Sum, int k, int format) +{ + double Sum2; + Int64_t val; + if(dithering) { + if(!shapingtype) { + double tmp = Random_Equi ( Dither.Dither ); + Sum2 = tmp - Dither.LastRandomNumber [k]; + Dither.LastRandomNumber [k] = (int)tmp; + Sum2 = Sum += Sum2; + val = ROUND64 (Sum2) & Dither.Mask; + } + else { + Sum2 = Random_Triangular ( Dither.Dither ) - scalar16 ( Dither.DitherHistory[k], Dither.FilterCoeff + i ); + Sum += Dither.DitherHistory [k] [(-1-i)&15] = (float)Sum2; + Sum2 = Sum + scalar16 ( Dither.ErrorHistory [k], Dither.FilterCoeff + i ); + val = ROUND64 (Sum2) & Dither.Mask; + Dither.ErrorHistory [k] [(-1-i)&15] = (float)(Sum - val); + } + } + else + val = (Int64_t)(ROUND64 (Sum)); + + if (format == WAV_FMT_8BIT) + val = val >> 24; + else if (format == WAV_FMT_16BIT || format == WAV_FMT_AIFF) + val = val >> 16; + else if (format == WAV_FMT_24BIT) + val = val >> 8; + + return (val); +} + +/* Get the gain and peak value for a file. Runs in audiophile mode if + * audiophile is true. + * + * If an error occured, 0 is returned (a message has been printed). + */ + +int get_gain(const char *filename, float *track_peak, float *track_gain, + double *dc_offset, double *offset, SETTINGS *settings) +{ + wavegain_opt *wg_opts = malloc(sizeof(wavegain_opt)); + FILE *infile; + int result = 0; + float new_peak, + factor_clip; + double scale, + peak = 0., + dB; + int k, i; + long chunk; + input_format *format; + + if(!strcmp(filename, "-")) { + infile = stdin; + settings->apply_gain = 0; +#ifdef _WIN32 + _setmode( _fileno(stdin), _O_BINARY ); +#endif + } + else + infile = fopen(filename, "rb"); + + if (infile == NULL) { + fprintf (stderr, " Not able to open input file %s.\n", filename) ; + goto exit; + } + wg_opts->apply_gain = 0; + + /* + * Now, we need to select an input audio format + */ + + format = open_audio_file(infile, wg_opts); + if (!format) { + /* error reported by reader */ + fprintf (stderr, " Unrecognized file format for %s.\n", filename) ; + goto exit; + } + + if ((wg_opts->channels != 1) && (wg_opts->channels != 2)) { + fprintf(stderr, " Unsupported number of channels.\n"); + goto exit; + } + + /* Only initialize gain analysis once in audiophile mode */ + if (settings->first_file || !settings->audiophile) { + if (InitGainAnalysis(wg_opts->rate) != INIT_GAIN_ANALYSIS_OK) { + fprintf(stderr, " Error Initializing Gain Analysis (nonstandard samplerate?)\n"); + goto exit; + } + } + + if (settings->first_file) { + total_samples = (double)wg_opts->total_samples_per_channel; + fprintf(stderr, "\n Analyzing...\n\n"); + fprintf(stderr, " Gain | Peak | Scale | New Peak |Left DC|Right DC| Track\n"); + fprintf(stderr, " | | | |Offset | Offset | Track\n"); + fprintf(stderr, " --------------------------------------------------------------\n"); + if(write_log) { + log_error("\n Analyzing...\n\n"); + log_error(" Gain | Peak | Scale | New Peak |Left DC|Right DC| Track\n"); + log_error(" | | | |Offset | Offset | Track\n"); + log_error(" --------------------------------------------------------------\n"); + } + settings->first_file = 0; + } + else + total_samples += (double)wg_opts->total_samples_per_channel; + + if (settings->fast && (wg_opts->total_samples_per_channel * (wg_opts->samplesize / 8) + * wg_opts->channels > 8192000)) { + + long samples_read; + double **buffer = malloc(sizeof(double *) * wg_opts->channels); + + for (i = 0; i < wg_opts->channels; i++) + buffer[i] = malloc(BUFFER_LEN * sizeof(double)); + + chunk = ((wg_opts->total_samples_per_channel * (wg_opts->samplesize / 8) * wg_opts->channels) + 44) / 1200; + + for(k = 100; k < 1100; k+=5) { + + samples_read = wg_opts->read_samples(wg_opts->readdata, buffer, BUFFER_LEN, + settings->fast, chunk * k); + if (samples_read == 0) { + break; + } + else { + if (samples_read < 0) { + /* Error in the stream. Not a problem, just reporting it in case + * we (the app) cares. In this case, we don't + */ + } + else { + int i; + int j; + + for (i = 0; i < wg_opts->channels; i++) { + for (j = 0; j < samples_read; j++) { + buffer[i][j] *= 0x7fff; + if (DABS(buffer[i][j]) > peak) + peak = DABS(buffer[i][j]); + } + } + + if (AnalyzeSamples(buffer[0], buffer[1], samples_read, + wg_opts->channels) != GAIN_ANALYSIS_OK) { + fprintf(stderr, " Error processing samples.\n"); + goto exit; + } + } + } + } + for (i = 0; i < wg_opts->channels; i++) + if (buffer[i]) free(buffer[i]); + if (buffer) free(buffer); + } + else + { + long samples_read; + double **buffer = malloc(sizeof(double *) * wg_opts->channels); + + for (i = 0; i < wg_opts->channels; i++) + buffer[i] = malloc(BUFFER_LEN * sizeof(double)); + + while (1) { + + samples_read = wg_opts->read_samples(wg_opts->readdata, buffer, BUFFER_LEN, 0, 0); + + if (samples_read == 0) { + break; + } + else { + if (samples_read < 0) { + /* Error in the stream. Not a problem, just reporting it in case + * we (the app) cares. In this case, we don't + */ + } + else { + int i; + int j; + + for (i = 0; i < wg_opts->channels; i++) { + for (j = 0; j < samples_read; j++) { + offset[i] += buffer[i][j]; + buffer[i][j] *= 0x7fff; + if (DABS(buffer[i][j]) > peak) + peak = DABS(buffer[i][j]); + } + } + + if (AnalyzeSamples(buffer[0], buffer[1], samples_read, + wg_opts->channels) != GAIN_ANALYSIS_OK) { + fprintf(stderr, " Error processing samples.\n"); + goto exit; + } + } + } + } + + for (i = 0; i < wg_opts->channels; i++) { + if (buffer[i]) free(buffer[i]); + dc_offset[i] = (double)(offset[i] / wg_opts->total_samples_per_channel); + } + if (buffer) free(buffer); + } + /* + * calculate factors for ReplayGain and ClippingPrevention + */ + *track_gain = (float)(GetTitleGain() + settings->man_gain); + scale = (float)(pow(10., *track_gain * 0.05)); + if(settings->clip_prev) { + factor_clip = (float) (32767./( peak + 1)); + if(scale < factor_clip) + factor_clip = 1.f; + else + factor_clip /= (float)scale; + scale *= factor_clip; + } + new_peak = (float) (peak * scale); + + dB = 20. * log10(scale); + *track_gain = (float) dB; + { + int dc_l; + int dc_r; + if (settings->no_offset) { + dc_l = 0; + dc_r = 0; + } + else { + dc_l = (int)(dc_offset[0] * 32768 * -1); + dc_r = (int)(dc_offset[1] * 32768 * -1); + } + fprintf(stderr, " %+6.2f dB | %6.0f | %5.2f | %8.0f | %4d | %4d | %s\n", + *track_gain, peak, scale, new_peak, dc_l, dc_r, filename); + if(write_log) { + log_error(" %+6.2f dB | %6.0f | %5.2f | %8.0f | %4d | %4d | %s\n", + *track_gain, peak, scale, new_peak, dc_l, dc_r, filename); + } + } + if (settings->scale && !settings->audiophile) + fprintf(stdout, "%8.6lf", scale); + + settings->album_peak = settings->album_peak < peak ? (float)peak : settings->album_peak; + *track_peak = new_peak; + result = 1; + +exit: + if (result) + format->close_func(wg_opts->readdata); + if (wg_opts) + free(wg_opts); + if (infile) + fclose(infile); + return result; +} + + +/* Use the ReplayGain calculations to adjust the gain on the wave file. + * If audiophile_gain is selected, that value is used, otherwise the + * radio_gain value is used. + */ +int write_gains(const char *filename, float radio_gain, float audiophile_gain, float TitlePeak, + double *dc_offset, double *album_dc_offset, SETTINGS *settings) +{ + wavegain_opt *wg_opts = malloc(sizeof(wavegain_opt)); + FILE *infile; + audio_file *aufile; + int readcount, + result = 0, + delete_temp = 0, + i; + float Gain; + double scale; + double total_read = 0.; + double wrap_prev_pos; + double wrap_prev_neg; + void *sample_buffer; + input_format *format; + + infile = fopen(filename, "rb"); + + if (infile == NULL) { + fprintf (stderr, " Not able to open input file %s.\n", filename) ; + goto exit; + } + wg_opts->apply_gain = 1; + + /* + * Now, we need to select an input audio format + */ + + format = open_audio_file(infile, wg_opts); + if (!format) { + format->close_func(wg_opts->readdata); + fclose(infile); + /* error reported by reader */ + fprintf (stderr, " Unrecognized file format for %s.\n", filename) ; + } + else { + double **pcm = malloc(sizeof(double *) * wg_opts->channels); + + for (i = 0; i < wg_opts->channels; i++) + pcm[i] = malloc(BUFFER_LEN * sizeof(double)); + + switch(settings->format) { + case WAV_NO_FMT: + if (wg_opts->format == WAV_FMT_AIFF || wg_opts->format == WAV_FMT_AIFC8 + || wg_opts->format == WAV_FMT_AIFC16) { + wg_opts->format = settings->format = WAV_FMT_AIFF; + wg_opts->endianness = BIG; + wrap_prev_pos = 0x7fff; + wrap_prev_neg = -0x8000; + } + else if (wg_opts->samplesize == 8) { + wg_opts->format = settings->format = WAV_FMT_8BIT; + wrap_prev_pos = 0x7f; + wrap_prev_neg = -0x80; + } + else if (wg_opts->samplesize == 16) { + wg_opts->format = settings->format = WAV_FMT_16BIT; + wg_opts->endianness = LITTLE; + wrap_prev_pos = 0x7fff; + wrap_prev_neg = -0x8000; + } + else if (wg_opts->samplesize == 24) { + wg_opts->format = settings->format = WAV_FMT_24BIT; + wg_opts->endianness = LITTLE; + wrap_prev_pos = 0x7fffff; + wrap_prev_neg = -0x800000; + } + else if (wg_opts->samplesize == 32) { + wg_opts->format = settings->format = WAV_FMT_32BIT; + wg_opts->endianness = LITTLE; + wrap_prev_pos = 0x7fffffff; + wrap_prev_neg = -0x7fffffff; + } + else if (wg_opts->format == WAV_FMT_FLOAT) { + wg_opts->format = settings->format = WAV_FMT_FLOAT; + wg_opts->endianness = LITTLE; + wrap_prev_pos = 1 - (1 / 0x80000000); + wrap_prev_neg = -1.; + } + break; + case WAV_FMT_8BIT: + wg_opts->format = WAV_FMT_8BIT; + wg_opts->samplesize = 8; + wrap_prev_pos = 0x7f; + wrap_prev_neg = -0x80; + break; + case WAV_FMT_AIFF: + wg_opts->format = WAV_FMT_AIFF; + wg_opts->samplesize = 16; + wg_opts->endianness = BIG; + wrap_prev_pos = 0x7fff; + wrap_prev_neg = -0x8000; + break; + case WAV_FMT_16BIT: + wg_opts->format = WAV_FMT_16BIT; + wg_opts->samplesize = 16; + wg_opts->endianness = LITTLE; + wrap_prev_pos = 0x7fff; + wrap_prev_neg = -0x8000; + break; + case WAV_FMT_24BIT: + wg_opts->format = WAV_FMT_24BIT; + wg_opts->samplesize = 24; + wg_opts->endianness = LITTLE; + wrap_prev_pos = 0x7fffff; + wrap_prev_neg = -0x800000; + break; + case WAV_FMT_32BIT: + wg_opts->format = WAV_FMT_32BIT; + wg_opts->samplesize = 32; + wg_opts->endianness = LITTLE; + wrap_prev_pos = 0x7fffffff; + wrap_prev_neg = -0x7fffffff; + break; + case WAV_FMT_FLOAT: + wg_opts->format = WAV_FMT_FLOAT; + wg_opts->endianness = LITTLE; + wrap_prev_pos = 1 - (1 / 0x80000000); + wrap_prev_neg = -1.; + break; + } + + wg_opts->std_out = settings->std_out; + + aufile = open_output_audio_file(TEMP_NAME, wg_opts); + + if (aufile == NULL) { + fprintf (stderr, " Not able to open output file %s.\n", TEMP_NAME); + fclose(infile); + goto exit; + } + + Init_Dither (wg_opts->samplesize, settings->shapingtype); + + if(settings->audiophile) + Gain = audiophile_gain; + else + Gain = radio_gain; + + scale = pow(10., Gain * 0.05); + + fprintf(stderr, " \r"); + fprintf(stderr, " Applying Gain of %+5.2f dB to file: %s\n", Gain, filename); + if (write_log) { + log_error(" Applying Gain of %+5.2f dB to file: %s\n", Gain, filename); + } + + while (1) { + + readcount = wg_opts->read_samples(wg_opts->readdata, pcm, BUFFER_LEN, 0, 0); + + total_read += ((double)readcount / wg_opts->rate); + total_files += ((double)readcount / wg_opts->rate); + if( (long)total_files % 4 == 0) { + fprintf(stderr, "This file %3.0lf%% done\tAll files %3.0lf%% done\r", + total_read / (wg_opts->total_samples_per_channel / wg_opts->rate) * 100, + total_files / (total_samples / wg_opts->rate) * 100); + } + + if (readcount == 0) { + break; + } + else if (readcount < 0) { + /* Error in the stream. Not a problem, just reporting it in case + * we (the app) cares. In this case, we don't + */ + } + else { + int convsize = BUFFER_LEN; + int j, + i = 0, + k; + int bout = (readcount < convsize ? readcount : convsize); + + /* scale doubles to 8, 16, 24 or 32 bit signed ints + * (host order) (unless float output) + * and apply ReplayGain scaling, etc. + */ + sample_buffer = malloc(sizeof(double) * wg_opts->channels * bout); + for(k = 0; k < wg_opts->channels; k++) { + for(j = 0; j < bout; j++, i++) { + Int64_t val; + double Sum; + + if (!settings->no_offset) { + if (settings->adc) + pcm[k][j] -= album_dc_offset[k]; + else + pcm[k][j] -= dc_offset[k]; + } + + pcm[k][j] *= scale; + if (settings->limiter) { /* hard 6dB limiting */ + if (pcm[k][j] < -0.5) + pcm[k][j] = tanh((pcm[k][j] + 0.5) / (1-0.5)) * (1-0.5) - 0.5; + else if (pcm[k][j] > 0.5) + pcm[k][j] = tanh((pcm[k][j] - 0.5) / (1-0.5)) * (1-0.5) + 0.5; + } + if (settings->format != WAV_FMT_FLOAT) { + Sum = pcm[k][j]*2147483647.f; + if (i > 31) + i = 0; + val = dither_output(settings->dithering, settings->shapingtype, i, + Sum, k, settings->format); + if (val > (Int64_t)wrap_prev_pos) + val = (Int64_t)wrap_prev_pos; + else if (val < (Int64_t)wrap_prev_neg) + val = (Int64_t)wrap_prev_neg; + pcm[k][j] = (double)val; + } + else { + if (pcm[k][j] > wrap_prev_pos) + pcm[k][j] = wrap_prev_pos; + else if (pcm[k][j] < wrap_prev_neg) + pcm[k][j] = wrap_prev_neg; + } + } + } + sample_buffer = output_to_PCM(pcm, sample_buffer, wg_opts->channels, + bout, settings->format); + /* write to file */ + write_audio_file(aufile, sample_buffer, bout * wg_opts->channels); + + free(sample_buffer); + } + } + for (i = 0; i < wg_opts->channels; i++) + if (pcm[i]) free(pcm[i]); + if (pcm) free(pcm); + format->close_func(wg_opts->readdata); + close_audio_file(infile, aufile, wg_opts); + fclose(infile); + + if (!settings->std_out) { + if (remove(filename) != 0) { + fprintf(stderr, " Error deleting old file '%s'\n", filename); + goto exit; + } + + if (rename(TEMP_NAME, filename) != 0) { + fprintf(stderr, " Error renaming '" TEMP_NAME "' to '%s' (uh-oh)\n", filename); + goto exit; + } + } + + result = 1; + } +exit: + return result; +} + + diff --git a/wavegain.h b/wavegain.h new file mode 100644 index 0000000..53ac0a8 --- /dev/null +++ b/wavegain.h @@ -0,0 +1,15 @@ +#ifndef WAVEGAIN_H +#define WAVEGAIN_H + +#include "main.h" + +#define NO_PEAK -1.f +#define NO_GAIN -10000.f + +extern int get_gain(const char *filename, float *track_peak, float *track_gain, double *dc_offset, double *offset, + SETTINGS *settings); +extern int write_gains(const char *filename, float radio_gain, float audiophile_gain, float TitlePeak, + double *dc_offset, double *album_dc_offset, SETTINGS *settings); + +#endif /* WAVEGAIN_H */ +