commit cb979e10a703033864f8f42c94e9d1d335e5be40 Author: Erik de Castro Lopo Date: Mon May 14 19:55:24 2007 +1000 First snapshot of the public project. diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 00000000..95770ab5 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,14 @@ +The main author of libsndfile is Erik de Castro Lopo . + +The code in the src/GSM610 directory was written by Jutta Degener + and Carsten Bormann . +They should not be contacted in relation to libsndfile or the GSM 6.10 code +that is part of libsndfile. Their original code can be found at: + + http://kbs.cs.tu-berlin.de/~jutta/toast.html + +Code in the src/G72x directory was released by Sun Microsystems, Inc. to the +public domain. Minor modifications were required to integrate these files +into libsndfile. The changes are listed in src/G72x/ChangeLog. + + diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..c396169e --- /dev/null +++ b/COPYING @@ -0,0 +1,503 @@ + 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/ChangeLog b/ChangeLog new file mode 100644 index 00000000..d61f2ddf --- /dev/null +++ b/ChangeLog @@ -0,0 +1,6641 @@ +2007-05-12 Erik de Castro Lopo + + * src/FLAC/src/share/getopt/getopt.c + Fix Mac OSX / PowerPC compile warnings. + + * configure.ac + Make sure WORDS_BIGENDIAN gets correctly defined for FLAC code. + +2007-05-04 Erik de Castro Lopo + + * doc/FAQ.html + Add Q/A about MP3 support. + +2007-05-03 Erik de Castro Lopo + + * doc/new_file_type.HOWTO + Minor updates. + +2007-05-02 Erik de Castro Lopo + + * src/wve.c + Fix a couple bad parameters with psf_log_printf. + + * src/pcm.c + Improve error reporting. + + * src/common.h src/common.c + Constify psf_hexdump. + +2007-04-30 Erik de Castro Lopo + + * src/FLAC + Ditch and re-import required FLAC code. + + * configure.ac + Force FLAC__HAS_OGG variable to 1. + + * src/FLAC/src/libFLAC/stream_encoder.c + Fix compiler warnings. + +2007-04-23 Erik de Castro Lopo + + * configure.ac tests/win32_ordinal_test.c + Detect if win32 DLL is beging generated and only run win32_ordinal_test if + true. + + * src/G72x/Makefile.am src/Makefile.am + Use $(EXEEXT) where possible. + +2007-04-18 Erik de Castro Lopo + + * src/wve.c src/common.h src/sndfile.c + Complete definition of SfE_WVE_NO_WVE error message. + + * src/wve.c + Fix error in files generated on big endian systems. Robustify parsing. + +2007-04-16 Erik de Castro Lopo + + * src/double64.c + Fix clipping of double to short conversions on 64 bit systems. + + * src/flac.c regtest/database.c tests/cpp_test.cc + Fix compile warnings for 64 bit systems. + +2007-04-15 Erik de Castro Lopo + + * src/wav.c src/wav_w64.c + Use audio detect function when 'fmt ' chunk data is suspicious. + + * configure.ac + Add ugly hack to remove -Werror from some Makefiles. + +2007-04-14 Erik de Castro Lopo + + * examples/sndfile-data-trim.c + New file. Program incomplete. + + * src/GSM610/long_term.c src/macbinary3.c tests/cpp_test.cc + Add patch from André Pang to clean up compiles on OSX. + + * src/wve.c src/common.h src/sndfile.c src/sndfile.h.in + examples/sndfile-convert.c + Merge changes from Reuben Thomas to improve WVE support. + + * tests/lossy_comp_test.c tests/Makefile.am + Add tests for WVE files. + +2007-04-11 Erik de Castro Lopo + + * src/sndfile.hh + Add a static SndfileHandle::formatCheck method as suggested by Jorge + Jiménez. + +2007-04-09 Erik de Castro Lopo + + * src/sndfile.c + Fixed a bug in sf_error() where the function itself was being compared + against zero. Add a check for a NULL return from peak_info_calloc. Fix a + possible NULL dereference. + +2007-04-07 Erik de Castro Lopo + + * src/flac.c + Turn off seekable flag when writing, return SFE_BAD_RDWR_FORMAT when + opening file for RDWR. + + * src/sndfile.c + Improve error message for SFE_BAD_RDWR_FORMAT. + + * src/mat4.c + Fix array indexing issue. Thanks to Ben Allison (Nullsoft) for alerting me. + +2007-03-05 Erik de Castro Lopo + + * doc/FAQ.html + Add Q/A 19 on project files. + +2007-03-01 Erik de Castro Lopo + + * src/sndfile.c + Guard agains MacOSX universal binary compiles. + + * doc/FAQ.html + Add Q/A 18 and clean up Q3. + +2007-02-22 Erik de Castro Lopo + + * src/aiff.c + Add support for 'in24' files. + +2007-02-13 Erik de Castro Lopo + + * src/wav.c src/wav_w64.c src/wav_w64.h + Start work towards detecting ausio codec type from the actual audio data. + + * src/audio_detect.c src/test_audio_detect.c + Add new file and its unit test. + +2007-02-07 Erik de Castro Lopo + + * examples/cooledit-fixer.c examples/Makefile.am + Remove old broken example program. + +2007-02-06 Erik de Castro Lopo + + * src/sndfile.c src/sndfile.h.in src/create_symbols_file.py + Add function sf_get_info. + +2007-01-25 Erik de Castro Lopo + + * examples/sndfile-play.c + For ALSA, use the 'default' device instead of 'plughw:0'. + +2007-01-22 Erik de Castro Lopo + + * src/sndfile.c + Allow writing of WAV/WAVEX 'BEXT' chunks in SFM_RDWR mode. + +2007-01-21 Erik de Castro Lopo + + * doc/development.html doc/embedded_files.html man/sndfile-play.1 + Minor documentation fixes. Thanks Reuben Thomas. + +2006-12-16 Erik de Castro Lopo + + * examples/sndfile-convert.c + Add -override-sample-rate command line option. + +2006-11-19 Erik de Castro Lopo + + * tests/misc_test.c + Force errno to zero at start of some tests. + + * src/sndfile.c + Minor clean up of error handling. + + * configure.ac + Remove an assembler test which was failing on OSX. + +2006-11-15 Erik de Castro Lopo + + * src/common.h + Fix the definition of SF_PLATFORM_S64 for MinGW. + + * src/FLAC/Makefile.am src/FLAC/share/grabbag/Makefile.am + Fix path problems for MinGW. + +2006-11-13 Erik de Castro Lopo + + * src/sfendian.h + Add include guard. + + * src/Makefile.am src/flac.c + Clean up include paths. + + * src/test_conversions.c + New file to test psf_binheader_readf/writef functions. + + * src/Makefile.am src/test_file_io.c src/test_log_printf.c src/common.c + Clean up unit testing. + + * src/common.c + Fix a bug reading/writing 64 bit header fields. Thanks to Jonathan Woithe + for reporting this. + + * src/test_conversions.c + Complete unit test for above fix. + +2006-11-11 Erik de Castro Lopo + + * src/sndfile.c + More refactoring to clean up psf_open_file() and vairous sf_open() + functions. + +2006-11-09 Erik de Castro Lopo + + * src/wav.c + Apply a patch from Jonathan Woithe to allow opening of (malformed) WAV + files of over 4 gigabytes. + +2006-11-05 Erik de Castro Lopo + + * src/sndfile.c + Refactor function psf_open_file() to provide a single return point. + + * tests/misc_test.c + Fix permission_test to ensure that read only file can be created. + +2006-11-03 Erik de Castro Lopo + + * src/common.h + Add SF_PLATFORM_S64 macro as a platform independant way of doing signed 64 + bit integers. + + * src/aiff.c src/svx.c src/wav.c + Add warning in log if files are larger than 4 gigabytes in size. + +2006-11-01 Erik de Castro Lopo + + * src/FLAC src/OGG confgure.ac src/Makefile.am + Pull in all required FLAC and OGG code so external libraries are not + needed. This makes compiling on stupid fscking Windoze easier. + +2006-10-27 Erik de Castro Lopo + + * src/sd2.c + Add workaround for switched sample rate and sample size. + + * src/wav.c + Add workaround for excessively long coding history in the 'bext' chunk. + +2006-10-23 Erik de Castro Lopo + + * src/sndfile.h.in src/sndfile.c src/wav.c doc/command.html + Use SF_AMBISONIC_* instead of SF_TRUE/SF_FALSE. + +2006-10-22 Erik de Castro Lopo + + * src/sndfile.h.in src/wav.c src/wav_w64.c src/common.h doc/command.html + Apply a patch from Fons Adriaensen to allow writing on WAVEX Ambisonic + files. Still needs a little tweaking before its ready for release. + + * src/*.c + Use the UNUSED macro to prevent compiler warnings. + +2006-10-19 Erik de Castro Lopo + + * src/aiff.c + Fix a bug in parsing AIFF files with a slightly unusual 'basc' chunk. Thanks + to David Viens for providing two example files. + + * src/common.(c|h) src/aiff.c + Add a function psf_sanitize_string and use it in aiff.c. + +2006-10-18 Erik de Castro Lopo + + * src/wav_w64.c + Apply a patch from Fons Adriaensen which fixes a minor WAVEX GUID issue. + +2006-10-17 Erik de Castro Lopo + + * src/Makefile.am + Fix problem related to recent test coverage changes. + +2006-10-15 Erik de Castro Lopo + + * configure.ac tests/Makefile.am + Add --enable-test-coverage configure option. + +2006-10-05 Erik de Castro Lopo + + * src/sndfile.hh + Add an std::string SndfileHandle constructor. + + * tests/scale_clip_test.tpl + Fix the 'make distcheck' target. + +2006-10-03 Erik de Castro Lopo + + * src/double64.c src/float32.c + Add optional clipping on float file data to int read data conversions. + + * tests/tests/scale_clip_test.(def|tpl) + Add test for above new code. + +2006-09-06 Erik de Castro Lopo + + * tests/aiff_rw_test.c + Add 'MARK' chunks to make sure they are parsed correctly. + +2006-09-05 Erik de Castro Lopo + + * src/aiff.c + Fix parsing of MARK chunks. Many thanks to Sciss for generating files to + help debug the problem. + +2006-09-02 Erik de Castro Lopo + + * src/common.h + Make the SF_MIN and SF_MAX macros at least partially type safe. + + * tests/lossy_comp_test.c + Fix overflow problems when ensuring that signalis not zero. + +2006-08-31 Erik de Castro Lopo + + * configure.ac docs/*.html + Changes for release 1.0.17. + +2006-08-08 Erik de Castro Lopo + + * src/flac.c + Remove inline from functions called by pointer. Thanks to Sampo Savolainen + for notifying me of this. + +2006-07-31 Erik de Castro Lopo + + * src/sndfile.hh + Add writeSync method. + Add copy constructor and assignment operator (thanks Daniel Schmitt). + Add methods readRaw and writeRaw. + Make read/write/readf/writef simple overlaods instead of templates (thanks + to Trent Apted for suggesting this). + + * tests/cpp_test.cc + Cleanup. Add tests. + +2006-07-30 Erik de Castro Lopo + + * src/sndfile.hh + Templatize the read/write/readf/writef methods as suggested by Lars Luthman. + Prevent the potential leak of SNDFILE* pointers in the openRead/openWrite/ + openReadWrite methods. + Add const to SF_INFO pointer in Sndfile constructor. + Make the destrictor call the close() method. + + * tests/cpp_test.cc + Add more tests. + +2006-07-29 Erik de Castro Lopo + + * tests/cpp_test.cc + Remove the generated file so "make distcheck" passes. + + * src/Makefile.am + Add sndfile.hh to distributed header files. + + * src/sndfile.hh + Change the license for the C++ wrapper to modified BSD. + +2006-07-28 Erik de Castro Lopo + + * src/sndfile.hh + Complete it. + + * tests/cpp_test.cc + Add more tests. + +2006-07-27 Erik de Castro Lopo + + * tests/utils.tpl + Add extern C to generated header file. + + * src/sndfile.hh + Work towards completing this. + + * tests/cpp_test.cc tests/Makefile.am + Add a C++ test and hook into build. + + * configure.ac + Add appropriate CXXFLAGS. + +2006-07-26 Erik de Castro Lopo + + * configure.ac + Test if compiler supports -Wpointer-arith. + + * src/common.c + Fix a warning resulting from -Wpointer-arith. + +2006-07-15 Erik de Castro Lopo + + * examples/sndfile-play.c + Explicitly set endian-ness as well as setting 16 bit output. + + * examples/sndfile-info.c + Make sure to parse info if file fails to open. + + * src/sndfile.c + Handle parse error a little better. + + * src/wav_w64.[ch] + Minor clean up, add detection of IPP ITU G723.1. + +2006-06-23 Erik de Castro Lopo + + * src/sndfile.c + Make sure psf->dataoffset gets reset to zero when openning headersless + files based on the file name extension. + +2006-06-21 Erik de Castro Lopo + + * tests/(command|lossy_comp|pcm|scale_clip)_test.c tests/fix_this.c + tests/write_read_test.(tpl|def) + Fix gcc-4.1 compiler warnings about "dereferencing type-punned pointer will + break strict-aliasing rules". + + * examples/cooledit-fixer.c + More fixes like above. + +2006-06-20 Erik de Castro Lopo + + * src/file_io.c + Fix a windows bug where the syserr string of SF_PRIVATE was not being set + correctly. + + * src/sndfile.c + Fixed a logic bug in sf_seek(). Thanks to Paul Davis for finding this. + +2006-06-04 Erik de Castro Lopo + + * configure.ac + Fixed detection of S_IRGRP. + +2006-05-30 Erik de Castro Lopo + + * sndfile-convert.c + Add conversion SF_INSTRUMENT data when present. + +2006-05-22 Erik de Castro Lopo + + * doc/development.html + Removed references to tla on windows. + + * src/common.h src/sndfile.c + Add separate void pointers for file containter and file codec data to + SF_PRIVATE struct. Still need to move all existing fdata pointers. + + * tests/write_read_test.tpl + Change the order of some tests. + + * src/aiff.c + When writing 'AIFC' files, make sure get an 'FVER' gets added. + + * src/common.h src/(dwvw|flac|g72x|gsm610|ima_adpcm|ms_adpcm|paf|sds).c + src/(sndfile|voc|vox_adpcm|xi).c + Remove fdata field from SF_PRIVATE struct and replace it with codec_data. + +2006-05-10 Erik de Castro Lopo + + * Win32/testprog.c Win32/Makefile.am + Add a minimal win32 test program. + + * Win32/README-precompiled-dll.txt Mingw-make-dist.sh + Update readme and Mingw build script. + +2006-05-09 Erik de Castro Lopo + + * configure.ac acinclude.m4 + Minor fixes for Solaris. + +2006-05-05 Erik de Castro Lopo + + * src/test_endswap.(def|tpl) + Fix printf formatting for int64_t on 64 bit machines. + +2006-05-04 Erik de Castro Lopo + + * src/binhead_check.py + New file to check for bad parameters passed to psf_binheader_writef(). + + * src/Makefile.am + Hook into test suite. + + * src/voc.c src/caf.c src/wav.c src/mat5.c src/mat4.c + Fix bugs found by new test program. + + * src/double64.c + Clean up double64_get_capability(). + +2006-05-03 Erik de Castro Lopo + + * src/wav_w64.c + Fix a bug on x86_64 where an int was being passed via stdargs and being + read using size_t which is 64 bits. Thenks to John ffitch for giving me a + login on his box. + +2006-05-02 Erik de Castro Lopo + + * src/caf.c src/double64.c examples/sndfile-info.c tests/virtual_io_test.c + tests/utils.tpl + Fix a couple of signed/unsigned problems. + +2006-05-01 Erik de Castro Lopo + + * tests/command_test.c + Add channel map tests. + + * src/common.h src/sndfile.c + Add a pointer the the SF_PRIVATE struct and make sure it gets freed in + sf_close(). + +2006-04-30 Erik de Castro Lopo + + * configure.ac doc/(command|index|api).html NEWS README + Updates for 1.0.16 release. + + * src/sndfile.h.in + Define enums for channel mapping. + + * examples/sndfile-info.c + Clean up usage of SF_INFO struct. + +2006-04-29 Erik de Castro Lopo + + * tests/util.tpl + Add function testing function exit_if_true(). + + * tests/floating_point_test.tpl + Fix a problem where the test program was not exiting when the test failed. + +2006-04-15 Erik de Castro Lopo + + * src/sndfile.h.in src/sndfile.c src/common.h src/command.c + Implement new commands SFC_GET_SIGNAL_MAX and SFC_GET_MAX_ALL_CHANNELS. + + * doc/commands.html + Document new commands. Other minor updates. + + * tests/peak_chunk_test.c + Update tests for new commands. + +2006-04-02 Erik de Castro Lopo + + * tests/peak_chunk_test.c + Add test for RIFX and WAVEX files. + Try and confuse the PEAK chunk writing by enabling and disabling it. + + * src/sndfile.c + Fix a bug where enabling and disabling PEAK chunk was screwing up. + +2006-03-31 Erik de Castro Lopo + + * src/sndfile.h.in + Add the block of 190 reserved bytes into this struct to allow for + future expansion. + + * src/wav.c src/sndfile.c src/broadcast.c + Significant cleanup of broadcast wave stuff. + + * examples/sndfile-info.c + Fix print message. + + * tests/command_test.c tests/Makefile.am + Complete bext tests, hook test in test suite. + +2006-03-30 Erik de Castro Lopo + + * src/sndfile.h.in + Make coding_history field of SF_BROADCAST_INFO struct a char array instead + of a char pointer. + + * src/sndfile.c src/common.h src/wav.c + Clean up knock on effects of above chnage. + + * examples/sndfile-info.c + Add -b command line option to usage message. + Clean up output of broadcast wave info. + + * src/wav.c + Ignore and skip the 'levl' chunk. + +2006-03-26 Erik de Castro Lopo + + * configure.ac + Fix handling of --enable and --disable configure args. Thanks to Diego + 'Flameeyes' Pettenò who sent the patch. + +2006-03-22 Erik de Castro Lopo + + * doc/win32.html + Make it really clear that although the MSVC++ cannot compile libsndfile, + the precompiled DLL can be used in C++ programs compiled with MSVC++. + +2006-03-18 Erik de Castro Lopo + + * src/aiff.c + Fix bug in writing of INST chunk in AIFF files. + Fix potential bug in writing MARK chunks. + + * src/sndfile.c + Make sure the instrument chunk can only be written at the start of the file. + + * tests/command_test.c + Add check of log buffer. + + * tests/utils.tpl + Add usage of space character to psf_binheader_writef. + +2006-03-17 Erik de Castro Lopo + + * src/Makefile.am tests/Makefile.am + Remove --source-time argument from autogen command lines. + + * src/broadcast.c + New file for EBU Broadcast chunk in WAV files. + + * src/sndfile.c src/sndfile.h.in src/wav.c src/common.h + Add patch from Paul Davis implementing read/write of the BEXT chunk. + +2006-03-16 Erik de Castro Lopo + + * Win32/README-precompiled-dll.txt + New file descibing how to use the precompiled DLL. + + * Win32/Makefile.am + Add Win32/README-precompiled-dll.txt to EXTRA_DIST files. + + * configure.ac + Bump version to 1.0.15. + +2006-03-11 Erik de Castro Lopo + + * src/wav.c + On read, only add the endian flag if the file is big endian. + + * src/ms_adpcm.c + Fixed writing of APDCM coeffs in RIFX files. + + * tests/write_read_test.tpl tests/lossy_comp_test.c + Add tests for RIFX files. + +2006-03-10 Erik de Castro Lopo + + * Mingw-make-dist.sh + Bunch of improvements. + + * doc/win32.html + Update MinGW program versions. + +2006-03-09 Erik de Castro Lopo + + * src/create_symbols_file.py + Fix the library name in created win32 DEF file. Add correct DLL name for + Cygwin DLL. + + * Win32/Makefile.am tests/Makefile.am + Remove redundant files, add win32_ordinal_test to test suite. + + * tests/win32_ordinal_test.c + Update to do test in cygsndfile-1.dll as well. + + * doc/win32.html + Fix typo, mention that -mno-cygwin with the Cygwin compiler does not work. + + * src/wav.c src/wav_w64.c src/sndfile.c src/sndfile.h.in + Apply large patch from Jesse Chappell which adds support for RIFX files. + +2006-03-08 Erik de Castro Lopo + + * Makefile.am + Add Mingw-make-dist.sh to the extra dist files. + + * configure.ac + Fix setting SHLIB_VERSION_ARG for MinGW. + + * tests/win32_ordinal_test.c + New test program to test that the win32 DLL ordinals agree with the DEF + file. + +2006-03-04 Erik de Castro Lopo + + * src/common.h + Add a static inline function to convert an int to a size_t. This will be + a compile to nothing on 32 bit CPUs and a sign extension on 64 bit CPUs. + + * src/aiff.c src/avr.c src/common.c src/xi.c src/gsm610.c + Fix an ia64 problem where a varargs function was being passed an int in + some places and a size_t in other places. + + * src/sd2.c + Add a workaround for situations where OSX seems to add an extra 0x52 bytes + to the start of the resource fork. + +2006-02-19 Erik de Castro Lopo + + * Mingw-make-dist.sh + Add a shell script to build the windows binary/source ZIP file. + + * doc/index.html + Add download link for windows binary/source ZIP file. Add links for GPG + signatures. + + * doc/win32.html + Remove info about building using microsoft compiler. + + * configure.ac + Bump version to 1.0.14. + +2006-02-11 Erik de Castro Lopo + + * src/sd2.c + Improve logging of errors in resource fork parser. + +2006-01-31 Erik de Castro Lopo + + * Win32/Makefile.msvc + Replace au_g72x.* with g72x.*. Thanks to ussell Borogove. + +2006-01-29 Erik de Castro Lopo + + * src/common.c + Make sure return values are initialised header buffer is full. + + * src/wav.c + Add workarounds for messed up WAV files. + +2006-01-21 Erik de Castro Lopo + + * Win32/config.h + Undef HAVE_INTTYPES_H for win32. + + * tests/command_test.c + Don't exit on error in instrument test for XI files. + + * configure.ac + Bump version to 1.0.13. + + * doc/*.html NEWS README + Update version numbers. + +2006-01-19 Erik de Castro Lopo + + * src/xi.c + Start work on add read/write of instrument chunks. + + * src/command_test.c + Add tests for XI instrument chunk. + + * tests/largefile_test.c tests/Makefile.am + Add new test and hook it into the build system. This test will not be run + automatically because it requires 3 Gig of disk space and takes 3 minutes + to run. + +2006-01-10 Erik de Castro Lopo + + * examples/sndfile-play.c + Fix calculation of samples remaining in win32 code. Thanks Axel Roebel. + + * src/common.h + Make sure length of header buffer can hold header plus strings. Thanks Axel + Roebel. + +2006-01-09 Erik de Castro Lopo + + * src/sndfile.h.in src/aiff.c src/wav.c + Apply a patch from John Fitch (Csound project). + Add detune field to SF_INSTRUMENT struct. + Add reading/writing instrument chunks to WAV files. + + * tests/command_test.c + Update SF_INSTRUMENT tests. + + * tests/Makefile.am + Hook instrument tests into test suite. + +2006-01-05 Erik de Castro Lopo + + * configure.ac + Check for because some broken systems (like Solaris) don't have + which is the 1999 ISO C standard file containing int64_t. + + * src/sfendian.h src/common.h + Use if is not available. + +2005-12-30 Erik de Castro Lopo + + * tests/peak_chunk_test.c + Extend and clean up tests. + + * src/sndfile.c + Fix a bug that prevented the turning off of PEAK chunks. + +2005-12-29 Erik de Castro Lopo + + * tests/error_test.c + Make the test distclean correct. + + * src/file_io.c + Fix an SD2 MacOSX bug (reported by vince schwarzinger). + +2005-12-28 Erik de Castro Lopo + + * src/aiff.c tests/command_test.c + Apply a big patch from John ffitch (Csound project) to add reading and + writing of instrument chunks to AIFF files. Also update the test. + +2005-12-10 Erik de Castro Lopo + + * tests/aiff_rw_test.c tests/virtual_io_test.c tests/utils.tpl + Move test function dump_data_to_file() to utils.tpl. + + * tests/error_test.c tests/Makefile.am + Updates, including a new test to test that sf_error() returns a valid error + number. + +2005-12-07 Erik de Castro Lopo + + * examples/list_formats.c + Make sure the SF_INFO struct is memset to all zero before being used. + Thanks to Stephen F. Booth. + + * src/sndfile.c + Make the return value of sf_error() match the API documentation. + +2005-11-19 Erik de Castro Lopo + + * examples/sndfile-convert.c + Allow conversion to raw gsm610. + + * src/common.h src/sndfile.c src/au.c + Remove au_nh_open() and all references to it (wasn't working anyway). + + * tests/headerless_test.c + Add new test for file extension based detection. + + * src/sndfile.c + Rejig file extension based file type detection. + +2005-11-16 Erik de Castro Lopo + + * src/sndfile.c + Add "gsm" as a recognised file extension when no magic number can be found. + + * tests/lossy_comp_test.c tests/Makefile.am + Test headerless GSM610. + +2005-11-13 Erik de Castro Lopo + + * doc/api.html + Fix a minor typo and a minor error. Thanks Christoph Kobe and John Pavel. + +2005-10-30 Erik de Castro Lopo + + * src/wav_w64.c + Add more reporting of 'fmt ' chunk for G721 encoded files. + + * src/wav.c + Gernerate a more correct 20 byte 'fmt ' chunk rather than a 16 byte one. + +2005-10-29 Erik de Castro Lopo + + * src/G72x/g72x.[ch] + Minor cleanup of interface. + +2005-10-28 Erik de Castro Lopo + + * src/ogg.c + Removed the horribly broken and non-functional OGG implementation when + --enable-experimental was enabled. When OGG does finally work it will be + merged. + + * src/caf.c + Fix a memory leak. + +2005-10-27 Erik de Castro Lopo + + * src/g72x.c src/G72x/*.(c|h) src/common.h src/sndfile.c src/wav.c src/au.c + Add support for G721 encoded WAV files. + + * doc/index.html + Update support matrix. + + * tests/lossy_comp_test.c + For file formats that support it, add string data after the audio data and + make sure it isn't treated as audio data on read. + + * src/gsm610.c + Add code to ensure that the container close function (ie for WAV files) gets + called after the codec's close function. This allows GSM610 encoded WAV files + to have string data following the audio data. + Add an AIFF specific check on psf->datalength. + + * src/wav.c + Simplify wav_close function. + + * src/aiff.c + Make sure the tailer data gets written at an even file offset. Pad if + necessary. + + * src/common.h + Replace the close function pointer in SF_PRIVATE with separate functions + codec_close and container_close. The former is always called first. + + * src/*.c + Fix knock on effects of above. + +2005-10-26 Erik de Castro Lopo + + * examples/sndfile-info.c + Complete dumping SF_INSTRUMENT data. + + * src/dwvw.c src/ima_adpcm.c src/gsm610.c src/ms_adpcm.c + Add extra checks in *_init function. + + * tests/lossy_comp_test.c + Add a string comment to the end of the files to make sure that the decoder + doesn't decode beyond the end of the audio data section. + +2005-10-25 Erik de Castro Lopo + + * examples/sndfile-info.c + Minor code cleanup. + Start work on dumping SF_INSTRUMENT data. + +2005-10-23 Erik de Castro Lopo + + * src/sndfile.h.in src/common.h src/common.c + Update definition of SF_INSTRUMENT struct and create a function to allocate + and initialize the struct (input from David Viens). + Clean up definition of SF_INSTRUMENT struct. + + * src/wav.c src/wav_w64.c + Add support for Ambisoncs B WAVEX files (David Viens). + + * src/aiff.c src/wav.c src/wav_w64.c + Start work on reading/writing the SF_INSTRUMENT data. + + * src/sndfile.c + Add code to get and set SF_INSTRUMENT data. + + * tests/command_test.* tests/Makefile.am + Add test for set and getof SF_INSTRUMENT data. + The file command_test.c is no longer autogen generated. + +2005-10-15 Erik de Castro Lopo + + * src/gsm610.c + Minor cleanup. + +2005-10-14 Erik de Castro Lopo + + * tests/lossy_comp_test.c + Minor cleanup. + +2005-10-13 Erik de Castro Lopo + + * src/*.c + Ensure sfconfig.h is included before any other header file. + + * src/file_io.c + Add comments documenting the three sections of the file. + + * src/gsm610.c + Make sure SF_FORMAT_WAVEX are handled correctly. + +2005-10-07 Erik de Castro Lopo + + * configure.ac + Add options to allow disabling of FLAC and ALSA. Suggested by Ben Greear. + +2005-09-30 Erik de Castro Lopo + + * tests/locale_test.c + Modify the way the unicode strings were encoded so that older compilers + do not complain. Thanks Axel Roebel. + + * configure.ac + Bump the version to 1.0.12 for release. + + * NEWS README Win32/config.h doc/(FAQ|index.html|command|api).html + Update version numbers. + +2005-09-26 Erik de Castro Lopo + + * src/flac.c + Fix valgrind error and minor cleanup. + +2005-09-25 Erik de Castro Lopo + + * src/(au|paf|aiff|w64|wav|svx).c + Make sure structs are initialised. + +2005-09-24 Erik de Castro Lopo + + * configure.ac + Make -Wdeclaration-after-statement work with --enable-gcc-werror configure + option. + Add -std=gnu99 (C99 plus posix style stuff like gmtime_r) to CFLAGS if the + compiler supports it. + +2005-09-23 Erik de Castro Lopo + + * configure.ac acinclude.m4 + Add -Wdeclaration-after-statement to CFLAGS if the compilers supports it. + +2005-09-22 Erik de Castro Lopo + + * tests/util.(tpl|def) + Make the test_write_*_or_die() functions const safe. + +2005-09-21 Erik de Castro Lopo + + * src/nist.c + Make sure the data offset is read from the file header. Thanks to + David A. van Leeuwen for a patch. + +2005-09-20 Erik de Castro Lopo + + * configure.ac src/sfconfig.h + Check for and the function setlocale(). + Set config variables to zero if not found. + + * tests/locale_test.c tests/Makefile.am + Add new test program and hook into build/test system. + +2005-09-18 Erik de Castro Lopo + + * src/common.h src/file_io.c + On windows, use windows specific types for file handles. + Add functions psf_init_files() and psf_use_rsrc(). + + * src/sd2.c + Make resource fork handling independant of file desciptor/handles. + + * src/sndfile.c src/test_file_io.c + Fix knock on effects. + +2005-09-06 Erik de Castro Lopo + + * src/float_cast.h + The lrint and lrintf implementations in Cygwin are both buggy and slow. + Add replacements which were pulled from the Public Domain MinGW math.h + header file. + +2005-09-05 Erik de Castro Lopo + + * tests/(lossy_comp_test|virtual_io_test).c + More Valgrind fixups. + + * configure.ac + Simplify and correct configuring for Cygwin. + + * Win32/config.h Win32/sndfile.h Win32/Makefile.msvc + Update build for MSVC. + +2005-09-04 Erik de Castro Lopo + + * tests/lossy_comp_test.c + Make sure to close SNDFILE when exiting test when file format is not seekable. + + * tests/(aiff_rw_test|virtual_io_test).c + Do a few valgrind fix ups. + +2005-09-03 Erik de Castro Lopo + + * src/float32.c src/double64.c + Replace floating point equality comparisons with greater/less comparisons. + Found by John Pavel using the Intel compiler. + + * src/sfconfig.h + New file to clean up issues surrounding autoconf generated preprocessor + symbols. + + * src/*.(c|h) tests/*.(c|tpl) examples/*.c + Fixed a bunch of other stuff found by John Pavel using the Intel compiler. + + * src/file_io.c + Remove Mac OS9 Metrowerks compiler specific hacks. + +2005-08-31 Erik de Castro Lopo + + * src/w64.c + Cast integer literal to sf_count_t in call to psf_binheader_writef() to + prevent Valgrind error. + +2005-08-30 Erik de Castro Lopo + + * doc/command.html + Improve documentation of SF_GET_FORMAT_SUBTYPE. + +2005-08-26 Erik de Castro Lopo + + * examples/sndfile-convert.c + Allow files to be converted to SD2 format. + + * src/sd2.c + Fix a bug in reading and writing of SD2 files on little endian CPUs. + Thanks to Matthew Willis for finding this. + +2005-08-25 Erik de Castro Lopo + + * doc/api.html + Update Note2 to point to SFC_SET_SCALE_FLOAT_INT_READ. + +2005-08-16 Erik de Castro Lopo + + * configure.ac + Use $host_os instead of $target_os (thanks to Mo De Jong). + +2005-08-15 Erik de Castro Lopo + + * src/Makefile.am + Apply a patch from Mo DeJong to allow building outside of the source dir. + + * src/file_io.c + Fix psf_fsync() for win32. + + * src/wav.c src/wav_w64.(c|h) + Move some code from wav.c to wav_w64.c to improve the log output of files of + type WAVE_FORMAT_EXTENSIBLE. + +2005-08-10 Erik de Castro Lopo + + * src/create_symbols_file.py + Make sure sf_write_fsync is an exported symbol. + + * examples/sndfile-convert.c + Add support for writing VOX adpcm files. + +2005-07-31 Erik de Castro Lopo + + * doc/api.html + Document the new function sf_write_sync(). + + * doc/FAQ.html + Do you plan to support XYZ codec. + +2005-07-28 Erik de Castro Lopo + + * src/sndfile.h.in src/sndfile.c + Add function sf_write_sync() to the API. + + * src/common.h src/file_io.c + Low level implementation (win32 not done yet). + + * tests/write_read_test.tpl + Use the new function in the tests. + +2005-07-24 Erik de Castro Lopo + + * src/common.h src/double64.c src/float32.c src/sndfile.c + Change the way PEAK chunk info is stored. Peaks now stored as an sf_count_t + for position and a double as the value. + + * src/aiff.c src/caf.c src/wav.c + Fix knock on effects of above changes. + + * src/caf.c + Implement 'peak' chunk for file wuth data in SF_FORMAT_FLOAT or + SF_FORMAT_DOUBLE format. + +2005-07-23 Erik de Castro Lopo + + * src/nist.c + Fix a bug where a variable was being used without being initialized. + + * src/flac.c + Add extra debug in sf_flac_meta_callback. + Make a bunch of private functions static. + + * src/aiff.c src/wav.c + Fix allocation for PEAK_CHUNK (bug found using valgrind). + +2005-07-21 Erik de Castro Lopo + + * src/common.h + Move the peak_loc field of SF_PRIVATE to the PEAK_CHUNK struct. + Remove had_peak field of SF_PRIVATE, use pchunk != NULL instead. + Rename PEAK_CHUNK and PEAK_POS to PEAK_CHUNK_32 and PEAK_POS_32. + + * src/aiff.c src/caf.c src/wav.c src/float32.c src/double64.c + Fix knock on effects from above. + +2005-07-19 Erik de Castro Lopo + + * src/wav.c + Prevent files with unknown chunks from being opened read/write. + +2005-07-14 Erik de Castro Lopo + + * src/flac.c + Do not use psf->end_of_file because it never gets set to anything. + + * src/common.h + Remove unused SF_PRIVATE field end_of_file. + +2005-07-12 Erik de Castro Lopo + + * src/common.c + Change the 'S' format specifier of psf_binheader_writef() to write AIFF + style strings (no terminating character). + + * src/aiff.c + Move to new (correct) AIFF string style. Thanks to Axel Roebel for being + so persistent on this issue. + +2005-07-11 Erik de Castro Lopo + + * src/sndfile.c + Allow SFE_UNSUPPORTED_FORMAT as an error from sf_open(). + + * doc/api.html doc/command.html + Documentation updates (thanks to Kyroz for promoting these updates). + + * src/mat5.c + Modify the way the header is written. + +2005-07-10 Erik de Castro Lopo + + * src/caf.c + Add a 'free' chunk to the written file so that the audio data starts at + an offset of 0x1000. + + * src/sndfile.c + Allow SFE_UNSUPPORTED_FORMAT as an error from sf_open(). + +2005-07-09 Erik de Castro Lopo + + * src/caf.c src/sndfile.c + Add support for signed 8 bit integers. + + * tests/write_read_test.tpl + Add test for signed 8 bit integers in CAF files. + + * doc/index.html + Update matrix for signed 8 bit integers in CAF files. + +2005-07-08 Erik de Castro Lopo + + * src/sndfile.c + Update sf_check_format() to support CAF. + + * examples/sndfile-convert.c + Add support for ".caf" file extension. + + * doc/index.html + Add Apple CAF to the support matrix. + + * src/caf.c + Add file write support. + + * src/common.c + Fix printing of Frames. + + * tests/Makefile.am tests/write_read_test.tpl tests/lossy_comp_test.c + tests/header_test.tpl misc_test.c + Add tests for CAF files. + +2005-07-07 Erik de Castro Lopo + + * doc/FAQ.html + Fix Q/A about reading/writing memory buffers. + + * src/caf.c + Bunch of work to support reading of CAF files. + +2005-07-04 Erik de Castro Lopo + + * src/(aiff|ima_adpcm|mat4|mat5|ms_adpcm).c examples/sndfile-play.c + Fix sign conversion errors reported by gcc-4.0. + + * src/caf.c + New file for Apple's Core Audio File format. + + * src/sndfile.c src/common.h src/sndfile.h.in src/Makefile.am + Hook new file into build system. + +2005-06-21 Erik de Castro Lopo + + * src_wav_w64.c + Fix handling of stupidly large 'fmt ' chunks. Thanks to Vadim Berezniker + for supplying an example file. + + * src/common.h src/sndfile.c + Remove redundant error code SFE_WAV_FMT_TOO_BIG. + +2005-06-20 Erik de Castro Lopo + + * src/sndfile.h.in src/common.h src/sndfile.c + Add public error value SF_ERR_MALFORMED_FILE. + + * src/sndfile.c + When parsing a file header fails and we don't have a system error, then set + the error number to SF_ERR_MALFORMED_FILE (suggested by Kyroz). + + * configure.ac + Allow sqlite support to be disabled in configure script. + + * regtest/database.c regtest/sndfile-regtest.c + Fix compiling when sqlite is missing. + +2005-06-11 Erik de Castro Lopo + + * src/file_io.c + Fix psf_is_pipe() and return value of psf_fread() when using virtual i/o. + + * src/sndfile.c + Fix VALIDATE_AND_ASSIGN_PSF macro for virtual i/o. + + * tests/virtual_io_test.c + Fill in skeleton test program. + + * tests/Makefile.am + Move virtual i/o tests to end of tests with stdio/pipe tests. + + * src/(sndfile.h.in|file_io.c|common.h|sndfile.c) tests/virtual_io_test.c + Rename some of the virtual i/o functions and data types. + +2005-06-10 Erik de Castro Lopo + + * src/sndfile.c + Fix the return values of sf_commands : SFC_SET_NORM_DOUBLE, + SFC_SET_NORM_FLOAT, SFC_GET_LIB_VERSION and SFC_GET_LOG_INFO. Thanks to + Kyroz for pointing out these errors. + + * doc/command.html + Correct documented return values for SFC_SET_NORM_DOUBLE and + SFC_SET_NORM_FLOAT. Thanks to Kyroz again. + +2005-05-17 Erik de Castro Lopo + + * regtest/* + Add new files for sndfile-regtest program. + + * configure.ac Makefile.am + Hook regetest into build. + + * src/wav.c src/common.c + Fix a regression where long ICMT chunks were causing the WAV parser + to exit. + +2005-05-15 Erik de Castro Lopo + + * libsndfile.spec.in + Add html docs to the files section as suggested by Karsten Jeppesen. + + * src/aiff.c + Fix parsing of odd length ANNO chunks. + +2005-05-13 Erik de Castro Lopo + + * src/common.h + Change the include guard to prevent clashes with other code. + +2005-05-12 Erik de Castro Lopo + + * examples/sndfile-play.c + Improve error handling in code for playback under Linux/ALSA. + +2005-05-10 Erik de Castro Lopo + + * src/ircam.c + Fix writing of IRCAM files on big endian systems (thanks to Axel Roebel). + + * src/wav.c + Add workaround for files created by the Peak audio editor on Mac which can + produce files with very short LIST chunks (thanks to Jonathan Segel who + supplied the file). + +2005-04-30 Erik de Castro Lopo + + * src/aiff.c + Apply a patch From David Viens to make the parsing of basc chunks more + robust. + + * src/wav.c + Another patch from David Viens to write correct wavex channel masks for + the most common channel configurations. + +2005-04-08 Erik de Castro Lopo + + * src/command.c + Only allow FLAC in the format arrays if FLAC is enabled. Thanks to + Leigh Smith. + +2005-03-09 Erik de Castro Lopo + + * src/common.h + Add a directory field for storing the file directory to the SF_PRIVATE + struct. + + * src/sndfile.c + Grab the directory name when copying the file path. + + * src/file_io.c + Cleanup psf_open_rsrc() and also check for resource fork in + .AppleDouble/filename. + +2005-03-01 Erik de Castro Lopo + + * src/svx.c + Fix a bug in the printing of the channel count. Bug reported by Michael + Schwendt. Thanks. + +2005-01-26 Erik de Castro Lopo + + * src/paf.c + Fix a seek bug for 24 bit PAF files. + + * tests/write_read_test.tpl + Update write_read_test to trigger the previously hidden PAF seek bug. + +2005-01-25 Erik de Castro Lopo + + * src/aiff.c src/w64.c src/wav.c + Do not return a header parse error when the log buffer overflows. + Continuing parsing works even on files where the log buffer does overflow. + This avoids a bug on some weirdo WAV (and other) files. + + * src/common.h src/sndfile.c + Remove SFE_LOG_OVERRIN error and its associated error message. + + * src/file_io.c + Fix a rsrc fork problem on MacOSX. + +2004-12-31 Erik de Castro Lopo + + * src/sndfile-play.c + In the ALSA output code, added call to snd_pcm_drain() just before + snd_pcm_close() as suggested by Thomas Kaeding. + In the OSS output code, added two ioctls (SNDCTL_DSP_POST and + SNDCTL_DSP_SYNC) just before the close of the audio device. + + * tests/virtual_io_test.c tests/Makefile.am + Add a new test program (currently empty) and add it to the build. + +2004-12-29 Erik de Castro Lopo + + * src/sndfile.h.in src/sndfile.h src/common.h src/file_io.c + src/create_symbols_file.py + Apply patch from Steve Baker which is the beginnings of a virtual + I/O interface. + +2004-12-23 Erik de Castro Lopo + + * src/*.c src/sndfile.h.in + Const-ify the write path throughout the library. + +2004-12-14 Erik de Castro Lopo + + * doc/development.html + Minor improvements. + +2004-11-29 Erik de Castro Lopo + + * doc/bugs.html + Minor improvements. + +2004-11-18 Erik de Castro Lopo + + * src/aiff.c + Add workaround for Logic Platinum AIFF files with broken COMT chunks. + +2004-11-16 Erik de Castro Lopo + + * doc/FAQ.html + Remove some ambiguities in the SD2 FAQ answer. + +2004-11-15 Erik de Castro Lopo + + * Win32/sndfile.h Win32/config.h MacOS9/sndfile.h MacOS9/config.h + Updates from autoconfig versions. + +2004-11-13 Erik de Castro Lopo + + * src/aiff.c + Fix parsing of COMT chunks. Store SF_STR_COMMENT data in ANNO chunks + instead of COMT chunk. + +2004-11-07 Erik de Castro Lopo + + * src/file_io.c src/common.h + Change the ptr argument to psf_write() from "void*" to a "const void*". + Thanks to Tobias Gehrig for suggesting this. + +2004-10-31 Erik de Castro Lopo + + * src/file_io.c src/common.h + Add functions psf_close_rsrc() and read length of resourse fork into + rsrclength field of SF_PRIVATE. + + * src/sd2.c + Make sure resource fork gets closed. + + * tests/util.tpl + Add functions to check for file descriptor leakage. + + * src/write_read_test.tpl + Use the file descriptor leak checks. + + * src/sndfile.h.in + Add SFC_GET_LOOP_INFO and SF_LOOP_INFO struct. + + * src/common.h + Add SF_LOOP_INFO pointer to SF_PRIVATE. + + * src/wav.c src/aiff.c + Improve and add parsing of 'ACID' and 'basc' chunks, filling in + SF_LOOP_INFO data in SF_PRIVATE. + +2004-10-30 Erik de Castro Lopo + + * src/sd2.c + Further cleanup: remove printfs, change snprintf to LSF_SNPRINTF. + + * Win32/config.h Win32/sndfile.h + Updates. + + * tests/util.tpl + Add win32 macro for snprintf. + +2004-10-29 Erik de Castro Lopo + + * src/sfendian.h + Add macros : H2BE_SHORT, H2BE_INT, H2LE_SHORT and H2LE_INT. + + * src/sd2.c + Use macros to make sure writing SD2 files on little endian machines works + correctly. + + * tests/util.tpl + Add a delete_file() function which also deletes the resource fork of SD2 + files. + + * tests/write_read_test.tpl + Use delete_file() so that "make distcheck" works. + +2004-10-28 Erik de Castro Lopo + + * src/sndfile.c src/file_io.c + Move resource filename construction and testing to psf_open_rsrc(). + + * src/common.h src/sndfile.c + Add error SFE_SD2_FD_DISALLOWED. + + * tests/util.tpl tests/*.(c|tpl) + Add and allow_fd parameter to test_open_file_or_die() so that use of + sf_open_fd() can be avoided when opening SD2 files. + +2004-10-27 Erik de Castro Lopo + + * src/wav.c + Update ACID chunk parsing. + + * src/sd2.c + More fixes for files with large resource forks. + +2004-10-23 Erik de Castro Lopo + + * src/common.h src/sndfile.c + Add error numbers and messages for sd2 files. + + * src/sd2.c + Reading of sd2 (resource fork version) now seems to be working. + +2004-10-17 Erik de Castro Lopo + + * src/file_io.h + Update file_io.c to include win32 psf_rsrc_open(). + + * tests/floating_point_test.tpl + Remove use of __func__ in test programs (MSVC++ doesn't grok this). + + * Win32/(config|sndfile).h MacOS9/(config|sndfile).h + Updates. + +2004-10-13 Erik de Castro Lopo + + * src/sfendian.h + Fix endswap_int64_t_(array|copy). + + * src/test_endswap.(tpl|def) + Add tests for above and inprove all tests. + +2004-10-12 Erik de Castro Lopo + + * src/sfendian.h + Improve type safety, add endswap_double_array(). + + * src/double64.c + Use endswap_double_array() instead of endswap_long_array(). + + * src/test_endswap.(tpl|def) src/Makefile.am + Add preliminary endswap tests and hook into build system. + +2004-10-06 Erik de Castro Lopo + + * src/configure.ac src/makefile.am + Finally fix the bulding of DLLs on Win32/MinGW. + + * tests/makefile.am + Fix running of tests on Win32/MinGW. + +2004-10-01 Erik de Castro Lopo + + * src/sndfile.h.in src/sndfile.c tests/floating_point_test.tpl + Rename SFC_SET_FLOAT_INT_MULTIPLIER to SFC_SET_SCALE_FLOAT_INT_READ. + + * doc/command.html + Document SFC_SET_SCALE_FLOAT_INT_READ. + +2004-09-30 Erik de Castro Lopo + + * tests/floating_point_test.(tpl|def) + Derived from floating_point_test.c. + Add (float|double)_(short|int)_test functions. + + * tests/util.(tpl|def) + Make separate float and double versions of gen_windowed_sine(). + + * tests/write_read_test.tpl + Fix after changes to gen_windowed_sine(). + + * src/(float32|double64).c + Implement SFC_SET_FLOAT_INT_MULTIPPLIER. + +2004-09-29 Erik de Castro Lopo + + * acinclude.m4 + Fix warnings from automake 1.8 and later. + + * examples/sndfile-info.c + Add a "fflush (stdout)" after printing Win32 message. + +2004-09-28 Erik de Castro Lopo + + * Win32/Makefile.mingw.in + Add a "make install" target. + +2004-09-24 Erik de Castro Lopo + + * src/sndfile.h.in src/common.h src/sndfile.c src/command.c + Start work on adding command SFC_SET_FLOAT_INT_MULTIPLIER. + +2004-09-22 Erik de Castro Lopo + + * examples/sndfile-convert.c + Fix a bug converting stereo integer PCM files to float. + +2004-09-22 Erik de Castro Lopo + + * examples/sndfile-play.c + Appy patch from Conrad Parker to make Mac OSX error messages more + consistent and informative. + + * doc/api.html + Fix a HTML HREF which was wrong. + + * doc/win32.html + Add information about when nmake fails. + +2004-09-05 Erik de Castro Lopo + + * examples/sndfile-play.c + Another patch from Denis Cote to prevent race conditions. + +2004-09-02 Erik de Castro Lopo + + * src/common.h src/ms_adpcm.c src/ima_adpcm.c + Fix alternative to ISO standard flexible struct array feature for broken + compilers. + +2004-08-31 Erik de Castro Lopo + + * src/common.h src/string.c src/sndfile.c + Make sf_set_string() return an error if trying to set a string when in + read mode. + +2004-08-29 Erik de Castro Lopo + + * src/common.h + Change the unnamed union into a named union so gcc-2.95 will compile it. + + * src/*.c + Fixes to allow for the above change. + +2004-08-20 Erik de Castro Lopo + + * examples/sndfile-play.c + Fixes for Win32. Thanks to Denis Cote. + + * Win32/Win32/Makefile.(msvc|mingw.in) + Fix build system after removal of sfendian.h. + Build sndfile-convert. + + * src/Makefile.am + Remove sfendian.c from dependancies. + +2004-08-10 Erik de Castro Lopo + + * src/sndfile.h.in + Fix typo in comments (thanks Tommi Sakari Uimonen). + +2004-07-31 Erik de Castro Lopo + + * tests/(a|u)law_test.c + Minor cleanup. + +2004-07-29 Erik de Castro Lopo + + * src/(pcm|float|double64|ulaw|alaw|xi).c + Optimise read/write loops by removing a redundant variable. + +2004-07-24 Erik de Castro Lopo + + * src/file_io.c + Remove call to fsync() in psf_close(). + +2004-07-19 Erik de Castro Lopo + + * src/pcm.c + Inline x2y_array() functions where possible. + + * configure.ac + Detect presence of type int64_t. + + * src/sfendian.c src/sfendian.h + Move functions in the first file to the sfendian.h as static inline + functions. + Improve endswap_long_*() where possible. + +2004-07-17 Erik de Castro Lopo + + * src/pcm.c + When converting from unsigned char to float or double, subtract 128 before + converting to float/double rather than after to save a floating point + operation as suggested by Stefan Briesenick. + + * src/(pcm|sfendian|alaw|ulaw|double64|float32).c + Optimize inner loops by changing the loop counting slightly as suggested + by Stefan Briesenick. + + * configure.ac + Detect presence of . + + * src/sfendian.h + Use if present as suggested by Stefan Briesenick. + + * src/pcm.c + Update bytewapping. + +2004-07-02 Erik de Castro Lopo + + * src/common.h src/*.c + Change the psf->buffer field of SF_PRIVATE into a more type safe union with + double, float, int etc elements. + +2004-06-28 Erik de Castro Lopo + + * examples/sndfile-play.c + Merge slightly modifed patch from Stanko Juzbasic which allows playback of + mono files on MacOSX. + +2004-06-25 Erik de Castro Lopo + + * examples/sndfile-convert.c + Move copy_metadata() after the second sf_open(). + +2004-06-21 Erik de Castro Lopo + + * examples/sndfile-convert.c + Fix a bug which caused the program to go into an infinite loop if the source + file has no meta-data. Thanks to Ron Parker for reporting this. + + * src/sndfile.h.in + Add SF_STR_FIRST and SF_STR_LAST to allow enumeration of string types. + + * Win32/sndfile.h MacOS9/sndfile.h + Update these as per the above file. + +2004-06-17 Erik de Castro Lopo + + * configure.ac src/common.h src/ogg.c src/sndfile.c src/sndfile.h.in + src/Makefile.am + Apply large patch from Conrad Parker implementing Ogg Vorbis, Ogg Speex and + Annodex support via liboggz and libfishsound. Thanks Conrad. + +2004-06-15 Erik de Castro Lopo + + * src/avr.c src/ircam.c src/nist.c src/paf.c src/xi.c + Add cast to size_t for some parameters passed to psf_binheader_writef. This + is Debian bug number 253490. Thanks to Anand Kumria and Andreas Jochens. + + * src/w64.c + Found and fixed a bug resulting from use of size_t when writing W64 'fmt ' + chunk. + +2004-06-14 Erik de Castro Lopo + + * configure.ac + Bump version to 1.0.10 ready for release. + + * Makefile.am + Remove redundant files (check_libsndfile.py libsndfile_version_convert.py) + from distribution tarball. + + * tests/header_test.tpl + Fix uninitialised variable. + + * src/GSM610/short_term.c + Fix compiler warning on MSVC++. + +2004-05-23 Erik de Castro Lopo + + * src/wav.c + Improve record keeping of chunks seen and return an error if a file with + unusual chunks is opened in mode SFM_RDWR. + + * src/mmreg.h + This file not needed so remove it. + +2004-05-22 Erik de Castro Lopo + + * tests/header_test.tpl + Add extra_header_test(). + + * src/common.h src/sndfile.c + Add SFE_RDWR_BAD_HEADER error number and string. + +2004-05-21 Erik de Castro Lopo + + * tests/utils.tpl tests/*.c tests/*.tpl + Add a line number argument to check_log_buffer_or_die() and update all + files that use that function. + + * tests/header_test.tpl + Modify/update tests for files opened SFM_RDWR and SFC_UPDATE_HEADER_AUTO. + + * src/aiff.c src/wav.c + Fix another bug in AIFF and WAV files opened in SFM_RDWR and using + SFC_UPDATE_HEADER_AUTO. + + * src/test_file_io.c + Add a test for psf_ftruncate() function. + +2004-05-19 Erik de Castro Lopo + + * src/sndfile.c + Fix another weird corner case bug found by Martin Rumori. Thanks. + + * tests/header_test.(tpl|def) + Two new files to test for the absence of the above bug and include tests + moved from tests/misc_test.c. + + * tests/Makefile.am + Hook new tests into build/test system. + + * tests/misc_test.c + Remove update_header_test() which has been moved to the new files above. + +2004-05-16 Erik de Castro Lopo + + * src/aiff.c + Fixed a bug reported by Martin Rumori on the LAD list. If a file created + with a format of SF_FORMAT_FLOAT and then closed before any data is written + to it, the header can get screwed up (PEAK chunk gets overwritten). + + * tests/write_read_test.tpl + Add a test (empty_file_test) for the above bug. + +2004-05-13 Erik de Castro Lopo + + * Win32/Makefile.mingw.in + Added a Makefile for MinGW (needs to be processed by configure). + + * src/mmsystem.h src/mmreg.h + Add files from the Wine project (under the LGPL) to allow build of + sndfile-play.exe under MinGW. + +2004-05-12 Erik de Castro Lopo + + * src/GSM610/gsm610_priv.h + Replace ugly macros with inline functions. + + * src/GSM610/*.c + Remove temporary variables used by macros and other minor fixes required by + above change. + +2004-05-10 Erik de Castro Lopo + + * tests/pipe_test.tpl tests/stdio_test.c Win32/Makefile.msvc + Make sure these programs compile (even though they do nothing) on Win32 + and add them to the "make check" target. + + * src/sfendian.h + Fix warning on Sparc CPU and code cleanup. + +2004-05-09 Erik de Castro Lopo + + * src/file_io.c + Fix warning messages when compiling under MinGW. + +2004-05-01 Erik de Castro Lopo + + * configure.ac + Set HAVE_FLEXIBLE_ARRAY in src/config.h depending on whether the compiler + accepts the flexible array struct member as per 1999 ISO C standard. + + * src/common.h src/ima_adpcm.c src/paf.c src/ms_adpcm.c + Added ugly #if HAVE_FLEXIBLE_ARRAY and provided a non-standards compliant + hack for non 1999 ISO C compliant compilers. + +2004-04-26 Erik de Castro Lopo + + * src/strings.c + If adding an SF_STR_SOFTWARE string, only append libsndfile-X.Y.Z if the + string does not already have libsndfile in the string. Thanks to Conrad + Parker. + + * tests/string_test.c + Add test to verify the above. + + * examples/sndfile-convert.c + Add ability to transcode meta data as well (Conrad Parker). + +2004-04-25 Erik de Castro Lopo + + * doc/command.html + Fix minor error. Thanks to Simon Burton. + + * doc/win32.html + Started adding instructions for compiling libsndfile under MinGW. + + * configure.ac + Add --enable-bow-docs to enable black text on a white background HTML docs. + + * doc/libsndfile.css.in + This is now a template file for configure which sets the foreground and + background colours. + +2004-04-20 Erik de Castro Lopo + + * configure.ac + Do some MinGW fixes. + + * configure.ac doc/Makefile.am + Install HTML docs when doing make install. + +2004-04-19 Erik de Castro Lopo + + * examples/sndfile-info.c + Print out the dB level with the signal max. + +2004-04-15 Erik de Castro Lopo + + * src/file_io.c + Define S_ISSOCK in src/file_io.c if required. + +2004-04-03 Erik de Castro Lopo + + * configure.ac + Improve printout configuration summary (as suggested by Axel Röbel). + + * doc/index.html + Add link to pre-release location. + + * src/sndfile.h.in + Remove comma after last element of enum. + + * src/float32.c src/double64.c + Fix read/write of float/double encoded raw files to/from pipes. + + * tests/pipe_test.c tests/pipe_test.tpl tests/pipe_test.def + Turn pipe_test.c into an autogenerated file and add tests for reading/ + writing floats and doubles. + + * tests/Makefile.am + Hook tests/pipe_test.* into build system. + +2004-04-02 Erik de Castro Lopo + + * configure.ac acinclude.m4 + Rename AC_C_STRUCT_HACK macro to AC_C99_FLEXIBLE_ARRAY. + +2004-03-31 Erik de Castro Lopo + + * tests/misc_test.c + Perform update_header_test in RDWR mode as well. + + * src/aiff.c + Fix problems when updating header in RDWR mode. + +2004-03-30 Erik de Castro Lopo + + * src/wav.c src/w64.c src/wav_w64.c + Integrate code supplied by David Viens for supporting microsoft's + WAVEFORMATEXTENSIBLE stuff. Thanks David for supplying this. + + * configure.ac doc/*.html + Bump version to 1.0.9. + +2004-03-28 Erik de Castro Lopo + + * src/command.c src/sndfile.c src/sndfile.h.in src/wav.c + Started work on supporting microsoft's WAVEFORMATEXTENSIBLE gunk. + +2004-03-26 Erik de Castro Lopo + + * src/avr.c + New file to handle Audio Visual Resaerch files. + + * src/sndfile.h.in src/common.h src/sndfile.c src/command.c + Hook AVR into everything else. + + * tests/Makefile.am tests/write_read_test.tpl tests/misc_test.c + Add testing for AVR files. + +2004-03-22 Erik de Castro Lopo + + * src/file_io.c + Fix psf_set_file() for win32. Thanks to Vincent Trussart (Plogue Art et + Technologie) for coming up with the solution. + +2004-03-21 Erik de Castro Lopo + + * tests/write_read_test.tpl + Fixed a bug that was causing valgrind to report a memory leak. The bug was + in the test code itself, not the library. + +2004-03-20 Erik de Castro Lopo + + * examples/generate.cs + An example showing how to use libsndfile from C#. Thanks to James Robson + for providing this. + +2004-03-19 Erik de Castro Lopo + + * src/common.c + Fix problems with WAV files containing large chunks after the 'data' + chunk. Thanks to Koen Tanghe for providing a sample file. + +2004-03-17 Erik de Castro Lopo + + * configure.ac + Detect presense of ALSA (Advanced Linux Sound Architecture). + + * examples/sndfile-play.c + Add ALSA output support. + + * examples/Makefile.am + Add ALSA_LIBS to link line of sndfile-play.c. + +2004-03-15 Erik de Castro Lopo + + * acinclude.m4 + Add new macro (AC_C_STRUCT_HACK) to detect whether the C compiler allows + the use of the what is known as the struct hack introduced by the 1999 ISO + C Standard. + + * configure.ac + The last release would not compile with gcc-2.95 due to the use of features + (ie struct hack) introduced by the 1999 ISO C Standard. + Add check to make sure compiler handles this and bomb out if it doesn't. + +2004-03-14 Erik de Castro Lopo + + * tests/write_read_test.tpl + Fix compiler warning on Win32. + + * src/file_io.c + Fix use of an un-initialised variable in Win32 stuff. + + * Win32/config.h examples/sndfile-play.c + Win32 fixes. + +2004-03-10 Erik de Castro Lopo + + * configure.ac + Fix bug which occurres when configuring for MinGW. + If compiler is gcc and cross compiling use -nostdinc. + +2004-03-09 Erik de Castro Lopo + + * src/common.h src/aiff.c src/wav.c src/float32.c src/double64.c + src/sndfile.c + Fix a bug with PEAK chunk handling for files with more than 16 channels. + Thanks to Remy Bruno for finding this. + +2004-03-08 Erik de Castro Lopo + + * src/common.c + Fix a bug which was preventing WAV files being openned correctly if the + file had a very large header. Thanks to Eldad Zack for finding this. + +2004-03-04 Erik de Castro Lopo + + * configure.ac src/file_io.c + Fix cross-compiling from Linux to Win32 using the MinGW tools. + +2004-03-01 Erik de Castro Lopo + + * src/create_symbols_file.sh + Christian Weisgerber pointed out that the shell script did not run on a + real Bourne shell although it did run under Bash in Bourne shell mode. + + * src/create_symbols_file.py + Rewrite of above in Python. Also add support for writing Win32 .def files. + The Python script generates Symbols.linux, Symbols.darwin and + libsndfile.def (Win32 version). These files get shipped with the tarball + so there should not be necessary to run the Python script when building + the code from the tarball. + + * configure.ac src/Makefile.am Win32/Makefile.am + Hook new Python script into the build system. + +2004-02-25 Erik de Castro Lopo + + * src/configure.ac + Add --enable-gcc-werror option and move GCC specific stuff down. + +2004-02-24 Erik de Castro Lopo + + * acinclude.m4 configure.ac + Fix clip mode detection (tested in one of HP's testdrive Itanium II boxes). + + * src/file_io.c + Added check for sizeof (off_t) != sizeof (sf_count_t) to prevent recurrence + of missing large file support on Linux and Solaris. + +2004-02-19 Erik de Castro Lopo + + * examples/sndfile-play.c + Fix a MacOSX specific bug which was caused by a space being inserted in + the middle of a file name. + + * configure.ac src/Makefile.am examples/Makefile.am + Fix a couple of MacOSX build issues. + +2004-02-17 Erik de Castro Lopo + + * doc/command.html + Document SFC_SET_CLIPPING and SFC_GET_CLIPPING. + +2004-02-14 Erik de Castro Lopo + + * doc/*.html + Applied patch from Frank Neumann (author of lakai) which fixes many minor + typos in documentation. Thanks Frank. + +2004-02-13 Erik de Castro Lopo + + * ChangeLog + Changed my email address throughout source and docs. + +2004-02-08 Erik de Castro Lopo + + * src/file_io.c + Make sure config.h is included before stdio.h to make sure large file + support is enabled on Linux (and Solaris). + + * tests/misc_test.c + Disable update_header test on Win32. This should work but doesn't and + I'm not sure why. + + * Make.bat Win32/Makefile.msvc + Updates. + +2004-01-07 Erik de Castro Lopo + + * src/common.h + Changed logindex, headindex and headend files of SF_PRIVATE from unsigned + int to int to prevent weird arithmetic bugs. + + * src/common.c src/aiff.c src/wav.c src/w64.c + Fixed compiler warnings resulting from above change. + +2004-01-06 Erik de Castro Lopo + + * src/common.c + Fixed a bug in header reader for some files with data after the sample data. + +2003-12-29 Erik de Castro Lopo + + * tests/lossy_comp_test.c tests/Makefile.am + Add tests for AIFF/IMA files. + +2003-12-26 Erik de Castro Lopo + + * src/macbinary3.c src/macos.c + Two new files required for handling SD2 files. + + * src/common.h + Add prototypes for functions in above two files. + + * src/Makefile.am + Hook new files into build system. + +2003-12-21 Erik de Castro Lopo + + * configure.ac + Add checks for mmap() and getpagesize() which might be used at some time + for faster file reads. + Add detection of MacOSX. + +2003-12-13 Erik de Castro Lopo + + * doc/FAQ.html + Minor mods to pkg-config section. + +2003-12-12 Erik de Castro Lopo + + * src/create_symbols_file.sh + Andre Pang (also known as Ozone) pointed out that on MacOSX, all non + static symbols are exported causing troubles when trying to link + libsndfile with another library which has any of the same symbols. + He fixed this by supplying the MacOSX linker with a file containing + all the public symbols so that only they would be exported and then + supplied a patch for libsndfile. + This wasn't quite ideal, because I would have to maintain two (3 if + you include Win32) separate files containing the exported symbols. + A better solution was to create this script which can generate a + Symbols file for Linux, MacoSX and any other OS that supports + minimising the number of exported symbols. + + * configure.ac src/Makefile.am + Hook the new script into the build process. + +2003-12-10 Erik de Castro Lopo + + * doc/index.html + Added comments about Steve Dekorte's SoundConverter scam. + +2003-12-07 Erik de Castro Lopo + + * src/file_io.c + Axel Roebel pointed out that on Mac OSX a pipe is not considered a fifo + (S_ISFIFO (st.st_mode) is false) but a socket (S_ISSOCK (st.st_mode) is + true). The test has therefore been changed to is S_ISREG and anything + which which does not return true for S_ISREG is considered a pipe. + +2003-11-25 Erik de Castro Lopo + + * tests/misc_test.c + Fix update_header_test to pass SDS. + + * src/sds.c + More minor fixes. + + * tests/floating_point_test.c + Add test for SDS files. + + * src/command.c + Add SDS to major_formats array. + +2003-11-24 Erik de Castro Lopo + + * tests/write_read_test.tpl tests/misc_test.c + Add tests for SDS files. + + * src/sds.c + Fix a bug in header update code. + +2003-11-23 Erik de Castro Lopo + + * src/sds.c + Get file write working. + + * src/paf.c + Fix a potential bug in paf24_seek(). + +2003-11-04 Erik de Castro Lopo + + * doc/FAQ.html + Add Q/A about u-law encoded WAV files. + + * Win32/*.h + Updated so it compiles on Win32. + +2003-11-03 Erik de Castro Lopo + + * examples/sndfile-convert.c + Add -alaw and -ulaw command line arguments. + + * configure.ac + Add library versioning comments. + Add arguments to AC_INIT. + +2003-10-28 Erik de Castro Lopo + + * src/file_io.c + Ross Bencina has contributed code to replace all of the (mostly broken) + Win32 POSIX emulation calls with calls the native Win32 file I/O API. + This code still needs testing but is likely to be a huge improvemnt + of support for Win32. Thanks Ross. + +2003-10-27 Erik de Castro Lopo + + * src/dwvw.c + Removed filedes field from the DWVW_PRIVATE struct. + + * src/file_io.c + Change psf_fopen() so it returns psf->error instead of the file descriptor. + Add new functions psf_set_stdio() and psf_set_file(). + + * src/sndfile.c + Change these to work with changed psf_fopen() return value. + Remove all uses of psf->filedes from sndfile, making it easier to slot native + Win32 API file handling functions. + + * src/test_file_io.c + Minor changes to make it compile with new file_io.c stuff. + +2003-10-26 Erik de Castro Lopo + + * src/gsm610.h + Rename a variable from true to true_flag. As Ross Bencina points out, + true is defined in the C99 header . + + * src/file_io.c + If fstat() fails, return SF_TRUE instead of -1 (Ross Bencina). + +2003-10-09 Erik de Castro Lopo + + * src/common.h + Increase the size of SF_BUFFER_LEN and SF_HEADER_LEN. + + * src/sndfile.c + Fix sf_read/write_raw which were dividing by psf->bytwidth and + psf->blockwidth which can both be zero. + + * examples/sndfile-info.c + Increase size of BUFFER_LEN. + +2003-09-21 Erik de Castro Lopo + + * configure.ac + Add checks for and ssize_t. + Other Win32/MinGW checks. + + * src/aiff.c src/au_g72x.c src/file_io.c src/gsm610.c src/interleave.c + src/paf.c src/sds.c src/svx.c src/voc.c src/w64.c src/wav.c src/xi.c + Fix compiler warnings. + +2003-09-20 Erik de Castro Lopo + + * tests/scale_clip_test.tpl + Add definition of M_PI if needed. + +2003-09-19 Erik de Castro Lopo + + * configure.ac + Detect if S_IRGRP is declared in . + + * src/file_io.c tests/*.tpl tests/*.c + More fixes for Win32/MSVC++ and MinGW. MinGW does have but that + file doesn't declare S_IRGRP. + +2003-10-18 Erik de Castro Lopo + + * src/config.h.in + Add comment stating that the sf_count_t typedef is determined when + libsndfile is being compiled. + + * tests/utils.tpl + Modified so that utils.c gets one copy of the GPL and not two. + + +2003-09-17 Erik de Castro Lopo + + * Win32/unistd.h src/sf_unistd.h + Move first file to the second. This will help for Win32/MSVC++ and MinGW. + + * Win32/Makefile.am src/Makefile.am + Changed in line with above. + + * Win32/Makefile.msvc + Removed "/I Win32" which is no longer required. + + * src/file_io.c src/test_file_io.c tests/*.tpl tests/*.c + If HAVE_UNISTD_H include else include . This should + work for Win32, MinGW and other fakes Unix-like OSes. + + * src/*.c + Removed #include from files which didn't need it. + +2003-09-16 Erik de Castro Lopo + + * libsndfile.spec.in + Apply fix from Andrew Schultz. + +2003-09-07 Erik de Castro Lopo + + * src/vox_adpcm.c + Only set psf->sf.samplerate if the existing value is invalid. + +2003-09-06 Erik de Castro Lopo + + * examples/sndfile-play.c + Started adding support for ALSA output. + +2003-09-04 Erik de Castro Lopo + + * src/sndfile.h.in + Removed from sndfile.h. + + * src/*.c examples/*.c tests/*.c tests/*.tpl + Added where needed. + +2003-09-02 Erik de Castro Lopo + + * src/common.h + Added ARRAY_LEN, SF_MAX and SF_MIN macros. + +2003-08-19 Erik de Castro Lopo + + * doc/index.html + Remove statements about alternative licensing arrangements. + +2003-08-17 Erik de Castro Lopo + + * MacOS MacOS9 Makefile.am configure.ac + Change directory name from MacOS to MacOS9 + + * MacOS9/MacOS9-readme.txt + Change name to make it really obvious, add text to top of file to make it + still more obvious again. + +2003-08-16 Erik de Castro Lopo + + * src/test_log_printf.c + Add tests for %u conversions. + + * src/common.c + Fix psf_log_printf() %u conversions. + +2003-08-15 Erik de Castro Lopo + + * src/aiff.c + Fixed a bug where opening a file with a non-trival header in SFM_RDWR mode + would over-write part of the header. Thanks to Axel Roebel for pointing + this out. Axel also provided a patch to fix this but I came up with a + neater and more general solution. + Return error when openning an AIFF file with data after the SSND chunk + (Thanks Axel Roebel). + + * tests/aiff_rw_test.c + Improvements to test program which will later allow it to be generalised to + test WAV, SVX and others as required. + +2003-08-14 Erik de Castro Lopo + + * tests/pipe_test.c + Add useek_pipe_rw_test() submitted by Russell Francis. + + * src/sndfile.c + In sf_open_fd(), check if input file descriptor is a pipe. + + * src/sndfile.[ch] + Fix typo in variable name do_not_close_descriptor. + +2003-08-13 Erik de Castro Lopo + + * src/test_log_printf.c + Improve the tests for %d and %s conversions. + + * src/common.c + Fixed a few problems in psf_log_printf() found using new tests. + +2003-08-06 Erik de Castro Lopo + + * configure.ac + Add -Wwrite-strings warning to CFLAGS if the compiler is GCC. Thanks to + Peter Miller (Aegis author) for suggesting this and supplying a patch. + + * src/*.c examples/*.c tests/*.c + Fix all compiler warnings arising from the above. + +2003-08-02 + + * tests/aiff_rw_test.c tests/Makefile.am + New test program to check for errors re-writing the headers of AIFC files + opened in mode SFM_RDWR. + +2003-07-21 Erik de Castro Lopo + + * examples/sndfile-play.c + Applied a patch from Tero Pelander to allow this program to run on systems + using devfs which used /dev/sound/dsp instead of /dev/dsp. + +2003-07-11 Erik de Castro Lopo + + * doc/new_file_type.HOWTO + Updated document. Still incomplete. + +2003-06-29 Erik de Castro Lopo + + * src/sndfile.c + Fix VALIDATE_SNDFILE_AND_ASSIGN_PSF which was returning an error rather + than saving it and returning zero. + +2003-06-25 Erik de Castro Lopo + + * src/file_io.c + Two fixes for Mac OS9. + Fix all casts from sf_count_t to ssize_t (not size_t). + +2003-06-22 Erik de Castro Lopo + + * src/wav.c + Fix for reading files with RIFF length of 8 and data length of 0. + +2003-06-14 Erik de Castro Lopo + + * src/*.c tests/*.c tests/*.tpl + Added comments to mark code for removal when make Lite version of + libsndfile. + +2003-06-09 Erik de Castro Lopo + + * examples/sndfile-convert.c + Add extra error checking for unrecognised arguments. + +2003-06-08 Erik de Castro Lopo + + * src/ima_adpcm.c + Started adding code to write IMA ADPCM encoded AIFF files. + + * src/test_log_printf.c src/Makefile.am + New file to test psf_log_printf() function and add hooks into build system. + + * src/common.c + Move psf_log_printf() function to top of the file and only compile the rest + of the file if if PSF_LOG_PRINTF_ONLY is not defined. + +2003-06-03 Erik de Castro Lopo + + * Win32/config.h Win32/sndfile.h + Updated with new config variables. + + * Win32/unistd.h src/file_io.c + Added implementation of S_ISFIFO macro which Win32 seems to lack and is + used in src/file_io.c. + + * tests/utils.tpl + Added #include to pull in Win32/unistd.h so it compiles for + Win32. + + * src/Makefile.msvc + Added src\test_file_io.exe build target and run this as the very first + test. + + * tests/win32_test.c + Add support for testing Cygwin32. + + * configure.ac + Detect POSIX fsync() and fdatasync() functions. + + * src/file_io.c + If compiling for Cygwin, call fsync() before calling fstat() to retrieve + file length. + + * tests/pcm_test.tpl + Add a test for lrintf() function. This was required to detect a really + broken lrint() and lrintf() on Cygwin. + + * tests/misc_test.c + Don't run permission test when compiling under Cygwin. + + * src/float_cast.h + Fix fallback macro for lrint() and lrintf() to cast to long instead of int + to match official function prototypes. + +2003-06-02 Erik de Castro Lopo + + * examples/sndfile-convert.c + Modifications to improve accuracy of conversions; use double data for + floating point and int for everything else. + + * src/ima_apdcm.c + Completed work on decoding IMA ADPCM encoded AIFF files. Still need to + get encoding working. + +2003-05-28 Erik de Castro Lopo + + * src/aiff.c src/ima_adpcm.c + Start working on getting IMA ADPCM encoded AIFF files working. + +2003-05-27 Erik de Castro Lopo + + * configure.ac + Fixed the touch command for when the autogen program is not found (Matt + Flax). + + * src/ulaw.c src/alaw.c + Made these pipe-able. + +2003-05-24 Erik de Castro Lopo + + * src/paf.c src/ircam.c + Fixed writing to pipe. + + * src/wav.c src/aiff.c src/nist.c src/mat*.c src/svx.c src/w64.c + Return SFE_NO_PIPE_WRITE if an attempt is made to write to a pipe. + +2003-05-23 Erik de Castro Lopo + + * examples/sndfile-info.c + Modified to detect unknown file lengths. + + * src/mat4.c + Fix reading from a pipe. + +2003-05-22 Erik de Castro Lopo + + * tests/pipe_test.c + Add more file types to tests. + + * src/mat4.c + Removed explicit setting of psf->sf.seekable to SF_TRUE. + + * tests/utils.tpl + Add macro for generating and check data in the stdio and pipe tests. + + * tests/stdout_test.c tests/stdin_test.c + Use the above macro to generate known data on output and check data on + input. + + * src/voc.c src/htk.c common.h sndfile.c + Disallow reading/writing VOC and HTK files from/to pipes be returning new + error values. + + * src/w64.c + Fixes to allow reading from a pipe. + +2003-05-21 Erik de Castro Lopo + + * configure.ac src/sndfile.h.in + When the configure script determines the sizeof (sf_count_t), also set the + value of SF_COUNT_MAX in sndfile.h. + + * configure.ac + Remove -pedantic flag from default GCC compiler flags. + + * tests/pipe_test.c + Add a pipe_read_test() before doing pipe_write_test(). + + * tests/scale_clip_test.c + Add test to make sure non-normalized values also clip in the right way. + +2003-05-18 Erik de Castro Lopo + + * configure.ac + Add test to detect processor clipping capabilities. + + * tests/stdin_test.c tests/stdout_test.c + Fix a pair of compiler warnings. + + * src/common.h + Add new pipeoffset field to SF_PRIVATE. This will contain the current file + offset when operating on a pipe. + + * src/common.c + Removed direct calls to psf_fread()/psf_fseek()/psf_fgets() etc from + psf_binheader_readf and redirect them to new buffered versions + header_read(), header_seek() and header_gets(). + Add "G" format specifier to emulate fgets() functionality with buffering. + This will allow reading some file types from pipes. + + * src/file_io.c + When the file descriptor is a pipe, manintain psf->pipeoffset. + + * src/pvf.c + Change use of psf_fgets() to psf_binheader_readf() as required but changes to header re + + * src/au.c + Fix reading from a pipe. + +2003-05-17 Erik de Castro Lopo + + * src/pcm.c + Add clipping versions of the f2XXX_array() functions to allow option of + clipping data that would otherwise overflow. + + * tests/scale_clip_test.tpl tests/scale_clip_test.def + New files test that clipping option does actually work. + +2003-05-14 Erik de Castro Lopo + + * doc/index.html + Fixed a typo ("OS(" instead of "OS9"). + +2003-05-13 Erik de Castro Lopo + + * tests/open_fail_test.c + Include to prevent warning message of missing declaration of + memset(). + +2003-05-12 Erik de Castro Lopo + + * src/common.h + Add new "add_clipping" field to SF_PRIVATE. + + * src/sndfile.h.in src/sndfile.c + Add command SFC_SET_CLIPPING which sets/resets add_clipping field. + +2003-05-11 Erik de Castro Lopo + + * doc/api.html + Add docs for sf_set_string() and sf_get_string(). + + * src/common.h src/sndfile.c + Add new SFE_STR_BAD_STRING error. + + * tests/stdin_test.c tests/stdout_test.c + Removed all non-error print statements. + + * tests/stdio_test.c tests/pipe_test.c tests/Makefile.am + Add print statements removed from two files above. + +2003-05-10 Erik de Castro Lopo + + * libsndfile.spec.in + Fixed a coulpe of minor errors discovered by someone calling themselves + Agent Smith. + + * src/common.c src/common.h src/file_io.h + Added is_pipe field to SF_PRIVATE and declaration of psf_is_pipe() + function. (Axel Roebel) + + * src/sndfile.c + Fixed determination of whether the file is a pipe. (Axel Roebel) + + * src/paf.c + Force paf24 to start with undefined mode. (Axel Roebel) + + * tests/pipe_test.c + Mods to make this test work and actually do the test on RAW files. (Axel + Roebel). + +2003-05-05 Erik de Castro Lopo + + * src/sndfile.c + Fixed a potential bug where psf->sf.seekable was being set to FALSE when + operating on stdin or stdout but then the default initialiser was reseting + it to TRUE. Thanks to Axel Roebel. + + * src/aiff.c + Fixed a bug in the header parser where it was not handling an odd length + COMM chunk correctly. Thanks to Axel Roebel. + + * src/test_file_io.c + Add more tests. + + * tests/win32_test.c + New file for showing the bugs in the Win32 implementation of the POSIX API. + It also runs on Linux for sanity checking. + + * tests/Makefile.am Win32/Makefile.msvc + Hook the new test program into the build system. + +2003-05-04 Erik de Castro Lopo + + * src/test_file_io.c + New test program to test operation of functions defined in file_io.c. This + should make supporting win32 significantly easier. + + * src/Makefile.am + Hook new test program into the build system. + + * src/file_io.c + Add compile/run time check that sizeof statbuf.st_size and sf_count_t are + the same. + + * src/common.h src/sndfile.c + Added new error code and error message for new check. + + * tests/benchmark.tpl + Fix to use frames instead of samples in SF_INFO. + +2003-05-03 Erik de Castro Lopo + + * src/file_io.c + More stuffing about working around PLAIN OLD-FASHIONED **BUGS** in Win32. + + * examples/sndfile-info.c + Applied patch from Conrad Parker to add "--help" and "-h" options as + well as an improved usage message. + +2003-05-02 Erik de Castro Lopo + + * src/au.c + Added embedded file support. + + * tests/multi_file_test.c + Added tests for embedded AU files. + Added verbose testing mode. + + * src/common.h src/sndfile.c + Added an embedded AU specific error code and message. + + * src/wav.c + Added patch from Conrad Parker which filled in a little more information + about ACIDized WAV files. + +2003-04-30 Erik de Castro Lopo + + * src/file_io.c + Fixed Win32 version of psf_fseek() which was calling psf_get_filelen() + which was in turn calling psf_fseek() which in the end blew the stack. + Now of course this would have been easy to find on Linux, but this blow + up was happening in kernel32.dll and the fscking MSVC++ debugger couldn't + figure out what call caused this (it couldn't even tell me the stack had + overflowed) and was absolutley useless for this debugging exercise. + On top of that, the reason I got into this mess was that windoze doesn't + have a working fstat() function which can return file lengths > 2 Gig. It + HAS a fscking _fstati64() but the file length value is only updated AFTER + the bloody file is closed. That makes it completely useless. + How the hell do people stand working on this crap excuse of an OS? + +2003-04-29 Erik de Castro Lopo + + * Win32/unistd.h src/file_io.c + Moved definitions of S_IGRP etc from file_io.c to unistd.h so that these + can be used in the test programs. + + * Win32/libsndfile.def + Added sf_open_fd. + + * Win32/sndfile.h + Updated to match src/sndfile.h.in. + + * Win32/Makefile.msvc + Added dither.c and htk.c to libsndfile.dll target. + +2003-04-28 Erik de Castro Lopo + + * src/file_io.c + First attempt at getting the Win32 versions of the these functions working. + They still need to be tested. + +2003-04-27 Erik de Castro Lopo + + * src/strings.c + Found and fixed a bug which was causing psf_store_string() to fail on + Motorola 68k processors. Many thanks fo Joshua Haberman (Debian maintainer + of libsndfile) for compiling and running debug code to help me debug the + problem. + +2003-04-26 Erik de Castro Lopo + + * src/sndfile.c src/file_io.c src/wav.c src/aiff.c + Much hacking to get reading and writing of embedded files working (ie sound + files at a non-zero files offset). + + * doc/embedded_files.html + First pass atempt at documenting reading/writing embedded files. + +2003-04-21 Erik de Castro Lopo + + * doc/FAQ.html + Updated answer to "Why doesn't libsndfile do interleaving/de-interleaving?" + +2003-04-19 Erik de Castro Lopo + + * src/wav.c src/aiff.c + Fix retrieving and storing of string data from files. Need to be careful + about using psf->buffer for strings. + +2003-04-18 Erik de Castro Lopo + + * src/file_io.c + Fix psf_fseek() for seeks withing embedded files. + +2003-04-15 Erik de Castro Lopo + + * src/sndfile.h.in + Changed the definition of SNDFILE slightly to produce warnings when it isn't + used correctly. This should have zero affect in code which uses the SNDFILE + type correctly. + + * src/sndfile.c + Fixed a few compiler warnings cause by the changes to the SNDFILE type. + +2003-04-12 Erik de Castro Lopo + + * doc/FAQ.html + Added question and answer to the question "How about adding the ability + to write/read sound files to/from memory buffers?". + +2003-04-08 Erik de Castro Lopo + + * tests/write_read_test.tpl + Removed un-needed enums declaring TRUE and FALSE and replaced usage of + these with SF_TRUE and SF_FALSE. + + * tests/multi_file_test.c + New test program to test sf_open_fd() on files containing data other than + a single sound file. + +2003-04-06 Erik de Castro Lopo + + * src/file_io.c + When creating files, set the readable by others flag. This still allows + further restrictions to be enforced by use of the user's umask. Fix + suggested by Eric Lyon. + +2003-04-05 Erik de Castro Lopo + + * src/sndfile.h.in src/sndfile.c + Changed sf_open_fd(). Dropped offset parameter and added a close_desc + parameter. If close desc is TRUE, the file descritpor passed into the + library will be closed when sf_close() is called. + + * tests/utils.tpl + Modified call to sf_open_fd() to set close_desc parameter to SF_TRUE. + +2003-04-04 Erik de Castro Lopo + + * tests/write_read_test.tpl + Add a string (using sf_set_string() function) before and after data section + of all files. This will make sure that if string data can be added, it + doesn't overwrite real audio data. + +2003-04-02 Erik de Castro Lopo + + * src/sndfile.c + Started work on supporting a non-zero offset parameter for sf_open_fd (). + + * src/.c + Removed many uses of psf_fseek (SEEK_END) which to allow for future use of + sf_open_fd() with non-zero offset. + Associated refactoring. + + * src/aiff.c + Implemented functionality required to get sf_get_string() and + sf_set_string() working for AIFF files. + +2003-04-01 Erik de Castro Lopo + + * tests/utils.tpl + Modified test_open_file_or_die() to alternately use sf_open() and + sf_open_fd(). + + * src/svx.c + Fixed a bug which occurred when openning an existing file for read/write + using sf_open_fd(). In this case, the existing NAME chunk needs to be + read into psf->filename. + Fixed printing of sf_count_t types to logbuffer. + +2003-03-31 Erik de Castro Lopo + + * src/sndfile.h.in + Added prototype for new function sf_open_fd(). + + * src/sndfile.c + Moved most of the code in sf_open() to a new function psf_open_file(). + Created new function sf_open_fd() which also uses psf_open_file() but + does not currently support the offset parameter. + + * doc/api.html + Document sf_open_fd(). + +2003-03-09 Erik de Castro Lopo + + * src/sndfile.c + Fixed a memory leak reported by Evgeny Karpov. Memory leak only occurred + when an attempt was made to read and the open() call fails. + +2003-03-08 Erik de Castro Lopo + + * tests/open_fail_test.c + New test program to check for memory leaks when sf_open fails on a valid + file. Currently this must be run manually under valgrid. + + * tests/Makefile.am + Hook new test program into build. + +2003-03-03 Erik de Castro Lopo + + * Octave/sndfile_save.m Octave/sndfile_play.m + Added a -mat-binary option to the octave save command to force the output + to binary mode even if the user has set ascii data as the default. Found + by Christopher Moore. + +2003-02-27 Erik de Castro Lopo + + * doc/dither.html + New file which will document the interface which allows the addition of + audio dither when sample word sizes are being reduced. + + * src/dither.c + More work. + +2003-02-26 Erik de Castro Lopo + + * tests/misc_test.c + In update_header_test(), make HTK files a special case. + + * doc/index.html + Added HTK to the feature matrix. + +2003-02-25 Erik de Castro Lopo + + * src/htk.c + New file for reading/writing HMM Tool Kit files. + + * src/sndfile.h.in src/sndfile.c src/command.c src/Makefile.am + Hook in htk.c + + * tests/write_read_test.tpl tests/misc_test.c tests/Makefile.am + Add tests for HTK files. + +2003-02-22 Erik de Castro Lopo + + * src/wav.c + Fixed a bug where the LIST chunk length was being written incorrectly. + + * tests/string_test.c + Added call to check_log_buffer(). + Minor cleanups. + +2003-02-10 Erik de Castro Lopo + + * src/wav_w64.h + Applied patch from Antoine Mathys to add extra WAV format definitions and + a G72x_ADPCM_WAV_FMT struct definition. + + * src/wav_w64.c + Applied patch from Antoine Mathys which converts wav_w64_format_str() from + one huge inefficient switch statement to a binary search. + + * tests/string_test.c + Dump log buffer if tests fail. + +2003-02-07 Erik de Castro Lopo + + * tests/string_test.c + David Viens supplied some modifications to this file which showed up a bug + when using sf_set_string() and the sf_writef_float() functions. + + * src/sndfile.c + Fixed the above bug. + +2003-02-06 Erik de Castro Lopo + + * doc/FAQ.html + Added Q and A on how to detect libsndfile in configure.in (at the suggestion + of Davy Durham). + +2003-02-05 Erik de Castro Lopo + + * src/sndfile.h.in + Add enums and typedefs for dither. + Deprecate SFC_SET_ADD_DITHER_ON_WRITE and SFC_SET_ADD_DITHER_ON_READ, to be + replaced with SFC_SET_DITHER_ON_WRITE and SFC_SET_DITHER_ON_READ which will + allow different dither algorithms to be enabled. + Added SFC_GET_DITHER_INFO_COUNT and SFC_GET_DITHER_INFO. + + * src/sndfile.h.in src/Version_script.in Win32/libsndfile.def. + Added public sf_dither_*() functions. + + * src/sndfile.c + Implement commands above. + + * src/dither.c + More work. Framework and external hooks into dither algorithms complete. + +2003-02-03 Erik de Castro Lopo + + * doc/version-1.html libsndfile_version_convert.py + Remove redundant files. + + * doc/index.html doc/api.html + Remove links to version-1.html. + + * src/dither.c + New file to allow the addition of audio dither on input and output. + + * src/common.h + Add prototype for dither_init() function. + + * Makefile.am doc/Makefile.am + Changes for added and removed files. + +2003-02-02 Erik de Castro Lopo + + * Win32/Makefile.msvc + Changes to force example binaries to be placed in the top level directory + instead of the examples/ directory. + Add src/strings.c and src/xi.c to the build. + Add string_test to build and to tests on WAV files. + + * doc/index.html + Added XI to support matrix. + +2003-01-27 Erik de Castro Lopo + + * src/sndfile.h.in + Added prototypes for sf_get_string() and sf_set_string() and SF_STR_* + enum values. + + * src/sndfile.c + Added public interface to sf_get_string() and sf_set_string(). + + * src/wav.c + Added code for setting and getting strings in WAV files. + + * tests/string_test.c + New test program for sf_get_string() and sf_set_string() functionality. + + * tests/Makefile.am + Hook new test program into build and test framework. + +2003-01-26 Erik de Castro Lopo + + * src/common.h + Added fields to SF_PRIVATE for string data needed to implement + sf_get_string() and sf_set_string(). + + * src/strings.c + New file for storing and retrieving strings to/from files. + + * src/Makefile.am + Added strings.c to build. + +2003-01-25 Erik de Castro Lopo + + * src/xi.c + Read seems to be working so looking at write. + + * src/sndfile.h.in + Added SF_FORMAT_XI, SF_FORMAT_DPCM_8 and SF_FORMAT_DPCM_16 enum values. + + * tests/floating_point_test.c tests/lossy_comp_test.c tests/Makefile.am + Added test for 8 and 16 bit XI format files. + +2003-01-24 Erik de Castro Lopo + + * doc/index.html + Added a non-lawyer readable summary of the licensing provisions as + suggested by Steve Dekorte. + +2003-01-23 Erik de Castro Lopo + + * src/wav.c + Fixed a compiler warning found by Alexander Lerch. + +2003-01-18 Erik de Castro Lopo + + * configure.ac + Fixed the multiple linking of libm. + +2003-01-17 Erik de Castro Lopo + + * Win32/Makefile.mcvs + Added comments on the correct way to set up the MSVCDir environment + variable. + + * doc/win32.html + Add on how to set up the MSVCDir environment variable. + +2003-01-15 Erik de Castro Lopo + + * examples/sndfile-play.c examples/sndfile-info.c + When run on Win32 without any command line parameters print a message and + then sleep for 5 seconds. This means the when somebody double clicks on + these programs in explorer the user will actually see the message. + +2003-01-14 Erik de Castro Lopo + + * tests/misc_test.c + Bypass permission test if running as root because root is allowed to open + a readonly file for write. + +2003-01-08 Erik de Castro Lopo + + * Win32/Makefile.msvc + Added pvf.c and xi.c source files to project. + + * src/sndfile.h + Updated for PVF files. + +2003-01-07 Erik de Castro Lopo + + * src/sndfile.c + Modified validate_sfinfo() to force samplerate, channels and sections + to be >= 1. + In format_from_extension() replaced calls to does_extension_match() + with strcmp(). + + * src/xi.c + More work. + +2003-01-06 Erik de Castro Lopo + + * doc/Makefile.am + Added octave.html which had been left out. Found by Jan Weil. + +2003-01-05 Erik de Castro Lopo + + * src/pvf.c src/common.h src/sndfile.c + Fixed error handling for PVF files. + + * src/xi.c + New file for handling Fasttracker 2 Extended Instrument files. Not working + yet and included when configured with --enable-experimental. + + * src/sndfile.c src/common.h + Hooked in new file xi.c. + +2002-12-30 Erik de Castro Lopo + + * src/rx2.c + Added a patch from Marek Peteraj which sheds a little more light on the + slices within an RX2 file. Still need to find out data encoding. + +2002-12-20 Erik de Castro Lopo + + * src/wav.c + Started work on decoding 'acid' and 'strc' chunks. + +2002-12-14 Erik de Castro Lopo + + * tests/peak_check_test.c + Minor cleanup. + +2002-12-12 Erik de Castro Lopo + + * tests/write_read_test.tpl + Added check to make sure no error was generated when an attempt was made to + read past the end of the file. + +2002-12-11 Erik de Castro Lopo + + * doc/lists.html + Added "mailto" links for all three lists. + + * src/pvf.c + New file for Portable Voice Format files. + + * src/sndfile.h.in src/sndfile.c src/common.h src/command.c src/Makefile.am + Added hooks for SF_FORMAT_PVF format files. + + * tests/write_read_test.tpl tests/std*.c + Add tests for SF_FORMAT_PVF. + + * doc/index.html + Add PVF to the compatibility matrix. + + * src/pcm.c src/alaw.c src/ulaw.c src/float32.c src/double64.c + Previously, attempts to read beyond the end of a file would set psf->error + to SFE_SHORT_ERROR. This behaviour diverged from the behaviour of the POSIX + read() call but has now been fixed. + Attempts to read beyond the end of the file will return a short read count + but will not longer set any error. + +2002-12-09 Erik de Castro Lopo + + * src/sndfile.c + Add more sanity checking when opening a RAW file for read. When format is + not RAW, zero out all members of the SF_INFO struct. + + * tests/raw_test.c + Add bad_raw_test() to check for above problem. + + * tests/stdin_test.c examples/sndfile-info.c + Set the format field of the SF_INFO struct to zero before calling + sf_open(). + + * doc/api.html + Add information about the need to set the format field of the SF_INFO struct + to zero when opening non-RAW files for read. + + * configure.ac + Removed use of conversion script on Solaris. Not all Solaris versions + support it. + + * doc/lists.html + New file containg details of the mailing lists. + + * doc/index.html + Add a link to the above new file. + +2002-12-04 Erik de Castro Lopo + + * tests/dft_cmp.c + Fixed a SIGFPE on Alpha caused by a log10 (0.0). Thanks to Joshua Haberman + for providing the gdb traceback. + +2002-11-28 Erik de Castro Lopo + + * src/wav.c + Added more capabilities to 'smpl' chunk parser. + + * src/sndfile.c + Fixed some (not all) possible problems found with Flawfinder. + +2002-11-24 Erik de Castro Lopo + + * src/sndfile.c + Fixed a bug in sf_seek(). This bug could only occur when an attempt was + made to read beyond the end and then sf_seek() was called with a whence + parameter of SEEK_CUR. + + * src/file_io.c + Win32's _fstati64() does not work, it returns BS. Re-implemented + psf_get_filelen() in terms of psf_fseek(). + + * tests/write_read_test.tpl + Add a test to detect above bug. + + * src/float_cast.h + Modification to prevent compiler warnings on Mac OS X. + + * src/file_io.c + Fixes for windows (what a f**ked OS). + +2002-11-08 Erik de Castro Lopo + + * configure.ac + Disable use of native lrint()/lrintf() on Mac OSX. These functions exist on + Mac OSX 10.2 but not on 10.1. Forcing the use of the versions in + src/float_cast.h means that a library compiled on 10.2 will still work on + 10.1. + +2002-11-06 Erik de Castro Lopo + + * configure.in configure.ac + Renamed configure.in to configure.ac as expected by later versions of + autoconf. + Slight hacking of configure.ac to work with version 2.54 of autoconf. + Changed to using -dumpversion instead of --version for determining GCC + version numer as suggested by Anand Kumria. + + * src/G72x/Makefile.am + Slight hacking required for operation with automake 1.6.3. + +2002-11-05 Erik de Castro Lopo + + * src/common.c + In psf_binheader_readf() changed type parameter type "b" type from size_t + to int to prevent errors on IA64 CPU where sizeof (size_t) != sizeof (int). + Thanks to Enrique Robledo Arnuncio for debugging this. + +2002-11-04 Erik de Castro Lopo + + * test/command_test.tpl + Changed test value so test would pass on Solaris. + + * src/Version_script.in + Modified version numbering so that later versions of 1.0.X can replace + earlier versions without recompilation. + + * src/vox_adpcm.c + Fixed bug causing short reads. + +2002-11-03 Erik de Castro Lopo + + * test/floating_point_test.c + Code cleanup using functions from util.c. + Add test for IEEE replacement floats and doubles. + +2002-11-01 Erik de Castro Lopo + + * src/wav.c + Fixed a possible divide by zero error when read the 'smpl' chunk. Thanks to + Serg Repalov for the example file. + + * tests/pcm_test.tpl + Used sf_command (SFC_TEST_IEEE_FLOAT_REPLACE) to test IEEE replacement code. + Clean up pcm_double_test(). + + * src/float32.c src/double64.c + Force use of IEEE replacement code using psf->ieee_replace is TRUE, + Print message to log_buffer as well. + Rename all broken_read_* and broken_write* functions to replace_read_* and + replace_write_*. + + * tests/util.tpl + Added string_in_log_buffer(). + + * tests/pcm_test.tpl + Use string_in_log_buffer() to ensure that IEEE replacement code has been + used. + + * configure.in + Removed --enable-force-broken-float option. IEEE replacement code is now + always tested. + +2002-10-31 Erik de Castro Lopo + + * src/double64.c + Implement code for read/writing IEEE doubles on platforms where the native + double format is not IEEE. + + * src/float32.c src/common.h + Remove float32_read() and float32_write(). Replace with float32_le_read(), + float32_be_read(), float32_le_write() and float32_be_write() to match stuff + in src/double64.c. + + * src/common.c + Fix all usage of float32_write(). + + * src/sndfile.h.in + Added SFC_TEST_IEEE_FLOAT_REPLACE command (testing only). + + * src/common.h + Added SF_PRIVATE field ieee_replace. + + * src/sndfile.c + In sf_command() set/reset psf->ieee_replace. + +2002-10-26 Erik de Castro Lopo + + * tests/pcm_test.tpl + Fixed a problem when testing with --enable-force-broken-float. The test was + generating a value of negative zero and the broken float code is not able + to write negative zero. Removing the negative zero fixed the test. + +2002-10-25 Erik de Castro Lopo + + * src/file_io.c + Added fix for Cygwin (suggested by Maros Michalik). + +2002-10-23 Erik de Castro Lopo + + * src/file_io.c + Improved error detection and handling. + + * src/file_io.c src/common.h + Removed functions psf_ferror() and psf_clearerr() which were redundant + after above improvements. + + * src/aiff.c src/svx.c src/w64.c src/wav.c + Removed all use of psf_ferror() and psf_clearerr(). + + * src/sndfile.c + Removed #include of , , and which + are no longer needed. + + * tests/misc_test.c + Added test to make sure the correct error message is returned with an + existing read-only file is openned for write. + +2002-10-21 Erik de Castro Lopo + + * doc/index.html doc/api.html + Updated for OKI Dialogic ADPCM files. + + * src/command.c + Added VOX ADPCM to sub_fomats. + +2002-10-20 Erik de Castro Lopo + + * src/vox_adpcm.c src/Makefile.am + New file for handling OKI Dialogic ADPCM files. + + * src/sndfile.h + Add new subtype SF_FORMAT_VOX_ADPCM. + + * src/sndfile.c + Renamed function is_au_snd_file () to format_from_extenstion () and expanded + its functionality to detect headerless VOX files. + + * src/raw.c + Added hooks for SF_FORMAT_VOX_ADPCM. + + * examples/sndfile-info.c + Print out file duration (suggested by Conrad Parker). + + * libsndfile.spec.in + Force installation of sndfile.pc file (found by John Thompson). + + * tests/Makefile.am tests/lossy_comp_test.c tests/floating_point_test.c + Add tests for SF_FORMAT_VOX_ADPCM. + +2002-10-18 Erik de Castro Lopo + + * tests/misc_test.c + Add test which attempts to write to /dev/full (on Linux anyway) to check + for correct handling of writing to a full filesystem. + + * src/sndfile.c + Return correct error message if the header cannot be written because the + filesystem is full. + + * tests/util.tpl + Corrected printing of file mode in error reporting. + + * src/mat5.c + Fixed a bug where a MAT5 file written by libsndfile could not be opened by + Octave 2.1.36. + +2002-10-13 Erik de Castro Lopo + + * src/common.h src/file_io.c + All low level file I/O have been modified to be better able to report + system errors resulting from calling system level open/read/write etc. + + * src/*.c + Updated for compatibility with above changes. + + * examples/cooledit-fixer.c + New example program which fixes badly broken file created by Syntrillium's + Cooledit which are marked as containing PCM samples but actually contain + floating point data. + + * examples/Makefile.am + Hooked cooledit-fixer into the build system. + +2002-10-10 Erik de Castro Lopo + + * doc/command.html + Document SFC_GET_FORMAT_INFO. + +2002-10-09 Erik de Castro Lopo + + * examples/wav32_aiff24.c examples/sndfile2oct.c examples/sfhexdump.c + examples/sfdump.c + Removed these files because they weren't interesting. + + * examples/sfconvert.c examples/sndfile-convert.c + Renamed the first to the latter. + + * examples/Makefile.am + Added sndfile-convert to the bin_PROGRAMS, so it is installed when the lib + is installed. + Removed old programs wav32_aiff24 and sndfile2oct. + + * man/sndfile-convert.1 + New man page. + + * examples/sndfile-convert.c + Added some gloss now that sndfile-convert.c is an installed program. + + * src/sndfile.h.in src/sndfile.c src/common.h src/command.h + Added command SFC_GET_FORMAT_INFO. + + * tests/command_test.c + Added tests form SFC_GET_FORMAT_INFO. + +2002-10-08 Erik de Castro Lopo + + * src/sndfile.c + In sf_format_check() return error if samplerate < 0. + +2002-10-07 Erik de Castro Lopo + + * src/aiff.c + Fixed bug in handling of COMM chunks with a 4 byte encoding byte but no + encoding string. + +2002-10-06 Erik de Castro Lopo + + * src/sndfile.c + Fixed repeated word in an error message. + +2002-10-05 Erik de Castro Lopo + + * doc/index.html + Improved advertising in Features section. + +2002-10-04 Erik de Castro Lopo + + * src/wav.c + Added decoding of 'labl' chunks within 'LIST' chunks. + + * src/common.h + Added (experimental only) SF_FORMAT_OGG and SF_FORMAT_VORBIS and definition + of ogg_open(). This is nowhere near working yet. + + * src/sndfile.c + Added detection of 'OggS' file marker and added call to ogg_open() to + switch statement. + + * src/ogg.c + New file. Very early start of Ogg Vorbis support. + + * src/wav.c + Added handling of brain-damaged and broken Cooledit "32 bit 24.0 float + type 1" files. These files are marked as being 24 bit WAVE_FORMAT_PCM with + a block alignment of 4 times the numbers of channels but are in fact 32 bit + floating point. + +2002-10-02 Erik de Castro Lopo + + * configure.in + Modified option --enable-experimental to set ENABLE_EXPERIMENTAL_CODE in + config.h to either 0 or 1. + + * src/sndfile.c + Modify sf_command (SFC_GET_LIB_VERSION) to append "-exp" to the version + string if experimental code has been enabled. + +2002-10-01 Erik de Castro Lopo + + * src/Makefile.am + Added -lm to libsndfile_la_LIBADD. This means that -lm is not longer needed + in the link line when linking something to libsndfile. + + * tests/Makefile.am examples/Makefile.am + Removed -lm from all link lines. + + * sndfile.pc.in + Removed -lm from Libs line. + +2002-09-24 Erik de Castro Lopo + + * src/file_io.c + Removed all perror() calls. + + * src/nist.c + Removed calls to exit() function. + Added check to detect NIST files dammaged from Unix CR -> Win32 CRLF + conversion process. + +2002-09-24 Erik de Castro Lopo + + * src/sndfile.h.in src/sndfile.c + New function sf_strerror() which will eventually replace functions + sf_perror() and sf_error_str(). + Function sf_error_number() has also been changed, but this was documented + as being for testing only. + + * doc/api.html + Documented above changes. + + * tests/*.c examples/*.c + Changed to new error functions. + +2002-09-22 Erik de Castro Lopo + + * configure.in + Detect GCC version, and print a warning message about writeable strings + it GCC major version number is less than 3. + +2002-09-21 Erik de Castro Lopo + + * src/sndfile.h.in doc/api.html + Documentation fixes. + +2002-09-19 Erik de Castro Lopo + + * src/Version_script.in src/Makefile.am configure.in + Use the version script to prevent the exporting of all non public symbols. + This currently only works with Linux. Will test on Solaris as well. + + * src/float_cast.h + Added #ifndef to prevent the #warning directives killing the SGI MIPSpro + compiler. + + * src/au_g72x.c src/double64.c src/float32.c src/gsm610.c src/ima_adpcm.c + src/ms_adpcm.c + Fix benign compiler warnings arising from previously added compiler + flags. + +2002-09-18 Erik de Castro Lopo + + * src/sndfile.c + Fixed a bug in sf_error_str() where errnum was used as the index instead + of k. Found by Tim Hockin. + + * examples/sndfile-play.c + Fixed a compiler warning resulting from a variable shadowing a previously + defined local. + +2002-09-17 Erik de Castro Lopo + + * src/sndfile.h.in src/sndfile.c + Added command SFC_SET_RAW_START_OFFSET. + + * doc/command.html + Document SFC_SET_RAW_START_OFFSET. + + * tests/raw_test.c tests/Makefile.am + Add new file for for testing SF_FORMAT_RAW specific functionality. + + * tests/dwvw_test.c + Updates. + +2002-09-16 Erik de Castro Lopo + + * src/wav.c + Modified reading of 'smpl' chunk to take account of the sampler data field. + + * tests/utils.tpl tests/utils.h + Added function print_test_name(). + + * tests/misc_test.c tests/write_read_test.tpl tests/lossy_comp_test.c + tests/pcm_test.tpl tests/command_test.tpl tests/floating_point_test.c + Convert to use function print_test_name(). + +2002-09-15 Erik de Castro Lopo + + * doc/octave.html + Added a link to some other Octave scripts for reading and writing sound + files. + + * src/paf.c + Change type of dummy data field to int. This should fix a benign compiler + warning on some CPUs. + Removed superfluous casts resulting from the above change. + + * src/rx2.c + More hacking. + +2002-09-14 Erik de Castro Lopo + + * src/mat5.c src/common.c + Changed usage of snprintf() to LSF_SNPRINTF(). + + * Win32/Makefile.msvc + Updated to include new files and add new tests. + + * Win32/config.h Win32/sndfile.h + Updated. + + * doc/api.html + Added note about the possibility of "missing" features actually being + implemented as an sf_command(). + +2002-09-13 Erik de Castro Lopo + + * tests/misc_test.c + Added previously missing update_header_test and zero_data_tests for PAF, + MAT4 and MAT5 formats. + + * src/paf.c src/mat4.c src/mat5.c + Fixed bugs uncovered by new tests above. + + * src/mat5.c + Generalised parsing of name fields of MAT5 files. + + * src/mat5.c src/sndfile.c + Added support for unsigned 8 bit PCM MAT5 files. + + * tests/write_read_test.tpl + Added test for unsigned 8 bit PCM MAT5 files. + + * doc/index.html + Added unsigned 8 bit PCM MAT5 to capabilities matrix. + +2002-09-12 Erik de Castro Lopo + + * test/update_header_test.c tests/misc_test.c + Renamed update_header_test.c to misc_test.c. + Added zero_data_test() to check for case where file is opened for write and + closed immediately. The resulting file can be left in a state where + libsndfile cannot open it. Problem reported by Werner Schweer, the author + of Muse. + + * src/aiff.c + Removed superfluous cast. + + * src/wav.c src/svx.c + Fixed case of file generated with no data. + Removed superfluous cast. + + * src/sndfile.c + Fixed error on IA64 platform caused by incorrect termination of + SndfileErrors struct array. This problem was found in the Debian buildd + logs (http://buildd.debian.org/). + + * configure.in + Added Octave directory. + + * Octave/Makefile.ma + New Makfile.am for Octave directory. + + * Octave/sndfile_load.m Octave/sndfile_save.m Octave/sndfile_play.m + New files for working with Octave. + + * doc/octave.html + Document explaining the use of the above three Octave scripts. + +2002-09-10 Erik de Castro Lopo + + * src/sndfile.c + Fixed bug in RDWR mode. + +2002-09-09 Erik de Castro Lopo + + * src/common.c + Fixed psf_get_date_str() for systems which don't have gmtime_r() or + gmtime(). + + * src/file_io.c + Added #include for Win32. Reported by Koen Tanghe. + +2002-09-08 Erik de Castro Lopo + + * src/common.c + Added 'S' format specifier for psf_binheader_writef() which writes a C + string, including single null terminator to the header. + Added 'j' format specifier to allow jumping forwards or backwards in the + header. + Added function psf_get_date_str(). + + * src/mat5.c + Complete read and write support. + + * doc/index.html + Added entries for MAT4 and MAT5 in capabilities matrix. + +2002-09-06 Erik de Castro Lopo + + * src/mat4.c + Completed read and write support. + + * src/common.h src/sndfile.c + Added MAT4 and MAT5 specific error messages. + + * tests/write_read_test.tpl tests/Makefile.am + Added tests for MAT4 and MAT5 files. + + * tests/stdio_test.c tests/stdout_test.c tests/stdin_test.c + Added tests for MAT4 and MAT5 files. + +2002-09-05 Erik de Castro Lopo + + * src/command.c + Added elements for SF_FORMAT_MAT4 and SF_FORMAT_MAT5 to major_formats + array. + + * examples/sfconvert.c + Added mat4 and mat5 output targets. + +2002-09-04 Erik de Castro Lopo + + * src/sndfile.c + Added check to prevent errors openning read only formats for read/write. + + * src/interleave.c + New file for interleaving non-interleaved data. Non-interleaved data is + only supported on read. + + * src/Makefile.am + Added src/interleave.c to build. + +2002-09-03 Erik de Castro Lopo + + * src/double64.c src/common.h + Added double64_be_read(), double64_le_read(), double64_be_write() and + double64_le_write() which replace double64_read() and double64_write(). + + * src/common.c + Cleanup of psf_binheader_readf() and add ability to read big and little + endian doubles (required by mat4.c and mat5.c). + Add ability for psf_binheader_writef() to write doubles to sound file + headers. + +2002-09-01 Erik de Castro Lopo + + * src/mat5.c + New file for reading Matlab (tm) version 5 data files. This is also the + native binary file format for version 2.1.X of GNU Octave which will be + used for testing. + Not complete yet. + + * src/mat4.c + New file for reading Matlab (tm) version 4.2 data files. This is also the + native binary file format for version 2.0.X of GNU Octave which will be + used for testing. + Not complete yet. + + * src/sndfile.h.in src/sndfile.c src/common.h src/command.c src/Makefile.am + Mods to add Matlab files. + + * src/common.[ch] + Added readf_endian field to SF_PRIVATE struct allowing endianness to + remembered across calls to sf_binheader_readf(). + Fixed bug in width_specifier behaviour for printing hex values. + +2002-08-31 Erik de Castro Lopo + + * src/file_io.c + Check return value of close() call in psf_fclose(). + +2002-08-24 Erik de Castro Lopo + + * src/ms_adpcm.c + Commented out some code where 0x10000 was being subtracted from a short + and the result assigned to a short again. Andrew Zaja found this. + +2002-08-23 Erik de Castro Lopo + + * doc/command.html + Fixed typo found by Tommi Ilmonen. + + * src/ima_adpcm.c + Changed type of diff from short to int to prevent errors which can occur + during very rare circumstances. Thanks to FUWAFUWA. + +2002-08-16 Erik de Castro Lopo + + * tests/floating_point_test.c + Disable testing on machines without lrintf(). + + * Win32/Makefile.msvc + Added dwd.c and wve.c to build. + + * configure.in + Bumped version to 1.0.0. + +2002-08-15 Erik de Castro Lopo + + * src/file_io.c + Add a #include for Mac OS 9. Thanks to Stephane Letz. + + * src/wav.c + Changed an snprintf to LSF_SNPRINTF. + + * doc/Makefile.am + Added version-1.html. + +2002-08-14 Erik de Castro Lopo + + * configure.in + Bumped version to 1.0.rc6. + + * src/*.c + Modified scaling of normalised floats and doubles to integers. Until now + this has been done by multiplying by 0x8000 for short output, 0x80000000 + for 32 bit ints and so on. Unfortunately this can cause an overflow and + wrap around in the target value. All thes values have therefore been + reduced to 0x7FFF, 0x7FFFFFFF and so on. The conversion from ints to + normalised floats and doubles remains unchanged. This does mean that for + repeated conversions normalised float -> pcm16 -> normalised float would + result in a decrease in amplitude of 0x7FFF/0x8000 on every round trip. + This is undesirable but less undesireable than the wrap around I am trying + to avoid. + + * tests/floating_point_test.c + Removed file hash checking because new float scaling procedure introduced + above prevented the ability to crate a has on both x86 and PowerPC systems. + +2002-08-13 Erik de Castro Lopo + + * src/txw.c + Completed reading of TXW files. Seek doesn't work yet. + + * src/file_io.c + Added a MacOS 9 replacement for ftruncate(). + + * MacOS/sndfile.h + Added MacOS 9 header file. This should be copied into src/ to compile + libsndfile for MacOS9. + +2002-08-12 Erik de Castro Lopo + + * src/sndfile.c + Fixed commands SF_SET_NORM_DOUBLE and SFC_SET_NORM_FLOAT to return their + values after being set. Reported by Jussi Laako. + + * configure.in + If autogen is not found, touch all .c and .h files in tests/. + + * src/common.c + Added format width specifier to psf_log_printf() for %u, %d, %D and %X. + + * src/dwd.c + Completed implementation of read only access to these files. + + * src/common.h src/*.c src/pcm.c + Removed redundant field chars from SF_PRIVATE struct and modified + pcm_init() to do without it. + +2002-08-11 Erik de Castro Lopo + + * src/wve.c + New file implementing read of Psion Alaw files. This will be a read only + format. Implementation complete. + + * src/dwd/c + Started implementation of DiamondWare Digitized files. Also read only, not + complete. + + * src/wav.c + Add parsing of 'smpl' chunk. + + * src/paf.c + Fixed reading on un-normalized doubles and floats from 24 bit PAF files. + This brings it into line with the reading of 8 bit files into + un-normalized doubles which returns values in the range [-128, 127]. + + * src/common.c + Modified psf_log_printf() to accept the %% conversion specifier to allow + printing of a single '%'. + + * src/sds.c + Read only of 16 bit samples is working. Need to build a test harness for + this and other read only formats. + +2002-08-10 Erik de Castro Lopo + + * configure.in + Added --enable-experimental configure option. + Removed pkg-config message at the end of the configure process. + + * src/sds.c src/txw.c src/rx2.c src/sd2.c + Moved all the code in these files inside #if ENABLE_EXPERIMENTAL_CODE + blocks and added new *_open() function for the case where experimental is + not enabled. These new functions just return SFE_UNIMPLMENTED. + + * Win32/sndfile.h src/sndfile.h.in src/common.h + Removed un-necessary #pragma pack commands. + + * src/file_io.c + Implemented psf_ftruncate() and much other hacking for Win32. + + * Win32/Makefile.msvc + Updated. + + * doc/win32.html + Updated to include the copying of the sndfile.h file from the Win32/ + directory to the src/ directory. + + * Make.bat + Batch file to make compiling on Wi32 a little easier. Implements "make" and + "make check". + +2002-08-09 Erik de Castro Lopo + + * src/file_io.c + Add place holder for ftruncate() on Win32 which doesn't have ftruncate(). + This will need to be fixed later. + + * src/sndfile.h.in + New file (copy of sndfile.h) with sets up @TYPEOF_SF_COUNT_T@ which will be + replaced by the correct type during configure. + + * configure.in + Modified to find a good type for TYPEOF_SF_COUNT_T. + + * src/aiff.c + Fixed a bug when reading malformed headers. + + * src/common.c + Set read values to zero before performing read. + +2002-08-08 Erik de Castro Lopo + + * doc/command.html + Fixed some HTML tags which were not allowing jumps to links within the + page. + + * src/sds.c + Massive hacking on this. + + * src/wav.c + Added recognition of 'clm ' tag. + +2002-08-07 Erik de Castro Lopo + + * doc/index.html + Added beginning of a capabilities list beyond simple file formats which + can be read/written. + + * src/aiff.c + Added parsing of INST and MARK chunks of AIFF files. At the moment this + data is simply recorded in the log buffer. Later it will be possible to + read this data from an application using sf_command(). + + * src/wav.c + Added parsing of 'cue ' chunk which contains loop information in WAV files. + + * exampes/sndfile-info.c + Changed reporting of Samples to Frames. + + * src/wav.c src/w64.c src/aiff.c src/wav_w64.h + Moved from a samples to a frames nomenclature to avoid confusion. + + * doc/FAQ.html + What's the best format for storing temporary files? + + * src/sds.c + New file for reading/writing Midi Sample Dump Standard files. + + * src/Makefile.am src/sndfile.c src/common.[ch] + Added hooks for sds.c. + + * examples/sndfile-info.c + Changed from using sf_perror() to using sf_error_str(). + +2002-08-06 Erik de Castro Lopo + + * doc/api.html + Added explanation of mode parameter for sf_open(). + Added explanation of usage of SFM_* values in sf_seek(). + + * src/sndfile.[ch] src/command.c src/file_io.c src/common.h + Implemented SFC_FILE_TRUNCATE to allow a file to be truncated. File + truncation was suggested by James McCartney. + + * src/command.html + Documented SFC_FILE_TRUNCATE. + + * tests/command_test.c + Add tests for SFC_FILE_TRUNCATE. + + * src/sndfile.c + Added a thrid parameter to the VALIDATE_SNDFILE_AND_ASSIGN_PSF macro to + make resetting the error number optional. All uses of the macro other than + in error reporting functions were changed to reset the error number. + + * src/pcm.c + Fixed a bug were sf_read_* was logging an SFE_SHORT_READ even when no error + occurred. + + * tests/write_read_test.tpl + Added tests of internal error state. + +2002-08-05 Erik de Castro Lopo + + * src/GSM610/private.h src/GSM610/*.c src/GSM610/Makefile.am + Renamed private.h to gsm610_priv.h to prevent clash with other headers + named private.h in other directories. (Probably only a problem on MacOS 9). + + * src/G72x/private.h src/G72x/*.c src/G72x/Makefile.am + Renamed private.h to g72x_priv.h to prevent clash with other headers + named private.h in other directories. (Probably only a problem on MacOS 9). + + * MacOS/config.h + Changed values of HAVE_LRINT and HAVE_LRINTF to force use of code in + float_cash.h. + + * src/sndfile.h + Changes the name of samples field of the SF_INFO to frames. The old name + had caused too much confusion and it simply had to be changed. There will + be at least one more pre-release. + +2002-08-04 Erik de Castro Lopo + + * doc/index.html + Updated formats matrix to include RAW (header-less) GSM 6.10. + Fix specificaltion of table and spelling mistakes. + + * src/sndfile.c src/command.c + Fixed bug in SFC_CALC_MAX_SIGNAL family and psf_calc_signal_max (). + + * tests/command.c + Removed cruft. + Added test for SFC_CALC_MAX_SIGNAL and SFC_CALC_NORM_MAX_SIGNAL. + + * configure.in + Update version to 1.0.0rc5. + + * sfendian.h + Removed inclusion of un-necessary header. + +2002-08-03 Erik de Castro Lopo + + * src/aiff.c + Minor fixes of info written to log buffer. + + * src/float_cast.h + Add definition of HAVE_LRINT_REPLACEMENT. + + * tests/floating_point_test.c + Fix file hash check on systems without lrint/lrintf. + + * tests/dft_cmp.c + Limit SNR to less than -500.0dB. + + * examples/sndfile2oct.c + Fixed compiler warnings. + + * doc/api.html + Fixed error where last parameter of sf_error_str() was sf_count_t instead + of size_t. + +2002-08-02 Erik de Castro Lopo + + * doc/FAQ.html + Why doesn't libsndfile do interleaving/de-interleaving. + + * tests/pcm_test.tpl + On Win32 do not perform hash check on files containing doubles. + +2002-08-01 Erik de Castro Lopo + + * src/common.h + Defined SF_COUNT_MAX_POSITIVE() macro, a portable way of setting variables + of type sf_count_t to their maximum positive value. + + * src/dwvw.c src/w64.c + Used SF_COUNT_MAX_POSITIVE(). + +2002-07-31 Erik de Castro Lopo + + * src/paf.c + Fixed bug in reading/writing of 24 bit PCM PAF files on big endian systems. + + * tests/floating_point_tests.c + Fixed hash values for 24 bit PCM PAF files. + Disabled file has check if lrintf() function is not available and added + warning. + Decreased level of signal from a peak of 1.0 to a value of 0.95 to prevent + problems on platforms without lrintf() ie Solaris. + +2002-07-30 Erik de Castro Lopo + + * src/wav.c + Fixed a problem with two different kinds of mal-formed WAV file header. The + first had the 'fact' chunk before the 'fmt ' chunk, the other had an + incomplete 'INFO' chunk at the end of the file. + + * src/w64.c + Added fix to allow differentiation between W64 files and ACID files. + + * src/au_g72x.c src/common.h src/sndfile.c + Added error for G72x encoded files with more than one channel. + + * tests/pcm_test.tpl tests/utils.tpl + Moved function check_file_hash_or_die() to utils.tpl. Function was then + modified to calculate the has of the whole file. + + * src/wav.c + Fixed problem writing the 'fact' chunk on big endian systems. + + * tests/sfconvert.c + Fixed bug where .paf files were being written as Sphere NIST. + +2002-07-29 Erik de Castro Lopo + + * src/voc.c + Fix for reading headers generated using SFC_UPDATE_HEADER_NOW. + + * doc/command.html + Add docs for SFC_UPDATE_HEADER_NOW and SFC_SET_UPDATE_HEADER_AUTO. + +2002-07-28 Erik de Castro Lopo + + * man/sndfile-info.1 man/sndfile-play.1 + Added manpages supplied by Joshua Haberman the Debian maintainer for + libsndfile. Additional tweaks by me. + + * configure.in man/Makefile.am + Hooked manpages into autoconf/automake system. + + * src/sndfile.c + Added hooks for SFC_SET_UPDATE_HEADER_AUTO. + + * tests/update_header_test.c + Improved rigor of testing. + + * src/*.c + Fixed problem with *_write_header() functions. + +2002-07-27 Erik de Castro Lopo + + * doc/*.html + Updates to documentation to fix problems found by wdg-html-validator. + + * src/common.h src/command.c + Added normalize parameter to calls to psf_calc_signal_max() and + psf_calc_max_all_channels(). + + * src/sndfile.c + Added handling for commands SFC_CALC_NORM_SIGNAL_MAX and + SFC_CALC_NORM_MAX_ALL_CHANNELS. + + * doc/command.html + Added entry for SFC_CALC_NORM_SIGNAL_MAX and SFC_CALC_NORM_MAX_ALL_CHANNELS. + +2002-07-26 Erik de Castro Lopo + + * examples/sndfile-play.c Win32/Makefile.msvc + Get sndfile-play program working on Win32. The Win32 PCM sample I/O API + sucks. The sndfile-play program now works on Linux, MacOSX, Solaris and + Win32. + +2002-07-25 Erik de Castro Lopo + + * doc/FAQ.html + New file for frequently asked questsions. + +2002-07-22 Erik de Castro Lopo + + * doc/api.html + Documentation fixes. + + * src/au.[ch] src/au_g72x.c src/G72x/g72x.h + Add support of 40kbps G723 ADPCM encoding. + + * tests/lossy_comp_test.c tests/floating_point_test.c + Add tests for 40kbps G723 ADPCM encoding. + + * doc/index.html + Update support matrix. + +2002-07-21 Erik de Castro Lopo + + * doc/command.html + Documented SFC_GET_SIMPLE_FORMAT_COUNT, SFC_GET_SIMPLE_FORMAT, + SFC_GET_FORMAT_* and SFC_SET_ADD_PEAK_CHUNK. + + * src/sndfile.c src/pcm.c + Add ability to turn on and off the addition of a PEAK chunk for floating + point WAV and AIFF files. + + * src/sndfile.[ch] src/common.h src/command.c + Added sf_command SFC_CALC_MAX_ALL_CHANNELS. Implemented by Maurizio Umberto + Puxeddu. + + * doc/command.html + Docs for SFC_CALC_MAX_ALL_CHANNELS (assisted by Maurizio Umberto Puxeddu). + +2002-07-18 Erik de Castro Lopo + + * src/sndfile.c src/gsm610.c + Finalised support for GSM 6.10 AIFF files and added support for GSM 6.10 + encoded RAW (header-less) files. + + * src/wav.c + Add support for IBM_FORMAT_MULAW and IBM_FORMAT_ALAW encodings. + + * src/api.html + Fixed more documentation bugs. + +2002-07-17 Erik de Castro Lopo + + * src/sndfile.h src/common.h + Moved some yet-to-be-implelmented values for SF_FORMAT_* from the public + header file sndfile.h to the private header file common.h to avoid + confusion about the actual capabilities of libsndfile. + +2002-07-16 Erik de Castro Lopo + + * src/aiff.c src/wav.c + Fixed file parsing for WAV and AIFF files containing non-audio data after + the data chunk. + + * src/aiff.c src/sndfile.c + Add support for GSM 6.10 encoded AIFF files. + + * tests/lossy_comp_test.c tests/Makefile.am + Add tests for GSM 6.10 encoded AIFF files. + + * src/*.c + Fix compiler warnings. + +2002-07-15 Erik de Castro Lopo + + * tests/command_test.c + For SFC_SET_NORM_* tests, change the file format from SF_FORMAT_WAV to + SF_FORMAT_RAW. + + * src/sndfile.c + Added sf_command(SFC_TEST_ADD_TRAILING_DATA) to allow testing of reading + from AIFF and WAV files with non-audio data after the audio chunk. + + * src/common.h + Add test commands SFC_TEST_WAV_ADD_INFO_CHUNK and + SFC_TEST_AIFF_ADD_INST_CHUNK. When these commands are working, they will be + moved to src/sndfile.h + + * src/aiff.c src/wav.c + Begin implementation of XXXX_command() hook for sf_command(). + + * tests/write_read_test.tpl + Added sf_command (SFC_TEST_ADD_TRAILING_DATA) to ensure above new code was + working. + +2002-07-13 Erik de Castro Lopo + + * tests/update_header_test.c + Allow read sample count == write sample count - 1 to fix problems with VOC + files. + + * tests/write_read_test.tpl tests/pcm_test.tpl + Fixed some problems in the test suite discovered by using Valgrind. + +2002-07-12 Erik de Castro Lopo + + * tests/utils.[ch] tests/*.c + Renamed check_log_buffer() to check_log_buffer_or_die(). + + * src/sndfile.c + SFC_UPDATE_HEADER_NOW and SFC_SETUPDATE_HEADER_AUTO almost finished. Works + for all file formats other than VOC. + +2002-07-11 Erik de Castro Lopo + + * src/sndfile.[ch] src/common.h + Started adding functionality to allow the file header to be updated before + the file is closed on files open for SFM_WRITE. This was requested by + Maurizio Umberto Puxeddu who is using libsndfile for file I/O in iCSound. + + * tests/update_header_test.c + New test program to test that the above functionality is working correctly. + + * tests/peak_chunk_test.c tests/floating_point_test.c + Cleanups. + +2002-07-10 Erik de Castro Lopo + + * src/sfendian.[ch] + Changed length count parameters for all endswap_XXX() functions from + sf_count_t (which can be 64 bit even on 32 bit architectures) to int. These + functions are only called frin inside the library, are always called with + integer parameters and doing the actual calculation on 64 bit values is + slow in comparision to doing it on ints. + + * examples/sndfile-play.c + More playback hacking for Win32. + +2002-07-09 Erik de Castro Lopo + + * src/common.c + In psf_log_printf(), changed %D format conversion specifier to %M (marker) and + added %D specifier for printing the sf_count_t type. + + * src/*.c + Changed all usage of psf_log_printf() with %D format conversion specifiers + to use %M conversion instead. + + * tests/pcm_test.tpl tests/pcm_test.def + New files to autogen pcm_test.c. + + * src/pcm.c + Fixed bug in scaling floats and doubles to 24 bit PCM and vice versa. + +2002-07-08 Erik de Castro Lopo + + * configure.in + Fix setup of $ac_cv_sys_largefile_CFLAGS so that sndfile.pc gets valid + values for CFLAGS. + + * examples/sndfile-play.c + Start adding playback support for Win32. + +2002-07-07 Erik de Castro Lopo + + * src/*.c + Worked to removed compiler warnings. + Extensive refactoring. + + * src/common.[ch] + Added function psf_memset() which works like the standard C function memset + but takes and sf_count_t as the length parameter. + + * src/sndfile.c + Replaced calls to memset(0 with calls to psf_memset() as required. + +2002-07-06 Erik de Castro Lopo + + * src/sndfile.c + Added "libsndfile : " to the start of all error messages. This was suggested + by Conrad Parker author of Sweep ( http://sweep.sourceforge.net/ ). + + * src/sfendian.[ch] + Added endswap_XXXX_copy() functions. + + * src/pcm.c src/float32.c src/double64.c + Use endswap_XXXX_copy() functions and removed dead code. + Cleanups and optimisations. + +2002-07-05 Erik de Castro Lopo + + * src/sndfile.c src/sndfile.h + Gave values to all the SFC_* enum values to allow better control of the + interface as commands are added and removed. + Added new command SFC_SET_ADD_PEAK_CHUNK. + + * src/wav.c src/aiff.c + Modified wav_write_header and aiff_write_header to make addition of a PEAK + chunk optional, even on floating point files. + + * tests/benchmark.tpl + Added call to sf_command(SFC_SET_ADD_PEAK_CHUNK) to turn off addition of a + PEAK chunk for the benchmark where we are trying to miximize speed. + + * src.pcm.c + Changed tribyte typedef to something more sensible. + Further conversion speed ups. + +2002-07-03 Erik de Castro Lopo + + * src/command.c + In major_formats rename "Sphere NIST" to "NIST Sphere". + + * src/common.c src/sfendian.c + Moved all endswap_XXX_array() functions to sfendian.c. These functions will + be tweaked to provide maximum performance. Since maximum performance on one + platform does not guarantee maximum performance on another, a small set of + functions will be written and the optimal one chosen at compile time. + + * src/common.h src/sfendian.h + Declarations of all endswap_XXX_array() functions moved to sfendian.h. + + * src/Makefile.am + Add sfendian.c to build targets. + +2002-07-01 Erik de Castro Lopo + + * src/pcm.c src/sfendian.h + Re-coded PCM encoders and decoders to match or better the speed of + libsndfile version 0.0.28. + +2002-06-30 Erik de Castro Lopo + + * src/wav.c + Add checking for WAVPACK data in standard PCM WAV file. Return error if + found. This WAVPACK is *WAY* broken. It uses the same PCM WAV file header + and then stores non-PCM data. + + * tests/benchmark.tpl + Added more tests. + +2002-06-29 Erik de Castro Lopo + + * tests/benchmark.tpl + Added conditional definition of M_PI. + For Win32, set WRITE_PERMS to 0777. + + * Win32/Makefile.msvc + Added target to make generate program on Win32. + + * src/samplitude.c + Removed handler for Samplitude RAP file format. This file type seems rarer + than hens teeth and is completely undocumented. + + * src/common.h src/sndfile.c src/Makefile.am Win32/Makefile.msvc + Removed references to sampltiude RAP format. + + * tests/benchmark.tpl + Benchmark program now prints the libsndfile version number when run. This + program was also backported to version 0 to compare results. Version + 1.0.0rc2 is faster than version 0.0.28 on most conversions but slower on + some. The slow ones need to be fixed before final release. + +2002-06-28 Erik de Castro Lopo + + * tests/benchmark.def tests/benchmark.tpl + New files which generate tests/benchmark.c using Autogen. Added int -> + SF_FORMAT_PCM_24 test. + + * tests/benchmark.c + Now and Autogen output file. + + * tests/Makefile.am + Updated for above changes. + +2002-06-27 Erik de Castro Lopo + + * tests/benchmark.c + Basic benchmark program complete. Need to convert it to Autogen. + + * Win32/Makefile.msvc + Added benchmark.exe target. + +2002-06-26 Erik de Castro Lopo + + * examples/generate.c + New program to generate a number of different output file formats from a + single input file. This allows testing of the created files. + + * tests/benchmark.c + New test program to benchmark libsndfile. Nowhere near complete yet. + + * examples/Makefile.am tests/Makefile.am + New make rules for the two new programs. + +2002-06-25 Erik de Castro Lopo + + * Win32/libsndfile.def + Removed definition for sf_signal_max(). + + * src/sndfile.c + Removed cruft. + + * doc/index.html + A number of documentation bugs were fixed. Thanks to Anand Kumria. + + * doc/version-1.html + Minor doc updates. + + * configure.in + Bumped version to 1.0.0rc2. + + * src/sf_command.h src/Makefile.am + Removed the header file as it was no longer being used. Thanks to Anand + Kunria for spotting this. + + * doc/index.html + A number of documentation bugs were fixed. Thanks to Anand Kumria. + +2002-06-24 Erik de Castro Lopo + + * src/common.h + Test for Win32 before testing SIZEOF_OFF_T so that it works correctly + on Win32.. + + * src/file_io.c + Win32 fixes to ensure O_BINARY is used for file open. + + * doc/win32.html + New file documenting the building libsndfile on Win32. + + * doc/*.html + Updating of documentation. + +2002-06-23 Erik de Castro Lopo + + * tests/pcm_test.c + Minor changes to allow easier determination of test file name. + + * src/sndfile.[ch] + Removed function sf_signal_max(). + + * examples/sndfile-play.c + Changed call to sf_signal_max() to a call to sf_command(). + +2002-06-22 Erik de Castro Lopo + + * src/format.c src/command.c + Renamed format.c to command.c which will now include code for sf_command() + calls to perform operations other than format commands. + + * src/sndfile.c src/sndfile.h + Removed function sf_get_signal_max() which is replaced by commands passed + to sf_command(). + + * src/command.c + Implement commands SFC_CALC_SIGNAL_MAX. + + * doc/command.html + Documented SFC_CALC_SIGNAL_MAX. + +2002-06-21 Erik de Castro Lopo + + * examples/sndfile-play.c + Mods to make sndfile-play work on Solaris. The program sndfile-play now + runs on Linux, MaxOSX and Solaris. Win32 to come. + + * src/format.c + Added SF_FORMAT_DWVW_* to subtype_formats array. + + * src/nist.c + Added support for 8 bit NIST Sphere files. Example file supplied by Anand + Kumria. + +2002-06-20 Erik de Castro Lopo + + * examples/sndfile-info.c + Tidy up of output format. + + * examnples/sndfile-play.c + Mods to make sndfile-play work on MacOSX using Apple's CoreAudio API. + + * configure.in + Add new variables OS_SPECIFIC_INCLUDES and OS_SPECIFIC_LINKS which were + required to supply extra include paths and link parameters to get + sndfile-play working on MacOSX. + + * examples/Makefile.am + Use OS_SPOECIFIC_INCLUDES and OS_SPECIFIC_LINKS to build commands for + sndfile-play. + +2002-06-19 Erik de Castro Lopo + + * src/nist.c + Added ability to read/write new NIST Sphere file types (A-law, u-law). + Header parser was re-written from scratch. Example files supplied by Anand + Kumria. + + * src/sndfile.c + Support for A-law and u-law NIST files. + + * tests/Makefile.am tests/lossy_comp_test.c + Tests for A-law and u-law NIST files. + +2002-06-18 Erik de Castro Lopo + + * tests/utils.c + Fixed an error in error string. + +2002-06-17 Erik de Castro Lopo + + * acinclude.m4 + Removed exit command to allow cross-compiling. + + * Win32/unistd.h src/file_io.c + Moved contents of first file into the second file (enclosed in #ifdef). + Win32/unistd.h is now an empty file but still must be there for libsndfile + to compile on Win32. + + * src/sd2.c, src/sndfile.c: + Fixes for Sound Designer II files on big endian systems. + +2002-06-16 Erik de Castro Lopo + + * configure.in + Modified to work around problems with crappy MacOSX version of sed. + Added sanity check for proper values for CFLAGS. + +2002-06-14 Erik de Castro Lopo + + * src/sndfile.c + Code clean up in sf_open (). + + * Win32/Makefile.msvc + Michael Fink's contributed MSVC++ makefile was hacked to bits and put back + together in a new improved form. + + * src/file_io.c + Fixes for Win32; _lseeki64() returns an invalid argument for calls like + _lseeki64(fd, 0, SEEK_CUR) so need to use _telli64 (fd) instead. + + * src/common.h src/sndfile.c src/wav.c src/aiff.c + Added SFE_LOG_OVERRUN error. + Added termination for potential infinite loop when parsing file headers. + + * src/wav.c src/w64.c + Fixed bug casuing incorrect header generation when opening file read/write. + +2002-06-12 Erik de Castro Lopo + + * doc/api.html + Improved the documentation to make it clearer that the file read method + and the underlying file format are completely disconnected. Suggested + by Josh Green. + + * doc/command.html + Started correcting docs to take into account changes made to the + operations of the sf_command () function. Not complete yet. + + * src/sndfile.c + Reverted some changes which had broken the partially working SDII header + parsing. Now have access to an iBook with OS X so reading and writing SDII + files on all platforms should be a reality in the near future. On Mac this + will involve reading the resource fork via the standard MacOS API. To move + a file from Mac to another OS, the resource and data forks will need to be + combined before transfer. The combined file will be read on both Mac and + other OSes like any other file. + +2002-06-08 Erik de Castro Lopo + + * ltmain.sh + Applied a patch from http://fink.sourceforge.net/doc/porting/libtool.php + which allows libsndfile to compile on MacOSX 10.1. This patch should not + interfere with compiling on other OSes. + + * src/GSM610/private.h + Changes to fix compile problems on MacOSX (see src/GSM610/ChangeLog). + + * src/float_cast.h + Added MacOSX replacements for lrint() and lrintf(). + +2002-06-05 Erik de Castro Lopo + + * src/sndfile.c + Replaced the code to print the filename to the log buffer when a file is + opened. This code seems to have been left out during the merge of + sf_open_read() and sf_open_write() to make a single functions sf_open(). + +2002-06-01 Erik de Castro Lopo + + * src/wav.c + Fixed a bug where the WAV header parser was going into an infinite loop + on a badly formed LIST chunk. File supplied by David Viens. + +2002-05-25 Erik de Castro Lopo + + * configure.in + Added a message at the end of the configuration process to warn about the + need for the use of pkg-config when linking programs against version 1 of + libsndfile. + + * doc/pkg-config.html + New documentation file containing details of how to use pkg-config to + retrieve settings for CFLAGS and library locations for linking files + against version 1 of libsndfile. + +2002-05-17 Erik de Castro Lopo + + * src/wav.c + Fixed minor bug in handling of so-called ACIDized WAV files. + +2002-05-16 Erik de Castro Lopo + + * Win32/libsndfile.def Win32/Makefile.msvc + Two new files contributed by Michael Fink (from the winLAME project) + which allows libsndfile to be built on windows in a MSDOS box by doing + "nmake -f Makefile.msvc". Way cool! + +2002-05-15 Erik de Castro Lopo + + * configure.in + MacOSX is SSSOOOOOOO screwed up!!! I can't believe how hard it is to + generate a tarball which will configure and compile on that platform. + Joined the libtool mailing list to try and get some answers. + +2002-05-13 Erik de Castro Lopo + + * configure.in + Changed to autoconf version 2.50. MacOSX uses autoconf version 2.53 which + is incompatible with with version 2.13 which had been using until now. + The AC_SYS_LARGE_FILE macro distributed withe autoconf 2.50 is missing a + few features so AC_SYS_EXTRA_LARGE file was defined to replace it. + + * configure.in + Changed to automake version 1.5 to try and make a tarball which will + work on MacOSX. + +2002-05-12 Erik de Castro Lopo + + * src/wav_gsm610.c + Changed name to gsm610.c. Added reading/writing of headerless files. + + * src/sndfile.c src/raw.c + Added ability to read/write headerless (SF_FORMAT_RAW) GSM 6.10 files. + +2002-05-11 Erik de Castro Lopo + + * tests/lossy_comp_test.c + Clean up in preparation for Autogen-ing this file. + + * src/GSM610/*.[ch] + Code cleanup and prepartion forgetting file seek working. Details in + src/GSM610/ChangeLog. + + * sndfile.pc.in + Testing complete. Is sndfile.m4 still needed? + +2002-05-09 Erik de Castro Lopo + + * tests/write_read_test.tpl tests/rdwr_test.tpl + Merged tests from these two programs into write_read_test.tpl and deleted + rdwr_test.tpl. + +2002-05-08 Erik de Castro Lopo + + * src/w64.c src/svx.c src/paf.c + Fixed bugs in read/write mode. + +2002-05-07 Erik de Castro Lopo + + * examples/Makefile.am + Renamed sfplay.c to sndfile-play.c and sndfile_info.c to sndfile-info.c for + consistency when these programs become part of the Debian package + sndfile-programs. + + * sndfile.pc.in + New file to replace sndfile-config.in. Libsndfile now uses the pkg-config + model for providing installation parameters to dependant programs. + + * src/sndfile.c + Cleanup of code in sf_open(). + +2002-05-06 Erik de Castro Lopo + + * tests/utils.tpl tests/write_read_test.tpl + More conversion to Autogen fixes and enchancements. + + * src/*.c + Read/write mode is now working for 16, 24 and 32 bit PCM as well as 32 + bit float and 64 bit double data. More tests still required. + + * src/Makefile.am + Added DISTCLEANFILES target to remove config.status and config.last. + + * Win32/Makefile.am MacOS/Makefile.am + Added DISTCLEANFILES target to remove Makefile. + +2002-05-05 Erik de Castro Lopo + + * src/*.[ch] tests/rdwr_test.c + More verifying workings of read/write mode. Fixing bugs found. + + * tests/utils.[ch] + Made these files Autogen generated files. + + * tests/util.tpl tests/util.def + New Autogen files to generate utils.[ch]. Moved some generic test functions + into this file. Autogen is such a great tool! + +2002-05-03 Erik de Castro Lopo + + * src/pcm.c src/float_cast.h Win32/config.h + Fixed a couple of Win32 specific bugs pointed out by Michael Fink + (maintainer of WinLAME) and David Viens. + + * tests/check_log_buffer.[ch] tests/utils.[ch] + Moved check_log_buffer() to utils.[ch] and deleted old file. + +2002-05-02 Erik de Castro Lopo + + * src/common.[ch] src/sndfile.c + New function psf_default_seek() which will be the default seek function + for things like PCM and floating point data. This default is set for + both read and write in sf_open() but can be over-ridden by any codec + during it's initialisation. + +2002-05-01 Erik de Castro Lopo + + * src/au.c + AU files use a data size value of -1 to mean unknown. Fixed au_open_read() + to allow opening files like this. + + * tests/rdwr_test .c + Added more tests. + + * src/sndfile.c + Fixed bugs in read/write mode found due to improvements in the test + program. + +2002-04-30 Erik de Castro Lopo + + * tests/rdwr_test .c + New file for testing read/write mode. + +2002-04-29 Erik de Castro Lopo + + * m4/* + Removed all m4 macros from this directory as they get concatenated to form + the file aclocal.m4 anyway. + + * sndfile.m4 + Moved this from the m4 directory to the root directory asn this is part of + the distribution and is installed during "make install". + +2002-04-29 Erik de Castro Lopo + + * src/float32.c + Removed logging of peaks for all file formats other than AIFF and WAV. + + * tests/write_read_test.tpl tests/write_read_test.def + New files which autogen uses to generate write_read_test.c. Doing it this + way makes write_read_test.c far easier to maintain. Other test programs + will be converted to autogen in the near future. + + * src/*.c + Fixed a few bugs found when testing on Sparc (bug endian) Solaris. + +2002-04-28 Erik de Castro Lopo + + * doc/*.html + Fixed documention versioning. + + * configure.in + Fixed a bug in the routines which search for Large File Support on systems + which have large file support by defualt. + +2002-04-27 Erik de Castro Lopo + + * src/*.[ch] + Found and fixed an issue which can cause a bug in other software (I was + porting Conrad Parker's Sweep program from version 0 of the library to + version 1). When opening a file for write, the libsndfile code would + set the sfinfo.samples field to a maximum value. + + * tests/write_read_test.c + Added tests to detect the above problem. + +2002-04-25 Erik de Castro Lopo + + * src/*.[ch] + Finished base implementation of read/write mode. Much more testing still + needed. + + * m4/largefile.m4 + Macro for detecting Large File Standard capabilities. This macro was ripped + out of the aclocal.m4 file of GNU tar-1.13. + + * configure.in + Added detection of large file support. Files larger than 2 Gigabytes should + now be supported on 64 bit platforms and many 32 bit platforms including + Linux (2.4 kernel, glibc-2.2), *BSD, MacOS, Win32. + + * libsndfile_convert_version.py + A Python script which attempts to autoconvert code written to use version 0 + to version 1. + +2002-04-24 Erik de Castro Lopo + + * src/*.[ch] + Finished base implementation of read/write mode. Much more testing still + needed. + + * tests/write_read_test.c + Preliminary tests for read/write mode added. More needed. + +2002-04-20 Erik de Castro Lopo + + * src/sndfile.[ch] + Removed sf_open_read() and sf_open_write() functions,replacting them with + sf_open() which takes an extra mode parameter (SF_OPEN_READ, SF_OPEN_WRITE, + or SF_OPEN_RDWR). This new function sf_open can now be modified to allow + opening a file formodification (RDWR). + +2002-04-19 Erik de Castro Lopo + + * src/*.c + Completed merging of separate xxx_open_read() and xxx_open_write() + functions. All tests pass. + +2002-04-18 Erik de Castro Lopo + + * src/au.c + Massive refactoring required to merge au_open_read() with au_open_write() + to create au_open(). + +2002-04-17 Erik de Castro Lopo + + * src/*.c + Started changes required to allow a sound file to be opened in read/write + mode, with separate file pointers for read and write. This involves merging + of encoder/decoder functions like pcm_read_init() and pcm_write_init() + int a new function pcm_init() as well as doing something similar for all + the file type specific functions ie aiff_open_read() and aiff_open_write() + were merged to make the function aiff_open(). + +2002-04-15 Erik de Castro Lopo + + * src/file_io.c + New file containing psf_fopen(), psf_fread(), psf_fwrite(), psf_fseek() and + psf_ftell() functions. These function will replace use of fopen/fread/fwrite + etc and allow access to files larger than 2 gigabytes on a number of 32 bit + OSes (Linux on x86, 32 bit Solaris user space apps, Win32 and MacOS). + + * src/*.c + Replaced all instances of fopen with psf_open, fread with psd_read, fwrite + with psf_write and so on. + +2002-03-11 Erik de Castro Lopo + + * src/dwvw.c + Finally fixed all known problems with 12, 16 and 24 bit DWVW encoding. + + * tests/floating_point_test.c + Added tests for 12, 16 and 24 bit DWVW encoding. + +2002-03-03 Erik de Castro Lopo + + * m4/endian.m4 + Defines a new m4 macro AC_C_FIND_ENDIAN, for determining the endian-ness of + the target CPU. It first checks for the definition of BYTE_ORDER in + , then in and . If none of these work + and the C compiler is not a cross compiler it compiles and runs a program + to test for endian-ness. If the compiler is a cross compiler it makes a + guess based on $target_cpu. + + * configure.in + Modified to use AC_C_FIND_ENDIAN. + + * src/sfendian.h + Simplified. + +2002-02-23 Erik de Castro Lopo + + * tests/floating_point_test.c + Tests completely rewritten using the dft_cmp function. Now able to + calculate a quick guesstimate of the Signal to Noise Ratio of the encoder. + +2002-02-15 Erik de Castro Lopo + + * tests/dft_cmp.[ch] + New files containing functions for comparing pre and post lossily + compressed data using a quickly hacked DFT. + + * tests/utils.[ch] + New files containing functions for saving pre and post encoded data in a + file readable by the GNU Octave package. + +2002-02-13 Erik de Castro Lopo + + * m4/lrint.m4 m4/lrintf.m4 + Fixed m4 macros to define HAVE_LRINT and HAVE_LRINTF even when the test + is cached. + +2002-02-12 Erik de Castro Lopo + + * tests/floating_point_test.c + Fixed improper use of strncat (). + +2002-02-11 Erik de Castro Lopo + + * tests/headerless_test.c + New test program to test the ability to open and read a known file type as a + RAW header-less file. + +2002-02-07 Erik de Castro Lopo + + * tests/losy_comp_test.c + Added a test to ensure that the data read from a file is not all zeros. + + * examples/sfconvert.c + Added "-gsm610" encoding types. + +2002-01-29 Erik de Castro Lopo + + * examples/sfconvert.c + Added "-dwvw12", "-dwvw16" and "-dwvw24" encoding types. + + * tests/dwvw_test.c + New file for testing DWVW encoder/decoder. + +2002-01-28 Erik de Castro Lopo + + * src/dwvw.c + Implemented writing of DWVW. 12 bit seems to work, 16 and 24 bit still broken. + + * src/aiff.c + Improved reporting of encoding types. + + * src/voc.c + Clean up. + +2002-01-27 Erik de Castro Lopo + + * src/dwvw.c + New file implementing lossless Delta Word Variable Width (DWVW) encoding. + Reading 12 bit DWVW is now working. + + * src/aiff.c common.h sndfile.c + Added hooks for DWVW encoded AIFF and RAW files. + +2002-01-15 Erik de Castro Lopo + + * src/w64.c + Robustify header parsing. + + * src/wav_w64.h + Header file wav.h was renamed to wav_w64.h to signify sharing of + definitions across the two file types. + + * src/wav.c src/w64.c src/wav_w64.c + Refactoring. + Modified and moved functions with a high degree of similarity between + wav.c and w64.c to wav_w64.c. + +2002-01-14 Erik de Castro Lopo + + * src/w64.c + Completed work on getting read and write working. + + * examples/sfplay.c + Added code to scale floating point data so it plays at a reasonable volume. + + * tests/Makefile.am tests/write_read_test.c + Added tests for W64 files. + +2002-01-13 Erik de Castro Lopo + + * src/*.c + Modded all code in file header writing routines to use + psf_new_binheader_writef(). + Removed psf_binheader_writef() from src/common.c. + Globally replaced psf_new_binheader_writef with psf_binheader_writef. + +2002-01-12 Erik de Castro Lopo + + * src/*.c + Modded all code in file parsing routines to use psf_new_binheader_readf(). + Removed psf_binheader_readf() from src/common.c. + Globally replaced psf_new_binheader_readf with psf_binheader_readf. + + * src/common.[ch] + Added new function psf_new_binheader_writef () which will soon replace + psf_binheader_writef (). The new function has basically the same function + as the original but has a more flexible and capable interface. It also + allows the writing of 64 bit integer values for files contains 64 bit file + offsets. + +2002-01-11 Erik de Castro Lopo + + * src/formats.c src/sndfile.c src/sndfile.h + Added code allowing full enumeration of supported file formats via the + sf_command () interface. + This feature will allow applications to avoid needing recompilation when + support for new file formats are added to libsndfile. + + * tests/command_test.c + Added test code for the above feature. + + * examples/list_formats.c + New file. An example of the use of the supported file enumeration + interface. This program lists all the major formats and for each major + format the supported subformats. + +2002-01-10 Erik de Castro Lopo + + * src/*.[ch] tests/*.c + Changed command parameter of sf_command () function from a test string to + an int. The valid values for the command parameter begin with SFC_ and are + listed in src/sndfile.h. + +2001-12-20 Erik de Castro Lopo + + * src/formats.c src/sndfile.c + Added an way of enumerating a set of common file formats using the + sf_command () interface. This interface was suggested by Dominic Mazzoni, + one of the main authors of Audacity (http://audacity.sourceforge.net/). + +2001-12-26 Erik de Castro Lopo + + * src/sndfile.c + Added checking of filename parameter in sf_open_read (). Previousy, if a + NULL pointer was passed the library would segfault. + +2001-12-18 Erik de Castro Lopo + + * src/common.c src/common.h + Changed the len parameter of the endswap_*_array () functions from type + int to type long. + + * src/pcm.c + Fixed a problem which + +2001-12-15 Erik de Castro Lopo + + * src/sndfile.c + Added conditional #include for EMX/gcc on OS/2. Thanks to + Paul Hartman for pointing this out. + + * tests/lossy_comp_test.c tests/floating_point_test.c + Added definitions for M_PI for when it isn't defined in . + +2001-11-30 Erik de Castro Lopo + + * src/ircam.c + Re-implemented the header reader. Old version was making incorrect + assumptions about the endian-ness of the file from the magic number at the + start of the file. The new code looks at the integer which holds the + number of channels and determines the endian-ness from that. + +2001-11-30 Erik de Castro Lopo + + * src/aiff.c + Added support for other AIFC types ('raw ', 'in32', '23ni'). + Further work on IMA ADPCM encoding. + +2001-11-29 Erik de Castro Lopo + + * src/ima_adpcm.c + Renamed from wav_ima_adpcm.c. This file will soon handle IMA ADPCM + encodings for both WAV and AIFF files. + + * src/aiff.c + Started adding IMA ADPCM support. + +2001-11-28 Erik de Castro Lopo + + * src/double.c + New file for handling double precision floating point (SF_FORMAT_DOUBLE) + data. + + * src/wav.c src/aiff.c src/au.c src/raw.c + Added support for SF_FORMAT_DOUBLE data. + + * src/common.[ch] + Addition of endswap_long_array () for endian swapping 64 bit integers. This + function will work correctly on processors with 32 bit and 64 bit longs. + Optimised endswap_short_array () and endswap_int_array (). + + * tests/pcm_test.c + Added and extra check. After the first file of each type is written to disk + a checksum is performed of the first 64 bytes and checked against a pre- + calculated value. This will work whatever the endian-ness of the host + machine. + +2001-11-27 Erik de Castro Lopo + + * src/aiff.c + Added handling of u-law, A-law encoded AIFF files. Thanks to Tom Erbe for + supplying example files. + + * tests/lossy_comp_test.c + Added tests for above. + + * src/common.h src/*.c + Removed function typedefs from common.h and function pointer casting in all + the other files. This allows the compiler to perform proper type checking. + Hopefully this will prevernt problems like the sf_seek bug for OpenBSD, + BeOS etc. + + * src/common.[ch] + Added new function psf_new_binheader_readf () which will eventually replace + psf_binheader_readf (). The new function has basically the same function as + the original but has a more flexible and capable interface. It also allows + the reading of 64 bit integer values for files contains 64 bit file + offsets. + +2001-11-26 Erik de Castro Lopo + + * src/voc.c + Completed implementation of VOC file handling. Can now handle 8 and 16 bit + PCM, u-law and A-law files with one or two channels. + + * src/write_read_test.c tests/lossy_comp_test.c + Added tests for VOC files. + +2001-11-22 Erik de Castro Lopo + + * src/float_cast.h + Added inline asm version of lrint/lrintf for MacOS. Solution provided by + Stephane Letz. + + * src/voc.c + More work on this braindamaged format. The VOC files produced by SoX also + have a number of inconsistencies. + +2001-11-19 Erik de Castro Lopo + + * src/paf.c + Added support for 8 bit PCM PAF files. + + * tests/write_read_test.c + Added tests for 8 bit PAF files. + +2001-11-18 Erik de Castro Lopo + + * tests/pcm_test.c + New test program to test for correct scaling of integer values between + different sized integer containers (ie short -> int). + The new specs for libsndfile state that when the source and destination + containers are of a different size, the most significant bit of the source + value becomes the most significant bit of the destination container. + + * src/pcm.c src/paf.c + Modified to pass the above test program. + + * tests/write_read_test.c tests/lossy_comp_test.c + Modified to work with the new scaling rules. + +2001-11-17 Erik de Castro Lopo + + * src/raw.c tests/write_read_test.c tests/write_read_test.c + Added ability to do raw reads/writes of float, u-law and A-law files. + + * src/*.[ch] examples/*.[ch] tests/*.[ch] + Removed dependance on pcmbitwidth field of SF_INFO struct and moved to new + SF_FORMAT_* types and use of SF_ENDIAN_BIG/LITTLE/CPU. + +2001-11-12 Erik de Castro Lopo + + * src/*.[ch] + Started implmentation of major changes documented in doc/version1.html. + + Removed all usage of off_t which is not part of the ISO C standard. All + places which were using it are now using type long which is the type of + the offset parameter for the fseek function. + This should fix problems on BeOS, MacOS and *BSD like systems which were + failing "make check" because sizeof (long) != sizeof (off_t). + +-------------------------------------------------------------------------------- +This is the boundary between version 1 of the library above and version 0 below. +-------------------------------------------------------------------------------- + +2001-11-11 Erik de Castro Lopo + + * examples/sfplay_beos.cpp + Added BeOS version of sfplay.c. This needs to be compiled using a C++ + compiler so is therefore not built by default. Thanks to Marcus Overhagen + for providing this. + +2001-11-10 Erik de Castro Lopo + + * examples/sfplay.c + New example file showing how libsndfile can be used to read and play a + sound file. + At the moment on Linux is supported. Others will follow in the near future. + +2001-11-09 Erik de Castro Lopo + + * src/pcm.c + Fixed problem with normalisation code where a value of 1.0 could map to + a value greater than MAX_SHORT or MAX_INT. Thanks to Roger Dannenberg for + pointing this out. + +2001-11-08 Erik de Castro Lopo + + * src/pcm.c + Fixed scaling issue when reading/writing 8 bit files using + sf_read/sf_write_short (). + On read, values are scaled so that the most significant bit in the char + ends up in the most significant bit of the short. On write, values are + scaled so that most significant bit in the short ends up as the most + significant bit in the char. + +2001-11-07 Erik de Castro Lopo + + * src/au.c src/sndfile.c + Added support for 32 bit float data in big and little endian AU files. + + * tests/write_read_test.c + Added tests for 32 bit float data in AU files. + +2001-11-06 Erik de Castro Lopo + + * tests/lossy_comp_test.c + Finalised testing of stereo files where possible. + +2001-11-05 Erik de Castro Lopo + + * src/wav_ms_adpcm.c + Fixed bug in writing stereo MS ADPCM WAV files. Thanks to Xu Xin for + pointing out this problem. + +2001-10-24 Erik de Castro Lopo + + * src/wav_ms_adpcm.c + Modified function srate2blocksize () to handle 44k1Hz stereo files. + +2001-10-21 Erik de Castro Lopo + + * src/w64.c + Added support for Sonic Foundry 64 bit WAV format. As Linux (my main + development platform) does not yet support 64 bit file offsets by default, + current handling of this file format treats everything as 32 bit and fails + openning the file, if it finds anything that goes beyond 32 bit values. + + * src/sndfile.[hc] src/common.h src/Makefile.am + Added hooks for W64 support. + +2001-10-21 Erik de Castro Lopo + + * configure.in + Added more warnings options to CFLAGS when the gcc compiler is detected. + + * src/*.[ch] tests/*.c examples/*.c + Started fixing the warning messages due to the new CFLASG. + + * src/voc.c + More work on VOC file read/writing. + + * src/paf.c + Found that PAF files were not checking the normalisation flag when reading + or writing floats and doubles. Fixed it. + + * tests/floating_point_test.c + Added specific test for the above problem. + + * src/float_cast.h src/pcm.c + Added a section for Win32 to define lrint () and lrintf () in the header + and implement it in the pcm.c + +2001-10-20 Erik de Castro Lopo + + * sndfile-config.in m4/sndfile.m4 + These files were donated by Conrad Parker who also provided instructions + on how to install them using autoconf/automake. + + * src/float_cast.h + Fiddled around with this file some more. On Linux and other gcc supported + OSes use the C99 functions lrintf() and lrint() for casting from floating + point to int without incurring the huge perfromance penalty (particularly + on the i386 family) caused by the regular C cast from float to int. + These new C99 functions replace the FLOAT_TO_* and DOUBLE_TO_* macros which + I had been playing with. + + * configure.in m4/lrint.m4 m4/lrintf.m4 + Add detection of these functions. + +2001-10-17 Erik de Castro Lopo + + * src/voc.c + Completed code for reading VOC files containing a single audio data + segment. + Started implementing code to handle files with multiple VOC_SOUND_DATA + segments but couldn't be bothered finishing it. Multiple segment files can + have different sample rates for different sections and other nasties like + silence and repeat segments. + +2001-10-16 Erik de Castro Lopo + + * src/common.h src/*.c + Removed SF_PRIVATE struct field fdata and replaced it with extra_data. + + * src/voc.c + Further development of the read part of this woefult file format. + +2001-10-04 Erik de Castro Lopo + + * src/float_cast.h + Implemented gcc and i386 floating point to int cast macros. Standard cast + will be used when not on gcc for i385. + + * src/pcm.c + Modified all uses of FLOAT/DOUBLE_TO_INT and FLOAT/DOUBLE_TO_SHORT casts to + comply with macros in float_cast.h. + +2001-10-04 Erik de Castro Lopo + + * src/voc.c + Changed the TYPE_xxx enum names to VOC_TYPE_xxx to prevent name clashes + on MacOS with CodeWarrior 6.0. + + * MacOS/MacOS-readme.txt + Updated the compile instructions. Probably still need work as I don't have + access to a Mac. + +2001-10-01 Erik de Castro Lopo + + * src/wav.c src/aiff.c common.c + Changed all references to snprintf to LSF_SNPRINTF and all vsnprintf to + LSF_VSNPRINTF. LSF_VSNPRINTF and LSF_VSNPRINTF are defined in common.h. + + * src/common.h + Added checking of HAVE_SNPRINTF and HAVE_VSNPRINTF and defining + LSF_VSNPRINTF and LSF_VSNPRINTF to appropriate values. + + * src/missing.c + New file containing a minimal implementation of snprintf and vsnprintf + functions named missing_snprintf and missing_vsnprintf respectively. These + are only compliled into the binary if snprintf and/or vsnprintf are not + available. + +2001-09-29 Erik de Castro Lopo + + * src/ircam.c + New file to handle Berkeley/IRCAM/CARL files. + + * src/sndfile.c src/common.h + Modified for IRCAM handling. + + * tests/*.c + Added tests for IRCAM files. + +2001-09-27 Erik de Castro Lopo + + * src/wav.c + Apparently microsoft windows (tm) doesn't like ulaw and Alaw WAV files with + 20 byte format chunks (contrary to ms's own documentation). Fixed the WAV + header writing code to generate smaller ms compliant ulaw and Alaw WAV + files. + +2001-09-17 Erik de Castro Lopo + + * tests/stdio_test.sh tests/stdio_test.c + Shell script was rewritten as a C program due to incompatibilities of the + sh shell on Linux and Solaris. + +2001-09-16 Erik de Castro Lopo + + * tests/stdio_test.sh tests/stdout_test.c tests/stdin_test.c + New test programs to verify the correct operation of reading from stdin and + writing to stdout. + + * src/sndfile.c wav.c au.c nist.c paf.c + Fixed a bugs uncovered by the new test programs above. + +2001-09-15 Erik de Castro Lopo + + * src/sndfile.c wav.c + Fixed a bug preventing reading a file from stdin. Found by T. Narita. + +2001-09-12 Erik de Castro Lopo + + * src/common.h + Fixed a problem on OpenBSD 2.9 which was causing sf_seek() to fail on IMA + WAV files. Root cause was the declaration of the func_seek typedef not + matching the functions it was actually being used to point to. In OpenBSD + sizeof (off_t) != sizeof (int). Thanks to Heikki Korpela for allowing me + to log into his OpenBSD machine to debug this problem. + +2001-09-03 Erik de Castro Lopo + + * src/sndfile.c + Implemented sf_command ("norm float"). + + * src/*.c + Implemented handling of sf_command ("set-norm-float"). Float normalization + can now be turned on and off. + + * tests/double_test.c + Renamed to floating_point_test.c. Modified to include tests for all scaled + reads and writes of floats and doubles. + + * src/au_g72x.c + Fixed bug in normalization code found with improved floating_point_test + program. + + * src/wav.c + Added code for parsing 'INFO' and 'LIST' chunks. Will be used for extract + text annotations from WAV files. + + * src/aiff.c + Added code for parsing '(c) ' and 'ANNO' chunks. Will be used for extract + text annotations from WAV files. + +2001-09-02 Erik de Castro Lopo + + * examples/sf_info.c example/Makefile.am + Renamed to sndfile_info.c. The program sndfile_info will now be installed + when the library is installed. + + * src/float_cast.h + New file defining floating point to short and int casts. These casts will + eventually replace all flot and double casts to short and int. See comments + at the top of the file for the reasoning. + + * src/*.c + Changed all default float and double casts to short or int with macros + defined in floatcast.h. At the moment these casts do nothing. They will be + replaced with faster float to int cast operations in the near future. + +2001-08-31 Erik de Castro Lopo + + * tests/command_test.c + New file for testing sf_command () functionality. + + * src/sndfile.c + Revisiting of error return values of some functions. + Started implementing sf_command () a new function will allow on-the-fly + modification of library behaviour, or instance, sample value scaling. + + * src/common.h + Added hook for format specific sf_command () calls to SNDFILE struct. + + * doc/api.html + Updated and errors corrected. + + * doc/command.html + New documentation file explaining new sf_command () function. + +2001-08-11 Erik de Castro Lopo + + * src/sndfile.c + Fixed error return values from sf_read*() and sf_write*(). There were + numerous instances of -1 being returned through size_t. These now all set + error int the SF_PRIVATE struct and return 0. Thanks to David Viens for + spotting this. + +2001-08-01 Erik de Castro Lopo + + * src/common.c + Fixed use of va_arg() calls that were causing warning messages with the + latest version of gcc (thanks Maurizio Umberto Puxeddu). + +2001-07-25 Erik de Castro Lopo + + * src/*.c src/sfendian.h + Moved definition of MAKE_MARKER macro to sfendian.h + +2001-07-23 Erik de Castro Lopo + + * src/sndfile.c + Modified sf_get_lib_version () so that version string will be visible using + the Unix strings command. + + * examples/Makefile.am examples/sfinfo.c + Renamed sfinfo program and source code to sf_info. This prevents a name + clash with the program included with libaudiofile. + +2001-07-22 Erik de Castro Lopo + + * tests/read_seek_test.c tests/lossy_comp_test.c + Added tests for sf_read_float () and sf_readf_float (). + + * src/voc.c + New files for handling Creative Voice files (not complete). + + * src/samplitude.c + New files for handling Samplitude files (not complete). + +2001-07-21 Erik de Castro Lopo + + * src/aiff.c src/au.c src/paf.c src/svx.c src/wav.c + Converted these files to using psf_binheader_readf() function. Will soon be + ready to attempt to make reading writing from pipes work reliably. + + * src/*.[ch] + Added code for sf_read_float () and sf_readf_float () methods of accessing + file data. + +2001-07-20 Erik de Castro Lopo + + * src/paf.c src/wav_gsm610.c + Removed two printf()s which had escaped notice for some time (thanks + Sigbjørn Skjæret). + +2001-07-19 Erik de Castro Lopo + + * src/wav_gsm610.c + Fixed a bug which prevented GSM 6.10 encoded WAV files generated by + libsndfile from being played in Windoze (thanks klay). + +2001-07-18 Erik de Castro Lopo + + * src/common.[ch] + Implemented psf_binheader_readf() which will do for file header reading what + psf_binheader_writef() did for writing headers. Will eventually allow + libsndfile to read and write from pipes, including named pipes. + +2001-07-16 Erik de Castro Lopo + + * MacOS/config.h Win32/config.h + Attempted to bring these two files uptodate with src/config.h. As I don't + have access to either of these systems support for them may be completely + broken. + +2001-06-18 Erik de Castro Lopo + + * src/float32.c + Fixed bug for big endian processors that can't read 32 bit IEEE floats. Now + tested on Intel x86 and UltraSparc processors. + +2001-06-13 Erik de Castro Lopo + + * src/aiff.c + Modified to allow REX files (from Propellorhead's Recycle and Reason + programs) to be read. + REX files are basically an AIFF file with slightly unusual sequence of + chunks (AIFF files are supposed to allow any sequence) and some extra + application specific information. + Not yet able to write a REX file as the details of the application specific + data is unknown. + +2001-06-12 Erik de Castro Lopo + + * src/wav.c + Fixed endian bug when reading PEAK chunk on big endian machines. + + * src/common.c + Fixed endian bug when reading PEAK chunk on big endian machines with + --enable-force-broken-float configure option. + Fix psf_binheader_writef for (FORCE_BROKEN_FLOAT ||______) + +2001-06-07 Erik de Castro Lopo + + * configure.in src/config.h.in + Removed old CAN_READ_WRITE_x86_IEEE configure variable now that float + capabilities are detected at run time. + Added FORCE_BROKEN_FLOAT to allow testing of broken float code on machines + where the processor can in fact handle floats correctly. + + * src/float32.c + Rejigged code reading and writing of floats on broken processors. + + * m4/ + Removed this directory and all its files as they are no longer needed. + +2001-06-05 Erik de Castro Lopo + + * tests/peak_chunk_test.c + New test to validate reading and writing of peak chunk. + + * examples/sfconvert + Added -float32 option. + + * src/*.c + Changed all error return values to negative values (ie the negative of what + they were). + + * src/sndfile.c tests/error_test.c + Modified to take account of the previous change. + +2001-06-04 Erik de Castro Lopo + + * src/float32.c + File renamed from wav_float.c and renamed function to something more + general. + Added runtime detection of floating point capabilities. + Added recording of peaks during write for generation of PEAK chunk. + + * src/wav.c src/aiff.c + Added handing for PEAK chunk for floating point files. PEAK is read when the + file headers are read and generated when the file is closed. Logic is in + place for adding PEAK chunk to end of file when writing to a pipe (reading + and writing from/to pipe to be implemented soon). + + * src/sndfile.c + Modified sf_signal_max () to use PEAK values if present. + +2001-06-03 Erik de Castro Lopo + + * src/*.c + Added pcm_read_init () and pcm_write_init () to src/pcm.c and removed all + other calls to functions in this file from the filetype specific files. + + * src/*.c + Added alaw_read_init (), alaw_write_int (), ulaw_read_init () and + ulaw_write_init () and removed all other calls to functions in alaw.c and + ulaw.c from the filetype specific files. + + * tests/write_read_test.c + Added tests to validate sf_seek () on all file types. + + * src/raw.c + Implemented raw_seek () function to fix a bug where + sf_seek (file, 0, SEEK_SET) on a RAW file failed. + + * src/paf.c + Fixed a bug in paf24_seek () found due to added seeks tests in + tests/write_read_test.c + +2001-06-01 Erik de Castro Lopo + + * tests/read_seek_test.c + Fixed a couple of broken binary files. + + * src/aiff.c src/wav.c + Added handling of PEAK chunks on file read. + +2001-05-31 Erik de Castro Lopo + + * check_libsndfile.py + New file for the regression testing of libsndfile. + check_libsndfile.py is a Python script which reads in a file containing + filenames of audio files. Each file is checked by running the examples/sfinfo + program on them and checking for error or warning messages in the libsndfile + log buffer. + + * check_libsndfile.list + This is an example list of audio files for use with check_libsndfile.py + + * tests/lossy_comp_test.c + Changed the defined value of M_PI for math header files which don't have it. + This fixed validation test failures on MetroWerks compilers. Thanks to Lord + Praetor Satanus of Acheron for bringing this to my attention. + +2001-05-30 Erik de Castro Lopo + + * src/common.[ch] + Removed psf_header_setf () which was no longer required after refactoring + and simplification of header writing. + Added 'z' format specifier to psf_binheader_writef () for zero filling header + with N bytes. Used by paf.c and nist.c + + * tests/check_log_buffer.c + New file implementing check_log_buffer () which reads the log buffer of a + SNDFILE* object and searches for error and warning messages. Calls exit () + if any are found. + + * tests/*.c + Added calls to check_log_buffer () after each call to sf_open_XXX (). + +2001-05-29 Erik de Castro Lopo + + * src/wav.c src/wav_ms_adpcm.c src/wav_gsm610.c + Major rehack of header writing using psf_binheader_writef (). + +2001-05-28 Erik de Castro Lopo + + * src/wav.c src/wav_ima_adpcm.c + Major rehack of header writing using psf_binheader_writef (). + +2001-05-27 Erik de Castro Lopo + + * src/wav.c + Changed return type of get_encoding_str () to prevent compiler warnings on + Mac OSX. + + * src/aiff.c src/au.c + Major rehack of header writing using psf_binheader_writef (). + +2001-05-25 Erik de Castro Lopo + + * src/common.h src/common.c + Added comments. + Name of log buffer changed from strbuffer to logbuffer. + Name of log buffer index variable changed from strindex to logindex. + + * src/*.[ch] + Changed name of internal logging function from psf_sprintf () to + psf_log_printf (). + Changed name of internal header generation functions from + psf_[ab]h_printf () to psf_asciiheader_printf () and + psf_binheader_writef (). + Changed name of internal header manipulation function psf_hsetf () to + psf_header_setf (). + +2001-05-24 Erik de Castro Lopo + + * src/nist.c + Fixed reading and writing of sample_byte_format header. "01" means little + endian and "10" means big endian regardless of bit width. + + * configure.in + Detect Mac OSX and disable -Wall and -pedantic gcc options. Mac OSX is + way screwed up and spews out buckets of warning messages from the system + headers. + Added --disable-gcc-opt configure option (sets gcc optimisation to -O0 ) for + easier debugging. + Made decision to harmonise source code version number and .so library + version number. Future releases will stick to this rule. + + * doc/new_file_type.HOWTO + New file to document the addition of new file types to libsndfile. + +2001-05-23 Erik de Castro Lopo + + * src/nist.c + New file for reading/writing Sphere NIST audio file format. + Originally requested by Elis Pomales in 1999. + Retrieved from unstable (and untouched for 18 months) branch of libsndfile. + Some vital information gleaned from the source code to Bill Schottstaedt's + sndlib library : ftp://ccrma-ftp.stanford.edu/pub/Lisp/sndlib.tar.gz + Currently reading and writing 16, 24 and 32 bit, big-endian and little + endian, stereo and mono files. + + * src/common.h src/common.c + Added psf_ah_printf () function to help construction of ASCII headers (ie NIST). + + * configure.in + Added test for vsnprintf () required by psf_ah_printf (). + + * tests/write_read_test.c + Added tests for supported NIST files. + +2001-05-22 Erik de Castro Lopo + + * tests/write_read_test.c + Added tests for little endian AIFC files. + + * src/aiff.c + Minor re-working of aiff_open_write (). + Added write support for little endian PCM encoded AIFC files. + +2001-05-13 Erik de Castro Lopo + + * src/aiff.c + Minor re-working of aiff_open_read (). + Added read support for little endian PCM encoded AIFC files from the Mac + OSX CD ripper program. Guillaume Lessard provided a couple of sample files + and a working patch. + The patch was not used as is but gave a good guide as to what to do. + +2001-05-11 Erik de Castro Lopo + + * src/sndfile.h + Fixed comments about endian-ness of WAV and AIFF files. Guillaume Lessard + pointed out the error. + +2001-04-23 Erik de Castro Lopo + + * examples/make_sine.c + Re-write of this example using sample rate and required frequency in Hz. + +2001-02-11 Erik de Castro Lopo + + * src/sndfile.c + Fixed bug that prevented known file types from being read as RAW PCM data. + +2000-12-16 Erik de Castro Lopo + + * src/aiff.c + Added handing of COMT chunk. + +2000-11-16 Erik de Castro Lopo + + * examples/sfconvert.c + Fixed bug in normalisatio code. Pointed out by Johnny Wu. + +2000-11-08 Erik de Castro Lopo + + * Win32/config.h + Fixed the incorrect setting of HAVE_ENDIAN_H parameter. Win32 only issue. + +2000-10-27 Erik de Castro Lopo + + * tests/Makefile.am + Added -lm for write_read_test_LDADD. + +2000-10-16 Erik de Castro Lopo + + * src/sndfile.c src/au.c + Fixed bug which prevented writing of G723 24kbps AU files. + + * tests/lossy_comp_test.c + Corrrection to options for G723 tests. + + * configure.in + Added --disable-gcc-pipe option for DJGPP compiler (gcc on MS-DOS) which + doesn't allow gcc -pipe option. + +2000-09-03 Erik de Castro Lopo + + * src/ulaw.c src/alaw.c src/wav_imaadpcm.c src/msadpcm.c src/wav_gsm610.c + Fixed normailsation bugs shown up by new double_test program. + +2000-08-31 Erik de Castro Lopo + + * src/pcm.c + Fixed bug in normalisation code (spotted by Steve Lhomme). + + * tests/double_test.c + New file to test scaled and unscaled sf_read_double() and sf_write_double() + functions. + +2000-08-28 Erik de Castro Lopo + + * COPYING + Changed to the LGPL COPYING file (spotted by H. S. Teoh). + +2000-08-21 Erik de Castro Lopo + + * src/sndfile.h + Removed prototype of unimplemented function sf_get_info(). Added prototype + for sf_error_number() Thanks to Sigbjørn Skjæret for spotting these. + +2000-08-18 Erik de Castro Lopo + + * src/newpcm.h + New file to contain a complete rewrite of the PCM data handling. + +2000-08-15 Erik de Castro Lopo + + * src/sndfile.c + Fixed a leak of FILE* pointers in sf_open_write(). Thanks to Sigbjørn + Skjæret for spotting this one. + +2000-08-13 Erik de Castro Lopo + + * src/au_g72x.c src/G72x/g72x.c + Added G723 encoded AU file support. + + * tests/lossy_comp_test.c + Added tests for G721 and G723 encoded AU files. + +2000-08-06 Erik de Castro Lopo + + * all files + Changed the license to LGPL. Albert Faber who had copyright on + Win32/unistd.h gave his permission to change the license on that file. All + other files were either copyright erikd AT mega-nerd DOT com or copyright + under a GPL/LGPL compatible license. + +2000-08-06 Erik de Castro Lopo + + * tests/lossy_comp_test.c + Fixed incorrect error message. + + * src/au_g72x.c src/G72x/* + G721 encoded AU files now working. + + * Win32/README-Win32.txt + Replaced this file with a new one which gives a full explanation + of how to build libsndfile under Win32. Thanks to Mike Ricos. + +2000-08-05 Erik de Castro Lopo + + * src/*.[ch] + Removed double leading underscores from the start of all variable and + function names. Identifiers with a leading underscores are reserved + for use by the compiler. + + * src/au_g72x.c src/G72x/* + Continued work on G721 encoded AU files. + +2000-07-12 Erik de Castro Lopo + + * src/G72x/* + New files for reading/writing G721 and G723 ADPCM audio. These files + are from a Sun Microsystems reference implementation released under a + free software licence. + Extensive changes to this code to make it fit in with libsndfile. + See the ChangeLog in this directory for details. + + * src/au_g72x.c + New file for G721 encoded AU files. + +2000-07-08 Erik de Castro Lopo + + * libsndfile.spec.in + Added a spec file for making RPMs. Thanks to Josh Green for supplying this. + +2000-06-28 Erik de Castro Lopo + + * src/sndfile.c src/sndfile.h + Add checking for and handling of header-less u-law encoded AU/SND files. + Any file with a ".au" or ".snd" file extension and without the normal + AU file header is treated as an 8kHz, u-law encoded file. + + * src/au.h + New function for opening a headerless u-law encoded file for read. + +2000-06-04 Erik de Castro Lopo + + * src/paf.c + Add checking for files shorter than minimal PAF file header length. + +2000-06-02 Erik de Castro Lopo + + * tests/write_read_test.c + Added extra sf_perror() calls when sf_write_XXXX fails. + +2000-05-29 Erik de Castro Lopo + + * src/common.c + Modified usage of va_arg() macro to work correctly on PowerPC + Linux. Thanks to Kyle Wheeler for giving me ssh access to his + machine while I was trying to track this down. + + * configure.in src/*.[ch] + Sorted out some endian-ness issues brought up by PowerPC Linux. + + * tests/read_seek_test.c + Added extra debugging for when tests fail. + +2000-05-18 Erik de Castro Lopo + + * src/wav.c + Fixed bug in GSM 6.10 handling for big-endian machines. Thanks + to Sigbjørn Skjæret for reporting this. + +2000-04-25 Erik de Castro Lopo + + * src/sndfile.c src/wav.c src/wav_gsm610.c + Finallised writing of GSM 6.10 WAV files. + + * tests/lossy_comp_test.c + Wrote new test code for GSM 6.10 files. + + * examples/sfinfo.c + Fixed incorrect format in printf() statement. + +2000-04-06 Erik de Castro Lopo + + * src/sndfile.h.in + Fixed comments about sf_perror () and sf_error_str (). + +2000-03-14 Erik de Castro Lopo + + * configure.in + Fixed --enable-justsrc option. + +2000-03-07 Erik de Castro Lopo + + * wav.c + Fixed checking of bytespersec field of header. Still some weirdness + with some files. + +2000-03-05 Erik de Castro Lopo + + * tests/lossy_comp_test.c + Added option to test PCM WAV files (sanity check). + Fixed bug in sf_seek() tests. + +2000-02-29 Erik de Castro Lopo + + * src/sndfile.c src/wav.c + Minor changes to allow writing of GSM 6.10 WAV files. + +2000-02-28 Erik de Castro Lopo + + * configure.in Makefile.am src/Makefile.am + Finally got around to figuring out how to build a single library from + multiple source directories. + Reading GSM 6.10 files now seems to work. + +2000-01-03 Erik de Castro Lopo + + * src/wav.c + Added more error reporting in read_fmt_chunk(). + +1999-12-21 Erik de Castro Lopo + + * examples/sfinfo.c + Modified program to accept multiple filenames from the command line. + +1999-11-27 Erik de Castro Lopo + + * src/wav_ima_adpcm.c + Moved code around in preparation to adding ability to read/write IMA ADPCM + encoded AIFF files. + +1999-11-16 Erik de Castro Lopo + + * src/common.c + Fixed put_int() and put_short() macros used by _psf_hprintf() which were + causing seg. faults on Sparc Solaris. + +1999-11-15 Erik de Castro Lopo + + * src/common.c + Added string.h to includes. Thanks to Sigbjxrn Skjfret. + + * src/svx.c + Fixed __svx_close() function to ensure FORM and BODY chunks are correctly + set. + +1999-10-01 Erik de Castro Lopo + + * src/au.c + Fixed handling of incorrect size field in AU header on read. Thanks to + Christoph Lauer for finding this problem. + +1999-09-28 Erik de Castro Lopo + + * src/aiff.c + Fixed a bug with incorrect SSND chunk length being written. This also lead + to finding an minor error in AIFF header parsing. Thanks to Dan Timis for + pointing this out. + +1999-09-24 Erik de Castro Lopo + + * src/paf.c + Fixed a bug with reading and writing 24 bit stereo PAF files. This problem + came to light when implementing tests for the new functions which operate + in terms of frames rather than items. + +1999-09-23 Erik de Castro Lopo + + * src/sndfile.c + Modified file type detection to use first 12 bytes of file rather than + file name extension. Required this because NIST files use the same + filename extension as Microsoft WAV files. + + * src/sndfile.c src/sndfile.h + Added short, int and double read/write functions which work in frames + rather than items. This was originally suggested by Maurizio Umberto + Puxeddu. + +1999-09-22 Erik de Castro Lopo + + * src/svx.c + Finished off implementation of write using __psf_hprintf(). + +1999-09-21 Erik de Castro Lopo + + * src/common.h + Added a buffer to SF_PRIVATE for writing the header. This is required + to make generating headers for IFF/SVX files easier as well as making + it easier to do re-write the headers which will be required when + sf_rewrite_header() is implemented. + + * src/common.c + Implemented __psf_hprintf() function. This is an internal function + which is documented briefly just above the code. + +1999-09-05 Erik de Castro Lopo + + * src/sndfile.c + Fixed a bug in sf_write_raw() where it was returning incorrect values + (thanks to Richard Dobson for finding this one). Must put in a test + routine for sf_read_raw and sf_write_raw. + + * src/aiff.c + Fixed default FORMsize in __aiff_open_write (). + + * src/sndfile.c + Added copy of filename to internal data structure. IFF/SVX files + contain a NAME header chunk. Both sf_open_read() and sf_open_write() + copy the file name (less the leading path information) to the + filename field. + + * src/svx.c + Started implementing writing of files. + +1999-08-04 Erik de Castro Lopo + + * src/svx.c + New file for reading/writing 8SVX and 16SVX files. + + * src/sndfile.[ch] src/common.h + Changes for SVX files. + + * src/aiff.c + Fixed header parsing when unknown chunk is found. + +1999-08-01 Erik de Castro Lopo + + * src/paf.c + New file for reading/writing Ensoniq PARIS audio file format. + + * src/sndfile.[ch] src/common.h + Changes for PAF files. + + * src/sndfile.[ch] + Added stuff for sf_get_lib_version() function. + + +1999-07-31 Erik de Castro Lopo + + * src/sndfile.h MacOS/config.h + Fixed minor MacOS configuration issues. + +1999-07-30 Erik de Castro Lopo + + * MacOS/ + Added a new directory for the MacOS config.h file and the + readme file. + + * src/aiff.c + Fixed calculation of datalength when reading SSND chunk. Thanks to + Sigbjørn Skjæret for pointing out this error. + +1999-07-29 Erik de Castro Lopo + + * src/sndfile.c src/sndfile.h src/raw.c + Further fixing of #includes for MacOS. + +1999-07-25 Erik de Castro Lopo + + * src/wav.c src/aiff.c + Added call to ferror () in main header parsing loop of __XXX_open_read + functions. This should fix problems on platforms (MacOS, AmigaOS) where + fseek()ing or fread()ing beyond the end of the file puts the FILE* + stream in an error state until clearerr() is called. + + * tests/write_read_test.c + Added tests for RAW header-less PCM files. + + * src/common.h + Moved definition of struct tribyte to pcm.c which is the only place + which needs it. + + * src/pcm.c + Modified all code which assumed sizeof (struct tribyte) == 3. This code + did not work on MacOS. Thanks to Ben "Jacobs" for pointing this out. + + * src/au.c + Removed from list of #includes (not being used). + + * src/sndfile.c + Added MacOS specific #ifdef to replace . + + * src/sndfile.h + Added MacOS specific #ifdef to replace . + + * src/sndfile.h + Added MacOS specific typedef for off_t. + + * MacOS-readme.txt + New file with instructions for building libsndfile under MacOS. Thanks + to Ben "Jacobs" for supplying these instructions. + +1999-07-24 Erik de Castro Lopo + + * configure.in + Removed sndfile.h from generated file list as there were no longer + any autoconf substitutions being made. + + * src/raw.c + New file for handling raw header-less PCM files. In order to open these + for read, the user must specify format, pcmbitwidth and channels in the + SF_INFO struct when calling sf_open_read (). + + * src/sndfile.c + Added support for raw header-less PCM files. + +1999-07-22 Erik de Castro Lopo + + * examples/sfinfo.c + Removed options so the sfinfo program always prints out all the information. + +1999-07-19 Erik de Castro Lopo + + * src/alaw.c + New file for A-law encoding (similar to u-law). + + * tests/alaw_test.c + New test program to test the A-law encode/decode lookup tables. + + * tests/lossy_comp_test.c + Added tests for a-law encoded WAV, AU and AULE files. + +1999-07-18 Erik de Castro Lopo + + * src/sndfile.c src/au.c + Removed second "#include ". Thanks to Ben "Jacobs" for pointing + this out. + +1999-07-18 Erik de Castro Lopo + + * tests/ulaw_test.c + New test program to test the u-law encode/decode lookup tables. + +1999-07-16 Erik de Castro Lopo + + * src/sndfile.h + Made corrections to comments on the return values from sf_seek (). + + * src/sndfile.c + Fixed boundary condition checking bug and accounting bug in sf_read_raw (). + +1999-07-15 Erik de Castro Lopo + + * src/au.c src/ulaw.c + Finished implementation of u-law encoded AU files. + + * src/wav.c + Implemented reading and writing of u-law encoded WAV files. + + * tests/ + Changed name of adpcm_test.c to lossy_comp_test.c. This test program + will now be used to test Ulaw and Alaw encoding as well as APDCM. + Added tests for Ulaw encoded WAV files. + +1999-07-14 Erik de Castro Lopo + + * tests/adpcm_test.c + Initialised amp variable in gen_signal() to remove compiler warning. + +1999-07-12 Erik de Castro Lopo + + * src/aiff.c + In __aiff_open_read () prevented fseek()ing beyond end of file which + was causing trouble on MacOS with the MetroWerks compiler. Thanks to + Ben "Jacobs" for pointing this out. + + *src/wav.c + Fixed as above in __wav_open_read (). + +1999-07-01 Erik de Castro Lopo + + * src/wav_ms_adpcm.c + Implemented MS ADPCM encoding. Code cleanup of decoder. + + * tests/adpcm_test.c + Added tests for MS ADPCM WAV files. + + * src/wav_ima_adpcm.c + Fixed incorrect parameter in call to srate2blocksize () from + __ima_writer_init (). + +1999-06-23 Erik de Castro Lopo + + * tests/read_seek_test.c + Added test for 8 bit AIFF files. + +1999-06-18 Erik de Castro Lopo + + * tests/write_read_test.c + Removed test for IMA ADPCM WAV files which is now done in adpcm_test.c + + * configure.in + Added -Wconversion to CFLAGS. + + * src/*.c tests/*.c examples/*.c + Fixed all warnings resulting from use of -Wconversion. + +1999-06-17 Erik de Castro Lopo + + * src/wav.c + Added fact chunk handling on read and write for all non WAVE_FORMAT_PCM + WAV files. + + * src/wav_ima.c + Changed block alignment to be dependant on sample rate. This should make + WAV files created with libsndfile compatible with the MS Windows media + players. + + * tests/adpcm_test.c + Reimplemented adpcm_test_short and implemented adpcm_test_int and + adpcm_test_double. + Now have full testing of IMA ADPCM WAV file read, write and seek. + +1999-06-15 Erik de Castro Lopo + + * src/wav_float.c + Fixed function prototype for x86f2d_array () which was causing ocassional + seg. faults on Sparc Solaris machines. + +1999-06-14 Erik de Castro Lopo + + * src/aiff.c + Fixed bug in __aiff_close where the length fields in the header were + not being correctly calculated before writing. + + * tests/write_read_test.c + Modified to detect the above bug in WAV, AIFF and AU files. + +1999-06-12 Erik de Castro Lopo + + * Win32/* + Added a contribution from Albert Faber to allow libsndfile to compile + under Win32 systems. libsndfile will now be used as part of LAME the + the MPEG 1 Layer 3 encoder (http://internet.roadrunner.com/~mt/mp3/). + +1999-06-11 Erik de Castro Lopo + + * configure.in + Changed to reflect previous changes. + + * src/wav_ima_adpcm.c + Fixed incorrect calculation of bytespersec header field (IMA ADPCM only). + + Fixed bug when writing from int or double data to IMA ADPCM file. Will need + to write test code for this. + + Fixed bug in __ima_write () whereby the length of the current block was + calculated incorrectly. Thanks to Jongcheon Park for pointing this out. + +1999-03-27 Erik de Castro Lopo + + * src/*.c + Changed all read/write/lseek function calls to fread/fwrite/ + fseek/ftell and added error checking of return values from + fread and fwrite in critical areas of the code. + + * src/au.c + Fixed incorrect datasize element in AU header on write. + + * tests/error_test.c + Add new test to check all error values have an associated error + string. This will avoid embarrassing real world core dumps. + +1999-03-23 Erik de Castro Lopo + + * src/wav.c src/aiff.c + Added handling for unknown chunk markers in the file. + +1999-03-22 Erik de Castro Lopo + + * src/sndfile.c + Filled in missing error strings in SndfileErrors array. Missing entries + can cause core dumps when calling sf_error-str (). Thanks to Sam + for finding this problem. + +1999-03-21 Erik de Castro Lopo + + * src/wav_ima_adpcm.c + Work on wav_ms_adpcm.c uncovered a bug in __ima_read () when reading + stereo files. Caused by not adjusting offset into buffer of decoded + samples for 2 channels. A similar bug existed in __ima_write (). + Need a test for stereo ADPCM files. + + * src/wav_ms_adpcm.c + Decoder working correctly. + +1999-03-18 Erik de Castro Lopo + + * configure.in Makefile.am + Added --enable-justsrc configuration variable sent by Sam + . + + * src/wav_ima_adpcm.c + Fixed bug when reading beyond end of data section due to not + checking pima->blockcount. + This uncovered __ima_seek () bug due to pima->blockcount being set + before calling __ima_init_block (). + +1999-03-17 Erik de Castro Lopo + + * src/wav.c + Started implementing MS ADPCM decoder. + If file is WAVE_FORMAT_ADPCM and length of data chunk is odd, this + encoder seems to add an extra byte. Why not just give an even data + length? + +1999-03-16 Erik de Castro Lopo + + * src/wav.c + Split code out of wav.c to create wav_float.c and wav_ima_adpcm.c. + This will make it easier to add and debug other kinds of WAV files + in future. + +1999-03-14 Erik de Castro Lopo + + * tests/ + Added adpcm_test.c which implements test functions for + IMA ADPCM reading/writing/seeking etc. + + * src/wav.c + Fixed many bugs in IMA ADPCM encoder and decoder. + +1999-03-11 Erik de Castro Lopo + + * src/wav.c + Finished implementing IMA ADPCM encoder and decoder (what a bitch!). + +1999-03-03 Erik de Castro Lopo + + * src/wav.c + Started implementing IMA ADPCM decoder. + +1999-03-02 Erik de Castro Lopo + + * src/sndfile.c + Fixed bug where the sf_read_XXX functions were returning a + incorrect read count when reading past end of file. + Fixed bug in sf_seek () when seeking backwards from end of file. + + * tests/read_seek_test.c + Added multiple read test to short_test(), int_test () and + double_test (). + Added extra chunk to all test WAV files to test that reading + stops at end of 'data' chunk. + +1999-02-21 Erik de Castro Lopo + + * tests/write_read_test.c + Added tests for little DEC endian AU files. + + * src/au.c + Add handling for DEC format little endian AU files. + +1999-02-20 Erik de Castro Lopo + + * src/aiff.c src/au.c src/wav.c + Add __psf_sprintf calls during header parsing. + + * src/sndfile.c src/common.c + Implement sf_header_info (sndfile.c) function and __psf_sprintf (common.c). + + * tests/write_read_test.c + Added tests for 8 bit PCM files (WAV, AIFF and AU). + + * src/au.c src/aiff.c + Add handling of 8 bit PCM data format. + + * src/aiff.c + On write, set blocksize in SSND chunk to zero like everybody else. + +1999-02-16 Erik de Castro Lopo + + * src/pcm.c: + Fixed bug in let2s_array (cptr was not being initialised). + + * src/sndfile.c: + Fixed bug in sf_read_raw and sf_write_raw. sf_seek should + now work when using these functions. + +1999-02-15 Erik de Castro Lopo + + * tests/write_read_test.c: + Force test_buffer array to be double aligned. Sparc Solaris + requires this. + +1999-02-14 Erik de Castro Lopo + + * src/pcm.c: + Fixed a bug which was causing errors in the reading + and writing of 24 bit PCM files. + + * doc/api.html + Finished of preliminary documentaion. + +1999-02-13 Erik de Castro Lopo + + * src/aiff.c: + Changed reading of 'COMM' chunk to avoid reading an int + which overlaps an int (4 byte) boundary. + diff --git a/INSTALL b/INSTALL new file mode 100644 index 00000000..b42a17ac --- /dev/null +++ b/INSTALL @@ -0,0 +1,182 @@ +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not supports the `VPATH' +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean' before reconfiguring for another +architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. diff --git a/Make.bat b/Make.bat new file mode 100644 index 00000000..62c167f3 --- /dev/null +++ b/Make.bat @@ -0,0 +1,33 @@ +@echo off + +if "%1"=="check" GOTO CHECK +if "%1"=="clean" GOTO CLEAN + +copy /y Win32\sndfile.h src\sndfile.h +copy /y Win32\config.h src\config.h + +nmake -f Win32\Makefile.msvc +goto END + + +:CHECK +nmake -f Win32\Makefile.msvc check +goto END + +:CLEAN +nmake -f Win32\Makefile.msvc clean +goto END + + +:END + + +goto skipArchTag + + Do not edit or modify anything in this comment block. + The arch-tag line is a file identity tag for the GNU Arch + revision control system. + + arch-tag: 8700080b-8d9a-4852-ad8a-8ecd027f1f61 + +:skipArchTag diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 00000000..bae93e10 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,27 @@ +## Process this file with automake to produce Makefile.in + +SUBDIRS = man doc Win32 Octave src examples regtest tests +DIST_SUBDIRS = $(SUBDIRS) +EXTRA_DIST = reconfigure.mk acinclude.m4 libsndfile.spec.in \ + sndfile.pc.in Mingw-make-dist.sh + + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = sndfile.pc + +m4datadir = $(datadir)/aclocal + +test: check-recursive + +# Target to make autogenerated files. +genfiles : + (cd src ; make genfiles) + (cd tests ; make genfiles) + + +## Do not edit or modify anything in this comment block. +## The arch-tag line is a file identity tag for the GNU Arch +## revision control system. +## +## arch-tag: e40c569e-8020-4e95-b774-6b0703614526 + diff --git a/NEWS b/NEWS new file mode 100644 index 00000000..27517fdf --- /dev/null +++ b/NEWS @@ -0,0 +1,131 @@ +Version 1.0.17 (2006-08-31) + * Add sndfile.hh C++ wrapper. + * Update Win32 MinGW build instructions. + * Minor bug fixes and cleanups. + +Version 1.0.16 (2006-04-30) + * Add support for Broadcast (BEXT) chunks in WAV files. + * Implement new commands SFC_GET_SIGNAL_MAX and SFC_GET_MAX_ALL_CHANNELS. + * Add support for RIFX (big endian WAV variant). + * Fix configure script bugs. + * Fix bug in INST and MARK chunk writing for AIFF files. + +Version 1.0.15 (2006-03-16) + * Fix some ia64 issues. + * Fix precompiled DLL. + * Minor bug fixes. + +Version 1.0.14 (2006-02-19) + * Really fix MinGW compile problems. + * Minor bug fixes. + +Version 1.0.13 (2006-01-21) + * Fix for MinGW compiler problems. + * Allow readin/write of instrument chunks from WAV and AIFF files. + * Compile problem fix for Solaris compiler. + * Minor cleanups and bug fixes. + +Version 1.0.12 (2005-09-30) + * Add support for FLAC and Apple's Core Audio Format (CAF). + * Add virtual I/O interface (still needs docs). + * Cygwin and other Win32 fixes. + * Minor bug fixes and cleanups. + +Version 1.0.11 (2004-11-15) + * Add support for SD2 files. + * Add read support for loop info in WAV and AIFF files. + * Add more tests. + * Improve type safety. + * Minor optimisations and bug fixes. + +Version 1.0.10 (2004-06-15) + * Fix AIFF read/write mode bugs. + * Add support for compiling Win32 DLLS using MinGW. + * Fix problems resulting in failed compiles with gcc-2.95. + * Improve test suite. + * Minor bug fixes. + +Version 1.0.9 (2004-03-30) + * Add handling of AVR (Audio Visual Research) files. + * Improve handling of WAVEFORMATEXTENSIBLE WAV files. + * Fix for using pipes on Win32. + +Version 1.0.8 (2004-03-14) + * Correct peak chunk handing for files with > 16 tracks. + * Fix for WAV files with huge number of CUE chunks. + +Version 1.0.7 (2004-02-25) + * Fix clip mode detection on ia64, MIPS and other CPUs. + * Fix two MacOSX build problems. + +Version 1.0.6 (2004-02-08) + * Added support for native Win32 file access API (Ross Bencina). + * New mode to add clippling then a converting from float/double to integer + would otherwise wrap around. + * Fixed a bug in reading/writing files > 2Gig on Linux, Solaris and others. + * Many minor bug fixes. + * Other random fixes for Win32. + +Version 1.0.5 (2003-05-03) + * Added support for HTK files. + * Added new function sf_open_fd() to allow for secure opening of temporary + files as well as reading/writing sound files embedded within larger + container files. + * Added string support for AIFF files. + * Minor bug fixes and code cleanups. + +Version 1.0.4 (2003-02-02) + * Added suport of PVF and XI files. + * Added functionality for setting and retreiving strings from sound files. + * Minor code cleanups and bug fixes. + +Version 1.0.3 (2002-12-09) + * Minor bug fixes. + +Version 1.0.2 (2002-11-24) + * Added support for VOX ADPCM. + * Improved error reporting. + * Added version scripting on Linux and Solaris. + * Minor bug fixes. + +Version 1.0.1 (2002-09-14) + * Added MAT and MAT5 file formats. + * Minor bug fixes. + +Version 1.0.0 (2002-08-16) + * Final release for 1.0.0. + +Version 1.0.0rc6 (2002-08-14) + * Release candidate 6 for the 1.0.0 series. + * MacOS9 fixes. + +Version 1.0.0rc5 (2002-08-10) + * Release candidate 5 for the 1.0.0 series. + * Changed the definition of sf_count_t which was causing problems when + libsndfile was compiled with other libraries (ie WxWindows). + * Minor bug fixes. + * Documentation cleanup. + +Version 1.0.0rc4 (2002-08-03) + * Release candidate 4 for the 1.0.0 series. + * Minor bug fixes. + * Fix broken Win32 "make check". + +Version 1.0.0rc3 (2002-08-02) + * Release candidate 3 for the 1.0.0 series. + * Fix bug where libsndfile was reading beyond the end of the data chunk. + * Added on-the-fly header updates on write. + * Fix a couple of documentation issues. + +Version 1.0.0rc2 (2002-06-24) + * Release candidate 2 for the 1.0.0 series. + * Fix compile problem for Win32. + +Version 1.0.0rc1 (2002-06-24) + * Release candidate 1 for the 1.0.0 series. + +Version 0.0.28 (2002-04-27) + * Last offical release of 0.0.X series of the library. + +Version 0.0.8 (1999-02-16) + * First offical release. diff --git a/Octave/Makefile.am b/Octave/Makefile.am new file mode 100644 index 00000000..146cd29c --- /dev/null +++ b/Octave/Makefile.am @@ -0,0 +1,14 @@ +## Process this file with automake to produce Makefile.in + +EXTRA_DIST = sndfile_load.m sndfile_save.m sndfile_play.m + +octconfigdir = $(exec_prefix)/share/octave/site/m +octconfig_DATA = sndfile_load.m sndfile_save.m sndfile_play.m + + +## Do not edit or modify anything in this comment block. +## The arch-tag line is a file identity tag for the GNU Arch +## revision control system. +## +## arch-tag: 56f1645a-3a13-4846-acc7-8b4abf2904ff + diff --git a/Octave/sndfile_load.m b/Octave/sndfile_load.m new file mode 100644 index 00000000..a2371cc8 --- /dev/null +++ b/Octave/sndfile_load.m @@ -0,0 +1,59 @@ +## Copyright (C) 2002 Erik de Castro Lopo +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2, or (at your option) +## any later version. +## +## This program is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this file. If not, write to the Free Software Foundation, +## 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +## -*- texinfo -*- +## @deftypefn {Function File} {} sndfile_load (@var{filename}) +## Load data from the file given by @var{filename}. +## @end deftypefn + +## Author: Erik de Castro Lopo +## Description: Load the sound data from the given file name + +function [data fs] = sndfile_load (filename) + +if (nargin != 1), + error ("Need an input filename") ; + endif + +samplerate = -1 ; +samplingrate = -1 ; +wavedata = -1 ; + + +eval (sprintf ('load -f %s', filename)) ; + +if (samplerate > 0), + fs = samplerate ; +elseif (samplingrate > 0), + fs = samplingrate ; +else + error ("Not able to find sample rate.") ; + endif + +if (max (size (wavedata)) > 1), + data = wavedata ; +else + error ("Not able to find waveform data.") ; + endif + +endfunction + +# Do not edit or modify anything in this comment block. +# The arch-tag line is a file identity tag for the GNU Arch +# revision control system. +# +# arch-tag: 9d7ed7ce-41fe-4efe-8bde-f5fc6f46bb03 + diff --git a/Octave/sndfile_play.m b/Octave/sndfile_play.m new file mode 100644 index 00000000..b1cccab4 --- /dev/null +++ b/Octave/sndfile_play.m @@ -0,0 +1,66 @@ +## Copyright (C) 2002 Erik de Castro Lopo +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2, or (at your option) +## any later version. +## +## This program is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this file. If not, write to the Free Software Foundation, +## 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +## -*- texinfo -*- +## @deftypefn {Function File} {} sndfile_play (@var{data, fs}) +## Play @var{data} at sample rate @var{fs} using the sndfile-play +## program. +## @end deftypefn + +## Author: Erik de Castro Lopo +## Description: Play the given data as a sound file + +function sndfile_play (data, fs) + +if nargin != 2, + error ("Need two input arguments: data and fs.") ; + endif + +if (max (size (fs)) > 1), + error ("Second parameter fs must be a single value.") ; + endif + +[nr nc] = size (data) ; + +if (nr > nc), + data = data' ; + endif + +samplerate = fs ; +wavedata = data ; + +filename = tmpnam () ; + +cmd = sprintf ("save -mat-binary %s fs data", filename) ; + +eval (cmd) ; + +cmd = sprintf ("sndfile-play %s", filename) ; + +[output, status] = system (cmd) ; + +if (status), + disp (outout) ; + endif + +endfunction + +# Do not edit or modify anything in this comment block. +# The arch-tag line is a file identity tag for the GNU Arch +# revision control system. +# +# arch-tag: 96fb14c8-2b5a-4b93-a576-ab83a6d9026b + diff --git a/Octave/sndfile_save.m b/Octave/sndfile_save.m new file mode 100644 index 00000000..1b888420 --- /dev/null +++ b/Octave/sndfile_save.m @@ -0,0 +1,60 @@ +## Copyright (C) 2002 Erik de Castro Lopo +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2, or (at your option) +## any later version. +## +## This program is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this file. If not, write to the Free Software Foundation, +## 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +## -*- texinfo -*- +## @deftypefn {Function File} {} sndfile_save (@var{filename, data, fs}) +## Save the given @var{data} as audio data to the given at @var{fs}. Set +## the sample rate to @var{fs}. +## @end deftypefn + +## Author: Erik de Castro Lopo +## Description: Save data as a sound file + +function sndfile_save (filename, data, fs) + +if nargin != 3, + error ("Need three input arguments: filename, data and fs.") ; + endif + +if (! isstr (filename)), + error ("First parameter 'filename' is must be a string.") ; + endif + +if (max (size (fs)) > 1), + error ("Second parameter 'fs' must be a single value, not an array or matrix.") ; + endif + +[nr nc] = size (data) ; + +if (nr > nc), + data = data' ; + endif + +samplerate = fs ; +wavedata = data ; + +str = sprintf ("save -mat-binary %s samplerate wavedata", filename) ; + +eval (str) ; + +endfunction + +# Do not edit or modify anything in this comment block. +# The arch-tag line is a file identity tag for the GNU Arch +# revision control system. +# +# arch-tag: 5e44602b-940b-4927-af7c-47639769a40b + diff --git a/README b/README new file mode 100644 index 00000000..134ae490 --- /dev/null +++ b/README @@ -0,0 +1,71 @@ +This is libsndfile, 1.0.17 + +libsndfile is a library of C routines for reading and writing +files containing sampled audio data. + +The src/ directory contains the source code for library itself. + +The doc/ directory contains the libsndfile documentation. + +The examples/ directory contains examples of how to write code using +libsndfile. 'wav32_aiff24' converts a WAV file containing 32 bit floating +point data into a 24 bit PCM AIFF file. 'sndfile2oct' dumps the audio +data of a file in a human readable format. 'sfconvert' is the beginnings +of a audio file format conversion utility. 'make_sine' generates a WAV +file containing one cycle of a sine wave with 4096 sample points in +32 bit floating point format. 'sfinfo' opens a sound file and prints +out information about that file. + +The tests/ directory contains programs which link against libsndfile +and test its functionality. + +The Win32/ directory contains files and documentation to allow libsndfile +to compile under Win32 with the Microsoft Visual C++ compiler. + +The src/GSM610 directory contains code written by Jutta Degener and Carsten +Bormann. Their original code can be found at : + http://kbs.cs.tu-berlin.de/~jutta/toast.html + +The src/G72x directory contains code written and released by Sun Microsystems +under a suitably free license. + + +Win32 +----- +There are detailed instructions for building libsndfile on Win32 in the file + + doc/win32.html + + +MacOSX +------ +Building on MacOSX should be the same as building it on any other Unix. + + +OTHER PLATFORMS +--------------- +To compile libsndfile on platforms which have a Bourne Shell compatible +shell, an ANSI C compiler and a make utility should require no more that +the following three commands : + ./configure + make + make install + +For platforms without the required shell, it is usually sufficient to +create an approriate config.h file in the src/ directory with correct +values for the following #defines (this would work for AmigaOS) : + +#define HAVE_ENDIAN_H 0 +#define GUESS_BIG_ENDIAN 1 +#define GUESS_LITTLE_ENDIAN 0 +#define FORCE_BROKEN_FLOAT 0 + + +CONTACTS +-------- + +libsndfile was written by Erik de Castro Lopo (erikd AT mega-nerd DOT com). +The libsndfile home page is at : + + http://www.mega-nerd.com/libsndfile/ + diff --git a/Win32/Makefile.am b/Win32/Makefile.am new file mode 100644 index 00000000..9ab93fa6 --- /dev/null +++ b/Win32/Makefile.am @@ -0,0 +1,10 @@ +## Process this file with automake to produce Makefile.in + +EXTRA_DIST = README-precompiled-dll.txt testprog.c + +## Do not edit or modify anything in this comment block. +## The arch-tag line is a file identity tag for the GNU Arch +## revision control system. +## +## arch-tag: c28c7540-67f6-47e7-8035-0abbc6a174bc + diff --git a/Win32/Makefile.mingw.in b/Win32/Makefile.mingw.in new file mode 100644 index 00000000..5081cd2b --- /dev/null +++ b/Win32/Makefile.mingw.in @@ -0,0 +1,401 @@ +# MinGW specific Makefile for libsndfile. + +@SET_MAKE@ + +PROGRAMS = sndfile-info.exe sndfile-play.exe sndfile-convert.exe +TARGETS = libsndfile.dll $(PROGRAMS) sndfile.pc src/sndfile.h + +AR = @AR@ +CC = @CC@ +CFLAGS = @CFLAGS@ -Isrc +COMPILER_IS_GCC = @COMPILER_IS_GCC@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +DLLTOOL = @DLLTOOL@ +DLLWRAP = @DLLWRAP@ +INSTALL = @INSTALL@ +INSTALL_DATA = ${INSTALL} -m 644 +install_sh = @install_sh@ +LDFLAGS = @LDFLAGS@ +LN_S = @LN_S@ +OS_SPECIFIC_CFLAGS = @OS_SPECIFIC_CFLAGS@ +OS_SPECIFIC_LINKS = @OS_SPECIFIC_LINKS@ +PEXPORTS = @PEXPORTS@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +RANLIB = @RANLIB@ +STRIP = @STRIP@ + +#==================================================================== +# Target directories. + +bindir = @bindir@ +exec_prefix = @exec_prefix@ +htmldocdir = @htmldocdir@ +includedir = @includedir@ +libdir = @libdir@ +pkgconfigdir = @libdir@/pkgconfig +prefix = @prefix@ + +#==================================================================== +# Targets + +all : $(TARGETS) + +clean : + rm -f $(TARGETS) *.del *.lib src/*.o src/G72x/*.o \ + src/GSM610/*.o tests/*.o tests/*.exe + +install : $(TARGETS) + $(install_sh) -c libsndfile.dll $(libdir) + $(install_sh) -c sndfile.pc $(pkgconfigdir) + @for prog in $(PROGRAMS); do \ + echo "installing ... $$prog" ; \ + $(install_sh) -c "$$prog" $(bindir) ; \ + done; + $(install_sh) -c src/sndfile.h $(includedir) + +uninstall : + rm -f $(libdir)/libsndfile.dll + rm -f $(pkgconfigdir)/sndfile.pc + @for prog in $(PROGRAMS); do \ + rm -f $(bindir)/"$$prog" ; \ + done; + rm -f $(includedir)/sndfile.h + +#==================================================================== + +LINK_OBJS = \ + src/GSM610/add.o \ + src/GSM610/code.o \ + src/GSM610/decode.o \ + src/GSM610/gsm_create.o \ + src/GSM610/gsm_decode.o \ + src/GSM610/gsm_destroy.o \ + src/GSM610/gsm_encode.o \ + src/GSM610/gsm_option.o \ + src/GSM610/long_term.o \ + src/GSM610/lpc.o \ + src/GSM610/preprocess.o \ + src/GSM610/rpe.o \ + src/GSM610/short_term.o \ + src/GSM610/table.o \ + src/G72x/g721.o \ + src/G72x/g723_16.o \ + src/G72x/g723_24.o \ + src/G72x/g723_40.o \ + src/G72x/g72x.o \ + src/aiff.o \ + src/alaw.o \ + src/au.o \ + src/au_g72x.o \ + src/avr.o \ + src/common.o \ + src/double64.o \ + src/dwvw.o \ + src/file_io.o \ + src/dither.o \ + src/float32.o \ + src/command.o \ + src/strings.o \ + src/ima_adpcm.o \ + src/ms_adpcm.o \ + src/nist.o \ + src/ircam.o \ + src/paf.o \ + src/pcm.o \ + src/raw.o \ + src/rx2.o \ + src/sd2.o \ + src/sndfile.o \ + src/svx.o \ + src/txw.o \ + src/sds.o \ + src/ulaw.o \ + src/voc.o \ + src/w64.o \ + src/wav.o \ + src/gsm610.o \ + src/wav_w64.o \ + src/dwd.o \ + src/wve.o \ + src/mat4.o \ + src/mat5.o \ + src/vox_adpcm.o \ + src/ogg.o \ + src/pvf.o \ + src/xi.o \ + src/htk.o + +libsndfile.dll libsndfile.lib : $(LINK_OBJS) + $(DLLWRAP) --def src/libsndfile.def $(LINK_OBJS) --output-lib libsndfile.lib -o libsndfile.dll + $(STRIP) libsndfile.dll + +sndfile-info.exe : examples/sndfile-info.o libsndfile.lib + $(CC) $+ -o $@ + +sndfile-convert.exe : examples/sndfile-convert.o libsndfile.lib + $(CC) $+ -o $@ + +sndfile-play.exe : examples/sndfile-play.o libsndfile.lib + $(CC) $+ -lwinmm -o $@ + +generate.exe : examples/generate.o libsndfile.lib + $(CC) $+ -o $@ + +sndfile.pc : sndfile.pc.in config.status + @./config.status $@ + +config.status : + ./configure + +#==================================================================== +# Testing programs. + +TEST_PROGS = \ + src/test_file_io.exe \ + src/test_log_printf.exe \ + tests/sfversion.exe \ + tests/error_test.exe \ + tests/pcm_test.exe \ + tests/ulaw_test.exe \ + tests/alaw_test.exe \ + tests/dwvw_test.exe \ + tests/command_test.exe \ + tests/floating_point_test.exe \ + tests/headerless_test.exe \ + tests/write_read_test.exe \ + tests/lossy_comp_test.exe \ + tests/peak_chunk_test.exe \ + tests/misc_test.exe \ + tests/string_test.exe \ + tests/win32_test.exe \ + tests/stdio_test.exe \ + tests/stdin_test.exe \ + tests/stdout_test.exe \ + tests/pipe_test.exe \ + tests/benchmark.exe + +check : $(TEST_PROGS) + @echo + @echo + @echo + @echo "============================================================" + src/test_file_io.exe + src/test_log_printf.exe + @echo "============================================================" + @echo + @echo + @echo + tests/error_test.exe + tests/pcm_test.exe + tests/ulaw_test.exe + tests/alaw_test.exe + tests/dwvw_test.exe + tests/command_test.exe ver + tests/command_test.exe norm + tests/command_test.exe format + tests/floating_point_test.exe + tests/headerless_test.exe + @echo "----------------------------------------------------------------------" + @tests/sfversion.exe + @echo "passed common tests" + @echo "----------------------------------------------------------------------" + tests/write_read_test wav + tests/lossy_comp_test wav_ima + tests/lossy_comp_test wav_msadpcm + tests/lossy_comp_test wav_ulaw + tests/lossy_comp_test wav_alaw + tests/lossy_comp_test wav_gsm610 + tests/peak_chunk_test wav + tests/misc_test wav + tests/string_test wav + @echo "----------------------------------------------------------------------" + @tests/sfversion.exe + @echo "passed tests on WAV files." + @echo "----------------------------------------------------------------------" + tests/write_read_test aiff + tests/lossy_comp_test aiff_ulaw + tests/lossy_comp_test aiff_alaw + tests/peak_chunk_test aiff + tests/misc_test aiff + @echo "----------------------------------------------------------------------" + @tests/sfversion.exe + @echo " passed tests on AIFF files." + @echo "----------------------------------------------------------------------" + tests/write_read_test au + tests/lossy_comp_test au_ulaw + tests/lossy_comp_test au_alaw + tests/lossy_comp_test au_g721 + tests/lossy_comp_test au_g723 + tests/misc_test au + @echo "----------------------------------------------------------------------" + @tests/sfversion.exe + @echo " passed tests on AU files." + @echo "----------------------------------------------------------------------" + tests/write_read_test raw + tests/lossy_comp_test raw_ulaw + tests/lossy_comp_test raw_alaw + @echo "----------------------------------------------------------------------" + @tests/sfversion.exe + @echo " passed tests on RAW (header-less) files." + @echo "----------------------------------------------------------------------" + tests/write_read_test paf + tests/misc_test paf + @echo "----------------------------------------------------------------------" + @tests/sfversion.exe + @echo " passed tests on PAF files." + @echo "----------------------------------------------------------------------" + tests/write_read_test svx + tests/misc_test svx + @echo "----------------------------------------------------------------------" + @tests/sfversion.exe + @echo " passed tests on SVX files." + @echo "----------------------------------------------------------------------" + tests/write_read_test nist + tests/misc_test nist + @echo "----------------------------------------------------------------------" + @tests/sfversion.exe + @echo " passed tests on NIST files." + @echo "----------------------------------------------------------------------" + tests/write_read_test ircam + tests/lossy_comp_test ircam_ulaw + tests/lossy_comp_test ircam_alaw + tests/misc_test ircam + @echo "----------------------------------------------------------------------" + @tests/sfversion.exe + @echo " passed tests on IRCAM files." + @echo "----------------------------------------------------------------------" + tests/write_read_test voc + tests/lossy_comp_test voc_ulaw + tests/lossy_comp_test voc_alaw + tests/misc_test voc + @echo "----------------------------------------------------------------------" + @tests/sfversion.exe + @echo " passed tests on VOC files." + @echo "----------------------------------------------------------------------" + tests/write_read_test w64 + tests/lossy_comp_test w64_ima + tests/lossy_comp_test w64_msadpcm + tests/lossy_comp_test w64_ulaw + tests/lossy_comp_test w64_alaw + tests/lossy_comp_test w64_gsm610 + tests/misc_test w64 + @echo "----------------------------------------------------------------------" + @tests/sfversion.exe + @echo " passed tests on W64 files." + @echo "----------------------------------------------------------------------" + tests/write_read_test mat4 + tests/misc_test mat4 + @echo "----------------------------------------------------------------------" + @tests/sfversion.exe + @echo " passed tests on MAT4 files." + @echo "----------------------------------------------------------------------" + tests/write_read_test mat5 + tests/misc_test mat5 + @echo "----------------------------------------------------------------------" + @tests/sfversion.exe + @echo " passed tests on MAT5 files." + @echo "----------------------------------------------------------------------" + tests/write_read_test htk + tests/misc_test htk + @echo "----------------------------------------------------------------------" + @tests/sfversion.exe + @echo " passed tests on HTK files." + @echo "----------------------------------------------------------------------" + tests/write_read_test avr + tests/misc_test avr + @echo "----------------------------------------------------------------------" + @tests/sfversion.exe + @echo " passed tests on AVR files." + @echo "----------------------------------------------------------------------" + @tests/stdio_test.exe + @tests/pipe_test.exe + @echo "----------------------------------------------------------------------" + @tests/sfversion.exe + @echo " passed all tests." + @echo "----------------------------------------------------------------------" + +#==================================================================== +# Test programs. + +src/test_file_io.exe : src/file_io.o src/test_file_io.o libsndfile.lib + $(CC) $+ -o $@ + +# Special case : test_log_printf.c #includes common.c +src/test_log_printf.exe : src/test_log_printf.c src/common.c libsndfile.lib + $(CC) $(CFLAGS) -c src/test_log_printf.c -o src/test_log_printf.o + $(CC) src/test_log_printf.o libsndfile.lib -o $@ + +tests/sfversion.exe : tests/sfversion.o libsndfile.lib + $(CC) $+ -o $@ + +tests/error_test.exe : tests/error_test.o libsndfile.lib + $(CC) $+ -o $@ + +tests/pcm_test.exe : tests/pcm_test.o tests/utils.o libsndfile.lib + $(CC) $+ -o $@ + +tests/ulaw_test.exe : tests/ulaw_test.o tests/utils.o libsndfile.lib + $(CC) $+ -o $@ + +tests/alaw_test.exe : tests/alaw_test.o tests/utils.o libsndfile.lib + $(CC) $+ -o $@ + +tests/dwvw_test.exe : tests/dwvw_test.o tests/utils.o libsndfile.lib + $(CC) $+ -o $@ + +tests/command_test.exe : tests/command_test.o tests/utils.o libsndfile.lib + $(CC) $+ -o $@ + +tests/floating_point_test.exe : tests/floating_point_test.o tests/utils.o tests/dft_cmp.o libsndfile.lib + $(CC) $+ -o $@ + +tests/headerless_test.exe : tests/headerless_test.o tests/utils.o libsndfile.lib + $(CC) $+ -o $@ + +tests/write_read_test.exe : tests/write_read_test.o tests/utils.o libsndfile.lib + $(CC) $+ -o $@ + +tests/lossy_comp_test.exe : tests/lossy_comp_test.o tests/utils.o libsndfile.lib + $(CC) $+ -o $@ + +tests/peak_chunk_test.exe : tests/peak_chunk_test.o tests/utils.o libsndfile.lib + $(CC) $+ -o $@ + +tests/misc_test.exe : tests/misc_test.o tests/utils.o libsndfile.lib + $(CC) $+ -o $@ + +tests/string_test.exe : tests/string_test.o tests/utils.o libsndfile.lib + $(CC) $+ -o $@ + +tests/win32_test.exe : tests/win32_test.o + $(CC) $+ -o $@ + +tests/stdio_test.exe : tests/stdio_test.o tests/utils.o libsndfile.lib + $(CC) $+ -o $@ + +tests/pipe_test.exe : tests/pipe_test.o tests/utils.o libsndfile.lib + $(CC) $+ -o $@ + +tests/stdin_test.exe : tests/stdin_test.o tests/utils.o libsndfile.lib + $(CC) $+ -o $@ + +tests/stdout_test.exe : tests/stdout_test.o tests/utils.o libsndfile.lib + $(CC) $+ -o $@ + +tests/benchmark.exe : tests/benchmark.o libsndfile.lib + $(CC) $+ -o $@ + +# End of Makefile +#==================================================================== +# Do not edit or modify anything in this comment block. +# The arch-tag line is a file identity tag for the GNU Arch +# revision control system. +# +# arch-tag: a588878f-6796-4a47-bbef-83a3aaac0983 diff --git a/Win32/Makefile.msvc b/Win32/Makefile.msvc new file mode 100644 index 00000000..53e55afd --- /dev/null +++ b/Win32/Makefile.msvc @@ -0,0 +1,577 @@ + +# Set the value of $(MSVCDir) for your installation. +# Please note that none of the directory names are allowed to have spaces +# in them. You must use the short name. +# +# "C:\Program Files\Microsoft Visual Studio\VC98" will not work. +# "C:\Progra~1\Micros~1\VC98" will work. + +MSVCDir="C:\Progra~1\Micros~1\VC98" + +CPP=cl.exe +LINK32=link.exe +DLL_LINK_FLAGS=/nologo /dll /incremental:no /libpath:"$(MSVCDir)\Lib" /pdb:"libsndfile.pdb" /implib:".\libsndfile.lib" /machine:I386 /out:"libsndfile.dll" +PROG_LINK_FLAGS=/nologo /incremental:no /libpath:"$(MSVCDir)\Lib" /pdb:"libsndfile.pdb" /machine:I386 /exetype:dynamic + +CFLAGS=/nologo /MD /W1 /GX /O2 /I "$(MSVCDir)\Include" /I "src" /D "WIN32" /D "_USRDLL" /D "inline=__inline" /YX /FD + +#==================================================================== +# Targets + +ALL : libsndfile.dll \ + "sndfile-info.exe" \ + "sndfile-convert.exe" \ + "sndfile-play.exe" \ + "generate.exe" + +CLEAN : + -@erase "src\*.obj" + -@erase "src\G72x\*.obj" + -@erase "src\GSM610\*.obj" + -@erase "tests\*.obj" + -@erase "tests\*.exe" + +#==================================================================== + +LINK32_OBJS= \ + ".\src\GSM610\add.obj" \ + ".\src\GSM610\code.obj" \ + ".\src\GSM610\decode.obj" \ + ".\src\GSM610\gsm_create.obj" \ + ".\src\GSM610\gsm_decode.obj" \ + ".\src\GSM610\gsm_destroy.obj" \ + ".\src\GSM610\gsm_encode.obj" \ + ".\src\GSM610\gsm_option.obj" \ + ".\src\GSM610\long_term.obj" \ + ".\src\GSM610\lpc.obj" \ + ".\src\GSM610\preprocess.obj" \ + ".\src\GSM610\rpe.obj" \ + ".\src\GSM610\short_term.obj" \ + ".\src\GSM610\table.obj" \ + ".\src\G72x\g721.obj" \ + ".\src\G72x\g723_16.obj" \ + ".\src\G72x\g723_24.obj" \ + ".\src\G72x\g723_40.obj" \ + ".\src\G72x\g72x.obj" \ + ".\src\aiff.obj" \ + ".\src\alaw.obj" \ + ".\src\au.obj" \ + ".\src\g72x.obj" \ + ".\src\avr.obj" \ + ".\src\common.obj" \ + ".\src\double64.obj" \ + ".\src\dwvw.obj" \ + ".\src\file_io.obj" \ + ".\src\dither.obj" \ + ".\src\float32.obj" \ + ".\src\command.obj" \ + ".\src\strings.obj" \ + ".\src\ima_adpcm.obj" \ + ".\src\ms_adpcm.obj" \ + ".\src\nist.obj" \ + ".\src\ircam.obj" \ + ".\src\paf.obj" \ + ".\src\pcm.obj" \ + ".\src\raw.obj" \ + ".\src\rx2.obj" \ + ".\src\sd2.obj" \ + ".\src\sndfile.obj" \ + ".\src\svx.obj" \ + ".\src\txw.obj" \ + ".\src\sds.obj" \ + ".\src\ulaw.obj" \ + ".\src\voc.obj" \ + ".\src\w64.obj" \ + ".\src\wav.obj" \ + ".\src\gsm610.obj" \ + ".\src\wav_w64.obj" \ + ".\src\dwd.obj" \ + ".\src\wve.obj" \ + ".\src\mat4.obj" \ + ".\src\mat5.obj" \ + ".\src\vox_adpcm.obj" \ + ".\src\ogg.obj" \ + ".\src\pvf.obj" \ + ".\src\xi.obj" \ + ".\src\htk.obj" \ + ".\src\flac.obj" \ + ".\src\caf.obj" + +libsndfile.dll : $(LINK32_OBJS) ".\src\libsndfile.def" + $(LINK32) $(DLL_LINK_FLAGS) /def:".\src\libsndfile.def" $(LINK32_OBJS) + +"sndfile-info.exe" : ".\examples\sndfile-info.c" + $(CPP) $(CFLAGS) /Fo".\examples\sndfile-info.obj" /c ".\examples\sndfile-info.c" + $(LINK32) $(PROG_LINK_FLAGS) /out:"sndfile-info.exe" ".\examples\sndfile-info.obj" libsndfile.lib + +"sndfile-convert.exe" : ".\examples\sndfile-convert.c" + $(CPP) $(CFLAGS) /Fo".\examples\sndfile-convert.obj" /c ".\examples\sndfile-convert.c" + $(LINK32) $(PROG_LINK_FLAGS) /out:"sndfile-convert.exe" ".\examples\sndfile-convert.obj" libsndfile.lib + +"sndfile-play.exe" : ".\examples\sndfile-play.c" + $(CPP) $(CFLAGS) /Fo".\examples\sndfile-play.obj" /c ".\examples\sndfile-play.c" + $(LINK32) $(PROG_LINK_FLAGS) /out:"sndfile-play.exe" ".\examples\sndfile-play.obj" libsndfile.lib winmm.lib + +"generate.exe" : ".\examples\generate.c" + $(CPP) $(CFLAGS) /Fo".\examples\generate.obj" /c ".\examples\generate.c" + $(LINK32) $(PROG_LINK_FLAGS) /out:"generate.exe" ".\examples\generate.obj" libsndfile.lib + +TEST_PROGS= \ + ".\src\test_file_io.exe" \ + ".\tests\sfversion.exe" \ + ".\tests\error_test.exe" \ + ".\tests\pcm_test.exe" \ + ".\tests\ulaw_test.exe" \ + ".\tests\alaw_test.exe" \ + ".\tests\dwvw_test.exe" \ + ".\tests\command_test.exe" \ + ".\tests\floating_point_test.exe" \ + ".\tests\headerless_test.exe" \ + ".\tests\write_read_test.exe" \ + ".\tests\lossy_comp_test.exe" \ + ".\tests\peak_chunk_test.exe" \ + ".\tests\misc_test.exe" \ + ".\tests\string_test.exe" \ + ".\tests\win32_test.exe" \ + ".\tests\stdio_test.exe" \ + ".\tests\pipe_test.exe" \ + ".\tests\benchmark.exe" + +CHECK: $(TEST_PROGS) + ".\src\test_file_io.exe" + ".\tests\error_test.exe" + ".\tests\pcm_test.exe" + ".\tests\ulaw_test.exe" + ".\tests\alaw_test.exe" + ".\tests\dwvw_test.exe" + ".\tests\command_test.exe" ver + ".\tests\command_test.exe" norm + ".\tests\command_test.exe" format + ".\tests\floating_point_test.exe" + ".\tests\headerless_test.exe" + -@echo ---------------------------------------------------------------------- + -@".\tests\sfversion.exe" + -@echo passed common tests + -@echo ---------------------------------------------------------------------- + ".\tests\write_read_test" wav + ".\tests\lossy_comp_test" wav_ima + ".\tests\lossy_comp_test" wav_msadpcm + ".\tests\lossy_comp_test" wav_ulaw + ".\tests\lossy_comp_test" wav_alaw + ".\tests\lossy_comp_test" wav_gsm610 + ".\tests\peak_chunk_test" wav + ".\tests\misc_test" wav + ".\tests\string_test" wav + -@echo ---------------------------------------------------------------------- + -@".\tests\sfversion.exe" + -@echo "passed tests on WAV files. + -@echo ---------------------------------------------------------------------- + ".\tests\write_read_test" aiff + ".\tests\lossy_comp_test" aiff_ulaw + ".\tests\lossy_comp_test" aiff_alaw + ".\tests\peak_chunk_test" aiff + ".\tests\misc_test" aiff + -@echo ---------------------------------------------------------------------- + -@".\tests\sfversion.exe" + -@echo " passed tests on AIFF files." + -@echo ---------------------------------------------------------------------- + ".\tests\write_read_test" au + ".\tests\lossy_comp_test" au_ulaw + ".\tests\lossy_comp_test" au_alaw + ".\tests\lossy_comp_test" au_g721 + ".\tests\lossy_comp_test" au_g723 + ".\tests\misc_test" au + -@echo ---------------------------------------------------------------------- + -@".\tests\sfversion.exe" + -@echo " passed tests on AU files." + -@echo ---------------------------------------------------------------------- + ".\tests\write_read_test" caf + ".\tests\lossy_comp_test" caf_ulaw + ".\tests\lossy_comp_test" caf_alaw + ".\tests\misc_test" caf + -@echo ---------------------------------------------------------------------- + -@echo `./sfversion` passed tests on CAF files." + -@echo ---------------------------------------------------------------------- + ".\tests\write_read_test" raw + ".\tests\lossy_comp_test" raw_ulaw + ".\tests\lossy_comp_test" raw_alaw + -@echo ---------------------------------------------------------------------- + -@".\tests\sfversion.exe" + -@echo " passed tests on RAW (header-less) files." + -@echo ---------------------------------------------------------------------- + ".\tests\write_read_test" paf + ".\tests\misc_test" paf + -@echo ---------------------------------------------------------------------- + -@".\tests\sfversion.exe" + -@echo " passed tests on PAF files." + -@echo ---------------------------------------------------------------------- + ".\tests\write_read_test" svx + ".\tests\misc_test" svx + -@echo ---------------------------------------------------------------------- + -@".\tests\sfversion.exe" + -@echo " passed tests on SVX files." + -@echo ---------------------------------------------------------------------- + ".\tests\write_read_test" nist + ".\tests\misc_test" nist + -@echo ---------------------------------------------------------------------- + -@".\tests\sfversion.exe" + -@echo " passed tests on NIST files." + -@echo ---------------------------------------------------------------------- + ".\tests\write_read_test" ircam + ".\tests\lossy_comp_test" ircam_ulaw + ".\tests\lossy_comp_test" ircam_alaw + ".\tests\misc_test" ircam + -@echo ---------------------------------------------------------------------- + -@".\tests\sfversion.exe" + -@echo " passed tests on IRCAM files." + -@echo ---------------------------------------------------------------------- + ".\tests\write_read_test" voc + ".\tests\lossy_comp_test" voc_ulaw + ".\tests\lossy_comp_test" voc_alaw + ".\tests\misc_test" voc + -@echo ---------------------------------------------------------------------- + -@".\tests\sfversion.exe" + -@echo " passed tests on VOC files." + -@echo ---------------------------------------------------------------------- + ".\tests\write_read_test" w64 + ".\tests\lossy_comp_test" w64_ima + ".\tests\lossy_comp_test" w64_msadpcm + ".\tests\lossy_comp_test" w64_ulaw + ".\tests\lossy_comp_test" w64_alaw + ".\tests\lossy_comp_test" w64_gsm610 + ".\tests\misc_test" w64 + -@echo ---------------------------------------------------------------------- + -@".\tests\sfversion.exe" + -@echo " passed tests on W64 files." + -@echo ---------------------------------------------------------------------- + ".\tests\write_read_test" mat4 + ".\tests\misc_test" mat4 + -@echo ---------------------------------------------------------------------- + -@".\tests\sfversion.exe" + -@echo " passed tests on MAT4 files." + -@echo ---------------------------------------------------------------------- + ".\tests\write_read_test" mat5 + ".\tests\misc_test" mat5 + -@echo ---------------------------------------------------------------------- + -@".\tests\sfversion.exe" + -@echo " passed tests on MAT5 files." + -@echo ---------------------------------------------------------------------- + ".\tests\write_read_test" htk + ".\tests\misc_test" htk + -@echo ---------------------------------------------------------------------- + -@".\tests\sfversion.exe" + -@echo " passed tests on HTK files." + -@echo ---------------------------------------------------------------------- + ".\tests\write_read_test" avr + ".\tests\misc_test" avr + -@echo ---------------------------------------------------------------------- + -@".\tests\sfversion.exe" + -@echo " passed tests on AVR files." + -@echo ---------------------------------------------------------------------- + -@".\tests\stdio_test.exe" + -@".\tests\pipe_test.exe" + -@echo ---------------------------------------------------------------------- + -@".\tests\sfversion.exe" + -@echo " passed all tests." + -@echo ---------------------------------------------------------------------- + +#==================================================================== +# C files in src\GSM610. + +".\src\GSM610\add.obj" : ".\src\Gsm610\add.c" + $(CPP) $(CFLAGS) /Fo".\src\Gsm610\add.obj" /c ".\src\Gsm610\add.c" + +".\src\GSM610\code.obj" : ".\src\Gsm610\code.c" + $(CPP) $(CFLAGS) /Fo".\src\Gsm610\code.obj" /c ".\src\Gsm610\code.c" + +".\src\GSM610\decode.obj" : ".\src\Gsm610\decode.c" + $(CPP) $(CFLAGS) /Fo".\src\Gsm610\decode.obj" /c ".\src\Gsm610\decode.c" + +".\src\GSM610\gsm_create.obj" : ".\src\Gsm610\gsm_create.c" + $(CPP) $(CFLAGS) /Fo".\src\Gsm610\gsm_create.obj" /c ".\src\Gsm610\gsm_create.c" + +".\src\GSM610\gsm_decode.obj" : ".\src\Gsm610\gsm_decode.c" + $(CPP) $(CFLAGS) /Fo".\src\Gsm610\gsm_decode.obj" /c ".\src\Gsm610\gsm_decode.c" + +".\src\GSM610\gsm_destroy.obj" : ".\src\Gsm610\gsm_destroy.c" + $(CPP) $(CFLAGS) /Fo".\src\Gsm610\gsm_destroy.obj" /c ".\src\Gsm610\gsm_destroy.c" + +".\src\GSM610\gsm_encode.obj" : ".\src\Gsm610\gsm_encode.c" + $(CPP) $(CFLAGS) /Fo".\src\Gsm610\gsm_encode.obj" /c ".\src\Gsm610\gsm_encode.c" + +".\src\GSM610\gsm_option.obj" : ".\src\Gsm610\gsm_option.c" + $(CPP) $(CFLAGS) /Fo".\src\Gsm610\gsm_option.obj" /c ".\src\Gsm610\gsm_option.c" + +".\src\GSM610\long_term.obj" : ".\src\Gsm610\long_term.c" + $(CPP) $(CFLAGS) /Fo".\src\Gsm610\long_term.obj" /c ".\src\Gsm610\long_term.c" + +".\src\GSM610\lpc.obj" : ".\src\Gsm610\lpc.c" + $(CPP) $(CFLAGS) /Fo".\src\Gsm610\lpc.obj" /c ".\src\Gsm610\lpc.c" + +".\src\GSM610\preprocess.obj" : ".\src\Gsm610\preprocess.c" + $(CPP) $(CFLAGS) /Fo".\src\Gsm610\preprocess.obj" /c ".\src\Gsm610\preprocess.c" + +".\src\GSM610\rpe.obj" : ".\src\Gsm610\rpe.c" + $(CPP) $(CFLAGS) /Fo".\src\Gsm610\rpe.obj" /c ".\src\Gsm610\rpe.c" + +".\src\GSM610\short_term.obj" : ".\src\Gsm610\short_term.c" + $(CPP) $(CFLAGS) /Fo".\src\Gsm610\short_term.obj" /c ".\src\Gsm610\short_term.c" + +".\src\GSM610\table.obj" : ".\src\Gsm610\table.c" + $(CPP) $(CFLAGS) /Fo".\src\Gsm610\table.obj" /c ".\src\Gsm610\table.c" + +#==================================================================== +# C files in src\G72x. + +".\src\G72x\g721.obj" : ".\src\G72x\g721.c" + $(CPP) $(CFLAGS) /Fo".\src\G72x\g721.obj" /c ".\src\G72x\g721.c" + +".\src\G72x\g723_16.obj" : ".\src\G72x\g723_16.c" + $(CPP) $(CFLAGS) /Fo".\src\G72x\g723_16.obj" /c ".\src\G72x\g723_16.c" + +".\src\G72x\g723_24.obj" : ".\src\G72x\g723_24.c" + $(CPP) $(CFLAGS) /Fo".\src\G72x\g723_24.obj" /c ".\src\G72x\g723_24.c" + +".\src\G72x\g723_40.obj" : ".\src\G72x\g723_40.c" + $(CPP) $(CFLAGS) /Fo".\src\G72x\g723_40.obj" /c ".\src\G72x\g723_40.c" + +".\src\G72x\g72x.obj" : ".\src\G72x\g72x.c" + $(CPP) $(CFLAGS) /Fo".\src\G72x\g72x.obj" /c ".\src\G72x\g72x.c" + +#==================================================================== +# C files in src. + +".\src\aiff.obj" : ".\src\aiff.c" + $(CPP) $(CFLAGS) /Fo".\src\aiff.obj" /c ".\src\aiff.c" + +".\src\alaw.obj" : ".\src\alaw.c" + $(CPP) $(CFLAGS) /Fo".\src\alaw.obj" /c ".\src\alaw.c" + +".\src\au.obj" : ".\src\au.c" + $(CPP) $(CFLAGS) /Fo".\src\au.obj" /c ".\src\au.c" + +".\src\g72x.obj" : ".\src\g72x.c" + $(CPP) $(CFLAGS) /Fo".\src\g72x.obj" /c ".\src\g72x.c" + +".\src\avr.obj" : ".\src\avr.c" + $(CPP) $(CFLAGS) /Fo".\src\avr.obj" /c ".\src\avr.c" + +".\src\common.obj" : ".\src\common.c" + $(CPP) $(CFLAGS) /Fo".\src\common.obj" /c ".\src\common.c" + +".\src\double64.obj" : ".\src\double64.c" + $(CPP) $(CFLAGS) /Fo".\src\double64.obj" /c ".\src\double64.c" + +".\src\dwvw.obj" : ".\src\dwvw.c" + $(CPP) $(CFLAGS) /Fo".\src\dwvw.obj" /c ".\src\dwvw.c" + +".\src\file_io.obj" : ".\src\file_io.c" + $(CPP) $(CFLAGS) /Fo".\src\file_io.obj" /c ".\src\file_io.c" + +".\src\dither.obj" : ".\src\dither.c" + $(CPP) $(CFLAGS) /Fo".\src\dither.obj" /c ".\src\dither.c" + +".\src\float32.obj" : ".\src\float32.c" + $(CPP) $(CFLAGS) /Fo".\src\float32.obj" /c ".\src\float32.c" + +".\src\command.obj" : ".\src\command.c" + $(CPP) $(CFLAGS) /Fo".\src\command.obj" /c ".\src\command.c" + +".\src\strings.obj" : ".\src\strings.c" + $(CPP) $(CFLAGS) /Fo".\src\strings.obj" /c ".\src\strings.c" + +".\src\ima_adpcm.obj" : ".\src\ima_adpcm.c" + $(CPP) $(CFLAGS) /Fo".\src\ima_adpcm.obj" /c ".\src\ima_adpcm.c" + +".\src\ircam.obj" : ".\src\ircam.c" + $(CPP) $(CFLAGS) /Fo".\src\ircam.obj" /c ".\src\ircam.c" + +".\src\ms_adpcm.obj" : ".\src\ms_adpcm.c" + $(CPP) $(CFLAGS) /Fo".\src\ms_adpcm.obj" /c ".\src\ms_adpcm.c" + +".\src\nist.obj" : ".\src\nist.c" + $(CPP) $(CFLAGS) /Fo".\src\nist.obj" /c ".\src\nist.c" + +".\src\paf.obj" : ".\src\paf.c" + $(CPP) $(CFLAGS) /Fo".\src\paf.obj" /c ".\src\paf.c" + +".\src\pcm.obj" : ".\src\pcm.c" + $(CPP) $(CFLAGS) /Fo".\src\pcm.obj" /c ".\src\pcm.c" + +".\src\raw.obj" : ".\src\raw.c" + $(CPP) $(CFLAGS) /Fo".\src\raw.obj" /c ".\src\raw.c" + +".\src\rx2.obj" : ".\src\rx2.c" + $(CPP) $(CFLAGS) /Fo".\src\rx2.obj" /c ".\src\rx2.c" + +".\src\sd2.obj" : ".\src\sd2.c" + $(CPP) $(CFLAGS) /Fo".\src\sd2.obj" /c ".\src\sd2.c" + +".\src\sndfile.obj" : ".\src\sndfile.c" + $(CPP) $(CFLAGS) /Fo".\src\sndfile.obj" /c ".\src\sndfile.c" + +".\src\svx.obj" : ".\src\svx.c" + $(CPP) $(CFLAGS) /Fo".\src\svx.obj" /c ".\src\svx.c" + +".\src\txw.obj" : ".\src\txw.c" + $(CPP) $(CFLAGS) /Fo".\src\txw.obj" /c ".\src\txw.c" + +".\src\sds.obj" : ".\src\sds.c" + $(CPP) $(CFLAGS) /Fo".\src\sds.obj" /c ".\src\sds.c" + +".\src\ulaw.obj" : ".\src\ulaw.c" + $(CPP) $(CFLAGS) /Fo".\src\ulaw.obj" /c ".\src\ulaw.c" + +".\src\voc.obj" : ".\src\voc.c" + $(CPP) $(CFLAGS) /Fo".\src\voc.obj" /c ".\src\voc.c" + +".\src\w64.obj" : ".\src\w64.c" + $(CPP) $(CFLAGS) /Fo".\src\w64.obj" /c ".\src\w64.c" + +".\src\wav.obj" : ".\src\wav.c" + $(CPP) $(CFLAGS) /Fo".\src\wav.obj" /c ".\src\wav.c" + +".\src\gsm610.obj" : ".\src\gsm610.c" + $(CPP) $(CFLAGS) /Fo".\src\gsm610.obj" /c ".\src\gsm610.c" + +".\src\wav_w64.obj" : ".\src\wav_w64.c" + $(CPP) $(CFLAGS) /Fo".\src\wav_w64.obj" /c ".\src\wav_w64.c" + +".\src\dwd.obj" : ".\src\dwd.c" + $(CPP) $(CFLAGS) /Fo".\src\dwd.obj" /c ".\src\dwd.c" + +".\src\wve.obj" : ".\src\wve.c" + $(CPP) $(CFLAGS) /Fo".\src\wve.obj" /c ".\src\wve.c" + +".\src\mat4.obj" : ".\src\mat4.c" + $(CPP) $(CFLAGS) /Fo".\src\mat4.obj" /c ".\src\mat4.c" + +".\src\mat5.obj" : ".\src\mat5.c" + $(CPP) $(CFLAGS) /Fo".\src\mat5.obj" /c ".\src\mat5.c" + +".\src\vox_adpcm.obj" : ".\src\vox_adpcm.c" + $(CPP) $(CFLAGS) /Fo".\src\vox_adpcm.obj" /c ".\src\vox_adpcm.c" + +".\src\ogg.obj" : ".\src\ogg.c" + $(CPP) $(CFLAGS) /Fo".\src\ogg.obj" /c ".\src\ogg.c" + +".\src\pvf.obj" : ".\src\pvf.c" + $(CPP) $(CFLAGS) /Fo".\src\pvf.obj" /c ".\src\pvf.c" + +".\src\xi.obj" : ".\src\xi.c" + $(CPP) $(CFLAGS) /Fo".\src\xi.obj" /c ".\src\xi.c" + +".\src\htk.obj" : ".\src\htk.c" + $(CPP) $(CFLAGS) /Fo".\src\htk.obj" /c ".\src\htk.c" + +".\src\flac.obj" : ".\src\flac.c" + $(CPP) $(CFLAGS) /Fo".\src\flac.obj" /c ".\src\flac.c" + +".\src\caf.obj" : ".\src\caf.c" + $(CPP) $(CFLAGS) /Fo".\src\caf.obj" /c ".\src\caf.c" + +#==================================================================== +# Object files for test programs. + +".\tests\utils.obj" : ".\tests\utils.c" + $(CPP) $(CFLAGS) /Fo".\tests\utils.obj" /c ".\tests\utils.c" + +".\tests\dft_cmp.obj" : ".\tests\dft_cmp.c" + $(CPP) $(CFLAGS) /Fo".\tests\dft_cmp.obj" /c ".\tests\dft_cmp.c" + +#==================================================================== +# Test programs. + +".\src\test_file_io.exe" : ".\src\file_io.obj" ".\src\test_file_io.c" + $(CPP) $(CFLAGS) /Fo".\src\test_file_io.obj" /c ".\src\test_file_io.c" + $(LINK32) $(PROG_LINK_FLAGS) /out:".\src\test_file_io.exe" ".\src\test_file_io.obj" ".\src\file_io.obj" + +".\tests\sfversion.exe" : ".\tests\sfversion.c" + $(CPP) $(CFLAGS) /Fo".\tests\sfversion.obj" /c ".\tests\sfversion.c" + $(LINK32) $(PROG_LINK_FLAGS) /out:".\tests\sfversion.exe" ".\tests\sfversion.obj" libsndfile.lib + +".\tests\error_test.exe" : ".\tests\error_test.c" + $(CPP) $(CFLAGS) /Fo".\tests\error_test.obj" /c ".\tests\error_test.c" + $(LINK32) $(PROG_LINK_FLAGS) /out:".\tests\error_test.exe" ".\tests\error_test.obj" libsndfile.lib + +".\tests\pcm_test.exe" : ".\tests\pcm_test.c" ".\tests\utils.obj" "libsndfile.dll" + $(CPP) $(CFLAGS) /Fo".\tests\pcm_test.obj" /c ".\tests\pcm_test.c" + $(LINK32) $(PROG_LINK_FLAGS) /out:".\tests\pcm_test.exe" ".\tests\pcm_test.obj" ".\tests\utils.obj" libsndfile.lib + +".\tests\ulaw_test.exe" : ".\tests\ulaw_test.c" ".\tests\utils.obj" "libsndfile.dll" + $(CPP) $(CFLAGS) /Fo".\tests\ulaw_test.obj" /c ".\tests\ulaw_test.c" + $(LINK32) $(PROG_LINK_FLAGS) /out:".\tests\ulaw_test.exe" ".\tests\ulaw_test.obj" ".\tests\utils.obj" libsndfile.lib + +".\tests\alaw_test.exe" : ".\tests\alaw_test.c" ".\tests\utils.obj" "libsndfile.dll" + $(CPP) $(CFLAGS) /Fo".\tests\alaw_test.obj" /c ".\tests\alaw_test.c" + $(LINK32) $(PROG_LINK_FLAGS) /out:".\tests\alaw_test.exe" ".\tests\alaw_test.obj" ".\tests\utils.obj" libsndfile.lib + +".\tests\dwvw_test.exe" : ".\tests\dwvw_test.c" ".\tests\utils.obj" + $(CPP) $(CFLAGS) /Fo".\tests\dwvw_test.obj" /c ".\tests\dwvw_test.c" + $(LINK32) $(PROG_LINK_FLAGS) /out:".\tests\dwvw_test.exe" ".\tests\dwvw_test.obj" ".\tests\utils.obj" libsndfile.lib + +".\tests\command_test.exe" : ".\tests\command_test.c" ".\tests\utils.obj" + $(CPP) $(CFLAGS) /Fo".\tests\command_test.obj" /c ".\tests\command_test.c" + $(LINK32) $(PROG_LINK_FLAGS) /out:".\tests\command_test.exe" ".\tests\command_test.obj" ".\tests\utils.obj" libsndfile.lib + +".\tests\floating_point_test.exe" : ".\tests\floating_point_test.c" ".\tests\utils.obj" ".\tests\dft_cmp.obj" + $(CPP) $(CFLAGS) /Fo".\tests\floating_point_test.obj" /c ".\tests\floating_point_test.c" + $(LINK32) $(PROG_LINK_FLAGS) /out:".\tests\floating_point_test.exe" ".\tests\floating_point_test.obj" ".\tests\utils.obj" ".\tests\dft_cmp.obj" libsndfile.lib + +".\tests\headerless_test.exe" : ".\tests\headerless_test.c" ".\tests\utils.obj" + $(CPP) $(CFLAGS) /Fo".\tests\headerless_test.obj" /c ".\tests\headerless_test.c" + $(LINK32) $(PROG_LINK_FLAGS) /out:".\tests\headerless_test.exe" ".\tests\headerless_test.obj" ".\tests\utils.obj" libsndfile.lib + +".\tests\write_read_test.exe" : ".\tests\write_read_test.c" ".\tests\utils.obj" + $(CPP) $(CFLAGS) /Fo".\tests\write_read_test.obj" /c ".\tests\write_read_test.c" + $(LINK32) $(PROG_LINK_FLAGS) /out:".\tests\write_read_test.exe" ".\tests\write_read_test.obj" ".\tests\utils.obj" libsndfile.lib + +".\tests\lossy_comp_test.exe" : ".\tests\lossy_comp_test.c" ".\tests\utils.obj" + $(CPP) $(CFLAGS) /Fo".\tests\lossy_comp_test.obj" /c ".\tests\lossy_comp_test.c" + $(LINK32) $(PROG_LINK_FLAGS) /out:".\tests\lossy_comp_test.exe" ".\tests\lossy_comp_test.obj" ".\tests\utils.obj" libsndfile.lib + +".\tests\peak_chunk_test.exe" : ".\tests\peak_chunk_test.c" ".\tests\utils.obj" + $(CPP) $(CFLAGS) /Fo".\tests\peak_chunk_test.obj" /c ".\tests\peak_chunk_test.c" + $(LINK32) $(PROG_LINK_FLAGS) /out:".\tests\peak_chunk_test.exe" ".\tests\peak_chunk_test.obj" ".\tests\utils.obj" libsndfile.lib + +".\tests\misc_test.exe" : ".\tests\misc_test.c" ".\tests\utils.obj" + $(CPP) $(CFLAGS) /Fo".\tests\misc_test.obj" /c ".\tests\misc_test.c" + $(LINK32) $(PROG_LINK_FLAGS) /out:".\tests\misc_test.exe" ".\tests\misc_test.obj" ".\tests\utils.obj" libsndfile.lib + +".\tests\string_test.exe" : ".\tests\string_test.c" ".\tests\utils.obj" + $(CPP) $(CFLAGS) /Fo".\tests\string_test.obj" /c ".\tests\string_test.c" + $(LINK32) $(PROG_LINK_FLAGS) /out:".\tests\string_test.exe" ".\tests\string_test.obj" ".\tests\utils.obj" libsndfile.lib + +".\tests\win32_test.exe" : ".\tests\win32_test.c" + $(CPP) $(CFLAGS) /Fo".\tests\win32_test.obj" /c ".\tests\win32_test.c" + $(LINK32) $(PROG_LINK_FLAGS) /out:".\tests\win32_test.exe" ".\tests\win32_test.obj" + +".\tests\stdio_test.exe" : ".\tests\stdio_test.c" ".\tests\utils.obj" + $(CPP) $(CFLAGS) /Fo".\tests\stdio_test.obj" /c ".\tests\stdio_test.c" + $(LINK32) $(PROG_LINK_FLAGS) /out:".\tests\stdio_test.exe" ".\tests\stdio_test.obj" ".\tests\utils.obj" libsndfile.lib + +".\tests\pipe_test.exe" : ".\tests\pipe_test.c" ".\tests\utils.obj" + $(CPP) $(CFLAGS) /Fo".\tests\pipe_test.obj" /c ".\tests\pipe_test.c" + $(LINK32) $(PROG_LINK_FLAGS) /out:".\tests\pipe_test.exe" ".\tests\pipe_test.obj" ".\tests\utils.obj" libsndfile.lib + +# ".\tests\stdin_test.exe" : ".\tests\stdin_test.c" ".\tests\utils.obj" +# $(CPP) $(CFLAGS) /Fo".\tests\stdin_test.obj" /c ".\tests\stdin_test.c" +# $(LINK32) $(PROG_LINK_FLAGS) /out:".\tests\stdin_test.exe" ".\tests\stdin_test.obj" ".\tests\utils.obj" libsndfile.lib +# +# ".\tests\stdout_test.exe" : ".\tests\stdout_test.c" ".\tests\utils.obj" +# $(CPP) $(CFLAGS) /Fo".\tests\stdout_test.obj" /c ".\tests\stdout_test.c" +# $(LINK32) $(PROG_LINK_FLAGS) /out:".\tests\stdout_test.exe" ".\tests\stdout_test.obj" ".\tests\utils.obj" libsndfile.lib + +".\tests\benchmark.exe" : ".\tests\benchmark.c" + $(CPP) $(CFLAGS) /Fo".\tests\benchmark.obj" /c ".\tests\benchmark.c" + $(LINK32) $(PROG_LINK_FLAGS) /out:".\tests\benchmark.exe" ".\tests\benchmark.obj" ".\tests\utils.obj" libsndfile.lib + +# End of Makefile +#==================================================================== + +# Do not edit or modify anything in this comment block. +# The arch-tag line is a file identity tag for the GNU Arch +# revision control system. +# +# arch-tag: 9a46d08e-921f-49f7-961e-3f21ea67851e + diff --git a/Win32/README-Win32.txt b/Win32/README-Win32.txt new file mode 100644 index 00000000..975bfe92 --- /dev/null +++ b/Win32/README-Win32.txt @@ -0,0 +1,96 @@ +NOTE: This is not the way the author builds libsndfile on Win32 +so this desciption may be out of date. For the authors method +of building libsndfile on Win32 have a look at the file named +win32.html in the doc\ directory of the source code distribution. + +***************************************************************** + +This is the readme-Win32.txt file associated with the LibSndFile +library. It describes how the included workspace and project +was created for Microsoft Visual C++ developer's studio (MSVC), +version 5.0. Skip to point 7 if you wish to create a new +project for building an executable. + +1. Extracted libsndfile.zip to d:\files\msvc\ + +2. It created (replace X.Y.Z with the libsndfile version number) + d:\files\msvc\libsndfile-X.Y.Z\Win32 * + d:\files\msvc\libsndfile-X.Y.Z\src * + d:\files\msvc\libsndfile-X.Y.Z\tests * + d:\files\msvc\libsndfile-X.Y.Z\examples + d:\files\msvc\libsndfile-X.Y.Z\doc + d:\files\msvc\libsndfile-X.Y.Z\m4 + d:\files\msvc\libsndfile-X.Y.Z\MacOS + + * are needed for this example + +3. From MSVC:New->Workspace, I created LibSndFileWorkspace at: + d:\files\msvc\libsndfile-X.Y.Z\Win32\ + (workspace files have the extension .dsw) + +3. In MSVC, rt-click on "Workspace LibSndFileWorkspace" and add project: + Project type: Win32 Static Library + Project Name: LibSndFile + Project Location: D:\files\msvc\libsndfile-X.Y.Z\Win32 + Select button: 'Add to current workspace' + Platforms: Win32 + +4. Rt-click newly formed "LibSndFile files" and add files: + d:\files\msvc\libsndfile-X.Y.Z\src\*.* + d:\files\msvc\libsndfile-X.Y.Z\src\Gsm610\*.* + d:\files\msvc\libsndfile-X.Y.Z\src\G72x\*.* + +5. Rt-click 'LibSndFile files' and go to Settings + a. Select all configurations on the left hand side + b. Then select C/C++/Preprocessor and add + "..\" (no quotes) to 'Additional include directories' + (This allows ..Win32\config.h and unistd.h to be found.) + +6. At this point you should be able to build the library. The output + will be found in ..\Win32\LibSndFile\Debug\LibSndFile.lib. You can + change the LibSndFile project to Release and a similar release + path will be created. + +The following describes how to add an application project to the +workspace. You may add as many as you wish. In general, you will +need one project for each executable you want to create. + +7. Rt-click LibSndFileWorkspace and select 'Add project' + Project type: Win32 Console Application + Project Name: sfversion + Location: d:\files\msvc\libsndfile-X.Y.Z\Win32\sfversion + Select button: 'Add to current workspace' + Platforms: Win32 + + Notes: + - MSVC will create a directory ..\Win32\sfversion\ + - MSVC will create the file sfversion.dsp in this directory + +8. Rt-click 'sfversion files' and add file: + d:\files\msvc\libsndfile-X.Y.Z\tests\sfversion.c + +9. Rt-click 'sfversion files' and go to Settings: + a. Select 'All configurations' on the left hand side + b. Then select C/C++/Preprocessor and add + "..\..\src,..\" (no quotes) to 'Additional include directories' + +9. Rt-click 'sfversion files' and go to Settings: + a. Select 'Debug Configuration' on left hand side + b. Then select Link tab and add + "..\LibSndFile\Debug\LibSndFile.lib " (no quotes) to + the list of 'Object/library modules'. Leave a space between new + addition existing lib files. + +10. Repeat above for Release build adding Release path info. + +11. Build your application, it should link and create an .exe + +Final notes: + +Files created during build by msvc but are not needed for archive: +*ncb *.plg *.opt *.obj *.idb *.pch *.lib *.exe + +Files associated with LibSndFile but not used by msvc: +Makefile.in +Makefile.am + - End - diff --git a/Win32/README-precompiled-dll.txt b/Win32/README-precompiled-dll.txt new file mode 100644 index 00000000..bde81242 --- /dev/null +++ b/Win32/README-precompiled-dll.txt @@ -0,0 +1,40 @@ +Notes on Using the Pre-compiled libsndfile DLL. +=============================================== + +In order to use this pre-compiled DLL with Visual Studio, you will need to +generate a .LIB file from the DLL. + +This can be achieved as follows: + + 1) In a CMD window, change to the directory containing this file and + run the command: + + lib /machine:i386 /def:libsndfile-1.def + +You now have two files: + + libsndfile-1.dll + libsndfile-1.lib + +to be used with VisualStudio. + +If the lib command fails with a command saying "'lib' is not recognized as +an internal or external command, operable program or batch file", you need +to find the location of "lib.exe" and add that directory to your PATH +environment variable. Another alternative is to use the "Visual Studio 2005 +Command Prompt" Start menu item: + + Start -> + All Programs -> + Visual Studio 2005 -> + Visual Studio Tools -> + Visual Studio 2005 Command Prompt + +If for some reason these instructions don't work for you or you are still +not able to use the libsndfile DLL with you project, please do not contact +the main author of libsndfile. Instead, join the libsndfile-users mailing +list : + + http://www.mega-nerd.com/libsndfile/lists.html + +and ask a question there. diff --git a/Win32/config.h b/Win32/config.h new file mode 100644 index 00000000..b6ed4b78 --- /dev/null +++ b/Win32/config.h @@ -0,0 +1,290 @@ +/* +** Copyright (C) 2002-2004 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* +** This is the Win32 version of the file config.h which is autogenerated +** on Unix systems. +*/ + +#pragma warning (disable : 4244) +#pragma warning (disable : 4761) + +/* Set to 1 if the compile is GNU GCC. */ +/* #undef COMPILER_IS_GCC */ + +/* Target processor clips on negative float to int conversion. */ +#define CPU_CLIPS_NEGATIVE 1 + +/* Target processor clips on positive float to int conversion. */ +#define CPU_CLIPS_POSITIVE 0 + +/* Target processor is big endian. */ +#define CPU_IS_BIG_ENDIAN 0 + +/* Target processor is little endian. */ +#define CPU_IS_LITTLE_ENDIAN 1 + +/* Set to 1 to enable experimental code. */ +#define ENABLE_EXPERIMENTAL_CODE 0 + +/* Major version of GCC or 3 otherwise. */ +/* #undef GCC_MAJOR_VERSION */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_ALSA_ASOUNDLIB_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_BYTESWAP_H */ + +/* Define to 1 if you have the `calloc' function. */ +#define HAVE_CALLOC 1 + +/* Define to 1 if you have the `ceil' function. */ +#define HAVE_CEIL 1 + +/* Set to 1 if S_IRGRP is defined. */ +#define HAVE_DECL_S_IRGRP 0 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_DLFCN_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_ENDIAN_H */ + +/* Define to 1 if you have the `fdatasync' function. */ + +/* #undef HAVE_FDATASYNC */ + +/* Define to 1 if you have libflac 1.1.1 */ +/* #undef HAVE_FLAC_1_1_1 1 */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_FLAC_ALL_H 1 */ + +/* Set to 1 if the compile supports the struct hack. */ +#define HAVE_FLEXIBLE_ARRAY 1 + +/* Define to 1 if you have the `floor' function. */ +#define HAVE_FLOOR 1 + +/* Define to 1 if you have the `fmod' function. */ +#define HAVE_FMOD 1 + +/* Define to 1 if you have the `free' function. */ +#define HAVE_FREE 1 + +/* Define to 1 if you have the `fstat' function. */ +#define HAVE_FSTAT 1 + +/* Define to 1 if you have the `fsync' function. */ +/* #undef HAVE_FSYNC */ + +/* Define to 1 if you have the `ftruncate' function. */ +/* #undef HAVE_FTRUNCATE */ + +/* Define to 1 if you have the `getpagesize' function. */ +#define HAVE_GETPAGESIZE 1 + +/* Define to 1 if you have the `gmtime' function. */ +#define HAVE_GMTIME 1 + +/* Define to 1 if you have the `gmtime_r' function. */ +/* #undef HAVE_GMTIME_R */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_INTTYPES_H */ + +/* Define to 1 if you have the `m' library (-lm). */ +#define HAVE_LIBM 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_LOCALE_H */ + +/* Define if you have C99's lrint function. */ +/* #undef HAVE_LRINT */ + +/* Define if you have C99's lrintf function. */ +/* #undef HAVE_LRINTF */ + +/* Define to 1 if you have the `lseek' function. */ +#define HAVE_LSEEK 1 + +/* Define to 1 if you have the `malloc' function. */ +#define HAVE_MALLOC 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `mmap' function. */ +/* #undef HAVE_MMAP */ + +/* Define to 1 if you have the `open' function. */ +#define HAVE_OPEN 1 + +/* Define to 1 if you have the `pread' function. */ +/* #undef HAVE_PREAD */ + +/* Define to 1 if you have the `pwrite' function. */ +/* #undef HAVE_PWRITE */ + +/* Define to 1 if you have the `read' function. */ +#define HAVE_READ 1 + +/* Define to 1 if you have the `realloc' function. */ +#define HAVE_REALLOC 1 + +/* Define to 1 if you have the `setlocale' function. */ +/* #undef HAVE_SETLOCALE */ + +/* Define to 1 if you have the `snprintf' function. */ +#define HAVE_SNPRINTF 1 + +/* Set to 1 if you have libsqlite3. */ +/* #undef HAVE_SQLITE3 */ + +/* Define to 1 if the system has the type `ssize_t'. */ +/* #undef HAVE_SSIZE_T */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_STDINT_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have that is POSIX.1 compatible. */ +/* #undef HAVE_SYS_WAIT_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_UNISTD_H */ + +/* Define to 1 if you have the `vsnprintf' function. */ +#define HAVE_VSNPRINTF 1 + +/* Define to 1 if you have the `write' function. */ +#define HAVE_WRITE 1 + +/* Set to 1 if compiling for MacOSX */ +#define OS_IS_MACOSX 0 + +/* Set to 1 if compiling for Win32 */ +#define OS_IS_WIN32 1 + +/* Name of package */ +#define PACKAGE "libsndfile" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "erikd@mega-nerd.com" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "libsndfile" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "libsndfile 1.0.13" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "libsndfile" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "1.0.13" + +/* Set to maximum allowed value of sf_count_t type. */ +#define SF_COUNT_MAX 0x7FFFFFFFFFFFFFFFi64 + +/* The size of a `double', as computed by sizeof. */ +#define SIZEOF_DOUBLE 8 + +/* The size of a `float', as computed by sizeof. */ +#define SIZEOF_FLOAT 4 + +/* The size of a `int', as computed by sizeof. */ +#define SIZEOF_INT 4 + +/* The size of a `int64_t', as computed by sizeof. */ +#define SIZEOF_INT64_T 0 + +/* The size of a `loff_t', as computed by sizeof. */ +#define SIZEOF_LOFF_T 0 + +/* The size of a `long', as computed by sizeof. */ +#define SIZEOF_LONG 4 + +/* The size of a `long long', as computed by sizeof. */ +#define SIZEOF_LONG_LONG 0 + +/* The size of a `off64_t', as computed by sizeof. */ +/* #undef SIZEOF_OFF64_T */ + +/* The size of a `off_t', as computed by sizeof. */ +#define SIZEOF_OFF_T 4 + +/* Set to sizeof (long) if unknown. */ +#define SIZEOF_SF_COUNT_T 8 + +/* The size of a `short', as computed by sizeof. */ +#define SIZEOF_SHORT 2 + +/* The size of a `size_t', as computed by sizeof. */ +#define SIZEOF_SIZE_T 4 + +/* The size of a `ssize_t', as computed by sizeof. */ +#define SIZEOF_SSIZE_T 4 + +/* The size of a `void*', as computed by sizeof. */ +#define SIZEOF_VOIDP 4 + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Set to long if unknown. */ +#define TYPEOF_SF_COUNT_T loff_t + +/* Set to 1 to use the native windows API */ +#define USE_WINDOWS_API 1 + +/* Version number of package */ +#define VERSION "1.0.13" + +/* Number of bits in a file offset, on hosts where this is settable. */ +/* #undef _FILE_OFFSET_BITS */ + +/* Define to make fseeko etc. visible, on some hosts. */ +/* #undef _LARGEFILE_SOURCE */ + +/* Define for large files, on AIX-style hosts. */ +/* #undef _LARGE_FILES */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 78a733c0-a430-46bd-81ab-4e479fbcffac +*/ + diff --git a/Win32/libtool.mingw b/Win32/libtool.mingw new file mode 100644 index 00000000..aee89bae --- /dev/null +++ b/Win32/libtool.mingw @@ -0,0 +1,7273 @@ +#! /bin/sh + +# libtoolT - Provide generalized library-building support services. +# Generated automatically by (GNU glib 2.2.3) +# NOTE: Changes made to this file will be lost: look at ltmain.sh. +# +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# +# This file is part of GNU Libtool: +# Originally by Gordon Matzigkeit , 1996 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# A sed program that does not truncate output. +SED="/bin/sed" + +# Sed that helps us avoid accidentally triggering echo(1) options like -n. +Xsed="/bin/sed -e s/^X//" + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +if test "X${CDPATH+set}" = Xset; then CDPATH=:; export CDPATH; fi + +# The names of the tagged configurations supported by this script. +available_tags=" CXX F77" + +# ### BEGIN LIBTOOL CONFIG + +# Libtool was configured on host QWEST-WKCH1I7I3: + +# Shell to use when invoking shell scripts. +SHELL="/bin/sh" + +# Whether or not to build shared libraries. +build_libtool_libs=yes + +# Whether or not to build static libraries. +build_old_libs=no + +# Whether or not to add -lc for building shared libraries. +build_libtool_need_lc=yes + +# Whether or not to disallow shared libs when runtime libs are static +allow_libtool_libs_with_static_runtimes=yes + +# Whether or not to optimize for fast installation. +fast_install=yes + +# The host system. +host_alias= +host=i686-pc-mingw32 + +# An echo program that does not interpret backslashes. +echo="echo" + +# The archiver. +AR="ar" +AR_FLAGS="cru" + +# A C compiler. +LTCC="gcc" + +# A language-specific compiler. +CC="gcc" + +# Is the compiler the GNU C compiler? +with_gcc=yes + +# An ERE matcher. +EGREP="grep -E" + +# The linker used to build libraries. +LD="c:/mnt/opt/mingw/mingw32/bin/ld.exe" + +# Whether we need hard or soft links. +LN_S="ln -s" + +# A BSD-compatible nm program. +NM="/bin/nm -B" + +# A symbol stripping program +STRIP=strip + +# Used to examine libraries when file_magic_cmd begins "file" +MAGIC_CMD=file + +# Used on cygwin: DLL creation program. +DLLTOOL="dlltool" + +# Used on cygwin: object dumper. +OBJDUMP="objdump" + +# Used on cygwin: assembler. +AS="as" + +# The name of the directory that contains temporary libtool files. +objdir=.libs + +# How to create reloadable object files. +reload_flag=" -r" +reload_cmds="\$LD\$reload_flag -o \$output\$reload_objs" + +# How to pass a linker flag through the compiler. +wl="-Wl," + +# Object file suffix (normally "o"). +objext="o" + +# Old archive suffix (normally "a"). +libext="a" + +# Shared library suffix (normally ".so"). +shrext='.dll' + +# Executable file suffix (normally ""). +exeext="" + +# Additional compiler flags for building library objects. +pic_flag=" -DDLL_EXPORT -DPIC" +pic_mode=default + +# What is the maximum length of a command? +max_cmd_len=8192 + +# Does compiler simultaneously support -c and -o options? +compiler_c_o="yes" + +# Must we lock files when doing compilation ? +need_locks="no" + +# Do we need the lib prefix for modules? +need_lib_prefix=no + +# Do we need a version for libraries? +need_version=no + +# Whether dlopen is supported. +dlopen_support=unknown + +# Whether dlopen of programs is supported. +dlopen_self=unknown + +# Whether dlopen of statically linked programs is supported. +dlopen_self_static=unknown + +# Compiler flag to prevent dynamic linking. +link_static_flag="-static" + +# Compiler flag to turn off builtin functions. +no_builtin_flag=" -fno-builtin -fno-rtti -fno-exceptions" + +# Compiler flag to allow reflexive dlopens. +export_dynamic_flag_spec="\${wl}--export-dynamic" + +# Compiler flag to generate shared objects directly from archives. +whole_archive_flag_spec="\${wl}--whole-archive\$convenience \${wl}--no-whole-archive" + +# Compiler flag to generate thread-safe objects. +thread_safe_flag_spec="" + +# Library versioning type. +version_type=windows + +# Format of library name prefix. +libname_spec="lib\$name" + +# List of archive names. First name is the real one, the rest are links. +# The last name is the one that the linker finds with -lNAME. +library_names_spec="\$libname.dll.a" + +# The coded name of the library, if different from the real name. +soname_spec="\${libname}\`echo \${release} | \$SED -e s/[.]/-/g\`\${versuffix}\${shared_ext}" + +# Commands used to build and install an old-style archive. +RANLIB="ranlib" +old_archive_cmds="\$AR \$AR_FLAGS \$oldlib\$oldobjs\$old_deplibs~\$RANLIB \$oldlib" +old_postinstall_cmds="\$RANLIB \$oldlib~chmod 644 \$oldlib" +old_postuninstall_cmds="" + +# Create an old-style archive from a shared archive. +old_archive_from_new_cmds="" + +# Create a temporary old-style archive to link instead of a shared archive. +old_archive_from_expsyms_cmds="" + +# Commands used to build and install a shared archive. +archive_cmds="\$CC -shared \$libobjs \$deplibs \$compiler_flags -o \$output_objdir/\$soname \${wl}--image-base=0x10000000 \${wl}--out-implib,\$lib" +archive_expsym_cmds="if test \\\"x\\\`\$SED 1q \$export_symbols\\\`\\\" = xEXPORTS; then + cp \$export_symbols \$output_objdir/\$soname.def; + else + echo EXPORTS > \$output_objdir/\$soname.def; + cat \$export_symbols >> \$output_objdir/\$soname.def; + fi~ + \$CC -shared \$output_objdir/\$soname.def \$libobjs \$deplibs \$compiler_flags -o \$output_objdir/\$soname \${wl}--image-base=0x10000000 \${wl}--out-implib,\$lib" +postinstall_cmds="base_file=\\\`basename \\\${file}\\\`~ + dlpath=\\\`\$SHELL 2>&1 -c '. \$dir/'\\\${base_file}'i;echo \\\$dlname'\\\`~ + dldir=\$destdir/\\\`dirname \\\$dlpath\\\`~ + test -d \\\$dldir || mkdir -p \\\$dldir~ + \$install_prog \$dir/\$dlname \\\$dldir/\$dlname" +postuninstall_cmds="dldll=\\\`\$SHELL 2>&1 -c '. \$file; echo \\\$dlname'\\\`~ + dlpath=\$dir/\\\$dldll~ + \$rm \\\$dlpath" + +# Commands used to build a loadable module (assumed same as above if empty) +module_cmds="" +module_expsym_cmds="" + +# Commands to strip libraries. +old_striplib="strip --strip-debug" +striplib="strip --strip-unneeded" + +# Dependencies to place before the objects being linked to create a +# shared library. +predep_objects="" + +# Dependencies to place after the objects being linked to create a +# shared library. +postdep_objects="" + +# Dependencies to place before the objects being linked to create a +# shared library. +predeps="" + +# Dependencies to place after the objects being linked to create a +# shared library. +postdeps="" + +# The library search path used internally by the compiler when linking +# a shared library. +compiler_lib_search_path="" + +# Method to check whether dependent libraries are shared objects. +deplibs_check_method="file_magic ^x86 archive import|^x86 DLL" +deplibs_check_method=pass_all + +# Command to use when deplibs_check_method == file_magic. +file_magic_cmd="win32_libid" + +# Flag that allows shared libraries with undefined symbols to be built. +allow_undefined_flag="supported" + +# Flag that forces no undefined symbols. +no_undefined_flag="" + +# Commands used to finish a libtool library installation in a directory. +finish_cmds="" + +# Same as above, but a single script fragment to be evaled but not shown. +finish_eval="" + +# Take the output of nm and produce a listing of raw symbols and C names. +global_symbol_pipe="sed -n -e 's/^.*[ ]\\([ABCDGIRSTW][ABCDGIRSTW]*\\)[ ][ ]*\\(_\\)\\([_A-Za-z][_A-Za-z0-9]*\\) \\{0,1\\}\$/\\1 \\2\\3 \\3/p'" + +# Transform the output of nm in a proper C declaration +global_symbol_to_cdecl="sed -n -e 's/^. .* \\(.*\\)\$/extern int \\1;/p'" + +# Transform the output of nm in a C name address pair +global_symbol_to_c_name_address="sed -n -e 's/^: \\([^ ]*\\) \$/ {\\\"\\1\\\", (lt_ptr) 0},/p' -e 's/^[BCDEGRST] \\([^ ]*\\) \\([^ ]*\\)\$/ {\"\\2\", (lt_ptr) \\&\\2},/p'" + +# This is the shared library runtime path variable. +runpath_var=LD_RUN_PATH + +# This is the shared library path variable. +shlibpath_var=PATH + +# Is shlibpath searched before the hard-coded library search path? +shlibpath_overrides_runpath=yes + +# How to hardcode a shared library path into an executable. +hardcode_action=immediate + +# Whether we should hardcode library paths into libraries. +hardcode_into_libs=no + +# Flag to hardcode $libdir into a binary during linking. +# This must work even if $libdir does not exist. +hardcode_libdir_flag_spec="\${wl}--rpath \${wl}\$libdir" + +# If ld is used when linking, flag to hardcode $libdir into +# a binary during linking. This must work even if $libdir does +# not exist. +hardcode_libdir_flag_spec_ld="" + +# Whether we need a single -rpath flag with a separated argument. +hardcode_libdir_separator="" + +# Set to yes if using DIR/libNAME during linking hardcodes DIR into the +# resulting binary. +hardcode_direct=no + +# Set to yes if using the -LDIR flag during linking hardcodes DIR into the +# resulting binary. +hardcode_minus_L=no + +# Set to yes if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into +# the resulting binary. +hardcode_shlibpath_var=unsupported + +# Set to yes if building a shared library automatically hardcodes DIR into the library +# and all subsequent libraries and executables linked against it. +hardcode_automatic=no + +# Variables whose values should be saved in libtool wrapper scripts and +# restored at relink time. +variables_saved_for_relink="PATH LD_RUN_PATH GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" + +# Whether libtool must link a program against all its dependency libraries. +link_all_deplibs=unknown + +# Compile-time system search path for libraries +sys_lib_search_path_spec=" =c:/mnt/opt/mingw/bin/../lib/gcc-lib/mingw32/3.2.3/ c:/mnt/opt/mingw/bin/../lib/gcc-lib/ /mingw/lib/gcc-lib/mingw32/3.2.3/ /usr/lib/gcc/mingw32/3.2.3/ c:/mnt/opt/mingw/bin/../lib/gcc-lib/mingw32/3.2.3/../../../../mingw32/lib/mingw32/3.2.3/ c:/mnt/opt/mingw/bin/../lib/gcc-lib/mingw32/3.2.3/../../../../mingw32/lib/ /mingw/lib/gcc-lib/mingw32/3.2.3/../../../../mingw32/lib/mingw32/3.2.3/ /mingw/lib/gcc-lib/mingw32/3.2.3/../../../../mingw32/lib/ c:/mnt/opt/mingw/bin/../lib/gcc-lib/mingw32/3.2.3/../../../mingw32/3.2.3/ c:/mnt/opt/mingw/bin/../lib/gcc-lib/mingw32/3.2.3/../../../ /mingw/lib/gcc-lib/mingw32/3.2.3/../../../mingw32/3.2.3/ /mingw/lib/gcc-lib/mingw32/3.2.3/../../../ /lib/mingw32/3.2.3/ /lib/ /usr/lib/mingw32/3.2.3/ /usr/lib/" + +# Run-time system search path for libraries +sys_lib_dlsearch_path_spec="/lib /usr/lib" + +# Fix the shell variable $srcfile for the compiler. +fix_srcfile_path="" + +# Set to yes if exported symbols are required. +always_export_symbols=no + +# The commands to list exported symbols. +export_symbols_cmds="\$NM \$libobjs \$convenience | \$global_symbol_pipe | \$SED -e '/^[BCDGS] /s/.* \\\\([^ ]*\\\\)/\\\\1 DATA/' | \$SED -e '/^[AITW] /s/.* //' | sort | uniq > \$export_symbols" + +# The commands to extract the exported symbol list from a shared archive. +extract_expsyms_cmds="" + +# Symbols that should not be listed in the preloaded symbols. +exclude_expsyms="_GLOBAL_OFFSET_TABLE_" + +# Symbols that must always be exported. +include_expsyms="" + +# ### END LIBTOOL CONFIG + +# ltmain.sh - Provide generalized library-building support services. +# NOTE: Changing this file will not affect anything until you rerun configure. +# +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003 +# Free Software Foundation, Inc. +# Originally by Gordon Matzigkeit , 1996 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Check that we have a working $echo. +if test "X$1" = X--no-reexec; then + # Discard the --no-reexec flag, and continue. + shift +elif test "X$1" = X--fallback-echo; then + # Avoid inline document here, it may be left over + : +elif test "X`($echo '\t') 2>/dev/null`" = 'X\t'; then + # Yippee, $echo works! + : +else + # Restart under the correct shell, and then maybe $echo will work. + exec $SHELL "$0" --no-reexec ${1+"$@"} +fi + +if test "X$1" = X--fallback-echo; then + # used as fallback echo + shift + cat <&2 + $echo "Fatal configuration error. See the $PACKAGE docs for more information." 1>&2 + exit 1 +fi + +# Global variables. +mode=$default_mode +nonopt= +prev= +prevopt= +run= +show="$echo" +show_help= +execute_dlfiles= +lo2o="s/\\.lo\$/.${objext}/" +o2lo="s/\\.${objext}\$/.lo/" + +##################################### +# Shell function definitions: +# This seems to be the best place for them + +# Need a lot of goo to handle *both* DLLs and import libs +# Has to be a shell function in order to 'eat' the argument +# that is supplied when $file_magic_command is called. +win32_libid () { + win32_libid_type="unknown" + win32_fileres=`file -L $1 2>/dev/null` + case $win32_fileres in + *ar\ archive\ import\ library*) # definitely import + win32_libid_type="x86 archive import" + ;; + *ar\ archive*) # could be an import, or static + if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | \ + grep -E 'file format pe-i386(.*architecture: i386)?' >/dev/null ; then + win32_nmres=`eval $NM -f posix -A $1 | \ + sed -n -e '1,100{/ I /{x;/import/!{s/^/import/;h;p;};x;};}'` + if test "X$win32_nmres" = "Ximport" ; then + win32_libid_type="x86 archive import" + else + win32_libid_type="x86 archive static" + fi + fi + ;; + *DLL*) + win32_libid_type="x86 DLL" + ;; + *executable*) # but shell scripts are "executable" too... + case $win32_fileres in + *MS\ Windows\ PE\ Intel*) + win32_libid_type="x86 DLL" + ;; + esac + ;; + esac + $echo $win32_libid_type +} + +# End of Shell function definitions +##################################### + +# Parse our command line options once, thoroughly. +while test "$#" -gt 0 +do + arg="$1" + shift + + case $arg in + -*=*) optarg=`$echo "X$arg" | $Xsed -e 's/[-_a-zA-Z0-9]*=//'` ;; + *) optarg= ;; + esac + + # If the previous option needs an argument, assign it. + if test -n "$prev"; then + case $prev in + execute_dlfiles) + execute_dlfiles="$execute_dlfiles $arg" + ;; + tag) + tagname="$arg" + + # Check whether tagname contains only valid characters + case $tagname in + *[!-_A-Za-z0-9,/]*) + $echo "$progname: invalid tag name: $tagname" 1>&2 + exit 1 + ;; + esac + + case $tagname in + CC) + # Don't test for the "default" C tag, as we know, it's there, but + # not specially marked. + ;; + *) + if grep "^# ### BEGIN LIBTOOL TAG CONFIG: $tagname$" < "$0" > /dev/null; then + taglist="$taglist $tagname" + # Evaluate the configuration. + eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$tagname'$/,/^# ### END LIBTOOL TAG CONFIG: '$tagname'$/p' < $0`" + else + $echo "$progname: ignoring unknown tag $tagname" 1>&2 + fi + ;; + esac + ;; + *) + eval "$prev=\$arg" + ;; + esac + + prev= + prevopt= + continue + fi + + # Have we seen a non-optional argument yet? + case $arg in + --help) + show_help=yes + ;; + + --version) + $echo "$PROGRAM (GNU $PACKAGE) $VERSION$TIMESTAMP" + $echo + $echo "Copyright (C) 2003 Free Software Foundation, Inc." + $echo "This is free software; see the source for copying conditions. There is NO" + $echo "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + exit 0 + ;; + + --config) + ${SED} -e '1,/^# ### BEGIN LIBTOOL CONFIG/d' -e '/^# ### END LIBTOOL CONFIG/,$d' $0 + # Now print the configurations for the tags. + for tagname in $taglist; do + ${SED} -n -e "/^# ### BEGIN LIBTOOL TAG CONFIG: $tagname$/,/^# ### END LIBTOOL TAG CONFIG: $tagname$/p" < "$0" + done + exit 0 + ;; + + --debug) + $echo "$progname: enabling shell trace mode" + set -x + ;; + + --dry-run | -n) + run=: + ;; + + --features) + $echo "host: $host" + if test "$build_libtool_libs" = yes; then + $echo "enable shared libraries" + else + $echo "disable shared libraries" + fi + if test "$build_old_libs" = yes; then + $echo "enable static libraries" + else + $echo "disable static libraries" + fi + exit 0 + ;; + + --finish) mode="finish" ;; + + --mode) prevopt="--mode" prev=mode ;; + --mode=*) mode="$optarg" ;; + + --preserve-dup-deps) duplicate_deps="yes" ;; + + --quiet | --silent) + show=: + ;; + + --tag) prevopt="--tag" prev=tag ;; + --tag=*) + set tag "$optarg" ${1+"$@"} + shift + prev=tag + ;; + + -dlopen) + prevopt="-dlopen" + prev=execute_dlfiles + ;; + + -*) + $echo "$modename: unrecognized option \`$arg'" 1>&2 + $echo "$help" 1>&2 + exit 1 + ;; + + *) + nonopt="$arg" + break + ;; + esac +done + +if test -n "$prevopt"; then + $echo "$modename: option \`$prevopt' requires an argument" 1>&2 + $echo "$help" 1>&2 + exit 1 +fi + +# If this variable is set in any of the actions, the command in it +# will be execed at the end. This prevents here-documents from being +# left over by shells. +exec_cmd= + +if test -z "$show_help"; then + + # Infer the operation mode. + if test -z "$mode"; then + $echo "*** Warning: inferring the mode of operation is deprecated." 1>&2 + $echo "*** Future versions of Libtool will require -mode=MODE be specified." 1>&2 + case $nonopt in + *cc | cc* | *++ | gcc* | *-gcc* | g++* | xlc*) + mode=link + for arg + do + case $arg in + -c) + mode=compile + break + ;; + esac + done + ;; + *db | *dbx | *strace | *truss) + mode=execute + ;; + *install*|cp|mv) + mode=install + ;; + *rm) + mode=uninstall + ;; + *) + # If we have no mode, but dlfiles were specified, then do execute mode. + test -n "$execute_dlfiles" && mode=execute + + # Just use the default operation mode. + if test -z "$mode"; then + if test -n "$nonopt"; then + $echo "$modename: warning: cannot infer operation mode from \`$nonopt'" 1>&2 + else + $echo "$modename: warning: cannot infer operation mode without MODE-ARGS" 1>&2 + fi + fi + ;; + esac + fi + + # Only execute mode is allowed to have -dlopen flags. + if test -n "$execute_dlfiles" && test "$mode" != execute; then + $echo "$modename: unrecognized option \`-dlopen'" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + + # Change the help message to a mode-specific one. + generic_help="$help" + help="Try \`$modename --help --mode=$mode' for more information." + + # These modes are in order of execution frequency so that they run quickly. + case $mode in + # libtool compile mode + compile) + modename="$modename: compile" + # Get the compilation command and the source file. + base_compile= + srcfile="$nonopt" # always keep a non-empty value in "srcfile" + suppress_output= + arg_mode=normal + libobj= + + for arg + do + case "$arg_mode" in + arg ) + # do not "continue". Instead, add this to base_compile + lastarg="$arg" + arg_mode=normal + ;; + + target ) + libobj="$arg" + arg_mode=normal + continue + ;; + + normal ) + # Accept any command-line options. + case $arg in + -o) + if test -n "$libobj" ; then + $echo "$modename: you cannot specify \`-o' more than once" 1>&2 + exit 1 + fi + arg_mode=target + continue + ;; + + -static) + build_old_libs=yes + continue + ;; + + -prefer-pic) + pic_mode=yes + continue + ;; + + -prefer-non-pic) + pic_mode=no + continue + ;; + + -Xcompiler) + arg_mode=arg # the next one goes into the "base_compile" arg list + continue # The current "srcfile" will either be retained or + ;; # replaced later. I would guess that would be a bug. + + -Wc,*) + args=`$echo "X$arg" | $Xsed -e "s/^-Wc,//"` + lastarg= + save_ifs="$IFS"; IFS=',' + for arg in $args; do + IFS="$save_ifs" + + # Double-quote args containing other shell metacharacters. + # Many Bourne shells cannot handle close brackets correctly + # in scan sets, so we specify it separately. + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + arg="\"$arg\"" + ;; + esac + lastarg="$lastarg $arg" + done + IFS="$save_ifs" + lastarg=`$echo "X$lastarg" | $Xsed -e "s/^ //"` + + # Add the arguments to base_compile. + base_compile="$base_compile $lastarg" + continue + ;; + + * ) + # Accept the current argument as the source file. + # The previous "srcfile" becomes the current argument. + # + lastarg="$srcfile" + srcfile="$arg" + ;; + esac # case $arg + ;; + esac # case $arg_mode + + # Aesthetically quote the previous argument. + lastarg=`$echo "X$lastarg" | $Xsed -e "$sed_quote_subst"` + + case $lastarg in + # Double-quote args containing other shell metacharacters. + # Many Bourne shells cannot handle close brackets correctly + # in scan sets, so we specify it separately. + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + lastarg="\"$lastarg\"" + ;; + esac + + base_compile="$base_compile $lastarg" + done # for arg + + case $arg_mode in + arg) + $echo "$modename: you must specify an argument for -Xcompile" + exit 1 + ;; + target) + $echo "$modename: you must specify a target with \`-o'" 1>&2 + exit 1 + ;; + *) + # Get the name of the library object. + [ -z "$libobj" ] && libobj=`$echo "X$srcfile" | $Xsed -e 's%^.*/%%'` + ;; + esac + + # Recognize several different file suffixes. + # If the user specifies -o file.o, it is replaced with file.lo + xform='[cCFSifmso]' + case $libobj in + *.ada) xform=ada ;; + *.adb) xform=adb ;; + *.ads) xform=ads ;; + *.asm) xform=asm ;; + *.c++) xform=c++ ;; + *.cc) xform=cc ;; + *.ii) xform=ii ;; + *.class) xform=class ;; + *.cpp) xform=cpp ;; + *.cxx) xform=cxx ;; + *.f90) xform=f90 ;; + *.for) xform=for ;; + *.java) xform=java ;; + esac + + libobj=`$echo "X$libobj" | $Xsed -e "s/\.$xform$/.lo/"` + + case $libobj in + *.lo) obj=`$echo "X$libobj" | $Xsed -e "$lo2o"` ;; + *) + $echo "$modename: cannot determine name of library object from \`$libobj'" 1>&2 + exit 1 + ;; + esac + + # Infer tagged configuration to use if any are available and + # if one wasn't chosen via the "--tag" command line option. + # Only attempt this if the compiler in the base compile + # command doesn't match the default compiler. + if test -n "$available_tags" && test -z "$tagname"; then + case $base_compile in + # Blanks in the command may have been stripped by the calling shell, + # but not from the CC environment variable when configure was run. + " $CC "* | "$CC "* | " `$echo $CC` "* | "`$echo $CC` "*) ;; + # Blanks at the start of $base_compile will cause this to fail + # if we don't check for them as well. + *) + for z in $available_tags; do + if grep "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$0" > /dev/null; then + # Evaluate the configuration. + eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $0`" + case "$base_compile " in + "$CC "* | " $CC "* | "`$echo $CC` "* | " `$echo $CC` "*) + # The compiler in the base compile command matches + # the one in the tagged configuration. + # Assume this is the tagged configuration we want. + tagname=$z + break + ;; + esac + fi + done + # If $tagname still isn't set, then no tagged configuration + # was found and let the user know that the "--tag" command + # line option must be used. + if test -z "$tagname"; then + $echo "$modename: unable to infer tagged configuration" + $echo "$modename: specify a tag with \`--tag'" 1>&2 + exit 1 +# else +# $echo "$modename: using $tagname tagged configuration" + fi + ;; + esac + fi + + objname=`$echo "X$obj" | $Xsed -e 's%^.*/%%'` + xdir=`$echo "X$obj" | $Xsed -e 's%/[^/]*$%%'` + if test "X$xdir" = "X$obj"; then + xdir= + else + xdir=$xdir/ + fi + lobj=${xdir}$objdir/$objname + + if test -z "$base_compile"; then + $echo "$modename: you must specify a compilation command" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + + # Delete any leftover library objects. + if test "$build_old_libs" = yes; then + removelist="$obj $lobj $libobj ${libobj}T" + else + removelist="$lobj $libobj ${libobj}T" + fi + + $run $rm $removelist + trap "$run $rm $removelist; exit 1" 1 2 15 + + # On Cygwin there's no "real" PIC flag so we must build both object types + case $host_os in + cygwin* | mingw* | pw32* | os2*) + pic_mode=default + ;; + esac + if test "$pic_mode" = no && test "$deplibs_check_method" != pass_all; then + # non-PIC code in shared libraries is not supported + pic_mode=default + fi + + # Calculate the filename of the output object if compiler does + # not support -o with -c + if test "$compiler_c_o" = no; then + output_obj=`$echo "X$srcfile" | $Xsed -e 's%^.*/%%' -e 's%\.[^.]*$%%'`.${objext} + lockfile="$output_obj.lock" + removelist="$removelist $output_obj $lockfile" + trap "$run $rm $removelist; exit 1" 1 2 15 + else + output_obj= + need_locks=no + lockfile= + fi + + # Lock this critical section if it is needed + # We use this script file to make the link, it avoids creating a new file + if test "$need_locks" = yes; then + until $run ln "$0" "$lockfile" 2>/dev/null; do + $show "Waiting for $lockfile to be removed" + sleep 2 + done + elif test "$need_locks" = warn; then + if test -f "$lockfile"; then + $echo "\ +*** ERROR, $lockfile exists and contains: +`cat $lockfile 2>/dev/null` + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support \`-c' and \`-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $run $rm $removelist + exit 1 + fi + $echo $srcfile > "$lockfile" + fi + + if test -n "$fix_srcfile_path"; then + eval srcfile=\"$fix_srcfile_path\" + fi + + $run $rm "$libobj" "${libobj}T" + + # Create a libtool object file (analogous to a ".la" file), + # but don't create it if we're doing a dry run. + test -z "$run" && cat > ${libobj}T </dev/null`" != "X$srcfile"; then + $echo "\ +*** ERROR, $lockfile contains: +`cat $lockfile 2>/dev/null` + +but it should contain: +$srcfile + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support \`-c' and \`-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $run $rm $removelist + exit 1 + fi + + # Just move the object if needed, then go on to compile the next one + if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then + $show "$mv $output_obj $lobj" + if $run $mv $output_obj $lobj; then : + else + error=$? + $run $rm $removelist + exit $error + fi + fi + + # Append the name of the PIC object to the libtool object file. + test -z "$run" && cat >> ${libobj}T <> ${libobj}T </dev/null`" != "X$srcfile"; then + $echo "\ +*** ERROR, $lockfile contains: +`cat $lockfile 2>/dev/null` + +but it should contain: +$srcfile + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support \`-c' and \`-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $run $rm $removelist + exit 1 + fi + + # Just move the object if needed + if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then + $show "$mv $output_obj $obj" + if $run $mv $output_obj $obj; then : + else + error=$? + $run $rm $removelist + exit $error + fi + fi + + # Append the name of the non-PIC object the libtool object file. + # Only append if the libtool object file exists. + test -z "$run" && cat >> ${libobj}T <> ${libobj}T <&2 + fi + if test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + else + if test -z "$pic_flag" && test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + fi + build_libtool_libs=no + build_old_libs=yes + prefer_static_libs=yes + break + ;; + esac + done + + # See if our shared archives depend on static archives. + test -n "$old_archive_from_new_cmds" && build_old_libs=yes + + # Go through the arguments, transforming them on the way. + while test "$#" -gt 0; do + arg="$1" + base_compile="$base_compile $arg" + shift + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + qarg=\"`$echo "X$arg" | $Xsed -e "$sed_quote_subst"`\" ### testsuite: skip nested quoting test + ;; + *) qarg=$arg ;; + esac + libtool_args="$libtool_args $qarg" + + # If the previous option needs an argument, assign it. + if test -n "$prev"; then + case $prev in + output) + compile_command="$compile_command @OUTPUT@" + finalize_command="$finalize_command @OUTPUT@" + ;; + esac + + case $prev in + dlfiles|dlprefiles) + if test "$preload" = no; then + # Add the symbol object into the linking commands. + compile_command="$compile_command @SYMFILE@" + finalize_command="$finalize_command @SYMFILE@" + preload=yes + fi + case $arg in + *.la | *.lo) ;; # We handle these cases below. + force) + if test "$dlself" = no; then + dlself=needless + export_dynamic=yes + fi + prev= + continue + ;; + self) + if test "$prev" = dlprefiles; then + dlself=yes + elif test "$prev" = dlfiles && test "$dlopen_self" != yes; then + dlself=yes + else + dlself=needless + export_dynamic=yes + fi + prev= + continue + ;; + *) + if test "$prev" = dlfiles; then + dlfiles="$dlfiles $arg" + else + dlprefiles="$dlprefiles $arg" + fi + prev= + continue + ;; + esac + ;; + expsyms) + export_symbols="$arg" + if test ! -f "$arg"; then + $echo "$modename: symbol file \`$arg' does not exist" + exit 1 + fi + prev= + continue + ;; + expsyms_regex) + export_symbols_regex="$arg" + prev= + continue + ;; + inst_prefix) + inst_prefix_dir="$arg" + prev= + continue + ;; + release) + release="-$arg" + prev= + continue + ;; + objectlist) + if test -f "$arg"; then + save_arg=$arg + moreargs= + for fil in `cat $save_arg` + do +# moreargs="$moreargs $fil" + arg=$fil + # A libtool-controlled object. + + # Check to see that this really is a libtool object. + if (${SED} -e '2q' $arg | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then + pic_object= + non_pic_object= + + # Read the .lo file + # If there is no directory component, then add one. + case $arg in + */* | *\\*) . $arg ;; + *) . ./$arg ;; + esac + + if test -z "$pic_object" || \ + test -z "$non_pic_object" || + test "$pic_object" = none && \ + test "$non_pic_object" = none; then + $echo "$modename: cannot find name of object for \`$arg'" 1>&2 + exit 1 + fi + + # Extract subdirectory from the argument. + xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'` + if test "X$xdir" = "X$arg"; then + xdir= + else + xdir="$xdir/" + fi + + if test "$pic_object" != none; then + # Prepend the subdirectory the object is found in. + pic_object="$xdir$pic_object" + + if test "$prev" = dlfiles; then + if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then + dlfiles="$dlfiles $pic_object" + prev= + continue + else + # If libtool objects are unsupported, then we need to preload. + prev=dlprefiles + fi + fi + + # CHECK ME: I think I busted this. -Ossama + if test "$prev" = dlprefiles; then + # Preload the old-style object. + dlprefiles="$dlprefiles $pic_object" + prev= + fi + + # A PIC object. + libobjs="$libobjs $pic_object" + arg="$pic_object" + fi + + # Non-PIC object. + if test "$non_pic_object" != none; then + # Prepend the subdirectory the object is found in. + non_pic_object="$xdir$non_pic_object" + + # A standard non-PIC object + non_pic_objects="$non_pic_objects $non_pic_object" + if test -z "$pic_object" || test "$pic_object" = none ; then + arg="$non_pic_object" + fi + fi + else + # Only an error if not doing a dry-run. + if test -z "$run"; then + $echo "$modename: \`$arg' is not a valid libtool object" 1>&2 + exit 1 + else + # Dry-run case. + + # Extract subdirectory from the argument. + xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'` + if test "X$xdir" = "X$arg"; then + xdir= + else + xdir="$xdir/" + fi + + pic_object=`$echo "X${xdir}${objdir}/${arg}" | $Xsed -e "$lo2o"` + non_pic_object=`$echo "X${xdir}${arg}" | $Xsed -e "$lo2o"` + libobjs="$libobjs $pic_object" + non_pic_objects="$non_pic_objects $non_pic_object" + fi + fi + done + else + $echo "$modename: link input file \`$save_arg' does not exist" + exit 1 + fi + arg=$save_arg + prev= + continue + ;; + rpath | xrpath) + # We need an absolute path. + case $arg in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + $echo "$modename: only absolute run-paths are allowed" 1>&2 + exit 1 + ;; + esac + if test "$prev" = rpath; then + case "$rpath " in + *" $arg "*) ;; + *) rpath="$rpath $arg" ;; + esac + else + case "$xrpath " in + *" $arg "*) ;; + *) xrpath="$xrpath $arg" ;; + esac + fi + prev= + continue + ;; + xcompiler) + compiler_flags="$compiler_flags $qarg" + prev= + compile_command="$compile_command $qarg" + finalize_command="$finalize_command $qarg" + continue + ;; + xlinker) + linker_flags="$linker_flags $qarg" + compiler_flags="$compiler_flags $wl$qarg" + prev= + compile_command="$compile_command $wl$qarg" + finalize_command="$finalize_command $wl$qarg" + continue + ;; + xcclinker) + linker_flags="$linker_flags $qarg" + compiler_flags="$compiler_flags $qarg" + prev= + compile_command="$compile_command $qarg" + finalize_command="$finalize_command $qarg" + continue + ;; + *) + eval "$prev=\"\$arg\"" + prev= + continue + ;; + esac + fi # test -n "$prev" + + prevarg="$arg" + + case $arg in + -all-static) + if test -n "$link_static_flag"; then + compile_command="$compile_command $link_static_flag" + finalize_command="$finalize_command $link_static_flag" + fi + continue + ;; + + -allow-undefined) + # FIXME: remove this flag sometime in the future. + $echo "$modename: \`-allow-undefined' is deprecated because it is the default" 1>&2 + continue + ;; + + -avoid-version) + avoid_version=yes + continue + ;; + + -dlopen) + prev=dlfiles + continue + ;; + + -dlpreopen) + prev=dlprefiles + continue + ;; + + -export-dynamic) + export_dynamic=yes + continue + ;; + + -export-symbols | -export-symbols-regex) + if test -n "$export_symbols" || test -n "$export_symbols_regex"; then + $echo "$modename: more than one -exported-symbols argument is not allowed" + exit 1 + fi + if test "X$arg" = "X-export-symbols"; then + prev=expsyms + else + prev=expsyms_regex + fi + continue + ;; + + -inst-prefix-dir) + prev=inst_prefix + continue + ;; + + # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:* + # so, if we see these flags be careful not to treat them like -L + -L[A-Z][A-Z]*:*) + case $with_gcc/$host in + no/*-*-irix* | /*-*-irix*) + compile_command="$compile_command $arg" + finalize_command="$finalize_command $arg" + ;; + esac + continue + ;; + + -L*) + dir=`$echo "X$arg" | $Xsed -e 's/^-L//'` + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + absdir=`cd "$dir" && pwd` + if test -z "$absdir"; then + $echo "$modename: cannot determine absolute directory name of \`$dir'" 1>&2 + exit 1 + fi + dir="$absdir" + ;; + esac + case "$deplibs " in + *" -L$dir "*) ;; + *) + deplibs="$deplibs -L$dir" + lib_search_path="$lib_search_path $dir" + ;; + esac + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2*) + case :$dllsearchpath: in + *":$dir:"*) ;; + *) dllsearchpath="$dllsearchpath:$dir";; + esac + ;; + esac + continue + ;; + + -l*) + if test "X$arg" = "X-lc" || test "X$arg" = "X-lm"; then + case $host in + *-*-cygwin* | *-*-pw32* | *-*-beos*) + # These systems don't actually have a C or math library (as such) + continue + ;; + *-*-mingw* | *-*-os2*) + # These systems don't actually have a C library (as such) + test "X$arg" = "X-lc" && continue + ;; + *-*-openbsd* | *-*-freebsd*) + # Do not include libc due to us having libc/libc_r. + test "X$arg" = "X-lc" && continue + ;; + *-*-rhapsody* | *-*-darwin1.[012]) + # Rhapsody C and math libraries are in the System framework + deplibs="$deplibs -framework System" + continue + esac + elif test "X$arg" = "X-lc_r"; then + case $host in + *-*-openbsd* | *-*-freebsd*) + # Do not include libc_r directly, use -pthread flag. + continue + ;; + esac + fi + deplibs="$deplibs $arg" + continue + ;; + + -module) + module=yes + continue + ;; + + # gcc -m* arguments should be passed to the linker via $compiler_flags + # in order to pass architecture information to the linker + # (e.g. 32 vs 64-bit). This may also be accomplished via -Wl,-mfoo + # but this is not reliable with gcc because gcc may use -mfoo to + # select a different linker, different libraries, etc, while + # -Wl,-mfoo simply passes -mfoo to the linker. + -m*) + # Unknown arguments in both finalize_command and compile_command need + # to be aesthetically quoted because they are evaled later. + arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + arg="\"$arg\"" + ;; + esac + compile_command="$compile_command $arg" + finalize_command="$finalize_command $arg" + if test "$with_gcc" = "yes" ; then + compiler_flags="$compiler_flags $arg" + fi + continue + ;; + + -shrext) + prev=shrext + continue + ;; + + -no-fast-install) + fast_install=no + continue + ;; + + -no-install) + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2*) + # The PATH hackery in wrapper scripts is required on Windows + # in order for the loader to find any dlls it needs. + $echo "$modename: warning: \`-no-install' is ignored for $host" 1>&2 + $echo "$modename: warning: assuming \`-no-fast-install' instead" 1>&2 + fast_install=no + ;; + *) no_install=yes ;; + esac + continue + ;; + + -no-undefined) + allow_undefined=no + continue + ;; + + -objectlist) + prev=objectlist + continue + ;; + + -o) prev=output ;; + + -release) + prev=release + continue + ;; + + -rpath) + prev=rpath + continue + ;; + + -R) + prev=xrpath + continue + ;; + + -R*) + dir=`$echo "X$arg" | $Xsed -e 's/^-R//'` + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + $echo "$modename: only absolute run-paths are allowed" 1>&2 + exit 1 + ;; + esac + case "$xrpath " in + *" $dir "*) ;; + *) xrpath="$xrpath $dir" ;; + esac + continue + ;; + + -static) + # The effects of -static are defined in a previous loop. + # We used to do the same as -all-static on platforms that + # didn't have a PIC flag, but the assumption that the effects + # would be equivalent was wrong. It would break on at least + # Digital Unix and AIX. + continue + ;; + + -thread-safe) + thread_safe=yes + continue + ;; + + -version-info) + prev=vinfo + continue + ;; + -version-number) + prev=vinfo + vinfo_number=yes + continue + ;; + + -Wc,*) + args=`$echo "X$arg" | $Xsed -e "$sed_quote_subst" -e 's/^-Wc,//'` + arg= + save_ifs="$IFS"; IFS=',' + for flag in $args; do + IFS="$save_ifs" + case $flag in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + flag="\"$flag\"" + ;; + esac + arg="$arg $wl$flag" + compiler_flags="$compiler_flags $flag" + done + IFS="$save_ifs" + arg=`$echo "X$arg" | $Xsed -e "s/^ //"` + ;; + + -Wl,*) + args=`$echo "X$arg" | $Xsed -e "$sed_quote_subst" -e 's/^-Wl,//'` + arg= + save_ifs="$IFS"; IFS=',' + for flag in $args; do + IFS="$save_ifs" + case $flag in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + flag="\"$flag\"" + ;; + esac + arg="$arg $wl$flag" + compiler_flags="$compiler_flags $wl$flag" + linker_flags="$linker_flags $flag" + done + IFS="$save_ifs" + arg=`$echo "X$arg" | $Xsed -e "s/^ //"` + ;; + + -Xcompiler) + prev=xcompiler + continue + ;; + + -Xlinker) + prev=xlinker + continue + ;; + + -XCClinker) + prev=xcclinker + continue + ;; + + # Some other compiler flag. + -* | +*) + # Unknown arguments in both finalize_command and compile_command need + # to be aesthetically quoted because they are evaled later. + arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + arg="\"$arg\"" + ;; + esac + ;; + + *.$objext) + # A standard object. + objs="$objs $arg" + ;; + + *.lo) + # A libtool-controlled object. + + # Check to see that this really is a libtool object. + if (${SED} -e '2q' $arg | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then + pic_object= + non_pic_object= + + # Read the .lo file + # If there is no directory component, then add one. + case $arg in + */* | *\\*) . $arg ;; + *) . ./$arg ;; + esac + + if test -z "$pic_object" || \ + test -z "$non_pic_object" || + test "$pic_object" = none && \ + test "$non_pic_object" = none; then + $echo "$modename: cannot find name of object for \`$arg'" 1>&2 + exit 1 + fi + + # Extract subdirectory from the argument. + xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'` + if test "X$xdir" = "X$arg"; then + xdir= + else + xdir="$xdir/" + fi + + if test "$pic_object" != none; then + # Prepend the subdirectory the object is found in. + pic_object="$xdir$pic_object" + + if test "$prev" = dlfiles; then + if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then + dlfiles="$dlfiles $pic_object" + prev= + continue + else + # If libtool objects are unsupported, then we need to preload. + prev=dlprefiles + fi + fi + + # CHECK ME: I think I busted this. -Ossama + if test "$prev" = dlprefiles; then + # Preload the old-style object. + dlprefiles="$dlprefiles $pic_object" + prev= + fi + + # A PIC object. + libobjs="$libobjs $pic_object" + arg="$pic_object" + fi + + # Non-PIC object. + if test "$non_pic_object" != none; then + # Prepend the subdirectory the object is found in. + non_pic_object="$xdir$non_pic_object" + + # A standard non-PIC object + non_pic_objects="$non_pic_objects $non_pic_object" + if test -z "$pic_object" || test "$pic_object" = none ; then + arg="$non_pic_object" + fi + fi + else + # Only an error if not doing a dry-run. + if test -z "$run"; then + $echo "$modename: \`$arg' is not a valid libtool object" 1>&2 + exit 1 + else + # Dry-run case. + + # Extract subdirectory from the argument. + xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'` + if test "X$xdir" = "X$arg"; then + xdir= + else + xdir="$xdir/" + fi + + pic_object=`$echo "X${xdir}${objdir}/${arg}" | $Xsed -e "$lo2o"` + non_pic_object=`$echo "X${xdir}${arg}" | $Xsed -e "$lo2o"` + libobjs="$libobjs $pic_object" + non_pic_objects="$non_pic_objects $non_pic_object" + fi + fi + ;; + + *.$libext) + # An archive. + deplibs="$deplibs $arg" + old_deplibs="$old_deplibs $arg" + continue + ;; + + *.la) + # A libtool-controlled library. + + if test "$prev" = dlfiles; then + # This library was specified with -dlopen. + dlfiles="$dlfiles $arg" + prev= + elif test "$prev" = dlprefiles; then + # The library was specified with -dlpreopen. + dlprefiles="$dlprefiles $arg" + prev= + else + deplibs="$deplibs $arg" + fi + continue + ;; + + # Some other compiler argument. + *) + # Unknown arguments in both finalize_command and compile_command need + # to be aesthetically quoted because they are evaled later. + arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + arg="\"$arg\"" + ;; + esac + ;; + esac # arg + + # Now actually substitute the argument into the commands. + if test -n "$arg"; then + compile_command="$compile_command $arg" + finalize_command="$finalize_command $arg" + fi + done # argument parsing loop + + if test -n "$prev"; then + $echo "$modename: the \`$prevarg' option requires an argument" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + + # Infer tagged configuration to use if any are available and + # if one wasn't chosen via the "--tag" command line option. + # Only attempt this if the compiler in the base link + # command doesn't match the default compiler. + if test -n "$available_tags" && test -z "$tagname"; then + case $base_compile in + # Blanks in the command may have been stripped by the calling shell, + # but not from the CC environment variable when configure was run. + "$CC "* | " $CC "* | "`$echo $CC` "* | " `$echo $CC` "*) ;; + # Blanks at the start of $base_compile will cause this to fail + # if we don't check for them as well. + *) + for z in $available_tags; do + if grep "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$0" > /dev/null; then + # Evaluate the configuration. + eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $0`" + case $base_compile in + "$CC "* | " $CC "* | "`$echo $CC` "* | " `$echo $CC` "*) + # The compiler in $compile_command matches + # the one in the tagged configuration. + # Assume this is the tagged configuration we want. + tagname=$z + break + ;; + esac + fi + done + # If $tagname still isn't set, then no tagged configuration + # was found and let the user know that the "--tag" command + # line option must be used. + if test -z "$tagname"; then + $echo "$modename: unable to infer tagged configuration" + $echo "$modename: specify a tag with \`--tag'" 1>&2 + exit 1 +# else +# $echo "$modename: using $tagname tagged configuration" + fi + ;; + esac + fi + + if test "$export_dynamic" = yes && test -n "$export_dynamic_flag_spec"; then + eval arg=\"$export_dynamic_flag_spec\" + compile_command="$compile_command $arg" + finalize_command="$finalize_command $arg" + fi + + oldlibs= + # calculate the name of the file, without its directory + outputname=`$echo "X$output" | $Xsed -e 's%^.*/%%'` + libobjs_save="$libobjs" + + if test -n "$shlibpath_var"; then + # get the directories listed in $shlibpath_var + eval shlib_search_path=\`\$echo \"X\${$shlibpath_var}\" \| \$Xsed -e \'s/:/ /g\'\` + else + shlib_search_path= + fi + eval sys_lib_search_path=\"$sys_lib_search_path_spec\" + eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\" + + output_objdir=`$echo "X$output" | $Xsed -e 's%/[^/]*$%%'` + if test "X$output_objdir" = "X$output"; then + output_objdir="$objdir" + else + output_objdir="$output_objdir/$objdir" + fi + # Create the object directory. + if test ! -d "$output_objdir"; then + $show "$mkdir $output_objdir" + $run $mkdir $output_objdir + status=$? + if test "$status" -ne 0 && test ! -d "$output_objdir"; then + exit $status + fi + fi + + # Determine the type of output + case $output in + "") + $echo "$modename: you must specify an output file" 1>&2 + $echo "$help" 1>&2 + exit 1 + ;; + *.$libext) linkmode=oldlib ;; + *.lo | *.$objext) linkmode=obj ;; + *.la) linkmode=lib ;; + *) linkmode=prog ;; # Anything else should be a program. + esac + + case $host in + *cygwin* | *mingw* | *pw32*) + # don't eliminate duplcations in $postdeps and $predeps + duplicate_compiler_generated_deps=yes + ;; + *) + duplicate_compiler_generated_deps=$duplicate_deps + ;; + esac + specialdeplibs= + + libs= + # Find all interdependent deplibs by searching for libraries + # that are linked more than once (e.g. -la -lb -la) + for deplib in $deplibs; do + if test "X$duplicate_deps" = "Xyes" ; then + case "$libs " in + *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; + esac + fi + libs="$libs $deplib" + done + + if test "$linkmode" = lib; then + libs="$predeps $libs $compiler_lib_search_path $postdeps" + + # Compute libraries that are listed more than once in $predeps + # $postdeps and mark them as special (i.e., whose duplicates are + # not to be eliminated). + pre_post_deps= + if test "X$duplicate_compiler_generated_deps" = "Xyes" ; then + for pre_post_dep in $predeps $postdeps; do + case "$pre_post_deps " in + *" $pre_post_dep "*) specialdeplibs="$specialdeplibs $pre_post_deps" ;; + esac + pre_post_deps="$pre_post_deps $pre_post_dep" + done + fi + pre_post_deps= + fi + + deplibs= + newdependency_libs= + newlib_search_path= + need_relink=no # whether we're linking any uninstalled libtool libraries + notinst_deplibs= # not-installed libtool libraries + notinst_path= # paths that contain not-installed libtool libraries + case $linkmode in + lib) + passes="conv link" + for file in $dlfiles $dlprefiles; do + case $file in + *.la) ;; + *) + $echo "$modename: libraries can \`-dlopen' only libtool libraries: $file" 1>&2 + exit 1 + ;; + esac + done + ;; + prog) + compile_deplibs= + finalize_deplibs= + alldeplibs=no + newdlfiles= + newdlprefiles= + passes="conv scan dlopen dlpreopen link" + ;; + *) passes="conv" + ;; + esac + for pass in $passes; do + if test "$linkmode,$pass" = "lib,link" || + test "$linkmode,$pass" = "prog,scan"; then + libs="$deplibs" + deplibs= + fi + if test "$linkmode" = prog; then + case $pass in + dlopen) libs="$dlfiles" ;; + dlpreopen) libs="$dlprefiles" ;; + link) libs="$deplibs %DEPLIBS% $dependency_libs" ;; + esac + fi + if test "$pass" = dlopen; then + # Collect dlpreopened libraries + save_deplibs="$deplibs" + deplibs= + fi + for deplib in $libs; do + lib= + found=no + case $deplib in + -l*) + if test "$linkmode" != lib && test "$linkmode" != prog; then + $echo "$modename: warning: \`-l' is ignored for archives/objects" 1>&2 + continue + fi + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + continue + fi + name=`$echo "X$deplib" | $Xsed -e 's/^-l//'` + for searchdir in $newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path; do + # Search the libtool library + lib="$searchdir/lib${name}.la" + if test -f "$lib"; then + found=yes + break + fi + done + if test "$found" != yes; then + # deplib doesn't seem to be a libtool library + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" + fi + continue + else # deplib is a libtool library + # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib, + # We need to do some special things here, and not later. + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + case " $predeps $postdeps " in + *" $deplib "*) + if (${SED} -e '2q' $lib | + grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then + library_names= + old_library= + case $lib in + */* | *\\*) . $lib ;; + *) . ./$lib ;; + esac + for l in $old_library $library_names; do + ll="$l" + done + if test "X$ll" = "X$old_library" ; then # only static version available + found=no + ladir=`$echo "X$lib" | $Xsed -e 's%/[^/]*$%%'` + test "X$ladir" = "X$lib" && ladir="." + lib=$ladir/$old_library + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" + fi + continue + fi + fi + ;; + *) ;; + esac + fi + fi + ;; # -l + -L*) + case $linkmode in + lib) + deplibs="$deplib $deplibs" + test "$pass" = conv && continue + newdependency_libs="$deplib $newdependency_libs" + newlib_search_path="$newlib_search_path "`$echo "X$deplib" | $Xsed -e 's/^-L//'` + ;; + prog) + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + continue + fi + if test "$pass" = scan; then + deplibs="$deplib $deplibs" + newlib_search_path="$newlib_search_path "`$echo "X$deplib" | $Xsed -e 's/^-L//'` + else + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + fi + ;; + *) + $echo "$modename: warning: \`-L' is ignored for archives/objects" 1>&2 + ;; + esac # linkmode + continue + ;; # -L + -R*) + if test "$pass" = link; then + dir=`$echo "X$deplib" | $Xsed -e 's/^-R//'` + # Make sure the xrpath contains only unique directories. + case "$xrpath " in + *" $dir "*) ;; + *) xrpath="$xrpath $dir" ;; + esac + fi + deplibs="$deplib $deplibs" + continue + ;; + *.la) lib="$deplib" ;; + *.$libext) + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + continue + fi + case $linkmode in + lib) + if test "$deplibs_check_method" != pass_all; then + $echo + $echo "*** Warning: Trying to link with static lib archive $deplib." + $echo "*** I have the capability to make that library automatically link in when" + $echo "*** you link to this library. But I can only do this if you have a" + $echo "*** shared version of the library, which you do not appear to have" + $echo "*** because the file extensions .$libext of this argument makes me believe" + $echo "*** that it is just a static archive that I should not used here." + else + $echo + $echo "*** Warning: Linking the shared library $output against the" + $echo "*** static library $deplib is not portable!" + deplibs="$deplib $deplibs" + fi + continue + ;; + prog) + if test "$pass" != link; then + deplibs="$deplib $deplibs" + else + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + fi + continue + ;; + esac # linkmode + ;; # *.$libext + *.lo | *.$objext) + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + elif test "$linkmode" = prog; then + if test "$pass" = dlpreopen || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then + # If there is no dlopen support or we're linking statically, + # we need to preload. + newdlprefiles="$newdlprefiles $deplib" + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + newdlfiles="$newdlfiles $deplib" + fi + fi + continue + ;; + %DEPLIBS%) + alldeplibs=yes + continue + ;; + esac # case $deplib + if test "$found" = yes || test -f "$lib"; then : + else + $echo "$modename: cannot find the library \`$lib'" 1>&2 + exit 1 + fi + + # Check to see that this really is a libtool archive. + if (${SED} -e '2q' $lib | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then : + else + $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2 + exit 1 + fi + + ladir=`$echo "X$lib" | $Xsed -e 's%/[^/]*$%%'` + test "X$ladir" = "X$lib" && ladir="." + + dlname= + dlopen= + dlpreopen= + libdir= + library_names= + old_library= + # If the library was installed with an old release of libtool, + # it will not redefine variables installed, or shouldnotlink + installed=yes + shouldnotlink=no + + # Read the .la file + case $lib in + */* | *\\*) . $lib ;; + *) . ./$lib ;; + esac + + if test "$linkmode,$pass" = "lib,link" || + test "$linkmode,$pass" = "prog,scan" || + { test "$linkmode" != prog && test "$linkmode" != lib; }; then + test -n "$dlopen" && dlfiles="$dlfiles $dlopen" + test -n "$dlpreopen" && dlprefiles="$dlprefiles $dlpreopen" + fi + + if test "$pass" = conv; then + # Only check for convenience libraries + deplibs="$lib $deplibs" + if test -z "$libdir"; then + if test -z "$old_library"; then + $echo "$modename: cannot find name of link library for \`$lib'" 1>&2 + exit 1 + fi + # It is a libtool convenience library, so add in its objects. + convenience="$convenience $ladir/$objdir/$old_library" + old_convenience="$old_convenience $ladir/$objdir/$old_library" + tmp_libs= + for deplib in $dependency_libs; do + deplibs="$deplib $deplibs" + if test "X$duplicate_deps" = "Xyes" ; then + case "$tmp_libs " in + *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; + esac + fi + tmp_libs="$tmp_libs $deplib" + done + elif test "$linkmode" != prog && test "$linkmode" != lib; then + $echo "$modename: \`$lib' is not a convenience library" 1>&2 + exit 1 + fi + continue + fi # $pass = conv + + + # Get the name of the library we link against. + linklib= + for l in $old_library $library_names; do + linklib="$l" + done + if test -z "$linklib"; then + $echo "$modename: cannot find name of link library for \`$lib'" 1>&2 + exit 1 + fi + + # This library was specified with -dlopen. + if test "$pass" = dlopen; then + if test -z "$libdir"; then + $echo "$modename: cannot -dlopen a convenience library: \`$lib'" 1>&2 + exit 1 + fi + if test -z "$dlname" || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then + # If there is no dlname, no dlopen support or we're linking + # statically, we need to preload. We also need to preload any + # dependent libraries so libltdl's deplib preloader doesn't + # bomb out in the load deplibs phase. + dlprefiles="$dlprefiles $lib $dependency_libs" + else + newdlfiles="$newdlfiles $lib" + fi + continue + fi # $pass = dlopen + + # We need an absolute path. + case $ladir in + [\\/]* | [A-Za-z]:[\\/]*) abs_ladir="$ladir" ;; + *) + abs_ladir=`cd "$ladir" && pwd` + if test -z "$abs_ladir"; then + $echo "$modename: warning: cannot determine absolute directory name of \`$ladir'" 1>&2 + $echo "$modename: passing it literally to the linker, although it might fail" 1>&2 + abs_ladir="$ladir" + fi + ;; + esac + laname=`$echo "X$lib" | $Xsed -e 's%^.*/%%'` + + # Find the relevant object directory and library name. + if test "X$installed" = Xyes; then + if test ! -f "$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then + $echo "$modename: warning: library \`$lib' was moved." 1>&2 + dir="$ladir" + absdir="$abs_ladir" + libdir="$abs_ladir" + else + dir="$libdir" + absdir="$libdir" + fi + else + dir="$ladir/$objdir" + absdir="$abs_ladir/$objdir" + # Remove this search path later + notinst_path="$notinst_path $abs_ladir" + fi # $installed = yes + name=`$echo "X$laname" | $Xsed -e 's/\.la$//' -e 's/^lib//'` + + # This library was specified with -dlpreopen. + if test "$pass" = dlpreopen; then + if test -z "$libdir"; then + $echo "$modename: cannot -dlpreopen a convenience library: \`$lib'" 1>&2 + exit 1 + fi + # Prefer using a static library (so that no silly _DYNAMIC symbols + # are required to link). + if test -n "$old_library"; then + newdlprefiles="$newdlprefiles $dir/$old_library" + # Otherwise, use the dlname, so that lt_dlopen finds it. + elif test -n "$dlname"; then + newdlprefiles="$newdlprefiles $dir/$dlname" + else + newdlprefiles="$newdlprefiles $dir/$linklib" + fi + fi # $pass = dlpreopen + + if test -z "$libdir"; then + # Link the convenience library + if test "$linkmode" = lib; then + deplibs="$dir/$old_library $deplibs" + elif test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$dir/$old_library $compile_deplibs" + finalize_deplibs="$dir/$old_library $finalize_deplibs" + else + deplibs="$lib $deplibs" # used for prog,scan pass + fi + continue + fi + + + if test "$linkmode" = prog && test "$pass" != link; then + newlib_search_path="$newlib_search_path $ladir" + deplibs="$lib $deplibs" + + linkalldeplibs=no + if test "$link_all_deplibs" != no || test -z "$library_names" || + test "$build_libtool_libs" = no; then + linkalldeplibs=yes + fi + + tmp_libs= + for deplib in $dependency_libs; do + case $deplib in + -L*) newlib_search_path="$newlib_search_path "`$echo "X$deplib" | $Xsed -e 's/^-L//'`;; ### testsuite: skip nested quoting test + esac + # Need to link against all dependency_libs? + if test "$linkalldeplibs" = yes; then + deplibs="$deplib $deplibs" + else + # Need to hardcode shared library paths + # or/and link against static libraries + newdependency_libs="$deplib $newdependency_libs" + fi + if test "X$duplicate_deps" = "Xyes" ; then + case "$tmp_libs " in + *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; + esac + fi + tmp_libs="$tmp_libs $deplib" + done # for deplib + continue + fi # $linkmode = prog... + + if test "$linkmode,$pass" = "prog,link"; then + if test -n "$library_names" && + { test "$prefer_static_libs" = no || test -z "$old_library"; }; then + # We need to hardcode the library path + if test -n "$shlibpath_var"; then + # Make sure the rpath contains only unique directories. + case "$temp_rpath " in + *" $dir "*) ;; + *" $absdir "*) ;; + *) temp_rpath="$temp_rpath $dir" ;; + esac + fi + + # Hardcode the library path. + # Skip directories that are in the system default run-time + # search path. + case " $sys_lib_dlsearch_path " in + *" $absdir "*) ;; + *) + case "$compile_rpath " in + *" $absdir "*) ;; + *) compile_rpath="$compile_rpath $absdir" + esac + ;; + esac + case " $sys_lib_dlsearch_path " in + *" $libdir "*) ;; + *) + case "$finalize_rpath " in + *" $libdir "*) ;; + *) finalize_rpath="$finalize_rpath $libdir" + esac + ;; + esac + fi # $linkmode,$pass = prog,link... + + if test "$alldeplibs" = yes && + { test "$deplibs_check_method" = pass_all || + { test "$build_libtool_libs" = yes && + test -n "$library_names"; }; }; then + # We only need to search for static libraries + continue + fi + fi + + link_static=no # Whether the deplib will be linked statically + if test -n "$library_names" && + { test "$prefer_static_libs" = no || test -z "$old_library"; }; then + if test "$installed" = no; then + notinst_deplibs="$notinst_deplibs $lib" + need_relink=yes + fi + # This is a shared library + + # Warn about portability, can't link against -module's on some systems (darwin) + if test "$shouldnotlink" = yes && test "$pass" = link ; then + $echo + if test "$linkmode" = prog; then + $echo "*** Warning: Linking the executable $output against the loadable module" + else + $echo "*** Warning: Linking the shared library $output against the loadable module" + fi + $echo "*** $linklib is not portable!" + fi + if test "$linkmode" = lib && + test "$hardcode_into_libs" = yes; then + # Hardcode the library path. + # Skip directories that are in the system default run-time + # search path. + case " $sys_lib_dlsearch_path " in + *" $absdir "*) ;; + *) + case "$compile_rpath " in + *" $absdir "*) ;; + *) compile_rpath="$compile_rpath $absdir" + esac + ;; + esac + case " $sys_lib_dlsearch_path " in + *" $libdir "*) ;; + *) + case "$finalize_rpath " in + *" $libdir "*) ;; + *) finalize_rpath="$finalize_rpath $libdir" + esac + ;; + esac + fi + + if test -n "$old_archive_from_expsyms_cmds"; then + # figure out the soname + set dummy $library_names + realname="$2" + shift; shift + libname=`eval \\$echo \"$libname_spec\"` + # use dlname if we got it. it's perfectly good, no? + if test -n "$dlname"; then + soname="$dlname" + elif test -n "$soname_spec"; then + # bleh windows + case $host in + *cygwin* | mingw*) + major=`expr $current - $age` + versuffix="-$major" + ;; + esac + eval soname=\"$soname_spec\" + else + soname="$realname" + fi + + # Make a new name for the extract_expsyms_cmds to use + soroot="$soname" + soname=`$echo $soroot | ${SED} -e 's/^.*\///'` + newlib="libimp-`$echo $soname | ${SED} 's/^lib//;s/\.dll$//'`.a" + + # If the library has no export list, then create one now + if test -f "$output_objdir/$soname-def"; then : + else + $show "extracting exported symbol list from \`$soname'" + save_ifs="$IFS"; IFS='~' + eval cmds=\"$extract_expsyms_cmds\" + for cmd in $cmds; do + IFS="$save_ifs" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + fi + + # Create $newlib + if test -f "$output_objdir/$newlib"; then :; else + $show "generating import library for \`$soname'" + save_ifs="$IFS"; IFS='~' + eval cmds=\"$old_archive_from_expsyms_cmds\" + for cmd in $cmds; do + IFS="$save_ifs" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + fi + # make sure the library variables are pointing to the new library + dir=$output_objdir + linklib=$newlib + fi # test -n "$old_archive_from_expsyms_cmds" + + if test "$linkmode" = prog || test "$mode" != relink; then + add_shlibpath= + add_dir= + add= + lib_linked=yes + case $hardcode_action in + immediate | unsupported) + if test "$hardcode_direct" = no; then + add="$dir/$linklib" + case $host in + *-*-sco3.2v5* ) add_dir="-L$dir" ;; + *-*-darwin* ) + # if the lib is a module then we can not link against it, someone + # is ignoring the new warnings I added + if /usr/bin/file -L $add 2> /dev/null | grep "bundle" >/dev/null ; then + $echo "** Warning, lib $linklib is a module, not a shared library" + if test -z "$old_library" ; then + $echo + $echo "** And there doesn't seem to be a static archive available" + $echo "** The link will probably fail, sorry" + else + add="$dir/$old_library" + fi + fi + esac + elif test "$hardcode_minus_L" = no; then + case $host in + *-*-sunos*) add_shlibpath="$dir" ;; + esac + add_dir="-L$dir" + add="-l$name" + elif test "$hardcode_shlibpath_var" = no; then + add_shlibpath="$dir" + add="-l$name" + else + lib_linked=no + fi + ;; + relink) + if test "$hardcode_direct" = yes; then + add="$dir/$linklib" + elif test "$hardcode_minus_L" = yes; then + add_dir="-L$dir" + # Try looking first in the location we're being installed to. + if test -n "$inst_prefix_dir"; then + case "$libdir" in + [\\/]*) + add_dir="$add_dir -L$inst_prefix_dir$libdir" + ;; + esac + fi + add="-l$name" + elif test "$hardcode_shlibpath_var" = yes; then + add_shlibpath="$dir" + add="-l$name" + else + lib_linked=no + fi + ;; + *) lib_linked=no ;; + esac + + if test "$lib_linked" != yes; then + $echo "$modename: configuration error: unsupported hardcode properties" + exit 1 + fi + + if test -n "$add_shlibpath"; then + case :$compile_shlibpath: in + *":$add_shlibpath:"*) ;; + *) compile_shlibpath="$compile_shlibpath$add_shlibpath:" ;; + esac + fi + if test "$linkmode" = prog; then + test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs" + test -n "$add" && compile_deplibs="$add $compile_deplibs" + else + test -n "$add_dir" && deplibs="$add_dir $deplibs" + test -n "$add" && deplibs="$add $deplibs" + if test "$hardcode_direct" != yes && \ + test "$hardcode_minus_L" != yes && \ + test "$hardcode_shlibpath_var" = yes; then + case :$finalize_shlibpath: in + *":$libdir:"*) ;; + *) finalize_shlibpath="$finalize_shlibpath$libdir:" ;; + esac + fi + fi + fi + + if test "$linkmode" = prog || test "$mode" = relink; then + add_shlibpath= + add_dir= + add= + # Finalize command for both is simple: just hardcode it. + if test "$hardcode_direct" = yes; then + add="$libdir/$linklib" + elif test "$hardcode_minus_L" = yes; then + add_dir="-L$libdir" + add="-l$name" + elif test "$hardcode_shlibpath_var" = yes; then + case :$finalize_shlibpath: in + *":$libdir:"*) ;; + *) finalize_shlibpath="$finalize_shlibpath$libdir:" ;; + esac + add="-l$name" + elif test "$hardcode_automatic" = yes; then + if test -n "$inst_prefix_dir" && test -f "$inst_prefix_dir$libdir/$linklib" ; then + add="$inst_prefix_dir$libdir/$linklib" + else + add="$libdir/$linklib" + fi + else + # We cannot seem to hardcode it, guess we'll fake it. + add_dir="-L$libdir" + # Try looking first in the location we're being installed to. + if test -n "$inst_prefix_dir"; then + case "$libdir" in + [\\/]*) + add_dir="$add_dir -L$inst_prefix_dir$libdir" + ;; + esac + fi + add="-l$name" + fi + + if test "$linkmode" = prog; then + test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs" + test -n "$add" && finalize_deplibs="$add $finalize_deplibs" + else + test -n "$add_dir" && deplibs="$add_dir $deplibs" + test -n "$add" && deplibs="$add $deplibs" + fi + fi + elif test "$linkmode" = prog; then + # Here we assume that one of hardcode_direct or hardcode_minus_L + # is not unsupported. This is valid on all known static and + # shared platforms. + if test "$hardcode_direct" != unsupported; then + test -n "$old_library" && linklib="$old_library" + compile_deplibs="$dir/$linklib $compile_deplibs" + finalize_deplibs="$dir/$linklib $finalize_deplibs" + else + compile_deplibs="-l$name -L$dir $compile_deplibs" + finalize_deplibs="-l$name -L$dir $finalize_deplibs" + fi + elif test "$build_libtool_libs" = yes; then + # Not a shared library + if test "$deplibs_check_method" != pass_all; then + # We're trying link a shared library against a static one + # but the system doesn't support it. + + # Just print a warning and add the library to dependency_libs so + # that the program can be linked against the static library. + $echo + $echo "*** Warning: This system can not link to static lib archive $lib." + $echo "*** I have the capability to make that library automatically link in when" + $echo "*** you link to this library. But I can only do this if you have a" + $echo "*** shared version of the library, which you do not appear to have." + if test "$module" = yes; then + $echo "*** But as you try to build a module library, libtool will still create " + $echo "*** a static module, that should work as long as the dlopening application" + $echo "*** is linked with the -dlopen flag to resolve symbols at runtime." + if test -z "$global_symbol_pipe"; then + $echo + $echo "*** However, this would only work if libtool was able to extract symbol" + $echo "*** lists from a program, using \`nm' or equivalent, but libtool could" + $echo "*** not find such a program. So, this module is probably useless." + $echo "*** \`nm' from GNU binutils and a full rebuild may help." + fi + if test "$build_old_libs" = no; then + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + fi + else + convenience="$convenience $dir/$old_library" + old_convenience="$old_convenience $dir/$old_library" + deplibs="$dir/$old_library $deplibs" + link_static=yes + fi + fi # link shared/static library? + + if test "$linkmode" = lib; then + if test -n "$dependency_libs" && + { test "$hardcode_into_libs" != yes || test "$build_old_libs" = yes || + test "$link_static" = yes; }; then + # Extract -R from dependency_libs + temp_deplibs= + for libdir in $dependency_libs; do + case $libdir in + -R*) temp_xrpath=`$echo "X$libdir" | $Xsed -e 's/^-R//'` + case " $xrpath " in + *" $temp_xrpath "*) ;; + *) xrpath="$xrpath $temp_xrpath";; + esac;; + *) temp_deplibs="$temp_deplibs $libdir";; + esac + done + dependency_libs="$temp_deplibs" + fi + + newlib_search_path="$newlib_search_path $absdir" + # Link against this library + test "$link_static" = no && newdependency_libs="$abs_ladir/$laname $newdependency_libs" + # ... and its dependency_libs + tmp_libs= + for deplib in $dependency_libs; do + newdependency_libs="$deplib $newdependency_libs" + if test "X$duplicate_deps" = "Xyes" ; then + case "$tmp_libs " in + *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; + esac + fi + tmp_libs="$tmp_libs $deplib" + done + + if test "$link_all_deplibs" != no; then + # Add the search paths of all dependency libraries + for deplib in $dependency_libs; do + case $deplib in + -L*) path="$deplib" ;; + *.la) + dir=`$echo "X$deplib" | $Xsed -e 's%/[^/]*$%%'` + test "X$dir" = "X$deplib" && dir="." + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) absdir="$dir" ;; + *) + absdir=`cd "$dir" && pwd` + if test -z "$absdir"; then + $echo "$modename: warning: cannot determine absolute directory name of \`$dir'" 1>&2 + absdir="$dir" + fi + ;; + esac + if grep "^installed=no" $deplib > /dev/null; then + path="$absdir/$objdir" + else + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` + if test -z "$libdir"; then + $echo "$modename: \`$deplib' is not a valid libtool archive" 1>&2 + exit 1 + fi + if test "$absdir" != "$libdir"; then + $echo "$modename: warning: \`$deplib' seems to be moved" 1>&2 + fi + path="$absdir" + fi + depdepl= + case $host in + *-*-darwin*) + # we do not want to link against static libs, but need to link against shared + eval deplibrary_names=`${SED} -n -e 's/^library_names=\(.*\)$/\1/p' $deplib` + if test -n "$deplibrary_names" ; then + for tmp in $deplibrary_names ; do + depdepl=$tmp + done + if test -f "$path/$depdepl" ; then + depdepl="$path/$depdepl" + fi + newlib_search_path="$newlib_search_path $path" + path="" + fi + ;; + *) + path="-L$path" + ;; + esac + + ;; + -l*) + case $host in + *-*-darwin*) + # Again, we only want to link against shared libraries + eval tmp_libs=`$echo "X$deplib" | $Xsed -e "s,^\-l,,"` + for tmp in $newlib_search_path ; do + if test -f "$tmp/lib$tmp_libs.dylib" ; then + eval depdepl="$tmp/lib$tmp_libs.dylib" + break + fi + done + path="" + ;; + *) continue ;; + esac + ;; + *) continue ;; + esac + case " $deplibs " in + *" $depdepl "*) ;; + *) deplibs="$deplibs $depdepl" ;; + esac + case " $deplibs " in + *" $path "*) ;; + *) deplibs="$deplibs $path" ;; + esac + done + fi # link_all_deplibs != no + fi # linkmode = lib + done # for deplib in $libs + dependency_libs="$newdependency_libs" + if test "$pass" = dlpreopen; then + # Link the dlpreopened libraries before other libraries + for deplib in $save_deplibs; do + deplibs="$deplib $deplibs" + done + fi + if test "$pass" != dlopen; then + if test "$pass" != conv; then + # Make sure lib_search_path contains only unique directories. + lib_search_path= + for dir in $newlib_search_path; do + case "$lib_search_path " in + *" $dir "*) ;; + *) lib_search_path="$lib_search_path $dir" ;; + esac + done + newlib_search_path= + fi + + if test "$linkmode,$pass" != "prog,link"; then + vars="deplibs" + else + vars="compile_deplibs finalize_deplibs" + fi + for var in $vars dependency_libs; do + # Add libraries to $var in reverse order + eval tmp_libs=\"\$$var\" + new_libs= + for deplib in $tmp_libs; do + # FIXME: Pedantically, this is the right thing to do, so + # that some nasty dependency loop isn't accidentally + # broken: + #new_libs="$deplib $new_libs" + # Pragmatically, this seems to cause very few problems in + # practice: + case $deplib in + -L*) new_libs="$deplib $new_libs" ;; + -R*) ;; + *) + # And here is the reason: when a library appears more + # than once as an explicit dependence of a library, or + # is implicitly linked in more than once by the + # compiler, it is considered special, and multiple + # occurrences thereof are not removed. Compare this + # with having the same library being listed as a + # dependency of multiple other libraries: in this case, + # we know (pedantically, we assume) the library does not + # need to be listed more than once, so we keep only the + # last copy. This is not always right, but it is rare + # enough that we require users that really mean to play + # such unportable linking tricks to link the library + # using -Wl,-lname, so that libtool does not consider it + # for duplicate removal. + case " $specialdeplibs " in + *" $deplib "*) new_libs="$deplib $new_libs" ;; + *) + case " $new_libs " in + *" $deplib "*) ;; + *) new_libs="$deplib $new_libs" ;; + esac + ;; + esac + ;; + esac + done + tmp_libs= + for deplib in $new_libs; do + case $deplib in + -L*) + case " $tmp_libs " in + *" $deplib "*) ;; + *) tmp_libs="$tmp_libs $deplib" ;; + esac + ;; + *) tmp_libs="$tmp_libs $deplib" ;; + esac + done + eval $var=\"$tmp_libs\" + done # for var + fi + # Last step: remove runtime libs from dependency_libs (they stay in deplibs) + tmp_libs= + for i in $dependency_libs ; do + case " $predeps $postdeps $compiler_lib_search_path " in + *" $i "*) + i="" + ;; + esac + if test -n "$i" ; then + tmp_libs="$tmp_libs $i" + fi + done + dependency_libs=$tmp_libs + done # for pass + if test "$linkmode" = prog; then + dlfiles="$newdlfiles" + dlprefiles="$newdlprefiles" + fi + + case $linkmode in + oldlib) + if test -n "$deplibs"; then + $echo "$modename: warning: \`-l' and \`-L' are ignored for archives" 1>&2 + fi + + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + $echo "$modename: warning: \`-dlopen' is ignored for archives" 1>&2 + fi + + if test -n "$rpath"; then + $echo "$modename: warning: \`-rpath' is ignored for archives" 1>&2 + fi + + if test -n "$xrpath"; then + $echo "$modename: warning: \`-R' is ignored for archives" 1>&2 + fi + + if test -n "$vinfo"; then + $echo "$modename: warning: \`-version-info/-version-number' is ignored for archives" 1>&2 + fi + + if test -n "$release"; then + $echo "$modename: warning: \`-release' is ignored for archives" 1>&2 + fi + + if test -n "$export_symbols" || test -n "$export_symbols_regex"; then + $echo "$modename: warning: \`-export-symbols' is ignored for archives" 1>&2 + fi + + # Now set the variables for building old libraries. + build_libtool_libs=no + oldlibs="$output" + objs="$objs$old_deplibs" + ;; + + lib) + # Make sure we only generate libraries of the form `libNAME.la'. + case $outputname in + lib*) + name=`$echo "X$outputname" | $Xsed -e 's/\.la$//' -e 's/^lib//'` + eval shared_ext=\"$shrext\" + eval libname=\"$libname_spec\" + ;; + *) + if test "$module" = no; then + $echo "$modename: libtool library \`$output' must begin with \`lib'" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + if test "$need_lib_prefix" != no; then + # Add the "lib" prefix for modules if required + name=`$echo "X$outputname" | $Xsed -e 's/\.la$//'` + eval shared_ext=\"$shrext\" + eval libname=\"$libname_spec\" + else + libname=`$echo "X$outputname" | $Xsed -e 's/\.la$//'` + fi + ;; + esac + + if test -n "$objs"; then + if test "$deplibs_check_method" != pass_all; then + $echo "$modename: cannot build libtool library \`$output' from non-libtool objects on this host:$objs" 2>&1 + exit 1 + else + $echo + $echo "*** Warning: Linking the shared library $output against the non-libtool" + $echo "*** objects $objs is not portable!" + libobjs="$libobjs $objs" + fi + fi + + if test "$dlself" != no; then + $echo "$modename: warning: \`-dlopen self' is ignored for libtool libraries" 1>&2 + fi + + set dummy $rpath + if test "$#" -gt 2; then + $echo "$modename: warning: ignoring multiple \`-rpath's for a libtool library" 1>&2 + fi + install_libdir="$2" + + oldlibs= + if test -z "$rpath"; then + if test "$build_libtool_libs" = yes; then + # Building a libtool convenience library. + # Some compilers have problems with a `.al' extension so + # convenience libraries should have the same extension an + # archive normally would. + oldlibs="$output_objdir/$libname.$libext $oldlibs" + build_libtool_libs=convenience + build_old_libs=yes + fi + + if test -n "$vinfo"; then + $echo "$modename: warning: \`-version-info/-version-number' is ignored for convenience libraries" 1>&2 + fi + + if test -n "$release"; then + $echo "$modename: warning: \`-release' is ignored for convenience libraries" 1>&2 + fi + else + + # Parse the version information argument. + save_ifs="$IFS"; IFS=':' + set dummy $vinfo 0 0 0 + IFS="$save_ifs" + + if test -n "$8"; then + $echo "$modename: too many parameters to \`-version-info'" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + + # convert absolute version numbers to libtool ages + # this retains compatibility with .la files and attempts + # to make the code below a bit more comprehensible + + case $vinfo_number in + yes) + number_major="$2" + number_minor="$3" + number_revision="$4" + # + # There are really only two kinds -- those that + # use the current revision as the major version + # and those that subtract age and use age as + # a minor version. But, then there is irix + # which has an extra 1 added just for fun + # + case $version_type in + darwin|linux|osf|windows) + current=`expr $number_major + $number_minor` + age="$number_minor" + revision="$number_revision" + ;; + freebsd-aout|freebsd-elf|sunos) + current="$number_major" + revision="$number_minor" + age="0" + ;; + irix|nonstopux) + current=`expr $number_major + $number_minor - 1` + age="$number_minor" + revision="$number_minor" + ;; + esac + ;; + no) + current="$2" + revision="$3" + age="$4" + ;; + esac + + # Check that each of the things are valid numbers. + case $current in + [0-9]*) ;; + *) + $echo "$modename: CURRENT \`$current' is not a nonnegative integer" 1>&2 + $echo "$modename: \`$vinfo' is not valid version information" 1>&2 + exit 1 + ;; + esac + + case $revision in + [0-9]*) ;; + *) + $echo "$modename: REVISION \`$revision' is not a nonnegative integer" 1>&2 + $echo "$modename: \`$vinfo' is not valid version information" 1>&2 + exit 1 + ;; + esac + + case $age in + [0-9]*) ;; + *) + $echo "$modename: AGE \`$age' is not a nonnegative integer" 1>&2 + $echo "$modename: \`$vinfo' is not valid version information" 1>&2 + exit 1 + ;; + esac + + if test "$age" -gt "$current"; then + $echo "$modename: AGE \`$age' is greater than the current interface number \`$current'" 1>&2 + $echo "$modename: \`$vinfo' is not valid version information" 1>&2 + exit 1 + fi + + # Calculate the version variables. + major= + versuffix= + verstring= + case $version_type in + none) ;; + + darwin) + # Like Linux, but with the current version available in + # verstring for coding it into the library header + major=.`expr $current - $age` + versuffix="$major.$age.$revision" + # Darwin ld doesn't like 0 for these options... + minor_current=`expr $current + 1` + verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" + ;; + + freebsd-aout) + major=".$current" + versuffix=".$current.$revision"; + ;; + + freebsd-elf) + major=".$current" + versuffix=".$current"; + ;; + + irix | nonstopux) + major=`expr $current - $age + 1` + + case $version_type in + nonstopux) verstring_prefix=nonstopux ;; + *) verstring_prefix=sgi ;; + esac + verstring="$verstring_prefix$major.$revision" + + # Add in all the interfaces that we are compatible with. + loop=$revision + while test "$loop" -ne 0; do + iface=`expr $revision - $loop` + loop=`expr $loop - 1` + verstring="$verstring_prefix$major.$iface:$verstring" + done + + # Before this point, $major must not contain `.'. + major=.$major + versuffix="$major.$revision" + ;; + + linux) + major=.`expr $current - $age` + versuffix="$major.$age.$revision" + ;; + + osf) + major=.`expr $current - $age` + versuffix=".$current.$age.$revision" + verstring="$current.$age.$revision" + + # Add in all the interfaces that we are compatible with. + loop=$age + while test "$loop" -ne 0; do + iface=`expr $current - $loop` + loop=`expr $loop - 1` + verstring="$verstring:${iface}.0" + done + + # Make executables depend on our current version. + verstring="$verstring:${current}.0" + ;; + + sunos) + major=".$current" + versuffix=".$current.$revision" + ;; + + windows) + # Use '-' rather than '.', since we only want one + # extension on DOS 8.3 filesystems. + major=`expr $current - $age` + versuffix="-$major" + ;; + + *) + $echo "$modename: unknown library version type \`$version_type'" 1>&2 + $echo "Fatal configuration error. See the $PACKAGE docs for more information." 1>&2 + exit 1 + ;; + esac + + # Clear the version info if we defaulted, and they specified a release. + if test -z "$vinfo" && test -n "$release"; then + major= + case $version_type in + darwin) + # we can't check for "0.0" in archive_cmds due to quoting + # problems, so we reset it completely + verstring= + ;; + *) + verstring="0.0" + ;; + esac + if test "$need_version" = no; then + versuffix= + else + versuffix=".0.0" + fi + fi + + # Remove version info from name if versioning should be avoided + if test "$avoid_version" = yes && test "$need_version" = no; then + major= + versuffix= + verstring="" + fi + + # Check to see if the archive will have undefined symbols. + if test "$allow_undefined" = yes; then + if test "$allow_undefined_flag" = unsupported; then + $echo "$modename: warning: undefined symbols not allowed in $host shared libraries" 1>&2 + build_libtool_libs=no + build_old_libs=yes + fi + else + # Don't allow undefined symbols. + allow_undefined_flag="$no_undefined_flag" + fi + fi + + if test "$mode" != relink; then + # Remove our outputs, but don't remove object files since they + # may have been created when compiling PIC objects. + removelist= + tempremovelist=`$echo "$output_objdir/*"` + for p in $tempremovelist; do + case $p in + *.$objext) + ;; + $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/${libname}${release}.*) + removelist="$removelist $p" + ;; + *) ;; + esac + done + if test -n "$removelist"; then + $show "${rm}r $removelist" + $run ${rm}r $removelist + fi + fi + + # Now set the variables for building old libraries. + if test "$build_old_libs" = yes && test "$build_libtool_libs" != convenience ; then + oldlibs="$oldlibs $output_objdir/$libname.$libext" + + # Transform .lo files to .o files. + oldobjs="$objs "`$echo "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}'$/d' -e "$lo2o" | $NL2SP` + fi + + # Eliminate all temporary directories. + for path in $notinst_path; do + lib_search_path=`$echo "$lib_search_path " | ${SED} -e 's% $path % %g'` + deplibs=`$echo "$deplibs " | ${SED} -e 's% -L$path % %g'` + dependency_libs=`$echo "$dependency_libs " | ${SED} -e 's% -L$path % %g'` + done + + if test -n "$xrpath"; then + # If the user specified any rpath flags, then add them. + temp_xrpath= + for libdir in $xrpath; do + temp_xrpath="$temp_xrpath -R$libdir" + case "$finalize_rpath " in + *" $libdir "*) ;; + *) finalize_rpath="$finalize_rpath $libdir" ;; + esac + done + if test "$hardcode_into_libs" != yes || test "$build_old_libs" = yes; then + dependency_libs="$temp_xrpath $dependency_libs" + fi + fi + + # Make sure dlfiles contains only unique files that won't be dlpreopened + old_dlfiles="$dlfiles" + dlfiles= + for lib in $old_dlfiles; do + case " $dlprefiles $dlfiles " in + *" $lib "*) ;; + *) dlfiles="$dlfiles $lib" ;; + esac + done + + # Make sure dlprefiles contains only unique files + old_dlprefiles="$dlprefiles" + dlprefiles= + for lib in $old_dlprefiles; do + case "$dlprefiles " in + *" $lib "*) ;; + *) dlprefiles="$dlprefiles $lib" ;; + esac + done + + if test "$build_libtool_libs" = yes; then + if test -n "$rpath"; then + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos*) + # these systems don't actually have a c library (as such)! + ;; + *-*-rhapsody* | *-*-darwin1.[012]) + # Rhapsody C library is in the System framework + deplibs="$deplibs -framework System" + ;; + *-*-netbsd*) + # Don't link with libc until the a.out ld.so is fixed. + ;; + *-*-openbsd* | *-*-freebsd*) + # Do not include libc due to us having libc/libc_r. + test "X$arg" = "X-lc" && continue + ;; + *) + # Add libc to deplibs on all other systems if necessary. + if test "$build_libtool_need_lc" = "yes"; then + deplibs="$deplibs -lc" + fi + ;; + esac + fi + + # Transform deplibs into only deplibs that can be linked in shared. + name_save=$name + libname_save=$libname + release_save=$release + versuffix_save=$versuffix + major_save=$major + # I'm not sure if I'm treating the release correctly. I think + # release should show up in the -l (ie -lgmp5) so we don't want to + # add it in twice. Is that correct? + release="" + versuffix="" + major="" + newdeplibs= + droppeddeps=no + case $deplibs_check_method in + pass_all) + # Don't check for shared/static. Everything works. + # This might be a little naive. We might want to check + # whether the library exists or not. But this is on + # osf3 & osf4 and I'm not really sure... Just + # implementing what was already the behavior. + newdeplibs=$deplibs + ;; + test_compile) + # This code stresses the "libraries are programs" paradigm to its + # limits. Maybe even breaks it. We compile a program, linking it + # against the deplibs as a proxy for the library. Then we can check + # whether they linked in statically or dynamically with ldd. + $rm conftest.c + cat > conftest.c </dev/null` + for potent_lib in $potential_libs; do + # Follow soft links. + if ls -lLd "$potent_lib" 2>/dev/null \ + | grep " -> " >/dev/null; then + continue + fi + # The statement above tries to avoid entering an + # endless loop below, in case of cyclic links. + # We might still enter an endless loop, since a link + # loop can be closed while we follow links, + # but so what? + potlib="$potent_lib" + while test -h "$potlib" 2>/dev/null; do + potliblink=`ls -ld $potlib | ${SED} 's/.* -> //'` + case $potliblink in + [\\/]* | [A-Za-z]:[\\/]*) potlib="$potliblink";; + *) potlib=`$echo "X$potlib" | $Xsed -e 's,[^/]*$,,'`"$potliblink";; + esac + done + if eval $file_magic_cmd \"\$potlib\" 2>/dev/null \ + | ${SED} 10q \ + | $EGREP "$file_magic_regex" > /dev/null; then + newdeplibs="$newdeplibs $a_deplib" + a_deplib="" + break 2 + fi + done + done + fi + if test -n "$a_deplib" ; then + droppeddeps=yes + $echo + $echo "*** Warning: linker path does not have real file for library $a_deplib." + $echo "*** I have the capability to make that library automatically link in when" + $echo "*** you link to this library. But I can only do this if you have a" + $echo "*** shared version of the library, which you do not appear to have" + $echo "*** because I did check the linker path looking for a file starting" + if test -z "$potlib" ; then + $echo "*** with $libname but no candidates were found. (...for file magic test)" + else + $echo "*** with $libname and none of the candidates passed a file format test" + $echo "*** using a file magic. Last file checked: $potlib" + fi + fi + else + # Add a -L argument. + newdeplibs="$newdeplibs $a_deplib" + fi + done # Gone through all deplibs. + ;; + match_pattern*) + set dummy $deplibs_check_method + match_pattern_regex=`expr "$deplibs_check_method" : "$2 \(.*\)"` + for a_deplib in $deplibs; do + name="`expr $a_deplib : '-l\(.*\)'`" + # If $name is empty we are operating on a -L argument. + if test -n "$name" && test "$name" != "0"; then + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + case " $predeps $postdeps " in + *" $a_deplib "*) + newdeplibs="$newdeplibs $a_deplib" + a_deplib="" + ;; + esac + fi + if test -n "$a_deplib" ; then + libname=`eval \\$echo \"$libname_spec\"` + for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do + potential_libs=`ls $i/$libname[.-]* 2>/dev/null` + for potent_lib in $potential_libs; do + potlib="$potent_lib" # see symlink-check above in file_magic test + if eval $echo \"$potent_lib\" 2>/dev/null \ + | ${SED} 10q \ + | $EGREP "$match_pattern_regex" > /dev/null; then + newdeplibs="$newdeplibs $a_deplib" + a_deplib="" + break 2 + fi + done + done + fi + if test -n "$a_deplib" ; then + droppeddeps=yes + $echo + $echo "*** Warning: linker path does not have real file for library $a_deplib." + $echo "*** I have the capability to make that library automatically link in when" + $echo "*** you link to this library. But I can only do this if you have a" + $echo "*** shared version of the library, which you do not appear to have" + $echo "*** because I did check the linker path looking for a file starting" + if test -z "$potlib" ; then + $echo "*** with $libname but no candidates were found. (...for regex pattern test)" + else + $echo "*** with $libname and none of the candidates passed a file format test" + $echo "*** using a regex pattern. Last file checked: $potlib" + fi + fi + else + # Add a -L argument. + newdeplibs="$newdeplibs $a_deplib" + fi + done # Gone through all deplibs. + ;; + none | unknown | *) + newdeplibs="" + tmp_deplibs=`$echo "X $deplibs" | $Xsed -e 's/ -lc$//' \ + -e 's/ -[LR][^ ]*//g'` + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + for i in $predeps $postdeps ; do + # can't use Xsed below, because $i might contain '/' + tmp_deplibs=`$echo "X $tmp_deplibs" | ${SED} -e "1s,^X,," -e "s,$i,,"` + done + fi + if $echo "X $tmp_deplibs" | $Xsed -e 's/[ ]//g' \ + | grep . >/dev/null; then + $echo + if test "X$deplibs_check_method" = "Xnone"; then + $echo "*** Warning: inter-library dependencies are not supported in this platform." + else + $echo "*** Warning: inter-library dependencies are not known to be supported." + fi + $echo "*** All declared inter-library dependencies are being dropped." + droppeddeps=yes + fi + ;; + esac + versuffix=$versuffix_save + major=$major_save + release=$release_save + libname=$libname_save + name=$name_save + + case $host in + *-*-rhapsody* | *-*-darwin1.[012]) + # On Rhapsody replace the C library is the System framework + newdeplibs=`$echo "X $newdeplibs" | $Xsed -e 's/ -lc / -framework System /'` + ;; + esac + + if test "$droppeddeps" = yes; then + if test "$module" = yes; then + $echo + $echo "*** Warning: libtool could not satisfy all declared inter-library" + $echo "*** dependencies of module $libname. Therefore, libtool will create" + $echo "*** a static module, that should work as long as the dlopening" + $echo "*** application is linked with the -dlopen flag." + if test -z "$global_symbol_pipe"; then + $echo + $echo "*** However, this would only work if libtool was able to extract symbol" + $echo "*** lists from a program, using \`nm' or equivalent, but libtool could" + $echo "*** not find such a program. So, this module is probably useless." + $echo "*** \`nm' from GNU binutils and a full rebuild may help." + fi + if test "$build_old_libs" = no; then + oldlibs="$output_objdir/$libname.$libext" + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + else + $echo "*** The inter-library dependencies that have been dropped here will be" + $echo "*** automatically added whenever a program is linked with this library" + $echo "*** or is declared to -dlopen it." + + if test "$allow_undefined" = no; then + $echo + $echo "*** Since this library must not contain undefined symbols," + $echo "*** because either the platform does not support them or" + $echo "*** it was explicitly requested with -no-undefined," + $echo "*** libtool will only create a static version of it." + if test "$build_old_libs" = no; then + oldlibs="$output_objdir/$libname.$libext" + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + fi + fi + fi + # Done checking deplibs! + deplibs=$newdeplibs + fi + + # All the library-specific variables (install_libdir is set above). + library_names= + old_library= + dlname= + + # Test again, we may have decided not to build it any more + if test "$build_libtool_libs" = yes; then + if test "$hardcode_into_libs" = yes; then + # Hardcode the library paths + hardcode_libdirs= + dep_rpath= + rpath="$finalize_rpath" + test "$mode" != relink && rpath="$compile_rpath$rpath" + for libdir in $rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + if test -z "$hardcode_libdirs"; then + hardcode_libdirs="$libdir" + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + dep_rpath="$dep_rpath $flag" + fi + elif test -n "$runpath_var"; then + case "$perm_rpath " in + *" $libdir "*) ;; + *) perm_rpath="$perm_rpath $libdir" ;; + esac + fi + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir="$hardcode_libdirs" + if test -n "$hardcode_libdir_flag_spec_ld"; then + eval dep_rpath=\"$hardcode_libdir_flag_spec_ld\" + else + eval dep_rpath=\"$hardcode_libdir_flag_spec\" + fi + fi + if test -n "$runpath_var" && test -n "$perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $perm_rpath; do + rpath="$rpath$dir:" + done + eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var" + fi + test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs" + fi + + shlibpath="$finalize_shlibpath" + test "$mode" != relink && shlibpath="$compile_shlibpath$shlibpath" + if test -n "$shlibpath"; then + eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var" + fi + + # Get the real and link names of the library. + eval shared_ext=\"$shrext\" + eval library_names=\"$library_names_spec\" + set dummy $library_names + realname="$2" + shift; shift + + if test -n "$soname_spec"; then + eval soname=\"$soname_spec\" + else + soname="$realname" + fi + if test -z "$dlname"; then + dlname=$soname + fi + + lib="$output_objdir/$realname" + for link + do + linknames="$linknames $link" + done + + # Use standard objects if they are pic + test -z "$pic_flag" && libobjs=`$echo "X$libobjs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` + + # Prepare the list of exported symbols + if test -z "$export_symbols"; then + if test "$always_export_symbols" = yes || test -n "$export_symbols_regex"; then + $show "generating symbol list for \`$libname.la'" + export_symbols="$output_objdir/$libname.exp" + $run $rm $export_symbols + eval cmds=\"$export_symbols_cmds\" + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + if len=`expr "X$cmd" : ".*"` && + test "$len" -le "$max_cmd_len" || test "$max_cmd_len" -le -1; then + $show "$cmd" + $run eval "$cmd" || exit $? + skipped_export=false + else + # The command line is too long to execute in one step. + $show "using reloadable object file for export list..." + skipped_export=: + fi + done + IFS="$save_ifs" + if test -n "$export_symbols_regex"; then + $show "$EGREP -e \"$export_symbols_regex\" \"$export_symbols\" > \"${export_symbols}T\"" + $run eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' + $show "$mv \"${export_symbols}T\" \"$export_symbols\"" + $run eval '$mv "${export_symbols}T" "$export_symbols"' + fi + fi + fi + + if test -n "$export_symbols" && test -n "$include_expsyms"; then + $run eval '$echo "X$include_expsyms" | $SP2NL >> "$export_symbols"' + fi + + tmp_deplibs= + for test_deplib in $deplibs; do + case " $convenience " in + *" $test_deplib "*) ;; + *) + tmp_deplibs="$tmp_deplibs $test_deplib" + ;; + esac + done + deplibs="$tmp_deplibs" + + if test -n "$convenience"; then + if test -n "$whole_archive_flag_spec"; then + save_libobjs=$libobjs + eval libobjs=\"\$libobjs $whole_archive_flag_spec\" + else + gentop="$output_objdir/${outputname}x" + $show "${rm}r $gentop" + $run ${rm}r "$gentop" + $show "$mkdir $gentop" + $run $mkdir "$gentop" + status=$? + if test "$status" -ne 0 && test ! -d "$gentop"; then + exit $status + fi + generated="$generated $gentop" + + for xlib in $convenience; do + # Extract the objects. + case $xlib in + [\\/]* | [A-Za-z]:[\\/]*) xabs="$xlib" ;; + *) xabs=`pwd`"/$xlib" ;; + esac + xlib=`$echo "X$xlib" | $Xsed -e 's%^.*/%%'` + xdir="$gentop/$xlib" + + $show "${rm}r $xdir" + $run ${rm}r "$xdir" + $show "$mkdir $xdir" + $run $mkdir "$xdir" + status=$? + if test "$status" -ne 0 && test ! -d "$xdir"; then + exit $status + fi + # We will extract separately just the conflicting names and we will no + # longer touch any unique names. It is faster to leave these extract + # automatically by $AR in one run. + $show "(cd $xdir && $AR x $xabs)" + $run eval "(cd \$xdir && $AR x \$xabs)" || exit $? + if ($AR t "$xabs" | sort | sort -uc >/dev/null 2>&1); then + : + else + $echo "$modename: warning: object name conflicts; renaming object files" 1>&2 + $echo "$modename: warning: to ensure that they will not overwrite" 1>&2 + $AR t "$xabs" | sort | uniq -cd | while read -r count name + do + i=1 + while test "$i" -le "$count" + do + # Put our $i before any first dot (extension) + # Never overwrite any file + name_to="$name" + while test "X$name_to" = "X$name" || test -f "$xdir/$name_to" + do + name_to=`$echo "X$name_to" | $Xsed -e "s/\([^.]*\)/\1-$i/"` + done + $show "(cd $xdir && $AR xN $i $xabs '$name' && $mv '$name' '$name_to')" + $run eval "(cd \$xdir && $AR xN $i \$xabs '$name' && $mv '$name' '$name_to')" || exit $? + i=`expr $i + 1` + done + done + fi + + libobjs="$libobjs "`find $xdir -name \*.$objext -print -o -name \*.lo -print | $NL2SP` + done + fi + fi + + if test "$thread_safe" = yes && test -n "$thread_safe_flag_spec"; then + eval flag=\"$thread_safe_flag_spec\" + linker_flags="$linker_flags $flag" + fi + + # Make a backup of the uninstalled library when relinking + if test "$mode" = relink; then + $run eval '(cd $output_objdir && $rm ${realname}U && $mv $realname ${realname}U)' || exit $? + fi + + # Do each of the archive commands. + if test "$module" = yes && test -n "$module_cmds" ; then + if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then + eval cmds=\"$module_expsym_cmds\" + else + eval cmds=\"$module_cmds\" + fi + else + if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then + eval cmds=\"$archive_expsym_cmds\" + else + eval cmds=\"$archive_cmds\" + fi + fi + + if test "X$skipped_export" != "X:" && len=`expr "X$cmds" : ".*"` && + test "$len" -le "$max_cmd_len" || test "$max_cmd_len" -le -1; then + : + else + # The command line is too long to link in one step, link piecewise. + $echo "creating reloadable object files..." + + # Save the value of $output and $libobjs because we want to + # use them later. If we have whole_archive_flag_spec, we + # want to use save_libobjs as it was before + # whole_archive_flag_spec was expanded, because we can't + # assume the linker understands whole_archive_flag_spec. + # This may have to be revisited, in case too many + # convenience libraries get linked in and end up exceeding + # the spec. + if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then + save_libobjs=$libobjs + fi + save_output=$output + + # Clear the reloadable object creation command queue and + # initialize k to one. + test_cmds= + concat_cmds= + objlist= + delfiles= + last_robj= + k=1 + output=$output_objdir/$save_output-${k}.$objext + # Loop over the list of objects to be linked. + for obj in $save_libobjs + do + eval test_cmds=\"$reload_cmds $objlist $last_robj\" + if test "X$objlist" = X || + { len=`expr "X$test_cmds" : ".*"` && + test "$len" -le "$max_cmd_len"; }; then + objlist="$objlist $obj" + else + # The command $test_cmds is almost too long, add a + # command to the queue. + if test "$k" -eq 1 ; then + # The first file doesn't have a previous command to add. + eval concat_cmds=\"$reload_cmds $objlist $last_robj\" + else + # All subsequent reloadable object files will link in + # the last one created. + eval concat_cmds=\"\$concat_cmds~$reload_cmds $objlist $last_robj\" + fi + last_robj=$output_objdir/$save_output-${k}.$objext + k=`expr $k + 1` + output=$output_objdir/$save_output-${k}.$objext + objlist=$obj + len=1 + fi + done + # Handle the remaining objects by creating one last + # reloadable object file. All subsequent reloadable object + # files will link in the last one created. + test -z "$concat_cmds" || concat_cmds=$concat_cmds~ + eval concat_cmds=\"\${concat_cmds}$reload_cmds $objlist $last_robj\" + + if ${skipped_export-false}; then + $show "generating symbol list for \`$libname.la'" + export_symbols="$output_objdir/$libname.exp" + $run $rm $export_symbols + libobjs=$output + # Append the command to create the export file. + eval concat_cmds=\"\$concat_cmds~$export_symbols_cmds\" + fi + + # Set up a command to remove the reloadale object files + # after they are used. + i=0 + while test "$i" -lt "$k" + do + i=`expr $i + 1` + delfiles="$delfiles $output_objdir/$save_output-${i}.$objext" + done + + $echo "creating a temporary reloadable object file: $output" + + # Loop through the commands generated above and execute them. + save_ifs="$IFS"; IFS='~' + for cmd in $concat_cmds; do + IFS="$save_ifs" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + + libobjs=$output + # Restore the value of output. + output=$save_output + + if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then + eval libobjs=\"\$libobjs $whole_archive_flag_spec\" + fi + # Expand the library linking commands again to reset the + # value of $libobjs for piecewise linking. + + # Do each of the archive commands. + if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then + eval cmds=\"$archive_expsym_cmds\" + else + eval cmds=\"$archive_cmds\" + fi + + # Append the command to remove the reloadable object files + # to the just-reset $cmds. + eval cmds=\"\$cmds~$rm $delfiles\" + fi + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + + # Restore the uninstalled library and exit + if test "$mode" = relink; then + $run eval '(cd $output_objdir && $rm ${realname}T && $mv $realname ${realname}T && $mv "$realname"U $realname)' || exit $? + exit 0 + fi + + # Create links to the real library. + for linkname in $linknames; do + if test "$realname" != "$linkname"; then + $show "(cd $output_objdir && $rm $linkname && $LN_S $realname $linkname)" + $run eval '(cd $output_objdir && $rm $linkname && $LN_S $realname $linkname)' || exit $? + fi + done + + # If -module or -export-dynamic was specified, set the dlname. + if test "$module" = yes || test "$export_dynamic" = yes; then + # On all known operating systems, these are identical. + dlname="$soname" + fi + fi + ;; + + obj) + if test -n "$deplibs"; then + $echo "$modename: warning: \`-l' and \`-L' are ignored for objects" 1>&2 + fi + + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + $echo "$modename: warning: \`-dlopen' is ignored for objects" 1>&2 + fi + + if test -n "$rpath"; then + $echo "$modename: warning: \`-rpath' is ignored for objects" 1>&2 + fi + + if test -n "$xrpath"; then + $echo "$modename: warning: \`-R' is ignored for objects" 1>&2 + fi + + if test -n "$vinfo"; then + $echo "$modename: warning: \`-version-info' is ignored for objects" 1>&2 + fi + + if test -n "$release"; then + $echo "$modename: warning: \`-release' is ignored for objects" 1>&2 + fi + + case $output in + *.lo) + if test -n "$objs$old_deplibs"; then + $echo "$modename: cannot build library object \`$output' from non-libtool objects" 1>&2 + exit 1 + fi + libobj="$output" + obj=`$echo "X$output" | $Xsed -e "$lo2o"` + ;; + *) + libobj= + obj="$output" + ;; + esac + + # Delete the old objects. + $run $rm $obj $libobj + + # Objects from convenience libraries. This assumes + # single-version convenience libraries. Whenever we create + # different ones for PIC/non-PIC, this we'll have to duplicate + # the extraction. + reload_conv_objs= + gentop= + # reload_cmds runs $LD directly, so let us get rid of + # -Wl from whole_archive_flag_spec + wl= + + if test -n "$convenience"; then + if test -n "$whole_archive_flag_spec"; then + eval reload_conv_objs=\"\$reload_objs $whole_archive_flag_spec\" + else + gentop="$output_objdir/${obj}x" + $show "${rm}r $gentop" + $run ${rm}r "$gentop" + $show "$mkdir $gentop" + $run $mkdir "$gentop" + status=$? + if test "$status" -ne 0 && test ! -d "$gentop"; then + exit $status + fi + generated="$generated $gentop" + + for xlib in $convenience; do + # Extract the objects. + case $xlib in + [\\/]* | [A-Za-z]:[\\/]*) xabs="$xlib" ;; + *) xabs=`pwd`"/$xlib" ;; + esac + xlib=`$echo "X$xlib" | $Xsed -e 's%^.*/%%'` + xdir="$gentop/$xlib" + + $show "${rm}r $xdir" + $run ${rm}r "$xdir" + $show "$mkdir $xdir" + $run $mkdir "$xdir" + status=$? + if test "$status" -ne 0 && test ! -d "$xdir"; then + exit $status + fi + # We will extract separately just the conflicting names and we will no + # longer touch any unique names. It is faster to leave these extract + # automatically by $AR in one run. + $show "(cd $xdir && $AR x $xabs)" + $run eval "(cd \$xdir && $AR x \$xabs)" || exit $? + if ($AR t "$xabs" | sort | sort -uc >/dev/null 2>&1); then + : + else + $echo "$modename: warning: object name conflicts; renaming object files" 1>&2 + $echo "$modename: warning: to ensure that they will not overwrite" 1>&2 + $AR t "$xabs" | sort | uniq -cd | while read -r count name + do + i=1 + while test "$i" -le "$count" + do + # Put our $i before any first dot (extension) + # Never overwrite any file + name_to="$name" + while test "X$name_to" = "X$name" || test -f "$xdir/$name_to" + do + name_to=`$echo "X$name_to" | $Xsed -e "s/\([^.]*\)/\1-$i/"` + done + $show "(cd $xdir && $AR xN $i $xabs '$name' && $mv '$name' '$name_to')" + $run eval "(cd \$xdir && $AR xN $i \$xabs '$name' && $mv '$name' '$name_to')" || exit $? + i=`expr $i + 1` + done + done + fi + + reload_conv_objs="$reload_objs "`find $xdir -name \*.$objext -print -o -name \*.lo -print | $NL2SP` + done + fi + fi + + # Create the old-style object. + reload_objs="$objs$old_deplibs "`$echo "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}$'/d' -e '/\.lib$/d' -e "$lo2o" | $NL2SP`" $reload_conv_objs" ### testsuite: skip nested quoting test + + output="$obj" + eval cmds=\"$reload_cmds\" + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + + # Exit if we aren't doing a library object file. + if test -z "$libobj"; then + if test -n "$gentop"; then + $show "${rm}r $gentop" + $run ${rm}r $gentop + fi + + exit 0 + fi + + if test "$build_libtool_libs" != yes; then + if test -n "$gentop"; then + $show "${rm}r $gentop" + $run ${rm}r $gentop + fi + + # Create an invalid libtool object if no PIC, so that we don't + # accidentally link it into a program. + # $show "echo timestamp > $libobj" + # $run eval "echo timestamp > $libobj" || exit $? + exit 0 + fi + + if test -n "$pic_flag" || test "$pic_mode" != default; then + # Only do commands if we really have different PIC objects. + reload_objs="$libobjs $reload_conv_objs" + output="$libobj" + eval cmds=\"$reload_cmds\" + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + fi + + if test -n "$gentop"; then + $show "${rm}r $gentop" + $run ${rm}r $gentop + fi + + exit 0 + ;; + + prog) + case $host in + *cygwin*) output=`$echo $output | ${SED} -e 's,.exe$,,;s,$,.exe,'` ;; + esac + if test -n "$vinfo"; then + $echo "$modename: warning: \`-version-info' is ignored for programs" 1>&2 + fi + + if test -n "$release"; then + $echo "$modename: warning: \`-release' is ignored for programs" 1>&2 + fi + + if test "$preload" = yes; then + if test "$dlopen_support" = unknown && test "$dlopen_self" = unknown && + test "$dlopen_self_static" = unknown; then + $echo "$modename: warning: \`AC_LIBTOOL_DLOPEN' not used. Assuming no dlopen support." + fi + fi + + case $host in + *-*-rhapsody* | *-*-darwin1.[012]) + # On Rhapsody replace the C library is the System framework + compile_deplibs=`$echo "X $compile_deplibs" | $Xsed -e 's/ -lc / -framework System /'` + finalize_deplibs=`$echo "X $finalize_deplibs" | $Xsed -e 's/ -lc / -framework System /'` + ;; + esac + + case $host in + *darwin*) + # Don't allow lazy linking, it breaks C++ global constructors + if test "$tagname" = CXX ; then + compile_command="$compile_command ${wl}-bind_at_load" + finalize_command="$finalize_command ${wl}-bind_at_load" + fi + ;; + esac + + compile_command="$compile_command $compile_deplibs" + finalize_command="$finalize_command $finalize_deplibs" + + if test -n "$rpath$xrpath"; then + # If the user specified any rpath flags, then add them. + for libdir in $rpath $xrpath; do + # This is the magic to use -rpath. + case "$finalize_rpath " in + *" $libdir "*) ;; + *) finalize_rpath="$finalize_rpath $libdir" ;; + esac + done + fi + + # Now hardcode the library paths + rpath= + hardcode_libdirs= + for libdir in $compile_rpath $finalize_rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + if test -z "$hardcode_libdirs"; then + hardcode_libdirs="$libdir" + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + rpath="$rpath $flag" + fi + elif test -n "$runpath_var"; then + case "$perm_rpath " in + *" $libdir "*) ;; + *) perm_rpath="$perm_rpath $libdir" ;; + esac + fi + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2*) + case :$dllsearchpath: in + *":$libdir:"*) ;; + *) dllsearchpath="$dllsearchpath:$libdir";; + esac + ;; + esac + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir="$hardcode_libdirs" + eval rpath=\" $hardcode_libdir_flag_spec\" + fi + compile_rpath="$rpath" + + rpath= + hardcode_libdirs= + for libdir in $finalize_rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + if test -z "$hardcode_libdirs"; then + hardcode_libdirs="$libdir" + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + rpath="$rpath $flag" + fi + elif test -n "$runpath_var"; then + case "$finalize_perm_rpath " in + *" $libdir "*) ;; + *) finalize_perm_rpath="$finalize_perm_rpath $libdir" ;; + esac + fi + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir="$hardcode_libdirs" + eval rpath=\" $hardcode_libdir_flag_spec\" + fi + finalize_rpath="$rpath" + + if test -n "$libobjs" && test "$build_old_libs" = yes; then + # Transform all the library objects into standard objects. + compile_command=`$echo "X$compile_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` + finalize_command=`$echo "X$finalize_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` + fi + + dlsyms= + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + if test -n "$NM" && test -n "$global_symbol_pipe"; then + dlsyms="${outputname}S.c" + else + $echo "$modename: not configured to extract global symbols from dlpreopened files" 1>&2 + fi + fi + + if test -n "$dlsyms"; then + case $dlsyms in + "") ;; + *.c) + # Discover the nlist of each of the dlfiles. + nlist="$output_objdir/${outputname}.nm" + + $show "$rm $nlist ${nlist}S ${nlist}T" + $run $rm "$nlist" "${nlist}S" "${nlist}T" + + # Parse the name list into a source file. + $show "creating $output_objdir/$dlsyms" + + test -z "$run" && $echo > "$output_objdir/$dlsyms" "\ +/* $dlsyms - symbol resolution table for \`$outputname' dlsym emulation. */ +/* Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP */ + +#ifdef __cplusplus +extern \"C\" { +#endif + +/* Prevent the only kind of declaration conflicts we can make. */ +#define lt_preloaded_symbols some_other_symbol + +/* External symbol declarations for the compiler. */\ +" + + if test "$dlself" = yes; then + $show "generating symbol list for \`$output'" + + test -z "$run" && $echo ': @PROGRAM@ ' > "$nlist" + + # Add our own program objects to the symbol list. + progfiles=`$echo "X$objs$old_deplibs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` + for arg in $progfiles; do + $show "extracting global C symbols from \`$arg'" + $run eval "$NM $arg | $global_symbol_pipe >> '$nlist'" + done + + if test -n "$exclude_expsyms"; then + $run eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T' + $run eval '$mv "$nlist"T "$nlist"' + fi + + if test -n "$export_symbols_regex"; then + $run eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T' + $run eval '$mv "$nlist"T "$nlist"' + fi + + # Prepare the list of exported symbols + if test -z "$export_symbols"; then + export_symbols="$output_objdir/$output.exp" + $run $rm $export_symbols + $run eval "${SED} -n -e '/^: @PROGRAM@$/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"' + else + $run eval "${SED} -e 's/\([][.*^$]\)/\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$output.exp"' + $run eval 'grep -f "$output_objdir/$output.exp" < "$nlist" > "$nlist"T' + $run eval 'mv "$nlist"T "$nlist"' + fi + fi + + for arg in $dlprefiles; do + $show "extracting global C symbols from \`$arg'" + name=`$echo "$arg" | ${SED} -e 's%^.*/%%'` + $run eval '$echo ": $name " >> "$nlist"' + $run eval "$NM $arg | $global_symbol_pipe >> '$nlist'" + done + + if test -z "$run"; then + # Make sure we have at least an empty file. + test -f "$nlist" || : > "$nlist" + + if test -n "$exclude_expsyms"; then + $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T + $mv "$nlist"T "$nlist" + fi + + # Try sorting and uniquifying the output. + if grep -v "^: " < "$nlist" | + if sort -k 3 /dev/null 2>&1; then + sort -k 3 + else + sort +2 + fi | + uniq > "$nlist"S; then + : + else + grep -v "^: " < "$nlist" > "$nlist"S + fi + + if test -f "$nlist"S; then + eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$dlsyms"' + else + $echo '/* NONE */' >> "$output_objdir/$dlsyms" + fi + + $echo >> "$output_objdir/$dlsyms" "\ + +#undef lt_preloaded_symbols + +#if defined (__STDC__) && __STDC__ +# define lt_ptr void * +#else +# define lt_ptr char * +# define const +#endif + +/* The mapping between symbol names and symbols. */ +const struct { + const char *name; + lt_ptr address; +} +lt_preloaded_symbols[] = +{\ +" + + eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$dlsyms" + + $echo >> "$output_objdir/$dlsyms" "\ + {0, (lt_ptr) 0} +}; + +/* This works around a problem in FreeBSD linker */ +#ifdef FREEBSD_WORKAROUND +static const void *lt_preloaded_setup() { + return lt_preloaded_symbols; +} +#endif + +#ifdef __cplusplus +} +#endif\ +" + fi + + pic_flag_for_symtable= + case $host in + # compiling the symbol table file with pic_flag works around + # a FreeBSD bug that causes programs to crash when -lm is + # linked before any other PIC object. But we must not use + # pic_flag when linking with -static. The problem exists in + # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1. + *-*-freebsd2*|*-*-freebsd3.0*|*-*-freebsdelf3.0*) + case "$compile_command " in + *" -static "*) ;; + *) pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND";; + esac;; + *-*-hpux*) + case "$compile_command " in + *" -static "*) ;; + *) pic_flag_for_symtable=" $pic_flag";; + esac + esac + + # Now compile the dynamic symbol file. + $show "(cd $output_objdir && $LTCC -c$no_builtin_flag$pic_flag_for_symtable \"$dlsyms\")" + $run eval '(cd $output_objdir && $LTCC -c$no_builtin_flag$pic_flag_for_symtable "$dlsyms")' || exit $? + + # Clean up the generated files. + $show "$rm $output_objdir/$dlsyms $nlist ${nlist}S ${nlist}T" + $run $rm "$output_objdir/$dlsyms" "$nlist" "${nlist}S" "${nlist}T" + + # Transform the symbol file into the correct name. + compile_command=`$echo "X$compile_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}S.${objext}%"` + finalize_command=`$echo "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}S.${objext}%"` + ;; + *) + $echo "$modename: unknown suffix for \`$dlsyms'" 1>&2 + exit 1 + ;; + esac + else + # We keep going just in case the user didn't refer to + # lt_preloaded_symbols. The linker will fail if global_symbol_pipe + # really was required. + + # Nullify the symbol file. + compile_command=`$echo "X$compile_command" | $Xsed -e "s% @SYMFILE@%%"` + finalize_command=`$echo "X$finalize_command" | $Xsed -e "s% @SYMFILE@%%"` + fi + + if test "$need_relink" = no || test "$build_libtool_libs" != yes; then + # Replace the output file specification. + compile_command=`$echo "X$compile_command" | $Xsed -e 's%@OUTPUT@%'"$output"'%g'` + link_command="$compile_command$compile_rpath" + + # We have no uninstalled library dependencies, so finalize right now. + $show "$link_command" + $run eval "$link_command" + status=$? + + # Delete the generated files. + if test -n "$dlsyms"; then + $show "$rm $output_objdir/${outputname}S.${objext}" + $run $rm "$output_objdir/${outputname}S.${objext}" + fi + + exit $status + fi + + if test -n "$shlibpath_var"; then + # We should set the shlibpath_var + rpath= + for dir in $temp_rpath; do + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) + # Absolute path. + rpath="$rpath$dir:" + ;; + *) + # Relative path: add a thisdir entry. + rpath="$rpath\$thisdir/$dir:" + ;; + esac + done + temp_rpath="$rpath" + fi + + if test -n "$compile_shlibpath$finalize_shlibpath"; then + compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command" + fi + if test -n "$finalize_shlibpath"; then + finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command" + fi + + compile_var= + finalize_var= + if test -n "$runpath_var"; then + if test -n "$perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $perm_rpath; do + rpath="$rpath$dir:" + done + compile_var="$runpath_var=\"$rpath\$$runpath_var\" " + fi + if test -n "$finalize_perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $finalize_perm_rpath; do + rpath="$rpath$dir:" + done + finalize_var="$runpath_var=\"$rpath\$$runpath_var\" " + fi + fi + + if test "$no_install" = yes; then + # We don't need to create a wrapper script. + link_command="$compile_var$compile_command$compile_rpath" + # Replace the output file specification. + link_command=`$echo "X$link_command" | $Xsed -e 's%@OUTPUT@%'"$output"'%g'` + # Delete the old output file. + $run $rm $output + # Link the executable and exit + $show "$link_command" + $run eval "$link_command" || exit $? + exit 0 + fi + + if test "$hardcode_action" = relink; then + # Fast installation is not supported + link_command="$compile_var$compile_command$compile_rpath" + relink_command="$finalize_var$finalize_command$finalize_rpath" + + $echo "$modename: warning: this platform does not like uninstalled shared libraries" 1>&2 + $echo "$modename: \`$output' will be relinked during installation" 1>&2 + else + if test "$fast_install" != no; then + link_command="$finalize_var$compile_command$finalize_rpath" + if test "$fast_install" = yes; then + relink_command=`$echo "X$compile_var$compile_command$compile_rpath" | $Xsed -e 's%@OUTPUT@%\$progdir/\$file%g'` + else + # fast_install is set to needless + relink_command= + fi + else + link_command="$compile_var$compile_command$compile_rpath" + relink_command="$finalize_var$finalize_command$finalize_rpath" + fi + fi + + # Replace the output file specification. + link_command=`$echo "X$link_command" | $Xsed -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'` + + # Delete the old output files. + $run $rm $output $output_objdir/$outputname $output_objdir/lt-$outputname + + $show "$link_command" + $run eval "$link_command" || exit $? + + # Now create the wrapper script. + $show "creating $output" + + # Quote the relink command for shipping. + if test -n "$relink_command"; then + # Preserve any variables that may affect compiler behavior + for var in $variables_saved_for_relink; do + if eval test -z \"\${$var+set}\"; then + relink_command="{ test -z \"\${$var+set}\" || unset $var || { $var=; export $var; }; }; $relink_command" + elif eval var_value=\$$var; test -z "$var_value"; then + relink_command="$var=; export $var; $relink_command" + else + var_value=`$echo "X$var_value" | $Xsed -e "$sed_quote_subst"` + relink_command="$var=\"$var_value\"; export $var; $relink_command" + fi + done + relink_command="(cd `pwd`; $relink_command)" + relink_command=`$echo "X$relink_command" | $Xsed -e "$sed_quote_subst"` + fi + + # Quote $echo for shipping. + if test "X$echo" = "X$SHELL $0 --fallback-echo"; then + case $0 in + [\\/]* | [A-Za-z]:[\\/]*) qecho="$SHELL $0 --fallback-echo";; + *) qecho="$SHELL `pwd`/$0 --fallback-echo";; + esac + qecho=`$echo "X$qecho" | $Xsed -e "$sed_quote_subst"` + else + qecho=`$echo "X$echo" | $Xsed -e "$sed_quote_subst"` + fi + + # Only actually do things if our run command is non-null. + if test -z "$run"; then + # win32 will think the script is a binary if it has + # a .exe suffix, so we strip it off here. + case $output in + *.exe) output=`$echo $output|${SED} 's,.exe$,,'` ;; + esac + # test for cygwin because mv fails w/o .exe extensions + case $host in + *cygwin*) + exeext=.exe + outputname=`$echo $outputname|${SED} 's,.exe$,,'` ;; + *) exeext= ;; + esac + case $host in + *cygwin* | *mingw* ) + cwrappersource=`$echo ${objdir}/lt-${output}.c` + cwrapper=`$echo ${output}.exe` + $rm $cwrappersource $cwrapper + trap "$rm $cwrappersource $cwrapper; exit 1" 1 2 15 + + cat > $cwrappersource <> $cwrappersource<<"EOF" +#include +#include +#include +#include +#include +#include + +#if defined(PATH_MAX) +# define LT_PATHMAX PATH_MAX +#elif defined(MAXPATHLEN) +# define LT_PATHMAX MAXPATHLEN +#else +# define LT_PATHMAX 1024 +#endif + +#ifndef DIR_SEPARATOR +#define DIR_SEPARATOR '/' +#endif + +#if defined (_WIN32) || defined (__MSDOS__) || defined (__DJGPP__) || \ + defined (__OS2__) +#define HAVE_DOS_BASED_FILE_SYSTEM +#ifndef DIR_SEPARATOR_2 +#define DIR_SEPARATOR_2 '\\' +#endif +#endif + +#ifndef DIR_SEPARATOR_2 +# define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR) +#else /* DIR_SEPARATOR_2 */ +# define IS_DIR_SEPARATOR(ch) \ + (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2)) +#endif /* DIR_SEPARATOR_2 */ + +#define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type))) +#define XFREE(stale) do { \ + if (stale) { free ((void *) stale); stale = 0; } \ +} while (0) + +const char *program_name = NULL; + +void * xmalloc (size_t num); +char * xstrdup (const char *string); +char * basename (const char *name); +char * fnqualify(const char *path); +char * strendzap(char *str, const char *pat); +void lt_fatal (const char *message, ...); + +int +main (int argc, char *argv[]) +{ + char **newargz; + int i; + + program_name = (char *) xstrdup ((char *) basename (argv[0])); + newargz = XMALLOC(char *, argc+2); +EOF + + cat >> $cwrappersource <> $cwrappersource <<"EOF" + newargz[1] = fnqualify(argv[0]); + /* we know the script has the same name, without the .exe */ + /* so make sure newargz[1] doesn't end in .exe */ + strendzap(newargz[1],".exe"); + for (i = 1; i < argc; i++) + newargz[i+1] = xstrdup(argv[i]); + newargz[argc+1] = NULL; +EOF + + cat >> $cwrappersource <> $cwrappersource <<"EOF" +} + +void * +xmalloc (size_t num) +{ + void * p = (void *) malloc (num); + if (!p) + lt_fatal ("Memory exhausted"); + + return p; +} + +char * +xstrdup (const char *string) +{ + return string ? strcpy ((char *) xmalloc (strlen (string) + 1), string) : NULL +; +} + +char * +basename (const char *name) +{ + const char *base; + +#if defined (HAVE_DOS_BASED_FILE_SYSTEM) + /* Skip over the disk name in MSDOS pathnames. */ + if (isalpha (name[0]) && name[1] == ':') + name += 2; +#endif + + for (base = name; *name; name++) + if (IS_DIR_SEPARATOR (*name)) + base = name + 1; + return (char *) base; +} + +char * +fnqualify(const char *path) +{ + size_t size; + char *p; + char tmp[LT_PATHMAX + 1]; + + assert(path != NULL); + + /* Is it qualified already? */ +#if defined (HAVE_DOS_BASED_FILE_SYSTEM) + if (isalpha (path[0]) && path[1] == ':') + return xstrdup (path); +#endif + if (IS_DIR_SEPARATOR (path[0])) + return xstrdup (path); + + /* prepend the current directory */ + /* doesn't handle '~' */ + if (getcwd (tmp, LT_PATHMAX) == NULL) + lt_fatal ("getcwd failed"); + size = strlen(tmp) + 1 + strlen(path) + 1; /* +2 for '/' and '\0' */ + p = XMALLOC(char, size); + sprintf(p, "%s%c%s", tmp, DIR_SEPARATOR, path); + return p; +} + +char * +strendzap(char *str, const char *pat) +{ + size_t len, patlen; + + assert(str != NULL); + assert(pat != NULL); + + len = strlen(str); + patlen = strlen(pat); + + if (patlen <= len) + { + str += len - patlen; + if (strcmp(str, pat) == 0) + *str = '\0'; + } + return str; +} + +static void +lt_error_core (int exit_status, const char * mode, + const char * message, va_list ap) +{ + fprintf (stderr, "%s: %s: ", program_name, mode); + vfprintf (stderr, message, ap); + fprintf (stderr, ".\n"); + + if (exit_status >= 0) + exit (exit_status); +} + +void +lt_fatal (const char *message, ...) +{ + va_list ap; + va_start (ap, message); + lt_error_core (EXIT_FAILURE, "FATAL", message, ap); + va_end (ap); +} +EOF + # we should really use a build-platform specific compiler + # here, but OTOH, the wrappers (shell script and this C one) + # are only useful if you want to execute the "real" binary. + # Since the "real" binary is built for $host, then this + # wrapper might as well be built for $host, too. + $run $LTCC -s -o $cwrapper $cwrappersource + ;; + esac + $rm $output + trap "$rm $output; exit 1" 1 2 15 + + $echo > $output "\ +#! $SHELL + +# $output - temporary wrapper script for $objdir/$outputname +# Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP +# +# The $output program cannot be directly executed until all the libtool +# libraries that it depends on are installed. +# +# This wrapper script should never be moved out of the build directory. +# If it is, it will not operate correctly. + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +Xsed='${SED} -e 1s/^X//' +sed_quote_subst='$sed_quote_subst' + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +if test \"\${CDPATH+set}\" = set; then CDPATH=:; export CDPATH; fi + +relink_command=\"$relink_command\" + +# This environment variable determines our operation mode. +if test \"\$libtool_install_magic\" = \"$magic\"; then + # install mode needs the following variable: + notinst_deplibs='$notinst_deplibs' +else + # When we are sourced in execute mode, \$file and \$echo are already set. + if test \"\$libtool_execute_magic\" != \"$magic\"; then + echo=\"$qecho\" + file=\"\$0\" + # Make sure echo works. + if test \"X\$1\" = X--no-reexec; then + # Discard the --no-reexec flag, and continue. + shift + elif test \"X\`(\$echo '\t') 2>/dev/null\`\" = 'X\t'; then + # Yippee, \$echo works! + : + else + # Restart under the correct shell, and then maybe \$echo will work. + exec $SHELL \"\$0\" --no-reexec \${1+\"\$@\"} + fi + fi\ +" + $echo >> $output "\ + + # Find the directory that this script lives in. + thisdir=\`\$echo \"X\$file\" | \$Xsed -e 's%/[^/]*$%%'\` + test \"x\$thisdir\" = \"x\$file\" && thisdir=. + + # Follow symbolic links until we get to the real thisdir. + file=\`ls -ld \"\$file\" | ${SED} -n 's/.*-> //p'\` + while test -n \"\$file\"; do + destdir=\`\$echo \"X\$file\" | \$Xsed -e 's%/[^/]*\$%%'\` + + # If there was a directory component, then change thisdir. + if test \"x\$destdir\" != \"x\$file\"; then + case \"\$destdir\" in + [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;; + *) thisdir=\"\$thisdir/\$destdir\" ;; + esac + fi + + file=\`\$echo \"X\$file\" | \$Xsed -e 's%^.*/%%'\` + file=\`ls -ld \"\$thisdir/\$file\" | ${SED} -n 's/.*-> //p'\` + done + + # Try to get the absolute directory name. + absdir=\`cd \"\$thisdir\" && pwd\` + test -n \"\$absdir\" && thisdir=\"\$absdir\" +" + + if test "$fast_install" = yes; then + $echo >> $output "\ + program=lt-'$outputname'$exeext + progdir=\"\$thisdir/$objdir\" + + if test ! -f \"\$progdir/\$program\" || \\ + { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | ${SED} 1q\`; \\ + test \"X\$file\" != \"X\$progdir/\$program\"; }; then + + file=\"\$\$-\$program\" + + if test ! -d \"\$progdir\"; then + $mkdir \"\$progdir\" + else + $rm \"\$progdir/\$file\" + fi" + + $echo >> $output "\ + + # relink executable if necessary + if test -n \"\$relink_command\"; then + if relink_command_output=\`eval \$relink_command 2>&1\`; then : + else + $echo \"\$relink_command_output\" >&2 + $rm \"\$progdir/\$file\" + exit 1 + fi + fi + + $mv \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null || + { $rm \"\$progdir/\$program\"; + $mv \"\$progdir/\$file\" \"\$progdir/\$program\"; } + $rm \"\$progdir/\$file\" + fi" + else + $echo >> $output "\ + program='$outputname' + progdir=\"\$thisdir/$objdir\" +" + fi + + $echo >> $output "\ + + if test -f \"\$progdir/\$program\"; then" + + # Export our shlibpath_var if we have one. + if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then + $echo >> $output "\ + # Add our own library path to $shlibpath_var + $shlibpath_var=\"$temp_rpath\$$shlibpath_var\" + + # Some systems cannot cope with colon-terminated $shlibpath_var + # The second colon is a workaround for a bug in BeOS R4 sed + $shlibpath_var=\`\$echo \"X\$$shlibpath_var\" | \$Xsed -e 's/::*\$//'\` + + export $shlibpath_var +" + fi + + # fixup the dll searchpath if we need to. + if test -n "$dllsearchpath"; then + $echo >> $output "\ + # Add the dll search path components to the executable PATH + PATH=$dllsearchpath:\$PATH +" + fi + + $echo >> $output "\ + if test \"\$libtool_execute_magic\" != \"$magic\"; then + # Run the actual program with our arguments. +" + case $host in + # Backslashes separate directories on plain windows + *-*-mingw | *-*-os2*) + $echo >> $output "\ + exec \$progdir\\\\\$program \${1+\"\$@\"} +" + ;; + + *) + $echo >> $output "\ + exec \$progdir/\$program \${1+\"\$@\"} +" + ;; + esac + $echo >> $output "\ + \$echo \"\$0: cannot exec \$program \${1+\"\$@\"}\" + exit 1 + fi + else + # The program doesn't exist. + \$echo \"\$0: error: \$progdir/\$program does not exist\" 1>&2 + \$echo \"This script is just a wrapper for \$program.\" 1>&2 + $echo \"See the $PACKAGE documentation for more information.\" 1>&2 + exit 1 + fi +fi\ +" + chmod +x $output + fi + exit 0 + ;; + esac + + # See if we need to build an old-fashioned archive. + for oldlib in $oldlibs; do + + if test "$build_libtool_libs" = convenience; then + oldobjs="$libobjs_save" + addlibs="$convenience" + build_libtool_libs=no + else + if test "$build_libtool_libs" = module; then + oldobjs="$libobjs_save" + build_libtool_libs=no + else + oldobjs="$old_deplibs $non_pic_objects" + fi + addlibs="$old_convenience" + fi + + if test -n "$addlibs"; then + gentop="$output_objdir/${outputname}x" + $show "${rm}r $gentop" + $run ${rm}r "$gentop" + $show "$mkdir $gentop" + $run $mkdir "$gentop" + status=$? + if test "$status" -ne 0 && test ! -d "$gentop"; then + exit $status + fi + generated="$generated $gentop" + + # Add in members from convenience archives. + for xlib in $addlibs; do + # Extract the objects. + case $xlib in + [\\/]* | [A-Za-z]:[\\/]*) xabs="$xlib" ;; + *) xabs=`pwd`"/$xlib" ;; + esac + xlib=`$echo "X$xlib" | $Xsed -e 's%^.*/%%'` + xdir="$gentop/$xlib" + + $show "${rm}r $xdir" + $run ${rm}r "$xdir" + $show "$mkdir $xdir" + $run $mkdir "$xdir" + status=$? + if test "$status" -ne 0 && test ! -d "$xdir"; then + exit $status + fi + # We will extract separately just the conflicting names and we will no + # longer touch any unique names. It is faster to leave these extract + # automatically by $AR in one run. + $show "(cd $xdir && $AR x $xabs)" + $run eval "(cd \$xdir && $AR x \$xabs)" || exit $? + if ($AR t "$xabs" | sort | sort -uc >/dev/null 2>&1); then + : + else + $echo "$modename: warning: object name conflicts; renaming object files" 1>&2 + $echo "$modename: warning: to ensure that they will not overwrite" 1>&2 + $AR t "$xabs" | sort | uniq -cd | while read -r count name + do + i=1 + while test "$i" -le "$count" + do + # Put our $i before any first dot (extension) + # Never overwrite any file + name_to="$name" + while test "X$name_to" = "X$name" || test -f "$xdir/$name_to" + do + name_to=`$echo "X$name_to" | $Xsed -e "s/\([^.]*\)/\1-$i/"` + done + $show "(cd $xdir && $AR xN $i $xabs '$name' && $mv '$name' '$name_to')" + $run eval "(cd \$xdir && $AR xN $i \$xabs '$name' && $mv '$name' '$name_to')" || exit $? + i=`expr $i + 1` + done + done + fi + + oldobjs="$oldobjs "`find $xdir -name \*.${objext} -print -o -name \*.lo -print | $NL2SP` + done + fi + + # Do each command in the archive commands. + if test -n "$old_archive_from_new_cmds" && test "$build_libtool_libs" = yes; then + eval cmds=\"$old_archive_from_new_cmds\" + else + eval cmds=\"$old_archive_cmds\" + + if len=`expr "X$cmds" : ".*"` && + test "$len" -le "$max_cmd_len" || test "$max_cmd_len" -le -1; then + : + else + # the command line is too long to link in one step, link in parts + $echo "using piecewise archive linking..." + save_RANLIB=$RANLIB + RANLIB=: + objlist= + concat_cmds= + save_oldobjs=$oldobjs + # GNU ar 2.10+ was changed to match POSIX; thus no paths are + # encoded into archives. This makes 'ar r' malfunction in + # this piecewise linking case whenever conflicting object + # names appear in distinct ar calls; check, warn and compensate. + if (for obj in $save_oldobjs + do + $echo "X$obj" | $Xsed -e 's%^.*/%%' + done | sort | sort -uc >/dev/null 2>&1); then + : + else + $echo "$modename: warning: object name conflicts; overriding AR_FLAGS to 'cq'" 1>&2 + $echo "$modename: warning: to ensure that POSIX-compatible ar will work" 1>&2 + AR_FLAGS=cq + fi + # Is there a better way of finding the last object in the list? + for obj in $save_oldobjs + do + last_oldobj=$obj + done + for obj in $save_oldobjs + do + oldobjs="$objlist $obj" + objlist="$objlist $obj" + eval test_cmds=\"$old_archive_cmds\" + if len=`expr "X$test_cmds" : ".*"` && + test "$len" -le "$max_cmd_len"; then + : + else + # the above command should be used before it gets too long + oldobjs=$objlist + if test "$obj" = "$last_oldobj" ; then + RANLIB=$save_RANLIB + fi + test -z "$concat_cmds" || concat_cmds=$concat_cmds~ + eval concat_cmds=\"\${concat_cmds}$old_archive_cmds\" + objlist= + fi + done + RANLIB=$save_RANLIB + oldobjs=$objlist + if test "X$oldobjs" = "X" ; then + eval cmds=\"\$concat_cmds\" + else + eval cmds=\"\$concat_cmds~$old_archive_cmds\" + fi + fi + fi + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + done + + if test -n "$generated"; then + $show "${rm}r$generated" + $run ${rm}r$generated + fi + + # Now create the libtool archive. + case $output in + *.la) + old_library= + test "$build_old_libs" = yes && old_library="$libname.$libext" + $show "creating $output" + + # Preserve any variables that may affect compiler behavior + for var in $variables_saved_for_relink; do + if eval test -z \"\${$var+set}\"; then + relink_command="{ test -z \"\${$var+set}\" || unset $var || { $var=; export $var; }; }; $relink_command" + elif eval var_value=\$$var; test -z "$var_value"; then + relink_command="$var=; export $var; $relink_command" + else + var_value=`$echo "X$var_value" | $Xsed -e "$sed_quote_subst"` + relink_command="$var=\"$var_value\"; export $var; $relink_command" + fi + done + # Quote the link command for shipping. + relink_command="(cd `pwd`; $SHELL $0 --mode=relink $libtool_args @inst_prefix_dir@)" + relink_command=`$echo "X$relink_command" | $Xsed -e "$sed_quote_subst"` + + # Only create the output if not a dry run. + if test -z "$run"; then + for installed in no yes; do + if test "$installed" = yes; then + if test -z "$install_libdir"; then + break + fi + output="$output_objdir/$outputname"i + # Replace all uninstalled libtool libraries with the installed ones + newdependency_libs= + for deplib in $dependency_libs; do + case $deplib in + *.la) + name=`$echo "X$deplib" | $Xsed -e 's%^.*/%%'` + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` + if test -z "$libdir"; then + $echo "$modename: \`$deplib' is not a valid libtool archive" 1>&2 + exit 1 + fi + newdependency_libs="$newdependency_libs $libdir/$name" + ;; + *) newdependency_libs="$newdependency_libs $deplib" ;; + esac + done + dependency_libs="$newdependency_libs" + newdlfiles= + for lib in $dlfiles; do + name=`$echo "X$lib" | $Xsed -e 's%^.*/%%'` + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` + if test -z "$libdir"; then + $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2 + exit 1 + fi + newdlfiles="$newdlfiles $libdir/$name" + done + dlfiles="$newdlfiles" + newdlprefiles= + for lib in $dlprefiles; do + name=`$echo "X$lib" | $Xsed -e 's%^.*/%%'` + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` + if test -z "$libdir"; then + $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2 + exit 1 + fi + newdlprefiles="$newdlprefiles $libdir/$name" + done + dlprefiles="$newdlprefiles" + fi + $rm $output + # place dlname in correct position for cygwin + tdlname=$dlname + case $host,$output,$installed,$module,$dlname in + *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll) tdlname=../bin/$dlname ;; + esac + $echo > $output "\ +# $outputname - a libtool library file +# Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='$tdlname' + +# Names of this library. +library_names='$library_names' + +# The name of the static archive. +old_library='$old_library' + +# Libraries that this one depends upon. +dependency_libs='$dependency_libs' + +# Version information for $libname. +current=$current +age=$age +revision=$revision + +# Is this an already installed library? +installed=$installed + +# Should we warn about portability when linking against -modules? +shouldnotlink=$module + +# Files to dlopen/dlpreopen +dlopen='$dlfiles' +dlpreopen='$dlprefiles' + +# Directory that this library needs to be installed in: +libdir='$install_libdir'" + if test "$installed" = no && test "$need_relink" = yes; then + $echo >> $output "\ +relink_command=\"$relink_command\"" + fi + done + fi + + # Do a symbolic link so that the libtool archive can be found in + # LD_LIBRARY_PATH before the program is installed. + $show "(cd $output_objdir && $rm $outputname && $LN_S ../$outputname $outputname)" + $run eval '(cd $output_objdir && $rm $outputname && $LN_S ../$outputname $outputname)' || exit $? + ;; + esac + exit 0 + ;; + + # libtool install mode + install) + modename="$modename: install" + + # There may be an optional sh(1) argument at the beginning of + # install_prog (especially on Windows NT). + if test "$nonopt" = "$SHELL" || test "$nonopt" = /bin/sh || + # Allow the use of GNU shtool's install command. + $echo "X$nonopt" | $Xsed | grep shtool > /dev/null; then + # Aesthetically quote it. + arg=`$echo "X$nonopt" | $Xsed -e "$sed_quote_subst"` + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*) + arg="\"$arg\"" + ;; + esac + install_prog="$arg " + arg="$1" + shift + else + install_prog= + arg="$nonopt" + fi + + # The real first argument should be the name of the installation program. + # Aesthetically quote it. + arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*) + arg="\"$arg\"" + ;; + esac + install_prog="$install_prog$arg" + + # We need to accept at least all the BSD install flags. + dest= + files= + opts= + prev= + install_type= + isdir=no + stripme= + for arg + do + if test -n "$dest"; then + files="$files $dest" + dest="$arg" + continue + fi + + case $arg in + -d) isdir=yes ;; + -f) prev="-f" ;; + -g) prev="-g" ;; + -m) prev="-m" ;; + -o) prev="-o" ;; + -s) + stripme=" -s" + continue + ;; + -*) ;; + + *) + # If the previous option needed an argument, then skip it. + if test -n "$prev"; then + prev= + else + dest="$arg" + continue + fi + ;; + esac + + # Aesthetically quote the argument. + arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*) + arg="\"$arg\"" + ;; + esac + install_prog="$install_prog $arg" + done + + if test -z "$install_prog"; then + $echo "$modename: you must specify an install program" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + + if test -n "$prev"; then + $echo "$modename: the \`$prev' option requires an argument" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + + if test -z "$files"; then + if test -z "$dest"; then + $echo "$modename: no file or destination specified" 1>&2 + else + $echo "$modename: you must specify a destination" 1>&2 + fi + $echo "$help" 1>&2 + exit 1 + fi + + # Strip any trailing slash from the destination. + dest=`$echo "X$dest" | $Xsed -e 's%/$%%'` + + # Check to see that the destination is a directory. + test -d "$dest" && isdir=yes + if test "$isdir" = yes; then + destdir="$dest" + destname= + else + destdir=`$echo "X$dest" | $Xsed -e 's%/[^/]*$%%'` + test "X$destdir" = "X$dest" && destdir=. + destname=`$echo "X$dest" | $Xsed -e 's%^.*/%%'` + + # Not a directory, so check to see that there is only one file specified. + set dummy $files + if test "$#" -gt 2; then + $echo "$modename: \`$dest' is not a directory" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + fi + case $destdir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + for file in $files; do + case $file in + *.lo) ;; + *) + $echo "$modename: \`$destdir' must be an absolute directory name" 1>&2 + $echo "$help" 1>&2 + exit 1 + ;; + esac + done + ;; + esac + + # This variable tells wrapper scripts just to set variables rather + # than running their programs. + libtool_install_magic="$magic" + + staticlibs= + future_libdirs= + current_libdirs= + for file in $files; do + + # Do each installation. + case $file in + *.$libext) + # Do the static libraries later. + staticlibs="$staticlibs $file" + ;; + + *.la) + # Check to see that this really is a libtool archive. + if (${SED} -e '2q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then : + else + $echo "$modename: \`$file' is not a valid libtool archive" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + + library_names= + old_library= + relink_command= + # If there is no directory component, then add one. + case $file in + */* | *\\*) . $file ;; + *) . ./$file ;; + esac + + # Add the libdir to current_libdirs if it is the destination. + if test "X$destdir" = "X$libdir"; then + case "$current_libdirs " in + *" $libdir "*) ;; + *) current_libdirs="$current_libdirs $libdir" ;; + esac + else + # Note the libdir as a future libdir. + case "$future_libdirs " in + *" $libdir "*) ;; + *) future_libdirs="$future_libdirs $libdir" ;; + esac + fi + + dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'`/ + test "X$dir" = "X$file/" && dir= + dir="$dir$objdir" + + if test -n "$relink_command"; then + # Determine the prefix the user has applied to our future dir. + inst_prefix_dir=`$echo "$destdir" | $SED "s%$libdir\$%%"` + + # Don't allow the user to place us outside of our expected + # location b/c this prevents finding dependent libraries that + # are installed to the same prefix. + # At present, this check doesn't affect windows .dll's that + # are installed into $libdir/../bin (currently, that works fine) + # but it's something to keep an eye on. + if test "$inst_prefix_dir" = "$destdir"; then + $echo "$modename: error: cannot install \`$file' to a directory not ending in $libdir" 1>&2 + exit 1 + fi + + if test -n "$inst_prefix_dir"; then + # Stick the inst_prefix_dir data into the link command. + relink_command=`$echo "$relink_command" | $SED "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"` + else + relink_command=`$echo "$relink_command" | $SED "s%@inst_prefix_dir@%%"` + fi + + $echo "$modename: warning: relinking \`$file'" 1>&2 + $show "$relink_command" + if $run eval "$relink_command"; then : + else + $echo "$modename: error: relink \`$file' with the above command before installing it" 1>&2 + exit 1 + fi + fi + + # See the names of the shared library. + set dummy $library_names + if test -n "$2"; then + realname="$2" + shift + shift + + srcname="$realname" + test -n "$relink_command" && srcname="$realname"T + + # Install the shared library and build the symlinks. + $show "$install_prog $dir/$srcname $destdir/$realname" + $run eval "$install_prog $dir/$srcname $destdir/$realname" || exit $? + if test -n "$stripme" && test -n "$striplib"; then + $show "$striplib $destdir/$realname" + $run eval "$striplib $destdir/$realname" || exit $? + fi + + if test "$#" -gt 0; then + # Delete the old symlinks, and create new ones. + for linkname + do + if test "$linkname" != "$realname"; then + $show "(cd $destdir && $rm $linkname && $LN_S $realname $linkname)" + $run eval "(cd $destdir && $rm $linkname && $LN_S $realname $linkname)" + fi + done + fi + + # Do each command in the postinstall commands. + lib="$destdir/$realname" + eval cmds=\"$postinstall_cmds\" + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + fi + + # Install the pseudo-library for information purposes. + name=`$echo "X$file" | $Xsed -e 's%^.*/%%'` + instname="$dir/$name"i + $show "$install_prog $instname $destdir/$name" + $run eval "$install_prog $instname $destdir/$name" || exit $? + + # Maybe install the static library, too. + test -n "$old_library" && staticlibs="$staticlibs $dir/$old_library" + ;; + + *.lo) + # Install (i.e. copy) a libtool object. + + # Figure out destination file name, if it wasn't already specified. + if test -n "$destname"; then + destfile="$destdir/$destname" + else + destfile=`$echo "X$file" | $Xsed -e 's%^.*/%%'` + destfile="$destdir/$destfile" + fi + + # Deduce the name of the destination old-style object file. + case $destfile in + *.lo) + staticdest=`$echo "X$destfile" | $Xsed -e "$lo2o"` + ;; + *.$objext) + staticdest="$destfile" + destfile= + ;; + *) + $echo "$modename: cannot copy a libtool object to \`$destfile'" 1>&2 + $echo "$help" 1>&2 + exit 1 + ;; + esac + + # Install the libtool object if requested. + if test -n "$destfile"; then + $show "$install_prog $file $destfile" + $run eval "$install_prog $file $destfile" || exit $? + fi + + # Install the old object if enabled. + if test "$build_old_libs" = yes; then + # Deduce the name of the old-style object file. + staticobj=`$echo "X$file" | $Xsed -e "$lo2o"` + + $show "$install_prog $staticobj $staticdest" + $run eval "$install_prog \$staticobj \$staticdest" || exit $? + fi + exit 0 + ;; + + *) + # Figure out destination file name, if it wasn't already specified. + if test -n "$destname"; then + destfile="$destdir/$destname" + else + destfile=`$echo "X$file" | $Xsed -e 's%^.*/%%'` + destfile="$destdir/$destfile" + fi + + # If the file is missing, and there is a .exe on the end, strip it + # because it is most likely a libtool script we actually want to + # install + stripped_ext="" + case $file in + *.exe) + if test ! -f "$file"; then + file=`$echo $file|${SED} 's,.exe$,,'` + stripped_ext=".exe" + fi + ;; + esac + + # Do a test to see if this is really a libtool program. + case $host in + *cygwin*|*mingw*) + wrapper=`$echo $file | ${SED} -e 's,.exe$,,'` + ;; + *) + wrapper=$file + ;; + esac + if (${SED} -e '4q' $wrapper | grep "^# Generated by .*$PACKAGE")>/dev/null 2>&1; then + notinst_deplibs= + relink_command= + + # To insure that "foo" is sourced, and not "foo.exe", + # finese the cygwin/MSYS system by explicitly sourcing "foo." + # which disallows the automatic-append-.exe behavior. + case $build in + *cygwin* | *mingw*) wrapperdot=${wrapper}. ;; + *) wrapperdot=${wrapper} ;; + esac + # If there is no directory component, then add one. + case $file in + */* | *\\*) . ${wrapperdot} ;; + *) . ./${wrapperdot} ;; + esac + + # Check the variables that should have been set. + if test -z "$notinst_deplibs"; then + $echo "$modename: invalid libtool wrapper script \`$wrapper'" 1>&2 + exit 1 + fi + + finalize=yes + for lib in $notinst_deplibs; do + # Check to see that each library is installed. + libdir= + if test -f "$lib"; then + # If there is no directory component, then add one. + case $lib in + */* | *\\*) . $lib ;; + *) . ./$lib ;; + esac + fi + libfile="$libdir/"`$echo "X$lib" | $Xsed -e 's%^.*/%%g'` ### testsuite: skip nested quoting test + if test -n "$libdir" && test ! -f "$libfile"; then + $echo "$modename: warning: \`$lib' has not been installed in \`$libdir'" 1>&2 + finalize=no + fi + done + + relink_command= + # To insure that "foo" is sourced, and not "foo.exe", + # finese the cygwin/MSYS system by explicitly sourcing "foo." + # which disallows the automatic-append-.exe behavior. + case $build in + *cygwin* | *mingw*) wrapperdot=${wrapper}. ;; + *) wrapperdot=${wrapper} ;; + esac + # If there is no directory component, then add one. + case $file in + */* | *\\*) . ${wrapperdot} ;; + *) . ./${wrapperdot} ;; + esac + + outputname= + if test "$fast_install" = no && test -n "$relink_command"; then + if test "$finalize" = yes && test -z "$run"; then + tmpdir="/tmp" + test -n "$TMPDIR" && tmpdir="$TMPDIR" + tmpdir_mktemp=`mktemp -d $tmpdir/libtool-XXXXXX 2> /dev/null` + if test "$?" = 0 ; then + tmpdir="$tmpdir_mktemp" + unset tmpdir_mktemp + else + tmpdir="$tmpdir/libtool-$$" + fi + if $mkdir -p "$tmpdir" && chmod 700 "$tmpdir"; then : + else + $echo "$modename: error: cannot create temporary directory \`$tmpdir'" 1>&2 + continue + fi + file=`$echo "X$file$stripped_ext" | $Xsed -e 's%^.*/%%'` + outputname="$tmpdir/$file" + # Replace the output file specification. + relink_command=`$echo "X$relink_command" | $Xsed -e 's%@OUTPUT@%'"$outputname"'%g'` + + $show "$relink_command" + if $run eval "$relink_command"; then : + else + $echo "$modename: error: relink \`$file' with the above command before installing it" 1>&2 + ${rm}r "$tmpdir" + continue + fi + file="$outputname" + else + $echo "$modename: warning: cannot relink \`$file'" 1>&2 + fi + else + # Install the binary that we compiled earlier. + file=`$echo "X$file$stripped_ext" | $Xsed -e "s%\([^/]*\)$%$objdir/\1%"` + fi + fi + + # remove .exe since cygwin /usr/bin/install will append another + # one anyways + case $install_prog,$host in + */usr/bin/install*,*cygwin*) + case $file:$destfile in + *.exe:*.exe) + # this is ok + ;; + *.exe:*) + destfile=$destfile.exe + ;; + *:*.exe) + destfile=`$echo $destfile | ${SED} -e 's,.exe$,,'` + ;; + esac + ;; + esac + $show "$install_prog$stripme $file $destfile" + $run eval "$install_prog\$stripme \$file \$destfile" || exit $? + test -n "$outputname" && ${rm}r "$tmpdir" + ;; + esac + done + + for file in $staticlibs; do + name=`$echo "X$file" | $Xsed -e 's%^.*/%%'` + + # Set up the ranlib parameters. + oldlib="$destdir/$name" + + $show "$install_prog $file $oldlib" + $run eval "$install_prog \$file \$oldlib" || exit $? + + if test -n "$stripme" && test -n "$striplib"; then + $show "$old_striplib $oldlib" + $run eval "$old_striplib $oldlib" || exit $? + fi + + # Do each command in the postinstall commands. + eval cmds=\"$old_postinstall_cmds\" + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + done + + if test -n "$future_libdirs"; then + $echo "$modename: warning: remember to run \`$progname --finish$future_libdirs'" 1>&2 + fi + + if test -n "$current_libdirs"; then + # Maybe just do a dry run. + test -n "$run" && current_libdirs=" -n$current_libdirs" + exec_cmd='$SHELL $0 --finish$current_libdirs' + else + exit 0 + fi + ;; + + # libtool finish mode + finish) + modename="$modename: finish" + libdirs="$nonopt" + admincmds= + + if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then + for dir + do + libdirs="$libdirs $dir" + done + + for libdir in $libdirs; do + if test -n "$finish_cmds"; then + # Do each command in the finish commands. + eval cmds=\"$finish_cmds\" + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + $show "$cmd" + $run eval "$cmd" || admincmds="$admincmds + $cmd" + done + IFS="$save_ifs" + fi + if test -n "$finish_eval"; then + # Do the single finish_eval. + eval cmds=\"$finish_eval\" + $run eval "$cmds" || admincmds="$admincmds + $cmds" + fi + done + fi + + # Exit here if they wanted silent mode. + test "$show" = : && exit 0 + + $echo "----------------------------------------------------------------------" + $echo "Libraries have been installed in:" + for libdir in $libdirs; do + $echo " $libdir" + done + $echo + $echo "If you ever happen to want to link against installed libraries" + $echo "in a given directory, LIBDIR, you must either use libtool, and" + $echo "specify the full pathname of the library, or use the \`-LLIBDIR'" + $echo "flag during linking and do at least one of the following:" + if test -n "$shlibpath_var"; then + $echo " - add LIBDIR to the \`$shlibpath_var' environment variable" + $echo " during execution" + fi + if test -n "$runpath_var"; then + $echo " - add LIBDIR to the \`$runpath_var' environment variable" + $echo " during linking" + fi + if test -n "$hardcode_libdir_flag_spec"; then + libdir=LIBDIR + eval flag=\"$hardcode_libdir_flag_spec\" + + $echo " - use the \`$flag' linker flag" + fi + if test -n "$admincmds"; then + $echo " - have your system administrator run these commands:$admincmds" + fi + if test -f /etc/ld.so.conf; then + $echo " - have your system administrator add LIBDIR to \`/etc/ld.so.conf'" + fi + $echo + $echo "See any operating system documentation about shared libraries for" + $echo "more information, such as the ld(1) and ld.so(8) manual pages." + $echo "----------------------------------------------------------------------" + exit 0 + ;; + + # libtool execute mode + execute) + modename="$modename: execute" + + # The first argument is the command name. + cmd="$nonopt" + if test -z "$cmd"; then + $echo "$modename: you must specify a COMMAND" 1>&2 + $echo "$help" + exit 1 + fi + + # Handle -dlopen flags immediately. + for file in $execute_dlfiles; do + if test ! -f "$file"; then + $echo "$modename: \`$file' is not a file" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + + dir= + case $file in + *.la) + # Check to see that this really is a libtool archive. + if (${SED} -e '2q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then : + else + $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + + # Read the libtool library. + dlname= + library_names= + + # If there is no directory component, then add one. + case $file in + */* | *\\*) . $file ;; + *) . ./$file ;; + esac + + # Skip this library if it cannot be dlopened. + if test -z "$dlname"; then + # Warn if it was a shared library. + test -n "$library_names" && $echo "$modename: warning: \`$file' was not linked with \`-export-dynamic'" + continue + fi + + dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'` + test "X$dir" = "X$file" && dir=. + + if test -f "$dir/$objdir/$dlname"; then + dir="$dir/$objdir" + else + $echo "$modename: cannot find \`$dlname' in \`$dir' or \`$dir/$objdir'" 1>&2 + exit 1 + fi + ;; + + *.lo) + # Just add the directory containing the .lo file. + dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'` + test "X$dir" = "X$file" && dir=. + ;; + + *) + $echo "$modename: warning \`-dlopen' is ignored for non-libtool libraries and objects" 1>&2 + continue + ;; + esac + + # Get the absolute pathname. + absdir=`cd "$dir" && pwd` + test -n "$absdir" && dir="$absdir" + + # Now add the directory to shlibpath_var. + if eval "test -z \"\$$shlibpath_var\""; then + eval "$shlibpath_var=\"\$dir\"" + else + eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\"" + fi + done + + # This variable tells wrapper scripts just to set shlibpath_var + # rather than running their programs. + libtool_execute_magic="$magic" + + # Check if any of the arguments is a wrapper script. + args= + for file + do + case $file in + -*) ;; + *) + # Do a test to see if this is really a libtool program. + if (${SED} -e '4q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then + # If there is no directory component, then add one. + case $file in + */* | *\\*) . $file ;; + *) . ./$file ;; + esac + + # Transform arg to wrapped name. + file="$progdir/$program" + fi + ;; + esac + # Quote arguments (to preserve shell metacharacters). + file=`$echo "X$file" | $Xsed -e "$sed_quote_subst"` + args="$args \"$file\"" + done + + if test -z "$run"; then + if test -n "$shlibpath_var"; then + # Export the shlibpath_var. + eval "export $shlibpath_var" + fi + + # Restore saved environment variables + if test "${save_LC_ALL+set}" = set; then + LC_ALL="$save_LC_ALL"; export LC_ALL + fi + if test "${save_LANG+set}" = set; then + LANG="$save_LANG"; export LANG + fi + + # Now prepare to actually exec the command. + exec_cmd="\$cmd$args" + else + # Display what would be done. + if test -n "$shlibpath_var"; then + eval "\$echo \"\$shlibpath_var=\$$shlibpath_var\"" + $echo "export $shlibpath_var" + fi + $echo "$cmd$args" + exit 0 + fi + ;; + + # libtool clean and uninstall mode + clean | uninstall) + modename="$modename: $mode" + rm="$nonopt" + files= + rmforce= + exit_status=0 + + # This variable tells wrapper scripts just to set variables rather + # than running their programs. + libtool_install_magic="$magic" + + for arg + do + case $arg in + -f) rm="$rm $arg"; rmforce=yes ;; + -*) rm="$rm $arg" ;; + *) files="$files $arg" ;; + esac + done + + if test -z "$rm"; then + $echo "$modename: you must specify an RM program" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + + rmdirs= + + origobjdir="$objdir" + for file in $files; do + dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'` + if test "X$dir" = "X$file"; then + dir=. + objdir="$origobjdir" + else + objdir="$dir/$origobjdir" + fi + name=`$echo "X$file" | $Xsed -e 's%^.*/%%'` + test "$mode" = uninstall && objdir="$dir" + + # Remember objdir for removal later, being careful to avoid duplicates + if test "$mode" = clean; then + case " $rmdirs " in + *" $objdir "*) ;; + *) rmdirs="$rmdirs $objdir" ;; + esac + fi + + # Don't error if the file doesn't exist and rm -f was used. + if (test -L "$file") >/dev/null 2>&1 \ + || (test -h "$file") >/dev/null 2>&1 \ + || test -f "$file"; then + : + elif test -d "$file"; then + exit_status=1 + continue + elif test "$rmforce" = yes; then + continue + fi + + rmfiles="$file" + + case $name in + *.la) + # Possibly a libtool archive, so verify it. + if (${SED} -e '2q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then + . $dir/$name + + # Delete the libtool libraries and symlinks. + for n in $library_names; do + rmfiles="$rmfiles $objdir/$n" + done + test -n "$old_library" && rmfiles="$rmfiles $objdir/$old_library" + test "$mode" = clean && rmfiles="$rmfiles $objdir/$name $objdir/${name}i" + + if test "$mode" = uninstall; then + if test -n "$library_names"; then + # Do each command in the postuninstall commands. + eval cmds=\"$postuninstall_cmds\" + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + $show "$cmd" + $run eval "$cmd" + if test "$?" -ne 0 && test "$rmforce" != yes; then + exit_status=1 + fi + done + IFS="$save_ifs" + fi + + if test -n "$old_library"; then + # Do each command in the old_postuninstall commands. + eval cmds=\"$old_postuninstall_cmds\" + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + $show "$cmd" + $run eval "$cmd" + if test "$?" -ne 0 && test "$rmforce" != yes; then + exit_status=1 + fi + done + IFS="$save_ifs" + fi + # FIXME: should reinstall the best remaining shared library. + fi + fi + ;; + + *.lo) + # Possibly a libtool object, so verify it. + if (${SED} -e '2q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then + + # Read the .lo file + . $dir/$name + + # Add PIC object to the list of files to remove. + if test -n "$pic_object" \ + && test "$pic_object" != none; then + rmfiles="$rmfiles $dir/$pic_object" + fi + + # Add non-PIC object to the list of files to remove. + if test -n "$non_pic_object" \ + && test "$non_pic_object" != none; then + rmfiles="$rmfiles $dir/$non_pic_object" + fi + fi + ;; + + *) + if test "$mode" = clean ; then + noexename=$name + case $file in + *.exe) + file=`$echo $file|${SED} 's,.exe$,,'` + noexename=`$echo $name|${SED} 's,.exe$,,'` + # $file with .exe has already been added to rmfiles, + # add $file without .exe + rmfiles="$rmfiles $file" + ;; + esac + # Do a test to see if this is a libtool program. + if (${SED} -e '4q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then + relink_command= + . $dir/$noexename + + # note $name still contains .exe if it was in $file originally + # as does the version of $file that was added into $rmfiles + rmfiles="$rmfiles $objdir/$name $objdir/${name}S.${objext}" + if test "$fast_install" = yes && test -n "$relink_command"; then + rmfiles="$rmfiles $objdir/lt-$name" + fi + if test "X$noexename" != "X$name" ; then + rmfiles="$rmfiles $objdir/lt-${noexename}.c" + fi + fi + fi + ;; + esac + $show "$rm $rmfiles" + $run $rm $rmfiles || exit_status=1 + done + objdir="$origobjdir" + + # Try to remove the ${objdir}s in the directories where we deleted files + for dir in $rmdirs; do + if test -d "$dir"; then + $show "rmdir $dir" + $run rmdir $dir >/dev/null 2>&1 + fi + done + + exit $exit_status + ;; + + "") + $echo "$modename: you must specify a MODE" 1>&2 + $echo "$generic_help" 1>&2 + exit 1 + ;; + esac + + if test -z "$exec_cmd"; then + $echo "$modename: invalid operation mode \`$mode'" 1>&2 + $echo "$generic_help" 1>&2 + exit 1 + fi +fi # test -z "$show_help" + +if test -n "$exec_cmd"; then + eval exec $exec_cmd + exit 1 +fi + +# We need to display help for each of the modes. +case $mode in +"") $echo \ +"Usage: $modename [OPTION]... [MODE-ARG]... + +Provide generalized library-building support services. + + --config show all configuration variables + --debug enable verbose shell tracing +-n, --dry-run display commands without modifying any files + --features display basic configuration information and exit + --finish same as \`--mode=finish' + --help display this help message and exit + --mode=MODE use operation mode MODE [default=inferred from MODE-ARGS] + --quiet same as \`--silent' + --silent don't print informational messages + --tag=TAG use configuration variables from tag TAG + --version print version information + +MODE must be one of the following: + + clean remove files from the build directory + compile compile a source file into a libtool object + execute automatically set library path, then run a program + finish complete the installation of libtool libraries + install install libraries or executables + link create a library or an executable + uninstall remove libraries from an installed directory + +MODE-ARGS vary depending on the MODE. Try \`$modename --help --mode=MODE' for +a more detailed description of MODE. + +Report bugs to ." + exit 0 + ;; + +clean) + $echo \ +"Usage: $modename [OPTION]... --mode=clean RM [RM-OPTION]... FILE... + +Remove files from the build directory. + +RM is the name of the program to use to delete files associated with each FILE +(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed +to RM. + +If FILE is a libtool library, object or program, all the files associated +with it are deleted. Otherwise, only FILE itself is deleted using RM." + ;; + +compile) + $echo \ +"Usage: $modename [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE + +Compile a source file into a libtool library object. + +This mode accepts the following additional options: + + -o OUTPUT-FILE set the output file name to OUTPUT-FILE + -prefer-pic try to building PIC objects only + -prefer-non-pic try to building non-PIC objects only + -static always build a \`.o' file suitable for static linking + +COMPILE-COMMAND is a command to be used in creating a \`standard' object file +from the given SOURCEFILE. + +The output file name is determined by removing the directory component from +SOURCEFILE, then substituting the C source code suffix \`.c' with the +library object suffix, \`.lo'." + ;; + +execute) + $echo \ +"Usage: $modename [OPTION]... --mode=execute COMMAND [ARGS]... + +Automatically set library path, then run a program. + +This mode accepts the following additional options: + + -dlopen FILE add the directory containing FILE to the library path + +This mode sets the library path environment variable according to \`-dlopen' +flags. + +If any of the ARGS are libtool executable wrappers, then they are translated +into their corresponding uninstalled binary, and any of their required library +directories are added to the library path. + +Then, COMMAND is executed, with ARGS as arguments." + ;; + +finish) + $echo \ +"Usage: $modename [OPTION]... --mode=finish [LIBDIR]... + +Complete the installation of libtool libraries. + +Each LIBDIR is a directory that contains libtool libraries. + +The commands that this mode executes may require superuser privileges. Use +the \`--dry-run' option if you just want to see what would be executed." + ;; + +install) + $echo \ +"Usage: $modename [OPTION]... --mode=install INSTALL-COMMAND... + +Install executables or libraries. + +INSTALL-COMMAND is the installation command. The first component should be +either the \`install' or \`cp' program. + +The rest of the components are interpreted as arguments to that command (only +BSD-compatible install options are recognized)." + ;; + +link) + $echo \ +"Usage: $modename [OPTION]... --mode=link LINK-COMMAND... + +Link object files or libraries together to form another library, or to +create an executable program. + +LINK-COMMAND is a command using the C compiler that you would use to create +a program from several object files. + +The following components of LINK-COMMAND are treated specially: + + -all-static do not do any dynamic linking at all + -avoid-version do not add a version suffix if possible + -dlopen FILE \`-dlpreopen' FILE if it cannot be dlopened at runtime + -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols + -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3) + -export-symbols SYMFILE + try to export only the symbols listed in SYMFILE + -export-symbols-regex REGEX + try to export only the symbols matching REGEX + -LLIBDIR search LIBDIR for required installed libraries + -lNAME OUTPUT-FILE requires the installed library libNAME + -module build a library that can dlopened + -no-fast-install disable the fast-install mode + -no-install link a not-installable executable + -no-undefined declare that a library does not refer to external symbols + -o OUTPUT-FILE create OUTPUT-FILE from the specified objects + -objectlist FILE Use a list of object files found in FILE to specify objects + -release RELEASE specify package release information + -rpath LIBDIR the created library will eventually be installed in LIBDIR + -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries + -static do not do any dynamic linking of libtool libraries + -version-info CURRENT[:REVISION[:AGE]] + specify library version info [each variable defaults to 0] + +All other options (arguments beginning with \`-') are ignored. + +Every other argument is treated as a filename. Files ending in \`.la' are +treated as uninstalled libtool libraries, other files are standard or library +object files. + +If the OUTPUT-FILE ends in \`.la', then a libtool library is created, +only library objects (\`.lo' files) may be specified, and \`-rpath' is +required, except when creating a convenience library. + +If OUTPUT-FILE ends in \`.a' or \`.lib', then a standard library is created +using \`ar' and \`ranlib', or on Windows using \`lib'. + +If OUTPUT-FILE ends in \`.lo' or \`.${objext}', then a reloadable object file +is created, otherwise an executable program is created." + ;; + +uninstall) + $echo \ +"Usage: $modename [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE... + +Remove libraries from an installation directory. + +RM is the name of the program to use to delete files associated with each FILE +(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed +to RM. + +If FILE is a libtool library, all the files associated with it are deleted. +Otherwise, only FILE itself is deleted using RM." + ;; + +*) + $echo "$modename: invalid operation mode \`$mode'" 1>&2 + $echo "$help" 1>&2 + exit 1 + ;; +esac + +$echo +$echo "Try \`$modename --help' for more information about other modes." + +exit 0 + +# The TAGs below are defined such that we never get into a situation +# in which we disable both kinds of libraries. Given conflicting +# choices, we go for a static library, that is the most portable, +# since we can't tell whether shared libraries were disabled because +# the user asked for that or because the platform doesn't support +# them. This is particularly important on AIX, because we don't +# support having both static and shared libraries enabled at the same +# time on that platform, so we default to a shared-only configuration. +# If a disable-shared tag is given, we'll fallback to a static-only +# configuration. But we'll never go from static-only to shared-only. + +# ### BEGIN LIBTOOL TAG CONFIG: disable-shared +build_libtool_libs=no +build_old_libs=yes +# ### END LIBTOOL TAG CONFIG: disable-shared + +# ### BEGIN LIBTOOL TAG CONFIG: disable-static +build_old_libs=`case $build_libtool_libs in yes) $echo no;; *) $echo yes;; esac` +# ### END LIBTOOL TAG CONFIG: disable-static + +# Local Variables: +# mode:shell-script +# sh-indentation:2 +# End: +# ### BEGIN LIBTOOL TAG CONFIG: CXX + +# Libtool was configured on host QWEST-WKCH1I7I3: + +# Shell to use when invoking shell scripts. +SHELL="/bin/sh" + +# Whether or not to build shared libraries. +build_libtool_libs=yes + +# Whether or not to build static libraries. +build_old_libs=no + +# Whether or not to add -lc for building shared libraries. +build_libtool_need_lc=no + +# Whether or not to disallow shared libs when runtime libs are static +allow_libtool_libs_with_static_runtimes=yes + +# Whether or not to optimize for fast installation. +fast_install=needless + +# The host system. +host_alias= +host=i686-pc-mingw32 + +# An echo program that does not interpret backslashes. +echo="echo" + +# The archiver. +AR="ar" +AR_FLAGS="cru" + +# A C compiler. +LTCC="gcc" + +# A language-specific compiler. +CC="c++" + +# Is the compiler the GNU C compiler? +with_gcc=yes + +# An ERE matcher. +EGREP="grep -E" + +# The linker used to build libraries. +LD="c:/mnt/opt/mingw/mingw32/bin/ld.exe" + +# Whether we need hard or soft links. +LN_S="ln -s" + +# A BSD-compatible nm program. +NM="/bin/nm -B" + +# A symbol stripping program +STRIP=strip + +# Used to examine libraries when file_magic_cmd begins "file" +MAGIC_CMD=file + +# Used on cygwin: DLL creation program. +DLLTOOL="dlltool" + +# Used on cygwin: object dumper. +OBJDUMP="objdump" + +# Used on cygwin: assembler. +AS="as" + +# The name of the directory that contains temporary libtool files. +objdir=.libs + +# How to create reloadable object files. +reload_flag=" -r" +reload_cmds="\$LD\$reload_flag -o \$output\$reload_objs" + +# How to pass a linker flag through the compiler. +wl="-Wl," + +# Object file suffix (normally "o"). +objext="o" + +# Old archive suffix (normally "a"). +libext="a" + +# Shared library suffix (normally ".so"). +shrext='.dll' + +# Executable file suffix (normally ""). +exeext="" + +# Additional compiler flags for building library objects. +pic_flag=" -DDLL_EXPORT -DPIC" +pic_mode=default + +# What is the maximum length of a command? +max_cmd_len=8192 + +# Does compiler simultaneously support -c and -o options? +compiler_c_o="yes" + +# Must we lock files when doing compilation ? +need_locks="no" + +# Do we need the lib prefix for modules? +need_lib_prefix=no + +# Do we need a version for libraries? +need_version=no + +# Whether dlopen is supported. +dlopen_support=unknown + +# Whether dlopen of programs is supported. +dlopen_self=unknown + +# Whether dlopen of statically linked programs is supported. +dlopen_self_static=unknown + +# Compiler flag to prevent dynamic linking. +link_static_flag="-static" + +# Compiler flag to turn off builtin functions. +no_builtin_flag=" -fno-builtin" + +# Compiler flag to allow reflexive dlopens. +export_dynamic_flag_spec="\${wl}--export-dynamic" + +# Compiler flag to generate shared objects directly from archives. +whole_archive_flag_spec="" + +# Compiler flag to generate thread-safe objects. +thread_safe_flag_spec="" + +# Library versioning type. +version_type=windows + +# Format of library name prefix. +libname_spec="lib\$name" + +# List of archive names. First name is the real one, the rest are links. +# The last name is the one that the linker finds with -lNAME. +library_names_spec="\$libname.dll.a" + +# The coded name of the library, if different from the real name. +soname_spec="\${libname}\`echo \${release} | \$SED -e s/[.]/-/g\`\${versuffix}\${shared_ext}" + +# Commands used to build and install an old-style archive. +RANLIB="ranlib" +old_archive_cmds="\$AR \$AR_FLAGS \$oldlib\$oldobjs\$old_deplibs~\$RANLIB \$oldlib" +old_postinstall_cmds="\$RANLIB \$oldlib~chmod 644 \$oldlib" +old_postuninstall_cmds="" + +# Create an old-style archive from a shared archive. +old_archive_from_new_cmds="" + +# Create a temporary old-style archive to link instead of a shared archive. +old_archive_from_expsyms_cmds="" + +# Commands used to build and install a shared archive. +archive_cmds="\$CC -shared -nostdlib \$predep_objects \$libobjs \$deplibs \$postdep_objects \$compiler_flags -o \$output_objdir/\$soname \${wl}--image-base=0x10000000 \${wl}--out-implib,\$lib" +archive_expsym_cmds="if test \\\"x\\\`\$SED 1q \$export_symbols\\\`\\\" = xEXPORTS; then + cp \$export_symbols \$output_objdir/\$soname.def; + else + echo EXPORTS > \$output_objdir/\$soname.def; + cat \$export_symbols >> \$output_objdir/\$soname.def; + fi~ + \$CC -shared -nostdlib \$output_objdir/\$soname.def \$predep_objects \$libobjs \$deplibs \$postdep_objects \$compiler_flags -o \$output_objdir/\$soname \${wl}--image-base=0x10000000 \${wl}--out-implib,\$lib" +postinstall_cmds="base_file=\\\`basename \\\${file}\\\`~ + dlpath=\\\`\$SHELL 2>&1 -c '. \$dir/'\\\${base_file}'i;echo \\\$dlname'\\\`~ + dldir=\$destdir/\\\`dirname \\\$dlpath\\\`~ + test -d \\\$dldir || mkdir -p \\\$dldir~ + \$install_prog \$dir/\$dlname \\\$dldir/\$dlname" +postuninstall_cmds="dldll=\\\`\$SHELL 2>&1 -c '. \$file; echo \\\$dlname'\\\`~ + dlpath=\$dir/\\\$dldll~ + \$rm \\\$dlpath" + +# Commands used to build a loadable module (assumed same as above if empty) +module_cmds="" +module_expsym_cmds="" + +# Commands to strip libraries. +old_striplib="strip --strip-debug" +striplib="strip --strip-unneeded" + +# Dependencies to place before the objects being linked to create a +# shared library. +predep_objects="c:/mnt/opt/mingw/bin/../lib/gcc-lib/mingw32/3.2.3/../../../dllcrt2.o c:/mnt/opt/mingw/bin/../lib/gcc-lib/mingw32/3.2.3/crtbegin.o" + +# Dependencies to place after the objects being linked to create a +# shared library. +postdep_objects="c:/mnt/opt/mingw/bin/../lib/gcc-lib/mingw32/3.2.3/crtend.o" + +# Dependencies to place before the objects being linked to create a +# shared library. +predeps="" + +# Dependencies to place after the objects being linked to create a +# shared library. +postdeps="-lstdc++ -lmingw32 -lgcc -lmoldname -lmingwex -lmsvcrt -luser32 -lkernel32 -ladvapi32 -lshell32 -lmingw32 -lgcc -lmoldname -lmingwex -lmsvcrt" + +# The library search path used internally by the compiler when linking +# a shared library. +compiler_lib_search_path="-Lc:/mnt/opt/mingw/bin/../lib/gcc-lib/mingw32/3.2.3 -Lc:/mnt/opt/mingw/bin/../lib/gcc-lib -Lc:/mnt/opt/mingw/bin/../lib/gcc-lib/mingw32/3.2.3/../../../../mingw32/lib -Lc:/mnt/opt/mingw/bin/../lib/gcc-lib/mingw32/3.2.3/../../.." + +# Method to check whether dependent libraries are shared objects. +deplibs_check_method="file_magic ^x86 archive import|^x86 DLL" + +# Command to use when deplibs_check_method == file_magic. +file_magic_cmd="win32_libid" + +# Flag that allows shared libraries with undefined symbols to be built. +allow_undefined_flag="supported" + +# Flag that forces no undefined symbols. +no_undefined_flag="" + +# Commands used to finish a libtool library installation in a directory. +finish_cmds="" + +# Same as above, but a single script fragment to be evaled but not shown. +finish_eval="" + +# Take the output of nm and produce a listing of raw symbols and C names. +global_symbol_pipe="sed -n -e 's/^.*[ ]\\([ABCDGIRSTW][ABCDGIRSTW]*\\)[ ][ ]*\\(_\\)\\([_A-Za-z][_A-Za-z0-9]*\\) \\{0,1\\}\$/\\1 \\2\\3 \\3/p'" + +# Transform the output of nm in a proper C declaration +global_symbol_to_cdecl="sed -n -e 's/^. .* \\(.*\\)\$/extern int \\1;/p'" + +# Transform the output of nm in a C name address pair +global_symbol_to_c_name_address="sed -n -e 's/^: \\([^ ]*\\) \$/ {\\\"\\1\\\", (lt_ptr) 0},/p' -e 's/^[BCDEGRST] \\([^ ]*\\) \\([^ ]*\\)\$/ {\"\\2\", (lt_ptr) \\&\\2},/p'" + +# This is the shared library runtime path variable. +runpath_var=LD_RUN_PATH + +# This is the shared library path variable. +shlibpath_var=PATH + +# Is shlibpath searched before the hard-coded library search path? +shlibpath_overrides_runpath=yes + +# How to hardcode a shared library path into an executable. +hardcode_action=immediate + +# Whether we should hardcode library paths into libraries. +hardcode_into_libs=no + +# Flag to hardcode $libdir into a binary during linking. +# This must work even if $libdir does not exist. +hardcode_libdir_flag_spec="-L\$libdir" + +# If ld is used when linking, flag to hardcode $libdir into +# a binary during linking. This must work even if $libdir does +# not exist. +hardcode_libdir_flag_spec_ld="" + +# Whether we need a single -rpath flag with a separated argument. +hardcode_libdir_separator="" + +# Set to yes if using DIR/libNAME during linking hardcodes DIR into the +# resulting binary. +hardcode_direct=no + +# Set to yes if using the -LDIR flag during linking hardcodes DIR into the +# resulting binary. +hardcode_minus_L=no + +# Set to yes if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into +# the resulting binary. +hardcode_shlibpath_var= + +# Set to yes if building a shared library automatically hardcodes DIR into the library +# and all subsequent libraries and executables linked against it. +hardcode_automatic=no + +# Variables whose values should be saved in libtool wrapper scripts and +# restored at relink time. +variables_saved_for_relink="PATH PATH LD_RUN_PATH GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" + +# Whether libtool must link a program against all its dependency libraries. +link_all_deplibs=unknown + +# Compile-time system search path for libraries +sys_lib_search_path_spec=" =c:/mnt/opt/mingw/bin/../lib/gcc-lib/mingw32/3.2.3/ c:/mnt/opt/mingw/bin/../lib/gcc-lib/ /mingw/lib/gcc-lib/mingw32/3.2.3/ /usr/lib/gcc/mingw32/3.2.3/ c:/mnt/opt/mingw/bin/../lib/gcc-lib/mingw32/3.2.3/../../../../mingw32/lib/mingw32/3.2.3/ c:/mnt/opt/mingw/bin/../lib/gcc-lib/mingw32/3.2.3/../../../../mingw32/lib/ /mingw/lib/gcc-lib/mingw32/3.2.3/../../../../mingw32/lib/mingw32/3.2.3/ /mingw/lib/gcc-lib/mingw32/3.2.3/../../../../mingw32/lib/ c:/mnt/opt/mingw/bin/../lib/gcc-lib/mingw32/3.2.3/../../../mingw32/3.2.3/ c:/mnt/opt/mingw/bin/../lib/gcc-lib/mingw32/3.2.3/../../../ /mingw/lib/gcc-lib/mingw32/3.2.3/../../../mingw32/3.2.3/ /mingw/lib/gcc-lib/mingw32/3.2.3/../../../ /lib/mingw32/3.2.3/ /lib/ /usr/lib/mingw32/3.2.3/ /usr/lib/" + +# Run-time system search path for libraries +sys_lib_dlsearch_path_spec="/lib /usr/lib" + +# Fix the shell variable $srcfile for the compiler. +fix_srcfile_path="" + +# Set to yes if exported symbols are required. +always_export_symbols=no + +# The commands to list exported symbols. +export_symbols_cmds="\$NM \$libobjs \$convenience | \$global_symbol_pipe | \$SED -e '/^[BCDGS] /s/.* \\\\([^ ]*\\\\)/\\\\1 DATA/' | \$SED -e '/^[AITW] /s/.* //' | sort | uniq > \$export_symbols" + +# The commands to extract the exported symbol list from a shared archive. +extract_expsyms_cmds="" + +# Symbols that should not be listed in the preloaded symbols. +exclude_expsyms="" + +# Symbols that must always be exported. +include_expsyms="" + +# ### END LIBTOOL TAG CONFIG: CXX + +# ### BEGIN LIBTOOL TAG CONFIG: F77 + +# Libtool was configured on host QWEST-WKCH1I7I3: + +# Shell to use when invoking shell scripts. +SHELL="/bin/sh" + +# Whether or not to build shared libraries. +build_libtool_libs=yes + +# Whether or not to build static libraries. +build_old_libs=no + +# Whether or not to add -lc for building shared libraries. +build_libtool_need_lc=no + +# Whether or not to disallow shared libs when runtime libs are static +allow_libtool_libs_with_static_runtimes=yes + +# Whether or not to optimize for fast installation. +fast_install=needless + +# The host system. +host_alias= +host=i686-pc-mingw32 + +# An echo program that does not interpret backslashes. +echo="echo" + +# The archiver. +AR="ar" +AR_FLAGS="cru" + +# A C compiler. +LTCC="gcc" + +# A language-specific compiler. +CC="g77" + +# Is the compiler the GNU C compiler? +with_gcc=yes + +# An ERE matcher. +EGREP="grep -E" + +# The linker used to build libraries. +LD="c:/mnt/opt/mingw/mingw32/bin/ld.exe" + +# Whether we need hard or soft links. +LN_S="ln -s" + +# A BSD-compatible nm program. +NM="/bin/nm -B" + +# A symbol stripping program +STRIP=strip + +# Used to examine libraries when file_magic_cmd begins "file" +MAGIC_CMD=file + +# Used on cygwin: DLL creation program. +DLLTOOL="dlltool" + +# Used on cygwin: object dumper. +OBJDUMP="objdump" + +# Used on cygwin: assembler. +AS="as" + +# The name of the directory that contains temporary libtool files. +objdir=.libs + +# How to create reloadable object files. +reload_flag=" -r" +reload_cmds="\$LD\$reload_flag -o \$output\$reload_objs" + +# How to pass a linker flag through the compiler. +wl="-Wl," + +# Object file suffix (normally "o"). +objext="o" + +# Old archive suffix (normally "a"). +libext="a" + +# Shared library suffix (normally ".so"). +shrext='.dll' + +# Executable file suffix (normally ""). +exeext="" + +# Additional compiler flags for building library objects. +pic_flag=" -DDLL_EXPORT" +pic_mode=default + +# What is the maximum length of a command? +max_cmd_len=8192 + +# Does compiler simultaneously support -c and -o options? +compiler_c_o="yes" + +# Must we lock files when doing compilation ? +need_locks="no" + +# Do we need the lib prefix for modules? +need_lib_prefix=no + +# Do we need a version for libraries? +need_version=no + +# Whether dlopen is supported. +dlopen_support=unknown + +# Whether dlopen of programs is supported. +dlopen_self=unknown + +# Whether dlopen of statically linked programs is supported. +dlopen_self_static=unknown + +# Compiler flag to prevent dynamic linking. +link_static_flag="-static" + +# Compiler flag to turn off builtin functions. +no_builtin_flag="" + +# Compiler flag to allow reflexive dlopens. +export_dynamic_flag_spec="\${wl}--export-dynamic" + +# Compiler flag to generate shared objects directly from archives. +whole_archive_flag_spec="\${wl}--whole-archive\$convenience \${wl}--no-whole-archive" + +# Compiler flag to generate thread-safe objects. +thread_safe_flag_spec="" + +# Library versioning type. +version_type=windows + +# Format of library name prefix. +libname_spec="lib\$name" + +# List of archive names. First name is the real one, the rest are links. +# The last name is the one that the linker finds with -lNAME. +library_names_spec="\$libname.dll.a" + +# The coded name of the library, if different from the real name. +soname_spec="\${libname}\`echo \${release} | \$SED -e s/[.]/-/g\`\${versuffix}\${shared_ext}" + +# Commands used to build and install an old-style archive. +RANLIB="ranlib" +old_archive_cmds="\$AR \$AR_FLAGS \$oldlib\$oldobjs\$old_deplibs~\$RANLIB \$oldlib" +old_postinstall_cmds="\$RANLIB \$oldlib~chmod 644 \$oldlib" +old_postuninstall_cmds="" + +# Create an old-style archive from a shared archive. +old_archive_from_new_cmds="" + +# Create a temporary old-style archive to link instead of a shared archive. +old_archive_from_expsyms_cmds="" + +# Commands used to build and install a shared archive. +archive_cmds="\$CC -shared \$libobjs \$deplibs \$compiler_flags -o \$output_objdir/\$soname \${wl}--image-base=0x10000000 \${wl}--out-implib,\$lib" +archive_expsym_cmds="if test \\\"x\\\`\$SED 1q \$export_symbols\\\`\\\" = xEXPORTS; then + cp \$export_symbols \$output_objdir/\$soname.def; + else + echo EXPORTS > \$output_objdir/\$soname.def; + cat \$export_symbols >> \$output_objdir/\$soname.def; + fi~ + \$CC -shared \$output_objdir/\$soname.def \$libobjs \$deplibs \$compiler_flags -o \$output_objdir/\$soname \${wl}--image-base=0x10000000 \${wl}--out-implib,\$lib" +postinstall_cmds="base_file=\\\`basename \\\${file}\\\`~ + dlpath=\\\`\$SHELL 2>&1 -c '. \$dir/'\\\${base_file}'i;echo \\\$dlname'\\\`~ + dldir=\$destdir/\\\`dirname \\\$dlpath\\\`~ + test -d \\\$dldir || mkdir -p \\\$dldir~ + \$install_prog \$dir/\$dlname \\\$dldir/\$dlname" +postuninstall_cmds="dldll=\\\`\$SHELL 2>&1 -c '. \$file; echo \\\$dlname'\\\`~ + dlpath=\$dir/\\\$dldll~ + \$rm \\\$dlpath" + +# Commands used to build a loadable module (assumed same as above if empty) +module_cmds="" +module_expsym_cmds="" + +# Commands to strip libraries. +old_striplib="strip --strip-debug" +striplib="strip --strip-unneeded" + +# Dependencies to place before the objects being linked to create a +# shared library. +predep_objects="" + +# Dependencies to place after the objects being linked to create a +# shared library. +postdep_objects="" + +# Dependencies to place before the objects being linked to create a +# shared library. +predeps="" + +# Dependencies to place after the objects being linked to create a +# shared library. +postdeps="" + +# The library search path used internally by the compiler when linking +# a shared library. +compiler_lib_search_path="" + +# Method to check whether dependent libraries are shared objects. +deplibs_check_method="file_magic ^x86 archive import|^x86 DLL" + +# Command to use when deplibs_check_method == file_magic. +file_magic_cmd="win32_libid" + +# Flag that allows shared libraries with undefined symbols to be built. +allow_undefined_flag="supported" + +# Flag that forces no undefined symbols. +no_undefined_flag="" + +# Commands used to finish a libtool library installation in a directory. +finish_cmds="" + +# Same as above, but a single script fragment to be evaled but not shown. +finish_eval="" + +# Take the output of nm and produce a listing of raw symbols and C names. +global_symbol_pipe="sed -n -e 's/^.*[ ]\\([ABCDGIRSTW][ABCDGIRSTW]*\\)[ ][ ]*\\(_\\)\\([_A-Za-z][_A-Za-z0-9]*\\) \\{0,1\\}\$/\\1 \\2\\3 \\3/p'" + +# Transform the output of nm in a proper C declaration +global_symbol_to_cdecl="sed -n -e 's/^. .* \\(.*\\)\$/extern int \\1;/p'" + +# Transform the output of nm in a C name address pair +global_symbol_to_c_name_address="sed -n -e 's/^: \\([^ ]*\\) \$/ {\\\"\\1\\\", (lt_ptr) 0},/p' -e 's/^[BCDEGRST] \\([^ ]*\\) \\([^ ]*\\)\$/ {\"\\2\", (lt_ptr) \\&\\2},/p'" + +# This is the shared library runtime path variable. +runpath_var=LD_RUN_PATH + +# This is the shared library path variable. +shlibpath_var=PATH + +# Is shlibpath searched before the hard-coded library search path? +shlibpath_overrides_runpath=yes + +# How to hardcode a shared library path into an executable. +hardcode_action=immediate + +# Whether we should hardcode library paths into libraries. +hardcode_into_libs=no + +# Flag to hardcode $libdir into a binary during linking. +# This must work even if $libdir does not exist. +hardcode_libdir_flag_spec="\${wl}--rpath \${wl}\$libdir" + +# If ld is used when linking, flag to hardcode $libdir into +# a binary during linking. This must work even if $libdir does +# not exist. +hardcode_libdir_flag_spec_ld="" + +# Whether we need a single -rpath flag with a separated argument. +hardcode_libdir_separator="" + +# Set to yes if using DIR/libNAME during linking hardcodes DIR into the +# resulting binary. +hardcode_direct=no + +# Set to yes if using the -LDIR flag during linking hardcodes DIR into the +# resulting binary. +hardcode_minus_L=no + +# Set to yes if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into +# the resulting binary. +hardcode_shlibpath_var=unsupported + +# Set to yes if building a shared library automatically hardcodes DIR into the library +# and all subsequent libraries and executables linked against it. +hardcode_automatic=no + +# Variables whose values should be saved in libtool wrapper scripts and +# restored at relink time. +variables_saved_for_relink="PATH PATH LD_RUN_PATH GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" + +# Whether libtool must link a program against all its dependency libraries. +link_all_deplibs=unknown + +# Compile-time system search path for libraries +sys_lib_search_path_spec=" =c:/mnt/opt/mingw/bin/../lib/gcc-lib/mingw32/3.2.3/ c:/mnt/opt/mingw/bin/../lib/gcc-lib/ /mingw/lib/gcc-lib/mingw32/3.2.3/ /usr/lib/gcc/mingw32/3.2.3/ c:/mnt/opt/mingw/bin/../lib/gcc-lib/mingw32/3.2.3/../../../../mingw32/lib/mingw32/3.2.3/ c:/mnt/opt/mingw/bin/../lib/gcc-lib/mingw32/3.2.3/../../../../mingw32/lib/ /mingw/lib/gcc-lib/mingw32/3.2.3/../../../../mingw32/lib/mingw32/3.2.3/ /mingw/lib/gcc-lib/mingw32/3.2.3/../../../../mingw32/lib/ c:/mnt/opt/mingw/bin/../lib/gcc-lib/mingw32/3.2.3/../../../mingw32/3.2.3/ c:/mnt/opt/mingw/bin/../lib/gcc-lib/mingw32/3.2.3/../../../ /mingw/lib/gcc-lib/mingw32/3.2.3/../../../mingw32/3.2.3/ /mingw/lib/gcc-lib/mingw32/3.2.3/../../../ /lib/mingw32/3.2.3/ /lib/ /usr/lib/mingw32/3.2.3/ /usr/lib/" + +# Run-time system search path for libraries +sys_lib_dlsearch_path_spec="/lib /usr/lib" + +# Fix the shell variable $srcfile for the compiler. +fix_srcfile_path="" + +# Set to yes if exported symbols are required. +always_export_symbols=no + +# The commands to list exported symbols. +export_symbols_cmds="\$NM \$libobjs \$convenience | \$global_symbol_pipe | \$SED -e '/^[BCDGS] /s/.* \\\\([^ ]*\\\\)/\\\\1 DATA/' | \$SED -e '/^[AITW] /s/.* //' | sort | uniq > \$export_symbols" + +# The commands to extract the exported symbol list from a shared archive. +extract_expsyms_cmds="" + +# Symbols that should not be listed in the preloaded symbols. +exclude_expsyms="_GLOBAL_OFFSET_TABLE_" + +# Symbols that must always be exported. +include_expsyms="" + +# ### END LIBTOOL TAG CONFIG: F77 + +# Do not edit or modify anything in this comment block. +# The arch-tag line is a file identity tag for the GNU Arch +# revision control system. +# +# arch-tag: f284401a-dcf1-41ea-97ea-c51edee14a27 diff --git a/Win32/sndfile.h b/Win32/sndfile.h new file mode 100644 index 00000000..23d2d128 --- /dev/null +++ b/Win32/sndfile.h @@ -0,0 +1,539 @@ +/* +** Copyright (C) 1999-2006 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* +** sndfile.h -- system-wide definitions +** +** API documentation is in the doc/ directory of the source code tarball +** and at http://www.mega-nerd.com/libsndfile/api.html. +*/ + +#ifndef SNDFILE_H +#define SNDFILE_H + +/* This is the version 1.0.X header file. */ +#define SNDFILE_1 + +#include + +/* For the Metrowerks CodeWarrior Pro Compiler (mainly MacOS) */ + +#if (defined (__MWERKS__)) +#include +#else +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* The following file types can be read and written. +** A file type would consist of a major type (ie SF_FORMAT_WAV) bitwise +** ORed with a minor type (ie SF_FORMAT_PCM). SF_FORMAT_TYPEMASK and +** SF_FORMAT_SUBMASK can be used to separate the major and minor file +** types. +*/ + +enum +{ /* Major formats. */ + SF_FORMAT_WAV = 0x010000, /* Microsoft WAV format (little endian). */ + SF_FORMAT_AIFF = 0x020000, /* Apple/SGI AIFF format (big endian). */ + SF_FORMAT_AU = 0x030000, /* Sun/NeXT AU format (big endian). */ + SF_FORMAT_RAW = 0x040000, /* RAW PCM data. */ + SF_FORMAT_PAF = 0x050000, /* Ensoniq PARIS file format. */ + SF_FORMAT_SVX = 0x060000, /* Amiga IFF / SVX8 / SV16 format. */ + SF_FORMAT_NIST = 0x070000, /* Sphere NIST format. */ + SF_FORMAT_VOC = 0x080000, /* VOC files. */ + SF_FORMAT_IRCAM = 0x0A0000, /* Berkeley/IRCAM/CARL */ + SF_FORMAT_W64 = 0x0B0000, /* Sonic Foundry's 64 bit RIFF/WAV */ + SF_FORMAT_MAT4 = 0x0C0000, /* Matlab (tm) V4.2 / GNU Octave 2.0 */ + SF_FORMAT_MAT5 = 0x0D0000, /* Matlab (tm) V5.0 / GNU Octave 2.1 */ + SF_FORMAT_PVF = 0x0E0000, /* Portable Voice Format */ + SF_FORMAT_XI = 0x0F0000, /* Fasttracker 2 Extended Instrument */ + SF_FORMAT_HTK = 0x100000, /* HMM Tool Kit format */ + SF_FORMAT_SDS = 0x110000, /* Midi Sample Dump Standard */ + SF_FORMAT_AVR = 0x120000, /* Audio Visual Research */ + SF_FORMAT_WAVEX = 0x130000, /* MS WAVE with WAVEFORMATEX */ + SF_FORMAT_SD2 = 0x160000, /* Sound Designer 2 */ + SF_FORMAT_FLAC = 0x170000, /* FLAC lossless file format */ + SF_FORMAT_CAF = 0x180000, /* Core Audio File format */ + + /* Subtypes from here on. */ + + SF_FORMAT_PCM_S8 = 0x0001, /* Signed 8 bit data */ + SF_FORMAT_PCM_16 = 0x0002, /* Signed 16 bit data */ + SF_FORMAT_PCM_24 = 0x0003, /* Signed 24 bit data */ + SF_FORMAT_PCM_32 = 0x0004, /* Signed 32 bit data */ + + SF_FORMAT_PCM_U8 = 0x0005, /* Unsigned 8 bit data (WAV and RAW only) */ + + SF_FORMAT_FLOAT = 0x0006, /* 32 bit float data */ + SF_FORMAT_DOUBLE = 0x0007, /* 64 bit float data */ + + SF_FORMAT_ULAW = 0x0010, /* U-Law encoded. */ + SF_FORMAT_ALAW = 0x0011, /* A-Law encoded. */ + SF_FORMAT_IMA_ADPCM = 0x0012, /* IMA ADPCM. */ + SF_FORMAT_MS_ADPCM = 0x0013, /* Microsoft ADPCM. */ + + SF_FORMAT_GSM610 = 0x0020, /* GSM 6.10 encoding. */ + SF_FORMAT_VOX_ADPCM = 0x0021, /* OKI / Dialogix ADPCM */ + + SF_FORMAT_G721_32 = 0x0030, /* 32kbs G721 ADPCM encoding. */ + SF_FORMAT_G723_24 = 0x0031, /* 24kbs G723 ADPCM encoding. */ + SF_FORMAT_G723_40 = 0x0032, /* 40kbs G723 ADPCM encoding. */ + + SF_FORMAT_DWVW_12 = 0x0040, /* 12 bit Delta Width Variable Word encoding. */ + SF_FORMAT_DWVW_16 = 0x0041, /* 16 bit Delta Width Variable Word encoding. */ + SF_FORMAT_DWVW_24 = 0x0042, /* 24 bit Delta Width Variable Word encoding. */ + SF_FORMAT_DWVW_N = 0x0043, /* N bit Delta Width Variable Word encoding. */ + + SF_FORMAT_DPCM_8 = 0x0050, /* 8 bit differential PCM (XI only) */ + SF_FORMAT_DPCM_16 = 0x0051, /* 16 bit differential PCM (XI only) */ + + /* Endian-ness options. */ + + SF_ENDIAN_FILE = 0x00000000, /* Default file endian-ness. */ + SF_ENDIAN_LITTLE = 0x10000000, /* Force little endian-ness. */ + SF_ENDIAN_BIG = 0x20000000, /* Force big endian-ness. */ + SF_ENDIAN_CPU = 0x30000000, /* Force CPU endian-ness. */ + + SF_FORMAT_SUBMASK = 0x0000FFFF, + SF_FORMAT_TYPEMASK = 0x0FFF0000, + SF_FORMAT_ENDMASK = 0x30000000 +} ; + +/* +** The following are the valid command numbers for the sf_command() +** interface. The use of these commands is documented in the file +** command.html in the doc directory of the source code distribution. +*/ + +enum +{ SFC_GET_LIB_VERSION = 0x1000, + SFC_GET_LOG_INFO = 0x1001, + + SFC_GET_NORM_DOUBLE = 0x1010, + SFC_GET_NORM_FLOAT = 0x1011, + SFC_SET_NORM_DOUBLE = 0x1012, + SFC_SET_NORM_FLOAT = 0x1013, + SFC_SET_SCALE_FLOAT_INT_READ = 0x1014, + + SFC_GET_SIMPLE_FORMAT_COUNT = 0x1020, + SFC_GET_SIMPLE_FORMAT = 0x1021, + + SFC_GET_FORMAT_INFO = 0x1028, + + SFC_GET_FORMAT_MAJOR_COUNT = 0x1030, + SFC_GET_FORMAT_MAJOR = 0x1031, + SFC_GET_FORMAT_SUBTYPE_COUNT = 0x1032, + SFC_GET_FORMAT_SUBTYPE = 0x1033, + + SFC_CALC_SIGNAL_MAX = 0x1040, + SFC_CALC_NORM_SIGNAL_MAX = 0x1041, + SFC_CALC_MAX_ALL_CHANNELS = 0x1042, + SFC_CALC_NORM_MAX_ALL_CHANNELS = 0x1043, + + SFC_SET_ADD_PEAK_CHUNK = 0x1050, + + SFC_UPDATE_HEADER_NOW = 0x1060, + SFC_SET_UPDATE_HEADER_AUTO = 0x1061, + + SFC_FILE_TRUNCATE = 0x1080, + + SFC_SET_RAW_START_OFFSET = 0x1090, + + SFC_SET_DITHER_ON_WRITE = 0x10A0, + SFC_SET_DITHER_ON_READ = 0x10A1, + + SFC_GET_DITHER_INFO_COUNT = 0x10A2, + SFC_GET_DITHER_INFO = 0x10A3, + + SFC_GET_EMBED_FILE_INFO = 0x10B0, + + SFC_SET_CLIPPING = 0x10C0, + SFC_GET_CLIPPING = 0x10C1, + + SFC_GET_INSTRUMENT = 0x10D0, + SFC_SET_INSTRUMENT = 0x10D1, + + SFC_GET_LOOP_INFO = 0x10E0, + + /* Following commands for testing only. */ + SFC_TEST_IEEE_FLOAT_REPLACE = 0x6001, + + /* + ** SFC_SET_ADD_* values are deprecated and will disappear at some + ** time in the future. They are guaranteed to be here up to and + ** including version 1.0.8 to avoid breakage of existng software. + ** They currently do nothing and will continue to do nothing. + */ + SFC_SET_ADD_DITHER_ON_WRITE = 0x1070, + SFC_SET_ADD_DITHER_ON_READ = 0x1071 +} ; + + +/* +** String types that can be set and read from files. Not all file types +** support this and even the file types which support one, may not support +** all string types. +*/ + +enum +{ SF_STR_TITLE = 0x01, + SF_STR_COPYRIGHT = 0x02, + SF_STR_SOFTWARE = 0x03, + SF_STR_ARTIST = 0x04, + SF_STR_COMMENT = 0x05, + SF_STR_DATE = 0x06 +} ; + +/* +** Use the following as the start and end index when doing metadata +** transcoding. +*/ + +#define SF_STR_FIRST SF_STR_TITLE +#define SF_STR_LAST SF_STR_DATE + +enum +{ /* True and false */ + SF_FALSE = 0, + SF_TRUE = 1, + + /* Modes for opening files. */ + SFM_READ = 0x10, + SFM_WRITE = 0x20, + SFM_RDWR = 0x30 +} ; + +/* Public error values. These are guaranteed to remain unchanged for the duration +** of the library major version number. +** There are also a large number of private error numbers which are internal to +** the library which can change at any time. +*/ + +enum +{ SF_ERR_NO_ERROR = 0, + SF_ERR_UNRECOGNISED_FORMAT = 1, + SF_ERR_SYSTEM = 2, + SF_ERR_MALFORMED_FILE = 3, + SF_ERR_UNSUPPORTED_ENCODING = 4 +} ; + +/* A SNDFILE* pointer can be passed around much like stdio.h's FILE* pointer. */ + +typedef struct SNDFILE_tag SNDFILE ; + +/* The following typedef is system specific and is defined when libsndfile is. +** compiled. sf_count_t can be one of loff_t (Linux), off_t (*BSD), +** off64_t (Solaris), __int64_t (Win32) etc. +*/ + +typedef __int64 sf_count_t ; + +#define SF_COUNT_MAX 0x7FFFFFFFFFFFFFFFi64 + +/* A pointer to a SF_INFO structure is passed to sf_open_read () and filled in. +** On write, the SF_INFO structure is filled in by the user and passed into +** sf_open_write (). +*/ + +struct SF_INFO +{ sf_count_t frames ; /* Used to be called samples. Changed to avoid confusion. */ + int samplerate ; + int channels ; + int format ; + int sections ; + int seekable ; +} ; + +typedef struct SF_INFO SF_INFO ; + +/* The SF_FORMAT_INFO struct is used to retrieve information about the sound +** file formats libsndfile supports using the sf_command () interface. +** +** Using this interface will allow applications to support new file formats +** and encoding types when libsndfile is upgraded, without requiring +** re-compilation of the application. +** +** Please consult the libsndfile documentation (particularly the information +** on the sf_command () interface) for examples of its use. +*/ + +typedef struct +{ int format ; + const char *name ; + const char *extension ; +} SF_FORMAT_INFO ; + +/* +** Enums and typedefs for adding dither on read and write. +** See the html documentation for sf_command(), SFC_SET_DITHER_ON_WRITE +** and SFC_SET_DITHER_ON_READ. +*/ + +enum +{ SFD_DEFAULT_LEVEL = 0, + SFD_CUSTOM_LEVEL = 0x40000000, + + SFD_NO_DITHER = 500, + SFD_WHITE = 501, + SFD_TRIANGULAR_PDF = 502 +} ; + +typedef struct +{ int type ; + double level ; + const char *name ; +} SF_DITHER_INFO ; + +/* Struct used to retrieve information about a file embedded within a +** larger file. See SFC_GET_EMBED_FILE_INFO. +*/ + +typedef struct +{ sf_count_t offset ; + sf_count_t length ; +} SF_EMBED_FILE_INFO ; + +/* +** Structs used to retrieve music sample information from a file. +*/ + +enum +{ /* + ** The loop mode field in SF_INSTRUMENT will be one of the following. + */ + SF_LOOP_NONE = 800, + SF_LOOP_FORWARD, + SF_LOOP_BACKWARD, + SF_LOOP_ALTERNATING +} ; + +typedef struct +{ int gain ; + char basenote, detune ; + char velocity_lo, velocity_hi ; + char key_lo, key_hi ; + int loop_count ; + + struct + { int mode ; + unsigned int start ; + unsigned int end ; + unsigned int count ; + } loops [16] ; /* make variable in a sensible way */ +} SF_INSTRUMENT ; + + + +/* Struct used to retrieve loop information from a file.*/ +typedef struct +{ + short time_sig_num ; /* any positive integer > 0 */ + short time_sig_den ; /* any positive power of 2 > 0 */ + int loop_mode ; /* see SF_LOOP enum */ + + int num_beats ; /* this is NOT the amount of quarter notes !!!*/ + /* a full bar of 4/4 is 4 beats */ + /* a full bar of 7/8 is 7 beats */ + + float bpm ; /* suggestion, as it can be calculated using other fields:*/ + /* file's lenght, file's sampleRate and our time_sig_den*/ + /* -> bpms are always the amount of _quarter notes_ per minute */ + + int root_key ; /* MIDI note, or -1 for None */ + int future [6] ; +} SF_LOOP_INFO ; + +typedef sf_count_t (*sf_vio_get_filelen) (void *user_data) ; +typedef sf_count_t (*sf_vio_seek) (sf_count_t offset, int whence, void *user_data) ; +typedef sf_count_t (*sf_vio_read) (void *ptr, sf_count_t count, void *user_data) ; +typedef sf_count_t (*sf_vio_write) (const void *ptr, sf_count_t count, void *user_data) ; +typedef sf_count_t (*sf_vio_tell) (void *user_data) ; + +struct SF_VIRTUAL_IO +{ sf_vio_get_filelen get_filelen ; + sf_vio_seek seek ; + sf_vio_read read ; + sf_vio_write write ; + sf_vio_tell tell ; +} ; + +typedef struct SF_VIRTUAL_IO SF_VIRTUAL_IO ; + +/* Open the specified file for read, write or both. On error, this will +** return a NULL pointer. To find the error number, pass a NULL SNDFILE +** to sf_perror () or sf_error_str (). +** All calls to sf_open() should be matched with a call to sf_close(). +*/ + +SNDFILE* sf_open (const char *path, int mode, SF_INFO *sfinfo) ; + +/* Use the existing file descriptor to create a SNDFILE object. If close_desc +** is TRUE, the file descriptor will be closed when sf_close() is called. If +** it is FALSE, the descritor will not be closed. +** When passed a descriptor like this, the library will assume that the start +** of file header is at the current file offset. This allows sound files within +** larger container files to be read and/or written. +** On error, this will return a NULL pointer. To find the error number, pass a +** NULL SNDFILE to sf_perror () or sf_error_str (). +** All calls to sf_open_fd() should be matched with a call to sf_close(). + +*/ + +SNDFILE* sf_open_fd (int fd, int mode, SF_INFO *sfinfo, int close_desc) ; + +SNDFILE* sf_open_virtual (SF_VIRTUAL_IO *sfvirtual, int mode, SF_INFO *sfinfo, void *user_data) ; + +/* sf_error () returns a error number which can be translated to a text +** string using sf_error_number(). +*/ + +int sf_error (SNDFILE *sndfile) ; + +/* sf_strerror () returns to the caller a pointer to the current error message for +** the given SNDFILE. +*/ + +const char* sf_strerror (SNDFILE *sndfile) ; + +/* sf_error_number () allows the retrieval of the error string for each internal +** error number. +** +*/ + +const char* sf_error_number (int errnum) ; + +/* The following three error functions are deprecated but they will remain in the +** library for the forseeable future. The function sf_strerror() should be used +** in their place. +*/ + +int sf_perror (SNDFILE *sndfile) ; +int sf_error_str (SNDFILE *sndfile, char* str, size_t len) ; + + +/* Return TRUE if fields of the SF_INFO struct are a valid combination of values. */ + +int sf_command (SNDFILE *sndfile, int command, void *data, int datasize) ; + +/* Return TRUE if fields of the SF_INFO struct are a valid combination of values. */ + +int sf_format_check (const SF_INFO *info) ; + +/* Seek within the waveform data chunk of the SNDFILE. sf_seek () uses +** the same values for whence (SEEK_SET, SEEK_CUR and SEEK_END) as +** stdio.h function fseek (). +** An offset of zero with whence set to SEEK_SET will position the +** read / write pointer to the first data sample. +** On success sf_seek returns the current position in (multi-channel) +** samples from the start of the file. +** Please see the libsndfile documentation for moving the read pointer +** separately from the write pointer on files open in mode SFM_RDWR. +** On error all of these functions return -1. +*/ + +sf_count_t sf_seek (SNDFILE *sndfile, sf_count_t frames, int whence) ; + +/* Functions for retrieving and setting string data within sound files. +** Not all file types support this features; AIFF and WAV do. For both +** functions, the str_type parameter must be one of the SF_STR_* values +** defined above. +** On error, sf_set_string() returns non-zero while sf_get_string() +** returns NULL. +*/ + +int sf_set_string (SNDFILE *sndfile, int str_type, const char* str) ; + +const char* sf_get_string (SNDFILE *sndfile, int str_type) ; + +/* Functions for reading/writing the waveform data of a sound file. +*/ + +sf_count_t sf_read_raw (SNDFILE *sndfile, void *ptr, sf_count_t bytes) ; +sf_count_t sf_write_raw (SNDFILE *sndfile, const void *ptr, sf_count_t bytes) ; + +/* Functions for reading and writing the data chunk in terms of frames. +** The number of items actually read/written = frames * number of channels. +** sf_xxxx_raw read/writes the raw data bytes from/to the file +** sf_xxxx_short passes data in the native short format +** sf_xxxx_int passes data in the native int format +** sf_xxxx_float passes data in the native float format +** sf_xxxx_double passes data in the native double format +** All of these read/write function return number of frames read/written. +*/ + +sf_count_t sf_readf_short (SNDFILE *sndfile, short *ptr, sf_count_t frames) ; +sf_count_t sf_writef_short (SNDFILE *sndfile, const short *ptr, sf_count_t frames) ; + +sf_count_t sf_readf_int (SNDFILE *sndfile, int *ptr, sf_count_t frames) ; +sf_count_t sf_writef_int (SNDFILE *sndfile, const int *ptr, sf_count_t frames) ; + +sf_count_t sf_readf_float (SNDFILE *sndfile, float *ptr, sf_count_t frames) ; +sf_count_t sf_writef_float (SNDFILE *sndfile, const float *ptr, sf_count_t frames) ; + +sf_count_t sf_readf_double (SNDFILE *sndfile, double *ptr, sf_count_t frames) ; +sf_count_t sf_writef_double (SNDFILE *sndfile, const double *ptr, sf_count_t frames) ; + +/* Functions for reading and writing the data chunk in terms of items. +** Otherwise similar to above. +** All of these read/write function return number of items read/written. +*/ + +sf_count_t sf_read_short (SNDFILE *sndfile, short *ptr, sf_count_t items) ; +sf_count_t sf_write_short (SNDFILE *sndfile, const short *ptr, sf_count_t items) ; + +sf_count_t sf_read_int (SNDFILE *sndfile, int *ptr, sf_count_t items) ; +sf_count_t sf_write_int (SNDFILE *sndfile, const int *ptr, sf_count_t items) ; + +sf_count_t sf_read_float (SNDFILE *sndfile, float *ptr, sf_count_t items) ; +sf_count_t sf_write_float (SNDFILE *sndfile, const float *ptr, sf_count_t items) ; + +sf_count_t sf_read_double (SNDFILE *sndfile, double *ptr, sf_count_t items) ; +sf_count_t sf_write_double (SNDFILE *sndfile, const double *ptr, sf_count_t items) ; + +/* Close the SNDFILE and clean up all memory allocations associated with this +** file. +** Returns 0 on success, or an error number. +*/ + +int sf_close (SNDFILE *sndfile) ; + +/* If the file is opened SFM_WRITE or SFM_RDWR, call fsync() on the file +** to force the writing of data to disk. If the file is opened SFM_READ +** no action is taken. +*/ + +void sf_write_sync (SNDFILE *sndfile) ; + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* SNDFILE_H */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 906bb197-18f2-4f66-a395-b4722bab5114 +*/ + diff --git a/Win32/testprog.c b/Win32/testprog.c new file mode 100644 index 00000000..add30349 --- /dev/null +++ b/Win32/testprog.c @@ -0,0 +1,24 @@ +/* Simple test program to make sure that Win32 linking to libsndfile is +** working. +*/ + +#include + +#include "sndfile.h" + +int +main (void) +{ static char strbuffer [256] ; + sf_command (NULL, SFC_GET_LIB_VERSION, strbuffer, sizeof (strbuffer)) ; + puts (strbuffer) ; + return 0 ; +} + + +/* +** Do not edit or modify anything in this comment block. +** The following line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 31165fd8-9d91-4e5d-8b31-8efd42ef7645 +*/ diff --git a/acinclude.m4 b/acinclude.m4 new file mode 100644 index 00000000..33d91294 --- /dev/null +++ b/acinclude.m4 @@ -0,0 +1,569 @@ +dnl By default, many hosts won't let programs access large files; +dnl one must use special compiler options to get large-file access to work. +dnl For more details about this brain damage please see: +dnl http://www.sas.com/standards/large.file/x_open.20Mar96.html + +dnl Written by Paul Eggert . + +dnl Internal subroutine of AC_SYS_EXTRA_LARGEFILE. +dnl AC_SYS_EXTRA_LARGEFILE_FLAGS(FLAGSNAME) +AC_DEFUN([AC_SYS_EXTRA_LARGEFILE_FLAGS], + [AC_CACHE_CHECK([for $1 value to request large file support], + ac_cv_sys_largefile_$1, + [ac_cv_sys_largefile_$1=`($GETCONF LFS_$1) 2>/dev/null` || { + ac_cv_sys_largefile_$1=no + ifelse($1, CFLAGS, + [case "$host_os" in + # IRIX 6.2 and later require cc -n32. +changequote(, )dnl + irix6.[2-9]* | irix6.1[0-9]* | irix[7-9].* | irix[1-9][0-9]*) +changequote([, ])dnl + if test "$GCC" != yes; then + ac_cv_sys_largefile_CFLAGS=-n32 + fi + ac_save_CC="$CC" + CC="$CC $ac_cv_sys_largefile_CFLAGS" + AC_TRY_LINK(, , , ac_cv_sys_largefile_CFLAGS=no) + CC="$ac_save_CC" + esac]) + }])]) + +dnl Internal subroutine of AC_SYS_EXTRA_LARGEFILE. +dnl AC_SYS_EXTRA_LARGEFILE_SPACE_APPEND(VAR, VAL) +AC_DEFUN([AC_SYS_EXTRA_LARGEFILE_SPACE_APPEND], + [case $2 in + no) ;; + ?*) + case "[$]$1" in + '') $1=$2 ;; + *) $1=[$]$1' '$2 ;; + esac ;; + esac]) + +dnl Internal subroutine of AC_SYS_EXTRA_LARGEFILE. +dnl AC_SYS_EXTRA_LARGEFILE_MACRO_VALUE(C-MACRO, CACHE-VAR, COMMENT, CODE-TO-SET-DEFAULT) +AC_DEFUN([AC_SYS_EXTRA_LARGEFILE_MACRO_VALUE], + [AC_CACHE_CHECK([for $1], $2, + [$2=no +changequote(, )dnl + $4 + for ac_flag in $ac_cv_sys_largefile_CFLAGS no; do + case "$ac_flag" in + -D$1) + $2=1 ;; + -D$1=*) + $2=`expr " $ac_flag" : '[^=]*=\(.*\)'` ;; + esac + done +changequote([, ])dnl + ]) + if test "[$]$2" != no; then + AC_DEFINE_UNQUOTED([$1], [$]$2, [$3]) + fi]) + +AC_DEFUN([AC_SYS_EXTRA_LARGEFILE], + [AC_REQUIRE([AC_CANONICAL_HOST]) + AC_ARG_ENABLE(largefile, + [ --disable-largefile omit support for large files]) + if test "$enable_largefile" != no; then + AC_CHECK_TOOL(GETCONF, getconf) + AC_SYS_EXTRA_LARGEFILE_FLAGS(CFLAGS) + AC_SYS_EXTRA_LARGEFILE_FLAGS(LDFLAGS) + AC_SYS_EXTRA_LARGEFILE_FLAGS(LIBS) + + for ac_flag in $ac_cv_sys_largefile_CFLAGS no; do + case "$ac_flag" in + no) ;; + -D_FILE_OFFSET_BITS=*) ;; + -D_LARGEFILE_SOURCE | -D_LARGEFILE_SOURCE=*) ;; + -D_LARGE_FILES | -D_LARGE_FILES=*) ;; + -D?* | -I?*) + AC_SYS_EXTRA_LARGEFILE_SPACE_APPEND(CPPFLAGS, "$ac_flag") ;; + *) + AC_SYS_EXTRA_LARGEFILE_SPACE_APPEND(CFLAGS, "$ac_flag") ;; + esac + done + AC_SYS_EXTRA_LARGEFILE_SPACE_APPEND(LDFLAGS, "$ac_cv_sys_largefile_LDFLAGS") + AC_SYS_EXTRA_LARGEFILE_SPACE_APPEND(LIBS, "$ac_cv_sys_largefile_LIBS") + AC_SYS_EXTRA_LARGEFILE_MACRO_VALUE(_FILE_OFFSET_BITS, + ac_cv_sys_file_offset_bits, + [Number of bits in a file offset, on hosts where this is settable.]) + [case "$host_os" in + # HP-UX 10.20 and later + hpux10.[2-9][0-9]* | hpux1[1-9]* | hpux[2-9][0-9]*) + ac_cv_sys_file_offset_bits=64 ;; + esac] + AC_SYS_EXTRA_LARGEFILE_MACRO_VALUE(_LARGEFILE_SOURCE, + ac_cv_sys_largefile_source, + [Define to make fseeko etc. visible, on some hosts.], + [case "$host_os" in + # HP-UX 10.20 and later + hpux10.[2-9][0-9]* | hpux1[1-9]* | hpux[2-9][0-9]*) + ac_cv_sys_largefile_source=1 ;; + esac]) + AC_SYS_EXTRA_LARGEFILE_MACRO_VALUE(_LARGE_FILES, + ac_cv_sys_large_files, + [Define for large files, on AIX-style hosts.], + [case "$host_os" in + # AIX 4.2 and later + aix4.[2-9]* | aix4.1[0-9]* | aix[5-9].* | aix[1-9][0-9]*) + ac_cv_sys_large_files=1 ;; + esac]) + fi + ]) + + + + + + +dnl @synopsis AC_C_FIND_ENDIAN +dnl +dnl Determine endian-ness of target processor. +dnl @version 1.1 Mar 03 2002 +dnl @author Erik de Castro Lopo +dnl +dnl Majority written from scratch to replace the standard autoconf macro +dnl AC_C_BIGENDIAN. Only part remaining from the original it the invocation +dnl of the AC_TRY_RUN macro. +dnl +dnl Permission to use, copy, modify, distribute, and sell this file for any +dnl purpose is hereby granted without fee, provided that the above copyright +dnl and this permission notice appear in all copies. No representations are +dnl made about the suitability of this software for any purpose. It is +dnl provided "as is" without express or implied warranty. + +dnl Find endian-ness in the following way: +dnl 1) Look in . +dnl 2) If 1) fails, look in and . +dnl 3) If 1) and 2) fails and not cross compiling run a test program. +dnl 4) If 1) and 2) fails and cross compiling then guess based on target. + +AC_DEFUN([AC_C_FIND_ENDIAN], +[AC_CACHE_CHECK(processor byte ordering, + ac_cv_c_byte_order, + +# Initialize to unknown +ac_cv_c_byte_order=unknown + +if test x$ac_cv_header_endian_h = xyes ; then + + # First try which should set BYTE_ORDER. + + [AC_TRY_LINK([ + #include + #if BYTE_ORDER != LITTLE_ENDIAN + not big endian + #endif + ], return 0 ;, + ac_cv_c_byte_order=little + )] + + [AC_TRY_LINK([ + #include + #if BYTE_ORDER != BIG_ENDIAN + not big endian + #endif + ], return 0 ;, + ac_cv_c_byte_order=big + )] + + fi + +if test $ac_cv_c_byte_order = unknown ; then + + [AC_TRY_LINK([ + #include + #include + #if !BYTE_ORDER || !BIG_ENDIAN || !LITTLE_ENDIAN + bogus endian macros + #endif + ], return 0 ;, + + [AC_TRY_LINK([ + #include + #include + #if BYTE_ORDER != LITTLE_ENDIAN + not big endian + #endif + ], return 0 ;, + ac_cv_c_byte_order=little + )] + + [AC_TRY_LINK([ + #include + #include + #if BYTE_ORDER != LITTLE_ENDIAN + not big endian + #endif + ], return 0 ;, + ac_cv_c_byte_order=little + )] + + )] + + fi + +if test $ac_cv_c_byte_order = unknown ; then + if test $cross_compiling = yes ; then + # This is the last resort. Try to guess the target processor endian-ness + # by looking at the target CPU type. + [ + case "$target_cpu" in + alpha* | i?86* | mipsel* | ia64*) + ac_cv_c_big_endian=0 + ac_cv_c_little_endian=1 + ;; + + m68* | mips* | powerpc* | hppa* | sparc*) + ac_cv_c_big_endian=1 + ac_cv_c_little_endian=0 + ;; + + esac + ] + else + AC_TRY_RUN( + [[ + int main (void) + { /* Are we little or big endian? From Harbison&Steele. */ + union + { long l ; + char c [sizeof (long)] ; + } u ; + u.l = 1 ; + return (u.c [sizeof (long) - 1] == 1); + } + ]], , ac_cv_c_byte_order=big, + ac_cv_c_byte_order=unknown + ) + + AC_TRY_RUN( + [[int main (void) + { /* Are we little or big endian? From Harbison&Steele. */ + union + { long l ; + char c [sizeof (long)] ; + } u ; + u.l = 1 ; + return (u.c [0] == 1); + }]], , ac_cv_c_byte_order=little, + ac_cv_c_byte_order=unknown + ) + fi + fi + +) +] + +if test $ac_cv_c_byte_order = big ; then + ac_cv_c_big_endian=1 + ac_cv_c_little_endian=0 +elif test $ac_cv_c_byte_order = little ; then + ac_cv_c_big_endian=0 + ac_cv_c_little_endian=1 +else + ac_cv_c_big_endian=0 + ac_cv_c_little_endian=0 + + AC_MSG_WARN([[*****************************************************************]]) + AC_MSG_WARN([[*** Not able to determine endian-ness of target processor. ]]) + AC_MSG_WARN([[*** The constants CPU_IS_BIG_ENDIAN and CPU_IS_LITTLE_ENDIAN in ]]) + AC_MSG_WARN([[*** src/config.h may need to be hand editied. ]]) + AC_MSG_WARN([[*****************************************************************]]) + fi + +)# AC_C_FIND_ENDIAN + + + + + +dnl @synopsis AC_C99_FLEXIBLE_ARRAY +dnl +dnl Dose the compiler support the 1999 ISO C Standard "stuct hack". +dnl @version 1.1 Mar 15 2004 +dnl @author Erik de Castro Lopo +dnl +dnl Permission to use, copy, modify, distribute, and sell this file for any +dnl purpose is hereby granted without fee, provided that the above copyright +dnl and this permission notice appear in all copies. No representations are +dnl made about the suitability of this software for any purpose. It is +dnl provided "as is" without express or implied warranty. + +AC_DEFUN([AC_C99_FLEXIBLE_ARRAY], +[AC_CACHE_CHECK(C99 struct flexible array support, + ac_cv_c99_flexible_array, + +# Initialize to unknown +ac_cv_c99_flexible_array=no + +AC_TRY_LINK([[ + #include + typedef struct { + int k; + char buffer [] ; + } MY_STRUCT ; + ]], + [ MY_STRUCT *p = calloc (1, sizeof (MY_STRUCT) + 42); ], + ac_cv_c99_flexible_array=yes, + ac_cv_c99_flexible_array=no + ))] +) # AC_C99_FLEXIBLE_ARRAY + + + + + +dnl @synopsis AC_C99_FUNC_LRINT +dnl +dnl Check whether C99's lrint function is available. +dnl @version 1.3 Feb 12 2002 +dnl @author Erik de Castro Lopo +dnl +dnl Permission to use, copy, modify, distribute, and sell this file for any +dnl purpose is hereby granted without fee, provided that the above copyright +dnl and this permission notice appear in all copies. No representations are +dnl made about the suitability of this software for any purpose. It is +dnl provided "as is" without express or implied warranty. +dnl +AC_DEFUN([AC_C99_FUNC_LRINT], +[AC_CACHE_CHECK(for lrint, + ac_cv_c99_lrint, +[ +lrint_save_CFLAGS=$CFLAGS +CFLAGS="-lm" +AC_TRY_LINK([ +#define _ISOC9X_SOURCE 1 +#define _ISOC99_SOURCE 1 +#define __USE_ISOC99 1 +#define __USE_ISOC9X 1 + +#include +], if (!lrint(3.14159)) lrint(2.7183);, ac_cv_c99_lrint=yes, ac_cv_c99_lrint=no) + +CFLAGS=$lrint_save_CFLAGS + +]) + +if test "$ac_cv_c99_lrint" = yes; then + AC_DEFINE(HAVE_LRINT, 1, + [Define if you have C99's lrint function.]) +fi +])# AC_C99_FUNC_LRINT +dnl @synopsis AC_C99_FUNC_LRINTF +dnl +dnl Check whether C99's lrintf function is available. +dnl @version 1.3 Feb 12 2002 +dnl @author Erik de Castro Lopo +dnl +dnl Permission to use, copy, modify, distribute, and sell this file for any +dnl purpose is hereby granted without fee, provided that the above copyright +dnl and this permission notice appear in all copies. No representations are +dnl made about the suitability of this software for any purpose. It is +dnl provided "as is" without express or implied warranty. +dnl +AC_DEFUN([AC_C99_FUNC_LRINTF], +[AC_CACHE_CHECK(for lrintf, + ac_cv_c99_lrintf, +[ +AC_TRY_LINK([ +#define _ISOC9X_SOURCE 1 +#define _ISOC99_SOURCE 1 +#define __USE_ISOC99 1 +#define __USE_ISOC9X 1 + +#include +], if (!lrintf(3.14159)) lrintf(2.7183);, ac_cv_c99_lrintf=yes, ac_cv_c99_lrintf=no) +]) + +if test "$ac_cv_c99_lrintf" = yes; then + AC_DEFINE(HAVE_LRINTF, 1, + [Define if you have C99's lrintf function.]) +fi +])# AC_C99_FUNC_LRINTF + + + + +dnl @synopsis AC_C99_FUNC_LLRINT +dnl +dnl Check whether C99's llrint function is available. +dnl @version 1.1 Sep 30 2002 +dnl @author Erik de Castro Lopo +dnl +dnl Permission to use, copy, modify, distribute, and sell this file for any +dnl purpose is hereby granted without fee, provided that the above copyright +dnl and this permission notice appear in all copies. No representations are +dnl made about the suitability of this software for any purpose. It is +dnl provided "as is" without express or implied warranty. +dnl +AC_DEFUN([AC_C99_FUNC_LLRINT], +[AC_CACHE_CHECK(for llrint, + ac_cv_c99_llrint, +[ +AC_TRY_LINK([ +#define _ISOC9X_SOURCE 1 +#define _ISOC99_SOURCE 1 +#define __USE_ISOC99 1 +#define __USE_ISOC9X 1 + +#include +#include +], int64_t x ; x = llrint(3.14159) ;, ac_cv_c99_llrint=yes, ac_cv_c99_llrint=no) +]) + +if test "$ac_cv_c99_llrint" = yes; then + AC_DEFINE(HAVE_LLRINT, 1, + [Define if you have C99's llrint function.]) +fi +])# AC_C99_FUNC_LLRINT + + + +dnl @synopsis AC_C_CLIP_MODE +dnl +dnl Determine the clipping mode when converting float to int. +dnl @version 1.0 May 17 2003 +dnl @author Erik de Castro Lopo +dnl +dnl Permission to use, copy, modify, distribute, and sell this file for any +dnl purpose is hereby granted without fee, provided that the above copyright +dnl and this permission notice appear in all copies. No representations are +dnl made about the suitability of this software for any purpose. It is +dnl provided "as is" without express or implied warranty. + + + +dnl Find the clipping mode in the following way: +dnl 1) If we are not cross compiling test it. +dnl 2) IF we are cross compiling, assume that clipping isn't done correctly. + +AC_DEFUN([AC_C_CLIP_MODE], +[AC_CACHE_CHECK(processor clipping capabilities, + ac_cv_c_clip_type, + +# Initialize to unknown +ac_cv_c_clip_positive=unknown +ac_cv_c_clip_negative=unknown + +if test $ac_cv_c_clip_positive = unknown ; then + AC_TRY_RUN( + [[ + #define _ISOC9X_SOURCE 1 + #define _ISOC99_SOURCE 1 + #define __USE_ISOC99 1 + #define __USE_ISOC9X 1 + #include + int main (void) + { double fval ; + int k, ival ; + + fval = 1.0 * 0x7FFFFFFF ; + for (k = 0 ; k < 100 ; k++) + { ival = (lrint (fval)) >> 24 ; + if (ival != 127) + return 1 ; + + fval *= 1.2499999 ; + } ; + + return 0 ; + } + ]], + ac_cv_c_clip_positive=yes, + ac_cv_c_clip_positive=no, + ac_cv_c_clip_positive=unknown + ) + + AC_TRY_RUN( + [[ + #define _ISOC9X_SOURCE 1 + #define _ISOC99_SOURCE 1 + #define __USE_ISOC99 1 + #define __USE_ISOC9X 1 + #include + int main (void) + { double fval ; + int k, ival ; + + fval = -8.0 * 0x10000000 ; + for (k = 0 ; k < 100 ; k++) + { ival = (lrint (fval)) >> 24 ; + if (ival != -128) + return 1 ; + + fval *= 1.2499999 ; + } ; + + return 0 ; + } + ]], + ac_cv_c_clip_negative=yes, + ac_cv_c_clip_negative=no, + ac_cv_c_clip_negative=unknown + ) + fi + +if test $ac_cv_c_clip_positive = yes ; then + ac_cv_c_clip_positive=1 +else + ac_cv_c_clip_positive=0 + fi + +if test $ac_cv_c_clip_negative = yes ; then + ac_cv_c_clip_negative=1 +else + ac_cv_c_clip_negative=0 + fi + +[[ +case "$ac_cv_c_clip_positive$ac_cv_c_clip_negative" in + "00") + ac_cv_c_clip_type="none" + ;; + "10") + ac_cv_c_clip_type="positive" + ;; + "01") + ac_cv_c_clip_type="negative" + ;; + "11") + ac_cv_c_clip_type="both" + ;; + esac + ]] + +) +] + +)# AC_C_CLIP_MODE + + +dnl @synopsis AC_ADD_CFLAGS +dnl +dnl Add the given option to CFLAGS, if it doesn't break the compiler + +AC_DEFUN([AC_ADD_CFLAGS], +[AC_MSG_CHECKING([if $CC accepts $1]) + ac_add_cflags__old_cflags="$CFLAGS" + CFLAGS="$CFLAGS $1" + AC_TRY_LINK([#include ], + [printf("Hello, World!\n"); return 0;], + AC_MSG_RESULT([yes]), + AC_MSG_RESULT([no]) + CFLAGS="$ac_add_cflags__old_cflags") +]) + + + + +ifelse(dnl + + Do not edit or modify anything in this comment block. + The arch-tag line is a file identity tag for the GNU Arch + revision control system. + + arch-tag: bc38294d-bb5c-42ad-90b9-779def5eaab7 + +)dnl diff --git a/binheader_readf_check.py b/binheader_readf_check.py new file mode 100644 index 00000000..abbc7cc5 --- /dev/null +++ b/binheader_readf_check.py @@ -0,0 +1,69 @@ +#!/usr/bin/python + +import re, string, sys + +def trim_function_and_params (section): + k = string.find (section, "(") + 1 + brackets = 1 + section_len = len (section) + while k < section_len: + if section [k] == '(': + brackets += 1 + elif section [k] == ')': + brackets -= 1 + if brackets < 1: + return section [:k+1] + k += 1 + print "Whoops!!!!" + sys.exit (1) + +def get_function_calls (filedata): + filedata = string.split (filedata, "psf_binheader_readf") + filedata = filedata [1:] + + func_calls = [] + for section in filedata: + section = "psf_binheader_readf" + section + func_calls.append (trim_function_and_params (section)) + + return func_calls + +def search_for_problems (filename): + filedata = open (filename, "r").read () + + if len (filedata) < 1: + print "Error : file '%s' contains no data." % filename + sys.exit (1) + + count = 0 + + calls = get_function_calls (filedata) + for call in calls: + if string.find (call, "sizeof") > 0: + print "Found : ", call + count += 1 + + if count == 0: + print "%-20s : No problems found." % filename + else: + print "\n%-20s : Found %d errors." % (filename, count) + sys.exit (1) + return + + +#------------------------------------------------------------------------------- + +if len (sys.argv) < 2: + print "Usage : %s " % sys.argv [0] + sys.exit (1) + +for file in sys.argv [1:]: + search_for_problems (file) + + +# Do not edit or modify anything in this comment block. +# The arch-tag line is a file identity tag for the GNU Arch +# revision control system. +# +# arch-tag: 64d2d700-a787-4843-a342-2bafd0761645 + diff --git a/configure.ac b/configure.ac new file mode 100644 index 00000000..0f640b2b --- /dev/null +++ b/configure.ac @@ -0,0 +1,673 @@ +# Copyright (C) 1999-2006 Erik de Castro Lopo (erikd AT mega-nerd DOT com). + +dnl Require autoconf version +AC_PREREQ(2.57) + +AC_INIT([libsndfile],[1.0.18pre9],[erikd@mega-nerd.com]) +AC_CONFIG_SRCDIR([src/sndfile.c]) +AC_CANONICAL_TARGET([]) + +AM_INIT_AUTOMAKE($PACKAGE_NAME,$PACKAGE_VERSION) +AC_CONFIG_HEADERS([src/config.h]) + +AC_LANG([C]) + +#------------------------------------------------------------------------------------ +# Rules for library version information: +# +# 1. Start with version information of `0:0:0' for each libtool library. +# 2. Update the version information only immediately before a public release of +# your software. More frequent updates are unnecessary, and only guarantee +# that the current interface number gets larger faster. +# 3. If the library source code has changed at all since the last update, then +# increment revision (`c:r:a' becomes `c:r+1:a'). +# 4. If any interfaces have been added, removed, or changed since the last update, +# increment current, and set revision to 0. +# 5. If any interfaces have been added since the last public release, then increment +# age. +# 6. If any interfaces have been removed since the last public release, then set age +# to 0. + +SHARED_VERSION_INFO="1:18:0" + +AC_PROG_CC +AM_PROG_LIBTOOL + +AC_CHECK_PROG(autogen, autogen, yes, no) + +AC_PROG_INSTALL +AC_PROG_LN_S + +AC_HEADER_STDC + +AC_CHECK_HEADERS(endian.h) +AC_CHECK_HEADERS(byteswap.h) +AC_CHECK_HEADERS(locale.h) +AC_CHECK_HEADERS(inttypes.h) + +AC_HEADER_SYS_WAIT + +AC_CHECK_DECLS(S_IRGRP) +if test x$ac_cv_have_decl_S_IRGRP = xyes ; then + AC_DEFINE_UNQUOTED([HAVE_DECL_S_IRGRP],1,[Set to 1 if S_IRGRP is defined.]) +else + AC_DEFINE_UNQUOTED([HAVE_DECL_S_IRGRP],0) + fi + +#==================================================================================== +# Check for support of the struct hack. + +AC_C99_FLEXIBLE_ARRAY + +if test x$ac_cv_c99_flexible_array = xyes ; then + AC_DEFINE([HAVE_FLEXIBLE_ARRAY],1, [Set to 1 if the compile supports the struct hack.]) +else + AC_MSG_WARN([[*** This compiler does not support the 1999 ISO C Standard ***]]) + AC_MSG_WARN([[*** feature known as the flexible array struct member. ***]]) + AC_DEFINE([HAVE_FLEXIBLE_ARRAY],0) + fi + +#==================================================================================== +# Couple of initializations here. Fill in real values later. + +SHLIB_VERSION_ARG="" + +#==================================================================================== +# Finished checking, handle options. + +AC_ARG_ENABLE(experimental, + AC_HELP_STRING([--enable-experimental], [enable experimental code])) + +EXPERIMENTAL_CODE=0 +if test x$enable_experimental = xyes ; then + EXPERIMENTAL_CODE=1 + fi +AC_DEFINE_UNQUOTED([ENABLE_EXPERIMENTAL_CODE],${EXPERIMENTAL_CODE}, [Set to 1 to enable experimental code.]) + +AC_ARG_ENABLE(gcc-werror, + AC_HELP_STRING([--enable-gcc-werror], [enable -Werror in all Makefiles])) + +AC_ARG_ENABLE(gcc-pipe, + AC_HELP_STRING([--disable-gcc-pipe], [disable gcc -pipe option])) + +AC_ARG_ENABLE(gcc-opt, + AC_HELP_STRING([--disable-gcc-opt], [disable gcc optimisations])) + +AC_ARG_ENABLE(cpu-clip, + AC_HELP_STRING([--disable-cpu-clip], [disable tricky cpu specific clipper])) + +AC_ARG_ENABLE(bow-docs, + AC_HELP_STRING([--enable-bow-docs], [enable black-on-white html docs])) + +AC_ARG_ENABLE(sqlite, + AC_HELP_STRING([--disable-sqlite], [disable use of sqlite])) + +AC_ARG_ENABLE(flac, + AC_HELP_STRING([--disable-flac], [disable use of FLAC])) + +AC_ARG_ENABLE(alsa, + AC_HELP_STRING([--disable-alsa], [disable use of ALSA])) + +AC_ARG_ENABLE(test-coverage, + AC_HELP_STRING([--enable-test-coverage], [enable test coverage])) +AM_CONDITIONAL([ENABLE_TEST_COVERAGE], [test "$enable_test_coverage" = yes]) + +#==================================================================================== +# Check types and their sizes. + +AC_CHECK_SIZEOF(short,2) +AC_CHECK_SIZEOF(int,4) +AC_CHECK_SIZEOF(long,4) +AC_CHECK_SIZEOF(float,4) +AC_CHECK_SIZEOF(double,4) +AC_CHECK_SIZEOF(void*,8) +AC_CHECK_SIZEOF(size_t,4) +AC_CHECK_SIZEOF(int64_t,8) +AC_CHECK_SIZEOF(long long,8) + +#==================================================================================== +# Find an appropriate type for sf_count_t. +# On systems supporting files larger than 2 Gig, sf_count_t must be a 64 bit value. +# Unfortunately there is more than one way of ensuring this so need to do some +# pretty rigourous testing here. + +unset ac_cv_sizeof_off_t + +AC_CHECK_SIZEOF(off_t,1) # Fake default value. + +case "$host_os" in + mingw*) + TYPEOF_SF_COUNT_T="__int64" + SF_COUNT_MAX="0x7FFFFFFFFFFFFFFFLL" + SIZEOF_SF_COUNT_T=8 + ;; + *) + if test "x$ac_cv_sizeof_off_t" = "x8" ; then + # If sizeof (off_t) is 8, no further checking is needed. + TYPEOF_SF_COUNT_T="off_t" + SF_COUNT_MAX="0x7FFFFFFFFFFFFFFFLL" + SIZEOF_SF_COUNT_T=8 + else + # Check for common 64 bit file offset types. + AC_CHECK_SIZEOF(loff_t,1) # Fake default value. + AC_CHECK_SIZEOF(off64_t,1) # Fake default value. + + TYPEOF_SF_COUNT_T="unknown" + if test "x$ac_cv_sizeof_loff_t" = "x8" ; then + TYPEOF_SF_COUNT_T="loff_t" + SIZEOF_SF_COUNT_T=8 + elif test "x$ac_cv_sizeof_off64_t" = "x8" ; then + TYPEOF_SF_COUNT_T="off64_t" + SIZEOF_SF_COUNT_T=8 + fi + + # Save the old sizeof (off_t) value and then unset it to see if it + # changes when Large File Support is enabled. + + pre_largefile_sizeof_off_t=$ac_cv_sizeof_off_t + unset ac_cv_sizeof_off_t + + AC_SYS_EXTRA_LARGEFILE + + if test "x$ac_cv_sys_largefile_CFLAGS" = "xno" ; then + ac_cv_sys_largefile_CFLAGS="" + fi + if test "x$ac_cv_sys_largefile_LDFLAGS" = "xno" ; then + ac_cv_sys_largefile_LDFLAGS="" + fi + if test "x$ac_cv_sys_largefile_LIBS" = "xno" ; then + ac_cv_sys_largefile_LIBS="" + fi + + AC_CHECK_SIZEOF(off_t,1) # Fake default value. + + if test "x$ac_cv_sizeof_off_t" = "x8" ; then + SF_COUNT_MAX="0x7FFFFFFFFFFFFFFFLL" + elif test "x$ac_cv_sizeof_off_t" = "x$pre_largefile_sizeof_off_t" ; then + AC_MSG_WARN([[This machine does not seem to support 64 bit file offsets.]]) + TYPEOF_SF_COUNT_T="off_t" + SIZEOF_SF_COUNT_T=$ac_cv_sizeof_off_t + elif test "x$TYPEOF_SF_COUNT_T" = "xunknown" ; then + echo + echo "*** The configure process has determined that this system is capable" + echo "*** of Large File Support but has not been able to find a type which" + echo "*** is an unambiguous 64 bit file offset." + echo "*** Please contact the author to help resolve this problem." + echo + AC_MSG_ERROR([[Bad file offset type.]]) + fi + fi + ;; + esac + +if test $SIZEOF_SF_COUNT_T = 4 ; then + SF_COUNT_MAX="0x7FFFFFFF" + fi + +AC_DEFINE_UNQUOTED([TYPEOF_SF_COUNT_T],${TYPEOF_SF_COUNT_T}, [Set to long if unknown.]) +AC_SUBST(TYPEOF_SF_COUNT_T) + +AC_DEFINE_UNQUOTED([SIZEOF_SF_COUNT_T],${SIZEOF_SF_COUNT_T}, [Set to sizeof (long) if unknown.]) +AC_SUBST(SIZEOF_SF_COUNT_T) + +AC_DEFINE_UNQUOTED([SF_COUNT_MAX],${SF_COUNT_MAX}, [Set to maximum allowed value of sf_count_t type.]) +AC_SUBST(SF_COUNT_MAX) + +AC_CHECK_TYPES(ssize_t) +AC_CHECK_SIZEOF(ssize_t,4) + +#==================================================================================== +# Determine endian-ness of target processor. + +AC_C_FIND_ENDIAN + +AC_DEFINE_UNQUOTED(CPU_IS_BIG_ENDIAN, ${ac_cv_c_big_endian}, + [Target processor is big endian.]) +AC_DEFINE_UNQUOTED(CPU_IS_LITTLE_ENDIAN, ${ac_cv_c_little_endian}, + [Target processor is little endian.]) +AC_DEFINE_UNQUOTED(WORDS_BIGENDIAN, ${ac_cv_c_big_endian}, + [Target processor is big endian.]) + +#==================================================================================== +# Check for functions. + +AC_CHECK_FUNCS(malloc calloc realloc free) +AC_CHECK_FUNCS(open read write lseek pread pwrite) +AC_CHECK_FUNCS(fstat ftruncate fsync) +AC_CHECK_FUNCS(snprintf vsnprintf) +AC_CHECK_FUNCS(gmtime gmtime_r) +AC_CHECK_FUNCS(mmap getpagesize) +AC_CHECK_FUNCS(setlocale) + +AC_CHECK_LIB([m],floor) +AC_CHECK_FUNCS(floor ceil fmod) + +case "$host_os" in + cygwin*) + AC_MSG_WARN([[Not using built-in lrint() and lrintf() because they are broken on Cygwin.]]) + ;; + *) + AC_C99_FUNC_LRINT + AC_C99_FUNC_LRINTF + + if test "x$ac_cv_c99_lrint" = "xno" ; then + if test "x$ac_cv_c99_lrintf" = "xno" ; then + AC_MSG_WARN([[*** Missing C99 standard functions lrint() and lrintf().]]) + AC_MSG_WARN([[*** This may cause benign compiler warnings on some systems (ie Solaris).]]) + fi + fi + ;; + esac + +#==================================================================================== +# Check for libsqlite3 (only used in regtest). + +ac_cv_sqlite3=no +if test x$enable_sqlite != xno ; then + PKG_CHECK_MODULES(SQLITE3, sqlite3 >= 3.2, ac_cv_sqlite3=yes, ac_cv_sqlite3=no) + fi + +if test x$ac_cv_sqlite3 = "xyes" ; then + HAVE_SQLITE3=1 +else + HAVE_SQLITE3=0 + fi + +AC_DEFINE_UNQUOTED([HAVE_SQLITE3],$HAVE_SQLITE3,[Set to 1 if you have libsqlite3.]) + +#==================================================================================== +# Determine if the processor can do clipping on float to int conversions. + +if test x$enable_cpu_clip != "xno" ; then + AC_C_CLIP_MODE +else + echo "checking processor clipping capabilities... disabled" + ac_cv_c_clip_positive=0 + ac_cv_c_clip_negative=0 + fi + +AC_DEFINE_UNQUOTED(CPU_CLIPS_POSITIVE, ${ac_cv_c_clip_positive}, + [Target processor clips on positive float to int conversion.]) +AC_DEFINE_UNQUOTED(CPU_CLIPS_NEGATIVE, ${ac_cv_c_clip_negative}, + [Target processor clips on negative float to int conversion.]) + + +#==================================================================================== +# FLAC defines. + +AC_DEFINE(FLAC__HAS_OGG, 1, [This is always true.]) + +case "$host_cpu" in + i*86) + cpu_ia32=true + AC_DEFINE(FLAC__CPU_IA32) + AH_TEMPLATE(FLAC__CPU_IA32, [define if building for ia32/i386]) + ;; + powerpc) + cpu_ppc=true + AC_DEFINE(FLAC__CPU_PPC) + AH_TEMPLATE(FLAC__CPU_PPC, [define if building for PowerPC]) + ;; + sparc) + cpu_sparc=true + AC_DEFINE(FLAC__CPU_SPARC) + AH_TEMPLATE(FLAC__CPU_SPARC, [define if building for SPARC]) + ;; + esac + +AM_CONDITIONAL(FLaC__CPU_IA32, test "x$cpu_ia32" = xtrue) +AM_CONDITIONAL(FLaC__CPU_PPC, test "x$cpu_ppc" = xtrue) +AM_CONDITIONAL(FLaC__CPU_SPARC, test "x$cpu_sparc" = xtrue) + +case "$host" in + *-pc-linux-gnu) + sys_linux=true + AC_DEFINE(FLAC__SYS_LINUX) + AH_TEMPLATE(FLAC__SYS_LINUX, [define if building for Linux]) + ;; + *-*-darwin*) + sys_darwin=true + AC_DEFINE(FLAC__SYS_DARWIN) + AH_TEMPLATE(FLAC__SYS_DARWIN, [define if building for Darwin / MacOS X]) + ;; + esac + +AM_CONDITIONAL(FLaC__SYS_DARWIN, test "x$sys_darwin" = xtrue) +AM_CONDITIONAL(FLaC__SYS_LINUX, test "x$sys_linux" = xtrue) + +AC_ARG_ENABLE(asm-optimizations, AC_HELP_STRING([--disable-asm-optimizations], [Don't use any assembly optimization routines]), asm_opt=no, asm_opt=yes) +AM_CONDITIONAL(FLaC__NO_ASM, test "x$asm_opt" = xno) + +if test "x$asm_opt" = xno ; then + AC_DEFINE(FLAC__NO_ASM) + AH_TEMPLATE(FLAC__NO_ASM, [define to disable use of assembly code]) + fi + +AC_CHECK_PROGS(NASM, nasm) +AM_CONDITIONAL(FLaC__HAS_NASM, test -n "$NASM") + +if test -n "$NASM" ; then + AC_DEFINE(FLAC__HAS_NASM) + AH_TEMPLATE(FLAC__HAS_NASM, [define if you are compiling for x86 and have the NASM assembler]) + fi + +# Only matters for PowerPC +AC_CHECK_PROGS(AS, as, as) +AC_CHECK_PROGS(GAS, gas, gas) + +AM_CONDITIONAL(FLaC__HAS_AS, test "$AS" = "as") +AM_CONDITIONAL(FLaC__HAS_GAS, test "$AS" = "gas") + +if test "$AS" = "as" ; then + AC_DEFINE(FLAC__HAS_AS) + AH_TEMPLATE(FLAC__HAS_AS, [define if you are compiling for PowerPC and have the 'as' assembler]) + fi + +if test "$AS" = "gas" ; then + # funniest. macro. ever. + AC_DEFINE(FLAC__HAS_GAS) + AH_TEMPLATE(FLAC__HAS_GAS, [define if you are compiling for PowerPC and have the 'gas' assembler]) + fi + +case "$host" in + i386-*-openbsd3.[[0-3]]) OBJ_FORMAT=aoutb ;; + *-*-cygwin|*mingw*) OBJ_FORMAT=win32 ;; + *) OBJ_FORMAT=elf ;; + esac + +AC_SUBST(OBJ_FORMAT) + +#==================================================================================== +# Target OS specific stuff. + +OS_SPECIFIC_CFLAGS="" +OS_SPECIFIC_LINKS="" +os_is_win32=0 +os_is_macosx=0 +use_windows_api=0 + +case "$host_os" in + darwin* | rhapsody*) + os_is_macosx=1 + OS_SPECIFIC_CFLAGS="-fpascal-strings -I/Developer/Headers/FlatCarbon" + OS_SPECIFIC_LINKS="-framework CoreAudio" + ;; + mingw*) + os_is_win32=1 + use_windows_api=1 + OS_SPECIFIC_LINKS="-lwinmm" + ;; + cygwin*) + os_is_win32=1 + OS_SPECIFIC_LINKS="-lwinmm" + ;; + esac + +AC_DEFINE_UNQUOTED(OS_IS_WIN32, ${os_is_win32}, [Set to 1 if compiling for Win32]) +AC_DEFINE_UNQUOTED(OS_IS_MACOSX, ${os_is_macosx}, [Set to 1 if compiling for MacOSX]) +AC_DEFINE_UNQUOTED(USE_WINDOWS_API, ${use_windows_api}, [Set to 1 to use the native windows API]) + +#==================================================================================== +# Check for ALSA. + +ALSA_LIBS="" + +if test x$enable_alsa != xno ; then + AC_CHECK_HEADERS(alsa/asoundlib.h) + if test x$ac_cv_header_alsa_asoundlib_h = xyes ; then + ALSA_LIBS="-lasound" + fi + fi + +#==================================================================================== +# Test for sanity when cross-compiling. + +if test x$cross_compiling = xyes ; then + AC_MSG_WARN([[******************************************************************]]) + AC_MSG_WARN([[*** We are cross-compiling, so have to assume sizeof (short) == 2 ]]) + AC_MSG_WARN([[*** and sizeof (int) == 4. If this is not the case there is no ]]) + AC_MSG_WARN([[*** chance of this working. Please contact the mantainer. ]]) + AC_MSG_WARN([[******************************************************************]]) + fi + +if test $ac_cv_sizeof_short != 2 ; then + AC_MSG_WARN([[******************************************************************]]) + AC_MSG_WARN([[*** sizeof (short) != 2. ]]) + AC_MSG_WARN([[******************************************************************]]) + fi + +if test $ac_cv_sizeof_int != 4 ; then + AC_MSG_WARN([[******************************************************************]]) + AC_MSG_WARN([[*** sizeof (int) != 4 ]]) + AC_MSG_WARN([[******************************************************************]]) + fi + +if test $ac_cv_sizeof_float != 4 ; then + AC_MSG_WARN([[******************************************************************]]) + AC_MSG_WARN([[*** sizeof (float) != 4. ]]) + AC_MSG_WARN([[******************************************************************]]) + fi + +if test $ac_cv_sizeof_double != 8 ; then + AC_MSG_WARN([[******************************************************************]]) + AC_MSG_WARN([[*** sizeof (double) != 8. ]]) + AC_MSG_WARN([[******************************************************************]]) + fi + +if test x"$ac_cv_prog_autogen" = "xno" ; then + AC_MSG_WARN([[Touching files in directory tests/.]]) + touch tests/*.c tests/*.h + fi + +#==================================================================================== +# Settings for the HTML documentation. + +htmldocdir=$prefix/share/doc/libsndfile1-dev/html + +if test $prefix = "NONE" ; then + htmldocdir=/usr/local/share/doc/libsndfile1-dev/html +else + htmldocdir=$prefix/share/doc/libsndfile1-dev/html + fi + +if test x$enable_bow_docs = "xyes" ; then + HTML_BGCOLOUR="white" + HTML_FGCOLOUR="black" +else + HTML_BGCOLOUR="black" + HTML_FGCOLOUR="white" + fi + +#==================================================================================== +# Now use the information from the checking stage. + +win32_target_dll=0 + +if test x$ac_cv_c_compiler_gnu = xyes ; then + AC_ADD_CFLAGS(-std=gnu99) + + CFLAGS="$CFLAGS -W -Wall" + CXXFLAGS="$CXXFLAGS -W -Wall" + + AC_ADD_CFLAGS([-Wdeclaration-after-statement]) + AC_ADD_CFLAGS([-Wpointer-arith]) + + if test x$enable_gcc_werror = "xyes" ; then + CFLAGS="-Werror $CFLAGS" + CXXFLAGS="-Werror $CXXFLAGS" + fi + + if test x$enable_test_coverage = "xyes" ; then + # AC_ADD_CFLAGS([-ftest-coverage]) + AC_ADD_CFLAGS([-coverage]) + fi + + CFLAGS="$CFLAGS -Wstrict-prototypes -Wmissing-prototypes -Waggregate-return -Wcast-align -Wcast-qual -Wnested-externs -Wshadow -Wbad-function-cast -Wwrite-strings " + # -Wundef -Wmissing-declarations -Winline -Wconversion" + CXXFLAGS="$CXXFLAGS -Wcast-align -Wcast-qual -Wshadow -Wwrite-strings -Wctor-dtor-privacy -Wnon-virtual-dtor -Woverloaded-virtual -Wreorder -Wsign-promo " + + if test "x$enable_gcc_opt" = "xno" ; then + temp_CFLAGS=`echo $CFLAGS | sed "s/O2/O0/"` + CFLAGS=$temp_CFLAGS + AC_MSG_WARN([[*** Compiler optimisations switched off. ***]]) + fi + + # OS specific tweaks. + case "$host_os" in + darwin* | rhapsody*) + # Disable -Wall, -pedantic and -Wshadow for Apple Darwin/Rhapsody. + # System headers on these systems are broken. + temp_CFLAGS=`echo $CFLAGS | sed "s/-Wall -pedantic//" | sed "s/-Wshadow//" | sed "s/-Waggregate-return//"` + CFLAGS=$temp_CFLAGS + SHLIB_VERSION_ARG="-Wl,-exported_symbols_list -Wl,\$(srcdir)/Symbols.darwin" + ;; + linux*) + SHLIB_VERSION_ARG="-Wl,--version-script=\$(srcdir)/Symbols.linux" + ;; + mingw*) + SHLIB_VERSION_ARG="-Wl,\$(srcdir)/libsndfile.def" + win32_target_dll=1 + if test x"$enable_shared" = xno ; then + win32_target_dll=0 + fi + ;; + cygwin*) + SHLIB_VERSION_ARG="-Wl,\$(srcdir)/cygsndfile.def" + win32_target_dll=1 + if test x"$enable_shared" = xno ; then + win32_target_dll=0 + fi + ;; + *) + ;; + esac + if test x$enable_gcc_pipe != "xno" ; then + CFLAGS="$CFLAGS -pipe" + fi + + AC_DEFINE([COMPILER_IS_GCC],1, [Set to 1 if the compile is GNU GCC.]) + GCC_MAJOR_VERSION=`$CC -dumpversion | sed "s/\..*//"` + AC_DEFINE_UNQUOTED([GCC_MAJOR_VERSION],${GCC_MAJOR_VERSION}, [Major version of GCC or 3 otherwise.]) + fi + +AC_DEFINE_UNQUOTED(WIN32_TARGET_DLL, ${win32_target_dll}, [Set to 1 if windows DLL is being built.]) + +CFLAGS="$CFLAGS $OS_SPECIFIC_CFLAGS" + +if test x"$CFLAGS" = x ; then + echo "Error in configure script. CFLAGS has been screwed up." + exit + fi + +#------------------------------------------------------------------------------- + +AC_SUBST(htmldocdir) +AC_SUBST(HTML_BGCOLOUR) +AC_SUBST(HTML_FGCOLOUR) + +AC_SUBST(SHLIB_VERSION_ARG) +AC_SUBST(SHARED_VERSION_INFO) +AC_SUBST(OS_SPECIFIC_CFLAGS) +AC_SUBST(OS_SPECIFIC_LINKS) +AC_SUBST(ALSA_LIBS) +AC_SUBST(ENABLE_EXPERIMENTAL_CODE) + +AC_SUBST(COMPILER_IS_GCC) +AC_SUBST(GCC_MAJOR_VERSION) + +dnl The following line causes the libtool distributed with the source +dnl to be replaced if the build system has a more recent version. +AC_SUBST(LIBTOOL_DEPS) + +AC_CONFIG_FILES([ \ + src/sndfile.h src/Makefile src/GSM610/Makefile src/G72x/Makefile \ + \ + src/FLAC/Makefile src/FLAC/include/FLAC/Makefile src/FLAC/include/Makefile \ + src/FLAC/include/share/grabbag/Makefile src/FLAC/include/share/Makefile \ + src/FLAC/include/test_libs_common/Makefile src/FLAC/src/Makefile \ + src/FLAC/src/libFLAC/ia32/Makefile src/FLAC/src/libFLAC/Makefile \ + src/FLAC/src/libFLAC/include/private/Makefile \ + src/FLAC/src/libFLAC/include/Makefile \ + src/FLAC/src/libFLAC/include/protected/Makefile \ + src/FLAC/src/libFLAC/ppc/as/Makefile src/FLAC/src/libFLAC/ppc/Makefile \ + src/FLAC/src/libFLAC/ppc/gas/Makefile src/FLAC/src/share/Makefile \ + src/FLAC/src/share/getopt/Makefile src/FLAC/src/share/grabbag/Makefile \ + src/FLAC/src/share/replaygain_anal/Makefile \ + src/FLAC/src/share/replaygain_syn/include/private/Makefile \ + src/FLAC/src/share/replaygain_syn/include/Makefile \ + src/FLAC/src/share/replaygain_syn/Makefile \ + src/FLAC/src/share/utf8/Makefile src/FLAC/src/test_libFLAC/Makefile \ + src/FLAC/src/test_libs_common/Makefile src/FLAC/src/test_seeking/Makefile \ + src/FLAC/src/test_streams/Makefile src/FLAC/test/Makefile \ + src/FLAC/src/monkeys_audio_utilities/Makefile \ + src/FLAC/src/monkeys_audio_utilities/flac_mac/Makefile \ + src/FLAC/src/monkeys_audio_utilities/flac_ren/Makefile \ + src/FLAC/src/test_grabbag/Makefile src/FLAC/src/test_grabbag/cuesheet/Makefile \ + src/FLAC/src/test_grabbag/picture/Makefile \ + \ + src/OGG/include/ogg/Makefile src/OGG/include/Makefile src/OGG/Makefile \ + man/Makefile examples/Makefile tests/Makefile regtest/Makefile \ + doc/Makefile doc/libsndfile.css \ + Win32/Makefile Octave/Makefile \ + Makefile libsndfile.spec sndfile.pc \ + ]) +AC_OUTPUT + +#==================================================================================== + +AC_MSG_RESULT([ +-=-=-=-=-=-=-=-=-=-= Configuration Complete =-=-=-=-=-=-=-=-=-=- + + Configuration summary : + + Version : ..................... ${VERSION} + Experimental code : ........... ${enable_experimental:-no} +]) + +if test x$ac_cv_c_compiler_gnu = xyes ; then + echo -e " Tools :\n" + echo " Compiler is GCC : ............. ${ac_cv_c_compiler_gnu}" + echo " GCC major version : ........... ${GCC_MAJOR_VERSION}" + if test $GCC_MAJOR_VERSION -lt 3 ; then + echo -e "\n ** This compiler version allows applications to write" + echo " ** to static strings within the library." + echo " ** Compile with GCC version 3.X to avoid this problem." + fi + fi + +if test $libdir = "\${exec_prefix}/lib" ; then + libdir="$prefix/lib" + fi + +if test $bindir = "\${exec_prefix}/bin" ; then + bindir="$prefix/bin" + fi + +AC_MSG_RESULT([[ + Installation directories : + + Library directory : ........... $libdir + Program directory : ........... $bindir + Pkgconfig directory : ......... $libdir/pkgconfig + HTML docs directory : ......... $htmldocdir +]]) + +if test x$prefix != "x/usr" ; then + echo "Compiling some other packages against libsndfile may require" + echo -e "the addition of \"$libdir/pkgconfig\" to the" + echo -e "PKG_CONFIG_PATH environment variable.\n" + fi + +#================================================================================== +# Ugly hack to remove -Werror from some Makefiles. + +TMPFILE=/tmp/strchange$$ + +for f in "src/FLAC/src/libFLAC/Makefile" ; do + tmp=`grep -l "\-Werror" "$f"` + if test -n "$tmp" ; then + if sed -e "s!\-Werror!!g" "$f" > $TMPFILE ; then + cp -f $TMPFILE "$f" + fi + fi + done diff --git a/doc/FAQ.html b/doc/FAQ.html new file mode 100644 index 00000000..afa25ea3 --- /dev/null +++ b/doc/FAQ.html @@ -0,0 +1,720 @@ + + + + + + libsndfile : Frequently Asked Questions. + + + + + + + + + +

libsndfile : Frequently Asked Questions.

+

+Q1 : Do you plan to support XYZ codec in libsndfile?
+Q2 : In version 0 the SF_INFO struct had a pcmbitwidth field + but version 1 does not. Why?
+Q3 : Compiling is really slow on MacOSX. Why?
+Q4 : When trying to compile libsndfile on Solaris I get a "bad + substitution" error during linking. What can I do to fix this?
+Q5 : Why doesn't libsndfile do interleaving/de-interleaving?
+Q6 : What's the best format for storing temporary files?
+Q7 : On Linux/Unix/MacOSX, what's the best way of detecting the + presence of libsndfile?
+Q8 : But I just want a simple Makefile! What do I do?
+Q9 : How about adding the ability to write/read sound files to/from + memory buffers?
+Q10 : Reading a 16 bit PCM file as normalised floats and then + writing them back changes some sample values. Why?
+Q11 : I'm having problems with u-law encoded WAV files generated by + libsndfile in Winamp. Why?
+Q12 : I'm looking at sf_read*. What are items? What are frames?
+Q13 : Why can't libsndfile open this Sound Designer II (SD2) + file?
+Q14 : I'd like to statically link libsndfile to my closed source + application. Can I buy a license so that this is possible?
+Q15 : My program is crashing during a call to a function in libsndfile. + Is this a bug in libsndfile?
+Q16 : Will you accept a fix for compiling libsndfile with compiler X? +
+Q17 : Can libsndfile read/write files from/to UNIX pipes? +
+Q18 : Is it possible to build a Universal Binary on Mac OSX? +
+Q19 : I have project files for Visual Studio / XCode / Whatever. Why + don't you distribute them with libsndfile? +
+Q20 : Why doesn't libsndfile support MP3? Lots of other Open Source + projects support it! +
+


+ + + +


Q1 : Do you plan to support XYZ codec in libsnfile?

+

+If source code for XYZ codec is available under a suitable license (LGPL, BSD, +MIT etc) then yes, I'd like to add it. +

+

+If suitable documentation is available on how to decode and enocde the format +then maybe, depending on how much work is involved. +

+

+If XYZ is some proprietary codec where no source code or documentation is +available then no. +

+

+So if you want support for XYZ codec, first find existing source code or +documentation. +If you can't find either then the answer is no. +

+ + +


Q2 : In version 0 the SF_INFO struct had a pcmbitwidth field + but version 1 does not. Why?

+

+ This was dropped for a number of reasons: +

+
    +
  • pcmbitwidth makes little sense on compressed or floating point formats +
  • with the new API you really don't need to know it +
+

+As documented + here +there is now a well defined behavior which ensures that no matter what the +bit width of the source file, the scaling always does something sensible. +This makes it safe to read 8, 16, 24 and 32 bit PCM files using sf_read_short() +and always have the optimal behavior. +

+ + + +


Q3 : Compiling is really slow on MacOSX. Why?

+

+When you configure and compile libsndfile, it uses the /bin/sh shell for a number +of tasks (ie configure script and libtool). +Older versions of OSX (10.2?) shipped a a really crappy Bourne shell as /bin/sh +which resulted in really slow compiles. +New version of OSX ship GNU BASh as /bin/sh and this answer doesn't apply in that +case. +

+

+To fix this I suggest that you install the GNU Bash shell, rename /bin/sh to +/bin/sh.old and make a softlink from /bin/sh to the bash shell. +Bash is designed to behave as a Bourne shell when is is called as /bin/sh. +

+

+When I did this on my iBook running MacOSX, compile times dropped from 13 minutes +to 3 minutes. +

+ + + +


Q4 : When trying to compile libsndfile on Solaris I get a "bad + substitution" error on linking. Why?

+

+It seems that the Solaris Bourne shell disagrees with GNU libtool. +

+

+To fix this I suggest that you install the GNU Bash shell, rename /bin/sh to +/bin/sh.old and make a softlink from /bin/sh to the bash shell. +Bash is designed to behave as a Bourne shell when is is called as /bin/sh. +

+ + + +


Q5 : Why doesn't libsndfile do interleaving/de-interleaving?

+

+This problem is bigger than it may seem at first. +

+

+For a stereo file, it is a pretty safe bet that a simple interleaving/de-interleaving +could satisfy most users. +However, for files with more than 2 channels this is unlikely to be the case. +If the user has a 4 channel file and want to play that file on a stereo output +sound card they either want the first two channels or they want some mixed combination +of the 4 channels. +

+

+When you add more channels, the combinations grow exponentially and it becomes +increasingly difficult to cover even a sensible subset of the possible combinations. +On top of that, coding any one style of interleaver/de-interleaver is trivial, while +coding one that can cover all combinations is far from trivial. +This means that this feature will not be added any time soon. +

+ + + +


Q6 : What's the best format for storing temporary files?

+ +

+When you want to store temporary data there are a number of requirements; +

+
    +
  • A simple, easy to parse header. +
  • The format must provide the fastest possible read and write rates (ie + avoid conversions and encoding/decoding). +
  • The file format must be reasonably common and playable by most players. +
  • Able to store data in either endian-ness. +
+

+The format which best meets these requirements is AU, which allows data to be +stored in any one of short, int, float and double (among others) formats. +

+

+For instance, if an application uses float data internally, its temporary files +should use a format of (SF_ENDIAN_CPU | SF_FORMAT_AU | SF_FORMAT_FLOAT) which +will store big endian float data in big endian CPUs and little endian float data +on little endian CPUs. +Reading and writing this format will not require any conversions or byte swapping +regardless of the host CPU. +

+ + + + +


Q7 : On Linux/Unix/MaxOSX, what's the best way of detecting the presence + of libsndfile using autoconf?

+ +

+libsndfile uses the pkg-config (man pkg-config) method of registering itself with the +host system. +The best way of detecting its presence is using something like this in configure.ac +(or configure.in): +

+
+        PKG_CHECK_MODULES(SNDFILE, sndfile >= 1.0.2, ac_cv_sndfile=1, ac_cv_sndfile=0)
+
+        AC_DEFINE_UNQUOTED([HAVE_SNDFILE],${ac_cv_sndfile},
+			[Set to 1 if you have libsndfile.])
+
+        AC_SUBST(SNDFILE_CFLAGS)
+        AC_SUBST(SNDFILE_LIBS)
+
+

+This will automatically set the SNDFILE_CFLAGS and SNDFILE_LIBS +variables which can be used in Makefile.am like this: +

+
+        SNDFILE_CFLAGS = @SNDFILE_CFLAGS@
+        SNDFILE_LIBS = @SNDFILE_LIBS@
+
+

+If you install libsndfile from source, you will probably need to set the +PKG_CONFIG_PATH environment variable as suggested at the end of the +libsndfile configure process. For instance on my system I get this: +

+
+        -=-=-=-=-=-=-=-=-=-= Configuration Complete =-=-=-=-=-=-=-=-=-=-
+
+          Configuration summary :
+
+            Version : ..................... 1.0.5
+            Experimental code : ........... no
+
+          Tools :
+
+            Compiler is GCC : ............. yes
+            GCC major version : ........... 3
+
+          Installation directories :
+
+            Library directory : ........... /usr/local/lib
+            Program directory : ........... /usr/local/bin
+            Pkgconfig directory : ......... /usr/local/lib/pkgconfig
+
+        Compiling some other packages against libsndfile may require
+        the addition of "/usr/local/lib/pkgconfig" to the
+        PKG_CONFIG_PATH environment variable.
+
+ + + + +


Q8 : But I just want a simple Makefile! What do I do?

+ +

+The pkg-config program makes finding the correct compiler flag values and +library location far easier. +During the installation of libsndfile, a file named sndfile.pc is installed +in the directory ${libdir}/pkgconfig (ie if libsndfile is installed in +/usr/local/lib, sndfile.pc will be installed in +/usr/local/lib/pkgconfig/). +

+

+In order for pkg-config to find sndfile.pc it may be necessary to point the +environment variable PKG_CONFIG_PATH in the right direction. +

+
+        export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
+
+ +

+Then, to compile a C file into an object file, the command would be: +

+
+        gcc `pkg-config --cflags sndfile` -c somefile.c
+
+

+and to link a number of objects into an executable that links against libsndfile, +the command would be: +

+
+        gcc `pkg-config --libs sndfile` obj1.o obj2.o -o program
+
+ + + + +


Q9 : How about adding the ability to write/read sound files to/from + memory buffers?

+ +

+This has been added for version 1.0.13. +

+ + + + +


Q10 : Reading a 16 bit PCM file as normalised floats and then + writing them back changes some sample values. Why?

+ +

+This is caused by the fact that the conversion from 16 bit short to float is +done by dividing by 32768 (0x8000 in hexadecimal) while the conversion from +float to 16 bit short is done by multiplying by 32767 (0x7FFF in hex). +So for instance, a value in a 16 bit PCM file of 20000 gets read as a floating +point number of 0.6103515625 (20000.0 / 0x8000). +Converting that back to a 16 bit short results in a value of 19999.3896484375 +(0.6103515625 * 0x7FFF) which then gets rounded down to 19999. +

+

+You will notice that for this particular case, the error is 1 in 20000 or +0.005%. +Interestingly, for values of less than 16369, dividing by 0x8000 followed +by multiplying by 0x7FFF and then rounding the result, gives back the +original value. +It turns out that as long as the host operating system supplies the 1999 ISO +C Standard functions lrintf and lrint (or a replacement has +been supplied) then the maximum possible error is 1 in 16369 or about 0.006%. +

+

+Regardless of the size of the error, the reason why this is done is rather +subtle. +

+

+In a file containing 16 bit PCM samples, the values are restricted to the range +[-32768, 32767] while we want floating point values in the range [-1.0, 1.0]. +The only way to do this conversion is to do a floating point division by a value +of 0x8000. +Converting the other way, the only way to ensure that floating point values in +the range [-1.0, 1.0] are within the valid range allowed by a 16 bit short is +to multiply by 0x7FFF. +

+

+Some people would say that this is a severe short-coming of libsndfile. +I would counter that anybody who is constantly converting back and forth +between 16 bit shorts and normalised floats is going to suffer other losses +in audio quality that they should also be concerned about. +

+

+The correct way to deal with this problem is to consider 16 bit short data as +a final destination format only, not as an intermediate storage format. +All intermediate data (ie which is going to be processed further) should be +stored in floating point format which is supported by all of the most common +file formats. +If floating point files are considered too large (2 times the size of a 16 bit +PCM file), it would also be possible to use 24 bit PCM as an intermediate +storage format (and which is also supported by most common file types). +

+ + + + +


Q11 : I'm having problems with u-law encoded WAV files generated by + libsndfile in Winamp. Why? +

+ +

+This is actually a Winamp problem. +The official Microsoft spec suggests that the 'fmt ' chunk should be 18 bytes. +Unfortunately at least one of Microsoft's own applications (Sound Recorder on +Win98 I believe) did not accept 18 bytes 'fmt ' chunks. +

+

+Michael Lee did some experimenting and found that: +

+
+    I have checked that Windows Media Player 9, QuickTime Player 6.4,
+    RealOne Player 2.0 and GoldWave 5.06 can all play u-law files with
+    16-byte or 18-byte 'fmt ' chunk. Only Winamp (2.91) and foobar2000
+    are unable to play u-law files with 16-byte 'fmt ' chunk.
+
+ +

+Even this is a very small sampling of all the players out there. +For that reason it is probably not a good idea to change this now because there +is the risk of breaking something that currently works. +

+ + + + +


Q12 : I'm looking at sf_read*. What are items? What are frames? +

+ +

+For a sound file with only one channel, a frame is the same as a item. +

+

+For multi channel sound files, a single frame contains a single item for +each channel. +

+ + + + +


Q13 : Why can't libsndfile open this Sound Designer II (SD2) file? +

+ +

+This is somewhat complicated. +First some background. +

+ +

+SD2 files are native to the Apple Macintosh platform and use features of +the Mac filesystem (file resource forks) to store the file's sample rate, +number of channels, sample width and more. +When you look at a file and its resource fork on Mac OSX it looks like +this: +

+ +
+        -rw-r--r--  1 erikd erikd   46512 Oct 18 22:57 file.sd2
+        -rw-r--r--  1 erikd erikd     538 Oct 18 22:57 file.sd2/rsrc
+
+ +

+Notice how the file itself looks like a directory containing a single file +named rsrc. +When libsndfile is compiled for MacOSX, it should open (for write and read) +SD2 file with resource forks like this without any problems. +It will also handle files with the resource fork in a separate file as +described below. +

+ +

+When SD2 files are moved to other platforms, the resource fork of the file +can sometimes be dropped altogether. +All that remains is the raw audio data and no information about the number +of channels, sample rate or bit width which makes it a little difficult for +libsndfile to open the file. +

+ +

+However, it is possible to safely move an SD2 file to a Linux or Windows +machine. +For instance, when an SD2 file is copied from inside MacOSX to a windows +shared directory or a Samba share (ie Linux), MacOSX is clever enough to +store the resource fork of the file in a separate hidden file in the +same directory like this: +

+
+        -rw-r--r--  1 erikd erikd     538 Oct 18 22:57 ._file.sd2
+        -rw-r--r--  1 erikd erikd   46512 Oct 18 22:57 file.sd2
+
+ +

+Regardless of what platform it is running on, when libsndfile is asked to +open a file named "foo" and it can't recognize the file type from +the data in the file, it will attempt to open the resource fork and if +that fails, it then tries to open a file named "._foo" to see if +the file has a valid resource fork. +This is the same regardless of whether the file is being opened for read +or write. +

+ +

+In short, libsndfile should open SD2 files with a valid resource fork on +all of the platforms that libsndfile supports. +If a file has lost its resource fork, the only option is the open the file +using the SF_FORMAT_RAW option and guessing its sample rate, channel count +and bit width. +

+ +

+Occasionally, when SD2 files are moved to other systems, the file is + BinHexed +which wraps the resource fork and the data fork together. +For these files, it would be possible to write a BinHex parser but +there is not a lot to gain considering how rare these BinHexed SD2 +files are. +

+ + + +


Q14 : I'd like to statically link libsndfile to my closed source + application. Can I buy a license so that this is possible? +

+ +

+Unfortunately no. +libsndfile contains code written by other people who have agreed that their +code be used under the GNU LGPL but no more. +Even if they were to agree, there would be significant difficulties in +dividing up the payments fairly. +

+ +

+The only way you can legally use libsndfile as a statically linked +library is if your application is released under the GNU GPL or LGPL. +

+ + + +


Q15 : My program is crashing during a call to a function in libsndfile. + Is this a bug in libsndfile? +

+ +

+libsndfile is being used by large numbers of people all over the world +without any problems like this. That means that it is much more likely +that your code has a bug than libsndfile. However, it is still possible +that there is a bug in libsndfile. +

+

+To figure out whether it is your code or libsndfile you should do the +following: +

+
    +
  • Make sure you are compiling your code with warnings switched on and + that you fix as many warnings as possible. + With the GNU compiler (gcc) I would recommend at least + -W -Wall -Werror which will force you to fix all warnings + before you can run the code. +
  • Try using a memory debugger. + Valgrind on x86 Linux is excellent. + Purify also + has a good reputation. +
  • If the code is clean after the above two steps and you still get + a crash in libsndfile, then send me a small snippet of code (no + more than 30-40 lines) which includes the call to sf_open() and + also shows how all variables passed to/returned from sf_open() + are defined. +
+ + + +


Q16 : Will you accept a fix for compiling libsndfile with compiler X? +

+ +

+If compiler X is a C++ compiler then no. +C and C++ are different enough to make writing code that compiles as valid C +and valid C++ too difficult. +I would rather spend my time fixing bugs and adding features. +

+ +

+If compiler X is a C compiler then I will do what I can as long as that does +not hamper the correctness, portability and maintainability of the existing +code. +It should be noted however that libsndfile uses features specified by the 1999 +ISO C Standard. +This can make compiling libsndfile with some older compilers difficult. +

+ + + +


Q17 : Can libsndfile read/write files from/to UNIX pipes? +

+ +

+Yes, libsndfile can read files from pipes. +Unfortunately, the write case is much more complicated. +

+ +

+File formats like AIFF and WAV have information at the start of the file (the +file header) which states the length of the file, the number of sample frames +etc. +This information must be filled in correctly when the file header is written, +but this information is not reliably known until the file is closed. +This means that libsndfile cannot write AIFF, WAV and many other file types +to a pipe. +

+ +

+However, there is at least one file format (AU) which is specifically designed +to be written to a pipe. +Like AIFF and WAV, AU has a header with a sample frames field, but it is +specifically allowable to set that frames field to 0x7FFFFFFF if the file +length is not known when the header is written. +The AU file format can also hold data in many of the standard formats (ie +SF_FORMAT_PCM_16, SF_FORMAT_PCM_24, SF_FORMAT_FLOAT etc) as well as allowing +data in both big and little endian format. +

+ +

+See also FAQ Q6. +

+ + + +


Q18 : Is it possible to build a Universal Binary on Mac OSX? +

+ +

+Yes, but you must do two separate configure/build/test runs; one on PowerPC +and one on Intel. +It is then possible to merge the binaries into a single universal binary using +one of the programs in the Apple tool chain. +

+ +

+It is not possible to build a working universal binary via a single +compile/build run on a single CPU. +

+ +

+The problem is that the libsndfile build process detects features of the CPU its +being built for during the configure process and when building a universal binary, +configure is only run once and that data is then used for both CPUs. +That configure data will be wrong for one of those CPUs. +You will still be able to compile libsndfile, and the test suite will pass on +the machine you compiled it on. +However, if you take the universal binary test suite programs compiled on one +CPU and run them on the other, the test suite will fail. +

+ +

+Part of the problem is the the CPU endian-ness is detected at configure time. +Yes, I know the Apple compiler defines one of the macros __LITTLE_ENDIAN__ +and __BIG_ENDIAN__, but those macros are not part of the 1999 ISO C Standard +and they are not portable. +

+ +

+In addition, endian issues are not the only reason why the cross compiled +binary will fail. +The configure script also detects other CPU specific idiosyncrasies to provide +more optimized code. +

+ +

+Now, if you have read this far you're probably thinking there must be a way +to fix this and there probably is. +The problem is that its a hell of a lot of work and would require significant +changes to the configure process, the internal code and the test suite. +In addition, these changes must not break compilation on any of the platforms +libsndfile is currently working on. +

+ + + + +


Q19 : I have project files for Visual Studio / XCode / Whatever. Why + don't you distribute them with libsndfile? +

+ +

+There's a very good reason for this. +I will only distribute things that I actually have an ability to test and +maintain. +Project files for a bunch of different compilers and Integrated Development +Environments are simply too difficult to maintain. +

+ +

+The problem is that every time I add a new file to libsndfile or rename an +existing file I would have to modify all the project files and then test that +libsndfile still built with all the different compilers. +

+ +

+Maintaining these project files is also rather difficult if I don't have access +to the required compiler/IDE. +If I just edit the project files without testing them I will almost certainly +get it wrong. +If I release a version of libsndfile with broken project files, I'll get a bunch +of emails from people complaining about it not building and have no way of +fixing or even testing it. +

+ +

+I currently release sources that I personally test on Win32, Linux and +MacOSX (PowerPC) using the compiler I trust (GNU GCC). +Supporting one compiler on three (actually much more because GCC is available +almost everywhere) platforms is doable without too much pain. +I also release binaries for Win32 with instructions on how to use those +binaries with Visual Studio. +As a guy who is mainly interested in Linux, I'm not to keen to jump through +a bunch of hoops to support compilers and operating systems I don't use. +

+ +

+So, I hear you want to volunteer to maintain the project files for Some Crappy +Compiler 2007? +Well sorry, that won't work either. +I have had numerous people over the years offer to maintaining the project +files for Microsoft's Visual Studio. +Every single time that happened, they maintained it for a release or two and +then disappeared off the face of the earth. +Hence, I'm not willing to enter into an arrangement like that again. +

+ + + +


Q20 : Why doesn't libsndfile support MP3? Lots of other Open Source + projects support it! +

+ +

+MP3 is not supported for one very good reason; doing so requires the payment +of licensing fees. +As can be seen from + + mp3licensing.com +the required royalty payments are not cheap. +

+ +

+Yes, I know other libraries ignore the licensing requirements, but their legal +status is extremely dubious. +At any time, the body selling the licenses could go after the authors of those +libraries. +Some of those authors may be students and hence wouldn't be worth pursuing. +

+ +

+However, libsndfile is released under the name of a company, Mega Nerd Pty Ltd; +a company which has income from from libsamplerate licensing, libsndfile based +consulting income and other unrelated consulting income. +Adding MP3 support to libsndfile could place that income would be under legal +threat. +

+ +

+Fortunately, Ogg Vorbis exists as an alternative to MP3. +Support for Ogg Vorbis in libsndfile is currently been worked on. +

+ + +
+

+ The libsndfile home page is here : + + http://www.mega-nerd.com/libsndfile/. +
+Version : 1.0.17 +

+ + + diff --git a/doc/Makefile.am b/doc/Makefile.am new file mode 100644 index 00000000..71ae9d5d --- /dev/null +++ b/doc/Makefile.am @@ -0,0 +1,16 @@ +## Process this file with automake to produce Makefile.in + +htmldir = $(htmldocdir) +html_DATA = index.html libsndfile.jpg libsndfile.css api.html command.html \ + bugs.html sndfile_info.html new_file_type.HOWTO pkgconfig.html \ + win32.html FAQ.html lists.html embedded_files.html octave.html \ + dither.html + +EXTRA_DIST = $(html_DATA) + +## Do not edit or modify anything in this comment block. +## The arch-tag line is a file identity tag for the GNU Arch +## revision control system. +## +## arch-tag: 2f7e97fe-5ef8-49a1-ae12-14ebab69c048 + diff --git a/doc/api.html b/doc/api.html new file mode 100644 index 00000000..340989fe --- /dev/null +++ b/doc/api.html @@ -0,0 +1,703 @@ + + + + + + The libsndfile API. + + + + + + + + + +
+

libsndfile

+

+ Libsndfile is a library designed to allow the reading and writing of many + different sampled sound file formats (such as MS Windows WAV and the Apple/SGI + AIFF format) through one standard library interface. +

+ +

+ During read and write operations, formats are seamlessly converted between the + format the application program has requested or supplied and the file's data + format. The application programmer can remain blissfully unaware of issues + such as file endian-ness and data format. See Note 1 and + Note 2. +

+ +

+ Every effort is made to keep these documents up-to-date, error free and + unambiguous. + However, since maintaining the documentation is the least fun part of working + on libsndfile, these docs can and do fall behind the behaviour of library. + If any errors omissions or ambiguities are found, please notify + + Erik de Castro Lopo. +

+ +

+ Finally, if you think there is some feature missing from libsndfile, check that + it isn't already implemented (and documented) + here. + +

+ +

SYNOPSIS

+

+The functions of libsndfile are defined as follows: +

+ +
+      #include <stdio.h>
+      #include <sndfile.h>
+
+      SNDFILE*    sf_open          (const char *path, int mode, SF_INFO *sfinfo) ;
+      SNDFILE*    sf_open_fd       (int fd, int mode, SF_INFO *sfinfo, int close_desc) ;
+
+      int         sf_format_check  (const SF_INFO *info) ;
+
+      sf_count_t  sf_seek          (SNDFILE *sndfile, sf_count_t frames, int whence) ;
+
+      int         sf_command       (SNDFILE *sndfile, int cmd, void *data, int datasize) ;
+
+      int         sf_error         (SNDFILE *sndfile) ;
+      const char* sf_strerror      (SNDFILE *sndfile) ;
+      const char* sf_error_number  (int errnum) ;
+
+      int         sf_perror        (SNDFILE *sndfile) ;
+      int         sf_error_str     (SNDFILE *sndfile, char* str, size_t len) ;
+
+      int         sf_close         (SNDFILE *sndfile) ;
+      void        sf_write_sync    (SNDFILE *sndfile) ;
+
+      sf_count_t  sf_read_short    (SNDFILE *sndfile, short *ptr, sf_count_t items) ;
+      sf_count_t  sf_read_int      (SNDFILE *sndfile, int *ptr, sf_count_t items) ;
+      sf_count_t  sf_read_float    (SNDFILE *sndfile, float *ptr, sf_count_t items) ;
+      sf_count_t  sf_read_double   (SNDFILE *sndfile, double *ptr, sf_count_t items) ;
+
+      sf_count_t  sf_readf_short   (SNDFILE *sndfile, short *ptr, sf_count_t frames) ;
+      sf_count_t  sf_readf_int     (SNDFILE *sndfile, int *ptr, sf_count_t frames) ;
+      sf_count_t  sf_readf_float   (SNDFILE *sndfile, float *ptr, sf_count_t frames) ;
+      sf_count_t  sf_readf_double  (SNDFILE *sndfile, double *ptr, sf_count_t frames) ;
+
+      sf_count_t  sf_write_short   (SNDFILE *sndfile, short *ptr, sf_count_t items) ;
+      sf_count_t  sf_write_int     (SNDFILE *sndfile, int *ptr, sf_count_t items) ;
+      sf_count_t  sf_write_float   (SNDFILE *sndfile, float *ptr, sf_count_t items) ;
+      sf_count_t  sf_write_double  (SNDFILE *sndfile, double *ptr, sf_count_t items) ;
+
+      sf_count_t  sf_writef_short  (SNDFILE *sndfile, short *ptr, sf_count_t frames) ;
+      sf_count_t  sf_writef_int    (SNDFILE *sndfile, int *ptr, sf_count_t frames) ;
+      sf_count_t  sf_writef_float  (SNDFILE *sndfile, float *ptr, sf_count_t frames) ;
+      sf_count_t  sf_writef_double (SNDFILE *sndfile, double *ptr, sf_count_t frames) ;
+
+      sf_count_t  sf_read_raw      (SNDFILE *sndfile, void *ptr, sf_count_t bytes) ;
+      sf_count_t  sf_write_raw     (SNDFILE *sndfile, void *ptr, sf_count_t bytes) ;
+
+      const char* sf_get_string    (SNDFILE *sndfile, int str_type) ;
+      int         sf_set_string    (SNDFILE *sndfile, int str_type, const char* str) ;
+
+
+ +

+SNDFILE* is an anonymous pointer to data which is private to the library. +

+ + + +

File Open Function

+ +
+      SNDFILE*  sf_open    (const char *path, int mode, SF_INFO *sfinfo) ;
+
+ +

+The SF_INFO structure is for passing data between the calling function and the library +when opening a file for reading or writing. It is defined in sndfile.h as follows: +

+ +
+      typedef struct
+      {    sf_count_t  frames ;     /* Used to be called samples. */
+           int         samplerate ;
+           int         channels ;
+           int         format ;
+           int         sections ;
+           int         seekable ;
+       } SF_INFO ;
+
+ +

+The mode parameter for this function can be any one of the following three values: +

+ +
+      SFM_READ    - read only mode
+      SFM_WRITE   - write only mode
+      SFM_RDWR    - read/write mode
+
+ +

+When opening a file for read, the format field should be set to zero before +calling sf_open(). +The only exception to this is the case of RAW files where the caller has to set +the samplerate, channels and format fields to valid values. +All other fields of the structure are filled in by the library. +

+ +

+When opening a file for write, the caller must fill in structure members samplerate, +channels, and format. +

+ +

+The format field in the above SF_INFO structure is made up of the bit-wise OR of a +major format type (values between 0x10000 and 0x08000000), a minor format type +(with values less than 0x10000) and an optional endian-ness value. +The currently understood formats are listed in sndfile.h as follows and also include +bitmasks for separating major and minor file types. +Not all combinations of endian-ness and major and minor file types are valid. +

+ +
+      enum
+      {   /* Major formats. */
+          SF_FORMAT_WAV          = 0x010000,     /* Microsoft WAV format (little endian). */
+          SF_FORMAT_AIFF         = 0x020000,     /* Apple/SGI AIFF format (big endian). */
+          SF_FORMAT_AU           = 0x030000,     /* Sun/NeXT AU format (big endian). */
+          SF_FORMAT_RAW          = 0x040000,     /* RAW PCM data. */
+          SF_FORMAT_PAF          = 0x050000,     /* Ensoniq PARIS file format. */
+          SF_FORMAT_SVX          = 0x060000,     /* Amiga IFF / SVX8 / SV16 format. */
+          SF_FORMAT_NIST         = 0x070000,     /* Sphere NIST format. */
+          SF_FORMAT_VOC          = 0x080000,     /* VOC files. */
+          SF_FORMAT_IRCAM        = 0x0A0000,     /* Berkeley/IRCAM/CARL */
+          SF_FORMAT_W64          = 0x0B0000,     /* Sonic Foundry's 64 bit RIFF/WAV */
+          SF_FORMAT_MAT4         = 0x0C0000,     /* Matlab (tm) V4.2 / GNU Octave 2.0 */
+          SF_FORMAT_MAT5         = 0x0D0000,     /* Matlab (tm) V5.0 / GNU Octave 2.1 */
+          SF_FORMAT_PVF          = 0x0E0000,     /* Portable Voice Format */
+          SF_FORMAT_XI           = 0x0F0000,     /* Fasttracker 2 Extended Instrument */
+          SF_FORMAT_HTK          = 0x100000,     /* HMM Tool Kit format */
+          SF_FORMAT_SDS          = 0x110000,     /* Midi Sample Dump Standard */
+          SF_FORMAT_AVR          = 0x120000,     /* Audio Visual Research */
+          SF_FORMAT_WAVEX        = 0x130000,     /* MS WAVE with WAVEFORMATEX */
+          SF_FORMAT_SD2          = 0x160000,     /* Sound Designer 2 */
+          SF_FORMAT_FLAC         = 0x170000,     /* FLAC lossless file format */
+          SF_FORMAT_CAF          = 0x180000,     /* Core Audio File format */
+
+          /* Subtypes from here on. */
+
+          SF_FORMAT_PCM_S8       = 0x0001,       /* Signed 8 bit data */
+          SF_FORMAT_PCM_16       = 0x0002,       /* Signed 16 bit data */
+          SF_FORMAT_PCM_24       = 0x0003,       /* Signed 24 bit data */
+          SF_FORMAT_PCM_32       = 0x0004,       /* Signed 32 bit data */
+
+          SF_FORMAT_PCM_U8       = 0x0005,       /* Unsigned 8 bit data (WAV and RAW only) */
+
+          SF_FORMAT_FLOAT        = 0x0006,       /* 32 bit float data */
+          SF_FORMAT_DOUBLE       = 0x0007,       /* 64 bit float data */
+
+          SF_FORMAT_ULAW         = 0x0010,       /* U-Law encoded. */
+          SF_FORMAT_ALAW         = 0x0011,       /* A-Law encoded. */
+          SF_FORMAT_IMA_ADPCM    = 0x0012,       /* IMA ADPCM. */
+          SF_FORMAT_MS_ADPCM     = 0x0013,       /* Microsoft ADPCM. */
+
+          SF_FORMAT_GSM610       = 0x0020,       /* GSM 6.10 encoding. */
+          SF_FORMAT_VOX_ADPCM    = 0x0021,       /* Oki Dialogic ADPCM encoding. */
+
+          SF_FORMAT_G721_32      = 0x0030,       /* 32kbs G721 ADPCM encoding. */
+          SF_FORMAT_G723_24      = 0x0031,       /* 24kbs G723 ADPCM encoding. */
+          SF_FORMAT_G723_40      = 0x0032,       /* 40kbs G723 ADPCM encoding. */
+
+          SF_FORMAT_DWVW_12      = 0x0040,       /* 12 bit Delta Width Variable Word encoding. */
+          SF_FORMAT_DWVW_16      = 0x0041,       /* 16 bit Delta Width Variable Word encoding. */
+          SF_FORMAT_DWVW_24      = 0x0042,       /* 24 bit Delta Width Variable Word encoding. */
+          SF_FORMAT_DWVW_N       = 0x0043,       /* N bit Delta Width Variable Word encoding. */
+
+          SF_FORMAT_DPCM_8       = 0x0050,       /* 8 bit differential PCM (XI only) */
+          SF_FORMAT_DPCM_16      = 0x0051,       /* 16 bit differential PCM (XI only) */
+
+          /* Endian-ness options. */
+
+          SF_ENDIAN_FILE         = 0x00000000,   /* Default file endian-ness. */
+          SF_ENDIAN_LITTLE       = 0x10000000,   /* Force little endian-ness. */
+          SF_ENDIAN_BIG          = 0x20000000,   /* Force big endian-ness. */
+          SF_ENDIAN_CPU          = 0x30000000,   /* Force CPU endian-ness. */
+
+          SF_FORMAT_SUBMASK      = 0x0000FFFF,
+          SF_FORMAT_TYPEMASK     = 0x0FFF0000,
+          SF_FORMAT_ENDMASK      = 0x30000000
+      } ;
+
+ +

+Every call to sf_open() should be matched with a call to sf_close() to free up +memory allocated during the call to sf_open(). +

+ +

+On success, the sf_open function returns a non NULL pointer which should be +passed as the first parameter to all subsequent libsndfile calls dealing with +that audio file. +On fail, the sf_open function returns a NULL pointer. +

+ + +

File Descriptor Open

+ +
+      SNDFILE*  sf_open_fd (int fd, int mode, SF_INFO *sfinfo, int close_desc) ;
+
+ +

+The second open function takes a file descriptor of a file that has already been +opened. +Care should be taken to ensure that the mode of the file represented by the +descriptor matches the mode argument. +This function is useful in the following circumstances: +

+ +
    +
  • Opening temporary files securely (ie use the tmpfile() to return a + FILE* pointer and then using fileno() to retrieve the file descriptor + which is then passed to libsndfile). +
  • Opening files with file names using OS specific character encodings + and then passing the file descriptor to sf_open_fd(). +
  • Opening sound files embedded within larger files. + More info. +
+ +

+Every call to sf_open_fd() should be matched with a call to sf_close() to free up +memory allocated during the call to sf_open(). +

+ +

+When sf_close() is called, the file descriptor is only closed if the close_desc +parameter was TRUE when the sf_open_fd() function was called. +

+ +

+On success, the sf_open_fd function returns a non NULL pointer which should be +passed as the first parameter to all subsequent libsndfile calls dealing with +that audio file. +On fail, the sf_open_fd function returns a NULL pointer. +

+ + +

Format Check Function

+ +
+      int  sf_format_check (const SF_INFO *info) ;
+
+ +

+This function allows the caller to check if a set of parameters in the SF_INFO struct +is valid before calling sf_open (SFM_WRITE). +

+

+sf_format_check returns TRUE if the parameters are valid and FALSE otherwise. +

+ + +

File Seek Functions

+ +
+      sf_count_t  sf_seek  (SNDFILE *sndfile, sf_count_t frames, int whence) ;
+
+ +

+The file seek functions work much like lseek in unistd.h with the exception that +the non-audio data is ignored and the seek only moves within the audio data section of +the file. +In addition, seeks are defined in number of (multichannel) frames. +Therefore, a seek in a stereo file from the current position forward with an offset +of 1 would skip forward by one sample of both channels. +

+ +

+like lseek(), the whence parameter can be any one of the following three values: +

+ +
+      SEEK_SET  - The offset is set to the start of the audio data plus offset (multichannel) frames.
+      SEEK_CUR  - The offset is set to its current location plus offset (multichannel) frames.
+      SEEK_END  - The offset is set to the end of the data plus offset (multichannel) frames.
+
+ +

+Internally, libsndfile keeps track of the read and write locations using separate +read and write pointers. +If a file has been opened with a mode of SFM_RDWR, bitwise OR-ing the standard whence +values above with either SFM_READ or SFM_WRITE allows the read and write pointers to +be modified separately. +If the SEEK_* values are used on their own, the read and write pointers are +both modified. +

+ +

+Note that the frames offset can be negative and in fact should be when SEEK_END is used for the +whence parameter. +

+

+sf_seek will return the offset in (multichannel) frames from the start of the audio data +or -1 if an error occured (ie an attempt is made to seek beyond the start or end of the file). +

+ + +


Command Interface

+ +
+      int  sf_command  (SNDFILE *sndfile, int cmd, void *data, int datasize) ;
+
+ +

+This function allows the caller to retrieve information from or change aspects of the +library behaviour on a per file basis. Examples include reading or writing text descriptions +to a file or changing the scaling applied to sample data during read and write. +

+ +

+The cmd parameter is a short null terminated string which specifies which command +to execute. Data is passed to and returned from the library by use of a void +pointer. The library will not read or write more than datasize bytes from the void +pointer. For some calls no data is required in which case data should be NULL and +datasize may be used for some other purpose. +

+

+The return value of sf_command () depends on the value of the cmd parameter, but it is +usually non-zero for success and zero on error. +

+

+This function is explained more fully here. +

+ + +


Error Reporting Functions

+ + +
+      int         sf_error        (SNDFILE *sndfile) ;
+
+

+This function returns the current error number for the given SNDFILE. +The error number may be one of the following: +

+
+        enum
+        {   SF_ERR_NO_ERROR             = 0,
+            SF_ERR_UNRECOGNISED_FORMAT  = 1,
+            SF_ERR_SYSTEM               = 2,
+            SF_ERR_MALFORMED_FILE       = 3,
+            SF_ERR_UNSUPPORTED_ENCODING = 4
+        } ;
+
+ +

+or any one of many other internal error values. +Applications should only test the return value against error values defined in +<sndfile.h> as the internal error values are subject to change at any +time. +For errors not in the above list, the function sf_error_number() can be used to +convert it to an error string. +

+ +
+      const char* sf_strerror     (SNDFILE *sndfile) ;
+      const char* sf_error_number (int errnum) ;
+
+ +

+The error functions sf_strerror() and sf_error_number() convert the library's internal +error enumerations into text strings. +

+
+      int         sf_perror     (SNDFILE *sndfile) ;
+      int         sf_error_str  (SNDFILE *sndfile, char* str, size_t len) ;
+
+ +

+The functions sf_perror() and sf_error_str() are deprecated and will be dropped +from the library at some later date. +

+ + +


File Close Function

+ +
+      int  sf_close  (SNDFILE *sndfile) ;
+
+ +

+The close function closes the file, deallocates its internal buffers and returns +0 on success or an error value otherwise. +

+
+ + +


Write Sync Function

+ +
+      void  sf_write_sync  (SNDFILE *sndfile) ;
+
+ +

+If the file is opened SFM_WRITE or SFM_RDWR, call the operating system's function +to force the writing of all file cache buffers to disk. If the file is opened +SFM_READ no action is taken. +

+
+ + + +


File Read Functions (Items)

+ +
+      sf_count_t  sf_read_short   (SNDFILE *sndfile, short *ptr, sf_count_t items) ;
+      sf_count_t  sf_read_int     (SNDFILE *sndfile, int *ptr, sf_count_t items) ;
+      sf_count_t  sf_read_float   (SNDFILE *sndfile, float *ptr, sf_count_t items) ;
+      sf_count_t  sf_read_double  (SNDFILE *sndfile, double *ptr, sf_count_t items) ;
+
+ +

+The file read items functions fill the array pointed to by ptr with the requested +number of items. The items parameter must be an integer product of the number +of channels or an error will occur. +

+ +

+It is important to note that the data type used by the calling program and the data +format of the file do not need to be the same. For instance, it is possible to open +a 16 bit PCM encoded WAV file and read the data using sf_read_float(). The library +seamlessly converts between the two formats on-the-fly. See +Note 1. +

+ +

+The sf_read_XXXX functions return the number of items read. +Unless the end of the file was reached during the read, the return value should +equal the number of items requested. +Attempts to read beyond the end of the file will not result in an error but will +cause the sf_read_XXXX functions to return less than the number of items requested +or 0 if already at the end of the file. +

+ + +


File Read Functions (Frames)

+ +
+      sf_count_t  sf_readf_short   (SNDFILE *sndfile, short *ptr, sf_count_t frames) ;
+      sf_count_t  sf_readf_int     (SNDFILE *sndfile, int *ptr, sf_count_t frames) ;
+      sf_count_t  sf_readf_float   (SNDFILE *sndfile, float *ptr, sf_count_t frames) ;
+      sf_count_t  sf_readf_double  (SNDFILE *sndfile, double *ptr, sf_count_t frames) ;
+
+ +

+The file read frames functions fill the array pointed to by ptr with the requested +number of frames of data. The array must be large enough to hold the product of +frames and the number of channels. +

+ +

+Care must be taken to ensure that there is enough space in the array pointed to by +ptr, to take (frames * channels) number of items (shorts, ints, floats or doubles). +

+ +

+The sf_readf_XXXX functions return the number of frames read. +Unless the end of the file was reached during the read, the return value should equal +the number of frames requested. +Attempts to read beyond the end of the file will not result in an error but will cause +the sf_readf_XXXX functions to return less than the number of frames requested or 0 if +already at the end of the file. +

+ + +


File Write Functions (Items)

+ +
+      sf_count_t  sf_write_short   (SNDFILE *sndfile, short *ptr, sf_count_t items) ;
+      sf_count_t  sf_write_int     (SNDFILE *sndfile, int *ptr, sf_count_t items) ;
+      sf_count_t  sf_write_float   (SNDFILE *sndfile, float *ptr, sf_count_t items) ;
+      sf_count_t  sf_write_double  (SNDFILE *sndfile, double *ptr, sf_count_t items) ;
+
+ +

+The file write items functions write the data in the array pointed to by ptr to the file. +The items parameter must be an integer product of the number of channels or an error +will occur. +

+ +

+It is important to note that the data type used by the calling program and the data +format of the file do not need to be the same. For instance, it is possible to open +a 16 bit PCM encoded WAV file and write the data using sf_write_float(). The library +seamlessly converts between the two formats on-the-fly. See +Note 1. +

+

+The sf_write_XXXX functions return the number of items written (which should be the +same as the items parameter). +

+ + +


File Write Functions (Frames)

+ +
+      sf_count_t  sf_writef_short  (SNDFILE *sndfile, short *ptr, sf_count_t frames) ;
+      sf_count_t  sf_writef_int    (SNDFILE *sndfile, int *ptr, sf_count_t frames) ;
+      sf_count_t  sf_writef_float  (SNDFILE *sndfile, float *ptr, sf_count_t frames) ;
+      sf_count_t  sf_writef_double (SNDFILE *sndfile, double *ptr, sf_count_t frames) ;
+
+ +

+The file write frames functions write the data in the array pointed to by ptr to the file. +The array must be large enough to hold the product of frames and the number of channels. +

+

+The sf_writef_XXXX functions return the number of frames written (which should be the +same as the frames parameter). +

+ + +


Raw File Read and Write Functions

+ +
+      sf_count_t  sf_read_raw     (SNDFILE *sndfile, void *ptr, sf_count_t bytes) ;
+      sf_count_t  sf_write_raw    (SNDFILE *sndfile, void *ptr, sf_count_t bytes) ;
+
+ +

+The raw read and write functions read raw audio data from the audio file (not to be +confused with reading RAW header-less PCM files). The number of bytes read or written +must always be an integer multiple of the number of channels multiplied by the number +of bytes required to represent one sample from one channel. +

+ +

+The raw read and write functions return the number of bytes read or written (which +should be the same as the bytes parameter). +

+ +

+ +Note : The result of using of both regular reads/writes and raw reads/writes on +compressed file formats other than SF_FORMAT_ALAW and SF_FORMAT_ULAW is undefined. + +

+ + +


Functions for Reading and Writing String Data

+ + +
+      const char* sf_get_string   (SNDFILE *sndfile, int str_type) ;
+      int         sf_set_string   (SNDFILE *sndfile, int str_type, const char* str) ;
+
+ +

+These functions allow strings to be set on files opened for write and to be +retrieved from files opened for read where supported by the given file type. +The str_type parameter can be any one of the following string types: +

+ +
+          enum
+          {   SF_STR_TITLE,
+              SF_STR_COPYRIGHT,
+              SF_STR_SOFTWARE,
+              SF_STR_ARTIST,
+              SF_STR_COMMENT,
+              SF_STR_DATE
+          } ;
+
+ +

+The sf_get_string() function returns the specificed string if it exists and a +NULL pointer otherwise. +In addition to the string ids above, SF_STR_FIRST (== SF_STR_TITLE) and +SF_STR_LAST (always the same as the highest numbers string id) are also +available to allow iteration over all the available string ids. +

+ +

+The sf_set_string() function sets the string data. +It returns zero on success and non-zero on error. +The error code can be converted to a string using sf_error_number(). +

+ + +

+ +

+ +
+ + +


Note 1

+ +

+When converting between integer PCM formats of differing size (ie using sf_read_int() +to read a 16 bit PCM encoded WAV file) libsndfile obeys one simple rule: +

+ +

+Whenever integer data is moved from one sized container to another sized container, +the most significant bit in the source container will become the most significant bit +in the destination container. +

+ +

+When converting between integer data and floating point data, different rules apply. +The default behaviour when reading floating point data (sf_read_float() or +sf_read_double ()) from a file with integer data is normalisation. Regardless of +whether data in the file is 8, 16, 24 or 32 bit wide, the data will be read as +floating point data in the range [-1.0, 1.0]. Similarly, data in the range [-1.0, 1.0] +will be written to an integer PCM file so that a data value of 1.0 will be the largest +allowable integer for the given bit width. This normalisation can be turned on or off +using the sf_command interface. +

+ + +


Note 2

+ +

+Reading a file containg floating point data (allowable with WAV, AIFF, AU and other +file formats) using integer read methods (sf_read_short() or sf_read_int()) can +produce unexpected results. +For instance the data in the file may have a maximum absolute value < 1.0 which +would mean that all sample values read from the file will be zero. +In order to read these files correctly using integer read methods, it is recommended +that you use the + sf_command +interface a command of + SFC_SET_SCALE_FLOAT_INT_READ +and a parameter of SF_TRUE to force correct scaling. +

+ +
+ +

+ The libsndfile home page is + here. +

+

+Version : 1.0.17 +

+ + + + + + + + + diff --git a/doc/bugs.html b/doc/bugs.html new file mode 100644 index 00000000..7a7575ce --- /dev/null +++ b/doc/bugs.html @@ -0,0 +1,84 @@ + + + + + + Bug Reporting + + + + + + + +
+

Reporting Bugs in libsndfile

+
+

+ Before even attempting to report a bug in libsndfile please make sure you have + read the + Frequently Asked Questions. + If you are having a problem writing code using libsndfile make sure you read + the + Application Programming Interface + documentation. +

+

+ That said, I am interested in finding and fixing all genuine bugs in libsndfile. + Bugs I want to fix include any of the following problems (and probably others) : +

+
    +
  • Compilation problems on new platforms. +
  • Errors being detected during the `make check' process. +
  • Segmentation faults occuring inside libsndfile. +
  • libsndfile hanging when opening a file. +
  • Supported sound file types being incorrectly read or written. +
  • Omissions, errors or spelling mistakes in the documentation. +
+ +

+ When submitting a bug report you must include : +

+
    +
  • Your system (CPU and memory size should be enough). +
  • The operating system you are using. +
  • Whether you are using a package provided by your distribution or you + compiled it youself. +
  • If you compiled it yourself, the compiler you are using. (Also make + sure to run "make check".) +
  • A description of the problem. +
  • Information generated by the sndfile-info program (see next paragraph). +
  • If you are having problems with sndfile-play and ALSA on Linux, I will + need information about your kernel, ALSA version, compiler version, + whether you compiled the kernel/ALSA your self or installed from a + package etc. +
+ +

+ If libsndfile compiles and installs correctly but has difficulty reading a particular + file or type of file you should run the sndfile-info program (from the examples + directory of the libsndfile distribution) on the file. See + here + for an example of the use of the sndfile-info program. +

+

+ Please do not send me a sound file which fails to open under libsndfile unless I + specifically ask you to. The above information should usually suffice for most + problems. +

+

+ Once you have the above information you should email it to one of the + mailing lists + (posting to these lists is limited to the list subscribers). + +

+ + + + diff --git a/doc/command.html b/doc/command.html new file mode 100644 index 00000000..ee5bf595 --- /dev/null +++ b/doc/command.html @@ -0,0 +1,1245 @@ + + + + + + libsndfile : the sf_command function. + + + + + + + + + + +

sf_command

+
+
+        int    sf_command (SNDFILE *sndfile, int cmd, void *data, int datasize) ;
+
+

+ This function allows the caller to retrieve information from or change aspects of the + library behaviour. + Examples include retrieving a string containing the library version or changing the + scaling applied to floating point sample data during read and write. + Most of these operations are performed on a per-file basis. +

+

+ The cmd parameter is a integer identifier which is defined in <sndfile.h>. + All of the valid command identifiers have names beginning with "SFC_". + Data is passed to and returned from the library by use of a void pointer. + The library will not read or write more than datasize bytes from the void pointer. + For some calls no data is required in which case data should be NULL and datasize + may be used for some other purpose. +

+

+ The available commands are as follows: +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
SFC_GET_LIB_VERSIONRetrieve the version of the library.
SFC_GET_LOG_INFORetrieve the internal per-file operation log.
SFC_CALC_SIGNAL_MAXCalculate the measured maximum signal value.
SFC_CALC_NORM_SIGNAL_MAXCalculate the measured normalised maximum signal value.
SFC_CALC_MAX_ALL_CHANNELSCalculate the peak value for each channel.
SFC_CALC_NORM_MAX_ALL_CHANNELSCalculate the normalised peak for each channel.
SFC_GET_SIGNAL_MAXRetrieve the peak value for the file (as stored in the file header).
SFC_GET_MAX_ALL_CHANNELSRetrieve the peak value for each channel (as stored in the file header).
SFC_SET_NORM_FLOATModify the normalisation behaviour of the floating point reading and writing functions.
SFC_SET_NORM_DOUBLEModify the normalisation behaviour of the double precision floating point reading and writing functions.
SFC_GET_NORM_FLOATRetrieve the current normalisation behaviour of the floating point reading and writing functions.
SFC_GET_NORM_DOUBLERetrieve the current normalisation behaviour of the double precision floating point reading and writing functions.
SFC_SET_SCALE_FLOAT_INT_READSet/clear the scale factor when integer (short/int) data is read from a file + containing floating point data.
SFC_GET_SIMPLE_FORMAT_COUNTRetrieve the number of simple formats supported by libsndfile.
SFC_GET_SIMPLE_FORMATRetrieve information about a simple format.
SFC_GET_FORMAT_INFORetrieve information about a major or subtype format.
SFC_GET_FORMAT_MAJOR_COUNTRetrieve the number of major formats.
SFC_GET_FORMAT_MAJORRetrieve information about a major format type.
SFC_GET_FORMAT_SUBTYPE_COUNTRetrieve the number of subformats.
SFC_GET_FORMAT_SUBTYPERetrieve information about a subformat.
SFC_SET_ADD_PEAK_CHUNKSwitch the code for adding the PEAK chunk to WAV and AIFF files on or off.
SFC_UPDATE_HEADER_NOWUsed when a file is open for write, this command will update the file + header to reflect the data written so far.
SFC_SET_UPDATE_HEADER_AUTOUsed when a file is open for write, this command will cause the file header + to be updated after each write to the file.
SFC_FILE_TRUNCATETruncate a file open for write or for read/write.
SFC_SET_RAW_START_OFFSETChange the data start offset for files opened up as SF_FORMAT_RAW.
SFC_SET_CLIPPINGTurn on/off automatic clipping when doing floating point to integer + conversion.
SFC_GET_CLIPPINGRetreive current clipping setting.
SFC_GET_EMBED_FILE_INFORetreive information about audio files embedded inside other files.
SFC_GET_AMBISONICTest a WAVEX file for Ambisonic format
SFC_SET_AMBISONICModify a WAVEX header for Ambisonic format
+
+ +

+ +
+ + + +


SFC_GET_LIB_VERSION

+

+Retrieve the version of the library as a string. +

+

+Parameters: +

+        sndfile  : Not used
+        cmd      : SFC_GET_LIB_VERSION
+        data     : A pointer to a char buffer
+        datasize : The size of the the buffer
+
+

+Example: +

+
+        char  buffer [128] ;
+        sf_command (NULL, SFC_GET_LIB_VERSION, buffer, sizeof (buffer)) ;
+
+ +
+
Return value:
+
This call will return the length of the retrieved version string. +
+
+
Notes:
+
+The string returned in the buffer passed to this function will not overflow +the buffer and will always be null terminated . +
+ + + +


SFC_GET_LOG_INFO

+

+Retrieve the log buffer generated when opening a file as a string. This log +buffer can often contain a good reason for why libsndfile failed to open a +particular file. +

+

+Parameters: +

+        sndfile  : A valid SNDFILE* pointer
+        cmd      : SFC_GET_LOG_INFO
+        data     : A pointer to a char buffer
+        datasize : The size of the the buffer
+
+

+Example: +

+
+        char  buffer [2048] ;
+        sf_command (sndfile, SFC_GET_LOG_INFO, buffer, sizeof (buffer)) ;
+
+ +
+
Return value:
+
This call will return the length of the retrieved version string. +
+
+
Notes:
+
+The string returned in the buffer passed to this function will not overflow +the buffer and will always be null terminated . +
+ + + +


SFC_CALC_SIGNAL_MAX

+

+Retrieve the measured maximum signal value. This involves reading through +the whole file which can be slow on large files. +

+

+Parameters: +

+        sndfile  : A valid SNDFILE* pointer
+        cmd      : SFC_CALC_SIGNAL_MAX
+        data     : A pointer to a double
+        datasize : sizeof (double)
+
+

+Example: +

+
+        double   max_val ;
+        sf_command (sndfile, SFC_CALC_SIGNAL_MAX, &max_val, sizeof (max_val)) ;
+
+ +
+
Return value:
+
Zero on success, non-zero otherwise. +
+ + + +


SFC_CALC_NORM_SIGNAL_MAX

+

+Retrieve the measured normalised maximum signal value. This involves reading +through the whole file which can be slow on large files. +

+

+Parameters: +

+        sndfile  : A valid SNDFILE* pointer
+        cmd      : SFC_CALC_NORM_SIGNAL_MAX
+        data     : A pointer to a double
+        datasize : sizeof (double)
+
+

+Example: +

+
+        double   max_val ;
+        sf_command (sndfile, SFC_CALC_NORM_SIGNAL_MAX, &max_val, sizeof (max_val)) ;
+
+ +
+
Return value:
+
Zero on success, non-zero otherwise. +
+ + + +


SFC_CALC_MAX_ALL_CHANNELS

+

+Calculate the peak value (ie a single number) for each channel. +This involves reading through the whole file which can be slow on large files. +

+

+Parameters: +

+        sndfile  : A valid SNDFILE* pointer
+        cmd      : SFC_CALC_MAX_ALL_CHANNELS
+        data     : A pointer to a double
+        datasize : sizeof (double) * number_of_channels
+
+

+Example: +

+
+        double   peaks [number_of_channels] ;
+        sf_command (sndfile, SFC_CALC_MAX_ALL_CHANNELS, peaks, sizeof (peaks)) ;
+
+
+
Return value:
+
Zero if peaks have been calculated successfully and non-zero otherwise. +
+ + + + +


SFC_CALC_NORM_MAX_ALL_CHANNELS

+

+Calculate the normalised peak for each channel. +This involves reading through the whole file which can be slow on large files. +

+

+Parameters: +

+        sndfile  : A valid SNDFILE* pointer
+        cmd      : SFC_CALC_NORM_MAX_ALL_CHANNELS
+        data     : A pointer to a double
+        datasize : sizeof (double) * number_of_channels
+
+

+Example: +

+
+        double   peaks [number_of_channels] ;
+        sf_command (sndfile, SFC_CALC_NORM_MAX_ALL_CHANNELS, peaks, sizeof (peaks)) ;
+
+
+
Return value:
+
Zero if peaks have been calculated successfully and non-zero otherwise. +
+ + + + + + +


SFC_GET_SIGNAL_MAX

+

+Retrieve the peak value for the file as stored in the file header. +

+

+Parameters: +

+        sndfile  : A valid SNDFILE* pointer
+        cmd      : SFC_GET_SIGNAL_MAX
+        data     : A pointer to a double
+        datasize : sizeof (double)
+
+

+Example: +

+
+        double   max_peak ;
+        sf_command (sndfile, SFC_GET_SIGNAL_MAX, &max_peak, sizeof (max_peak)) ;
+
+
+
Return value:
+
SF_TRUE if the file header contained the peak value. SF_FALSE otherwise. +
+ + + +


SFC_GET_MAX_ALL_CHANNELS

+

+Retrieve the peak value for the file as stored in the file header. +

+

+Parameters: +

+        sndfile  : A valid SNDFILE* pointer
+        cmd      : SFC_GET_SIGNAL_MAX
+        data     : A pointer to an array of doubles
+        datasize : sizeof (double) * number_of_channels
+
+

+Example: +

+
+        double   peaks [number_of_channels] ;
+        sf_command (sndfile, SFC_GET_MAX_ALL_CHANNELS, peaks, sizeof (peaks)) ;
+
+
+
Return value:
+
SF_TRUE if the file header contains per channel peak values for the file. + SF_FALSE otherwise. +
+ + + + +


SFC_SET_NORM_FLOAT

+

+This command only affects data read from or written to using the floating point functions: +

+
+	size_t    sf_read_float    (SNDFILE *sndfile, float *ptr, size_t items) ;
+	size_t    sf_readf_float   (SNDFILE *sndfile, float *ptr, size_t frames) ;
+
+	size_t    sf_write_float   (SNDFILE *sndfile, float *ptr, size_t items) ;
+	size_t    sf_writef_float  (SNDFILE *sndfile, float *ptr, size_t frames) ;
+
+

+Parameters: +

+
+        sndfile  : A valid SNDFILE* pointer
+        cmd      : SFC_SET_NORM_FLOAT
+        data     : NULL
+        datasize : SF_TRUE or SF_FALSE
+
+

+For read operations setting normalisation to SF_TRUE means that the data from all +subsequent reads will be be normalised to the range [-1.0, 1.0]. +

+

+For write operations, setting normalisation to SF_TRUE means than all data supplied +to the float write functions should be in the range [-1.0, 1.0] and will be scaled +for the file format as necessary. +

+

+For both cases, setting normalisation to SF_FALSE means that no scaling will take place. +

+

+Example: +

+
+        sf_command (sndfile, SFC_SET_NORM_FLOAT, NULL, SF_TRUE) ;
+
+        sf_command (sndfile, SFC_SET_NORM_FLOAT, NULL, SF_FALSE) ;
+
+
+
Return value:
+
Returns the previous float normalisation mode. +
+ + + +


SFC_SET_NORM_DOUBLE

+

+This command only affects data read from or written to using the double precision +floating point functions: +

+
+	size_t    sf_read_double    (SNDFILE *sndfile, double *ptr, size_t items) ;
+	size_t    sf_readf_double   (SNDFILE *sndfile, double *ptr, size_t frames) ;
+
+	size_t    sf_write_double   (SNDFILE *sndfile, double *ptr, size_t items) ;
+	size_t    sf_writef_double  (SNDFILE *sndfile, double *ptr, size_t frames) ;
+
+

+Parameters: +

+
+        sndfile  : A valid SNDFILE* pointer
+        cmd      : SFC_SET_NORM_DOUBLE
+        data     : NULL
+        datasize : SF_TRUE or SF_FALSE
+
+

+For read operations setting normalisation to SF_TRUE means that the data +from all subsequent reads will be be normalised to the range [-1.0, 1.0]. +

+

+For write operations, setting normalisation to SF_TRUE means than all data supplied +to the double write functions should be in the range [-1.0, 1.0] and will be scaled +for the file format as necessary. +

+

+For both cases, setting normalisation to SF_FALSE means that no scaling will take place. +

+

+Example: +

+
+        sf_command (sndfile, SFC_SET_NORM_DOUBLE, NULL, SF_TRUE) ;
+
+        sf_command (sndfile, SFC_SET_NORM_DOUBLE, NULL, SF_FALSE) ;
+
+
+
Return value:
+
Returns the previous double normalisation mode. +
+ + + +


SFC_GET_NORM_FLOAT

+

+Retrieve the current float normalisation mode. +

+

+Parameters: +

+
+        sndfile  : A valid SNDFILE* pointer
+        cmd      : SFC_GET_NORM_FLOAT
+        data     : NULL
+        datasize : anything
+
+

+Example: +

+
+        normalisation = sf_command (sndfile, SFC_GET_NORM_FLOAT, NULL, 0) ;
+
+
+
Return value:
+
Returns TRUE if normalisation is on and FALSE otherwise. +
+ + + +


SFC_GET_NORM_DOUBLE

+

+Retrieve the current float normalisation mode. +

+

+Parameters: +

+
+        sndfile  : A valid SNDFILE* pointer
+        cmd      : SFC_GET_NORM_DOUBLE
+        data     : NULL
+        datasize : anything
+
+

+Example: +

+
+        normalisation = sf_command (sndfile, SFC_GET_NORM_DOUBLE, NULL, 0) ;
+
+
+
Return value:
+
Returns TRUE if normalisation is on and FALSE otherwise. +
+ + + + +


SFC_SET_SCALE_FLOAT_INT_READ

+

+Set/clear the scale factor when integer (short/int) data is read from a file +containing floating point data. +

+

+Parameters: +

+
+        sndfile  : A valid SNDFILE* pointer
+        cmd      : SFC_SET_SCALE_FLOAT_INT_READ
+        data     : NULL
+        datasize : TRUE or FALSE
+
+

+Example: +

+
+        sf_command (sndfile, SFC_SET_SCALE_FLOAT_INT_READ, NULL, SF_TRUE) ;
+
+
+
Return value:
+
Returns the previous SFC_SET_SCALE_FLOAT_INT_READ setting for this file. +
+ + + + +


SFC_GET_SIMPLE_FORMAT_COUNT

+

+Retrieve the number of simple formats supported by libsndfile. +

+

+Parameters: +

+
+        sndfile  : Not used.
+        cmd      : SFC_GET_SIMPLE_FORMAT_COUNT
+        data     : a pointer to an int
+        datasize : sizeof (int)
+
+

+Example: +

+
+        int  count ;
+        sf_command (sndfile, SFC_GET_SIMPLE_FORMAT_COUNT, &count, sizeof (int)) ;
+
+
+
Return value:
+
0 +
+ + + +


SFC_GET_SIMPLE_FORMAT

+

+Retrieve information about a simple format. +

+

+Parameters: +

+
+        sndfile  : Not used.
+        cmd      : SFC_GET_SIMPLE_FORMAT
+        data     : a pointer to an  SF_FORMAT_INFO struct
+        datasize : sizeof (SF_FORMAT_INFO)
+
+

+The SF_FORMAT_INFO struct is defined in <sndfile.h> as: +

+
+        typedef struct
+        {   int         format ;
+            const char  *name ;
+            const char  *extension ;
+        } SF_FORMAT_INFO ;
+
+

+When sf_command() is called with SF_GET_SIMPLE_FORMAT, the value of the format +field should be the format number (ie 0 <= format <= count value obtained using +SF_GET_SIMPLE_FORMAT_COUNT). +

+

+Example: +

+
+        SF_FORMAT_INFO	format_info ;
+        int             k, count ;
+
+        sf_command (sndfile, SFC_GET_SIMPLE_FORMAT_COUNT, &count, sizeof (int)) ;
+
+        for (k = 0 ; k < count ; k++)
+        {   format_info.format = k ;
+            sf_command (sndfile, SFC_GET_SIMPLE_FORMAT, &format_info, sizeof (format_info)) ;
+            printf ("%08x  %s %s\n", format_info.format, format_info.name, format_info.extension) ;
+            } ;
+
+
+
Return value:
+
0 on success and non-zero otherwise. +
The value of the format field of the SF_FORMAT_INFO struct will be a value which + can be placed in the format field of an SF_INFO struct when a file is to be opened + for write. +
The name field will contain a char* pointer to the name of the string, eg. "WAV (Microsoft 16 bit PCM)". +
The extension field will contain the most commonly used file extension for that file type. +
+ + + +


SFC_GET_FORMAT_INFO

+

+Retrieve information about a major or subtype format. +

+

+Parameters: +

+
+        sndfile  : Not used.
+        cmd      : SFC_GET_FORMAT_INFO
+        data     : a pointer to an SF_FORMAT_INFO struct
+        datasize : sizeof (SF_FORMAT_INFO)
+
+

+The SF_FORMAT_INFO struct is defined in <sndfile.h> as: +

+
+        typedef struct
+        {   int         format ;
+            const char  *name ;
+            const char  *extension ;
+        } SF_FORMAT_INFO ;
+
+

+When sf_command() is called with SF_GET_FORMAT_INFO, the format field is +examined and if (format & SF_FORMAT_TYPEMASK) is a valid format then the struct +is filled in with information about the given major type. +If (format & SF_FORMAT_TYPEMASK) is FALSE and (format & SF_FORMAT_SUBMASK) is a +valid subtype format then the struct is filled in with information about the given +subtype. +

+

+Example: +

+
+        SF_FORMAT_INFO	format_info ;
+
+        format_info.format = SF_FORMAT_WAV ;
+        sf_command (sndfile, SFC_GET_FORMAT_INFO, &format_info, sizeof (format_info)) ;
+        printf ("%08x  %s %s\n", format_info.format, format_info.name, format_info.extension) ;
+
+        format_info.format = SF_FORMAT_ULAW ;
+        sf_command (sndfile, SFC_GET_FORMAT_INFO, &format_info, sizeof (format_info)) ;
+        printf ("%08x  %s\n", format_info.format, format_info.name) ;
+
+
+
Return value:
+
0 on success and non-zero otherwise. +
+ + +


SFC_GET_FORMAT_MAJOR_COUNT

+

+Retrieve the number of major formats. +

+

+Parameters: +

+
+        sndfile  : Not used.
+        cmd      : SFC_GET_FORMAT_MAJOR_COUNT
+        data     : a pointer to an int
+        datasize : sizeof (int)
+
+

+Example: +

+
+        int  count ;
+        sf_command (sndfile, SFC_GET_FORMAT_MAJOR_COUNT, &count, sizeof (int)) ;
+
+
+
Return value:
+
0 +
+ + + +


SFC_GET_FORMAT_MAJOR

+

+Retrieve information about a major format type. +

+

+Parameters: +

+
+        sndfile  : Not used.
+        cmd      : SFC_GET_FORMAT_MAJOR
+        data     : a pointer to an  SF_FORMAT_INFO struct
+        datasize : sizeof (SF_FORMAT_INFO)
+
+

+Example: +

+
+        SF_FORMAT_INFO	format_info ;
+        int             k, count ;
+
+        sf_command (sndfile, SFC_GET_FORMAT_MAJOR_COUNT, &count, sizeof (int)) ;
+
+        for (k = 0 ; k < count ; k++)
+        {   format_info.format = k ;
+            sf_command (sndfile, SFC_GET_FORMAT_MAJOR, &format_info, sizeof (format_info)) ;
+            printf ("%08x  %s %s\n", format_info.format, format_info.name, format_info.extension) ;
+            } ;
+
+

+For a more comprehensive example, see the program list_formats.c in the examples/ +directory of the libsndfile source code distribution. +

+
+
Return value:
+
0 on success and non-zero otherwise. +
The value of the format field will be one of the major format identifiers such as + SF_FORMAT_WAV or SF_FORMAT_AIFF. +
The name field will contain a char* pointer to the name of the string, eg. "WAV (Microsoft)". +
The extension field will contain the most commonly used file extension for that file type. +
+ + + +


SFC_GET_FORMAT_SUBTYPE_COUNT

+

+Retrieve the number of subformats. +

+

+Parameters: +

+
+        sndfile  : Not used.
+        cmd      : SFC_GET_FORMAT_SUBTYPE_COUNT
+        data     : a pointer to an int
+        datasize : sizeof (int)
+
+

+Example: +

+
+        int   count ;
+        sf_command (sndfile, SFC_GET_FORMAT_SUBTYPE_COUNT, &count, sizeof (int)) ;
+
+
+
Return value:
+
0 +
+ + + +


SFC_GET_FORMAT_SUBTYPE

+

+Enumerate the subtypes (this function does not translate a sub type into +a string describing that subtype). +A typical use case might be retrieving a string description of all subtypes +so that a dialog box can be filled in. +

+

+ +

+

+Parameters: +

+
+        sndfile  : Not used.
+        cmd      : SFC_GET_FORMAT_SUBTYPE
+        data     : a pointer to an SF_FORMAT_INFO struct
+        datasize : sizeof (SF_FORMAT_INFO)
+
+

+Example 1: Retrieve all subytpes supported by the WAV format. +

+
+        SF_FORMAT_INFO	format_info ;
+        int             k, count ;
+
+        sf_command (sndfile, SFC_GET_FORMAT_SUBTYPE_COUNT, &count, sizeof (int)) ;
+
+        for (k = 0 ; k < count ; k++)
+        {   format_info.format = k ;
+            sf_command (sndfile, SFC_GET_FORMAT_SUBTYPE, &format_info, sizeof (format_info)) ;
+            if (! sf_format_check (format_info.format | SF_FORMAT_WAV))
+               continue ;
+            printf ("%08x  %s\n", format_info.format, format_info.name) ;
+            } ;
+
+

+Example 2: Print a string describing the SF_FORMAT_PCM_16 subtype. +

+
+        SF_FORMAT_INFO	format_info ;
+        int             k, count ;
+
+        sf_command (sndfile, SFC_GET_FORMAT_SUBTYPE_COUNT, &count, sizeof (int)) ;
+
+        for (k = 0 ; k < count ; k++)
+        {   format_info.format = k ;
+            sf_command (sndfile, SFC_GET_FORMAT_SUBTYPE, &format_info, sizeof (format_info)) ;
+            if (format_info.format == SF_FORMAT_PCM_16)
+            {   printf ("%08x  %s\n", format_info.format, format_info.name) ;
+                break ;
+                } ;
+            } ;
+
+

+For a more comprehensive example, see the program list_formats.c in the examples/ +directory of the libsndfile source code distribution. +

+
+
Return value:
+
0 on success and non-zero otherwise. +
The value of the format field will be one of the major format identifiers such as + SF_FORMAT_WAV or SF_FORMAT_AIFF. +
The name field will contain a char* pointer to the name of the string; for instance + "WAV (Microsoft)" or "AIFF (Apple/SGI)". +
The extension field will be a NULL pointer. +
+ + + +


SFC_SET_ADD_PEAK_CHUNK

+

+By default, WAV and AIFF files which contain floating point data (subtype SF_FORMAT_FLOAT +or SF_FORMAT_DOUBLE) have a PEAK chunk. +By using this command, the addition of a PEAK chunk can be turned on or off. +

+

+Note : This call must be made before any data is written to the file. +

+

+Parameters: +

+        sndfile  : A valid SNDFILE* pointer
+        cmd      : SFC_SET_ADD_PEAK_CHUNK
+        data     : Not used (should be NULL)
+        datasize : TRUE or FALSE.
+
+

+Example: +

+
+        /* Turn on the PEAK chunk. */
+        sf_command (sndfile, SFC_SET_ADD_PEAK_CHUNK, NULL, SF_TRUE) ;
+
+        /* Turn off the PEAK chunk. */
+        sf_command (sndfile, SFC_SET_ADD_PEAK_CHUNK, NULL, SF_FALSE) ;
+
+
+
Return value:
+
Returns SF_TRUE if the peak chunk will be written after this call. +
Returns SF_FALSE if the peak chunk will not be written after this call. +
+ + + +


SFC_UPDATE_HEADER_NOW

+

+The header of an audio file is normally written by libsndfile when the file is +closed using sf_close(). +

+

+There are however situations where large files are being generated and it would +be nice to have valid data in the header before the file is complete. +Using this command will update the file header to reflect the amount of data written +to the file so far. +Other programs opening the file for read (before any more data is written) will +then read a valid sound file header. +

+

+Parameters: +

+        sndfile  : A valid SNDFILE* pointer
+        cmd      : SFC_UPDATE_HEADER_NOW
+        data     : Not used (should be NULL)
+        datasize : Not used.
+
+

+Example: +

+
+        /* Update the header now. */
+        sf_command (sndfile, SFC_UPDATE_HEADER_NOW, NULL, 0) ;
+
+
+
Return value:
+
0 +
+ + + +


SFC_SET_UPDATE_HEADER_AUTO

+

+Similar to SFC_UPDATE_HEADER_NOW but updates the header at the end of every call +to the sf_write* functions. +

+

+Parameters: +

+        sndfile  : A valid SNDFILE* pointer
+        cmd      : SFC_UPDATE_HEADER_NOW
+        data     : Not used (should be NULL)
+        datasize : SF_TRUE or SF_FALSE
+
+

+Example: +

+
+        /* Turn on auto header update. */
+        sf_command (sndfile, SFC_SET_UPDATE_HEADER_AUTO, NULL, SF_TRUE) ;
+
+        /* Turn off auto header update. */
+        sf_command (sndfile, SFC_SET_UPDATE_HEADER_AUTO, NULL, SF_FALSE) ;
+
+
+
Return value:
+
TRUE if auto update header is now on; FALSE otherwise. +
+ + + +


SFC_FILE_TRUNCATE

+

+Truncate a file that was opened for write or read/write. +

+

+Parameters: +

+        sndfile  : A valid SNDFILE* pointer
+        cmd      : SFC_FILE_TRUNCATE
+        data     : A pointer to an sf_count_t.
+        datasize : sizeof (sf_count_t)
+
+ +

+Truncate the file to the number of frames specified by the sf_count_t pointed +to by data. +After this command, both the read and the write pointer will be +at the new end of the file. +This command will fail (returning non-zero) if the requested truncate position +is beyond the end of the file. +

+

+Example: +

+
+        /* Truncate the file to a length of 20 frames. */
+        sf_count_t  frames = 20 ;
+        sf_command (sndfile, SFC_FILE_TRUNCATE, &frames, sizeof (frames)) ;
+
+
+
Return value:
+
Zero on sucess, non-zero otherwise. +
+ + + +


SFC_SET_RAW_START_OFFSET

+

+Change the data start offset for files opened up as SF_FORMAT_RAW. +

+

+Parameters: +

+        sndfile  : A valid SNDFILE* pointer
+        cmd      : SFC_SET_RAW_START_OFFSET
+        data     : A pointer to an sf_count_t.
+        datasize : sizeof (sf_count_t)
+
+ +

+For a file opened as format SF_FORMAT_RAW, set the data offset to the value +given by data. +

+

+Example: +

+
+        /* Reset the data offset to 5 bytes from the start of the file. */
+        sf_count_t  offset = 5 ;
+        sf_command (sndfile, SFC_SET_RAW_START_OFFSET, &offset, sizeof (offset)) ;
+
+
+
Return value:
+
Zero on sucess, non-zero otherwise. +
+ + + +


SFC_SET_CLIPPING

+

+Turn on/off automatic clipping when doing floating point to integer conversion. +

+

+Parameters: +

+        sndfile  : A valid SNDFILE* pointer
+        cmd      : SFC_SET_CLIPPING
+        data     : NULL
+        datasize : SF_TRUE or SF_FALSE.
+
+ +

+Turn on (datasize == SF_TRUE) or off (datasize == SF_FALSE) clipping. +

+

+Example: +

+
+        sf_command (sndfile, SFC_SET_CLIPPING, NULL, SF_TRUE) ;
+
+
+
Return value:
+
Clipping mode (SF_TRUE or SF_FALSE). +
+ + + + +


SFC_GET_CLIPPING

+

+Turn on/off automatic clipping when doing floating point to integer conversion. +

+

+Parameters: +

+        sndfile  : A valid SNDFILE* pointer
+        cmd      : SFC_GET_CLIPPING
+        data     : NULL
+        datasize : 0
+
+ +

+Retrieve the current cliiping setting. +

+

+Example: +

+
+        sf_command (sndfile, SFC_GET_CLIPPING, NULL, 0) ;
+
+
+
Return value:
+
Clipping mode (SF_TRUE or SF_FALSE). +
+ + + +


SFC_GET_EMBED_FILE_INFO

+

+Get the file offset and file length of a file enbedded within another +larger file. +

+

+Parameters: +

+        sndfile  : A valid SNDFILE* pointer
+        cmd      : SFC_GET_CLIPPING
+        data     : a pointer to an  SF_EMBED_FILE_INFO struct
+        datasize : sizeof (SF_EMBED_FILE_INFO)
+
+

+The SF_FORMAT_INFO struct is defined in <sndfile.h> as: +

+
+        typedef struct
+        {   sf_count_t	offset ;
+            sf_count_t	length ;
+        } SF_EMBED_FILE_INFO ;
+
+
+
Return value:
+
0 on success and non-zero otherwise. +
The value of the offset field of the SF_EMBED_FILE_INFO struct will be + the offsets in bytes from the start of the outer file to the start of + the audio file. +
The value of the offset field of the SF_EMBED_FILE_INFO struct will be + the length in bytes of the embedded file. +
+ + + + + +


SFC_WAVEX_GET_AMBISONIC

+

+Test if the current file has the GUID of a WAVEX file for any of the Ambisonic +formats. +

+

+Parameters: +

+        sndfile  : A valid SNDFILE* pointer
+        cmd      : SFC_WAVEX_GET_AMBISONIC
+        data     : NULL
+        datasize : 0
+
+

+ The Ambisonic WAVEX formats are defined here : + + http://dream.cs.bath.ac.uk/researchdev/wave-ex/bformat.html. +

+
+
Return value:
+
SF_AMBISONIC_NONE or SF_AMBISONIC_B_FORMAT or zero if the file format + does not support ambisonic formats. +
+ + + +


SFC_WAVEX_SET_AMBISONIC

+

+Set the GUID of a new WAVEX file to indicate an Ambisonics format. +

+

+Parameters: +

+        sndfile  : A valid SNDFILE* pointer
+        cmd      : SFC_WAVEX_SET_AMBISONIC
+        data     : NULL
+        datasize : SF_AMBISONIC_NONE or SF_AMBISONIC_B_FORMAT
+
+

+Turn on (SF_AMBISONIC_B_FORMAT) or off (SF_AMBISONIC_NONE) encoding. +This command is currently only supported for files with SF_FORMAT_WAVEX format. +

+

+ The Ambisonic WAVEX formats are defined here : + + http://dream.cs.bath.ac.uk/researchdev/wave-ex/bformat.html. +

+
+
Return value:
+
Return the ambisonic value that has just been set or zero if the file + format does not support ambisonic encoding. +
+ + + + +
+

+ The libsndfile home page is here : + + http://www.mega-nerd.com/libsndfile/. +
+Version : 1.0.17 +

+ + + + + diff --git a/doc/development.html b/doc/development.html new file mode 100644 index 00000000..81397fe6 --- /dev/null +++ b/doc/development.html @@ -0,0 +1,83 @@ + + + + + + libsndfile Development + + + + + + + +


libsndfile Development

+ +

+libsndfile is being developed by a small but growing community of users +and hackers led by Erik de Castro Lopo. +People interested in helping should join the libsndfile-devel + mailing list +where most of the discussion about new features takes place. +

+ +

+libsndfile is being developed using the + Bzr +revision control system which is especially well suited to distributed +development. +In most respects, Bzr is significantly better than most other revision +control systems. +It is even available for windows, but I (Erik) have not personally used +it on windows. +

+ +

+The main archive archive can be found at: + + http://www.mega-nerd.com/Bzr/libsndfile-mdev/ +

+ +

+Assuming that you have Bzr installed (on Debian GNU/Linux "apt-get install bzr") +you can do: +

+ +
+	bzr get http://www.mega-nerd.com/Bzr/libsndfile-mdev/
+
+ +

+to retreive a copy of whats currently in the public archive. +To configure, build and verify the build you can do: +

+ +
+	./reconfigure.mk
+	./configure
+	make
+	make check
+
+ + +

+During the configuration stage, some later versions of aclocal (I see this +with 1.9.X) may spew out a bunch of warning messages about underquoted +defintions in AC_WHATEVER, but these can safely be ignored. +

+

+In order to build the sources from the Bzr archive, you will need the +Python interpreter and GNU Autogen which is available + here. +

+ + + + + diff --git a/doc/dither.html b/doc/dither.html new file mode 100644 index 00000000..16ca2dee --- /dev/null +++ b/doc/dither.html @@ -0,0 +1,1024 @@ + + + + + + libsndfile : the sf_command function. + + + + + + + + + + +

sf_command

+
+
+        int    sf_command (SNDFILE *sndfile, int cmd, void *data, int datasize) ;
+
+

+ This function allows the caller to retrieve information from or change aspects of the + library behaviour. + Examples include retrieving a string containing the library version or changing the + scaling applied to floating point sample data during read and write. + Most of these operations are performed on a per-file basis. +

+

+ The cmd parameter is a integer identifier which is defined in <sndfile.h>. + All of the valid command identifiers have names begining with "SFC_". + Data is passed to and returned from the library by use of a void pointer. + The library will not read or write more than datasize bytes from the void pointer. + For some calls no data is required in which case data should be NULL and datasize + may be used for some other purpose. +

+

+ The available commands are as follows: +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
SFC_GET_LIB_VERSIONRetrieve the version of the library.
SFC_GET_LOG_INFORetrieve the internal per-file operation log.
SFC_CALC_SIGNAL_MAXRetrieve the measured maximum signal value.
SFC_CALC_NORM_SIGNAL_MAXRetrieve the measured normalised maximum signal value.
SFC_CALC_MAX_ALL_CHANNELSCalculate peaks for all channels.
SFC_CALC_NORM_MAX_ALL_CHANNELSCalculate normalised peaks for all channels.
SFC_SET_NORM_FLOATModify the normalisation behaviour of the floating point reading and writing functions.
SFC_SET_NORM_DOUBLEModify the normalisation behaviour of the double precision floating point reading and writing functions.
SFC_GET_NORM_FLOATRetrieve the current normalisation behaviour of the floating point reading and writing functions.
SFC_GET_NORM_DOUBLERetrieve the current normalisation behaviour of the double precision floating point reading and writing functions.
SFC_GET_SIMPLE_FORMAT_COUNTRetrieve the number of simple formats supported by libsndfile.
SFC_GET_SIMPLE_FORMATRetrieve information about a simple format.
SFC_GET_FORMAT_INFORetrieve information about a major or subtype format.
SFC_GET_FORMAT_MAJOR_COUNTRetrieve the number of major formats.
SFC_GET_FORMAT_MAJORRetrieve information about a major format type.
SFC_GET_FORMAT_SUBTYPE_COUNTRetrieve the number of subformats.
SFC_GET_FORMAT_SUBTYPERetrieve information about a subformat.
SFC_SET_ADD_PEAK_CHUNKSwitch the code for adding the PEAK chunk to WAV and AIFF files on or off.
SFC_UPDATE_HEADER_NOWUsed when a file is open for write, this command will update the file + header to reflect the data written so far.
SFC_SET_UPDATE_HEADER_AUTOUsed when a file is open for write, this command will cause the file header + to be updated after each write to the file.
SFC_FILE_TRUNCATETruncate a file open for write or for read/write.
SFC_SET_RAW_START_OFFSETChange the data start offset for files opened up as SF_FORMAT_RAW.
+
+ +

+ +
+ + + +


SFC_GET_LIB_VERSION

+

+Retrieve the version of the library as a string. +

+

+Parameters: +

+        sndfile  : Not used
+        cmd      : SFC_GET_LIB_VERSION
+        data     : A pointer to a char buffer
+        datasize : The size of the the buffer
+
+

+Example: +

+
+        char  buffer [128] ;
+        sf_command (NULL, SFC_GET_LIB_VERSION, buffer, sizeof (buffer)) ;
+
+ +
+
Return value:
+
This call will return the length of the retrieved version string. +
+
+
Notes:
+
+The string returned in the buffer passed to this function will not overflow +the buffer and will always be null terminated . +
+ + + +


SFC_GET_LOG_INFO

+

+Retrieve the log buffer generated when opening a file as a string. This log +buffer can often contain a good reason for why libsndfile failed to open a +particular file. +

+

+Parameters: +

+        sndfile  : A valid SNDFILE* pointer
+        cmd      : SFC_GET_LOG_INFO
+        data     : A pointer to a char buffer
+        datasize : The size of the the buffer
+
+

+Example: +

+
+        char  buffer [2048] ;
+        sf_command (sndfile, SFC_GET_LOG_INFO, buffer, sizeof (buffer)) ;
+
+ +
+
Return value:
+
This call will return the length of the retrieved version string. +
+
+
Notes:
+
+The string returned in the buffer passed to this function will not overflow +the buffer and will always be null terminated . +
+ + + +


SFC_CALC_SIGNAL_MAX

+

+Retrieve the measured maximum signal value. This involves reading through +the whole file which can be slow on large files. +

+

+Parameters: +

+        sndfile  : A valid SNDFILE* pointer
+        cmd      : SFC_CALC_SIGNAL_MAX
+        data     : A pointer to a double
+        datasize : sizeof (double)
+
+

+Example: +

+
+        double   max_val ;
+        sf_command (sndfile, SFC_CALC_SIGNAL_MAX, &max_val, sizeof (max_val)) ;
+
+ +
+
Return value:
+
Zero on success, non-zero otherwise. +
+ + + +


SFC_CALC_NORM_SIGNAL_MAX

+

+Retrieve the measured normailised maximum signal value. This involves reading +through the whole file which can be slow on large files. +

+

+Parameters: +

+        sndfile  : A valid SNDFILE* pointer
+        cmd      : SFC_CALC_NORM_SIGNAL_MAX
+        data     : A pointer to a double
+        datasize : sizeof (double)
+
+

+Example: +

+
+        double   max_val ;
+        sf_command (sndfile, SFC_CALC_NORM_SIGNAL_MAX, &max_val, sizeof (max_val)) ;
+
+ +
+
Return value:
+
Zero on success, non-zero otherwise. +
+ + + +


SFC_CALC_MAX_ALL_CHANNELS

+

+Calculate peaks for all channels. This involves reading through +the whole file which can be slow on large files. +

+

+Parameters: +

+        sndfile  : A valid SNDFILE* pointer
+        cmd      : SFC_CALC_MAX_ALL_CHANNELS
+        data     : A pointer to a double
+        datasize : sizeof (double) * number_of_channels
+
+

+Example: +

+
+        double   peaks [number_of_channels] ;
+        sf_command (sndfile, SFC_CALC_MAX_ALL_CHANNELS, peaks, sizeof (peaks)) ;
+
+
+
Return value:
+
Zero if peaks have been calculated successfully and non-zero otherwise. +
+ + + + +


SFC_CALC_NORM_MAX_ALL_CHANNELS

+

+Calculate normalised peaks for all channels. This involves reading through +the whole file which can be slow on large files. +

+

+Parameters: +

+        sndfile  : A valid SNDFILE* pointer
+        cmd      : SFC_CALC_NORM_MAX_ALL_CHANNELS
+        data     : A pointer to a double
+        datasize : sizeof (double) * number_of_channels
+
+

+Example: +

+
+        double   peaks [number_of_channels] ;
+        sf_command (sndfile, SFC_CALC_NORM_MAX_ALL_CHANNELS, peaks, sizeof (peaks)) ;
+
+
+
Return value:
+
Zero if peaks have been calculated successfully and non-zero otherwise. +
+ + + + + + + + + + + +


SFC_SET_NORM_FLOAT

+

+This command only affects data read from or written to using the floating point functions: +

+
+	size_t    sf_read_float    (SNDFILE *sndfile, float *ptr, size_t items) ;
+	size_t    sf_readf_float   (SNDFILE *sndfile, float *ptr, size_t frames) ;
+
+	size_t    sf_write_float   (SNDFILE *sndfile, float *ptr, size_t items) ;
+	size_t    sf_writef_float  (SNDFILE *sndfile, float *ptr, size_t frames) ;
+
+

+Parameters: +

+
+        sndfile  : A valid SNDFILE* pointer
+        cmd      : SFC_SET_NORM_FLOAT
+        data     : NULL
+        datasize : SF_TRUE or SF_FALSE
+
+

+For read operations setting normalisation to SF_TRUE means that the data from all +subsequent reads will be be normalised to the range [-1.0, 1.0]. +

+

+For write operations, setting normalisation to SF_TRUE means than all data supplied +to the float write functions should be in the range [-1.0, 1.0] and will be scaled +for the file format as necessary. +

+

+For both cases, setting normalisation to SF_FALSE means that no scaling will take place. +

+

+Example: +

+
+        sf_command (sndfile, SFC_SET_NORM_FLOAT, NULL, SF_TRUE) ;
+
+        sf_command (sndfile, SFC_SET_NORM_FLOAT, NULL, SF_FALSE) ;
+
+
+
Return value:
+
Returns 1 on success or 0 for failure. +
+ + + +


SFC_SET_NORM_DOUBLE

+

+This command only affects data read from or written to using the double precision +floating point functions: +

+
+	size_t    sf_read_double    (SNDFILE *sndfile, double *ptr, size_t items) ;
+	size_t    sf_readf_double   (SNDFILE *sndfile, double *ptr, size_t frames) ;
+
+	size_t    sf_write_double   (SNDFILE *sndfile, double *ptr, size_t items) ;
+	size_t    sf_writef_double  (SNDFILE *sndfile, double *ptr, size_t frames) ;
+
+

+Parameters: +

+
+        sndfile  : A valid SNDFILE* pointer
+        cmd      : SFC_SET_NORM_DOUBLE
+        data     : NULL
+        datasize : SF_TRUE or SF_FALSE
+
+

+For read operations setting normalisation to SF_TRUE means that the data +from all subsequent reads will be be normalised to the range [-1.0, 1.0]. +

+

+For write operations, setting normalisation to SF_TRUE means than all data supplied +to the double write functions should be in the range [-1.0, 1.0] and will be scaled +for the file format as necessary. +

+

+For both cases, setting normalisation to SF_FALSE means that no scaling will take place. +

+

+Example: +

+
+        sf_command (sndfile, SFC_SET_NORM_DOUBLE, NULL, SF_TRUE) ;
+
+        sf_command (sndfile, SFC_SET_NORM_DOUBLE, NULL, SF_FALSE) ;
+
+
+
Return value:
+
Returns 1 on success or 0 for failure. +
+ + + +


SFC_GET_NORM_FLOAT

+

+Retrieve the current float normalisation mode. +

+

+Parameters: +

+
+        sndfile  : A valid SNDFILE* pointer
+        cmd      : SFC_GET_NORM_FLOAT
+        data     : NULL
+        datasize : anything
+
+

+Example: +

+
+        normalisation = sf_command (sndfile, SFC_GET_NORM_FLOAT, NULL, 0) ;
+
+
+
Return value:
+
Returns TRUE if normaisation is on and FALSE otherwise. +
+ + + +


SFC_GET_NORM_DOUBLE

+

+Retrieve the current float normalisation mode. +

+

+Parameters: +

+
+        sndfile  : A valid SNDFILE* pointer
+        cmd      : SFC_GET_NORM_DOUBLE
+        data     : NULL
+        datasize : anything
+
+

+Example: +

+
+        normalisation = sf_command (sndfile, SFC_GET_NORM_DOUBLE, NULL, 0) ;
+
+
+
Return value:
+
Returns TRUE if normalisation is on and FALSE otherwise. +
+ + + +


SFC_GET_SIMPLE_FORMAT_COUNT

+

+Retrieve the number of simple formats supported by libsndfile. +

+

+Parameters: +

+
+        sndfile  : Not used.
+        cmd      : SFC_GET_SIMPLE_FORMAT_COUNT
+        data     : a pointer to an int
+        datasize : sizeof (int)
+
+

+Example: +

+
+        int  count ;
+        sf_command (sndfile, SFC_GET_SIMPLE_FORMAT_COUNT, &count, sizeof (int)) ;
+
+
+
Return value:
+
0 +
+ + + +


SFC_GET_SIMPLE_FORMAT

+

+Retrieve information about a simple format. +

+

+Parameters: +

+
+        sndfile  : Not used.
+        cmd      : SFC_GET_SIMPLE_FORMAT
+        data     : a pointer to an  SF_FORMAT_INFO struct
+        datasize : sizeof (SF_FORMAT_INFO)
+
+

+The SF_FORMAT_INFO struct is defined in <sndfile.h> as: +

+
+        typedef struct
+        {   int         format ;
+            const char  *name ;
+            const char  *extension ;
+        } SF_FORMAT_INFO ;
+
+

+When sf_command() is called with SF_GET_SIMPLE_FORMAT, the value of the format +field should be the format number (ie 0 <= format <= count value obtained using +SF_GET_SIMPLE_FORMAT_COUNT). +

+

+Example: +

+
+        SF_FORMAT_INFO	format_info ;
+        int             k, count ;
+
+        sf_command (sndfile, SFC_GET_SIMPLE_FORMAT_COUNT, &count, sizeof (int)) ;
+
+        for (k = 0 ; k < count ; k++)
+        {   format_info.format = k ;
+            sf_command (sndfile, SFC_GET_SIMPLE_FORMAT, &format_info, sizeof (format_info)) ;
+            printf ("%08x  %s %s\n", format_info.format, format_info.name, format_info.extension) ;
+            } ;
+
+
+
Return value:
+
0 on success and non-zero otherwise. +
The value of the format field of the SF_FORMAT_INFO struct will be an value which + can be placed in the format field of an SF_INFO struct when a file is to be opened + for write. +
The name field will contain a char* pointer to the name of the string ie "WAV (Microsoft 16 bit PCM)". +
The extention field will contain the most commonly used file extension for that file type. +
+ + + +


SFC_GET_FORMAT_INFO

+

+Retrieve information about a major or subtype format. +

+

+Parameters: +

+
+        sndfile  : Not used.
+        cmd      : SFC_GET_FORMAT_INFO
+        data     : a pointer to an SF_FORMAT_INFO struct
+        datasize : sizeof (SF_FORMAT_INFO)
+
+

+The SF_FORMAT_INFO struct is defined in <sndfile.h> as: +

+
+        typedef struct
+        {   int         format ;
+            const char  *name ;
+            const char  *extension ;
+        } SF_FORMAT_INFO ;
+
+

+When sf_command() is called with SF_GET_FORMAT_INFO, the format field is +examined and if (format & SF_FORMAT_TYPEMASK) is a valid format then the struct +is filled in with information about the given major type. +If (format & SF_FORMAT_TYPEMASK) is FALSE and (format & SF_FORMAT_SUBMASK) is a +valid subtype format then the struct is filled in with information about the given +subtype. +

+

+Example: +

+
+        SF_FORMAT_INFO	format_info ;
+
+        format_info.format = SF_FORMAT_WAV ;
+        sf_command (sndfile, SFC_GET_FORMAT_INFO, &format_info, sizeof (format_info)) ;
+        printf ("%08x  %s %s\n", format_info.format, format_info.name, format_info.extension) ;
+
+        format_info.format = SF_FORMAT_ULAW ;
+        sf_command (sndfile, SFC_GET_FORMAT_INFO, &format_info, sizeof (format_info)) ;
+        printf ("%08x  %s\n", format_info.format, format_info.name) ;
+
+
+
Return value:
+
0 on success and non-zero otherwise. +
+ + +


SFC_GET_FORMAT_MAJOR_COUNT

+

+Retrieve the number of major formats. +

+

+Parameters: +

+
+        sndfile  : Not used.
+        cmd      : SFC_GET_FORMAT_MAJOR_COUNT
+        data     : a pointer to an int
+        datasize : sizeof (int)
+
+

+Example: +

+
+        int  count ;
+        sf_command (sndfile, SFC_GET_FORMAT_MAJOR_COUNT, &count, sizeof (int)) ;
+
+
+
Return value:
+
0 +
+ + + +


SFC_GET_FORMAT_MAJOR

+

+Retrieve information about a major format type. +

+

+Parameters: +

+
+        sndfile  : Not used.
+        cmd      : SFC_GET_FORMAT_MAJOR
+        data     : a pointer to an  SF_FORMAT_INFO struct
+        datasize : sizeof (SF_FORMAT_INFO)
+
+

+Example: +

+
+        SF_FORMAT_INFO	format_info ;
+        int             k, count ;
+
+        sf_command (sndfile, SFC_GET_FORMAT_MAJOR_COUNT, &count, sizeof (int)) ;
+
+        for (k = 0 ; k < count ; k++)
+        {   format_info.format = k ;
+            sf_command (sndfile, SFC_GET_FORMAT_MAJOR, &format_info, sizeof (format_info)) ;
+            printf ("%08x  %s %s\n", format_info.format, format_info.name, format_info.extension) ;
+            } ;
+
+

+For a more comprehensive example, see the program list_formats.c in the examples/ +directory of the libsndfile source code distribution. +

+
+
Return value:
+
0 on success and non-zero otherwise. +
The value of the format field will one of the major format identifiers suc as SF_FORMAT_WAV + SF_FORMAT_AIFF. +
The name field will contain a char* pointer to the name of the string ie "WAV (Microsoft)". +
The extention field will contain the most commonly used file extension for that file type. +
+ + + +


SFC_GET_FORMAT_SUBTYPE_COUNT

+

+Retrieve the number of subformats. +

+

+Parameters: +

+
+        sndfile  : Not used.
+        cmd      : SFC_GET_FORMAT_SUBTYPE_COUNT
+        data     : a pointer to an int
+        datasize : sizeof (int)
+
+

+Example: +

+
+        int   count ;
+        sf_command (sndfile, SFC_GET_FORMAT_SUBTYPE_COUNT, &count, sizeof (int)) ;
+
+
+
Return value:
+
0 +
+ + + +


SFC_GET_FORMAT_SUBTYPE

+

+Retrieve information about a subformat. +

+

+Parameters: +

+
+        sndfile  : Not used.
+        cmd      : SFC_GET_FORMAT_SUBTYPE
+        data     : a pointer to an SF_FORMAT_INFO struct
+        datasize : sizeof (SF_FORMAT_INFO)
+
+

+Example: +

+
+        SF_FORMAT_INFO	format_info ;
+        int             k, count ;
+
+        sf_command (sndfile, SFC_GET_FORMAT_SUBTYPE_COUNT, &count, sizeof (int)) ;
+
+        /* Retrieve all the subtypes supported by the WAV format. */
+        for (k = 0 ; k < count ; k++)
+        {   format_info.format = k ;
+            sf_command (sndfile, SFC_GET_FORMAT_SUBTYPE, &format_info, sizeof (format_info)) ;
+            if (! sf_format_check (format.info | SF_FORMAT_WAV))
+               continue ;
+            printf ("%08x  %s\n", format_info.format, format_info.name) ;
+            } ;
+
+

+For a more comprehensive example, see the program list_formats.c in the examples/ +directory of the libsndfile source code distribution. +

+
+
Return value:
+
0 on success and non-zero otherwise. +
The value of the format field will one of the major format identifiers such as SF_FORMAT_WAV + SF_FORMAT_AIFF. +
The name field will contain a char* pointer to the name of the string; for instance + "WAV (Microsoft)" or "AIFF (Apple/SGI)". +
The extention field will be a NULL pointer. +
+ + + +


SFC_SET_ADD_PEAK_CHUNK

+

+By default, WAV and AIFF files which contain floating point data (subtype SF_FORMAT_FLOAT +or SF_FORMAT_DOUBLE) have a PEAK chunk. +By using this command, the addition of a PEAK chunk can be turned on or off. +

+

+Note : This call must be made before any data is written to the file. +

+

+Parameters: +

+        sndfile  : A valid SNDFILE* pointer
+        cmd      : SFC_SET_ADD_PEAK_CHUNK
+        data     : Not used (should be NULL)
+        datasize : TRUE or FALSE.
+
+

+Example: +

+
+        /* Turn on the PEAK chunk. */
+        sf_command (sndfile, SFC_SET_ADD_PEAK_CHUNK, NULL, SF_TRUE) ;
+
+        /* Turn off the PEAK chunk. */
+        sf_command (sndfile, SFC_SET_ADD_PEAK_CHUNK, NULL, SF_FALSE) ;
+
+
+
Return value:
+
Returns SF_TRUE if the peak chunk will be written after this call. +
Returns SF_FALSE if the peak chunk will not be written after this call. +
+ + + +


SFC_UPDATE_HEADER_NOW

+

+The header of an audio file is normally written by libsndfile when the file is +closed using sf_close(). +

+

+There are however situations where large files are being generated and it would +be nice to have valid data in the header before the file is complete. +Using this command will update the file header to reflect the amount of data written +to the file so far. +Other programs opening the file for read (before any more data is written) will +then read a valid sound file header. +

+

+Parameters: +

+        sndfile  : A valid SNDFILE* pointer
+        cmd      : SFC_UPDATE_HEADER_NOW
+        data     : Not used (should be NULL)
+        datasize : Not used.
+
+

+Example: +

+
+        /* Update the header now. */
+        sf_command (sndfile, SFC_UPDATE_HEADER_NOW, NULL, 0) ;
+
+
+
Return value:
+
0 +
+ + + +


SFC_SET_UPDATE_HEADER_AUTO

+

+Similar to SFC_UPDATE_HEADER_NOW but updates the header at the end of every call +to the sf_write* functions. +

+

+Parameters: +

+        sndfile  : A valid SNDFILE* pointer
+        cmd      : SFC_UPDATE_HEADER_NOW
+        data     : Not used (should be NULL)
+        datasize : SF_TRUE or SF_FALSE
+
+

+Example: +

+
+        /* Turn on auto header update. */
+        sf_command (sndfile, SFC_SET_UPDATE_HEADER_AUTO, NULL, SF_TRUE) ;
+
+        /* Turn off auto header update. */
+        sf_command (sndfile, SFC_SET_UPDATE_HEADER_AUTO, NULL, SF_FALSE) ;
+
+
+
Return value:
+
TRUE if auto update header is now on; FALSE otherwise. +
+ + + +


SFC_FILE_TRUNCATE

+

+Truncate a file open for write or for read/write. +

+

+Parameters: +

+        sndfile  : A valid SNDFILE* pointer
+        cmd      : SFC_FILE_TRUNCATE
+        data     : A pointer to an sf_count_t.
+        datasize : sizeof (sf_count_t)
+
+ +

+Truncate the file to the number of frames specified by the sf_count_t pointed +to by data. +After this command, both the read and the write pointer will be +at the new end of the file. +This command will fail (returning non-zero) if the requested truncate position +is beyond the end of the file. +

+

+Example: +

+
+        /* Truncate the file to a length of 20 frames. */
+        sf_count_t  frames = 20 ;
+        sf_command (sndfile, SFC_FILE_TRUNCATE, &frames, sizeof (frames)) ;
+
+
+
Return value:
+
Zero on sucess, non-zero otherwise. +
+ + + +


SFC_SET_RAW_START_OFFSET

+

+Change the data start offset for files opened up as SF_FORMAT_RAW. +

+

+Parameters: +

+        sndfile  : A valid SNDFILE* pointer
+        cmd      : SFC_SET_RAW_START_OFFSET
+        data     : A pointer to an sf_count_t.
+        datasize : sizeof (sf_count_t)
+
+ +

+For a file opened as format SF_FORMAT_RAW, set the data offset to the value +given by data. +

+

+Example: +

+
+        /* Reset the data offset to 5 bytes from the start of the file. */
+        sf_count_t  offset = 5 ;
+        sf_command (sndfile, SFC_SET_RAW_START_OFFSET, &offset, sizeof (offset)) ;
+
+
+
Return value:
+
Zero on sucess, non-zero otherwise. +
+ + + +
+

+ The libsndfile home page is here : + + http://www.mega-nerd.com/libsndfile/. +
+Version : 1.0.17 +

+ + + + + diff --git a/doc/embedded_files.html b/doc/embedded_files.html new file mode 100644 index 00000000..e9a84e82 --- /dev/null +++ b/doc/embedded_files.html @@ -0,0 +1,54 @@ + + + + + + libsndfile : Embedded Sound Files. + + + + + + + + + +

Embedded Sound Files.

+ +

+By using the open SNDFILE with a file descriptor function: +

+ +
+      SNDFILE*  sf_open_fd (int fd, int mode, SF_INFO *sfinfo, int close_desc) ;
+
+ +

+it is possible to open sound files embedded within larger files. +There are however a couple of caveats: +

+ +

    +
  • Read/Write mode (SFM_RDWR) is not supported. +
  • Writing of embedded files is only supported at the end of the file. +
  • Reading of embedded files is only supported at file offsets greater + than zero. +
  • Not all file formats are supported (currently only WAV, AIFF and AU). +
+ +

+The test program multi_file_test.c in the tests/ directory of the +source code tarball shows how this functionality is used to read and write +embedded files. +

+ + + + + diff --git a/doc/index.html b/doc/index.html new file mode 100644 index 00000000..fce8c4fb --- /dev/null +++ b/doc/index.html @@ -0,0 +1,476 @@ + + + + + + libsndfile + + + + + + + + + + + +
+ libsndfile.jpg +
+ +
+ History -+- + Features -+- + Similar or Related Projects -+- + News +
+ Development -+- + Programming Interface -+- + Bug Reporting -+- + Download +
+ FAQ -+- + Mailing Lists -+- + Change Log -+- + Licensing Information +
+ +

+

+ Libsndfile is a C library for reading and writing files containing sampled sound + (such as MS Windows WAV and the Apple/SGI AIFF format) through one standard + library interface. It is released in source code format under the + Gnu Lesser General Public License. +

+ +

+ The library was written to compile and run on a Linux system but should compile + and run on just about any Unix (including MacOSX). + It can also be compiled and run on Win32 systems using the Microsoft compiler and + MacOS (OS9 and earlier) using the Metrowerks compiler. + There are directions for compiling libsndfile on these platforms in the Win32 and + MacOS directories of the source code distribution. +

+

+ It was designed to handle both little-endian (such as WAV) and big-endian + (such as AIFF) data, and to compile and run correctly on little-endian (such as Intel + and DEC/Compaq Alpha) processor systems as well as big-endian processor systems such + as Motorola 68k, Power PC, MIPS and Sparc. + Hopefully the design of the library will also make it easy to extend for reading and + writing new sound file formats. +

+ +

+ It has been compiled and tested (at one time or another) on the following systems: +

+ +
    +
  • i586-pc-linux-gnu (Linux on PC hardware) +
  • powerpc-unknown-linux-gnu (Linux on Apple Mac hardware) +
  • powerpc-apple-darwin7.0 (Mac OS X 10.3) +
  • sparc-sun-solaris2.8 (using gcc) +
  • mips-sgi-irix5.3 (using gcc) +
  • QNX 6.0 +
  • i386-unknown-openbsd2.9 +
    +
  • Win32 (Microsoft Visual C++) +
+ +

+ At the moment, each new release is being tested on i386 Linux, PowerPC Linux, + MacOSX on PowerPC and Win32. +

+ + + + +

Features

+

+ libsndfile has the following main features : +

+
    +
  • Ability to read and write a large number of file formats. +
  • A simple, elegant and easy to use Applications Programming Interface. +
  • Usable on Unix, Win32, MacOS and others. +
  • On the fly format conversion, including endian-ness swapping, type conversion + and bitwidth scaling. +
  • Optional normalisation when reading floating point data from files containing + integer data. +
  • Ability to open files in read/write mode. +
  • The ability to write the file header without closing the file (only on files + open for write or read/write). +
  • Ability to query the library about all supported formats and retrieve text + strings describing each format. +
+

+ libsndfile has a comprehensive test suite so that each release is as bug free + as possible. + When new bugs are found, new tests are added to the test suite to ensure that + these bugs don't creep back into the code. + When new features are added, tests are added to the test suite to make sure that + these features continue to work correctly even when they are old features. +

+

+ The following table lists the file formats and encodings that libsndfile can read + and write. + The file formats are arranged across the top and encodings along the left + edge. +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 Micro- soft
WAV
SGI / Apple
AIFF / AIFC
Sun / DEC /
NeXT
AU / SND
Header- less
RAW
Paris Audio
File
PAF
Commo- dore
Amiga
IFF / SVX
Sphere
Nist
WAV
IRCAM
SF
Creative
VOC
Sound forge
W64
GNU Octave 2.0
MAT4
GNU Octave 2.1
MAT5
Portable Voice Format
PVF
Fasttracker 2
XI
HMM Tool Kit
HTK
Apple
CAF
Unsigned 8 bit PCMR/WR/W R/W    R/WR/W R/W    
Signed 8 bit PCM R/WR/WR/WR/WR/WR/W     R/W  R/W
Signed 16 bit PCMR/WR/WR/WR/WR/WR/WR/WR/WR/WR/WR/WR/WR/W R/WR/W
Signed 24 bit PCMR/WR/WR/WR/WR/W R/WR/W R/W     R/W
Signed 32 bit PCMR/WR/WR/WR/W  R/WR/W R/WR/WR/WR/W  R/W
32 bit floatR/WR/WR/WR/W   R/W R/WR/WR/W   R/W
64 bit doubleR/WR/WR/WR/W     R/WR/WR/W   R/W
u-law encodingR/WR/WR/WR/W  R/WR/WR/WR/W     R/W
A-law encodingR/WR/WR/WR/W  R/WR/WR/WR/W     R/W
IMA ADPCMR/W        R/W      
MS ADPCMR/W        R/W      
GSM 6.10R/WR/W R/W     R/W      
G721 ADPCM 32kbpsR/W R/W             
G723 ADPCM 24kbps  R/W             
G723 ADPCM 40kbps  R/W             
12 bit DWVW R/W R/W            
16 bit DWVW R/W R/W            
24 bit DWVW R/W R/W            
Ok Dialogic ADPCM   R/W            
8 bit DPCM             R/W  
16 bit DPCM             R/W  
+ + +

+ Some of the file formats I am also interested in adding are: +

+
    +
  • Kurzweil K2000 sampler files. +
  • Ogg Vorbis. +
  • Ogg Speex. +
  • FLAC. +
+

+ I have decided that I will not be adding support for MPEG Layer 3 due to the + patent issues surrounding this file format. +

+

+ Other file formats may also be added on request. +

+ + + + +

History

+

+ My first attempt at reading and writing WAV files was in 1990 or so under Windows + 3.1. + I started using Linux in early 1995 and contributed some code to the + wavplay + program. + That contributed code would eventually mutate into this library. + As one of my interests is Digital Signal Processing (DSP) I decided that as well as + reading data from an audio file in the native format (typically 16 bit short integers) + it would also be useful to be able to have the library do the conversion to floating + point numbers for DSP applications. + It then dawned on me that whatever file format (anything from 8 bit unsigned chars, + to 32 bit floating point numbers) the library should be able to convert the data to + whatever format the library user wishes to use it in. + For example, in a sound playback program, the library caller typically wants the sound + data in 16 bit short integers to dump into a sound card even though the data in the + file may be 32 bit floating point numbers (ie Microsoft's WAVE_FORMAT_IEEE_FLOAT + format). + Another example would be someone doing speech recognition research who has recorded + some speech as a 16 bit WAV file but wants to process it as double precision floating + point numbers. +

+

+ Here is the release history for libsndfile : +

+
    +
  • Version 0.0.8 (Feb 15 1999) First official release. +
  • Version 0.0.28 (Apr 26 2002) Final release of version 0 of libsndfile. +
  • Version 1.0.0rc1 (Jun 24 2002) Release candidate 1 of version 1 of libsndfile. +
  • Version 1.0.0rc6 (Aug 14 2002) MacOS 9 fixes. +
  • Version 1.0.0 (Aug 16 2002) First 1.0.X release. +
  • Version 1.0.1 (Sep 14 2002) Added MAT4 and MAT5 file formats. +
  • Version 1.0.2 (Nov 24 2002) Added VOX ADPCM format. +
  • Version 1.0.3 (Dec 09 2002) Fixes for Linux on ia64 CPUs. +
  • Version 1.0.4 (Feb 02 2003) New file formats and functionality. +
  • Version 1.0.5 (May 03 2003) One new file format and new functionality. +
  • Version 1.0.6 (Feb 08 2004) Large file fix for Linux/Solaris, new functionality + and Win32 improvements. +
  • Version 1.0.7 (Feb 24 2004) Fix build problems on MacOSX and fix ia64/MIPS etc + clip mode detction. +
  • Version 1.0.8 (Mar 14 2004) Minor bug fixes. +
  • Version 1.0.9 (Mar 30 2004) Add AVR format. Improve handling of some WAV files. +
  • Version 1.0.10 (Jun 15 2004) Minor bug fixes. Fix support for Win32 MinGW compiler. +
  • Version 1.0.11 (Nov 15 2004) Add SD2 file support, reading of loop data in WAV and AIFF. + Minor bug fixes. +
  • Version 1.0.12 (Sep 30 2005) Add FLAC and CAF file support, virtual I/O interface. + Minor bug fixes and cleanups. +
  • Version 1.0.13 (Jan 21 2006) Add read/write of instrument chunks. Minor bug fixes. +
  • Version 1.0.14 (Feb 19 2006) Minor bug fixes. Start shipping windows binary/source ZIP. +
  • Version 1.0.15 (Mar 16 2006) Minor bug fixes. +
  • Version 1.0.16 (Apr 30 2006) Add support for RIFX. Other minor feature enhancements and + bug fixes. +
  • Version 1.0.17 (Aug 31 2006) Add C++ wrapper sndfile.hh. Minor bug fixes and cleanups. +
+ + +

Similar or Related Projects

+ +
    +
  • SoX is a program for + converting between sound file formats. +
  • Wavplay started out + as a minimal WAV file player under Linux and has mutated into Gnuwave, a client/server + application for more general multimedia and games sound playback. +
  • Audiofile (libaudiofile) is + a library similar to libsndfile but with a different programming interface. The + author Michael Pruett has set out to clone (and fix some bugs in) the libaudiofile + library which ships with SGI's IRIX OS. +
  • sndlib.tar.gz is + another library written by Bill Schottstaedt of CCRMA. +
+ + +

Licensing

+

+ libsndfile is released under the terms of the GNU Lesser General Public License. You may + read the license + here + or read a simple explanation of the ideas behind the GPL and the LGPL + here. +

+

+ You can use libsndfile with + Free Software, + Open Source, + proprietary, shareware or other closed source applications as long as libsndfile + is used as a dynamically loaded library and you abide by a small number of other + conditions (read the LGPL for more info). + With applications released under the GNU GPL you can also use libsndfile statically + linked to your application. +

+

+ I would like to see libsndfile used as widely as possible but I would prefer it + if you released software that uses libsndfile as + Free Software + or + Open Source. + However, if you put in a great deal of effort building a huge application + which simply uses libsndfile for file I/O, then I have no problem with you releasing + that as closed source and charging as much money as you want for it as long as you + abide by the license. +

+ +

+ What I don't like to see is things like Steve Dekorte's + SoundConverter + for Mac OSX. + Mr Dekorte has grabbed a number of Free Software packages and wrapped them in a + rather amateurish, buggy GUI and released the result as shareware. + He charges US$10 for the full version when his contribution to the whole is, by + his own + + admission, + less than 10%. +

+ +

Download

+

+ Here is the latest version. It is available in the following formats (I am no longer + distributing RPMs). +

+ +

+ Compiling libsndfile is relatively easy. The INSTALL file in the top level directory + gives instructions on compiling and installing libsndfile on Unix-like systems + (including MacOSX). For Win32 there are instructions in the doc/ directory of the + tarball. These instructions are mirrored + here. +

+

+ Pre-release versions of libsndfile are available + here + and are announced on the + libsndfile-devel + mailing list. +

+

+ +
+ +

+ The latest version of this document can be found + here. +

+

+Author : + + Erik de Castro Lopo +

+ +

+This page has been accessed + counter.gif +times. +

+ + + + +

+ + + + diff --git a/doc/libsndfile.css.in b/doc/libsndfile.css.in new file mode 100644 index 00000000..027d2c79 --- /dev/null +++ b/doc/libsndfile.css.in @@ -0,0 +1,81 @@ +body { + background : @HTML_BGCOLOUR@ ; + color : @HTML_FGCOLOUR@ ; + font-family : arial, helvetica, sans-serif ; +} +td { + font-family : arial, helvetica, sans-serif ; + background : @HTML_BGCOLOUR@ ; + color : @HTML_FGCOLOUR@ ; +} +center { + font-family : arial, helvetica, sans-serif ; +} +p { + font-family : arial, helvetica, sans-serif ; + text-align : left ; + margin-left : 3% ; + margin-right : 3% ; +} +.indent_block { + font-family : arial, helvetica, sans-serif ; + text-align : left ; + margin-left : 10% ; + margin-right : 10% ; +} +br { + font-family : arial, helvetica, sans-serif ; +} +form { + font-family : arial, helvetica, sans-serif ; +} +ul { + font-family : arial, helvetica, sans-serif ; + text-align : left ; + margin-left : 3% ; + margin-right : 6% ; +} +ol { + font-family : arial, helvetica, sans-serif ; + text-align : left ; + margin-left : 3% ; + margin-right : 6% ; +} +dl { + font-family : arial, helvetica, sans-serif ; + text-align : left ; + margin-left : 3% ; + margin-right : 3% ; +} +h1 { + font-size : xx-large ; + background : @HTML_BGCOLOUR@ ; + color : #5050FF ; + text-align : left ; + margin-left : 3% ; + margin-right : 3% ; +} +h2 { + font-size : x-large ; + background : @HTML_BGCOLOUR@ ; + color : #5050FF ; + text-align : left ; + margin-left : 3% ; + margin-right : 3% ; +} +h3 { + font-size : large ; + background : @HTML_BGCOLOUR@ ; + color : #5050FF ; + text-align : left ; + margin-left : 3% ; + margin-right : 3% ; +} +pre { + font-family : courier, monospace ; + font-size : medium ; +} +a:link { color : #9090FF ; } +a:visited { color : #5050FF ; } +a:active { color : #FF00FF ; } +a:hover { background-color : #202080 ; } diff --git a/doc/libsndfile.jpg b/doc/libsndfile.jpg new file mode 100644 index 00000000..7855b92d Binary files /dev/null and b/doc/libsndfile.jpg differ diff --git a/doc/linux_games_programming.txt b/doc/linux_games_programming.txt new file mode 100644 index 00000000..576f86ba --- /dev/null +++ b/doc/linux_games_programming.txt @@ -0,0 +1,440 @@ +# Here are some some emails I exchanged with a guy trying to use +# libsndfile version 1 with code from the book "Linux Games Programming" +# by John Hall. The email addresses have been changed to foil the spam +# bots. + +Date: Tue, 20 Jul 2004 22:49:21 +0100 +From: Paul +To: erikd@fake-domain-name.com +Subject: Can you help with a problem? +Date: Tue, 20 Jul 2004 22:49:21 +0100 + +Hi, + +I'm trying to get the source examples in the "Programming Linux Games" +(NoStarch, Loki Software + John R. Hall) which use sndfile.h/libsndfile. + +While I can guess some of the newer versions of function calls and +enumerations, there are some which I cannot guess. + +Would you be able to translate them to the current version of +enumeration and function calls so that I can update the source? + +These are the three currently failing me: + + sf_open_read(filename, SF_INFO *sfinfo) (guess: sf_open(filename,SFM_READ, &sfinfo)) + SF_FORMAT_PCM (guess: either SF_FORMAT_PCM_U8 or _RAW) + SF_INFO.pcmbitwidth (guess: no idea!) + +There are probably more. I'm happy to send you the source files for +sound calls, scan the pages or anything else. Failing that, is there +somewhere with the changes listed so I can try and fix the code for myself? + +Thanks + +TTFN + +Paul + +================================================================================ + +Date: Wed, 21 Jul 2004 17:38:08 +1000 +From: Erik de Castro Lopo +To: Paul +Subject: Re: Can you help with a problem? + +On Tue, 20 Jul 2004 22:49:21 +0100 +Paul wrote: + +> Hi, +> +> I'm trying to get the source examples in the "Programming Linux Games" +> (NoStarch, Loki Software + John R. Hall) which use sndfile.h/libsndfile. +> +> While I can guess some of the newer versions of function calls and +> enumerations, there are some which I cannot guess. +> +> Would you be able to translate them to the current version of +> enumeration and function calls so that I can update the source? +> +> These are the three currently failing me: +> +> sf_open_read(filename, SF_INFO *sfinfo) (guess: sf_open(filename, +> SFM_READ, &sfinfo)) + +yes. + +> SF_FORMAT_PCM (guess: either SF_FORMAT_PCM_U8 or _RAW) + +Actually this list: + + SF_FORMAT_PCM_U8 + SF_FORMAT_PCM_S8 + SF_FORMAT_PCM_16 + SF_FORMAT_PCM_24 + SF_FORMAT_PCM_32 + +> SF_INFO.pcmbitwidth (guess: no idea!) + +WIth the above change, pcmbitwidth becomes redundant. + +> There are probably more. I'm happy to send you the source files for +> sound calls, scan the pages or anything else. Failing that, is there +> somewhere with the changes listed so I can try and fix the code for +> myself? + +Version 1.0.0 came out some time ago, but I think this: + + http://www.mega-nerd.com/libsndfile/version-1.html + +lists most of the changes. You should also look at the API docs: + + http://www.mega-nerd.com/libsndfile/api.html + +HTH, +Erik +-- ++-----------------------------------------------------------+ + Erik de Castro Lopo nospam@fake-domain-name.com ++-----------------------------------------------------------+ +"There is no reason why anyone would want a computer in their home" +Ken Olson, DEC, 1977 + +================================================================================ + +From: PFJ +To: Erik de Castro Lopo +Subject: Re: Can you help with a problem? +Date: Wed, 21 Jul 2004 09:07:39 +0100 + + +Hi Erik, + +Thanks for getting back to me. + +> > sf_open_read(filename, SF_INFO *sfinfo) (guess: sf_open(filename, SFM_READ, &sfinfo)) +> +> yes. + +Yay! + +> > SF_FORMAT_PCM (guess: either SF_FORMAT_PCM_U8 or _RAW) +> +> Actually this list: +> +> SF_FORMAT_PCM_U8 +> SF_FORMAT_PCM_S8 +> SF_FORMAT_PCM_16 +> SF_FORMAT_PCM_24 +> SF_FORMAT_PCM_32 + +I know, but the source code explicitly has SF_FORMAT_PCM which given the +code afterwards would equate to one of the above, but given that PCM +files can have a varied bitwidth the author probably wanted to cover all +bases. + +> Version 1.0.0 came out some time ago, but I think this: +> +> http://www.mega-nerd.com/libsndfile/version-1.html +> +> lists most of the changes. You should also look at the API docs: +> +> http://www.mega-nerd.com/libsndfile/api.html + +I'll download them and see what I can gleen. + +Thanks again for getting back to me + +TTFN + +Paul + +================================================================================ + +Date: Wed, 21 Jul 2004 18:20:29 +1000 +From: Erik de Castro Lopo +To: PFJ +Subject: Re: Can you help with a problem? + +On Wed, 21 Jul 2004 09:07:39 +0100 +PFJ wrote: + +> I know, but the source code explicitly has SF_FORMAT_PCM which given the +> code afterwards would equate to one of the above, but given that PCM +> files can have a varied bitwidth the author probably wanted to cover all +> bases. + +But surely the existing code does something like: + + sfinfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM; + sfinfo.pcmbitwidth = 16; + +which can be directly translated to: + + sfinfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16; + +and the same for pcmbitwitdhs of 24 and 32. For pcmbitwidth of 8 +you need to know that WAV files use SF_FORMAT_PCM_U8 and AIFF +files use SF_FORMAT_PCM_S8. Thats all there is to it. + +Erik +-- ++-----------------------------------------------------------+ + Erik de Castro Lopo nospam@fake-domain-name.com ++-----------------------------------------------------------+ +"Python addresses true pseudocode's two major failings: that it +isn't standardized, and it isn't executable." +- Grant R. Griffin in comp.dsp + +================================================================================ + +Subject: Re: Can you help with a problem? +From: PFJ +To: Erik de Castro Lopo +Date: Wed, 21 Jul 2004 09:50:55 +0100 + +Hi Erik, + +> > I know, but the source code explicitly has SF_FORMAT_PCM which given the +> > code afterwards would equate to one of the above, but given that PCM +> > files can have a varied bitwidth the author probably wanted to cover all +> > bases. +> +> But surely the existing code does something like: +> +> sfinfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM; +> sfinfo.pcmbitwidth = 16; + +If only! + +The actual code is this + +int LoadSoundFile(char *filename, sound_p sound) +{ + SNDFILE *file; + SF_INFO file_info; + short *buffer_short = NULL; + u_int8_t *buffer_8 = NULL; + int16_t *buffer_16 = NULL; + unsigned int i; + + /* Open the file and retrieve sample information. */ + file = sf_open_read(filename, &file_info); + // I've sorted this one already - PFJ + + /* Make sure the format is acceptable. */ + if ((file_info.format & 0x0F) != SF_FORMAT_PCM) { + printf("'%s' is not a PCM-based audio file.\n", filename); + sf_close(file); + return -1; + } + + if ((file_info.pcmbitwidth == 8) && (file_info.channels == 1)) { + sound->format = AL_FORMAT_MONO8; + } else if ((file_info.pcmbitwidth == 8) && (file_info.channels == 2)) { + sound->format = AL_FORMAT_STEREO8; + } else if ((file_info.pcmbitwidth == 16) && (file_info.channels == 1)) { + sound->format = AL_FORMAT_MONO16; + } else if ((file_info.pcmbitwidth == 16) && (file_info.channels == 2)) { + sound->format = AL_FORMAT_STEREO16; + } else { + printf("Unknown sample format in %s.\n", filename); + sf_close(file); + return -1; + } + + /* Allocate buffers. */ + buffer_short = (short *)malloc(file_info.samples * file_info.channels * sizeof (short)); + + buffer_8 = (u_int8_t *)malloc(file_info.samples * file_info.channels * file_info.pcmbitwidth / 8); + + buffer_16 = (int16_t *)buffer_8; + + if (buffer_short == NULL || buffer_8 == NULL) { + printf("Unable to allocate enough memory for '%s'.\n", filename); + goto error_cleanup; + } + + /* Read the entire sound file. */ + if (sf_readf_short(file,buffer_short,file_info.samples) == (size_t)-1) { + printf("Error while reading samples from '%s'.\n", filename); + goto error_cleanup; + } + + + + /* Fill in the sound data structure. */ + sound->freq = file_info.samplerate; + sound->size = file_info.samples * file_info.channels * file_info.pcmbitwidth / 8; + + /* Give our sound data to OpenAL. */ + alGenBuffers(1, &sound->name); + if (alGetError() != AL_NO_ERROR) { + printf("Error creating an AL buffer name for %s.\n", filename); + goto error_cleanup; + } + + alBufferData(sound->name, sound->format, buffer_8, sound->size,sound->freq); + if (alGetError() != AL_NO_ERROR) { + printf("Error sending buffer data to OpenAL for %s.\n", filename); + goto error_cleanup; + } + + /* Close the file and return success. */ + sf_close(file); + free(buffer_short); + free(buffer_8); + + return 0; + + error_cleanup: + if (file != NULL) fclose(file); + free(buffer_short); + free(buffer_8); + return -1; +} + +As you can see, the PCM material in the listing will not currently +compile and for the other sndfile material, it probably won't either. + +Any help would be appreciated. + +TTFN + +Paul + +================================================================================ + +From: Erik de Castro Lopo +To: PFJ +Subject: Re: Can you help with a problem? +Date: Wed, 21 Jul 2004 19:36:46 +1000 + +On Wed, 21 Jul 2004 09:50:55 +0100 +PFJ wrote: + +> Hi Erik, +> +> > > I know, but the source code explicitly has SF_FORMAT_PCM which given the +> > > code afterwards would equate to one of the above, but given that PCM +> > > files can have a varied bitwidth the author probably wanted to cover all +> > > bases. +> > +> > But surely the existing code does something like: +> > +> > sfinfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM; +> > sfinfo.pcmbitwidth = 16; +> +> If only! + +No, really. + +Drop this completely: + +> /* Make sure the format is acceptable. */ +> if ((file_info.format & 0x0F) != SF_FORMAT_PCM) { +> printf("'%s' is not a PCM-based audio file.\n", filename); +> sf_close(file); +> return -1; +> } + +Replace this block: + +> if ((file_info.pcmbitwidth == 8) && (file_info.channels == 1)) { +> sound->format = AL_FORMAT_MONO8; +> } else if ((file_info.pcmbitwidth == 8) && (file_info.channels == 2)) { +> sound->format = AL_FORMAT_STEREO8; +> } else if ((file_info.pcmbitwidth == 16) && (file_info.channels == 1)) { +> sound->format = AL_FORMAT_MONO16; +> } else if ((file_info.pcmbitwidth == 16) && (file_info.channels == 2)) { +> sound->format = AL_FORMAT_STEREO16; +> } else { +> printf("Unknown sample format in %s.\n", filename); +> sf_close(file); +> return -1; +> } + +with: + + int pcmbitwidth = 0; + + if (file_info.format & SF_FORMAT_SUBMASK != SF_FORMAT_PCM_16) + { printf("'%s' is not a PCM-based audio file.\n", filename); + sf_close(file); + return -1; + } + + if (file_info.channels < 1 || file_info.channels > 2) + { printf("'%s' bad channel count.\n", filename); + sf_close(file); + return -1; + } + + switch (file_info.format & SF_FORMAT_SUBMASK + file_info.channels << 16) + { case (SF_FORMAT_PCM_U8 + 1 << 16): + sound->format = AL_FORMAT_MONO8; + pcmbitwidth = 8; + break; + case (SF_FORMAT_PCM_U8 + 2 << 16): + sound->format = AL_FORMAT_STEREO8; + pcmbitwidth = 8; + break; + case (SF_FORMAT_PCM_16 + 1 << 16): + sound->format = AL_FORMAT_MONO16; + pcmbitwidth = 16; + break; + case (SF_FORMAT_PCM_16 + 2 << 16): + sound->format = AL_FORMAT_STEREO16; + pcmbitwidth = 16; + break; + default: + printf("Unknown sample format in %s.\n", filename); + sf_close(file); + return -1; + } + +> /* Allocate buffers. */ +> buffer_short = (short *)malloc(file_info.samples * +> file_info.channels * +> sizeof (short)); +> +> buffer_8 = (u_int8_t *)malloc(file_info.samples * +> file_info.channels * +> file_info.pcmbitwidth / 8); + +Use pcmbitwidth as calculated above. + +> buffer_16 = (int16_t *)buffer_8; +> +> if (buffer_short == NULL || buffer_8 == NULL) { +> printf("Unable to allocate enough memory for '%s'.\n", filename); +> goto error_cleanup; +> } +> +> /* Read the entire sound file. */ +> if (sf_readf_short(file,buffer_short,file_info.samples) == (size_t)- 1) { + +Replace "(size_t) - 1" with " < 0". + +> As you can see, the PCM material in the listing will not currently +> compile and for the other sndfile material, it probably won't either. + +None of the changes above should have been very difficult to figure +out. + +Erik +-- ++-----------------------------------------------------------+ + Erik de Castro Lopo nospam@fake-domain-name.com ++-----------------------------------------------------------+ +Microsoft is finally bringing all of its Windows operating system families +under one roof. It will combine all of the features of CE, stability and +support of ME and the speed of NT. +It will be called Windows CEMENT... + + +# Do not edit or modify anything in this comment block. +# The arch-tag line is a file identity tag for the GNU Arch +# revision control system. +# +# arch-tag: 78d64eac-20f0-4d71-a4e2-3e1b8821596f diff --git a/doc/lists.html b/doc/lists.html new file mode 100644 index 00000000..73e318fa --- /dev/null +++ b/doc/lists.html @@ -0,0 +1,59 @@ + + + + + + libsndfile Mailing Lists + + + + + + + +


libsndfile Mailing Lists

+ +

+There are three mailing lists for libsndfile: +

+ +
    +
  • libsndfile-announce@mega-nerd.com   + Subscribe +
    + A list which will announce each new release of libsndfile. + Noone can post to this list except the author. +

    + +
  • libsndfile-devel@mega-nerd.com   + Subscribe +
    + A list for discussing bugs, porting issues and feature requests. + Posting is restricted to subscribers. +

    + +
  • libsndfile-users@mega-nerd.com   + Subscribe +
    + A list for discussing the use of libsndfile in other programs. + Posting is restricted to subscribers. + +

    +
+ +

+The libsndfile-devel and libsndfile-users list will automatically receive a +copy of all emails to the libsndfile-announce list. +

+
+ + + + + diff --git a/doc/new_file_type.HOWTO b/doc/new_file_type.HOWTO new file mode 100644 index 00000000..a6da80ae --- /dev/null +++ b/doc/new_file_type.HOWTO @@ -0,0 +1,135 @@ +new_file_type.HOWTO +=================== + + Original : Wed May 23 19:05:07 EST 2001 + Update 1 : Fri Jul 11 22:12:38 EST 2003 + +This document will attempt to explain as fully as possible how to add code to +libsndfile to allow the reading and writing of new file types. By new file +type I particularly mean a new header type rather than a new encoding method +for an existing file type. + +This HOWTO will take the form of a step by step guide. It will assume that you +have all required tools including : + + - gcc + - make (should really be the GNU version) + - autoconf + - automake + - libtool + +These should all be available on the GNU ftp site: ftp://ftp.gnu.org/pub/gnu/. + +To help make these steps clearer let's suppose we are adding support for the +Whacky file format whose files contain 'W','A','C' and 'K' as the first four +bytes of the file format. Lets also assume that Whacky files contain PCM encoded +data. + +Step 1 +------ +Create a new .c file in the src/ directory of the libsndfile source tree. The +file name should be reasonable descriptive so that is is obvious that files of +the new type are handled by this file. In this particular case the file might +be named 'whacky.c'. + +Step 2 +------ +Add your new source code file to the build process. + +Edit the file src/Makefile.am and add the name of your file handler to the +FILESPECIFIC list of handlers. This list looks something like this: + +FILESPECIFIC = aiff.c au.c au_g72x.c nist.c paf.c raw.c samplitude.c \ + svx.c wav.c wav_float.c wav_gsm610.c wav_ima_adpcm.c \ + wav_ms_adpcm.c + +Then, run the script named 'reconf' in the libsndfile top level directory, +which will run autoconf and other associated tools. Finally run "./configure" +in the top level directory. You may want to use the "--disable-gcc-opt" option +to disable gcc optimisations and make debugging with gdb/ddd easier. + +Step 3 +------ +Add a unique identifier for the new file type. + +Edit src/sndfile.h.in and find the enum containing the SF_FORMAT_XXX identifiers. +Since you will be adding a major file type you should add your identifier to the +top part of the list where the values are above 0x10000 in value. The easiest +way to do this is to find the largest value in the list, add 0x10000 to it and +make that your new identifier value. The identifier should be something like +SF_FORMAT_WACK. + +Step 4 +------ +Add code to the file type recogniser function. + +Edit src/sndfile.c and find the function guess_file_type (). This function +reads the first 3 ints of the file and from that makes a guess at the file +type. In our case we would add: + + + if (buffer [0] == MAKE_MARKER ('W','A','C','K')) + return SF_FORMAT_WACK ; + +The use of the MAKE_MARKER macro should be pretty obvious and it is defined at the +top of file should you need to have a look at it. + +Step 5 +------ +Add a call to your open function from psf_open_file (). + +Edit src/sndfile.c and find the switch statement in psf_open_file (). It starts +like this: + + switch (filetype) + { case SF_FORMAT_WAV : + error = wav_open (psf) ; + break ; + + case SF_FORMAT_AIFF : + error = aiff_open (psf) ; + break ; + +Towards the bottom of this switch statement your should add one for the new file +type. Something like: + + case SF_FORMAT_WACK : + sf_errno = whacky_open (psf) ; + break ; + +Setp 6 +------ +Add prototypes for new open read and open write functions. + +Edit src/common.h, go to the bottom of the file and add something like + + int whacky_open (SF_PRIVATE *psf) ; + +Step 7 +------ + +Implement your open read function. The best way to do this is by coding +something much like one of the other file formats. The file src/au.c might be +a good place to start. + +In src/whacky.c you should now implement the function whacky_open() which +was prototyped in src/common.h. This function should return 0 on success and +a non-zero number on error. + +Error values are defined in src/common.h in a enum which starts at SFE_NO_ERROR. +When adding a new error value, you also need to add an error string to the +SndfileErrors array in src/sndfile.c. + +To parse the header of your new file type you should avoid using standard read/ +write/seek functions (and the fread/fwrite/fseek etc) and instead use +psf_binheader_readf () which is implemented and documented in src/common.h. + +During the parsing process, you should also print logging information to +libsndfile's internal log buffer using the psf_log_printf() function. + +At the end of the open read process, you should have set a number of fields in the +SF_PRIVATE structure pointed to by psf. + + + +*** THIS FILE IS INCOMPLETE *** diff --git a/doc/octave.html b/doc/octave.html new file mode 100644 index 00000000..afe013fa --- /dev/null +++ b/doc/octave.html @@ -0,0 +1,125 @@ + + + + + + libsndfile and GNU Octave + + + + + + + +
+

libsndfile and GNU Octave

+

+ GNU Octave is a high-level interactive + language for numerical computations. + There are currently two development streams, a stable 2.0.X series and a + development 2.1.X series. + Octave reads and writes data in binary formats that were originally developed + for + MATLAB. + Version 2.0.X of Octave uses binary data files compatible with MATLAB + version 4.2 while Octave 2.1.X uses binary data files compatible + with MATLAB version 5.0 as well as being able to read the older MATLAB 4.2 + format. +

+

+ From version 1.0.1 of libsndfile onwards, libsndfile has the ability of reading + and writing a small subset of the binary data files used by both versions + of GNU Octave. + This gives people using GNU Octave for audio based work an easy method of + moving audio data between GNU Octave and other programs which use libsndfile. +

+

+ For instance it is now possible to do the following: +

+ +
    +
  • Load a WAV file into a sound file editor such as + Sweep. +
  • Save it as a MAT4 file. +
  • Load the data into Octave for manipulation. +
  • Save the modified data. +
  • Reload it in Sweep. +
+

+ Another example would be using the MAT4 or MAT5 file formats as a format which + can be easily loaded into Octave for viewing/analyzing as well as a format + which can be played with command line players such as the one included with + libsndfile. +

+ +

Details

+

+ Octave, like most programming languages, uses variables to store data, and + Octave variables can contain both arrays and matrices. + It is also able to store one or more of these variables in a file. + When reading Octave files, libsndfile expects a file to contain two + variables and their associated data. + The first variable should contain a variable holding the file sample rate + while the second variable contains the audio data. +

+

+ For example, to generate a sine wave and store it as a binary file which + is compatible with libsndfile, do the following: +

+
+        octave:1 > samplerate = 44100 ;
+        octave:2 > wavedata = sin ((0:1023)*2*pi/1024) ;
+        octave:3 > save sine.mat samplerate wavedata 
+
+ +

+ The process of reading and writing files compatible with libsndfile can be + made easier by use of two Octave script files : +

+
+        octave:4 > [data fs] = sndfile_load ("sine.mat") ;
+        octave:5 > sndfile_save ("sine2.mat", data, fs) ;
+
+

+ In addition, libsndfile contains a command line program which which is able + to play the correct types of Octave files. + Using this command line player sndfile-play and a third Octave script + file allows Octave data to be played from within Octave on any of the platforms + which sndfile-play supports (at the moment: Linux, MacOSX, Solaris and + Win32). +

+
+        octave:6 > sndfile_play (data, fs) ;
+
+

+ These three Octave scripts are installed automatically in Octave's site + script directory when libsndfile is installed (except on Win32) ie when + libsndfile is being installed into /usr/local, the Octave scripts will + be installed in /usr/local/share/octave/site/m/. +

+ +

+ There are some other Octave scripts for audio to be found + here. +

+ +
+ + +
+

+ The libsndfile home page is here : + + http://www.mega-nerd.com/libsndfile/. +

+ + + + + diff --git a/doc/pkgconfig.html b/doc/pkgconfig.html new file mode 100644 index 00000000..40d15c3b --- /dev/null +++ b/doc/pkgconfig.html @@ -0,0 +1,78 @@ + + + + + + libsndfile : pkg-config + + + + + + + +
+

libsndfile and pkg-config

+ +

+ From version 1.0.0 libsndfile has had the ability to read and write files of + greater than 2 Gig in size on most OSes even if sizeof (long) == 4. + OSes which support this feature include Linux (2.4 kernel, glibc6) on x86, PPC and + probably others, Win32, MacOSX, *BSD, Solaris and probably others. + OSes on 64 bit processors where the default compile environment is LP64 (longs and + pointers are 64 bit ie Linux on DEC/Compaq/HP Alpha processors) automatically + support large file access. +

+

+ Other OSes including Linux on 32 bit processors, 32 bit Solaris and others require + special compiler flags to add large file support. + This applies to both the compilation of the library itself and the compilation of + programs which link to the library. +

+

+ Note : People using Win32, MacOS (both OSX and pre-OSX) or *BSD can disregard the + rest of this document as it does not apply to either of these OSes. +

+

+ The pkg-config program makes finding the correct compiler flag values and + library location far easier. + During the installation of libsndfile, a file named sndfile.pc is installed + in the directory ${libdir}/pkgconfig (ie if libsndfile is installed in + /usr/local/lib, sndfile.pc will be installed in + /usr/local/lib/pkgconfig/). +

+

+ In order for pkg-config to find sndfile.pc it may be necessary to point the + environment variable PKG_CONFIG_PATH in the right direction. +

+
+        export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
+	
+ +

+ Then, to compile a C file into an object file, the command would be: +

+
+        gcc `pkg-config --cflags sndfile` -c somefile.c
+	
+

+ and to link a number of objects into an executable that links against libsndfile, + the command would be: +

+
+        gcc `pkg-config --libs sndfile` obj1.o obj2.o -o program
+	
+ +

+ Obviously all this can be rolled into a Makefile for easier maintenance. +

+ + + + diff --git a/doc/sndfile_info.html b/doc/sndfile_info.html new file mode 100644 index 00000000..2ac0e5fe --- /dev/null +++ b/doc/sndfile_info.html @@ -0,0 +1,60 @@ + + + + + + sndfile-info + + + + + + + +

+ Here is an example of the output from the sndfile-info program distributed with + libsndfile. +

+ +

+ This file was opened and parsed correctly but had been truncated so that the values + in the FORM and SSND chunks were incorrect. +

+
+        erikd@hendrix > examples/sndfile-info truncated.aiff 
+        truncated.aiff
+        size : 200000
+        FORM : 307474 (should be 199992)
+         AIFF
+         COMM : 18
+          Sample Rate : 16000
+          Samples     : 76857
+          Channels    : 2
+          Sample Size : 16
+         SSND : 307436 (should be 199946)
+          Offset     : 0
+          Block Size : 0
+        
+        --------------------------------
+        Sample Rate : 16000
+        Frames      : 76857
+        Channels    : 2
+        Bit Width   : 16
+        Format      : 0x00020001
+        Sections    : 1
+        Seekable    : TRUE
+        Signal Max  : 32766
+        	
+
+ + + + + + diff --git a/doc/win32.html b/doc/win32.html new file mode 100644 index 00000000..4222d1f6 --- /dev/null +++ b/doc/win32.html @@ -0,0 +1,141 @@ + + + + + + Building libsndfile on Win32 + + + + + + + +


Building libsndfile on Win32

+ +

+Note : For pre-compiled binaries for windows, see the main web page. +

+ +

+There are currently two ways of building libsndfile under Win32: +

+
    +
  • Using the free MinGW tools. +
  • Using Cygwin (this is the full Cygwin without the -mno-cygwin option). +
+ +

+Building libsndfile using the microsoft compiler does not currently work. +Microsoft's compiler is a C++ compiler and does not compile a number of ISO +C99 Standard constructs. +If you insist on compiling libsndfile with the microsoft compiler you are +on your own. +

+

+However, even though the libsndfile source code cannot be compiled with +the microsoft compiler, the pre-compiled windows DLL distributed on the +main web page can be used with C++ projects compiled with the microsoft +compiler. +

+ +

+The free MinGW tools are the preferred option because they are a free (as in +beer and speech), easily obtainable and much closer to the Linux/GCC +environment on which libsndfile is developed. +

+ + +


Building libsndfile using MinGW.

+

+The MinGW tools are available from +http://www.mingw.org/. +The files you will need will be named something like: +

+
    +
  • msysDTK-1.0.1.exe +
  • MSYS-1.0.10.exe +
  • gcc-core-3.4.2-20040916-1.tar.gz +
  • gcc-g++-3.4.2-20040916-1.tar.gz +
  • binutils-2.15.91-20040901-1.tar.gz +
  • mingw-runtime-3.9.tar.gz +
  • w32api-3.6.tar.gz +
  • mingw-utils-0.3.tar.gz +
+ +

+They should be installed using the directions on the MinGW site. +I know the instructions aren't great, but I don't have the time or expertise +to write better ones. +

+ +

+Once you have the tools installed you will get an icon named MSYS on +your desktop. +Clicking on that icon will bring up something that looks a little like an +xterm. +

+ +

+Assuming that you have downloaded the .tar.gz file into the +C:\temp\ directory you should now be able to execute the following +commands: +

+ +
+		tar zxf /c/temp/libsndfile.X.Y.Z.tar.gz
+		cd libsndfile-X.Y.Z
+		./configure
+		make
+		make check
+
+ +

+If there is a failure during compiling or the "make check" stage, you should +check that you are using the current stable version of the MinGW tools, and +the latest version of libsndfile before sending the author a bug report. +

+ +

+Once this is done, anyone interested in using libsndfile in other projects +will be interested in the following files: +

+
    +
  • libsndfile.dll +
  • libsndfile.lib +
  • libsndfile.def +
  • src/sndfile.h +
  • doc/*.html +
+ + + + +


Compile Problems

+ +

+Compile problems using the above method of building the libsndfile DLL on Win32 should +be emailed to + + Erik de Castro Lopo. +

+ +

+If you are using some other method to compile the libsndfile DLL you are on your own. +

+
+ + + + + + + + + diff --git a/examples/Makefile.am b/examples/Makefile.am new file mode 100644 index 00000000..94b170c5 --- /dev/null +++ b/examples/Makefile.am @@ -0,0 +1,46 @@ +## Process this file with automake to produce Makefile.in + +bin_PROGRAMS = sndfile-info sndfile-play sndfile-convert + +noinst_PROGRAMS = sndfile-data-trim make_sine sfprocess list_formats generate + +# This is the BeOS version of sndfile-play. It needs to be compiled with the C++ +# compiler. +EXTRA_DIST = sndfile-play-beos.cpp + +OS_SPECIFIC_CFLAGS = @OS_SPECIFIC_CFLAGS@ +OS_SPECIFIC_LINKS = @OS_SPECIFIC_LINKS@ + +SNDFILEDIR =../src +INCLUDES = -I$(srcdir)/$(SNDFILEDIR) $(OS_SPECIFIC_CFLAGS) + +sndfile_info_SOURCES = sndfile-info.c +sndfile_info_LDADD = $(SNDFILEDIR)/libsndfile.la + +sndfile_play_SOURCES = sndfile-play.c +sndfile_play_LDADD = $(SNDFILEDIR)/libsndfile.la $(OS_SPECIFIC_LINKS) $(ALSA_LIBS) + +sndfile_convert_SOURCES = sndfile-convert.c +sndfile_convert_LDADD = $(SNDFILEDIR)/libsndfile.la + +sndfile_data_trim_SOURCES = sndfile-data-trim.c +sndfile_data_trim_LDADD = $(SNDFILEDIR)/libsndfile.la + +make_sine_SOURCES = make_sine.c +make_sine_LDADD = $(SNDFILEDIR)/libsndfile.la + +sfprocess_SOURCES = sfprocess.c +sfprocess_LDADD = $(SNDFILEDIR)/libsndfile.la + +list_formats_SOURCES = list_formats.c +list_formats_LDADD = $(SNDFILEDIR)/libsndfile.la + +generate_SOURCES = generate.c +generate_LDADD = $(SNDFILEDIR)/libsndfile.la + +## Do not edit or modify anything in this comment block. +## The arch-tag line is a file identity tag for the GNU Arch +## revision control system. +## +## arch-tag: faeb8674-e417-4162-9ac9-05f2b8369b57 + diff --git a/examples/generate.c b/examples/generate.c new file mode 100644 index 00000000..1b0eec79 --- /dev/null +++ b/examples/generate.c @@ -0,0 +1,125 @@ +/* +** Copyright (C) 2002-2005 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include +#include + +#include + +#define BUFFER_LEN 4096 + +static void encode_file (const char *infilename, const char *outfilename, int filetype) ; + +int +main (int argc, char **argv) +{ + if (argc != 2) + { puts ("\nEncode a single input file into a number of different output ") ; + puts ("encodings. These output encodings can then be moved to another ") ; + puts ("OS for testing.\n") ; + puts (" Usage : generate \n") ; + exit (1) ; + } ; + + /* A couple of standard WAV files. Make sure Win32 plays these. */ + encode_file (argv [1], "pcmu8.wav" , SF_FORMAT_WAV | SF_FORMAT_PCM_U8) ; + encode_file (argv [1], "pcm16.wav" , SF_FORMAT_WAV | SF_FORMAT_PCM_16) ; + encode_file (argv [1], "imaadpcm.wav", SF_FORMAT_WAV | SF_FORMAT_MS_ADPCM) ; + encode_file (argv [1], "msadpcm.wav", SF_FORMAT_WAV | SF_FORMAT_IMA_ADPCM) ; + encode_file (argv [1], "gsm610.wav" , SF_FORMAT_WAV | SF_FORMAT_GSM610) ; + + /* Soundforge W64. */ + encode_file (argv [1], "pcmu8.w64" , SF_FORMAT_W64 | SF_FORMAT_PCM_U8) ; + encode_file (argv [1], "pcm16.w64" , SF_FORMAT_W64 | SF_FORMAT_PCM_16) ; + encode_file (argv [1], "imaadpcm.w64", SF_FORMAT_W64 | SF_FORMAT_MS_ADPCM) ; + encode_file (argv [1], "msadpcm.w64", SF_FORMAT_W64 | SF_FORMAT_IMA_ADPCM) ; + encode_file (argv [1], "gsm610.w64" , SF_FORMAT_W64 | SF_FORMAT_GSM610) ; + + return 0 ; +} /* main */ + +/*============================================================================================ +** Helper functions and macros. +*/ + +#define PUT_DOTS(k) \ + { while (k--) \ + putchar ('.') ; \ + putchar (' ') ; \ + } + +/*======================================================================================== +*/ + +static void +encode_file (const char *infilename, const char *outfilename, int filetype) +{ static float buffer [BUFFER_LEN] ; + + SNDFILE *infile, *outfile ; + SF_INFO sfinfo ; + int k, readcount ; + + printf (" %s -> %s ", infilename, outfilename) ; + fflush (stdout) ; + + k = 16 - strlen (outfilename) ; + PUT_DOTS (k) ; + + if (! (infile = sf_open (infilename, SFM_READ, &sfinfo))) + { printf ("Error : could not open file : %s\n", infilename) ; + puts (sf_strerror (NULL)) ; + exit (1) ; + } + + sfinfo.format = filetype ; + + if (! sf_format_check (&sfinfo)) + { sf_close (infile) ; + printf ("Invalid encoding\n") ; + return ; + } ; + + if (! (outfile = sf_open (outfilename, SFM_WRITE, &sfinfo))) + { printf ("Error : could not open file : %s\n", outfilename) ; + puts (sf_strerror (NULL)) ; + exit (1) ; + } ; + + while ((readcount = sf_read_float (infile, buffer, BUFFER_LEN)) > 0) + sf_write_float (outfile, buffer, BUFFER_LEN) ; + + sf_close (infile) ; + sf_close (outfile) ; + + printf ("ok\n") ; + + return ; +} /* encode_file */ + + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: fe28ef37-ae89-4f61-966b-0b1f68e37425 +*/ diff --git a/examples/generate.cs b/examples/generate.cs new file mode 100644 index 00000000..9262e5b5 --- /dev/null +++ b/examples/generate.cs @@ -0,0 +1,255 @@ +/* (c) 2004 James Robson, http://www.arbingersys.com +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +** **************************** +** +** How to use: +** - libsndfile.dll must have already been compiled and be in this +** application's search path +** +** - You must edit this file to point to the file you want to convert. Set +** the following line of code (found in the Main() function further below) +** to the name of a .WAV file that exists on your system. +** 186: string sfn = "input.wav"; +** +** - From a command prompt type +** csc generate.cs +** +** - Run the resulting executable 'generate.exe' +** +** +** Note: You will obviously need the csc compiler and the .NET runtime. I think +** these are freely available for download from Microsoft's website +** (part of the .NET SDK?). +*/ + + +using System; +using System.Runtime.InteropServices; +using sf_count_t = System.Int64; //alias; see SF_INFO struct + +#if PLATFORM_64 +using size_t = System.UInt64; +#else +using size_t = System.UInt32; +#endif + + +class lsndf_example { + + +//sound file formats + public enum lsndf_frmts { + SF_FORMAT_WAV = 0x010000, /* Microsoft WAV format (little endian). */ + SF_FORMAT_AIFF = 0x020000, /* Apple/SGI AIFF format (big endian). */ + SF_FORMAT_AU = 0x030000, /* Sun/NeXT AU format (big endian). */ + SF_FORMAT_RAW = 0x040000, /* RAW PCM data. */ + SF_FORMAT_PAF = 0x050000, /* Ensoniq PARIS file format. */ + SF_FORMAT_SVX = 0x060000, /* Amiga IFF / SVX8 / SV16 format. */ + SF_FORMAT_NIST = 0x070000, /* Sphere NIST format. */ + SF_FORMAT_VOC = 0x080000, /* VOC files. */ + SF_FORMAT_IRCAM = 0x0A0000, /* Berkeley/IRCAM/CARL */ + SF_FORMAT_W64 = 0x0B0000, /* Sonic Foundry's 64 bit RIFF/WAV */ + SF_FORMAT_MAT4 = 0x0C0000, /* Matlab (tm) V4.2 / GNU Octave 2.0 */ + SF_FORMAT_MAT5 = 0x0D0000, /* Matlab (tm) V5.0 / GNU Octave 2.1 */ + SF_FORMAT_PVF = 0x0E0000, /* Portable Voice Format */ + SF_FORMAT_XI = 0x0F0000, /* Fasttracker 2 Extended Instrument */ + SF_FORMAT_HTK = 0x100000, /* HMM Tool Kit format */ + SF_FORMAT_SDS = 0x110000, /* Midi Sample Dump Standard */ + + /* Subtypes from here on. */ + + SF_FORMAT_PCM_S8 = 0x0001, /* Signed 8 bit data */ + SF_FORMAT_PCM_16 = 0x0002, /* Signed 16 bit data */ + SF_FORMAT_PCM_24 = 0x0003, /* Signed 24 bit data */ + SF_FORMAT_PCM_32 = 0x0004, /* Signed 32 bit data */ + + SF_FORMAT_PCM_U8 = 0x0005, /* Unsigned 8 bit data (WAV and RAW only) */ + + SF_FORMAT_FLOAT = 0x0006, /* 32 bit float data */ + SF_FORMAT_DOUBLE = 0x0007, /* 64 bit float data */ + + SF_FORMAT_ULAW = 0x0010, /* U-Law encoded. */ + SF_FORMAT_ALAW = 0x0011, /* A-Law encoded. */ + SF_FORMAT_IMA_ADPCM = 0x0012, /* IMA ADPCM. */ + SF_FORMAT_MS_ADPCM = 0x0013, /* Microsoft ADPCM. */ + + SF_FORMAT_GSM610 = 0x0020, /* GSM 6.10 encoding. */ + SF_FORMAT_VOX_ADPCM = 0x0021, /* OKI / Dialogix ADPCM */ + + SF_FORMAT_G721_32 = 0x0030, /* 32kbs G721 ADPCM encoding. */ + SF_FORMAT_G723_24 = 0x0031, /* 24kbs G723 ADPCM encoding. */ + SF_FORMAT_G723_40 = 0x0032, /* 40kbs G723 ADPCM encoding. */ + + SF_FORMAT_DWVW_12 = 0x0040, /* 12 bit Delta Width Variable Word encoding. */ + SF_FORMAT_DWVW_16 = 0x0041, /* 16 bit Delta Width Variable Word encoding. */ + SF_FORMAT_DWVW_24 = 0x0042, /* 24 bit Delta Width Variable Word encoding. */ + SF_FORMAT_DWVW_N = 0x0043, /* N bit Delta Width Variable Word encoding. */ + + SF_FORMAT_DPCM_8 = 0x0050, /* 8 bit differential PCM (XI only) */ + SF_FORMAT_DPCM_16 = 0x0051, /* 16 bit differential PCM (XI only) */ + + + /* Endian-ness options. */ + + SF_ENDIAN_FILE = 0x00000000, /* Default file endian-ness. */ + SF_ENDIAN_LITTLE = 0x10000000, /* Force little endian-ness. */ + SF_ENDIAN_BIG = 0x20000000, /* Force big endian-ness. */ + SF_ENDIAN_CPU = 0x30000000, /* Force CPU endian-ness. */ + + SF_FORMAT_SUBMASK = 0x0000FFFF, + SF_FORMAT_TYPEMASK = 0x0FFF0000, + SF_FORMAT_ENDMASK = 0x30000000 + } + + +//modes and other + public enum lsndf_tf + { /* True and false */ + SF_FALSE = 0, + SF_TRUE = 1, + + /* Modes for opening files. */ + SFM_READ = 0x10, + SFM_WRITE = 0x20, + SFM_RDWR = 0x30 + } + + +//important SF_INFO structure + [StructLayout(LayoutKind.Sequential)] + public struct SF_INFO + { + public sf_count_t frames ; // Used to be called samples. Changed to avoid confusion. + public int samplerate ; + public int channels ; + public int format ; + public int sections ; + public int seekable ; + }; + + +//function declarations +//Note: Not all functions have been prototyped here. Only the ones necessary to +// make this application work. The below code should give some clues as to +// how to add the rest since they have a lot of parameter and return type +// similarities. + [DllImport("libsndfile.dll")] + public static extern IntPtr sf_open ([MarshalAs(UnmanagedType.LPStr)] string path, int mode, ref SF_INFO sfinfo); + + [DllImport("libsndfile.dll")] + static extern int sf_error (IntPtr sndfile); + + [DllImport("libsndfile.dll")] + static extern IntPtr sf_strerror (IntPtr sndfile); + + [DllImport("libsndfile.dll")] + static extern int sf_format_check (ref SF_INFO info); + + [DllImport("libsndfile.dll")] + static extern sf_count_t sf_read_float (IntPtr sndfile, float[] ptr, sf_count_t items); + + [DllImport("libsndfile.dll")] + static extern sf_count_t sf_write_float (IntPtr sndfile, float[] ptr, sf_count_t items); + + [DllImport("libsndfile.dll")] + static extern int sf_close (IntPtr sndfile); + + + public const sf_count_t BUFFER_LEN = 4096; + + +//program entry + static void Main( ) { + + +//declarations + SF_INFO sfinfo = new SF_INFO(); + float[] buffer = new float[BUFFER_LEN]; + sf_count_t rcnt; + +//set the input file + string sfn = "input.wav"; //set to a file on YOUR system + //string sfn = "noexist.wav"; //test with non-existent file + +//set the output file + string ofn = "output.wav"; + +//read in sound file to convert + IntPtr infile = sf_open (sfn, (int)lsndf_tf.SFM_READ, ref sfinfo); + +//exit if error was thrown + if ( (int)infile == 0 ) { + Console.WriteLine("Error opening " + sfn); + Console.WriteLine("Error #" + sf_error(infile)); + return; + } + +//set the file type for the output file +//uncomment one and only one of the statements below to change the output +//file encoding. + //sfinfo.format = (int)(lsndf_frmts.SF_FORMAT_WAV | lsndf_frmts.SF_FORMAT_PCM_U8); + //sfinfo.format = (int)(lsndf_frmts.SF_FORMAT_WAV | lsndf_frmts.SF_FORMAT_PCM_16); + //sfinfo.format = (int)(lsndf_frmts.SF_FORMAT_WAV | lsndf_frmts.SF_FORMAT_MS_ADPCM); + sfinfo.format = (int)(lsndf_frmts.SF_FORMAT_WAV | lsndf_frmts.SF_FORMAT_IMA_ADPCM); + //sfinfo.format = (int)(lsndf_frmts.SF_FORMAT_WAV | lsndf_frmts.SF_FORMAT_GSM610); + /* Soundforge W64. */ + //sfinfo.format = (int)(lsndf_frmts.SF_FORMAT_W64 | lsndf_frmts.SF_FORMAT_PCM_U8); + //sfinfo.format = (int)(lsndf_frmts.SF_FORMAT_W64 | lsndf_frmts.SF_FORMAT_PCM_16); + //sfinfo.format = (int)(lsndf_frmts.SF_FORMAT_W64 | lsndf_frmts.SF_FORMAT_MS_ADPCM); + //sfinfo.format = (int)(lsndf_frmts.SF_FORMAT_W64 | lsndf_frmts.SF_FORMAT_IMA_ADPCM); + //sfinfo.format = (int)(lsndf_frmts.SF_FORMAT_W64 | lsndf_frmts.SF_FORMAT_GSM610); + + +//check that SF_INFO is valid + if ( sf_format_check(ref sfinfo) == 0 ) { + Console.WriteLine("sf_format_check failed. Invalid encoding"); + return; + } + +//open output file + IntPtr outfile = sf_open (ofn, (int)lsndf_tf.SFM_WRITE, ref sfinfo); + +//exit if error was thrown + if ( (int)outfile == 0 ) { + Console.WriteLine("Error opening " + ofn); + Console.WriteLine("Error #" + sf_error(outfile)); + return; + } + +//infile -> outfile + Console.Write(sfn + " -> " + ofn); + while ( (rcnt = sf_read_float (infile, buffer, BUFFER_LEN)) > 0) { + Console.Write("."); + sf_write_float (outfile, buffer, BUFFER_LEN); + } + Console.WriteLine("done."); + +//close up shop + sf_close(infile); + sf_close(outfile); + + + } //main() + + +} //class lsndf_example {} + +// Do not edit or modify anything in this comment block. +// The arch-tag line is a file identity tag for the GNU Arch +// revision control system. +// +// arch-tag: 61a46c48-431c-4a16-97a3-b5485ca0e157 diff --git a/examples/list_formats.c b/examples/list_formats.c new file mode 100644 index 00000000..0b6db499 --- /dev/null +++ b/examples/list_formats.c @@ -0,0 +1,76 @@ +/* +** Copyright (C) 2001-2005 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include +#include +#include + +#include + +int +main (void) +{ SF_FORMAT_INFO info ; + SF_INFO sfinfo ; + char buffer [128] ; + int format, major_count, subtype_count, m, s ; + + memset (&sfinfo, 0, sizeof (sfinfo)) ; + buffer [0] = 0 ; + sf_command (NULL, SFC_GET_LIB_VERSION, buffer, sizeof (buffer)) ; + if (strlen (buffer) < 1) + { printf ("Line %d: could not retrieve lib version.\n", __LINE__) ; + exit (1) ; + } ; + printf ("Version : %s\n\n", buffer) ; + + sf_command (NULL, SFC_GET_FORMAT_MAJOR_COUNT, &major_count, sizeof (int)) ; + sf_command (NULL, SFC_GET_FORMAT_SUBTYPE_COUNT, &subtype_count, sizeof (int)) ; + + sfinfo.channels = 1 ; + for (m = 0 ; m < major_count ; m++) + { info.format = m ; + sf_command (NULL, SFC_GET_FORMAT_MAJOR, &info, sizeof (info)) ; + printf ("%s (extension \"%s\")\n", info.name, info.extension) ; + + format = info.format ; + + for (s = 0 ; s < subtype_count ; s++) + { info.format = s ; + sf_command (NULL, SFC_GET_FORMAT_SUBTYPE, &info, sizeof (info)) ; + + format = (format & SF_FORMAT_TYPEMASK) | info.format ; + + sfinfo.format = format ; + if (sf_format_check (&sfinfo)) + printf (" %s\n", info.name) ; + } ; + puts ("") ; + } ; + puts ("") ; + + return 0 ; +} /* main */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 58127a0c-93a2-46cf-b615-fcb9adacf3f1 +*/ diff --git a/examples/make_sine.c b/examples/make_sine.c new file mode 100644 index 00000000..3a50c26f --- /dev/null +++ b/examples/make_sine.c @@ -0,0 +1,89 @@ +/* +** Copyright (C) 1999-2005 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include +#include +#include + +#include + +#ifndef M_PI +#define M_PI 3.14159265358979323846264338 +#endif + +#define SAMPLE_RATE 44100 +#define SAMPLE_COUNT (SAMPLE_RATE * 4) /* 4 seconds */ +#define AMPLITUDE (1.0 * 0x7F000000) +#define LEFT_FREQ (344.0 / SAMPLE_RATE) +#define RIGHT_FREQ (466.0 / SAMPLE_RATE) + +int +main (void) +{ SNDFILE *file ; + SF_INFO sfinfo ; + int k ; + int *buffer ; + + if (! (buffer = malloc (2 * SAMPLE_COUNT * sizeof (int)))) + { printf ("Malloc failed.\n") ; + exit (0) ; + } ; + + memset (&sfinfo, 0, sizeof (sfinfo)) ; + + sfinfo.samplerate = SAMPLE_RATE ; + sfinfo.frames = SAMPLE_COUNT ; + sfinfo.channels = 2 ; + sfinfo.format = (SF_FORMAT_WAV | SF_FORMAT_PCM_24) ; + + if (! (file = sf_open ("sine.wav", SFM_WRITE, &sfinfo))) + { printf ("Error : Not able to open output file.\n") ; + return 1 ; + } ; + + if (sfinfo.channels == 1) + { for (k = 0 ; k < SAMPLE_COUNT ; k++) + buffer [k] = AMPLITUDE * sin (LEFT_FREQ * 2 * k * M_PI) ; + } + else if (sfinfo.channels == 2) + { for (k = 0 ; k < SAMPLE_COUNT ; k++) + { buffer [2 * k] = AMPLITUDE * sin (LEFT_FREQ * 2 * k * M_PI) ; + buffer [2 * k + 1] = AMPLITUDE * sin (RIGHT_FREQ * 2 * k * M_PI) ; + } ; + } + else + { printf ("makesine can only generate mono or stereo files.\n") ; + exit (1) ; + } ; + + if (sf_write_int (file, buffer, sfinfo.channels * SAMPLE_COUNT) != + sfinfo.channels * SAMPLE_COUNT) + puts (sf_strerror (file)) ; + + sf_close (file) ; + return 0 ; +} /* main */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: fd945a2c-a306-49ef-a262-6336ced15246 +*/ diff --git a/examples/paf_write.c b/examples/paf_write.c new file mode 100644 index 00000000..394b2a35 --- /dev/null +++ b/examples/paf_write.c @@ -0,0 +1,88 @@ +/* +** Copyright (C) 1999-2005 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + + +#include +#include +#include + +#include + +#define SAMPLE_RATE 8000 +#define SAMPLE_COUNT 100 + +int +main (void) +{ SNDFILE *file ; + SF_INFO sfinfo ; + char *filename ; + int k, buffer [SAMPLE_COUNT] ; + + for (k = 0 ; k < SAMPLE_COUNT ; k++) + buffer [k] = ((3 * k + 2) << 24) + ((3 * k + 1) << 16) + ((3 * k) << 8) ; + + /* Big endian first. */ + sfinfo.samplerate = SAMPLE_RATE ; + sfinfo.frames = SAMPLE_COUNT ; + sfinfo.channels = 1 ; + sfinfo.format = (SF_ENDIAN_BIG | SF_FORMAT_PAF | SF_FORMAT_PCM_24) ; + + filename = "be-pcm24.paf" ; + + if (! (file = sf_open (filename, SFM_WRITE, &sfinfo))) + { printf ("Error : Not able to open output file.\n") ; + return 1 ; + } ; + + printf ("Writing data to '%s'\n", filename) ; + + if (sf_write_int (file, buffer, SAMPLE_COUNT) != SAMPLE_COUNT) + puts (sf_strerror (file)) ; + + sf_close (file) ; + + /* Little endian first. */ + sfinfo.samplerate = SAMPLE_RATE ; + sfinfo.frames = SAMPLE_COUNT ; + sfinfo.channels = 1 ; + sfinfo.format = (SF_ENDIAN_LITTLE | SF_FORMAT_PAF | SF_FORMAT_PCM_24) ; + + filename = "le-pcm24.paf" ; + + if (! (file = sf_open (filename, SFM_WRITE, &sfinfo))) + { printf ("Error : Not able to open output file.\n") ; + return 1 ; + } ; + + printf ("Writing data to '%s'\n", filename) ; + + if (sf_write_int (file, buffer, SAMPLE_COUNT) != SAMPLE_COUNT) + puts (sf_strerror (file)) ; + + sf_close (file) ; + + return 0 ; +} /* main */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 62025b88-1f3f-46fb-994a-8220bf915772 +*/ diff --git a/examples/sfprocess.c b/examples/sfprocess.c new file mode 100644 index 00000000..e48fcfb6 --- /dev/null +++ b/examples/sfprocess.c @@ -0,0 +1,131 @@ +/* +** Copyright (C) 2001-2005 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include + +/* Include this header file to use functions from libsndfile. */ +#include + +/* This will be the length of the buffer used to hold.frames while +** we process them. +*/ +#define BUFFER_LEN 1024 + +/* libsndfile can handle more than 6 channels but we'll restrict it to 6. */ +#define MAX_CHANNELS 6 + +/* Function prototype. */ +static void process_data (double *data, int count, int channels) ; + + +int +main (void) +{ /* This is a buffer of double precision floating point values + ** which will hold our data while we process it. + */ + static double data [BUFFER_LEN] ; + + /* A SNDFILE is very much like a FILE in the Standard C library. The + ** sf_open function return an SNDFILE* pointer when they sucessfully + ** open the specified file. + */ + SNDFILE *infile, *outfile ; + + /* A pointer to an SF_INFO stutct is passed to sf_open. + ** On read, the library fills this struct with information about the file. + ** On write, the struct must be filled in before calling sf_open. + */ + SF_INFO sfinfo ; + int readcount ; + const char *infilename = "input.wav" ; + const char *outfilename = "output.wav" ; + + /* Here's where we open the input file. We pass sf_open the file name and + ** a pointer to an SF_INFO struct. + ** On successful open, sf_open returns a SNDFILE* pointer which is used + ** for all subsequent operations on that file. + ** If an error occurs during sf_open, the function returns a NULL pointer. + ** + ** If you are trying to open a raw headerless file you will need to set the + ** format and channels fields of sfinfo before calling sf_open(). For + ** instance to open a raw 16 bit stereo PCM file you would need the following + ** two lines: + ** + ** sfinfo.format = SF_FORMAT_RAW | SF_FORMAT_PCM_16 ; + ** sfinfo.channels = 2 ; + */ + if (! (infile = sf_open (infilename, SFM_READ, &sfinfo))) + { /* Open failed so print an error message. */ + printf ("Not able to open input file %s.\n", infilename) ; + /* Print the error message from libsndfile. */ + puts (sf_strerror (NULL)) ; + return 1 ; + } ; + + if (sfinfo.channels > MAX_CHANNELS) + { printf ("Not able to process more than %d channels\n", MAX_CHANNELS) ; + return 1 ; + } ; + /* Open the output file. */ + if (! (outfile = sf_open (outfilename, SFM_WRITE, &sfinfo))) + { printf ("Not able to open output file %s.\n", outfilename) ; + puts (sf_strerror (NULL)) ; + return 1 ; + } ; + + /* While there are.frames in the input file, read them, process + ** them and write them to the output file. + */ + while ((readcount = sf_read_double (infile, data, BUFFER_LEN))) + { process_data (data, readcount, sfinfo.channels) ; + sf_write_double (outfile, data, readcount) ; + } ; + + /* Close input and output files. */ + sf_close (infile) ; + sf_close (outfile) ; + + return 0 ; +} /* main */ + +static void +process_data (double *data, int count, int channels) +{ double channel_gain [MAX_CHANNELS] = { 0.5, 0.8, 0.1, 0.4, 0.4, 0.9 } ; + int k, chan ; + + /* Process the data here. + ** If the soundfile contains more then 1 channel you need to take care of + ** the data interleaving youself. + ** Current we just apply a channel dependant gain. + */ + + for (chan = 0 ; chan < channels ; chan ++) + for (k = chan ; k < count ; k+= channels) + data [k] *= channel_gain [chan] ; + + return ; +} /* process_data */ + + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: de9fdd1e-b807-41ef-9d51-075ba383e536 +*/ diff --git a/examples/sndfile-convert.c b/examples/sndfile-convert.c new file mode 100644 index 00000000..7072382e --- /dev/null +++ b/examples/sndfile-convert.c @@ -0,0 +1,395 @@ +/* +** Copyright (C) 1999-2005 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + + +#include +#include +#include +#include + +#include + +#define BUFFER_LEN 1024 + + +typedef struct +{ char *infilename, *outfilename ; + SF_INFO infileinfo, outfileinfo ; +} OptionData ; + +typedef struct +{ const char *ext ; + int len ; + int format ; +} OUTPUT_FORMAT_MAP ; + +static void copy_metadata (SNDFILE *outfile, SNDFILE *infile) ; +static void copy_data_fp (SNDFILE *outfile, SNDFILE *infile, int channels) ; +static void copy_data_int (SNDFILE *outfile, SNDFILE *infile, int channels) ; + +static OUTPUT_FORMAT_MAP format_map [] = +{ + { "aif", 3, SF_FORMAT_AIFF }, + { "wav", 0, SF_FORMAT_WAV }, + { "au", 0, SF_FORMAT_AU }, + { "caf", 0, SF_FORMAT_CAF }, + { "flac", 0, SF_FORMAT_FLAC }, + { "snd", 0, SF_FORMAT_AU }, + { "svx", 0, SF_FORMAT_SVX }, + { "paf", 0, SF_ENDIAN_BIG | SF_FORMAT_PAF }, + { "fap", 0, SF_ENDIAN_LITTLE | SF_FORMAT_PAF }, + { "gsm", 0, SF_FORMAT_RAW }, + { "nist", 0, SF_FORMAT_NIST }, + { "ircam", 0, SF_FORMAT_IRCAM }, + { "sf", 0, SF_FORMAT_IRCAM }, + { "voc", 0, SF_FORMAT_VOC }, + { "w64", 0, SF_FORMAT_W64 }, + { "raw", 0, SF_FORMAT_RAW }, + { "mat4", 0, SF_FORMAT_MAT4 }, + { "mat5", 0, SF_FORMAT_MAT5 }, + { "mat", 0, SF_FORMAT_MAT4 }, + { "pvf", 0, SF_FORMAT_PVF }, + { "sds", 0, SF_FORMAT_SDS }, + { "sd2", 0, SF_FORMAT_SD2 }, + { "vox", 0, SF_FORMAT_RAW }, + { "xi", 0, SF_FORMAT_XI }, + { "wve", 0, SF_FORMAT_WVE } +} ; /* format_map */ + +static int +guess_output_file_type (char *str, int format) +{ char buffer [16], *cptr ; + int k ; + + format &= SF_FORMAT_SUBMASK ; + + if ((cptr = strrchr (str, '.')) == NULL) + return 0 ; + + strncpy (buffer, cptr + 1, 15) ; + buffer [15] = 0 ; + + for (k = 0 ; buffer [k] ; k++) + buffer [k] = tolower ((buffer [k])) ; + + if (strcmp (buffer, "gsm") == 0) + return SF_FORMAT_RAW | SF_FORMAT_GSM610 ; + + if (strcmp (buffer, "vox") == 0) + return SF_FORMAT_RAW | SF_FORMAT_VOX_ADPCM ; + + for (k = 0 ; k < (int) (sizeof (format_map) / sizeof (format_map [0])) ; k++) + { if (format_map [k].len > 0 && strncmp (buffer, format_map [k].ext, format_map [k].len) == 0) + return format_map [k].format | format ; + else if (strcmp (buffer, format_map [k].ext) == 0) + return format_map [k].format | format ; + } ; + + return 0 ; +} /* guess_output_file_type */ + + +static void +print_usage (char *progname) +{ SF_FORMAT_INFO info ; + + int k ; + + printf ("\nUsage : %s [options] [encoding] \n", progname) ; + puts ("\n" + " where [option] may be:\n\n" + " -override-sample-rate=X : force sample rate of input to X\n\n" + ) ; + + puts ("\n" + " where [encoding] may be one of the following:\n\n" + " -pcms8 : force the output to signed 8 bit pcm\n" + " -pcmu8 : force the output to unsigned 8 bit pcm\n" + " -pcm16 : force the output to 16 bit pcm\n" + " -pcm24 : force the output to 24 bit pcm\n" + " -pcm32 : force the output to 32 bit pcm\n" + " -float32 : force the output to 32 bit floating point" + ) ; + puts ( + " -ulaw : force the output ULAW\n" + " -alaw : force the output ALAW\n" + " -ima-adpcm : force the output to IMA ADPCM (WAV only)\n" + " -ms-adpcm : force the output to MS ADPCM (WAV only)\n" + " -gsm610 : force the GSM6.10 (WAV only)\n" + " -dwvw12 : force the output to 12 bit DWVW (AIFF only)\n" + " -dwvw16 : force the output to 16 bit DWVW (AIFF only)\n" + " -dwvw24 : force the output to 24 bit DWVW (AIFF only)\n" + ) ; + + puts ( + " The format of the output file is determined by the file extension of the\n" + " output file name. The following extensions are currently understood:\n" + ) ; + + for (k = 0 ; k < (int) (sizeof (format_map) / sizeof (format_map [0])) ; k++) + { info.format = format_map [k].format ; + sf_command (NULL, SFC_GET_FORMAT_INFO, &info, sizeof (info)) ; + printf (" %-10s : %s\n", format_map [k].ext, info.name) ; + } ; + + puts ("") ; +} /* print_usage */ + +int +main (int argc, char * argv []) +{ char *progname, *infilename, *outfilename ; + SNDFILE *infile = NULL, *outfile = NULL ; + SF_INFO sfinfo ; + int k, outfilemajor, outfileminor = 0, infileminor ; + int override_sample_rate = 0 ; /* assume no sample rate override. */ + + progname = strrchr (argv [0], '/') ; + progname = progname ? progname + 1 : argv [0] ; + + if (argc < 3 || argc > 5) + { print_usage (progname) ; + return 1 ; + } ; + + infilename = argv [argc-2] ; + outfilename = argv [argc-1] ; + + if (strcmp (infilename, outfilename) == 0) + { printf ("Error : Input and output filenames are the same.\n\n") ; + print_usage (progname) ; + return 1 ; + } ; + + if (infilename [0] == '-') + { printf ("Error : Input filename (%s) looks like an option.\n\n", infilename) ; + print_usage (progname) ; + return 1 ; + } ; + + if (outfilename [0] == '-') + { printf ("Error : Output filename (%s) looks like an option.\n\n", outfilename) ; + print_usage (progname) ; + return 1 ; + } ; + + for (k = 1 ; k < argc - 2 ; k++) + { if (! strcmp (argv [k], "-pcms8")) + { outfileminor = SF_FORMAT_PCM_S8 ; + continue ; + } ; + if (! strcmp (argv [k], "-pcmu8")) + { outfileminor = SF_FORMAT_PCM_U8 ; + continue ; + } ; + if (! strcmp (argv [k], "-pcm16")) + { outfileminor = SF_FORMAT_PCM_16 ; + continue ; + } ; + if (! strcmp (argv [k], "-pcm24")) + { outfileminor = SF_FORMAT_PCM_24 ; + continue ; + } ; + if (! strcmp (argv [k], "-pcm32")) + { outfileminor = SF_FORMAT_PCM_32 ; + continue ; + } ; + if (! strcmp (argv [k], "-float32")) + { outfileminor = SF_FORMAT_FLOAT ; + continue ; + } ; + if (! strcmp (argv [k], "-ulaw")) + { outfileminor = SF_FORMAT_ULAW ; + continue ; + } ; + if (! strcmp (argv [k], "-alaw")) + { outfileminor = SF_FORMAT_ALAW ; + continue ; + } ; + if (! strcmp (argv [k], "-ima-adpcm")) + { outfileminor = SF_FORMAT_IMA_ADPCM ; + continue ; + } ; + if (! strcmp (argv [k], "-ms-adpcm")) + { outfileminor = SF_FORMAT_MS_ADPCM ; + continue ; + } ; + if (! strcmp (argv [k], "-gsm610")) + { outfileminor = SF_FORMAT_GSM610 ; + continue ; + } ; + if (! strcmp (argv [k], "-dwvw12")) + { outfileminor = SF_FORMAT_DWVW_12 ; + continue ; + } ; + if (! strcmp (argv [k], "-dwvw16")) + { outfileminor = SF_FORMAT_DWVW_16 ; + continue ; + } ; + if (! strcmp (argv [k], "-dwvw24")) + { outfileminor = SF_FORMAT_DWVW_24 ; + continue ; + } ; + + if (! strcmp (argv [k], "-override-sample-rate=")) + { char *ptr ; + + ptr = argv [k] + strlen ("-override-sample-rate=") ; + override_sample_rate = atoi (ptr) ; + continue ; + } ; + + printf ("Error : Not able to decode argunment '%s'.\n", argv [k]) ; + exit (1) ; + } ; + + if ((infile = sf_open (infilename, SFM_READ, &sfinfo)) == NULL) + { printf ("Not able to open input file %s.\n", infilename) ; + puts (sf_strerror (NULL)) ; + return 1 ; + } ; + + /* Update sample rate if forced to something else. */ + if (override_sample_rate) + sfinfo.samplerate=override_sample_rate ; + + infileminor = sfinfo.format & SF_FORMAT_SUBMASK ; + + if ((sfinfo.format = guess_output_file_type (outfilename, sfinfo.format)) == 0) + { printf ("Error : Not able to determine output file type for %s.\n", outfilename) ; + return 1 ; + } ; + + outfilemajor = sfinfo.format & (SF_FORMAT_TYPEMASK | SF_FORMAT_ENDMASK) ; + + if (outfileminor == 0) + outfileminor = sfinfo.format & SF_FORMAT_SUBMASK ; + + if (outfileminor != 0) + sfinfo.format = outfilemajor | outfileminor ; + else + sfinfo.format = outfilemajor | (sfinfo.format & SF_FORMAT_SUBMASK) ; + + if ((sfinfo.format & SF_FORMAT_TYPEMASK) == SF_FORMAT_XI) + switch (sfinfo.format & SF_FORMAT_SUBMASK) + { case SF_FORMAT_PCM_16 : + sfinfo.format = outfilemajor | SF_FORMAT_DPCM_16 ; + break ; + + case SF_FORMAT_PCM_S8 : + case SF_FORMAT_PCM_U8 : + sfinfo.format = outfilemajor | SF_FORMAT_DPCM_8 ; + break ; + } ; + + if (sf_format_check (&sfinfo) == 0) + { printf ("Error : output file format is invalid (0x%08X).\n", sfinfo.format) ; + return 1 ; + } ; + + /* Open the output file. */ + if ((outfile = sf_open (outfilename, SFM_WRITE, &sfinfo)) == NULL) + { printf ("Not able to open output file %s : %s\n", outfilename, sf_strerror (NULL)) ; + return 1 ; + } ; + + /* Copy the metadata */ + copy_metadata (outfile, infile) ; + + if ((outfileminor == SF_FORMAT_DOUBLE) || (outfileminor == SF_FORMAT_FLOAT) || + (infileminor == SF_FORMAT_DOUBLE) || (infileminor == SF_FORMAT_FLOAT)) + copy_data_fp (outfile, infile, sfinfo.channels) ; + else + copy_data_int (outfile, infile, sfinfo.channels) ; + + sf_close (infile) ; + sf_close (outfile) ; + + return 0 ; +} /* main */ + +static void +copy_metadata (SNDFILE *outfile, SNDFILE *infile) +{ SF_INSTRUMENT inst ; + const char *str ; + int k, err = 0 ; + + for (k = SF_STR_FIRST ; k <= SF_STR_LAST ; k++) + { str = sf_get_string (infile, k) ; + if (str != NULL) + err = sf_set_string (outfile, k, str) ; + } ; + + memset (&inst, 0, sizeof (inst)) ; + if (sf_command (infile, SFC_GET_INSTRUMENT, &inst, sizeof (inst)) == SF_TRUE) + sf_command (outfile, SFC_SET_INSTRUMENT, &inst, sizeof (inst)) ; + +} /* copy_metadata */ + +static void +copy_data_fp (SNDFILE *outfile, SNDFILE *infile, int channels) +{ static double data [BUFFER_LEN], max ; + int frames, readcount, k ; + + frames = BUFFER_LEN / channels ; + readcount = frames ; + + sf_command (infile, SFC_CALC_SIGNAL_MAX, &max, sizeof (max)) ; + + if (max < 1.0) + { while (readcount > 0) + { readcount = sf_readf_double (infile, data, frames) ; + sf_writef_double (outfile, data, readcount) ; + } ; + } + else + { sf_command (infile, SFC_SET_NORM_DOUBLE, NULL, SF_FALSE) ; + + while (readcount > 0) + { readcount = sf_readf_double (infile, data, frames) ; + for (k = 0 ; k < readcount * channels ; k++) + data [k] /= max ; + sf_writef_double (outfile, data, readcount) ; + } ; + } ; + + return ; +} /* copy_data_fp */ + +static void +copy_data_int (SNDFILE *outfile, SNDFILE *infile, int channels) +{ static int data [BUFFER_LEN] ; + int frames, readcount ; + + frames = BUFFER_LEN / channels ; + readcount = frames ; + + while (readcount > 0) + { readcount = sf_readf_int (infile, data, frames) ; + sf_writef_int (outfile, data, readcount) ; + } ; + + return ; +} /* copy_data_int */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 259682b3-2887-48a6-b5bb-3cde00521ba3 +*/ diff --git a/examples/sndfile-data-trim.c b/examples/sndfile-data-trim.c new file mode 100644 index 00000000..25b8f921 --- /dev/null +++ b/examples/sndfile-data-trim.c @@ -0,0 +1,65 @@ +/* +** Copyright (C) 2007 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + + +#include +#include +#include +#include + +#include + +static void +usage_exit (const char * progname) +{ + printf ("\nUsage : %s \n\n", progname) ; + exit (0) ; +} /* usage_exit */ + +int +main (int argc, char * argv []) +{ char *progname, *infilename, *outfilename ; + SNDFILE *infile = NULL ; + SF_INFO sfinfo ; + + progname = strrchr (argv [0], '/') ; + progname = progname ? progname + 1 : argv [0] ; + + if (argc != 3) + usage_exit (progname) ; + + infilename = argv [argc-2] ; + outfilename = argv [argc-1] ; + + if (strcmp (infilename, outfilename) == 0) + { printf ("Error : Input and output filenames are the same.\n\n") ; + usage_exit (progname) ; + } ; + + + if ((infile = sf_open (infilename, SFM_READ, &sfinfo)) == NULL) + { printf ("Not able to open input file %s.\n", infilename) ; + puts (sf_strerror (NULL)) ; + return 1 ; + } ; + + sf_close (infile) ; + + return 0 ; +} /* main */ + diff --git a/examples/sndfile-info.c b/examples/sndfile-info.c new file mode 100644 index 00000000..44e2ec17 --- /dev/null +++ b/examples/sndfile-info.c @@ -0,0 +1,354 @@ +/* +** Copyright (C) 1999-2006 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include +#include +#include +#include + +#include + +#define BUFFER_LEN (1 << 16) + +#if (defined (WIN32) || defined (_WIN32)) +#define snprintf _snprintf +#endif + +static void print_version (void) ; +static void print_usage (const char *progname) ; + +static void info_dump (const char *filename) ; +static void instrument_dump (const char *filename) ; +static void broadcast_dump (const char *filename) ; + +int +main (int argc, char *argv []) +{ int k ; + + print_version () ; + + if (argc < 2 || strcmp (argv [1], "--help") == 0 || strcmp (argv [1], "-h") == 0) + { char *progname ; + + progname = strrchr (argv [0], '/') ; + progname = progname ? progname + 1 : argv [0] ; + + print_usage (progname) ; + return 1 ; + } ; + + if (strcmp (argv [1], "-i") == 0) + { instrument_dump (argv [2]) ; + return 0 ; + } ; + + if (strcmp (argv [1], "-b") == 0) + { broadcast_dump (argv [2]) ; + return 0 ; + } ; + + for (k = 1 ; k < argc ; k++) + info_dump (argv [k]) ; + + return 0 ; +} /* main */ + +/*============================================================================== +** Print version and usage. +*/ + +static double data [BUFFER_LEN] ; + +static void +print_version (void) +{ char buffer [256] ; + + sf_command (NULL, SFC_GET_LIB_VERSION, buffer, sizeof (buffer)) ; + printf ("\nVersion : %s\n\n", buffer) ; +} /* print_version */ + + +static void +print_usage (const char *progname) +{ printf ("Usage :\n %s ...\n", progname) ; + printf (" Prints out information about one or more sound files.\n\n") ; + printf (" %s -i \n", progname) ; + printf (" Prints out the instrument data for the given file.\n\n") ; + printf (" %s -b \n", progname) ; + printf (" Prints out the broadcast WAV info for the given file.\n\n") ; +#if (defined (_WIN32) || defined (WIN32)) + printf ("This is a Unix style command line application which\n" + "should be run in a MSDOS box or Command Shell window.\n\n") ; + printf ("Sleeping for 5 seconds before exiting.\n\n") ; + fflush (stdout) ; + + /* This is the officially blessed by microsoft way but I can't get + ** it to link. + ** Sleep (15) ; + ** Instead, use this: + */ + _sleep (5 * 1000) ; +#endif +} /* print_usage */ + +/*============================================================================== +** Dumping of sndfile info. +*/ + +static double data [BUFFER_LEN] ; + +static double +get_signal_max (SNDFILE *file) +{ double max, temp ; + int readcount, k, save_state ; + + save_state = sf_command (file, SFC_GET_NORM_DOUBLE, NULL, 0) ; + sf_command (file, SFC_SET_NORM_DOUBLE, NULL, SF_FALSE) ; + + max = 0.0 ; + while ((readcount = sf_read_double (file, data, BUFFER_LEN))) + { for (k = 0 ; k < readcount ; k++) + { temp = fabs (data [k]) ; + if (temp > max) + max = temp ; + } ; + } ; + + sf_command (file, SFC_SET_NORM_DOUBLE, NULL, save_state) ; + + return max ; +} /* get_signal_max */ + +static double +calc_decibels (SF_INFO * sfinfo, double max) +{ double decibels ; + + switch (sfinfo->format & SF_FORMAT_SUBMASK) + { case SF_FORMAT_PCM_U8 : + case SF_FORMAT_PCM_S8 : + decibels = max / 0x80 ; + break ; + + case SF_FORMAT_PCM_16 : + decibels = max / 0x8000 ; + break ; + + case SF_FORMAT_PCM_24 : + decibels = max / 0x800000 ; + break ; + + case SF_FORMAT_PCM_32 : + decibels = max / 0x80000000 ; + break ; + + case SF_FORMAT_FLOAT : + case SF_FORMAT_DOUBLE : + decibels = max / 1.0 ; + break ; + + default : + decibels = max / 0x8000 ; + break ; + } ; + + return 20.0 * log10 (decibels) ; +} /* calc_decibels */ + +static const char * +generate_duration_str (SF_INFO *sfinfo) +{ static char str [128] ; + + int seconds ; + + memset (str, 0, sizeof (str)) ; + + if (sfinfo->samplerate < 1) + return NULL ; + + if (sfinfo->frames / sfinfo->samplerate > 0x7FFFFFFF) + return "unknown" ; + + seconds = sfinfo->frames / sfinfo->samplerate ; + + snprintf (str, sizeof (str) - 1, "%02d:", seconds / 60 / 60) ; + + seconds = seconds % (60 * 60) ; + snprintf (str + strlen (str), sizeof (str) - strlen (str) - 1, "%02d:", seconds / 60) ; + + seconds = seconds % 60 ; + snprintf (str + strlen (str), sizeof (str) - strlen (str) - 1, "%02d.", seconds) ; + + seconds = ((1000 * sfinfo->frames) / sfinfo->samplerate) % 1000 ; + snprintf (str + strlen (str), sizeof (str) - strlen (str) - 1, "%03d", seconds) ; + + return str ; +} /* generate_duration_str */ + +static void +info_dump (const char *filename) +{ static char strbuffer [BUFFER_LEN] ; + SNDFILE *file ; + SF_INFO sfinfo ; + double signal_max, decibels ; + + memset (&sfinfo, 0, sizeof (sfinfo)) ; + + if ((file = sf_open (filename, SFM_READ, &sfinfo)) == NULL) + { printf ("Error : Not able to open input file %s.\n", filename) ; + fflush (stdout) ; + memset (data, 0, sizeof (data)) ; + sf_command (file, SFC_GET_LOG_INFO, strbuffer, BUFFER_LEN) ; + puts (strbuffer) ; + puts (sf_strerror (NULL)) ; + return ; + } ; + + printf ("========================================\n") ; + sf_command (file, SFC_GET_LOG_INFO, strbuffer, BUFFER_LEN) ; + puts (strbuffer) ; + printf ("----------------------------------------\n") ; + + if (file == NULL) + { printf ("Error : Not able to open input file %s.\n", filename) ; + fflush (stdout) ; + memset (data, 0, sizeof (data)) ; + puts (sf_strerror (NULL)) ; + } + else + { printf ("Sample Rate : %d\n", sfinfo.samplerate) ; + if (sfinfo.frames > 0x7FFFFFFF) + printf ("Frames : unknown\n") ; + else + printf ("Frames : %ld\n", (long) sfinfo.frames) ; + printf ("Channels : %d\n", sfinfo.channels) ; + printf ("Format : 0x%08X\n", sfinfo.format) ; + printf ("Sections : %d\n", sfinfo.sections) ; + printf ("Seekable : %s\n", (sfinfo.seekable ? "TRUE" : "FALSE")) ; + printf ("Duration : %s\n", generate_duration_str (&sfinfo)) ; + + /* Do not use sf_signal_max because it doesn work for non-seekable files . */ + signal_max = get_signal_max (file) ; + decibels = calc_decibels (&sfinfo, signal_max) ; + printf ("Signal Max : %g (%4.2f dB)\n\n", signal_max, decibels) ; + } ; + + sf_close (file) ; + +} /* info_dump */ + +/*============================================================================== +** Dumping of SF_INSTRUMENT data. +*/ + +static const char * +str_of_type (int mode) +{ switch (mode) + { case SF_LOOP_NONE : return "none" ; + case SF_LOOP_FORWARD : return "fwd " ; + case SF_LOOP_BACKWARD : return "back" ; + case SF_LOOP_ALTERNATING : return "alt " ; + default : break ; + } ; + + return "????" ; +} /* str_of_mode */ + +static void +instrument_dump (const char *filename) +{ SNDFILE *file ; + SF_INFO sfinfo ; + SF_INSTRUMENT inst ; + int got_inst, k ; + + memset (&sfinfo, 0, sizeof (sfinfo)) ; + + if ((file = sf_open (filename, SFM_READ, &sfinfo)) == NULL) + { printf ("Error : Not able to open input file %s.\n", filename) ; + fflush (stdout) ; + memset (data, 0, sizeof (data)) ; + puts (sf_strerror (NULL)) ; + return ; + } ; + + got_inst = sf_command (file, SFC_GET_INSTRUMENT, &inst, sizeof (inst)) ; + sf_close (file) ; + + if (got_inst == SF_FALSE) + { printf ("Error : File '%s' does not contain instrument data.\n\n", filename) ; + return ; + } ; + + printf ("Instrument : %s\n\n", filename) ; + printf (" Gain : %d\n", inst.gain) ; + printf (" Base note : %d\n", inst.basenote) ; + printf (" Velocity : %d - %d\n", (int) inst.velocity_lo, (int) inst.velocity_hi) ; + printf (" Key : %d - %d\n", (int) inst.key_lo, (int) inst.key_hi) ; + printf (" Loop points : %d\n", inst.loop_count) ; + + for (k = 0 ; k < inst.loop_count ; k++) + printf (" %-2d Mode : %s Start : %6d End : %6d Count : %6d\n", k, str_of_type (inst.loops [k].mode), inst.loops [k].start, inst.loops [k].end, inst.loops [k].count) ; + + putchar ('\n') ; +} /* instrument_dump */ + +static void +broadcast_dump (const char *filename) +{ SNDFILE *file ; + SF_INFO sfinfo ; + SF_BROADCAST_INFO bext ; + int got_bext ; + + memset (&sfinfo, 0, sizeof (sfinfo)) ; + + if ((file = sf_open (filename, SFM_READ, &sfinfo)) == NULL) + { printf ("Error : Not able to open input file %s.\n", filename) ; + fflush (stdout) ; + memset (data, 0, sizeof (data)) ; + puts (sf_strerror (NULL)) ; + return ; + } ; + + memset (&bext, 0, sizeof (SF_BROADCAST_INFO)) ; + + got_bext = sf_command (file, SFC_GET_BROADCAST_INFO, &bext, sizeof (bext)) ; + sf_close (file) ; + + if (got_bext == SF_FALSE) + { printf ("Error : File '%s' does not contain broadcast information.\n\n", filename) ; + return ; + } ; + + printf ("Description : %.*s\n", (int) sizeof (bext.description), bext.description) ; + printf ("Originator : %.*s\n", (int) sizeof (bext.originator), bext.originator) ; + printf ("Origination ref : %.*s\n", (int) sizeof (bext.originator_reference), bext.originator_reference) ; + printf ("Origination date : %.*s\n", (int) sizeof (bext.origination_date), bext.origination_date) ; + printf ("Origination time : %.*s\n", (int) sizeof (bext.origination_time), bext.origination_time) ; + printf ("BWF version : %d\n", bext.version) ; + printf ("UMID : %.*s\n", (int) sizeof (bext.umid), bext.umid) ; + printf ("Coding history : %.*s\n", bext.coding_history_size, bext.coding_history) ; + +} /* broadcast_dump */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: f59a05db-a182-41de-aedd-d717ce2bb099 +*/ diff --git a/examples/sndfile-play-beos.cpp b/examples/sndfile-play-beos.cpp new file mode 100644 index 00000000..56f74151 --- /dev/null +++ b/examples/sndfile-play-beos.cpp @@ -0,0 +1,153 @@ +/* +** Copyright (C) 2001 Marcus Overhagen +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include + +#include +#include +#include + +#include + +#define BUFFER_LEN 1024 + +/*------------------------------------------------------------------------------ +** BeOS functions for playing a sound. +*/ + +#if defined (__BEOS__) + +struct shared_data +{ + BSoundPlayer *player; + SNDFILE *sndfile; + SF_INFO sfinfo; + sem_id finished; +}; + +static void +buffer_callback(void *theCookie, void *buf, size_t size, const media_raw_audio_format &format) +{ + shared_data *data = (shared_data *)theCookie; + short *buffer = (short *)buf; + int count = size / sizeof(short); + int m, readcount; + + if (!data->player->HasData()) + return; + + readcount = sf_read_short(data->sndfile, buffer, count); + if (readcount == 0) + { data->player->SetHasData(false); + release_sem(data->finished); + } + if (readcount < count) + { for (m = readcount ; m < count ; m++) + buffer [m] = 0 ; + } + if (data->sfinfo.pcmbitwidth < 16) + { for (m = 0 ; m < count ; m++) + buffer [m] *= 256 ; + } +} + +static void +beos_play (int argc, char *argv []) +{ + shared_data data; + status_t status; + int k; + + /* BSoundPlayer requires a BApplication object */ + BApplication app("application/x-vnd.MarcusOverhagen-sfplay"); + + for (k = 1 ; k < argc ; k++) + { printf ("Playing %s\n", argv [k]) ; + if (! (data.sndfile = sf_open_read (argv [k], &data.sfinfo))) + { sf_perror (NULL) ; + continue ; + } ; + + if (data.sfinfo.channels < 1 || data.sfinfo.channels > 2) + { printf ("Error : channels = %d.\n", data.sfinfo.channels) ; + sf_close (data.sndfile) ; + continue ; + } ; + + data.finished = create_sem(0,"finished"); + + media_raw_audio_format format = + { data.sfinfo.samplerate, + data.sfinfo.channels, + media_raw_audio_format::B_AUDIO_SHORT, + B_HOST_IS_LENDIAN ? B_MEDIA_LITTLE_ENDIAN : B_MEDIA_BIG_ENDIAN, + BUFFER_LEN * sizeof(short) + }; + + BSoundPlayer player(&format,"player",buffer_callback,NULL,&data); + data.player = &player; + + if ((status = player.InitCheck()) != B_OK) + { + printf ("Error : BSoundPlayer init failed, %s.\n", strerror(status)) ; + delete_sem(data.finished); + sf_close (data.sndfile) ; + continue ; + } + + player.SetVolume(1.0); + player.Start(); + player.SetHasData(true); + acquire_sem(data.finished); + player.Stop(); + delete_sem(data.finished); + + sf_close (data.sndfile) ; + + } ; + +} /* beos_play */ + +#endif + +/*============================================================================== +** Main function. +*/ + +int +main (int argc, char *argv []) +{ + if (argc < 2) + { printf ("Usage : %s \n\n", argv [0]) ; + return 1 ; + } ; + + beos_play (argc, argv) ; + + return 0 ; +} /* main */ + + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 5407a79d-88de-41c7-8d8e-9acf2cf13cc1 +*/ + diff --git a/examples/sndfile-play.c b/examples/sndfile-play.c new file mode 100644 index 00000000..60ebf93a --- /dev/null +++ b/examples/sndfile-play.c @@ -0,0 +1,960 @@ +/* +** Copyright (C) 1999-2005 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include +#include + +#if HAVE_UNISTD_H +#include +#endif + +#if HAVE_ALSA_ASOUNDLIB_H + #define ALSA_PCM_NEW_HW_PARAMS_API + #define ALSA_PCM_NEW_SW_PARAMS_API + #include + #include +#endif + +#if defined (__linux__) + #include + #include + #include + +#elif (defined (__MACH__) && defined (__APPLE__)) + #include + #include + +#elif (defined (sun) && defined (unix)) + #include + #include + #include + +#elif (OS_IS_WIN32 == 1) + #include + #include + +#endif + +#include + +#define SIGNED_SIZEOF(x) ((int) sizeof (x)) +#define BUFFER_LEN (2048) + +/*------------------------------------------------------------------------------ +** Linux/OSS functions for playing a sound. +*/ + +#if HAVE_ALSA_ASOUNDLIB_H + +static snd_pcm_t * alsa_open (int channels, unsigned srate, int realtime) ; +static int alsa_write_float (snd_pcm_t *alsa_dev, float *data, int frames, int channels) ; + +static void +alsa_play (int argc, char *argv []) +{ static float buffer [BUFFER_LEN] ; + SNDFILE *sndfile ; + SF_INFO sfinfo ; + snd_pcm_t * alsa_dev ; + int k, readcount, subformat ; + + for (k = 1 ; k < argc ; k++) + { memset (&sfinfo, 0, sizeof (sfinfo)) ; + + printf ("Playing %s\n", argv [k]) ; + if (! (sndfile = sf_open (argv [k], SFM_READ, &sfinfo))) + { puts (sf_strerror (NULL)) ; + continue ; + } ; + + if (sfinfo.channels < 1 || sfinfo.channels > 2) + { printf ("Error : channels = %d.\n", sfinfo.channels) ; + continue ; + } ; + + if ((alsa_dev = alsa_open (sfinfo.channels, (unsigned) sfinfo.samplerate, SF_FALSE)) == NULL) + continue ; + + subformat = sfinfo.format & SF_FORMAT_SUBMASK ; + + if (subformat == SF_FORMAT_FLOAT || subformat == SF_FORMAT_DOUBLE) + { double scale ; + int m ; + + sf_command (sndfile, SFC_CALC_SIGNAL_MAX, &scale, sizeof (scale)) ; + if (scale < 1e-10) + scale = 1.0 ; + else + scale = 32700.0 / scale ; + + while ((readcount = sf_read_float (sndfile, buffer, BUFFER_LEN))) + { for (m = 0 ; m < readcount ; m++) + buffer [m] *= scale ; + alsa_write_float (alsa_dev, buffer, BUFFER_LEN / sfinfo.channels, sfinfo.channels) ; + } ; + } + else + { while ((readcount = sf_read_float (sndfile, buffer, BUFFER_LEN))) + alsa_write_float (alsa_dev, buffer, BUFFER_LEN / sfinfo.channels, sfinfo.channels) ; + } ; + + snd_pcm_drain (alsa_dev) ; + snd_pcm_close (alsa_dev) ; + + sf_close (sndfile) ; + } ; + + return ; +} /* alsa_play */ + +static snd_pcm_t * +alsa_open (int channels, unsigned samplerate, int realtime) +{ const char * device = "default" ; + snd_pcm_t *alsa_dev = NULL ; + snd_pcm_hw_params_t *hw_params ; + snd_pcm_uframes_t buffer_size, xfer_align, start_threshold ; + snd_pcm_uframes_t alsa_period_size, alsa_buffer_frames ; + snd_pcm_sw_params_t *sw_params ; + + int err ; + + if (realtime) + { alsa_period_size = 256 ; + alsa_buffer_frames = 3 * alsa_period_size ; + } + else + { alsa_period_size = 1024 ; + alsa_buffer_frames = 4 * alsa_period_size ; + } ; + + if ((err = snd_pcm_open (&alsa_dev, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) + { fprintf (stderr, "cannot open audio device \"%s\" (%s)\n", device, snd_strerror (err)) ; + goto catch_error ; + } ; + + snd_pcm_nonblock (alsa_dev, 0) ; + + if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) + { fprintf (stderr, "cannot allocate hardware parameter structure (%s)\n", snd_strerror (err)) ; + goto catch_error ; + } ; + + if ((err = snd_pcm_hw_params_any (alsa_dev, hw_params)) < 0) + { fprintf (stderr, "cannot initialize hardware parameter structure (%s)\n", snd_strerror (err)) ; + goto catch_error ; + } ; + + if ((err = snd_pcm_hw_params_set_access (alsa_dev, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) + { fprintf (stderr, "cannot set access type (%s)\n", snd_strerror (err)) ; + goto catch_error ; + } ; + + if ((err = snd_pcm_hw_params_set_format (alsa_dev, hw_params, SND_PCM_FORMAT_FLOAT)) < 0) + { fprintf (stderr, "cannot set sample format (%s)\n", snd_strerror (err)) ; + goto catch_error ; + } ; + + if ((err = snd_pcm_hw_params_set_rate_near (alsa_dev, hw_params, &samplerate, 0)) < 0) + { fprintf (stderr, "cannot set sample rate (%s)\n", snd_strerror (err)) ; + goto catch_error ; + } ; + + if ((err = snd_pcm_hw_params_set_channels (alsa_dev, hw_params, channels)) < 0) + { fprintf (stderr, "cannot set channel count (%s)\n", snd_strerror (err)) ; + goto catch_error ; + } ; + + if ((err = snd_pcm_hw_params_set_buffer_size_near (alsa_dev, hw_params, &alsa_buffer_frames)) < 0) + { fprintf (stderr, "cannot set buffer size (%s)\n", snd_strerror (err)) ; + goto catch_error ; + } ; + + if ((err = snd_pcm_hw_params_set_period_size_near (alsa_dev, hw_params, &alsa_period_size, 0)) < 0) + { fprintf (stderr, "cannot set period size (%s)\n", snd_strerror (err)) ; + goto catch_error ; + } ; + + if ((err = snd_pcm_hw_params (alsa_dev, hw_params)) < 0) + { fprintf (stderr, "cannot set parameters (%s)\n", snd_strerror (err)) ; + goto catch_error ; + } ; + + /* extra check: if we have only one period, this code won't work */ + snd_pcm_hw_params_get_period_size (hw_params, &alsa_period_size, 0) ; + snd_pcm_hw_params_get_buffer_size (hw_params, &buffer_size) ; + if (alsa_period_size == buffer_size) + { fprintf (stderr, "Can't use period equal to buffer size (%lu == %lu)", alsa_period_size, buffer_size) ; + goto catch_error ; + } ; + + snd_pcm_hw_params_free (hw_params) ; + + if ((err = snd_pcm_sw_params_malloc (&sw_params)) != 0) + { fprintf (stderr, "%s: snd_pcm_sw_params_malloc: %s", __func__, snd_strerror (err)) ; + goto catch_error ; + } ; + + if ((err = snd_pcm_sw_params_current (alsa_dev, sw_params)) != 0) + { fprintf (stderr, "%s: snd_pcm_sw_params_current: %s", __func__, snd_strerror (err)) ; + goto catch_error ; + } ; + + /* note: set start threshold to delay start until the ring buffer is full */ + snd_pcm_sw_params_current (alsa_dev, sw_params) ; + if ((err = snd_pcm_sw_params_get_xfer_align (sw_params, &xfer_align)) < 0) + { fprintf (stderr, "cannot get xfer align (%s)\n", snd_strerror (err)) ; + goto catch_error ; + } ; + + /* round up to closest transfer boundary */ + start_threshold = (buffer_size / xfer_align) * xfer_align ; + if (start_threshold < 1) + start_threshold = 1 ; + if ((err = snd_pcm_sw_params_set_start_threshold (alsa_dev, sw_params, start_threshold)) < 0) + { fprintf (stderr, "cannot set start threshold (%s)\n", snd_strerror (err)) ; + goto catch_error ; + } ; + + if ((err = snd_pcm_sw_params (alsa_dev, sw_params)) != 0) + { fprintf (stderr, "%s: snd_pcm_sw_params: %s", __func__, snd_strerror (err)) ; + goto catch_error ; + } ; + + snd_pcm_sw_params_free (sw_params) ; + + snd_pcm_reset (alsa_dev) ; + +catch_error : + + if (err < 0 && alsa_dev != NULL) + { snd_pcm_close (alsa_dev) ; + return NULL ; + } ; + + return alsa_dev ; +} /* alsa_open */ + +static int +alsa_write_float (snd_pcm_t *alsa_dev, float *data, int frames, int channels) +{ static int epipe_count = 0 ; + + snd_pcm_status_t *status ; + int total = 0 ; + int retval ; + + if (epipe_count > 0) + epipe_count -- ; + + while (total < frames) + { retval = snd_pcm_writei (alsa_dev, data + total * channels, frames - total) ; + + if (retval >= 0) + { total += retval ; + if (total == frames) + return total ; + + continue ; + } ; + + switch (retval) + { case -EAGAIN : + puts ("alsa_write_float: EAGAIN") ; + continue ; + break ; + + case -EPIPE : + if (epipe_count > 0) + { printf ("alsa_write_float: EPIPE %d\n", epipe_count) ; + if (epipe_count > 140) + return retval ; + } ; + epipe_count += 100 ; + + if (0) + { snd_pcm_status_alloca (&status) ; + if ((retval = snd_pcm_status (alsa_dev, status)) < 0) + fprintf (stderr, "alsa_out: xrun. can't determine length\n") ; + else if (snd_pcm_status_get_state (status) == SND_PCM_STATE_XRUN) + { struct timeval now, diff, tstamp ; + + gettimeofday (&now, 0) ; + snd_pcm_status_get_trigger_tstamp (status, &tstamp) ; + timersub (&now, &tstamp, &diff) ; + + fprintf (stderr, "alsa_write_float xrun: of at least %.3f msecs. resetting stream\n", + diff.tv_sec * 1000 + diff.tv_usec / 1000.0) ; + } + else + fprintf (stderr, "alsa_write_float: xrun. can't determine length\n") ; + } ; + + snd_pcm_prepare (alsa_dev) ; + break ; + + case -EBADFD : + fprintf (stderr, "alsa_write_float: Bad PCM state.n") ; + return 0 ; + break ; + + case -ESTRPIPE : + fprintf (stderr, "alsa_write_float: Suspend event.n") ; + return 0 ; + break ; + + case -EIO : + puts ("alsa_write_float: EIO") ; + return 0 ; + + default : + fprintf (stderr, "alsa_write_float: retval = %d\n", retval) ; + return 0 ; + break ; + } ; /* switch */ + } ; /* while */ + + return total ; +} /* alsa_write_float */ + +#endif /* HAVE_ALSA_ASOUNDLIB_H */ + +/*------------------------------------------------------------------------------ +** Linux/OSS functions for playing a sound. +*/ + +#if defined (__linux__) + +static int linux_open_dsp_device (int channels, int srate) ; + +static void +linux_play (int argc, char *argv []) +{ static short buffer [BUFFER_LEN] ; + SNDFILE *sndfile ; + SF_INFO sfinfo ; + int k, audio_device, readcount, subformat ; + + for (k = 1 ; k < argc ; k++) + { memset (&sfinfo, 0, sizeof (sfinfo)) ; + + printf ("Playing %s\n", argv [k]) ; + if (! (sndfile = sf_open (argv [k], SFM_READ, &sfinfo))) + { puts (sf_strerror (NULL)) ; + continue ; + } ; + + if (sfinfo.channels < 1 || sfinfo.channels > 2) + { printf ("Error : channels = %d.\n", sfinfo.channels) ; + continue ; + } ; + + audio_device = linux_open_dsp_device (sfinfo.channels, sfinfo.samplerate) ; + + subformat = sfinfo.format & SF_FORMAT_SUBMASK ; + + if (subformat == SF_FORMAT_FLOAT || subformat == SF_FORMAT_DOUBLE) + { static float float_buffer [BUFFER_LEN] ; + double scale ; + int m ; + + sf_command (sndfile, SFC_CALC_SIGNAL_MAX, &scale, sizeof (scale)) ; + if (scale < 1e-10) + scale = 1.0 ; + else + scale = 32700.0 / scale ; + + while ((readcount = sf_read_float (sndfile, float_buffer, BUFFER_LEN))) + { for (m = 0 ; m < readcount ; m++) + buffer [m] = scale * float_buffer [m] ; + write (audio_device, buffer, readcount * sizeof (short)) ; + } ; + } + else + { while ((readcount = sf_read_short (sndfile, buffer, BUFFER_LEN))) + write (audio_device, buffer, readcount * sizeof (short)) ; + } ; + + if (ioctl (audio_device, SNDCTL_DSP_POST, 0) == -1) + perror ("ioctl (SNDCTL_DSP_POST) ") ; + + if (ioctl (audio_device, SNDCTL_DSP_SYNC, 0) == -1) + perror ("ioctl (SNDCTL_DSP_SYNC) ") ; + + close (audio_device) ; + + sf_close (sndfile) ; + } ; + + return ; +} /* linux_play */ + +static int +linux_open_dsp_device (int channels, int srate) +{ int fd, stereo, fmt ; + + if ((fd = open ("/dev/dsp", O_WRONLY, 0)) == -1 && + (fd = open ("/dev/sound/dsp", O_WRONLY, 0)) == -1) + { perror ("linux_open_dsp_device : open ") ; + exit (1) ; + } ; + + stereo = 0 ; + if (ioctl (fd, SNDCTL_DSP_STEREO, &stereo) == -1) + { /* Fatal error */ + perror ("linux_open_dsp_device : stereo ") ; + exit (1) ; + } ; + + if (ioctl (fd, SNDCTL_DSP_RESET, 0)) + { perror ("linux_open_dsp_device : reset ") ; + exit (1) ; + } ; + + fmt = CPU_IS_BIG_ENDIAN ? AFMT_S16_BE : AFMT_S16_LE ; + if (ioctl (fd, SOUND_PCM_SETFMT, &fmt) != 0) + { perror ("linux_open_dsp_device : set format ") ; + exit (1) ; + } ; + + if (ioctl (fd, SOUND_PCM_WRITE_CHANNELS, &channels) != 0) + { perror ("linux_open_dsp_device : channels ") ; + exit (1) ; + } ; + + if (ioctl (fd, SOUND_PCM_WRITE_RATE, &srate) != 0) + { perror ("linux_open_dsp_device : sample rate ") ; + exit (1) ; + } ; + + if (ioctl (fd, SNDCTL_DSP_SYNC, 0) != 0) + { perror ("linux_open_dsp_device : sync ") ; + exit (1) ; + } ; + + return fd ; +} /* linux_open_dsp_device */ + +#endif /* __linux__ */ + +/*------------------------------------------------------------------------------ +** Mac OS X functions for playing a sound. +*/ + +#if (defined (__MACH__) && defined (__APPLE__)) /* MacOSX */ + +typedef struct +{ AudioStreamBasicDescription format ; + + UInt32 buf_size ; + AudioDeviceID device ; + + SNDFILE *sndfile ; + SF_INFO sfinfo ; + + int fake_stereo ; + int done_playing ; +} MacOSXAudioData ; + +#include + +static OSStatus +macosx_audio_out_callback (AudioDeviceID device, const AudioTimeStamp* current_time, + const AudioBufferList* data_in, const AudioTimeStamp* time_in, + AudioBufferList* data_out, const AudioTimeStamp* time_out, + void* client_data) +{ MacOSXAudioData *audio_data ; + int size, sample_count, read_count, k ; + float *buffer ; + + /* Prevent compiler warnings. */ + device = device ; + current_time = current_time ; + data_in = data_in ; + time_in = time_in ; + time_out = time_out ; + + audio_data = (MacOSXAudioData*) client_data ; + + size = data_out->mBuffers [0].mDataByteSize ; + sample_count = size / sizeof (float) ; + + buffer = (float*) data_out->mBuffers [0].mData ; + + if (audio_data->fake_stereo != 0) + { read_count = sf_read_float (audio_data->sndfile, buffer, sample_count / 2) ; + + for (k = read_count - 1 ; k >= 0 ; k--) + { buffer [2 * k ] = buffer [k] ; + buffer [2 * k + 1] = buffer [k] ; + } ; + read_count *= 2 ; + } + else + read_count = sf_read_float (audio_data->sndfile, buffer, sample_count) ; + + /* Fill the remainder with zeroes. */ + if (read_count < sample_count) + { if (audio_data->fake_stereo == 0) + memset (&(buffer [read_count]), 0, (sample_count - read_count) * sizeof (float)) ; + /* Tell the main application to terminate. */ + audio_data->done_playing = SF_TRUE ; + } ; + + return noErr ; +} /* macosx_audio_out_callback */ + +static void +macosx_play (int argc, char *argv []) +{ MacOSXAudioData audio_data ; + OSStatus err ; + UInt32 count, buffer_size ; + int k ; + + audio_data.fake_stereo = 0 ; + audio_data.device = kAudioDeviceUnknown ; + + /* get the default output device for the HAL */ + count = sizeof (AudioDeviceID) ; + if ((err = AudioHardwareGetProperty (kAudioHardwarePropertyDefaultOutputDevice, + &count, (void *) &(audio_data.device))) != noErr) + { printf ("AudioHardwareGetProperty (kAudioDevicePropertyDefaultOutputDevice) failed.\n") ; + return ; + } ; + + /* get the buffersize that the default device uses for IO */ + count = sizeof (UInt32) ; + if ((err = AudioDeviceGetProperty (audio_data.device, 0, false, kAudioDevicePropertyBufferSize, + &count, &buffer_size)) != noErr) + { printf ("AudioDeviceGetProperty (kAudioDevicePropertyBufferSize) failed.\n") ; + return ; + } ; + + /* get a description of the data format used by the default device */ + count = sizeof (AudioStreamBasicDescription) ; + if ((err = AudioDeviceGetProperty (audio_data.device, 0, false, kAudioDevicePropertyStreamFormat, + &count, &(audio_data.format))) != noErr) + { printf ("AudioDeviceGetProperty (kAudioDevicePropertyStreamFormat) failed.\n") ; + return ; + } ; + + /* Base setup completed. Now play files. */ + for (k = 1 ; k < argc ; k++) + { printf ("Playing %s\n", argv [k]) ; + if (! (audio_data.sndfile = sf_open (argv [k], SFM_READ, &(audio_data.sfinfo)))) + { puts (sf_strerror (NULL)) ; + continue ; + } ; + + if (audio_data.sfinfo.channels < 1 || audio_data.sfinfo.channels > 2) + { printf ("Error : channels = %d.\n", audio_data.sfinfo.channels) ; + continue ; + } ; + + audio_data.format.mSampleRate = audio_data.sfinfo.samplerate ; + + if (audio_data.sfinfo.channels == 1) + { audio_data.format.mChannelsPerFrame = 2 ; + audio_data.fake_stereo = 1 ; + } + else + audio_data.format.mChannelsPerFrame = audio_data.sfinfo.channels ; + + if ((err = AudioDeviceSetProperty (audio_data.device, NULL, 0, false, kAudioDevicePropertyStreamFormat, + sizeof (AudioStreamBasicDescription), &(audio_data.format))) != noErr) + { printf ("AudioDeviceSetProperty (kAudioDevicePropertyStreamFormat) failed.\n") ; + return ; + } ; + + /* we want linear pcm */ + if (audio_data.format.mFormatID != kAudioFormatLinearPCM) + return ; + + /* Fire off the device. */ + if ((err = AudioDeviceAddIOProc (audio_data.device, macosx_audio_out_callback, + (void *) &audio_data)) != noErr) + { printf ("AudioDeviceAddIOProc failed.\n") ; + return ; + } ; + + err = AudioDeviceStart (audio_data.device, macosx_audio_out_callback) ; + if (err != noErr) + return ; + + audio_data.done_playing = SF_FALSE ; + + while (audio_data.done_playing == SF_FALSE) + usleep (10 * 1000) ; /* 10 000 milliseconds. */ + + if ((err = AudioDeviceStop (audio_data.device, macosx_audio_out_callback)) != noErr) + { printf ("AudioDeviceStop failed.\n") ; + return ; + } ; + + err = AudioDeviceRemoveIOProc (audio_data.device, macosx_audio_out_callback) ; + if (err != noErr) + { printf ("AudioDeviceRemoveIOProc failed.\n") ; + return ; + } ; + + sf_close (audio_data.sndfile) ; + } ; + + return ; +} /* macosx_play */ + +#endif /* MacOSX */ + + +/*------------------------------------------------------------------------------ +** Win32 functions for playing a sound. +** +** This API sucks. Its needlessly complicated and is *WAY* too loose with +** passing pointers arounf in integers and and using char* pointers to +** point to data instead of short*. It plain sucks! +*/ + +#if (OS_IS_WIN32 == 1) + +#define WIN32_BUFFER_LEN (1<<15) + +typedef struct +{ HWAVEOUT hwave ; + WAVEHDR whdr [2] ; + + CRITICAL_SECTION mutex ; /* to control access to BuffersInUSe */ + HANDLE Event ; /* signal that a buffer is free */ + + short buffer [WIN32_BUFFER_LEN / sizeof (short)] ; + int current, bufferlen ; + int BuffersInUse ; + + SNDFILE *sndfile ; + SF_INFO sfinfo ; + + sf_count_t remaining ; +} Win32_Audio_Data ; + + +static void +win32_play_data (Win32_Audio_Data *audio_data) +{ int thisread, readcount ; + + /* fill a buffer if there is more data and we can read it sucessfully */ + readcount = (audio_data->remaining > audio_data->bufferlen) ? audio_data->bufferlen : (int) audio_data->remaining ; + + thisread = (int) sf_read_short (audio_data->sndfile, (short *) (audio_data->whdr [audio_data->current].lpData), readcount) ; + + audio_data->remaining -= thisread ; + + if (thisread > 0) + { /* Fix buffer length if this is only a partial block. */ + if (thisread < audio_data->bufferlen) + audio_data->whdr [audio_data->current].dwBufferLength = thisread * sizeof (short) ; + + /* Queue the WAVEHDR */ + waveOutWrite (audio_data->hwave, (LPWAVEHDR) &(audio_data->whdr [audio_data->current]), sizeof (WAVEHDR)) ; + + /* count another buffer in use */ + EnterCriticalSection (&audio_data->mutex) ; + audio_data->BuffersInUse ++ ; + LeaveCriticalSection (&audio_data->mutex) ; + + /* use the other buffer next time */ + audio_data->current = (audio_data->current + 1) % 2 ; + } ; + + return ; +} /* win32_play_data */ + +static void CALLBACK +win32_audio_out_callback (HWAVEOUT hwave, UINT msg, DWORD data, DWORD param1, DWORD param2) +{ Win32_Audio_Data *audio_data ; + + /* Prevent compiler warnings. */ + hwave = hwave ; + param1 = param2 ; + + if (data == 0) + return ; + + /* + ** I consider this technique of passing a pointer via an integer as + ** fundamentally broken but thats the way microsoft has defined the + ** interface. + */ + audio_data = (Win32_Audio_Data*) data ; + + /* let main loop know a buffer is free */ + if (msg == MM_WOM_DONE) + { EnterCriticalSection (&audio_data->mutex) ; + audio_data->BuffersInUse -- ; + LeaveCriticalSection (&audio_data->mutex) ; + SetEvent (audio_data->Event) ; + } ; + + return ; +} /* win32_audio_out_callback */ + +/* This is needed for earlier versions of the M$ development tools. */ +#ifndef DWORD_PTR +#define DWORD_PTR DWORD +#endif + +static void +win32_play (int argc, char *argv []) +{ Win32_Audio_Data audio_data ; + + WAVEFORMATEX wf ; + int k, error ; + + audio_data.sndfile = NULL ; + audio_data.hwave = 0 ; + + for (k = 1 ; k < argc ; k++) + { printf ("Playing %s\n", argv [k]) ; + + if (! (audio_data.sndfile = sf_open (argv [k], SFM_READ, &(audio_data.sfinfo)))) + { puts (sf_strerror (NULL)) ; + continue ; + } ; + + audio_data.remaining = audio_data.sfinfo.frames * audio_data.sfinfo.channels ; + audio_data.current = 0 ; + + InitializeCriticalSection (&audio_data.mutex) ; + audio_data.Event = CreateEvent (0, FALSE, FALSE, 0) ; + + wf.nChannels = audio_data.sfinfo.channels ; + wf.wFormatTag = WAVE_FORMAT_PCM ; + wf.cbSize = 0 ; + wf.wBitsPerSample = 16 ; + + wf.nSamplesPerSec = audio_data.sfinfo.samplerate ; + + wf.nBlockAlign = audio_data.sfinfo.channels * sizeof (short) ; + + wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec ; + + error = waveOutOpen (&(audio_data.hwave), WAVE_MAPPER, &wf, (DWORD_PTR) win32_audio_out_callback, + (DWORD_PTR) &audio_data, CALLBACK_FUNCTION) ; + if (error) + { puts ("waveOutOpen failed.") ; + audio_data.hwave = 0 ; + continue ; + } ; + + audio_data.whdr [0].lpData = (char*) audio_data.buffer ; + audio_data.whdr [1].lpData = ((char*) audio_data.buffer) + sizeof (audio_data.buffer) / 2 ; + + audio_data.whdr [0].dwBufferLength = sizeof (audio_data.buffer) / 2 ; + audio_data.whdr [1].dwBufferLength = sizeof (audio_data.buffer) / 2 ; + + audio_data.whdr [0].dwFlags = 0 ; + audio_data.whdr [1].dwFlags = 0 ; + + /* length of each audio buffer in samples */ + audio_data.bufferlen = sizeof (audio_data.buffer) / 2 / sizeof (short) ; + + /* Prepare the WAVEHDRs */ + if ((error = waveOutPrepareHeader (audio_data.hwave, &(audio_data.whdr [0]), sizeof (WAVEHDR)))) + { printf ("waveOutPrepareHeader [0] failed : %08X\n", error) ; + waveOutClose (audio_data.hwave) ; + continue ; + } ; + + if ((error = waveOutPrepareHeader (audio_data.hwave, &(audio_data.whdr [1]), sizeof (WAVEHDR)))) + { printf ("waveOutPrepareHeader [1] failed : %08X\n", error) ; + waveOutUnprepareHeader (audio_data.hwave, &(audio_data.whdr [0]), sizeof (WAVEHDR)) ; + waveOutClose (audio_data.hwave) ; + continue ; + } ; + + /* Fill up both buffers with audio data */ + audio_data.BuffersInUse = 0 ; + win32_play_data (&audio_data) ; + win32_play_data (&audio_data) ; + + /* loop until both buffers are released */ + while (audio_data.BuffersInUse > 0) + { + /* wait for buffer to be released */ + WaitForSingleObject (audio_data.Event, INFINITE) ; + + /* refill the buffer if there is more data to play */ + win32_play_data (&audio_data) ; + } ; + + waveOutUnprepareHeader (audio_data.hwave, &(audio_data.whdr [0]), sizeof (WAVEHDR)) ; + waveOutUnprepareHeader (audio_data.hwave, &(audio_data.whdr [1]), sizeof (WAVEHDR)) ; + + waveOutClose (audio_data.hwave) ; + audio_data.hwave = 0 ; + + DeleteCriticalSection (&audio_data.mutex) ; + + sf_close (audio_data.sndfile) ; + } ; + +} /* win32_play */ + +#endif /* Win32 */ + +/*------------------------------------------------------------------------------ +** Solaris. +*/ + +#if (defined (sun) && defined (unix)) /* ie Solaris */ + +static void +solaris_play (int argc, char *argv []) +{ static short buffer [BUFFER_LEN] ; + audio_info_t audio_info ; + SNDFILE *sndfile ; + SF_INFO sfinfo ; + unsigned long delay_time ; + long k, start_count, output_count, write_count, read_count ; + int audio_fd, error, done ; + + for (k = 1 ; k < argc ; k++) + { printf ("Playing %s\n", argv [k]) ; + if (! (sndfile = sf_open (argv [k], SFM_READ, &sfinfo))) + { puts (sf_strerror (NULL)) ; + continue ; + } ; + + if (sfinfo.channels < 1 || sfinfo.channels > 2) + { printf ("Error : channels = %d.\n", sfinfo.channels) ; + continue ; + } ; + + /* open the audio device - write only, non-blocking */ + if ((audio_fd = open ("/dev/audio", O_WRONLY | O_NONBLOCK)) < 0) + { perror ("open (/dev/audio) failed") ; + return ; + } ; + + /* Retrive standard values. */ + AUDIO_INITINFO (&audio_info) ; + + audio_info.play.sample_rate = sfinfo.samplerate ; + audio_info.play.channels = sfinfo.channels ; + audio_info.play.precision = 16 ; + audio_info.play.encoding = AUDIO_ENCODING_LINEAR ; + audio_info.play.gain = AUDIO_MAX_GAIN ; + audio_info.play.balance = AUDIO_MID_BALANCE ; + + if ((error = ioctl (audio_fd, AUDIO_SETINFO, &audio_info))) + { perror ("ioctl (AUDIO_SETINFO) failed") ; + return ; + } ; + + /* Delay time equal to 1/4 of a buffer in microseconds. */ + delay_time = (BUFFER_LEN * 1000000) / (audio_info.play.sample_rate * 4) ; + + done = 0 ; + while (! done) + { read_count = sf_read_short (sndfile, buffer, BUFFER_LEN) ; + if (read_count < BUFFER_LEN) + { memset (&(buffer [read_count]), 0, (BUFFER_LEN - read_count) * sizeof (short)) ; + /* Tell the main application to terminate. */ + done = SF_TRUE ; + } ; + + start_count = 0 ; + output_count = BUFFER_LEN * sizeof (short) ; + + while (output_count > 0) + { /* write as much data as possible */ + write_count = write (audio_fd, &(buffer [start_count]), output_count) ; + if (write_count > 0) + { output_count -= write_count ; + start_count += write_count ; + } + else + { /* Give the audio output time to catch up. */ + usleep (delay_time) ; + } ; + } ; /* while (outpur_count > 0) */ + } ; /* while (! done) */ + + close (audio_fd) ; + } ; + + return ; +} /* solaris_play */ + +#endif /* Solaris */ + +/*============================================================================== +** Main function. +*/ + +int +main (int argc, char *argv []) +{ + if (argc < 2) + { + printf ("\nUsage : %s \n\n", argv [0]) ; +#if (OS_IS_WIN32 == 1) + printf ("This is a Unix style command line application which\n" + "should be run in a MSDOS box or Command Shell window.\n\n") ; + printf ("Sleeping for 5 seconds before exiting.\n\n") ; + + /* This is the officially blessed by microsoft way but I can't get + ** it to link. + ** Sleep (15) ; + ** Instead, use this: + */ + _sleep (5 * 1000) ; +#endif + return 1 ; + } ; + +#if defined (__linux__) + #if HAVE_ALSA_ASOUNDLIB_H + if (access ("/proc/asound/cards", R_OK) == 0) + alsa_play (argc, argv) ; + else + #endif + linux_play (argc, argv) ; +#elif (defined (__MACH__) && defined (__APPLE__)) + macosx_play (argc, argv) ; +#elif (defined (sun) && defined (unix)) + solaris_play (argc, argv) ; +#elif (OS_IS_WIN32 == 1) + win32_play (argc, argv) ; +#elif defined (__BEOS__) + printf ("This program cannot be compiled on BeOS.\n") ; + printf ("Instead, compile the file sfplay_beos.cpp.\n") ; + return 1 ; +#else + puts ("*** Playing sound not yet supported on this platform.") ; + puts ("*** Please feel free to submit a patch.") ; + return 1 ; +#endif + + return 0 ; +} /* main */ +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 8fc4110d-6cec-4e03-91df-0f384cabedac +*/ diff --git a/libsndfile.spec.in b/libsndfile.spec.in new file mode 100644 index 00000000..ad47a5f3 --- /dev/null +++ b/libsndfile.spec.in @@ -0,0 +1,69 @@ + +%define name @PACKAGE@ +%define version @VERSION@ +%define release 1 + +Summary: A library to handle various audio file formats. +Name: %{name} +Version: %{version} +Release: %{release} +Copyright: LGPL +Group: Libraries/Sound +Source: http://www.mega-nerd.com/libsndfile/libsndfile-%{version}.tar.gz +URL: http://www.mega-nerd.com/libsndfile/ +BuildRoot: /var/tmp/%{name}-%{version} + +%description +libsndfile is a C library for reading and writing sound files such as +AIFF, AU and WAV files through one standard interface. It can currently +read/write 8, 16, 24 and 32-bit PCM files as well as 32-bit floating +point WAV files and a number of compressed formats. + +%package devel +Summary: Libraries, includes, etc to develop libsndfile applications +Group: Libraries + +%description devel +Libraries, include files, etc you can use to develop libsndfile applications. + +%prep +%setup + +%build +%configure +make + +%install +if [ -d $RPM_BUILD_ROOT ]; then rm -rf $RPM_BUILD_ROOT; fi +mkdir -p $RPM_BUILD_ROOT +make DESTDIR=$RPM_BUILD_ROOT install +%clean +if [ -d $RPM_BUILD_ROOT ]; then rm -rf $RPM_BUILD_ROOT; fi + +%files +%defattr(-,root,root) +%doc AUTHORS COPYING ChangeLog INSTALL NEWS README TODO doc +%{_libdir}/libsndfile.so.* +%{_bindir}/* +%{_mandir}/man1/* +%{_datadir}/octave/site/m/* +%{_defaultdocdir}/libsndfile1-dev/html/* + +%files devel +%defattr(-,root,root) +%{_libdir}/libsndfile.a +%{_libdir}/libsndfile.la +%{_libdir}/libsndfile.so +%{_includedir}/sndfile.h +%{_libdir}/pkgconfig/sndfile.pc + +%changelog +* Sun May 15 2005 Erik de Castro Lopo +- Add html files to the files section. +* Tue Sep 16 2003 Erik de Castro Lopo +- Apply corrections from Andrew Schultz. +* Mon Oct 21 2002 Erik de Castro Lopo +- Force installation of sndfile.pc file. +* Thu Jul 6 2000 Josh Green +- Created libsndfile.spec.in + diff --git a/make_lite.py b/make_lite.py new file mode 100644 index 00000000..56e79e39 --- /dev/null +++ b/make_lite.py @@ -0,0 +1,499 @@ +#!/usr/bin/python + +import commands, os, re, string, sys, time + +def count_enclosed_functions (source): + func_count = 0 + open_brace = 0 + close_brace = 0 + for ch in source: + if ch == '{': + open_brace += 1 + elif ch == '}': + close_brace += 1 + if open_brace == close_brace: + func_count += 1 + if open_brace < close_brace: + print "count_enclosed_functions : open_brace < close_brace" + return -1 + return func_count + +def find_function_prototype (source, proto_name): + proto_re = "(^[a-zA-Z_ \t]+\s+%s[^a-zA-Z0-9_]\s*\([^\)]+\)\s+;\n)" % (proto_name) + proto_result = re.search (proto_re, source, re.MULTILINE | re.DOTALL) + if not proto_result: + return None + proto_text = proto_result.groups ()[0] + return proto_text + +def find_function_definition (source, func_name): + func_re = "(\n[a-zA-Z_ \t]+\n%s[^a-zA-Z0-9_].* /\* %s \*/\n)" % (func_name, func_name) + func_result = re.search (func_re, source, re.MULTILINE | re.DOTALL) + if not func_result: + sys.exit (1) + return None + func_text = func_result.groups ()[0] + + # Now to check that we only have one enclosing function. + func_count = count_enclosed_functions (func_text) + if func_count != 1: + return None + return func_text + +def find_include (source, inc_name): + inc_re = "(^#include\s+[\<\"]%s[\"\>]\s*)" % inc_name + inc_result = re.search (inc_re, source, re.MULTILINE | re.DOTALL) + if not inc_result: + return None + inc_text = inc_result.groups ()[0] + return inc_text + +def find_assign_statement (source, var_name): + var_re = "(^\s+%s\s*=[^;]+;)" % var_name + var_result = re.search (var_re, source, re.MULTILINE | re.DOTALL) + if not var_result: + return None + assign_text = var_result.groups ()[0] + return assign_text + +#-------------------------------------------------------------------------------- + +def remove_include (source, inc_name): + inc_text = find_include (source, inc_name) + if not inc_text: + print "remove_include : include '%s' not found. Exiting." % inc_name + sys.exit (1) + + source = string.replace (source, inc_text, "") + return source + +def remove_assign (source, assign_name): + assign_text = find_assign (source, inc_name) + if not inc_text: + print "remove_include : include '%s' not found. Exiting." % inc_name + sys.exit (1) + + source = string.replace (source, inc_text, "") + return source + +def remove_prototype (source, proto_name): + proto_text = find_function_prototype (source, proto_name) + if not proto_text: + print "remove_prototype : prototype '%s' not found. Exiting." % proto_name + sys.exit (1) + + source = string.replace (source, proto_text, "") + return source + +def remove_function (source, func_name): + func_text = find_function_definition (source, func_name) + if not func_text: + print "remove_function : function '%s' not found. Exiting." % func_name + sys.exit (1) + + source = string.replace (source, func_text, "/* Function %s() removed here. */\n" % func_name) + return source + +def remove_all_assignments (source, var): + count = 0 + while 1: + assign_text = find_assign_statement (source, var) + if not assign_text: + if count != 0: + break + print "remove_all_assignments : variable '%s' not found. Exiting." % var + sys.exit (1) + + source = string.replace (source, assign_text, "") + count += 1 + return source + + + +#---------------------------------------------------------------- + +def remove_funcs_and_protos_from_file (filename, func_list): + source_code = open (filename, 'r').read () + + for func in func_list: + source_code = remove_prototype (source_code, func) ; + source_code = remove_function (source_code, func) ; + open (filename, 'w').write (source_code) + +def remove_funcs_from_file (filename, func_list): + source_code = open (filename, 'r').read () + + for func in func_list: + source_code = remove_function (source_code, func) ; + open (filename, 'w').write (source_code) + +def remove_protos_from_file (filename, func_list): + source_code = open (filename, 'r').read () + + for func in func_list: + source_code = remove_prototype (source_code, func) ; + open (filename, 'w').write (source_code) + +def remove_includes_from_file (filename, inc_list): + source_code = open (filename, 'r').read () + + for inc in inc_list: + source_code = remove_include (source_code, inc) ; + open (filename, 'w').write (source_code) + +def remove_all_assignments_from_file (filename, var_list): + source_code = open (filename, 'r').read () + + for var in var_list: + source_code = remove_all_assignments (source_code, var) ; + open (filename, 'w').write (source_code) + +def remove_comment_start_end (filename, start_comment, end_comment): + source_code = open (filename, 'r').read () + + while 1: + start_index = string.find (source_code, start_comment) + end_index = string.find (source_code, end_comment) + if start_index < 0 or end_index < start_index: + break + end_index += len (end_comment) + source_code = source_code [:start_index-1] + source_code [end_index:] ; + + open (filename, 'w').write (source_code) + +def remove_strings_from_file (filename, str_list): + file_text = open (filename, 'r').read () + for current_str in str_list: + file_text = string.replace (file_text, current_str, '') + open (filename, 'w').write (file_text) + +def string_replace_in_file (filename, from_str, to_str): + file_text = open (filename, 'r').read () + file_text = string.replace (file_text, from_str, to_str) + open (filename, 'w').write (file_text) + +def remove_regex_from_file (filename, regex_list): + file_text = open (filename, 'r').read () + for regex in regex_list: + file_text = re.sub (regex, '', file_text, re.MULTILINE | re.DOTALL) + open (filename, 'w').write (file_text) + +#========================================================================== + +def find_configure_version (filename): + # AM_INIT_AUTOMAKE(libsndfile,0.0.21pre6) + file = open (filename) + while 1: + line = file.readline () + if re.search ("AC_INIT", line): + x = re.sub ("[^\(]+\(", "", line) + x = re.sub ("\).*\n", "", x) + x = string.split (x, ",") + package = x [0] + version = x [1] + break + file.close () + # version = re.escape (version) + return package, version + +def fix_configure_ac_file (filename): + data = open (filename, 'r').read () + data = string.replace (data, "AM_INIT_AUTOMAKE(libsndfile,", "AM_INIT_AUTOMAKE(libsndfile_lite,", 1) + + file = open (filename, 'w') + file.write (data) + file.close () + + +def make_dist_file (package, version): + print "Making dist file." + tar_gz_file = "%s-%s.tar.gz" % (package, version) + if os.path.exists (tar_gz_file): + return + if os.system ("make dist"): + sys.exit (1) + return + +def delete_files (file_list): + for file_name in file_list: + os.remove (file_name) + +#======================================================================= + +source_dir = os.getcwd () + +conf_package, conf_version = find_configure_version ('configure.ac') + +package_version = "%s-%s" % (conf_package, conf_version) +lite_version = "%s_lite-%s" % (conf_package, conf_version) + +os.system ("rm -rf %s%s.tar.gz" % (source_dir, package_version)) + +os.system ("make dist") + +make_dist_file (conf_package, conf_version) + +os.chdir ("/tmp") + +print "Uncompressing .tar.gz file." +os.system ("rm -rf %s" % package_version) +if os.system ("tar zxf %s/%s.tar.gz" % (source_dir, package_version)): + sys.exit (1) + + +print "Renaming to libsndfile_lite." +os.system ("rm -rf %s" % lite_version) +os.rename (package_version, lite_version) + +print "Changing into libsndfile_lite directory." +os.chdir (lite_version) + +print "Removing un-neeed directories." +delete_dirs = [ 'src/G72x' ] + +for dir_name in delete_dirs: + os.system ("rm -rf %s" % dir_name) + +print "Removing un-needed files." +delete_files ([ 'src/ircam.c', 'src/nist.c', + 'src/ima_adpcm.c', 'src/ms_adpcm.c', 'src/au_g72x.c', + 'src/mat4.c', 'src/mat5.c', 'src/dwvw.c', 'src/paf.c', + 'src/ogg.c', 'src/pvf.c', 'src/xi.c', 'src/htk.c', + 'src/sd2.c', 'src/rx2.c', 'src/txw.c', 'src/wve.c', + 'src/dwd.c', 'src/svx.c', 'src/voc.c', 'src/vox_adpcm.c', + 'src/sds.c' + ]) + + +print "Hacking 'configure.ac' and 'src/Makefile.am'." +remove_strings_from_file ('configure.ac', [ 'src/G72x/Makefile' ]) +remove_strings_from_file ('src/Makefile.am', [ 'G72x/libg72x.la', 'G72x', + 'ircam.c', 'nist.c', 'ima_adpcm.c', 'ms_adpcm.c', 'au_g72x.c', 'mat4.c', + 'mat5.c', 'dwvw.c', 'paf.c', 'ogg.c', 'pvf.c', 'xi.c', 'htk.c', + 'sd2.c', 'rx2.c', 'txw.c', 'wve.c', 'dwd.c', 'svx.c', 'voc.c', + 'vox_adpcm.c', 'sds.c' + ]) + +#---------------------------------------------------------------------------- + +print "Hacking header files." + +remove_protos_from_file ('src/common.h', [ 'xi_open', 'sd2_open', 'ogg_open', + 'dwvw_init', 'paf_open', 'svx_open', 'nist_open', 'rx2_open', 'mat4_open', + 'voc_open', 'txw_open', 'dwd_open', 'htk_open', 'wve_open', 'mat5_open', + 'pvf_open', 'ircam_open', 'sds_open', + 'float32_init', 'double64_init', 'aiff_ima_init', 'vox_adpcm_init', + 'wav_w64_ima_init', 'wav_w64_msadpcm_init' + ]) + +remove_protos_from_file ('src/au.h', + [ 'au_g72x_reader_init', 'au_g72x_writer_init' ]) + +remove_protos_from_file ('src/wav_w64.h', [ 'msadpcm_write_adapt_coeffs' ]) + +#---------------------------------------------------------------------------- + +print "Hacking case statements." + +remove_comment_start_end ('src/sndfile.c', '/* Lite remove start */' , '/* Lite remove end */') +remove_comment_start_end ('src/aiff.c', '/* Lite remove start */' , '/* Lite remove end */') +remove_comment_start_end ('src/au.c', '/* Lite remove start */' , '/* Lite remove end */') +remove_comment_start_end ('src/raw.c', '/* Lite remove start */' , '/* Lite remove end */') +remove_comment_start_end ('src/w64.c', '/* Lite remove start */' , '/* Lite remove end */') +remove_comment_start_end ('src/wav.c', '/* Lite remove start */' , '/* Lite remove end */') +remove_comment_start_end ('src/double64.c', '/* Lite remove start */' , '/* Lite remove end */') +remove_comment_start_end ('src/float32.c', '/* Lite remove start */' , '/* Lite remove end */') + + +#---------------------------------------------------------------------------- + +print "Hacking src/pcm.c." +remove_funcs_from_file ('src/pcm.c', [ + 'f2sc_array', 'f2sc_clip_array', 'f2uc_array', 'f2uc_clip_array', + 'f2bes_array', 'f2bes_clip_array', 'f2les_array', 'f2les_clip_array', + 'f2let_array', 'f2let_clip_array', 'f2bet_array', 'f2bet_clip_array', + 'f2bei_array', 'f2bei_clip_array', 'f2lei_array', 'f2lei_clip_array', + 'd2sc_array', 'd2sc_clip_array', 'd2uc_array', 'd2uc_clip_array', + 'd2bes_array', 'd2bes_clip_array', 'd2les_array', 'd2les_clip_array', + 'd2let_array', 'd2let_clip_array', 'd2bet_array', 'd2bet_clip_array', + 'd2bei_array', 'd2bei_clip_array', 'd2lei_array', 'd2lei_clip_array', + ]) + +remove_funcs_and_protos_from_file ('src/pcm.c', [ + 'pcm_read_sc2f', 'pcm_read_uc2f', 'pcm_read_les2f', 'pcm_read_bes2f', + 'pcm_read_let2f', 'pcm_read_bet2f', 'pcm_read_lei2f', 'pcm_read_bei2f', + 'pcm_read_sc2d', 'pcm_read_uc2d', 'pcm_read_les2d', 'pcm_read_bes2d', + 'pcm_read_let2d', 'pcm_read_bet2d', 'pcm_read_lei2d', 'pcm_read_bei2d', + 'pcm_write_f2sc', 'pcm_write_f2uc', 'pcm_write_f2bes', 'pcm_write_f2les', + 'pcm_write_f2bet', 'pcm_write_f2let', 'pcm_write_f2bei', 'pcm_write_f2lei', + 'pcm_write_d2sc', 'pcm_write_d2uc', 'pcm_write_d2bes', 'pcm_write_d2les', + 'pcm_write_d2bet', 'pcm_write_d2let', 'pcm_write_d2bei', 'pcm_write_d2lei', + + 'sc2f_array', 'uc2f_array', 'bes2f_array', 'les2f_array', + 'bet2f_array', 'let2f_array', 'bei2f_array', 'lei2f_array', + 'sc2d_array', 'uc2d_array', 'bes2d_array', 'les2d_array', + 'bet2d_array', 'let2d_array', 'bei2d_array', 'lei2d_array' + ]) + +remove_includes_from_file ('src/pcm.c', [ 'float_cast.h' ]) +remove_all_assignments_from_file ('src/pcm.c', [ + 'psf-\>write_float', 'psf\-\>write_double', + 'psf-\>read_float', 'psf\-\>read_double' ]) + +#---------------------------------------------------------------------------- +print "Hacking src/ulaw.c." +remove_funcs_and_protos_from_file ('src/ulaw.c', [ + 'ulaw_read_ulaw2f', 'ulaw_read_ulaw2d', + 'ulaw_write_f2ulaw', 'ulaw_write_d2ulaw', + 'ulaw2f_array', 'ulaw2d_array', 'f2ulaw_array', 'd2ulaw_array' + ]) + +remove_includes_from_file ('src/ulaw.c', [ 'float_cast.h' ]) +remove_all_assignments_from_file ('src/ulaw.c', [ + 'psf-\>write_float', 'psf\-\>write_double', + 'psf-\>read_float', 'psf\-\>read_double' ]) + +#---------------------------------------------------------------------------- + +print "Hacking src/alaw.c." +remove_funcs_and_protos_from_file ('src/alaw.c', [ + 'alaw_read_alaw2f', 'alaw_read_alaw2d', + 'alaw_write_f2alaw', 'alaw_write_d2alaw', + 'alaw2f_array', 'alaw2d_array', 'f2alaw_array', 'd2alaw_array' + ]) + +remove_includes_from_file ('src/alaw.c', [ 'float_cast.h' ]) +remove_all_assignments_from_file ('src/alaw.c', [ + 'psf-\>write_float', 'psf\-\>write_double', + 'psf-\>read_float', 'psf\-\>read_double' ]) + +#---------------------------------------------------------------------------- + +print "Hacking src/gsm610.c." +remove_funcs_and_protos_from_file ('src/gsm610.c', [ + 'gsm610_read_f', 'gsm610_read_d', 'gsm610_write_f', 'gsm610_write_d' + ]) + +remove_includes_from_file ('src/gsm610.c', [ 'float_cast.h' ]) +remove_all_assignments_from_file ('src/gsm610.c', [ + 'psf-\>write_float', 'psf\-\>write_double', + 'psf-\>read_float', 'psf\-\>read_double' ]) + +#---------------------------------------------------------------------------- + +print "Hacking src/float32.c." + +# string_replace_in_file ('src/float32.c', '"float_cast.h"', '') +remove_funcs_from_file ('src/float32.c', [ 'float32_init' ]) + +remove_funcs_and_protos_from_file ('src/float32.c', [ + 'host_read_f2s', 'host_read_f2i', 'host_read_f', 'host_read_f2d', + 'host_write_s2f', 'host_write_i2f', 'host_write_f', 'host_write_d2f', + 'f2s_array', 'f2i_array', 'f2d_array', 's2f_array', 'i2f_array', 'd2f_array', + 'float32_peak_update', + 'replace_read_f2s', 'replace_read_f2i', 'replace_read_f', 'replace_read_f2d', + 'replace_write_s2f', 'replace_write_i2f', 'replace_write_f', 'replace_write_d2f', + 'bf2f_array', 'f2bf_array', + 'float32_get_capability', + ]) + +#---------------------------------------------------------------------------- + +print "Hacking src/double64.c." +remove_funcs_from_file ('src/double64.c', [ 'double64_init' ]) + +remove_funcs_and_protos_from_file ('src/double64.c', [ + 'host_read_d2s', 'host_read_d2i', 'host_read_d2f', 'host_read_d', + 'host_write_s2d', 'host_write_i2d', 'host_write_f2d', 'host_write_d', + 'd2s_array', 'd2i_array', 'd2f_array', + 's2d_array', 'i2d_array', 'f2d_array', + 'double64_peak_update', 'double64_get_capability', + 'replace_read_d2s', 'replace_read_d2i', 'replace_read_d2f', 'replace_read_d', + 'replace_write_s2d', 'replace_write_i2d', 'replace_write_f2d', 'replace_write_d', + 'd2bd_read', 'bd2d_write' + ]) + +#---------------------------------------------------------------------------- + +print "Hacking test programs." +delete_files ([ 'tests/dwvw_test.c', 'tests/floating_point_test.c', + 'tests/dft_cmp.c', 'tests/peak_chunk_test.c', + 'tests/scale_clip_test.tpl', 'tests/scale_clip_test.def' + ]) + +remove_comment_start_end ('tests/write_read_test.def', '/* Lite remove start */', '/* Lite remove end */') +remove_comment_start_end ('tests/write_read_test.tpl', '/* Lite remove start */', '/* Lite remove end */') + +remove_comment_start_end ('tests/Makefile.am', '# Lite remove start', '# Lite remove end') + +remove_strings_from_file ('tests/Makefile.am', [ + 'scale_clip_test.tpl', 'scale_clip_test.def', + '\n\t./dwvw_test', + '\n\t./floating_point_test', '\n\t./scale_clip_test', + '\n\t./peak_chunk_test aiff', '\n\t./peak_chunk_test wav', + '\n\t./command_test norm', '\n\t./command_test peak', + '\n\t./lossy_comp_test wav_ima', '\n\t./lossy_comp_test wav_msadpcm', + '\n\t./lossy_comp_test au_g721', '\n\t./lossy_comp_test au_g723', + '\n\t./lossy_comp_test vox_adpcm', + '\n\t./lossy_comp_test w64_ima', '\n\t./lossy_comp_test w64_msadpcm', + 'peak_chunk_test', 'dwvw_test', 'floating_point_test', 'scale_clip_test', + + 'paf-tests', 'svx-tests', 'nist-tests', 'ircam-tests', 'voc-tests', + 'mat4-tests', 'mat5-tests', 'pvf-tests', 'xi-tests', 'htk-tests', + 'sds-tests' + ]) + +remove_comment_start_end ('tests/pcm_test.c', '/* Lite remove start */', '/* Lite remove end */') +remove_funcs_and_protos_from_file ('tests/pcm_test.c', [ + 'pcm_test_float', 'pcm_test_double' + ]) + +remove_comment_start_end ('tests/lossy_comp_test.c', '/* Lite remove start */', '/* Lite remove end */') +remove_funcs_and_protos_from_file ('tests/lossy_comp_test.c', [ + 'lcomp_test_float', 'lcomp_test_double', 'sdlcomp_test_float', 'sdlcomp_test_double', + 'smoothed_diff_float', 'smoothed_diff_double' + ]) + +remove_comment_start_end ('tests/multi_file_test.c', '/* Lite remove start */', '/* Lite remove end */') + +remove_strings_from_file ('tests/stdio_test.c', [ + '"paf",', '"svx",', '"nist",', '"ircam",', '"voc",', '"mat4",', '"mat5",', '"pvf",' + ]) + +remove_comment_start_end ('tests/pipe_test.c', '/* Lite remove start */', '/* Lite remove end */') + +#---------------------------------------------------------------------------- + +print "Fixing configure.ac file." +fix_configure_ac_file ('configure.ac') + +print "Building and testing source." + # Try --disable-shared --disable-gcc-opt +if os.system ("./reconfigure.mk && ./configure --disable-shared --disable-gcc-opt && make check"): + os.system ('PS1="FIX > " bash --norc') + sys.exit (1) + +print "Making distcheck" +if os.system ("make distcheck"): + os.system ('PS1="FIX > " bash --norc') + sys.exit (1) + +print "Copying tarball" +if os.system ("cp %s.tar.gz %s" % (lite_version, source_dir)): + print "??? %s.tar.gz ???" % lite_version + os.system ('PS1="FIX > " bash --norc') + sys.exit (1) + +os.chdir (source_dir) + +os.system ("rm -rf /tmp/%s" % lite_version) + +print "Done." + + +# Do not edit or modify anything in this comment block. +# The arch-tag line is a file identity tag for the GNU Arch +# revision control system. +# +# arch-tag: 4aa63c4d-930b-4ef8-a0f2-d1c63992b60b + diff --git a/man/Makefile.am b/man/Makefile.am new file mode 100644 index 00000000..6ad00bfb --- /dev/null +++ b/man/Makefile.am @@ -0,0 +1,12 @@ +## Process this file with automake to produce Makefile.in + +man_MANS = sndfile-info.1 sndfile-play.1 sndfile-convert.1 + +EXTRA_DIST = sndfile-info.1 sndfile-play.1 sndfile-convert.1 + +## Do not edit or modify anything in this comment block. +## The arch-tag line is a file identity tag for the GNU Arch +## revision control system. +## +## arch-tag: a7e49b1a-d2f1-414d-aa5a-3a91ef566d9f + diff --git a/man/sndfile-convert.1 b/man/sndfile-convert.1 new file mode 100644 index 00000000..105c9e0f --- /dev/null +++ b/man/sndfile-convert.1 @@ -0,0 +1,28 @@ +.TH SNDFILE-CONVERT 1 "October 09, 2002" +.SH NAME +sndfile-convert \- convert a sound files from one format to another +.SH SYNOPSIS +.B sndfile-convert +.RI "[encoding] input_file output_file" +.LP +.B sndfile-convert +.RI --help +.SH DESCRIPTION +sndfile-convert converts sound files from one format to another using +libsndfile (http://www.mega-nerd.com/libsndfile/) to read and write +the data. +.LP +The format of the output file is determined by the filename extension +of the output file. +.LP +The optional encoding parameter allows setting of the data encoding for +the output file. Run "sndfile-convert --help" for more information. +.SH AUTHOR +This manual page was written by Erik de Castro Lopo . + +.\" Do not edit or modify anything in this comment block. +.\" The following line is a file identity tag for the GNU Arch +.\" revision control system. + +.\" arch-tag: 23aa443e-f38d-488b-b7e3-290fc5c77d20 + diff --git a/man/sndfile-info.1 b/man/sndfile-info.1 new file mode 100644 index 00000000..744a7015 --- /dev/null +++ b/man/sndfile-info.1 @@ -0,0 +1,22 @@ +.TH SNDFILE-INFO 1 "July 28, 2002" +.SH NAME +sndfile-info \- display information about a sound file +.SH SYNOPSIS +.B sndfile-info +.RI file +.SH DESCRIPTION +sndfile-info will display basic information about a sound file such as +its format, its sample rate, and the number of channels. This information +is obtained using libsndfile (http://www.mega-nerd.com/libsndfile/). +.SH AUTHOR +This manual page was originally written by Joshua Haberman +, for the Debian GNU/Linux system (but may be used by +others). Further additions have been made by Erik de Castro Lopo +. + +.\" Do not edit or modify anything in this comment block. +.\" The following line is a file identity tag for the GNU Arch +.\" revision control system. + +.\" arch-tag: f9eca35e-5519-434d-bc55-fe845df977cd + diff --git a/man/sndfile-play.1 b/man/sndfile-play.1 new file mode 100644 index 00000000..1e50e65e --- /dev/null +++ b/man/sndfile-play.1 @@ -0,0 +1,41 @@ +.de EX +.ne 5 +.if n .sp 1 +.if t .sp .5 +.nf +.in +.5i +.. +.de EE +.fi +.in -.5i +.if n .sp 1 +.if t .sp .5 +.. +.TH SNDFILE-PLAY 1 "July 28, 2002" +.SH NAME +sndfile-play \- play a sound file +.SH SYNOPSIS +.B sndfile-play +.RI file +.SH DESCRIPTION +sndfile-play plays the specified sound file using : +.EX +ALSA on Linux +/dev/dsp on systems supporting OSS (including Linux) +/dev/audio on Sun Solaris +CoreAudio on Apple MacOSX +waveOut on Microsoft Win32 +.EE +sndfile-play uses libsndfile (http://www.mega-nerd.com/libsndfile/) +to read the file. +.SH AUTHOR +This manual page was originally written by Joshua Haberman +, for the Debian GNU/Linux system (but may be used by +others). Further additions have been made by Erik de Castro Lopo +. + +.\" Do not edit or modify anything in this comment block. +.\" The arch-tag line is a file identity tag for the GNU Arch +.\" revision control system. + +.\" arch-tag: 5a0120d9-040c-464e-b01e-89dc2c2428c9 diff --git a/reconfigure.mk b/reconfigure.mk new file mode 100755 index 00000000..1cc9cfb3 --- /dev/null +++ b/reconfigure.mk @@ -0,0 +1,58 @@ +#!/usr/bin/make -f + +# The auto tools MUST be run in the following order: +# +# 1. aclocal +# 2. libtoolize (if you use libtool) +# 3. autoconf +# 4. autoheader (if you use autoheader) +# 5. automake (if you use automake) +# +# The following makefile runs these in the correct order according to their +# dependancies. It also makes up for Mac OSX's fucked-upped-ness. + +ACLOCAL = aclocal + +ifneq ($(shell uname -s), Darwin) + LIBTOOLIZE = libtoolize +else + # Fuck Apple! Why the hell did they rename libtoolize???? + LIBTOOLIZE = glibtoolize + # Fink (and DarwinPorts/MacPorts) sucks as well, but this seems necessary. + ACLOCAL_INC = -I /opt/local/share/aclocal +endif + +genfiles : config.status + (cd src && make genfiles) + (cd tests && make genfiles) + +config.status: configure src/config.h.in Makefile.in src/Makefile.in tests/Makefile.in + ./configure --enable-gcc-werror + +configure: ltmain.sh + autoconf + +Makefile.in: Makefile.am + automake --copy --add-missing + +src/Makefile.in: src/Makefile.am + automake --copy --add-missing + +tests/Makefile.in: tests/Makefile.am + automake --copy --add-missing + +src/config.h.in: configure + autoheader + +libtool ltmain.sh: aclocal.m4 + $(LIBTOOLIZE) --copy --force + +# Need to re-run aclocal whenever acinclude.m4 is modified. +aclocal.m4: acinclude.m4 + $(ACLOCAL) $(ACLOCAL_INC) + +clean: + rm -f libtool ltmain.sh aclocal.m4 Makefile.in src/config.h.in config.cache config.status + find . -name .deps -type d -exec rm -rf {} \; + + diff --git a/regtest/Makefile.am b/regtest/Makefile.am new file mode 100644 index 00000000..ad539867 --- /dev/null +++ b/regtest/Makefile.am @@ -0,0 +1,18 @@ +## Process this file with automake to produce Makefile.in + +bin_PROGRAMS = sndfile-regtest + +noinst_HEADERS = regtest.h + +SNDFILEDIR =../src +INCLUDES = -I$(srcdir)/$(SNDFILEDIR) $(OS_SPECIFIC_CFLAGS) + +sndfile_regtest_SOURCES = sndfile-regtest.c database.c checksum.c +sndfile_regtest_LDADD = $(SNDFILEDIR)/libsndfile.la $(SQLITE3_LIBS) + + +## Do not edit or modify anything in this comment block. +## The arch-tag line is a file identity tag for the GNU Arch +## revision control system. +## +## arch-tag: ac4a9626-49ce-4bb4-9fb6-e43de0a23432 diff --git a/regtest/Readme.txt b/regtest/Readme.txt new file mode 100644 index 00000000..5102cb88 --- /dev/null +++ b/regtest/Readme.txt @@ -0,0 +1,114 @@ +sndfile-regtest +=============== + +The 'sndfile-regtest' program is a regression test-suite for libsndile. + +This program is intended to allow anyone who has an interest in the +reliability and correctness of libsndfile to do their own regression +testing. From the point of view of the libsndfile developers, this +program now allows for distributed regression testing of libsndfile +which will make libsndfile better. + + +How Does it Work +---------------- +Anyone who wishes to take part in the distributed regression testing of +libsndfile can download the regression test program and install it. + +Once installed the user can start collecting files and adding them to +their own personal database. Then, as new versions of libsndfile come +out, the user should test the new library version against their database +of files (instructions below). + +Any files which were successfully added to the database in the past but +now fail the check with the new library version represent a regression. +The user should then contact the libsndfile developers so that a copy +of the test file can be made available to the developers. + + +Requirements +------------ +The regression test program uses sqlite3 as the database engine. On +Debian, the required packages are : + + sqlite3 + libsqlite3-0 + libsqlite3-dev + +but similar packages should be available on any other Linux style +system. + +The regression test currently only compiles under Unix-like systems. +At some time in the future the regression test will distributed along +with the libsndfile source code distribution. + + +Organization of Files +--------------------- +The regession test program keeps its database file in the directory it +is run from. In addition, the database only contains information about +the files, not the files themselves. + +This means that database file should probably be kept in the same +directory (or a directory above) the test files. + + +Setting it Up for the First Time +-------------------------------- +The sndfile-regtest program should be on your PATH. You can then cd into +the directory where you intend to keep you test files and +run the command: + + sndfile-regtest --create-db + +which creates a file named '.sndfile-regtest.db' in the current directory. + +Files can then be added to the database using the command: + + sndfile-regtest --add-file file1.wav + +The --add-file option allows more than one file to be added at a time +using: + + sndfile-regtest --add-file file1.wav file2.aif ..... + + +Checking Files +-------------- +One or more files that have already been added to the database can be +checked using: + + sndfile-regtest --check-file file1.wav file2.aif ..... + +It is also possible to check all files in the database using: + + sndfile-regtest --check-all + + +Running a Regression Test +------------------------- +Once you have a collection of files and a database it is possible to test +new versions of libsndfile before you install them. If for instance you +have just compiled a new version of libsndfile in the directory +/usr/src/libsndfile-X.Y.Z, then you can use an existing sndfile-regtest +binary with the new libsndfile using something like: + + LD_PRELOAD=/usr/src/libsndfile-X.Y.Z/src/.libs/libsndfile.so.X.Y.Z \ + sndfile-regtest --check-all + + +Reporting Regressions +--------------------- +Any user who finds a file which was added to the regression database with +an earlier version of libsndfile and then fails the check with a later +version of the library should contact the author (erikd at mega dash nerd +dot com). If possible place the file on a web server and email the author +a link to it. + + +-------------------------------------------------------------------------- +# Do not edit or modify anything in this comment block. +# The arch-tag line is a file identity tag for the GNU Arch +# revision control system. +# +# arch-tag: 174661f1-9874-424e-8d8b-301d0322dfdb diff --git a/regtest/checksum.c b/regtest/checksum.c new file mode 100644 index 00000000..033a1564 --- /dev/null +++ b/regtest/checksum.c @@ -0,0 +1,125 @@ +/* +** Copyright (C) 2005 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* +** A simple checksum for short, int and float data. +*/ + +#include "config.h" + +#include +#include +#include + +#include + +#include "float_cast.h" + +#include "regtest.h" + +#define BIG_PRIME 999983 + +#define ARRAY_LEN(x) ((int) (sizeof (x)) / (sizeof ((x) [0]))) + +static int short_checksum (SNDFILE * file, int start) ; +static int int_checksum (SNDFILE * file, int start) ; +static int float_checksum (SNDFILE * file, int start) ; + +int +calc_checksum (SNDFILE * file, const SF_INFO * info) +{ int start ; + + /* Seed the checksum with data from the SF_INFO struct. */ + start = info->samplerate ; + start = start * BIG_PRIME + info->channels ; + start = start * BIG_PRIME + info->format ; + + switch (info->format & SF_FORMAT_SUBMASK) + { case SF_FORMAT_FLOAT : + case SF_FORMAT_DOUBLE : + return float_checksum (file, start) ; + + case SF_FORMAT_PCM_24 : + case SF_FORMAT_PCM_32 : + return int_checksum (file, start) ; + + default : + return short_checksum (file, start) ; + } ; + + return 0 ; +} /* calc_checksum */ + +/*------------------------------------------------------------------------------ +*/ + +static union +{ short s [1 << 16] ; + int i [1 << 15] ; + float f [1 << 15] ; +} data ; + +static int +short_checksum (SNDFILE * file, int start) +{ int k, count ; + + do + { count = (int) sf_read_short (file, data.s, ARRAY_LEN (data.s)) ; + for (k = 0 ; k < count ; k++) + start = start * BIG_PRIME + data.s [k] ; + } + while (count > 0) ; + + return start ; +} /* short_checksum */ + +static int +int_checksum (SNDFILE * file, int start) +{ int k, count ; + + do + { count = (int) sf_read_int (file, data.i, ARRAY_LEN (data.i)) ; + for (k = 0 ; k < count ; k++) + start = start * BIG_PRIME + data.i [k] ; + } + while (count > 0) ; + + return start ; +} /* int_checksum */ + +static int +float_checksum (SNDFILE * file, int start) +{ int k, count ; + + do + { count = (int) sf_read_float (file, data.f, ARRAY_LEN (data.f)) ; + for (k = 0 ; k < count ; k++) + start = start * BIG_PRIME + lrintf (0x7FFFFFFF * data.f [k]) ; + } + while (count > 0) ; + + return start ; +} /* float_checksum */ + +/* +** Do not edit or modify anything in this comment block. +** The following line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 6ae999d1-dd5a-4306-9e11-d4cfc9e8ae27 +*/ diff --git a/regtest/database.c b/regtest/database.c new file mode 100644 index 00000000..7830d68a --- /dev/null +++ b/regtest/database.c @@ -0,0 +1,502 @@ +/* +** Copyright (C) 2005 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include "regtest.h" + +#if HAVE_SQLITE3 + +#include + +typedef struct +{ sqlite3 *sql ; + + int count ; + int ekey_max ; + + /* Filename and pathname for file. */ + char filename [256] ; + char pathname [512] ; + + /* Storage for createding SQL commands. Must be larger than logbuf below. */ + char cmdbuf [1 << 15] ; + + /* Storage for log buffer retrieved from SNDFILE* .*/ + char logbuf [1 << 14] ; + +} REGTEST_DB ; + +/* In checksum.c */ +int calc_checksum (SNDFILE * file, const SF_INFO * info) ; + +static void get_filename_pathname (REGTEST_DB * db, const char *filepath) ; +static void single_quote_replace (char * buf) ; + +static int get_ekey_from_filename (REGTEST_DB * db, const char *filepath) ; +static int get_filename_pathname_by_ekey (REGTEST_DB * db, int ekey) ; +static int check_file_by_ekey (REGTEST_DB * db, int ekey) ; + +static int count_callback (REGTEST_DB * db, int argc, char **argv, char **colname) ; +static int ekey_max_callback (REGTEST_DB * db, int argc, char **argv, char **colname) ; +static int callback (void *unused, int argc, char **argv, char **colname) ; + +REG_DB * +db_open (const char * db_name) +{ REGTEST_DB * db ; + int err ; + + if ((db = malloc (sizeof (REGTEST_DB))) == NULL) + { perror ("malloc") ; + exit (1) ; + } ; + + if ((err = sqlite3_open (db_name, &(db->sql))) != 0) + { printf ("Can't open database: %s\n", sqlite3_errmsg (db->sql)) ; + sqlite3_close (db->sql) ; + free (db) ; + exit (1) ; + } ; + + return (REG_DB *) db ; +} /* db_open */ + +int +db_create (const char * db_name) +{ REGTEST_DB * db ; + const char *cmd ; + char * errmsg = NULL ; + int err ; + + db = (REGTEST_DB *) db_open (db_name) ; + + cmd = "create table sndfile (ekey INTEGER PRIMARY KEY," + "fname VARCHAR(1)," + "fpath VARCHAR(1)," + "srate INTEGER," + "frames VARCHAR(1)," + "channels INTEGER," + "format VARCHAR(1)," + "checksum VARCHAR(1)," + "logbuf VARCHAR(1)" + ");" ; + + err = sqlite3_exec (db->sql, cmd, callback, 0, &errmsg) ; + if (err != SQLITE_OK) + printf ("Line %d : SQL error: %s\n", __LINE__, errmsg) ; + + sqlite3_close (db->sql) ; + free (db) ; + + return 0 ; +} /* db_create */ + +int +db_close (REG_DB * db_handle) +{ REGTEST_DB * db ; + + db = (REGTEST_DB *) db_handle ; + + sqlite3_close (db->sql) ; + free (db) ; + + return 0 ; +} /* db_close */ + +/*============================================================================== +*/ + +int +db_file_exists (REG_DB * db_handle, const char * filename) +{ REGTEST_DB * db ; + const char * cptr ; + char * errmsg ; + int err ; + + db = (REGTEST_DB *) db_handle ; + + if ((cptr = strrchr (filename, '/')) != NULL) + filename = cptr + 1 ; + + snprintf (db->cmdbuf, sizeof (db->cmdbuf), "select fname from sndfile where fname='%s'", filename) ; + + db->count = 0 ; + err = sqlite3_exec (db->sql, db->cmdbuf, (sqlite3_callback) count_callback, db, &errmsg) ; + if (db->count == 1) + return 1 ; + + return 0 ; +} /* db_file_exists */ + +int +db_add_file (REG_DB * db_handle, const char * filepath) +{ REGTEST_DB * db ; + SNDFILE * sndfile ; + SF_INFO info ; + char * errmsg ; + int err, checksum ; + + db = (REGTEST_DB *) db_handle ; + + get_filename_pathname (db, filepath) ; + + if (db_file_exists (db_handle, filepath)) + { printf (" %s : already in database\n", db->filename) ; + return 0 ; + } ; + + memset (&info, 0, sizeof (info)) ; + sndfile = sf_open (db->pathname, SFM_READ, &info) ; + sf_command (sndfile, SFC_GET_LOG_INFO, db->logbuf, sizeof (db->logbuf)) ; + checksum = (sndfile == NULL) ? 0 : calc_checksum (sndfile, &info) ; + sf_close (sndfile) ; + + if (sndfile == NULL) + { printf (" %s : could not open : %s\n", db->filename, sf_strerror (NULL)) ; + puts (db->logbuf) ; + return 1 ; + } ; + + single_quote_replace (db->logbuf) ; + + snprintf (db->cmdbuf, sizeof (db->cmdbuf), "insert into sndfile " + "(fname, fpath, srate, frames, channels, format, checksum, logbuf) values" + "('%s','%s',%d,'%ld', %d, '0x%08x', '0x%08x', '%s');", + db->filename, db->pathname, info.samplerate, (long) info.frames, info.channels, info.format, checksum, db->logbuf) ; + + if (strlen (db->cmdbuf) >= sizeof (db->cmdbuf) - 1) + { printf ("strlen (db->cmdbuf) too long.\n") ; + exit (1) ; + } ; + + err = sqlite3_exec (db->sql, db->cmdbuf, callback, 0, &errmsg) ; + if (err != SQLITE_OK) + { printf ("Line %d : SQL error: %s\n", __LINE__, errmsg) ; + puts (db->cmdbuf) ; + } ; + + return 0 ; +} /* db_add_file */ + +int +db_check_file (REG_DB * db_handle, const char * filepath) +{ REGTEST_DB * db ; + int ekey ; + + if (db_file_exists (db_handle, filepath) == 0) + { printf ("\nFile not in database.\n\n") ; + exit (0) ; + } ; + + db = (REGTEST_DB *) db_handle ; + + ekey = get_ekey_from_filename (db, filepath) ; + + return check_file_by_ekey (db, ekey) ; +} /* db_check_file */ + +/*============================================================================== +*/ + +int +db_check_all (REG_DB * db_handle) +{ REGTEST_DB * db ; + char * errmsg ; + int err, ekey ; + + db = (REGTEST_DB *) db_handle ; + + db->ekey_max = 0 ; + + snprintf (db->cmdbuf, sizeof (db->cmdbuf), "select ekey from sndfile") ; + + err = sqlite3_exec (db->sql, db->cmdbuf, (sqlite3_callback) ekey_max_callback, db, &errmsg) ; + if (err != SQLITE_OK) + { printf ("Line %d : SQL error: %s\n", __LINE__, errmsg) ; + puts (db->cmdbuf) ; + } ; + + for (ekey = 1 ; ekey <= db->ekey_max ; ekey++) + if (get_filename_pathname_by_ekey (db, ekey) != 0) + check_file_by_ekey (db, ekey) ; + + return 0 ; +} /* db_check_all */ + + +int +db_list_all (REG_DB * db_handle) +{ + printf ("%s : %p\n", __func__, db_handle) ; + return 0 ; +} /* db_list_all */ + +int +db_del_entry (REG_DB * db_handle, const char * entry) +{ + printf ("%s : %p %s\n", __func__, db_handle, entry) ; + return 0 ; +} /* db_del_entry */ + +/*============================================================================== +*/ + +static int +get_ekey_from_filename (REGTEST_DB * db, const char *filepath) +{ char * errmsg, **result ; + int err, ekey = 0, rows, cols ; + + get_filename_pathname (db, filepath) ; + + snprintf (db->cmdbuf, sizeof (db->cmdbuf), "select ekey from sndfile where fname='%s'", db->filename) ; + + err = sqlite3_get_table (db->sql, db->cmdbuf, &result, &rows, &cols, &errmsg) ; + if (err != SQLITE_OK) + { printf ("Line %d : SQL error: %s\n", __LINE__, errmsg) ; + puts (db->cmdbuf) ; + } ; + + if (cols != 1 || rows != 1) + { printf ("Bad juju!! rows = %d cols = %d\n", rows, cols) ; + exit (1) ; + } ; + + ekey = strtol (result [1], NULL, 10) ; + + sqlite3_free_table (result) ; + + return ekey ; +} /* get_ekey_from_filename */ + +static int +get_filename_pathname_by_ekey (REGTEST_DB * db, int ekey) +{ char *errmsg, **result ; + int err, rows, cols ; + + snprintf (db->cmdbuf, sizeof (db->cmdbuf), "select fname,fpath from sndfile where ekey='%d'", ekey) ; + + err = sqlite3_get_table (db->sql, db->cmdbuf, &result, &rows, &cols, &errmsg) ; + if (err != SQLITE_OK) + { printf ("Line %d : SQL error: %s\n", __LINE__, errmsg) ; + puts (db->cmdbuf) ; + return 0 ; + } ; + + if (cols != 2 || rows != 1) + { printf ("\nError (%s %d) : rows = %d cols = %d\n", __func__, __LINE__, rows, cols) ; + exit (1) ; + } ; + + strncpy (db->filename, result [2], sizeof (db->filename)) ; + strncpy (db->pathname, result [3], sizeof (db->pathname)) ; + + sqlite3_free_table (result) ; + + return 1 ; +} /* get_filename_pathname_by_ekey */ + +static int +check_file_by_ekey (REGTEST_DB * db, int ekey) +{ SNDFILE * sndfile ; + SF_INFO info ; + char * errmsg, **result ; + int err, k, rows, cols, checksum ; + + printf (" %s : ", db->filename) ; + fflush (stdout) ; + + memset (&info, 0, sizeof (info)) ; + sndfile = sf_open (db->pathname, SFM_READ, &info) ; + sf_command (sndfile, SFC_GET_LOG_INFO, db->logbuf, sizeof (db->logbuf)) ; + checksum = (sndfile == NULL) ? 0 : calc_checksum (sndfile, &info) ; + sf_close (sndfile) ; + + if (sndfile == NULL) + { printf ("\n\nError : Could not open '%s' : %s\n", db->pathname, sf_strerror (NULL)) ; + puts (db->logbuf) ; + exit (1) ; + } ; + + single_quote_replace (db->logbuf) ; + + snprintf (db->cmdbuf, sizeof (db->cmdbuf), "select fname,srate,frames,channels,format," + "checksum,logbuf from sndfile where ekey='%d'", ekey) ; + + err = sqlite3_get_table (db->sql, db->cmdbuf, &result, &rows, &cols, &errmsg) ; + if (err != SQLITE_OK) + { printf ("Line %d : SQL error: %s\n", __LINE__, errmsg) ; + puts (db->cmdbuf) ; + } ; + + for (k = 0 ; k < cols ; k++) + { if (strcmp (result [k], "fname") == 0) + { if (strcmp (result [k + cols], db->filename) == 0) + continue ; + printf ("\n\nError : fname doesn't match : %s != %s\n", result [k + cols], db->filename) ; + } ; + + if (strcmp (result [k], "srate") == 0) + { if (strtol (result [k + cols], NULL, 10) == info.samplerate) + continue ; + printf ("\n\nError : srate doesn't match : %s == %d\n", result [k + cols], info.samplerate) ; + } ; + + if (strcmp (result [k], "frames") == 0) + { if (strtoll (result [k + cols], NULL, 10) == info.frames) + continue ; + printf ("\n\nError : frames doesn't match : %s == %ld\n", result [k + cols], (long) info.frames) ; + } ; + + if (strcmp (result [k], "channels") == 0) + { if (strtol (result [k + cols], NULL, 10) == info.channels) + continue ; + printf ("\n\nError : channels doesn't match : %s == %d\n", result [k + cols], info.channels) ; + } ; + + if (strcmp (result [k], "format") == 0) + { if (strtol (result [k + cols], NULL, 16) == info.format) + continue ; + printf ("\n\nError : format doesn't match : %s == 0x%08x\n", result [k + cols], info.format) ; + } ; + + if (strcmp (result [k], "checksum") == 0) + { int db_val = (int) strtoll (result [k + cols], NULL, 16) ; + + if (db_val == checksum) + continue ; + printf ("\n\nError : checksum doesn't match : 0x%08x == 0x%08x\n", db_val, checksum) ; + } ; + + if (strcmp (result [k], "logbuf") == 0) + continue ; + + printf ("\nHere is the old logubuffer :\n\n%s\n\nand the new :\n\n%s\n\n", result [2 * cols - 1], db->logbuf) ; + exit (1) ; + } ; + + sqlite3_free_table (result) ; + + puts ("ok") ; + + return 0 ; +} /* check_file_by_ekey */ + +/*============================================================================== +*/ + +static void +get_filename_pathname (REGTEST_DB * db, const char *filepath) +{ const char * cptr ; + + if (filepath [0] != '/') + { memset (db->pathname, 0, sizeof (db->pathname)) ; + if (getcwd (db->pathname, sizeof (db->pathname)) == NULL) + { perror ("\ngetcwd failed") ; + exit (1) ; + } ; + + db->pathname [strlen (db->pathname)] = '/' ; + strncat (db->pathname, filepath, sizeof (db->pathname)) ; + } + else + strncpy (db->pathname, filepath, sizeof (db->pathname)) ; + + if ((cptr = strrchr (db->pathname, '/')) == NULL) + { printf ("\nError : bad pathname %s\n", filepath) ; + exit (1) ; + } ; + + strncpy (db->filename, cptr + 1, sizeof (db->filename)) ; +} /* get filename_pathname */ + +static void +single_quote_replace (char * buf) +{ while ((buf = strchr (buf, '\'')) != 0) + buf [0] = '"' ; +} /* single_quote_replace */ + +static int +count_callback (REGTEST_DB * db, int argc, char **argv, char **colname) +{ db->count ++ ; + + argc = 0 ; + argv = NULL ; + colname = NULL ; + return 0 ; +} /* count_callback */ + +static int +ekey_max_callback (REGTEST_DB * db, int argc, char **argv, char **unused) +{ int ekey ; + + argc = 0 ; + unused = NULL ; + + ekey = strtol (argv [0], NULL, 10) ; + if (ekey > db->ekey_max) + db->ekey_max = ekey ; + + return 0 ; +} /* ekey_max_callback */ + +static int +callback (void *unused, int argc, char **argv, char **colname) +{ int k ; + + unused = NULL ; + + for (k = 0 ; k < argc ; k++) + printf ("%s = %s\n", colname [k], argv [k] ? argv [k] : "NULL") ; + + printf ("\n") ; + + return 0 ; +} /* callback */ + +#else + +int dummy (void) ; + +int +dummy (void) +{ /* + ** Empty dummy fnction so tha compiler doesn't winge about an + ** empty file. + */ + return 0 ; +} /* dummy */ + +#endif + + +/* +** Do not edit or modify anything in this comment block. +** The following line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: c6bcb7cb-0d9e-47b6-a89a-66304df4d462 +*/ diff --git a/regtest/regtest.h b/regtest/regtest.h new file mode 100644 index 00000000..cc8ecaa7 --- /dev/null +++ b/regtest/regtest.h @@ -0,0 +1,45 @@ +/* +** Copyright (C) 2005 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +typedef struct REG_DB_tag REG_DB ; + +/* In database.c */ +REG_DB * db_open (const char * db_name) ; + +int db_create (const char * dbname) ; + +int db_close (REG_DB * db_handle) ; + +int db_file_exists (REG_DB * db_handle, const char * filename) ; +int db_add_file (REG_DB * db_handle, const char * filename) ; +int db_check_file (REG_DB * db_handle, const char * filename) ; + +int db_list_all (REG_DB * db_handle) ; +int db_check_all (REG_DB * db_handle) ; +int db_del_entry (REG_DB * db_handle, const char * entry) ; + +/* In checksum.c */ +int calc_checksum (SNDFILE * file, const SF_INFO * info) ; + +/* +** Do not edit or modify anything in this comment block. +** The following line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 80138e38-f373-48d3-8152-7f7882a62cd7 +*/ diff --git a/regtest/sndfile-regtest.c b/regtest/sndfile-regtest.c new file mode 100644 index 00000000..ee7eaa9c --- /dev/null +++ b/regtest/sndfile-regtest.c @@ -0,0 +1,128 @@ +/* +** Copyright (C) 2005 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "config.h" + +#include +#include +#include + +#include + +#if HAVE_SQLITE3 + +#include "regtest.h" + +enum +{ OPT_ADD_FILE = 0x0100, + OPT_CREATE_DB = 0x0200, + OPT_DEL_ENTRY = 0x0400, + OPT_LIST_ALL = 0x0800, + OPT_TEST_ALL = 0x1000, + OPT_VERBOSE = 0x2000 +} ; + +static void print_libsndfile_version (void) ; + +int +main (int argc, char * argv []) +{ const char *db_name = "./.sndfile-regtest.db" ; + REG_DB *reg_db ; + int k, retval ; + + if (argc < 2) + { printf ("\nUsage message goes here.\n\n") ; + exit (0) ; + } ; + + if (argc == 2 && strcmp (argv [1], "--create-db") == 0) + return db_create (db_name) ; + + reg_db = db_open (db_name) ; + + if (argc == 2) + { if (strcmp (argv [1], "--list-all") == 0) + return db_list_all (reg_db) ; + + if (strcmp (argv [1], "--check-all") == 0) + { print_libsndfile_version () ; + retval = db_check_all (reg_db) ; + puts ("\nDone.\n") ; + return retval ; + } ; + } ; + + if (argc == 3 && strcmp (argv [1], "--del-entry") == 0) + { db_del_entry (reg_db, argv [2]) ; + db_close (reg_db) ; + return 0 ; + } ; + + if (strcmp (argv [1], "--check-file") == 0) + { print_libsndfile_version () ; + + for (k = 2 ; k < argc ; k++) + db_check_file (reg_db, argv [k]) ; + db_close (reg_db) ; + return 0 ; + } ; + + if (strcmp (argv [1], "--add-file") == 0) + { print_libsndfile_version () ; + + for (k = 2 ; k < argc ; k++) + db_add_file (reg_db, argv [k]) ; + db_close (reg_db) ; + return 0 ; + } ; + + printf ("\nError : unhandled command line args :") ; + for (k = 1 ; k < argc ; k++) + printf (" %s", argv [k]) ; + puts ("\n") ; + + return 1 ; +} /* main */ + +static void +print_libsndfile_version (void) +{ char version [64] ; + + sf_command (NULL, SFC_GET_LIB_VERSION, version, sizeof (version)) ; + printf ("\nsndfile-regtest : using %s\n\n", version) ; +} /* print_lib_version */ + +#else + +int +main (void) +{ + puts ("\nThis program was not compiled with libsqlite3 and hence doesn't work.\n") ; + + return 0 ; +} /* main */ + +#endif + +/* +** Do not edit or modify anything in this comment block. +** The following line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 7f318f08-9bfa-4249-856d-fe994819bdce +*/ diff --git a/sndfile.pc.in b/sndfile.pc.in new file mode 100644 index 00000000..f2a833bd --- /dev/null +++ b/sndfile.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: sndfile +Description: A library for reading and writing audio files +Requires: +Version: @VERSION@ +Libs: -L${libdir} -lsndfile +Cflags: -I${includedir} diff --git a/src/FLAC/AUTHORS b/src/FLAC/AUTHORS new file mode 100644 index 00000000..217baf41 --- /dev/null +++ b/src/FLAC/AUTHORS @@ -0,0 +1,41 @@ +/* FLAC - Free Lossless Audio Codec + * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * This file is part the FLAC project. FLAC is comprised of several + * components distributed under difference licenses. The codec libraries + * are distributed under Xiph.Org's BSD-like license (see the file + * COPYING.Xiph in this distribution). All other programs, libraries, and + * plugins are distributed under the GPL (see COPYING.GPL). The documentation + * is distributed under the Gnu FDL (see COPYING.FDL). Each file in the + * FLAC distribution contains at the top the terms under which it may be + * distributed. + * + * Since this particular file is relevant to all components of FLAC, + * it may be distributed under the Xiph.Org license, which is the least + * restrictive of those mentioned above. See the file COPYING.Xiph in this + * distribution. + */ + + +FLAC (http://flac.sourceforge.net/) is an Open Source lossless audio +codec developed by Josh Coalson . + +Other major contributors and their contributions: +"Andrey Astafiev" +* Russian translation of the HTML documentation + +"Miroslav Lichvar" +* IA-32 assembly versions of several libFLAC routines + +"Brady Patterson" +* AIFF file support, PPC assembly versions of libFLAC routines + +"Daisuke Shimamura" +* i18n support in the XMMS plugin + +"X-Fixer" +* Configuration system, tag editing, and file info in the Winamp2 plugin + +"Matt Zimmerman" +* Libtool/autoconf/automake make system, flac man page + diff --git a/src/FLAC/COPYING.FDL b/src/FLAC/COPYING.FDL new file mode 100644 index 00000000..4a0fe1c8 --- /dev/null +++ b/src/FLAC/COPYING.FDL @@ -0,0 +1,397 @@ + GNU Free Documentation License + Version 1.2, November 2002 + + + Copyright (C) 2000,2001,2002 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + +0. PREAMBLE + +The purpose of this License is to make a manual, textbook, or other +functional and useful document "free" in the sense of freedom: to +assure everyone the effective freedom to copy and redistribute it, +with or without modifying it, either commercially or noncommercially. +Secondarily, this License preserves for the author and publisher a way +to get credit for their work, while not being considered responsible +for modifications made by others. + +This License is a kind of "copyleft", which means that derivative +works of the document must themselves be free in the same sense. It +complements the GNU General Public License, which is a copyleft +license designed for free software. + +We have designed this License in order to use it for manuals for free +software, because free software needs free documentation: a free +program should come with manuals providing the same freedoms that the +software does. But this License is not limited to software manuals; +it can be used for any textual work, regardless of subject matter or +whether it is published as a printed book. We recommend this License +principally for works whose purpose is instruction or reference. + + +1. APPLICABILITY AND DEFINITIONS + +This License applies to any manual or other work, in any medium, that +contains a notice placed by the copyright holder saying it can be +distributed under the terms of this License. Such a notice grants a +world-wide, royalty-free license, unlimited in duration, to use that +work under the conditions stated herein. The "Document", below, +refers to any such manual or work. Any member of the public is a +licensee, and is addressed as "you". You accept the license if you +copy, modify or distribute the work in a way requiring permission +under copyright law. + +A "Modified Version" of the Document means any work containing the +Document or a portion of it, either copied verbatim, or with +modifications and/or translated into another language. + +A "Secondary Section" is a named appendix or a front-matter section of +the Document that deals exclusively with the relationship of the +publishers or authors of the Document to the Document's overall subject +(or to related matters) and contains nothing that could fall directly +within that overall subject. (Thus, if the Document is in part a +textbook of mathematics, a Secondary Section may not explain any +mathematics.) The relationship could be a matter of historical +connection with the subject or with related matters, or of legal, +commercial, philosophical, ethical or political position regarding +them. + +The "Invariant Sections" are certain Secondary Sections whose titles +are designated, as being those of Invariant Sections, in the notice +that says that the Document is released under this License. If a +section does not fit the above definition of Secondary then it is not +allowed to be designated as Invariant. The Document may contain zero +Invariant Sections. If the Document does not identify any Invariant +Sections then there are none. + +The "Cover Texts" are certain short passages of text that are listed, +as Front-Cover Texts or Back-Cover Texts, in the notice that says that +the Document is released under this License. A Front-Cover Text may +be at most 5 words, and a Back-Cover Text may be at most 25 words. + +A "Transparent" copy of the Document means a machine-readable copy, +represented in a format whose specification is available to the +general public, that is suitable for revising the document +straightforwardly with generic text editors or (for images composed of +pixels) generic paint programs or (for drawings) some widely available +drawing editor, and that is suitable for input to text formatters or +for automatic translation to a variety of formats suitable for input +to text formatters. A copy made in an otherwise Transparent file +format whose markup, or absence of markup, has been arranged to thwart +or discourage subsequent modification by readers is not Transparent. +An image format is not Transparent if used for any substantial amount +of text. A copy that is not "Transparent" is called "Opaque". + +Examples of suitable formats for Transparent copies include plain +ASCII without markup, Texinfo input format, LaTeX input format, SGML +or XML using a publicly available DTD, and standard-conforming simple +HTML, PostScript or PDF designed for human modification. Examples of +transparent image formats include PNG, XCF and JPG. Opaque formats +include proprietary formats that can be read and edited only by +proprietary word processors, SGML or XML for which the DTD and/or +processing tools are not generally available, and the +machine-generated HTML, PostScript or PDF produced by some word +processors for output purposes only. + +The "Title Page" means, for a printed book, the title page itself, +plus such following pages as are needed to hold, legibly, the material +this License requires to appear in the title page. For works in +formats which do not have any title page as such, "Title Page" means +the text near the most prominent appearance of the work's title, +preceding the beginning of the body of the text. + +A section "Entitled XYZ" means a named subunit of the Document whose +title either is precisely XYZ or contains XYZ in parentheses following +text that translates XYZ in another language. (Here XYZ stands for a +specific section name mentioned below, such as "Acknowledgements", +"Dedications", "Endorsements", or "History".) To "Preserve the Title" +of such a section when you modify the Document means that it remains a +section "Entitled XYZ" according to this definition. + +The Document may include Warranty Disclaimers next to the notice which +states that this License applies to the Document. These Warranty +Disclaimers are considered to be included by reference in this +License, but only as regards disclaiming warranties: any other +implication that these Warranty Disclaimers may have is void and has +no effect on the meaning of this License. + + +2. VERBATIM COPYING + +You may copy and distribute the Document in any medium, either +commercially or noncommercially, provided that this License, the +copyright notices, and the license notice saying this License applies +to the Document are reproduced in all copies, and that you add no other +conditions whatsoever to those of this License. You may not use +technical measures to obstruct or control the reading or further +copying of the copies you make or distribute. However, you may accept +compensation in exchange for copies. If you distribute a large enough +number of copies you must also follow the conditions in section 3. + +You may also lend copies, under the same conditions stated above, and +you may publicly display copies. + + +3. COPYING IN QUANTITY + +If you publish printed copies (or copies in media that commonly have +printed covers) of the Document, numbering more than 100, and the +Document's license notice requires Cover Texts, you must enclose the +copies in covers that carry, clearly and legibly, all these Cover +Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on +the back cover. Both covers must also clearly and legibly identify +you as the publisher of these copies. The front cover must present +the full title with all words of the title equally prominent and +visible. You may add other material on the covers in addition. +Copying with changes limited to the covers, as long as they preserve +the title of the Document and satisfy these conditions, can be treated +as verbatim copying in other respects. + +If the required texts for either cover are too voluminous to fit +legibly, you should put the first ones listed (as many as fit +reasonably) on the actual cover, and continue the rest onto adjacent +pages. + +If you publish or distribute Opaque copies of the Document numbering +more than 100, you must either include a machine-readable Transparent +copy along with each Opaque copy, or state in or with each Opaque copy +a computer-network location from which the general network-using +public has access to download using public-standard network protocols +a complete Transparent copy of the Document, free of added material. +If you use the latter option, you must take reasonably prudent steps, +when you begin distribution of Opaque copies in quantity, to ensure +that this Transparent copy will remain thus accessible at the stated +location until at least one year after the last time you distribute an +Opaque copy (directly or through your agents or retailers) of that +edition to the public. + +It is requested, but not required, that you contact the authors of the +Document well before redistributing any large number of copies, to give +them a chance to provide you with an updated version of the Document. + + +4. MODIFICATIONS + +You may copy and distribute a Modified Version of the Document under +the conditions of sections 2 and 3 above, provided that you release +the Modified Version under precisely this License, with the Modified +Version filling the role of the Document, thus licensing distribution +and modification of the Modified Version to whoever possesses a copy +of it. In addition, you must do these things in the Modified Version: + +A. Use in the Title Page (and on the covers, if any) a title distinct + from that of the Document, and from those of previous versions + (which should, if there were any, be listed in the History section + of the Document). You may use the same title as a previous version + if the original publisher of that version gives permission. +B. List on the Title Page, as authors, one or more persons or entities + responsible for authorship of the modifications in the Modified + Version, together with at least five of the principal authors of the + Document (all of its principal authors, if it has fewer than five), + unless they release you from this requirement. +C. State on the Title page the name of the publisher of the + Modified Version, as the publisher. +D. Preserve all the copyright notices of the Document. +E. Add an appropriate copyright notice for your modifications + adjacent to the other copyright notices. +F. Include, immediately after the copyright notices, a license notice + giving the public permission to use the Modified Version under the + terms of this License, in the form shown in the Addendum below. +G. Preserve in that license notice the full lists of Invariant Sections + and required Cover Texts given in the Document's license notice. +H. Include an unaltered copy of this License. +I. Preserve the section Entitled "History", Preserve its Title, and add + to it an item stating at least the title, year, new authors, and + publisher of the Modified Version as given on the Title Page. If + there is no section Entitled "History" in the Document, create one + stating the title, year, authors, and publisher of the Document as + given on its Title Page, then add an item describing the Modified + Version as stated in the previous sentence. +J. Preserve the network location, if any, given in the Document for + public access to a Transparent copy of the Document, and likewise + the network locations given in the Document for previous versions + it was based on. These may be placed in the "History" section. + You may omit a network location for a work that was published at + least four years before the Document itself, or if the original + publisher of the version it refers to gives permission. +K. For any section Entitled "Acknowledgements" or "Dedications", + Preserve the Title of the section, and preserve in the section all + the substance and tone of each of the contributor acknowledgements + and/or dedications given therein. +L. Preserve all the Invariant Sections of the Document, + unaltered in their text and in their titles. Section numbers + or the equivalent are not considered part of the section titles. +M. Delete any section Entitled "Endorsements". Such a section + may not be included in the Modified Version. +N. Do not retitle any existing section to be Entitled "Endorsements" + or to conflict in title with any Invariant Section. +O. Preserve any Warranty Disclaimers. + +If the Modified Version includes new front-matter sections or +appendices that qualify as Secondary Sections and contain no material +copied from the Document, you may at your option designate some or all +of these sections as invariant. To do this, add their titles to the +list of Invariant Sections in the Modified Version's license notice. +These titles must be distinct from any other section titles. + +You may add a section Entitled "Endorsements", provided it contains +nothing but endorsements of your Modified Version by various +parties--for example, statements of peer review or that the text has +been approved by an organization as the authoritative definition of a +standard. + +You may add a passage of up to five words as a Front-Cover Text, and a +passage of up to 25 words as a Back-Cover Text, to the end of the list +of Cover Texts in the Modified Version. Only one passage of +Front-Cover Text and one of Back-Cover Text may be added by (or +through arrangements made by) any one entity. If the Document already +includes a cover text for the same cover, previously added by you or +by arrangement made by the same entity you are acting on behalf of, +you may not add another; but you may replace the old one, on explicit +permission from the previous publisher that added the old one. + +The author(s) and publisher(s) of the Document do not by this License +give permission to use their names for publicity for or to assert or +imply endorsement of any Modified Version. + + +5. COMBINING DOCUMENTS + +You may combine the Document with other documents released under this +License, under the terms defined in section 4 above for modified +versions, provided that you include in the combination all of the +Invariant Sections of all of the original documents, unmodified, and +list them all as Invariant Sections of your combined work in its +license notice, and that you preserve all their Warranty Disclaimers. + +The combined work need only contain one copy of this License, and +multiple identical Invariant Sections may be replaced with a single +copy. If there are multiple Invariant Sections with the same name but +different contents, make the title of each such section unique by +adding at the end of it, in parentheses, the name of the original +author or publisher of that section if known, or else a unique number. +Make the same adjustment to the section titles in the list of +Invariant Sections in the license notice of the combined work. + +In the combination, you must combine any sections Entitled "History" +in the various original documents, forming one section Entitled +"History"; likewise combine any sections Entitled "Acknowledgements", +and any sections Entitled "Dedications". You must delete all sections +Entitled "Endorsements". + + +6. COLLECTIONS OF DOCUMENTS + +You may make a collection consisting of the Document and other documents +released under this License, and replace the individual copies of this +License in the various documents with a single copy that is included in +the collection, provided that you follow the rules of this License for +verbatim copying of each of the documents in all other respects. + +You may extract a single document from such a collection, and distribute +it individually under this License, provided you insert a copy of this +License into the extracted document, and follow this License in all +other respects regarding verbatim copying of that document. + + +7. AGGREGATION WITH INDEPENDENT WORKS + +A compilation of the Document or its derivatives with other separate +and independent documents or works, in or on a volume of a storage or +distribution medium, is called an "aggregate" if the copyright +resulting from the compilation is not used to limit the legal rights +of the compilation's users beyond what the individual works permit. +When the Document is included in an aggregate, this License does not +apply to the other works in the aggregate which are not themselves +derivative works of the Document. + +If the Cover Text requirement of section 3 is applicable to these +copies of the Document, then if the Document is less than one half of +the entire aggregate, the Document's Cover Texts may be placed on +covers that bracket the Document within the aggregate, or the +electronic equivalent of covers if the Document is in electronic form. +Otherwise they must appear on printed covers that bracket the whole +aggregate. + + +8. TRANSLATION + +Translation is considered a kind of modification, so you may +distribute translations of the Document under the terms of section 4. +Replacing Invariant Sections with translations requires special +permission from their copyright holders, but you may include +translations of some or all Invariant Sections in addition to the +original versions of these Invariant Sections. You may include a +translation of this License, and all the license notices in the +Document, and any Warranty Disclaimers, provided that you also include +the original English version of this License and the original versions +of those notices and disclaimers. In case of a disagreement between +the translation and the original version of this License or a notice +or disclaimer, the original version will prevail. + +If a section in the Document is Entitled "Acknowledgements", +"Dedications", or "History", the requirement (section 4) to Preserve +its Title (section 1) will typically require changing the actual +title. + + +9. TERMINATION + +You may not copy, modify, sublicense, or distribute the Document except +as expressly provided for under this License. Any other attempt to +copy, modify, sublicense or distribute the Document 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. + + +10. FUTURE REVISIONS OF THIS LICENSE + +The Free Software Foundation may publish new, revised versions +of the GNU Free Documentation 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. See +http://www.gnu.org/copyleft/. + +Each version of the License is given a distinguishing version number. +If the Document specifies that a particular numbered version of this +License "or any later version" applies to it, you have the option of +following the terms and conditions either of that specified version or +of any later version that has been published (not as a draft) by the +Free Software Foundation. If the Document does not specify a version +number of this License, you may choose any version ever published (not +as a draft) by the Free Software Foundation. + + +ADDENDUM: How to use this License for your documents + +To use this License in a document you have written, include a copy of +the License in the document and put the following copyright and +license notices just after the title page: + + Copyright (c) YEAR YOUR NAME. + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.2 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. + A copy of the license is included in the section entitled "GNU + Free Documentation License". + +If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, +replace the "with...Texts." line with this: + + with the Invariant Sections being LIST THEIR TITLES, with the + Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. + +If you have Invariant Sections without Cover Texts, or some other +combination of the three, merge those two alternatives to suit the +situation. + +If your document contains nontrivial examples of program code, we +recommend releasing these examples in parallel under your choice of +free software license, such as the GNU General Public License, +to permit their use in free software. diff --git a/src/FLAC/COPYING.GPL b/src/FLAC/COPYING.GPL new file mode 100644 index 00000000..c3c7a9ea --- /dev/null +++ b/src/FLAC/COPYING.GPL @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/src/FLAC/COPYING.LGPL b/src/FLAC/COPYING.LGPL new file mode 100644 index 00000000..5ab7695a --- /dev/null +++ b/src/FLAC/COPYING.LGPL @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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/src/FLAC/COPYING.Xiph b/src/FLAC/COPYING.Xiph new file mode 100644 index 00000000..0a104a9c --- /dev/null +++ b/src/FLAC/COPYING.Xiph @@ -0,0 +1,28 @@ +Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +- Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +- Neither the name of the Xiph.org Foundation nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/FLAC/Makefile.am b/src/FLAC/Makefile.am new file mode 100644 index 00000000..2b9a80e1 --- /dev/null +++ b/src/FLAC/Makefile.am @@ -0,0 +1,39 @@ +# FLAC - Free Lossless Audio Codec +# Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson +# +# This file is part the FLAC project. FLAC is comprised of several +# components distributed under difference licenses. The codec libraries +# are distributed under Xiph.Org's BSD-like license (see the file +# COPYING.Xiph in this distribution). All other programs, libraries, and +# plugins are distributed under the GPL (see COPYING.GPL). The documentation +# is distributed under the Gnu FDL (see COPYING.FDL). Each file in the +# FLAC distribution contains at the top the terms under which it may be +# distributed. +# +# Since this particular file is relevant to all components of FLAC, +# it may be distributed under the Xiph.Org license, which is the least +# restrictive of those mentioned above. See the file COPYING.Xiph in this +# distribution. + +# +# automake provides the following useful targets: +# +# all: build all programs and libraries using the current +# configuration (set by configure) +# +# check: build and run all self-tests +# +# clean: remove everything except what's required to build everything +# +# distclean: remove everything except what goes in the distribution +# + +SUBDIRS = include src test + +EXTRA_DIST = \ + COPYING.FDL \ + COPYING.GPL \ + COPYING.LGPL \ + COPYING.Xiph \ + README \ + strip_non_asm_libtool_args.sh diff --git a/src/FLAC/README b/src/FLAC/README new file mode 100644 index 00000000..aa162b2f --- /dev/null +++ b/src/FLAC/README @@ -0,0 +1,269 @@ +/* FLAC - Free Lossless Audio Codec + * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * This file is part the FLAC project. FLAC is comprised of several + * components distributed under difference licenses. The codec libraries + * are distributed under Xiph.Org's BSD-like license (see the file + * COPYING.Xiph in this distribution). All other programs, libraries, and + * plugins are distributed under the LGPL or GPL (see COPYING.LGPL and + * COPYING.GPL). The documentation is distributed under the Gnu FDL (see + * COPYING.FDL). Each file in the FLAC distribution contains at the top the + * terms under which it may be distributed. + * + * Since this particular file is relevant to all components of FLAC, + * it may be distributed under the Xiph.Org license, which is the least + * restrictive of those mentioned above. See the file COPYING.Xiph in this + * distribution. + */ + + +FLAC (http://flac.sourceforge.net/) is an Open Source lossless audio +codec developed by Josh Coalson. + +FLAC is comprised of + * `libFLAC', a library which implements reference encoders and + decoders for native FLAC and Ogg FLAC, and a metadata interface + * `libFLAC++', a C++ object wrapper library around libFLAC + * `flac', a command-line program for encoding and decoding files + * `metaflac', a command-line program for viewing and editing FLAC + metadata + * player plugins for XMMS and Winamp + * user and API documentation + +The libraries (libFLAC, libFLAC++) are +licensed under Xiph.org's BSD-like license (see COPYING.Xiph). All other +programs and plugins are licensed under the GNU General Public License +(see COPYING.GPL). The documentation is licensed under the GNU Free +Documentation License (see COPYING.FDL). + + +=============================================================================== +FLAC - 1.1.4 - Contents +=============================================================================== + +- Introduction +- Prerequisites +- Building in a GNU environment +- Building with Makefile.lite +- Building with MSVC +- Building on Mac OS X +- Note to embedded developers + + +=============================================================================== +Introduction +=============================================================================== + +This is the source release for the FLAC project. See + + doc/html/index.html + +for full documentation. + +A brief description of the directory tree: + + doc/ the HTML documentation + flac.pbproj/ the Mac OS X Project Builder project + include/ public include files for libFLAC and libFLAC++ + man/ the man page for `flac' + src/ the source code and private headers + test/ the test scripts + + +=============================================================================== +Prerequisites +=============================================================================== + +To build FLAC with support for Ogg FLAC you must have built and installed +libogg according to the specific instructions below. You must have +libogg 1.1.2 or greater, or there will be seeking problems with Ogg FLAC. + +If you are building on x86 and want the assembly optimizations, you will +need to have NASM >= 0.98.30 installed according to the specific instructions +below. + + +=============================================================================== +Building in a GNU environment +=============================================================================== + +FLAC uses autoconf and libtool for configuring and building. +Better documentation for these will be forthcoming, but in +general, this should work: + +./configure && make && make check && make install + +The 'make check' step is optional; omit it to skip all the tests, +which can take several hours and use around 70-80 megs of disk space. +Even though it will stop with an explicit message on any failure, it +does print out a lot of stuff so you might want to capture the output +to a file if you're having a problem. Also, don't run 'make check' +as root because it confuses some of the tests. + +NOTE: Despite our best efforts it's entirely possible to have +problems when using older versions of autoconf, automake, or +libtool. If you have the latest versions and still can't get it +to work, see the next section on Makefile.lite. + +There are a few FLAC-specific arguments you can give to +`configure': + +--enable-debug : Builds everything with debug symbols and some +extra (and more verbose) error checking. + +--disable-asm-optimizations : Disables the compilation of the +assembly routines. Many routines have assembly versions for +speed and `configure' is pretty good about knowing what is +supported, but you can use this option to build only from the +C sources. + +--enable-sse : If you are building for an x86 CPU that supports +SSE instructions, you can enable some of the faster routines +if your operating system also supports SSE instructions. flac +can tell if the CPU supports the instructions but currently has +no way to test if the OS does, so if it does, you must pass +this argument to configure to use the SSE routines. If flac +crashes when built with this option you will have to go back and +configure without --enable-sse. Note that +--disable-asm-optimizations implies --disable-sse. + +--enable-local-xmms-plugin : Installs the FLAC XMMS plugin in +$HOME/.xmms/Plugins, instead of the global XMMS plugin area +(usually /usr/lib/xmms/Input). + +--with-ogg= +--with-xmms-prefix= +--with-libiconv-prefix= +Use these if you have these packages but configure can't find them. + +If you want to build completely from scratch (i.e. starting with just +configure.in and Makefile.am) you should be able to just run 'autogen.sh' +but make sure and read the comments in that file first. + + +=============================================================================== +Building with Makefile.lite +=============================================================================== + +There is a more lightweight build system for do-it-yourself-ers. +It is also useful if configure isn't working, which may be the +case since lately we've had some problems with different versions +of automake and libtool. The Makefile.lite system should work +on GNU systems with few or no adjustments. + +From the top level just 'make -f Makefile.lite'. You can +specify zero or one optional target from 'release', 'debug', +'test', or 'clean'. The default is 'release'. There is no +'install' target but everything you need will end up in the +obj/ directory. + +If you are not on an x86 system or you don't have nasm, you +may have to change the DEFINES in src/libFLAC/Makefile.lite. If +you don't have nasm, remove -DFLAC__HAS_NASM. If your target is +not an x86, change -DFLAC__CPU_IA32 to -DFLAC__CPU_UNKNOWN. + + +=============================================================================== +Building with MSVC +=============================================================================== + +There are .dsp projects and a master FLAC.dsw workspace to build all +the libraries and executables with MSVC6. There are also .vcproj +projects and a master FLAC.sln solution to build all the libraries and +executables with VC++ 2005. + +Prerequisite: you must have the Ogg libraries installed as described +later. + +Prerequisite: you must have nasm installed, and nasmw.exe must be in +your PATH, or the path to nasmw.exe must be added to the list of +directories for executable files in the MSVC global options. + +MSVC6: +To build everything, run Developer Studio, do File|Open Workspace, +and open FLAC.dsw. Select "Build | Set active configuration..." +from the menu, then in the dialog, select "All - Win32 Release" (or +Debug if you prefer). Click "Ok" then hit F7 to build. + +VC++ 2005: +To build everything, run Visual Studio, do File|Open and open FLAC.sln. +From the dropdown in the toolbar, select "Release" instead of "Debug", +then hit F7 to build. + +Either way, this will build all libraries both statically (e.g. +obj\release\lib\libFLAC_static.lib) and as DLLs (e.g. +obj\release\lib\libFLAC.dll), and it will build all binaries, statically +linked (e.g. obj\release\bin\flac.exe). + +Everything will end up in the "obj" directory. DLLs and .exe files +are all that are needed and can be copied to an installation area and +added to the PATH. The plugins have to be copied to their appropriate +place in the player area. For Winamp2 this is \Plugins. + +By default the code is configured with Ogg support. Before building FLAC +you will need to get the Ogg source distribution +(see http://xiph.org/ogg/vorbis/download/), build ogg_static.lib (load and +build win32\ogg_static.dsp), copy ogg_static.lib into FLAC's +'obj\release\lib' directory, and copy the entire include\ogg tree into +FLAC's 'include' directory (so that there is an 'ogg' directory in FLAC's +'include' directory with the files ogg.h, os_types.h and config_types.h). + +If you want to build without Ogg support, instead edit all .dsp or +.vcproj files and remove any occurrences of "/D FLAC__HAS_OGG". + + +=============================================================================== +Building on Mac OS X +=============================================================================== + +If you have Fink, the GNU flow above should work. Otherwise, +there is a Project Builder project in the top-level source +directory to build libFLAC and the command-line utilities on +Mac OS X. In a terminal, cd to the top-level directory (the +one that contains this README file) and type: + + pbxbuild -alltargets + +This will create everything and leave it in the build/ directory. +Don't worry about the rest of the stuff that is in build/ or +the stuff that was already there before building. + +The Project Builder project requires that you have libiconv and +libogg in /sw, ala fink. If you don't, you'll need to install +them somewhere and change the path to them in the Library Paths +section of several targets. + +It also assumes the CPU supports Altivec instructions. If it does +not, you will also have to add -DFLAC__NO_ASM to the CFLAGS in the +libFLAC target. + +There currently is no install procedure; you will have to +manually copy the tools to wherever you need them. + + +=============================================================================== +Note to embedded developers +=============================================================================== + +libFLAC has grown larger over time as more functionality has been +included, but much of it may be unnecessary for a particular embedded +implementation. Unused parts may be pruned by some simple editing of +configure.in and src/libFLAC/Makefile.am; the following dependency +graph shows which modules may be pruned without breaking things +further down: + +stream_encoder.h + stream_decoder.h + format.h + +stream_decoder.h + format.h + +metadata.h + format.h + +In other words, for pure decoding applications, both the stream encoder +and metadata editing interfaces can be safely removed. + +There is a section dedicated to embedded use in the libFLAC API +HTML documentation (see doc/html/api/index.html). diff --git a/src/FLAC/include/FLAC/Makefile.am b/src/FLAC/include/FLAC/Makefile.am new file mode 100644 index 00000000..19f49b1f --- /dev/null +++ b/src/FLAC/include/FLAC/Makefile.am @@ -0,0 +1,42 @@ +# libFLAC - Free Lossless Audio Codec library +# Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# - Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# - Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# - Neither the name of the Xiph.org Foundation nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +flaccincludedir = $(includedir)/FLAC + +flaccinclude_HEADERS = \ + all.h \ + assert.h \ + callback.h \ + export.h \ + format.h \ + metadata.h \ + ordinals.h \ + stream_decoder.h \ + stream_encoder.h diff --git a/src/FLAC/include/FLAC/all.h b/src/FLAC/include/FLAC/all.h new file mode 100644 index 00000000..22d0d440 --- /dev/null +++ b/src/FLAC/include/FLAC/all.h @@ -0,0 +1,344 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__ALL_H +#define FLAC__ALL_H + +#include "export.h" + +#include "assert.h" +#include "callback.h" +#include "format.h" +#include "metadata.h" +#include "ordinals.h" +#include "stream_decoder.h" +#include "stream_encoder.h" + +/** \mainpage + * + * \section intro Introduction + * + * This is the documentation for the FLAC C and C++ APIs. It is + * highly interconnected; this introduction should give you a top + * level idea of the structure and how to find the information you + * need. As a prerequisite you should have at least a basic + * knowledge of the FLAC format, documented + * here. + * + * \section c_api FLAC C API + * + * The FLAC C API is the interface to libFLAC, a set of structures + * describing the components of FLAC streams, and functions for + * encoding and decoding streams, as well as manipulating FLAC + * metadata in files. The public include files will be installed + * in your include area (for example /usr/include/FLAC/...). + * + * By writing a little code and linking against libFLAC, it is + * relatively easy to add FLAC support to another program. The + * library is licensed under Xiph's BSD license. + * Complete source code of libFLAC as well as the command-line + * encoder and plugins is available and is a useful source of + * examples. + * + * Aside from encoders and decoders, libFLAC provides a powerful + * metadata interface for manipulating metadata in FLAC files. It + * allows the user to add, delete, and modify FLAC metadata blocks + * and it can automatically take advantage of PADDING blocks to avoid + * rewriting the entire FLAC file when changing the size of the + * metadata. + * + * libFLAC usually only requires the standard C library and C math + * library. In particular, threading is not used so there is no + * dependency on a thread library. However, libFLAC does not use + * global variables and should be thread-safe. + * + * libFLAC also supports encoding to and decoding from Ogg FLAC. + * However the metadata editing interfaces currently have limited + * read-only support for Ogg FLAC files. + * + * \section cpp_api FLAC C++ API + * + * The FLAC C++ API is a set of classes that encapsulate the + * structures and functions in libFLAC. They provide slightly more + * functionality with respect to metadata but are otherwise + * equivalent. For the most part, they share the same usage as + * their counterparts in libFLAC, and the FLAC C API documentation + * can be used as a supplement. The public include files + * for the C++ API will be installed in your include area (for + * example /usr/include/FLAC++/...). + * + * libFLAC++ is also licensed under + * Xiph's BSD license. + * + * \section getting_started Getting Started + * + * A good starting point for learning the API is to browse through + * the modules. Modules are logical + * groupings of related functions or classes, which correspond roughly + * to header files or sections of header files. Each module includes a + * detailed description of the general usage of its functions or + * classes. + * + * From there you can go on to look at the documentation of + * individual functions. You can see different views of the individual + * functions through the links in top bar across this page. + * + * \section porting_guide Porting Guide + * + * Starting with FLAC 1.1.3 a \link porting Porting Guide \endlink + * has been introduced which gives detailed instructions on how to + * port your code to newer versions of FLAC. + * + * \section embedded_developers Embedded Developers + * + * libFLAC has grown larger over time as more functionality has been + * included, but much of it may be unnecessary for a particular embedded + * implementation. Unused parts may be pruned by some simple editing of + * src/libFLAC/Makefile.am. In general, the decoders, encoders, and + * metadata interface are all independent from each other. + * + * It is easiest to just describe the dependencies: + * + * - All modules depend on the \link flac_format Format \endlink module. + * - The decoders and encoders depend on the bitbuffer. + * - The decoder is independent of the encoder. The encoder uses the + * decoder because of the verify feature, but this can be removed if + * not needed. + * - Parts of the metadata interface require the stream decoder (but not + * the encoder). + * - Ogg support is selectable through the compile time macro + * \c FLAC__HAS_OGG. + * + * For example, if your application only requires the stream decoder, no + * encoder, and no metadata interface, you can remove the stream encoder + * and the metadata interface, which will greatly reduce the size of the + * library. + */ + +/** \defgroup porting Porting Guide for New Versions + * + * This module describes differences in the library interfaces from + * version to version. It assists in the porting of code that uses + * the libraries to newer versions of FLAC. + * + * One simple facility for making porting easier that has been added + * in FLAC 1.1.3 is a set of \c #defines in \c export.h of each + * library's includes (e.g. \c include/FLAC/export.h). The + * \c #defines mirror the libraries' + * libtool version numbers, + * e.g. in libFLAC there are \c FLAC_API_VERSION_CURRENT, + * \c FLAC_API_VERSION_REVISION, and \c FLAC_API_VERSION_AGE. + * These can be used to support multiple versions of an API during the + * transition phase, e.g. + * + * \code + * #if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7 + * legacy code + * #else + * new code + * #endif + * \endcode + * + * The the source will work for multiple versions and the legacy code can + * easily be removed when the transition is complete. + * + * Another available symbol is FLAC_API_SUPPORTS_OGG_FLAC (defined in + * include/FLAC/export.h), which can be used to determine whether or not + * the library has been compiled with support for Ogg FLAC. This is + * simpler than trying to call an Ogg init function and catching the + * error. + */ + +/** \defgroup porting_1_1_2_to_1_1_3 Porting from FLAC 1.1.2 to 1.1.3 + * \ingroup porting + * + * \brief + * This module describes porting from FLAC 1.1.2 to FLAC 1.1.3. + * + * The main change between the APIs in 1.1.2 and 1.1.3 is that they have + * been simplified. First, libOggFLAC has been merged into libFLAC and + * libOggFLAC++ has been merged into libFLAC++. Second, both the three + * decoding layers and three encoding layers have been merged into a + * single stream decoder and stream encoder. That is, the functionality + * of FLAC__SeekableStreamDecoder and FLAC__FileDecoder has been merged + * into FLAC__StreamDecoder, and FLAC__SeekableStreamEncoder and + * FLAC__FileEncoder into FLAC__StreamEncoder. Only the + * FLAC__StreamDecoder and FLAC__StreamEncoder remain. What this means + * is there is now a single API that can be used to encode or decode + * streams to/from native FLAC or Ogg FLAC and the single API can work + * on both seekable and non-seekable streams. + * + * Instead of creating an encoder or decoder of a certain layer, now the + * client will always create a FLAC__StreamEncoder or + * FLAC__StreamDecoder. The old layers are now differentiated by the + * initialization function. For example, for the decoder, + * FLAC__stream_decoder_init() has been replaced by + * FLAC__stream_decoder_init_stream(). This init function takes + * callbacks for the I/O, and the seeking callbacks are optional. This + * allows the client to use the same object for seekable and + * non-seekable streams. For decoding a FLAC file directly, the client + * can use FLAC__stream_decoder_init_file() and pass just a filename + * and fewer callbacks; most of the other callbacks are supplied + * internally. For situations where fopen()ing by filename is not + * possible (e.g. Unicode filenames on Windows) the client can instead + * open the file itself and supply the FILE* to + * FLAC__stream_decoder_init_FILE(). The init functions now returns a + * FLAC__StreamDecoderInitStatus instead of FLAC__StreamDecoderState. + * Since the callbacks and client data are now passed to the init + * function, the FLAC__stream_decoder_set_*_callback() functions and + * FLAC__stream_decoder_set_client_data() are no longer needed. The + * rest of the calls to the decoder are the same as before. + * + * There are counterpart init functions for Ogg FLAC, e.g. + * FLAC__stream_decoder_init_ogg_stream(). All the rest of the calls + * and callbacks are the same as for native FLAC. + * + * As an example, in FLAC 1.1.2 a seekable stream decoder would have + * been set up like so: + * + * \code + * FLAC__SeekableStreamDecoder *decoder = FLAC__seekable_stream_decoder_new(); + * if(decoder == NULL) do_something; + * FLAC__seekable_stream_decoder_set_md5_checking(decoder, true); + * [... other settings ...] + * FLAC__seekable_stream_decoder_set_read_callback(decoder, my_read_callback); + * FLAC__seekable_stream_decoder_set_seek_callback(decoder, my_seek_callback); + * FLAC__seekable_stream_decoder_set_tell_callback(decoder, my_tell_callback); + * FLAC__seekable_stream_decoder_set_length_callback(decoder, my_length_callback); + * FLAC__seekable_stream_decoder_set_eof_callback(decoder, my_eof_callback); + * FLAC__seekable_stream_decoder_set_write_callback(decoder, my_write_callback); + * FLAC__seekable_stream_decoder_set_metadata_callback(decoder, my_metadata_callback); + * FLAC__seekable_stream_decoder_set_error_callback(decoder, my_error_callback); + * FLAC__seekable_stream_decoder_set_client_data(decoder, my_client_data); + * if(FLAC__seekable_stream_decoder_init(decoder) != FLAC__SEEKABLE_STREAM_DECODER_OK) do_something; + * \endcode + * + * In FLAC 1.1.3 it is like this: + * + * \code + * FLAC__StreamDecoder *decoder = FLAC__stream_decoder_new(); + * if(decoder == NULL) do_something; + * FLAC__stream_decoder_set_md5_checking(decoder, true); + * [... other settings ...] + * if(FLAC__stream_decoder_init_stream( + * decoder, + * my_read_callback, + * my_seek_callback, // or NULL + * my_tell_callback, // or NULL + * my_length_callback, // or NULL + * my_eof_callback, // or NULL + * my_write_callback, + * my_metadata_callback, // or NULL + * my_error_callback, + * my_client_data + * ) != FLAC__STREAM_DECODER_INIT_STATUS_OK) do_something; + * \endcode + * + * or you could do; + * + * \code + * [...] + * FILE *file = fopen("somefile.flac","rb"); + * if(file == NULL) do_somthing; + * if(FLAC__stream_decoder_init_FILE( + * decoder, + * file, + * my_write_callback, + * my_metadata_callback, // or NULL + * my_error_callback, + * my_client_data + * ) != FLAC__STREAM_DECODER_INIT_STATUS_OK) do_something; + * \endcode + * + * or just: + * + * \code + * [...] + * if(FLAC__stream_decoder_init_file( + * decoder, + * "somefile.flac", + * my_write_callback, + * my_metadata_callback, // or NULL + * my_error_callback, + * my_client_data + * ) != FLAC__STREAM_DECODER_INIT_STATUS_OK) do_something; + * \endcode + * + * Another small change to the decoder is in how it handles unparseable + * streams. Before, when the decoder found an unparseable stream + * (reserved for when the decoder encounters a stream from a future + * encoder that it can't parse), it changed the state to + * \c FLAC__STREAM_DECODER_UNPARSEABLE_STREAM. Now the decoder instead + * drops sync and calls the error callback with a new error code + * \c FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM. This is + * more robust. If your error callback does not discriminate on the the + * error state, your code does not need to be changed. + * + * The encoder now has a new setting: + * FLAC__stream_encoder_set_apodization(). This is for setting the + * method used to window the data before LPC analysis. You only need to + * add a call to this function if the default is not suitable. There + * are also two new convenience functions that may be useful: + * FLAC__metadata_object_cuesheet_calculate_cddb_id() and + * FLAC__metadata_get_cuesheet(). + * + * The \a bytes parameter to FLAC__StreamDecoderReadCallback, + * FLAC__StreamEncoderReadCallback, and FLAC__StreamEncoderWriteCallback + * is now \c size_t instead of \c unsigned. + */ + +/** \defgroup porting_1_1_3_to_1_1_4 Porting from FLAC 1.1.3 to 1.1.4 + * \ingroup porting + * + * \brief + * This module describes porting from FLAC 1.1.3 to FLAC 1.1.4. + * + * There were no changes to any of the interfaces from 1.1.3 to 1.1.4. + * There was a slight change in the implementation of + * FLAC__stream_encoder_set_metadata(); the function now makes a copy + * of the \a metadata array of pointers so the client no longer needs + * to maintain it after the call. The objects themselves that are + * pointed to by the array are still not copied though and must be + * maintained until the call to FLAC__stream_encoder_finish(). + */ + +/** \defgroup flac FLAC C API + * + * The FLAC C API is the interface to libFLAC, a set of structures + * describing the components of FLAC streams, and functions for + * encoding and decoding streams, as well as manipulating FLAC + * metadata in files. + * + * You should start with the format components as all other modules + * are dependent on it. + */ + +#endif diff --git a/src/FLAC/include/FLAC/assert.h b/src/FLAC/include/FLAC/assert.h new file mode 100644 index 00000000..3fc03f31 --- /dev/null +++ b/src/FLAC/include/FLAC/assert.h @@ -0,0 +1,45 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__ASSERT_H +#define FLAC__ASSERT_H + +/* we need this since some compilers (like MSVC) leave assert()s on release code (and we don't want to use their ASSERT) */ +#ifdef DEBUG +#include +#define FLAC__ASSERT(x) assert(x) +#define FLAC__ASSERT_DECLARATION(x) x +#else +#define FLAC__ASSERT(x) +#define FLAC__ASSERT_DECLARATION(x) +#endif + +#endif diff --git a/src/FLAC/include/FLAC/callback.h b/src/FLAC/include/FLAC/callback.h new file mode 100644 index 00000000..c9541210 --- /dev/null +++ b/src/FLAC/include/FLAC/callback.h @@ -0,0 +1,184 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__CALLBACK_H +#define FLAC__CALLBACK_H + +#include "ordinals.h" +#include /* for size_t */ + +/** \file include/FLAC/callback.h + * + * \brief + * This module defines the structures for describing I/O callbacks + * to the other FLAC interfaces. + * + * See the detailed documentation for callbacks in the + * \link flac_callbacks callbacks \endlink module. + */ + +/** \defgroup flac_callbacks FLAC/callback.h: I/O callback structures + * \ingroup flac + * + * \brief + * This module defines the structures for describing I/O callbacks + * to the other FLAC interfaces. + * + * The purpose of the I/O callback functions is to create a common way + * for the metadata interfaces to handle I/O. + * + * Originally the metadata interfaces required filenames as the way of + * specifying FLAC files to operate on. This is problematic in some + * environments so there is an additional option to specify a set of + * callbacks for doing I/O on the FLAC file, instead of the filename. + * + * In addition to the callbacks, a FLAC__IOHandle type is defined as an + * opaque structure for a data source. + * + * The callback function prototypes are similar (but not identical) to the + * stdio functions fread, fwrite, fseek, ftell, feof, and fclose. If you use + * stdio streams to implement the callbacks, you can pass fread, fwrite, and + * fclose anywhere a FLAC__IOCallback_Read, FLAC__IOCallback_Write, or + * FLAC__IOCallback_Close is required, and a FILE* anywhere a FLAC__IOHandle + * is required. \warning You generally CANNOT directly use fseek or ftell + * for FLAC__IOCallback_Seek or FLAC__IOCallback_Tell since on most systems + * these use 32-bit offsets and FLAC requires 64-bit offsets to deal with + * large files. You will have to find an equivalent function (e.g. ftello), + * or write a wrapper. The same is true for feof() since this is usually + * implemented as a macro, not as a function whose address can be taken. + * + * \{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** This is the opaque handle type used by the callbacks. Typically + * this is a \c FILE* or address of a file descriptor. + */ +typedef void* FLAC__IOHandle; + +/** Signature for the read callback. + * The signature and semantics match POSIX fread() implementations + * and can generally be used interchangeably. + * + * \param ptr The address of the read buffer. + * \param size The size of the records to be read. + * \param nmemb The number of records to be read. + * \param handle The handle to the data source. + * \retval size_t + * The number of records read. + */ +typedef size_t (*FLAC__IOCallback_Read) (void *ptr, size_t size, size_t nmemb, FLAC__IOHandle handle); + +/** Signature for the write callback. + * The signature and semantics match POSIX fwrite() implementations + * and can generally be used interchangeably. + * + * \param ptr The address of the write buffer. + * \param size The size of the records to be written. + * \param nmemb The number of records to be written. + * \param handle The handle to the data source. + * \retval size_t + * The number of records written. + */ +typedef size_t (*FLAC__IOCallback_Write) (const void *ptr, size_t size, size_t nmemb, FLAC__IOHandle handle); + +/** Signature for the seek callback. + * The signature and semantics mostly match POSIX fseek() WITH ONE IMPORTANT + * EXCEPTION: the offset is a 64-bit type whereas fseek() is generally 'long' + * and 32-bits wide. + * + * \param handle The handle to the data source. + * \param offset The new position, relative to \a whence + * \param whence \c SEEK_SET, \c SEEK_CUR, or \c SEEK_END + * \retval int + * \c 0 on success, \c -1 on error. + */ +typedef int (*FLAC__IOCallback_Seek) (FLAC__IOHandle handle, FLAC__int64 offset, int whence); + +/** Signature for the tell callback. + * The signature and semantics mostly match POSIX ftell() WITH ONE IMPORTANT + * EXCEPTION: the offset is a 64-bit type whereas ftell() is generally 'long' + * and 32-bits wide. + * + * \param handle The handle to the data source. + * \retval FLAC__int64 + * The current position on success, \c -1 on error. + */ +typedef FLAC__int64 (*FLAC__IOCallback_Tell) (FLAC__IOHandle handle); + +/** Signature for the EOF callback. + * The signature and semantics mostly match POSIX feof() but WATCHOUT: + * on many systems, feof() is a macro, so in this case a wrapper function + * must be provided instead. + * + * \param handle The handle to the data source. + * \retval int + * \c 0 if not at end of file, nonzero if at end of file. + */ +typedef int (*FLAC__IOCallback_Eof) (FLAC__IOHandle handle); + +/** Signature for the close callback. + * The signature and semantics match POSIX fclose() implementations + * and can generally be used interchangeably. + * + * \param handle The handle to the data source. + * \retval int + * \c 0 on success, \c EOF on error. + */ +typedef int (*FLAC__IOCallback_Close) (FLAC__IOHandle handle); + +/** A structure for holding a set of callbacks. + * Each FLAC interface that requires a FLAC__IOCallbacks structure will + * describe which of the callbacks are required. The ones that are not + * required may be set to NULL. + * + * If the seek requirement for an interface is optional, you can signify that + * a data sorce is not seekable by setting the \a seek field to \c NULL. + */ +typedef struct { + FLAC__IOCallback_Read read; + FLAC__IOCallback_Write write; + FLAC__IOCallback_Seek seek; + FLAC__IOCallback_Tell tell; + FLAC__IOCallback_Eof eof; + FLAC__IOCallback_Close close; +} FLAC__IOCallbacks; + +/* \} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/FLAC/include/FLAC/export.h b/src/FLAC/include/FLAC/export.h new file mode 100644 index 00000000..98d2d288 --- /dev/null +++ b/src/FLAC/include/FLAC/export.h @@ -0,0 +1,87 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__EXPORT_H +#define FLAC__EXPORT_H + +/** \file include/FLAC/export.h + * + * \brief + * This module contains #defines and symbols for exporting function + * calls, and providing version information and compiled-in features. + * + * See the \link flac_export export \endlink module. + */ + +/** \defgroup flac_export FLAC/export.h: export symbols + * \ingroup flac + * + * \brief + * This module contains #defines and symbols for exporting function + * calls, and providing version information and compiled-in features. + * + * \{ + */ + +#if defined(FLAC__NO_DLL) || !defined(_MSC_VER) +#define FLAC_API + +#else + +#ifdef FLAC_API_EXPORTS +#define FLAC_API _declspec(dllexport) +#else +#define FLAC_API _declspec(dllimport) + +#endif +#endif + +/** These #defines will mirror the libtool-based library version number, see + * http://www.gnu.org/software/libtool/manual.html#Libtool-versioning + */ +#define FLAC_API_VERSION_CURRENT 8 +#define FLAC_API_VERSION_REVISION 1 /**< see above */ +#define FLAC_API_VERSION_AGE 0 /**< see above */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** \c 1 if the library has been compiled with support for Ogg FLAC, else \c 0. */ +extern FLAC_API int FLAC_API_SUPPORTS_OGG_FLAC; + +#ifdef __cplusplus +} +#endif + +/* \} */ + +#endif diff --git a/src/FLAC/include/FLAC/format.h b/src/FLAC/include/FLAC/format.h new file mode 100644 index 00000000..9e744cdd --- /dev/null +++ b/src/FLAC/include/FLAC/format.h @@ -0,0 +1,995 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__FORMAT_H +#define FLAC__FORMAT_H + +#include "export.h" +#include "ordinals.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file include/FLAC/format.h + * + * \brief + * This module contains structure definitions for the representation + * of FLAC format components in memory. These are the basic + * structures used by the rest of the interfaces. + * + * See the detailed documentation in the + * \link flac_format format \endlink module. + */ + +/** \defgroup flac_format FLAC/format.h: format components + * \ingroup flac + * + * \brief + * This module contains structure definitions for the representation + * of FLAC format components in memory. These are the basic + * structures used by the rest of the interfaces. + * + * First, you should be familiar with the + * FLAC format. Many of the values here + * follow directly from the specification. As a user of libFLAC, the + * interesting parts really are the structures that describe the frame + * header and metadata blocks. + * + * The format structures here are very primitive, designed to store + * information in an efficient way. Reading information from the + * structures is easy but creating or modifying them directly is + * more complex. For the most part, as a user of a library, editing + * is not necessary; however, for metadata blocks it is, so there are + * convenience functions provided in the \link flac_metadata metadata + * module \endlink to simplify the manipulation of metadata blocks. + * + * \note + * It's not the best convention, but symbols ending in _LEN are in bits + * and _LENGTH are in bytes. _LENGTH symbols are \#defines instead of + * global variables because they are usually used when declaring byte + * arrays and some compilers require compile-time knowledge of array + * sizes when declared on the stack. + * + * \{ + */ + + +/* + Most of the values described in this file are defined by the FLAC + format specification. There is nothing to tune here. +*/ + +/** The largest legal metadata type code. */ +#define FLAC__MAX_METADATA_TYPE_CODE (126u) + +/** The minimum block size, in samples, permitted by the format. */ +#define FLAC__MIN_BLOCK_SIZE (16u) + +/** The maximum block size, in samples, permitted by the format. */ +#define FLAC__MAX_BLOCK_SIZE (65535u) + +/** The maximum block size, in samples, permitted by the FLAC subset for + * sample rates up to 48kHz. */ +#define FLAC__SUBSET_MAX_BLOCK_SIZE_48000HZ (4608u) + +/** The maximum number of channels permitted by the format. */ +#define FLAC__MAX_CHANNELS (8u) + +/** The minimum sample resolution permitted by the format. */ +#define FLAC__MIN_BITS_PER_SAMPLE (4u) + +/** The maximum sample resolution permitted by the format. */ +#define FLAC__MAX_BITS_PER_SAMPLE (32u) + +/** The maximum sample resolution permitted by libFLAC. + * + * \warning + * FLAC__MAX_BITS_PER_SAMPLE is the limit of the FLAC format. However, + * the reference encoder/decoder is currently limited to 24 bits because + * of prevalent 32-bit math, so make sure and use this value when + * appropriate. + */ +#define FLAC__REFERENCE_CODEC_MAX_BITS_PER_SAMPLE (24u) + +/** The maximum sample rate permitted by the format. The value is + * ((2 ^ 16) - 1) * 10; see FLAC format + * as to why. + */ +#define FLAC__MAX_SAMPLE_RATE (655350u) + +/** The maximum LPC order permitted by the format. */ +#define FLAC__MAX_LPC_ORDER (32u) + +/** The maximum LPC order permitted by the FLAC subset for sample rates + * up to 48kHz. */ +#define FLAC__SUBSET_MAX_LPC_ORDER_48000HZ (12u) + +/** The minimum quantized linear predictor coefficient precision + * permitted by the format. + */ +#define FLAC__MIN_QLP_COEFF_PRECISION (5u) + +/** The maximum quantized linear predictor coefficient precision + * permitted by the format. + */ +#define FLAC__MAX_QLP_COEFF_PRECISION (15u) + +/** The maximum order of the fixed predictors permitted by the format. */ +#define FLAC__MAX_FIXED_ORDER (4u) + +/** The maximum Rice partition order permitted by the format. */ +#define FLAC__MAX_RICE_PARTITION_ORDER (15u) + +/** The maximum Rice partition order permitted by the FLAC Subset. */ +#define FLAC__SUBSET_MAX_RICE_PARTITION_ORDER (8u) + +/** The version string of the release, stamped onto the libraries and binaries. + * + * \note + * This does not correspond to the shared library version number, which + * is used to determine binary compatibility. + */ +extern FLAC_API const char *FLAC__VERSION_STRING; + +/** The vendor string inserted by the encoder into the VORBIS_COMMENT block. + * This is a NUL-terminated ASCII string; when inserted into the + * VORBIS_COMMENT the trailing null is stripped. + */ +extern FLAC_API const char *FLAC__VENDOR_STRING; + +/** The byte string representation of the beginning of a FLAC stream. */ +extern FLAC_API const FLAC__byte FLAC__STREAM_SYNC_STRING[4]; /* = "fLaC" */ + +/** The 32-bit integer big-endian representation of the beginning of + * a FLAC stream. + */ +extern FLAC_API const unsigned FLAC__STREAM_SYNC; /* = 0x664C6143 */ + +/** The length of the FLAC signature in bits. */ +extern FLAC_API const unsigned FLAC__STREAM_SYNC_LEN; /* = 32 bits */ + +/** The length of the FLAC signature in bytes. */ +#define FLAC__STREAM_SYNC_LENGTH (4u) + + +/***************************************************************************** + * + * Subframe structures + * + *****************************************************************************/ + +/*****************************************************************************/ + +/** An enumeration of the available entropy coding methods. */ +typedef enum { + FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE = 0 + /**< Residual is coded by partitioning into contexts, each with it's own + * Rice parameter. */ +} FLAC__EntropyCodingMethodType; + +/** Maps a FLAC__EntropyCodingMethodType to a C string. + * + * Using a FLAC__EntropyCodingMethodType as the index to this array will + * give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__EntropyCodingMethodTypeString[]; + + +/** Contents of a Rice partitioned residual + */ +typedef struct { + + unsigned *parameters; + /**< The Rice parameters for each context. */ + + unsigned *raw_bits; + /**< Widths for escape-coded partitions. */ + + unsigned capacity_by_order; + /**< The capacity of the \a parameters and \a raw_bits arrays + * specified as an order, i.e. the number of array elements + * allocated is 2 ^ \a capacity_by_order. + */ +} FLAC__EntropyCodingMethod_PartitionedRiceContents; + +/** Header for a Rice partitioned residual. (c.f. format specification) + */ +typedef struct { + + unsigned order; + /**< The partition order, i.e. # of contexts = 2 ^ \a order. */ + + const FLAC__EntropyCodingMethod_PartitionedRiceContents *contents; + /**< The context's Rice parameters and/or raw bits. */ + +} FLAC__EntropyCodingMethod_PartitionedRice; + +extern FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN; /**< == 4 (bits) */ +extern FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN; /**< == 4 (bits) */ +extern FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_RAW_LEN; /**< == 5 (bits) */ + +extern FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER; +/**< == (1<format specification) + */ +typedef struct { + FLAC__EntropyCodingMethodType type; + union { + FLAC__EntropyCodingMethod_PartitionedRice partitioned_rice; + } data; +} FLAC__EntropyCodingMethod; + +extern FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_TYPE_LEN; /**< == 2 (bits) */ + +/*****************************************************************************/ + +/** An enumeration of the available subframe types. */ +typedef enum { + FLAC__SUBFRAME_TYPE_CONSTANT = 0, /**< constant signal */ + FLAC__SUBFRAME_TYPE_VERBATIM = 1, /**< uncompressed signal */ + FLAC__SUBFRAME_TYPE_FIXED = 2, /**< fixed polynomial prediction */ + FLAC__SUBFRAME_TYPE_LPC = 3 /**< linear prediction */ +} FLAC__SubframeType; + +/** Maps a FLAC__SubframeType to a C string. + * + * Using a FLAC__SubframeType as the index to this array will + * give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__SubframeTypeString[]; + + +/** CONSTANT subframe. (c.f. format specification) + */ +typedef struct { + FLAC__int32 value; /**< The constant signal value. */ +} FLAC__Subframe_Constant; + + +/** VERBATIM subframe. (c.f. format specification) + */ +typedef struct { + const FLAC__int32 *data; /**< A pointer to verbatim signal. */ +} FLAC__Subframe_Verbatim; + + +/** FIXED subframe. (c.f. format specification) + */ +typedef struct { + FLAC__EntropyCodingMethod entropy_coding_method; + /**< The residual coding method. */ + + unsigned order; + /**< The polynomial order. */ + + FLAC__int32 warmup[FLAC__MAX_FIXED_ORDER]; + /**< Warmup samples to prime the predictor, length == order. */ + + const FLAC__int32 *residual; + /**< The residual signal, length == (blocksize minus order) samples. */ +} FLAC__Subframe_Fixed; + + +/** LPC subframe. (c.f. format specification) + */ +typedef struct { + FLAC__EntropyCodingMethod entropy_coding_method; + /**< The residual coding method. */ + + unsigned order; + /**< The FIR order. */ + + unsigned qlp_coeff_precision; + /**< Quantized FIR filter coefficient precision in bits. */ + + int quantization_level; + /**< The qlp coeff shift needed. */ + + FLAC__int32 qlp_coeff[FLAC__MAX_LPC_ORDER]; + /**< FIR filter coefficients. */ + + FLAC__int32 warmup[FLAC__MAX_LPC_ORDER]; + /**< Warmup samples to prime the predictor, length == order. */ + + const FLAC__int32 *residual; + /**< The residual signal, length == (blocksize minus order) samples. */ +} FLAC__Subframe_LPC; + +extern FLAC_API const unsigned FLAC__SUBFRAME_LPC_QLP_COEFF_PRECISION_LEN; /**< == 4 (bits) */ +extern FLAC_API const unsigned FLAC__SUBFRAME_LPC_QLP_SHIFT_LEN; /**< == 5 (bits) */ + + +/** FLAC subframe structure. (c.f. format specification) + */ +typedef struct { + FLAC__SubframeType type; + union { + FLAC__Subframe_Constant constant; + FLAC__Subframe_Fixed fixed; + FLAC__Subframe_LPC lpc; + FLAC__Subframe_Verbatim verbatim; + } data; + unsigned wasted_bits; +} FLAC__Subframe; + +extern FLAC_API const unsigned FLAC__SUBFRAME_ZERO_PAD_LEN; /**< == 1 (bit) */ +extern FLAC_API const unsigned FLAC__SUBFRAME_TYPE_LEN; /**< == 6 (bits) */ +extern FLAC_API const unsigned FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN; /**< == 1 (bit) */ + +extern FLAC_API const unsigned FLAC__SUBFRAME_TYPE_CONSTANT_BYTE_ALIGNED_MASK; /**< = 0x00 */ +extern FLAC_API const unsigned FLAC__SUBFRAME_TYPE_VERBATIM_BYTE_ALIGNED_MASK; /**< = 0x02 */ +extern FLAC_API const unsigned FLAC__SUBFRAME_TYPE_FIXED_BYTE_ALIGNED_MASK; /**< = 0x10 */ +extern FLAC_API const unsigned FLAC__SUBFRAME_TYPE_LPC_BYTE_ALIGNED_MASK; /**< = 0x40 */ + +/*****************************************************************************/ + + +/***************************************************************************** + * + * Frame structures + * + *****************************************************************************/ + +/** An enumeration of the available channel assignments. */ +typedef enum { + FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT = 0, /**< independent channels */ + FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE = 1, /**< left+side stereo */ + FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE = 2, /**< right+side stereo */ + FLAC__CHANNEL_ASSIGNMENT_MID_SIDE = 3 /**< mid+side stereo */ +} FLAC__ChannelAssignment; + +/** Maps a FLAC__ChannelAssignment to a C string. + * + * Using a FLAC__ChannelAssignment as the index to this array will + * give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__ChannelAssignmentString[]; + +/** An enumeration of the possible frame numbering methods. */ +typedef enum { + FLAC__FRAME_NUMBER_TYPE_FRAME_NUMBER, /**< number contains the frame number */ + FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER /**< number contains the sample number of first sample in frame */ +} FLAC__FrameNumberType; + +/** Maps a FLAC__FrameNumberType to a C string. + * + * Using a FLAC__FrameNumberType as the index to this array will + * give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__FrameNumberTypeString[]; + + +/** FLAC frame header structure. (c.f. format specification) + */ +typedef struct { + unsigned blocksize; + /**< The number of samples per subframe. */ + + unsigned sample_rate; + /**< The sample rate in Hz. */ + + unsigned channels; + /**< The number of channels (== number of subframes). */ + + FLAC__ChannelAssignment channel_assignment; + /**< The channel assignment for the frame. */ + + unsigned bits_per_sample; + /**< The sample resolution. */ + + FLAC__FrameNumberType number_type; + /**< The numbering scheme used for the frame. */ + + union { + FLAC__uint32 frame_number; + FLAC__uint64 sample_number; + } number; + /**< The frame number or sample number of first sample in frame; + * use the \a number_type value to determine which to use. */ + + FLAC__uint8 crc; + /**< CRC-8 (polynomial = x^8 + x^2 + x^1 + x^0, initialized with 0) + * of the raw frame header bytes, meaning everything before the CRC byte + * including the sync code. + */ +} FLAC__FrameHeader; + +extern FLAC_API const unsigned FLAC__FRAME_HEADER_SYNC; /**< == 0x3ffe; the frame header sync code */ +extern FLAC_API const unsigned FLAC__FRAME_HEADER_SYNC_LEN; /**< == 14 (bits) */ +extern FLAC_API const unsigned FLAC__FRAME_HEADER_RESERVED_LEN; /**< == 2 (bits) */ +extern FLAC_API const unsigned FLAC__FRAME_HEADER_BLOCK_SIZE_LEN; /**< == 4 (bits) */ +extern FLAC_API const unsigned FLAC__FRAME_HEADER_SAMPLE_RATE_LEN; /**< == 4 (bits) */ +extern FLAC_API const unsigned FLAC__FRAME_HEADER_CHANNEL_ASSIGNMENT_LEN; /**< == 4 (bits) */ +extern FLAC_API const unsigned FLAC__FRAME_HEADER_BITS_PER_SAMPLE_LEN; /**< == 3 (bits) */ +extern FLAC_API const unsigned FLAC__FRAME_HEADER_ZERO_PAD_LEN; /**< == 1 (bit) */ +extern FLAC_API const unsigned FLAC__FRAME_HEADER_CRC_LEN; /**< == 8 (bits) */ + + +/** FLAC frame footer structure. (c.f. format specification) + */ +typedef struct { + FLAC__uint16 crc; + /**< CRC-16 (polynomial = x^16 + x^15 + x^2 + x^0, initialized with + * 0) of the bytes before the crc, back to and including the frame header + * sync code. + */ +} FLAC__FrameFooter; + +extern FLAC_API const unsigned FLAC__FRAME_FOOTER_CRC_LEN; /**< == 16 (bits) */ + + +/** FLAC frame structure. (c.f. format specification) + */ +typedef struct { + FLAC__FrameHeader header; + FLAC__Subframe subframes[FLAC__MAX_CHANNELS]; + FLAC__FrameFooter footer; +} FLAC__Frame; + +/*****************************************************************************/ + + +/***************************************************************************** + * + * Meta-data structures + * + *****************************************************************************/ + +/** An enumeration of the available metadata block types. */ +typedef enum { + + FLAC__METADATA_TYPE_STREAMINFO = 0, + /**< STREAMINFO block */ + + FLAC__METADATA_TYPE_PADDING = 1, + /**< PADDING block */ + + FLAC__METADATA_TYPE_APPLICATION = 2, + /**< APPLICATION block */ + + FLAC__METADATA_TYPE_SEEKTABLE = 3, + /**< SEEKTABLE block */ + + FLAC__METADATA_TYPE_VORBIS_COMMENT = 4, + /**< VORBISCOMMENT block (a.k.a. FLAC tags) */ + + FLAC__METADATA_TYPE_CUESHEET = 5, + /**< CUESHEET block */ + + FLAC__METADATA_TYPE_PICTURE = 6, + /**< PICTURE block */ + + FLAC__METADATA_TYPE_UNDEFINED = 7 + /**< marker to denote beginning of undefined type range; this number will increase as new metadata types are added */ + +} FLAC__MetadataType; + +/** Maps a FLAC__MetadataType to a C string. + * + * Using a FLAC__MetadataType as the index to this array will + * give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__MetadataTypeString[]; + + +/** FLAC STREAMINFO structure. (c.f. format specification) + */ +typedef struct { + unsigned min_blocksize, max_blocksize; + unsigned min_framesize, max_framesize; + unsigned sample_rate; + unsigned channels; + unsigned bits_per_sample; + FLAC__uint64 total_samples; + FLAC__byte md5sum[16]; +} FLAC__StreamMetadata_StreamInfo; + +extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN; /**< == 16 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN; /**< == 16 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN; /**< == 24 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN; /**< == 24 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN; /**< == 20 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN; /**< == 3 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN; /**< == 5 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN; /**< == 36 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MD5SUM_LEN; /**< == 128 (bits) */ + +/** The total stream length of the STREAMINFO block in bytes. */ +#define FLAC__STREAM_METADATA_STREAMINFO_LENGTH (34u) + +/** FLAC PADDING structure. (c.f. format specification) + */ +typedef struct { + int dummy; + /**< Conceptually this is an empty struct since we don't store the + * padding bytes. Empty structs are not allowed by some C compilers, + * hence the dummy. + */ +} FLAC__StreamMetadata_Padding; + + +/** FLAC APPLICATION structure. (c.f. format specification) + */ +typedef struct { + FLAC__byte id[4]; + FLAC__byte *data; +} FLAC__StreamMetadata_Application; + +extern FLAC_API const unsigned FLAC__STREAM_METADATA_APPLICATION_ID_LEN; /**< == 32 (bits) */ + +/** SeekPoint structure used in SEEKTABLE blocks. (c.f. format specification) + */ +typedef struct { + FLAC__uint64 sample_number; + /**< The sample number of the target frame. */ + + FLAC__uint64 stream_offset; + /**< The offset, in bytes, of the target frame with respect to + * beginning of the first frame. */ + + unsigned frame_samples; + /**< The number of samples in the target frame. */ +} FLAC__StreamMetadata_SeekPoint; + +extern FLAC_API const unsigned FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN; /**< == 64 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN; /**< == 64 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN; /**< == 16 (bits) */ + +/** The total stream length of a seek point in bytes. */ +#define FLAC__STREAM_METADATA_SEEKPOINT_LENGTH (18u) + +/** The value used in the \a sample_number field of + * FLAC__StreamMetadataSeekPoint used to indicate a placeholder + * point (== 0xffffffffffffffff). + */ +extern FLAC_API const FLAC__uint64 FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER; + + +/** FLAC SEEKTABLE structure. (c.f. format specification) + * + * \note From the format specification: + * - The seek points must be sorted by ascending sample number. + * - Each seek point's sample number must be the first sample of the + * target frame. + * - Each seek point's sample number must be unique within the table. + * - Existence of a SEEKTABLE block implies a correct setting of + * total_samples in the stream_info block. + * - Behavior is undefined when more than one SEEKTABLE block is + * present in a stream. + */ +typedef struct { + unsigned num_points; + FLAC__StreamMetadata_SeekPoint *points; +} FLAC__StreamMetadata_SeekTable; + + +/** Vorbis comment entry structure used in VORBIS_COMMENT blocks. (c.f. format specification) + * + * For convenience, the APIs maintain a trailing NUL character at the end of + * \a entry which is not counted toward \a length, i.e. + * \code strlen(entry) == length \endcode + */ +typedef struct { + FLAC__uint32 length; + FLAC__byte *entry; +} FLAC__StreamMetadata_VorbisComment_Entry; + +extern FLAC_API const unsigned FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN; /**< == 32 (bits) */ + + +/** FLAC VORBIS_COMMENT structure. (c.f. format specification) + */ +typedef struct { + FLAC__StreamMetadata_VorbisComment_Entry vendor_string; + FLAC__uint32 num_comments; + FLAC__StreamMetadata_VorbisComment_Entry *comments; +} FLAC__StreamMetadata_VorbisComment; + +extern FLAC_API const unsigned FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN; /**< == 32 (bits) */ + + +/** FLAC CUESHEET track index structure. (See the + * format specification for + * the full description of each field.) + */ +typedef struct { + FLAC__uint64 offset; + /**< Offset in samples, relative to the track offset, of the index + * point. + */ + + FLAC__byte number; + /**< The index point number. */ +} FLAC__StreamMetadata_CueSheet_Index; + +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN; /**< == 64 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN; /**< == 8 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN; /**< == 3*8 (bits) */ + + +/** FLAC CUESHEET track structure. (See the + * format specification for + * the full description of each field.) + */ +typedef struct { + FLAC__uint64 offset; + /**< Track offset in samples, relative to the beginning of the FLAC audio stream. */ + + FLAC__byte number; + /**< The track number. */ + + char isrc[13]; + /**< Track ISRC. This is a 12-digit alphanumeric code plus a trailing \c NUL byte */ + + unsigned type:1; + /**< The track type: 0 for audio, 1 for non-audio. */ + + unsigned pre_emphasis:1; + /**< The pre-emphasis flag: 0 for no pre-emphasis, 1 for pre-emphasis. */ + + FLAC__byte num_indices; + /**< The number of track index points. */ + + FLAC__StreamMetadata_CueSheet_Index *indices; + /**< NULL if num_indices == 0, else pointer to array of index points. */ + +} FLAC__StreamMetadata_CueSheet_Track; + +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN; /**< == 64 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN; /**< == 8 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN; /**< == 12*8 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN; /**< == 1 (bit) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN; /**< == 1 (bit) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN; /**< == 6+13*8 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN; /**< == 8 (bits) */ + + +/** FLAC CUESHEET structure. (See the + * format specification + * for the full description of each field.) + */ +typedef struct { + char media_catalog_number[129]; + /**< Media catalog number, in ASCII printable characters 0x20-0x7e. In + * general, the media catalog number may be 0 to 128 bytes long; any + * unused characters should be right-padded with NUL characters. + */ + + FLAC__uint64 lead_in; + /**< The number of lead-in samples. */ + + FLAC__bool is_cd; + /**< \c true if CUESHEET corresponds to a Compact Disc, else \c false. */ + + unsigned num_tracks; + /**< The number of tracks. */ + + FLAC__StreamMetadata_CueSheet_Track *tracks; + /**< NULL if num_tracks == 0, else pointer to array of tracks. */ + +} FLAC__StreamMetadata_CueSheet; + +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN; /**< == 128*8 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN; /**< == 64 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN; /**< == 1 (bit) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN; /**< == 7+258*8 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN; /**< == 8 (bits) */ + + +/** An enumeration of the PICTURE types (see FLAC__StreamMetadataPicture and id3 v2.4 APIC tag). */ +typedef enum { + FLAC__STREAM_METADATA_PICTURE_TYPE_OTHER = 0, /**< Other */ + FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON_STANDARD = 1, /**< 32x32 pixels 'file icon' (PNG only) */ + FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON = 2, /**< Other file icon */ + FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER = 3, /**< Cover (front) */ + FLAC__STREAM_METADATA_PICTURE_TYPE_BACK_COVER = 4, /**< Cover (back) */ + FLAC__STREAM_METADATA_PICTURE_TYPE_LEAFLET_PAGE = 5, /**< Leaflet page */ + FLAC__STREAM_METADATA_PICTURE_TYPE_MEDIA = 6, /**< Media (e.g. label side of CD) */ + FLAC__STREAM_METADATA_PICTURE_TYPE_LEAD_ARTIST = 7, /**< Lead artist/lead performer/soloist */ + FLAC__STREAM_METADATA_PICTURE_TYPE_ARTIST = 8, /**< Artist/performer */ + FLAC__STREAM_METADATA_PICTURE_TYPE_CONDUCTOR = 9, /**< Conductor */ + FLAC__STREAM_METADATA_PICTURE_TYPE_BAND = 10, /**< Band/Orchestra */ + FLAC__STREAM_METADATA_PICTURE_TYPE_COMPOSER = 11, /**< Composer */ + FLAC__STREAM_METADATA_PICTURE_TYPE_LYRICIST = 12, /**< Lyricist/text writer */ + FLAC__STREAM_METADATA_PICTURE_TYPE_RECORDING_LOCATION = 13, /**< Recording Location */ + FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_RECORDING = 14, /**< During recording */ + FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_PERFORMANCE = 15, /**< During performance */ + FLAC__STREAM_METADATA_PICTURE_TYPE_VIDEO_SCREEN_CAPTURE = 16, /**< Movie/video screen capture */ + FLAC__STREAM_METADATA_PICTURE_TYPE_FISH = 17, /**< A bright coloured fish */ + FLAC__STREAM_METADATA_PICTURE_TYPE_ILLUSTRATION = 18, /**< Illustration */ + FLAC__STREAM_METADATA_PICTURE_TYPE_BAND_LOGOTYPE = 19, /**< Band/artist logotype */ + FLAC__STREAM_METADATA_PICTURE_TYPE_PUBLISHER_LOGOTYPE = 20, /**< Publisher/Studio logotype */ + FLAC__STREAM_METADATA_PICTURE_TYPE_UNDEFINED +} FLAC__StreamMetadata_Picture_Type; + +/** Maps a FLAC__StreamMetadata_Picture_Type to a C string. + * + * Using a FLAC__StreamMetadata_Picture_Type as the index to this array + * will give the string equivalent. The contents should not be + * modified. + */ +extern FLAC_API const char * const FLAC__StreamMetadata_Picture_TypeString[]; + +/** FLAC PICTURE structure. (See the + * format specification + * for the full description of each field.) + */ +typedef struct { + FLAC__StreamMetadata_Picture_Type type; + /**< The kind of picture stored. */ + + char *mime_type; + /**< Picture data's MIME type, in ASCII printable characters + * 0x20-0x7e, NUL terminated. For best compatibility with players, + * use picture data of MIME type \c image/jpeg or \c image/png. A + * MIME type of '-->' is also allowed, in which case the picture + * data should be a complete URL. In file storage, the MIME type is + * stored as a 32-bit length followed by the ASCII string with no NUL + * terminator, but is converted to a plain C string in this structure + * for convenience. + */ + + FLAC__byte *description; + /**< Picture's description in UTF-8, NUL terminated. In file storage, + * the description is stored as a 32-bit length followed by the UTF-8 + * string with no NUL terminator, but is converted to a plain C string + * in this structure for convenience. + */ + + FLAC__uint32 width; + /**< Picture's width in pixels. */ + + FLAC__uint32 height; + /**< Picture's height in pixels. */ + + FLAC__uint32 depth; + /**< Picture's color depth in bits-per-pixel. */ + + FLAC__uint32 colors; + /**< For indexed palettes (like GIF), picture's number of colors (the + * number of palette entries), or \c 0 for non-indexed (i.e. 2^depth). + */ + + FLAC__uint32 data_length; + /**< Length of binary picture data in bytes. */ + + FLAC__byte *data; + /**< Binary picture data. */ + +} FLAC__StreamMetadata_Picture; + +extern FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_TYPE_LEN; /**< == 32 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN; /**< == 32 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN; /**< == 32 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN; /**< == 32 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN; /**< == 32 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN; /**< == 32 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_COLORS_LEN; /**< == 32 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN; /**< == 32 (bits) */ + + +/** Structure that is used when a metadata block of unknown type is loaded. + * The contents are opaque. The structure is used only internally to + * correctly handle unknown metadata. + */ +typedef struct { + FLAC__byte *data; +} FLAC__StreamMetadata_Unknown; + + +/** FLAC metadata block structure. (c.f. format specification) + */ +typedef struct { + FLAC__MetadataType type; + /**< The type of the metadata block; used determine which member of the + * \a data union to dereference. If type >= FLAC__METADATA_TYPE_UNDEFINED + * then \a data.unknown must be used. */ + + FLAC__bool is_last; + /**< \c true if this metadata block is the last, else \a false */ + + unsigned length; + /**< Length, in bytes, of the block data as it appears in the stream. */ + + union { + FLAC__StreamMetadata_StreamInfo stream_info; + FLAC__StreamMetadata_Padding padding; + FLAC__StreamMetadata_Application application; + FLAC__StreamMetadata_SeekTable seek_table; + FLAC__StreamMetadata_VorbisComment vorbis_comment; + FLAC__StreamMetadata_CueSheet cue_sheet; + FLAC__StreamMetadata_Picture picture; + FLAC__StreamMetadata_Unknown unknown; + } data; + /**< Polymorphic block data; use the \a type value to determine which + * to use. */ +} FLAC__StreamMetadata; + +extern FLAC_API const unsigned FLAC__STREAM_METADATA_IS_LAST_LEN; /**< == 1 (bit) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_TYPE_LEN; /**< == 7 (bits) */ +extern FLAC_API const unsigned FLAC__STREAM_METADATA_LENGTH_LEN; /**< == 24 (bits) */ + +/** The total stream length of a metadata block header in bytes. */ +#define FLAC__STREAM_METADATA_HEADER_LENGTH (4u) + +/*****************************************************************************/ + + +/***************************************************************************** + * + * Utility functions + * + *****************************************************************************/ + +/** Tests that a sample rate is valid for FLAC. + * + * \param sample_rate The sample rate to test for compliance. + * \retval FLAC__bool + * \c true if the given sample rate conforms to the specification, else + * \c false. + */ +FLAC_API FLAC__bool FLAC__format_sample_rate_is_valid(unsigned sample_rate); + +/** Tests that a sample rate is valid for the FLAC subset. The subset rules + * for valid sample rates are slightly more complex since the rate has to + * be expressible completely in the frame header. + * + * \param sample_rate The sample rate to test for compliance. + * \retval FLAC__bool + * \c true if the given sample rate conforms to the specification for the + * subset, else \c false. + */ +FLAC_API FLAC__bool FLAC__format_sample_rate_is_subset(unsigned sample_rate); + +/** Check a Vorbis comment entry name to see if it conforms to the Vorbis + * comment specification. + * + * Vorbis comment names must be composed only of characters from + * [0x20-0x3C,0x3E-0x7D]. + * + * \param name A NUL-terminated string to be checked. + * \assert + * \code name != NULL \endcode + * \retval FLAC__bool + * \c false if entry name is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_name_is_legal(const char *name); + +/** Check a Vorbis comment entry value to see if it conforms to the Vorbis + * comment specification. + * + * Vorbis comment values must be valid UTF-8 sequences. + * + * \param value A string to be checked. + * \param length A the length of \a value in bytes. May be + * \c (unsigned)(-1) to indicate that \a value is a plain + * UTF-8 NUL-terminated string. + * \assert + * \code value != NULL \endcode + * \retval FLAC__bool + * \c false if entry name is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_value_is_legal(const FLAC__byte *value, unsigned length); + +/** Check a Vorbis comment entry to see if it conforms to the Vorbis + * comment specification. + * + * Vorbis comment entries must be of the form 'name=value', and 'name' and + * 'value' must be legal according to + * FLAC__format_vorbiscomment_entry_name_is_legal() and + * FLAC__format_vorbiscomment_entry_value_is_legal() respectively. + * + * \param entry An entry to be checked. + * \param length The length of \a entry in bytes. + * \assert + * \code value != NULL \endcode + * \retval FLAC__bool + * \c false if entry name is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_is_legal(const FLAC__byte *entry, unsigned length); + +/* @@@@ add to unit tests; it is already indirectly tested by the metadata_object tests */ +/** Check a seek table to see if it conforms to the FLAC specification. + * See the format specification for limits on the contents of the + * seek table. + * + * \param seek_table A pointer to a seek table to be checked. + * \assert + * \code seek_table != NULL \endcode + * \retval FLAC__bool + * \c false if seek table is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__format_seektable_is_legal(const FLAC__StreamMetadata_SeekTable *seek_table); + +/* @@@@ add to unit tests; it is already indirectly tested by the metadata_object tests */ +/** Sort a seek table's seek points according to the format specification. + * This includes a "unique-ification" step to remove duplicates, i.e. + * seek points with identical \a sample_number values. Duplicate seek + * points are converted into placeholder points and sorted to the end of + * the table. + * + * \param seek_table A pointer to a seek table to be sorted. + * \assert + * \code seek_table != NULL \endcode + * \retval unsigned + * The number of duplicate seek points converted into placeholders. + */ +FLAC_API unsigned FLAC__format_seektable_sort(FLAC__StreamMetadata_SeekTable *seek_table); + +/* @@@@ add to unit tests; it is already indirectly tested by the metadata_object tests */ +/** Check a cue sheet to see if it conforms to the FLAC specification. + * See the format specification for limits on the contents of the + * cue sheet. + * + * \param cue_sheet A pointer to an existing cue sheet to be checked. + * \param check_cd_da_subset If \c true, check CUESHEET against more + * stringent requirements for a CD-DA (audio) disc. + * \param violation Address of a pointer to a string. If there is a + * violation, a pointer to a string explanation of the + * violation will be returned here. \a violation may be + * \c NULL if you don't need the returned string. Do not + * free the returned string; it will always point to static + * data. + * \assert + * \code cue_sheet != NULL \endcode + * \retval FLAC__bool + * \c false if cue sheet is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__format_cuesheet_is_legal(const FLAC__StreamMetadata_CueSheet *cue_sheet, FLAC__bool check_cd_da_subset, const char **violation); + +/* @@@@ add to unit tests; it is already indirectly tested by the metadata_object tests */ +/** Check picture data to see if it conforms to the FLAC specification. + * See the format specification for limits on the contents of the + * PICTURE block. + * + * \param picture A pointer to existing picture data to be checked. + * \param violation Address of a pointer to a string. If there is a + * violation, a pointer to a string explanation of the + * violation will be returned here. \a violation may be + * \c NULL if you don't need the returned string. Do not + * free the returned string; it will always point to static + * data. + * \assert + * \code picture != NULL \endcode + * \retval FLAC__bool + * \c false if picture data is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__format_picture_is_legal(const FLAC__StreamMetadata_Picture *picture, const char **violation); + +/* \} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/FLAC/include/FLAC/metadata.h b/src/FLAC/include/FLAC/metadata.h new file mode 100644 index 00000000..f9796aea --- /dev/null +++ b/src/FLAC/include/FLAC/metadata.h @@ -0,0 +1,2112 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__METADATA_H +#define FLAC__METADATA_H + +#include "export.h" +#include "callback.h" +#include "format.h" + +/* -------------------------------------------------------------------- + (For an example of how all these routines are used, see the source + code for the unit tests in src/test_libFLAC/metadata_*.c, or + metaflac in src/metaflac/) + ------------------------------------------------------------------*/ + +/** \file include/FLAC/metadata.h + * + * \brief + * This module provides functions for creating and manipulating FLAC + * metadata blocks in memory, and three progressively more powerful + * interfaces for traversing and editing metadata in FLAC files. + * + * See the detailed documentation for each interface in the + * \link flac_metadata metadata \endlink module. + */ + +/** \defgroup flac_metadata FLAC/metadata.h: metadata interfaces + * \ingroup flac + * + * \brief + * This module provides functions for creating and manipulating FLAC + * metadata blocks in memory, and three progressively more powerful + * interfaces for traversing and editing metadata in native FLAC files. + * Note that currently only the Chain interface (level 2) supports Ogg + * FLAC files, and it is read-only i.e. no writing back changed + * metadata to file. + * + * There are three metadata interfaces of increasing complexity: + * + * Level 0: + * Read-only access to the STREAMINFO, VORBIS_COMMENT, CUESHEET, and + * PICTURE blocks. + * + * Level 1: + * Read-write access to all metadata blocks. This level is write- + * efficient in most cases (more on this below), and uses less memory + * than level 2. + * + * Level 2: + * Read-write access to all metadata blocks. This level is write- + * efficient in all cases, but uses more memory since all metadata for + * the whole file is read into memory and manipulated before writing + * out again. + * + * What do we mean by efficient? Since FLAC metadata appears at the + * beginning of the file, when writing metadata back to a FLAC file + * it is possible to grow or shrink the metadata such that the entire + * file must be rewritten. However, if the size remains the same during + * changes or PADDING blocks are utilized, only the metadata needs to be + * overwritten, which is much faster. + * + * Efficient means the whole file is rewritten at most one time, and only + * when necessary. Level 1 is not efficient only in the case that you + * cause more than one metadata block to grow or shrink beyond what can + * be accomodated by padding. In this case you should probably use level + * 2, which allows you to edit all the metadata for a file in memory and + * write it out all at once. + * + * All levels know how to skip over and not disturb an ID3v2 tag at the + * front of the file. + * + * All levels access files via their filenames. In addition, level 2 + * has additional alternative read and write functions that take an I/O + * handle and callbacks, for situations where access by filename is not + * possible. + * + * In addition to the three interfaces, this module defines functions for + * creating and manipulating various metadata objects in memory. As we see + * from the Format module, FLAC metadata blocks in memory are very primitive + * structures for storing information in an efficient way. Reading + * information from the structures is easy but creating or modifying them + * directly is more complex. The metadata object routines here facilitate + * this by taking care of the consistency and memory management drudgery. + * + * Unless you will be using the level 1 or 2 interfaces to modify existing + * metadata however, you will not probably not need these. + * + * From a dependency standpoint, none of the encoders or decoders require + * the metadata module. This is so that embedded users can strip out the + * metadata module from libFLAC to reduce the size and complexity. + */ + +#ifdef __cplusplus +extern "C" { +#endif + + +/** \defgroup flac_metadata_level0 FLAC/metadata.h: metadata level 0 interface + * \ingroup flac_metadata + * + * \brief + * The level 0 interface consists of individual routines to read the + * STREAMINFO, VORBIS_COMMENT, CUESHEET, and PICTURE blocks, requiring + * only a filename. + * + * They try to skip any ID3v2 tag at the head of the file. + * + * \{ + */ + +/** Read the STREAMINFO metadata block of the given FLAC file. This function + * will try to skip any ID3v2 tag at the head of the file. + * + * \param filename The path to the FLAC file to read. + * \param streaminfo A pointer to space for the STREAMINFO block. Since + * FLAC__StreamMetadata is a simple structure with no + * memory allocation involved, you pass the address of + * an existing structure. It need not be initialized. + * \assert + * \code filename != NULL \endcode + * \code streaminfo != NULL \endcode + * \retval FLAC__bool + * \c true if a valid STREAMINFO block was read from \a filename. Returns + * \c false if there was a memory allocation error, a file decoder error, + * or the file contained no STREAMINFO block. (A memory allocation error + * is possible because this function must set up a file decoder.) + */ +FLAC_API FLAC__bool FLAC__metadata_get_streaminfo(const char *filename, FLAC__StreamMetadata *streaminfo); + +/** Read the VORBIS_COMMENT metadata block of the given FLAC file. This + * function will try to skip any ID3v2 tag at the head of the file. + * + * \param filename The path to the FLAC file to read. + * \param tags The address where the returned pointer will be + * stored. The \a tags object must be deleted by + * the caller using FLAC__metadata_object_delete(). + * \assert + * \code filename != NULL \endcode + * \code tags != NULL \endcode + * \retval FLAC__bool + * \c true if a valid VORBIS_COMMENT block was read from \a filename, + * and \a *tags will be set to the address of the metadata structure. + * Returns \c false if there was a memory allocation error, a file + * decoder error, or the file contained no VORBIS_COMMENT block, and + * \a *tags will be set to \c NULL. + */ +FLAC_API FLAC__bool FLAC__metadata_get_tags(const char *filename, FLAC__StreamMetadata **tags); + +/** Read the CUESHEET metadata block of the given FLAC file. This + * function will try to skip any ID3v2 tag at the head of the file. + * + * \param filename The path to the FLAC file to read. + * \param cuesheet The address where the returned pointer will be + * stored. The \a cuesheet object must be deleted by + * the caller using FLAC__metadata_object_delete(). + * \assert + * \code filename != NULL \endcode + * \code cuesheet != NULL \endcode + * \retval FLAC__bool + * \c true if a valid CUESHEET block was read from \a filename, + * and \a *cuesheet will be set to the address of the metadata + * structure. Returns \c false if there was a memory allocation + * error, a file decoder error, or the file contained no CUESHEET + * block, and \a *cuesheet will be set to \c NULL. + */ +FLAC_API FLAC__bool FLAC__metadata_get_cuesheet(const char *filename, FLAC__StreamMetadata **cuesheet); + +/** Read a PICTURE metadata block of the given FLAC file. This + * function will try to skip any ID3v2 tag at the head of the file. + * Since there can be more than one PICTURE block in a file, this + * function takes a number of parameters that act as constraints to + * the search. The PICTURE block with the largest area matching all + * the constraints will be returned, or \a *picture will be set to + * \c NULL if there was no such block. + * + * \param filename The path to the FLAC file to read. + * \param picture The address where the returned pointer will be + * stored. The \a picture object must be deleted by + * the caller using FLAC__metadata_object_delete(). + * \param type The desired picture type. Use \c -1 to mean + * "any type". + * \param mime_type The desired MIME type, e.g. "image/jpeg". The + * string will be matched exactly. Use \c NULL to + * mean "any MIME type". + * \param description The desired description. The string will be + * matched exactly. Use \c NULL to mean "any + * description". + * \param max_width The maximum width in pixels desired. Use + * \c (unsigned)(-1) to mean "any width". + * \param max_height The maximum height in pixels desired. Use + * \c (unsigned)(-1) to mean "any height". + * \param max_depth The maximum color depth in bits-per-pixel desired. + * Use \c (unsigned)(-1) to mean "any depth". + * \param max_colors The maximum number of colors desired. Use + * \c (unsigned)(-1) to mean "any number of colors". + * \assert + * \code filename != NULL \endcode + * \code picture != NULL \endcode + * \retval FLAC__bool + * \c true if a valid PICTURE block was read from \a filename, + * and \a *picture will be set to the address of the metadata + * structure. Returns \c false if there was a memory allocation + * error, a file decoder error, or the file contained no PICTURE + * block, and \a *picture will be set to \c NULL. + */ +FLAC_API FLAC__bool FLAC__metadata_get_picture(const char *filename, FLAC__StreamMetadata **picture, FLAC__StreamMetadata_Picture_Type type, const char *mime_type, const FLAC__byte *description, unsigned max_width, unsigned max_height, unsigned max_depth, unsigned max_colors); + +/* \} */ + + +/** \defgroup flac_metadata_level1 FLAC/metadata.h: metadata level 1 interface + * \ingroup flac_metadata + * + * \brief + * The level 1 interface provides read-write access to FLAC file metadata and + * operates directly on the FLAC file. + * + * The general usage of this interface is: + * + * - Create an iterator using FLAC__metadata_simple_iterator_new() + * - Attach it to a file using FLAC__metadata_simple_iterator_init() and check + * the exit code. Call FLAC__metadata_simple_iterator_is_writable() to + * see if the file is writable, or only read access is allowed. + * - Use FLAC__metadata_simple_iterator_next() and + * FLAC__metadata_simple_iterator_prev() to traverse the blocks. + * This is does not read the actual blocks themselves. + * FLAC__metadata_simple_iterator_next() is relatively fast. + * FLAC__metadata_simple_iterator_prev() is slower since it needs to search + * forward from the front of the file. + * - Use FLAC__metadata_simple_iterator_get_block_type() or + * FLAC__metadata_simple_iterator_get_block() to access the actual data at + * the current iterator position. The returned object is yours to modify + * and free. + * - Use FLAC__metadata_simple_iterator_set_block() to write a modified block + * back. You must have write permission to the original file. Make sure to + * read the whole comment to FLAC__metadata_simple_iterator_set_block() + * below. + * - Use FLAC__metadata_simple_iterator_insert_block_after() to add new blocks. + * Use the object creation functions from + * \link flac_metadata_object here \endlink to generate new objects. + * - Use FLAC__metadata_simple_iterator_delete_block() to remove the block + * currently referred to by the iterator, or replace it with padding. + * - Destroy the iterator with FLAC__metadata_simple_iterator_delete() when + * finished. + * + * \note + * The FLAC file remains open the whole time between + * FLAC__metadata_simple_iterator_init() and + * FLAC__metadata_simple_iterator_delete(), so make sure you are not altering + * the file during this time. + * + * \note + * Do not modify the \a is_last, \a length, or \a type fields of returned + * FLAC__StreamMetadata objects. These are managed automatically. + * + * \note + * If any of the modification functions + * (FLAC__metadata_simple_iterator_set_block(), + * FLAC__metadata_simple_iterator_delete_block(), + * FLAC__metadata_simple_iterator_insert_block_after(), etc.) return \c false, + * you should delete the iterator as it may no longer be valid. + * + * \{ + */ + +struct FLAC__Metadata_SimpleIterator; +/** The opaque structure definition for the level 1 iterator type. + * See the + * \link flac_metadata_level1 metadata level 1 module \endlink + * for a detailed description. + */ +typedef struct FLAC__Metadata_SimpleIterator FLAC__Metadata_SimpleIterator; + +/** Status type for FLAC__Metadata_SimpleIterator. + * + * The iterator's current status can be obtained by calling FLAC__metadata_simple_iterator_status(). + */ +typedef enum { + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK = 0, + /**< The iterator is in the normal OK state */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT, + /**< The data passed into a function violated the function's usage criteria */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE, + /**< The iterator could not open the target file */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_A_FLAC_FILE, + /**< The iterator could not find the FLAC signature at the start of the file */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_WRITABLE, + /**< The iterator tried to write to a file that was not writable */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_BAD_METADATA, + /**< The iterator encountered input that does not conform to the FLAC metadata specification */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR, + /**< The iterator encountered an error while reading the FLAC file */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR, + /**< The iterator encountered an error while seeking in the FLAC file */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR, + /**< The iterator encountered an error while writing the FLAC file */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_RENAME_ERROR, + /**< The iterator encountered an error renaming the FLAC file */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_UNLINK_ERROR, + /**< The iterator encountered an error removing the temporary file */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR, + /**< Memory allocation failed */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_INTERNAL_ERROR + /**< The caller violated an assertion or an unexpected error occurred */ + +} FLAC__Metadata_SimpleIteratorStatus; + +/** Maps a FLAC__Metadata_SimpleIteratorStatus to a C string. + * + * Using a FLAC__Metadata_SimpleIteratorStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__Metadata_SimpleIteratorStatusString[]; + + +/** Create a new iterator instance. + * + * \retval FLAC__Metadata_SimpleIterator* + * \c NULL if there was an error allocating memory, else the new instance. + */ +FLAC_API FLAC__Metadata_SimpleIterator *FLAC__metadata_simple_iterator_new(void); + +/** Free an iterator instance. Deletes the object pointed to by \a iterator. + * + * \param iterator A pointer to an existing iterator. + * \assert + * \code iterator != NULL \endcode + */ +FLAC_API void FLAC__metadata_simple_iterator_delete(FLAC__Metadata_SimpleIterator *iterator); + +/** Get the current status of the iterator. Call this after a function + * returns \c false to get the reason for the error. Also resets the status + * to FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK. + * + * \param iterator A pointer to an existing iterator. + * \assert + * \code iterator != NULL \endcode + * \retval FLAC__Metadata_SimpleIteratorStatus + * The current status of the iterator. + */ +FLAC_API FLAC__Metadata_SimpleIteratorStatus FLAC__metadata_simple_iterator_status(FLAC__Metadata_SimpleIterator *iterator); + +/** Initialize the iterator to point to the first metadata block in the + * given FLAC file. + * + * \param iterator A pointer to an existing iterator. + * \param filename The path to the FLAC file. + * \param read_only If \c true, the FLAC file will be opened + * in read-only mode; if \c false, the FLAC + * file will be opened for edit even if no + * edits are performed. + * \param preserve_file_stats If \c true, the owner and modification + * time will be preserved even if the FLAC + * file is written to. + * \assert + * \code iterator != NULL \endcode + * \code filename != NULL \endcode + * \retval FLAC__bool + * \c false if a memory allocation error occurs, the file can't be + * opened, or another error occurs, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_init(FLAC__Metadata_SimpleIterator *iterator, const char *filename, FLAC__bool read_only, FLAC__bool preserve_file_stats); + +/** Returns \c true if the FLAC file is writable. If \c false, calls to + * FLAC__metadata_simple_iterator_set_block() and + * FLAC__metadata_simple_iterator_insert_block_after() will fail. + * + * \param iterator A pointer to an existing iterator. + * \assert + * \code iterator != NULL \endcode + * \retval FLAC__bool + * See above. + */ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_is_writable(const FLAC__Metadata_SimpleIterator *iterator); + +/** Moves the iterator forward one metadata block, returning \c false if + * already at the end. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \retval FLAC__bool + * \c false if already at the last metadata block of the chain, else + * \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_next(FLAC__Metadata_SimpleIterator *iterator); + +/** Moves the iterator backward one metadata block, returning \c false if + * already at the beginning. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \retval FLAC__bool + * \c false if already at the first metadata block of the chain, else + * \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_prev(FLAC__Metadata_SimpleIterator *iterator); + +/** Get the type of the metadata block at the current position. This + * avoids reading the actual block data which can save time for large + * blocks. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \retval FLAC__MetadataType + * The type of the metadata block at the current iterator position. + */ + +FLAC_API FLAC__MetadataType FLAC__metadata_simple_iterator_get_block_type(const FLAC__Metadata_SimpleIterator *iterator); + +/** Get the metadata block at the current position. You can modify the + * block but must use FLAC__metadata_simple_iterator_set_block() to + * write it back to the FLAC file. + * + * You must call FLAC__metadata_object_delete() on the returned object + * when you are finished with it. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \retval FLAC__StreamMetadata* + * The current metadata block. + */ +FLAC_API FLAC__StreamMetadata *FLAC__metadata_simple_iterator_get_block(FLAC__Metadata_SimpleIterator *iterator); + +/** Write a block back to the FLAC file. This function tries to be + * as efficient as possible; how the block is actually written is + * shown by the following: + * + * Existing block is a STREAMINFO block and the new block is a + * STREAMINFO block: the new block is written in place. Make sure + * you know what you're doing when changing the values of a + * STREAMINFO block. + * + * Existing block is a STREAMINFO block and the new block is a + * not a STREAMINFO block: this is an error since the first block + * must be a STREAMINFO block. Returns \c false without altering the + * file. + * + * Existing block is not a STREAMINFO block and the new block is a + * STREAMINFO block: this is an error since there may be only one + * STREAMINFO block. Returns \c false without altering the file. + * + * Existing block and new block are the same length: the existing + * block will be replaced by the new block, written in place. + * + * Existing block is longer than new block: if use_padding is \c true, + * the existing block will be overwritten in place with the new + * block followed by a PADDING block, if possible, to make the total + * size the same as the existing block. Remember that a padding + * block requires at least four bytes so if the difference in size + * between the new block and existing block is less than that, the + * entire file will have to be rewritten, using the new block's + * exact size. If use_padding is \c false, the entire file will be + * rewritten, replacing the existing block by the new block. + * + * Existing block is shorter than new block: if use_padding is \c true, + * the function will try and expand the new block into the following + * PADDING block, if it exists and doing so won't shrink the PADDING + * block to less than 4 bytes. If there is no following PADDING + * block, or it will shrink to less than 4 bytes, or use_padding is + * \c false, the entire file is rewritten, replacing the existing block + * with the new block. Note that in this case any following PADDING + * block is preserved as is. + * + * After writing the block, the iterator will remain in the same + * place, i.e. pointing to the new block. + * + * \param iterator A pointer to an existing initialized iterator. + * \param block The block to set. + * \param use_padding See above. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \code block != NULL \endcode + * \retval FLAC__bool + * \c true if successful, else \c false. + */ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_set_block(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, FLAC__bool use_padding); + +/** This is similar to FLAC__metadata_simple_iterator_set_block() + * except that instead of writing over an existing block, it appends + * a block after the existing block. \a use_padding is again used to + * tell the function to try an expand into following padding in an + * attempt to avoid rewriting the entire file. + * + * This function will fail and return \c false if given a STREAMINFO + * block. + * + * After writing the block, the iterator will be pointing to the + * new block. + * + * \param iterator A pointer to an existing initialized iterator. + * \param block The block to set. + * \param use_padding See above. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \code block != NULL \endcode + * \retval FLAC__bool + * \c true if successful, else \c false. + */ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_insert_block_after(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, FLAC__bool use_padding); + +/** Deletes the block at the current position. This will cause the + * entire FLAC file to be rewritten, unless \a use_padding is \c true, + * in which case the block will be replaced by an equal-sized PADDING + * block. The iterator will be left pointing to the block before the + * one just deleted. + * + * You may not delete the STREAMINFO block. + * + * \param iterator A pointer to an existing initialized iterator. + * \param use_padding See above. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \retval FLAC__bool + * \c true if successful, else \c false. + */ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_delete_block(FLAC__Metadata_SimpleIterator *iterator, FLAC__bool use_padding); + +/* \} */ + + +/** \defgroup flac_metadata_level2 FLAC/metadata.h: metadata level 2 interface + * \ingroup flac_metadata + * + * \brief + * The level 2 interface provides read-write access to FLAC file metadata; + * all metadata is read into memory, operated on in memory, and then written + * to file, which is more efficient than level 1 when editing multiple blocks. + * + * Currently Ogg FLAC is supported for read only, via + * FLAC__metadata_chain_read_ogg() but a subsequent + * FLAC__metadata_chain_write() will fail. + * + * The general usage of this interface is: + * + * - Create a new chain using FLAC__metadata_chain_new(). A chain is a + * linked list of FLAC metadata blocks. + * - Read all metadata into the the chain from a FLAC file using + * FLAC__metadata_chain_read() or FLAC__metadata_chain_read_ogg() and + * check the status. + * - Optionally, consolidate the padding using + * FLAC__metadata_chain_merge_padding() or + * FLAC__metadata_chain_sort_padding(). + * - Create a new iterator using FLAC__metadata_iterator_new() + * - Initialize the iterator to point to the first element in the chain + * using FLAC__metadata_iterator_init() + * - Traverse the chain using FLAC__metadata_iterator_next and + * FLAC__metadata_iterator_prev(). + * - Get a block for reading or modification using + * FLAC__metadata_iterator_get_block(). The pointer to the object + * inside the chain is returned, so the block is yours to modify. + * Changes will be reflected in the FLAC file when you write the + * chain. You can also add and delete blocks (see functions below). + * - When done, write out the chain using FLAC__metadata_chain_write(). + * Make sure to read the whole comment to the function below. + * - Delete the chain using FLAC__metadata_chain_delete(). + * + * \note + * Even though the FLAC file is not open while the chain is being + * manipulated, you must not alter the file externally during + * this time. The chain assumes the FLAC file will not change + * between the time of FLAC__metadata_chain_read()/FLAC__metadata_chain_read_ogg() + * and FLAC__metadata_chain_write(). + * + * \note + * Do not modify the is_last, length, or type fields of returned + * FLAC__StreamMetadata objects. These are managed automatically. + * + * \note + * The metadata objects returned by FLAC__metadata_iterator_get_block() + * are owned by the chain; do not FLAC__metadata_object_delete() them. + * In the same way, blocks passed to FLAC__metadata_iterator_set_block() + * become owned by the chain and they will be deleted when the chain is + * deleted. + * + * \{ + */ + +struct FLAC__Metadata_Chain; +/** The opaque structure definition for the level 2 chain type. + */ +typedef struct FLAC__Metadata_Chain FLAC__Metadata_Chain; + +struct FLAC__Metadata_Iterator; +/** The opaque structure definition for the level 2 iterator type. + */ +typedef struct FLAC__Metadata_Iterator FLAC__Metadata_Iterator; + +typedef enum { + FLAC__METADATA_CHAIN_STATUS_OK = 0, + /**< The chain is in the normal OK state */ + + FLAC__METADATA_CHAIN_STATUS_ILLEGAL_INPUT, + /**< The data passed into a function violated the function's usage criteria */ + + FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE, + /**< The chain could not open the target file */ + + FLAC__METADATA_CHAIN_STATUS_NOT_A_FLAC_FILE, + /**< The chain could not find the FLAC signature at the start of the file */ + + FLAC__METADATA_CHAIN_STATUS_NOT_WRITABLE, + /**< The chain tried to write to a file that was not writable */ + + FLAC__METADATA_CHAIN_STATUS_BAD_METADATA, + /**< The chain encountered input that does not conform to the FLAC metadata specification */ + + FLAC__METADATA_CHAIN_STATUS_READ_ERROR, + /**< The chain encountered an error while reading the FLAC file */ + + FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR, + /**< The chain encountered an error while seeking in the FLAC file */ + + FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR, + /**< The chain encountered an error while writing the FLAC file */ + + FLAC__METADATA_CHAIN_STATUS_RENAME_ERROR, + /**< The chain encountered an error renaming the FLAC file */ + + FLAC__METADATA_CHAIN_STATUS_UNLINK_ERROR, + /**< The chain encountered an error removing the temporary file */ + + FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR, + /**< Memory allocation failed */ + + FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR, + /**< The caller violated an assertion or an unexpected error occurred */ + + FLAC__METADATA_CHAIN_STATUS_INVALID_CALLBACKS, + /**< One or more of the required callbacks was NULL */ + + FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH, + /**< FLAC__metadata_chain_write() was called on a chain read by + * FLAC__metadata_chain_read_with_callbacks()/FLAC__metadata_chain_read_ogg_with_callbacks(), + * or + * FLAC__metadata_chain_write_with_callbacks()/FLAC__metadata_chain_write_with_callbacks_and_tempfile() + * was called on a chain read by + * FLAC__metadata_chain_read()/FLAC__metadata_chain_read_ogg(). + * Matching read/write methods must always be used. */ + + FLAC__METADATA_CHAIN_STATUS_WRONG_WRITE_CALL + /**< FLAC__metadata_chain_write_with_callbacks() was called when the + * chain write requires a tempfile; use + * FLAC__metadata_chain_write_with_callbacks_and_tempfile() instead. + * Or, FLAC__metadata_chain_write_with_callbacks_and_tempfile() was + * called when the chain write does not require a tempfile; use + * FLAC__metadata_chain_write_with_callbacks() instead. + * Always check FLAC__metadata_chain_check_if_tempfile_needed() + * before writing via callbacks. */ + +} FLAC__Metadata_ChainStatus; + +/** Maps a FLAC__Metadata_ChainStatus to a C string. + * + * Using a FLAC__Metadata_ChainStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__Metadata_ChainStatusString[]; + +/*********** FLAC__Metadata_Chain ***********/ + +/** Create a new chain instance. + * + * \retval FLAC__Metadata_Chain* + * \c NULL if there was an error allocating memory, else the new instance. + */ +FLAC_API FLAC__Metadata_Chain *FLAC__metadata_chain_new(void); + +/** Free a chain instance. Deletes the object pointed to by \a chain. + * + * \param chain A pointer to an existing chain. + * \assert + * \code chain != NULL \endcode + */ +FLAC_API void FLAC__metadata_chain_delete(FLAC__Metadata_Chain *chain); + +/** Get the current status of the chain. Call this after a function + * returns \c false to get the reason for the error. Also resets the + * status to FLAC__METADATA_CHAIN_STATUS_OK. + * + * \param chain A pointer to an existing chain. + * \assert + * \code chain != NULL \endcode + * \retval FLAC__Metadata_ChainStatus + * The current status of the chain. + */ +FLAC_API FLAC__Metadata_ChainStatus FLAC__metadata_chain_status(FLAC__Metadata_Chain *chain); + +/** Read all metadata from a FLAC file into the chain. + * + * \param chain A pointer to an existing chain. + * \param filename The path to the FLAC file to read. + * \assert + * \code chain != NULL \endcode + * \code filename != NULL \endcode + * \retval FLAC__bool + * \c true if a valid list of metadata blocks was read from + * \a filename, else \c false. On failure, check the status with + * FLAC__metadata_chain_status(). + */ +FLAC_API FLAC__bool FLAC__metadata_chain_read(FLAC__Metadata_Chain *chain, const char *filename); + +/*@@@@ add to unit tests*/ +/** Read all metadata from an Ogg FLAC file into the chain. + * + * \note Ogg FLAC metadata data writing is not supported yet and + * FLAC__metadata_chain_write() will fail. + * + * \param chain A pointer to an existing chain. + * \param filename The path to the Ogg FLAC file to read. + * \assert + * \code chain != NULL \endcode + * \code filename != NULL \endcode + * \retval FLAC__bool + * \c true if a valid list of metadata blocks was read from + * \a filename, else \c false. On failure, check the status with + * FLAC__metadata_chain_status(). + */ +FLAC_API FLAC__bool FLAC__metadata_chain_read_ogg(FLAC__Metadata_Chain *chain, const char *filename); + +/** Read all metadata from a FLAC stream into the chain via I/O callbacks. + * + * The \a handle need only be open for reading, but must be seekable. + * The equivalent minimum stdio fopen() file mode is \c "r" (or \c "rb" + * for Windows). + * + * \param chain A pointer to an existing chain. + * \param handle The I/O handle of the FLAC stream to read. The + * handle will NOT be closed after the metadata is read; + * that is the duty of the caller. + * \param callbacks + * A set of callbacks to use for I/O. The mandatory + * callbacks are \a read, \a seek, and \a tell. + * \assert + * \code chain != NULL \endcode + * \retval FLAC__bool + * \c true if a valid list of metadata blocks was read from + * \a handle, else \c false. On failure, check the status with + * FLAC__metadata_chain_status(). + */ +FLAC_API FLAC__bool FLAC__metadata_chain_read_with_callbacks(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks); + +/*@@@@ add to unit tests*/ +/** Read all metadata from an Ogg FLAC stream into the chain via I/O callbacks. + * + * The \a handle need only be open for reading, but must be seekable. + * The equivalent minimum stdio fopen() file mode is \c "r" (or \c "rb" + * for Windows). + * + * \note Ogg FLAC metadata data writing is not supported yet and + * FLAC__metadata_chain_write() will fail. + * + * \param chain A pointer to an existing chain. + * \param handle The I/O handle of the Ogg FLAC stream to read. The + * handle will NOT be closed after the metadata is read; + * that is the duty of the caller. + * \param callbacks + * A set of callbacks to use for I/O. The mandatory + * callbacks are \a read, \a seek, and \a tell. + * \assert + * \code chain != NULL \endcode + * \retval FLAC__bool + * \c true if a valid list of metadata blocks was read from + * \a handle, else \c false. On failure, check the status with + * FLAC__metadata_chain_status(). + */ +FLAC_API FLAC__bool FLAC__metadata_chain_read_ogg_with_callbacks(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks); + +/** Checks if writing the given chain would require the use of a + * temporary file, or if it could be written in place. + * + * Under certain conditions, padding can be utilized so that writing + * edited metadata back to the FLAC file does not require rewriting the + * entire file. If rewriting is required, then a temporary workfile is + * required. When writing metadata using callbacks, you must check + * this function to know whether to call + * FLAC__metadata_chain_write_with_callbacks() or + * FLAC__metadata_chain_write_with_callbacks_and_tempfile(). When + * writing with FLAC__metadata_chain_write(), the temporary file is + * handled internally. + * + * \param chain A pointer to an existing chain. + * \param use_padding + * Whether or not padding will be allowed to be used + * during the write. The value of \a use_padding given + * here must match the value later passed to + * FLAC__metadata_chain_write_with_callbacks() or + * FLAC__metadata_chain_write_with_callbacks_with_tempfile(). + * \assert + * \code chain != NULL \endcode + * \retval FLAC__bool + * \c true if writing the current chain would require a tempfile, or + * \c false if metadata can be written in place. + */ +FLAC_API FLAC__bool FLAC__metadata_chain_check_if_tempfile_needed(FLAC__Metadata_Chain *chain, FLAC__bool use_padding); + +/** Write all metadata out to the FLAC file. This function tries to be as + * efficient as possible; how the metadata is actually written is shown by + * the following: + * + * If the current chain is the same size as the existing metadata, the new + * data is written in place. + * + * If the current chain is longer than the existing metadata, and + * \a use_padding is \c true, and the last block is a PADDING block of + * sufficient length, the function will truncate the final padding block + * so that the overall size of the metadata is the same as the existing + * metadata, and then just rewrite the metadata. Otherwise, if not all of + * the above conditions are met, the entire FLAC file must be rewritten. + * If you want to use padding this way it is a good idea to call + * FLAC__metadata_chain_sort_padding() first so that you have the maximum + * amount of padding to work with, unless you need to preserve ordering + * of the PADDING blocks for some reason. + * + * If the current chain is shorter than the existing metadata, and + * \a use_padding is \c true, and the final block is a PADDING block, the padding + * is extended to make the overall size the same as the existing data. If + * \a use_padding is \c true and the last block is not a PADDING block, a new + * PADDING block is added to the end of the new data to make it the same + * size as the existing data (if possible, see the note to + * FLAC__metadata_simple_iterator_set_block() about the four byte limit) + * and the new data is written in place. If none of the above apply or + * \a use_padding is \c false, the entire FLAC file is rewritten. + * + * If \a preserve_file_stats is \c true, the owner and modification time will + * be preserved even if the FLAC file is written. + * + * For this write function to be used, the chain must have been read with + * FLAC__metadata_chain_read()/FLAC__metadata_chain_read_ogg(), not + * FLAC__metadata_chain_read_with_callbacks()/FLAC__metadata_chain_read_ogg_with_callbacks(). + * + * \param chain A pointer to an existing chain. + * \param use_padding See above. + * \param preserve_file_stats See above. + * \assert + * \code chain != NULL \endcode + * \retval FLAC__bool + * \c true if the write succeeded, else \c false. On failure, + * check the status with FLAC__metadata_chain_status(). + */ +FLAC_API FLAC__bool FLAC__metadata_chain_write(FLAC__Metadata_Chain *chain, FLAC__bool use_padding, FLAC__bool preserve_file_stats); + +/** Write all metadata out to a FLAC stream via callbacks. + * + * (See FLAC__metadata_chain_write() for the details on how padding is + * used to write metadata in place if possible.) + * + * The \a handle must be open for updating and be seekable. The + * equivalent minimum stdio fopen() file mode is \c "r+" (or \c "r+b" + * for Windows). + * + * For this write function to be used, the chain must have been read with + * FLAC__metadata_chain_read_with_callbacks()/FLAC__metadata_chain_read_ogg_with_callbacks(), + * not FLAC__metadata_chain_read()/FLAC__metadata_chain_read_ogg(). + * Also, FLAC__metadata_chain_check_if_tempfile_needed() must have returned + * \c false. + * + * \param chain A pointer to an existing chain. + * \param use_padding See FLAC__metadata_chain_write() + * \param handle The I/O handle of the FLAC stream to write. The + * handle will NOT be closed after the metadata is + * written; that is the duty of the caller. + * \param callbacks A set of callbacks to use for I/O. The mandatory + * callbacks are \a write and \a seek. + * \assert + * \code chain != NULL \endcode + * \retval FLAC__bool + * \c true if the write succeeded, else \c false. On failure, + * check the status with FLAC__metadata_chain_status(). + */ +FLAC_API FLAC__bool FLAC__metadata_chain_write_with_callbacks(FLAC__Metadata_Chain *chain, FLAC__bool use_padding, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks); + +/** Write all metadata out to a FLAC stream via callbacks. + * + * (See FLAC__metadata_chain_write() for the details on how padding is + * used to write metadata in place if possible.) + * + * This version of the write-with-callbacks function must be used when + * FLAC__metadata_chain_check_if_tempfile_needed() returns true. In + * this function, you must supply an I/O handle corresponding to the + * FLAC file to edit, and a temporary handle to which the new FLAC + * file will be written. It is the caller's job to move this temporary + * FLAC file on top of the original FLAC file to complete the metadata + * edit. + * + * The \a handle must be open for reading and be seekable. The + * equivalent minimum stdio fopen() file mode is \c "r" (or \c "rb" + * for Windows). + * + * The \a temp_handle must be open for writing. The + * equivalent minimum stdio fopen() file mode is \c "w" (or \c "wb" + * for Windows). It should be an empty stream, or at least positioned + * at the start-of-file (in which case it is the caller's duty to + * truncate it on return). + * + * For this write function to be used, the chain must have been read with + * FLAC__metadata_chain_read_with_callbacks()/FLAC__metadata_chain_read_ogg_with_callbacks(), + * not FLAC__metadata_chain_read()/FLAC__metadata_chain_read_ogg(). + * Also, FLAC__metadata_chain_check_if_tempfile_needed() must have returned + * \c true. + * + * \param chain A pointer to an existing chain. + * \param use_padding See FLAC__metadata_chain_write() + * \param handle The I/O handle of the original FLAC stream to read. + * The handle will NOT be closed after the metadata is + * written; that is the duty of the caller. + * \param callbacks A set of callbacks to use for I/O on \a handle. + * The mandatory callbacks are \a read, \a seek, and + * \a eof. + * \param temp_handle The I/O handle of the FLAC stream to write. The + * handle will NOT be closed after the metadata is + * written; that is the duty of the caller. + * \param temp_callbacks + * A set of callbacks to use for I/O on temp_handle. + * The only mandatory callback is \a write. + * \assert + * \code chain != NULL \endcode + * \retval FLAC__bool + * \c true if the write succeeded, else \c false. On failure, + * check the status with FLAC__metadata_chain_status(). + */ +FLAC_API FLAC__bool FLAC__metadata_chain_write_with_callbacks_and_tempfile(FLAC__Metadata_Chain *chain, FLAC__bool use_padding, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks, FLAC__IOHandle temp_handle, FLAC__IOCallbacks temp_callbacks); + +/** Merge adjacent PADDING blocks into a single block. + * + * \note This function does not write to the FLAC file, it only + * modifies the chain. + * + * \warning Any iterator on the current chain will become invalid after this + * call. You should delete the iterator and get a new one. + * + * \param chain A pointer to an existing chain. + * \assert + * \code chain != NULL \endcode + */ +FLAC_API void FLAC__metadata_chain_merge_padding(FLAC__Metadata_Chain *chain); + +/** This function will move all PADDING blocks to the end on the metadata, + * then merge them into a single block. + * + * \note This function does not write to the FLAC file, it only + * modifies the chain. + * + * \warning Any iterator on the current chain will become invalid after this + * call. You should delete the iterator and get a new one. + * + * \param chain A pointer to an existing chain. + * \assert + * \code chain != NULL \endcode + */ +FLAC_API void FLAC__metadata_chain_sort_padding(FLAC__Metadata_Chain *chain); + + +/*********** FLAC__Metadata_Iterator ***********/ + +/** Create a new iterator instance. + * + * \retval FLAC__Metadata_Iterator* + * \c NULL if there was an error allocating memory, else the new instance. + */ +FLAC_API FLAC__Metadata_Iterator *FLAC__metadata_iterator_new(void); + +/** Free an iterator instance. Deletes the object pointed to by \a iterator. + * + * \param iterator A pointer to an existing iterator. + * \assert + * \code iterator != NULL \endcode + */ +FLAC_API void FLAC__metadata_iterator_delete(FLAC__Metadata_Iterator *iterator); + +/** Initialize the iterator to point to the first metadata block in the + * given chain. + * + * \param iterator A pointer to an existing iterator. + * \param chain A pointer to an existing and initialized (read) chain. + * \assert + * \code iterator != NULL \endcode + * \code chain != NULL \endcode + */ +FLAC_API void FLAC__metadata_iterator_init(FLAC__Metadata_Iterator *iterator, FLAC__Metadata_Chain *chain); + +/** Moves the iterator forward one metadata block, returning \c false if + * already at the end. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_iterator_init() + * \retval FLAC__bool + * \c false if already at the last metadata block of the chain, else + * \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_iterator_next(FLAC__Metadata_Iterator *iterator); + +/** Moves the iterator backward one metadata block, returning \c false if + * already at the beginning. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_iterator_init() + * \retval FLAC__bool + * \c false if already at the first metadata block of the chain, else + * \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_iterator_prev(FLAC__Metadata_Iterator *iterator); + +/** Get the type of the metadata block at the current position. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_iterator_init() + * \retval FLAC__MetadataType + * The type of the metadata block at the current iterator position. + */ +FLAC_API FLAC__MetadataType FLAC__metadata_iterator_get_block_type(const FLAC__Metadata_Iterator *iterator); + +/** Get the metadata block at the current position. You can modify + * the block in place but must write the chain before the changes + * are reflected to the FLAC file. You do not need to call + * FLAC__metadata_iterator_set_block() to reflect the changes; + * the pointer returned by FLAC__metadata_iterator_get_block() + * points directly into the chain. + * + * \warning + * Do not call FLAC__metadata_object_delete() on the returned object; + * to delete a block use FLAC__metadata_iterator_delete_block(). + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_iterator_init() + * \retval FLAC__StreamMetadata* + * The current metadata block. + */ +FLAC_API FLAC__StreamMetadata *FLAC__metadata_iterator_get_block(FLAC__Metadata_Iterator *iterator); + +/** Set the metadata block at the current position, replacing the existing + * block. The new block passed in becomes owned by the chain and it will be + * deleted when the chain is deleted. + * + * \param iterator A pointer to an existing initialized iterator. + * \param block A pointer to a metadata block. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_iterator_init() + * \code block != NULL \endcode + * \retval FLAC__bool + * \c false if the conditions in the above description are not met, or + * a memory allocation error occurs, otherwise \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_iterator_set_block(FLAC__Metadata_Iterator *iterator, FLAC__StreamMetadata *block); + +/** Removes the current block from the chain. If \a replace_with_padding is + * \c true, the block will instead be replaced with a padding block of equal + * size. You can not delete the STREAMINFO block. The iterator will be + * left pointing to the block before the one just "deleted", even if + * \a replace_with_padding is \c true. + * + * \param iterator A pointer to an existing initialized iterator. + * \param replace_with_padding See above. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_iterator_init() + * \retval FLAC__bool + * \c false if the conditions in the above description are not met, + * otherwise \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_iterator_delete_block(FLAC__Metadata_Iterator *iterator, FLAC__bool replace_with_padding); + +/** Insert a new block before the current block. You cannot insert a block + * before the first STREAMINFO block. You cannot insert a STREAMINFO block + * as there can be only one, the one that already exists at the head when you + * read in a chain. The chain takes ownership of the new block and it will be + * deleted when the chain is deleted. The iterator will be left pointing to + * the new block. + * + * \param iterator A pointer to an existing initialized iterator. + * \param block A pointer to a metadata block to insert. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_iterator_init() + * \retval FLAC__bool + * \c false if the conditions in the above description are not met, or + * a memory allocation error occurs, otherwise \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_iterator_insert_block_before(FLAC__Metadata_Iterator *iterator, FLAC__StreamMetadata *block); + +/** Insert a new block after the current block. You cannot insert a STREAMINFO + * block as there can be only one, the one that already exists at the head when + * you read in a chain. The chain takes ownership of the new block and it will + * be deleted when the chain is deleted. The iterator will be left pointing to + * the new block. + * + * \param iterator A pointer to an existing initialized iterator. + * \param block A pointer to a metadata block to insert. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_iterator_init() + * \retval FLAC__bool + * \c false if the conditions in the above description are not met, or + * a memory allocation error occurs, otherwise \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_iterator_insert_block_after(FLAC__Metadata_Iterator *iterator, FLAC__StreamMetadata *block); + +/* \} */ + + +/** \defgroup flac_metadata_object FLAC/metadata.h: metadata object methods + * \ingroup flac_metadata + * + * \brief + * This module contains methods for manipulating FLAC metadata objects. + * + * Since many are variable length we have to be careful about the memory + * management. We decree that all pointers to data in the object are + * owned by the object and memory-managed by the object. + * + * Use the FLAC__metadata_object_new() and FLAC__metadata_object_delete() + * functions to create all instances. When using the + * FLAC__metadata_object_set_*() functions to set pointers to data, set + * \a copy to \c true to have the function make it's own copy of the data, or + * to \c false to give the object ownership of your data. In the latter case + * your pointer must be freeable by free() and will be free()d when the object + * is FLAC__metadata_object_delete()d. It is legal to pass a null pointer as + * the data pointer to a FLAC__metadata_object_set_*() function as long as + * the length argument is 0 and the \a copy argument is \c false. + * + * The FLAC__metadata_object_new() and FLAC__metadata_object_clone() function + * will return \c NULL in the case of a memory allocation error, otherwise a new + * object. The FLAC__metadata_object_set_*() functions return \c false in the + * case of a memory allocation error. + * + * We don't have the convenience of C++ here, so note that the library relies + * on you to keep the types straight. In other words, if you pass, for + * example, a FLAC__StreamMetadata* that represents a STREAMINFO block to + * FLAC__metadata_object_application_set_data(), you will get an assertion + * failure. + * + * For convenience the FLAC__metadata_object_vorbiscomment_*() functions + * maintain a trailing NUL on each Vorbis comment entry. This is not counted + * toward the length or stored in the stream, but it can make working with plain + * comments (those that don't contain embedded-NULs in the value) easier. + * Entries passed into these functions have trailing NULs added if missing, and + * returned entries are guaranteed to have a trailing NUL. + * + * The FLAC__metadata_object_vorbiscomment_*() functions that take a Vorbis + * comment entry/name/value will first validate that it complies with the Vorbis + * comment specification and return false if it does not. + * + * There is no need to recalculate the length field on metadata blocks you + * have modified. They will be calculated automatically before they are + * written back to a file. + * + * \{ + */ + + +/** Create a new metadata object instance of the given type. + * + * The object will be "empty"; i.e. values and data pointers will be \c 0, + * with the exception of FLAC__METADATA_TYPE_VORBIS_COMMENT, which will have + * the vendor string set (but zero comments). + * + * Do not pass in a value greater than or equal to + * \a FLAC__METADATA_TYPE_UNDEFINED unless you really know what you're + * doing. + * + * \param type Type of object to create + * \retval FLAC__StreamMetadata* + * \c NULL if there was an error allocating memory or the type code is + * greater than FLAC__MAX_METADATA_TYPE_CODE, else the new instance. + */ +FLAC_API FLAC__StreamMetadata *FLAC__metadata_object_new(FLAC__MetadataType type); + +/** Create a copy of an existing metadata object. + * + * The copy is a "deep" copy, i.e. dynamically allocated data within the + * object is also copied. The caller takes ownership of the new block and + * is responsible for freeing it with FLAC__metadata_object_delete(). + * + * \param object Pointer to object to copy. + * \assert + * \code object != NULL \endcode + * \retval FLAC__StreamMetadata* + * \c NULL if there was an error allocating memory, else the new instance. + */ +FLAC_API FLAC__StreamMetadata *FLAC__metadata_object_clone(const FLAC__StreamMetadata *object); + +/** Free a metadata object. Deletes the object pointed to by \a object. + * + * The delete is a "deep" delete, i.e. dynamically allocated data within the + * object is also deleted. + * + * \param object A pointer to an existing object. + * \assert + * \code object != NULL \endcode + */ +FLAC_API void FLAC__metadata_object_delete(FLAC__StreamMetadata *object); + +/** Compares two metadata objects. + * + * The compare is "deep", i.e. dynamically allocated data within the + * object is also compared. + * + * \param block1 A pointer to an existing object. + * \param block2 A pointer to an existing object. + * \assert + * \code block1 != NULL \endcode + * \code block2 != NULL \endcode + * \retval FLAC__bool + * \c true if objects are identical, else \c false. + */ +FLAC_API FLAC__bool FLAC__metadata_object_is_equal(const FLAC__StreamMetadata *block1, const FLAC__StreamMetadata *block2); + +/** Sets the application data of an APPLICATION block. + * + * If \a copy is \c true, a copy of the data is stored; otherwise, the object + * takes ownership of the pointer. The existing data will be freed if this + * function is successful, otherwise the original data will remain if \a copy + * is \c true and malloc() fails. + * + * \note It is safe to pass a const pointer to \a data if \a copy is \c true. + * + * \param object A pointer to an existing APPLICATION object. + * \param data A pointer to the data to set. + * \param length The length of \a data in bytes. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_APPLICATION \endcode + * \code (data != NULL && length > 0) || + * (data == NULL && length == 0 && copy == false) \endcode + * \retval FLAC__bool + * \c false if \a copy is \c true and malloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_application_set_data(FLAC__StreamMetadata *object, FLAC__byte *data, unsigned length, FLAC__bool copy); + +/** Resize the seekpoint array. + * + * If the size shrinks, elements will truncated; if it grows, new placeholder + * points will be added to the end. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param new_num_points The desired length of the array; may be \c 0. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \code (object->data.seek_table.points == NULL && object->data.seek_table.num_points == 0) || + * (object->data.seek_table.points != NULL && object->data.seek_table.num_points > 0) \endcode + * \retval FLAC__bool + * \c false if memory allocation error, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_resize_points(FLAC__StreamMetadata *object, unsigned new_num_points); + +/** Set a seekpoint in a seektable. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param point_num Index into seekpoint array to set. + * \param point The point to set. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \code object->data.seek_table.num_points > point_num \endcode + */ +FLAC_API void FLAC__metadata_object_seektable_set_point(FLAC__StreamMetadata *object, unsigned point_num, FLAC__StreamMetadata_SeekPoint point); + +/** Insert a seekpoint into a seektable. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param point_num Index into seekpoint array to set. + * \param point The point to set. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \code object->data.seek_table.num_points >= point_num \endcode + * \retval FLAC__bool + * \c false if memory allocation error, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_insert_point(FLAC__StreamMetadata *object, unsigned point_num, FLAC__StreamMetadata_SeekPoint point); + +/** Delete a seekpoint from a seektable. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param point_num Index into seekpoint array to set. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \code object->data.seek_table.num_points > point_num \endcode + * \retval FLAC__bool + * \c false if memory allocation error, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_delete_point(FLAC__StreamMetadata *object, unsigned point_num); + +/** Check a seektable to see if it conforms to the FLAC specification. + * See the format specification for limits on the contents of the + * seektable. + * + * \param object A pointer to an existing SEEKTABLE object. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \retval FLAC__bool + * \c false if seek table is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_is_legal(const FLAC__StreamMetadata *object); + +/** Append a number of placeholder points to the end of a seek table. + * + * \note + * As with the other ..._seektable_template_... functions, you should + * call FLAC__metadata_object_seektable_template_sort() when finished + * to make the seek table legal. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param num The number of placeholder points to append. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \retval FLAC__bool + * \c false if memory allocation fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_placeholders(FLAC__StreamMetadata *object, unsigned num); + +/** Append a specific seek point template to the end of a seek table. + * + * \note + * As with the other ..._seektable_template_... functions, you should + * call FLAC__metadata_object_seektable_template_sort() when finished + * to make the seek table legal. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param sample_number The sample number of the seek point template. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \retval FLAC__bool + * \c false if memory allocation fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_point(FLAC__StreamMetadata *object, FLAC__uint64 sample_number); + +/** Append specific seek point templates to the end of a seek table. + * + * \note + * As with the other ..._seektable_template_... functions, you should + * call FLAC__metadata_object_seektable_template_sort() when finished + * to make the seek table legal. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param sample_numbers An array of sample numbers for the seek points. + * \param num The number of seek point templates to append. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \retval FLAC__bool + * \c false if memory allocation fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_points(FLAC__StreamMetadata *object, FLAC__uint64 sample_numbers[], unsigned num); + +/** Append a set of evenly-spaced seek point templates to the end of a + * seek table. + * + * \note + * As with the other ..._seektable_template_... functions, you should + * call FLAC__metadata_object_seektable_template_sort() when finished + * to make the seek table legal. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param num The number of placeholder points to append. + * \param total_samples The total number of samples to be encoded; + * the seekpoints will be spaced approximately + * \a total_samples / \a num samples apart. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \code total_samples > 0 \endcode + * \retval FLAC__bool + * \c false if memory allocation fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_spaced_points(FLAC__StreamMetadata *object, unsigned num, FLAC__uint64 total_samples); + +/** Append a set of evenly-spaced seek point templates to the end of a + * seek table. + * + * \note + * As with the other ..._seektable_template_... functions, you should + * call FLAC__metadata_object_seektable_template_sort() when finished + * to make the seek table legal. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param samples The number of samples apart to space the placeholder + * points. The first point will be at sample \c 0, the + * second at sample \a samples, then 2*\a samples, and + * so on. As long as \a samples and \a total_samples + * are greater than \c 0, there will always be at least + * one seekpoint at sample \c 0. + * \param total_samples The total number of samples to be encoded; + * the seekpoints will be spaced + * \a samples samples apart. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \code samples > 0 \endcode + * \code total_samples > 0 \endcode + * \retval FLAC__bool + * \c false if memory allocation fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_spaced_points_by_samples(FLAC__StreamMetadata *object, unsigned samples, FLAC__uint64 total_samples); + +/** Sort a seek table's seek points according to the format specification, + * removing duplicates. + * + * \param object A pointer to a seek table to be sorted. + * \param compact If \c false, behaves like FLAC__format_seektable_sort(). + * If \c true, duplicates are deleted and the seek table is + * shrunk appropriately; the number of placeholder points + * present in the seek table will be the same after the call + * as before. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \retval FLAC__bool + * \c false if realloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_sort(FLAC__StreamMetadata *object, FLAC__bool compact); + +/** Sets the vendor string in a VORBIS_COMMENT block. + * + * For convenience, a trailing NUL is added to the entry if it doesn't have + * one already. + * + * If \a copy is \c true, a copy of the entry is stored; otherwise, the object + * takes ownership of the \c entry.entry pointer. + * + * \note If this function returns \c false, the caller still owns the + * pointer. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param entry The entry to set the vendor string to. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \code (entry.entry != NULL && entry.length > 0) || + * (entry.entry == NULL && entry.length == 0) \endcode + * \retval FLAC__bool + * \c false if memory allocation fails or \a entry does not comply with the + * Vorbis comment specification, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_vendor_string(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy); + +/** Resize the comment array. + * + * If the size shrinks, elements will truncated; if it grows, new empty + * fields will be added to the end. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param new_num_comments The desired length of the array; may be \c 0. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \code (object->data.vorbis_comment.comments == NULL && object->data.vorbis_comment.num_comments == 0) || + * (object->data.vorbis_comment.comments != NULL && object->data.vorbis_comment.num_comments > 0) \endcode + * \retval FLAC__bool + * \c false if memory allocation fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_resize_comments(FLAC__StreamMetadata *object, unsigned new_num_comments); + +/** Sets a comment in a VORBIS_COMMENT block. + * + * For convenience, a trailing NUL is added to the entry if it doesn't have + * one already. + * + * If \a copy is \c true, a copy of the entry is stored; otherwise, the object + * takes ownership of the \c entry.entry pointer. + * + * \note If this function returns \c false, the caller still owns the + * pointer. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param comment_num Index into comment array to set. + * \param entry The entry to set the comment to. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \code comment_num < object->data.vorbis_comment.num_comments \endcode + * \code (entry.entry != NULL && entry.length > 0) || + * (entry.entry == NULL && entry.length == 0) \endcode + * \retval FLAC__bool + * \c false if memory allocation fails or \a entry does not comply with the + * Vorbis comment specification, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_comment(FLAC__StreamMetadata *object, unsigned comment_num, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy); + +/** Insert a comment in a VORBIS_COMMENT block at the given index. + * + * For convenience, a trailing NUL is added to the entry if it doesn't have + * one already. + * + * If \a copy is \c true, a copy of the entry is stored; otherwise, the object + * takes ownership of the \c entry.entry pointer. + * + * \note If this function returns \c false, the caller still owns the + * pointer. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param comment_num The index at which to insert the comment. The comments + * at and after \a comment_num move right one position. + * To append a comment to the end, set \a comment_num to + * \c object->data.vorbis_comment.num_comments . + * \param entry The comment to insert. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \code object->data.vorbis_comment.num_comments >= comment_num \endcode + * \code (entry.entry != NULL && entry.length > 0) || + * (entry.entry == NULL && entry.length == 0 && copy == false) \endcode + * \retval FLAC__bool + * \c false if memory allocation fails or \a entry does not comply with the + * Vorbis comment specification, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_insert_comment(FLAC__StreamMetadata *object, unsigned comment_num, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy); + +/** Appends a comment to a VORBIS_COMMENT block. + * + * For convenience, a trailing NUL is added to the entry if it doesn't have + * one already. + * + * If \a copy is \c true, a copy of the entry is stored; otherwise, the object + * takes ownership of the \c entry.entry pointer. + * + * \note If this function returns \c false, the caller still owns the + * pointer. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param entry The comment to insert. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \code (entry.entry != NULL && entry.length > 0) || + * (entry.entry == NULL && entry.length == 0 && copy == false) \endcode + * \retval FLAC__bool + * \c false if memory allocation fails or \a entry does not comply with the + * Vorbis comment specification, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_append_comment(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy); + +/** Replaces comments in a VORBIS_COMMENT block with a new one. + * + * For convenience, a trailing NUL is added to the entry if it doesn't have + * one already. + * + * Depending on the the value of \a all, either all or just the first comment + * whose field name(s) match the given entry's name will be replaced by the + * given entry. If no comments match, \a entry will simply be appended. + * + * If \a copy is \c true, a copy of the entry is stored; otherwise, the object + * takes ownership of the \c entry.entry pointer. + * + * \note If this function returns \c false, the caller still owns the + * pointer. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param entry The comment to insert. + * \param all If \c true, all comments whose field name matches + * \a entry's field name will be removed, and \a entry will + * be inserted at the position of the first matching + * comment. If \c false, only the first comment whose + * field name matches \a entry's field name will be + * replaced with \a entry. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \code (entry.entry != NULL && entry.length > 0) || + * (entry.entry == NULL && entry.length == 0 && copy == false) \endcode + * \retval FLAC__bool + * \c false if memory allocation fails or \a entry does not comply with the + * Vorbis comment specification, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_replace_comment(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool all, FLAC__bool copy); + +/** Delete a comment in a VORBIS_COMMENT block at the given index. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param comment_num The index of the comment to delete. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \code object->data.vorbis_comment.num_comments > comment_num \endcode + * \retval FLAC__bool + * \c false if realloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_delete_comment(FLAC__StreamMetadata *object, unsigned comment_num); + +/** Creates a Vorbis comment entry from NUL-terminated name and value strings. + * + * On return, the filled-in \a entry->entry pointer will point to malloc()ed + * memory and shall be owned by the caller. For convenience the entry will + * have a terminating NUL. + * + * \param entry A pointer to a Vorbis comment entry. The entry's + * \c entry pointer should not point to allocated + * memory as it will be overwritten. + * \param field_name The field name in ASCII, \c NUL terminated. + * \param field_value The field value in UTF-8, \c NUL terminated. + * \assert + * \code entry != NULL \endcode + * \code field_name != NULL \endcode + * \code field_value != NULL \endcode + * \retval FLAC__bool + * \c false if malloc() fails, or if \a field_name or \a field_value does + * not comply with the Vorbis comment specification, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(FLAC__StreamMetadata_VorbisComment_Entry *entry, const char *field_name, const char *field_value); + +/** Splits a Vorbis comment entry into NUL-terminated name and value strings. + * + * The returned pointers to name and value will be allocated by malloc() + * and shall be owned by the caller. + * + * \param entry An existing Vorbis comment entry. + * \param field_name The address of where the returned pointer to the + * field name will be stored. + * \param field_value The address of where the returned pointer to the + * field value will be stored. + * \assert + * \code (entry.entry != NULL && entry.length > 0) \endcode + * \code memchr(entry.entry, '=', entry.length) != NULL \endcode + * \code field_name != NULL \endcode + * \code field_value != NULL \endcode + * \retval FLAC__bool + * \c false if memory allocation fails or \a entry does not comply with the + * Vorbis comment specification, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair(const FLAC__StreamMetadata_VorbisComment_Entry entry, char **field_name, char **field_value); + +/** Check if the given Vorbis comment entry's field name matches the given + * field name. + * + * \param entry An existing Vorbis comment entry. + * \param field_name The field name to check. + * \param field_name_length The length of \a field_name, not including the + * terminating \c NUL. + * \assert + * \code (entry.entry != NULL && entry.length > 0) \endcode + * \retval FLAC__bool + * \c true if the field names match, else \c false + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_matches(const FLAC__StreamMetadata_VorbisComment_Entry entry, const char *field_name, unsigned field_name_length); + +/** Find a Vorbis comment with the given field name. + * + * The search begins at entry number \a offset; use an offset of 0 to + * search from the beginning of the comment array. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param offset The offset into the comment array from where to start + * the search. + * \param field_name The field name of the comment to find. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \code field_name != NULL \endcode + * \retval int + * The offset in the comment array of the first comment whose field + * name matches \a field_name, or \c -1 if no match was found. + */ +FLAC_API int FLAC__metadata_object_vorbiscomment_find_entry_from(const FLAC__StreamMetadata *object, unsigned offset, const char *field_name); + +/** Remove first Vorbis comment matching the given field name. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param field_name The field name of comment to delete. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \retval int + * \c -1 for memory allocation error, \c 0 for no matching entries, + * \c 1 for one matching entry deleted. + */ +FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entry_matching(FLAC__StreamMetadata *object, const char *field_name); + +/** Remove all Vorbis comments matching the given field name. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param field_name The field name of comments to delete. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \retval int + * \c -1 for memory allocation error, \c 0 for no matching entries, + * else the number of matching entries deleted. + */ +FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entries_matching(FLAC__StreamMetadata *object, const char *field_name); + +/** Create a new CUESHEET track instance. + * + * The object will be "empty"; i.e. values and data pointers will be \c 0. + * + * \retval FLAC__StreamMetadata_CueSheet_Track* + * \c NULL if there was an error allocating memory, else the new instance. + */ +FLAC_API FLAC__StreamMetadata_CueSheet_Track *FLAC__metadata_object_cuesheet_track_new(void); + +/** Create a copy of an existing CUESHEET track object. + * + * The copy is a "deep" copy, i.e. dynamically allocated data within the + * object is also copied. The caller takes ownership of the new object and + * is responsible for freeing it with + * FLAC__metadata_object_cuesheet_track_delete(). + * + * \param object Pointer to object to copy. + * \assert + * \code object != NULL \endcode + * \retval FLAC__StreamMetadata_CueSheet_Track* + * \c NULL if there was an error allocating memory, else the new instance. + */ +FLAC_API FLAC__StreamMetadata_CueSheet_Track *FLAC__metadata_object_cuesheet_track_clone(const FLAC__StreamMetadata_CueSheet_Track *object); + +/** Delete a CUESHEET track object + * + * \param object A pointer to an existing CUESHEET track object. + * \assert + * \code object != NULL \endcode + */ +FLAC_API void FLAC__metadata_object_cuesheet_track_delete(FLAC__StreamMetadata_CueSheet_Track *object); + +/** Resize a track's index point array. + * + * If the size shrinks, elements will truncated; if it grows, new blank + * indices will be added to the end. + * + * \param object A pointer to an existing CUESHEET object. + * \param track_num The index of the track to modify. NOTE: this is not + * necessarily the same as the track's \a number field. + * \param new_num_indices The desired length of the array; may be \c 0. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code object->data.cue_sheet.num_tracks > track_num \endcode + * \code (object->data.cue_sheet.tracks[track_num].indices == NULL && object->data.cue_sheet.tracks[track_num].num_indices == 0) || + * (object->data.cue_sheet.tracks[track_num].indices != NULL && object->data.cue_sheet.tracks[track_num].num_indices > 0) \endcode + * \retval FLAC__bool + * \c false if memory allocation error, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_resize_indices(FLAC__StreamMetadata *object, unsigned track_num, unsigned new_num_indices); + +/** Insert an index point in a CUESHEET track at the given index. + * + * \param object A pointer to an existing CUESHEET object. + * \param track_num The index of the track to modify. NOTE: this is not + * necessarily the same as the track's \a number field. + * \param index_num The index into the track's index array at which to + * insert the index point. NOTE: this is not necessarily + * the same as the index point's \a number field. The + * indices at and after \a index_num move right one + * position. To append an index point to the end, set + * \a index_num to + * \c object->data.cue_sheet.tracks[track_num].num_indices . + * \param index The index point to insert. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code object->data.cue_sheet.num_tracks > track_num \endcode + * \code object->data.cue_sheet.tracks[track_num].num_indices >= index_num \endcode + * \retval FLAC__bool + * \c false if realloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_insert_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num, FLAC__StreamMetadata_CueSheet_Index index); + +/** Insert a blank index point in a CUESHEET track at the given index. + * + * A blank index point is one in which all field values are zero. + * + * \param object A pointer to an existing CUESHEET object. + * \param track_num The index of the track to modify. NOTE: this is not + * necessarily the same as the track's \a number field. + * \param index_num The index into the track's index array at which to + * insert the index point. NOTE: this is not necessarily + * the same as the index point's \a number field. The + * indices at and after \a index_num move right one + * position. To append an index point to the end, set + * \a index_num to + * \c object->data.cue_sheet.tracks[track_num].num_indices . + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code object->data.cue_sheet.num_tracks > track_num \endcode + * \code object->data.cue_sheet.tracks[track_num].num_indices >= index_num \endcode + * \retval FLAC__bool + * \c false if realloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_insert_blank_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num); + +/** Delete an index point in a CUESHEET track at the given index. + * + * \param object A pointer to an existing CUESHEET object. + * \param track_num The index into the track array of the track to + * modify. NOTE: this is not necessarily the same + * as the track's \a number field. + * \param index_num The index into the track's index array of the index + * to delete. NOTE: this is not necessarily the same + * as the index's \a number field. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code object->data.cue_sheet.num_tracks > track_num \endcode + * \code object->data.cue_sheet.tracks[track_num].num_indices > index_num \endcode + * \retval FLAC__bool + * \c false if realloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_delete_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num); + +/** Resize the track array. + * + * If the size shrinks, elements will truncated; if it grows, new blank + * tracks will be added to the end. + * + * \param object A pointer to an existing CUESHEET object. + * \param new_num_tracks The desired length of the array; may be \c 0. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code (object->data.cue_sheet.tracks == NULL && object->data.cue_sheet.num_tracks == 0) || + * (object->data.cue_sheet.tracks != NULL && object->data.cue_sheet.num_tracks > 0) \endcode + * \retval FLAC__bool + * \c false if memory allocation error, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_resize_tracks(FLAC__StreamMetadata *object, unsigned new_num_tracks); + +/** Sets a track in a CUESHEET block. + * + * If \a copy is \c true, a copy of the track is stored; otherwise, the object + * takes ownership of the \a track pointer. + * + * \param object A pointer to an existing CUESHEET object. + * \param track_num Index into track array to set. NOTE: this is not + * necessarily the same as the track's \a number field. + * \param track The track to set the track to. You may safely pass in + * a const pointer if \a copy is \c true. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code track_num < object->data.cue_sheet.num_tracks \endcode + * \code (track->indices != NULL && track->num_indices > 0) || + * (track->indices == NULL && track->num_indices == 0) + * \retval FLAC__bool + * \c false if \a copy is \c true and malloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_set_track(FLAC__StreamMetadata *object, unsigned track_num, FLAC__StreamMetadata_CueSheet_Track *track, FLAC__bool copy); + +/** Insert a track in a CUESHEET block at the given index. + * + * If \a copy is \c true, a copy of the track is stored; otherwise, the object + * takes ownership of the \a track pointer. + * + * \param object A pointer to an existing CUESHEET object. + * \param track_num The index at which to insert the track. NOTE: this + * is not necessarily the same as the track's \a number + * field. The tracks at and after \a track_num move right + * one position. To append a track to the end, set + * \a track_num to \c object->data.cue_sheet.num_tracks . + * \param track The track to insert. You may safely pass in a const + * pointer if \a copy is \c true. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code object->data.cue_sheet.num_tracks >= track_num \endcode + * \retval FLAC__bool + * \c false if \a copy is \c true and malloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_track(FLAC__StreamMetadata *object, unsigned track_num, FLAC__StreamMetadata_CueSheet_Track *track, FLAC__bool copy); + +/** Insert a blank track in a CUESHEET block at the given index. + * + * A blank track is one in which all field values are zero. + * + * \param object A pointer to an existing CUESHEET object. + * \param track_num The index at which to insert the track. NOTE: this + * is not necessarily the same as the track's \a number + * field. The tracks at and after \a track_num move right + * one position. To append a track to the end, set + * \a track_num to \c object->data.cue_sheet.num_tracks . + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code object->data.cue_sheet.num_tracks >= track_num \endcode + * \retval FLAC__bool + * \c false if \a copy is \c true and malloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_blank_track(FLAC__StreamMetadata *object, unsigned track_num); + +/** Delete a track in a CUESHEET block at the given index. + * + * \param object A pointer to an existing CUESHEET object. + * \param track_num The index into the track array of the track to + * delete. NOTE: this is not necessarily the same + * as the track's \a number field. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code object->data.cue_sheet.num_tracks > track_num \endcode + * \retval FLAC__bool + * \c false if realloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_delete_track(FLAC__StreamMetadata *object, unsigned track_num); + +/** Check a cue sheet to see if it conforms to the FLAC specification. + * See the format specification for limits on the contents of the + * cue sheet. + * + * \param object A pointer to an existing CUESHEET object. + * \param check_cd_da_subset If \c true, check CUESHEET against more + * stringent requirements for a CD-DA (audio) disc. + * \param violation Address of a pointer to a string. If there is a + * violation, a pointer to a string explanation of the + * violation will be returned here. \a violation may be + * \c NULL if you don't need the returned string. Do not + * free the returned string; it will always point to static + * data. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \retval FLAC__bool + * \c false if cue sheet is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_is_legal(const FLAC__StreamMetadata *object, FLAC__bool check_cd_da_subset, const char **violation); + +/* @@@@ add to unit tests */ +/** Calculate and return the CDDB/freedb ID for a cue sheet. The function + * assumes the cue sheet corresponds to a CD; the result is undefined + * if the cuesheet's is_cd bit is not set. + * + * \param object A pointer to an existing CUESHEET object. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \retval FLAC__uint32 + * The unsigned integer representation of the CDDB/freedb ID + */ +FLAC_API FLAC__uint32 FLAC__metadata_object_cuesheet_calculate_cddb_id(const FLAC__StreamMetadata *object); + +/** Sets the MIME type of a PICTURE block. + * + * If \a copy is \c true, a copy of the string is stored; otherwise, the object + * takes ownership of the pointer. The existing string will be freed if this + * function is successful, otherwise the original string will remain if \a copy + * is \c true and malloc() fails. + * + * \note It is safe to pass a const pointer to \a mime_type if \a copy is \c true. + * + * \param object A pointer to an existing PICTURE object. + * \param mime_type A pointer to the MIME type string. The string must be + * ASCII characters 0x20-0x7e, NUL-terminated. No validation + * is done. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_PICTURE \endcode + * \code (mime_type != NULL) \endcode + * \retval FLAC__bool + * \c false if \a copy is \c true and malloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_picture_set_mime_type(FLAC__StreamMetadata *object, const char *mime_type, FLAC__bool copy); + +/** Sets the description of a PICTURE block. + * + * If \a copy is \c true, a copy of the string is stored; otherwise, the object + * takes ownership of the pointer. The existing string will be freed if this + * function is successful, otherwise the original string will remain if \a copy + * is \c true and malloc() fails. + * + * \note It is safe to pass a const pointer to \a description if \a copy is \c true. + * + * \param object A pointer to an existing PICTURE object. + * \param description A pointer to the description string. The string must be + * valid UTF-8, NUL-terminated. No validation is done. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_PICTURE \endcode + * \code (description != NULL) \endcode + * \retval FLAC__bool + * \c false if \a copy is \c true and malloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_picture_set_description(FLAC__StreamMetadata *object, const FLAC__byte *description, FLAC__bool copy); + +/** Sets the picture data of a PICTURE block. + * + * If \a copy is \c true, a copy of the data is stored; otherwise, the object + * takes ownership of the pointer. Also sets the \a data_length field of the + * metadata object to what is passed in as the \a length parameter. The + * existing data will be freed if this function is successful, otherwise the + * original data and data_length will remain if \a copy is \c true and + * malloc() fails. + * + * \note It is safe to pass a const pointer to \a data if \a copy is \c true. + * + * \param object A pointer to an existing PICTURE object. + * \param data A pointer to the data to set. + * \param length The length of \a data in bytes. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_PICTURE \endcode + * \code (data != NULL && length > 0) || + * (data == NULL && length == 0 && copy == false) \endcode + * \retval FLAC__bool + * \c false if \a copy is \c true and malloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_picture_set_data(FLAC__StreamMetadata *object, const FLAC__byte *data, FLAC__uint32 length, FLAC__bool copy); + +/** Check a PICTURE block to see if it conforms to the FLAC specification. + * See the format specification for limits on the contents of the + * PICTURE block. + * + * \param object A pointer to existing PICTURE block to be checked. + * \param violation Address of a pointer to a string. If there is a + * violation, a pointer to a string explanation of the + * violation will be returned here. \a violation may be + * \c NULL if you don't need the returned string. Do not + * free the returned string; it will always point to static + * data. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_PICTURE \endcode + * \retval FLAC__bool + * \c false if PICTURE block is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_picture_is_legal(const FLAC__StreamMetadata *object, const char **violation); + +/* \} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/FLAC/include/FLAC/ordinals.h b/src/FLAC/include/FLAC/ordinals.h new file mode 100644 index 00000000..a7a5cd96 --- /dev/null +++ b/src/FLAC/include/FLAC/ordinals.h @@ -0,0 +1,80 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__ORDINALS_H +#define FLAC__ORDINALS_H + +#if !(defined(_MSC_VER) || defined(__BORLANDC__) || defined(__EMX__)) +#include +#endif + +typedef signed char FLAC__int8; +typedef unsigned char FLAC__uint8; + +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef __int16 FLAC__int16; +typedef __int32 FLAC__int32; +typedef __int64 FLAC__int64; +typedef unsigned __int16 FLAC__uint16; +typedef unsigned __int32 FLAC__uint32; +typedef unsigned __int64 FLAC__uint64; +#elif defined(__EMX__) +typedef short FLAC__int16; +typedef long FLAC__int32; +typedef long long FLAC__int64; +typedef unsigned short FLAC__uint16; +typedef unsigned long FLAC__uint32; +typedef unsigned long long FLAC__uint64; +#else +typedef int16_t FLAC__int16; +typedef int32_t FLAC__int32; +typedef int64_t FLAC__int64; +typedef uint16_t FLAC__uint16; +typedef uint32_t FLAC__uint32; +typedef uint64_t FLAC__uint64; +#endif + +typedef int FLAC__bool; + +typedef FLAC__uint8 FLAC__byte; + +#ifdef true +#undef true +#endif +#ifdef false +#undef false +#endif +#ifndef __cplusplus +#define true 1 +#define false 0 +#endif + +#endif diff --git a/src/FLAC/include/FLAC/stream_decoder.h b/src/FLAC/include/FLAC/stream_decoder.h new file mode 100644 index 00000000..9ac15947 --- /dev/null +++ b/src/FLAC/include/FLAC/stream_decoder.h @@ -0,0 +1,1559 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__STREAM_DECODER_H +#define FLAC__STREAM_DECODER_H + +#include /* for FILE */ +#include "export.h" +#include "format.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/** \file include/FLAC/stream_decoder.h + * + * \brief + * This module contains the functions which implement the stream + * decoder. + * + * See the detailed documentation in the + * \link flac_stream_decoder stream decoder \endlink module. + */ + +/** \defgroup flac_decoder FLAC/ \*_decoder.h: decoder interfaces + * \ingroup flac + * + * \brief + * This module describes the decoder layers provided by libFLAC. + * + * The stream decoder can be used to decode complete streams either from + * the client via callbacks, or directly from a file, depending on how + * it is initialized. When decoding via callbacks, the client provides + * callbacks for reading FLAC data and writing decoded samples, and + * handling metadata and errors. If the client also supplies seek-related + * callback, the decoder function for sample-accurate seeking within the + * FLAC input is also available. When decoding from a file, the client + * needs only supply a filename or open \c FILE* and write/metadata/error + * callbacks; the rest of the callbacks are supplied internally. For more + * info see the \link flac_stream_decoder stream decoder \endlink module. + */ + +/** \defgroup flac_stream_decoder FLAC/stream_decoder.h: stream decoder interface + * \ingroup flac_decoder + * + * \brief + * This module contains the functions which implement the stream + * decoder. + * + * The stream decoder can decode native FLAC, and optionally Ogg FLAC + * (check FLAC_API_SUPPORTS_OGG_FLAC) streams and files. + * + * The basic usage of this decoder is as follows: + * - The program creates an instance of a decoder using + * FLAC__stream_decoder_new(). + * - The program overrides the default settings using + * FLAC__stream_decoder_set_*() functions. + * - The program initializes the instance to validate the settings and + * prepare for decoding using + * - FLAC__stream_decoder_init_stream() or FLAC__stream_decoder_init_FILE() + * or FLAC__stream_decoder_init_file() for native FLAC, + * - FLAC__stream_decoder_init_ogg_stream() or FLAC__stream_decoder_init_ogg_FILE() + * or FLAC__stream_decoder_init_ogg_file() for Ogg FLAC + * - The program calls the FLAC__stream_decoder_process_*() functions + * to decode data, which subsequently calls the callbacks. + * - The program finishes the decoding with FLAC__stream_decoder_finish(), + * which flushes the input and output and resets the decoder to the + * uninitialized state. + * - The instance may be used again or deleted with + * FLAC__stream_decoder_delete(). + * + * In more detail, the program will create a new instance by calling + * FLAC__stream_decoder_new(), then call FLAC__stream_decoder_set_*() + * functions to override the default decoder options, and call + * one of the FLAC__stream_decoder_init_*() functions. + * + * There are three initialization functions for native FLAC, one for + * setting up the decoder to decode FLAC data from the client via + * callbacks, and two for decoding directly from a FLAC file. + * + * For decoding via callbacks, use FLAC__stream_decoder_init_stream(). + * You must also supply several callbacks for handling I/O. Some (like + * seeking) are optional, depending on the capabilities of the input. + * + * For decoding directly from a file, use FLAC__stream_decoder_init_FILE() + * or FLAC__stream_decoder_init_file(). Then you must only supply an open + * \c FILE* or filename and fewer callbacks; the decoder will handle + * the other callbacks internally. + * + * There are three similarly-named init functions for decoding from Ogg + * FLAC streams. Check \c FLAC_API_SUPPORTS_OGG_FLAC to find out if the + * library has been built with Ogg support. + * + * Once the decoder is initialized, your program will call one of several + * functions to start the decoding process: + * + * - FLAC__stream_decoder_process_single() - Tells the decoder to process at + * most one metadata block or audio frame and return, calling either the + * metadata callback or write callback, respectively, once. If the decoder + * loses sync it will return with only the error callback being called. + * - FLAC__stream_decoder_process_until_end_of_metadata() - Tells the decoder + * to process the stream from the current location and stop upon reaching + * the first audio frame. The client will get one metadata, write, or error + * callback per metadata block, audio frame, or sync error, respectively. + * - FLAC__stream_decoder_process_until_end_of_stream() - Tells the decoder + * to process the stream from the current location until the read callback + * returns FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM or + * FLAC__STREAM_DECODER_READ_STATUS_ABORT. The client will get one metadata, + * write, or error callback per metadata block, audio frame, or sync error, + * respectively. + * + * When the decoder has finished decoding (normally or through an abort), + * the instance is finished by calling FLAC__stream_decoder_finish(), which + * ensures the decoder is in the correct state and frees memory. Then the + * instance may be deleted with FLAC__stream_decoder_delete() or initialized + * again to decode another stream. + * + * Seeking is exposed through the FLAC__stream_decoder_seek_absolute() method. + * At any point after the stream decoder has been initialized, the client can + * call this function to seek to an exact sample within the stream. + * Subsequently, the first time the write callback is called it will be + * passed a (possibly partial) block starting at that sample. + * + * If the client cannot seek via the callback interface provided, but still + * has another way of seeking, it can flush the decoder using + * FLAC__stream_decoder_flush() and start feeding data from the new position + * through the read callback. + * + * The stream decoder also provides MD5 signature checking. If this is + * turned on before initialization, FLAC__stream_decoder_finish() will + * report when the decoded MD5 signature does not match the one stored + * in the STREAMINFO block. MD5 checking is automatically turned off + * (until the next FLAC__stream_decoder_reset()) if there is no signature + * in the STREAMINFO block or when a seek is attempted. + * + * The FLAC__stream_decoder_set_metadata_*() functions deserve special + * attention. By default, the decoder only calls the metadata_callback for + * the STREAMINFO block. These functions allow you to tell the decoder + * explicitly which blocks to parse and return via the metadata_callback + * and/or which to skip. Use a FLAC__stream_decoder_set_metadata_respond_all(), + * FLAC__stream_decoder_set_metadata_ignore() ... or FLAC__stream_decoder_set_metadata_ignore_all(), + * FLAC__stream_decoder_set_metadata_respond() ... sequence to exactly specify + * which blocks to return. Remember that metadata blocks can potentially + * be big (for example, cover art) so filtering out the ones you don't + * use can reduce the memory requirements of the decoder. Also note the + * special forms FLAC__stream_decoder_set_metadata_respond_application(id) + * and FLAC__stream_decoder_set_metadata_ignore_application(id) for + * filtering APPLICATION blocks based on the application ID. + * + * STREAMINFO and SEEKTABLE blocks are always parsed and used internally, but + * they still can legally be filtered from the metadata_callback. + * + * \note + * The "set" functions may only be called when the decoder is in the + * state FLAC__STREAM_DECODER_UNINITIALIZED, i.e. after + * FLAC__stream_decoder_new() or FLAC__stream_decoder_finish(), but + * before FLAC__stream_decoder_init_*(). If this is the case they will + * return \c true, otherwise \c false. + * + * \note + * FLAC__stream_decoder_finish() resets all settings to the constructor + * defaults, including the callbacks. + * + * \{ + */ + + +/** State values for a FLAC__StreamDecoder + * + * The decoder's state can be obtained by calling FLAC__stream_decoder_get_state(). + */ +typedef enum { + + FLAC__STREAM_DECODER_SEARCH_FOR_METADATA = 0, + /**< The decoder is ready to search for metadata. */ + + FLAC__STREAM_DECODER_READ_METADATA, + /**< The decoder is ready to or is in the process of reading metadata. */ + + FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC, + /**< The decoder is ready to or is in the process of searching for the + * frame sync code. + */ + + FLAC__STREAM_DECODER_READ_FRAME, + /**< The decoder is ready to or is in the process of reading a frame. */ + + FLAC__STREAM_DECODER_END_OF_STREAM, + /**< The decoder has reached the end of the stream. */ + + FLAC__STREAM_DECODER_OGG_ERROR, + /**< An error occurred in the underlying Ogg layer. */ + + FLAC__STREAM_DECODER_SEEK_ERROR, + /**< An error occurred while seeking. The decoder must be flushed + * with FLAC__stream_decoder_flush() or reset with + * FLAC__stream_decoder_reset() before decoding can continue. + */ + + FLAC__STREAM_DECODER_ABORTED, + /**< The decoder was aborted by the read callback. */ + + FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR, + /**< An error occurred allocating memory. The decoder is in an invalid + * state and can no longer be used. + */ + + FLAC__STREAM_DECODER_UNINITIALIZED + /**< The decoder is in the uninitialized state; one of the + * FLAC__stream_decoder_init_*() functions must be called before samples + * can be processed. + */ + +} FLAC__StreamDecoderState; + +/** Maps a FLAC__StreamDecoderState to a C string. + * + * Using a FLAC__StreamDecoderState as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderStateString[]; + + +/** Possible return values for the FLAC__stream_decoder_init_*() functions. + */ +typedef enum { + + FLAC__STREAM_DECODER_INIT_STATUS_OK = 0, + /**< Initialization was successful. */ + + FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER, + /**< The library was not compiled with support for the given container + * format. + */ + + FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS, + /**< A required callback was not supplied. */ + + FLAC__STREAM_DECODER_INIT_STATUS_MEMORY_ALLOCATION_ERROR, + /**< An error occurred allocating memory. */ + + FLAC__STREAM_DECODER_INIT_STATUS_ERROR_OPENING_FILE, + /**< fopen() failed in FLAC__stream_decoder_init_file() or + * FLAC__stream_decoder_init_ogg_file(). */ + + FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED + /**< FLAC__stream_decoder_init_*() was called when the decoder was + * already initialized, usually because + * FLAC__stream_decoder_finish() was not called. + */ + +} FLAC__StreamDecoderInitStatus; + +/** Maps a FLAC__StreamDecoderInitStatus to a C string. + * + * Using a FLAC__StreamDecoderInitStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderInitStatusString[]; + + +/** Return values for the FLAC__StreamDecoder read callback. + */ +typedef enum { + + FLAC__STREAM_DECODER_READ_STATUS_CONTINUE, + /**< The read was OK and decoding can continue. */ + + FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM, + /**< The read was attempted while at the end of the stream. Note that + * the client must only return this value when the read callback was + * called when already at the end of the stream. Otherwise, if the read + * itself moves to the end of the stream, the client should still return + * the data and \c FLAC__STREAM_DECODER_READ_STATUS_CONTINUE, and then on + * the next read callback it should return + * \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM with a byte count + * of \c 0. + */ + + FLAC__STREAM_DECODER_READ_STATUS_ABORT + /**< An unrecoverable error occurred. The decoder will return from the process call. */ + +} FLAC__StreamDecoderReadStatus; + +/** Maps a FLAC__StreamDecoderReadStatus to a C string. + * + * Using a FLAC__StreamDecoderReadStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderReadStatusString[]; + + +/** Return values for the FLAC__StreamDecoder seek callback. + */ +typedef enum { + + FLAC__STREAM_DECODER_SEEK_STATUS_OK, + /**< The seek was OK and decoding can continue. */ + + FLAC__STREAM_DECODER_SEEK_STATUS_ERROR, + /**< An unrecoverable error occurred. The decoder will return from the process call. */ + + FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED + /**< Client does not support seeking. */ + +} FLAC__StreamDecoderSeekStatus; + +/** Maps a FLAC__StreamDecoderSeekStatus to a C string. + * + * Using a FLAC__StreamDecoderSeekStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderSeekStatusString[]; + + +/** Return values for the FLAC__StreamDecoder tell callback. + */ +typedef enum { + + FLAC__STREAM_DECODER_TELL_STATUS_OK, + /**< The tell was OK and decoding can continue. */ + + FLAC__STREAM_DECODER_TELL_STATUS_ERROR, + /**< An unrecoverable error occurred. The decoder will return from the process call. */ + + FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED + /**< Client does not support telling the position. */ + +} FLAC__StreamDecoderTellStatus; + +/** Maps a FLAC__StreamDecoderTellStatus to a C string. + * + * Using a FLAC__StreamDecoderTellStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderTellStatusString[]; + + +/** Return values for the FLAC__StreamDecoder length callback. + */ +typedef enum { + + FLAC__STREAM_DECODER_LENGTH_STATUS_OK, + /**< The length call was OK and decoding can continue. */ + + FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR, + /**< An unrecoverable error occurred. The decoder will return from the process call. */ + + FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED + /**< Client does not support reporting the length. */ + +} FLAC__StreamDecoderLengthStatus; + +/** Maps a FLAC__StreamDecoderLengthStatus to a C string. + * + * Using a FLAC__StreamDecoderLengthStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderLengthStatusString[]; + + +/** Return values for the FLAC__StreamDecoder write callback. + */ +typedef enum { + + FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE, + /**< The write was OK and decoding can continue. */ + + FLAC__STREAM_DECODER_WRITE_STATUS_ABORT + /**< An unrecoverable error occurred. The decoder will return from the process call. */ + +} FLAC__StreamDecoderWriteStatus; + +/** Maps a FLAC__StreamDecoderWriteStatus to a C string. + * + * Using a FLAC__StreamDecoderWriteStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderWriteStatusString[]; + + +/** Possible values passed back to the FLAC__StreamDecoder error callback. + * \c FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC is the generic catch- + * all. The rest could be caused by bad sync (false synchronization on + * data that is not the start of a frame) or corrupted data. The error + * itself is the decoder's best guess at what happened assuming a correct + * sync. For example \c FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER + * could be caused by a correct sync on the start of a frame, but some + * data in the frame header was corrupted. Or it could be the result of + * syncing on a point the stream that looked like the starting of a frame + * but was not. \c FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM + * could be because the decoder encountered a valid frame made by a future + * version of the encoder which it cannot parse, or because of a false + * sync making it appear as though an encountered frame was generated by + * a future encoder. + */ +typedef enum { + + FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC, + /**< An error in the stream caused the decoder to lose synchronization. */ + + FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER, + /**< The decoder encountered a corrupted frame header. */ + + FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH, + /**< The frame's data did not match the CRC in the footer. */ + + FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM + /**< The decoder encountered reserved fields in use in the stream. */ + +} FLAC__StreamDecoderErrorStatus; + +/** Maps a FLAC__StreamDecoderErrorStatus to a C string. + * + * Using a FLAC__StreamDecoderErrorStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderErrorStatusString[]; + + +/*********************************************************************** + * + * class FLAC__StreamDecoder + * + ***********************************************************************/ + +struct FLAC__StreamDecoderProtected; +struct FLAC__StreamDecoderPrivate; +/** The opaque structure definition for the stream decoder type. + * See the \link flac_stream_decoder stream decoder module \endlink + * for a detailed description. + */ +typedef struct { + struct FLAC__StreamDecoderProtected *protected_; /* avoid the C++ keyword 'protected' */ + struct FLAC__StreamDecoderPrivate *private_; /* avoid the C++ keyword 'private' */ +} FLAC__StreamDecoder; + +/** Signature for the read callback. + * + * A function pointer matching this signature must be passed to + * FLAC__stream_decoder_init*_stream(). The supplied function will be + * called when the decoder needs more input data. The address of the + * buffer to be filled is supplied, along with the number of bytes the + * buffer can hold. The callback may choose to supply less data and + * modify the byte count but must be careful not to overflow the buffer. + * The callback then returns a status code chosen from + * FLAC__StreamDecoderReadStatus. + * + * Here is an example of a read callback for stdio streams: + * \code + * FLAC__StreamDecoderReadStatus read_cb(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data) + * { + * FILE *file = ((MyClientData*)client_data)->file; + * if(*bytes > 0) { + * *bytes = fread(buffer, sizeof(FLAC__byte), *bytes, file); + * if(ferror(file)) + * return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + * else if(*bytes == 0) + * return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; + * else + * return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; + * } + * else + * return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + * } + * \endcode + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param buffer A pointer to a location for the callee to store + * data to be decoded. + * \param bytes A pointer to the size of the buffer. On entry + * to the callback, it contains the maximum number + * of bytes that may be stored in \a buffer. The + * callee must set it to the actual number of bytes + * stored (0 in case of error or end-of-stream) before + * returning. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + * \retval FLAC__StreamDecoderReadStatus + * The callee's return status. Note that the callback should return + * \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM if and only if + * zero bytes were read and there is no more data to be read. + */ +typedef FLAC__StreamDecoderReadStatus (*FLAC__StreamDecoderReadCallback)(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data); + +/** Signature for the seek callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_decoder_init*_stream(). The supplied function will be + * called when the decoder needs to seek the input stream. The decoder + * will pass the absolute byte offset to seek to, 0 meaning the + * beginning of the stream. + * + * Here is an example of a seek callback for stdio streams: + * \code + * FLAC__StreamDecoderSeekStatus seek_cb(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data) + * { + * FILE *file = ((MyClientData*)client_data)->file; + * if(file == stdin) + * return FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED; + * else if(fseeko(file, (off_t)absolute_byte_offset, SEEK_SET) < 0) + * return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR; + * else + * return FLAC__STREAM_DECODER_SEEK_STATUS_OK; + * } + * \endcode + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param absolute_byte_offset The offset from the beginning of the stream + * to seek to. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + * \retval FLAC__StreamDecoderSeekStatus + * The callee's return status. + */ +typedef FLAC__StreamDecoderSeekStatus (*FLAC__StreamDecoderSeekCallback)(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data); + +/** Signature for the tell callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_decoder_init*_stream(). The supplied function will be + * called when the decoder wants to know the current position of the + * stream. The callback should return the byte offset from the + * beginning of the stream. + * + * Here is an example of a tell callback for stdio streams: + * \code + * FLAC__StreamDecoderTellStatus tell_cb(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data) + * { + * FILE *file = ((MyClientData*)client_data)->file; + * off_t pos; + * if(file == stdin) + * return FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED; + * else if((pos = ftello(file)) < 0) + * return FLAC__STREAM_DECODER_TELL_STATUS_ERROR; + * else { + * *absolute_byte_offset = (FLAC__uint64)pos; + * return FLAC__STREAM_DECODER_TELL_STATUS_OK; + * } + * } + * \endcode + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param absolute_byte_offset A pointer to storage for the current offset + * from the beginning of the stream. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + * \retval FLAC__StreamDecoderTellStatus + * The callee's return status. + */ +typedef FLAC__StreamDecoderTellStatus (*FLAC__StreamDecoderTellCallback)(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data); + +/** Signature for the length callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_decoder_init*_stream(). The supplied function will be + * called when the decoder wants to know the total length of the stream + * in bytes. + * + * Here is an example of a length callback for stdio streams: + * \code + * FLAC__StreamDecoderLengthStatus length_cb(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data) + * { + * FILE *file = ((MyClientData*)client_data)->file; + * struct stat filestats; + * + * if(file == stdin) + * return FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED; + * else if(fstat(fileno(file), &filestats) != 0) + * return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR; + * else { + * *stream_length = (FLAC__uint64)filestats.st_size; + * return FLAC__STREAM_DECODER_LENGTH_STATUS_OK; + * } + * } + * \endcode + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param stream_length A pointer to storage for the length of the stream + * in bytes. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + * \retval FLAC__StreamDecoderLengthStatus + * The callee's return status. + */ +typedef FLAC__StreamDecoderLengthStatus (*FLAC__StreamDecoderLengthCallback)(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data); + +/** Signature for the EOF callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_decoder_init*_stream(). The supplied function will be + * called when the decoder needs to know if the end of the stream has + * been reached. + * + * Here is an example of a EOF callback for stdio streams: + * FLAC__bool eof_cb(const FLAC__StreamDecoder *decoder, void *client_data) + * \code + * { + * FILE *file = ((MyClientData*)client_data)->file; + * return feof(file)? true : false; + * } + * \endcode + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + * \retval FLAC__bool + * \c true if the currently at the end of the stream, else \c false. + */ +typedef FLAC__bool (*FLAC__StreamDecoderEofCallback)(const FLAC__StreamDecoder *decoder, void *client_data); + +/** Signature for the write callback. + * + * A function pointer matching this signature must be passed to one of + * the FLAC__stream_decoder_init_*() functions. + * The supplied function will be called when the decoder has decoded a + * single audio frame. The decoder will pass the frame metadata as well + * as an array of pointers (one for each channel) pointing to the + * decoded audio. + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param frame The description of the decoded frame. See + * FLAC__Frame. + * \param buffer An array of pointers to decoded channels of data. + * Each pointer will point to an array of signed + * samples of length \a frame->header.blocksize. + * Channels will be ordered according to the FLAC + * specification; see the documentation for the + * frame header. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + * \retval FLAC__StreamDecoderWriteStatus + * The callee's return status. + */ +typedef FLAC__StreamDecoderWriteStatus (*FLAC__StreamDecoderWriteCallback)(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data); + +/** Signature for the metadata callback. + * + * A function pointer matching this signature must be passed to one of + * the FLAC__stream_decoder_init_*() functions. + * The supplied function will be called when the decoder has decoded a + * metadata block. In a valid FLAC file there will always be one + * \c STREAMINFO block, followed by zero or more other metadata blocks. + * These will be supplied by the decoder in the same order as they + * appear in the stream and always before the first audio frame (i.e. + * write callback). The metadata block that is passed in must not be + * modified, and it doesn't live beyond the callback, so you should make + * a copy of it with FLAC__metadata_object_clone() if you will need it + * elsewhere. Since metadata blocks can potentially be large, by + * default the decoder only calls the metadata callback for the + * \c STREAMINFO block; you can instruct the decoder to pass or filter + * other blocks with FLAC__stream_decoder_set_metadata_*() calls. + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param metadata The decoded metadata block. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + */ +typedef void (*FLAC__StreamDecoderMetadataCallback)(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data); + +/** Signature for the error callback. + * + * A function pointer matching this signature must be passed to one of + * the FLAC__stream_decoder_init_*() functions. + * The supplied function will be called whenever an error occurs during + * decoding. + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param status The error encountered by the decoder. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + */ +typedef void (*FLAC__StreamDecoderErrorCallback)(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data); + + +/*********************************************************************** + * + * Class constructor/destructor + * + ***********************************************************************/ + +/** Create a new stream decoder instance. The instance is created with + * default settings; see the individual FLAC__stream_decoder_set_*() + * functions for each setting's default. + * + * \retval FLAC__StreamDecoder* + * \c NULL if there was an error allocating memory, else the new instance. + */ +FLAC_API FLAC__StreamDecoder *FLAC__stream_decoder_new(void); + +/** Free a decoder instance. Deletes the object pointed to by \a decoder. + * + * \param decoder A pointer to an existing decoder. + * \assert + * \code decoder != NULL \endcode + */ +FLAC_API void FLAC__stream_decoder_delete(FLAC__StreamDecoder *decoder); + + +/*********************************************************************** + * + * Public class method prototypes + * + ***********************************************************************/ + +/** Set the serial number for the FLAC stream within the Ogg container. + * The default behavior is to use the serial number of the first Ogg + * page. Setting a serial number here will explicitly specify which + * stream is to be decoded. + * + * \note + * This does not need to be set for native FLAC decoding. + * + * \default \c use serial number of first page + * \param decoder A decoder instance to set. + * \param serial_number See above. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_ogg_serial_number(FLAC__StreamDecoder *decoder, long serial_number); + +/** Set the "MD5 signature checking" flag. If \c true, the decoder will + * compute the MD5 signature of the unencoded audio data while decoding + * and compare it to the signature from the STREAMINFO block, if it + * exists, during FLAC__stream_decoder_finish(). + * + * MD5 signature checking will be turned off (until the next + * FLAC__stream_decoder_reset()) if there is no signature in the + * STREAMINFO block or when a seek is attempted. + * + * Clients that do not use the MD5 check should leave this off to speed + * up decoding. + * + * \default \c false + * \param decoder A decoder instance to set. + * \param value Flag value (see above). + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_md5_checking(FLAC__StreamDecoder *decoder, FLAC__bool value); + +/** Direct the decoder to pass on all metadata blocks of type \a type. + * + * \default By default, only the \c STREAMINFO block is returned via the + * metadata callback. + * \param decoder A decoder instance to set. + * \param type See above. + * \assert + * \code decoder != NULL \endcode + * \a type is valid + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond(FLAC__StreamDecoder *decoder, FLAC__MetadataType type); + +/** Direct the decoder to pass on all APPLICATION metadata blocks of the + * given \a id. + * + * \default By default, only the \c STREAMINFO block is returned via the + * metadata callback. + * \param decoder A decoder instance to set. + * \param id See above. + * \assert + * \code decoder != NULL \endcode + * \code id != NULL \endcode + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond_application(FLAC__StreamDecoder *decoder, const FLAC__byte id[4]); + +/** Direct the decoder to pass on all metadata blocks of any type. + * + * \default By default, only the \c STREAMINFO block is returned via the + * metadata callback. + * \param decoder A decoder instance to set. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond_all(FLAC__StreamDecoder *decoder); + +/** Direct the decoder to filter out all metadata blocks of type \a type. + * + * \default By default, only the \c STREAMINFO block is returned via the + * metadata callback. + * \param decoder A decoder instance to set. + * \param type See above. + * \assert + * \code decoder != NULL \endcode + * \a type is valid + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore(FLAC__StreamDecoder *decoder, FLAC__MetadataType type); + +/** Direct the decoder to filter out all APPLICATION metadata blocks of + * the given \a id. + * + * \default By default, only the \c STREAMINFO block is returned via the + * metadata callback. + * \param decoder A decoder instance to set. + * \param id See above. + * \assert + * \code decoder != NULL \endcode + * \code id != NULL \endcode + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore_application(FLAC__StreamDecoder *decoder, const FLAC__byte id[4]); + +/** Direct the decoder to filter out all metadata blocks of any type. + * + * \default By default, only the \c STREAMINFO block is returned via the + * metadata callback. + * \param decoder A decoder instance to set. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore_all(FLAC__StreamDecoder *decoder); + +/** Get the current decoder state. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__StreamDecoderState + * The current decoder state. + */ +FLAC_API FLAC__StreamDecoderState FLAC__stream_decoder_get_state(const FLAC__StreamDecoder *decoder); + +/** Get the current decoder state as a C string. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval const char * + * The decoder state as a C string. Do not modify the contents. + */ +FLAC_API const char *FLAC__stream_decoder_get_resolved_state_string(const FLAC__StreamDecoder *decoder); + +/** Get the "MD5 signature checking" flag. + * This is the value of the setting, not whether or not the decoder is + * currently checking the MD5 (remember, it can be turned off automatically + * by a seek). When the decoder is reset the flag will be restored to the + * value returned by this function. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * See above. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_get_md5_checking(const FLAC__StreamDecoder *decoder); + +/** Get the total number of samples in the stream being decoded. + * Will only be valid after decoding has started and will contain the + * value from the \c STREAMINFO block. A value of \c 0 means "unknown". + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval unsigned + * See above. + */ +FLAC_API FLAC__uint64 FLAC__stream_decoder_get_total_samples(const FLAC__StreamDecoder *decoder); + +/** Get the current number of channels in the stream being decoded. + * Will only be valid after decoding has started and will contain the + * value from the most recently decoded frame header. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval unsigned + * See above. + */ +FLAC_API unsigned FLAC__stream_decoder_get_channels(const FLAC__StreamDecoder *decoder); + +/** Get the current channel assignment in the stream being decoded. + * Will only be valid after decoding has started and will contain the + * value from the most recently decoded frame header. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__ChannelAssignment + * See above. + */ +FLAC_API FLAC__ChannelAssignment FLAC__stream_decoder_get_channel_assignment(const FLAC__StreamDecoder *decoder); + +/** Get the current sample resolution in the stream being decoded. + * Will only be valid after decoding has started and will contain the + * value from the most recently decoded frame header. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval unsigned + * See above. + */ +FLAC_API unsigned FLAC__stream_decoder_get_bits_per_sample(const FLAC__StreamDecoder *decoder); + +/** Get the current sample rate in Hz of the stream being decoded. + * Will only be valid after decoding has started and will contain the + * value from the most recently decoded frame header. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval unsigned + * See above. + */ +FLAC_API unsigned FLAC__stream_decoder_get_sample_rate(const FLAC__StreamDecoder *decoder); + +/** Get the current blocksize of the stream being decoded. + * Will only be valid after decoding has started and will contain the + * value from the most recently decoded frame header. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval unsigned + * See above. + */ +FLAC_API unsigned FLAC__stream_decoder_get_blocksize(const FLAC__StreamDecoder *decoder); + +/** Returns the decoder's current read position within the stream. + * The position is the byte offset from the start of the stream. + * Bytes before this position have been fully decoded. Note that + * there may still be undecoded bytes in the decoder's read FIFO. + * The returned position is correct even after a seek. + * + * \warning This function currently only works for native FLAC, + * not Ogg FLAC streams. + * + * \param decoder A decoder instance to query. + * \param position Address at which to return the desired position. + * \assert + * \code decoder != NULL \endcode + * \code position != NULL \endcode + * \retval FLAC__bool + * \c true if successful, \c false if the stream is not native FLAC, + * or there was an error from the 'tell' callback or it returned + * \c FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_get_decode_position(const FLAC__StreamDecoder *decoder, FLAC__uint64 *position); + +/** Initialize the decoder instance to decode native FLAC streams. + * + * This flavor of initialization sets up the decoder to decode from a + * native FLAC stream. I/O is performed via callbacks to the client. + * For decoding from a plain file via filename or open FILE*, + * FLAC__stream_decoder_init_file() and FLAC__stream_decoder_init_FILE() + * provide a simpler interface. + * + * This function should be called after FLAC__stream_decoder_new() and + * FLAC__stream_decoder_set_*() but before any of the + * FLAC__stream_decoder_process_*() functions. Will set and return the + * decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA + * if initialization succeeded. + * + * \param decoder An uninitialized decoder instance. + * \param read_callback See FLAC__StreamDecoderReadCallback. This + * pointer must not be \c NULL. + * \param seek_callback See FLAC__StreamDecoderSeekCallback. This + * pointer may be \c NULL if seeking is not + * supported. If \a seek_callback is not \c NULL then a + * \a tell_callback, \a length_callback, and \a eof_callback must also be supplied. + * Alternatively, a dummy seek callback that just + * returns \c FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param tell_callback See FLAC__StreamDecoderTellCallback. This + * pointer may be \c NULL if not supported by the client. If + * \a seek_callback is not \c NULL then a + * \a tell_callback must also be supplied. + * Alternatively, a dummy tell callback that just + * returns \c FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param length_callback See FLAC__StreamDecoderLengthCallback. This + * pointer may be \c NULL if not supported by the client. If + * \a seek_callback is not \c NULL then a + * \a length_callback must also be supplied. + * Alternatively, a dummy length callback that just + * returns \c FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param eof_callback See FLAC__StreamDecoderEofCallback. This + * pointer may be \c NULL if not supported by the client. If + * \a seek_callback is not \c NULL then a + * \a eof_callback must also be supplied. + * Alternatively, a dummy length callback that just + * returns \c false + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param write_callback See FLAC__StreamDecoderWriteCallback. This + * pointer must not be \c NULL. + * \param metadata_callback See FLAC__StreamDecoderMetadataCallback. This + * pointer may be \c NULL if the callback is not + * desired. + * \param error_callback See FLAC__StreamDecoderErrorCallback. This + * pointer must not be \c NULL. + * \param client_data This value will be supplied to callbacks in their + * \a client_data argument. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__StreamDecoderInitStatus + * \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful; + * see FLAC__StreamDecoderInitStatus for the meanings of other return values. + */ +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_stream( + FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderReadCallback read_callback, + FLAC__StreamDecoderSeekCallback seek_callback, + FLAC__StreamDecoderTellCallback tell_callback, + FLAC__StreamDecoderLengthCallback length_callback, + FLAC__StreamDecoderEofCallback eof_callback, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +); + +/** Initialize the decoder instance to decode Ogg FLAC streams. + * + * This flavor of initialization sets up the decoder to decode from a + * FLAC stream in an Ogg container. I/O is performed via callbacks to the + * client. For decoding from a plain file via filename or open FILE*, + * FLAC__stream_decoder_init_ogg_file() and FLAC__stream_decoder_init_ogg_FILE() + * provide a simpler interface. + * + * This function should be called after FLAC__stream_decoder_new() and + * FLAC__stream_decoder_set_*() but before any of the + * FLAC__stream_decoder_process_*() functions. Will set and return the + * decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA + * if initialization succeeded. + * + * \note Support for Ogg FLAC in the library is optional. If this + * library has been built without support for Ogg FLAC, this function + * will return \c FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER. + * + * \param decoder An uninitialized decoder instance. + * \param read_callback See FLAC__StreamDecoderReadCallback. This + * pointer must not be \c NULL. + * \param seek_callback See FLAC__StreamDecoderSeekCallback. This + * pointer may be \c NULL if seeking is not + * supported. If \a seek_callback is not \c NULL then a + * \a tell_callback, \a length_callback, and \a eof_callback must also be supplied. + * Alternatively, a dummy seek callback that just + * returns \c FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param tell_callback See FLAC__StreamDecoderTellCallback. This + * pointer may be \c NULL if not supported by the client. If + * \a seek_callback is not \c NULL then a + * \a tell_callback must also be supplied. + * Alternatively, a dummy tell callback that just + * returns \c FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param length_callback See FLAC__StreamDecoderLengthCallback. This + * pointer may be \c NULL if not supported by the client. If + * \a seek_callback is not \c NULL then a + * \a length_callback must also be supplied. + * Alternatively, a dummy length callback that just + * returns \c FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param eof_callback See FLAC__StreamDecoderEofCallback. This + * pointer may be \c NULL if not supported by the client. If + * \a seek_callback is not \c NULL then a + * \a eof_callback must also be supplied. + * Alternatively, a dummy length callback that just + * returns \c false + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param write_callback See FLAC__StreamDecoderWriteCallback. This + * pointer must not be \c NULL. + * \param metadata_callback See FLAC__StreamDecoderMetadataCallback. This + * pointer may be \c NULL if the callback is not + * desired. + * \param error_callback See FLAC__StreamDecoderErrorCallback. This + * pointer must not be \c NULL. + * \param client_data This value will be supplied to callbacks in their + * \a client_data argument. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__StreamDecoderInitStatus + * \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful; + * see FLAC__StreamDecoderInitStatus for the meanings of other return values. + */ +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_stream( + FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderReadCallback read_callback, + FLAC__StreamDecoderSeekCallback seek_callback, + FLAC__StreamDecoderTellCallback tell_callback, + FLAC__StreamDecoderLengthCallback length_callback, + FLAC__StreamDecoderEofCallback eof_callback, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +); + +/** Initialize the decoder instance to decode native FLAC files. + * + * This flavor of initialization sets up the decoder to decode from a + * plain native FLAC file. For non-stdio streams, you must use + * FLAC__stream_decoder_init_stream() and provide callbacks for the I/O. + * + * This function should be called after FLAC__stream_decoder_new() and + * FLAC__stream_decoder_set_*() but before any of the + * FLAC__stream_decoder_process_*() functions. Will set and return the + * decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA + * if initialization succeeded. + * + * \param decoder An uninitialized decoder instance. + * \param file An open FLAC file. The file should have been + * opened with mode \c "rb" and rewound. The file + * becomes owned by the decoder and should not be + * manipulated by the client while decoding. + * Unless \a file is \c stdin, it will be closed + * when FLAC__stream_decoder_finish() is called. + * Note however that seeking will not work when + * decoding from \c stdout since it is not seekable. + * \param write_callback See FLAC__StreamDecoderWriteCallback. This + * pointer must not be \c NULL. + * \param metadata_callback See FLAC__StreamDecoderMetadataCallback. This + * pointer may be \c NULL if the callback is not + * desired. + * \param error_callback See FLAC__StreamDecoderErrorCallback. This + * pointer must not be \c NULL. + * \param client_data This value will be supplied to callbacks in their + * \a client_data argument. + * \assert + * \code decoder != NULL \endcode + * \code file != NULL \endcode + * \retval FLAC__StreamDecoderInitStatus + * \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful; + * see FLAC__StreamDecoderInitStatus for the meanings of other return values. + */ +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_FILE( + FLAC__StreamDecoder *decoder, + FILE *file, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +); + +/** Initialize the decoder instance to decode Ogg FLAC files. + * + * This flavor of initialization sets up the decoder to decode from a + * plain Ogg FLAC file. For non-stdio streams, you must use + * FLAC__stream_decoder_init_ogg_stream() and provide callbacks for the I/O. + * + * This function should be called after FLAC__stream_decoder_new() and + * FLAC__stream_decoder_set_*() but before any of the + * FLAC__stream_decoder_process_*() functions. Will set and return the + * decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA + * if initialization succeeded. + * + * \note Support for Ogg FLAC in the library is optional. If this + * library has been built without support for Ogg FLAC, this function + * will return \c FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER. + * + * \param decoder An uninitialized decoder instance. + * \param file An open FLAC file. The file should have been + * opened with mode \c "rb" and rewound. The file + * becomes owned by the decoder and should not be + * manipulated by the client while decoding. + * Unless \a file is \c stdin, it will be closed + * when FLAC__stream_decoder_finish() is called. + * Note however that seeking will not work when + * decoding from \c stdout since it is not seekable. + * \param write_callback See FLAC__StreamDecoderWriteCallback. This + * pointer must not be \c NULL. + * \param metadata_callback See FLAC__StreamDecoderMetadataCallback. This + * pointer may be \c NULL if the callback is not + * desired. + * \param error_callback See FLAC__StreamDecoderErrorCallback. This + * pointer must not be \c NULL. + * \param client_data This value will be supplied to callbacks in their + * \a client_data argument. + * \assert + * \code decoder != NULL \endcode + * \code file != NULL \endcode + * \retval FLAC__StreamDecoderInitStatus + * \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful; + * see FLAC__StreamDecoderInitStatus for the meanings of other return values. + */ +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_FILE( + FLAC__StreamDecoder *decoder, + FILE *file, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +); + +/** Initialize the decoder instance to decode native FLAC files. + * + * This flavor of initialization sets up the decoder to decode from a plain + * native FLAC file. If POSIX fopen() semantics are not sufficient, (for + * example, with Unicode filenames on Windows), you must use + * FLAC__stream_decoder_init_FILE(), or FLAC__stream_decoder_init_stream() + * and provide callbacks for the I/O. + * + * This function should be called after FLAC__stream_decoder_new() and + * FLAC__stream_decoder_set_*() but before any of the + * FLAC__stream_decoder_process_*() functions. Will set and return the + * decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA + * if initialization succeeded. + * + * \param decoder An uninitialized decoder instance. + * \param filename The name of the file to decode from. The file will + * be opened with fopen(). Use \c NULL to decode from + * \c stdin. Note that \c stdin is not seekable. + * \param write_callback See FLAC__StreamDecoderWriteCallback. This + * pointer must not be \c NULL. + * \param metadata_callback See FLAC__StreamDecoderMetadataCallback. This + * pointer may be \c NULL if the callback is not + * desired. + * \param error_callback See FLAC__StreamDecoderErrorCallback. This + * pointer must not be \c NULL. + * \param client_data This value will be supplied to callbacks in their + * \a client_data argument. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__StreamDecoderInitStatus + * \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful; + * see FLAC__StreamDecoderInitStatus for the meanings of other return values. + */ +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_file( + FLAC__StreamDecoder *decoder, + const char *filename, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +); + +/** Initialize the decoder instance to decode Ogg FLAC files. + * + * This flavor of initialization sets up the decoder to decode from a plain + * Ogg FLAC file. If POSIX fopen() semantics are not sufficient, (for + * example, with Unicode filenames on Windows), you must use + * FLAC__stream_decoder_init_ogg_FILE(), or FLAC__stream_decoder_init_ogg_stream() + * and provide callbacks for the I/O. + * + * This function should be called after FLAC__stream_decoder_new() and + * FLAC__stream_decoder_set_*() but before any of the + * FLAC__stream_decoder_process_*() functions. Will set and return the + * decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA + * if initialization succeeded. + * + * \note Support for Ogg FLAC in the library is optional. If this + * library has been built without support for Ogg FLAC, this function + * will return \c FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER. + * + * \param decoder An uninitialized decoder instance. + * \param filename The name of the file to decode from. The file will + * be opened with fopen(). Use \c NULL to decode from + * \c stdin. Note that \c stdin is not seekable. + * \param write_callback See FLAC__StreamDecoderWriteCallback. This + * pointer must not be \c NULL. + * \param metadata_callback See FLAC__StreamDecoderMetadataCallback. This + * pointer may be \c NULL if the callback is not + * desired. + * \param error_callback See FLAC__StreamDecoderErrorCallback. This + * pointer must not be \c NULL. + * \param client_data This value will be supplied to callbacks in their + * \a client_data argument. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__StreamDecoderInitStatus + * \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful; + * see FLAC__StreamDecoderInitStatus for the meanings of other return values. + */ +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_file( + FLAC__StreamDecoder *decoder, + const char *filename, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +); + +/** Finish the decoding process. + * Flushes the decoding buffer, releases resources, resets the decoder + * settings to their defaults, and returns the decoder state to + * FLAC__STREAM_DECODER_UNINITIALIZED. + * + * In the event of a prematurely-terminated decode, it is not strictly + * necessary to call this immediately before FLAC__stream_decoder_delete() + * but it is good practice to match every FLAC__stream_decoder_init_*() + * with a FLAC__stream_decoder_finish(). + * + * \param decoder An uninitialized decoder instance. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if MD5 checking is on AND a STREAMINFO block was available + * AND the MD5 signature in the STREAMINFO block was non-zero AND the + * signature does not match the one computed by the decoder; else + * \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_finish(FLAC__StreamDecoder *decoder); + +/** Flush the stream input. + * The decoder's input buffer will be cleared and the state set to + * \c FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC. This will also turn + * off MD5 checking. + * + * \param decoder A decoder instance. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c true if successful, else \c false if a memory allocation + * error occurs (in which case the state will be set to + * \c FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR). + */ +FLAC_API FLAC__bool FLAC__stream_decoder_flush(FLAC__StreamDecoder *decoder); + +/** Reset the decoding process. + * The decoder's input buffer will be cleared and the state set to + * \c FLAC__STREAM_DECODER_SEARCH_FOR_METADATA. This is similar to + * FLAC__stream_decoder_finish() except that the settings are + * preserved; there is no need to call FLAC__stream_decoder_init_*() + * before decoding again. MD5 checking will be restored to its original + * setting. + * + * If the decoder is seekable, or was initialized with + * FLAC__stream_decoder_init*_FILE() or FLAC__stream_decoder_init*_file(), + * the decoder will also attempt to seek to the beginning of the file. + * If this rewind fails, this function will return \c false. It follows + * that FLAC__stream_decoder_reset() cannot be used when decoding from + * \c stdin. + * + * If the decoder was initialized with FLAC__stream_encoder_init*_stream() + * and is not seekable (i.e. no seek callback was provided or the seek + * callback returns \c FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED), it + * is the duty of the client to start feeding data from the beginning of + * the stream on the next FLAC__stream_decoder_process() or + * FLAC__stream_decoder_process_interleaved() call. + * + * \param decoder A decoder instance. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c true if successful, else \c false if a memory allocation occurs + * (in which case the state will be set to + * \c FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR) or a seek error + * occurs (the state will be unchanged). + */ +FLAC_API FLAC__bool FLAC__stream_decoder_reset(FLAC__StreamDecoder *decoder); + +/** Decode one metadata block or audio frame. + * This version instructs the decoder to decode a either a single metadata + * block or a single frame and stop, unless the callbacks return a fatal + * error or the read callback returns + * \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM. + * + * As the decoder needs more input it will call the read callback. + * Depending on what was decoded, the metadata or write callback will be + * called with the decoded metadata block or audio frame. + * + * Unless there is a fatal read error or end of stream, this function + * will return once one whole frame is decoded. In other words, if the + * stream is not synchronized or points to a corrupt frame header, the + * decoder will continue to try and resync until it gets to a valid + * frame, then decode one frame, then return. If the decoder points to + * a frame whose frame CRC in the frame footer does not match the + * computed frame CRC, this function will issue a + * FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH error to the + * error callback, and return, having decoded one complete, although + * corrupt, frame. (Such corrupted frames are sent as silence of the + * correct length to the write callback.) + * + * \param decoder An initialized decoder instance. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if any fatal read, write, or memory allocation error + * occurred (meaning decoding must stop), else \c true; for more + * information about the decoder, check the decoder state with + * FLAC__stream_decoder_get_state(). + */ +FLAC_API FLAC__bool FLAC__stream_decoder_process_single(FLAC__StreamDecoder *decoder); + +/** Decode until the end of the metadata. + * This version instructs the decoder to decode from the current position + * and continue until all the metadata has been read, or until the + * callbacks return a fatal error or the read callback returns + * \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM. + * + * As the decoder needs more input it will call the read callback. + * As each metadata block is decoded, the metadata callback will be called + * with the decoded metadata. + * + * \param decoder An initialized decoder instance. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if any fatal read, write, or memory allocation error + * occurred (meaning decoding must stop), else \c true; for more + * information about the decoder, check the decoder state with + * FLAC__stream_decoder_get_state(). + */ +FLAC_API FLAC__bool FLAC__stream_decoder_process_until_end_of_metadata(FLAC__StreamDecoder *decoder); + +/** Decode until the end of the stream. + * This version instructs the decoder to decode from the current position + * and continue until the end of stream (the read callback returns + * \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM), or until the + * callbacks return a fatal error. + * + * As the decoder needs more input it will call the read callback. + * As each metadata block and frame is decoded, the metadata or write + * callback will be called with the decoded metadata or frame. + * + * \param decoder An initialized decoder instance. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if any fatal read, write, or memory allocation error + * occurred (meaning decoding must stop), else \c true; for more + * information about the decoder, check the decoder state with + * FLAC__stream_decoder_get_state(). + */ +FLAC_API FLAC__bool FLAC__stream_decoder_process_until_end_of_stream(FLAC__StreamDecoder *decoder); + +/** Skip one audio frame. + * This version instructs the decoder to 'skip' a single frame and stop, + * unless the callbacks return a fatal error or the read callback returns + * \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM. + * + * The decoding flow is the same as what occurs when + * FLAC__stream_decoder_process_single() is called to process an audio + * frame, except that this function does not decode the parsed data into + * PCM or call the write callback. The integrity of the frame is still + * checked the same way as in the other process functions. + * + * This function will return once one whole frame is skipped, in the + * same way that FLAC__stream_decoder_process_single() will return once + * one whole frame is decoded. + * + * This function can be used in more quickly determining FLAC frame + * boundaries when decoding of the actual data is not needed, for + * example when an application is separating a FLAC stream into frames + * for editing or storing in a container. To do this, the application + * can use FLAC__stream_decoder_skip_single_frame() to quickly advance + * to the next frame, then use + * FLAC__stream_decoder_get_decode_position() to find the new frame + * boundary. + * + * This function should only be called when the stream has advanced + * past all the metadata, otherwise it will return \c false. + * + * \param decoder An initialized decoder instance not in a metadata + * state. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if any fatal read, write, or memory allocation error + * occurred (meaning decoding must stop), or if the decoder + * is in the FLAC__STREAM_DECODER_SEARCH_FOR_METADATA or + * FLAC__STREAM_DECODER_READ_METADATA state, else \c true; for more + * information about the decoder, check the decoder state with + * FLAC__stream_decoder_get_state(). + */ +FLAC_API FLAC__bool FLAC__stream_decoder_skip_single_frame(FLAC__StreamDecoder *decoder); + +/** Flush the input and seek to an absolute sample. + * Decoding will resume at the given sample. Note that because of + * this, the next write callback may contain a partial block. The + * client must support seeking the input or this function will fail + * and return \c false. Furthermore, if the decoder state is + * \c FLAC__STREAM_DECODER_SEEK_ERROR, then the decoder must be flushed + * with FLAC__stream_decoder_flush() or reset with + * FLAC__stream_decoder_reset() before decoding can continue. + * + * \param decoder A decoder instance. + * \param sample The target sample number to seek to. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c true if successful, else \c false. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_seek_absolute(FLAC__StreamDecoder *decoder, FLAC__uint64 sample); + +/* \} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/FLAC/include/FLAC/stream_encoder.h b/src/FLAC/include/FLAC/stream_encoder.h new file mode 100644 index 00000000..bf389d2f --- /dev/null +++ b/src/FLAC/include/FLAC/stream_encoder.h @@ -0,0 +1,1769 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__STREAM_ENCODER_H +#define FLAC__STREAM_ENCODER_H + +#include /* for FILE */ +#include "export.h" +#include "format.h" +#include "stream_decoder.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/** \file include/FLAC/stream_encoder.h + * + * \brief + * This module contains the functions which implement the stream + * encoder. + * + * See the detailed documentation in the + * \link flac_stream_encoder stream encoder \endlink module. + */ + +/** \defgroup flac_encoder FLAC/ \*_encoder.h: encoder interfaces + * \ingroup flac + * + * \brief + * This module describes the encoder layers provided by libFLAC. + * + * The stream encoder can be used to encode complete streams either to the + * client via callbacks, or directly to a file, depending on how it is + * initialized. When encoding via callbacks, the client provides a write + * callback which will be called whenever FLAC data is ready to be written. + * If the client also supplies a seek callback, the encoder will also + * automatically handle the writing back of metadata discovered while + * encoding, like stream info, seek points offsets, etc. When encoding to + * a file, the client needs only supply a filename or open \c FILE* and an + * optional progress callback for periodic notification of progress; the + * write and seek callbacks are supplied internally. For more info see the + * \link flac_stream_encoder stream encoder \endlink module. + */ + +/** \defgroup flac_stream_encoder FLAC/stream_encoder.h: stream encoder interface + * \ingroup flac_encoder + * + * \brief + * This module contains the functions which implement the stream + * encoder. + * + * The stream encoder can encode to native FLAC, and optionally Ogg FLAC + * (check FLAC_API_SUPPORTS_OGG_FLAC) streams and files. + * + * The basic usage of this encoder is as follows: + * - The program creates an instance of an encoder using + * FLAC__stream_encoder_new(). + * - The program overrides the default settings using + * FLAC__stream_encoder_set_*() functions. At a minimum, the following + * functions should be called: + * - FLAC__stream_encoder_set_channels() + * - FLAC__stream_encoder_set_bits_per_sample() + * - FLAC__stream_encoder_set_sample_rate() + * - FLAC__stream_encoder_set_ogg_serial_number() (if encoding to Ogg FLAC) + * - FLAC__stream_encoder_set_total_samples_estimate() (if known) + * - If the application wants to control the compression level or set its own + * metadata, then the following should also be called: + * - FLAC__stream_encoder_set_compression_level() + * - FLAC__stream_encoder_set_verify() + * - FLAC__stream_encoder_set_metadata() + * - The rest of the set functions should only be called if the client needs + * exact control over how the audio is compressed; thorough understanding + * of the FLAC format is necessary to achieve good results. + * - The program initializes the instance to validate the settings and + * prepare for encoding using + * - FLAC__stream_encoder_init_stream() or FLAC__stream_encoder_init_FILE() + * or FLAC__stream_encoder_init_file() for native FLAC + * - FLAC__stream_encoder_init_ogg_stream() or FLAC__stream_encoder_init_ogg_FILE() + * or FLAC__stream_encoder_init_ogg_file() for Ogg FLAC + * - The program calls FLAC__stream_encoder_process() or + * FLAC__stream_encoder_process_interleaved() to encode data, which + * subsequently calls the callbacks when there is encoder data ready + * to be written. + * - The program finishes the encoding with FLAC__stream_encoder_finish(), + * which causes the encoder to encode any data still in its input pipe, + * update the metadata with the final encoding statistics if output + * seeking is possible, and finally reset the encoder to the + * uninitialized state. + * - The instance may be used again or deleted with + * FLAC__stream_encoder_delete(). + * + * In more detail, the stream encoder functions similarly to the + * \link flac_stream_decoder stream decoder \endlink, but has fewer + * callbacks and more options. Typically the client will create a new + * instance by calling FLAC__stream_encoder_new(), then set the necessary + * parameters with FLAC__stream_encoder_set_*(), and initialize it by + * calling one of the FLAC__stream_encoder_init_*() functions. + * + * Unlike the decoders, the stream encoder has many options that can + * affect the speed and compression ratio. When setting these parameters + * you should have some basic knowledge of the format (see the + * user-level documentation + * or the formal description). The + * FLAC__stream_encoder_set_*() functions themselves do not validate the + * values as many are interdependent. The FLAC__stream_encoder_init_*() + * functions will do this, so make sure to pay attention to the state + * returned by FLAC__stream_encoder_init_*() to make sure that it is + * FLAC__STREAM_ENCODER_INIT_STATUS_OK. Any parameters that are not set + * before FLAC__stream_encoder_init_*() will take on the defaults from + * the constructor. + * + * There are three initialization functions for native FLAC, one for + * setting up the encoder to encode FLAC data to the client via + * callbacks, and two for encoding directly to a file. + * + * For encoding via callbacks, use FLAC__stream_encoder_init_stream(). + * You must also supply a write callback which will be called anytime + * there is raw encoded data to write. If the client can seek the output + * it is best to also supply seek and tell callbacks, as this allows the + * encoder to go back after encoding is finished to write back + * information that was collected while encoding, like seek point offsets, + * frame sizes, etc. + * + * For encoding directly to a file, use FLAC__stream_encoder_init_FILE() + * or FLAC__stream_encoder_init_file(). Then you must only supply a + * filename or open \c FILE*; the encoder will handle all the callbacks + * internally. You may also supply a progress callback for periodic + * notification of the encoding progress. + * + * There are three similarly-named init functions for encoding to Ogg + * FLAC streams. Check \c FLAC_API_SUPPORTS_OGG_FLAC to find out if the + * library has been built with Ogg support. + * + * The call to FLAC__stream_encoder_init_*() currently will also immediately + * call the write callback several times, once with the \c fLaC signature, + * and once for each encoded metadata block. Note that for Ogg FLAC + * encoding you will usually get at least twice the number of callbacks than + * with native FLAC, one for the Ogg page header and one for the page body. + * + * After initializing the instance, the client may feed audio data to the + * encoder in one of two ways: + * + * - Channel separate, through FLAC__stream_encoder_process() - The client + * will pass an array of pointers to buffers, one for each channel, to + * the encoder, each of the same length. The samples need not be + * block-aligned, but each channel should have the same number of samples. + * - Channel interleaved, through + * FLAC__stream_encoder_process_interleaved() - The client will pass a single + * pointer to data that is channel-interleaved (i.e. channel0_sample0, + * channel1_sample0, ... , channelN_sample0, channel0_sample1, ...). + * Again, the samples need not be block-aligned but they must be + * sample-aligned, i.e. the first value should be channel0_sample0 and + * the last value channelN_sampleM. + * + * Note that for either process call, each sample in the buffers should be a + * signed integer, right-justified to the resolution set by + * FLAC__stream_encoder_set_bits_per_sample(). For example, if the resolution + * is 16 bits per sample, the samples should all be in the range [-32768,32767]. + * + * When the client is finished encoding data, it calls + * FLAC__stream_encoder_finish(), which causes the encoder to encode any + * data still in its input pipe, and call the metadata callback with the + * final encoding statistics. Then the instance may be deleted with + * FLAC__stream_encoder_delete() or initialized again to encode another + * stream. + * + * For programs that write their own metadata, but that do not know the + * actual metadata until after encoding, it is advantageous to instruct + * the encoder to write a PADDING block of the correct size, so that + * instead of rewriting the whole stream after encoding, the program can + * just overwrite the PADDING block. If only the maximum size of the + * metadata is known, the program can write a slightly larger padding + * block, then split it after encoding. + * + * Make sure you understand how lengths are calculated. All FLAC metadata + * blocks have a 4 byte header which contains the type and length. This + * length does not include the 4 bytes of the header. See the format page + * for the specification of metadata blocks and their lengths. + * + * \note + * If you are writing the FLAC data to a file via callbacks, make sure it + * is open for update (e.g. mode "w+" for stdio streams). This is because + * after the first encoding pass, the encoder will try to seek back to the + * beginning of the stream, to the STREAMINFO block, to write some data + * there. (If using FLAC__stream_encoder_init*_file() or + * FLAC__stream_encoder_init*_FILE(), the file is managed internally.) + * + * \note + * The "set" functions may only be called when the encoder is in the + * state FLAC__STREAM_ENCODER_UNINITIALIZED, i.e. after + * FLAC__stream_encoder_new() or FLAC__stream_encoder_finish(), but + * before FLAC__stream_encoder_init_*(). If this is the case they will + * return \c true, otherwise \c false. + * + * \note + * FLAC__stream_encoder_finish() resets all settings to the constructor + * defaults. + * + * \{ + */ + + +/** State values for a FLAC__StreamEncoder. + * + * The encoder's state can be obtained by calling FLAC__stream_encoder_get_state(). + * + * If the encoder gets into any other state besides \c FLAC__STREAM_ENCODER_OK + * or \c FLAC__STREAM_ENCODER_UNINITIALIZED, it becomes invalid for encoding and + * must be deleted with FLAC__stream_encoder_delete(). + */ +typedef enum { + + FLAC__STREAM_ENCODER_OK = 0, + /**< The encoder is in the normal OK state and samples can be processed. */ + + FLAC__STREAM_ENCODER_UNINITIALIZED, + /**< The encoder is in the uninitialized state; one of the + * FLAC__stream_encoder_init_*() functions must be called before samples + * can be processed. + */ + + FLAC__STREAM_ENCODER_OGG_ERROR, + /**< An error occurred in the underlying Ogg layer. */ + + FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR, + /**< An error occurred in the underlying verify stream decoder; + * check FLAC__stream_encoder_get_verify_decoder_state(). + */ + + FLAC__STREAM_ENCODER_VERIFY_MISMATCH_IN_AUDIO_DATA, + /**< The verify decoder detected a mismatch between the original + * audio signal and the decoded audio signal. + */ + + FLAC__STREAM_ENCODER_CLIENT_ERROR, + /**< One of the callbacks returned a fatal error. */ + + FLAC__STREAM_ENCODER_IO_ERROR, + /**< An I/O error occurred while opening/reading/writing a file. + * Check \c errno. + */ + + FLAC__STREAM_ENCODER_FRAMING_ERROR, + /**< An error occurred while writing the stream; usually, the + * write_callback returned an error. + */ + + FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR + /**< Memory allocation failed. */ + +} FLAC__StreamEncoderState; + +/** Maps a FLAC__StreamEncoderState to a C string. + * + * Using a FLAC__StreamEncoderState as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamEncoderStateString[]; + + +/** Possible return values for the FLAC__stream_encoder_init_*() functions. + */ +typedef enum { + + FLAC__STREAM_ENCODER_INIT_STATUS_OK = 0, + /**< Initialization was successful. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR, + /**< General failure to set up encoder; call FLAC__stream_encoder_get_state() for cause. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_UNSUPPORTED_CONTAINER, + /**< The library was not compiled with support for the given container + * format. + */ + + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_CALLBACKS, + /**< A required callback was not supplied. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_NUMBER_OF_CHANNELS, + /**< The encoder has an invalid setting for number of channels. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_BITS_PER_SAMPLE, + /**< The encoder has an invalid setting for bits-per-sample. + * FLAC supports 4-32 bps but the reference encoder currently supports + * only up to 24 bps. + */ + + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_SAMPLE_RATE, + /**< The encoder has an invalid setting for the input sample rate. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_BLOCK_SIZE, + /**< The encoder has an invalid setting for the block size. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_MAX_LPC_ORDER, + /**< The encoder has an invalid setting for the maximum LPC order. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_QLP_COEFF_PRECISION, + /**< The encoder has an invalid setting for the precision of the quantized linear predictor coefficients. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_BLOCK_SIZE_TOO_SMALL_FOR_LPC_ORDER, + /**< The specified block size is less than the maximum LPC order. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_NOT_STREAMABLE, + /**< The encoder is bound to the Subset but other settings violate it. */ + + FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_METADATA, + /**< The metadata input to the encoder is invalid, in one of the following ways: + * - FLAC__stream_encoder_set_metadata() was called with a null pointer but a block count > 0 + * - One of the metadata blocks contains an undefined type + * - It contains an illegal CUESHEET as checked by FLAC__format_cuesheet_is_legal() + * - It contains an illegal SEEKTABLE as checked by FLAC__format_seektable_is_legal() + * - It contains more than one SEEKTABLE block or more than one VORBIS_COMMENT block + */ + + FLAC__STREAM_ENCODER_INIT_STATUS_ALREADY_INITIALIZED + /**< FLAC__stream_encoder_init_*() was called when the encoder was + * already initialized, usually because + * FLAC__stream_encoder_finish() was not called. + */ + +} FLAC__StreamEncoderInitStatus; + +/** Maps a FLAC__StreamEncoderInitStatus to a C string. + * + * Using a FLAC__StreamEncoderInitStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamEncoderInitStatusString[]; + + +/** Return values for the FLAC__StreamEncoder read callback. + */ +typedef enum { + + FLAC__STREAM_ENCODER_READ_STATUS_CONTINUE, + /**< The read was OK and decoding can continue. */ + + FLAC__STREAM_ENCODER_READ_STATUS_END_OF_STREAM, + /**< The read was attempted at the end of the stream. */ + + FLAC__STREAM_ENCODER_READ_STATUS_ABORT, + /**< An unrecoverable error occurred. */ + + FLAC__STREAM_ENCODER_READ_STATUS_UNSUPPORTED + /**< Client does not support reading back from the output. */ + +} FLAC__StreamEncoderReadStatus; + +/** Maps a FLAC__StreamEncoderReadStatus to a C string. + * + * Using a FLAC__StreamEncoderReadStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamEncoderReadStatusString[]; + + +/** Return values for the FLAC__StreamEncoder write callback. + */ +typedef enum { + + FLAC__STREAM_ENCODER_WRITE_STATUS_OK = 0, + /**< The write was OK and encoding can continue. */ + + FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR + /**< An unrecoverable error occurred. The encoder will return from the process call. */ + +} FLAC__StreamEncoderWriteStatus; + +/** Maps a FLAC__StreamEncoderWriteStatus to a C string. + * + * Using a FLAC__StreamEncoderWriteStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamEncoderWriteStatusString[]; + + +/** Return values for the FLAC__StreamEncoder seek callback. + */ +typedef enum { + + FLAC__STREAM_ENCODER_SEEK_STATUS_OK, + /**< The seek was OK and encoding can continue. */ + + FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR, + /**< An unrecoverable error occurred. */ + + FLAC__STREAM_ENCODER_SEEK_STATUS_UNSUPPORTED + /**< Client does not support seeking. */ + +} FLAC__StreamEncoderSeekStatus; + +/** Maps a FLAC__StreamEncoderSeekStatus to a C string. + * + * Using a FLAC__StreamEncoderSeekStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamEncoderSeekStatusString[]; + + +/** Return values for the FLAC__StreamEncoder tell callback. + */ +typedef enum { + + FLAC__STREAM_ENCODER_TELL_STATUS_OK, + /**< The tell was OK and encoding can continue. */ + + FLAC__STREAM_ENCODER_TELL_STATUS_ERROR, + /**< An unrecoverable error occurred. */ + + FLAC__STREAM_ENCODER_TELL_STATUS_UNSUPPORTED + /**< Client does not support seeking. */ + +} FLAC__StreamEncoderTellStatus; + +/** Maps a FLAC__StreamEncoderTellStatus to a C string. + * + * Using a FLAC__StreamEncoderTellStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamEncoderTellStatusString[]; + + +/*********************************************************************** + * + * class FLAC__StreamEncoder + * + ***********************************************************************/ + +struct FLAC__StreamEncoderProtected; +struct FLAC__StreamEncoderPrivate; +/** The opaque structure definition for the stream encoder type. + * See the \link flac_stream_encoder stream encoder module \endlink + * for a detailed description. + */ +typedef struct { + struct FLAC__StreamEncoderProtected *protected_; /* avoid the C++ keyword 'protected' */ + struct FLAC__StreamEncoderPrivate *private_; /* avoid the C++ keyword 'private' */ +} FLAC__StreamEncoder; + +/** Signature for the read callback. + * + * A function pointer matching this signature must be passed to + * FLAC__stream_encoder_init_ogg_stream() if seeking is supported. + * The supplied function will be called when the encoder needs to read back + * encoded data. This happens during the metadata callback, when the encoder + * has to read, modify, and rewrite the metadata (e.g. seekpoints) gathered + * while encoding. The address of the buffer to be filled is supplied, along + * with the number of bytes the buffer can hold. The callback may choose to + * supply less data and modify the byte count but must be careful not to + * overflow the buffer. The callback then returns a status code chosen from + * FLAC__StreamEncoderReadStatus. + * + * Here is an example of a read callback for stdio streams: + * \code + * FLAC__StreamEncoderReadStatus read_cb(const FLAC__StreamEncoder *encoder, FLAC__byte buffer[], size_t *bytes, void *client_data) + * { + * FILE *file = ((MyClientData*)client_data)->file; + * if(*bytes > 0) { + * *bytes = fread(buffer, sizeof(FLAC__byte), *bytes, file); + * if(ferror(file)) + * return FLAC__STREAM_ENCODER_READ_STATUS_ABORT; + * else if(*bytes == 0) + * return FLAC__STREAM_ENCODER_READ_STATUS_END_OF_STREAM; + * else + * return FLAC__STREAM_ENCODER_READ_STATUS_CONTINUE; + * } + * else + * return FLAC__STREAM_ENCODER_READ_STATUS_ABORT; + * } + * \endcode + * + * \note In general, FLAC__StreamEncoder functions which change the + * state should not be called on the \a encoder while in the callback. + * + * \param encoder The encoder instance calling the callback. + * \param buffer A pointer to a location for the callee to store + * data to be encoded. + * \param bytes A pointer to the size of the buffer. On entry + * to the callback, it contains the maximum number + * of bytes that may be stored in \a buffer. The + * callee must set it to the actual number of bytes + * stored (0 in case of error or end-of-stream) before + * returning. + * \param client_data The callee's client data set through + * FLAC__stream_encoder_set_client_data(). + * \retval FLAC__StreamEncoderReadStatus + * The callee's return status. + */ +typedef FLAC__StreamEncoderReadStatus (*FLAC__StreamEncoderReadCallback)(const FLAC__StreamEncoder *encoder, FLAC__byte buffer[], size_t *bytes, void *client_data); + +/** Signature for the write callback. + * + * A function pointer matching this signature must be passed to + * FLAC__stream_encoder_init*_stream(). The supplied function will be called + * by the encoder anytime there is raw encoded data ready to write. It may + * include metadata mixed with encoded audio frames and the data is not + * guaranteed to be aligned on frame or metadata block boundaries. + * + * The only duty of the callback is to write out the \a bytes worth of data + * in \a buffer to the current position in the output stream. The arguments + * \a samples and \a current_frame are purely informational. If \a samples + * is greater than \c 0, then \a current_frame will hold the current frame + * number that is being written; otherwise it indicates that the write + * callback is being called to write metadata. + * + * \note + * Unlike when writing to native FLAC, when writing to Ogg FLAC the + * write callback will be called twice when writing each audio + * frame; once for the page header, and once for the page body. + * When writing the page header, the \a samples argument to the + * write callback will be \c 0. + * + * \note In general, FLAC__StreamEncoder functions which change the + * state should not be called on the \a encoder while in the callback. + * + * \param encoder The encoder instance calling the callback. + * \param buffer An array of encoded data of length \a bytes. + * \param bytes The byte length of \a buffer. + * \param samples The number of samples encoded by \a buffer. + * \c 0 has a special meaning; see above. + * \param current_frame The number of the current frame being encoded. + * \param client_data The callee's client data set through + * FLAC__stream_encoder_init_*(). + * \retval FLAC__StreamEncoderWriteStatus + * The callee's return status. + */ +typedef FLAC__StreamEncoderWriteStatus (*FLAC__StreamEncoderWriteCallback)(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame, void *client_data); + +/** Signature for the seek callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_encoder_init*_stream(). The supplied function will be called + * when the encoder needs to seek the output stream. The encoder will pass + * the absolute byte offset to seek to, 0 meaning the beginning of the stream. + * + * Here is an example of a seek callback for stdio streams: + * \code + * FLAC__StreamEncoderSeekStatus seek_cb(const FLAC__StreamEncoder *encoder, FLAC__uint64 absolute_byte_offset, void *client_data) + * { + * FILE *file = ((MyClientData*)client_data)->file; + * if(file == stdin) + * return FLAC__STREAM_ENCODER_SEEK_STATUS_UNSUPPORTED; + * else if(fseeko(file, (off_t)absolute_byte_offset, SEEK_SET) < 0) + * return FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR; + * else + * return FLAC__STREAM_ENCODER_SEEK_STATUS_OK; + * } + * \endcode + * + * \note In general, FLAC__StreamEncoder functions which change the + * state should not be called on the \a encoder while in the callback. + * + * \param encoder The encoder instance calling the callback. + * \param absolute_byte_offset The offset from the beginning of the stream + * to seek to. + * \param client_data The callee's client data set through + * FLAC__stream_encoder_init_*(). + * \retval FLAC__StreamEncoderSeekStatus + * The callee's return status. + */ +typedef FLAC__StreamEncoderSeekStatus (*FLAC__StreamEncoderSeekCallback)(const FLAC__StreamEncoder *encoder, FLAC__uint64 absolute_byte_offset, void *client_data); + +/** Signature for the tell callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_encoder_init*_stream(). The supplied function will be called + * when the encoder needs to know the current position of the output stream. + * + * \warning + * The callback must return the true current byte offset of the output to + * which the encoder is writing. If you are buffering the output, make + * sure and take this into account. If you are writing directly to a + * FILE* from your write callback, ftell() is sufficient. If you are + * writing directly to a file descriptor from your write callback, you + * can use lseek(fd, SEEK_CUR, 0). The encoder may later seek back to + * these points to rewrite metadata after encoding. + * + * Here is an example of a tell callback for stdio streams: + * \code + * FLAC__StreamEncoderTellStatus tell_cb(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_byte_offset, void *client_data) + * { + * FILE *file = ((MyClientData*)client_data)->file; + * off_t pos; + * if(file == stdin) + * return FLAC__STREAM_ENCODER_TELL_STATUS_UNSUPPORTED; + * else if((pos = ftello(file)) < 0) + * return FLAC__STREAM_ENCODER_TELL_STATUS_ERROR; + * else { + * *absolute_byte_offset = (FLAC__uint64)pos; + * return FLAC__STREAM_ENCODER_TELL_STATUS_OK; + * } + * } + * \endcode + * + * \note In general, FLAC__StreamEncoder functions which change the + * state should not be called on the \a encoder while in the callback. + * + * \param encoder The encoder instance calling the callback. + * \param absolute_byte_offset The address at which to store the current + * position of the output. + * \param client_data The callee's client data set through + * FLAC__stream_encoder_init_*(). + * \retval FLAC__StreamEncoderTellStatus + * The callee's return status. + */ +typedef FLAC__StreamEncoderTellStatus (*FLAC__StreamEncoderTellCallback)(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_byte_offset, void *client_data); + +/** Signature for the metadata callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_encoder_init*_stream(). The supplied function will be called + * once at the end of encoding with the populated STREAMINFO structure. This + * is so the client can seek back to the beginning of the file and write the + * STREAMINFO block with the correct statistics after encoding (like + * minimum/maximum frame size and total samples). + * + * \note In general, FLAC__StreamEncoder functions which change the + * state should not be called on the \a encoder while in the callback. + * + * \param encoder The encoder instance calling the callback. + * \param metadata The final populated STREAMINFO block. + * \param client_data The callee's client data set through + * FLAC__stream_encoder_init_*(). + */ +typedef void (*FLAC__StreamEncoderMetadataCallback)(const FLAC__StreamEncoder *encoder, const FLAC__StreamMetadata *metadata, void *client_data); + +/** Signature for the progress callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_encoder_init*_file() or FLAC__stream_encoder_init*_FILE(). + * The supplied function will be called when the encoder has finished + * writing a frame. The \c total_frames_estimate argument to the + * callback will be based on the value from + * FLAC__stream_encoder_set_total_samples_estimate(). + * + * \note In general, FLAC__StreamEncoder functions which change the + * state should not be called on the \a encoder while in the callback. + * + * \param encoder The encoder instance calling the callback. + * \param bytes_written Bytes written so far. + * \param samples_written Samples written so far. + * \param frames_written Frames written so far. + * \param total_frames_estimate The estimate of the total number of + * frames to be written. + * \param client_data The callee's client data set through + * FLAC__stream_encoder_init_*(). + */ +typedef void (*FLAC__StreamEncoderProgressCallback)(const FLAC__StreamEncoder *encoder, FLAC__uint64 bytes_written, FLAC__uint64 samples_written, unsigned frames_written, unsigned total_frames_estimate, void *client_data); + + +/*********************************************************************** + * + * Class constructor/destructor + * + ***********************************************************************/ + +/** Create a new stream encoder instance. The instance is created with + * default settings; see the individual FLAC__stream_encoder_set_*() + * functions for each setting's default. + * + * \retval FLAC__StreamEncoder* + * \c NULL if there was an error allocating memory, else the new instance. + */ +FLAC_API FLAC__StreamEncoder *FLAC__stream_encoder_new(void); + +/** Free an encoder instance. Deletes the object pointed to by \a encoder. + * + * \param encoder A pointer to an existing encoder. + * \assert + * \code encoder != NULL \endcode + */ +FLAC_API void FLAC__stream_encoder_delete(FLAC__StreamEncoder *encoder); + + +/*********************************************************************** + * + * Public class method prototypes + * + ***********************************************************************/ + +/** Set the serial number for the FLAC stream to use in the Ogg container. + * + * \note + * This does not need to be set for native FLAC encoding. + * + * \note + * It is recommended to set a serial number explicitly as the default of '0' + * may collide with other streams. + * + * \default \c 0 + * \param encoder An encoder instance to set. + * \param serial_number See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_ogg_serial_number(FLAC__StreamEncoder *encoder, long serial_number); + +/** Set the "verify" flag. If \c true, the encoder will verify it's own + * encoded output by feeding it through an internal decoder and comparing + * the original signal against the decoded signal. If a mismatch occurs, + * the process call will return \c false. Note that this will slow the + * encoding process by the extra time required for decoding and comparison. + * + * \default \c false + * \param encoder An encoder instance to set. + * \param value Flag value (see above). + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_verify(FLAC__StreamEncoder *encoder, FLAC__bool value); + +/** Set the Subset flag. If \c true, + * the encoder will comply with the Subset and will check the + * settings during FLAC__stream_encoder_init_*() to see if all settings + * comply. If \c false, the settings may take advantage of the full + * range that the format allows. + * + * Make sure you know what it entails before setting this to \c false. + * + * \default \c true + * \param encoder An encoder instance to set. + * \param value Flag value (see above). + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_streamable_subset(FLAC__StreamEncoder *encoder, FLAC__bool value); + +/** Set the number of channels to be encoded. + * + * \default \c 2 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_channels(FLAC__StreamEncoder *encoder, unsigned value); + +/** Set the sample resolution of the input to be encoded. + * + * \warning + * Do not feed the encoder data that is wider than the value you + * set here or you will generate an invalid stream. + * + * \default \c 16 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_bits_per_sample(FLAC__StreamEncoder *encoder, unsigned value); + +/** Set the sample rate (in Hz) of the input to be encoded. + * + * \default \c 44100 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_sample_rate(FLAC__StreamEncoder *encoder, unsigned value); + +/** Set the compression level + * + * The compression level is roughly proportional to the amount of effort + * the encoder expends to compress the file. A higher level usually + * means more computation but higher compression. The default level is + * suitable for most applications. + * + * Currently the levels range from \c 0 (fastest, least compression) to + * \c 8 (slowest, most compression). A value larger than \c 8 will be + * treated as \c 8. + * + * This function automatically calls the following other \c _set_ + * functions with appropriate values, so the client does not need to + * unless it specifically wants to override them: + * - FLAC__stream_encoder_set_do_mid_side_stereo() + * - FLAC__stream_encoder_set_loose_mid_side_stereo() + * - FLAC__stream_encoder_set_apodization() + * - FLAC__stream_encoder_set_max_lpc_order() + * - FLAC__stream_encoder_set_qlp_coeff_precision() + * - FLAC__stream_encoder_set_do_qlp_coeff_prec_search() + * - FLAC__stream_encoder_set_do_escape_coding() + * - FLAC__stream_encoder_set_do_exhaustive_model_search() + * - FLAC__stream_encoder_set_min_residual_partition_order() + * - FLAC__stream_encoder_set_max_residual_partition_order() + * - FLAC__stream_encoder_set_rice_parameter_search_dist() + * + * The actual values set for each level are: + * + * + * + * + * + * + * + * + * + * + * + * + *
level + * do mid-side stereo + * loose mid-side stereo + * apodization + * max lpc order + * qlp coeff precision + * qlp coeff prec search + * escape coding + * exhaustive model search + * min residual partition order + * max residual partition order + * rice parameter search dist + *
0 false false tukey(0.5) 0 0 false false false 0 3 0
1 true true tukey(0.5) 0 0 false false false 0 3 0
2 true false tukey(0.5) 0 0 false false false 0 3 0
3 false false tukey(0.5) 6 0 false false false 0 4 0
4 true true tukey(0.5) 8 0 false false false 0 4 0
5 true false tukey(0.5) 8 0 false false false 0 5 0
6 true false tukey(0.5) 8 0 false false false 0 6 0
7 true false tukey(0.5) 8 0 false false true 0 6 0
8 true false tukey(0.5) 12 0 false false true 0 6 0
+ * + * \default \c 5 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_compression_level(FLAC__StreamEncoder *encoder, unsigned value); + +/** Set the blocksize to use while encoding. + * + * The number of samples to use per frame. Use \c 0 to let the encoder + * estimate a blocksize; this is usually best. + * + * \default \c 0 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_blocksize(FLAC__StreamEncoder *encoder, unsigned value); + +/** Set to \c true to enable mid-side encoding on stereo input. The + * number of channels must be 2 for this to have any effect. Set to + * \c false to use only independent channel coding. + * + * \default \c false + * \param encoder An encoder instance to set. + * \param value Flag value (see above). + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_do_mid_side_stereo(FLAC__StreamEncoder *encoder, FLAC__bool value); + +/** Set to \c true to enable adaptive switching between mid-side and + * left-right encoding on stereo input. Set to \c false to use + * exhaustive searching. Setting this to \c true requires + * FLAC__stream_encoder_set_do_mid_side_stereo() to also be set to + * \c true in order to have any effect. + * + * \default \c false + * \param encoder An encoder instance to set. + * \param value Flag value (see above). + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_loose_mid_side_stereo(FLAC__StreamEncoder *encoder, FLAC__bool value); + +/* @@@@add to unit tests*/ +/** Sets the apodization function(s) the encoder will use when windowing + * audio data for LPC analysis. + * + * The \a specification is a plain ASCII string which specifies exactly + * which functions to use. There may be more than one (up to 32), + * separated by \c ';' characters. Some functions take one or more + * comma-separated arguments in parentheses. + * + * The available functions are \c bartlett, \c bartlett_hann, + * \c blackman, \c blackman_harris_4term_92db, \c connes, \c flattop, + * \c gauss(STDDEV), \c hamming, \c hann, \c kaiser_bessel, \c nuttall, + * \c rectangle, \c triangle, \c tukey(P), \c welch. + * + * For \c gauss(STDDEV), STDDEV specifies the standard deviation + * (0blocksize / (2 ^ order). + * + * Set both min and max values to \c 0 to force a single context, + * whose Rice parameter is based on the residual signal variance. + * Otherwise, set a min and max order, and the encoder will search + * all orders, using the mean of each context for its Rice parameter, + * and use the best. + * + * \default \c 0 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_min_residual_partition_order(FLAC__StreamEncoder *encoder, unsigned value); + +/** Set the maximum partition order to search when coding the residual. + * This is used in tandem with + * FLAC__stream_encoder_set_min_residual_partition_order(). + * + * The partition order determines the context size in the residual. + * The context size will be approximately blocksize / (2 ^ order). + * + * Set both min and max values to \c 0 to force a single context, + * whose Rice parameter is based on the residual signal variance. + * Otherwise, set a min and max order, and the encoder will search + * all orders, using the mean of each context for its Rice parameter, + * and use the best. + * + * \default \c 0 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_max_residual_partition_order(FLAC__StreamEncoder *encoder, unsigned value); + +/** Deprecated. Setting this value has no effect. + * + * \default \c 0 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_rice_parameter_search_dist(FLAC__StreamEncoder *encoder, unsigned value); + +/** Set an estimate of the total samples that will be encoded. + * This is merely an estimate and may be set to \c 0 if unknown. + * This value will be written to the STREAMINFO block before encoding, + * and can remove the need for the caller to rewrite the value later + * if the value is known before encoding. + * + * \default \c 0 + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_total_samples_estimate(FLAC__StreamEncoder *encoder, FLAC__uint64 value); + +/** Set the metadata blocks to be emitted to the stream before encoding. + * A value of \c NULL, \c 0 implies no metadata; otherwise, supply an + * array of pointers to metadata blocks. The array is non-const since + * the encoder may need to change the \a is_last flag inside them, and + * in some cases update seek point offsets. Otherwise, the encoder will + * not modify or free the blocks. It is up to the caller to free the + * metadata blocks after encoding finishes. + * + * \note + * The encoder stores only copies of the pointers in the \a metadata array; + * the metadata blocks themselves must survive at least until after + * FLAC__stream_encoder_finish() returns. Do not free the blocks until then. + * + * \note + * The STREAMINFO block is always written and no STREAMINFO block may + * occur in the supplied array. + * + * \note + * By default the encoder does not create a SEEKTABLE. If one is supplied + * in the \a metadata array, but the client has specified that it does not + * support seeking, then the SEEKTABLE will be written verbatim. However + * by itself this is not very useful as the client will not know the stream + * offsets for the seekpoints ahead of time. In order to get a proper + * seektable the client must support seeking. See next note. + * + * \note + * SEEKTABLE blocks are handled specially. Since you will not know + * the values for the seek point stream offsets, you should pass in + * a SEEKTABLE 'template', that is, a SEEKTABLE object with the + * required sample numbers (or placeholder points), with \c 0 for the + * \a frame_samples and \a stream_offset fields for each point. If the + * client has specified that it supports seeking by providing a seek + * callback to FLAC__stream_encoder_init_stream() or both seek AND read + * callback to FLAC__stream_encoder_init_ogg_stream() (or by using + * FLAC__stream_encoder_init*_file() or FLAC__stream_encoder_init*_FILE()), + * then while it is encoding the encoder will fill the stream offsets in + * for you and when encoding is finished, it will seek back and write the + * real values into the SEEKTABLE block in the stream. There are helper + * routines for manipulating seektable template blocks; see metadata.h: + * FLAC__metadata_object_seektable_template_*(). If the client does + * not support seeking, the SEEKTABLE will have inaccurate offsets which + * will slow down or remove the ability to seek in the FLAC stream. + * + * \note + * The encoder instance \b will modify the first \c SEEKTABLE block + * as it transforms the template to a valid seektable while encoding, + * but it is still up to the caller to free all metadata blocks after + * encoding. + * + * \note + * A VORBIS_COMMENT block may be supplied. The vendor string in it + * will be ignored. libFLAC will use it's own vendor string. libFLAC + * will not modify the passed-in VORBIS_COMMENT's vendor string, it + * will simply write it's own into the stream. If no VORBIS_COMMENT + * block is present in the \a metadata array, libFLAC will write an + * empty one, containing only the vendor string. + * + * \note The Ogg FLAC mapping requires that the VORBIS_COMMENT block be + * the second metadata block of the stream. The encoder already supplies + * the STREAMINFO block automatically. If \a metadata does not contain a + * VORBIS_COMMENT block, the encoder will supply that too. Otherwise, if + * \a metadata does contain a VORBIS_COMMENT block and it is not the + * first, the init function will reorder \a metadata by moving the + * VORBIS_COMMENT block to the front; the relative ordering of the other + * blocks will remain as they were. + * + * \note The Ogg FLAC mapping limits the number of metadata blocks per + * stream to \c 65535. If \a num_blocks exceeds this the function will + * return \c false. + * + * \default \c NULL, 0 + * \param encoder An encoder instance to set. + * \param metadata See above. + * \param num_blocks See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + * \c false if the encoder is already initialized, or if + * \a num_blocks > 65535 if encoding to Ogg FLAC, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_set_metadata(FLAC__StreamEncoder *encoder, FLAC__StreamMetadata **metadata, unsigned num_blocks); + +/** Get the current encoder state. + * + * \param encoder An encoder instance to query. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__StreamEncoderState + * The current encoder state. + */ +FLAC_API FLAC__StreamEncoderState FLAC__stream_encoder_get_state(const FLAC__StreamEncoder *encoder); + +/** Get the state of the verify stream decoder. + * Useful when the stream encoder state is + * \c FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR. + * + * \param encoder An encoder instance to query. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__StreamDecoderState + * The verify stream decoder state. + */ +FLAC_API FLAC__StreamDecoderState FLAC__stream_encoder_get_verify_decoder_state(const FLAC__StreamEncoder *encoder); + +/** Get the current encoder state as a C string. + * This version automatically resolves + * \c FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR by getting the + * verify decoder's state. + * + * \param encoder A encoder instance to query. + * \assert + * \code encoder != NULL \endcode + * \retval const char * + * The encoder state as a C string. Do not modify the contents. + */ +FLAC_API const char *FLAC__stream_encoder_get_resolved_state_string(const FLAC__StreamEncoder *encoder); + +/** Get relevant values about the nature of a verify decoder error. + * Useful when the stream encoder state is + * \c FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR. The arguments should + * be addresses in which the stats will be returned, or NULL if value + * is not desired. + * + * \param encoder An encoder instance to query. + * \param absolute_sample The absolute sample number of the mismatch. + * \param frame_number The number of the frame in which the mismatch occurred. + * \param channel The channel in which the mismatch occurred. + * \param sample The number of the sample (relative to the frame) in + * which the mismatch occurred. + * \param expected The expected value for the sample in question. + * \param got The actual value returned by the decoder. + * \assert + * \code encoder != NULL \endcode + */ +FLAC_API void FLAC__stream_encoder_get_verify_decoder_error_stats(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_sample, unsigned *frame_number, unsigned *channel, unsigned *sample, FLAC__int32 *expected, FLAC__int32 *got); + +/** Get the "verify" flag. + * + * \param encoder An encoder instance to query. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * See FLAC__stream_encoder_set_verify(). + */ +FLAC_API FLAC__bool FLAC__stream_encoder_get_verify(const FLAC__StreamEncoder *encoder); + +/** Get the frame header. + * + * \param encoder An initialized encoder instance in the OK state. + * \param buffer An array of pointers to each channel's signal. + * \param samples The number of samples in one channel. + * \assert + * \code encoder != NULL \endcode + * \code FLAC__stream_encoder_get_state(encoder) == FLAC__STREAM_ENCODER_OK \endcode + * \retval FLAC__bool + * \c true if successful, else \c false; in this case, check the + * encoder state with FLAC__stream_encoder_get_state() to see what + * went wrong. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_process(FLAC__StreamEncoder *encoder, const FLAC__int32 * const buffer[], unsigned samples); + +/** Submit data for encoding. + * This version allows you to supply the input data where the channels + * are interleaved into a single array (i.e. channel0_sample0, + * channel1_sample0, ... , channelN_sample0, channel0_sample1, ...). + * The samples need not be block-aligned but they must be + * sample-aligned, i.e. the first value should be channel0_sample0 + * and the last value channelN_sampleM. Each sample should be a signed + * integer, right-justified to the resolution set by + * FLAC__stream_encoder_set_bits_per_sample(). For example, if the + * resolution is 16 bits per sample, the samples should all be in the + * range [-32768,32767]. + * + * For applications where channel order is important, channels must + * follow the order as described in the + * frame header. + * + * \param encoder An initialized encoder instance in the OK state. + * \param buffer An array of channel-interleaved data (see above). + * \param samples The number of samples in one channel, the same as for + * FLAC__stream_encoder_process(). For example, if + * encoding two channels, \c 1000 \a samples corresponds + * to a \a buffer of 2000 values. + * \assert + * \code encoder != NULL \endcode + * \code FLAC__stream_encoder_get_state(encoder) == FLAC__STREAM_ENCODER_OK \endcode + * \retval FLAC__bool + * \c true if successful, else \c false; in this case, check the + * encoder state with FLAC__stream_encoder_get_state() to see what + * went wrong. + */ +FLAC_API FLAC__bool FLAC__stream_encoder_process_interleaved(FLAC__StreamEncoder *encoder, const FLAC__int32 buffer[], unsigned samples); + +/* \} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/FLAC/include/Makefile.am b/src/FLAC/include/Makefile.am new file mode 100644 index 00000000..2015f83f --- /dev/null +++ b/src/FLAC/include/Makefile.am @@ -0,0 +1,18 @@ +# FLAC - Free Lossless Audio Codec +# Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson +# +# This file is part the FLAC project. FLAC is comprised of several +# components distributed under difference licenses. The codec libraries +# are distributed under Xiph.Org's BSD-like license (see the file +# COPYING.Xiph in this distribution). All other programs, libraries, and +# plugins are distributed under the GPL (see COPYING.GPL). The documentation +# is distributed under the Gnu FDL (see COPYING.FDL). Each file in the +# FLAC distribution contains at the top the terms under which it may be +# distributed. +# +# Since this particular file is relevant to all components of FLAC, +# it may be distributed under the Xiph.Org license, which is the least +# restrictive of those mentioned above. See the file COPYING.Xiph in this +# distribution. + +SUBDIRS = FLAC share test_libs_common diff --git a/src/FLAC/include/share/Makefile.am b/src/FLAC/include/share/Makefile.am new file mode 100644 index 00000000..b50d883d --- /dev/null +++ b/src/FLAC/include/share/Makefile.am @@ -0,0 +1,12 @@ +## Process this file with automake to produce Makefile.in + +AUTOMAKE_OPTIONS = foreign + +SUBDIRS = grabbag + +EXTRA_DIST = \ + getopt.h \ + grabbag.h \ + replaygain_analysis.h \ + replaygain_synthesis.h \ + utf8.h diff --git a/src/FLAC/include/share/getopt.h b/src/FLAC/include/share/getopt.h new file mode 100644 index 00000000..5b976913 --- /dev/null +++ b/src/FLAC/include/share/getopt.h @@ -0,0 +1,184 @@ +/* + NOTE: + I cannot get the vanilla getopt code to work (i.e. compile only what + is needed and not duplicate symbols found in the standard library) + on all the platforms that FLAC supports. In particular the gating + of code with the ELIDE_CODE #define is not accurate enough on systems + that are POSIX but not glibc. If someone has a patch that works on + GNU/Linux, Darwin, AND Solaris please submit it on the project page: + http://sourceforge.net/projects/flac + + In the meantime I have munged the global symbols and removed gates + around code, while at the same time trying to touch the original as + little as possible. +*/ +/* 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 SHARE__GETOPT_H +#define SHARE__GETOPT_H + +/*[JEC] was:#ifndef __need_getopt*/ +/*[JEC] was:# define _GETOPT_H 1*/ +/*[JEC] was:#endif*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/* For communication from `share__getopt' to the caller. + When `share__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 *share__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 `share__getopt'. + + On entry to `share__getopt', zero means this is the first call; initialize. + + When `share__getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `share__optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int share__optind; + +/* Callers store zero here to inhibit the error message `share__getopt' prints + for unrecognized options. */ + +extern int share__opterr; + +/* Set to an option character which was unrecognized. */ + +extern int share__optopt; + +/*[JEC] was:#ifndef __need_getopt */ +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to share__getopt_long or share__getopt_long_only is a vector + of `struct share__option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + share__no_argument (or 0) if the option does not take an argument, + share__required_argument (or 1) if the option requires an argument, + share__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 `share__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, `share__getopt' + returns the contents of the `val' field. */ + +struct share__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 share__option'. */ + +# define share__no_argument 0 +# define share__required_argument 1 +# define share__optional_argument 2 +/*[JEC] was:#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, `share__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 `share__optarg'. + + If a letter in OPTS is followed by two colons, its argument is + optional. This behavior is specific to the GNU `share__getopt'. + + The argument `--' causes premature termination of argument + scanning, explicitly telling `share__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 + `share__getopt'. */ + +/*[JEC] was:#if defined __STDC__ && __STDC__*/ +/*[JEC] was:# 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 share__getopt (int argc, char **argv, const char *shortopts); +/*[JEC] was:# else*/ /* not __GNU_LIBRARY__ */ +/*[JEC] was:extern int getopt ();*/ +/*[JEC] was:# endif*/ /* __GNU_LIBRARY__ */ + +/*[JEC] was:# ifndef __need_getopt*/ +extern int share__getopt_long (int argc, char **argv, const char *shortopts, + const struct share__option *longopts, int *longind); +extern int share__getopt_long_only (int argc, char **argv, + const char *shortopts, + const struct share__option *longopts, int *longind); + +/* Internal only. Users should not call this directly. */ +extern int share___getopt_internal (int argc, char **argv, + const char *shortopts, + const struct share__option *longopts, int *longind, + int long_only); +/*[JEC] was:# endif*/ +/*[JEC] was:#else*/ /* not __STDC__ */ +/*[JEC] was:extern int getopt ();*/ +/*[JEC] was:# ifndef __need_getopt*/ +/*[JEC] was:extern int getopt_long ();*/ +/*[JEC] was:extern int getopt_long_only ();*/ + +/*[JEC] was:extern int _getopt_internal ();*/ +/*[JEC] was:# endif*/ +/*[JEC] was:#endif*/ /* __STDC__ */ + +#ifdef __cplusplus +} +#endif + +/* Make sure we later can get all the definitions and declarations. */ +/*[JEC] was:#undef __need_getopt*/ + +#endif /* getopt.h */ diff --git a/src/FLAC/include/share/grabbag.h b/src/FLAC/include/share/grabbag.h new file mode 100644 index 00000000..42c6998e --- /dev/null +++ b/src/FLAC/include/share/grabbag.h @@ -0,0 +1,29 @@ +/* grabbag - Convenience lib for various routines common to several tools + * Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef SHARE__GRABBAG_H +#define SHARE__GRABBAG_H + +/* These can't be included by themselves, only from within grabbag.h */ +#include "grabbag/cuesheet.h" +#include "grabbag/file.h" +#include "grabbag/picture.h" +#include "grabbag/replaygain.h" +#include "grabbag/seektable.h" + +#endif diff --git a/src/FLAC/include/share/grabbag/Makefile.am b/src/FLAC/include/share/grabbag/Makefile.am new file mode 100644 index 00000000..0d520aa5 --- /dev/null +++ b/src/FLAC/include/share/grabbag/Makefile.am @@ -0,0 +1,10 @@ +## Process this file with automake to produce Makefile.in + +AUTOMAKE_OPTIONS = foreign + +EXTRA_DIST = \ + cuesheet.h \ + file.h \ + picture.h \ + replaygain.h \ + seektable.h diff --git a/src/FLAC/include/share/grabbag/cuesheet.h b/src/FLAC/include/share/grabbag/cuesheet.h new file mode 100644 index 00000000..698d49bf --- /dev/null +++ b/src/FLAC/include/share/grabbag/cuesheet.h @@ -0,0 +1,42 @@ +/* grabbag - Convenience lib for various routines common to several tools + * Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* This .h cannot be included by itself; #include "share/grabbag.h" instead. */ + +#ifndef GRABBAG__CUESHEET_H +#define GRABBAG__CUESHEET_H + +#include +#include "FLAC/metadata.h" + +#ifdef __cplusplus +extern "C" { +#endif + +unsigned grabbag__cuesheet_msf_to_frame(unsigned minutes, unsigned seconds, unsigned frames); +void grabbag__cuesheet_frame_to_msf(unsigned frame, unsigned *minutes, unsigned *seconds, unsigned *frames); + +FLAC__StreamMetadata *grabbag__cuesheet_parse(FILE *file, const char **error_message, unsigned *last_line_read, FLAC__bool is_cdda, FLAC__uint64 lead_out_offset); + +void grabbag__cuesheet_emit(FILE *file, const FLAC__StreamMetadata *cuesheet, const char *file_reference); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/FLAC/include/share/grabbag/file.h b/src/FLAC/include/share/grabbag/file.h new file mode 100644 index 00000000..c8b149f8 --- /dev/null +++ b/src/FLAC/include/share/grabbag/file.h @@ -0,0 +1,63 @@ +/* grabbag - Convenience lib for various routines common to several tools + * Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* Convenience routines for manipulating files */ + +/* This .h cannot be included by itself; #include "share/grabbag.h" instead. */ + +#ifndef GRABAG__FILE_H +#define GRABAG__FILE_H + +/* needed because of off_t */ +#if HAVE_CONFIG_H +# include +#endif + +#include /* for off_t */ +#include /* for FILE */ +#include "FLAC/ordinals.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void grabbag__file_copy_metadata(const char *srcpath, const char *destpath); +off_t grabbag__file_get_filesize(const char *srcpath); +const char *grabbag__file_get_basename(const char *srcpath); + +/* read_only == false means "make file writable by user" + * read_only == true means "make file read-only for everyone" + */ +FLAC__bool grabbag__file_change_stats(const char *filename, FLAC__bool read_only); + +/* returns true iff stat() succeeds for both files and they have the same device and inode. */ +/* on windows, uses GetFileInformationByHandle() to compare */ +FLAC__bool grabbag__file_are_same(const char *f1, const char *f2); + +/* attempts to make writable before unlinking */ +FLAC__bool grabbag__file_remove_file(const char *filename); + +/* these will forcibly set stdin/stdout to binary mode (for OSes that require it) */ +FILE *grabbag__file_get_binary_stdin(void); +FILE *grabbag__file_get_binary_stdout(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/FLAC/include/share/grabbag/picture.h b/src/FLAC/include/share/grabbag/picture.h new file mode 100644 index 00000000..e7f42788 --- /dev/null +++ b/src/FLAC/include/share/grabbag/picture.h @@ -0,0 +1,46 @@ +/* grabbag - Convenience lib for various routines common to several tools + * Copyright (C) 2006,2007 Josh Coalson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* This .h cannot be included by itself; #include "share/grabbag.h" instead. */ + +#ifndef GRABBAG__PICTURE_H +#define GRABBAG__PICTURE_H + +#include "FLAC/metadata.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* spec should be of the form "[TYPE]|MIME_TYPE|[DESCRIPTION]|[WIDTHxHEIGHTxDEPTH[/COLORS]]|FILE", e.g. + * "|image/jpeg|||cover.jpg" + * "4|image/jpeg||300x300x24|backcover.jpg" + * "|image/png|description|300x300x24/71|cover.png" + * "-->|image/gif||300x300x24/71|http://blah.blah.blah/cover.gif" + * + * empty type means default to FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER + * empty resolution spec means to get from the file (cannot get used with "-->" linked images) + * spec and error_message must not be NULL + */ +FLAC__StreamMetadata *grabbag__picture_parse_specification(const char *spec, const char **error_message); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/FLAC/include/share/grabbag/replaygain.h b/src/FLAC/include/share/grabbag/replaygain.h new file mode 100644 index 00000000..ea8c9359 --- /dev/null +++ b/src/FLAC/include/share/grabbag/replaygain.h @@ -0,0 +1,72 @@ +/* grabbag - Convenience lib for various routines common to several tools + * Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * This wraps the replaygain_analysis lib, which is LGPL. This wrapper + * allows analysis of different input resolutions by automatically + * scaling the input signal + */ + +/* This .h cannot be included by itself; #include "share/grabbag.h" instead. */ + +#ifndef GRABBAG__REPLAYGAIN_H +#define GRABBAG__REPLAYGAIN_H + +#include "FLAC/metadata.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const unsigned GRABBAG__REPLAYGAIN_MAX_TAG_SPACE_REQUIRED; + +extern const FLAC__byte * const GRABBAG__REPLAYGAIN_TAG_REFERENCE_LOUDNESS; /* = "REPLAYGAIN_REFERENCE_LOUDNESS" */ +extern const FLAC__byte * const GRABBAG__REPLAYGAIN_TAG_TITLE_GAIN; /* = "REPLAYGAIN_TRACK_GAIN" */ +extern const FLAC__byte * const GRABBAG__REPLAYGAIN_TAG_TITLE_PEAK; /* = "REPLAYGAIN_TRACK_PEAK" */ +extern const FLAC__byte * const GRABBAG__REPLAYGAIN_TAG_ALBUM_GAIN; /* = "REPLAYGAIN_ALBUM_GAIN" */ +extern const FLAC__byte * const GRABBAG__REPLAYGAIN_TAG_ALBUM_PEAK; /* = "REPLAYGAIN_ALBUM_PEAK" */ + +FLAC__bool grabbag__replaygain_is_valid_sample_frequency(unsigned sample_frequency); + +FLAC__bool grabbag__replaygain_init(unsigned sample_frequency); + +/* 'bps' must be valid for FLAC, i.e. >=4 and <= 32 */ +FLAC__bool grabbag__replaygain_analyze(const FLAC__int32 * const input[], FLAC__bool is_stereo, unsigned bps, unsigned samples); + +void grabbag__replaygain_get_album(float *gain, float *peak); +void grabbag__replaygain_get_title(float *gain, float *peak); + +/* These three functions return an error string on error, or NULL if successful */ +const char *grabbag__replaygain_analyze_file(const char *filename, float *title_gain, float *title_peak); +const char *grabbag__replaygain_store_to_vorbiscomment(FLAC__StreamMetadata *block, float album_gain, float album_peak, float title_gain, float title_peak); +const char *grabbag__replaygain_store_to_vorbiscomment_reference(FLAC__StreamMetadata *block); +const char *grabbag__replaygain_store_to_vorbiscomment_album(FLAC__StreamMetadata *block, float album_gain, float album_peak); +const char *grabbag__replaygain_store_to_vorbiscomment_title(FLAC__StreamMetadata *block, float title_gain, float title_peak); +const char *grabbag__replaygain_store_to_file(const char *filename, float album_gain, float album_peak, float title_gain, float title_peak, FLAC__bool preserve_modtime); +const char *grabbag__replaygain_store_to_file_reference(const char *filename, FLAC__bool preserve_modtime); +const char *grabbag__replaygain_store_to_file_album(const char *filename, float album_gain, float album_peak, FLAC__bool preserve_modtime); +const char *grabbag__replaygain_store_to_file_title(const char *filename, float title_gain, float title_peak, FLAC__bool preserve_modtime); + +FLAC__bool grabbag__replaygain_load_from_vorbiscomment(const FLAC__StreamMetadata *block, FLAC__bool album_mode, FLAC__bool strict, double *reference, double *gain, double *peak); +double grabbag__replaygain_compute_scale_factor(double peak, double gain, double preamp, FLAC__bool prevent_clipping); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/FLAC/include/share/grabbag/seektable.h b/src/FLAC/include/share/grabbag/seektable.h new file mode 100644 index 00000000..8010fc94 --- /dev/null +++ b/src/FLAC/include/share/grabbag/seektable.h @@ -0,0 +1,38 @@ +/* grabbag - Convenience lib for various routines common to several tools + * Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* Convenience routines for working with seek tables */ + +/* This .h cannot be included by itself; #include "share/grabbag.h" instead. */ + +#ifndef GRABAG__SEEKTABLE_H +#define GRABAG__SEEKTABLE_H + +#include "FLAC/format.h" + +#ifdef __cplusplus +extern "C" { +#endif + +FLAC__bool grabbag__seektable_convert_specification_to_template(const char *spec, FLAC__bool only_explicit_placeholders, FLAC__uint64 total_samples_to_encode, unsigned sample_rate, FLAC__StreamMetadata *seektable_template, FLAC__bool *spec_has_real_points); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/FLAC/include/share/replaygain_analysis.h b/src/FLAC/include/share/replaygain_analysis.h new file mode 100644 index 00000000..02067d21 --- /dev/null +++ b/src/FLAC/include/share/replaygain_analysis.h @@ -0,0 +1,59 @@ +/* + * 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 (glensawyer@hotmail.com) 442 N 700 E, Provo, UT 84606 USA + * -- blame him if you think this runs too slowly, or the coding is otherwise flawed + * minor cosmetic tweaks to integrate with FLAC by Josh Coalson + * + * 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 float Float_t; /* Type used for filtering */ + +extern Float_t ReplayGainReferenceLoudness; /* in dB SPL, currently == 89.0 */ + +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/src/FLAC/include/share/replaygain_synthesis.h b/src/FLAC/include/share/replaygain_synthesis.h new file mode 100644 index 00000000..ca1ae641 --- /dev/null +++ b/src/FLAC/include/share/replaygain_synthesis.h @@ -0,0 +1,51 @@ +/* replaygain_synthesis - Routines for applying ReplayGain to a signal + * Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef FLAC__SHARE__REPLAYGAIN_SYNTHESIS_H +#define FLAC__SHARE__REPLAYGAIN_SYNTHESIS_H + +#include /* for size_t */ +#include "FLAC/ordinals.h" + +#define FLAC_SHARE__MAX_SUPPORTED_CHANNELS 2 + +typedef enum { + NOISE_SHAPING_NONE = 0, + NOISE_SHAPING_LOW = 1, + NOISE_SHAPING_MEDIUM = 2, + NOISE_SHAPING_HIGH = 3 +} NoiseShaping; + +typedef struct { + const float* FilterCoeff; + FLAC__uint64 Mask; + double Add; + float Dither; + float ErrorHistory [FLAC_SHARE__MAX_SUPPORTED_CHANNELS] [16]; /* 16th order Noise shaping */ + float DitherHistory [FLAC_SHARE__MAX_SUPPORTED_CHANNELS] [16]; + int LastRandomNumber [FLAC_SHARE__MAX_SUPPORTED_CHANNELS]; + unsigned LastHistoryIndex; + NoiseShaping ShapingType; +} DitherContext; + +void FLAC__replaygain_synthesis__init_dither_context(DitherContext *dither, int bits, int shapingtype); + +/* scale = (float) pow(10., (double)replaygain * 0.05); */ +size_t FLAC__replaygain_synthesis__apply_gain(FLAC__byte *data_out, FLAC__bool little_endian_data_out, FLAC__bool unsigned_data_out, const FLAC__int32 * const input[], unsigned wide_samples, unsigned channels, const unsigned source_bps, const unsigned target_bps, const double scale, const FLAC__bool hard_limit, FLAC__bool do_dithering, DitherContext *dither_context); + +#endif diff --git a/src/FLAC/include/share/utf8.h b/src/FLAC/include/share/utf8.h new file mode 100644 index 00000000..7d6650d6 --- /dev/null +++ b/src/FLAC/include/share/utf8.h @@ -0,0 +1,25 @@ +#ifndef SHARE__UTF8_H +#define SHARE__UTF8_H + +/* + * Convert a string between UTF-8 and the locale's charset. + * Invalid bytes are replaced by '#', and characters that are + * not available in the target encoding are replaced by '?'. + * + * If the locale's charset is not set explicitly then it is + * obtained using nl_langinfo(CODESET), where available, the + * environment variable CHARSET, or assumed to be US-ASCII. + * + * Return value of conversion functions: + * + * -1 : memory allocation failed + * 0 : data was converted exactly + * 1 : valid data was converted approximately (using '?') + * 2 : input was invalid (but still converted, using '#') + * 3 : unknown encoding (but still converted, using '?') + */ + +int utf8_encode(const char *from, char **to); +int utf8_decode(const char *from, char **to); + +#endif diff --git a/src/FLAC/include/test_libs_common/Makefile.am b/src/FLAC/include/test_libs_common/Makefile.am new file mode 100644 index 00000000..a0342dc1 --- /dev/null +++ b/src/FLAC/include/test_libs_common/Makefile.am @@ -0,0 +1,7 @@ +## Process this file with automake to produce Makefile.in + +AUTOMAKE_OPTIONS = foreign + +EXTRA_DIST = \ + file_utils_flac.h \ + metadata_utils.h diff --git a/src/FLAC/include/test_libs_common/file_utils_flac.h b/src/FLAC/include/test_libs_common/file_utils_flac.h new file mode 100644 index 00000000..27e3671a --- /dev/null +++ b/src/FLAC/include/test_libs_common/file_utils_flac.h @@ -0,0 +1,34 @@ +/* test_libFLAC - Unit tester for libFLAC + * Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef FLAC__TEST_LIBFLAC_FILE_UTILS_H +#define FLAC__TEST_LIBFLAC_FILE_UTILS_H + +/* needed because of off_t */ +#if HAVE_CONFIG_H +# include +#endif + +#include "FLAC/format.h" +#include /* for off_t */ + +extern const long file_utils__ogg_serial_number; + +FLAC__bool file_utils__generate_flacfile(FLAC__bool is_ogg, const char *output_filename, off_t *output_filesize, unsigned length, const FLAC__StreamMetadata *streaminfo, FLAC__StreamMetadata **metadata, unsigned num_metadata); + +#endif diff --git a/src/FLAC/include/test_libs_common/metadata_utils.h b/src/FLAC/include/test_libs_common/metadata_utils.h new file mode 100644 index 00000000..933bece7 --- /dev/null +++ b/src/FLAC/include/test_libs_common/metadata_utils.h @@ -0,0 +1,73 @@ +/* test_libFLAC - Unit tester for libFLAC + * Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef FLAC__TEST_LIBFLAC_METADATA_UTILS_H +#define FLAC__TEST_LIBFLAC_METADATA_UTILS_H + +/* + * These are not tests, just utility functions used by the metadata tests + */ + +#include "FLAC/format.h" +#include +#include /* for malloc() */ +#include /* for memcmp() */ + +FLAC__bool mutils__compare_block_data_streaminfo(const FLAC__StreamMetadata_StreamInfo *block, const FLAC__StreamMetadata_StreamInfo *blockcopy); + +FLAC__bool mutils__compare_block_data_padding(const FLAC__StreamMetadata_Padding *block, const FLAC__StreamMetadata_Padding *blockcopy, unsigned block_length); + +FLAC__bool mutils__compare_block_data_application(const FLAC__StreamMetadata_Application *block, const FLAC__StreamMetadata_Application *blockcopy, unsigned block_length); + +FLAC__bool mutils__compare_block_data_seektable(const FLAC__StreamMetadata_SeekTable *block, const FLAC__StreamMetadata_SeekTable *blockcopy); + +FLAC__bool mutils__compare_block_data_vorbiscomment(const FLAC__StreamMetadata_VorbisComment *block, const FLAC__StreamMetadata_VorbisComment *blockcopy); + +FLAC__bool mutils__compare_block_data_cuesheet(const FLAC__StreamMetadata_CueSheet *block, const FLAC__StreamMetadata_CueSheet *blockcopy); + +FLAC__bool mutils__compare_block_data_picture(const FLAC__StreamMetadata_Picture *block, const FLAC__StreamMetadata_Picture *blockcopy); + +FLAC__bool mutils__compare_block_data_unknown(const FLAC__StreamMetadata_Unknown *block, const FLAC__StreamMetadata_Unknown *blockcopy, unsigned block_length); + +FLAC__bool mutils__compare_block(const FLAC__StreamMetadata *block, const FLAC__StreamMetadata *blockcopy); + +void mutils__init_metadata_blocks( + FLAC__StreamMetadata *streaminfo, + FLAC__StreamMetadata *padding, + FLAC__StreamMetadata *seektable, + FLAC__StreamMetadata *application1, + FLAC__StreamMetadata *application2, + FLAC__StreamMetadata *vorbiscomment, + FLAC__StreamMetadata *cuesheet, + FLAC__StreamMetadata *picture, + FLAC__StreamMetadata *unknown +); + +void mutils__free_metadata_blocks( + FLAC__StreamMetadata *streaminfo, + FLAC__StreamMetadata *padding, + FLAC__StreamMetadata *seektable, + FLAC__StreamMetadata *application1, + FLAC__StreamMetadata *application2, + FLAC__StreamMetadata *vorbiscomment, + FLAC__StreamMetadata *cuesheet, + FLAC__StreamMetadata *picture, + FLAC__StreamMetadata *unknown +); + +#endif diff --git a/src/FLAC/src/Makefile.am b/src/FLAC/src/Makefile.am new file mode 100644 index 00000000..ec715bb3 --- /dev/null +++ b/src/FLAC/src/Makefile.am @@ -0,0 +1,27 @@ +# FLAC - Free Lossless Audio Codec +# Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson +# +# This file is part the FLAC project. FLAC is comprised of several +# components distributed under difference licenses. The codec libraries +# are distributed under Xiph.Org's BSD-like license (see the file +# COPYING.Xiph in this distribution). All other programs, libraries, and +# plugins are distributed under the GPL (see COPYING.GPL). The documentation +# is distributed under the Gnu FDL (see COPYING.FDL). Each file in the +# FLAC distribution contains at the top the terms under which it may be +# distributed. +# +# Since this particular file is relevant to all components of FLAC, +# it may be distributed under the Xiph.Org license, which is the least +# restrictive of those mentioned above. See the file COPYING.Xiph in this +# distribution. + +SUBDIRS = \ + libFLAC \ + share \ + monkeys_audio_utilities \ + test_grabbag \ + test_libs_common \ + test_libFLAC \ + test_seeking \ + test_streams + diff --git a/src/FLAC/src/libFLAC/Makefile.am b/src/FLAC/src/libFLAC/Makefile.am new file mode 100644 index 00000000..cfae71ca --- /dev/null +++ b/src/FLAC/src/libFLAC/Makefile.am @@ -0,0 +1,92 @@ +# libFLAC - Free Lossless Audio Codec library +# Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# - Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# - Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# - Neither the name of the Xiph.org Foundation nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +noinst_LTLIBRARIES = libFLAC.la + +INCLUDES = -I$(top_srcdir)/src -I$(top_srcdir)/src/FLAC/src/libFLAC/include \ + -I$(top_srcdir)/src/FLAC/include -I$(top_srcdir)/src/OGG/include + +if FLaC__CPU_PPC +# The -force_cpusubtype_ALL is needed to insert a ppc64 instruction +# into cpu.c with an asm(). +if FLaC__SYS_DARWIN +#@@@ PPC optimizations temporarily disabled +CPUCFLAGS = -faltivec -force_cpusubtype_ALL -DFLAC__NO_ASM +else +# Linux-gcc for PPC does not have -force_cpusubtype_ALL, it is Darwin-specific +#@@@ PPC optimizations temporarily disabled +CPUCFLAGS = -maltivec -mabi=altivec -DFLAC__NO_ASM +endif +endif + +AM_CFLAGS = $(DEBUGCFLAGS) $(CPUCFLAGS) + +if FLaC__NO_ASM +else +if FLaC__CPU_IA32 +if FLaC__HAS_NASM +ARCH_SUBDIRS = ia32 +LOCAL_EXTRA_LIBADD = $(top_builddir)/src/FLAC/src/libFLAC/ia32/libFLAC-asm.la +endif +endif +if FLaC__CPU_PPC +ARCH_SUBDIRS = ppc +endif +endif + +libFLAC_la_LIBADD = $(LOCAL_EXTRA_LIBADD) + +SUBDIRS = $(ARCH_SUBDIRS) include . + +extra_ogg_sources = \ + ogg_decoder_aspect.c \ + ogg_encoder_aspect.c \ + ogg_helper.c \ + ogg_mapping.c + +libFLAC_la_SOURCES = \ + bitmath.c \ + bitreader.c \ + bitwriter.c \ + cpu.c \ + crc.c \ + fixed.c \ + float.c \ + format.c \ + lpc.c \ + md5.c \ + memory.c \ + metadata_iterators.c \ + metadata_object.c \ + stream_decoder.c \ + stream_encoder.c \ + stream_encoder_framing.c \ + window.c \ + $(extra_ogg_sources) diff --git a/src/FLAC/src/libFLAC/bitmath.c b/src/FLAC/src/libFLAC/bitmath.c new file mode 100644 index 00000000..27e25f0f --- /dev/null +++ b/src/FLAC/src/libFLAC/bitmath.c @@ -0,0 +1,149 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include "private/bitmath.h" +#include "FLAC/assert.h" + +/* An example of what FLAC__bitmath_ilog2() computes: + * + * ilog2( 0) = assertion failure + * ilog2( 1) = 0 + * ilog2( 2) = 1 + * ilog2( 3) = 1 + * ilog2( 4) = 2 + * ilog2( 5) = 2 + * ilog2( 6) = 2 + * ilog2( 7) = 2 + * ilog2( 8) = 3 + * ilog2( 9) = 3 + * ilog2(10) = 3 + * ilog2(11) = 3 + * ilog2(12) = 3 + * ilog2(13) = 3 + * ilog2(14) = 3 + * ilog2(15) = 3 + * ilog2(16) = 4 + * ilog2(17) = 4 + * ilog2(18) = 4 + */ +unsigned FLAC__bitmath_ilog2(FLAC__uint32 v) +{ + unsigned l = 0; + FLAC__ASSERT(v > 0); + while(v >>= 1) + l++; + return l; +} + +unsigned FLAC__bitmath_ilog2_wide(FLAC__uint64 v) +{ + unsigned l = 0; + FLAC__ASSERT(v > 0); + while(v >>= 1) + l++; + return l; +} + +/* An example of what FLAC__bitmath_silog2() computes: + * + * silog2(-10) = 5 + * silog2(- 9) = 5 + * silog2(- 8) = 4 + * silog2(- 7) = 4 + * silog2(- 6) = 4 + * silog2(- 5) = 4 + * silog2(- 4) = 3 + * silog2(- 3) = 3 + * silog2(- 2) = 2 + * silog2(- 1) = 2 + * silog2( 0) = 0 + * silog2( 1) = 2 + * silog2( 2) = 3 + * silog2( 3) = 3 + * silog2( 4) = 4 + * silog2( 5) = 4 + * silog2( 6) = 4 + * silog2( 7) = 4 + * silog2( 8) = 5 + * silog2( 9) = 5 + * silog2( 10) = 5 + */ +unsigned FLAC__bitmath_silog2(int v) +{ + while(1) { + if(v == 0) { + return 0; + } + else if(v > 0) { + unsigned l = 0; + while(v) { + l++; + v >>= 1; + } + return l+1; + } + else if(v == -1) { + return 2; + } + else { + v++; + v = -v; + } + } +} + +unsigned FLAC__bitmath_silog2_wide(FLAC__int64 v) +{ + while(1) { + if(v == 0) { + return 0; + } + else if(v > 0) { + unsigned l = 0; + while(v) { + l++; + v >>= 1; + } + return l+1; + } + else if(v == -1) { + return 2; + } + else { + v++; + v = -v; + } + } +} diff --git a/src/FLAC/src/libFLAC/bitreader.c b/src/FLAC/src/libFLAC/bitreader.c new file mode 100644 index 00000000..e3f85b85 --- /dev/null +++ b/src/FLAC/src/libFLAC/bitreader.c @@ -0,0 +1,1363 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include /* for malloc() */ +#include /* for memcpy(), memset() */ + +#include + + +#include "private/bitmath.h" +#include "private/bitreader.h" +#include "private/crc.h" +#include "FLAC/assert.h" + +/* Things should be fastest when this matches the machine word size */ +/* WATCHOUT: if you change this you must also change the following #defines down to COUNT_ZERO_MSBS below to match */ +/* WATCHOUT: there are a few places where the code will not work unless brword is >= 32 bits wide */ +/* also, some sections currently only have fast versions for 4 or 8 bytes per word */ +typedef FLAC__uint32 brword; +#define FLAC__BYTES_PER_WORD 4 +#define FLAC__BITS_PER_WORD 32 +#define FLAC__WORD_ALL_ONES ((FLAC__uint32)0xffffffff) +/* SWAP_BE_WORD_TO_HOST swaps bytes in a brword (which is always big-endian) if necessary to match host byte order */ +#define SWAP_BE_WORD_TO_HOST(x) BEI2H_INT(x) + +/* counts the # of zero MSBs in a word */ +#define COUNT_ZERO_MSBS(word) ( \ + (word) <= 0xffff ? \ + ( (word) <= 0xff? byte_to_unary_table[word] + 24 : byte_to_unary_table[(word) >> 8] + 16 ) : \ + ( (word) <= 0xffffff? byte_to_unary_table[word >> 16] + 8 : byte_to_unary_table[(word) >> 24] ) \ +) +/* this alternate might be slightly faster on some systems/compilers: */ +#define COUNT_ZERO_MSBS2(word) ( (word) <= 0xff ? byte_to_unary_table[word] + 24 : ((word) <= 0xffff ? byte_to_unary_table[(word) >> 8] + 16 : ((word) <= 0xffffff ? byte_to_unary_table[(word) >> 16] + 8 : byte_to_unary_table[(word) >> 24])) ) + + +/* + * This should be at least twice as large as the largest number of words + * required to represent any 'number' (in any encoding) you are going to + * read. With FLAC this is on the order of maybe a few hundred bits. + * If the buffer is smaller than that, the decoder won't be able to read + * in a whole number that is in a variable length encoding (e.g. Rice). + * But to be practical it should be at least 1K bytes. + * + * Increase this number to decrease the number of read callbacks, at the + * expense of using more memory. Or decrease for the reverse effect, + * keeping in mind the limit from the first paragraph. The optimal size + * also depends on the CPU cache size and other factors; some twiddling + * may be necessary to squeeze out the best performance. + */ +static const unsigned FLAC__BITREADER_DEFAULT_CAPACITY = 65536u / FLAC__BITS_PER_WORD; /* in words */ + +static const unsigned char byte_to_unary_table[] = { + 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +#ifdef min +#undef min +#endif +#define min(x,y) ((x)<(y)?(x):(y)) +#ifdef max +#undef max +#endif +#define max(x,y) ((x)>(y)?(x):(y)) + +/* adjust for compilers that can't understand using LLU suffix for uint64_t literals */ +#ifdef _MSC_VER +#define FLAC__U64L(x) x +#else +#define FLAC__U64L(x) x##LLU +#endif + +#ifndef FLaC__INLINE +#define FLaC__INLINE +#endif + +/* WATCHOUT: assembly routines rely on the order in which these fields are declared */ +struct FLAC__BitReader { + /* any partially-consumed word at the head will stay right-justified as bits are consumed from the left */ + /* any incomplete word at the tail will be left-justified, and bytes from the read callback are added on the right */ + brword *buffer; + unsigned capacity; /* in words */ + unsigned words; /* # of completed words in buffer */ + unsigned bytes; /* # of bytes in incomplete word at buffer[words] */ + unsigned consumed_words; /* #words ... */ + unsigned consumed_bits; /* ... + (#bits of head word) already consumed from the front of buffer */ + unsigned read_crc16; /* the running frame CRC */ + unsigned crc16_align; /* the number of bits in the current consumed word that should not be CRC'd */ + FLAC__BitReaderReadCallback read_callback; + void *client_data; + FLAC__CPUInfo cpu_info; +}; + +#ifdef _MSC_VER +/* OPT: an MSVC built-in would be better */ +static _inline FLAC__uint32 local_swap32_(FLAC__uint32 x) +{ + x = ((x<<8)&0xFF00FF00) | ((x>>8)&0x00FF00FF); + return (x>>16) | (x<<16); +} +static void local_swap32_block_(FLAC__uint32 *start, FLAC__uint32 len) +{ + __asm { + mov edx, start + mov ecx, len + test ecx, ecx +loop1: + jz done1 + mov eax, [edx] + bswap eax + mov [edx], eax + add edx, 4 + dec ecx + jmp short loop1 +done1: + } +} +#endif + +static FLaC__INLINE void crc16_update_word_(FLAC__BitReader *br, brword word) +{ + register unsigned crc = br->read_crc16; +#if FLAC__BYTES_PER_WORD == 4 + switch(br->crc16_align) { + case 0: crc = FLAC__CRC16_UPDATE((unsigned)(word >> 24), crc); + case 8: crc = FLAC__CRC16_UPDATE((unsigned)((word >> 16) & 0xff), crc); + case 16: crc = FLAC__CRC16_UPDATE((unsigned)((word >> 8) & 0xff), crc); + case 24: br->read_crc16 = FLAC__CRC16_UPDATE((unsigned)(word & 0xff), crc); + } +#elif FLAC__BYTES_PER_WORD == 8 + switch(br->crc16_align) { + case 0: crc = FLAC__CRC16_UPDATE((unsigned)(word >> 56), crc); + case 8: crc = FLAC__CRC16_UPDATE((unsigned)((word >> 48) & 0xff), crc); + case 16: crc = FLAC__CRC16_UPDATE((unsigned)((word >> 40) & 0xff), crc); + case 24: crc = FLAC__CRC16_UPDATE((unsigned)((word >> 32) & 0xff), crc); + case 32: crc = FLAC__CRC16_UPDATE((unsigned)((word >> 24) & 0xff), crc); + case 40: crc = FLAC__CRC16_UPDATE((unsigned)((word >> 16) & 0xff), crc); + case 48: crc = FLAC__CRC16_UPDATE((unsigned)((word >> 8) & 0xff), crc); + case 56: br->read_crc16 = FLAC__CRC16_UPDATE((unsigned)(word & 0xff), crc); + } +#else + for( ; br->crc16_align < FLAC__BITS_PER_WORD; br->crc16_align += 8) + crc = FLAC__CRC16_UPDATE((unsigned)((word >> (FLAC__BITS_PER_WORD-8-br->crc16_align)) & 0xff), crc); + br->read_crc16 = crc; +#endif + br->crc16_align = 0; +} + +/* would be static except it needs to be called by asm routines */ +FLAC__bool bitreader_read_from_client_(FLAC__BitReader *br) +{ + unsigned start, end; + size_t bytes; + FLAC__byte *target; + + /* first shift the unconsumed buffer data toward the front as much as possible */ + if(br->consumed_words > 0) { + start = br->consumed_words; + end = br->words + (br->bytes? 1:0); + memmove(br->buffer, br->buffer+start, FLAC__BYTES_PER_WORD * (end - start)); + + br->words -= start; + br->consumed_words = 0; + } + + /* + * set the target for reading, taking into account word alignment and endianness + */ + bytes = (br->capacity - br->words) * FLAC__BYTES_PER_WORD - br->bytes; + if(bytes == 0) + return false; /* no space left, buffer is too small; see note for FLAC__BITREADER_DEFAULT_CAPACITY */ + target = ((FLAC__byte*)(br->buffer+br->words)) + br->bytes; + + /* before reading, if the existing reader looks like this (say brword is 32 bits wide) + * bitstream : 11 22 33 44 55 br->words=1 br->bytes=1 (partial tail word is left-justified) + * buffer[BE]: 11 22 33 44 55 ?? ?? ?? (shown layed out as bytes sequentially in memory) + * buffer[LE]: 44 33 22 11 ?? ?? ?? 55 (?? being don't-care) + * ^^-------target, bytes=3 + * on LE machines, have to byteswap the odd tail word so nothing is + * overwritten: + */ +#if WORDS_BIGENDIAN +#else + if(br->bytes) + br->buffer[br->words] = SWAP_BE_WORD_TO_HOST(br->buffer[br->words]); +#endif + + /* now it looks like: + * bitstream : 11 22 33 44 55 br->words=1 br->bytes=1 + * buffer[BE]: 11 22 33 44 55 ?? ?? ?? + * buffer[LE]: 44 33 22 11 55 ?? ?? ?? + * ^^-------target, bytes=3 + */ + + /* read in the data; note that the callback may return a smaller number of bytes */ + if(!br->read_callback(target, &bytes, br->client_data)) + return false; + + /* after reading bytes 66 77 88 99 AA BB CC DD EE FF from the client: + * bitstream : 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF + * buffer[BE]: 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF ?? + * buffer[LE]: 44 33 22 11 55 66 77 88 99 AA BB CC DD EE FF ?? + * now have to byteswap on LE machines: + */ +#if WORDS_BIGENDIAN +#else + end = (br->words*FLAC__BYTES_PER_WORD + br->bytes + bytes + (FLAC__BYTES_PER_WORD-1)) / FLAC__BYTES_PER_WORD; +# if defined(_MSC_VER) && (FLAC__BYTES_PER_WORD == 4) + if(br->cpu_info.type == FLAC__CPUINFO_TYPE_IA32 && br->cpu_info.data.ia32.bswap) { + start = br->words; + local_swap32_block_(br->buffer + start, end - start); + } + else +# endif + for(start = br->words; start < end; start++) + br->buffer[start] = SWAP_BE_WORD_TO_HOST(br->buffer[start]); +#endif + + /* now it looks like: + * bitstream : 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF + * buffer[BE]: 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF ?? + * buffer[LE]: 44 33 22 11 88 77 66 55 CC BB AA 99 ?? FF EE DD + * finally we'll update the reader values: + */ + end = br->words*FLAC__BYTES_PER_WORD + br->bytes + bytes; + br->words = end / FLAC__BYTES_PER_WORD; + br->bytes = end % FLAC__BYTES_PER_WORD; + + return true; +} + +/*********************************************************************** + * + * Class constructor/destructor + * + ***********************************************************************/ + +FLAC__BitReader *FLAC__bitreader_new(void) +{ + FLAC__BitReader *br = (FLAC__BitReader*)calloc(1, sizeof(FLAC__BitReader)); + + /* calloc() implies: + memset(br, 0, sizeof(FLAC__BitReader)); + br->buffer = 0; + br->capacity = 0; + br->words = br->bytes = 0; + br->consumed_words = br->consumed_bits = 0; + br->read_callback = 0; + br->client_data = 0; + */ + return br; +} + +void FLAC__bitreader_delete(FLAC__BitReader *br) +{ + FLAC__ASSERT(0 != br); + + FLAC__bitreader_free(br); + free(br); +} + +/*********************************************************************** + * + * Public class methods + * + ***********************************************************************/ + +FLAC__bool FLAC__bitreader_init(FLAC__BitReader *br, FLAC__CPUInfo cpu, FLAC__BitReaderReadCallback rcb, void *cd) +{ + FLAC__ASSERT(0 != br); + + br->words = br->bytes = 0; + br->consumed_words = br->consumed_bits = 0; + br->capacity = FLAC__BITREADER_DEFAULT_CAPACITY; + br->buffer = (brword*)malloc(sizeof(brword) * br->capacity); + if(br->buffer == 0) + return false; + br->read_callback = rcb; + br->client_data = cd; + br->cpu_info = cpu; + + return true; +} + +void FLAC__bitreader_free(FLAC__BitReader *br) +{ + FLAC__ASSERT(0 != br); + + if(0 != br->buffer) + free(br->buffer); + br->buffer = 0; + br->capacity = 0; + br->words = br->bytes = 0; + br->consumed_words = br->consumed_bits = 0; + br->read_callback = 0; + br->client_data = 0; +} + +FLAC__bool FLAC__bitreader_clear(FLAC__BitReader *br) +{ + br->words = br->bytes = 0; + br->consumed_words = br->consumed_bits = 0; + return true; +} + +void FLAC__bitreader_dump(const FLAC__BitReader *br, FILE *out) +{ + unsigned i, j; + if(br == 0) { + fprintf(out, "bitreader is NULL\n"); + } + else { + fprintf(out, "bitreader: capacity=%u words=%u bytes=%u consumed: words=%u, bits=%u\n", br->capacity, br->words, br->bytes, br->consumed_words, br->consumed_bits); + + for(i = 0; i < br->words; i++) { + fprintf(out, "%08X: ", i); + for(j = 0; j < FLAC__BITS_PER_WORD; j++) + if(i < br->consumed_words || (i == br->consumed_words && j < br->consumed_bits)) + fprintf(out, "."); + else + fprintf(out, "%01u", br->buffer[i] & (1 << (FLAC__BITS_PER_WORD-j-1)) ? 1:0); + fprintf(out, "\n"); + } + if(br->bytes > 0) { + fprintf(out, "%08X: ", i); + for(j = 0; j < br->bytes*8; j++) + if(i < br->consumed_words || (i == br->consumed_words && j < br->consumed_bits)) + fprintf(out, "."); + else + fprintf(out, "%01u", br->buffer[i] & (1 << (br->bytes*8-j-1)) ? 1:0); + fprintf(out, "\n"); + } + } +} + +void FLAC__bitreader_reset_read_crc16(FLAC__BitReader *br, FLAC__uint16 seed) +{ + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + FLAC__ASSERT((br->consumed_bits & 7) == 0); + + br->read_crc16 = (unsigned)seed; + br->crc16_align = br->consumed_bits; +} + +FLAC__uint16 FLAC__bitreader_get_read_crc16(FLAC__BitReader *br) +{ + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + FLAC__ASSERT((br->consumed_bits & 7) == 0); + FLAC__ASSERT(br->crc16_align <= br->consumed_bits); + + /* CRC any tail bytes in a partially-consumed word */ + if(br->consumed_bits) { + const brword tail = br->buffer[br->consumed_words]; + for( ; br->crc16_align < br->consumed_bits; br->crc16_align += 8) + br->read_crc16 = FLAC__CRC16_UPDATE((unsigned)((tail >> (FLAC__BITS_PER_WORD-8-br->crc16_align)) & 0xff), br->read_crc16); + } + return br->read_crc16; +} + +FLaC__INLINE FLAC__bool FLAC__bitreader_is_consumed_byte_aligned(const FLAC__BitReader *br) +{ + return ((br->consumed_bits & 7) == 0); +} + +FLaC__INLINE unsigned FLAC__bitreader_bits_left_for_byte_alignment(const FLAC__BitReader *br) +{ + return 8 - (br->consumed_bits & 7); +} + +FLaC__INLINE unsigned FLAC__bitreader_get_input_bits_unconsumed(const FLAC__BitReader *br) +{ + return (br->words-br->consumed_words)*FLAC__BITS_PER_WORD + br->bytes*8 - br->consumed_bits; +} + +FLaC__INLINE FLAC__bool FLAC__bitreader_read_raw_uint32(FLAC__BitReader *br, FLAC__uint32 *val, unsigned bits) +{ + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + + FLAC__ASSERT(bits <= 32); + FLAC__ASSERT((br->capacity*FLAC__BITS_PER_WORD) * 2 >= bits); + FLAC__ASSERT(br->consumed_words <= br->words); + + /* WATCHOUT: code does not work with <32bit words; we can make things much faster with this assertion */ + FLAC__ASSERT(FLAC__BITS_PER_WORD >= 32); + + if(bits == 0) { /* OPT: investigate if this can ever happen, maybe change to assertion */ + *val = 0; + return true; + } + + while((br->words-br->consumed_words)*FLAC__BITS_PER_WORD + br->bytes*8 - br->consumed_bits < bits) { + if(!bitreader_read_from_client_(br)) + return false; + } + if(br->consumed_words < br->words) { /* if we've not consumed up to a partial tail word... */ + /* OPT: taking out the consumed_bits==0 "else" case below might make things faster if less code allows the compiler to inline this function */ + if(br->consumed_bits) { + /* this also works when consumed_bits==0, it's just a little slower than necessary for that case */ + const unsigned n = FLAC__BITS_PER_WORD - br->consumed_bits; + const brword word = br->buffer[br->consumed_words]; + if(bits < n) { + *val = (word & (FLAC__WORD_ALL_ONES >> br->consumed_bits)) >> (n-bits); + br->consumed_bits += bits; + return true; + } + *val = word & (FLAC__WORD_ALL_ONES >> br->consumed_bits); + bits -= n; + crc16_update_word_(br, word); + br->consumed_words++; + br->consumed_bits = 0; + if(bits) { /* if there are still bits left to read, there have to be less than 32 so they will all be in the next word */ + *val <<= bits; + *val |= (br->buffer[br->consumed_words] >> (FLAC__BITS_PER_WORD-bits)); + br->consumed_bits = bits; + } + return true; + } + else { + const brword word = br->buffer[br->consumed_words]; + if(bits < FLAC__BITS_PER_WORD) { + *val = word >> (FLAC__BITS_PER_WORD-bits); + br->consumed_bits = bits; + return true; + } + /* at this point 'bits' must be == FLAC__BITS_PER_WORD; because of previous assertions, it can't be larger */ + *val = word; + crc16_update_word_(br, word); + br->consumed_words++; + return true; + } + } + else { + /* in this case we're starting our read at a partial tail word; + * the reader has guaranteed that we have at least 'bits' bits + * available to read, which makes this case simpler. + */ + /* OPT: taking out the consumed_bits==0 "else" case below might make things faster if less code allows the compiler to inline this function */ + if(br->consumed_bits) { + /* this also works when consumed_bits==0, it's just a little slower than necessary for that case */ + FLAC__ASSERT(br->consumed_bits + bits <= br->bytes*8); + *val = (br->buffer[br->consumed_words] & (FLAC__WORD_ALL_ONES >> br->consumed_bits)) >> (FLAC__BITS_PER_WORD-br->consumed_bits-bits); + br->consumed_bits += bits; + return true; + } + else { + *val = br->buffer[br->consumed_words] >> (FLAC__BITS_PER_WORD-bits); + br->consumed_bits += bits; + return true; + } + } +} + +FLAC__bool FLAC__bitreader_read_raw_int32(FLAC__BitReader *br, FLAC__int32 *val, unsigned bits) +{ + /* OPT: inline raw uint32 code here, or make into a macro if possible in the .h file */ + if(!FLAC__bitreader_read_raw_uint32(br, (FLAC__uint32*)val, bits)) + return false; + /* sign-extend: */ + *val <<= (32-bits); + *val >>= (32-bits); + return true; +} + +FLAC__bool FLAC__bitreader_read_raw_uint64(FLAC__BitReader *br, FLAC__uint64 *val, unsigned bits) +{ + FLAC__uint32 hi, lo; + + if(bits > 32) { + if(!FLAC__bitreader_read_raw_uint32(br, &hi, bits-32)) + return false; + if(!FLAC__bitreader_read_raw_uint32(br, &lo, 32)) + return false; + *val = hi; + *val <<= 32; + *val |= lo; + } + else { + if(!FLAC__bitreader_read_raw_uint32(br, &lo, bits)) + return false; + *val = lo; + } + return true; +} + +FLaC__INLINE FLAC__bool FLAC__bitreader_read_uint32_little_endian(FLAC__BitReader *br, FLAC__uint32 *val) +{ + FLAC__uint32 x8, x32 = 0; + + /* this doesn't need to be that fast as currently it is only used for vorbis comments */ + + if(!FLAC__bitreader_read_raw_uint32(br, &x32, 8)) + return false; + + if(!FLAC__bitreader_read_raw_uint32(br, &x8, 8)) + return false; + x32 |= (x8 << 8); + + if(!FLAC__bitreader_read_raw_uint32(br, &x8, 8)) + return false; + x32 |= (x8 << 16); + + if(!FLAC__bitreader_read_raw_uint32(br, &x8, 8)) + return false; + x32 |= (x8 << 24); + + *val = x32; + return true; +} + +FLAC__bool FLAC__bitreader_skip_bits_no_crc(FLAC__BitReader *br, unsigned bits) +{ + /* + * OPT: a faster implementation is possible but probably not that useful + * since this is only called a couple of times in the metadata readers. + */ + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + + if(bits > 0) { + const unsigned n = br->consumed_bits & 7; + unsigned m; + FLAC__uint32 x; + + if(n != 0) { + m = min(8-n, bits); + if(!FLAC__bitreader_read_raw_uint32(br, &x, m)) + return false; + bits -= m; + } + m = bits / 8; + if(m > 0) { + if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(br, m)) + return false; + bits %= 8; + } + if(bits > 0) { + if(!FLAC__bitreader_read_raw_uint32(br, &x, bits)) + return false; + } + } + + return true; +} + +FLAC__bool FLAC__bitreader_skip_byte_block_aligned_no_crc(FLAC__BitReader *br, unsigned nvals) +{ + FLAC__uint32 x; + + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(br)); + + /* step 1: skip over partial head word to get word aligned */ + while(nvals && br->consumed_bits) { /* i.e. run until we read 'nvals' bytes or we hit the end of the head word */ + if(!FLAC__bitreader_read_raw_uint32(br, &x, 8)) + return false; + nvals--; + } + if(0 == nvals) + return true; + /* step 2: skip whole words in chunks */ + while(nvals >= FLAC__BYTES_PER_WORD) { + if(br->consumed_words < br->words) { + br->consumed_words++; + nvals -= FLAC__BYTES_PER_WORD; + } + else if(!bitreader_read_from_client_(br)) + return false; + } + /* step 3: skip any remainder from partial tail bytes */ + while(nvals) { + if(!FLAC__bitreader_read_raw_uint32(br, &x, 8)) + return false; + nvals--; + } + + return true; +} + +FLAC__bool FLAC__bitreader_read_byte_block_aligned_no_crc(FLAC__BitReader *br, FLAC__byte *val, unsigned nvals) +{ + FLAC__uint32 x; + + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(br)); + + /* step 1: read from partial head word to get word aligned */ + while(nvals && br->consumed_bits) { /* i.e. run until we read 'nvals' bytes or we hit the end of the head word */ + if(!FLAC__bitreader_read_raw_uint32(br, &x, 8)) + return false; + *val++ = (FLAC__byte)x; + nvals--; + } + if(0 == nvals) + return true; + /* step 2: read whole words in chunks */ + while(nvals >= FLAC__BYTES_PER_WORD) { + if(br->consumed_words < br->words) { + const brword word = br->buffer[br->consumed_words++]; +#if FLAC__BYTES_PER_WORD == 4 + val[0] = (FLAC__byte)(word >> 24); + val[1] = (FLAC__byte)(word >> 16); + val[2] = (FLAC__byte)(word >> 8); + val[3] = (FLAC__byte)word; +#elif FLAC__BYTES_PER_WORD == 8 + val[0] = (FLAC__byte)(word >> 56); + val[1] = (FLAC__byte)(word >> 48); + val[2] = (FLAC__byte)(word >> 40); + val[3] = (FLAC__byte)(word >> 32); + val[4] = (FLAC__byte)(word >> 24); + val[5] = (FLAC__byte)(word >> 16); + val[6] = (FLAC__byte)(word >> 8); + val[7] = (FLAC__byte)word; +#else + for(x = 0; x < FLAC__BYTES_PER_WORD; x++) + val[x] = (FLAC__byte)(word >> (8*(FLAC__BYTES_PER_WORD-x-1))); +#endif + val += FLAC__BYTES_PER_WORD; + nvals -= FLAC__BYTES_PER_WORD; + } + else if(!bitreader_read_from_client_(br)) + return false; + } + /* step 3: read any remainder from partial tail bytes */ + while(nvals) { + if(!FLAC__bitreader_read_raw_uint32(br, &x, 8)) + return false; + *val++ = (FLAC__byte)x; + nvals--; + } + + return true; +} + +FLaC__INLINE FLAC__bool FLAC__bitreader_read_unary_unsigned(FLAC__BitReader *br, unsigned *val) +#if 0 /* slow but readable version */ +{ + unsigned bit; + + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + + *val = 0; + while(1) { + if(!FLAC__bitreader_read_bit(br, &bit)) + return false; + if(bit) + break; + else + *val++; + } + return true; +} +#else +{ + unsigned i; + + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + + *val = 0; + while(1) { + while(br->consumed_words < br->words) { /* if we've not consumed up to a partial tail word... */ + brword b = br->buffer[br->consumed_words] << br->consumed_bits; + if(b) { + i = COUNT_ZERO_MSBS(b); + *val += i; + i++; + br->consumed_bits += i; + if(br->consumed_bits >= FLAC__BITS_PER_WORD) { /* faster way of testing if(br->consumed_bits == FLAC__BITS_PER_WORD) */ + crc16_update_word_(br, br->buffer[br->consumed_words]); + br->consumed_words++; + br->consumed_bits = 0; + } + return true; + } + else { + *val += FLAC__BITS_PER_WORD - br->consumed_bits; + crc16_update_word_(br, br->buffer[br->consumed_words]); + br->consumed_words++; + br->consumed_bits = 0; + /* didn't find stop bit yet, have to keep going... */ + } + } + /* at this point we've eaten up all the whole words; have to try + * reading through any tail bytes before calling the read callback. + * this is a repeat of the above logic adjusted for the fact we + * don't have a whole word. note though if the client is feeding + * us data a byte at a time (unlikely), br->consumed_bits may not + * be zero. + */ + if(br->bytes) { + const unsigned end = br->bytes * 8; + brword b = (br->buffer[br->consumed_words] & (FLAC__WORD_ALL_ONES << (FLAC__BITS_PER_WORD-end))) << br->consumed_bits; + if(b) { + i = COUNT_ZERO_MSBS(b); + *val += i; + i++; + br->consumed_bits += i; + FLAC__ASSERT(br->consumed_bits < FLAC__BITS_PER_WORD); + return true; + } + else { + *val += end - br->consumed_bits; + br->consumed_bits += end; + FLAC__ASSERT(br->consumed_bits < FLAC__BITS_PER_WORD); + /* didn't find stop bit yet, have to keep going... */ + } + } + if(!bitreader_read_from_client_(br)) + return false; + } +} +#endif + +FLAC__bool FLAC__bitreader_read_rice_signed(FLAC__BitReader *br, int *val, unsigned parameter) +{ + FLAC__uint32 lsbs = 0, msbs = 0; + unsigned uval; + + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + FLAC__ASSERT(parameter <= 31); + + /* read the unary MSBs and end bit */ + if(!FLAC__bitreader_read_unary_unsigned(br, &msbs)) + return false; + + /* read the binary LSBs */ + if(!FLAC__bitreader_read_raw_uint32(br, &lsbs, parameter)) + return false; + + /* compose the value */ + uval = (msbs << parameter) | lsbs; + if(uval & 1) + *val = -((int)(uval >> 1)) - 1; + else + *val = (int)(uval >> 1); + + return true; +} + +/* this is by far the most heavily used reader call. it ain't pretty but it's fast */ +/* a lot of the logic is copied, then adapted, from FLAC__bitreader_read_unary_unsigned() and FLAC__bitreader_read_raw_uint32() */ +FLAC__bool FLAC__bitreader_read_rice_signed_block(FLAC__BitReader *br, int vals[], unsigned nvals, unsigned parameter) +#ifdef _MSC_VER +{ + unsigned i; + unsigned uval = 0; + unsigned bits; /* the # of binary LSBs left to read to finish a rice codeword */ + + /* try and get br->consumed_words and br->consumed_bits into register; + * must remember to flush them back to *br before calling other + * bitwriter functions that use them, and before returning */ + register unsigned cwords; + register unsigned cbits; + + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + /* WATCHOUT: code does not work with <32bit words; we can make things much faster with this assertion */ + FLAC__ASSERT(FLAC__BITS_PER_WORD >= 32); + FLAC__ASSERT(parameter < 32); + /* the above two asserts also guarantee that the binary part never straddles more that 2 words, so we don't have to loop to read it */ + + if(nvals == 0) + return true; + + cbits = br->consumed_bits; + cwords = br->consumed_words; + + while(1) { + + /* read unary part */ + while(1) { + while(cwords < br->words) { /* if we've not consumed up to a partial tail word... */ + brword b = br->buffer[cwords] << cbits; + if(b) { +#if 0 /* slower, probably due to bad register allocation... */ && defined FLAC__CPU_IA32 && !defined FLAC__NO_ASM && FLAC__BITS_PER_WORD == 32 + __asm { + bsr eax, b + not eax + and eax, 31 + mov i, eax + } +#else + i = COUNT_ZERO_MSBS(b); +#endif + uval += i; + bits = parameter; + i++; + cbits += i; + if(cbits == FLAC__BITS_PER_WORD) { + crc16_update_word_(br, br->buffer[cwords]); + cwords++; + cbits = 0; + } + goto break1; + } + else { + uval += FLAC__BITS_PER_WORD - cbits; + crc16_update_word_(br, br->buffer[cwords]); + cwords++; + cbits = 0; + /* didn't find stop bit yet, have to keep going... */ + } + } + /* at this point we've eaten up all the whole words; have to try + * reading through any tail bytes before calling the read callback. + * this is a repeat of the above logic adjusted for the fact we + * don't have a whole word. note though if the client is feeding + * us data a byte at a time (unlikely), br->consumed_bits may not + * be zero. + */ + if(br->bytes) { + const unsigned end = br->bytes * 8; + brword b = (br->buffer[cwords] & (FLAC__WORD_ALL_ONES << (FLAC__BITS_PER_WORD-end))) << cbits; + if(b) { + i = COUNT_ZERO_MSBS(b); + uval += i; + bits = parameter; + i++; + cbits += i; + FLAC__ASSERT(cbits < FLAC__BITS_PER_WORD); + goto break1; + } + else { + uval += end - cbits; + cbits += end; + FLAC__ASSERT(cbits < FLAC__BITS_PER_WORD); + /* didn't find stop bit yet, have to keep going... */ + } + } + /* flush registers and read; bitreader_read_from_client_() does + * not touch br->consumed_bits at all but we still need to set + * it in case it fails and we have to return false. + */ + br->consumed_bits = cbits; + br->consumed_words = cwords; + if(!bitreader_read_from_client_(br)) + return false; + cwords = br->consumed_words; + } +break1: + /* read binary part */ + FLAC__ASSERT(cwords <= br->words); + + if(bits) { + while((br->words-cwords)*FLAC__BITS_PER_WORD + br->bytes*8 - cbits < bits) { + /* flush registers and read; bitreader_read_from_client_() does + * not touch br->consumed_bits at all but we still need to set + * it in case it fails and we have to return false. + */ + br->consumed_bits = cbits; + br->consumed_words = cwords; + if(!bitreader_read_from_client_(br)) + return false; + cwords = br->consumed_words; + } + if(cwords < br->words) { /* if we've not consumed up to a partial tail word... */ + if(cbits) { + /* this also works when consumed_bits==0, it's just a little slower than necessary for that case */ + const unsigned n = FLAC__BITS_PER_WORD - cbits; + const brword word = br->buffer[cwords]; + if(bits < n) { + uval <<= bits; + uval |= (word & (FLAC__WORD_ALL_ONES >> cbits)) >> (n-bits); + cbits += bits; + goto break2; + } + uval <<= n; + uval |= word & (FLAC__WORD_ALL_ONES >> cbits); + bits -= n; + crc16_update_word_(br, word); + cwords++; + cbits = 0; + if(bits) { /* if there are still bits left to read, there have to be less than 32 so they will all be in the next word */ + uval <<= bits; + uval |= (br->buffer[cwords] >> (FLAC__BITS_PER_WORD-bits)); + cbits = bits; + } + goto break2; + } + else { + FLAC__ASSERT(bits < FLAC__BITS_PER_WORD); + uval <<= bits; + uval |= br->buffer[cwords] >> (FLAC__BITS_PER_WORD-bits); + cbits = bits; + goto break2; + } + } + else { + /* in this case we're starting our read at a partial tail word; + * the reader has guaranteed that we have at least 'bits' bits + * available to read, which makes this case simpler. + */ + uval <<= bits; + if(cbits) { + /* this also works when consumed_bits==0, it's just a little slower than necessary for that case */ + FLAC__ASSERT(cbits + bits <= br->bytes*8); + uval |= (br->buffer[cwords] & (FLAC__WORD_ALL_ONES >> cbits)) >> (FLAC__BITS_PER_WORD-cbits-bits); + cbits += bits; + goto break2; + } + else { + uval |= br->buffer[cwords] >> (FLAC__BITS_PER_WORD-bits); + cbits += bits; + goto break2; + } + } + } +break2: + /* compose the value */ + *vals = (int)(uval >> 1 ^ -(int)(uval & 1)); + + /* are we done? */ + --nvals; + if(nvals == 0) { + br->consumed_bits = cbits; + br->consumed_words = cwords; + return true; + } + + uval = 0; + ++vals; + + } +} +#else +{ + unsigned i; + unsigned uval = 0; + + /* try and get br->consumed_words and br->consumed_bits into register; + * must remember to flush them back to *br before calling other + * bitwriter functions that use them, and before returning */ + register unsigned cwords; + register unsigned cbits; + unsigned ucbits; /* keep track of the number of unconsumed bits in the buffer */ + + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + /* WATCHOUT: code does not work with <32bit words; we can make things much faster with this assertion */ + FLAC__ASSERT(FLAC__BITS_PER_WORD >= 32); + FLAC__ASSERT(parameter < 32); + /* the above two asserts also guarantee that the binary part never straddles more than 2 words, so we don't have to loop to read it */ + + if(nvals == 0) + return true; + + cbits = br->consumed_bits; + cwords = br->consumed_words; + ucbits = (br->words-cwords)*FLAC__BITS_PER_WORD + br->bytes*8 - cbits; + + while(1) { + + /* read unary part */ + while(1) { + while(cwords < br->words) { /* if we've not consumed up to a partial tail word... */ + brword b = br->buffer[cwords] << cbits; + if(b) { +#if 0 /* is not discernably faster... */ && defined FLAC__CPU_IA32 && !defined FLAC__NO_ASM && FLAC__BITS_PER_WORD == 32 && defined __GNUC__ + asm volatile ( + "bsrl %1, %0;" + "notl %0;" + "andl $31, %0;" + : "=r"(i) + : "r"(b) + ); +#else + i = COUNT_ZERO_MSBS(b); +#endif + uval += i; + cbits += i; + cbits++; /* skip over stop bit */ + if(cbits >= FLAC__BITS_PER_WORD) { /* faster way of testing if(cbits == FLAC__BITS_PER_WORD) */ + crc16_update_word_(br, br->buffer[cwords]); + cwords++; + cbits = 0; + } + goto break1; + } + else { + uval += FLAC__BITS_PER_WORD - cbits; + crc16_update_word_(br, br->buffer[cwords]); + cwords++; + cbits = 0; + /* didn't find stop bit yet, have to keep going... */ + } + } + /* at this point we've eaten up all the whole words; have to try + * reading through any tail bytes before calling the read callback. + * this is a repeat of the above logic adjusted for the fact we + * don't have a whole word. note though if the client is feeding + * us data a byte at a time (unlikely), br->consumed_bits may not + * be zero. + */ + if(br->bytes) { + const unsigned end = br->bytes * 8; + brword b = (br->buffer[cwords] & ~(FLAC__WORD_ALL_ONES >> end)) << cbits; + if(b) { + i = COUNT_ZERO_MSBS(b); + uval += i; + cbits += i; + cbits++; /* skip over stop bit */ + FLAC__ASSERT(cbits < FLAC__BITS_PER_WORD); + goto break1; + } + else { + uval += end - cbits; + cbits += end; + FLAC__ASSERT(cbits < FLAC__BITS_PER_WORD); + /* didn't find stop bit yet, have to keep going... */ + } + } + /* flush registers and read; bitreader_read_from_client_() does + * not touch br->consumed_bits at all but we still need to set + * it in case it fails and we have to return false. + */ + br->consumed_bits = cbits; + br->consumed_words = cwords; + if(!bitreader_read_from_client_(br)) + return false; + cwords = br->consumed_words; + ucbits = (br->words-cwords)*FLAC__BITS_PER_WORD + br->bytes*8 - cbits + uval; + /* + uval to offset our count by the # of unary bits already + * consumed before the read, because we will add these back + * in all at once at break1 + */ + } +break1: + ucbits -= uval; + ucbits--; /* account for stop bit */ + + /* read binary part */ + FLAC__ASSERT(cwords <= br->words); + + if(parameter) { + while(ucbits < parameter) { + /* flush registers and read; bitreader_read_from_client_() does + * not touch br->consumed_bits at all but we still need to set + * it in case it fails and we have to return false. + */ + br->consumed_bits = cbits; + br->consumed_words = cwords; + if(!bitreader_read_from_client_(br)) + return false; + cwords = br->consumed_words; + ucbits = (br->words-cwords)*FLAC__BITS_PER_WORD + br->bytes*8 - cbits; + } + if(cwords < br->words) { /* if we've not consumed up to a partial tail word... */ + if(cbits) { + /* this also works when consumed_bits==0, it's just slower than necessary for that case */ + const unsigned n = FLAC__BITS_PER_WORD - cbits; + const brword word = br->buffer[cwords]; + if(parameter < n) { + uval <<= parameter; + uval |= (word & (FLAC__WORD_ALL_ONES >> cbits)) >> (n-parameter); + cbits += parameter; + } + else { + uval <<= n; + uval |= word & (FLAC__WORD_ALL_ONES >> cbits); + crc16_update_word_(br, word); + cwords++; + cbits = parameter - n; + if(cbits) { /* parameter > n, i.e. if there are still bits left to read, there have to be less than 32 so they will all be in the next word */ + uval <<= cbits; + uval |= (br->buffer[cwords] >> (FLAC__BITS_PER_WORD-cbits)); + } + } + } + else { + cbits = parameter; + uval <<= parameter; + uval |= br->buffer[cwords] >> (FLAC__BITS_PER_WORD-cbits); + } + } + else { + /* in this case we're starting our read at a partial tail word; + * the reader has guaranteed that we have at least 'parameter' + * bits available to read, which makes this case simpler. + */ + uval <<= parameter; + if(cbits) { + /* this also works when consumed_bits==0, it's just a little slower than necessary for that case */ + FLAC__ASSERT(cbits + parameter <= br->bytes*8); + uval |= (br->buffer[cwords] & (FLAC__WORD_ALL_ONES >> cbits)) >> (FLAC__BITS_PER_WORD-cbits-parameter); + cbits += parameter; + } + else { + cbits = parameter; + uval |= br->buffer[cwords] >> (FLAC__BITS_PER_WORD-cbits); + } + } + } + + ucbits -= parameter; + + /* compose the value */ + *vals = (int)(uval >> 1 ^ -(int)(uval & 1)); + + /* are we done? */ + --nvals; + if(nvals == 0) { + br->consumed_bits = cbits; + br->consumed_words = cwords; + return true; + } + + uval = 0; + ++vals; + + } +} +#endif + +#if 0 /* UNUSED */ +FLAC__bool FLAC__bitreader_read_golomb_signed(FLAC__BitReader *br, int *val, unsigned parameter) +{ + FLAC__uint32 lsbs = 0, msbs = 0; + unsigned bit, uval, k; + + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + + k = FLAC__bitmath_ilog2(parameter); + + /* read the unary MSBs and end bit */ + if(!FLAC__bitreader_read_unary_unsigned(br, &msbs)) + return false; + + /* read the binary LSBs */ + if(!FLAC__bitreader_read_raw_uint32(br, &lsbs, k)) + return false; + + if(parameter == 1u<= d) { + if(!FLAC__bitreader_read_bit(br, &bit)) + return false; + lsbs <<= 1; + lsbs |= bit; + lsbs -= d; + } + /* compose the value */ + uval = msbs * parameter + lsbs; + } + + /* unfold unsigned to signed */ + if(uval & 1) + *val = -((int)(uval >> 1)) - 1; + else + *val = (int)(uval >> 1); + + return true; +} + +FLAC__bool FLAC__bitreader_read_golomb_unsigned(FLAC__BitReader *br, unsigned *val, unsigned parameter) +{ + FLAC__uint32 lsbs, msbs = 0; + unsigned bit, k; + + FLAC__ASSERT(0 != br); + FLAC__ASSERT(0 != br->buffer); + + k = FLAC__bitmath_ilog2(parameter); + + /* read the unary MSBs and end bit */ + if(!FLAC__bitreader_read_unary_unsigned(br, &msbs)) + return false; + + /* read the binary LSBs */ + if(!FLAC__bitreader_read_raw_uint32(br, &lsbs, k)) + return false; + + if(parameter == 1u<= d) { + if(!FLAC__bitreader_read_bit(br, &bit)) + return false; + lsbs <<= 1; + lsbs |= bit; + lsbs -= d; + } + /* compose the value */ + *val = msbs * parameter + lsbs; + } + + return true; +} +#endif /* UNUSED */ + +/* on return, if *val == 0xffffffff then the utf-8 sequence was invalid, but the return value will be true */ +FLAC__bool FLAC__bitreader_read_utf8_uint32(FLAC__BitReader *br, FLAC__uint32 *val, FLAC__byte *raw, unsigned *rawlen) +{ + FLAC__uint32 v = 0; + FLAC__uint32 x; + unsigned i; + + if(!FLAC__bitreader_read_raw_uint32(br, &x, 8)) + return false; + if(raw) + raw[(*rawlen)++] = (FLAC__byte)x; + if(!(x & 0x80)) { /* 0xxxxxxx */ + v = x; + i = 0; + } + else if(x & 0xC0 && !(x & 0x20)) { /* 110xxxxx */ + v = x & 0x1F; + i = 1; + } + else if(x & 0xE0 && !(x & 0x10)) { /* 1110xxxx */ + v = x & 0x0F; + i = 2; + } + else if(x & 0xF0 && !(x & 0x08)) { /* 11110xxx */ + v = x & 0x07; + i = 3; + } + else if(x & 0xF8 && !(x & 0x04)) { /* 111110xx */ + v = x & 0x03; + i = 4; + } + else if(x & 0xFC && !(x & 0x02)) { /* 1111110x */ + v = x & 0x01; + i = 5; + } + else { + *val = 0xffffffff; + return true; + } + for( ; i; i--) { + if(!FLAC__bitreader_read_raw_uint32(br, &x, 8)) + return false; + if(raw) + raw[(*rawlen)++] = (FLAC__byte)x; + if(!(x & 0x80) || (x & 0x40)) { /* 10xxxxxx */ + *val = 0xffffffff; + return true; + } + v <<= 6; + v |= (x & 0x3F); + } + *val = v; + return true; +} + +/* on return, if *val == 0xffffffffffffffff then the utf-8 sequence was invalid, but the return value will be true */ +FLAC__bool FLAC__bitreader_read_utf8_uint64(FLAC__BitReader *br, FLAC__uint64 *val, FLAC__byte *raw, unsigned *rawlen) +{ + FLAC__uint64 v = 0; + FLAC__uint32 x; + unsigned i; + + if(!FLAC__bitreader_read_raw_uint32(br, &x, 8)) + return false; + if(raw) + raw[(*rawlen)++] = (FLAC__byte)x; + if(!(x & 0x80)) { /* 0xxxxxxx */ + v = x; + i = 0; + } + else if(x & 0xC0 && !(x & 0x20)) { /* 110xxxxx */ + v = x & 0x1F; + i = 1; + } + else if(x & 0xE0 && !(x & 0x10)) { /* 1110xxxx */ + v = x & 0x0F; + i = 2; + } + else if(x & 0xF0 && !(x & 0x08)) { /* 11110xxx */ + v = x & 0x07; + i = 3; + } + else if(x & 0xF8 && !(x & 0x04)) { /* 111110xx */ + v = x & 0x03; + i = 4; + } + else if(x & 0xFC && !(x & 0x02)) { /* 1111110x */ + v = x & 0x01; + i = 5; + } + else if(x & 0xFE && !(x & 0x01)) { /* 11111110 */ + v = 0; + i = 6; + } + else { + *val = FLAC__U64L(0xffffffffffffffff); + return true; + } + for( ; i; i--) { + if(!FLAC__bitreader_read_raw_uint32(br, &x, 8)) + return false; + if(raw) + raw[(*rawlen)++] = (FLAC__byte)x; + if(!(x & 0x80) || (x & 0x40)) { /* 10xxxxxx */ + *val = FLAC__U64L(0xffffffffffffffff); + return true; + } + v <<= 6; + v |= (x & 0x3F); + } + *val = v; + return true; +} diff --git a/src/FLAC/src/libFLAC/bitwriter.c b/src/FLAC/src/libFLAC/bitwriter.c new file mode 100644 index 00000000..82190a5a --- /dev/null +++ b/src/FLAC/src/libFLAC/bitwriter.c @@ -0,0 +1,872 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include /* for malloc() */ +#include /* for memcpy(), memset() */ + +#include + +#if 0 /* UNUSED */ +#include "private/bitmath.h" +#endif +#include "private/bitwriter.h" +#include "private/crc.h" +#include "FLAC/assert.h" + +/* Things should be fastest when this matches the machine word size */ +/* WATCHOUT: if you change this you must also change the following #defines down to SWAP_BE_WORD_TO_HOST below to match */ +/* WATCHOUT: there are a few places where the code will not work unless bwword is >= 32 bits wide */ +typedef FLAC__uint32 bwword; +#define FLAC__BYTES_PER_WORD 4 +#define FLAC__BITS_PER_WORD 32 +#define FLAC__WORD_ALL_ONES ((FLAC__uint32)0xffffffff) +/* SWAP_BE_WORD_TO_HOST swaps bytes in a bwword (which is always big-endian) if necessary to match host byte order */ +#define SWAP_BE_WORD_TO_HOST(x) BEI2H_INT(x) + +/* + * The default capacity here doesn't matter too much. The buffer always grows + * to hold whatever is written to it. Usually the encoder will stop adding at + * a frame or metadata block, then write that out and clear the buffer for the + * next one. + */ +static const unsigned FLAC__BITWRITER_DEFAULT_CAPACITY = 32768u / sizeof(bwword); /* size in words */ +/* When growing, increment 4K at a time */ +static const unsigned FLAC__BITWRITER_DEFAULT_INCREMENT = 4096u / sizeof(bwword); /* size in words */ + +#define FLAC__WORDS_TO_BITS(words) ((words) * FLAC__BITS_PER_WORD) +#define FLAC__TOTAL_BITS(bw) (FLAC__WORDS_TO_BITS((bw)->words) + (bw)->bits) + +#ifdef min +#undef min +#endif +#define min(x,y) ((x)<(y)?(x):(y)) + +/* adjust for compilers that can't understand using LLU suffix for uint64_t literals */ +#ifdef _MSC_VER +#define FLAC__U64L(x) x +#else +#define FLAC__U64L(x) x##LLU +#endif + +#ifndef FLaC__INLINE +#define FLaC__INLINE +#endif + +struct FLAC__BitWriter { + bwword *buffer; + bwword accum; /* accumulator; bits are right-justified; when full, accum is appended to buffer */ + unsigned capacity; /* capacity of buffer in words */ + unsigned words; /* # of complete words in buffer */ + unsigned bits; /* # of used bits in accum */ +}; + +#ifdef _MSC_VER +/* OPT: an MSVC built-in would be better */ +static _inline FLAC__uint32 local_swap32_(FLAC__uint32 x) +{ + x = ((x<<8)&0xFF00FF00) | ((x>>8)&0x00FF00FF); + return (x>>16) | (x<<16); +} +#endif + +/* * WATCHOUT: The current implementation only grows the buffer. */ +static FLAC__bool bitwriter_grow_(FLAC__BitWriter *bw, unsigned bits_to_add) +{ + unsigned new_capacity; + bwword *new_buffer; + + FLAC__ASSERT(0 != bw); + FLAC__ASSERT(0 != bw->buffer); + + /* calculate total words needed to store 'bits_to_add' additional bits */ + new_capacity = bw->words + ((bw->bits + bits_to_add + FLAC__BITS_PER_WORD - 1) / FLAC__BITS_PER_WORD); + + /* it's possible (due to pessimism in the growth estimation that + * leads to this call) that we don't actually need to grow + */ + if(bw->capacity >= new_capacity) + return true; + + /* round up capacity increase to the nearest FLAC__BITWRITER_DEFAULT_INCREMENT */ + if((new_capacity - bw->capacity) % FLAC__BITWRITER_DEFAULT_INCREMENT) + new_capacity += FLAC__BITWRITER_DEFAULT_INCREMENT - ((new_capacity - bw->capacity) % FLAC__BITWRITER_DEFAULT_INCREMENT); + /* make sure we got everything right */ + FLAC__ASSERT(0 == (new_capacity - bw->capacity) % FLAC__BITWRITER_DEFAULT_INCREMENT); + FLAC__ASSERT(new_capacity > bw->capacity); + FLAC__ASSERT(new_capacity >= bw->words + ((bw->bits + bits_to_add + FLAC__BITS_PER_WORD - 1) / FLAC__BITS_PER_WORD)); + + new_buffer = (bwword*)realloc(bw->buffer, sizeof(bwword)*new_capacity); + if(new_buffer == 0) + return false; + bw->buffer = new_buffer; + bw->capacity = new_capacity; + return true; +} + + +/*********************************************************************** + * + * Class constructor/destructor + * + ***********************************************************************/ + +FLAC__BitWriter *FLAC__bitwriter_new(void) +{ + FLAC__BitWriter *bw = (FLAC__BitWriter*)calloc(1, sizeof(FLAC__BitWriter)); + /* note that calloc() sets all members to 0 for us */ + return bw; +} + +void FLAC__bitwriter_delete(FLAC__BitWriter *bw) +{ + FLAC__ASSERT(0 != bw); + + FLAC__bitwriter_free(bw); + free(bw); +} + +/*********************************************************************** + * + * Public class methods + * + ***********************************************************************/ + +FLAC__bool FLAC__bitwriter_init(FLAC__BitWriter *bw) +{ + FLAC__ASSERT(0 != bw); + + bw->words = bw->bits = 0; + bw->capacity = FLAC__BITWRITER_DEFAULT_CAPACITY; + bw->buffer = (bwword*)malloc(sizeof(bwword) * bw->capacity); + if(bw->buffer == 0) + return false; + + return true; +} + +void FLAC__bitwriter_free(FLAC__BitWriter *bw) +{ + FLAC__ASSERT(0 != bw); + + if(0 != bw->buffer) + free(bw->buffer); + bw->buffer = 0; + bw->capacity = 0; + bw->words = bw->bits = 0; +} + +void FLAC__bitwriter_clear(FLAC__BitWriter *bw) +{ + bw->words = bw->bits = 0; +} + +void FLAC__bitwriter_dump(const FLAC__BitWriter *bw, FILE *out) +{ + unsigned i, j; + if(bw == 0) { + fprintf(out, "bitwriter is NULL\n"); + } + else { + fprintf(out, "bitwriter: capacity=%u words=%u bits=%u total_bits=%u\n", bw->capacity, bw->words, bw->bits, FLAC__TOTAL_BITS(bw)); + + for(i = 0; i < bw->words; i++) { + fprintf(out, "%08X: ", i); + for(j = 0; j < FLAC__BITS_PER_WORD; j++) + fprintf(out, "%01u", bw->buffer[i] & (1 << (FLAC__BITS_PER_WORD-j-1)) ? 1:0); + fprintf(out, "\n"); + } + if(bw->bits > 0) { + fprintf(out, "%08X: ", i); + for(j = 0; j < bw->bits; j++) + fprintf(out, "%01u", bw->accum & (1 << (bw->bits-j-1)) ? 1:0); + fprintf(out, "\n"); + } + } +} + +FLAC__bool FLAC__bitwriter_get_write_crc16(FLAC__BitWriter *bw, FLAC__uint16 *crc) +{ + const FLAC__byte *buffer; + size_t bytes; + + FLAC__ASSERT((bw->bits & 7) == 0); /* assert that we're byte-aligned */ + + if(!FLAC__bitwriter_get_buffer(bw, &buffer, &bytes)) + return false; + + *crc = (FLAC__uint16)FLAC__crc16(buffer, bytes); + FLAC__bitwriter_release_buffer(bw); + return true; +} + +FLAC__bool FLAC__bitwriter_get_write_crc8(FLAC__BitWriter *bw, FLAC__byte *crc) +{ + const FLAC__byte *buffer; + size_t bytes; + + FLAC__ASSERT((bw->bits & 7) == 0); /* assert that we're byte-aligned */ + + if(!FLAC__bitwriter_get_buffer(bw, &buffer, &bytes)) + return false; + + *crc = FLAC__crc8(buffer, bytes); + FLAC__bitwriter_release_buffer(bw); + return true; +} + +FLAC__bool FLAC__bitwriter_is_byte_aligned(const FLAC__BitWriter *bw) +{ + return ((bw->bits & 7) == 0); +} + +unsigned FLAC__bitwriter_get_input_bits_unconsumed(const FLAC__BitWriter *bw) +{ + return FLAC__TOTAL_BITS(bw); +} + +FLAC__bool FLAC__bitwriter_get_buffer(FLAC__BitWriter *bw, const FLAC__byte **buffer, size_t *bytes) +{ + FLAC__ASSERT((bw->bits & 7) == 0); + /* double protection */ + if(bw->bits & 7) + return false; + /* if we have bits in the accumulator we have to flush those to the buffer first */ + if(bw->bits) { + FLAC__ASSERT(bw->words <= bw->capacity); + if(bw->words == bw->capacity && !bitwriter_grow_(bw, FLAC__BITS_PER_WORD)) + return false; + /* append bits as complete word to buffer, but don't change bw->accum or bw->bits */ + bw->buffer[bw->words] = SWAP_BE_WORD_TO_HOST(bw->accum << (FLAC__BITS_PER_WORD-bw->bits)); + } + /* now we can just return what we have */ + *buffer = (FLAC__byte*)bw->buffer; + *bytes = (FLAC__BYTES_PER_WORD * bw->words) + (bw->bits >> 3); + return true; +} + +void FLAC__bitwriter_release_buffer(FLAC__BitWriter *bw) +{ + /* nothing to do. in the future, strict checking of a 'writer-is-in- + * get-mode' flag could be added everywhere and then cleared here + */ + (void)bw; +} + +FLaC__INLINE FLAC__bool FLAC__bitwriter_write_zeroes(FLAC__BitWriter *bw, unsigned bits) +{ + unsigned n; + + FLAC__ASSERT(0 != bw); + FLAC__ASSERT(0 != bw->buffer); + + if(bits == 0) + return true; + /* slightly pessimistic size check but faster than "<= bw->words + (bw->bits+bits+FLAC__BITS_PER_WORD-1)/FLAC__BITS_PER_WORD" */ + if(bw->capacity <= bw->words + bits && !bitwriter_grow_(bw, bits)) + return false; + /* first part gets to word alignment */ + if(bw->bits) { + n = min(FLAC__BITS_PER_WORD - bw->bits, bits); + bw->accum <<= n; + bits -= n; + bw->bits += n; + if(bw->bits == FLAC__BITS_PER_WORD) { + bw->buffer[bw->words++] = SWAP_BE_WORD_TO_HOST(bw->accum); + bw->bits = 0; + } + else + return true; + } + /* do whole words */ + while(bits >= FLAC__BITS_PER_WORD) { + bw->buffer[bw->words++] = 0; + bits -= FLAC__BITS_PER_WORD; + } + /* do any leftovers */ + if(bits > 0) { + bw->accum = 0; + bw->bits = bits; + } + return true; +} + +FLaC__INLINE FLAC__bool FLAC__bitwriter_write_raw_uint32(FLAC__BitWriter *bw, FLAC__uint32 val, unsigned bits) +{ + register unsigned left; + + /* WATCHOUT: code does not work with <32bit words; we can make things much faster with this assertion */ + FLAC__ASSERT(FLAC__BITS_PER_WORD >= 32); + + FLAC__ASSERT(0 != bw); + FLAC__ASSERT(0 != bw->buffer); + + FLAC__ASSERT(bits <= 32); + if(bits == 0) + return true; + + /* slightly pessimistic size check but faster than "<= bw->words + (bw->bits+bits+FLAC__BITS_PER_WORD-1)/FLAC__BITS_PER_WORD" */ + if(bw->capacity <= bw->words + bits && !bitwriter_grow_(bw, bits)) + return false; + + left = FLAC__BITS_PER_WORD - bw->bits; + if(bits < left) { + bw->accum <<= bits; + bw->accum |= val; + bw->bits += bits; + } + else if(bw->bits) { /* WATCHOUT: if bw->bits == 0, left==FLAC__BITS_PER_WORD and bw->accum<<=left is a NOP instead of setting to 0 */ + bw->accum <<= left; + bw->accum |= val >> (bw->bits = bits - left); + bw->buffer[bw->words++] = SWAP_BE_WORD_TO_HOST(bw->accum); + bw->accum = val; + } + else { + bw->accum = val; + bw->bits = 0; + bw->buffer[bw->words++] = SWAP_BE_WORD_TO_HOST(val); + } + + return true; +} + +FLaC__INLINE FLAC__bool FLAC__bitwriter_write_raw_int32(FLAC__BitWriter *bw, FLAC__int32 val, unsigned bits) +{ + /* zero-out unused bits */ + if(bits < 32) + val &= (~(0xffffffff << bits)); + + return FLAC__bitwriter_write_raw_uint32(bw, (FLAC__uint32)val, bits); +} + +FLaC__INLINE FLAC__bool FLAC__bitwriter_write_raw_uint64(FLAC__BitWriter *bw, FLAC__uint64 val, unsigned bits) +{ + /* this could be a little faster but it's not used for much */ + if(bits > 32) { + return + FLAC__bitwriter_write_raw_uint32(bw, (FLAC__uint32)(val>>32), bits-32) && + FLAC__bitwriter_write_raw_uint32(bw, (FLAC__uint32)val, 32); + } + else + return FLAC__bitwriter_write_raw_uint32(bw, (FLAC__uint32)val, bits); +} + +FLaC__INLINE FLAC__bool FLAC__bitwriter_write_raw_uint32_little_endian(FLAC__BitWriter *bw, FLAC__uint32 val) +{ + /* this doesn't need to be that fast as currently it is only used for vorbis comments */ + + if(!FLAC__bitwriter_write_raw_uint32(bw, val & 0xff, 8)) + return false; + if(!FLAC__bitwriter_write_raw_uint32(bw, (val>>8) & 0xff, 8)) + return false; + if(!FLAC__bitwriter_write_raw_uint32(bw, (val>>16) & 0xff, 8)) + return false; + if(!FLAC__bitwriter_write_raw_uint32(bw, val>>24, 8)) + return false; + + return true; +} + +FLaC__INLINE FLAC__bool FLAC__bitwriter_write_byte_block(FLAC__BitWriter *bw, const FLAC__byte vals[], unsigned nvals) +{ + unsigned i; + + /* this could be faster but currently we don't need it to be since it's only used for writing metadata */ + for(i = 0; i < nvals; i++) { + if(!FLAC__bitwriter_write_raw_uint32(bw, (FLAC__uint32)(vals[i]), 8)) + return false; + } + + return true; +} + +FLAC__bool FLAC__bitwriter_write_unary_unsigned(FLAC__BitWriter *bw, unsigned val) +{ + if(val < 32) + return FLAC__bitwriter_write_raw_uint32(bw, 1, ++val); + else + return + FLAC__bitwriter_write_zeroes(bw, val) && + FLAC__bitwriter_write_raw_uint32(bw, 1, 1); +} + +unsigned FLAC__bitwriter_rice_bits(FLAC__int32 val, unsigned parameter) +{ + FLAC__uint32 uval; + + FLAC__ASSERT(parameter < sizeof(unsigned)*8); + + /* fold signed to unsigned; actual formula is: negative(v)? -2v-1 : 2v */ + uval = (val<<1) ^ (val>>31); + + return 1 + parameter + (uval >> parameter); +} + +#if 0 /* UNUSED */ +unsigned FLAC__bitwriter_golomb_bits_signed(int val, unsigned parameter) +{ + unsigned bits, msbs, uval; + unsigned k; + + FLAC__ASSERT(parameter > 0); + + /* fold signed to unsigned */ + if(val < 0) + uval = (unsigned)(((-(++val)) << 1) + 1); + else + uval = (unsigned)(val << 1); + + k = FLAC__bitmath_ilog2(parameter); + if(parameter == 1u<> k; + bits = 1 + k + msbs; + } + else { + unsigned q, r, d; + + d = (1 << (k+1)) - parameter; + q = uval / parameter; + r = uval - (q * parameter); + + bits = 1 + q + k; + if(r >= d) + bits++; + } + return bits; +} + +unsigned FLAC__bitwriter_golomb_bits_unsigned(unsigned uval, unsigned parameter) +{ + unsigned bits, msbs; + unsigned k; + + FLAC__ASSERT(parameter > 0); + + k = FLAC__bitmath_ilog2(parameter); + if(parameter == 1u<> k; + bits = 1 + k + msbs; + } + else { + unsigned q, r, d; + + d = (1 << (k+1)) - parameter; + q = uval / parameter; + r = uval - (q * parameter); + + bits = 1 + q + k; + if(r >= d) + bits++; + } + return bits; +} +#endif /* UNUSED */ + +FLAC__bool FLAC__bitwriter_write_rice_signed(FLAC__BitWriter *bw, FLAC__int32 val, unsigned parameter) +{ + unsigned total_bits, interesting_bits, msbs; + FLAC__uint32 uval, pattern; + + FLAC__ASSERT(0 != bw); + FLAC__ASSERT(0 != bw->buffer); + FLAC__ASSERT(parameter < 8*sizeof(uval)); + + /* fold signed to unsigned; actual formula is: negative(v)? -2v-1 : 2v */ + uval = (val<<1) ^ (val>>31); + + msbs = uval >> parameter; + interesting_bits = 1 + parameter; + total_bits = interesting_bits + msbs; + pattern = 1 << parameter; /* the unary end bit */ + pattern |= (uval & ((1<> (31-parameter); /* ...then mask off the bits above the stop bit with val&=mask2*/ + FLAC__uint32 uval; + unsigned left; + const unsigned lsbits = 1 + parameter; + unsigned msbits; + + FLAC__ASSERT(0 != bw); + FLAC__ASSERT(0 != bw->buffer); + FLAC__ASSERT(parameter < 8*sizeof(bwword)-1); + /* WATCHOUT: code does not work with <32bit words; we can make things much faster with this assertion */ + FLAC__ASSERT(FLAC__BITS_PER_WORD >= 32); + + while(nvals) { + /* fold signed to unsigned; actual formula is: negative(v)? -2v-1 : 2v */ + uval = (*vals<<1) ^ (*vals>>31); + + msbits = uval >> parameter; + +#if 1 /* OPT: can remove this special case if it doesn't make up for the extra compare */ + if(bw->bits + msbits + lsbits <= FLAC__BITS_PER_WORD) { /* i.e. if the whole thing fits in the current bwword */ + bw->bits = bw->bits + msbits + lsbits; + uval |= mask1; /* set stop bit */ + uval &= mask2; /* mask off unused top bits */ + /* NOT: bw->accum <<= msbits + lsbits because msbits+lsbits could be 32, then the shift would be a NOP */ + bw->accum <<= msbits; + bw->accum <<= lsbits; + bw->accum |= uval; + if(bw->bits == FLAC__BITS_PER_WORD) { + bw->buffer[bw->words++] = SWAP_BE_WORD_TO_HOST(bw->accum); + bw->bits = 0; + /* burying the capacity check down here means we have to grow the buffer a little if there are more vals to do */ + if(bw->capacity <= bw->words && nvals > 1 && !bitwriter_grow_(bw, 1)) { + FLAC__ASSERT(bw->capacity == bw->words); + return false; + } + } + } + else { +#elif 0 /*@@@@@@ OPT: try this version with MSVC6 to see if better, not much difference for gcc-4 */ + if(bw->bits + msbits + lsbits < FLAC__BITS_PER_WORD) { /* i.e. if the whole thing fits in the current bwword */ + bw->bits = bw->bits + msbits + lsbits; + uval |= mask1; /* set stop bit */ + uval &= mask2; /* mask off unused top bits */ + bw->accum <<= msbits + lsbits; + bw->accum |= uval; + } + else { +#endif + /* slightly pessimistic size check but faster than "<= bw->words + (bw->bits+msbits+lsbits+FLAC__BITS_PER_WORD-1)/FLAC__BITS_PER_WORD" */ + /* OPT: pessimism may cause flurry of false calls to grow_ which eat up all savings before it */ + if(bw->capacity <= bw->words + bw->bits + msbits + 1/*lsbits always fit in 1 bwword*/ && !bitwriter_grow_(bw, msbits+lsbits)) + return false; + + if(msbits) { + /* first part gets to word alignment */ + if(bw->bits) { + left = FLAC__BITS_PER_WORD - bw->bits; + if(msbits < left) { + bw->accum <<= msbits; + bw->bits += msbits; + goto break1; + } + else { + bw->accum <<= left; + msbits -= left; + bw->buffer[bw->words++] = SWAP_BE_WORD_TO_HOST(bw->accum); + bw->bits = 0; + } + } + /* do whole words */ + while(msbits >= FLAC__BITS_PER_WORD) { + bw->buffer[bw->words++] = 0; + msbits -= FLAC__BITS_PER_WORD; + } + /* do any leftovers */ + if(msbits > 0) { + bw->accum = 0; + bw->bits = msbits; + } + } +break1: + uval |= mask1; /* set stop bit */ + uval &= mask2; /* mask off unused top bits */ + + left = FLAC__BITS_PER_WORD - bw->bits; + if(lsbits < left) { + bw->accum <<= lsbits; + bw->accum |= uval; + bw->bits += lsbits; + } + else { + /* if bw->bits == 0, left==FLAC__BITS_PER_WORD which will always + * be > lsbits (because of previous assertions) so it would have + * triggered the (lsbitsbits); + FLAC__ASSERT(left < FLAC__BITS_PER_WORD); + bw->accum <<= left; + bw->accum |= uval >> (bw->bits = lsbits - left); + bw->buffer[bw->words++] = SWAP_BE_WORD_TO_HOST(bw->accum); + bw->accum = uval; + } +#if 1 + } +#endif + vals++; + nvals--; + } + return true; +} + +#if 0 /* UNUSED */ +FLAC__bool FLAC__bitwriter_write_golomb_signed(FLAC__BitWriter *bw, int val, unsigned parameter) +{ + unsigned total_bits, msbs, uval; + unsigned k; + + FLAC__ASSERT(0 != bw); + FLAC__ASSERT(0 != bw->buffer); + FLAC__ASSERT(parameter > 0); + + /* fold signed to unsigned */ + if(val < 0) + uval = (unsigned)(((-(++val)) << 1) + 1); + else + uval = (unsigned)(val << 1); + + k = FLAC__bitmath_ilog2(parameter); + if(parameter == 1u<> k; + total_bits = 1 + k + msbs; + pattern = 1 << k; /* the unary end bit */ + pattern |= (uval & ((1u<= d) { + if(!FLAC__bitwriter_write_raw_uint32(bw, r+d, k+1)) + return false; + } + else { + if(!FLAC__bitwriter_write_raw_uint32(bw, r, k)) + return false; + } + } + return true; +} + +FLAC__bool FLAC__bitwriter_write_golomb_unsigned(FLAC__BitWriter *bw, unsigned uval, unsigned parameter) +{ + unsigned total_bits, msbs; + unsigned k; + + FLAC__ASSERT(0 != bw); + FLAC__ASSERT(0 != bw->buffer); + FLAC__ASSERT(parameter > 0); + + k = FLAC__bitmath_ilog2(parameter); + if(parameter == 1u<> k; + total_bits = 1 + k + msbs; + pattern = 1 << k; /* the unary end bit */ + pattern |= (uval & ((1u<= d) { + if(!FLAC__bitwriter_write_raw_uint32(bw, r+d, k+1)) + return false; + } + else { + if(!FLAC__bitwriter_write_raw_uint32(bw, r, k)) + return false; + } + } + return true; +} +#endif /* UNUSED */ + +FLAC__bool FLAC__bitwriter_write_utf8_uint32(FLAC__BitWriter *bw, FLAC__uint32 val) +{ + FLAC__bool ok = 1; + + FLAC__ASSERT(0 != bw); + FLAC__ASSERT(0 != bw->buffer); + + FLAC__ASSERT(!(val & 0x80000000)); /* this version only handles 31 bits */ + + if(val < 0x80) { + return FLAC__bitwriter_write_raw_uint32(bw, val, 8); + } + else if(val < 0x800) { + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xC0 | (val>>6), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (val&0x3F), 8); + } + else if(val < 0x10000) { + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xE0 | (val>>12), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | ((val>>6)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (val&0x3F), 8); + } + else if(val < 0x200000) { + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xF0 | (val>>18), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | ((val>>12)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | ((val>>6)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (val&0x3F), 8); + } + else if(val < 0x4000000) { + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xF8 | (val>>24), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | ((val>>18)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | ((val>>12)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | ((val>>6)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (val&0x3F), 8); + } + else { + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xFC | (val>>30), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | ((val>>24)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | ((val>>18)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | ((val>>12)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | ((val>>6)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (val&0x3F), 8); + } + + return ok; +} + +FLAC__bool FLAC__bitwriter_write_utf8_uint64(FLAC__BitWriter *bw, FLAC__uint64 val) +{ + FLAC__bool ok = 1; + + FLAC__ASSERT(0 != bw); + FLAC__ASSERT(0 != bw->buffer); + + FLAC__ASSERT(!(val & FLAC__U64L(0xFFFFFFF000000000))); /* this version only handles 36 bits */ + + if(val < 0x80) { + return FLAC__bitwriter_write_raw_uint32(bw, (FLAC__uint32)val, 8); + } + else if(val < 0x800) { + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xC0 | (FLAC__uint32)(val>>6), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)(val&0x3F), 8); + } + else if(val < 0x10000) { + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xE0 | (FLAC__uint32)(val>>12), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>6)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)(val&0x3F), 8); + } + else if(val < 0x200000) { + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xF0 | (FLAC__uint32)(val>>18), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>12)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>6)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)(val&0x3F), 8); + } + else if(val < 0x4000000) { + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xF8 | (FLAC__uint32)(val>>24), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>18)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>12)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>6)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)(val&0x3F), 8); + } + else if(val < 0x80000000) { + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xFC | (FLAC__uint32)(val>>30), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>24)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>18)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>12)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>6)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)(val&0x3F), 8); + } + else { + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xFE, 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>30)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>24)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>18)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>12)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>6)&0x3F), 8); + ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)(val&0x3F), 8); + } + + return ok; +} + +FLAC__bool FLAC__bitwriter_zero_pad_to_byte_boundary(FLAC__BitWriter *bw) +{ + /* 0-pad to byte boundary */ + if(bw->bits & 7u) + return FLAC__bitwriter_write_zeroes(bw, 8 - (bw->bits & 7u)); + else + return true; +} diff --git a/src/FLAC/src/libFLAC/cpu.c b/src/FLAC/src/libFLAC/cpu.c new file mode 100644 index 00000000..8d787620 --- /dev/null +++ b/src/FLAC/src/libFLAC/cpu.c @@ -0,0 +1,418 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include "private/cpu.h" +#include +#include + +#if defined FLAC__CPU_IA32 +# include +#elif defined FLAC__CPU_PPC +# if !defined FLAC__NO_ASM +# if defined FLAC__SYS_DARWIN +# include +# include +# include +# include +# include +# ifndef CPU_SUBTYPE_POWERPC_970 +# define CPU_SUBTYPE_POWERPC_970 ((cpu_subtype_t) 100) +# endif +# else /* FLAC__SYS_DARWIN */ + +# include +# include + +static sigjmp_buf jmpbuf; +static volatile sig_atomic_t canjump = 0; + +static void sigill_handler (int sig) +{ + if (!canjump) { + signal (sig, SIG_DFL); + raise (sig); + } + canjump = 0; + siglongjmp (jmpbuf, 1); +} +# endif /* FLAC__SYS_DARWIN */ +# endif /* FLAC__NO_ASM */ +#endif /* FLAC__CPU_PPC */ + +#if defined (__NetBSD__) || defined(__OpenBSD__) +#include +#include +#include +#endif + +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) +#include +#include +#endif + +#if defined(__APPLE__) +/* how to get sysctlbyname()? */ +#endif + +/* these are flags in EDX of CPUID AX=00000001 */ +static const unsigned FLAC__CPUINFO_IA32_CPUID_CMOV = 0x00008000; +static const unsigned FLAC__CPUINFO_IA32_CPUID_MMX = 0x00800000; +static const unsigned FLAC__CPUINFO_IA32_CPUID_FXSR = 0x01000000; +static const unsigned FLAC__CPUINFO_IA32_CPUID_SSE = 0x02000000; +static const unsigned FLAC__CPUINFO_IA32_CPUID_SSE2 = 0x04000000; +/* these are flags in ECX of CPUID AX=00000001 */ +static const unsigned FLAC__CPUINFO_IA32_CPUID_SSE3 = 0x00000001; +static const unsigned FLAC__CPUINFO_IA32_CPUID_SSSE3 = 0x00000200; +/* these are flags in EDX of CPUID AX=80000001 */ +static const unsigned FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_3DNOW = 0x80000000; +static const unsigned FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_EXT3DNOW = 0x40000000; +static const unsigned FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_EXTMMX = 0x00400000; + + +/* + * Extra stuff needed for detection of OS support for SSE on IA-32 + */ +#if defined(FLAC__CPU_IA32) && !defined FLAC__NO_ASM && defined FLAC__HAS_NASM && !defined FLAC__NO_SSE_OS && !defined FLAC__SSE_OS +# if defined(__linux__) +/* + * If the OS doesn't support SSE, we will get here with a SIGILL. We + * modify the return address to jump over the offending SSE instruction + * and also the operation following it that indicates the instruction + * executed successfully. In this way we use no global variables and + * stay thread-safe. + * + * 3 + 3 + 6: + * 3 bytes for "xorps xmm0,xmm0" + * 3 bytes for estimate of how long the follwing "inc var" instruction is + * 6 bytes extra in case our estimate is wrong + * 12 bytes puts us in the NOP "landing zone" + */ +# undef USE_OBSOLETE_SIGCONTEXT_FLAVOR /* #define this to use the older signal handler method */ +# ifdef USE_OBSOLETE_SIGCONTEXT_FLAVOR + static void sigill_handler_sse_os(int signal, struct sigcontext sc) + { + (void)signal; + sc.eip += 3 + 3 + 6; + } +# else +# include + static void sigill_handler_sse_os(int sig, siginfo_t *si, void *uc) + { + (void)sig, (void)si; + ((ucontext_t*)uc)->uc_mcontext.gregs[14/*REG_EIP*/] += 3 + 3 + 6; + } +# endif +# elif defined(_MSC_VER) +# include +# undef USE_TRY_CATCH_FLAVOR /* #define this to use the try/catch method for catching illegal opcode exception */ +# ifdef USE_TRY_CATCH_FLAVOR +# else + LONG CALLBACK sigill_handler_sse_os(EXCEPTION_POINTERS *ep) + { + if(ep->ExceptionRecord->ExceptionCode == EXCEPTION_ILLEGAL_INSTRUCTION) { + ep->ContextRecord->Eip += 3 + 3 + 6; + return EXCEPTION_CONTINUE_EXECUTION; + } + return EXCEPTION_CONTINUE_SEARCH; + } +# endif +# endif +#endif + + +void FLAC__cpu_info(FLAC__CPUInfo *info) +{ +/* + * IA32-specific + */ +#ifdef FLAC__CPU_IA32 + info->type = FLAC__CPUINFO_TYPE_IA32; +#if !defined FLAC__NO_ASM && defined FLAC__HAS_NASM + info->use_asm = true; /* we assume a minimum of 80386 with FLAC__CPU_IA32 */ + info->data.ia32.cpuid = FLAC__cpu_have_cpuid_asm_ia32()? true : false; + info->data.ia32.bswap = info->data.ia32.cpuid; /* CPUID => BSWAP since it came after */ + info->data.ia32.cmov = false; + info->data.ia32.mmx = false; + info->data.ia32.fxsr = false; + info->data.ia32.sse = false; + info->data.ia32.sse2 = false; + info->data.ia32.sse3 = false; + info->data.ia32.ssse3 = false; + info->data.ia32._3dnow = false; + info->data.ia32.ext3dnow = false; + info->data.ia32.extmmx = false; + if(info->data.ia32.cpuid) { + /* http://www.sandpile.org/ia32/cpuid.htm */ + FLAC__uint32 flags_edx, flags_ecx; + FLAC__cpu_info_asm_ia32(&flags_edx, &flags_ecx); + info->data.ia32.cmov = (flags_edx & FLAC__CPUINFO_IA32_CPUID_CMOV )? true : false; + info->data.ia32.mmx = (flags_edx & FLAC__CPUINFO_IA32_CPUID_MMX )? true : false; + info->data.ia32.fxsr = (flags_edx & FLAC__CPUINFO_IA32_CPUID_FXSR )? true : false; + info->data.ia32.sse = (flags_edx & FLAC__CPUINFO_IA32_CPUID_SSE )? true : false; + info->data.ia32.sse2 = (flags_edx & FLAC__CPUINFO_IA32_CPUID_SSE2 )? true : false; + info->data.ia32.sse3 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE3 )? true : false; + info->data.ia32.ssse3 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSSE3)? true : false; + +#ifdef FLAC__USE_3DNOW + flags_edx = FLAC__cpu_info_extended_amd_asm_ia32(); + info->data.ia32._3dnow = (flags_edx & FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_3DNOW )? true : false; + info->data.ia32.ext3dnow = (flags_edx & FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_EXT3DNOW)? true : false; + info->data.ia32.extmmx = (flags_edx & FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_EXTMMX )? true : false; +#else + info->data.ia32._3dnow = info->data.ia32.ext3dnow = info->data.ia32.extmmx = false; +#endif + +#ifdef DEBUG + fprintf(stderr, "CPU info (IA-32):\n"); + fprintf(stderr, " CPUID ...... %c\n", info->data.ia32.cpuid ? 'Y' : 'n'); + fprintf(stderr, " BSWAP ...... %c\n", info->data.ia32.bswap ? 'Y' : 'n'); + fprintf(stderr, " CMOV ....... %c\n", info->data.ia32.cmov ? 'Y' : 'n'); + fprintf(stderr, " MMX ........ %c\n", info->data.ia32.mmx ? 'Y' : 'n'); + fprintf(stderr, " FXSR ....... %c\n", info->data.ia32.fxsr ? 'Y' : 'n'); + fprintf(stderr, " SSE ........ %c\n", info->data.ia32.sse ? 'Y' : 'n'); + fprintf(stderr, " SSE2 ....... %c\n", info->data.ia32.sse2 ? 'Y' : 'n'); + fprintf(stderr, " SSE3 ....... %c\n", info->data.ia32.sse3 ? 'Y' : 'n'); + fprintf(stderr, " SSSE3 ...... %c\n", info->data.ia32.ssse3 ? 'Y' : 'n'); + fprintf(stderr, " 3DNow! ..... %c\n", info->data.ia32._3dnow ? 'Y' : 'n'); + fprintf(stderr, " 3DNow!-ext . %c\n", info->data.ia32.ext3dnow? 'Y' : 'n'); + fprintf(stderr, " 3DNow!-MMX . %c\n", info->data.ia32.extmmx ? 'Y' : 'n'); +#endif + + /* + * now have to check for OS support of SSE/SSE2 + */ + if(info->data.ia32.fxsr || info->data.ia32.sse || info->data.ia32.sse2) { +#if defined FLAC__NO_SSE_OS + /* assume user knows better than us; turn it off */ + info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; +#elif defined FLAC__SSE_OS + /* assume user knows better than us; leave as detected above */ +#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) || defined(__APPLE__) + int sse = 0; + size_t len; + /* at least one of these must work: */ + len = sizeof(sse); sse = sse || (sysctlbyname("hw.instruction_sse", &sse, &len, NULL, 0) == 0 && sse); + len = sizeof(sse); sse = sse || (sysctlbyname("hw.optional.sse" , &sse, &len, NULL, 0) == 0 && sse); /* __APPLE__ ? */ + if(!sse) + info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; +#elif defined(__NetBSD__) || defined (__OpenBSD__) +# if __NetBSD_Version__ >= 105250000 || (defined __OpenBSD__) + int val = 0, mib[2] = { CTL_MACHDEP, CPU_SSE }; + size_t len = sizeof(val); + if(sysctl(mib, 2, &val, &len, NULL, 0) < 0 || !val) + info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; + else { /* double-check SSE2 */ + mib[1] = CPU_SSE2; + len = sizeof(val); + if(sysctl(mib, 2, &val, &len, NULL, 0) < 0 || !val) + info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; + } +# else + info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; +# endif +#elif defined(__linux__) + int sse = 0; + struct sigaction sigill_save; +#ifdef USE_OBSOLETE_SIGCONTEXT_FLAVOR + if(0 == sigaction(SIGILL, NULL, &sigill_save) && signal(SIGILL, (void (*)(int))sigill_handler_sse_os) != SIG_ERR) +#else + struct sigaction sigill_sse; + sigill_sse.sa_sigaction = sigill_handler_sse_os; + __sigemptyset(&sigill_sse.sa_mask); + sigill_sse.sa_flags = SA_SIGINFO | SA_RESETHAND; /* SA_RESETHAND just in case our SIGILL return jump breaks, so we don't get stuck in a loop */ + if(0 == sigaction(SIGILL, &sigill_sse, &sigill_save)) +#endif + { + /* http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html */ + /* see sigill_handler_sse_os() for an explanation of the following: */ + asm volatile ( + "xorl %0,%0\n\t" /* for some reason, still need to do this to clear 'sse' var */ + "xorps %%xmm0,%%xmm0\n\t" /* will cause SIGILL if unsupported by OS */ + "incl %0\n\t" /* SIGILL handler will jump over this */ + /* landing zone */ + "nop\n\t" /* SIGILL jump lands here if "inc" is 9 bytes */ + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" /* SIGILL jump lands here if "inc" is 3 bytes (expected) */ + "nop\n\t" + "nop" /* SIGILL jump lands here if "inc" is 1 byte */ + : "=r"(sse) + : "r"(sse) + ); + + sigaction(SIGILL, &sigill_save, NULL); + } + + if(!sse) + info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; +#elif defined(_MSC_VER) +# ifdef USE_TRY_CATCH_FLAVOR + _try { + __asm { +# if _MSC_VER <= 1200 + /* VC6 assembler doesn't know SSE, have to emit bytecode instead */ + _emit 0x0F + _emit 0x57 + _emit 0xC0 +# else + xorps xmm0,xmm0 +# endif + } + } + _except(EXCEPTION_EXECUTE_HANDLER) { + if (_exception_code() == STATUS_ILLEGAL_INSTRUCTION) + info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; + } +# else + int sse = 0; + LPTOP_LEVEL_EXCEPTION_FILTER save = SetUnhandledExceptionFilter(sigill_handler_sse_os); + /* see GCC version above for explanation */ + //@@@@@@ http://msdn2.microsoft.com/en-us/library/4ks26t93.aspx + //@@@@@@ http://www.codeproject.com/cpp/gccasm.asp + //@@@@@@ http://www.hick.org/~mmiller/msvc_inline_asm.html + __asm { +# if _MSC_VER <= 1200 + /* VC6 assembler doesn't know SSE, have to emit bytecode instead */ + _emit 0x0F + _emit 0x57 + _emit 0xC0 +# else + xorps xmm0,xmm0 +# endif + inc sse + nop + nop + nop + nop + nop + nop + nop + nop + nop + } + SetUnhandledExceptionFilter(save); + if(!sse) + info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; +# endif +#else + /* no way to test, disable to be safe */ + info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; +#endif +#ifdef DEBUG + fprintf(stderr, " SSE OS sup . %c\n", info->data.ia32.sse ? 'Y' : 'n'); +#endif + + } + } +#else + info->use_asm = false; +#endif + +/* + * PPC-specific + */ +#elif defined FLAC__CPU_PPC + info->type = FLAC__CPUINFO_TYPE_PPC; +# if !defined FLAC__NO_ASM + info->use_asm = true; +# ifdef FLAC__USE_ALTIVEC +# if defined FLAC__SYS_DARWIN + { + int val = 0, mib[2] = { CTL_HW, HW_VECTORUNIT }; + size_t len = sizeof(val); + info->data.ppc.altivec = !(sysctl(mib, 2, &val, &len, NULL, 0) || !val); + } + { + host_basic_info_data_t hostInfo; + mach_msg_type_number_t infoCount; + + infoCount = HOST_BASIC_INFO_COUNT; + host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)&hostInfo, &infoCount); + + info->data.ppc.ppc64 = (hostInfo.cpu_type == CPU_TYPE_POWERPC) && (hostInfo.cpu_subtype == CPU_SUBTYPE_POWERPC_970); + } +# else /* FLAC__USE_ALTIVEC && !FLAC__SYS_DARWIN */ + { + /* no Darwin, do it the brute-force way */ + /* @@@@@@ this is not thread-safe; replace with SSE OS method above or remove */ + info->data.ppc.altivec = 0; + info->data.ppc.ppc64 = 0; + + signal (SIGILL, sigill_handler); + canjump = 0; + if (!sigsetjmp (jmpbuf, 1)) { + canjump = 1; + + asm volatile ( + "mtspr 256, %0\n\t" + "vand %%v0, %%v0, %%v0" + : + : "r" (-1) + ); + + info->data.ppc.altivec = 1; + } + canjump = 0; + if (!sigsetjmp (jmpbuf, 1)) { + int x = 0; + canjump = 1; + + /* PPC64 hardware implements the cntlzd instruction */ + asm volatile ("cntlzd %0, %1" : "=r" (x) : "r" (x) ); + + info->data.ppc.ppc64 = 1; + } + signal (SIGILL, SIG_DFL); /*@@@@@@ should save and restore old signal */ + } +# endif +# else /* !FLAC__USE_ALTIVEC */ + info->data.ppc.altivec = 0; + info->data.ppc.ppc64 = 0; +# endif +# else + info->use_asm = false; +# endif + +/* + * unknown CPI + */ +#else + info->type = FLAC__CPUINFO_TYPE_UNKNOWN; + info->use_asm = false; +#endif +} diff --git a/src/FLAC/src/libFLAC/crc.c b/src/FLAC/src/libFLAC/crc.c new file mode 100644 index 00000000..463ab65e --- /dev/null +++ b/src/FLAC/src/libFLAC/crc.c @@ -0,0 +1,142 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include "private/crc.h" + +/* CRC-8, poly = x^8 + x^2 + x^1 + x^0, init = 0 */ + +FLAC__byte const FLAC__crc8_table[256] = { + 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, + 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, + 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, + 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, + 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, + 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, + 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, + 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD, + 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, + 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, + 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, + 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, + 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, + 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, + 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, + 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, + 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, + 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, + 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, + 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, + 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, + 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, + 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, + 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34, + 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, + 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, + 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, + 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, + 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, + 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, + 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, + 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 +}; + +/* CRC-16, poly = x^16 + x^15 + x^2 + x^0, init = 0 */ + +unsigned FLAC__crc16_table[256] = { + 0x0000, 0x8005, 0x800f, 0x000a, 0x801b, 0x001e, 0x0014, 0x8011, + 0x8033, 0x0036, 0x003c, 0x8039, 0x0028, 0x802d, 0x8027, 0x0022, + 0x8063, 0x0066, 0x006c, 0x8069, 0x0078, 0x807d, 0x8077, 0x0072, + 0x0050, 0x8055, 0x805f, 0x005a, 0x804b, 0x004e, 0x0044, 0x8041, + 0x80c3, 0x00c6, 0x00cc, 0x80c9, 0x00d8, 0x80dd, 0x80d7, 0x00d2, + 0x00f0, 0x80f5, 0x80ff, 0x00fa, 0x80eb, 0x00ee, 0x00e4, 0x80e1, + 0x00a0, 0x80a5, 0x80af, 0x00aa, 0x80bb, 0x00be, 0x00b4, 0x80b1, + 0x8093, 0x0096, 0x009c, 0x8099, 0x0088, 0x808d, 0x8087, 0x0082, + 0x8183, 0x0186, 0x018c, 0x8189, 0x0198, 0x819d, 0x8197, 0x0192, + 0x01b0, 0x81b5, 0x81bf, 0x01ba, 0x81ab, 0x01ae, 0x01a4, 0x81a1, + 0x01e0, 0x81e5, 0x81ef, 0x01ea, 0x81fb, 0x01fe, 0x01f4, 0x81f1, + 0x81d3, 0x01d6, 0x01dc, 0x81d9, 0x01c8, 0x81cd, 0x81c7, 0x01c2, + 0x0140, 0x8145, 0x814f, 0x014a, 0x815b, 0x015e, 0x0154, 0x8151, + 0x8173, 0x0176, 0x017c, 0x8179, 0x0168, 0x816d, 0x8167, 0x0162, + 0x8123, 0x0126, 0x012c, 0x8129, 0x0138, 0x813d, 0x8137, 0x0132, + 0x0110, 0x8115, 0x811f, 0x011a, 0x810b, 0x010e, 0x0104, 0x8101, + 0x8303, 0x0306, 0x030c, 0x8309, 0x0318, 0x831d, 0x8317, 0x0312, + 0x0330, 0x8335, 0x833f, 0x033a, 0x832b, 0x032e, 0x0324, 0x8321, + 0x0360, 0x8365, 0x836f, 0x036a, 0x837b, 0x037e, 0x0374, 0x8371, + 0x8353, 0x0356, 0x035c, 0x8359, 0x0348, 0x834d, 0x8347, 0x0342, + 0x03c0, 0x83c5, 0x83cf, 0x03ca, 0x83db, 0x03de, 0x03d4, 0x83d1, + 0x83f3, 0x03f6, 0x03fc, 0x83f9, 0x03e8, 0x83ed, 0x83e7, 0x03e2, + 0x83a3, 0x03a6, 0x03ac, 0x83a9, 0x03b8, 0x83bd, 0x83b7, 0x03b2, + 0x0390, 0x8395, 0x839f, 0x039a, 0x838b, 0x038e, 0x0384, 0x8381, + 0x0280, 0x8285, 0x828f, 0x028a, 0x829b, 0x029e, 0x0294, 0x8291, + 0x82b3, 0x02b6, 0x02bc, 0x82b9, 0x02a8, 0x82ad, 0x82a7, 0x02a2, + 0x82e3, 0x02e6, 0x02ec, 0x82e9, 0x02f8, 0x82fd, 0x82f7, 0x02f2, + 0x02d0, 0x82d5, 0x82df, 0x02da, 0x82cb, 0x02ce, 0x02c4, 0x82c1, + 0x8243, 0x0246, 0x024c, 0x8249, 0x0258, 0x825d, 0x8257, 0x0252, + 0x0270, 0x8275, 0x827f, 0x027a, 0x826b, 0x026e, 0x0264, 0x8261, + 0x0220, 0x8225, 0x822f, 0x022a, 0x823b, 0x023e, 0x0234, 0x8231, + 0x8213, 0x0216, 0x021c, 0x8219, 0x0208, 0x820d, 0x8207, 0x0202 +}; + + +void FLAC__crc8_update(const FLAC__byte data, FLAC__uint8 *crc) +{ + *crc = FLAC__crc8_table[*crc ^ data]; +} + +void FLAC__crc8_update_block(const FLAC__byte *data, unsigned len, FLAC__uint8 *crc) +{ + while(len--) + *crc = FLAC__crc8_table[*crc ^ *data++]; +} + +FLAC__uint8 FLAC__crc8(const FLAC__byte *data, unsigned len) +{ + FLAC__uint8 crc = 0; + + while(len--) + crc = FLAC__crc8_table[crc ^ *data++]; + + return crc; +} + +unsigned FLAC__crc16(const FLAC__byte *data, unsigned len) +{ + unsigned crc = 0; + + while(len--) + crc = ((crc<<8) ^ FLAC__crc16_table[(crc>>8) ^ *data++]) & 0xffff; + + return crc; +} diff --git a/src/FLAC/src/libFLAC/fixed.c b/src/FLAC/src/libFLAC/fixed.c new file mode 100644 index 00000000..1a3aac09 --- /dev/null +++ b/src/FLAC/src/libFLAC/fixed.c @@ -0,0 +1,435 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include +#include "private/bitmath.h" +#include "private/fixed.h" +#include "FLAC/assert.h" + +#ifndef M_LN2 +/* math.h in VC++ doesn't seem to have this (how Microsoft is that?) */ +#define M_LN2 0.69314718055994530942 +#endif + +#ifdef min +#undef min +#endif +#define min(x,y) ((x) < (y)? (x) : (y)) + +#ifdef local_abs +#undef local_abs +#endif +#define local_abs(x) ((unsigned)((x)<0? -(x) : (x))) + +#ifdef FLAC__INTEGER_ONLY_LIBRARY +/* rbps stands for residual bits per sample + * + * (ln(2) * err) + * rbps = log (-----------) + * 2 ( n ) + */ +static FLAC__fixedpoint local__compute_rbps_integerized(FLAC__uint32 err, FLAC__uint32 n) +{ + FLAC__uint32 rbps; + unsigned bits; /* the number of bits required to represent a number */ + int fracbits; /* the number of bits of rbps that comprise the fractional part */ + + FLAC__ASSERT(sizeof(rbps) == sizeof(FLAC__fixedpoint)); + FLAC__ASSERT(err > 0); + FLAC__ASSERT(n > 0); + + FLAC__ASSERT(n <= FLAC__MAX_BLOCK_SIZE); + if(err <= n) + return 0; + /* + * The above two things tell us 1) n fits in 16 bits; 2) err/n > 1. + * These allow us later to know we won't lose too much precision in the + * fixed-point division (err< 0); + bits = FLAC__bitmath_ilog2(err)+1; + if(bits > 16) { + err >>= (bits-16); + fracbits -= (bits-16); + } + rbps = (FLAC__uint32)err; + + /* Multiply by fixed-point version of ln(2), with 16 fractional bits */ + rbps *= FLAC__FP_LN2; + fracbits += 16; + FLAC__ASSERT(fracbits >= 0); + + /* FLAC__fixedpoint_log2 requires fracbits%4 to be 0 */ + { + const int f = fracbits & 3; + if(f) { + rbps >>= f; + fracbits -= f; + } + } + + rbps = FLAC__fixedpoint_log2(rbps, fracbits, (unsigned)(-1)); + + if(rbps == 0) + return 0; + + /* + * The return value must have 16 fractional bits. Since the whole part + * of the base-2 log of a 32 bit number must fit in 5 bits, and fracbits + * must be >= -3, these assertion allows us to be able to shift rbps + * left if necessary to get 16 fracbits without losing any bits of the + * whole part of rbps. + * + * There is a slight chance due to accumulated error that the whole part + * will require 6 bits, so we use 6 in the assertion. Really though as + * long as it fits in 13 bits (32 - (16 - (-3))) we are fine. + */ + FLAC__ASSERT((int)FLAC__bitmath_ilog2(rbps)+1 <= fracbits + 6); + FLAC__ASSERT(fracbits >= -3); + + /* now shift the decimal point into place */ + if(fracbits < 16) + return rbps << (16-fracbits); + else if(fracbits > 16) + return rbps >> (fracbits-16); + else + return rbps; +} + +static FLAC__fixedpoint local__compute_rbps_wide_integerized(FLAC__uint64 err, FLAC__uint32 n) +{ + FLAC__uint32 rbps; + unsigned bits; /* the number of bits required to represent a number */ + int fracbits; /* the number of bits of rbps that comprise the fractional part */ + + FLAC__ASSERT(sizeof(rbps) == sizeof(FLAC__fixedpoint)); + FLAC__ASSERT(err > 0); + FLAC__ASSERT(n > 0); + + FLAC__ASSERT(n <= FLAC__MAX_BLOCK_SIZE); + if(err <= n) + return 0; + /* + * The above two things tell us 1) n fits in 16 bits; 2) err/n > 1. + * These allow us later to know we won't lose too much precision in the + * fixed-point division (err< 0); + bits = FLAC__bitmath_ilog2_wide(err)+1; + if(bits > 16) { + err >>= (bits-16); + fracbits -= (bits-16); + } + rbps = (FLAC__uint32)err; + + /* Multiply by fixed-point version of ln(2), with 16 fractional bits */ + rbps *= FLAC__FP_LN2; + fracbits += 16; + FLAC__ASSERT(fracbits >= 0); + + /* FLAC__fixedpoint_log2 requires fracbits%4 to be 0 */ + { + const int f = fracbits & 3; + if(f) { + rbps >>= f; + fracbits -= f; + } + } + + rbps = FLAC__fixedpoint_log2(rbps, fracbits, (unsigned)(-1)); + + if(rbps == 0) + return 0; + + /* + * The return value must have 16 fractional bits. Since the whole part + * of the base-2 log of a 32 bit number must fit in 5 bits, and fracbits + * must be >= -3, these assertion allows us to be able to shift rbps + * left if necessary to get 16 fracbits without losing any bits of the + * whole part of rbps. + * + * There is a slight chance due to accumulated error that the whole part + * will require 6 bits, so we use 6 in the assertion. Really though as + * long as it fits in 13 bits (32 - (16 - (-3))) we are fine. + */ + FLAC__ASSERT((int)FLAC__bitmath_ilog2(rbps)+1 <= fracbits + 6); + FLAC__ASSERT(fracbits >= -3); + + /* now shift the decimal point into place */ + if(fracbits < 16) + return rbps << (16-fracbits); + else if(fracbits > 16) + return rbps >> (fracbits-16); + else + return rbps; +} +#endif + +#ifndef FLAC__INTEGER_ONLY_LIBRARY +unsigned FLAC__fixed_compute_best_predictor(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]) +#else +unsigned FLAC__fixed_compute_best_predictor(const FLAC__int32 data[], unsigned data_len, FLAC__fixedpoint residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]) +#endif +{ + FLAC__int32 last_error_0 = data[-1]; + FLAC__int32 last_error_1 = data[-1] - data[-2]; + FLAC__int32 last_error_2 = last_error_1 - (data[-2] - data[-3]); + FLAC__int32 last_error_3 = last_error_2 - (data[-2] - 2*data[-3] + data[-4]); + FLAC__int32 error, save; + FLAC__uint32 total_error_0 = 0, total_error_1 = 0, total_error_2 = 0, total_error_3 = 0, total_error_4 = 0; + unsigned i, order; + + for(i = 0; i < data_len; i++) { + error = data[i] ; total_error_0 += local_abs(error); save = error; + error -= last_error_0; total_error_1 += local_abs(error); last_error_0 = save; save = error; + error -= last_error_1; total_error_2 += local_abs(error); last_error_1 = save; save = error; + error -= last_error_2; total_error_3 += local_abs(error); last_error_2 = save; save = error; + error -= last_error_3; total_error_4 += local_abs(error); last_error_3 = save; + } + + if(total_error_0 < min(min(min(total_error_1, total_error_2), total_error_3), total_error_4)) + order = 0; + else if(total_error_1 < min(min(total_error_2, total_error_3), total_error_4)) + order = 1; + else if(total_error_2 < min(total_error_3, total_error_4)) + order = 2; + else if(total_error_3 < total_error_4) + order = 3; + else + order = 4; + + /* Estimate the expected number of bits per residual signal sample. */ + /* 'total_error*' is linearly related to the variance of the residual */ + /* signal, so we use it directly to compute E(|x|) */ + FLAC__ASSERT(data_len > 0 || total_error_0 == 0); + FLAC__ASSERT(data_len > 0 || total_error_1 == 0); + FLAC__ASSERT(data_len > 0 || total_error_2 == 0); + FLAC__ASSERT(data_len > 0 || total_error_3 == 0); + FLAC__ASSERT(data_len > 0 || total_error_4 == 0); +#ifndef FLAC__INTEGER_ONLY_LIBRARY + residual_bits_per_sample[0] = (FLAC__float)((total_error_0 > 0) ? log(M_LN2 * (FLAC__double)total_error_0 / (FLAC__double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[1] = (FLAC__float)((total_error_1 > 0) ? log(M_LN2 * (FLAC__double)total_error_1 / (FLAC__double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[2] = (FLAC__float)((total_error_2 > 0) ? log(M_LN2 * (FLAC__double)total_error_2 / (FLAC__double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[3] = (FLAC__float)((total_error_3 > 0) ? log(M_LN2 * (FLAC__double)total_error_3 / (FLAC__double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[4] = (FLAC__float)((total_error_4 > 0) ? log(M_LN2 * (FLAC__double)total_error_4 / (FLAC__double)data_len) / M_LN2 : 0.0); +#else + residual_bits_per_sample[0] = (total_error_0 > 0) ? local__compute_rbps_integerized(total_error_0, data_len) : 0; + residual_bits_per_sample[1] = (total_error_1 > 0) ? local__compute_rbps_integerized(total_error_1, data_len) : 0; + residual_bits_per_sample[2] = (total_error_2 > 0) ? local__compute_rbps_integerized(total_error_2, data_len) : 0; + residual_bits_per_sample[3] = (total_error_3 > 0) ? local__compute_rbps_integerized(total_error_3, data_len) : 0; + residual_bits_per_sample[4] = (total_error_4 > 0) ? local__compute_rbps_integerized(total_error_4, data_len) : 0; +#endif + + return order; +} + +#ifndef FLAC__INTEGER_ONLY_LIBRARY +unsigned FLAC__fixed_compute_best_predictor_wide(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]) +#else +unsigned FLAC__fixed_compute_best_predictor_wide(const FLAC__int32 data[], unsigned data_len, FLAC__fixedpoint residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]) +#endif +{ + FLAC__int32 last_error_0 = data[-1]; + FLAC__int32 last_error_1 = data[-1] - data[-2]; + FLAC__int32 last_error_2 = last_error_1 - (data[-2] - data[-3]); + FLAC__int32 last_error_3 = last_error_2 - (data[-2] - 2*data[-3] + data[-4]); + FLAC__int32 error, save; + /* total_error_* are 64-bits to avoid overflow when encoding + * erratic signals when the bits-per-sample and blocksize are + * large. + */ + FLAC__uint64 total_error_0 = 0, total_error_1 = 0, total_error_2 = 0, total_error_3 = 0, total_error_4 = 0; + unsigned i, order; + + for(i = 0; i < data_len; i++) { + error = data[i] ; total_error_0 += local_abs(error); save = error; + error -= last_error_0; total_error_1 += local_abs(error); last_error_0 = save; save = error; + error -= last_error_1; total_error_2 += local_abs(error); last_error_1 = save; save = error; + error -= last_error_2; total_error_3 += local_abs(error); last_error_2 = save; save = error; + error -= last_error_3; total_error_4 += local_abs(error); last_error_3 = save; + } + + if(total_error_0 < min(min(min(total_error_1, total_error_2), total_error_3), total_error_4)) + order = 0; + else if(total_error_1 < min(min(total_error_2, total_error_3), total_error_4)) + order = 1; + else if(total_error_2 < min(total_error_3, total_error_4)) + order = 2; + else if(total_error_3 < total_error_4) + order = 3; + else + order = 4; + + /* Estimate the expected number of bits per residual signal sample. */ + /* 'total_error*' is linearly related to the variance of the residual */ + /* signal, so we use it directly to compute E(|x|) */ + FLAC__ASSERT(data_len > 0 || total_error_0 == 0); + FLAC__ASSERT(data_len > 0 || total_error_1 == 0); + FLAC__ASSERT(data_len > 0 || total_error_2 == 0); + FLAC__ASSERT(data_len > 0 || total_error_3 == 0); + FLAC__ASSERT(data_len > 0 || total_error_4 == 0); +#ifndef FLAC__INTEGER_ONLY_LIBRARY +#if defined _MSC_VER || defined __MINGW32__ + /* with MSVC you have to spoon feed it the casting */ + residual_bits_per_sample[0] = (FLAC__float)((total_error_0 > 0) ? log(M_LN2 * (FLAC__double)(FLAC__int64)total_error_0 / (FLAC__double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[1] = (FLAC__float)((total_error_1 > 0) ? log(M_LN2 * (FLAC__double)(FLAC__int64)total_error_1 / (FLAC__double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[2] = (FLAC__float)((total_error_2 > 0) ? log(M_LN2 * (FLAC__double)(FLAC__int64)total_error_2 / (FLAC__double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[3] = (FLAC__float)((total_error_3 > 0) ? log(M_LN2 * (FLAC__double)(FLAC__int64)total_error_3 / (FLAC__double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[4] = (FLAC__float)((total_error_4 > 0) ? log(M_LN2 * (FLAC__double)(FLAC__int64)total_error_4 / (FLAC__double)data_len) / M_LN2 : 0.0); +#else + residual_bits_per_sample[0] = (FLAC__float)((total_error_0 > 0) ? log(M_LN2 * (FLAC__double)total_error_0 / (FLAC__double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[1] = (FLAC__float)((total_error_1 > 0) ? log(M_LN2 * (FLAC__double)total_error_1 / (FLAC__double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[2] = (FLAC__float)((total_error_2 > 0) ? log(M_LN2 * (FLAC__double)total_error_2 / (FLAC__double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[3] = (FLAC__float)((total_error_3 > 0) ? log(M_LN2 * (FLAC__double)total_error_3 / (FLAC__double)data_len) / M_LN2 : 0.0); + residual_bits_per_sample[4] = (FLAC__float)((total_error_4 > 0) ? log(M_LN2 * (FLAC__double)total_error_4 / (FLAC__double)data_len) / M_LN2 : 0.0); +#endif +#else + residual_bits_per_sample[0] = (total_error_0 > 0) ? local__compute_rbps_wide_integerized(total_error_0, data_len) : 0; + residual_bits_per_sample[1] = (total_error_1 > 0) ? local__compute_rbps_wide_integerized(total_error_1, data_len) : 0; + residual_bits_per_sample[2] = (total_error_2 > 0) ? local__compute_rbps_wide_integerized(total_error_2, data_len) : 0; + residual_bits_per_sample[3] = (total_error_3 > 0) ? local__compute_rbps_wide_integerized(total_error_3, data_len) : 0; + residual_bits_per_sample[4] = (total_error_4 > 0) ? local__compute_rbps_wide_integerized(total_error_4, data_len) : 0; +#endif + + return order; +} + +void FLAC__fixed_compute_residual(const FLAC__int32 data[], unsigned data_len, unsigned order, FLAC__int32 residual[]) +{ + const int idata_len = (int)data_len; + int i; + + switch(order) { + case 0: + FLAC__ASSERT(sizeof(residual[0]) == sizeof(data[0])); + memcpy(residual, data, sizeof(residual[0])*data_len); + break; + case 1: + for(i = 0; i < idata_len; i++) + residual[i] = data[i] - data[i-1]; + break; + case 2: + for(i = 0; i < idata_len; i++) +#if 1 /* OPT: may be faster with some compilers on some systems */ + residual[i] = data[i] - (data[i-1] << 1) + data[i-2]; +#else + residual[i] = data[i] - 2*data[i-1] + data[i-2]; +#endif + break; + case 3: + for(i = 0; i < idata_len; i++) +#if 1 /* OPT: may be faster with some compilers on some systems */ + residual[i] = data[i] - (((data[i-1]-data[i-2])<<1) + (data[i-1]-data[i-2])) - data[i-3]; +#else + residual[i] = data[i] - 3*data[i-1] + 3*data[i-2] - data[i-3]; +#endif + break; + case 4: + for(i = 0; i < idata_len; i++) +#if 1 /* OPT: may be faster with some compilers on some systems */ + residual[i] = data[i] - ((data[i-1]+data[i-3])<<2) + ((data[i-2]<<2) + (data[i-2]<<1)) + data[i-4]; +#else + residual[i] = data[i] - 4*data[i-1] + 6*data[i-2] - 4*data[i-3] + data[i-4]; +#endif + break; + default: + FLAC__ASSERT(0); + } +} + +void FLAC__fixed_restore_signal(const FLAC__int32 residual[], unsigned data_len, unsigned order, FLAC__int32 data[]) +{ + int i, idata_len = (int)data_len; + + switch(order) { + case 0: + FLAC__ASSERT(sizeof(residual[0]) == sizeof(data[0])); + memcpy(data, residual, sizeof(residual[0])*data_len); + break; + case 1: + for(i = 0; i < idata_len; i++) + data[i] = residual[i] + data[i-1]; + break; + case 2: + for(i = 0; i < idata_len; i++) +#if 1 /* OPT: may be faster with some compilers on some systems */ + data[i] = residual[i] + (data[i-1]<<1) - data[i-2]; +#else + data[i] = residual[i] + 2*data[i-1] - data[i-2]; +#endif + break; + case 3: + for(i = 0; i < idata_len; i++) +#if 1 /* OPT: may be faster with some compilers on some systems */ + data[i] = residual[i] + (((data[i-1]-data[i-2])<<1) + (data[i-1]-data[i-2])) + data[i-3]; +#else + data[i] = residual[i] + 3*data[i-1] - 3*data[i-2] + data[i-3]; +#endif + break; + case 4: + for(i = 0; i < idata_len; i++) +#if 1 /* OPT: may be faster with some compilers on some systems */ + data[i] = residual[i] + ((data[i-1]+data[i-3])<<2) - ((data[i-2]<<2) + (data[i-2]<<1)) - data[i-4]; +#else + data[i] = residual[i] + 4*data[i-1] - 6*data[i-2] + 4*data[i-3] - data[i-4]; +#endif + break; + default: + FLAC__ASSERT(0); + } +} diff --git a/src/FLAC/src/libFLAC/flac.pc.in b/src/FLAC/src/libFLAC/flac.pc.in new file mode 100644 index 00000000..8fc39f83 --- /dev/null +++ b/src/FLAC/src/libFLAC/flac.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: FLAC +Description: Free Lossless Audio Codec Library +Version: @VERSION@ +Libs: -L${libdir} -lFLAC -lm +Cflags: -I${includedir}/FLAC diff --git a/src/FLAC/src/libFLAC/float.c b/src/FLAC/src/libFLAC/float.c new file mode 100644 index 00000000..8e192802 --- /dev/null +++ b/src/FLAC/src/libFLAC/float.c @@ -0,0 +1,308 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include "FLAC/assert.h" + +#include "private/float.h" + +#ifdef FLAC__INTEGER_ONLY_LIBRARY + +/* adjust for compilers that can't understand using LLU suffix for uint64_t literals */ +#ifdef _MSC_VER +#define FLAC__U64L(x) x +#else +#define FLAC__U64L(x) x##LLU +#endif + +const FLAC__fixedpoint FLAC__FP_ZERO = 0; +const FLAC__fixedpoint FLAC__FP_ONE_HALF = 0x00008000; +const FLAC__fixedpoint FLAC__FP_ONE = 0x00010000; +const FLAC__fixedpoint FLAC__FP_LN2 = 45426; +const FLAC__fixedpoint FLAC__FP_E = 178145; + +/* Lookup tables for Knuth's logarithm algorithm */ +#define LOG2_LOOKUP_PRECISION 16 +static const FLAC__uint32 log2_lookup[][LOG2_LOOKUP_PRECISION] = { + { + /* + * 0 fraction bits + */ + /* undefined */ 0x00000000, + /* lg(2/1) = */ 0x00000001, + /* lg(4/3) = */ 0x00000000, + /* lg(8/7) = */ 0x00000000, + /* lg(16/15) = */ 0x00000000, + /* lg(32/31) = */ 0x00000000, + /* lg(64/63) = */ 0x00000000, + /* lg(128/127) = */ 0x00000000, + /* lg(256/255) = */ 0x00000000, + /* lg(512/511) = */ 0x00000000, + /* lg(1024/1023) = */ 0x00000000, + /* lg(2048/2047) = */ 0x00000000, + /* lg(4096/4095) = */ 0x00000000, + /* lg(8192/8191) = */ 0x00000000, + /* lg(16384/16383) = */ 0x00000000, + /* lg(32768/32767) = */ 0x00000000 + }, + { + /* + * 4 fraction bits + */ + /* undefined */ 0x00000000, + /* lg(2/1) = */ 0x00000010, + /* lg(4/3) = */ 0x00000007, + /* lg(8/7) = */ 0x00000003, + /* lg(16/15) = */ 0x00000001, + /* lg(32/31) = */ 0x00000001, + /* lg(64/63) = */ 0x00000000, + /* lg(128/127) = */ 0x00000000, + /* lg(256/255) = */ 0x00000000, + /* lg(512/511) = */ 0x00000000, + /* lg(1024/1023) = */ 0x00000000, + /* lg(2048/2047) = */ 0x00000000, + /* lg(4096/4095) = */ 0x00000000, + /* lg(8192/8191) = */ 0x00000000, + /* lg(16384/16383) = */ 0x00000000, + /* lg(32768/32767) = */ 0x00000000 + }, + { + /* + * 8 fraction bits + */ + /* undefined */ 0x00000000, + /* lg(2/1) = */ 0x00000100, + /* lg(4/3) = */ 0x0000006a, + /* lg(8/7) = */ 0x00000031, + /* lg(16/15) = */ 0x00000018, + /* lg(32/31) = */ 0x0000000c, + /* lg(64/63) = */ 0x00000006, + /* lg(128/127) = */ 0x00000003, + /* lg(256/255) = */ 0x00000001, + /* lg(512/511) = */ 0x00000001, + /* lg(1024/1023) = */ 0x00000000, + /* lg(2048/2047) = */ 0x00000000, + /* lg(4096/4095) = */ 0x00000000, + /* lg(8192/8191) = */ 0x00000000, + /* lg(16384/16383) = */ 0x00000000, + /* lg(32768/32767) = */ 0x00000000 + }, + { + /* + * 12 fraction bits + */ + /* undefined */ 0x00000000, + /* lg(2/1) = */ 0x00001000, + /* lg(4/3) = */ 0x000006a4, + /* lg(8/7) = */ 0x00000315, + /* lg(16/15) = */ 0x0000017d, + /* lg(32/31) = */ 0x000000bc, + /* lg(64/63) = */ 0x0000005d, + /* lg(128/127) = */ 0x0000002e, + /* lg(256/255) = */ 0x00000017, + /* lg(512/511) = */ 0x0000000c, + /* lg(1024/1023) = */ 0x00000006, + /* lg(2048/2047) = */ 0x00000003, + /* lg(4096/4095) = */ 0x00000001, + /* lg(8192/8191) = */ 0x00000001, + /* lg(16384/16383) = */ 0x00000000, + /* lg(32768/32767) = */ 0x00000000 + }, + { + /* + * 16 fraction bits + */ + /* undefined */ 0x00000000, + /* lg(2/1) = */ 0x00010000, + /* lg(4/3) = */ 0x00006a40, + /* lg(8/7) = */ 0x00003151, + /* lg(16/15) = */ 0x000017d6, + /* lg(32/31) = */ 0x00000bba, + /* lg(64/63) = */ 0x000005d1, + /* lg(128/127) = */ 0x000002e6, + /* lg(256/255) = */ 0x00000172, + /* lg(512/511) = */ 0x000000b9, + /* lg(1024/1023) = */ 0x0000005c, + /* lg(2048/2047) = */ 0x0000002e, + /* lg(4096/4095) = */ 0x00000017, + /* lg(8192/8191) = */ 0x0000000c, + /* lg(16384/16383) = */ 0x00000006, + /* lg(32768/32767) = */ 0x00000003 + }, + { + /* + * 20 fraction bits + */ + /* undefined */ 0x00000000, + /* lg(2/1) = */ 0x00100000, + /* lg(4/3) = */ 0x0006a3fe, + /* lg(8/7) = */ 0x00031513, + /* lg(16/15) = */ 0x00017d60, + /* lg(32/31) = */ 0x0000bb9d, + /* lg(64/63) = */ 0x00005d10, + /* lg(128/127) = */ 0x00002e59, + /* lg(256/255) = */ 0x00001721, + /* lg(512/511) = */ 0x00000b8e, + /* lg(1024/1023) = */ 0x000005c6, + /* lg(2048/2047) = */ 0x000002e3, + /* lg(4096/4095) = */ 0x00000171, + /* lg(8192/8191) = */ 0x000000b9, + /* lg(16384/16383) = */ 0x0000005c, + /* lg(32768/32767) = */ 0x0000002e + }, + { + /* + * 24 fraction bits + */ + /* undefined */ 0x00000000, + /* lg(2/1) = */ 0x01000000, + /* lg(4/3) = */ 0x006a3fe6, + /* lg(8/7) = */ 0x00315130, + /* lg(16/15) = */ 0x0017d605, + /* lg(32/31) = */ 0x000bb9ca, + /* lg(64/63) = */ 0x0005d0fc, + /* lg(128/127) = */ 0x0002e58f, + /* lg(256/255) = */ 0x0001720e, + /* lg(512/511) = */ 0x0000b8d8, + /* lg(1024/1023) = */ 0x00005c61, + /* lg(2048/2047) = */ 0x00002e2d, + /* lg(4096/4095) = */ 0x00001716, + /* lg(8192/8191) = */ 0x00000b8b, + /* lg(16384/16383) = */ 0x000005c5, + /* lg(32768/32767) = */ 0x000002e3 + }, + { + /* + * 28 fraction bits + */ + /* undefined */ 0x00000000, + /* lg(2/1) = */ 0x10000000, + /* lg(4/3) = */ 0x06a3fe5c, + /* lg(8/7) = */ 0x03151301, + /* lg(16/15) = */ 0x017d6049, + /* lg(32/31) = */ 0x00bb9ca6, + /* lg(64/63) = */ 0x005d0fba, + /* lg(128/127) = */ 0x002e58f7, + /* lg(256/255) = */ 0x001720da, + /* lg(512/511) = */ 0x000b8d87, + /* lg(1024/1023) = */ 0x0005c60b, + /* lg(2048/2047) = */ 0x0002e2d7, + /* lg(4096/4095) = */ 0x00017160, + /* lg(8192/8191) = */ 0x0000b8ad, + /* lg(16384/16383) = */ 0x00005c56, + /* lg(32768/32767) = */ 0x00002e2b + } +}; + +#if 0 +static const FLAC__uint64 log2_lookup_wide[] = { + { + /* + * 32 fraction bits + */ + /* undefined */ 0x00000000, + /* lg(2/1) = */ FLAC__U64L(0x100000000), + /* lg(4/3) = */ FLAC__U64L(0x6a3fe5c6), + /* lg(8/7) = */ FLAC__U64L(0x31513015), + /* lg(16/15) = */ FLAC__U64L(0x17d60497), + /* lg(32/31) = */ FLAC__U64L(0x0bb9ca65), + /* lg(64/63) = */ FLAC__U64L(0x05d0fba2), + /* lg(128/127) = */ FLAC__U64L(0x02e58f74), + /* lg(256/255) = */ FLAC__U64L(0x01720d9c), + /* lg(512/511) = */ FLAC__U64L(0x00b8d875), + /* lg(1024/1023) = */ FLAC__U64L(0x005c60aa), + /* lg(2048/2047) = */ FLAC__U64L(0x002e2d72), + /* lg(4096/4095) = */ FLAC__U64L(0x00171600), + /* lg(8192/8191) = */ FLAC__U64L(0x000b8ad2), + /* lg(16384/16383) = */ FLAC__U64L(0x0005c55d), + /* lg(32768/32767) = */ FLAC__U64L(0x0002e2ac) + }, + { + /* + * 48 fraction bits + */ + /* undefined */ 0x00000000, + /* lg(2/1) = */ FLAC__U64L(0x1000000000000), + /* lg(4/3) = */ FLAC__U64L(0x6a3fe5c60429), + /* lg(8/7) = */ FLAC__U64L(0x315130157f7a), + /* lg(16/15) = */ FLAC__U64L(0x17d60496cfbb), + /* lg(32/31) = */ FLAC__U64L(0xbb9ca64ecac), + /* lg(64/63) = */ FLAC__U64L(0x5d0fba187cd), + /* lg(128/127) = */ FLAC__U64L(0x2e58f7441ee), + /* lg(256/255) = */ FLAC__U64L(0x1720d9c06a8), + /* lg(512/511) = */ FLAC__U64L(0xb8d8752173), + /* lg(1024/1023) = */ FLAC__U64L(0x5c60aa252e), + /* lg(2048/2047) = */ FLAC__U64L(0x2e2d71b0d8), + /* lg(4096/4095) = */ FLAC__U64L(0x1716001719), + /* lg(8192/8191) = */ FLAC__U64L(0xb8ad1de1b), + /* lg(16384/16383) = */ FLAC__U64L(0x5c55d640d), + /* lg(32768/32767) = */ FLAC__U64L(0x2e2abcf52) + } +}; +#endif + +FLAC__uint32 FLAC__fixedpoint_log2(FLAC__uint32 x, unsigned fracbits, unsigned precision) +{ + const FLAC__uint32 ONE = (1u << fracbits); + const FLAC__uint32 *table = log2_lookup[fracbits >> 2]; + + FLAC__ASSERT(fracbits < 32); + FLAC__ASSERT((fracbits & 0x3) == 0); + + if(x < ONE) + return 0; + + if(precision > LOG2_LOOKUP_PRECISION) + precision = LOG2_LOOKUP_PRECISION; + + /* Knuth's algorithm for computing logarithms, optimized for base-2 with lookup tables */ + { + FLAC__uint32 y = 0; + FLAC__uint32 z = x >> 1, k = 1; + while (x > ONE && k < precision) { + if (x - z >= ONE) { + x -= z; + z = x >> k; + y += table[k]; + } + else { + z >>= 1; + k++; + } + } + return y; + } +} + +#endif /* defined FLAC__INTEGER_ONLY_LIBRARY */ diff --git a/src/FLAC/src/libFLAC/format.c b/src/FLAC/src/libFLAC/format.c new file mode 100644 index 00000000..de911ac3 --- /dev/null +++ b/src/FLAC/src/libFLAC/format.c @@ -0,0 +1,583 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include /* for qsort() */ +#include "FLAC/assert.h" +#include "FLAC/format.h" +#include "private/format.h" + +#ifndef FLaC__INLINE +#define FLaC__INLINE +#endif + +#ifdef min +#undef min +#endif +#define min(a,b) ((a)<(b)?(a):(b)) + +/* adjust for compilers that can't understand using LLU suffix for uint64_t literals */ +#ifdef _MSC_VER +#define FLAC__U64L(x) x +#else +#define FLAC__U64L(x) x##LLU +#endif + +/* VERSION should come from configure */ +FLAC_API const char *FLAC__VERSION_STRING = VERSION; + +#if defined _MSC_VER || defined __BORLANDC__ || defined __MINW32__ +/* yet one more hack because of MSVC6: */ +FLAC_API const char *FLAC__VENDOR_STRING = "reference libFLAC 1.1.4 20070213"; +#else +FLAC_API const char *FLAC__VENDOR_STRING = "reference libFLAC " VERSION " 20070213"; +#endif + +FLAC_API const FLAC__byte FLAC__STREAM_SYNC_STRING[4] = { 'f','L','a','C' }; +FLAC_API const unsigned FLAC__STREAM_SYNC = 0x664C6143; +FLAC_API const unsigned FLAC__STREAM_SYNC_LEN = 32; /* bits */ + +FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN = 16; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN = 16; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN = 24; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN = 24; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN = 20; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN = 3; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN = 5; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN = 36; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MD5SUM_LEN = 128; /* bits */ + +FLAC_API const unsigned FLAC__STREAM_METADATA_APPLICATION_ID_LEN = 32; /* bits */ + +FLAC_API const unsigned FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN = 64; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN = 64; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN = 16; /* bits */ + +FLAC_API const FLAC__uint64 FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER = FLAC__U64L(0xffffffffffffffff); + +FLAC_API const unsigned FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN = 32; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN = 32; /* bits */ + +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN = 64; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN = 8; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN = 3*8; /* bits */ + +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN = 64; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN = 8; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN = 12*8; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN = 1; /* bit */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN = 1; /* bit */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN = 6+13*8; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN = 8; /* bits */ + +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN = 128*8; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN = 64; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN = 1; /* bit */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN = 7+258*8; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN = 8; /* bits */ + +FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_TYPE_LEN = 32; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN = 32; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN = 32; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN = 32; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN = 32; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN = 32; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_COLORS_LEN = 32; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN = 32; /* bits */ + +FLAC_API const unsigned FLAC__STREAM_METADATA_IS_LAST_LEN = 1; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_TYPE_LEN = 7; /* bits */ +FLAC_API const unsigned FLAC__STREAM_METADATA_LENGTH_LEN = 24; /* bits */ + +FLAC_API const unsigned FLAC__FRAME_HEADER_SYNC = 0x3ffe; +FLAC_API const unsigned FLAC__FRAME_HEADER_SYNC_LEN = 14; /* bits */ +FLAC_API const unsigned FLAC__FRAME_HEADER_RESERVED_LEN = 2; /* bits */ +FLAC_API const unsigned FLAC__FRAME_HEADER_BLOCK_SIZE_LEN = 4; /* bits */ +FLAC_API const unsigned FLAC__FRAME_HEADER_SAMPLE_RATE_LEN = 4; /* bits */ +FLAC_API const unsigned FLAC__FRAME_HEADER_CHANNEL_ASSIGNMENT_LEN = 4; /* bits */ +FLAC_API const unsigned FLAC__FRAME_HEADER_BITS_PER_SAMPLE_LEN = 3; /* bits */ +FLAC_API const unsigned FLAC__FRAME_HEADER_ZERO_PAD_LEN = 1; /* bits */ +FLAC_API const unsigned FLAC__FRAME_HEADER_CRC_LEN = 8; /* bits */ + +FLAC_API const unsigned FLAC__FRAME_FOOTER_CRC_LEN = 16; /* bits */ + +FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_TYPE_LEN = 2; /* bits */ +FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN = 4; /* bits */ +FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN = 4; /* bits */ +FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_RAW_LEN = 5; /* bits */ + +FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER = 15; /* == (1< FLAC__MAX_SAMPLE_RATE) { + return false; + } + else + return true; +} + +FLAC_API FLAC__bool FLAC__format_sample_rate_is_subset(unsigned sample_rate) +{ + if( + !FLAC__format_sample_rate_is_valid(sample_rate) || + ( + sample_rate >= (1u << 16) && + !(sample_rate % 1000 == 0 || sample_rate % 10 == 0) + ) + ) { + return false; + } + else + return true; +} + +FLAC_API FLAC__bool FLAC__format_seektable_is_legal(const FLAC__StreamMetadata_SeekTable *seek_table) +{ + unsigned i; + FLAC__uint64 prev_sample_number = 0; + FLAC__bool got_prev = false; + + FLAC__ASSERT(0 != seek_table); + + for(i = 0; i < seek_table->num_points; i++) { + if(got_prev) { + if( + seek_table->points[i].sample_number != FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER && + seek_table->points[i].sample_number <= prev_sample_number + ) + return false; + } + prev_sample_number = seek_table->points[i].sample_number; + got_prev = true; + } + + return true; +} + +/* used as the sort predicate for qsort() */ +static int seekpoint_compare_(const FLAC__StreamMetadata_SeekPoint *l, const FLAC__StreamMetadata_SeekPoint *r) +{ + /* we don't just 'return l->sample_number - r->sample_number' since the result (FLAC__int64) might overflow an 'int' */ + if(l->sample_number == r->sample_number) + return 0; + else if(l->sample_number < r->sample_number) + return -1; + else + return 1; +} + +FLAC_API unsigned FLAC__format_seektable_sort(FLAC__StreamMetadata_SeekTable *seek_table) +{ + unsigned i, j; + FLAC__bool first; + + FLAC__ASSERT(0 != seek_table); + + /* sort the seekpoints */ + qsort(seek_table->points, seek_table->num_points, sizeof(FLAC__StreamMetadata_SeekPoint), (int (*)(const void *, const void *))seekpoint_compare_); + + /* uniquify the seekpoints */ + first = true; + for(i = j = 0; i < seek_table->num_points; i++) { + if(seek_table->points[i].sample_number != FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER) { + if(!first) { + if(seek_table->points[i].sample_number == seek_table->points[j-1].sample_number) + continue; + } + } + first = false; + seek_table->points[j++] = seek_table->points[i]; + } + + for(i = j; i < seek_table->num_points; i++) { + seek_table->points[i].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER; + seek_table->points[i].stream_offset = 0; + seek_table->points[i].frame_samples = 0; + } + + return j; +} + +/* + * also disallows non-shortest-form encodings, c.f. + * http://www.unicode.org/versions/corrigendum1.html + * and a more clear explanation at the end of this section: + * http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + */ +static FLaC__INLINE unsigned utf8len_(const FLAC__byte *utf8) +{ + FLAC__ASSERT(0 != utf8); + if ((utf8[0] & 0x80) == 0) { + return 1; + } + else if ((utf8[0] & 0xE0) == 0xC0 && (utf8[1] & 0xC0) == 0x80) { + if ((utf8[0] & 0xFE) == 0xC0) /* overlong sequence check */ + return 0; + return 2; + } + else if ((utf8[0] & 0xF0) == 0xE0 && (utf8[1] & 0xC0) == 0x80 && (utf8[2] & 0xC0) == 0x80) { + if (utf8[0] == 0xE0 && (utf8[1] & 0xE0) == 0x80) /* overlong sequence check */ + return 0; + /* illegal surrogates check (U+D800...U+DFFF and U+FFFE...U+FFFF) */ + if (utf8[0] == 0xED && (utf8[1] & 0xE0) == 0xA0) /* D800-DFFF */ + return 0; + if (utf8[0] == 0xEF && utf8[1] == 0xBF && (utf8[2] & 0xFE) == 0xBE) /* FFFE-FFFF */ + return 0; + return 3; + } + else if ((utf8[0] & 0xF8) == 0xF0 && (utf8[1] & 0xC0) == 0x80 && (utf8[2] & 0xC0) == 0x80 && (utf8[3] & 0xC0) == 0x80) { + if (utf8[0] == 0xF0 && (utf8[1] & 0xF0) == 0x80) /* overlong sequence check */ + return 0; + return 4; + } + else if ((utf8[0] & 0xFC) == 0xF8 && (utf8[1] & 0xC0) == 0x80 && (utf8[2] & 0xC0) == 0x80 && (utf8[3] & 0xC0) == 0x80 && (utf8[4] & 0xC0) == 0x80) { + if (utf8[0] == 0xF8 && (utf8[1] & 0xF8) == 0x80) /* overlong sequence check */ + return 0; + return 5; + } + else if ((utf8[0] & 0xFE) == 0xFC && (utf8[1] & 0xC0) == 0x80 && (utf8[2] & 0xC0) == 0x80 && (utf8[3] & 0xC0) == 0x80 && (utf8[4] & 0xC0) == 0x80 && (utf8[5] & 0xC0) == 0x80) { + if (utf8[0] == 0xFC && (utf8[1] & 0xFC) == 0x80) /* overlong sequence check */ + return 0; + return 6; + } + else { + return 0; + } +} + +FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_name_is_legal(const char *name) +{ + char c; + for(c = *name; c; c = *(++name)) + if(c < 0x20 || c == 0x3d || c > 0x7d) + return false; + return true; +} + +FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_value_is_legal(const FLAC__byte *value, unsigned length) +{ + if(length == (unsigned)(-1)) { + while(*value) { + unsigned n = utf8len_(value); + if(n == 0) + return false; + value += n; + } + } + else { + const FLAC__byte *end = value + length; + while(value < end) { + unsigned n = utf8len_(value); + if(n == 0) + return false; + value += n; + } + if(value != end) + return false; + } + return true; +} + +FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_is_legal(const FLAC__byte *entry, unsigned length) +{ + const FLAC__byte *s, *end; + + for(s = entry, end = s + length; s < end && *s != '='; s++) { + if(*s < 0x20 || *s > 0x7D) + return false; + } + if(s == end) + return false; + + s++; /* skip '=' */ + + while(s < end) { + unsigned n = utf8len_(s); + if(n == 0) + return false; + s += n; + } + if(s != end) + return false; + + return true; +} + +FLAC_API FLAC__bool FLAC__format_cuesheet_is_legal(const FLAC__StreamMetadata_CueSheet *cue_sheet, FLAC__bool check_cd_da_subset, const char **violation) +{ + unsigned i, j; + + if(check_cd_da_subset) { + if(cue_sheet->lead_in < 2 * 44100) { + if(violation) *violation = "CD-DA cue sheet must have a lead-in length of at least 2 seconds"; + return false; + } + if(cue_sheet->lead_in % 588 != 0) { + if(violation) *violation = "CD-DA cue sheet lead-in length must be evenly divisible by 588 samples"; + return false; + } + } + + if(cue_sheet->num_tracks == 0) { + if(violation) *violation = "cue sheet must have at least one track (the lead-out)"; + return false; + } + + if(check_cd_da_subset && cue_sheet->tracks[cue_sheet->num_tracks-1].number != 170) { + if(violation) *violation = "CD-DA cue sheet must have a lead-out track number 170 (0xAA)"; + return false; + } + + for(i = 0; i < cue_sheet->num_tracks; i++) { + if(cue_sheet->tracks[i].number == 0) { + if(violation) *violation = "cue sheet may not have a track number 0"; + return false; + } + + if(check_cd_da_subset) { + if(!((cue_sheet->tracks[i].number >= 1 && cue_sheet->tracks[i].number <= 99) || cue_sheet->tracks[i].number == 170)) { + if(violation) *violation = "CD-DA cue sheet track number must be 1-99 or 170"; + return false; + } + } + + if(check_cd_da_subset && cue_sheet->tracks[i].offset % 588 != 0) { + if(violation) { + if(i == cue_sheet->num_tracks-1) /* the lead-out track... */ + *violation = "CD-DA cue sheet lead-out offset must be evenly divisible by 588 samples"; + else + *violation = "CD-DA cue sheet track offset must be evenly divisible by 588 samples"; + } + return false; + } + + if(i < cue_sheet->num_tracks - 1) { + if(cue_sheet->tracks[i].num_indices == 0) { + if(violation) *violation = "cue sheet track must have at least one index point"; + return false; + } + + if(cue_sheet->tracks[i].indices[0].number > 1) { + if(violation) *violation = "cue sheet track's first index number must be 0 or 1"; + return false; + } + } + + for(j = 0; j < cue_sheet->tracks[i].num_indices; j++) { + if(check_cd_da_subset && cue_sheet->tracks[i].indices[j].offset % 588 != 0) { + if(violation) *violation = "CD-DA cue sheet track index offset must be evenly divisible by 588 samples"; + return false; + } + + if(j > 0) { + if(cue_sheet->tracks[i].indices[j].number != cue_sheet->tracks[i].indices[j-1].number + 1) { + if(violation) *violation = "cue sheet track index numbers must increase by 1"; + return false; + } + } + } + } + + return true; +} + +FLAC_API FLAC__bool FLAC__format_picture_is_legal(const FLAC__StreamMetadata_Picture *picture, const char **violation) +{ + char *p; + FLAC__byte *b; + + for(p = picture->mime_type; *p; p++) { + if(*p < 0x20 || *p > 0x7e) { + if(violation) *violation = "MIME type string must contain only printable ASCII characters (0x20-0x7e)"; + return false; + } + } + + for(b = picture->description; *b; ) { + unsigned n = utf8len_(b); + if(n == 0) { + if(violation) *violation = "description string must be valid UTF-8"; + return false; + } + b += n; + } + + return true; +} + +/* + * These routines are private to libFLAC + */ +unsigned FLAC__format_get_max_rice_partition_order(unsigned blocksize, unsigned predictor_order) +{ + return + FLAC__format_get_max_rice_partition_order_from_blocksize_limited_max_and_predictor_order( + FLAC__format_get_max_rice_partition_order_from_blocksize(blocksize), + blocksize, + predictor_order + ); +} + +unsigned FLAC__format_get_max_rice_partition_order_from_blocksize(unsigned blocksize) +{ + unsigned max_rice_partition_order = 0; + while(!(blocksize & 1)) { + max_rice_partition_order++; + blocksize >>= 1; + } + return min(FLAC__MAX_RICE_PARTITION_ORDER, max_rice_partition_order); +} + +unsigned FLAC__format_get_max_rice_partition_order_from_blocksize_limited_max_and_predictor_order(unsigned limit, unsigned blocksize, unsigned predictor_order) +{ + unsigned max_rice_partition_order = limit; + + while(max_rice_partition_order > 0 && (blocksize >> max_rice_partition_order) <= predictor_order) + max_rice_partition_order--; + + FLAC__ASSERT( + (max_rice_partition_order == 0 && blocksize >= predictor_order) || + (max_rice_partition_order > 0 && blocksize >> max_rice_partition_order > predictor_order) + ); + + return max_rice_partition_order; +} + +void FLAC__format_entropy_coding_method_partitioned_rice_contents_init(FLAC__EntropyCodingMethod_PartitionedRiceContents *object) +{ + FLAC__ASSERT(0 != object); + + object->parameters = 0; + object->raw_bits = 0; + object->capacity_by_order = 0; +} + +void FLAC__format_entropy_coding_method_partitioned_rice_contents_clear(FLAC__EntropyCodingMethod_PartitionedRiceContents *object) +{ + FLAC__ASSERT(0 != object); + + if(0 != object->parameters) + free(object->parameters); + if(0 != object->raw_bits) + free(object->raw_bits); + FLAC__format_entropy_coding_method_partitioned_rice_contents_init(object); +} + +FLAC__bool FLAC__format_entropy_coding_method_partitioned_rice_contents_ensure_size(FLAC__EntropyCodingMethod_PartitionedRiceContents *object, unsigned max_partition_order) +{ + FLAC__ASSERT(0 != object); + + FLAC__ASSERT(object->capacity_by_order > 0 || (0 == object->parameters && 0 == object->raw_bits)); + + if(object->capacity_by_order < max_partition_order) { + if(0 == (object->parameters = (unsigned*)realloc(object->parameters, sizeof(unsigned)*(1 << max_partition_order)))) + return false; + if(0 == (object->raw_bits = (unsigned*)realloc(object->raw_bits, sizeof(unsigned)*(1 << max_partition_order)))) + return false; + object->capacity_by_order = max_partition_order; + } + + return true; +} diff --git a/src/FLAC/src/libFLAC/ia32/Makefile.am b/src/FLAC/src/libFLAC/ia32/Makefile.am new file mode 100644 index 00000000..93a1c4f4 --- /dev/null +++ b/src/FLAC/src/libFLAC/ia32/Makefile.am @@ -0,0 +1,45 @@ +# libFLAC - Free Lossless Audio Codec library +# Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# - Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# - Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# - Neither the name of the Xiph.org Foundation nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +SUFFIXES = .nasm .lo + +STRIP_NON_ASM = sh $(top_srcdir)/src/FLAC/strip_non_asm_libtool_args.sh + +.nasm.lo: + $(LIBTOOL) --tag=CC --mode=compile $(STRIP_NON_ASM) $(NASM) -f $(OBJ_FORMAT) -d OBJ_FORMAT_$(OBJ_FORMAT) -i$(srcdir)/ $< -o $@ + +noinst_LTLIBRARIES = libFLAC-asm.la +libFLAC_asm_la_SOURCES = \ + bitreader_asm.nasm \ + cpu_asm.nasm \ + fixed_asm.nasm \ + lpc_asm.nasm \ + nasm.h \ + stream_encoder_asm.nasm diff --git a/src/FLAC/src/libFLAC/ia32/bitreader_asm.nasm b/src/FLAC/src/libFLAC/ia32/bitreader_asm.nasm new file mode 100644 index 00000000..5d1bbfa4 --- /dev/null +++ b/src/FLAC/src/libFLAC/ia32/bitreader_asm.nasm @@ -0,0 +1,568 @@ +; vim:filetype=nasm ts=8 + +; libFLAC - Free Lossless Audio Codec library +; Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions +; are met: +; +; - Redistributions of source code must retain the above copyright +; notice, this list of conditions and the following disclaimer. +; +; - Redistributions in binary form must reproduce the above copyright +; notice, this list of conditions and the following disclaimer in the +; documentation and/or other materials provided with the distribution. +; +; - Neither the name of the Xiph.org Foundation nor the names of its +; contributors may be used to endorse or promote products derived from +; this software without specific prior written permission. +; +; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +; ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +; LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +; A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR +; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +; EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +; PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +; LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +%include "nasm.h" + + data_section + +cextern FLAC__crc16_table ; unsigned FLAC__crc16_table[256]; +cextern bitreader_read_from_client_ ; FLAC__bool bitreader_read_from_client_(FLAC__BitReader *br); + +cglobal FLAC__bitreader_read_rice_signed_block_asm_ia32_bswap + + code_section + + +; ********************************************************************** +; +; void FLAC__bool FLAC__bitreader_read_rice_signed_block(FLAC__BitReader *br, int vals[], unsigned nvals, unsigned parameter) +; +; Some details like assertions and other checking is performed by the caller. + ALIGN 16 +cident FLAC__bitreader_read_rice_signed_block_asm_ia32_bswap + + ;ASSERT(0 != br); + ;ASSERT(0 != br->buffer); + ; WATCHOUT: code only works if sizeof(brword)==32; we can make things much faster with this assertion + ;ASSERT(FLAC__BITS_PER_WORD == 32); + ;ASSERT(parameter < 32); + ; the above two asserts also guarantee that the binary part never straddles more than 2 words, so we don't have to loop to read it + + ;; peppered throughout the code at major checkpoints are keys like this as to where things are at that point in time + ;; [esp + 16] unsigned parameter + ;; [esp + 12] unsigned nvals + ;; [esp + 8] int vals[] + ;; [esp + 4] FLAC__BitReader *br + mov eax, [esp + 12] ; if(nvals == 0) + test eax, eax + ja .nvals_gt_0 + mov eax, 1 ; return true; + ret + +.nvals_gt_0: + push ebp + push ebx + push esi + push edi + sub esp, 4 + ;; [esp + 36] unsigned parameter + ;; [esp + 32] unsigned nvals + ;; [esp + 28] int vals[] + ;; [esp + 24] FLAC__BitReader *br + ;; [esp] ucbits + mov ebp, [esp + 24] ; ebp <- br == br->buffer + mov esi, [ebp + 16] ; esi <- br->consumed_words (aka 'cwords' in the C version) + mov ecx, [ebp + 20] ; ecx <- br->consumed_bits (aka 'cbits' in the C version) + xor edi, edi ; edi <- 0 'uval' + ;; ecx cbits + ;; esi cwords + ;; edi uval + ;; ebp br + ;; [ebp] br->buffer + ;; [ebp + 8] br->words + ;; [ebp + 12] br->bytes + ;; [ebp + 16] br->consumed_words + ;; [ebp + 20] br->consumed_bits + ;; [ebp + 24] br->read_crc + ;; [ebp + 28] br->crc16_align + + ; ucbits = (br->words-cwords)*FLAC__BITS_PER_WORD + br->bytes*8 - cbits; + mov eax, [ebp + 8] ; eax <- br->words + sub eax, esi ; eax <- br->words-cwords + shl eax, 2 ; eax <- (br->words-cwords)*FLAC__BYTES_PER_WORD + add eax, [ebp + 12] ; eax <- (br->words-cwords)*FLAC__BYTES_PER_WORD + br->bytes + shl eax, 3 ; eax <- (br->words-cwords)*FLAC__BITS_PER_WORD + br->bytes*8 + sub eax, ecx ; eax <- (br->words-cwords)*FLAC__BITS_PER_WORD + br->bytes*8 - cbits + mov [esp], eax ; ucbits <- eax + + ALIGN 16 +.val_loop: ; while(1) { + + ; + ; read unary part + ; +.unary_loop: ; while(1) { + ;; ecx cbits + ;; esi cwords + ;; edi uval + ;; ebp br + cmp esi, [ebp + 8] ; while(cwords < br->words) /* if we've not consumed up to a partial tail word... */ + jae near .c1_next1 +.c1_loop: ; { + mov ebx, [ebp] + mov eax, [ebx + 4*esi] ; b = br->buffer[cwords] + mov edx, eax ; edx = br->buffer[cwords] (saved for later use) + shl eax, cl ; b = br->buffer[cwords] << cbits + test eax, eax ; (still have to test since cbits may be 0, thus ZF not updated for shl eax,0) + jz near .c1_next2 ; if(b) { + bsr ebx, eax + not ebx + and ebx, 31 ; ebx = 'i' = # of leading 0 bits in 'b' (eax) + add ecx, ebx ; cbits += i; + add edi, ebx ; uval += i; + add ecx, byte 1 ; cbits++; /* skip over stop bit */ + test ecx, ~31 + jz near .break1 ; if(cbits >= FLAC__BITS_PER_WORD) { /* faster way of testing if(cbits == FLAC__BITS_PER_WORD) */ + ; crc16_update_word_(br, br->buffer[cwords]); + push edi ; [need more registers] + bswap edx ; edx = br->buffer[cwords] swapped; now we can CRC the bytes from LSByte to MSByte which makes things much easier + mov ecx, [ebp + 28] ; ecx <- br->crc16_align + mov eax, [ebp + 24] ; ax <- br->read_crc (a.k.a. crc) +%ifdef FLAC__PUBLIC_NEEDS_UNDERSCORE + mov edi, _FLAC__crc16_table +%else + mov edi, FLAC__crc16_table +%endif + ;; eax (ax) crc a.k.a. br->read_crc + ;; ebx (bl) intermediate result index into FLAC__crc16_table[] + ;; ecx br->crc16_align + ;; edx byteswapped brword to CRC + ;; esi cwords + ;; edi unsigned FLAC__crc16_table[] + ;; ebp br + test ecx, ecx ; switch(br->crc16_align) ... + jnz .c0b4 ; [br->crc16_align is 0 the vast majority of the time so we optimize the common case] +.c0b0: xor dl, ah ; dl <- (crc>>8)^(word>>24) + movzx ebx, dl + mov ecx, [ebx*4 + edi] ; cx <- FLAC__crc16_table[(crc>>8)^(word>>24)] + shl eax, 8 ; ax <- (crc<<8) + xor eax, ecx ; crc <- ax <- (crc<<8) ^ FLAC__crc16_table[(crc>>8)^(word>>24)] +.c0b1: xor dh, ah ; dh <- (crc>>8)^((word>>16)&0xff)) + movzx ebx, dh + mov ecx, [ebx*4 + edi] ; cx <- FLAC__crc16_table[(crc>>8)^((word>>16)&0xff))] + shl eax, 8 ; ax <- (crc<<8) + xor eax, ecx ; crc <- ax <- (crc<<8) ^ FLAC__crc16_table[(crc>>8)^((word>>16)&0xff))] + shr edx, 16 +.c0b2: xor dl, ah ; dl <- (crc>>8)^((word>>8)&0xff)) + movzx ebx, dl + mov ecx, [ebx*4 + edi] ; cx <- FLAC__crc16_table[(crc>>8)^((word>>8)&0xff))] + shl eax, 8 ; ax <- (crc<<8) + xor eax, ecx ; crc <- ax <- (crc<<8) ^ FLAC__crc16_table[(crc>>8)^((word>>8)&0xff))] +.c0b3: xor dh, ah ; dh <- (crc>>8)^(word&0xff) + movzx ebx, dh + mov ecx, [ebx*4 + edi] ; cx <- FLAC__crc16_table[(crc>>8)^(word&0xff)] + shl eax, 8 ; ax <- (crc<<8) + xor eax, ecx ; crc <- ax <- (crc<<8) ^ FLAC__crc16_table[(crc>>8)^(word&0xff)] + movzx eax, ax + mov [ebp + 24], eax ; br->read_crc <- crc + pop edi + + add esi, byte 1 ; cwords++; + xor ecx, ecx ; cbits = 0; + ; } + jmp near .break1 ; goto break1; + ;; this section relocated out of the way for performance +.c0b4: + mov [ebp + 28], dword 0 ; br->crc16_align <- 0 + cmp ecx, 8 + je .c0b1 + shr edx, 16 + cmp ecx, 16 + je .c0b2 + jmp .c0b3 + + ;; this section relocated out of the way for performance +.c1b4: + mov [ebp + 28], dword 0 ; br->crc16_align <- 0 + cmp ecx, 8 + je .c1b1 + shr edx, 16 + cmp ecx, 16 + je .c1b2 + jmp .c1b3 + +.c1_next2: ; } else { + ;; ecx cbits + ;; edx current brword 'b' + ;; esi cwords + ;; edi uval + ;; ebp br + add edi, 32 + sub edi, ecx ; uval += FLAC__BITS_PER_WORD - cbits; + ; crc16_update_word_(br, br->buffer[cwords]); + push edi ; [need more registers] + bswap edx ; edx = br->buffer[cwords] swapped; now we can CRC the bytes from LSByte to MSByte which makes things much easier + mov ecx, [ebp + 28] ; ecx <- br->crc16_align + mov eax, [ebp + 24] ; ax <- br->read_crc (a.k.a. crc) +%ifdef FLAC__PUBLIC_NEEDS_UNDERSCORE + mov edi, _FLAC__crc16_table +%else + mov edi, FLAC__crc16_table +%endif + ;; eax (ax) crc a.k.a. br->read_crc + ;; ebx (bl) intermediate result index into FLAC__crc16_table[] + ;; ecx br->crc16_align + ;; edx byteswapped brword to CRC + ;; esi cwords + ;; edi unsigned FLAC__crc16_table[] + ;; ebp br + test ecx, ecx ; switch(br->crc16_align) ... + jnz .c1b4 ; [br->crc16_align is 0 the vast majority of the time so we optimize the common case] +.c1b0: xor dl, ah ; dl <- (crc>>8)^(word>>24) + movzx ebx, dl + mov ecx, [ebx*4 + edi] ; cx <- FLAC__crc16_table[(crc>>8)^(word>>24)] + shl eax, 8 ; ax <- (crc<<8) + xor eax, ecx ; crc <- ax <- (crc<<8) ^ FLAC__crc16_table[(crc>>8)^(word>>24)] +.c1b1: xor dh, ah ; dh <- (crc>>8)^((word>>16)&0xff)) + movzx ebx, dh + mov ecx, [ebx*4 + edi] ; cx <- FLAC__crc16_table[(crc>>8)^((word>>16)&0xff))] + shl eax, 8 ; ax <- (crc<<8) + xor eax, ecx ; crc <- ax <- (crc<<8) ^ FLAC__crc16_table[(crc>>8)^((word>>16)&0xff))] + shr edx, 16 +.c1b2: xor dl, ah ; dl <- (crc>>8)^((word>>8)&0xff)) + movzx ebx, dl + mov ecx, [ebx*4 + edi] ; cx <- FLAC__crc16_table[(crc>>8)^((word>>8)&0xff))] + shl eax, 8 ; ax <- (crc<<8) + xor eax, ecx ; crc <- ax <- (crc<<8) ^ FLAC__crc16_table[(crc>>8)^((word>>8)&0xff))] +.c1b3: xor dh, ah ; dh <- (crc>>8)^(word&0xff) + movzx ebx, dh + mov ecx, [ebx*4 + edi] ; cx <- FLAC__crc16_table[(crc>>8)^(word&0xff)] + shl eax, 8 ; ax <- (crc<<8) + xor eax, ecx ; crc <- ax <- (crc<<8) ^ FLAC__crc16_table[(crc>>8)^(word&0xff)] + movzx eax, ax + mov [ebp + 24], eax ; br->read_crc <- crc + pop edi + + add esi, byte 1 ; cwords++; + xor ecx, ecx ; cbits = 0; + ; /* didn't find stop bit yet, have to keep going... */ + ; } + + cmp esi, [ebp + 8] ; } while(cwords < br->words) /* if we've not consumed up to a partial tail word... */ + jb near .c1_loop + +.c1_next1: + ; at this point we've eaten up all the whole words; have to try + ; reading through any tail bytes before calling the read callback. + ; this is a repeat of the above logic adjusted for the fact we + ; don't have a whole word. note though if the client is feeding + ; us data a byte at a time (unlikely), br->consumed_bits may not + ; be zero. + ;; ecx cbits + ;; esi cwords + ;; edi uval + ;; ebp br + mov edx, [ebp + 12] ; edx <- br->bytes + test edx, edx + jz .read1 ; if(br->bytes) { [NOTE: this case is rare so it doesn't have to be all that fast ] + mov ebx, [ebp] + shl edx, 3 ; edx <- const unsigned end = br->bytes * 8; + mov eax, [ebx + 4*esi] ; b = br->buffer[cwords] + xchg edx, ecx ; [edx <- cbits , ecx <- end] + mov ebx, 0xffffffff ; ebx <- FLAC__WORD_ALL_ONES + shr ebx, cl ; ebx <- FLAC__WORD_ALL_ONES >> end + not ebx ; ebx <- ~(FLAC__WORD_ALL_ONES >> end) + xchg edx, ecx ; [edx <- end , ecx <- cbits] + and eax, ebx ; b = (br->buffer[cwords] & ~(FLAC__WORD_ALL_ONES >> end)); + shl eax, cl ; b = (br->buffer[cwords] & ~(FLAC__WORD_ALL_ONES >> end)) << cbits; + test eax, eax ; (still have to test since cbits may be 0, thus ZF not updated for shl eax,0) + jz .c1_next3 ; if(b) { + bsr ebx, eax + not ebx + and ebx, 31 ; ebx = 'i' = # of leading 0 bits in 'b' (eax) + add ecx, ebx ; cbits += i; + add edi, ebx ; uval += i; + add ecx, byte 1 ; cbits++; /* skip over stop bit */ + jmp short .break1 ; goto break1; +.c1_next3: ; } else { + sub edi, ecx + add edi, edx ; uval += end - cbits; + add ecx, edx ; cbits += end + ; /* didn't find stop bit yet, have to keep going... */ + ; } + ; } +.read1: + ; flush registers and read; bitreader_read_from_client_() does + ; not touch br->consumed_bits at all but we still need to set + ; it in case it fails and we have to return false. + ;; ecx cbits + ;; esi cwords + ;; edi uval + ;; ebp br + mov [ebp + 16], esi ; br->consumed_words = cwords; + mov [ebp + 20], ecx ; br->consumed_bits = cbits; + push ecx ; /* save */ + push ebp ; /* push br argument */ +%ifdef FLAC__PUBLIC_NEEDS_UNDERSCORE + call _bitreader_read_from_client_ +%else + call bitreader_read_from_client_ +%endif + pop edx ; /* discard, unused */ + pop ecx ; /* restore */ + mov esi, [ebp + 16] ; cwords = br->consumed_words; + ; ucbits = (br->words-cwords)*FLAC__BITS_PER_WORD + br->bytes*8 - cbits; + mov ebx, [ebp + 8] ; ebx <- br->words + sub ebx, esi ; ebx <- br->words-cwords + shl ebx, 2 ; ebx <- (br->words-cwords)*FLAC__BYTES_PER_WORD + add ebx, [ebp + 12] ; ebx <- (br->words-cwords)*FLAC__BYTES_PER_WORD + br->bytes + shl ebx, 3 ; ebx <- (br->words-cwords)*FLAC__BITS_PER_WORD + br->bytes*8 + sub ebx, ecx ; ebx <- (br->words-cwords)*FLAC__BITS_PER_WORD + br->bytes*8 - cbits + add ebx, edi ; ebx <- (br->words-cwords)*FLAC__BITS_PER_WORD + br->bytes*8 - cbits + uval + ; + uval to offset our count by the # of unary bits already + ; consumed before the read, because we will add these back + ; in all at once at break1 + mov [esp], ebx ; ucbits <- ebx + test eax, eax ; if(!bitreader_read_from_client_(br)) + jnz near .unary_loop + jmp .end ; return false; /* eax (the return value) is already 0 */ + ; } /* end while(1) unary part */ + + ALIGN 16 +.break1: + ;; ecx cbits + ;; esi cwords + ;; edi uval + ;; ebp br + ;; [esp] ucbits + sub [esp], edi ; ucbits -= uval; + sub dword [esp], byte 1 ; ucbits--; /* account for stop bit */ + + ; + ; read binary part + ; + mov ebx, [esp + 36] ; ebx <- parameter + test ebx, ebx ; if(parameter) { + jz near .break2 +.read2: + cmp [esp], ebx ; while(ucbits < parameter) { + jae .c2_next1 + ; flush registers and read; bitreader_read_from_client_() does + ; not touch br->consumed_bits at all but we still need to set + ; it in case it fails and we have to return false. + mov [ebp + 16], esi ; br->consumed_words = cwords; + mov [ebp + 20], ecx ; br->consumed_bits = cbits; + push ecx ; /* save */ + push ebp ; /* push br argument */ +%ifdef FLAC__PUBLIC_NEEDS_UNDERSCORE + call _bitreader_read_from_client_ +%else + call bitreader_read_from_client_ +%endif + pop edx ; /* discard, unused */ + pop ecx ; /* restore */ + mov esi, [ebp + 16] ; cwords = br->consumed_words; + ; ucbits = (br->words-cwords)*FLAC__BITS_PER_WORD + br->bytes*8 - cbits; + mov edx, [ebp + 8] ; edx <- br->words + sub edx, esi ; edx <- br->words-cwords + shl edx, 2 ; edx <- (br->words-cwords)*FLAC__BYTES_PER_WORD + add edx, [ebp + 12] ; edx <- (br->words-cwords)*FLAC__BYTES_PER_WORD + br->bytes + shl edx, 3 ; edx <- (br->words-cwords)*FLAC__BITS_PER_WORD + br->bytes*8 + sub edx, ecx ; edx <- (br->words-cwords)*FLAC__BITS_PER_WORD + br->bytes*8 - cbits + mov [esp], edx ; ucbits <- edx + test eax, eax ; if(!bitreader_read_from_client_(br)) + jnz .read2 + jmp .end ; return false; /* eax (the return value) is already 0 */ + ; } +.c2_next1: + ;; ebx parameter + ;; ecx cbits + ;; esi cwords + ;; edi uval + ;; ebp br + ;; [esp] ucbits + cmp esi, [ebp + 8] ; if(cwords < br->words) { /* if we've not consumed up to a partial tail word... */ + jae near .c2_next2 + test ecx, ecx ; if(cbits) { + jz near .c2_next3 ; /* this also works when consumed_bits==0, it's just a little slower than necessary for that case */ + mov eax, 32 + mov edx, [ebp] + sub eax, ecx ; const unsigned n = FLAC__BITS_PER_WORD - cbits; + mov edx, [edx + 4*esi] ; const brword word = br->buffer[cwords]; + cmp ebx, eax ; if(parameter < n) { + jae .c2_next4 + ; uval <<= parameter; + ; uval |= (word & (FLAC__WORD_ALL_ONES >> cbits)) >> (n-parameter); + shl edx, cl + xchg ebx, ecx + shld edi, edx, cl + add ebx, ecx ; cbits += parameter; + xchg ebx, ecx ; ebx <- parameter, ecx <- cbits + jmp .break2 ; goto break2; + ; } +.c2_next4: + ; uval <<= n; + ; uval |= word & (FLAC__WORD_ALL_ONES >> cbits); +%if 1 + rol edx, cl ; @@@@@@OPT: may be faster to use rol to save edx so we can restore it for CRC'ing + ; @@@@@@OPT: or put parameter in ch instead and free up ebx completely again +%else + shl edx, cl +%endif + xchg eax, ecx + shld edi, edx, cl + xchg eax, ecx +%if 1 + ror edx, cl ; restored. +%else + mov edx, [ebp] + mov edx, [edx + 4*esi] +%endif + ; crc16_update_word_(br, br->buffer[cwords]); + push edi ; [need more registers] + push ebx ; [need more registers] + push eax ; [need more registers] + bswap edx ; edx = br->buffer[cwords] swapped; now we can CRC the bytes from LSByte to MSByte which makes things much easier + mov ecx, [ebp + 28] ; ecx <- br->crc16_align + mov eax, [ebp + 24] ; ax <- br->read_crc (a.k.a. crc) +%ifdef FLAC__PUBLIC_NEEDS_UNDERSCORE + mov edi, _FLAC__crc16_table +%else + mov edi, FLAC__crc16_table +%endif + ;; eax (ax) crc a.k.a. br->read_crc + ;; ebx (bl) intermediate result index into FLAC__crc16_table[] + ;; ecx br->crc16_align + ;; edx byteswapped brword to CRC + ;; esi cwords + ;; edi unsigned FLAC__crc16_table[] + ;; ebp br + test ecx, ecx ; switch(br->crc16_align) ... + jnz .c2b4 ; [br->crc16_align is 0 the vast majority of the time so we optimize the common case] +.c2b0: xor dl, ah ; dl <- (crc>>8)^(word>>24) + movzx ebx, dl + mov ecx, [ebx*4 + edi] ; cx <- FLAC__crc16_table[(crc>>8)^(word>>24)] + shl eax, 8 ; ax <- (crc<<8) + xor eax, ecx ; crc <- ax <- (crc<<8) ^ FLAC__crc16_table[(crc>>8)^(word>>24)] +.c2b1: xor dh, ah ; dh <- (crc>>8)^((word>>16)&0xff)) + movzx ebx, dh + mov ecx, [ebx*4 + edi] ; cx <- FLAC__crc16_table[(crc>>8)^((word>>16)&0xff))] + shl eax, 8 ; ax <- (crc<<8) + xor eax, ecx ; crc <- ax <- (crc<<8) ^ FLAC__crc16_table[(crc>>8)^((word>>16)&0xff))] + shr edx, 16 +.c2b2: xor dl, ah ; dl <- (crc>>8)^((word>>8)&0xff)) + movzx ebx, dl + mov ecx, [ebx*4 + edi] ; cx <- FLAC__crc16_table[(crc>>8)^((word>>8)&0xff))] + shl eax, 8 ; ax <- (crc<<8) + xor eax, ecx ; crc <- ax <- (crc<<8) ^ FLAC__crc16_table[(crc>>8)^((word>>8)&0xff))] +.c2b3: xor dh, ah ; dh <- (crc>>8)^(word&0xff) + movzx ebx, dh + mov ecx, [ebx*4 + edi] ; cx <- FLAC__crc16_table[(crc>>8)^(word&0xff)] + shl eax, 8 ; ax <- (crc<<8) + xor eax, ecx ; crc <- ax <- (crc<<8) ^ FLAC__crc16_table[(crc>>8)^(word&0xff)] + movzx eax, ax + mov [ebp + 24], eax ; br->read_crc <- crc + pop eax + pop ebx + pop edi + add esi, byte 1 ; cwords++; + mov ecx, ebx + sub ecx, eax ; cbits = parameter - n; + jz .break2 ; if(cbits) { /* parameter > n, i.e. if there are still bits left to read, there have to be less than 32 so they will all be in the next word */ + ; uval <<= cbits; + ; uval |= (br->buffer[cwords] >> (FLAC__BITS_PER_WORD-cbits)); + mov eax, [ebp] + mov eax, [eax + 4*esi] + shld edi, eax, cl + ; } + jmp .break2 ; goto break2; + + ;; this section relocated out of the way for performance +.c2b4: + mov [ebp + 28], dword 0 ; br->crc16_align <- 0 + cmp ecx, 8 + je .c2b1 + shr edx, 16 + cmp ecx, 16 + je .c2b2 + jmp .c2b3 + +.c2_next3: ; } else { + mov ecx, ebx ; cbits = parameter; + ; uval <<= cbits; + ; uval |= (br->buffer[cwords] >> (FLAC__BITS_PER_WORD-cbits)); + mov eax, [ebp] + mov eax, [eax + 4*esi] + shld edi, eax, cl + jmp .break2 ; goto break2; + ; } +.c2_next2: ; } else { + ; in this case we're starting our read at a partial tail word; + ; the reader has guaranteed that we have at least 'parameter' + ; bits available to read, which makes this case simpler. + ; uval <<= parameter; + ; if(cbits) { + ; /* this also works when consumed_bits==0, it's just a little slower than necessary for that case */ + ; uval |= (br->buffer[cwords] & (FLAC__WORD_ALL_ONES >> cbits)) >> (FLAC__BITS_PER_WORD-cbits-parameter); + ; cbits += parameter; + ; goto break2; + ; } else { + ; cbits = parameter; + ; uval |= br->buffer[cwords] >> (FLAC__BITS_PER_WORD-cbits); + ; goto break2; + ; } + ; the above is much shorter in assembly: + mov eax, [ebp] + mov eax, [eax + 4*esi] ; eax <- br->buffer[cwords] + shl eax, cl ; eax <- br->buffer[cwords] << cbits + add ecx, ebx ; cbits += parameter + xchg ebx, ecx ; ebx <- cbits, ecx <- parameter + shld edi, eax, cl ; uval <<= parameter <<< 'parameter' bits of tail word + xchg ebx, ecx ; ebx <- parameter, ecx <- cbits + ; } + ; } +.break2: + sub [esp], ebx ; ucbits -= parameter; + + ; + ; compose the value + ; + mov ebx, [esp + 28] ; ebx <- vals + mov edx, edi ; edx <- uval + and edi, 1 ; edi <- uval & 1 + shr edx, 1 ; edx <- uval >> 1 + neg edi ; edi <- -(int)(uval & 1) + xor edx, edi ; edx <- (uval >> 1 ^ -(int)(uval & 1)) + mov [ebx], edx ; *vals <- edx + sub dword [esp + 32], byte 1 ; --nvals; + jz .finished ; if(nvals == 0) /* jump to finish */ + xor edi, edi ; uval = 0; + add dword [esp + 28], 4 ; ++vals + jmp .val_loop ; } + +.finished: + mov [ebp + 16], esi ; br->consumed_words = cwords; + mov [ebp + 20], ecx ; br->consumed_bits = cbits; + mov eax, 1 +.end: + add esp, 4 + pop edi + pop esi + pop ebx + pop ebp + ret + +end + +%ifdef OBJ_FORMAT_elf + section .note.GNU-stack noalloc +%endif diff --git a/src/FLAC/src/libFLAC/ia32/cpu_asm.nasm b/src/FLAC/src/libFLAC/ia32/cpu_asm.nasm new file mode 100644 index 00000000..f5eb1102 --- /dev/null +++ b/src/FLAC/src/libFLAC/ia32/cpu_asm.nasm @@ -0,0 +1,121 @@ +; vim:filetype=nasm ts=8 + +; libFLAC - Free Lossless Audio Codec library +; Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions +; are met: +; +; - Redistributions of source code must retain the above copyright +; notice, this list of conditions and the following disclaimer. +; +; - Redistributions in binary form must reproduce the above copyright +; notice, this list of conditions and the following disclaimer in the +; documentation and/or other materials provided with the distribution. +; +; - Neither the name of the Xiph.org Foundation nor the names of its +; contributors may be used to endorse or promote products derived from +; this software without specific prior written permission. +; +; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +; ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +; LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +; A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR +; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +; EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +; PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +; LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +%include "nasm.h" + + data_section + +cglobal FLAC__cpu_have_cpuid_asm_ia32 +cglobal FLAC__cpu_info_asm_ia32 +cglobal FLAC__cpu_info_extended_amd_asm_ia32 + + code_section + +; ********************************************************************** +; +; FLAC__uint32 FLAC__cpu_have_cpuid_asm_ia32() +; + +cident FLAC__cpu_have_cpuid_asm_ia32 + push ebx + pushfd + pop eax + mov edx, eax + xor eax, 0x00200000 + push eax + popfd + pushfd + pop eax + cmp eax, edx + jz .no_cpuid + mov eax, 1 + jmp .end +.no_cpuid: + xor eax, eax +.end: + pop ebx + ret + +; ********************************************************************** +; +; void FLAC__cpu_info_asm_ia32(FLAC__uint32 *flags_edx, FLAC__uint32 *flags_ecx) +; + +cident FLAC__cpu_info_asm_ia32 + ;[esp + 8] == flags_edx + ;[esp + 12] == flags_ecx + + push ebx + call FLAC__cpu_have_cpuid_asm_ia32 + test eax, eax + jz .no_cpuid + mov eax, 1 + cpuid + mov ebx, [esp + 8] + mov [ebx], edx + mov ebx, [esp + 12] + mov [ebx], ecx + jmp .end +.no_cpuid + xor eax, eax + mov ebx, [esp + 8] + mov [ebx], eax + mov ebx, [esp + 12] + mov [ebx], eax +.end + pop ebx + ret + +cident FLAC__cpu_info_extended_amd_asm_ia32 + push ebx + call FLAC__cpu_have_cpuid_asm_ia32 + test eax, eax + jz .no_cpuid + mov eax, 0x80000000 + cpuid + cmp eax, 0x80000001 + jb .no_cpuid + mov eax, 0x80000001 + cpuid + mov eax, edx + jmp .end +.no_cpuid + xor eax, eax +.end + pop ebx + ret + +end + +%ifdef OBJ_FORMAT_elf + section .note.GNU-stack noalloc +%endif diff --git a/src/FLAC/src/libFLAC/ia32/fixed_asm.nasm b/src/FLAC/src/libFLAC/ia32/fixed_asm.nasm new file mode 100644 index 00000000..0185f4d8 --- /dev/null +++ b/src/FLAC/src/libFLAC/ia32/fixed_asm.nasm @@ -0,0 +1,312 @@ +; vim:filetype=nasm ts=8 + +; libFLAC - Free Lossless Audio Codec library +; Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions +; are met: +; +; - Redistributions of source code must retain the above copyright +; notice, this list of conditions and the following disclaimer. +; +; - Redistributions in binary form must reproduce the above copyright +; notice, this list of conditions and the following disclaimer in the +; documentation and/or other materials provided with the distribution. +; +; - Neither the name of the Xiph.org Foundation nor the names of its +; contributors may be used to endorse or promote products derived from +; this software without specific prior written permission. +; +; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +; ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +; LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +; A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR +; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +; EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +; PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +; LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +%include "nasm.h" + + data_section + +cglobal FLAC__fixed_compute_best_predictor_asm_ia32_mmx_cmov + + code_section + +; ********************************************************************** +; +; unsigned FLAC__fixed_compute_best_predictor(const FLAC__int32 *data, unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]) +; { +; FLAC__int32 last_error_0 = data[-1]; +; FLAC__int32 last_error_1 = data[-1] - data[-2]; +; FLAC__int32 last_error_2 = last_error_1 - (data[-2] - data[-3]); +; FLAC__int32 last_error_3 = last_error_2 - (data[-2] - 2*data[-3] + data[-4]); +; FLAC__int32 error, save; +; FLAC__uint32 total_error_0 = 0, total_error_1 = 0, total_error_2 = 0, total_error_3 = 0, total_error_4 = 0; +; unsigned i, order; +; +; for(i = 0; i < data_len; i++) { +; error = data[i] ; total_error_0 += local_abs(error); save = error; +; error -= last_error_0; total_error_1 += local_abs(error); last_error_0 = save; save = error; +; error -= last_error_1; total_error_2 += local_abs(error); last_error_1 = save; save = error; +; error -= last_error_2; total_error_3 += local_abs(error); last_error_2 = save; save = error; +; error -= last_error_3; total_error_4 += local_abs(error); last_error_3 = save; +; } +; +; if(total_error_0 < min(min(min(total_error_1, total_error_2), total_error_3), total_error_4)) +; order = 0; +; else if(total_error_1 < min(min(total_error_2, total_error_3), total_error_4)) +; order = 1; +; else if(total_error_2 < min(total_error_3, total_error_4)) +; order = 2; +; else if(total_error_3 < total_error_4) +; order = 3; +; else +; order = 4; +; +; residual_bits_per_sample[0] = (FLAC__float)((data_len > 0 && total_error_0 > 0) ? log(M_LN2 * (FLAC__double)total_error_0 / (FLAC__double)data_len) / M_LN2 : 0.0); +; residual_bits_per_sample[1] = (FLAC__float)((data_len > 0 && total_error_1 > 0) ? log(M_LN2 * (FLAC__double)total_error_1 / (FLAC__double)data_len) / M_LN2 : 0.0); +; residual_bits_per_sample[2] = (FLAC__float)((data_len > 0 && total_error_2 > 0) ? log(M_LN2 * (FLAC__double)total_error_2 / (FLAC__double)data_len) / M_LN2 : 0.0); +; residual_bits_per_sample[3] = (FLAC__float)((data_len > 0 && total_error_3 > 0) ? log(M_LN2 * (FLAC__double)total_error_3 / (FLAC__double)data_len) / M_LN2 : 0.0); +; residual_bits_per_sample[4] = (FLAC__float)((data_len > 0 && total_error_4 > 0) ? log(M_LN2 * (FLAC__double)total_error_4 / (FLAC__double)data_len) / M_LN2 : 0.0); +; +; return order; +; } + ALIGN 16 +cident FLAC__fixed_compute_best_predictor_asm_ia32_mmx_cmov + + ; esp + 36 == data[] + ; esp + 40 == data_len + ; esp + 44 == residual_bits_per_sample[] + + push ebp + push ebx + push esi + push edi + sub esp, byte 16 + ; qword [esp] == temp space for loading FLAC__uint64s to FPU regs + + ; ebx == &data[i] + ; ecx == loop counter (i) + ; ebp == order + ; mm0 == total_error_1:total_error_0 + ; mm1 == total_error_2:total_error_3 + ; mm2 == :total_error_4 + ; mm3 == last_error_1:last_error_0 + ; mm4 == last_error_2:last_error_3 + + mov ecx, [esp + 40] ; ecx = data_len + test ecx, ecx + jz near .data_len_is_0 + + mov ebx, [esp + 36] ; ebx = data[] + movd mm3, [ebx - 4] ; mm3 = 0:last_error_0 + movd mm2, [ebx - 8] ; mm2 = 0:data[-2] + movd mm1, [ebx - 12] ; mm1 = 0:data[-3] + movd mm0, [ebx - 16] ; mm0 = 0:data[-4] + movq mm5, mm3 ; mm5 = 0:last_error_0 + psubd mm5, mm2 ; mm5 = 0:last_error_1 + punpckldq mm3, mm5 ; mm3 = last_error_1:last_error_0 + psubd mm2, mm1 ; mm2 = 0:data[-2] - data[-3] + psubd mm5, mm2 ; mm5 = 0:last_error_2 + movq mm4, mm5 ; mm4 = 0:last_error_2 + psubd mm4, mm2 ; mm4 = 0:last_error_2 - (data[-2] - data[-3]) + paddd mm4, mm1 ; mm4 = 0:last_error_2 - (data[-2] - 2 * data[-3]) + psubd mm4, mm0 ; mm4 = 0:last_error_3 + punpckldq mm4, mm5 ; mm4 = last_error_2:last_error_3 + pxor mm0, mm0 ; mm0 = total_error_1:total_error_0 + pxor mm1, mm1 ; mm1 = total_error_2:total_error_3 + pxor mm2, mm2 ; mm2 = 0:total_error_4 + + ALIGN 16 +.loop: + movd mm7, [ebx] ; mm7 = 0:error_0 + add ebx, byte 4 + movq mm6, mm7 ; mm6 = 0:error_0 + psubd mm7, mm3 ; mm7 = :error_1 + punpckldq mm6, mm7 ; mm6 = error_1:error_0 + movq mm5, mm6 ; mm5 = error_1:error_0 + movq mm7, mm6 ; mm7 = error_1:error_0 + psubd mm5, mm3 ; mm5 = error_2: + movq mm3, mm6 ; mm3 = error_1:error_0 + psrad mm6, 31 + pxor mm7, mm6 + psubd mm7, mm6 ; mm7 = abs(error_1):abs(error_0) + paddd mm0, mm7 ; mm0 = total_error_1:total_error_0 + movq mm6, mm5 ; mm6 = error_2: + psubd mm5, mm4 ; mm5 = error_3: + punpckhdq mm5, mm6 ; mm5 = error_2:error_3 + movq mm7, mm5 ; mm7 = error_2:error_3 + movq mm6, mm5 ; mm6 = error_2:error_3 + psubd mm5, mm4 ; mm5 = :error_4 + movq mm4, mm6 ; mm4 = error_2:error_3 + psrad mm6, 31 + pxor mm7, mm6 + psubd mm7, mm6 ; mm7 = abs(error_2):abs(error_3) + paddd mm1, mm7 ; mm1 = total_error_2:total_error_3 + movq mm6, mm5 ; mm6 = :error_4 + psrad mm5, 31 + pxor mm6, mm5 + psubd mm6, mm5 ; mm6 = :abs(error_4) + paddd mm2, mm6 ; mm2 = :total_error_4 + + dec ecx + jnz short .loop + +; if(total_error_0 < min(min(min(total_error_1, total_error_2), total_error_3), total_error_4)) +; order = 0; +; else if(total_error_1 < min(min(total_error_2, total_error_3), total_error_4)) +; order = 1; +; else if(total_error_2 < min(total_error_3, total_error_4)) +; order = 2; +; else if(total_error_3 < total_error_4) +; order = 3; +; else +; order = 4; + movq mm3, mm0 ; mm3 = total_error_1:total_error_0 + movd edi, mm2 ; edi = total_error_4 + movd esi, mm1 ; esi = total_error_3 + movd eax, mm0 ; eax = total_error_0 + punpckhdq mm1, mm1 ; mm1 = total_error_2:total_error_2 + punpckhdq mm3, mm3 ; mm3 = total_error_1:total_error_1 + movd edx, mm1 ; edx = total_error_2 + movd ecx, mm3 ; ecx = total_error_1 + + xor ebx, ebx + xor ebp, ebp + inc ebx + cmp ecx, eax + cmovb eax, ecx ; eax = min(total_error_0, total_error_1) + cmovbe ebp, ebx + inc ebx + cmp edx, eax + cmovb eax, edx ; eax = min(total_error_0, total_error_1, total_error_2) + cmovbe ebp, ebx + inc ebx + cmp esi, eax + cmovb eax, esi ; eax = min(total_error_0, total_error_1, total_error_2, total_error_3) + cmovbe ebp, ebx + inc ebx + cmp edi, eax + cmovb eax, edi ; eax = min(total_error_0, total_error_1, total_error_2, total_error_3, total_error_4) + cmovbe ebp, ebx + movd ebx, mm0 ; ebx = total_error_0 + emms + + ; residual_bits_per_sample[0] = (FLAC__float)((data_len > 0 && total_error_0 > 0) ? log(M_LN2 * (FLAC__double)total_error_0 / (FLAC__double)data_len) / M_LN2 : 0.0); + ; residual_bits_per_sample[1] = (FLAC__float)((data_len > 0 && total_error_1 > 0) ? log(M_LN2 * (FLAC__double)total_error_1 / (FLAC__double)data_len) / M_LN2 : 0.0); + ; residual_bits_per_sample[2] = (FLAC__float)((data_len > 0 && total_error_2 > 0) ? log(M_LN2 * (FLAC__double)total_error_2 / (FLAC__double)data_len) / M_LN2 : 0.0); + ; residual_bits_per_sample[3] = (FLAC__float)((data_len > 0 && total_error_3 > 0) ? log(M_LN2 * (FLAC__double)total_error_3 / (FLAC__double)data_len) / M_LN2 : 0.0); + ; residual_bits_per_sample[4] = (FLAC__float)((data_len > 0 && total_error_4 > 0) ? log(M_LN2 * (FLAC__double)total_error_4 / (FLAC__double)data_len) / M_LN2 : 0.0); + xor eax, eax + fild dword [esp + 40] ; ST = data_len (NOTE: assumes data_len is <2gigs) +.rbps_0: + test ebx, ebx + jz .total_error_0_is_0 + fld1 ; ST = 1.0 data_len + mov [esp], ebx + mov [esp + 4], eax ; [esp] = (FLAC__uint64)total_error_0 + mov ebx, [esp + 44] + fild qword [esp] ; ST = total_error_0 1.0 data_len + fdiv st2 ; ST = total_error_0/data_len 1.0 data_len + fldln2 ; ST = ln2 total_error_0/data_len 1.0 data_len + fmulp st1 ; ST = ln2*total_error_0/data_len 1.0 data_len + fyl2x ; ST = log2(ln2*total_error_0/data_len) data_len + fstp dword [ebx] ; residual_bits_per_sample[0] = log2(ln2*total_error_0/data_len) ST = data_len + jmp short .rbps_1 +.total_error_0_is_0: + mov ebx, [esp + 44] + mov [ebx], eax ; residual_bits_per_sample[0] = 0.0 +.rbps_1: + test ecx, ecx + jz .total_error_1_is_0 + fld1 ; ST = 1.0 data_len + mov [esp], ecx + mov [esp + 4], eax ; [esp] = (FLAC__uint64)total_error_1 + fild qword [esp] ; ST = total_error_1 1.0 data_len + fdiv st2 ; ST = total_error_1/data_len 1.0 data_len + fldln2 ; ST = ln2 total_error_1/data_len 1.0 data_len + fmulp st1 ; ST = ln2*total_error_1/data_len 1.0 data_len + fyl2x ; ST = log2(ln2*total_error_1/data_len) data_len + fstp dword [ebx + 4] ; residual_bits_per_sample[1] = log2(ln2*total_error_1/data_len) ST = data_len + jmp short .rbps_2 +.total_error_1_is_0: + mov [ebx + 4], eax ; residual_bits_per_sample[1] = 0.0 +.rbps_2: + test edx, edx + jz .total_error_2_is_0 + fld1 ; ST = 1.0 data_len + mov [esp], edx + mov [esp + 4], eax ; [esp] = (FLAC__uint64)total_error_2 + fild qword [esp] ; ST = total_error_2 1.0 data_len + fdiv st2 ; ST = total_error_2/data_len 1.0 data_len + fldln2 ; ST = ln2 total_error_2/data_len 1.0 data_len + fmulp st1 ; ST = ln2*total_error_2/data_len 1.0 data_len + fyl2x ; ST = log2(ln2*total_error_2/data_len) data_len + fstp dword [ebx + 8] ; residual_bits_per_sample[2] = log2(ln2*total_error_2/data_len) ST = data_len + jmp short .rbps_3 +.total_error_2_is_0: + mov [ebx + 8], eax ; residual_bits_per_sample[2] = 0.0 +.rbps_3: + test esi, esi + jz .total_error_3_is_0 + fld1 ; ST = 1.0 data_len + mov [esp], esi + mov [esp + 4], eax ; [esp] = (FLAC__uint64)total_error_3 + fild qword [esp] ; ST = total_error_3 1.0 data_len + fdiv st2 ; ST = total_error_3/data_len 1.0 data_len + fldln2 ; ST = ln2 total_error_3/data_len 1.0 data_len + fmulp st1 ; ST = ln2*total_error_3/data_len 1.0 data_len + fyl2x ; ST = log2(ln2*total_error_3/data_len) data_len + fstp dword [ebx + 12] ; residual_bits_per_sample[3] = log2(ln2*total_error_3/data_len) ST = data_len + jmp short .rbps_4 +.total_error_3_is_0: + mov [ebx + 12], eax ; residual_bits_per_sample[3] = 0.0 +.rbps_4: + test edi, edi + jz .total_error_4_is_0 + fld1 ; ST = 1.0 data_len + mov [esp], edi + mov [esp + 4], eax ; [esp] = (FLAC__uint64)total_error_4 + fild qword [esp] ; ST = total_error_4 1.0 data_len + fdiv st2 ; ST = total_error_4/data_len 1.0 data_len + fldln2 ; ST = ln2 total_error_4/data_len 1.0 data_len + fmulp st1 ; ST = ln2*total_error_4/data_len 1.0 data_len + fyl2x ; ST = log2(ln2*total_error_4/data_len) data_len + fstp dword [ebx + 16] ; residual_bits_per_sample[4] = log2(ln2*total_error_4/data_len) ST = data_len + jmp short .rbps_end +.total_error_4_is_0: + mov [ebx + 16], eax ; residual_bits_per_sample[4] = 0.0 +.rbps_end: + fstp st0 ; ST = [empty] + jmp short .end +.data_len_is_0: + ; data_len == 0, so residual_bits_per_sample[*] = 0.0 + xor ebp, ebp + mov edi, [esp + 44] + mov [edi], ebp + mov [edi + 4], ebp + mov [edi + 8], ebp + mov [edi + 12], ebp + mov [edi + 16], ebp + add ebp, byte 4 ; order = 4 + +.end: + mov eax, ebp ; return order + add esp, byte 16 + pop edi + pop esi + pop ebx + pop ebp + ret + +end + +%ifdef OBJ_FORMAT_elf + section .note.GNU-stack noalloc +%endif diff --git a/src/FLAC/src/libFLAC/ia32/lpc_asm-unrolled.nasm b/src/FLAC/src/libFLAC/ia32/lpc_asm-unrolled.nasm new file mode 100644 index 00000000..c0eab415 --- /dev/null +++ b/src/FLAC/src/libFLAC/ia32/lpc_asm-unrolled.nasm @@ -0,0 +1,784 @@ +; vim:filetype=nasm ts=8 + +; libFLAC - Free Lossless Audio Codec library +; Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions +; are met: +; +; - Redistributions of source code must retain the above copyright +; notice, this list of conditions and the following disclaimer. +; +; - Redistributions in binary form must reproduce the above copyright +; notice, this list of conditions and the following disclaimer in the +; documentation and/or other materials provided with the distribution. +; +; - Neither the name of the Xiph.org Foundation nor the names of its +; contributors may be used to endorse or promote products derived from +; this software without specific prior written permission. +; +; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +; ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +; LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +; A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR +; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +; EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +; PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +; LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +; [CR] is a note to flag that the instruction can be easily reordered + +%include "nasm.h" + + data_section + +cglobal FLAC__lpc_compute_autocorrelation_asm + + code_section + +; ********************************************************************** +; +; void FLAC__lpc_compute_autocorrelation_asm(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]) +; { +; FLAC__real d; +; unsigned sample, coeff; +; const unsigned limit = data_len - lag; +; +; assert(lag > 0); +; assert(lag <= data_len); +; +; for(coeff = 0; coeff < lag; coeff++) +; autoc[coeff] = 0.0; +; for(sample = 0; sample <= limit; sample++){ +; d = data[sample]; +; for(coeff = 0; coeff < lag; coeff++) +; autoc[coeff] += d * data[sample+coeff]; +; } +; for(; sample < data_len; sample++){ +; d = data[sample]; +; for(coeff = 0; coeff < data_len - sample; coeff++) +; autoc[coeff] += d * data[sample+coeff]; +; } +; } +; +FLAC__lpc_compute_autocorrelation_asm: + + push ebp + lea ebp, [esp + 8] + push ebx + push esi + push edi + + mov edx, [ebp + 8] ; edx == lag + mov ecx, [ebp + 4] ; ecx == data_len + mov esi, [ebp] ; esi == data + mov edi, [ebp + 12] ; edi == autoc + + cmp edx, 1 + ja short .lag_above_1 +.lag_eq_1: + fldz ; will accumulate autoc[0] + ALIGN 16 +.lag_1_loop: + fld dword [esi] + add esi, byte 4 ; sample++ + fmul st0, st0 + faddp st1, st0 + dec ecx + jnz .lag_1_loop + fstp dword [edi] + jmp .end + +.lag_above_1: + cmp edx, 2 + ja short .lag_above_2 +.lag_eq_2: + fldz ; will accumulate autoc[1] + dec ecx + fldz ; will accumulate autoc[0] + fld dword [esi] + ALIGN 16 +.lag_2_loop: + add esi, byte 4 ; [CR] sample++ + fld st0 + fmul st0, st0 + faddp st2, st0 ; add to autoc[0] + fld dword [esi] + fmul st1, st0 + fxch + faddp st3, st0 ; add to autoc[1] + dec ecx + jnz .lag_2_loop + ; clean up the leftovers + fmul st0, st0 + faddp st1, st0 ; add to autoc[0] + fstp dword [edi] + fstp dword [edi + 4] + jmp .end + +.lag_above_2: + cmp edx, 3 + ja short .lag_above_3 +.lag_eq_3: + fldz ; will accumulate autoc[2] + dec ecx + fldz ; will accumulate autoc[1] + dec ecx + fldz ; will accumulate autoc[0] + ALIGN 16 +.lag_3_loop: + fld dword [esi] + fld st0 + fmul st0, st0 + faddp st2, st0 ; add to autoc[0] + fld dword [esi + 4] + fmul st0, st1 + faddp st3, st0 ; add to autoc[1] + fld dword [esi + 8] + fmulp st1, st0 + add esi, byte 4 ; [CR] sample++ + faddp st3, st0 ; add to autoc[2] + dec ecx + jnz .lag_3_loop + ; clean up the leftovers + fld dword [esi] + fld st0 + fmul st0, st0 + faddp st2, st0 ; add to autoc[0] + fld dword [esi + 4] + fmul st1, st0 + fxch + faddp st3, st0 ; add to autoc[1] + fmul st0, st0 + faddp st1, st0 ; add to autoc[0] + fstp dword [edi] + fstp dword [edi + 4] + fstp dword [edi + 8] + jmp .end + +.lag_above_3: + cmp edx, 4 + ja near .lag_above_4 +.lag_eq_4: + fldz ; will accumulate autoc[3] + dec ecx + fldz ; will accumulate autoc[2] + dec ecx + fldz ; will accumulate autoc[1] + dec ecx + fldz ; will accumulate autoc[0] + ALIGN 16 +.lag_4_loop: + fld dword [esi] + fld st0 + fmul st0, st0 + faddp st2, st0 ; add to autoc[0] + fld dword [esi + 4] + fmul st0, st1 + faddp st3, st0 ; add to autoc[1] + fld dword [esi + 8] + fmul st0, st1 + faddp st4, st0 ; add to autoc[2] + fld dword [esi + 12] + fmulp st1, st0 + add esi, byte 4 ; [CR] sample++ + faddp st4, st0 ; add to autoc[3] + dec ecx + jnz .lag_4_loop + ; clean up the leftovers + fld dword [esi] + fld st0 + fmul st0, st0 + faddp st2, st0 ; add to autoc[0] + fld dword [esi + 4] + fmul st0, st1 + faddp st3, st0 ; add to autoc[1] + fld dword [esi + 8] + fmulp st1, st0 + add esi, byte 4 ; [CR] sample++ + faddp st3, st0 ; add to autoc[2] + fld dword [esi] + fld st0 + fmul st0, st0 + faddp st2, st0 ; add to autoc[0] + fld dword [esi + 4] + fmul st1, st0 + fxch + faddp st3, st0 ; add to autoc[1] + fmul st0, st0 + faddp st1, st0 ; add to autoc[0] + fstp dword [edi] + fstp dword [edi + 4] + fstp dword [edi + 8] + fstp dword [edi + 12] + jmp .end + +.lag_above_4: + cmp edx, 5 + ja near .lag_above_5 +.lag_eq_5: + fldz ; will accumulate autoc[4] + fldz ; will accumulate autoc[3] + fldz ; will accumulate autoc[2] + fldz ; will accumulate autoc[1] + fldz ; will accumulate autoc[0] + sub ecx, byte 4 + ALIGN 16 +.lag_5_loop: + fld dword [esi] + fld st0 + fmul st0, st0 + faddp st2, st0 ; add to autoc[0] + fld dword [esi + 4] + fmul st0, st1 + faddp st3, st0 ; add to autoc[1] + fld dword [esi + 8] + fmul st0, st1 + faddp st4, st0 ; add to autoc[2] + fld dword [esi + 12] + fmul st0, st1 + faddp st5, st0 ; add to autoc[3] + fld dword [esi + 16] + fmulp st1, st0 + add esi, byte 4 ; [CR] sample++ + faddp st5, st0 ; add to autoc[4] + dec ecx + jnz .lag_5_loop + ; clean up the leftovers + fld dword [esi] + fld st0 + fmul st0, st0 + faddp st2, st0 ; add to autoc[0] + fld dword [esi + 4] + fmul st0, st1 + faddp st3, st0 ; add to autoc[1] + fld dword [esi + 8] + fmul st0, st1 + faddp st4, st0 ; add to autoc[2] + fld dword [esi + 12] + fmulp st1, st0 + add esi, byte 4 ; [CR] sample++ + faddp st4, st0 ; add to autoc[3] + fld dword [esi] + fld st0 + fmul st0, st0 + faddp st2, st0 ; add to autoc[0] + fld dword [esi + 4] + fmul st0, st1 + faddp st3, st0 ; add to autoc[1] + fld dword [esi + 8] + fmulp st1, st0 + add esi, byte 4 ; [CR] sample++ + faddp st3, st0 ; add to autoc[2] + fld dword [esi] + fld st0 + fmul st0, st0 + faddp st2, st0 ; add to autoc[0] + fld dword [esi + 4] + fmul st1, st0 + fxch + faddp st3, st0 ; add to autoc[1] + fmul st0, st0 + faddp st1, st0 ; add to autoc[0] + fstp dword [edi] + fstp dword [edi + 4] + fstp dword [edi + 8] + fstp dword [edi + 12] + fstp dword [edi + 16] + jmp .end + +.lag_above_5: + cmp edx, 6 + ja .lag_above_6 +.lag_eq_6: + fldz ; will accumulate autoc[5] + fldz ; will accumulate autoc[4] + fldz ; will accumulate autoc[3] + fldz ; will accumulate autoc[2] + fldz ; will accumulate autoc[1] + fldz ; will accumulate autoc[0] + sub ecx, byte 5 + ALIGN 16 +.lag_6_loop: + fld dword [esi] + fld st0 + fmul st0, st0 + faddp st2, st0 ; add to autoc[0] + fld dword [esi + 4] + fmul st0, st1 + faddp st3, st0 ; add to autoc[1] + fld dword [esi + 8] + fmul st0, st1 + faddp st4, st0 ; add to autoc[2] + fld dword [esi + 12] + fmul st0, st1 + faddp st5, st0 ; add to autoc[3] + fld dword [esi + 16] + fmul st0, st1 + faddp st6, st0 ; add to autoc[4] + fld dword [esi + 20] + fmulp st1, st0 + add esi, byte 4 ; [CR] sample++ + faddp st6, st0 ; add to autoc[5] + dec ecx + jnz .lag_6_loop + ; clean up the leftovers + fld dword [esi] + fld st0 + fmul st0, st0 + faddp st2, st0 ; add to autoc[0] + fld dword [esi + 4] + fmul st0, st1 + faddp st3, st0 ; add to autoc[1] + fld dword [esi + 8] + fmul st0, st1 + faddp st4, st0 ; add to autoc[2] + fld dword [esi + 12] + fmul st0, st1 + faddp st5, st0 ; add to autoc[3] + fld dword [esi + 16] + fmulp st1, st0 + add esi, byte 4 ; [CR] sample++ + faddp st5, st0 ; add to autoc[4] + fld dword [esi] + fld st0 + fmul st0, st0 + faddp st2, st0 ; add to autoc[0] + fld dword [esi + 4] + fmul st0, st1 + faddp st3, st0 ; add to autoc[1] + fld dword [esi + 8] + fmul st0, st1 + faddp st4, st0 ; add to autoc[2] + fld dword [esi + 12] + fmulp st1, st0 + add esi, byte 4 ; [CR] sample++ + faddp st4, st0 ; add to autoc[3] + fld dword [esi] + fld st0 + fmul st0, st0 + faddp st2, st0 ; add to autoc[0] + fld dword [esi + 4] + fmul st0, st1 + faddp st3, st0 ; add to autoc[1] + fld dword [esi + 8] + fmulp st1, st0 + add esi, byte 4 ; [CR] sample++ + faddp st3, st0 ; add to autoc[2] + fld dword [esi] + fld st0 + fmul st0, st0 + faddp st2, st0 ; add to autoc[0] + fld dword [esi + 4] + fmul st1, st0 + fxch + faddp st3, st0 ; add to autoc[1] + fmul st0, st0 + faddp st1, st0 ; add to autoc[0] + fstp dword [edi] + fstp dword [edi + 4] + fstp dword [edi + 8] + fstp dword [edi + 12] + fstp dword [edi + 16] + fstp dword [edi + 20] + jmp .end + +.lag_above_6: + ; for(coeff = 0; coeff < lag; coeff++) + ; autoc[coeff] = 0.0; + lea ecx, [edx * 2] ; ecx = # of dwords of 0 to write + xor eax, eax + rep stosd + mov ecx, [ebp + 4] ; ecx == data_len + mov edi, [ebp + 12] ; edi == autoc + ; const unsigned limit = data_len - lag; + sub ecx, edx + inc ecx ; we are looping <= limit so we add one to the counter + ; for(sample = 0; sample <= limit; sample++){ + ; d = data[sample]; + ; for(coeff = 0; coeff < lag; coeff++) + ; autoc[coeff] += d * data[sample+coeff]; + ; } + xor eax, eax ; eax == sample <- 0 + ALIGN 16 +.outer_loop: + push eax ; save sample + fld dword [esi + eax * 4] ; ST = d <- data[sample] + mov ebx, eax ; ebx == sample+coeff <- sample + mov edx, [ebp + 8] ; edx <- lag + xor eax, eax ; eax == coeff <- 0 + ALIGN 16 +.inner_loop: + fld st0 ; ST = d d + fmul dword [esi + ebx * 4] ; ST = d*data[sample+coeff] d + fadd dword [edi + eax * 4] ; ST = autoc[coeff]+d*data[sample+coeff] d + fstp dword [edi + eax * 4] ; autoc[coeff]+=d*data[sample+coeff] ST = d + inc ebx ; (sample+coeff)++ + inc eax ; coeff++ + dec edx + jnz .inner_loop + pop eax ; restore sample + fstp st0 ; pop d, ST = empty + inc eax ; sample++ + loop .outer_loop + ; for(; sample < data_len; sample++){ + ; d = data[sample]; + ; for(coeff = 0; coeff < data_len - sample; coeff++) + ; autoc[coeff] += d * data[sample+coeff]; + ; } + mov ecx, [ebp + 8] ; ecx <- lag + dec ecx ; ecx <- lag - 1 + jz .outer_end ; skip loop if 0 +.outer_loop2: + push eax ; save sample + fld dword [esi + eax * 4] ; ST = d <- data[sample] + mov ebx, eax ; ebx == sample+coeff <- sample + mov edx, [ebp + 4] ; edx <- data_len + sub edx, eax ; edx <- data_len-sample + xor eax, eax ; eax == coeff <- 0 +.inner_loop2: + fld st0 ; ST = d d + fmul dword [esi + ebx * 4] ; ST = d*data[sample+coeff] d + fadd dword [edi + eax * 4] ; ST = autoc[coeff]+d*data[sample+coeff] d + fstp dword [edi + eax * 4] ; autoc[coeff]+=d*data[sample+coeff] ST = d + inc ebx ; (sample+coeff)++ + inc eax ; coeff++ + dec edx + jnz .inner_loop2 + pop eax ; restore sample + fstp st0 ; pop d, ST = empty + inc eax ; sample++ + loop .outer_loop2 +.outer_end: + jmp .end + +.lag_eq_6_plus_1: + mov ecx, [ebp + 4] ; ecx == data_len + mov esi, [ebp] ; esi == data + mov edi, [ebp + 12] ; edi == autoc + fldz ; will accumulate autoc[6] + sub ecx, byte 6 + ALIGN 16 +.lag_6_1_loop: + fld dword [esi] + fld dword [esi + 24] + fmulp st1, st0 + add esi, byte 4 ; [CR] sample++ + faddp st1, st0 ; add to autoc[6] + dec ecx + jnz .lag_6_1_loop + fstp dword [edi + 24] + jmp .end + +.lag_eq_6_plus_2: + mov ecx, [ebp + 4] ; ecx == data_len + mov esi, [ebp] ; esi == data + mov edi, [ebp + 12] ; edi == autoc + fldz ; will accumulate autoc[7] + fldz ; will accumulate autoc[6] + sub ecx, byte 7 + ALIGN 16 +.lag_6_2_loop: + fld dword [esi] + fld dword [esi + 24] + fmul st0, st1 + faddp st2, st0 ; add to autoc[6] + fld dword [esi + 28] + fmulp st1, st0 + add esi, byte 4 ; [CR] sample++ + faddp st2, st0 ; add to autoc[7] + dec ecx + jnz .lag_6_2_loop + ; clean up the leftovers + fld dword [esi] + fld dword [esi + 24] + fmulp st1, st0 + faddp st1, st0 ; add to autoc[6] + fstp dword [edi + 24] + fstp dword [edi + 28] + jmp .end + +.lag_eq_6_plus_3: + mov ecx, [ebp + 4] ; ecx == data_len + mov esi, [ebp] ; esi == data + mov edi, [ebp + 12] ; edi == autoc + fldz ; will accumulate autoc[8] + fldz ; will accumulate autoc[7] + fldz ; will accumulate autoc[6] + sub ecx, byte 8 + ALIGN 16 +.lag_6_3_loop: + fld dword [esi] + fld dword [esi + 24] + fmul st0, st1 + faddp st2, st0 ; add to autoc[6] + fld dword [esi + 28] + fmul st0, st1 + faddp st3, st0 ; add to autoc[7] + fld dword [esi + 32] + fmulp st1, st0 + add esi, byte 4 ; [CR] sample++ + faddp st3, st0 ; add to autoc[8] + dec ecx + jnz .lag_6_3_loop + ; clean up the leftovers + fld dword [esi] + fld dword [esi + 24] + fmul st0, st1 + faddp st2, st0 ; add to autoc[6] + fld dword [esi + 28] + fmulp st1, st0 + add esi, byte 4 ; [CR] sample++ + faddp st2, st0 ; add to autoc[7] + fld dword [esi] + fld dword [esi + 24] + fmulp st1, st0 + faddp st1, st0 ; add to autoc[6] + fstp dword [edi + 24] + fstp dword [edi + 28] + fstp dword [edi + 32] + jmp .end + +.lag_eq_6_plus_4: + mov ecx, [ebp + 4] ; ecx == data_len + mov esi, [ebp] ; esi == data + mov edi, [ebp + 12] ; edi == autoc + fldz ; will accumulate autoc[9] + fldz ; will accumulate autoc[8] + fldz ; will accumulate autoc[7] + fldz ; will accumulate autoc[6] + sub ecx, byte 9 + ALIGN 16 +.lag_6_4_loop: + fld dword [esi] + fld dword [esi + 24] + fmul st0, st1 + faddp st2, st0 ; add to autoc[6] + fld dword [esi + 28] + fmul st0, st1 + faddp st3, st0 ; add to autoc[7] + fld dword [esi + 32] + fmul st0, st1 + faddp st4, st0 ; add to autoc[8] + fld dword [esi + 36] + fmulp st1, st0 + add esi, byte 4 ; [CR] sample++ + faddp st4, st0 ; add to autoc[9] + dec ecx + jnz .lag_6_4_loop + ; clean up the leftovers + fld dword [esi] + fld dword [esi + 24] + fmul st0, st1 + faddp st2, st0 ; add to autoc[6] + fld dword [esi + 28] + fmul st0, st1 + faddp st3, st0 ; add to autoc[7] + fld dword [esi + 32] + fmulp st1, st0 + add esi, byte 4 ; [CR] sample++ + faddp st3, st0 ; add to autoc[8] + fld dword [esi] + fld dword [esi + 24] + fmul st0, st1 + faddp st2, st0 ; add to autoc[6] + fld dword [esi + 28] + fmulp st1, st0 + add esi, byte 4 ; [CR] sample++ + faddp st2, st0 ; add to autoc[7] + fld dword [esi] + fld dword [esi + 24] + fmulp st1, st0 + faddp st1, st0 ; add to autoc[6] + fstp dword [edi + 24] + fstp dword [edi + 28] + fstp dword [edi + 32] + fstp dword [edi + 36] + jmp .end + +.lag_eq_6_plus_5: + mov ecx, [ebp + 4] ; ecx == data_len + mov esi, [ebp] ; esi == data + mov edi, [ebp + 12] ; edi == autoc + fldz ; will accumulate autoc[10] + fldz ; will accumulate autoc[9] + fldz ; will accumulate autoc[8] + fldz ; will accumulate autoc[7] + fldz ; will accumulate autoc[6] + sub ecx, byte 10 + ALIGN 16 +.lag_6_5_loop: + fld dword [esi] + fld dword [esi + 24] + fmul st0, st1 + faddp st2, st0 ; add to autoc[6] + fld dword [esi + 28] + fmul st0, st1 + faddp st3, st0 ; add to autoc[7] + fld dword [esi + 32] + fmul st0, st1 + faddp st4, st0 ; add to autoc[8] + fld dword [esi + 36] + fmul st0, st1 + faddp st5, st0 ; add to autoc[9] + fld dword [esi + 40] + fmulp st1, st0 + add esi, byte 4 ; [CR] sample++ + faddp st5, st0 ; add to autoc[10] + dec ecx + jnz .lag_6_5_loop + ; clean up the leftovers + fld dword [esi] + fld dword [esi + 24] + fmul st0, st1 + faddp st2, st0 ; add to autoc[6] + fld dword [esi + 28] + fmul st0, st1 + faddp st3, st0 ; add to autoc[7] + fld dword [esi + 32] + fmul st0, st1 + faddp st4, st0 ; add to autoc[8] + fld dword [esi + 36] + fmulp st1, st0 + add esi, byte 4 ; [CR] sample++ + faddp st4, st0 ; add to autoc[9] + fld dword [esi] + fld dword [esi + 24] + fmul st0, st1 + faddp st2, st0 ; add to autoc[6] + fld dword [esi + 28] + fmul st0, st1 + faddp st3, st0 ; add to autoc[7] + fld dword [esi + 32] + fmulp st1, st0 + add esi, byte 4 ; [CR] sample++ + faddp st3, st0 ; add to autoc[8] + fld dword [esi] + fld dword [esi + 24] + fmul st0, st1 + faddp st2, st0 ; add to autoc[6] + fld dword [esi + 28] + fmulp st1, st0 + add esi, byte 4 ; [CR] sample++ + faddp st2, st0 ; add to autoc[7] + fld dword [esi] + fld dword [esi + 24] + fmulp st1, st0 + faddp st1, st0 ; add to autoc[6] + fstp dword [edi + 24] + fstp dword [edi + 28] + fstp dword [edi + 32] + fstp dword [edi + 36] + fstp dword [edi + 40] + jmp .end + +.lag_eq_6_plus_6: + mov ecx, [ebp + 4] ; ecx == data_len + mov esi, [ebp] ; esi == data + mov edi, [ebp + 12] ; edi == autoc + fldz ; will accumulate autoc[11] + fldz ; will accumulate autoc[10] + fldz ; will accumulate autoc[9] + fldz ; will accumulate autoc[8] + fldz ; will accumulate autoc[7] + fldz ; will accumulate autoc[6] + sub ecx, byte 11 + ALIGN 16 +.lag_6_6_loop: + fld dword [esi] + fld dword [esi + 24] + fmul st0, st1 + faddp st2, st0 ; add to autoc[6] + fld dword [esi + 28] + fmul st0, st1 + faddp st3, st0 ; add to autoc[7] + fld dword [esi + 32] + fmul st0, st1 + faddp st4, st0 ; add to autoc[8] + fld dword [esi + 36] + fmul st0, st1 + faddp st5, st0 ; add to autoc[9] + fld dword [esi + 40] + fmul st0, st1 + faddp st6, st0 ; add to autoc[10] + fld dword [esi + 44] + fmulp st1, st0 + add esi, byte 4 ; [CR] sample++ + faddp st6, st0 ; add to autoc[11] + dec ecx + jnz .lag_6_6_loop + ; clean up the leftovers + fld dword [esi] + fld dword [esi + 24] + fmul st0, st1 + faddp st2, st0 ; add to autoc[6] + fld dword [esi + 28] + fmul st0, st1 + faddp st3, st0 ; add to autoc[7] + fld dword [esi + 32] + fmul st0, st1 + faddp st4, st0 ; add to autoc[8] + fld dword [esi + 36] + fmul st0, st1 + faddp st5, st0 ; add to autoc[9] + fld dword [esi + 40] + fmulp st1, st0 + add esi, byte 4 ; [CR] sample++ + faddp st5, st0 ; add to autoc[10] + fld dword [esi] + fld dword [esi + 24] + fmul st0, st1 + faddp st2, st0 ; add to autoc[6] + fld dword [esi + 28] + fmul st0, st1 + faddp st3, st0 ; add to autoc[7] + fld dword [esi + 32] + fmul st0, st1 + faddp st4, st0 ; add to autoc[8] + fld dword [esi + 36] + fmulp st1, st0 + add esi, byte 4 ; [CR] sample++ + faddp st4, st0 ; add to autoc[9] + fld dword [esi] + fld dword [esi + 24] + fmul st0, st1 + faddp st2, st0 ; add to autoc[6] + fld dword [esi + 28] + fmul st0, st1 + faddp st3, st0 ; add to autoc[7] + fld dword [esi + 32] + fmulp st1, st0 + add esi, byte 4 ; [CR] sample++ + faddp st3, st0 ; add to autoc[8] + fld dword [esi] + fld dword [esi + 24] + fmul st0, st1 + faddp st2, st0 ; add to autoc[6] + fld dword [esi + 28] + fmulp st1, st0 + add esi, byte 4 ; [CR] sample++ + faddp st2, st0 ; add to autoc[7] + fld dword [esi] + fld dword [esi + 24] + fmulp st1, st0 + faddp st1, st0 ; add to autoc[6] + fstp dword [edi + 24] + fstp dword [edi + 28] + fstp dword [edi + 32] + fstp dword [edi + 36] + fstp dword [edi + 40] + fstp dword [edi + 44] + jmp .end + +.end: + pop edi + pop esi + pop ebx + pop ebp + ret + +end diff --git a/src/FLAC/src/libFLAC/ia32/lpc_asm.nasm b/src/FLAC/src/libFLAC/ia32/lpc_asm.nasm new file mode 100644 index 00000000..4bc4c913 --- /dev/null +++ b/src/FLAC/src/libFLAC/ia32/lpc_asm.nasm @@ -0,0 +1,1511 @@ +; vim:filetype=nasm ts=8 + +; libFLAC - Free Lossless Audio Codec library +; Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions +; are met: +; +; - Redistributions of source code must retain the above copyright +; notice, this list of conditions and the following disclaimer. +; +; - Redistributions in binary form must reproduce the above copyright +; notice, this list of conditions and the following disclaimer in the +; documentation and/or other materials provided with the distribution. +; +; - Neither the name of the Xiph.org Foundation nor the names of its +; contributors may be used to endorse or promote products derived from +; this software without specific prior written permission. +; +; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +; ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +; LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +; A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR +; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +; EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +; PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +; LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +%include "nasm.h" + + data_section + +cglobal FLAC__lpc_compute_autocorrelation_asm_ia32 +cglobal FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_4 +cglobal FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_8 +cglobal FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_12 +cglobal FLAC__lpc_compute_autocorrelation_asm_ia32_3dnow +cglobal FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32 +cglobal FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32_mmx +cglobal FLAC__lpc_restore_signal_asm_ia32 +cglobal FLAC__lpc_restore_signal_asm_ia32_mmx + + code_section + +; ********************************************************************** +; +; void FLAC__lpc_compute_autocorrelation_asm(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]) +; { +; FLAC__real d; +; unsigned sample, coeff; +; const unsigned limit = data_len - lag; +; +; FLAC__ASSERT(lag > 0); +; FLAC__ASSERT(lag <= data_len); +; +; for(coeff = 0; coeff < lag; coeff++) +; autoc[coeff] = 0.0; +; for(sample = 0; sample <= limit; sample++) { +; d = data[sample]; +; for(coeff = 0; coeff < lag; coeff++) +; autoc[coeff] += d * data[sample+coeff]; +; } +; for(; sample < data_len; sample++) { +; d = data[sample]; +; for(coeff = 0; coeff < data_len - sample; coeff++) +; autoc[coeff] += d * data[sample+coeff]; +; } +; } +; + ALIGN 16 +cident FLAC__lpc_compute_autocorrelation_asm_ia32 + ;[esp + 28] == autoc[] + ;[esp + 24] == lag + ;[esp + 20] == data_len + ;[esp + 16] == data[] + + ;ASSERT(lag > 0) + ;ASSERT(lag <= 33) + ;ASSERT(lag <= data_len) + +.begin: + push esi + push edi + push ebx + + ; for(coeff = 0; coeff < lag; coeff++) + ; autoc[coeff] = 0.0; + mov edi, [esp + 28] ; edi == autoc + mov ecx, [esp + 24] ; ecx = # of dwords (=lag) of 0 to write + xor eax, eax + rep stosd + + ; const unsigned limit = data_len - lag; + mov eax, [esp + 24] ; eax == lag + mov ecx, [esp + 20] + sub ecx, eax ; ecx == limit + + mov edi, [esp + 28] ; edi == autoc + mov esi, [esp + 16] ; esi == data + inc ecx ; we are looping <= limit so we add one to the counter + + ; for(sample = 0; sample <= limit; sample++) { + ; d = data[sample]; + ; for(coeff = 0; coeff < lag; coeff++) + ; autoc[coeff] += d * data[sample+coeff]; + ; } + fld dword [esi] ; ST = d <- data[sample] + ; each iteration is 11 bytes so we need (-eax)*11, so we do (-12*eax + eax) + lea edx, [eax + eax*2] + neg edx + lea edx, [eax + edx*4 + .jumper1_0 - .get_eip1] + call .get_eip1 +.get_eip1: + pop ebx + add edx, ebx + inc edx ; compensate for the shorter opcode on the last iteration + inc edx ; compensate for the shorter opcode on the last iteration + inc edx ; compensate for the shorter opcode on the last iteration + cmp eax, 33 + jne .loop1_start + sub edx, byte 9 ; compensate for the longer opcodes on the first iteration +.loop1_start: + jmp edx + + fld st0 ; ST = d d + fmul dword [esi + (32*4)] ; ST = d*data[sample+32] d WATCHOUT: not a byte displacement here! + fadd dword [edi + (32*4)] ; ST = autoc[32]+d*data[sample+32] d WATCHOUT: not a byte displacement here! + fstp dword [edi + (32*4)] ; autoc[32]+=d*data[sample+32] ST = d WATCHOUT: not a byte displacement here! + fld st0 ; ST = d d + fmul dword [esi + (31*4)] ; ST = d*data[sample+31] d + fadd dword [edi + (31*4)] ; ST = autoc[31]+d*data[sample+31] d + fstp dword [edi + (31*4)] ; autoc[31]+=d*data[sample+31] ST = d + fld st0 ; ST = d d + fmul dword [esi + (30*4)] ; ST = d*data[sample+30] d + fadd dword [edi + (30*4)] ; ST = autoc[30]+d*data[sample+30] d + fstp dword [edi + (30*4)] ; autoc[30]+=d*data[sample+30] ST = d + fld st0 ; ST = d d + fmul dword [esi + (29*4)] ; ST = d*data[sample+29] d + fadd dword [edi + (29*4)] ; ST = autoc[29]+d*data[sample+29] d + fstp dword [edi + (29*4)] ; autoc[29]+=d*data[sample+29] ST = d + fld st0 ; ST = d d + fmul dword [esi + (28*4)] ; ST = d*data[sample+28] d + fadd dword [edi + (28*4)] ; ST = autoc[28]+d*data[sample+28] d + fstp dword [edi + (28*4)] ; autoc[28]+=d*data[sample+28] ST = d + fld st0 ; ST = d d + fmul dword [esi + (27*4)] ; ST = d*data[sample+27] d + fadd dword [edi + (27*4)] ; ST = autoc[27]+d*data[sample+27] d + fstp dword [edi + (27*4)] ; autoc[27]+=d*data[sample+27] ST = d + fld st0 ; ST = d d + fmul dword [esi + (26*4)] ; ST = d*data[sample+26] d + fadd dword [edi + (26*4)] ; ST = autoc[26]+d*data[sample+26] d + fstp dword [edi + (26*4)] ; autoc[26]+=d*data[sample+26] ST = d + fld st0 ; ST = d d + fmul dword [esi + (25*4)] ; ST = d*data[sample+25] d + fadd dword [edi + (25*4)] ; ST = autoc[25]+d*data[sample+25] d + fstp dword [edi + (25*4)] ; autoc[25]+=d*data[sample+25] ST = d + fld st0 ; ST = d d + fmul dword [esi + (24*4)] ; ST = d*data[sample+24] d + fadd dword [edi + (24*4)] ; ST = autoc[24]+d*data[sample+24] d + fstp dword [edi + (24*4)] ; autoc[24]+=d*data[sample+24] ST = d + fld st0 ; ST = d d + fmul dword [esi + (23*4)] ; ST = d*data[sample+23] d + fadd dword [edi + (23*4)] ; ST = autoc[23]+d*data[sample+23] d + fstp dword [edi + (23*4)] ; autoc[23]+=d*data[sample+23] ST = d + fld st0 ; ST = d d + fmul dword [esi + (22*4)] ; ST = d*data[sample+22] d + fadd dword [edi + (22*4)] ; ST = autoc[22]+d*data[sample+22] d + fstp dword [edi + (22*4)] ; autoc[22]+=d*data[sample+22] ST = d + fld st0 ; ST = d d + fmul dword [esi + (21*4)] ; ST = d*data[sample+21] d + fadd dword [edi + (21*4)] ; ST = autoc[21]+d*data[sample+21] d + fstp dword [edi + (21*4)] ; autoc[21]+=d*data[sample+21] ST = d + fld st0 ; ST = d d + fmul dword [esi + (20*4)] ; ST = d*data[sample+20] d + fadd dword [edi + (20*4)] ; ST = autoc[20]+d*data[sample+20] d + fstp dword [edi + (20*4)] ; autoc[20]+=d*data[sample+20] ST = d + fld st0 ; ST = d d + fmul dword [esi + (19*4)] ; ST = d*data[sample+19] d + fadd dword [edi + (19*4)] ; ST = autoc[19]+d*data[sample+19] d + fstp dword [edi + (19*4)] ; autoc[19]+=d*data[sample+19] ST = d + fld st0 ; ST = d d + fmul dword [esi + (18*4)] ; ST = d*data[sample+18] d + fadd dword [edi + (18*4)] ; ST = autoc[18]+d*data[sample+18] d + fstp dword [edi + (18*4)] ; autoc[18]+=d*data[sample+18] ST = d + fld st0 ; ST = d d + fmul dword [esi + (17*4)] ; ST = d*data[sample+17] d + fadd dword [edi + (17*4)] ; ST = autoc[17]+d*data[sample+17] d + fstp dword [edi + (17*4)] ; autoc[17]+=d*data[sample+17] ST = d + fld st0 ; ST = d d + fmul dword [esi + (16*4)] ; ST = d*data[sample+16] d + fadd dword [edi + (16*4)] ; ST = autoc[16]+d*data[sample+16] d + fstp dword [edi + (16*4)] ; autoc[16]+=d*data[sample+16] ST = d + fld st0 ; ST = d d + fmul dword [esi + (15*4)] ; ST = d*data[sample+15] d + fadd dword [edi + (15*4)] ; ST = autoc[15]+d*data[sample+15] d + fstp dword [edi + (15*4)] ; autoc[15]+=d*data[sample+15] ST = d + fld st0 ; ST = d d + fmul dword [esi + (14*4)] ; ST = d*data[sample+14] d + fadd dword [edi + (14*4)] ; ST = autoc[14]+d*data[sample+14] d + fstp dword [edi + (14*4)] ; autoc[14]+=d*data[sample+14] ST = d + fld st0 ; ST = d d + fmul dword [esi + (13*4)] ; ST = d*data[sample+13] d + fadd dword [edi + (13*4)] ; ST = autoc[13]+d*data[sample+13] d + fstp dword [edi + (13*4)] ; autoc[13]+=d*data[sample+13] ST = d + fld st0 ; ST = d d + fmul dword [esi + (12*4)] ; ST = d*data[sample+12] d + fadd dword [edi + (12*4)] ; ST = autoc[12]+d*data[sample+12] d + fstp dword [edi + (12*4)] ; autoc[12]+=d*data[sample+12] ST = d + fld st0 ; ST = d d + fmul dword [esi + (11*4)] ; ST = d*data[sample+11] d + fadd dword [edi + (11*4)] ; ST = autoc[11]+d*data[sample+11] d + fstp dword [edi + (11*4)] ; autoc[11]+=d*data[sample+11] ST = d + fld st0 ; ST = d d + fmul dword [esi + (10*4)] ; ST = d*data[sample+10] d + fadd dword [edi + (10*4)] ; ST = autoc[10]+d*data[sample+10] d + fstp dword [edi + (10*4)] ; autoc[10]+=d*data[sample+10] ST = d + fld st0 ; ST = d d + fmul dword [esi + ( 9*4)] ; ST = d*data[sample+9] d + fadd dword [edi + ( 9*4)] ; ST = autoc[9]+d*data[sample+9] d + fstp dword [edi + ( 9*4)] ; autoc[9]+=d*data[sample+9] ST = d + fld st0 ; ST = d d + fmul dword [esi + ( 8*4)] ; ST = d*data[sample+8] d + fadd dword [edi + ( 8*4)] ; ST = autoc[8]+d*data[sample+8] d + fstp dword [edi + ( 8*4)] ; autoc[8]+=d*data[sample+8] ST = d + fld st0 ; ST = d d + fmul dword [esi + ( 7*4)] ; ST = d*data[sample+7] d + fadd dword [edi + ( 7*4)] ; ST = autoc[7]+d*data[sample+7] d + fstp dword [edi + ( 7*4)] ; autoc[7]+=d*data[sample+7] ST = d + fld st0 ; ST = d d + fmul dword [esi + ( 6*4)] ; ST = d*data[sample+6] d + fadd dword [edi + ( 6*4)] ; ST = autoc[6]+d*data[sample+6] d + fstp dword [edi + ( 6*4)] ; autoc[6]+=d*data[sample+6] ST = d + fld st0 ; ST = d d + fmul dword [esi + ( 5*4)] ; ST = d*data[sample+4] d + fadd dword [edi + ( 5*4)] ; ST = autoc[4]+d*data[sample+4] d + fstp dword [edi + ( 5*4)] ; autoc[4]+=d*data[sample+4] ST = d + fld st0 ; ST = d d + fmul dword [esi + ( 4*4)] ; ST = d*data[sample+4] d + fadd dword [edi + ( 4*4)] ; ST = autoc[4]+d*data[sample+4] d + fstp dword [edi + ( 4*4)] ; autoc[4]+=d*data[sample+4] ST = d + fld st0 ; ST = d d + fmul dword [esi + ( 3*4)] ; ST = d*data[sample+3] d + fadd dword [edi + ( 3*4)] ; ST = autoc[3]+d*data[sample+3] d + fstp dword [edi + ( 3*4)] ; autoc[3]+=d*data[sample+3] ST = d + fld st0 ; ST = d d + fmul dword [esi + ( 2*4)] ; ST = d*data[sample+2] d + fadd dword [edi + ( 2*4)] ; ST = autoc[2]+d*data[sample+2] d + fstp dword [edi + ( 2*4)] ; autoc[2]+=d*data[sample+2] ST = d + fld st0 ; ST = d d + fmul dword [esi + ( 1*4)] ; ST = d*data[sample+1] d + fadd dword [edi + ( 1*4)] ; ST = autoc[1]+d*data[sample+1] d + fstp dword [edi + ( 1*4)] ; autoc[1]+=d*data[sample+1] ST = d + fld st0 ; ST = d d + fmul dword [esi] ; ST = d*data[sample] d WATCHOUT: no displacement byte here! + fadd dword [edi] ; ST = autoc[0]+d*data[sample] d WATCHOUT: no displacement byte here! + fstp dword [edi] ; autoc[0]+=d*data[sample] ST = d WATCHOUT: no displacement byte here! +.jumper1_0: + + fstp st0 ; pop d, ST = empty + add esi, byte 4 ; sample++ + dec ecx + jz .loop1_end + fld dword [esi] ; ST = d <- data[sample] + jmp edx +.loop1_end: + + ; for(; sample < data_len; sample++) { + ; d = data[sample]; + ; for(coeff = 0; coeff < data_len - sample; coeff++) + ; autoc[coeff] += d * data[sample+coeff]; + ; } + mov ecx, [esp + 24] ; ecx <- lag + dec ecx ; ecx <- lag - 1 + jz near .end ; skip loop if 0 (i.e. lag == 1) + + fld dword [esi] ; ST = d <- data[sample] + mov eax, ecx ; eax <- lag - 1 == data_len - sample the first time through + ; each iteration is 11 bytes so we need (-eax)*11, so we do (-12*eax + eax) + lea edx, [eax + eax*2] + neg edx + lea edx, [eax + edx*4 + .jumper2_0 - .get_eip2] + call .get_eip2 +.get_eip2: + pop ebx + add edx, ebx + inc edx ; compensate for the shorter opcode on the last iteration + inc edx ; compensate for the shorter opcode on the last iteration + inc edx ; compensate for the shorter opcode on the last iteration + jmp edx + + fld st0 ; ST = d d + fmul dword [esi + (31*4)] ; ST = d*data[sample+31] d + fadd dword [edi + (31*4)] ; ST = autoc[31]+d*data[sample+31] d + fstp dword [edi + (31*4)] ; autoc[31]+=d*data[sample+31] ST = d + fld st0 ; ST = d d + fmul dword [esi + (30*4)] ; ST = d*data[sample+30] d + fadd dword [edi + (30*4)] ; ST = autoc[30]+d*data[sample+30] d + fstp dword [edi + (30*4)] ; autoc[30]+=d*data[sample+30] ST = d + fld st0 ; ST = d d + fmul dword [esi + (29*4)] ; ST = d*data[sample+29] d + fadd dword [edi + (29*4)] ; ST = autoc[29]+d*data[sample+29] d + fstp dword [edi + (29*4)] ; autoc[29]+=d*data[sample+29] ST = d + fld st0 ; ST = d d + fmul dword [esi + (28*4)] ; ST = d*data[sample+28] d + fadd dword [edi + (28*4)] ; ST = autoc[28]+d*data[sample+28] d + fstp dword [edi + (28*4)] ; autoc[28]+=d*data[sample+28] ST = d + fld st0 ; ST = d d + fmul dword [esi + (27*4)] ; ST = d*data[sample+27] d + fadd dword [edi + (27*4)] ; ST = autoc[27]+d*data[sample+27] d + fstp dword [edi + (27*4)] ; autoc[27]+=d*data[sample+27] ST = d + fld st0 ; ST = d d + fmul dword [esi + (26*4)] ; ST = d*data[sample+26] d + fadd dword [edi + (26*4)] ; ST = autoc[26]+d*data[sample+26] d + fstp dword [edi + (26*4)] ; autoc[26]+=d*data[sample+26] ST = d + fld st0 ; ST = d d + fmul dword [esi + (25*4)] ; ST = d*data[sample+25] d + fadd dword [edi + (25*4)] ; ST = autoc[25]+d*data[sample+25] d + fstp dword [edi + (25*4)] ; autoc[25]+=d*data[sample+25] ST = d + fld st0 ; ST = d d + fmul dword [esi + (24*4)] ; ST = d*data[sample+24] d + fadd dword [edi + (24*4)] ; ST = autoc[24]+d*data[sample+24] d + fstp dword [edi + (24*4)] ; autoc[24]+=d*data[sample+24] ST = d + fld st0 ; ST = d d + fmul dword [esi + (23*4)] ; ST = d*data[sample+23] d + fadd dword [edi + (23*4)] ; ST = autoc[23]+d*data[sample+23] d + fstp dword [edi + (23*4)] ; autoc[23]+=d*data[sample+23] ST = d + fld st0 ; ST = d d + fmul dword [esi + (22*4)] ; ST = d*data[sample+22] d + fadd dword [edi + (22*4)] ; ST = autoc[22]+d*data[sample+22] d + fstp dword [edi + (22*4)] ; autoc[22]+=d*data[sample+22] ST = d + fld st0 ; ST = d d + fmul dword [esi + (21*4)] ; ST = d*data[sample+21] d + fadd dword [edi + (21*4)] ; ST = autoc[21]+d*data[sample+21] d + fstp dword [edi + (21*4)] ; autoc[21]+=d*data[sample+21] ST = d + fld st0 ; ST = d d + fmul dword [esi + (20*4)] ; ST = d*data[sample+20] d + fadd dword [edi + (20*4)] ; ST = autoc[20]+d*data[sample+20] d + fstp dword [edi + (20*4)] ; autoc[20]+=d*data[sample+20] ST = d + fld st0 ; ST = d d + fmul dword [esi + (19*4)] ; ST = d*data[sample+19] d + fadd dword [edi + (19*4)] ; ST = autoc[19]+d*data[sample+19] d + fstp dword [edi + (19*4)] ; autoc[19]+=d*data[sample+19] ST = d + fld st0 ; ST = d d + fmul dword [esi + (18*4)] ; ST = d*data[sample+18] d + fadd dword [edi + (18*4)] ; ST = autoc[18]+d*data[sample+18] d + fstp dword [edi + (18*4)] ; autoc[18]+=d*data[sample+18] ST = d + fld st0 ; ST = d d + fmul dword [esi + (17*4)] ; ST = d*data[sample+17] d + fadd dword [edi + (17*4)] ; ST = autoc[17]+d*data[sample+17] d + fstp dword [edi + (17*4)] ; autoc[17]+=d*data[sample+17] ST = d + fld st0 ; ST = d d + fmul dword [esi + (16*4)] ; ST = d*data[sample+16] d + fadd dword [edi + (16*4)] ; ST = autoc[16]+d*data[sample+16] d + fstp dword [edi + (16*4)] ; autoc[16]+=d*data[sample+16] ST = d + fld st0 ; ST = d d + fmul dword [esi + (15*4)] ; ST = d*data[sample+15] d + fadd dword [edi + (15*4)] ; ST = autoc[15]+d*data[sample+15] d + fstp dword [edi + (15*4)] ; autoc[15]+=d*data[sample+15] ST = d + fld st0 ; ST = d d + fmul dword [esi + (14*4)] ; ST = d*data[sample+14] d + fadd dword [edi + (14*4)] ; ST = autoc[14]+d*data[sample+14] d + fstp dword [edi + (14*4)] ; autoc[14]+=d*data[sample+14] ST = d + fld st0 ; ST = d d + fmul dword [esi + (13*4)] ; ST = d*data[sample+13] d + fadd dword [edi + (13*4)] ; ST = autoc[13]+d*data[sample+13] d + fstp dword [edi + (13*4)] ; autoc[13]+=d*data[sample+13] ST = d + fld st0 ; ST = d d + fmul dword [esi + (12*4)] ; ST = d*data[sample+12] d + fadd dword [edi + (12*4)] ; ST = autoc[12]+d*data[sample+12] d + fstp dword [edi + (12*4)] ; autoc[12]+=d*data[sample+12] ST = d + fld st0 ; ST = d d + fmul dword [esi + (11*4)] ; ST = d*data[sample+11] d + fadd dword [edi + (11*4)] ; ST = autoc[11]+d*data[sample+11] d + fstp dword [edi + (11*4)] ; autoc[11]+=d*data[sample+11] ST = d + fld st0 ; ST = d d + fmul dword [esi + (10*4)] ; ST = d*data[sample+10] d + fadd dword [edi + (10*4)] ; ST = autoc[10]+d*data[sample+10] d + fstp dword [edi + (10*4)] ; autoc[10]+=d*data[sample+10] ST = d + fld st0 ; ST = d d + fmul dword [esi + ( 9*4)] ; ST = d*data[sample+9] d + fadd dword [edi + ( 9*4)] ; ST = autoc[9]+d*data[sample+9] d + fstp dword [edi + ( 9*4)] ; autoc[9]+=d*data[sample+9] ST = d + fld st0 ; ST = d d + fmul dword [esi + ( 8*4)] ; ST = d*data[sample+8] d + fadd dword [edi + ( 8*4)] ; ST = autoc[8]+d*data[sample+8] d + fstp dword [edi + ( 8*4)] ; autoc[8]+=d*data[sample+8] ST = d + fld st0 ; ST = d d + fmul dword [esi + ( 7*4)] ; ST = d*data[sample+7] d + fadd dword [edi + ( 7*4)] ; ST = autoc[7]+d*data[sample+7] d + fstp dword [edi + ( 7*4)] ; autoc[7]+=d*data[sample+7] ST = d + fld st0 ; ST = d d + fmul dword [esi + ( 6*4)] ; ST = d*data[sample+6] d + fadd dword [edi + ( 6*4)] ; ST = autoc[6]+d*data[sample+6] d + fstp dword [edi + ( 6*4)] ; autoc[6]+=d*data[sample+6] ST = d + fld st0 ; ST = d d + fmul dword [esi + ( 5*4)] ; ST = d*data[sample+4] d + fadd dword [edi + ( 5*4)] ; ST = autoc[4]+d*data[sample+4] d + fstp dword [edi + ( 5*4)] ; autoc[4]+=d*data[sample+4] ST = d + fld st0 ; ST = d d + fmul dword [esi + ( 4*4)] ; ST = d*data[sample+4] d + fadd dword [edi + ( 4*4)] ; ST = autoc[4]+d*data[sample+4] d + fstp dword [edi + ( 4*4)] ; autoc[4]+=d*data[sample+4] ST = d + fld st0 ; ST = d d + fmul dword [esi + ( 3*4)] ; ST = d*data[sample+3] d + fadd dword [edi + ( 3*4)] ; ST = autoc[3]+d*data[sample+3] d + fstp dword [edi + ( 3*4)] ; autoc[3]+=d*data[sample+3] ST = d + fld st0 ; ST = d d + fmul dword [esi + ( 2*4)] ; ST = d*data[sample+2] d + fadd dword [edi + ( 2*4)] ; ST = autoc[2]+d*data[sample+2] d + fstp dword [edi + ( 2*4)] ; autoc[2]+=d*data[sample+2] ST = d + fld st0 ; ST = d d + fmul dword [esi + ( 1*4)] ; ST = d*data[sample+1] d + fadd dword [edi + ( 1*4)] ; ST = autoc[1]+d*data[sample+1] d + fstp dword [edi + ( 1*4)] ; autoc[1]+=d*data[sample+1] ST = d + fld st0 ; ST = d d + fmul dword [esi] ; ST = d*data[sample] d WATCHOUT: no displacement byte here! + fadd dword [edi] ; ST = autoc[0]+d*data[sample] d WATCHOUT: no displacement byte here! + fstp dword [edi] ; autoc[0]+=d*data[sample] ST = d WATCHOUT: no displacement byte here! +.jumper2_0: + + fstp st0 ; pop d, ST = empty + add esi, byte 4 ; sample++ + dec ecx + jz .loop2_end + add edx, byte 11 ; adjust our inner loop counter by adjusting the jump target + fld dword [esi] ; ST = d <- data[sample] + jmp edx +.loop2_end: + +.end: + pop ebx + pop edi + pop esi + ret + + ALIGN 16 +cident FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_4 + ;[esp + 16] == autoc[] + ;[esp + 12] == lag + ;[esp + 8] == data_len + ;[esp + 4] == data[] + + ;ASSERT(lag > 0) + ;ASSERT(lag <= 4) + ;ASSERT(lag <= data_len) + + ; for(coeff = 0; coeff < lag; coeff++) + ; autoc[coeff] = 0.0; + xorps xmm5, xmm5 + + mov edx, [esp + 8] ; edx == data_len + mov eax, [esp + 4] ; eax == &data[sample] <- &data[0] + + movss xmm0, [eax] ; xmm0 = 0,0,0,data[0] + add eax, 4 + movaps xmm2, xmm0 ; xmm2 = 0,0,0,data[0] + shufps xmm0, xmm0, 0 ; xmm0 == data[sample],data[sample],data[sample],data[sample] = data[0],data[0],data[0],data[0] +.warmup: ; xmm2 == data[sample-3],data[sample-2],data[sample-1],data[sample] + mulps xmm0, xmm2 ; xmm0 = xmm0 * xmm2 + addps xmm5, xmm0 ; xmm5 += xmm0 * xmm2 + dec edx + jz .loop_end + ALIGN 16 +.loop_start: + ; start by reading the next sample + movss xmm0, [eax] ; xmm0 = 0,0,0,data[sample] + add eax, 4 + shufps xmm0, xmm0, 0 ; xmm0 = data[sample],data[sample],data[sample],data[sample] + shufps xmm2, xmm2, 93h ; 93h=2-1-0-3 => xmm2 gets rotated left by one float + movss xmm2, xmm0 + mulps xmm0, xmm2 ; xmm0 = xmm0 * xmm2 + addps xmm5, xmm0 ; xmm5 += xmm0 * xmm2 + dec edx + jnz .loop_start +.loop_end: + ; store autoc + mov edx, [esp + 16] ; edx == autoc + movups [edx], xmm5 + +.end: + ret + + ALIGN 16 +cident FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_8 + ;[esp + 16] == autoc[] + ;[esp + 12] == lag + ;[esp + 8] == data_len + ;[esp + 4] == data[] + + ;ASSERT(lag > 0) + ;ASSERT(lag <= 8) + ;ASSERT(lag <= data_len) + + ; for(coeff = 0; coeff < lag; coeff++) + ; autoc[coeff] = 0.0; + xorps xmm5, xmm5 + xorps xmm6, xmm6 + + mov edx, [esp + 8] ; edx == data_len + mov eax, [esp + 4] ; eax == &data[sample] <- &data[0] + + movss xmm0, [eax] ; xmm0 = 0,0,0,data[0] + add eax, 4 + movaps xmm2, xmm0 ; xmm2 = 0,0,0,data[0] + shufps xmm0, xmm0, 0 ; xmm0 == data[sample],data[sample],data[sample],data[sample] = data[0],data[0],data[0],data[0] + movaps xmm1, xmm0 ; xmm1 == data[sample],data[sample],data[sample],data[sample] = data[0],data[0],data[0],data[0] + xorps xmm3, xmm3 ; xmm3 = 0,0,0,0 +.warmup: ; xmm3:xmm2 == data[sample-7],data[sample-6],...,data[sample] + mulps xmm0, xmm2 + mulps xmm1, xmm3 ; xmm1:xmm0 = xmm1:xmm0 * xmm3:xmm2 + addps xmm5, xmm0 + addps xmm6, xmm1 ; xmm6:xmm5 += xmm1:xmm0 * xmm3:xmm2 + dec edx + jz .loop_end + ALIGN 16 +.loop_start: + ; start by reading the next sample + movss xmm0, [eax] ; xmm0 = 0,0,0,data[sample] + ; here we reorder the instructions; see the (#) indexes for a logical order + shufps xmm2, xmm2, 93h ; (3) 93h=2-1-0-3 => xmm2 gets rotated left by one float + add eax, 4 ; (0) + shufps xmm3, xmm3, 93h ; (4) 93h=2-1-0-3 => xmm3 gets rotated left by one float + shufps xmm0, xmm0, 0 ; (1) xmm0 = data[sample],data[sample],data[sample],data[sample] + movss xmm3, xmm2 ; (5) + movaps xmm1, xmm0 ; (2) xmm1 = data[sample],data[sample],data[sample],data[sample] + movss xmm2, xmm0 ; (6) + mulps xmm1, xmm3 ; (8) + mulps xmm0, xmm2 ; (7) xmm1:xmm0 = xmm1:xmm0 * xmm3:xmm2 + addps xmm6, xmm1 ; (10) + addps xmm5, xmm0 ; (9) xmm6:xmm5 += xmm1:xmm0 * xmm3:xmm2 + dec edx + jnz .loop_start +.loop_end: + ; store autoc + mov edx, [esp + 16] ; edx == autoc + movups [edx], xmm5 + movups [edx + 16], xmm6 + +.end: + ret + + ALIGN 16 +cident FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_12 + ;[esp + 16] == autoc[] + ;[esp + 12] == lag + ;[esp + 8] == data_len + ;[esp + 4] == data[] + + ;ASSERT(lag > 0) + ;ASSERT(lag <= 12) + ;ASSERT(lag <= data_len) + + ; for(coeff = 0; coeff < lag; coeff++) + ; autoc[coeff] = 0.0; + xorps xmm5, xmm5 + xorps xmm6, xmm6 + xorps xmm7, xmm7 + + mov edx, [esp + 8] ; edx == data_len + mov eax, [esp + 4] ; eax == &data[sample] <- &data[0] + + movss xmm0, [eax] ; xmm0 = 0,0,0,data[0] + add eax, 4 + movaps xmm2, xmm0 ; xmm2 = 0,0,0,data[0] + shufps xmm0, xmm0, 0 ; xmm0 == data[sample],data[sample],data[sample],data[sample] = data[0],data[0],data[0],data[0] + xorps xmm3, xmm3 ; xmm3 = 0,0,0,0 + xorps xmm4, xmm4 ; xmm4 = 0,0,0,0 +.warmup: ; xmm3:xmm2 == data[sample-7],data[sample-6],...,data[sample] + movaps xmm1, xmm0 + mulps xmm1, xmm2 + addps xmm5, xmm1 + movaps xmm1, xmm0 + mulps xmm1, xmm3 + addps xmm6, xmm1 + mulps xmm0, xmm4 + addps xmm7, xmm0 ; xmm7:xmm6:xmm5 += xmm0:xmm0:xmm0 * xmm4:xmm3:xmm2 + dec edx + jz .loop_end + ALIGN 16 +.loop_start: + ; start by reading the next sample + movss xmm0, [eax] ; xmm0 = 0,0,0,data[sample] + add eax, 4 + shufps xmm0, xmm0, 0 ; xmm0 = data[sample],data[sample],data[sample],data[sample] + + ; shift xmm4:xmm3:xmm2 left by one float + shufps xmm2, xmm2, 93h ; 93h=2-1-0-3 => xmm2 gets rotated left by one float + shufps xmm3, xmm3, 93h ; 93h=2-1-0-3 => xmm3 gets rotated left by one float + shufps xmm4, xmm4, 93h ; 93h=2-1-0-3 => xmm4 gets rotated left by one float + movss xmm4, xmm3 + movss xmm3, xmm2 + movss xmm2, xmm0 + + ; xmm7:xmm6:xmm5 += xmm0:xmm0:xmm0 * xmm3:xmm3:xmm2 + movaps xmm1, xmm0 + mulps xmm1, xmm2 + addps xmm5, xmm1 + movaps xmm1, xmm0 + mulps xmm1, xmm3 + addps xmm6, xmm1 + mulps xmm0, xmm4 + addps xmm7, xmm0 + + dec edx + jnz .loop_start +.loop_end: + ; store autoc + mov edx, [esp + 16] ; edx == autoc + movups [edx], xmm5 + movups [edx + 16], xmm6 + movups [edx + 32], xmm7 + +.end: + ret + + ALIGN 16 +cident FLAC__lpc_compute_autocorrelation_asm_ia32_3dnow + ;[ebp + 32] autoc + ;[ebp + 28] lag + ;[ebp + 24] data_len + ;[ebp + 20] data + + push ebp + push ebx + push esi + push edi + mov ebp, esp + + mov esi, [ebp + 20] + mov edi, [ebp + 24] + mov edx, [ebp + 28] + inc edx + and edx, byte -2 + mov eax, edx + neg eax + and esp, byte -8 + lea esp, [esp + 4 * eax] + mov ecx, edx + xor eax, eax +.loop0: + dec ecx + mov [esp + 4 * ecx], eax + jnz short .loop0 + + mov eax, edi + sub eax, edx + mov ebx, edx + and ebx, byte 1 + sub eax, ebx + lea ecx, [esi + 4 * eax - 12] + cmp esi, ecx + mov eax, esi + ja short .loop2_pre + ALIGN 16 ;4 nops +.loop1_i: + movd mm0, [eax] + movd mm2, [eax + 4] + movd mm4, [eax + 8] + movd mm6, [eax + 12] + mov ebx, edx + punpckldq mm0, mm0 + punpckldq mm2, mm2 + punpckldq mm4, mm4 + punpckldq mm6, mm6 + ALIGN 16 ;3 nops +.loop1_j: + sub ebx, byte 2 + movd mm1, [eax + 4 * ebx] + movd mm3, [eax + 4 * ebx + 4] + movd mm5, [eax + 4 * ebx + 8] + movd mm7, [eax + 4 * ebx + 12] + punpckldq mm1, mm3 + punpckldq mm3, mm5 + pfmul mm1, mm0 + punpckldq mm5, mm7 + pfmul mm3, mm2 + punpckldq mm7, [eax + 4 * ebx + 16] + pfmul mm5, mm4 + pfmul mm7, mm6 + pfadd mm1, mm3 + movq mm3, [esp + 4 * ebx] + pfadd mm5, mm7 + pfadd mm1, mm5 + pfadd mm3, mm1 + movq [esp + 4 * ebx], mm3 + jg short .loop1_j + + add eax, byte 16 + cmp eax, ecx + jb short .loop1_i + +.loop2_pre: + mov ebx, eax + sub eax, esi + shr eax, 2 + lea ecx, [esi + 4 * edi] + mov esi, ebx +.loop2_i: + movd mm0, [esi] + mov ebx, edi + sub ebx, eax + cmp ebx, edx + jbe short .loop2_j + mov ebx, edx +.loop2_j: + dec ebx + movd mm1, [esi + 4 * ebx] + pfmul mm1, mm0 + movd mm2, [esp + 4 * ebx] + pfadd mm1, mm2 + movd [esp + 4 * ebx], mm1 + + jnz short .loop2_j + + add esi, byte 4 + inc eax + cmp esi, ecx + jnz short .loop2_i + + mov edi, [ebp + 32] + mov edx, [ebp + 28] +.loop3: + dec edx + mov eax, [esp + 4 * edx] + mov [edi + 4 * edx], eax + jnz short .loop3 + + femms + + mov esp, ebp + pop edi + pop esi + pop ebx + pop ebp + ret + +;void FLAC__lpc_compute_residual_from_qlp_coefficients(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]) +; +; for(i = 0; i < data_len; i++) { +; sum = 0; +; for(j = 0; j < order; j++) +; sum += qlp_coeff[j] * data[i-j-1]; +; residual[i] = data[i] - (sum >> lp_quantization); +; } +; + ALIGN 16 +cident FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32 + ;[esp + 40] residual[] + ;[esp + 36] lp_quantization + ;[esp + 32] order + ;[esp + 28] qlp_coeff[] + ;[esp + 24] data_len + ;[esp + 20] data[] + + ;ASSERT(order > 0) + + push ebp + push ebx + push esi + push edi + + mov esi, [esp + 20] ; esi = data[] + mov edi, [esp + 40] ; edi = residual[] + mov eax, [esp + 32] ; eax = order + mov ebx, [esp + 24] ; ebx = data_len + + test ebx, ebx + jz near .end ; do nothing if data_len == 0 +.begin: + cmp eax, byte 1 + jg short .i_1more + + mov ecx, [esp + 28] + mov edx, [ecx] ; edx = qlp_coeff[0] + mov eax, [esi - 4] ; eax = data[-1] + mov cl, [esp + 36] ; cl = lp_quantization + ALIGN 16 +.i_1_loop_i: + imul eax, edx + sar eax, cl + neg eax + add eax, [esi] + mov [edi], eax + mov eax, [esi] + add edi, byte 4 + add esi, byte 4 + dec ebx + jnz .i_1_loop_i + + jmp .end + +.i_1more: + cmp eax, byte 32 ; for order <= 32 there is a faster routine + jbe short .i_32 + + ; This version is here just for completeness, since FLAC__MAX_LPC_ORDER == 32 + ALIGN 16 +.i_32more_loop_i: + xor ebp, ebp + mov ecx, [esp + 32] + mov edx, ecx + shl edx, 2 + add edx, [esp + 28] + neg ecx + ALIGN 16 +.i_32more_loop_j: + sub edx, byte 4 + mov eax, [edx] + imul eax, [esi + 4 * ecx] + add ebp, eax + inc ecx + jnz short .i_32more_loop_j + + mov cl, [esp + 36] + sar ebp, cl + neg ebp + add ebp, [esi] + mov [edi], ebp + add esi, byte 4 + add edi, byte 4 + + dec ebx + jnz .i_32more_loop_i + + jmp .end + +.i_32: + sub edi, esi + neg eax + lea edx, [eax + eax * 8 + .jumper_0 - .get_eip0] + call .get_eip0 +.get_eip0: + pop eax + add edx, eax + inc edx + mov eax, [esp + 28] ; eax = qlp_coeff[] + xor ebp, ebp + jmp edx + + mov ecx, [eax + 124] + imul ecx, [esi - 128] + add ebp, ecx + mov ecx, [eax + 120] + imul ecx, [esi - 124] + add ebp, ecx + mov ecx, [eax + 116] + imul ecx, [esi - 120] + add ebp, ecx + mov ecx, [eax + 112] + imul ecx, [esi - 116] + add ebp, ecx + mov ecx, [eax + 108] + imul ecx, [esi - 112] + add ebp, ecx + mov ecx, [eax + 104] + imul ecx, [esi - 108] + add ebp, ecx + mov ecx, [eax + 100] + imul ecx, [esi - 104] + add ebp, ecx + mov ecx, [eax + 96] + imul ecx, [esi - 100] + add ebp, ecx + mov ecx, [eax + 92] + imul ecx, [esi - 96] + add ebp, ecx + mov ecx, [eax + 88] + imul ecx, [esi - 92] + add ebp, ecx + mov ecx, [eax + 84] + imul ecx, [esi - 88] + add ebp, ecx + mov ecx, [eax + 80] + imul ecx, [esi - 84] + add ebp, ecx + mov ecx, [eax + 76] + imul ecx, [esi - 80] + add ebp, ecx + mov ecx, [eax + 72] + imul ecx, [esi - 76] + add ebp, ecx + mov ecx, [eax + 68] + imul ecx, [esi - 72] + add ebp, ecx + mov ecx, [eax + 64] + imul ecx, [esi - 68] + add ebp, ecx + mov ecx, [eax + 60] + imul ecx, [esi - 64] + add ebp, ecx + mov ecx, [eax + 56] + imul ecx, [esi - 60] + add ebp, ecx + mov ecx, [eax + 52] + imul ecx, [esi - 56] + add ebp, ecx + mov ecx, [eax + 48] + imul ecx, [esi - 52] + add ebp, ecx + mov ecx, [eax + 44] + imul ecx, [esi - 48] + add ebp, ecx + mov ecx, [eax + 40] + imul ecx, [esi - 44] + add ebp, ecx + mov ecx, [eax + 36] + imul ecx, [esi - 40] + add ebp, ecx + mov ecx, [eax + 32] + imul ecx, [esi - 36] + add ebp, ecx + mov ecx, [eax + 28] + imul ecx, [esi - 32] + add ebp, ecx + mov ecx, [eax + 24] + imul ecx, [esi - 28] + add ebp, ecx + mov ecx, [eax + 20] + imul ecx, [esi - 24] + add ebp, ecx + mov ecx, [eax + 16] + imul ecx, [esi - 20] + add ebp, ecx + mov ecx, [eax + 12] + imul ecx, [esi - 16] + add ebp, ecx + mov ecx, [eax + 8] + imul ecx, [esi - 12] + add ebp, ecx + mov ecx, [eax + 4] + imul ecx, [esi - 8] + add ebp, ecx + mov ecx, [eax] ; there is one byte missing + imul ecx, [esi - 4] + add ebp, ecx +.jumper_0: + + mov cl, [esp + 36] + sar ebp, cl + neg ebp + add ebp, [esi] + mov [edi + esi], ebp + add esi, byte 4 + + dec ebx + jz short .end + xor ebp, ebp + jmp edx + +.end: + pop edi + pop esi + pop ebx + pop ebp + ret + +; WATCHOUT: this routine works on 16 bit data which means bits-per-sample for +; the channel and qlp_coeffs must be <= 16. Especially note that this routine +; cannot be used for side-channel coded 16bps channels since the effective bps +; is 17. + ALIGN 16 +cident FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32_mmx + ;[esp + 40] residual[] + ;[esp + 36] lp_quantization + ;[esp + 32] order + ;[esp + 28] qlp_coeff[] + ;[esp + 24] data_len + ;[esp + 20] data[] + + ;ASSERT(order > 0) + + push ebp + push ebx + push esi + push edi + + mov esi, [esp + 20] ; esi = data[] + mov edi, [esp + 40] ; edi = residual[] + mov eax, [esp + 32] ; eax = order + mov ebx, [esp + 24] ; ebx = data_len + + test ebx, ebx + jz near .end ; do nothing if data_len == 0 + dec ebx + test ebx, ebx + jz near .last_one + + mov edx, [esp + 28] ; edx = qlp_coeff[] + movd mm6, [esp + 36] ; mm6 = 0:lp_quantization + mov ebp, esp + + and esp, 0xfffffff8 + + xor ecx, ecx +.copy_qlp_loop: + push word [edx + 4 * ecx] + inc ecx + cmp ecx, eax + jnz short .copy_qlp_loop + + and ecx, 0x3 + test ecx, ecx + je short .za_end + sub ecx, byte 4 +.za_loop: + push word 0 + inc eax + inc ecx + jnz short .za_loop +.za_end: + + movq mm5, [esp + 2 * eax - 8] + movd mm4, [esi - 16] + punpckldq mm4, [esi - 12] + movd mm0, [esi - 8] + punpckldq mm0, [esi - 4] + packssdw mm4, mm0 + + cmp eax, byte 4 + jnbe short .mmx_4more + + ALIGN 16 +.mmx_4_loop_i: + movd mm1, [esi] + movq mm3, mm4 + punpckldq mm1, [esi + 4] + psrlq mm4, 16 + movq mm0, mm1 + psllq mm0, 48 + por mm4, mm0 + movq mm2, mm4 + psrlq mm4, 16 + pxor mm0, mm0 + punpckhdq mm0, mm1 + pmaddwd mm3, mm5 + pmaddwd mm2, mm5 + psllq mm0, 16 + por mm4, mm0 + movq mm0, mm3 + punpckldq mm3, mm2 + punpckhdq mm0, mm2 + paddd mm3, mm0 + psrad mm3, mm6 + psubd mm1, mm3 + movd [edi], mm1 + punpckhdq mm1, mm1 + movd [edi + 4], mm1 + + add edi, byte 8 + add esi, byte 8 + + sub ebx, 2 + jg .mmx_4_loop_i + jmp .mmx_end + +.mmx_4more: + shl eax, 2 + neg eax + add eax, byte 16 + + ALIGN 16 +.mmx_4more_loop_i: + movd mm1, [esi] + punpckldq mm1, [esi + 4] + movq mm3, mm4 + psrlq mm4, 16 + movq mm0, mm1 + psllq mm0, 48 + por mm4, mm0 + movq mm2, mm4 + psrlq mm4, 16 + pxor mm0, mm0 + punpckhdq mm0, mm1 + pmaddwd mm3, mm5 + pmaddwd mm2, mm5 + psllq mm0, 16 + por mm4, mm0 + + mov ecx, esi + add ecx, eax + mov edx, esp + + ALIGN 16 +.mmx_4more_loop_j: + movd mm0, [ecx - 16] + movd mm7, [ecx - 8] + punpckldq mm0, [ecx - 12] + punpckldq mm7, [ecx - 4] + packssdw mm0, mm7 + pmaddwd mm0, [edx] + punpckhdq mm7, mm7 + paddd mm3, mm0 + movd mm0, [ecx - 12] + punpckldq mm0, [ecx - 8] + punpckldq mm7, [ecx] + packssdw mm0, mm7 + pmaddwd mm0, [edx] + paddd mm2, mm0 + + add edx, byte 8 + add ecx, byte 16 + cmp ecx, esi + jnz .mmx_4more_loop_j + + movq mm0, mm3 + punpckldq mm3, mm2 + punpckhdq mm0, mm2 + paddd mm3, mm0 + psrad mm3, mm6 + psubd mm1, mm3 + movd [edi], mm1 + punpckhdq mm1, mm1 + movd [edi + 4], mm1 + + add edi, byte 8 + add esi, byte 8 + + sub ebx, 2 + jg near .mmx_4more_loop_i + +.mmx_end: + emms + mov esp, ebp +.last_one: + mov eax, [esp + 32] + inc ebx + jnz near FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32.begin + +.end: + pop edi + pop esi + pop ebx + pop ebp + ret + +; ********************************************************************** +; +; void FLAC__lpc_restore_signal(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]) +; { +; unsigned i, j; +; FLAC__int32 sum; +; +; FLAC__ASSERT(order > 0); +; +; for(i = 0; i < data_len; i++) { +; sum = 0; +; for(j = 0; j < order; j++) +; sum += qlp_coeff[j] * data[i-j-1]; +; data[i] = residual[i] + (sum >> lp_quantization); +; } +; } + ALIGN 16 +cident FLAC__lpc_restore_signal_asm_ia32 + ;[esp + 40] data[] + ;[esp + 36] lp_quantization + ;[esp + 32] order + ;[esp + 28] qlp_coeff[] + ;[esp + 24] data_len + ;[esp + 20] residual[] + + ;ASSERT(order > 0) + + push ebp + push ebx + push esi + push edi + + mov esi, [esp + 20] ; esi = residual[] + mov edi, [esp + 40] ; edi = data[] + mov eax, [esp + 32] ; eax = order + mov ebx, [esp + 24] ; ebx = data_len + + test ebx, ebx + jz near .end ; do nothing if data_len == 0 + +.begin: + cmp eax, byte 1 + jg short .x87_1more + + mov ecx, [esp + 28] + mov edx, [ecx] + mov eax, [edi - 4] + mov cl, [esp + 36] + ALIGN 16 +.x87_1_loop_i: + imul eax, edx + sar eax, cl + add eax, [esi] + mov [edi], eax + add esi, byte 4 + add edi, byte 4 + dec ebx + jnz .x87_1_loop_i + + jmp .end + +.x87_1more: + cmp eax, byte 32 ; for order <= 32 there is a faster routine + jbe short .x87_32 + + ; This version is here just for completeness, since FLAC__MAX_LPC_ORDER == 32 + ALIGN 16 +.x87_32more_loop_i: + xor ebp, ebp + mov ecx, [esp + 32] + mov edx, ecx + shl edx, 2 + add edx, [esp + 28] + neg ecx + ALIGN 16 +.x87_32more_loop_j: + sub edx, byte 4 + mov eax, [edx] + imul eax, [edi + 4 * ecx] + add ebp, eax + inc ecx + jnz short .x87_32more_loop_j + + mov cl, [esp + 36] + sar ebp, cl + add ebp, [esi] + mov [edi], ebp + add edi, byte 4 + add esi, byte 4 + + dec ebx + jnz .x87_32more_loop_i + + jmp .end + +.x87_32: + sub esi, edi + neg eax + lea edx, [eax + eax * 8 + .jumper_0 - .get_eip0] + call .get_eip0 +.get_eip0: + pop eax + add edx, eax + inc edx ; compensate for the shorter opcode on the last iteration + mov eax, [esp + 28] ; eax = qlp_coeff[] + xor ebp, ebp + jmp edx + + mov ecx, [eax + 124] ; ecx = qlp_coeff[31] + imul ecx, [edi - 128] ; ecx = qlp_coeff[31] * data[i-32] + add ebp, ecx ; sum += qlp_coeff[31] * data[i-32] + mov ecx, [eax + 120] ; ecx = qlp_coeff[30] + imul ecx, [edi - 124] ; ecx = qlp_coeff[30] * data[i-31] + add ebp, ecx ; sum += qlp_coeff[30] * data[i-31] + mov ecx, [eax + 116] ; ecx = qlp_coeff[29] + imul ecx, [edi - 120] ; ecx = qlp_coeff[29] * data[i-30] + add ebp, ecx ; sum += qlp_coeff[29] * data[i-30] + mov ecx, [eax + 112] ; ecx = qlp_coeff[28] + imul ecx, [edi - 116] ; ecx = qlp_coeff[28] * data[i-29] + add ebp, ecx ; sum += qlp_coeff[28] * data[i-29] + mov ecx, [eax + 108] ; ecx = qlp_coeff[27] + imul ecx, [edi - 112] ; ecx = qlp_coeff[27] * data[i-28] + add ebp, ecx ; sum += qlp_coeff[27] * data[i-28] + mov ecx, [eax + 104] ; ecx = qlp_coeff[26] + imul ecx, [edi - 108] ; ecx = qlp_coeff[26] * data[i-27] + add ebp, ecx ; sum += qlp_coeff[26] * data[i-27] + mov ecx, [eax + 100] ; ecx = qlp_coeff[25] + imul ecx, [edi - 104] ; ecx = qlp_coeff[25] * data[i-26] + add ebp, ecx ; sum += qlp_coeff[25] * data[i-26] + mov ecx, [eax + 96] ; ecx = qlp_coeff[24] + imul ecx, [edi - 100] ; ecx = qlp_coeff[24] * data[i-25] + add ebp, ecx ; sum += qlp_coeff[24] * data[i-25] + mov ecx, [eax + 92] ; ecx = qlp_coeff[23] + imul ecx, [edi - 96] ; ecx = qlp_coeff[23] * data[i-24] + add ebp, ecx ; sum += qlp_coeff[23] * data[i-24] + mov ecx, [eax + 88] ; ecx = qlp_coeff[22] + imul ecx, [edi - 92] ; ecx = qlp_coeff[22] * data[i-23] + add ebp, ecx ; sum += qlp_coeff[22] * data[i-23] + mov ecx, [eax + 84] ; ecx = qlp_coeff[21] + imul ecx, [edi - 88] ; ecx = qlp_coeff[21] * data[i-22] + add ebp, ecx ; sum += qlp_coeff[21] * data[i-22] + mov ecx, [eax + 80] ; ecx = qlp_coeff[20] + imul ecx, [edi - 84] ; ecx = qlp_coeff[20] * data[i-21] + add ebp, ecx ; sum += qlp_coeff[20] * data[i-21] + mov ecx, [eax + 76] ; ecx = qlp_coeff[19] + imul ecx, [edi - 80] ; ecx = qlp_coeff[19] * data[i-20] + add ebp, ecx ; sum += qlp_coeff[19] * data[i-20] + mov ecx, [eax + 72] ; ecx = qlp_coeff[18] + imul ecx, [edi - 76] ; ecx = qlp_coeff[18] * data[i-19] + add ebp, ecx ; sum += qlp_coeff[18] * data[i-19] + mov ecx, [eax + 68] ; ecx = qlp_coeff[17] + imul ecx, [edi - 72] ; ecx = qlp_coeff[17] * data[i-18] + add ebp, ecx ; sum += qlp_coeff[17] * data[i-18] + mov ecx, [eax + 64] ; ecx = qlp_coeff[16] + imul ecx, [edi - 68] ; ecx = qlp_coeff[16] * data[i-17] + add ebp, ecx ; sum += qlp_coeff[16] * data[i-17] + mov ecx, [eax + 60] ; ecx = qlp_coeff[15] + imul ecx, [edi - 64] ; ecx = qlp_coeff[15] * data[i-16] + add ebp, ecx ; sum += qlp_coeff[15] * data[i-16] + mov ecx, [eax + 56] ; ecx = qlp_coeff[14] + imul ecx, [edi - 60] ; ecx = qlp_coeff[14] * data[i-15] + add ebp, ecx ; sum += qlp_coeff[14] * data[i-15] + mov ecx, [eax + 52] ; ecx = qlp_coeff[13] + imul ecx, [edi - 56] ; ecx = qlp_coeff[13] * data[i-14] + add ebp, ecx ; sum += qlp_coeff[13] * data[i-14] + mov ecx, [eax + 48] ; ecx = qlp_coeff[12] + imul ecx, [edi - 52] ; ecx = qlp_coeff[12] * data[i-13] + add ebp, ecx ; sum += qlp_coeff[12] * data[i-13] + mov ecx, [eax + 44] ; ecx = qlp_coeff[11] + imul ecx, [edi - 48] ; ecx = qlp_coeff[11] * data[i-12] + add ebp, ecx ; sum += qlp_coeff[11] * data[i-12] + mov ecx, [eax + 40] ; ecx = qlp_coeff[10] + imul ecx, [edi - 44] ; ecx = qlp_coeff[10] * data[i-11] + add ebp, ecx ; sum += qlp_coeff[10] * data[i-11] + mov ecx, [eax + 36] ; ecx = qlp_coeff[ 9] + imul ecx, [edi - 40] ; ecx = qlp_coeff[ 9] * data[i-10] + add ebp, ecx ; sum += qlp_coeff[ 9] * data[i-10] + mov ecx, [eax + 32] ; ecx = qlp_coeff[ 8] + imul ecx, [edi - 36] ; ecx = qlp_coeff[ 8] * data[i- 9] + add ebp, ecx ; sum += qlp_coeff[ 8] * data[i- 9] + mov ecx, [eax + 28] ; ecx = qlp_coeff[ 7] + imul ecx, [edi - 32] ; ecx = qlp_coeff[ 7] * data[i- 8] + add ebp, ecx ; sum += qlp_coeff[ 7] * data[i- 8] + mov ecx, [eax + 24] ; ecx = qlp_coeff[ 6] + imul ecx, [edi - 28] ; ecx = qlp_coeff[ 6] * data[i- 7] + add ebp, ecx ; sum += qlp_coeff[ 6] * data[i- 7] + mov ecx, [eax + 20] ; ecx = qlp_coeff[ 5] + imul ecx, [edi - 24] ; ecx = qlp_coeff[ 5] * data[i- 6] + add ebp, ecx ; sum += qlp_coeff[ 5] * data[i- 6] + mov ecx, [eax + 16] ; ecx = qlp_coeff[ 4] + imul ecx, [edi - 20] ; ecx = qlp_coeff[ 4] * data[i- 5] + add ebp, ecx ; sum += qlp_coeff[ 4] * data[i- 5] + mov ecx, [eax + 12] ; ecx = qlp_coeff[ 3] + imul ecx, [edi - 16] ; ecx = qlp_coeff[ 3] * data[i- 4] + add ebp, ecx ; sum += qlp_coeff[ 3] * data[i- 4] + mov ecx, [eax + 8] ; ecx = qlp_coeff[ 2] + imul ecx, [edi - 12] ; ecx = qlp_coeff[ 2] * data[i- 3] + add ebp, ecx ; sum += qlp_coeff[ 2] * data[i- 3] + mov ecx, [eax + 4] ; ecx = qlp_coeff[ 1] + imul ecx, [edi - 8] ; ecx = qlp_coeff[ 1] * data[i- 2] + add ebp, ecx ; sum += qlp_coeff[ 1] * data[i- 2] + mov ecx, [eax] ; ecx = qlp_coeff[ 0] (NOTE: one byte missing from instruction) + imul ecx, [edi - 4] ; ecx = qlp_coeff[ 0] * data[i- 1] + add ebp, ecx ; sum += qlp_coeff[ 0] * data[i- 1] +.jumper_0: + + mov cl, [esp + 36] + sar ebp, cl ; ebp = (sum >> lp_quantization) + add ebp, [esi + edi] ; ebp = residual[i] + (sum >> lp_quantization) + mov [edi], ebp ; data[i] = residual[i] + (sum >> lp_quantization) + add edi, byte 4 + + dec ebx + jz short .end + xor ebp, ebp + jmp edx + +.end: + pop edi + pop esi + pop ebx + pop ebp + ret + +; WATCHOUT: this routine works on 16 bit data which means bits-per-sample for +; the channel and qlp_coeffs must be <= 16. Especially note that this routine +; cannot be used for side-channel coded 16bps channels since the effective bps +; is 17. +; WATCHOUT: this routine requires that each data array have a buffer of up to +; 3 zeroes in front (at negative indices) for alignment purposes, i.e. for each +; channel n, data[n][-1] through data[n][-3] should be accessible and zero. + ALIGN 16 +cident FLAC__lpc_restore_signal_asm_ia32_mmx + ;[esp + 40] data[] + ;[esp + 36] lp_quantization + ;[esp + 32] order + ;[esp + 28] qlp_coeff[] + ;[esp + 24] data_len + ;[esp + 20] residual[] + + ;ASSERT(order > 0) + + push ebp + push ebx + push esi + push edi + + mov esi, [esp + 20] + mov edi, [esp + 40] + mov eax, [esp + 32] + mov ebx, [esp + 24] + + test ebx, ebx + jz near .end ; do nothing if data_len == 0 + cmp eax, byte 4 + jb near FLAC__lpc_restore_signal_asm_ia32.begin + + mov edx, [esp + 28] + movd mm6, [esp + 36] + mov ebp, esp + + and esp, 0xfffffff8 + + xor ecx, ecx +.copy_qlp_loop: + push word [edx + 4 * ecx] + inc ecx + cmp ecx, eax + jnz short .copy_qlp_loop + + and ecx, 0x3 + test ecx, ecx + je short .za_end + sub ecx, byte 4 +.za_loop: + push word 0 + inc eax + inc ecx + jnz short .za_loop +.za_end: + + movq mm5, [esp + 2 * eax - 8] + movd mm4, [edi - 16] + punpckldq mm4, [edi - 12] + movd mm0, [edi - 8] + punpckldq mm0, [edi - 4] + packssdw mm4, mm0 + + cmp eax, byte 4 + jnbe short .mmx_4more + + ALIGN 16 +.mmx_4_loop_i: + movq mm7, mm4 + pmaddwd mm7, mm5 + movq mm0, mm7 + punpckhdq mm7, mm7 + paddd mm7, mm0 + psrad mm7, mm6 + movd mm1, [esi] + paddd mm7, mm1 + movd [edi], mm7 + psllq mm7, 48 + psrlq mm4, 16 + por mm4, mm7 + + add esi, byte 4 + add edi, byte 4 + + dec ebx + jnz .mmx_4_loop_i + jmp .mmx_end +.mmx_4more: + shl eax, 2 + neg eax + add eax, byte 16 + ALIGN 16 +.mmx_4more_loop_i: + mov ecx, edi + add ecx, eax + mov edx, esp + + movq mm7, mm4 + pmaddwd mm7, mm5 + + ALIGN 16 +.mmx_4more_loop_j: + movd mm0, [ecx - 16] + punpckldq mm0, [ecx - 12] + movd mm1, [ecx - 8] + punpckldq mm1, [ecx - 4] + packssdw mm0, mm1 + pmaddwd mm0, [edx] + paddd mm7, mm0 + + add edx, byte 8 + add ecx, byte 16 + cmp ecx, edi + jnz .mmx_4more_loop_j + + movq mm0, mm7 + punpckhdq mm7, mm7 + paddd mm7, mm0 + psrad mm7, mm6 + movd mm1, [esi] + paddd mm7, mm1 + movd [edi], mm7 + psllq mm7, 48 + psrlq mm4, 16 + por mm4, mm7 + + add esi, byte 4 + add edi, byte 4 + + dec ebx + jnz short .mmx_4more_loop_i +.mmx_end: + emms + mov esp, ebp + +.end: + pop edi + pop esi + pop ebx + pop ebp + ret + +end + +%ifdef OBJ_FORMAT_elf + section .note.GNU-stack noalloc +%endif diff --git a/src/FLAC/src/libFLAC/ia32/nasm.h b/src/FLAC/src/libFLAC/ia32/nasm.h new file mode 100644 index 00000000..215498d8 --- /dev/null +++ b/src/FLAC/src/libFLAC/ia32/nasm.h @@ -0,0 +1,75 @@ +; libFLAC - Free Lossless Audio Codec library +; Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions +; are met: +; +; - Redistributions of source code must retain the above copyright +; notice, this list of conditions and the following disclaimer. +; +; - Redistributions in binary form must reproduce the above copyright +; notice, this list of conditions and the following disclaimer in the +; documentation and/or other materials provided with the distribution. +; +; - Neither the name of the Xiph.org Foundation nor the names of its +; contributors may be used to endorse or promote products derived from +; this software without specific prior written permission. +; +; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +; ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +; LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +; A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR +; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +; EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +; PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +; LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + bits 32 + +%ifdef OBJ_FORMAT_win32 + %define FLAC__PUBLIC_NEEDS_UNDERSCORE + %idefine code_section section .text align=16 class=CODE use32 + %idefine data_section section .data align=32 class=DATA use32 + %idefine bss_section section .bss align=32 class=DATA use32 +%elifdef OBJ_FORMAT_aout + %define FLAC__PUBLIC_NEEDS_UNDERSCORE + %idefine code_section section .text + %idefine data_section section .data + %idefine bss_section section .bss +%elifdef OBJ_FORMAT_aoutb + %define FLAC__PUBLIC_NEEDS_UNDERSCORE + %idefine code_section section .text + %idefine data_section section .data + %idefine bss_section section .bss +%elifdef OBJ_FORMAT_elf + %idefine code_section section .text align=16 + %idefine data_section section .data align=32 + %idefine bss_section section .bss align=32 +%else + %error unsupported object format! +%endif + +%imacro cglobal 1 + %ifdef FLAC__PUBLIC_NEEDS_UNDERSCORE + global _%1 + %else + global %1 + %endif +%endmacro + +%imacro cextern 1 + %ifdef FLAC__PUBLIC_NEEDS_UNDERSCORE + extern _%1 + %else + extern %1 + %endif +%endmacro + +%imacro cident 1 +_%1: +%1: +%endmacro diff --git a/src/FLAC/src/libFLAC/ia32/stream_encoder_asm.nasm b/src/FLAC/src/libFLAC/ia32/stream_encoder_asm.nasm new file mode 100644 index 00000000..da2d0e63 --- /dev/null +++ b/src/FLAC/src/libFLAC/ia32/stream_encoder_asm.nasm @@ -0,0 +1,157 @@ +; vim:filetype=nasm ts=8 + +; libFLAC - Free Lossless Audio Codec library +; Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions +; are met: +; +; - Redistributions of source code must retain the above copyright +; notice, this list of conditions and the following disclaimer. +; +; - Redistributions in binary form must reproduce the above copyright +; notice, this list of conditions and the following disclaimer in the +; documentation and/or other materials provided with the distribution. +; +; - Neither the name of the Xiph.org Foundation nor the names of its +; contributors may be used to endorse or promote products derived from +; this software without specific prior written permission. +; +; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +; ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +; LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +; A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR +; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +; EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +; PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +; LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +%include "nasm.h" + + data_section + +cglobal precompute_partition_info_sums_32bit_asm_ia32_ + + code_section + + +; ********************************************************************** +; +; void FLAC__bool FLAC__bitreader_read_rice_signed_block(FLAC__BitReader *br, int vals[], unsigned nvals, unsigned parameter) +; void precompute_partition_info_sums_32bit_( +; const FLAC__int32 residual[], +; FLAC__uint64 abs_residual_partition_sums[], +; unsigned blocksize, +; unsigned predictor_order, +; unsigned min_partition_order, +; unsigned max_partition_order +; ) +; + ALIGN 16 +cident precompute_partition_info_sums_32bit_asm_ia32_ + + ;; peppered throughout the code at major checkpoints are keys like this as to where things are at that point in time + ;; [esp + 4] const FLAC__int32 residual[] + ;; [esp + 8] FLAC__uint64 abs_residual_partition_sums[] + ;; [esp + 12] unsigned blocksize + ;; [esp + 16] unsigned predictor_order + ;; [esp + 20] unsigned min_partition_order + ;; [esp + 24] unsigned max_partition_order + push ebp + push ebx + push esi + push edi + sub esp, 8 + ;; [esp + 28] const FLAC__int32 residual[] + ;; [esp + 32] FLAC__uint64 abs_residual_partition_sums[] + ;; [esp + 36] unsigned blocksize + ;; [esp + 40] unsigned predictor_order + ;; [esp + 44] unsigned min_partition_order + ;; [esp + 48] unsigned max_partition_order + ;; [esp] partitions + ;; [esp + 4] default_partition_samples + + mov ecx, [esp + 48] + mov eax, 1 + shl eax, cl + mov [esp], eax ; [esp] <- partitions = 1u << max_partition_order; + mov eax, [esp + 36] + shr eax, cl + mov [esp + 4], eax ; [esp + 4] <- default_partition_samples = blocksize >> max_partition_order; + + ; + ; first do max_partition_order + ; + mov edi, [esp + 4] + sub edi, [esp + 40] ; edi <- end = (unsigned)(-(int)predictor_order) + default_partition_samples + xor esi, esi ; esi <- residual_sample = 0 + xor ecx, ecx ; ecx <- partition = 0 + mov ebp, [esp + 28] ; ebp <- residual[] + xor ebx, ebx ; ebx <- abs_residual_partition_sum = 0; + ; note we put the updates to 'end' and 'abs_residual_partition_sum' at the end of loop0 and in the initialization above so we could align loop0 and loop1 + ALIGN 16 +.loop0: ; for(partition = residual_sample = 0; partition < partitions; partition++) { +.loop1: ; for( ; residual_sample < end; residual_sample++) + mov eax, [ebp + esi * 4] + cdq + xor eax, edx + sub eax, edx + add ebx, eax ; abs_residual_partition_sum += abs(residual[residual_sample]); + add esi, byte 1 + cmp esi, edi ; /* since the loop will always run at least once, we can put the loop check down here */ + jb .loop1 +.next1: + add edi, [esp + 4] ; end += default_partition_samples; + mov eax, [esp + 32] + mov [eax + ecx * 8], ebx ; abs_residual_partition_sums[partition] = abs_residual_partition_sum; + mov [eax + ecx * 8 + 4], dword 0 + xor ebx, ebx ; abs_residual_partition_sum = 0; + add ecx, byte 1 + cmp ecx, [esp] ; /* since the loop will always run at least once, we can put the loop check down here */ + jb .loop0 +.next0: ; } + ; + ; now merge partitions for lower orders + ; + mov esi, [esp + 32] ; esi <- abs_residual_partition_sums[from_partition==0]; + mov eax, [esp] + lea edi, [esi + eax * 8] ; edi <- abs_residual_partition_sums[to_partition==partitions]; + mov ecx, [esp + 48] + sub ecx, byte 1 ; ecx <- partition_order = (int)max_partition_order - 1; + ALIGN 16 +.loop2: ; for(; partition_order >= (int)min_partition_order; partition_order--) { + cmp ecx, [esp + 44] + jl .next2 + mov edx, 1 + shl edx, cl ; const unsigned partitions = 1u << partition_order; + ALIGN 16 +.loop3: ; for(i = 0; i < partitions; i++) { + mov eax, [esi] + mov [edi + 4], dword 0 + mov [edi], eax + mov ebx, [esi + 8] + add [edi], ebx ; a_r_p_s[to_partition] = a_r_p_s[from_partition] + a_r_p_s[from_partition+1]; + add esi, byte 16 + add edi, byte 8 + sub edx, byte 1 + jnz .loop3 ; } + sub ecx, byte 1 + jmp .loop2 ; } +.next2: + + add esp, 8 + pop edi + pop esi + pop ebx + pop ebp + ret + +end + +%ifdef OBJ_FORMAT_elf + section .note.GNU-stack noalloc +%endif diff --git a/src/FLAC/src/libFLAC/include/Makefile.am b/src/FLAC/src/libFLAC/include/Makefile.am new file mode 100644 index 00000000..866a8948 --- /dev/null +++ b/src/FLAC/src/libFLAC/include/Makefile.am @@ -0,0 +1,31 @@ +# libFLAC - Free Lossless Audio Codec library +# Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# - Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# - Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# - Neither the name of the Xiph.org Foundation nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +SUBDIRS = private protected diff --git a/src/FLAC/src/libFLAC/include/private/Makefile.am b/src/FLAC/src/libFLAC/include/private/Makefile.am new file mode 100644 index 00000000..e8abc582 --- /dev/null +++ b/src/FLAC/src/libFLAC/include/private/Makefile.am @@ -0,0 +1,50 @@ +# libFLAC - Free Lossless Audio Codec library +# Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# - Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# - Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# - Neither the name of the Xiph.org Foundation nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +noinst_HEADERS = \ + all.h \ + bitmath.h \ + bitreader.h \ + bitwriter.h \ + cpu.h \ + crc.h \ + fixed.h \ + float.h \ + format.h \ + lpc.h \ + md5.h \ + memory.h \ + metadata.h \ + ogg_decoder_aspect.h \ + ogg_encoder_aspect.h \ + ogg_helper.h \ + ogg_mapping.h \ + stream_encoder_framing.h \ + window.h diff --git a/src/FLAC/src/libFLAC/include/private/all.h b/src/FLAC/src/libFLAC/include/private/all.h new file mode 100644 index 00000000..304c471a --- /dev/null +++ b/src/FLAC/src/libFLAC/include/private/all.h @@ -0,0 +1,49 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__ALL_H +#define FLAC__PRIVATE__ALL_H + +#include "bitmath.h" +#include "bitreader.h" +#include "bitwriter.h" +#include "cpu.h" +#include "crc.h" +#include "fixed.h" +#include "float.h" +#include "format.h" +#include "lpc.h" +#include "md5.h" +#include "memory.h" +#include "metadata.h" +#include "stream_encoder_framing.h" + +#endif diff --git a/src/FLAC/src/libFLAC/include/private/bitmath.h b/src/FLAC/src/libFLAC/include/private/bitmath.h new file mode 100644 index 00000000..87fa0fac --- /dev/null +++ b/src/FLAC/src/libFLAC/include/private/bitmath.h @@ -0,0 +1,42 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__BITMATH_H +#define FLAC__PRIVATE__BITMATH_H + +#include "FLAC/ordinals.h" + +unsigned FLAC__bitmath_ilog2(FLAC__uint32 v); +unsigned FLAC__bitmath_ilog2_wide(FLAC__uint64 v); +unsigned FLAC__bitmath_silog2(int v); +unsigned FLAC__bitmath_silog2_wide(FLAC__int64 v); + +#endif diff --git a/src/FLAC/src/libFLAC/include/private/bitreader.h b/src/FLAC/src/libFLAC/include/private/bitreader.h new file mode 100644 index 00000000..1ad97421 --- /dev/null +++ b/src/FLAC/src/libFLAC/include/private/bitreader.h @@ -0,0 +1,99 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__BITREADER_H +#define FLAC__PRIVATE__BITREADER_H + +#include /* for FILE */ +#include "FLAC/ordinals.h" +#include "cpu.h" + +/* + * opaque structure definition + */ +struct FLAC__BitReader; +typedef struct FLAC__BitReader FLAC__BitReader; + +typedef FLAC__bool (*FLAC__BitReaderReadCallback)(FLAC__byte buffer[], size_t *bytes, void *client_data); + +/* + * construction, deletion, initialization, etc functions + */ +FLAC__BitReader *FLAC__bitreader_new(void); +void FLAC__bitreader_delete(FLAC__BitReader *br); +FLAC__bool FLAC__bitreader_init(FLAC__BitReader *br, FLAC__CPUInfo cpu, FLAC__BitReaderReadCallback rcb, void *cd); +void FLAC__bitreader_free(FLAC__BitReader *br); /* does not 'free(br)' */ +FLAC__bool FLAC__bitreader_clear(FLAC__BitReader *br); +void FLAC__bitreader_dump(const FLAC__BitReader *br, FILE *out); + +/* + * CRC functions + */ +void FLAC__bitreader_reset_read_crc16(FLAC__BitReader *br, FLAC__uint16 seed); +FLAC__uint16 FLAC__bitreader_get_read_crc16(FLAC__BitReader *br); + +/* + * info functions + */ +FLAC__bool FLAC__bitreader_is_consumed_byte_aligned(const FLAC__BitReader *br); +unsigned FLAC__bitreader_bits_left_for_byte_alignment(const FLAC__BitReader *br); +unsigned FLAC__bitreader_get_input_bits_unconsumed(const FLAC__BitReader *br); + +/* + * read functions + */ + +FLAC__bool FLAC__bitreader_read_raw_uint32(FLAC__BitReader *br, FLAC__uint32 *val, unsigned bits); +FLAC__bool FLAC__bitreader_read_raw_int32(FLAC__BitReader *br, FLAC__int32 *val, unsigned bits); +FLAC__bool FLAC__bitreader_read_raw_uint64(FLAC__BitReader *br, FLAC__uint64 *val, unsigned bits); +FLAC__bool FLAC__bitreader_read_uint32_little_endian(FLAC__BitReader *br, FLAC__uint32 *val); /*only for bits=32*/ +FLAC__bool FLAC__bitreader_skip_bits_no_crc(FLAC__BitReader *br, unsigned bits); /* WATCHOUT: does not CRC the skipped data! */ /*@@@@ add to unit tests */ +FLAC__bool FLAC__bitreader_skip_byte_block_aligned_no_crc(FLAC__BitReader *br, unsigned nvals); /* WATCHOUT: does not CRC the read data! */ +FLAC__bool FLAC__bitreader_read_byte_block_aligned_no_crc(FLAC__BitReader *br, FLAC__byte *val, unsigned nvals); /* WATCHOUT: does not CRC the read data! */ +FLAC__bool FLAC__bitreader_read_unary_unsigned(FLAC__BitReader *br, unsigned *val); +FLAC__bool FLAC__bitreader_read_rice_signed(FLAC__BitReader *br, int *val, unsigned parameter); +FLAC__bool FLAC__bitreader_read_rice_signed_block(FLAC__BitReader *br, int vals[], unsigned nvals, unsigned parameter); +#ifndef FLAC__NO_ASM +# ifdef FLAC__CPU_IA32 +# ifdef FLAC__HAS_NASM +FLAC__bool FLAC__bitreader_read_rice_signed_block_asm_ia32_bswap(FLAC__BitReader *br, int vals[], unsigned nvals, unsigned parameter); +# endif +# endif +#endif +#if 0 /* UNUSED */ +FLAC__bool FLAC__bitreader_read_golomb_signed(FLAC__BitReader *br, int *val, unsigned parameter); +FLAC__bool FLAC__bitreader_read_golomb_unsigned(FLAC__BitReader *br, unsigned *val, unsigned parameter); +#endif +FLAC__bool FLAC__bitreader_read_utf8_uint32(FLAC__BitReader *br, FLAC__uint32 *val, FLAC__byte *raw, unsigned *rawlen); +FLAC__bool FLAC__bitreader_read_utf8_uint64(FLAC__BitReader *br, FLAC__uint64 *val, FLAC__byte *raw, unsigned *rawlen); + +FLAC__bool bitreader_read_from_client_(FLAC__BitReader *br);//@@@@@@ +#endif diff --git a/src/FLAC/src/libFLAC/include/private/bitwriter.h b/src/FLAC/src/libFLAC/include/private/bitwriter.h new file mode 100644 index 00000000..aa5c4f74 --- /dev/null +++ b/src/FLAC/src/libFLAC/include/private/bitwriter.h @@ -0,0 +1,103 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__BITWRITER_H +#define FLAC__PRIVATE__BITWRITER_H + +#include /* for FILE */ +#include "FLAC/ordinals.h" + +/* + * opaque structure definition + */ +struct FLAC__BitWriter; +typedef struct FLAC__BitWriter FLAC__BitWriter; + +/* + * construction, deletion, initialization, etc functions + */ +FLAC__BitWriter *FLAC__bitwriter_new(void); +void FLAC__bitwriter_delete(FLAC__BitWriter *bw); +FLAC__bool FLAC__bitwriter_init(FLAC__BitWriter *bw); +void FLAC__bitwriter_free(FLAC__BitWriter *bw); /* does not 'free(buffer)' */ +void FLAC__bitwriter_clear(FLAC__BitWriter *bw); +void FLAC__bitwriter_dump(const FLAC__BitWriter *bw, FILE *out); + +/* + * CRC functions + * + * non-const *bw because they have to cal FLAC__bitwriter_get_buffer() + */ +FLAC__bool FLAC__bitwriter_get_write_crc16(FLAC__BitWriter *bw, FLAC__uint16 *crc); +FLAC__bool FLAC__bitwriter_get_write_crc8(FLAC__BitWriter *bw, FLAC__byte *crc); + +/* + * info functions + */ +FLAC__bool FLAC__bitwriter_is_byte_aligned(const FLAC__BitWriter *bw); +unsigned FLAC__bitwriter_get_input_bits_unconsumed(const FLAC__BitWriter *bw); /* can be called anytime, returns total # of bits unconsumed */ + +/* + * direct buffer access + * + * there may be no calls on the bitwriter between get and release. + * the bitwriter continues to own the returned buffer. + * before get, bitwriter MUST be byte aligned: check with FLAC__bitwriter_is_byte_aligned() + */ +FLAC__bool FLAC__bitwriter_get_buffer(FLAC__BitWriter *bw, const FLAC__byte **buffer, size_t *bytes); +void FLAC__bitwriter_release_buffer(FLAC__BitWriter *bw); + +/* + * write functions + */ +FLAC__bool FLAC__bitwriter_write_zeroes(FLAC__BitWriter *bw, unsigned bits); +FLAC__bool FLAC__bitwriter_write_raw_uint32(FLAC__BitWriter *bw, FLAC__uint32 val, unsigned bits); +FLAC__bool FLAC__bitwriter_write_raw_int32(FLAC__BitWriter *bw, FLAC__int32 val, unsigned bits); +FLAC__bool FLAC__bitwriter_write_raw_uint64(FLAC__BitWriter *bw, FLAC__uint64 val, unsigned bits); +FLAC__bool FLAC__bitwriter_write_raw_uint32_little_endian(FLAC__BitWriter *bw, FLAC__uint32 val); /*only for bits=32*/ +FLAC__bool FLAC__bitwriter_write_byte_block(FLAC__BitWriter *bw, const FLAC__byte vals[], unsigned nvals); +FLAC__bool FLAC__bitwriter_write_unary_unsigned(FLAC__BitWriter *bw, unsigned val); +unsigned FLAC__bitwriter_rice_bits(FLAC__int32 val, unsigned parameter); +#if 0 /* UNUSED */ +unsigned FLAC__bitwriter_golomb_bits_signed(int val, unsigned parameter); +unsigned FLAC__bitwriter_golomb_bits_unsigned(unsigned val, unsigned parameter); +#endif +FLAC__bool FLAC__bitwriter_write_rice_signed(FLAC__BitWriter *bw, FLAC__int32 val, unsigned parameter); +FLAC__bool FLAC__bitwriter_write_rice_signed_block(FLAC__BitWriter *bw, const FLAC__int32 *vals, unsigned nvals, unsigned parameter); +#if 0 /* UNUSED */ +FLAC__bool FLAC__bitwriter_write_golomb_signed(FLAC__BitWriter *bw, int val, unsigned parameter); +FLAC__bool FLAC__bitwriter_write_golomb_unsigned(FLAC__BitWriter *bw, unsigned val, unsigned parameter); +#endif +FLAC__bool FLAC__bitwriter_write_utf8_uint32(FLAC__BitWriter *bw, FLAC__uint32 val); +FLAC__bool FLAC__bitwriter_write_utf8_uint64(FLAC__BitWriter *bw, FLAC__uint64 val); +FLAC__bool FLAC__bitwriter_zero_pad_to_byte_boundary(FLAC__BitWriter *bw); + +#endif diff --git a/src/FLAC/src/libFLAC/include/private/cpu.h b/src/FLAC/src/libFLAC/include/private/cpu.h new file mode 100644 index 00000000..651bb224 --- /dev/null +++ b/src/FLAC/src/libFLAC/include/private/cpu.h @@ -0,0 +1,88 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__CPU_H +#define FLAC__PRIVATE__CPU_H + +#include "FLAC/ordinals.h" + +#ifdef HAVE_CONFIG_H +#include +#endif + +typedef enum { + FLAC__CPUINFO_TYPE_IA32, + FLAC__CPUINFO_TYPE_PPC, + FLAC__CPUINFO_TYPE_UNKNOWN +} FLAC__CPUInfo_Type; + +typedef struct { + FLAC__bool cpuid; + FLAC__bool bswap; + FLAC__bool cmov; + FLAC__bool mmx; + FLAC__bool fxsr; + FLAC__bool sse; + FLAC__bool sse2; + FLAC__bool sse3; + FLAC__bool ssse3; + FLAC__bool _3dnow; + FLAC__bool ext3dnow; + FLAC__bool extmmx; +} FLAC__CPUInfo_IA32; + +typedef struct { + FLAC__bool altivec; + FLAC__bool ppc64; +} FLAC__CPUInfo_PPC; + +typedef struct { + FLAC__bool use_asm; + FLAC__CPUInfo_Type type; + union { + FLAC__CPUInfo_IA32 ia32; + FLAC__CPUInfo_PPC ppc; + } data; +} FLAC__CPUInfo; + +void FLAC__cpu_info(FLAC__CPUInfo *info); + +#ifndef FLAC__NO_ASM +#ifdef FLAC__CPU_IA32 +#ifdef FLAC__HAS_NASM +FLAC__uint32 FLAC__cpu_have_cpuid_asm_ia32(void); +void FLAC__cpu_info_asm_ia32(FLAC__uint32 *flags_edx, FLAC__uint32 *flags_ecx); +FLAC__uint32 FLAC__cpu_info_extended_amd_asm_ia32(void); +#endif +#endif +#endif + +#endif diff --git a/src/FLAC/src/libFLAC/include/private/crc.h b/src/FLAC/src/libFLAC/include/private/crc.h new file mode 100644 index 00000000..0b67fb4b --- /dev/null +++ b/src/FLAC/src/libFLAC/include/private/crc.h @@ -0,0 +1,61 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__CRC_H +#define FLAC__PRIVATE__CRC_H + +#include "FLAC/ordinals.h" + +/* 8 bit CRC generator, MSB shifted first +** polynomial = x^8 + x^2 + x^1 + x^0 +** init = 0 +*/ +extern FLAC__byte const FLAC__crc8_table[256]; +#define FLAC__CRC8_UPDATE(data, crc) (crc) = FLAC__crc8_table[(crc) ^ (data)]; +void FLAC__crc8_update(const FLAC__byte data, FLAC__uint8 *crc); +void FLAC__crc8_update_block(const FLAC__byte *data, unsigned len, FLAC__uint8 *crc); +FLAC__uint8 FLAC__crc8(const FLAC__byte *data, unsigned len); + +/* 16 bit CRC generator, MSB shifted first +** polynomial = x^16 + x^15 + x^2 + x^0 +** init = 0 +*/ +extern unsigned FLAC__crc16_table[256]; + +#define FLAC__CRC16_UPDATE(data, crc) (((((crc)<<8) & 0xffff) ^ FLAC__crc16_table[((crc)>>8) ^ (data)])) +/* this alternate may be faster on some systems/compilers */ +#if 0 +#define FLAC__CRC16_UPDATE(data, crc) ((((crc)<<8) ^ FLAC__crc16_table[((crc)>>8) ^ (data)]) & 0xffff) +#endif + +unsigned FLAC__crc16(const FLAC__byte *data, unsigned len); + +#endif diff --git a/src/FLAC/src/libFLAC/include/private/fixed.h b/src/FLAC/src/libFLAC/include/private/fixed.h new file mode 100644 index 00000000..6656b796 --- /dev/null +++ b/src/FLAC/src/libFLAC/include/private/fixed.h @@ -0,0 +1,97 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__FIXED_H +#define FLAC__PRIVATE__FIXED_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "private/float.h" +#include "FLAC/format.h" + +/* + * FLAC__fixed_compute_best_predictor() + * -------------------------------------------------------------------- + * Compute the best fixed predictor and the expected bits-per-sample + * of the residual signal for each order. The _wide() version uses + * 64-bit integers which is statistically necessary when bits-per- + * sample + log2(blocksize) > 30 + * + * IN data[0,data_len-1] + * IN data_len + * OUT residual_bits_per_sample[0,FLAC__MAX_FIXED_ORDER] + */ +#ifndef FLAC__INTEGER_ONLY_LIBRARY +unsigned FLAC__fixed_compute_best_predictor(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); +# ifndef FLAC__NO_ASM +# ifdef FLAC__CPU_IA32 +# ifdef FLAC__HAS_NASM +unsigned FLAC__fixed_compute_best_predictor_asm_ia32_mmx_cmov(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); +# endif +# endif +# endif +unsigned FLAC__fixed_compute_best_predictor_wide(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); +#else +unsigned FLAC__fixed_compute_best_predictor(const FLAC__int32 data[], unsigned data_len, FLAC__fixedpoint residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); +unsigned FLAC__fixed_compute_best_predictor_wide(const FLAC__int32 data[], unsigned data_len, FLAC__fixedpoint residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); +#endif + +/* + * FLAC__fixed_compute_residual() + * -------------------------------------------------------------------- + * Compute the residual signal obtained from sutracting the predicted + * signal from the original. + * + * IN data[-order,data_len-1] original signal (NOTE THE INDICES!) + * IN data_len length of original signal + * IN order <= FLAC__MAX_FIXED_ORDER fixed-predictor order + * OUT residual[0,data_len-1] residual signal + */ +void FLAC__fixed_compute_residual(const FLAC__int32 data[], unsigned data_len, unsigned order, FLAC__int32 residual[]); + +/* + * FLAC__fixed_restore_signal() + * -------------------------------------------------------------------- + * Restore the original signal by summing the residual and the + * predictor. + * + * IN residual[0,data_len-1] residual signal + * IN data_len length of original signal + * IN order <= FLAC__MAX_FIXED_ORDER fixed-predictor order + * *** IMPORTANT: the caller must pass in the historical samples: + * IN data[-order,-1] previously-reconstructed historical samples + * OUT data[0,data_len-1] original signal + */ +void FLAC__fixed_restore_signal(const FLAC__int32 residual[], unsigned data_len, unsigned order, FLAC__int32 data[]); + +#endif diff --git a/src/FLAC/src/libFLAC/include/private/float.h b/src/FLAC/src/libFLAC/include/private/float.h new file mode 100644 index 00000000..73313f6d --- /dev/null +++ b/src/FLAC/src/libFLAC/include/private/float.h @@ -0,0 +1,97 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__FLOAT_H +#define FLAC__PRIVATE__FLOAT_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "FLAC/ordinals.h" + +/* + * These typedefs make it easier to ensure that integer versions of + * the library really only contain integer operations. All the code + * in libFLAC should use FLAC__float and FLAC__double in place of + * float and double, and be protected by checks of the macro + * FLAC__INTEGER_ONLY_LIBRARY. + * + * FLAC__real is the basic floating point type used in LPC analysis. + */ +#ifndef FLAC__INTEGER_ONLY_LIBRARY +typedef double FLAC__double; +typedef float FLAC__float; +/* + * WATCHOUT: changing FLAC__real will change the signatures of many + * functions that have assembly language equivalents and break them. + */ +typedef float FLAC__real; +#else +/* + * The convention for FLAC__fixedpoint is to use the upper 16 bits + * for the integer part and lower 16 bits for the fractional part. + */ +typedef FLAC__int32 FLAC__fixedpoint; +extern const FLAC__fixedpoint FLAC__FP_ZERO; +extern const FLAC__fixedpoint FLAC__FP_ONE_HALF; +extern const FLAC__fixedpoint FLAC__FP_ONE; +extern const FLAC__fixedpoint FLAC__FP_LN2; +extern const FLAC__fixedpoint FLAC__FP_E; + +#define FLAC__fixedpoint_trunc(x) ((x)>>16) + +#define FLAC__fixedpoint_mul(x, y) ( (FLAC__fixedpoint) ( ((FLAC__int64)(x)*(FLAC__int64)(y)) >> 16 ) ) + +#define FLAC__fixedpoint_div(x, y) ( (FLAC__fixedpoint) ( ( ((FLAC__int64)(x)<<32) / (FLAC__int64)(y) ) >> 16 ) ) + +/* + * FLAC__fixedpoint_log2() + * -------------------------------------------------------------------- + * Returns the base-2 logarithm of the fixed-point number 'x' using an + * algorithm by Knuth for x >= 1.0 + * + * 'fracbits' is the number of fractional bits of 'x'. 'fracbits' must + * be < 32 and evenly divisible by 4 (0 is OK but not very precise). + * + * 'precision' roughly limits the number of iterations that are done; + * use (unsigned)(-1) for maximum precision. + * + * If 'x' is less than one -- that is, x < (1< +#endif + +#include "private/float.h" +#include "FLAC/format.h" + +#ifndef FLAC__INTEGER_ONLY_LIBRARY + +/* + * FLAC__lpc_window_data() + * -------------------------------------------------------------------- + * Applies the given window to the data. + * OPT: asm implementation + * + * IN in[0,data_len-1] + * IN window[0,data_len-1] + * OUT out[0,lag-1] + * IN data_len + */ +void FLAC__lpc_window_data(const FLAC__int32 in[], const FLAC__real window[], FLAC__real out[], unsigned data_len); + +/* + * FLAC__lpc_compute_autocorrelation() + * -------------------------------------------------------------------- + * Compute the autocorrelation for lags between 0 and lag-1. + * Assumes data[] outside of [0,data_len-1] == 0. + * Asserts that lag > 0. + * + * IN data[0,data_len-1] + * IN data_len + * IN 0 < lag <= data_len + * OUT autoc[0,lag-1] + */ +void FLAC__lpc_compute_autocorrelation(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); +#ifndef FLAC__NO_ASM +# ifdef FLAC__CPU_IA32 +# ifdef FLAC__HAS_NASM +void FLAC__lpc_compute_autocorrelation_asm_ia32(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_4(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_8(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_12(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_asm_ia32_3dnow(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); +# endif +# endif +#endif + +/* + * FLAC__lpc_compute_lp_coefficients() + * -------------------------------------------------------------------- + * Computes LP coefficients for orders 1..max_order. + * Do not call if autoc[0] == 0.0. This means the signal is zero + * and there is no point in calculating a predictor. + * + * IN autoc[0,max_order] autocorrelation values + * IN 0 < max_order <= FLAC__MAX_LPC_ORDER max LP order to compute + * OUT lp_coeff[0,max_order-1][0,max_order-1] LP coefficients for each order + * *** IMPORTANT: + * *** lp_coeff[0,max_order-1][max_order,FLAC__MAX_LPC_ORDER-1] are untouched + * OUT error[0,max_order-1] error for each order (more + * specifically, the variance of + * the error signal times # of + * samples in the signal) + * + * Example: if max_order is 9, the LP coefficients for order 9 will be + * in lp_coeff[8][0,8], the LP coefficients for order 8 will be + * in lp_coeff[7][0,7], etc. + */ +void FLAC__lpc_compute_lp_coefficients(const FLAC__real autoc[], unsigned *max_order, FLAC__real lp_coeff[][FLAC__MAX_LPC_ORDER], FLAC__double error[]); + +/* + * FLAC__lpc_quantize_coefficients() + * -------------------------------------------------------------------- + * Quantizes the LP coefficients. NOTE: precision + bits_per_sample + * must be less than 32 (sizeof(FLAC__int32)*8). + * + * IN lp_coeff[0,order-1] LP coefficients + * IN order LP order + * IN FLAC__MIN_QLP_COEFF_PRECISION < precision + * desired precision (in bits, including sign + * bit) of largest coefficient + * OUT qlp_coeff[0,order-1] quantized coefficients + * OUT shift # of bits to shift right to get approximated + * LP coefficients. NOTE: could be negative. + * RETURN 0 => quantization OK + * 1 => coefficients require too much shifting for *shift to + * fit in the LPC subframe header. 'shift' is unset. + * 2 => coefficients are all zero, which is bad. 'shift' is + * unset. + */ +int FLAC__lpc_quantize_coefficients(const FLAC__real lp_coeff[], unsigned order, unsigned precision, FLAC__int32 qlp_coeff[], int *shift); + +/* + * FLAC__lpc_compute_residual_from_qlp_coefficients() + * -------------------------------------------------------------------- + * Compute the residual signal obtained from sutracting the predicted + * signal from the original. + * + * IN data[-order,data_len-1] original signal (NOTE THE INDICES!) + * IN data_len length of original signal + * IN qlp_coeff[0,order-1] quantized LP coefficients + * IN order > 0 LP order + * IN lp_quantization quantization of LP coefficients in bits + * OUT residual[0,data_len-1] residual signal + */ +void FLAC__lpc_compute_residual_from_qlp_coefficients(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); +void FLAC__lpc_compute_residual_from_qlp_coefficients_wide(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); +#ifndef FLAC__NO_ASM +# ifdef FLAC__CPU_IA32 +# ifdef FLAC__HAS_NASM +void FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); +void FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32_mmx(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); +# endif +# endif +#endif + +#endif /* !defined FLAC__INTEGER_ONLY_LIBRARY */ + +/* + * FLAC__lpc_restore_signal() + * -------------------------------------------------------------------- + * Restore the original signal by summing the residual and the + * predictor. + * + * IN residual[0,data_len-1] residual signal + * IN data_len length of original signal + * IN qlp_coeff[0,order-1] quantized LP coefficients + * IN order > 0 LP order + * IN lp_quantization quantization of LP coefficients in bits + * *** IMPORTANT: the caller must pass in the historical samples: + * IN data[-order,-1] previously-reconstructed historical samples + * OUT data[0,data_len-1] original signal + */ +void FLAC__lpc_restore_signal(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); +void FLAC__lpc_restore_signal_wide(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); +#ifndef FLAC__NO_ASM +# ifdef FLAC__CPU_IA32 +# ifdef FLAC__HAS_NASM +void FLAC__lpc_restore_signal_asm_ia32(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); +void FLAC__lpc_restore_signal_asm_ia32_mmx(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); +# endif /* FLAC__HAS_NASM */ +# elif defined FLAC__CPU_PPC +void FLAC__lpc_restore_signal_asm_ppc_altivec_16(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); +void FLAC__lpc_restore_signal_asm_ppc_altivec_16_order8(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); +# endif/* FLAC__CPU_IA32 || FLAC__CPU_PPC */ +#endif /* FLAC__NO_ASM */ + +#ifndef FLAC__INTEGER_ONLY_LIBRARY + +/* + * FLAC__lpc_compute_expected_bits_per_residual_sample() + * -------------------------------------------------------------------- + * Compute the expected number of bits per residual signal sample + * based on the LP error (which is related to the residual variance). + * + * IN lpc_error >= 0.0 error returned from calculating LP coefficients + * IN total_samples > 0 # of samples in residual signal + * RETURN expected bits per sample + */ +FLAC__double FLAC__lpc_compute_expected_bits_per_residual_sample(FLAC__double lpc_error, unsigned total_samples); +FLAC__double FLAC__lpc_compute_expected_bits_per_residual_sample_with_error_scale(FLAC__double lpc_error, FLAC__double error_scale); + +/* + * FLAC__lpc_compute_best_order() + * -------------------------------------------------------------------- + * Compute the best order from the array of signal errors returned + * during coefficient computation. + * + * IN lpc_error[0,max_order-1] >= 0.0 error returned from calculating LP coefficients + * IN max_order > 0 max LP order + * IN total_samples > 0 # of samples in residual signal + * IN overhead_bits_per_order # of bits overhead for each increased LP order + * (includes warmup sample size and quantized LP coefficient) + * RETURN [1,max_order] best order + */ +unsigned FLAC__lpc_compute_best_order(const FLAC__double lpc_error[], unsigned max_order, unsigned total_samples, unsigned overhead_bits_per_order); + +#endif /* !defined FLAC__INTEGER_ONLY_LIBRARY */ + +#endif diff --git a/src/FLAC/src/libFLAC/include/private/md5.h b/src/FLAC/src/libFLAC/include/private/md5.h new file mode 100644 index 00000000..659912c5 --- /dev/null +++ b/src/FLAC/src/libFLAC/include/private/md5.h @@ -0,0 +1,44 @@ +#ifndef FLAC__PRIVATE__MD5_H +#define FLAC__PRIVATE__MD5_H + +/* + * This is the header file for the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + * + * Changed so as no longer to depend on Colin Plumb's `usual.h' + * header definitions; now uses stuff from dpkg's config.h + * - Ian Jackson . + * Still in the public domain. + * + * Josh Coalson: made some changes to integrate with libFLAC. + * Still in the public domain, with no warranty. + */ + +#include "FLAC/ordinals.h" + +typedef struct { + FLAC__uint32 in[16]; + FLAC__uint32 buf[4]; + FLAC__uint32 bytes[2]; + FLAC__byte *internal_buf; + unsigned capacity; +} FLAC__MD5Context; + +void FLAC__MD5Init(FLAC__MD5Context *context); +void FLAC__MD5Final(FLAC__byte digest[16], FLAC__MD5Context *context); + +FLAC__bool FLAC__MD5Accumulate(FLAC__MD5Context *ctx, const FLAC__int32 * const signal[], unsigned channels, unsigned samples, unsigned bytes_per_sample); + +#endif diff --git a/src/FLAC/src/libFLAC/include/private/memory.h b/src/FLAC/src/libFLAC/include/private/memory.h new file mode 100644 index 00000000..7852c815 --- /dev/null +++ b/src/FLAC/src/libFLAC/include/private/memory.h @@ -0,0 +1,56 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__MEMORY_H +#define FLAC__PRIVATE__MEMORY_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include /* for size_t */ + +#include "private/float.h" +#include "FLAC/ordinals.h" /* for FLAC__bool */ + +/* Returns the unaligned address returned by malloc. + * Use free() on this address to deallocate. + */ +void *FLAC__memory_alloc_aligned(size_t bytes, void **aligned_address); +FLAC__bool FLAC__memory_alloc_aligned_int32_array(unsigned elements, FLAC__int32 **unaligned_pointer, FLAC__int32 **aligned_pointer); +FLAC__bool FLAC__memory_alloc_aligned_uint32_array(unsigned elements, FLAC__uint32 **unaligned_pointer, FLAC__uint32 **aligned_pointer); +FLAC__bool FLAC__memory_alloc_aligned_uint64_array(unsigned elements, FLAC__uint64 **unaligned_pointer, FLAC__uint64 **aligned_pointer); +FLAC__bool FLAC__memory_alloc_aligned_unsigned_array(unsigned elements, unsigned **unaligned_pointer, unsigned **aligned_pointer); +#ifndef FLAC__INTEGER_ONLY_LIBRARY +FLAC__bool FLAC__memory_alloc_aligned_real_array(unsigned elements, FLAC__real **unaligned_pointer, FLAC__real **aligned_pointer); +#endif + +#endif diff --git a/src/FLAC/src/libFLAC/include/private/metadata.h b/src/FLAC/src/libFLAC/include/private/metadata.h new file mode 100644 index 00000000..b5268c95 --- /dev/null +++ b/src/FLAC/src/libFLAC/include/private/metadata.h @@ -0,0 +1,45 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__METADATA_H +#define FLAC__PRIVATE__METADATA_H + +#include "FLAC/metadata.h" + +/* WATCHOUT: all malloc()ed data in the block is free()ed; this may not + * be a consistent state (e.g. PICTURE) or equivalent to the initial + * state after FLAC__metadata_object_new() + */ +void FLAC__metadata_object_delete_data(FLAC__StreamMetadata *object); + +void FLAC__metadata_object_cuesheet_track_delete_data(FLAC__StreamMetadata_CueSheet_Track *object); + +#endif diff --git a/src/FLAC/src/libFLAC/include/private/ogg_decoder_aspect.h b/src/FLAC/src/libFLAC/include/private/ogg_decoder_aspect.h new file mode 100644 index 00000000..df2b6b55 --- /dev/null +++ b/src/FLAC/src/libFLAC/include/private/ogg_decoder_aspect.h @@ -0,0 +1,79 @@ +/* libFLAC - Free Lossless Audio Codec + * Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__OGG_DECODER_ASPECT_H +#define FLAC__PRIVATE__OGG_DECODER_ASPECT_H + +#include + +#include "FLAC/ordinals.h" +#include "FLAC/stream_decoder.h" /* for FLAC__StreamDecoderReadStatus */ + +typedef struct FLAC__OggDecoderAspect { + /* these are storage for values that can be set through the API */ + FLAC__bool use_first_serial_number; + long serial_number; + + /* these are for internal state related to Ogg decoding */ + ogg_stream_state stream_state; + ogg_sync_state sync_state; + unsigned version_major, version_minor; + FLAC__bool need_serial_number; + FLAC__bool end_of_stream; + FLAC__bool have_working_page; /* only if true will the following vars be valid */ + ogg_page working_page; + FLAC__bool have_working_packet; /* only if true will the following vars be valid */ + ogg_packet working_packet; /* as we work through the packet we will move working_packet.packet forward and working_packet.bytes down */ +} FLAC__OggDecoderAspect; + +void FLAC__ogg_decoder_aspect_set_serial_number(FLAC__OggDecoderAspect *aspect, long value); +void FLAC__ogg_decoder_aspect_set_defaults(FLAC__OggDecoderAspect *aspect); +FLAC__bool FLAC__ogg_decoder_aspect_init(FLAC__OggDecoderAspect *aspect); +void FLAC__ogg_decoder_aspect_finish(FLAC__OggDecoderAspect *aspect); +void FLAC__ogg_decoder_aspect_flush(FLAC__OggDecoderAspect *aspect); +void FLAC__ogg_decoder_aspect_reset(FLAC__OggDecoderAspect *aspect); + +typedef enum { + FLAC__OGG_DECODER_ASPECT_READ_STATUS_OK = 0, + FLAC__OGG_DECODER_ASPECT_READ_STATUS_END_OF_STREAM, + FLAC__OGG_DECODER_ASPECT_READ_STATUS_LOST_SYNC, + FLAC__OGG_DECODER_ASPECT_READ_STATUS_NOT_FLAC, + FLAC__OGG_DECODER_ASPECT_READ_STATUS_UNSUPPORTED_MAPPING_VERSION, + FLAC__OGG_DECODER_ASPECT_READ_STATUS_ABORT, + FLAC__OGG_DECODER_ASPECT_READ_STATUS_ERROR, + FLAC__OGG_DECODER_ASPECT_READ_STATUS_MEMORY_ALLOCATION_ERROR +} FLAC__OggDecoderAspectReadStatus; + +typedef FLAC__OggDecoderAspectReadStatus (*FLAC__OggDecoderAspectReadCallbackProxy)(const void *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data); + +FLAC__OggDecoderAspectReadStatus FLAC__ogg_decoder_aspect_read_callback_wrapper(FLAC__OggDecoderAspect *aspect, FLAC__byte buffer[], size_t *bytes, FLAC__OggDecoderAspectReadCallbackProxy read_callback, const FLAC__StreamDecoder *decoder, void *client_data); + +#endif diff --git a/src/FLAC/src/libFLAC/include/private/ogg_encoder_aspect.h b/src/FLAC/src/libFLAC/include/private/ogg_encoder_aspect.h new file mode 100644 index 00000000..290da071 --- /dev/null +++ b/src/FLAC/src/libFLAC/include/private/ogg_encoder_aspect.h @@ -0,0 +1,62 @@ +/* libFLAC - Free Lossless Audio Codec + * Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__OGG_ENCODER_ASPECT_H +#define FLAC__PRIVATE__OGG_ENCODER_ASPECT_H + +#include + +#include "FLAC/ordinals.h" +#include "FLAC/stream_encoder.h" /* for FLAC__StreamEncoderWriteStatus */ + +typedef struct FLAC__OggEncoderAspect { + /* these are storage for values that can be set through the API */ + long serial_number; + unsigned num_metadata; + + /* these are for internal state related to Ogg encoding */ + ogg_stream_state stream_state; + ogg_page page; + FLAC__bool seen_magic; /* true if we've seen the fLaC magic in the write callback yet */ + FLAC__bool is_first_packet; + FLAC__uint64 samples_written; +} FLAC__OggEncoderAspect; + +void FLAC__ogg_encoder_aspect_set_serial_number(FLAC__OggEncoderAspect *aspect, long value); +FLAC__bool FLAC__ogg_encoder_aspect_set_num_metadata(FLAC__OggEncoderAspect *aspect, unsigned value); +void FLAC__ogg_encoder_aspect_set_defaults(FLAC__OggEncoderAspect *aspect); +FLAC__bool FLAC__ogg_encoder_aspect_init(FLAC__OggEncoderAspect *aspect); +void FLAC__ogg_encoder_aspect_finish(FLAC__OggEncoderAspect *aspect); + +typedef FLAC__StreamEncoderWriteStatus (*FLAC__OggEncoderAspectWriteCallbackProxy)(const void *encoder, const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame, void *client_data); + +FLAC__StreamEncoderWriteStatus FLAC__ogg_encoder_aspect_write_callback_wrapper(FLAC__OggEncoderAspect *aspect, const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame, FLAC__bool is_last_block, FLAC__OggEncoderAspectWriteCallbackProxy write_callback, void *encoder, void *client_data); +#endif diff --git a/src/FLAC/src/libFLAC/include/private/ogg_helper.h b/src/FLAC/src/libFLAC/include/private/ogg_helper.h new file mode 100644 index 00000000..389be18a --- /dev/null +++ b/src/FLAC/src/libFLAC/include/private/ogg_helper.h @@ -0,0 +1,43 @@ +/* libFLAC - Free Lossless Audio Codec + * Copyright (C) 2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__OGG_HELPER_H +#define FLAC__PRIVATE__OGG_HELPER_H + +#include +#include "FLAC/stream_encoder.h" /* for FLAC__StreamEncoder */ + +void simple_ogg_page__init(ogg_page *page); +void simple_ogg_page__clear(ogg_page *page); +FLAC__bool simple_ogg_page__get_at(FLAC__StreamEncoder *encoder, FLAC__uint64 position, ogg_page *page, FLAC__StreamEncoderSeekCallback seek_callback, FLAC__StreamEncoderReadCallback read_callback, void *client_data); +FLAC__bool simple_ogg_page__set_at(FLAC__StreamEncoder *encoder, FLAC__uint64 position, ogg_page *page, FLAC__StreamEncoderSeekCallback seek_callback, FLAC__StreamEncoderWriteCallback write_callback, void *client_data); + +#endif diff --git a/src/FLAC/src/libFLAC/include/private/ogg_mapping.h b/src/FLAC/src/libFLAC/include/private/ogg_mapping.h new file mode 100644 index 00000000..07dd8b27 --- /dev/null +++ b/src/FLAC/src/libFLAC/include/private/ogg_mapping.h @@ -0,0 +1,63 @@ +/* libFLAC - Free Lossless Audio Codec + * Copyright (C) 2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__OGG_MAPPING_H +#define FLAC__PRIVATE__OGG_MAPPING_H + +#include "FLAC/ordinals.h" + +/** The length of the 'FLAC' magic in bytes. */ +#define FLAC__OGG_MAPPING_PACKET_TYPE_LENGTH (1u) + +extern const unsigned FLAC__OGG_MAPPING_PACKET_TYPE_LEN; /* = 8 bits */ + +extern const FLAC__byte FLAC__OGG_MAPPING_FIRST_HEADER_PACKET_TYPE; /* = 0x7f */ + +/** The length of the 'FLAC' magic in bytes. */ +#define FLAC__OGG_MAPPING_MAGIC_LENGTH (4u) + +extern const FLAC__byte * const FLAC__OGG_MAPPING_MAGIC; /* = "FLAC" */ + +extern const unsigned FLAC__OGG_MAPPING_VERSION_MAJOR_LEN; /* = 8 bits */ +extern const unsigned FLAC__OGG_MAPPING_VERSION_MINOR_LEN; /* = 8 bits */ + +/** The length of the Ogg FLAC mapping major version number in bytes. */ +#define FLAC__OGG_MAPPING_VERSION_MAJOR_LENGTH (1u) + +/** The length of the Ogg FLAC mapping minor version number in bytes. */ +#define FLAC__OGG_MAPPING_VERSION_MINOR_LENGTH (1u) + +extern const unsigned FLAC__OGG_MAPPING_NUM_HEADERS_LEN; /* = 16 bits */ + +/** The length of the #-of-header-packets number bytes. */ +#define FLAC__OGG_MAPPING_NUM_HEADERS_LENGTH (2u) + +#endif diff --git a/src/FLAC/src/libFLAC/include/private/stream_encoder_framing.h b/src/FLAC/src/libFLAC/include/private/stream_encoder_framing.h new file mode 100644 index 00000000..4865c16c --- /dev/null +++ b/src/FLAC/src/libFLAC/include/private/stream_encoder_framing.h @@ -0,0 +1,45 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__STREAM_ENCODER_FRAMING_H +#define FLAC__PRIVATE__STREAM_ENCODER_FRAMING_H + +#include "FLAC/format.h" +#include "bitwriter.h" + +FLAC__bool FLAC__add_metadata_block(const FLAC__StreamMetadata *metadata, FLAC__BitWriter *bw); +FLAC__bool FLAC__frame_add_header(const FLAC__FrameHeader *header, FLAC__BitWriter *bw); +FLAC__bool FLAC__subframe_add_constant(const FLAC__Subframe_Constant *subframe, unsigned subframe_bps, unsigned wasted_bits, FLAC__BitWriter *bw); +FLAC__bool FLAC__subframe_add_fixed(const FLAC__Subframe_Fixed *subframe, unsigned residual_samples, unsigned subframe_bps, unsigned wasted_bits, FLAC__BitWriter *bw); +FLAC__bool FLAC__subframe_add_lpc(const FLAC__Subframe_LPC *subframe, unsigned residual_samples, unsigned subframe_bps, unsigned wasted_bits, FLAC__BitWriter *bw); +FLAC__bool FLAC__subframe_add_verbatim(const FLAC__Subframe_Verbatim *subframe, unsigned samples, unsigned subframe_bps, unsigned wasted_bits, FLAC__BitWriter *bw); + +#endif diff --git a/src/FLAC/src/libFLAC/include/private/window.h b/src/FLAC/src/libFLAC/include/private/window.h new file mode 100644 index 00000000..01e0fc46 --- /dev/null +++ b/src/FLAC/src/libFLAC/include/private/window.h @@ -0,0 +1,71 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__WINDOW_H +#define FLAC__PRIVATE__WINDOW_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "private/float.h" +#include "FLAC/format.h" + +#ifndef FLAC__INTEGER_ONLY_LIBRARY + +/* + * FLAC__window_*() + * -------------------------------------------------------------------- + * Calculates window coefficients according to different apodization + * functions. + * + * OUT window[0,L-1] + * IN L (number of points in window) + */ +void FLAC__window_bartlett(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_bartlett_hann(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_blackman(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_blackman_harris_4term_92db_sidelobe(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_connes(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_flattop(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_gauss(FLAC__real *window, const FLAC__int32 L, const FLAC__real stddev); /* 0.0 < stddev <= 0.5 */ +void FLAC__window_hamming(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_hann(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_kaiser_bessel(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_nuttall(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_rectangle(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_triangle(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_tukey(FLAC__real *window, const FLAC__int32 L, const FLAC__real p); +void FLAC__window_welch(FLAC__real *window, const FLAC__int32 L); + +#endif /* !defined FLAC__INTEGER_ONLY_LIBRARY */ + +#endif diff --git a/src/FLAC/src/libFLAC/include/protected/Makefile.am b/src/FLAC/src/libFLAC/include/protected/Makefile.am new file mode 100644 index 00000000..66c697c8 --- /dev/null +++ b/src/FLAC/src/libFLAC/include/protected/Makefile.am @@ -0,0 +1,34 @@ +# libFLAC - Free Lossless Audio Codec library +# Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# - Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# - Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# - Neither the name of the Xiph.org Foundation nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +noinst_HEADERS = \ + all.h \ + stream_decoder.h \ + stream_encoder.h diff --git a/src/FLAC/src/libFLAC/include/protected/all.h b/src/FLAC/src/libFLAC/include/protected/all.h new file mode 100644 index 00000000..2921092b --- /dev/null +++ b/src/FLAC/src/libFLAC/include/protected/all.h @@ -0,0 +1,38 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PROTECTED__ALL_H +#define FLAC__PROTECTED__ALL_H + +#include "stream_decoder.h" +#include "stream_encoder.h" + +#endif diff --git a/src/FLAC/src/libFLAC/include/protected/stream_decoder.h b/src/FLAC/src/libFLAC/include/protected/stream_decoder.h new file mode 100644 index 00000000..9108ca78 --- /dev/null +++ b/src/FLAC/src/libFLAC/include/protected/stream_decoder.h @@ -0,0 +1,58 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PROTECTED__STREAM_DECODER_H +#define FLAC__PROTECTED__STREAM_DECODER_H + +#include "FLAC/stream_decoder.h" +#if FLAC__HAS_OGG +#include "private/ogg_decoder_aspect.h" +#endif + +typedef struct FLAC__StreamDecoderProtected { + FLAC__StreamDecoderState state; + unsigned channels; + FLAC__ChannelAssignment channel_assignment; + unsigned bits_per_sample; + unsigned sample_rate; /* in Hz */ + unsigned blocksize; /* in samples (per channel) */ + FLAC__bool md5_checking; /* if true, generate MD5 signature of decoded data and compare against signature in the STREAMINFO metadata block */ +#if FLAC__HAS_OGG + FLAC__OggDecoderAspect ogg_decoder_aspect; +#endif +} FLAC__StreamDecoderProtected; + +/* + * return the number of input bytes consumed + */ +unsigned FLAC__stream_decoder_get_input_bytes_unconsumed(const FLAC__StreamDecoder *decoder); + +#endif diff --git a/src/FLAC/src/libFLAC/include/protected/stream_encoder.h b/src/FLAC/src/libFLAC/include/protected/stream_encoder.h new file mode 100644 index 00000000..e299916d --- /dev/null +++ b/src/FLAC/src/libFLAC/include/protected/stream_encoder.h @@ -0,0 +1,109 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PROTECTED__STREAM_ENCODER_H +#define FLAC__PROTECTED__STREAM_ENCODER_H + +#include "FLAC/stream_encoder.h" +#if FLAC__HAS_OGG +#include "private/ogg_encoder_aspect.h" +#endif + +#ifndef FLAC__INTEGER_ONLY_LIBRARY + +#include "private/float.h" + +#define FLAC__MAX_APODIZATION_FUNCTIONS 32 + +typedef enum { + FLAC__APODIZATION_BARTLETT, + FLAC__APODIZATION_BARTLETT_HANN, + FLAC__APODIZATION_BLACKMAN, + FLAC__APODIZATION_BLACKMAN_HARRIS_4TERM_92DB_SIDELOBE, + FLAC__APODIZATION_CONNES, + FLAC__APODIZATION_FLATTOP, + FLAC__APODIZATION_GAUSS, + FLAC__APODIZATION_HAMMING, + FLAC__APODIZATION_HANN, + FLAC__APODIZATION_KAISER_BESSEL, + FLAC__APODIZATION_NUTTALL, + FLAC__APODIZATION_RECTANGLE, + FLAC__APODIZATION_TRIANGLE, + FLAC__APODIZATION_TUKEY, + FLAC__APODIZATION_WELCH +} FLAC__ApodizationFunction; + +typedef struct { + FLAC__ApodizationFunction type; + union { + struct { + FLAC__real stddev; + } gauss; + struct { + FLAC__real p; + } tukey; + } parameters; +} FLAC__ApodizationSpecification; + +#endif // #ifndef FLAC__INTEGER_ONLY_LIBRARY + +typedef struct FLAC__StreamEncoderProtected { + FLAC__StreamEncoderState state; + FLAC__bool verify; + FLAC__bool streamable_subset; + FLAC__bool do_mid_side_stereo; + FLAC__bool loose_mid_side_stereo; + unsigned channels; + unsigned bits_per_sample; + unsigned sample_rate; + unsigned blocksize; +#ifndef FLAC__INTEGER_ONLY_LIBRARY + unsigned num_apodizations; + FLAC__ApodizationSpecification apodizations[FLAC__MAX_APODIZATION_FUNCTIONS]; +#endif + unsigned max_lpc_order; + unsigned qlp_coeff_precision; + FLAC__bool do_qlp_coeff_prec_search; + FLAC__bool do_exhaustive_model_search; + FLAC__bool do_escape_coding; + unsigned min_residual_partition_order; + unsigned max_residual_partition_order; + unsigned rice_parameter_search_dist; + FLAC__uint64 total_samples_estimate; + FLAC__StreamMetadata **metadata; + unsigned num_metadata_blocks; + FLAC__uint64 streaminfo_offset, seektable_offset, audio_offset; +#if FLAC__HAS_OGG + FLAC__OggEncoderAspect ogg_encoder_aspect; +#endif +} FLAC__StreamEncoderProtected; + +#endif diff --git a/src/FLAC/src/libFLAC/libFLAC.m4 b/src/FLAC/src/libFLAC/libFLAC.m4 new file mode 100644 index 00000000..ee589e2a --- /dev/null +++ b/src/FLAC/src/libFLAC/libFLAC.m4 @@ -0,0 +1,114 @@ +# Configure paths for libFLAC +# "Inspired" by ogg.m4 + +dnl AM_PATH_LIBFLAC([ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]) +dnl Test for libFLAC, and define LIBFLAC_CFLAGS, LIBFLAC_LIBS, LIBFLAC_LIBDIR +dnl +AC_DEFUN([AM_PATH_LIBFLAC], +[dnl +dnl Get the cflags and libraries +dnl +AC_ARG_WITH(libFLAC,[ --with-libFLAC=PFX Prefix where libFLAC is installed (optional)], libFLAC_prefix="$withval", libFLAC_prefix="") +AC_ARG_WITH(libFLAC-libraries,[ --with-libFLAC-libraries=DIR Directory where libFLAC library is installed (optional)], libFLAC_libraries="$withval", libFLAC_libraries="") +AC_ARG_WITH(libFLAC-includes,[ --with-libFLAC-includes=DIR Directory where libFLAC header files are installed (optional)], libFLAC_includes="$withval", libFLAC_includes="") +AC_ARG_ENABLE(libFLACtest, [ --disable-libFLACtest Do not try to compile and run a test libFLAC program],, enable_libFLACtest=yes) + + if test "x$libFLAC_libraries" != "x" ; then + LIBFLAC_LIBDIR="$libFLAC_libraries" + elif test "x$libFLAC_prefix" != "x" ; then + LIBFLAC_LIBDIR="$libFLAC_prefix/lib" + elif test "x$prefix" != "xNONE" ; then + LIBFLAC_LIBDIR="$libdir" + fi + + LIBFLAC_LIBS="-L$LIBFLAC_LIBDIR -lFLAC $OGG_LIBS -lm" + + if test "x$libFLAC_includes" != "x" ; then + LIBFLAC_CFLAGS="-I$libFLAC_includes" + elif test "x$libFLAC_prefix" != "x" ; then + LIBFLAC_CFLAGS="-I$libFLAC_prefix/include" + elif test "$prefix" != "xNONE"; then + LIBFLAC_CFLAGS="" + fi + + AC_MSG_CHECKING(for libFLAC) + no_libFLAC="" + + + if test "x$enable_libFLACtest" = "xyes" ; then + ac_save_CFLAGS="$CFLAGS" + ac_save_CXXFLAGS="$CXXFLAGS" + ac_save_LIBS="$LIBS" + ac_save_LDPATH="$LD_LIBRARY_PATH" + CFLAGS="$CFLAGS $LIBFLAC_CFLAGS" + CXXFLAGS="$CXXFLAGS $LIBFLAC_CFLAGS" + LIBS="$LIBS $LIBFLAC_LIBS" + LD_LIBRARY_PATH="$LIBFLAC_LIBDIR:$LD_LIBRARY_PATH" +dnl +dnl Now check if the installed libFLAC is sufficiently new. +dnl + rm -f conf.libFLACtest + AC_TRY_RUN([ +#include +#include +#include +#include + +int main () +{ + system("touch conf.libFLACtest"); + return 0; +} + +],, no_libFLAC=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) + CFLAGS="$ac_save_CFLAGS" + CXXFLAGS="$ac_save_CXXFLAGS" + LIBS="$ac_save_LIBS" + LD_LIBRARY_PATH="$ac_save_LD_LIBRARY_PATH" + fi + + if test "x$no_libFLAC" = "x" ; then + AC_MSG_RESULT(yes) + ifelse([$1], , :, [$1]) + else + AC_MSG_RESULT(no) + if test -f conf.libFLACtest ; then + : + else + echo "*** Could not run libFLAC test program, checking why..." + CFLAGS="$CFLAGS $LIBFLAC_CFLAGS" + CXXFLAGS="$CXXFLAGS $LIBFLAC_CFLAGS" + LIBS="$LIBS $LIBFLAC_LIBS" + LD_LIBRARY_PATH="$LIBFLAC_LIBDIR:$LD_LIBRARY_PATH" + AC_TRY_LINK([ +#include +#include +], [ return 0; ], + [ echo "*** The test program compiled, but did not run. This usually means" + echo "*** that the run-time linker is not finding libFLAC or finding the wrong" + echo "*** version of libFLAC. If it is not finding libFLAC, you'll need to set your" + echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" + echo "*** to the installed location Also, make sure you have run ldconfig if that" + echo "*** is required on your system" + echo "***" + echo "*** If you have an old version installed, it is best to remove it, although" + echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"], + [ echo "*** The test program failed to compile or link. See the file config.log for the" + echo "*** exact error that occured. This usually means libFLAC was incorrectly installed" + echo "*** or that you have moved libFLAC since it was installed. In the latter case, you" + echo "*** may want to edit the libFLAC-config script: $LIBFLAC_CONFIG" ]) + CFLAGS="$ac_save_CFLAGS" + CXXFLAGS="$ac_save_CXXFLAGS" + LIBS="$ac_save_LIBS" + LD_LIBRARY_PATH="$ac_save_LD_LIBRARY_PATH" + fi + LIBFLAC_CFLAGS="" + LIBFLAC_LIBDIR="" + LIBFLAC_LIBS="" + ifelse([$2], , :, [$2]) + fi + AC_SUBST(LIBFLAC_CFLAGS) + AC_SUBST(LIBFLAC_LIBDIR) + AC_SUBST(LIBFLAC_LIBS) + rm -f conf.libFLACtest +]) diff --git a/src/FLAC/src/libFLAC/lpc.c b/src/FLAC/src/libFLAC/lpc.c new file mode 100644 index 00000000..37dcdf8f --- /dev/null +++ b/src/FLAC/src/libFLAC/lpc.c @@ -0,0 +1,1373 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include "FLAC/assert.h" +#include "FLAC/format.h" +#include "private/bitmath.h" +#include "private/lpc.h" +#if defined DEBUG || defined FLAC__OVERFLOW_DETECT || defined FLAC__OVERFLOW_DETECT_VERBOSE +#include +#endif + +#ifndef FLAC__INTEGER_ONLY_LIBRARY + +#ifndef M_LN2 +/* math.h in VC++ doesn't seem to have this (how Microsoft is that?) */ +#define M_LN2 0.69314718055994530942 +#endif + +void FLAC__lpc_window_data(const FLAC__int32 in[], const FLAC__real window[], FLAC__real out[], unsigned data_len) +{ + unsigned i; + for(i = 0; i < data_len; i++) + out[i] = in[i] * window[i]; +} + +void FLAC__lpc_compute_autocorrelation(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]) +{ + /* a readable, but slower, version */ +#if 0 + FLAC__real d; + unsigned i; + + FLAC__ASSERT(lag > 0); + FLAC__ASSERT(lag <= data_len); + + /* + * Technically we should subtract the mean first like so: + * for(i = 0; i < data_len; i++) + * data[i] -= mean; + * but it appears not to make enough of a difference to matter, and + * most signals are already closely centered around zero + */ + while(lag--) { + for(i = lag, d = 0.0; i < data_len; i++) + d += data[i] * data[i - lag]; + autoc[lag] = d; + } +#endif + + /* + * this version tends to run faster because of better data locality + * ('data_len' is usually much larger than 'lag') + */ + FLAC__real d; + unsigned sample, coeff; + const unsigned limit = data_len - lag; + + FLAC__ASSERT(lag > 0); + FLAC__ASSERT(lag <= data_len); + + for(coeff = 0; coeff < lag; coeff++) + autoc[coeff] = 0.0; + for(sample = 0; sample <= limit; sample++) { + d = data[sample]; + for(coeff = 0; coeff < lag; coeff++) + autoc[coeff] += d * data[sample+coeff]; + } + for(; sample < data_len; sample++) { + d = data[sample]; + for(coeff = 0; coeff < data_len - sample; coeff++) + autoc[coeff] += d * data[sample+coeff]; + } +} + +void FLAC__lpc_compute_lp_coefficients(const FLAC__real autoc[], unsigned *max_order, FLAC__real lp_coeff[][FLAC__MAX_LPC_ORDER], FLAC__double error[]) +{ + unsigned i, j; + FLAC__double r, err, ref[FLAC__MAX_LPC_ORDER], lpc[FLAC__MAX_LPC_ORDER]; + + FLAC__ASSERT(0 != max_order); + FLAC__ASSERT(0 < *max_order); + FLAC__ASSERT(*max_order <= FLAC__MAX_LPC_ORDER); + FLAC__ASSERT(autoc[0] != 0.0); + + err = autoc[0]; + + for(i = 0; i < *max_order; i++) { + /* Sum up this iteration's reflection coefficient. */ + r = -autoc[i+1]; + for(j = 0; j < i; j++) + r -= lpc[j] * autoc[i-j]; + ref[i] = (r/=err); + + /* Update LPC coefficients and total error. */ + lpc[i]=r; + for(j = 0; j < (i>>1); j++) { + FLAC__double tmp = lpc[j]; + lpc[j] += r * lpc[i-1-j]; + lpc[i-1-j] += r * tmp; + } + if(i & 1) + lpc[j] += lpc[j] * r; + + err *= (1.0 - r * r); + + /* save this order */ + for(j = 0; j <= i; j++) + lp_coeff[i][j] = (FLAC__real)(-lpc[j]); /* negate FIR filter coeff to get predictor coeff */ + error[i] = err; + + /* see SF bug #1601812 http://sourceforge.net/tracker/index.php?func=detail&aid=1601812&group_id=13478&atid=113478 */ + if(err == 0.0) { + *max_order = i+1; + return; + } + } +} + +int FLAC__lpc_quantize_coefficients(const FLAC__real lp_coeff[], unsigned order, unsigned precision, FLAC__int32 qlp_coeff[], int *shift) +{ + unsigned i; + FLAC__double cmax; + FLAC__int32 qmax, qmin; + + FLAC__ASSERT(precision > 0); + FLAC__ASSERT(precision >= FLAC__MIN_QLP_COEFF_PRECISION); + + /* drop one bit for the sign; from here on out we consider only |lp_coeff[i]| */ + precision--; + qmax = 1 << precision; + qmin = -qmax; + qmax--; + + /* calc cmax = max( |lp_coeff[i]| ) */ + cmax = 0.0; + for(i = 0; i < order; i++) { + const FLAC__double d = fabs(lp_coeff[i]); + if(d > cmax) + cmax = d; + } + + if(cmax <= 0.0) { + /* => coefficients are all 0, which means our constant-detect didn't work */ + return 2; + } + else { + const int max_shiftlimit = (1 << (FLAC__SUBFRAME_LPC_QLP_SHIFT_LEN-1)) - 1; + const int min_shiftlimit = -max_shiftlimit - 1; + int log2cmax; + + (void)frexp(cmax, &log2cmax); + log2cmax--; + *shift = (int)precision - log2cmax - 1; + + if(*shift > max_shiftlimit) + *shift = max_shiftlimit; + else if(*shift < min_shiftlimit) + return 1; + } + + if(*shift >= 0) { + FLAC__double error = 0.0; + FLAC__int32 q; + for(i = 0; i < order; i++) { + error += lp_coeff[i] * (1 << *shift); +#if 1 /* unfortunately lround() is C99 */ + if(error >= 0.0) + q = (FLAC__int32)(error + 0.5); + else + q = (FLAC__int32)(error - 0.5); +#else + q = lround(error); +#endif +#ifdef FLAC__OVERFLOW_DETECT + if(q > qmax+1) /* we expect q==qmax+1 occasionally due to rounding */ + fprintf(stderr,"FLAC__lpc_quantize_coefficients: quantizer overflow: q>qmax %d>%d shift=%d cmax=%f precision=%u lpc[%u]=%f\n",q,qmax,*shift,cmax,precision+1,i,lp_coeff[i]); + else if(q < qmin) + fprintf(stderr,"FLAC__lpc_quantize_coefficients: quantizer overflow: q qmax) + q = qmax; + else if(q < qmin) + q = qmin; + error -= q; + qlp_coeff[i] = q; + } + } + /* negative shift is very rare but due to design flaw, negative shift is + * a NOP in the decoder, so it must be handled specially by scaling down + * coeffs + */ + else { + const int nshift = -(*shift); + FLAC__double error = 0.0; + FLAC__int32 q; +#ifdef DEBUG + fprintf(stderr,"FLAC__lpc_quantize_coefficients: negative shift=%d order=%u cmax=%f\n", *shift, order, cmax); +#endif + for(i = 0; i < order; i++) { + error += lp_coeff[i] / (1 << nshift); +#if 1 /* unfortunately lround() is C99 */ + if(error >= 0.0) + q = (FLAC__int32)(error + 0.5); + else + q = (FLAC__int32)(error - 0.5); +#else + q = lround(error); +#endif +#ifdef FLAC__OVERFLOW_DETECT + if(q > qmax+1) /* we expect q==qmax+1 occasionally due to rounding */ + fprintf(stderr,"FLAC__lpc_quantize_coefficients: quantizer overflow: q>qmax %d>%d shift=%d cmax=%f precision=%u lpc[%u]=%f\n",q,qmax,*shift,cmax,precision+1,i,lp_coeff[i]); + else if(q < qmin) + fprintf(stderr,"FLAC__lpc_quantize_coefficients: quantizer overflow: q qmax) + q = qmax; + else if(q < qmin) + q = qmin; + error -= q; + qlp_coeff[i] = q; + } + *shift = 0; + } + + return 0; +} + +void FLAC__lpc_compute_residual_from_qlp_coefficients(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]) +#ifdef FLAC__OVERFLOW_DETECT /* this ugly flavor is only for debugging */ +{ + FLAC__int64 sumo; + unsigned i, j; + FLAC__int32 sum; + const FLAC__int32 *history; + +#ifdef FLAC__OVERFLOW_DETECT_VERBOSE + fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients: data_len=%d, order=%u, lpq=%d",data_len,order,lp_quantization); + for(i=0;i 0); + + for(i = 0; i < data_len; i++) { + sumo = 0; + sum = 0; + history = data; + for(j = 0; j < order; j++) { + sum += qlp_coeff[j] * (*(--history)); + sumo += (FLAC__int64)qlp_coeff[j] * (FLAC__int64)(*history); +#if defined _MSC_VER + if(sumo > 2147483647I64 || sumo < -2147483648I64) + fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients: OVERFLOW, i=%u, j=%u, c=%d, d=%d, sumo=%I64d\n",i,j,qlp_coeff[j],*history,sumo); +#else + if(sumo > 2147483647ll || sumo < -2147483648ll) + fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients: OVERFLOW, i=%u, j=%u, c=%d, d=%d, sumo=%lld\n",i,j,qlp_coeff[j],*history,(long long)sumo); +#endif + } + *(residual++) = *(data++) - (sum >> lp_quantization); + } + + /* Here's a slower but clearer version: + for(i = 0; i < data_len; i++) { + sum = 0; + for(j = 0; j < order; j++) + sum += qlp_coeff[j] * data[i-j-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + */ +} +#else /* fully unrolled version for normal use */ +{ + unsigned i; + FLAC__int32 sum; + + FLAC__ASSERT(order > 0); + FLAC__ASSERT(order <= 32); + + /* + * We do unique versions up to 12th order since that's the subset limit. + * Also they are roughly ordered to match frequency of occurrence to + * minimize branching. + */ + if(order <= 12) { + if(order > 8) { + if(order > 10) { + if(order == 12) { + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[11] * data[i-12]; + sum += qlp_coeff[10] * data[i-11]; + sum += qlp_coeff[9] * data[i-10]; + sum += qlp_coeff[8] * data[i-9]; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + else { /* order == 11 */ + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[10] * data[i-11]; + sum += qlp_coeff[9] * data[i-10]; + sum += qlp_coeff[8] * data[i-9]; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + } + else { + if(order == 10) { + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[9] * data[i-10]; + sum += qlp_coeff[8] * data[i-9]; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + else { /* order == 9 */ + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[8] * data[i-9]; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + } + } + else if(order > 4) { + if(order > 6) { + if(order == 8) { + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + else { /* order == 7 */ + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + } + else { + if(order == 6) { + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + else { /* order == 5 */ + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + } + } + else { + if(order > 2) { + if(order == 4) { + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + else { /* order == 3 */ + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + } + else { + if(order == 2) { + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + else { /* order == 1 */ + for(i = 0; i < data_len; i++) + residual[i] = data[i] - ((qlp_coeff[0] * data[i-1]) >> lp_quantization); + } + } + } + } + else { /* order > 12 */ + for(i = 0; i < data_len; i++) { + sum = 0; + switch(order) { + case 32: sum += qlp_coeff[31] * data[i-32]; + case 31: sum += qlp_coeff[30] * data[i-31]; + case 30: sum += qlp_coeff[29] * data[i-30]; + case 29: sum += qlp_coeff[28] * data[i-29]; + case 28: sum += qlp_coeff[27] * data[i-28]; + case 27: sum += qlp_coeff[26] * data[i-27]; + case 26: sum += qlp_coeff[25] * data[i-26]; + case 25: sum += qlp_coeff[24] * data[i-25]; + case 24: sum += qlp_coeff[23] * data[i-24]; + case 23: sum += qlp_coeff[22] * data[i-23]; + case 22: sum += qlp_coeff[21] * data[i-22]; + case 21: sum += qlp_coeff[20] * data[i-21]; + case 20: sum += qlp_coeff[19] * data[i-20]; + case 19: sum += qlp_coeff[18] * data[i-19]; + case 18: sum += qlp_coeff[17] * data[i-18]; + case 17: sum += qlp_coeff[16] * data[i-17]; + case 16: sum += qlp_coeff[15] * data[i-16]; + case 15: sum += qlp_coeff[14] * data[i-15]; + case 14: sum += qlp_coeff[13] * data[i-14]; + case 13: sum += qlp_coeff[12] * data[i-13]; + sum += qlp_coeff[11] * data[i-12]; + sum += qlp_coeff[10] * data[i-11]; + sum += qlp_coeff[ 9] * data[i-10]; + sum += qlp_coeff[ 8] * data[i- 9]; + sum += qlp_coeff[ 7] * data[i- 8]; + sum += qlp_coeff[ 6] * data[i- 7]; + sum += qlp_coeff[ 5] * data[i- 6]; + sum += qlp_coeff[ 4] * data[i- 5]; + sum += qlp_coeff[ 3] * data[i- 4]; + sum += qlp_coeff[ 2] * data[i- 3]; + sum += qlp_coeff[ 1] * data[i- 2]; + sum += qlp_coeff[ 0] * data[i- 1]; + } + residual[i] = data[i] - (sum >> lp_quantization); + } + } +} +#endif + +void FLAC__lpc_compute_residual_from_qlp_coefficients_wide(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]) +#ifdef FLAC__OVERFLOW_DETECT /* this ugly flavor is only for debugging */ +{ + unsigned i, j; + FLAC__int64 sum; + const FLAC__int32 *history; + +#ifdef FLAC__OVERFLOW_DETECT_VERBOSE + fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients_wide: data_len=%d, order=%u, lpq=%d",data_len,order,lp_quantization); + for(i=0;i 0); + + for(i = 0; i < data_len; i++) { + sum = 0; + history = data; + for(j = 0; j < order; j++) + sum += (FLAC__int64)qlp_coeff[j] * (FLAC__int64)(*(--history)); + if(FLAC__bitmath_silog2_wide(sum >> lp_quantization) > 32) { +#if defined _MSC_VER + fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients_wide: OVERFLOW, i=%u, sum=%I64d\n", i, sum >> lp_quantization); +#else + fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients_wide: OVERFLOW, i=%u, sum=%lld\n", i, (long long)(sum >> lp_quantization)); +#endif + break; + } + if(FLAC__bitmath_silog2_wide((FLAC__int64)(*data) - (sum >> lp_quantization)) > 32) { +#if defined _MSC_VER + fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients_wide: OVERFLOW, i=%u, data=%d, sum=%I64d, residual=%I64d\n", i, *data, sum >> lp_quantization, (FLAC__int64)(*data) - (sum >> lp_quantization)); +#else + fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients_wide: OVERFLOW, i=%u, data=%d, sum=%lld, residual=%lld\n", i, *data, (long long)(sum >> lp_quantization), (long long)((FLAC__int64)(*data) - (sum >> lp_quantization))); +#endif + break; + } + *(residual++) = *(data++) - (FLAC__int32)(sum >> lp_quantization); + } +} +#else /* fully unrolled version for normal use */ +{ + unsigned i; + FLAC__int64 sum; + + FLAC__ASSERT(order > 0); + FLAC__ASSERT(order <= 32); + + /* + * We do unique versions up to 12th order since that's the subset limit. + * Also they are roughly ordered to match frequency of occurrence to + * minimize branching. + */ + if(order <= 12) { + if(order > 8) { + if(order > 10) { + if(order == 12) { + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[11] * (FLAC__int64)data[i-12]; + sum += qlp_coeff[10] * (FLAC__int64)data[i-11]; + sum += qlp_coeff[9] * (FLAC__int64)data[i-10]; + sum += qlp_coeff[8] * (FLAC__int64)data[i-9]; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 11 */ + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[10] * (FLAC__int64)data[i-11]; + sum += qlp_coeff[9] * (FLAC__int64)data[i-10]; + sum += qlp_coeff[8] * (FLAC__int64)data[i-9]; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + } + else { + if(order == 10) { + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[9] * (FLAC__int64)data[i-10]; + sum += qlp_coeff[8] * (FLAC__int64)data[i-9]; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 9 */ + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[8] * (FLAC__int64)data[i-9]; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + } + } + else if(order > 4) { + if(order > 6) { + if(order == 8) { + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 7 */ + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + } + else { + if(order == 6) { + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 5 */ + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + } + } + else { + if(order > 2) { + if(order == 4) { + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 3 */ + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + } + else { + if(order == 2) { + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 1 */ + for(i = 0; i < data_len; i++) + residual[i] = data[i] - (FLAC__int32)((qlp_coeff[0] * (FLAC__int64)data[i-1]) >> lp_quantization); + } + } + } + } + else { /* order > 12 */ + for(i = 0; i < data_len; i++) { + sum = 0; + switch(order) { + case 32: sum += qlp_coeff[31] * (FLAC__int64)data[i-32]; + case 31: sum += qlp_coeff[30] * (FLAC__int64)data[i-31]; + case 30: sum += qlp_coeff[29] * (FLAC__int64)data[i-30]; + case 29: sum += qlp_coeff[28] * (FLAC__int64)data[i-29]; + case 28: sum += qlp_coeff[27] * (FLAC__int64)data[i-28]; + case 27: sum += qlp_coeff[26] * (FLAC__int64)data[i-27]; + case 26: sum += qlp_coeff[25] * (FLAC__int64)data[i-26]; + case 25: sum += qlp_coeff[24] * (FLAC__int64)data[i-25]; + case 24: sum += qlp_coeff[23] * (FLAC__int64)data[i-24]; + case 23: sum += qlp_coeff[22] * (FLAC__int64)data[i-23]; + case 22: sum += qlp_coeff[21] * (FLAC__int64)data[i-22]; + case 21: sum += qlp_coeff[20] * (FLAC__int64)data[i-21]; + case 20: sum += qlp_coeff[19] * (FLAC__int64)data[i-20]; + case 19: sum += qlp_coeff[18] * (FLAC__int64)data[i-19]; + case 18: sum += qlp_coeff[17] * (FLAC__int64)data[i-18]; + case 17: sum += qlp_coeff[16] * (FLAC__int64)data[i-17]; + case 16: sum += qlp_coeff[15] * (FLAC__int64)data[i-16]; + case 15: sum += qlp_coeff[14] * (FLAC__int64)data[i-15]; + case 14: sum += qlp_coeff[13] * (FLAC__int64)data[i-14]; + case 13: sum += qlp_coeff[12] * (FLAC__int64)data[i-13]; + sum += qlp_coeff[11] * (FLAC__int64)data[i-12]; + sum += qlp_coeff[10] * (FLAC__int64)data[i-11]; + sum += qlp_coeff[ 9] * (FLAC__int64)data[i-10]; + sum += qlp_coeff[ 8] * (FLAC__int64)data[i- 9]; + sum += qlp_coeff[ 7] * (FLAC__int64)data[i- 8]; + sum += qlp_coeff[ 6] * (FLAC__int64)data[i- 7]; + sum += qlp_coeff[ 5] * (FLAC__int64)data[i- 6]; + sum += qlp_coeff[ 4] * (FLAC__int64)data[i- 5]; + sum += qlp_coeff[ 3] * (FLAC__int64)data[i- 4]; + sum += qlp_coeff[ 2] * (FLAC__int64)data[i- 3]; + sum += qlp_coeff[ 1] * (FLAC__int64)data[i- 2]; + sum += qlp_coeff[ 0] * (FLAC__int64)data[i- 1]; + } + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } +} +#endif + +#endif /* !defined FLAC__INTEGER_ONLY_LIBRARY */ + +void FLAC__lpc_restore_signal(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]) +#ifdef FLAC__OVERFLOW_DETECT /* this ugly flavor is only for debugging */ +{ + FLAC__int64 sumo; + unsigned i, j; + FLAC__int32 sum; + const FLAC__int32 *r = residual, *history; + +#ifdef FLAC__OVERFLOW_DETECT_VERBOSE + fprintf(stderr,"FLAC__lpc_restore_signal: data_len=%d, order=%u, lpq=%d",data_len,order,lp_quantization); + for(i=0;i 0); + + for(i = 0; i < data_len; i++) { + sumo = 0; + sum = 0; + history = data; + for(j = 0; j < order; j++) { + sum += qlp_coeff[j] * (*(--history)); + sumo += (FLAC__int64)qlp_coeff[j] * (FLAC__int64)(*history); +#if defined _MSC_VER + if(sumo > 2147483647I64 || sumo < -2147483648I64) + fprintf(stderr,"FLAC__lpc_restore_signal: OVERFLOW, i=%u, j=%u, c=%d, d=%d, sumo=%I64d\n",i,j,qlp_coeff[j],*history,sumo); +#else + if(sumo > 2147483647ll || sumo < -2147483648ll) + fprintf(stderr,"FLAC__lpc_restore_signal: OVERFLOW, i=%u, j=%u, c=%d, d=%d, sumo=%lld\n",i,j,qlp_coeff[j],*history,(long long)sumo); +#endif + } + *(data++) = *(r++) + (sum >> lp_quantization); + } + + /* Here's a slower but clearer version: + for(i = 0; i < data_len; i++) { + sum = 0; + for(j = 0; j < order; j++) + sum += qlp_coeff[j] * data[i-j-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + */ +} +#else /* fully unrolled version for normal use */ +{ + unsigned i; + FLAC__int32 sum; + + FLAC__ASSERT(order > 0); + FLAC__ASSERT(order <= 32); + + /* + * We do unique versions up to 12th order since that's the subset limit. + * Also they are roughly ordered to match frequency of occurrence to + * minimize branching. + */ + if(order <= 12) { + if(order > 8) { + if(order > 10) { + if(order == 12) { + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[11] * data[i-12]; + sum += qlp_coeff[10] * data[i-11]; + sum += qlp_coeff[9] * data[i-10]; + sum += qlp_coeff[8] * data[i-9]; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + else { /* order == 11 */ + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[10] * data[i-11]; + sum += qlp_coeff[9] * data[i-10]; + sum += qlp_coeff[8] * data[i-9]; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + } + else { + if(order == 10) { + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[9] * data[i-10]; + sum += qlp_coeff[8] * data[i-9]; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + else { /* order == 9 */ + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[8] * data[i-9]; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + } + } + else if(order > 4) { + if(order > 6) { + if(order == 8) { + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + else { /* order == 7 */ + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + } + else { + if(order == 6) { + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + else { /* order == 5 */ + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + } + } + else { + if(order > 2) { + if(order == 4) { + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + else { /* order == 3 */ + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + } + else { + if(order == 2) { + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + else { /* order == 1 */ + for(i = 0; i < data_len; i++) + data[i] = residual[i] + ((qlp_coeff[0] * data[i-1]) >> lp_quantization); + } + } + } + } + else { /* order > 12 */ + for(i = 0; i < data_len; i++) { + sum = 0; + switch(order) { + case 32: sum += qlp_coeff[31] * data[i-32]; + case 31: sum += qlp_coeff[30] * data[i-31]; + case 30: sum += qlp_coeff[29] * data[i-30]; + case 29: sum += qlp_coeff[28] * data[i-29]; + case 28: sum += qlp_coeff[27] * data[i-28]; + case 27: sum += qlp_coeff[26] * data[i-27]; + case 26: sum += qlp_coeff[25] * data[i-26]; + case 25: sum += qlp_coeff[24] * data[i-25]; + case 24: sum += qlp_coeff[23] * data[i-24]; + case 23: sum += qlp_coeff[22] * data[i-23]; + case 22: sum += qlp_coeff[21] * data[i-22]; + case 21: sum += qlp_coeff[20] * data[i-21]; + case 20: sum += qlp_coeff[19] * data[i-20]; + case 19: sum += qlp_coeff[18] * data[i-19]; + case 18: sum += qlp_coeff[17] * data[i-18]; + case 17: sum += qlp_coeff[16] * data[i-17]; + case 16: sum += qlp_coeff[15] * data[i-16]; + case 15: sum += qlp_coeff[14] * data[i-15]; + case 14: sum += qlp_coeff[13] * data[i-14]; + case 13: sum += qlp_coeff[12] * data[i-13]; + sum += qlp_coeff[11] * data[i-12]; + sum += qlp_coeff[10] * data[i-11]; + sum += qlp_coeff[ 9] * data[i-10]; + sum += qlp_coeff[ 8] * data[i- 9]; + sum += qlp_coeff[ 7] * data[i- 8]; + sum += qlp_coeff[ 6] * data[i- 7]; + sum += qlp_coeff[ 5] * data[i- 6]; + sum += qlp_coeff[ 4] * data[i- 5]; + sum += qlp_coeff[ 3] * data[i- 4]; + sum += qlp_coeff[ 2] * data[i- 3]; + sum += qlp_coeff[ 1] * data[i- 2]; + sum += qlp_coeff[ 0] * data[i- 1]; + } + data[i] = residual[i] + (sum >> lp_quantization); + } + } +} +#endif + +void FLAC__lpc_restore_signal_wide(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]) +#ifdef FLAC__OVERFLOW_DETECT /* this ugly flavor is only for debugging */ +{ + unsigned i, j; + FLAC__int64 sum; + const FLAC__int32 *r = residual, *history; + +#ifdef FLAC__OVERFLOW_DETECT_VERBOSE + fprintf(stderr,"FLAC__lpc_restore_signal_wide: data_len=%d, order=%u, lpq=%d",data_len,order,lp_quantization); + for(i=0;i 0); + + for(i = 0; i < data_len; i++) { + sum = 0; + history = data; + for(j = 0; j < order; j++) + sum += (FLAC__int64)qlp_coeff[j] * (FLAC__int64)(*(--history)); + if(FLAC__bitmath_silog2_wide(sum >> lp_quantization) > 32) { +#ifdef _MSC_VER + fprintf(stderr,"FLAC__lpc_restore_signal_wide: OVERFLOW, i=%u, sum=%I64d\n", i, sum >> lp_quantization); +#else + fprintf(stderr,"FLAC__lpc_restore_signal_wide: OVERFLOW, i=%u, sum=%lld\n", i, (long long)(sum >> lp_quantization)); +#endif + break; + } + if(FLAC__bitmath_silog2_wide((FLAC__int64)(*r) + (sum >> lp_quantization)) > 32) { +#ifdef _MSC_VER + fprintf(stderr,"FLAC__lpc_restore_signal_wide: OVERFLOW, i=%u, residual=%d, sum=%I64d, data=%I64d\n", i, *r, sum >> lp_quantization, (FLAC__int64)(*r) + (sum >> lp_quantization)); +#else + fprintf(stderr,"FLAC__lpc_restore_signal_wide: OVERFLOW, i=%u, residual=%d, sum=%lld, data=%lld\n", i, *r, (long long)(sum >> lp_quantization), (long long)((FLAC__int64)(*r) + (sum >> lp_quantization))); +#endif + break; + } + *(data++) = *(r++) + (FLAC__int32)(sum >> lp_quantization); + } +} +#else /* fully unrolled version for normal use */ +{ + unsigned i; + FLAC__int64 sum; + + FLAC__ASSERT(order > 0); + FLAC__ASSERT(order <= 32); + + /* + * We do unique versions up to 12th order since that's the subset limit. + * Also they are roughly ordered to match frequency of occurrence to + * minimize branching. + */ + if(order <= 12) { + if(order > 8) { + if(order > 10) { + if(order == 12) { + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[11] * (FLAC__int64)data[i-12]; + sum += qlp_coeff[10] * (FLAC__int64)data[i-11]; + sum += qlp_coeff[9] * (FLAC__int64)data[i-10]; + sum += qlp_coeff[8] * (FLAC__int64)data[i-9]; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 11 */ + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[10] * (FLAC__int64)data[i-11]; + sum += qlp_coeff[9] * (FLAC__int64)data[i-10]; + sum += qlp_coeff[8] * (FLAC__int64)data[i-9]; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + } + else { + if(order == 10) { + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[9] * (FLAC__int64)data[i-10]; + sum += qlp_coeff[8] * (FLAC__int64)data[i-9]; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 9 */ + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[8] * (FLAC__int64)data[i-9]; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + } + } + else if(order > 4) { + if(order > 6) { + if(order == 8) { + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 7 */ + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + } + else { + if(order == 6) { + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 5 */ + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + } + } + else { + if(order > 2) { + if(order == 4) { + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 3 */ + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + } + else { + if(order == 2) { + for(i = 0; i < data_len; i++) { + sum = 0; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 1 */ + for(i = 0; i < data_len; i++) + data[i] = residual[i] + (FLAC__int32)((qlp_coeff[0] * (FLAC__int64)data[i-1]) >> lp_quantization); + } + } + } + } + else { /* order > 12 */ + for(i = 0; i < data_len; i++) { + sum = 0; + switch(order) { + case 32: sum += qlp_coeff[31] * (FLAC__int64)data[i-32]; + case 31: sum += qlp_coeff[30] * (FLAC__int64)data[i-31]; + case 30: sum += qlp_coeff[29] * (FLAC__int64)data[i-30]; + case 29: sum += qlp_coeff[28] * (FLAC__int64)data[i-29]; + case 28: sum += qlp_coeff[27] * (FLAC__int64)data[i-28]; + case 27: sum += qlp_coeff[26] * (FLAC__int64)data[i-27]; + case 26: sum += qlp_coeff[25] * (FLAC__int64)data[i-26]; + case 25: sum += qlp_coeff[24] * (FLAC__int64)data[i-25]; + case 24: sum += qlp_coeff[23] * (FLAC__int64)data[i-24]; + case 23: sum += qlp_coeff[22] * (FLAC__int64)data[i-23]; + case 22: sum += qlp_coeff[21] * (FLAC__int64)data[i-22]; + case 21: sum += qlp_coeff[20] * (FLAC__int64)data[i-21]; + case 20: sum += qlp_coeff[19] * (FLAC__int64)data[i-20]; + case 19: sum += qlp_coeff[18] * (FLAC__int64)data[i-19]; + case 18: sum += qlp_coeff[17] * (FLAC__int64)data[i-18]; + case 17: sum += qlp_coeff[16] * (FLAC__int64)data[i-17]; + case 16: sum += qlp_coeff[15] * (FLAC__int64)data[i-16]; + case 15: sum += qlp_coeff[14] * (FLAC__int64)data[i-15]; + case 14: sum += qlp_coeff[13] * (FLAC__int64)data[i-14]; + case 13: sum += qlp_coeff[12] * (FLAC__int64)data[i-13]; + sum += qlp_coeff[11] * (FLAC__int64)data[i-12]; + sum += qlp_coeff[10] * (FLAC__int64)data[i-11]; + sum += qlp_coeff[ 9] * (FLAC__int64)data[i-10]; + sum += qlp_coeff[ 8] * (FLAC__int64)data[i- 9]; + sum += qlp_coeff[ 7] * (FLAC__int64)data[i- 8]; + sum += qlp_coeff[ 6] * (FLAC__int64)data[i- 7]; + sum += qlp_coeff[ 5] * (FLAC__int64)data[i- 6]; + sum += qlp_coeff[ 4] * (FLAC__int64)data[i- 5]; + sum += qlp_coeff[ 3] * (FLAC__int64)data[i- 4]; + sum += qlp_coeff[ 2] * (FLAC__int64)data[i- 3]; + sum += qlp_coeff[ 1] * (FLAC__int64)data[i- 2]; + sum += qlp_coeff[ 0] * (FLAC__int64)data[i- 1]; + } + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } +} +#endif + +#ifndef FLAC__INTEGER_ONLY_LIBRARY + +FLAC__double FLAC__lpc_compute_expected_bits_per_residual_sample(FLAC__double lpc_error, unsigned total_samples) +{ + FLAC__double error_scale; + + FLAC__ASSERT(total_samples > 0); + + error_scale = 0.5 * M_LN2 * M_LN2 / (FLAC__double)total_samples; + + return FLAC__lpc_compute_expected_bits_per_residual_sample_with_error_scale(lpc_error, error_scale); +} + +FLAC__double FLAC__lpc_compute_expected_bits_per_residual_sample_with_error_scale(FLAC__double lpc_error, FLAC__double error_scale) +{ + if(lpc_error > 0.0) { + FLAC__double bps = (FLAC__double)0.5 * log(error_scale * lpc_error) / M_LN2; + if(bps >= 0.0) + return bps; + else + return 0.0; + } + else if(lpc_error < 0.0) { /* error should not be negative but can happen due to inadequate floating-point resolution */ + return 1e32; + } + else { + return 0.0; + } +} + +unsigned FLAC__lpc_compute_best_order(const FLAC__double lpc_error[], unsigned max_order, unsigned total_samples, unsigned overhead_bits_per_order) +{ + unsigned order, index, best_index; /* 'index' the index into lpc_error; index==order-1 since lpc_error[0] is for order==1, lpc_error[1] is for order==2, etc */ + FLAC__double bits, best_bits, error_scale; + + FLAC__ASSERT(max_order > 0); + FLAC__ASSERT(total_samples > 0); + + error_scale = 0.5 * M_LN2 * M_LN2 / (FLAC__double)total_samples; + + best_index = 0; + best_bits = (unsigned)(-1); + + for(index = 0, order = 1; index < max_order; index++, order++) { + bits = FLAC__lpc_compute_expected_bits_per_residual_sample_with_error_scale(lpc_error[index], error_scale) * (FLAC__double)(total_samples - order) + (FLAC__double)(order * overhead_bits_per_order); + if(bits < best_bits) { + best_index = index; + best_bits = bits; + } + } + + return best_index+1; /* +1 since index of lpc_error[] is order-1 */ +} + +#endif /* !defined FLAC__INTEGER_ONLY_LIBRARY */ diff --git a/src/FLAC/src/libFLAC/md5.c b/src/FLAC/src/libFLAC/md5.c new file mode 100644 index 00000000..47e163f9 --- /dev/null +++ b/src/FLAC/src/libFLAC/md5.c @@ -0,0 +1,417 @@ +#if HAVE_CONFIG_H +# include +#endif + +#include /* for malloc() */ +#include /* for memcpy() */ + +#include "private/md5.h" + +#ifndef FLaC__INLINE +#define FLaC__INLINE +#endif + +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + * + * Changed so as no longer to depend on Colin Plumb's `usual.h' header + * definitions; now uses stuff from dpkg's config.h. + * - Ian Jackson . + * Still in the public domain. + * + * Josh Coalson: made some changes to integrate with libFLAC. + * Still in the public domain. + */ + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f,w,x,y,z,in,s) \ + (w += f(x,y,z) + in, w = (w<>(32-s)) + x) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +static void FLAC__MD5Transform(FLAC__uint32 buf[4], FLAC__uint32 const in[16]) +{ + register FLAC__uint32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +#if WORDS_BIGENDIAN +//@@@@@@ OPT: use bswap/intrinsics +static void byteSwap(FLAC__uint32 *buf, unsigned words) +{ + register FLAC__uint32 x; + do { + x = *buf; + x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); + *buf++ = (x >> 16) | (x << 16); + } while (--words); +} +static void byteSwapX16(FLAC__uint32 *buf) +{ + register FLAC__uint32 x; + + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); + x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf = (x >> 16) | (x << 16); +} +#else +#define byteSwap(buf, words) +#define byteSwapX16(buf) +#endif + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +static void FLAC__MD5Update(FLAC__MD5Context *ctx, FLAC__byte const *buf, unsigned len) +{ + FLAC__uint32 t; + + /* Update byte count */ + + t = ctx->bytes[0]; + if ((ctx->bytes[0] = t + len) < t) + ctx->bytes[1]++; /* Carry from low to high */ + + t = 64 - (t & 0x3f); /* Space available in ctx->in (at least 1) */ + if (t > len) { + memcpy((FLAC__byte *)ctx->in + 64 - t, buf, len); + return; + } + /* First chunk is an odd size */ + memcpy((FLAC__byte *)ctx->in + 64 - t, buf, t); + byteSwapX16(ctx->in); + FLAC__MD5Transform(ctx->buf, ctx->in); + buf += t; + len -= t; + + /* Process data in 64-byte chunks */ + while (len >= 64) { + memcpy(ctx->in, buf, 64); + byteSwapX16(ctx->in); + FLAC__MD5Transform(ctx->buf, ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + memcpy(ctx->in, buf, len); +} + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void FLAC__MD5Init(FLAC__MD5Context *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bytes[0] = 0; + ctx->bytes[1] = 0; + + ctx->internal_buf = 0; + ctx->capacity = 0; +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void FLAC__MD5Final(FLAC__byte digest[16], FLAC__MD5Context *ctx) +{ + int count = ctx->bytes[0] & 0x3f; /* Number of bytes in ctx->in */ + FLAC__byte *p = (FLAC__byte *)ctx->in + count; + + /* Set the first char of padding to 0x80. There is always room. */ + *p++ = 0x80; + + /* Bytes of padding needed to make 56 bytes (-8..55) */ + count = 56 - 1 - count; + + if (count < 0) { /* Padding forces an extra block */ + memset(p, 0, count + 8); + byteSwapX16(ctx->in); + FLAC__MD5Transform(ctx->buf, ctx->in); + p = (FLAC__byte *)ctx->in; + count = 56; + } + memset(p, 0, count); + byteSwap(ctx->in, 14); + + /* Append length in bits and transform */ + ctx->in[14] = ctx->bytes[0] << 3; + ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29; + FLAC__MD5Transform(ctx->buf, ctx->in); + + byteSwap(ctx->buf, 4); + memcpy(digest, ctx->buf, 16); + memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ + if(0 != ctx->internal_buf) { + free(ctx->internal_buf); + ctx->internal_buf = 0; + ctx->capacity = 0; + } +} + +/* + * Convert the incoming audio signal to a byte stream + */ +static void format_input_(FLAC__byte *buf, const FLAC__int32 * const signal[], unsigned channels, unsigned samples, unsigned bytes_per_sample) +{ + unsigned channel, sample; + register FLAC__int32 a_word; + register FLAC__byte *buf_ = buf; + +#if WORDS_BIGENDIAN +#else + if(channels == 2 && bytes_per_sample == 2) { + FLAC__int16 *buf1_ = ((FLAC__int16*)buf_) + 1; + memcpy(buf_, signal[0], sizeof(FLAC__int32) * samples); + for(sample = 0; sample < samples; sample++, buf1_+=2) + *buf1_ = (FLAC__int16)signal[1][sample]; + } + else if(channels == 1 && bytes_per_sample == 2) { + FLAC__int16 *buf1_ = (FLAC__int16*)buf_; + for(sample = 0; sample < samples; sample++) + *buf1_++ = (FLAC__int16)signal[0][sample]; + } + else +#endif + if(bytes_per_sample == 2) { + if(channels == 2) { + for(sample = 0; sample < samples; sample++) { + a_word = signal[0][sample]; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; + a_word = signal[1][sample]; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; + } + } + else if(channels == 1) { + for(sample = 0; sample < samples; sample++) { + a_word = signal[0][sample]; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; + } + } + else { + for(sample = 0; sample < samples; sample++) { + for(channel = 0; channel < channels; channel++) { + a_word = signal[channel][sample]; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; + } + } + } + } + else if(bytes_per_sample == 3) { + if(channels == 2) { + for(sample = 0; sample < samples; sample++) { + a_word = signal[0][sample]; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; + a_word = signal[1][sample]; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; + } + } + else if(channels == 1) { + for(sample = 0; sample < samples; sample++) { + a_word = signal[0][sample]; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; + } + } + else { + for(sample = 0; sample < samples; sample++) { + for(channel = 0; channel < channels; channel++) { + a_word = signal[channel][sample]; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; + } + } + } + } + else if(bytes_per_sample == 1) { + if(channels == 2) { + for(sample = 0; sample < samples; sample++) { + a_word = signal[0][sample]; + *buf_++ = (FLAC__byte)a_word; + a_word = signal[1][sample]; + *buf_++ = (FLAC__byte)a_word; + } + } + else if(channels == 1) { + for(sample = 0; sample < samples; sample++) { + a_word = signal[0][sample]; + *buf_++ = (FLAC__byte)a_word; + } + } + else { + for(sample = 0; sample < samples; sample++) { + for(channel = 0; channel < channels; channel++) { + a_word = signal[channel][sample]; + *buf_++ = (FLAC__byte)a_word; + } + } + } + } + else { /* bytes_per_sample == 4, maybe optimize more later */ + for(sample = 0; sample < samples; sample++) { + for(channel = 0; channel < channels; channel++) { + a_word = signal[channel][sample]; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; + } + } + } +} + +/* + * Convert the incoming audio signal to a byte stream and FLAC__MD5Update it. + */ +FLAC__bool FLAC__MD5Accumulate(FLAC__MD5Context *ctx, const FLAC__int32 * const signal[], unsigned channels, unsigned samples, unsigned bytes_per_sample) +{ + const unsigned bytes_needed = channels * samples * bytes_per_sample; + + if(ctx->capacity < bytes_needed) { + FLAC__byte *tmp = (FLAC__byte*)realloc(ctx->internal_buf, bytes_needed); + if(0 == tmp) { + free(ctx->internal_buf); + if(0 == (ctx->internal_buf = (FLAC__byte*)malloc(bytes_needed))) + return false; + } + ctx->internal_buf = tmp; + ctx->capacity = bytes_needed; + } + + format_input_(ctx->internal_buf, signal, channels, samples, bytes_per_sample); + + FLAC__MD5Update(ctx, ctx->internal_buf, bytes_needed); + + return true; +} diff --git a/src/FLAC/src/libFLAC/memory.c b/src/FLAC/src/libFLAC/memory.c new file mode 100644 index 00000000..2df3bfb5 --- /dev/null +++ b/src/FLAC/src/libFLAC/memory.c @@ -0,0 +1,194 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include "private/memory.h" +#include "FLAC/assert.h" + +void *FLAC__memory_alloc_aligned(size_t bytes, void **aligned_address) +{ + void *x; + + FLAC__ASSERT(0 != aligned_address); + +#ifdef FLAC__ALIGN_MALLOC_DATA + /* align on 32-byte (256-bit) boundary */ + x = malloc(bytes+31); + /* there's got to be a better way to do this right for all archs */ + if(sizeof(void*) == sizeof(unsigned)) + *aligned_address = (void*)(((unsigned)x + 31) & -32); + else if(sizeof(void*) == sizeof(FLAC__uint64)) + *aligned_address = (void*)(((FLAC__uint64)x + 31) & (FLAC__uint64)(-((FLAC__int64)32))); + else + return 0; +#else + x = malloc(bytes); + *aligned_address = x; +#endif + return x; +} + +FLAC__bool FLAC__memory_alloc_aligned_int32_array(unsigned elements, FLAC__int32 **unaligned_pointer, FLAC__int32 **aligned_pointer) +{ + FLAC__int32 *pu; /* unaligned pointer */ + union { /* union needed to comply with C99 pointer aliasing rules */ + FLAC__int32 *pa; /* aligned pointer */ + void *pv; /* aligned pointer alias */ + } u; + + FLAC__ASSERT(elements > 0); + FLAC__ASSERT(0 != unaligned_pointer); + FLAC__ASSERT(0 != aligned_pointer); + FLAC__ASSERT(unaligned_pointer != aligned_pointer); + + pu = (FLAC__int32*)FLAC__memory_alloc_aligned(sizeof(FLAC__int32) * elements, &u.pv); + if(0 == pu) { + return false; + } + else { + if(*unaligned_pointer != 0) + free(*unaligned_pointer); + *unaligned_pointer = pu; + *aligned_pointer = u.pa; + return true; + } +} + +FLAC__bool FLAC__memory_alloc_aligned_uint32_array(unsigned elements, FLAC__uint32 **unaligned_pointer, FLAC__uint32 **aligned_pointer) +{ + FLAC__uint32 *pu; /* unaligned pointer */ + union { /* union needed to comply with C99 pointer aliasing rules */ + FLAC__uint32 *pa; /* aligned pointer */ + void *pv; /* aligned pointer alias */ + } u; + + FLAC__ASSERT(elements > 0); + FLAC__ASSERT(0 != unaligned_pointer); + FLAC__ASSERT(0 != aligned_pointer); + FLAC__ASSERT(unaligned_pointer != aligned_pointer); + + pu = (FLAC__uint32*)FLAC__memory_alloc_aligned(sizeof(FLAC__uint32) * elements, &u.pv); + if(0 == pu) { + return false; + } + else { + if(*unaligned_pointer != 0) + free(*unaligned_pointer); + *unaligned_pointer = pu; + *aligned_pointer = u.pa; + return true; + } +} + +FLAC__bool FLAC__memory_alloc_aligned_uint64_array(unsigned elements, FLAC__uint64 **unaligned_pointer, FLAC__uint64 **aligned_pointer) +{ + FLAC__uint64 *pu; /* unaligned pointer */ + union { /* union needed to comply with C99 pointer aliasing rules */ + FLAC__uint64 *pa; /* aligned pointer */ + void *pv; /* aligned pointer alias */ + } u; + + FLAC__ASSERT(elements > 0); + FLAC__ASSERT(0 != unaligned_pointer); + FLAC__ASSERT(0 != aligned_pointer); + FLAC__ASSERT(unaligned_pointer != aligned_pointer); + + pu = (FLAC__uint64*)FLAC__memory_alloc_aligned(sizeof(FLAC__uint64) * elements, &u.pv); + if(0 == pu) { + return false; + } + else { + if(*unaligned_pointer != 0) + free(*unaligned_pointer); + *unaligned_pointer = pu; + *aligned_pointer = u.pa; + return true; + } +} + +FLAC__bool FLAC__memory_alloc_aligned_unsigned_array(unsigned elements, unsigned **unaligned_pointer, unsigned **aligned_pointer) +{ + unsigned *pu; /* unaligned pointer */ + union { /* union needed to comply with C99 pointer aliasing rules */ + unsigned *pa; /* aligned pointer */ + void *pv; /* aligned pointer alias */ + } u; + + FLAC__ASSERT(elements > 0); + FLAC__ASSERT(0 != unaligned_pointer); + FLAC__ASSERT(0 != aligned_pointer); + FLAC__ASSERT(unaligned_pointer != aligned_pointer); + + pu = (unsigned*)FLAC__memory_alloc_aligned(sizeof(unsigned) * elements, &u.pv); + if(0 == pu) { + return false; + } + else { + if(*unaligned_pointer != 0) + free(*unaligned_pointer); + *unaligned_pointer = pu; + *aligned_pointer = u.pa; + return true; + } +} + +#ifndef FLAC__INTEGER_ONLY_LIBRARY + +FLAC__bool FLAC__memory_alloc_aligned_real_array(unsigned elements, FLAC__real **unaligned_pointer, FLAC__real **aligned_pointer) +{ + FLAC__real *pu; /* unaligned pointer */ + union { /* union needed to comply with C99 pointer aliasing rules */ + FLAC__real *pa; /* aligned pointer */ + void *pv; /* aligned pointer alias */ + } u; + + FLAC__ASSERT(elements > 0); + FLAC__ASSERT(0 != unaligned_pointer); + FLAC__ASSERT(0 != aligned_pointer); + FLAC__ASSERT(unaligned_pointer != aligned_pointer); + + pu = (FLAC__real*)FLAC__memory_alloc_aligned(sizeof(FLAC__real) * elements, &u.pv); + if(0 == pu) { + return false; + } + else { + if(*unaligned_pointer != 0) + free(*unaligned_pointer); + *unaligned_pointer = pu; + *aligned_pointer = u.pa; + return true; + } +} + +#endif diff --git a/src/FLAC/src/libFLAC/metadata_iterators.c b/src/FLAC/src/libFLAC/metadata_iterators.c new file mode 100644 index 00000000..d06d195e --- /dev/null +++ b/src/FLAC/src/libFLAC/metadata_iterators.c @@ -0,0 +1,3304 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include + +#if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__ +#if defined __BORLANDC__ +#include /* for utime() */ +#else +#include /* for utime() */ +#endif +#include /* for chmod() */ +#include /* for off_t */ +#if _MSC_VER <= 1600 || defined __BORLANDC__ /* @@@ [2G limit] */ +#define fseeko fseek +#define ftello ftell +#endif +#else +#include /* some flavors of BSD (like OS X) require this to get time_t */ +#include /* for utime() */ +#include /* for chown(), unlink() */ +#endif +#include /* for stat(), maybe chmod() */ + +#include "private/metadata.h" + +#include "FLAC/assert.h" +#include "FLAC/stream_decoder.h" + +#ifdef max +#undef max +#endif +#define max(a,b) ((a)>(b)?(a):(b)) +#ifdef min +#undef min +#endif +#define min(a,b) ((a)<(b)?(a):(b)) + + +/**************************************************************************** + * + * Local function declarations + * + ***************************************************************************/ + +static void pack_uint32_(FLAC__uint32 val, FLAC__byte *b, unsigned bytes); +static void pack_uint32_little_endian_(FLAC__uint32 val, FLAC__byte *b, unsigned bytes); +static void pack_uint64_(FLAC__uint64 val, FLAC__byte *b, unsigned bytes); +static FLAC__uint32 unpack_uint32_(FLAC__byte *b, unsigned bytes); +static FLAC__uint32 unpack_uint32_little_endian_(FLAC__byte *b, unsigned bytes); +static FLAC__uint64 unpack_uint64_(FLAC__byte *b, unsigned bytes); + +static FLAC__bool read_metadata_block_header_(FLAC__Metadata_SimpleIterator *iterator); +static FLAC__bool read_metadata_block_data_(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block); +static FLAC__bool read_metadata_block_header_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__bool *is_last, FLAC__MetadataType *type, unsigned *length); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb, FLAC__StreamMetadata *block); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_streaminfo_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_StreamInfo *block); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_padding_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Seek seek_cb, FLAC__StreamMetadata_Padding *block, unsigned block_length); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_application_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_Application *block, unsigned block_length); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_seektable_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_SeekTable *block, unsigned block_length); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_vorbis_comment_entry_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_VorbisComment_Entry *entry); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_vorbis_comment_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_VorbisComment *block); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_cuesheet_track_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_CueSheet_Track *track); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_cuesheet_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_CueSheet *block); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_picture_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_Picture *block); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_unknown_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_Unknown *block, unsigned block_length); + +static FLAC__bool write_metadata_block_header_(FILE *file, FLAC__Metadata_SimpleIteratorStatus *status, const FLAC__StreamMetadata *block); +static FLAC__bool write_metadata_block_data_(FILE *file, FLAC__Metadata_SimpleIteratorStatus *status, const FLAC__StreamMetadata *block); +static FLAC__bool write_metadata_block_header_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata *block); +static FLAC__bool write_metadata_block_data_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata *block); +static FLAC__bool write_metadata_block_data_streaminfo_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_StreamInfo *block); +static FLAC__bool write_metadata_block_data_padding_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Padding *block, unsigned block_length); +static FLAC__bool write_metadata_block_data_application_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Application *block, unsigned block_length); +static FLAC__bool write_metadata_block_data_seektable_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_SeekTable *block); +static FLAC__bool write_metadata_block_data_vorbis_comment_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_VorbisComment *block); +static FLAC__bool write_metadata_block_data_cuesheet_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_CueSheet *block); +static FLAC__bool write_metadata_block_data_picture_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Picture *block); +static FLAC__bool write_metadata_block_data_unknown_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Unknown *block, unsigned block_length); + +static FLAC__bool write_metadata_block_stationary_(FLAC__Metadata_SimpleIterator *iterator, const FLAC__StreamMetadata *block); +static FLAC__bool write_metadata_block_stationary_with_padding_(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, unsigned padding_length, FLAC__bool padding_is_last); +static FLAC__bool rewrite_whole_file_(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, FLAC__bool append); + +static void simple_iterator_push_(FLAC__Metadata_SimpleIterator *iterator); +static FLAC__bool simple_iterator_pop_(FLAC__Metadata_SimpleIterator *iterator); + +static unsigned seek_to_first_metadata_block_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb); +static unsigned seek_to_first_metadata_block_(FILE *f); + +static FLAC__bool simple_iterator_copy_file_prefix_(FLAC__Metadata_SimpleIterator *iterator, FILE **tempfile, char **tempfilename, FLAC__bool append); +static FLAC__bool simple_iterator_copy_file_postfix_(FLAC__Metadata_SimpleIterator *iterator, FILE **tempfile, char **tempfilename, int fixup_is_last_code, off_t fixup_is_last_flag_offset, FLAC__bool backup); + +static FLAC__bool copy_n_bytes_from_file_(FILE *file, FILE *tempfile, off_t bytes, FLAC__Metadata_SimpleIteratorStatus *status); +static FLAC__bool copy_n_bytes_from_file_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOHandle temp_handle, FLAC__IOCallback_Write temp_write_cb, off_t bytes, FLAC__Metadata_SimpleIteratorStatus *status); +static FLAC__bool copy_remaining_bytes_from_file_(FILE *file, FILE *tempfile, FLAC__Metadata_SimpleIteratorStatus *status); +static FLAC__bool copy_remaining_bytes_from_file_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Eof eof_cb, FLAC__IOHandle temp_handle, FLAC__IOCallback_Write temp_write_cb, FLAC__Metadata_SimpleIteratorStatus *status); + +static FLAC__bool open_tempfile_(const char *filename, const char *tempfile_path_prefix, FILE **tempfile, char **tempfilename, FLAC__Metadata_SimpleIteratorStatus *status); +static FLAC__bool transport_tempfile_(const char *filename, FILE **tempfile, char **tempfilename, FLAC__Metadata_SimpleIteratorStatus *status); +static void cleanup_tempfile_(FILE **tempfile, char **tempfilename); + +static FLAC__bool get_file_stats_(const char *filename, struct stat *stats); +static void set_file_stats_(const char *filename, struct stat *stats); + +static int fseek_wrapper_(FLAC__IOHandle handle, FLAC__int64 offset, int whence); +static FLAC__int64 ftell_wrapper_(FLAC__IOHandle handle); + +static FLAC__Metadata_ChainStatus get_equivalent_status_(FLAC__Metadata_SimpleIteratorStatus status); + + +#ifdef FLAC__VALGRIND_TESTING +static size_t local__fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) +{ + size_t ret = fwrite(ptr, size, nmemb, stream); + if(!ferror(stream)) + fflush(stream); + return ret; +} +#else +#define local__fwrite fwrite +#endif + +/**************************************************************************** + * + * Level 0 implementation + * + ***************************************************************************/ + +static FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data); +static void metadata_callback_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data); +static void error_callback_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data); + +typedef struct { + FLAC__bool got_error; + FLAC__StreamMetadata *object; +} level0_client_data; + +static FLAC__StreamMetadata *get_one_metadata_block_(const char *filename, FLAC__MetadataType type) +{ + level0_client_data cd; + FLAC__StreamDecoder *decoder; + + FLAC__ASSERT(0 != filename); + + cd.got_error = false; + cd.object = 0; + + decoder = FLAC__stream_decoder_new(); + + if(0 == decoder) + return 0; + + FLAC__stream_decoder_set_md5_checking(decoder, false); + FLAC__stream_decoder_set_metadata_ignore_all(decoder); + FLAC__stream_decoder_set_metadata_respond(decoder, type); + + if(FLAC__stream_decoder_init_file(decoder, filename, write_callback_, metadata_callback_, error_callback_, &cd) != FLAC__STREAM_DECODER_INIT_STATUS_OK || cd.got_error) { + (void)FLAC__stream_decoder_finish(decoder); + FLAC__stream_decoder_delete(decoder); + return 0; + } + + if(!FLAC__stream_decoder_process_until_end_of_metadata(decoder) || cd.got_error) { + (void)FLAC__stream_decoder_finish(decoder); + FLAC__stream_decoder_delete(decoder); + if(0 != cd.object) + FLAC__metadata_object_delete(cd.object); + return 0; + } + + (void)FLAC__stream_decoder_finish(decoder); + FLAC__stream_decoder_delete(decoder); + + return cd.object; +} + +FLAC_API FLAC__bool FLAC__metadata_get_streaminfo(const char *filename, FLAC__StreamMetadata *streaminfo) +{ + FLAC__StreamMetadata *object; + + FLAC__ASSERT(0 != filename); + FLAC__ASSERT(0 != streaminfo); + + object = get_one_metadata_block_(filename, FLAC__METADATA_TYPE_STREAMINFO); + + if (object) { + /* can just copy the contents since STREAMINFO has no internal structure */ + *streaminfo = *object; + FLAC__metadata_object_delete(object); + return true; + } + else { + return false; + } +} + +FLAC_API FLAC__bool FLAC__metadata_get_tags(const char *filename, FLAC__StreamMetadata **tags) +{ + FLAC__ASSERT(0 != filename); + FLAC__ASSERT(0 != tags); + + *tags = get_one_metadata_block_(filename, FLAC__METADATA_TYPE_VORBIS_COMMENT); + + return 0 != *tags; +} + +FLAC_API FLAC__bool FLAC__metadata_get_cuesheet(const char *filename, FLAC__StreamMetadata **cuesheet) +{ + FLAC__ASSERT(0 != filename); + FLAC__ASSERT(0 != cuesheet); + + *cuesheet = get_one_metadata_block_(filename, FLAC__METADATA_TYPE_CUESHEET); + + return 0 != *cuesheet; +} + +FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data) +{ + (void)decoder, (void)frame, (void)buffer, (void)client_data; + + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; +} + +void metadata_callback_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) +{ + level0_client_data *cd = (level0_client_data *)client_data; + (void)decoder; + + /* + * we assume we only get here when the one metadata block we were + * looking for was passed to us + */ + if(!cd->got_error && 0 == cd->object) { + if(0 == (cd->object = FLAC__metadata_object_clone(metadata))) + cd->got_error = true; + } +} + +void error_callback_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) +{ + level0_client_data *cd = (level0_client_data *)client_data; + (void)decoder; + + if(status != FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC) + cd->got_error = true; +} + +FLAC_API FLAC__bool FLAC__metadata_get_picture(const char *filename, FLAC__StreamMetadata **picture, FLAC__StreamMetadata_Picture_Type type, const char *mime_type, const FLAC__byte *description, unsigned max_width, unsigned max_height, unsigned max_depth, unsigned max_colors) +{ + FLAC__Metadata_SimpleIterator *it; + FLAC__uint64 max_area_seen = 0; + FLAC__uint64 max_depth_seen = 0; + + FLAC__ASSERT(0 != filename); + FLAC__ASSERT(0 != picture); + + *picture = 0; + + it = FLAC__metadata_simple_iterator_new(); + if(0 == it) + return false; + if(!FLAC__metadata_simple_iterator_init(it, filename, /*read_only=*/true, /*preserve_file_stats=*/true)) { + FLAC__metadata_simple_iterator_delete(it); + return false; + } + do { + if(FLAC__metadata_simple_iterator_get_block_type(it) == FLAC__METADATA_TYPE_PICTURE) { + FLAC__StreamMetadata *obj = FLAC__metadata_simple_iterator_get_block(it); + FLAC__uint64 area = (FLAC__uint64)obj->data.picture.width * (FLAC__uint64)obj->data.picture.height; + /* check constraints */ + if( + (type == (FLAC__StreamMetadata_Picture_Type)(-1) || type == obj->data.picture.type) && + (mime_type == 0 || !strcmp(mime_type, obj->data.picture.mime_type)) && + (description == 0 || !strcmp((const char *)description, (const char *)obj->data.picture.description)) && + obj->data.picture.width <= max_width && + obj->data.picture.height <= max_height && + obj->data.picture.depth <= max_depth && + obj->data.picture.colors <= max_colors && + (area > max_area_seen || (area == max_area_seen && obj->data.picture.depth > max_depth_seen)) + ) { + if(*picture) + FLAC__metadata_object_delete(*picture); + *picture = obj; + max_area_seen = area; + max_depth_seen = obj->data.picture.depth; + } + else { + FLAC__metadata_object_delete(obj); + } + } + } while(FLAC__metadata_simple_iterator_next(it)); + + FLAC__metadata_simple_iterator_delete(it); + + return (0 != *picture); +} + + +/**************************************************************************** + * + * Level 1 implementation + * + ***************************************************************************/ + +#define SIMPLE_ITERATOR_MAX_PUSH_DEPTH (1+4) +/* 1 for initial offset, +4 for our own personal use */ + +struct FLAC__Metadata_SimpleIterator { + FILE *file; + char *filename, *tempfile_path_prefix; + struct stat stats; + FLAC__bool has_stats; + FLAC__bool is_writable; + FLAC__Metadata_SimpleIteratorStatus status; + off_t offset[SIMPLE_ITERATOR_MAX_PUSH_DEPTH]; + off_t first_offset; /* this is the offset to the STREAMINFO block */ + unsigned depth; + /* this is the metadata block header of the current block we are pointing to: */ + FLAC__bool is_last; + FLAC__MetadataType type; + unsigned length; +}; + +FLAC_API const char * const FLAC__Metadata_SimpleIteratorStatusString[] = { + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_A_FLAC_FILE", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_WRITABLE", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_BAD_METADATA", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_RENAME_ERROR", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_UNLINK_ERROR", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_INTERNAL_ERROR" +}; + + +FLAC_API FLAC__Metadata_SimpleIterator *FLAC__metadata_simple_iterator_new(void) +{ + FLAC__Metadata_SimpleIterator *iterator = (FLAC__Metadata_SimpleIterator*)calloc(1, sizeof(FLAC__Metadata_SimpleIterator)); + + if(0 != iterator) { + iterator->file = 0; + iterator->filename = 0; + iterator->tempfile_path_prefix = 0; + iterator->has_stats = false; + iterator->is_writable = false; + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; + iterator->first_offset = iterator->offset[0] = -1; + iterator->depth = 0; + } + + return iterator; +} + +static void simple_iterator_free_guts_(FLAC__Metadata_SimpleIterator *iterator) +{ + FLAC__ASSERT(0 != iterator); + + if(0 != iterator->file) { + fclose(iterator->file); + iterator->file = 0; + if(iterator->has_stats) + set_file_stats_(iterator->filename, &iterator->stats); + } + if(0 != iterator->filename) { + free(iterator->filename); + iterator->filename = 0; + } + if(0 != iterator->tempfile_path_prefix) { + free(iterator->tempfile_path_prefix); + iterator->tempfile_path_prefix = 0; + } +} + +FLAC_API void FLAC__metadata_simple_iterator_delete(FLAC__Metadata_SimpleIterator *iterator) +{ + FLAC__ASSERT(0 != iterator); + + simple_iterator_free_guts_(iterator); + free(iterator); +} + +FLAC_API FLAC__Metadata_SimpleIteratorStatus FLAC__metadata_simple_iterator_status(FLAC__Metadata_SimpleIterator *iterator) +{ + FLAC__Metadata_SimpleIteratorStatus status; + + FLAC__ASSERT(0 != iterator); + + status = iterator->status; + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; + return status; +} + +static FLAC__bool simple_iterator_prime_input_(FLAC__Metadata_SimpleIterator *iterator, FLAC__bool read_only) +{ + unsigned ret; + + FLAC__ASSERT(0 != iterator); + + if(read_only || 0 == (iterator->file = fopen(iterator->filename, "r+b"))) { + iterator->is_writable = false; + if(read_only || errno == EACCES) { + if(0 == (iterator->file = fopen(iterator->filename, "rb"))) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE; + return false; + } + } + else { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE; + return false; + } + } + else { + iterator->is_writable = true; + } + + ret = seek_to_first_metadata_block_(iterator->file); + switch(ret) { + case 0: + iterator->depth = 0; + iterator->first_offset = iterator->offset[iterator->depth] = ftello(iterator->file); + return read_metadata_block_header_(iterator); + case 1: + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + return false; + case 2: + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + case 3: + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_A_FLAC_FILE; + return false; + default: + FLAC__ASSERT(0); + return false; + } +} + +#if 0 +@@@ If we decide to finish implementing this, put this comment back in metadata.h +/* + * The 'tempfile_path_prefix' allows you to specify a directory where + * tempfiles should go. Remember that if your metadata edits cause the + * FLAC file to grow, the entire file will have to be rewritten. If + * 'tempfile_path_prefix' is NULL, the temp file will be written in the + * same directory as the original FLAC file. This makes replacing the + * original with the tempfile fast but requires extra space in the same + * partition for the tempfile. If space is a problem, you can pass a + * directory name belonging to a different partition in + * 'tempfile_path_prefix'. Note that you should use the forward slash + * '/' as the directory separator. A trailing slash is not needed; it + * will be added automatically. + */ +FLAC__bool FLAC__metadata_simple_iterator_init(FLAC__Metadata_SimpleIterator *iterator, const char *filename, FLAC__bool preserve_file_stats, const char *tempfile_path_prefix); +#endif + +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_init(FLAC__Metadata_SimpleIterator *iterator, const char *filename, FLAC__bool read_only, FLAC__bool preserve_file_stats) +{ + const char *tempfile_path_prefix = 0; /*@@@ search for comments near 'rename(...)' for what it will take to finish implementing this */ + + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != filename); + + simple_iterator_free_guts_(iterator); + + if(!read_only && preserve_file_stats) + iterator->has_stats = get_file_stats_(filename, &iterator->stats); + + if(0 == (iterator->filename = strdup(filename))) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + return false; + } + if(0 != tempfile_path_prefix && 0 == (iterator->tempfile_path_prefix = strdup(tempfile_path_prefix))) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + return false; + } + + return simple_iterator_prime_input_(iterator, read_only); +} + +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_is_writable(const FLAC__Metadata_SimpleIterator *iterator) +{ + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->file); + + return iterator->is_writable; +} + +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_next(FLAC__Metadata_SimpleIterator *iterator) +{ + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->file); + + if(iterator->is_last) + return false; + + if(0 != fseeko(iterator->file, iterator->length, SEEK_CUR)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + + iterator->offset[iterator->depth] = ftello(iterator->file); + + return read_metadata_block_header_(iterator); +} + +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_prev(FLAC__Metadata_SimpleIterator *iterator) +{ + off_t this_offset; + + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->file); + + if(iterator->offset[iterator->depth] == iterator->first_offset) + return false; + + if(0 != fseeko(iterator->file, iterator->first_offset, SEEK_SET)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + this_offset = iterator->first_offset; + if(!read_metadata_block_header_(iterator)) + return false; + + /* we ignore any error from ftello() and catch it in fseeko() */ + while(ftello(iterator->file) + (off_t)iterator->length < iterator->offset[iterator->depth]) { + if(0 != fseeko(iterator->file, iterator->length, SEEK_CUR)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + this_offset = ftello(iterator->file); + if(!read_metadata_block_header_(iterator)) + return false; + } + + iterator->offset[iterator->depth] = this_offset; + + return true; +} + +FLAC_API FLAC__MetadataType FLAC__metadata_simple_iterator_get_block_type(const FLAC__Metadata_SimpleIterator *iterator) +{ + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->file); + + return iterator->type; +} + +FLAC_API FLAC__StreamMetadata *FLAC__metadata_simple_iterator_get_block(FLAC__Metadata_SimpleIterator *iterator) +{ + FLAC__StreamMetadata *block = FLAC__metadata_object_new(iterator->type); + + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->file); + + if(0 != block) { + block->is_last = iterator->is_last; + block->length = iterator->length; + + if(!read_metadata_block_data_(iterator, block)) { + FLAC__metadata_object_delete(block); + return 0; + } + + /* back up to the beginning of the block data to stay consistent */ + if(0 != fseeko(iterator->file, iterator->offset[iterator->depth] + FLAC__STREAM_METADATA_HEADER_LENGTH, SEEK_SET)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + FLAC__metadata_object_delete(block); + return 0; + } + } + else + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + + return block; +} + +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_set_block(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, FLAC__bool use_padding) +{ + FLAC__ASSERT_DECLARATION(off_t debug_target_offset = iterator->offset[iterator->depth];) + FLAC__bool ret; + + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->file); + FLAC__ASSERT(0 != block); + + if(!iterator->is_writable) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_WRITABLE; + return false; + } + + if(iterator->type == FLAC__METADATA_TYPE_STREAMINFO || block->type == FLAC__METADATA_TYPE_STREAMINFO) { + if(iterator->type != block->type) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT; + return false; + } + } + + block->is_last = iterator->is_last; + + if(iterator->length == block->length) + return write_metadata_block_stationary_(iterator, block); + else if(iterator->length > block->length) { + if(use_padding && iterator->length >= FLAC__STREAM_METADATA_HEADER_LENGTH + block->length) { + ret = write_metadata_block_stationary_with_padding_(iterator, block, iterator->length - FLAC__STREAM_METADATA_HEADER_LENGTH - block->length, block->is_last); + FLAC__ASSERT(!ret || iterator->offset[iterator->depth] == debug_target_offset); + FLAC__ASSERT(!ret || ftello(iterator->file) == debug_target_offset + (off_t)FLAC__STREAM_METADATA_HEADER_LENGTH); + return ret; + } + else { + ret = rewrite_whole_file_(iterator, block, /*append=*/false); + FLAC__ASSERT(!ret || iterator->offset[iterator->depth] == debug_target_offset); + FLAC__ASSERT(!ret || ftello(iterator->file) == debug_target_offset + (off_t)FLAC__STREAM_METADATA_HEADER_LENGTH); + return ret; + } + } + else /* iterator->length < block->length */ { + unsigned padding_leftover = 0; + FLAC__bool padding_is_last = false; + if(use_padding) { + /* first see if we can even use padding */ + if(iterator->is_last) { + use_padding = false; + } + else { + const unsigned extra_padding_bytes_required = block->length - iterator->length; + simple_iterator_push_(iterator); + if(!FLAC__metadata_simple_iterator_next(iterator)) { + (void)simple_iterator_pop_(iterator); + return false; + } + if(iterator->type != FLAC__METADATA_TYPE_PADDING) { + use_padding = false; + } + else { + if(FLAC__STREAM_METADATA_HEADER_LENGTH + iterator->length == extra_padding_bytes_required) { + padding_leftover = 0; + block->is_last = iterator->is_last; + } + else if(iterator->length < extra_padding_bytes_required) + use_padding = false; + else { + padding_leftover = FLAC__STREAM_METADATA_HEADER_LENGTH + iterator->length - extra_padding_bytes_required; + padding_is_last = iterator->is_last; + block->is_last = false; + } + } + if(!simple_iterator_pop_(iterator)) + return false; + } + } + if(use_padding) { + if(padding_leftover == 0) { + ret = write_metadata_block_stationary_(iterator, block); + FLAC__ASSERT(!ret || iterator->offset[iterator->depth] == debug_target_offset); + FLAC__ASSERT(!ret || ftello(iterator->file) == debug_target_offset + (off_t)FLAC__STREAM_METADATA_HEADER_LENGTH); + return ret; + } + else { + FLAC__ASSERT(padding_leftover >= FLAC__STREAM_METADATA_HEADER_LENGTH); + ret = write_metadata_block_stationary_with_padding_(iterator, block, padding_leftover - FLAC__STREAM_METADATA_HEADER_LENGTH, padding_is_last); + FLAC__ASSERT(!ret || iterator->offset[iterator->depth] == debug_target_offset); + FLAC__ASSERT(!ret || ftello(iterator->file) == debug_target_offset + (off_t)FLAC__STREAM_METADATA_HEADER_LENGTH); + return ret; + } + } + else { + ret = rewrite_whole_file_(iterator, block, /*append=*/false); + FLAC__ASSERT(!ret || iterator->offset[iterator->depth] == debug_target_offset); + FLAC__ASSERT(!ret || ftello(iterator->file) == debug_target_offset + (off_t)FLAC__STREAM_METADATA_HEADER_LENGTH); + return ret; + } + } +} + +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_insert_block_after(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, FLAC__bool use_padding) +{ + unsigned padding_leftover = 0; + FLAC__bool padding_is_last = false; + + FLAC__ASSERT_DECLARATION(off_t debug_target_offset = iterator->offset[iterator->depth] + FLAC__STREAM_METADATA_HEADER_LENGTH + iterator->length;) + FLAC__bool ret; + + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->file); + FLAC__ASSERT(0 != block); + + if(!iterator->is_writable) + return false; + + if(block->type == FLAC__METADATA_TYPE_STREAMINFO) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT; + return false; + } + + block->is_last = iterator->is_last; + + if(use_padding) { + /* first see if we can even use padding */ + if(iterator->is_last) { + use_padding = false; + } + else { + simple_iterator_push_(iterator); + if(!FLAC__metadata_simple_iterator_next(iterator)) { + (void)simple_iterator_pop_(iterator); + return false; + } + if(iterator->type != FLAC__METADATA_TYPE_PADDING) { + use_padding = false; + } + else { + if(iterator->length == block->length) { + padding_leftover = 0; + block->is_last = iterator->is_last; + } + else if(iterator->length < FLAC__STREAM_METADATA_HEADER_LENGTH + block->length) + use_padding = false; + else { + padding_leftover = iterator->length - block->length; + padding_is_last = iterator->is_last; + block->is_last = false; + } + } + if(!simple_iterator_pop_(iterator)) + return false; + } + } + if(use_padding) { + /* move to the next block, which is suitable padding */ + if(!FLAC__metadata_simple_iterator_next(iterator)) + return false; + if(padding_leftover == 0) { + ret = write_metadata_block_stationary_(iterator, block); + FLAC__ASSERT(iterator->offset[iterator->depth] == debug_target_offset); + FLAC__ASSERT(ftello(iterator->file) == debug_target_offset + (off_t)FLAC__STREAM_METADATA_HEADER_LENGTH); + return ret; + } + else { + FLAC__ASSERT(padding_leftover >= FLAC__STREAM_METADATA_HEADER_LENGTH); + ret = write_metadata_block_stationary_with_padding_(iterator, block, padding_leftover - FLAC__STREAM_METADATA_HEADER_LENGTH, padding_is_last); + FLAC__ASSERT(iterator->offset[iterator->depth] == debug_target_offset); + FLAC__ASSERT(ftello(iterator->file) == debug_target_offset + (off_t)FLAC__STREAM_METADATA_HEADER_LENGTH); + return ret; + } + } + else { + ret = rewrite_whole_file_(iterator, block, /*append=*/true); + FLAC__ASSERT(iterator->offset[iterator->depth] == debug_target_offset); + FLAC__ASSERT(ftello(iterator->file) == debug_target_offset + (off_t)FLAC__STREAM_METADATA_HEADER_LENGTH); + return ret; + } +} + +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_delete_block(FLAC__Metadata_SimpleIterator *iterator, FLAC__bool use_padding) +{ + FLAC__ASSERT_DECLARATION(off_t debug_target_offset = iterator->offset[iterator->depth];) + FLAC__bool ret; + + if(iterator->type == FLAC__METADATA_TYPE_STREAMINFO) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT; + return false; + } + + if(use_padding) { + FLAC__StreamMetadata *padding = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING); + if(0 == padding) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + return false; + } + padding->length = iterator->length; + if(!FLAC__metadata_simple_iterator_set_block(iterator, padding, false)) { + FLAC__metadata_object_delete(padding); + return false; + } + FLAC__metadata_object_delete(padding); + if(!FLAC__metadata_simple_iterator_prev(iterator)) + return false; + FLAC__ASSERT(iterator->offset[iterator->depth] + (off_t)FLAC__STREAM_METADATA_HEADER_LENGTH + (off_t)iterator->length == debug_target_offset); + FLAC__ASSERT(ftello(iterator->file) + (off_t)iterator->length == debug_target_offset); + return true; + } + else { + ret = rewrite_whole_file_(iterator, 0, /*append=*/false); + FLAC__ASSERT(iterator->offset[iterator->depth] + (off_t)FLAC__STREAM_METADATA_HEADER_LENGTH + (off_t)iterator->length == debug_target_offset); + FLAC__ASSERT(ftello(iterator->file) + (off_t)iterator->length == debug_target_offset); + return ret; + } +} + + + +/**************************************************************************** + * + * Level 2 implementation + * + ***************************************************************************/ + + +typedef struct FLAC__Metadata_Node { + FLAC__StreamMetadata *data; + struct FLAC__Metadata_Node *prev, *next; +} FLAC__Metadata_Node; + +struct FLAC__Metadata_Chain { + char *filename; /* will be NULL if using callbacks */ + FLAC__bool is_ogg; + FLAC__Metadata_Node *head; + FLAC__Metadata_Node *tail; + unsigned nodes; + FLAC__Metadata_ChainStatus status; + off_t first_offset, last_offset; + /* + * This is the length of the chain initially read from the FLAC file. + * it is used to compare against the current length to decide whether + * or not the whole file has to be rewritten. + */ + off_t initial_length; + /* @@@ hacky, these are currently only needed by ogg reader */ + FLAC__IOHandle handle; + FLAC__IOCallback_Read read_cb; +}; + +struct FLAC__Metadata_Iterator { + FLAC__Metadata_Chain *chain; + FLAC__Metadata_Node *current; +}; + +FLAC_API const char * const FLAC__Metadata_ChainStatusString[] = { + "FLAC__METADATA_CHAIN_STATUS_OK", + "FLAC__METADATA_CHAIN_STATUS_ILLEGAL_INPUT", + "FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE", + "FLAC__METADATA_CHAIN_STATUS_NOT_A_FLAC_FILE", + "FLAC__METADATA_CHAIN_STATUS_NOT_WRITABLE", + "FLAC__METADATA_CHAIN_STATUS_BAD_METADATA", + "FLAC__METADATA_CHAIN_STATUS_READ_ERROR", + "FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR", + "FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR", + "FLAC__METADATA_CHAIN_STATUS_RENAME_ERROR", + "FLAC__METADATA_CHAIN_STATUS_UNLINK_ERROR", + "FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR", + "FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR", + "FLAC__METADATA_CHAIN_STATUS_INVALID_CALLBACKS", + "FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH", + "FLAC__METADATA_CHAIN_STATUS_WRONG_WRITE_CALL" +}; + + +static FLAC__Metadata_Node *node_new_(void) +{ + return (FLAC__Metadata_Node*)calloc(1, sizeof(FLAC__Metadata_Node)); +} + +static void node_delete_(FLAC__Metadata_Node *node) +{ + FLAC__ASSERT(0 != node); + if(0 != node->data) + FLAC__metadata_object_delete(node->data); + free(node); +} + +static void chain_init_(FLAC__Metadata_Chain *chain) +{ + FLAC__ASSERT(0 != chain); + + chain->filename = 0; + chain->is_ogg = false; + chain->head = chain->tail = 0; + chain->nodes = 0; + chain->status = FLAC__METADATA_CHAIN_STATUS_OK; + chain->initial_length = 0; + chain->read_cb = 0; +} + +static void chain_clear_(FLAC__Metadata_Chain *chain) +{ + FLAC__Metadata_Node *node, *next; + + FLAC__ASSERT(0 != chain); + + for(node = chain->head; node; ) { + next = node->next; + node_delete_(node); + node = next; + } + + if(0 != chain->filename) + free(chain->filename); + + chain_init_(chain); +} + +static void chain_append_node_(FLAC__Metadata_Chain *chain, FLAC__Metadata_Node *node) +{ + FLAC__ASSERT(0 != chain); + FLAC__ASSERT(0 != node); + FLAC__ASSERT(0 != node->data); + + node->next = node->prev = 0; + node->data->is_last = true; + if(0 != chain->tail) + chain->tail->data->is_last = false; + + if(0 == chain->head) + chain->head = node; + else { + FLAC__ASSERT(0 != chain->tail); + chain->tail->next = node; + node->prev = chain->tail; + } + chain->tail = node; + chain->nodes++; +} + +static void chain_remove_node_(FLAC__Metadata_Chain *chain, FLAC__Metadata_Node *node) +{ + FLAC__ASSERT(0 != chain); + FLAC__ASSERT(0 != node); + + if(node == chain->head) + chain->head = node->next; + else + node->prev->next = node->next; + + if(node == chain->tail) + chain->tail = node->prev; + else + node->next->prev = node->prev; + + if(0 != chain->tail) + chain->tail->data->is_last = true; + + chain->nodes--; +} + +static void chain_delete_node_(FLAC__Metadata_Chain *chain, FLAC__Metadata_Node *node) +{ + chain_remove_node_(chain, node); + node_delete_(node); +} + +static off_t chain_calculate_length_(FLAC__Metadata_Chain *chain) +{ + const FLAC__Metadata_Node *node; + off_t length = 0; + for(node = chain->head; node; node = node->next) + length += (FLAC__STREAM_METADATA_HEADER_LENGTH + node->data->length); + return length; +} + +static void iterator_insert_node_(FLAC__Metadata_Iterator *iterator, FLAC__Metadata_Node *node) +{ + FLAC__ASSERT(0 != node); + FLAC__ASSERT(0 != node->data); + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->current); + FLAC__ASSERT(0 != iterator->chain); + FLAC__ASSERT(0 != iterator->chain->head); + FLAC__ASSERT(0 != iterator->chain->tail); + + node->data->is_last = false; + + node->prev = iterator->current->prev; + node->next = iterator->current; + + if(0 == node->prev) + iterator->chain->head = node; + else + node->prev->next = node; + + iterator->current->prev = node; + + iterator->chain->nodes++; +} + +static void iterator_insert_node_after_(FLAC__Metadata_Iterator *iterator, FLAC__Metadata_Node *node) +{ + FLAC__ASSERT(0 != node); + FLAC__ASSERT(0 != node->data); + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->current); + FLAC__ASSERT(0 != iterator->chain); + FLAC__ASSERT(0 != iterator->chain->head); + FLAC__ASSERT(0 != iterator->chain->tail); + + iterator->current->data->is_last = false; + + node->prev = iterator->current; + node->next = iterator->current->next; + + if(0 == node->next) + iterator->chain->tail = node; + else + node->next->prev = node; + + node->prev->next = node; + + iterator->chain->tail->data->is_last = true; + + iterator->chain->nodes++; +} + +/* return true iff node and node->next are both padding */ +static FLAC__bool chain_merge_adjacent_padding_(FLAC__Metadata_Chain *chain, FLAC__Metadata_Node *node) +{ + if(node->data->type == FLAC__METADATA_TYPE_PADDING && 0 != node->next && node->next->data->type == FLAC__METADATA_TYPE_PADDING) { + const unsigned growth = FLAC__STREAM_METADATA_HEADER_LENGTH + node->next->data->length; + node->data->length += growth; + + chain_delete_node_(chain, node->next); + return true; + } + else + return false; +} + +/* Returns the new length of the chain, or 0 if there was an error. */ +/* WATCHOUT: This can get called multiple times before a write, so + * it should still work when this happens. + */ +/* WATCHOUT: Make sure to also update the logic in + * FLAC__metadata_chain_check_if_tempfile_needed() if the logic here changes. + */ +static off_t chain_prepare_for_write_(FLAC__Metadata_Chain *chain, FLAC__bool use_padding) +{ + off_t current_length = chain_calculate_length_(chain); + + if(use_padding) { + /* if the metadata shrank and the last block is padding, we just extend the last padding block */ + if(current_length < chain->initial_length && chain->tail->data->type == FLAC__METADATA_TYPE_PADDING) { + const off_t delta = chain->initial_length - current_length; + chain->tail->data->length += delta; + current_length += delta; + FLAC__ASSERT(current_length == chain->initial_length); + } + /* if the metadata shrank more than 4 bytes then there's room to add another padding block */ + else if(current_length + (off_t)FLAC__STREAM_METADATA_HEADER_LENGTH <= chain->initial_length) { + FLAC__StreamMetadata *padding; + FLAC__Metadata_Node *node; + if(0 == (padding = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING))) { + chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR; + return 0; + } + padding->length = chain->initial_length - (FLAC__STREAM_METADATA_HEADER_LENGTH + current_length); + if(0 == (node = node_new_())) { + FLAC__metadata_object_delete(padding); + chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR; + return 0; + } + node->data = padding; + chain_append_node_(chain, node); + current_length = chain_calculate_length_(chain); + FLAC__ASSERT(current_length == chain->initial_length); + } + /* if the metadata grew but the last block is padding, try cutting the padding to restore the original length so we don't have to rewrite the whole file */ + else if(current_length > chain->initial_length) { + const off_t delta = current_length - chain->initial_length; + if(chain->tail->data->type == FLAC__METADATA_TYPE_PADDING) { + /* if the delta is exactly the size of the last padding block, remove the padding block */ + if((off_t)chain->tail->data->length + (off_t)FLAC__STREAM_METADATA_HEADER_LENGTH == delta) { + chain_delete_node_(chain, chain->tail); + current_length = chain_calculate_length_(chain); + FLAC__ASSERT(current_length == chain->initial_length); + } + /* if there is at least 'delta' bytes of padding, trim the padding down */ + else if((off_t)chain->tail->data->length >= delta) { + chain->tail->data->length -= delta; + current_length -= delta; + FLAC__ASSERT(current_length == chain->initial_length); + } + } + } + } + + return current_length; +} + +static FLAC__bool chain_read_cb_(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb, FLAC__IOCallback_Tell tell_cb) +{ + FLAC__Metadata_Node *node; + + FLAC__ASSERT(0 != chain); + + /* we assume we're already at the beginning of the file */ + + switch(seek_to_first_metadata_block_cb_(handle, read_cb, seek_cb)) { + case 0: + break; + case 1: + chain->status = FLAC__METADATA_CHAIN_STATUS_READ_ERROR; + return false; + case 2: + chain->status = FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR; + return false; + case 3: + chain->status = FLAC__METADATA_CHAIN_STATUS_NOT_A_FLAC_FILE; + return false; + default: + FLAC__ASSERT(0); + return false; + } + + { + FLAC__int64 pos = tell_cb(handle); + if(pos < 0) { + chain->status = FLAC__METADATA_CHAIN_STATUS_READ_ERROR; + return false; + } + chain->first_offset = (off_t)pos; + } + + { + FLAC__bool is_last; + FLAC__MetadataType type; + unsigned length; + + do { + node = node_new_(); + if(0 == node) { + chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR; + return false; + } + + if(!read_metadata_block_header_cb_(handle, read_cb, &is_last, &type, &length)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_READ_ERROR; + return false; + } + + node->data = FLAC__metadata_object_new(type); + if(0 == node->data) { + node_delete_(node); + chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR; + return false; + } + + node->data->is_last = is_last; + node->data->length = length; + + chain->status = get_equivalent_status_(read_metadata_block_data_cb_(handle, read_cb, seek_cb, node->data)); + if(chain->status != FLAC__METADATA_CHAIN_STATUS_OK) { + node_delete_(node); + return false; + } + chain_append_node_(chain, node); + } while(!is_last); + } + + { + FLAC__int64 pos = tell_cb(handle); + if(pos < 0) { + chain->status = FLAC__METADATA_CHAIN_STATUS_READ_ERROR; + return false; + } + chain->last_offset = (off_t)pos; + } + + chain->initial_length = chain_calculate_length_(chain); + + return true; +} + +static +FLAC__StreamDecoderReadStatus chain_read_ogg_read_cb_(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data) +{ + FLAC__Metadata_Chain *chain = (FLAC__Metadata_Chain*)client_data; + (void)decoder; + if(*bytes > 0 && chain->status == FLAC__METADATA_CHAIN_STATUS_OK) { + *bytes = chain->read_cb(buffer, sizeof(FLAC__byte), *bytes, chain->handle); + if(*bytes == 0) + return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; + else + return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; + } + else + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; +} + +static FLAC__StreamDecoderWriteStatus chain_read_ogg_write_cb_(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data) +{ + (void)decoder, (void)frame, (void)buffer, (void)client_data; + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; +} + +static void chain_read_ogg_metadata_cb_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) +{ + FLAC__Metadata_Chain *chain = (FLAC__Metadata_Chain*)client_data; + FLAC__Metadata_Node *node; + + (void)decoder; + + node = node_new_(); + if(0 == node) { + chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR; + return; + } + + node->data = FLAC__metadata_object_clone(metadata); + if(0 == node->data) { + node_delete_(node); + chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR; + return; + } + + chain_append_node_(chain, node); +} + +static void chain_read_ogg_error_cb_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) +{ + FLAC__Metadata_Chain *chain = (FLAC__Metadata_Chain*)client_data; + (void)decoder, (void)status; + chain->status = FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR; /*@@@ maybe needs better error code */ +} + +static FLAC__bool chain_read_ogg_cb_(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb) +{ + FLAC__StreamDecoder *decoder; + + FLAC__ASSERT(0 != chain); + + /* we assume we're already at the beginning of the file */ + + chain->handle = handle; + chain->read_cb = read_cb; + if(0 == (decoder = FLAC__stream_decoder_new())) { + chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR; + return false; + } + FLAC__stream_decoder_set_metadata_respond_all(decoder); + if(FLAC__stream_decoder_init_ogg_stream(decoder, chain_read_ogg_read_cb_, /*seek_callback=*/0, /*tell_callback=*/0, /*length_callback=*/0, /*eof_callback=*/0, chain_read_ogg_write_cb_, chain_read_ogg_metadata_cb_, chain_read_ogg_error_cb_, chain) != FLAC__STREAM_DECODER_INIT_STATUS_OK) { + FLAC__stream_decoder_delete(decoder); + chain->status = FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR; /*@@@ maybe needs better error code */ + return false; + } + + chain->first_offset = 0; /*@@@ wrong; will need to be set correctly to implement metadata writing for Ogg FLAC */ + + if(!FLAC__stream_decoder_process_until_end_of_metadata(decoder)) + chain->status = FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR; /*@@@ maybe needs better error code */ + if(chain->status != FLAC__METADATA_CHAIN_STATUS_OK) { + FLAC__stream_decoder_delete(decoder); + return false; + } + + FLAC__stream_decoder_delete(decoder); + + chain->last_offset = 0; /*@@@ wrong; will need to be set correctly to implement metadata writing for Ogg FLAC */ + + chain->initial_length = chain_calculate_length_(chain); + + return true; +} + +static FLAC__bool chain_rewrite_metadata_in_place_cb_(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, FLAC__IOCallback_Seek seek_cb) +{ + FLAC__Metadata_Node *node; + + FLAC__ASSERT(0 != chain); + FLAC__ASSERT(0 != chain->head); + + if(0 != seek_cb(handle, chain->first_offset, SEEK_SET)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR; + return false; + } + + for(node = chain->head; node; node = node->next) { + if(!write_metadata_block_header_cb_(handle, write_cb, node->data)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR; + return false; + } + if(!write_metadata_block_data_cb_(handle, write_cb, node->data)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR; + return false; + } + } + + /*FLAC__ASSERT(fflush(), ftello() == chain->last_offset);*/ + + chain->status = FLAC__METADATA_CHAIN_STATUS_OK; + return true; +} + +static FLAC__bool chain_rewrite_metadata_in_place_(FLAC__Metadata_Chain *chain) +{ + FILE *file; + FLAC__bool ret; + + FLAC__ASSERT(0 != chain->filename); + + if(0 == (file = fopen(chain->filename, "r+b"))) { + chain->status = FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE; + return false; + } + + /* chain_rewrite_metadata_in_place_cb_() sets chain->status for us */ + ret = chain_rewrite_metadata_in_place_cb_(chain, (FLAC__IOHandle)file, (FLAC__IOCallback_Write)fwrite, fseek_wrapper_); + + fclose(file); + + return ret; +} + +static FLAC__bool chain_rewrite_file_(FLAC__Metadata_Chain *chain, const char *tempfile_path_prefix) +{ + FILE *f, *tempfile; + char *tempfilename; + FLAC__Metadata_SimpleIteratorStatus status; + const FLAC__Metadata_Node *node; + + FLAC__ASSERT(0 != chain); + FLAC__ASSERT(0 != chain->filename); + FLAC__ASSERT(0 != chain->head); + + /* copy the file prefix (data up to first metadata block */ + if(0 == (f = fopen(chain->filename, "rb"))) { + chain->status = FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE; + return false; + } + if(!open_tempfile_(chain->filename, tempfile_path_prefix, &tempfile, &tempfilename, &status)) { + chain->status = get_equivalent_status_(status); + cleanup_tempfile_(&tempfile, &tempfilename); + return false; + } + if(!copy_n_bytes_from_file_(f, tempfile, chain->first_offset, &status)) { + chain->status = get_equivalent_status_(status); + cleanup_tempfile_(&tempfile, &tempfilename); + return false; + } + + /* write the metadata */ + for(node = chain->head; node; node = node->next) { + if(!write_metadata_block_header_(tempfile, &status, node->data)) { + chain->status = get_equivalent_status_(status); + return false; + } + if(!write_metadata_block_data_(tempfile, &status, node->data)) { + chain->status = get_equivalent_status_(status); + return false; + } + } + /*FLAC__ASSERT(fflush(), ftello() == chain->last_offset);*/ + + /* copy the file postfix (everything after the metadata) */ + if(0 != fseeko(f, chain->last_offset, SEEK_SET)) { + cleanup_tempfile_(&tempfile, &tempfilename); + chain->status = FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR; + return false; + } + if(!copy_remaining_bytes_from_file_(f, tempfile, &status)) { + cleanup_tempfile_(&tempfile, &tempfilename); + chain->status = get_equivalent_status_(status); + return false; + } + + /* move the tempfile on top of the original */ + (void)fclose(f); + if(!transport_tempfile_(chain->filename, &tempfile, &tempfilename, &status)) + return false; + + return true; +} + +/* assumes 'handle' is already at beginning of file */ +static FLAC__bool chain_rewrite_file_cb_(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb, FLAC__IOCallback_Eof eof_cb, FLAC__IOHandle temp_handle, FLAC__IOCallback_Write temp_write_cb) +{ + FLAC__Metadata_SimpleIteratorStatus status; + const FLAC__Metadata_Node *node; + + FLAC__ASSERT(0 != chain); + FLAC__ASSERT(0 == chain->filename); + FLAC__ASSERT(0 != chain->head); + + /* copy the file prefix (data up to first metadata block */ + if(!copy_n_bytes_from_file_cb_(handle, read_cb, temp_handle, temp_write_cb, chain->first_offset, &status)) { + chain->status = get_equivalent_status_(status); + return false; + } + + /* write the metadata */ + for(node = chain->head; node; node = node->next) { + if(!write_metadata_block_header_cb_(temp_handle, temp_write_cb, node->data)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR; + return false; + } + if(!write_metadata_block_data_cb_(temp_handle, temp_write_cb, node->data)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR; + return false; + } + } + /*FLAC__ASSERT(fflush(), ftello() == chain->last_offset);*/ + + /* copy the file postfix (everything after the metadata) */ + if(0 != seek_cb(handle, chain->last_offset, SEEK_SET)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR; + return false; + } + if(!copy_remaining_bytes_from_file_cb_(handle, read_cb, eof_cb, temp_handle, temp_write_cb, &status)) { + chain->status = get_equivalent_status_(status); + return false; + } + + return true; +} + +FLAC_API FLAC__Metadata_Chain *FLAC__metadata_chain_new(void) +{ + FLAC__Metadata_Chain *chain = (FLAC__Metadata_Chain*)calloc(1, sizeof(FLAC__Metadata_Chain)); + + if(0 != chain) + chain_init_(chain); + + return chain; +} + +FLAC_API void FLAC__metadata_chain_delete(FLAC__Metadata_Chain *chain) +{ + FLAC__ASSERT(0 != chain); + + chain_clear_(chain); + + free(chain); +} + +FLAC_API FLAC__Metadata_ChainStatus FLAC__metadata_chain_status(FLAC__Metadata_Chain *chain) +{ + FLAC__Metadata_ChainStatus status; + + FLAC__ASSERT(0 != chain); + + status = chain->status; + chain->status = FLAC__METADATA_CHAIN_STATUS_OK; + return status; +} + +static FLAC__bool chain_read_(FLAC__Metadata_Chain *chain, const char *filename, FLAC__bool is_ogg) +{ + FILE *file; + FLAC__bool ret; + + FLAC__ASSERT(0 != chain); + FLAC__ASSERT(0 != filename); + + chain_clear_(chain); + + if(0 == (chain->filename = strdup(filename))) { + chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR; + return false; + } + + chain->is_ogg = is_ogg; + + if(0 == (file = fopen(filename, "rb"))) { + chain->status = FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE; + return false; + } + + /* the function also sets chain->status for us */ + ret = is_ogg? + chain_read_ogg_cb_(chain, file, (FLAC__IOCallback_Read)fread) : + chain_read_cb_(chain, file, (FLAC__IOCallback_Read)fread, fseek_wrapper_, ftell_wrapper_) + ; + + fclose(file); + + return ret; +} + +FLAC_API FLAC__bool FLAC__metadata_chain_read(FLAC__Metadata_Chain *chain, const char *filename) +{ + return chain_read_(chain, filename, /*is_ogg=*/false); +} + +FLAC_API FLAC__bool FLAC__metadata_chain_read_ogg(FLAC__Metadata_Chain *chain, const char *filename) +{ + return chain_read_(chain, filename, /*is_ogg=*/true); +} + +static FLAC__bool chain_read_with_callbacks_(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks, FLAC__bool is_ogg) +{ + FLAC__bool ret; + + FLAC__ASSERT(0 != chain); + + chain_clear_(chain); + + if (0 == callbacks.read || 0 == callbacks.seek || 0 == callbacks.tell) { + chain->status = FLAC__METADATA_CHAIN_STATUS_INVALID_CALLBACKS; + return false; + } + + chain->is_ogg = is_ogg; + + /* rewind */ + if(0 != callbacks.seek(handle, 0, SEEK_SET)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR; + return false; + } + + /* the function also sets chain->status for us */ + ret = is_ogg? + chain_read_ogg_cb_(chain, handle, callbacks.read) : + chain_read_cb_(chain, handle, callbacks.read, callbacks.seek, callbacks.tell) + ; + + return ret; +} + +FLAC_API FLAC__bool FLAC__metadata_chain_read_with_callbacks(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks) +{ + return chain_read_with_callbacks_(chain, handle, callbacks, /*is_ogg=*/false); +} + +FLAC_API FLAC__bool FLAC__metadata_chain_read_ogg_with_callbacks(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks) +{ + return chain_read_with_callbacks_(chain, handle, callbacks, /*is_ogg=*/true); +} + +FLAC_API FLAC__bool FLAC__metadata_chain_check_if_tempfile_needed(FLAC__Metadata_Chain *chain, FLAC__bool use_padding) +{ + /* This does all the same checks that are in chain_prepare_for_write_() + * but doesn't actually alter the chain. Make sure to update the logic + * here if chain_prepare_for_write_() changes. + */ + const off_t current_length = chain_calculate_length_(chain); + + FLAC__ASSERT(0 != chain); + + if(use_padding) { + /* if the metadata shrank and the last block is padding, we just extend the last padding block */ + if(current_length < chain->initial_length && chain->tail->data->type == FLAC__METADATA_TYPE_PADDING) + return false; + /* if the metadata shrank more than 4 bytes then there's room to add another padding block */ + else if(current_length + (off_t)FLAC__STREAM_METADATA_HEADER_LENGTH <= chain->initial_length) + return false; + /* if the metadata grew but the last block is padding, try cutting the padding to restore the original length so we don't have to rewrite the whole file */ + else if(current_length > chain->initial_length) { + const off_t delta = current_length - chain->initial_length; + if(chain->tail->data->type == FLAC__METADATA_TYPE_PADDING) { + /* if the delta is exactly the size of the last padding block, remove the padding block */ + if((off_t)chain->tail->data->length + (off_t)FLAC__STREAM_METADATA_HEADER_LENGTH == delta) + return false; + /* if there is at least 'delta' bytes of padding, trim the padding down */ + else if((off_t)chain->tail->data->length >= delta) + return false; + } + } + } + + return (current_length != chain->initial_length); +} + +FLAC_API FLAC__bool FLAC__metadata_chain_write(FLAC__Metadata_Chain *chain, FLAC__bool use_padding, FLAC__bool preserve_file_stats) +{ + struct stat stats; + const char *tempfile_path_prefix = 0; + off_t current_length; + + FLAC__ASSERT(0 != chain); + + if (chain->is_ogg) { /* cannot write back to Ogg FLAC yet */ + chain->status = FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR; + return false; + } + + if (0 == chain->filename) { + chain->status = FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH; + return false; + } + + current_length = chain_prepare_for_write_(chain, use_padding); + + /* a return value of 0 means there was an error; chain->status is already set */ + if (0 == current_length) + return false; + + if(preserve_file_stats) + get_file_stats_(chain->filename, &stats); + + if(current_length == chain->initial_length) { + if(!chain_rewrite_metadata_in_place_(chain)) + return false; + } + else { + if(!chain_rewrite_file_(chain, tempfile_path_prefix)) + return false; + + /* recompute lengths and offsets */ + { + const FLAC__Metadata_Node *node; + chain->initial_length = current_length; + chain->last_offset = chain->first_offset; + for(node = chain->head; node; node = node->next) + chain->last_offset += (FLAC__STREAM_METADATA_HEADER_LENGTH + node->data->length); + } + } + + if(preserve_file_stats) + set_file_stats_(chain->filename, &stats); + + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_chain_write_with_callbacks(FLAC__Metadata_Chain *chain, FLAC__bool use_padding, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks) +{ + off_t current_length; + + FLAC__ASSERT(0 != chain); + + if (chain->is_ogg) { /* cannot write back to Ogg FLAC yet */ + chain->status = FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR; + return false; + } + + if (0 != chain->filename) { + chain->status = FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH; + return false; + } + + if (0 == callbacks.write || 0 == callbacks.seek) { + chain->status = FLAC__METADATA_CHAIN_STATUS_INVALID_CALLBACKS; + return false; + } + + if (FLAC__metadata_chain_check_if_tempfile_needed(chain, use_padding)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_WRONG_WRITE_CALL; + return false; + } + + current_length = chain_prepare_for_write_(chain, use_padding); + + /* a return value of 0 means there was an error; chain->status is already set */ + if (0 == current_length) + return false; + + FLAC__ASSERT(current_length == chain->initial_length); + + return chain_rewrite_metadata_in_place_cb_(chain, handle, callbacks.write, callbacks.seek); +} + +FLAC_API FLAC__bool FLAC__metadata_chain_write_with_callbacks_and_tempfile(FLAC__Metadata_Chain *chain, FLAC__bool use_padding, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks, FLAC__IOHandle temp_handle, FLAC__IOCallbacks temp_callbacks) +{ + off_t current_length; + + FLAC__ASSERT(0 != chain); + + if (chain->is_ogg) { /* cannot write back to Ogg FLAC yet */ + chain->status = FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR; + return false; + } + + if (0 != chain->filename) { + chain->status = FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH; + return false; + } + + if (0 == callbacks.read || 0 == callbacks.seek || 0 == callbacks.eof) { + chain->status = FLAC__METADATA_CHAIN_STATUS_INVALID_CALLBACKS; + return false; + } + if (0 == temp_callbacks.write) { + chain->status = FLAC__METADATA_CHAIN_STATUS_INVALID_CALLBACKS; + return false; + } + + if (!FLAC__metadata_chain_check_if_tempfile_needed(chain, use_padding)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_WRONG_WRITE_CALL; + return false; + } + + current_length = chain_prepare_for_write_(chain, use_padding); + + /* a return value of 0 means there was an error; chain->status is already set */ + if (0 == current_length) + return false; + + FLAC__ASSERT(current_length != chain->initial_length); + + /* rewind */ + if(0 != callbacks.seek(handle, 0, SEEK_SET)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR; + return false; + } + + if(!chain_rewrite_file_cb_(chain, handle, callbacks.read, callbacks.seek, callbacks.eof, temp_handle, temp_callbacks.write)) + return false; + + /* recompute lengths and offsets */ + { + const FLAC__Metadata_Node *node; + chain->initial_length = current_length; + chain->last_offset = chain->first_offset; + for(node = chain->head; node; node = node->next) + chain->last_offset += (FLAC__STREAM_METADATA_HEADER_LENGTH + node->data->length); + } + + return true; +} + +FLAC_API void FLAC__metadata_chain_merge_padding(FLAC__Metadata_Chain *chain) +{ + FLAC__Metadata_Node *node; + + FLAC__ASSERT(0 != chain); + + for(node = chain->head; node; ) { + if(!chain_merge_adjacent_padding_(chain, node)) + node = node->next; + } +} + +FLAC_API void FLAC__metadata_chain_sort_padding(FLAC__Metadata_Chain *chain) +{ + FLAC__Metadata_Node *node, *save; + unsigned i; + + FLAC__ASSERT(0 != chain); + + /* + * Don't try and be too smart... this simple algo is good enough for + * the small number of nodes that we deal with. + */ + for(i = 0, node = chain->head; i < chain->nodes; i++) { + if(node->data->type == FLAC__METADATA_TYPE_PADDING) { + save = node->next; + chain_remove_node_(chain, node); + chain_append_node_(chain, node); + node = save; + } + else { + node = node->next; + } + } + + FLAC__metadata_chain_merge_padding(chain); +} + + +FLAC_API FLAC__Metadata_Iterator *FLAC__metadata_iterator_new(void) +{ + FLAC__Metadata_Iterator *iterator = (FLAC__Metadata_Iterator*)calloc(1, sizeof(FLAC__Metadata_Iterator)); + + /* calloc() implies: + iterator->current = 0; + iterator->chain = 0; + */ + + return iterator; +} + +FLAC_API void FLAC__metadata_iterator_delete(FLAC__Metadata_Iterator *iterator) +{ + FLAC__ASSERT(0 != iterator); + + free(iterator); +} + +FLAC_API void FLAC__metadata_iterator_init(FLAC__Metadata_Iterator *iterator, FLAC__Metadata_Chain *chain) +{ + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != chain); + FLAC__ASSERT(0 != chain->head); + + iterator->chain = chain; + iterator->current = chain->head; +} + +FLAC_API FLAC__bool FLAC__metadata_iterator_next(FLAC__Metadata_Iterator *iterator) +{ + FLAC__ASSERT(0 != iterator); + + if(0 == iterator->current || 0 == iterator->current->next) + return false; + + iterator->current = iterator->current->next; + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_iterator_prev(FLAC__Metadata_Iterator *iterator) +{ + FLAC__ASSERT(0 != iterator); + + if(0 == iterator->current || 0 == iterator->current->prev) + return false; + + iterator->current = iterator->current->prev; + return true; +} + +FLAC_API FLAC__MetadataType FLAC__metadata_iterator_get_block_type(const FLAC__Metadata_Iterator *iterator) +{ + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->current); + FLAC__ASSERT(0 != iterator->current->data); + + return iterator->current->data->type; +} + +FLAC_API FLAC__StreamMetadata *FLAC__metadata_iterator_get_block(FLAC__Metadata_Iterator *iterator) +{ + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->current); + + return iterator->current->data; +} + +FLAC_API FLAC__bool FLAC__metadata_iterator_set_block(FLAC__Metadata_Iterator *iterator, FLAC__StreamMetadata *block) +{ + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != block); + return FLAC__metadata_iterator_delete_block(iterator, false) && FLAC__metadata_iterator_insert_block_after(iterator, block); +} + +FLAC_API FLAC__bool FLAC__metadata_iterator_delete_block(FLAC__Metadata_Iterator *iterator, FLAC__bool replace_with_padding) +{ + FLAC__Metadata_Node *save; + + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->current); + + if(0 == iterator->current->prev) { + FLAC__ASSERT(iterator->current->data->type == FLAC__METADATA_TYPE_STREAMINFO); + return false; + } + + save = iterator->current->prev; + + if(replace_with_padding) { + FLAC__metadata_object_delete_data(iterator->current->data); + iterator->current->data->type = FLAC__METADATA_TYPE_PADDING; + } + else { + chain_delete_node_(iterator->chain, iterator->current); + } + + iterator->current = save; + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_iterator_insert_block_before(FLAC__Metadata_Iterator *iterator, FLAC__StreamMetadata *block) +{ + FLAC__Metadata_Node *node; + + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->current); + FLAC__ASSERT(0 != block); + + if(block->type == FLAC__METADATA_TYPE_STREAMINFO) + return false; + + if(0 == iterator->current->prev) { + FLAC__ASSERT(iterator->current->data->type == FLAC__METADATA_TYPE_STREAMINFO); + return false; + } + + if(0 == (node = node_new_())) + return false; + + node->data = block; + iterator_insert_node_(iterator, node); + iterator->current = node; + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_iterator_insert_block_after(FLAC__Metadata_Iterator *iterator, FLAC__StreamMetadata *block) +{ + FLAC__Metadata_Node *node; + + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->current); + FLAC__ASSERT(0 != block); + + if(block->type == FLAC__METADATA_TYPE_STREAMINFO) + return false; + + if(0 == (node = node_new_())) + return false; + + node->data = block; + iterator_insert_node_after_(iterator, node); + iterator->current = node; + return true; +} + + +/**************************************************************************** + * + * Local function definitions + * + ***************************************************************************/ + +void pack_uint32_(FLAC__uint32 val, FLAC__byte *b, unsigned bytes) +{ + unsigned i; + + b += bytes; + + for(i = 0; i < bytes; i++) { + *(--b) = (FLAC__byte)(val & 0xff); + val >>= 8; + } +} + +void pack_uint32_little_endian_(FLAC__uint32 val, FLAC__byte *b, unsigned bytes) +{ + unsigned i; + + for(i = 0; i < bytes; i++) { + *(b++) = (FLAC__byte)(val & 0xff); + val >>= 8; + } +} + +void pack_uint64_(FLAC__uint64 val, FLAC__byte *b, unsigned bytes) +{ + unsigned i; + + b += bytes; + + for(i = 0; i < bytes; i++) { + *(--b) = (FLAC__byte)(val & 0xff); + val >>= 8; + } +} + +FLAC__uint32 unpack_uint32_(FLAC__byte *b, unsigned bytes) +{ + FLAC__uint32 ret = 0; + unsigned i; + + for(i = 0; i < bytes; i++) + ret = (ret << 8) | (FLAC__uint32)(*b++); + + return ret; +} + +FLAC__uint32 unpack_uint32_little_endian_(FLAC__byte *b, unsigned bytes) +{ + FLAC__uint32 ret = 0; + unsigned i; + + b += bytes; + + for(i = 0; i < bytes; i++) + ret = (ret << 8) | (FLAC__uint32)(*--b); + + return ret; +} + +FLAC__uint64 unpack_uint64_(FLAC__byte *b, unsigned bytes) +{ + FLAC__uint64 ret = 0; + unsigned i; + + for(i = 0; i < bytes; i++) + ret = (ret << 8) | (FLAC__uint64)(*b++); + + return ret; +} + +FLAC__bool read_metadata_block_header_(FLAC__Metadata_SimpleIterator *iterator) +{ + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->file); + + if(!read_metadata_block_header_cb_((FLAC__IOHandle)iterator->file, (FLAC__IOCallback_Read)fread, &iterator->is_last, &iterator->type, &iterator->length)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + return false; + } + + return true; +} + +FLAC__bool read_metadata_block_data_(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block) +{ + FLAC__ASSERT(0 != iterator); + FLAC__ASSERT(0 != iterator->file); + + iterator->status = read_metadata_block_data_cb_((FLAC__IOHandle)iterator->file, (FLAC__IOCallback_Read)fread, fseek_wrapper_, block); + + return (iterator->status == FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK); +} + +FLAC__bool read_metadata_block_header_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__bool *is_last, FLAC__MetadataType *type, unsigned *length) +{ + FLAC__byte raw_header[FLAC__STREAM_METADATA_HEADER_LENGTH]; + + if(read_cb(raw_header, 1, FLAC__STREAM_METADATA_HEADER_LENGTH, handle) != FLAC__STREAM_METADATA_HEADER_LENGTH) + return false; + + *is_last = raw_header[0] & 0x80? true : false; + *type = (FLAC__MetadataType)(raw_header[0] & 0x7f); + *length = unpack_uint32_(raw_header + 1, 3); + + /* Note that we don't check: + * if(iterator->type >= FLAC__METADATA_TYPE_UNDEFINED) + * we just will read in an opaque block + */ + + return true; +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb, FLAC__StreamMetadata *block) +{ + switch(block->type) { + case FLAC__METADATA_TYPE_STREAMINFO: + return read_metadata_block_data_streaminfo_cb_(handle, read_cb, &block->data.stream_info); + case FLAC__METADATA_TYPE_PADDING: + return read_metadata_block_data_padding_cb_(handle, seek_cb, &block->data.padding, block->length); + case FLAC__METADATA_TYPE_APPLICATION: + return read_metadata_block_data_application_cb_(handle, read_cb, &block->data.application, block->length); + case FLAC__METADATA_TYPE_SEEKTABLE: + return read_metadata_block_data_seektable_cb_(handle, read_cb, &block->data.seek_table, block->length); + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + return read_metadata_block_data_vorbis_comment_cb_(handle, read_cb, &block->data.vorbis_comment); + case FLAC__METADATA_TYPE_CUESHEET: + return read_metadata_block_data_cuesheet_cb_(handle, read_cb, &block->data.cue_sheet); + case FLAC__METADATA_TYPE_PICTURE: + return read_metadata_block_data_picture_cb_(handle, read_cb, &block->data.picture); + default: + return read_metadata_block_data_unknown_cb_(handle, read_cb, &block->data.unknown, block->length); + } +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_streaminfo_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_StreamInfo *block) +{ + FLAC__byte buffer[FLAC__STREAM_METADATA_STREAMINFO_LENGTH], *b; + + if(read_cb(buffer, 1, FLAC__STREAM_METADATA_STREAMINFO_LENGTH, handle) != FLAC__STREAM_METADATA_STREAMINFO_LENGTH) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + + b = buffer; + + /* we are using hardcoded numbers for simplicity but we should + * probably eventually write a bit-level unpacker and use the + * _STREAMINFO_ constants. + */ + block->min_blocksize = unpack_uint32_(b, 2); b += 2; + block->max_blocksize = unpack_uint32_(b, 2); b += 2; + block->min_framesize = unpack_uint32_(b, 3); b += 3; + block->max_framesize = unpack_uint32_(b, 3); b += 3; + block->sample_rate = (unpack_uint32_(b, 2) << 4) | ((unsigned)(b[2] & 0xf0) >> 4); + block->channels = (unsigned)((b[2] & 0x0e) >> 1) + 1; + block->bits_per_sample = ((((unsigned)(b[2] & 0x01)) << 4) | (((unsigned)(b[3] & 0xf0)) >> 4)) + 1; + block->total_samples = (((FLAC__uint64)(b[3] & 0x0f)) << 32) | unpack_uint64_(b+4, 4); + memcpy(block->md5sum, b+8, 16); + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_padding_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Seek seek_cb, FLAC__StreamMetadata_Padding *block, unsigned block_length) +{ + (void)block; /* nothing to do; we don't care about reading the padding bytes */ + + if(0 != seek_cb(handle, block_length, SEEK_CUR)) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_application_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_Application *block, unsigned block_length) +{ + const unsigned id_bytes = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8; + + if(read_cb(block->id, 1, id_bytes, handle) != id_bytes) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + + block_length -= id_bytes; + + if(block_length == 0) { + block->data = 0; + } + else { + if(0 == (block->data = (FLAC__byte*)malloc(block_length))) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + + if(read_cb(block->data, 1, block_length, handle) != block_length) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + } + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_seektable_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_SeekTable *block, unsigned block_length) +{ + unsigned i; + FLAC__byte buffer[FLAC__STREAM_METADATA_SEEKPOINT_LENGTH]; + + FLAC__ASSERT(block_length % FLAC__STREAM_METADATA_SEEKPOINT_LENGTH == 0); + + block->num_points = block_length / FLAC__STREAM_METADATA_SEEKPOINT_LENGTH; + + if(block->num_points == 0) + block->points = 0; + else if(0 == (block->points = (FLAC__StreamMetadata_SeekPoint*)malloc(block->num_points * sizeof(FLAC__StreamMetadata_SeekPoint)))) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + + for(i = 0; i < block->num_points; i++) { + if(read_cb(buffer, 1, FLAC__STREAM_METADATA_SEEKPOINT_LENGTH, handle) != FLAC__STREAM_METADATA_SEEKPOINT_LENGTH) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + /* some MAGIC NUMBERs here */ + block->points[i].sample_number = unpack_uint64_(buffer, 8); + block->points[i].stream_offset = unpack_uint64_(buffer+8, 8); + block->points[i].frame_samples = unpack_uint32_(buffer+16, 2); + } + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_vorbis_comment_entry_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_VorbisComment_Entry *entry) +{ + const unsigned entry_length_len = FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN / 8; + FLAC__byte buffer[4]; /* magic number is asserted below */ + + FLAC__ASSERT(FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN / 8 == sizeof(buffer)); + + if(read_cb(buffer, 1, entry_length_len, handle) != entry_length_len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + entry->length = unpack_uint32_little_endian_(buffer, entry_length_len); + + if(0 != entry->entry) + free(entry->entry); + + if(entry->length == 0) { + entry->entry = 0; + } + else { + if(0 == (entry->entry = (FLAC__byte*)malloc(entry->length+1))) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + + if(read_cb(entry->entry, 1, entry->length, handle) != entry->length) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + + entry->entry[entry->length] = '\0'; + } + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_vorbis_comment_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_VorbisComment *block) +{ + unsigned i; + FLAC__Metadata_SimpleIteratorStatus status; + const unsigned num_comments_len = FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN / 8; + FLAC__byte buffer[4]; /* magic number is asserted below */ + + FLAC__ASSERT(FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN / 8 == sizeof(buffer)); + + if(FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK != (status = read_metadata_block_data_vorbis_comment_entry_cb_(handle, read_cb, &(block->vendor_string)))) + return status; + + if(read_cb(buffer, 1, num_comments_len, handle) != num_comments_len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + block->num_comments = unpack_uint32_little_endian_(buffer, num_comments_len); + + if(block->num_comments == 0) { + block->comments = 0; + } + else if(0 == (block->comments = (FLAC__StreamMetadata_VorbisComment_Entry*)calloc(block->num_comments, sizeof(FLAC__StreamMetadata_VorbisComment_Entry)))) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + + for(i = 0; i < block->num_comments; i++) { + if(FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK != (status = read_metadata_block_data_vorbis_comment_entry_cb_(handle, read_cb, block->comments + i))) + return status; + } + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_cuesheet_track_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_CueSheet_Track *track) +{ + unsigned i, len; + FLAC__byte buffer[32]; /* asserted below that this is big enough */ + + FLAC__ASSERT(sizeof(buffer) >= sizeof(FLAC__uint64)); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN/8); + FLAC__ASSERT(sizeof(buffer) >= (FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN) / 8); + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + track->offset = unpack_uint64_(buffer, len); + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + track->number = (FLAC__byte)unpack_uint32_(buffer, len); + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN / 8; + if(read_cb(track->isrc, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + + FLAC__ASSERT((FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN) % 8 == 0); + len = (FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN) / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN == 1); + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN == 1); + track->type = buffer[0] >> 7; + track->pre_emphasis = (buffer[0] >> 6) & 1; + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + track->num_indices = (FLAC__byte)unpack_uint32_(buffer, len); + + if(track->num_indices == 0) { + track->indices = 0; + } + else if(0 == (track->indices = (FLAC__StreamMetadata_CueSheet_Index*)calloc(track->num_indices, sizeof(FLAC__StreamMetadata_CueSheet_Index)))) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + + for(i = 0; i < track->num_indices; i++) { + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + track->indices[i].offset = unpack_uint64_(buffer, len); + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + track->indices[i].number = (FLAC__byte)unpack_uint32_(buffer, len); + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + } + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_cuesheet_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_CueSheet *block) +{ + unsigned i, len; + FLAC__Metadata_SimpleIteratorStatus status; + FLAC__byte buffer[1024]; /* MSVC needs a constant expression so we put a magic number and assert */ + + FLAC__ASSERT((FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN + FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN)/8 <= sizeof(buffer)); + FLAC__ASSERT(sizeof(FLAC__uint64) <= sizeof(buffer)); + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN / 8; + if(read_cb(block->media_catalog_number, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + block->lead_in = unpack_uint64_(buffer, len); + + FLAC__ASSERT((FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN + FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN) % 8 == 0); + len = (FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN + FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN) / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + block->is_cd = buffer[0]&0x80? true : false; + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + block->num_tracks = unpack_uint32_(buffer, len); + + if(block->num_tracks == 0) { + block->tracks = 0; + } + else if(0 == (block->tracks = (FLAC__StreamMetadata_CueSheet_Track*)calloc(block->num_tracks, sizeof(FLAC__StreamMetadata_CueSheet_Track)))) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + + for(i = 0; i < block->num_tracks; i++) { + if(FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK != (status = read_metadata_block_data_cuesheet_track_cb_(handle, read_cb, block->tracks + i))) + return status; + } + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +static +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_picture_cstring_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__byte **data, FLAC__uint32 *length, FLAC__uint32 length_len) +{ + FLAC__byte buffer[sizeof(FLAC__uint32)]; + + FLAC__ASSERT(0 != data); + FLAC__ASSERT(length_len%8 == 0); + + length_len /= 8; /* convert to bytes */ + + FLAC__ASSERT(sizeof(buffer) >= length_len); + + if(read_cb(buffer, 1, length_len, handle) != length_len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + *length = unpack_uint32_(buffer, length_len); + + if(0 != *data) + free(*data); + + if(0 == (*data = (FLAC__byte*)malloc(*length+1))) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + + if(*length > 0) { + if(read_cb(*data, 1, *length, handle) != *length) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + } + + (*data)[*length] = '\0'; + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_picture_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_Picture *block) +{ + FLAC__Metadata_SimpleIteratorStatus status; + FLAC__byte buffer[4]; /* asserted below that this is big enough */ + FLAC__uint32 len; + + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_PICTURE_TYPE_LEN/8); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN/8); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN/8); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN/8); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_PICTURE_COLORS_LEN/8); + + FLAC__ASSERT(FLAC__STREAM_METADATA_PICTURE_TYPE_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_PICTURE_TYPE_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + block->type = unpack_uint32_(buffer, len); + + if((status = read_metadata_block_data_picture_cstring_cb_(handle, read_cb, (FLAC__byte**)(&(block->mime_type)), &len, FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN)) != FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK) + return status; + + if((status = read_metadata_block_data_picture_cstring_cb_(handle, read_cb, &(block->description), &len, FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN)) != FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK) + return status; + + FLAC__ASSERT(FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + block->width = unpack_uint32_(buffer, len); + + FLAC__ASSERT(FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + block->height = unpack_uint32_(buffer, len); + + FLAC__ASSERT(FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + block->depth = unpack_uint32_(buffer, len); + + FLAC__ASSERT(FLAC__STREAM_METADATA_PICTURE_COLORS_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_PICTURE_COLORS_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + block->colors = unpack_uint32_(buffer, len); + + /* for convenience we use read_metadata_block_data_picture_cstring_cb_() even though it adds an extra terminating NUL we don't use */ + if((status = read_metadata_block_data_picture_cstring_cb_(handle, read_cb, &(block->data), &(block->data_length), FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN)) != FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK) + return status; + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_unknown_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_Unknown *block, unsigned block_length) +{ + if(block_length == 0) { + block->data = 0; + } + else { + if(0 == (block->data = (FLAC__byte*)malloc(block_length))) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + + if(read_cb(block->data, 1, block_length, handle) != block_length) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + } + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +FLAC__bool write_metadata_block_header_(FILE *file, FLAC__Metadata_SimpleIteratorStatus *status, const FLAC__StreamMetadata *block) +{ + FLAC__ASSERT(0 != file); + FLAC__ASSERT(0 != status); + + if(!write_metadata_block_header_cb_((FLAC__IOHandle)file, (FLAC__IOCallback_Write)fwrite, block)) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR; + return false; + } + + return true; +} + +FLAC__bool write_metadata_block_data_(FILE *file, FLAC__Metadata_SimpleIteratorStatus *status, const FLAC__StreamMetadata *block) +{ + FLAC__ASSERT(0 != file); + FLAC__ASSERT(0 != status); + + if (write_metadata_block_data_cb_((FLAC__IOHandle)file, (FLAC__IOCallback_Write)fwrite, block)) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; + return true; + } + else { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR; + return false; + } +} + +FLAC__bool write_metadata_block_header_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata *block) +{ + FLAC__byte buffer[FLAC__STREAM_METADATA_HEADER_LENGTH]; + + FLAC__ASSERT(block->length < (1u << FLAC__STREAM_METADATA_LENGTH_LEN)); + + buffer[0] = (block->is_last? 0x80 : 0) | (FLAC__byte)block->type; + pack_uint32_(block->length, buffer + 1, 3); + + if(write_cb(buffer, 1, FLAC__STREAM_METADATA_HEADER_LENGTH, handle) != FLAC__STREAM_METADATA_HEADER_LENGTH) + return false; + + return true; +} + +FLAC__bool write_metadata_block_data_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata *block) +{ + FLAC__ASSERT(0 != block); + + switch(block->type) { + case FLAC__METADATA_TYPE_STREAMINFO: + return write_metadata_block_data_streaminfo_cb_(handle, write_cb, &block->data.stream_info); + case FLAC__METADATA_TYPE_PADDING: + return write_metadata_block_data_padding_cb_(handle, write_cb, &block->data.padding, block->length); + case FLAC__METADATA_TYPE_APPLICATION: + return write_metadata_block_data_application_cb_(handle, write_cb, &block->data.application, block->length); + case FLAC__METADATA_TYPE_SEEKTABLE: + return write_metadata_block_data_seektable_cb_(handle, write_cb, &block->data.seek_table); + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + return write_metadata_block_data_vorbis_comment_cb_(handle, write_cb, &block->data.vorbis_comment); + case FLAC__METADATA_TYPE_CUESHEET: + return write_metadata_block_data_cuesheet_cb_(handle, write_cb, &block->data.cue_sheet); + case FLAC__METADATA_TYPE_PICTURE: + return write_metadata_block_data_picture_cb_(handle, write_cb, &block->data.picture); + default: + return write_metadata_block_data_unknown_cb_(handle, write_cb, &block->data.unknown, block->length); + } +} + +FLAC__bool write_metadata_block_data_streaminfo_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_StreamInfo *block) +{ + FLAC__byte buffer[FLAC__STREAM_METADATA_STREAMINFO_LENGTH]; + const unsigned channels1 = block->channels - 1; + const unsigned bps1 = block->bits_per_sample - 1; + + /* we are using hardcoded numbers for simplicity but we should + * probably eventually write a bit-level packer and use the + * _STREAMINFO_ constants. + */ + pack_uint32_(block->min_blocksize, buffer, 2); + pack_uint32_(block->max_blocksize, buffer+2, 2); + pack_uint32_(block->min_framesize, buffer+4, 3); + pack_uint32_(block->max_framesize, buffer+7, 3); + buffer[10] = (block->sample_rate >> 12) & 0xff; + buffer[11] = (block->sample_rate >> 4) & 0xff; + buffer[12] = ((block->sample_rate & 0x0f) << 4) | (channels1 << 1) | (bps1 >> 4); + buffer[13] = (FLAC__byte)(((bps1 & 0x0f) << 4) | ((block->total_samples >> 32) & 0x0f)); + pack_uint32_((FLAC__uint32)block->total_samples, buffer+14, 4); + memcpy(buffer+18, block->md5sum, 16); + + if(write_cb(buffer, 1, FLAC__STREAM_METADATA_STREAMINFO_LENGTH, handle) != FLAC__STREAM_METADATA_STREAMINFO_LENGTH) + return false; + + return true; +} + +FLAC__bool write_metadata_block_data_padding_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Padding *block, unsigned block_length) +{ + unsigned i, n = block_length; + FLAC__byte buffer[1024]; + + (void)block; + + memset(buffer, 0, 1024); + + for(i = 0; i < n/1024; i++) + if(write_cb(buffer, 1, 1024, handle) != 1024) + return false; + + n %= 1024; + + if(write_cb(buffer, 1, n, handle) != n) + return false; + + return true; +} + +FLAC__bool write_metadata_block_data_application_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Application *block, unsigned block_length) +{ + const unsigned id_bytes = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8; + + if(write_cb(block->id, 1, id_bytes, handle) != id_bytes) + return false; + + block_length -= id_bytes; + + if(write_cb(block->data, 1, block_length, handle) != block_length) + return false; + + return true; +} + +FLAC__bool write_metadata_block_data_seektable_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_SeekTable *block) +{ + unsigned i; + FLAC__byte buffer[FLAC__STREAM_METADATA_SEEKPOINT_LENGTH]; + + for(i = 0; i < block->num_points; i++) { + /* some MAGIC NUMBERs here */ + pack_uint64_(block->points[i].sample_number, buffer, 8); + pack_uint64_(block->points[i].stream_offset, buffer+8, 8); + pack_uint32_(block->points[i].frame_samples, buffer+16, 2); + if(write_cb(buffer, 1, FLAC__STREAM_METADATA_SEEKPOINT_LENGTH, handle) != FLAC__STREAM_METADATA_SEEKPOINT_LENGTH) + return false; + } + + return true; +} + +FLAC__bool write_metadata_block_data_vorbis_comment_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_VorbisComment *block) +{ + unsigned i; + const unsigned entry_length_len = FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN / 8; + const unsigned num_comments_len = FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN / 8; + FLAC__byte buffer[4]; /* magic number is asserted below */ + + FLAC__ASSERT(max(FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN, FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN) / 8 == sizeof(buffer)); + + pack_uint32_little_endian_(block->vendor_string.length, buffer, entry_length_len); + if(write_cb(buffer, 1, entry_length_len, handle) != entry_length_len) + return false; + if(write_cb(block->vendor_string.entry, 1, block->vendor_string.length, handle) != block->vendor_string.length) + return false; + + pack_uint32_little_endian_(block->num_comments, buffer, num_comments_len); + if(write_cb(buffer, 1, num_comments_len, handle) != num_comments_len) + return false; + + for(i = 0; i < block->num_comments; i++) { + pack_uint32_little_endian_(block->comments[i].length, buffer, entry_length_len); + if(write_cb(buffer, 1, entry_length_len, handle) != entry_length_len) + return false; + if(write_cb(block->comments[i].entry, 1, block->comments[i].length, handle) != block->comments[i].length) + return false; + } + + return true; +} + +FLAC__bool write_metadata_block_data_cuesheet_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_CueSheet *block) +{ + unsigned i, j, len; + FLAC__byte buffer[1024]; /* asserted below that this is big enough */ + + FLAC__ASSERT(sizeof(buffer) >= sizeof(FLAC__uint64)); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN/8); + FLAC__ASSERT(sizeof(buffer) >= (FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN + FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN)/8); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN/8); + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN / 8; + if(write_cb(block->media_catalog_number, 1, len, handle) != len) + return false; + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN / 8; + pack_uint64_(block->lead_in, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + FLAC__ASSERT((FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN + FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN) % 8 == 0); + len = (FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN + FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN) / 8; + memset(buffer, 0, len); + if(block->is_cd) + buffer[0] |= 0x80; + if(write_cb(buffer, 1, len, handle) != len) + return false; + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN / 8; + pack_uint32_(block->num_tracks, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + for(i = 0; i < block->num_tracks; i++) { + FLAC__StreamMetadata_CueSheet_Track *track = block->tracks + i; + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN / 8; + pack_uint64_(track->offset, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN / 8; + pack_uint32_(track->number, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN / 8; + if(write_cb(track->isrc, 1, len, handle) != len) + return false; + + FLAC__ASSERT((FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN) % 8 == 0); + len = (FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN) / 8; + memset(buffer, 0, len); + buffer[0] = (track->type << 7) | (track->pre_emphasis << 6); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN / 8; + pack_uint32_(track->num_indices, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + for(j = 0; j < track->num_indices; j++) { + FLAC__StreamMetadata_CueSheet_Index *indx = track->indices + j; + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN / 8; + pack_uint64_(indx->offset, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN / 8; + pack_uint32_(indx->number, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN % 8 == 0); + len = FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN / 8; + memset(buffer, 0, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + } + } + + return true; +} + +FLAC__bool write_metadata_block_data_picture_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Picture *block) +{ + unsigned len; + size_t slen; + FLAC__byte buffer[4]; /* magic number is asserted below */ + + FLAC__ASSERT(0 == FLAC__STREAM_METADATA_PICTURE_TYPE_LEN%8); + FLAC__ASSERT(0 == FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN%8); + FLAC__ASSERT(0 == FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN%8); + FLAC__ASSERT(0 == FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN%8); + FLAC__ASSERT(0 == FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN%8); + FLAC__ASSERT(0 == FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN%8); + FLAC__ASSERT(0 == FLAC__STREAM_METADATA_PICTURE_COLORS_LEN%8); + FLAC__ASSERT(0 == FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN%8); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_PICTURE_TYPE_LEN/8); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN/8); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN/8); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN/8); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN/8); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN/8); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_PICTURE_COLORS_LEN/8); + FLAC__ASSERT(sizeof(buffer) >= FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN/8); + + len = FLAC__STREAM_METADATA_PICTURE_TYPE_LEN/8; + pack_uint32_(block->type, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + len = FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN/8; + slen = strlen(block->mime_type); + pack_uint32_(slen, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + if(write_cb(block->mime_type, 1, slen, handle) != slen) + return false; + + len = FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN/8; + slen = strlen((const char *)block->description); + pack_uint32_(slen, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + if(write_cb(block->description, 1, slen, handle) != slen) + return false; + + len = FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN/8; + pack_uint32_(block->width, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + len = FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN/8; + pack_uint32_(block->height, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + len = FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN/8; + pack_uint32_(block->depth, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + len = FLAC__STREAM_METADATA_PICTURE_COLORS_LEN/8; + pack_uint32_(block->colors, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + len = FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN/8; + pack_uint32_(block->data_length, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + if(write_cb(block->data, 1, block->data_length, handle) != block->data_length) + return false; + + return true; +} + +FLAC__bool write_metadata_block_data_unknown_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Unknown *block, unsigned block_length) +{ + if(write_cb(block->data, 1, block_length, handle) != block_length) + return false; + + return true; +} + +FLAC__bool write_metadata_block_stationary_(FLAC__Metadata_SimpleIterator *iterator, const FLAC__StreamMetadata *block) +{ + if(0 != fseeko(iterator->file, iterator->offset[iterator->depth], SEEK_SET)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + + if(!write_metadata_block_header_(iterator->file, &iterator->status, block)) + return false; + + if(!write_metadata_block_data_(iterator->file, &iterator->status, block)) + return false; + + if(0 != fseeko(iterator->file, iterator->offset[iterator->depth], SEEK_SET)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + + return read_metadata_block_header_(iterator); +} + +FLAC__bool write_metadata_block_stationary_with_padding_(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, unsigned padding_length, FLAC__bool padding_is_last) +{ + FLAC__StreamMetadata *padding; + + if(0 != fseeko(iterator->file, iterator->offset[iterator->depth], SEEK_SET)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + + block->is_last = false; + + if(!write_metadata_block_header_(iterator->file, &iterator->status, block)) + return false; + + if(!write_metadata_block_data_(iterator->file, &iterator->status, block)) + return false; + + if(0 == (padding = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING))) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + + padding->is_last = padding_is_last; + padding->length = padding_length; + + if(!write_metadata_block_header_(iterator->file, &iterator->status, padding)) { + FLAC__metadata_object_delete(padding); + return false; + } + + if(!write_metadata_block_data_(iterator->file, &iterator->status, padding)) { + FLAC__metadata_object_delete(padding); + return false; + } + + FLAC__metadata_object_delete(padding); + + if(0 != fseeko(iterator->file, iterator->offset[iterator->depth], SEEK_SET)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + + return read_metadata_block_header_(iterator); +} + +FLAC__bool rewrite_whole_file_(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, FLAC__bool append) +{ + FILE *tempfile; + char *tempfilename; + int fixup_is_last_code = 0; /* 0 => no need to change any is_last flags */ + off_t fixup_is_last_flag_offset = -1; + + FLAC__ASSERT(0 != block || append == false); + + if(iterator->is_last) { + if(append) { + fixup_is_last_code = 1; /* 1 => clear the is_last flag at the following offset */ + fixup_is_last_flag_offset = iterator->offset[iterator->depth]; + } + else if(0 == block) { + simple_iterator_push_(iterator); + if(!FLAC__metadata_simple_iterator_prev(iterator)) { + (void)simple_iterator_pop_(iterator); + return false; + } + fixup_is_last_code = -1; /* -1 => set the is_last the flag at the following offset */ + fixup_is_last_flag_offset = iterator->offset[iterator->depth]; + if(!simple_iterator_pop_(iterator)) + return false; + } + } + + if(!simple_iterator_copy_file_prefix_(iterator, &tempfile, &tempfilename, append)) + return false; + + if(0 != block) { + if(!write_metadata_block_header_(tempfile, &iterator->status, block)) { + cleanup_tempfile_(&tempfile, &tempfilename); + return false; + } + + if(!write_metadata_block_data_(tempfile, &iterator->status, block)) { + cleanup_tempfile_(&tempfile, &tempfilename); + return false; + } + } + + if(!simple_iterator_copy_file_postfix_(iterator, &tempfile, &tempfilename, fixup_is_last_code, fixup_is_last_flag_offset, block==0)) + return false; + + if(append) + return FLAC__metadata_simple_iterator_next(iterator); + + return true; +} + +void simple_iterator_push_(FLAC__Metadata_SimpleIterator *iterator) +{ + FLAC__ASSERT(iterator->depth+1 < SIMPLE_ITERATOR_MAX_PUSH_DEPTH); + iterator->offset[iterator->depth+1] = iterator->offset[iterator->depth]; + iterator->depth++; +} + +FLAC__bool simple_iterator_pop_(FLAC__Metadata_SimpleIterator *iterator) +{ + FLAC__ASSERT(iterator->depth > 0); + iterator->depth--; + if(0 != fseeko(iterator->file, iterator->offset[iterator->depth], SEEK_SET)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + + return read_metadata_block_header_(iterator); +} + +/* return meanings: + * 0: ok + * 1: read error + * 2: seek error + * 3: not a FLAC file + */ +unsigned seek_to_first_metadata_block_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb) +{ + FLAC__byte buffer[4]; + size_t n; + unsigned i; + + FLAC__ASSERT(FLAC__STREAM_SYNC_LENGTH == sizeof(buffer)); + + /* skip any id3v2 tag */ + errno = 0; + n = read_cb(buffer, 1, 4, handle); + if(errno) + return 1; + else if(n != 4) + return 3; + else if(0 == memcmp(buffer, "ID3", 3)) { + unsigned tag_length = 0; + + /* skip to the tag length */ + if(seek_cb(handle, 2, SEEK_CUR) < 0) + return 2; + + /* read the length */ + for(i = 0; i < 4; i++) { + if(read_cb(buffer, 1, 1, handle) < 1 || buffer[0] & 0x80) + return 1; + tag_length <<= 7; + tag_length |= (buffer[0] & 0x7f); + } + + /* skip the rest of the tag */ + if(seek_cb(handle, tag_length, SEEK_CUR) < 0) + return 2; + + /* read the stream sync code */ + errno = 0; + n = read_cb(buffer, 1, 4, handle); + if(errno) + return 1; + else if(n != 4) + return 3; + } + + /* check for the fLaC signature */ + if(0 == memcmp(FLAC__STREAM_SYNC_STRING, buffer, FLAC__STREAM_SYNC_LENGTH)) + return 0; + else + return 3; +} + +unsigned seek_to_first_metadata_block_(FILE *f) +{ + return seek_to_first_metadata_block_cb_((FLAC__IOHandle)f, (FLAC__IOCallback_Read)fread, fseek_wrapper_); +} + +FLAC__bool simple_iterator_copy_file_prefix_(FLAC__Metadata_SimpleIterator *iterator, FILE **tempfile, char **tempfilename, FLAC__bool append) +{ + const off_t offset_end = append? iterator->offset[iterator->depth] + (off_t)FLAC__STREAM_METADATA_HEADER_LENGTH + (off_t)iterator->length : iterator->offset[iterator->depth]; + + if(0 != fseeko(iterator->file, 0, SEEK_SET)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + if(!open_tempfile_(iterator->filename, iterator->tempfile_path_prefix, tempfile, tempfilename, &iterator->status)) { + cleanup_tempfile_(tempfile, tempfilename); + return false; + } + if(!copy_n_bytes_from_file_(iterator->file, *tempfile, offset_end, &iterator->status)) { + cleanup_tempfile_(tempfile, tempfilename); + return false; + } + + return true; +} + +FLAC__bool simple_iterator_copy_file_postfix_(FLAC__Metadata_SimpleIterator *iterator, FILE **tempfile, char **tempfilename, int fixup_is_last_code, off_t fixup_is_last_flag_offset, FLAC__bool backup) +{ + off_t save_offset = iterator->offset[iterator->depth]; + FLAC__ASSERT(0 != *tempfile); + + if(0 != fseeko(iterator->file, save_offset + (off_t)FLAC__STREAM_METADATA_HEADER_LENGTH + (off_t)iterator->length, SEEK_SET)) { + cleanup_tempfile_(tempfile, tempfilename); + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + if(!copy_remaining_bytes_from_file_(iterator->file, *tempfile, &iterator->status)) { + cleanup_tempfile_(tempfile, tempfilename); + return false; + } + + if(fixup_is_last_code != 0) { + /* + * if code == 1, it means a block was appended to the end so + * we have to clear the is_last flag of the previous block + * if code == -1, it means the last block was deleted so + * we have to set the is_last flag of the previous block + */ + /* MAGIC NUMBERs here; we know the is_last flag is the high bit of the byte at this location */ + FLAC__byte x; + if(0 != fseeko(*tempfile, fixup_is_last_flag_offset, SEEK_SET)) { + cleanup_tempfile_(tempfile, tempfilename); + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + if(fread(&x, 1, 1, *tempfile) != 1) { + cleanup_tempfile_(tempfile, tempfilename); + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + return false; + } + if(fixup_is_last_code > 0) { + FLAC__ASSERT(x & 0x80); + x &= 0x7f; + } + else { + FLAC__ASSERT(!(x & 0x80)); + x |= 0x80; + } + if(0 != fseeko(*tempfile, fixup_is_last_flag_offset, SEEK_SET)) { + cleanup_tempfile_(tempfile, tempfilename); + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + if(local__fwrite(&x, 1, 1, *tempfile) != 1) { + cleanup_tempfile_(tempfile, tempfilename); + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR; + return false; + } + } + + (void)fclose(iterator->file); + + if(!transport_tempfile_(iterator->filename, tempfile, tempfilename, &iterator->status)) + return false; + + if(iterator->has_stats) + set_file_stats_(iterator->filename, &iterator->stats); + + if(!simple_iterator_prime_input_(iterator, !iterator->is_writable)) + return false; + if(backup) { + while(iterator->offset[iterator->depth] + (off_t)FLAC__STREAM_METADATA_HEADER_LENGTH + (off_t)iterator->length < save_offset) + if(!FLAC__metadata_simple_iterator_next(iterator)) + return false; + return true; + } + else { + /* move the iterator to it's original block faster by faking a push, then doing a pop_ */ + FLAC__ASSERT(iterator->depth == 0); + iterator->offset[0] = save_offset; + iterator->depth++; + return simple_iterator_pop_(iterator); + } +} + +FLAC__bool copy_n_bytes_from_file_(FILE *file, FILE *tempfile, off_t bytes, FLAC__Metadata_SimpleIteratorStatus *status) +{ + FLAC__byte buffer[8192]; + size_t n; + + FLAC__ASSERT(bytes >= 0); + while(bytes > 0) { + n = min(sizeof(buffer), (size_t)bytes); + if(fread(buffer, 1, n, file) != n) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + return false; + } + if(local__fwrite(buffer, 1, n, tempfile) != n) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR; + return false; + } + bytes -= n; + } + + return true; +} + +FLAC__bool copy_n_bytes_from_file_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOHandle temp_handle, FLAC__IOCallback_Write temp_write_cb, off_t bytes, FLAC__Metadata_SimpleIteratorStatus *status) +{ + FLAC__byte buffer[8192]; + size_t n; + + FLAC__ASSERT(bytes >= 0); + while(bytes > 0) { + n = min(sizeof(buffer), (size_t)bytes); + if(read_cb(buffer, 1, n, handle) != n) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + return false; + } + if(temp_write_cb(buffer, 1, n, temp_handle) != n) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR; + return false; + } + bytes -= n; + } + + return true; +} + +FLAC__bool copy_remaining_bytes_from_file_(FILE *file, FILE *tempfile, FLAC__Metadata_SimpleIteratorStatus *status) +{ + FLAC__byte buffer[8192]; + size_t n; + + while(!feof(file)) { + n = fread(buffer, 1, sizeof(buffer), file); + if(n == 0 && !feof(file)) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + return false; + } + if(n > 0 && local__fwrite(buffer, 1, n, tempfile) != n) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR; + return false; + } + } + + return true; +} + +FLAC__bool copy_remaining_bytes_from_file_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Eof eof_cb, FLAC__IOHandle temp_handle, FLAC__IOCallback_Write temp_write_cb, FLAC__Metadata_SimpleIteratorStatus *status) +{ + FLAC__byte buffer[8192]; + size_t n; + + while(!eof_cb(handle)) { + n = read_cb(buffer, 1, sizeof(buffer), handle); + if(n == 0 && !eof_cb(handle)) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + return false; + } + if(n > 0 && temp_write_cb(buffer, 1, n, temp_handle) != n) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR; + return false; + } + } + + return true; +} + +FLAC__bool open_tempfile_(const char *filename, const char *tempfile_path_prefix, FILE **tempfile, char **tempfilename, FLAC__Metadata_SimpleIteratorStatus *status) +{ + static const char *tempfile_suffix = ".metadata_edit"; + if(0 == tempfile_path_prefix) { + if(0 == (*tempfilename = (char*)malloc(strlen(filename) + strlen(tempfile_suffix) + 1))) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + return false; + } + strcpy(*tempfilename, filename); + strcat(*tempfilename, tempfile_suffix); + } + else { + const char *p = strrchr(filename, '/'); + if(0 == p) + p = filename; + else + p++; + + if(0 == (*tempfilename = (char*)malloc(strlen(tempfile_path_prefix) + 1 + strlen(p) + strlen(tempfile_suffix) + 1))) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + return false; + } + strcpy(*tempfilename, tempfile_path_prefix); + strcat(*tempfilename, "/"); + strcat(*tempfilename, p); + strcat(*tempfilename, tempfile_suffix); + } + + if(0 == (*tempfile = fopen(*tempfilename, "w+b"))) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE; + return false; + } + + return true; +} + +FLAC__bool transport_tempfile_(const char *filename, FILE **tempfile, char **tempfilename, FLAC__Metadata_SimpleIteratorStatus *status) +{ + FLAC__ASSERT(0 != filename); + FLAC__ASSERT(0 != tempfile); + FLAC__ASSERT(0 != *tempfile); + FLAC__ASSERT(0 != tempfilename); + FLAC__ASSERT(0 != *tempfilename); + FLAC__ASSERT(0 != status); + + (void)fclose(*tempfile); + *tempfile = 0; + +#if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__ || defined __EMX__ + /* on some flavors of windows, rename() will fail if the destination already exists */ + if(unlink(filename) < 0) { + cleanup_tempfile_(tempfile, tempfilename); + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_UNLINK_ERROR; + return false; + } +#endif + + /*@@@ to fully support the tempfile_path_prefix we need to update this piece to actually copy across filesystems instead of just rename(): */ + if(0 != rename(*tempfilename, filename)) { + cleanup_tempfile_(tempfile, tempfilename); + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_RENAME_ERROR; + return false; + } + + cleanup_tempfile_(tempfile, tempfilename); + + return true; +} + +void cleanup_tempfile_(FILE **tempfile, char **tempfilename) +{ + if(0 != *tempfile) { + (void)fclose(*tempfile); + *tempfile = 0; + } + + if(0 != *tempfilename) { + (void)unlink(*tempfilename); + free(*tempfilename); + *tempfilename = 0; + } +} + +FLAC__bool get_file_stats_(const char *filename, struct stat *stats) +{ + FLAC__ASSERT(0 != filename); + FLAC__ASSERT(0 != stats); + return (0 == stat(filename, stats)); +} + +void set_file_stats_(const char *filename, struct stat *stats) +{ + struct utimbuf srctime; + + FLAC__ASSERT(0 != filename); + FLAC__ASSERT(0 != stats); + + srctime.actime = stats->st_atime; + srctime.modtime = stats->st_mtime; + (void)chmod(filename, stats->st_mode); + (void)utime(filename, &srctime); +#if !defined _MSC_VER && !defined __BORLANDC__ && !defined __MINGW32__ && !defined __EMX__ + (void)chown(filename, stats->st_uid, -1); + (void)chown(filename, -1, stats->st_gid); +#endif +} + +int fseek_wrapper_(FLAC__IOHandle handle, FLAC__int64 offset, int whence) +{ + return fseeko((FILE*)handle, (off_t)offset, whence); +} + +FLAC__int64 ftell_wrapper_(FLAC__IOHandle handle) +{ + return ftello((FILE*)handle); +} + +FLAC__Metadata_ChainStatus get_equivalent_status_(FLAC__Metadata_SimpleIteratorStatus status) +{ + switch(status) { + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK: + return FLAC__METADATA_CHAIN_STATUS_OK; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT: + return FLAC__METADATA_CHAIN_STATUS_ILLEGAL_INPUT; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE: + return FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_A_FLAC_FILE: + return FLAC__METADATA_CHAIN_STATUS_NOT_A_FLAC_FILE; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_WRITABLE: + return FLAC__METADATA_CHAIN_STATUS_NOT_WRITABLE; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_BAD_METADATA: + return FLAC__METADATA_CHAIN_STATUS_BAD_METADATA; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR: + return FLAC__METADATA_CHAIN_STATUS_READ_ERROR; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR: + return FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR: + return FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_RENAME_ERROR: + return FLAC__METADATA_CHAIN_STATUS_RENAME_ERROR; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_UNLINK_ERROR: + return FLAC__METADATA_CHAIN_STATUS_UNLINK_ERROR; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR: + return FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_INTERNAL_ERROR: + default: + return FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR; + } +} diff --git a/src/FLAC/src/libFLAC/metadata_object.c b/src/FLAC/src/libFLAC/metadata_object.c new file mode 100644 index 00000000..75117670 --- /dev/null +++ b/src/FLAC/src/libFLAC/metadata_object.c @@ -0,0 +1,1789 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include + +#include "private/metadata.h" + +#include "FLAC/assert.h" + + +/**************************************************************************** + * + * Local routines + * + ***************************************************************************/ + +/* copy bytes: + * from = NULL && bytes = 0 + * to <- NULL + * from != NULL && bytes > 0 + * to <- copy of from + * else ASSERT + * malloc error leaved 'to' unchanged + */ +static FLAC__bool copy_bytes_(FLAC__byte **to, const FLAC__byte *from, unsigned bytes) +{ + FLAC__ASSERT(0 != to); + if(bytes > 0 && 0 != from) { + FLAC__byte *x; + if(0 == (x = (FLAC__byte*)malloc(bytes))) + return false; + memcpy(x, from, bytes); + *to = x; + } + else { + FLAC__ASSERT(0 == from); + FLAC__ASSERT(bytes == 0); + *to = 0; + } + return true; +} + +#if 0 /* UNUSED */ +/* like copy_bytes_(), but free()s the original '*to' if the copy succeeds and the original '*to' is non-NULL */ +static FLAC__bool free_copy_bytes_(FLAC__byte **to, const FLAC__byte *from, unsigned bytes) +{ + FLAC__byte *copy; + FLAC__ASSERT(0 != to); + if(copy_bytes_(©, from, bytes)) { + if(*to) + free(*to); + *to = copy; + return true; + } + else + return false; +} +#endif + +/* reallocate entry to 1 byte larger and add a terminating NUL */ +/* realloc() failure leaves entry unchanged */ +static FLAC__bool ensure_null_terminated_(FLAC__byte **entry, unsigned length) +{ + FLAC__byte *x = (FLAC__byte*)realloc(*entry, length+1); + if(0 != x) { + x[length] = '\0'; + *entry = x; + return true; + } + else + return false; +} + +/* copies the NUL-terminated C-string 'from' to '*to', leaving '*to' + * unchanged if malloc fails, free()ing the original '*to' if it + * succeeds and the original '*to' was not NULL + */ +static FLAC__bool copy_cstring_(char **to, const char *from) +{ + char *copy = strdup(from); + FLAC__ASSERT(to); + if(copy) { + if(*to) + free(*to); + *to = copy; + return true; + } + else + return false; +} + +static FLAC__bool copy_vcentry_(FLAC__StreamMetadata_VorbisComment_Entry *to, const FLAC__StreamMetadata_VorbisComment_Entry *from) +{ + to->length = from->length; + if(0 == from->entry) { + FLAC__ASSERT(from->length == 0); + to->entry = 0; + } + else { + FLAC__byte *x; + FLAC__ASSERT(from->length > 0); + if(0 == (x = (FLAC__byte*)malloc(from->length+1))) + return false; + memcpy(x, from->entry, from->length); + x[from->length] = '\0'; + to->entry = x; + } + return true; +} + +static FLAC__bool copy_track_(FLAC__StreamMetadata_CueSheet_Track *to, const FLAC__StreamMetadata_CueSheet_Track *from) +{ + memcpy(to, from, sizeof(FLAC__StreamMetadata_CueSheet_Track)); + if(0 == from->indices) { + FLAC__ASSERT(from->num_indices == 0); + } + else { + FLAC__StreamMetadata_CueSheet_Index *x; + FLAC__ASSERT(from->num_indices > 0); + if(0 == (x = (FLAC__StreamMetadata_CueSheet_Index*)malloc(from->num_indices * sizeof(FLAC__StreamMetadata_CueSheet_Index)))) + return false; + memcpy(x, from->indices, from->num_indices * sizeof(FLAC__StreamMetadata_CueSheet_Index)); + to->indices = x; + } + return true; +} + +static void seektable_calculate_length_(FLAC__StreamMetadata *object) +{ + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE); + + object->length = object->data.seek_table.num_points * FLAC__STREAM_METADATA_SEEKPOINT_LENGTH; +} + +static FLAC__StreamMetadata_SeekPoint *seekpoint_array_new_(unsigned num_points) +{ + FLAC__StreamMetadata_SeekPoint *object_array; + + FLAC__ASSERT(num_points > 0); + + object_array = (FLAC__StreamMetadata_SeekPoint*)malloc(num_points * sizeof(FLAC__StreamMetadata_SeekPoint)); + + if(0 != object_array) { + unsigned i; + for(i = 0; i < num_points; i++) { + object_array[i].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER; + object_array[i].stream_offset = 0; + object_array[i].frame_samples = 0; + } + } + + return object_array; +} + +static void vorbiscomment_calculate_length_(FLAC__StreamMetadata *object) +{ + unsigned i; + + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + + object->length = (FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN) / 8; + object->length += object->data.vorbis_comment.vendor_string.length; + object->length += (FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN) / 8; + for(i = 0; i < object->data.vorbis_comment.num_comments; i++) { + object->length += (FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN / 8); + object->length += object->data.vorbis_comment.comments[i].length; + } +} + +static FLAC__StreamMetadata_VorbisComment_Entry *vorbiscomment_entry_array_new_(unsigned num_comments) +{ + FLAC__ASSERT(num_comments > 0); + + return (FLAC__StreamMetadata_VorbisComment_Entry*)calloc(num_comments, sizeof(FLAC__StreamMetadata_VorbisComment_Entry)); +} + +static void vorbiscomment_entry_array_delete_(FLAC__StreamMetadata_VorbisComment_Entry *object_array, unsigned num_comments) +{ + unsigned i; + + FLAC__ASSERT(0 != object_array && num_comments > 0); + + for(i = 0; i < num_comments; i++) + if(0 != object_array[i].entry) + free(object_array[i].entry); + + if(0 != object_array) + free(object_array); +} + +static FLAC__StreamMetadata_VorbisComment_Entry *vorbiscomment_entry_array_copy_(const FLAC__StreamMetadata_VorbisComment_Entry *object_array, unsigned num_comments) +{ + FLAC__StreamMetadata_VorbisComment_Entry *return_array; + + FLAC__ASSERT(0 != object_array); + FLAC__ASSERT(num_comments > 0); + + return_array = vorbiscomment_entry_array_new_(num_comments); + + if(0 != return_array) { + unsigned i; + + for(i = 0; i < num_comments; i++) { + if(!copy_vcentry_(return_array+i, object_array+i)) { + vorbiscomment_entry_array_delete_(return_array, num_comments); + return 0; + } + } + } + + return return_array; +} + +static FLAC__bool vorbiscomment_set_entry_(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry *dest, const FLAC__StreamMetadata_VorbisComment_Entry *src, FLAC__bool copy) +{ + FLAC__byte *save; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(0 != dest); + FLAC__ASSERT(0 != src); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + FLAC__ASSERT((0 != src->entry && src->length > 0) || (0 == src->entry && src->length == 0)); + + save = dest->entry; + + if(0 != src->entry && src->length > 0) { + if(copy) { + /* do the copy first so that if we fail we leave the dest object untouched */ + if(!copy_vcentry_(dest, src)) + return false; + } + else { + /* we have to make sure that the string we're taking over is null-terminated */ + + /* + * Stripping the const from src->entry is OK since we're taking + * ownership of the pointer. This is a hack around a deficiency + * in the API where the same function is used for 'copy' and + * 'own', but the source entry is a const pointer. If we were + * precise, the 'own' flavor would be a separate function with a + * non-const source pointer. But it's not, so we hack away. + */ + if(!ensure_null_terminated_((FLAC__byte**)(&src->entry), src->length)) + return false; + *dest = *src; + } + } + else { + /* the src is null */ + *dest = *src; + } + + if(0 != save) + free(save); + + vorbiscomment_calculate_length_(object); + return true; +} + +static int vorbiscomment_find_entry_from_(const FLAC__StreamMetadata *object, unsigned offset, const char *field_name, unsigned field_name_length) +{ + unsigned i; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + FLAC__ASSERT(0 != field_name); + + for(i = offset; i < object->data.vorbis_comment.num_comments; i++) { + if(FLAC__metadata_object_vorbiscomment_entry_matches(object->data.vorbis_comment.comments[i], field_name, field_name_length)) + return (int)i; + } + + return -1; +} + +static void cuesheet_calculate_length_(FLAC__StreamMetadata *object) +{ + unsigned i; + + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET); + + object->length = ( + FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN + + FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN + + FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN + + FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN + + FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN + ) / 8; + + object->length += object->data.cue_sheet.num_tracks * ( + FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN + + FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN + + FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN + + FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN + + FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN + + FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN + + FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN + ) / 8; + + for(i = 0; i < object->data.cue_sheet.num_tracks; i++) { + object->length += object->data.cue_sheet.tracks[i].num_indices * ( + FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN + + FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN + + FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN + ) / 8; + } +} + +static FLAC__StreamMetadata_CueSheet_Index *cuesheet_track_index_array_new_(unsigned num_indices) +{ + FLAC__ASSERT(num_indices > 0); + + return (FLAC__StreamMetadata_CueSheet_Index*)calloc(num_indices, sizeof(FLAC__StreamMetadata_CueSheet_Index)); +} + +static FLAC__StreamMetadata_CueSheet_Track *cuesheet_track_array_new_(unsigned num_tracks) +{ + FLAC__ASSERT(num_tracks > 0); + + return (FLAC__StreamMetadata_CueSheet_Track*)calloc(num_tracks, sizeof(FLAC__StreamMetadata_CueSheet_Track)); +} + +static void cuesheet_track_array_delete_(FLAC__StreamMetadata_CueSheet_Track *object_array, unsigned num_tracks) +{ + unsigned i; + + FLAC__ASSERT(0 != object_array && num_tracks > 0); + + for(i = 0; i < num_tracks; i++) { + if(0 != object_array[i].indices) { + FLAC__ASSERT(object_array[i].num_indices > 0); + free(object_array[i].indices); + } + } + + if(0 != object_array) + free(object_array); +} + +static FLAC__StreamMetadata_CueSheet_Track *cuesheet_track_array_copy_(const FLAC__StreamMetadata_CueSheet_Track *object_array, unsigned num_tracks) +{ + FLAC__StreamMetadata_CueSheet_Track *return_array; + + FLAC__ASSERT(0 != object_array); + FLAC__ASSERT(num_tracks > 0); + + return_array = cuesheet_track_array_new_(num_tracks); + + if(0 != return_array) { + unsigned i; + + for(i = 0; i < num_tracks; i++) { + if(!copy_track_(return_array+i, object_array+i)) { + cuesheet_track_array_delete_(return_array, num_tracks); + return 0; + } + } + } + + return return_array; +} + +static FLAC__bool cuesheet_set_track_(FLAC__StreamMetadata *object, FLAC__StreamMetadata_CueSheet_Track *dest, const FLAC__StreamMetadata_CueSheet_Track *src, FLAC__bool copy) +{ + FLAC__StreamMetadata_CueSheet_Index *save; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(0 != dest); + FLAC__ASSERT(0 != src); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET); + FLAC__ASSERT((0 != src->indices && src->num_indices > 0) || (0 == src->indices && src->num_indices == 0)); + + save = dest->indices; + + /* do the copy first so that if we fail we leave the object untouched */ + if(copy) { + if(!copy_track_(dest, src)) + return false; + } + else { + *dest = *src; + } + + if(0 != save) + free(save); + + cuesheet_calculate_length_(object); + return true; +} + + +/**************************************************************************** + * + * Metadata object routines + * + ***************************************************************************/ + +FLAC_API FLAC__StreamMetadata *FLAC__metadata_object_new(FLAC__MetadataType type) +{ + FLAC__StreamMetadata *object; + + if(type > FLAC__MAX_METADATA_TYPE_CODE) + return 0; + + object = (FLAC__StreamMetadata*)calloc(1, sizeof(FLAC__StreamMetadata)); + if(0 != object) { + object->is_last = false; + object->type = type; + switch(type) { + case FLAC__METADATA_TYPE_STREAMINFO: + object->length = FLAC__STREAM_METADATA_STREAMINFO_LENGTH; + break; + case FLAC__METADATA_TYPE_PADDING: + /* calloc() took care of this for us: + object->length = 0; + */ + break; + case FLAC__METADATA_TYPE_APPLICATION: + object->length = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8; + /* calloc() took care of this for us: + object->data.application.data = 0; + */ + break; + case FLAC__METADATA_TYPE_SEEKTABLE: + /* calloc() took care of this for us: + object->length = 0; + object->data.seek_table.num_points = 0; + object->data.seek_table.points = 0; + */ + break; + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + object->data.vorbis_comment.vendor_string.length = (unsigned)strlen(FLAC__VENDOR_STRING); + if(!copy_bytes_(&object->data.vorbis_comment.vendor_string.entry, (const FLAC__byte*)FLAC__VENDOR_STRING, object->data.vorbis_comment.vendor_string.length+1)) { + free(object); + return 0; + } + vorbiscomment_calculate_length_(object); + break; + case FLAC__METADATA_TYPE_CUESHEET: + cuesheet_calculate_length_(object); + break; + case FLAC__METADATA_TYPE_PICTURE: + object->length = ( + FLAC__STREAM_METADATA_PICTURE_TYPE_LEN + + FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN + /* empty mime_type string */ + FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN + /* empty description string */ + FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN + + FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN + + FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN + + FLAC__STREAM_METADATA_PICTURE_COLORS_LEN + + FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN + + 0 /* no data */ + ) / 8; + object->data.picture.type = FLAC__STREAM_METADATA_PICTURE_TYPE_OTHER; + object->data.picture.mime_type = 0; + object->data.picture.description = 0; + /* calloc() took care of this for us: + object->data.picture.width = 0; + object->data.picture.height = 0; + object->data.picture.depth = 0; + object->data.picture.colors = 0; + object->data.picture.data_length = 0; + object->data.picture.data = 0; + */ + /* now initialize mime_type and description with empty strings to make things easier on the client */ + if(!copy_cstring_(&object->data.picture.mime_type, "")) { + free(object); + return 0; + } + if(!copy_cstring_((char**)(&object->data.picture.description), "")) { + if(object->data.picture.mime_type) + free(object->data.picture.mime_type); + free(object); + return 0; + } + break; + default: + /* calloc() took care of this for us: + object->length = 0; + object->data.unknown.data = 0; + */ + break; + } + } + + return object; +} + +FLAC_API FLAC__StreamMetadata *FLAC__metadata_object_clone(const FLAC__StreamMetadata *object) +{ + FLAC__StreamMetadata *to; + + FLAC__ASSERT(0 != object); + + if(0 != (to = FLAC__metadata_object_new(object->type))) { + to->is_last = object->is_last; + to->type = object->type; + to->length = object->length; + switch(to->type) { + case FLAC__METADATA_TYPE_STREAMINFO: + memcpy(&to->data.stream_info, &object->data.stream_info, sizeof(FLAC__StreamMetadata_StreamInfo)); + break; + case FLAC__METADATA_TYPE_PADDING: + break; + case FLAC__METADATA_TYPE_APPLICATION: + memcpy(&to->data.application.id, &object->data.application.id, FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8); + if(!copy_bytes_(&to->data.application.data, object->data.application.data, object->length - FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8)) { + FLAC__metadata_object_delete(to); + return 0; + } + break; + case FLAC__METADATA_TYPE_SEEKTABLE: + to->data.seek_table.num_points = object->data.seek_table.num_points; + if(!copy_bytes_((FLAC__byte**)&to->data.seek_table.points, (FLAC__byte*)object->data.seek_table.points, object->data.seek_table.num_points * sizeof(FLAC__StreamMetadata_SeekPoint))) { + FLAC__metadata_object_delete(to); + return 0; + } + break; + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + if(0 != to->data.vorbis_comment.vendor_string.entry) { + free(to->data.vorbis_comment.vendor_string.entry); + to->data.vorbis_comment.vendor_string.entry = 0; + } + if(!copy_vcentry_(&to->data.vorbis_comment.vendor_string, &object->data.vorbis_comment.vendor_string)) { + FLAC__metadata_object_delete(to); + return 0; + } + if(object->data.vorbis_comment.num_comments == 0) { + FLAC__ASSERT(0 == object->data.vorbis_comment.comments); + to->data.vorbis_comment.comments = 0; + } + else { + FLAC__ASSERT(0 != object->data.vorbis_comment.comments); + to->data.vorbis_comment.comments = vorbiscomment_entry_array_copy_(object->data.vorbis_comment.comments, object->data.vorbis_comment.num_comments); + if(0 == to->data.vorbis_comment.comments) { + FLAC__metadata_object_delete(to); + return 0; + } + } + to->data.vorbis_comment.num_comments = object->data.vorbis_comment.num_comments; + break; + case FLAC__METADATA_TYPE_CUESHEET: + memcpy(&to->data.cue_sheet, &object->data.cue_sheet, sizeof(FLAC__StreamMetadata_CueSheet)); + if(object->data.cue_sheet.num_tracks == 0) { + FLAC__ASSERT(0 == object->data.cue_sheet.tracks); + } + else { + FLAC__ASSERT(0 != object->data.cue_sheet.tracks); + to->data.cue_sheet.tracks = cuesheet_track_array_copy_(object->data.cue_sheet.tracks, object->data.cue_sheet.num_tracks); + if(0 == to->data.cue_sheet.tracks) { + FLAC__metadata_object_delete(to); + return 0; + } + } + break; + case FLAC__METADATA_TYPE_PICTURE: + to->data.picture.type = object->data.picture.type; + if(!copy_cstring_(&to->data.picture.mime_type, object->data.picture.mime_type)) { + FLAC__metadata_object_delete(to); + return 0; + } + if(!copy_cstring_((char**)(&to->data.picture.description), (const char*)object->data.picture.description)) { + FLAC__metadata_object_delete(to); + return 0; + } + to->data.picture.width = object->data.picture.width; + to->data.picture.height = object->data.picture.height; + to->data.picture.depth = object->data.picture.depth; + to->data.picture.colors = object->data.picture.colors; + to->data.picture.data_length = object->data.picture.data_length; + if(!copy_bytes_((&to->data.picture.data), object->data.picture.data, object->data.picture.data_length)) { + FLAC__metadata_object_delete(to); + return 0; + } + break; + default: + if(!copy_bytes_(&to->data.unknown.data, object->data.unknown.data, object->length)) { + FLAC__metadata_object_delete(to); + return 0; + } + break; + } + } + + return to; +} + +void FLAC__metadata_object_delete_data(FLAC__StreamMetadata *object) +{ + FLAC__ASSERT(0 != object); + + switch(object->type) { + case FLAC__METADATA_TYPE_STREAMINFO: + case FLAC__METADATA_TYPE_PADDING: + break; + case FLAC__METADATA_TYPE_APPLICATION: + if(0 != object->data.application.data) { + free(object->data.application.data); + object->data.application.data = 0; + } + break; + case FLAC__METADATA_TYPE_SEEKTABLE: + if(0 != object->data.seek_table.points) { + free(object->data.seek_table.points); + object->data.seek_table.points = 0; + } + break; + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + if(0 != object->data.vorbis_comment.vendor_string.entry) { + free(object->data.vorbis_comment.vendor_string.entry); + object->data.vorbis_comment.vendor_string.entry = 0; + } + if(0 != object->data.vorbis_comment.comments) { + FLAC__ASSERT(object->data.vorbis_comment.num_comments > 0); + vorbiscomment_entry_array_delete_(object->data.vorbis_comment.comments, object->data.vorbis_comment.num_comments); + } + break; + case FLAC__METADATA_TYPE_CUESHEET: + if(0 != object->data.cue_sheet.tracks) { + FLAC__ASSERT(object->data.cue_sheet.num_tracks > 0); + cuesheet_track_array_delete_(object->data.cue_sheet.tracks, object->data.cue_sheet.num_tracks); + } + break; + case FLAC__METADATA_TYPE_PICTURE: + if(0 != object->data.picture.mime_type) { + free(object->data.picture.mime_type); + object->data.picture.mime_type = 0; + } + if(0 != object->data.picture.description) { + free(object->data.picture.description); + object->data.picture.description = 0; + } + if(0 != object->data.picture.data) { + free(object->data.picture.data); + object->data.picture.data = 0; + } + break; + default: + if(0 != object->data.unknown.data) { + free(object->data.unknown.data); + object->data.unknown.data = 0; + } + break; + } +} + +FLAC_API void FLAC__metadata_object_delete(FLAC__StreamMetadata *object) +{ + FLAC__metadata_object_delete_data(object); + free(object); +} + +static FLAC__bool compare_block_data_streaminfo_(const FLAC__StreamMetadata_StreamInfo *block1, const FLAC__StreamMetadata_StreamInfo *block2) +{ + if(block1->min_blocksize != block2->min_blocksize) + return false; + if(block1->max_blocksize != block2->max_blocksize) + return false; + if(block1->min_framesize != block2->min_framesize) + return false; + if(block1->max_framesize != block2->max_framesize) + return false; + if(block1->sample_rate != block2->sample_rate) + return false; + if(block1->channels != block2->channels) + return false; + if(block1->bits_per_sample != block2->bits_per_sample) + return false; + if(block1->total_samples != block2->total_samples) + return false; + if(0 != memcmp(block1->md5sum, block2->md5sum, 16)) + return false; + return true; +} + +static FLAC__bool compare_block_data_application_(const FLAC__StreamMetadata_Application *block1, const FLAC__StreamMetadata_Application *block2, unsigned block_length) +{ + FLAC__ASSERT(0 != block1); + FLAC__ASSERT(0 != block2); + FLAC__ASSERT(block_length >= sizeof(block1->id)); + + if(0 != memcmp(block1->id, block2->id, sizeof(block1->id))) + return false; + if(0 != block1->data && 0 != block2->data) + return 0 == memcmp(block1->data, block2->data, block_length - sizeof(block1->id)); + else + return block1->data == block2->data; +} + +static FLAC__bool compare_block_data_seektable_(const FLAC__StreamMetadata_SeekTable *block1, const FLAC__StreamMetadata_SeekTable *block2) +{ + unsigned i; + + FLAC__ASSERT(0 != block1); + FLAC__ASSERT(0 != block2); + + if(block1->num_points != block2->num_points) + return false; + + if(0 != block1->points && 0 != block2->points) { + for(i = 0; i < block1->num_points; i++) { + if(block1->points[i].sample_number != block2->points[i].sample_number) + return false; + if(block1->points[i].stream_offset != block2->points[i].stream_offset) + return false; + if(block1->points[i].frame_samples != block2->points[i].frame_samples) + return false; + } + return true; + } + else + return block1->points == block2->points; +} + +static FLAC__bool compare_block_data_vorbiscomment_(const FLAC__StreamMetadata_VorbisComment *block1, const FLAC__StreamMetadata_VorbisComment *block2) +{ + unsigned i; + + if(block1->vendor_string.length != block2->vendor_string.length) + return false; + + if(0 != block1->vendor_string.entry && 0 != block2->vendor_string.entry) { + if(0 != memcmp(block1->vendor_string.entry, block2->vendor_string.entry, block1->vendor_string.length)) + return false; + } + else if(block1->vendor_string.entry != block2->vendor_string.entry) + return false; + + if(block1->num_comments != block2->num_comments) + return false; + + for(i = 0; i < block1->num_comments; i++) { + if(0 != block1->comments[i].entry && 0 != block2->comments[i].entry) { + if(0 != memcmp(block1->comments[i].entry, block2->comments[i].entry, block1->comments[i].length)) + return false; + } + else if(block1->comments[i].entry != block2->comments[i].entry) + return false; + } + return true; +} + +static FLAC__bool compare_block_data_cuesheet_(const FLAC__StreamMetadata_CueSheet *block1, const FLAC__StreamMetadata_CueSheet *block2) +{ + unsigned i, j; + + if(0 != strcmp(block1->media_catalog_number, block2->media_catalog_number)) + return false; + + if(block1->lead_in != block2->lead_in) + return false; + + if(block1->is_cd != block2->is_cd) + return false; + + if(block1->num_tracks != block2->num_tracks) + return false; + + if(0 != block1->tracks && 0 != block2->tracks) { + FLAC__ASSERT(block1->num_tracks > 0); + for(i = 0; i < block1->num_tracks; i++) { + if(block1->tracks[i].offset != block2->tracks[i].offset) + return false; + if(block1->tracks[i].number != block2->tracks[i].number) + return false; + if(0 != memcmp(block1->tracks[i].isrc, block2->tracks[i].isrc, sizeof(block1->tracks[i].isrc))) + return false; + if(block1->tracks[i].type != block2->tracks[i].type) + return false; + if(block1->tracks[i].pre_emphasis != block2->tracks[i].pre_emphasis) + return false; + if(block1->tracks[i].num_indices != block2->tracks[i].num_indices) + return false; + if(0 != block1->tracks[i].indices && 0 != block2->tracks[i].indices) { + FLAC__ASSERT(block1->tracks[i].num_indices > 0); + for(j = 0; j < block1->tracks[i].num_indices; j++) { + if(block1->tracks[i].indices[j].offset != block2->tracks[i].indices[j].offset) + return false; + if(block1->tracks[i].indices[j].number != block2->tracks[i].indices[j].number) + return false; + } + } + else if(block1->tracks[i].indices != block2->tracks[i].indices) + return false; + } + } + else if(block1->tracks != block2->tracks) + return false; + return true; +} + +static FLAC__bool compare_block_data_picture_(const FLAC__StreamMetadata_Picture *block1, const FLAC__StreamMetadata_Picture *block2) +{ + if(block1->type != block2->type) + return false; + if(block1->mime_type != block2->mime_type && (0 == block1->mime_type || 0 == block2->mime_type || strcmp(block1->mime_type, block2->mime_type))) + return false; + if(block1->description != block2->description && (0 == block1->description || 0 == block2->description || strcmp((const char *)block1->description, (const char *)block2->description))) + return false; + if(block1->width != block2->width) + return false; + if(block1->height != block2->height) + return false; + if(block1->depth != block2->depth) + return false; + if(block1->colors != block2->colors) + return false; + if(block1->data_length != block2->data_length) + return false; + if(block1->data != block2->data && (0 == block1->data || 0 == block2->data || memcmp(block1->data, block2->data, block1->data_length))) + return false; + return true; +} + +static FLAC__bool compare_block_data_unknown_(const FLAC__StreamMetadata_Unknown *block1, const FLAC__StreamMetadata_Unknown *block2, unsigned block_length) +{ + FLAC__ASSERT(0 != block1); + FLAC__ASSERT(0 != block2); + + if(0 != block1->data && 0 != block2->data) + return 0 == memcmp(block1->data, block2->data, block_length); + else + return block1->data == block2->data; +} + +FLAC_API FLAC__bool FLAC__metadata_object_is_equal(const FLAC__StreamMetadata *block1, const FLAC__StreamMetadata *block2) +{ + FLAC__ASSERT(0 != block1); + FLAC__ASSERT(0 != block2); + + if(block1->type != block2->type) { + return false; + } + if(block1->is_last != block2->is_last) { + return false; + } + if(block1->length != block2->length) { + return false; + } + switch(block1->type) { + case FLAC__METADATA_TYPE_STREAMINFO: + return compare_block_data_streaminfo_(&block1->data.stream_info, &block2->data.stream_info); + case FLAC__METADATA_TYPE_PADDING: + return true; /* we don't compare the padding guts */ + case FLAC__METADATA_TYPE_APPLICATION: + return compare_block_data_application_(&block1->data.application, &block2->data.application, block1->length); + case FLAC__METADATA_TYPE_SEEKTABLE: + return compare_block_data_seektable_(&block1->data.seek_table, &block2->data.seek_table); + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + return compare_block_data_vorbiscomment_(&block1->data.vorbis_comment, &block2->data.vorbis_comment); + case FLAC__METADATA_TYPE_CUESHEET: + return compare_block_data_cuesheet_(&block1->data.cue_sheet, &block2->data.cue_sheet); + case FLAC__METADATA_TYPE_PICTURE: + return compare_block_data_picture_(&block1->data.picture, &block2->data.picture); + default: + return compare_block_data_unknown_(&block1->data.unknown, &block2->data.unknown, block1->length); + } +} + +FLAC_API FLAC__bool FLAC__metadata_object_application_set_data(FLAC__StreamMetadata *object, FLAC__byte *data, unsigned length, FLAC__bool copy) +{ + FLAC__byte *save; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_APPLICATION); + FLAC__ASSERT((0 != data && length > 0) || (0 == data && length == 0 && copy == false)); + + save = object->data.application.data; + + /* do the copy first so that if we fail we leave the object untouched */ + if(copy) { + if(!copy_bytes_(&object->data.application.data, data, length)) + return false; + } + else { + object->data.application.data = data; + } + + if(0 != save) + free(save); + + object->length = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8 + length; + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_seektable_resize_points(FLAC__StreamMetadata *object, unsigned new_num_points) +{ + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE); + + if(0 == object->data.seek_table.points) { + FLAC__ASSERT(object->data.seek_table.num_points == 0); + if(0 == new_num_points) + return true; + else if(0 == (object->data.seek_table.points = seekpoint_array_new_(new_num_points))) + return false; + } + else { + const unsigned old_size = object->data.seek_table.num_points * sizeof(FLAC__StreamMetadata_SeekPoint); + const unsigned new_size = new_num_points * sizeof(FLAC__StreamMetadata_SeekPoint); + + FLAC__ASSERT(object->data.seek_table.num_points > 0); + + if(new_size == 0) { + free(object->data.seek_table.points); + object->data.seek_table.points = 0; + } + else if(0 == (object->data.seek_table.points = (FLAC__StreamMetadata_SeekPoint*)realloc(object->data.seek_table.points, new_size))) + return false; + + /* if growing, set new elements to placeholders */ + if(new_size > old_size) { + unsigned i; + for(i = object->data.seek_table.num_points; i < new_num_points; i++) { + object->data.seek_table.points[i].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER; + object->data.seek_table.points[i].stream_offset = 0; + object->data.seek_table.points[i].frame_samples = 0; + } + } + } + + object->data.seek_table.num_points = new_num_points; + + seektable_calculate_length_(object); + return true; +} + +FLAC_API void FLAC__metadata_object_seektable_set_point(FLAC__StreamMetadata *object, unsigned point_num, FLAC__StreamMetadata_SeekPoint point) +{ + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE); + FLAC__ASSERT(point_num < object->data.seek_table.num_points); + + object->data.seek_table.points[point_num] = point; +} + +FLAC_API FLAC__bool FLAC__metadata_object_seektable_insert_point(FLAC__StreamMetadata *object, unsigned point_num, FLAC__StreamMetadata_SeekPoint point) +{ + int i; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE); + FLAC__ASSERT(point_num <= object->data.seek_table.num_points); + + if(!FLAC__metadata_object_seektable_resize_points(object, object->data.seek_table.num_points+1)) + return false; + + /* move all points >= point_num forward one space */ + for(i = (int)object->data.seek_table.num_points-1; i > (int)point_num; i--) + object->data.seek_table.points[i] = object->data.seek_table.points[i-1]; + + FLAC__metadata_object_seektable_set_point(object, point_num, point); + seektable_calculate_length_(object); + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_seektable_delete_point(FLAC__StreamMetadata *object, unsigned point_num) +{ + unsigned i; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE); + FLAC__ASSERT(point_num < object->data.seek_table.num_points); + + /* move all points > point_num backward one space */ + for(i = point_num; i < object->data.seek_table.num_points-1; i++) + object->data.seek_table.points[i] = object->data.seek_table.points[i+1]; + + return FLAC__metadata_object_seektable_resize_points(object, object->data.seek_table.num_points-1); +} + +FLAC_API FLAC__bool FLAC__metadata_object_seektable_is_legal(const FLAC__StreamMetadata *object) +{ + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE); + + return FLAC__format_seektable_is_legal(&object->data.seek_table); +} + +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_placeholders(FLAC__StreamMetadata *object, unsigned num) +{ + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE); + + if(num > 0) + /* WATCHOUT: we rely on the fact that growing the array adds PLACEHOLDERS at the end */ + return FLAC__metadata_object_seektable_resize_points(object, object->data.seek_table.num_points + num); + else + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_point(FLAC__StreamMetadata *object, FLAC__uint64 sample_number) +{ + FLAC__StreamMetadata_SeekTable *seek_table; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE); + + seek_table = &object->data.seek_table; + + if(!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + 1)) + return false; + + seek_table->points[seek_table->num_points - 1].sample_number = sample_number; + seek_table->points[seek_table->num_points - 1].stream_offset = 0; + seek_table->points[seek_table->num_points - 1].frame_samples = 0; + + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_points(FLAC__StreamMetadata *object, FLAC__uint64 sample_numbers[], unsigned num) +{ + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE); + FLAC__ASSERT(0 != sample_numbers || num == 0); + + if(num > 0) { + FLAC__StreamMetadata_SeekTable *seek_table = &object->data.seek_table; + unsigned i, j; + + i = seek_table->num_points; + + if(!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + num)) + return false; + + for(j = 0; j < num; i++, j++) { + seek_table->points[i].sample_number = sample_numbers[j]; + seek_table->points[i].stream_offset = 0; + seek_table->points[i].frame_samples = 0; + } + } + + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_spaced_points(FLAC__StreamMetadata *object, unsigned num, FLAC__uint64 total_samples) +{ + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE); + FLAC__ASSERT(total_samples > 0); + + if(num > 0 && total_samples > 0) { + FLAC__StreamMetadata_SeekTable *seek_table = &object->data.seek_table; + unsigned i, j; + + i = seek_table->num_points; + + if(!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + num)) + return false; + + for(j = 0; j < num; i++, j++) { + seek_table->points[i].sample_number = total_samples * (FLAC__uint64)j / (FLAC__uint64)num; + seek_table->points[i].stream_offset = 0; + seek_table->points[i].frame_samples = 0; + } + } + + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_spaced_points_by_samples(FLAC__StreamMetadata *object, unsigned samples, FLAC__uint64 total_samples) +{ + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE); + FLAC__ASSERT(samples > 0); + FLAC__ASSERT(total_samples > 0); + + if(samples > 0 && total_samples > 0) { + FLAC__StreamMetadata_SeekTable *seek_table = &object->data.seek_table; + unsigned i, j; + FLAC__uint64 num, sample; + + num = 1 + total_samples / samples; /* 1+ for the first sample at 0 */ + /* now account for the fact that we don't place a seekpoint at "total_samples" since samples are number from 0: */ + if(total_samples % samples == 0) + num--; + + i = seek_table->num_points; + + if(!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + (unsigned)num)) + return false; + + sample = 0; + for(j = 0; j < num; i++, j++, sample += samples) { + seek_table->points[i].sample_number = sample; + seek_table->points[i].stream_offset = 0; + seek_table->points[i].frame_samples = 0; + } + } + + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_sort(FLAC__StreamMetadata *object, FLAC__bool compact) +{ + unsigned unique; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE); + + unique = FLAC__format_seektable_sort(&object->data.seek_table); + + return !compact || FLAC__metadata_object_seektable_resize_points(object, unique); +} + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_vendor_string(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy) +{ + if(!FLAC__format_vorbiscomment_entry_value_is_legal(entry.entry, entry.length)) + return false; + return vorbiscomment_set_entry_(object, &object->data.vorbis_comment.vendor_string, &entry, copy); +} + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_resize_comments(FLAC__StreamMetadata *object, unsigned new_num_comments) +{ + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + + if(0 == object->data.vorbis_comment.comments) { + FLAC__ASSERT(object->data.vorbis_comment.num_comments == 0); + if(0 == new_num_comments) + return true; + else if(0 == (object->data.vorbis_comment.comments = vorbiscomment_entry_array_new_(new_num_comments))) + return false; + } + else { + const unsigned old_size = object->data.vorbis_comment.num_comments * sizeof(FLAC__StreamMetadata_VorbisComment_Entry); + const unsigned new_size = new_num_comments * sizeof(FLAC__StreamMetadata_VorbisComment_Entry); + + FLAC__ASSERT(object->data.vorbis_comment.num_comments > 0); + + /* if shrinking, free the truncated entries */ + if(new_num_comments < object->data.vorbis_comment.num_comments) { + unsigned i; + for(i = new_num_comments; i < object->data.vorbis_comment.num_comments; i++) + if(0 != object->data.vorbis_comment.comments[i].entry) + free(object->data.vorbis_comment.comments[i].entry); + } + + if(new_size == 0) { + free(object->data.vorbis_comment.comments); + object->data.vorbis_comment.comments = 0; + } + else if(0 == (object->data.vorbis_comment.comments = (FLAC__StreamMetadata_VorbisComment_Entry*)realloc(object->data.vorbis_comment.comments, new_size))) + return false; + + /* if growing, zero all the length/pointers of new elements */ + if(new_size > old_size) + memset(object->data.vorbis_comment.comments + object->data.vorbis_comment.num_comments, 0, new_size - old_size); + } + + object->data.vorbis_comment.num_comments = new_num_comments; + + vorbiscomment_calculate_length_(object); + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_comment(FLAC__StreamMetadata *object, unsigned comment_num, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy) +{ + FLAC__ASSERT(0 != object); + FLAC__ASSERT(comment_num < object->data.vorbis_comment.num_comments); + + if(!FLAC__format_vorbiscomment_entry_is_legal(entry.entry, entry.length)) + return false; + return vorbiscomment_set_entry_(object, &object->data.vorbis_comment.comments[comment_num], &entry, copy); +} + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_insert_comment(FLAC__StreamMetadata *object, unsigned comment_num, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy) +{ + FLAC__StreamMetadata_VorbisComment *vc; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + FLAC__ASSERT(comment_num <= object->data.vorbis_comment.num_comments); + + if(!FLAC__format_vorbiscomment_entry_is_legal(entry.entry, entry.length)) + return false; + + vc = &object->data.vorbis_comment; + + if(!FLAC__metadata_object_vorbiscomment_resize_comments(object, vc->num_comments+1)) + return false; + + /* move all comments >= comment_num forward one space */ + memmove(&vc->comments[comment_num+1], &vc->comments[comment_num], sizeof(FLAC__StreamMetadata_VorbisComment_Entry)*(vc->num_comments-1-comment_num)); + vc->comments[comment_num].length = 0; + vc->comments[comment_num].entry = 0; + + return FLAC__metadata_object_vorbiscomment_set_comment(object, comment_num, entry, copy); +} + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_append_comment(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy) +{ + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + return FLAC__metadata_object_vorbiscomment_insert_comment(object, object->data.vorbis_comment.num_comments, entry, copy); +} + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_replace_comment(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool all, FLAC__bool copy) +{ + FLAC__ASSERT(0 != entry.entry && entry.length > 0); + + if(!FLAC__format_vorbiscomment_entry_is_legal(entry.entry, entry.length)) + return false; + + { + int i; + size_t field_name_length; + const FLAC__byte *eq = (FLAC__byte*)memchr(entry.entry, '=', entry.length); + + FLAC__ASSERT(0 != eq); + + if(0 == eq) + return false; /* double protection */ + + field_name_length = eq-entry.entry; + + if((i = vorbiscomment_find_entry_from_(object, 0, (const char *)entry.entry, field_name_length)) >= 0) { + unsigned indx = (unsigned)i; + if(!FLAC__metadata_object_vorbiscomment_set_comment(object, indx, entry, copy)) + return false; + if(all && (indx+1 < object->data.vorbis_comment.num_comments)) { + for(i = vorbiscomment_find_entry_from_(object, indx+1, (const char *)entry.entry, field_name_length); i >= 0; ) { + if(!FLAC__metadata_object_vorbiscomment_delete_comment(object, (unsigned)i)) + return false; + if((unsigned)i < object->data.vorbis_comment.num_comments) + i = vorbiscomment_find_entry_from_(object, (unsigned)i, (const char *)entry.entry, field_name_length); + else + i = -1; + } + } + return true; + } + else + return FLAC__metadata_object_vorbiscomment_append_comment(object, entry, copy); + } +} + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_delete_comment(FLAC__StreamMetadata *object, unsigned comment_num) +{ + FLAC__StreamMetadata_VorbisComment *vc; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + FLAC__ASSERT(comment_num < object->data.vorbis_comment.num_comments); + + vc = &object->data.vorbis_comment; + + /* free the comment at comment_num */ + if(0 != vc->comments[comment_num].entry) + free(vc->comments[comment_num].entry); + + /* move all comments > comment_num backward one space */ + memmove(&vc->comments[comment_num], &vc->comments[comment_num+1], sizeof(FLAC__StreamMetadata_VorbisComment_Entry)*(vc->num_comments-comment_num-1)); + vc->comments[vc->num_comments-1].length = 0; + vc->comments[vc->num_comments-1].entry = 0; + + return FLAC__metadata_object_vorbiscomment_resize_comments(object, vc->num_comments-1); +} + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(FLAC__StreamMetadata_VorbisComment_Entry *entry, const char *field_name, const char *field_value) +{ + FLAC__ASSERT(0 != entry); + FLAC__ASSERT(0 != field_name); + FLAC__ASSERT(0 != field_value); + + if(!FLAC__format_vorbiscomment_entry_name_is_legal(field_name)) + return false; + if(!FLAC__format_vorbiscomment_entry_value_is_legal((const FLAC__byte *)field_value, (unsigned)(-1))) + return false; + + { + const size_t nn = strlen(field_name); + const size_t nv = strlen(field_value); + entry->length = nn + 1 /*=*/ + nv; + if(0 == (entry->entry = (FLAC__byte*)malloc(entry->length+1))) + return false; + memcpy(entry->entry, field_name, nn); + entry->entry[nn] = '='; + memcpy(entry->entry+nn+1, field_value, nv); + entry->entry[entry->length] = '\0'; + } + + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair(const FLAC__StreamMetadata_VorbisComment_Entry entry, char **field_name, char **field_value) +{ + FLAC__ASSERT(0 != entry.entry && entry.length > 0); + FLAC__ASSERT(0 != field_name); + FLAC__ASSERT(0 != field_value); + + if(!FLAC__format_vorbiscomment_entry_is_legal(entry.entry, entry.length)) + return false; + + { + const FLAC__byte *eq = (FLAC__byte*)memchr(entry.entry, '=', entry.length); + const size_t nn = eq-entry.entry; + const size_t nv = entry.length-nn-1; /* -1 for the '=' */ + FLAC__ASSERT(0 != eq); + if(0 == eq) + return false; /* double protection */ + if(0 == (*field_name = (char*)malloc(nn+1))) + return false; + if(0 == (*field_value = (char*)malloc(nv+1))) { + free(*field_name); + return false; + } + memcpy(*field_name, entry.entry, nn); + memcpy(*field_value, entry.entry+nn+1, nv); + (*field_name)[nn] = '\0'; + (*field_value)[nv] = '\0'; + } + + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_matches(const FLAC__StreamMetadata_VorbisComment_Entry entry, const char *field_name, unsigned field_name_length) +{ + FLAC__ASSERT(0 != entry.entry && entry.length > 0); + { + const FLAC__byte *eq = (FLAC__byte*)memchr(entry.entry, '=', entry.length); +#if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__ || defined __EMX__ +#define FLAC__STRNCASECMP strnicmp +#else +#define FLAC__STRNCASECMP strncasecmp +#endif + return (0 != eq && (unsigned)(eq-entry.entry) == field_name_length && 0 == FLAC__STRNCASECMP(field_name, (const char *)entry.entry, field_name_length)); +#undef FLAC__STRNCASECMP + } +} + +FLAC_API int FLAC__metadata_object_vorbiscomment_find_entry_from(const FLAC__StreamMetadata *object, unsigned offset, const char *field_name) +{ + FLAC__ASSERT(0 != field_name); + + return vorbiscomment_find_entry_from_(object, offset, field_name, strlen(field_name)); +} + +FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entry_matching(FLAC__StreamMetadata *object, const char *field_name) +{ + const unsigned field_name_length = strlen(field_name); + unsigned i; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + + for(i = 0; i < object->data.vorbis_comment.num_comments; i++) { + if(FLAC__metadata_object_vorbiscomment_entry_matches(object->data.vorbis_comment.comments[i], field_name, field_name_length)) { + if(!FLAC__metadata_object_vorbiscomment_delete_comment(object, i)) + return -1; + else + return 1; + } + } + + return 0; +} + +FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entries_matching(FLAC__StreamMetadata *object, const char *field_name) +{ + FLAC__bool ok = true; + unsigned matching = 0; + const unsigned field_name_length = strlen(field_name); + int i; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + + /* must delete from end to start otherwise it will interfere with our iteration */ + for(i = (int)object->data.vorbis_comment.num_comments - 1; ok && i >= 0; i--) { + if(FLAC__metadata_object_vorbiscomment_entry_matches(object->data.vorbis_comment.comments[i], field_name, field_name_length)) { + matching++; + ok &= FLAC__metadata_object_vorbiscomment_delete_comment(object, (unsigned)i); + } + } + + return ok? (int)matching : -1; +} + +FLAC_API FLAC__StreamMetadata_CueSheet_Track *FLAC__metadata_object_cuesheet_track_new(void) +{ + return (FLAC__StreamMetadata_CueSheet_Track*)calloc(1, sizeof(FLAC__StreamMetadata_CueSheet_Track)); +} + +FLAC_API FLAC__StreamMetadata_CueSheet_Track *FLAC__metadata_object_cuesheet_track_clone(const FLAC__StreamMetadata_CueSheet_Track *object) +{ + FLAC__StreamMetadata_CueSheet_Track *to; + + FLAC__ASSERT(0 != object); + + if(0 != (to = FLAC__metadata_object_cuesheet_track_new())) { + if(!copy_track_(to, object)) { + FLAC__metadata_object_cuesheet_track_delete(to); + return 0; + } + } + + return to; +} + +void FLAC__metadata_object_cuesheet_track_delete_data(FLAC__StreamMetadata_CueSheet_Track *object) +{ + FLAC__ASSERT(0 != object); + + if(0 != object->indices) { + FLAC__ASSERT(object->num_indices > 0); + free(object->indices); + } +} + +FLAC_API void FLAC__metadata_object_cuesheet_track_delete(FLAC__StreamMetadata_CueSheet_Track *object) +{ + FLAC__metadata_object_cuesheet_track_delete_data(object); + free(object); +} + +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_resize_indices(FLAC__StreamMetadata *object, unsigned track_num, unsigned new_num_indices) +{ + FLAC__StreamMetadata_CueSheet_Track *track; + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET); + FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks); + + track = &object->data.cue_sheet.tracks[track_num]; + + if(0 == track->indices) { + FLAC__ASSERT(track->num_indices == 0); + if(0 == new_num_indices) + return true; + else if(0 == (track->indices = cuesheet_track_index_array_new_(new_num_indices))) + return false; + } + else { + const unsigned old_size = track->num_indices * sizeof(FLAC__StreamMetadata_CueSheet_Index); + const unsigned new_size = new_num_indices * sizeof(FLAC__StreamMetadata_CueSheet_Index); + + FLAC__ASSERT(track->num_indices > 0); + + if(new_size == 0) { + free(track->indices); + track->indices = 0; + } + else if(0 == (track->indices = (FLAC__StreamMetadata_CueSheet_Index*)realloc(track->indices, new_size))) + return false; + + /* if growing, zero all the lengths/pointers of new elements */ + if(new_size > old_size) + memset(track->indices + track->num_indices, 0, new_size - old_size); + } + + track->num_indices = new_num_indices; + + cuesheet_calculate_length_(object); + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_insert_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num, FLAC__StreamMetadata_CueSheet_Index indx) +{ + FLAC__StreamMetadata_CueSheet_Track *track; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET); + FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks); + FLAC__ASSERT(index_num <= object->data.cue_sheet.tracks[track_num].num_indices); + + track = &object->data.cue_sheet.tracks[track_num]; + + if(!FLAC__metadata_object_cuesheet_track_resize_indices(object, track_num, track->num_indices+1)) + return false; + + /* move all indices >= index_num forward one space */ + memmove(&track->indices[index_num+1], &track->indices[index_num], sizeof(FLAC__StreamMetadata_CueSheet_Index)*(track->num_indices-1-index_num)); + + track->indices[index_num] = indx; + cuesheet_calculate_length_(object); + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_insert_blank_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num) +{ + FLAC__StreamMetadata_CueSheet_Index indx; + memset(&indx, 0, sizeof(indx)); + return FLAC__metadata_object_cuesheet_track_insert_index(object, track_num, index_num, indx); +} + +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_delete_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num) +{ + FLAC__StreamMetadata_CueSheet_Track *track; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET); + FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks); + FLAC__ASSERT(index_num < object->data.cue_sheet.tracks[track_num].num_indices); + + track = &object->data.cue_sheet.tracks[track_num]; + + /* move all indices > index_num backward one space */ + memmove(&track->indices[index_num], &track->indices[index_num+1], sizeof(FLAC__StreamMetadata_CueSheet_Index)*(track->num_indices-index_num-1)); + + FLAC__metadata_object_cuesheet_track_resize_indices(object, track_num, track->num_indices-1); + cuesheet_calculate_length_(object); + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_resize_tracks(FLAC__StreamMetadata *object, unsigned new_num_tracks) +{ + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET); + + if(0 == object->data.cue_sheet.tracks) { + FLAC__ASSERT(object->data.cue_sheet.num_tracks == 0); + if(0 == new_num_tracks) + return true; + else if(0 == (object->data.cue_sheet.tracks = cuesheet_track_array_new_(new_num_tracks))) + return false; + } + else { + const unsigned old_size = object->data.cue_sheet.num_tracks * sizeof(FLAC__StreamMetadata_CueSheet_Track); + const unsigned new_size = new_num_tracks * sizeof(FLAC__StreamMetadata_CueSheet_Track); + + FLAC__ASSERT(object->data.cue_sheet.num_tracks > 0); + + /* if shrinking, free the truncated entries */ + if(new_num_tracks < object->data.cue_sheet.num_tracks) { + unsigned i; + for(i = new_num_tracks; i < object->data.cue_sheet.num_tracks; i++) + if(0 != object->data.cue_sheet.tracks[i].indices) + free(object->data.cue_sheet.tracks[i].indices); + } + + if(new_size == 0) { + free(object->data.cue_sheet.tracks); + object->data.cue_sheet.tracks = 0; + } + else if(0 == (object->data.cue_sheet.tracks = (FLAC__StreamMetadata_CueSheet_Track*)realloc(object->data.cue_sheet.tracks, new_size))) + return false; + + /* if growing, zero all the lengths/pointers of new elements */ + if(new_size > old_size) + memset(object->data.cue_sheet.tracks + object->data.cue_sheet.num_tracks, 0, new_size - old_size); + } + + object->data.cue_sheet.num_tracks = new_num_tracks; + + cuesheet_calculate_length_(object); + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_set_track(FLAC__StreamMetadata *object, unsigned track_num, FLAC__StreamMetadata_CueSheet_Track *track, FLAC__bool copy) +{ + FLAC__ASSERT(0 != object); + FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks); + + return cuesheet_set_track_(object, object->data.cue_sheet.tracks + track_num, track, copy); +} + +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_track(FLAC__StreamMetadata *object, unsigned track_num, FLAC__StreamMetadata_CueSheet_Track *track, FLAC__bool copy) +{ + FLAC__StreamMetadata_CueSheet *cs; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET); + FLAC__ASSERT(track_num <= object->data.cue_sheet.num_tracks); + + cs = &object->data.cue_sheet; + + if(!FLAC__metadata_object_cuesheet_resize_tracks(object, cs->num_tracks+1)) + return false; + + /* move all tracks >= track_num forward one space */ + memmove(&cs->tracks[track_num+1], &cs->tracks[track_num], sizeof(FLAC__StreamMetadata_CueSheet_Track)*(cs->num_tracks-1-track_num)); + cs->tracks[track_num].num_indices = 0; + cs->tracks[track_num].indices = 0; + + return FLAC__metadata_object_cuesheet_set_track(object, track_num, track, copy); +} + +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_blank_track(FLAC__StreamMetadata *object, unsigned track_num) +{ + FLAC__StreamMetadata_CueSheet_Track track; + memset(&track, 0, sizeof(track)); + return FLAC__metadata_object_cuesheet_insert_track(object, track_num, &track, /*copy=*/false); +} + +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_delete_track(FLAC__StreamMetadata *object, unsigned track_num) +{ + FLAC__StreamMetadata_CueSheet *cs; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET); + FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks); + + cs = &object->data.cue_sheet; + + /* free the track at track_num */ + if(0 != cs->tracks[track_num].indices) + free(cs->tracks[track_num].indices); + + /* move all tracks > track_num backward one space */ + memmove(&cs->tracks[track_num], &cs->tracks[track_num+1], sizeof(FLAC__StreamMetadata_CueSheet_Track)*(cs->num_tracks-track_num-1)); + cs->tracks[cs->num_tracks-1].num_indices = 0; + cs->tracks[cs->num_tracks-1].indices = 0; + + return FLAC__metadata_object_cuesheet_resize_tracks(object, cs->num_tracks-1); +} + +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_is_legal(const FLAC__StreamMetadata *object, FLAC__bool check_cd_da_subset, const char **violation) +{ + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET); + + return FLAC__format_cuesheet_is_legal(&object->data.cue_sheet, check_cd_da_subset, violation); +} + +static FLAC__uint64 get_index_01_offset_(const FLAC__StreamMetadata_CueSheet *cs, unsigned track) +{ + if (track >= (cs->num_tracks-1) || cs->tracks[track].num_indices < 1) + return 0; + else if (cs->tracks[track].indices[0].number == 1) + return cs->tracks[track].indices[0].offset + cs->tracks[track].offset + cs->lead_in; + else if (cs->tracks[track].num_indices < 2) + return 0; + else if (cs->tracks[track].indices[1].number == 1) + return cs->tracks[track].indices[1].offset + cs->tracks[track].offset + cs->lead_in; + else + return 0; +} + +static FLAC__uint32 cddb_add_digits_(FLAC__uint32 x) +{ + FLAC__uint32 n = 0; + while (x) { + n += (x%10); + x /= 10; + } + return n; +} + +FLAC_API FLAC__uint32 FLAC__metadata_object_cuesheet_calculate_cddb_id(const FLAC__StreamMetadata *object) +{ + const FLAC__StreamMetadata_CueSheet *cs; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET); + + cs = &object->data.cue_sheet; + + if (cs->num_tracks < 2) /* need at least one real track and the lead-out track */ + return 0; + + { + FLAC__uint32 i, length, sum = 0; + for (i = 0; i < (cs->num_tracks-1); i++) /* -1 to avoid counting the lead-out */ + sum += cddb_add_digits_((FLAC__uint32)(get_index_01_offset_(cs, i) / 44100)); + length = (FLAC__uint32)((cs->tracks[cs->num_tracks-1].offset+cs->lead_in) / 44100) - (FLAC__uint32)(get_index_01_offset_(cs, 0) / 44100); + + return (sum % 0xFF) << 24 | length << 8 | (FLAC__uint32)(cs->num_tracks-1); + } +} + +FLAC_API FLAC__bool FLAC__metadata_object_picture_set_mime_type(FLAC__StreamMetadata *object, const char *mime_type, FLAC__bool copy) +{ + char *old; + size_t old_length, new_length; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_PICTURE); + FLAC__ASSERT(0 != mime_type); + + old = object->data.picture.mime_type; + old_length = old? strlen(old) : 0; + new_length = strlen(mime_type); + + /* do the copy first so that if we fail we leave the object untouched */ + if(copy) { + if(!copy_bytes_((FLAC__byte**)(&object->data.picture.mime_type), (FLAC__byte*)mime_type, new_length+1)) + return false; + } + else { + object->data.picture.mime_type = mime_type; + } + + if(0 != old) + free(old); + + object->length -= old_length; + object->length += new_length; + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_picture_set_description(FLAC__StreamMetadata *object, const FLAC__byte *description, FLAC__bool copy) +{ + FLAC__byte *old; + size_t old_length, new_length; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_PICTURE); + FLAC__ASSERT(0 != description); + + old = object->data.picture.description; + old_length = old? strlen((const char *)old) : 0; + new_length = strlen((const char *)description); + + /* do the copy first so that if we fail we leave the object untouched */ + if(copy) { + if(!copy_bytes_(&object->data.picture.description, description, new_length+1)) + return false; + } + else { + object->data.picture.description = description; + } + + if(0 != old) + free(old); + + object->length -= old_length; + object->length += new_length; + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_picture_set_data(FLAC__StreamMetadata *object, const FLAC__byte *data, FLAC__uint32 length, FLAC__bool copy) +{ + FLAC__byte *old; + + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_PICTURE); + FLAC__ASSERT((0 != data && length > 0) || (0 == data && length == 0 && copy == false)); + + old = object->data.picture.data; + + /* do the copy first so that if we fail we leave the object untouched */ + if(copy) { + if(!copy_bytes_(&object->data.picture.data, data, length)) + return false; + } + else { + object->data.picture.data = data; + } + + if(0 != old) + free(old); + + object->length -= object->data.picture.data_length; + object->data.picture.data_length = length; + object->length += length; + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_picture_is_legal(const FLAC__StreamMetadata *object, const char **violation) +{ + FLAC__ASSERT(0 != object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_PICTURE); + + return FLAC__format_picture_is_legal(&object->data.picture, violation); +} diff --git a/src/FLAC/src/libFLAC/ogg_decoder_aspect.c b/src/FLAC/src/libFLAC/ogg_decoder_aspect.c new file mode 100644 index 00000000..6974de30 --- /dev/null +++ b/src/FLAC/src/libFLAC/ogg_decoder_aspect.c @@ -0,0 +1,253 @@ +/* libFLAC - Free Lossless Audio Codec + * Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include /* for memcpy() */ +#include "FLAC/assert.h" +#include "private/ogg_decoder_aspect.h" +#include "private/ogg_mapping.h" + +#ifdef max +#undef max +#endif +#define max(x,y) ((x)>(y)?(x):(y)) + +/*********************************************************************** + * + * Public class methods + * + ***********************************************************************/ + +FLAC__bool FLAC__ogg_decoder_aspect_init(FLAC__OggDecoderAspect *aspect) +{ + /* we will determine the serial number later if necessary */ + if(ogg_stream_init(&aspect->stream_state, aspect->serial_number) != 0) + return false; + + if(ogg_sync_init(&aspect->sync_state) != 0) + return false; + + aspect->version_major = ~(0u); + aspect->version_minor = ~(0u); + + aspect->need_serial_number = aspect->use_first_serial_number; + + aspect->end_of_stream = false; + aspect->have_working_page = false; + + return true; +} + +void FLAC__ogg_decoder_aspect_finish(FLAC__OggDecoderAspect *aspect) +{ + (void)ogg_sync_clear(&aspect->sync_state); + (void)ogg_stream_clear(&aspect->stream_state); +} + +void FLAC__ogg_decoder_aspect_set_serial_number(FLAC__OggDecoderAspect *aspect, long value) +{ + aspect->use_first_serial_number = false; + aspect->serial_number = value; +} + +void FLAC__ogg_decoder_aspect_set_defaults(FLAC__OggDecoderAspect *aspect) +{ + aspect->use_first_serial_number = true; +} + +void FLAC__ogg_decoder_aspect_flush(FLAC__OggDecoderAspect *aspect) +{ + (void)ogg_stream_reset(&aspect->stream_state); + (void)ogg_sync_reset(&aspect->sync_state); + aspect->end_of_stream = false; + aspect->have_working_page = false; +} + +void FLAC__ogg_decoder_aspect_reset(FLAC__OggDecoderAspect *aspect) +{ + FLAC__ogg_decoder_aspect_flush(aspect); + + if(aspect->use_first_serial_number) + aspect->need_serial_number = true; +} + +FLAC__OggDecoderAspectReadStatus FLAC__ogg_decoder_aspect_read_callback_wrapper(FLAC__OggDecoderAspect *aspect, FLAC__byte buffer[], size_t *bytes, FLAC__OggDecoderAspectReadCallbackProxy read_callback, const FLAC__StreamDecoder *decoder, void *client_data) +{ + static const size_t OGG_BYTES_CHUNK = 8192; + const size_t bytes_requested = *bytes; + + /* + * The FLAC decoding API uses pull-based reads, whereas Ogg decoding + * is push-based. In libFLAC, when you ask to decode a frame, the + * decoder will eventually call the read callback to supply some data, + * but how much it asks for depends on how much free space it has in + * its internal buffer. It does not try to grow its internal buffer + * to accomodate a whole frame because then the internal buffer size + * could not be limited, which is necessary in embedded applications. + * + * Ogg however grows its internal buffer until a whole page is present; + * only then can you get decoded data out. So we can't just ask for + * the same number of bytes from Ogg, then pass what's decoded down to + * libFLAC. If what libFLAC is asking for will not contain a whole + * page, then we will get no data from ogg_sync_pageout(), and at the + * same time cannot just read more data from the client for the purpose + * of getting a whole decoded page because the decoded size might be + * larger than libFLAC's internal buffer. + * + * Instead, whenever this read callback wrapper is called, we will + * continually request data from the client until we have at least one + * page, and manage pages internally so that we can send pieces of + * pages down to libFLAC in such a way that we obey its size + * requirement. To limit the amount of callbacks, we will always try + * to read in enough pages to return the full number of bytes + * requested. + */ + *bytes = 0; + while (*bytes < bytes_requested && !aspect->end_of_stream) { + if (aspect->have_working_page) { + if (aspect->have_working_packet) { + size_t n = bytes_requested - *bytes; + if ((size_t)aspect->working_packet.bytes <= n) { + /* the rest of the packet will fit in the buffer */ + n = aspect->working_packet.bytes; + memcpy(buffer, aspect->working_packet.packet, n); + *bytes += n; + buffer += n; + aspect->have_working_packet = false; + } + else { + /* only n bytes of the packet will fit in the buffer */ + memcpy(buffer, aspect->working_packet.packet, n); + *bytes += n; + buffer += n; + aspect->working_packet.packet += n; + aspect->working_packet.bytes -= n; + } + } + else { + /* try and get another packet */ + const int ret = ogg_stream_packetout(&aspect->stream_state, &aspect->working_packet); + if (ret > 0) { + aspect->have_working_packet = true; + /* if it is the first header packet, check for magic and a supported Ogg FLAC mapping version */ + if (aspect->working_packet.bytes > 0 && aspect->working_packet.packet[0] == FLAC__OGG_MAPPING_FIRST_HEADER_PACKET_TYPE) { + const FLAC__byte *b = aspect->working_packet.packet; + const unsigned header_length = + FLAC__OGG_MAPPING_PACKET_TYPE_LENGTH + + FLAC__OGG_MAPPING_MAGIC_LENGTH + + FLAC__OGG_MAPPING_VERSION_MAJOR_LENGTH + + FLAC__OGG_MAPPING_VERSION_MINOR_LENGTH + + FLAC__OGG_MAPPING_NUM_HEADERS_LENGTH; + if (aspect->working_packet.bytes < (long)header_length) + return FLAC__OGG_DECODER_ASPECT_READ_STATUS_NOT_FLAC; + b += FLAC__OGG_MAPPING_PACKET_TYPE_LENGTH; + if (memcmp(b, FLAC__OGG_MAPPING_MAGIC, FLAC__OGG_MAPPING_MAGIC_LENGTH)) + return FLAC__OGG_DECODER_ASPECT_READ_STATUS_NOT_FLAC; + b += FLAC__OGG_MAPPING_MAGIC_LENGTH; + aspect->version_major = (unsigned)(*b); + b += FLAC__OGG_MAPPING_VERSION_MAJOR_LENGTH; + aspect->version_minor = (unsigned)(*b); + if (aspect->version_major != 1) + return FLAC__OGG_DECODER_ASPECT_READ_STATUS_UNSUPPORTED_MAPPING_VERSION; + aspect->working_packet.packet += header_length; + aspect->working_packet.bytes -= header_length; + } + } + else if (ret == 0) { + aspect->have_working_page = false; + } + else { /* ret < 0 */ + /* lost sync, we'll leave the working page for the next call */ + return FLAC__OGG_DECODER_ASPECT_READ_STATUS_LOST_SYNC; + } + } + } + else { + /* try and get another page */ + const int ret = ogg_sync_pageout(&aspect->sync_state, &aspect->working_page); + if (ret > 0) { + /* got a page, grab the serial number if necessary */ + if(aspect->need_serial_number) { + aspect->stream_state.serialno = aspect->serial_number = ogg_page_serialno(&aspect->working_page); + aspect->need_serial_number = false; + } + if(ogg_stream_pagein(&aspect->stream_state, &aspect->working_page) == 0) { + aspect->have_working_page = true; + aspect->have_working_packet = false; + } + /* else do nothing, could be a page from another stream */ + } + else if (ret == 0) { + /* need more data */ + const size_t ogg_bytes_to_read = max(bytes_requested - *bytes, OGG_BYTES_CHUNK); + char *oggbuf = ogg_sync_buffer(&aspect->sync_state, ogg_bytes_to_read); + + if(0 == oggbuf) { + return FLAC__OGG_DECODER_ASPECT_READ_STATUS_MEMORY_ALLOCATION_ERROR; + } + else { + size_t ogg_bytes_read = ogg_bytes_to_read; + + switch(read_callback(decoder, (FLAC__byte*)oggbuf, &ogg_bytes_read, client_data)) { + case FLAC__OGG_DECODER_ASPECT_READ_STATUS_OK: + break; + case FLAC__OGG_DECODER_ASPECT_READ_STATUS_END_OF_STREAM: + aspect->end_of_stream = true; + break; + case FLAC__OGG_DECODER_ASPECT_READ_STATUS_ABORT: + return FLAC__OGG_DECODER_ASPECT_READ_STATUS_ABORT; + default: + FLAC__ASSERT(0); + } + + if(ogg_sync_wrote(&aspect->sync_state, ogg_bytes_read) < 0) { + /* double protection; this will happen if the read callback returns more bytes than the max requested, which would overflow Ogg's internal buffer */ + FLAC__ASSERT(0); + return FLAC__OGG_DECODER_ASPECT_READ_STATUS_ERROR; + } + } + } + else { /* ret < 0 */ + /* lost sync, bail out */ + return FLAC__OGG_DECODER_ASPECT_READ_STATUS_LOST_SYNC; + } + } + } + + if (aspect->end_of_stream && *bytes == 0) { + return FLAC__OGG_DECODER_ASPECT_READ_STATUS_END_OF_STREAM; + } + + return FLAC__OGG_DECODER_ASPECT_READ_STATUS_OK; +} diff --git a/src/FLAC/src/libFLAC/ogg_encoder_aspect.c b/src/FLAC/src/libFLAC/ogg_encoder_aspect.c new file mode 100644 index 00000000..37df0893 --- /dev/null +++ b/src/FLAC/src/libFLAC/ogg_encoder_aspect.c @@ -0,0 +1,227 @@ +/* libFLAC - Free Lossless Audio Codec + * Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include /* for memset() */ +#include "FLAC/assert.h" +#include "private/ogg_encoder_aspect.h" +#include "private/ogg_mapping.h" + +static const FLAC__byte FLAC__OGG_MAPPING_VERSION_MAJOR = 1; +static const FLAC__byte FLAC__OGG_MAPPING_VERSION_MINOR = 0; + +/*********************************************************************** + * + * Public class methods + * + ***********************************************************************/ + +FLAC__bool FLAC__ogg_encoder_aspect_init(FLAC__OggEncoderAspect *aspect) +{ + /* we will determine the serial number later if necessary */ + if(ogg_stream_init(&aspect->stream_state, aspect->serial_number) != 0) + return false; + + aspect->seen_magic = false; + aspect->is_first_packet = true; + aspect->samples_written = 0; + + return true; +} + +void FLAC__ogg_encoder_aspect_finish(FLAC__OggEncoderAspect *aspect) +{ + (void)ogg_stream_clear(&aspect->stream_state); + /*@@@ what about the page? */ +} + +void FLAC__ogg_encoder_aspect_set_serial_number(FLAC__OggEncoderAspect *aspect, long value) +{ + aspect->serial_number = value; +} + +FLAC__bool FLAC__ogg_encoder_aspect_set_num_metadata(FLAC__OggEncoderAspect *aspect, unsigned value) +{ + if(value < (1u << FLAC__OGG_MAPPING_NUM_HEADERS_LEN)) { + aspect->num_metadata = value; + return true; + } + else + return false; +} + +void FLAC__ogg_encoder_aspect_set_defaults(FLAC__OggEncoderAspect *aspect) +{ + aspect->serial_number = 0; + aspect->num_metadata = 0; +} + +/* + * The basic FLAC -> Ogg mapping goes like this: + * + * - 'fLaC' magic and STREAMINFO block get combined into the first + * packet. The packet is prefixed with + * + the one-byte packet type 0x7F + * + 'FLAC' magic + * + the 2 byte Ogg FLAC mapping version number + * + tne 2 byte big-endian # of header packets + * - The first packet is flushed to the first page. + * - Each subsequent metadata block goes into its own packet. + * - Each metadata packet is flushed to page (this is not required, + * the mapping only requires that a flush must occur after all + * metadata is written). + * - Each subsequent FLAC audio frame goes into its own packet. + * + * WATCHOUT: + * This depends on the behavior of FLAC__StreamEncoder that we get a + * separate write callback for the fLaC magic, and then separate write + * callbacks for each metadata block and audio frame. + */ +FLAC__StreamEncoderWriteStatus FLAC__ogg_encoder_aspect_write_callback_wrapper(FLAC__OggEncoderAspect *aspect, const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame, FLAC__bool is_last_block, FLAC__OggEncoderAspectWriteCallbackProxy write_callback, void *encoder, void *client_data) +{ + /* WATCHOUT: + * This depends on the behavior of FLAC__StreamEncoder that 'samples' + * will be 0 for metadata writes. + */ + const FLAC__bool is_metadata = (samples == 0); + + /* + * Treat fLaC magic packet specially. We will note when we see it, then + * wait until we get the STREAMINFO and prepend it in that packet + */ + if(aspect->seen_magic) { + ogg_packet packet; + FLAC__byte synthetic_first_packet_body[ + FLAC__OGG_MAPPING_PACKET_TYPE_LENGTH + + FLAC__OGG_MAPPING_MAGIC_LENGTH + + FLAC__OGG_MAPPING_VERSION_MAJOR_LENGTH + + FLAC__OGG_MAPPING_VERSION_MINOR_LENGTH + + FLAC__OGG_MAPPING_NUM_HEADERS_LENGTH + + FLAC__STREAM_SYNC_LENGTH + + FLAC__STREAM_METADATA_HEADER_LENGTH + + FLAC__STREAM_METADATA_STREAMINFO_LENGTH + ]; + + memset(&packet, 0, sizeof(packet)); + packet.granulepos = aspect->samples_written + samples; + + if(aspect->is_first_packet) { + FLAC__byte *b = synthetic_first_packet_body; + if(bytes != FLAC__STREAM_METADATA_HEADER_LENGTH + FLAC__STREAM_METADATA_STREAMINFO_LENGTH) { + /* + * If we get here, our assumption about the way write callbacks happen + * (explained above) is wrong + */ + FLAC__ASSERT(0); + return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR; + } + /* add first header packet type */ + *b = FLAC__OGG_MAPPING_FIRST_HEADER_PACKET_TYPE; + b += FLAC__OGG_MAPPING_PACKET_TYPE_LENGTH; + /* add 'FLAC' mapping magic */ + memcpy(b, FLAC__OGG_MAPPING_MAGIC, FLAC__OGG_MAPPING_MAGIC_LENGTH); + b += FLAC__OGG_MAPPING_MAGIC_LENGTH; + /* add Ogg FLAC mapping major version number */ + memcpy(b, &FLAC__OGG_MAPPING_VERSION_MAJOR, FLAC__OGG_MAPPING_VERSION_MAJOR_LENGTH); + b += FLAC__OGG_MAPPING_VERSION_MAJOR_LENGTH; + /* add Ogg FLAC mapping minor version number */ + memcpy(b, &FLAC__OGG_MAPPING_VERSION_MINOR, FLAC__OGG_MAPPING_VERSION_MINOR_LENGTH); + b += FLAC__OGG_MAPPING_VERSION_MINOR_LENGTH; + /* add number of header packets */ + *b = (FLAC__byte)(aspect->num_metadata >> 8); + b++; + *b = (FLAC__byte)(aspect->num_metadata); + b++; + /* add native FLAC 'fLaC' magic */ + memcpy(b, FLAC__STREAM_SYNC_STRING, FLAC__STREAM_SYNC_LENGTH); + b += FLAC__STREAM_SYNC_LENGTH; + /* add STREAMINFO */ + memcpy(b, buffer, bytes); + FLAC__ASSERT(b + bytes - synthetic_first_packet_body == sizeof(synthetic_first_packet_body)); + packet.packet = (unsigned char *)synthetic_first_packet_body; + packet.bytes = sizeof(synthetic_first_packet_body); + + packet.b_o_s = 1; + aspect->is_first_packet = false; + } + else { + packet.packet = (unsigned char *)buffer; + packet.bytes = bytes; + } + + if(is_last_block) { + /* we used to check: + * FLAC__ASSERT(total_samples_estimate == 0 || total_samples_estimate == aspect->samples_written + samples); + * but it's really not useful since total_samples_estimate is an estimate and can be inexact + */ + packet.e_o_s = 1; + } + + if(ogg_stream_packetin(&aspect->stream_state, &packet) != 0) + return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR; + + /*@@@ can't figure out a way to pass a useful number for 'samples' to the write_callback, so we'll just pass 0 */ + if(is_metadata) { + while(ogg_stream_flush(&aspect->stream_state, &aspect->page) != 0) { + if(write_callback(encoder, aspect->page.header, aspect->page.header_len, 0, current_frame, client_data) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK) + return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR; + if(write_callback(encoder, aspect->page.body, aspect->page.body_len, 0, current_frame, client_data) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK) + return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR; + } + } + else { + while(ogg_stream_pageout(&aspect->stream_state, &aspect->page) != 0) { + if(write_callback(encoder, aspect->page.header, aspect->page.header_len, 0, current_frame, client_data) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK) + return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR; + if(write_callback(encoder, aspect->page.body, aspect->page.body_len, 0, current_frame, client_data) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK) + return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR; + } + } + } + else if(is_metadata && current_frame == 0 && samples == 0 && bytes == 4 && 0 == memcmp(buffer, FLAC__STREAM_SYNC_STRING, sizeof(FLAC__STREAM_SYNC_STRING))) { + aspect->seen_magic = true; + } + else { + /* + * If we get here, our assumption about the way write callbacks happen + * explained above is wrong + */ + FLAC__ASSERT(0); + return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR; + } + + aspect->samples_written += samples; + + return FLAC__STREAM_ENCODER_WRITE_STATUS_OK; +} diff --git a/src/FLAC/src/libFLAC/ogg_helper.c b/src/FLAC/src/libFLAC/ogg_helper.c new file mode 100644 index 00000000..aeaf99f4 --- /dev/null +++ b/src/FLAC/src/libFLAC/ogg_helper.c @@ -0,0 +1,208 @@ +/* libFLAC - Free Lossless Audio Codec + * Copyright (C) 2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include /* for malloc() */ +#include /* for memcmp(), memcpy() */ +#include "FLAC/assert.h" +#include "private/ogg_helper.h" +#include "protected/stream_encoder.h" + + +static FLAC__bool full_read_(FLAC__StreamEncoder *encoder, FLAC__byte *buffer, size_t bytes, FLAC__StreamEncoderReadCallback read_callback, void *client_data) +{ + while(bytes > 0) { + size_t bytes_read = bytes; + switch(read_callback(encoder, buffer, &bytes_read, client_data)) { + case FLAC__STREAM_ENCODER_READ_STATUS_CONTINUE: + bytes -= bytes_read; + buffer += bytes_read; + break; + case FLAC__STREAM_ENCODER_READ_STATUS_END_OF_STREAM: + if(bytes_read == 0) { + encoder->protected_->state = FLAC__STREAM_ENCODER_OGG_ERROR; + return false; + } + bytes -= bytes_read; + buffer += bytes_read; + break; + case FLAC__STREAM_ENCODER_READ_STATUS_ABORT: + encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR; + return false; + case FLAC__STREAM_ENCODER_READ_STATUS_UNSUPPORTED: + return false; + default: + /* double protection: */ + FLAC__ASSERT(0); + encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR; + return false; + } + } + + return true; +} + +void simple_ogg_page__init(ogg_page *page) +{ + page->header = 0; + page->header_len = 0; + page->body = 0; + page->body_len = 0; +} + +void simple_ogg_page__clear(ogg_page *page) +{ + if(page->header) + free(page->header); + if(page->body) + free(page->body); + simple_ogg_page__init(page); +} + +FLAC__bool simple_ogg_page__get_at(FLAC__StreamEncoder *encoder, FLAC__uint64 position, ogg_page *page, FLAC__StreamEncoderSeekCallback seek_callback, FLAC__StreamEncoderReadCallback read_callback, void *client_data) +{ + static const unsigned OGG_HEADER_FIXED_PORTION_LEN = 27; + static const unsigned OGG_MAX_HEADER_LEN = 27/*OGG_HEADER_FIXED_PORTION_LEN*/ + 255; + FLAC__byte crc[4]; + FLAC__StreamEncoderSeekStatus seek_status; + + FLAC__ASSERT(page->header == 0); + FLAC__ASSERT(page->header_len == 0); + FLAC__ASSERT(page->body == 0); + FLAC__ASSERT(page->body_len == 0); + + /* move the stream pointer to the supposed beginning of the page */ + if(0 == seek_callback) + return false; + if((seek_status = seek_callback((FLAC__StreamEncoder*)encoder, position, client_data)) != FLAC__STREAM_ENCODER_SEEK_STATUS_OK) { + if(seek_status == FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR) + encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR; + return false; + } + + /* allocate space for the page header */ + if(0 == (page->header = (unsigned char *)malloc(OGG_MAX_HEADER_LEN))) { + encoder->protected_->state = FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR; + return false; + } + + /* read in the fixed part of the page header (up to but not including + * the segment table */ + if(!full_read_(encoder, page->header, OGG_HEADER_FIXED_PORTION_LEN, read_callback, client_data)) + return false; + + page->header_len = OGG_HEADER_FIXED_PORTION_LEN + page->header[26]; + + /* check to see if it's a correct, "simple" page (one packet only) */ + if( + memcmp(page->header, "OggS", 4) || /* doesn't start with OggS */ + (page->header[5] & 0x01) || /* continued packet */ + memcmp(page->header+6, "\0\0\0\0\0\0\0\0", 8) || /* granulepos is non-zero */ + page->header[26] == 0 /* packet is 0-size */ + ) { + encoder->protected_->state = FLAC__STREAM_ENCODER_OGG_ERROR; + return false; + } + + /* read in the segment table */ + if(!full_read_(encoder, page->header + OGG_HEADER_FIXED_PORTION_LEN, page->header[26], read_callback, client_data)) + return false; + + { + unsigned i; + + /* check to see that it specifies a single packet */ + for(i = 0; i < (unsigned)page->header[26] - 1; i++) { + if(page->header[i + OGG_HEADER_FIXED_PORTION_LEN] != 255) { + encoder->protected_->state = FLAC__STREAM_ENCODER_OGG_ERROR; + return false; + } + } + + page->body_len = 255 * i + page->header[i + OGG_HEADER_FIXED_PORTION_LEN]; + } + + /* allocate space for the page body */ + if(0 == (page->body = (unsigned char *)malloc(page->body_len))) { + encoder->protected_->state = FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR; + return false; + } + + /* read in the page body */ + if(!full_read_(encoder, page->body, page->body_len, read_callback, client_data)) + return false; + + /* check the CRC */ + memcpy(crc, page->header+22, 4); + ogg_page_checksum_set(page); + if(memcmp(crc, page->header+22, 4)) { + encoder->protected_->state = FLAC__STREAM_ENCODER_OGG_ERROR; + return false; + } + + return true; +} + +FLAC__bool simple_ogg_page__set_at(FLAC__StreamEncoder *encoder, FLAC__uint64 position, ogg_page *page, FLAC__StreamEncoderSeekCallback seek_callback, FLAC__StreamEncoderWriteCallback write_callback, void *client_data) +{ + FLAC__StreamEncoderSeekStatus seek_status; + + FLAC__ASSERT(page->header != 0); + FLAC__ASSERT(page->header_len != 0); + FLAC__ASSERT(page->body != 0); + FLAC__ASSERT(page->body_len != 0); + + /* move the stream pointer to the supposed beginning of the page */ + if(0 == seek_callback) + return false; + if((seek_status = seek_callback((FLAC__StreamEncoder*)encoder, position, client_data)) != FLAC__STREAM_ENCODER_SEEK_STATUS_OK) { + if(seek_status == FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR) + encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR; + return false; + } + + ogg_page_checksum_set(page); + + /* re-write the page */ + if(write_callback((FLAC__StreamEncoder*)encoder, page->header, page->header_len, 0, 0, client_data) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK) { + encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR; + return false; + } + if(write_callback((FLAC__StreamEncoder*)encoder, page->body, page->body_len, 0, 0, client_data) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK) { + encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR; + return false; + } + + return true; +} diff --git a/src/FLAC/src/libFLAC/ogg_mapping.c b/src/FLAC/src/libFLAC/ogg_mapping.c new file mode 100644 index 00000000..a5188926 --- /dev/null +++ b/src/FLAC/src/libFLAC/ogg_mapping.c @@ -0,0 +1,47 @@ +/* libFLAC - Free Lossless Audio Codec + * Copyright (C) 2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include "private/ogg_mapping.h" + +const unsigned FLAC__OGG_MAPPING_PACKET_TYPE_LEN = 8; /* bits */ + +const FLAC__byte FLAC__OGG_MAPPING_FIRST_HEADER_PACKET_TYPE = 0x7f; + +const FLAC__byte * const FLAC__OGG_MAPPING_MAGIC = (const FLAC__byte * const)"FLAC"; + +const unsigned FLAC__OGG_MAPPING_VERSION_MAJOR_LEN = 8; /* bits */ +const unsigned FLAC__OGG_MAPPING_VERSION_MINOR_LEN = 8; /* bits */ + +const unsigned FLAC__OGG_MAPPING_NUM_HEADERS_LEN = 16; /* bits */ diff --git a/src/FLAC/src/libFLAC/ppc/Makefile.am b/src/FLAC/src/libFLAC/ppc/Makefile.am new file mode 100644 index 00000000..87cd95c6 --- /dev/null +++ b/src/FLAC/src/libFLAC/ppc/Makefile.am @@ -0,0 +1,31 @@ +# libFLAC - Free Lossless Audio Codec library +# Copyright (C) 2004,2005,2006,2007 Josh Coalson +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# - Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# - Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# - Neither the name of the Xiph.org Foundation nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +SUBDIRS = as gas diff --git a/src/FLAC/src/libFLAC/ppc/as/Makefile.am b/src/FLAC/src/libFLAC/ppc/as/Makefile.am new file mode 100644 index 00000000..f65e3cf7 --- /dev/null +++ b/src/FLAC/src/libFLAC/ppc/as/Makefile.am @@ -0,0 +1,34 @@ +# libFLAC - Free Lossless Audio Codec library +# Copyright (C) 2004,2005,2006,2007 Josh Coalson +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# - Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# - Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# - Neither the name of the Xiph.org Foundation nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +EXTRA_DIST = \ + lpc_asm.s + diff --git a/src/FLAC/src/libFLAC/ppc/as/lpc_asm.s b/src/FLAC/src/libFLAC/ppc/as/lpc_asm.s new file mode 100644 index 00000000..ca39c6ff --- /dev/null +++ b/src/FLAC/src/libFLAC/ppc/as/lpc_asm.s @@ -0,0 +1,429 @@ +; libFLAC - Free Lossless Audio Codec library +; Copyright (C) 2004,2005,2006,2007 Josh Coalson +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions +; are met: +; +; - Redistributions of source code must retain the above copyright +; notice, this list of conditions and the following disclaimer. +; +; - Redistributions in binary form must reproduce the above copyright +; notice, this list of conditions and the following disclaimer in the +; documentation and/or other materials provided with the distribution. +; +; - Neither the name of the Xiph.org Foundation nor the names of its +; contributors may be used to endorse or promote products derived from +; this software without specific prior written permission. +; +; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +; ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +; LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +; A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR +; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +; EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +; PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +; LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +.text + .align 2 +.globl _FLAC__lpc_restore_signal_asm_ppc_altivec_16 + +.globl _FLAC__lpc_restore_signal_asm_ppc_altivec_16_order8 + +_FLAC__lpc_restore_signal_asm_ppc_altivec_16: +; r3: residual[] +; r4: data_len +; r5: qlp_coeff[] +; r6: order +; r7: lp_quantization +; r8: data[] + +; see src/libFLAC/lpc.c:FLAC__lpc_restore_signal() +; these is a PowerPC/Altivec assembly version which requires bps<=16 (or actual +; bps<=15 for mid-side coding, since that uses an extra bit) + +; these should be fast; the inner loop is unrolled (it takes no more than +; 3*(order%4) instructions, all of which are arithmetic), and all of the +; coefficients and all relevant history stay in registers, so the outer loop +; has only one load from memory (the residual) + +; I have not yet run this through simg4, so there may be some avoidable stalls, +; and there may be a somewhat more clever way to do the outer loop + +; the branch mechanism may prevent dynamic loading; I still need to examine +; this issue, and there may be a more elegant method + + stmw r31,-4(r1) + + addi r9,r1,-28 + li r31,0xf + andc r9,r9,r31 ; for quadword-aligned stack data + + slwi r6,r6,2 ; adjust for word size + slwi r4,r4,2 + add r4,r4,r8 ; r4 = data+data_len + + mfspr r0,256 ; cache old vrsave + addis r31,0,hi16(0xfffffc00) + ori r31,r31,lo16(0xfffffc00) + mtspr 256,r31 ; declare VRs in vrsave + + cmplw cr0,r8,r4 ; i> lp_quantization + + lvewx v21,0,r3 ; v21[n]: *residual + vperm v21,v21,v21,v18 ; v21[3]: *residual + vaddsws v20,v21,v20 ; v20[3]: *residual + (sum >> lp_quantization) + vsldoi v18,v18,v18,4 ; increment shift vector + + vperm v21,v20,v20,v17 ; v21[n]: shift for storage + vsldoi v17,v17,v17,12 ; increment shift vector + stvewx v21,0,r8 + + vsldoi v20,v20,v20,12 + vsldoi v8,v8,v20,4 ; insert value onto history + + addi r3,r3,4 + addi r8,r8,4 + cmplw cr0,r8,r4 ; i> lp_quantization + + lvewx v9,0,r3 ; v9[n]: *residual + vperm v9,v9,v9,v6 ; v9[3]: *residual + vaddsws v8,v9,v8 ; v8[3]: *residual + (sum >> lp_quantization) + vsldoi v6,v6,v6,4 ; increment shift vector + + vperm v9,v8,v8,v5 ; v9[n]: shift for storage + vsldoi v5,v5,v5,12 ; increment shift vector + stvewx v9,0,r8 + + vsldoi v8,v8,v8,12 + vsldoi v2,v2,v8,4 ; insert value onto history + + addi r3,r3,4 + addi r8,r8,4 + cmplw cr0,r8,r4 ; i> lp_quantization + + lvewx v21,0,r3 # v21[n]: *residual + vperm v21,v21,v21,v18 # v21[3]: *residual + vaddsws v20,v21,v20 # v20[3]: *residual + (sum >> lp_quantization) + vsldoi v18,v18,v18,4 # increment shift vector + + vperm v21,v20,v20,v17 # v21[n]: shift for storage + vsldoi v17,v17,v17,12 # increment shift vector + stvewx v21,0,r8 + + vsldoi v20,v20,v20,12 + vsldoi v8,v8,v20,4 # insert value onto history + + addi r3,r3,4 + addi r8,r8,4 + cmplw cr0,r8,r4 # i> lp_quantization + + lvewx v9,0,r3 # v9[n]: *residual + vperm v9,v9,v9,v6 # v9[3]: *residual + vaddsws v8,v9,v8 # v8[3]: *residual + (sum >> lp_quantization) + vsldoi v6,v6,v6,4 # increment shift vector + + vperm v9,v8,v8,v5 # v9[n]: shift for storage + vsldoi v5,v5,v5,12 # increment shift vector + stvewx v9,0,r8 + + vsldoi v8,v8,v8,12 + vsldoi v2,v2,v8,4 # insert value onto history + + addi r3,r3,4 + addi r8,r8,4 + cmplw cr0,r8,r4 # i +#endif + +#if defined _MSC_VER || defined __MINGW32__ +#include /* for _setmode() */ +#include /* for _O_BINARY */ +#endif +#if defined __CYGWIN__ || defined __EMX__ +#include /* for setmode(), O_BINARY */ +#include /* for _O_BINARY */ +#endif +#include +#include /* for malloc() */ +#include /* for memset/memcpy() */ +#include /* for stat() */ +#include /* for off_t */ +#if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__ +#if _MSC_VER <= 1600 || defined __BORLANDC__ /* @@@ [2G limit] */ +#define fseeko fseek +#define ftello ftell +#endif +#endif +#include "FLAC/assert.h" +#include "protected/stream_decoder.h" +#include "private/bitreader.h" +#include "private/bitmath.h" +#include "private/cpu.h" +#include "private/crc.h" +#include "private/fixed.h" +#include "private/format.h" +#include "private/lpc.h" +#include "private/md5.h" +#include "private/memory.h" + +#ifdef max +#undef max +#endif +#define max(a,b) ((a)>(b)?(a):(b)) + +/* adjust for compilers that can't understand using LLU suffix for uint64_t literals */ +#ifdef _MSC_VER +#define FLAC__U64L(x) x +#else +#define FLAC__U64L(x) x##LLU +#endif + + +/* technically this should be in an "export.c" but this is convenient enough */ +FLAC_API int FLAC_API_SUPPORTS_OGG_FLAC = +#if FLAC__HAS_OGG + 1 +#else + 0 +#endif +; + + +/*********************************************************************** + * + * Private static data + * + ***********************************************************************/ + +static FLAC__byte ID3V2_TAG_[3] = { 'I', 'D', '3' }; + +/*********************************************************************** + * + * Private class method prototypes + * + ***********************************************************************/ + +static void set_defaults_(FLAC__StreamDecoder *decoder); +static FILE *get_binary_stdin_(void); +static FLAC__bool allocate_output_(FLAC__StreamDecoder *decoder, unsigned size, unsigned channels); +static FLAC__bool has_id_filtered_(FLAC__StreamDecoder *decoder, FLAC__byte *id); +static FLAC__bool find_metadata_(FLAC__StreamDecoder *decoder); +static FLAC__bool read_metadata_(FLAC__StreamDecoder *decoder); +static FLAC__bool read_metadata_streaminfo_(FLAC__StreamDecoder *decoder, FLAC__bool is_last, unsigned length); +static FLAC__bool read_metadata_seektable_(FLAC__StreamDecoder *decoder, FLAC__bool is_last, unsigned length); +static FLAC__bool read_metadata_vorbiscomment_(FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_VorbisComment *obj); +static FLAC__bool read_metadata_cuesheet_(FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_CueSheet *obj); +static FLAC__bool read_metadata_picture_(FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_Picture *obj); +static FLAC__bool skip_id3v2_tag_(FLAC__StreamDecoder *decoder); +static FLAC__bool frame_sync_(FLAC__StreamDecoder *decoder); +static FLAC__bool read_frame_(FLAC__StreamDecoder *decoder, FLAC__bool *got_a_frame, FLAC__bool do_full_decode); +static FLAC__bool read_frame_header_(FLAC__StreamDecoder *decoder); +static FLAC__bool read_subframe_(FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, FLAC__bool do_full_decode); +static FLAC__bool read_subframe_constant_(FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, FLAC__bool do_full_decode); +static FLAC__bool read_subframe_fixed_(FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, const unsigned order, FLAC__bool do_full_decode); +static FLAC__bool read_subframe_lpc_(FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, const unsigned order, FLAC__bool do_full_decode); +static FLAC__bool read_subframe_verbatim_(FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, FLAC__bool do_full_decode); +static FLAC__bool read_residual_partitioned_rice_(FLAC__StreamDecoder *decoder, unsigned predictor_order, unsigned partition_order, FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents, FLAC__int32 *residual); +static FLAC__bool read_zero_padding_(FLAC__StreamDecoder *decoder); +static FLAC__bool read_callback_(FLAC__byte buffer[], size_t *bytes, void *client_data); +#if FLAC__HAS_OGG +static FLAC__StreamDecoderReadStatus read_callback_ogg_aspect_(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes); +static FLAC__OggDecoderAspectReadStatus read_callback_proxy_(const void *void_decoder, FLAC__byte buffer[], size_t *bytes, void *client_data); +#endif +static FLAC__StreamDecoderWriteStatus write_audio_frame_to_client_(FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[]); +static void send_error_to_client_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status); +static FLAC__bool seek_to_absolute_sample_(FLAC__StreamDecoder *decoder, FLAC__uint64 stream_length, FLAC__uint64 target_sample); +#if FLAC__HAS_OGG +static FLAC__bool seek_to_absolute_sample_ogg_(FLAC__StreamDecoder *decoder, FLAC__uint64 stream_length, FLAC__uint64 target_sample); +#endif +static FLAC__StreamDecoderReadStatus file_read_callback_(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data); +static FLAC__StreamDecoderSeekStatus file_seek_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data); +static FLAC__StreamDecoderTellStatus file_tell_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data); +static FLAC__StreamDecoderLengthStatus file_length_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data); +static FLAC__bool file_eof_callback_(const FLAC__StreamDecoder *decoder, void *client_data); + +/*********************************************************************** + * + * Private class data + * + ***********************************************************************/ + +typedef struct FLAC__StreamDecoderPrivate { +#if FLAC__HAS_OGG + FLAC__bool is_ogg; +#endif + FLAC__StreamDecoderReadCallback read_callback; + FLAC__StreamDecoderSeekCallback seek_callback; + FLAC__StreamDecoderTellCallback tell_callback; + FLAC__StreamDecoderLengthCallback length_callback; + FLAC__StreamDecoderEofCallback eof_callback; + FLAC__StreamDecoderWriteCallback write_callback; + FLAC__StreamDecoderMetadataCallback metadata_callback; + FLAC__StreamDecoderErrorCallback error_callback; + /* generic 32-bit datapath: */ + void (*local_lpc_restore_signal)(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); + /* generic 64-bit datapath: */ + void (*local_lpc_restore_signal_64bit)(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); + /* for use when the signal is <= 16 bits-per-sample, or <= 15 bits-per-sample on a side channel (which requires 1 extra bit): */ + void (*local_lpc_restore_signal_16bit)(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); + /* for use when the signal is <= 16 bits-per-sample, or <= 15 bits-per-sample on a side channel (which requires 1 extra bit), AND order <= 8: */ + void (*local_lpc_restore_signal_16bit_order8)(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); + FLAC__bool (*local_bitreader_read_rice_signed_block)(FLAC__BitReader *br, int vals[], unsigned nvals, unsigned parameter); + void *client_data; + FILE *file; /* only used if FLAC__stream_decoder_init_file()/FLAC__stream_decoder_init_file() called, else NULL */ + FLAC__BitReader *input; + FLAC__int32 *output[FLAC__MAX_CHANNELS]; + FLAC__int32 *residual[FLAC__MAX_CHANNELS]; /* WATCHOUT: these are the aligned pointers; the real pointers that should be free()'d are residual_unaligned[] below */ + FLAC__EntropyCodingMethod_PartitionedRiceContents partitioned_rice_contents[FLAC__MAX_CHANNELS]; + unsigned output_capacity, output_channels; + FLAC__uint32 last_frame_number; + FLAC__uint32 last_block_size; + FLAC__uint64 samples_decoded; + FLAC__bool has_stream_info, has_seek_table; + FLAC__StreamMetadata stream_info; + FLAC__StreamMetadata seek_table; + FLAC__bool metadata_filter[128]; /* MAGIC number 128 == total number of metadata block types == 1 << 7 */ + FLAC__byte *metadata_filter_ids; + unsigned metadata_filter_ids_count, metadata_filter_ids_capacity; /* units for both are IDs, not bytes */ + FLAC__Frame frame; + FLAC__bool cached; /* true if there is a byte in lookahead */ + FLAC__CPUInfo cpuinfo; + FLAC__byte header_warmup[2]; /* contains the sync code and reserved bits */ + FLAC__byte lookahead; /* temp storage when we need to look ahead one byte in the stream */ + /* unaligned (original) pointers to allocated data */ + FLAC__int32 *residual_unaligned[FLAC__MAX_CHANNELS]; + FLAC__bool do_md5_checking; /* initially gets protected_->md5_checking but is turned off after a seek or if the metadata has a zero MD5 */ + FLAC__bool internal_reset_hack; /* used only during init() so we can call reset to set up the decoder without rewinding the input */ + FLAC__bool is_seeking; + FLAC__MD5Context md5context; + FLAC__byte computed_md5sum[16]; /* this is the sum we computed from the decoded data */ + /* (the rest of these are only used for seeking) */ + FLAC__Frame last_frame; /* holds the info of the last frame we seeked to */ + FLAC__uint64 first_frame_offset; /* hint to the seek routine of where in the stream the first audio frame starts */ + FLAC__uint64 target_sample; + unsigned unparseable_frame_count; /* used to tell whether we're decoding a future version of FLAC or just got a bad sync */ +#if FLAC__HAS_OGG + FLAC__bool got_a_frame; /* hack needed in Ogg FLAC seek routine to check when process_single() actually writes a frame */ +#endif +} FLAC__StreamDecoderPrivate; + +/*********************************************************************** + * + * Public static class data + * + ***********************************************************************/ + +FLAC_API const char * const FLAC__StreamDecoderStateString[] = { + "FLAC__STREAM_DECODER_SEARCH_FOR_METADATA", + "FLAC__STREAM_DECODER_READ_METADATA", + "FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC", + "FLAC__STREAM_DECODER_READ_FRAME", + "FLAC__STREAM_DECODER_END_OF_STREAM", + "FLAC__STREAM_DECODER_OGG_ERROR", + "FLAC__STREAM_DECODER_SEEK_ERROR", + "FLAC__STREAM_DECODER_ABORTED", + "FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR", + "FLAC__STREAM_DECODER_UNINITIALIZED" +}; + +FLAC_API const char * const FLAC__StreamDecoderInitStatusString[] = { + "FLAC__STREAM_DECODER_INIT_STATUS_OK", + "FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER", + "FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS", + "FLAC__STREAM_DECODER_INIT_STATUS_MEMORY_ALLOCATION_ERROR", + "FLAC__STREAM_DECODER_INIT_STATUS_ERROR_OPENING_FILE", + "FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED" +}; + +FLAC_API const char * const FLAC__StreamDecoderReadStatusString[] = { + "FLAC__STREAM_DECODER_READ_STATUS_CONTINUE", + "FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM", + "FLAC__STREAM_DECODER_READ_STATUS_ABORT" +}; + +FLAC_API const char * const FLAC__StreamDecoderSeekStatusString[] = { + "FLAC__STREAM_DECODER_SEEK_STATUS_OK", + "FLAC__STREAM_DECODER_SEEK_STATUS_ERROR", + "FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED" +}; + +FLAC_API const char * const FLAC__StreamDecoderTellStatusString[] = { + "FLAC__STREAM_DECODER_TELL_STATUS_OK", + "FLAC__STREAM_DECODER_TELL_STATUS_ERROR", + "FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED" +}; + +FLAC_API const char * const FLAC__StreamDecoderLengthStatusString[] = { + "FLAC__STREAM_DECODER_LENGTH_STATUS_OK", + "FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR", + "FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED" +}; + +FLAC_API const char * const FLAC__StreamDecoderWriteStatusString[] = { + "FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE", + "FLAC__STREAM_DECODER_WRITE_STATUS_ABORT" +}; + +FLAC_API const char * const FLAC__StreamDecoderErrorStatusString[] = { + "FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC", + "FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER", + "FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH", + "FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM" +}; + +/*********************************************************************** + * + * Class constructor/destructor + * + ***********************************************************************/ +FLAC_API FLAC__StreamDecoder *FLAC__stream_decoder_new(void) +{ + FLAC__StreamDecoder *decoder; + unsigned i; + + FLAC__ASSERT(sizeof(int) >= 4); /* we want to die right away if this is not true */ + + decoder = (FLAC__StreamDecoder*)calloc(1, sizeof(FLAC__StreamDecoder)); + if(decoder == 0) { + return 0; + } + + decoder->protected_ = (FLAC__StreamDecoderProtected*)calloc(1, sizeof(FLAC__StreamDecoderProtected)); + if(decoder->protected_ == 0) { + free(decoder); + return 0; + } + + decoder->private_ = (FLAC__StreamDecoderPrivate*)calloc(1, sizeof(FLAC__StreamDecoderPrivate)); + if(decoder->private_ == 0) { + free(decoder->protected_); + free(decoder); + return 0; + } + + decoder->private_->input = FLAC__bitreader_new(); + if(decoder->private_->input == 0) { + free(decoder->private_); + free(decoder->protected_); + free(decoder); + return 0; + } + + decoder->private_->metadata_filter_ids_capacity = 16; + if(0 == (decoder->private_->metadata_filter_ids = (FLAC__byte*)malloc((FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8) * decoder->private_->metadata_filter_ids_capacity))) { + FLAC__bitreader_delete(decoder->private_->input); + free(decoder->private_); + free(decoder->protected_); + free(decoder); + return 0; + } + + for(i = 0; i < FLAC__MAX_CHANNELS; i++) { + decoder->private_->output[i] = 0; + decoder->private_->residual_unaligned[i] = decoder->private_->residual[i] = 0; + } + + decoder->private_->output_capacity = 0; + decoder->private_->output_channels = 0; + decoder->private_->has_seek_table = false; + + for(i = 0; i < FLAC__MAX_CHANNELS; i++) + FLAC__format_entropy_coding_method_partitioned_rice_contents_init(&decoder->private_->partitioned_rice_contents[i]); + + decoder->private_->file = 0; + + set_defaults_(decoder); + + decoder->protected_->state = FLAC__STREAM_DECODER_UNINITIALIZED; + + return decoder; +} + +FLAC_API void FLAC__stream_decoder_delete(FLAC__StreamDecoder *decoder) +{ + unsigned i; + + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->private_->input); + + (void)FLAC__stream_decoder_finish(decoder); + + if(0 != decoder->private_->metadata_filter_ids) + free(decoder->private_->metadata_filter_ids); + + FLAC__bitreader_delete(decoder->private_->input); + + for(i = 0; i < FLAC__MAX_CHANNELS; i++) + FLAC__format_entropy_coding_method_partitioned_rice_contents_clear(&decoder->private_->partitioned_rice_contents[i]); + + free(decoder->private_); + free(decoder->protected_); + free(decoder); +} + +/*********************************************************************** + * + * Public class methods + * + ***********************************************************************/ + +static FLAC__StreamDecoderInitStatus init_stream_internal_( + FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderReadCallback read_callback, + FLAC__StreamDecoderSeekCallback seek_callback, + FLAC__StreamDecoderTellCallback tell_callback, + FLAC__StreamDecoderLengthCallback length_callback, + FLAC__StreamDecoderEofCallback eof_callback, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data, + FLAC__bool is_ogg +) +{ + FLAC__ASSERT(0 != decoder); + + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED; + +#if !FLAC__HAS_OGG + if(is_ogg) + return FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER; +#endif + + if( + 0 == read_callback || + 0 == write_callback || + 0 == error_callback || + (seek_callback && (0 == tell_callback || 0 == length_callback || 0 == eof_callback)) + ) + return FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS; + +#if FLAC__HAS_OGG + decoder->private_->is_ogg = is_ogg; + if(is_ogg && !FLAC__ogg_decoder_aspect_init(&decoder->protected_->ogg_decoder_aspect)) + return decoder->protected_->state = FLAC__STREAM_DECODER_OGG_ERROR; +#endif + + /* + * get the CPU info and set the function pointers + */ + FLAC__cpu_info(&decoder->private_->cpuinfo); + /* first default to the non-asm routines */ + decoder->private_->local_lpc_restore_signal = FLAC__lpc_restore_signal; + decoder->private_->local_lpc_restore_signal_64bit = FLAC__lpc_restore_signal_wide; + decoder->private_->local_lpc_restore_signal_16bit = FLAC__lpc_restore_signal; + decoder->private_->local_lpc_restore_signal_16bit_order8 = FLAC__lpc_restore_signal; + decoder->private_->local_bitreader_read_rice_signed_block = FLAC__bitreader_read_rice_signed_block; + /* now override with asm where appropriate */ +#ifndef FLAC__NO_ASM + if(decoder->private_->cpuinfo.use_asm) { +#ifdef FLAC__CPU_IA32 + FLAC__ASSERT(decoder->private_->cpuinfo.type == FLAC__CPUINFO_TYPE_IA32); +#ifdef FLAC__HAS_NASM + if(decoder->private_->cpuinfo.data.ia32.bswap) + decoder->private_->local_bitreader_read_rice_signed_block = FLAC__bitreader_read_rice_signed_block_asm_ia32_bswap; + if(decoder->private_->cpuinfo.data.ia32.mmx) { + decoder->private_->local_lpc_restore_signal = FLAC__lpc_restore_signal_asm_ia32; + decoder->private_->local_lpc_restore_signal_16bit = FLAC__lpc_restore_signal_asm_ia32_mmx; + decoder->private_->local_lpc_restore_signal_16bit_order8 = FLAC__lpc_restore_signal_asm_ia32_mmx; + } + else { + decoder->private_->local_lpc_restore_signal = FLAC__lpc_restore_signal_asm_ia32; + decoder->private_->local_lpc_restore_signal_16bit = FLAC__lpc_restore_signal_asm_ia32; + decoder->private_->local_lpc_restore_signal_16bit_order8 = FLAC__lpc_restore_signal_asm_ia32; + } +#endif +#elif defined FLAC__CPU_PPC + FLAC__ASSERT(decoder->private_->cpuinfo.type == FLAC__CPUINFO_TYPE_PPC); + if(decoder->private_->cpuinfo.data.ppc.altivec) { + decoder->private_->local_lpc_restore_signal_16bit = FLAC__lpc_restore_signal_asm_ppc_altivec_16; + decoder->private_->local_lpc_restore_signal_16bit_order8 = FLAC__lpc_restore_signal_asm_ppc_altivec_16_order8; + } +#endif + } +#endif + + /* from here on, errors are fatal */ + + if(!FLAC__bitreader_init(decoder->private_->input, decoder->private_->cpuinfo, read_callback_, decoder)) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return FLAC__STREAM_DECODER_INIT_STATUS_MEMORY_ALLOCATION_ERROR; + } + + decoder->private_->read_callback = read_callback; + decoder->private_->seek_callback = seek_callback; + decoder->private_->tell_callback = tell_callback; + decoder->private_->length_callback = length_callback; + decoder->private_->eof_callback = eof_callback; + decoder->private_->write_callback = write_callback; + decoder->private_->metadata_callback = metadata_callback; + decoder->private_->error_callback = error_callback; + decoder->private_->client_data = client_data; + decoder->private_->last_frame_number = 0; + decoder->private_->last_block_size = 0; + decoder->private_->samples_decoded = 0; + decoder->private_->has_stream_info = false; + decoder->private_->cached = false; + + decoder->private_->do_md5_checking = decoder->protected_->md5_checking; + decoder->private_->is_seeking = false; + + decoder->private_->internal_reset_hack = true; /* so the following reset does not try to rewind the input */ + if(!FLAC__stream_decoder_reset(decoder)) { + /* above call sets the state for us */ + return FLAC__STREAM_DECODER_INIT_STATUS_MEMORY_ALLOCATION_ERROR; + } + + return FLAC__STREAM_DECODER_INIT_STATUS_OK; +} + +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_stream( + FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderReadCallback read_callback, + FLAC__StreamDecoderSeekCallback seek_callback, + FLAC__StreamDecoderTellCallback tell_callback, + FLAC__StreamDecoderLengthCallback length_callback, + FLAC__StreamDecoderEofCallback eof_callback, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +) +{ + return init_stream_internal_( + decoder, + read_callback, + seek_callback, + tell_callback, + length_callback, + eof_callback, + write_callback, + metadata_callback, + error_callback, + client_data, + /*is_ogg=*/false + ); +} + +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_stream( + FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderReadCallback read_callback, + FLAC__StreamDecoderSeekCallback seek_callback, + FLAC__StreamDecoderTellCallback tell_callback, + FLAC__StreamDecoderLengthCallback length_callback, + FLAC__StreamDecoderEofCallback eof_callback, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +) +{ + return init_stream_internal_( + decoder, + read_callback, + seek_callback, + tell_callback, + length_callback, + eof_callback, + write_callback, + metadata_callback, + error_callback, + client_data, + /*is_ogg=*/true + ); +} + +static FLAC__StreamDecoderInitStatus init_FILE_internal_( + FLAC__StreamDecoder *decoder, + FILE *file, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data, + FLAC__bool is_ogg +) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != file); + + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return decoder->protected_->state = FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED; + + if(0 == write_callback || 0 == error_callback) + return decoder->protected_->state = FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS; + + /* + * To make sure that our file does not go unclosed after an error, we + * must assign the FILE pointer before any further error can occur in + * this routine. + */ + if(file == stdin) + file = get_binary_stdin_(); /* just to be safe */ + + decoder->private_->file = file; + + return init_stream_internal_( + decoder, + file_read_callback_, + decoder->private_->file == stdin? 0: file_seek_callback_, + decoder->private_->file == stdin? 0: file_tell_callback_, + decoder->private_->file == stdin? 0: file_length_callback_, + file_eof_callback_, + write_callback, + metadata_callback, + error_callback, + client_data, + is_ogg + ); +} + +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_FILE( + FLAC__StreamDecoder *decoder, + FILE *file, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +) +{ + return init_FILE_internal_(decoder, file, write_callback, metadata_callback, error_callback, client_data, /*is_ogg=*/false); +} + +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_FILE( + FLAC__StreamDecoder *decoder, + FILE *file, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +) +{ + return init_FILE_internal_(decoder, file, write_callback, metadata_callback, error_callback, client_data, /*is_ogg=*/true); +} + +static FLAC__StreamDecoderInitStatus init_file_internal_( + FLAC__StreamDecoder *decoder, + const char *filename, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data, + FLAC__bool is_ogg +) +{ + FILE *file; + + FLAC__ASSERT(0 != decoder); + + /* + * To make sure that our file does not go unclosed after an error, we + * have to do the same entrance checks here that are later performed + * in FLAC__stream_decoder_init_FILE() before the FILE* is assigned. + */ + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return decoder->protected_->state = FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED; + + if(0 == write_callback || 0 == error_callback) + return decoder->protected_->state = FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS; + + file = filename? fopen(filename, "rb") : stdin; + + if(0 == file) + return FLAC__STREAM_DECODER_INIT_STATUS_ERROR_OPENING_FILE; + + return init_FILE_internal_(decoder, file, write_callback, metadata_callback, error_callback, client_data, is_ogg); +} + +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_file( + FLAC__StreamDecoder *decoder, + const char *filename, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +) +{ + return init_file_internal_(decoder, filename, write_callback, metadata_callback, error_callback, client_data, /*is_ogg=*/false); +} + +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_file( + FLAC__StreamDecoder *decoder, + const char *filename, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +) +{ + return init_file_internal_(decoder, filename, write_callback, metadata_callback, error_callback, client_data, /*is_ogg=*/true); +} + +FLAC_API FLAC__bool FLAC__stream_decoder_finish(FLAC__StreamDecoder *decoder) +{ + FLAC__bool md5_failed = false; + unsigned i; + + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->protected_); + + if(decoder->protected_->state == FLAC__STREAM_DECODER_UNINITIALIZED) + return true; + + /* see the comment in FLAC__seekable_stream_decoder_reset() as to why we + * always call FLAC__MD5Final() + */ + FLAC__MD5Final(decoder->private_->computed_md5sum, &decoder->private_->md5context); + + if(decoder->private_->has_seek_table && 0 != decoder->private_->seek_table.data.seek_table.points) { + free(decoder->private_->seek_table.data.seek_table.points); + decoder->private_->seek_table.data.seek_table.points = 0; + decoder->private_->has_seek_table = false; + } + FLAC__bitreader_free(decoder->private_->input); + for(i = 0; i < FLAC__MAX_CHANNELS; i++) { + /* WATCHOUT: + * FLAC__lpc_restore_signal_asm_ia32_mmx() requires that the + * output arrays have a buffer of up to 3 zeroes in front + * (at negative indices) for alignment purposes; we use 4 + * to keep the data well-aligned. + */ + if(0 != decoder->private_->output[i]) { + free(decoder->private_->output[i]-4); + decoder->private_->output[i] = 0; + } + if(0 != decoder->private_->residual_unaligned[i]) { + free(decoder->private_->residual_unaligned[i]); + decoder->private_->residual_unaligned[i] = decoder->private_->residual[i] = 0; + } + } + decoder->private_->output_capacity = 0; + decoder->private_->output_channels = 0; + +#if FLAC__HAS_OGG + if(decoder->private_->is_ogg) + FLAC__ogg_decoder_aspect_finish(&decoder->protected_->ogg_decoder_aspect); +#endif + + if(0 != decoder->private_->file) { + if(decoder->private_->file != stdin) + fclose(decoder->private_->file); + decoder->private_->file = 0; + } + + if(decoder->private_->do_md5_checking) { + if(memcmp(decoder->private_->stream_info.data.stream_info.md5sum, decoder->private_->computed_md5sum, 16)) + md5_failed = true; + } + decoder->private_->is_seeking = false; + + set_defaults_(decoder); + + decoder->protected_->state = FLAC__STREAM_DECODER_UNINITIALIZED; + + return !md5_failed; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_set_ogg_serial_number(FLAC__StreamDecoder *decoder, long value) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->protected_); + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return false; +#if FLAC__HAS_OGG + /* can't check decoder->private_->is_ogg since that's not set until init time */ + FLAC__ogg_decoder_aspect_set_serial_number(&decoder->protected_->ogg_decoder_aspect, value); + return true; +#else + (void)value; + return false; +#endif +} + +FLAC_API FLAC__bool FLAC__stream_decoder_set_md5_checking(FLAC__StreamDecoder *decoder, FLAC__bool value) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return false; + decoder->protected_->md5_checking = value; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond(FLAC__StreamDecoder *decoder, FLAC__MetadataType type) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->protected_); + FLAC__ASSERT((unsigned)type <= FLAC__MAX_METADATA_TYPE_CODE); + /* double protection */ + if((unsigned)type > FLAC__MAX_METADATA_TYPE_CODE) + return false; + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return false; + decoder->private_->metadata_filter[type] = true; + if(type == FLAC__METADATA_TYPE_APPLICATION) + decoder->private_->metadata_filter_ids_count = 0; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond_application(FLAC__StreamDecoder *decoder, const FLAC__byte id[4]) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->protected_); + FLAC__ASSERT(0 != id); + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return false; + + if(decoder->private_->metadata_filter[FLAC__METADATA_TYPE_APPLICATION]) + return true; + + FLAC__ASSERT(0 != decoder->private_->metadata_filter_ids); + + if(decoder->private_->metadata_filter_ids_count == decoder->private_->metadata_filter_ids_capacity) { + if(0 == (decoder->private_->metadata_filter_ids = (FLAC__byte*)realloc(decoder->private_->metadata_filter_ids, decoder->private_->metadata_filter_ids_capacity * 2))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + decoder->private_->metadata_filter_ids_capacity *= 2; + } + + memcpy(decoder->private_->metadata_filter_ids + decoder->private_->metadata_filter_ids_count * (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8), id, (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8)); + decoder->private_->metadata_filter_ids_count++; + + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond_all(FLAC__StreamDecoder *decoder) +{ + unsigned i; + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->protected_); + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return false; + for(i = 0; i < sizeof(decoder->private_->metadata_filter) / sizeof(decoder->private_->metadata_filter[0]); i++) + decoder->private_->metadata_filter[i] = true; + decoder->private_->metadata_filter_ids_count = 0; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore(FLAC__StreamDecoder *decoder, FLAC__MetadataType type) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->protected_); + FLAC__ASSERT((unsigned)type <= FLAC__MAX_METADATA_TYPE_CODE); + /* double protection */ + if((unsigned)type > FLAC__MAX_METADATA_TYPE_CODE) + return false; + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return false; + decoder->private_->metadata_filter[type] = false; + if(type == FLAC__METADATA_TYPE_APPLICATION) + decoder->private_->metadata_filter_ids_count = 0; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore_application(FLAC__StreamDecoder *decoder, const FLAC__byte id[4]) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->protected_); + FLAC__ASSERT(0 != id); + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return false; + + if(!decoder->private_->metadata_filter[FLAC__METADATA_TYPE_APPLICATION]) + return true; + + FLAC__ASSERT(0 != decoder->private_->metadata_filter_ids); + + if(decoder->private_->metadata_filter_ids_count == decoder->private_->metadata_filter_ids_capacity) { + if(0 == (decoder->private_->metadata_filter_ids = (FLAC__byte*)realloc(decoder->private_->metadata_filter_ids, decoder->private_->metadata_filter_ids_capacity * 2))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + decoder->private_->metadata_filter_ids_capacity *= 2; + } + + memcpy(decoder->private_->metadata_filter_ids + decoder->private_->metadata_filter_ids_count * (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8), id, (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8)); + decoder->private_->metadata_filter_ids_count++; + + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore_all(FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->protected_); + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return false; + memset(decoder->private_->metadata_filter, 0, sizeof(decoder->private_->metadata_filter)); + decoder->private_->metadata_filter_ids_count = 0; + return true; +} + +FLAC_API FLAC__StreamDecoderState FLAC__stream_decoder_get_state(const FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + return decoder->protected_->state; +} + +FLAC_API const char *FLAC__stream_decoder_get_resolved_state_string(const FLAC__StreamDecoder *decoder) +{ + return FLAC__StreamDecoderStateString[decoder->protected_->state]; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_get_md5_checking(const FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + return decoder->protected_->md5_checking; +} + +FLAC_API FLAC__uint64 FLAC__stream_decoder_get_total_samples(const FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + return decoder->private_->has_stream_info? decoder->private_->stream_info.data.stream_info.total_samples : 0; +} + +FLAC_API unsigned FLAC__stream_decoder_get_channels(const FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + return decoder->protected_->channels; +} + +FLAC_API FLAC__ChannelAssignment FLAC__stream_decoder_get_channel_assignment(const FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + return decoder->protected_->channel_assignment; +} + +FLAC_API unsigned FLAC__stream_decoder_get_bits_per_sample(const FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + return decoder->protected_->bits_per_sample; +} + +FLAC_API unsigned FLAC__stream_decoder_get_sample_rate(const FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + return decoder->protected_->sample_rate; +} + +FLAC_API unsigned FLAC__stream_decoder_get_blocksize(const FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + return decoder->protected_->blocksize; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_get_decode_position(const FLAC__StreamDecoder *decoder, FLAC__uint64 *position) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != position); + +#if FLAC__HAS_OGG + if(decoder->private_->is_ogg) + return false; +#endif + if(0 == decoder->private_->tell_callback) + return false; + if(decoder->private_->tell_callback(decoder, position, decoder->private_->client_data) != FLAC__STREAM_DECODER_TELL_STATUS_OK) + return false; + /* should never happen since all FLAC frames and metadata blocks are byte aligned, but check just in case */ + if(!FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)) + return false; + FLAC__ASSERT(*position >= FLAC__stream_decoder_get_input_bytes_unconsumed(decoder)); + *position -= FLAC__stream_decoder_get_input_bytes_unconsumed(decoder); + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_flush(FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->protected_); + + decoder->private_->samples_decoded = 0; + decoder->private_->do_md5_checking = false; + +#if FLAC__HAS_OGG + if(decoder->private_->is_ogg) + FLAC__ogg_decoder_aspect_flush(&decoder->protected_->ogg_decoder_aspect); +#endif + + if(!FLAC__bitreader_clear(decoder->private_->input)) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + decoder->private_->last_frame_number = 0; + decoder->private_->last_block_size = 0; + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_reset(FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + FLAC__ASSERT(0 != decoder->protected_); + + if(!FLAC__stream_decoder_flush(decoder)) { + /* above call sets the state for us */ + return false; + } + +#if FLAC__HAS_OGG + /*@@@ could go in !internal_reset_hack block below */ + if(decoder->private_->is_ogg) + FLAC__ogg_decoder_aspect_reset(&decoder->protected_->ogg_decoder_aspect); +#endif + + /* Rewind if necessary. If FLAC__stream_decoder_init() is calling us, + * (internal_reset_hack) don't try to rewind since we are already at + * the beginning of the stream and don't want to fail if the input is + * not seekable. + */ + if(!decoder->private_->internal_reset_hack) { + if(decoder->private_->file == stdin) + return false; /* can't rewind stdin, reset fails */ + if(decoder->private_->seek_callback && decoder->private_->seek_callback(decoder, 0, decoder->private_->client_data) == FLAC__STREAM_DECODER_SEEK_STATUS_ERROR) + return false; /* seekable and seek fails, reset fails */ + } + else + decoder->private_->internal_reset_hack = false; + + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_METADATA; + + decoder->private_->has_stream_info = false; + if(decoder->private_->has_seek_table && 0 != decoder->private_->seek_table.data.seek_table.points) { + free(decoder->private_->seek_table.data.seek_table.points); + decoder->private_->seek_table.data.seek_table.points = 0; + decoder->private_->has_seek_table = false; + } + decoder->private_->do_md5_checking = decoder->protected_->md5_checking; + + /* We initialize the FLAC__MD5Context even though we may never use it. This + * is because md5 checking may be turned on to start and then turned off if + * a seek occurs. So we init the context here and finalize it in + * FLAC__stream_decoder_finish() to make sure things are always cleaned up + * properly. + */ + FLAC__MD5Init(&decoder->private_->md5context); + + decoder->private_->first_frame_offset = 0; + decoder->private_->unparseable_frame_count = 0; + + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_process_single(FLAC__StreamDecoder *decoder) +{ + FLAC__bool got_a_frame; + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + + while(1) { + switch(decoder->protected_->state) { + case FLAC__STREAM_DECODER_SEARCH_FOR_METADATA: + if(!find_metadata_(decoder)) + return false; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_READ_METADATA: + if(!read_metadata_(decoder)) + return false; /* above function sets the status for us */ + else + return true; + case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC: + if(!frame_sync_(decoder)) + return true; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_READ_FRAME: + if(!read_frame_(decoder, &got_a_frame, /*do_full_decode=*/true)) + return false; /* above function sets the status for us */ + if(got_a_frame) + return true; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_END_OF_STREAM: + case FLAC__STREAM_DECODER_ABORTED: + return true; + default: + FLAC__ASSERT(0); + return false; + } + } +} + +FLAC_API FLAC__bool FLAC__stream_decoder_process_until_end_of_metadata(FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + + while(1) { + switch(decoder->protected_->state) { + case FLAC__STREAM_DECODER_SEARCH_FOR_METADATA: + if(!find_metadata_(decoder)) + return false; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_READ_METADATA: + if(!read_metadata_(decoder)) + return false; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC: + case FLAC__STREAM_DECODER_READ_FRAME: + case FLAC__STREAM_DECODER_END_OF_STREAM: + case FLAC__STREAM_DECODER_ABORTED: + return true; + default: + FLAC__ASSERT(0); + return false; + } + } +} + +FLAC_API FLAC__bool FLAC__stream_decoder_process_until_end_of_stream(FLAC__StreamDecoder *decoder) +{ + FLAC__bool dummy; + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + + while(1) { + switch(decoder->protected_->state) { + case FLAC__STREAM_DECODER_SEARCH_FOR_METADATA: + if(!find_metadata_(decoder)) + return false; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_READ_METADATA: + if(!read_metadata_(decoder)) + return false; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC: + if(!frame_sync_(decoder)) + return true; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_READ_FRAME: + if(!read_frame_(decoder, &dummy, /*do_full_decode=*/true)) + return false; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_END_OF_STREAM: + case FLAC__STREAM_DECODER_ABORTED: + return true; + default: + FLAC__ASSERT(0); + return false; + } + } +} + +FLAC_API FLAC__bool FLAC__stream_decoder_skip_single_frame(FLAC__StreamDecoder *decoder) +{ + FLAC__bool got_a_frame; + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->protected_); + + while(1) { + switch(decoder->protected_->state) { + case FLAC__STREAM_DECODER_SEARCH_FOR_METADATA: + case FLAC__STREAM_DECODER_READ_METADATA: + return false; /* above function sets the status for us */ + case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC: + if(!frame_sync_(decoder)) + return true; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_READ_FRAME: + if(!read_frame_(decoder, &got_a_frame, /*do_full_decode=*/false)) + return false; /* above function sets the status for us */ + if(got_a_frame) + return true; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_END_OF_STREAM: + case FLAC__STREAM_DECODER_ABORTED: + return true; + default: + FLAC__ASSERT(0); + return false; + } + } +} + +FLAC_API FLAC__bool FLAC__stream_decoder_seek_absolute(FLAC__StreamDecoder *decoder, FLAC__uint64 sample) +{ + FLAC__uint64 length; + + FLAC__ASSERT(0 != decoder); + + if( + decoder->protected_->state != FLAC__STREAM_DECODER_SEARCH_FOR_METADATA && + decoder->protected_->state != FLAC__STREAM_DECODER_READ_METADATA && + decoder->protected_->state != FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC && + decoder->protected_->state != FLAC__STREAM_DECODER_READ_FRAME && + decoder->protected_->state != FLAC__STREAM_DECODER_END_OF_STREAM + ) + return false; + + if(0 == decoder->private_->seek_callback) + return false; + + FLAC__ASSERT(decoder->private_->seek_callback); + FLAC__ASSERT(decoder->private_->tell_callback); + FLAC__ASSERT(decoder->private_->length_callback); + FLAC__ASSERT(decoder->private_->eof_callback); + + if(FLAC__stream_decoder_get_total_samples(decoder) > 0 && sample >= FLAC__stream_decoder_get_total_samples(decoder)) + return false; + + decoder->private_->is_seeking = true; + + /* turn off md5 checking if a seek is attempted */ + decoder->private_->do_md5_checking = false; + + /* get the file length (currently our algorithm needs to know the length so it's also an error to get FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED) */ + if(decoder->private_->length_callback(decoder, &length, decoder->private_->client_data) != FLAC__STREAM_DECODER_LENGTH_STATUS_OK) { + decoder->private_->is_seeking = false; + return false; + } + + /* if we haven't finished processing the metadata yet, do that so we have the STREAMINFO, SEEK_TABLE, and first_frame_offset */ + if( + decoder->protected_->state == FLAC__STREAM_DECODER_SEARCH_FOR_METADATA || + decoder->protected_->state == FLAC__STREAM_DECODER_READ_METADATA + ) { + if(!FLAC__stream_decoder_process_until_end_of_metadata(decoder)) { + /* above call sets the state for us */ + decoder->private_->is_seeking = false; + return false; + } + /* check this again in case we didn't know total_samples the first time */ + if(FLAC__stream_decoder_get_total_samples(decoder) > 0 && sample >= FLAC__stream_decoder_get_total_samples(decoder)) { + decoder->private_->is_seeking = false; + return false; + } + } + + { + const FLAC__bool ok = +#if FLAC__HAS_OGG + decoder->private_->is_ogg? + seek_to_absolute_sample_ogg_(decoder, length, sample) : +#endif + seek_to_absolute_sample_(decoder, length, sample) + ; + decoder->private_->is_seeking = false; + return ok; + } +} + +/*********************************************************************** + * + * Protected class methods + * + ***********************************************************************/ + +unsigned FLAC__stream_decoder_get_input_bytes_unconsumed(const FLAC__StreamDecoder *decoder) +{ + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)); + FLAC__ASSERT(!(FLAC__bitreader_get_input_bits_unconsumed(decoder->private_->input) & 7)); + return FLAC__bitreader_get_input_bits_unconsumed(decoder->private_->input) / 8; +} + +/*********************************************************************** + * + * Private class methods + * + ***********************************************************************/ + +void set_defaults_(FLAC__StreamDecoder *decoder) +{ +#if FLAC__HAS_OGG + decoder->private_->is_ogg = false; +#endif + decoder->private_->read_callback = 0; + decoder->private_->seek_callback = 0; + decoder->private_->tell_callback = 0; + decoder->private_->length_callback = 0; + decoder->private_->eof_callback = 0; + decoder->private_->write_callback = 0; + decoder->private_->metadata_callback = 0; + decoder->private_->error_callback = 0; + decoder->private_->client_data = 0; + + memset(decoder->private_->metadata_filter, 0, sizeof(decoder->private_->metadata_filter)); + decoder->private_->metadata_filter[FLAC__METADATA_TYPE_STREAMINFO] = true; + decoder->private_->metadata_filter_ids_count = 0; + + decoder->protected_->md5_checking = false; + +#if FLAC__HAS_OGG + FLAC__ogg_decoder_aspect_set_defaults(&decoder->protected_->ogg_decoder_aspect); +#endif +} + +/* + * This will forcibly set stdin to binary mode (for OSes that require it) + */ +FILE *get_binary_stdin_(void) +{ + /* if something breaks here it is probably due to the presence or + * absence of an underscore before the identifiers 'setmode', + * 'fileno', and/or 'O_BINARY'; check your system header files. + */ +#if defined _MSC_VER || defined __MINGW32__ + _setmode(_fileno(stdin), _O_BINARY); +#elif defined __CYGWIN__ || defined __EMX__ + /* almost certainly not needed for any modern Cygwin, but let's be safe... */ + setmode(_fileno(stdin), _O_BINARY); +#endif + + return stdin; +} + +FLAC__bool allocate_output_(FLAC__StreamDecoder *decoder, unsigned size, unsigned channels) +{ + unsigned i; + FLAC__int32 *tmp; + + if(size <= decoder->private_->output_capacity && channels <= decoder->private_->output_channels) + return true; + + /* simply using realloc() is not practical because the number of channels may change mid-stream */ + + for(i = 0; i < FLAC__MAX_CHANNELS; i++) { + if(0 != decoder->private_->output[i]) { + free(decoder->private_->output[i]-4); + decoder->private_->output[i] = 0; + } + if(0 != decoder->private_->residual_unaligned[i]) { + free(decoder->private_->residual_unaligned[i]); + decoder->private_->residual_unaligned[i] = decoder->private_->residual[i] = 0; + } + } + + for(i = 0; i < channels; i++) { + /* WATCHOUT: + * FLAC__lpc_restore_signal_asm_ia32_mmx() requires that the + * output arrays have a buffer of up to 3 zeroes in front + * (at negative indices) for alignment purposes; we use 4 + * to keep the data well-aligned. + */ + tmp = (FLAC__int32*)malloc(sizeof(FLAC__int32)*(size+4)); + if(tmp == 0) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + memset(tmp, 0, sizeof(FLAC__int32)*4); + decoder->private_->output[i] = tmp + 4; + + /* WATCHOUT: + * minimum of quadword alignment for PPC vector optimizations is REQUIRED: + */ + if(!FLAC__memory_alloc_aligned_int32_array(size, &decoder->private_->residual_unaligned[i], &decoder->private_->residual[i])) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + } + + decoder->private_->output_capacity = size; + decoder->private_->output_channels = channels; + + return true; +} + +FLAC__bool has_id_filtered_(FLAC__StreamDecoder *decoder, FLAC__byte *id) +{ + unsigned i; + + FLAC__ASSERT(0 != decoder); + FLAC__ASSERT(0 != decoder->private_); + + for(i = 0; i < decoder->private_->metadata_filter_ids_count; i++) + if(0 == memcmp(decoder->private_->metadata_filter_ids + i * (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8), id, (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8))) + return true; + + return false; +} + +FLAC__bool find_metadata_(FLAC__StreamDecoder *decoder) +{ + FLAC__uint32 x; + unsigned i, id; + FLAC__bool first = true; + + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)); + + for(i = id = 0; i < 4; ) { + if(decoder->private_->cached) { + x = (FLAC__uint32)decoder->private_->lookahead; + decoder->private_->cached = false; + } + else { + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + } + if(x == FLAC__STREAM_SYNC_STRING[i]) { + first = true; + i++; + id = 0; + continue; + } + if(x == ID3V2_TAG_[id]) { + id++; + i = 0; + if(id == 3) { + if(!skip_id3v2_tag_(decoder)) + return false; /* skip_id3v2_tag_ sets the state for us */ + } + continue; + } + id = 0; + if(x == 0xff) { /* MAGIC NUMBER for the first 8 frame sync bits */ + decoder->private_->header_warmup[0] = (FLAC__byte)x; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + + /* we have to check if we just read two 0xff's in a row; the second may actually be the beginning of the sync code */ + /* else we have to check if the second byte is the end of a sync code */ + if(x == 0xff) { /* MAGIC NUMBER for the first 8 frame sync bits */ + decoder->private_->lookahead = (FLAC__byte)x; + decoder->private_->cached = true; + } + else if(x >> 2 == 0x3e) { /* MAGIC NUMBER for the last 6 sync bits */ + decoder->private_->header_warmup[1] = (FLAC__byte)x; + decoder->protected_->state = FLAC__STREAM_DECODER_READ_FRAME; + return true; + } + } + i = 0; + if(first) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + first = false; + } + } + + decoder->protected_->state = FLAC__STREAM_DECODER_READ_METADATA; + return true; +} + +FLAC__bool read_metadata_(FLAC__StreamDecoder *decoder) +{ + FLAC__bool is_last; + FLAC__uint32 i, x, type, length; + + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)); + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_IS_LAST_LEN)) + return false; /* read_callback_ sets the state for us */ + is_last = x? true : false; + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &type, FLAC__STREAM_METADATA_TYPE_LEN)) + return false; /* read_callback_ sets the state for us */ + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &length, FLAC__STREAM_METADATA_LENGTH_LEN)) + return false; /* read_callback_ sets the state for us */ + + if(type == FLAC__METADATA_TYPE_STREAMINFO) { + if(!read_metadata_streaminfo_(decoder, is_last, length)) + return false; + + decoder->private_->has_stream_info = true; + if(0 == memcmp(decoder->private_->stream_info.data.stream_info.md5sum, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16)) + decoder->private_->do_md5_checking = false; + if(!decoder->private_->is_seeking && decoder->private_->metadata_filter[FLAC__METADATA_TYPE_STREAMINFO] && decoder->private_->metadata_callback) + decoder->private_->metadata_callback(decoder, &decoder->private_->stream_info, decoder->private_->client_data); + } + else if(type == FLAC__METADATA_TYPE_SEEKTABLE) { + if(!read_metadata_seektable_(decoder, is_last, length)) + return false; + + decoder->private_->has_seek_table = true; + if(!decoder->private_->is_seeking && decoder->private_->metadata_filter[FLAC__METADATA_TYPE_SEEKTABLE] && decoder->private_->metadata_callback) + decoder->private_->metadata_callback(decoder, &decoder->private_->seek_table, decoder->private_->client_data); + } + else { + FLAC__bool skip_it = !decoder->private_->metadata_filter[type]; + unsigned real_length = length; + FLAC__StreamMetadata block; + + block.is_last = is_last; + block.type = (FLAC__MetadataType)type; + block.length = length; + + if(type == FLAC__METADATA_TYPE_APPLICATION) { + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, block.data.application.id, FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8)) + return false; /* read_callback_ sets the state for us */ + + real_length -= FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8; + + if(decoder->private_->metadata_filter_ids_count > 0 && has_id_filtered_(decoder, block.data.application.id)) + skip_it = !skip_it; + } + + if(skip_it) { + if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(decoder->private_->input, real_length)) + return false; /* read_callback_ sets the state for us */ + } + else { + switch(type) { + case FLAC__METADATA_TYPE_PADDING: + /* skip the padding bytes */ + if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(decoder->private_->input, real_length)) + return false; /* read_callback_ sets the state for us */ + break; + case FLAC__METADATA_TYPE_APPLICATION: + /* remember, we read the ID already */ + if(real_length > 0) { + if(0 == (block.data.application.data = (FLAC__byte*)malloc(real_length))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, block.data.application.data, real_length)) + return false; /* read_callback_ sets the state for us */ + } + else + block.data.application.data = 0; + break; + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + if(!read_metadata_vorbiscomment_(decoder, &block.data.vorbis_comment)) + return false; + break; + case FLAC__METADATA_TYPE_CUESHEET: + if(!read_metadata_cuesheet_(decoder, &block.data.cue_sheet)) + return false; + break; + case FLAC__METADATA_TYPE_PICTURE: + if(!read_metadata_picture_(decoder, &block.data.picture)) + return false; + break; + case FLAC__METADATA_TYPE_STREAMINFO: + case FLAC__METADATA_TYPE_SEEKTABLE: + FLAC__ASSERT(0); + break; + default: + if(real_length > 0) { + if(0 == (block.data.unknown.data = (FLAC__byte*)malloc(real_length))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, block.data.unknown.data, real_length)) + return false; /* read_callback_ sets the state for us */ + } + else + block.data.unknown.data = 0; + break; + } + if(!decoder->private_->is_seeking && decoder->private_->metadata_callback) + decoder->private_->metadata_callback(decoder, &block, decoder->private_->client_data); + + /* now we have to free any malloc'ed data in the block */ + switch(type) { + case FLAC__METADATA_TYPE_PADDING: + break; + case FLAC__METADATA_TYPE_APPLICATION: + if(0 != block.data.application.data) + free(block.data.application.data); + break; + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + if(0 != block.data.vorbis_comment.vendor_string.entry) + free(block.data.vorbis_comment.vendor_string.entry); + if(block.data.vorbis_comment.num_comments > 0) + for(i = 0; i < block.data.vorbis_comment.num_comments; i++) + if(0 != block.data.vorbis_comment.comments[i].entry) + free(block.data.vorbis_comment.comments[i].entry); + if(0 != block.data.vorbis_comment.comments) + free(block.data.vorbis_comment.comments); + break; + case FLAC__METADATA_TYPE_CUESHEET: + if(block.data.cue_sheet.num_tracks > 0) + for(i = 0; i < block.data.cue_sheet.num_tracks; i++) + if(0 != block.data.cue_sheet.tracks[i].indices) + free(block.data.cue_sheet.tracks[i].indices); + if(0 != block.data.cue_sheet.tracks) + free(block.data.cue_sheet.tracks); + break; + case FLAC__METADATA_TYPE_PICTURE: + if(0 != block.data.picture.mime_type) + free(block.data.picture.mime_type); + if(0 != block.data.picture.description) + free(block.data.picture.description); + if(0 != block.data.picture.data) + free(block.data.picture.data); + break; + case FLAC__METADATA_TYPE_STREAMINFO: + case FLAC__METADATA_TYPE_SEEKTABLE: + FLAC__ASSERT(0); + default: + if(0 != block.data.unknown.data) + free(block.data.unknown.data); + break; + } + } + } + + if(is_last) { + /* if this fails, it's OK, it's just a hint for the seek routine */ + if(!FLAC__stream_decoder_get_decode_position(decoder, &decoder->private_->first_frame_offset)) + decoder->private_->first_frame_offset = 0; + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + } + + return true; +} + +FLAC__bool read_metadata_streaminfo_(FLAC__StreamDecoder *decoder, FLAC__bool is_last, unsigned length) +{ + FLAC__uint32 x; + unsigned bits, used_bits = 0; + + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)); + + decoder->private_->stream_info.type = FLAC__METADATA_TYPE_STREAMINFO; + decoder->private_->stream_info.is_last = is_last; + decoder->private_->stream_info.length = length; + + bits = FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, bits)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->stream_info.data.stream_info.min_blocksize = x; + used_bits += bits; + + bits = FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->stream_info.data.stream_info.max_blocksize = x; + used_bits += bits; + + bits = FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->stream_info.data.stream_info.min_framesize = x; + used_bits += bits; + + bits = FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->stream_info.data.stream_info.max_framesize = x; + used_bits += bits; + + bits = FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->stream_info.data.stream_info.sample_rate = x; + used_bits += bits; + + bits = FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->stream_info.data.stream_info.channels = x+1; + used_bits += bits; + + bits = FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->stream_info.data.stream_info.bits_per_sample = x+1; + used_bits += bits; + + bits = FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN; + if(!FLAC__bitreader_read_raw_uint64(decoder->private_->input, &decoder->private_->stream_info.data.stream_info.total_samples, FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN)) + return false; /* read_callback_ sets the state for us */ + used_bits += bits; + + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, decoder->private_->stream_info.data.stream_info.md5sum, 16)) + return false; /* read_callback_ sets the state for us */ + used_bits += 16*8; + + /* skip the rest of the block */ + FLAC__ASSERT(used_bits % 8 == 0); + length -= (used_bits / 8); + if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(decoder->private_->input, length)) + return false; /* read_callback_ sets the state for us */ + + return true; +} + +FLAC__bool read_metadata_seektable_(FLAC__StreamDecoder *decoder, FLAC__bool is_last, unsigned length) +{ + FLAC__uint32 i, x; + FLAC__uint64 xx; + + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)); + + decoder->private_->seek_table.type = FLAC__METADATA_TYPE_SEEKTABLE; + decoder->private_->seek_table.is_last = is_last; + decoder->private_->seek_table.length = length; + + decoder->private_->seek_table.data.seek_table.num_points = length / FLAC__STREAM_METADATA_SEEKPOINT_LENGTH; + + /* use realloc since we may pass through here several times (e.g. after seeking) */ + if(0 == (decoder->private_->seek_table.data.seek_table.points = (FLAC__StreamMetadata_SeekPoint*)realloc(decoder->private_->seek_table.data.seek_table.points, decoder->private_->seek_table.data.seek_table.num_points * sizeof(FLAC__StreamMetadata_SeekPoint)))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + for(i = 0; i < decoder->private_->seek_table.data.seek_table.num_points; i++) { + if(!FLAC__bitreader_read_raw_uint64(decoder->private_->input, &xx, FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->seek_table.data.seek_table.points[i].sample_number = xx; + + if(!FLAC__bitreader_read_raw_uint64(decoder->private_->input, &xx, FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->seek_table.data.seek_table.points[i].stream_offset = xx; + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->seek_table.data.seek_table.points[i].frame_samples = x; + } + length -= (decoder->private_->seek_table.data.seek_table.num_points * FLAC__STREAM_METADATA_SEEKPOINT_LENGTH); + /* if there is a partial point left, skip over it */ + if(length > 0) { + /*@@@ do a send_error_to_client_() here? there's an argument for either way */ + if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(decoder->private_->input, length)) + return false; /* read_callback_ sets the state for us */ + } + + return true; +} + +FLAC__bool read_metadata_vorbiscomment_(FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_VorbisComment *obj) +{ + FLAC__uint32 i; + + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)); + + /* read vendor string */ + FLAC__ASSERT(FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN == 32); + if(!FLAC__bitreader_read_uint32_little_endian(decoder->private_->input, &obj->vendor_string.length)) + return false; /* read_callback_ sets the state for us */ + if(obj->vendor_string.length > 0) { + if(0 == (obj->vendor_string.entry = (FLAC__byte*)malloc(obj->vendor_string.length+1))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, obj->vendor_string.entry, obj->vendor_string.length)) + return false; /* read_callback_ sets the state for us */ + obj->vendor_string.entry[obj->vendor_string.length] = '\0'; + } + else + obj->vendor_string.entry = 0; + + /* read num comments */ + FLAC__ASSERT(FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN == 32); + if(!FLAC__bitreader_read_uint32_little_endian(decoder->private_->input, &obj->num_comments)) + return false; /* read_callback_ sets the state for us */ + + /* read comments */ + if(obj->num_comments > 0) { + if(0 == (obj->comments = (FLAC__StreamMetadata_VorbisComment_Entry*)malloc(obj->num_comments * sizeof(FLAC__StreamMetadata_VorbisComment_Entry)))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + for(i = 0; i < obj->num_comments; i++) { + FLAC__ASSERT(FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN == 32); + if(!FLAC__bitreader_read_uint32_little_endian(decoder->private_->input, &obj->comments[i].length)) + return false; /* read_callback_ sets the state for us */ + if(obj->comments[i].length > 0) { + if(0 == (obj->comments[i].entry = (FLAC__byte*)malloc(obj->comments[i].length+1))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, obj->comments[i].entry, obj->comments[i].length)) + return false; /* read_callback_ sets the state for us */ + obj->comments[i].entry[obj->comments[i].length] = '\0'; + } + else + obj->comments[i].entry = 0; + } + } + else { + obj->comments = 0; + } + + return true; +} + +FLAC__bool read_metadata_cuesheet_(FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_CueSheet *obj) +{ + FLAC__uint32 i, j, x; + + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)); + + memset(obj, 0, sizeof(FLAC__StreamMetadata_CueSheet)); + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN % 8 == 0); + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, (FLAC__byte*)obj->media_catalog_number, FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN/8)) + return false; /* read_callback_ sets the state for us */ + + if(!FLAC__bitreader_read_raw_uint64(decoder->private_->input, &obj->lead_in, FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN)) + return false; /* read_callback_ sets the state for us */ + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN)) + return false; /* read_callback_ sets the state for us */ + obj->is_cd = x? true : false; + + if(!FLAC__bitreader_skip_bits_no_crc(decoder->private_->input, FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN)) + return false; /* read_callback_ sets the state for us */ + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN)) + return false; /* read_callback_ sets the state for us */ + obj->num_tracks = x; + + if(obj->num_tracks > 0) { + if(0 == (obj->tracks = (FLAC__StreamMetadata_CueSheet_Track*)calloc(obj->num_tracks, sizeof(FLAC__StreamMetadata_CueSheet_Track)))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + for(i = 0; i < obj->num_tracks; i++) { + FLAC__StreamMetadata_CueSheet_Track *track = &obj->tracks[i]; + if(!FLAC__bitreader_read_raw_uint64(decoder->private_->input, &track->offset, FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN)) + return false; /* read_callback_ sets the state for us */ + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN)) + return false; /* read_callback_ sets the state for us */ + track->number = (FLAC__byte)x; + + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN % 8 == 0); + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, (FLAC__byte*)track->isrc, FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN/8)) + return false; /* read_callback_ sets the state for us */ + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN)) + return false; /* read_callback_ sets the state for us */ + track->type = x; + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN)) + return false; /* read_callback_ sets the state for us */ + track->pre_emphasis = x; + + if(!FLAC__bitreader_skip_bits_no_crc(decoder->private_->input, FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN)) + return false; /* read_callback_ sets the state for us */ + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN)) + return false; /* read_callback_ sets the state for us */ + track->num_indices = (FLAC__byte)x; + + if(track->num_indices > 0) { + if(0 == (track->indices = (FLAC__StreamMetadata_CueSheet_Index*)calloc(track->num_indices, sizeof(FLAC__StreamMetadata_CueSheet_Index)))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + for(j = 0; j < track->num_indices; j++) { + FLAC__StreamMetadata_CueSheet_Index *index = &track->indices[j]; + if(!FLAC__bitreader_read_raw_uint64(decoder->private_->input, &index->offset, FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN)) + return false; /* read_callback_ sets the state for us */ + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN)) + return false; /* read_callback_ sets the state for us */ + index->number = (FLAC__byte)x; + + if(!FLAC__bitreader_skip_bits_no_crc(decoder->private_->input, FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN)) + return false; /* read_callback_ sets the state for us */ + } + } + } + } + + return true; +} + +FLAC__bool read_metadata_picture_(FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_Picture *obj) +{ + FLAC__uint32 x; + + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)); + + /* read type */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_PICTURE_TYPE_LEN)) + return false; /* read_callback_ sets the state for us */ + obj->type = x; + + /* read MIME type */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN)) + return false; /* read_callback_ sets the state for us */ + if(0 == (obj->mime_type = (char*)malloc(x+1))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + if(x > 0) { + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, (FLAC__byte*)obj->mime_type, x)) + return false; /* read_callback_ sets the state for us */ + } + obj->mime_type[x] = '\0'; + + /* read description */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN)) + return false; /* read_callback_ sets the state for us */ + if(0 == (obj->description = (FLAC__byte*)malloc(x+1))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + if(x > 0) { + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, obj->description, x)) + return false; /* read_callback_ sets the state for us */ + } + obj->description[x] = '\0'; + + /* read width */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &obj->width, FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN)) + return false; /* read_callback_ sets the state for us */ + + /* read height */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &obj->height, FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN)) + return false; /* read_callback_ sets the state for us */ + + /* read depth */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &obj->depth, FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN)) + return false; /* read_callback_ sets the state for us */ + + /* read colors */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &obj->colors, FLAC__STREAM_METADATA_PICTURE_COLORS_LEN)) + return false; /* read_callback_ sets the state for us */ + + /* read data */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &(obj->data_length), FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN)) + return false; /* read_callback_ sets the state for us */ + if(0 == (obj->data = (FLAC__byte*)malloc(obj->data_length))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + if(obj->data_length > 0) { + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, obj->data, obj->data_length)) + return false; /* read_callback_ sets the state for us */ + } + + return true; +} + +FLAC__bool skip_id3v2_tag_(FLAC__StreamDecoder *decoder) +{ + FLAC__uint32 x; + unsigned i, skip; + + /* skip the version and flags bytes */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 24)) + return false; /* read_callback_ sets the state for us */ + /* get the size (in bytes) to skip */ + skip = 0; + for(i = 0; i < 4; i++) { + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + skip <<= 7; + skip |= (x & 0x7f); + } + /* skip the rest of the tag */ + if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(decoder->private_->input, skip)) + return false; /* read_callback_ sets the state for us */ + return true; +} + +FLAC__bool frame_sync_(FLAC__StreamDecoder *decoder) +{ + FLAC__uint32 x; + FLAC__bool first = true; + + /* If we know the total number of samples in the stream, stop if we've read that many. */ + /* This will stop us, for example, from wasting time trying to sync on an ID3V1 tag. */ + if(FLAC__stream_decoder_get_total_samples(decoder) > 0) { + if(decoder->private_->samples_decoded >= FLAC__stream_decoder_get_total_samples(decoder)) { + decoder->protected_->state = FLAC__STREAM_DECODER_END_OF_STREAM; + return true; + } + } + + /* make sure we're byte aligned */ + if(!FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)) { + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__bitreader_bits_left_for_byte_alignment(decoder->private_->input))) + return false; /* read_callback_ sets the state for us */ + } + + while(1) { + if(decoder->private_->cached) { + x = (FLAC__uint32)decoder->private_->lookahead; + decoder->private_->cached = false; + } + else { + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + } + if(x == 0xff) { /* MAGIC NUMBER for the first 8 frame sync bits */ + decoder->private_->header_warmup[0] = (FLAC__byte)x; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + + /* we have to check if we just read two 0xff's in a row; the second may actually be the beginning of the sync code */ + /* else we have to check if the second byte is the end of a sync code */ + if(x == 0xff) { /* MAGIC NUMBER for the first 8 frame sync bits */ + decoder->private_->lookahead = (FLAC__byte)x; + decoder->private_->cached = true; + } + else if(x >> 2 == 0x3e) { /* MAGIC NUMBER for the last 6 sync bits */ + decoder->private_->header_warmup[1] = (FLAC__byte)x; + decoder->protected_->state = FLAC__STREAM_DECODER_READ_FRAME; + return true; + } + } + if(first) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + first = false; + } + } + + return true; +} + +FLAC__bool read_frame_(FLAC__StreamDecoder *decoder, FLAC__bool *got_a_frame, FLAC__bool do_full_decode) +{ + unsigned channel; + unsigned i; + FLAC__int32 mid, side; + unsigned frame_crc; /* the one we calculate from the input stream */ + FLAC__uint32 x; + + *got_a_frame = false; + + /* init the CRC */ + frame_crc = 0; + frame_crc = FLAC__CRC16_UPDATE(decoder->private_->header_warmup[0], frame_crc); + frame_crc = FLAC__CRC16_UPDATE(decoder->private_->header_warmup[1], frame_crc); + FLAC__bitreader_reset_read_crc16(decoder->private_->input, (FLAC__uint16)frame_crc); + + if(!read_frame_header_(decoder)) + return false; + if(decoder->protected_->state == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC) /* means we didn't sync on a valid header */ + return true; + if(!allocate_output_(decoder, decoder->private_->frame.header.blocksize, decoder->private_->frame.header.channels)) + return false; + for(channel = 0; channel < decoder->private_->frame.header.channels; channel++) { + /* + * first figure the correct bits-per-sample of the subframe + */ + unsigned bps = decoder->private_->frame.header.bits_per_sample; + switch(decoder->private_->frame.header.channel_assignment) { + case FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT: + /* no adjustment needed */ + break; + case FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE: + FLAC__ASSERT(decoder->private_->frame.header.channels == 2); + if(channel == 1) + bps++; + break; + case FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE: + FLAC__ASSERT(decoder->private_->frame.header.channels == 2); + if(channel == 0) + bps++; + break; + case FLAC__CHANNEL_ASSIGNMENT_MID_SIDE: + FLAC__ASSERT(decoder->private_->frame.header.channels == 2); + if(channel == 1) + bps++; + break; + default: + FLAC__ASSERT(0); + } + /* + * now read it + */ + if(!read_subframe_(decoder, channel, bps, do_full_decode)) + return false; + if(decoder->protected_->state == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC) /* means bad sync or got corruption */ + return true; + } + if(!read_zero_padding_(decoder)) + return false; + + /* + * Read the frame CRC-16 from the footer and check + */ + frame_crc = FLAC__bitreader_get_read_crc16(decoder->private_->input); + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__FRAME_FOOTER_CRC_LEN)) + return false; /* read_callback_ sets the state for us */ + if(frame_crc == x) { + if(do_full_decode) { + /* Undo any special channel coding */ + switch(decoder->private_->frame.header.channel_assignment) { + case FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT: + /* do nothing */ + break; + case FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE: + FLAC__ASSERT(decoder->private_->frame.header.channels == 2); + for(i = 0; i < decoder->private_->frame.header.blocksize; i++) + decoder->private_->output[1][i] = decoder->private_->output[0][i] - decoder->private_->output[1][i]; + break; + case FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE: + FLAC__ASSERT(decoder->private_->frame.header.channels == 2); + for(i = 0; i < decoder->private_->frame.header.blocksize; i++) + decoder->private_->output[0][i] += decoder->private_->output[1][i]; + break; + case FLAC__CHANNEL_ASSIGNMENT_MID_SIDE: + FLAC__ASSERT(decoder->private_->frame.header.channels == 2); + for(i = 0; i < decoder->private_->frame.header.blocksize; i++) { +#if 1 + mid = decoder->private_->output[0][i]; + side = decoder->private_->output[1][i]; + mid <<= 1; + mid |= (side & 1); /* i.e. if 'side' is odd... */ + decoder->private_->output[0][i] = (mid + side) >> 1; + decoder->private_->output[1][i] = (mid - side) >> 1; +#else + //@@@@@@ OPT: try without 'side' temp variable + mid = (decoder->private_->output[0][i] << 1) | (decoder->private_->output[1][i] & 1); /* i.e. if 'side' is odd... */ + decoder->private_->output[0][i] = (mid + decoder->private_->output[1][i]) >> 1; + decoder->private_->output[1][i] = (mid - decoder->private_->output[1][i]) >> 1; +#endif + } + break; + default: + FLAC__ASSERT(0); + break; + } + } + } + else { + /* Bad frame, emit error and zero the output signal */ + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH); + if(do_full_decode) { + for(channel = 0; channel < decoder->private_->frame.header.channels; channel++) { + memset(decoder->private_->output[channel], 0, sizeof(FLAC__int32) * decoder->private_->frame.header.blocksize); + } + } + } + + *got_a_frame = true; + + /* put the latest values into the public section of the decoder instance */ + decoder->protected_->channels = decoder->private_->frame.header.channels; + decoder->protected_->channel_assignment = decoder->private_->frame.header.channel_assignment; + decoder->protected_->bits_per_sample = decoder->private_->frame.header.bits_per_sample; + decoder->protected_->sample_rate = decoder->private_->frame.header.sample_rate; + decoder->protected_->blocksize = decoder->private_->frame.header.blocksize; + + FLAC__ASSERT(decoder->private_->frame.header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER); + decoder->private_->samples_decoded = decoder->private_->frame.header.number.sample_number + decoder->private_->frame.header.blocksize; + + /* write it */ + if(do_full_decode) { + if(write_audio_frame_to_client_(decoder, &decoder->private_->frame, (const FLAC__int32 * const *)decoder->private_->output) != FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE) + return false; + } + + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; +} + +FLAC__bool read_frame_header_(FLAC__StreamDecoder *decoder) +{ + FLAC__uint32 x; + FLAC__uint64 xx; + unsigned i, blocksize_hint = 0, sample_rate_hint = 0; + FLAC__byte crc8, raw_header[16]; /* MAGIC NUMBER based on the maximum frame header size, including CRC */ + unsigned raw_header_len; + FLAC__bool is_unparseable = false; + const FLAC__bool is_known_variable_blocksize_stream = (decoder->private_->has_stream_info && decoder->private_->stream_info.data.stream_info.min_blocksize != decoder->private_->stream_info.data.stream_info.max_blocksize); + const FLAC__bool is_known_fixed_blocksize_stream = (decoder->private_->has_stream_info && decoder->private_->stream_info.data.stream_info.min_blocksize == decoder->private_->stream_info.data.stream_info.max_blocksize); + + FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)); + + /* init the raw header with the saved bits from synchronization */ + raw_header[0] = decoder->private_->header_warmup[0]; + raw_header[1] = decoder->private_->header_warmup[1]; + raw_header_len = 2; + + /* + * check to make sure that the reserved bits are 0 + */ + if(raw_header[1] & 0x03) { /* MAGIC NUMBER */ + is_unparseable = true; + } + + /* + * Note that along the way as we read the header, we look for a sync + * code inside. If we find one it would indicate that our original + * sync was bad since there cannot be a sync code in a valid header. + * + * Three kinds of things can go wrong when reading the frame header: + * 1) We may have sync'ed incorrectly and not landed on a frame header. + * If we don't find a sync code, it can end up looking like we read + * a valid but unparseable header, until getting to the frame header + * CRC. Even then we could get a false positive on the CRC. + * 2) We may have sync'ed correctly but on an unparseable frame (from a + * future encoder). + * 3) We may be on a damaged frame which appears valid but unparseable. + * + * For all these reasons, we try and read a complete frame header as + * long as it seems valid, even if unparseable, up until the frame + * header CRC. + */ + + /* + * read in the raw header as bytes so we can CRC it, and parse it on the way + */ + for(i = 0; i < 2; i++) { + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + if(x == 0xff) { /* MAGIC NUMBER for the first 8 frame sync bits */ + /* if we get here it means our original sync was erroneous since the sync code cannot appear in the header */ + decoder->private_->lookahead = (FLAC__byte)x; + decoder->private_->cached = true; + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + raw_header[raw_header_len++] = (FLAC__byte)x; + } + + switch(x = raw_header[2] >> 4) { + case 0: + if(is_known_fixed_blocksize_stream) + decoder->private_->frame.header.blocksize = decoder->private_->stream_info.data.stream_info.min_blocksize; + else + is_unparseable = true; + break; + case 1: + decoder->private_->frame.header.blocksize = 192; + break; + case 2: + case 3: + case 4: + case 5: + decoder->private_->frame.header.blocksize = 576 << (x-2); + break; + case 6: + case 7: + blocksize_hint = x; + break; + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + decoder->private_->frame.header.blocksize = 256 << (x-8); + break; + default: + FLAC__ASSERT(0); + break; + } + + switch(x = raw_header[2] & 0x0f) { + case 0: + if(decoder->private_->has_stream_info) + decoder->private_->frame.header.sample_rate = decoder->private_->stream_info.data.stream_info.sample_rate; + else + is_unparseable = true; + break; + case 1: + case 2: + case 3: + is_unparseable = true; + break; + case 4: + decoder->private_->frame.header.sample_rate = 8000; + break; + case 5: + decoder->private_->frame.header.sample_rate = 16000; + break; + case 6: + decoder->private_->frame.header.sample_rate = 22050; + break; + case 7: + decoder->private_->frame.header.sample_rate = 24000; + break; + case 8: + decoder->private_->frame.header.sample_rate = 32000; + break; + case 9: + decoder->private_->frame.header.sample_rate = 44100; + break; + case 10: + decoder->private_->frame.header.sample_rate = 48000; + break; + case 11: + decoder->private_->frame.header.sample_rate = 96000; + break; + case 12: + case 13: + case 14: + sample_rate_hint = x; + break; + case 15: + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + default: + FLAC__ASSERT(0); + } + + x = (unsigned)(raw_header[3] >> 4); + if(x & 8) { + decoder->private_->frame.header.channels = 2; + switch(x & 7) { + case 0: + decoder->private_->frame.header.channel_assignment = FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE; + break; + case 1: + decoder->private_->frame.header.channel_assignment = FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE; + break; + case 2: + decoder->private_->frame.header.channel_assignment = FLAC__CHANNEL_ASSIGNMENT_MID_SIDE; + break; + default: + is_unparseable = true; + break; + } + } + else { + decoder->private_->frame.header.channels = (unsigned)x + 1; + decoder->private_->frame.header.channel_assignment = FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT; + } + + switch(x = (unsigned)(raw_header[3] & 0x0e) >> 1) { + case 0: + if(decoder->private_->has_stream_info) + decoder->private_->frame.header.bits_per_sample = decoder->private_->stream_info.data.stream_info.bits_per_sample; + else + is_unparseable = true; + break; + case 1: + decoder->private_->frame.header.bits_per_sample = 8; + break; + case 2: + decoder->private_->frame.header.bits_per_sample = 12; + break; + case 4: + decoder->private_->frame.header.bits_per_sample = 16; + break; + case 5: + decoder->private_->frame.header.bits_per_sample = 20; + break; + case 6: + decoder->private_->frame.header.bits_per_sample = 24; + break; + case 3: + case 7: + is_unparseable = true; + break; + default: + FLAC__ASSERT(0); + break; + } + + if(raw_header[3] & 0x01) { /* this should be a zero padding bit */ + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + + /* + * Now we get to the regrettable consequences of not knowing for sure + * whether we got a frame number or a sample number. There are no + * encoders that do variable-blocksize encoding so unless we know from + * the STREAMINFO that it is variable-blocksize we will assume it is + * fixed-blocksize. The trouble comes when we have no STREAMINFO; again + * we will guess that is fixed-blocksize. Where this can go wrong: 1) a + * variable-blocksize stream with no STREAMINFO; 2) a fixed-blocksize + * stream that was edited such that one or more frames before or + * including this one do not have the same number of samples as the + * STREAMINFO's min and max blocksize. + */ + if(is_known_variable_blocksize_stream) { + if(blocksize_hint) { + if(!FLAC__bitreader_read_utf8_uint64(decoder->private_->input, &xx, raw_header, &raw_header_len)) + return false; /* read_callback_ sets the state for us */ + if(xx == FLAC__U64L(0xffffffffffffffff)) { /* i.e. non-UTF8 code... */ + decoder->private_->lookahead = raw_header[raw_header_len-1]; /* back up as much as we can */ + decoder->private_->cached = true; + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + decoder->private_->frame.header.number_type = FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER; + decoder->private_->frame.header.number.sample_number = xx; + } + else + is_unparseable = true; + } + else { + if(!FLAC__bitreader_read_utf8_uint32(decoder->private_->input, &x, raw_header, &raw_header_len)) + return false; /* read_callback_ sets the state for us */ + if(x == 0xffffffff) { /* i.e. non-UTF8 code... */ + decoder->private_->lookahead = raw_header[raw_header_len-1]; /* back up as much as we can */ + decoder->private_->cached = true; + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + decoder->private_->last_frame_number = x; + decoder->private_->frame.header.number_type = FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER; + if(decoder->private_->has_stream_info) { + FLAC__ASSERT(decoder->private_->stream_info.data.stream_info.min_blocksize == decoder->private_->stream_info.data.stream_info.max_blocksize); + decoder->private_->frame.header.number.sample_number = (FLAC__uint64)decoder->private_->stream_info.data.stream_info.min_blocksize * (FLAC__uint64)x; + decoder->private_->last_block_size = decoder->private_->frame.header.blocksize; + } + else if(blocksize_hint) { + if(decoder->private_->last_block_size) + decoder->private_->frame.header.number.sample_number = (FLAC__uint64)decoder->private_->last_block_size * (FLAC__uint64)x; + else + is_unparseable = true; + } + else { + decoder->private_->frame.header.number.sample_number = (FLAC__uint64)decoder->private_->frame.header.blocksize * (FLAC__uint64)x; + decoder->private_->last_block_size = decoder->private_->frame.header.blocksize; + } + } + + if(blocksize_hint) { + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + raw_header[raw_header_len++] = (FLAC__byte)x; + if(blocksize_hint == 7) { + FLAC__uint32 _x; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &_x, 8)) + return false; /* read_callback_ sets the state for us */ + raw_header[raw_header_len++] = (FLAC__byte)_x; + x = (x << 8) | _x; + } + decoder->private_->frame.header.blocksize = x+1; + } + + if(sample_rate_hint) { + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + raw_header[raw_header_len++] = (FLAC__byte)x; + if(sample_rate_hint != 12) { + FLAC__uint32 _x; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &_x, 8)) + return false; /* read_callback_ sets the state for us */ + raw_header[raw_header_len++] = (FLAC__byte)_x; + x = (x << 8) | _x; + } + if(sample_rate_hint == 12) + decoder->private_->frame.header.sample_rate = x*1000; + else if(sample_rate_hint == 13) + decoder->private_->frame.header.sample_rate = x; + else + decoder->private_->frame.header.sample_rate = x*10; + } + + /* read the CRC-8 byte */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + crc8 = (FLAC__byte)x; + + if(FLAC__crc8(raw_header, raw_header_len) != crc8) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + + if(is_unparseable) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + + return true; +} + +FLAC__bool read_subframe_(FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, FLAC__bool do_full_decode) +{ + FLAC__uint32 x; + FLAC__bool wasted_bits; + unsigned i; + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) /* MAGIC NUMBER */ + return false; /* read_callback_ sets the state for us */ + + wasted_bits = (x & 1); + x &= 0xfe; + + if(wasted_bits) { + unsigned u; + if(!FLAC__bitreader_read_unary_unsigned(decoder->private_->input, &u)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->frame.subframes[channel].wasted_bits = u+1; + bps -= decoder->private_->frame.subframes[channel].wasted_bits; + } + else + decoder->private_->frame.subframes[channel].wasted_bits = 0; + + /* + * Lots of magic numbers here + */ + if(x & 0x80) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + else if(x == 0) { + if(!read_subframe_constant_(decoder, channel, bps, do_full_decode)) + return false; + } + else if(x == 2) { + if(!read_subframe_verbatim_(decoder, channel, bps, do_full_decode)) + return false; + } + else if(x < 16) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + else if(x <= 24) { + if(!read_subframe_fixed_(decoder, channel, bps, (x>>1)&7, do_full_decode)) + return false; + if(decoder->protected_->state == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC) /* means bad sync or got corruption */ + return true; + } + else if(x < 64) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + else { + if(!read_subframe_lpc_(decoder, channel, bps, ((x>>1)&31)+1, do_full_decode)) + return false; + if(decoder->protected_->state == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC) /* means bad sync or got corruption */ + return true; + } + + if(wasted_bits && do_full_decode) { + x = decoder->private_->frame.subframes[channel].wasted_bits; + for(i = 0; i < decoder->private_->frame.header.blocksize; i++) + decoder->private_->output[channel][i] <<= x; + } + + return true; +} + +FLAC__bool read_subframe_constant_(FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, FLAC__bool do_full_decode) +{ + FLAC__Subframe_Constant *subframe = &decoder->private_->frame.subframes[channel].data.constant; + FLAC__int32 x; + unsigned i; + FLAC__int32 *output = decoder->private_->output[channel]; + + decoder->private_->frame.subframes[channel].type = FLAC__SUBFRAME_TYPE_CONSTANT; + + if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &x, bps)) + return false; /* read_callback_ sets the state for us */ + + subframe->value = x; + + /* decode the subframe */ + if(do_full_decode) { + for(i = 0; i < decoder->private_->frame.header.blocksize; i++) + output[i] = x; + } + + return true; +} + +FLAC__bool read_subframe_fixed_(FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, const unsigned order, FLAC__bool do_full_decode) +{ + FLAC__Subframe_Fixed *subframe = &decoder->private_->frame.subframes[channel].data.fixed; + FLAC__int32 i32; + FLAC__uint32 u32; + unsigned u; + + decoder->private_->frame.subframes[channel].type = FLAC__SUBFRAME_TYPE_FIXED; + + subframe->residual = decoder->private_->residual[channel]; + subframe->order = order; + + /* read warm-up samples */ + for(u = 0; u < order; u++) { + if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &i32, bps)) + return false; /* read_callback_ sets the state for us */ + subframe->warmup[u] = i32; + } + + /* read entropy coding method info */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &u32, FLAC__ENTROPY_CODING_METHOD_TYPE_LEN)) + return false; /* read_callback_ sets the state for us */ + subframe->entropy_coding_method.type = (FLAC__EntropyCodingMethodType)u32; + switch(subframe->entropy_coding_method.type) { + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE: + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &u32, FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN)) + return false; /* read_callback_ sets the state for us */ + subframe->entropy_coding_method.data.partitioned_rice.order = u32; + subframe->entropy_coding_method.data.partitioned_rice.contents = &decoder->private_->partitioned_rice_contents[channel]; + break; + default: + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + + /* read residual */ + switch(subframe->entropy_coding_method.type) { + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE: + if(!read_residual_partitioned_rice_(decoder, order, subframe->entropy_coding_method.data.partitioned_rice.order, &decoder->private_->partitioned_rice_contents[channel], decoder->private_->residual[channel])) + return false; + break; + default: + FLAC__ASSERT(0); + } + + /* decode the subframe */ + if(do_full_decode) { + memcpy(decoder->private_->output[channel], subframe->warmup, sizeof(FLAC__int32) * order); + FLAC__fixed_restore_signal(decoder->private_->residual[channel], decoder->private_->frame.header.blocksize-order, order, decoder->private_->output[channel]+order); + } + + return true; +} + +FLAC__bool read_subframe_lpc_(FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, const unsigned order, FLAC__bool do_full_decode) +{ + FLAC__Subframe_LPC *subframe = &decoder->private_->frame.subframes[channel].data.lpc; + FLAC__int32 i32; + FLAC__uint32 u32; + unsigned u; + + decoder->private_->frame.subframes[channel].type = FLAC__SUBFRAME_TYPE_LPC; + + subframe->residual = decoder->private_->residual[channel]; + subframe->order = order; + + /* read warm-up samples */ + for(u = 0; u < order; u++) { + if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &i32, bps)) + return false; /* read_callback_ sets the state for us */ + subframe->warmup[u] = i32; + } + + /* read qlp coeff precision */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &u32, FLAC__SUBFRAME_LPC_QLP_COEFF_PRECISION_LEN)) + return false; /* read_callback_ sets the state for us */ + if(u32 == (1u << FLAC__SUBFRAME_LPC_QLP_COEFF_PRECISION_LEN) - 1) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + subframe->qlp_coeff_precision = u32+1; + + /* read qlp shift */ + if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &i32, FLAC__SUBFRAME_LPC_QLP_SHIFT_LEN)) + return false; /* read_callback_ sets the state for us */ + subframe->quantization_level = i32; + + /* read quantized lp coefficiencts */ + for(u = 0; u < order; u++) { + if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &i32, subframe->qlp_coeff_precision)) + return false; /* read_callback_ sets the state for us */ + subframe->qlp_coeff[u] = i32; + } + + /* read entropy coding method info */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &u32, FLAC__ENTROPY_CODING_METHOD_TYPE_LEN)) + return false; /* read_callback_ sets the state for us */ + subframe->entropy_coding_method.type = (FLAC__EntropyCodingMethodType)u32; + switch(subframe->entropy_coding_method.type) { + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE: + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &u32, FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN)) + return false; /* read_callback_ sets the state for us */ + subframe->entropy_coding_method.data.partitioned_rice.order = u32; + subframe->entropy_coding_method.data.partitioned_rice.contents = &decoder->private_->partitioned_rice_contents[channel]; + break; + default: + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + + /* read residual */ + switch(subframe->entropy_coding_method.type) { + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE: + if(!read_residual_partitioned_rice_(decoder, order, subframe->entropy_coding_method.data.partitioned_rice.order, &decoder->private_->partitioned_rice_contents[channel], decoder->private_->residual[channel])) + return false; + break; + default: + FLAC__ASSERT(0); + } + + /* decode the subframe */ + if(do_full_decode) { + memcpy(decoder->private_->output[channel], subframe->warmup, sizeof(FLAC__int32) * order); + /*@@@@@@ technically not pessimistic enough, should be more like + if( (FLAC__uint64)order * ((((FLAC__uint64)1)<qlp_coeff_precision)-1) < (((FLAC__uint64)-1) << 32) ) + */ + if(bps + subframe->qlp_coeff_precision + FLAC__bitmath_ilog2(order) <= 32) + if(bps <= 16 && subframe->qlp_coeff_precision <= 16) { + if(order <= 8) + decoder->private_->local_lpc_restore_signal_16bit_order8(decoder->private_->residual[channel], decoder->private_->frame.header.blocksize-order, subframe->qlp_coeff, order, subframe->quantization_level, decoder->private_->output[channel]+order); + else + decoder->private_->local_lpc_restore_signal_16bit(decoder->private_->residual[channel], decoder->private_->frame.header.blocksize-order, subframe->qlp_coeff, order, subframe->quantization_level, decoder->private_->output[channel]+order); + } + else + decoder->private_->local_lpc_restore_signal(decoder->private_->residual[channel], decoder->private_->frame.header.blocksize-order, subframe->qlp_coeff, order, subframe->quantization_level, decoder->private_->output[channel]+order); + else + decoder->private_->local_lpc_restore_signal_64bit(decoder->private_->residual[channel], decoder->private_->frame.header.blocksize-order, subframe->qlp_coeff, order, subframe->quantization_level, decoder->private_->output[channel]+order); + } + + return true; +} + +FLAC__bool read_subframe_verbatim_(FLAC__StreamDecoder *decoder, unsigned channel, unsigned bps, FLAC__bool do_full_decode) +{ + FLAC__Subframe_Verbatim *subframe = &decoder->private_->frame.subframes[channel].data.verbatim; + FLAC__int32 x, *residual = decoder->private_->residual[channel]; + unsigned i; + + decoder->private_->frame.subframes[channel].type = FLAC__SUBFRAME_TYPE_VERBATIM; + + subframe->data = residual; + + for(i = 0; i < decoder->private_->frame.header.blocksize; i++) { + if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &x, bps)) + return false; /* read_callback_ sets the state for us */ + residual[i] = x; + } + + /* decode the subframe */ + if(do_full_decode) + memcpy(decoder->private_->output[channel], subframe->data, sizeof(FLAC__int32) * decoder->private_->frame.header.blocksize); + + return true; +} + +FLAC__bool read_residual_partitioned_rice_(FLAC__StreamDecoder *decoder, unsigned predictor_order, unsigned partition_order, FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents, FLAC__int32 *residual) +{ + FLAC__uint32 rice_parameter; + int i; + unsigned partition, sample, u; + const unsigned partitions = 1u << partition_order; + const unsigned partition_samples = partition_order > 0? decoder->private_->frame.header.blocksize >> partition_order : decoder->private_->frame.header.blocksize - predictor_order; + + /* sanity checks */ + if(partition_order == 0) { + if(decoder->private_->frame.header.blocksize < predictor_order) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + } + else { + if(partition_samples < predictor_order) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + } + + if(!FLAC__format_entropy_coding_method_partitioned_rice_contents_ensure_size(partitioned_rice_contents, max(6, partition_order))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + + sample = 0; + for(partition = 0; partition < partitions; partition++) { + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &rice_parameter, FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN)) + return false; /* read_callback_ sets the state for us */ + partitioned_rice_contents->parameters[partition] = rice_parameter; + if(rice_parameter < FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER) { + u = (partition_order == 0 || partition > 0)? partition_samples : partition_samples - predictor_order; + if(!decoder->private_->local_bitreader_read_rice_signed_block(decoder->private_->input, residual + sample, u, rice_parameter)) + return false; /* read_callback_ sets the state for us */ + sample += u; + } + else { + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &rice_parameter, FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_RAW_LEN)) + return false; /* read_callback_ sets the state for us */ + partitioned_rice_contents->raw_bits[partition] = rice_parameter; + for(u = (partition_order == 0 || partition > 0)? 0 : predictor_order; u < partition_samples; u++, sample++) { + if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &i, rice_parameter)) + return false; /* read_callback_ sets the state for us */ + residual[sample] = i; + } + } + } + + return true; +} + +FLAC__bool read_zero_padding_(FLAC__StreamDecoder *decoder) +{ + if(!FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)) { + FLAC__uint32 zero = 0; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &zero, FLAC__bitreader_bits_left_for_byte_alignment(decoder->private_->input))) + return false; /* read_callback_ sets the state for us */ + if(zero != 0) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + } + } + return true; +} + +FLAC__bool read_callback_(FLAC__byte buffer[], size_t *bytes, void *client_data) +{ + FLAC__StreamDecoder *decoder = (FLAC__StreamDecoder *)client_data; + + if( +#if FLAC__HAS_OGG + /* see [1] HACK NOTE below for why we don't call the eof_callback when decoding Ogg FLAC */ + !decoder->private_->is_ogg && +#endif + decoder->private_->eof_callback && decoder->private_->eof_callback(decoder, decoder->private_->client_data) + ) { + *bytes = 0; + decoder->protected_->state = FLAC__STREAM_DECODER_END_OF_STREAM; + return false; + } + else if(*bytes > 0) { + /* While seeking, it is possible for our seek to land in the + * middle of audio data that looks exactly like a frame header + * from a future version of an encoder. When that happens, our + * error callback will get an + * FLAC__STREAM_DECODER_UNPARSEABLE_STREAM and increment its + * unparseable_frame_count. But there is a remote possibility + * that it is properly synced at such a "future-codec frame", + * so to make sure, we wait to see many "unparseable" errors in + * a row before bailing out. + */ + if(decoder->private_->is_seeking && decoder->private_->unparseable_frame_count > 20) { + decoder->protected_->state = FLAC__STREAM_DECODER_ABORTED; + return false; + } + else { + const FLAC__StreamDecoderReadStatus status = +#if FLAC__HAS_OGG + decoder->private_->is_ogg? + read_callback_ogg_aspect_(decoder, buffer, bytes) : +#endif + decoder->private_->read_callback(decoder, buffer, bytes, decoder->private_->client_data) + ; + if(status == FLAC__STREAM_DECODER_READ_STATUS_ABORT) { + decoder->protected_->state = FLAC__STREAM_DECODER_ABORTED; + return false; + } + else if(*bytes == 0) { + if( + status == FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM || + ( +#if FLAC__HAS_OGG + /* see [1] HACK NOTE below for why we don't call the eof_callback when decoding Ogg FLAC */ + !decoder->private_->is_ogg && +#endif + decoder->private_->eof_callback && decoder->private_->eof_callback(decoder, decoder->private_->client_data) + ) + ) { + decoder->protected_->state = FLAC__STREAM_DECODER_END_OF_STREAM; + return false; + } + else + return true; + } + else + return true; + } + } + else { + /* abort to avoid a deadlock */ + decoder->protected_->state = FLAC__STREAM_DECODER_ABORTED; + return false; + } + /* [1] @@@ HACK NOTE: The end-of-stream checking has to be hacked around + * for Ogg FLAC. This is because the ogg decoder aspect can lose sync + * and at the same time hit the end of the stream (for example, seeking + * to a point that is after the beginning of the last Ogg page). There + * is no way to report an Ogg sync loss through the callbacks (see note + * in read_callback_ogg_aspect_()) so it returns CONTINUE with *bytes==0. + * So to keep the decoder from stopping at this point we gate the call + * to the eof_callback and let the Ogg decoder aspect set the + * end-of-stream state when it is needed. + */ +} + +#if FLAC__HAS_OGG +FLAC__StreamDecoderReadStatus read_callback_ogg_aspect_(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes) +{ + switch(FLAC__ogg_decoder_aspect_read_callback_wrapper(&decoder->protected_->ogg_decoder_aspect, buffer, bytes, read_callback_proxy_, decoder, decoder->private_->client_data)) { + case FLAC__OGG_DECODER_ASPECT_READ_STATUS_OK: + return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; + /* we don't really have a way to handle lost sync via read + * callback so we'll let it pass and let the underlying + * FLAC decoder catch the error + */ + case FLAC__OGG_DECODER_ASPECT_READ_STATUS_LOST_SYNC: + return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; + case FLAC__OGG_DECODER_ASPECT_READ_STATUS_END_OF_STREAM: + return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; + case FLAC__OGG_DECODER_ASPECT_READ_STATUS_NOT_FLAC: + case FLAC__OGG_DECODER_ASPECT_READ_STATUS_UNSUPPORTED_MAPPING_VERSION: + case FLAC__OGG_DECODER_ASPECT_READ_STATUS_ABORT: + case FLAC__OGG_DECODER_ASPECT_READ_STATUS_ERROR: + case FLAC__OGG_DECODER_ASPECT_READ_STATUS_MEMORY_ALLOCATION_ERROR: + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + default: + FLAC__ASSERT(0); + /* double protection */ + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + } +} + +FLAC__OggDecoderAspectReadStatus read_callback_proxy_(const void *void_decoder, FLAC__byte buffer[], size_t *bytes, void *client_data) +{ + FLAC__StreamDecoder *decoder = (FLAC__StreamDecoder*)void_decoder; + + switch(decoder->private_->read_callback(decoder, buffer, bytes, client_data)) { + case FLAC__STREAM_DECODER_READ_STATUS_CONTINUE: + return FLAC__OGG_DECODER_ASPECT_READ_STATUS_OK; + case FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM: + return FLAC__OGG_DECODER_ASPECT_READ_STATUS_END_OF_STREAM; + case FLAC__STREAM_DECODER_READ_STATUS_ABORT: + return FLAC__OGG_DECODER_ASPECT_READ_STATUS_ABORT; + default: + /* double protection: */ + FLAC__ASSERT(0); + return FLAC__OGG_DECODER_ASPECT_READ_STATUS_ABORT; + } +} +#endif + +FLAC__StreamDecoderWriteStatus write_audio_frame_to_client_(FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[]) +{ + if(decoder->private_->is_seeking) { + FLAC__uint64 this_frame_sample = frame->header.number.sample_number; + FLAC__uint64 next_frame_sample = this_frame_sample + (FLAC__uint64)frame->header.blocksize; + FLAC__uint64 target_sample = decoder->private_->target_sample; + + FLAC__ASSERT(frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER); + +#if FLAC__HAS_OGG + decoder->private_->got_a_frame = true; +#endif + decoder->private_->last_frame = *frame; /* save the frame */ + if(this_frame_sample <= target_sample && target_sample < next_frame_sample) { /* we hit our target frame */ + unsigned delta = (unsigned)(target_sample - this_frame_sample); + /* kick out of seek mode */ + decoder->private_->is_seeking = false; + /* shift out the samples before target_sample */ + if(delta > 0) { + unsigned channel; + const FLAC__int32 *newbuffer[FLAC__MAX_CHANNELS]; + for(channel = 0; channel < frame->header.channels; channel++) + newbuffer[channel] = buffer[channel] + delta; + decoder->private_->last_frame.header.blocksize -= delta; + decoder->private_->last_frame.header.number.sample_number += (FLAC__uint64)delta; + /* write the relevant samples */ + return decoder->private_->write_callback(decoder, &decoder->private_->last_frame, newbuffer, decoder->private_->client_data); + } + else { + /* write the relevant samples */ + return decoder->private_->write_callback(decoder, frame, buffer, decoder->private_->client_data); + } + } + else { + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; + } + } + else { + /* + * If we never got STREAMINFO, turn off MD5 checking to save + * cycles since we don't have a sum to compare to anyway + */ + if(!decoder->private_->has_stream_info) + decoder->private_->do_md5_checking = false; + if(decoder->private_->do_md5_checking) { + if(!FLAC__MD5Accumulate(&decoder->private_->md5context, buffer, frame->header.channels, frame->header.blocksize, (frame->header.bits_per_sample+7) / 8)) + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + return decoder->private_->write_callback(decoder, frame, buffer, decoder->private_->client_data); + } +} + +void send_error_to_client_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status) +{ + if(!decoder->private_->is_seeking) + decoder->private_->error_callback(decoder, status, decoder->private_->client_data); + else if(status == FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM) + decoder->private_->unparseable_frame_count++; +} + +FLAC__bool seek_to_absolute_sample_(FLAC__StreamDecoder *decoder, FLAC__uint64 stream_length, FLAC__uint64 target_sample) +{ + FLAC__uint64 first_frame_offset = decoder->private_->first_frame_offset, lower_bound, upper_bound, lower_bound_sample, upper_bound_sample, this_frame_sample; + FLAC__int64 pos = -1; + int i; + unsigned approx_bytes_per_frame; + FLAC__bool first_seek = true; + const FLAC__uint64 total_samples = FLAC__stream_decoder_get_total_samples(decoder); + const unsigned min_blocksize = decoder->private_->stream_info.data.stream_info.min_blocksize; + const unsigned max_blocksize = decoder->private_->stream_info.data.stream_info.max_blocksize; + const unsigned max_framesize = decoder->private_->stream_info.data.stream_info.max_framesize; + const unsigned min_framesize = decoder->private_->stream_info.data.stream_info.min_framesize; + /* take these from the current frame in case they've changed mid-stream */ + unsigned channels = FLAC__stream_decoder_get_channels(decoder); + unsigned bps = FLAC__stream_decoder_get_bits_per_sample(decoder); + const FLAC__StreamMetadata_SeekTable *seek_table = decoder->private_->has_seek_table? &decoder->private_->seek_table.data.seek_table : 0; + + /* use values from stream info if we didn't decode a frame */ + if(channels == 0) + channels = decoder->private_->stream_info.data.stream_info.channels; + if(bps == 0) + bps = decoder->private_->stream_info.data.stream_info.bits_per_sample; + + /* we are just guessing here */ + if(max_framesize > 0) + approx_bytes_per_frame = (max_framesize + min_framesize) / 2 + 1; + + /* + * Check if it's a known fixed-blocksize stream. Note that though + * the spec doesn't allow zeroes in the STREAMINFO block, we may + * never get a STREAMINFO block when decoding so the value of + * min_blocksize might be zero. + */ + else if(min_blocksize == max_blocksize && min_blocksize > 0) { + /* note there are no () around 'bps/8' to keep precision up since it's an integer calulation */ + approx_bytes_per_frame = min_blocksize * channels * bps/8 + 64; + } + else + approx_bytes_per_frame = 4096 * channels * bps/8 + 64; + + /* + * First, we set an upper and lower bound on where in the + * stream we will search. For now we assume the worst case + * scenario, which is our best guess at the beginning of + * the first frame and end of the stream. + */ + lower_bound = first_frame_offset; + lower_bound_sample = 0; + upper_bound = stream_length; + upper_bound_sample = total_samples > 0 ? total_samples : target_sample; + if(upper_bound_sample == 0) + upper_bound_sample = 1; + + /* + * Now we refine the bounds if we have a seektable with + * suitable points. Note that according to the spec they + * must be ordered by ascending sample number. + */ + if(seek_table) { + /* find the closest seek point <= target_sample, if it exists */ + for(i = (int)seek_table->num_points - 1; i >= 0; i--) { + if(seek_table->points[i].sample_number != FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER && seek_table->points[i].sample_number <= target_sample) + break; + } + if(i >= 0) { /* i.e. we found a suitable seek point... */ + lower_bound = first_frame_offset + seek_table->points[i].stream_offset; + lower_bound_sample = seek_table->points[i].sample_number; + } + + /* find the closest seek point > target_sample, if it exists */ + for(i = 0; i < (int)seek_table->num_points; i++) { + if(seek_table->points[i].sample_number != FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER && seek_table->points[i].sample_number > target_sample) + break; + } + if(i < (int)seek_table->num_points) { /* i.e. we found a suitable seek point... */ + upper_bound = first_frame_offset + seek_table->points[i].stream_offset; + upper_bound_sample = seek_table->points[i].sample_number; + } + } + + decoder->private_->target_sample = target_sample; + while(1) { + /* check if the bounds are still ok */ + if (lower_bound_sample >= upper_bound_sample || lower_bound > upper_bound) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } +#ifndef FLAC__INTEGER_ONLY_LIBRARY +#if defined _MSC_VER || defined __MINGW32__ + /* with VC++ you have to spoon feed it the casting */ + pos = (FLAC__int64)lower_bound + (FLAC__int64)((FLAC__double)(FLAC__int64)(target_sample - lower_bound_sample) / (FLAC__double)(FLAC__int64)(upper_bound_sample - lower_bound_sample) * (FLAC__double)(FLAC__int64)(upper_bound - lower_bound)) - approx_bytes_per_frame; +#else + pos = (FLAC__int64)lower_bound + (FLAC__int64)((FLAC__double)(target_sample - lower_bound_sample) / (FLAC__double)(upper_bound_sample - lower_bound_sample) * (FLAC__double)(upper_bound - lower_bound)) - approx_bytes_per_frame; +#endif +#else + /* a little less accurate: */ + if(upper_bound - lower_bound < 0xffffffff) + pos = (FLAC__int64)lower_bound + (FLAC__int64)(((target_sample - lower_bound_sample) * (upper_bound - lower_bound)) / (upper_bound_sample - lower_bound_sample)) - approx_bytes_per_frame; + else /* @@@ WATCHOUT, ~2TB limit */ + pos = (FLAC__int64)lower_bound + (FLAC__int64)((((target_sample - lower_bound_sample)>>8) * ((upper_bound - lower_bound)>>8)) / ((upper_bound_sample - lower_bound_sample)>>16)) - approx_bytes_per_frame; +#endif + if(pos >= (FLAC__int64)upper_bound) + pos = (FLAC__int64)upper_bound - 1; + if(pos < (FLAC__int64)lower_bound) + pos = (FLAC__int64)lower_bound; + if(decoder->private_->seek_callback(decoder, (FLAC__uint64)pos, decoder->private_->client_data) != FLAC__STREAM_DECODER_SEEK_STATUS_OK) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + if(!FLAC__stream_decoder_flush(decoder)) { + /* above call sets the state for us */ + return false; + } + /* Now we need to get a frame. First we need to reset our + * unparseable_frame_count; if we get too many unparseable + * frames in a row, the read callback will return + * FLAC__STREAM_DECODER_READ_STATUS_ABORT, causing + * FLAC__stream_decoder_process_single() to return false. + */ + decoder->private_->unparseable_frame_count = 0; + if(!FLAC__stream_decoder_process_single(decoder)) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + /* our write callback will change the state when it gets to the target frame */ + /* actually, we could have got_a_frame if our decoder is at FLAC__STREAM_DECODER_END_OF_STREAM so we need to check for that also */ +#if 0 + /*@@@@@@ used to be the following; not clear if the check for end of stream is needed anymore */ + if(decoder->protected_->state != FLAC__SEEKABLE_STREAM_DECODER_SEEKING && decoder->protected_->state != FLAC__STREAM_DECODER_END_OF_STREAM) + break; +#endif + if(!decoder->private_->is_seeking) { + break; + } + this_frame_sample = decoder->private_->last_frame.header.number.sample_number; + + if (!decoder->private_->samples_decoded || (this_frame_sample + decoder->private_->last_frame.header.blocksize >= upper_bound_sample && !first_seek)) { + if (pos == (FLAC__int64)lower_bound) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + /* our last move backwards wasn't big enough, try again */ + approx_bytes_per_frame *= 2; + continue; + } + /* allow one seek over upper bound, required for streams with unknown total_samples */ + first_seek = false; + + /* make sure we are not seeking in corrupted stream */ + if (this_frame_sample < lower_bound_sample) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + + FLAC__ASSERT(decoder->private_->last_frame.header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER); + + /* we need to narrow the search */ + if(target_sample < this_frame_sample) { + upper_bound_sample = this_frame_sample + decoder->private_->last_frame.header.blocksize; + if(!FLAC__stream_decoder_get_decode_position(decoder, &upper_bound)) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + approx_bytes_per_frame = (unsigned)(2 * (upper_bound - pos) / 3 + 16); + } + else { + /* target_sample >= this_frame_sample + this frame's blocksize */ + + lower_bound_sample = this_frame_sample + decoder->private_->last_frame.header.blocksize; + if(!FLAC__stream_decoder_get_decode_position(decoder, &lower_bound)) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + approx_bytes_per_frame = (unsigned)(2 * (lower_bound - pos) / 3 + 16); + } + } + + return true; +} + +#if FLAC__HAS_OGG +FLAC__bool seek_to_absolute_sample_ogg_(FLAC__StreamDecoder *decoder, FLAC__uint64 stream_length, FLAC__uint64 target_sample) +{ + FLAC__uint64 left_pos = 0, right_pos = stream_length; + FLAC__uint64 left_sample = 0, right_sample = FLAC__stream_decoder_get_total_samples(decoder); + FLAC__uint64 this_frame_sample = 0; /* only initialized to avoid compiler warning */ + FLAC__uint64 pos = 0; /* only initialized to avoid compiler warning */ + FLAC__bool did_a_seek; + unsigned iteration = 0; + + /* In the first iterations, we will calculate the target byte position + * by the distance from the target sample to left_sample and + * right_sample (let's call it "proportional search"). After that, we + * will switch to binary search. + */ + unsigned BINARY_SEARCH_AFTER_ITERATION = 2; + + /* We will switch to a linear search once our current sample is less + * than this number of samples ahead of the target sample + */ + static const FLAC__uint64 LINEAR_SEARCH_WITHIN_SAMPLES = FLAC__MAX_BLOCK_SIZE * 2; + + /* If the total number of samples is unknown, use a large value, and + * force binary search immediately. + */ + if(right_sample == 0) { + right_sample = (FLAC__uint64)(-1); + BINARY_SEARCH_AFTER_ITERATION = 0; + } + + decoder->private_->target_sample = target_sample; + for( ; ; iteration++) { + if (iteration == 0 || this_frame_sample > target_sample || target_sample - this_frame_sample > LINEAR_SEARCH_WITHIN_SAMPLES) { + if (iteration >= BINARY_SEARCH_AFTER_ITERATION) { + pos = (right_pos + left_pos) / 2; + } + else { +#ifndef FLAC__INTEGER_ONLY_LIBRARY +#if defined _MSC_VER || defined __MINGW32__ + /* with MSVC you have to spoon feed it the casting */ + pos = (FLAC__uint64)((FLAC__double)(FLAC__int64)(target_sample - left_sample) / (FLAC__double)(FLAC__int64)(right_sample - left_sample) * (FLAC__double)(FLAC__int64)(right_pos - left_pos)); +#else + pos = (FLAC__uint64)((FLAC__double)(target_sample - left_sample) / (FLAC__double)(right_sample - left_sample) * (FLAC__double)(right_pos - left_pos)); +#endif +#else + /* a little less accurate: */ + if ((target_sample-left_sample <= 0xffffffff) && (right_pos-left_pos <= 0xffffffff)) + pos = (FLAC__int64)(((target_sample-left_sample) * (right_pos-left_pos)) / (right_sample-left_sample)); + else /* @@@ WATCHOUT, ~2TB limit */ + pos = (FLAC__int64)((((target_sample-left_sample)>>8) * ((right_pos-left_pos)>>8)) / ((right_sample-left_sample)>>16)); +#endif + /* @@@ TODO: might want to limit pos to some distance + * before EOF, to make sure we land before the last frame, + * thereby getting a this_frame_sample and so having a better + * estimate. @@@@@@DELETE:this would also mostly (or totally if we could + * be sure to land before the last frame) avoid the + * end-of-stream case we have to check later. + */ + } + + /* physical seek */ + if(decoder->private_->seek_callback((FLAC__StreamDecoder*)decoder, (FLAC__uint64)pos, decoder->private_->client_data) != FLAC__STREAM_DECODER_SEEK_STATUS_OK) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + if(!FLAC__stream_decoder_flush(decoder)) { + /* above call sets the state for us */ + return false; + } + did_a_seek = true; + } + else + did_a_seek = false; + + decoder->private_->got_a_frame = false; + if(!FLAC__stream_decoder_process_single(decoder)) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + if(!decoder->private_->got_a_frame) { + if(did_a_seek) { + /* this can happen if we seek to a point after the last frame; we drop + * to binary search right away in this case to avoid any wasted + * iterations of proportional search. + */ + right_pos = pos; + BINARY_SEARCH_AFTER_ITERATION = 0; + } + else { + /* this can probably only happen if total_samples is unknown and the + * target_sample is past the end of the stream + */ + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + } + /* our write callback will change the state when it gets to the target frame */ + else if(!decoder->private_->is_seeking/*@@@@@@ && decoder->protected_->state != FLAC__STREAM_DECODER_END_OF_STREAM*/) { + break; + } + else { + this_frame_sample = decoder->private_->last_frame.header.number.sample_number; + FLAC__ASSERT(decoder->private_->last_frame.header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER); + + if (did_a_seek) { + if (this_frame_sample <= target_sample) { + /* The 'equal' case should not happen, since + * FLAC__stream_decoder_process_single() + * should recognize that it has hit the + * target sample and we would exit through + * the 'break' above. + */ + FLAC__ASSERT(this_frame_sample != target_sample); + + left_sample = this_frame_sample; + /* sanity check to avoid infinite loop */ + if (left_pos == pos) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + left_pos = pos; + } + else if(this_frame_sample > target_sample) { + right_sample = this_frame_sample; + /* sanity check to avoid infinite loop */ + if (right_pos == pos) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + right_pos = pos; + } + } + } + } + + return true; +} +#endif + +FLAC__StreamDecoderReadStatus file_read_callback_(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data) +{ + (void)client_data; + + if(*bytes > 0) { + *bytes = fread(buffer, sizeof(FLAC__byte), *bytes, decoder->private_->file); + if(ferror(decoder->private_->file)) + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + else if(*bytes == 0) + return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; + else + return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; + } + else + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; /* abort to avoid a deadlock */ +} + +FLAC__StreamDecoderSeekStatus file_seek_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data) +{ + (void)client_data; + + if(decoder->private_->file == stdin) + return FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED; + else if(fseeko(decoder->private_->file, (off_t)absolute_byte_offset, SEEK_SET) < 0) + return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR; + else + return FLAC__STREAM_DECODER_SEEK_STATUS_OK; +} + +FLAC__StreamDecoderTellStatus file_tell_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data) +{ + off_t pos; + (void)client_data; + + if(decoder->private_->file == stdin) + return FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED; + else if((pos = ftello(decoder->private_->file)) < 0) + return FLAC__STREAM_DECODER_TELL_STATUS_ERROR; + else { + *absolute_byte_offset = (FLAC__uint64)pos; + return FLAC__STREAM_DECODER_TELL_STATUS_OK; + } +} + +FLAC__StreamDecoderLengthStatus file_length_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data) +{ + struct stat filestats; + (void)client_data; + + if(decoder->private_->file == stdin) + return FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED; + else if(fstat(fileno(decoder->private_->file), &filestats) != 0) + return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR; + else { + *stream_length = (FLAC__uint64)filestats.st_size; + return FLAC__STREAM_DECODER_LENGTH_STATUS_OK; + } +} + +FLAC__bool file_eof_callback_(const FLAC__StreamDecoder *decoder, void *client_data) +{ + (void)client_data; + + return feof(decoder->private_->file)? true : false; +} diff --git a/src/FLAC/src/libFLAC/stream_encoder.c b/src/FLAC/src/libFLAC/stream_encoder.c new file mode 100644 index 00000000..f9f14362 --- /dev/null +++ b/src/FLAC/src/libFLAC/stream_encoder.c @@ -0,0 +1,4252 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#if defined _MSC_VER || defined __MINGW32__ +#include /* for _setmode() */ +#include /* for _O_BINARY */ +#endif +#if defined __CYGWIN__ || defined __EMX__ +#include /* for setmode(), O_BINARY */ +#include /* for _O_BINARY */ +#endif +#include +#include +#include /* for malloc() */ +#include /* for memcpy() */ +#include /* for off_t */ +#if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__ +#if _MSC_VER <= 1600 || defined __BORLANDC__ /* @@@ [2G limit] */ +#define fseeko fseek +#define ftello ftell +#endif +#endif +#include "FLAC/assert.h" +#include "FLAC/stream_decoder.h" +#include "protected/stream_encoder.h" +#include "private/bitwriter.h" +#include "private/bitmath.h" +#include "private/crc.h" +#include "private/cpu.h" +#include "private/fixed.h" +#include "private/format.h" +#include "private/lpc.h" +#include "private/md5.h" +#include "private/memory.h" +#if FLAC__HAS_OGG +#include "private/ogg_helper.h" +#include "private/ogg_mapping.h" +#endif +#include "private/stream_encoder_framing.h" +#include "private/window.h" + +#ifndef FLaC__INLINE +#define FLaC__INLINE +#endif + +#ifdef min +#undef min +#endif +#define min(x,y) ((x)<(y)?(x):(y)) + +#ifdef max +#undef max +#endif +#define max(x,y) ((x)>(y)?(x):(y)) + +/* Exact Rice codeword length calculation is off by default. The simple + * (and fast) estimation (of how many bits a residual value will be + * encoded with) in this encoder is very good, almost always yielding + * compression within 0.1% of exact calculation. + */ +#undef EXACT_RICE_BITS_CALCULATION +/* Rice parameter searching is off by default. The simple (and fast) + * parameter estimation in this encoder is very good, almost always + * yielding compression within 0.1% of the optimal parameters. + */ +#undef ENABLE_RICE_PARAMETER_SEARCH + + +typedef struct { + FLAC__int32 *data[FLAC__MAX_CHANNELS]; + unsigned size; /* of each data[] in samples */ + unsigned tail; +} verify_input_fifo; + +typedef struct { + const FLAC__byte *data; + unsigned capacity; + unsigned bytes; +} verify_output; + +typedef enum { + ENCODER_IN_MAGIC = 0, + ENCODER_IN_METADATA = 1, + ENCODER_IN_AUDIO = 2 +} EncoderStateHint; + +static struct CompressionLevels { + FLAC__bool do_mid_side_stereo; + FLAC__bool loose_mid_side_stereo; + unsigned max_lpc_order; + unsigned qlp_coeff_precision; + FLAC__bool do_qlp_coeff_prec_search; + FLAC__bool do_escape_coding; + FLAC__bool do_exhaustive_model_search; + unsigned min_residual_partition_order; + unsigned max_residual_partition_order; + unsigned rice_parameter_search_dist; +} compression_levels_[] = { + { false, false, 0, 0, false, false, false, 0, 3, 0 }, + { true , true , 0, 0, false, false, false, 0, 3, 0 }, + { true , false, 0, 0, false, false, false, 0, 3, 0 }, + { false, false, 6, 0, false, false, false, 0, 4, 0 }, + { true , true , 8, 0, false, false, false, 0, 4, 0 }, + { true , false, 8, 0, false, false, false, 0, 5, 0 }, + { true , false, 8, 0, false, false, false, 0, 6, 0 }, + { true , false, 8, 0, false, false, true , 0, 6, 0 }, + { true , false, 12, 0, false, false, true , 0, 6, 0 } +}; + + +/*********************************************************************** + * + * Private class method prototypes + * + ***********************************************************************/ + +static void set_defaults_(FLAC__StreamEncoder *encoder); +static void free_(FLAC__StreamEncoder *encoder); +static FLAC__bool resize_buffers_(FLAC__StreamEncoder *encoder, unsigned new_blocksize); +static FLAC__bool write_bitbuffer_(FLAC__StreamEncoder *encoder, unsigned samples, FLAC__bool is_last_block); +static FLAC__StreamEncoderWriteStatus write_frame_(FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, unsigned samples, FLAC__bool is_last_block); +static void update_metadata_(const FLAC__StreamEncoder *encoder); +#if FLAC__HAS_OGG +static void update_ogg_metadata_(FLAC__StreamEncoder *encoder); +#endif +static FLAC__bool process_frame_(FLAC__StreamEncoder *encoder, FLAC__bool is_fractional_block, FLAC__bool is_last_block); +static FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder, FLAC__bool is_fractional_block); + +static FLAC__bool process_subframe_( + FLAC__StreamEncoder *encoder, + unsigned min_partition_order, + unsigned max_partition_order, + const FLAC__FrameHeader *frame_header, + unsigned subframe_bps, + const FLAC__int32 integer_signal[], + FLAC__Subframe *subframe[2], + FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents[2], + FLAC__int32 *residual[2], + unsigned *best_subframe, + unsigned *best_bits +); + +static FLAC__bool add_subframe_( + FLAC__StreamEncoder *encoder, + unsigned blocksize, + unsigned subframe_bps, + const FLAC__Subframe *subframe, + FLAC__BitWriter *frame +); + +static unsigned evaluate_constant_subframe_( + FLAC__StreamEncoder *encoder, + const FLAC__int32 signal, + unsigned blocksize, + unsigned subframe_bps, + FLAC__Subframe *subframe +); + +static unsigned evaluate_fixed_subframe_( + FLAC__StreamEncoder *encoder, + const FLAC__int32 signal[], + FLAC__int32 residual[], + FLAC__uint64 abs_residual_partition_sums[], + unsigned raw_bits_per_partition[], + unsigned blocksize, + unsigned subframe_bps, + unsigned order, + unsigned rice_parameter, + unsigned min_partition_order, + unsigned max_partition_order, + FLAC__bool do_escape_coding, + unsigned rice_parameter_search_dist, + FLAC__Subframe *subframe, + FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents +); + +#ifndef FLAC__INTEGER_ONLY_LIBRARY +static unsigned evaluate_lpc_subframe_( + FLAC__StreamEncoder *encoder, + const FLAC__int32 signal[], + FLAC__int32 residual[], + FLAC__uint64 abs_residual_partition_sums[], + unsigned raw_bits_per_partition[], + const FLAC__real lp_coeff[], + unsigned blocksize, + unsigned subframe_bps, + unsigned order, + unsigned qlp_coeff_precision, + unsigned rice_parameter, + unsigned min_partition_order, + unsigned max_partition_order, + FLAC__bool do_escape_coding, + unsigned rice_parameter_search_dist, + FLAC__Subframe *subframe, + FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents +); +#endif + +static unsigned evaluate_verbatim_subframe_( + FLAC__StreamEncoder *encoder, + const FLAC__int32 signal[], + unsigned blocksize, + unsigned subframe_bps, + FLAC__Subframe *subframe +); + +static unsigned find_best_partition_order_( + struct FLAC__StreamEncoderPrivate *private_, + const FLAC__int32 residual[], + FLAC__uint64 abs_residual_partition_sums[], + unsigned raw_bits_per_partition[], + unsigned residual_samples, + unsigned predictor_order, + unsigned rice_parameter, + unsigned min_partition_order, + unsigned max_partition_order, + unsigned bps, + FLAC__bool do_escape_coding, + unsigned rice_parameter_search_dist, + FLAC__EntropyCodingMethod_PartitionedRice *best_partitioned_rice +); + +static void precompute_partition_info_sums_( + const FLAC__int32 residual[], + FLAC__uint64 abs_residual_partition_sums[], + unsigned residual_samples, + unsigned predictor_order, + unsigned min_partition_order, + unsigned max_partition_order, + unsigned bps +); + +static void precompute_partition_info_escapes_( + const FLAC__int32 residual[], + unsigned raw_bits_per_partition[], + unsigned residual_samples, + unsigned predictor_order, + unsigned min_partition_order, + unsigned max_partition_order +); + +static FLAC__bool set_partitioned_rice_( +#ifdef EXACT_RICE_BITS_CALCULATION + const FLAC__int32 residual[], +#endif + const FLAC__uint64 abs_residual_partition_sums[], + const unsigned raw_bits_per_partition[], + const unsigned residual_samples, + const unsigned predictor_order, + const unsigned suggested_rice_parameter, + const unsigned rice_parameter_search_dist, + const unsigned partition_order, + const FLAC__bool search_for_escapes, + FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents, + unsigned *bits +); + +static unsigned get_wasted_bits_(FLAC__int32 signal[], unsigned samples); + +/* verify-related routines: */ +static void append_to_verify_fifo_( + verify_input_fifo *fifo, + const FLAC__int32 * const input[], + unsigned input_offset, + unsigned channels, + unsigned wide_samples +); + +static void append_to_verify_fifo_interleaved_( + verify_input_fifo *fifo, + const FLAC__int32 input[], + unsigned input_offset, + unsigned channels, + unsigned wide_samples +); + +static FLAC__StreamDecoderReadStatus verify_read_callback_(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data); +static FLAC__StreamDecoderWriteStatus verify_write_callback_(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data); +static void verify_metadata_callback_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data); +static void verify_error_callback_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data); + +static FLAC__StreamEncoderReadStatus file_read_callback_(const FLAC__StreamEncoder *encoder, FLAC__byte buffer[], size_t *bytes, void *client_data); +static FLAC__StreamEncoderSeekStatus file_seek_callback_(const FLAC__StreamEncoder *encoder, FLAC__uint64 absolute_byte_offset, void *client_data); +static FLAC__StreamEncoderTellStatus file_tell_callback_(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_byte_offset, void *client_data); +static FLAC__StreamEncoderWriteStatus file_write_callback_(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame, void *client_data); +static FILE *get_binary_stdout_(void); + + +/*********************************************************************** + * + * Private class data + * + ***********************************************************************/ + +typedef struct FLAC__StreamEncoderPrivate { + unsigned input_capacity; /* current size (in samples) of the signal and residual buffers */ + FLAC__int32 *integer_signal[FLAC__MAX_CHANNELS]; /* the integer version of the input signal */ + FLAC__int32 *integer_signal_mid_side[2]; /* the integer version of the mid-side input signal (stereo only) */ +#ifndef FLAC__INTEGER_ONLY_LIBRARY + FLAC__real *real_signal[FLAC__MAX_CHANNELS]; /* (@@@ currently unused) the floating-point version of the input signal */ + FLAC__real *real_signal_mid_side[2]; /* (@@@ currently unused) the floating-point version of the mid-side input signal (stereo only) */ + FLAC__real *window[FLAC__MAX_APODIZATION_FUNCTIONS]; /* the pre-computed floating-point window for each apodization function */ + FLAC__real *windowed_signal; /* the integer_signal[] * current window[] */ +#endif + unsigned subframe_bps[FLAC__MAX_CHANNELS]; /* the effective bits per sample of the input signal (stream bps - wasted bits) */ + unsigned subframe_bps_mid_side[2]; /* the effective bits per sample of the mid-side input signal (stream bps - wasted bits + 0/1) */ + FLAC__int32 *residual_workspace[FLAC__MAX_CHANNELS][2]; /* each channel has a candidate and best workspace where the subframe residual signals will be stored */ + FLAC__int32 *residual_workspace_mid_side[2][2]; + FLAC__Subframe subframe_workspace[FLAC__MAX_CHANNELS][2]; + FLAC__Subframe subframe_workspace_mid_side[2][2]; + FLAC__Subframe *subframe_workspace_ptr[FLAC__MAX_CHANNELS][2]; + FLAC__Subframe *subframe_workspace_ptr_mid_side[2][2]; + FLAC__EntropyCodingMethod_PartitionedRiceContents partitioned_rice_contents_workspace[FLAC__MAX_CHANNELS][2]; + FLAC__EntropyCodingMethod_PartitionedRiceContents partitioned_rice_contents_workspace_mid_side[FLAC__MAX_CHANNELS][2]; + FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents_workspace_ptr[FLAC__MAX_CHANNELS][2]; + FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents_workspace_ptr_mid_side[FLAC__MAX_CHANNELS][2]; + unsigned best_subframe[FLAC__MAX_CHANNELS]; /* index (0 or 1) into 2nd dimension of the above workspaces */ + unsigned best_subframe_mid_side[2]; + unsigned best_subframe_bits[FLAC__MAX_CHANNELS]; /* size in bits of the best subframe for each channel */ + unsigned best_subframe_bits_mid_side[2]; + FLAC__uint64 *abs_residual_partition_sums; /* workspace where the sum of abs(candidate residual) for each partition is stored */ + unsigned *raw_bits_per_partition; /* workspace where the sum of silog2(candidate residual) for each partition is stored */ + FLAC__BitWriter *frame; /* the current frame being worked on */ + unsigned loose_mid_side_stereo_frames; /* rounded number of frames the encoder will use before trying both independent and mid/side frames again */ + unsigned loose_mid_side_stereo_frame_count; /* number of frames using the current channel assignment */ + FLAC__ChannelAssignment last_channel_assignment; + FLAC__StreamMetadata streaminfo; /* scratchpad for STREAMINFO as it is built */ + FLAC__StreamMetadata_SeekTable *seek_table; /* pointer into encoder->protected_->metadata_ where the seek table is */ + unsigned current_sample_number; + unsigned current_frame_number; + FLAC__MD5Context md5context; + FLAC__CPUInfo cpuinfo; +#ifndef FLAC__INTEGER_ONLY_LIBRARY + unsigned (*local_fixed_compute_best_predictor)(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); +#else + unsigned (*local_fixed_compute_best_predictor)(const FLAC__int32 data[], unsigned data_len, FLAC__fixedpoint residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); +#endif +#ifndef FLAC__INTEGER_ONLY_LIBRARY + void (*local_lpc_compute_autocorrelation)(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); + void (*local_lpc_compute_residual_from_qlp_coefficients)(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); + void (*local_lpc_compute_residual_from_qlp_coefficients_64bit)(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); + void (*local_lpc_compute_residual_from_qlp_coefficients_16bit)(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); +#endif + FLAC__bool use_wide_by_block; /* use slow 64-bit versions of some functions because of the block size */ + FLAC__bool use_wide_by_partition; /* use slow 64-bit versions of some functions because of the min partition order and blocksize */ + FLAC__bool use_wide_by_order; /* use slow 64-bit versions of some functions because of the lpc order */ + FLAC__bool disable_constant_subframes; + FLAC__bool disable_fixed_subframes; + FLAC__bool disable_verbatim_subframes; +#if FLAC__HAS_OGG + FLAC__bool is_ogg; +#endif + FLAC__StreamEncoderReadCallback read_callback; /* currently only needed for Ogg FLAC */ + FLAC__StreamEncoderSeekCallback seek_callback; + FLAC__StreamEncoderTellCallback tell_callback; + FLAC__StreamEncoderWriteCallback write_callback; + FLAC__StreamEncoderMetadataCallback metadata_callback; + FLAC__StreamEncoderProgressCallback progress_callback; + void *client_data; + unsigned first_seekpoint_to_check; + FILE *file; /* only used when encoding to a file */ + FLAC__uint64 bytes_written; + FLAC__uint64 samples_written; + unsigned frames_written; + unsigned total_frames_estimate; + /* unaligned (original) pointers to allocated data */ + FLAC__int32 *integer_signal_unaligned[FLAC__MAX_CHANNELS]; + FLAC__int32 *integer_signal_mid_side_unaligned[2]; +#ifndef FLAC__INTEGER_ONLY_LIBRARY + FLAC__real *real_signal_unaligned[FLAC__MAX_CHANNELS]; /* (@@@ currently unused) */ + FLAC__real *real_signal_mid_side_unaligned[2]; /* (@@@ currently unused) */ + FLAC__real *window_unaligned[FLAC__MAX_APODIZATION_FUNCTIONS]; + FLAC__real *windowed_signal_unaligned; +#endif + FLAC__int32 *residual_workspace_unaligned[FLAC__MAX_CHANNELS][2]; + FLAC__int32 *residual_workspace_mid_side_unaligned[2][2]; + FLAC__uint64 *abs_residual_partition_sums_unaligned; + unsigned *raw_bits_per_partition_unaligned; + /* + * These fields have been moved here from private function local + * declarations merely to save stack space during encoding. + */ +#ifndef FLAC__INTEGER_ONLY_LIBRARY + FLAC__real lp_coeff[FLAC__MAX_LPC_ORDER][FLAC__MAX_LPC_ORDER]; /* from process_subframe_() */ +#endif + FLAC__EntropyCodingMethod_PartitionedRiceContents partitioned_rice_contents_extra[2]; /* from find_best_partition_order_() */ + /* + * The data for the verify section + */ + struct { + FLAC__StreamDecoder *decoder; + EncoderStateHint state_hint; + FLAC__bool needs_magic_hack; + verify_input_fifo input_fifo; + verify_output output; + struct { + FLAC__uint64 absolute_sample; + unsigned frame_number; + unsigned channel; + unsigned sample; + FLAC__int32 expected; + FLAC__int32 got; + } error_stats; + } verify; + FLAC__bool is_being_deleted; /* if true, call to ..._finish() from ..._delete() will not call the callbacks */ +} FLAC__StreamEncoderPrivate; + +/*********************************************************************** + * + * Public static class data + * + ***********************************************************************/ + +FLAC_API const char * const FLAC__StreamEncoderStateString[] = { + "FLAC__STREAM_ENCODER_OK", + "FLAC__STREAM_ENCODER_UNINITIALIZED", + "FLAC__STREAM_ENCODER_OGG_ERROR", + "FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR", + "FLAC__STREAM_ENCODER_VERIFY_MISMATCH_IN_AUDIO_DATA", + "FLAC__STREAM_ENCODER_CLIENT_ERROR", + "FLAC__STREAM_ENCODER_IO_ERROR", + "FLAC__STREAM_ENCODER_FRAMING_ERROR", + "FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR" +}; + +FLAC_API const char * const FLAC__StreamEncoderInitStatusString[] = { + "FLAC__STREAM_ENCODER_INIT_STATUS_OK", + "FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR", + "FLAC__STREAM_ENCODER_INIT_STATUS_UNSUPPORTED_CONTAINER", + "FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_CALLBACKS", + "FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_NUMBER_OF_CHANNELS", + "FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_BITS_PER_SAMPLE", + "FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_SAMPLE_RATE", + "FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_BLOCK_SIZE", + "FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_MAX_LPC_ORDER", + "FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_QLP_COEFF_PRECISION", + "FLAC__STREAM_ENCODER_INIT_STATUS_BLOCK_SIZE_TOO_SMALL_FOR_LPC_ORDER", + "FLAC__STREAM_ENCODER_INIT_STATUS_NOT_STREAMABLE", + "FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_METADATA", + "FLAC__STREAM_ENCODER_INIT_STATUS_ALREADY_INITIALIZED" +}; + +FLAC_API const char * const FLAC__treamEncoderReadStatusString[] = { + "FLAC__STREAM_ENCODER_READ_STATUS_CONTINUE", + "FLAC__STREAM_ENCODER_READ_STATUS_END_OF_STREAM", + "FLAC__STREAM_ENCODER_READ_STATUS_ABORT", + "FLAC__STREAM_ENCODER_READ_STATUS_UNSUPPORTED" +}; + +FLAC_API const char * const FLAC__StreamEncoderWriteStatusString[] = { + "FLAC__STREAM_ENCODER_WRITE_STATUS_OK", + "FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR" +}; + +FLAC_API const char * const FLAC__StreamEncoderSeekStatusString[] = { + "FLAC__STREAM_ENCODER_SEEK_STATUS_OK", + "FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR", + "FLAC__STREAM_ENCODER_SEEK_STATUS_UNSUPPORTED" +}; + +FLAC_API const char * const FLAC__StreamEncoderTellStatusString[] = { + "FLAC__STREAM_ENCODER_TELL_STATUS_OK", + "FLAC__STREAM_ENCODER_TELL_STATUS_ERROR", + "FLAC__STREAM_ENCODER_TELL_STATUS_UNSUPPORTED" +}; + +/* Number of samples that will be overread to watch for end of stream. By + * 'overread', we mean that the FLAC__stream_encoder_process*() calls will + * always try to read blocksize+1 samples before encoding a block, so that + * even if the stream has a total sample count that is an integral multiple + * of the blocksize, we will still notice when we are encoding the last + * block. This is needed, for example, to correctly set the end-of-stream + * marker in Ogg FLAC. + * + * WATCHOUT: some parts of the code assert that OVERREAD_ == 1 and there's + * not really any reason to change it. + */ +static const unsigned OVERREAD_ = 1; + +/*********************************************************************** + * + * Class constructor/destructor + * + */ +FLAC_API FLAC__StreamEncoder *FLAC__stream_encoder_new(void) +{ + FLAC__StreamEncoder *encoder; + unsigned i; + + FLAC__ASSERT(sizeof(int) >= 4); /* we want to die right away if this is not true */ + + encoder = (FLAC__StreamEncoder*)calloc(1, sizeof(FLAC__StreamEncoder)); + if(encoder == 0) { + return 0; + } + + encoder->protected_ = (FLAC__StreamEncoderProtected*)calloc(1, sizeof(FLAC__StreamEncoderProtected)); + if(encoder->protected_ == 0) { + free(encoder); + return 0; + } + + encoder->private_ = (FLAC__StreamEncoderPrivate*)calloc(1, sizeof(FLAC__StreamEncoderPrivate)); + if(encoder->private_ == 0) { + free(encoder->protected_); + free(encoder); + return 0; + } + + encoder->private_->frame = FLAC__bitwriter_new(); + if(encoder->private_->frame == 0) { + free(encoder->private_); + free(encoder->protected_); + free(encoder); + return 0; + } + + encoder->private_->file = 0; + + set_defaults_(encoder); + + encoder->private_->is_being_deleted = false; + + for(i = 0; i < FLAC__MAX_CHANNELS; i++) { + encoder->private_->subframe_workspace_ptr[i][0] = &encoder->private_->subframe_workspace[i][0]; + encoder->private_->subframe_workspace_ptr[i][1] = &encoder->private_->subframe_workspace[i][1]; + } + for(i = 0; i < 2; i++) { + encoder->private_->subframe_workspace_ptr_mid_side[i][0] = &encoder->private_->subframe_workspace_mid_side[i][0]; + encoder->private_->subframe_workspace_ptr_mid_side[i][1] = &encoder->private_->subframe_workspace_mid_side[i][1]; + } + for(i = 0; i < FLAC__MAX_CHANNELS; i++) { + encoder->private_->partitioned_rice_contents_workspace_ptr[i][0] = &encoder->private_->partitioned_rice_contents_workspace[i][0]; + encoder->private_->partitioned_rice_contents_workspace_ptr[i][1] = &encoder->private_->partitioned_rice_contents_workspace[i][1]; + } + for(i = 0; i < 2; i++) { + encoder->private_->partitioned_rice_contents_workspace_ptr_mid_side[i][0] = &encoder->private_->partitioned_rice_contents_workspace_mid_side[i][0]; + encoder->private_->partitioned_rice_contents_workspace_ptr_mid_side[i][1] = &encoder->private_->partitioned_rice_contents_workspace_mid_side[i][1]; + } + + for(i = 0; i < FLAC__MAX_CHANNELS; i++) { + FLAC__format_entropy_coding_method_partitioned_rice_contents_init(&encoder->private_->partitioned_rice_contents_workspace[i][0]); + FLAC__format_entropy_coding_method_partitioned_rice_contents_init(&encoder->private_->partitioned_rice_contents_workspace[i][1]); + } + for(i = 0; i < 2; i++) { + FLAC__format_entropy_coding_method_partitioned_rice_contents_init(&encoder->private_->partitioned_rice_contents_workspace_mid_side[i][0]); + FLAC__format_entropy_coding_method_partitioned_rice_contents_init(&encoder->private_->partitioned_rice_contents_workspace_mid_side[i][1]); + } + for(i = 0; i < 2; i++) + FLAC__format_entropy_coding_method_partitioned_rice_contents_init(&encoder->private_->partitioned_rice_contents_extra[i]); + + encoder->protected_->state = FLAC__STREAM_ENCODER_UNINITIALIZED; + + return encoder; +} + +FLAC_API void FLAC__stream_encoder_delete(FLAC__StreamEncoder *encoder) +{ + unsigned i; + + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->protected_); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->private_->frame); + + encoder->private_->is_being_deleted = true; + + (void)FLAC__stream_encoder_finish(encoder); + + if(0 != encoder->private_->verify.decoder) + FLAC__stream_decoder_delete(encoder->private_->verify.decoder); + + for(i = 0; i < FLAC__MAX_CHANNELS; i++) { + FLAC__format_entropy_coding_method_partitioned_rice_contents_clear(&encoder->private_->partitioned_rice_contents_workspace[i][0]); + FLAC__format_entropy_coding_method_partitioned_rice_contents_clear(&encoder->private_->partitioned_rice_contents_workspace[i][1]); + } + for(i = 0; i < 2; i++) { + FLAC__format_entropy_coding_method_partitioned_rice_contents_clear(&encoder->private_->partitioned_rice_contents_workspace_mid_side[i][0]); + FLAC__format_entropy_coding_method_partitioned_rice_contents_clear(&encoder->private_->partitioned_rice_contents_workspace_mid_side[i][1]); + } + for(i = 0; i < 2; i++) + FLAC__format_entropy_coding_method_partitioned_rice_contents_clear(&encoder->private_->partitioned_rice_contents_extra[i]); + + FLAC__bitwriter_delete(encoder->private_->frame); + free(encoder->private_); + free(encoder->protected_); + free(encoder); +} + +/*********************************************************************** + * + * Public class methods + * + ***********************************************************************/ + +static FLAC__StreamEncoderInitStatus init_stream_internal_( + FLAC__StreamEncoder *encoder, + FLAC__StreamEncoderReadCallback read_callback, + FLAC__StreamEncoderWriteCallback write_callback, + FLAC__StreamEncoderSeekCallback seek_callback, + FLAC__StreamEncoderTellCallback tell_callback, + FLAC__StreamEncoderMetadataCallback metadata_callback, + void *client_data, + FLAC__bool is_ogg +) +{ + unsigned i; + FLAC__bool metadata_has_seektable, metadata_has_vorbis_comment, metadata_picture_has_type1, metadata_picture_has_type2; + + FLAC__ASSERT(0 != encoder); + + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return FLAC__STREAM_ENCODER_INIT_STATUS_ALREADY_INITIALIZED; + +#if !FLAC__HAS_OGG + if(is_ogg) + return FLAC__STREAM_ENCODER_INIT_STATUS_UNSUPPORTED_CONTAINER; +#endif + + if(0 == write_callback || (seek_callback && 0 == tell_callback)) + return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_CALLBACKS; + + if(encoder->protected_->channels == 0 || encoder->protected_->channels > FLAC__MAX_CHANNELS) + return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_NUMBER_OF_CHANNELS; + + if(encoder->protected_->channels != 2) { + encoder->protected_->do_mid_side_stereo = false; + encoder->protected_->loose_mid_side_stereo = false; + } + else if(!encoder->protected_->do_mid_side_stereo) + encoder->protected_->loose_mid_side_stereo = false; + + if(encoder->protected_->bits_per_sample >= 32) + encoder->protected_->do_mid_side_stereo = false; /* since we currenty do 32-bit math, the side channel would have 33 bps and overflow */ + + if(encoder->protected_->bits_per_sample < FLAC__MIN_BITS_PER_SAMPLE || encoder->protected_->bits_per_sample > FLAC__REFERENCE_CODEC_MAX_BITS_PER_SAMPLE) + return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_BITS_PER_SAMPLE; + + if(!FLAC__format_sample_rate_is_valid(encoder->protected_->sample_rate)) + return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_SAMPLE_RATE; + + if(encoder->protected_->blocksize == 0) { + if(encoder->protected_->max_lpc_order == 0) + encoder->protected_->blocksize = 1152; + else + encoder->protected_->blocksize = 4096; + } + + if(encoder->protected_->blocksize < FLAC__MIN_BLOCK_SIZE || encoder->protected_->blocksize > FLAC__MAX_BLOCK_SIZE) + return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_BLOCK_SIZE; + + if(encoder->protected_->max_lpc_order > FLAC__MAX_LPC_ORDER) + return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_MAX_LPC_ORDER; + + if(encoder->protected_->blocksize < encoder->protected_->max_lpc_order) + return FLAC__STREAM_ENCODER_INIT_STATUS_BLOCK_SIZE_TOO_SMALL_FOR_LPC_ORDER; + + if(encoder->protected_->qlp_coeff_precision == 0) { + if(encoder->protected_->bits_per_sample < 16) { + /* @@@ need some data about how to set this here w.r.t. blocksize and sample rate */ + /* @@@ until then we'll make a guess */ + encoder->protected_->qlp_coeff_precision = max(FLAC__MIN_QLP_COEFF_PRECISION, 2 + encoder->protected_->bits_per_sample / 2); + } + else if(encoder->protected_->bits_per_sample == 16) { + if(encoder->protected_->blocksize <= 192) + encoder->protected_->qlp_coeff_precision = 7; + else if(encoder->protected_->blocksize <= 384) + encoder->protected_->qlp_coeff_precision = 8; + else if(encoder->protected_->blocksize <= 576) + encoder->protected_->qlp_coeff_precision = 9; + else if(encoder->protected_->blocksize <= 1152) + encoder->protected_->qlp_coeff_precision = 10; + else if(encoder->protected_->blocksize <= 2304) + encoder->protected_->qlp_coeff_precision = 11; + else if(encoder->protected_->blocksize <= 4608) + encoder->protected_->qlp_coeff_precision = 12; + else + encoder->protected_->qlp_coeff_precision = 13; + } + else { + if(encoder->protected_->blocksize <= 384) + encoder->protected_->qlp_coeff_precision = FLAC__MAX_QLP_COEFF_PRECISION-2; + else if(encoder->protected_->blocksize <= 1152) + encoder->protected_->qlp_coeff_precision = FLAC__MAX_QLP_COEFF_PRECISION-1; + else + encoder->protected_->qlp_coeff_precision = FLAC__MAX_QLP_COEFF_PRECISION; + } + FLAC__ASSERT(encoder->protected_->qlp_coeff_precision <= FLAC__MAX_QLP_COEFF_PRECISION); + } + else if(encoder->protected_->qlp_coeff_precision < FLAC__MIN_QLP_COEFF_PRECISION || encoder->protected_->qlp_coeff_precision > FLAC__MAX_QLP_COEFF_PRECISION) + return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_QLP_COEFF_PRECISION; + + if(encoder->protected_->streamable_subset) { + if( + encoder->protected_->blocksize != 192 && + encoder->protected_->blocksize != 576 && + encoder->protected_->blocksize != 1152 && + encoder->protected_->blocksize != 2304 && + encoder->protected_->blocksize != 4608 && + encoder->protected_->blocksize != 256 && + encoder->protected_->blocksize != 512 && + encoder->protected_->blocksize != 1024 && + encoder->protected_->blocksize != 2048 && + encoder->protected_->blocksize != 4096 && + encoder->protected_->blocksize != 8192 && + encoder->protected_->blocksize != 16384 + ) + return FLAC__STREAM_ENCODER_INIT_STATUS_NOT_STREAMABLE; + if(!FLAC__format_sample_rate_is_subset(encoder->protected_->sample_rate)) + return FLAC__STREAM_ENCODER_INIT_STATUS_NOT_STREAMABLE; + if( + encoder->protected_->bits_per_sample != 8 && + encoder->protected_->bits_per_sample != 12 && + encoder->protected_->bits_per_sample != 16 && + encoder->protected_->bits_per_sample != 20 && + encoder->protected_->bits_per_sample != 24 + ) + return FLAC__STREAM_ENCODER_INIT_STATUS_NOT_STREAMABLE; + if(encoder->protected_->max_residual_partition_order > FLAC__SUBSET_MAX_RICE_PARTITION_ORDER) + return FLAC__STREAM_ENCODER_INIT_STATUS_NOT_STREAMABLE; + if( + encoder->protected_->sample_rate <= 48000 && + ( + encoder->protected_->blocksize > FLAC__SUBSET_MAX_BLOCK_SIZE_48000HZ || + encoder->protected_->max_lpc_order > FLAC__SUBSET_MAX_LPC_ORDER_48000HZ + ) + ) { + return FLAC__STREAM_ENCODER_INIT_STATUS_NOT_STREAMABLE; + } + } + + if(encoder->protected_->max_residual_partition_order >= (1u << FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN)) + encoder->protected_->max_residual_partition_order = (1u << FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN) - 1; + if(encoder->protected_->min_residual_partition_order >= encoder->protected_->max_residual_partition_order) + encoder->protected_->min_residual_partition_order = encoder->protected_->max_residual_partition_order; + +#if FLAC__HAS_OGG + /* reorder metadata if necessary to ensure that any VORBIS_COMMENT is the first, according to the mapping spec */ + if(is_ogg && 0 != encoder->protected_->metadata && encoder->protected_->num_metadata_blocks > 1) { + for(i = 1; i < encoder->protected_->num_metadata_blocks; i++) { + if(0 != encoder->protected_->metadata[i] && encoder->protected_->metadata[i]->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) { + FLAC__StreamMetadata *vc = encoder->protected_->metadata[i]; + for( ; i > 0; i--) + encoder->protected_->metadata[i] = encoder->protected_->metadata[i-1]; + encoder->protected_->metadata[0] = vc; + break; + } + } + } +#endif + /* keep track of any SEEKTABLE block */ + if(0 != encoder->protected_->metadata && encoder->protected_->num_metadata_blocks > 0) { + for(i = 0; i < encoder->protected_->num_metadata_blocks; i++) { + if(0 != encoder->protected_->metadata[i] && encoder->protected_->metadata[i]->type == FLAC__METADATA_TYPE_SEEKTABLE) { + encoder->private_->seek_table = &encoder->protected_->metadata[i]->data.seek_table; + break; /* take only the first one */ + } + } + } + + /* validate metadata */ + if(0 == encoder->protected_->metadata && encoder->protected_->num_metadata_blocks > 0) + return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_METADATA; + metadata_has_seektable = false; + metadata_has_vorbis_comment = false; + metadata_picture_has_type1 = false; + metadata_picture_has_type2 = false; + for(i = 0; i < encoder->protected_->num_metadata_blocks; i++) { + const FLAC__StreamMetadata *m = encoder->protected_->metadata[i]; + if(m->type == FLAC__METADATA_TYPE_STREAMINFO) + return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_METADATA; + else if(m->type == FLAC__METADATA_TYPE_SEEKTABLE) { + if(metadata_has_seektable) /* only one is allowed */ + return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_METADATA; + metadata_has_seektable = true; + if(!FLAC__format_seektable_is_legal(&m->data.seek_table)) + return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_METADATA; + } + else if(m->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) { + if(metadata_has_vorbis_comment) /* only one is allowed */ + return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_METADATA; + metadata_has_vorbis_comment = true; + } + else if(m->type == FLAC__METADATA_TYPE_CUESHEET) { + if(!FLAC__format_cuesheet_is_legal(&m->data.cue_sheet, m->data.cue_sheet.is_cd, /*violation=*/0)) + return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_METADATA; + } + else if(m->type == FLAC__METADATA_TYPE_PICTURE) { + if(!FLAC__format_picture_is_legal(&m->data.picture, /*violation=*/0)) + return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_METADATA; + if(m->data.picture.type == FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON_STANDARD) { + if(metadata_picture_has_type1) /* there should only be 1 per stream */ + return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_METADATA; + metadata_picture_has_type1 = true; + /* standard icon must be 32x32 pixel PNG */ + if( + m->data.picture.type == FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON_STANDARD && + ( + (strcmp(m->data.picture.mime_type, "image/png") && strcmp(m->data.picture.mime_type, "-->")) || + m->data.picture.width != 32 || + m->data.picture.height != 32 + ) + ) + return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_METADATA; + } + else if(m->data.picture.type == FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON) { + if(metadata_picture_has_type2) /* there should only be 1 per stream */ + return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_METADATA; + metadata_picture_has_type2 = true; + } + } + } + + encoder->private_->input_capacity = 0; + for(i = 0; i < encoder->protected_->channels; i++) { + encoder->private_->integer_signal_unaligned[i] = encoder->private_->integer_signal[i] = 0; +#ifndef FLAC__INTEGER_ONLY_LIBRARY + encoder->private_->real_signal_unaligned[i] = encoder->private_->real_signal[i] = 0; +#endif + } + for(i = 0; i < 2; i++) { + encoder->private_->integer_signal_mid_side_unaligned[i] = encoder->private_->integer_signal_mid_side[i] = 0; +#ifndef FLAC__INTEGER_ONLY_LIBRARY + encoder->private_->real_signal_mid_side_unaligned[i] = encoder->private_->real_signal_mid_side[i] = 0; +#endif + } +#ifndef FLAC__INTEGER_ONLY_LIBRARY + for(i = 0; i < encoder->protected_->num_apodizations; i++) + encoder->private_->window_unaligned[i] = encoder->private_->window[i] = 0; + encoder->private_->windowed_signal_unaligned = encoder->private_->windowed_signal = 0; +#endif + for(i = 0; i < encoder->protected_->channels; i++) { + encoder->private_->residual_workspace_unaligned[i][0] = encoder->private_->residual_workspace[i][0] = 0; + encoder->private_->residual_workspace_unaligned[i][1] = encoder->private_->residual_workspace[i][1] = 0; + encoder->private_->best_subframe[i] = 0; + } + for(i = 0; i < 2; i++) { + encoder->private_->residual_workspace_mid_side_unaligned[i][0] = encoder->private_->residual_workspace_mid_side[i][0] = 0; + encoder->private_->residual_workspace_mid_side_unaligned[i][1] = encoder->private_->residual_workspace_mid_side[i][1] = 0; + encoder->private_->best_subframe_mid_side[i] = 0; + } + encoder->private_->abs_residual_partition_sums_unaligned = encoder->private_->abs_residual_partition_sums = 0; + encoder->private_->raw_bits_per_partition_unaligned = encoder->private_->raw_bits_per_partition = 0; +#ifndef FLAC__INTEGER_ONLY_LIBRARY + encoder->private_->loose_mid_side_stereo_frames = (unsigned)((FLAC__double)encoder->protected_->sample_rate * 0.4 / (FLAC__double)encoder->protected_->blocksize + 0.5); +#else + /* 26214 is the approximate fixed-point equivalent to 0.4 (0.4 * 2^16) */ + /* sample rate can be up to 655350 Hz, and thus use 20 bits, so we do the multiply÷ by hand */ + FLAC__ASSERT(FLAC__MAX_SAMPLE_RATE <= 655350); + FLAC__ASSERT(FLAC__MAX_BLOCK_SIZE <= 65535); + FLAC__ASSERT(encoder->protected_->sample_rate <= 655350); + FLAC__ASSERT(encoder->protected_->blocksize <= 65535); + encoder->private_->loose_mid_side_stereo_frames = (unsigned)FLAC__fixedpoint_trunc((((FLAC__uint64)(encoder->protected_->sample_rate) * (FLAC__uint64)(26214)) << 16) / (encoder->protected_->blocksize<<16) + FLAC__FP_ONE_HALF); +#endif + if(encoder->private_->loose_mid_side_stereo_frames == 0) + encoder->private_->loose_mid_side_stereo_frames = 1; + encoder->private_->loose_mid_side_stereo_frame_count = 0; + encoder->private_->current_sample_number = 0; + encoder->private_->current_frame_number = 0; + + encoder->private_->use_wide_by_block = (encoder->protected_->bits_per_sample + FLAC__bitmath_ilog2(encoder->protected_->blocksize)+1 > 30); + encoder->private_->use_wide_by_order = (encoder->protected_->bits_per_sample + FLAC__bitmath_ilog2(max(encoder->protected_->max_lpc_order, FLAC__MAX_FIXED_ORDER))+1 > 30); /*@@@ need to use this? */ + encoder->private_->use_wide_by_partition = (false); /*@@@ need to set this */ + + /* + * get the CPU info and set the function pointers + */ + FLAC__cpu_info(&encoder->private_->cpuinfo); + /* first default to the non-asm routines */ +#ifndef FLAC__INTEGER_ONLY_LIBRARY + encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation; +#endif + encoder->private_->local_fixed_compute_best_predictor = FLAC__fixed_compute_best_predictor; +#ifndef FLAC__INTEGER_ONLY_LIBRARY + encoder->private_->local_lpc_compute_residual_from_qlp_coefficients = FLAC__lpc_compute_residual_from_qlp_coefficients; + encoder->private_->local_lpc_compute_residual_from_qlp_coefficients_64bit = FLAC__lpc_compute_residual_from_qlp_coefficients_wide; + encoder->private_->local_lpc_compute_residual_from_qlp_coefficients_16bit = FLAC__lpc_compute_residual_from_qlp_coefficients; +#endif + /* now override with asm where appropriate */ +#ifndef FLAC__INTEGER_ONLY_LIBRARY +# ifndef FLAC__NO_ASM + if(encoder->private_->cpuinfo.use_asm) { +# ifdef FLAC__CPU_IA32 + FLAC__ASSERT(encoder->private_->cpuinfo.type == FLAC__CPUINFO_TYPE_IA32); +# ifdef FLAC__HAS_NASM + if(encoder->private_->cpuinfo.data.ia32.sse) { + if(encoder->protected_->max_lpc_order < 4) + encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_4; + else if(encoder->protected_->max_lpc_order < 8) + encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_8; + else if(encoder->protected_->max_lpc_order < 12) + encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_12; + else + encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_asm_ia32; + } + else if(encoder->private_->cpuinfo.data.ia32._3dnow) + encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_asm_ia32_3dnow; + else + encoder->private_->local_lpc_compute_autocorrelation = FLAC__lpc_compute_autocorrelation_asm_ia32; + if(encoder->private_->cpuinfo.data.ia32.mmx) { + encoder->private_->local_lpc_compute_residual_from_qlp_coefficients = FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32; + encoder->private_->local_lpc_compute_residual_from_qlp_coefficients_16bit = FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32_mmx; + } + else { + encoder->private_->local_lpc_compute_residual_from_qlp_coefficients = FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32; + encoder->private_->local_lpc_compute_residual_from_qlp_coefficients_16bit = FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32; + } + if(encoder->private_->cpuinfo.data.ia32.mmx && encoder->private_->cpuinfo.data.ia32.cmov) + encoder->private_->local_fixed_compute_best_predictor = FLAC__fixed_compute_best_predictor_asm_ia32_mmx_cmov; +# endif /* FLAC__HAS_NASM */ +# endif /* FLAC__CPU_IA32 */ + } +# endif /* !FLAC__NO_ASM */ +#endif /* !FLAC__INTEGER_ONLY_LIBRARY */ + /* finally override based on wide-ness if necessary */ + if(encoder->private_->use_wide_by_block) { + encoder->private_->local_fixed_compute_best_predictor = FLAC__fixed_compute_best_predictor_wide; + } + + /* set state to OK; from here on, errors are fatal and we'll override the state then */ + encoder->protected_->state = FLAC__STREAM_ENCODER_OK; + +#if FLAC__HAS_OGG + encoder->private_->is_ogg = is_ogg; + if(is_ogg && !FLAC__ogg_encoder_aspect_init(&encoder->protected_->ogg_encoder_aspect)) { + encoder->protected_->state = FLAC__STREAM_ENCODER_OGG_ERROR; + return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR; + } +#endif + + encoder->private_->read_callback = read_callback; + encoder->private_->write_callback = write_callback; + encoder->private_->seek_callback = seek_callback; + encoder->private_->tell_callback = tell_callback; + encoder->private_->metadata_callback = metadata_callback; + encoder->private_->client_data = client_data; + + if(!resize_buffers_(encoder, encoder->protected_->blocksize)) { + /* the above function sets the state for us in case of an error */ + return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR; + } + + if(!FLAC__bitwriter_init(encoder->private_->frame)) { + encoder->protected_->state = FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR; + return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR; + } + + /* + * Set up the verify stuff if necessary + */ + if(encoder->protected_->verify) { + /* + * First, set up the fifo which will hold the + * original signal to compare against + */ + encoder->private_->verify.input_fifo.size = encoder->protected_->blocksize+OVERREAD_; + for(i = 0; i < encoder->protected_->channels; i++) { + if(0 == (encoder->private_->verify.input_fifo.data[i] = (FLAC__int32*)malloc(sizeof(FLAC__int32) * encoder->private_->verify.input_fifo.size))) { + encoder->protected_->state = FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR; + return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR; + } + } + encoder->private_->verify.input_fifo.tail = 0; + + /* + * Now set up a stream decoder for verification + */ + encoder->private_->verify.decoder = FLAC__stream_decoder_new(); + if(0 == encoder->private_->verify.decoder) { + encoder->protected_->state = FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR; + return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR; + } + + if(FLAC__stream_decoder_init_stream(encoder->private_->verify.decoder, verify_read_callback_, /*seek_callback=*/0, /*tell_callback=*/0, /*length_callback=*/0, /*eof_callback=*/0, verify_write_callback_, verify_metadata_callback_, verify_error_callback_, /*client_data=*/encoder) != FLAC__STREAM_DECODER_INIT_STATUS_OK) { + encoder->protected_->state = FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR; + return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR; + } + } + encoder->private_->verify.error_stats.absolute_sample = 0; + encoder->private_->verify.error_stats.frame_number = 0; + encoder->private_->verify.error_stats.channel = 0; + encoder->private_->verify.error_stats.sample = 0; + encoder->private_->verify.error_stats.expected = 0; + encoder->private_->verify.error_stats.got = 0; + + /* + * These must be done before we write any metadata, because that + * calls the write_callback, which uses these values. + */ + encoder->private_->first_seekpoint_to_check = 0; + encoder->private_->samples_written = 0; + encoder->protected_->streaminfo_offset = 0; + encoder->protected_->seektable_offset = 0; + encoder->protected_->audio_offset = 0; + + /* + * write the stream header + */ + if(encoder->protected_->verify) + encoder->private_->verify.state_hint = ENCODER_IN_MAGIC; + if(!FLAC__bitwriter_write_raw_uint32(encoder->private_->frame, FLAC__STREAM_SYNC, FLAC__STREAM_SYNC_LEN)) { + encoder->protected_->state = FLAC__STREAM_ENCODER_FRAMING_ERROR; + return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR; + } + if(!write_bitbuffer_(encoder, 0, /*is_last_block=*/false)) { + /* the above function sets the state for us in case of an error */ + return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR; + } + + /* + * write the STREAMINFO metadata block + */ + if(encoder->protected_->verify) + encoder->private_->verify.state_hint = ENCODER_IN_METADATA; + encoder->private_->streaminfo.type = FLAC__METADATA_TYPE_STREAMINFO; + encoder->private_->streaminfo.is_last = false; /* we will have at a minimum a VORBIS_COMMENT afterwards */ + encoder->private_->streaminfo.length = FLAC__STREAM_METADATA_STREAMINFO_LENGTH; + encoder->private_->streaminfo.data.stream_info.min_blocksize = encoder->protected_->blocksize; /* this encoder uses the same blocksize for the whole stream */ + encoder->private_->streaminfo.data.stream_info.max_blocksize = encoder->protected_->blocksize; + encoder->private_->streaminfo.data.stream_info.min_framesize = 0; /* we don't know this yet; have to fill it in later */ + encoder->private_->streaminfo.data.stream_info.max_framesize = 0; /* we don't know this yet; have to fill it in later */ + encoder->private_->streaminfo.data.stream_info.sample_rate = encoder->protected_->sample_rate; + encoder->private_->streaminfo.data.stream_info.channels = encoder->protected_->channels; + encoder->private_->streaminfo.data.stream_info.bits_per_sample = encoder->protected_->bits_per_sample; + encoder->private_->streaminfo.data.stream_info.total_samples = encoder->protected_->total_samples_estimate; /* we will replace this later with the real total */ + memset(encoder->private_->streaminfo.data.stream_info.md5sum, 0, 16); /* we don't know this yet; have to fill it in later */ + FLAC__MD5Init(&encoder->private_->md5context); + if(!FLAC__add_metadata_block(&encoder->private_->streaminfo, encoder->private_->frame)) { + encoder->protected_->state = FLAC__STREAM_ENCODER_FRAMING_ERROR; + return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR; + } + if(!write_bitbuffer_(encoder, 0, /*is_last_block=*/false)) { + /* the above function sets the state for us in case of an error */ + return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR; + } + + /* + * Now that the STREAMINFO block is written, we can init this to an + * absurdly-high value... + */ + encoder->private_->streaminfo.data.stream_info.min_framesize = (1u << FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN) - 1; + /* ... and clear this to 0 */ + encoder->private_->streaminfo.data.stream_info.total_samples = 0; + + /* + * Check to see if the supplied metadata contains a VORBIS_COMMENT; + * if not, we will write an empty one (FLAC__add_metadata_block() + * automatically supplies the vendor string). + * + * WATCHOUT: the Ogg FLAC mapping requires us to write this block after + * the STREAMINFO. (In the case that metadata_has_vorbis_comment is + * true it will have already insured that the metadata list is properly + * ordered.) + */ + if(!metadata_has_vorbis_comment) { + FLAC__StreamMetadata vorbis_comment; + vorbis_comment.type = FLAC__METADATA_TYPE_VORBIS_COMMENT; + vorbis_comment.is_last = (encoder->protected_->num_metadata_blocks == 0); + vorbis_comment.length = 4 + 4; /* MAGIC NUMBER */ + vorbis_comment.data.vorbis_comment.vendor_string.length = 0; + vorbis_comment.data.vorbis_comment.vendor_string.entry = 0; + vorbis_comment.data.vorbis_comment.num_comments = 0; + vorbis_comment.data.vorbis_comment.comments = 0; + if(!FLAC__add_metadata_block(&vorbis_comment, encoder->private_->frame)) { + encoder->protected_->state = FLAC__STREAM_ENCODER_FRAMING_ERROR; + return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR; + } + if(!write_bitbuffer_(encoder, 0, /*is_last_block=*/false)) { + /* the above function sets the state for us in case of an error */ + return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR; + } + } + + /* + * write the user's metadata blocks + */ + for(i = 0; i < encoder->protected_->num_metadata_blocks; i++) { + encoder->protected_->metadata[i]->is_last = (i == encoder->protected_->num_metadata_blocks - 1); + if(!FLAC__add_metadata_block(encoder->protected_->metadata[i], encoder->private_->frame)) { + encoder->protected_->state = FLAC__STREAM_ENCODER_FRAMING_ERROR; + return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR; + } + if(!write_bitbuffer_(encoder, 0, /*is_last_block=*/false)) { + /* the above function sets the state for us in case of an error */ + return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR; + } + } + + /* now that all the metadata is written, we save the stream offset */ + if(encoder->private_->tell_callback && encoder->private_->tell_callback(encoder, &encoder->protected_->audio_offset, encoder->private_->client_data) == FLAC__STREAM_ENCODER_TELL_STATUS_ERROR) { /* FLAC__STREAM_ENCODER_TELL_STATUS_UNSUPPORTED just means we didn't get the offset; no error */ + encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR; + return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR; + } + + if(encoder->protected_->verify) + encoder->private_->verify.state_hint = ENCODER_IN_AUDIO; + + return FLAC__STREAM_ENCODER_INIT_STATUS_OK; +} + +FLAC_API FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_stream( + FLAC__StreamEncoder *encoder, + FLAC__StreamEncoderWriteCallback write_callback, + FLAC__StreamEncoderSeekCallback seek_callback, + FLAC__StreamEncoderTellCallback tell_callback, + FLAC__StreamEncoderMetadataCallback metadata_callback, + void *client_data +) +{ + return init_stream_internal_( + encoder, + /*read_callback=*/0, + write_callback, + seek_callback, + tell_callback, + metadata_callback, + client_data, + /*is_ogg=*/false + ); +} + +FLAC_API FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_ogg_stream( + FLAC__StreamEncoder *encoder, + FLAC__StreamEncoderReadCallback read_callback, + FLAC__StreamEncoderWriteCallback write_callback, + FLAC__StreamEncoderSeekCallback seek_callback, + FLAC__StreamEncoderTellCallback tell_callback, + FLAC__StreamEncoderMetadataCallback metadata_callback, + void *client_data +) +{ + return init_stream_internal_( + encoder, + read_callback, + write_callback, + seek_callback, + tell_callback, + metadata_callback, + client_data, + /*is_ogg=*/true + ); +} + +static FLAC__StreamEncoderInitStatus init_FILE_internal_( + FLAC__StreamEncoder *encoder, + FILE *file, + FLAC__StreamEncoderProgressCallback progress_callback, + void *client_data, + FLAC__bool is_ogg +) +{ + FLAC__StreamEncoderInitStatus init_status; + + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != file); + + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return FLAC__STREAM_ENCODER_INIT_STATUS_ALREADY_INITIALIZED; + + /* double protection */ + if(file == 0) { + encoder->protected_->state = FLAC__STREAM_ENCODER_IO_ERROR; + return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR; + } + + /* + * To make sure that our file does not go unclosed after an error, we + * must assign the FILE pointer before any further error can occur in + * this routine. + */ + if(file == stdout) + file = get_binary_stdout_(); /* just to be safe */ + + encoder->private_->file = file; + + encoder->private_->progress_callback = progress_callback; + encoder->private_->bytes_written = 0; + encoder->private_->samples_written = 0; + encoder->private_->frames_written = 0; + + init_status = init_stream_internal_( + encoder, + encoder->private_->file == stdout? 0 : is_ogg? file_read_callback_ : 0, + file_write_callback_, + encoder->private_->file == stdout? 0 : file_seek_callback_, + encoder->private_->file == stdout? 0 : file_tell_callback_, + /*metadata_callback=*/0, + client_data, + is_ogg + ); + if(init_status != FLAC__STREAM_ENCODER_INIT_STATUS_OK) { + /* the above function sets the state for us in case of an error */ + return init_status; + } + + { + unsigned blocksize = FLAC__stream_encoder_get_blocksize(encoder); + + FLAC__ASSERT(blocksize != 0); + encoder->private_->total_frames_estimate = (unsigned)((FLAC__stream_encoder_get_total_samples_estimate(encoder) + blocksize - 1) / blocksize); + } + + return init_status; +} + +FLAC_API FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_FILE( + FLAC__StreamEncoder *encoder, + FILE *file, + FLAC__StreamEncoderProgressCallback progress_callback, + void *client_data +) +{ + return init_FILE_internal_(encoder, file, progress_callback, client_data, /*is_ogg=*/false); +} + +FLAC_API FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_ogg_FILE( + FLAC__StreamEncoder *encoder, + FILE *file, + FLAC__StreamEncoderProgressCallback progress_callback, + void *client_data +) +{ + return init_FILE_internal_(encoder, file, progress_callback, client_data, /*is_ogg=*/true); +} + +static FLAC__StreamEncoderInitStatus init_file_internal_( + FLAC__StreamEncoder *encoder, + const char *filename, + FLAC__StreamEncoderProgressCallback progress_callback, + void *client_data, + FLAC__bool is_ogg +) +{ + FILE *file; + + FLAC__ASSERT(0 != encoder); + + /* + * To make sure that our file does not go unclosed after an error, we + * have to do the same entrance checks here that are later performed + * in FLAC__stream_encoder_init_FILE() before the FILE* is assigned. + */ + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return FLAC__STREAM_ENCODER_INIT_STATUS_ALREADY_INITIALIZED; + + file = filename? fopen(filename, "w+b") : stdout; + + if(file == 0) { + encoder->protected_->state = FLAC__STREAM_ENCODER_IO_ERROR; + return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR; + } + + return init_FILE_internal_(encoder, file, progress_callback, client_data, is_ogg); +} + +FLAC_API FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_file( + FLAC__StreamEncoder *encoder, + const char *filename, + FLAC__StreamEncoderProgressCallback progress_callback, + void *client_data +) +{ + return init_file_internal_(encoder, filename, progress_callback, client_data, /*is_ogg=*/false); +} + +FLAC_API FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_ogg_file( + FLAC__StreamEncoder *encoder, + const char *filename, + FLAC__StreamEncoderProgressCallback progress_callback, + void *client_data +) +{ + return init_file_internal_(encoder, filename, progress_callback, client_data, /*is_ogg=*/true); +} + +FLAC_API FLAC__bool FLAC__stream_encoder_finish(FLAC__StreamEncoder *encoder) +{ + FLAC__bool error = false; + + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + + if(encoder->protected_->state == FLAC__STREAM_ENCODER_UNINITIALIZED) + return true; + + if(encoder->protected_->state == FLAC__STREAM_ENCODER_OK && !encoder->private_->is_being_deleted) { + if(encoder->private_->current_sample_number != 0) { + const FLAC__bool is_fractional_block = encoder->protected_->blocksize != encoder->private_->current_sample_number; + encoder->protected_->blocksize = encoder->private_->current_sample_number; + if(!process_frame_(encoder, is_fractional_block, /*is_last_block=*/true)) + error = true; + } + } + + FLAC__MD5Final(encoder->private_->streaminfo.data.stream_info.md5sum, &encoder->private_->md5context); + + if(!encoder->private_->is_being_deleted) { + if(encoder->protected_->state == FLAC__STREAM_ENCODER_OK) { + if(encoder->private_->seek_callback) { +#if FLAC__HAS_OGG + if(encoder->private_->is_ogg) + update_ogg_metadata_(encoder); + else +#endif + update_metadata_(encoder); + + /* check if an error occurred while updating metadata */ + if(encoder->protected_->state != FLAC__STREAM_ENCODER_OK) + error = true; + } + if(encoder->private_->metadata_callback) + encoder->private_->metadata_callback(encoder, &encoder->private_->streaminfo, encoder->private_->client_data); + } + + if(encoder->protected_->verify && 0 != encoder->private_->verify.decoder && !FLAC__stream_decoder_finish(encoder->private_->verify.decoder)) { + if(!error) + encoder->protected_->state = FLAC__STREAM_ENCODER_VERIFY_MISMATCH_IN_AUDIO_DATA; + error = true; + } + } + + if(0 != encoder->private_->file) { + if(encoder->private_->file != stdout) + fclose(encoder->private_->file); + encoder->private_->file = 0; + } + +#if FLAC__HAS_OGG + if(encoder->private_->is_ogg) + FLAC__ogg_encoder_aspect_finish(&encoder->protected_->ogg_encoder_aspect); +#endif + + free_(encoder); + set_defaults_(encoder); + + if(!error) + encoder->protected_->state = FLAC__STREAM_ENCODER_UNINITIALIZED; + + return !error; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_set_ogg_serial_number(FLAC__StreamEncoder *encoder, long value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; +#if FLAC__HAS_OGG + /* can't check encoder->private_->is_ogg since that's not set until init time */ + FLAC__ogg_encoder_aspect_set_serial_number(&encoder->protected_->ogg_encoder_aspect, value); + return true; +#else + (void)value; + return false; +#endif +} + +FLAC_API FLAC__bool FLAC__stream_encoder_set_verify(FLAC__StreamEncoder *encoder, FLAC__bool value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; +#ifndef FLAC__MANDATORY_VERIFY_WHILE_ENCODING + encoder->protected_->verify = value; +#endif + return true; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_set_streamable_subset(FLAC__StreamEncoder *encoder, FLAC__bool value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; + encoder->protected_->streamable_subset = value; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_set_channels(FLAC__StreamEncoder *encoder, unsigned value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; + encoder->protected_->channels = value; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_set_bits_per_sample(FLAC__StreamEncoder *encoder, unsigned value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; + encoder->protected_->bits_per_sample = value; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_set_sample_rate(FLAC__StreamEncoder *encoder, unsigned value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; + encoder->protected_->sample_rate = value; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_set_compression_level(FLAC__StreamEncoder *encoder, unsigned value) +{ + FLAC__bool ok = true; + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; + if(value >= sizeof(compression_levels_)/sizeof(compression_levels_[0])) + value = sizeof(compression_levels_)/sizeof(compression_levels_[0]) - 1; + ok &= FLAC__stream_encoder_set_do_mid_side_stereo (encoder, compression_levels_[value].do_mid_side_stereo); + ok &= FLAC__stream_encoder_set_loose_mid_side_stereo (encoder, compression_levels_[value].loose_mid_side_stereo); +#if 0 + /* was: */ + ok &= FLAC__stream_encoder_set_apodization (encoder, compression_levels_[value].apodization); + /* but it's too hard to specify the string in a locale-specific way */ +#else + encoder->protected_->num_apodizations = 1; + encoder->protected_->apodizations[0].type = FLAC__APODIZATION_TUKEY; + encoder->protected_->apodizations[0].parameters.tukey.p = 0.5; +#endif + ok &= FLAC__stream_encoder_set_max_lpc_order (encoder, compression_levels_[value].max_lpc_order); + ok &= FLAC__stream_encoder_set_qlp_coeff_precision (encoder, compression_levels_[value].qlp_coeff_precision); + ok &= FLAC__stream_encoder_set_do_qlp_coeff_prec_search (encoder, compression_levels_[value].do_qlp_coeff_prec_search); + ok &= FLAC__stream_encoder_set_do_escape_coding (encoder, compression_levels_[value].do_escape_coding); + ok &= FLAC__stream_encoder_set_do_exhaustive_model_search (encoder, compression_levels_[value].do_exhaustive_model_search); + ok &= FLAC__stream_encoder_set_min_residual_partition_order(encoder, compression_levels_[value].min_residual_partition_order); + ok &= FLAC__stream_encoder_set_max_residual_partition_order(encoder, compression_levels_[value].max_residual_partition_order); + ok &= FLAC__stream_encoder_set_rice_parameter_search_dist (encoder, compression_levels_[value].rice_parameter_search_dist); + return ok; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_set_blocksize(FLAC__StreamEncoder *encoder, unsigned value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; + encoder->protected_->blocksize = value; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_set_do_mid_side_stereo(FLAC__StreamEncoder *encoder, FLAC__bool value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; + encoder->protected_->do_mid_side_stereo = value; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_set_loose_mid_side_stereo(FLAC__StreamEncoder *encoder, FLAC__bool value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; + encoder->protected_->loose_mid_side_stereo = value; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_set_apodization(FLAC__StreamEncoder *encoder, const char *specification) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + FLAC__ASSERT(0 != specification); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; +#ifdef FLAC__INTEGER_ONLY_LIBRARY + (void)specification; /* silently ignore since we haven't integerized; will always use a rectangular window */ +#else + encoder->protected_->num_apodizations = 0; + while(1) { + const char *s = strchr(specification, ';'); + const size_t n = s? (size_t)(s - specification) : strlen(specification); + if (n==8 && 0 == strncmp("bartlett" , specification, n)) + encoder->protected_->apodizations[encoder->protected_->num_apodizations++].type = FLAC__APODIZATION_BARTLETT; + else if(n==13 && 0 == strncmp("bartlett_hann", specification, n)) + encoder->protected_->apodizations[encoder->protected_->num_apodizations++].type = FLAC__APODIZATION_BARTLETT_HANN; + else if(n==8 && 0 == strncmp("blackman" , specification, n)) + encoder->protected_->apodizations[encoder->protected_->num_apodizations++].type = FLAC__APODIZATION_BLACKMAN; + else if(n==26 && 0 == strncmp("blackman_harris_4term_92db", specification, n)) + encoder->protected_->apodizations[encoder->protected_->num_apodizations++].type = FLAC__APODIZATION_BLACKMAN_HARRIS_4TERM_92DB_SIDELOBE; + else if(n==6 && 0 == strncmp("connes" , specification, n)) + encoder->protected_->apodizations[encoder->protected_->num_apodizations++].type = FLAC__APODIZATION_CONNES; + else if(n==7 && 0 == strncmp("flattop" , specification, n)) + encoder->protected_->apodizations[encoder->protected_->num_apodizations++].type = FLAC__APODIZATION_FLATTOP; + else if(n>7 && 0 == strncmp("gauss(" , specification, 6)) { + FLAC__real stddev = (FLAC__real)strtod(specification+6, 0); + if (stddev > 0.0 && stddev <= 0.5) { + encoder->protected_->apodizations[encoder->protected_->num_apodizations].parameters.gauss.stddev = stddev; + encoder->protected_->apodizations[encoder->protected_->num_apodizations++].type = FLAC__APODIZATION_GAUSS; + } + } + else if(n==7 && 0 == strncmp("hamming" , specification, n)) + encoder->protected_->apodizations[encoder->protected_->num_apodizations++].type = FLAC__APODIZATION_HAMMING; + else if(n==4 && 0 == strncmp("hann" , specification, n)) + encoder->protected_->apodizations[encoder->protected_->num_apodizations++].type = FLAC__APODIZATION_HANN; + else if(n==13 && 0 == strncmp("kaiser_bessel", specification, n)) + encoder->protected_->apodizations[encoder->protected_->num_apodizations++].type = FLAC__APODIZATION_KAISER_BESSEL; + else if(n==7 && 0 == strncmp("nuttall" , specification, n)) + encoder->protected_->apodizations[encoder->protected_->num_apodizations++].type = FLAC__APODIZATION_NUTTALL; + else if(n==9 && 0 == strncmp("rectangle" , specification, n)) + encoder->protected_->apodizations[encoder->protected_->num_apodizations++].type = FLAC__APODIZATION_RECTANGLE; + else if(n==8 && 0 == strncmp("triangle" , specification, n)) + encoder->protected_->apodizations[encoder->protected_->num_apodizations++].type = FLAC__APODIZATION_TRIANGLE; + else if(n>7 && 0 == strncmp("tukey(" , specification, 6)) { + FLAC__real p = (FLAC__real)strtod(specification+6, 0); + if (p >= 0.0 && p <= 1.0) { + encoder->protected_->apodizations[encoder->protected_->num_apodizations].parameters.tukey.p = p; + encoder->protected_->apodizations[encoder->protected_->num_apodizations++].type = FLAC__APODIZATION_TUKEY; + } + } + else if(n==5 && 0 == strncmp("welch" , specification, n)) + encoder->protected_->apodizations[encoder->protected_->num_apodizations++].type = FLAC__APODIZATION_WELCH; + if (encoder->protected_->num_apodizations == 32) + break; + if (s) + specification = s+1; + else + break; + } + if(encoder->protected_->num_apodizations == 0) { + encoder->protected_->num_apodizations = 1; + encoder->protected_->apodizations[0].type = FLAC__APODIZATION_TUKEY; + encoder->protected_->apodizations[0].parameters.tukey.p = 0.5; + } +#endif + return true; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_set_max_lpc_order(FLAC__StreamEncoder *encoder, unsigned value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; + encoder->protected_->max_lpc_order = value; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_set_qlp_coeff_precision(FLAC__StreamEncoder *encoder, unsigned value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; + encoder->protected_->qlp_coeff_precision = value; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_set_do_qlp_coeff_prec_search(FLAC__StreamEncoder *encoder, FLAC__bool value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; + encoder->protected_->do_qlp_coeff_prec_search = value; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_set_do_escape_coding(FLAC__StreamEncoder *encoder, FLAC__bool value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; +#if 0 + /*@@@ deprecated: */ + encoder->protected_->do_escape_coding = value; +#else + (void)value; +#endif + return true; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_set_do_exhaustive_model_search(FLAC__StreamEncoder *encoder, FLAC__bool value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; + encoder->protected_->do_exhaustive_model_search = value; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_set_min_residual_partition_order(FLAC__StreamEncoder *encoder, unsigned value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; + encoder->protected_->min_residual_partition_order = value; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_set_max_residual_partition_order(FLAC__StreamEncoder *encoder, unsigned value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; + encoder->protected_->max_residual_partition_order = value; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_set_rice_parameter_search_dist(FLAC__StreamEncoder *encoder, unsigned value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; +#if 0 + /*@@@ deprecated: */ + encoder->protected_->rice_parameter_search_dist = value; +#else + (void)value; +#endif + return true; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_set_total_samples_estimate(FLAC__StreamEncoder *encoder, FLAC__uint64 value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; + encoder->protected_->total_samples_estimate = value; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_set_metadata(FLAC__StreamEncoder *encoder, FLAC__StreamMetadata **metadata, unsigned num_blocks) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) + return false; + if(0 == metadata) + num_blocks = 0; + if(0 == num_blocks) + metadata = 0; + /* realloc() does not do exactly what we want so... */ + if(encoder->protected_->metadata) { + free(encoder->protected_->metadata); + encoder->protected_->metadata = 0; + encoder->protected_->num_metadata_blocks = 0; + } + if(num_blocks) { + FLAC__StreamMetadata **m; + if(0 == (m = (FLAC__StreamMetadata**)malloc(sizeof(m[0]) * num_blocks))) + return false; + memcpy(m, metadata, sizeof(m[0]) * num_blocks); + encoder->protected_->metadata = m; + encoder->protected_->num_metadata_blocks = num_blocks; + } +#if FLAC__HAS_OGG + if(!FLAC__ogg_encoder_aspect_set_num_metadata(&encoder->protected_->ogg_encoder_aspect, num_blocks)) + return false; +#endif + return true; +} + +FLAC_API FLAC__StreamEncoderState FLAC__stream_encoder_get_state(const FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + return encoder->protected_->state; +} + +FLAC_API FLAC__StreamDecoderState FLAC__stream_encoder_get_verify_decoder_state(const FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->verify) + return FLAC__stream_decoder_get_state(encoder->private_->verify.decoder); + else + return FLAC__STREAM_DECODER_UNINITIALIZED; +} + +FLAC_API const char *FLAC__stream_encoder_get_resolved_state_string(const FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR) + return FLAC__StreamEncoderStateString[encoder->protected_->state]; + else + return FLAC__stream_decoder_get_resolved_state_string(encoder->private_->verify.decoder); +} + +FLAC_API void FLAC__stream_encoder_get_verify_decoder_error_stats(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_sample, unsigned *frame_number, unsigned *channel, unsigned *sample, FLAC__int32 *expected, FLAC__int32 *got) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(0 != absolute_sample) + *absolute_sample = encoder->private_->verify.error_stats.absolute_sample; + if(0 != frame_number) + *frame_number = encoder->private_->verify.error_stats.frame_number; + if(0 != channel) + *channel = encoder->private_->verify.error_stats.channel; + if(0 != sample) + *sample = encoder->private_->verify.error_stats.sample; + if(0 != expected) + *expected = encoder->private_->verify.error_stats.expected; + if(0 != got) + *got = encoder->private_->verify.error_stats.got; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_get_verify(const FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + return encoder->protected_->verify; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_get_streamable_subset(const FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + return encoder->protected_->streamable_subset; +} + +FLAC_API unsigned FLAC__stream_encoder_get_channels(const FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + return encoder->protected_->channels; +} + +FLAC_API unsigned FLAC__stream_encoder_get_bits_per_sample(const FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + return encoder->protected_->bits_per_sample; +} + +FLAC_API unsigned FLAC__stream_encoder_get_sample_rate(const FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + return encoder->protected_->sample_rate; +} + +FLAC_API unsigned FLAC__stream_encoder_get_blocksize(const FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + return encoder->protected_->blocksize; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_get_do_mid_side_stereo(const FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + return encoder->protected_->do_mid_side_stereo; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_get_loose_mid_side_stereo(const FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + return encoder->protected_->loose_mid_side_stereo; +} + +FLAC_API unsigned FLAC__stream_encoder_get_max_lpc_order(const FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + return encoder->protected_->max_lpc_order; +} + +FLAC_API unsigned FLAC__stream_encoder_get_qlp_coeff_precision(const FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + return encoder->protected_->qlp_coeff_precision; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_get_do_qlp_coeff_prec_search(const FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + return encoder->protected_->do_qlp_coeff_prec_search; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_get_do_escape_coding(const FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + return encoder->protected_->do_escape_coding; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_get_do_exhaustive_model_search(const FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + return encoder->protected_->do_exhaustive_model_search; +} + +FLAC_API unsigned FLAC__stream_encoder_get_min_residual_partition_order(const FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + return encoder->protected_->min_residual_partition_order; +} + +FLAC_API unsigned FLAC__stream_encoder_get_max_residual_partition_order(const FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + return encoder->protected_->max_residual_partition_order; +} + +FLAC_API unsigned FLAC__stream_encoder_get_rice_parameter_search_dist(const FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + return encoder->protected_->rice_parameter_search_dist; +} + +FLAC_API FLAC__uint64 FLAC__stream_encoder_get_total_samples_estimate(const FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + return encoder->protected_->total_samples_estimate; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_process(FLAC__StreamEncoder *encoder, const FLAC__int32 * const buffer[], unsigned samples) +{ + unsigned i, j = 0, channel; + const unsigned channels = encoder->protected_->channels, blocksize = encoder->protected_->blocksize; + + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + FLAC__ASSERT(encoder->protected_->state == FLAC__STREAM_ENCODER_OK); + + do { + const unsigned n = min(blocksize+OVERREAD_-encoder->private_->current_sample_number, samples-j); + + if(encoder->protected_->verify) + append_to_verify_fifo_(&encoder->private_->verify.input_fifo, buffer, j, channels, n); + + for(channel = 0; channel < channels; channel++) + memcpy(&encoder->private_->integer_signal[channel][encoder->private_->current_sample_number], &buffer[channel][j], sizeof(buffer[channel][0]) * n); + + if(encoder->protected_->do_mid_side_stereo) { + FLAC__ASSERT(channels == 2); + /* "i <= blocksize" to overread 1 sample; see comment in OVERREAD_ decl */ + for(i = encoder->private_->current_sample_number; i <= blocksize && j < samples; i++, j++) { + encoder->private_->integer_signal_mid_side[1][i] = buffer[0][j] - buffer[1][j]; + encoder->private_->integer_signal_mid_side[0][i] = (buffer[0][j] + buffer[1][j]) >> 1; /* NOTE: not the same as 'mid = (buffer[0][j] + buffer[1][j]) / 2' ! */ + } + } + else + j += n; + + encoder->private_->current_sample_number += n; + + /* we only process if we have a full block + 1 extra sample; final block is always handled by FLAC__stream_encoder_finish() */ + if(encoder->private_->current_sample_number > blocksize) { + FLAC__ASSERT(encoder->private_->current_sample_number == blocksize+OVERREAD_); + FLAC__ASSERT(OVERREAD_ == 1); /* assert we only overread 1 sample which simplifies the rest of the code below */ + if(!process_frame_(encoder, /*is_fractional_block=*/false, /*is_last_block=*/false)) + return false; + /* move unprocessed overread samples to beginnings of arrays */ + for(channel = 0; channel < channels; channel++) + encoder->private_->integer_signal[channel][0] = encoder->private_->integer_signal[channel][blocksize]; + if(encoder->protected_->do_mid_side_stereo) { + encoder->private_->integer_signal_mid_side[0][0] = encoder->private_->integer_signal_mid_side[0][blocksize]; + encoder->private_->integer_signal_mid_side[1][0] = encoder->private_->integer_signal_mid_side[1][blocksize]; + } + encoder->private_->current_sample_number = 1; + } + } while(j < samples); + + return true; +} + +FLAC_API FLAC__bool FLAC__stream_encoder_process_interleaved(FLAC__StreamEncoder *encoder, const FLAC__int32 buffer[], unsigned samples) +{ + unsigned i, j, k, channel; + FLAC__int32 x, mid, side; + const unsigned channels = encoder->protected_->channels, blocksize = encoder->protected_->blocksize; + + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + FLAC__ASSERT(encoder->protected_->state == FLAC__STREAM_ENCODER_OK); + + j = k = 0; + /* + * we have several flavors of the same basic loop, optimized for + * different conditions: + */ + if(encoder->protected_->do_mid_side_stereo && channels == 2) { + /* + * stereo coding: unroll channel loop + */ + do { + if(encoder->protected_->verify) + append_to_verify_fifo_interleaved_(&encoder->private_->verify.input_fifo, buffer, j, channels, min(blocksize+OVERREAD_-encoder->private_->current_sample_number, samples-j)); + + /* "i <= blocksize" to overread 1 sample; see comment in OVERREAD_ decl */ + for(i = encoder->private_->current_sample_number; i <= blocksize && j < samples; i++, j++) { + encoder->private_->integer_signal[0][i] = mid = side = buffer[k++]; + x = buffer[k++]; + encoder->private_->integer_signal[1][i] = x; + mid += x; + side -= x; + mid >>= 1; /* NOTE: not the same as 'mid = (left + right) / 2' ! */ + encoder->private_->integer_signal_mid_side[1][i] = side; + encoder->private_->integer_signal_mid_side[0][i] = mid; + } + encoder->private_->current_sample_number = i; + /* we only process if we have a full block + 1 extra sample; final block is always handled by FLAC__stream_encoder_finish() */ + if(i > blocksize) { + if(!process_frame_(encoder, /*is_fractional_block=*/false, /*is_last_block=*/false)) + return false; + /* move unprocessed overread samples to beginnings of arrays */ + FLAC__ASSERT(i == blocksize+OVERREAD_); + FLAC__ASSERT(OVERREAD_ == 1); /* assert we only overread 1 sample which simplifies the rest of the code below */ + encoder->private_->integer_signal[0][0] = encoder->private_->integer_signal[0][blocksize]; + encoder->private_->integer_signal[1][0] = encoder->private_->integer_signal[1][blocksize]; + encoder->private_->integer_signal_mid_side[0][0] = encoder->private_->integer_signal_mid_side[0][blocksize]; + encoder->private_->integer_signal_mid_side[1][0] = encoder->private_->integer_signal_mid_side[1][blocksize]; + encoder->private_->current_sample_number = 1; + } + } while(j < samples); + } + else { + /* + * independent channel coding: buffer each channel in inner loop + */ + do { + if(encoder->protected_->verify) + append_to_verify_fifo_interleaved_(&encoder->private_->verify.input_fifo, buffer, j, channels, min(blocksize+OVERREAD_-encoder->private_->current_sample_number, samples-j)); + + /* "i <= blocksize" to overread 1 sample; see comment in OVERREAD_ decl */ + for(i = encoder->private_->current_sample_number; i <= blocksize && j < samples; i++, j++) { + for(channel = 0; channel < channels; channel++) + encoder->private_->integer_signal[channel][i] = buffer[k++]; + } + encoder->private_->current_sample_number = i; + /* we only process if we have a full block + 1 extra sample; final block is always handled by FLAC__stream_encoder_finish() */ + if(i > blocksize) { + if(!process_frame_(encoder, /*is_fractional_block=*/false, /*is_last_block=*/false)) + return false; + /* move unprocessed overread samples to beginnings of arrays */ + FLAC__ASSERT(i == blocksize+OVERREAD_); + FLAC__ASSERT(OVERREAD_ == 1); /* assert we only overread 1 sample which simplifies the rest of the code below */ + for(channel = 0; channel < channels; channel++) + encoder->private_->integer_signal[channel][0] = encoder->private_->integer_signal[channel][blocksize]; + encoder->private_->current_sample_number = 1; + } + } while(j < samples); + } + + return true; +} + +/*********************************************************************** + * + * Private class methods + * + ***********************************************************************/ + +void set_defaults_(FLAC__StreamEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + +#ifdef FLAC__MANDATORY_VERIFY_WHILE_ENCODING + encoder->protected_->verify = true; +#else + encoder->protected_->verify = false; +#endif + encoder->protected_->streamable_subset = true; + encoder->protected_->do_mid_side_stereo = false; + encoder->protected_->loose_mid_side_stereo = false; + encoder->protected_->channels = 2; + encoder->protected_->bits_per_sample = 16; + encoder->protected_->sample_rate = 44100; + encoder->protected_->blocksize = 0; +#ifndef FLAC__INTEGER_ONLY_LIBRARY + encoder->protected_->num_apodizations = 1; + encoder->protected_->apodizations[0].type = FLAC__APODIZATION_TUKEY; + encoder->protected_->apodizations[0].parameters.tukey.p = 0.5; +#endif + encoder->protected_->max_lpc_order = 0; + encoder->protected_->qlp_coeff_precision = 0; + encoder->protected_->do_qlp_coeff_prec_search = false; + encoder->protected_->do_exhaustive_model_search = false; + encoder->protected_->do_escape_coding = false; + encoder->protected_->min_residual_partition_order = 0; + encoder->protected_->max_residual_partition_order = 0; + encoder->protected_->rice_parameter_search_dist = 0; + encoder->protected_->total_samples_estimate = 0; + encoder->protected_->metadata = 0; + encoder->protected_->num_metadata_blocks = 0; + + encoder->private_->seek_table = 0; + encoder->private_->disable_constant_subframes = false; + encoder->private_->disable_fixed_subframes = false; + encoder->private_->disable_verbatim_subframes = false; +#if FLAC__HAS_OGG + encoder->private_->is_ogg = false; +#endif + encoder->private_->read_callback = 0; + encoder->private_->write_callback = 0; + encoder->private_->seek_callback = 0; + encoder->private_->tell_callback = 0; + encoder->private_->metadata_callback = 0; + encoder->private_->progress_callback = 0; + encoder->private_->client_data = 0; + +#if FLAC__HAS_OGG + FLAC__ogg_encoder_aspect_set_defaults(&encoder->protected_->ogg_encoder_aspect); +#endif +} + +void free_(FLAC__StreamEncoder *encoder) +{ + unsigned i, channel; + + FLAC__ASSERT(0 != encoder); + if(encoder->protected_->metadata) { + free(encoder->protected_->metadata); + encoder->protected_->metadata = 0; + encoder->protected_->num_metadata_blocks = 0; + } + for(i = 0; i < encoder->protected_->channels; i++) { + if(0 != encoder->private_->integer_signal_unaligned[i]) { + free(encoder->private_->integer_signal_unaligned[i]); + encoder->private_->integer_signal_unaligned[i] = 0; + } +#ifndef FLAC__INTEGER_ONLY_LIBRARY + if(0 != encoder->private_->real_signal_unaligned[i]) { + free(encoder->private_->real_signal_unaligned[i]); + encoder->private_->real_signal_unaligned[i] = 0; + } +#endif + } + for(i = 0; i < 2; i++) { + if(0 != encoder->private_->integer_signal_mid_side_unaligned[i]) { + free(encoder->private_->integer_signal_mid_side_unaligned[i]); + encoder->private_->integer_signal_mid_side_unaligned[i] = 0; + } +#ifndef FLAC__INTEGER_ONLY_LIBRARY + if(0 != encoder->private_->real_signal_mid_side_unaligned[i]) { + free(encoder->private_->real_signal_mid_side_unaligned[i]); + encoder->private_->real_signal_mid_side_unaligned[i] = 0; + } +#endif + } +#ifndef FLAC__INTEGER_ONLY_LIBRARY + for(i = 0; i < encoder->protected_->num_apodizations; i++) { + if(0 != encoder->private_->window_unaligned[i]) { + free(encoder->private_->window_unaligned[i]); + encoder->private_->window_unaligned[i] = 0; + } + } + if(0 != encoder->private_->windowed_signal_unaligned) { + free(encoder->private_->windowed_signal_unaligned); + encoder->private_->windowed_signal_unaligned = 0; + } +#endif + for(channel = 0; channel < encoder->protected_->channels; channel++) { + for(i = 0; i < 2; i++) { + if(0 != encoder->private_->residual_workspace_unaligned[channel][i]) { + free(encoder->private_->residual_workspace_unaligned[channel][i]); + encoder->private_->residual_workspace_unaligned[channel][i] = 0; + } + } + } + for(channel = 0; channel < 2; channel++) { + for(i = 0; i < 2; i++) { + if(0 != encoder->private_->residual_workspace_mid_side_unaligned[channel][i]) { + free(encoder->private_->residual_workspace_mid_side_unaligned[channel][i]); + encoder->private_->residual_workspace_mid_side_unaligned[channel][i] = 0; + } + } + } + if(0 != encoder->private_->abs_residual_partition_sums_unaligned) { + free(encoder->private_->abs_residual_partition_sums_unaligned); + encoder->private_->abs_residual_partition_sums_unaligned = 0; + } + if(0 != encoder->private_->raw_bits_per_partition_unaligned) { + free(encoder->private_->raw_bits_per_partition_unaligned); + encoder->private_->raw_bits_per_partition_unaligned = 0; + } + if(encoder->protected_->verify) { + for(i = 0; i < encoder->protected_->channels; i++) { + if(0 != encoder->private_->verify.input_fifo.data[i]) { + free(encoder->private_->verify.input_fifo.data[i]); + encoder->private_->verify.input_fifo.data[i] = 0; + } + } + } + FLAC__bitwriter_free(encoder->private_->frame); +} + +FLAC__bool resize_buffers_(FLAC__StreamEncoder *encoder, unsigned new_blocksize) +{ + FLAC__bool ok; + unsigned i, channel; + + FLAC__ASSERT(new_blocksize > 0); + FLAC__ASSERT(encoder->protected_->state == FLAC__STREAM_ENCODER_OK); + FLAC__ASSERT(encoder->private_->current_sample_number == 0); + + /* To avoid excessive malloc'ing, we only grow the buffer; no shrinking. */ + if(new_blocksize <= encoder->private_->input_capacity) + return true; + + ok = true; + + /* WATCHOUT: FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32_mmx() + * requires that the input arrays (in our case the integer signals) + * have a buffer of up to 3 zeroes in front (at negative indices) for + * alignment purposes; we use 4 in front to keep the data well-aligned. + */ + + for(i = 0; ok && i < encoder->protected_->channels; i++) { + ok = ok && FLAC__memory_alloc_aligned_int32_array(new_blocksize+4+OVERREAD_, &encoder->private_->integer_signal_unaligned[i], &encoder->private_->integer_signal[i]); + memset(encoder->private_->integer_signal[i], 0, sizeof(FLAC__int32)*4); + encoder->private_->integer_signal[i] += 4; +#ifndef FLAC__INTEGER_ONLY_LIBRARY +#if 0 /* @@@ currently unused */ + if(encoder->protected_->max_lpc_order > 0) + ok = ok && FLAC__memory_alloc_aligned_real_array(new_blocksize+OVERREAD_, &encoder->private_->real_signal_unaligned[i], &encoder->private_->real_signal[i]); +#endif +#endif + } + for(i = 0; ok && i < 2; i++) { + ok = ok && FLAC__memory_alloc_aligned_int32_array(new_blocksize+4+OVERREAD_, &encoder->private_->integer_signal_mid_side_unaligned[i], &encoder->private_->integer_signal_mid_side[i]); + memset(encoder->private_->integer_signal_mid_side[i], 0, sizeof(FLAC__int32)*4); + encoder->private_->integer_signal_mid_side[i] += 4; +#ifndef FLAC__INTEGER_ONLY_LIBRARY +#if 0 /* @@@ currently unused */ + if(encoder->protected_->max_lpc_order > 0) + ok = ok && FLAC__memory_alloc_aligned_real_array(new_blocksize+OVERREAD_, &encoder->private_->real_signal_mid_side_unaligned[i], &encoder->private_->real_signal_mid_side[i]); +#endif +#endif + } +#ifndef FLAC__INTEGER_ONLY_LIBRARY + if(ok && encoder->protected_->max_lpc_order > 0) { + for(i = 0; ok && i < encoder->protected_->num_apodizations; i++) + ok = ok && FLAC__memory_alloc_aligned_real_array(new_blocksize, &encoder->private_->window_unaligned[i], &encoder->private_->window[i]); + ok = ok && FLAC__memory_alloc_aligned_real_array(new_blocksize, &encoder->private_->windowed_signal_unaligned, &encoder->private_->windowed_signal); + } +#endif + for(channel = 0; ok && channel < encoder->protected_->channels; channel++) { + for(i = 0; ok && i < 2; i++) { + ok = ok && FLAC__memory_alloc_aligned_int32_array(new_blocksize, &encoder->private_->residual_workspace_unaligned[channel][i], &encoder->private_->residual_workspace[channel][i]); + } + } + for(channel = 0; ok && channel < 2; channel++) { + for(i = 0; ok && i < 2; i++) { + ok = ok && FLAC__memory_alloc_aligned_int32_array(new_blocksize, &encoder->private_->residual_workspace_mid_side_unaligned[channel][i], &encoder->private_->residual_workspace_mid_side[channel][i]); + } + } + /* the *2 is an approximation to the series 1 + 1/2 + 1/4 + ... that sums tree occupies in a flat array */ + /*@@@ new_blocksize*2 is too pessimistic, but to fix, we need smarter logic because a smaller new_blocksize can actually increase the # of partitions; would require moving this out into a separate function, then checking its capacity against the need of the current blocksize&min/max_partition_order (and maybe predictor order) */ + ok = ok && FLAC__memory_alloc_aligned_uint64_array(new_blocksize * 2, &encoder->private_->abs_residual_partition_sums_unaligned, &encoder->private_->abs_residual_partition_sums); + if(encoder->protected_->do_escape_coding) + ok = ok && FLAC__memory_alloc_aligned_unsigned_array(new_blocksize * 2, &encoder->private_->raw_bits_per_partition_unaligned, &encoder->private_->raw_bits_per_partition); + + /* now adjust the windows if the blocksize has changed */ +#ifndef FLAC__INTEGER_ONLY_LIBRARY + if(ok && new_blocksize != encoder->private_->input_capacity && encoder->protected_->max_lpc_order > 0) { + for(i = 0; ok && i < encoder->protected_->num_apodizations; i++) { + switch(encoder->protected_->apodizations[i].type) { + case FLAC__APODIZATION_BARTLETT: + FLAC__window_bartlett(encoder->private_->window[i], new_blocksize); + break; + case FLAC__APODIZATION_BARTLETT_HANN: + FLAC__window_bartlett_hann(encoder->private_->window[i], new_blocksize); + break; + case FLAC__APODIZATION_BLACKMAN: + FLAC__window_blackman(encoder->private_->window[i], new_blocksize); + break; + case FLAC__APODIZATION_BLACKMAN_HARRIS_4TERM_92DB_SIDELOBE: + FLAC__window_blackman_harris_4term_92db_sidelobe(encoder->private_->window[i], new_blocksize); + break; + case FLAC__APODIZATION_CONNES: + FLAC__window_connes(encoder->private_->window[i], new_blocksize); + break; + case FLAC__APODIZATION_FLATTOP: + FLAC__window_flattop(encoder->private_->window[i], new_blocksize); + break; + case FLAC__APODIZATION_GAUSS: + FLAC__window_gauss(encoder->private_->window[i], new_blocksize, encoder->protected_->apodizations[i].parameters.gauss.stddev); + break; + case FLAC__APODIZATION_HAMMING: + FLAC__window_hamming(encoder->private_->window[i], new_blocksize); + break; + case FLAC__APODIZATION_HANN: + FLAC__window_hann(encoder->private_->window[i], new_blocksize); + break; + case FLAC__APODIZATION_KAISER_BESSEL: + FLAC__window_kaiser_bessel(encoder->private_->window[i], new_blocksize); + break; + case FLAC__APODIZATION_NUTTALL: + FLAC__window_nuttall(encoder->private_->window[i], new_blocksize); + break; + case FLAC__APODIZATION_RECTANGLE: + FLAC__window_rectangle(encoder->private_->window[i], new_blocksize); + break; + case FLAC__APODIZATION_TRIANGLE: + FLAC__window_triangle(encoder->private_->window[i], new_blocksize); + break; + case FLAC__APODIZATION_TUKEY: + FLAC__window_tukey(encoder->private_->window[i], new_blocksize, encoder->protected_->apodizations[i].parameters.tukey.p); + break; + case FLAC__APODIZATION_WELCH: + FLAC__window_welch(encoder->private_->window[i], new_blocksize); + break; + default: + FLAC__ASSERT(0); + /* double protection */ + FLAC__window_hann(encoder->private_->window[i], new_blocksize); + break; + } + } + } +#endif + + if(ok) + encoder->private_->input_capacity = new_blocksize; + else + encoder->protected_->state = FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR; + + return ok; +} + +FLAC__bool write_bitbuffer_(FLAC__StreamEncoder *encoder, unsigned samples, FLAC__bool is_last_block) +{ + const FLAC__byte *buffer; + size_t bytes; + + FLAC__ASSERT(FLAC__bitwriter_is_byte_aligned(encoder->private_->frame)); + + if(!FLAC__bitwriter_get_buffer(encoder->private_->frame, &buffer, &bytes)) { + encoder->protected_->state = FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR; + return false; + } + + if(encoder->protected_->verify) { + encoder->private_->verify.output.data = buffer; + encoder->private_->verify.output.bytes = bytes; + if(encoder->private_->verify.state_hint == ENCODER_IN_MAGIC) { + encoder->private_->verify.needs_magic_hack = true; + } + else { + if(!FLAC__stream_decoder_process_single(encoder->private_->verify.decoder)) { + FLAC__bitwriter_release_buffer(encoder->private_->frame); + FLAC__bitwriter_clear(encoder->private_->frame); + if(encoder->protected_->state != FLAC__STREAM_ENCODER_VERIFY_MISMATCH_IN_AUDIO_DATA) + encoder->protected_->state = FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR; + return false; + } + } + } + + if(write_frame_(encoder, buffer, bytes, samples, is_last_block) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK) { + FLAC__bitwriter_release_buffer(encoder->private_->frame); + FLAC__bitwriter_clear(encoder->private_->frame); + encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR; + return false; + } + + FLAC__bitwriter_release_buffer(encoder->private_->frame); + FLAC__bitwriter_clear(encoder->private_->frame); + + if(samples > 0) { + encoder->private_->streaminfo.data.stream_info.min_framesize = min(bytes, encoder->private_->streaminfo.data.stream_info.min_framesize); + encoder->private_->streaminfo.data.stream_info.max_framesize = max(bytes, encoder->private_->streaminfo.data.stream_info.max_framesize); + } + + return true; +} + +FLAC__StreamEncoderWriteStatus write_frame_(FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, unsigned samples, FLAC__bool is_last_block) +{ + FLAC__StreamEncoderWriteStatus status; + FLAC__uint64 output_position = 0; + + /* FLAC__STREAM_ENCODER_TELL_STATUS_UNSUPPORTED just means we didn't get the offset; no error */ + if(encoder->private_->tell_callback && encoder->private_->tell_callback(encoder, &output_position, encoder->private_->client_data) == FLAC__STREAM_ENCODER_TELL_STATUS_ERROR) { + encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR; + return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR; + } + + /* + * Watch for the STREAMINFO block and first SEEKTABLE block to go by and store their offsets. + */ + if(samples == 0) { + FLAC__MetadataType type = (buffer[0] & 0x7f); + if(type == FLAC__METADATA_TYPE_STREAMINFO) + encoder->protected_->streaminfo_offset = output_position; + else if(type == FLAC__METADATA_TYPE_SEEKTABLE && encoder->protected_->seektable_offset == 0) + encoder->protected_->seektable_offset = output_position; + } + + /* + * Mark the current seek point if hit (if audio_offset == 0 that + * means we're still writing metadata and haven't hit the first + * frame yet) + */ + if(0 != encoder->private_->seek_table && encoder->protected_->audio_offset > 0 && encoder->private_->seek_table->num_points > 0) { + const unsigned blocksize = FLAC__stream_encoder_get_blocksize(encoder); + const FLAC__uint64 frame_first_sample = encoder->private_->samples_written; + const FLAC__uint64 frame_last_sample = frame_first_sample + (FLAC__uint64)blocksize - 1; + FLAC__uint64 test_sample; + unsigned i; + for(i = encoder->private_->first_seekpoint_to_check; i < encoder->private_->seek_table->num_points; i++) { + test_sample = encoder->private_->seek_table->points[i].sample_number; + if(test_sample > frame_last_sample) { + break; + } + else if(test_sample >= frame_first_sample) { + encoder->private_->seek_table->points[i].sample_number = frame_first_sample; + encoder->private_->seek_table->points[i].stream_offset = output_position - encoder->protected_->audio_offset; + encoder->private_->seek_table->points[i].frame_samples = blocksize; + encoder->private_->first_seekpoint_to_check++; + /* DO NOT: "break;" and here's why: + * The seektable template may contain more than one target + * sample for any given frame; we will keep looping, generating + * duplicate seekpoints for them, and we'll clean it up later, + * just before writing the seektable back to the metadata. + */ + } + else { + encoder->private_->first_seekpoint_to_check++; + } + } + } + +#if FLAC__HAS_OGG + if(encoder->private_->is_ogg) { + status = FLAC__ogg_encoder_aspect_write_callback_wrapper( + &encoder->protected_->ogg_encoder_aspect, + buffer, + bytes, + samples, + encoder->private_->current_frame_number, + is_last_block, + (FLAC__OggEncoderAspectWriteCallbackProxy)encoder->private_->write_callback, + encoder, + encoder->private_->client_data + ); + } + else +#endif + status = encoder->private_->write_callback(encoder, buffer, bytes, samples, encoder->private_->current_frame_number, encoder->private_->client_data); + + if(status == FLAC__STREAM_ENCODER_WRITE_STATUS_OK) { + encoder->private_->bytes_written += bytes; + encoder->private_->samples_written += samples; + /* we keep a high watermark on the number of frames written because + * when the encoder goes back to write metadata, 'current_frame' + * will drop back to 0. + */ + encoder->private_->frames_written = max(encoder->private_->frames_written, encoder->private_->current_frame_number+1); + } + else + encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR; + + return status; +} + +/* Gets called when the encoding process has finished so that we can update the STREAMINFO and SEEKTABLE blocks. */ +void update_metadata_(const FLAC__StreamEncoder *encoder) +{ + FLAC__byte b[max(6, FLAC__STREAM_METADATA_SEEKPOINT_LENGTH)]; + const FLAC__StreamMetadata *metadata = &encoder->private_->streaminfo; + const FLAC__uint64 samples = metadata->data.stream_info.total_samples; + const unsigned min_framesize = metadata->data.stream_info.min_framesize; + const unsigned max_framesize = metadata->data.stream_info.max_framesize; + const unsigned bps = metadata->data.stream_info.bits_per_sample; + FLAC__StreamEncoderSeekStatus seek_status; + + FLAC__ASSERT(metadata->type == FLAC__METADATA_TYPE_STREAMINFO); + + /* All this is based on intimate knowledge of the stream header + * layout, but a change to the header format that would break this + * would also break all streams encoded in the previous format. + */ + + /* + * Write MD5 signature + */ + { + const unsigned md5_offset = + FLAC__STREAM_METADATA_HEADER_LENGTH + + ( + FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN + + FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN + ) / 8; + + if((seek_status = encoder->private_->seek_callback(encoder, encoder->protected_->streaminfo_offset + md5_offset, encoder->private_->client_data)) != FLAC__STREAM_ENCODER_SEEK_STATUS_OK) { + if(seek_status == FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR) + encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR; + return; + } + if(encoder->private_->write_callback(encoder, metadata->data.stream_info.md5sum, 16, 0, 0, encoder->private_->client_data) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK) { + encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR; + return; + } + } + + /* + * Write total samples + */ + { + const unsigned total_samples_byte_offset = + FLAC__STREAM_METADATA_HEADER_LENGTH + + ( + FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN + + FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN + - 4 + ) / 8; + + b[0] = ((FLAC__byte)(bps-1) << 4) | (FLAC__byte)((samples >> 32) & 0x0F); + b[1] = (FLAC__byte)((samples >> 24) & 0xFF); + b[2] = (FLAC__byte)((samples >> 16) & 0xFF); + b[3] = (FLAC__byte)((samples >> 8) & 0xFF); + b[4] = (FLAC__byte)(samples & 0xFF); + if((seek_status = encoder->private_->seek_callback(encoder, encoder->protected_->streaminfo_offset + total_samples_byte_offset, encoder->private_->client_data)) != FLAC__STREAM_ENCODER_SEEK_STATUS_OK) { + if(seek_status == FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR) + encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR; + return; + } + if(encoder->private_->write_callback(encoder, b, 5, 0, 0, encoder->private_->client_data) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK) { + encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR; + return; + } + } + + /* + * Write min/max framesize + */ + { + const unsigned min_framesize_offset = + FLAC__STREAM_METADATA_HEADER_LENGTH + + ( + FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN + ) / 8; + + b[0] = (FLAC__byte)((min_framesize >> 16) & 0xFF); + b[1] = (FLAC__byte)((min_framesize >> 8) & 0xFF); + b[2] = (FLAC__byte)(min_framesize & 0xFF); + b[3] = (FLAC__byte)((max_framesize >> 16) & 0xFF); + b[4] = (FLAC__byte)((max_framesize >> 8) & 0xFF); + b[5] = (FLAC__byte)(max_framesize & 0xFF); + if((seek_status = encoder->private_->seek_callback(encoder, encoder->protected_->streaminfo_offset + min_framesize_offset, encoder->private_->client_data)) != FLAC__STREAM_ENCODER_SEEK_STATUS_OK) { + if(seek_status == FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR) + encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR; + return; + } + if(encoder->private_->write_callback(encoder, b, 6, 0, 0, encoder->private_->client_data) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK) { + encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR; + return; + } + } + + /* + * Write seektable + */ + if(0 != encoder->private_->seek_table && encoder->private_->seek_table->num_points > 0 && encoder->protected_->seektable_offset > 0) { + unsigned i; + + FLAC__format_seektable_sort(encoder->private_->seek_table); + + FLAC__ASSERT(FLAC__format_seektable_is_legal(encoder->private_->seek_table)); + + if((seek_status = encoder->private_->seek_callback(encoder, encoder->protected_->seektable_offset + FLAC__STREAM_METADATA_HEADER_LENGTH, encoder->private_->client_data)) != FLAC__STREAM_ENCODER_SEEK_STATUS_OK) { + if(seek_status == FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR) + encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR; + return; + } + + for(i = 0; i < encoder->private_->seek_table->num_points; i++) { + FLAC__uint64 xx; + unsigned x; + xx = encoder->private_->seek_table->points[i].sample_number; + b[7] = (FLAC__byte)xx; xx >>= 8; + b[6] = (FLAC__byte)xx; xx >>= 8; + b[5] = (FLAC__byte)xx; xx >>= 8; + b[4] = (FLAC__byte)xx; xx >>= 8; + b[3] = (FLAC__byte)xx; xx >>= 8; + b[2] = (FLAC__byte)xx; xx >>= 8; + b[1] = (FLAC__byte)xx; xx >>= 8; + b[0] = (FLAC__byte)xx; xx >>= 8; + xx = encoder->private_->seek_table->points[i].stream_offset; + b[15] = (FLAC__byte)xx; xx >>= 8; + b[14] = (FLAC__byte)xx; xx >>= 8; + b[13] = (FLAC__byte)xx; xx >>= 8; + b[12] = (FLAC__byte)xx; xx >>= 8; + b[11] = (FLAC__byte)xx; xx >>= 8; + b[10] = (FLAC__byte)xx; xx >>= 8; + b[9] = (FLAC__byte)xx; xx >>= 8; + b[8] = (FLAC__byte)xx; xx >>= 8; + x = encoder->private_->seek_table->points[i].frame_samples; + b[17] = (FLAC__byte)x; x >>= 8; + b[16] = (FLAC__byte)x; x >>= 8; + if(encoder->private_->write_callback(encoder, b, 18, 0, 0, encoder->private_->client_data) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK) { + encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR; + return; + } + } + } +} + +#if FLAC__HAS_OGG +/* Gets called when the encoding process has finished so that we can update the STREAMINFO and SEEKTABLE blocks. */ +void update_ogg_metadata_(FLAC__StreamEncoder *encoder) +{ + /* the # of bytes in the 1st packet that precede the STREAMINFO */ + static const unsigned FIRST_OGG_PACKET_STREAMINFO_PREFIX_LENGTH = + FLAC__OGG_MAPPING_PACKET_TYPE_LENGTH + + FLAC__OGG_MAPPING_MAGIC_LENGTH + + FLAC__OGG_MAPPING_VERSION_MAJOR_LENGTH + + FLAC__OGG_MAPPING_VERSION_MINOR_LENGTH + + FLAC__OGG_MAPPING_NUM_HEADERS_LENGTH + + FLAC__STREAM_SYNC_LENGTH + ; + FLAC__byte b[max(6, FLAC__STREAM_METADATA_SEEKPOINT_LENGTH)]; + const FLAC__StreamMetadata *metadata = &encoder->private_->streaminfo; + const FLAC__uint64 samples = metadata->data.stream_info.total_samples; + const unsigned min_framesize = metadata->data.stream_info.min_framesize; + const unsigned max_framesize = metadata->data.stream_info.max_framesize; + ogg_page page; + + FLAC__ASSERT(metadata->type == FLAC__METADATA_TYPE_STREAMINFO); + FLAC__ASSERT(0 != encoder->private_->seek_callback); + + /* Pre-check that client supports seeking, since we don't want the + * ogg_helper code to ever have to deal with this condition. + */ + if(encoder->private_->seek_callback(encoder, 0, encoder->private_->client_data) == FLAC__STREAM_ENCODER_SEEK_STATUS_UNSUPPORTED) + return; + + /* All this is based on intimate knowledge of the stream header + * layout, but a change to the header format that would break this + * would also break all streams encoded in the previous format. + */ + + /** + ** Write STREAMINFO stats + **/ + simple_ogg_page__init(&page); + if(!simple_ogg_page__get_at(encoder, encoder->protected_->streaminfo_offset, &page, encoder->private_->seek_callback, encoder->private_->read_callback, encoder->private_->client_data)) { + simple_ogg_page__clear(&page); + return; /* state already set */ + } + + /* + * Write MD5 signature + */ + { + const unsigned md5_offset = + FIRST_OGG_PACKET_STREAMINFO_PREFIX_LENGTH + + FLAC__STREAM_METADATA_HEADER_LENGTH + + ( + FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN + + FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN + ) / 8; + + if(md5_offset + 16 > (unsigned)page.body_len) { + encoder->protected_->state = FLAC__STREAM_ENCODER_OGG_ERROR; + simple_ogg_page__clear(&page); + return; + } + memcpy(page.body + md5_offset, metadata->data.stream_info.md5sum, 16); + } + + /* + * Write total samples + */ + { + const unsigned total_samples_byte_offset = + FIRST_OGG_PACKET_STREAMINFO_PREFIX_LENGTH + + FLAC__STREAM_METADATA_HEADER_LENGTH + + ( + FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN + + FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN + - 4 + ) / 8; + + if(total_samples_byte_offset + 5 > (unsigned)page.body_len) { + encoder->protected_->state = FLAC__STREAM_ENCODER_OGG_ERROR; + simple_ogg_page__clear(&page); + return; + } + b[0] = (FLAC__byte)page.body[total_samples_byte_offset] & 0xF0; + b[0] |= (FLAC__byte)((samples >> 32) & 0x0F); + b[1] = (FLAC__byte)((samples >> 24) & 0xFF); + b[2] = (FLAC__byte)((samples >> 16) & 0xFF); + b[3] = (FLAC__byte)((samples >> 8) & 0xFF); + b[4] = (FLAC__byte)(samples & 0xFF); + memcpy(page.body + total_samples_byte_offset, b, 5); + } + + /* + * Write min/max framesize + */ + { + const unsigned min_framesize_offset = + FIRST_OGG_PACKET_STREAMINFO_PREFIX_LENGTH + + FLAC__STREAM_METADATA_HEADER_LENGTH + + ( + FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN + + FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN + ) / 8; + + if(min_framesize_offset + 6 > (unsigned)page.body_len) { + encoder->protected_->state = FLAC__STREAM_ENCODER_OGG_ERROR; + simple_ogg_page__clear(&page); + return; + } + b[0] = (FLAC__byte)((min_framesize >> 16) & 0xFF); + b[1] = (FLAC__byte)((min_framesize >> 8) & 0xFF); + b[2] = (FLAC__byte)(min_framesize & 0xFF); + b[3] = (FLAC__byte)((max_framesize >> 16) & 0xFF); + b[4] = (FLAC__byte)((max_framesize >> 8) & 0xFF); + b[5] = (FLAC__byte)(max_framesize & 0xFF); + memcpy(page.body + min_framesize_offset, b, 6); + } + if(!simple_ogg_page__set_at(encoder, encoder->protected_->streaminfo_offset, &page, encoder->private_->seek_callback, encoder->private_->write_callback, encoder->private_->client_data)) { + simple_ogg_page__clear(&page); + return; /* state already set */ + } + simple_ogg_page__clear(&page); + + /* + * Write seektable + */ + if(0 != encoder->private_->seek_table && encoder->private_->seek_table->num_points > 0 && encoder->protected_->seektable_offset > 0) { + unsigned i; + FLAC__byte *p; + + FLAC__format_seektable_sort(encoder->private_->seek_table); + + FLAC__ASSERT(FLAC__format_seektable_is_legal(encoder->private_->seek_table)); + + simple_ogg_page__init(&page); + if(!simple_ogg_page__get_at(encoder, encoder->protected_->seektable_offset, &page, encoder->private_->seek_callback, encoder->private_->read_callback, encoder->private_->client_data)) { + simple_ogg_page__clear(&page); + return; /* state already set */ + } + + if((FLAC__STREAM_METADATA_HEADER_LENGTH + 18*encoder->private_->seek_table->num_points) != (unsigned)page.body_len) { + encoder->protected_->state = FLAC__STREAM_ENCODER_OGG_ERROR; + simple_ogg_page__clear(&page); + return; + } + + for(i = 0, p = page.body + FLAC__STREAM_METADATA_HEADER_LENGTH; i < encoder->private_->seek_table->num_points; i++, p += 18) { + FLAC__uint64 xx; + unsigned x; + xx = encoder->private_->seek_table->points[i].sample_number; + b[7] = (FLAC__byte)xx; xx >>= 8; + b[6] = (FLAC__byte)xx; xx >>= 8; + b[5] = (FLAC__byte)xx; xx >>= 8; + b[4] = (FLAC__byte)xx; xx >>= 8; + b[3] = (FLAC__byte)xx; xx >>= 8; + b[2] = (FLAC__byte)xx; xx >>= 8; + b[1] = (FLAC__byte)xx; xx >>= 8; + b[0] = (FLAC__byte)xx; xx >>= 8; + xx = encoder->private_->seek_table->points[i].stream_offset; + b[15] = (FLAC__byte)xx; xx >>= 8; + b[14] = (FLAC__byte)xx; xx >>= 8; + b[13] = (FLAC__byte)xx; xx >>= 8; + b[12] = (FLAC__byte)xx; xx >>= 8; + b[11] = (FLAC__byte)xx; xx >>= 8; + b[10] = (FLAC__byte)xx; xx >>= 8; + b[9] = (FLAC__byte)xx; xx >>= 8; + b[8] = (FLAC__byte)xx; xx >>= 8; + x = encoder->private_->seek_table->points[i].frame_samples; + b[17] = (FLAC__byte)x; x >>= 8; + b[16] = (FLAC__byte)x; x >>= 8; + memcpy(p, b, 18); + } + + if(!simple_ogg_page__set_at(encoder, encoder->protected_->seektable_offset, &page, encoder->private_->seek_callback, encoder->private_->write_callback, encoder->private_->client_data)) { + simple_ogg_page__clear(&page); + return; /* state already set */ + } + simple_ogg_page__clear(&page); + } +} +#endif + +FLAC__bool process_frame_(FLAC__StreamEncoder *encoder, FLAC__bool is_fractional_block, FLAC__bool is_last_block) +{ + FLAC__uint16 crc; + FLAC__ASSERT(encoder->protected_->state == FLAC__STREAM_ENCODER_OK); + + /* + * Accumulate raw signal to the MD5 signature + */ + if(!FLAC__MD5Accumulate(&encoder->private_->md5context, (const FLAC__int32 * const *)encoder->private_->integer_signal, encoder->protected_->channels, encoder->protected_->blocksize, (encoder->protected_->bits_per_sample+7) / 8)) { + encoder->protected_->state = FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR; + return false; + } + + /* + * Process the frame header and subframes into the frame bitbuffer + */ + if(!process_subframes_(encoder, is_fractional_block)) { + /* the above function sets the state for us in case of an error */ + return false; + } + + /* + * Zero-pad the frame to a byte_boundary + */ + if(!FLAC__bitwriter_zero_pad_to_byte_boundary(encoder->private_->frame)) { + encoder->protected_->state = FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR; + return false; + } + + /* + * CRC-16 the whole thing + */ + FLAC__ASSERT(FLAC__bitwriter_is_byte_aligned(encoder->private_->frame)); + if( + !FLAC__bitwriter_get_write_crc16(encoder->private_->frame, &crc) || + !FLAC__bitwriter_write_raw_uint32(encoder->private_->frame, crc, FLAC__FRAME_FOOTER_CRC_LEN) + ) { + encoder->protected_->state = FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR; + return false; + } + + /* + * Write it + */ + if(!write_bitbuffer_(encoder, encoder->protected_->blocksize, is_last_block)) { + /* the above function sets the state for us in case of an error */ + return false; + } + + /* + * Get ready for the next frame + */ + encoder->private_->current_sample_number = 0; + encoder->private_->current_frame_number++; + encoder->private_->streaminfo.data.stream_info.total_samples += (FLAC__uint64)encoder->protected_->blocksize; + + return true; +} + +FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder, FLAC__bool is_fractional_block) +{ + FLAC__FrameHeader frame_header; + unsigned channel, min_partition_order = encoder->protected_->min_residual_partition_order, max_partition_order; + FLAC__bool do_independent, do_mid_side; + + /* + * Calculate the min,max Rice partition orders + */ + if(is_fractional_block) { + max_partition_order = 0; + } + else { + max_partition_order = FLAC__format_get_max_rice_partition_order_from_blocksize(encoder->protected_->blocksize); + max_partition_order = min(max_partition_order, encoder->protected_->max_residual_partition_order); + } + min_partition_order = min(min_partition_order, max_partition_order); + + /* + * Setup the frame + */ + frame_header.blocksize = encoder->protected_->blocksize; + frame_header.sample_rate = encoder->protected_->sample_rate; + frame_header.channels = encoder->protected_->channels; + frame_header.channel_assignment = FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT; /* the default unless the encoder determines otherwise */ + frame_header.bits_per_sample = encoder->protected_->bits_per_sample; + frame_header.number_type = FLAC__FRAME_NUMBER_TYPE_FRAME_NUMBER; + frame_header.number.frame_number = encoder->private_->current_frame_number; + + /* + * Figure out what channel assignments to try + */ + if(encoder->protected_->do_mid_side_stereo) { + if(encoder->protected_->loose_mid_side_stereo) { + if(encoder->private_->loose_mid_side_stereo_frame_count == 0) { + do_independent = true; + do_mid_side = true; + } + else { + do_independent = (encoder->private_->last_channel_assignment == FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT); + do_mid_side = !do_independent; + } + } + else { + do_independent = true; + do_mid_side = true; + } + } + else { + do_independent = true; + do_mid_side = false; + } + + FLAC__ASSERT(do_independent || do_mid_side); + + /* + * Check for wasted bits; set effective bps for each subframe + */ + if(do_independent) { + for(channel = 0; channel < encoder->protected_->channels; channel++) { + const unsigned w = get_wasted_bits_(encoder->private_->integer_signal[channel], encoder->protected_->blocksize); + encoder->private_->subframe_workspace[channel][0].wasted_bits = encoder->private_->subframe_workspace[channel][1].wasted_bits = w; + encoder->private_->subframe_bps[channel] = encoder->protected_->bits_per_sample - w; + } + } + if(do_mid_side) { + FLAC__ASSERT(encoder->protected_->channels == 2); + for(channel = 0; channel < 2; channel++) { + const unsigned w = get_wasted_bits_(encoder->private_->integer_signal_mid_side[channel], encoder->protected_->blocksize); + encoder->private_->subframe_workspace_mid_side[channel][0].wasted_bits = encoder->private_->subframe_workspace_mid_side[channel][1].wasted_bits = w; + encoder->private_->subframe_bps_mid_side[channel] = encoder->protected_->bits_per_sample - w + (channel==0? 0:1); + } + } + + /* + * First do a normal encoding pass of each independent channel + */ + if(do_independent) { + for(channel = 0; channel < encoder->protected_->channels; channel++) { + if(! + process_subframe_( + encoder, + min_partition_order, + max_partition_order, + &frame_header, + encoder->private_->subframe_bps[channel], + encoder->private_->integer_signal[channel], + encoder->private_->subframe_workspace_ptr[channel], + encoder->private_->partitioned_rice_contents_workspace_ptr[channel], + encoder->private_->residual_workspace[channel], + encoder->private_->best_subframe+channel, + encoder->private_->best_subframe_bits+channel + ) + ) + return false; + } + } + + /* + * Now do mid and side channels if requested + */ + if(do_mid_side) { + FLAC__ASSERT(encoder->protected_->channels == 2); + + for(channel = 0; channel < 2; channel++) { + if(! + process_subframe_( + encoder, + min_partition_order, + max_partition_order, + &frame_header, + encoder->private_->subframe_bps_mid_side[channel], + encoder->private_->integer_signal_mid_side[channel], + encoder->private_->subframe_workspace_ptr_mid_side[channel], + encoder->private_->partitioned_rice_contents_workspace_ptr_mid_side[channel], + encoder->private_->residual_workspace_mid_side[channel], + encoder->private_->best_subframe_mid_side+channel, + encoder->private_->best_subframe_bits_mid_side+channel + ) + ) + return false; + } + } + + /* + * Compose the frame bitbuffer + */ + if(do_mid_side) { + unsigned left_bps = 0, right_bps = 0; /* initialized only to prevent superfluous compiler warning */ + FLAC__Subframe *left_subframe = 0, *right_subframe = 0; /* initialized only to prevent superfluous compiler warning */ + FLAC__ChannelAssignment channel_assignment; + + FLAC__ASSERT(encoder->protected_->channels == 2); + + if(encoder->protected_->loose_mid_side_stereo && encoder->private_->loose_mid_side_stereo_frame_count > 0) { + channel_assignment = (encoder->private_->last_channel_assignment == FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT? FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT : FLAC__CHANNEL_ASSIGNMENT_MID_SIDE); + } + else { + unsigned bits[4]; /* WATCHOUT - indexed by FLAC__ChannelAssignment */ + unsigned min_bits; + int ca; + + FLAC__ASSERT(FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT == 0); + FLAC__ASSERT(FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE == 1); + FLAC__ASSERT(FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE == 2); + FLAC__ASSERT(FLAC__CHANNEL_ASSIGNMENT_MID_SIDE == 3); + FLAC__ASSERT(do_independent && do_mid_side); + + /* We have to figure out which channel assignent results in the smallest frame */ + bits[FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT] = encoder->private_->best_subframe_bits [0] + encoder->private_->best_subframe_bits [1]; + bits[FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE ] = encoder->private_->best_subframe_bits [0] + encoder->private_->best_subframe_bits_mid_side[1]; + bits[FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE ] = encoder->private_->best_subframe_bits [1] + encoder->private_->best_subframe_bits_mid_side[1]; + bits[FLAC__CHANNEL_ASSIGNMENT_MID_SIDE ] = encoder->private_->best_subframe_bits_mid_side[0] + encoder->private_->best_subframe_bits_mid_side[1]; + + channel_assignment = FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT; + min_bits = bits[channel_assignment]; + for(ca = 1; ca <= 3; ca++) { + if(bits[ca] < min_bits) { + min_bits = bits[ca]; + channel_assignment = (FLAC__ChannelAssignment)ca; + } + } + } + + frame_header.channel_assignment = channel_assignment; + + if(!FLAC__frame_add_header(&frame_header, encoder->private_->frame)) { + encoder->protected_->state = FLAC__STREAM_ENCODER_FRAMING_ERROR; + return false; + } + + switch(channel_assignment) { + case FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT: + left_subframe = &encoder->private_->subframe_workspace [0][encoder->private_->best_subframe [0]]; + right_subframe = &encoder->private_->subframe_workspace [1][encoder->private_->best_subframe [1]]; + break; + case FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE: + left_subframe = &encoder->private_->subframe_workspace [0][encoder->private_->best_subframe [0]]; + right_subframe = &encoder->private_->subframe_workspace_mid_side[1][encoder->private_->best_subframe_mid_side[1]]; + break; + case FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE: + left_subframe = &encoder->private_->subframe_workspace_mid_side[1][encoder->private_->best_subframe_mid_side[1]]; + right_subframe = &encoder->private_->subframe_workspace [1][encoder->private_->best_subframe [1]]; + break; + case FLAC__CHANNEL_ASSIGNMENT_MID_SIDE: + left_subframe = &encoder->private_->subframe_workspace_mid_side[0][encoder->private_->best_subframe_mid_side[0]]; + right_subframe = &encoder->private_->subframe_workspace_mid_side[1][encoder->private_->best_subframe_mid_side[1]]; + break; + default: + FLAC__ASSERT(0); + } + + switch(channel_assignment) { + case FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT: + left_bps = encoder->private_->subframe_bps [0]; + right_bps = encoder->private_->subframe_bps [1]; + break; + case FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE: + left_bps = encoder->private_->subframe_bps [0]; + right_bps = encoder->private_->subframe_bps_mid_side[1]; + break; + case FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE: + left_bps = encoder->private_->subframe_bps_mid_side[1]; + right_bps = encoder->private_->subframe_bps [1]; + break; + case FLAC__CHANNEL_ASSIGNMENT_MID_SIDE: + left_bps = encoder->private_->subframe_bps_mid_side[0]; + right_bps = encoder->private_->subframe_bps_mid_side[1]; + break; + default: + FLAC__ASSERT(0); + } + + /* note that encoder_add_subframe_ sets the state for us in case of an error */ + if(!add_subframe_(encoder, frame_header.blocksize, left_bps , left_subframe , encoder->private_->frame)) + return false; + if(!add_subframe_(encoder, frame_header.blocksize, right_bps, right_subframe, encoder->private_->frame)) + return false; + } + else { + if(!FLAC__frame_add_header(&frame_header, encoder->private_->frame)) { + encoder->protected_->state = FLAC__STREAM_ENCODER_FRAMING_ERROR; + return false; + } + + for(channel = 0; channel < encoder->protected_->channels; channel++) { + if(!add_subframe_(encoder, frame_header.blocksize, encoder->private_->subframe_bps[channel], &encoder->private_->subframe_workspace[channel][encoder->private_->best_subframe[channel]], encoder->private_->frame)) { + /* the above function sets the state for us in case of an error */ + return false; + } + } + } + + if(encoder->protected_->loose_mid_side_stereo) { + encoder->private_->loose_mid_side_stereo_frame_count++; + if(encoder->private_->loose_mid_side_stereo_frame_count >= encoder->private_->loose_mid_side_stereo_frames) + encoder->private_->loose_mid_side_stereo_frame_count = 0; + } + + encoder->private_->last_channel_assignment = frame_header.channel_assignment; + + return true; +} + +FLAC__bool process_subframe_( + FLAC__StreamEncoder *encoder, + unsigned min_partition_order, + unsigned max_partition_order, + const FLAC__FrameHeader *frame_header, + unsigned subframe_bps, + const FLAC__int32 integer_signal[], + FLAC__Subframe *subframe[2], + FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents[2], + FLAC__int32 *residual[2], + unsigned *best_subframe, + unsigned *best_bits +) +{ +#ifndef FLAC__INTEGER_ONLY_LIBRARY + FLAC__float fixed_residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]; +#else + FLAC__fixedpoint fixed_residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]; +#endif +#ifndef FLAC__INTEGER_ONLY_LIBRARY + FLAC__double lpc_residual_bits_per_sample; + FLAC__real autoc[FLAC__MAX_LPC_ORDER+1]; /* WATCHOUT: the size is important even though encoder->protected_->max_lpc_order might be less; some asm routines need all the space */ + FLAC__double lpc_error[FLAC__MAX_LPC_ORDER]; + unsigned min_lpc_order, max_lpc_order, lpc_order; + unsigned min_qlp_coeff_precision, max_qlp_coeff_precision, qlp_coeff_precision; +#endif + unsigned min_fixed_order, max_fixed_order, guess_fixed_order, fixed_order; + unsigned rice_parameter; + unsigned _candidate_bits, _best_bits; + unsigned _best_subframe; + + FLAC__ASSERT(frame_header->blocksize > 0); + + /* verbatim subframe is the baseline against which we measure other compressed subframes */ + _best_subframe = 0; + if(encoder->private_->disable_verbatim_subframes && frame_header->blocksize >= FLAC__MAX_FIXED_ORDER) + _best_bits = UINT_MAX; + else + _best_bits = evaluate_verbatim_subframe_(encoder, integer_signal, frame_header->blocksize, subframe_bps, subframe[_best_subframe]); + + if(frame_header->blocksize >= FLAC__MAX_FIXED_ORDER) { + unsigned signal_is_constant = false; + guess_fixed_order = encoder->private_->local_fixed_compute_best_predictor(integer_signal+FLAC__MAX_FIXED_ORDER, frame_header->blocksize-FLAC__MAX_FIXED_ORDER, fixed_residual_bits_per_sample); + /* check for constant subframe */ + if( + !encoder->private_->disable_constant_subframes && +#ifndef FLAC__INTEGER_ONLY_LIBRARY + fixed_residual_bits_per_sample[1] == 0.0 +#else + fixed_residual_bits_per_sample[1] == FLAC__FP_ZERO +#endif + ) { + /* the above means it's possible all samples are the same value; now double-check it: */ + unsigned i; + signal_is_constant = true; + for(i = 1; i < frame_header->blocksize; i++) { + if(integer_signal[0] != integer_signal[i]) { + signal_is_constant = false; + break; + } + } + } + if(signal_is_constant) { + _candidate_bits = evaluate_constant_subframe_(encoder, integer_signal[0], frame_header->blocksize, subframe_bps, subframe[!_best_subframe]); + if(_candidate_bits < _best_bits) { + _best_subframe = !_best_subframe; + _best_bits = _candidate_bits; + } + } + else { + if(!encoder->private_->disable_fixed_subframes || (encoder->protected_->max_lpc_order == 0 && _best_bits == UINT_MAX)) { + /* encode fixed */ + if(encoder->protected_->do_exhaustive_model_search) { + min_fixed_order = 0; + max_fixed_order = FLAC__MAX_FIXED_ORDER; + } + else { + min_fixed_order = max_fixed_order = guess_fixed_order; + } + if(max_fixed_order >= frame_header->blocksize) + max_fixed_order = frame_header->blocksize - 1; + for(fixed_order = min_fixed_order; fixed_order <= max_fixed_order; fixed_order++) { +#ifndef FLAC__INTEGER_ONLY_LIBRARY + if(fixed_residual_bits_per_sample[fixed_order] >= (FLAC__float)subframe_bps) + continue; /* don't even try */ + rice_parameter = (fixed_residual_bits_per_sample[fixed_order] > 0.0)? (unsigned)(fixed_residual_bits_per_sample[fixed_order]+0.5) : 0; /* 0.5 is for rounding */ +#else + if(FLAC__fixedpoint_trunc(fixed_residual_bits_per_sample[fixed_order]) >= (int)subframe_bps) + continue; /* don't even try */ + rice_parameter = (fixed_residual_bits_per_sample[fixed_order] > FLAC__FP_ZERO)? (unsigned)FLAC__fixedpoint_trunc(fixed_residual_bits_per_sample[fixed_order]+FLAC__FP_ONE_HALF) : 0; /* 0.5 is for rounding */ +#endif + rice_parameter++; /* to account for the signed->unsigned conversion during rice coding */ + if(rice_parameter >= FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER) { +#ifdef DEBUG_VERBOSE + fprintf(stderr, "clipping rice_parameter (%u -> %u) @0\n", rice_parameter, FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER - 1); +#endif + rice_parameter = FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER - 1; + } + _candidate_bits = + evaluate_fixed_subframe_( + encoder, + integer_signal, + residual[!_best_subframe], + encoder->private_->abs_residual_partition_sums, + encoder->private_->raw_bits_per_partition, + frame_header->blocksize, + subframe_bps, + fixed_order, + rice_parameter, + min_partition_order, + max_partition_order, + encoder->protected_->do_escape_coding, + encoder->protected_->rice_parameter_search_dist, + subframe[!_best_subframe], + partitioned_rice_contents[!_best_subframe] + ); + if(_candidate_bits < _best_bits) { + _best_subframe = !_best_subframe; + _best_bits = _candidate_bits; + } + } + } + +#ifndef FLAC__INTEGER_ONLY_LIBRARY + /* encode lpc */ + if(encoder->protected_->max_lpc_order > 0) { + if(encoder->protected_->max_lpc_order >= frame_header->blocksize) + max_lpc_order = frame_header->blocksize-1; + else + max_lpc_order = encoder->protected_->max_lpc_order; + if(max_lpc_order > 0) { + unsigned a; + for (a = 0; a < encoder->protected_->num_apodizations; a++) { + FLAC__lpc_window_data(integer_signal, encoder->private_->window[a], encoder->private_->windowed_signal, frame_header->blocksize); + encoder->private_->local_lpc_compute_autocorrelation(encoder->private_->windowed_signal, frame_header->blocksize, max_lpc_order+1, autoc); + /* if autoc[0] == 0.0, the signal is constant and we usually won't get here, but it can happen */ + if(autoc[0] != 0.0) { + FLAC__lpc_compute_lp_coefficients(autoc, &max_lpc_order, encoder->private_->lp_coeff, lpc_error); + if(encoder->protected_->do_exhaustive_model_search) { + min_lpc_order = 1; + } + else { + const unsigned guess_lpc_order = + FLAC__lpc_compute_best_order( + lpc_error, + max_lpc_order, + frame_header->blocksize, + subframe_bps + ( + encoder->protected_->do_qlp_coeff_prec_search? + FLAC__MIN_QLP_COEFF_PRECISION : /* have to guess; use the min possible size to avoid accidentally favoring lower orders */ + encoder->protected_->qlp_coeff_precision + ) + ); + min_lpc_order = max_lpc_order = guess_lpc_order; + } + if(max_lpc_order >= frame_header->blocksize) + max_lpc_order = frame_header->blocksize - 1; + for(lpc_order = min_lpc_order; lpc_order <= max_lpc_order; lpc_order++) { + lpc_residual_bits_per_sample = FLAC__lpc_compute_expected_bits_per_residual_sample(lpc_error[lpc_order-1], frame_header->blocksize-lpc_order); + if(lpc_residual_bits_per_sample >= (FLAC__double)subframe_bps) + continue; /* don't even try */ + rice_parameter = (lpc_residual_bits_per_sample > 0.0)? (unsigned)(lpc_residual_bits_per_sample+0.5) : 0; /* 0.5 is for rounding */ + rice_parameter++; /* to account for the signed->unsigned conversion during rice coding */ + if(rice_parameter >= FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER) { +#ifdef DEBUG_VERBOSE + fprintf(stderr, "clipping rice_parameter (%u -> %u) @1\n", rice_parameter, FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER - 1); +#endif + rice_parameter = FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER - 1; + } + if(encoder->protected_->do_qlp_coeff_prec_search) { + min_qlp_coeff_precision = FLAC__MIN_QLP_COEFF_PRECISION; + /* try to ensure a 32-bit datapath throughout for 16bps(+1bps for side channel) or less */ + if(subframe_bps <= 17) { + max_qlp_coeff_precision = min(32 - subframe_bps - lpc_order, FLAC__MAX_QLP_COEFF_PRECISION); + max_qlp_coeff_precision = max(max_qlp_coeff_precision, min_qlp_coeff_precision); + } + else + max_qlp_coeff_precision = FLAC__MAX_QLP_COEFF_PRECISION; + } + else { + min_qlp_coeff_precision = max_qlp_coeff_precision = encoder->protected_->qlp_coeff_precision; + } + for(qlp_coeff_precision = min_qlp_coeff_precision; qlp_coeff_precision <= max_qlp_coeff_precision; qlp_coeff_precision++) { + _candidate_bits = + evaluate_lpc_subframe_( + encoder, + integer_signal, + residual[!_best_subframe], + encoder->private_->abs_residual_partition_sums, + encoder->private_->raw_bits_per_partition, + encoder->private_->lp_coeff[lpc_order-1], + frame_header->blocksize, + subframe_bps, + lpc_order, + qlp_coeff_precision, + rice_parameter, + min_partition_order, + max_partition_order, + encoder->protected_->do_escape_coding, + encoder->protected_->rice_parameter_search_dist, + subframe[!_best_subframe], + partitioned_rice_contents[!_best_subframe] + ); + if(_candidate_bits > 0) { /* if == 0, there was a problem quantizing the lpcoeffs */ + if(_candidate_bits < _best_bits) { + _best_subframe = !_best_subframe; + _best_bits = _candidate_bits; + } + } + } + } + } + } + } + } +#endif /* !defined FLAC__INTEGER_ONLY_LIBRARY */ + } + } + + /* under rare circumstances this can happen when all but lpc subframe types are disabled: */ + if(_best_bits == UINT_MAX) { + FLAC__ASSERT(_best_subframe == 0); + _best_bits = evaluate_verbatim_subframe_(encoder, integer_signal, frame_header->blocksize, subframe_bps, subframe[_best_subframe]); + } + + *best_subframe = _best_subframe; + *best_bits = _best_bits; + + return true; +} + +FLAC__bool add_subframe_( + FLAC__StreamEncoder *encoder, + unsigned blocksize, + unsigned subframe_bps, + const FLAC__Subframe *subframe, + FLAC__BitWriter *frame +) +{ + switch(subframe->type) { + case FLAC__SUBFRAME_TYPE_CONSTANT: + if(!FLAC__subframe_add_constant(&(subframe->data.constant), subframe_bps, subframe->wasted_bits, frame)) { + encoder->protected_->state = FLAC__STREAM_ENCODER_FRAMING_ERROR; + return false; + } + break; + case FLAC__SUBFRAME_TYPE_FIXED: + if(!FLAC__subframe_add_fixed(&(subframe->data.fixed), blocksize - subframe->data.fixed.order, subframe_bps, subframe->wasted_bits, frame)) { + encoder->protected_->state = FLAC__STREAM_ENCODER_FRAMING_ERROR; + return false; + } + break; + case FLAC__SUBFRAME_TYPE_LPC: + if(!FLAC__subframe_add_lpc(&(subframe->data.lpc), blocksize - subframe->data.lpc.order, subframe_bps, subframe->wasted_bits, frame)) { + encoder->protected_->state = FLAC__STREAM_ENCODER_FRAMING_ERROR; + return false; + } + break; + case FLAC__SUBFRAME_TYPE_VERBATIM: + if(!FLAC__subframe_add_verbatim(&(subframe->data.verbatim), blocksize, subframe_bps, subframe->wasted_bits, frame)) { + encoder->protected_->state = FLAC__STREAM_ENCODER_FRAMING_ERROR; + return false; + } + break; + default: + FLAC__ASSERT(0); + } + + return true; +} + +#define SPOTCHECK_ESTIMATE 0 +#if SPOTCHECK_ESTIMATE +static void spotcheck_subframe_estimate_( + FLAC__StreamEncoder *encoder, + unsigned blocksize, + unsigned subframe_bps, + const FLAC__Subframe *subframe, + unsigned estimate +) +{ + FLAC__bool ret; + FLAC__BitWriter *frame = FLAC__bitwriter_new(); + if(frame == 0) { + fprintf(stderr, "EST: can't allocate frame\n"); + return; + } + if(!FLAC__bitwriter_init(frame)) { + fprintf(stderr, "EST: can't init frame\n"); + return; + } + ret = add_subframe_(encoder, blocksize, subframe_bps, subframe, frame); + FLAC__ASSERT(ret); + { + const unsigned actual = FLAC__bitwriter_get_input_bits_unconsumed(frame); + if(estimate != actual) + fprintf(stderr, "EST: bad, frame#%u sub#%%d type=%8s est=%u, actual=%u, delta=%d\n", encoder->private_->current_frame_number, FLAC__SubframeTypeString[subframe->type], estimate, actual, (int)actual-(int)estimate); + } + FLAC__bitwriter_delete(frame); +} +#endif + +unsigned evaluate_constant_subframe_( + FLAC__StreamEncoder *encoder, + const FLAC__int32 signal, + unsigned blocksize, + unsigned subframe_bps, + FLAC__Subframe *subframe +) +{ + unsigned estimate; + subframe->type = FLAC__SUBFRAME_TYPE_CONSTANT; + subframe->data.constant.value = signal; + + estimate = FLAC__SUBFRAME_ZERO_PAD_LEN + FLAC__SUBFRAME_TYPE_LEN + FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN + subframe->wasted_bits + subframe_bps; + +#if SPOTCHECK_ESTIMATE + spotcheck_subframe_estimate_(encoder, blocksize, subframe_bps, subframe, estimate); +#else + (void)encoder, (void)blocksize; +#endif + + return estimate; +} + +unsigned evaluate_fixed_subframe_( + FLAC__StreamEncoder *encoder, + const FLAC__int32 signal[], + FLAC__int32 residual[], + FLAC__uint64 abs_residual_partition_sums[], + unsigned raw_bits_per_partition[], + unsigned blocksize, + unsigned subframe_bps, + unsigned order, + unsigned rice_parameter, + unsigned min_partition_order, + unsigned max_partition_order, + FLAC__bool do_escape_coding, + unsigned rice_parameter_search_dist, + FLAC__Subframe *subframe, + FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents +) +{ + unsigned i, residual_bits, estimate; + const unsigned residual_samples = blocksize - order; + + FLAC__fixed_compute_residual(signal+order, residual_samples, order, residual); + + subframe->type = FLAC__SUBFRAME_TYPE_FIXED; + + subframe->data.fixed.entropy_coding_method.type = FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE; + subframe->data.fixed.entropy_coding_method.data.partitioned_rice.contents = partitioned_rice_contents; + subframe->data.fixed.residual = residual; + + residual_bits = + find_best_partition_order_( + encoder->private_, + residual, + abs_residual_partition_sums, + raw_bits_per_partition, + residual_samples, + order, + rice_parameter, + min_partition_order, + max_partition_order, + subframe_bps, + do_escape_coding, + rice_parameter_search_dist, + &subframe->data.fixed.entropy_coding_method.data.partitioned_rice + ); + + subframe->data.fixed.order = order; + for(i = 0; i < order; i++) + subframe->data.fixed.warmup[i] = signal[i]; + + estimate = FLAC__SUBFRAME_ZERO_PAD_LEN + FLAC__SUBFRAME_TYPE_LEN + FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN + subframe->wasted_bits + (order * subframe_bps) + residual_bits; + +#if SPOTCHECK_ESTIMATE + spotcheck_subframe_estimate_(encoder, blocksize, subframe_bps, subframe, estimate); +#endif + + return estimate; +} + +#ifndef FLAC__INTEGER_ONLY_LIBRARY +unsigned evaluate_lpc_subframe_( + FLAC__StreamEncoder *encoder, + const FLAC__int32 signal[], + FLAC__int32 residual[], + FLAC__uint64 abs_residual_partition_sums[], + unsigned raw_bits_per_partition[], + const FLAC__real lp_coeff[], + unsigned blocksize, + unsigned subframe_bps, + unsigned order, + unsigned qlp_coeff_precision, + unsigned rice_parameter, + unsigned min_partition_order, + unsigned max_partition_order, + FLAC__bool do_escape_coding, + unsigned rice_parameter_search_dist, + FLAC__Subframe *subframe, + FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents +) +{ + FLAC__int32 qlp_coeff[FLAC__MAX_LPC_ORDER]; + unsigned i, residual_bits, estimate; + int quantization, ret; + const unsigned residual_samples = blocksize - order; + + /* try to keep qlp coeff precision such that only 32-bit math is required for decode of <=16bps streams */ + if(subframe_bps <= 16) { + FLAC__ASSERT(order > 0); + FLAC__ASSERT(order <= FLAC__MAX_LPC_ORDER); + qlp_coeff_precision = min(qlp_coeff_precision, 32 - subframe_bps - FLAC__bitmath_ilog2(order)); + } + + ret = FLAC__lpc_quantize_coefficients(lp_coeff, order, qlp_coeff_precision, qlp_coeff, &quantization); + if(ret != 0) + return 0; /* this is a hack to indicate to the caller that we can't do lp at this order on this subframe */ + + if(subframe_bps + qlp_coeff_precision + FLAC__bitmath_ilog2(order) <= 32) + if(subframe_bps <= 16 && qlp_coeff_precision <= 16) + encoder->private_->local_lpc_compute_residual_from_qlp_coefficients_16bit(signal+order, residual_samples, qlp_coeff, order, quantization, residual); + else + encoder->private_->local_lpc_compute_residual_from_qlp_coefficients(signal+order, residual_samples, qlp_coeff, order, quantization, residual); + else + encoder->private_->local_lpc_compute_residual_from_qlp_coefficients_64bit(signal+order, residual_samples, qlp_coeff, order, quantization, residual); + + subframe->type = FLAC__SUBFRAME_TYPE_LPC; + + subframe->data.lpc.entropy_coding_method.type = FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE; + subframe->data.lpc.entropy_coding_method.data.partitioned_rice.contents = partitioned_rice_contents; + subframe->data.lpc.residual = residual; + + residual_bits = + find_best_partition_order_( + encoder->private_, + residual, + abs_residual_partition_sums, + raw_bits_per_partition, + residual_samples, + order, + rice_parameter, + min_partition_order, + max_partition_order, + subframe_bps, + do_escape_coding, + rice_parameter_search_dist, + &subframe->data.lpc.entropy_coding_method.data.partitioned_rice + ); + + subframe->data.lpc.order = order; + subframe->data.lpc.qlp_coeff_precision = qlp_coeff_precision; + subframe->data.lpc.quantization_level = quantization; + memcpy(subframe->data.lpc.qlp_coeff, qlp_coeff, sizeof(FLAC__int32)*FLAC__MAX_LPC_ORDER); + for(i = 0; i < order; i++) + subframe->data.lpc.warmup[i] = signal[i]; + + estimate = FLAC__SUBFRAME_ZERO_PAD_LEN + FLAC__SUBFRAME_TYPE_LEN + FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN + subframe->wasted_bits + FLAC__SUBFRAME_LPC_QLP_COEFF_PRECISION_LEN + FLAC__SUBFRAME_LPC_QLP_SHIFT_LEN + (order * (qlp_coeff_precision + subframe_bps)) + residual_bits; + +#if SPOTCHECK_ESTIMATE + spotcheck_subframe_estimate_(encoder, blocksize, subframe_bps, subframe, estimate); +#endif + + return estimate; +} +#endif + +unsigned evaluate_verbatim_subframe_( + FLAC__StreamEncoder *encoder, + const FLAC__int32 signal[], + unsigned blocksize, + unsigned subframe_bps, + FLAC__Subframe *subframe +) +{ + unsigned estimate; + + subframe->type = FLAC__SUBFRAME_TYPE_VERBATIM; + + subframe->data.verbatim.data = signal; + + estimate = FLAC__SUBFRAME_ZERO_PAD_LEN + FLAC__SUBFRAME_TYPE_LEN + FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN + subframe->wasted_bits + (blocksize * subframe_bps); + +#if SPOTCHECK_ESTIMATE + spotcheck_subframe_estimate_(encoder, blocksize, subframe_bps, subframe, estimate); +#else + (void)encoder; +#endif + + return estimate; +} + +unsigned find_best_partition_order_( + FLAC__StreamEncoderPrivate *private_, + const FLAC__int32 residual[], + FLAC__uint64 abs_residual_partition_sums[], + unsigned raw_bits_per_partition[], + unsigned residual_samples, + unsigned predictor_order, + unsigned rice_parameter, + unsigned min_partition_order, + unsigned max_partition_order, + unsigned bps, + FLAC__bool do_escape_coding, + unsigned rice_parameter_search_dist, + FLAC__EntropyCodingMethod_PartitionedRice *best_partitioned_rice +) +{ + unsigned residual_bits, best_residual_bits = 0; + unsigned best_parameters_index = 0; + const unsigned blocksize = residual_samples + predictor_order; + + max_partition_order = FLAC__format_get_max_rice_partition_order_from_blocksize_limited_max_and_predictor_order(max_partition_order, blocksize, predictor_order); + min_partition_order = min(min_partition_order, max_partition_order); + + precompute_partition_info_sums_(residual, abs_residual_partition_sums, residual_samples, predictor_order, min_partition_order, max_partition_order, bps); + + if(do_escape_coding) + precompute_partition_info_escapes_(residual, raw_bits_per_partition, residual_samples, predictor_order, min_partition_order, max_partition_order); + + { + int partition_order; + unsigned sum; + + for(partition_order = (int)max_partition_order, sum = 0; partition_order >= (int)min_partition_order; partition_order--) { + if(! + set_partitioned_rice_( +#ifdef EXACT_RICE_BITS_CALCULATION + residual, +#endif + abs_residual_partition_sums+sum, + raw_bits_per_partition+sum, + residual_samples, + predictor_order, + rice_parameter, + rice_parameter_search_dist, + (unsigned)partition_order, + do_escape_coding, + &private_->partitioned_rice_contents_extra[!best_parameters_index], + &residual_bits + ) + ) + { + FLAC__ASSERT(best_residual_bits != 0); + break; + } + sum += 1u << partition_order; + if(best_residual_bits == 0 || residual_bits < best_residual_bits) { + best_residual_bits = residual_bits; + best_parameters_index = !best_parameters_index; + best_partitioned_rice->order = partition_order; + } + } + } + + /* + * We are allowed to de-const the pointer based on our special knowledge; + * it is const to the outside world. + */ + { + FLAC__EntropyCodingMethod_PartitionedRiceContents* best_partitioned_rice_contents = (FLAC__EntropyCodingMethod_PartitionedRiceContents*)best_partitioned_rice->contents; + FLAC__format_entropy_coding_method_partitioned_rice_contents_ensure_size(best_partitioned_rice_contents, max(6, best_partitioned_rice->order)); + memcpy(best_partitioned_rice_contents->parameters, private_->partitioned_rice_contents_extra[best_parameters_index].parameters, sizeof(unsigned)*(1<<(best_partitioned_rice->order))); + memcpy(best_partitioned_rice_contents->raw_bits, private_->partitioned_rice_contents_extra[best_parameters_index].raw_bits, sizeof(unsigned)*(1<<(best_partitioned_rice->order))); + } + + return best_residual_bits; +} + +#if defined(FLAC__CPU_IA32) && !defined FLAC__NO_ASM && defined FLAC__HAS_NASM +extern void precompute_partition_info_sums_32bit_asm_ia32_( + const FLAC__int32 residual[], + FLAC__uint64 abs_residual_partition_sums[], + unsigned blocksize, + unsigned predictor_order, + unsigned min_partition_order, + unsigned max_partition_order +); +#endif + +void precompute_partition_info_sums_( + const FLAC__int32 residual[], + FLAC__uint64 abs_residual_partition_sums[], + unsigned residual_samples, + unsigned predictor_order, + unsigned min_partition_order, + unsigned max_partition_order, + unsigned bps +) +{ + const unsigned default_partition_samples = (residual_samples + predictor_order) >> max_partition_order; + unsigned partitions = 1u << max_partition_order; + + FLAC__ASSERT(default_partition_samples > predictor_order); + +#if defined(FLAC__CPU_IA32) && !defined FLAC__NO_ASM && defined FLAC__HAS_NASM + if(FLAC__bitmath_ilog2(default_partition_samples) + bps < 32) { /* very slightly pessimistic but still catches all common cases */ + precompute_partition_info_sums_32bit_asm_ia32_(residual, abs_residual_partition_sums, residual_samples + predictor_order, predictor_order, min_partition_order, max_partition_order); + return; + } +#endif + + /* first do max_partition_order */ + { + unsigned partition, residual_sample, end = (unsigned)(-(int)predictor_order); + if(FLAC__bitmath_ilog2(default_partition_samples) + bps < 32) { /* very slightly pessimistic but still catches all common cases */ + FLAC__uint32 abs_residual_partition_sum; + + for(partition = residual_sample = 0; partition < partitions; partition++) { + end += default_partition_samples; + abs_residual_partition_sum = 0; + for( ; residual_sample < end; residual_sample++) + abs_residual_partition_sum += abs(residual[residual_sample]); /* abs(INT_MIN) is undefined, but if the residual is INT_MIN we have bigger problems */ + abs_residual_partition_sums[partition] = abs_residual_partition_sum; + } + } + else { /* have to pessimistically use 64 bits for accumulator */ + FLAC__uint64 abs_residual_partition_sum; + + for(partition = residual_sample = 0; partition < partitions; partition++) { + end += default_partition_samples; + abs_residual_partition_sum = 0; + for( ; residual_sample < end; residual_sample++) + abs_residual_partition_sum += abs(residual[residual_sample]); /* abs(INT_MIN) is undefined, but if the residual is INT_MIN we have bigger problems */ + abs_residual_partition_sums[partition] = abs_residual_partition_sum; + } + } + } + + /* now merge partitions for lower orders */ + { + unsigned from_partition = 0, to_partition = partitions; + int partition_order; + for(partition_order = (int)max_partition_order - 1; partition_order >= (int)min_partition_order; partition_order--) { + unsigned i; + partitions >>= 1; + for(i = 0; i < partitions; i++) { + abs_residual_partition_sums[to_partition++] = + abs_residual_partition_sums[from_partition ] + + abs_residual_partition_sums[from_partition+1]; + from_partition += 2; + } + } + } +} + +void precompute_partition_info_escapes_( + const FLAC__int32 residual[], + unsigned raw_bits_per_partition[], + unsigned residual_samples, + unsigned predictor_order, + unsigned min_partition_order, + unsigned max_partition_order +) +{ + int partition_order; + unsigned from_partition, to_partition = 0; + const unsigned blocksize = residual_samples + predictor_order; + + /* first do max_partition_order */ + for(partition_order = (int)max_partition_order; partition_order >= 0; partition_order--) { + FLAC__int32 r; + FLAC__uint32 rmax; + unsigned partition, partition_sample, partition_samples, residual_sample; + const unsigned partitions = 1u << partition_order; + const unsigned default_partition_samples = blocksize >> partition_order; + + FLAC__ASSERT(default_partition_samples > predictor_order); + + for(partition = residual_sample = 0; partition < partitions; partition++) { + partition_samples = default_partition_samples; + if(partition == 0) + partition_samples -= predictor_order; + rmax = 0; + for(partition_sample = 0; partition_sample < partition_samples; partition_sample++) { + r = residual[residual_sample++]; + /* OPT: maybe faster: rmax |= r ^ (r>>31) */ + if(r < 0) + rmax |= ~r; + else + rmax |= r; + } + /* now we know all residual values are in the range [-rmax-1,rmax] */ + raw_bits_per_partition[partition] = rmax? FLAC__bitmath_ilog2(rmax) + 2 : 1; + } + to_partition = partitions; + break; /*@@@ yuck, should remove the 'for' loop instead */ + } + + /* now merge partitions for lower orders */ + for(from_partition = 0, --partition_order; partition_order >= (int)min_partition_order; partition_order--) { + unsigned m; + unsigned i; + const unsigned partitions = 1u << partition_order; + for(i = 0; i < partitions; i++) { + m = raw_bits_per_partition[from_partition]; + from_partition++; + raw_bits_per_partition[to_partition] = max(m, raw_bits_per_partition[from_partition]); + from_partition++; + to_partition++; + } + } +} + +#ifdef EXACT_RICE_BITS_CALCULATION +static FLaC__INLINE unsigned count_rice_bits_in_partition_( + const unsigned rice_parameter, + const unsigned partition_samples, + const FLAC__int32 *residual +) +{ + unsigned i, partition_bits = + FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN + + (1+rice_parameter) * partition_samples /* 1 for unary stop bit + rice_parameter for the binary portion */ + ; + for(i = 0; i < partition_samples; i++) + partition_bits += ( (FLAC__uint32)((residual[i]<<1)^(residual[i]>>31)) >> rice_parameter ); + return partition_bits; +} +#else +static FLaC__INLINE unsigned count_rice_bits_in_partition_( + const unsigned rice_parameter, + const unsigned partition_samples, + const FLAC__uint64 abs_residual_partition_sum +) +{ + return + FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN + + (1+rice_parameter) * partition_samples + /* 1 for unary stop bit + rice_parameter for the binary portion */ + ( + rice_parameter? + (unsigned)(abs_residual_partition_sum >> (rice_parameter-1)) /* rice_parameter-1 because the real coder sign-folds instead of using a sign bit */ + : (unsigned)(abs_residual_partition_sum << 1) /* can't shift by negative number, so reverse */ + ) + - (partition_samples >> 1) + /* -(partition_samples>>1) to subtract out extra contributions to the abs_residual_partition_sum. + * The actual number of bits used is closer to the sum(for all i in the partition) of abs(residual[i])>>(rice_parameter-1) + * By using the abs_residual_partition sum, we also add in bits in the LSBs that would normally be shifted out. + * So the subtraction term tries to guess how many extra bits were contributed. + * If the LSBs are randomly distributed, this should average to 0.5 extra bits per sample. + */ + ; +} +#endif + +FLAC__bool set_partitioned_rice_( +#ifdef EXACT_RICE_BITS_CALCULATION + const FLAC__int32 residual[], +#endif + const FLAC__uint64 abs_residual_partition_sums[], + const unsigned raw_bits_per_partition[], + const unsigned residual_samples, + const unsigned predictor_order, + const unsigned suggested_rice_parameter, + const unsigned rice_parameter_search_dist, + const unsigned partition_order, + const FLAC__bool search_for_escapes, + FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents, + unsigned *bits +) +{ + unsigned rice_parameter, partition_bits; + unsigned best_partition_bits, best_rice_parameter = 0; + unsigned bits_ = FLAC__ENTROPY_CODING_METHOD_TYPE_LEN + FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN; + unsigned *parameters, *raw_bits; +#ifdef ENABLE_RICE_PARAMETER_SEARCH + unsigned min_rice_parameter, max_rice_parameter; +#else + (void)rice_parameter_search_dist; +#endif + + FLAC__ASSERT(suggested_rice_parameter < FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER); + + FLAC__format_entropy_coding_method_partitioned_rice_contents_ensure_size(partitioned_rice_contents, max(6, partition_order)); + parameters = partitioned_rice_contents->parameters; + raw_bits = partitioned_rice_contents->raw_bits; + + if(partition_order == 0) { + best_partition_bits = (unsigned)(-1); +#ifdef ENABLE_RICE_PARAMETER_SEARCH + if(rice_parameter_search_dist) { + if(suggested_rice_parameter < rice_parameter_search_dist) + min_rice_parameter = 0; + else + min_rice_parameter = suggested_rice_parameter - rice_parameter_search_dist; + max_rice_parameter = suggested_rice_parameter + rice_parameter_search_dist; + if(max_rice_parameter >= FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER) { +#ifdef DEBUG_VERBOSE + fprintf(stderr, "clipping rice_parameter (%u -> %u) @5\n", max_rice_parameter, FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER - 1); +#endif + max_rice_parameter = FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER - 1; + } + } + else + min_rice_parameter = max_rice_parameter = suggested_rice_parameter; + + for(rice_parameter = min_rice_parameter; rice_parameter <= max_rice_parameter; rice_parameter++) { +#else + rice_parameter = suggested_rice_parameter; +#endif +#ifdef EXACT_RICE_BITS_CALCULATION + partition_bits = count_rice_bits_in_partition_(rice_parameter, residual_samples, residual); +#else + partition_bits = count_rice_bits_in_partition_(rice_parameter, residual_samples, abs_residual_partition_sums[0]); +#endif + if(partition_bits < best_partition_bits) { + best_rice_parameter = rice_parameter; + best_partition_bits = partition_bits; + } +#ifdef ENABLE_RICE_PARAMETER_SEARCH + } +#endif + if(search_for_escapes) { + partition_bits = FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN + FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_RAW_LEN + raw_bits_per_partition[0] * residual_samples; + if(partition_bits <= best_partition_bits) { + raw_bits[0] = raw_bits_per_partition[0]; + best_rice_parameter = FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER; + best_partition_bits = partition_bits; + } + } + parameters[0] = best_rice_parameter; + bits_ += best_partition_bits; + } + else { + unsigned partition, residual_sample; + unsigned partition_samples; + FLAC__uint64 mean, k; + const unsigned partitions = 1u << partition_order; + for(partition = residual_sample = 0; partition < partitions; partition++) { + partition_samples = (residual_samples+predictor_order) >> partition_order; + if(partition == 0) { + if(partition_samples <= predictor_order) + return false; + else + partition_samples -= predictor_order; + } + mean = abs_residual_partition_sums[partition]; + /* we are basically calculating the size in bits of the + * average residual magnitude in the partition: + * rice_parameter = floor(log2(mean/partition_samples)) + * 'mean' is not a good name for the variable, it is + * actually the sum of magnitudes of all residual values + * in the partition, so the actual mean is + * mean/partition_samples + */ + for(rice_parameter = 0, k = partition_samples; k < mean; rice_parameter++, k <<= 1) + ; + if(rice_parameter >= FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER) { +#ifdef DEBUG_VERBOSE + fprintf(stderr, "clipping rice_parameter (%u -> %u) @6\n", rice_parameter, FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER - 1); +#endif + rice_parameter = FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER - 1; + } + + best_partition_bits = (unsigned)(-1); +#ifdef ENABLE_RICE_PARAMETER_SEARCH + if(rice_parameter_search_dist) { + if(rice_parameter < rice_parameter_search_dist) + min_rice_parameter = 0; + else + min_rice_parameter = rice_parameter - rice_parameter_search_dist; + max_rice_parameter = rice_parameter + rice_parameter_search_dist; + if(max_rice_parameter >= FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER) { +#ifdef DEBUG_VERBOSE + fprintf(stderr, "clipping rice_parameter (%u -> %u) @7\n", max_rice_parameter, FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER - 1); +#endif + max_rice_parameter = FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER - 1; + } + } + else + min_rice_parameter = max_rice_parameter = rice_parameter; + + for(rice_parameter = min_rice_parameter; rice_parameter <= max_rice_parameter; rice_parameter++) { +#endif +#ifdef EXACT_RICE_BITS_CALCULATION + partition_bits = count_rice_bits_in_partition_(rice_parameter, partition_samples, residual+residual_sample); +#else + partition_bits = count_rice_bits_in_partition_(rice_parameter, partition_samples, abs_residual_partition_sums[partition]); +#endif + if(partition_bits < best_partition_bits) { + best_rice_parameter = rice_parameter; + best_partition_bits = partition_bits; + } +#ifdef ENABLE_RICE_PARAMETER_SEARCH + } +#endif + if(search_for_escapes) { + partition_bits = FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN + FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_RAW_LEN + raw_bits_per_partition[partition] * partition_samples; + if(partition_bits <= best_partition_bits) { + raw_bits[partition] = raw_bits_per_partition[partition]; + best_rice_parameter = FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER; + best_partition_bits = partition_bits; + } + } + parameters[partition] = best_rice_parameter; + bits_ += best_partition_bits; + residual_sample += partition_samples; + } + } + + *bits = bits_; + return true; +} + +unsigned get_wasted_bits_(FLAC__int32 signal[], unsigned samples) +{ + unsigned i, shift; + FLAC__int32 x = 0; + + for(i = 0; i < samples && !(x&1); i++) + x |= signal[i]; + + if(x == 0) { + shift = 0; + } + else { + for(shift = 0; !(x&1); shift++) + x >>= 1; + } + + if(shift > 0) { + for(i = 0; i < samples; i++) + signal[i] >>= shift; + } + + return shift; +} + +void append_to_verify_fifo_(verify_input_fifo *fifo, const FLAC__int32 * const input[], unsigned input_offset, unsigned channels, unsigned wide_samples) +{ + unsigned channel; + + for(channel = 0; channel < channels; channel++) + memcpy(&fifo->data[channel][fifo->tail], &input[channel][input_offset], sizeof(FLAC__int32) * wide_samples); + + fifo->tail += wide_samples; + + FLAC__ASSERT(fifo->tail <= fifo->size); +} + +void append_to_verify_fifo_interleaved_(verify_input_fifo *fifo, const FLAC__int32 input[], unsigned input_offset, unsigned channels, unsigned wide_samples) +{ + unsigned channel; + unsigned sample, wide_sample; + unsigned tail = fifo->tail; + + sample = input_offset * channels; + for(wide_sample = 0; wide_sample < wide_samples; wide_sample++) { + for(channel = 0; channel < channels; channel++) + fifo->data[channel][tail] = input[sample++]; + tail++; + } + fifo->tail = tail; + + FLAC__ASSERT(fifo->tail <= fifo->size); +} + +FLAC__StreamDecoderReadStatus verify_read_callback_(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data) +{ + FLAC__StreamEncoder *encoder = (FLAC__StreamEncoder*)client_data; + const size_t encoded_bytes = encoder->private_->verify.output.bytes; + (void)decoder; + + if(encoder->private_->verify.needs_magic_hack) { + FLAC__ASSERT(*bytes >= FLAC__STREAM_SYNC_LENGTH); + *bytes = FLAC__STREAM_SYNC_LENGTH; + memcpy(buffer, FLAC__STREAM_SYNC_STRING, *bytes); + encoder->private_->verify.needs_magic_hack = false; + } + else { + if(encoded_bytes == 0) { + /* + * If we get here, a FIFO underflow has occurred, + * which means there is a bug somewhere. + */ + FLAC__ASSERT(0); + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + } + else if(encoded_bytes < *bytes) + *bytes = encoded_bytes; + memcpy(buffer, encoder->private_->verify.output.data, *bytes); + encoder->private_->verify.output.data += *bytes; + encoder->private_->verify.output.bytes -= *bytes; + } + + return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; +} + +FLAC__StreamDecoderWriteStatus verify_write_callback_(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data) +{ + FLAC__StreamEncoder *encoder = (FLAC__StreamEncoder *)client_data; + unsigned channel; + const unsigned channels = frame->header.channels; + const unsigned blocksize = frame->header.blocksize; + const unsigned bytes_per_block = sizeof(FLAC__int32) * blocksize; + + (void)decoder; + + for(channel = 0; channel < channels; channel++) { + if(0 != memcmp(buffer[channel], encoder->private_->verify.input_fifo.data[channel], bytes_per_block)) { + unsigned i, sample = 0; + FLAC__int32 expect = 0, got = 0; + + for(i = 0; i < blocksize; i++) { + if(buffer[channel][i] != encoder->private_->verify.input_fifo.data[channel][i]) { + sample = i; + expect = (FLAC__int32)encoder->private_->verify.input_fifo.data[channel][i]; + got = (FLAC__int32)buffer[channel][i]; + break; + } + } + FLAC__ASSERT(i < blocksize); + FLAC__ASSERT(frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER); + encoder->private_->verify.error_stats.absolute_sample = frame->header.number.sample_number + sample; + encoder->private_->verify.error_stats.frame_number = (unsigned)(frame->header.number.sample_number / blocksize); + encoder->private_->verify.error_stats.channel = channel; + encoder->private_->verify.error_stats.sample = sample; + encoder->private_->verify.error_stats.expected = expect; + encoder->private_->verify.error_stats.got = got; + encoder->protected_->state = FLAC__STREAM_ENCODER_VERIFY_MISMATCH_IN_AUDIO_DATA; + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + } + /* dequeue the frame from the fifo */ + encoder->private_->verify.input_fifo.tail -= blocksize; + FLAC__ASSERT(encoder->private_->verify.input_fifo.tail <= OVERREAD_); + for(channel = 0; channel < channels; channel++) + memmove(&encoder->private_->verify.input_fifo.data[channel][0], &encoder->private_->verify.input_fifo.data[channel][blocksize], encoder->private_->verify.input_fifo.tail * sizeof(encoder->private_->verify.input_fifo.data[0][0])); + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; +} + +void verify_metadata_callback_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) +{ + (void)decoder, (void)metadata, (void)client_data; +} + +void verify_error_callback_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) +{ + FLAC__StreamEncoder *encoder = (FLAC__StreamEncoder*)client_data; + (void)decoder, (void)status; + encoder->protected_->state = FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR; +} + +FLAC__StreamEncoderReadStatus file_read_callback_(const FLAC__StreamEncoder *encoder, FLAC__byte buffer[], size_t *bytes, void *client_data) +{ + (void)client_data; + + *bytes = fread(buffer, 1, *bytes, encoder->private_->file); + if (*bytes == 0) { + if (feof(encoder->private_->file)) + return FLAC__STREAM_ENCODER_READ_STATUS_END_OF_STREAM; + else if (ferror(encoder->private_->file)) + return FLAC__STREAM_ENCODER_READ_STATUS_ABORT; + } + return FLAC__STREAM_ENCODER_READ_STATUS_CONTINUE; +} + +FLAC__StreamEncoderSeekStatus file_seek_callback_(const FLAC__StreamEncoder *encoder, FLAC__uint64 absolute_byte_offset, void *client_data) +{ + (void)client_data; + + if(fseeko(encoder->private_->file, (off_t)absolute_byte_offset, SEEK_SET) < 0) + return FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR; + else + return FLAC__STREAM_ENCODER_SEEK_STATUS_OK; +} + +FLAC__StreamEncoderTellStatus file_tell_callback_(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_byte_offset, void *client_data) +{ + off_t offset; + + (void)client_data; + + offset = ftello(encoder->private_->file); + + if(offset < 0) { + return FLAC__STREAM_ENCODER_TELL_STATUS_ERROR; + } + else { + *absolute_byte_offset = (FLAC__uint64)offset; + return FLAC__STREAM_ENCODER_TELL_STATUS_OK; + } +} + +#ifdef FLAC__VALGRIND_TESTING +static size_t local__fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) +{ + size_t ret = fwrite(ptr, size, nmemb, stream); + if(!ferror(stream)) + fflush(stream); + return ret; +} +#else +#define local__fwrite fwrite +#endif + +FLAC__StreamEncoderWriteStatus file_write_callback_(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame, void *client_data) +{ + (void)client_data, (void)current_frame; + + if(local__fwrite(buffer, sizeof(FLAC__byte), bytes, encoder->private_->file) == bytes) { + FLAC__bool call_it = 0 != encoder->private_->progress_callback && ( +#if FLAC__HAS_OGG + /* We would like to be able to use 'samples > 0' in the + * clause here but currently because of the nature of our + * Ogg writing implementation, 'samples' is always 0 (see + * ogg_encoder_aspect.c). The downside is extra progress + * callbacks. + */ + encoder->private_->is_ogg? true : +#endif + samples > 0 + ); + if(call_it) { + /* NOTE: We have to add +bytes, +samples, and +1 to the stats + * because at this point in the callback chain, the stats + * have not been updated. Only after we return and control + * gets back to write_frame_() are the stats updated + */ + encoder->private_->progress_callback(encoder, encoder->private_->bytes_written+bytes, encoder->private_->samples_written+samples, encoder->private_->frames_written+(samples?1:0), encoder->private_->total_frames_estimate, encoder->private_->client_data); + } + return FLAC__STREAM_ENCODER_WRITE_STATUS_OK; + } + else + return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR; +} + +/* + * This will forcibly set stdout to binary mode (for OSes that require it) + */ +FILE *get_binary_stdout_(void) +{ + /* if something breaks here it is probably due to the presence or + * absence of an underscore before the identifiers 'setmode', + * 'fileno', and/or 'O_BINARY'; check your system header files. + */ +#if defined _MSC_VER || defined __MINGW32__ + _setmode(_fileno(stdout), _O_BINARY); +#elif defined __CYGWIN__ + /* almost certainly not needed for any modern Cygwin, but let's be safe... */ + setmode(_fileno(stdout), _O_BINARY); +#elif defined __EMX__ + setmode(fileno(stdout), O_BINARY); +#endif + + return stdout; +} diff --git a/src/FLAC/src/libFLAC/stream_encoder_framing.c b/src/FLAC/src/libFLAC/stream_encoder_framing.c new file mode 100644 index 00000000..8b9558b1 --- /dev/null +++ b/src/FLAC/src/libFLAC/stream_encoder_framing.c @@ -0,0 +1,515 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include /* for strlen() */ +#include "private/stream_encoder_framing.h" +#include "private/crc.h" +#include "FLAC/assert.h" + +#ifdef max +#undef max +#endif +#define max(x,y) ((x)>(y)?(x):(y)) + +static FLAC__bool add_entropy_coding_method_(FLAC__BitWriter *bw, const FLAC__EntropyCodingMethod *method); +static FLAC__bool add_residual_partitioned_rice_(FLAC__BitWriter *bw, const FLAC__int32 residual[], const unsigned residual_samples, const unsigned predictor_order, const unsigned rice_parameters[], const unsigned raw_bits[], const unsigned partition_order); + +FLAC__bool FLAC__add_metadata_block(const FLAC__StreamMetadata *metadata, FLAC__BitWriter *bw) +{ + unsigned i, j; + const unsigned vendor_string_length = (unsigned)strlen(FLAC__VENDOR_STRING); + + if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->is_last, FLAC__STREAM_METADATA_IS_LAST_LEN)) + return false; + + if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->type, FLAC__STREAM_METADATA_TYPE_LEN)) + return false; + + /* + * First, for VORBIS_COMMENTs, adjust the length to reflect our vendor string + */ + i = metadata->length; + if(metadata->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) { + FLAC__ASSERT(metadata->data.vorbis_comment.vendor_string.length == 0 || 0 != metadata->data.vorbis_comment.vendor_string.entry); + i -= metadata->data.vorbis_comment.vendor_string.length; + i += vendor_string_length; + } + FLAC__ASSERT(i < (1u << FLAC__STREAM_METADATA_LENGTH_LEN)); + if(!FLAC__bitwriter_write_raw_uint32(bw, i, FLAC__STREAM_METADATA_LENGTH_LEN)) + return false; + + switch(metadata->type) { + case FLAC__METADATA_TYPE_STREAMINFO: + FLAC__ASSERT(metadata->data.stream_info.min_blocksize < (1u << FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN)); + if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.stream_info.min_blocksize, FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN)) + return false; + FLAC__ASSERT(metadata->data.stream_info.max_blocksize < (1u << FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN)); + if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.stream_info.max_blocksize, FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN)) + return false; + FLAC__ASSERT(metadata->data.stream_info.min_framesize < (1u << FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN)); + if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.stream_info.min_framesize, FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN)) + return false; + FLAC__ASSERT(metadata->data.stream_info.max_framesize < (1u << FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN)); + if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.stream_info.max_framesize, FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN)) + return false; + FLAC__ASSERT(FLAC__format_sample_rate_is_valid(metadata->data.stream_info.sample_rate)); + if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.stream_info.sample_rate, FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN)) + return false; + FLAC__ASSERT(metadata->data.stream_info.channels > 0); + FLAC__ASSERT(metadata->data.stream_info.channels <= (1u << FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN)); + if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.stream_info.channels-1, FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN)) + return false; + FLAC__ASSERT(metadata->data.stream_info.bits_per_sample > 0); + FLAC__ASSERT(metadata->data.stream_info.bits_per_sample <= (1u << FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN)); + if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.stream_info.bits_per_sample-1, FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN)) + return false; + if(!FLAC__bitwriter_write_raw_uint64(bw, metadata->data.stream_info.total_samples, FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN)) + return false; + if(!FLAC__bitwriter_write_byte_block(bw, metadata->data.stream_info.md5sum, 16)) + return false; + break; + case FLAC__METADATA_TYPE_PADDING: + if(!FLAC__bitwriter_write_zeroes(bw, metadata->length * 8)) + return false; + break; + case FLAC__METADATA_TYPE_APPLICATION: + if(!FLAC__bitwriter_write_byte_block(bw, metadata->data.application.id, FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8)) + return false; + if(!FLAC__bitwriter_write_byte_block(bw, metadata->data.application.data, metadata->length - (FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8))) + return false; + break; + case FLAC__METADATA_TYPE_SEEKTABLE: + for(i = 0; i < metadata->data.seek_table.num_points; i++) { + if(!FLAC__bitwriter_write_raw_uint64(bw, metadata->data.seek_table.points[i].sample_number, FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN)) + return false; + if(!FLAC__bitwriter_write_raw_uint64(bw, metadata->data.seek_table.points[i].stream_offset, FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN)) + return false; + if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.seek_table.points[i].frame_samples, FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN)) + return false; + } + break; + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + if(!FLAC__bitwriter_write_raw_uint32_little_endian(bw, vendor_string_length)) + return false; + if(!FLAC__bitwriter_write_byte_block(bw, (const FLAC__byte*)FLAC__VENDOR_STRING, vendor_string_length)) + return false; + if(!FLAC__bitwriter_write_raw_uint32_little_endian(bw, metadata->data.vorbis_comment.num_comments)) + return false; + for(i = 0; i < metadata->data.vorbis_comment.num_comments; i++) { + if(!FLAC__bitwriter_write_raw_uint32_little_endian(bw, metadata->data.vorbis_comment.comments[i].length)) + return false; + if(!FLAC__bitwriter_write_byte_block(bw, metadata->data.vorbis_comment.comments[i].entry, metadata->data.vorbis_comment.comments[i].length)) + return false; + } + break; + case FLAC__METADATA_TYPE_CUESHEET: + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN % 8 == 0); + if(!FLAC__bitwriter_write_byte_block(bw, (const FLAC__byte*)metadata->data.cue_sheet.media_catalog_number, FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN/8)) + return false; + if(!FLAC__bitwriter_write_raw_uint64(bw, metadata->data.cue_sheet.lead_in, FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN)) + return false; + if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.cue_sheet.is_cd? 1 : 0, FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN)) + return false; + if(!FLAC__bitwriter_write_zeroes(bw, FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN)) + return false; + if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.cue_sheet.num_tracks, FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN)) + return false; + for(i = 0; i < metadata->data.cue_sheet.num_tracks; i++) { + const FLAC__StreamMetadata_CueSheet_Track *track = metadata->data.cue_sheet.tracks + i; + + if(!FLAC__bitwriter_write_raw_uint64(bw, track->offset, FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN)) + return false; + if(!FLAC__bitwriter_write_raw_uint32(bw, track->number, FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN)) + return false; + FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN % 8 == 0); + if(!FLAC__bitwriter_write_byte_block(bw, (const FLAC__byte*)track->isrc, FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN/8)) + return false; + if(!FLAC__bitwriter_write_raw_uint32(bw, track->type, FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN)) + return false; + if(!FLAC__bitwriter_write_raw_uint32(bw, track->pre_emphasis, FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN)) + return false; + if(!FLAC__bitwriter_write_zeroes(bw, FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN)) + return false; + if(!FLAC__bitwriter_write_raw_uint32(bw, track->num_indices, FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN)) + return false; + for(j = 0; j < track->num_indices; j++) { + const FLAC__StreamMetadata_CueSheet_Index *index = track->indices + j; + + if(!FLAC__bitwriter_write_raw_uint64(bw, index->offset, FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN)) + return false; + if(!FLAC__bitwriter_write_raw_uint32(bw, index->number, FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN)) + return false; + if(!FLAC__bitwriter_write_zeroes(bw, FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN)) + return false; + } + } + break; + case FLAC__METADATA_TYPE_PICTURE: + { + size_t len; + if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.picture.type, FLAC__STREAM_METADATA_PICTURE_TYPE_LEN)) + return false; + len = strlen(metadata->data.picture.mime_type); + if(!FLAC__bitwriter_write_raw_uint32(bw, len, FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN)) + return false; + if(!FLAC__bitwriter_write_byte_block(bw, (const FLAC__byte*)metadata->data.picture.mime_type, len)) + return false; + len = strlen((const char *)metadata->data.picture.description); + if(!FLAC__bitwriter_write_raw_uint32(bw, len, FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN)) + return false; + if(!FLAC__bitwriter_write_byte_block(bw, metadata->data.picture.description, len)) + return false; + if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.picture.width, FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN)) + return false; + if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.picture.height, FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN)) + return false; + if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.picture.depth, FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN)) + return false; + if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.picture.colors, FLAC__STREAM_METADATA_PICTURE_COLORS_LEN)) + return false; + if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.picture.data_length, FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN)) + return false; + if(!FLAC__bitwriter_write_byte_block(bw, metadata->data.picture.data, metadata->data.picture.data_length)) + return false; + } + break; + default: + if(!FLAC__bitwriter_write_byte_block(bw, metadata->data.unknown.data, metadata->length)) + return false; + break; + } + + FLAC__ASSERT(FLAC__bitwriter_is_byte_aligned(bw)); + return true; +} + +FLAC__bool FLAC__frame_add_header(const FLAC__FrameHeader *header, FLAC__BitWriter *bw) +{ + unsigned u, blocksize_hint, sample_rate_hint; + FLAC__byte crc; + + FLAC__ASSERT(FLAC__bitwriter_is_byte_aligned(bw)); + + if(!FLAC__bitwriter_write_raw_uint32(bw, FLAC__FRAME_HEADER_SYNC, FLAC__FRAME_HEADER_SYNC_LEN)) + return false; + + if(!FLAC__bitwriter_write_raw_uint32(bw, 0, FLAC__FRAME_HEADER_RESERVED_LEN)) + return false; + + FLAC__ASSERT(header->blocksize > 0 && header->blocksize <= FLAC__MAX_BLOCK_SIZE); + /* when this assertion holds true, any legal blocksize can be expressed in the frame header */ + FLAC__ASSERT(FLAC__MAX_BLOCK_SIZE <= 65535u); + blocksize_hint = 0; + switch(header->blocksize) { + case 192: u = 1; break; + case 576: u = 2; break; + case 1152: u = 3; break; + case 2304: u = 4; break; + case 4608: u = 5; break; + case 256: u = 8; break; + case 512: u = 9; break; + case 1024: u = 10; break; + case 2048: u = 11; break; + case 4096: u = 12; break; + case 8192: u = 13; break; + case 16384: u = 14; break; + case 32768: u = 15; break; + default: + if(header->blocksize <= 0x100) + blocksize_hint = u = 6; + else if(header->blocksize <= 0x10000) + blocksize_hint = u = 7; + else + u = 0; + break; + } + if(!FLAC__bitwriter_write_raw_uint32(bw, u, FLAC__FRAME_HEADER_BLOCK_SIZE_LEN)) + return false; + + FLAC__ASSERT(FLAC__format_sample_rate_is_valid(header->sample_rate)); + sample_rate_hint = 0; + switch(header->sample_rate) { + case 8000: u = 4; break; + case 16000: u = 5; break; + case 22050: u = 6; break; + case 24000: u = 7; break; + case 32000: u = 8; break; + case 44100: u = 9; break; + case 48000: u = 10; break; + case 96000: u = 11; break; + default: + if(header->sample_rate <= 255000 && header->sample_rate % 1000 == 0) + sample_rate_hint = u = 12; + else if(header->sample_rate % 10 == 0) + sample_rate_hint = u = 14; + else if(header->sample_rate <= 0xffff) + sample_rate_hint = u = 13; + else + u = 0; + break; + } + if(!FLAC__bitwriter_write_raw_uint32(bw, u, FLAC__FRAME_HEADER_SAMPLE_RATE_LEN)) + return false; + + FLAC__ASSERT(header->channels > 0 && header->channels <= (1u << FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN) && header->channels <= FLAC__MAX_CHANNELS); + switch(header->channel_assignment) { + case FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT: + u = header->channels - 1; + break; + case FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE: + FLAC__ASSERT(header->channels == 2); + u = 8; + break; + case FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE: + FLAC__ASSERT(header->channels == 2); + u = 9; + break; + case FLAC__CHANNEL_ASSIGNMENT_MID_SIDE: + FLAC__ASSERT(header->channels == 2); + u = 10; + break; + default: + FLAC__ASSERT(0); + } + if(!FLAC__bitwriter_write_raw_uint32(bw, u, FLAC__FRAME_HEADER_CHANNEL_ASSIGNMENT_LEN)) + return false; + + FLAC__ASSERT(header->bits_per_sample > 0 && header->bits_per_sample <= (1u << FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN)); + switch(header->bits_per_sample) { + case 8 : u = 1; break; + case 12: u = 2; break; + case 16: u = 4; break; + case 20: u = 5; break; + case 24: u = 6; break; + default: u = 0; break; + } + if(!FLAC__bitwriter_write_raw_uint32(bw, u, FLAC__FRAME_HEADER_BITS_PER_SAMPLE_LEN)) + return false; + + if(!FLAC__bitwriter_write_raw_uint32(bw, 0, FLAC__FRAME_HEADER_ZERO_PAD_LEN)) + return false; + + FLAC__ASSERT(header->number_type == FLAC__FRAME_NUMBER_TYPE_FRAME_NUMBER); + if(!FLAC__bitwriter_write_utf8_uint32(bw, header->number.frame_number)) + return false; + + if(blocksize_hint) + if(!FLAC__bitwriter_write_raw_uint32(bw, header->blocksize-1, (blocksize_hint==6)? 8:16)) + return false; + + switch(sample_rate_hint) { + case 12: + if(!FLAC__bitwriter_write_raw_uint32(bw, header->sample_rate / 1000, 8)) + return false; + break; + case 13: + if(!FLAC__bitwriter_write_raw_uint32(bw, header->sample_rate, 16)) + return false; + break; + case 14: + if(!FLAC__bitwriter_write_raw_uint32(bw, header->sample_rate / 10, 16)) + return false; + break; + } + + /* write the CRC */ + if(!FLAC__bitwriter_get_write_crc8(bw, &crc)) + return false; + if(!FLAC__bitwriter_write_raw_uint32(bw, crc, FLAC__FRAME_HEADER_CRC_LEN)) + return false; + + return true; +} + +FLAC__bool FLAC__subframe_add_constant(const FLAC__Subframe_Constant *subframe, unsigned subframe_bps, unsigned wasted_bits, FLAC__BitWriter *bw) +{ + FLAC__bool ok; + + ok = + FLAC__bitwriter_write_raw_uint32(bw, FLAC__SUBFRAME_TYPE_CONSTANT_BYTE_ALIGNED_MASK | (wasted_bits? 1:0), FLAC__SUBFRAME_ZERO_PAD_LEN + FLAC__SUBFRAME_TYPE_LEN + FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN) && + (wasted_bits? FLAC__bitwriter_write_unary_unsigned(bw, wasted_bits-1) : true) && + FLAC__bitwriter_write_raw_int32(bw, subframe->value, subframe_bps) + ; + + return ok; +} + +FLAC__bool FLAC__subframe_add_fixed(const FLAC__Subframe_Fixed *subframe, unsigned residual_samples, unsigned subframe_bps, unsigned wasted_bits, FLAC__BitWriter *bw) +{ + unsigned i; + + if(!FLAC__bitwriter_write_raw_uint32(bw, FLAC__SUBFRAME_TYPE_FIXED_BYTE_ALIGNED_MASK | (subframe->order<<1) | (wasted_bits? 1:0), FLAC__SUBFRAME_ZERO_PAD_LEN + FLAC__SUBFRAME_TYPE_LEN + FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN)) + return false; + if(wasted_bits) + if(!FLAC__bitwriter_write_unary_unsigned(bw, wasted_bits-1)) + return false; + + for(i = 0; i < subframe->order; i++) + if(!FLAC__bitwriter_write_raw_int32(bw, subframe->warmup[i], subframe_bps)) + return false; + + if(!add_entropy_coding_method_(bw, &subframe->entropy_coding_method)) + return false; + switch(subframe->entropy_coding_method.type) { + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE: + if(!add_residual_partitioned_rice_(bw, subframe->residual, residual_samples, subframe->order, subframe->entropy_coding_method.data.partitioned_rice.contents->parameters, subframe->entropy_coding_method.data.partitioned_rice.contents->raw_bits, subframe->entropy_coding_method.data.partitioned_rice.order)) + return false; + break; + default: + FLAC__ASSERT(0); + } + + return true; +} + +FLAC__bool FLAC__subframe_add_lpc(const FLAC__Subframe_LPC *subframe, unsigned residual_samples, unsigned subframe_bps, unsigned wasted_bits, FLAC__BitWriter *bw) +{ + unsigned i; + + if(!FLAC__bitwriter_write_raw_uint32(bw, FLAC__SUBFRAME_TYPE_LPC_BYTE_ALIGNED_MASK | ((subframe->order-1)<<1) | (wasted_bits? 1:0), FLAC__SUBFRAME_ZERO_PAD_LEN + FLAC__SUBFRAME_TYPE_LEN + FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN)) + return false; + if(wasted_bits) + if(!FLAC__bitwriter_write_unary_unsigned(bw, wasted_bits-1)) + return false; + + for(i = 0; i < subframe->order; i++) + if(!FLAC__bitwriter_write_raw_int32(bw, subframe->warmup[i], subframe_bps)) + return false; + + if(!FLAC__bitwriter_write_raw_uint32(bw, subframe->qlp_coeff_precision-1, FLAC__SUBFRAME_LPC_QLP_COEFF_PRECISION_LEN)) + return false; + if(!FLAC__bitwriter_write_raw_int32(bw, subframe->quantization_level, FLAC__SUBFRAME_LPC_QLP_SHIFT_LEN)) + return false; + for(i = 0; i < subframe->order; i++) + if(!FLAC__bitwriter_write_raw_int32(bw, subframe->qlp_coeff[i], subframe->qlp_coeff_precision)) + return false; + + if(!add_entropy_coding_method_(bw, &subframe->entropy_coding_method)) + return false; + switch(subframe->entropy_coding_method.type) { + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE: + if(!add_residual_partitioned_rice_(bw, subframe->residual, residual_samples, subframe->order, subframe->entropy_coding_method.data.partitioned_rice.contents->parameters, subframe->entropy_coding_method.data.partitioned_rice.contents->raw_bits, subframe->entropy_coding_method.data.partitioned_rice.order)) + return false; + break; + default: + FLAC__ASSERT(0); + } + + return true; +} + +FLAC__bool FLAC__subframe_add_verbatim(const FLAC__Subframe_Verbatim *subframe, unsigned samples, unsigned subframe_bps, unsigned wasted_bits, FLAC__BitWriter *bw) +{ + unsigned i; + const FLAC__int32 *signal = subframe->data; + + if(!FLAC__bitwriter_write_raw_uint32(bw, FLAC__SUBFRAME_TYPE_VERBATIM_BYTE_ALIGNED_MASK | (wasted_bits? 1:0), FLAC__SUBFRAME_ZERO_PAD_LEN + FLAC__SUBFRAME_TYPE_LEN + FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN)) + return false; + if(wasted_bits) + if(!FLAC__bitwriter_write_unary_unsigned(bw, wasted_bits-1)) + return false; + + for(i = 0; i < samples; i++) + if(!FLAC__bitwriter_write_raw_int32(bw, signal[i], subframe_bps)) + return false; + + return true; +} + +FLAC__bool add_entropy_coding_method_(FLAC__BitWriter *bw, const FLAC__EntropyCodingMethod *method) +{ + if(!FLAC__bitwriter_write_raw_uint32(bw, method->type, FLAC__ENTROPY_CODING_METHOD_TYPE_LEN)) + return false; + switch(method->type) { + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE: + if(!FLAC__bitwriter_write_raw_uint32(bw, method->data.partitioned_rice.order, FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN)) + return false; + break; + default: + FLAC__ASSERT(0); + } + return true; +} + +FLAC__bool add_residual_partitioned_rice_(FLAC__BitWriter *bw, const FLAC__int32 residual[], const unsigned residual_samples, const unsigned predictor_order, const unsigned rice_parameters[], const unsigned raw_bits[], const unsigned partition_order) +{ + if(partition_order == 0) { + unsigned i; + + if(!FLAC__bitwriter_write_raw_uint32(bw, rice_parameters[0], FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN)) + return false; + if(rice_parameters[0] < FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER) { + if(!FLAC__bitwriter_write_rice_signed_block(bw, residual, residual_samples, rice_parameters[0])) + return false; + } + else { + if(!FLAC__bitwriter_write_raw_uint32(bw, raw_bits[0], FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_RAW_LEN)) + return false; + for(i = 0; i < residual_samples; i++) { + if(!FLAC__bitwriter_write_raw_int32(bw, residual[i], raw_bits[0])) + return false; + } + } + return true; + } + else { + unsigned i, j, k = 0, k_last = 0; + unsigned partition_samples; + const unsigned default_partition_samples = (residual_samples+predictor_order) >> partition_order; + for(i = 0; i < (1u< +#endif + +#include +#include "FLAC/assert.h" +#include "FLAC/format.h" +#include "private/window.h" + +#ifndef FLAC__INTEGER_ONLY_LIBRARY + +#ifndef M_PI +/* math.h in VC++ doesn't seem to have this (how Microsoft is that?) */ +#define M_PI 3.14159265358979323846 +#endif + + +void FLAC__window_bartlett(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + if (L & 1) { + for (n = 0; n <= N/2; n++) + window[n] = 2.0f * n / (float)N; + for (; n <= N; n++) + window[n] = 2.0f - 2.0f * n / (float)N; + } + else { + for (n = 0; n <= L/2-1; n++) + window[n] = 2.0f * n / (float)N; + for (; n <= N; n++) + window[n] = 2.0f - 2.0f * (N-n) / (float)N; + } +} + +void FLAC__window_bartlett_hann(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + for (n = 0; n < L; n++) + window[n] = (FLAC__real)(0.62f - 0.48f * fabs((float)n/(float)N+0.5f) + 0.38f * cos(2.0f * M_PI * ((float)n/(float)N+0.5f))); +} + +void FLAC__window_blackman(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + for (n = 0; n < L; n++) + window[n] = (FLAC__real)(0.42f - 0.5f * cos(2.0f * M_PI * n / N) + 0.08f * cos(4.0f * M_PI * n / N)); +} + +/* 4-term -92dB side-lobe */ +void FLAC__window_blackman_harris_4term_92db_sidelobe(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + for (n = 0; n <= N; n++) + window[n] = (FLAC__real)(0.35875f - 0.48829f * cos(2.0f * M_PI * n / N) + 0.14128f * cos(4.0f * M_PI * n / N) - 0.01168f * cos(6.0f * M_PI * n / N)); +} + +void FLAC__window_connes(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + const double N2 = (double)N / 2.; + FLAC__int32 n; + + for (n = 0; n <= N; n++) { + double k = ((double)n - N2) / N2; + k = 1.0f - k * k; + window[n] = (FLAC__real)(k * k); + } +} + +void FLAC__window_flattop(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + for (n = 0; n < L; n++) + window[n] = (FLAC__real)(1.0f - 1.93f * cos(2.0f * M_PI * n / N) + 1.29f * cos(4.0f * M_PI * n / N) - 0.388f * cos(6.0f * M_PI * n / N) + 0.0322f * cos(8.0f * M_PI * n / N)); +} + +void FLAC__window_gauss(FLAC__real *window, const FLAC__int32 L, const FLAC__real stddev) +{ + const FLAC__int32 N = L - 1; + const double N2 = (double)N / 2.; + FLAC__int32 n; + + for (n = 0; n <= N; n++) { + const double k = ((double)n - N2) / (stddev * N2); + window[n] = (FLAC__real)exp(-0.5f * k * k); + } +} + +void FLAC__window_hamming(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + for (n = 0; n < L; n++) + window[n] = (FLAC__real)(0.54f - 0.46f * cos(2.0f * M_PI * n / N)); +} + +void FLAC__window_hann(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + for (n = 0; n < L; n++) + window[n] = (FLAC__real)(0.5f - 0.5f * cos(2.0f * M_PI * n / N)); +} + +void FLAC__window_kaiser_bessel(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + for (n = 0; n < L; n++) + window[n] = (FLAC__real)(0.402f - 0.498f * cos(2.0f * M_PI * n / N) + 0.098f * cos(4.0f * M_PI * n / N) - 0.001f * cos(6.0f * M_PI * n / N)); +} + +void FLAC__window_nuttall(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + for (n = 0; n < L; n++) + window[n] = (FLAC__real)(0.3635819f - 0.4891775f*cos(2.0f*M_PI*n/N) + 0.1365995f*cos(4.0f*M_PI*n/N) - 0.0106411f*cos(6.0f*M_PI*n/N)); +} + +void FLAC__window_rectangle(FLAC__real *window, const FLAC__int32 L) +{ + FLAC__int32 n; + + for (n = 0; n < L; n++) + window[n] = 1.0f; +} + +void FLAC__window_triangle(FLAC__real *window, const FLAC__int32 L) +{ + FLAC__int32 n; + + if (L & 1) { + for (n = 1; n <= L+1/2; n++) + window[n-1] = 2.0f * n / ((float)L + 1.0f); + for (; n <= L; n++) + window[n-1] = - (float)(2 * (L - n + 1)) / ((float)L + 1.0f); + } + else { + for (n = 1; n <= L/2; n++) + window[n-1] = 2.0f * n / (float)L; + for (; n <= L; n++) + window[n-1] = ((float)(2 * (L - n)) + 1.0f) / (float)L; + } +} + +void FLAC__window_tukey(FLAC__real *window, const FLAC__int32 L, const FLAC__real p) +{ + if (p <= 0.0) + FLAC__window_rectangle(window, L); + else if (p >= 1.0) + FLAC__window_hann(window, L); + else { + const FLAC__int32 Np = (FLAC__int32)(p / 2.0f * L) - 1; + FLAC__int32 n; + /* start with rectangle... */ + FLAC__window_rectangle(window, L); + /* ...replace ends with hann */ + if (Np > 0) { + for (n = 0; n <= Np; n++) { + window[n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * n / Np)); + window[L-Np-1+n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * (n+Np) / Np)); + } + } + } +} + +void FLAC__window_welch(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + const double N2 = (double)N / 2.; + FLAC__int32 n; + + for (n = 0; n <= N; n++) { + const double k = ((double)n - N2) / N2; + window[n] = (FLAC__real)(1.0f - k * k); + } +} + +#endif /* !defined FLAC__INTEGER_ONLY_LIBRARY */ diff --git a/src/FLAC/src/monkeys_audio_utilities/Makefile.am b/src/FLAC/src/monkeys_audio_utilities/Makefile.am new file mode 100644 index 00000000..3cfe69af --- /dev/null +++ b/src/FLAC/src/monkeys_audio_utilities/Makefile.am @@ -0,0 +1,18 @@ +# FLAC - Free Lossless Audio Codec +# Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson +# +# This file is part the FLAC project. FLAC is comprised of several +# components distributed under difference licenses. The codec libraries +# are distributed under Xiph.Org's BSD-like license (see the file +# COPYING.Xiph in this distribution). All other programs, libraries, and +# plugins are distributed under the GPL (see COPYING.GPL). The documentation +# is distributed under the Gnu FDL (see COPYING.FDL). Each file in the +# FLAC distribution contains at the top the terms under which it may be +# distributed. +# +# Since this particular file is relevant to all components of FLAC, +# it may be distributed under the Xiph.Org license, which is the least +# restrictive of those mentioned above. See the file COPYING.Xiph in this +# distribution. + +SUBDIRS = flac_mac flac_ren diff --git a/src/FLAC/src/monkeys_audio_utilities/flac_mac/Makefile.am b/src/FLAC/src/monkeys_audio_utilities/flac_mac/Makefile.am new file mode 100644 index 00000000..9b03e485 --- /dev/null +++ b/src/FLAC/src/monkeys_audio_utilities/flac_mac/Makefile.am @@ -0,0 +1,19 @@ +# flac_mac - wedge utility to add FLAC support to Monkey's Audio +# Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +EXTRA_DIST = \ + main.c diff --git a/src/FLAC/src/monkeys_audio_utilities/flac_mac/main.c b/src/FLAC/src/monkeys_audio_utilities/flac_mac/main.c new file mode 100644 index 00000000..99109c0b --- /dev/null +++ b/src/FLAC/src/monkeys_audio_utilities/flac_mac/main.c @@ -0,0 +1,208 @@ +/* flac_mac - wedge utility to add FLAC support to Monkey's Audio + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * This program can be used to allow FLAC to masquerade as one of the other + * supported lossless codecs in Monkey's Audio. See the documentation for + * how to do this. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include + +static int execit(char *prog, char *args); +static int forkit(char *prog, char *args); + +int main(int argc, char *argv[]) +{ + int flac_return_val = 0, opt_arg = 1, from_arg = -1, to_arg = -1, flac_level = 5, i; + char prog[MAX_PATH], cmdline[MAX_PATH*2], from[MAX_PATH], to[MAX_PATH], macdir[MAX_PATH], options[256], *p; + enum { WAVPACK, RKAU, SHORTEN } codec; + + /* get the directory where MAC external codecs reside */ + if(0 != (p = strrchr(argv[0],'\\'))) { + strcpy(macdir, argv[0]); + *(strrchr(macdir,'\\')+1) = '\0'; + } + else { + strcpy(macdir, ""); + } + + /* determine which codec we were called as and parse the options */ + if(p == 0) + p = argv[0]; + else + p++; + if(0 == strnicmp(p, "short", 5)) { + codec = SHORTEN; + } + else if(0 == strnicmp(p, "rkau", 4)) { + codec = RKAU; + if(argv[1][0] == '-' && argv[1][1] == 'l') { + opt_arg = 2; + switch(argv[1][2]) { + case '1': flac_level = 1; break; + case '2': flac_level = 5; break; + case '3': flac_level = 8; break; + } + } + } + else if(0 == strnicmp(p, "wavpack", 7)) { + codec = WAVPACK; + if(argv[1][0] == '-') { + opt_arg = 2; + switch(argv[1][1]) { + case 'f': flac_level = 1; break; + case 'h': flac_level = 8; break; + default: opt_arg = 1; + } + } + } + else { + return -5; + } + + /* figure out which arguments are the source and destination files */ + for(i = 1; i < argc; i++) + if(argv[i][0] != '-') { + from_arg = i++; + break; + } + for( ; i < argc; i++) + if(argv[i][0] != '-') { + to_arg = i++; + break; + } + if(to_arg < 0) + return -4; + + /* build the command to call flac with */ + sprintf(prog, "%sflac.exe", macdir); + sprintf(options, "-%d", flac_level); + for(i = opt_arg; i < argc; i++) + if(argv[i][0] == '-') { + strcat(options, " "); + strcat(options, argv[i]); + } + sprintf(cmdline, "\"%s\" %s -o \"%s\" \"%s\"", prog, options, argv[to_arg], argv[from_arg]); + + flac_return_val = execit(prog, cmdline); + + /* + * Now that flac has finished, we need to fork a process that will rename + * the resulting file with the correct extension once MAC has moved it to + * it's final resting place. + */ + if(0 == flac_return_val) { + /* get the destination directory, if any */ + if(0 != (p = strchr(argv[to_arg],'\\'))) { + strcpy(from, argv[to_arg]); + *(strrchr(from,'\\')+1) = '\0'; + } + else { + strcpy(from, ""); + } + + /* for the full 'from' and 'to' paths for the renamer process */ + p = strrchr(argv[from_arg],'\\'); + strcat(from, p? p+1 : argv[from_arg]); + strcpy(to, from); + if(0 == strchr(from,'.')) + return -3; + switch(codec) { + case SHORTEN: strcpy(strrchr(from,'.'), ".shn"); break; + case WAVPACK: strcpy(strrchr(from,'.'), ".wv"); break; + case RKAU: strcpy(strrchr(from,'.'), ".rka"); break; + } + strcpy(strrchr(to,'.'), ".flac"); + + sprintf(prog, "%sflac_ren.exe", macdir); + sprintf(cmdline, "\"%s\" \"%s\" \"%s\"", prog, from, to); + + flac_return_val = forkit(prog, cmdline); + } + + return flac_return_val; +} + +int execit(char *prog, char *args) +{ + BOOL ok; + STARTUPINFO startup_info; + PROCESS_INFORMATION proc_info; + + GetStartupInfo(&startup_info); + + ok = CreateProcess( + prog, + args, + 0, /*process security attributes*/ + 0, /*thread security attributes*/ + FALSE, + 0, /*dwCreationFlags*/ + 0, /*environment*/ + 0, /*lpCurrentDirectory*/ + &startup_info, + &proc_info + ); + if(ok) { + DWORD dw; + dw = WaitForSingleObject(proc_info.hProcess, INFINITE); + ok = (dw != 0xFFFFFFFF); + CloseHandle(proc_info.hThread); + CloseHandle(proc_info.hProcess); + } + + return ok? 0 : -1; +} + +int forkit(char *prog, char *args) +{ + BOOL ok; + STARTUPINFO startup_info; + PROCESS_INFORMATION proc_info; + + GetStartupInfo(&startup_info); + + ok = CreateProcess( + prog, + args, + 0, /*process security attributes*/ + 0, /*thread security attributes*/ + FALSE, + DETACHED_PROCESS, /*dwCreationFlags*/ + 0, /*environment*/ + 0, /*lpCurrentDirectory*/ + &startup_info, + &proc_info + ); + if(ok) { + CloseHandle(proc_info.hThread); + CloseHandle(proc_info.hProcess); + } + + return ok? 0 : -2; +} diff --git a/src/FLAC/src/monkeys_audio_utilities/flac_ren/Makefile.am b/src/FLAC/src/monkeys_audio_utilities/flac_ren/Makefile.am new file mode 100644 index 00000000..57c6a7c5 --- /dev/null +++ b/src/FLAC/src/monkeys_audio_utilities/flac_ren/Makefile.am @@ -0,0 +1,19 @@ +# flac_ren - renamer part of utility to add FLAC support to Monkey's Audio +# Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +EXTRA_DIST = \ + main.c diff --git a/src/FLAC/src/monkeys_audio_utilities/flac_ren/main.c b/src/FLAC/src/monkeys_audio_utilities/flac_ren/main.c new file mode 100644 index 00000000..fa849836 --- /dev/null +++ b/src/FLAC/src/monkeys_audio_utilities/flac_ren/main.c @@ -0,0 +1,39 @@ +/* flac_ren - renamer part of utility to add FLAC support to Monkey's Audio + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + struct stat s; + + /* wait till the 'from' file has reached its final destination */ + do { + Sleep(2000); + } while(stat(argv[1], &s) < 0); + + /* now rename it */ + return rename(argv[1], argv[2]); +} diff --git a/src/FLAC/src/share/Makefile.am b/src/FLAC/src/share/Makefile.am new file mode 100644 index 00000000..3ef99608 --- /dev/null +++ b/src/FLAC/src/share/Makefile.am @@ -0,0 +1,19 @@ +# FLAC - Free Lossless Audio Codec +# Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson +# +# This file is part the FLAC project. FLAC is comprised of several +# components distributed under difference licenses. The codec libraries +# are distributed under Xiph.Org's BSD-like license (see the file +# COPYING.Xiph in this distribution). All other programs, libraries, and +# plugins are distributed under the GPL (see COPYING.GPL). The documentation +# is distributed under the Gnu FDL (see COPYING.FDL). Each file in the +# FLAC distribution contains at the top the terms under which it may be +# distributed. +# +# Since this particular file is relevant to all components of FLAC, +# it may be distributed under the Xiph.Org license, which is the least +# restrictive of those mentioned above. See the file COPYING.Xiph in this +# distribution. + +SUBDIRS = getopt replaygain_anal replaygain_syn grabbag utf8 + diff --git a/src/FLAC/src/share/README b/src/FLAC/src/share/README new file mode 100644 index 00000000..1d4feded --- /dev/null +++ b/src/FLAC/src/share/README @@ -0,0 +1,5 @@ +This directory contains several convenience libraries used by the rest of the +tools and plugins. Two of them (getopt and utf8) are shamelessly copied from +vorbistools, one for manipulating UTF-8 strings (GPL) and one for implementing +getopt (LGPL). libFLAC does not link to either; the only FLAC tools that do +are GPL'ed. diff --git a/src/FLAC/src/share/getopt/Makefile.am b/src/FLAC/src/share/getopt/Makefile.am new file mode 100644 index 00000000..5464c831 --- /dev/null +++ b/src/FLAC/src/share/getopt/Makefile.am @@ -0,0 +1,15 @@ +## Process this file with automake to produce Makefile.in + +AUTOMAKE_OPTIONS = foreign + +INCLUDES = -I$(top_srcdir)/src/FLAC/include -I$(top_srcdir)/src/FLAC/include + +noinst_LIBRARIES = libgetopt.a + +libgetopt_a_SOURCES = getopt.c getopt1.c + +debug: + $(MAKE) all CFLAGS="@DEBUG@" + +profile: + $(MAKE) all CFLAGS="@PROFILE@" diff --git a/src/FLAC/src/share/getopt/getopt.c b/src/FLAC/src/share/getopt/getopt.c new file mode 100644 index 00000000..48e3c6e6 --- /dev/null +++ b/src/FLAC/src/share/getopt/getopt.c @@ -0,0 +1,1064 @@ +/* + NOTE: + I cannot get the vanilla getopt code to work (i.e. compile only what + is needed and not duplicate symbols found in the standard library) + on all the platforms that FLAC supports. In particular the gating + of code with the ELIDE_CODE #define is not accurate enough on systems + that are POSIX but not glibc. If someone has a patch that works on + GNU/Linux, Darwin, AND Solaris please submit it on the project page: + http://sourceforge.net/projects/flac + + In the meantime I have munged the global symbols and removed gates + around code, while at the same time trying to touch the original as + little as possible. +*/ +/* 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 + +#if HAVE_CONFIG_H +# include +#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 + +#if 1 +/*[JEC] was:#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 `share__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 `share__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 "share/getopt.h" +/*[JEC] was:#include "getopt.h"*/ + +/* For communication from `share__getopt' to the caller. + When `share__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 *share__optarg = 0; /*[JEC] initialize to avoid being a 'Common' symbol */ + +/* 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 `share__getopt'. + + On entry to `share__getopt', zero means this is the first call; initialize. + + When `share__getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `share__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 share__optind = 1; + +/* Formerly, initialization of getopt depended on share__optind==0, which + causes problems with re-calling getopt as programs generally don't + know that. */ + +static int share____getopt_initialized = 0; + +/* 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 share__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 share__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 `share__getopt' to return -1 with `share__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 (const char *); +#endif + +static const char * +my_index (const char *str, int chr) +{ + while (*str) + { + if (*str == chr) + return 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 **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,share__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 = share__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 += (share__optind - last_nonopt); + last_nonopt = share__optind; +} + +/* Initialize the internal data when the first call is made. */ + +#if defined __STDC__ && __STDC__ +static const char *share___getopt_initialize (int, char **, const char *); +#endif +static const char * +share___getopt_initialize (argc, argv, optstring) + int argc; + char **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 = share__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; +#else + (void)argc, (void)argv; +#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 `share__getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `share__getopt' finds another option character, it returns that character, + updating `share__optind' and `nextchar' so that the next call to `share__getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `share__getopt' returns -1. + Then `share__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 `share__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 `share__optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `share__optarg', otherwise `share__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 `share__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 share__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 +share___getopt_internal (argc, argv, optstring, longopts, longind, long_only) + int argc; + char **argv; + const char *optstring; + const struct share__option *longopts; + int *longind; + int long_only; +{ + share__optarg = NULL; + + if (share__optind == 0 || !share____getopt_initialized) + { + if (share__optind == 0) + share__optind = 1; /* Don't scan ARGV[0], the program name. */ + optstring = share___getopt_initialize (argc, argv, optstring); + share____getopt_initialized = 1; + } + + /* Test whether ARGV[share__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[share__optind][0] != '-' || argv[share__optind][1] == '\0' \ + || (share__optind < nonoption_flags_len \ + && __getopt_nonoption_flags[share__optind] == '1')) +#else +# define NONOPTION_P (argv[share__optind][0] != '-' || argv[share__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 > share__optind) + last_nonopt = share__optind; + if (first_nonopt > share__optind) + first_nonopt = share__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 != share__optind) + exchange ((char **) argv); + else if (last_nonopt != share__optind) + first_nonopt = share__optind; + + /* Skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (share__optind < argc && NONOPTION_P) + share__optind++; + last_nonopt = share__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 (share__optind != argc && !strcmp (argv[share__optind], "--")) + { + share__optind++; + + if (first_nonopt != last_nonopt && last_nonopt != share__optind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = share__optind; + last_nonopt = argc; + + share__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 (share__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) + share__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; + share__optarg = argv[share__optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Skip the initial punctuation. */ + + nextchar = (argv[share__optind] + 1 + + (longopts != NULL && argv[share__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[share__optind][1] == '-' + || (long_only && (argv[share__optind][2] || !my_index (optstring, argv[share__optind][1]))))) + { + char *nameend; + const struct share__option *p; + const struct share__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 (share__opterr) + fprintf (stderr, _("%s: option `%s' is ambiguous\n"), + argv[0], argv[share__optind]); + nextchar += strlen (nextchar); + share__optind++; + share__optopt = 0; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + share__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) + share__optarg = nameend + 1; + else + { + if (share__opterr) + { + if (argv[share__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[share__optind - 1][0], pfound->name); + } + + nextchar += strlen (nextchar); + + share__optopt = pfound->val; + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (share__optind < argc) + share__optarg = argv[share__optind++]; + else + { + if (share__opterr) + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[share__optind - 1]); + nextchar += strlen (nextchar); + share__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 share__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[share__optind][1] == '-' + || my_index (optstring, *nextchar) == NULL) + { + static char empty_string [1] = { 0 } ; + if (share__opterr) + { + if (argv[share__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[share__optind][0], nextchar); + } + nextchar = empty_string; + share__optind++; + share__optopt = 0; + return '?'; + } + } + + /* Look at and handle the next short option-character. */ + + { + char c = *nextchar++; + const char *temp = my_index (optstring, c); + + /* Increment `share__optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++share__optind; + + if (temp == NULL || c == ':') + { + if (share__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); + } + share__optopt = c; + return '?'; + } + /* Convenience. Treat POSIX -W foo same as long option --foo */ + if (temp[0] == 'W' && temp[1] == ';') + { + char *nameend; + const struct share__option *p; + const struct share__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') + { + share__optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + share__optind++; + } + else if (share__optind == argc) + { + if (share__opterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + share__optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + return c; + } + else + /* We already incremented `share__optind' once; + increment it again when taking next ARGV-elt as argument. */ + share__optarg = argv[share__optind++]; + + /* share__optarg is now the argument, see if it's in the + table of longopts. */ + + for (nextchar = nameend = share__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 (share__opterr) + fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"), + argv[0], argv[share__optind]); + nextchar += strlen (nextchar); + share__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) + share__optarg = nameend + 1; + else + { + if (share__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 (share__optind < argc) + share__optarg = argv[share__optind++]; + else + { + if (share__opterr) + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[share__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') + { + share__optarg = nextchar; + share__optind++; + } + else + share__optarg = NULL; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + share__optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + share__optind++; + } + else if (share__optind == argc) + { + if (share__opterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, + _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + share__optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `share__optind' once; + increment it again when taking next ARGV-elt as argument. */ + share__optarg = argv[share__optind++]; + nextchar = NULL; + } + } + return c; + } +} + +int +share__getopt (argc, argv, optstring) + int argc; + char **argv; + const char *optstring; +{ + return share___getopt_internal (argc, argv, optstring, + (const struct share__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 `share__getopt'. */ + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = share__optind ? share__optind : 1; + + c = share__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", share__optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (share__optind < argc) + { + printf ("non-option ARGV-elements: "); + while (share__optind < argc) + printf ("%s ", argv[share__optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/src/FLAC/src/share/getopt/getopt1.c b/src/FLAC/src/share/getopt/getopt1.c new file mode 100644 index 00000000..969e0ff7 --- /dev/null +++ b/src/FLAC/src/share/getopt/getopt1.c @@ -0,0 +1,204 @@ +/* + NOTE: + I cannot get the vanilla getopt code to work (i.e. compile only what + is needed and not duplicate symbols found in the standard library) + on all the platforms that FLAC supports. In particular the gating + of code with the ELIDE_CODE #define is not accurate enough on systems + that are POSIX but not glibc. If someone has a patch that works on + GNU/Linux, Darwin, AND Solaris please submit it on the project page: + http://sourceforge.net/projects/flac + + In the meantime I have munged the global symbols and removed gates + around code, while at the same time trying to touch the original as + little as possible. +*/ +/* 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. */ + +#if HAVE_CONFIG_H +# include +#endif + +#include "share/getopt.h" +/*[JEC] was:#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 + +#if 1 +/*[JEC] was:#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 +share__getopt_long (argc, argv, options, long_options, opt_index) + int argc; + char **argv; + const char *options; + const struct share__option *long_options; + int *opt_index; +{ + return share___getopt_internal (argc, argv, options, long_options, opt_index, 0); +} + +/* Like share__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 +share__getopt_long_only (argc, argv, options, long_options, opt_index) + int argc; + char **argv; + const char *options; + const struct share__option *long_options; + int *opt_index; +{ + return share___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 = share__optind ? share__optind : 1; + int option_index = 0; + static struct share__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 = share__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 (share__optarg) + printf (" with arg %s", share__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", share__optarg); + break; + + case 'd': + printf ("option d with value `%s'\n", share__optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (share__optind < argc) + { + printf ("non-option ARGV-elements: "); + while (share__optind < argc) + printf ("%s ", argv[share__optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/src/FLAC/src/share/grabbag/Makefile.am b/src/FLAC/src/share/grabbag/Makefile.am new file mode 100644 index 00000000..10f3bef4 --- /dev/null +++ b/src/FLAC/src/share/grabbag/Makefile.am @@ -0,0 +1,20 @@ +## Process this file with automake to produce Makefile.in + +AUTOMAKE_OPTIONS = foreign + +INCLUDES = -I$(top_srcdir)/src/FLAC/include + +noinst_LTLIBRARIES = libgrabbag.la + +libgrabbag_la_SOURCES = \ + cuesheet.c \ + file.c \ + picture.c \ + replaygain.c \ + seektable.c + +debug: + $(MAKE) all CFLAGS="@DEBUG@" + +profile: + $(MAKE) all CFLAGS="@PROFILE@" diff --git a/src/FLAC/src/share/grabbag/cuesheet.c b/src/FLAC/src/share/grabbag/cuesheet.c new file mode 100644 index 00000000..a30fa033 --- /dev/null +++ b/src/FLAC/src/share/grabbag/cuesheet.c @@ -0,0 +1,599 @@ +/* grabbag - Convenience lib for various routines common to several tools + * Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include "share/grabbag.h" +#include "FLAC/assert.h" +#include +#include +#include + +unsigned grabbag__cuesheet_msf_to_frame(unsigned minutes, unsigned seconds, unsigned frames) +{ + return ((minutes * 60) + seconds) * 75 + frames; +} + +void grabbag__cuesheet_frame_to_msf(unsigned frame, unsigned *minutes, unsigned *seconds, unsigned *frames) +{ + *frames = frame % 75; + frame /= 75; + *seconds = frame % 60; + frame /= 60; + *minutes = frame; +} + +/* since we only care about values >= 0 or error, returns < 0 for any illegal string, else value */ +static int local__parse_int_(const char *s) +{ + int ret = 0; + char c; + + if(*s == '\0') + return -1; + + while('\0' != (c = *s++)) + if(c >= '0' && c <= '9') + ret = ret * 10 + (c - '0'); + else + return -1; + + return ret; +} + +/* since we only care about values >= 0 or error, returns < 0 for any illegal string, else value */ +static FLAC__int64 local__parse_int64_(const char *s) +{ + FLAC__int64 ret = 0; + char c; + + if(*s == '\0') + return -1; + + while('\0' != (c = *s++)) + if(c >= '0' && c <= '9') + ret = ret * 10 + (c - '0'); + else + return -1; + + return ret; +} + +/* accept '[0-9]+:[0-9][0-9]?:[0-9][0-9]?', but max second of 59 and max frame of 74, e.g. 0:0:0, 123:45:67 + * return sample number or <0 for error + */ +static FLAC__int64 local__parse_msf_(const char *s) +{ + FLAC__int64 ret, field; + char c; + + c = *s++; + if(c >= '0' && c <= '9') + field = (c - '0'); + else + return -1; + while(':' != (c = *s++)) { + if(c >= '0' && c <= '9') + field = field * 10 + (c - '0'); + else + return -1; + } + + ret = field * 60 * 44100; + + c = *s++; + if(c >= '0' && c <= '9') + field = (c - '0'); + else + return -1; + if(':' != (c = *s++)) { + if(c >= '0' && c <= '9') { + field = field * 10 + (c - '0'); + c = *s++; + if(c != ':') + return -1; + } + else + return -1; + } + + if(field >= 60) + return -1; + + ret += field * 44100; + + c = *s++; + if(c >= '0' && c <= '9') + field = (c - '0'); + else + return -1; + if('\0' != (c = *s++)) { + if(c >= '0' && c <= '9') { + field = field * 10 + (c - '0'); + c = *s++; + } + else + return -1; + } + + if(c != '\0') + return -1; + + if(field >= 75) + return -1; + + ret += field * (44100 / 75); + + return ret; +} + +static char *local__get_field_(char **s, FLAC__bool allow_quotes) +{ + FLAC__bool has_quote = false; + char *p; + + FLAC__ASSERT(0 != s); + + if(0 == *s) + return 0; + + /* skip leading whitespace */ + while(**s && 0 != strchr(" \t\r\n", **s)) + (*s)++; + + if(**s == 0) { + *s = 0; + return 0; + } + + if(allow_quotes && (**s == '"')) { + has_quote = true; + (*s)++; + if(**s == 0) { + *s = 0; + return 0; + } + } + + p = *s; + + if(has_quote) { + *s = strchr(*s, '\"'); + /* if there is no matching end quote, it's an error */ + if(0 == *s) + p = *s = 0; + else { + **s = '\0'; + (*s)++; + } + } + else { + while(**s && 0 == strchr(" \t\r\n", **s)) + (*s)++; + if(**s) { + **s = '\0'; + (*s)++; + } + else + *s = 0; + } + + return p; +} + +static FLAC__bool local__cuesheet_parse_(FILE *file, const char **error_message, unsigned *last_line_read, FLAC__StreamMetadata *cuesheet, FLAC__bool is_cdda, FLAC__uint64 lead_out_offset) +{ +#if defined _MSC_VER || defined __MINGW32__ || defined __EMX__ +#define FLAC__STRCASECMP stricmp +#else +#define FLAC__STRCASECMP strcasecmp +#endif + char buffer[4096], *line, *field; + unsigned forced_leadout_track_num = 0; + FLAC__uint64 forced_leadout_track_offset = 0; + int in_track_num = -1, in_index_num = -1; + FLAC__bool disc_has_catalog = false, track_has_flags = false, track_has_isrc = false, has_forced_leadout = false; + FLAC__StreamMetadata_CueSheet *cs = &cuesheet->data.cue_sheet; + + cs->lead_in = is_cdda? 2 * 44100 /* The default lead-in size for CD-DA */ : 0; + cs->is_cd = is_cdda; + + while(0 != fgets(buffer, sizeof(buffer), file)) { + (*last_line_read)++; + line = buffer; + + { + size_t linelen = strlen(line); + if((linelen == sizeof(buffer)-1) && line[linelen-1] != '\n') { + *error_message = "line too long"; + return false; + } + } + + if(0 != (field = local__get_field_(&line, /*allow_quotes=*/false))) { + if(0 == FLAC__STRCASECMP(field, "CATALOG")) { + if(disc_has_catalog) { + *error_message = "found multiple CATALOG commands"; + return false; + } + if(0 == (field = local__get_field_(&line, /*allow_quotes=*/true))) { + *error_message = "CATALOG is missing catalog number"; + return false; + } + if(strlen(field) >= sizeof(cs->media_catalog_number)) { + *error_message = "CATALOG number is too long"; + return false; + } + if(is_cdda && (strlen(field) != 13 || strspn(field, "0123456789") != 13)) { + *error_message = "CD-DA CATALOG number must be 13 decimal digits"; + return false; + } + strcpy(cs->media_catalog_number, field); + disc_has_catalog = true; + } + else if(0 == FLAC__STRCASECMP(field, "FLAGS")) { + if(track_has_flags) { + *error_message = "found multiple FLAGS commands"; + return false; + } + if(in_track_num < 0 || in_index_num >= 0) { + *error_message = "FLAGS command must come after TRACK but before INDEX"; + return false; + } + while(0 != (field = local__get_field_(&line, /*allow_quotes=*/false))) { + if(0 == FLAC__STRCASECMP(field, "PRE")) + cs->tracks[cs->num_tracks-1].pre_emphasis = 1; + } + track_has_flags = true; + } + else if(0 == FLAC__STRCASECMP(field, "INDEX")) { + FLAC__int64 xx; + FLAC__StreamMetadata_CueSheet_Track *track = &cs->tracks[cs->num_tracks-1]; + if(in_track_num < 0) { + *error_message = "found INDEX before any TRACK"; + return false; + } + if(0 == (field = local__get_field_(&line, /*allow_quotes=*/false))) { + *error_message = "INDEX is missing index number"; + return false; + } + in_index_num = local__parse_int_(field); + if(in_index_num < 0) { + *error_message = "INDEX has invalid index number"; + return false; + } + FLAC__ASSERT(cs->num_tracks > 0); + if(track->num_indices == 0) { + /* it's the first index point of the track */ + if(in_index_num > 1) { + *error_message = "first INDEX number of a TRACK must be 0 or 1"; + return false; + } + } + else { + if(in_index_num != track->indices[track->num_indices-1].number + 1) { + *error_message = "INDEX numbers must be sequential"; + return false; + } + } + if(is_cdda && in_index_num > 99) { + *error_message = "CD-DA INDEX number must be between 0 and 99, inclusive"; + return false; + } + /*@@@ search for duplicate track number? */ + if(0 == (field = local__get_field_(&line, /*allow_quotes=*/false))) { + *error_message = "INDEX is missing an offset after the index number"; + return false; + } + xx = local__parse_msf_(field); + if(xx < 0) { + if(is_cdda) { + *error_message = "illegal INDEX offset (not of the form MM:SS:FF)"; + return false; + } + xx = local__parse_int64_(field); + if(xx < 0) { + *error_message = "illegal INDEX offset"; + return false; + } + } + if(is_cdda && cs->num_tracks == 1 && cs->tracks[0].num_indices == 0 && xx != 0) { + *error_message = "first INDEX of first TRACK must have an offset of 00:00:00"; + return false; + } + if(is_cdda && track->num_indices > 0 && (FLAC__uint64)xx <= track->indices[track->num_indices-1].offset) { + *error_message = "CD-DA INDEX offsets must increase in time"; + return false; + } + /* fill in track offset if it's the first index of the track */ + if(track->num_indices == 0) + track->offset = (FLAC__uint64)xx; + if(is_cdda && cs->num_tracks > 1) { + const FLAC__StreamMetadata_CueSheet_Track *prev = &cs->tracks[cs->num_tracks-2]; + if((FLAC__uint64)xx <= prev->offset + prev->indices[prev->num_indices-1].offset) { + *error_message = "CD-DA INDEX offsets must increase in time"; + return false; + } + } + if(!FLAC__metadata_object_cuesheet_track_insert_blank_index(cuesheet, cs->num_tracks-1, track->num_indices)) { + *error_message = "memory allocation error"; + return false; + } + track->indices[track->num_indices-1].offset = (FLAC__uint64)xx - track->offset; + track->indices[track->num_indices-1].number = in_index_num; + } + else if(0 == FLAC__STRCASECMP(field, "ISRC")) { + char *l, *r; + if(track_has_isrc) { + *error_message = "found multiple ISRC commands"; + return false; + } + if(in_track_num < 0 || in_index_num >= 0) { + *error_message = "ISRC command must come after TRACK but before INDEX"; + return false; + } + if(0 == (field = local__get_field_(&line, /*allow_quotes=*/false))) { + *error_message = "ISRC is missing ISRC number"; + return false; + } + /* strip out dashes */ + for(l = r = field; *r; r++) { + if(*r != '-') + *l++ = *r; + } + *l = '\0'; + if(strlen(field) != 12 || strspn(field, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") < 5 || strspn(field+5, "1234567890") != 7) { + *error_message = "invalid ISRC number"; + return false; + } + strcpy(cs->tracks[cs->num_tracks-1].isrc, field); + track_has_isrc = true; + } + else if(0 == FLAC__STRCASECMP(field, "TRACK")) { + if(cs->num_tracks > 0) { + const FLAC__StreamMetadata_CueSheet_Track *prev = &cs->tracks[cs->num_tracks-1]; + if( + prev->num_indices == 0 || + ( + is_cdda && + ( + (prev->num_indices == 1 && prev->indices[0].number != 1) || + (prev->num_indices == 2 && prev->indices[0].number != 1 && prev->indices[1].number != 1) + ) + ) + ) { + *error_message = is_cdda? + "previous TRACK must specify at least one INDEX 01" : + "previous TRACK must specify at least one INDEX"; + return false; + } + } + if(0 == (field = local__get_field_(&line, /*allow_quotes=*/false))) { + *error_message = "TRACK is missing track number"; + return false; + } + in_track_num = local__parse_int_(field); + if(in_track_num < 0) { + *error_message = "TRACK has invalid track number"; + return false; + } + if(in_track_num == 0) { + *error_message = "TRACK number must be greater than 0"; + return false; + } + if(is_cdda && in_track_num > 99) { + *error_message = "CD-DA TRACK number must be between 1 and 99, inclusive"; + return false; + } + if(is_cdda && cs->num_tracks > 0 && in_track_num != cs->tracks[cs->num_tracks-1].number + 1) { + *error_message = "CD-DA TRACK numbers must be sequential"; + return false; + } + /*@@@ search for duplicate track number? */ + if(0 == (field = local__get_field_(&line, /*allow_quotes=*/false))) { + *error_message = "TRACK is missing a track type after the track number"; + return false; + } + if(!FLAC__metadata_object_cuesheet_insert_blank_track(cuesheet, cs->num_tracks)) { + *error_message = "memory allocation error"; + return false; + } + cs->tracks[cs->num_tracks-1].number = in_track_num; + cs->tracks[cs->num_tracks-1].type = (0 == FLAC__STRCASECMP(field, "AUDIO"))? 0 : 1; /*@@@ should we be more strict with the value here? */ + in_index_num = -1; + track_has_flags = false; + track_has_isrc = false; + } + else if(0 == FLAC__STRCASECMP(field, "REM")) { + if(0 != (field = local__get_field_(&line, /*allow_quotes=*/false))) { + if(0 == strcmp(field, "FLAC__lead-in")) { + FLAC__int64 xx; + if(0 == (field = local__get_field_(&line, /*allow_quotes=*/false))) { + *error_message = "FLAC__lead-in is missing offset"; + return false; + } + xx = local__parse_int64_(field); + if(xx < 0) { + *error_message = "illegal FLAC__lead-in offset"; + return false; + } + if(is_cdda && xx % 588 != 0) { + *error_message = "illegal CD-DA FLAC__lead-in offset, must be even multiple of 588 samples"; + return false; + } + cs->lead_in = (FLAC__uint64)xx; + } + else if(0 == strcmp(field, "FLAC__lead-out")) { + int track_num; + FLAC__int64 offset; + if(has_forced_leadout) { + *error_message = "multiple FLAC__lead-out commands"; + return false; + } + if(0 == (field = local__get_field_(&line, /*allow_quotes=*/false))) { + *error_message = "FLAC__lead-out is missing track number"; + return false; + } + track_num = local__parse_int_(field); + if(track_num < 0) { + *error_message = "illegal FLAC__lead-out track number"; + return false; + } + forced_leadout_track_num = (unsigned)track_num; + /*@@@ search for duplicate track number? */ + if(0 == (field = local__get_field_(&line, /*allow_quotes=*/false))) { + *error_message = "FLAC__lead-out is missing offset"; + return false; + } + offset = local__parse_int64_(field); + if(offset < 0) { + *error_message = "illegal FLAC__lead-out offset"; + return false; + } + forced_leadout_track_offset = (FLAC__uint64)offset; + if(forced_leadout_track_offset != lead_out_offset) { + *error_message = "FLAC__lead-out offset does not match end-of-stream offset"; + return false; + } + has_forced_leadout = true; + } + } + } + } + } + + if(cs->num_tracks == 0) { + *error_message = "there must be at least one TRACK command"; + return false; + } + else { + const FLAC__StreamMetadata_CueSheet_Track *prev = &cs->tracks[cs->num_tracks-1]; + if( + prev->num_indices == 0 || + ( + is_cdda && + ( + (prev->num_indices == 1 && prev->indices[0].number != 1) || + (prev->num_indices == 2 && prev->indices[0].number != 1 && prev->indices[1].number != 1) + ) + ) + ) { + *error_message = is_cdda? + "previous TRACK must specify at least one INDEX 01" : + "previous TRACK must specify at least one INDEX"; + return false; + } + } + + if(!has_forced_leadout) { + forced_leadout_track_num = is_cdda? 170 : cs->num_tracks; + forced_leadout_track_offset = lead_out_offset; + } + if(!FLAC__metadata_object_cuesheet_insert_blank_track(cuesheet, cs->num_tracks)) { + *error_message = "memory allocation error"; + return false; + } + cs->tracks[cs->num_tracks-1].number = forced_leadout_track_num; + cs->tracks[cs->num_tracks-1].offset = forced_leadout_track_offset; + + if(!feof(file)) { + *error_message = "read error"; + return false; + } + return true; +#undef FLAC__STRCASECMP +} + +FLAC__StreamMetadata *grabbag__cuesheet_parse(FILE *file, const char **error_message, unsigned *last_line_read, FLAC__bool is_cdda, FLAC__uint64 lead_out_offset) +{ + FLAC__StreamMetadata *cuesheet; + + FLAC__ASSERT(0 != file); + FLAC__ASSERT(0 != error_message); + FLAC__ASSERT(0 != last_line_read); + + *last_line_read = 0; + cuesheet = FLAC__metadata_object_new(FLAC__METADATA_TYPE_CUESHEET); + + if(0 == cuesheet) { + *error_message = "memory allocation error"; + return 0; + } + + if(!local__cuesheet_parse_(file, error_message, last_line_read, cuesheet, is_cdda, lead_out_offset)) { + FLAC__metadata_object_delete(cuesheet); + return 0; + } + + return cuesheet; +} + +void grabbag__cuesheet_emit(FILE *file, const FLAC__StreamMetadata *cuesheet, const char *file_reference) +{ + const FLAC__StreamMetadata_CueSheet *cs; + unsigned track_num, index_num; + + FLAC__ASSERT(0 != file); + FLAC__ASSERT(0 != cuesheet); + FLAC__ASSERT(cuesheet->type == FLAC__METADATA_TYPE_CUESHEET); + + cs = &cuesheet->data.cue_sheet; + + if(*(cs->media_catalog_number)) + fprintf(file, "CATALOG %s\n", cs->media_catalog_number); + fprintf(file, "FILE %s\n", file_reference); + + for(track_num = 0; track_num < cs->num_tracks-1; track_num++) { + const FLAC__StreamMetadata_CueSheet_Track *track = cs->tracks + track_num; + + fprintf(file, " TRACK %02u %s\n", (unsigned)track->number, track->type == 0? "AUDIO" : "DATA"); + + if(track->pre_emphasis) + fprintf(file, " FLAGS PRE\n"); + if(*(track->isrc)) + fprintf(file, " ISRC %s\n", track->isrc); + + for(index_num = 0; index_num < track->num_indices; index_num++) { + const FLAC__StreamMetadata_CueSheet_Index *indx = track->indices + index_num; + + fprintf(file, " INDEX %02u ", (unsigned)indx->number); + if(cs->is_cd) { + const unsigned logical_frame = (unsigned)((track->offset + indx->offset) / (44100 / 75)); + unsigned m, s, f; + grabbag__cuesheet_frame_to_msf(logical_frame, &m, &s, &f); + fprintf(file, "%02u:%02u:%02u\n", m, s, f); + } + else +#ifdef _MSC_VER + fprintf(file, "%I64u\n", track->offset + indx->offset); +#else + fprintf(file, "%llu\n", (unsigned long long)(track->offset + indx->offset)); +#endif + } + } + +#ifdef _MSC_VER + fprintf(file, "REM FLAC__lead-in %I64u\n", cs->lead_in); + fprintf(file, "REM FLAC__lead-out %u %I64u\n", (unsigned)cs->tracks[track_num].number, cs->tracks[track_num].offset); +#else + fprintf(file, "REM FLAC__lead-in %llu\n", (unsigned long long)cs->lead_in); + fprintf(file, "REM FLAC__lead-out %u %llu\n", (unsigned)cs->tracks[track_num].number, (unsigned long long)cs->tracks[track_num].offset); +#endif +} diff --git a/src/FLAC/src/share/grabbag/file.c b/src/FLAC/src/share/grabbag/file.c new file mode 100644 index 00000000..a481f743 --- /dev/null +++ b/src/FLAC/src/share/grabbag/file.c @@ -0,0 +1,192 @@ +/* grabbag - Convenience lib for various routines common to several tools + * Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#if HAVE_CONFIG_H +# include +#endif + +#if defined _MSC_VER || defined __MINGW32__ +#include /* for utime() */ +#include /* for chmod(), _setmode(), unlink() */ +#include /* for _O_BINARY */ +#else +#include /* some flavors of BSD (like OS X) require this to get time_t */ +#include /* for utime() */ +#endif +#if defined __CYGWIN__ || defined __EMX__ +#include /* for setmode(), O_BINARY */ +#include /* for _O_BINARY */ +#endif +#include /* for stat(), maybe chmod() */ +#if defined _WIN32 && !defined __CYGWIN__ +#else +#include /* for unlink() */ +#endif +#include +#include +#include /* for strrchr() */ +#if defined _WIN32 && !defined __CYGWIN__ +// for GetFileInformationByHandle() etc +#include +#include +#endif +#include "share/grabbag.h" + + +void grabbag__file_copy_metadata(const char *srcpath, const char *destpath) +{ + struct stat srcstat; + struct utimbuf srctime; + + if(0 == stat(srcpath, &srcstat)) { + srctime.actime = srcstat.st_atime; + srctime.modtime = srcstat.st_mtime; + (void)chmod(destpath, srcstat.st_mode); + (void)utime(destpath, &srctime); + } +} + +off_t grabbag__file_get_filesize(const char *srcpath) +{ + struct stat srcstat; + + if(0 == stat(srcpath, &srcstat)) + return srcstat.st_size; + else + return -1; +} + +const char *grabbag__file_get_basename(const char *srcpath) +{ + const char *p; + + p = strrchr(srcpath, '/'); + if(0 == p) { + p = strrchr(srcpath, '\\'); + if(0 == p) + return srcpath; + } + return ++p; +} + +FLAC__bool grabbag__file_change_stats(const char *filename, FLAC__bool read_only) +{ + struct stat stats; + + if(0 == stat(filename, &stats)) { +#if !defined _MSC_VER && !defined __MINGW32__ + if(read_only) { + stats.st_mode &= ~S_IWUSR; + stats.st_mode &= ~S_IWGRP; + stats.st_mode &= ~S_IWOTH; + } + else { + stats.st_mode |= S_IWUSR; + } +#else + if(read_only) + stats.st_mode &= ~S_IWRITE; + else + stats.st_mode |= S_IWRITE; +#endif + if(0 != chmod(filename, stats.st_mode)) + return false; + } + else + return false; + + return true; +} + +FLAC__bool grabbag__file_are_same(const char *f1, const char *f2) +{ +#if defined _MSC_VER || defined __MINGW32__ + /* see + * http://www.hydrogenaudio.org/forums/index.php?showtopic=49439&pid=444300&st=0 + * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/fs/getfileinformationbyhandle.asp + * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/fs/by_handle_file_information_str.asp + * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/fs/createfile.asp + * apparently both the files have to be open at the same time for the comparison to work + */ + FLAC__bool same = false; + BY_HANDLE_FILE_INFORMATION info1, info2; + HANDLE h1, h2; + BOOL ok = 1; + h1 = CreateFile(f1, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + h2 = CreateFile(f2, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if(h1 == INVALID_HANDLE_VALUE || h2 == INVALID_HANDLE_VALUE) + ok = 0; + ok &= GetFileInformationByHandle(h1, &info1); + ok &= GetFileInformationByHandle(h2, &info2); + if(ok) + same = + info1.dwVolumeSerialNumber == info2.dwVolumeSerialNumber && + info1.nFileIndexHigh == info2.nFileIndexHigh && + info1.nFileIndexLow == info2.nFileIndexLow + ; + if(h1 != INVALID_HANDLE_VALUE) + CloseHandle(h1); + if(h2 != INVALID_HANDLE_VALUE) + CloseHandle(h2); + return same; +#else + struct stat s1, s2; + return f1 && f2 && stat(f1, &s1) == 0 && stat(f2, &s2) == 0 && s1.st_ino == s2.st_ino && s1.st_dev == s2.st_dev; +#endif +} + +FLAC__bool grabbag__file_remove_file(const char *filename) +{ + return grabbag__file_change_stats(filename, /*read_only=*/false) && 0 == unlink(filename); +} + +FILE *grabbag__file_get_binary_stdin(void) +{ + /* if something breaks here it is probably due to the presence or + * absence of an underscore before the identifiers 'setmode', + * 'fileno', and/or 'O_BINARY'; check your system header files. + */ +#if defined _MSC_VER || defined __MINGW32__ + _setmode(_fileno(stdin), _O_BINARY); +#elif defined __CYGWIN__ + /* almost certainly not needed for any modern Cygwin, but let's be safe... */ + setmode(_fileno(stdin), _O_BINARY); +#elif defined __EMX__ + setmode(fileno(stdin), O_BINARY); +#endif + + return stdin; +} + +FILE *grabbag__file_get_binary_stdout(void) +{ + /* if something breaks here it is probably due to the presence or + * absence of an underscore before the identifiers 'setmode', + * 'fileno', and/or 'O_BINARY'; check your system header files. + */ +#if defined _MSC_VER || defined __MINGW32__ + _setmode(_fileno(stdout), _O_BINARY); +#elif defined __CYGWIN__ + /* almost certainly not needed for any modern Cygwin, but let's be safe... */ + setmode(_fileno(stdout), _O_BINARY); +#elif defined __EMX__ + setmode(fileno(stdout), O_BINARY); +#endif + + return stdout; +} diff --git a/src/FLAC/src/share/grabbag/picture.c b/src/FLAC/src/share/grabbag/picture.c new file mode 100644 index 00000000..68e1f51d --- /dev/null +++ b/src/FLAC/src/share/grabbag/picture.c @@ -0,0 +1,406 @@ +/* grabbag - Convenience lib for various routines common to several tools + * Copyright (C) 2006,2007 Josh Coalson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include "share/grabbag.h" +#include "FLAC/assert.h" +#include +#include +#include + +/* slightly different that strndup(): this always copies 'size' bytes starting from s into a NUL-terminated string. */ +static char *local__strndup_(const char *s, size_t size) +{ + char *x = (char*)malloc(size+1); + if(x) { + memcpy(x, s, size); + x[size] = '\0'; + } + return x; +} + +static FLAC__bool local__parse_type_(const char *s, size_t len, FLAC__StreamMetadata_Picture *picture) +{ + size_t i; + FLAC__uint32 val = 0; + + picture->type = FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER; + + if(len == 0) + return true; /* empty string implies default to 'front cover' */ + + for(i = 0; i < len; i++) { + if(s[i] >= '0' && s[i] <= '9') + val = 10*val + (FLAC__uint32)(s[i] - '0'); + else + return false; + } + + if(i == len) + picture->type = val; + else + return false; + + return true; +} + +static FLAC__bool local__parse_resolution_(const char *s, size_t len, FLAC__StreamMetadata_Picture *picture) +{ + int state = 0; + size_t i; + FLAC__uint32 val = 0; + + picture->width = picture->height = picture->depth = picture->colors = 0; + + if(len == 0) + return true; /* empty string implies client wants to get info from the file itself */ + + for(i = 0; i < len; i++) { + if(s[i] == 'x') { + if(state == 0) + picture->width = val; + else if(state == 1) + picture->height = val; + else + return false; + state++; + val = 0; + } + else if(s[i] == '/') { + if(state == 2) + picture->depth = val; + else + return false; + state++; + val = 0; + } + else if(s[i] >= '0' && s[i] <= '9') + val = 10*val + (FLAC__uint32)(s[i] - '0'); + else + return false; + } + + if(state < 2) + return false; + else if(state == 2) + picture->depth = val; + else if(state == 3) + picture->colors = val; + else + return false; + if(picture->depth < 32 && 1u<depth < picture->colors) + return false; + + return true; +} + +static FLAC__bool local__extract_mime_type_(FLAC__StreamMetadata *obj) +{ + if(obj->data.picture.data_length >= 8 && 0 == memcmp(obj->data.picture.data, "\x89PNG\x0d\x0a\x1a\x0a", 8)) + return FLAC__metadata_object_picture_set_mime_type(obj, "image/png", /*copy=*/true); + else if(obj->data.picture.data_length >= 6 && (0 == memcmp(obj->data.picture.data, "GIF87a", 6) || 0 == memcmp(obj->data.picture.data, "GIF89a", 6))) + return FLAC__metadata_object_picture_set_mime_type(obj, "image/gif", /*copy=*/true); + else if(obj->data.picture.data_length >= 2 && 0 == memcmp(obj->data.picture.data, "\xff\xd8", 2)) + return FLAC__metadata_object_picture_set_mime_type(obj, "image/jpeg", /*copy=*/true); + return false; +} + +static FLAC__bool local__extract_resolution_color_info_(FLAC__StreamMetadata_Picture *picture) +{ + const FLAC__byte *data = picture->data; + FLAC__uint32 len = picture->data_length; + + if(0 == strcmp(picture->mime_type, "image/png")) { + /* c.f. http://www.w3.org/TR/PNG/ */ + FLAC__bool need_palette = false; /* if IHDR has color_type=3, we need to also read the PLTE chunk to get the #colors */ + if(len < 8 || memcmp(data, "\x89PNG\x0d\x0a\x1a\x0a", 8)) + return false; + /* try to find IHDR chunk */ + data += 8; + len -= 8; + while(len > 12) { /* every PNG chunk must be at least 12 bytes long */ + const FLAC__uint32 clen = (FLAC__uint32)data[0] << 24 | (FLAC__uint32)data[1] << 16 | (FLAC__uint32)data[2] << 8 | (FLAC__uint32)data[3]; + if(0 == memcmp(data+4, "IHDR", 4) && clen == 13) { + unsigned color_type = data[17]; + picture->width = (FLAC__uint32)data[8] << 24 | (FLAC__uint32)data[9] << 16 | (FLAC__uint32)data[10] << 8 | (FLAC__uint32)data[11]; + picture->height = (FLAC__uint32)data[12] << 24 | (FLAC__uint32)data[13] << 16 | (FLAC__uint32)data[14] << 8 | (FLAC__uint32)data[15]; + if(color_type == 3) { + /* even though the bit depth for color_type==3 can be 1,2,4,or 8, + * the spec in 11.2.2 of http://www.w3.org/TR/PNG/ says that the + * sample depth is always 8 + */ + picture->depth = 8 * 3u; + need_palette = true; + data += 12 + clen; + len -= 12 + clen; + } + else { + if(color_type == 0) /* greyscale, 1 sample per pixel */ + picture->depth = (FLAC__uint32)data[16]; + if(color_type == 2) /* truecolor, 3 samples per pixel */ + picture->depth = (FLAC__uint32)data[16] * 3u; + if(color_type == 4) /* greyscale+alpha, 2 samples per pixel */ + picture->depth = (FLAC__uint32)data[16] * 2u; + if(color_type == 6) /* truecolor+alpha, 4 samples per pixel */ + picture->depth = (FLAC__uint32)data[16] * 4u; + picture->colors = 0; + return true; + } + } + else if(need_palette && 0 == memcmp(data+4, "PLTE", 4)) { + picture->colors = clen / 3u; + return true; + } + else if(clen + 12 > len) + return false; + else { + data += 12 + clen; + len -= 12 + clen; + } + } + } + else if(0 == strcmp(picture->mime_type, "image/jpeg")) { + /* c.f. http://www.w3.org/Graphics/JPEG/itu-t81.pdf and Q22 of http://www.faqs.org/faqs/jpeg-faq/part1/ */ + if(len < 2 || memcmp(data, "\xff\xd8", 2)) + return false; + data += 2; + len -= 2; + while(1) { + /* look for sync FF byte */ + for( ; len > 0; data++, len--) { + if(*data == 0xff) + break; + } + if(len == 0) + return false; + /* eat any extra pad FF bytes before marker */ + for( ; len > 0; data++, len--) { + if(*data != 0xff) + break; + } + if(len == 0) + return false; + /* if we hit SOS or EOI, bail */ + if(*data == 0xda || *data == 0xd9) + return false; + /* looking for some SOFn */ + else if(memchr("\xc0\xc1\xc2\xc3\xc5\xc6\xc7\xc9\xca\xcb\xcd\xce\xcf", *data, 13)) { + data++; len--; /* skip marker byte */ + if(len < 2) + return false; + else { + const FLAC__uint32 clen = (FLAC__uint32)data[0] << 8 | (FLAC__uint32)data[1]; + if(clen < 8 || len < clen) + return false; + picture->width = (FLAC__uint32)data[5] << 8 | (FLAC__uint32)data[6]; + picture->height = (FLAC__uint32)data[3] << 8 | (FLAC__uint32)data[4]; + picture->depth = (FLAC__uint32)data[2] * (FLAC__uint32)data[7]; + picture->colors = 0; + return true; + } + } + /* else skip it */ + else { + data++; len--; /* skip marker byte */ + if(len < 2) + return false; + else { + const FLAC__uint32 clen = (FLAC__uint32)data[0] << 8 | (FLAC__uint32)data[1]; + if(clen < 2 || len < clen) + return false; + data += clen; + len -= clen; + } + } + } + } + else if(0 == strcmp(picture->mime_type, "image/gif")) { + /* c.f. http://www.w3.org/Graphics/GIF/spec-gif89a.txt */ + if(len < 14) + return false; + if(memcmp(data, "GIF87a", 6) && memcmp(data, "GIF89a", 6)) + return false; +#if 0 + /* according to the GIF spec, even if the GCTF is 0, the low 3 bits should still tell the total # colors used */ + if(data[10] & 0x80 == 0) + return false; +#endif + picture->width = (FLAC__uint32)data[6] | ((FLAC__uint32)data[7] << 8); + picture->height = (FLAC__uint32)data[8] | ((FLAC__uint32)data[9] << 8); +#if 0 + /* this value doesn't seem to be reliable... */ + picture->depth = (((FLAC__uint32)(data[10] & 0x70) >> 4) + 1) * 3u; +#else + /* ...just pessimistically assume it's 24-bit color without scanning all the color tables */ + picture->depth = 8u * 3u; +#endif + picture->colors = 1u << ((FLAC__uint32)(data[10] & 0x07) + 1u); + return true; + } + return false; +} + +FLAC__StreamMetadata *grabbag__picture_parse_specification(const char *spec, const char **error_message) +{ + FLAC__StreamMetadata *obj; + int state = 0; + static const char *error_messages[] = { + "memory allocation error", + "invalid picture specification", + "invalid picture specification: can't parse resolution/color part", + "unable to extract resolution and color info from URL, user must set explicitly", + "unable to extract resolution and color info from file, user must set explicitly", + "error opening picture file", + "error reading picture file", + "invalid picture type", + "unable to guess MIME type from file, user must set explicitly", + "type 1 icon must be a 32x32 pixel PNG" + }; + + FLAC__ASSERT(0 != spec); + FLAC__ASSERT(0 != error_message); + + /* double protection */ + if(0 == spec) + return 0; + if(0 == error_message) + return 0; + + *error_message = 0; + + if(0 == (obj = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PICTURE))) + *error_message = error_messages[0]; + + if(strchr(spec, '|')) { /* full format */ + const char *p; + char *q; + for(p = spec; *error_message==0 && *p; ) { + if(*p == '|') { + switch(state) { + case 0: /* type */ + if(!local__parse_type_(spec, p-spec, &obj->data.picture)) + *error_message = error_messages[7]; + break; + case 1: /* mime type */ + if(p-spec) { /* if blank, we'll try to guess later from the picture data */ + if(0 == (q = local__strndup_(spec, p-spec))) + *error_message = error_messages[0]; + else if(!FLAC__metadata_object_picture_set_mime_type(obj, q, /*copy=*/false)) + *error_message = error_messages[0]; + } + break; + case 2: /* description */ + if(0 == (q = local__strndup_(spec, p-spec))) + *error_message = error_messages[0]; + else if(!FLAC__metadata_object_picture_set_description(obj, (FLAC__byte*)q, /*copy=*/false)) + *error_message = error_messages[0]; + break; + case 3: /* resolution/color (e.g. [300x300x16[/1234]] */ + if(!local__parse_resolution_(spec, p-spec, &obj->data.picture)) + *error_message = error_messages[2]; + break; + default: + *error_message = error_messages[1]; + break; + } + p++; + spec = p; + state++; + } + else + p++; + } + } + else { /* simple format, filename only, everything else guessed */ + if(!local__parse_type_("", 0, &obj->data.picture)) /* use default picture type */ + *error_message = error_messages[7]; + /* leave MIME type to be filled in later */ + /* leave description empty */ + /* leave the rest to be filled in later: */ + else if(!local__parse_resolution_("", 0, &obj->data.picture)) + *error_message = error_messages[2]; + else + state = 4; + } + + /* parse filename, read file, try to extract resolution/color info if needed */ + if(*error_message == 0) { + if(state != 4) + *error_message = error_messages[1]; + else { /* 'spec' points to filename/URL */ + if(0 == strcmp(obj->data.picture.mime_type, "-->")) { /* magic MIME type means URL */ + if(!FLAC__metadata_object_picture_set_data(obj, (const FLAC__byte*)spec, strlen(spec), /*copy=*/true)) + *error_message = error_messages[0]; + else if(obj->data.picture.width == 0 || obj->data.picture.height == 0 || obj->data.picture.depth == 0) + *error_message = error_messages[3]; + } + else { /* regular picture file */ + const off_t size = grabbag__file_get_filesize(spec); + if(size < 0) + *error_message = error_messages[5]; + else { + FLAC__byte *buffer = (FLAC__byte*)malloc(size); + if(0 == buffer) + *error_message = error_messages[0]; + else { + FILE *f = fopen(spec, "rb"); + if(0 == f) + *error_message = error_messages[5]; + else { + if(fread(buffer, 1, size, f) != (size_t)size) + *error_message = error_messages[6]; + fclose(f); + if(0 == *error_message) { + if(!FLAC__metadata_object_picture_set_data(obj, buffer, size, /*copy=*/false)) + *error_message = error_messages[6]; + /* try to extract MIME type if user left it blank */ + else if(*obj->data.picture.mime_type == '\0' && !local__extract_mime_type_(obj)) + *error_message = error_messages[8]; + /* try to extract resolution/color info if user left it blank */ + else if((obj->data.picture.width == 0 || obj->data.picture.height == 0 || obj->data.picture.depth == 0) && !local__extract_resolution_color_info_(&obj->data.picture)) + *error_message = error_messages[4]; + } + } + } + } + } + } + } + + if(*error_message == 0) { + if( + obj->data.picture.type == FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON_STANDARD && + ( + (strcmp(obj->data.picture.mime_type, "image/png") && strcmp(obj->data.picture.mime_type, "-->")) || + obj->data.picture.width != 32 || + obj->data.picture.height != 32 + ) + ) + *error_message = error_messages[9]; + } + + if(*error_message && obj) { + FLAC__metadata_object_delete(obj); + obj = 0; + } + + return obj; +} diff --git a/src/FLAC/src/share/grabbag/replaygain.c b/src/FLAC/src/share/grabbag/replaygain.c new file mode 100644 index 00000000..c816a2c8 --- /dev/null +++ b/src/FLAC/src/share/grabbag/replaygain.c @@ -0,0 +1,670 @@ +/* grabbag - Convenience lib for various routines common to several tools + * Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include "share/grabbag.h" +#include "share/replaygain_analysis.h" +#include "FLAC/assert.h" +#include "FLAC/metadata.h" +#include "FLAC/stream_decoder.h" +#include +#include +#include +#include +#include +#if defined _MSC_VER || defined __MINGW32__ +#include /* for chmod() */ +#endif +#include /* for stat(), maybe chmod() */ + +#ifdef local_min +#undef local_min +#endif +#define local_min(a,b) ((a)<(b)?(a):(b)) + +#ifdef local_max +#undef local_max +#endif +#define local_max(a,b) ((a)>(b)?(a):(b)) + +#define signed_sizeof(x) ((int) (sizeof (x))) + +static const char *reference_format_ = "%s=%2.1f dB"; +static const char *gain_format_ = "%s=%+2.2f dB"; +static const char *peak_format_ = "%s=%1.8f"; + +static double album_peak_, title_peak_; + +const unsigned GRABBAG__REPLAYGAIN_MAX_TAG_SPACE_REQUIRED = 190; +/* + FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN/8 + 29 + 1 + 8 + + FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN/8 + 21 + 1 + 10 + + FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN/8 + 21 + 1 + 12 + + FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN/8 + 21 + 1 + 10 + + FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN/8 + 21 + 1 + 12 +*/ + +const FLAC__byte * const GRABBAG__REPLAYGAIN_TAG_REFERENCE_LOUDNESS = (const FLAC__byte * const)"REPLAYGAIN_REFERENCE_LOUDNESS"; +const FLAC__byte * const GRABBAG__REPLAYGAIN_TAG_TITLE_GAIN = (const FLAC__byte * const)"REPLAYGAIN_TRACK_GAIN"; +const FLAC__byte * const GRABBAG__REPLAYGAIN_TAG_TITLE_PEAK = (const FLAC__byte * const)"REPLAYGAIN_TRACK_PEAK"; +const FLAC__byte * const GRABBAG__REPLAYGAIN_TAG_ALBUM_GAIN = (const FLAC__byte * const)"REPLAYGAIN_ALBUM_GAIN"; +const FLAC__byte * const GRABBAG__REPLAYGAIN_TAG_ALBUM_PEAK = (const FLAC__byte * const)"REPLAYGAIN_ALBUM_PEAK"; + + +static FLAC__bool get_file_stats_(const char *filename, struct stat *stats) +{ + FLAC__ASSERT(0 != filename); + FLAC__ASSERT(0 != stats); + return (0 == stat(filename, stats)); +} + +static void set_file_stats_(const char *filename, struct stat *stats) +{ + FLAC__ASSERT(0 != filename); + FLAC__ASSERT(0 != stats); + + (void)chmod(filename, stats->st_mode); +} + +static FLAC__bool append_tag_(FLAC__StreamMetadata *block, const char *format, const FLAC__byte *name, float value) +{ + char buffer[256]; + char *saved_locale; + FLAC__StreamMetadata_VorbisComment_Entry entry; + + FLAC__ASSERT(0 != block); + FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + FLAC__ASSERT(0 != format); + FLAC__ASSERT(0 != name); + + buffer[sizeof(buffer)-1] = '\0'; + /* + * We need to save the old locale and switch to "C" because the locale + * influences the formatting of %f and we want it a certain way. + */ + saved_locale = strdup(setlocale(LC_ALL, 0)); + if (0 == saved_locale) + return false; + setlocale(LC_ALL, "C"); +#if defined _MSC_VER || defined __MINGW32__ + _snprintf(buffer, sizeof(buffer)-1, format, name, value); +#else + snprintf(buffer, sizeof(buffer)-1, format, name, value); +#endif + setlocale(LC_ALL, saved_locale); + free(saved_locale); + + entry.entry = (FLAC__byte *)buffer; + entry.length = strlen(buffer); + + return FLAC__metadata_object_vorbiscomment_append_comment(block, entry, /*copy=*/true); +} + +FLAC__bool grabbag__replaygain_is_valid_sample_frequency(unsigned sample_frequency) +{ + static const unsigned valid_sample_rates[] = { + 8000, + 11025, + 12000, + 16000, + 22050, + 24000, + 32000, + 44100, + 48000 + }; + static const unsigned n_valid_sample_rates = sizeof(valid_sample_rates) / sizeof(valid_sample_rates[0]); + + unsigned i; + + for(i = 0; i < n_valid_sample_rates; i++) + if(sample_frequency == valid_sample_rates[i]) + return true; + return false; +} + +FLAC__bool grabbag__replaygain_init(unsigned sample_frequency) +{ + title_peak_ = album_peak_ = 0.0; + return InitGainAnalysis((long)sample_frequency) == INIT_GAIN_ANALYSIS_OK; +} + +FLAC__bool grabbag__replaygain_analyze(const FLAC__int32 * const input[], FLAC__bool is_stereo, unsigned bps, unsigned samples) +{ + /* using a small buffer improves data locality; we'd like it to fit easily in the dcache */ + static Float_t lbuffer[2048], rbuffer[2048]; + static const unsigned nbuffer = sizeof(lbuffer) / sizeof(lbuffer[0]); + FLAC__int32 block_peak = 0, s; + unsigned i, j; + + FLAC__ASSERT(bps >= 4 && bps <= FLAC__REFERENCE_CODEC_MAX_BITS_PER_SAMPLE); + FLAC__ASSERT(FLAC__MIN_BITS_PER_SAMPLE == 4); + /* + * We use abs() on a FLAC__int32 which is undefined for the most negative value. + * If the reference codec ever handles 32bps we will have to write a special + * case here. + */ + FLAC__ASSERT(FLAC__REFERENCE_CODEC_MAX_BITS_PER_SAMPLE < 32); + + if(bps == 16) { + if(is_stereo) { + j = 0; + while(samples > 0) { + const unsigned n = local_min(samples, nbuffer); + for(i = 0; i < n; i++, j++) { + s = input[0][j]; + lbuffer[i] = (Float_t)s; + s = abs(s); + block_peak = local_max(block_peak, s); + + s = input[1][j]; + rbuffer[i] = (Float_t)s; + s = abs(s); + block_peak = local_max(block_peak, s); + } + samples -= n; + if(AnalyzeSamples(lbuffer, rbuffer, n, 2) != GAIN_ANALYSIS_OK) + return false; + } + } + else { + j = 0; + while(samples > 0) { + const unsigned n = local_min(samples, nbuffer); + for(i = 0; i < n; i++, j++) { + s = input[0][j]; + lbuffer[i] = (Float_t)s; + s = abs(s); + block_peak = local_max(block_peak, s); + } + samples -= n; + if(AnalyzeSamples(lbuffer, 0, n, 1) != GAIN_ANALYSIS_OK) + return false; + } + } + } + else { /* bps must be < 32 according to above assertion */ + const double scale = ( + (bps > 16)? + (double)1. / (double)(1u << (bps - 16)) : + (double)(1u << (16 - bps)) + ); + + if(is_stereo) { + j = 0; + while(samples > 0) { + const unsigned n = local_min(samples, nbuffer); + for(i = 0; i < n; i++, j++) { + s = input[0][j]; + lbuffer[i] = (Float_t)(scale * (double)s); + s = abs(s); + block_peak = local_max(block_peak, s); + + s = input[1][j]; + rbuffer[i] = (Float_t)(scale * (double)s); + s = abs(s); + block_peak = local_max(block_peak, s); + } + samples -= n; + if(AnalyzeSamples(lbuffer, rbuffer, n, 2) != GAIN_ANALYSIS_OK) + return false; + } + } + else { + j = 0; + while(samples > 0) { + const unsigned n = local_min(samples, nbuffer); + for(i = 0; i < n; i++, j++) { + s = input[0][j]; + lbuffer[i] = (Float_t)(scale * (double)s); + s = abs(s); + block_peak = local_max(block_peak, s); + } + samples -= n; + if(AnalyzeSamples(lbuffer, 0, n, 1) != GAIN_ANALYSIS_OK) + return false; + } + } + } + + { + const double peak_scale = (double)(1u << (bps - 1)); + double peak = (double)block_peak / peak_scale; + if(peak > title_peak_) + title_peak_ = peak; + if(peak > album_peak_) + album_peak_ = peak; + } + + return true; +} + +void grabbag__replaygain_get_album(float *gain, float *peak) +{ + *gain = (float)GetAlbumGain(); + *peak = (float)album_peak_; + album_peak_ = 0.0; +} + +void grabbag__replaygain_get_title(float *gain, float *peak) +{ + *gain = (float)GetTitleGain(); + *peak = (float)title_peak_; + title_peak_ = 0.0; +} + + +typedef struct { + unsigned channels; + unsigned bits_per_sample; + unsigned sample_rate; + FLAC__bool error; +} DecoderInstance; + +static FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data) +{ + DecoderInstance *instance = (DecoderInstance*)client_data; + const unsigned bits_per_sample = frame->header.bits_per_sample; + const unsigned channels = frame->header.channels; + const unsigned sample_rate = frame->header.sample_rate; + const unsigned samples = frame->header.blocksize; + + (void)decoder; + + if( + !instance->error && + (channels == 2 || channels == 1) && + bits_per_sample == instance->bits_per_sample && + channels == instance->channels && + sample_rate == instance->sample_rate + ) { + instance->error = !grabbag__replaygain_analyze(buffer, channels==2, bits_per_sample, samples); + } + else { + instance->error = true; + } + + if(!instance->error) + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; + else + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; +} + +static void metadata_callback_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) +{ + DecoderInstance *instance = (DecoderInstance*)client_data; + + (void)decoder; + + if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO) { + instance->bits_per_sample = metadata->data.stream_info.bits_per_sample; + instance->channels = metadata->data.stream_info.channels; + instance->sample_rate = metadata->data.stream_info.sample_rate; + + if(instance->channels != 1 && instance->channels != 2) { + instance->error = true; + return; + } + + if(!grabbag__replaygain_is_valid_sample_frequency(instance->sample_rate)) { + instance->error = true; + return; + } + } +} + +static void error_callback_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) +{ + DecoderInstance *instance = (DecoderInstance*)client_data; + + (void)decoder, (void)status; + + instance->error = true; +} + +const char *grabbag__replaygain_analyze_file(const char *filename, float *title_gain, float *title_peak) +{ + DecoderInstance instance; + FLAC__StreamDecoder *decoder = FLAC__stream_decoder_new(); + + if(0 == decoder) + return "memory allocation error"; + + instance.error = false; + + /* It does these three by default but lets be explicit: */ + FLAC__stream_decoder_set_md5_checking(decoder, false); + FLAC__stream_decoder_set_metadata_ignore_all(decoder); + FLAC__stream_decoder_set_metadata_respond(decoder, FLAC__METADATA_TYPE_STREAMINFO); + + if(FLAC__stream_decoder_init_file(decoder, filename, write_callback_, metadata_callback_, error_callback_, &instance) != FLAC__STREAM_DECODER_INIT_STATUS_OK) { + FLAC__stream_decoder_delete(decoder); + return "initializing decoder"; + } + + if(!FLAC__stream_decoder_process_until_end_of_stream(decoder) || instance.error) { + FLAC__stream_decoder_delete(decoder); + return "decoding file"; + } + + FLAC__stream_decoder_delete(decoder); + + grabbag__replaygain_get_title(title_gain, title_peak); + + return 0; +} + +const char *grabbag__replaygain_store_to_vorbiscomment(FLAC__StreamMetadata *block, float album_gain, float album_peak, float title_gain, float title_peak) +{ + const char *error; + + if(0 != (error = grabbag__replaygain_store_to_vorbiscomment_reference(block))) + return error; + + if(0 != (error = grabbag__replaygain_store_to_vorbiscomment_title(block, title_gain, title_peak))) + return error; + + if(0 != (error = grabbag__replaygain_store_to_vorbiscomment_album(block, album_gain, album_peak))) + return error; + + return 0; +} + +const char *grabbag__replaygain_store_to_vorbiscomment_reference(FLAC__StreamMetadata *block) +{ + FLAC__ASSERT(0 != block); + FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + + if(FLAC__metadata_object_vorbiscomment_remove_entries_matching(block, (const char *)GRABBAG__REPLAYGAIN_TAG_REFERENCE_LOUDNESS) < 0) + return "memory allocation error"; + + if(!append_tag_(block, reference_format_, GRABBAG__REPLAYGAIN_TAG_REFERENCE_LOUDNESS, ReplayGainReferenceLoudness)) + return "memory allocation error"; + + return 0; +} + +const char *grabbag__replaygain_store_to_vorbiscomment_album(FLAC__StreamMetadata *block, float album_gain, float album_peak) +{ + FLAC__ASSERT(0 != block); + FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + + if( + FLAC__metadata_object_vorbiscomment_remove_entries_matching(block, (const char *)GRABBAG__REPLAYGAIN_TAG_ALBUM_GAIN) < 0 || + FLAC__metadata_object_vorbiscomment_remove_entries_matching(block, (const char *)GRABBAG__REPLAYGAIN_TAG_ALBUM_PEAK) < 0 + ) + return "memory allocation error"; + + if( + !append_tag_(block, gain_format_, GRABBAG__REPLAYGAIN_TAG_ALBUM_GAIN, album_gain) || + !append_tag_(block, peak_format_, GRABBAG__REPLAYGAIN_TAG_ALBUM_PEAK, album_peak) + ) + return "memory allocation error"; + + return 0; +} + +const char *grabbag__replaygain_store_to_vorbiscomment_title(FLAC__StreamMetadata *block, float title_gain, float title_peak) +{ + FLAC__ASSERT(0 != block); + FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + + if( + FLAC__metadata_object_vorbiscomment_remove_entries_matching(block, (const char *)GRABBAG__REPLAYGAIN_TAG_TITLE_GAIN) < 0 || + FLAC__metadata_object_vorbiscomment_remove_entries_matching(block, (const char *)GRABBAG__REPLAYGAIN_TAG_TITLE_PEAK) < 0 + ) + return "memory allocation error"; + + if( + !append_tag_(block, gain_format_, GRABBAG__REPLAYGAIN_TAG_TITLE_GAIN, title_gain) || + !append_tag_(block, peak_format_, GRABBAG__REPLAYGAIN_TAG_TITLE_PEAK, title_peak) + ) + return "memory allocation error"; + + return 0; +} + +static const char *store_to_file_pre_(const char *filename, FLAC__Metadata_Chain **chain, FLAC__StreamMetadata **block) +{ + FLAC__Metadata_Iterator *iterator; + const char *error; + FLAC__bool found_vc_block = false; + + if(0 == (*chain = FLAC__metadata_chain_new())) + return "memory allocation error"; + + if(!FLAC__metadata_chain_read(*chain, filename)) { + error = FLAC__Metadata_ChainStatusString[FLAC__metadata_chain_status(*chain)]; + FLAC__metadata_chain_delete(*chain); + return error; + } + + if(0 == (iterator = FLAC__metadata_iterator_new())) { + FLAC__metadata_chain_delete(*chain); + return "memory allocation error"; + } + + FLAC__metadata_iterator_init(iterator, *chain); + + do { + *block = FLAC__metadata_iterator_get_block(iterator); + if((*block)->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) + found_vc_block = true; + } while(!found_vc_block && FLAC__metadata_iterator_next(iterator)); + + if(!found_vc_block) { + /* create a new block */ + *block = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT); + if(0 == *block) { + FLAC__metadata_chain_delete(*chain); + FLAC__metadata_iterator_delete(iterator); + return "memory allocation error"; + } + while(FLAC__metadata_iterator_next(iterator)) + ; + if(!FLAC__metadata_iterator_insert_block_after(iterator, *block)) { + error = FLAC__Metadata_ChainStatusString[FLAC__metadata_chain_status(*chain)]; + FLAC__metadata_chain_delete(*chain); + FLAC__metadata_iterator_delete(iterator); + return error; + } + /* iterator is left pointing to new block */ + FLAC__ASSERT(FLAC__metadata_iterator_get_block(iterator) == *block); + } + + FLAC__metadata_iterator_delete(iterator); + + FLAC__ASSERT(0 != *block); + FLAC__ASSERT((*block)->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + + return 0; +} + +static const char *store_to_file_post_(const char *filename, FLAC__Metadata_Chain *chain, FLAC__bool preserve_modtime) +{ + struct stat stats; + const FLAC__bool have_stats = get_file_stats_(filename, &stats); + + (void)grabbag__file_change_stats(filename, /*read_only=*/false); + + FLAC__metadata_chain_sort_padding(chain); + if(!FLAC__metadata_chain_write(chain, /*use_padding=*/true, preserve_modtime)) { + FLAC__metadata_chain_delete(chain); + return FLAC__Metadata_ChainStatusString[FLAC__metadata_chain_status(chain)]; + } + + FLAC__metadata_chain_delete(chain); + + if(have_stats) + set_file_stats_(filename, &stats); + + return 0; +} + +const char *grabbag__replaygain_store_to_file(const char *filename, float album_gain, float album_peak, float title_gain, float title_peak, FLAC__bool preserve_modtime) +{ + FLAC__Metadata_Chain *chain; + FLAC__StreamMetadata *block; + const char *error; + + if(0 != (error = store_to_file_pre_(filename, &chain, &block))) + return error; + + if(0 != (error = grabbag__replaygain_store_to_vorbiscomment(block, album_gain, album_peak, title_gain, title_peak))) { + FLAC__metadata_chain_delete(chain); + return error; + } + + if(0 != (error = store_to_file_post_(filename, chain, preserve_modtime))) + return error; + + return 0; +} + +const char *grabbag__replaygain_store_to_file_reference(const char *filename, FLAC__bool preserve_modtime) +{ + FLAC__Metadata_Chain *chain; + FLAC__StreamMetadata *block; + const char *error; + + if(0 != (error = store_to_file_pre_(filename, &chain, &block))) + return error; + + if(0 != (error = grabbag__replaygain_store_to_vorbiscomment_reference(block))) { + FLAC__metadata_chain_delete(chain); + return error; + } + + if(0 != (error = store_to_file_post_(filename, chain, preserve_modtime))) + return error; + + return 0; +} + +const char *grabbag__replaygain_store_to_file_album(const char *filename, float album_gain, float album_peak, FLAC__bool preserve_modtime) +{ + FLAC__Metadata_Chain *chain; + FLAC__StreamMetadata *block; + const char *error; + + if(0 != (error = store_to_file_pre_(filename, &chain, &block))) + return error; + + if(0 != (error = grabbag__replaygain_store_to_vorbiscomment_album(block, album_gain, album_peak))) { + FLAC__metadata_chain_delete(chain); + return error; + } + + if(0 != (error = store_to_file_post_(filename, chain, preserve_modtime))) + return error; + + return 0; +} + +const char *grabbag__replaygain_store_to_file_title(const char *filename, float title_gain, float title_peak, FLAC__bool preserve_modtime) +{ + FLAC__Metadata_Chain *chain; + FLAC__StreamMetadata *block; + const char *error; + + if(0 != (error = store_to_file_pre_(filename, &chain, &block))) + return error; + + if(0 != (error = grabbag__replaygain_store_to_vorbiscomment_title(block, title_gain, title_peak))) { + FLAC__metadata_chain_delete(chain); + return error; + } + + if(0 != (error = store_to_file_post_(filename, chain, preserve_modtime))) + return error; + + return 0; +} + +static FLAC__bool parse_double_(const FLAC__StreamMetadata_VorbisComment_Entry *entry, double *val) +{ + char s[32], *end; + const char *p, *q; + double v; + + FLAC__ASSERT(0 != entry); + FLAC__ASSERT(0 != val); + + p = (const char *)entry->entry; + q = strchr(p, '='); + if(0 == q) + return false; + q++; + memset(s, 0, sizeof(s)-1); + strncpy(s, q, local_min(signed_sizeof(s)-1, entry->length - (q-p))); + + v = strtod(s, &end); + if(end == s) + return false; + + *val = v; + return true; +} + +FLAC__bool grabbag__replaygain_load_from_vorbiscomment(const FLAC__StreamMetadata *block, FLAC__bool album_mode, FLAC__bool strict, double *reference, double *gain, double *peak) +{ + int reference_offset, gain_offset, peak_offset; + + FLAC__ASSERT(0 != block); + FLAC__ASSERT(0 != reference); + FLAC__ASSERT(0 != gain); + FLAC__ASSERT(0 != peak); + FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + + /* Default to current level until overridden by a detected tag; this + * will always be true until we change replaygain_analysis.c + */ + *reference = ReplayGainReferenceLoudness; + + if(0 <= (reference_offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block, /*offset=*/0, (const char *)GRABBAG__REPLAYGAIN_TAG_REFERENCE_LOUDNESS))) + (void)parse_double_(block->data.vorbis_comment.comments + reference_offset, reference); + + if(0 > (gain_offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block, /*offset=*/0, (const char *)(album_mode? GRABBAG__REPLAYGAIN_TAG_ALBUM_GAIN : GRABBAG__REPLAYGAIN_TAG_TITLE_GAIN)))) + return !strict && grabbag__replaygain_load_from_vorbiscomment(block, !album_mode, /*strict=*/true, reference, gain, peak); + if(0 > (peak_offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block, /*offset=*/0, (const char *)(album_mode? GRABBAG__REPLAYGAIN_TAG_ALBUM_PEAK : GRABBAG__REPLAYGAIN_TAG_TITLE_PEAK)))) + return !strict && grabbag__replaygain_load_from_vorbiscomment(block, !album_mode, /*strict=*/true, reference, gain, peak); + + if(!parse_double_(block->data.vorbis_comment.comments + gain_offset, gain)) + return !strict && grabbag__replaygain_load_from_vorbiscomment(block, !album_mode, /*strict=*/true, reference, gain, peak); + if(!parse_double_(block->data.vorbis_comment.comments + peak_offset, peak)) + return !strict && grabbag__replaygain_load_from_vorbiscomment(block, !album_mode, /*strict=*/true, reference, gain, peak); + + return true; +} + +double grabbag__replaygain_compute_scale_factor(double peak, double gain, double preamp, FLAC__bool prevent_clipping) +{ + double scale; + FLAC__ASSERT(peak >= 0.0); + gain += preamp; + scale = (float) pow(10.0, gain * 0.05); + if(prevent_clipping && peak > 0.0) { + const double max_scale = (float)(1.0 / peak); + if(scale > max_scale) + scale = max_scale; + } + return scale; +} diff --git a/src/FLAC/src/share/grabbag/seektable.c b/src/FLAC/src/share/grabbag/seektable.c new file mode 100644 index 00000000..55ac766e --- /dev/null +++ b/src/FLAC/src/share/grabbag/seektable.c @@ -0,0 +1,132 @@ +/* grabbag - Convenience lib for various routines common to several tools + * Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include "share/grabbag.h" +#include "FLAC/assert.h" +#include /* for atoi() */ +#include + +#ifdef _MSC_VER +/* There's no strtoll() in MSVC6 so we just write a specialized one */ +static FLAC__int64 local__strtoll(const char *src, char **endptr) +{ + FLAC__bool neg = false; + FLAC__int64 ret = 0; + int c; + FLAC__ASSERT(0 != src); + if(*src == '-') { + neg = true; + src++; + } + while(0 != (c = *src)) { + c -= '0'; + if(c >= 0 && c <= 9) + ret = (ret * 10) + c; + else + break; + src++; + } + if(endptr) + *endptr = (char*)src; + return neg? -ret : ret; +} +#endif + +FLAC__bool grabbag__seektable_convert_specification_to_template(const char *spec, FLAC__bool only_explicit_placeholders, FLAC__uint64 total_samples_to_encode, unsigned sample_rate, FLAC__StreamMetadata *seektable_template, FLAC__bool *spec_has_real_points) +{ + unsigned i; + const char *pt; + + FLAC__ASSERT(0 != spec); + FLAC__ASSERT(0 != seektable_template); + FLAC__ASSERT(seektable_template->type = FLAC__METADATA_TYPE_SEEKTABLE); + + if(0 != spec_has_real_points) + *spec_has_real_points = false; + + for(pt = spec, i = 0; pt && *pt; i++) { + const char *q = strchr(pt, ';'); + FLAC__ASSERT(0 != q); + + if(q > pt) { + if(0 == strncmp(pt, "X;", 2)) { /* -S X */ + if(!FLAC__metadata_object_seektable_template_append_placeholders(seektable_template, 1)) + return false; + } + else if(q[-1] == 'x') { /* -S #x */ + if(total_samples_to_encode > 0) { /* we can only do these if we know the number of samples to encode up front */ + if(0 != spec_has_real_points) + *spec_has_real_points = true; + if(!only_explicit_placeholders) { + const int n = (unsigned)atoi(pt); + if(n > 0) + if(!FLAC__metadata_object_seektable_template_append_spaced_points(seektable_template, (unsigned)n, total_samples_to_encode)) + return false; + } + } + } + else if(q[-1] == 's') { /* -S #s */ + if(total_samples_to_encode > 0) { /* we can only do these if we know the number of samples to encode up front */ + FLAC__ASSERT(sample_rate > 0); + if(0 != spec_has_real_points) + *spec_has_real_points = true; + if(!only_explicit_placeholders) { + const double sec = atof(pt); + if(sec > 0.0) { + unsigned samples = (unsigned)(sec * (double)sample_rate); + if(samples > 0) { + /* +1 for the initial point at sample 0 */ + if(!FLAC__metadata_object_seektable_template_append_spaced_points_by_samples(seektable_template, samples, total_samples_to_encode)) + return false; + } + } + } + } + } + else { /* -S # */ + if(0 != spec_has_real_points) + *spec_has_real_points = true; + if(!only_explicit_placeholders) { + char *endptr; +#ifdef _MSC_VER + const FLAC__int64 n = local__strtoll(pt, &endptr); +#else + const FLAC__int64 n = (FLAC__int64)strtoll(pt, &endptr, 10); +#endif + if( + (n > 0 || (endptr > pt && *endptr == ';')) && /* is a valid number (extra check needed for "0") */ + (total_samples_to_encode == 0 || (FLAC__uint64)n < total_samples_to_encode) /* number is not >= the known total_samples_to_encode */ + ) + if(!FLAC__metadata_object_seektable_template_append_point(seektable_template, (FLAC__uint64)n)) + return false; + } + } + } + + pt = ++q; + } + + if(!FLAC__metadata_object_seektable_template_sort(seektable_template, /*compact=*/true)) + return false; + + return true; +} diff --git a/src/FLAC/src/share/replaygain_anal/Makefile.am b/src/FLAC/src/share/replaygain_anal/Makefile.am new file mode 100644 index 00000000..b8c064ea --- /dev/null +++ b/src/FLAC/src/share/replaygain_anal/Makefile.am @@ -0,0 +1,15 @@ +## Process this file with automake to produce Makefile.in + +AUTOMAKE_OPTIONS = foreign + +INCLUDES = -I$(top_srcdir)/src/FLAC/include/share + +noinst_LTLIBRARIES = libreplaygain_analysis.la + +libreplaygain_analysis_la_SOURCES = replaygain_analysis.c + +debug: + $(MAKE) all CFLAGS="@DEBUG@" + +profile: + $(MAKE) all CFLAGS="@PROFILE@" diff --git a/src/FLAC/src/share/replaygain_anal/replaygain_analysis.c b/src/FLAC/src/share/replaygain_anal/replaygain_analysis.c new file mode 100644 index 00000000..ad9e5ec6 --- /dev/null +++ b/src/FLAC/src/share/replaygain_anal/replaygain_analysis.c @@ -0,0 +1,419 @@ +/* + * 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 + * original coding by Glen Sawyer (glensawyer@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 ;) + * + * minor cosmetic tweaks to integrate with FLAC by Josh Coalson + * + * + * 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. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include + +#include "replaygain_analysis.h" + +Float_t ReplayGainReferenceLoudness = 89.0; /* in dB SPL */ + +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 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) +/* [JEC] the following was originally #defined as: + * (size_t) (MAX_SAMP_FREQ * RMS_WINDOW_TIME) + * but that seemed to fail to take into account the ceil() part of the + * sampleWindow calculation in ResetSampleFrequency(), and was causing + * buffer overflows for 48kHz analysis, hence the +1. + */ +#define MAX_SAMPLES_PER_WINDOW (size_t) (MAX_SAMP_FREQ * RMS_WINDOW_TIME + 1.) /* max. Samples per Time slice */ +#define PINK_REF 64.82 /* 298640883795 */ /* calibration value */ + +static Float_t linprebuf [MAX_ORDER * 2]; +static Float_t* linpre; /* left input samples, with pre-buffer */ +static Float_t lstepbuf [MAX_SAMPLES_PER_WINDOW + MAX_ORDER]; +static Float_t* lstep; /* left "first step" (i.e. post first filter) samples */ +static Float_t loutbuf [MAX_SAMPLES_PER_WINDOW + MAX_ORDER]; +static Float_t* lout; /* left "out" (i.e. post second filter) samples */ +static Float_t rinprebuf [MAX_ORDER * 2]; +static Float_t* rinpre; /* right input samples ... */ +static Float_t rstepbuf [MAX_SAMPLES_PER_WINDOW + MAX_ORDER]; +static Float_t* rstep; +static Float_t routbuf [MAX_SAMPLES_PER_WINDOW + MAX_ORDER]; +static Float_t* rout; +static unsigned int sampleWindow; /* number of samples required to reach number of milliseconds required for RMS window */ +static unsigned long totsamp; +static double lsum; +static double rsum; +static int freqindex; +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 +#pragma warning ( disable : 4305 ) +#endif + +static const Float_t AYule [9] [11] = { + { 1., -3.84664617118067, 7.81501653005538,-11.34170355132042, 13.05504219327545,-12.28759895145294, 9.48293806319790, -5.87257861775999, 2.75465861874613, -0.86984376593551, 0.13919314567432 }, + { 1., -3.47845948550071, 6.36317777566148, -8.54751527471874, 9.47693607801280, -8.81498681370155, 6.85401540936998, -4.39470996079559, 2.19611684890774, -0.75104302451432, 0.13149317958808 }, + { 1., -2.37898834973084, 2.84868151156327, -2.64577170229825, 2.23697657451713, -1.67148153367602, 1.00595954808547, -0.45953458054983, 0.16378164858596, -0.05032077717131, 0.02347897407020 }, + { 1., -1.61273165137247, 1.07977492259970, -0.25656257754070, -0.16276719120440, -0.22638893773906, 0.39120800788284, -0.22138138954925, 0.04500235387352, 0.02005851806501, 0.00302439095741 }, + { 1., -1.49858979367799, 0.87350271418188, 0.12205022308084, -0.80774944671438, 0.47854794562326, -0.12453458140019, -0.04067510197014, 0.08333755284107, -0.04237348025746, 0.02977207319925 }, + { 1., -0.62820619233671, 0.29661783706366, -0.37256372942400, 0.00213767857124, -0.42029820170918, 0.22199650564824, 0.00613424350682, 0.06747620744683, 0.05784820375801, 0.03222754072173 }, + { 1., -1.04800335126349, 0.29156311971249, -0.26806001042947, 0.00819999645858, 0.45054734505008, -0.33032403314006, 0.06739368333110, -0.04784254229033, 0.01639907836189, 0.01807364323573 }, + { 1., -0.51035327095184, -0.31863563325245, -0.20256413484477, 0.14728154134330, 0.38952639978999, -0.23313271880868, -0.05246019024463, -0.02505961724053, 0.02442357316099, 0.01818801111503 }, + { 1., -0.25049871956020, -0.43193942311114, -0.03424681017675, -0.04678328784242, 0.26408300200955, 0.15113130533216, -0.17556493366449, -0.18823009262115, 0.05477720428674, 0.04704409688120 } +}; + +static const Float_t BYule [9] [11] = { + { 0.03857599435200, -0.02160367184185, -0.00123395316851, -0.00009291677959, -0.01655260341619, 0.02161526843274, -0.02074045215285, 0.00594298065125, 0.00306428023191, 0.00012025322027, 0.00288463683916 }, + { 0.05418656406430, -0.02911007808948, -0.00848709379851, -0.00851165645469, -0.00834990904936, 0.02245293253339, -0.02596338512915, 0.01624864962975, -0.00240879051584, 0.00674613682247, -0.00187763777362 }, + { 0.15457299681924, -0.09331049056315, -0.06247880153653, 0.02163541888798, -0.05588393329856, 0.04781476674921, 0.00222312597743, 0.03174092540049, -0.01390589421898, 0.00651420667831, -0.00881362733839 }, + { 0.30296907319327, -0.22613988682123, -0.08587323730772, 0.03282930172664, -0.00915702933434, -0.02364141202522, -0.00584456039913, 0.06276101321749, -0.00000828086748, 0.00205861885564, -0.02950134983287 }, + { 0.33642304856132, -0.25572241425570, -0.11828570177555, 0.11921148675203, -0.07834489609479, -0.00469977914380, -0.00589500224440, 0.05724228140351, 0.00832043980773, -0.01635381384540, -0.01760176568150 }, + { 0.44915256608450, -0.14351757464547, -0.22784394429749, -0.01419140100551, 0.04078262797139, -0.12398163381748, 0.04097565135648, 0.10478503600251, -0.01863887810927, -0.03193428438915, 0.00541907748707 }, + { 0.56619470757641, -0.75464456939302, 0.16242137742230, 0.16744243493672, -0.18901604199609, 0.30931782841830, -0.27562961986224, 0.00647310677246, 0.08647503780351, -0.03788984554840, -0.00588215443421 }, + { 0.58100494960553, -0.53174909058578, -0.14289799034253, 0.17520704835522, 0.02377945217615, 0.15558449135573, -0.25344790059353, 0.01628462406333, 0.06920467763959, -0.03721611395801, -0.00749618797172 }, + { 0.53648789255105, -0.42163034350696, -0.00275953611929, 0.04267842219415, -0.10214864179676, 0.14590772289388, -0.02459864859345, -0.11202315195388, -0.04060034127000, 0.04788665548180, -0.02217936801134 } +}; + +static const Float_t AButter [9] [3] = { + { 1., -1.97223372919527, 0.97261396931306 }, + { 1., -1.96977855582618, 0.97022847566350 }, + { 1., -1.95835380975398, 0.95920349965459 }, + { 1., -1.95002759149878, 0.95124613669835 }, + { 1., -1.94561023566527, 0.94705070426118 }, + { 1., -1.92783286977036, 0.93034775234268 }, + { 1., -1.91858953033784, 0.92177618768381 }, + { 1., -1.91542108074780, 0.91885558323625 }, + { 1., -1.88903307939452, 0.89487434461664 } +}; + +static const Float_t BButter [9] [3] = { + { 0.98621192462708, -1.97242384925416, 0.98621192462708 }, + { 0.98500175787242, -1.97000351574484, 0.98500175787242 }, + { 0.97938932735214, -1.95877865470428, 0.97938932735214 }, + { 0.97531843204928, -1.95063686409857, 0.97531843204928 }, + { 0.97316523498161, -1.94633046996323, 0.97316523498161 }, + { 0.96454515552826, -1.92909031105652, 0.96454515552826 }, + { 0.96009142950541, -1.92018285901082, 0.96009142950541 }, + { 0.95856916599601, -1.91713833199203, 0.95856916599601 }, + { 0.94597685600279, -1.89195371200558, 0.94597685600279 } +}; + +#ifdef WIN32 +#pragma warning ( default : 4305 ) +#endif + +/* When calling this procedure, make sure that ip[-order] and op[-order] point to real data! */ + +static void +filter ( const Float_t* input, Float_t* output, size_t nSamples, const Float_t* a, const Float_t* b, size_t order ) +{ + double y; + size_t i; + size_t k; + + for ( i = 0; i < nSamples; i++ ) { + y = input[i] * b[0]; + for ( k = 1; k <= order; k++ ) + y += input[i-k] * b[k] - output[i-k] * a[k]; + output[i] = (Float_t)y; + } +} + +/* 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 = 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 */ + +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 > (long)(sampleWindow-totsamp) ? (long)(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; + } + + filter ( curleft , lstep + totsamp, cursamples, AYule[freqindex], BYule[freqindex], YULE_ORDER ); + filter ( curright, rstep + totsamp, cursamples, AYule[freqindex], BYule[freqindex], YULE_ORDER ); + + filter ( lstep + totsamp, lout + totsamp, cursamples, AButter[freqindex], BButter[freqindex], BUTTER_ORDER ); + filter ( rstep + totsamp, rout + totsamp, cursamples, AButter[freqindex], BButter[freqindex], BUTTER_ORDER ); + + for ( i = 0; i < cursamples; i++ ) { /* Get the squared values */ + lsum += lout [totsamp+i] * lout [totsamp+i]; + rsum += rout [totsamp+i] * rout [totsamp+i]; + } + + 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 = (int)(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 = 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; + unsigned int i; + + retval = analyzeResult ( A, sizeof(A)/sizeof(*A) ); + + for ( i = 0; i < 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 replaygain_analysis.c */ diff --git a/src/FLAC/src/share/replaygain_syn/Makefile.am b/src/FLAC/src/share/replaygain_syn/Makefile.am new file mode 100644 index 00000000..cf05b330 --- /dev/null +++ b/src/FLAC/src/share/replaygain_syn/Makefile.am @@ -0,0 +1,18 @@ +## Process this file with automake to produce Makefile.in + +AUTOMAKE_OPTIONS = foreign + +SUBDIRS = include . + +INCLUDES = -I$(top_srcdir)/src/FLAC/include -I$(top_srcdir)/src/FLAC/include/share \ + -I$(top_srcdir)/src/FLAC/src/share/replaygain_syn/include + +noinst_LTLIBRARIES = libreplaygain_synthesis.la + +libreplaygain_synthesis_la_SOURCES = replaygain_synthesis.c + +debug: + $(MAKE) all CFLAGS="@DEBUG@" + +profile: + $(MAKE) all CFLAGS="@PROFILE@" diff --git a/src/FLAC/src/share/replaygain_syn/include/Makefile.am b/src/FLAC/src/share/replaygain_syn/include/Makefile.am new file mode 100644 index 00000000..4418640f --- /dev/null +++ b/src/FLAC/src/share/replaygain_syn/include/Makefile.am @@ -0,0 +1,18 @@ +# replaygain_synthesis - Routines for applying ReplayGain to a signal +# Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson +# +# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +SUBDIRS = private diff --git a/src/FLAC/src/share/replaygain_syn/include/private/Makefile.am b/src/FLAC/src/share/replaygain_syn/include/private/Makefile.am new file mode 100644 index 00000000..081d7ba4 --- /dev/null +++ b/src/FLAC/src/share/replaygain_syn/include/private/Makefile.am @@ -0,0 +1,19 @@ +# replaygain_synthesis - Routines for applying ReplayGain to a signal +# Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson +# +# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +noinst_HEADERS = \ + fast_float_math_hack.h diff --git a/src/FLAC/src/share/replaygain_syn/include/private/fast_float_math_hack.h b/src/FLAC/src/share/replaygain_syn/include/private/fast_float_math_hack.h new file mode 100644 index 00000000..d8608fc4 --- /dev/null +++ b/src/FLAC/src/share/replaygain_syn/include/private/fast_float_math_hack.h @@ -0,0 +1,39 @@ +# ifdef __ICL /* only Intel C compiler has fmath ??? */ + + #include + +/* Nearest integer, absolute value, etc. */ + + #define ceil ceilf + #define fabs fabsf + #define floor floorf + #define fmod fmodf + #define rint rintf + #define hypot hypotf + +/* Power functions */ + + #define pow powf + #define sqrt sqrtf + +/* Exponential and logarithmic functions */ + + #define exp expf + #define log logf + #define log10 log10f + +/* Trigonometric functions */ + + #define acos acosf + #define asin asinf + #define atan atanf + #define cos cosf + #define sin sinf + #define tan tanf + +/* Hyperbolic functions */ + #define cosh coshf + #define sinh sinhf + #define tanh tanhf + +# endif diff --git a/src/FLAC/src/share/replaygain_syn/replaygain_synthesis.c b/src/FLAC/src/share/replaygain_syn/replaygain_synthesis.c new file mode 100644 index 00000000..fa850211 --- /dev/null +++ b/src/FLAC/src/share/replaygain_syn/replaygain_synthesis.c @@ -0,0 +1,467 @@ +/* replaygain_synthesis - Routines for applying ReplayGain to a signal + * Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +/* + * This is an aggregation of pieces of code from John Edwards' WaveGain + * program. Mostly cosmetic changes were made; otherwise, the dithering + * code is almost untouched and the gain processing was converted from + * processing a whole file to processing chunks of samples. + * + * The original copyright notices for WaveGain's dither.c and wavegain.c + * appear below: + */ +/* + * (c) 2002 John Edwards + * mostly lifted from work by Frank Klemm + * random functions for dithering. + */ +/* + * Copyright (C) 2002 John Edwards + * Additional code by Magnus Holmgren and Gian-Carlo Pascutto + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include /* for memset() */ +#include +#include "private/fast_float_math_hack.h" +#include "replaygain_synthesis.h" +#include "FLAC/assert.h" + +#ifndef FLaC__INLINE +#define FLaC__INLINE +#endif + +/* adjust for compilers that can't understand using LL suffix for int64_t literals */ +#ifdef _MSC_VER +#define FLAC__I64L(x) x +#else +#define FLAC__I64L(x) x##LL +#endif + + +/* + * the following is based on parts of dither.c + */ + + +/* + * 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. + */ + +static unsigned int random_int_(void) +{ + static const unsigned char parity_[256] = { + 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; + + unsigned int t1, t2, t3, t4; + + /* Parity calculation is done via table lookup, this is also available + * on CPUs without parity, can be implemented in C and avoid unpredictable + * jumps and slow rotate through the carry flag operations. + */ + t3 = t1 = r1_; t4 = t2 = r2_; + t1 &= 0xF5; t2 >>= 25; + t1 = parity_[t1]; t2 &= 0x63; + t1 <<= 31; t2 = parity_[t2]; + + return (r1_ = (t3 >> 1) | t1 ) ^ (r2_ = (t4 + t4) | t2 ); +} + +/* gives a equal distributed random number */ +/* between -2^31*mult and +2^31*mult */ +static double random_equi_(double mult) +{ + return mult * (int) random_int_(); +} + +/* gives a triangular distributed random number */ +/* between -2^32*mult and +2^32*mult */ +static double random_triangular_(double 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 +}; + + +static 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 FLAC__replaygain_synthesis__init_dither_context(DitherContext *d, 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 indx; + + if (shapingtype < 0) shapingtype = 0; + if (shapingtype > 3) shapingtype = 3; + d->ShapingType = (NoiseShaping)shapingtype; + indx = bits - 11 - shapingtype; + if (indx < 0) indx = 0; + if (indx > 9) indx = 9; + + memset ( d->ErrorHistory , 0, sizeof (d->ErrorHistory ) ); + memset ( d->DitherHistory, 0, sizeof (d->DitherHistory) ); + + d->FilterCoeff = F [shapingtype]; + d->Mask = ((FLAC__uint64)-1) << (32 - bits); + d->Add = 0.5 * ((1L << (32 - bits)) - 1); + d->Dither = 0.01f*default_dither[indx] / (((FLAC__int64)1) << bits); + d->LastHistoryIndex = 0; +} + +/* + * the following is based on parts of wavegain.c + */ + +static FLaC__INLINE FLAC__int64 dither_output_(DitherContext *d, FLAC__bool do_dithering, int shapingtype, int i, double Sum, int k) +{ + union { + double d; + FLAC__int64 i; + } doubletmp; + double Sum2; + FLAC__int64 val; + +#define ROUND64(x) ( doubletmp.d = (x) + d->Add + (FLAC__int64)FLAC__I64L(0x001FFFFD80000000), doubletmp.i - (FLAC__int64)FLAC__I64L(0x433FFFFD80000000) ) + + if(do_dithering) { + if(shapingtype == 0) { + double tmp = random_equi_(d->Dither); + Sum2 = tmp - d->LastRandomNumber [k]; + d->LastRandomNumber [k] = (int)tmp; + Sum2 = Sum += Sum2; + val = ROUND64(Sum2) & d->Mask; + } + else { + Sum2 = random_triangular_(d->Dither) - scalar16_(d->DitherHistory[k], d->FilterCoeff + i); + Sum += d->DitherHistory [k] [(-1-i)&15] = (float)Sum2; + Sum2 = Sum + scalar16_(d->ErrorHistory [k], d->FilterCoeff + i); + val = ROUND64(Sum2) & d->Mask; + d->ErrorHistory [k] [(-1-i)&15] = (float)(Sum - val); + } + return val; + } + else + return ROUND64(Sum); + +#undef ROUND64 +} + +#if 0 + float peak = 0.f, + new_peak, + factor_clip + double scale, + dB; + + ... + + peak is in the range -32768.0 .. 32767.0 + + /* calculate factors for ReplayGain and ClippingPrevention */ + *track_gain = 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 /= scale; + scale *= factor_clip; + } + new_peak = (float) peak * scale; + + dB = 20. * log10(scale); + *track_gain = (float) dB; + + const double scale = pow(10., (double)gain * 0.05); +#endif + + +size_t FLAC__replaygain_synthesis__apply_gain(FLAC__byte *data_out, FLAC__bool little_endian_data_out, FLAC__bool unsigned_data_out, const FLAC__int32 * const input[], unsigned wide_samples, unsigned channels, const unsigned source_bps, const unsigned target_bps, const double scale, const FLAC__bool hard_limit, FLAC__bool do_dithering, DitherContext *dither_context) +{ + static const FLAC__int32 conv_factors_[33] = { + -1, /* 0 bits-per-sample (not supported) */ + -1, /* 1 bits-per-sample (not supported) */ + -1, /* 2 bits-per-sample (not supported) */ + -1, /* 3 bits-per-sample (not supported) */ + 268435456, /* 4 bits-per-sample */ + 134217728, /* 5 bits-per-sample */ + 67108864, /* 6 bits-per-sample */ + 33554432, /* 7 bits-per-sample */ + 16777216, /* 8 bits-per-sample */ + 8388608, /* 9 bits-per-sample */ + 4194304, /* 10 bits-per-sample */ + 2097152, /* 11 bits-per-sample */ + 1048576, /* 12 bits-per-sample */ + 524288, /* 13 bits-per-sample */ + 262144, /* 14 bits-per-sample */ + 131072, /* 15 bits-per-sample */ + 65536, /* 16 bits-per-sample */ + 32768, /* 17 bits-per-sample */ + 16384, /* 18 bits-per-sample */ + 8192, /* 19 bits-per-sample */ + 4096, /* 20 bits-per-sample */ + 2048, /* 21 bits-per-sample */ + 1024, /* 22 bits-per-sample */ + 512, /* 23 bits-per-sample */ + 256, /* 24 bits-per-sample */ + 128, /* 25 bits-per-sample */ + 64, /* 26 bits-per-sample */ + 32, /* 27 bits-per-sample */ + 16, /* 28 bits-per-sample */ + 8, /* 29 bits-per-sample */ + 4, /* 30 bits-per-sample */ + 2, /* 31 bits-per-sample */ + 1 /* 32 bits-per-sample */ + }; + static const FLAC__int64 hard_clip_factors_[33] = { + 0, /* 0 bits-per-sample (not supported) */ + 0, /* 1 bits-per-sample (not supported) */ + 0, /* 2 bits-per-sample (not supported) */ + 0, /* 3 bits-per-sample (not supported) */ + -8, /* 4 bits-per-sample */ + -16, /* 5 bits-per-sample */ + -32, /* 6 bits-per-sample */ + -64, /* 7 bits-per-sample */ + -128, /* 8 bits-per-sample */ + -256, /* 9 bits-per-sample */ + -512, /* 10 bits-per-sample */ + -1024, /* 11 bits-per-sample */ + -2048, /* 12 bits-per-sample */ + -4096, /* 13 bits-per-sample */ + -8192, /* 14 bits-per-sample */ + -16384, /* 15 bits-per-sample */ + -32768, /* 16 bits-per-sample */ + -65536, /* 17 bits-per-sample */ + -131072, /* 18 bits-per-sample */ + -262144, /* 19 bits-per-sample */ + -524288, /* 20 bits-per-sample */ + -1048576, /* 21 bits-per-sample */ + -2097152, /* 22 bits-per-sample */ + -4194304, /* 23 bits-per-sample */ + -8388608, /* 24 bits-per-sample */ + -16777216, /* 25 bits-per-sample */ + -33554432, /* 26 bits-per-sample */ + -67108864, /* 27 bits-per-sample */ + -134217728, /* 28 bits-per-sample */ + -268435456, /* 29 bits-per-sample */ + -536870912, /* 30 bits-per-sample */ + -1073741824, /* 31 bits-per-sample */ + (FLAC__int64)(-1073741824) * 2 /* 32 bits-per-sample */ + }; + const FLAC__int32 conv_factor = conv_factors_[target_bps]; + const FLAC__int64 hard_clip_factor = hard_clip_factors_[target_bps]; + /* + * The integer input coming in has a varying range based on the + * source_bps. We want to normalize it to [-1.0, 1.0) so instead + * of doing two multiplies on each sample, we just multiple + * 'scale' by 1/(2^(source_bps-1)) + */ + const double multi_scale = scale / (double)(1u << (source_bps-1)); + + FLAC__byte * const start = data_out; + unsigned i, channel; + const FLAC__int32 *input_; + double sample; + const unsigned bytes_per_sample = target_bps / 8; + const unsigned last_history_index = dither_context->LastHistoryIndex; + NoiseShaping noise_shaping = dither_context->ShapingType; + FLAC__int64 val64; + FLAC__int32 val32; + FLAC__int32 uval32; + const FLAC__uint32 twiggle = 1u << (target_bps - 1); + + FLAC__ASSERT(channels > 0 && channels <= FLAC_SHARE__MAX_SUPPORTED_CHANNELS); + FLAC__ASSERT(source_bps >= 4); + FLAC__ASSERT(target_bps >= 4); + FLAC__ASSERT(source_bps <= 32); + FLAC__ASSERT(target_bps < 32); + FLAC__ASSERT((target_bps & 7) == 0); + + for(channel = 0; channel < channels; channel++) { + const unsigned incr = bytes_per_sample * channels; + data_out = start + bytes_per_sample * channel; + input_ = input[channel]; + for(i = 0; i < wide_samples; i++, data_out += incr) { + sample = (double)input_[i] * multi_scale; + + if(hard_limit) { + /* hard 6dB limiting */ + if(sample < -0.5) + sample = tanh((sample + 0.5) / (1-0.5)) * (1-0.5) - 0.5; + else if(sample > 0.5) + sample = tanh((sample - 0.5) / (1-0.5)) * (1-0.5) + 0.5; + } + sample *= 2147483647.f; + + val64 = dither_output_(dither_context, do_dithering, noise_shaping, (i + last_history_index) % 32, sample, channel) / conv_factor; + + val32 = (FLAC__int32)val64; + if(val64 >= -hard_clip_factor) + val32 = (FLAC__int32)(-(hard_clip_factor+1)); + else if(val64 < hard_clip_factor) + val32 = (FLAC__int32)hard_clip_factor; + + uval32 = (FLAC__uint32)val32; + if (unsigned_data_out) + uval32 ^= twiggle; + + if (little_endian_data_out) { + switch(target_bps) { + case 24: + data_out[2] = (FLAC__byte)(uval32 >> 16); + /* fall through */ + case 16: + data_out[1] = (FLAC__byte)(uval32 >> 8); + /* fall through */ + case 8: + data_out[0] = (FLAC__byte)uval32; + break; + } + } + else { + switch(target_bps) { + case 24: + data_out[0] = (FLAC__byte)(uval32 >> 16); + data_out[1] = (FLAC__byte)(uval32 >> 8); + data_out[2] = (FLAC__byte)uval32; + break; + case 16: + data_out[0] = (FLAC__byte)(uval32 >> 8); + data_out[1] = (FLAC__byte)uval32; + break; + case 8: + data_out[0] = (FLAC__byte)uval32; + break; + } + } + } + } + dither_context->LastHistoryIndex = (last_history_index + wide_samples) % 32; + + return wide_samples * channels * (target_bps/8); +} diff --git a/src/FLAC/src/share/utf8/Makefile.am b/src/FLAC/src/share/utf8/Makefile.am new file mode 100644 index 00000000..9b3282d5 --- /dev/null +++ b/src/FLAC/src/share/utf8/Makefile.am @@ -0,0 +1,21 @@ +## Process this file with automake to produce Makefile.in + +AUTOMAKE_OPTIONS = foreign + +INCLUDES = -I$(top_srcdir)/src/FLAC/include/share + +noinst_LTLIBRARIES = libutf8.la + +libutf8_la_SOURCES = charset.c charset.h iconvert.c utf8.c + +EXTRA_DIST = \ + charmaps.h \ + makemap.c \ + charset_test.c \ + charsetmap.h + +debug: + $(MAKE) all CFLAGS="@DEBUG@" + +profile: + $(MAKE) all CFLAGS="@PROFILE@" diff --git a/src/FLAC/src/share/utf8/charmaps.h b/src/FLAC/src/share/utf8/charmaps.h new file mode 100644 index 00000000..690d8906 --- /dev/null +++ b/src/FLAC/src/share/utf8/charmaps.h @@ -0,0 +1,57 @@ + +/* + * If you need to generate more maps, use makemap.c on a system + * with a decent iconv. + */ + +static const unsigned short mapping_iso_8859_2[256] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, + 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, + 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, + 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, + 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, + 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, + 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, + 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, + 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, + 0x00a0, 0x0104, 0x02d8, 0x0141, 0x00a4, 0x013d, 0x015a, 0x00a7, + 0x00a8, 0x0160, 0x015e, 0x0164, 0x0179, 0x00ad, 0x017d, 0x017b, + 0x00b0, 0x0105, 0x02db, 0x0142, 0x00b4, 0x013e, 0x015b, 0x02c7, + 0x00b8, 0x0161, 0x015f, 0x0165, 0x017a, 0x02dd, 0x017e, 0x017c, + 0x0154, 0x00c1, 0x00c2, 0x0102, 0x00c4, 0x0139, 0x0106, 0x00c7, + 0x010c, 0x00c9, 0x0118, 0x00cb, 0x011a, 0x00cd, 0x00ce, 0x010e, + 0x0110, 0x0143, 0x0147, 0x00d3, 0x00d4, 0x0150, 0x00d6, 0x00d7, + 0x0158, 0x016e, 0x00da, 0x0170, 0x00dc, 0x00dd, 0x0162, 0x00df, + 0x0155, 0x00e1, 0x00e2, 0x0103, 0x00e4, 0x013a, 0x0107, 0x00e7, + 0x010d, 0x00e9, 0x0119, 0x00eb, 0x011b, 0x00ed, 0x00ee, 0x010f, + 0x0111, 0x0144, 0x0148, 0x00f3, 0x00f4, 0x0151, 0x00f6, 0x00f7, + 0x0159, 0x016f, 0x00fa, 0x0171, 0x00fc, 0x00fd, 0x0163, 0x02d9 +}; + +static struct { + const char *name; + const unsigned short *map; + struct charset *charset; +} maps[] = { + { "ISO-8859-2", mapping_iso_8859_2, 0 }, + { 0, 0, 0 } +}; + +static const struct { + const char *bad; + const char *good; +} names[] = { + { "ANSI_X3.4-1968", "us-ascii" }, + { 0, 0 } +}; diff --git a/src/FLAC/src/share/utf8/charset.c b/src/FLAC/src/share/utf8/charset.c new file mode 100644 index 00000000..1610ad22 --- /dev/null +++ b/src/FLAC/src/share/utf8/charset.c @@ -0,0 +1,532 @@ +/* + * Copyright (C) 2001 Edmund Grimley Evans + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * See the corresponding header file for a description of the functions + * that this file provides. + * + * This was first written for Ogg Vorbis but could be of general use. + * + * The only deliberate assumption about data sizes is that a short has + * at least 16 bits, but this code has only been tested on systems with + * 8-bit char, 16-bit short and 32-bit int. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#ifndef HAVE_ICONV /* should be ifdef USE_CHARSET_CONVERT */ + +#include + +#include "charset.h" + +#include "charmaps.h" + +/* + * This is like the standard strcasecmp, but it does not depend + * on the locale. Locale-dependent functions can be dangerous: + * we once had a bug involving strcasecmp("iso", "ISO") in a + * Turkish locale! + * + * (I'm not really sure what the official standard says + * about the sign of strcasecmp("Z", "["), but usually + * we're only interested in whether it's zero.) + */ + +static int ascii_strcasecmp(const char *s1, const char *s2) +{ + char c1, c2; + + for (;; s1++, s2++) { + if (!*s1 || !*s1) + break; + if (*s1 == *s2) + continue; + c1 = *s1; + if ('a' <= c1 && c1 <= 'z') + c1 += 'A' - 'a'; + c2 = *s2; + if ('a' <= c2 && c2 <= 'z') + c2 += 'A' - 'a'; + if (c1 != c2) + break; + } + return (unsigned char)*s1 - (unsigned char)*s2; +} + +/* + * UTF-8 equivalents of the C library's wctomb() and mbtowc(). + */ + +int utf8_mbtowc(int *pwc, const char *s, size_t n) +{ + unsigned char c; + int wc, i, k; + + if (!n || !s) + return 0; + + c = *s; + if (c < 0x80) { + if (pwc) + *pwc = c; + return c ? 1 : 0; + } + else if (c < 0xc2) + return -1; + else if (c < 0xe0) { + if (n >= 2 && (s[1] & 0xc0) == 0x80) { + if (pwc) + *pwc = ((c & 0x1f) << 6) | (s[1] & 0x3f); + return 2; + } + else + return -1; + } + else if (c < 0xf0) + k = 3; + else if (c < 0xf8) + k = 4; + else if (c < 0xfc) + k = 5; + else if (c < 0xfe) + k = 6; + else + return -1; + + if (n < (size_t)k) + return -1; + wc = *s++ & ((1 << (7 - k)) - 1); + for (i = 1; i < k; i++) { + if ((*s & 0xc0) != 0x80) + return -1; + wc = (wc << 6) | (*s++ & 0x3f); + } + if (wc < (1 << (5 * k - 4))) + return -1; + if (pwc) + *pwc = wc; + return k; +} + +int utf8_wctomb(char *s, int wc1) +{ + unsigned int wc = wc1; + + if (!s) + return 0; + if (wc < (1u << 7)) { + *s++ = wc; + return 1; + } + else if (wc < (1u << 11)) { + *s++ = 0xc0 | (wc >> 6); + *s++ = 0x80 | (wc & 0x3f); + return 2; + } + else if (wc < (1u << 16)) { + *s++ = 0xe0 | (wc >> 12); + *s++ = 0x80 | ((wc >> 6) & 0x3f); + *s++ = 0x80 | (wc & 0x3f); + return 3; + } + else if (wc < (1u << 21)) { + *s++ = 0xf0 | (wc >> 18); + *s++ = 0x80 | ((wc >> 12) & 0x3f); + *s++ = 0x80 | ((wc >> 6) & 0x3f); + *s++ = 0x80 | (wc & 0x3f); + return 4; + } + else if (wc < (1u << 26)) { + *s++ = 0xf8 | (wc >> 24); + *s++ = 0x80 | ((wc >> 18) & 0x3f); + *s++ = 0x80 | ((wc >> 12) & 0x3f); + *s++ = 0x80 | ((wc >> 6) & 0x3f); + *s++ = 0x80 | (wc & 0x3f); + return 5; + } + else if (wc < (1u << 31)) { + *s++ = 0xfc | (wc >> 30); + *s++ = 0x80 | ((wc >> 24) & 0x3f); + *s++ = 0x80 | ((wc >> 18) & 0x3f); + *s++ = 0x80 | ((wc >> 12) & 0x3f); + *s++ = 0x80 | ((wc >> 6) & 0x3f); + *s++ = 0x80 | (wc & 0x3f); + return 6; + } + else + return -1; +} + +/* + * The charset "object" and methods. + */ + +struct charset { + int max; + int (*mbtowc)(void *table, int *pwc, const char *s, size_t n); + int (*wctomb)(void *table, char *s, int wc); + void *map; +}; + +int charset_mbtowc(struct charset *charset, int *pwc, const char *s, size_t n) +{ + return (*charset->mbtowc)(charset->map, pwc, s, n); +} + +int charset_wctomb(struct charset *charset, char *s, int wc) +{ + return (*charset->wctomb)(charset->map, s, wc); +} + +int charset_max(struct charset *charset) +{ + return charset->max; +} + +/* + * Implementation of UTF-8. + */ + +static int mbtowc_utf8(void *map, int *pwc, const char *s, size_t n) +{ + (void)map; + return utf8_mbtowc(pwc, s, n); +} + +static int wctomb_utf8(void *map, char *s, int wc) +{ + (void)map; + return utf8_wctomb(s, wc); +} + +/* + * Implementation of US-ASCII. + * Probably on most architectures this compiles to less than 256 bytes + * of code, so we can save space by not having a table for this one. + */ + +static int mbtowc_ascii(void *map, int *pwc, const char *s, size_t n) +{ + int wc; + + (void)map; + if (!n || !s) + return 0; + wc = (unsigned char)*s; + if (wc & ~0x7f) + return -1; + if (pwc) + *pwc = wc; + return wc ? 1 : 0; +} + +static int wctomb_ascii(void *map, char *s, int wc) +{ + (void)map; + if (!s) + return 0; + if (wc & ~0x7f) + return -1; + *s = wc; + return 1; +} + +/* + * Implementation of ISO-8859-1. + * Probably on most architectures this compiles to less than 256 bytes + * of code, so we can save space by not having a table for this one. + */ + +static int mbtowc_iso1(void *map, int *pwc, const char *s, size_t n) +{ + int wc; + + (void)map; + if (!n || !s) + return 0; + wc = (unsigned char)*s; + if (wc & ~0xff) + return -1; + if (pwc) + *pwc = wc; + return wc ? 1 : 0; +} + +static int wctomb_iso1(void *map, char *s, int wc) +{ + (void)map; + if (!s) + return 0; + if (wc & ~0xff) + return -1; + *s = wc; + return 1; +} + +/* + * Implementation of any 8-bit charset. + */ + +struct map { + const unsigned short *from; + struct inverse_map *to; +}; + +static int mbtowc_8bit(void *map1, int *pwc, const char *s, size_t n) +{ + struct map *map = map1; + unsigned short wc; + + if (!n || !s) + return 0; + wc = map->from[(unsigned char)*s]; + if (wc == 0xffff) + return -1; + if (pwc) + *pwc = (int)wc; + return wc ? 1 : 0; +} + +/* + * For the inverse map we use a hash table, which has the advantages + * of small constant memory requirement and simple memory allocation, + * but the disadvantage of slow conversion in the worst case. + * If you need real-time performance while letting a potentially + * malicious user define their own map, then the method used in + * linux/drivers/char/consolemap.c would be more appropriate. + */ + +struct inverse_map { + unsigned char first[256]; + unsigned char next[256]; +}; + +/* + * The simple hash is good enough for this application. + * Use the alternative trivial hashes for testing. + */ +#define HASH(i) ((i) & 0xff) +/* #define HASH(i) 0 */ +/* #define HASH(i) 99 */ + +static struct inverse_map *make_inverse_map(const unsigned short *from) +{ + struct inverse_map *to; + char used[256]; + int i, j, k; + + to = (struct inverse_map *)malloc(sizeof(struct inverse_map)); + if (!to) + return 0; + for (i = 0; i < 256; i++) + to->first[i] = to->next[i] = used[i] = 0; + for (i = 255; i >= 0; i--) + if (from[i] != 0xffff) { + k = HASH(from[i]); + to->next[i] = to->first[k]; + to->first[k] = i; + used[k] = 1; + } + + /* Point the empty buckets at an empty list. */ + for (i = 0; i < 256; i++) + if (!to->next[i]) + break; + if (i < 256) + for (j = 0; j < 256; j++) + if (!used[j]) + to->first[j] = i; + + return to; +} + +static +int wctomb_8bit(void *map1, char *s, int wc1) +{ + struct map *map = map1; + unsigned short wc = wc1; + int i; + + if (!s) + return 0; + + if (wc1 & ~0xffff) + return -1; + + if (1) /* Change 1 to 0 to test the case where malloc fails. */ + if (!map->to) + map->to = make_inverse_map(map->from); + + if (map->to) { + /* Use the inverse map. */ + i = map->to->first[HASH(wc)]; + for (;;) { + if (map->from[i] == wc) { + *s = i; + return 1; + } + if (!(i = map->to->next[i])) + break; + } + } + else { + /* We don't have an inverse map, so do a linear search. */ + for (i = 0; i < 256; i++) + if (map->from[i] == wc) { + *s = i; + return 1; + } + } + + return -1; +} + +/* + * The "constructor" charset_find(). + */ + +struct charset charset_utf8 = { + 6, + &mbtowc_utf8, + &wctomb_utf8, + 0 +}; + +struct charset charset_iso1 = { + 1, + &mbtowc_iso1, + &wctomb_iso1, + 0 +}; + +struct charset charset_ascii = { + 1, + &mbtowc_ascii, + &wctomb_ascii, + 0 +}; + +struct charset *charset_find(const char *code) +{ + int i; + + /* Find good (MIME) name. */ + for (i = 0; names[i].bad; i++) + if (!ascii_strcasecmp(code, names[i].bad)) { + code = names[i].good; + break; + } + + /* Recognise some charsets for which we avoid using a table. */ + if (!ascii_strcasecmp(code, "UTF-8")) + return &charset_utf8; + if (!ascii_strcasecmp(code, "US-ASCII")) + return &charset_ascii; + if (!ascii_strcasecmp(code, "ISO-8859-1")) + return &charset_iso1; + + /* Look for a mapping for a simple 8-bit encoding. */ + for (i = 0; maps[i].name; i++) + if (!ascii_strcasecmp(code, maps[i].name)) { + if (!maps[i].charset) { + maps[i].charset = (struct charset *)malloc(sizeof(struct charset)); + if (maps[i].charset) { + struct map *map = (struct map *)malloc(sizeof(struct map)); + if (!map) { + free(maps[i].charset); + maps[i].charset = 0; + } + else { + maps[i].charset->max = 1; + maps[i].charset->mbtowc = &mbtowc_8bit; + maps[i].charset->wctomb = &wctomb_8bit; + maps[i].charset->map = map; + map->from = maps[i].map; + map->to = 0; /* inverse mapping is created when required */ + } + } + } + return maps[i].charset; + } + + return 0; +} + +/* + * Function to convert a buffer from one encoding to another. + * Invalid bytes are replaced by '#', and characters that are + * not available in the target encoding are replaced by '?'. + * Each of TO and TOLEN may be zero, if the result is not needed. + * The output buffer is null-terminated, so it is all right to + * use charset_convert(fromcode, tocode, s, strlen(s), &t, 0). + */ + +int charset_convert(const char *fromcode, const char *tocode, + const char *from, size_t fromlen, + char **to, size_t *tolen) +{ + int ret = 0; + struct charset *charset1, *charset2; + char *tobuf, *p, *newbuf; + int i, j, wc; + + charset1 = charset_find(fromcode); + charset2 = charset_find(tocode); + if (!charset1 || !charset2 ) + return -1; + + tobuf = (char *)malloc(fromlen * charset2->max + 1); + if (!tobuf) + return -2; + + for (p = tobuf; fromlen; from += i, fromlen -= i, p += j) { + i = charset_mbtowc(charset1, &wc, from, fromlen); + if (!i) + i = 1; + else if (i == -1) { + i = 1; + wc = '#'; + ret = 2; + } + j = charset_wctomb(charset2, p, wc); + if (j == -1) { + if (!ret) + ret = 1; + j = charset_wctomb(charset2, p, '?'); + if (j == -1) + j = 0; + } + } + + if (tolen) + *tolen = p - tobuf; + *p++ = '\0'; + if (to) { + newbuf = realloc(tobuf, p - tobuf); + *to = newbuf ? newbuf : tobuf; + } + else + free(tobuf); + + return ret; +} + +#endif /* USE_CHARSET_ICONV */ diff --git a/src/FLAC/src/share/utf8/charset.h b/src/FLAC/src/share/utf8/charset.h new file mode 100644 index 00000000..b5e2fb73 --- /dev/null +++ b/src/FLAC/src/share/utf8/charset.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2001 Edmund Grimley Evans + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +/* + * These functions are like the C library's mbtowc() and wctomb(), + * but instead of depending on the locale they always work in UTF-8, + * and they use int instead of wchar_t. + */ + +int utf8_mbtowc(int *pwc, const char *s, size_t n); +int utf8_wctomb(char *s, int wc); + +/* + * This is an object-oriented version of mbtowc() and wctomb(). + * The caller first uses charset_find() to get a pointer to struct + * charset, then uses the mbtowc() and wctomb() methods on it. + * The function charset_max() gives the maximum length of a + * multibyte character in that encoding. + * This API is only appropriate for stateless encodings like UTF-8 + * or ISO-8859-3, but I have no intention of implementing anything + * other than UTF-8 and 8-bit encodings. + * + * MINOR BUG: If there is no memory charset_find() may return 0 and + * there is no way to distinguish this case from an unknown encoding. + */ + +struct charset; + +struct charset *charset_find(const char *code); + +int charset_mbtowc(struct charset *charset, int *pwc, const char *s, size_t n); +int charset_wctomb(struct charset *charset, char *s, int wc); +int charset_max(struct charset *charset); + +/* + * Function to convert a buffer from one encoding to another. + * Invalid bytes are replaced by '#', and characters that are + * not available in the target encoding are replaced by '?'. + * Each of TO and TOLEN may be zero if the result is not wanted. + * The input or output may contain null bytes, but the output + * buffer is also null-terminated, so it is all right to + * use charset_convert(fromcode, tocode, s, strlen(s), &t, 0). + * + * Return value: + * + * -2 : memory allocation failed + * -1 : unknown encoding + * 0 : data was converted exactly + * 1 : valid data was converted approximately (using '?') + * 2 : input was invalid (but still converted, using '#') + */ + +int charset_convert(const char *fromcode, const char *tocode, + const char *from, size_t fromlen, + char **to, size_t *tolen); diff --git a/src/FLAC/src/share/utf8/charset_test.c b/src/FLAC/src/share/utf8/charset_test.c new file mode 100644 index 00000000..1d5bf717 --- /dev/null +++ b/src/FLAC/src/share/utf8/charset_test.c @@ -0,0 +1,263 @@ +/* + * Copyright (C) 2001 Edmund Grimley Evans + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include + +#include "charset.h" + +void test_any(struct charset *charset) +{ + int wc; + char s[2]; + + assert(charset); + + /* Decoder */ + + assert(charset_mbtowc(charset, 0, 0, 0) == 0); + assert(charset_mbtowc(charset, 0, 0, 1) == 0); + assert(charset_mbtowc(charset, 0, (char *)(-1), 0) == 0); + + assert(charset_mbtowc(charset, 0, "a", 0) == 0); + assert(charset_mbtowc(charset, 0, "", 1) == 0); + assert(charset_mbtowc(charset, 0, "b", 1) == 1); + assert(charset_mbtowc(charset, 0, "", 2) == 0); + assert(charset_mbtowc(charset, 0, "c", 2) == 1); + + wc = 'x'; + assert(charset_mbtowc(charset, &wc, "a", 0) == 0 && wc == 'x'); + assert(charset_mbtowc(charset, &wc, "", 1) == 0 && wc == 0); + assert(charset_mbtowc(charset, &wc, "b", 1) == 1 && wc == 'b'); + assert(charset_mbtowc(charset, &wc, "", 2) == 0 && wc == 0); + assert(charset_mbtowc(charset, &wc, "c", 2) == 1 && wc == 'c'); + + /* Encoder */ + + assert(charset_wctomb(charset, 0, 0) == 0); + + s[0] = s[1] = '.'; + assert(charset_wctomb(charset, s, 0) == 1 && + s[0] == '\0' && s[1] == '.'); + assert(charset_wctomb(charset, s, 'x') == 1 && + s[0] == 'x' && s[1] == '.'); +} + +void test_utf8() +{ + struct charset *charset; + int wc; + char s[8]; + + charset = charset_find("UTF-8"); + test_any(charset); + + /* Decoder */ + wc = 0; + assert(charset_mbtowc(charset, &wc, "\177", 1) == 1 && wc == 127); + assert(charset_mbtowc(charset, &wc, "\200", 2) == -1); + assert(charset_mbtowc(charset, &wc, "\301\277", 9) == -1); + assert(charset_mbtowc(charset, &wc, "\302\200", 1) == -1); + assert(charset_mbtowc(charset, &wc, "\302\200", 2) == 2 && wc == 128); + assert(charset_mbtowc(charset, &wc, "\302\200", 3) == 2 && wc == 128); + assert(charset_mbtowc(charset, &wc, "\340\237\200", 9) == -1); + assert(charset_mbtowc(charset, &wc, "\340\240\200", 9) == 3 && + wc == 1 << 11); + assert(charset_mbtowc(charset, &wc, "\360\217\277\277", 9) == -1); + assert(charset_mbtowc(charset, &wc, "\360\220\200\200", 9) == 4 && + wc == 1 << 16); + assert(charset_mbtowc(charset, &wc, "\370\207\277\277\277", 9) == -1); + assert(charset_mbtowc(charset, &wc, "\370\210\200\200\200", 9) == 5 && + wc == 1 << 21); + assert(charset_mbtowc(charset, &wc, "\374\203\277\277\277\277", 9) == -1); + assert(charset_mbtowc(charset, &wc, "\374\204\200\200\200\200", 9) == 6 && + wc == 1 << 26); + assert(charset_mbtowc(charset, &wc, "\375\277\277\277\277\277", 9) == 6 && + wc == 0x7fffffff); + + assert(charset_mbtowc(charset, &wc, "\302\000", 2) == -1); + assert(charset_mbtowc(charset, &wc, "\302\300", 2) == -1); + assert(charset_mbtowc(charset, &wc, "\340\040\200", 9) == -1); + assert(charset_mbtowc(charset, &wc, "\340\340\200", 9) == -1); + assert(charset_mbtowc(charset, &wc, "\340\240\000", 9) == -1); + assert(charset_mbtowc(charset, &wc, "\340\240\300", 9) == -1); + assert(charset_mbtowc(charset, &wc, "\360\020\200\200", 9) == -1); + assert(charset_mbtowc(charset, &wc, "\360\320\200\200", 9) == -1); + assert(charset_mbtowc(charset, &wc, "\360\220\000\200", 9) == -1); + assert(charset_mbtowc(charset, &wc, "\360\220\300\200", 9) == -1); + assert(charset_mbtowc(charset, &wc, "\360\220\200\000", 9) == -1); + assert(charset_mbtowc(charset, &wc, "\360\220\200\300", 9) == -1); + assert(charset_mbtowc(charset, &wc, "\375\077\277\277\277\277", 9) == -1); + assert(charset_mbtowc(charset, &wc, "\375\377\277\277\277\277", 9) == -1); + assert(charset_mbtowc(charset, &wc, "\375\277\077\277\277\277", 9) == -1); + assert(charset_mbtowc(charset, &wc, "\375\277\377\277\277\277", 9) == -1); + assert(charset_mbtowc(charset, &wc, "\375\277\277\277\077\277", 9) == -1); + assert(charset_mbtowc(charset, &wc, "\375\277\277\277\377\277", 9) == -1); + assert(charset_mbtowc(charset, &wc, "\375\277\277\277\277\077", 9) == -1); + assert(charset_mbtowc(charset, &wc, "\375\277\277\277\277\377", 9) == -1); + + assert(charset_mbtowc(charset, &wc, "\376\277\277\277\277\277", 9) == -1); + assert(charset_mbtowc(charset, &wc, "\377\277\277\277\277\277", 9) == -1); + + /* Encoder */ + strcpy(s, "......."); + assert(charset_wctomb(charset, s, 1 << 31) == -1 && + !strcmp(s, ".......")); + assert(charset_wctomb(charset, s, 127) == 1 && + !strcmp(s, "\177......")); + assert(charset_wctomb(charset, s, 128) == 2 && + !strcmp(s, "\302\200.....")); + assert(charset_wctomb(charset, s, 0x7ff) == 2 && + !strcmp(s, "\337\277.....")); + assert(charset_wctomb(charset, s, 0x800) == 3 && + !strcmp(s, "\340\240\200....")); + assert(charset_wctomb(charset, s, 0xffff) == 3 && + !strcmp(s, "\357\277\277....")); + assert(charset_wctomb(charset, s, 0x10000) == 4 && + !strcmp(s, "\360\220\200\200...")); + assert(charset_wctomb(charset, s, 0x1fffff) == 4 && + !strcmp(s, "\367\277\277\277...")); + assert(charset_wctomb(charset, s, 0x200000) == 5 && + !strcmp(s, "\370\210\200\200\200..")); + assert(charset_wctomb(charset, s, 0x3ffffff) == 5 && + !strcmp(s, "\373\277\277\277\277..")); + assert(charset_wctomb(charset, s, 0x4000000) == 6 && + !strcmp(s, "\374\204\200\200\200\200.")); + assert(charset_wctomb(charset, s, 0x7fffffff) == 6 && + !strcmp(s, "\375\277\277\277\277\277.")); +} + +void test_ascii() +{ + struct charset *charset; + int wc; + char s[3]; + + charset = charset_find("us-ascii"); + test_any(charset); + + /* Decoder */ + wc = 0; + assert(charset_mbtowc(charset, &wc, "\177", 2) == 1 && wc == 127); + assert(charset_mbtowc(charset, &wc, "\200", 2) == -1); + + /* Encoder */ + strcpy(s, ".."); + assert(charset_wctomb(charset, s, 256) == -1 && !strcmp(s, "..")); + assert(charset_wctomb(charset, s, 255) == -1); + assert(charset_wctomb(charset, s, 128) == -1); + assert(charset_wctomb(charset, s, 127) == 1 && !strcmp(s, "\177.")); +} + +void test_iso1() +{ + struct charset *charset; + int wc; + char s[3]; + + charset = charset_find("iso-8859-1"); + test_any(charset); + + /* Decoder */ + wc = 0; + assert(charset_mbtowc(charset, &wc, "\302\200", 9) == 1 && wc == 0xc2); + + /* Encoder */ + strcpy(s, ".."); + assert(charset_wctomb(charset, s, 256) == -1 && !strcmp(s, "..")); + assert(charset_wctomb(charset, s, 255) == 1 && !strcmp(s, "\377.")); + assert(charset_wctomb(charset, s, 128) == 1 && !strcmp(s, "\200.")); +} + +void test_iso2() +{ + struct charset *charset; + int wc; + char s[3]; + + charset = charset_find("iso-8859-2"); + test_any(charset); + + /* Decoder */ + wc = 0; + assert(charset_mbtowc(charset, &wc, "\302\200", 9) == 1 && wc == 0xc2); + assert(charset_mbtowc(charset, &wc, "\377", 2) == 1 && wc == 0x2d9); + + /* Encoder */ + strcpy(s, ".."); + assert(charset_wctomb(charset, s, 256) == -1 && !strcmp(s, "..")); + assert(charset_wctomb(charset, s, 255) == -1 && !strcmp(s, "..")); + assert(charset_wctomb(charset, s, 258) == 1 && !strcmp(s, "\303.")); + assert(charset_wctomb(charset, s, 128) == 1 && !strcmp(s, "\200.")); +} + +void test_convert() +{ + const char *p; + char *q, *r; + char s[256]; + size_t n, n2; + int i; + + p = "\000x\302\200\375\277\277\277\277\277"; + assert(charset_convert("UTF-8", "UTF-8", p, 10, &q, &n) == 0 && + n == 10 && !strcmp(p, q)); + assert(charset_convert("UTF-8", "UTF-8", "x\301\277y", 4, &q, &n) == 2 && + n == 4 && !strcmp(q, "x##y")); + assert(charset_convert("UTF-8", "UTF-8", "x\301\277y", 4, 0, &n) == 2 && + n == 4); + assert(charset_convert("UTF-8", "UTF-8", "x\301\277y", 4, &q, 0) == 2 && + !strcmp(q, "x##y")); + assert(charset_convert("UTF-8", "iso-8859-1", + "\302\200\304\200x", 5, &q, &n) == 1 && + n == 3 && !strcmp(q, "\200?x")); + assert(charset_convert("iso-8859-1", "UTF-8", + "\000\200\377", 3, &q, &n) == 0 && + n == 5 && !memcmp(q, "\000\302\200\303\277", 5)); + assert(charset_convert("iso-8859-1", "iso-8859-1", + "\000\200\377", 3, &q, &n) == 0 && + n == 3 && !memcmp(q, "\000\200\377", 3)); + + assert(charset_convert("iso-8859-2", "utf-8", "\300", 1, &q, &n) == 0 && + n == 2 && !strcmp(q, "\305\224")); + assert(charset_convert("utf-8", "iso-8859-2", "\305\224", 2, &q, &n) == 0 && + n == 1 && !strcmp(q, "\300")); + + for (i = 0; i < 256; i++) + s[i] = i; + + assert(charset_convert("iso-8859-2", "utf-8", s, 256, &q, &n) == 0); + assert(charset_convert("utf-8", "iso-8859-2", q, n, &r, &n2) == 0); + assert(n2 == 256 && !memcmp(r, s, n2)); +} + +int main() +{ + test_utf8(); + test_ascii(); + test_iso1(); + test_iso2(); + + test_convert(); + + return 0; +} diff --git a/src/FLAC/src/share/utf8/charsetmap.h b/src/FLAC/src/share/utf8/charsetmap.h new file mode 100644 index 00000000..b8125905 --- /dev/null +++ b/src/FLAC/src/share/utf8/charsetmap.h @@ -0,0 +1,79 @@ +/* This file was automatically generated by make_code_map.pl + please don't edit directly + Daniel Resare +*/ +charset_map maps[] = { + {"ISO-8859-1", + { + 0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007, + 0x0008,0x0009,0x000A,0x000B,0x000C,0x000D,0x000E,0x000F, + 0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017, + 0x0018,0x0019,0x001A,0x001B,0x001C,0x001D,0x001E,0x001F, + 0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027, + 0x0028,0x0029,0x002A,0x002B,0x002C,0x002D,0x002E,0x002F, + 0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037, + 0x0038,0x0039,0x003A,0x003B,0x003C,0x003D,0x003E,0x003F, + 0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047, + 0x0048,0x0049,0x004A,0x004B,0x004C,0x004D,0x004E,0x004F, + 0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057, + 0x0058,0x0059,0x005A,0x005B,0x005C,0x005D,0x005E,0x005F, + 0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067, + 0x0068,0x0069,0x006A,0x006B,0x006C,0x006D,0x006E,0x006F, + 0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077, + 0x0078,0x0079,0x007A,0x007B,0x007C,0x007D,0x007E,0x007F, + 0x0080,0x0081,0x0082,0x0083,0x0084,0x0085,0x0086,0x0087, + 0x0088,0x0089,0x008A,0x008B,0x008C,0x008D,0x008E,0x008F, + 0x0090,0x0091,0x0092,0x0093,0x0094,0x0095,0x0096,0x0097, + 0x0098,0x0099,0x009A,0x009B,0x009C,0x009D,0x009E,0x009F, + 0x00A0,0x00A1,0x00A2,0x00A3,0x00A4,0x00A5,0x00A6,0x00A7, + 0x00A8,0x00A9,0x00AA,0x00AB,0x00AC,0x00AD,0x00AE,0x00AF, + 0x00B0,0x00B1,0x00B2,0x00B3,0x00B4,0x00B5,0x00B6,0x00B7, + 0x00B8,0x00B9,0x00BA,0x00BB,0x00BC,0x00BD,0x00BE,0x00BF, + 0x00C0,0x00C1,0x00C2,0x00C3,0x00C4,0x00C5,0x00C6,0x00C7, + 0x00C8,0x00C9,0x00CA,0x00CB,0x00CC,0x00CD,0x00CE,0x00CF, + 0x00D0,0x00D1,0x00D2,0x00D3,0x00D4,0x00D5,0x00D6,0x00D7, + 0x00D8,0x00D9,0x00DA,0x00DB,0x00DC,0x00DD,0x00DE,0x00DF, + 0x00E0,0x00E1,0x00E2,0x00E3,0x00E4,0x00E5,0x00E6,0x00E7, + 0x00E8,0x00E9,0x00EA,0x00EB,0x00EC,0x00ED,0x00EE,0x00EF, + 0x00F0,0x00F1,0x00F2,0x00F3,0x00F4,0x00F5,0x00F6,0x00F7, + 0x00F8,0x00F9,0x00FA,0x00FB,0x00FC,0x00FD,0x00FE,0x00FF + } + }, + {"ISO-8859-2", + { + 0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007, + 0x0008,0x0009,0x000A,0x000B,0x000C,0x000D,0x000E,0x000F, + 0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017, + 0x0018,0x0019,0x001A,0x001B,0x001C,0x001D,0x001E,0x001F, + 0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027, + 0x0028,0x0029,0x002A,0x002B,0x002C,0x002D,0x002E,0x002F, + 0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037, + 0x0038,0x0039,0x003A,0x003B,0x003C,0x003D,0x003E,0x003F, + 0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047, + 0x0048,0x0049,0x004A,0x004B,0x004C,0x004D,0x004E,0x004F, + 0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057, + 0x0058,0x0059,0x005A,0x005B,0x005C,0x005D,0x005E,0x005F, + 0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067, + 0x0068,0x0069,0x006A,0x006B,0x006C,0x006D,0x006E,0x006F, + 0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077, + 0x0078,0x0079,0x007A,0x007B,0x007C,0x007D,0x007E,0x007F, + 0x0080,0x0081,0x0082,0x0083,0x0084,0x0085,0x0086,0x0087, + 0x0088,0x0089,0x008A,0x008B,0x008C,0x008D,0x008E,0x008F, + 0x0090,0x0091,0x0092,0x0093,0x0094,0x0095,0x0096,0x0097, + 0x0098,0x0099,0x009A,0x009B,0x009C,0x009D,0x009E,0x009F, + 0x00A0,0x0104,0x02D8,0x0141,0x00A4,0x013D,0x015A,0x00A7, + 0x00A8,0x0160,0x015E,0x0164,0x0179,0x00AD,0x017D,0x017B, + 0x00B0,0x0105,0x02DB,0x0142,0x00B4,0x013E,0x015B,0x02C7, + 0x00B8,0x0161,0x015F,0x0165,0x017A,0x02DD,0x017E,0x017C, + 0x0154,0x00C1,0x00C2,0x0102,0x00C4,0x0139,0x0106,0x00C7, + 0x010C,0x00C9,0x0118,0x00CB,0x011A,0x00CD,0x00CE,0x010E, + 0x0110,0x0143,0x0147,0x00D3,0x00D4,0x0150,0x00D6,0x00D7, + 0x0158,0x016E,0x00DA,0x0170,0x00DC,0x00DD,0x0162,0x00DF, + 0x0155,0x00E1,0x00E2,0x0103,0x00E4,0x013A,0x0107,0x00E7, + 0x010D,0x00E9,0x0119,0x00EB,0x011B,0x00ED,0x00EE,0x010F, + 0x0111,0x0144,0x0148,0x00F3,0x00F4,0x0151,0x00F6,0x00F7, + 0x0159,0x016F,0x00FA,0x0171,0x00FC,0x00FD,0x0163,0x02D9 + } + }, + {NULL} +}; diff --git a/src/FLAC/src/share/utf8/iconvert.c b/src/FLAC/src/share/utf8/iconvert.c new file mode 100644 index 00000000..b72ad863 --- /dev/null +++ b/src/FLAC/src/share/utf8/iconvert.c @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2001 Edmund Grimley Evans + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if HAVE_CONFIG_H +# include +#endif + +#ifdef HAVE_ICONV + +#include +#include +#include +#include +#include + +/* + * Convert data from one encoding to another. Return: + * + * -2 : memory allocation failed + * -1 : unknown encoding + * 0 : data was converted exactly + * 1 : data was converted inexactly + * 2 : data was invalid (but still converted) + * + * We convert in two steps, via UTF-8, as this is the only + * reliable way of distinguishing between invalid input + * and valid input which iconv refuses to transliterate. + * We convert from UTF-8 twice, because we have no way of + * knowing whether the conversion was exact if iconv returns + * E2BIG (due to a bug in the specification of iconv). + * An alternative approach is to assume that the output of + * iconv is never more than 4 times as long as the input, + * but I prefer to avoid that assumption if possible. + */ + +int iconvert(const char *fromcode, const char *tocode, + const char *from, size_t fromlen, + char **to, size_t *tolen) +{ + int ret = 0; + iconv_t cd1, cd2; + char *ib; + char *ob; + char *utfbuf = 0, *outbuf, *newbuf; + size_t utflen, outlen, ibl, obl, k; + char tbuf[2048]; + + cd1 = iconv_open("UTF-8", fromcode); + if (cd1 == (iconv_t)(-1)) + return -1; + + cd2 = (iconv_t)(-1); + /* Don't use strcasecmp() as it's locale-dependent. */ + if (!strchr("Uu", tocode[0]) || + !strchr("Tt", tocode[1]) || + !strchr("Ff", tocode[2]) || + tocode[3] != '-' || + tocode[4] != '8' || + tocode[5] != '\0') { + char *tocode1; + + /* + * Try using this non-standard feature of glibc and libiconv. + * This is deliberately not a config option as people often + * change their iconv library without rebuilding applications. + */ + tocode1 = (char *)malloc(strlen(tocode) + 11); + if (!tocode1) + goto fail; + + strcpy(tocode1, tocode); + strcat(tocode1, "//TRANSLIT"); + cd2 = iconv_open(tocode1, "UTF-8"); + free(tocode1); + + if (cd2 == (iconv_t)(-1)) + cd2 = iconv_open(tocode, fromcode); + + if (cd2 == (iconv_t)(-1)) { + iconv_close(cd1); + return -1; + } + } + + utflen = 1; /*fromlen * 2 + 1; XXX */ + utfbuf = (char *)malloc(utflen); + if (!utfbuf) + goto fail; + + /* Convert to UTF-8 */ + ib = (char *)from; + ibl = fromlen; + ob = utfbuf; + obl = utflen; + for (;;) { + k = iconv(cd1, &ib, &ibl, &ob, &obl); + assert((!k && !ibl) || + (k == (size_t)(-1) && errno == E2BIG && ibl && obl < 6) || + (k == (size_t)(-1) && + (errno == EILSEQ || errno == EINVAL) && ibl)); + if (!ibl) + break; + if (obl < 6) { + /* Enlarge the buffer */ + utflen *= 2; + newbuf = (char *)realloc(utfbuf, utflen); + if (!newbuf) + goto fail; + ob = (ob - utfbuf) + newbuf; + obl = utflen - (ob - newbuf); + utfbuf = newbuf; + } + else { + /* Invalid input */ + ib++, ibl--; + *ob++ = '#', obl--; + ret = 2; + iconv(cd1, 0, 0, 0, 0); + } + } + + if (cd2 == (iconv_t)(-1)) { + /* The target encoding was UTF-8 */ + if (tolen) + *tolen = ob - utfbuf; + if (!to) { + free(utfbuf); + iconv_close(cd1); + return ret; + } + newbuf = (char *)realloc(utfbuf, (ob - utfbuf) + 1); + if (!newbuf) + goto fail; + ob = (ob - utfbuf) + newbuf; + *ob = '\0'; + *to = newbuf; + iconv_close(cd1); + return ret; + } + + /* Truncate the buffer to be tidy */ + utflen = ob - utfbuf; + newbuf = (char *)realloc(utfbuf, utflen); + if (!newbuf) + goto fail; + utfbuf = newbuf; + + /* Convert from UTF-8 to discover how long the output is */ + outlen = 0; + ib = utfbuf; + ibl = utflen; + while (ibl) { + ob = tbuf; + obl = sizeof(tbuf); + k = iconv(cd2, &ib, &ibl, &ob, &obl); + assert((k != (size_t)(-1) && !ibl) || + (k == (size_t)(-1) && errno == E2BIG && ibl) || + (k == (size_t)(-1) && errno == EILSEQ && ibl)); + if (ibl && !(k == (size_t)(-1) && errno == E2BIG)) { + /* Replace one character */ + char *tb = "?"; + size_t tbl = 1; + + outlen += ob - tbuf; + ob = tbuf; + obl = sizeof(tbuf); + k = iconv(cd2, &tb, &tbl, &ob, &obl); + assert((!k && !tbl) || + (k == (size_t)(-1) && errno == EILSEQ && tbl)); + for (++ib, --ibl; ibl && (*ib & 0x80); ib++, ibl--) + ; + } + outlen += ob - tbuf; + } + ob = tbuf; + obl = sizeof(tbuf); + k = iconv(cd2, 0, 0, &ob, &obl); + assert(!k); + outlen += ob - tbuf; + + /* Convert from UTF-8 for real */ + outbuf = (char *)malloc(outlen + 1); + if (!outbuf) + goto fail; + ib = utfbuf; + ibl = utflen; + ob = outbuf; + obl = outlen; + while (ibl) { + k = iconv(cd2, &ib, &ibl, &ob, &obl); + assert((k != (size_t)(-1) && !ibl) || + (k == (size_t)(-1) && errno == EILSEQ && ibl)); + if (k && !ret) + ret = 1; + if (ibl && !(k == (size_t)(-1) && errno == E2BIG)) { + /* Replace one character */ + char *tb = "?"; + size_t tbl = 1; + + k = iconv(cd2, &tb, &tbl, &ob, &obl); + assert((!k && !tbl) || + (k == (size_t)(-1) && errno == EILSEQ && tbl)); + for (++ib, --ibl; ibl && (*ib & 0x80); ib++, ibl--) + ; + } + } + k = iconv(cd2, 0, 0, &ob, &obl); + assert(!k); + assert(!obl); + *ob = '\0'; + + free(utfbuf); + iconv_close(cd1); + iconv_close(cd2); + if (tolen) + *tolen = outlen; + if (!to) { + free(outbuf); + return ret; + } + *to = outbuf; + return ret; + + fail: + if(0 != utfbuf) + free(utfbuf); + iconv_close(cd1); + if (cd2 != (iconv_t)(-1)) + iconv_close(cd2); + return -2; +} + +#endif /* HAVE_ICONV */ diff --git a/src/FLAC/src/share/utf8/makemap.c b/src/FLAC/src/share/utf8/makemap.c new file mode 100644 index 00000000..72a9de18 --- /dev/null +++ b/src/FLAC/src/share/utf8/makemap.c @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2001 Edmund Grimley Evans + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include +#include + +int main(int argc, char *argv[]) +{ + iconv_t cd; + const char *ib; + char *ob; + size_t ibl, obl, k; + unsigned char c, buf[4]; + int i, wc; + + if (argc != 2) { + printf("Usage: %s ENCODING\n", argv[0]); + printf("Output a charset map for the 8-bit ENCODING.\n"); + return 1; + } + + cd = iconv_open("UCS-4", argv[1]); + if (cd == (iconv_t)(-1)) { + perror("iconv_open"); + return 1; + } + + for (i = 0; i < 256; i++) { + c = i; + ib = &c; + ibl = 1; + ob = buf; + obl = 4; + k = iconv(cd, &ib, &ibl, &ob, &obl); + if (!k && !ibl && !obl) { + wc = (buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3]; + if (wc >= 0xffff) { + printf("Dodgy value.\n"); + return 1; + } + } + else if (k == (size_t)(-1) && errno == EILSEQ) + wc = 0xffff; + else { + printf("Non-standard iconv.\n"); + return 1; + } + + if (i % 8 == 0) + printf(" "); + printf("0x%04x", wc); + if (i == 255) + printf("\n"); + else if (i % 8 == 7) + printf(",\n"); + else + printf(", "); + } + + return 0; +} diff --git a/src/FLAC/src/share/utf8/utf8.c b/src/FLAC/src/share/utf8/utf8.c new file mode 100644 index 00000000..fc2c69bc --- /dev/null +++ b/src/FLAC/src/share/utf8/utf8.c @@ -0,0 +1,303 @@ +/* + * Copyright (C) 2001 Peter Harris + * Copyright (C) 2001 Edmund Grimley Evans + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Convert a string between UTF-8 and the locale's charset. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include + +#include "utf8.h" +#include "charset.h" + + +#ifdef _WIN32 + + /* Thanks to Peter Harris for this win32 + * code. + */ + +#include +#include + +static unsigned char *make_utf8_string(const wchar_t *unicode) +{ + int size = 0, index = 0, out_index = 0; + unsigned char *out; + unsigned short c; + + /* first calculate the size of the target string */ + c = unicode[index++]; + while(c) { + if(c < 0x0080) { + size += 1; + } else if(c < 0x0800) { + size += 2; + } else { + size += 3; + } + c = unicode[index++]; + } + + out = malloc(size + 1); + if (out == NULL) + return NULL; + index = 0; + + c = unicode[index++]; + while(c) + { + if(c < 0x080) { + out[out_index++] = (unsigned char)c; + } else if(c < 0x800) { + out[out_index++] = 0xc0 | (c >> 6); + out[out_index++] = 0x80 | (c & 0x3f); + } else { + out[out_index++] = 0xe0 | (c >> 12); + out[out_index++] = 0x80 | ((c >> 6) & 0x3f); + out[out_index++] = 0x80 | (c & 0x3f); + } + c = unicode[index++]; + } + out[out_index] = 0x00; + + return out; +} + +static wchar_t *make_unicode_string(const unsigned char *utf8) +{ + int size = 0, index = 0, out_index = 0; + wchar_t *out; + unsigned char c; + + /* first calculate the size of the target string */ + c = utf8[index++]; + while(c) { + if((c & 0x80) == 0) { + index += 0; + } else if((c & 0xe0) == 0xe0) { + index += 2; + } else { + index += 1; + } + size += 1; + c = utf8[index++]; + } + + out = malloc((size + 1) * sizeof(wchar_t)); + if (out == NULL) + return NULL; + index = 0; + + c = utf8[index++]; + while(c) + { + if((c & 0x80) == 0) { + out[out_index++] = c; + } else if((c & 0xe0) == 0xe0) { + out[out_index] = (c & 0x1F) << 12; + c = utf8[index++]; + out[out_index] |= (c & 0x3F) << 6; + c = utf8[index++]; + out[out_index++] |= (c & 0x3F); + } else { + out[out_index] = (c & 0x3F) << 6; + c = utf8[index++]; + out[out_index++] |= (c & 0x3F); + } + c = utf8[index++]; + } + out[out_index] = 0; + + return out; +} + +int utf8_encode(const char *from, char **to) +{ + wchar_t *unicode; + int wchars, err; + + wchars = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, from, + strlen(from), NULL, 0); + + if(wchars == 0) + { + fprintf(stderr, "Unicode translation error %d\n", GetLastError()); + return -1; + } + + unicode = calloc(wchars + 1, sizeof(unsigned short)); + if(unicode == NULL) + { + fprintf(stderr, "Out of memory processing string to UTF8\n"); + return -1; + } + + err = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, from, + strlen(from), unicode, wchars); + if(err != wchars) + { + free(unicode); + fprintf(stderr, "Unicode translation error %d\n", GetLastError()); + return -1; + } + + /* On NT-based windows systems, we could use WideCharToMultiByte(), but + * MS doesn't actually have a consistent API across win32. + */ + *to = make_utf8_string(unicode); + + free(unicode); + return 0; +} + +int utf8_decode(const char *from, char **to) +{ + wchar_t *unicode; + int chars, err; + + /* On NT-based windows systems, we could use MultiByteToWideChar(CP_UTF8), but + * MS doesn't actually have a consistent API across win32. + */ + unicode = make_unicode_string(from); + if(unicode == NULL) + { + fprintf(stderr, "Out of memory processing string from UTF8 to UNICODE16\n"); + return -1; + } + + chars = WideCharToMultiByte(GetConsoleCP(), WC_COMPOSITECHECK, unicode, + -1, NULL, 0, NULL, NULL); + + if(chars == 0) + { + fprintf(stderr, "Unicode translation error %d\n", GetLastError()); + free(unicode); + return -1; + } + + *to = calloc(chars + 1, sizeof(unsigned char)); + if(*to == NULL) + { + fprintf(stderr, "Out of memory processing string to local charset\n"); + free(unicode); + return -1; + } + + err = WideCharToMultiByte(GetConsoleCP(), WC_COMPOSITECHECK, unicode, + -1, *to, chars, NULL, NULL); + if(err != chars) + { + fprintf(stderr, "Unicode translation error %d\n", GetLastError()); + free(unicode); + free(*to); + *to = NULL; + return -1; + } + + free(unicode); + return 0; +} + +#else /* End win32. Rest is for real operating systems */ + + +#ifdef HAVE_LANGINFO_CODESET +#include +#endif + +int iconvert(const char *fromcode, const char *tocode, + const char *from, size_t fromlen, + char **to, size_t *tolen); + +static const char *current_charset(void) +{ + const char *c = 0; +#ifdef HAVE_LANGINFO_CODESET + c = nl_langinfo(CODESET); +#endif + + if (!c) + c = getenv("CHARSET"); + + return c? c : "US-ASCII"; +} + +static int convert_buffer(const char *fromcode, const char *tocode, + const char *from, size_t fromlen, + char **to, size_t *tolen) +{ + int ret = -1; + +#ifdef HAVE_ICONV + ret = iconvert(fromcode, tocode, from, fromlen, to, tolen); + if (ret != -1) + return ret; +#endif + +#ifndef HAVE_ICONV /* should be ifdef USE_CHARSET_CONVERT */ + ret = charset_convert(fromcode, tocode, from, fromlen, to, tolen); + if (ret != -1) + return ret; +#endif + + return ret; +} + +static int convert_string(const char *fromcode, const char *tocode, + const char *from, char **to, char replace) +{ + int ret; + size_t fromlen; + char *s; + + fromlen = strlen(from); + ret = convert_buffer(fromcode, tocode, from, fromlen, to, 0); + if (ret == -2) + return -1; + if (ret != -1) + return ret; + + s = malloc(fromlen + 1); + if (!s) + return -1; + strcpy(s, from); + *to = s; + for (; *s; s++) + if (*s & ~0x7f) + *s = replace; + return 3; +} + +int utf8_encode(const char *from, char **to) +{ + return convert_string(current_charset(), "UTF-8", from, to, '#'); +} + +int utf8_decode(const char *from, char **to) +{ + return convert_string("UTF-8", current_charset(), from, to, '?'); +} + +#endif diff --git a/src/FLAC/src/test_grabbag/Makefile.am b/src/FLAC/src/test_grabbag/Makefile.am new file mode 100644 index 00000000..6e614aab --- /dev/null +++ b/src/FLAC/src/test_grabbag/Makefile.am @@ -0,0 +1,19 @@ +# FLAC - Free Lossless Audio Codec +# Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson +# +# This file is part the FLAC project. FLAC is comprised of several +# components distributed under difference licenses. The codec libraries +# are distributed under Xiph.Org's BSD-like license (see the file +# COPYING.Xiph in this distribution). All other programs, libraries, and +# plugins are distributed under the GPL (see COPYING.GPL). The documentation +# is distributed under the Gnu FDL (see COPYING.FDL). Each file in the +# FLAC distribution contains at the top the terms under which it may be +# distributed. +# +# Since this particular file is relevant to all components of FLAC, +# it may be distributed under the Xiph.Org license, which is the least +# restrictive of those mentioned above. See the file COPYING.Xiph in this +# distribution. + +SUBDIRS = cuesheet picture + diff --git a/src/FLAC/src/test_grabbag/cuesheet/Makefile.am b/src/FLAC/src/test_grabbag/cuesheet/Makefile.am new file mode 100644 index 00000000..e4ea0fd8 --- /dev/null +++ b/src/FLAC/src/test_grabbag/cuesheet/Makefile.am @@ -0,0 +1,30 @@ +# test_cuesheet - Simple tester for cuesheet routines in grabbag +# Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +INCLUDES = -I$(top_srcdir)/src/FLAC/include + +noinst_PROGRAMS = test_cuesheet +test_cuesheet_SOURCES = \ + main.c +test_cuesheet_LDADD = \ + $(top_builddir)/src/FLAC/src/share/grabbag/libgrabbag.la \ + $(top_builddir)/src/FLAC/src/share/replaygain_anal/libreplaygain_analysis.la \ + $(top_builddir)/src/FLAC/src/libFLAC/libFLAC.la \ + $(top_builddir)/src/OGG/libogg.la \ + -lm + +CLEANFILES = $(wildcard *.raw) diff --git a/src/FLAC/src/test_grabbag/cuesheet/main.c b/src/FLAC/src/test_grabbag/cuesheet/main.c new file mode 100644 index 00000000..bb99834c --- /dev/null +++ b/src/FLAC/src/test_grabbag/cuesheet/main.c @@ -0,0 +1,138 @@ +/* test_cuesheet - Simple tester for cuesheet routines in grabbag + * Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include "FLAC/assert.h" +#include "FLAC/metadata.h" +#include "share/grabbag.h" + +static int do_cuesheet(const char *infilename, FLAC__bool is_cdda, FLAC__uint64 lead_out_offset) +{ + FILE *fin, *fout; + const char *error_message; + char tmpfilename[4096]; + unsigned last_line_read; + FLAC__StreamMetadata *cuesheet; + + FLAC__ASSERT(strlen(infilename) + 2 < sizeof(tmpfilename)); + + /* + * pass 1 + */ + if(0 == strcmp(infilename, "-")) { + fin = stdin; + } + else if(0 == (fin = fopen(infilename, "r"))) { + fprintf(stderr, "can't open file %s for reading: %s\n", infilename, strerror(errno)); + return 255; + } + if(0 != (cuesheet = grabbag__cuesheet_parse(fin, &error_message, &last_line_read, is_cdda, lead_out_offset))) { + if(fin != stdin) + fclose(fin); + } + else { + printf("pass1: parse error, line %u: \"%s\"\n", last_line_read, error_message); + if(fin != stdin) + fclose(fin); + return 1; + } + if(!FLAC__metadata_object_cuesheet_is_legal(cuesheet, is_cdda, &error_message)) { + printf("pass1: illegal cuesheet: \"%s\"\n", error_message); + FLAC__metadata_object_delete(cuesheet); + return 1; + } + sprintf(tmpfilename, "%s.1", infilename); + if(0 == (fout = fopen(tmpfilename, "w"))) { + fprintf(stderr, "can't open file %s for writing: %s\n", tmpfilename, strerror(errno)); + FLAC__metadata_object_delete(cuesheet); + return 255; + } + grabbag__cuesheet_emit(fout, cuesheet, "\"dummy.wav\" WAVE"); + FLAC__metadata_object_delete(cuesheet); + fclose(fout); + + /* + * pass 2 + */ + if(0 == (fin = fopen(tmpfilename, "r"))) { + fprintf(stderr, "can't open file %s for reading: %s\n", tmpfilename, strerror(errno)); + return 255; + } + if(0 != (cuesheet = grabbag__cuesheet_parse(fin, &error_message, &last_line_read, is_cdda, lead_out_offset))) { + if(fin != stdin) + fclose(fin); + } + else { + printf("pass2: parse error, line %u: \"%s\"\n", last_line_read, error_message); + if(fin != stdin) + fclose(fin); + return 1; + } + if(!FLAC__metadata_object_cuesheet_is_legal(cuesheet, is_cdda, &error_message)) { + printf("pass2: illegal cuesheet: \"%s\"\n", error_message); + FLAC__metadata_object_delete(cuesheet); + return 1; + } + sprintf(tmpfilename, "%s.2", infilename); + if(0 == (fout = fopen(tmpfilename, "w"))) { + fprintf(stderr, "can't open file %s for writing: %s\n", tmpfilename, strerror(errno)); + FLAC__metadata_object_delete(cuesheet); + return 255; + } + grabbag__cuesheet_emit(fout, cuesheet, "\"dummy.wav\" WAVE"); + FLAC__metadata_object_delete(cuesheet); + fclose(fout); + + return 0; +} + +int main(int argc, char *argv[]) +{ + FLAC__uint64 lead_out_offset; + FLAC__bool is_cdda = false; + const char *usage = "usage: test_cuesheet cuesheet_file lead_out_offset [ cdda ]\n"; + + if(argc > 1 && 0 == strcmp(argv[1], "-h")) { + printf(usage); + return 0; + } + + if(argc < 3 || argc > 4) { + fprintf(stderr, usage); + return 255; + } + + lead_out_offset = (FLAC__uint64)strtoul(argv[2], 0, 10); + if(argc == 4) { + if(0 == strcmp(argv[3], "cdda")) + is_cdda = true; + else { + fprintf(stderr, usage); + return 255; + } + } + + return do_cuesheet(argv[1], is_cdda, lead_out_offset); +} diff --git a/src/FLAC/src/test_grabbag/picture/Makefile.am b/src/FLAC/src/test_grabbag/picture/Makefile.am new file mode 100644 index 00000000..9abcb47c --- /dev/null +++ b/src/FLAC/src/test_grabbag/picture/Makefile.am @@ -0,0 +1,29 @@ +# test_picture - Simple tester for picture routines in grabbag +# Copyright (C) 2006,2007 Josh Coalson +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +INCLUDES = -I$(top_srcdir)/src/FLAC/include + +noinst_PROGRAMS = test_picture +test_picture_SOURCES = \ + main.c +test_picture_LDADD = \ + $(top_builddir)/src/FLAC/src/share/grabbag/libgrabbag.la \ + $(top_builddir)/src/FLAC/src/libFLAC/libFLAC.la \ + $(top_builddir)/src/OGG/libogg.la \ + -lm + +CLEANFILES = $(wildcard *.raw) diff --git a/src/FLAC/src/test_grabbag/picture/main.c b/src/FLAC/src/test_grabbag/picture/main.c new file mode 100644 index 00000000..2ca34a65 --- /dev/null +++ b/src/FLAC/src/test_grabbag/picture/main.c @@ -0,0 +1,224 @@ +/* test_picture - Simple tester for picture routines in grabbag + * Copyright (C) 2006,2007 Josh Coalson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include "FLAC/assert.h" +#include "share/grabbag.h" + +typedef struct { + const char *path; + const char *mime_type; + const char *description; + FLAC__uint32 width; + FLAC__uint32 height; + FLAC__uint32 depth; + FLAC__uint32 colors; + FLAC__StreamMetadata_Picture_Type type; +} PictureFile; + +PictureFile picturefiles[] = { + { "0.gif", "image/gif" , "", 24, 24, 24, 2, FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER }, + { "1.gif", "image/gif" , "", 12, 8, 24, 256, FLAC__STREAM_METADATA_PICTURE_TYPE_BACK_COVER }, + { "2.gif", "image/gif" , "", 16, 14, 24, 128, FLAC__STREAM_METADATA_PICTURE_TYPE_OTHER }, + { "0.jpg", "image/jpeg", "", 30, 20, 8, 0, FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER }, + { "4.jpg", "image/jpeg", "", 31, 47, 24, 0, FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER }, + { "0.png", "image/png" , "", 30, 20, 8, 0, FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER }, + { "1.png", "image/png" , "", 30, 20, 8, 0, FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER }, + { "2.png", "image/png" , "", 30, 20, 24, 7, FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER }, + { "3.png", "image/png" , "", 30, 20, 24, 7, FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER }, + { "4.png", "image/png" , "", 31, 47, 24, 0, FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER }, + { "5.png", "image/png" , "", 31, 47, 24, 0, FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER }, + { "6.png", "image/png" , "", 31, 47, 24, 23, FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER }, + { "7.png", "image/png" , "", 31, 47, 24, 23, FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER }, + { "8.png", "image/png" , "", 32, 32, 32, 0, 999 } +}; + +static FLAC__bool debug_ = false; + +static FLAC__bool failed_(const char *msg) +{ + if(msg) + printf("FAILED, %s\n", msg); + else + printf("FAILED\n"); + + return false; +} + +static FLAC__bool test_one_picture(const char *prefix, const PictureFile *pf, const char *res, FLAC__bool fn_only) +{ + FLAC__StreamMetadata *obj; + const char *error; + char s[4096]; + if(fn_only) +#if defined _MSC_VER || defined __MINGW32__ + _snprintf(s, sizeof(s)-1, "%s/%s", prefix, pf->path); +#else + snprintf(s, sizeof(s)-1, "%s/%s", prefix, pf->path); +#endif + else +#if defined _MSC_VER || defined __MINGW32__ + _snprintf(s, sizeof(s)-1, "%u|%s|%s|%s|%s/%s", (unsigned)pf->type, pf->mime_type, pf->description, res, prefix, pf->path); +#else + snprintf(s, sizeof(s)-1, "%u|%s|%s|%s|%s/%s", (unsigned)pf->type, pf->mime_type, pf->description, res, prefix, pf->path); +#endif + + printf("testing grabbag__picture_parse_specification(\"%s\")... ", s); + if(0 == (obj = grabbag__picture_parse_specification(s, &error))) + return failed_(error); + if(debug_) { + printf("\ntype=%u (%s)\nmime_type=%s\ndescription=%s\nwidth=%u\nheight=%u\ndepth=%u\ncolors=%u\ndata_length=%u\n", + obj->data.picture.type, + obj->data.picture.type < FLAC__STREAM_METADATA_PICTURE_TYPE_UNDEFINED? + FLAC__StreamMetadata_Picture_TypeString[obj->data.picture.type] : "UNDEFINED", + obj->data.picture.mime_type, + obj->data.picture.description, + obj->data.picture.width, + obj->data.picture.height, + obj->data.picture.depth, + obj->data.picture.colors, + obj->data.picture.data_length + ); + } + if(obj->data.picture.type != (fn_only? FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER : pf->type)) + return failed_("picture type mismatch"); + if(strcmp(obj->data.picture.mime_type, pf->mime_type)) + return failed_("picture MIME type mismatch"); + if(strcmp((const char *)obj->data.picture.description, (const char *)pf->description)) + return failed_("picture description mismatch"); + if(obj->data.picture.width != pf->width) + return failed_("picture width mismatch"); + if(obj->data.picture.height != pf->height) + return failed_("picture height mismatch"); + if(obj->data.picture.depth != pf->depth) + return failed_("picture depth mismatch"); + if(obj->data.picture.colors != pf->colors) + return failed_("picture colors mismatch"); + printf("OK\n"); + FLAC__metadata_object_delete(obj); + return true; +} + +static FLAC__bool do_picture(const char *prefix) +{ + FLAC__StreamMetadata *obj; + const char *error; + size_t i; + + printf("\n+++ grabbag unit test: picture\n\n"); + + /* invalid spec: no filename */ + printf("testing grabbag__picture_parse_specification(\"\")... "); + if(0 != (obj = grabbag__picture_parse_specification("", &error))) + return failed_("expected error, got object"); + printf("OK (failed as expected, error: %s)\n", error); + + /* invalid spec: no filename */ + printf("testing grabbag__picture_parse_specification(\"||||\")... "); + if(0 != (obj = grabbag__picture_parse_specification("||||", &error))) + return failed_("expected error, got object"); + printf("OK (failed as expected: %s)\n", error); + + /* invalid spec: no filename */ + printf("testing grabbag__picture_parse_specification(\"|image/gif|||\")... "); + if(0 != (obj = grabbag__picture_parse_specification("|image/gif|||", &error))) + return failed_("expected error, got object"); + printf("OK (failed as expected: %s)\n", error); + + /* invalid spec: bad resolution */ + printf("testing grabbag__picture_parse_specification(\"|image/gif|desc|320|0.gif\")... "); + if(0 != (obj = grabbag__picture_parse_specification("|image/gif|desc|320|0.gif", &error))) + return failed_("expected error, got object"); + printf("OK (failed as expected: %s)\n", error); + + /* invalid spec: bad resolution */ + printf("testing grabbag__picture_parse_specification(\"|image/gif|desc|320x240|0.gif\")... "); + if(0 != (obj = grabbag__picture_parse_specification("|image/gif|desc|320x240|0.gif", &error))) + return failed_("expected error, got object"); + printf("OK (failed as expected: %s)\n", error); + + /* invalid spec: no filename */ + printf("testing grabbag__picture_parse_specification(\"|image/gif|desc|320x240x9|\")... "); + if(0 != (obj = grabbag__picture_parse_specification("|image/gif|desc|320x240x9|", &error))) + return failed_("expected error, got object"); + printf("OK (failed as expected: %s)\n", error); + + /* invalid spec: #colors exceeds color depth */ + printf("testing grabbag__picture_parse_specification(\"|image/gif|desc|320x240x9/2345|0.gif\")... "); + if(0 != (obj = grabbag__picture_parse_specification("|image/gif|desc|320x240x9/2345|0.gif", &error))) + return failed_("expected error, got object"); + printf("OK (failed as expected: %s)\n", error); + + /* invalid spec: standard icon has to be 32x32 PNG */ + printf("testing grabbag__picture_parse_specification(\"1|-->|desc|32x24x9|0.gif\")... "); + if(0 != (obj = grabbag__picture_parse_specification("1|-->|desc|32x24x9|0.gif", &error))) + return failed_("expected error, got object"); + printf("OK (failed as expected: %s)\n", error); + + /* invalid spec: need resolution for linked URL */ + printf("testing grabbag__picture_parse_specification(\"|-->|desc||http://blah.blah.blah/z.gif\")... "); + if(0 != (obj = grabbag__picture_parse_specification("|-->|desc||http://blah.blah.blah/z.gif", &error))) + return failed_("expected error, got object"); + printf("OK (failed as expected: %s)\n", error); + + printf("testing grabbag__picture_parse_specification(\"|-->|desc|320x240x9|http://blah.blah.blah/z.gif\")... "); + if(0 == (obj = grabbag__picture_parse_specification("|-->|desc|320x240x9|http://blah.blah.blah/z.gif", &error))) + return failed_(error); + printf("OK\n"); + FLAC__metadata_object_delete(obj); + + /* test automatic parsing of picture files from only the file name */ + for(i = 0; i < sizeof(picturefiles)/sizeof(picturefiles[0]); i++) + if(!test_one_picture(prefix, picturefiles+i, "", /*fn_only=*/true)) + return false; + + /* test automatic parsing of picture files to get resolution/color info */ + for(i = 0; i < sizeof(picturefiles)/sizeof(picturefiles[0]); i++) + if(!test_one_picture(prefix, picturefiles+i, "", /*fn_only=*/false)) + return false; + + picturefiles[0].width = 320; + picturefiles[0].height = 240; + picturefiles[0].depth = 3; + picturefiles[0].colors = 2; + if(!test_one_picture(prefix, picturefiles+0, "320x240x3/2", /*fn_only=*/false)) + return false; + + return true; +} + +int main(int argc, char *argv[]) +{ + const char *usage = "usage: test_pictures path_prefix\n"; + + if(argc > 1 && 0 == strcmp(argv[1], "-h")) { + printf(usage); + return 0; + } + + if(argc != 2) { + fprintf(stderr, usage); + return 255; + } + + return do_picture(argv[1])? 0 : 1; +} diff --git a/src/FLAC/src/test_libFLAC/Makefile.am b/src/FLAC/src/test_libFLAC/Makefile.am new file mode 100644 index 00000000..62f03427 --- /dev/null +++ b/src/FLAC/src/test_libFLAC/Makefile.am @@ -0,0 +1,42 @@ +# test_libFLAC - Unit tester for libFLAC +# Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +INCLUDES = -I$(top_srcdir)/src/FLAC/src/libFLAC/include -I$(top_srcdir)/src/FLAC/include + +noinst_PROGRAMS = test_libFLAC +test_libFLAC_LDADD = \ + $(top_builddir)/src/FLAC/src/share/grabbag/libgrabbag.la \ + $(top_builddir)/src/FLAC/src/share/replaygain_anal/libreplaygain_analysis.la \ + $(top_builddir)/src/FLAC/src/test_libs_common/libtest_libs_common.la \ + $(top_builddir)/src/FLAC/src/libFLAC/libFLAC.la \ + $(top_builddir)/src/OGG/libogg.la \ + -lm + +test_libFLAC_SOURCES = \ + bitwriter.c \ + decoders.c \ + encoders.c \ + format.c \ + main.c \ + metadata.c \ + metadata_manip.c \ + metadata_object.c \ + bitwriter.h \ + decoders.h \ + encoders.h \ + format.h \ + metadata.h diff --git a/src/FLAC/src/test_libFLAC/bitwriter.c b/src/FLAC/src/test_libFLAC/bitwriter.c new file mode 100644 index 00000000..7bc23a87 --- /dev/null +++ b/src/FLAC/src/test_libFLAC/bitwriter.c @@ -0,0 +1,584 @@ +/* test_libFLAC - Unit tester for libFLAC + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include "FLAC/assert.h" +#include "private/bitwriter.h" /* from the libFLAC private include area */ +#include "bitwriter.h" +#include +#include /* for memcmp() */ + +/* adjust for compilers that can't understand using LLU suffix for uint64_t literals */ +#ifdef _MSC_VER +#define FLAC__U64L(x) x +#else +#define FLAC__U64L(x) x##LLU +#endif + +/* + * WATCHOUT! Since FLAC__BitWriter is a private structure, we use a copy of + * the definition here to get at the internals. Make sure this is kept up + * to date with what is in ../libFLAC/bitwriter.c + */ +typedef FLAC__uint32 bwword; + +struct FLAC__BitWriter { + bwword *buffer; + bwword accum; /* accumulator; when full, accum is appended to buffer */ + unsigned capacity; /* of buffer in words */ + unsigned words; /* # of complete words in buffer */ + unsigned bits; /* # of used bits in accum */ +}; + +#define TOTAL_BITS(bw) ((bw)->words*sizeof(bwword)*8 + (bw)->bits) + + +FLAC__bool test_bitwriter(void) +{ + FLAC__BitWriter *bw; + FLAC__bool ok; + unsigned i, j; +#if WORDS_BIGENDIAN + static bwword test_pattern1[5] = { 0xaaf0aabe, 0xaaaaaaa8, 0x300aaaaa, 0xaaadeadb, 0x00eeface }; +#else + static bwword test_pattern1[5] = { 0xbeaaf0aa, 0xa8aaaaaa, 0xaaaa0a30, 0xdbeaadaa, 0x00eeface }; +#endif + unsigned words, bits; /* what we think bw->words and bw->bits should be */ + + printf("\n+++ libFLAC unit test: bitwriter\n\n"); + + /* + * test new -> delete + */ + printf("testing new... "); + bw = FLAC__bitwriter_new(); + if(0 == bw) { + printf("FAILED, returned NULL\n"); + return false; + } + printf("OK\n"); + + printf("testing delete... "); + FLAC__bitwriter_delete(bw); + printf("OK\n"); + + /* + * test new -> init -> delete + */ + printf("testing new... "); + bw = FLAC__bitwriter_new(); + if(0 == bw) { + printf("FAILED, returned NULL\n"); + return false; + } + printf("OK\n"); + + printf("testing init... "); + FLAC__bitwriter_init(bw); + if(0 == bw) { + printf("FAILED, returned NULL\n"); + return false; + } + printf("OK\n"); + + printf("testing delete... "); + FLAC__bitwriter_delete(bw); + printf("OK\n"); + + /* + * test new -> init -> clear -> delete + */ + printf("testing new... "); + bw = FLAC__bitwriter_new(); + if(0 == bw) { + printf("FAILED, returned NULL\n"); + return false; + } + printf("OK\n"); + + printf("testing init... "); + FLAC__bitwriter_init(bw); + if(0 == bw) { + printf("FAILED, returned NULL\n"); + return false; + } + printf("OK\n"); + + printf("testing clear... "); + FLAC__bitwriter_clear(bw); + if(0 == bw) { + printf("FAILED, returned NULL\n"); + return false; + } + printf("OK\n"); + + printf("testing delete... "); + FLAC__bitwriter_delete(bw); + printf("OK\n"); + + /* + * test normal usage + */ + printf("testing new... "); + bw = FLAC__bitwriter_new(); + if(0 == bw) { + printf("FAILED, returned NULL\n"); + return false; + } + printf("OK\n"); + + printf("testing init... "); + ok = FLAC__bitwriter_init(bw); + printf("%s\n", ok?"OK":"FAILED"); + if(!ok) + return false; + + printf("testing clear... "); + FLAC__bitwriter_clear(bw); + printf("OK\n"); + + words = bits = 0; + + printf("capacity = %u\n", bw->capacity); + + printf("testing zeroes, raw_uint32*... "); + ok = + FLAC__bitwriter_write_raw_uint32(bw, 0x1, 1) && + FLAC__bitwriter_write_raw_uint32(bw, 0x1, 2) && + FLAC__bitwriter_write_raw_uint32(bw, 0xa, 5) && + FLAC__bitwriter_write_raw_uint32(bw, 0xf0, 8) && + FLAC__bitwriter_write_raw_uint32(bw, 0x2aa, 10) && + FLAC__bitwriter_write_raw_uint32(bw, 0xf, 4) && + FLAC__bitwriter_write_raw_uint32(bw, 0xaaaaaaaa, 32) && + FLAC__bitwriter_write_zeroes(bw, 4) && + FLAC__bitwriter_write_raw_uint32(bw, 0x3, 2) && + FLAC__bitwriter_write_zeroes(bw, 8) && + FLAC__bitwriter_write_raw_uint64(bw, FLAC__U64L(0xaaaaaaaadeadbeef), 64) && + FLAC__bitwriter_write_raw_uint32(bw, 0xace, 12) + ; + if(!ok) { + printf("FAILED\n"); + FLAC__bitwriter_dump(bw, stdout); + return false; + } + words = 4; + bits = 24; + if(bw->words != words) { + printf("FAILED byte count %u != %u\n", bw->words, words); + FLAC__bitwriter_dump(bw, stdout); + return false; + } + if(bw->bits != bits) { + printf("FAILED bit count %u != %u\n", bw->bits, bits); + FLAC__bitwriter_dump(bw, stdout); + return false; + } + if(memcmp(bw->buffer, test_pattern1, sizeof(bwword)*words) != 0) { + printf("FAILED pattern match (buffer)\n"); + FLAC__bitwriter_dump(bw, stdout); + return false; + } + if((bw->accum & 0x00ffffff) != test_pattern1[words]) { + printf("FAILED pattern match (bw->accum=%08X != %08X)\n", bw->accum&0x00ffffff, test_pattern1[words]); + FLAC__bitwriter_dump(bw, stdout); + return false; + } + printf("OK\n"); + FLAC__bitwriter_dump(bw, stdout); + + printf("testing raw_uint32 some more... "); + ok = FLAC__bitwriter_write_raw_uint32(bw, 0x3d, 6); + if(!ok) { + printf("FAILED\n"); + FLAC__bitwriter_dump(bw, stdout); + return false; + } + bits += 6; + test_pattern1[words] <<= 6; + test_pattern1[words] |= 0x3d; + if(bw->words != words) { + printf("FAILED byte count %u != %u\n", bw->words, words); + FLAC__bitwriter_dump(bw, stdout); + return false; + } + if(bw->bits != bits) { + printf("FAILED bit count %u != %u\n", bw->bits, bits); + FLAC__bitwriter_dump(bw, stdout); + return false; + } + if(memcmp(bw->buffer, test_pattern1, sizeof(bwword)*words) != 0) { + printf("FAILED pattern match (buffer)\n"); + FLAC__bitwriter_dump(bw, stdout); + return false; + } + if((bw->accum & 0x3fffffff) != test_pattern1[words]) { + printf("FAILED pattern match (bw->accum=%08X != %08X)\n", bw->accum&0x3fffffff, test_pattern1[words]); + FLAC__bitwriter_dump(bw, stdout); + return false; + } + printf("OK\n"); + FLAC__bitwriter_dump(bw, stdout); + + printf("testing utf8_uint32(0x00000000)... "); + FLAC__bitwriter_clear(bw); + FLAC__bitwriter_write_utf8_uint32(bw, 0x00000000); + ok = TOTAL_BITS(bw) == 8 && (bw->accum & 0xff) == 0; + printf("%s\n", ok?"OK":"FAILED"); + if(!ok) { + FLAC__bitwriter_dump(bw, stdout); + return false; + } + + printf("testing utf8_uint32(0x0000007F)... "); + FLAC__bitwriter_clear(bw); + FLAC__bitwriter_write_utf8_uint32(bw, 0x0000007F); + ok = TOTAL_BITS(bw) == 8 && (bw->accum & 0xff) == 0x7F; + printf("%s\n", ok?"OK":"FAILED"); + if(!ok) { + FLAC__bitwriter_dump(bw, stdout); + return false; + } + + printf("testing utf8_uint32(0x00000080)... "); + FLAC__bitwriter_clear(bw); + FLAC__bitwriter_write_utf8_uint32(bw, 0x00000080); + ok = TOTAL_BITS(bw) == 16 && (bw->accum & 0xffff) == 0xC280; + printf("%s\n", ok?"OK":"FAILED"); + if(!ok) { + FLAC__bitwriter_dump(bw, stdout); + return false; + } + + printf("testing utf8_uint32(0x000007FF)... "); + FLAC__bitwriter_clear(bw); + FLAC__bitwriter_write_utf8_uint32(bw, 0x000007FF); + ok = TOTAL_BITS(bw) == 16 && (bw->accum & 0xffff) == 0xDFBF; + printf("%s\n", ok?"OK":"FAILED"); + if(!ok) { + FLAC__bitwriter_dump(bw, stdout); + return false; + } + + printf("testing utf8_uint32(0x00000800)... "); + FLAC__bitwriter_clear(bw); + FLAC__bitwriter_write_utf8_uint32(bw, 0x00000800); + ok = TOTAL_BITS(bw) == 24 && (bw->accum & 0xffffff) == 0xE0A080; + printf("%s\n", ok?"OK":"FAILED"); + if(!ok) { + FLAC__bitwriter_dump(bw, stdout); + return false; + } + + printf("testing utf8_uint32(0x0000FFFF)... "); + FLAC__bitwriter_clear(bw); + FLAC__bitwriter_write_utf8_uint32(bw, 0x0000FFFF); + ok = TOTAL_BITS(bw) == 24 && (bw->accum & 0xffffff) == 0xEFBFBF; + printf("%s\n", ok?"OK":"FAILED"); + if(!ok) { + FLAC__bitwriter_dump(bw, stdout); + return false; + } + + printf("testing utf8_uint32(0x00010000)... "); + FLAC__bitwriter_clear(bw); + FLAC__bitwriter_write_utf8_uint32(bw, 0x00010000); +#if WORDS_BIGENDIAN + ok = TOTAL_BITS(bw) == 32 && bw->buffer[0] == 0xF0908080; +#else + ok = TOTAL_BITS(bw) == 32 && bw->buffer[0] == 0x808090F0; +#endif + printf("%s\n", ok?"OK":"FAILED"); + if(!ok) { + FLAC__bitwriter_dump(bw, stdout); + return false; + } + + printf("testing utf8_uint32(0x001FFFFF)... "); + FLAC__bitwriter_clear(bw); + FLAC__bitwriter_write_utf8_uint32(bw, 0x001FFFFF); +#if WORDS_BIGENDIAN + ok = TOTAL_BITS(bw) == 32 && bw->buffer[0] == 0xF7BFBFBF; +#else + ok = TOTAL_BITS(bw) == 32 && bw->buffer[0] == 0xBFBFBFF7; +#endif + printf("%s\n", ok?"OK":"FAILED"); + if(!ok) { + FLAC__bitwriter_dump(bw, stdout); + return false; + } + + printf("testing utf8_uint32(0x00200000)... "); + FLAC__bitwriter_clear(bw); + FLAC__bitwriter_write_utf8_uint32(bw, 0x00200000); +#if WORDS_BIGENDIAN + ok = TOTAL_BITS(bw) == 40 && bw->buffer[0] == 0xF8888080 && (bw->accum & 0xff) == 0x80; +#else + ok = TOTAL_BITS(bw) == 40 && bw->buffer[0] == 0x808088F8 && (bw->accum & 0xff) == 0x80; +#endif + printf("%s\n", ok?"OK":"FAILED"); + if(!ok) { + FLAC__bitwriter_dump(bw, stdout); + return false; + } + + printf("testing utf8_uint32(0x03FFFFFF)... "); + FLAC__bitwriter_clear(bw); + FLAC__bitwriter_write_utf8_uint32(bw, 0x03FFFFFF); +#if WORDS_BIGENDIAN + ok = TOTAL_BITS(bw) == 40 && bw->buffer[0] == 0xFBBFBFBF && (bw->accum & 0xff) == 0xBF; +#else + ok = TOTAL_BITS(bw) == 40 && bw->buffer[0] == 0xBFBFBFFB && (bw->accum & 0xff) == 0xBF; +#endif + printf("%s\n", ok?"OK":"FAILED"); + if(!ok) { + FLAC__bitwriter_dump(bw, stdout); + return false; + } + + printf("testing utf8_uint32(0x04000000)... "); + FLAC__bitwriter_clear(bw); + FLAC__bitwriter_write_utf8_uint32(bw, 0x04000000); +#if WORDS_BIGENDIAN + ok = TOTAL_BITS(bw) == 48 && bw->buffer[0] == 0xFC848080 && (bw->accum & 0xffff) == 0x8080; +#else + ok = TOTAL_BITS(bw) == 48 && bw->buffer[0] == 0x808084FC && (bw->accum & 0xffff) == 0x8080; +#endif + printf("%s\n", ok?"OK":"FAILED"); + if(!ok) { + FLAC__bitwriter_dump(bw, stdout); + return false; + } + + printf("testing utf8_uint32(0x7FFFFFFF)... "); + FLAC__bitwriter_clear(bw); + FLAC__bitwriter_write_utf8_uint32(bw, 0x7FFFFFFF); +#if WORDS_BIGENDIAN + ok = TOTAL_BITS(bw) == 48 && bw->buffer[0] == 0xFDBFBFBF && (bw->accum & 0xffff) == 0xBFBF; +#else + ok = TOTAL_BITS(bw) == 48 && bw->buffer[0] == 0xBFBFBFFD && (bw->accum & 0xffff) == 0xBFBF; +#endif + printf("%s\n", ok?"OK":"FAILED"); + if(!ok) { + FLAC__bitwriter_dump(bw, stdout); + return false; + } + + printf("testing utf8_uint64(0x0000000000000000)... "); + FLAC__bitwriter_clear(bw); + FLAC__bitwriter_write_utf8_uint64(bw, 0x0000000000000000); + ok = TOTAL_BITS(bw) == 8 && (bw->accum & 0xff) == 0; + printf("%s\n", ok?"OK":"FAILED"); + if(!ok) { + FLAC__bitwriter_dump(bw, stdout); + return false; + } + + printf("testing utf8_uint64(0x000000000000007F)... "); + FLAC__bitwriter_clear(bw); + FLAC__bitwriter_write_utf8_uint64(bw, 0x000000000000007F); + ok = TOTAL_BITS(bw) == 8 && (bw->accum & 0xff) == 0x7F; + printf("%s\n", ok?"OK":"FAILED"); + if(!ok) { + FLAC__bitwriter_dump(bw, stdout); + return false; + } + + printf("testing utf8_uint64(0x0000000000000080)... "); + FLAC__bitwriter_clear(bw); + FLAC__bitwriter_write_utf8_uint64(bw, 0x0000000000000080); + ok = TOTAL_BITS(bw) == 16 && (bw->accum & 0xffff) == 0xC280; + printf("%s\n", ok?"OK":"FAILED"); + if(!ok) { + FLAC__bitwriter_dump(bw, stdout); + return false; + } + + printf("testing utf8_uint64(0x00000000000007FF)... "); + FLAC__bitwriter_clear(bw); + FLAC__bitwriter_write_utf8_uint64(bw, 0x00000000000007FF); + ok = TOTAL_BITS(bw) == 16 && (bw->accum & 0xffff) == 0xDFBF; + printf("%s\n", ok?"OK":"FAILED"); + if(!ok) { + FLAC__bitwriter_dump(bw, stdout); + return false; + } + + printf("testing utf8_uint64(0x0000000000000800)... "); + FLAC__bitwriter_clear(bw); + FLAC__bitwriter_write_utf8_uint64(bw, 0x0000000000000800); + ok = TOTAL_BITS(bw) == 24 && (bw->accum & 0xffffff) == 0xE0A080; + printf("%s\n", ok?"OK":"FAILED"); + if(!ok) { + FLAC__bitwriter_dump(bw, stdout); + return false; + } + + printf("testing utf8_uint64(0x000000000000FFFF)... "); + FLAC__bitwriter_clear(bw); + FLAC__bitwriter_write_utf8_uint64(bw, 0x000000000000FFFF); + ok = TOTAL_BITS(bw) == 24 && (bw->accum & 0xffffff) == 0xEFBFBF; + printf("%s\n", ok?"OK":"FAILED"); + if(!ok) { + FLAC__bitwriter_dump(bw, stdout); + return false; + } + + printf("testing utf8_uint64(0x0000000000010000)... "); + FLAC__bitwriter_clear(bw); + FLAC__bitwriter_write_utf8_uint64(bw, 0x0000000000010000); +#if WORDS_BIGENDIAN + ok = TOTAL_BITS(bw) == 32 && bw->buffer[0] == 0xF0908080; +#else + ok = TOTAL_BITS(bw) == 32 && bw->buffer[0] == 0x808090F0; +#endif + printf("%s\n", ok?"OK":"FAILED"); + if(!ok) { + FLAC__bitwriter_dump(bw, stdout); + return false; + } + + printf("testing utf8_uint64(0x00000000001FFFFF)... "); + FLAC__bitwriter_clear(bw); + FLAC__bitwriter_write_utf8_uint64(bw, 0x00000000001FFFFF); +#if WORDS_BIGENDIAN + ok = TOTAL_BITS(bw) == 32 && bw->buffer[0] == 0xF7BFBFBF; +#else + ok = TOTAL_BITS(bw) == 32 && bw->buffer[0] == 0xBFBFBFF7; +#endif + printf("%s\n", ok?"OK":"FAILED"); + if(!ok) { + FLAC__bitwriter_dump(bw, stdout); + return false; + } + + printf("testing utf8_uint64(0x0000000000200000)... "); + FLAC__bitwriter_clear(bw); + FLAC__bitwriter_write_utf8_uint64(bw, 0x0000000000200000); +#if WORDS_BIGENDIAN + ok = TOTAL_BITS(bw) == 40 && bw->buffer[0] == 0xF8888080 && (bw->accum & 0xff) == 0x80; +#else + ok = TOTAL_BITS(bw) == 40 && bw->buffer[0] == 0x808088F8 && (bw->accum & 0xff) == 0x80; +#endif + printf("%s\n", ok?"OK":"FAILED"); + if(!ok) { + FLAC__bitwriter_dump(bw, stdout); + return false; + } + + printf("testing utf8_uint64(0x0000000003FFFFFF)... "); + FLAC__bitwriter_clear(bw); + FLAC__bitwriter_write_utf8_uint64(bw, 0x0000000003FFFFFF); +#if WORDS_BIGENDIAN + ok = TOTAL_BITS(bw) == 40 && bw->buffer[0] == 0xFBBFBFBF && (bw->accum & 0xff) == 0xBF; +#else + ok = TOTAL_BITS(bw) == 40 && bw->buffer[0] == 0xBFBFBFFB && (bw->accum & 0xff) == 0xBF; +#endif + printf("%s\n", ok?"OK":"FAILED"); + if(!ok) { + FLAC__bitwriter_dump(bw, stdout); + return false; + } + + printf("testing utf8_uint64(0x0000000004000000)... "); + FLAC__bitwriter_clear(bw); + FLAC__bitwriter_write_utf8_uint64(bw, 0x0000000004000000); +#if WORDS_BIGENDIAN + ok = TOTAL_BITS(bw) == 48 && bw->buffer[0] == 0xFC848080 && (bw->accum & 0xffff) == 0x8080; +#else + ok = TOTAL_BITS(bw) == 48 && bw->buffer[0] == 0x808084FC && (bw->accum & 0xffff) == 0x8080; +#endif + printf("%s\n", ok?"OK":"FAILED"); + if(!ok) { + FLAC__bitwriter_dump(bw, stdout); + return false; + } + + printf("testing utf8_uint64(0x000000007FFFFFFF)... "); + FLAC__bitwriter_clear(bw); + FLAC__bitwriter_write_utf8_uint64(bw, 0x000000007FFFFFFF); +#if WORDS_BIGENDIAN + ok = TOTAL_BITS(bw) == 48 && bw->buffer[0] == 0xFDBFBFBF && (bw->accum & 0xffff) == 0xBFBF; +#else + ok = TOTAL_BITS(bw) == 48 && bw->buffer[0] == 0xBFBFBFFD && (bw->accum & 0xffff) == 0xBFBF; +#endif + printf("%s\n", ok?"OK":"FAILED"); + if(!ok) { + FLAC__bitwriter_dump(bw, stdout); + return false; + } + + printf("testing utf8_uint64(0x0000000080000000)... "); + FLAC__bitwriter_clear(bw); + FLAC__bitwriter_write_utf8_uint64(bw, 0x0000000080000000); +#if WORDS_BIGENDIAN + ok = TOTAL_BITS(bw) == 56 && bw->buffer[0] == 0xFE828080 && (bw->accum & 0xffffff) == 0x808080; +#else + ok = TOTAL_BITS(bw) == 56 && bw->buffer[0] == 0x808082FE && (bw->accum & 0xffffff) == 0x808080; +#endif + printf("%s\n", ok?"OK":"FAILED"); + if(!ok) { + FLAC__bitwriter_dump(bw, stdout); + return false; + } + + printf("testing utf8_uint64(0x0000000FFFFFFFFF)... "); + FLAC__bitwriter_clear(bw); + FLAC__bitwriter_write_utf8_uint64(bw, FLAC__U64L(0x0000000FFFFFFFFF)); +#if WORDS_BIGENDIAN + ok = TOTAL_BITS(bw) == 56 && bw->buffer[0] == 0xFEBFBFBF && (bw->accum & 0xffffff) == 0xBFBFBF; +#else + ok = TOTAL_BITS(bw) == 56 && bw->buffer[0] == 0xBFBFBFFE && (bw->accum & 0xffffff) == 0xBFBFBF; +#endif + printf("%s\n", ok?"OK":"FAILED"); + if(!ok) { + FLAC__bitwriter_dump(bw, stdout); + return false; + } + + printf("testing grow... "); + FLAC__bitwriter_clear(bw); + FLAC__bitwriter_write_raw_uint32(bw, 0x5, 4); + j = bw->capacity; + for(i = 0; i < j; i++) + FLAC__bitwriter_write_raw_uint32(bw, 0xaaaaaaaa, 32); +#if WORDS_BIGENDIAN + ok = TOTAL_BITS(bw) == i*32+4 && bw->buffer[0] == 0x5aaaaaaa && (bw->accum & 0xf) == 0xa; +#else + ok = TOTAL_BITS(bw) == i*32+4 && bw->buffer[0] == 0xaaaaaa5a && (bw->accum & 0xf) == 0xa; +#endif + printf("%s\n", ok?"OK":"FAILED"); + if(!ok) { + FLAC__bitwriter_dump(bw, stdout); + return false; + } + printf("capacity = %u\n", bw->capacity); + + printf("testing free... "); + FLAC__bitwriter_free(bw); + printf("OK\n"); + + printf("testing delete... "); + FLAC__bitwriter_delete(bw); + printf("OK\n"); + + printf("\nPASSED!\n"); + return true; +} diff --git a/src/FLAC/src/test_libFLAC/bitwriter.h b/src/FLAC/src/test_libFLAC/bitwriter.h new file mode 100644 index 00000000..44b6ade5 --- /dev/null +++ b/src/FLAC/src/test_libFLAC/bitwriter.h @@ -0,0 +1,26 @@ +/* test_libFLAC - Unit tester for libFLAC + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef FLAC__TEST_LIBFLAC_BITBUFFER_H +#define FLAC__TEST_LIBFLAC_BITBUFFER_H + +#include "FLAC/ordinals.h" + +FLAC__bool test_bitwriter(void); + +#endif diff --git a/src/FLAC/src/test_libFLAC/decoders.c b/src/FLAC/src/test_libFLAC/decoders.c new file mode 100644 index 00000000..9e77caca --- /dev/null +++ b/src/FLAC/src/test_libFLAC/decoders.c @@ -0,0 +1,1048 @@ +/* test_libFLAC - Unit tester for libFLAC + * Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#if defined _MSC_VER || defined __MINGW32__ +#if _MSC_VER <= 1600 /* @@@ [2G limit] */ +#define fseeko fseek +#define ftello ftell +#endif +#endif +#include "decoders.h" +#include "FLAC/assert.h" +#include "FLAC/stream_decoder.h" +#include "share/grabbag.h" +#include "test_libs_common/file_utils_flac.h" +#include "test_libs_common/metadata_utils.h" + +typedef enum { + LAYER_STREAM = 0, /* FLAC__stream_decoder_init_[ogg_]stream() without seeking */ + LAYER_SEEKABLE_STREAM, /* FLAC__stream_decoder_init_[ogg_]stream() with seeking */ + LAYER_FILE, /* FLAC__stream_decoder_init_[ogg_]FILE() */ + LAYER_FILENAME /* FLAC__stream_decoder_init_[ogg_]file() */ +} Layer; + +static const char * const LayerString[] = { + "Stream", + "Seekable Stream", + "FILE*", + "Filename" +}; + +typedef struct { + Layer layer; + FILE *file; + unsigned current_metadata_number; + FLAC__bool ignore_errors; + FLAC__bool error_occurred; +} StreamDecoderClientData; + +static FLAC__StreamMetadata streaminfo_, padding_, seektable_, application1_, application2_, vorbiscomment_, cuesheet_, picture_, unknown_; +static FLAC__StreamMetadata *expected_metadata_sequence_[9]; +static unsigned num_expected_; +static off_t flacfilesize_; + +static const char *flacfilename(FLAC__bool is_ogg) +{ + return is_ogg? "metadata.ogg" : "metadata.flac"; +} + +static FLAC__bool die_(const char *msg) +{ + printf("ERROR: %s\n", msg); + return false; +} + +static FLAC__bool die_s_(const char *msg, const FLAC__StreamDecoder *decoder) +{ + FLAC__StreamDecoderState state = FLAC__stream_decoder_get_state(decoder); + + if(msg) + printf("FAILED, %s", msg); + else + printf("FAILED"); + + printf(", state = %u (%s)\n", (unsigned)state, FLAC__StreamDecoderStateString[state]); + + return false; +} + +static void init_metadata_blocks_(void) +{ + mutils__init_metadata_blocks(&streaminfo_, &padding_, &seektable_, &application1_, &application2_, &vorbiscomment_, &cuesheet_, &picture_, &unknown_); +} + +static void free_metadata_blocks_(void) +{ + mutils__free_metadata_blocks(&streaminfo_, &padding_, &seektable_, &application1_, &application2_, &vorbiscomment_, &cuesheet_, &picture_, &unknown_); +} + +static FLAC__bool generate_file_(FLAC__bool is_ogg) +{ + printf("\n\ngenerating %sFLAC file for decoder tests...\n", is_ogg? "Ogg ":""); + + num_expected_ = 0; + expected_metadata_sequence_[num_expected_++] = &padding_; + expected_metadata_sequence_[num_expected_++] = &seektable_; + expected_metadata_sequence_[num_expected_++] = &application1_; + expected_metadata_sequence_[num_expected_++] = &application2_; + expected_metadata_sequence_[num_expected_++] = &vorbiscomment_; + expected_metadata_sequence_[num_expected_++] = &cuesheet_; + expected_metadata_sequence_[num_expected_++] = &picture_; + expected_metadata_sequence_[num_expected_++] = &unknown_; + /* WATCHOUT: for Ogg FLAC the encoder should move the VORBIS_COMMENT block to the front, right after STREAMINFO */ + + if(!file_utils__generate_flacfile(is_ogg, flacfilename(is_ogg), &flacfilesize_, 512 * 1024, &streaminfo_, expected_metadata_sequence_, num_expected_)) + return die_("creating the encoded file"); + + return true; +} + +static FLAC__StreamDecoderReadStatus stream_decoder_read_callback_(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data) +{ + StreamDecoderClientData *dcd = (StreamDecoderClientData*)client_data; + const size_t requested_bytes = *bytes; + + (void)decoder; + + if(0 == dcd) { + printf("ERROR: client_data in read callback is NULL\n"); + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + } + + if(dcd->error_occurred) + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + + if(feof(dcd->file)) { + *bytes = 0; + return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; + } + else if(requested_bytes > 0) { + *bytes = fread(buffer, 1, requested_bytes, dcd->file); + if(*bytes == 0) { + if(feof(dcd->file)) + return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; + else + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + } + else { + return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; + } + } + else + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; /* abort to avoid a deadlock */ +} + +static FLAC__StreamDecoderSeekStatus stream_decoder_seek_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data) +{ + StreamDecoderClientData *dcd = (StreamDecoderClientData*)client_data; + + (void)decoder; + + if(0 == dcd) { + printf("ERROR: client_data in seek callback is NULL\n"); + return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR; + } + + if(dcd->error_occurred) + return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR; + + if(fseeko(dcd->file, (off_t)absolute_byte_offset, SEEK_SET) < 0) { + dcd->error_occurred = true; + return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR; + } + + return FLAC__STREAM_DECODER_SEEK_STATUS_OK; +} + +static FLAC__StreamDecoderTellStatus stream_decoder_tell_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data) +{ + StreamDecoderClientData *dcd = (StreamDecoderClientData*)client_data; + off_t offset; + + (void)decoder; + + if(0 == dcd) { + printf("ERROR: client_data in tell callback is NULL\n"); + return FLAC__STREAM_DECODER_TELL_STATUS_ERROR; + } + + if(dcd->error_occurred) + return FLAC__STREAM_DECODER_TELL_STATUS_ERROR; + + offset = ftello(dcd->file); + *absolute_byte_offset = (FLAC__uint64)offset; + + if(offset < 0) { + dcd->error_occurred = true; + return FLAC__STREAM_DECODER_TELL_STATUS_ERROR; + } + + return FLAC__STREAM_DECODER_TELL_STATUS_OK; +} + +static FLAC__StreamDecoderLengthStatus stream_decoder_length_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data) +{ + StreamDecoderClientData *dcd = (StreamDecoderClientData*)client_data; + + (void)decoder; + + if(0 == dcd) { + printf("ERROR: client_data in length callback is NULL\n"); + return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR; + } + + if(dcd->error_occurred) + return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR; + + *stream_length = (FLAC__uint64)flacfilesize_; + return FLAC__STREAM_DECODER_LENGTH_STATUS_OK; +} + +static FLAC__bool stream_decoder_eof_callback_(const FLAC__StreamDecoder *decoder, void *client_data) +{ + StreamDecoderClientData *dcd = (StreamDecoderClientData*)client_data; + + (void)decoder; + + if(0 == dcd) { + printf("ERROR: client_data in eof callback is NULL\n"); + return true; + } + + if(dcd->error_occurred) + return true; + + return feof(dcd->file); +} + +static FLAC__StreamDecoderWriteStatus stream_decoder_write_callback_(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data) +{ + StreamDecoderClientData *dcd = (StreamDecoderClientData*)client_data; + + (void)decoder, (void)buffer; + + if(0 == dcd) { + printf("ERROR: client_data in write callback is NULL\n"); + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + + if(dcd->error_occurred) + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + + if( + (frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_FRAME_NUMBER && frame->header.number.frame_number == 0) || + (frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER && frame->header.number.sample_number == 0) + ) { + printf("content... "); + fflush(stdout); + } + + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; +} + +static void stream_decoder_metadata_callback_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) +{ + StreamDecoderClientData *dcd = (StreamDecoderClientData*)client_data; + + (void)decoder; + + if(0 == dcd) { + printf("ERROR: client_data in metadata callback is NULL\n"); + return; + } + + if(dcd->error_occurred) + return; + + printf("%d... ", dcd->current_metadata_number); + fflush(stdout); + + if(dcd->current_metadata_number >= num_expected_) { + (void)die_("got more metadata blocks than expected"); + dcd->error_occurred = true; + } + else { + if(!mutils__compare_block(expected_metadata_sequence_[dcd->current_metadata_number], metadata)) { + (void)die_("metadata block mismatch"); + dcd->error_occurred = true; + } + } + dcd->current_metadata_number++; +} + +static void stream_decoder_error_callback_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) +{ + StreamDecoderClientData *dcd = (StreamDecoderClientData*)client_data; + + (void)decoder; + + if(0 == dcd) { + printf("ERROR: client_data in error callback is NULL\n"); + return; + } + + if(!dcd->ignore_errors) { + printf("ERROR: got error callback: err = %u (%s)\n", (unsigned)status, FLAC__StreamDecoderErrorStatusString[status]); + dcd->error_occurred = true; + } +} + +static FLAC__bool stream_decoder_test_respond_(FLAC__StreamDecoder *decoder, StreamDecoderClientData *dcd, FLAC__bool is_ogg) +{ + FLAC__StreamDecoderInitStatus init_status; + + if(!FLAC__stream_decoder_set_md5_checking(decoder, true)) + return die_s_("at FLAC__stream_decoder_set_md5_checking(), returned false", decoder); + + /* for FLAC__stream_encoder_init_FILE(), the FLAC__stream_encoder_finish() closes the file so we have to keep re-opening: */ + if(dcd->layer == LAYER_FILE) { + printf("opening %sFLAC file... ", is_ogg? "Ogg ":""); + dcd->file = fopen(flacfilename(is_ogg), "rb"); + if(0 == dcd->file) { + printf("ERROR (%s)\n", strerror(errno)); + return false; + } + printf("OK\n"); + } + + switch(dcd->layer) { + case LAYER_STREAM: + printf("testing FLAC__stream_decoder_init_%sstream()... ", is_ogg? "ogg_":""); + init_status = is_ogg? + FLAC__stream_decoder_init_ogg_stream(decoder, stream_decoder_read_callback_, /*seek_callback=*/0, /*tell_callback=*/0, /*length_callback=*/0, /*eof_callback=*/0, stream_decoder_write_callback_, stream_decoder_metadata_callback_, stream_decoder_error_callback_, dcd) : + FLAC__stream_decoder_init_stream(decoder, stream_decoder_read_callback_, /*seek_callback=*/0, /*tell_callback=*/0, /*length_callback=*/0, /*eof_callback=*/0, stream_decoder_write_callback_, stream_decoder_metadata_callback_, stream_decoder_error_callback_, dcd) + ; + break; + case LAYER_SEEKABLE_STREAM: + printf("testing FLAC__stream_decoder_init_%sstream()... ", is_ogg? "ogg_":""); + init_status = is_ogg? + FLAC__stream_decoder_init_ogg_stream(decoder, stream_decoder_read_callback_, stream_decoder_seek_callback_, stream_decoder_tell_callback_, stream_decoder_length_callback_, stream_decoder_eof_callback_, stream_decoder_write_callback_, stream_decoder_metadata_callback_, stream_decoder_error_callback_, dcd) : + FLAC__stream_decoder_init_stream(decoder, stream_decoder_read_callback_, stream_decoder_seek_callback_, stream_decoder_tell_callback_, stream_decoder_length_callback_, stream_decoder_eof_callback_, stream_decoder_write_callback_, stream_decoder_metadata_callback_, stream_decoder_error_callback_, dcd); + break; + case LAYER_FILE: + printf("testing FLAC__stream_decoder_init_%sFILE()... ", is_ogg? "ogg_":""); + init_status = is_ogg? + FLAC__stream_decoder_init_ogg_FILE(decoder, dcd->file, stream_decoder_write_callback_, stream_decoder_metadata_callback_, stream_decoder_error_callback_, dcd) : + FLAC__stream_decoder_init_FILE(decoder, dcd->file, stream_decoder_write_callback_, stream_decoder_metadata_callback_, stream_decoder_error_callback_, dcd); + break; + case LAYER_FILENAME: + printf("testing FLAC__stream_decoder_init_%sfile()... ", is_ogg? "ogg_":""); + init_status = is_ogg? + FLAC__stream_decoder_init_ogg_file(decoder, flacfilename(is_ogg), stream_decoder_write_callback_, stream_decoder_metadata_callback_, stream_decoder_error_callback_, dcd) : + FLAC__stream_decoder_init_file(decoder, flacfilename(is_ogg), stream_decoder_write_callback_, stream_decoder_metadata_callback_, stream_decoder_error_callback_, dcd); + break; + default: + die_("internal error 000"); + return false; + } + if(init_status != FLAC__STREAM_DECODER_INIT_STATUS_OK) + return die_s_(0, decoder); + printf("OK\n"); + + dcd->current_metadata_number = 0; + + if(dcd->layer < LAYER_FILE && fseeko(dcd->file, 0, SEEK_SET) < 0) { + printf("FAILED rewinding input, errno = %d\n", errno); + return false; + } + + printf("testing FLAC__stream_decoder_process_until_end_of_stream()... "); + if(!FLAC__stream_decoder_process_until_end_of_stream(decoder)) + return die_s_("returned false", decoder); + printf("OK\n"); + + printf("testing FLAC__stream_decoder_finish()... "); + if(!FLAC__stream_decoder_finish(decoder)) + return die_s_("returned false", decoder); + printf("OK\n"); + + return true; +} + +static FLAC__bool test_stream_decoder(Layer layer, FLAC__bool is_ogg) +{ + FLAC__StreamDecoder *decoder; + FLAC__StreamDecoderInitStatus init_status; + FLAC__StreamDecoderState state; + StreamDecoderClientData decoder_client_data; + FLAC__bool expect; + + decoder_client_data.layer = layer; + + printf("\n+++ libFLAC unit test: FLAC__StreamDecoder (layer: %s, format: %s)\n\n", LayerString[layer], is_ogg? "Ogg FLAC" : "FLAC"); + + printf("testing FLAC__stream_decoder_new()... "); + decoder = FLAC__stream_decoder_new(); + if(0 == decoder) { + printf("FAILED, returned NULL\n"); + return false; + } + printf("OK\n"); + + printf("testing FLAC__stream_decoder_delete()... "); + FLAC__stream_decoder_delete(decoder); + printf("OK\n"); + + printf("testing FLAC__stream_decoder_new()... "); + decoder = FLAC__stream_decoder_new(); + if(0 == decoder) { + printf("FAILED, returned NULL\n"); + return false; + } + printf("OK\n"); + + switch(layer) { + case LAYER_STREAM: + case LAYER_SEEKABLE_STREAM: + printf("testing FLAC__stream_decoder_init_%sstream()... ", is_ogg? "ogg_":""); + init_status = is_ogg? + FLAC__stream_decoder_init_ogg_stream(decoder, 0, 0, 0, 0, 0, 0, 0, 0, 0) : + FLAC__stream_decoder_init_stream(decoder, 0, 0, 0, 0, 0, 0, 0, 0, 0); + break; + case LAYER_FILE: + printf("testing FLAC__stream_decoder_init_%sFILE()... ", is_ogg? "ogg_":""); + init_status = is_ogg? + FLAC__stream_decoder_init_ogg_FILE(decoder, stdin, 0, 0, 0, 0) : + FLAC__stream_decoder_init_FILE(decoder, stdin, 0, 0, 0, 0); + break; + case LAYER_FILENAME: + printf("testing FLAC__stream_decoder_init_%sfile()... ", is_ogg? "ogg_":""); + init_status = is_ogg? + FLAC__stream_decoder_init_ogg_file(decoder, flacfilename(is_ogg), 0, 0, 0, 0) : + FLAC__stream_decoder_init_file(decoder, flacfilename(is_ogg), 0, 0, 0, 0); + break; + default: + die_("internal error 003"); + return false; + } + if(init_status != FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS) + return die_s_(0, decoder); + printf("OK\n"); + + printf("testing FLAC__stream_decoder_delete()... "); + FLAC__stream_decoder_delete(decoder); + printf("OK\n"); + + num_expected_ = 0; + expected_metadata_sequence_[num_expected_++] = &streaminfo_; + + printf("testing FLAC__stream_decoder_new()... "); + decoder = FLAC__stream_decoder_new(); + if(0 == decoder) { + printf("FAILED, returned NULL\n"); + return false; + } + printf("OK\n"); + + if(is_ogg) { + printf("testing FLAC__stream_decoder_set_ogg_serial_number()... "); + if(!FLAC__stream_decoder_set_ogg_serial_number(decoder, file_utils__ogg_serial_number)) + return die_s_("returned false", decoder); + printf("OK\n"); + } + + printf("testing FLAC__stream_decoder_set_md5_checking()... "); + if(!FLAC__stream_decoder_set_md5_checking(decoder, true)) + return die_s_("returned false", decoder); + printf("OK\n"); + + if(layer < LAYER_FILENAME) { + printf("opening %sFLAC file... ", is_ogg? "Ogg ":""); + decoder_client_data.file = fopen(flacfilename(is_ogg), "rb"); + if(0 == decoder_client_data.file) { + printf("ERROR (%s)\n", strerror(errno)); + return false; + } + printf("OK\n"); + } + + switch(layer) { + case LAYER_STREAM: + printf("testing FLAC__stream_decoder_init_%sstream()... ", is_ogg? "ogg_":""); + init_status = is_ogg? + FLAC__stream_decoder_init_ogg_stream(decoder, stream_decoder_read_callback_, /*seek_callback=*/0, /*tell_callback=*/0, /*length_callback=*/0, /*eof_callback=*/0, stream_decoder_write_callback_, stream_decoder_metadata_callback_, stream_decoder_error_callback_, &decoder_client_data) : + FLAC__stream_decoder_init_stream(decoder, stream_decoder_read_callback_, /*seek_callback=*/0, /*tell_callback=*/0, /*length_callback=*/0, /*eof_callback=*/0, stream_decoder_write_callback_, stream_decoder_metadata_callback_, stream_decoder_error_callback_, &decoder_client_data); + break; + case LAYER_SEEKABLE_STREAM: + printf("testing FLAC__stream_decoder_init_%sstream()... ", is_ogg? "ogg_":""); + init_status = is_ogg? + FLAC__stream_decoder_init_ogg_stream(decoder, stream_decoder_read_callback_, stream_decoder_seek_callback_, stream_decoder_tell_callback_, stream_decoder_length_callback_, stream_decoder_eof_callback_, stream_decoder_write_callback_, stream_decoder_metadata_callback_, stream_decoder_error_callback_, &decoder_client_data) : + FLAC__stream_decoder_init_stream(decoder, stream_decoder_read_callback_, stream_decoder_seek_callback_, stream_decoder_tell_callback_, stream_decoder_length_callback_, stream_decoder_eof_callback_, stream_decoder_write_callback_, stream_decoder_metadata_callback_, stream_decoder_error_callback_, &decoder_client_data); + break; + case LAYER_FILE: + printf("testing FLAC__stream_decoder_init_%sFILE()... ", is_ogg? "ogg_":""); + init_status = is_ogg? + FLAC__stream_decoder_init_ogg_FILE(decoder, decoder_client_data.file, stream_decoder_write_callback_, stream_decoder_metadata_callback_, stream_decoder_error_callback_, &decoder_client_data) : + FLAC__stream_decoder_init_FILE(decoder, decoder_client_data.file, stream_decoder_write_callback_, stream_decoder_metadata_callback_, stream_decoder_error_callback_, &decoder_client_data); + break; + case LAYER_FILENAME: + printf("testing FLAC__stream_decoder_init_%sfile()... ", is_ogg? "ogg_":""); + init_status = is_ogg? + FLAC__stream_decoder_init_ogg_file(decoder, flacfilename(is_ogg), stream_decoder_write_callback_, stream_decoder_metadata_callback_, stream_decoder_error_callback_, &decoder_client_data) : + FLAC__stream_decoder_init_file(decoder, flacfilename(is_ogg), stream_decoder_write_callback_, stream_decoder_metadata_callback_, stream_decoder_error_callback_, &decoder_client_data); + break; + default: + die_("internal error 009"); + return false; + } + if(init_status != FLAC__STREAM_DECODER_INIT_STATUS_OK) + return die_s_(0, decoder); + printf("OK\n"); + + printf("testing FLAC__stream_decoder_get_state()... "); + state = FLAC__stream_decoder_get_state(decoder); + printf("returned state = %u (%s)... OK\n", state, FLAC__StreamDecoderStateString[state]); + + decoder_client_data.current_metadata_number = 0; + decoder_client_data.ignore_errors = false; + decoder_client_data.error_occurred = false; + + printf("testing FLAC__stream_decoder_get_md5_checking()... "); + if(!FLAC__stream_decoder_get_md5_checking(decoder)) { + printf("FAILED, returned false, expected true\n"); + return false; + } + printf("OK\n"); + + printf("testing FLAC__stream_decoder_process_until_end_of_metadata()... "); + if(!FLAC__stream_decoder_process_until_end_of_metadata(decoder)) + return die_s_("returned false", decoder); + printf("OK\n"); + + printf("testing FLAC__stream_decoder_process_single()... "); + if(!FLAC__stream_decoder_process_single(decoder)) + return die_s_("returned false", decoder); + printf("OK\n"); + + printf("testing FLAC__stream_decoder_skip_single_frame()... "); + if(!FLAC__stream_decoder_skip_single_frame(decoder)) + return die_s_("returned false", decoder); + printf("OK\n"); + + if(layer < LAYER_FILE) { + printf("testing FLAC__stream_decoder_flush()... "); + if(!FLAC__stream_decoder_flush(decoder)) + return die_s_("returned false", decoder); + printf("OK\n"); + + decoder_client_data.ignore_errors = true; + printf("testing FLAC__stream_decoder_process_single()... "); + if(!FLAC__stream_decoder_process_single(decoder)) + return die_s_("returned false", decoder); + printf("OK\n"); + decoder_client_data.ignore_errors = false; + } + + expect = (layer != LAYER_STREAM); + printf("testing FLAC__stream_decoder_seek_absolute()... "); + if(FLAC__stream_decoder_seek_absolute(decoder, 0) != expect) + return die_s_(expect? "returned false" : "returned true", decoder); + printf("OK\n"); + + printf("testing FLAC__stream_decoder_process_until_end_of_stream()... "); + if(!FLAC__stream_decoder_process_until_end_of_stream(decoder)) + return die_s_("returned false", decoder); + printf("OK\n"); + + expect = (layer != LAYER_STREAM); + printf("testing FLAC__stream_decoder_seek_absolute()... "); + if(FLAC__stream_decoder_seek_absolute(decoder, 0) != expect) + return die_s_(expect? "returned false" : "returned true", decoder); + printf("OK\n"); + + printf("testing FLAC__stream_decoder_get_channels()... "); + { + unsigned channels = FLAC__stream_decoder_get_channels(decoder); + if(channels != streaminfo_.data.stream_info.channels) { + printf("FAILED, returned %u, expected %u\n", channels, streaminfo_.data.stream_info.channels); + return false; + } + } + printf("OK\n"); + + printf("testing FLAC__stream_decoder_get_bits_per_sample()... "); + { + unsigned bits_per_sample = FLAC__stream_decoder_get_bits_per_sample(decoder); + if(bits_per_sample != streaminfo_.data.stream_info.bits_per_sample) { + printf("FAILED, returned %u, expected %u\n", bits_per_sample, streaminfo_.data.stream_info.bits_per_sample); + return false; + } + } + printf("OK\n"); + + printf("testing FLAC__stream_decoder_get_sample_rate()... "); + { + unsigned sample_rate = FLAC__stream_decoder_get_sample_rate(decoder); + if(sample_rate != streaminfo_.data.stream_info.sample_rate) { + printf("FAILED, returned %u, expected %u\n", sample_rate, streaminfo_.data.stream_info.sample_rate); + return false; + } + } + printf("OK\n"); + + printf("testing FLAC__stream_decoder_get_blocksize()... "); + { + unsigned blocksize = FLAC__stream_decoder_get_blocksize(decoder); + /* value could be anything since we're at the last block, so accept any reasonable answer */ + printf("returned %u... %s\n", blocksize, blocksize>0? "OK" : "FAILED"); + if(blocksize == 0) + return false; + } + + printf("testing FLAC__stream_decoder_get_channel_assignment()... "); + { + FLAC__ChannelAssignment ca = FLAC__stream_decoder_get_channel_assignment(decoder); + printf("returned %u (%s)... OK\n", (unsigned)ca, FLAC__ChannelAssignmentString[ca]); + } + + if(layer < LAYER_FILE) { + printf("testing FLAC__stream_decoder_reset()... "); + if(!FLAC__stream_decoder_reset(decoder)) { + state = FLAC__stream_decoder_get_state(decoder); + printf("FAILED, returned false, state = %u (%s)\n", state, FLAC__StreamDecoderStateString[state]); + return false; + } + printf("OK\n"); + + if(layer == LAYER_STREAM) { + /* after a reset() we have to rewind the input ourselves */ + printf("rewinding input... "); + if(fseeko(decoder_client_data.file, 0, SEEK_SET) < 0) { + printf("FAILED, errno = %d\n", errno); + return false; + } + printf("OK\n"); + } + + decoder_client_data.current_metadata_number = 0; + + printf("testing FLAC__stream_decoder_process_until_end_of_stream()... "); + if(!FLAC__stream_decoder_process_until_end_of_stream(decoder)) + return die_s_("returned false", decoder); + printf("OK\n"); + } + + printf("testing FLAC__stream_decoder_finish()... "); + if(!FLAC__stream_decoder_finish(decoder)) + return die_s_("returned false", decoder); + printf("OK\n"); + + /* + * respond all + */ + + printf("testing FLAC__stream_decoder_set_metadata_respond_all()... "); + if(!FLAC__stream_decoder_set_metadata_respond_all(decoder)) + return die_s_("returned false", decoder); + printf("OK\n"); + + num_expected_ = 0; + if(is_ogg) { /* encoder moves vorbis comment after streaminfo according to ogg mapping */ + expected_metadata_sequence_[num_expected_++] = &streaminfo_; + expected_metadata_sequence_[num_expected_++] = &vorbiscomment_; + expected_metadata_sequence_[num_expected_++] = &padding_; + expected_metadata_sequence_[num_expected_++] = &seektable_; + expected_metadata_sequence_[num_expected_++] = &application1_; + expected_metadata_sequence_[num_expected_++] = &application2_; + expected_metadata_sequence_[num_expected_++] = &cuesheet_; + expected_metadata_sequence_[num_expected_++] = &picture_; + expected_metadata_sequence_[num_expected_++] = &unknown_; + } + else { + expected_metadata_sequence_[num_expected_++] = &streaminfo_; + expected_metadata_sequence_[num_expected_++] = &padding_; + expected_metadata_sequence_[num_expected_++] = &seektable_; + expected_metadata_sequence_[num_expected_++] = &application1_; + expected_metadata_sequence_[num_expected_++] = &application2_; + expected_metadata_sequence_[num_expected_++] = &vorbiscomment_; + expected_metadata_sequence_[num_expected_++] = &cuesheet_; + expected_metadata_sequence_[num_expected_++] = &picture_; + expected_metadata_sequence_[num_expected_++] = &unknown_; + } + + if(!stream_decoder_test_respond_(decoder, &decoder_client_data, is_ogg)) + return false; + + /* + * ignore all + */ + + printf("testing FLAC__stream_decoder_set_metadata_ignore_all()... "); + if(!FLAC__stream_decoder_set_metadata_ignore_all(decoder)) + return die_s_("returned false", decoder); + printf("OK\n"); + + num_expected_ = 0; + + if(!stream_decoder_test_respond_(decoder, &decoder_client_data, is_ogg)) + return false; + + /* + * respond all, ignore VORBIS_COMMENT + */ + + printf("testing FLAC__stream_decoder_set_metadata_respond_all()... "); + if(!FLAC__stream_decoder_set_metadata_respond_all(decoder)) + return die_s_("returned false", decoder); + printf("OK\n"); + + printf("testing FLAC__stream_decoder_set_metadata_ignore(VORBIS_COMMENT)... "); + if(!FLAC__stream_decoder_set_metadata_ignore(decoder, FLAC__METADATA_TYPE_VORBIS_COMMENT)) + return die_s_("returned false", decoder); + printf("OK\n"); + + num_expected_ = 0; + expected_metadata_sequence_[num_expected_++] = &streaminfo_; + expected_metadata_sequence_[num_expected_++] = &padding_; + expected_metadata_sequence_[num_expected_++] = &seektable_; + expected_metadata_sequence_[num_expected_++] = &application1_; + expected_metadata_sequence_[num_expected_++] = &application2_; + expected_metadata_sequence_[num_expected_++] = &cuesheet_; + expected_metadata_sequence_[num_expected_++] = &picture_; + expected_metadata_sequence_[num_expected_++] = &unknown_; + + if(!stream_decoder_test_respond_(decoder, &decoder_client_data, is_ogg)) + return false; + + /* + * respond all, ignore APPLICATION + */ + + printf("testing FLAC__stream_decoder_set_metadata_respond_all()... "); + if(!FLAC__stream_decoder_set_metadata_respond_all(decoder)) + return die_s_("returned false", decoder); + printf("OK\n"); + + printf("testing FLAC__stream_decoder_set_metadata_ignore(APPLICATION)... "); + if(!FLAC__stream_decoder_set_metadata_ignore(decoder, FLAC__METADATA_TYPE_APPLICATION)) + return die_s_("returned false", decoder); + printf("OK\n"); + + num_expected_ = 0; + if(is_ogg) { /* encoder moves vorbis comment after streaminfo according to ogg mapping */ + expected_metadata_sequence_[num_expected_++] = &streaminfo_; + expected_metadata_sequence_[num_expected_++] = &vorbiscomment_; + expected_metadata_sequence_[num_expected_++] = &padding_; + expected_metadata_sequence_[num_expected_++] = &seektable_; + expected_metadata_sequence_[num_expected_++] = &cuesheet_; + expected_metadata_sequence_[num_expected_++] = &picture_; + expected_metadata_sequence_[num_expected_++] = &unknown_; + } + else { + expected_metadata_sequence_[num_expected_++] = &streaminfo_; + expected_metadata_sequence_[num_expected_++] = &padding_; + expected_metadata_sequence_[num_expected_++] = &seektable_; + expected_metadata_sequence_[num_expected_++] = &vorbiscomment_; + expected_metadata_sequence_[num_expected_++] = &cuesheet_; + expected_metadata_sequence_[num_expected_++] = &picture_; + expected_metadata_sequence_[num_expected_++] = &unknown_; + } + + if(!stream_decoder_test_respond_(decoder, &decoder_client_data, is_ogg)) + return false; + + /* + * respond all, ignore APPLICATION id of app#1 + */ + + printf("testing FLAC__stream_decoder_set_metadata_respond_all()... "); + if(!FLAC__stream_decoder_set_metadata_respond_all(decoder)) + return die_s_("returned false", decoder); + printf("OK\n"); + + printf("testing FLAC__stream_decoder_set_metadata_ignore_application(of app block #1)... "); + if(!FLAC__stream_decoder_set_metadata_ignore_application(decoder, application1_.data.application.id)) + return die_s_("returned false", decoder); + printf("OK\n"); + + num_expected_ = 0; + if(is_ogg) { /* encoder moves vorbis comment after streaminfo according to ogg mapping */ + expected_metadata_sequence_[num_expected_++] = &streaminfo_; + expected_metadata_sequence_[num_expected_++] = &vorbiscomment_; + expected_metadata_sequence_[num_expected_++] = &padding_; + expected_metadata_sequence_[num_expected_++] = &seektable_; + expected_metadata_sequence_[num_expected_++] = &application2_; + expected_metadata_sequence_[num_expected_++] = &cuesheet_; + expected_metadata_sequence_[num_expected_++] = &picture_; + expected_metadata_sequence_[num_expected_++] = &unknown_; + } + else { + expected_metadata_sequence_[num_expected_++] = &streaminfo_; + expected_metadata_sequence_[num_expected_++] = &padding_; + expected_metadata_sequence_[num_expected_++] = &seektable_; + expected_metadata_sequence_[num_expected_++] = &application2_; + expected_metadata_sequence_[num_expected_++] = &vorbiscomment_; + expected_metadata_sequence_[num_expected_++] = &cuesheet_; + expected_metadata_sequence_[num_expected_++] = &picture_; + expected_metadata_sequence_[num_expected_++] = &unknown_; + } + + if(!stream_decoder_test_respond_(decoder, &decoder_client_data, is_ogg)) + return false; + + /* + * respond all, ignore APPLICATION id of app#1 & app#2 + */ + + printf("testing FLAC__stream_decoder_set_metadata_respond_all()... "); + if(!FLAC__stream_decoder_set_metadata_respond_all(decoder)) + return die_s_("returned false", decoder); + printf("OK\n"); + + printf("testing FLAC__stream_decoder_set_metadata_ignore_application(of app block #1)... "); + if(!FLAC__stream_decoder_set_metadata_ignore_application(decoder, application1_.data.application.id)) + return die_s_("returned false", decoder); + printf("OK\n"); + + printf("testing FLAC__stream_decoder_set_metadata_ignore_application(of app block #2)... "); + if(!FLAC__stream_decoder_set_metadata_ignore_application(decoder, application2_.data.application.id)) + return die_s_("returned false", decoder); + printf("OK\n"); + + num_expected_ = 0; + if(is_ogg) { /* encoder moves vorbis comment after streaminfo according to ogg mapping */ + expected_metadata_sequence_[num_expected_++] = &streaminfo_; + expected_metadata_sequence_[num_expected_++] = &vorbiscomment_; + expected_metadata_sequence_[num_expected_++] = &padding_; + expected_metadata_sequence_[num_expected_++] = &seektable_; + expected_metadata_sequence_[num_expected_++] = &cuesheet_; + expected_metadata_sequence_[num_expected_++] = &picture_; + expected_metadata_sequence_[num_expected_++] = &unknown_; + } + else { + expected_metadata_sequence_[num_expected_++] = &streaminfo_; + expected_metadata_sequence_[num_expected_++] = &padding_; + expected_metadata_sequence_[num_expected_++] = &seektable_; + expected_metadata_sequence_[num_expected_++] = &vorbiscomment_; + expected_metadata_sequence_[num_expected_++] = &cuesheet_; + expected_metadata_sequence_[num_expected_++] = &picture_; + expected_metadata_sequence_[num_expected_++] = &unknown_; + } + + if(!stream_decoder_test_respond_(decoder, &decoder_client_data, is_ogg)) + return false; + + /* + * ignore all, respond VORBIS_COMMENT + */ + + printf("testing FLAC__stream_decoder_set_metadata_ignore_all()... "); + if(!FLAC__stream_decoder_set_metadata_ignore_all(decoder)) + return die_s_("returned false", decoder); + printf("OK\n"); + + printf("testing FLAC__stream_decoder_set_metadata_respond(VORBIS_COMMENT)... "); + if(!FLAC__stream_decoder_set_metadata_respond(decoder, FLAC__METADATA_TYPE_VORBIS_COMMENT)) + return die_s_("returned false", decoder); + printf("OK\n"); + + num_expected_ = 0; + expected_metadata_sequence_[num_expected_++] = &vorbiscomment_; + + if(!stream_decoder_test_respond_(decoder, &decoder_client_data, is_ogg)) + return false; + + /* + * ignore all, respond APPLICATION + */ + + printf("testing FLAC__stream_decoder_set_metadata_ignore_all()... "); + if(!FLAC__stream_decoder_set_metadata_ignore_all(decoder)) + return die_s_("returned false", decoder); + printf("OK\n"); + + printf("testing FLAC__stream_decoder_set_metadata_respond(APPLICATION)... "); + if(!FLAC__stream_decoder_set_metadata_respond(decoder, FLAC__METADATA_TYPE_APPLICATION)) + return die_s_("returned false", decoder); + printf("OK\n"); + + num_expected_ = 0; + expected_metadata_sequence_[num_expected_++] = &application1_; + expected_metadata_sequence_[num_expected_++] = &application2_; + + if(!stream_decoder_test_respond_(decoder, &decoder_client_data, is_ogg)) + return false; + + /* + * ignore all, respond APPLICATION id of app#1 + */ + + printf("testing FLAC__stream_decoder_set_metadata_ignore_all()... "); + if(!FLAC__stream_decoder_set_metadata_ignore_all(decoder)) + return die_s_("returned false", decoder); + printf("OK\n"); + + printf("testing FLAC__stream_decoder_set_metadata_respond_application(of app block #1)... "); + if(!FLAC__stream_decoder_set_metadata_respond_application(decoder, application1_.data.application.id)) + return die_s_("returned false", decoder); + printf("OK\n"); + + num_expected_ = 0; + expected_metadata_sequence_[num_expected_++] = &application1_; + + if(!stream_decoder_test_respond_(decoder, &decoder_client_data, is_ogg)) + return false; + + /* + * ignore all, respond APPLICATION id of app#1 & app#2 + */ + + printf("testing FLAC__stream_decoder_set_metadata_ignore_all()... "); + if(!FLAC__stream_decoder_set_metadata_ignore_all(decoder)) + return die_s_("returned false", decoder); + printf("OK\n"); + + printf("testing FLAC__stream_decoder_set_metadata_respond_application(of app block #1)... "); + if(!FLAC__stream_decoder_set_metadata_respond_application(decoder, application1_.data.application.id)) + return die_s_("returned false", decoder); + printf("OK\n"); + + printf("testing FLAC__stream_decoder_set_metadata_respond_application(of app block #2)... "); + if(!FLAC__stream_decoder_set_metadata_respond_application(decoder, application2_.data.application.id)) + return die_s_("returned false", decoder); + printf("OK\n"); + + num_expected_ = 0; + expected_metadata_sequence_[num_expected_++] = &application1_; + expected_metadata_sequence_[num_expected_++] = &application2_; + + if(!stream_decoder_test_respond_(decoder, &decoder_client_data, is_ogg)) + return false; + + /* + * respond all, ignore APPLICATION, respond APPLICATION id of app#1 + */ + + printf("testing FLAC__stream_decoder_set_metadata_respond_all()... "); + if(!FLAC__stream_decoder_set_metadata_respond_all(decoder)) + return die_s_("returned false", decoder); + printf("OK\n"); + + printf("testing FLAC__stream_decoder_set_metadata_ignore(APPLICATION)... "); + if(!FLAC__stream_decoder_set_metadata_ignore(decoder, FLAC__METADATA_TYPE_APPLICATION)) + return die_s_("returned false", decoder); + printf("OK\n"); + + printf("testing FLAC__stream_decoder_set_metadata_respond_application(of app block #1)... "); + if(!FLAC__stream_decoder_set_metadata_respond_application(decoder, application1_.data.application.id)) + return die_s_("returned false", decoder); + printf("OK\n"); + + num_expected_ = 0; + if(is_ogg) { /* encoder moves vorbis comment after streaminfo according to ogg mapping */ + expected_metadata_sequence_[num_expected_++] = &streaminfo_; + expected_metadata_sequence_[num_expected_++] = &vorbiscomment_; + expected_metadata_sequence_[num_expected_++] = &padding_; + expected_metadata_sequence_[num_expected_++] = &seektable_; + expected_metadata_sequence_[num_expected_++] = &application1_; + expected_metadata_sequence_[num_expected_++] = &cuesheet_; + expected_metadata_sequence_[num_expected_++] = &picture_; + expected_metadata_sequence_[num_expected_++] = &unknown_; + } + else { + expected_metadata_sequence_[num_expected_++] = &streaminfo_; + expected_metadata_sequence_[num_expected_++] = &padding_; + expected_metadata_sequence_[num_expected_++] = &seektable_; + expected_metadata_sequence_[num_expected_++] = &application1_; + expected_metadata_sequence_[num_expected_++] = &vorbiscomment_; + expected_metadata_sequence_[num_expected_++] = &cuesheet_; + expected_metadata_sequence_[num_expected_++] = &picture_; + expected_metadata_sequence_[num_expected_++] = &unknown_; + } + + if(!stream_decoder_test_respond_(decoder, &decoder_client_data, is_ogg)) + return false; + + /* + * ignore all, respond APPLICATION, ignore APPLICATION id of app#1 + */ + + printf("testing FLAC__stream_decoder_set_metadata_ignore_all()... "); + if(!FLAC__stream_decoder_set_metadata_ignore_all(decoder)) + return die_s_("returned false", decoder); + printf("OK\n"); + + printf("testing FLAC__stream_decoder_set_metadata_respond(APPLICATION)... "); + if(!FLAC__stream_decoder_set_metadata_respond(decoder, FLAC__METADATA_TYPE_APPLICATION)) + return die_s_("returned false", decoder); + printf("OK\n"); + + printf("testing FLAC__stream_decoder_set_metadata_ignore_application(of app block #1)... "); + if(!FLAC__stream_decoder_set_metadata_ignore_application(decoder, application1_.data.application.id)) + return die_s_("returned false", decoder); + printf("OK\n"); + + num_expected_ = 0; + expected_metadata_sequence_[num_expected_++] = &application2_; + + if(!stream_decoder_test_respond_(decoder, &decoder_client_data, is_ogg)) + return false; + + if(layer < LAYER_FILE) /* for LAYER_FILE, FLAC__stream_decoder_finish() closes the file */ + fclose(decoder_client_data.file); + + printf("testing FLAC__stream_decoder_delete()... "); + FLAC__stream_decoder_delete(decoder); + printf("OK\n"); + + printf("\nPASSED!\n"); + + return true; +} + +FLAC__bool test_decoders(void) +{ + FLAC__bool is_ogg = false; + + while(1) { + init_metadata_blocks_(); + + if(!generate_file_(is_ogg)) + return false; + + if(!test_stream_decoder(LAYER_STREAM, is_ogg)) + return false; + + if(!test_stream_decoder(LAYER_SEEKABLE_STREAM, is_ogg)) + return false; + + if(!test_stream_decoder(LAYER_FILE, is_ogg)) + return false; + + if(!test_stream_decoder(LAYER_FILENAME, is_ogg)) + return false; + + (void) grabbag__file_remove_file(flacfilename(is_ogg)); + + free_metadata_blocks_(); + + if(!FLAC_API_SUPPORTS_OGG_FLAC || is_ogg) + break; + is_ogg = true; + } + + return true; +} diff --git a/src/FLAC/src/test_libFLAC/decoders.h b/src/FLAC/src/test_libFLAC/decoders.h new file mode 100644 index 00000000..11df1062 --- /dev/null +++ b/src/FLAC/src/test_libFLAC/decoders.h @@ -0,0 +1,26 @@ +/* test_libFLAC - Unit tester for libFLAC + * Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef FLAC__TEST_LIBFLAC_DECODERS_H +#define FLAC__TEST_LIBFLAC_DECODERS_H + +#include "FLAC/ordinals.h" + +FLAC__bool test_decoders(void); + +#endif diff --git a/src/FLAC/src/test_libFLAC/encoders.c b/src/FLAC/src/test_libFLAC/encoders.c new file mode 100644 index 00000000..c1de7ba3 --- /dev/null +++ b/src/FLAC/src/test_libFLAC/encoders.c @@ -0,0 +1,521 @@ +/* test_libFLAC - Unit tester for libFLAC + * Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include "encoders.h" +#include "FLAC/assert.h" +#include "FLAC/stream_encoder.h" +#include "share/grabbag.h" +#include "test_libs_common/file_utils_flac.h" +#include "test_libs_common/metadata_utils.h" + +typedef enum { + LAYER_STREAM = 0, /* FLAC__stream_encoder_init_[ogg_]stream() without seeking */ + LAYER_SEEKABLE_STREAM, /* FLAC__stream_encoder_init_[ogg_]stream() with seeking */ + LAYER_FILE, /* FLAC__stream_encoder_init_[ogg_]FILE() */ + LAYER_FILENAME /* FLAC__stream_encoder_init_[ogg_]file() */ +} Layer; + +static const char * const LayerString[] = { + "Stream", + "Seekable Stream", + "FILE*", + "Filename" +}; + +static FLAC__StreamMetadata streaminfo_, padding_, seektable_, application1_, application2_, vorbiscomment_, cuesheet_, picture_, unknown_; +static FLAC__StreamMetadata *metadata_sequence_[] = { &vorbiscomment_, &padding_, &seektable_, &application1_, &application2_, &cuesheet_, &picture_, &unknown_ }; +static const unsigned num_metadata_ = sizeof(metadata_sequence_) / sizeof(metadata_sequence_[0]); + +static const char *flacfilename(FLAC__bool is_ogg) +{ + return is_ogg? "metadata.ogg" : "metadata.flac"; +} + +static FLAC__bool die_(const char *msg) +{ + printf("ERROR: %s\n", msg); + return false; +} + +static FLAC__bool die_s_(const char *msg, const FLAC__StreamEncoder *encoder) +{ + FLAC__StreamEncoderState state = FLAC__stream_encoder_get_state(encoder); + + if(msg) + printf("FAILED, %s", msg); + else + printf("FAILED"); + + printf(", state = %u (%s)\n", (unsigned)state, FLAC__StreamEncoderStateString[state]); + if(state == FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR) { + FLAC__StreamDecoderState dstate = FLAC__stream_encoder_get_verify_decoder_state(encoder); + printf(" verify decoder state = %u (%s)\n", (unsigned)dstate, FLAC__StreamDecoderStateString[dstate]); + } + + return false; +} + +static void init_metadata_blocks_(void) +{ + mutils__init_metadata_blocks(&streaminfo_, &padding_, &seektable_, &application1_, &application2_, &vorbiscomment_, &cuesheet_, &picture_, &unknown_); +} + +static void free_metadata_blocks_(void) +{ + mutils__free_metadata_blocks(&streaminfo_, &padding_, &seektable_, &application1_, &application2_, &vorbiscomment_, &cuesheet_, &picture_, &unknown_); +} + +static FLAC__StreamEncoderReadStatus stream_encoder_read_callback_(const FLAC__StreamEncoder *encoder, FLAC__byte buffer[], size_t *bytes, void *client_data) +{ + FILE *f = (FILE*)client_data; + (void)encoder; + if(*bytes > 0) { + *bytes = fread(buffer, sizeof(FLAC__byte), *bytes, f); + if(ferror(f)) + return FLAC__STREAM_ENCODER_READ_STATUS_ABORT; + else if(*bytes == 0) + return FLAC__STREAM_ENCODER_READ_STATUS_END_OF_STREAM; + else + return FLAC__STREAM_ENCODER_READ_STATUS_CONTINUE; + } + else + return FLAC__STREAM_ENCODER_READ_STATUS_ABORT; +} + +static FLAC__StreamEncoderWriteStatus stream_encoder_write_callback_(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame, void *client_data) +{ + FILE *f = (FILE*)client_data; + (void)encoder, (void)samples, (void)current_frame; + if(fwrite(buffer, 1, bytes, f) != bytes) + return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR; + else + return FLAC__STREAM_ENCODER_WRITE_STATUS_OK; +} + +static FLAC__StreamEncoderSeekStatus stream_encoder_seek_callback_(const FLAC__StreamEncoder *encoder, FLAC__uint64 absolute_byte_offset, void *client_data) +{ + FILE *f = (FILE*)client_data; + (void)encoder; + if(fseek(f, (long)absolute_byte_offset, SEEK_SET) < 0) + return FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR; + else + return FLAC__STREAM_ENCODER_SEEK_STATUS_OK; +} + +static FLAC__StreamEncoderTellStatus stream_encoder_tell_callback_(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_byte_offset, void *client_data) +{ + FILE *f = (FILE*)client_data; + long pos; + (void)encoder; + if((pos = ftell(f)) < 0) + return FLAC__STREAM_ENCODER_TELL_STATUS_ERROR; + else { + *absolute_byte_offset = (FLAC__uint64)pos; + return FLAC__STREAM_ENCODER_TELL_STATUS_OK; + } +} + +static void stream_encoder_metadata_callback_(const FLAC__StreamEncoder *encoder, const FLAC__StreamMetadata *metadata, void *client_data) +{ + (void)encoder, (void)metadata, (void)client_data; +} + +static void stream_encoder_progress_callback_(const FLAC__StreamEncoder *encoder, FLAC__uint64 bytes_written, FLAC__uint64 samples_written, unsigned frames_written, unsigned total_frames_estimate, void *client_data) +{ + (void)encoder, (void)bytes_written, (void)samples_written, (void)frames_written, (void)total_frames_estimate, (void)client_data; +} + +static FLAC__bool test_stream_encoder(Layer layer, FLAC__bool is_ogg) +{ + FLAC__StreamEncoder *encoder; + FLAC__StreamEncoderInitStatus init_status; + FLAC__StreamEncoderState state; + FLAC__StreamDecoderState dstate; + FILE *file = 0; + FLAC__int32 samples[1024]; + FLAC__int32 *samples_array[1]; + unsigned i; + + samples_array[0] = samples; + + printf("\n+++ libFLAC unit test: FLAC__StreamEncoder (layer: %s, format: %s)\n\n", LayerString[layer], is_ogg? "Ogg FLAC":"FLAC"); + + printf("testing FLAC__stream_encoder_new()... "); + encoder = FLAC__stream_encoder_new(); + if(0 == encoder) { + printf("FAILED, returned NULL\n"); + return false; + } + printf("OK\n"); + + if(is_ogg) { + printf("testing FLAC__stream_encoder_set_ogg_serial_number()... "); + if(!FLAC__stream_encoder_set_ogg_serial_number(encoder, file_utils__ogg_serial_number)) + return die_s_("returned false", encoder); + printf("OK\n"); + } + + printf("testing FLAC__stream_encoder_set_verify()... "); + if(!FLAC__stream_encoder_set_verify(encoder, true)) + return die_s_("returned false", encoder); + printf("OK\n"); + + printf("testing FLAC__stream_encoder_set_streamable_subset()... "); + if(!FLAC__stream_encoder_set_streamable_subset(encoder, true)) + return die_s_("returned false", encoder); + printf("OK\n"); + + printf("testing FLAC__stream_encoder_set_channels()... "); + if(!FLAC__stream_encoder_set_channels(encoder, streaminfo_.data.stream_info.channels)) + return die_s_("returned false", encoder); + printf("OK\n"); + + printf("testing FLAC__stream_encoder_set_bits_per_sample()... "); + if(!FLAC__stream_encoder_set_bits_per_sample(encoder, streaminfo_.data.stream_info.bits_per_sample)) + return die_s_("returned false", encoder); + printf("OK\n"); + + printf("testing FLAC__stream_encoder_set_sample_rate()... "); + if(!FLAC__stream_encoder_set_sample_rate(encoder, streaminfo_.data.stream_info.sample_rate)) + return die_s_("returned false", encoder); + printf("OK\n"); + + printf("testing FLAC__stream_encoder_set_compression_level()... "); + if(!FLAC__stream_encoder_set_compression_level(encoder, (unsigned)(-1))) + return die_s_("returned false", encoder); + printf("OK\n"); + + printf("testing FLAC__stream_encoder_set_blocksize()... "); + if(!FLAC__stream_encoder_set_blocksize(encoder, streaminfo_.data.stream_info.min_blocksize)) + return die_s_("returned false", encoder); + printf("OK\n"); + + printf("testing FLAC__stream_encoder_set_do_mid_side_stereo()... "); + if(!FLAC__stream_encoder_set_do_mid_side_stereo(encoder, false)) + return die_s_("returned false", encoder); + printf("OK\n"); + + printf("testing FLAC__stream_encoder_set_loose_mid_side_stereo()... "); + if(!FLAC__stream_encoder_set_loose_mid_side_stereo(encoder, false)) + return die_s_("returned false", encoder); + printf("OK\n"); + + printf("testing FLAC__stream_encoder_set_max_lpc_order()... "); + if(!FLAC__stream_encoder_set_max_lpc_order(encoder, 0)) + return die_s_("returned false", encoder); + printf("OK\n"); + + printf("testing FLAC__stream_encoder_set_qlp_coeff_precision()... "); + if(!FLAC__stream_encoder_set_qlp_coeff_precision(encoder, 0)) + return die_s_("returned false", encoder); + printf("OK\n"); + + printf("testing FLAC__stream_encoder_set_do_qlp_coeff_prec_search()... "); + if(!FLAC__stream_encoder_set_do_qlp_coeff_prec_search(encoder, false)) + return die_s_("returned false", encoder); + printf("OK\n"); + + printf("testing FLAC__stream_encoder_set_do_escape_coding()... "); + if(!FLAC__stream_encoder_set_do_escape_coding(encoder, false)) + return die_s_("returned false", encoder); + printf("OK\n"); + + printf("testing FLAC__stream_encoder_set_do_exhaustive_model_search()... "); + if(!FLAC__stream_encoder_set_do_exhaustive_model_search(encoder, false)) + return die_s_("returned false", encoder); + printf("OK\n"); + + printf("testing FLAC__stream_encoder_set_min_residual_partition_order()... "); + if(!FLAC__stream_encoder_set_min_residual_partition_order(encoder, 0)) + return die_s_("returned false", encoder); + printf("OK\n"); + + printf("testing FLAC__stream_encoder_set_max_residual_partition_order()... "); + if(!FLAC__stream_encoder_set_max_residual_partition_order(encoder, 0)) + return die_s_("returned false", encoder); + printf("OK\n"); + + printf("testing FLAC__stream_encoder_set_rice_parameter_search_dist()... "); + if(!FLAC__stream_encoder_set_rice_parameter_search_dist(encoder, 0)) + return die_s_("returned false", encoder); + printf("OK\n"); + + printf("testing FLAC__stream_encoder_set_total_samples_estimate()... "); + if(!FLAC__stream_encoder_set_total_samples_estimate(encoder, streaminfo_.data.stream_info.total_samples)) + return die_s_("returned false", encoder); + printf("OK\n"); + + printf("testing FLAC__stream_encoder_set_metadata()... "); + if(!FLAC__stream_encoder_set_metadata(encoder, metadata_sequence_, num_metadata_)) + return die_s_("returned false", encoder); + printf("OK\n"); + + if(layer < LAYER_FILENAME) { + printf("opening file for FLAC output... "); + file = fopen(flacfilename(is_ogg), "w+b"); + if(0 == file) { + printf("ERROR (%s)\n", strerror(errno)); + return false; + } + printf("OK\n"); + } + + switch(layer) { + case LAYER_STREAM: + printf("testing FLAC__stream_encoder_init_%sstream()... ", is_ogg? "ogg_":""); + init_status = is_ogg? + FLAC__stream_encoder_init_ogg_stream(encoder, /*read_callback=*/0, stream_encoder_write_callback_, /*seek_callback=*/0, /*tell_callback=*/0, stream_encoder_metadata_callback_, /*client_data=*/file) : + FLAC__stream_encoder_init_stream(encoder, stream_encoder_write_callback_, /*seek_callback=*/0, /*tell_callback=*/0, stream_encoder_metadata_callback_, /*client_data=*/file); + break; + case LAYER_SEEKABLE_STREAM: + printf("testing FLAC__stream_encoder_init_%sstream()... ", is_ogg? "ogg_":""); + init_status = is_ogg? + FLAC__stream_encoder_init_ogg_stream(encoder, stream_encoder_read_callback_, stream_encoder_write_callback_, stream_encoder_seek_callback_, stream_encoder_tell_callback_, /*metadata_callback=*/0, /*client_data=*/file) : + FLAC__stream_encoder_init_stream(encoder, stream_encoder_write_callback_, stream_encoder_seek_callback_, stream_encoder_tell_callback_, /*metadata_callback=*/0, /*client_data=*/file); + break; + case LAYER_FILE: + printf("testing FLAC__stream_encoder_init_%sFILE()... ", is_ogg? "ogg_":""); + init_status = is_ogg? + FLAC__stream_encoder_init_ogg_FILE(encoder, file, stream_encoder_progress_callback_, /*client_data=*/0) : + FLAC__stream_encoder_init_FILE(encoder, file, stream_encoder_progress_callback_, /*client_data=*/0); + break; + case LAYER_FILENAME: + printf("testing FLAC__stream_encoder_init_%sfile()... ", is_ogg? "ogg_":""); + init_status = is_ogg? + FLAC__stream_encoder_init_ogg_file(encoder, flacfilename(is_ogg), stream_encoder_progress_callback_, /*client_data=*/0) : + FLAC__stream_encoder_init_file(encoder, flacfilename(is_ogg), stream_encoder_progress_callback_, /*client_data=*/0); + break; + default: + die_("internal error 001"); + return false; + } + if(init_status != FLAC__STREAM_ENCODER_INIT_STATUS_OK) + return die_s_(0, encoder); + printf("OK\n"); + + printf("testing FLAC__stream_encoder_get_state()... "); + state = FLAC__stream_encoder_get_state(encoder); + printf("returned state = %u (%s)... OK\n", (unsigned)state, FLAC__StreamEncoderStateString[state]); + + printf("testing FLAC__stream_encoder_get_verify_decoder_state()... "); + dstate = FLAC__stream_encoder_get_verify_decoder_state(encoder); + printf("returned state = %u (%s)... OK\n", (unsigned)dstate, FLAC__StreamDecoderStateString[dstate]); + + { + FLAC__uint64 absolute_sample; + unsigned frame_number; + unsigned channel; + unsigned sample; + FLAC__int32 expected; + FLAC__int32 got; + + printf("testing FLAC__stream_encoder_get_verify_decoder_error_stats()... "); + FLAC__stream_encoder_get_verify_decoder_error_stats(encoder, &absolute_sample, &frame_number, &channel, &sample, &expected, &got); + printf("OK\n"); + } + + printf("testing FLAC__stream_encoder_get_verify()... "); + if(FLAC__stream_encoder_get_verify(encoder) != true) { + printf("FAILED, expected true, got false\n"); + return false; + } + printf("OK\n"); + + printf("testing FLAC__stream_encoder_get_streamable_subset()... "); + if(FLAC__stream_encoder_get_streamable_subset(encoder) != true) { + printf("FAILED, expected true, got false\n"); + return false; + } + printf("OK\n"); + + printf("testing FLAC__stream_encoder_get_do_mid_side_stereo()... "); + if(FLAC__stream_encoder_get_do_mid_side_stereo(encoder) != false) { + printf("FAILED, expected false, got true\n"); + return false; + } + printf("OK\n"); + + printf("testing FLAC__stream_encoder_get_loose_mid_side_stereo()... "); + if(FLAC__stream_encoder_get_loose_mid_side_stereo(encoder) != false) { + printf("FAILED, expected false, got true\n"); + return false; + } + printf("OK\n"); + + printf("testing FLAC__stream_encoder_get_channels()... "); + if(FLAC__stream_encoder_get_channels(encoder) != streaminfo_.data.stream_info.channels) { + printf("FAILED, expected %u, got %u\n", streaminfo_.data.stream_info.channels, FLAC__stream_encoder_get_channels(encoder)); + return false; + } + printf("OK\n"); + + printf("testing FLAC__stream_encoder_get_bits_per_sample()... "); + if(FLAC__stream_encoder_get_bits_per_sample(encoder) != streaminfo_.data.stream_info.bits_per_sample) { + printf("FAILED, expected %u, got %u\n", streaminfo_.data.stream_info.bits_per_sample, FLAC__stream_encoder_get_bits_per_sample(encoder)); + return false; + } + printf("OK\n"); + + printf("testing FLAC__stream_encoder_get_sample_rate()... "); + if(FLAC__stream_encoder_get_sample_rate(encoder) != streaminfo_.data.stream_info.sample_rate) { + printf("FAILED, expected %u, got %u\n", streaminfo_.data.stream_info.sample_rate, FLAC__stream_encoder_get_sample_rate(encoder)); + return false; + } + printf("OK\n"); + + printf("testing FLAC__stream_encoder_get_blocksize()... "); + if(FLAC__stream_encoder_get_blocksize(encoder) != streaminfo_.data.stream_info.min_blocksize) { + printf("FAILED, expected %u, got %u\n", streaminfo_.data.stream_info.min_blocksize, FLAC__stream_encoder_get_blocksize(encoder)); + return false; + } + printf("OK\n"); + + printf("testing FLAC__stream_encoder_get_max_lpc_order()... "); + if(FLAC__stream_encoder_get_max_lpc_order(encoder) != 0) { + printf("FAILED, expected %u, got %u\n", 0, FLAC__stream_encoder_get_max_lpc_order(encoder)); + return false; + } + printf("OK\n"); + + printf("testing FLAC__stream_encoder_get_qlp_coeff_precision()... "); + (void)FLAC__stream_encoder_get_qlp_coeff_precision(encoder); + /* we asked the encoder to auto select this so we accept anything */ + printf("OK\n"); + + printf("testing FLAC__stream_encoder_get_do_qlp_coeff_prec_search()... "); + if(FLAC__stream_encoder_get_do_qlp_coeff_prec_search(encoder) != false) { + printf("FAILED, expected false, got true\n"); + return false; + } + printf("OK\n"); + + printf("testing FLAC__stream_encoder_get_do_escape_coding()... "); + if(FLAC__stream_encoder_get_do_escape_coding(encoder) != false) { + printf("FAILED, expected false, got true\n"); + return false; + } + printf("OK\n"); + + printf("testing FLAC__stream_encoder_get_do_exhaustive_model_search()... "); + if(FLAC__stream_encoder_get_do_exhaustive_model_search(encoder) != false) { + printf("FAILED, expected false, got true\n"); + return false; + } + printf("OK\n"); + + printf("testing FLAC__stream_encoder_get_min_residual_partition_order()... "); + if(FLAC__stream_encoder_get_min_residual_partition_order(encoder) != 0) { + printf("FAILED, expected %u, got %u\n", 0, FLAC__stream_encoder_get_min_residual_partition_order(encoder)); + return false; + } + printf("OK\n"); + + printf("testing FLAC__stream_encoder_get_max_residual_partition_order()... "); + if(FLAC__stream_encoder_get_max_residual_partition_order(encoder) != 0) { + printf("FAILED, expected %u, got %u\n", 0, FLAC__stream_encoder_get_max_residual_partition_order(encoder)); + return false; + } + printf("OK\n"); + + printf("testing FLAC__stream_encoder_get_rice_parameter_search_dist()... "); + if(FLAC__stream_encoder_get_rice_parameter_search_dist(encoder) != 0) { + printf("FAILED, expected %u, got %u\n", 0, FLAC__stream_encoder_get_rice_parameter_search_dist(encoder)); + return false; + } + printf("OK\n"); + + printf("testing FLAC__stream_encoder_get_total_samples_estimate()... "); + if(FLAC__stream_encoder_get_total_samples_estimate(encoder) != streaminfo_.data.stream_info.total_samples) { +#ifdef _MSC_VER + printf("FAILED, expected %I64u, got %I64u\n", streaminfo_.data.stream_info.total_samples, FLAC__stream_encoder_get_total_samples_estimate(encoder)); +#else + printf("FAILED, expected %llu, got %llu\n", (unsigned long long)streaminfo_.data.stream_info.total_samples, (unsigned long long)FLAC__stream_encoder_get_total_samples_estimate(encoder)); +#endif + return false; + } + printf("OK\n"); + + /* init the dummy sample buffer */ + for(i = 0; i < sizeof(samples) / sizeof(FLAC__int32); i++) + samples[i] = i & 7; + + printf("testing FLAC__stream_encoder_process()... "); + if(!FLAC__stream_encoder_process(encoder, (const FLAC__int32 * const *)samples_array, sizeof(samples) / sizeof(FLAC__int32))) + return die_s_("returned false", encoder); + printf("OK\n"); + + printf("testing FLAC__stream_encoder_process_interleaved()... "); + if(!FLAC__stream_encoder_process_interleaved(encoder, samples, sizeof(samples) / sizeof(FLAC__int32))) + return die_s_("returned false", encoder); + printf("OK\n"); + + printf("testing FLAC__stream_encoder_finish()... "); + if(!FLAC__stream_encoder_finish(encoder)) + return die_s_("returned false", encoder); + printf("OK\n"); + + if(layer < LAYER_FILE) + fclose(file); + + printf("testing FLAC__stream_encoder_delete()... "); + FLAC__stream_encoder_delete(encoder); + printf("OK\n"); + + printf("\nPASSED!\n"); + + return true; +} + +FLAC__bool test_encoders(void) +{ + FLAC__bool is_ogg = false; + + while(1) { + init_metadata_blocks_(); + + if(!test_stream_encoder(LAYER_STREAM, is_ogg)) + return false; + + if(!test_stream_encoder(LAYER_SEEKABLE_STREAM, is_ogg)) + return false; + + if(!test_stream_encoder(LAYER_FILE, is_ogg)) + return false; + + if(!test_stream_encoder(LAYER_FILENAME, is_ogg)) + return false; + + (void) grabbag__file_remove_file(flacfilename(is_ogg)); + + free_metadata_blocks_(); + + if(!FLAC_API_SUPPORTS_OGG_FLAC || is_ogg) + break; + is_ogg = true; + } + + return true; +} diff --git a/src/FLAC/src/test_libFLAC/encoders.h b/src/FLAC/src/test_libFLAC/encoders.h new file mode 100644 index 00000000..86c36909 --- /dev/null +++ b/src/FLAC/src/test_libFLAC/encoders.h @@ -0,0 +1,26 @@ +/* test_libFLAC - Unit tester for libFLAC + * Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef FLAC__TEST_LIBFLAC_ENCODERS_H +#define FLAC__TEST_LIBFLAC_ENCODERS_H + +#include "FLAC/ordinals.h" + +FLAC__bool test_encoders(void); + +#endif diff --git a/src/FLAC/src/test_libFLAC/format.c b/src/FLAC/src/test_libFLAC/format.c new file mode 100644 index 00000000..7ac115d0 --- /dev/null +++ b/src/FLAC/src/test_libFLAC/format.c @@ -0,0 +1,256 @@ +/* test_libFLAC - Unit tester for libFLAC + * Copyright (C) 2004,2005,2006,2007 Josh Coalson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include "FLAC/assert.h" +#include "FLAC/format.h" +#include "format.h" +#include + +static const char *true_false_string_[2] = { "false", "true" }; + +static struct { + unsigned rate; + FLAC__bool valid; + FLAC__bool subset; +} SAMPLE_RATES[] = { + { 0 , false, false }, + { 1 , true , true }, + { 9 , true , true }, + { 10 , true , true }, + { 4000 , true , true }, + { 8000 , true , true }, + { 11025 , true , true }, + { 12000 , true , true }, + { 16000 , true , true }, + { 22050 , true , true }, + { 24000 , true , true }, + { 32000 , true , true }, + { 32768 , true , true }, + { 44100 , true , true }, + { 48000 , true , true }, + { 65000 , true , true }, + { 65535 , true , true }, + { 65536 , true , false }, + { 65540 , true , true }, + { 65550 , true , true }, + { 65555 , true , false }, + { 66000 , true , true }, + { 66001 , true , false }, + { 96000 , true , true }, + { 100000 , true , true }, + { 100001 , true , false }, + { 192000 , true , true }, + { 500000 , true , true }, + { 500001 , true , false }, + { 500010 , true , true }, + { 655349 , true , false }, + { 655350 , true , true }, + { 655351 , false, false }, + { 655360 , false, false }, + { 700000 , false, false }, + { 700010 , false, false }, + { 1000000, false, false }, + { 1100000, false, false } +}; + +static struct { + const char *string; + FLAC__bool valid; +} VCENTRY_NAMES[] = { + { "" , true }, + { "a" , true }, + { "=" , false }, + { "a=" , false }, + { "\x01", false }, + { "\x1f", false }, + { "\x7d", true }, + { "\x7e", false }, + { "\xff", false } +}; + +static struct { + unsigned length; + const FLAC__byte *string; + FLAC__bool valid; +} VCENTRY_VALUES[] = { + { 0, (const FLAC__byte*)"" , true }, + { 1, (const FLAC__byte*)"" , true }, + { 1, (const FLAC__byte*)"\x01" , true }, + { 1, (const FLAC__byte*)"\x7f" , true }, + { 1, (const FLAC__byte*)"\x80" , false }, + { 1, (const FLAC__byte*)"\x81" , false }, + { 1, (const FLAC__byte*)"\xc0" , false }, + { 1, (const FLAC__byte*)"\xe0" , false }, + { 1, (const FLAC__byte*)"\xf0" , false }, + { 2, (const FLAC__byte*)"\xc0\x41" , false }, + { 2, (const FLAC__byte*)"\xc1\x41" , false }, + { 2, (const FLAC__byte*)"\xc0\x85" , false }, /* non-shortest form */ + { 2, (const FLAC__byte*)"\xc1\x85" , false }, /* non-shortest form */ + { 2, (const FLAC__byte*)"\xc2\x85" , true }, + { 2, (const FLAC__byte*)"\xe0\x41" , false }, + { 2, (const FLAC__byte*)"\xe1\x41" , false }, + { 2, (const FLAC__byte*)"\xe0\x85" , false }, + { 2, (const FLAC__byte*)"\xe1\x85" , false }, + { 3, (const FLAC__byte*)"\xe0\x85\x41", false }, + { 3, (const FLAC__byte*)"\xe1\x85\x41", false }, + { 3, (const FLAC__byte*)"\xe0\x85\x80", false }, /* non-shortest form */ + { 3, (const FLAC__byte*)"\xe0\x95\x80", false }, /* non-shortest form */ + { 3, (const FLAC__byte*)"\xe0\xa5\x80", true }, + { 3, (const FLAC__byte*)"\xe1\x85\x80", true }, + { 3, (const FLAC__byte*)"\xe1\x95\x80", true }, + { 3, (const FLAC__byte*)"\xe1\xa5\x80", true } +}; + +static struct { + const FLAC__byte *string; + FLAC__bool valid; +} VCENTRY_VALUES_NT[] = { + { (const FLAC__byte*)"" , true }, + { (const FLAC__byte*)"\x01" , true }, + { (const FLAC__byte*)"\x7f" , true }, + { (const FLAC__byte*)"\x80" , false }, + { (const FLAC__byte*)"\x81" , false }, + { (const FLAC__byte*)"\xc0" , false }, + { (const FLAC__byte*)"\xe0" , false }, + { (const FLAC__byte*)"\xf0" , false }, + { (const FLAC__byte*)"\xc0\x41" , false }, + { (const FLAC__byte*)"\xc1\x41" , false }, + { (const FLAC__byte*)"\xc0\x85" , false }, /* non-shortest form */ + { (const FLAC__byte*)"\xc1\x85" , false }, /* non-shortest form */ + { (const FLAC__byte*)"\xc2\x85" , true }, + { (const FLAC__byte*)"\xe0\x41" , false }, + { (const FLAC__byte*)"\xe1\x41" , false }, + { (const FLAC__byte*)"\xe0\x85" , false }, + { (const FLAC__byte*)"\xe1\x85" , false }, + { (const FLAC__byte*)"\xe0\x85\x41", false }, + { (const FLAC__byte*)"\xe1\x85\x41", false }, + { (const FLAC__byte*)"\xe0\x85\x80", false }, /* non-shortest form */ + { (const FLAC__byte*)"\xe0\x95\x80", false }, /* non-shortest form */ + { (const FLAC__byte*)"\xe0\xa5\x80", true }, + { (const FLAC__byte*)"\xe1\x85\x80", true }, + { (const FLAC__byte*)"\xe1\x95\x80", true }, + { (const FLAC__byte*)"\xe1\xa5\x80", true } +}; + +static struct { + unsigned length; + const FLAC__byte *string; + FLAC__bool valid; +} VCENTRIES[] = { + { 0, (const FLAC__byte*)"" , false }, + { 1, (const FLAC__byte*)"a" , false }, + { 1, (const FLAC__byte*)"=" , true }, + { 2, (const FLAC__byte*)"a=" , true }, + { 2, (const FLAC__byte*)"\x01=" , false }, + { 2, (const FLAC__byte*)"\x1f=" , false }, + { 2, (const FLAC__byte*)"\x7d=" , true }, + { 2, (const FLAC__byte*)"\x7e=" , false }, + { 2, (const FLAC__byte*)"\xff=" , false }, + { 3, (const FLAC__byte*)"a=\x01" , true }, + { 3, (const FLAC__byte*)"a=\x7f" , true }, + { 3, (const FLAC__byte*)"a=\x80" , false }, + { 3, (const FLAC__byte*)"a=\x81" , false }, + { 3, (const FLAC__byte*)"a=\xc0" , false }, + { 3, (const FLAC__byte*)"a=\xe0" , false }, + { 3, (const FLAC__byte*)"a=\xf0" , false }, + { 4, (const FLAC__byte*)"a=\xc0\x41" , false }, + { 4, (const FLAC__byte*)"a=\xc1\x41" , false }, + { 4, (const FLAC__byte*)"a=\xc0\x85" , false }, /* non-shortest form */ + { 4, (const FLAC__byte*)"a=\xc1\x85" , false }, /* non-shortest form */ + { 4, (const FLAC__byte*)"a=\xc2\x85" , true }, + { 4, (const FLAC__byte*)"a=\xe0\x41" , false }, + { 4, (const FLAC__byte*)"a=\xe1\x41" , false }, + { 4, (const FLAC__byte*)"a=\xe0\x85" , false }, + { 4, (const FLAC__byte*)"a=\xe1\x85" , false }, + { 5, (const FLAC__byte*)"a=\xe0\x85\x41", false }, + { 5, (const FLAC__byte*)"a=\xe1\x85\x41", false }, + { 5, (const FLAC__byte*)"a=\xe0\x85\x80", false }, /* non-shortest form */ + { 5, (const FLAC__byte*)"a=\xe0\x95\x80", false }, /* non-shortest form */ + { 5, (const FLAC__byte*)"a=\xe0\xa5\x80", true }, + { 5, (const FLAC__byte*)"a=\xe1\x85\x80", true }, + { 5, (const FLAC__byte*)"a=\xe1\x95\x80", true }, + { 5, (const FLAC__byte*)"a=\xe1\xa5\x80", true } +}; + +FLAC__bool test_format(void) +{ + unsigned i; + + printf("\n+++ libFLAC unit test: format\n\n"); + + for(i = 0; i < sizeof(SAMPLE_RATES)/sizeof(SAMPLE_RATES[0]); i++) { + printf("testing FLAC__format_sample_rate_is_valid(%u)... ", SAMPLE_RATES[i].rate); + if(FLAC__format_sample_rate_is_valid(SAMPLE_RATES[i].rate) != SAMPLE_RATES[i].valid) { + printf("FAILED, expected %s, got %s\n", true_false_string_[SAMPLE_RATES[i].valid], true_false_string_[!SAMPLE_RATES[i].valid]); + return false; + } + printf("OK\n"); + } + + for(i = 0; i < sizeof(SAMPLE_RATES)/sizeof(SAMPLE_RATES[0]); i++) { + printf("testing FLAC__format_sample_rate_is_subset(%u)... ", SAMPLE_RATES[i].rate); + if(FLAC__format_sample_rate_is_subset(SAMPLE_RATES[i].rate) != SAMPLE_RATES[i].subset) { + printf("FAILED, expected %s, got %s\n", true_false_string_[SAMPLE_RATES[i].subset], true_false_string_[!SAMPLE_RATES[i].subset]); + return false; + } + printf("OK\n"); + } + + for(i = 0; i < sizeof(VCENTRY_NAMES)/sizeof(VCENTRY_NAMES[0]); i++) { + printf("testing FLAC__format_vorbiscomment_entry_name_is_legal(\"%s\")... ", VCENTRY_NAMES[i].string); + if(FLAC__format_vorbiscomment_entry_name_is_legal(VCENTRY_NAMES[i].string) != VCENTRY_NAMES[i].valid) { + printf("FAILED, expected %s, got %s\n", true_false_string_[VCENTRY_NAMES[i].valid], true_false_string_[!VCENTRY_NAMES[i].valid]); + return false; + } + printf("OK\n"); + } + + for(i = 0; i < sizeof(VCENTRY_VALUES)/sizeof(VCENTRY_VALUES[0]); i++) { + printf("testing FLAC__format_vorbiscomment_entry_value_is_legal(\"%s\", %u)... ", VCENTRY_VALUES[i].string, VCENTRY_VALUES[i].length); + if(FLAC__format_vorbiscomment_entry_value_is_legal(VCENTRY_VALUES[i].string, VCENTRY_VALUES[i].length) != VCENTRY_VALUES[i].valid) { + printf("FAILED, expected %s, got %s\n", true_false_string_[VCENTRY_VALUES[i].valid], true_false_string_[!VCENTRY_VALUES[i].valid]); + return false; + } + printf("OK\n"); + } + + for(i = 0; i < sizeof(VCENTRY_VALUES_NT)/sizeof(VCENTRY_VALUES_NT[0]); i++) { + printf("testing FLAC__format_vorbiscomment_entry_value_is_legal(\"%s\", -1)... ", VCENTRY_VALUES_NT[i].string); + if(FLAC__format_vorbiscomment_entry_value_is_legal(VCENTRY_VALUES_NT[i].string, (unsigned)(-1)) != VCENTRY_VALUES_NT[i].valid) { + printf("FAILED, expected %s, got %s\n", true_false_string_[VCENTRY_VALUES_NT[i].valid], true_false_string_[!VCENTRY_VALUES_NT[i].valid]); + return false; + } + printf("OK\n"); + } + + for(i = 0; i < sizeof(VCENTRIES)/sizeof(VCENTRIES[0]); i++) { + printf("testing FLAC__format_vorbiscomment_entry_is_legal(\"%s\", %u)... ", VCENTRIES[i].string, VCENTRIES[i].length); + if(FLAC__format_vorbiscomment_entry_is_legal(VCENTRIES[i].string, VCENTRIES[i].length) != VCENTRIES[i].valid) { + printf("FAILED, expected %s, got %s\n", true_false_string_[VCENTRIES[i].valid], true_false_string_[!VCENTRIES[i].valid]); + return false; + } + printf("OK\n"); + } + + printf("\nPASSED!\n"); + return true; +} diff --git a/src/FLAC/src/test_libFLAC/format.h b/src/FLAC/src/test_libFLAC/format.h new file mode 100644 index 00000000..e48a0e09 --- /dev/null +++ b/src/FLAC/src/test_libFLAC/format.h @@ -0,0 +1,26 @@ +/* test_libFLAC - Unit tester for libFLAC + * Copyright (C) 2004,2005,2006,2007 Josh Coalson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef FLAC__TEST_LIBFLAC_FORMAT_H +#define FLAC__TEST_LIBFLAC_FORMAT_H + +#include "FLAC/ordinals.h" + +FLAC__bool test_format(void); + +#endif diff --git a/src/FLAC/src/test_libFLAC/main.c b/src/FLAC/src/test_libFLAC/main.c new file mode 100644 index 00000000..1807ff58 --- /dev/null +++ b/src/FLAC/src/test_libFLAC/main.c @@ -0,0 +1,49 @@ +/* test_libFLAC - Unit tester for libFLAC + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include "bitwriter.h" +#include "decoders.h" +#include "encoders.h" +#include "format.h" +#include "metadata.h" + +int main(int argc, char *argv[]) +{ + (void)argc, (void)argv; + + if(!test_bitwriter()) + return 1; + + if(!test_format()) + return 1; + + if(!test_encoders()) + return 1; + + if(!test_decoders()) + return 1; + + if(!test_metadata()) + return 1; + + return 0; +} diff --git a/src/FLAC/src/test_libFLAC/metadata.c b/src/FLAC/src/test_libFLAC/metadata.c new file mode 100644 index 00000000..aacb6bfd --- /dev/null +++ b/src/FLAC/src/test_libFLAC/metadata.c @@ -0,0 +1,40 @@ +/* test_libFLAC - Unit tester for libFLAC + * Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include "metadata.h" +#include + +extern FLAC__bool test_metadata_object(void); +extern FLAC__bool test_metadata_file_manipulation(void); + +FLAC__bool test_metadata(void) +{ + if(!test_metadata_object()) + return false; + + if(!test_metadata_file_manipulation()) + return false; + + printf("\nPASSED!\n"); + + return true; +} diff --git a/src/FLAC/src/test_libFLAC/metadata.h b/src/FLAC/src/test_libFLAC/metadata.h new file mode 100644 index 00000000..5ebdea19 --- /dev/null +++ b/src/FLAC/src/test_libFLAC/metadata.h @@ -0,0 +1,28 @@ +/* test_libFLAC - Unit tester for libFLAC + * Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef FLAC__TEST_LIBFLAC_METADATA_H +#define FLAC__TEST_LIBFLAC_METADATA_H + +#include "FLAC/ordinals.h" + +FLAC__bool test_metadata(void); +FLAC__bool test_metadata_file_manipulation(void); +FLAC__bool test_metadata_object(void) ; + +#endif diff --git a/src/FLAC/src/test_libFLAC/metadata_manip.c b/src/FLAC/src/test_libFLAC/metadata_manip.c new file mode 100644 index 00000000..2647d37b --- /dev/null +++ b/src/FLAC/src/test_libFLAC/metadata_manip.c @@ -0,0 +1,2138 @@ +/* test_libFLAC - Unit tester for libFLAC + * Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include /* for malloc() */ +#include /* for memcpy()/memset() */ +#if defined _MSC_VER || defined __MINGW32__ +#include /* for utime() */ +#include /* for chmod() */ +#if _MSC_VER <= 1600 /* @@@ [2G limit] */ +#define fseeko fseek +#define ftello ftell +#endif +#else +#include /* some flavors of BSD (like OS X) require this to get time_t */ +#include /* for utime() */ +#include /* for chown(), unlink() */ +#endif +#include /* for stat(), maybe chmod() */ +#include "FLAC/assert.h" +#include "FLAC/stream_decoder.h" +#include "FLAC/metadata.h" +#include "share/grabbag.h" +#include "test_libs_common/file_utils_flac.h" +#include "test_libs_common/metadata_utils.h" +#include "metadata.h" + + +/****************************************************************************** + The general strategy of these tests (for interface levels 1 and 2) is + to create a dummy FLAC file with a known set of initial metadata + blocks, then keep a mirror locally of what we expect the metadata to be + after each operation. Then testing becomes a simple matter of running + a FLAC__StreamDecoder over the dummy file after each operation, comparing + the decoded metadata to what's in our local copy. If there are any + differences in the metadata, or the actual audio data is corrupted, we + will catch it while decoding. +******************************************************************************/ + +typedef struct { + FLAC__bool error_occurred; +} decoder_client_struct; + +typedef struct { + FLAC__StreamMetadata *blocks[64]; + unsigned num_blocks; +} our_metadata_struct; + +/* our copy of the metadata in flacfilename() */ +static our_metadata_struct our_metadata_; + +/* the current block number that corresponds to the position of the iterator we are testing */ +static unsigned mc_our_block_number_ = 0; + +static const char *flacfilename(FLAC__bool is_ogg) +{ + return is_ogg? "metadata.ogg" : "metadata.flac"; +} + +static FLAC__bool die_(const char *msg) +{ + printf("ERROR: %s\n", msg); + return false; +} + +static FLAC__bool die_c_(const char *msg, FLAC__Metadata_ChainStatus status) +{ + printf("ERROR: %s\n", msg); + printf(" status=%s\n", FLAC__Metadata_ChainStatusString[status]); + return false; +} + +static FLAC__bool die_ss_(const char *msg, FLAC__Metadata_SimpleIterator *iterator) +{ + printf("ERROR: %s\n", msg); + printf(" status=%s\n", FLAC__Metadata_SimpleIteratorStatusString[FLAC__metadata_simple_iterator_status(iterator)]); + return false; +} + +static void *malloc_or_die_(size_t size) +{ + void *x = malloc(size); + if(0 == x) { + fprintf(stderr, "ERROR: out of memory allocating %u bytes\n", (unsigned)size); + exit(1); + } + return x; +} + +static char *strdup_or_die_(const char *s) +{ + char *x = strdup(s); + if(0 == x) { + fprintf(stderr, "ERROR: out of memory copying string \"%s\"\n", s); + exit(1); + } + return x; +} + +/* functions for working with our metadata copy */ + +static FLAC__bool replace_in_our_metadata_(FLAC__StreamMetadata *block, unsigned position, FLAC__bool copy) +{ + unsigned i; + FLAC__StreamMetadata *obj = block; + FLAC__ASSERT(position < our_metadata_.num_blocks); + if(copy) { + if(0 == (obj = FLAC__metadata_object_clone(block))) + return die_("during FLAC__metadata_object_clone()"); + } + FLAC__metadata_object_delete(our_metadata_.blocks[position]); + our_metadata_.blocks[position] = obj; + + /* set the is_last flags */ + for(i = 0; i < our_metadata_.num_blocks - 1; i++) + our_metadata_.blocks[i]->is_last = false; + our_metadata_.blocks[i]->is_last = true; + + return true; +} + +static FLAC__bool insert_to_our_metadata_(FLAC__StreamMetadata *block, unsigned position, FLAC__bool copy) +{ + unsigned i; + FLAC__StreamMetadata *obj = block; + if(copy) { + if(0 == (obj = FLAC__metadata_object_clone(block))) + return die_("during FLAC__metadata_object_clone()"); + } + if(position > our_metadata_.num_blocks) { + position = our_metadata_.num_blocks; + } + else { + for(i = our_metadata_.num_blocks; i > position; i--) + our_metadata_.blocks[i] = our_metadata_.blocks[i-1]; + } + our_metadata_.blocks[position] = obj; + our_metadata_.num_blocks++; + + /* set the is_last flags */ + for(i = 0; i < our_metadata_.num_blocks - 1; i++) + our_metadata_.blocks[i]->is_last = false; + our_metadata_.blocks[i]->is_last = true; + + return true; +} + +static void delete_from_our_metadata_(unsigned position) +{ + unsigned i; + FLAC__ASSERT(position < our_metadata_.num_blocks); + FLAC__metadata_object_delete(our_metadata_.blocks[position]); + for(i = position; i < our_metadata_.num_blocks - 1; i++) + our_metadata_.blocks[i] = our_metadata_.blocks[i+1]; + our_metadata_.num_blocks--; + + /* set the is_last flags */ + if(our_metadata_.num_blocks > 0) { + for(i = 0; i < our_metadata_.num_blocks - 1; i++) + our_metadata_.blocks[i]->is_last = false; + our_metadata_.blocks[i]->is_last = true; + } +} + +/* + * This wad of functions supports filename- and callback-based chain reading/writing. + * Everything up to set_file_stats_() is copied from libFLAC/metadata_iterators.c + */ +static +FLAC__bool open_tempfile_(const char *filename, FILE **tempfile, char **tempfilename) +{ + static const char *tempfile_suffix = ".metadata_edit"; + + if(0 == (*tempfilename = (char*)malloc(strlen(filename) + strlen(tempfile_suffix) + 1))) + return false; + strcpy(*tempfilename, filename); + strcat(*tempfilename, tempfile_suffix); + + if(0 == (*tempfile = fopen(*tempfilename, "wb"))) + return false; + + return true; +} + +static +void cleanup_tempfile_(FILE **tempfile, char **tempfilename) +{ + if(0 != *tempfile) { + (void)fclose(*tempfile); + *tempfile = 0; + } + + if(0 != *tempfilename) { + (void)unlink(*tempfilename); + free(*tempfilename); + *tempfilename = 0; + } +} + +static +FLAC__bool transport_tempfile_(const char *filename, FILE **tempfile, char **tempfilename) +{ + FLAC__ASSERT(0 != filename); + FLAC__ASSERT(0 != tempfile); + FLAC__ASSERT(0 != tempfilename); + FLAC__ASSERT(0 != *tempfilename); + + if(0 != *tempfile) { + (void)fclose(*tempfile); + *tempfile = 0; + } + +#if defined _MSC_VER || defined __MINGW32__ || defined __EMX__ + /* on some flavors of windows, rename() will fail if the destination already exists */ + if(unlink(filename) < 0) { + cleanup_tempfile_(tempfile, tempfilename); + return false; + } +#endif + + if(0 != rename(*tempfilename, filename)) { + cleanup_tempfile_(tempfile, tempfilename); + return false; + } + + cleanup_tempfile_(tempfile, tempfilename); + + return true; +} + +static +FLAC__bool get_file_stats_(const char *filename, struct stat *stats) +{ + FLAC__ASSERT(0 != filename); + FLAC__ASSERT(0 != stats); + return (0 == stat(filename, stats)); +} + +static +void set_file_stats_(const char *filename, struct stat *stats) +{ + struct utimbuf srctime; + + FLAC__ASSERT(0 != filename); + FLAC__ASSERT(0 != stats); + + srctime.actime = stats->st_atime; + srctime.modtime = stats->st_mtime; + (void)chmod(filename, stats->st_mode); + (void)utime(filename, &srctime); +#if !defined _MSC_VER && !defined __MINGW32__ && !defined __EMX__ + (void)chown(filename, stats->st_uid, -1); + (void)chown(filename, -1, stats->st_gid); +#endif +} + +#ifdef FLAC__VALGRIND_TESTING +static size_t chain_write_cb_(const void *ptr, size_t size, size_t nmemb, FLAC__IOHandle handle) +{ + FILE *stream = (FILE*)handle; + size_t ret = fwrite(ptr, size, nmemb, stream); + if(!ferror(stream)) + fflush(stream); + return ret; +} +#endif + +static int chain_seek_cb_(FLAC__IOHandle handle, FLAC__int64 offset, int whence) +{ + off_t o = (off_t)offset; + FLAC__ASSERT(offset == o); + return fseeko((FILE*)handle, o, whence); +} + +static FLAC__int64 chain_tell_cb_(FLAC__IOHandle handle) +{ + return ftello((FILE*)handle); +} + +static int chain_eof_cb_(FLAC__IOHandle handle) +{ + return feof((FILE*)handle); +} + +static FLAC__bool write_chain_(FLAC__Metadata_Chain *chain, FLAC__bool use_padding, FLAC__bool preserve_file_stats, FLAC__bool filename_based, const char *filename) +{ + if(filename_based) + return FLAC__metadata_chain_write(chain, use_padding, preserve_file_stats); + else { + FLAC__IOCallbacks callbacks; + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.read = (FLAC__IOCallback_Read)fread; +#ifdef FLAC__VALGRIND_TESTING + callbacks.write = chain_write_cb_; +#else + callbacks.write = (FLAC__IOCallback_Write)fwrite; +#endif + callbacks.seek = chain_seek_cb_; + callbacks.eof = chain_eof_cb_; + + if(FLAC__metadata_chain_check_if_tempfile_needed(chain, use_padding)) { + struct stat stats; + FILE *file, *tempfile; + char *tempfilename; + if(preserve_file_stats) { + if(!get_file_stats_(filename, &stats)) + return false; + } + if(0 == (file = fopen(filename, "rb"))) + return false; /*@@@@ chain status still says OK though */ + if(!open_tempfile_(filename, &tempfile, &tempfilename)) { + fclose(file); + cleanup_tempfile_(&tempfile, &tempfilename); + return false; /*@@@@ chain status still says OK though */ + } + if(!FLAC__metadata_chain_write_with_callbacks_and_tempfile(chain, use_padding, (FLAC__IOHandle)file, callbacks, (FLAC__IOHandle)tempfile, callbacks)) { + fclose(file); + fclose(tempfile); + return false; + } + fclose(file); + fclose(tempfile); + file = tempfile = 0; + if(!transport_tempfile_(filename, &tempfile, &tempfilename)) + return false; + if(preserve_file_stats) + set_file_stats_(filename, &stats); + } + else { + FILE *file = fopen(filename, "r+b"); + if(0 == file) + return false; /*@@@@ chain status still says OK though */ + if(!FLAC__metadata_chain_write_with_callbacks(chain, use_padding, (FLAC__IOHandle)file, callbacks)) + return false; + fclose(file); + } + } + + return true; +} + +static FLAC__bool read_chain_(FLAC__Metadata_Chain *chain, const char *filename, FLAC__bool filename_based, FLAC__bool is_ogg) +{ + if(filename_based) + return is_ogg? + FLAC__metadata_chain_read_ogg(chain, flacfilename(is_ogg)) : + FLAC__metadata_chain_read(chain, flacfilename(is_ogg)) + ; + else { + FLAC__IOCallbacks callbacks; + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.read = (FLAC__IOCallback_Read)fread; + callbacks.seek = chain_seek_cb_; + callbacks.tell = chain_tell_cb_; + + { + FLAC__bool ret; + FILE *file = fopen(filename, "rb"); + if(0 == file) + return false; /*@@@@ chain status still says OK though */ + ret = is_ogg? + FLAC__metadata_chain_read_ogg_with_callbacks(chain, (FLAC__IOHandle)file, callbacks) : + FLAC__metadata_chain_read_with_callbacks(chain, (FLAC__IOHandle)file, callbacks) + ; + fclose(file); + return ret; + } + } +} + +/* function for comparing our metadata to a FLAC__Metadata_Chain */ + +static FLAC__bool compare_chain_(FLAC__Metadata_Chain *chain, unsigned current_position, FLAC__StreamMetadata *current_block) +{ + unsigned i; + FLAC__Metadata_Iterator *iterator; + FLAC__StreamMetadata *block; + FLAC__bool next_ok = true; + + FLAC__ASSERT(0 != chain); + + printf("\tcomparing chain... "); + fflush(stdout); + + if(0 == (iterator = FLAC__metadata_iterator_new())) + return die_("allocating memory for iterator"); + + FLAC__metadata_iterator_init(iterator, chain); + + i = 0; + do { + printf("%u... ", i); + fflush(stdout); + + if(0 == (block = FLAC__metadata_iterator_get_block(iterator))) { + FLAC__metadata_iterator_delete(iterator); + return die_("getting block from iterator"); + } + + if(!mutils__compare_block(our_metadata_.blocks[i], block)) { + FLAC__metadata_iterator_delete(iterator); + return die_("metadata block mismatch"); + } + + i++; + next_ok = FLAC__metadata_iterator_next(iterator); + } while(i < our_metadata_.num_blocks && next_ok); + + FLAC__metadata_iterator_delete(iterator); + + if(next_ok) + return die_("chain has more blocks than expected"); + + if(i < our_metadata_.num_blocks) + return die_("short block count in chain"); + + if(0 != current_block) { + printf("CURRENT_POSITION... "); + fflush(stdout); + + if(!mutils__compare_block(our_metadata_.blocks[current_position], current_block)) + return die_("metadata block mismatch"); + } + + printf("PASSED\n"); + + return true; +} + +/* decoder callbacks for checking the file */ + +static FLAC__StreamDecoderWriteStatus decoder_write_callback_(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data) +{ + (void)decoder, (void)buffer, (void)client_data; + + if( + (frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_FRAME_NUMBER && frame->header.number.frame_number == 0) || + (frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER && frame->header.number.sample_number == 0) + ) { + printf("content... "); + fflush(stdout); + } + + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; +} + +/* this version pays no attention to the metadata */ +static void decoder_metadata_callback_null_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) +{ + (void)decoder, (void)metadata, (void)client_data; + + printf("%d... ", mc_our_block_number_); + fflush(stdout); + + mc_our_block_number_++; +} + +/* this version is used when we want to compare to our metadata copy */ +static void decoder_metadata_callback_compare_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) +{ + decoder_client_struct *dcd = (decoder_client_struct*)client_data; + + (void)decoder; + + /* don't bother checking if we've already hit an error */ + if(dcd->error_occurred) + return; + + printf("%d... ", mc_our_block_number_); + fflush(stdout); + + if(mc_our_block_number_ >= our_metadata_.num_blocks) { + (void)die_("got more metadata blocks than expected"); + dcd->error_occurred = true; + } + else { + if(!mutils__compare_block(our_metadata_.blocks[mc_our_block_number_], metadata)) { + (void)die_("metadata block mismatch"); + dcd->error_occurred = true; + } + } + mc_our_block_number_++; +} + +static void decoder_error_callback_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) +{ + decoder_client_struct *dcd = (decoder_client_struct*)client_data; + (void)decoder; + + dcd->error_occurred = true; + printf("ERROR: got error callback, status = %s (%u)\n", FLAC__StreamDecoderErrorStatusString[status], (unsigned)status); +} + +static FLAC__bool generate_file_(FLAC__bool include_extras, FLAC__bool is_ogg) +{ + FLAC__StreamMetadata streaminfo, vorbiscomment, *cuesheet, picture, padding; + FLAC__StreamMetadata *metadata[4]; + unsigned i = 0, n = 0; + + printf("generating %sFLAC file for test\n", is_ogg? "Ogg " : ""); + + while(our_metadata_.num_blocks > 0) + delete_from_our_metadata_(0); + + streaminfo.is_last = false; + streaminfo.type = FLAC__METADATA_TYPE_STREAMINFO; + streaminfo.length = FLAC__STREAM_METADATA_STREAMINFO_LENGTH; + streaminfo.data.stream_info.min_blocksize = 576; + streaminfo.data.stream_info.max_blocksize = 576; + streaminfo.data.stream_info.min_framesize = 0; + streaminfo.data.stream_info.max_framesize = 0; + streaminfo.data.stream_info.sample_rate = 44100; + streaminfo.data.stream_info.channels = 1; + streaminfo.data.stream_info.bits_per_sample = 8; + streaminfo.data.stream_info.total_samples = 0; + memset(streaminfo.data.stream_info.md5sum, 0, 16); + + { + const unsigned vendor_string_length = (unsigned)strlen(FLAC__VENDOR_STRING); + vorbiscomment.is_last = false; + vorbiscomment.type = FLAC__METADATA_TYPE_VORBIS_COMMENT; + vorbiscomment.length = (4 + vendor_string_length) + 4; + vorbiscomment.data.vorbis_comment.vendor_string.length = vendor_string_length; + vorbiscomment.data.vorbis_comment.vendor_string.entry = malloc_or_die_(vendor_string_length+1); + memcpy(vorbiscomment.data.vorbis_comment.vendor_string.entry, FLAC__VENDOR_STRING, vendor_string_length+1); + vorbiscomment.data.vorbis_comment.num_comments = 0; + vorbiscomment.data.vorbis_comment.comments = 0; + } + + { + if (0 == (cuesheet = FLAC__metadata_object_new(FLAC__METADATA_TYPE_CUESHEET))) + return die_("priming our metadata"); + cuesheet->is_last = false; + strcpy(cuesheet->data.cue_sheet.media_catalog_number, "bogo-MCN"); + cuesheet->data.cue_sheet.lead_in = 123; + cuesheet->data.cue_sheet.is_cd = false; + if (!FLAC__metadata_object_cuesheet_insert_blank_track(cuesheet, 0)) + return die_("priming our metadata"); + cuesheet->data.cue_sheet.tracks[0].number = 1; + if (!FLAC__metadata_object_cuesheet_track_insert_blank_index(cuesheet, 0, 0)) + return die_("priming our metadata"); + } + + { + picture.is_last = false; + picture.type = FLAC__METADATA_TYPE_PICTURE; + picture.length = + ( + FLAC__STREAM_METADATA_PICTURE_TYPE_LEN + + FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN + /* will add the length for the string later */ + FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN + /* will add the length for the string later */ + FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN + + FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN + + FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN + + FLAC__STREAM_METADATA_PICTURE_COLORS_LEN + + FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN /* will add the length for the data later */ + ) / 8 + ; + picture.data.picture.type = FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER; + picture.data.picture.mime_type = strdup_or_die_("image/jpeg"); + picture.length += strlen(picture.data.picture.mime_type); + picture.data.picture.description = (FLAC__byte*)strdup_or_die_("desc"); + picture.length += strlen((const char *)picture.data.picture.description); + picture.data.picture.width = 300; + picture.data.picture.height = 300; + picture.data.picture.depth = 24; + picture.data.picture.colors = 0; + picture.data.picture.data = (FLAC__byte*)strdup_or_die_("SOMEJPEGDATA"); + picture.data.picture.data_length = strlen((const char *)picture.data.picture.data); + picture.length += picture.data.picture.data_length; + } + + padding.is_last = true; + padding.type = FLAC__METADATA_TYPE_PADDING; + padding.length = 1234; + + metadata[n++] = &vorbiscomment; + if(include_extras) { + metadata[n++] = cuesheet; + metadata[n++] = &picture; + } + metadata[n++] = &padding; + + if( + !insert_to_our_metadata_(&streaminfo, i++, /*copy=*/true) || + !insert_to_our_metadata_(&vorbiscomment, i++, /*copy=*/true) || + (include_extras && !insert_to_our_metadata_(cuesheet, i++, /*copy=*/false)) || + (include_extras && !insert_to_our_metadata_(&picture, i++, /*copy=*/true)) || + !insert_to_our_metadata_(&padding, i++, /*copy=*/true) + ) + return die_("priming our metadata"); + + if(!file_utils__generate_flacfile(is_ogg, flacfilename(is_ogg), 0, 512 * 1024, &streaminfo, metadata, n)) + return die_("creating the encoded file"); + + free(vorbiscomment.data.vorbis_comment.vendor_string.entry); + free(picture.data.picture.mime_type); + free(picture.data.picture.description); + free(picture.data.picture.data); + if(!include_extras) + FLAC__metadata_object_delete(cuesheet); + + return true; +} + +static FLAC__bool test_file_(FLAC__bool is_ogg, FLAC__StreamDecoderMetadataCallback metadata_callback) +{ + const char *filename = flacfilename(is_ogg); + FLAC__StreamDecoder *decoder; + decoder_client_struct decoder_client_data; + + FLAC__ASSERT(0 != metadata_callback); + + mc_our_block_number_ = 0; + decoder_client_data.error_occurred = false; + + printf("\ttesting '%s'... ", filename); + fflush(stdout); + + if(0 == (decoder = FLAC__stream_decoder_new())) + return die_("couldn't allocate decoder instance"); + + FLAC__stream_decoder_set_md5_checking(decoder, true); + FLAC__stream_decoder_set_metadata_respond_all(decoder); + if( + (is_ogg? + FLAC__stream_decoder_init_ogg_file(decoder, filename, decoder_write_callback_, metadata_callback, decoder_error_callback_, &decoder_client_data) : + FLAC__stream_decoder_init_file(decoder, filename, decoder_write_callback_, metadata_callback, decoder_error_callback_, &decoder_client_data) + ) != FLAC__STREAM_DECODER_INIT_STATUS_OK + ) { + (void)FLAC__stream_decoder_finish(decoder); + FLAC__stream_decoder_delete(decoder); + return die_("initializing decoder\n"); + } + if(!FLAC__stream_decoder_process_until_end_of_stream(decoder)) { + (void)FLAC__stream_decoder_finish(decoder); + FLAC__stream_decoder_delete(decoder); + return die_("decoding file\n"); + } + + (void)FLAC__stream_decoder_finish(decoder); + FLAC__stream_decoder_delete(decoder); + + if(decoder_client_data.error_occurred) + return false; + + if(mc_our_block_number_ != our_metadata_.num_blocks) + return die_("short metadata block count"); + + printf("PASSED\n"); + return true; +} + +static FLAC__bool change_stats_(const char *filename, FLAC__bool read_only) +{ + if(!grabbag__file_change_stats(filename, read_only)) + return die_("during grabbag__file_change_stats()"); + + return true; +} + +static FLAC__bool remove_file_(const char *filename) +{ + while(our_metadata_.num_blocks > 0) + delete_from_our_metadata_(0); + + if(!grabbag__file_remove_file(filename)) + return die_("removing file"); + + return true; +} + +static FLAC__bool test_level_0_(void) +{ + FLAC__StreamMetadata streaminfo; + FLAC__StreamMetadata *tags = 0; + FLAC__StreamMetadata *cuesheet = 0; + FLAC__StreamMetadata *picture = 0; + + printf("\n\n++++++ testing level 0 interface\n"); + + if(!generate_file_(/*include_extras=*/true, /*is_ogg=*/false)) + return false; + + if(!test_file_(/*is_ogg=*/false, decoder_metadata_callback_null_)) + return false; + + printf("testing FLAC__metadata_get_streaminfo()... "); + + if(!FLAC__metadata_get_streaminfo(flacfilename(/*is_ogg=*/false), &streaminfo)) + return die_("during FLAC__metadata_get_streaminfo()"); + + /* check to see if some basic data matches (c.f. generate_file_()) */ + if(streaminfo.data.stream_info.channels != 1) + return die_("mismatch in streaminfo.data.stream_info.channels"); + if(streaminfo.data.stream_info.bits_per_sample != 8) + return die_("mismatch in streaminfo.data.stream_info.bits_per_sample"); + if(streaminfo.data.stream_info.sample_rate != 44100) + return die_("mismatch in streaminfo.data.stream_info.sample_rate"); + if(streaminfo.data.stream_info.min_blocksize != 576) + return die_("mismatch in streaminfo.data.stream_info.min_blocksize"); + if(streaminfo.data.stream_info.max_blocksize != 576) + return die_("mismatch in streaminfo.data.stream_info.max_blocksize"); + + printf("OK\n"); + + printf("testing FLAC__metadata_get_tags()... "); + + if(!FLAC__metadata_get_tags(flacfilename(/*is_ogg=*/false), &tags)) + return die_("during FLAC__metadata_get_tags()"); + + /* check to see if some basic data matches (c.f. generate_file_()) */ + if(tags->data.vorbis_comment.num_comments != 0) + return die_("mismatch in tags->data.vorbis_comment.num_comments"); + + printf("OK\n"); + + FLAC__metadata_object_delete(tags); + + printf("testing FLAC__metadata_get_cuesheet()... "); + + if(!FLAC__metadata_get_cuesheet(flacfilename(/*is_ogg=*/false), &cuesheet)) + return die_("during FLAC__metadata_get_cuesheet()"); + + /* check to see if some basic data matches (c.f. generate_file_()) */ + if(cuesheet->data.cue_sheet.lead_in != 123) + return die_("mismatch in cuesheet->data.cue_sheet.lead_in"); + + printf("OK\n"); + + FLAC__metadata_object_delete(cuesheet); + + printf("testing FLAC__metadata_get_picture()... "); + + if(!FLAC__metadata_get_picture(flacfilename(/*is_ogg=*/false), &picture, /*type=*/(FLAC__StreamMetadata_Picture_Type)(-1), /*mime_type=*/0, /*description=*/0, /*max_width=*/(unsigned)(-1), /*max_height=*/(unsigned)(-1), /*max_depth=*/(unsigned)(-1), /*max_colors=*/(unsigned)(-1))) + return die_("during FLAC__metadata_get_picture()"); + + /* check to see if some basic data matches (c.f. generate_file_()) */ + if(picture->data.picture.type != FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER) + return die_("mismatch in picture->data.picture.type"); + + printf("OK\n"); + + FLAC__metadata_object_delete(picture); + + if(!remove_file_(flacfilename(/*is_ogg=*/false))) + return false; + + return true; +} + +static FLAC__bool test_level_1_(void) +{ + FLAC__Metadata_SimpleIterator *iterator; + FLAC__StreamMetadata *block, *app, *padding; + FLAC__byte data[1000]; + unsigned our_current_position = 0; + + /* initialize 'data' to avoid Valgrind errors */ + memset(data, 0, sizeof(data)); + + printf("\n\n++++++ testing level 1 interface\n"); + + /************************************************************/ + + printf("simple iterator on read-only file\n"); + + if(!generate_file_(/*include_extras=*/false, /*is_ogg=*/false)) + return false; + + if(!change_stats_(flacfilename(/*is_ogg=*/false), /*read_only=*/true)) + return false; + + if(!test_file_(/*is_ogg=*/false, decoder_metadata_callback_null_)) + return false; + + if(0 == (iterator = FLAC__metadata_simple_iterator_new())) + return die_("FLAC__metadata_simple_iterator_new()"); + + if(!FLAC__metadata_simple_iterator_init(iterator, flacfilename(/*is_ogg=*/false), /*read_only=*/false, /*preserve_file_stats=*/false)) + return die_("FLAC__metadata_simple_iterator_init() returned false"); + + printf("is writable = %u\n", (unsigned)FLAC__metadata_simple_iterator_is_writable(iterator)); + if(FLAC__metadata_simple_iterator_is_writable(iterator)) + return die_("iterator claims file is writable when tester thinks it should not be; are you running as root?\n"); + + printf("iterate forwards\n"); + + if(FLAC__metadata_simple_iterator_get_block_type(iterator) != FLAC__METADATA_TYPE_STREAMINFO) + return die_("expected STREAMINFO type from FLAC__metadata_simple_iterator_get_block_type()"); + if(0 == (block = FLAC__metadata_simple_iterator_get_block(iterator))) + return die_("getting block 0"); + if(block->type != FLAC__METADATA_TYPE_STREAMINFO) + return die_("expected STREAMINFO type"); + if(block->is_last) + return die_("expected is_last to be false"); + if(block->length != FLAC__STREAM_METADATA_STREAMINFO_LENGTH) + return die_("bad STREAMINFO length"); + /* check to see if some basic data matches (c.f. generate_file_()) */ + if(block->data.stream_info.channels != 1) + return die_("mismatch in channels"); + if(block->data.stream_info.bits_per_sample != 8) + return die_("mismatch in bits_per_sample"); + if(block->data.stream_info.sample_rate != 44100) + return die_("mismatch in sample_rate"); + if(block->data.stream_info.min_blocksize != 576) + return die_("mismatch in min_blocksize"); + if(block->data.stream_info.max_blocksize != 576) + return die_("mismatch in max_blocksize"); + FLAC__metadata_object_delete(block); + + if(!FLAC__metadata_simple_iterator_next(iterator)) + return die_("forward iterator ended early"); + our_current_position++; + + if(!FLAC__metadata_simple_iterator_next(iterator)) + return die_("forward iterator ended early"); + our_current_position++; + + if(FLAC__metadata_simple_iterator_get_block_type(iterator) != FLAC__METADATA_TYPE_PADDING) + return die_("expected PADDING type from FLAC__metadata_simple_iterator_get_block_type()"); + if(0 == (block = FLAC__metadata_simple_iterator_get_block(iterator))) + return die_("getting block 2"); + if(block->type != FLAC__METADATA_TYPE_PADDING) + return die_("expected PADDING type"); + if(!block->is_last) + return die_("expected is_last to be true"); + /* check to see if some basic data matches (c.f. generate_file_()) */ + if(block->length != 1234) + return die_("bad PADDING length"); + FLAC__metadata_object_delete(block); + + if(FLAC__metadata_simple_iterator_next(iterator)) + return die_("forward iterator returned true but should have returned false"); + + printf("iterate backwards\n"); + if(!FLAC__metadata_simple_iterator_prev(iterator)) + return die_("reverse iterator ended early"); + if(!FLAC__metadata_simple_iterator_prev(iterator)) + return die_("reverse iterator ended early"); + if(FLAC__metadata_simple_iterator_prev(iterator)) + return die_("reverse iterator returned true but should have returned false"); + + printf("testing FLAC__metadata_simple_iterator_set_block() on read-only file...\n"); + + if(!FLAC__metadata_simple_iterator_set_block(iterator, (FLAC__StreamMetadata*)99, false)) + printf("OK: FLAC__metadata_simple_iterator_set_block() returned false like it should\n"); + else + return die_("FLAC__metadata_simple_iterator_set_block() returned true but shouldn't have"); + + FLAC__metadata_simple_iterator_delete(iterator); + + /************************************************************/ + + printf("simple iterator on writable file\n"); + + if(!change_stats_(flacfilename(/*is_ogg=*/false), /*read-only=*/false)) + return false; + + printf("creating APPLICATION block\n"); + + if(0 == (app = FLAC__metadata_object_new(FLAC__METADATA_TYPE_APPLICATION))) + return die_("FLAC__metadata_object_new(FLAC__METADATA_TYPE_APPLICATION)"); + memcpy(app->data.application.id, "duh", (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8)); + + printf("creating PADDING block\n"); + + if(0 == (padding = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING))) + return die_("FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING)"); + padding->length = 20; + + if(0 == (iterator = FLAC__metadata_simple_iterator_new())) + return die_("FLAC__metadata_simple_iterator_new()"); + + if(!FLAC__metadata_simple_iterator_init(iterator, flacfilename(/*is_ogg=*/false), /*read_only=*/false, /*preserve_file_stats=*/false)) + return die_("FLAC__metadata_simple_iterator_init() returned false"); + our_current_position = 0; + + printf("is writable = %u\n", (unsigned)FLAC__metadata_simple_iterator_is_writable(iterator)); + + printf("[S]VP\ttry to write over STREAMINFO block...\n"); + if(!FLAC__metadata_simple_iterator_set_block(iterator, app, false)) + printf("\tFLAC__metadata_simple_iterator_set_block() returned false like it should\n"); + else + return die_("FLAC__metadata_simple_iterator_set_block() returned true but shouldn't have"); + + printf("[S]VP\tnext\n"); + if(!FLAC__metadata_simple_iterator_next(iterator)) + return die_("iterator ended early\n"); + our_current_position++; + + printf("S[V]P\tnext\n"); + if(!FLAC__metadata_simple_iterator_next(iterator)) + return die_("iterator ended early\n"); + our_current_position++; + + printf("SV[P]\tinsert PADDING after, don't expand into padding\n"); + padding->length = 25; + if(!FLAC__metadata_simple_iterator_insert_block_after(iterator, padding, false)) + return die_ss_("FLAC__metadata_simple_iterator_insert_block_after(iterator, padding, false)", iterator); + if(!insert_to_our_metadata_(padding, ++our_current_position, /*copy=*/true)) + return false; + + printf("SVP[P]\tprev\n"); + if(!FLAC__metadata_simple_iterator_prev(iterator)) + return die_("iterator ended early\n"); + our_current_position--; + + printf("SV[P]P\tprev\n"); + if(!FLAC__metadata_simple_iterator_prev(iterator)) + return die_("iterator ended early\n"); + our_current_position--; + + printf("S[V]PP\tinsert PADDING after, don't expand into padding\n"); + padding->length = 30; + if(!FLAC__metadata_simple_iterator_insert_block_after(iterator, padding, false)) + return die_ss_("FLAC__metadata_simple_iterator_insert_block_after(iterator, padding, false)", iterator); + if(!insert_to_our_metadata_(padding, ++our_current_position, /*copy=*/true)) + return false; + + if(!test_file_(/*is_ogg=*/false, decoder_metadata_callback_compare_)) + return false; + + printf("SV[P]PP\tprev\n"); + if(!FLAC__metadata_simple_iterator_prev(iterator)) + return die_("iterator ended early\n"); + our_current_position--; + + printf("S[V]PPP\tprev\n"); + if(!FLAC__metadata_simple_iterator_prev(iterator)) + return die_("iterator ended early\n"); + our_current_position--; + + printf("[S]VPPP\tdelete (STREAMINFO block), must fail\n"); + if(FLAC__metadata_simple_iterator_delete_block(iterator, false)) + return die_ss_("FLAC__metadata_simple_iterator_delete_block(iterator, false) should have returned false", iterator); + + if(!test_file_(/*is_ogg=*/false, decoder_metadata_callback_compare_)) + return false; + + printf("[S]VPPP\tnext\n"); + if(!FLAC__metadata_simple_iterator_next(iterator)) + return die_("iterator ended early\n"); + our_current_position++; + + printf("S[V]PPP\tnext\n"); + if(!FLAC__metadata_simple_iterator_next(iterator)) + return die_("iterator ended early\n"); + our_current_position++; + + printf("SV[P]PP\tdelete (middle block), replace with padding\n"); + if(!FLAC__metadata_simple_iterator_delete_block(iterator, true)) + return die_ss_("FLAC__metadata_simple_iterator_delete_block(iterator, true)", iterator); + our_current_position--; + + printf("S[V]PPP\tnext\n"); + if(!FLAC__metadata_simple_iterator_next(iterator)) + return die_("iterator ended early\n"); + our_current_position++; + + printf("SV[P]PP\tdelete (middle block), don't replace with padding\n"); + if(!FLAC__metadata_simple_iterator_delete_block(iterator, false)) + return die_ss_("FLAC__metadata_simple_iterator_delete_block(iterator, false)", iterator); + delete_from_our_metadata_(our_current_position--); + + if(!test_file_(/*is_ogg=*/false, decoder_metadata_callback_compare_)) + return false; + + printf("S[V]PP\tnext\n"); + if(!FLAC__metadata_simple_iterator_next(iterator)) + return die_("iterator ended early\n"); + our_current_position++; + + printf("SV[P]P\tnext\n"); + if(!FLAC__metadata_simple_iterator_next(iterator)) + return die_("iterator ended early\n"); + our_current_position++; + + printf("SVP[P]\tdelete (last block), replace with padding\n"); + if(!FLAC__metadata_simple_iterator_delete_block(iterator, true)) + return die_ss_("FLAC__metadata_simple_iterator_delete_block(iterator, false)", iterator); + our_current_position--; + + if(!test_file_(/*is_ogg=*/false, decoder_metadata_callback_compare_)) + return false; + + printf("SV[P]P\tnext\n"); + if(!FLAC__metadata_simple_iterator_next(iterator)) + return die_("iterator ended early\n"); + our_current_position++; + + printf("SVP[P]\tdelete (last block), don't replace with padding\n"); + if(!FLAC__metadata_simple_iterator_delete_block(iterator, false)) + return die_ss_("FLAC__metadata_simple_iterator_delete_block(iterator, false)", iterator); + delete_from_our_metadata_(our_current_position--); + + if(!test_file_(/*is_ogg=*/false, decoder_metadata_callback_compare_)) + return false; + + printf("SV[P]\tprev\n"); + if(!FLAC__metadata_simple_iterator_prev(iterator)) + return die_("iterator ended early\n"); + our_current_position--; + + printf("S[V]P\tprev\n"); + if(!FLAC__metadata_simple_iterator_prev(iterator)) + return die_("iterator ended early\n"); + our_current_position--; + + printf("[S]VP\tset STREAMINFO (change sample rate)\n"); + FLAC__ASSERT(our_current_position == 0); + block = FLAC__metadata_simple_iterator_get_block(iterator); + block->data.stream_info.sample_rate = 32000; + if(!replace_in_our_metadata_(block, our_current_position, /*copy=*/true)) + return die_("copying object"); + if(!FLAC__metadata_simple_iterator_set_block(iterator, block, false)) + return die_ss_("FLAC__metadata_simple_iterator_set_block(iterator, block, false)", iterator); + FLAC__metadata_object_delete(block); + + if(!test_file_(/*is_ogg=*/false, decoder_metadata_callback_compare_)) + return false; + + printf("[S]VP\tnext\n"); + if(!FLAC__metadata_simple_iterator_next(iterator)) + return die_("iterator ended early\n"); + our_current_position++; + + printf("S[V]P\tinsert APPLICATION after, expand into padding of exceeding size\n"); + app->data.application.id[0] = 'e'; /* twiddle the id so that our comparison doesn't miss transposition */ + if(!FLAC__metadata_simple_iterator_insert_block_after(iterator, app, true)) + return die_ss_("FLAC__metadata_simple_iterator_insert_block_after(iterator, app, true)", iterator); + if(!insert_to_our_metadata_(app, ++our_current_position, /*copy=*/true)) + return false; + our_metadata_.blocks[our_current_position+1]->length -= (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8) + app->length; + + if(!test_file_(/*is_ogg=*/false, decoder_metadata_callback_compare_)) + return false; + + printf("SV[A]P\tnext\n"); + if(!FLAC__metadata_simple_iterator_next(iterator)) + return die_("iterator ended early\n"); + our_current_position++; + + printf("SVA[P]\tset APPLICATION, expand into padding of exceeding size\n"); + app->data.application.id[0] = 'f'; /* twiddle the id */ + if(!FLAC__metadata_simple_iterator_set_block(iterator, app, true)) + return die_ss_("FLAC__metadata_simple_iterator_set_block(iterator, app, true)", iterator); + if(!insert_to_our_metadata_(app, our_current_position, /*copy=*/true)) + return false; + our_metadata_.blocks[our_current_position+1]->length -= (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8) + app->length; + + if(!test_file_(/*is_ogg=*/false, decoder_metadata_callback_compare_)) + return false; + + printf("SVA[A]P\tset APPLICATION (grow), don't expand into padding\n"); + app->data.application.id[0] = 'g'; /* twiddle the id */ + if(!FLAC__metadata_object_application_set_data(app, data, sizeof(data), true)) + return die_("setting APPLICATION data"); + if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true)) + return die_("copying object"); + if(!FLAC__metadata_simple_iterator_set_block(iterator, app, false)) + return die_ss_("FLAC__metadata_simple_iterator_set_block(iterator, app, false)", iterator); + + if(!test_file_(/*is_ogg=*/false, decoder_metadata_callback_compare_)) + return false; + + printf("SVA[A]P\tset APPLICATION (shrink), don't fill in with padding\n"); + app->data.application.id[0] = 'h'; /* twiddle the id */ + if(!FLAC__metadata_object_application_set_data(app, data, 12, true)) + return die_("setting APPLICATION data"); + if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true)) + return die_("copying object"); + if(!FLAC__metadata_simple_iterator_set_block(iterator, app, false)) + return die_ss_("FLAC__metadata_simple_iterator_set_block(iterator, app, false)", iterator); + + if(!test_file_(/*is_ogg=*/false, decoder_metadata_callback_compare_)) + return false; + + printf("SVA[A]P\tset APPLICATION (grow), expand into padding of exceeding size\n"); + app->data.application.id[0] = 'i'; /* twiddle the id */ + if(!FLAC__metadata_object_application_set_data(app, data, sizeof(data), true)) + return die_("setting APPLICATION data"); + if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true)) + return die_("copying object"); + our_metadata_.blocks[our_current_position+1]->length -= (sizeof(data) - 12); + if(!FLAC__metadata_simple_iterator_set_block(iterator, app, true)) + return die_ss_("FLAC__metadata_simple_iterator_set_block(iterator, app, true)", iterator); + + if(!test_file_(/*is_ogg=*/false, decoder_metadata_callback_compare_)) + return false; + + printf("SVA[A]P\tset APPLICATION (shrink), fill in with padding\n"); + app->data.application.id[0] = 'j'; /* twiddle the id */ + if(!FLAC__metadata_object_application_set_data(app, data, 23, true)) + return die_("setting APPLICATION data"); + if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true)) + return die_("copying object"); + if(!insert_to_our_metadata_(padding, our_current_position+1, /*copy=*/true)) + return die_("copying object"); + our_metadata_.blocks[our_current_position+1]->length = sizeof(data) - 23 - FLAC__STREAM_METADATA_HEADER_LENGTH; + if(!FLAC__metadata_simple_iterator_set_block(iterator, app, true)) + return die_ss_("FLAC__metadata_simple_iterator_set_block(iterator, app, true)", iterator); + + if(!test_file_(/*is_ogg=*/false, decoder_metadata_callback_compare_)) + return false; + + printf("SVA[A]PP\tnext\n"); + if(!FLAC__metadata_simple_iterator_next(iterator)) + return die_("iterator ended early\n"); + our_current_position++; + + printf("SVAA[P]P\tnext\n"); + if(!FLAC__metadata_simple_iterator_next(iterator)) + return die_("iterator ended early\n"); + our_current_position++; + + printf("SVAAP[P]\tset PADDING (shrink), don't fill in with padding\n"); + padding->length = 5; + if(!replace_in_our_metadata_(padding, our_current_position, /*copy=*/true)) + return die_("copying object"); + if(!FLAC__metadata_simple_iterator_set_block(iterator, padding, false)) + return die_ss_("FLAC__metadata_simple_iterator_set_block(iterator, padding, false)", iterator); + + if(!test_file_(/*is_ogg=*/false, decoder_metadata_callback_compare_)) + return false; + + printf("SVAAP[P]\tset APPLICATION (grow)\n"); + app->data.application.id[0] = 'k'; /* twiddle the id */ + if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true)) + return die_("copying object"); + if(!FLAC__metadata_simple_iterator_set_block(iterator, app, false)) + return die_ss_("FLAC__metadata_simple_iterator_set_block(iterator, app, false)", iterator); + + if(!test_file_(/*is_ogg=*/false, decoder_metadata_callback_compare_)) + return false; + + printf("SVAAP[A]\tset PADDING (equal)\n"); + padding->length = 27; + if(!replace_in_our_metadata_(padding, our_current_position, /*copy=*/true)) + return die_("copying object"); + if(!FLAC__metadata_simple_iterator_set_block(iterator, padding, false)) + return die_ss_("FLAC__metadata_simple_iterator_set_block(iterator, padding, false)", iterator); + + if(!test_file_(/*is_ogg=*/false, decoder_metadata_callback_compare_)) + return false; + + printf("SVAAP[P]\tprev\n"); + if(!FLAC__metadata_simple_iterator_prev(iterator)) + return die_("iterator ended early\n"); + our_current_position--; + + printf("SVAA[P]P\tdelete (middle block), don't replace with padding\n"); + if(!FLAC__metadata_simple_iterator_delete_block(iterator, false)) + return die_ss_("FLAC__metadata_simple_iterator_delete_block(iterator, false)", iterator); + delete_from_our_metadata_(our_current_position--); + + if(!test_file_(/*is_ogg=*/false, decoder_metadata_callback_compare_)) + return false; + + printf("SVA[A]P\tdelete (middle block), don't replace with padding\n"); + if(!FLAC__metadata_simple_iterator_delete_block(iterator, false)) + return die_ss_("FLAC__metadata_simple_iterator_delete_block(iterator, false)", iterator); + delete_from_our_metadata_(our_current_position--); + + if(!test_file_(/*is_ogg=*/false, decoder_metadata_callback_compare_)) + return false; + + printf("SV[A]P\tnext\n"); + if(!FLAC__metadata_simple_iterator_next(iterator)) + return die_("iterator ended early\n"); + our_current_position++; + + printf("SVA[P]\tinsert PADDING after\n"); + padding->length = 5; + if(!FLAC__metadata_simple_iterator_insert_block_after(iterator, padding, false)) + return die_ss_("FLAC__metadata_simple_iterator_insert_block_after(iterator, padding, false)", iterator); + if(!insert_to_our_metadata_(padding, ++our_current_position, /*copy=*/true)) + return false; + + if(!test_file_(/*is_ogg=*/false, decoder_metadata_callback_compare_)) + return false; + + printf("SVAP[P]\tprev\n"); + if(!FLAC__metadata_simple_iterator_prev(iterator)) + return die_("iterator ended early\n"); + our_current_position--; + + printf("SVA[P]P\tprev\n"); + if(!FLAC__metadata_simple_iterator_prev(iterator)) + return die_("iterator ended early\n"); + our_current_position--; + + printf("SV[A]PP\tset APPLICATION (grow), try to expand into padding which is too small\n"); + if(!FLAC__metadata_object_application_set_data(app, data, 32, true)) + return die_("setting APPLICATION data"); + if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true)) + return die_("copying object"); + if(!FLAC__metadata_simple_iterator_set_block(iterator, app, true)) + return die_ss_("FLAC__metadata_simple_iterator_set_block(iterator, app, true)", iterator); + + if(!test_file_(/*is_ogg=*/false, decoder_metadata_callback_compare_)) + return false; + + printf("SV[A]PP\tset APPLICATION (grow), try to expand into padding which is 'close' but still too small\n"); + if(!FLAC__metadata_object_application_set_data(app, data, 60, true)) + return die_("setting APPLICATION data"); + if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true)) + return die_("copying object"); + if(!FLAC__metadata_simple_iterator_set_block(iterator, app, true)) + return die_ss_("FLAC__metadata_simple_iterator_set_block(iterator, app, true)", iterator); + + if(!test_file_(/*is_ogg=*/false, decoder_metadata_callback_compare_)) + return false; + + printf("SV[A]PP\tset APPLICATION (grow), expand into padding which will leave 0-length pad\n"); + if(!FLAC__metadata_object_application_set_data(app, data, 87, true)) + return die_("setting APPLICATION data"); + if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true)) + return die_("copying object"); + our_metadata_.blocks[our_current_position+1]->length = 0; + if(!FLAC__metadata_simple_iterator_set_block(iterator, app, true)) + return die_ss_("FLAC__metadata_simple_iterator_set_block(iterator, app, true)", iterator); + + if(!test_file_(/*is_ogg=*/false, decoder_metadata_callback_compare_)) + return false; + + printf("SV[A]PP\tset APPLICATION (grow), expand into padding which is exactly consumed\n"); + if(!FLAC__metadata_object_application_set_data(app, data, 91, true)) + return die_("setting APPLICATION data"); + if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true)) + return die_("copying object"); + delete_from_our_metadata_(our_current_position+1); + if(!FLAC__metadata_simple_iterator_set_block(iterator, app, true)) + return die_ss_("FLAC__metadata_simple_iterator_set_block(iterator, app, true)", iterator); + + if(!test_file_(/*is_ogg=*/false, decoder_metadata_callback_compare_)) + return false; + + printf("SV[A]P\tset APPLICATION (grow), expand into padding which is exactly consumed\n"); + if(!FLAC__metadata_object_application_set_data(app, data, 100, true)) + return die_("setting APPLICATION data"); + if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true)) + return die_("copying object"); + delete_from_our_metadata_(our_current_position+1); + our_metadata_.blocks[our_current_position]->is_last = true; + if(!FLAC__metadata_simple_iterator_set_block(iterator, app, true)) + return die_ss_("FLAC__metadata_simple_iterator_set_block(iterator, app, true)", iterator); + + if(!test_file_(/*is_ogg=*/false, decoder_metadata_callback_compare_)) + return false; + + printf("SV[A]\tset PADDING (equal size)\n"); + padding->length = app->length; + if(!replace_in_our_metadata_(padding, our_current_position, /*copy=*/true)) + return die_("copying object"); + if(!FLAC__metadata_simple_iterator_set_block(iterator, padding, true)) + return die_ss_("FLAC__metadata_simple_iterator_set_block(iterator, padding, true)", iterator); + + if(!test_file_(/*is_ogg=*/false, decoder_metadata_callback_compare_)) + return false; + + printf("SV[P]\tinsert PADDING after\n"); + if(!FLAC__metadata_simple_iterator_insert_block_after(iterator, padding, false)) + return die_ss_("FLAC__metadata_simple_iterator_insert_block_after(iterator, padding, false)", iterator); + if(!insert_to_our_metadata_(padding, ++our_current_position, /*copy=*/true)) + return false; + + if(!test_file_(/*is_ogg=*/false, decoder_metadata_callback_compare_)) + return false; + + printf("SVP[P]\tinsert PADDING after\n"); + padding->length = 5; + if(!FLAC__metadata_simple_iterator_insert_block_after(iterator, padding, false)) + return die_ss_("FLAC__metadata_simple_iterator_insert_block_after(iterator, padding, false)", iterator); + if(!insert_to_our_metadata_(padding, ++our_current_position, /*copy=*/true)) + return false; + + if(!test_file_(/*is_ogg=*/false, decoder_metadata_callback_compare_)) + return false; + + printf("SVPP[P]\tprev\n"); + if(!FLAC__metadata_simple_iterator_prev(iterator)) + return die_("iterator ended early\n"); + our_current_position--; + + printf("SVP[P]P\tprev\n"); + if(!FLAC__metadata_simple_iterator_prev(iterator)) + return die_("iterator ended early\n"); + our_current_position--; + + printf("SV[P]PP\tprev\n"); + if(!FLAC__metadata_simple_iterator_prev(iterator)) + return die_("iterator ended early\n"); + our_current_position--; + + printf("S[V]PPP\tinsert APPLICATION after, try to expand into padding which is too small\n"); + if(!FLAC__metadata_object_application_set_data(app, data, 101, true)) + return die_("setting APPLICATION data"); + if(!insert_to_our_metadata_(app, ++our_current_position, /*copy=*/true)) + return die_("copying object"); + if(!FLAC__metadata_simple_iterator_insert_block_after(iterator, app, true)) + return die_ss_("FLAC__metadata_simple_iterator_insert_block_after(iterator, app, true)", iterator); + + if(!test_file_(/*is_ogg=*/false, decoder_metadata_callback_compare_)) + return false; + + printf("SV[A]PPP\tdelete (middle block), don't replace with padding\n"); + if(!FLAC__metadata_simple_iterator_delete_block(iterator, false)) + return die_ss_("FLAC__metadata_simple_iterator_delete_block(iterator, false)", iterator); + delete_from_our_metadata_(our_current_position--); + + if(!test_file_(/*is_ogg=*/false, decoder_metadata_callback_compare_)) + return false; + + printf("S[V]PPP\tinsert APPLICATION after, try to expand into padding which is 'close' but still too small\n"); + if(!FLAC__metadata_object_application_set_data(app, data, 97, true)) + return die_("setting APPLICATION data"); + if(!insert_to_our_metadata_(app, ++our_current_position, /*copy=*/true)) + return die_("copying object"); + if(!FLAC__metadata_simple_iterator_insert_block_after(iterator, app, true)) + return die_ss_("FLAC__metadata_simple_iterator_insert_block_after(iterator, app, true)", iterator); + + if(!test_file_(/*is_ogg=*/false, decoder_metadata_callback_compare_)) + return false; + + printf("SV[A]PPP\tdelete (middle block), don't replace with padding\n"); + if(!FLAC__metadata_simple_iterator_delete_block(iterator, false)) + return die_ss_("FLAC__metadata_simple_iterator_delete_block(iterator, false)", iterator); + delete_from_our_metadata_(our_current_position--); + + if(!test_file_(/*is_ogg=*/false, decoder_metadata_callback_compare_)) + return false; + + printf("S[V]PPP\tinsert APPLICATION after, expand into padding which is exactly consumed\n"); + if(!FLAC__metadata_object_application_set_data(app, data, 100, true)) + return die_("setting APPLICATION data"); + if(!insert_to_our_metadata_(app, ++our_current_position, /*copy=*/true)) + return die_("copying object"); + delete_from_our_metadata_(our_current_position+1); + if(!FLAC__metadata_simple_iterator_insert_block_after(iterator, app, true)) + return die_ss_("FLAC__metadata_simple_iterator_insert_block_after(iterator, app, true)", iterator); + + if(!test_file_(/*is_ogg=*/false, decoder_metadata_callback_compare_)) + return false; + + printf("SV[A]PP\tdelete (middle block), don't replace with padding\n"); + if(!FLAC__metadata_simple_iterator_delete_block(iterator, false)) + return die_ss_("FLAC__metadata_simple_iterator_delete_block(iterator, false)", iterator); + delete_from_our_metadata_(our_current_position--); + + if(!test_file_(/*is_ogg=*/false, decoder_metadata_callback_compare_)) + return false; + + printf("S[V]PP\tinsert APPLICATION after, expand into padding which will leave 0-length pad\n"); + if(!FLAC__metadata_object_application_set_data(app, data, 96, true)) + return die_("setting APPLICATION data"); + if(!insert_to_our_metadata_(app, ++our_current_position, /*copy=*/true)) + return die_("copying object"); + our_metadata_.blocks[our_current_position+1]->length = 0; + if(!FLAC__metadata_simple_iterator_insert_block_after(iterator, app, true)) + return die_ss_("FLAC__metadata_simple_iterator_insert_block_after(iterator, app, true)", iterator); + + if(!test_file_(/*is_ogg=*/false, decoder_metadata_callback_compare_)) + return false; + + printf("SV[A]PP\tdelete (middle block), don't replace with padding\n"); + if(!FLAC__metadata_simple_iterator_delete_block(iterator, false)) + return die_ss_("FLAC__metadata_simple_iterator_delete_block(iterator, false)", iterator); + delete_from_our_metadata_(our_current_position--); + + if(!test_file_(/*is_ogg=*/false, decoder_metadata_callback_compare_)) + return false; + + printf("S[V]PP\tnext\n"); + if(!FLAC__metadata_simple_iterator_next(iterator)) + return die_("iterator ended early\n"); + our_current_position++; + + printf("SV[P]P\tdelete (middle block), don't replace with padding\n"); + if(!FLAC__metadata_simple_iterator_delete_block(iterator, false)) + return die_ss_("FLAC__metadata_simple_iterator_delete_block(iterator, false)", iterator); + delete_from_our_metadata_(our_current_position--); + + if(!test_file_(/*is_ogg=*/false, decoder_metadata_callback_compare_)) + return false; + + printf("S[V]P\tinsert APPLICATION after, expand into padding which is exactly consumed\n"); + if(!FLAC__metadata_object_application_set_data(app, data, 1, true)) + return die_("setting APPLICATION data"); + if(!insert_to_our_metadata_(app, ++our_current_position, /*copy=*/true)) + return die_("copying object"); + delete_from_our_metadata_(our_current_position+1); + if(!FLAC__metadata_simple_iterator_insert_block_after(iterator, app, true)) + return die_ss_("FLAC__metadata_simple_iterator_insert_block_after(iterator, app, true)", iterator); + + if(!test_file_(/*is_ogg=*/false, decoder_metadata_callback_compare_)) + return false; + + printf("delete simple iterator\n"); + + FLAC__metadata_simple_iterator_delete(iterator); + + FLAC__metadata_object_delete(app); + FLAC__metadata_object_delete(padding); + + if(!remove_file_(flacfilename(/*is_ogg=*/false))) + return false; + + return true; +} + +static FLAC__bool test_level_2_(FLAC__bool filename_based, FLAC__bool is_ogg) +{ + FLAC__Metadata_Iterator *iterator; + FLAC__Metadata_Chain *chain; + FLAC__StreamMetadata *block, *app, *padding; + FLAC__byte data[2000]; + unsigned our_current_position; + + /* initialize 'data' to avoid Valgrind errors */ + memset(data, 0, sizeof(data)); + + printf("\n\n++++++ testing level 2 interface (%s-based, %s FLAC)\n", filename_based? "filename":"callback", is_ogg? "Ogg":"native"); + + printf("generate read-only file\n"); + + if(!generate_file_(/*include_extras=*/false, is_ogg)) + return false; + + if(!change_stats_(flacfilename(is_ogg), /*read_only=*/true)) + return false; + + printf("create chain\n"); + + if(0 == (chain = FLAC__metadata_chain_new())) + return die_("allocating chain"); + + printf("read chain\n"); + + if(!read_chain_(chain, flacfilename(is_ogg), filename_based, is_ogg)) + return die_c_("reading chain", FLAC__metadata_chain_status(chain)); + + printf("[S]VP\ttest initial metadata\n"); + + if(!compare_chain_(chain, 0, 0)) + return false; + if(!test_file_(is_ogg, decoder_metadata_callback_compare_)) + return false; + + if(is_ogg) + goto end; + + printf("switch file to read-write\n"); + + if(!change_stats_(flacfilename(is_ogg), /*read-only=*/false)) + return false; + + printf("create iterator\n"); + if(0 == (iterator = FLAC__metadata_iterator_new())) + return die_("allocating memory for iterator"); + + our_current_position = 0; + + FLAC__metadata_iterator_init(iterator, chain); + + if(0 == (block = FLAC__metadata_iterator_get_block(iterator))) + return die_("getting block from iterator"); + + FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_STREAMINFO); + + printf("[S]VP\tmodify STREAMINFO, write\n"); + + block->data.stream_info.sample_rate = 32000; + if(!replace_in_our_metadata_(block, our_current_position, /*copy=*/true)) + return die_("copying object"); + + if(!write_chain_(chain, /*use_padding=*/false, /*preserve_file_stats=*/true, filename_based, flacfilename(is_ogg))) + return die_c_("during FLAC__metadata_chain_write(chain, false, true)", FLAC__metadata_chain_status(chain)); + if(!compare_chain_(chain, our_current_position, FLAC__metadata_iterator_get_block(iterator))) + return false; + if(!test_file_(is_ogg, decoder_metadata_callback_compare_)) + return false; + + printf("[S]VP\tnext\n"); + if(!FLAC__metadata_iterator_next(iterator)) + return die_("iterator ended early\n"); + our_current_position++; + + printf("S[V]P\tnext\n"); + if(!FLAC__metadata_iterator_next(iterator)) + return die_("iterator ended early\n"); + our_current_position++; + + printf("SV[P]\treplace PADDING with identical-size APPLICATION\n"); + if(0 == (block = FLAC__metadata_iterator_get_block(iterator))) + return die_("getting block from iterator"); + if(0 == (app = FLAC__metadata_object_new(FLAC__METADATA_TYPE_APPLICATION))) + return die_("FLAC__metadata_object_new(FLAC__METADATA_TYPE_APPLICATION)"); + memcpy(app->data.application.id, "duh", (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8)); + if(!FLAC__metadata_object_application_set_data(app, data, block->length-(FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8), true)) + return die_("setting APPLICATION data"); + if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true)) + return die_("copying object"); + if(!FLAC__metadata_iterator_set_block(iterator, app)) + return die_c_("FLAC__metadata_iterator_set_block(iterator, app)", FLAC__metadata_chain_status(chain)); + + if(!write_chain_(chain, /*use_padding=*/false, /*preserve_file_stats=*/false, filename_based, flacfilename(is_ogg))) + return die_c_("during FLAC__metadata_chain_write(chain, false, false)", FLAC__metadata_chain_status(chain)); + if(!compare_chain_(chain, our_current_position, FLAC__metadata_iterator_get_block(iterator))) + return false; + if(!test_file_(is_ogg, decoder_metadata_callback_compare_)) + return false; + + printf("SV[A]\tshrink APPLICATION, don't use padding\n"); + if(0 == (app = FLAC__metadata_object_clone(our_metadata_.blocks[our_current_position]))) + return die_("copying object"); + if(!FLAC__metadata_object_application_set_data(app, data, 26, true)) + return die_("setting APPLICATION data"); + if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true)) + return die_("copying object"); + if(!FLAC__metadata_iterator_set_block(iterator, app)) + return die_c_("FLAC__metadata_iterator_set_block(iterator, app)", FLAC__metadata_chain_status(chain)); + + if(!write_chain_(chain, /*use_padding=*/false, /*preserve_file_stats=*/false, filename_based, flacfilename(is_ogg))) + return die_c_("during FLAC__metadata_chain_write(chain, false, false)", FLAC__metadata_chain_status(chain)); + if(!compare_chain_(chain, our_current_position, FLAC__metadata_iterator_get_block(iterator))) + return false; + if(!test_file_(is_ogg, decoder_metadata_callback_compare_)) + return false; + + printf("SV[A]\tgrow APPLICATION, don't use padding\n"); + if(0 == (app = FLAC__metadata_object_clone(our_metadata_.blocks[our_current_position]))) + return die_("copying object"); + if(!FLAC__metadata_object_application_set_data(app, data, 28, true)) + return die_("setting APPLICATION data"); + if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true)) + return die_("copying object"); + if(!FLAC__metadata_iterator_set_block(iterator, app)) + return die_c_("FLAC__metadata_iterator_set_block(iterator, app)", FLAC__metadata_chain_status(chain)); + + if(!write_chain_(chain, /*use_padding=*/false, /*preserve_file_stats=*/false, filename_based, flacfilename(is_ogg))) + return die_c_("during FLAC__metadata_chain_write(chain, false, false)", FLAC__metadata_chain_status(chain)); + if(!compare_chain_(chain, our_current_position, FLAC__metadata_iterator_get_block(iterator))) + return false; + if(!test_file_(is_ogg, decoder_metadata_callback_compare_)) + return false; + + printf("SV[A]\tgrow APPLICATION, use padding, but last block is not padding\n"); + if(0 == (app = FLAC__metadata_object_clone(our_metadata_.blocks[our_current_position]))) + return die_("copying object"); + if(!FLAC__metadata_object_application_set_data(app, data, 36, true)) + return die_("setting APPLICATION data"); + if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true)) + return die_("copying object"); + if(!FLAC__metadata_iterator_set_block(iterator, app)) + return die_c_("FLAC__metadata_iterator_set_block(iterator, app)", FLAC__metadata_chain_status(chain)); + + if(!write_chain_(chain, /*use_padding=*/false, /*preserve_file_stats=*/false, filename_based, flacfilename(is_ogg))) + return die_c_("during FLAC__metadata_chain_write(chain, false, false)", FLAC__metadata_chain_status(chain)); + if(!compare_chain_(chain, our_current_position, FLAC__metadata_iterator_get_block(iterator))) + return false; + if(!test_file_(is_ogg, decoder_metadata_callback_compare_)) + return false; + + printf("SV[A]\tshrink APPLICATION, use padding, last block is not padding, but delta is too small for new PADDING block\n"); + if(0 == (app = FLAC__metadata_object_clone(our_metadata_.blocks[our_current_position]))) + return die_("copying object"); + if(!FLAC__metadata_object_application_set_data(app, data, 33, true)) + return die_("setting APPLICATION data"); + if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true)) + return die_("copying object"); + if(!FLAC__metadata_iterator_set_block(iterator, app)) + return die_c_("FLAC__metadata_iterator_set_block(iterator, app)", FLAC__metadata_chain_status(chain)); + + if(!write_chain_(chain, /*use_padding=*/true, /*preserve_file_stats=*/false, filename_based, flacfilename(is_ogg))) + return die_c_("during FLAC__metadata_chain_write(chain, true, false)", FLAC__metadata_chain_status(chain)); + if(!compare_chain_(chain, our_current_position, FLAC__metadata_iterator_get_block(iterator))) + return false; + if(!test_file_(is_ogg, decoder_metadata_callback_compare_)) + return false; + + printf("SV[A]\tshrink APPLICATION, use padding, last block is not padding, delta is enough for new PADDING block\n"); + if(0 == (padding = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING))) + return die_("creating PADDING block"); + if(0 == (app = FLAC__metadata_object_clone(our_metadata_.blocks[our_current_position]))) + return die_("copying object"); + if(!FLAC__metadata_object_application_set_data(app, data, 29, true)) + return die_("setting APPLICATION data"); + if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true)) + return die_("copying object"); + padding->length = 0; + if(!insert_to_our_metadata_(padding, our_current_position+1, /*copy=*/false)) + return die_("internal error"); + if(!FLAC__metadata_iterator_set_block(iterator, app)) + return die_c_("FLAC__metadata_iterator_set_block(iterator, app)", FLAC__metadata_chain_status(chain)); + + if(!write_chain_(chain, /*use_padding=*/true, /*preserve_file_stats=*/false, filename_based, flacfilename(is_ogg))) + return die_c_("during FLAC__metadata_chain_write(chain, true, false)", FLAC__metadata_chain_status(chain)); + if(!compare_chain_(chain, our_current_position, FLAC__metadata_iterator_get_block(iterator))) + return false; + if(!test_file_(is_ogg, decoder_metadata_callback_compare_)) + return false; + + printf("SV[A]P\tshrink APPLICATION, use padding, last block is padding\n"); + if(0 == (app = FLAC__metadata_object_clone(our_metadata_.blocks[our_current_position]))) + return die_("copying object"); + if(!FLAC__metadata_object_application_set_data(app, data, 16, true)) + return die_("setting APPLICATION data"); + if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true)) + return die_("copying object"); + our_metadata_.blocks[our_current_position+1]->length = 13; + if(!FLAC__metadata_iterator_set_block(iterator, app)) + return die_c_("FLAC__metadata_iterator_set_block(iterator, app)", FLAC__metadata_chain_status(chain)); + + if(!write_chain_(chain, /*use_padding=*/true, /*preserve_file_stats=*/false, filename_based, flacfilename(is_ogg))) + return die_c_("during FLAC__metadata_chain_write(chain, true, false)", FLAC__metadata_chain_status(chain)); + if(!compare_chain_(chain, our_current_position, FLAC__metadata_iterator_get_block(iterator))) + return false; + if(!test_file_(is_ogg, decoder_metadata_callback_compare_)) + return false; + + printf("SV[A]P\tgrow APPLICATION, use padding, last block is padding, but delta is too small\n"); + if(0 == (app = FLAC__metadata_object_clone(our_metadata_.blocks[our_current_position]))) + return die_("copying object"); + if(!FLAC__metadata_object_application_set_data(app, data, 50, true)) + return die_("setting APPLICATION data"); + if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true)) + return die_("copying object"); + if(!FLAC__metadata_iterator_set_block(iterator, app)) + return die_c_("FLAC__metadata_iterator_set_block(iterator, app)", FLAC__metadata_chain_status(chain)); + + if(!write_chain_(chain, /*use_padding=*/true, /*preserve_file_stats=*/false, filename_based, flacfilename(is_ogg))) + return die_c_("during FLAC__metadata_chain_write(chain, true, false)", FLAC__metadata_chain_status(chain)); + if(!compare_chain_(chain, our_current_position, FLAC__metadata_iterator_get_block(iterator))) + return false; + if(!test_file_(is_ogg, decoder_metadata_callback_compare_)) + return false; + + printf("SV[A]P\tgrow APPLICATION, use padding, last block is padding of exceeding size\n"); + if(0 == (app = FLAC__metadata_object_clone(our_metadata_.blocks[our_current_position]))) + return die_("copying object"); + if(!FLAC__metadata_object_application_set_data(app, data, 56, true)) + return die_("setting APPLICATION data"); + if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true)) + return die_("copying object"); + our_metadata_.blocks[our_current_position+1]->length -= (56 - 50); + if(!FLAC__metadata_iterator_set_block(iterator, app)) + return die_c_("FLAC__metadata_iterator_set_block(iterator, app)", FLAC__metadata_chain_status(chain)); + + if(!write_chain_(chain, /*use_padding=*/true, /*preserve_file_stats=*/false, filename_based, flacfilename(is_ogg))) + return die_c_("during FLAC__metadata_chain_write(chain, true, false)", FLAC__metadata_chain_status(chain)); + if(!compare_chain_(chain, our_current_position, FLAC__metadata_iterator_get_block(iterator))) + return false; + if(!test_file_(is_ogg, decoder_metadata_callback_compare_)) + return false; + + printf("SV[A]P\tgrow APPLICATION, use padding, last block is padding of exact size\n"); + if(0 == (app = FLAC__metadata_object_clone(our_metadata_.blocks[our_current_position]))) + return die_("copying object"); + if(!FLAC__metadata_object_application_set_data(app, data, 67, true)) + return die_("setting APPLICATION data"); + if(!replace_in_our_metadata_(app, our_current_position, /*copy=*/true)) + return die_("copying object"); + delete_from_our_metadata_(our_current_position+1); + if(!FLAC__metadata_iterator_set_block(iterator, app)) + return die_c_("FLAC__metadata_iterator_set_block(iterator, app)", FLAC__metadata_chain_status(chain)); + + if(!write_chain_(chain, /*use_padding=*/true, /*preserve_file_stats=*/false, filename_based, flacfilename(is_ogg))) + return die_c_("during FLAC__metadata_chain_write(chain, true, false)", FLAC__metadata_chain_status(chain)); + if(!compare_chain_(chain, our_current_position, FLAC__metadata_iterator_get_block(iterator))) + return false; + if(!test_file_(is_ogg, decoder_metadata_callback_compare_)) + return false; + + printf("SV[A]\tprev\n"); + if(!FLAC__metadata_iterator_prev(iterator)) + return die_("iterator ended early\n"); + our_current_position--; + + printf("S[V]A\tprev\n"); + if(!FLAC__metadata_iterator_prev(iterator)) + return die_("iterator ended early\n"); + our_current_position--; + + printf("[S]VA\tinsert PADDING before STREAMINFO (should fail)\n"); + if(0 == (padding = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING))) + return die_("creating PADDING block"); + padding->length = 30; + if(!FLAC__metadata_iterator_insert_block_before(iterator, padding)) + printf("\tFLAC__metadata_iterator_insert_block_before() returned false like it should\n"); + else + return die_("FLAC__metadata_iterator_insert_block_before() should have returned false"); + + printf("[S]VP\tnext\n"); + if(!FLAC__metadata_iterator_next(iterator)) + return die_("iterator ended early\n"); + our_current_position++; + + printf("S[V]A\tinsert PADDING after\n"); + if(!insert_to_our_metadata_(padding, ++our_current_position, /*copy=*/true)) + return die_("copying metadata"); + if(!FLAC__metadata_iterator_insert_block_after(iterator, padding)) + return die_("FLAC__metadata_iterator_insert_block_after(iterator, padding)"); + + if(!compare_chain_(chain, our_current_position, FLAC__metadata_iterator_get_block(iterator))) + return false; + + printf("SV[P]A\tinsert PADDING before\n"); + if(0 == (padding = FLAC__metadata_object_clone(our_metadata_.blocks[our_current_position]))) + return die_("creating PADDING block"); + padding->length = 17; + if(!insert_to_our_metadata_(padding, our_current_position, /*copy=*/true)) + return die_("copying metadata"); + if(!FLAC__metadata_iterator_insert_block_before(iterator, padding)) + return die_("FLAC__metadata_iterator_insert_block_before(iterator, padding)"); + + if(!compare_chain_(chain, our_current_position, FLAC__metadata_iterator_get_block(iterator))) + return false; + + printf("SV[P]PA\tinsert PADDING before\n"); + if(0 == (padding = FLAC__metadata_object_clone(our_metadata_.blocks[our_current_position]))) + return die_("creating PADDING block"); + padding->length = 0; + if(!insert_to_our_metadata_(padding, our_current_position, /*copy=*/true)) + return die_("copying metadata"); + if(!FLAC__metadata_iterator_insert_block_before(iterator, padding)) + return die_("FLAC__metadata_iterator_insert_block_before(iterator, padding)"); + + if(!compare_chain_(chain, our_current_position, FLAC__metadata_iterator_get_block(iterator))) + return false; + + printf("SV[P]PPA\tnext\n"); + if(!FLAC__metadata_iterator_next(iterator)) + return die_("iterator ended early\n"); + our_current_position++; + + printf("SVP[P]PA\tnext\n"); + if(!FLAC__metadata_iterator_next(iterator)) + return die_("iterator ended early\n"); + our_current_position++; + + printf("SVPP[P]A\tnext\n"); + if(!FLAC__metadata_iterator_next(iterator)) + return die_("iterator ended early\n"); + our_current_position++; + + printf("SVPPP[A]\tinsert PADDING after\n"); + if(0 == (padding = FLAC__metadata_object_clone(our_metadata_.blocks[2]))) + return die_("creating PADDING block"); + padding->length = 57; + if(!insert_to_our_metadata_(padding, ++our_current_position, /*copy=*/true)) + return die_("copying metadata"); + if(!FLAC__metadata_iterator_insert_block_after(iterator, padding)) + return die_("FLAC__metadata_iterator_insert_block_after(iterator, padding)"); + + if(!compare_chain_(chain, our_current_position, FLAC__metadata_iterator_get_block(iterator))) + return false; + + printf("SVPPPA[P]\tinsert PADDING before\n"); + if(0 == (padding = FLAC__metadata_object_clone(our_metadata_.blocks[2]))) + return die_("creating PADDING block"); + padding->length = 99; + if(!insert_to_our_metadata_(padding, our_current_position, /*copy=*/true)) + return die_("copying metadata"); + if(!FLAC__metadata_iterator_insert_block_before(iterator, padding)) + return die_("FLAC__metadata_iterator_insert_block_before(iterator, padding)"); + + if(!compare_chain_(chain, our_current_position, FLAC__metadata_iterator_get_block(iterator))) + return false; + + printf("delete iterator\n"); + FLAC__metadata_iterator_delete(iterator); + our_current_position = 0; + + printf("SVPPPAPP\tmerge padding\n"); + FLAC__metadata_chain_merge_padding(chain); + our_metadata_.blocks[2]->length += (FLAC__STREAM_METADATA_HEADER_LENGTH + our_metadata_.blocks[3]->length); + our_metadata_.blocks[2]->length += (FLAC__STREAM_METADATA_HEADER_LENGTH + our_metadata_.blocks[4]->length); + our_metadata_.blocks[6]->length += (FLAC__STREAM_METADATA_HEADER_LENGTH + our_metadata_.blocks[7]->length); + delete_from_our_metadata_(7); + delete_from_our_metadata_(4); + delete_from_our_metadata_(3); + + if(!write_chain_(chain, /*use_padding=*/true, /*preserve_file_stats=*/false, filename_based, flacfilename(is_ogg))) + return die_c_("during FLAC__metadata_chain_write(chain, true, false)", FLAC__metadata_chain_status(chain)); + if(!compare_chain_(chain, 0, 0)) + return false; + if(!test_file_(is_ogg, decoder_metadata_callback_compare_)) + return false; + + printf("SVPAP\tsort padding\n"); + FLAC__metadata_chain_sort_padding(chain); + our_metadata_.blocks[4]->length += (FLAC__STREAM_METADATA_HEADER_LENGTH + our_metadata_.blocks[2]->length); + delete_from_our_metadata_(2); + + if(!write_chain_(chain, /*use_padding=*/true, /*preserve_file_stats=*/false, filename_based, flacfilename(is_ogg))) + return die_c_("during FLAC__metadata_chain_write(chain, true, false)", FLAC__metadata_chain_status(chain)); + if(!compare_chain_(chain, 0, 0)) + return false; + if(!test_file_(is_ogg, decoder_metadata_callback_compare_)) + return false; + + printf("create iterator\n"); + if(0 == (iterator = FLAC__metadata_iterator_new())) + return die_("allocating memory for iterator"); + + our_current_position = 0; + + FLAC__metadata_iterator_init(iterator, chain); + + printf("[S]VAP\tnext\n"); + if(!FLAC__metadata_iterator_next(iterator)) + return die_("iterator ended early\n"); + our_current_position++; + + printf("S[V]AP\tnext\n"); + if(!FLAC__metadata_iterator_next(iterator)) + return die_("iterator ended early\n"); + our_current_position++; + + printf("SV[A]P\tdelete middle block, replace with padding\n"); + if(0 == (padding = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING))) + return die_("creating PADDING block"); + padding->length = 71; + if(!replace_in_our_metadata_(padding, our_current_position--, /*copy=*/false)) + return die_("copying object"); + if(!FLAC__metadata_iterator_delete_block(iterator, /*replace_with_padding=*/true)) + return die_c_("FLAC__metadata_iterator_delete_block(iterator, true)", FLAC__metadata_chain_status(chain)); + + if(!compare_chain_(chain, our_current_position, FLAC__metadata_iterator_get_block(iterator))) + return false; + + printf("S[V]PP\tnext\n"); + if(!FLAC__metadata_iterator_next(iterator)) + return die_("iterator ended early\n"); + our_current_position++; + + printf("SV[P]P\tdelete middle block, don't replace with padding\n"); + delete_from_our_metadata_(our_current_position--); + if(!FLAC__metadata_iterator_delete_block(iterator, /*replace_with_padding=*/false)) + return die_c_("FLAC__metadata_iterator_delete_block(iterator, false)", FLAC__metadata_chain_status(chain)); + + if(!compare_chain_(chain, our_current_position, FLAC__metadata_iterator_get_block(iterator))) + return false; + + printf("S[V]P\tnext\n"); + if(!FLAC__metadata_iterator_next(iterator)) + return die_("iterator ended early\n"); + our_current_position++; + + printf("SV[P]\tdelete last block, replace with padding\n"); + if(0 == (padding = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING))) + return die_("creating PADDING block"); + padding->length = 219; + if(!replace_in_our_metadata_(padding, our_current_position--, /*copy=*/false)) + return die_("copying object"); + if(!FLAC__metadata_iterator_delete_block(iterator, /*replace_with_padding=*/true)) + return die_c_("FLAC__metadata_iterator_delete_block(iterator, true)", FLAC__metadata_chain_status(chain)); + + if(!compare_chain_(chain, our_current_position, FLAC__metadata_iterator_get_block(iterator))) + return false; + + printf("S[V]P\tnext\n"); + if(!FLAC__metadata_iterator_next(iterator)) + return die_("iterator ended early\n"); + our_current_position++; + + printf("SV[P]\tdelete last block, don't replace with padding\n"); + delete_from_our_metadata_(our_current_position--); + if(!FLAC__metadata_iterator_delete_block(iterator, /*replace_with_padding=*/false)) + return die_c_("FLAC__metadata_iterator_delete_block(iterator, false)", FLAC__metadata_chain_status(chain)); + + if(!compare_chain_(chain, our_current_position, FLAC__metadata_iterator_get_block(iterator))) + return false; + + printf("S[V]\tprev\n"); + if(!FLAC__metadata_iterator_prev(iterator)) + return die_("iterator ended early\n"); + our_current_position--; + + printf("[S]V\tdelete STREAMINFO block, should fail\n"); + if(FLAC__metadata_iterator_delete_block(iterator, /*replace_with_padding=*/false)) + return die_("FLAC__metadata_iterator_delete_block() on STREAMINFO should have failed but didn't"); + + if(!compare_chain_(chain, our_current_position, FLAC__metadata_iterator_get_block(iterator))) + return false; + + printf("delete iterator\n"); + FLAC__metadata_iterator_delete(iterator); + our_current_position = 0; + + printf("SV\tmerge padding\n"); + FLAC__metadata_chain_merge_padding(chain); + + if(!write_chain_(chain, /*use_padding=*/false, /*preserve_file_stats=*/false, filename_based, flacfilename(is_ogg))) + return die_c_("during FLAC__metadata_chain_write(chain, false, false)", FLAC__metadata_chain_status(chain)); + if(!compare_chain_(chain, 0, 0)) + return false; + if(!test_file_(is_ogg, decoder_metadata_callback_compare_)) + return false; + + printf("SV\tsort padding\n"); + FLAC__metadata_chain_sort_padding(chain); + + if(!write_chain_(chain, /*use_padding=*/false, /*preserve_file_stats=*/false, filename_based, flacfilename(is_ogg))) + return die_c_("during FLAC__metadata_chain_write(chain, false, false)", FLAC__metadata_chain_status(chain)); + if(!compare_chain_(chain, 0, 0)) + return false; + if(!test_file_(is_ogg, decoder_metadata_callback_compare_)) + return false; + +end: + printf("delete chain\n"); + + FLAC__metadata_chain_delete(chain); + + if(!remove_file_(flacfilename(is_ogg))) + return false; + + return true; +} + +static FLAC__bool test_level_2_misc_(FLAC__bool is_ogg) +{ + FLAC__Metadata_Iterator *iterator; + FLAC__Metadata_Chain *chain; + FLAC__IOCallbacks callbacks; + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.read = (FLAC__IOCallback_Read)fread; +#ifdef FLAC__VALGRIND_TESTING + callbacks.write = chain_write_cb_; +#else + callbacks.write = (FLAC__IOCallback_Write)fwrite; +#endif + callbacks.seek = chain_seek_cb_; + callbacks.tell = chain_tell_cb_; + callbacks.eof = chain_eof_cb_; + + printf("\n\n++++++ testing level 2 interface (mismatched read/write protections)\n"); + + printf("generate file\n"); + + if(!generate_file_(/*include_extras=*/false, is_ogg)) + return false; + + printf("create chain\n"); + + if(0 == (chain = FLAC__metadata_chain_new())) + return die_("allocating chain"); + + printf("read chain (filename-based)\n"); + + if(!FLAC__metadata_chain_read(chain, flacfilename(is_ogg))) + return die_c_("reading chain", FLAC__metadata_chain_status(chain)); + + printf("write chain with wrong method FLAC__metadata_chain_write_with_callbacks()\n"); + { + if(FLAC__metadata_chain_write_with_callbacks(chain, /*use_padding=*/false, 0, callbacks)) + return die_c_("mismatched write should have failed", FLAC__metadata_chain_status(chain)); + if(FLAC__metadata_chain_status(chain) != FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH) + return die_c_("expected FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH", FLAC__metadata_chain_status(chain)); + printf(" OK: FLAC__metadata_chain_write_with_callbacks() returned false,FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH like it should\n"); + } + + printf("read chain (filename-based)\n"); + + if(!FLAC__metadata_chain_read(chain, flacfilename(is_ogg))) + return die_c_("reading chain", FLAC__metadata_chain_status(chain)); + + printf("write chain with wrong method FLAC__metadata_chain_write_with_callbacks_and_tempfile()\n"); + { + if(FLAC__metadata_chain_write_with_callbacks_and_tempfile(chain, /*use_padding=*/false, 0, callbacks, 0, callbacks)) + return die_c_("mismatched write should have failed", FLAC__metadata_chain_status(chain)); + if(FLAC__metadata_chain_status(chain) != FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH) + return die_c_("expected FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH", FLAC__metadata_chain_status(chain)); + printf(" OK: FLAC__metadata_chain_write_with_callbacks_and_tempfile() returned false,FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH like it should\n"); + } + + printf("read chain (callback-based)\n"); + { + FILE *file = fopen(flacfilename(is_ogg), "rb"); + if(0 == file) + return die_("opening file"); + if(!FLAC__metadata_chain_read_with_callbacks(chain, (FLAC__IOHandle)file, callbacks)) { + fclose(file); + return die_c_("reading chain", FLAC__metadata_chain_status(chain)); + } + fclose(file); + } + + printf("write chain with wrong method FLAC__metadata_chain_write()\n"); + { + if(FLAC__metadata_chain_write(chain, /*use_padding=*/false, /*preserve_file_stats=*/false)) + return die_c_("mismatched write should have failed", FLAC__metadata_chain_status(chain)); + if(FLAC__metadata_chain_status(chain) != FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH) + return die_c_("expected FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH", FLAC__metadata_chain_status(chain)); + printf(" OK: FLAC__metadata_chain_write() returned false,FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH like it should\n"); + } + + printf("read chain (callback-based)\n"); + { + FILE *file = fopen(flacfilename(is_ogg), "rb"); + if(0 == file) + return die_("opening file"); + if(!FLAC__metadata_chain_read_with_callbacks(chain, (FLAC__IOHandle)file, callbacks)) { + fclose(file); + return die_c_("reading chain", FLAC__metadata_chain_status(chain)); + } + fclose(file); + } + + printf("testing FLAC__metadata_chain_check_if_tempfile_needed()... "); + + if(!FLAC__metadata_chain_check_if_tempfile_needed(chain, /*use_padding=*/false)) + printf("OK: FLAC__metadata_chain_check_if_tempfile_needed() returned false like it should\n"); + else + return die_("FLAC__metadata_chain_check_if_tempfile_needed() returned true but shouldn't have"); + + printf("write chain with wrong method FLAC__metadata_chain_write_with_callbacks_and_tempfile()\n"); + { + if(FLAC__metadata_chain_write_with_callbacks_and_tempfile(chain, /*use_padding=*/false, 0, callbacks, 0, callbacks)) + return die_c_("mismatched write should have failed", FLAC__metadata_chain_status(chain)); + if(FLAC__metadata_chain_status(chain) != FLAC__METADATA_CHAIN_STATUS_WRONG_WRITE_CALL) + return die_c_("expected FLAC__METADATA_CHAIN_STATUS_WRONG_WRITE_CALL", FLAC__metadata_chain_status(chain)); + printf(" OK: FLAC__metadata_chain_write_with_callbacks_and_tempfile() returned false,FLAC__METADATA_CHAIN_STATUS_WRONG_WRITE_CALL like it should\n"); + } + + printf("read chain (callback-based)\n"); + { + FILE *file = fopen(flacfilename(is_ogg), "rb"); + if(0 == file) + return die_("opening file"); + if(!FLAC__metadata_chain_read_with_callbacks(chain, (FLAC__IOHandle)file, callbacks)) { + fclose(file); + return die_c_("reading chain", FLAC__metadata_chain_status(chain)); + } + fclose(file); + } + + printf("create iterator\n"); + if(0 == (iterator = FLAC__metadata_iterator_new())) + return die_("allocating memory for iterator"); + + FLAC__metadata_iterator_init(iterator, chain); + + printf("[S]VP\tnext\n"); + if(!FLAC__metadata_iterator_next(iterator)) + return die_("iterator ended early\n"); + + printf("S[V]P\tdelete VORBIS_COMMENT, write\n"); + if(!FLAC__metadata_iterator_delete_block(iterator, /*replace_with_padding=*/false)) + return die_c_("block delete failed\n", FLAC__metadata_chain_status(chain)); + + printf("testing FLAC__metadata_chain_check_if_tempfile_needed()... "); + + if(FLAC__metadata_chain_check_if_tempfile_needed(chain, /*use_padding=*/false)) + printf("OK: FLAC__metadata_chain_check_if_tempfile_needed() returned true like it should\n"); + else + return die_("FLAC__metadata_chain_check_if_tempfile_needed() returned false but shouldn't have"); + + printf("write chain with wrong method FLAC__metadata_chain_write_with_callbacks()\n"); + { + if(FLAC__metadata_chain_write_with_callbacks(chain, /*use_padding=*/false, 0, callbacks)) + return die_c_("mismatched write should have failed", FLAC__metadata_chain_status(chain)); + if(FLAC__metadata_chain_status(chain) != FLAC__METADATA_CHAIN_STATUS_WRONG_WRITE_CALL) + return die_c_("expected FLAC__METADATA_CHAIN_STATUS_WRONG_WRITE_CALL", FLAC__metadata_chain_status(chain)); + printf(" OK: FLAC__metadata_chain_write_with_callbacks() returned false,FLAC__METADATA_CHAIN_STATUS_WRONG_WRITE_CALL like it should\n"); + } + + printf("delete iterator\n"); + + FLAC__metadata_iterator_delete(iterator); + + printf("delete chain\n"); + + FLAC__metadata_chain_delete(chain); + + if(!remove_file_(flacfilename(is_ogg))) + return false; + + return true; +} + +FLAC__bool test_metadata_file_manipulation(void) +{ + printf("\n+++ libFLAC unit test: metadata manipulation\n\n"); + + our_metadata_.num_blocks = 0; + + if(!test_level_0_()) + return false; + + if(!test_level_1_()) + return false; + + if(!test_level_2_(/*filename_based=*/true, /*is_ogg=*/false)) /* filename-based */ + return false; + if(!test_level_2_(/*filename_based=*/false, /*is_ogg=*/false)) /* callback-based */ + return false; + if(!test_level_2_misc_(/*is_ogg=*/false)) + return false; + + if(FLAC_API_SUPPORTS_OGG_FLAC) { + if(!test_level_2_(/*filename_based=*/true, /*is_ogg=*/true)) /* filename-based */ + return false; + if(!test_level_2_(/*filename_based=*/false, /*is_ogg=*/true)) /* callback-based */ + return false; +#if 0 + /* when ogg flac write is supported, will have to add this: */ + if(!test_level_2_misc_(/*is_ogg=*/true)) + return false; +#endif + } + + return true; +} diff --git a/src/FLAC/src/test_libFLAC/metadata_object.c b/src/FLAC/src/test_libFLAC/metadata_object.c new file mode 100644 index 00000000..e3089aa8 --- /dev/null +++ b/src/FLAC/src/test_libFLAC/metadata_object.c @@ -0,0 +1,2299 @@ +/* test_libFLAC - Unit tester for libFLAC + * Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include "FLAC/assert.h" +#include "FLAC/metadata.h" +#include "test_libs_common/metadata_utils.h" +#include "metadata.h" +#include +#include /* for malloc() */ +#include /* for memcmp() */ + +static FLAC__byte *make_dummydata_(FLAC__byte *dummydata, unsigned len) +{ + FLAC__byte *ret; + + if(0 == (ret = (FLAC__byte*)malloc(len))) { + printf("FAILED, malloc error\n"); + exit(1); + } + else + memcpy(ret, dummydata, len); + + return ret; +} + +static FLAC__bool compare_track_(const FLAC__StreamMetadata_CueSheet_Track *from, const FLAC__StreamMetadata_CueSheet_Track *to) +{ + unsigned i; + + if(from->offset != to->offset) { +#ifdef _MSC_VER + printf("FAILED, track offset mismatch, expected %I64u, got %I64u\n", to->offset, from->offset); +#else + printf("FAILED, track offset mismatch, expected %llu, got %llu\n", (unsigned long long)to->offset, (unsigned long long)from->offset); +#endif + return false; + } + if(from->number != to->number) { + printf("FAILED, track number mismatch, expected %u, got %u\n", (unsigned)to->number, (unsigned)from->number); + return false; + } + if(0 != strcmp(from->isrc, to->isrc)) { + printf("FAILED, track number mismatch, expected %s, got %s\n", to->isrc, from->isrc); + return false; + } + if(from->type != to->type) { + printf("FAILED, track type mismatch, expected %u, got %u\n", (unsigned)to->type, (unsigned)from->type); + return false; + } + if(from->pre_emphasis != to->pre_emphasis) { + printf("FAILED, track pre_emphasis mismatch, expected %u, got %u\n", (unsigned)to->pre_emphasis, (unsigned)from->pre_emphasis); + return false; + } + if(from->num_indices != to->num_indices) { + printf("FAILED, track num_indices mismatch, expected %u, got %u\n", (unsigned)to->num_indices, (unsigned)from->num_indices); + return false; + } + if(0 == to->indices || 0 == from->indices) { + if(to->indices != from->indices) { + printf("FAILED, track indices mismatch\n"); + return false; + } + } + else { + for(i = 0; i < to->num_indices; i++) { + if(from->indices[i].offset != to->indices[i].offset) { +#ifdef _MSC_VER + printf("FAILED, track indices[%u].offset mismatch, expected %I64u, got %I64u\n", i, to->indices[i].offset, from->indices[i].offset); +#else + printf("FAILED, track indices[%u].offset mismatch, expected %llu, got %llu\n", i, (unsigned long long)to->indices[i].offset, (unsigned long long)from->indices[i].offset); +#endif + return false; + } + if(from->indices[i].number != to->indices[i].number) { + printf("FAILED, track indices[%u].number mismatch, expected %u, got %u\n", i, (unsigned)to->indices[i].number, (unsigned)from->indices[i].number); + return false; + } + } + } + + return true; +} + +static FLAC__bool compare_seekpoint_array_(const FLAC__StreamMetadata_SeekPoint *from, const FLAC__StreamMetadata_SeekPoint *to, unsigned n) +{ + unsigned i; + + FLAC__ASSERT(0 != from); + FLAC__ASSERT(0 != to); + + for(i = 0; i < n; i++) { + if(from[i].sample_number != to[i].sample_number) { +#ifdef _MSC_VER + printf("FAILED, point[%u].sample_number mismatch, expected %I64u, got %I64u\n", i, to[i].sample_number, from[i].sample_number); +#else + printf("FAILED, point[%u].sample_number mismatch, expected %llu, got %llu\n", i, (unsigned long long)to[i].sample_number, (unsigned long long)from[i].sample_number); +#endif + return false; + } + if(from[i].stream_offset != to[i].stream_offset) { +#ifdef _MSC_VER + printf("FAILED, point[%u].stream_offset mismatch, expected %I64u, got %I64u\n", i, to[i].stream_offset, from[i].stream_offset); +#else + printf("FAILED, point[%u].stream_offset mismatch, expected %llu, got %llu\n", i, (unsigned long long)to[i].stream_offset, (unsigned long long)from[i].stream_offset); +#endif + return false; + } + if(from[i].frame_samples != to[i].frame_samples) { + printf("FAILED, point[%u].frame_samples mismatch, expected %u, got %u\n", i, to[i].frame_samples, from[i].frame_samples); + return false; + } + } + + return true; +} + +static FLAC__bool check_seektable_(const FLAC__StreamMetadata *block, unsigned num_points, const FLAC__StreamMetadata_SeekPoint *array) +{ + const unsigned expected_length = num_points * FLAC__STREAM_METADATA_SEEKPOINT_LENGTH; + + if(block->length != expected_length) { + printf("FAILED, bad length, expected %u, got %u\n", expected_length, block->length); + return false; + } + if(block->data.seek_table.num_points != num_points) { + printf("FAILED, expected %u point, got %u\n", num_points, block->data.seek_table.num_points); + return false; + } + if(0 == array) { + if(0 != block->data.seek_table.points) { + printf("FAILED, 'points' pointer is not null\n"); + return false; + } + } + else { + if(!compare_seekpoint_array_(block->data.seek_table.points, array, num_points)) + return false; + } + printf("OK\n"); + + return true; +} + +static void entry_new_(FLAC__StreamMetadata_VorbisComment_Entry *entry, const char *field) +{ + entry->length = strlen(field); + entry->entry = (FLAC__byte*)malloc(entry->length+1); + FLAC__ASSERT(0 != entry->entry); + memcpy(entry->entry, field, entry->length); + entry->entry[entry->length] = '\0'; +} + +static void entry_clone_(FLAC__StreamMetadata_VorbisComment_Entry *entry) +{ + FLAC__byte *x = (FLAC__byte*)malloc(entry->length+1); + FLAC__ASSERT(0 != x); + memcpy(x, entry->entry, entry->length); + x[entry->length] = '\0'; + entry->entry = x; +} + +static void vc_calc_len_(FLAC__StreamMetadata *block) +{ + const FLAC__StreamMetadata_VorbisComment *vc = &block->data.vorbis_comment; + unsigned i; + + block->length = FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN / 8; + block->length += vc->vendor_string.length; + block->length += FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN / 8; + for(i = 0; i < vc->num_comments; i++) { + block->length += FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN / 8; + block->length += vc->comments[i].length; + } +} + +static void vc_resize_(FLAC__StreamMetadata *block, unsigned num) +{ + FLAC__StreamMetadata_VorbisComment *vc = &block->data.vorbis_comment; + + if(vc->num_comments != 0) { + FLAC__ASSERT(0 != vc->comments); + if(num < vc->num_comments) { + unsigned i; + for(i = num; i < vc->num_comments; i++) { + if(0 != vc->comments[i].entry) + free(vc->comments[i].entry); + } + } + } + if(num == 0) { + if(0 != vc->comments) { + free(vc->comments); + vc->comments = 0; + } + } + else { + vc->comments = (FLAC__StreamMetadata_VorbisComment_Entry*)realloc(vc->comments, sizeof(FLAC__StreamMetadata_VorbisComment_Entry)*num); + FLAC__ASSERT(0 != vc->comments); + if(num > vc->num_comments) + memset(vc->comments+vc->num_comments, 0, sizeof(FLAC__StreamMetadata_VorbisComment_Entry)*(num-vc->num_comments)); + } + + vc->num_comments = num; + vc_calc_len_(block); +} + +static int vc_find_from_(FLAC__StreamMetadata *block, const char *name, unsigned start) +{ + const unsigned n = strlen(name); + unsigned i; + for(i = start; i < block->data.vorbis_comment.num_comments; i++) { + const FLAC__StreamMetadata_VorbisComment_Entry *entry = &block->data.vorbis_comment.comments[i]; + if(entry->length > n && 0 == strncmp((const char *)entry->entry, name, n) && entry->entry[n] == '=') + return (int)i; + } + return -1; +} + +static void vc_set_vs_new_(FLAC__StreamMetadata_VorbisComment_Entry *entry, FLAC__StreamMetadata *block, const char *field) +{ + if(0 != block->data.vorbis_comment.vendor_string.entry) + free(block->data.vorbis_comment.vendor_string.entry); + entry_new_(entry, field); + block->data.vorbis_comment.vendor_string = *entry; + vc_calc_len_(block); +} + +static void vc_set_new_(FLAC__StreamMetadata_VorbisComment_Entry *entry, FLAC__StreamMetadata *block, unsigned pos, const char *field) +{ + if(0 != block->data.vorbis_comment.comments[pos].entry) + free(block->data.vorbis_comment.comments[pos].entry); + entry_new_(entry, field); + block->data.vorbis_comment.comments[pos] = *entry; + vc_calc_len_(block); +} + +static void vc_insert_new_(FLAC__StreamMetadata_VorbisComment_Entry *entry, FLAC__StreamMetadata *block, unsigned pos, const char *field) +{ + vc_resize_(block, block->data.vorbis_comment.num_comments+1); + memmove(&block->data.vorbis_comment.comments[pos+1], &block->data.vorbis_comment.comments[pos], sizeof(FLAC__StreamMetadata_VorbisComment_Entry)*(block->data.vorbis_comment.num_comments-1-pos)); + memset(&block->data.vorbis_comment.comments[pos], 0, sizeof(FLAC__StreamMetadata_VorbisComment_Entry)); + vc_set_new_(entry, block, pos, field); + vc_calc_len_(block); +} + +static void vc_delete_(FLAC__StreamMetadata *block, unsigned pos) +{ + if(0 != block->data.vorbis_comment.comments[pos].entry) + free(block->data.vorbis_comment.comments[pos].entry); + memmove(&block->data.vorbis_comment.comments[pos], &block->data.vorbis_comment.comments[pos+1], sizeof(FLAC__StreamMetadata_VorbisComment_Entry)*(block->data.vorbis_comment.num_comments-pos-1)); + block->data.vorbis_comment.comments[block->data.vorbis_comment.num_comments-1].entry = 0; + block->data.vorbis_comment.comments[block->data.vorbis_comment.num_comments-1].length = 0; + vc_resize_(block, block->data.vorbis_comment.num_comments-1); + vc_calc_len_(block); +} + +static void vc_replace_new_(FLAC__StreamMetadata_VorbisComment_Entry *entry, FLAC__StreamMetadata *block, const char *field, FLAC__bool all) +{ + int indx; + char field_name[256]; + const char *eq = strchr(field, '='); + FLAC__ASSERT(eq>field && (unsigned)(eq-field) < sizeof(field_name)); + memcpy(field_name, field, eq-field); + field_name[eq-field]='\0'; + + indx = vc_find_from_(block, field_name, 0); + if(indx < 0) + vc_insert_new_(entry, block, block->data.vorbis_comment.num_comments, field); + else { + vc_set_new_(entry, block, (unsigned)indx, field); + if(all) { + for(indx = indx+1; indx >= 0 && (unsigned)indx < block->data.vorbis_comment.num_comments; ) + if((indx = vc_find_from_(block, field_name, (unsigned)indx)) >= 0) + vc_delete_(block, (unsigned)indx); + } + } + + vc_calc_len_(block); +} + +static void track_new_(FLAC__StreamMetadata_CueSheet_Track *track, FLAC__uint64 offset, FLAC__byte number, const char *isrc, FLAC__bool data, FLAC__bool pre_em) +{ + track->offset = offset; + track->number = number; + memcpy(track->isrc, isrc, sizeof(track->isrc)); + track->type = data; + track->pre_emphasis = pre_em; + track->num_indices = 0; + track->indices = 0; +} + +static void track_clone_(FLAC__StreamMetadata_CueSheet_Track *track) +{ + if(track->num_indices > 0) { + size_t bytes = sizeof(FLAC__StreamMetadata_CueSheet_Index) * track->num_indices; + FLAC__StreamMetadata_CueSheet_Index *x = (FLAC__StreamMetadata_CueSheet_Index*)malloc(bytes); + FLAC__ASSERT(0 != x); + memcpy(x, track->indices, bytes); + track->indices = x; + } +} + +static void cs_calc_len_(FLAC__StreamMetadata *block) +{ + const FLAC__StreamMetadata_CueSheet *cs = &block->data.cue_sheet; + unsigned i; + + block->length = ( + FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN + + FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN + + FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN + + FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN + + FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN + ) / 8; + block->length += cs->num_tracks * ( + FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN + + FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN + + FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN + + FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN + + FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN + + FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN + + FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN + ) / 8; + for(i = 0; i < cs->num_tracks; i++) { + block->length += cs->tracks[i].num_indices * ( + FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN + + FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN + + FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN + ) / 8; + } +} + +static void tr_resize_(FLAC__StreamMetadata *block, unsigned track_num, unsigned num) +{ + FLAC__StreamMetadata_CueSheet_Track *tr; + + FLAC__ASSERT(track_num < block->data.cue_sheet.num_tracks); + + tr = &block->data.cue_sheet.tracks[track_num]; + + if(tr->num_indices != 0) { + FLAC__ASSERT(0 != tr->indices); + } + if(num == 0) { + if(0 != tr->indices) { + free(tr->indices); + tr->indices = 0; + } + } + else { + tr->indices = (FLAC__StreamMetadata_CueSheet_Index*)realloc(tr->indices, sizeof(FLAC__StreamMetadata_CueSheet_Index)*num); + FLAC__ASSERT(0 != tr->indices); + if(num > tr->num_indices) + memset(tr->indices+tr->num_indices, 0, sizeof(FLAC__StreamMetadata_CueSheet_Index)*(num-tr->num_indices)); + } + + tr->num_indices = num; + cs_calc_len_(block); +} + +static void tr_set_new_(FLAC__StreamMetadata *block, unsigned track_num, unsigned pos, FLAC__StreamMetadata_CueSheet_Index indx) +{ + FLAC__StreamMetadata_CueSheet_Track *tr; + + FLAC__ASSERT(track_num < block->data.cue_sheet.num_tracks); + + tr = &block->data.cue_sheet.tracks[track_num]; + + FLAC__ASSERT(pos < tr->num_indices); + + tr->indices[pos] = indx; + + cs_calc_len_(block); +} + +static void tr_insert_new_(FLAC__StreamMetadata *block, unsigned track_num, unsigned pos, FLAC__StreamMetadata_CueSheet_Index indx) +{ + FLAC__StreamMetadata_CueSheet_Track *tr; + + FLAC__ASSERT(track_num < block->data.cue_sheet.num_tracks); + + tr = &block->data.cue_sheet.tracks[track_num]; + + FLAC__ASSERT(pos <= tr->num_indices); + + tr_resize_(block, track_num, tr->num_indices+1); + memmove(&tr->indices[pos+1], &tr->indices[pos], sizeof(FLAC__StreamMetadata_CueSheet_Index)*(tr->num_indices-1-pos)); + tr_set_new_(block, track_num, pos, indx); + cs_calc_len_(block); +} + +static void tr_delete_(FLAC__StreamMetadata *block, unsigned track_num, unsigned pos) +{ + FLAC__StreamMetadata_CueSheet_Track *tr; + + FLAC__ASSERT(track_num < block->data.cue_sheet.num_tracks); + + tr = &block->data.cue_sheet.tracks[track_num]; + + FLAC__ASSERT(pos <= tr->num_indices); + + memmove(&tr->indices[pos], &tr->indices[pos+1], sizeof(FLAC__StreamMetadata_CueSheet_Index)*(tr->num_indices-pos-1)); + tr_resize_(block, track_num, tr->num_indices-1); + cs_calc_len_(block); +} + +static void cs_resize_(FLAC__StreamMetadata *block, unsigned num) +{ + FLAC__StreamMetadata_CueSheet *cs = &block->data.cue_sheet; + + if(cs->num_tracks != 0) { + FLAC__ASSERT(0 != cs->tracks); + if(num < cs->num_tracks) { + unsigned i; + for(i = num; i < cs->num_tracks; i++) { + if(0 != cs->tracks[i].indices) + free(cs->tracks[i].indices); + } + } + } + if(num == 0) { + if(0 != cs->tracks) { + free(cs->tracks); + cs->tracks = 0; + } + } + else { + cs->tracks = (FLAC__StreamMetadata_CueSheet_Track*)realloc(cs->tracks, sizeof(FLAC__StreamMetadata_CueSheet_Track)*num); + FLAC__ASSERT(0 != cs->tracks); + if(num > cs->num_tracks) + memset(cs->tracks+cs->num_tracks, 0, sizeof(FLAC__StreamMetadata_CueSheet_Track)*(num-cs->num_tracks)); + } + + cs->num_tracks = num; + cs_calc_len_(block); +} + +static void cs_set_new_(FLAC__StreamMetadata_CueSheet_Track *track, FLAC__StreamMetadata *block, unsigned pos, FLAC__uint64 offset, FLAC__byte number, const char *isrc, FLAC__bool data, FLAC__bool pre_em) +{ + track_new_(track, offset, number, isrc, data, pre_em); + block->data.cue_sheet.tracks[pos] = *track; + cs_calc_len_(block); +} + +static void cs_insert_new_(FLAC__StreamMetadata_CueSheet_Track *track, FLAC__StreamMetadata *block, unsigned pos, FLAC__uint64 offset, FLAC__byte number, const char *isrc, FLAC__bool data, FLAC__bool pre_em) +{ + cs_resize_(block, block->data.cue_sheet.num_tracks+1); + memmove(&block->data.cue_sheet.tracks[pos+1], &block->data.cue_sheet.tracks[pos], sizeof(FLAC__StreamMetadata_CueSheet_Track)*(block->data.cue_sheet.num_tracks-1-pos)); + cs_set_new_(track, block, pos, offset, number, isrc, data, pre_em); + cs_calc_len_(block); +} + +static void cs_delete_(FLAC__StreamMetadata *block, unsigned pos) +{ + if(0 != block->data.cue_sheet.tracks[pos].indices) + free(block->data.cue_sheet.tracks[pos].indices); + memmove(&block->data.cue_sheet.tracks[pos], &block->data.cue_sheet.tracks[pos+1], sizeof(FLAC__StreamMetadata_CueSheet_Track)*(block->data.cue_sheet.num_tracks-pos-1)); + block->data.cue_sheet.tracks[block->data.cue_sheet.num_tracks-1].indices = 0; + block->data.cue_sheet.tracks[block->data.cue_sheet.num_tracks-1].num_indices = 0; + cs_resize_(block, block->data.cue_sheet.num_tracks-1); + cs_calc_len_(block); +} + +static void pi_set_mime_type(FLAC__StreamMetadata *block, const char *s) +{ + if(block->data.picture.mime_type) { + block->length -= strlen(block->data.picture.mime_type); + free(block->data.picture.mime_type); + } + block->data.picture.mime_type = strdup(s); + FLAC__ASSERT(block->data.picture.mime_type); + block->length += strlen(block->data.picture.mime_type); +} + +static void pi_set_description(FLAC__StreamMetadata *block, const FLAC__byte *s) +{ + if(block->data.picture.description) { + block->length -= strlen((const char *)block->data.picture.description); + free(block->data.picture.description); + } + block->data.picture.description = (FLAC__byte*)strdup((const char *)s); + FLAC__ASSERT(block->data.picture.description); + block->length += strlen((const char *)block->data.picture.description); +} + +static void pi_set_data(FLAC__StreamMetadata *block, const FLAC__byte *data, FLAC__uint32 len) +{ + if(block->data.picture.data) { + block->length -= block->data.picture.data_length; + free(block->data.picture.data); + } + block->data.picture.data = (FLAC__byte*)strdup((const char *)data); + FLAC__ASSERT(block->data.picture.data); + block->data.picture.data_length = len; + block->length += len; +} + +FLAC__bool test_metadata_object(void) +{ + FLAC__StreamMetadata *block, *blockcopy, *vorbiscomment, *cuesheet, *picture; + FLAC__StreamMetadata_SeekPoint seekpoint_array[14]; + FLAC__StreamMetadata_VorbisComment_Entry entry; + FLAC__StreamMetadata_CueSheet_Index indx; + FLAC__StreamMetadata_CueSheet_Track track; + unsigned i, expected_length, seekpoints; + int j; + static FLAC__byte dummydata[4] = { 'a', 'b', 'c', 'd' }; + + printf("\n+++ libFLAC unit test: metadata objects\n\n"); + + + printf("testing STREAMINFO\n"); + + printf("testing FLAC__metadata_object_new()... "); + block = FLAC__metadata_object_new(FLAC__METADATA_TYPE_STREAMINFO); + if(0 == block) { + printf("FAILED, returned NULL\n"); + return false; + } + expected_length = FLAC__STREAM_METADATA_STREAMINFO_LENGTH; + if(block->length != expected_length) { + printf("FAILED, bad length, expected %u, got %u\n", expected_length, block->length); + return false; + } + printf("OK\n"); + + printf("testing FLAC__metadata_object_clone()... "); + blockcopy = FLAC__metadata_object_clone(block); + if(0 == blockcopy) { + printf("FAILED, returned NULL\n"); + return false; + } + if(!mutils__compare_block(block, blockcopy)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_delete()... "); + FLAC__metadata_object_delete(blockcopy); + FLAC__metadata_object_delete(block); + printf("OK\n"); + + + printf("testing PADDING\n"); + + printf("testing FLAC__metadata_object_new()... "); + block = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING); + if(0 == block) { + printf("FAILED, returned NULL\n"); + return false; + } + expected_length = 0; + if(block->length != expected_length) { + printf("FAILED, bad length, expected %u, got %u\n", expected_length, block->length); + return false; + } + printf("OK\n"); + + printf("testing FLAC__metadata_object_clone()... "); + blockcopy = FLAC__metadata_object_clone(block); + if(0 == blockcopy) { + printf("FAILED, returned NULL\n"); + return false; + } + if(!mutils__compare_block(block, blockcopy)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_delete()... "); + FLAC__metadata_object_delete(blockcopy); + FLAC__metadata_object_delete(block); + printf("OK\n"); + + + printf("testing APPLICATION\n"); + + printf("testing FLAC__metadata_object_new()... "); + block = FLAC__metadata_object_new(FLAC__METADATA_TYPE_APPLICATION); + if(0 == block) { + printf("FAILED, returned NULL\n"); + return false; + } + expected_length = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8; + if(block->length != expected_length) { + printf("FAILED, bad length, expected %u, got %u\n", expected_length, block->length); + return false; + } + printf("OK\n"); + + printf("testing FLAC__metadata_object_clone()... "); + blockcopy = FLAC__metadata_object_clone(block); + if(0 == blockcopy) { + printf("FAILED, returned NULL\n"); + return false; + } + if(!mutils__compare_block(block, blockcopy)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_delete()... "); + FLAC__metadata_object_delete(blockcopy); + printf("OK\n"); + + printf("testing FLAC__metadata_object_application_set_data(copy)... "); + if(!FLAC__metadata_object_application_set_data(block, dummydata, sizeof(dummydata), true/*copy*/)) { + printf("FAILED, returned false\n"); + return false; + } + expected_length = (FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8) + sizeof(dummydata); + if(block->length != expected_length) { + printf("FAILED, bad length, expected %u, got %u\n", expected_length, block->length); + return false; + } + if(0 != memcmp(block->data.application.data, dummydata, sizeof(dummydata))) { + printf("FAILED, data mismatch\n"); + return false; + } + printf("OK\n"); + + printf("testing FLAC__metadata_object_clone()... "); + blockcopy = FLAC__metadata_object_clone(block); + if(0 == blockcopy) { + printf("FAILED, returned NULL\n"); + return false; + } + if(!mutils__compare_block(block, blockcopy)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_delete()... "); + FLAC__metadata_object_delete(blockcopy); + printf("OK\n"); + + printf("testing FLAC__metadata_object_application_set_data(own)... "); + if(!FLAC__metadata_object_application_set_data(block, make_dummydata_(dummydata, sizeof(dummydata)), sizeof(dummydata), false/*own*/)) { + printf("FAILED, returned false\n"); + return false; + } + expected_length = (FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8) + sizeof(dummydata); + if(block->length != expected_length) { + printf("FAILED, bad length, expected %u, got %u\n", expected_length, block->length); + return false; + } + if(0 != memcmp(block->data.application.data, dummydata, sizeof(dummydata))) { + printf("FAILED, data mismatch\n"); + return false; + } + printf("OK\n"); + + printf("testing FLAC__metadata_object_clone()... "); + blockcopy = FLAC__metadata_object_clone(block); + if(0 == blockcopy) { + printf("FAILED, returned NULL\n"); + return false; + } + if(!mutils__compare_block(block, blockcopy)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_delete()... "); + FLAC__metadata_object_delete(blockcopy); + FLAC__metadata_object_delete(block); + printf("OK\n"); + + + printf("testing SEEKTABLE\n"); + + for(i = 0; i < sizeof(seekpoint_array) / sizeof(FLAC__StreamMetadata_SeekPoint); i++) { + seekpoint_array[i].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER; + seekpoint_array[i].stream_offset = 0; + seekpoint_array[i].frame_samples = 0; + } + + seekpoints = 0; + printf("testing FLAC__metadata_object_new()... "); + block = FLAC__metadata_object_new(FLAC__METADATA_TYPE_SEEKTABLE); + if(0 == block) { + printf("FAILED, returned NULL\n"); + return false; + } + if(!check_seektable_(block, seekpoints, 0)) + return false; + + printf("testing FLAC__metadata_object_clone()... "); + blockcopy = FLAC__metadata_object_clone(block); + if(0 == blockcopy) { + printf("FAILED, returned NULL\n"); + return false; + } + if(!mutils__compare_block(block, blockcopy)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_delete()... "); + FLAC__metadata_object_delete(blockcopy); + printf("OK\n"); + + seekpoints = 2; + printf("testing FLAC__metadata_object_seektable_resize_points(grow to %u)...", seekpoints); + if(!FLAC__metadata_object_seektable_resize_points(block, seekpoints)) { + printf("FAILED, returned false\n"); + return false; + } + if(!check_seektable_(block, seekpoints, seekpoint_array)) + return false; + + seekpoints = 1; + printf("testing FLAC__metadata_object_seektable_resize_points(shrink to %u)...", seekpoints); + if(!FLAC__metadata_object_seektable_resize_points(block, seekpoints)) { + printf("FAILED, returned false\n"); + return false; + } + if(!check_seektable_(block, seekpoints, seekpoint_array)) + return false; + + printf("testing FLAC__metadata_object_seektable_is_legal()..."); + if(!FLAC__metadata_object_seektable_is_legal(block)) { + printf("FAILED, returned false\n"); + return false; + } + printf("OK\n"); + + seekpoints = 0; + printf("testing FLAC__metadata_object_seektable_resize_points(shrink to %u)...", seekpoints); + if(!FLAC__metadata_object_seektable_resize_points(block, seekpoints)) { + printf("FAILED, returned false\n"); + return false; + } + if(!check_seektable_(block, seekpoints, 0)) + return false; + + seekpoints++; + printf("testing FLAC__metadata_object_seektable_insert_point() on empty array..."); + if(!FLAC__metadata_object_seektable_insert_point(block, 0, seekpoint_array[0])) { + printf("FAILED, returned false\n"); + return false; + } + if(!check_seektable_(block, seekpoints, seekpoint_array)) + return false; + + seekpoint_array[0].sample_number = 1; + seekpoints++; + printf("testing FLAC__metadata_object_seektable_insert_point() on beginning of non-empty array..."); + if(!FLAC__metadata_object_seektable_insert_point(block, 0, seekpoint_array[0])) { + printf("FAILED, returned false\n"); + return false; + } + if(!check_seektable_(block, seekpoints, seekpoint_array)) + return false; + + seekpoint_array[1].sample_number = 2; + seekpoints++; + printf("testing FLAC__metadata_object_seektable_insert_point() on middle of non-empty array..."); + if(!FLAC__metadata_object_seektable_insert_point(block, 1, seekpoint_array[1])) { + printf("FAILED, returned false\n"); + return false; + } + if(!check_seektable_(block, seekpoints, seekpoint_array)) + return false; + + seekpoint_array[3].sample_number = 3; + seekpoints++; + printf("testing FLAC__metadata_object_seektable_insert_point() on end of non-empty array..."); + if(!FLAC__metadata_object_seektable_insert_point(block, 3, seekpoint_array[3])) { + printf("FAILED, returned false\n"); + return false; + } + if(!check_seektable_(block, seekpoints, seekpoint_array)) + return false; + + printf("testing FLAC__metadata_object_clone()... "); + blockcopy = FLAC__metadata_object_clone(block); + if(0 == blockcopy) { + printf("FAILED, returned NULL\n"); + return false; + } + if(!mutils__compare_block(block, blockcopy)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_delete()... "); + FLAC__metadata_object_delete(blockcopy); + printf("OK\n"); + + seekpoint_array[2].sample_number = seekpoint_array[3].sample_number; + seekpoints--; + printf("testing FLAC__metadata_object_seektable_delete_point() on middle of array..."); + if(!FLAC__metadata_object_seektable_delete_point(block, 2)) { + printf("FAILED, returned false\n"); + return false; + } + if(!check_seektable_(block, seekpoints, seekpoint_array)) + return false; + + seekpoints--; + printf("testing FLAC__metadata_object_seektable_delete_point() on end of array..."); + if(!FLAC__metadata_object_seektable_delete_point(block, 2)) { + printf("FAILED, returned false\n"); + return false; + } + if(!check_seektable_(block, seekpoints, seekpoint_array)) + return false; + + seekpoints--; + printf("testing FLAC__metadata_object_seektable_delete_point() on beginning of array..."); + if(!FLAC__metadata_object_seektable_delete_point(block, 0)) { + printf("FAILED, returned false\n"); + return false; + } + if(!check_seektable_(block, seekpoints, seekpoint_array+1)) + return false; + + printf("testing FLAC__metadata_object_seektable_set_point()..."); + FLAC__metadata_object_seektable_set_point(block, 0, seekpoint_array[0]); + if(!check_seektable_(block, seekpoints, seekpoint_array)) + return false; + + printf("testing FLAC__metadata_object_delete()... "); + FLAC__metadata_object_delete(block); + printf("OK\n"); + + /* seektable template functions */ + + for(i = 0; i < sizeof(seekpoint_array) / sizeof(FLAC__StreamMetadata_SeekPoint); i++) { + seekpoint_array[i].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER; + seekpoint_array[i].stream_offset = 0; + seekpoint_array[i].frame_samples = 0; + } + + seekpoints = 0; + printf("testing FLAC__metadata_object_new()... "); + block = FLAC__metadata_object_new(FLAC__METADATA_TYPE_SEEKTABLE); + if(0 == block) { + printf("FAILED, returned NULL\n"); + return false; + } + if(!check_seektable_(block, seekpoints, 0)) + return false; + + seekpoints += 2; + printf("testing FLAC__metadata_object_seekpoint_template_append_placeholders()... "); + if(!FLAC__metadata_object_seektable_template_append_placeholders(block, 2)) { + printf("FAILED, returned false\n"); + return false; + } + if(!check_seektable_(block, seekpoints, seekpoint_array)) + return false; + + seekpoint_array[seekpoints++].sample_number = 7; + printf("testing FLAC__metadata_object_seekpoint_template_append_point()... "); + if(!FLAC__metadata_object_seektable_template_append_point(block, 7)) { + printf("FAILED, returned false\n"); + return false; + } + if(!check_seektable_(block, seekpoints, seekpoint_array)) + return false; + + { + FLAC__uint64 nums[2] = { 3, 7 }; + seekpoint_array[seekpoints++].sample_number = nums[0]; + seekpoint_array[seekpoints++].sample_number = nums[1]; + printf("testing FLAC__metadata_object_seekpoint_template_append_points()... "); + if(!FLAC__metadata_object_seektable_template_append_points(block, nums, sizeof(nums)/sizeof(FLAC__uint64))) { + printf("FAILED, returned false\n"); + return false; + } + if(!check_seektable_(block, seekpoints, seekpoint_array)) + return false; + } + + seekpoint_array[seekpoints++].sample_number = 0; + seekpoint_array[seekpoints++].sample_number = 10; + seekpoint_array[seekpoints++].sample_number = 20; + printf("testing FLAC__metadata_object_seekpoint_template_append_spaced_points()... "); + if(!FLAC__metadata_object_seektable_template_append_spaced_points(block, 3, 30)) { + printf("FAILED, returned false\n"); + return false; + } + if(!check_seektable_(block, seekpoints, seekpoint_array)) + return false; + + seekpoints--; + seekpoint_array[0].sample_number = 0; + seekpoint_array[1].sample_number = 3; + seekpoint_array[2].sample_number = 7; + seekpoint_array[3].sample_number = 10; + seekpoint_array[4].sample_number = 20; + seekpoint_array[5].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER; + seekpoint_array[6].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER; + printf("testing FLAC__metadata_object_seekpoint_template_sort(compact=true)... "); + if(!FLAC__metadata_object_seektable_template_sort(block, /*compact=*/true)) { + printf("FAILED, returned false\n"); + return false; + } + if(!FLAC__metadata_object_seektable_is_legal(block)) { + printf("FAILED, seek table is illegal\n"); + return false; + } + if(!check_seektable_(block, seekpoints, seekpoint_array)) + return false; + + printf("testing FLAC__metadata_object_seekpoint_template_sort(compact=false)... "); + if(!FLAC__metadata_object_seektable_template_sort(block, /*compact=*/false)) { + printf("FAILED, returned false\n"); + return false; + } + if(!FLAC__metadata_object_seektable_is_legal(block)) { + printf("FAILED, seek table is illegal\n"); + return false; + } + if(!check_seektable_(block, seekpoints, seekpoint_array)) + return false; + + seekpoint_array[seekpoints++].sample_number = 0; + seekpoint_array[seekpoints++].sample_number = 10; + seekpoint_array[seekpoints++].sample_number = 20; + printf("testing FLAC__metadata_object_seekpoint_template_append_spaced_points_by_samples()... "); + if(!FLAC__metadata_object_seektable_template_append_spaced_points_by_samples(block, 10, 30)) { + printf("FAILED, returned false\n"); + return false; + } + if(!check_seektable_(block, seekpoints, seekpoint_array)) + return false; + + seekpoint_array[seekpoints++].sample_number = 0; + seekpoint_array[seekpoints++].sample_number = 11; + seekpoint_array[seekpoints++].sample_number = 22; + printf("testing FLAC__metadata_object_seekpoint_template_append_spaced_points_by_samples()... "); + if(!FLAC__metadata_object_seektable_template_append_spaced_points_by_samples(block, 11, 30)) { + printf("FAILED, returned false\n"); + return false; + } + if(!check_seektable_(block, seekpoints, seekpoint_array)) + return false; + + printf("testing FLAC__metadata_object_delete()... "); + FLAC__metadata_object_delete(block); + printf("OK\n"); + + + printf("testing VORBIS_COMMENT\n"); + + { + FLAC__StreamMetadata_VorbisComment_Entry entry_; + char *field_name, *field_value; + + printf("testing FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair()... "); + if(!FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(&entry_, "name", "value")) { + printf("FAILED, returned false\n"); + return false; + } + if(strcmp((const char *)entry_.entry, "name=value")) { + printf("FAILED, field mismatch\n"); + return false; + } + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair()... "); + if(!FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair(entry_, &field_name, &field_value)) { + printf("FAILED, returned false\n"); + return false; + } + if(strcmp(field_name, "name")) { + printf("FAILED, field name mismatch\n"); + return false; + } + if(strcmp(field_value, "value")) { + printf("FAILED, field value mismatch\n"); + return false; + } + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_entry_matches()... "); + if(!FLAC__metadata_object_vorbiscomment_entry_matches(entry_, field_name, strlen(field_name))) { + printf("FAILED, expected true, returned false\n"); + return false; + } + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_entry_matches()... "); + if(FLAC__metadata_object_vorbiscomment_entry_matches(entry_, "blah", strlen("blah"))) { + printf("FAILED, expected false, returned true\n"); + return false; + } + printf("OK\n"); + + free(entry_.entry); + free(field_name); + free(field_value); + } + + printf("testing FLAC__metadata_object_new()... "); + block = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT); + if(0 == block) { + printf("FAILED, returned NULL\n"); + return false; + } + expected_length = (FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN/8 + strlen(FLAC__VENDOR_STRING) + FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN/8); + if(block->length != expected_length) { + printf("FAILED, bad length, expected %u, got %u\n", expected_length, block->length); + return false; + } + printf("OK\n"); + + printf("testing FLAC__metadata_object_clone()... "); + vorbiscomment = FLAC__metadata_object_clone(block); + if(0 == vorbiscomment) { + printf("FAILED, returned NULL\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + vc_resize_(vorbiscomment, 2); + printf("testing FLAC__metadata_object_vorbiscomment_resize_comments(grow to %u)...", vorbiscomment->data.vorbis_comment.num_comments); + if(!FLAC__metadata_object_vorbiscomment_resize_comments(block, vorbiscomment->data.vorbis_comment.num_comments)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + vc_resize_(vorbiscomment, 1); + printf("testing FLAC__metadata_object_vorbiscomment_resize_comments(shrink to %u)...", vorbiscomment->data.vorbis_comment.num_comments); + if(!FLAC__metadata_object_vorbiscomment_resize_comments(block, vorbiscomment->data.vorbis_comment.num_comments)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + vc_resize_(vorbiscomment, 0); + printf("testing FLAC__metadata_object_vorbiscomment_resize_comments(shrink to %u)...", vorbiscomment->data.vorbis_comment.num_comments); + if(!FLAC__metadata_object_vorbiscomment_resize_comments(block, vorbiscomment->data.vorbis_comment.num_comments)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_append_comment(copy) on empty array..."); + vc_insert_new_(&entry, vorbiscomment, 0, "name1=field1"); + if(!FLAC__metadata_object_vorbiscomment_append_comment(block, entry, /*copy=*/true)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_append_comment(copy) on non-empty array..."); + vc_insert_new_(&entry, vorbiscomment, 1, "name2=field2"); + if(!FLAC__metadata_object_vorbiscomment_append_comment(block, entry, /*copy=*/true)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + vc_resize_(vorbiscomment, 0); + printf("testing FLAC__metadata_object_vorbiscomment_resize_comments(shrink to %u)...", vorbiscomment->data.vorbis_comment.num_comments); + if(!FLAC__metadata_object_vorbiscomment_resize_comments(block, vorbiscomment->data.vorbis_comment.num_comments)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_insert_comment(copy) on empty array..."); + vc_insert_new_(&entry, vorbiscomment, 0, "name1=field1"); + if(!FLAC__metadata_object_vorbiscomment_insert_comment(block, 0, entry, /*copy=*/true)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_insert_comment(copy) on beginning of non-empty array..."); + vc_insert_new_(&entry, vorbiscomment, 0, "name2=field2"); + if(!FLAC__metadata_object_vorbiscomment_insert_comment(block, 0, entry, /*copy=*/true)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_insert_comment(copy) on middle of non-empty array..."); + vc_insert_new_(&entry, vorbiscomment, 1, "name3=field3"); + if(!FLAC__metadata_object_vorbiscomment_insert_comment(block, 1, entry, /*copy=*/true)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_insert_comment(copy) on end of non-empty array..."); + vc_insert_new_(&entry, vorbiscomment, 3, "name4=field4"); + if(!FLAC__metadata_object_vorbiscomment_insert_comment(block, 3, entry, /*copy=*/true)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_insert_comment(copy) on end of non-empty array..."); + vc_insert_new_(&entry, vorbiscomment, 4, "name3=field3dup1"); + if(!FLAC__metadata_object_vorbiscomment_insert_comment(block, 4, entry, /*copy=*/true)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_insert_comment(copy) on end of non-empty array..."); + vc_insert_new_(&entry, vorbiscomment, 5, "name3=field3dup1"); + if(!FLAC__metadata_object_vorbiscomment_insert_comment(block, 5, entry, /*copy=*/true)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_find_entry_from()..."); + if((j = FLAC__metadata_object_vorbiscomment_find_entry_from(block, 0, "name3")) != 1) { + printf("FAILED, expected 1, got %d\n", j); + return false; + } + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_find_entry_from()..."); + if((j = FLAC__metadata_object_vorbiscomment_find_entry_from(block, j+1, "name3")) != 4) { + printf("FAILED, expected 4, got %d\n", j); + return false; + } + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_find_entry_from()..."); + if((j = FLAC__metadata_object_vorbiscomment_find_entry_from(block, j+1, "name3")) != 5) { + printf("FAILED, expected 5, got %d\n", j); + return false; + } + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_find_entry_from()..."); + if((j = FLAC__metadata_object_vorbiscomment_find_entry_from(block, 0, "name2")) != 0) { + printf("FAILED, expected 0, got %d\n", j); + return false; + } + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_find_entry_from()..."); + if((j = FLAC__metadata_object_vorbiscomment_find_entry_from(block, j+1, "name2")) != -1) { + printf("FAILED, expected -1, got %d\n", j); + return false; + } + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_find_entry_from()..."); + if((j = FLAC__metadata_object_vorbiscomment_find_entry_from(block, 0, "blah")) != -1) { + printf("FAILED, expected -1, got %d\n", j); + return false; + } + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_replace_comment(first, copy)..."); + vc_replace_new_(&entry, vorbiscomment, "name3=field3new1", /*all=*/false); + if(!FLAC__metadata_object_vorbiscomment_replace_comment(block, entry, /*all=*/false, /*copy=*/true)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + if(block->data.vorbis_comment.num_comments != 6) { + printf("FAILED, expected 6 comments, got %u\n", block->data.vorbis_comment.num_comments); + return false; + } + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_replace_comment(all, copy)..."); + vc_replace_new_(&entry, vorbiscomment, "name3=field3new2", /*all=*/true); + if(!FLAC__metadata_object_vorbiscomment_replace_comment(block, entry, /*all=*/true, /*copy=*/true)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + if(block->data.vorbis_comment.num_comments != 4) { + printf("FAILED, expected 4 comments, got %u\n", block->data.vorbis_comment.num_comments); + return false; + } + printf("OK\n"); + + printf("testing FLAC__metadata_object_clone()... "); + blockcopy = FLAC__metadata_object_clone(block); + if(0 == blockcopy) { + printf("FAILED, returned NULL\n"); + return false; + } + if(!mutils__compare_block(block, blockcopy)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_delete()... "); + FLAC__metadata_object_delete(blockcopy); + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_delete_comment() on middle of array..."); + vc_delete_(vorbiscomment, 2); + if(!FLAC__metadata_object_vorbiscomment_delete_comment(block, 2)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_delete_comment() on end of array..."); + vc_delete_(vorbiscomment, 2); + if(!FLAC__metadata_object_vorbiscomment_delete_comment(block, 2)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_delete_comment() on beginning of array..."); + vc_delete_(vorbiscomment, 0); + if(!FLAC__metadata_object_vorbiscomment_delete_comment(block, 0)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_append_comment(copy) on non-empty array..."); + vc_insert_new_(&entry, vorbiscomment, 1, "rem0=val0"); + if(!FLAC__metadata_object_vorbiscomment_append_comment(block, entry, /*copy=*/true)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_append_comment(copy) on non-empty array..."); + vc_insert_new_(&entry, vorbiscomment, 2, "rem0=val1"); + if(!FLAC__metadata_object_vorbiscomment_append_comment(block, entry, /*copy=*/true)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_append_comment(copy) on non-empty array..."); + vc_insert_new_(&entry, vorbiscomment, 3, "rem0=val2"); + if(!FLAC__metadata_object_vorbiscomment_append_comment(block, entry, /*copy=*/true)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_remove_entry_matching(\"blah\")..."); + if((j = FLAC__metadata_object_vorbiscomment_remove_entry_matching(block, "blah")) != 0) { + printf("FAILED, expected 0, got %d\n", j); + return false; + } + if(block->data.vorbis_comment.num_comments != 4) { + printf("FAILED, expected 4 comments, got %u\n", block->data.vorbis_comment.num_comments); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_remove_entry_matching(\"rem0\")..."); + vc_delete_(vorbiscomment, 1); + if((j = FLAC__metadata_object_vorbiscomment_remove_entry_matching(block, "rem0")) != 1) { + printf("FAILED, expected 1, got %d\n", j); + return false; + } + if(block->data.vorbis_comment.num_comments != 3) { + printf("FAILED, expected 3 comments, got %u\n", block->data.vorbis_comment.num_comments); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_remove_entries_matching(\"blah\")..."); + if((j = FLAC__metadata_object_vorbiscomment_remove_entries_matching(block, "blah")) != 0) { + printf("FAILED, expected 0, got %d\n", j); + return false; + } + if(block->data.vorbis_comment.num_comments != 3) { + printf("FAILED, expected 3 comments, got %u\n", block->data.vorbis_comment.num_comments); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_remove_entries_matching(\"rem0\")..."); + vc_delete_(vorbiscomment, 1); + vc_delete_(vorbiscomment, 1); + if((j = FLAC__metadata_object_vorbiscomment_remove_entries_matching(block, "rem0")) != 2) { + printf("FAILED, expected 2, got %d\n", j); + return false; + } + if(block->data.vorbis_comment.num_comments != 1) { + printf("FAILED, expected 1 comments, got %u\n", block->data.vorbis_comment.num_comments); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_set_comment(copy)..."); + vc_set_new_(&entry, vorbiscomment, 0, "name5=field5"); + FLAC__metadata_object_vorbiscomment_set_comment(block, 0, entry, /*copy=*/true); + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_set_vendor_string(copy)..."); + vc_set_vs_new_(&entry, vorbiscomment, "name6=field6"); + FLAC__metadata_object_vorbiscomment_set_vendor_string(block, entry, /*copy=*/true); + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_delete()... "); + FLAC__metadata_object_delete(vorbiscomment); + FLAC__metadata_object_delete(block); + printf("OK\n"); + + + printf("testing FLAC__metadata_object_new()... "); + block = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT); + if(0 == block) { + printf("FAILED, returned NULL\n"); + return false; + } + printf("OK\n"); + + printf("testing FLAC__metadata_object_clone()... "); + vorbiscomment = FLAC__metadata_object_clone(block); + if(0 == vorbiscomment) { + printf("FAILED, returned NULL\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_append_comment(own) on empty array..."); + vc_insert_new_(&entry, vorbiscomment, 0, "name1=field1"); + entry_clone_(&entry); + if(!FLAC__metadata_object_vorbiscomment_append_comment(block, entry, /*copy=*/false)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_append_comment(own) on non-empty array..."); + vc_insert_new_(&entry, vorbiscomment, 1, "name2=field2"); + entry_clone_(&entry); + if(!FLAC__metadata_object_vorbiscomment_append_comment(block, entry, /*copy=*/false)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_delete()... "); + FLAC__metadata_object_delete(vorbiscomment); + FLAC__metadata_object_delete(block); + printf("OK\n"); + + printf("testing FLAC__metadata_object_new()... "); + block = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT); + if(0 == block) { + printf("FAILED, returned NULL\n"); + return false; + } + printf("OK\n"); + + printf("testing FLAC__metadata_object_clone()... "); + vorbiscomment = FLAC__metadata_object_clone(block); + if(0 == vorbiscomment) { + printf("FAILED, returned NULL\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_insert_comment(own) on empty array..."); + vc_insert_new_(&entry, vorbiscomment, 0, "name1=field1"); + entry_clone_(&entry); + if(!FLAC__metadata_object_vorbiscomment_insert_comment(block, 0, entry, /*copy=*/false)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_insert_comment(own) on beginning of non-empty array..."); + vc_insert_new_(&entry, vorbiscomment, 0, "name2=field2"); + entry_clone_(&entry); + if(!FLAC__metadata_object_vorbiscomment_insert_comment(block, 0, entry, /*copy=*/false)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_insert_comment(own) on middle of non-empty array..."); + vc_insert_new_(&entry, vorbiscomment, 1, "name3=field3"); + entry_clone_(&entry); + if(!FLAC__metadata_object_vorbiscomment_insert_comment(block, 1, entry, /*copy=*/false)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_insert_comment(own) on end of non-empty array..."); + vc_insert_new_(&entry, vorbiscomment, 3, "name4=field4"); + entry_clone_(&entry); + if(!FLAC__metadata_object_vorbiscomment_insert_comment(block, 3, entry, /*copy=*/false)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_insert_comment(own) on end of non-empty array..."); + vc_insert_new_(&entry, vorbiscomment, 4, "name3=field3dup1"); + entry_clone_(&entry); + if(!FLAC__metadata_object_vorbiscomment_insert_comment(block, 4, entry, /*copy=*/false)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_insert_comment(own) on end of non-empty array..."); + vc_insert_new_(&entry, vorbiscomment, 5, "name3=field3dup1"); + entry_clone_(&entry); + if(!FLAC__metadata_object_vorbiscomment_insert_comment(block, 5, entry, /*copy=*/false)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_replace_comment(first, own)..."); + vc_replace_new_(&entry, vorbiscomment, "name3=field3new1", /*all=*/false); + entry_clone_(&entry); + if(!FLAC__metadata_object_vorbiscomment_replace_comment(block, entry, /*all=*/false, /*copy=*/false)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + if(block->data.vorbis_comment.num_comments != 6) { + printf("FAILED, expected 6 comments, got %u\n", block->data.vorbis_comment.num_comments); + return false; + } + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_replace_comment(all, own)..."); + vc_replace_new_(&entry, vorbiscomment, "name3=field3new2", /*all=*/true); + entry_clone_(&entry); + if(!FLAC__metadata_object_vorbiscomment_replace_comment(block, entry, /*all=*/true, /*copy=*/false)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + if(block->data.vorbis_comment.num_comments != 4) { + printf("FAILED, expected 4 comments, got %u\n", block->data.vorbis_comment.num_comments); + return false; + } + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_delete_comment() on middle of array..."); + vc_delete_(vorbiscomment, 2); + if(!FLAC__metadata_object_vorbiscomment_delete_comment(block, 2)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_delete_comment() on end of array..."); + vc_delete_(vorbiscomment, 2); + if(!FLAC__metadata_object_vorbiscomment_delete_comment(block, 2)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_delete_comment() on beginning of array..."); + vc_delete_(vorbiscomment, 0); + if(!FLAC__metadata_object_vorbiscomment_delete_comment(block, 0)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_set_comment(own)..."); + vc_set_new_(&entry, vorbiscomment, 0, "name5=field5"); + entry_clone_(&entry); + FLAC__metadata_object_vorbiscomment_set_comment(block, 0, entry, /*copy=*/false); + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_vorbiscomment_set_vendor_string(own)..."); + vc_set_vs_new_(&entry, vorbiscomment, "name6=field6"); + entry_clone_(&entry); + FLAC__metadata_object_vorbiscomment_set_vendor_string(block, entry, /*copy=*/false); + if(!mutils__compare_block(vorbiscomment, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_delete()... "); + FLAC__metadata_object_delete(vorbiscomment); + FLAC__metadata_object_delete(block); + printf("OK\n"); + + + printf("testing CUESHEET\n"); + + { + FLAC__StreamMetadata_CueSheet_Track *track_, *trackcopy_; + + printf("testing FLAC__metadata_object_cuesheet_track_new()... "); + track_ = FLAC__metadata_object_cuesheet_track_new(); + if(0 == track_) { + printf("FAILED, returned NULL\n"); + return false; + } + printf("OK\n"); + + printf("testing FLAC__metadata_object_cuesheet_track_clone()... "); + trackcopy_ = FLAC__metadata_object_cuesheet_track_clone(track_); + if(0 == trackcopy_) { + printf("FAILED, returned NULL\n"); + return false; + } + if(!compare_track_(trackcopy_, track_)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_cuesheet_track_delete()... "); + FLAC__metadata_object_cuesheet_track_delete(trackcopy_); + FLAC__metadata_object_cuesheet_track_delete(track_); + printf("OK\n"); + } + + + printf("testing FLAC__metadata_object_new()... "); + block = FLAC__metadata_object_new(FLAC__METADATA_TYPE_CUESHEET); + if(0 == block) { + printf("FAILED, returned NULL\n"); + return false; + } + expected_length = ( + FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN + + FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN + + FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN + + FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN + + FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN + ) / 8; + if(block->length != expected_length) { + printf("FAILED, bad length, expected %u, got %u\n", expected_length, block->length); + return false; + } + printf("OK\n"); + + printf("testing FLAC__metadata_object_clone()... "); + cuesheet = FLAC__metadata_object_clone(block); + if(0 == cuesheet) { + printf("FAILED, returned NULL\n"); + return false; + } + if(!mutils__compare_block(cuesheet, block)) + return false; + printf("OK\n"); + + cs_resize_(cuesheet, 2); + printf("testing FLAC__metadata_object_cuesheet_resize_tracks(grow to %u)...", cuesheet->data.cue_sheet.num_tracks); + if(!FLAC__metadata_object_cuesheet_resize_tracks(block, cuesheet->data.cue_sheet.num_tracks)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(cuesheet, block)) + return false; + printf("OK\n"); + + cs_resize_(cuesheet, 1); + printf("testing FLAC__metadata_object_cuesheet_resize_tracks(shrink to %u)...", cuesheet->data.cue_sheet.num_tracks); + if(!FLAC__metadata_object_cuesheet_resize_tracks(block, cuesheet->data.cue_sheet.num_tracks)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(cuesheet, block)) + return false; + printf("OK\n"); + + cs_resize_(cuesheet, 0); + printf("testing FLAC__metadata_object_cuesheet_resize_tracks(shrink to %u)...", cuesheet->data.cue_sheet.num_tracks); + if(!FLAC__metadata_object_cuesheet_resize_tracks(block, cuesheet->data.cue_sheet.num_tracks)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(cuesheet, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_cuesheet_insert_track(copy) on empty array..."); + cs_insert_new_(&track, cuesheet, 0, 0, 1, "ABCDE1234567", false, false); + if(!FLAC__metadata_object_cuesheet_insert_track(block, 0, &track, /*copy=*/true)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(cuesheet, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_cuesheet_insert_track(copy) on beginning of non-empty array..."); + cs_insert_new_(&track, cuesheet, 0, 10, 2, "BBCDE1234567", false, false); + if(!FLAC__metadata_object_cuesheet_insert_track(block, 0, &track, /*copy=*/true)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(cuesheet, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_cuesheet_insert_track(copy) on middle of non-empty array..."); + cs_insert_new_(&track, cuesheet, 1, 20, 3, "CBCDE1234567", false, false); + if(!FLAC__metadata_object_cuesheet_insert_track(block, 1, &track, /*copy=*/true)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(cuesheet, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_cuesheet_insert_track(copy) on end of non-empty array..."); + cs_insert_new_(&track, cuesheet, 3, 30, 4, "DBCDE1234567", false, false); + if(!FLAC__metadata_object_cuesheet_insert_track(block, 3, &track, /*copy=*/true)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(cuesheet, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_cuesheet_insert_blank_track() on end of non-empty array..."); + cs_insert_new_(&track, cuesheet, 4, 0, 0, "\0\0\0\0\0\0\0\0\0\0\0\0", false, false); + if(!FLAC__metadata_object_cuesheet_insert_blank_track(block, 4)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(cuesheet, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_clone()... "); + blockcopy = FLAC__metadata_object_clone(block); + if(0 == blockcopy) { + printf("FAILED, returned NULL\n"); + return false; + } + if(!mutils__compare_block(block, blockcopy)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_delete()... "); + FLAC__metadata_object_delete(blockcopy); + printf("OK\n"); + + printf("testing FLAC__metadata_object_cuesheet_delete_track() on end of array..."); + cs_delete_(cuesheet, 4); + if(!FLAC__metadata_object_cuesheet_delete_track(block, 4)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(cuesheet, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_cuesheet_delete_track() on middle of array..."); + cs_delete_(cuesheet, 2); + if(!FLAC__metadata_object_cuesheet_delete_track(block, 2)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(cuesheet, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_cuesheet_delete_track() on end of array..."); + cs_delete_(cuesheet, 2); + if(!FLAC__metadata_object_cuesheet_delete_track(block, 2)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(cuesheet, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_cuesheet_delete_track() on beginning of array..."); + cs_delete_(cuesheet, 0); + if(!FLAC__metadata_object_cuesheet_delete_track(block, 0)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(cuesheet, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_cuesheet_set_track(copy)..."); + cs_set_new_(&track, cuesheet, 0, 40, 5, "EBCDE1234567", false, false); + FLAC__metadata_object_cuesheet_set_track(block, 0, &track, /*copy=*/true); + if(!mutils__compare_block(cuesheet, block)) + return false; + printf("OK\n"); + + tr_resize_(cuesheet, 0, 2); + printf("testing FLAC__metadata_object_cuesheet_track_resize_indices(grow to %u)...", cuesheet->data.cue_sheet.tracks[0].num_indices); + if(!FLAC__metadata_object_cuesheet_track_resize_indices(block, 0, cuesheet->data.cue_sheet.tracks[0].num_indices)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(cuesheet, block)) + return false; + printf("OK\n"); + + tr_resize_(cuesheet, 0, 1); + printf("testing FLAC__metadata_object_cuesheet_track_resize_indices(shrink to %u)...", cuesheet->data.cue_sheet.tracks[0].num_indices); + if(!FLAC__metadata_object_cuesheet_track_resize_indices(block, 0, cuesheet->data.cue_sheet.tracks[0].num_indices)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(cuesheet, block)) + return false; + printf("OK\n"); + + tr_resize_(cuesheet, 0, 0); + printf("testing FLAC__metadata_object_cuesheet_track_resize_indices(shrink to %u)...", cuesheet->data.cue_sheet.tracks[0].num_indices); + if(!FLAC__metadata_object_cuesheet_track_resize_indices(block, 0, cuesheet->data.cue_sheet.tracks[0].num_indices)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(cuesheet, block)) + return false; + printf("OK\n"); + + indx.offset = 0; + indx.number = 1; + printf("testing FLAC__metadata_object_cuesheet_track_insert_index() on empty array..."); + tr_insert_new_(cuesheet, 0, 0, indx); + if(!FLAC__metadata_object_cuesheet_track_insert_index(block, 0, 0, indx)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(cuesheet, block)) + return false; + printf("OK\n"); + + indx.offset = 10; + indx.number = 2; + printf("testing FLAC__metadata_object_cuesheet_track_insert_index() on beginning of non-empty array..."); + tr_insert_new_(cuesheet, 0, 0, indx); + if(!FLAC__metadata_object_cuesheet_track_insert_index(block, 0, 0, indx)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(cuesheet, block)) + return false; + printf("OK\n"); + + indx.offset = 20; + indx.number = 3; + printf("testing FLAC__metadata_object_cuesheet_track_insert_index() on middle of non-empty array..."); + tr_insert_new_(cuesheet, 0, 1, indx); + if(!FLAC__metadata_object_cuesheet_track_insert_index(block, 0, 1, indx)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(cuesheet, block)) + return false; + printf("OK\n"); + + indx.offset = 30; + indx.number = 4; + printf("testing FLAC__metadata_object_cuesheet_track_insert_index() on end of non-empty array..."); + tr_insert_new_(cuesheet, 0, 3, indx); + if(!FLAC__metadata_object_cuesheet_track_insert_index(block, 0, 3, indx)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(cuesheet, block)) + return false; + printf("OK\n"); + + indx.offset = 0; + indx.number = 0; + printf("testing FLAC__metadata_object_cuesheet_track_insert_blank_index() on end of non-empty array..."); + tr_insert_new_(cuesheet, 0, 4, indx); + if(!FLAC__metadata_object_cuesheet_track_insert_blank_index(block, 0, 4)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(cuesheet, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_clone()... "); + blockcopy = FLAC__metadata_object_clone(block); + if(0 == blockcopy) { + printf("FAILED, returned NULL\n"); + return false; + } + if(!mutils__compare_block(block, blockcopy)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_delete()... "); + FLAC__metadata_object_delete(blockcopy); + printf("OK\n"); + + printf("testing FLAC__metadata_object_cuesheet_track_delete_index() on end of array..."); + tr_delete_(cuesheet, 0, 4); + if(!FLAC__metadata_object_cuesheet_track_delete_index(block, 0, 4)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(cuesheet, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_cuesheet_track_delete_index() on middle of array..."); + tr_delete_(cuesheet, 0, 2); + if(!FLAC__metadata_object_cuesheet_track_delete_index(block, 0, 2)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(cuesheet, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_cuesheet_track_delete_index() on end of array..."); + tr_delete_(cuesheet, 0, 2); + if(!FLAC__metadata_object_cuesheet_track_delete_index(block, 0, 2)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(cuesheet, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_cuesheet_track_delete_index() on beginning of array..."); + tr_delete_(cuesheet, 0, 0); + if(!FLAC__metadata_object_cuesheet_track_delete_index(block, 0, 0)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(cuesheet, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_delete()... "); + FLAC__metadata_object_delete(cuesheet); + FLAC__metadata_object_delete(block); + printf("OK\n"); + + + printf("testing FLAC__metadata_object_new()... "); + block = FLAC__metadata_object_new(FLAC__METADATA_TYPE_CUESHEET); + if(0 == block) { + printf("FAILED, returned NULL\n"); + return false; + } + printf("OK\n"); + + printf("testing FLAC__metadata_object_clone()... "); + cuesheet = FLAC__metadata_object_clone(block); + if(0 == cuesheet) { + printf("FAILED, returned NULL\n"); + return false; + } + if(!mutils__compare_block(cuesheet, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_cuesheet_insert_track(own) on empty array..."); + cs_insert_new_(&track, cuesheet, 0, 60, 7, "GBCDE1234567", false, false); + track_clone_(&track); + if(!FLAC__metadata_object_cuesheet_insert_track(block, 0, &track, /*copy=*/false)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(cuesheet, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_cuesheet_insert_track(own) on beginning of non-empty array..."); + cs_insert_new_(&track, cuesheet, 0, 70, 8, "HBCDE1234567", false, false); + track_clone_(&track); + if(!FLAC__metadata_object_cuesheet_insert_track(block, 0, &track, /*copy=*/false)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(cuesheet, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_cuesheet_insert_track(own) on middle of non-empty array..."); + cs_insert_new_(&track, cuesheet, 1, 80, 9, "IBCDE1234567", false, false); + track_clone_(&track); + if(!FLAC__metadata_object_cuesheet_insert_track(block, 1, &track, /*copy=*/false)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(cuesheet, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_cuesheet_insert_track(own) on end of non-empty array..."); + cs_insert_new_(&track, cuesheet, 3, 90, 10, "JBCDE1234567", false, false); + track_clone_(&track); + if(!FLAC__metadata_object_cuesheet_insert_track(block, 3, &track, /*copy=*/false)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(cuesheet, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_cuesheet_delete_track() on middle of array..."); + cs_delete_(cuesheet, 2); + if(!FLAC__metadata_object_cuesheet_delete_track(block, 2)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(cuesheet, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_cuesheet_delete_track() on end of array..."); + cs_delete_(cuesheet, 2); + if(!FLAC__metadata_object_cuesheet_delete_track(block, 2)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(cuesheet, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_cuesheet_delete_track() on beginning of array..."); + cs_delete_(cuesheet, 0); + if(!FLAC__metadata_object_cuesheet_delete_track(block, 0)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(cuesheet, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_cuesheet_set_track(own)..."); + cs_set_new_(&track, cuesheet, 0, 100, 11, "KBCDE1234567", false, false); + track_clone_(&track); + FLAC__metadata_object_cuesheet_set_track(block, 0, &track, /*copy=*/false); + if(!mutils__compare_block(cuesheet, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_cuesheet_is_legal()..."); + { + const char *violation; + if(FLAC__metadata_object_cuesheet_is_legal(block, /*check_cd_da_subset=*/true, &violation)) { + printf("FAILED, returned true when expecting false\n"); + return false; + } + printf("returned false as expected, violation=\"%s\" OK\n", violation); + } + + printf("testing FLAC__metadata_object_delete()... "); + FLAC__metadata_object_delete(cuesheet); + FLAC__metadata_object_delete(block); + printf("OK\n"); + + + printf("testing PICTURE\n"); + + printf("testing FLAC__metadata_object_new()... "); + block = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PICTURE); + if(0 == block) { + printf("FAILED, returned NULL\n"); + return false; + } + expected_length = ( + FLAC__STREAM_METADATA_PICTURE_TYPE_LEN + + FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN + + FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN + + FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN + + FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN + + FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN + + FLAC__STREAM_METADATA_PICTURE_COLORS_LEN + + FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN + ) / 8; + if(block->length != expected_length) { + printf("FAILED, bad length, expected %u, got %u\n", expected_length, block->length); + return false; + } + printf("OK\n"); + + printf("testing FLAC__metadata_object_clone()... "); + picture = FLAC__metadata_object_clone(block); + if(0 == picture) { + printf("FAILED, returned NULL\n"); + return false; + } + if(!mutils__compare_block(picture, block)) + return false; + printf("OK\n"); + + pi_set_mime_type(picture, "image/png\t"); + printf("testing FLAC__metadata_object_picture_set_mime_type(copy)..."); + if(!FLAC__metadata_object_picture_set_mime_type(block, "image/png\t", /*copy=*/true)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(picture, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_picture_is_legal()..."); + { + const char *violation; + if(FLAC__metadata_object_picture_is_legal(block, &violation)) { + printf("FAILED, returned true when expecting false\n"); + return false; + } + printf("returned false as expected, violation=\"%s\" OK\n", violation); + } + + pi_set_mime_type(picture, "image/png"); + printf("testing FLAC__metadata_object_picture_set_mime_type(copy)..."); + if(!FLAC__metadata_object_picture_set_mime_type(block, "image/png", /*copy=*/true)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(picture, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_picture_is_legal()..."); + { + const char *violation; + if(!FLAC__metadata_object_picture_is_legal(block, &violation)) { + printf("FAILED, returned false, violation=\"%s\"\n", violation); + return false; + } + printf("OK\n"); + } + + pi_set_description(picture, (const FLAC__byte *)"DESCRIPTION\xff"); + printf("testing FLAC__metadata_object_picture_set_description(copy)..."); + if(!FLAC__metadata_object_picture_set_description(block, (const FLAC__byte *)"DESCRIPTION\xff", /*copy=*/true)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(picture, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_picture_is_legal()..."); + { + const char *violation; + if(FLAC__metadata_object_picture_is_legal(block, &violation)) { + printf("FAILED, returned true when expecting false\n"); + return false; + } + printf("returned false as expected, violation=\"%s\" OK\n", violation); + } + + pi_set_description(picture, (const FLAC__byte *)"DESCRIPTION"); + printf("testing FLAC__metadata_object_picture_set_description(copy)..."); + if(!FLAC__metadata_object_picture_set_description(block, (const FLAC__byte *)"DESCRIPTION", /*copy=*/true)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(picture, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_picture_is_legal()..."); + { + const char *violation; + if(!FLAC__metadata_object_picture_is_legal(block, &violation)) { + printf("FAILED, returned false, violation=\"%s\"\n", violation); + return false; + } + printf("OK\n"); + } + + + pi_set_data(picture, (const FLAC__byte*)"PNGDATA", strlen("PNGDATA")); + printf("testing FLAC__metadata_object_picture_set_data(copy)..."); + if(!FLAC__metadata_object_picture_set_data(block, (const FLAC__byte*)"PNGDATA", strlen("PNGDATA"), /*copy=*/true)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(picture, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_clone()... "); + blockcopy = FLAC__metadata_object_clone(block); + if(0 == blockcopy) { + printf("FAILED, returned NULL\n"); + return false; + } + if(!mutils__compare_block(block, blockcopy)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_delete()... "); + FLAC__metadata_object_delete(blockcopy); + printf("OK\n"); + + pi_set_mime_type(picture, "image/png\t"); + printf("testing FLAC__metadata_object_picture_set_mime_type(own)..."); + if(!FLAC__metadata_object_picture_set_mime_type(block, strdup("image/png\t"), /*copy=*/false)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(picture, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_picture_is_legal()..."); + { + const char *violation; + if(FLAC__metadata_object_picture_is_legal(block, &violation)) { + printf("FAILED, returned true when expecting false\n"); + return false; + } + printf("returned false as expected, violation=\"%s\" OK\n", violation); + } + + pi_set_mime_type(picture, "image/png"); + printf("testing FLAC__metadata_object_picture_set_mime_type(own)..."); + if(!FLAC__metadata_object_picture_set_mime_type(block, strdup("image/png"), /*copy=*/false)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(picture, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_picture_is_legal()..."); + { + const char *violation; + if(!FLAC__metadata_object_picture_is_legal(block, &violation)) { + printf("FAILED, returned false, violation=\"%s\"\n", violation); + return false; + } + printf("OK\n"); + } + + pi_set_description(picture, (const FLAC__byte *)"DESCRIPTION\xff"); + printf("testing FLAC__metadata_object_picture_set_description(own)..."); + if(!FLAC__metadata_object_picture_set_description(block, (FLAC__byte *)strdup("DESCRIPTION\xff"), /*copy=*/false)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(picture, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_picture_is_legal()..."); + { + const char *violation; + if(FLAC__metadata_object_picture_is_legal(block, &violation)) { + printf("FAILED, returned true when expecting false\n"); + return false; + } + printf("returned false as expected, violation=\"%s\" OK\n", violation); + } + + pi_set_description(picture, (const FLAC__byte *)"DESCRIPTION"); + printf("testing FLAC__metadata_object_picture_set_description(own)..."); + if(!FLAC__metadata_object_picture_set_description(block, (FLAC__byte *)strdup("DESCRIPTION"), /*copy=*/false)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(picture, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_picture_is_legal()..."); + { + const char *violation; + if(!FLAC__metadata_object_picture_is_legal(block, &violation)) { + printf("FAILED, returned false, violation=\"%s\"\n", violation); + return false; + } + printf("OK\n"); + } + + pi_set_data(picture, (const FLAC__byte*)"PNGDATA", strlen("PNGDATA")); + printf("testing FLAC__metadata_object_picture_set_data(own)..."); + if(!FLAC__metadata_object_picture_set_data(block, (FLAC__byte*)strdup("PNGDATA"), strlen("PNGDATA"), /*copy=*/false)) { + printf("FAILED, returned false\n"); + return false; + } + if(!mutils__compare_block(picture, block)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_clone()... "); + blockcopy = FLAC__metadata_object_clone(block); + if(0 == blockcopy) { + printf("FAILED, returned NULL\n"); + return false; + } + if(!mutils__compare_block(block, blockcopy)) + return false; + printf("OK\n"); + + printf("testing FLAC__metadata_object_delete()... "); + FLAC__metadata_object_delete(blockcopy); + printf("OK\n"); + + printf("testing FLAC__metadata_object_delete()... "); + FLAC__metadata_object_delete(picture); + FLAC__metadata_object_delete(block); + printf("OK\n"); + + + return true; +} diff --git a/src/FLAC/src/test_libs_common/Makefile.am b/src/FLAC/src/test_libs_common/Makefile.am new file mode 100644 index 00000000..498b247a --- /dev/null +++ b/src/FLAC/src/test_libs_common/Makefile.am @@ -0,0 +1,27 @@ +# test_libs_common - Common code to library unit tests +# Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +INCLUDES = -I$(top_srcdir)/src/FLAC/include + +noinst_LTLIBRARIES = libtest_libs_common.la + +libtest_libs_common_la_SOURCES = \ + file_utils_flac.c \ + metadata_utils.c + +EXTRA_DIST = \ + README diff --git a/src/FLAC/src/test_libs_common/README b/src/FLAC/src/test_libs_common/README new file mode 100644 index 00000000..6a704c20 --- /dev/null +++ b/src/FLAC/src/test_libs_common/README @@ -0,0 +1,2 @@ +This directory contains a convenience library of routines that are +common to the library unit testers. diff --git a/src/FLAC/src/test_libs_common/file_utils_flac.c b/src/FLAC/src/test_libs_common/file_utils_flac.c new file mode 100644 index 00000000..a52fee09 --- /dev/null +++ b/src/FLAC/src/test_libs_common/file_utils_flac.c @@ -0,0 +1,153 @@ +/* test_libFLAC - Unit tester for libFLAC + * Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include "FLAC/assert.h" +#include "FLAC/stream_encoder.h" +#include "test_libs_common/file_utils_flac.h" +#include +#include +#include /* for stat() */ + +#ifdef min +#undef min +#endif +#define min(a,b) ((a)<(b)?(a):(b)) + +const long file_utils__ogg_serial_number = 12345; + +#ifdef FLAC__VALGRIND_TESTING +static size_t local__fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) +{ + size_t ret = fwrite(ptr, size, nmemb, stream); + if(!ferror(stream)) + fflush(stream); + return ret; +} +#else +#define local__fwrite fwrite +#endif + +typedef struct { + FILE *file; +} encoder_client_struct; + +static FLAC__StreamEncoderWriteStatus encoder_write_callback_(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame, void *client_data) +{ + encoder_client_struct *ecd = (encoder_client_struct*)client_data; + + (void)encoder, (void)samples, (void)current_frame; + + if(local__fwrite(buffer, 1, bytes, ecd->file) != bytes) + return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR; + else + return FLAC__STREAM_ENCODER_WRITE_STATUS_OK; +} + +static void encoder_metadata_callback_(const FLAC__StreamEncoder *encoder, const FLAC__StreamMetadata *metadata, void *client_data) +{ + (void)encoder, (void)metadata, (void)client_data; +} + +FLAC__bool file_utils__generate_flacfile(FLAC__bool is_ogg, const char *output_filename, off_t *output_filesize, unsigned length, const FLAC__StreamMetadata *streaminfo, FLAC__StreamMetadata **metadata, unsigned num_metadata) +{ + FLAC__int32 samples[1024]; + FLAC__StreamEncoder *encoder; + FLAC__StreamEncoderInitStatus init_status; + encoder_client_struct encoder_client_data; + unsigned i, n; + + FLAC__ASSERT(0 != output_filename); + FLAC__ASSERT(0 != streaminfo); + FLAC__ASSERT(streaminfo->type == FLAC__METADATA_TYPE_STREAMINFO); + FLAC__ASSERT((streaminfo->is_last && num_metadata == 0) || (!streaminfo->is_last && num_metadata > 0)); + + if(0 == (encoder_client_data.file = fopen(output_filename, "wb"))) + return false; + + encoder = FLAC__stream_encoder_new(); + if(0 == encoder) { + fclose(encoder_client_data.file); + return false; + } + + FLAC__stream_encoder_set_ogg_serial_number(encoder, file_utils__ogg_serial_number); + FLAC__stream_encoder_set_verify(encoder, true); + FLAC__stream_encoder_set_streamable_subset(encoder, true); + FLAC__stream_encoder_set_do_mid_side_stereo(encoder, false); + FLAC__stream_encoder_set_loose_mid_side_stereo(encoder, false); + FLAC__stream_encoder_set_channels(encoder, streaminfo->data.stream_info.channels); + FLAC__stream_encoder_set_bits_per_sample(encoder, streaminfo->data.stream_info.bits_per_sample); + FLAC__stream_encoder_set_sample_rate(encoder, streaminfo->data.stream_info.sample_rate); + FLAC__stream_encoder_set_blocksize(encoder, streaminfo->data.stream_info.min_blocksize); + FLAC__stream_encoder_set_max_lpc_order(encoder, 0); + FLAC__stream_encoder_set_qlp_coeff_precision(encoder, 0); + FLAC__stream_encoder_set_do_qlp_coeff_prec_search(encoder, false); + FLAC__stream_encoder_set_do_escape_coding(encoder, false); + FLAC__stream_encoder_set_do_exhaustive_model_search(encoder, false); + FLAC__stream_encoder_set_min_residual_partition_order(encoder, 0); + FLAC__stream_encoder_set_max_residual_partition_order(encoder, 0); + FLAC__stream_encoder_set_rice_parameter_search_dist(encoder, 0); + FLAC__stream_encoder_set_total_samples_estimate(encoder, streaminfo->data.stream_info.total_samples); + FLAC__stream_encoder_set_metadata(encoder, metadata, num_metadata); + + if(is_ogg) + init_status = FLAC__stream_encoder_init_ogg_stream(encoder, /*read_callback=*/0, encoder_write_callback_, /*seek_callback=*/0, /*tell_callback=*/0, encoder_metadata_callback_, &encoder_client_data); + else + init_status = FLAC__stream_encoder_init_stream(encoder, encoder_write_callback_, /*seek_callback=*/0, /*tell_callback=*/0, encoder_metadata_callback_, &encoder_client_data); + + if(init_status != FLAC__STREAM_ENCODER_INIT_STATUS_OK) { + fclose(encoder_client_data.file); + return false; + } + + /* init the dummy sample buffer */ + for(i = 0; i < sizeof(samples) / sizeof(FLAC__int32); i++) + samples[i] = i & 7; + + while(length > 0) { + n = min(length, sizeof(samples) / sizeof(FLAC__int32)); + + if(!FLAC__stream_encoder_process_interleaved(encoder, samples, n)) { + fclose(encoder_client_data.file); + return false; + } + + length -= n; + } + + (void)FLAC__stream_encoder_finish(encoder); + + fclose(encoder_client_data.file); + + FLAC__stream_encoder_delete(encoder); + + if(0 != output_filesize) { + struct stat filestats; + + if(stat(output_filename, &filestats) != 0) + return false; + else + *output_filesize = filestats.st_size; + } + + return true; +} diff --git a/src/FLAC/src/test_libs_common/metadata_utils.c b/src/FLAC/src/test_libs_common/metadata_utils.c new file mode 100644 index 00000000..fe192e7c --- /dev/null +++ b/src/FLAC/src/test_libs_common/metadata_utils.c @@ -0,0 +1,657 @@ +/* test_libFLAC - Unit tester for libFLAC + * Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * These are not tests, just utility functions used by the metadata tests + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include "FLAC/metadata.h" +#include "test_libs_common/metadata_utils.h" +#include +#include /* for malloc() */ +#include /* for memcmp() */ + +FLAC__bool mutils__compare_block_data_streaminfo(const FLAC__StreamMetadata_StreamInfo *block, const FLAC__StreamMetadata_StreamInfo *blockcopy) +{ + if(blockcopy->min_blocksize != block->min_blocksize) { + printf("FAILED, min_blocksize mismatch, expected %u, got %u\n", block->min_blocksize, blockcopy->min_blocksize); + return false; + } + if(blockcopy->max_blocksize != block->max_blocksize) { + printf("FAILED, max_blocksize mismatch, expected %u, got %u\n", block->max_blocksize, blockcopy->max_blocksize); + return false; + } + if(blockcopy->min_framesize != block->min_framesize) { + printf("FAILED, min_framesize mismatch, expected %u, got %u\n", block->min_framesize, blockcopy->min_framesize); + return false; + } + if(blockcopy->max_framesize != block->max_framesize) { + printf("FAILED, max_framesize mismatch, expected %u, got %u\n", block->max_framesize, blockcopy->max_framesize); + return false; + } + if(blockcopy->sample_rate != block->sample_rate) { + printf("FAILED, sample_rate mismatch, expected %u, got %u\n", block->sample_rate, blockcopy->sample_rate); + return false; + } + if(blockcopy->channels != block->channels) { + printf("FAILED, channels mismatch, expected %u, got %u\n", block->channels, blockcopy->channels); + return false; + } + if(blockcopy->bits_per_sample != block->bits_per_sample) { + printf("FAILED, bits_per_sample mismatch, expected %u, got %u\n", block->bits_per_sample, blockcopy->bits_per_sample); + return false; + } + if(blockcopy->total_samples != block->total_samples) { +#ifdef _MSC_VER + printf("FAILED, total_samples mismatch, expected %I64u, got %I64u\n", block->total_samples, blockcopy->total_samples); +#else + printf("FAILED, total_samples mismatch, expected %llu, got %llu\n", (unsigned long long)block->total_samples, (unsigned long long)blockcopy->total_samples); +#endif + return false; + } + if(0 != memcmp(blockcopy->md5sum, block->md5sum, sizeof(block->md5sum))) { + printf("FAILED, md5sum mismatch, expected %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X, got %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\n", + (unsigned)block->md5sum[0], + (unsigned)block->md5sum[1], + (unsigned)block->md5sum[2], + (unsigned)block->md5sum[3], + (unsigned)block->md5sum[4], + (unsigned)block->md5sum[5], + (unsigned)block->md5sum[6], + (unsigned)block->md5sum[7], + (unsigned)block->md5sum[8], + (unsigned)block->md5sum[9], + (unsigned)block->md5sum[10], + (unsigned)block->md5sum[11], + (unsigned)block->md5sum[12], + (unsigned)block->md5sum[13], + (unsigned)block->md5sum[14], + (unsigned)block->md5sum[15], + (unsigned)blockcopy->md5sum[0], + (unsigned)blockcopy->md5sum[1], + (unsigned)blockcopy->md5sum[2], + (unsigned)blockcopy->md5sum[3], + (unsigned)blockcopy->md5sum[4], + (unsigned)blockcopy->md5sum[5], + (unsigned)blockcopy->md5sum[6], + (unsigned)blockcopy->md5sum[7], + (unsigned)blockcopy->md5sum[8], + (unsigned)blockcopy->md5sum[9], + (unsigned)blockcopy->md5sum[10], + (unsigned)blockcopy->md5sum[11], + (unsigned)blockcopy->md5sum[12], + (unsigned)blockcopy->md5sum[13], + (unsigned)blockcopy->md5sum[14], + (unsigned)blockcopy->md5sum[15] + ); + return false; + } + return true; +} + +FLAC__bool mutils__compare_block_data_padding(const FLAC__StreamMetadata_Padding *block, const FLAC__StreamMetadata_Padding *blockcopy, unsigned block_length) +{ + /* we don't compare the padding guts */ + (void)block, (void)blockcopy, (void)block_length; + return true; +} + +FLAC__bool mutils__compare_block_data_application(const FLAC__StreamMetadata_Application *block, const FLAC__StreamMetadata_Application *blockcopy, unsigned block_length) +{ + if(block_length < sizeof(block->id)) { + printf("FAILED, bad block length = %u\n", block_length); + return false; + } + if(0 != memcmp(blockcopy->id, block->id, sizeof(block->id))) { + printf("FAILED, id mismatch, expected %02X%02X%02X%02X, got %02X%02X%02X%02X\n", + (unsigned)block->id[0], + (unsigned)block->id[1], + (unsigned)block->id[2], + (unsigned)block->id[3], + (unsigned)blockcopy->id[0], + (unsigned)blockcopy->id[1], + (unsigned)blockcopy->id[2], + (unsigned)blockcopy->id[3] + ); + return false; + } + if(0 == block->data || 0 == blockcopy->data) { + if(block->data != blockcopy->data) { + printf("FAILED, data mismatch (%s's data pointer is null)\n", 0==block->data?"original":"copy"); + return false; + } + else if(block_length - sizeof(block->id) > 0) { + printf("FAILED, data pointer is null but block length is not 0\n"); + return false; + } + } + else { + if(block_length - sizeof(block->id) == 0) { + printf("FAILED, data pointer is not null but block length is 0\n"); + return false; + } + else if(0 != memcmp(blockcopy->data, block->data, block_length - sizeof(block->id))) { + printf("FAILED, data mismatch\n"); + return false; + } + } + return true; +} + +FLAC__bool mutils__compare_block_data_seektable(const FLAC__StreamMetadata_SeekTable *block, const FLAC__StreamMetadata_SeekTable *blockcopy) +{ + unsigned i; + if(blockcopy->num_points != block->num_points) { + printf("FAILED, num_points mismatch, expected %u, got %u\n", block->num_points, blockcopy->num_points); + return false; + } + for(i = 0; i < block->num_points; i++) { + if(blockcopy->points[i].sample_number != block->points[i].sample_number) { +#ifdef _MSC_VER + printf("FAILED, points[%u].sample_number mismatch, expected %I64u, got %I64u\n", i, block->points[i].sample_number, blockcopy->points[i].sample_number); +#else + printf("FAILED, points[%u].sample_number mismatch, expected %llu, got %llu\n", i, (unsigned long long)block->points[i].sample_number, (unsigned long long)blockcopy->points[i].sample_number); +#endif + return false; + } + if(blockcopy->points[i].stream_offset != block->points[i].stream_offset) { +#ifdef _MSC_VER + printf("FAILED, points[%u].stream_offset mismatch, expected %I64u, got %I64u\n", i, block->points[i].stream_offset, blockcopy->points[i].stream_offset); +#else + printf("FAILED, points[%u].stream_offset mismatch, expected %llu, got %llu\n", i, (unsigned long long)block->points[i].stream_offset, (unsigned long long)blockcopy->points[i].stream_offset); +#endif + return false; + } + if(blockcopy->points[i].frame_samples != block->points[i].frame_samples) { + printf("FAILED, points[%u].frame_samples mismatch, expected %u, got %u\n", i, block->points[i].frame_samples, blockcopy->points[i].frame_samples); + return false; + } + } + return true; +} + +FLAC__bool mutils__compare_block_data_vorbiscomment(const FLAC__StreamMetadata_VorbisComment *block, const FLAC__StreamMetadata_VorbisComment *blockcopy) +{ + unsigned i; + if(blockcopy->vendor_string.length != block->vendor_string.length) { + printf("FAILED, vendor_string.length mismatch, expected %u, got %u\n", block->vendor_string.length, blockcopy->vendor_string.length); + return false; + } + if(0 == block->vendor_string.entry || 0 == blockcopy->vendor_string.entry) { + if(block->vendor_string.entry != blockcopy->vendor_string.entry) { + printf("FAILED, vendor_string.entry mismatch\n"); + return false; + } + } + else if(0 != memcmp(blockcopy->vendor_string.entry, block->vendor_string.entry, block->vendor_string.length)) { + printf("FAILED, vendor_string.entry mismatch\n"); + return false; + } + if(blockcopy->num_comments != block->num_comments) { + printf("FAILED, num_comments mismatch, expected %u, got %u\n", block->num_comments, blockcopy->num_comments); + return false; + } + for(i = 0; i < block->num_comments; i++) { + if(blockcopy->comments[i].length != block->comments[i].length) { + printf("FAILED, comments[%u].length mismatch, expected %u, got %u\n", i, block->comments[i].length, blockcopy->comments[i].length); + return false; + } + if(0 == block->comments[i].entry || 0 == blockcopy->comments[i].entry) { + if(block->comments[i].entry != blockcopy->comments[i].entry) { + printf("FAILED, comments[%u].entry mismatch\n", i); + return false; + } + } + else { + if(0 != memcmp(blockcopy->comments[i].entry, block->comments[i].entry, block->comments[i].length)) { + printf("FAILED, comments[%u].entry mismatch\n", i); + return false; + } + } + } + return true; +} + +FLAC__bool mutils__compare_block_data_cuesheet(const FLAC__StreamMetadata_CueSheet *block, const FLAC__StreamMetadata_CueSheet *blockcopy) +{ + unsigned i, j; + + if(0 != strcmp(blockcopy->media_catalog_number, block->media_catalog_number)) { + printf("FAILED, media_catalog_number mismatch, expected %s, got %s\n", block->media_catalog_number, blockcopy->media_catalog_number); + return false; + } + if(blockcopy->lead_in != block->lead_in) { +#ifdef _MSC_VER + printf("FAILED, lead_in mismatch, expected %I64u, got %I64u\n", block->lead_in, blockcopy->lead_in); +#else + printf("FAILED, lead_in mismatch, expected %llu, got %llu\n", (unsigned long long)block->lead_in, (unsigned long long)blockcopy->lead_in); +#endif + return false; + } + if(blockcopy->is_cd != block->is_cd) { + printf("FAILED, is_cd mismatch, expected %u, got %u\n", (unsigned)block->is_cd, (unsigned)blockcopy->is_cd); + return false; + } + if(blockcopy->num_tracks != block->num_tracks) { + printf("FAILED, num_tracks mismatch, expected %u, got %u\n", block->num_tracks, blockcopy->num_tracks); + return false; + } + for(i = 0; i < block->num_tracks; i++) { + if(blockcopy->tracks[i].offset != block->tracks[i].offset) { +#ifdef _MSC_VER + printf("FAILED, tracks[%u].offset mismatch, expected %I64u, got %I64u\n", i, block->tracks[i].offset, blockcopy->tracks[i].offset); +#else + printf("FAILED, tracks[%u].offset mismatch, expected %llu, got %llu\n", i, (unsigned long long)block->tracks[i].offset, (unsigned long long)blockcopy->tracks[i].offset); +#endif + return false; + } + if(blockcopy->tracks[i].number != block->tracks[i].number) { + printf("FAILED, tracks[%u].number mismatch, expected %u, got %u\n", i, (unsigned)block->tracks[i].number, (unsigned)blockcopy->tracks[i].number); + return false; + } + if(blockcopy->tracks[i].num_indices != block->tracks[i].num_indices) { + printf("FAILED, tracks[%u].num_indices mismatch, expected %u, got %u\n", i, (unsigned)block->tracks[i].num_indices, (unsigned)blockcopy->tracks[i].num_indices); + return false; + } + /* num_indices == 0 means lead-out track so only the track offset and number are valid */ + if(block->tracks[i].num_indices > 0) { + if(0 != strcmp(blockcopy->tracks[i].isrc, block->tracks[i].isrc)) { + printf("FAILED, tracks[%u].isrc mismatch, expected %s, got %s\n", i, block->tracks[i].isrc, blockcopy->tracks[i].isrc); + return false; + } + if(blockcopy->tracks[i].type != block->tracks[i].type) { + printf("FAILED, tracks[%u].type mismatch, expected %u, got %u\n", i, (unsigned)block->tracks[i].type, (unsigned)blockcopy->tracks[i].type); + return false; + } + if(blockcopy->tracks[i].pre_emphasis != block->tracks[i].pre_emphasis) { + printf("FAILED, tracks[%u].pre_emphasis mismatch, expected %u, got %u\n", i, (unsigned)block->tracks[i].pre_emphasis, (unsigned)blockcopy->tracks[i].pre_emphasis); + return false; + } + if(0 == block->tracks[i].indices || 0 == blockcopy->tracks[i].indices) { + if(block->tracks[i].indices != blockcopy->tracks[i].indices) { + printf("FAILED, tracks[%u].indices mismatch\n", i); + return false; + } + } + else { + for(j = 0; j < block->tracks[i].num_indices; j++) { + if(blockcopy->tracks[i].indices[j].offset != block->tracks[i].indices[j].offset) { +#ifdef _MSC_VER + printf("FAILED, tracks[%u].indices[%u].offset mismatch, expected %I64u, got %I64u\n", i, j, block->tracks[i].indices[j].offset, blockcopy->tracks[i].indices[j].offset); +#else + printf("FAILED, tracks[%u].indices[%u].offset mismatch, expected %llu, got %llu\n", i, j, (unsigned long long)block->tracks[i].indices[j].offset, (unsigned long long)blockcopy->tracks[i].indices[j].offset); +#endif + return false; + } + if(blockcopy->tracks[i].indices[j].number != block->tracks[i].indices[j].number) { + printf("FAILED, tracks[%u].indices[%u].number mismatch, expected %u, got %u\n", i, j, (unsigned)block->tracks[i].indices[j].number, (unsigned)blockcopy->tracks[i].indices[j].number); + return false; + } + } + } + } + } + return true; +} + +FLAC__bool mutils__compare_block_data_picture(const FLAC__StreamMetadata_Picture *block, const FLAC__StreamMetadata_Picture *blockcopy) +{ + size_t len, lencopy; + if(blockcopy->type != block->type) { + printf("FAILED, type mismatch, expected %u, got %u\n", (unsigned)block->type, (unsigned)blockcopy->type); + return false; + } + len = strlen(block->mime_type); + lencopy = strlen(blockcopy->mime_type); + if(lencopy != len) { + printf("FAILED, mime_type length mismatch, expected %u, got %u\n", (unsigned)len, (unsigned)lencopy); + return false; + } + if(strcmp(blockcopy->mime_type, block->mime_type)) { + printf("FAILED, mime_type mismatch, expected %s, got %s\n", block->mime_type, blockcopy->mime_type); + return false; + } + len = strlen((const char *)block->description); + lencopy = strlen((const char *)blockcopy->description); + if(lencopy != len) { + printf("FAILED, description length mismatch, expected %u, got %u\n", (unsigned)len, (unsigned)lencopy); + return false; + } + if(strcmp((const char *)blockcopy->description, (const char *)block->description)) { + printf("FAILED, description mismatch, expected %s, got %s\n", block->description, blockcopy->description); + return false; + } + if(blockcopy->width != block->width) { + printf("FAILED, width mismatch, expected %u, got %u\n", block->width, blockcopy->width); + return false; + } + if(blockcopy->height != block->height) { + printf("FAILED, height mismatch, expected %u, got %u\n", block->height, blockcopy->height); + return false; + } + if(blockcopy->depth != block->depth) { + printf("FAILED, depth mismatch, expected %u, got %u\n", block->depth, blockcopy->depth); + return false; + } + if(blockcopy->colors != block->colors) { + printf("FAILED, colors mismatch, expected %u, got %u\n", block->colors, blockcopy->colors); + return false; + } + if(blockcopy->data_length != block->data_length) { + printf("FAILED, data_length mismatch, expected %u, got %u\n", block->data_length, blockcopy->data_length); + return false; + } + if(memcmp(blockcopy->data, block->data, block->data_length)) { + printf("FAILED, data mismatch\n"); + return false; + } + return true; +} + +FLAC__bool mutils__compare_block_data_unknown(const FLAC__StreamMetadata_Unknown *block, const FLAC__StreamMetadata_Unknown *blockcopy, unsigned block_length) +{ + if(0 == block->data || 0 == blockcopy->data) { + if(block->data != blockcopy->data) { + printf("FAILED, data mismatch (%s's data pointer is null)\n", 0==block->data?"original":"copy"); + return false; + } + else if(block_length > 0) { + printf("FAILED, data pointer is null but block length is not 0\n"); + return false; + } + } + else { + if(block_length == 0) { + printf("FAILED, data pointer is not null but block length is 0\n"); + return false; + } + else if(0 != memcmp(blockcopy->data, block->data, block_length)) { + printf("FAILED, data mismatch\n"); + return false; + } + } + return true; +} + +FLAC__bool mutils__compare_block(const FLAC__StreamMetadata *block, const FLAC__StreamMetadata *blockcopy) +{ + if(blockcopy->type != block->type) { + printf("FAILED, type mismatch, expected %s, got %s\n", FLAC__MetadataTypeString[block->type], FLAC__MetadataTypeString[blockcopy->type]); + return false; + } + if(blockcopy->is_last != block->is_last) { + printf("FAILED, is_last mismatch, expected %u, got %u\n", (unsigned)block->is_last, (unsigned)blockcopy->is_last); + return false; + } + if(blockcopy->length != block->length) { + printf("FAILED, length mismatch, expected %u, got %u\n", block->length, blockcopy->length); + return false; + } + switch(block->type) { + case FLAC__METADATA_TYPE_STREAMINFO: + return mutils__compare_block_data_streaminfo(&block->data.stream_info, &blockcopy->data.stream_info); + case FLAC__METADATA_TYPE_PADDING: + return mutils__compare_block_data_padding(&block->data.padding, &blockcopy->data.padding, block->length); + case FLAC__METADATA_TYPE_APPLICATION: + return mutils__compare_block_data_application(&block->data.application, &blockcopy->data.application, block->length); + case FLAC__METADATA_TYPE_SEEKTABLE: + return mutils__compare_block_data_seektable(&block->data.seek_table, &blockcopy->data.seek_table); + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + return mutils__compare_block_data_vorbiscomment(&block->data.vorbis_comment, &blockcopy->data.vorbis_comment); + case FLAC__METADATA_TYPE_CUESHEET: + return mutils__compare_block_data_cuesheet(&block->data.cue_sheet, &blockcopy->data.cue_sheet); + case FLAC__METADATA_TYPE_PICTURE: + return mutils__compare_block_data_picture(&block->data.picture, &blockcopy->data.picture); + default: + return mutils__compare_block_data_unknown(&block->data.unknown, &blockcopy->data.unknown, block->length); + } +} + +static void *malloc_or_die_(size_t size) +{ + void *x = malloc(size); + if(0 == x) { + fprintf(stderr, "ERROR: out of memory allocating %u bytes\n", (unsigned)size); + exit(1); + } + return x; +} + +static void *calloc_or_die_(size_t n, size_t size) +{ + void *x = calloc(n, size); + if(0 == x) { + fprintf(stderr, "ERROR: out of memory allocating %u bytes\n", (unsigned)n * (unsigned)size); + exit(1); + } + return x; +} + +static char *strdup_or_die_(const char *s) +{ + char *x = strdup(s); + if(0 == x) { + fprintf(stderr, "ERROR: out of memory copying string \"%s\"\n", s); + exit(1); + } + return x; +} + +void mutils__init_metadata_blocks( + FLAC__StreamMetadata *streaminfo, + FLAC__StreamMetadata *padding, + FLAC__StreamMetadata *seektable, + FLAC__StreamMetadata *application1, + FLAC__StreamMetadata *application2, + FLAC__StreamMetadata *vorbiscomment, + FLAC__StreamMetadata *cuesheet, + FLAC__StreamMetadata *picture, + FLAC__StreamMetadata *unknown +) +{ + /* + most of the actual numbers and data in the blocks don't matter, + we just want to make sure the decoder parses them correctly + + remember, the metadata interface gets tested after the decoders, + so we do all the metadata manipulation here without it. + */ + + /* min/max_framesize and md5sum don't get written at first, so we have to leave them 0 */ + streaminfo->is_last = false; + streaminfo->type = FLAC__METADATA_TYPE_STREAMINFO; + streaminfo->length = FLAC__STREAM_METADATA_STREAMINFO_LENGTH; + streaminfo->data.stream_info.min_blocksize = 576; + streaminfo->data.stream_info.max_blocksize = 576; + streaminfo->data.stream_info.min_framesize = 0; + streaminfo->data.stream_info.max_framesize = 0; + streaminfo->data.stream_info.sample_rate = 44100; + streaminfo->data.stream_info.channels = 1; + streaminfo->data.stream_info.bits_per_sample = 8; + streaminfo->data.stream_info.total_samples = 0; + memset(streaminfo->data.stream_info.md5sum, 0, 16); + + padding->is_last = false; + padding->type = FLAC__METADATA_TYPE_PADDING; + padding->length = 1234; + + seektable->is_last = false; + seektable->type = FLAC__METADATA_TYPE_SEEKTABLE; + seektable->data.seek_table.num_points = 2; + seektable->length = seektable->data.seek_table.num_points * FLAC__STREAM_METADATA_SEEKPOINT_LENGTH; + seektable->data.seek_table.points = (FLAC__StreamMetadata_SeekPoint*)malloc_or_die_(seektable->data.seek_table.num_points * sizeof(FLAC__StreamMetadata_SeekPoint)); + seektable->data.seek_table.points[0].sample_number = 0; + seektable->data.seek_table.points[0].stream_offset = 0; + seektable->data.seek_table.points[0].frame_samples = streaminfo->data.stream_info.min_blocksize; + seektable->data.seek_table.points[1].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER; + seektable->data.seek_table.points[1].stream_offset = 1000; + seektable->data.seek_table.points[1].frame_samples = streaminfo->data.stream_info.min_blocksize; + + application1->is_last = false; + application1->type = FLAC__METADATA_TYPE_APPLICATION; + application1->length = 8; + memcpy(application1->data.application.id, "\xfe\xdc\xba\x98", 4); + application1->data.application.data = (FLAC__byte*)malloc_or_die_(4); + memcpy(application1->data.application.data, "\xf0\xe1\xd2\xc3", 4); + + application2->is_last = false; + application2->type = FLAC__METADATA_TYPE_APPLICATION; + application2->length = 4; + memcpy(application2->data.application.id, "\x76\x54\x32\x10", 4); + application2->data.application.data = 0; + + { + const unsigned vendor_string_length = (unsigned)strlen(FLAC__VENDOR_STRING); + vorbiscomment->is_last = false; + vorbiscomment->type = FLAC__METADATA_TYPE_VORBIS_COMMENT; + vorbiscomment->length = (4 + vendor_string_length) + 4 + (4 + 5) + (4 + 0); + vorbiscomment->data.vorbis_comment.vendor_string.length = vendor_string_length; + vorbiscomment->data.vorbis_comment.vendor_string.entry = (FLAC__byte*)malloc_or_die_(vendor_string_length+1); + memcpy(vorbiscomment->data.vorbis_comment.vendor_string.entry, FLAC__VENDOR_STRING, vendor_string_length+1); + vorbiscomment->data.vorbis_comment.num_comments = 2; + vorbiscomment->data.vorbis_comment.comments = (FLAC__StreamMetadata_VorbisComment_Entry*)malloc_or_die_(vorbiscomment->data.vorbis_comment.num_comments * sizeof(FLAC__StreamMetadata_VorbisComment_Entry)); + vorbiscomment->data.vorbis_comment.comments[0].length = 5; + vorbiscomment->data.vorbis_comment.comments[0].entry = (FLAC__byte*)malloc_or_die_(5+1); + memcpy(vorbiscomment->data.vorbis_comment.comments[0].entry, "ab=cd", 5+1); + vorbiscomment->data.vorbis_comment.comments[1].length = 0; + vorbiscomment->data.vorbis_comment.comments[1].entry = 0; + } + + cuesheet->is_last = false; + cuesheet->type = FLAC__METADATA_TYPE_CUESHEET; + cuesheet->length = + /* cuesheet guts */ + ( + FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN + + FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN + + FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN + + FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN + + FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN + ) / 8 + + /* 2 tracks */ + 3 * ( + FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN + + FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN + + FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN + + FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN + + FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN + + FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN + + FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN + ) / 8 + + /* 3 index points */ + 3 * ( + FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN + + FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN + + FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN + ) / 8 + ; + memset(cuesheet->data.cue_sheet.media_catalog_number, 0, sizeof(cuesheet->data.cue_sheet.media_catalog_number)); + cuesheet->data.cue_sheet.media_catalog_number[0] = 'j'; + cuesheet->data.cue_sheet.media_catalog_number[1] = 'C'; + cuesheet->data.cue_sheet.lead_in = 2 * 44100; + cuesheet->data.cue_sheet.is_cd = true; + cuesheet->data.cue_sheet.num_tracks = 3; + cuesheet->data.cue_sheet.tracks = (FLAC__StreamMetadata_CueSheet_Track*)calloc_or_die_(cuesheet->data.cue_sheet.num_tracks, sizeof(FLAC__StreamMetadata_CueSheet_Track)); + cuesheet->data.cue_sheet.tracks[0].offset = 0; + cuesheet->data.cue_sheet.tracks[0].number = 1; + memcpy(cuesheet->data.cue_sheet.tracks[0].isrc, "ACBDE1234567", sizeof(cuesheet->data.cue_sheet.tracks[0].isrc)); + cuesheet->data.cue_sheet.tracks[0].type = 0; + cuesheet->data.cue_sheet.tracks[0].pre_emphasis = 1; + cuesheet->data.cue_sheet.tracks[0].num_indices = 2; + cuesheet->data.cue_sheet.tracks[0].indices = (FLAC__StreamMetadata_CueSheet_Index*)malloc_or_die_(cuesheet->data.cue_sheet.tracks[0].num_indices * sizeof(FLAC__StreamMetadata_CueSheet_Index)); + cuesheet->data.cue_sheet.tracks[0].indices[0].offset = 0; + cuesheet->data.cue_sheet.tracks[0].indices[0].number = 0; + cuesheet->data.cue_sheet.tracks[0].indices[1].offset = 123 * 588; + cuesheet->data.cue_sheet.tracks[0].indices[1].number = 1; + cuesheet->data.cue_sheet.tracks[1].offset = 1234 * 588; + cuesheet->data.cue_sheet.tracks[1].number = 2; + memcpy(cuesheet->data.cue_sheet.tracks[1].isrc, "ACBDE7654321", sizeof(cuesheet->data.cue_sheet.tracks[1].isrc)); + cuesheet->data.cue_sheet.tracks[1].type = 1; + cuesheet->data.cue_sheet.tracks[1].pre_emphasis = 0; + cuesheet->data.cue_sheet.tracks[1].num_indices = 1; + cuesheet->data.cue_sheet.tracks[1].indices = (FLAC__StreamMetadata_CueSheet_Index*)malloc_or_die_(cuesheet->data.cue_sheet.tracks[1].num_indices * sizeof(FLAC__StreamMetadata_CueSheet_Index)); + cuesheet->data.cue_sheet.tracks[1].indices[0].offset = 0; + cuesheet->data.cue_sheet.tracks[1].indices[0].number = 1; + cuesheet->data.cue_sheet.tracks[2].offset = 12345 * 588; + cuesheet->data.cue_sheet.tracks[2].number = 170; + cuesheet->data.cue_sheet.tracks[2].num_indices = 0; + + picture->is_last = false; + picture->type = FLAC__METADATA_TYPE_PICTURE; + picture->length = + ( + FLAC__STREAM_METADATA_PICTURE_TYPE_LEN + + FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN + /* will add the length for the string later */ + FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN + /* will add the length for the string later */ + FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN + + FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN + + FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN + + FLAC__STREAM_METADATA_PICTURE_COLORS_LEN + + FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN /* will add the length for the data later */ + ) / 8 + ; + picture->data.picture.type = FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER; + picture->data.picture.mime_type = strdup_or_die_("image/jpeg"); + picture->length += strlen(picture->data.picture.mime_type); + picture->data.picture.description = (FLAC__byte*)strdup_or_die_("desc"); + picture->length += strlen((const char *)picture->data.picture.description); + picture->data.picture.width = 300; + picture->data.picture.height = 300; + picture->data.picture.depth = 24; + picture->data.picture.colors = 0; + picture->data.picture.data = (FLAC__byte*)strdup_or_die_("SOMEJPEGDATA"); + picture->data.picture.data_length = strlen((const char *)picture->data.picture.data); + picture->length += picture->data.picture.data_length; + + unknown->is_last = true; + unknown->type = 126; + unknown->length = 8; + unknown->data.unknown.data = (FLAC__byte*)malloc_or_die_(unknown->length); + memcpy(unknown->data.unknown.data, "\xfe\xdc\xba\x98\xf0\xe1\xd2\xc3", unknown->length); +} + +void mutils__free_metadata_blocks( + FLAC__StreamMetadata *streaminfo, + FLAC__StreamMetadata *padding, + FLAC__StreamMetadata *seektable, + FLAC__StreamMetadata *application1, + FLAC__StreamMetadata *application2, + FLAC__StreamMetadata *vorbiscomment, + FLAC__StreamMetadata *cuesheet, + FLAC__StreamMetadata *picture, + FLAC__StreamMetadata *unknown +) +{ + (void)streaminfo, (void)padding, (void)application2; + free(seektable->data.seek_table.points); + free(application1->data.application.data); + free(vorbiscomment->data.vorbis_comment.vendor_string.entry); + free(vorbiscomment->data.vorbis_comment.comments[0].entry); + free(vorbiscomment->data.vorbis_comment.comments); + free(cuesheet->data.cue_sheet.tracks[0].indices); + free(cuesheet->data.cue_sheet.tracks[1].indices); + free(cuesheet->data.cue_sheet.tracks); + free(picture->data.picture.mime_type); + free(picture->data.picture.description); + free(picture->data.picture.data); + free(unknown->data.unknown.data); +} diff --git a/src/FLAC/src/test_seeking/Makefile.am b/src/FLAC/src/test_seeking/Makefile.am new file mode 100644 index 00000000..09cc5efa --- /dev/null +++ b/src/FLAC/src/test_seeking/Makefile.am @@ -0,0 +1,28 @@ +# test_seeking - Seeking tester for libFLAC +# Copyright (C) 2004,2005,2006,2007 Josh Coalson +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +AM_CFLAGS = -I$(top_srcdir)/src/FLAC/include -I$(top_srcdir)/src/OGG/include + +INCLUDES = + +noinst_PROGRAMS = test_seeking +test_seeking_LDADD = \ + $(top_builddir)/src/FLAC/src/libFLAC/libFLAC.la \ + $(top_builddir)/src/OGG/libogg.la \ + -lm +test_seeking_SOURCES = \ + main.c diff --git a/src/FLAC/src/test_seeking/main.c b/src/FLAC/src/test_seeking/main.c new file mode 100644 index 00000000..1a2f3a01 --- /dev/null +++ b/src/FLAC/src/test_seeking/main.c @@ -0,0 +1,399 @@ +/* test_seeking - Seeking tester for libFLAC + * Copyright (C) 2004,2005,2006,2007 Josh Coalson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#if defined _MSC_VER || defined __MINGW32__ +#include +#else +#include +#endif +#include /* for stat() */ +#include "FLAC/assert.h" +#include "FLAC/stream_decoder.h" + +typedef struct { + FLAC__bool got_data; + FLAC__uint64 total_samples; + unsigned channels; + unsigned bits_per_sample; + FLAC__bool quiet; + FLAC__bool ignore_errors; + FLAC__bool error_occurred; +} DecoderClientData; + +static FLAC__bool stop_signal_ = false; + +static void our_sigint_handler_(int signo) +{ + (void)signo; + printf("(caught SIGINT) "); + fflush(stdout); + stop_signal_ = true; +} + +static FLAC__bool die_(const char *msg) +{ + printf("ERROR: %s\n", msg); + return false; +} + +static FLAC__bool die_s_(const char *msg, const FLAC__StreamDecoder *decoder) +{ + FLAC__StreamDecoderState state = FLAC__stream_decoder_get_state(decoder); + + if(msg) + printf("FAILED, %s", msg); + else + printf("FAILED"); + + printf(", state = %u (%s)\n", (unsigned)state, FLAC__StreamDecoderStateString[state]); + + return false; +} + +static off_t get_filesize_(const char *srcpath) +{ + struct stat srcstat; + + if(0 == stat(srcpath, &srcstat)) + return srcstat.st_size; + else + return -1; +} + +static FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data) +{ + DecoderClientData *dcd = (DecoderClientData*)client_data; + + (void)decoder, (void)buffer; + + if(0 == dcd) { + printf("ERROR: client_data in write callback is NULL\n"); + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + + if(dcd->error_occurred) + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + + if (frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_FRAME_NUMBER) { + if (!dcd->quiet) + printf("frame@%uf(%u)... ", frame->header.number.frame_number, frame->header.blocksize); + } + else if (frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER) { + if (!dcd->quiet) +#ifdef _MSC_VER + printf("frame@%I64u(%u)... ", frame->header.number.sample_number, frame->header.blocksize); +#else + printf("frame@%llu(%u)... ", (unsigned long long)frame->header.number.sample_number, frame->header.blocksize); +#endif + } + else { + FLAC__ASSERT(0); + dcd->error_occurred = true; + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + fflush(stdout); + + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; +} + +static void metadata_callback_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) +{ + DecoderClientData *dcd = (DecoderClientData*)client_data; + + (void)decoder; + + if(0 == dcd) { + printf("ERROR: client_data in metadata callback is NULL\n"); + return; + } + + if(dcd->error_occurred) + return; + + if (!dcd->got_data && metadata->type == FLAC__METADATA_TYPE_STREAMINFO) { + dcd->got_data = true; + dcd->total_samples = metadata->data.stream_info.total_samples; + dcd->channels = metadata->data.stream_info.channels; + dcd->bits_per_sample = metadata->data.stream_info.bits_per_sample; + } +} + +static void error_callback_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) +{ + DecoderClientData *dcd = (DecoderClientData*)client_data; + + (void)decoder; + + if(0 == dcd) { + printf("ERROR: client_data in error callback is NULL\n"); + return; + } + + if(!dcd->ignore_errors) { + printf("ERROR: got error callback: err = %u (%s)\n", (unsigned)status, FLAC__StreamDecoderErrorStatusString[status]); + dcd->error_occurred = true; + } +} + +/* read mode: + * 0 - no read after seek + * 1 - read 2 frames + * 2 - read until end + */ +static FLAC__bool seek_barrage(FLAC__bool is_ogg, const char *filename, off_t filesize, unsigned count, FLAC__int64 total_samples, unsigned read_mode) +{ + FLAC__StreamDecoder *decoder; + DecoderClientData decoder_client_data; + unsigned i; + long int n; + + decoder_client_data.got_data = false; + decoder_client_data.total_samples = 0; + decoder_client_data.quiet = false; + decoder_client_data.ignore_errors = false; + decoder_client_data.error_occurred = false; + + printf("\n+++ seek test: FLAC__StreamDecoder (%s FLAC, read_mode=%u)\n\n", is_ogg? "Ogg":"native", read_mode); + + decoder = FLAC__stream_decoder_new(); + if(0 == decoder) + return die_("FLAC__stream_decoder_new() FAILED, returned NULL\n"); + + if(is_ogg) { + if(FLAC__stream_decoder_init_ogg_file(decoder, filename, write_callback_, metadata_callback_, error_callback_, &decoder_client_data) != FLAC__STREAM_DECODER_INIT_STATUS_OK) + return die_s_("FLAC__stream_decoder_init_file() FAILED", decoder); + } + else { + if(FLAC__stream_decoder_init_file(decoder, filename, write_callback_, metadata_callback_, error_callback_, &decoder_client_data) != FLAC__STREAM_DECODER_INIT_STATUS_OK) + return die_s_("FLAC__stream_decoder_init_file() FAILED", decoder); + } + + if(!FLAC__stream_decoder_process_until_end_of_metadata(decoder)) + return die_s_("FLAC__stream_decoder_process_until_end_of_metadata() FAILED", decoder); + + if(!is_ogg) { /* not necessary to do this for Ogg because of its seeking method */ + /* process until end of stream to make sure we can still seek in that state */ + decoder_client_data.quiet = true; + if(!FLAC__stream_decoder_process_until_end_of_stream(decoder)) + return die_s_("FLAC__stream_decoder_process_until_end_of_stream() FAILED", decoder); + decoder_client_data.quiet = false; + + printf("stream decoder state is %s\n", FLAC__stream_decoder_get_resolved_state_string(decoder)); + if(FLAC__stream_decoder_get_state(decoder) != FLAC__STREAM_DECODER_END_OF_STREAM) + return die_s_("expected FLAC__STREAM_DECODER_END_OF_STREAM", decoder); + } + +#ifdef _MSC_VER + printf("file's total_samples is %I64u\n", decoder_client_data.total_samples); +#else + printf("file's total_samples is %llu\n", (unsigned long long)decoder_client_data.total_samples); +#endif +#if !defined _MSC_VER && !defined __MINGW32__ && !defined __EMX__ + if (decoder_client_data.total_samples > (FLAC__uint64)RAND_MAX) { + printf("ERROR: must be total_samples < %u\n", (unsigned)RAND_MAX); + return false; + } +#endif + n = (long int)decoder_client_data.total_samples; + + if(n == 0 && total_samples >= 0) + n = (long int)total_samples; + + /* if we don't have a total samples count, just guess based on the file size */ + /* @@@ for is_ogg we should get it from last page's granulepos */ + if(n == 0) { + /* 8 would imply no compression, 9 guarantees that we will get some samples off the end of the stream to test that case */ + n = 9 * filesize / (decoder_client_data.channels * decoder_client_data.bits_per_sample); +#if !defined _MSC_VER && !defined __MINGW32__ + if(n > RAND_MAX) + n = RAND_MAX; +#endif + } + + printf("Begin seek barrage, count=%u\n", count); + + for (i = 0; !stop_signal_ && (count == 0 || i < count); i++) { + FLAC__uint64 pos; + + /* for the first 10, seek to the first 10 samples */ + if (n >= 10 && i < 10) { + pos = i; + } + /* for the second 10, seek to the last 10 samples */ + else if (n >= 10 && i < 20) { + pos = n - 1 - (i-10); + } + /* for the third 10, seek past the end and make sure we fail properly as expected */ + else if (i < 30) { + pos = n + (i-20); + } + else { +#if !defined _MSC_VER && !defined __MINGW32__ + pos = (FLAC__uint64)(random() % n); +#else + /* RAND_MAX is only 32767 in my MSVC */ + pos = (FLAC__uint64)((rand()<<15|rand()) % n); +#endif + } + +#ifdef _MSC_VER + printf("seek(%I64u)... ", pos); +#else + printf("seek(%llu)... ", (unsigned long long)pos); +#endif + fflush(stdout); + if(!FLAC__stream_decoder_seek_absolute(decoder, pos)) { + if(pos >= (FLAC__uint64)n) + printf("seek past end failed as expected... "); + else if(decoder_client_data.total_samples == 0 && total_samples <= 0) + printf("seek failed, assuming it was past EOF... "); + else + return die_s_("FLAC__stream_decoder_seek_absolute() FAILED", decoder); + if(!FLAC__stream_decoder_flush(decoder)) + return die_s_("FLAC__stream_decoder_flush() FAILED", decoder); + } + else if(read_mode == 1) { + printf("decode_frame... "); + fflush(stdout); + if(!FLAC__stream_decoder_process_single(decoder)) + return die_s_("FLAC__stream_decoder_process_single() FAILED", decoder); + + printf("decode_frame... "); + fflush(stdout); + if(!FLAC__stream_decoder_process_single(decoder)) + return die_s_("FLAC__stream_decoder_process_single() FAILED", decoder); + } + else if(read_mode == 2) { + printf("decode_all... "); + fflush(stdout); + decoder_client_data.quiet = true; + if(!FLAC__stream_decoder_process_until_end_of_stream(decoder)) + return die_s_("FLAC__stream_decoder_process_until_end_of_stream() FAILED", decoder); + decoder_client_data.quiet = false; + } + + printf("OK\n"); + fflush(stdout); + } + + if(FLAC__stream_decoder_get_state(decoder) != FLAC__STREAM_DECODER_UNINITIALIZED) { + if(!FLAC__stream_decoder_finish(decoder)) + return die_s_("FLAC__stream_decoder_finish() FAILED", decoder); + } + + FLAC__stream_decoder_delete(decoder); + printf("\nPASSED!\n"); + + return true; +} + +#ifdef _MSC_VER +/* There's no strtoull() in MSVC6 so we just write a specialized one */ +static FLAC__uint64 local__strtoull(const char *src) +{ + FLAC__uint64 ret = 0; + int c; + FLAC__ASSERT(0 != src); + while(0 != (c = *src++)) { + c -= '0'; + if(c >= 0 && c <= 9) + ret = (ret * 10) + c; + else + break; + } + return ret; +} +#endif + +int main(int argc, char *argv[]) +{ + const char *filename; + unsigned count = 0, read_mode; + FLAC__int64 samples = -1; + off_t filesize; + + static const char * const usage = "usage: test_seeking file.flac [#seeks] [#samples-in-file.flac]\n"; + + if (argc < 2 || argc > 4) { + fprintf(stderr, usage); + return 1; + } + + filename = argv[1]; + + if (argc > 2) + count = strtoul(argv[2], 0, 10); + if (argc > 3) +#ifdef _MSC_VER + samples = local__strtoull(argv[3]); +#else + samples = strtoull(argv[3], 0, 10); +#endif + + if (count < 30) + fprintf(stderr, "WARNING: random seeks don't kick in until after 30 preprogrammed ones\n"); + +#if !defined _MSC_VER && !defined __MINGW32__ + { + struct timeval tv; + + if (gettimeofday(&tv, 0) < 0) { + fprintf(stderr, "WARNING: couldn't seed RNG with time\n"); + tv.tv_usec = 4321; + } + srandom(tv.tv_usec); + } +#else + srand((unsigned)time(0)); +#endif + + filesize = get_filesize_(filename); + if (filesize < 0) { + fprintf(stderr, "ERROR: can't determine filesize for %s\n", filename); + return 1; + } + + (void) signal(SIGINT, our_sigint_handler_); + + for (read_mode = 0; read_mode <= 2; read_mode++) { + FLAC__bool ok; + if (strlen(filename) > 4 && 0 == strcmp(filename+strlen(filename)-4, ".ogg")) { +#if FLAC__HAS_OGG + ok = seek_barrage(/*is_ogg=*/true, filename, filesize, count, samples, read_mode); +#else + fprintf(stderr, "ERROR: Ogg FLAC not supported\n"); + ok = false; +#endif + } + else { + ok = seek_barrage(/*is_ogg=*/false, filename, filesize, count, samples, read_mode); + } + if (!ok) + return 2; + } + + return 0; +} diff --git a/src/FLAC/src/test_streams/Makefile.am b/src/FLAC/src/test_streams/Makefile.am new file mode 100644 index 00000000..422bcde6 --- /dev/null +++ b/src/FLAC/src/test_streams/Makefile.am @@ -0,0 +1,26 @@ +# test_streams - Simple test pattern generator +# Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +noinst_PROGRAMS = test_streams + +INCLUDES = -I$(top_srcdir)/src/FLAC/include + +test_streams_SOURCES = \ + main.c +test_streams_LDADD = -lm + +CLEANFILES = $(wildcard *.raw) diff --git a/src/FLAC/src/test_streams/main.c b/src/FLAC/src/test_streams/main.c new file mode 100644 index 00000000..f3f14ecf --- /dev/null +++ b/src/FLAC/src/test_streams/main.c @@ -0,0 +1,933 @@ +/* test_streams - Simple test pattern generator + * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#if defined _MSC_VER || defined __MINGW32__ +#include +#else +#include +#endif +#include "FLAC/assert.h" +#include "FLAC/ordinals.h" + +#ifndef M_PI +/* math.h in VC++ doesn't seem to have this (how Microsoft is that?) */ +#define M_PI 3.14159265358979323846 +#endif + +#if defined _WIN32 || defined __EMX__ + static const char *mode = "wb"; +#else + static const char *mode = "w"; +#endif + +#if !defined _MSC_VER && !defined __MINGW32__ +#define GET_RANDOM_BYTE (((unsigned)random()) & 0xff) +#else +#define GET_RANDOM_BYTE (((unsigned)rand()) & 0xff) +#endif + +static FLAC__bool is_big_endian_host; + + +static FLAC__bool write_little_endian(FILE *f, FLAC__int32 x, size_t bytes) +{ + while(bytes) { + if(fputc(x, f) == EOF) + return false; + x >>= 8; + bytes--; + } + return true; +} + +static FLAC__bool write_little_endian_uint16(FILE *f, FLAC__uint16 x) +{ + return + fputc(x, f) != EOF && + fputc(x >> 8, f) != EOF + ; +} + +static FLAC__bool write_little_endian_int16(FILE *f, FLAC__int16 x) +{ + return write_little_endian_uint16(f, (FLAC__uint16)x); +} + +static FLAC__bool write_little_endian_uint24(FILE *f, FLAC__uint32 x) +{ + return + fputc(x, f) != EOF && + fputc(x >> 8, f) != EOF && + fputc(x >> 16, f) != EOF + ; +} + +static FLAC__bool write_little_endian_int24(FILE *f, FLAC__int32 x) +{ + return write_little_endian_uint24(f, (FLAC__uint32)x); +} + +static FLAC__bool write_little_endian_uint32(FILE *f, FLAC__uint32 x) +{ + return + fputc(x, f) != EOF && + fputc(x >> 8, f) != EOF && + fputc(x >> 16, f) != EOF && + fputc(x >> 24, f) != EOF + ; +} + +#if 0 +/* @@@ not used (yet) */ +static FLAC__bool write_little_endian_int32(FILE *f, FLAC__int32 x) +{ + return write_little_endian_uint32(f, (FLAC__uint32)x); +} +#endif + +static FLAC__bool write_big_endian(FILE *f, FLAC__int32 x, size_t bytes) +{ + if(bytes < 4) + x <<= 8*(4-bytes); + while(bytes) { + if(fputc(x>>24, f) == EOF) + return false; + x <<= 8; + bytes--; + } + return true; +} + +static FLAC__bool write_big_endian_uint16(FILE *f, FLAC__uint16 x) +{ + return + fputc(x >> 8, f) != EOF && + fputc(x, f) != EOF + ; +} + +#if 0 +/* @@@ not used (yet) */ +static FLAC__bool write_big_endian_int16(FILE *f, FLAC__int16 x) +{ + return write_big_endian_uint16(f, (FLAC__uint16)x); +} +#endif + +#if 0 +/* @@@ not used (yet) */ +static FLAC__bool write_big_endian_uint24(FILE *f, FLAC__uint32 x) +{ + return + fputc(x >> 16, f) != EOF && + fputc(x >> 8, f) != EOF && + fputc(x, f) != EOF + ; +} +#endif + +#if 0 +/* @@@ not used (yet) */ +static FLAC__bool write_big_endian_int24(FILE *f, FLAC__int32 x) +{ + return write_big_endian_uint24(f, (FLAC__uint32)x); +} +#endif + +static FLAC__bool write_big_endian_uint32(FILE *f, FLAC__uint32 x) +{ + return + fputc(x >> 24, f) != EOF && + fputc(x >> 16, f) != EOF && + fputc(x >> 8, f) != EOF && + fputc(x, f) != EOF + ; +} + +#if 0 +/* @@@ not used (yet) */ +static FLAC__bool write_big_endian_int32(FILE *f, FLAC__int32 x) +{ + return write_big_endian_uint32(f, (FLAC__uint32)x); +} +#endif + +static FLAC__bool write_sane_extended(FILE *f, unsigned val) + /* Write to 'f' a SANE extended representation of 'val'. Return false if + * the write succeeds; return true otherwise. + * + * SANE extended is an 80-bit IEEE-754 representation with sign bit, 15 bits + * of exponent, and 64 bits of significand (mantissa). Unlike most IEEE-754 + * representations, it does not imply a 1 above the MSB of the significand. + * + * Preconditions: + * val!=0U + */ +{ + unsigned int shift, exponent; + + FLAC__ASSERT(val!=0U); /* handling 0 would require a special case */ + + for(shift= 0U; (val>>(31-shift))==0U; ++shift) + ; + val<<= shift; + exponent= 63U-(shift+32U); /* add 32 for unused second word */ + + if(!write_big_endian_uint16(f, (FLAC__uint16)(exponent+0x3FFF))) + return false; + if(!write_big_endian_uint32(f, val)) + return false; + if(!write_big_endian_uint32(f, 0)) /* unused second word */ + return false; + + return true; +} + +/* a mono one-sample 16bps stream */ +static FLAC__bool generate_01(void) +{ + FILE *f; + FLAC__int16 x = -32768; + + if(0 == (f = fopen("test01.raw", mode))) + return false; + + if(!write_little_endian_int16(f, x)) + goto foo; + + fclose(f); + return true; +foo: + fclose(f); + return false; +} + +/* a stereo one-sample 16bps stream */ +static FLAC__bool generate_02(void) +{ + FILE *f; + FLAC__int16 xl = -32768, xr = 32767; + + if(0 == (f = fopen("test02.raw", mode))) + return false; + + if(!write_little_endian_int16(f, xl)) + goto foo; + if(!write_little_endian_int16(f, xr)) + goto foo; + + fclose(f); + return true; +foo: + fclose(f); + return false; +} + +/* a mono five-sample 16bps stream */ +static FLAC__bool generate_03(void) +{ + FILE *f; + FLAC__int16 x[] = { -25, 0, 25, 50, 100 }; + unsigned i; + + if(0 == (f = fopen("test03.raw", mode))) + return false; + + for(i = 0; i < 5; i++) + if(!write_little_endian_int16(f, x[i])) + goto foo; + + fclose(f); + return true; +foo: + fclose(f); + return false; +} + +/* a stereo five-sample 16bps stream */ +static FLAC__bool generate_04(void) +{ + FILE *f; + FLAC__int16 x[] = { -25, 500, 0, 400, 25, 300, 50, 200, 100, 100 }; + unsigned i; + + if(0 == (f = fopen("test04.raw", mode))) + return false; + + for(i = 0; i < 10; i++) + if(!write_little_endian_int16(f, x[i])) + goto foo; + + fclose(f); + return true; +foo: + fclose(f); + return false; +} + +/* a mono full-scale deflection 8bps stream */ +static FLAC__bool generate_fsd8(const char *fn, const int pattern[], unsigned reps) +{ + FILE *f; + unsigned rep, p; + + FLAC__ASSERT(pattern != 0); + + if(0 == (f = fopen(fn, mode))) + return false; + + for(rep = 0; rep < reps; rep++) { + for(p = 0; pattern[p]; p++) { + signed char x = pattern[p] > 0? 127 : -128; + if(fwrite(&x, sizeof(x), 1, f) < 1) + goto foo; + } + } + + fclose(f); + return true; +foo: + fclose(f); + return false; +} + +/* a mono full-scale deflection 16bps stream */ +static FLAC__bool generate_fsd16(const char *fn, const int pattern[], unsigned reps) +{ + FILE *f; + unsigned rep, p; + + FLAC__ASSERT(pattern != 0); + + if(0 == (f = fopen(fn, mode))) + return false; + + for(rep = 0; rep < reps; rep++) { + for(p = 0; pattern[p]; p++) { + FLAC__int16 x = pattern[p] > 0? 32767 : -32768; + if(!write_little_endian_int16(f, x)) + goto foo; + } + } + + fclose(f); + return true; +foo: + fclose(f); + return false; +} + +/* a stereo wasted-bits-per-sample 16bps stream */ +static FLAC__bool generate_wbps16(const char *fn, unsigned samples) +{ + FILE *f; + unsigned sample; + + if(0 == (f = fopen(fn, mode))) + return false; + + for(sample = 0; sample < samples; sample++) { + FLAC__int16 l = (sample % 2000) << 2; + FLAC__int16 r = (sample % 1000) << 3; + if(!write_little_endian_int16(f, l)) + goto foo; + if(!write_little_endian_int16(f, r)) + goto foo; + } + + fclose(f); + return true; +foo: + fclose(f); + return false; +} + +/* a mono full-scale deflection 24bps stream */ +static FLAC__bool generate_fsd24(const char *fn, const int pattern[], unsigned reps) +{ + FILE *f; + unsigned rep, p; + + FLAC__ASSERT(pattern != 0); + + if(0 == (f = fopen(fn, mode))) + return false; + + for(rep = 0; rep < reps; rep++) { + for(p = 0; pattern[p]; p++) { + FLAC__int32 x = pattern[p] > 0? 8388607 : -8388608; + if(!write_little_endian_int24(f, x)) + goto foo; + } + } + + fclose(f); + return true; +foo: + fclose(f); + return false; +} + +/* a mono sine-wave 8bps stream */ +static FLAC__bool generate_sine8_1(const char *fn, const double sample_rate, const unsigned samples, const double f1, const double a1, const double f2, const double a2) +{ + const FLAC__int8 full_scale = 127; + const double delta1 = 2.0 * M_PI / ( sample_rate / f1); + const double delta2 = 2.0 * M_PI / ( sample_rate / f2); + FILE *f; + double theta1, theta2; + unsigned i; + + if(0 == (f = fopen(fn, mode))) + return false; + + for(i = 0, theta1 = theta2 = 0.0; i < samples; i++, theta1 += delta1, theta2 += delta2) { + double val = (a1*sin(theta1) + a2*sin(theta2))*(double)full_scale; + FLAC__int8 v = (FLAC__int8)(val + 0.5); + if(fwrite(&v, sizeof(v), 1, f) < 1) + goto foo; + } + + fclose(f); + return true; +foo: + fclose(f); + return false; +} + +/* a stereo sine-wave 8bps stream */ +static FLAC__bool generate_sine8_2(const char *fn, const double sample_rate, const unsigned samples, const double f1, const double a1, const double f2, const double a2, double fmult) +{ + const FLAC__int8 full_scale = 127; + const double delta1 = 2.0 * M_PI / ( sample_rate / f1); + const double delta2 = 2.0 * M_PI / ( sample_rate / f2); + FILE *f; + double theta1, theta2; + unsigned i; + + if(0 == (f = fopen(fn, mode))) + return false; + + for(i = 0, theta1 = theta2 = 0.0; i < samples; i++, theta1 += delta1, theta2 += delta2) { + double val = (a1*sin(theta1) + a2*sin(theta2))*(double)full_scale; + FLAC__int8 v = (FLAC__int8)(val + 0.5); + if(fwrite(&v, sizeof(v), 1, f) < 1) + goto foo; + val = -(a1*sin(theta1*fmult) + a2*sin(theta2*fmult))*(double)full_scale; + v = (FLAC__int8)(val + 0.5); + if(fwrite(&v, sizeof(v), 1, f) < 1) + goto foo; + } + + fclose(f); + return true; +foo: + fclose(f); + return false; +} + +/* a mono sine-wave 16bps stream */ +static FLAC__bool generate_sine16_1(const char *fn, const double sample_rate, const unsigned samples, const double f1, const double a1, const double f2, const double a2) +{ + const FLAC__int16 full_scale = 32767; + const double delta1 = 2.0 * M_PI / ( sample_rate / f1); + const double delta2 = 2.0 * M_PI / ( sample_rate / f2); + FILE *f; + double theta1, theta2; + unsigned i; + + if(0 == (f = fopen(fn, mode))) + return false; + + for(i = 0, theta1 = theta2 = 0.0; i < samples; i++, theta1 += delta1, theta2 += delta2) { + double val = (a1*sin(theta1) + a2*sin(theta2))*(double)full_scale; + FLAC__int16 v = (FLAC__int16)(val + 0.5); + if(!write_little_endian_int16(f, v)) + goto foo; + } + + fclose(f); + return true; +foo: + fclose(f); + return false; +} + +/* a stereo sine-wave 16bps stream */ +static FLAC__bool generate_sine16_2(const char *fn, const double sample_rate, const unsigned samples, const double f1, const double a1, const double f2, const double a2, double fmult) +{ + const FLAC__int16 full_scale = 32767; + const double delta1 = 2.0 * M_PI / ( sample_rate / f1); + const double delta2 = 2.0 * M_PI / ( sample_rate / f2); + FILE *f; + double theta1, theta2; + unsigned i; + + if(0 == (f = fopen(fn, mode))) + return false; + + for(i = 0, theta1 = theta2 = 0.0; i < samples; i++, theta1 += delta1, theta2 += delta2) { + double val = (a1*sin(theta1) + a2*sin(theta2))*(double)full_scale; + FLAC__int16 v = (FLAC__int16)(val + 0.5); + if(!write_little_endian_int16(f, v)) + goto foo; + val = -(a1*sin(theta1*fmult) + a2*sin(theta2*fmult))*(double)full_scale; + v = (FLAC__int16)(val + 0.5); + if(!write_little_endian_int16(f, v)) + goto foo; + } + + fclose(f); + return true; +foo: + fclose(f); + return false; +} + +/* a mono sine-wave 24bps stream */ +static FLAC__bool generate_sine24_1(const char *fn, const double sample_rate, const unsigned samples, const double f1, const double a1, const double f2, const double a2) +{ + const FLAC__int32 full_scale = 0x7fffff; + const double delta1 = 2.0 * M_PI / ( sample_rate / f1); + const double delta2 = 2.0 * M_PI / ( sample_rate / f2); + FILE *f; + double theta1, theta2; + unsigned i; + + if(0 == (f = fopen(fn, mode))) + return false; + + for(i = 0, theta1 = theta2 = 0.0; i < samples; i++, theta1 += delta1, theta2 += delta2) { + double val = (a1*sin(theta1) + a2*sin(theta2))*(double)full_scale; + FLAC__int32 v = (FLAC__int32)(val + 0.5); + if(!write_little_endian_int24(f, v)) + goto foo; + } + + fclose(f); + return true; +foo: + fclose(f); + return false; +} + +/* a stereo sine-wave 24bps stream */ +static FLAC__bool generate_sine24_2(const char *fn, const double sample_rate, const unsigned samples, const double f1, const double a1, const double f2, const double a2, double fmult) +{ + const FLAC__int32 full_scale = 0x7fffff; + const double delta1 = 2.0 * M_PI / ( sample_rate / f1); + const double delta2 = 2.0 * M_PI / ( sample_rate / f2); + FILE *f; + double theta1, theta2; + unsigned i; + + if(0 == (f = fopen(fn, mode))) + return false; + + for(i = 0, theta1 = theta2 = 0.0; i < samples; i++, theta1 += delta1, theta2 += delta2) { + double val = (a1*sin(theta1) + a2*sin(theta2))*(double)full_scale; + FLAC__int32 v = (FLAC__int32)(val + 0.5); + if(!write_little_endian_int24(f, v)) + goto foo; + val = -(a1*sin(theta1*fmult) + a2*sin(theta2*fmult))*(double)full_scale; + v = (FLAC__int32)(val + 0.5); + if(!write_little_endian_int24(f, v)) + goto foo; + } + + fclose(f); + return true; +foo: + fclose(f); + return false; +} + +static FLAC__bool generate_noise(const char *fn, unsigned bytes) +{ + FILE *f; + unsigned b; + + if(0 == (f = fopen(fn, mode))) + return false; + + for(b = 0; b < bytes; b++) { +#if !defined _MSC_VER && !defined __MINGW32__ + FLAC__byte x = (FLAC__byte)(((unsigned)random()) & 0xff); +#else + FLAC__byte x = (FLAC__byte)(((unsigned)rand()) & 0xff); +#endif + if(fwrite(&x, sizeof(x), 1, f) < 1) + goto foo; + } + + fclose(f); + return true; +foo: + fclose(f); + return false; +} + +static FLAC__bool generate_raw(const char *filename, unsigned channels, unsigned bytes_per_sample, unsigned samples) +{ + const FLAC__int32 full_scale = (1 << (bytes_per_sample*8-1)) - 1; + const double f1 = 441.0, a1 = 0.61, f2 = 661.5, a2 = 0.37; + const double delta1 = 2.0 * M_PI / ( 44100.0 / f1); + const double delta2 = 2.0 * M_PI / ( 44100.0 / f2); + double theta1, theta2; + FILE *f; + unsigned i, j; + + if(0 == (f = fopen(filename, mode))) + return false; + + for(i = 0, theta1 = theta2 = 0.0; i < samples; i++, theta1 += delta1, theta2 += delta2) { + for(j = 0; j < channels; j++) { + double val = (a1*sin(theta1) + a2*sin(theta2))*(double)full_scale; + FLAC__int32 v = (FLAC__int32)(val + 0.5) + ((GET_RANDOM_BYTE>>4)-8); + if(!write_little_endian(f, v, bytes_per_sample)) + goto foo; + } + } + + fclose(f); + return true; +foo: + fclose(f); + return false; +} + +static FLAC__bool generate_aiff(const char *filename, unsigned sample_rate, unsigned channels, unsigned bps, unsigned samples) +{ + const unsigned bytes_per_sample = (bps+7)/8; + const unsigned true_size = channels * bytes_per_sample * samples; + const unsigned padded_size = (true_size + 1) & (~1u); + const unsigned shift = (bps%8)? 8 - (bps%8) : 0; + const FLAC__int32 full_scale = (1 << (bps-1)) - 1; + const double f1 = 441.0, a1 = 0.61, f2 = 661.5, a2 = 0.37; + const double delta1 = 2.0 * M_PI / ( sample_rate / f1); + const double delta2 = 2.0 * M_PI / ( sample_rate / f2); + double theta1, theta2; + FILE *f; + unsigned i, j; + + if(0 == (f = fopen(filename, mode))) + return false; + if(fwrite("FORM", 1, 4, f) < 4) + goto foo; + if(!write_big_endian_uint32(f, padded_size + 46)) + goto foo; + if(fwrite("AIFFCOMM\000\000\000\022", 1, 12, f) < 12) + goto foo; + if(!write_big_endian_uint16(f, (FLAC__uint16)channels)) + goto foo; + if(!write_big_endian_uint32(f, samples)) + goto foo; + if(!write_big_endian_uint16(f, (FLAC__uint16)bps)) + goto foo; + if(!write_sane_extended(f, sample_rate)) + goto foo; + if(fwrite("SSND", 1, 4, f) < 4) + goto foo; + if(!write_big_endian_uint32(f, true_size + 8)) + goto foo; + if(fwrite("\000\000\000\000\000\000\000\000", 1, 8, f) < 8) + goto foo; + + for(i = 0, theta1 = theta2 = 0.0; i < samples; i++, theta1 += delta1, theta2 += delta2) { + for(j = 0; j < channels; j++) { + double val = (a1*sin(theta1) + a2*sin(theta2))*(double)full_scale; + FLAC__int32 v = ((FLAC__int32)(val + 0.5) + ((GET_RANDOM_BYTE>>4)-8)) << shift; + if(!write_big_endian(f, v, bytes_per_sample)) + goto foo; + } + } + for(i = true_size; i < padded_size; i++) + if(fputc(0, f) == EOF) + goto foo; + + fclose(f); + return true; +foo: + fclose(f); + return false; +} + +static FLAC__bool generate_wav(const char *filename, unsigned sample_rate, unsigned channels, unsigned bps, unsigned samples, FLAC__bool strict) +{ + const FLAC__bool waveformatextensible = strict && (channels > 2 || (bps%8)); + /* ^^^^^^^ + * (bps%8) allows 24 bps which is technically supposed to be WAVEFORMATEXTENSIBLE but we + * write 24bps as WAVEFORMATEX since it's unambiguous and matches how flac writes it + */ + const unsigned bytes_per_sample = (bps+7)/8; + const unsigned true_size = channels * bytes_per_sample * samples; + const unsigned padded_size = (true_size + 1) & (~1u); + const unsigned shift = (bps%8)? 8 - (bps%8) : 0; + const FLAC__int32 full_scale = (1 << (bps-1)) - 1; + const double f1 = 441.0, a1 = 0.61, f2 = 661.5, a2 = 0.37; + const double delta1 = 2.0 * M_PI / ( sample_rate / f1); + const double delta2 = 2.0 * M_PI / ( sample_rate / f2); + double theta1, theta2; + FILE *f; + unsigned i, j; + + if(0 == (f = fopen(filename, mode))) + return false; + if(fwrite("RIFF", 1, 4, f) < 4) + goto foo; + if(!write_little_endian_uint32(f, padded_size + (waveformatextensible?60:36))) + goto foo; + if(fwrite("WAVEfmt ", 1, 8, f) < 8) + goto foo; + if(!write_little_endian_uint32(f, waveformatextensible?40:16)) + goto foo; + if(!write_little_endian_uint16(f, (FLAC__uint16)(waveformatextensible?65534:1))) + goto foo; + if(!write_little_endian_uint16(f, (FLAC__uint16)channels)) + goto foo; + if(!write_little_endian_uint32(f, sample_rate)) + goto foo; + if(!write_little_endian_uint32(f, sample_rate * channels * bytes_per_sample)) + goto foo; + if(!write_little_endian_uint16(f, (FLAC__uint16)(channels * bytes_per_sample))) /* block align */ + goto foo; + if(!write_little_endian_uint16(f, (FLAC__uint16)(bps+shift))) + goto foo; + if(waveformatextensible) { + if(!write_little_endian_uint16(f, (FLAC__uint16)22)) /* cbSize */ + goto foo; + if(!write_little_endian_uint16(f, (FLAC__uint16)bps)) /* validBitsPerSample */ + goto foo; + if(!write_little_endian_uint32(f, 0)) /* channelMask */ + goto foo; + /* GUID = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}} */ + if(fwrite("\x01\x00\x00\x00\x00\x00\x10\x00\x80\x00\x00\xaa\x00\x38\x9b\x71", 1, 16, f) != 16) + goto foo; + } + if(fwrite("data", 1, 4, f) < 4) + goto foo; + if(!write_little_endian_uint32(f, true_size)) + goto foo; + + for(i = 0, theta1 = theta2 = 0.0; i < samples; i++, theta1 += delta1, theta2 += delta2) { + for(j = 0; j < channels; j++) { + double val = (a1*sin(theta1) + a2*sin(theta2))*(double)full_scale; + FLAC__int32 v = ((FLAC__int32)(val + 0.5) + ((GET_RANDOM_BYTE>>4)-8)) << shift; + if(!write_little_endian(f, v, bytes_per_sample)) + goto foo; + } + } + for(i = true_size; i < padded_size; i++) + if(fputc(0, f) == EOF) + goto foo; + + fclose(f); + return true; +foo: + fclose(f); + return false; +} + +static FLAC__bool generate_wackywavs(void) +{ + FILE *f; + FLAC__byte wav[] = { + 'R', 'I', 'F', 'F', 76, 0, 0, 0, + 'W', 'A', 'V', 'E', 'f', 'a', 'c', 't', + 4, 0, 0, 0 , 'b', 'l', 'a', 'h', + 'p', 'a', 'd', ' ', 4, 0, 0, 0, + 'B', 'L', 'A', 'H', 'f', 'm', 't', ' ', + 16, 0, 0, 0, 1, 0, 1, 0, + 0x44,0xAC, 0, 0, 0, 0, 0, 0, + 2, 0, 16, 0, 'd', 'a', 't', 'a', + 16, 0, 0, 0, 0, 0, 1, 0, + 4, 0, 9, 0, 16, 0, 25, 0, + 36, 0, 49, 0, 'p', 'a', 'd', ' ', + 4, 0, 0, 0, 'b', 'l', 'a', 'h' + }; + + if(0 == (f = fopen("wacky1.wav", mode))) + return false; + if(fwrite(wav, 1, 84, f) < 84) + goto foo; + fclose(f); + + wav[4] += 12; + if(0 == (f = fopen("wacky2.wav", mode))) + return false; + if(fwrite(wav, 1, 96, f) < 96) + goto foo; + fclose(f); + + return true; +foo: + fclose(f); + return false; +} + +int main(int argc, char *argv[]) +{ + FLAC__uint32 test = 1; + unsigned channels; + + int pattern01[] = { 1, -1, 0 }; + int pattern02[] = { 1, 1, -1, 0 }; + int pattern03[] = { 1, -1, -1, 0 }; + int pattern04[] = { 1, -1, 1, -1, 0 }; + int pattern05[] = { 1, -1, -1, 1, 0 }; + int pattern06[] = { 1, -1, 1, 1, -1, 0 }; + int pattern07[] = { 1, -1, -1, 1, -1, 0 }; + + (void)argc; + (void)argv; + is_big_endian_host = (*((FLAC__byte*)(&test)))? false : true; + +#if !defined _MSC_VER && !defined __MINGW32__ + { + struct timeval tv; + + if(gettimeofday(&tv, 0) < 0) { + fprintf(stderr, "WARNING: couldn't seed RNG with time\n"); + tv.tv_usec = 4321; + } + srandom(tv.tv_usec); + } +#else + srand((unsigned)time(0)); +#endif + + if(!generate_01()) return 1; + if(!generate_02()) return 1; + if(!generate_03()) return 1; + if(!generate_04()) return 1; + + if(!generate_fsd8("fsd8-01.raw", pattern01, 100)) return 1; + if(!generate_fsd8("fsd8-02.raw", pattern02, 100)) return 1; + if(!generate_fsd8("fsd8-03.raw", pattern03, 100)) return 1; + if(!generate_fsd8("fsd8-04.raw", pattern04, 100)) return 1; + if(!generate_fsd8("fsd8-05.raw", pattern05, 100)) return 1; + if(!generate_fsd8("fsd8-06.raw", pattern06, 100)) return 1; + if(!generate_fsd8("fsd8-07.raw", pattern07, 100)) return 1; + + if(!generate_fsd16("fsd16-01.raw", pattern01, 100)) return 1; + if(!generate_fsd16("fsd16-02.raw", pattern02, 100)) return 1; + if(!generate_fsd16("fsd16-03.raw", pattern03, 100)) return 1; + if(!generate_fsd16("fsd16-04.raw", pattern04, 100)) return 1; + if(!generate_fsd16("fsd16-05.raw", pattern05, 100)) return 1; + if(!generate_fsd16("fsd16-06.raw", pattern06, 100)) return 1; + if(!generate_fsd16("fsd16-07.raw", pattern07, 100)) return 1; + + if(!generate_fsd24("fsd24-01.raw", pattern01, 100)) return 1; + if(!generate_fsd24("fsd24-02.raw", pattern02, 100)) return 1; + if(!generate_fsd24("fsd24-03.raw", pattern03, 100)) return 1; + if(!generate_fsd24("fsd24-04.raw", pattern04, 100)) return 1; + if(!generate_fsd24("fsd24-05.raw", pattern05, 100)) return 1; + if(!generate_fsd24("fsd24-06.raw", pattern06, 100)) return 1; + if(!generate_fsd24("fsd24-07.raw", pattern07, 100)) return 1; + + if(!generate_wbps16("wbps16-01.raw", 1000)) return 1; + + if(!generate_sine8_1("sine8-00.raw", 48000.0, 200000, 441.0, 0.50, 441.0, 0.49)) return 1; + if(!generate_sine8_1("sine8-01.raw", 96000.0, 200000, 441.0, 0.61, 661.5, 0.37)) return 1; + if(!generate_sine8_1("sine8-02.raw", 44100.0, 200000, 441.0, 0.50, 882.0, 0.49)) return 1; + if(!generate_sine8_1("sine8-03.raw", 44100.0, 200000, 441.0, 0.50, 4410.0, 0.49)) return 1; + if(!generate_sine8_1("sine8-04.raw", 44100.0, 200000, 8820.0, 0.70, 4410.0, 0.29)) return 1; + + if(!generate_sine8_2("sine8-10.raw", 48000.0, 200000, 441.0, 0.50, 441.0, 0.49, 1.0)) return 1; + if(!generate_sine8_2("sine8-11.raw", 48000.0, 200000, 441.0, 0.61, 661.5, 0.37, 1.0)) return 1; + if(!generate_sine8_2("sine8-12.raw", 96000.0, 200000, 441.0, 0.50, 882.0, 0.49, 1.0)) return 1; + if(!generate_sine8_2("sine8-13.raw", 44100.0, 200000, 441.0, 0.50, 4410.0, 0.49, 1.0)) return 1; + if(!generate_sine8_2("sine8-14.raw", 44100.0, 200000, 8820.0, 0.70, 4410.0, 0.29, 1.0)) return 1; + if(!generate_sine8_2("sine8-15.raw", 44100.0, 200000, 441.0, 0.50, 441.0, 0.49, 0.5)) return 1; + if(!generate_sine8_2("sine8-16.raw", 44100.0, 200000, 441.0, 0.61, 661.5, 0.37, 2.0)) return 1; + if(!generate_sine8_2("sine8-17.raw", 44100.0, 200000, 441.0, 0.50, 882.0, 0.49, 0.7)) return 1; + if(!generate_sine8_2("sine8-18.raw", 44100.0, 200000, 441.0, 0.50, 4410.0, 0.49, 1.3)) return 1; + if(!generate_sine8_2("sine8-19.raw", 44100.0, 200000, 8820.0, 0.70, 4410.0, 0.29, 0.1)) return 1; + + if(!generate_sine16_1("sine16-00.raw", 48000.0, 200000, 441.0, 0.50, 441.0, 0.49)) return 1; + if(!generate_sine16_1("sine16-01.raw", 96000.0, 200000, 441.0, 0.61, 661.5, 0.37)) return 1; + if(!generate_sine16_1("sine16-02.raw", 44100.0, 200000, 441.0, 0.50, 882.0, 0.49)) return 1; + if(!generate_sine16_1("sine16-03.raw", 44100.0, 200000, 441.0, 0.50, 4410.0, 0.49)) return 1; + if(!generate_sine16_1("sine16-04.raw", 44100.0, 200000, 8820.0, 0.70, 4410.0, 0.29)) return 1; + + if(!generate_sine16_2("sine16-10.raw", 48000.0, 200000, 441.0, 0.50, 441.0, 0.49, 1.0)) return 1; + if(!generate_sine16_2("sine16-11.raw", 48000.0, 200000, 441.0, 0.61, 661.5, 0.37, 1.0)) return 1; + if(!generate_sine16_2("sine16-12.raw", 96000.0, 200000, 441.0, 0.50, 882.0, 0.49, 1.0)) return 1; + if(!generate_sine16_2("sine16-13.raw", 44100.0, 200000, 441.0, 0.50, 4410.0, 0.49, 1.0)) return 1; + if(!generate_sine16_2("sine16-14.raw", 44100.0, 200000, 8820.0, 0.70, 4410.0, 0.29, 1.0)) return 1; + if(!generate_sine16_2("sine16-15.raw", 44100.0, 200000, 441.0, 0.50, 441.0, 0.49, 0.5)) return 1; + if(!generate_sine16_2("sine16-16.raw", 44100.0, 200000, 441.0, 0.61, 661.5, 0.37, 2.0)) return 1; + if(!generate_sine16_2("sine16-17.raw", 44100.0, 200000, 441.0, 0.50, 882.0, 0.49, 0.7)) return 1; + if(!generate_sine16_2("sine16-18.raw", 44100.0, 200000, 441.0, 0.50, 4410.0, 0.49, 1.3)) return 1; + if(!generate_sine16_2("sine16-19.raw", 44100.0, 200000, 8820.0, 0.70, 4410.0, 0.29, 0.1)) return 1; + + if(!generate_sine24_1("sine24-00.raw", 48000.0, 200000, 441.0, 0.50, 441.0, 0.49)) return 1; + if(!generate_sine24_1("sine24-01.raw", 96000.0, 200000, 441.0, 0.61, 661.5, 0.37)) return 1; + if(!generate_sine24_1("sine24-02.raw", 44100.0, 200000, 441.0, 0.50, 882.0, 0.49)) return 1; + if(!generate_sine24_1("sine24-03.raw", 44100.0, 200000, 441.0, 0.50, 4410.0, 0.49)) return 1; + if(!generate_sine24_1("sine24-04.raw", 44100.0, 200000, 8820.0, 0.70, 4410.0, 0.29)) return 1; + + if(!generate_sine24_2("sine24-10.raw", 48000.0, 200000, 441.0, 0.50, 441.0, 0.49, 1.0)) return 1; + if(!generate_sine24_2("sine24-11.raw", 48000.0, 200000, 441.0, 0.61, 661.5, 0.37, 1.0)) return 1; + if(!generate_sine24_2("sine24-12.raw", 96000.0, 200000, 441.0, 0.50, 882.0, 0.49, 1.0)) return 1; + if(!generate_sine24_2("sine24-13.raw", 44100.0, 200000, 441.0, 0.50, 4410.0, 0.49, 1.0)) return 1; + if(!generate_sine24_2("sine24-14.raw", 44100.0, 200000, 8820.0, 0.70, 4410.0, 0.29, 1.0)) return 1; + if(!generate_sine24_2("sine24-15.raw", 44100.0, 200000, 441.0, 0.50, 441.0, 0.49, 0.5)) return 1; + if(!generate_sine24_2("sine24-16.raw", 44100.0, 200000, 441.0, 0.61, 661.5, 0.37, 2.0)) return 1; + if(!generate_sine24_2("sine24-17.raw", 44100.0, 200000, 441.0, 0.50, 882.0, 0.49, 0.7)) return 1; + if(!generate_sine24_2("sine24-18.raw", 44100.0, 200000, 441.0, 0.50, 4410.0, 0.49, 1.3)) return 1; + if(!generate_sine24_2("sine24-19.raw", 44100.0, 200000, 8820.0, 0.70, 4410.0, 0.29, 0.1)) return 1; + + /* WATCHOUT: the size of noise.raw is hardcoded into test/test_flac.sh */ + if(!generate_noise("noise.raw", 65536 * 8 * 3)) return 1; + if(!generate_noise("noise8m32.raw", 32)) return 1; + if(!generate_wackywavs()) return 1; + for(channels = 1; channels <= 8; channels++) { + unsigned bits_per_sample; + for(bits_per_sample = 4; bits_per_sample <= 24; bits_per_sample++) { + static const unsigned nsamples[] = { 1, 111, 4777 } ; + unsigned samples; + for(samples = 0; samples < sizeof(nsamples)/sizeof(nsamples[0]); samples++) { + char fn[64]; + + sprintf(fn, "rt-%u-%u-%u.aiff", channels, bits_per_sample, nsamples[samples]); + if(!generate_aiff(fn, 44100, channels, bits_per_sample, nsamples[samples])) + return 1; + + sprintf(fn, "rt-%u-%u-%u.wav", channels, bits_per_sample, nsamples[samples]); + if(!generate_wav(fn, 44100, channels, bits_per_sample, nsamples[samples], /*strict=*/true)) + return 1; + + if(bits_per_sample % 8 == 0) { + sprintf(fn, "rt-%u-%u-%u.raw", channels, bits_per_sample, nsamples[samples]); + if(!generate_raw(fn, channels, bits_per_sample/8, nsamples[samples])) + return 1; + } + } + } + } + + return 0; +} diff --git a/src/FLAC/strip_non_asm_libtool_args.sh b/src/FLAC/strip_non_asm_libtool_args.sh new file mode 100755 index 00000000..d5a61f15 --- /dev/null +++ b/src/FLAC/strip_non_asm_libtool_args.sh @@ -0,0 +1,19 @@ +#!/bin/sh +# +# libtool assumes that the compiler can handle the -fPIC flag. +# This isn't always true (for example, nasm can't handle it). +# Also, on some versions of OS X it tries to pass -fno-common +# to 'as' which causes problems. +command="" +while [ $1 ]; do + if [ "$1" != "-fPIC" ]; then + if [ "$1" != "-DPIC" ]; then + if [ "$1" != "-fno-common" ]; then + command="$command $1" + fi + fi + fi + shift +done +echo $command +exec $command diff --git a/src/FLAC/test/Makefile.am b/src/FLAC/test/Makefile.am new file mode 100644 index 00000000..8da031a8 --- /dev/null +++ b/src/FLAC/test/Makefile.am @@ -0,0 +1,36 @@ +# FLAC - Free Lossless Audio Codec +# Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson +# +# This file is part the FLAC project. FLAC is comprised of several +# components distributed under difference licenses. The codec libraries +# are distributed under Xiph.Org's BSD-like license (see the file +# COPYING.Xiph in this distribution). All other programs, libraries, and +# plugins are distributed under the GPL (see COPYING.GPL). The documentation +# is distributed under the Gnu FDL (see COPYING.FDL). Each file in the +# FLAC distribution contains at the top the terms under which it may be +# distributed. +# +# Since this particular file is relevant to all components of FLAC, +# it may be distributed under the Xiph.Org license, which is the least +# restrictive of those mentioned above. See the file COPYING.Xiph in this +# distribution. + +TESTS_ENVIRONMENT = FLAC__TEST_LEVEL=@FLAC__TEST_LEVEL@ FLAC__TEST_WITH_VALRGIND=@FLAC__TEST_WITH_VALGRIND@ + +TESTS = \ + ./test_libFLAC.sh + +EXTRA_DIST = \ + test_libFLAC.sh + +CLEANFILES = \ + $(wildcard *.raw) \ + $(wildcard *.flac) \ + $(wildcard *.ogg) \ + $(wildcard *.cmp) \ + $(wildcard *.aiff) \ + $(wildcard *.wav) \ + $(wildcard *.diff) \ + $(wildcard *.log) \ + $(wildcard *.cue) \ + core diff --git a/src/FLAC/test/test_bins.sh b/src/FLAC/test/test_bins.sh new file mode 100755 index 00000000..a8c6517b --- /dev/null +++ b/src/FLAC/test/test_bins.sh @@ -0,0 +1,114 @@ +#!/bin/sh + +# FLAC - Free Lossless Audio Codec +# Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson +# +# This file is part the FLAC project. FLAC is comprised of several +# components distributed under difference licenses. The codec libraries +# are distributed under Xiph.Org's BSD-like license (see the file +# COPYING.Xiph in this distribution). All other programs, libraries, and +# plugins are distributed under the GPL (see COPYING.GPL). The documentation +# is distributed under the Gnu FDL (see COPYING.FDL). Each file in the +# FLAC distribution contains at the top the terms under which it may be +# distributed. +# +# Since this particular file is relevant to all components of FLAC, +# it may be distributed under the Xiph.Org license, which is the least +# restrictive of those mentioned above. See the file COPYING.Xiph in this +# distribution. + +die () +{ + echo $* 1>&2 + exit 1 +} + +if [ x = x"$1" ] ; then + BUILD=debug +else + BUILD="$1" +fi + +LD_LIBRARY_PATH=../src/libFLAC/.libs:$LD_LIBRARY_PATH +LD_LIBRARY_PATH=../src/share/grabbag/.libs:$LD_LIBRARY_PATH +LD_LIBRARY_PATH=../src/share/getopt/.libs:$LD_LIBRARY_PATH +LD_LIBRARY_PATH=../src/share/replaygain_analysis/.libs:$LD_LIBRARY_PATH +LD_LIBRARY_PATH=../src/share/replaygain_synthesis/.libs:$LD_LIBRARY_PATH +LD_LIBRARY_PATH=../src/share/utf8/.libs:$LD_LIBRARY_PATH +LD_LIBRARY_PATH=../obj/$BUILD/lib:$LD_LIBRARY_PATH +export LD_LIBRARY_PATH +PATH=../src/flac:$PATH +PATH=../obj/$BUILD/bin:$PATH +BINS_PATH=../../test_files/bins + +if [ x"$FLAC__TEST_LEVEL" = x ] ; then + FLAC__TEST_LEVEL=1 +fi + +flac --help 1>/dev/null 2>/dev/null || die "ERROR can't find flac executable" + +run_flac () +{ + if [ x"$FLAC__TEST_WITH_VALGRIND" = xyes ] ; then + echo "valgrind --leak-check=yes --show-reachable=yes --num-callers=100 flac $*" >>test_bins.valgrind.log + valgrind --leak-check=yes --show-reachable=yes --num-callers=100 --logfile-fd=4 flac $* 4>>test_bins.valgrind.log + else + flac $* + fi +} + +test -d ${BINS_PATH} || exit 77 + +test_file () +{ + name=$1 + channels=$2 + bps=$3 + encode_options="$4" + + echo -n "$name.bin (--channels=$channels --bps=$bps $encode_options): encode..." + cmd="run_flac --verify --silent --force --force-raw-format --endian=big --sign=signed --sample-rate=44100 --bps=$bps --channels=$channels $encode_options --no-padding $name.bin" + echo "### ENCODE $name #######################################################" >> ./streams.log + echo "### cmd=$cmd" >> ./streams.log + $cmd 2>>./streams.log || die "ERROR during encode of $name" + + echo -n "decode..." + cmd="run_flac --silent --force --endian=big --sign=signed --decode --force-raw-format $name.flac"; + echo "### DECODE $name #######################################################" >> ./streams.log + echo "### cmd=$cmd" >> ./streams.log + $cmd 2>>./streams.log || die "ERROR during decode of $name" + + ls -1l $name.bin >> ./streams.log + ls -1l $name.flac >> ./streams.log + ls -1l $name.raw >> ./streams.log + + echo -n "compare..." + cmp $name.bin $name.raw || die "ERROR during compare of $name" + + echo OK +} + +echo "Testing bins..." +for f in b00 b01 b02 b03 b04 ; do + binfile=$BINS_PATH/$f + if [ -f $binfile.bin ] ; then + for disable in '' '--disable-verbatim-subframes --disable-constant-subframes' '--disable-verbatim-subframes --disable-constant-subframes --disable-fixed-subframes' ; do + for channels in 1 2 4 8 ; do + for bps in 8 16 24 ; do + for opt in 0 1 2 4 5 6 8 ; do + for extras in '' '-p' '-e' ; do + for blocksize in '' '--lax -b 32' '--lax -b 32768' '--lax -b 65535' ; do + test_file $binfile $channels $bps "-$opt $extras $blocksize $disable" + done + done + done + if [ "$FLAC__TEST_LEVEL" -gt 1 ] ; then + test_file $binfile $channels $bps "--lax -b 16384 -m -r 8 -l 32 -e -p $disable" + fi + done + done + done + else + echo "$binfile not found, skipping." + fi +done diff --git a/src/FLAC/test/test_libFLAC.sh b/src/FLAC/test/test_libFLAC.sh new file mode 100755 index 00000000..0a4e7c02 --- /dev/null +++ b/src/FLAC/test/test_libFLAC.sh @@ -0,0 +1,49 @@ +#!/bin/sh + +# FLAC - Free Lossless Audio Codec +# Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson +# +# This file is part the FLAC project. FLAC is comprised of several +# components distributed under difference licenses. The codec libraries +# are distributed under Xiph.Org's BSD-like license (see the file +# COPYING.Xiph in this distribution). All other programs, libraries, and +# plugins are distributed under the GPL (see COPYING.GPL). The documentation +# is distributed under the Gnu FDL (see COPYING.FDL). Each file in the +# FLAC distribution contains at the top the terms under which it may be +# distributed. +# +# Since this particular file is relevant to all components of FLAC, +# it may be distributed under the Xiph.Org license, which is the least +# restrictive of those mentioned above. See the file COPYING.Xiph in this +# distribution. + +die () +{ + echo $* 1>&2 + exit 1 +} + +if [ x = x"$1" ] ; then + BUILD=debug +else + BUILD="$1" +fi + +LD_LIBRARY_PATH=../src/libFLAC/.libs:$LD_LIBRARY_PATH +LD_LIBRARY_PATH=../src/share/grabbag/.libs:$LD_LIBRARY_PATH +LD_LIBRARY_PATH=../src/share/replaygain_analysis/.libs:$LD_LIBRARY_PATH +LD_LIBRARY_PATH=../obj/$BUILD/lib:$LD_LIBRARY_PATH +export LD_LIBRARY_PATH +PATH=../src/test_libFLAC:$PATH +PATH=../obj/$BUILD/bin:$PATH + +run_test_libFLAC () +{ + if [ x"$FLAC__TEST_WITH_VALGRIND" = xyes ] ; then + valgrind --leak-check=yes --show-reachable=yes --num-callers=100 --logfile-fd=4 test_libFLAC $* 4>>test_libFLAC.valgrind.log + else + test_libFLAC $* + fi +} + +run_test_libFLAC || die "ERROR during test_libFLAC" diff --git a/src/G72x/ChangeLog b/src/G72x/ChangeLog new file mode 100644 index 00000000..aa108dff --- /dev/null +++ b/src/G72x/ChangeLog @@ -0,0 +1,50 @@ +2001-06-05 Erik de Castro Lopo + + * g72x.c + Added {} in function update () to prevent 'ambiguous else' warning messages. + +2000-07-14 Erik de Castro Lopo + + * g72x.c + Modified g72x_init_state () to fit in with the new structure of the code. + Implemented g72x_encode_block () and g72x_decode_block (). + +2000-07-12 Erik de Castro Lopo + + * g72x.h + Moved nearly all definitions and function prototypes from this file have been + moved to private.h. + Added an enum defining the 4 different G72x ADPCM codecs. + Added new function prototypes to define a cleaner interface to the encoder + and decoder. This new interface also allows samples to be processed in blocks + rather than on a sample by sample basis like the original code. + + * private.h + Added prototypes moved from g72x.h. + Changed struct g72x_state to a typedef struct { .. } G72x_PRIVATE. + Added fields to G72x_PRIVATE required for working on blocks of samples. + +2000-06-07 Erik de Castro Lopo + + * g72x.c + Fixed all compiler warnings. + Removed functions tandem_adjust() which is not required by libsndfile. + + * g721.c + Fixed all compiler warnings. + Removed functions tandem_adjust_alaw() and tandem_adjust_ulaw () which are not + required by libsndfile. + Removed second parameter to g721_encoder () which is not required. + + * g72x.h + Removed in_coding and out_coding parameters from all functions. These allowed + g72x encoding/decoding to/from A-law or u-law and are not required by libsndfile. + Removed unneeded defines for A-law, u-law and linear encoding. + + * g723_16.c + Removed second parameter (in_coding) for g723_16_encoder(). + Removed second parameter (out_coding) for g723_16_decoder(). + + * private.h + New file containing prototypes and tyepdefs private to G72x code. + diff --git a/src/G72x/Makefile.am b/src/G72x/Makefile.am new file mode 100644 index 00000000..8fb2909c --- /dev/null +++ b/src/G72x/Makefile.am @@ -0,0 +1,28 @@ +## Process this file with automake to produce Makefile.in + +EXTRA_DIST = README README.original ChangeLog + +noinst_HEADERS = g72x.h g72x_priv.h +noinst_LTLIBRARIES = libg72x.la + +noinst_PROGRAMS = g72x_test$(EXEEXT) + +CFILES = g72x.c g721.c g723_16.c g723_24.c g723_40.c + +libg72x_la_SOURCES = $(CFILES) $(noinst_HEADERS) + +g72x_test_SOURCES = g72x_test.c +g72x_test_LDADD = ./libg72x.la -lm + +check: g72x_test + ./g72x_test all + +# Disable autoheader. +AUTOHEADER=echo + +## Do not edit or modify anything in this comment block. +## The arch-tag line is a file identity tag for the GNU Arch +## revision control system. +## +## arch-tag: d417a8e8-da7f-423d-884d-f03c93379348 + diff --git a/src/G72x/README b/src/G72x/README new file mode 100644 index 00000000..e69de29b diff --git a/src/G72x/README.original b/src/G72x/README.original new file mode 100644 index 00000000..23b0e7dd --- /dev/null +++ b/src/G72x/README.original @@ -0,0 +1,94 @@ +The files in this directory comprise ANSI-C language reference implementations +of the CCITT (International Telegraph and Telephone Consultative Committee) +G.711, G.721 and G.723 voice compressions. They have been tested on Sun +SPARCstations and passed 82 out of 84 test vectors published by CCITT +(Dec. 20, 1988) for G.721 and G.723. [The two remaining test vectors, +which the G.721 decoder implementation for u-law samples did not pass, +may be in error because they are identical to two other vectors for G.723_40.] + +This source code is released by Sun Microsystems, Inc. to the public domain. +Please give your acknowledgement in product literature if this code is used +in your product implementation. + +Sun Microsystems supports some CCITT audio formats in Solaris 2.0 system +software. However, Sun's implementations have been optimized for higher +performance on SPARCstations. + + +The source files for CCITT conversion routines in this directory are: + + g72x.h header file for g721.c, g723_24.c and g723_40.c + g711.c CCITT G.711 u-law and A-law compression + g72x.c common denominator of G.721 and G.723 ADPCM codes + g721.c CCITT G.721 32Kbps ADPCM coder (with g72x.c) + g723_24.c CCITT G.723 24Kbps ADPCM coder (with g72x.c) + g723_40.c CCITT G.723 40Kbps ADPCM coder (with g72x.c) + + +Simple conversions between u-law, A-law, and 16-bit linear PCM are invoked +as follows: + + unsigned char ucode, acode; + short pcm_val; + + ucode = linear2ulaw(pcm_val); + ucode = alaw2ulaw(acode); + + acode = linear2alaw(pcm_val); + acode = ulaw2alaw(ucode); + + pcm_val = ulaw2linear(ucode); + pcm_val = alaw2linear(acode); + + +The other CCITT compression routines are invoked as follows: + + #include "g72x.h" + + struct g72x_state state; + int sample, code; + + g72x_init_state(&state); + code = {g721,g723_24,g723_40}_encoder(sample, coding, &state); + sample = {g721,g723_24,g723_40}_decoder(code, coding, &state); + +where + coding = AUDIO_ENCODING_ULAW for 8-bit u-law samples + AUDIO_ENCODING_ALAW for 8-bit A-law samples + AUDIO_ENCODING_LINEAR for 16-bit linear PCM samples + + + +This directory also includes the following sample programs: + + encode.c CCITT ADPCM encoder + decode.c CCITT ADPCM decoder + Makefile makefile for the sample programs + + +The sample programs contain examples of how to call the various compression +routines and pack/unpack the bits. The sample programs read byte streams from +stdin and write to stdout. The input/output data is raw data (no file header +or other identifying information is embedded). The sample programs are +invoked as follows: + + encode [-3|4|5] [-a|u|l] outfile + decode [-3|4|5] [-a|u|l] outfile +where: + -3 encode to (decode from) G.723 24kbps (3-bit) data + -4 encode to (decode from) G.721 32kbps (4-bit) data [the default] + -5 encode to (decode from) G.723 40kbps (5-bit) data + -a encode from (decode to) A-law data + -u encode from (decode to) u-law data [the default] + -l encode from (decode to) 16-bit linear data + +Examples: + # Read 16-bit linear and output G.721 + encode -4 -l g721file + + # Read 40Kbps G.723 and output A-law + decode -5 -a alawfile + + # Compress and then decompress u-law data using 24Kbps G.723 + encode -3 ulawout + diff --git a/src/G72x/g721.c b/src/G72x/g721.c new file mode 100644 index 00000000..4f51bb19 --- /dev/null +++ b/src/G72x/g721.c @@ -0,0 +1,162 @@ +/* + * This source code is a product of Sun Microsystems, Inc. and is provided + * for unrestricted use. Users may copy or modify this source code without + * charge. + * + * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING + * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun source code is provided with no support and without any obligation on + * the part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * g721.c + * + * Description: + * + * g721_encoder(), g721_decoder() + * + * These routines comprise an implementation of the CCITT G.721 ADPCM + * coding algorithm. Essentially, this implementation is identical to + * the bit level description except for a few deviations which + * take advantage of work station attributes, such as hardware 2's + * complement arithmetic and large memory. Specifically, certain time + * consuming operations such as multiplications are replaced + * with lookup tables and software 2's complement operations are + * replaced with hardware 2's complement. + * + * The deviation from the bit level specification (lookup tables) + * preserves the bit level performance specifications. + * + * As outlined in the G.721 Recommendation, the algorithm is broken + * down into modules. Each section of code below is preceded by + * the name of the module which it is implementing. + * + */ + +#include "g72x.h" +#include "g72x_priv.h" + +static short qtab_721[7] = {-124, 80, 178, 246, 300, 349, 400}; +/* + * Maps G.721 code word to reconstructed scale factor normalized log + * magnitude values. + */ +static short _dqlntab[16] = {-2048, 4, 135, 213, 273, 323, 373, 425, + 425, 373, 323, 273, 213, 135, 4, -2048}; + +/* Maps G.721 code word to log of scale factor multiplier. */ +static short _witab[16] = {-12, 18, 41, 64, 112, 198, 355, 1122, + 1122, 355, 198, 112, 64, 41, 18, -12}; +/* + * Maps G.721 code words to a set of values whose long and short + * term averages are computed and then compared to give an indication + * how stationary (steady state) the signal is. + */ +static short _fitab[16] = {0, 0, 0, 0x200, 0x200, 0x200, 0x600, 0xE00, + 0xE00, 0x600, 0x200, 0x200, 0x200, 0, 0, 0}; + +/* + * g721_encoder() + * + * Encodes the input vale of linear PCM, A-law or u-law data sl and returns + * the resulting code. -1 is returned for unknown input coding value. + */ +int +g721_encoder( + int sl, + G72x_STATE *state_ptr) +{ + short sezi, se, sez; /* ACCUM */ + short d; /* SUBTA */ + short sr; /* ADDB */ + short y; /* MIX */ + short dqsez; /* ADDC */ + short dq, i; + + /* linearize input sample to 14-bit PCM */ + sl >>= 2; /* 14-bit dynamic range */ + + sezi = predictor_zero(state_ptr); + sez = sezi >> 1; + se = (sezi + predictor_pole(state_ptr)) >> 1; /* estimated signal */ + + d = sl - se; /* estimation difference */ + + /* quantize the prediction difference */ + y = step_size(state_ptr); /* quantizer step size */ + i = quantize(d, y, qtab_721, 7); /* i = ADPCM code */ + + dq = reconstruct(i & 8, _dqlntab[i], y); /* quantized est diff */ + + sr = (dq < 0) ? se - (dq & 0x3FFF) : se + dq; /* reconst. signal */ + + dqsez = sr + sez - se; /* pole prediction diff. */ + + update(4, y, _witab[i] << 5, _fitab[i], dq, sr, dqsez, state_ptr); + + return (i); +} + +/* + * g721_decoder() + * + * Description: + * + * Decodes a 4-bit code of G.721 encoded data of i and + * returns the resulting linear PCM, A-law or u-law value. + * return -1 for unknown out_coding value. + */ +int +g721_decoder( + int i, + G72x_STATE *state_ptr) +{ + short sezi, sei, sez, se; /* ACCUM */ + short y; /* MIX */ + short sr; /* ADDB */ + short dq; + short dqsez; + + i &= 0x0f; /* mask to get proper bits */ + sezi = predictor_zero(state_ptr); + sez = sezi >> 1; + sei = sezi + predictor_pole(state_ptr); + se = sei >> 1; /* se = estimated signal */ + + y = step_size(state_ptr); /* dynamic quantizer step size */ + + dq = reconstruct(i & 0x08, _dqlntab[i], y); /* quantized diff. */ + + sr = (dq < 0) ? (se - (dq & 0x3FFF)) : se + dq; /* reconst. signal */ + + dqsez = sr - se + sez; /* pole prediction diff. */ + + update(4, y, _witab[i] << 5, _fitab[i], dq, sr, dqsez, state_ptr); + + /* sr was 14-bit dynamic range */ + return (sr << 2); +} +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 101b6e25-457d-490a-99ae-e2e74a26ea24 +*/ + diff --git a/src/G72x/g723_16.c b/src/G72x/g723_16.c new file mode 100644 index 00000000..0c317450 --- /dev/null +++ b/src/G72x/g723_16.c @@ -0,0 +1,169 @@ +/* + * This source code is a product of Sun Microsystems, Inc. and is provided + * for unrestricted use. Users may copy or modify this source code without + * charge. + * + * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING + * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun source code is provided with no support and without any obligation on + * the part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +/* 16kbps version created, used 24kbps code and changing as little as possible. + * G.726 specs are available from ITU's gopher or WWW site (http://www.itu.ch) + * If any errors are found, please contact me at mrand@tamu.edu + * -Marc Randolph + */ + +/* + * g723_16.c + * + * Description: + * + * g723_16_encoder(), g723_16_decoder() + * + * These routines comprise an implementation of the CCITT G.726 16 Kbps + * ADPCM coding algorithm. Essentially, this implementation is identical to + * the bit level description except for a few deviations which take advantage + * of workstation attributes, such as hardware 2's complement arithmetic. + * + */ + +#include "g72x.h" +#include "g72x_priv.h" + +/* + * Maps G.723_16 code word to reconstructed scale factor normalized log + * magnitude values. Comes from Table 11/G.726 + */ +static short _dqlntab[4] = { 116, 365, 365, 116}; + +/* Maps G.723_16 code word to log of scale factor multiplier. + * + * _witab[4] is actually {-22 , 439, 439, -22}, but FILTD wants it + * as WI << 5 (multiplied by 32), so we'll do that here + */ +static short _witab[4] = {-704, 14048, 14048, -704}; + +/* + * Maps G.723_16 code words to a set of values whose long and short + * term averages are computed and then compared to give an indication + * how stationary (steady state) the signal is. + */ + +/* Comes from FUNCTF */ +static short _fitab[4] = {0, 0xE00, 0xE00, 0}; + +/* Comes from quantizer decision level tables (Table 7/G.726) + */ +static short qtab_723_16[1] = {261}; + + +/* + * g723_16_encoder() + * + * Encodes a linear PCM, A-law or u-law input sample and returns its 2-bit code. + * Returns -1 if invalid input coding value. + */ +int +g723_16_encoder( + int sl, + G72x_STATE *state_ptr) +{ + short sei, sezi, se, sez; /* ACCUM */ + short d; /* SUBTA */ + short y; /* MIX */ + short sr; /* ADDB */ + short dqsez; /* ADDC */ + short dq, i; + + /* linearize input sample to 14-bit PCM */ + sl >>= 2; /* sl of 14-bit dynamic range */ + + sezi = predictor_zero(state_ptr); + sez = sezi >> 1; + sei = sezi + predictor_pole(state_ptr); + se = sei >> 1; /* se = estimated signal */ + + d = sl - se; /* d = estimation diff. */ + + /* quantize prediction difference d */ + y = step_size(state_ptr); /* quantizer step size */ + i = quantize(d, y, qtab_723_16, 1); /* i = ADPCM code */ + + /* Since quantize() only produces a three level output + * (1, 2, or 3), we must create the fourth one on our own + */ + if (i == 3) /* i code for the zero region */ + if ((d & 0x8000) == 0) /* If d > 0, i=3 isn't right... */ + i = 0; + + dq = reconstruct(i & 2, _dqlntab[i], y); /* quantized diff. */ + + sr = (dq < 0) ? se - (dq & 0x3FFF) : se + dq; /* reconstructed signal */ + + dqsez = sr + sez - se; /* pole prediction diff. */ + + update(2, y, _witab[i], _fitab[i], dq, sr, dqsez, state_ptr); + + return (i); +} + +/* + * g723_16_decoder() + * + * Decodes a 2-bit CCITT G.723_16 ADPCM code and returns + * the resulting 16-bit linear PCM, A-law or u-law sample value. + * -1 is returned if the output coding is unknown. + */ +int +g723_16_decoder( + int i, + G72x_STATE *state_ptr) +{ + short sezi, sei, sez, se; /* ACCUM */ + short y; /* MIX */ + short sr; /* ADDB */ + short dq; + short dqsez; + + i &= 0x03; /* mask to get proper bits */ + sezi = predictor_zero(state_ptr); + sez = sezi >> 1; + sei = sezi + predictor_pole(state_ptr); + se = sei >> 1; /* se = estimated signal */ + + y = step_size(state_ptr); /* adaptive quantizer step size */ + dq = reconstruct(i & 0x02, _dqlntab[i], y); /* unquantize pred diff */ + + sr = (dq < 0) ? (se - (dq & 0x3FFF)) : (se + dq); /* reconst. signal */ + + dqsez = sr - se + sez; /* pole prediction diff. */ + + update(2, y, _witab[i], _fitab[i], dq, sr, dqsez, state_ptr); + + /* sr was of 14-bit dynamic range */ + return (sr << 2); +} +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: ae265466-c3fc-4f83-bb32-edae488a5ca5 +*/ + diff --git a/src/G72x/g723_24.c b/src/G72x/g723_24.c new file mode 100644 index 00000000..8748459a --- /dev/null +++ b/src/G72x/g723_24.c @@ -0,0 +1,146 @@ +/* + * This source code is a product of Sun Microsystems, Inc. and is provided + * for unrestricted use. Users may copy or modify this source code without + * charge. + * + * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING + * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun source code is provided with no support and without any obligation on + * the part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * g723_24.c + * + * Description: + * + * g723_24_encoder(), g723_24_decoder() + * + * These routines comprise an implementation of the CCITT G.723 24 Kbps + * ADPCM coding algorithm. Essentially, this implementation is identical to + * the bit level description except for a few deviations which take advantage + * of workstation attributes, such as hardware 2's complement arithmetic. + * + */ + +#include "g72x.h" +#include "g72x_priv.h" + +/* + * Maps G.723_24 code word to reconstructed scale factor normalized log + * magnitude values. + */ +static short _dqlntab[8] = {-2048, 135, 273, 373, 373, 273, 135, -2048}; + +/* Maps G.723_24 code word to log of scale factor multiplier. */ +static short _witab[8] = {-128, 960, 4384, 18624, 18624, 4384, 960, -128}; + +/* + * Maps G.723_24 code words to a set of values whose long and short + * term averages are computed and then compared to give an indication + * how stationary (steady state) the signal is. + */ +static short _fitab[8] = {0, 0x200, 0x400, 0xE00, 0xE00, 0x400, 0x200, 0}; + +static short qtab_723_24[3] = {8, 218, 331}; + +/* + * g723_24_encoder() + * + * Encodes a linear PCM, A-law or u-law input sample and returns its 3-bit code. + * Returns -1 if invalid input coding value. + */ +int +g723_24_encoder( + int sl, + G72x_STATE *state_ptr) +{ + short sei, sezi, se, sez; /* ACCUM */ + short d; /* SUBTA */ + short y; /* MIX */ + short sr; /* ADDB */ + short dqsez; /* ADDC */ + short dq, i; + + /* linearize input sample to 14-bit PCM */ + sl >>= 2; /* sl of 14-bit dynamic range */ + + sezi = predictor_zero(state_ptr); + sez = sezi >> 1; + sei = sezi + predictor_pole(state_ptr); + se = sei >> 1; /* se = estimated signal */ + + d = sl - se; /* d = estimation diff. */ + + /* quantize prediction difference d */ + y = step_size(state_ptr); /* quantizer step size */ + i = quantize(d, y, qtab_723_24, 3); /* i = ADPCM code */ + dq = reconstruct(i & 4, _dqlntab[i], y); /* quantized diff. */ + + sr = (dq < 0) ? se - (dq & 0x3FFF) : se + dq; /* reconstructed signal */ + + dqsez = sr + sez - se; /* pole prediction diff. */ + + update(3, y, _witab[i], _fitab[i], dq, sr, dqsez, state_ptr); + + return (i); +} + +/* + * g723_24_decoder() + * + * Decodes a 3-bit CCITT G.723_24 ADPCM code and returns + * the resulting 16-bit linear PCM, A-law or u-law sample value. + * -1 is returned if the output coding is unknown. + */ +int +g723_24_decoder( + int i, + G72x_STATE *state_ptr) +{ + short sezi, sei, sez, se; /* ACCUM */ + short y; /* MIX */ + short sr; /* ADDB */ + short dq; + short dqsez; + + i &= 0x07; /* mask to get proper bits */ + sezi = predictor_zero(state_ptr); + sez = sezi >> 1; + sei = sezi + predictor_pole(state_ptr); + se = sei >> 1; /* se = estimated signal */ + + y = step_size(state_ptr); /* adaptive quantizer step size */ + dq = reconstruct(i & 0x04, _dqlntab[i], y); /* unquantize pred diff */ + + sr = (dq < 0) ? (se - (dq & 0x3FFF)) : (se + dq); /* reconst. signal */ + + dqsez = sr - se + sez; /* pole prediction diff. */ + + update(3, y, _witab[i], _fitab[i], dq, sr, dqsez, state_ptr); + + return (sr << 2); /* sr was of 14-bit dynamic range */ +} +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 75389236-650b-4427-98f3-0df6e8fb24bc +*/ + diff --git a/src/G72x/g723_40.c b/src/G72x/g723_40.c new file mode 100644 index 00000000..6ddb577d --- /dev/null +++ b/src/G72x/g723_40.c @@ -0,0 +1,160 @@ +/* + * This source code is a product of Sun Microsystems, Inc. and is provided + * for unrestricted use. Users may copy or modify this source code without + * charge. + * + * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING + * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun source code is provided with no support and without any obligation on + * the part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * g723_40.c + * + * Description: + * + * g723_40_encoder(), g723_40_decoder() + * + * These routines comprise an implementation of the CCITT G.723 40Kbps + * ADPCM coding algorithm. Essentially, this implementation is identical to + * the bit level description except for a few deviations which + * take advantage of workstation attributes, such as hardware 2's + * complement arithmetic. + * + * The deviation from the bit level specification (lookup tables), + * preserves the bit level performance specifications. + * + * As outlined in the G.723 Recommendation, the algorithm is broken + * down into modules. Each section of code below is preceded by + * the name of the module which it is implementing. + * + */ + +#include "g72x.h" +#include "g72x_priv.h" + +/* + * Maps G.723_40 code word to ructeconstructed scale factor normalized log + * magnitude values. + */ +static short _dqlntab[32] = {-2048, -66, 28, 104, 169, 224, 274, 318, + 358, 395, 429, 459, 488, 514, 539, 566, + 566, 539, 514, 488, 459, 429, 395, 358, + 318, 274, 224, 169, 104, 28, -66, -2048}; + +/* Maps G.723_40 code word to log of scale factor multiplier. */ +static short _witab[32] = {448, 448, 768, 1248, 1280, 1312, 1856, 3200, + 4512, 5728, 7008, 8960, 11456, 14080, 16928, 22272, + 22272, 16928, 14080, 11456, 8960, 7008, 5728, 4512, + 3200, 1856, 1312, 1280, 1248, 768, 448, 448}; + +/* + * Maps G.723_40 code words to a set of values whose long and short + * term averages are computed and then compared to give an indication + * how stationary (steady state) the signal is. + */ +static short _fitab[32] = {0, 0, 0, 0, 0, 0x200, 0x200, 0x200, + 0x200, 0x200, 0x400, 0x600, 0x800, 0xA00, 0xC00, 0xC00, + 0xC00, 0xC00, 0xA00, 0x800, 0x600, 0x400, 0x200, 0x200, + 0x200, 0x200, 0x200, 0, 0, 0, 0, 0}; + +static short qtab_723_40[15] = {-122, -16, 68, 139, 198, 250, 298, 339, + 378, 413, 445, 475, 502, 528, 553}; + +/* + * g723_40_encoder() + * + * Encodes a 16-bit linear PCM, A-law or u-law input sample and retuens + * the resulting 5-bit CCITT G.723 40Kbps code. + * Returns -1 if the input coding value is invalid. + */ +int g723_40_encoder (int sl, G72x_STATE *state_ptr) +{ + short sei, sezi, se, sez; /* ACCUM */ + short d; /* SUBTA */ + short y; /* MIX */ + short sr; /* ADDB */ + short dqsez; /* ADDC */ + short dq, i; + + /* linearize input sample to 14-bit PCM */ + sl >>= 2; /* sl of 14-bit dynamic range */ + + sezi = predictor_zero(state_ptr); + sez = sezi >> 1; + sei = sezi + predictor_pole(state_ptr); + se = sei >> 1; /* se = estimated signal */ + + d = sl - se; /* d = estimation difference */ + + /* quantize prediction difference */ + y = step_size(state_ptr); /* adaptive quantizer step size */ + i = quantize(d, y, qtab_723_40, 15); /* i = ADPCM code */ + + dq = reconstruct(i & 0x10, _dqlntab[i], y); /* quantized diff */ + + sr = (dq < 0) ? se - (dq & 0x7FFF) : se + dq; /* reconstructed signal */ + + dqsez = sr + sez - se; /* dqsez = pole prediction diff. */ + + update(5, y, _witab[i], _fitab[i], dq, sr, dqsez, state_ptr); + + return (i); +} + +/* + * g723_40_decoder() + * + * Decodes a 5-bit CCITT G.723 40Kbps code and returns + * the resulting 16-bit linear PCM, A-law or u-law sample value. + * -1 is returned if the output coding is unknown. + */ +int g723_40_decoder (int i, G72x_STATE *state_ptr) +{ + short sezi, sei, sez, se; /* ACCUM */ + short y ; /* MIX */ + short sr; /* ADDB */ + short dq; + short dqsez; + + i &= 0x1f; /* mask to get proper bits */ + sezi = predictor_zero(state_ptr); + sez = sezi >> 1; + sei = sezi + predictor_pole(state_ptr); + se = sei >> 1; /* se = estimated signal */ + + y = step_size(state_ptr); /* adaptive quantizer step size */ + dq = reconstruct(i & 0x10, _dqlntab[i], y); /* estimation diff. */ + + sr = (dq < 0) ? (se - (dq & 0x7FFF)) : (se + dq); /* reconst. signal */ + + dqsez = sr - se + sez; /* pole prediction diff. */ + + update(5, y, _witab[i], _fitab[i], dq, sr, dqsez, state_ptr); + + return (sr << 2); /* sr was of 14-bit dynamic range */ +} +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: eb8d9a00-32bf-4dd2-b287-01b0336d72bf +*/ + diff --git a/src/G72x/g72x.c b/src/G72x/g72x.c new file mode 100644 index 00000000..fe366f4b --- /dev/null +++ b/src/G72x/g72x.c @@ -0,0 +1,652 @@ +/* + * This source code is a product of Sun Microsystems, Inc. and is provided + * for unrestricted use. Users may copy or modify this source code without + * charge. + * + * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING + * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun source code is provided with no support and without any obligation on + * the part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * g72x.c + * + * Common routines for G.721 and G.723 conversions. + */ + +#include +#include +#include + +#include "g72x.h" +#include "g72x_priv.h" + +static G72x_STATE * g72x_state_new (void) ; +static int unpack_bytes (int bits, int blocksize, const unsigned char * block, short * samples) ; +static int pack_bytes (int bits, const short * samples, unsigned char * block) ; + +static +short power2 [15] = +{ 1, 2, 4, 8, 0x10, 0x20, 0x40, 0x80, + 0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000 +} ; + +/* + * quan() + * + * quantizes the input val against the table of size short integers. + * It returns i if table[i - 1] <= val < table[i]. + * + * Using linear search for simple coding. + */ +static +int quan (int val, short *table, int size) +{ + int i; + + for (i = 0; i < size; i++) + if (val < *table++) + break; + return (i); +} + +/* + * fmult() + * + * returns the integer product of the 14-bit integer "an" and + * "floating point" representation (4-bit exponent, 6-bit mantessa) "srn". + */ +static +int fmult (int an, int srn) +{ + short anmag, anexp, anmant; + short wanexp, wanmant; + short retval; + + anmag = (an > 0) ? an : ((-an) & 0x1FFF); + anexp = quan(anmag, power2, 15) - 6; + anmant = (anmag == 0) ? 32 : + (anexp >= 0) ? anmag >> anexp : anmag << -anexp; + wanexp = anexp + ((srn >> 6) & 0xF) - 13; + + /* + ** The original was : + ** wanmant = (anmant * (srn & 0x3F) + 0x30) >> 4 ; + ** but could see no valid reason for the + 0x30. + ** Removed it and it improved the SNR of the codec. + */ + + wanmant = (anmant * (srn & 0x3F)) >> 4 ; + + retval = (wanexp >= 0) ? ((wanmant << wanexp) & 0x7FFF) : + (wanmant >> -wanexp); + + return (((an ^ srn) < 0) ? -retval : retval); +} + +static G72x_STATE * g72x_state_new (void) +{ return calloc (1, sizeof (G72x_STATE)) ; +} + +/* + * private_init_state() + * + * This routine initializes and/or resets the G72x_PRIVATE structure + * pointed to by 'state_ptr'. + * All the initial state values are specified in the CCITT G.721 document. + */ +void private_init_state (G72x_STATE *state_ptr) +{ + int cnta; + + state_ptr->yl = 34816; + state_ptr->yu = 544; + state_ptr->dms = 0; + state_ptr->dml = 0; + state_ptr->ap = 0; + for (cnta = 0; cnta < 2; cnta++) { + state_ptr->a[cnta] = 0; + state_ptr->pk[cnta] = 0; + state_ptr->sr[cnta] = 32; + } + for (cnta = 0; cnta < 6; cnta++) { + state_ptr->b[cnta] = 0; + state_ptr->dq[cnta] = 32; + } + state_ptr->td = 0; +} /* private_init_state */ + +struct g72x_state * g72x_reader_init (int codec, int *blocksize, int *samplesperblock) +{ G72x_STATE *pstate ; + + if ((pstate = g72x_state_new ()) == NULL) + return NULL ; + + private_init_state (pstate) ; + + pstate->encoder = NULL ; + + switch (codec) + { case G723_16_BITS_PER_SAMPLE : /* 2 bits per sample. */ + pstate->decoder = g723_16_decoder ; + *blocksize = G723_16_BYTES_PER_BLOCK ; + *samplesperblock = G723_16_SAMPLES_PER_BLOCK ; + pstate->codec_bits = 2 ; + pstate->blocksize = G723_16_BYTES_PER_BLOCK ; + pstate->samplesperblock = G723_16_SAMPLES_PER_BLOCK ; + break ; + + case G723_24_BITS_PER_SAMPLE : /* 3 bits per sample. */ + pstate->decoder = g723_24_decoder ; + *blocksize = G723_24_BYTES_PER_BLOCK ; + *samplesperblock = G723_24_SAMPLES_PER_BLOCK ; + pstate->codec_bits = 3 ; + pstate->blocksize = G723_24_BYTES_PER_BLOCK ; + pstate->samplesperblock = G723_24_SAMPLES_PER_BLOCK ; + break ; + + case G721_32_BITS_PER_SAMPLE : /* 4 bits per sample. */ + pstate->decoder = g721_decoder ; + *blocksize = G721_32_BYTES_PER_BLOCK ; + *samplesperblock = G721_32_SAMPLES_PER_BLOCK ; + pstate->codec_bits = 4 ; + pstate->blocksize = G721_32_BYTES_PER_BLOCK ; + pstate->samplesperblock = G721_32_SAMPLES_PER_BLOCK ; + break ; + + case G721_40_BITS_PER_SAMPLE : /* 5 bits per sample. */ + pstate->decoder = g723_40_decoder ; + *blocksize = G721_40_BYTES_PER_BLOCK ; + *samplesperblock = G721_40_SAMPLES_PER_BLOCK ; + pstate->codec_bits = 5 ; + pstate->blocksize = G721_40_BYTES_PER_BLOCK ; + pstate->samplesperblock = G721_40_SAMPLES_PER_BLOCK ; + break ; + + default : + free (pstate) ; + return NULL ; + } ; + + return pstate ; +} /* g72x_reader_init */ + +struct g72x_state * g72x_writer_init (int codec, int *blocksize, int *samplesperblock) +{ G72x_STATE *pstate ; + + if ((pstate = g72x_state_new ()) == NULL) + return NULL ; + + private_init_state (pstate) ; + pstate->decoder = NULL ; + + switch (codec) + { case G723_16_BITS_PER_SAMPLE : /* 2 bits per sample. */ + pstate->encoder = g723_16_encoder ; + *blocksize = G723_16_BYTES_PER_BLOCK ; + *samplesperblock = G723_16_SAMPLES_PER_BLOCK ; + pstate->codec_bits = 2 ; + pstate->blocksize = G723_16_BYTES_PER_BLOCK ; + pstate->samplesperblock = G723_16_SAMPLES_PER_BLOCK ; + break ; + + case G723_24_BITS_PER_SAMPLE : /* 3 bits per sample. */ + pstate->encoder = g723_24_encoder ; + *blocksize = G723_24_BYTES_PER_BLOCK ; + *samplesperblock = G723_24_SAMPLES_PER_BLOCK ; + pstate->codec_bits = 3 ; + pstate->blocksize = G723_24_BYTES_PER_BLOCK ; + pstate->samplesperblock = G723_24_SAMPLES_PER_BLOCK ; + break ; + + case G721_32_BITS_PER_SAMPLE : /* 4 bits per sample. */ + pstate->encoder = g721_encoder ; + *blocksize = G721_32_BYTES_PER_BLOCK ; + *samplesperblock = G721_32_SAMPLES_PER_BLOCK ; + pstate->codec_bits = 4 ; + pstate->blocksize = G721_32_BYTES_PER_BLOCK ; + pstate->samplesperblock = G721_32_SAMPLES_PER_BLOCK ; + break ; + + case G721_40_BITS_PER_SAMPLE : /* 5 bits per sample. */ + pstate->encoder = g723_40_encoder ; + *blocksize = G721_40_BYTES_PER_BLOCK ; + *samplesperblock = G721_40_SAMPLES_PER_BLOCK ; + pstate->codec_bits = 5 ; + pstate->blocksize = G721_40_BYTES_PER_BLOCK ; + pstate->samplesperblock = G721_40_SAMPLES_PER_BLOCK ; + break ; + + default : + free (pstate) ; + return NULL ; + } ; + + return pstate ; +} /* g72x_writer_init */ + +int g72x_decode_block (G72x_STATE *pstate, const unsigned char *block, short *samples) +{ int k, count ; + + count = unpack_bytes (pstate->codec_bits, pstate->blocksize, block, samples) ; + + for (k = 0 ; k < count ; k++) + samples [k] = pstate->decoder (samples [k], pstate) ; + + return 0 ; +} /* g72x_decode_block */ + +int g72x_encode_block (G72x_STATE *pstate, short *samples, unsigned char *block) +{ int k, count ; + + for (k = 0 ; k < pstate->samplesperblock ; k++) + samples [k] = pstate->encoder (samples [k], pstate) ; + + count = pack_bytes (pstate->codec_bits, samples, block) ; + + return count ; +} /* g72x_encode_block */ + +/* + * predictor_zero() + * + * computes the estimated signal from 6-zero predictor. + * + */ +int predictor_zero (G72x_STATE *state_ptr) +{ + int i; + int sezi; + + sezi = fmult(state_ptr->b[0] >> 2, state_ptr->dq[0]); + for (i = 1; i < 6; i++) /* ACCUM */ + sezi += fmult(state_ptr->b[i] >> 2, state_ptr->dq[i]); + return (sezi); +} +/* + * predictor_pole() + * + * computes the estimated signal from 2-pole predictor. + * + */ +int predictor_pole(G72x_STATE *state_ptr) +{ + return (fmult(state_ptr->a[1] >> 2, state_ptr->sr[1]) + + fmult(state_ptr->a[0] >> 2, state_ptr->sr[0])); +} +/* + * step_size() + * + * computes the quantization step size of the adaptive quantizer. + * + */ +int step_size (G72x_STATE *state_ptr) +{ + int y; + int dif; + int al; + + if (state_ptr->ap >= 256) + return (state_ptr->yu); + else { + y = state_ptr->yl >> 6; + dif = state_ptr->yu - y; + al = state_ptr->ap >> 2; + if (dif > 0) + y += (dif * al) >> 6; + else if (dif < 0) + y += (dif * al + 0x3F) >> 6; + return (y); + } +} + +/* + * quantize() + * + * Given a raw sample, 'd', of the difference signal and a + * quantization step size scale factor, 'y', this routine returns the + * ADPCM codeword to which that sample gets quantized. The step + * size scale factor division operation is done in the log base 2 domain + * as a subtraction. + */ +int quantize( + int d, /* Raw difference signal sample */ + int y, /* Step size multiplier */ + short *table, /* quantization table */ + int size) /* table size of short integers */ +{ + short dqm; /* Magnitude of 'd' */ + short expon; /* Integer part of base 2 log of 'd' */ + short mant; /* Fractional part of base 2 log */ + short dl; /* Log of magnitude of 'd' */ + short dln; /* Step size scale factor normalized log */ + int i; + + /* + * LOG + * + * Compute base 2 log of 'd', and store in 'dl'. + */ + dqm = abs(d); + expon = quan(dqm >> 1, power2, 15); + mant = ((dqm << 7) >> expon) & 0x7F; /* Fractional portion. */ + dl = (expon << 7) + mant; + + /* + * SUBTB + * + * "Divide" by step size multiplier. + */ + dln = dl - (y >> 2); + + /* + * QUAN + * + * Obtain codword i for 'd'. + */ + i = quan(dln, table, size); + if (d < 0) /* take 1's complement of i */ + return ((size << 1) + 1 - i); + else if (i == 0) /* take 1's complement of 0 */ + return ((size << 1) + 1); /* new in 1988 */ + else + return (i); +} +/* + * reconstruct() + * + * Returns reconstructed difference signal 'dq' obtained from + * codeword 'i' and quantization step size scale factor 'y'. + * Multiplication is performed in log base 2 domain as addition. + */ +int +reconstruct( + int sign, /* 0 for non-negative value */ + int dqln, /* G.72x codeword */ + int y) /* Step size multiplier */ +{ + short dql; /* Log of 'dq' magnitude */ + short dex; /* Integer part of log */ + short dqt; + short dq; /* Reconstructed difference signal sample */ + + dql = dqln + (y >> 2); /* ADDA */ + + if (dql < 0) { + return ((sign) ? -0x8000 : 0); + } else { /* ANTILOG */ + dex = (dql >> 7) & 15; + dqt = 128 + (dql & 127); + dq = (dqt << 7) >> (14 - dex); + return ((sign) ? (dq - 0x8000) : dq); + } +} + + +/* + * update() + * + * updates the state variables for each output code + */ +void +update( + int code_size, /* distinguish 723_40 with others */ + int y, /* quantizer step size */ + int wi, /* scale factor multiplier */ + int fi, /* for long/short term energies */ + int dq, /* quantized prediction difference */ + int sr, /* reconstructed signal */ + int dqsez, /* difference from 2-pole predictor */ + G72x_STATE *state_ptr) /* coder state pointer */ +{ + int cnt; + short mag, expon; /* Adaptive predictor, FLOAT A */ + short a2p = 0; /* LIMC */ + short a1ul; /* UPA1 */ + short pks1; /* UPA2 */ + short fa1; + char tr; /* tone/transition detector */ + short ylint, thr2, dqthr; + short ylfrac, thr1; + short pk0; + + pk0 = (dqsez < 0) ? 1 : 0; /* needed in updating predictor poles */ + + mag = dq & 0x7FFF; /* prediction difference magnitude */ + /* TRANS */ + ylint = state_ptr->yl >> 15; /* exponent part of yl */ + ylfrac = (state_ptr->yl >> 10) & 0x1F; /* fractional part of yl */ + thr1 = (32 + ylfrac) << ylint; /* threshold */ + thr2 = (ylint > 9) ? 31 << 10 : thr1; /* limit thr2 to 31 << 10 */ + dqthr = (thr2 + (thr2 >> 1)) >> 1; /* dqthr = 0.75 * thr2 */ + if (state_ptr->td == 0) /* signal supposed voice */ + tr = 0; + else if (mag <= dqthr) /* supposed data, but small mag */ + tr = 0; /* treated as voice */ + else /* signal is data (modem) */ + tr = 1; + + /* + * Quantizer scale factor adaptation. + */ + + /* FUNCTW & FILTD & DELAY */ + /* update non-steady state step size multiplier */ + state_ptr->yu = y + ((wi - y) >> 5); + + /* LIMB */ + if (state_ptr->yu < 544) /* 544 <= yu <= 5120 */ + state_ptr->yu = 544; + else if (state_ptr->yu > 5120) + state_ptr->yu = 5120; + + /* FILTE & DELAY */ + /* update steady state step size multiplier */ + state_ptr->yl += state_ptr->yu + ((-state_ptr->yl) >> 6); + + /* + * Adaptive predictor coefficients. + */ + if (tr == 1) { /* reset a's and b's for modem signal */ + state_ptr->a[0] = 0; + state_ptr->a[1] = 0; + state_ptr->b[0] = 0; + state_ptr->b[1] = 0; + state_ptr->b[2] = 0; + state_ptr->b[3] = 0; + state_ptr->b[4] = 0; + state_ptr->b[5] = 0; + } else { /* update a's and b's */ + pks1 = pk0 ^ state_ptr->pk[0]; /* UPA2 */ + + /* update predictor pole a[1] */ + a2p = state_ptr->a[1] - (state_ptr->a[1] >> 7); + if (dqsez != 0) { + fa1 = (pks1) ? state_ptr->a[0] : -state_ptr->a[0]; + if (fa1 < -8191) /* a2p = function of fa1 */ + a2p -= 0x100; + else if (fa1 > 8191) + a2p += 0xFF; + else + a2p += fa1 >> 5; + + if (pk0 ^ state_ptr->pk[1]) + { /* LIMC */ + if (a2p <= -12160) + a2p = -12288; + else if (a2p >= 12416) + a2p = 12288; + else + a2p -= 0x80; + } + else if (a2p <= -12416) + a2p = -12288; + else if (a2p >= 12160) + a2p = 12288; + else + a2p += 0x80; + } + + /* TRIGB & DELAY */ + state_ptr->a[1] = a2p; + + /* UPA1 */ + /* update predictor pole a[0] */ + state_ptr->a[0] -= state_ptr->a[0] >> 8; + if (dqsez != 0) + { if (pks1 == 0) + state_ptr->a[0] += 192; + else + state_ptr->a[0] -= 192; + } ; + + /* LIMD */ + a1ul = 15360 - a2p; + if (state_ptr->a[0] < -a1ul) + state_ptr->a[0] = -a1ul; + else if (state_ptr->a[0] > a1ul) + state_ptr->a[0] = a1ul; + + /* UPB : update predictor zeros b[6] */ + for (cnt = 0; cnt < 6; cnt++) { + if (code_size == 5) /* for 40Kbps G.723 */ + state_ptr->b[cnt] -= state_ptr->b[cnt] >> 9; + else /* for G.721 and 24Kbps G.723 */ + state_ptr->b[cnt] -= state_ptr->b[cnt] >> 8; + if (dq & 0x7FFF) { /* XOR */ + if ((dq ^ state_ptr->dq[cnt]) >= 0) + state_ptr->b[cnt] += 128; + else + state_ptr->b[cnt] -= 128; + } + } + } + + for (cnt = 5; cnt > 0; cnt--) + state_ptr->dq[cnt] = state_ptr->dq[cnt-1]; + /* FLOAT A : convert dq[0] to 4-bit exp, 6-bit mantissa f.p. */ + if (mag == 0) { + state_ptr->dq[0] = (dq >= 0) ? 0x20 : 0xFC20; + } else { + expon = quan(mag, power2, 15); + state_ptr->dq[0] = (dq >= 0) ? + (expon << 6) + ((mag << 6) >> expon) : + (expon << 6) + ((mag << 6) >> expon) - 0x400; + } + + state_ptr->sr[1] = state_ptr->sr[0]; + /* FLOAT B : convert sr to 4-bit exp., 6-bit mantissa f.p. */ + if (sr == 0) { + state_ptr->sr[0] = 0x20; + } else if (sr > 0) { + expon = quan(sr, power2, 15); + state_ptr->sr[0] = (expon << 6) + ((sr << 6) >> expon); + } else if (sr > -32768) { + mag = -sr; + expon = quan(mag, power2, 15); + state_ptr->sr[0] = (expon << 6) + ((mag << 6) >> expon) - 0x400; + } else + state_ptr->sr[0] = (short) 0xFC20; + + /* DELAY A */ + state_ptr->pk[1] = state_ptr->pk[0]; + state_ptr->pk[0] = pk0; + + /* TONE */ + if (tr == 1) /* this sample has been treated as data */ + state_ptr->td = 0; /* next one will be treated as voice */ + else if (a2p < -11776) /* small sample-to-sample correlation */ + state_ptr->td = 1; /* signal may be data */ + else /* signal is voice */ + state_ptr->td = 0; + + /* + * Adaptation speed control. + */ + state_ptr->dms += (fi - state_ptr->dms) >> 5; /* FILTA */ + state_ptr->dml += (((fi << 2) - state_ptr->dml) >> 7); /* FILTB */ + + if (tr == 1) + state_ptr->ap = 256; + else if (y < 1536) /* SUBTC */ + state_ptr->ap += (0x200 - state_ptr->ap) >> 4; + else if (state_ptr->td == 1) + state_ptr->ap += (0x200 - state_ptr->ap) >> 4; + else if (abs((state_ptr->dms << 2) - state_ptr->dml) >= + (state_ptr->dml >> 3)) + state_ptr->ap += (0x200 - state_ptr->ap) >> 4; + else + state_ptr->ap += (-state_ptr->ap) >> 4; + + return ; +} /* update */ + +/*------------------------------------------------------------------------------ +*/ + +static int +unpack_bytes (int bits, int blocksize, const unsigned char * block, short * samples) +{ unsigned int in_buffer = 0 ; + unsigned char in_byte ; + int k, in_bits = 0, bindex = 0 ; + + for (k = 0 ; bindex <= blocksize && k < G72x_BLOCK_SIZE ; k++) + { if (in_bits < bits) + { in_byte = block [bindex++] ; + + in_buffer |= (in_byte << in_bits); + in_bits += 8; + } + samples [k] = in_buffer & ((1 << bits) - 1); + in_buffer >>= bits; + in_bits -= bits; + } ; + + return k ; +} /* unpack_bytes */ + +static int +pack_bytes (int bits, const short * samples, unsigned char * block) +{ + unsigned int out_buffer = 0 ; + int k, bindex = 0, out_bits = 0 ; + unsigned char out_byte ; + + for (k = 0 ; k < G72x_BLOCK_SIZE ; k++) + { out_buffer |= (samples [k] << out_bits) ; + out_bits += bits ; + if (out_bits >= 8) + { out_byte = out_buffer & 0xFF ; + out_bits -= 8 ; + out_buffer >>= 8 ; + block [bindex++] = out_byte ; + } + } ; + + return bindex ; +} /* pack_bytes */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 6298dc75-fd0f-4062-9b90-f73ed69f22d4 +*/ + diff --git a/src/G72x/g72x.h b/src/G72x/g72x.h new file mode 100644 index 00000000..e6319e62 --- /dev/null +++ b/src/G72x/g72x.h @@ -0,0 +1,99 @@ +/* +** Copyright (C) 1999-2005 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* +** This file is not the same as the original file from Sun Microsystems. Nearly +** all the original definitions and function prototypes that were in the file +** of this name have been moved to g72x_priv.h. +*/ + +#ifndef G72X_HEADER_FILE +#define G72X_HEADER_FILE + +/* +** Number of samples per block to process. +** Must be a common multiple of possible bits per sample : 2, 3, 4, 5 and 8. +*/ +#define G72x_BLOCK_SIZE (3 * 5 * 8) + +/* +** Identifiers for the differing kinds of G72x ADPCM codecs. +** The identifiers also define the number of encoded bits per sample. +*/ + +enum +{ G723_16_BITS_PER_SAMPLE = 2, + G723_24_BITS_PER_SAMPLE = 3, + G723_40_BITS_PER_SAMPLE = 5, + + G721_32_BITS_PER_SAMPLE = 4, + G721_40_BITS_PER_SAMPLE = 5, + + G723_16_SAMPLES_PER_BLOCK = G72x_BLOCK_SIZE, + G723_24_SAMPLES_PER_BLOCK = G723_24_BITS_PER_SAMPLE * (G72x_BLOCK_SIZE / G723_24_BITS_PER_SAMPLE), + G723_40_SAMPLES_PER_BLOCK = G723_40_BITS_PER_SAMPLE * (G72x_BLOCK_SIZE / G723_40_BITS_PER_SAMPLE), + + G721_32_SAMPLES_PER_BLOCK = G72x_BLOCK_SIZE, + G721_40_SAMPLES_PER_BLOCK = G721_40_BITS_PER_SAMPLE * (G72x_BLOCK_SIZE / G721_40_BITS_PER_SAMPLE), + + G723_16_BYTES_PER_BLOCK = (G723_16_BITS_PER_SAMPLE * G72x_BLOCK_SIZE) / 8, + G723_24_BYTES_PER_BLOCK = (G723_24_BITS_PER_SAMPLE * G72x_BLOCK_SIZE) / 8, + G723_40_BYTES_PER_BLOCK = (G723_40_BITS_PER_SAMPLE * G72x_BLOCK_SIZE) / 8, + + G721_32_BYTES_PER_BLOCK = (G721_32_BITS_PER_SAMPLE * G72x_BLOCK_SIZE) / 8, + G721_40_BYTES_PER_BLOCK = (G721_40_BITS_PER_SAMPLE * G72x_BLOCK_SIZE) / 8 +} ; + +/* Forward declaration of of g72x_state. */ + +struct g72x_state ; + +/* External function definitions. */ + +struct g72x_state * g72x_reader_init (int codec, int *blocksize, int *samplesperblock) ; +struct g72x_state * g72x_writer_init (int codec, int *blocksize, int *samplesperblock) ; +/* +** Initialize the ADPCM state table for the given codec. +** Return 0 on success, 1 on fail. +*/ + +int g72x_decode_block (struct g72x_state *pstate, const unsigned char *block, short *samples) ; +/* +** The caller fills data->block with data->bytes bytes before calling the +** function. The value data->bytes must be an integer multiple of +** data->blocksize and be <= data->max_bytes. +** When it returns, the caller can read out data->samples samples. +*/ + +int g72x_encode_block (struct g72x_state *pstate, short *samples, unsigned char *block) ; +/* +** The caller fills state->samples some integer multiple data->samples_per_block +** (up to G72x_BLOCK_SIZE) samples before calling the function. +** When it returns, the caller can read out bytes encoded bytes. +*/ + +#endif /* !G72X_HEADER_FILE */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 6ca84e5f-f932-4ba1-87ee-37056d921621 +*/ + diff --git a/src/G72x/g72x_priv.h b/src/G72x/g72x_priv.h new file mode 100644 index 00000000..a88e96d0 --- /dev/null +++ b/src/G72x/g72x_priv.h @@ -0,0 +1,118 @@ +/* + * This source code is a product of Sun Microsystems, Inc. and is provided + * for unrestricted use. Users may copy or modify this source code without + * charge. + * + * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING + * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun source code is provided with no support and without any obligation on + * the part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +#ifndef G72X_PRIVATE_H +#define G72X_PRIVATE_H + +#ifdef __cplusplus +#error "This code is not designed to be compiled with a C++ compiler." +#endif + +/* +** The following is the definition of the state structure used by the +** G.721/G.723 encoder and decoder to preserve their internal state +** between successive calls. The meanings of the majority of the state +** structure fields are explained in detail in the CCITT Recommendation +** G.721. The field names are essentially identical to variable names +** in the bit level description of the coding algorithm included in this +** Recommendation. +*/ + +struct g72x_state +{ long yl; /* Locked or steady state step size multiplier. */ + short yu; /* Unlocked or non-steady state step size multiplier. */ + short dms; /* Short term energy estimate. */ + short dml; /* Long term energy estimate. */ + short ap; /* Linear weighting coefficient of 'yl' and 'yu'. */ + + short a[2]; /* Coefficients of pole portion of prediction filter. */ + short b[6]; /* Coefficients of zero portion of prediction filter. */ + short pk[2]; /* + ** Signs of previous two samples of a partially + ** reconstructed signal. + **/ + short dq[6]; /* + ** Previous 6 samples of the quantized difference + ** signal represented in an internal floating point + ** format. + **/ + short sr[2]; /* + ** Previous 2 samples of the quantized difference + ** signal represented in an internal floating point + ** format. + */ + char td; /* delayed tone detect, new in 1988 version */ + + /* The following struct members were added for libsndfile. The original + ** code worked by calling a set of functions on a sample by sample basis + ** which is slow on architectures like Intel x86. For libsndfile, this + ** was changed so that the encoding and decoding routines could work on + ** a block of samples at a time to reduce the function call overhead. + */ + int (*encoder) (int, struct g72x_state* state) ; + int (*decoder) (int, struct g72x_state* state) ; + + int codec_bits, blocksize, samplesperblock ; +} ; + +typedef struct g72x_state G72x_STATE ; + +int predictor_zero (G72x_STATE *state_ptr); + +int predictor_pole (G72x_STATE *state_ptr); + +int step_size (G72x_STATE *state_ptr); + +int quantize (int d, int y, short *table, int size); + +int reconstruct (int sign, int dqln, int y); + +void update (int code_size, int y, int wi, int fi, int dq, int sr, int dqsez, G72x_STATE *state_ptr); + +int g721_encoder (int sample, G72x_STATE *state_ptr); +int g721_decoder (int code, G72x_STATE *state_ptr); + +int g723_16_encoder (int sample, G72x_STATE *state_ptr); +int g723_16_decoder (int code, G72x_STATE *state_ptr); + +int g723_24_encoder (int sample, G72x_STATE *state_ptr); +int g723_24_decoder (int code, G72x_STATE *state_ptr); + +int g723_40_encoder (int sample, G72x_STATE *state_ptr); +int g723_40_decoder (int code, G72x_STATE *state_ptr); + +void private_init_state (G72x_STATE *state_ptr) ; + +#endif /* G72X_PRIVATE_H */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: d9ad4da7-0fa3-471d-8020-720b5cfb5e5b +*/ + diff --git a/src/G72x/g72x_test.c b/src/G72x/g72x_test.c new file mode 100644 index 00000000..caf58467 --- /dev/null +++ b/src/G72x/g72x_test.c @@ -0,0 +1,222 @@ +/* +** Copyright (C) 1999-2004 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include +#include +#include +#include + +#include "g72x.h" +#include "g72x_priv.h" + +#ifndef M_PI +#define M_PI 3.14159265358979323846264338 +#endif + +#define BUFFER_SIZE (1<<14) /* Should be (1<<14) */ +#define SAMPLE_RATE 11025 + + +static void g721_test (void) ; +static void g723_test (double margin) ; + +static void gen_signal_double (double *data, double scale, int datalen) ; +static int error_function (double data, double orig, double margin) ; + +static int oct_save_short (short *a, short *b, int len) ; + +int +main (int argc, char *argv []) +{ int bDoAll = 0 ; + int nTests = 0 ; + + if (argc != 2) + { printf ("Usage : %s \n", argv [0]) ; + printf (" Where is one of the following:\n") ; + printf (" g721 - test G721 encoder and decoder\n") ; + printf (" g723 - test G721 encoder and decoder\n") ; + printf (" all - perform all tests\n") ; + exit (1) ; + } ; + + bDoAll=!strcmp (argv [1], "all"); + + if (bDoAll || ! strcmp (argv [1], "g721")) + { g721_test () ; + nTests++ ; + } ; + + if (bDoAll || ! strcmp (argv [1], "g723")) + { g723_test (0.53) ; + nTests++ ; + } ; + + if (nTests == 0) + { printf ("Mono : ************************************\n") ; + printf ("Mono : * No '%s' test defined.\n", argv [1]) ; + printf ("Mono : ************************************\n") ; + return 1 ; + } ; + + return 0 ; +} /* main */ + +static void +g721_test (void) +{ + return ; +} /* g721_test */ + +static void +g723_test (double margin) +{ static double orig_buffer [BUFFER_SIZE] ; + static short orig [BUFFER_SIZE] ; + static short data [BUFFER_SIZE] ; + + G72x_STATE encoder_state, decoder_state ; + + long k ; + int code, position, max_err ; + + private_init_state (&encoder_state) ; + encoder_state.encoder = g723_24_encoder ; + encoder_state.codec_bits = 3 ; + + private_init_state (&decoder_state) ; + decoder_state.decoder = g723_24_decoder ; + decoder_state.codec_bits = 3 ; + + memset (data, 0, BUFFER_SIZE * sizeof (short)) ; + memset (orig, 0, BUFFER_SIZE * sizeof (short)) ; + + printf (" g723_test : ") ; + fflush (stdout) ; + + gen_signal_double (orig_buffer, 32000.0, BUFFER_SIZE) ; + for (k = 0 ; k < BUFFER_SIZE ; k++) + orig [k] = (short) orig_buffer [k] ; + + /* Write and read data here. */ + position = 0 ; + max_err = 0 ; + for (k = 0 ; k < BUFFER_SIZE ; k++) + { code = encoder_state.encoder (orig [k], &encoder_state) ; + data [k] = decoder_state.decoder (code, &decoder_state) ; + if (abs (orig [k] - data [k]) > max_err) + { position = k ; + max_err = abs (orig [k] - data [k]) ; + } ; + } ; + + printf ("\n\nMax error of %d at postion %d.\n", max_err, position) ; + + for (k = 0 ; k < BUFFER_SIZE ; k++) + { if (error_function (data [k], orig [k], margin)) + { printf ("Line %d: Incorrect sample A (#%ld : %d should be %d).\n", __LINE__, k, data [k], orig [k]) ; + oct_save_short (orig, data, BUFFER_SIZE) ; + exit (1) ; + } ; + } ; + + + printf ("ok\n") ; + + return ; +} /* g723_test */ + + +#define SIGNAL_MAXVAL 30000.0 +#define DECAY_COUNT 1000 + +static void +gen_signal_double (double *gendata, double scale, int gendatalen) +{ int k, ramplen ; + double amp = 0.0 ; + + ramplen = DECAY_COUNT ; + + for (k = 0 ; k < gendatalen ; k++) + { if (k <= ramplen) + amp = scale * k / ((double) ramplen) ; + else if (k > gendatalen - ramplen) + amp = scale * (gendatalen - k) / ((double) ramplen) ; + + gendata [k] = amp * (0.4 * sin (33.3 * 2.0 * M_PI * ((double) (k+1)) / ((double) SAMPLE_RATE)) + + 0.3 * cos (201.1 * 2.0 * M_PI * ((double) (k+1)) / ((double) SAMPLE_RATE))) ; + } ; + + return ; +} /* gen_signal_double */ + +static int +error_function (double data, double orig, double margin) +{ double error ; + + if (fabs (orig) <= 500.0) + error = fabs (fabs (data) - fabs(orig)) / 2000.0 ; + else if (fabs (orig) <= 1000.0) + error = fabs (data - orig) / 3000.0 ; + else + error = fabs (data - orig) / fabs (orig) ; + + if (error > margin) + { printf ("\n\n*******************\nError : %f\n", error) ; + return 1 ; + } ; + return 0 ; +} /* error_function */ + +static int +oct_save_short (short *a, short *b, int len) +{ FILE *file ; + int k ; + + if (! (file = fopen ("error.dat", "w"))) + return 1 ; + + fprintf (file, "# Not created by Octave\n") ; + + fprintf (file, "# name: a\n") ; + fprintf (file, "# type: matrix\n") ; + fprintf (file, "# rows: %d\n", len) ; + fprintf (file, "# columns: 1\n") ; + + for (k = 0 ; k < len ; k++) + fprintf (file, "% d\n", a [k]) ; + + fprintf (file, "# name: b\n") ; + fprintf (file, "# type: matrix\n") ; + fprintf (file, "# rows: %d\n", len) ; + fprintf (file, "# columns: 1\n") ; + + for (k = 0 ; k < len ; k++) + fprintf (file, "% d\n", b [k]) ; + + fclose (file) ; + return 0 ; +} /* oct_save_short */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 0597b442-a5b0-4abf-92a4-92f6c24e85a6 +*/ + diff --git a/src/GSM610/COPYRIGHT b/src/GSM610/COPYRIGHT new file mode 100644 index 00000000..eba0e523 --- /dev/null +++ b/src/GSM610/COPYRIGHT @@ -0,0 +1,16 @@ +Copyright 1992, 1993, 1994 by Jutta Degener and Carsten Bormann, +Technische Universitaet Berlin + +Any use of this software is permitted provided that this notice is not +removed and that neither the authors nor the Technische Universitaet Berlin +are deemed to have made any representations as to the suitability of this +software for any purpose nor are held responsible for any defects of +this software. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + +As a matter of courtesy, the authors request to be informed about uses +this software has found, about bugs in this software, and about any +improvements that may be of general interest. + +Berlin, 28.11.1994 +Jutta Degener +Carsten Bormann diff --git a/src/GSM610/ChangeLog b/src/GSM610/ChangeLog new file mode 100644 index 00000000..24f52488 --- /dev/null +++ b/src/GSM610/ChangeLog @@ -0,0 +1,56 @@ +2004-05-12 Erik de Castro Lopo + + * gsm610_priv.h + Replace ugly macros with inline functions. + + * *.c + Remove temporary variables used by macros and other minor fixes required by + above change. + +2003-06-02 Erik de Castro Lopo + + * rpe.c + Renamed variables "exp" to "expon" to avoid shadowed parameter warnigns. + +2002-06-08 Erik de Castro Lopo + + * long_term.c + Changes tp removed compiler warnings about shadowed parameters. + +2002-06-08 Erik de Castro Lopo + + * private.h + Made declarations of gsm_A, gsm_B, gsm_MIC etc extern. This fixed a compile + problem on MacOSX. + +2002-05-10 Erik de Castro Lopo + + * *.[ch] + Removed all pre-ANSI prototype kludges. Removed proto.h and unproto.h. + Started work on making GSM 6.10 files seekable. Currently they are not. + + * code.c private.h + Function Gsm_Coder () used a statically defined array. This was obviously + not re-entrant so moved it to struct gsm_state. + +2001-09-16 Erik de Castro Lopo + + * code.c + Added #includes for string.h and stdlib.h. + +2000-10-27 Erik de Castro Lopo + + * config.h + Removed some commented out #defines (ie //*efine) which were causing problems on + the Sun cc compiler. + +2000-02-29 Erik de Castro Lopo + + * private.h + Added #defines to emulate normal compile time options. + +2000-02-28 Erik de Castro Lopo + + * everthing + Created this directory and copied files from libgsm. + http://kbs.cs.tu-berlin.de/~jutta/toast.html diff --git a/src/GSM610/Makefile.am b/src/GSM610/Makefile.am new file mode 100644 index 00000000..e754ba98 --- /dev/null +++ b/src/GSM610/Makefile.am @@ -0,0 +1,21 @@ +## Process this file with automake to produce Makefile.in + +EXTRA_DIST = README COPYRIGHT ChangeLog + +noinst_HEADERS = gsm.h config.h gsm610_priv.h +noinst_LTLIBRARIES = libgsm.la + +CFILES = add.c decode.c gsm_decode.c gsm_encode.c long_term.c preprocess.c \ + short_term.c code.c gsm_create.c gsm_destroy.c gsm_option.c lpc.c rpe.c table.c + +libgsm_la_SOURCES = $(CFILES) $(noinst_HEADERS) + +# Disable autoheader. +AUTOHEADER=echo + +## Do not edit or modify anything in this comment block. +## The arch-tag line is a file identity tag for the GNU Arch +## revision control system. +## +## arch-tag: ba91ffbe-9d1d-4044-a1de-e8ee2f890560 + diff --git a/src/GSM610/README b/src/GSM610/README new file mode 100644 index 00000000..b57132b0 --- /dev/null +++ b/src/GSM610/README @@ -0,0 +1,36 @@ +GSM 06.10 13 kbit/s RPE/LTP speech codec +---------------------------------------- + +All the file in this directory were written by Jutta Degener +and Carsten Borman for The Communications and Operating Systems +Research Group (KBS) at the Technische Universitaet Berlin. + +Their work was released under the following license which is +assumed to be compatible with The GNU Lesser General Public License. + +---------------------------------------------------------------------------- + +Copyright 1992, 1993, 1994 by Jutta Degener and Carsten Bormann, +Technische Universitaet Berlin + +Any use of this software is permitted provided that this notice is not +removed and that neither the authors nor the Technische Universitaet Berlin +are deemed to have made any representations as to the suitability of this +software for any purpose nor are held responsible for any defects of +this software. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + +As a matter of courtesy, the authors request to be informed about uses +this software has found, about bugs in this software, and about any +improvements that may be of general interest. + +Berlin, 28.11.1994 +Jutta Degener (jutta@cs.tu-berlin.de) +Carsten Bormann (cabo@cs.tu-berlin.de) + +---------------------------------------------------------------------------- + +Jutta Degener and Carsten Bormann's work can be found on their homepage +at: + + http://kbs.cs.tu-berlin.de/~jutta/toast.html + diff --git a/src/GSM610/add.c b/src/GSM610/add.c new file mode 100644 index 00000000..fbf7cf14 --- /dev/null +++ b/src/GSM610/add.c @@ -0,0 +1,248 @@ +/* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + +/* + * See private.h for the more commonly used macro versions. + */ + +#include +#include + +#include "gsm610_priv.h" +#include "gsm.h" + +#define saturate(x) \ + ((x) < MIN_WORD ? MIN_WORD : (x) > MAX_WORD ? MAX_WORD: (x)) + +word gsm_add ( word a, word b) +{ + longword sum = (longword)a + (longword)b; + return saturate(sum); +} + +word gsm_sub ( word a, word b) +{ + longword diff = (longword)a - (longword)b; + return saturate(diff); +} + +word gsm_mult ( word a, word b) +{ + if (a == MIN_WORD && b == MIN_WORD) + return MAX_WORD; + + return SASR_L( (longword)a * (longword)b, 15 ); +} + +word gsm_mult_r ( word a, word b) +{ + if (b == MIN_WORD && a == MIN_WORD) return MAX_WORD; + else { + longword prod = (longword)a * (longword)b + 16384; + prod >>= 15; + return prod & 0xFFFF; + } +} + +word gsm_abs (word a) +{ + return a < 0 ? (a == MIN_WORD ? MAX_WORD : -a) : a; +} + +longword gsm_L_mult (word a, word b) +{ + assert( a != MIN_WORD || b != MIN_WORD ); + return ((longword)a * (longword)b) << 1; +} + +longword gsm_L_add ( longword a, longword b) +{ + if (a < 0) { + if (b >= 0) return a + b; + else { + ulongword A = (ulongword)-(a + 1) + (ulongword)-(b + 1); + return A >= MAX_LONGWORD ? MIN_LONGWORD :-(longword)A-2; + } + } + else if (b <= 0) return a + b; + else { + ulongword A = (ulongword)a + (ulongword)b; + return A > MAX_LONGWORD ? MAX_LONGWORD : A; + } +} + +longword gsm_L_sub ( longword a, longword b) +{ + if (a >= 0) { + if (b >= 0) return a - b; + else { + /* a>=0, b<0 */ + + ulongword A = (ulongword)a + -(b + 1); + return A >= MAX_LONGWORD ? MAX_LONGWORD : (A + 1); + } + } + else if (b <= 0) return a - b; + else { + /* a<0, b>0 */ + + ulongword A = (ulongword)-(a + 1) + b; + return A >= MAX_LONGWORD ? MIN_LONGWORD : -(longword)A - 1; + } +} + +static unsigned char const bitoff[ 256 ] = { + 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +word gsm_norm (longword a ) +/* + * the number of left shifts needed to normalize the 32 bit + * variable L_var1 for positive values on the interval + * + * with minimum of + * minimum of 1073741824 (01000000000000000000000000000000) and + * maximum of 2147483647 (01111111111111111111111111111111) + * + * + * and for negative values on the interval with + * minimum of -2147483648 (-10000000000000000000000000000000) and + * maximum of -1073741824 ( -1000000000000000000000000000000). + * + * in order to normalize the result, the following + * operation must be done: L_norm_var1 = L_var1 << norm( L_var1 ); + * + * (That's 'ffs', only from the left, not the right..) + */ +{ + assert(a != 0); + + if (a < 0) { + if (a <= -1073741824) return 0; + a = ~a; + } + + return a & 0xffff0000 + ? ( a & 0xff000000 + ? -1 + bitoff[ 0xFF & (a >> 24) ] + : 7 + bitoff[ 0xFF & (a >> 16) ] ) + : ( a & 0xff00 + ? 15 + bitoff[ 0xFF & (a >> 8) ] + : 23 + bitoff[ 0xFF & a ] ); +} + +longword gsm_L_asl (longword a, int n) +{ + if (n >= 32) return 0; + if (n <= -32) return -(a < 0); + if (n < 0) return gsm_L_asr(a, -n); + return a << n; +} + +word gsm_asr (word a, int n) +{ + if (n >= 16) return -(a < 0); + if (n <= -16) return 0; + if (n < 0) return a << -n; + + return SASR_W (a, (word) n); +} + +word gsm_asl (word a, int n) +{ + if (n >= 16) return 0; + if (n <= -16) return -(a < 0); + if (n < 0) return gsm_asr(a, -n); + return a << n; +} + +longword gsm_L_asr (longword a, int n) +{ + if (n >= 32) return -(a < 0); + if (n <= -32) return 0; + if (n < 0) return a << -n; + + return SASR_L (a, (word) n); +} + +/* +** word gsm_asr (word a, int n) +** { +** if (n >= 16) return -(a < 0); +** if (n <= -16) return 0; +** if (n < 0) return a << -n; +** +** # ifdef SASR_W +** return a >> n; +** # else +** if (a >= 0) return a >> n; +** else return -(word)( -(uword)a >> n ); +** # endif +** } +** +*/ +/* + * (From p. 46, end of section 4.2.5) + * + * NOTE: The following lines gives [sic] one correct implementation + * of the div(num, denum) arithmetic operation. Compute div + * which is the integer division of num by denum: with denum + * >= num > 0 + */ + +word gsm_div (word num, word denum) +{ + longword L_num = num; + longword L_denum = denum; + word div = 0; + int k = 15; + + /* The parameter num sometimes becomes zero. + * Although this is explicitly guarded against in 4.2.5, + * we assume that the result should then be zero as well. + */ + + /* assert(num != 0); */ + + assert(num >= 0 && denum >= num); + if (num == 0) + return 0; + + while (k--) { + div <<= 1; + L_num <<= 1; + + if (L_num >= L_denum) { + L_num -= L_denum; + div++; + } + } + + return div; +} +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: a7398579-e2e1-4733-aa2d-4c6efc0c58ff +*/ + diff --git a/src/GSM610/code.c b/src/GSM610/code.c new file mode 100644 index 00000000..02ec75bf --- /dev/null +++ b/src/GSM610/code.c @@ -0,0 +1,97 @@ +/* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + + +#include +#include + +#include "config.h" + +#include "gsm610_priv.h" +#include "gsm.h" + +/* + * 4.2 FIXED POINT IMPLEMENTATION OF THE RPE-LTP CODER + */ + +void Gsm_Coder ( + + struct gsm_state * State, + + word * s, /* [0..159] samples IN */ + +/* + * The RPE-LTD coder works on a frame by frame basis. The length of + * the frame is equal to 160 samples. Some computations are done + * once per frame to produce at the output of the coder the + * LARc[1..8] parameters which are the coded LAR coefficients and + * also to realize the inverse filtering operation for the entire + * frame (160 samples of signal d[0..159]). These parts produce at + * the output of the coder: + */ + + word * LARc, /* [0..7] LAR coefficients OUT */ + +/* + * Procedure 4.2.11 to 4.2.18 are to be executed four times per + * frame. That means once for each sub-segment RPE-LTP analysis of + * 40 samples. These parts produce at the output of the coder: + */ + + word * Nc, /* [0..3] LTP lag OUT */ + word * bc, /* [0..3] coded LTP gain OUT */ + word * Mc, /* [0..3] RPE grid selection OUT */ + word * xmaxc,/* [0..3] Coded maximum amplitude OUT */ + word * xMc /* [13*4] normalized RPE samples OUT */ +) +{ + int k; + word * dp = State->dp0 + 120; /* [ -120...-1 ] */ + word * dpp = dp; /* [ 0...39 ] */ + + word so[160]; + + Gsm_Preprocess (State, s, so); + Gsm_LPC_Analysis (State, so, LARc); + Gsm_Short_Term_Analysis_Filter (State, LARc, so); + + for (k = 0; k <= 3; k++, xMc += 13) { + + Gsm_Long_Term_Predictor ( State, + so+k*40, /* d [0..39] IN */ + dp, /* dp [-120..-1] IN */ + State->e + 5, /* e [0..39] OUT */ + dpp, /* dpp [0..39] OUT */ + Nc++, + bc++); + + Gsm_RPE_Encoding ( /*-S,-*/ + State->e + 5, /* e ][0..39][ IN/OUT */ + xmaxc++, Mc++, xMc ); + /* + * Gsm_Update_of_reconstructed_short_time_residual_signal + * ( dpp, e + 5, dp ); + */ + + { register int i; + for (i = 0; i <= 39; i++) + dp[ i ] = GSM_ADD( State->e[5 + i], dpp[i] ); + } + dp += 40; + dpp += 40; + + } + (void)memcpy( (char *)State->dp0, (char *)(State->dp0 + 160), + 120 * sizeof(*State->dp0) ); +} +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: ae8ef1b2-5a1e-4263-94cd-42b15dca81a3 +*/ + diff --git a/src/GSM610/config.h b/src/GSM610/config.h new file mode 100644 index 00000000..23ac5ad0 --- /dev/null +++ b/src/GSM610/config.h @@ -0,0 +1,33 @@ +/* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + +#ifndef CONFIG_H +#define CONFIG_H + +#define HAS_STDLIB_H 1 /* /usr/include/stdlib.h */ +#define HAS_FCNTL_H 1 /* /usr/include/fcntl.h */ + +#define HAS_FSTAT 1 /* fstat syscall */ +#define HAS_FCHMOD 1 /* fchmod syscall */ +#define HAS_CHMOD 1 /* chmod syscall */ +#define HAS_FCHOWN 1 /* fchown syscall */ +#define HAS_CHOWN 1 /* chown syscall */ + +#define HAS_STRING_H 1 /* /usr/include/string.h */ + +#define HAS_UNISTD_H 1 /* /usr/include/unistd.h */ +#define HAS_UTIME 1 /* POSIX utime(path, times) */ +#define HAS_UTIME_H 1 /* UTIME header file */ + +#endif /* CONFIG_H */ +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 5338dfef-8e59-4f51-af47-627c9ea85353 +*/ + diff --git a/src/GSM610/decode.c b/src/GSM610/decode.c new file mode 100644 index 00000000..46db3182 --- /dev/null +++ b/src/GSM610/decode.c @@ -0,0 +1,67 @@ +/* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + +#include + +#include "gsm610_priv.h" +#include "gsm.h" + +/* + * 4.3 FIXED POINT IMPLEMENTATION OF THE RPE-LTP DECODER + */ + +static void Postprocessing ( + struct gsm_state * S, + register word * s) +{ + register int k; + register word msr = S->msr; + register word tmp; + + for (k = 160; k--; s++) { + tmp = GSM_MULT_R( msr, 28180 ); + msr = GSM_ADD(*s, tmp); /* Deemphasis */ + *s = GSM_ADD(msr, msr) & 0xFFF8; /* Truncation & Upscaling */ + } + S->msr = msr; +} + +void Gsm_Decoder ( + struct gsm_state * S, + + word * LARcr, /* [0..7] IN */ + + word * Ncr, /* [0..3] IN */ + word * bcr, /* [0..3] IN */ + word * Mcr, /* [0..3] IN */ + word * xmaxcr, /* [0..3] IN */ + word * xMcr, /* [0..13*4] IN */ + + word * s) /* [0..159] OUT */ +{ + int j, k; + word erp[40], wt[160]; + word * drp = S->dp0 + 120; + + for (j=0; j <= 3; j++, xmaxcr++, bcr++, Ncr++, Mcr++, xMcr += 13) { + + Gsm_RPE_Decoding( /*-S,-*/ *xmaxcr, *Mcr, xMcr, erp ); + Gsm_Long_Term_Synthesis_Filtering( S, *Ncr, *bcr, erp, drp ); + + for (k = 0; k <= 39; k++) wt[ j * 40 + k ] = drp[ k ]; + } + + Gsm_Short_Term_Synthesis_Filter( S, LARcr, wt, s ); + Postprocessing(S, s); +} +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 11ae5b90-2e8b-400b-ac64-a69a1fc6cc41 +*/ + diff --git a/src/GSM610/gsm.h b/src/GSM610/gsm.h new file mode 100644 index 00000000..a13a6061 --- /dev/null +++ b/src/GSM610/gsm.h @@ -0,0 +1,58 @@ +/* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + +#ifndef GSM_H +#define GSM_H + +#include /* for FILE * */ + +/* + * Interface + */ + +typedef struct gsm_state * gsm; +typedef short gsm_signal; /* signed 16 bit */ +typedef unsigned char gsm_byte; +typedef gsm_byte gsm_frame[33]; /* 33 * 8 bits */ + +#define GSM_MAGIC 0xD /* 13 kbit/s RPE-LTP */ + +#define GSM_PATCHLEVEL 10 +#define GSM_MINOR 0 +#define GSM_MAJOR 1 + +#define GSM_OPT_VERBOSE 1 +#define GSM_OPT_FAST 2 +#define GSM_OPT_LTP_CUT 3 +#define GSM_OPT_WAV49 4 +#define GSM_OPT_FRAME_INDEX 5 +#define GSM_OPT_FRAME_CHAIN 6 + +gsm gsm_create (void); + +/* Added for libsndfile : May 6, 2002 */ +void gsm_init (gsm); + +void gsm_destroy (gsm); + +int gsm_print (FILE *, gsm, gsm_byte *); +int gsm_option (gsm, int, int *); + +void gsm_encode (gsm, gsm_signal *, gsm_byte *); +int gsm_decode (gsm, gsm_byte *, gsm_signal *); + +int gsm_explode (gsm, gsm_byte *, gsm_signal *); +void gsm_implode (gsm, gsm_signal *, gsm_byte *); + +#endif /* GSM_H */ +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 8cfc7698-5433-4b6f-aeca-967c6fda4dec +*/ + diff --git a/src/GSM610/gsm610_priv.h b/src/GSM610/gsm610_priv.h new file mode 100644 index 00000000..c9ab3f25 --- /dev/null +++ b/src/GSM610/gsm610_priv.h @@ -0,0 +1,308 @@ +/* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + +#ifndef PRIVATE_H +#define PRIVATE_H + +/* Added by Erik de Castro Lopo */ +#define USE_FLOAT_MUL +#define FAST +#define WAV49 + +#ifdef __cplusplus +#error "This code is not designed to be compiled with a C++ compiler." +#endif +/* Added by Erik de Castro Lopo */ + + + +typedef short word; /* 16 bit signed int */ +typedef int longword; /* 32 bit signed int */ + +typedef unsigned short uword; /* unsigned word */ +typedef unsigned int ulongword; /* unsigned longword */ + +struct gsm_state +{ word dp0[ 280 ] ; + + word z1; /* preprocessing.c, Offset_com. */ + longword L_z2; /* Offset_com. */ + int mp; /* Preemphasis */ + + word u[8] ; /* short_term_aly_filter.c */ + word LARpp[2][8] ; /* */ + word j; /* */ + + word ltp_cut; /* long_term.c, LTP crosscorr. */ + word nrp; /* 40 */ /* long_term.c, synthesis */ + word v[9] ; /* short_term.c, synthesis */ + word msr; /* decoder.c, Postprocessing */ + + char verbose; /* only used if !NDEBUG */ + char fast; /* only used if FAST */ + + char wav_fmt; /* only used if WAV49 defined */ + unsigned char frame_index; /* odd/even chaining */ + unsigned char frame_chain; /* half-byte to carry forward */ + + /* Moved here from code.c where it was defined as static */ + word e[50] ; +} ; + +typedef struct gsm_state GSM_STATE ; + +#define MIN_WORD (-32767 - 1) +#define MAX_WORD 32767 + +#define MIN_LONGWORD (-2147483647 - 1) +#define MAX_LONGWORD 2147483647 + +/* Signed arithmetic shift right. */ +static inline word +SASR_W (word x, word by) +{ return (x >> by) ; +} /* SASR */ + +static inline longword +SASR_L (longword x, word by) +{ return (x >> by) ; +} /* SASR */ + +/* + * Prototypes from add.c + */ +word gsm_mult (word a, word b) ; +longword gsm_L_mult (word a, word b) ; +word gsm_mult_r (word a, word b) ; + +word gsm_div (word num, word denum) ; + +word gsm_add (word a, word b ) ; +longword gsm_L_add (longword a, longword b ) ; + +word gsm_sub (word a, word b) ; +longword gsm_L_sub (longword a, longword b) ; + +word gsm_abs (word a) ; + +word gsm_norm (longword a ) ; + +longword gsm_L_asl (longword a, int n) ; +word gsm_asl (word a, int n) ; + +longword gsm_L_asr (longword a, int n) ; +word gsm_asr (word a, int n) ; + +/* + * Inlined functions from add.h + */ + +static inline longword +GSM_MULT_R (word a, word b) +{ return (((longword) (a)) * ((longword) (b)) + 16384) >> 15 ; +} /* GSM_MULT_R */ + +static inline longword +GSM_MULT (word a, word b) +{ return (((longword) (a)) * ((longword) (b))) >> 15 ; +} /* GSM_MULT */ + +static inline longword +GSM_L_MULT (word a, word b) +{ return ((longword) (a)) * ((longword) (b)) << 1 ; +} /* GSM_L_MULT */ + +static inline longword +GSM_L_ADD (longword a, longword b) +{ ulongword utmp ; + + if (a < 0 && b < 0) + { utmp = (ulongword)-((a) + 1) + (ulongword)-((b) + 1) ; + return (utmp >= (ulongword) MAX_LONGWORD) ? MIN_LONGWORD : -(longword)utmp-2 ; + } ; + + if (a > 0 && b > 0) + { utmp = (ulongword) a + (ulongword) b ; + return (utmp >= (ulongword) MAX_LONGWORD) ? MAX_LONGWORD : utmp ; + } ; + + return a + b ; +} /* GSM_L_ADD */ + +static inline longword +GSM_ADD (word a, word b) +{ longword ltmp ; + + ltmp = ((longword) a) + ((longword) b) ; + + if (ltmp >= MAX_WORD) + return MAX_WORD ; + if (ltmp <= MIN_WORD) + return MIN_WORD ; + + return ltmp ; +} /* GSM_ADD */ + +static inline longword +GSM_SUB (word a, word b) +{ longword ltmp ; + + ltmp = ((longword) a) - ((longword) b) ; + + if (ltmp >= MAX_WORD) + ltmp = MAX_WORD ; + else if (ltmp <= MIN_WORD) + ltmp = MIN_WORD ; + + return ltmp ; +} /* GSM_SUB */ + +static inline word +GSM_ABS (word a) +{ + if (a > 0) + return a ; + if (a == MIN_WORD) + return MAX_WORD ; + return -a ; +} /* GSM_ADD */ + + +/* + * More prototypes from implementations.. + */ +void Gsm_Coder ( + struct gsm_state * S, + word * s, /* [0..159] samples IN */ + word * LARc, /* [0..7] LAR coefficients OUT */ + word * Nc, /* [0..3] LTP lag OUT */ + word * bc, /* [0..3] coded LTP gain OUT */ + word * Mc, /* [0..3] RPE grid selection OUT */ + word * xmaxc,/* [0..3] Coded maximum amplitude OUT */ + word * xMc) ;/* [13*4] normalized RPE samples OUT */ + +void Gsm_Long_Term_Predictor ( /* 4x for 160 samples */ + struct gsm_state * S, + word * d, /* [0..39] residual signal IN */ + word * dp, /* [-120..-1] d' IN */ + word * e, /* [0..40] OUT */ + word * dpp, /* [0..40] OUT */ + word * Nc, /* correlation lag OUT */ + word * bc) ; /* gain factor OUT */ + +void Gsm_LPC_Analysis ( + struct gsm_state * S, + word * s, /* 0..159 signals IN/OUT */ + word * LARc) ; /* 0..7 LARc's OUT */ + +void Gsm_Preprocess ( + struct gsm_state * S, + word * s, word * so) ; + +void Gsm_Encoding ( + struct gsm_state * S, + word * e, + word * ep, + word * xmaxc, + word * Mc, + word * xMc) ; + +void Gsm_Short_Term_Analysis_Filter ( + struct gsm_state * S, + word * LARc, /* coded log area ratio [0..7] IN */ + word * d) ; /* st res. signal [0..159] IN/OUT */ + +void Gsm_Decoder ( + struct gsm_state * S, + word * LARcr, /* [0..7] IN */ + word * Ncr, /* [0..3] IN */ + word * bcr, /* [0..3] IN */ + word * Mcr, /* [0..3] IN */ + word * xmaxcr, /* [0..3] IN */ + word * xMcr, /* [0..13*4] IN */ + word * s) ; /* [0..159] OUT */ + +void Gsm_Decoding ( + struct gsm_state * S, + word xmaxcr, + word Mcr, + word * xMcr, /* [0..12] IN */ + word * erp) ; /* [0..39] OUT */ + +void Gsm_Long_Term_Synthesis_Filtering ( + struct gsm_state* S, + word Ncr, + word bcr, + word * erp, /* [0..39] IN */ + word * drp) ; /* [-120..-1] IN, [0..40] OUT */ + +void Gsm_RPE_Decoding ( + /*-struct gsm_state *S,-*/ + word xmaxcr, + word Mcr, + word * xMcr, /* [0..12], 3 bits IN */ + word * erp) ; /* [0..39] OUT */ + +void Gsm_RPE_Encoding ( + /*-struct gsm_state * S,-*/ + word * e, /* -5..-1][0..39][40..44 IN/OUT */ + word * xmaxc, /* OUT */ + word * Mc, /* OUT */ + word * xMc) ; /* [0..12] OUT */ + +void Gsm_Short_Term_Synthesis_Filter ( + struct gsm_state * S, + word * LARcr, /* log area ratios [0..7] IN */ + word * drp, /* received d [0...39] IN */ + word * s) ; /* signal s [0..159] OUT */ + +void Gsm_Update_of_reconstructed_short_time_residual_signal ( + word * dpp, /* [0...39] IN */ + word * ep, /* [0...39] IN */ + word * dp) ; /* [-120...-1] IN/OUT */ + +/* + * Tables from table.c + */ +#ifndef GSM_TABLE_C + +extern word gsm_A [8], gsm_B [8], gsm_MIC [8], gsm_MAC [8] ; +extern word gsm_INVA [8] ; +extern word gsm_DLB [4], gsm_QLB [4] ; +extern word gsm_H [11] ; +extern word gsm_NRFAC [8] ; +extern word gsm_FAC [8] ; + +#endif /* GSM_TABLE_C */ + +/* + * Debugging + */ +#ifdef NDEBUG + +# define gsm_debug_words(a, b, c, d) /* nil */ +# define gsm_debug_longwords(a, b, c, d) /* nil */ +# define gsm_debug_word(a, b) /* nil */ +# define gsm_debug_longword(a, b) /* nil */ + +#else /* !NDEBUG => DEBUG */ + + void gsm_debug_words (char * name, int, int, word *) ; + void gsm_debug_longwords (char * name, int, int, longword *) ; + void gsm_debug_longword (char * name, longword) ; + void gsm_debug_word (char * name, word) ; + +#endif /* !NDEBUG */ + +#endif /* PRIVATE_H */ +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 8bc5fdf2-e8c8-4686-9bd7-a30b512bef0c +*/ + diff --git a/src/GSM610/gsm_create.c b/src/GSM610/gsm_create.c new file mode 100644 index 00000000..94e8d7d7 --- /dev/null +++ b/src/GSM610/gsm_create.c @@ -0,0 +1,44 @@ +/* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + +#include "config.h" + +#include +#include +#include + + + +#include "gsm.h" +#include "gsm610_priv.h" + +gsm gsm_create (void) +{ + gsm r; + + r = malloc (sizeof(struct gsm_state)); + if (!r) return r; + + memset((char *)r, 0, sizeof (struct gsm_state)); + r->nrp = 40; + + return r; +} + +/* Added for libsndfile : May 6, 2002. Not sure if it works. */ +void gsm_init (gsm state) +{ + memset (state, 0, sizeof (struct gsm_state)) ; + state->nrp = 40 ; +} +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 9fedb6b3-ed99-40c2-aac1-484c536261fe +*/ + diff --git a/src/GSM610/gsm_decode.c b/src/GSM610/gsm_decode.c new file mode 100644 index 00000000..e6425587 --- /dev/null +++ b/src/GSM610/gsm_decode.c @@ -0,0 +1,366 @@ +/* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + +#include "gsm610_priv.h" + +#include "gsm.h" + +int gsm_decode (gsm s, gsm_byte * c, gsm_signal * target) +{ + word LARc[8], Nc[4], Mc[4], bc[4], xmaxc[4], xmc[13*4]; + +#ifdef WAV49 + if (s->wav_fmt) { + + uword sr = 0; + + s->frame_index = !s->frame_index; + if (s->frame_index) { + + sr = *c++; + LARc[0] = sr & 0x3f; sr >>= 6; + sr |= (uword)*c++ << 2; + LARc[1] = sr & 0x3f; sr >>= 6; + sr |= (uword)*c++ << 4; + LARc[2] = sr & 0x1f; sr >>= 5; + LARc[3] = sr & 0x1f; sr >>= 5; + sr |= (uword)*c++ << 2; + LARc[4] = sr & 0xf; sr >>= 4; + LARc[5] = sr & 0xf; sr >>= 4; + sr |= (uword)*c++ << 2; /* 5 */ + LARc[6] = sr & 0x7; sr >>= 3; + LARc[7] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 4; + Nc[0] = sr & 0x7f; sr >>= 7; + bc[0] = sr & 0x3; sr >>= 2; + Mc[0] = sr & 0x3; sr >>= 2; + sr |= (uword)*c++ << 1; + xmaxc[0] = sr & 0x3f; sr >>= 6; + xmc[0] = sr & 0x7; sr >>= 3; + sr = *c++; + xmc[1] = sr & 0x7; sr >>= 3; + xmc[2] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; + xmc[3] = sr & 0x7; sr >>= 3; + xmc[4] = sr & 0x7; sr >>= 3; + xmc[5] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; /* 10 */ + xmc[6] = sr & 0x7; sr >>= 3; + xmc[7] = sr & 0x7; sr >>= 3; + xmc[8] = sr & 0x7; sr >>= 3; + sr = *c++; + xmc[9] = sr & 0x7; sr >>= 3; + xmc[10] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; + xmc[11] = sr & 0x7; sr >>= 3; + xmc[12] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 4; + Nc[1] = sr & 0x7f; sr >>= 7; + bc[1] = sr & 0x3; sr >>= 2; + Mc[1] = sr & 0x3; sr >>= 2; + sr |= (uword)*c++ << 1; + xmaxc[1] = sr & 0x3f; sr >>= 6; + xmc[13] = sr & 0x7; sr >>= 3; + sr = *c++; /* 15 */ + xmc[14] = sr & 0x7; sr >>= 3; + xmc[15] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; + xmc[16] = sr & 0x7; sr >>= 3; + xmc[17] = sr & 0x7; sr >>= 3; + xmc[18] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; + xmc[19] = sr & 0x7; sr >>= 3; + xmc[20] = sr & 0x7; sr >>= 3; + xmc[21] = sr & 0x7; sr >>= 3; + sr = *c++; + xmc[22] = sr & 0x7; sr >>= 3; + xmc[23] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; + xmc[24] = sr & 0x7; sr >>= 3; + xmc[25] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 4; /* 20 */ + Nc[2] = sr & 0x7f; sr >>= 7; + bc[2] = sr & 0x3; sr >>= 2; + Mc[2] = sr & 0x3; sr >>= 2; + sr |= (uword)*c++ << 1; + xmaxc[2] = sr & 0x3f; sr >>= 6; + xmc[26] = sr & 0x7; sr >>= 3; + sr = *c++; + xmc[27] = sr & 0x7; sr >>= 3; + xmc[28] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; + xmc[29] = sr & 0x7; sr >>= 3; + xmc[30] = sr & 0x7; sr >>= 3; + xmc[31] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; + xmc[32] = sr & 0x7; sr >>= 3; + xmc[33] = sr & 0x7; sr >>= 3; + xmc[34] = sr & 0x7; sr >>= 3; + sr = *c++; /* 25 */ + xmc[35] = sr & 0x7; sr >>= 3; + xmc[36] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; + xmc[37] = sr & 0x7; sr >>= 3; + xmc[38] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 4; + Nc[3] = sr & 0x7f; sr >>= 7; + bc[3] = sr & 0x3; sr >>= 2; + Mc[3] = sr & 0x3; sr >>= 2; + sr |= (uword)*c++ << 1; + xmaxc[3] = sr & 0x3f; sr >>= 6; + xmc[39] = sr & 0x7; sr >>= 3; + sr = *c++; + xmc[40] = sr & 0x7; sr >>= 3; + xmc[41] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; /* 30 */ + xmc[42] = sr & 0x7; sr >>= 3; + xmc[43] = sr & 0x7; sr >>= 3; + xmc[44] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; + xmc[45] = sr & 0x7; sr >>= 3; + xmc[46] = sr & 0x7; sr >>= 3; + xmc[47] = sr & 0x7; sr >>= 3; + sr = *c++; + xmc[48] = sr & 0x7; sr >>= 3; + xmc[49] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; + xmc[50] = sr & 0x7; sr >>= 3; + xmc[51] = sr & 0x7; sr >>= 3; + + s->frame_chain = sr & 0xf; + } + else { + sr = s->frame_chain; + sr |= (uword)*c++ << 4; /* 1 */ + LARc[0] = sr & 0x3f; sr >>= 6; + LARc[1] = sr & 0x3f; sr >>= 6; + sr = *c++; + LARc[2] = sr & 0x1f; sr >>= 5; + sr |= (uword)*c++ << 3; + LARc[3] = sr & 0x1f; sr >>= 5; + LARc[4] = sr & 0xf; sr >>= 4; + sr |= (uword)*c++ << 2; + LARc[5] = sr & 0xf; sr >>= 4; + LARc[6] = sr & 0x7; sr >>= 3; + LARc[7] = sr & 0x7; sr >>= 3; + sr = *c++; /* 5 */ + Nc[0] = sr & 0x7f; sr >>= 7; + sr |= (uword)*c++ << 1; + bc[0] = sr & 0x3; sr >>= 2; + Mc[0] = sr & 0x3; sr >>= 2; + sr |= (uword)*c++ << 5; + xmaxc[0] = sr & 0x3f; sr >>= 6; + xmc[0] = sr & 0x7; sr >>= 3; + xmc[1] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; + xmc[2] = sr & 0x7; sr >>= 3; + xmc[3] = sr & 0x7; sr >>= 3; + xmc[4] = sr & 0x7; sr >>= 3; + sr = *c++; + xmc[5] = sr & 0x7; sr >>= 3; + xmc[6] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; /* 10 */ + xmc[7] = sr & 0x7; sr >>= 3; + xmc[8] = sr & 0x7; sr >>= 3; + xmc[9] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; + xmc[10] = sr & 0x7; sr >>= 3; + xmc[11] = sr & 0x7; sr >>= 3; + xmc[12] = sr & 0x7; sr >>= 3; + sr = *c++; + Nc[1] = sr & 0x7f; sr >>= 7; + sr |= (uword)*c++ << 1; + bc[1] = sr & 0x3; sr >>= 2; + Mc[1] = sr & 0x3; sr >>= 2; + sr |= (uword)*c++ << 5; + xmaxc[1] = sr & 0x3f; sr >>= 6; + xmc[13] = sr & 0x7; sr >>= 3; + xmc[14] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; /* 15 */ + xmc[15] = sr & 0x7; sr >>= 3; + xmc[16] = sr & 0x7; sr >>= 3; + xmc[17] = sr & 0x7; sr >>= 3; + sr = *c++; + xmc[18] = sr & 0x7; sr >>= 3; + xmc[19] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; + xmc[20] = sr & 0x7; sr >>= 3; + xmc[21] = sr & 0x7; sr >>= 3; + xmc[22] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; + xmc[23] = sr & 0x7; sr >>= 3; + xmc[24] = sr & 0x7; sr >>= 3; + xmc[25] = sr & 0x7; sr >>= 3; + sr = *c++; + Nc[2] = sr & 0x7f; sr >>= 7; + sr |= (uword)*c++ << 1; /* 20 */ + bc[2] = sr & 0x3; sr >>= 2; + Mc[2] = sr & 0x3; sr >>= 2; + sr |= (uword)*c++ << 5; + xmaxc[2] = sr & 0x3f; sr >>= 6; + xmc[26] = sr & 0x7; sr >>= 3; + xmc[27] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; + xmc[28] = sr & 0x7; sr >>= 3; + xmc[29] = sr & 0x7; sr >>= 3; + xmc[30] = sr & 0x7; sr >>= 3; + sr = *c++; + xmc[31] = sr & 0x7; sr >>= 3; + xmc[32] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; + xmc[33] = sr & 0x7; sr >>= 3; + xmc[34] = sr & 0x7; sr >>= 3; + xmc[35] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; /* 25 */ + xmc[36] = sr & 0x7; sr >>= 3; + xmc[37] = sr & 0x7; sr >>= 3; + xmc[38] = sr & 0x7; sr >>= 3; + sr = *c++; + Nc[3] = sr & 0x7f; sr >>= 7; + sr |= (uword)*c++ << 1; + bc[3] = sr & 0x3; sr >>= 2; + Mc[3] = sr & 0x3; sr >>= 2; + sr |= (uword)*c++ << 5; + xmaxc[3] = sr & 0x3f; sr >>= 6; + xmc[39] = sr & 0x7; sr >>= 3; + xmc[40] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; + xmc[41] = sr & 0x7; sr >>= 3; + xmc[42] = sr & 0x7; sr >>= 3; + xmc[43] = sr & 0x7; sr >>= 3; + sr = *c++; /* 30 */ + xmc[44] = sr & 0x7; sr >>= 3; + xmc[45] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 2; + xmc[46] = sr & 0x7; sr >>= 3; + xmc[47] = sr & 0x7; sr >>= 3; + xmc[48] = sr & 0x7; sr >>= 3; + sr |= (uword)*c++ << 1; + xmc[49] = sr & 0x7; sr >>= 3; + xmc[50] = sr & 0x7; sr >>= 3; + xmc[51] = sr & 0x7; sr >>= 3; + } + } + else +#endif + { + /* GSM_MAGIC = (*c >> 4) & 0xF; */ + + if (((*c >> 4) & 0x0F) != GSM_MAGIC) return -1; + + LARc[0] = (*c++ & 0xF) << 2; /* 1 */ + LARc[0] |= (*c >> 6) & 0x3; + LARc[1] = *c++ & 0x3F; + LARc[2] = (*c >> 3) & 0x1F; + LARc[3] = (*c++ & 0x7) << 2; + LARc[3] |= (*c >> 6) & 0x3; + LARc[4] = (*c >> 2) & 0xF; + LARc[5] = (*c++ & 0x3) << 2; + LARc[5] |= (*c >> 6) & 0x3; + LARc[6] = (*c >> 3) & 0x7; + LARc[7] = *c++ & 0x7; + Nc[0] = (*c >> 1) & 0x7F; + bc[0] = (*c++ & 0x1) << 1; + bc[0] |= (*c >> 7) & 0x1; + Mc[0] = (*c >> 5) & 0x3; + xmaxc[0] = (*c++ & 0x1F) << 1; + xmaxc[0] |= (*c >> 7) & 0x1; + xmc[0] = (*c >> 4) & 0x7; + xmc[1] = (*c >> 1) & 0x7; + xmc[2] = (*c++ & 0x1) << 2; + xmc[2] |= (*c >> 6) & 0x3; + xmc[3] = (*c >> 3) & 0x7; + xmc[4] = *c++ & 0x7; + xmc[5] = (*c >> 5) & 0x7; + xmc[6] = (*c >> 2) & 0x7; + xmc[7] = (*c++ & 0x3) << 1; /* 10 */ + xmc[7] |= (*c >> 7) & 0x1; + xmc[8] = (*c >> 4) & 0x7; + xmc[9] = (*c >> 1) & 0x7; + xmc[10] = (*c++ & 0x1) << 2; + xmc[10] |= (*c >> 6) & 0x3; + xmc[11] = (*c >> 3) & 0x7; + xmc[12] = *c++ & 0x7; + Nc[1] = (*c >> 1) & 0x7F; + bc[1] = (*c++ & 0x1) << 1; + bc[1] |= (*c >> 7) & 0x1; + Mc[1] = (*c >> 5) & 0x3; + xmaxc[1] = (*c++ & 0x1F) << 1; + xmaxc[1] |= (*c >> 7) & 0x1; + xmc[13] = (*c >> 4) & 0x7; + xmc[14] = (*c >> 1) & 0x7; + xmc[15] = (*c++ & 0x1) << 2; + xmc[15] |= (*c >> 6) & 0x3; + xmc[16] = (*c >> 3) & 0x7; + xmc[17] = *c++ & 0x7; + xmc[18] = (*c >> 5) & 0x7; + xmc[19] = (*c >> 2) & 0x7; + xmc[20] = (*c++ & 0x3) << 1; + xmc[20] |= (*c >> 7) & 0x1; + xmc[21] = (*c >> 4) & 0x7; + xmc[22] = (*c >> 1) & 0x7; + xmc[23] = (*c++ & 0x1) << 2; + xmc[23] |= (*c >> 6) & 0x3; + xmc[24] = (*c >> 3) & 0x7; + xmc[25] = *c++ & 0x7; + Nc[2] = (*c >> 1) & 0x7F; + bc[2] = (*c++ & 0x1) << 1; /* 20 */ + bc[2] |= (*c >> 7) & 0x1; + Mc[2] = (*c >> 5) & 0x3; + xmaxc[2] = (*c++ & 0x1F) << 1; + xmaxc[2] |= (*c >> 7) & 0x1; + xmc[26] = (*c >> 4) & 0x7; + xmc[27] = (*c >> 1) & 0x7; + xmc[28] = (*c++ & 0x1) << 2; + xmc[28] |= (*c >> 6) & 0x3; + xmc[29] = (*c >> 3) & 0x7; + xmc[30] = *c++ & 0x7; + xmc[31] = (*c >> 5) & 0x7; + xmc[32] = (*c >> 2) & 0x7; + xmc[33] = (*c++ & 0x3) << 1; + xmc[33] |= (*c >> 7) & 0x1; + xmc[34] = (*c >> 4) & 0x7; + xmc[35] = (*c >> 1) & 0x7; + xmc[36] = (*c++ & 0x1) << 2; + xmc[36] |= (*c >> 6) & 0x3; + xmc[37] = (*c >> 3) & 0x7; + xmc[38] = *c++ & 0x7; + Nc[3] = (*c >> 1) & 0x7F; + bc[3] = (*c++ & 0x1) << 1; + bc[3] |= (*c >> 7) & 0x1; + Mc[3] = (*c >> 5) & 0x3; + xmaxc[3] = (*c++ & 0x1F) << 1; + xmaxc[3] |= (*c >> 7) & 0x1; + xmc[39] = (*c >> 4) & 0x7; + xmc[40] = (*c >> 1) & 0x7; + xmc[41] = (*c++ & 0x1) << 2; + xmc[41] |= (*c >> 6) & 0x3; + xmc[42] = (*c >> 3) & 0x7; + xmc[43] = *c++ & 0x7; /* 30 */ + xmc[44] = (*c >> 5) & 0x7; + xmc[45] = (*c >> 2) & 0x7; + xmc[46] = (*c++ & 0x3) << 1; + xmc[46] |= (*c >> 7) & 0x1; + xmc[47] = (*c >> 4) & 0x7; + xmc[48] = (*c >> 1) & 0x7; + xmc[49] = (*c++ & 0x1) << 2; + xmc[49] |= (*c >> 6) & 0x3; + xmc[50] = (*c >> 3) & 0x7; + xmc[51] = *c & 0x7; /* 33 */ + } + + Gsm_Decoder(s, LARc, Nc, bc, Mc, xmaxc, xmc, target); + + return 0; +} +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 6a9b6628-821c-4a96-84c1-485ebd35f170 +*/ + diff --git a/src/GSM610/gsm_destroy.c b/src/GSM610/gsm_destroy.c new file mode 100644 index 00000000..9e2d6a49 --- /dev/null +++ b/src/GSM610/gsm_destroy.c @@ -0,0 +1,31 @@ +/* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + +#include "gsm.h" +#include "config.h" + +#ifdef HAS_STDLIB_H +# include +#else +# ifdef HAS_MALLOC_H +# include +# else + extern void free(); +# endif +#endif + +void gsm_destroy (gsm S) +{ + if (S) free((char *)S); +} +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: f423d09b-6ccc-47e0-9b18-ee1cf7a8e473 +*/ + diff --git a/src/GSM610/gsm_encode.c b/src/GSM610/gsm_encode.c new file mode 100644 index 00000000..02af4ba2 --- /dev/null +++ b/src/GSM610/gsm_encode.c @@ -0,0 +1,456 @@ +/* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + +#include "gsm610_priv.h" +#include "gsm.h" + +void gsm_encode (gsm s, gsm_signal * source, gsm_byte * c) +{ + word LARc[8], Nc[4], Mc[4], bc[4], xmaxc[4], xmc[13*4]; + + Gsm_Coder(s, source, LARc, Nc, bc, Mc, xmaxc, xmc); + + + /* variable size + + GSM_MAGIC 4 + + LARc[0] 6 + LARc[1] 6 + LARc[2] 5 + LARc[3] 5 + LARc[4] 4 + LARc[5] 4 + LARc[6] 3 + LARc[7] 3 + + Nc[0] 7 + bc[0] 2 + Mc[0] 2 + xmaxc[0] 6 + xmc[0] 3 + xmc[1] 3 + xmc[2] 3 + xmc[3] 3 + xmc[4] 3 + xmc[5] 3 + xmc[6] 3 + xmc[7] 3 + xmc[8] 3 + xmc[9] 3 + xmc[10] 3 + xmc[11] 3 + xmc[12] 3 + + Nc[1] 7 + bc[1] 2 + Mc[1] 2 + xmaxc[1] 6 + xmc[13] 3 + xmc[14] 3 + xmc[15] 3 + xmc[16] 3 + xmc[17] 3 + xmc[18] 3 + xmc[19] 3 + xmc[20] 3 + xmc[21] 3 + xmc[22] 3 + xmc[23] 3 + xmc[24] 3 + xmc[25] 3 + + Nc[2] 7 + bc[2] 2 + Mc[2] 2 + xmaxc[2] 6 + xmc[26] 3 + xmc[27] 3 + xmc[28] 3 + xmc[29] 3 + xmc[30] 3 + xmc[31] 3 + xmc[32] 3 + xmc[33] 3 + xmc[34] 3 + xmc[35] 3 + xmc[36] 3 + xmc[37] 3 + xmc[38] 3 + + Nc[3] 7 + bc[3] 2 + Mc[3] 2 + xmaxc[3] 6 + xmc[39] 3 + xmc[40] 3 + xmc[41] 3 + xmc[42] 3 + xmc[43] 3 + xmc[44] 3 + xmc[45] 3 + xmc[46] 3 + xmc[47] 3 + xmc[48] 3 + xmc[49] 3 + xmc[50] 3 + xmc[51] 3 + */ + +#ifdef WAV49 + + if (s->wav_fmt) { + s->frame_index = !s->frame_index; + if (s->frame_index) { + + uword sr; + + sr = 0; + sr = sr >> 6 | LARc[0] << 10; + sr = sr >> 6 | LARc[1] << 10; + *c++ = sr >> 4; + sr = sr >> 5 | LARc[2] << 11; + *c++ = sr >> 7; + sr = sr >> 5 | LARc[3] << 11; + sr = sr >> 4 | LARc[4] << 12; + *c++ = sr >> 6; + sr = sr >> 4 | LARc[5] << 12; + sr = sr >> 3 | LARc[6] << 13; + *c++ = sr >> 7; + sr = sr >> 3 | LARc[7] << 13; + sr = sr >> 7 | Nc[0] << 9; + *c++ = sr >> 5; + sr = sr >> 2 | bc[0] << 14; + sr = sr >> 2 | Mc[0] << 14; + sr = sr >> 6 | xmaxc[0] << 10; + *c++ = sr >> 3; + sr = sr >> 3 | xmc[0] << 13; + *c++ = sr >> 8; + sr = sr >> 3 | xmc[1] << 13; + sr = sr >> 3 | xmc[2] << 13; + sr = sr >> 3 | xmc[3] << 13; + *c++ = sr >> 7; + sr = sr >> 3 | xmc[4] << 13; + sr = sr >> 3 | xmc[5] << 13; + sr = sr >> 3 | xmc[6] << 13; + *c++ = sr >> 6; + sr = sr >> 3 | xmc[7] << 13; + sr = sr >> 3 | xmc[8] << 13; + *c++ = sr >> 8; + sr = sr >> 3 | xmc[9] << 13; + sr = sr >> 3 | xmc[10] << 13; + sr = sr >> 3 | xmc[11] << 13; + *c++ = sr >> 7; + sr = sr >> 3 | xmc[12] << 13; + sr = sr >> 7 | Nc[1] << 9; + *c++ = sr >> 5; + sr = sr >> 2 | bc[1] << 14; + sr = sr >> 2 | Mc[1] << 14; + sr = sr >> 6 | xmaxc[1] << 10; + *c++ = sr >> 3; + sr = sr >> 3 | xmc[13] << 13; + *c++ = sr >> 8; + sr = sr >> 3 | xmc[14] << 13; + sr = sr >> 3 | xmc[15] << 13; + sr = sr >> 3 | xmc[16] << 13; + *c++ = sr >> 7; + sr = sr >> 3 | xmc[17] << 13; + sr = sr >> 3 | xmc[18] << 13; + sr = sr >> 3 | xmc[19] << 13; + *c++ = sr >> 6; + sr = sr >> 3 | xmc[20] << 13; + sr = sr >> 3 | xmc[21] << 13; + *c++ = sr >> 8; + sr = sr >> 3 | xmc[22] << 13; + sr = sr >> 3 | xmc[23] << 13; + sr = sr >> 3 | xmc[24] << 13; + *c++ = sr >> 7; + sr = sr >> 3 | xmc[25] << 13; + sr = sr >> 7 | Nc[2] << 9; + *c++ = sr >> 5; + sr = sr >> 2 | bc[2] << 14; + sr = sr >> 2 | Mc[2] << 14; + sr = sr >> 6 | xmaxc[2] << 10; + *c++ = sr >> 3; + sr = sr >> 3 | xmc[26] << 13; + *c++ = sr >> 8; + sr = sr >> 3 | xmc[27] << 13; + sr = sr >> 3 | xmc[28] << 13; + sr = sr >> 3 | xmc[29] << 13; + *c++ = sr >> 7; + sr = sr >> 3 | xmc[30] << 13; + sr = sr >> 3 | xmc[31] << 13; + sr = sr >> 3 | xmc[32] << 13; + *c++ = sr >> 6; + sr = sr >> 3 | xmc[33] << 13; + sr = sr >> 3 | xmc[34] << 13; + *c++ = sr >> 8; + sr = sr >> 3 | xmc[35] << 13; + sr = sr >> 3 | xmc[36] << 13; + sr = sr >> 3 | xmc[37] << 13; + *c++ = sr >> 7; + sr = sr >> 3 | xmc[38] << 13; + sr = sr >> 7 | Nc[3] << 9; + *c++ = sr >> 5; + sr = sr >> 2 | bc[3] << 14; + sr = sr >> 2 | Mc[3] << 14; + sr = sr >> 6 | xmaxc[3] << 10; + *c++ = sr >> 3; + sr = sr >> 3 | xmc[39] << 13; + *c++ = sr >> 8; + sr = sr >> 3 | xmc[40] << 13; + sr = sr >> 3 | xmc[41] << 13; + sr = sr >> 3 | xmc[42] << 13; + *c++ = sr >> 7; + sr = sr >> 3 | xmc[43] << 13; + sr = sr >> 3 | xmc[44] << 13; + sr = sr >> 3 | xmc[45] << 13; + *c++ = sr >> 6; + sr = sr >> 3 | xmc[46] << 13; + sr = sr >> 3 | xmc[47] << 13; + *c++ = sr >> 8; + sr = sr >> 3 | xmc[48] << 13; + sr = sr >> 3 | xmc[49] << 13; + sr = sr >> 3 | xmc[50] << 13; + *c++ = sr >> 7; + sr = sr >> 3 | xmc[51] << 13; + sr = sr >> 4; + *c = sr >> 8; + s->frame_chain = *c; + } + else { + uword sr; + + sr = 0; + sr = sr >> 4 | s->frame_chain << 12; + sr = sr >> 6 | LARc[0] << 10; + *c++ = sr >> 6; + sr = sr >> 6 | LARc[1] << 10; + *c++ = sr >> 8; + sr = sr >> 5 | LARc[2] << 11; + sr = sr >> 5 | LARc[3] << 11; + *c++ = sr >> 6; + sr = sr >> 4 | LARc[4] << 12; + sr = sr >> 4 | LARc[5] << 12; + *c++ = sr >> 6; + sr = sr >> 3 | LARc[6] << 13; + sr = sr >> 3 | LARc[7] << 13; + *c++ = sr >> 8; + sr = sr >> 7 | Nc[0] << 9; + sr = sr >> 2 | bc[0] << 14; + *c++ = sr >> 7; + sr = sr >> 2 | Mc[0] << 14; + sr = sr >> 6 | xmaxc[0] << 10; + *c++ = sr >> 7; + sr = sr >> 3 | xmc[0] << 13; + sr = sr >> 3 | xmc[1] << 13; + sr = sr >> 3 | xmc[2] << 13; + *c++ = sr >> 6; + sr = sr >> 3 | xmc[3] << 13; + sr = sr >> 3 | xmc[4] << 13; + *c++ = sr >> 8; + sr = sr >> 3 | xmc[5] << 13; + sr = sr >> 3 | xmc[6] << 13; + sr = sr >> 3 | xmc[7] << 13; + *c++ = sr >> 7; + sr = sr >> 3 | xmc[8] << 13; + sr = sr >> 3 | xmc[9] << 13; + sr = sr >> 3 | xmc[10] << 13; + *c++ = sr >> 6; + sr = sr >> 3 | xmc[11] << 13; + sr = sr >> 3 | xmc[12] << 13; + *c++ = sr >> 8; + sr = sr >> 7 | Nc[1] << 9; + sr = sr >> 2 | bc[1] << 14; + *c++ = sr >> 7; + sr = sr >> 2 | Mc[1] << 14; + sr = sr >> 6 | xmaxc[1] << 10; + *c++ = sr >> 7; + sr = sr >> 3 | xmc[13] << 13; + sr = sr >> 3 | xmc[14] << 13; + sr = sr >> 3 | xmc[15] << 13; + *c++ = sr >> 6; + sr = sr >> 3 | xmc[16] << 13; + sr = sr >> 3 | xmc[17] << 13; + *c++ = sr >> 8; + sr = sr >> 3 | xmc[18] << 13; + sr = sr >> 3 | xmc[19] << 13; + sr = sr >> 3 | xmc[20] << 13; + *c++ = sr >> 7; + sr = sr >> 3 | xmc[21] << 13; + sr = sr >> 3 | xmc[22] << 13; + sr = sr >> 3 | xmc[23] << 13; + *c++ = sr >> 6; + sr = sr >> 3 | xmc[24] << 13; + sr = sr >> 3 | xmc[25] << 13; + *c++ = sr >> 8; + sr = sr >> 7 | Nc[2] << 9; + sr = sr >> 2 | bc[2] << 14; + *c++ = sr >> 7; + sr = sr >> 2 | Mc[2] << 14; + sr = sr >> 6 | xmaxc[2] << 10; + *c++ = sr >> 7; + sr = sr >> 3 | xmc[26] << 13; + sr = sr >> 3 | xmc[27] << 13; + sr = sr >> 3 | xmc[28] << 13; + *c++ = sr >> 6; + sr = sr >> 3 | xmc[29] << 13; + sr = sr >> 3 | xmc[30] << 13; + *c++ = sr >> 8; + sr = sr >> 3 | xmc[31] << 13; + sr = sr >> 3 | xmc[32] << 13; + sr = sr >> 3 | xmc[33] << 13; + *c++ = sr >> 7; + sr = sr >> 3 | xmc[34] << 13; + sr = sr >> 3 | xmc[35] << 13; + sr = sr >> 3 | xmc[36] << 13; + *c++ = sr >> 6; + sr = sr >> 3 | xmc[37] << 13; + sr = sr >> 3 | xmc[38] << 13; + *c++ = sr >> 8; + sr = sr >> 7 | Nc[3] << 9; + sr = sr >> 2 | bc[3] << 14; + *c++ = sr >> 7; + sr = sr >> 2 | Mc[3] << 14; + sr = sr >> 6 | xmaxc[3] << 10; + *c++ = sr >> 7; + sr = sr >> 3 | xmc[39] << 13; + sr = sr >> 3 | xmc[40] << 13; + sr = sr >> 3 | xmc[41] << 13; + *c++ = sr >> 6; + sr = sr >> 3 | xmc[42] << 13; + sr = sr >> 3 | xmc[43] << 13; + *c++ = sr >> 8; + sr = sr >> 3 | xmc[44] << 13; + sr = sr >> 3 | xmc[45] << 13; + sr = sr >> 3 | xmc[46] << 13; + *c++ = sr >> 7; + sr = sr >> 3 | xmc[47] << 13; + sr = sr >> 3 | xmc[48] << 13; + sr = sr >> 3 | xmc[49] << 13; + *c++ = sr >> 6; + sr = sr >> 3 | xmc[50] << 13; + sr = sr >> 3 | xmc[51] << 13; + *c++ = sr >> 8; + } + } + + else + +#endif /* WAV49 */ + { + + *c++ = ((GSM_MAGIC & 0xF) << 4) /* 1 */ + | ((LARc[0] >> 2) & 0xF); + *c++ = ((LARc[0] & 0x3) << 6) + | (LARc[1] & 0x3F); + *c++ = ((LARc[2] & 0x1F) << 3) + | ((LARc[3] >> 2) & 0x7); + *c++ = ((LARc[3] & 0x3) << 6) + | ((LARc[4] & 0xF) << 2) + | ((LARc[5] >> 2) & 0x3); + *c++ = ((LARc[5] & 0x3) << 6) + | ((LARc[6] & 0x7) << 3) + | (LARc[7] & 0x7); + *c++ = ((Nc[0] & 0x7F) << 1) + | ((bc[0] >> 1) & 0x1); + *c++ = ((bc[0] & 0x1) << 7) + | ((Mc[0] & 0x3) << 5) + | ((xmaxc[0] >> 1) & 0x1F); + *c++ = ((xmaxc[0] & 0x1) << 7) + | ((xmc[0] & 0x7) << 4) + | ((xmc[1] & 0x7) << 1) + | ((xmc[2] >> 2) & 0x1); + *c++ = ((xmc[2] & 0x3) << 6) + | ((xmc[3] & 0x7) << 3) + | (xmc[4] & 0x7); + *c++ = ((xmc[5] & 0x7) << 5) /* 10 */ + | ((xmc[6] & 0x7) << 2) + | ((xmc[7] >> 1) & 0x3); + *c++ = ((xmc[7] & 0x1) << 7) + | ((xmc[8] & 0x7) << 4) + | ((xmc[9] & 0x7) << 1) + | ((xmc[10] >> 2) & 0x1); + *c++ = ((xmc[10] & 0x3) << 6) + | ((xmc[11] & 0x7) << 3) + | (xmc[12] & 0x7); + *c++ = ((Nc[1] & 0x7F) << 1) + | ((bc[1] >> 1) & 0x1); + *c++ = ((bc[1] & 0x1) << 7) + | ((Mc[1] & 0x3) << 5) + | ((xmaxc[1] >> 1) & 0x1F); + *c++ = ((xmaxc[1] & 0x1) << 7) + | ((xmc[13] & 0x7) << 4) + | ((xmc[14] & 0x7) << 1) + | ((xmc[15] >> 2) & 0x1); + *c++ = ((xmc[15] & 0x3) << 6) + | ((xmc[16] & 0x7) << 3) + | (xmc[17] & 0x7); + *c++ = ((xmc[18] & 0x7) << 5) + | ((xmc[19] & 0x7) << 2) + | ((xmc[20] >> 1) & 0x3); + *c++ = ((xmc[20] & 0x1) << 7) + | ((xmc[21] & 0x7) << 4) + | ((xmc[22] & 0x7) << 1) + | ((xmc[23] >> 2) & 0x1); + *c++ = ((xmc[23] & 0x3) << 6) + | ((xmc[24] & 0x7) << 3) + | (xmc[25] & 0x7); + *c++ = ((Nc[2] & 0x7F) << 1) /* 20 */ + | ((bc[2] >> 1) & 0x1); + *c++ = ((bc[2] & 0x1) << 7) + | ((Mc[2] & 0x3) << 5) + | ((xmaxc[2] >> 1) & 0x1F); + *c++ = ((xmaxc[2] & 0x1) << 7) + | ((xmc[26] & 0x7) << 4) + | ((xmc[27] & 0x7) << 1) + | ((xmc[28] >> 2) & 0x1); + *c++ = ((xmc[28] & 0x3) << 6) + | ((xmc[29] & 0x7) << 3) + | (xmc[30] & 0x7); + *c++ = ((xmc[31] & 0x7) << 5) + | ((xmc[32] & 0x7) << 2) + | ((xmc[33] >> 1) & 0x3); + *c++ = ((xmc[33] & 0x1) << 7) + | ((xmc[34] & 0x7) << 4) + | ((xmc[35] & 0x7) << 1) + | ((xmc[36] >> 2) & 0x1); + *c++ = ((xmc[36] & 0x3) << 6) + | ((xmc[37] & 0x7) << 3) + | (xmc[38] & 0x7); + *c++ = ((Nc[3] & 0x7F) << 1) + | ((bc[3] >> 1) & 0x1); + *c++ = ((bc[3] & 0x1) << 7) + | ((Mc[3] & 0x3) << 5) + | ((xmaxc[3] >> 1) & 0x1F); + *c++ = ((xmaxc[3] & 0x1) << 7) + | ((xmc[39] & 0x7) << 4) + | ((xmc[40] & 0x7) << 1) + | ((xmc[41] >> 2) & 0x1); + *c++ = ((xmc[41] & 0x3) << 6) /* 30 */ + | ((xmc[42] & 0x7) << 3) + | (xmc[43] & 0x7); + *c++ = ((xmc[44] & 0x7) << 5) + | ((xmc[45] & 0x7) << 2) + | ((xmc[46] >> 1) & 0x3); + *c++ = ((xmc[46] & 0x1) << 7) + | ((xmc[47] & 0x7) << 4) + | ((xmc[48] & 0x7) << 1) + | ((xmc[49] >> 2) & 0x1); + *c++ = ((xmc[49] & 0x3) << 6) + | ((xmc[50] & 0x7) << 3) + | (xmc[51] & 0x7); + + } +} +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: cfe9c43d-d97c-4216-b5e5-ccd6a25b582b +*/ + diff --git a/src/GSM610/gsm_option.c b/src/GSM610/gsm_option.c new file mode 100644 index 00000000..5c56d78d --- /dev/null +++ b/src/GSM610/gsm_option.c @@ -0,0 +1,74 @@ +/* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + +#include "gsm610_priv.h" + +#include "gsm.h" + +int gsm_option (gsm r, int opt, int * val) +{ + int result = -1; + + switch (opt) { + case GSM_OPT_LTP_CUT: +#ifdef LTP_CUT + result = r->ltp_cut; + if (val) r->ltp_cut = *val; +#endif + break; + + case GSM_OPT_VERBOSE: +#ifndef NDEBUG + result = r->verbose; + if (val) r->verbose = *val; +#endif + break; + + case GSM_OPT_FAST: + +#if defined(FAST) && defined(USE_FLOAT_MUL) + result = r->fast; + if (val) r->fast = !!*val; +#endif + break; + + case GSM_OPT_FRAME_CHAIN: + +#ifdef WAV49 + result = r->frame_chain; + if (val) r->frame_chain = *val; +#endif + break; + + case GSM_OPT_FRAME_INDEX: + +#ifdef WAV49 + result = r->frame_index; + if (val) r->frame_index = *val; +#endif + break; + + case GSM_OPT_WAV49: + +#ifdef WAV49 + result = r->wav_fmt; + if (val) r->wav_fmt = !!*val; +#endif + break; + + default: + break; + } + return result; +} +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 963ff156-506f-4359-9145-371e9060b030 +*/ + diff --git a/src/GSM610/long_term.c b/src/GSM610/long_term.c new file mode 100644 index 00000000..b782d111 --- /dev/null +++ b/src/GSM610/long_term.c @@ -0,0 +1,969 @@ +/* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + +#include +#include + +#include "gsm610_priv.h" + +#include "gsm.h" + +/* + * 4.2.11 .. 4.2.12 LONG TERM PREDICTOR (LTP) SECTION + */ + + +/* + * This module computes the LTP gain (bc) and the LTP lag (Nc) + * for the long term analysis filter. This is done by calculating a + * maximum of the cross-correlation function between the current + * sub-segment short term residual signal d[0..39] (output of + * the short term analysis filter; for simplification the index + * of this array begins at 0 and ends at 39 for each sub-segment of the + * RPE-LTP analysis) and the previous reconstructed short term + * residual signal dp[ -120 .. -1 ]. A dynamic scaling must be + * performed to avoid overflow. + */ + + /* The next procedure exists in six versions. First two integer + * version (if USE_FLOAT_MUL is not defined); then four floating + * point versions, twice with proper scaling (USE_FLOAT_MUL defined), + * once without (USE_FLOAT_MUL and FAST defined, and fast run-time + * option used). Every pair has first a Cut version (see the -C + * option to toast or the LTP_CUT option to gsm_option()), then the + * uncut one. (For a detailed explanation of why this is altogether + * a bad idea, see Henry Spencer and Geoff Collyer, ``#ifdef Considered + * Harmful''.) + */ + +#ifndef USE_FLOAT_MUL + +#ifdef LTP_CUT + +static void Cut_Calculation_of_the_LTP_parameters ( + + struct gsm_state * st, + + register word * d, /* [0..39] IN */ + register word * dp, /* [-120..-1] IN */ + word * bc_out, /* OUT */ + word * Nc_out /* OUT */ +) +{ + register int k, lambda; + word Nc, bc; + word wt[40]; + + longword L_result; + longword L_max, L_power; + word R, S, dmax, scal, best_k; + word ltp_cut; + + register word temp, wt_k; + + /* Search of the optimum scaling of d[0..39]. + */ + dmax = 0; + for (k = 0; k <= 39; k++) { + temp = d[k]; + temp = GSM_ABS( temp ); + if (temp > dmax) { + dmax = temp; + best_k = k; + } + } + temp = 0; + if (dmax == 0) scal = 0; + else { + assert(dmax > 0); + temp = gsm_norm( (longword)dmax << 16 ); + } + if (temp > 6) scal = 0; + else scal = 6 - temp; + assert(scal >= 0); + + /* Search for the maximum cross-correlation and coding of the LTP lag + */ + L_max = 0; + Nc = 40; /* index for the maximum cross-correlation */ + wt_k = SASR_W(d[best_k], scal); + + for (lambda = 40; lambda <= 120; lambda++) { + L_result = (longword)wt_k * dp[best_k - lambda]; + if (L_result > L_max) { + Nc = lambda; + L_max = L_result; + } + } + *Nc_out = Nc; + L_max <<= 1; + + /* Rescaling of L_max + */ + assert(scal <= 100 && scal >= -100); + L_max = L_max >> (6 - scal); /* sub(6, scal) */ + + assert( Nc <= 120 && Nc >= 40); + + /* Compute the power of the reconstructed short term residual + * signal dp[..] + */ + L_power = 0; + for (k = 0; k <= 39; k++) { + + register longword L_temp; + + L_temp = SASR_W( dp[k - Nc], 3 ); + L_power += L_temp * L_temp; + } + L_power <<= 1; /* from L_MULT */ + + /* Normalization of L_max and L_power + */ + + if (L_max <= 0) { + *bc_out = 0; + return; + } + if (L_max >= L_power) { + *bc_out = 3; + return; + } + + temp = gsm_norm( L_power ); + + R = SASR( L_max << temp, 16 ); + S = SASR( L_power << temp, 16 ); + + /* Coding of the LTP gain + */ + + /* Table 4.3a must be used to obtain the level DLB[i] for the + * quantization of the LTP gain b to get the coded version bc. + */ + for (bc = 0; bc <= 2; bc++) if (R <= gsm_mult(S, gsm_DLB[bc])) break; + *bc_out = bc; +} + +#endif /* LTP_CUT */ + +static void Calculation_of_the_LTP_parameters ( + register word * d, /* [0..39] IN */ + register word * dp, /* [-120..-1] IN */ + word * bc_out, /* OUT */ + word * Nc_out /* OUT */ +) +{ + register int k, lambda; + word Nc, bc; + word wt[40]; + + longword L_max, L_power; + word R, S, dmax, scal; + register word temp; + + /* Search of the optimum scaling of d[0..39]. + */ + dmax = 0; + + for (k = 0; k <= 39; k++) { + temp = d[k]; + temp = GSM_ABS( temp ); + if (temp > dmax) dmax = temp; + } + + temp = 0; + if (dmax == 0) scal = 0; + else { + assert(dmax > 0); + temp = gsm_norm( (longword)dmax << 16 ); + } + + if (temp > 6) scal = 0; + else scal = 6 - temp; + + assert(scal >= 0); + + /* Initialization of a working array wt + */ + + for (k = 0; k <= 39; k++) wt[k] = SASR_W( d[k], scal ); + + /* Search for the maximum cross-correlation and coding of the LTP lag + */ + L_max = 0; + Nc = 40; /* index for the maximum cross-correlation */ + + for (lambda = 40; lambda <= 120; lambda++) { + +# undef STEP +# define STEP(k) (longword)wt[k] * dp[k - lambda] + + register longword L_result; + + L_result = STEP(0) ; L_result += STEP(1) ; + L_result += STEP(2) ; L_result += STEP(3) ; + L_result += STEP(4) ; L_result += STEP(5) ; + L_result += STEP(6) ; L_result += STEP(7) ; + L_result += STEP(8) ; L_result += STEP(9) ; + L_result += STEP(10) ; L_result += STEP(11) ; + L_result += STEP(12) ; L_result += STEP(13) ; + L_result += STEP(14) ; L_result += STEP(15) ; + L_result += STEP(16) ; L_result += STEP(17) ; + L_result += STEP(18) ; L_result += STEP(19) ; + L_result += STEP(20) ; L_result += STEP(21) ; + L_result += STEP(22) ; L_result += STEP(23) ; + L_result += STEP(24) ; L_result += STEP(25) ; + L_result += STEP(26) ; L_result += STEP(27) ; + L_result += STEP(28) ; L_result += STEP(29) ; + L_result += STEP(30) ; L_result += STEP(31) ; + L_result += STEP(32) ; L_result += STEP(33) ; + L_result += STEP(34) ; L_result += STEP(35) ; + L_result += STEP(36) ; L_result += STEP(37) ; + L_result += STEP(38) ; L_result += STEP(39) ; + + if (L_result > L_max) { + + Nc = lambda; + L_max = L_result; + } + } + + *Nc_out = Nc; + + L_max <<= 1; + + /* Rescaling of L_max + */ + assert(scal <= 100 && scal >= -100); + L_max = L_max >> (6 - scal); /* sub(6, scal) */ + + assert( Nc <= 120 && Nc >= 40); + + /* Compute the power of the reconstructed short term residual + * signal dp[..] + */ + L_power = 0; + for (k = 0; k <= 39; k++) { + + register longword L_temp; + + L_temp = SASR_W( dp[k - Nc], 3 ); + L_power += L_temp * L_temp; + } + L_power <<= 1; /* from L_MULT */ + + /* Normalization of L_max and L_power + */ + + if (L_max <= 0) { + *bc_out = 0; + return; + } + if (L_max >= L_power) { + *bc_out = 3; + return; + } + + temp = gsm_norm( L_power ); + + R = SASR_L( L_max << temp, 16 ); + S = SASR_L( L_power << temp, 16 ); + + /* Coding of the LTP gain + */ + + /* Table 4.3a must be used to obtain the level DLB[i] for the + * quantization of the LTP gain b to get the coded version bc. + */ + for (bc = 0; bc <= 2; bc++) if (R <= gsm_mult(S, gsm_DLB[bc])) break; + *bc_out = bc; +} + +#else /* USE_FLOAT_MUL */ + +#ifdef LTP_CUT + +static void Cut_Calculation_of_the_LTP_parameters ( + struct gsm_state * st, /* IN */ + register word * d, /* [0..39] IN */ + register word * dp, /* [-120..-1] IN */ + word * bc_out, /* OUT */ + word * Nc_out /* OUT */ +) +{ + register int k, lambda; + word Nc, bc; + word ltp_cut; + + float wt_float[40]; + float dp_float_base[120], * dp_float = dp_float_base + 120; + + longword L_max, L_power; + word R, S, dmax, scal; + register word temp; + + /* Search of the optimum scaling of d[0..39]. + */ + dmax = 0; + + for (k = 0; k <= 39; k++) { + temp = d[k]; + temp = GSM_ABS( temp ); + if (temp > dmax) dmax = temp; + } + + temp = 0; + if (dmax == 0) scal = 0; + else { + assert(dmax > 0); + temp = gsm_norm( (longword)dmax << 16 ); + } + + if (temp > 6) scal = 0; + else scal = 6 - temp; + + assert(scal >= 0); + ltp_cut = (longword)SASR_W(dmax, scal) * st->ltp_cut / 100; + + + /* Initialization of a working array wt + */ + + for (k = 0; k < 40; k++) { + register word w = SASR_W( d[k], scal ); + if (w < 0 ? w > -ltp_cut : w < ltp_cut) { + wt_float[k] = 0.0; + } + else { + wt_float[k] = w; + } + } + for (k = -120; k < 0; k++) dp_float[k] = dp[k]; + + /* Search for the maximum cross-correlation and coding of the LTP lag + */ + L_max = 0; + Nc = 40; /* index for the maximum cross-correlation */ + + for (lambda = 40; lambda <= 120; lambda += 9) { + + /* Calculate L_result for l = lambda .. lambda + 9. + */ + register float *lp = dp_float - lambda; + + register float W; + register float a = lp[-8], b = lp[-7], c = lp[-6], + d = lp[-5], e = lp[-4], f = lp[-3], + g = lp[-2], h = lp[-1]; + register float E; + register float S0 = 0, S1 = 0, S2 = 0, S3 = 0, S4 = 0, + S5 = 0, S6 = 0, S7 = 0, S8 = 0; + +# undef STEP +# define STEP(K, a, b, c, d, e, f, g, h) \ + if ((W = wt_float[K]) != 0.0) { \ + E = W * a; S8 += E; \ + E = W * b; S7 += E; \ + E = W * c; S6 += E; \ + E = W * d; S5 += E; \ + E = W * e; S4 += E; \ + E = W * f; S3 += E; \ + E = W * g; S2 += E; \ + E = W * h; S1 += E; \ + a = lp[K]; \ + E = W * a; S0 += E; } else (a = lp[K]) + +# define STEP_A(K) STEP(K, a, b, c, d, e, f, g, h) +# define STEP_B(K) STEP(K, b, c, d, e, f, g, h, a) +# define STEP_C(K) STEP(K, c, d, e, f, g, h, a, b) +# define STEP_D(K) STEP(K, d, e, f, g, h, a, b, c) +# define STEP_E(K) STEP(K, e, f, g, h, a, b, c, d) +# define STEP_F(K) STEP(K, f, g, h, a, b, c, d, e) +# define STEP_G(K) STEP(K, g, h, a, b, c, d, e, f) +# define STEP_H(K) STEP(K, h, a, b, c, d, e, f, g) + + STEP_A( 0); STEP_B( 1); STEP_C( 2); STEP_D( 3); + STEP_E( 4); STEP_F( 5); STEP_G( 6); STEP_H( 7); + + STEP_A( 8); STEP_B( 9); STEP_C(10); STEP_D(11); + STEP_E(12); STEP_F(13); STEP_G(14); STEP_H(15); + + STEP_A(16); STEP_B(17); STEP_C(18); STEP_D(19); + STEP_E(20); STEP_F(21); STEP_G(22); STEP_H(23); + + STEP_A(24); STEP_B(25); STEP_C(26); STEP_D(27); + STEP_E(28); STEP_F(29); STEP_G(30); STEP_H(31); + + STEP_A(32); STEP_B(33); STEP_C(34); STEP_D(35); + STEP_E(36); STEP_F(37); STEP_G(38); STEP_H(39); + +# undef STEP_A +# undef STEP_B +# undef STEP_C +# undef STEP_D +# undef STEP_E +# undef STEP_F +# undef STEP_G +# undef STEP_H + + if (S0 > L_max) { L_max = S0; Nc = lambda; } + if (S1 > L_max) { L_max = S1; Nc = lambda + 1; } + if (S2 > L_max) { L_max = S2; Nc = lambda + 2; } + if (S3 > L_max) { L_max = S3; Nc = lambda + 3; } + if (S4 > L_max) { L_max = S4; Nc = lambda + 4; } + if (S5 > L_max) { L_max = S5; Nc = lambda + 5; } + if (S6 > L_max) { L_max = S6; Nc = lambda + 6; } + if (S7 > L_max) { L_max = S7; Nc = lambda + 7; } + if (S8 > L_max) { L_max = S8; Nc = lambda + 8; } + + } + *Nc_out = Nc; + + L_max <<= 1; + + /* Rescaling of L_max + */ + assert(scal <= 100 && scal >= -100); + L_max = L_max >> (6 - scal); /* sub(6, scal) */ + + assert( Nc <= 120 && Nc >= 40); + + /* Compute the power of the reconstructed short term residual + * signal dp[..] + */ + L_power = 0; + for (k = 0; k <= 39; k++) { + + register longword L_temp; + + L_temp = SASR_W( dp[k - Nc], 3 ); + L_power += L_temp * L_temp; + } + L_power <<= 1; /* from L_MULT */ + + /* Normalization of L_max and L_power + */ + + if (L_max <= 0) { + *bc_out = 0; + return; + } + if (L_max >= L_power) { + *bc_out = 3; + return; + } + + temp = gsm_norm( L_power ); + + R = SASR( L_max << temp, 16 ); + S = SASR( L_power << temp, 16 ); + + /* Coding of the LTP gain + */ + + /* Table 4.3a must be used to obtain the level DLB[i] for the + * quantization of the LTP gain b to get the coded version bc. + */ + for (bc = 0; bc <= 2; bc++) if (R <= gsm_mult(S, gsm_DLB[bc])) break; + *bc_out = bc; +} + +#endif /* LTP_CUT */ + +static void Calculation_of_the_LTP_parameters ( + register word * din, /* [0..39] IN */ + register word * dp, /* [-120..-1] IN */ + word * bc_out, /* OUT */ + word * Nc_out /* OUT */ +) +{ + register int k, lambda; + word Nc, bc; + + float wt_float[40]; + float dp_float_base[120], * dp_float = dp_float_base + 120; + + longword L_max, L_power; + word R, S, dmax, scal; + register word temp; + + /* Search of the optimum scaling of d[0..39]. + */ + dmax = 0; + + for (k = 0; k <= 39; k++) { + temp = din [k] ; + temp = GSM_ABS (temp) ; + if (temp > dmax) dmax = temp; + } + + temp = 0; + if (dmax == 0) scal = 0; + else { + assert(dmax > 0); + temp = gsm_norm( (longword)dmax << 16 ); + } + + if (temp > 6) scal = 0; + else scal = 6 - temp; + + assert(scal >= 0); + + /* Initialization of a working array wt + */ + + for (k = 0; k < 40; k++) wt_float[k] = SASR_W (din [k], scal) ; + for (k = -120; k < 0; k++) dp_float[k] = dp[k]; + + /* Search for the maximum cross-correlation and coding of the LTP lag + */ + L_max = 0; + Nc = 40; /* index for the maximum cross-correlation */ + + for (lambda = 40; lambda <= 120; lambda += 9) { + + /* Calculate L_result for l = lambda .. lambda + 9. + */ + register float *lp = dp_float - lambda; + + register float W; + register float a = lp[-8], b = lp[-7], c = lp[-6], + d = lp[-5], e = lp[-4], f = lp[-3], + g = lp[-2], h = lp[-1]; + register float E; + register float S0 = 0, S1 = 0, S2 = 0, S3 = 0, S4 = 0, + S5 = 0, S6 = 0, S7 = 0, S8 = 0; + +# undef STEP +# define STEP(K, a, b, c, d, e, f, g, h) \ + W = wt_float[K]; \ + E = W * a; S8 += E; \ + E = W * b; S7 += E; \ + E = W * c; S6 += E; \ + E = W * d; S5 += E; \ + E = W * e; S4 += E; \ + E = W * f; S3 += E; \ + E = W * g; S2 += E; \ + E = W * h; S1 += E; \ + a = lp[K]; \ + E = W * a; S0 += E + +# define STEP_A(K) STEP(K, a, b, c, d, e, f, g, h) +# define STEP_B(K) STEP(K, b, c, d, e, f, g, h, a) +# define STEP_C(K) STEP(K, c, d, e, f, g, h, a, b) +# define STEP_D(K) STEP(K, d, e, f, g, h, a, b, c) +# define STEP_E(K) STEP(K, e, f, g, h, a, b, c, d) +# define STEP_F(K) STEP(K, f, g, h, a, b, c, d, e) +# define STEP_G(K) STEP(K, g, h, a, b, c, d, e, f) +# define STEP_H(K) STEP(K, h, a, b, c, d, e, f, g) + + STEP_A( 0); STEP_B( 1); STEP_C( 2); STEP_D( 3); + STEP_E( 4); STEP_F( 5); STEP_G( 6); STEP_H( 7); + + STEP_A( 8); STEP_B( 9); STEP_C(10); STEP_D(11); + STEP_E(12); STEP_F(13); STEP_G(14); STEP_H(15); + + STEP_A(16); STEP_B(17); STEP_C(18); STEP_D(19); + STEP_E(20); STEP_F(21); STEP_G(22); STEP_H(23); + + STEP_A(24); STEP_B(25); STEP_C(26); STEP_D(27); + STEP_E(28); STEP_F(29); STEP_G(30); STEP_H(31); + + STEP_A(32); STEP_B(33); STEP_C(34); STEP_D(35); + STEP_E(36); STEP_F(37); STEP_G(38); STEP_H(39); + +# undef STEP_A +# undef STEP_B +# undef STEP_C +# undef STEP_D +# undef STEP_E +# undef STEP_F +# undef STEP_G +# undef STEP_H + + if (S0 > L_max) { L_max = S0; Nc = lambda; } + if (S1 > L_max) { L_max = S1; Nc = lambda + 1; } + if (S2 > L_max) { L_max = S2; Nc = lambda + 2; } + if (S3 > L_max) { L_max = S3; Nc = lambda + 3; } + if (S4 > L_max) { L_max = S4; Nc = lambda + 4; } + if (S5 > L_max) { L_max = S5; Nc = lambda + 5; } + if (S6 > L_max) { L_max = S6; Nc = lambda + 6; } + if (S7 > L_max) { L_max = S7; Nc = lambda + 7; } + if (S8 > L_max) { L_max = S8; Nc = lambda + 8; } + } + *Nc_out = Nc; + + L_max <<= 1; + + /* Rescaling of L_max + */ + assert(scal <= 100 && scal >= -100); + L_max = L_max >> (6 - scal); /* sub(6, scal) */ + + assert( Nc <= 120 && Nc >= 40); + + /* Compute the power of the reconstructed short term residual + * signal dp[..] + */ + L_power = 0; + for (k = 0; k <= 39; k++) { + + register longword L_temp; + + L_temp = SASR_W( dp[k - Nc], 3 ); + L_power += L_temp * L_temp; + } + L_power <<= 1; /* from L_MULT */ + + /* Normalization of L_max and L_power + */ + + if (L_max <= 0) { + *bc_out = 0; + return; + } + if (L_max >= L_power) { + *bc_out = 3; + return; + } + + temp = gsm_norm( L_power ); + + R = SASR_L ( L_max << temp, 16 ); + S = SASR_L ( L_power << temp, 16 ); + + /* Coding of the LTP gain + */ + + /* Table 4.3a must be used to obtain the level DLB[i] for the + * quantization of the LTP gain b to get the coded version bc. + */ + for (bc = 0; bc <= 2; bc++) if (R <= gsm_mult(S, gsm_DLB[bc])) break; + *bc_out = bc; +} + +#ifdef FAST +#ifdef LTP_CUT + +static void Cut_Fast_Calculation_of_the_LTP_parameters ( + struct gsm_state * st, /* IN */ + register word * d, /* [0..39] IN */ + register word * dp, /* [-120..-1] IN */ + word * bc_out, /* OUT */ + word * Nc_out /* OUT */ +) +{ + register int k, lambda; + register float wt_float; + word Nc, bc; + word wt_max, best_k, ltp_cut; + + float dp_float_base[120], * dp_float = dp_float_base + 120; + + register float L_result, L_max, L_power; + + wt_max = 0; + + for (k = 0; k < 40; ++k) { + if ( d[k] > wt_max) wt_max = d[best_k = k]; + else if (-d[k] > wt_max) wt_max = -d[best_k = k]; + } + + assert(wt_max >= 0); + wt_float = (float)wt_max; + + for (k = -120; k < 0; ++k) dp_float[k] = (float)dp[k]; + + /* Search for the maximum cross-correlation and coding of the LTP lag + */ + L_max = 0; + Nc = 40; /* index for the maximum cross-correlation */ + + for (lambda = 40; lambda <= 120; lambda++) { + L_result = wt_float * dp_float[best_k - lambda]; + if (L_result > L_max) { + Nc = lambda; + L_max = L_result; + } + } + + *Nc_out = Nc; + if (L_max <= 0.) { + *bc_out = 0; + return; + } + + /* Compute the power of the reconstructed short term residual + * signal dp[..] + */ + dp_float -= Nc; + L_power = 0; + for (k = 0; k < 40; ++k) { + register float f = dp_float[k]; + L_power += f * f; + } + + if (L_max >= L_power) { + *bc_out = 3; + return; + } + + /* Coding of the LTP gain + * Table 4.3a must be used to obtain the level DLB[i] for the + * quantization of the LTP gain b to get the coded version bc. + */ + lambda = L_max / L_power * 32768.; + for (bc = 0; bc <= 2; ++bc) if (lambda <= gsm_DLB[bc]) break; + *bc_out = bc; +} + +#endif /* LTP_CUT */ + +static void Fast_Calculation_of_the_LTP_parameters ( + register word * din, /* [0..39] IN */ + register word * dp, /* [-120..-1] IN */ + word * bc_out, /* OUT */ + word * Nc_out /* OUT */ +) +{ + register int k, lambda; + word Nc, bc; + + float wt_float[40]; + float dp_float_base[120], * dp_float = dp_float_base + 120; + + register float L_max, L_power; + + for (k = 0; k < 40; ++k) wt_float[k] = (float) din [k] ; + for (k = -120; k < 0; ++k) dp_float[k] = (float) dp [k] ; + + /* Search for the maximum cross-correlation and coding of the LTP lag + */ + L_max = 0; + Nc = 40; /* index for the maximum cross-correlation */ + + for (lambda = 40; lambda <= 120; lambda += 9) { + + /* Calculate L_result for l = lambda .. lambda + 9. + */ + register float *lp = dp_float - lambda; + + register float W; + register float a = lp[-8], b = lp[-7], c = lp[-6], + d = lp[-5], e = lp[-4], f = lp[-3], + g = lp[-2], h = lp[-1]; + register float E; + register float S0 = 0, S1 = 0, S2 = 0, S3 = 0, S4 = 0, + S5 = 0, S6 = 0, S7 = 0, S8 = 0; + +# undef STEP +# define STEP(K, a, b, c, d, e, f, g, h) \ + W = wt_float[K]; \ + E = W * a; S8 += E; \ + E = W * b; S7 += E; \ + E = W * c; S6 += E; \ + E = W * d; S5 += E; \ + E = W * e; S4 += E; \ + E = W * f; S3 += E; \ + E = W * g; S2 += E; \ + E = W * h; S1 += E; \ + a = lp[K]; \ + E = W * a; S0 += E + +# define STEP_A(K) STEP(K, a, b, c, d, e, f, g, h) +# define STEP_B(K) STEP(K, b, c, d, e, f, g, h, a) +# define STEP_C(K) STEP(K, c, d, e, f, g, h, a, b) +# define STEP_D(K) STEP(K, d, e, f, g, h, a, b, c) +# define STEP_E(K) STEP(K, e, f, g, h, a, b, c, d) +# define STEP_F(K) STEP(K, f, g, h, a, b, c, d, e) +# define STEP_G(K) STEP(K, g, h, a, b, c, d, e, f) +# define STEP_H(K) STEP(K, h, a, b, c, d, e, f, g) + + STEP_A( 0); STEP_B( 1); STEP_C( 2); STEP_D( 3); + STEP_E( 4); STEP_F( 5); STEP_G( 6); STEP_H( 7); + + STEP_A( 8); STEP_B( 9); STEP_C(10); STEP_D(11); + STEP_E(12); STEP_F(13); STEP_G(14); STEP_H(15); + + STEP_A(16); STEP_B(17); STEP_C(18); STEP_D(19); + STEP_E(20); STEP_F(21); STEP_G(22); STEP_H(23); + + STEP_A(24); STEP_B(25); STEP_C(26); STEP_D(27); + STEP_E(28); STEP_F(29); STEP_G(30); STEP_H(31); + + STEP_A(32); STEP_B(33); STEP_C(34); STEP_D(35); + STEP_E(36); STEP_F(37); STEP_G(38); STEP_H(39); + + if (S0 > L_max) { L_max = S0; Nc = lambda; } + if (S1 > L_max) { L_max = S1; Nc = lambda + 1; } + if (S2 > L_max) { L_max = S2; Nc = lambda + 2; } + if (S3 > L_max) { L_max = S3; Nc = lambda + 3; } + if (S4 > L_max) { L_max = S4; Nc = lambda + 4; } + if (S5 > L_max) { L_max = S5; Nc = lambda + 5; } + if (S6 > L_max) { L_max = S6; Nc = lambda + 6; } + if (S7 > L_max) { L_max = S7; Nc = lambda + 7; } + if (S8 > L_max) { L_max = S8; Nc = lambda + 8; } + } + *Nc_out = Nc; + + if (L_max <= 0.) { + *bc_out = 0; + return; + } + + /* Compute the power of the reconstructed short term residual + * signal dp[..] + */ + dp_float -= Nc; + L_power = 0; + for (k = 0; k < 40; ++k) { + register float f = dp_float[k]; + L_power += f * f; + } + + if (L_max >= L_power) { + *bc_out = 3; + return; + } + + /* Coding of the LTP gain + * Table 4.3a must be used to obtain the level DLB[i] for the + * quantization of the LTP gain b to get the coded version bc. + */ + lambda = L_max / L_power * 32768.; + for (bc = 0; bc <= 2; ++bc) if (lambda <= gsm_DLB[bc]) break; + *bc_out = bc; +} + +#endif /* FAST */ +#endif /* USE_FLOAT_MUL */ + + +/* 4.2.12 */ + +static void Long_term_analysis_filtering ( + word bc, /* IN */ + word Nc, /* IN */ + register word * dp, /* previous d [-120..-1] IN */ + register word * d, /* d [0..39] IN */ + register word * dpp, /* estimate [0..39] OUT */ + register word * e /* long term res. signal [0..39] OUT */ +) +/* + * In this part, we have to decode the bc parameter to compute + * the samples of the estimate dpp[0..39]. The decoding of bc needs the + * use of table 4.3b. The long term residual signal e[0..39] + * is then calculated to be fed to the RPE encoding section. + */ +{ + register int k; + +# undef STEP +# define STEP(BP) \ + for (k = 0; k <= 39; k++) { \ + dpp[k] = GSM_MULT_R( BP, dp[k - Nc]); \ + e[k] = GSM_SUB( d[k], dpp[k] ); \ + } + + switch (bc) { + case 0: STEP( 3277 ); break; + case 1: STEP( 11469 ); break; + case 2: STEP( 21299 ); break; + case 3: STEP( 32767 ); break; + } +} + +void Gsm_Long_Term_Predictor ( /* 4x for 160 samples */ + + struct gsm_state * S, + + word * d, /* [0..39] residual signal IN */ + word * dp, /* [-120..-1] d' IN */ + + word * e, /* [0..39] OUT */ + word * dpp, /* [0..39] OUT */ + word * Nc, /* correlation lag OUT */ + word * bc /* gain factor OUT */ +) +{ + assert( d ); assert( dp ); assert( e ); + assert( dpp); assert( Nc ); assert( bc ); + +#if defined(FAST) && defined(USE_FLOAT_MUL) + if (S->fast) +#if defined (LTP_CUT) + if (S->ltp_cut) + Cut_Fast_Calculation_of_the_LTP_parameters(S, + d, dp, bc, Nc); + else +#endif /* LTP_CUT */ + Fast_Calculation_of_the_LTP_parameters(d, dp, bc, Nc ); + else +#endif /* FAST & USE_FLOAT_MUL */ +#ifdef LTP_CUT + if (S->ltp_cut) + Cut_Calculation_of_the_LTP_parameters(S, d, dp, bc, Nc); + else +#endif + Calculation_of_the_LTP_parameters(d, dp, bc, Nc); + + Long_term_analysis_filtering( *bc, *Nc, dp, d, dpp, e ); +} + +/* 4.3.2 */ +void Gsm_Long_Term_Synthesis_Filtering ( + struct gsm_state * S, + + word Ncr, + word bcr, + register word * erp, /* [0..39] IN */ + register word * drp /* [-120..-1] IN, [-120..40] OUT */ +) +/* + * This procedure uses the bcr and Ncr parameter to realize the + * long term synthesis filtering. The decoding of bcr needs + * table 4.3b. + */ +{ + register int k; + word brp, drpp, Nr; + + /* Check the limits of Nr. + */ + Nr = Ncr < 40 || Ncr > 120 ? S->nrp : Ncr; + S->nrp = Nr; + assert(Nr >= 40 && Nr <= 120); + + /* Decoding of the LTP gain bcr + */ + brp = gsm_QLB[ bcr ]; + + /* Computation of the reconstructed short term residual + * signal drp[0..39] + */ + assert(brp != MIN_WORD); + + for (k = 0; k <= 39; k++) { + drpp = GSM_MULT_R( brp, drp[ k - Nr ] ); + drp[k] = GSM_ADD( erp[k], drpp ); + } + + /* + * Update of the reconstructed short term residual signal + * drp[ -1..-120 ] + */ + + for (k = 0; k <= 119; k++) drp[ -120 + k ] = drp[ -80 + k ]; +} +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: b369b90d-0284-42a0-87b0-99a25bbd93ac +*/ + diff --git a/src/GSM610/lpc.c b/src/GSM610/lpc.c new file mode 100644 index 00000000..0a879f35 --- /dev/null +++ b/src/GSM610/lpc.c @@ -0,0 +1,341 @@ +/* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + +#include +#include + +#include "gsm610_priv.h" + +#include "gsm.h" + +/* + * 4.2.4 .. 4.2.7 LPC ANALYSIS SECTION + */ + +/* 4.2.4 */ + + +static void Autocorrelation ( + word * s, /* [0..159] IN/OUT */ + longword * L_ACF) /* [0..8] OUT */ +/* + * The goal is to compute the array L_ACF[k]. The signal s[i] must + * be scaled in order to avoid an overflow situation. + */ +{ + register int k, i; + + word temp, smax, scalauto; + +#ifdef USE_FLOAT_MUL + float float_s[160]; +#endif + + /* Dynamic scaling of the array s[0..159] + */ + + /* Search for the maximum. + */ + smax = 0; + for (k = 0; k <= 159; k++) { + temp = GSM_ABS( s[k] ); + if (temp > smax) smax = temp; + } + + /* Computation of the scaling factor. + */ + if (smax == 0) scalauto = 0; + else { + assert(smax > 0); + scalauto = 4 - gsm_norm( (longword)smax << 16 );/* sub(4,..) */ + } + + /* Scaling of the array s[0...159] + */ + + if (scalauto > 0) { + +# ifdef USE_FLOAT_MUL +# define SCALE(n) \ + case n: for (k = 0; k <= 159; k++) \ + float_s[k] = (float) \ + (s[k] = GSM_MULT_R(s[k], 16384 >> (n-1)));\ + break; +# else +# define SCALE(n) \ + case n: for (k = 0; k <= 159; k++) \ + s[k] = GSM_MULT_R( s[k], 16384 >> (n-1) );\ + break; +# endif /* USE_FLOAT_MUL */ + + switch (scalauto) { + SCALE(1) + SCALE(2) + SCALE(3) + SCALE(4) + } +# undef SCALE + } +# ifdef USE_FLOAT_MUL + else for (k = 0; k <= 159; k++) float_s[k] = (float) s[k]; +# endif + + /* Compute the L_ACF[..]. + */ + { +# ifdef USE_FLOAT_MUL + register float * sp = float_s; + register float sl = *sp; + +# define STEP(k) L_ACF[k] += (longword)(sl * sp[ -(k) ]); +# else + word * sp = s; + word sl = *sp; + +# define STEP(k) L_ACF[k] += ((longword)sl * sp[ -(k) ]); +# endif + +# define NEXTI sl = *++sp + + + for (k = 9; k--; L_ACF[k] = 0) ; + + STEP (0); + NEXTI; + STEP(0); STEP(1); + NEXTI; + STEP(0); STEP(1); STEP(2); + NEXTI; + STEP(0); STEP(1); STEP(2); STEP(3); + NEXTI; + STEP(0); STEP(1); STEP(2); STEP(3); STEP(4); + NEXTI; + STEP(0); STEP(1); STEP(2); STEP(3); STEP(4); STEP(5); + NEXTI; + STEP(0); STEP(1); STEP(2); STEP(3); STEP(4); STEP(5); STEP(6); + NEXTI; + STEP(0); STEP(1); STEP(2); STEP(3); STEP(4); STEP(5); STEP(6); STEP(7); + + for (i = 8; i <= 159; i++) { + + NEXTI; + + STEP(0); + STEP(1); STEP(2); STEP(3); STEP(4); + STEP(5); STEP(6); STEP(7); STEP(8); + } + + for (k = 9; k--; L_ACF[k] <<= 1) ; + + } + /* Rescaling of the array s[0..159] + */ + if (scalauto > 0) { + assert(scalauto <= 4); + for (k = 160; k--; *s++ <<= scalauto) ; + } +} + +#if defined(USE_FLOAT_MUL) && defined(FAST) + +static void Fast_Autocorrelation ( + word * s, /* [0..159] IN/OUT */ + longword * L_ACF) /* [0..8] OUT */ +{ + register int k, i; + float f_L_ACF[9]; + float scale; + + float s_f[160]; + register float *sf = s_f; + + for (i = 0; i < 160; ++i) sf[i] = s[i]; + for (k = 0; k <= 8; k++) { + register float L_temp2 = 0; + register float *sfl = sf - k; + for (i = k; i < 160; ++i) L_temp2 += sf[i] * sfl[i]; + f_L_ACF[k] = L_temp2; + } + scale = MAX_LONGWORD / f_L_ACF[0]; + + for (k = 0; k <= 8; k++) { + L_ACF[k] = f_L_ACF[k] * scale; + } +} +#endif /* defined (USE_FLOAT_MUL) && defined (FAST) */ + +/* 4.2.5 */ + +static void Reflection_coefficients ( + longword * L_ACF, /* 0...8 IN */ + register word * r /* 0...7 OUT */ +) +{ + register int i, m, n; + register word temp; + word ACF[9]; /* 0..8 */ + word P[ 9]; /* 0..8 */ + word K[ 9]; /* 2..8 */ + + /* Schur recursion with 16 bits arithmetic. + */ + + if (L_ACF[0] == 0) { + for (i = 8; i--; *r++ = 0) ; + return; + } + + assert( L_ACF[0] != 0 ); + temp = gsm_norm( L_ACF[0] ); + + assert(temp >= 0 && temp < 32); + + /* ? overflow ? */ + for (i = 0; i <= 8; i++) ACF[i] = SASR_L( L_ACF[i] << temp, 16 ); + + /* Initialize array P[..] and K[..] for the recursion. + */ + + for (i = 1; i <= 7; i++) K[ i ] = ACF[ i ]; + for (i = 0; i <= 8; i++) P[ i ] = ACF[ i ]; + + /* Compute reflection coefficients + */ + for (n = 1; n <= 8; n++, r++) { + + temp = P[1]; + temp = GSM_ABS(temp); + if (P[0] < temp) { + for (i = n; i <= 8; i++) *r++ = 0; + return; + } + + *r = gsm_div( temp, P[0] ); + + assert(*r >= 0); + if (P[1] > 0) *r = -*r; /* r[n] = sub(0, r[n]) */ + assert (*r != MIN_WORD); + if (n == 8) return; + + /* Schur recursion + */ + temp = GSM_MULT_R( P[1], *r ); + P[0] = GSM_ADD( P[0], temp ); + + for (m = 1; m <= 8 - n; m++) { + temp = GSM_MULT_R( K[ m ], *r ); + P[m] = GSM_ADD( P[ m+1 ], temp ); + + temp = GSM_MULT_R( P[ m+1 ], *r ); + K[m] = GSM_ADD( K[ m ], temp ); + } + } +} + +/* 4.2.6 */ + +static void Transformation_to_Log_Area_Ratios ( + register word * r /* 0..7 IN/OUT */ +) +/* + * The following scaling for r[..] and LAR[..] has been used: + * + * r[..] = integer( real_r[..]*32768. ); -1 <= real_r < 1. + * LAR[..] = integer( real_LAR[..] * 16384 ); + * with -1.625 <= real_LAR <= 1.625 + */ +{ + register word temp; + register int i; + + + /* Computation of the LAR[0..7] from the r[0..7] + */ + for (i = 1; i <= 8; i++, r++) { + + temp = *r; + temp = GSM_ABS(temp); + assert(temp >= 0); + + if (temp < 22118) { + temp >>= 1; + } else if (temp < 31130) { + assert( temp >= 11059 ); + temp -= 11059; + } else { + assert( temp >= 26112 ); + temp -= 26112; + temp <<= 2; + } + + *r = *r < 0 ? -temp : temp; + assert( *r != MIN_WORD ); + } +} + +/* 4.2.7 */ + +static void Quantization_and_coding ( + register word * LAR /* [0..7] IN/OUT */ +) +{ + register word temp; + + /* This procedure needs four tables; the following equations + * give the optimum scaling for the constants: + * + * A[0..7] = integer( real_A[0..7] * 1024 ) + * B[0..7] = integer( real_B[0..7] * 512 ) + * MAC[0..7] = maximum of the LARc[0..7] + * MIC[0..7] = minimum of the LARc[0..7] + */ + +# undef STEP +# define STEP( A, B, MAC, MIC ) \ + temp = GSM_MULT( A, *LAR ); \ + temp = GSM_ADD( temp, B ); \ + temp = GSM_ADD( temp, 256 ); \ + temp = SASR_W( temp, 9 ); \ + *LAR = temp>MAC ? MAC - MIC : (tempfast) Fast_Autocorrelation (s, L_ACF ); + else +#endif + Autocorrelation (s, L_ACF ); + Reflection_coefficients (L_ACF, LARc ); + Transformation_to_Log_Area_Ratios (LARc); + Quantization_and_coding (LARc); +} +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 63146664-a002-4e1e-8b7b-f0cc8a6a53da +*/ + diff --git a/src/GSM610/preprocess.c b/src/GSM610/preprocess.c new file mode 100644 index 00000000..d1b473d6 --- /dev/null +++ b/src/GSM610/preprocess.c @@ -0,0 +1,115 @@ +/* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + +#include +#include + +#include "gsm610_priv.h" + +#include "gsm.h" + +/* 4.2.0 .. 4.2.3 PREPROCESSING SECTION + * + * After A-law to linear conversion (or directly from the + * Ato D converter) the following scaling is assumed for + * input to the RPE-LTP algorithm: + * + * in: 0.1.....................12 + * S.v.v.v.v.v.v.v.v.v.v.v.v.*.*.* + * + * Where S is the sign bit, v a valid bit, and * a "don't care" bit. + * The original signal is called sop[..] + * + * out: 0.1................... 12 + * S.S.v.v.v.v.v.v.v.v.v.v.v.v.0.0 + */ + + +void Gsm_Preprocess ( + struct gsm_state * S, + word * s, + word * so ) /* [0..159] IN/OUT */ +{ + + word z1 = S->z1; + longword L_z2 = S->L_z2; + word mp = S->mp; + + word s1; + longword L_s2; + + longword L_temp; + + word msp, lsp; + word SO; + + register int k = 160; + + while (k--) { + + /* 4.2.1 Downscaling of the input signal + */ + SO = SASR_W( *s, 3 ) << 2; + s++; + + assert (SO >= -0x4000); /* downscaled by */ + assert (SO <= 0x3FFC); /* previous routine. */ + + + /* 4.2.2 Offset compensation + * + * This part implements a high-pass filter and requires extended + * arithmetic precision for the recursive part of this filter. + * The input of this procedure is the array so[0...159] and the + * output the array sof[ 0...159 ]. + */ + /* Compute the non-recursive part + */ + + s1 = SO - z1; /* s1 = gsm_sub( *so, z1 ); */ + z1 = SO; + + assert(s1 != MIN_WORD); + + /* Compute the recursive part + */ + L_s2 = s1; + L_s2 <<= 15; + + /* Execution of a 31 bv 16 bits multiplication + */ + + msp = SASR_L( L_z2, 15 ); + lsp = L_z2-((longword)msp<<15); /* gsm_L_sub(L_z2,(msp<<15)); */ + + L_s2 += GSM_MULT_R( lsp, 32735 ); + L_temp = (longword)msp * 32735; /* GSM_L_MULT(msp,32735) >> 1;*/ + L_z2 = GSM_L_ADD( L_temp, L_s2 ); + + /* Compute sof[k] with rounding + */ + L_temp = GSM_L_ADD( L_z2, 16384 ); + + /* 4.2.3 Preemphasis + */ + + msp = GSM_MULT_R( mp, -28180 ); + mp = SASR_L( L_temp, 15 ); + *so++ = GSM_ADD( mp, msp ); + } + + S->z1 = z1; + S->L_z2 = L_z2; + S->mp = mp; +} +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: b760b0d9-3a05-4da3-9dc9-441ffb905d87 +*/ + diff --git a/src/GSM610/rpe.c b/src/GSM610/rpe.c new file mode 100644 index 00000000..1d91f38b --- /dev/null +++ b/src/GSM610/rpe.c @@ -0,0 +1,490 @@ +/* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + +#include +#include + +#include "gsm610_priv.h" + +#include "gsm.h" + +/* 4.2.13 .. 4.2.17 RPE ENCODING SECTION + */ + +/* 4.2.13 */ + +static void Weighting_filter ( + register word * e, /* signal [-5..0.39.44] IN */ + word * x /* signal [0..39] OUT */ +) +/* + * The coefficients of the weighting filter are stored in a table + * (see table 4.4). The following scaling is used: + * + * H[0..10] = integer( real_H[ 0..10] * 8192 ); + */ +{ + /* word wt[ 50 ]; */ + + register longword L_result; + register int k /* , i */ ; + + /* Initialization of a temporary working array wt[0...49] + */ + + /* for (k = 0; k <= 4; k++) wt[k] = 0; + * for (k = 5; k <= 44; k++) wt[k] = *e++; + * for (k = 45; k <= 49; k++) wt[k] = 0; + * + * (e[-5..-1] and e[40..44] are allocated by the caller, + * are initially zero and are not written anywhere.) + */ + e -= 5; + + /* Compute the signal x[0..39] + */ + for (k = 0; k <= 39; k++) { + + L_result = 8192 >> 1; + + /* for (i = 0; i <= 10; i++) { + * L_temp = GSM_L_MULT( wt[k+i], gsm_H[i] ); + * L_result = GSM_L_ADD( L_result, L_temp ); + * } + */ + +#undef STEP +#define STEP( i, H ) (e[ k + i ] * (longword)H) + + /* Every one of these multiplications is done twice -- + * but I don't see an elegant way to optimize this. + * Do you? + */ + +#ifdef STUPID_COMPILER + L_result += STEP( 0, -134 ) ; + L_result += STEP( 1, -374 ) ; + /* + STEP( 2, 0 ) */ + L_result += STEP( 3, 2054 ) ; + L_result += STEP( 4, 5741 ) ; + L_result += STEP( 5, 8192 ) ; + L_result += STEP( 6, 5741 ) ; + L_result += STEP( 7, 2054 ) ; + /* + STEP( 8, 0 ) */ + L_result += STEP( 9, -374 ) ; + L_result += STEP( 10, -134 ) ; +#else + L_result += + STEP( 0, -134 ) + + STEP( 1, -374 ) + /* + STEP( 2, 0 ) */ + + STEP( 3, 2054 ) + + STEP( 4, 5741 ) + + STEP( 5, 8192 ) + + STEP( 6, 5741 ) + + STEP( 7, 2054 ) + /* + STEP( 8, 0 ) */ + + STEP( 9, -374 ) + + STEP(10, -134 ) + ; +#endif + + /* L_result = GSM_L_ADD( L_result, L_result ); (* scaling(x2) *) + * L_result = GSM_L_ADD( L_result, L_result ); (* scaling(x4) *) + * + * x[k] = SASR( L_result, 16 ); + */ + + /* 2 adds vs. >>16 => 14, minus one shift to compensate for + * those we lost when replacing L_MULT by '*'. + */ + + L_result = SASR_L( L_result, 13 ); + x[k] = ( L_result < MIN_WORD ? MIN_WORD + : (L_result > MAX_WORD ? MAX_WORD : L_result )); + } +} + +/* 4.2.14 */ + +static void RPE_grid_selection ( + word * x, /* [0..39] IN */ + word * xM, /* [0..12] OUT */ + word * Mc_out /* OUT */ +) +/* + * The signal x[0..39] is used to select the RPE grid which is + * represented by Mc. + */ +{ + /* register word temp1; */ + register int /* m, */ i; + register longword L_result, L_temp; + longword EM; /* xxx should be L_EM? */ + word Mc; + + longword L_common_0_3; + + EM = 0; + Mc = 0; + + /* for (m = 0; m <= 3; m++) { + * L_result = 0; + * + * + * for (i = 0; i <= 12; i++) { + * + * temp1 = SASR_W( x[m + 3*i], 2 ); + * + * assert(temp1 != MIN_WORD); + * + * L_temp = GSM_L_MULT( temp1, temp1 ); + * L_result = GSM_L_ADD( L_temp, L_result ); + * } + * + * if (L_result > EM) { + * Mc = m; + * EM = L_result; + * } + * } + */ + +#undef STEP +#define STEP( m, i ) L_temp = SASR_W( x[m + 3 * i], 2 ); \ + L_result += L_temp * L_temp; + + /* common part of 0 and 3 */ + + L_result = 0; + STEP( 0, 1 ); STEP( 0, 2 ); STEP( 0, 3 ); STEP( 0, 4 ); + STEP( 0, 5 ); STEP( 0, 6 ); STEP( 0, 7 ); STEP( 0, 8 ); + STEP( 0, 9 ); STEP( 0, 10); STEP( 0, 11); STEP( 0, 12); + L_common_0_3 = L_result; + + /* i = 0 */ + + STEP( 0, 0 ); + L_result <<= 1; /* implicit in L_MULT */ + EM = L_result; + + /* i = 1 */ + + L_result = 0; + STEP( 1, 0 ); + STEP( 1, 1 ); STEP( 1, 2 ); STEP( 1, 3 ); STEP( 1, 4 ); + STEP( 1, 5 ); STEP( 1, 6 ); STEP( 1, 7 ); STEP( 1, 8 ); + STEP( 1, 9 ); STEP( 1, 10); STEP( 1, 11); STEP( 1, 12); + L_result <<= 1; + if (L_result > EM) { + Mc = 1; + EM = L_result; + } + + /* i = 2 */ + + L_result = 0; + STEP( 2, 0 ); + STEP( 2, 1 ); STEP( 2, 2 ); STEP( 2, 3 ); STEP( 2, 4 ); + STEP( 2, 5 ); STEP( 2, 6 ); STEP( 2, 7 ); STEP( 2, 8 ); + STEP( 2, 9 ); STEP( 2, 10); STEP( 2, 11); STEP( 2, 12); + L_result <<= 1; + if (L_result > EM) { + Mc = 2; + EM = L_result; + } + + /* i = 3 */ + + L_result = L_common_0_3; + STEP( 3, 12 ); + L_result <<= 1; + if (L_result > EM) { + Mc = 3; + EM = L_result; + } + + /**/ + + /* Down-sampling by a factor 3 to get the selected xM[0..12] + * RPE sequence. + */ + for (i = 0; i <= 12; i ++) xM[i] = x[Mc + 3*i]; + *Mc_out = Mc; +} + +/* 4.12.15 */ + +static void APCM_quantization_xmaxc_to_exp_mant ( + word xmaxc, /* IN */ + word * expon_out, /* OUT */ + word * mant_out ) /* OUT */ +{ + word expon, mant; + + /* Compute expononent and mantissa of the decoded version of xmaxc + */ + + expon = 0; + if (xmaxc > 15) expon = SASR_W(xmaxc, 3) - 1; + mant = xmaxc - (expon << 3); + + if (mant == 0) { + expon = -4; + mant = 7; + } + else { + while (mant <= 7) { + mant = mant << 1 | 1; + expon--; + } + mant -= 8; + } + + assert( expon >= -4 && expon <= 6 ); + assert( mant >= 0 && mant <= 7 ); + + *expon_out = expon; + *mant_out = mant; +} + +static void APCM_quantization ( + word * xM, /* [0..12] IN */ + word * xMc, /* [0..12] OUT */ + word * mant_out, /* OUT */ + word * expon_out, /* OUT */ + word * xmaxc_out /* OUT */ +) +{ + int i, itest; + + word xmax, xmaxc, temp, temp1, temp2; + word expon, mant; + + + /* Find the maximum absolute value xmax of xM[0..12]. + */ + + xmax = 0; + for (i = 0; i <= 12; i++) { + temp = xM[i]; + temp = GSM_ABS(temp); + if (temp > xmax) xmax = temp; + } + + /* Qantizing and coding of xmax to get xmaxc. + */ + + expon = 0; + temp = SASR_W( xmax, 9 ); + itest = 0; + + for (i = 0; i <= 5; i++) { + + itest |= (temp <= 0); + temp = SASR_W( temp, 1 ); + + assert(expon <= 5); + if (itest == 0) expon++; /* expon = add (expon, 1) */ + } + + assert(expon <= 6 && expon >= 0); + temp = expon + 5; + + assert(temp <= 11 && temp >= 0); + xmaxc = gsm_add( SASR_W(xmax, temp), (word) (expon << 3) ); + + /* Quantizing and coding of the xM[0..12] RPE sequence + * to get the xMc[0..12] + */ + + APCM_quantization_xmaxc_to_exp_mant( xmaxc, &expon, &mant ); + + /* This computation uses the fact that the decoded version of xmaxc + * can be calculated by using the expononent and the mantissa part of + * xmaxc (logarithmic table). + * So, this method avoids any division and uses only a scaling + * of the RPE samples by a function of the expononent. A direct + * multiplication by the inverse of the mantissa (NRFAC[0..7] + * found in table 4.5) gives the 3 bit coded version xMc[0..12] + * of the RPE samples. + */ + + + /* Direct computation of xMc[0..12] using table 4.5 + */ + + assert( expon <= 4096 && expon >= -4096); + assert( mant >= 0 && mant <= 7 ); + + temp1 = 6 - expon; /* normalization by the expononent */ + temp2 = gsm_NRFAC[ mant ]; /* inverse mantissa */ + + for (i = 0; i <= 12; i++) { + + assert(temp1 >= 0 && temp1 < 16); + + temp = xM[i] << temp1; + temp = GSM_MULT( temp, temp2 ); + temp = SASR_W(temp, 12); + xMc[i] = temp + 4; /* see note below */ + } + + /* NOTE: This equation is used to make all the xMc[i] positive. + */ + + *mant_out = mant; + *expon_out = expon; + *xmaxc_out = xmaxc; +} + +/* 4.2.16 */ + +static void APCM_inverse_quantization ( + register word * xMc, /* [0..12] IN */ + word mant, + word expon, + register word * xMp) /* [0..12] OUT */ +/* + * This part is for decoding the RPE sequence of coded xMc[0..12] + * samples to obtain the xMp[0..12] array. Table 4.6 is used to get + * the mantissa of xmaxc (FAC[0..7]). + */ +{ + int i; + word temp, temp1, temp2, temp3; + + assert( mant >= 0 && mant <= 7 ); + + temp1 = gsm_FAC[ mant ]; /* see 4.2-15 for mant */ + temp2 = gsm_sub( 6, expon ); /* see 4.2-15 for exp */ + temp3 = gsm_asl( 1, gsm_sub( temp2, 1 )); + + for (i = 13; i--;) { + + assert( *xMc <= 7 && *xMc >= 0 ); /* 3 bit unsigned */ + + /* temp = gsm_sub( *xMc++ << 1, 7 ); */ + temp = (*xMc++ << 1) - 7; /* restore sign */ + assert( temp <= 7 && temp >= -7 ); /* 4 bit signed */ + + temp <<= 12; /* 16 bit signed */ + temp = GSM_MULT_R( temp1, temp ); + temp = GSM_ADD( temp, temp3 ); + *xMp++ = gsm_asr( temp, temp2 ); + } +} + +/* 4.2.17 */ + +static void RPE_grid_positioning ( + word Mc, /* grid position IN */ + register word * xMp, /* [0..12] IN */ + register word * ep /* [0..39] OUT */ +) +/* + * This procedure computes the reconstructed long term residual signal + * ep[0..39] for the LTP analysis filter. The inputs are the Mc + * which is the grid position selection and the xMp[0..12] decoded + * RPE samples which are upsampled by a factor of 3 by inserting zero + * values. + */ +{ + int i = 13; + + assert(0 <= Mc && Mc <= 3); + + switch (Mc) { + case 3: *ep++ = 0; + case 2: do { + *ep++ = 0; + case 1: *ep++ = 0; + case 0: *ep++ = *xMp++; + } while (--i); + } + while (++Mc < 4) *ep++ = 0; + + /* + + int i, k; + for (k = 0; k <= 39; k++) ep[k] = 0; + for (i = 0; i <= 12; i++) { + ep[ Mc + (3*i) ] = xMp[i]; + } + */ +} + +/* 4.2.18 */ + +/* This procedure adds the reconstructed long term residual signal + * ep[0..39] to the estimated signal dpp[0..39] from the long term + * analysis filter to compute the reconstructed short term residual + * signal dp[-40..-1]; also the reconstructed short term residual + * array dp[-120..-41] is updated. + */ + +#if 0 /* Has been inlined in code.c */ +void Gsm_Update_of_reconstructed_short_time_residual_signal ( + word * dpp, /* [0...39] IN */ + word * ep, /* [0...39] IN */ + word * dp) /* [-120...-1] IN/OUT */ +{ + int k; + + for (k = 0; k <= 79; k++) + dp[ -120 + k ] = dp[ -80 + k ]; + + for (k = 0; k <= 39; k++) + dp[ -40 + k ] = gsm_add( ep[k], dpp[k] ); +} +#endif /* Has been inlined in code.c */ + +void Gsm_RPE_Encoding ( + /*-struct gsm_state * S,-*/ + + word * e, /* -5..-1][0..39][40..44 IN/OUT */ + word * xmaxc, /* OUT */ + word * Mc, /* OUT */ + word * xMc) /* [0..12] OUT */ +{ + word x[40]; + word xM[13], xMp[13]; + word mant, expon; + + Weighting_filter(e, x); + RPE_grid_selection(x, xM, Mc); + + APCM_quantization( xM, xMc, &mant, &expon, xmaxc); + APCM_inverse_quantization( xMc, mant, expon, xMp); + + RPE_grid_positioning( *Mc, xMp, e ); + +} + +void Gsm_RPE_Decoding ( + /*-struct gsm_state * S,-*/ + + word xmaxcr, + word Mcr, + word * xMcr, /* [0..12], 3 bits IN */ + word * erp /* [0..39] OUT */ +) +{ + word expon, mant; + word xMp[ 13 ]; + + APCM_quantization_xmaxc_to_exp_mant( xmaxcr, &expon, &mant ); + APCM_inverse_quantization( xMcr, mant, expon, xMp ); + RPE_grid_positioning( Mcr, xMp, erp ); + +} +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 82005b9e-1560-4e94-9ddb-00cb14867295 +*/ + diff --git a/src/GSM610/short_term.c b/src/GSM610/short_term.c new file mode 100644 index 00000000..0174b052 --- /dev/null +++ b/src/GSM610/short_term.c @@ -0,0 +1,427 @@ +/* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + +#include +#include + +#include "gsm610_priv.h" + +#include "gsm.h" + +/* + * SHORT TERM ANALYSIS FILTERING SECTION + */ + +/* 4.2.8 */ + +static void Decoding_of_the_coded_Log_Area_Ratios ( + word * LARc, /* coded log area ratio [0..7] IN */ + word * LARpp) /* out: decoded .. */ +{ + register word temp1 /* , temp2 */; + + /* This procedure requires for efficient implementation + * two tables. + * + * INVA[1..8] = integer( (32768 * 8) / real_A[1..8]) + * MIC[1..8] = minimum value of the LARc[1..8] + */ + + /* Compute the LARpp[1..8] + */ + + /* for (i = 1; i <= 8; i++, B++, MIC++, INVA++, LARc++, LARpp++) { + * + * temp1 = GSM_ADD( *LARc, *MIC ) << 10; + * temp2 = *B << 1; + * temp1 = GSM_SUB( temp1, temp2 ); + * + * assert(*INVA != MIN_WORD); + * + * temp1 = GSM_MULT_R( *INVA, temp1 ); + * *LARpp = GSM_ADD( temp1, temp1 ); + * } + */ + +#undef STEP +#define STEP( B, MIC, INVA ) \ + temp1 = GSM_ADD( *LARc++, MIC ) << 10; \ + temp1 = GSM_SUB( temp1, B << 1 ); \ + temp1 = GSM_MULT_R( INVA, temp1 ); \ + *LARpp++ = GSM_ADD( temp1, temp1 ); + + STEP( 0, -32, 13107 ); + STEP( 0, -32, 13107 ); + STEP( 2048, -16, 13107 ); + STEP( -2560, -16, 13107 ); + + STEP( 94, -8, 19223 ); + STEP( -1792, -8, 17476 ); + STEP( -341, -4, 31454 ); + STEP( -1144, -4, 29708 ); + + /* NOTE: the addition of *MIC is used to restore + * the sign of *LARc. + */ +} + +/* 4.2.9 */ +/* Computation of the quantized reflection coefficients + */ + +/* 4.2.9.1 Interpolation of the LARpp[1..8] to get the LARp[1..8] + */ + +/* + * Within each frame of 160 analyzed speech samples the short term + * analysis and synthesis filters operate with four different sets of + * coefficients, derived from the previous set of decoded LARs(LARpp(j-1)) + * and the actual set of decoded LARs (LARpp(j)) + * + * (Initial value: LARpp(j-1)[1..8] = 0.) + */ + +static void Coefficients_0_12 ( + register word * LARpp_j_1, + register word * LARpp_j, + register word * LARp) +{ + register int i; + + for (i = 1; i <= 8; i++, LARp++, LARpp_j_1++, LARpp_j++) { + *LARp = GSM_ADD( SASR_W( *LARpp_j_1, 2 ), SASR_W( *LARpp_j, 2 )); + *LARp = GSM_ADD( *LARp, SASR_W( *LARpp_j_1, 1)); + } +} + +static void Coefficients_13_26 ( + register word * LARpp_j_1, + register word * LARpp_j, + register word * LARp) +{ + register int i; + for (i = 1; i <= 8; i++, LARpp_j_1++, LARpp_j++, LARp++) { + *LARp = GSM_ADD( SASR_W( *LARpp_j_1, 1), SASR_W( *LARpp_j, 1 )); + } +} + +static void Coefficients_27_39 ( + register word * LARpp_j_1, + register word * LARpp_j, + register word * LARp) +{ + register int i; + + for (i = 1; i <= 8; i++, LARpp_j_1++, LARpp_j++, LARp++) { + *LARp = GSM_ADD( SASR_W( *LARpp_j_1, 2 ), SASR_W( *LARpp_j, 2 )); + *LARp = GSM_ADD( *LARp, SASR_W( *LARpp_j, 1 )); + } +} + + +static void Coefficients_40_159 ( + register word * LARpp_j, + register word * LARp) +{ + register int i; + + for (i = 1; i <= 8; i++, LARp++, LARpp_j++) + *LARp = *LARpp_j; +} + +/* 4.2.9.2 */ + +static void LARp_to_rp ( + register word * LARp) /* [0..7] IN/OUT */ +/* + * The input of this procedure is the interpolated LARp[0..7] array. + * The reflection coefficients, rp[i], are used in the analysis + * filter and in the synthesis filter. + */ +{ + register int i; + register word temp; + + for (i = 1; i <= 8; i++, LARp++) { + + /* temp = GSM_ABS( *LARp ); + * + * if (temp < 11059) temp <<= 1; + * else if (temp < 20070) temp += 11059; + * else temp = GSM_ADD( temp >> 2, 26112 ); + * + * *LARp = *LARp < 0 ? -temp : temp; + */ + + if (*LARp < 0) { + temp = *LARp == MIN_WORD ? MAX_WORD : -(*LARp); + *LARp = - ((temp < 11059) ? temp << 1 + : ((temp < 20070) ? temp + 11059 + : GSM_ADD( (word) (temp >> 2), (word) 26112 ))); + } else { + temp = *LARp; + *LARp = (temp < 11059) ? temp << 1 + : ((temp < 20070) ? temp + 11059 + : GSM_ADD( (word) (temp >> 2), (word) 26112 )); + } + } +} + + +/* 4.2.10 */ +static void Short_term_analysis_filtering ( + struct gsm_state * S, + register word * rp, /* [0..7] IN */ + register int k_n, /* k_end - k_start */ + register word * s /* [0..n-1] IN/OUT */ +) +/* + * This procedure computes the short term residual signal d[..] to be fed + * to the RPE-LTP loop from the s[..] signal and from the local rp[..] + * array (quantized reflection coefficients). As the call of this + * procedure can be done in many ways (see the interpolation of the LAR + * coefficient), it is assumed that the computation begins with index + * k_start (for arrays d[..] and s[..]) and stops with index k_end + * (k_start and k_end are defined in 4.2.9.1). This procedure also + * needs to keep the array u[0..7] in memory for each call. + */ +{ + register word * u = S->u; + register int i; + register word di, zzz, ui, sav, rpi; + + for (; k_n--; s++) { + + di = sav = *s; + + for (i = 0; i < 8; i++) { /* YYY */ + + ui = u[i]; + rpi = rp[i]; + u[i] = sav; + + zzz = GSM_MULT_R(rpi, di); + sav = GSM_ADD( ui, zzz); + + zzz = GSM_MULT_R(rpi, ui); + di = GSM_ADD( di, zzz ); + } + + *s = di; + } +} + +#if defined(USE_FLOAT_MUL) && defined(FAST) + +static void Fast_Short_term_analysis_filtering ( + struct gsm_state * S, + register word * rp, /* [0..7] IN */ + register int k_n, /* k_end - k_start */ + register word * s /* [0..n-1] IN/OUT */ +) +{ + register word * u = S->u; + register int i; + + float uf[8], + rpf[8]; + + register float scalef = 3.0517578125e-5; + register float sav, di, temp; + + for (i = 0; i < 8; ++i) { + uf[i] = u[i]; + rpf[i] = rp[i] * scalef; + } + for (; k_n--; s++) { + sav = di = *s; + for (i = 0; i < 8; ++i) { + register float rpfi = rpf[i]; + register float ufi = uf[i]; + + uf[i] = sav; + temp = rpfi * di + ufi; + di += rpfi * ufi; + sav = temp; + } + *s = di; + } + for (i = 0; i < 8; ++i) u[i] = uf[i]; +} +#endif /* ! (defined (USE_FLOAT_MUL) && defined (FAST)) */ + +static void Short_term_synthesis_filtering ( + struct gsm_state * S, + register word * rrp, /* [0..7] IN */ + register int k, /* k_end - k_start */ + register word * wt, /* [0..k-1] IN */ + register word * sr /* [0..k-1] OUT */ +) +{ + register word * v = S->v; + register int i; + register word sri, tmp1, tmp2; + + while (k--) { + sri = *wt++; + for (i = 8; i--;) { + + /* sri = GSM_SUB( sri, gsm_mult_r( rrp[i], v[i] ) ); + */ + tmp1 = rrp[i]; + tmp2 = v[i]; + tmp2 = ( tmp1 == MIN_WORD && tmp2 == MIN_WORD + ? MAX_WORD + : 0x0FFFF & (( (longword)tmp1 * (longword)tmp2 + + 16384) >> 15)) ; + + sri = GSM_SUB( sri, tmp2 ); + + /* v[i+1] = GSM_ADD( v[i], gsm_mult_r( rrp[i], sri ) ); + */ + tmp1 = ( tmp1 == MIN_WORD && sri == MIN_WORD + ? MAX_WORD + : 0x0FFFF & (( (longword)tmp1 * (longword)sri + + 16384) >> 15)) ; + + v[i+1] = GSM_ADD( v[i], tmp1); + } + *sr++ = v[0] = sri; + } +} + + +#if defined(FAST) && defined(USE_FLOAT_MUL) + +static void Fast_Short_term_synthesis_filtering ( + struct gsm_state * S, + register word * rrp, /* [0..7] IN */ + register int k, /* k_end - k_start */ + register word * wt, /* [0..k-1] IN */ + register word * sr /* [0..k-1] OUT */ +) +{ + register word * v = S->v; + register int i; + + float va[9], rrpa[8]; + register float scalef = 3.0517578125e-5, temp; + + for (i = 0; i < 8; ++i) { + va[i] = v[i]; + rrpa[i] = (float)rrp[i] * scalef; + } + while (k--) { + register float sri = *wt++; + for (i = 8; i--;) { + sri -= rrpa[i] * va[i]; + if (sri < -32768.) sri = -32768.; + else if (sri > 32767.) sri = 32767.; + + temp = va[i] + rrpa[i] * sri; + if (temp < -32768.) temp = -32768.; + else if (temp > 32767.) temp = 32767.; + va[i+1] = temp; + } + *sr++ = va[0] = sri; + } + for (i = 0; i < 9; ++i) v[i] = va[i]; +} + +#endif /* defined(FAST) && defined(USE_FLOAT_MUL) */ + +void Gsm_Short_Term_Analysis_Filter ( + + struct gsm_state * S, + + word * LARc, /* coded log area ratio [0..7] IN */ + word * s /* signal [0..159] IN/OUT */ +) +{ + word * LARpp_j = S->LARpp[ S->j ]; + word * LARpp_j_1 = S->LARpp[ S->j ^= 1 ]; + + word LARp[8]; + +#undef FILTER +#if defined(FAST) && defined(USE_FLOAT_MUL) +# define FILTER (* (S->fast \ + ? Fast_Short_term_analysis_filtering \ + : Short_term_analysis_filtering )) + +#else +# define FILTER Short_term_analysis_filtering +#endif + + Decoding_of_the_coded_Log_Area_Ratios( LARc, LARpp_j ); + + Coefficients_0_12( LARpp_j_1, LARpp_j, LARp ); + LARp_to_rp( LARp ); + FILTER( S, LARp, 13, s); + + Coefficients_13_26( LARpp_j_1, LARpp_j, LARp); + LARp_to_rp( LARp ); + FILTER( S, LARp, 14, s + 13); + + Coefficients_27_39( LARpp_j_1, LARpp_j, LARp); + LARp_to_rp( LARp ); + FILTER( S, LARp, 13, s + 27); + + Coefficients_40_159( LARpp_j, LARp); + LARp_to_rp( LARp ); + FILTER( S, LARp, 120, s + 40); +} + +void Gsm_Short_Term_Synthesis_Filter ( + struct gsm_state * S, + + word * LARcr, /* received log area ratios [0..7] IN */ + word * wt, /* received d [0..159] IN */ + + word * s /* signal s [0..159] OUT */ +) +{ + word * LARpp_j = S->LARpp[ S->j ]; + word * LARpp_j_1 = S->LARpp[ S->j ^=1 ]; + + word LARp[8]; + +#undef FILTER +#if defined(FAST) && defined(USE_FLOAT_MUL) + +# define FILTER (* (S->fast \ + ? Fast_Short_term_synthesis_filtering \ + : Short_term_synthesis_filtering )) +#else +# define FILTER Short_term_synthesis_filtering +#endif + + Decoding_of_the_coded_Log_Area_Ratios( LARcr, LARpp_j ); + + Coefficients_0_12( LARpp_j_1, LARpp_j, LARp ); + LARp_to_rp( LARp ); + FILTER( S, LARp, 13, wt, s ); + + Coefficients_13_26( LARpp_j_1, LARpp_j, LARp); + LARp_to_rp( LARp ); + FILTER( S, LARp, 14, wt + 13, s + 13 ); + + Coefficients_27_39( LARpp_j_1, LARpp_j, LARp); + LARp_to_rp( LARp ); + FILTER( S, LARp, 13, wt + 27, s + 27 ); + + Coefficients_40_159( LARpp_j, LARp ); + LARp_to_rp( LARp ); + FILTER(S, LARp, 120, wt + 40, s + 40); +} +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 019ac7ba-c6dd-4540-abf0-8644b6c4a633 +*/ + diff --git a/src/GSM610/table.c b/src/GSM610/table.c new file mode 100644 index 00000000..b5aa881e --- /dev/null +++ b/src/GSM610/table.c @@ -0,0 +1,69 @@ +/* + * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische + * Universitaet Berlin. See the accompanying file "COPYRIGHT" for + * details. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. + */ + +/* Most of these tables are inlined at their point of use. + */ + +/* 4.4 TABLES USED IN THE FIXED POINT IMPLEMENTATION OF THE RPE-LTP + * CODER AND DECODER + * + * (Most of them inlined, so watch out.) + */ + +#define GSM_TABLE_C +#include "gsm610_priv.h" +#include "gsm.h" + +/* Table 4.1 Quantization of the Log.-Area Ratios + */ +/* i 1 2 3 4 5 6 7 8 */ +word gsm_A[8] = {20480, 20480, 20480, 20480, 13964, 15360, 8534, 9036}; +word gsm_B[8] = { 0, 0, 2048, -2560, 94, -1792, -341, -1144}; +word gsm_MIC[8] = { -32, -32, -16, -16, -8, -8, -4, -4 }; +word gsm_MAC[8] = { 31, 31, 15, 15, 7, 7, 3, 3 }; + + +/* Table 4.2 Tabulation of 1/A[1..8] + */ +word gsm_INVA[8]={ 13107, 13107, 13107, 13107, 19223, 17476, 31454, 29708 }; + + +/* Table 4.3a Decision level of the LTP gain quantizer + */ +/* bc 0 1 2 3 */ +word gsm_DLB[4] = { 6554, 16384, 26214, 32767 }; + + +/* Table 4.3b Quantization levels of the LTP gain quantizer + */ +/* bc 0 1 2 3 */ +word gsm_QLB[4] = { 3277, 11469, 21299, 32767 }; + + +/* Table 4.4 Coefficients of the weighting filter + */ +/* i 0 1 2 3 4 5 6 7 8 9 10 */ +word gsm_H[11] = {-134, -374, 0, 2054, 5741, 8192, 5741, 2054, 0, -374, -134 }; + + +/* Table 4.5 Normalized inverse mantissa used to compute xM/xmax + */ +/* i 0 1 2 3 4 5 6 7 */ +word gsm_NRFAC[8] = { 29128, 26215, 23832, 21846, 20165, 18725, 17476, 16384 }; + + +/* Table 4.6 Normalized direct mantissa used to compute xM/xmax + */ +/* i 0 1 2 3 4 5 6 7 */ +word gsm_FAC[8] = { 18431, 20479, 22527, 24575, 26623, 28671, 30719, 32767 }; +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 8957c531-e6b0-4097-9202-da7ca42729ca +*/ + diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 00000000..24d852d5 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,121 @@ +## Process this file with automake to produce Makefile.in + +SUBDIRS = OGG FLAC GSM610 G72x + +lib_LTLIBRARIES = libsndfile.la +include_HEADERS = sndfile.hh +nodist_include_HEADERS = sndfile.h + +noinst_LTLIBRARIES = libcommon.la + +OS_SPECIFIC_CFLAGS = @OS_SPECIFIC_CFLAGS@ +OS_SPECIFIC_LINKS = @OS_SPECIFIC_LINKS@ + +EXTRA_DIST = sndfile.h.in config.h.in test_endswap.tpl test_endswap.def \ + Symbols.linux Symbols.darwin libsndfile.def cygsndfile.def \ + create_symbols_file.py binheader_writef_check.py + +noinst_HEADERS = common.h sfconfig.h sfendian.h float_cast.h wav_w64.h sf_unistd.h + +noinst_PROGRAMS = test_endswap test_file_io test_conversions test_log_printf \ + test_audio_detect + +COMMON = common.c file_io.c command.c pcm.c ulaw.c alaw.c float32.c \ + double64.c ima_adpcm.c ms_adpcm.c gsm610.c dwvw.c vox_adpcm.c \ + interleave.c strings.c dither.c broadcast.c audio_detect.c + +FILESPECIFIC = sndfile.c aiff.c au.c avr.c caf.c dwd.c flac.c g72x.c htk.c ircam.c \ + macbinary3.c macos.c mat4.c mat5.c nist.c ogg.c paf.c pvf.c raw.c rx2.c sd2.c \ + sds.c svx.c txw.c voc.c wve.c w64.c wav_w64.c wav.c xi.c + +# MinGW requires -no-undefined if a DLL is to be built. +libsndfile_la_LDFLAGS = -no-undefined -version-info @SHARED_VERSION_INFO@ @SHLIB_VERSION_ARG@ +libsndfile_la_SOURCES = $(FILESPECIFIC) $(noinst_HEADERS) +nodist_libsndfile_la_SOURCES = $(nodist_include_HEADERS) +libsndfile_la_LIBADD = libcommon.la FLAC/src/libFLAC/libFLAC.la GSM610/libgsm.la \ + G72x/libg72x.la OGG/libogg.la -lm + +libcommon_la_SOURCES = $(COMMON) + +test_endswap_SOURCES = test_endswap.c + +test_file_io_CFLAGS = $(AM_CFLAGS) +test_file_io_SOURCES = test_file_io.c +test_file_io_LDADD = libcommon.la + +test_log_printf_CFLAGS = $(AM_CFLAGS) +test_log_printf_SOURCES = test_log_printf.c +test_log_printf_LDADD = libcommon.la + +test_conversions_CFLAGS = $(AM_CFLAGS) +test_conversions_SOURCES = test_conversions.c +test_conversions_LDADD = libcommon.la + +test_audio_detect_CFLAGS = $(AM_CFLAGS) +test_audio_detect_SOURCES = test_audio_detect.c +test_audio_detect_LDADD = libcommon.la + +test_endswap.c: test_endswap.def test_endswap.tpl + autogen --writable test_endswap.def + +genfiles : test_endswap.c Symbols.linux Symbols.darwin libsndfile.def cygsndfile.def + +# Two test programs. +# It is not possible to place these in the tests/ directory because they +# need access to the internals of the SF_PRIVATE struct. + +check: test_endswap test_file_io test_conversions test_log_printf + @echo + @echo + @echo + @echo "============================================================" + @if [ -x /usr/bin/python2.4 ]; then $(srcdir)/binheader_writef_check.py $(srcdir)/*.c ; fi + ./test_endswap + ./test_file_io + ./test_conversions + ./test_log_printf + ./test_audio_detect + @echo "============================================================" + @echo + @echo + @echo + +#====================================================================== +# Generate an OS specific Symbols files. This is done when the author +# builds the distribution tarball. There should be not need for the +# end user to create these files. + +Symbols.linux: create_symbols_file.py + ./create_symbols_file.py linux $(VERSION) > Symbols.linux + +Symbols.darwin: create_symbols_file.py + ./create_symbols_file.py darwin $(VERSION) > Symbols.darwin + +libsndfile.def: create_symbols_file.py + ./create_symbols_file.py win32 $(VERSION) > libsndfile.def + +cygsndfile.def: create_symbols_file.py + ./create_symbols_file.py cygwin $(VERSION) > cygsndfile.def + +# Fake dependancy to force the creation of these files. +sndfile.o : Symbols.linux Symbols.darwin libsndfile.def cygsndfile.def + +#====================================================================== +# Disable autoheader. +AUTOHEADER=echo + +# Dependancies. + +aiff.c au.c g72x.c ircam.c mat4.c mat5.c nist.c paf.c pvf.c : sndfile.h common.h +raw.c svx.c voc.c w64.c wav.c wav_w64.c htk.c sd2.c rx2.c txw.c : sndfile.h common.h +sds.c wve.c dwd.c ogg.c xi.c sndfile.c common.c file_io.c : sndfile.h common.h +command.c pcm.c ulaw.c alaw.c float32.c double64.c ima_adpcm.c : sndfile.h common.h +ms_adpcm.c gsm610.c dwvw.c vox_adpcm.c interleave.c strings.c : sndfile.h common.h +dither.c : sndfile.h common.h + +## Do not edit or modify anything in this comment block. +## The arch-tag line is a file identity tag for the GNU Arch +## revision control system. +## +## arch-tag: fc3511e6-4230-4bcb-9c86-f728d7a06fe7 + diff --git a/src/OGG/Makefile.am b/src/OGG/Makefile.am new file mode 100644 index 00000000..b597096c --- /dev/null +++ b/src/OGG/Makefile.am @@ -0,0 +1,29 @@ +## Process this file with automake to produce Makefile.in + +INCLUDES = -I$(top_srcdir)/src/OGG/include -I$(top_builddir)/src/OGG/include + +SUBDIRS = include + +noinst_LTLIBRARIES = libogg.la + +libogg_la_SOURCES = framing.c bitwise.c + +# build and run the self tests on 'make check' + +noinst_PROGRAMS = test_bitwise test_framing + +test_bitwise_SOURCES = bitwise.c +test_bitwise_CFLAGS = -D_V_SELFTEST + +test_framing_SOURCES = framing.c +test_framing_CFLAGS = -D_V_SELFTEST + +check: test_bitwise test_framing + ./test_bitwise + ./test_framing + +debug: + $(MAKE) all CFLAGS="@DEBUG@" + +profile: + $(MAKE) all CFLAGS="@PROFILE@" diff --git a/src/OGG/README.txt b/src/OGG/README.txt new file mode 100644 index 00000000..5cb7102b --- /dev/null +++ b/src/OGG/README.txt @@ -0,0 +1,7 @@ +All code in this directory and below is from the libogg by Mony of the Xiph +Foundation and others. Please see the individual files for their +copyrights and licensing. + +The version here is from the file : + + libogg-1.1.3.tar.gz md5sum : eaf7dc6ebbff30975de7527a80831585 diff --git a/src/OGG/bitwise.c b/src/OGG/bitwise.c new file mode 100644 index 00000000..f2a89dda --- /dev/null +++ b/src/OGG/bitwise.c @@ -0,0 +1,788 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: packing variable sized words into an octet stream + last mod: $Id: bitwise.c 7675 2004-09-01 00:34:39Z xiphmont $ + + ********************************************************************/ + +/* We're 'LSb' endian; if we write a word but read individual bits, + then we'll read the lsb first */ + +#include +#include +#include + +#define BUFFER_INCREMENT 256 + +static const unsigned long mask[]= +{0x00000000,0x00000001,0x00000003,0x00000007,0x0000000f, + 0x0000001f,0x0000003f,0x0000007f,0x000000ff,0x000001ff, + 0x000003ff,0x000007ff,0x00000fff,0x00001fff,0x00003fff, + 0x00007fff,0x0000ffff,0x0001ffff,0x0003ffff,0x0007ffff, + 0x000fffff,0x001fffff,0x003fffff,0x007fffff,0x00ffffff, + 0x01ffffff,0x03ffffff,0x07ffffff,0x0fffffff,0x1fffffff, + 0x3fffffff,0x7fffffff,0xffffffff }; + +static const unsigned int mask8B[]= +{0x00,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff}; + +void oggpack_writeinit(oggpack_buffer *b){ + memset(b,0,sizeof(*b)); + b->ptr=b->buffer=_ogg_malloc(BUFFER_INCREMENT); + b->buffer[0]='\0'; + b->storage=BUFFER_INCREMENT; +} + +void oggpackB_writeinit(oggpack_buffer *b){ + oggpack_writeinit(b); +} + +void oggpack_writetrunc(oggpack_buffer *b,long bits){ + long bytes=bits>>3; + bits-=bytes*8; + b->ptr=b->buffer+bytes; + b->endbit=bits; + b->endbyte=bytes; + *b->ptr&=mask[bits]; +} + +void oggpackB_writetrunc(oggpack_buffer *b,long bits){ + long bytes=bits>>3; + bits-=bytes*8; + b->ptr=b->buffer+bytes; + b->endbit=bits; + b->endbyte=bytes; + *b->ptr&=mask8B[bits]; +} + +/* Takes only up to 32 bits. */ +void oggpack_write(oggpack_buffer *b,unsigned long value,int bits){ + if(b->endbyte+4>=b->storage){ + b->buffer=_ogg_realloc(b->buffer,b->storage+BUFFER_INCREMENT); + b->storage+=BUFFER_INCREMENT; + b->ptr=b->buffer+b->endbyte; + } + + value&=mask[bits]; + bits+=b->endbit; + + b->ptr[0]|=value<endbit; + + if(bits>=8){ + b->ptr[1]=(unsigned char)(value>>(8-b->endbit)); + if(bits>=16){ + b->ptr[2]=(unsigned char)(value>>(16-b->endbit)); + if(bits>=24){ + b->ptr[3]=(unsigned char)(value>>(24-b->endbit)); + if(bits>=32){ + if(b->endbit) + b->ptr[4]=(unsigned char)(value>>(32-b->endbit)); + else + b->ptr[4]=0; + } + } + } + } + + b->endbyte+=bits/8; + b->ptr+=bits/8; + b->endbit=bits&7; +} + +/* Takes only up to 32 bits. */ +void oggpackB_write(oggpack_buffer *b,unsigned long value,int bits){ + if(b->endbyte+4>=b->storage){ + b->buffer=_ogg_realloc(b->buffer,b->storage+BUFFER_INCREMENT); + b->storage+=BUFFER_INCREMENT; + b->ptr=b->buffer+b->endbyte; + } + + value=(value&mask[bits])<<(32-bits); + bits+=b->endbit; + + b->ptr[0]|=value>>(24+b->endbit); + + if(bits>=8){ + b->ptr[1]=(unsigned char)(value>>(16+b->endbit)); + if(bits>=16){ + b->ptr[2]=(unsigned char)(value>>(8+b->endbit)); + if(bits>=24){ + b->ptr[3]=(unsigned char)(value>>(b->endbit)); + if(bits>=32){ + if(b->endbit) + b->ptr[4]=(unsigned char)(value<<(8-b->endbit)); + else + b->ptr[4]=0; + } + } + } + } + + b->endbyte+=bits/8; + b->ptr+=bits/8; + b->endbit=bits&7; +} + +void oggpack_writealign(oggpack_buffer *b){ + int bits=8-b->endbit; + if(bits<8) + oggpack_write(b,0,bits); +} + +void oggpackB_writealign(oggpack_buffer *b){ + int bits=8-b->endbit; + if(bits<8) + oggpackB_write(b,0,bits); +} + +static void oggpack_writecopy_helper(oggpack_buffer *b, + void *source, + long bits, + void (*w)(oggpack_buffer *, + unsigned long, + int), + int msb){ + unsigned char *ptr=(unsigned char *)source; + + long bytes=bits/8; + bits-=bytes*8; + + if(b->endbit){ + int i; + /* unaligned copy. Do it the hard way. */ + for(i=0;iendbyte+bytes+1>=b->storage){ + b->storage=b->endbyte+bytes+BUFFER_INCREMENT; + b->buffer=_ogg_realloc(b->buffer,b->storage); + b->ptr=b->buffer+b->endbyte; + } + + memmove(b->ptr,source,bytes); + b->ptr+=bytes; + b->endbyte+=bytes; + *b->ptr=0; + + } + if(bits){ + if(msb) + w(b,(unsigned long)(ptr[bytes]>>(8-bits)),bits); + else + w(b,(unsigned long)(ptr[bytes]),bits); + } +} + +void oggpack_writecopy(oggpack_buffer *b,void *source,long bits){ + oggpack_writecopy_helper(b,source,bits,oggpack_write,0); +} + +void oggpackB_writecopy(oggpack_buffer *b,void *source,long bits){ + oggpack_writecopy_helper(b,source,bits,oggpackB_write,1); +} + +void oggpack_reset(oggpack_buffer *b){ + b->ptr=b->buffer; + b->buffer[0]=0; + b->endbit=b->endbyte=0; +} + +void oggpackB_reset(oggpack_buffer *b){ + oggpack_reset(b); +} + +void oggpack_writeclear(oggpack_buffer *b){ + _ogg_free(b->buffer); + memset(b,0,sizeof(*b)); +} + +void oggpackB_writeclear(oggpack_buffer *b){ + oggpack_writeclear(b); +} + +void oggpack_readinit(oggpack_buffer *b,unsigned char *buf,int bytes){ + memset(b,0,sizeof(*b)); + b->buffer=b->ptr=buf; + b->storage=bytes; +} + +void oggpackB_readinit(oggpack_buffer *b,unsigned char *buf,int bytes){ + oggpack_readinit(b,buf,bytes); +} + +/* Read in bits without advancing the bitptr; bits <= 32 */ +long oggpack_look(oggpack_buffer *b,int bits){ + unsigned long ret; + unsigned long m=mask[bits]; + + bits+=b->endbit; + + if(b->endbyte+4>=b->storage){ + /* not the main path */ + if(b->endbyte*8+bits>b->storage*8)return(-1); + } + + ret=b->ptr[0]>>b->endbit; + if(bits>8){ + ret|=b->ptr[1]<<(8-b->endbit); + if(bits>16){ + ret|=b->ptr[2]<<(16-b->endbit); + if(bits>24){ + ret|=b->ptr[3]<<(24-b->endbit); + if(bits>32 && b->endbit) + ret|=b->ptr[4]<<(32-b->endbit); + } + } + } + return(m&ret); +} + +/* Read in bits without advancing the bitptr; bits <= 32 */ +long oggpackB_look(oggpack_buffer *b,int bits){ + unsigned long ret; + int m=32-bits; + + bits+=b->endbit; + + if(b->endbyte+4>=b->storage){ + /* not the main path */ + if(b->endbyte*8+bits>b->storage*8)return(-1); + } + + ret=b->ptr[0]<<(24+b->endbit); + if(bits>8){ + ret|=b->ptr[1]<<(16+b->endbit); + if(bits>16){ + ret|=b->ptr[2]<<(8+b->endbit); + if(bits>24){ + ret|=b->ptr[3]<<(b->endbit); + if(bits>32 && b->endbit) + ret|=b->ptr[4]>>(8-b->endbit); + } + } + } + return ((ret&0xffffffff)>>(m>>1))>>((m+1)>>1); +} + +long oggpack_look1(oggpack_buffer *b){ + if(b->endbyte>=b->storage)return(-1); + return((b->ptr[0]>>b->endbit)&1); +} + +long oggpackB_look1(oggpack_buffer *b){ + if(b->endbyte>=b->storage)return(-1); + return((b->ptr[0]>>(7-b->endbit))&1); +} + +void oggpack_adv(oggpack_buffer *b,int bits){ + bits+=b->endbit; + b->ptr+=bits/8; + b->endbyte+=bits/8; + b->endbit=bits&7; +} + +void oggpackB_adv(oggpack_buffer *b,int bits){ + oggpack_adv(b,bits); +} + +void oggpack_adv1(oggpack_buffer *b){ + if(++(b->endbit)>7){ + b->endbit=0; + b->ptr++; + b->endbyte++; + } +} + +void oggpackB_adv1(oggpack_buffer *b){ + oggpack_adv1(b); +} + +/* bits <= 32 */ +long oggpack_read(oggpack_buffer *b,int bits){ + long ret; + unsigned long m=mask[bits]; + + bits+=b->endbit; + + if(b->endbyte+4>=b->storage){ + /* not the main path */ + ret=-1L; + if(b->endbyte*8+bits>b->storage*8)goto overflow; + } + + ret=b->ptr[0]>>b->endbit; + if(bits>8){ + ret|=b->ptr[1]<<(8-b->endbit); + if(bits>16){ + ret|=b->ptr[2]<<(16-b->endbit); + if(bits>24){ + ret|=b->ptr[3]<<(24-b->endbit); + if(bits>32 && b->endbit){ + ret|=b->ptr[4]<<(32-b->endbit); + } + } + } + } + ret&=m; + + overflow: + + b->ptr+=bits/8; + b->endbyte+=bits/8; + b->endbit=bits&7; + return(ret); +} + +/* bits <= 32 */ +long oggpackB_read(oggpack_buffer *b,int bits){ + long ret; + long m=32-bits; + + bits+=b->endbit; + + if(b->endbyte+4>=b->storage){ + /* not the main path */ + ret=-1L; + if(b->endbyte*8+bits>b->storage*8)goto overflow; + } + + ret=b->ptr[0]<<(24+b->endbit); + if(bits>8){ + ret|=b->ptr[1]<<(16+b->endbit); + if(bits>16){ + ret|=b->ptr[2]<<(8+b->endbit); + if(bits>24){ + ret|=b->ptr[3]<<(b->endbit); + if(bits>32 && b->endbit) + ret|=b->ptr[4]>>(8-b->endbit); + } + } + } + ret=((ret&0xffffffffUL)>>(m>>1))>>((m+1)>>1); + + overflow: + + b->ptr+=bits/8; + b->endbyte+=bits/8; + b->endbit=bits&7; + return(ret); +} + +long oggpack_read1(oggpack_buffer *b){ + long ret; + + if(b->endbyte>=b->storage){ + /* not the main path */ + ret=-1L; + goto overflow; + } + + ret=(b->ptr[0]>>b->endbit)&1; + + overflow: + + b->endbit++; + if(b->endbit>7){ + b->endbit=0; + b->ptr++; + b->endbyte++; + } + return(ret); +} + +long oggpackB_read1(oggpack_buffer *b){ + long ret; + + if(b->endbyte>=b->storage){ + /* not the main path */ + ret=-1L; + goto overflow; + } + + ret=(b->ptr[0]>>(7-b->endbit))&1; + + overflow: + + b->endbit++; + if(b->endbit>7){ + b->endbit=0; + b->ptr++; + b->endbyte++; + } + return(ret); +} + +long oggpack_bytes(oggpack_buffer *b){ + return(b->endbyte+(b->endbit+7)/8); +} + +long oggpack_bits(oggpack_buffer *b){ + return(b->endbyte*8+b->endbit); +} + +long oggpackB_bytes(oggpack_buffer *b){ + return oggpack_bytes(b); +} + +long oggpackB_bits(oggpack_buffer *b){ + return oggpack_bits(b); +} + +unsigned char *oggpack_get_buffer(oggpack_buffer *b){ + return(b->buffer); +} + +unsigned char *oggpackB_get_buffer(oggpack_buffer *b){ + return oggpack_get_buffer(b); +} + +/* Self test of the bitwise routines; everything else is based on + them, so they damned well better be solid. */ + +#ifdef _V_SELFTEST +#include + +static int ilog(unsigned int v){ + int ret=0; + while(v){ + ret++; + v>>=1; + } + return(ret); +} + +oggpack_buffer o; +oggpack_buffer r; + +static +void report(const char *in){ + fprintf(stderr,"%s",in); + exit(1); +} + +static +void cliptest(unsigned long *b,int vals,int bits,int *comp,int compsize){ + long bytes,i; + unsigned char *buffer; + + oggpack_reset(&o); + for(i=0;i +#include +#include + +/* A complete description of Ogg framing exists in docs/framing.html */ + +int ogg_page_version(ogg_page *og){ + return((int)(og->header[4])); +} + +int ogg_page_continued(ogg_page *og){ + return((int)(og->header[5]&0x01)); +} + +int ogg_page_bos(ogg_page *og){ + return((int)(og->header[5]&0x02)); +} + +int ogg_page_eos(ogg_page *og){ + return((int)(og->header[5]&0x04)); +} + +ogg_int64_t ogg_page_granulepos(ogg_page *og){ + unsigned char *page=og->header; + ogg_int64_t granulepos=page[13]&(0xff); + granulepos= (granulepos<<8)|(page[12]&0xff); + granulepos= (granulepos<<8)|(page[11]&0xff); + granulepos= (granulepos<<8)|(page[10]&0xff); + granulepos= (granulepos<<8)|(page[9]&0xff); + granulepos= (granulepos<<8)|(page[8]&0xff); + granulepos= (granulepos<<8)|(page[7]&0xff); + granulepos= (granulepos<<8)|(page[6]&0xff); + return(granulepos); +} + +int ogg_page_serialno(ogg_page *og){ + return(og->header[14] | + (og->header[15]<<8) | + (og->header[16]<<16) | + (og->header[17]<<24)); +} + +long ogg_page_pageno(ogg_page *og){ + return(og->header[18] | + (og->header[19]<<8) | + (og->header[20]<<16) | + (og->header[21]<<24)); +} + + + +/* returns the number of packets that are completed on this page (if + the leading packet is begun on a previous page, but ends on this + page, it's counted */ + +/* NOTE: +If a page consists of a packet begun on a previous page, and a new +packet begun (but not completed) on this page, the return will be: + ogg_page_packets(page) ==1, + ogg_page_continued(page) !=0 + +If a page happens to be a single packet that was begun on a +previous page, and spans to the next page (in the case of a three or +more page packet), the return will be: + ogg_page_packets(page) ==0, + ogg_page_continued(page) !=0 +*/ + +int ogg_page_packets(ogg_page *og){ + int i,n=og->header[26],count=0; + for(i=0;iheader[27+i]<255)count++; + return(count); +} + + +#if 0 +/* helper to initialize lookup for direct-table CRC (illustrative; we + use the static init below) */ + +static ogg_uint32_t _ogg_crc_entry(unsigned long index){ + int i; + unsigned long r; + + r = index << 24; + for (i=0; i<8; i++) + if (r & 0x80000000UL) + r = (r << 1) ^ 0x04c11db7; /* The same as the ethernet generator + polynomial, although we use an + unreflected alg and an init/final + of 0, not 0xffffffff */ + else + r<<=1; + return (r & 0xffffffffUL); +} +#endif + +static const ogg_uint32_t crc_lookup[256]={ + 0x00000000,0x04c11db7,0x09823b6e,0x0d4326d9, + 0x130476dc,0x17c56b6b,0x1a864db2,0x1e475005, + 0x2608edb8,0x22c9f00f,0x2f8ad6d6,0x2b4bcb61, + 0x350c9b64,0x31cd86d3,0x3c8ea00a,0x384fbdbd, + 0x4c11db70,0x48d0c6c7,0x4593e01e,0x4152fda9, + 0x5f15adac,0x5bd4b01b,0x569796c2,0x52568b75, + 0x6a1936c8,0x6ed82b7f,0x639b0da6,0x675a1011, + 0x791d4014,0x7ddc5da3,0x709f7b7a,0x745e66cd, + 0x9823b6e0,0x9ce2ab57,0x91a18d8e,0x95609039, + 0x8b27c03c,0x8fe6dd8b,0x82a5fb52,0x8664e6e5, + 0xbe2b5b58,0xbaea46ef,0xb7a96036,0xb3687d81, + 0xad2f2d84,0xa9ee3033,0xa4ad16ea,0xa06c0b5d, + 0xd4326d90,0xd0f37027,0xddb056fe,0xd9714b49, + 0xc7361b4c,0xc3f706fb,0xceb42022,0xca753d95, + 0xf23a8028,0xf6fb9d9f,0xfbb8bb46,0xff79a6f1, + 0xe13ef6f4,0xe5ffeb43,0xe8bccd9a,0xec7dd02d, + 0x34867077,0x30476dc0,0x3d044b19,0x39c556ae, + 0x278206ab,0x23431b1c,0x2e003dc5,0x2ac12072, + 0x128e9dcf,0x164f8078,0x1b0ca6a1,0x1fcdbb16, + 0x018aeb13,0x054bf6a4,0x0808d07d,0x0cc9cdca, + 0x7897ab07,0x7c56b6b0,0x71159069,0x75d48dde, + 0x6b93dddb,0x6f52c06c,0x6211e6b5,0x66d0fb02, + 0x5e9f46bf,0x5a5e5b08,0x571d7dd1,0x53dc6066, + 0x4d9b3063,0x495a2dd4,0x44190b0d,0x40d816ba, + 0xaca5c697,0xa864db20,0xa527fdf9,0xa1e6e04e, + 0xbfa1b04b,0xbb60adfc,0xb6238b25,0xb2e29692, + 0x8aad2b2f,0x8e6c3698,0x832f1041,0x87ee0df6, + 0x99a95df3,0x9d684044,0x902b669d,0x94ea7b2a, + 0xe0b41de7,0xe4750050,0xe9362689,0xedf73b3e, + 0xf3b06b3b,0xf771768c,0xfa325055,0xfef34de2, + 0xc6bcf05f,0xc27dede8,0xcf3ecb31,0xcbffd686, + 0xd5b88683,0xd1799b34,0xdc3abded,0xd8fba05a, + 0x690ce0ee,0x6dcdfd59,0x608edb80,0x644fc637, + 0x7a089632,0x7ec98b85,0x738aad5c,0x774bb0eb, + 0x4f040d56,0x4bc510e1,0x46863638,0x42472b8f, + 0x5c007b8a,0x58c1663d,0x558240e4,0x51435d53, + 0x251d3b9e,0x21dc2629,0x2c9f00f0,0x285e1d47, + 0x36194d42,0x32d850f5,0x3f9b762c,0x3b5a6b9b, + 0x0315d626,0x07d4cb91,0x0a97ed48,0x0e56f0ff, + 0x1011a0fa,0x14d0bd4d,0x19939b94,0x1d528623, + 0xf12f560e,0xf5ee4bb9,0xf8ad6d60,0xfc6c70d7, + 0xe22b20d2,0xe6ea3d65,0xeba91bbc,0xef68060b, + 0xd727bbb6,0xd3e6a601,0xdea580d8,0xda649d6f, + 0xc423cd6a,0xc0e2d0dd,0xcda1f604,0xc960ebb3, + 0xbd3e8d7e,0xb9ff90c9,0xb4bcb610,0xb07daba7, + 0xae3afba2,0xaafbe615,0xa7b8c0cc,0xa379dd7b, + 0x9b3660c6,0x9ff77d71,0x92b45ba8,0x9675461f, + 0x8832161a,0x8cf30bad,0x81b02d74,0x857130c3, + 0x5d8a9099,0x594b8d2e,0x5408abf7,0x50c9b640, + 0x4e8ee645,0x4a4ffbf2,0x470cdd2b,0x43cdc09c, + 0x7b827d21,0x7f436096,0x7200464f,0x76c15bf8, + 0x68860bfd,0x6c47164a,0x61043093,0x65c52d24, + 0x119b4be9,0x155a565e,0x18197087,0x1cd86d30, + 0x029f3d35,0x065e2082,0x0b1d065b,0x0fdc1bec, + 0x3793a651,0x3352bbe6,0x3e119d3f,0x3ad08088, + 0x2497d08d,0x2056cd3a,0x2d15ebe3,0x29d4f654, + 0xc5a92679,0xc1683bce,0xcc2b1d17,0xc8ea00a0, + 0xd6ad50a5,0xd26c4d12,0xdf2f6bcb,0xdbee767c, + 0xe3a1cbc1,0xe760d676,0xea23f0af,0xeee2ed18, + 0xf0a5bd1d,0xf464a0aa,0xf9278673,0xfde69bc4, + 0x89b8fd09,0x8d79e0be,0x803ac667,0x84fbdbd0, + 0x9abc8bd5,0x9e7d9662,0x933eb0bb,0x97ffad0c, + 0xafb010b1,0xab710d06,0xa6322bdf,0xa2f33668, + 0xbcb4666d,0xb8757bda,0xb5365d03,0xb1f740b4}; + +/* init the encode/decode logical stream state */ + +int ogg_stream_init(ogg_stream_state *os,int serialno){ + if(os){ + memset(os,0,sizeof(*os)); + os->body_storage=16*1024; + os->body_data=_ogg_malloc(os->body_storage*sizeof(*os->body_data)); + + os->lacing_storage=1024; + os->lacing_vals=_ogg_malloc(os->lacing_storage*sizeof(*os->lacing_vals)); + os->granule_vals=_ogg_malloc(os->lacing_storage*sizeof(*os->granule_vals)); + + os->serialno=serialno; + + return(0); + } + return(-1); +} + +/* _clear does not free os, only the non-flat storage within */ +int ogg_stream_clear(ogg_stream_state *os){ + if(os){ + if(os->body_data)_ogg_free(os->body_data); + if(os->lacing_vals)_ogg_free(os->lacing_vals); + if(os->granule_vals)_ogg_free(os->granule_vals); + + memset(os,0,sizeof(*os)); + } + return(0); +} + +int ogg_stream_destroy(ogg_stream_state *os){ + if(os){ + ogg_stream_clear(os); + _ogg_free(os); + } + return(0); +} + +/* Helpers for ogg_stream_encode; this keeps the structure and + what's happening fairly clear */ + +static void _os_body_expand(ogg_stream_state *os,int needed){ + if(os->body_storage<=os->body_fill+needed){ + os->body_storage+=(needed+1024); + os->body_data=_ogg_realloc(os->body_data,os->body_storage*sizeof(*os->body_data)); + } +} + +static void _os_lacing_expand(ogg_stream_state *os,int needed){ + if(os->lacing_storage<=os->lacing_fill+needed){ + os->lacing_storage+=(needed+32); + os->lacing_vals=_ogg_realloc(os->lacing_vals,os->lacing_storage*sizeof(*os->lacing_vals)); + os->granule_vals=_ogg_realloc(os->granule_vals,os->lacing_storage*sizeof(*os->granule_vals)); + } +} + +/* checksum the page */ +/* Direct table CRC; note that this will be faster in the future if we + perform the checksum silmultaneously with other copies */ + +void ogg_page_checksum_set(ogg_page *og){ + if(og){ + ogg_uint32_t crc_reg=0; + int i; + + /* safety; needed for API behavior, but not framing code */ + og->header[22]=0; + og->header[23]=0; + og->header[24]=0; + og->header[25]=0; + + for(i=0;iheader_len;i++) + crc_reg=(crc_reg<<8)^crc_lookup[((crc_reg >> 24)&0xff)^og->header[i]]; + for(i=0;ibody_len;i++) + crc_reg=(crc_reg<<8)^crc_lookup[((crc_reg >> 24)&0xff)^og->body[i]]; + + og->header[22]=(unsigned char)(crc_reg&0xff); + og->header[23]=(unsigned char)((crc_reg>>8)&0xff); + og->header[24]=(unsigned char)((crc_reg>>16)&0xff); + og->header[25]=(unsigned char)((crc_reg>>24)&0xff); + } +} + +/* submit data to the internal buffer of the framing engine */ +int ogg_stream_packetin(ogg_stream_state *os,ogg_packet *op){ + int lacing_vals=op->bytes/255+1,i; + + if(os->body_returned){ + /* advance packet data according to the body_returned pointer. We + had to keep it around to return a pointer into the buffer last + call */ + + os->body_fill-=os->body_returned; + if(os->body_fill) + memmove(os->body_data,os->body_data+os->body_returned, + os->body_fill); + os->body_returned=0; + } + + /* make sure we have the buffer storage */ + _os_body_expand(os,op->bytes); + _os_lacing_expand(os,lacing_vals); + + /* Copy in the submitted packet. Yes, the copy is a waste; this is + the liability of overly clean abstraction for the time being. It + will actually be fairly easy to eliminate the extra copy in the + future */ + + memcpy(os->body_data+os->body_fill,op->packet,op->bytes); + os->body_fill+=op->bytes; + + /* Store lacing vals for this packet */ + for(i=0;ilacing_vals[os->lacing_fill+i]=255; + os->granule_vals[os->lacing_fill+i]=os->granulepos; + } + os->lacing_vals[os->lacing_fill+i]=(op->bytes)%255; + os->granulepos=os->granule_vals[os->lacing_fill+i]=op->granulepos; + + /* flag the first segment as the beginning of the packet */ + os->lacing_vals[os->lacing_fill]|= 0x100; + + os->lacing_fill+=lacing_vals; + + /* for the sake of completeness */ + os->packetno++; + + if(op->e_o_s)os->e_o_s=1; + + return(0); +} + +/* This will flush remaining packets into a page (returning nonzero), + even if there is not enough data to trigger a flush normally + (undersized page). If there are no packets or partial packets to + flush, ogg_stream_flush returns 0. Note that ogg_stream_flush will + try to flush a normal sized page like ogg_stream_pageout; a call to + ogg_stream_flush does not guarantee that all packets have flushed. + Only a return value of 0 from ogg_stream_flush indicates all packet + data is flushed into pages. + + since ogg_stream_flush will flush the last page in a stream even if + it's undersized, you almost certainly want to use ogg_stream_pageout + (and *not* ogg_stream_flush) unless you specifically need to flush + an page regardless of size in the middle of a stream. */ + +int ogg_stream_flush(ogg_stream_state *os,ogg_page *og){ + int i; + int vals=0; + int maxvals=(os->lacing_fill>255?255:os->lacing_fill); + int bytes=0; + long acc=0; + ogg_int64_t granule_pos=-1; + + if(maxvals==0)return(0); + + /* construct a page */ + /* decide how many segments to include */ + + /* If this is the initial header case, the first page must only include + the initial header packet */ + if(os->b_o_s==0){ /* 'initial header page' case */ + granule_pos=0; + for(vals=0;valslacing_vals[vals]&0x0ff)<255){ + vals++; + break; + } + } + }else{ + for(vals=0;vals4096)break; + acc+=os->lacing_vals[vals]&0x0ff; + if((os->lacing_vals[vals]&0xff)<255) + granule_pos=os->granule_vals[vals]; + } + } + + /* construct the header in temp storage */ + memcpy(os->header,"OggS",4); + + /* stream structure version */ + os->header[4]=0x00; + + /* continued packet flag? */ + os->header[5]=0x00; + if((os->lacing_vals[0]&0x100)==0)os->header[5]|=0x01; + /* first page flag? */ + if(os->b_o_s==0)os->header[5]|=0x02; + /* last page flag? */ + if(os->e_o_s && os->lacing_fill==vals)os->header[5]|=0x04; + os->b_o_s=1; + + /* 64 bits of PCM position */ + for(i=6;i<14;i++){ + os->header[i]=(unsigned char)(granule_pos&0xff); + granule_pos>>=8; + } + + /* 32 bits of stream serial number */ + { + long serialno=os->serialno; + for(i=14;i<18;i++){ + os->header[i]=(unsigned char)(serialno&0xff); + serialno>>=8; + } + } + + /* 32 bits of page counter (we have both counter and page header + because this val can roll over) */ + if(os->pageno==-1)os->pageno=0; /* because someone called + stream_reset; this would be a + strange thing to do in an + encode stream, but it has + plausible uses */ + { + long pageno=os->pageno++; + for(i=18;i<22;i++){ + os->header[i]=(unsigned char)(pageno&0xff); + pageno>>=8; + } + } + + /* zero for computation; filled in later */ + os->header[22]=0; + os->header[23]=0; + os->header[24]=0; + os->header[25]=0; + + /* segment table */ + os->header[26]=(unsigned char)(vals&0xff); + for(i=0;iheader[i+27]=(unsigned char)(os->lacing_vals[i]&0xff); + + /* set pointers in the ogg_page struct */ + og->header=os->header; + og->header_len=os->header_fill=vals+27; + og->body=os->body_data+os->body_returned; + og->body_len=bytes; + + /* advance the lacing data and set the body_returned pointer */ + + os->lacing_fill-=vals; + memmove(os->lacing_vals,os->lacing_vals+vals,os->lacing_fill*sizeof(*os->lacing_vals)); + memmove(os->granule_vals,os->granule_vals+vals,os->lacing_fill*sizeof(*os->granule_vals)); + os->body_returned+=bytes; + + /* calculate the checksum */ + + ogg_page_checksum_set(og); + + /* done */ + return(1); +} + + +/* This constructs pages from buffered packet segments. The pointers +returned are to static buffers; do not free. The returned buffers are +good only until the next call (using the same ogg_stream_state) */ + +int ogg_stream_pageout(ogg_stream_state *os, ogg_page *og){ + + if((os->e_o_s&&os->lacing_fill) || /* 'were done, now flush' case */ + os->body_fill-os->body_returned > 4096 ||/* 'page nominal size' case */ + os->lacing_fill>=255 || /* 'segment table full' case */ + (os->lacing_fill&&!os->b_o_s)){ /* 'initial header page' case */ + + return(ogg_stream_flush(os,og)); + } + + /* not enough data to construct a page and not end of stream */ + return(0); +} + +int ogg_stream_eos(ogg_stream_state *os){ + return os->e_o_s; +} + +/* DECODING PRIMITIVES: packet streaming layer **********************/ + +/* This has two layers to place more of the multi-serialno and paging + control in the application's hands. First, we expose a data buffer + using ogg_sync_buffer(). The app either copies into the + buffer, or passes it directly to read(), etc. We then call + ogg_sync_wrote() to tell how many bytes we just added. + + Pages are returned (pointers into the buffer in ogg_sync_state) + by ogg_sync_pageout(). The page is then submitted to + ogg_stream_pagein() along with the appropriate + ogg_stream_state* (ie, matching serialno). We then get raw + packets out calling ogg_stream_packetout() with a + ogg_stream_state. */ + +/* initialize the struct to a known state */ +int ogg_sync_init(ogg_sync_state *oy){ + if(oy){ + memset(oy,0,sizeof(*oy)); + } + return(0); +} + +/* clear non-flat storage within */ +int ogg_sync_clear(ogg_sync_state *oy){ + if(oy){ + if(oy->data)_ogg_free(oy->data); + ogg_sync_init(oy); + } + return(0); +} + +int ogg_sync_destroy(ogg_sync_state *oy){ + if(oy){ + ogg_sync_clear(oy); + _ogg_free(oy); + } + return(0); +} + +char *ogg_sync_buffer(ogg_sync_state *oy, long size){ + + /* first, clear out any space that has been previously returned */ + if(oy->returned){ + oy->fill-=oy->returned; + if(oy->fill>0) + memmove(oy->data,oy->data+oy->returned,oy->fill); + oy->returned=0; + } + + if(size>oy->storage-oy->fill){ + /* We need to extend the internal buffer */ + long newsize=size+oy->fill+4096; /* an extra page to be nice */ + + if(oy->data) + oy->data=_ogg_realloc(oy->data,newsize); + else + oy->data=_ogg_malloc(newsize); + oy->storage=newsize; + } + + /* expose a segment at least as large as requested at the fill mark */ + return((char *)oy->data+oy->fill); +} + +int ogg_sync_wrote(ogg_sync_state *oy, long bytes){ + if(oy->fill+bytes>oy->storage)return(-1); + oy->fill+=bytes; + return(0); +} + +/* sync the stream. This is meant to be useful for finding page + boundaries. + + return values for this: + -n) skipped n bytes + 0) page not ready; more data (no bytes skipped) + n) page synced at current location; page length n bytes + +*/ + +long ogg_sync_pageseek(ogg_sync_state *oy,ogg_page *og){ + unsigned char *outerpage=oy->data+oy->returned; + unsigned char *next; + long outerbytes=oy->fill-oy->returned; + + if(oy->headerbytes==0){ + int headerbytes,i; + if(outerbytes<27)return(0); /* not enough for a header */ + + /* verify capture pattern */ + if(memcmp(outerpage,"OggS",4))goto sync_fail; + + headerbytes=outerpage[26]+27; + if(outerbytesbodybytes+=outerpage[27+i]; + oy->headerbytes=headerbytes; + } + + if(oy->bodybytes+oy->headerbytes>outerbytes)return(0); + + /* The whole test page is buffered. Verify the checksum */ + { + /* Grab the checksum bytes, set the header field to zero */ + char chksum[4]; + ogg_page log; + + memcpy(chksum,outerpage+22,4); + memset(outerpage+22,0,4); + + /* set up a temp page struct and recompute the checksum */ + log.header=outerpage; + log.header_len=oy->headerbytes; + log.body=outerpage+oy->headerbytes; + log.body_len=oy->bodybytes; + ogg_page_checksum_set(&log); + + /* Compare */ + if(memcmp(chksum,outerpage+22,4)){ + /* D'oh. Mismatch! Corrupt page (or miscapture and not a page + at all) */ + /* replace the computed checksum with the one actually read in */ + memcpy(outerpage+22,chksum,4); + + /* Bad checksum. Lose sync */ + goto sync_fail; + } + } + + /* yes, have a whole page all ready to go */ + { + unsigned char *page=oy->data+oy->returned; + long bytes; + + if(og){ + og->header=page; + og->header_len=oy->headerbytes; + og->body=page+oy->headerbytes; + og->body_len=oy->bodybytes; + } + + oy->unsynced=0; + oy->returned+=(bytes=oy->headerbytes+oy->bodybytes); + oy->headerbytes=0; + oy->bodybytes=0; + return(bytes); + } + + sync_fail: + + oy->headerbytes=0; + oy->bodybytes=0; + + /* search for possible capture */ + next=memchr(outerpage+1,'O',outerbytes-1); + if(!next) + next=oy->data+oy->fill; + + oy->returned=next-oy->data; + return(-(next-outerpage)); +} + +/* sync the stream and get a page. Keep trying until we find a page. + Supress 'sync errors' after reporting the first. + + return values: + -1) recapture (hole in data) + 0) need more data + 1) page returned + + Returns pointers into buffered data; invalidated by next call to + _stream, _clear, _init, or _buffer */ + +int ogg_sync_pageout(ogg_sync_state *oy, ogg_page *og){ + + /* all we need to do is verify a page at the head of the stream + buffer. If it doesn't verify, we look for the next potential + frame */ + + for(;;){ + long ret=ogg_sync_pageseek(oy,og); + if(ret>0){ + /* have a page */ + return(1); + } + if(ret==0){ + /* need more data */ + return(0); + } + + /* head did not start a synced page... skipped some bytes */ + if(!oy->unsynced){ + oy->unsynced=1; + return(-1); + } + + /* loop. keep looking */ + + } +} + +/* add the incoming page to the stream state; we decompose the page + into packet segments here as well. */ + +int ogg_stream_pagein(ogg_stream_state *os, ogg_page *og){ + unsigned char *header=og->header; + unsigned char *body=og->body; + long bodysize=og->body_len; + int segptr=0; + + int version=ogg_page_version(og); + int continued=ogg_page_continued(og); + int bos=ogg_page_bos(og); + int eos=ogg_page_eos(og); + ogg_int64_t granulepos=ogg_page_granulepos(og); + int serialno=ogg_page_serialno(og); + long pageno=ogg_page_pageno(og); + int segments=header[26]; + + /* clean up 'returned data' */ + { + long lr=os->lacing_returned; + long br=os->body_returned; + + /* body data */ + if(br){ + os->body_fill-=br; + if(os->body_fill) + memmove(os->body_data,os->body_data+br,os->body_fill); + os->body_returned=0; + } + + if(lr){ + /* segment table */ + if(os->lacing_fill-lr){ + memmove(os->lacing_vals,os->lacing_vals+lr, + (os->lacing_fill-lr)*sizeof(*os->lacing_vals)); + memmove(os->granule_vals,os->granule_vals+lr, + (os->lacing_fill-lr)*sizeof(*os->granule_vals)); + } + os->lacing_fill-=lr; + os->lacing_packet-=lr; + os->lacing_returned=0; + } + } + + /* check the serial number */ + if(serialno!=os->serialno)return(-1); + if(version>0)return(-1); + + _os_lacing_expand(os,segments+1); + + /* are we in sequence? */ + if(pageno!=os->pageno){ + int i; + + /* unroll previous partial packet (if any) */ + for(i=os->lacing_packet;ilacing_fill;i++) + os->body_fill-=os->lacing_vals[i]&0xff; + os->lacing_fill=os->lacing_packet; + + /* make a note of dropped data in segment table */ + if(os->pageno!=-1){ + os->lacing_vals[os->lacing_fill++]=0x400; + os->lacing_packet++; + } + } + + /* are we a 'continued packet' page? If so, we may need to skip + some segments */ + if(continued){ + if(os->lacing_fill<1 || + os->lacing_vals[os->lacing_fill-1]==0x400){ + bos=0; + for(;segptrbody_data+os->body_fill,body,bodysize); + os->body_fill+=bodysize; + } + + { + int saved=-1; + while(segptrlacing_vals[os->lacing_fill]=val; + os->granule_vals[os->lacing_fill]=-1; + + if(bos){ + os->lacing_vals[os->lacing_fill]|=0x100; + bos=0; + } + + if(val<255)saved=os->lacing_fill; + + os->lacing_fill++; + segptr++; + + if(val<255)os->lacing_packet=os->lacing_fill; + } + + /* set the granulepos on the last granuleval of the last full packet */ + if(saved!=-1){ + os->granule_vals[saved]=granulepos; + } + + } + + if(eos){ + os->e_o_s=1; + if(os->lacing_fill>0) + os->lacing_vals[os->lacing_fill-1]|=0x200; + } + + os->pageno=pageno+1; + + return(0); +} + +/* clear things to an initial state. Good to call, eg, before seeking */ +int ogg_sync_reset(ogg_sync_state *oy){ + oy->fill=0; + oy->returned=0; + oy->unsynced=0; + oy->headerbytes=0; + oy->bodybytes=0; + return(0); +} + +int ogg_stream_reset(ogg_stream_state *os){ + os->body_fill=0; + os->body_returned=0; + + os->lacing_fill=0; + os->lacing_packet=0; + os->lacing_returned=0; + + os->header_fill=0; + + os->e_o_s=0; + os->b_o_s=0; + os->pageno=-1; + os->packetno=0; + os->granulepos=0; + + return(0); +} + +int ogg_stream_reset_serialno(ogg_stream_state *os,int serialno){ + ogg_stream_reset(os); + os->serialno=serialno; + return(0); +} + +static int _packetout(ogg_stream_state *os,ogg_packet *op,int adv){ + + /* The last part of decode. We have the stream broken into packet + segments. Now we need to group them into packets (or return the + out of sync markers) */ + + int ptr=os->lacing_returned; + + if(os->lacing_packet<=ptr)return(0); + + if(os->lacing_vals[ptr]&0x400){ + /* we need to tell the codec there's a gap; it might need to + handle previous packet dependencies. */ + os->lacing_returned++; + os->packetno++; + return(-1); + } + + if(!op && !adv)return(1); /* just using peek as an inexpensive way + to ask if there's a whole packet + waiting */ + + /* Gather the whole packet. We'll have no holes or a partial packet */ + { + int size=os->lacing_vals[ptr]&0xff; + int bytes=size; + int eos=os->lacing_vals[ptr]&0x200; /* last packet of the stream? */ + int bos=os->lacing_vals[ptr]&0x100; /* first packet of the stream? */ + + while(size==255){ + int val=os->lacing_vals[++ptr]; + size=val&0xff; + if(val&0x200)eos=0x200; + bytes+=size; + } + + if(op){ + op->e_o_s=eos; + op->b_o_s=bos; + op->packet=os->body_data+os->body_returned; + op->packetno=os->packetno; + op->granulepos=os->granule_vals[ptr]; + op->bytes=bytes; + } + + if(adv){ + os->body_returned+=bytes; + os->lacing_returned=ptr+1; + os->packetno++; + } + } + return(1); +} + +int ogg_stream_packetout(ogg_stream_state *os,ogg_packet *op){ + return _packetout(os,op,1); +} + +int ogg_stream_packetpeek(ogg_stream_state *os,ogg_packet *op){ + return _packetout(os,op,0); +} + +void ogg_packet_clear(ogg_packet *op) { + _ogg_free(op->packet); + memset(op, 0, sizeof(*op)); +} + +#ifdef _V_SELFTEST +#include + +ogg_stream_state os_en, os_de; +ogg_sync_state oy; + +static +void checkpacket(ogg_packet *op,int len, int no, int pos){ + long j; + static int sequence=0; + static int lastno=0; + + if(op->bytes!=len){ + fprintf(stderr,"incorrect packet length!\n"); + exit(1); + } + if(op->granulepos!=pos){ + fprintf(stderr,"incorrect packet position!\n"); + exit(1); + } + + /* packet number just follows sequence/gap; adjust the input number + for that */ + if(no==0){ + sequence=0; + }else{ + sequence++; + if(no>lastno+1) + sequence++; + } + lastno=no; + if(op->packetno!=sequence){ + fprintf(stderr,"incorrect packet sequence %ld != %d\n", + (long)(op->packetno),sequence); + exit(1); + } + + /* Test data */ + for(j=0;jbytes;j++) + if(op->packet[j]!=((j+no)&0xff)){ + fprintf(stderr,"body data mismatch (1) at pos %ld: %x!=%lx!\n\n", + j,op->packet[j],(j+no)&0xff); + exit(1); + } +} + +static +void check_page(unsigned char *data,const int *header,ogg_page *og){ + long j; + /* Test data */ + for(j=0;jbody_len;j++) + if(og->body[j]!=data[j]){ + fprintf(stderr,"body data mismatch (2) at pos %ld: %x!=%x!\n\n", + j,data[j],og->body[j]); + exit(1); + } + + /* Test header */ + for(j=0;jheader_len;j++){ + if(og->header[j]!=header[j]){ + fprintf(stderr,"header content mismatch at pos %ld:\n",j); + for(j=0;jheader[j]); + fprintf(stderr,"\n"); + exit(1); + } + } + if(og->header_len!=header[26]+27){ + fprintf(stderr,"header length incorrect! (%ld!=%d)\n", + og->header_len,header[26]+27); + exit(1); + } +} + +void print_header(ogg_page *og); +void print_header(ogg_page *og){ + int j; + fprintf(stderr,"\nHEADER:\n"); + fprintf(stderr," capture: %c %c %c %c version: %d flags: %x\n", + og->header[0],og->header[1],og->header[2],og->header[3], + (int)og->header[4],(int)og->header[5]); + + fprintf(stderr," granulepos: %d serialno: %d pageno: %ld\n", + (og->header[9]<<24)|(og->header[8]<<16)| + (og->header[7]<<8)|og->header[6], + (og->header[17]<<24)|(og->header[16]<<16)| + (og->header[15]<<8)|og->header[14], + ((long)(og->header[21])<<24)|(og->header[20]<<16)| + (og->header[19]<<8)|og->header[18]); + + fprintf(stderr," checksum: %02x:%02x:%02x:%02x\n segments: %d (", + (int)og->header[22],(int)og->header[23], + (int)og->header[24],(int)og->header[25], + (int)og->header[26]); + + for(j=27;jheader_len;j++) + fprintf(stderr,"%d ",(int)og->header[j]); + fprintf(stderr,")\n\n"); +} + +static +void copy_page(ogg_page *og){ + unsigned char *temp=_ogg_malloc(og->header_len); + memcpy(temp,og->header,og->header_len); + og->header=temp; + + temp=_ogg_malloc(og->body_len); + memcpy(temp,og->body,og->body_len); + og->body=temp; +} + +static +void free_page(ogg_page *og){ + _ogg_free (og->header); + _ogg_free (og->body); +} + +static +void error(void){ + fprintf(stderr,"error!\n"); + exit(1); +} + +/* 17 only */ +static +const int head1_0[] = {0x4f,0x67,0x67,0x53,0,0x06, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0x15,0xed,0xec,0x91, + 1, + 17}; + +/* 17, 254, 255, 256, 500, 510, 600 byte, pad */ +const int head1_1[] = {0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0x59,0x10,0x6c,0x2c, + 1, + 17}; +const int head2_1[] = {0x4f,0x67,0x67,0x53,0,0x04, + 0x07,0x18,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0x89,0x33,0x85,0xce, + 13, + 254,255,0,255,1,255,245,255,255,0, + 255,255,90}; + +/* nil packets; beginning,middle,end */ +const int head1_2[] = {0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0xff,0x7b,0x23,0x17, + 1, + 0}; +const int head2_2[] = {0x4f,0x67,0x67,0x53,0,0x04, + 0x07,0x28,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0x5c,0x3f,0x66,0xcb, + 17, + 17,254,255,0,0,255,1,0,255,245,255,255,0, + 255,255,90,0}; + +/* large initial packet */ +const int head1_3[] = {0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0x01,0x27,0x31,0xaa, + 18, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,10}; + +const int head2_3[] = {0x4f,0x67,0x67,0x53,0,0x04, + 0x07,0x08,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0x7f,0x4e,0x8a,0xd2, + 4, + 255,4,255,0}; + + +/* continuing packet test */ +const int head1_4[] = {0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0xff,0x7b,0x23,0x17, + 1, + 0}; + +const int head2_4[] = {0x4f,0x67,0x67,0x53,0,0x00, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0x54,0x05,0x51,0xc8, + 17, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255}; + +const int head3_4[] = {0x4f,0x67,0x67,0x53,0,0x05, + 0x07,0x0c,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,2,0,0,0, + 0xc8,0xc3,0xcb,0xed, + 5, + 10,255,4,255,0}; + + +/* page with the 255 segment limit */ +const int head1_5[] = {0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0xff,0x7b,0x23,0x17, + 1, + 0}; + +const int head2_5[] = {0x4f,0x67,0x67,0x53,0,0x00, + 0x07,0xfc,0x03,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0xed,0x2a,0x2e,0xa7, + 255, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10}; + +const int head3_5[] = {0x4f,0x67,0x67,0x53,0,0x04, + 0x07,0x00,0x04,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,2,0,0,0, + 0x6c,0x3b,0x82,0x3d, + 1, + 50}; + + +/* packet that overspans over an entire page */ +const int head1_6[] = {0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0xff,0x7b,0x23,0x17, + 1, + 0}; + +const int head2_6[] = {0x4f,0x67,0x67,0x53,0,0x00, + 0x07,0x04,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0x3c,0xd9,0x4d,0x3f, + 17, + 100,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255}; + +const int head3_6[] = {0x4f,0x67,0x67,0x53,0,0x01, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0x01,0x02,0x03,0x04,2,0,0,0, + 0x01,0xd2,0xe5,0xe5, + 17, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255}; + +const int head4_6[] = {0x4f,0x67,0x67,0x53,0,0x05, + 0x07,0x10,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,3,0,0,0, + 0xef,0xdd,0x88,0xde, + 7, + 255,255,75,255,4,255,0}; + +/* packet that overspans over an entire page */ +const int head1_7[] = {0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0xff,0x7b,0x23,0x17, + 1, + 0}; + +const int head2_7[] = {0x4f,0x67,0x67,0x53,0,0x00, + 0x07,0x04,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0x3c,0xd9,0x4d,0x3f, + 17, + 100,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255}; + +const int head3_7[] = {0x4f,0x67,0x67,0x53,0,0x05, + 0x07,0x08,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,2,0,0,0, + 0xd4,0xe0,0x60,0xe5, + 1,0}; + +static +void test_pack(const int *pl, const int **headers, int byteskip, + int pageskip, int packetskip){ + unsigned char *data=_ogg_malloc(1024*1024); /* for scripted test cases only */ + long inptr=0; + long outptr=0; + long deptr=0; + long depacket=0; + long granule_pos=7,pageno=0; + int i,j,packets,pageout=pageskip; + int eosflag=0; + int bosflag=0; + + int byteskipcount=0; + + ogg_stream_reset(&os_en); + ogg_stream_reset(&os_de); + ogg_sync_reset(&oy); + + for(packets=0;packetsbyteskip){ + memcpy(next,og.header,byteskipcount-byteskip); + next+=byteskipcount-byteskip; + byteskipcount=byteskip; + } + + byteskipcount+=og.body_len; + if(byteskipcount>byteskip){ + memcpy(next,og.body,byteskipcount-byteskip); + next+=byteskipcount-byteskip; + byteskipcount=byteskip; + } + + ogg_sync_wrote(&oy,next-buf); + + while(1){ + int ret=ogg_sync_pageout(&oy,&og_de); + if(ret==0)break; + if(ret<0)continue; + /* got a page. Happy happy. Verify that it's good. */ + + fprintf(stderr,"(%d), ",pageout); + + check_page(data+deptr,headers[pageout],&og_de); + deptr+=og_de.body_len; + pageout++; + + /* submit it to deconstitution */ + ogg_stream_pagein(&os_de,&og_de); + + /* packets out? */ + while(ogg_stream_packetpeek(&os_de,&op_de2)>0){ + ogg_stream_packetpeek(&os_de,NULL); + ogg_stream_packetout(&os_de,&op_de); /* just catching them all */ + + /* verify peek and out match */ + if(memcmp(&op_de,&op_de2,sizeof(op_de))){ + fprintf(stderr,"packetout != packetpeek! pos=%ld\n", + depacket); + exit(1); + } + + /* verify the packet! */ + /* check data */ + if(memcmp(data+depacket,op_de.packet,op_de.bytes)){ + fprintf(stderr,"packet data mismatch in decode! pos=%ld\n", + depacket); + exit(1); + } + /* check bos flag */ + if(bosflag==0 && op_de.b_o_s==0){ + fprintf(stderr,"b_o_s flag not set on packet!\n"); + exit(1); + } + if(bosflag && op_de.b_o_s){ + fprintf(stderr,"b_o_s flag incorrectly set on packet!\n"); + exit(1); + } + bosflag=1; + depacket+=op_de.bytes; + + /* check eos flag */ + if(eosflag){ + fprintf(stderr,"Multiple decoded packets with eos flag!\n"); + exit(1); + } + + if(op_de.e_o_s)eosflag=1; + + /* check granulepos flag */ + if(op_de.granulepos!=-1){ + fprintf(stderr," granule:%ld ",(long)op_de.granulepos); + } + } + } + } + } + } + } + _ogg_free(data); + if(headers[pageno]!=NULL){ + fprintf(stderr,"did not write last page!\n"); + exit(1); + } + if(headers[pageout]!=NULL){ + fprintf(stderr,"did not decode last page!\n"); + exit(1); + } + if(inptr!=outptr){ + fprintf(stderr,"encoded page data incomplete!\n"); + exit(1); + } + if(inptr!=deptr){ + fprintf(stderr,"decoded page data incomplete!\n"); + exit(1); + } + if(inptr!=depacket){ + fprintf(stderr,"decoded packet data incomplete!\n"); + exit(1); + } + if(!eosflag){ + fprintf(stderr,"Never got a packet with EOS set!\n"); + exit(1); + } + fprintf(stderr,"ok.\n"); +} + +int main(void){ + + ogg_stream_init(&os_en,0x04030201); + ogg_stream_init(&os_de,0x04030201); + ogg_sync_init(&oy); + + /* Exercise each code path in the framing code. Also verify that + the checksums are working. */ + + { + /* 17 only */ + const int packets[]={17, -1}; + const int *headret[]={head1_0,NULL}; + + fprintf(stderr,"testing single page encoding... "); + test_pack(packets,headret,0,0,0); + } + + { + /* 17, 254, 255, 256, 500, 510, 600 byte, pad */ + const int packets[]={17, 254, 255, 256, 500, 510, 600, -1}; + const int *headret[]={head1_1,head2_1,NULL}; + + fprintf(stderr,"testing basic page encoding... "); + test_pack(packets,headret,0,0,0); + } + + { + /* nil packets; beginning,middle,end */ + const int packets[]={0,17, 254, 255, 0, 256, 0, 500, 510, 600, 0, -1}; + const int *headret[]={head1_2,head2_2,NULL}; + + fprintf(stderr,"testing basic nil packets... "); + test_pack(packets,headret,0,0,0); + } + + { + /* large initial packet */ + const int packets[]={4345,259,255,-1}; + const int *headret[]={head1_3,head2_3,NULL}; + + fprintf(stderr,"testing initial-packet lacing > 4k... "); + test_pack(packets,headret,0,0,0); + } + + { + /* continuing packet test */ + const int packets[]={0,4345,259,255,-1}; + const int *headret[]={head1_4,head2_4,head3_4,NULL}; + + fprintf(stderr,"testing single packet page span... "); + test_pack(packets,headret,0,0,0); + } + + /* page with the 255 segment limit */ + { + + const int packets[]={0,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,50,-1}; + const int *headret[]={head1_5,head2_5,head3_5,NULL}; + + fprintf(stderr,"testing max packet segments... "); + test_pack(packets,headret,0,0,0); + } + + { + /* packet that overspans over an entire page */ + const int packets[]={0,100,9000,259,255,-1}; + const int *headret[]={head1_6,head2_6,head3_6,head4_6,NULL}; + + fprintf(stderr,"testing very large packets... "); + test_pack(packets,headret,0,0,0); + } + + { + /* test for the libogg 1.1.1 resync in large continuation bug + found by Josh Coalson) */ + const int packets[]={0,100,9000,259,255,-1}; + const int *headret[]={head1_6,head2_6,head3_6,head4_6,NULL}; + + fprintf(stderr,"testing continuation resync in very large packets... "); + test_pack(packets,headret,100,2,3); + } + + { + /* term only page. why not? */ + const int packets[]={0,100,4080,-1}; + const int *headret[]={head1_7,head2_7,head3_7,NULL}; + + fprintf(stderr,"testing zero data page (1 nil packet)... "); + test_pack(packets,headret,0,0,0); + } + + + + { + /* build a bunch of pages for testing */ + unsigned char *data=_ogg_malloc(1024*1024); + int pl[]={0,100,4079,2956,2057,76,34,912,0,234,1000,1000,1000,300,-1}; + int inptr=0,i,j; + ogg_page og[5]; + + ogg_stream_reset(&os_en); + + for(i=0;pl[i]!=-1;i++){ + ogg_packet op; + int len=pl[i]; + + op.packet=data+inptr; + op.bytes=len; + op.e_o_s=(pl[i+1]<0?1:0); + op.granulepos=(i+1)*1000; + + for(j=0;j0)error(); + + /* Test fractional page inputs: incomplete fixed header */ + memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header+3, + 20); + ogg_sync_wrote(&oy,20); + if(ogg_sync_pageout(&oy,&og_de)>0)error(); + + /* Test fractional page inputs: incomplete header */ + memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header+23, + 5); + ogg_sync_wrote(&oy,5); + if(ogg_sync_pageout(&oy,&og_de)>0)error(); + + /* Test fractional page inputs: incomplete body */ + + memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header+28, + og[1].header_len-28); + ogg_sync_wrote(&oy,og[1].header_len-28); + if(ogg_sync_pageout(&oy,&og_de)>0)error(); + + memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body,1000); + ogg_sync_wrote(&oy,1000); + if(ogg_sync_pageout(&oy,&og_de)>0)error(); + + memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body+1000, + og[1].body_len-1000); + ogg_sync_wrote(&oy,og[1].body_len-1000); + if(ogg_sync_pageout(&oy,&og_de)<=0)error(); + + fprintf(stderr,"ok.\n"); + } + + /* Test fractional page inputs: page + incomplete capture */ + { + ogg_page og_de; + fprintf(stderr,"Testing sync on 1+partial inputs... "); + ogg_sync_reset(&oy); + + memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header, + og[1].header_len); + ogg_sync_wrote(&oy,og[1].header_len); + + memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body, + og[1].body_len); + ogg_sync_wrote(&oy,og[1].body_len); + + memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header, + 20); + ogg_sync_wrote(&oy,20); + if(ogg_sync_pageout(&oy,&og_de)<=0)error(); + if(ogg_sync_pageout(&oy,&og_de)>0)error(); + + memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header+20, + og[1].header_len-20); + ogg_sync_wrote(&oy,og[1].header_len-20); + memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body, + og[1].body_len); + ogg_sync_wrote(&oy,og[1].body_len); + if(ogg_sync_pageout(&oy,&og_de)<=0)error(); + + fprintf(stderr,"ok.\n"); + } + + /* Test recapture: garbage + page */ + { + ogg_page og_de; + fprintf(stderr,"Testing search for capture... "); + ogg_sync_reset(&oy); + + /* 'garbage' */ + memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body, + og[1].body_len); + ogg_sync_wrote(&oy,og[1].body_len); + + memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header, + og[1].header_len); + ogg_sync_wrote(&oy,og[1].header_len); + + memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body, + og[1].body_len); + ogg_sync_wrote(&oy,og[1].body_len); + + memcpy(ogg_sync_buffer(&oy,og[2].header_len),og[2].header, + 20); + ogg_sync_wrote(&oy,20); + if(ogg_sync_pageout(&oy,&og_de)>0)error(); + if(ogg_sync_pageout(&oy,&og_de)<=0)error(); + if(ogg_sync_pageout(&oy,&og_de)>0)error(); + + memcpy(ogg_sync_buffer(&oy,og[2].header_len),og[2].header+20, + og[2].header_len-20); + ogg_sync_wrote(&oy,og[2].header_len-20); + memcpy(ogg_sync_buffer(&oy,og[2].body_len),og[2].body, + og[2].body_len); + ogg_sync_wrote(&oy,og[2].body_len); + if(ogg_sync_pageout(&oy,&og_de)<=0)error(); + + fprintf(stderr,"ok.\n"); + } + + /* Test recapture: page + garbage + page */ + { + ogg_page og_de; + fprintf(stderr,"Testing recapture... "); + ogg_sync_reset(&oy); + + memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header, + og[1].header_len); + ogg_sync_wrote(&oy,og[1].header_len); + + memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body, + og[1].body_len); + ogg_sync_wrote(&oy,og[1].body_len); + + memcpy(ogg_sync_buffer(&oy,og[2].header_len),og[2].header, + og[2].header_len); + ogg_sync_wrote(&oy,og[2].header_len); + + memcpy(ogg_sync_buffer(&oy,og[2].header_len),og[2].header, + og[2].header_len); + ogg_sync_wrote(&oy,og[2].header_len); + + if(ogg_sync_pageout(&oy,&og_de)<=0)error(); + + memcpy(ogg_sync_buffer(&oy,og[2].body_len),og[2].body, + og[2].body_len-5); + ogg_sync_wrote(&oy,og[2].body_len-5); + + memcpy(ogg_sync_buffer(&oy,og[3].header_len),og[3].header, + og[3].header_len); + ogg_sync_wrote(&oy,og[3].header_len); + + memcpy(ogg_sync_buffer(&oy,og[3].body_len),og[3].body, + og[3].body_len); + ogg_sync_wrote(&oy,og[3].body_len); + + if(ogg_sync_pageout(&oy,&og_de)>0)error(); + if(ogg_sync_pageout(&oy,&og_de)<=0)error(); + + fprintf(stderr,"ok.\n"); + } + + /* Free page data that was previously copied */ + { + for(i=0;i<5;i++){ + free_page(&og[i]); + } + } + } + + return(0); +} + +#endif + + + + diff --git a/src/OGG/include/Makefile.am b/src/OGG/include/Makefile.am new file mode 100644 index 00000000..0084e4d8 --- /dev/null +++ b/src/OGG/include/Makefile.am @@ -0,0 +1,3 @@ +## Process this file with automake to produce Makefile.in + +SUBDIRS = ogg diff --git a/src/OGG/include/ogg/Makefile.am b/src/OGG/include/ogg/Makefile.am new file mode 100644 index 00000000..bc323934 --- /dev/null +++ b/src/OGG/include/ogg/Makefile.am @@ -0,0 +1,3 @@ +## Process this file with automake to produce Makefile.in + +noinst_HEADERS = ogg.h os_types.h config_types.h diff --git a/src/OGG/include/ogg/config_types.h b/src/OGG/include/ogg/config_types.h new file mode 100644 index 00000000..aa3341c2 --- /dev/null +++ b/src/OGG/include/ogg/config_types.h @@ -0,0 +1,12 @@ +#ifndef __CONFIG_TYPES_H__ +#define __CONFIG_TYPES_H__ + +#include + +typedef int16_t ogg_int16_t; +typedef uint16_t ogg_uint16_t; +typedef int32_t ogg_int32_t; +typedef uint32_t ogg_uint32_t; +typedef int64_t ogg_int64_t; + +#endif diff --git a/src/OGG/include/ogg/ogg.h b/src/OGG/include/ogg/ogg.h new file mode 100644 index 00000000..9082679d --- /dev/null +++ b/src/OGG/include/ogg/ogg.h @@ -0,0 +1,202 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: toplevel libogg include + last mod: $Id: ogg.h 7188 2004-07-20 07:26:04Z xiphmont $ + + ********************************************************************/ +#ifndef _OGG_H +#define _OGG_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef struct { + long endbyte; + int endbit; + + unsigned char *buffer; + unsigned char *ptr; + long storage; +} oggpack_buffer; + +/* ogg_page is used to encapsulate the data in one Ogg bitstream page *****/ + +typedef struct { + unsigned char *header; + long header_len; + unsigned char *body; + long body_len; +} ogg_page; + +/* ogg_stream_state contains the current encode/decode state of a logical + Ogg bitstream **********************************************************/ + +typedef struct { + unsigned char *body_data; /* bytes from packet bodies */ + long body_storage; /* storage elements allocated */ + long body_fill; /* elements stored; fill mark */ + long body_returned; /* elements of fill returned */ + + + int *lacing_vals; /* The values that will go to the segment table */ + ogg_int64_t *granule_vals; /* granulepos values for headers. Not compact + this way, but it is simple coupled to the + lacing fifo */ + long lacing_storage; + long lacing_fill; + long lacing_packet; + long lacing_returned; + + unsigned char header[282]; /* working space for header encode */ + int header_fill; + + int e_o_s; /* set when we have buffered the last packet in the + logical bitstream */ + int b_o_s; /* set after we've written the initial page + of a logical bitstream */ + long serialno; + long pageno; + ogg_int64_t packetno; /* sequence number for decode; the framing + knows where there's a hole in the data, + but we need coupling so that the codec + (which is in a seperate abstraction + layer) also knows about the gap */ + ogg_int64_t granulepos; + +} ogg_stream_state; + +/* ogg_packet is used to encapsulate the data and metadata belonging + to a single raw Ogg/Vorbis packet *************************************/ + +typedef struct { + unsigned char *packet; + long bytes; + long b_o_s; + long e_o_s; + + ogg_int64_t granulepos; + + ogg_int64_t packetno; /* sequence number for decode; the framing + knows where there's a hole in the data, + but we need coupling so that the codec + (which is in a seperate abstraction + layer) also knows about the gap */ +} ogg_packet; + +typedef struct { + unsigned char *data; + int storage; + int fill; + int returned; + + int unsynced; + int headerbytes; + int bodybytes; +} ogg_sync_state; + +/* Ogg BITSTREAM PRIMITIVES: bitstream ************************/ + +extern void oggpack_writeinit(oggpack_buffer *b); +extern void oggpack_writetrunc(oggpack_buffer *b,long bits); +extern void oggpack_writealign(oggpack_buffer *b); +extern void oggpack_writecopy(oggpack_buffer *b,void *source,long bits); +extern void oggpack_reset(oggpack_buffer *b); +extern void oggpack_writeclear(oggpack_buffer *b); +extern void oggpack_readinit(oggpack_buffer *b,unsigned char *buf,int bytes); +extern void oggpack_write(oggpack_buffer *b,unsigned long value,int bits); +extern long oggpack_look(oggpack_buffer *b,int bits); +extern long oggpack_look1(oggpack_buffer *b); +extern void oggpack_adv(oggpack_buffer *b,int bits); +extern void oggpack_adv1(oggpack_buffer *b); +extern long oggpack_read(oggpack_buffer *b,int bits); +extern long oggpack_read1(oggpack_buffer *b); +extern long oggpack_bytes(oggpack_buffer *b); +extern long oggpack_bits(oggpack_buffer *b); +extern unsigned char *oggpack_get_buffer(oggpack_buffer *b); + +extern void oggpackB_writeinit(oggpack_buffer *b); +extern void oggpackB_writetrunc(oggpack_buffer *b,long bits); +extern void oggpackB_writealign(oggpack_buffer *b); +extern void oggpackB_writecopy(oggpack_buffer *b,void *source,long bits); +extern void oggpackB_reset(oggpack_buffer *b); +extern void oggpackB_writeclear(oggpack_buffer *b); +extern void oggpackB_readinit(oggpack_buffer *b,unsigned char *buf,int bytes); +extern void oggpackB_write(oggpack_buffer *b,unsigned long value,int bits); +extern long oggpackB_look(oggpack_buffer *b,int bits); +extern long oggpackB_look1(oggpack_buffer *b); +extern void oggpackB_adv(oggpack_buffer *b,int bits); +extern void oggpackB_adv1(oggpack_buffer *b); +extern long oggpackB_read(oggpack_buffer *b,int bits); +extern long oggpackB_read1(oggpack_buffer *b); +extern long oggpackB_bytes(oggpack_buffer *b); +extern long oggpackB_bits(oggpack_buffer *b); +extern unsigned char *oggpackB_get_buffer(oggpack_buffer *b); + +/* Ogg BITSTREAM PRIMITIVES: encoding **************************/ + +extern int ogg_stream_packetin(ogg_stream_state *os, ogg_packet *op); +extern int ogg_stream_pageout(ogg_stream_state *os, ogg_page *og); +extern int ogg_stream_flush(ogg_stream_state *os, ogg_page *og); + +/* Ogg BITSTREAM PRIMITIVES: decoding **************************/ + +extern int ogg_sync_init(ogg_sync_state *oy); +extern int ogg_sync_clear(ogg_sync_state *oy); +extern int ogg_sync_reset(ogg_sync_state *oy); +extern int ogg_sync_destroy(ogg_sync_state *oy); + +extern char *ogg_sync_buffer(ogg_sync_state *oy, long size); +extern int ogg_sync_wrote(ogg_sync_state *oy, long bytes); +extern long ogg_sync_pageseek(ogg_sync_state *oy,ogg_page *og); +extern int ogg_sync_pageout(ogg_sync_state *oy, ogg_page *og); +extern int ogg_stream_pagein(ogg_stream_state *os, ogg_page *og); +extern int ogg_stream_packetout(ogg_stream_state *os,ogg_packet *op); +extern int ogg_stream_packetpeek(ogg_stream_state *os,ogg_packet *op); + +/* Ogg BITSTREAM PRIMITIVES: general ***************************/ + +extern int ogg_stream_init(ogg_stream_state *os,int serialno); +extern int ogg_stream_clear(ogg_stream_state *os); +extern int ogg_stream_reset(ogg_stream_state *os); +extern int ogg_stream_reset_serialno(ogg_stream_state *os,int serialno); +extern int ogg_stream_destroy(ogg_stream_state *os); +extern int ogg_stream_eos(ogg_stream_state *os); + +extern void ogg_page_checksum_set(ogg_page *og); + +extern int ogg_page_version(ogg_page *og); +extern int ogg_page_continued(ogg_page *og); +extern int ogg_page_bos(ogg_page *og); +extern int ogg_page_eos(ogg_page *og); +extern ogg_int64_t ogg_page_granulepos(ogg_page *og); +extern int ogg_page_serialno(ogg_page *og); +extern long ogg_page_pageno(ogg_page *og); +extern int ogg_page_packets(ogg_page *og); + +extern void ogg_packet_clear(ogg_packet *op); + + +#ifdef __cplusplus +} +#endif + +#endif /* _OGG_H */ + + + + + + diff --git a/src/OGG/include/ogg/os_types.h b/src/OGG/include/ogg/os_types.h new file mode 100644 index 00000000..32dcb8bf --- /dev/null +++ b/src/OGG/include/ogg/os_types.h @@ -0,0 +1,127 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: #ifdef jail to whip a few platforms into the UNIX ideal. + last mod: $Id: os_types.h 7524 2004-08-11 04:20:36Z conrad $ + + ********************************************************************/ +#ifndef _OS_TYPES_H +#define _OS_TYPES_H + +/* make it easy on the folks that want to compile the libs with a + different malloc than stdlib */ +#define _ogg_malloc malloc +#define _ogg_calloc calloc +#define _ogg_realloc realloc +#define _ogg_free free + +#if defined(_WIN32) + +# if defined(__CYGWIN__) +# include <_G_config.h> + typedef _G_int64_t ogg_int64_t; + typedef _G_int32_t ogg_int32_t; + typedef _G_uint32_t ogg_uint32_t; + typedef _G_int16_t ogg_int16_t; + typedef _G_uint16_t ogg_uint16_t; +# elif defined(__MINGW32__) + typedef short ogg_int16_t; + typedef unsigned short ogg_uint16_t; + typedef int ogg_int32_t; + typedef unsigned int ogg_uint32_t; + typedef long long ogg_int64_t; + typedef unsigned long long ogg_uint64_t; +# elif defined(__MWERKS__) + typedef long long ogg_int64_t; + typedef int ogg_int32_t; + typedef unsigned int ogg_uint32_t; + typedef short ogg_int16_t; + typedef unsigned short ogg_uint16_t; +# else + /* MSVC/Borland */ + typedef __int64 ogg_int64_t; + typedef __int32 ogg_int32_t; + typedef unsigned __int32 ogg_uint32_t; + typedef __int16 ogg_int16_t; + typedef unsigned __int16 ogg_uint16_t; +# endif + +#elif defined(__MACOS__) + +# include + typedef SInt16 ogg_int16_t; + typedef UInt16 ogg_uint16_t; + typedef SInt32 ogg_int32_t; + typedef UInt32 ogg_uint32_t; + typedef SInt64 ogg_int64_t; + +#elif defined(__MACOSX__) /* MacOS X Framework build */ + +# include + typedef int16_t ogg_int16_t; + typedef u_int16_t ogg_uint16_t; + typedef int32_t ogg_int32_t; + typedef u_int32_t ogg_uint32_t; + typedef int64_t ogg_int64_t; + +#elif defined(__BEOS__) + + /* Be */ +# include + typedef int16_t ogg_int16_t; + typedef u_int16_t ogg_uint16_t; + typedef int32_t ogg_int32_t; + typedef u_int32_t ogg_uint32_t; + typedef int64_t ogg_int64_t; + +#elif defined (__EMX__) + + /* OS/2 GCC */ + typedef short ogg_int16_t; + typedef unsigned short ogg_uint16_t; + typedef int ogg_int32_t; + typedef unsigned int ogg_uint32_t; + typedef long long ogg_int64_t; + +#elif defined (DJGPP) + + /* DJGPP */ + typedef short ogg_int16_t; + typedef int ogg_int32_t; + typedef unsigned int ogg_uint32_t; + typedef long long ogg_int64_t; + +#elif defined(R5900) + + /* PS2 EE */ + typedef long ogg_int64_t; + typedef int ogg_int32_t; + typedef unsigned ogg_uint32_t; + typedef short ogg_int16_t; + +#elif defined(__SYMBIAN32__) + + /* Symbian GCC */ + typedef signed short ogg_int16_t; + typedef unsigned short ogg_uint16_t; + typedef signed int ogg_int32_t; + typedef unsigned int ogg_uint32_t; + typedef long long int ogg_int64_t; + +#else + +# include +# include + +#endif + +#endif /* _OS_TYPES_H */ diff --git a/src/aiff.c b/src/aiff.c new file mode 100644 index 00000000..63484772 --- /dev/null +++ b/src/aiff.c @@ -0,0 +1,1522 @@ +/* +** Copyright (C) 1999-2006 Erik de Castro Lopo +** Copyright (C) 2005 David Viens +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include +#include +#include + +#include "sndfile.h" +#include "sfendian.h" +#include "common.h" + +/*------------------------------------------------------------------------------ + * Macros to handle big/little endian issues. + */ + +#define FORM_MARKER (MAKE_MARKER ('F', 'O', 'R', 'M')) +#define AIFF_MARKER (MAKE_MARKER ('A', 'I', 'F', 'F')) +#define AIFC_MARKER (MAKE_MARKER ('A', 'I', 'F', 'C')) +#define COMM_MARKER (MAKE_MARKER ('C', 'O', 'M', 'M')) +#define SSND_MARKER (MAKE_MARKER ('S', 'S', 'N', 'D')) +#define MARK_MARKER (MAKE_MARKER ('M', 'A', 'R', 'K')) +#define INST_MARKER (MAKE_MARKER ('I', 'N', 'S', 'T')) +#define APPL_MARKER (MAKE_MARKER ('A', 'P', 'P', 'L')) + +#define c_MARKER (MAKE_MARKER ('(', 'c', ')', ' ')) +#define NAME_MARKER (MAKE_MARKER ('N', 'A', 'M', 'E')) +#define AUTH_MARKER (MAKE_MARKER ('A', 'U', 'T', 'H')) +#define ANNO_MARKER (MAKE_MARKER ('A', 'N', 'N', 'O')) +#define COMT_MARKER (MAKE_MARKER ('C', 'O', 'M', 'T')) +#define FVER_MARKER (MAKE_MARKER ('F', 'V', 'E', 'R')) +#define SFX_MARKER (MAKE_MARKER ('S', 'F', 'X', '!')) + +#define PEAK_MARKER (MAKE_MARKER ('P', 'E', 'A', 'K')) +#define basc_MARKER (MAKE_MARKER ('b', 'a', 's', 'c')) + +/* Supported AIFC encodings.*/ +#define NONE_MARKER (MAKE_MARKER ('N', 'O', 'N', 'E')) +#define sowt_MARKER (MAKE_MARKER ('s', 'o', 'w', 't')) +#define twos_MARKER (MAKE_MARKER ('t', 'w', 'o', 's')) +#define raw_MARKER (MAKE_MARKER ('r', 'a', 'w', ' ')) +#define in24_MARKER (MAKE_MARKER ('i', 'n', '2', '4')) +#define ni24_MARKER (MAKE_MARKER ('4', '2', 'n', '1')) +#define in32_MARKER (MAKE_MARKER ('i', 'n', '3', '2')) +#define ni32_MARKER (MAKE_MARKER ('2', '3', 'n', 'i')) + +#define fl32_MARKER (MAKE_MARKER ('f', 'l', '3', '2')) +#define FL32_MARKER (MAKE_MARKER ('F', 'L', '3', '2')) +#define fl64_MARKER (MAKE_MARKER ('f', 'l', '6', '4')) +#define FL64_MARKER (MAKE_MARKER ('F', 'L', '6', '4')) + +#define ulaw_MARKER (MAKE_MARKER ('u', 'l', 'a', 'w')) +#define ULAW_MARKER (MAKE_MARKER ('U', 'L', 'A', 'W')) +#define alaw_MARKER (MAKE_MARKER ('a', 'l', 'a', 'w')) +#define ALAW_MARKER (MAKE_MARKER ('A', 'L', 'A', 'W')) + +#define DWVW_MARKER (MAKE_MARKER ('D', 'W', 'V', 'W')) +#define GSM_MARKER (MAKE_MARKER ('G', 'S', 'M', ' ')) +#define ima4_MARKER (MAKE_MARKER ('i', 'm', 'a', '4')) + +/* Unsupported AIFC encodings.*/ + +#define MAC3_MARKER (MAKE_MARKER ('M', 'A', 'C', '3')) +#define MAC6_MARKER (MAKE_MARKER ('M', 'A', 'C', '6')) +#define ADP4_MARKER (MAKE_MARKER ('A', 'D', 'P', '4')) + +/* Predfined chunk sizes. */ +#define SIZEOF_AIFF_COMM 18 +#define SIZEOF_AIFC_COMM_MIN 22 +#define SIZEOF_AIFC_COMM 24 +#define SIZEOF_SSND_CHUNK 8 +#define SIZEOF_INST_CHUNK 20 + +/* Is it constant? */ + +/* AIFC/IMA4 defines. */ +#define AIFC_IMA4_BLOCK_LEN 34 +#define AIFC_IMA4_SAMPLES_PER_BLOCK 64 + +#define AIFF_PEAK_CHUNK_SIZE(ch) (2 * sizeof (int) + ch * (sizeof (float) + sizeof (int))) + +/*------------------------------------------------------------------------------ + * Typedefs for file chunks. + */ + +enum +{ HAVE_FORM = 0x01, + HAVE_AIFF = 0x02, + HAVE_AIFC = 0x04, + HAVE_FVER = 0x08, + HAVE_COMM = 0x10, + HAVE_SSND = 0x20 +} ; + +typedef struct +{ unsigned int size ; + short numChannels ; + unsigned int numSampleFrames ; + short sampleSize ; + unsigned char sampleRate [10] ; + unsigned int encoding ; + char zero_bytes [2] ; +} COMM_CHUNK ; + +typedef struct +{ unsigned int offset ; + unsigned int blocksize ; +} SSND_CHUNK ; + +typedef struct +{ short playMode ; + unsigned short beginLoop ; + unsigned short endLoop ; +} INST_LOOP ; + +typedef struct +{ char baseNote ; /* all notes are MIDI note numbers */ + char detune ; /* cents off, only -50 to +50 are significant */ + char lowNote ; + char highNote ; + char lowVelocity ; /* 1 to 127 */ + char highVelocity ; /* 1 to 127 */ + short gain ; /* in dB, 0 is normal */ + INST_LOOP sustain_loop ; + INST_LOOP release_loop ; +} INST_CHUNK ; + + +enum +{ basc_SCALE_MINOR = 1, + basc_SCALE_MAJOR, + basc_SCALE_NEITHER, + basc_SCALE_BOTH +} ; + +enum +{ basc_TYPE_LOOP = 0, + basc_TYPE_ONE_SHOT +} ; + + +typedef struct +{ unsigned int version ; + unsigned int numBeats ; + unsigned short rootNote ; + unsigned short scaleType ; + unsigned short sigNumerator ; + unsigned short sigDenominator ; + unsigned short loopType ; +} basc_CHUNK ; + +typedef struct +{ unsigned short markerID ; + unsigned int position ; +} MARK_ID_POS ; + +typedef struct +{ sf_count_t comm_offset ; + sf_count_t ssnd_offset ; +} AIFF_PRIVATE ; + +/*------------------------------------------------------------------------------ + * Private static functions. + */ + +static int aiff_close (SF_PRIVATE *psf) ; + +static int tenbytefloat2int (unsigned char *bytes) ; +static void uint2tenbytefloat (unsigned int num, unsigned char *bytes) ; + +static int aiff_read_comm_chunk (SF_PRIVATE *psf, COMM_CHUNK *comm_fmt) ; + +static int aiff_read_header (SF_PRIVATE *psf, COMM_CHUNK *comm_fmt) ; + +static int aiff_write_header (SF_PRIVATE *psf, int calc_length) ; +static int aiff_write_tailer (SF_PRIVATE *psf) ; +static void aiff_write_strings (SF_PRIVATE *psf, int location) ; + +static int aiff_command (SF_PRIVATE *psf, int command, void *data, int datasize) ; + +static const char *get_loop_mode_str (short mode) ; + +static short get_loop_mode (short mode) ; + +static int aiff_read_basc_chunk (SF_PRIVATE * psf, int) ; + +static unsigned int marker_to_position (const MARK_ID_POS *m, unsigned short n, int marksize) ; + +/*------------------------------------------------------------------------------ +** Public function. +*/ + +int +aiff_open (SF_PRIVATE *psf) +{ COMM_CHUNK comm_fmt ; + int error, subformat ; + + memset (&comm_fmt, 0, sizeof (comm_fmt)) ; + + subformat = psf->sf.format & SF_FORMAT_SUBMASK ; + + if ((psf->container_data = calloc (1, sizeof (AIFF_PRIVATE))) == NULL) + return SFE_MALLOC_FAILED ; + + if (psf->mode == SFM_READ || (psf->mode == SFM_RDWR && psf->filelength > 0)) + { if ((error = aiff_read_header (psf, &comm_fmt))) + return error ; + + psf_fseek (psf, psf->dataoffset, SEEK_SET) ; + } ; + + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + { if (psf->is_pipe) + return SFE_NO_PIPE_WRITE ; + + if ((psf->sf.format & SF_FORMAT_TYPEMASK) != SF_FORMAT_AIFF) + return SFE_BAD_OPEN_FORMAT ; + + if (psf->mode == SFM_WRITE && (subformat == SF_FORMAT_FLOAT || subformat == SF_FORMAT_DOUBLE)) + { if ((psf->peak_info = peak_info_calloc (psf->sf.channels)) == NULL) + return SFE_MALLOC_FAILED ; + psf->peak_info->peak_loc = SF_PEAK_START ; + } ; + + if (psf->mode != SFM_RDWR || psf->filelength < 40) + { psf->filelength = 0 ; + psf->datalength = 0 ; + psf->dataoffset = 0 ; + psf->sf.frames = 0 ; + } ; + + psf->str_flags = SF_STR_ALLOW_START | SF_STR_ALLOW_END ; + + if ((error = aiff_write_header (psf, SF_FALSE))) + return error ; + + psf->write_header = aiff_write_header ; + } ; + + psf->container_close = aiff_close ; + psf->command = aiff_command ; + + switch (psf->sf.format & SF_FORMAT_SUBMASK) + { case SF_FORMAT_PCM_U8 : + error = pcm_init (psf) ; + break ; + + case SF_FORMAT_PCM_S8 : + error = pcm_init (psf) ; + break ; + + case SF_FORMAT_PCM_16 : + case SF_FORMAT_PCM_24 : + case SF_FORMAT_PCM_32 : + error = pcm_init (psf) ; + break ; + + case SF_FORMAT_ULAW : + error = ulaw_init (psf) ; + break ; + + case SF_FORMAT_ALAW : + error = alaw_init (psf) ; + break ; + + /* Lite remove start */ + case SF_FORMAT_FLOAT : + error = float32_init (psf) ; + break ; + + case SF_FORMAT_DOUBLE : + error = double64_init (psf) ; + break ; + + case SF_FORMAT_DWVW_12 : + error = dwvw_init (psf, 12) ; + break ; + + case SF_FORMAT_DWVW_16 : + error = dwvw_init (psf, 16) ; + break ; + + case SF_FORMAT_DWVW_24 : + error = dwvw_init (psf, 24) ; + break ; + + case SF_FORMAT_DWVW_N : + if (psf->mode != SFM_READ) + { error = SFE_DWVW_BAD_BITWIDTH ; + break ; + } ; + if (comm_fmt.sampleSize >= 8 && comm_fmt.sampleSize < 24) + { error = dwvw_init (psf, comm_fmt.sampleSize) ; + psf->sf.frames = comm_fmt.numSampleFrames ; + break ; + } ; + psf_log_printf (psf, "AIFC/DWVW : Bad bitwidth %d\n", comm_fmt.sampleSize) ; + error = SFE_DWVW_BAD_BITWIDTH ; + break ; + + case SF_FORMAT_IMA_ADPCM : + /* + ** IMA ADPCM encoded AIFF files always have a block length + ** of 34 which decodes to 64 samples. + */ + error = aiff_ima_init (psf, AIFC_IMA4_BLOCK_LEN, AIFC_IMA4_SAMPLES_PER_BLOCK) ; + break ; + /* Lite remove end */ + + case SF_FORMAT_GSM610 : + error = gsm610_init (psf) ; + break ; + + default : return SFE_UNIMPLEMENTED ; + } ; + + + return error ; +} /* aiff_open */ + +/*========================================================================================== +** Private functions. +*/ + +/* This function ought to check size */ +static unsigned int +marker_to_position (const MARK_ID_POS *m, unsigned short n, int marksize) +{ int i ; + + for (i = 0 ; i < marksize ; i++) + if (m [i].markerID == n) + return m [i].position ; + return 0 ; +} /* marker_to_position */ + +static int +aiff_read_header (SF_PRIVATE *psf, COMM_CHUNK *comm_fmt) +{ SSND_CHUNK ssnd_fmt ; + MARK_ID_POS *markstr = NULL ; + AIFF_PRIVATE *paiff ; + unsigned marker, dword, FORMsize, SSNDsize, bytesread ; + int k, found_chunk = 0, done = 0, error = 0 ; + char *cptr, byte ; + int instr_found = 0, mark_found = 0, mark_count = 0 ; + + if (psf->filelength > SF_PLATFORM_S64 (0xffffffff)) + psf_log_printf (psf, "Warning : filelength > 0xffffffff. This is bad!!!!\n") ; + + if ((paiff = psf->container_data) == NULL) + return SFE_INTERNAL ; + + paiff->comm_offset = 0 ; + paiff->ssnd_offset = 0 ; + + /* Set position to start of file to begin reading header. */ + psf_binheader_readf (psf, "p", 0) ; + + memset (comm_fmt, 0, sizeof (COMM_CHUNK)) ; + + /* Until recently AIF* file were all BIG endian. */ + psf->endian = SF_ENDIAN_BIG ; + + /* AIFF files can apparently have their chunks in any order. However, they + ** must have a FORM chunk. Approach here is to read all the chunks one by + ** one and then check for the mandatory chunks at the end. + */ + while (! done) + { psf_binheader_readf (psf, "m", &marker) ; + + if (psf->mode == SFM_RDWR && (found_chunk & HAVE_SSND)) + return SFE_AIFF_RW_SSND_NOT_LAST ; + + switch (marker) + { case FORM_MARKER : + if (found_chunk) + return SFE_AIFF_NO_FORM ; + + psf_binheader_readf (psf, "E4", &FORMsize) ; + + if (psf->fileoffset > 0 && psf->filelength > FORMsize + 8) + { /* Set file length. */ + psf->filelength = FORMsize + 8 ; + psf_log_printf (psf, "FORM : %u\n", FORMsize) ; + } + else if (FORMsize != psf->filelength - 2 * SIGNED_SIZEOF (dword)) + { dword = psf->filelength - 2 * sizeof (dword) ; + psf_log_printf (psf, "FORM : %u (should be %u)\n", FORMsize, dword) ; + FORMsize = dword ; + } + else + psf_log_printf (psf, "FORM : %u\n", FORMsize) ; + found_chunk |= HAVE_FORM ; + break ; + + case AIFC_MARKER : + case AIFF_MARKER : + if ((found_chunk & HAVE_FORM) == 0) + return SFE_AIFF_AIFF_NO_FORM ; + psf_log_printf (psf, " %M\n", marker) ; + found_chunk |= (marker == AIFC_MARKER) ? (HAVE_AIFC | HAVE_AIFF) : HAVE_AIFF ; + break ; + + case COMM_MARKER : + paiff->comm_offset = psf_ftell (psf) - 4 ; + error = aiff_read_comm_chunk (psf, comm_fmt) ; + + psf->sf.samplerate = tenbytefloat2int (comm_fmt->sampleRate) ; + psf->sf.frames = comm_fmt->numSampleFrames ; + psf->sf.channels = comm_fmt->numChannels ; + psf->bytewidth = BITWIDTH2BYTES (comm_fmt->sampleSize) ; + + if (error) + return error ; + + found_chunk |= HAVE_COMM ; + break ; + + case PEAK_MARKER : + /* Must have COMM chunk before PEAK chunk. */ + if ((found_chunk & (HAVE_FORM | HAVE_AIFF | HAVE_COMM)) != (HAVE_FORM | HAVE_AIFF | HAVE_COMM)) + return SFE_AIFF_PEAK_B4_COMM ; + + psf_binheader_readf (psf, "E4", &dword) ; + + psf_log_printf (psf, "%M : %d\n", marker, dword) ; + if (dword != AIFF_PEAK_CHUNK_SIZE (psf->sf.channels)) + { psf_binheader_readf (psf, "j", dword) ; + psf_log_printf (psf, "*** File PEAK chunk too big.\n") ; + return SFE_WAV_BAD_PEAK ; + } ; + + if ((psf->peak_info = peak_info_calloc (psf->sf.channels)) == NULL) + return SFE_MALLOC_FAILED ; + + /* read in rest of PEAK chunk. */ + psf_binheader_readf (psf, "E44", &(psf->peak_info->version), &(psf->peak_info->timestamp)) ; + + if (psf->peak_info->version != 1) + psf_log_printf (psf, " version : %d *** (should be version 1)\n", psf->peak_info->version) ; + else + psf_log_printf (psf, " version : %d\n", psf->peak_info->version) ; + + psf_log_printf (psf, " time stamp : %d\n", psf->peak_info->timestamp) ; + psf_log_printf (psf, " Ch Position Value\n") ; + + cptr = psf->u.cbuf ; + for (dword = 0 ; dword < (unsigned) psf->sf.channels ; dword++) + { float value ; + unsigned int position ; + + psf_binheader_readf (psf, "Ef4", &value, &position) ; + psf->peak_info->peaks [dword].value = value ; + psf->peak_info->peaks [dword].position = position ; + + LSF_SNPRINTF (cptr, sizeof (psf->u.scbuf), " %2d %-12ld %g\n", + dword, (long) psf->peak_info->peaks [dword].position, psf->peak_info->peaks [dword].value) ; + cptr [sizeof (psf->u.scbuf) - 1] = 0 ; + psf_log_printf (psf, cptr) ; + } ; + + break ; + + case SSND_MARKER : + if ((found_chunk & HAVE_AIFC) && (found_chunk & HAVE_FVER) == 0) + psf_log_printf (psf, "*** Valid AIFC files should have an FVER chunk.\n") ; + + paiff->ssnd_offset = psf_ftell (psf) - 4 ; + psf_binheader_readf (psf, "E444", &SSNDsize, &(ssnd_fmt.offset), &(ssnd_fmt.blocksize)) ; + + psf->datalength = SSNDsize - sizeof (ssnd_fmt) ; + psf->dataoffset = psf_ftell (psf) ; + + if (psf->datalength > psf->filelength - psf->dataoffset || psf->datalength < 0) + { psf_log_printf (psf, " SSND : %u (should be %D)\n", SSNDsize, psf->filelength - psf->dataoffset + sizeof (SSND_CHUNK)) ; + psf->datalength = psf->filelength - psf->dataoffset ; + } + else + psf_log_printf (psf, " SSND : %u\n", SSNDsize) ; + + /* Only set dataend if there really is data at the end. */ + if (psf->datalength + psf->dataoffset < psf->filelength) + psf->dataend = psf->datalength + psf->dataoffset ; + + psf_log_printf (psf, " Offset : %u\n", ssnd_fmt.offset) ; + psf_log_printf (psf, " Block Size : %u\n", ssnd_fmt.blocksize) ; + + found_chunk |= HAVE_SSND ; + + if (! psf->sf.seekable) + break ; + + /* Seek to end of SSND chunk. */ + psf_fseek (psf, psf->dataoffset + psf->datalength + (SSNDsize & 1), SEEK_SET) ; + break ; + + case c_MARKER : + psf_binheader_readf (psf, "E4", &dword) ; + if (dword == 0) + break ; + if (dword > SIGNED_SIZEOF (psf->u.scbuf) - 1) + { psf_log_printf (psf, " %M : %d (too big)\n", marker, dword) ; + return SFE_INTERNAL ; + } ; + + cptr = psf->u.cbuf ; + psf_binheader_readf (psf, "b", cptr, dword + (dword & 1)) ; + cptr [dword] = 0 ; + + psf_sanitize_string (cptr, dword) ; + + psf_log_printf (psf, " %M : %s\n", marker, cptr) ; + psf_store_string (psf, SF_STR_COPYRIGHT, cptr) ; + break ; + + case AUTH_MARKER : + psf_binheader_readf (psf, "E4", &dword) ; + if (dword == 0) + break ; + if (dword > SIGNED_SIZEOF (psf->u.scbuf) - 1) + { psf_log_printf (psf, " %M : %d (too big)\n", marker, dword) ; + return SFE_INTERNAL ; + } ; + + cptr = psf->u.cbuf ; + psf_binheader_readf (psf, "b", cptr, dword + (dword & 1)) ; + cptr [dword] = 0 ; + psf_log_printf (psf, " %M : %s\n", marker, cptr) ; + psf_store_string (psf, SF_STR_ARTIST, cptr) ; + break ; + + case COMT_MARKER : + { unsigned short count, id, len ; + unsigned int timestamp ; + + psf_binheader_readf (psf, "E42", &dword, &count) ; + psf_log_printf (psf, " %M : %d\n count : %d\n", marker, dword, count) ; + dword += (dword & 1) ; + if (dword == 0) + break ; + dword -= 2 ; + + for (k = 0 ; k < count ; k++) + { dword -= psf_binheader_readf (psf, "E422", ×tamp, &id, &len) ; + psf_log_printf (psf, " time : 0x%x\n marker : %x\n length : %d\n", timestamp, id, len) ; + + if (len + 1 > SIGNED_SIZEOF (psf->u.scbuf)) + { psf_log_printf (psf, "\nError : string length (%d) too big.\n", len) ; + return SFE_INTERNAL ; + } ; + + cptr = psf->u.cbuf ; + dword -= psf_binheader_readf (psf, "b", cptr, len) ; + cptr [len] = 0 ; + psf_log_printf (psf, " string : %s\n", cptr) ; + } ; + + if (dword > 0) + psf_binheader_readf (psf, "j", dword) ; + } ; + break ; + + case APPL_MARKER : + psf_binheader_readf (psf, "E4", &dword) ; + if (dword == 0) + break ; + if (dword >= SIGNED_SIZEOF (psf->u.scbuf) - 1) + { psf_log_printf (psf, " %M : %d (too big, skipping)\n", marker, dword) ; + psf_binheader_readf (psf, "j", dword + (dword & 1)) ; + break ; + } ; + + cptr = psf->u.cbuf ; + psf_binheader_readf (psf, "b", cptr, dword + (dword & 1)) ; + cptr [dword] = 0 ; + + for (k = 0 ; k < (int) dword ; k++) + if (! isprint (cptr [k])) + { cptr [k] = 0 ; + break ; + } ; + + psf_log_printf (psf, " %M : %s\n", marker, cptr) ; + psf_store_string (psf, SF_STR_SOFTWARE, cptr) ; + break ; + + case NAME_MARKER : + psf_binheader_readf (psf, "E4", &dword) ; + if (dword == 0) + break ; + if (dword > SIGNED_SIZEOF (psf->u.scbuf) - 2) + { psf_log_printf (psf, " %M : %d (too big)\n", marker, dword) ; + return SFE_INTERNAL ; + } ; + + cptr = psf->u.cbuf ; + psf_binheader_readf (psf, "b", cptr, dword + (dword & 1)) ; + cptr [dword] = 0 ; + psf_log_printf (psf, " %M : %s\n", marker, cptr) ; + psf_store_string (psf, SF_STR_TITLE, cptr) ; + break ; + + case ANNO_MARKER : + psf_binheader_readf (psf, "E4", &dword) ; + if (dword == 0) + break ; + if (dword > SIGNED_SIZEOF (psf->u.scbuf) - 2) + { psf_log_printf (psf, " %M : %d (too big)\n", marker, dword) ; + return SFE_INTERNAL ; + } ; + + cptr = psf->u.cbuf ; + psf_binheader_readf (psf, "b", cptr, dword + (dword & 1)) ; + cptr [dword] = 0 ; + psf_log_printf (psf, " %M : %s\n", marker, cptr) ; + psf_store_string (psf, SF_STR_COMMENT, cptr) ; + break ; + + case INST_MARKER : + psf_binheader_readf (psf, "E4", &dword) ; + if (dword != SIZEOF_INST_CHUNK) + { psf_log_printf (psf, " %M : %d (should be %d)\n", marker, dword, SIZEOF_INST_CHUNK) ; + psf_binheader_readf (psf, "j", dword) ; + break ; + } ; + psf_log_printf (psf, " %M : %d\n", marker, dword) ; + { unsigned char bytes [6] ; + short gain ; + + if (psf->instrument == NULL && (psf->instrument = psf_instrument_alloc ()) == NULL) + return SFE_MALLOC_FAILED ; + + psf_binheader_readf (psf, "b", bytes, 6) ; + psf_log_printf (psf, " Base Note : %u\n Detune : %u\n" + " Low Note : %u\n High Note : %u\n" + " Low Vel. : %u\n High Vel. : %u\n", + bytes [0], bytes [1], bytes [2], bytes [3], bytes [4], bytes [5]) ; + psf->instrument->basenote = bytes [0] ; + psf->instrument->detune = bytes [1] ; + psf->instrument->key_lo = bytes [2] ; + psf->instrument->key_hi = bytes [3] ; + psf->instrument->velocity_lo = bytes [4] ; + psf->instrument->velocity_hi = bytes [5] ; + psf_binheader_readf (psf, "E2", &gain) ; + psf->instrument->gain = gain ; + psf_log_printf (psf, " Gain (dB) : %d\n", gain) ; + } ; + { short mode ; /* 0 - no loop, 1 - forward looping, 2 - backward looping */ + const char *loop_mode ; + unsigned short begin, end ; + + psf_binheader_readf (psf, "E222", &mode, &begin, &end) ; + loop_mode = get_loop_mode_str (mode) ; + mode = get_loop_mode (mode) ; + if (mode == SF_LOOP_NONE) + { psf->instrument->loop_count = 0 ; + psf->instrument->loops [0].mode = SF_LOOP_NONE ; + } + else + { psf->instrument->loop_count = 1 ; + psf->instrument->loops [0].mode = SF_LOOP_FORWARD ; + psf->instrument->loops [0].start = begin ; + psf->instrument->loops [0].end = end ; + psf->instrument->loops [0].count = 0 ; + } ; + psf_log_printf (psf, " Sustain\n mode : %d => %s\n begin : %u\n end : %u\n", + mode, loop_mode, begin, end) ; + psf_binheader_readf (psf, "E222", &mode, &begin, &end) ; + loop_mode = get_loop_mode_str (mode) ; + mode = get_loop_mode (mode) ; + if (mode == SF_LOOP_NONE) + psf->instrument->loops [0].mode = SF_LOOP_NONE ; + else + { psf->instrument->loop_count += 1 ; + psf->instrument->loops [1].mode = SF_LOOP_FORWARD ; + psf->instrument->loops [1].start = begin ; + psf->instrument->loops [1].end = end ; + psf->instrument->loops [1].count = 0 ; + } ; + psf_log_printf (psf, " Release\n mode : %d => %s\n begin : %u\n end : %u\n", + mode, loop_mode, begin, end) ; + } ; + instr_found++ ; + break ; + + case basc_MARKER : + psf_binheader_readf (psf, "E4", &dword) ; + psf_log_printf (psf, " basc : %u\n", dword) ; + + if ((error = aiff_read_basc_chunk (psf, dword))) + return error ; + break ; + + case MARK_MARKER : + psf_binheader_readf (psf, "E4", &dword) ; + psf_log_printf (psf, " %M : %d\n", marker, dword) ; + { unsigned short mark_id, n = 0 ; + unsigned char pstr_len ; + unsigned int position ; + + bytesread = psf_binheader_readf (psf, "E2", &n) ; + mark_count = n ; + markstr = calloc (mark_count, sizeof (MARK_ID_POS)) ; + psf_log_printf (psf, " Count : %d\n", mark_count) ; + + for (n = 0 ; n < mark_count && bytesread < dword ; n++) + { bytesread += psf_binheader_readf (psf, "E241", &mark_id, &position, &pstr_len) ; + psf_log_printf (psf, " Mark ID : %u\n Position : %u\n", mark_id, position) ; + + pstr_len += (pstr_len & 1) ? 0 : 1 ; + + bytesread += psf_binheader_readf (psf, "b", psf->u.scbuf, pstr_len) ; + psf->u.scbuf [pstr_len] = 0 ; + psf_log_printf (psf, " Name : %s\n", psf->u.scbuf) ; + + markstr [n].markerID = mark_id ; + markstr [n].position = position ; + /* + ** TODO if psf->u.scbuf is equal to + ** either Beg_loop, Beg loop or beg loop and spam + ** if (psf->instrument == NULL && (psf->instrument = psf_instrument_alloc ()) == NULL) + ** return SFE_MALLOC_FAILED ; + */ + } ; + } ; + mark_found++ ; + psf_binheader_readf (psf, "j", dword - bytesread) ; + break ; + + case FVER_MARKER : + found_chunk |= HAVE_FVER ; + /* Fall through to next case. */ + + case SFX_MARKER : + psf_binheader_readf (psf, "E4", &dword) ; + psf_log_printf (psf, " %M : %d\n", marker, dword) ; + + psf_binheader_readf (psf, "j", dword) ; + break ; + + case NONE_MARKER : + /* Fix for broken AIFC files with incorrect COMM chunk length. */ + psf_binheader_readf (psf, "1", &byte) ; + dword = byte ; + psf_binheader_readf (psf, "j", dword) ; + break ; + + default : + if (isprint ((marker >> 24) & 0xFF) && isprint ((marker >> 16) & 0xFF) + && isprint ((marker >> 8) & 0xFF) && isprint (marker & 0xFF)) + { psf_binheader_readf (psf, "E4", &dword) ; + psf_log_printf (psf, " %M : %d (unknown marker)\n", marker, dword) ; + + psf_binheader_readf (psf, "j", dword) ; + break ; + } ; + if ((dword = psf_ftell (psf)) & 0x03) + { psf_log_printf (psf, " Unknown chunk marker %X at position %d. Resyncing.\n", marker, dword - 4) ; + + psf_binheader_readf (psf, "j", -3) ; + break ; + } ; + psf_log_printf (psf, "*** Unknown chunk marker %X at position %D. Exiting parser.\n", marker, psf_ftell (psf)) ; + done = 1 ; + break ; + } ; /* switch (marker) */ + + if ((! psf->sf.seekable) && (found_chunk & HAVE_SSND)) + break ; + + if (psf_ftell (psf) >= psf->filelength - (2 * SIGNED_SIZEOF (dword))) + break ; + } ; /* while (1) */ + + if (instr_found && mark_found) + { int j ; + + for (j = 0 ; jinstrument->loop_count ; j ++) + { if (j < ARRAY_LEN (psf->instrument->loops)) + { psf->instrument->loops [j].start = marker_to_position (markstr, psf->instrument->loops [j].start, mark_count) ; + psf->instrument->loops [j].end = marker_to_position (markstr, psf->instrument->loops [j].end, mark_count) ; + psf->instrument->loops [j].mode = SF_LOOP_FORWARD ; + } ; + } ; + } ; + + if (markstr) + free (markstr) ; + + if (! (found_chunk & HAVE_FORM)) + return SFE_AIFF_NO_FORM ; + + if (! (found_chunk & HAVE_AIFF)) + return SFE_AIFF_COMM_NO_FORM ; + + if (! (found_chunk & HAVE_COMM)) + return SFE_AIFF_SSND_NO_COMM ; + + if (! psf->dataoffset) + return SFE_AIFF_NO_DATA ; + + return 0 ; +} /* aiff_read_header */ + +static int +aiff_close (SF_PRIVATE *psf) +{ + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + { aiff_write_tailer (psf) ; + aiff_write_header (psf, SF_TRUE) ; + } ; + + return 0 ; +} /* aiff_close */ + +static int +aiff_read_comm_chunk (SF_PRIVATE *psf, COMM_CHUNK *comm_fmt) +{ int error = 0, bytesread, subformat ; + + psf->u.scbuf [0] = 0 ; + + bytesread = psf_binheader_readf (psf, "E4", &(comm_fmt->size)) ; + + /* The COMM chunk has an int aligned to an odd word boundary. Some + ** procesors are not able to deal with this (ie bus fault) so we have + ** to take special care. + */ + comm_fmt->size += comm_fmt->size & 1 ; + + bytesread += + psf_binheader_readf (psf, "E242b", &(comm_fmt->numChannels), &(comm_fmt->numSampleFrames), + &(comm_fmt->sampleSize), &(comm_fmt->sampleRate), SIGNED_SIZEOF (comm_fmt->sampleRate)) ; + + if (comm_fmt->size == SIZEOF_AIFF_COMM) + comm_fmt->encoding = NONE_MARKER ; + else if (comm_fmt->size == SIZEOF_AIFC_COMM_MIN) + bytesread += psf_binheader_readf (psf, "Em", &(comm_fmt->encoding)) ; + else if (comm_fmt->size >= SIZEOF_AIFC_COMM) + { unsigned char encoding_len ; + + bytesread += psf_binheader_readf (psf, "Em1", &(comm_fmt->encoding), &encoding_len) ; + + memset (psf->u.scbuf, 0, comm_fmt->size) ; + + bytesread += psf_binheader_readf (psf, "b", psf->u.scbuf, + comm_fmt->size - SIZEOF_AIFC_COMM + 1) ; + psf->u.scbuf [encoding_len] = 0 ; + } ; + + psf_log_printf (psf, " COMM : %d\n", comm_fmt->size) ; + psf_log_printf (psf, " Sample Rate : %d\n", tenbytefloat2int (comm_fmt->sampleRate)) ; + psf_log_printf (psf, " Frames : %u%s\n", comm_fmt->numSampleFrames, (comm_fmt->numSampleFrames == 0 && psf->filelength > 104) ? " (Should not be 0)" : "") ; + psf_log_printf (psf, " Channels : %d\n", comm_fmt->numChannels) ; + + /* Found some broken 'fl32' files with comm.samplesize == 16. Fix it here. */ + if ((comm_fmt->encoding == fl32_MARKER || comm_fmt->encoding == FL32_MARKER) && comm_fmt->sampleSize != 32) + { psf_log_printf (psf, " Sample Size : %d (should be 32)\n", comm_fmt->sampleSize) ; + comm_fmt->sampleSize = 32 ; + } + else if ((comm_fmt->encoding == fl64_MARKER || comm_fmt->encoding == FL64_MARKER) && comm_fmt->sampleSize != 64) + { psf_log_printf (psf, " Sample Size : %d (should be 64)\n", comm_fmt->sampleSize) ; + comm_fmt->sampleSize = 64 ; + } + else + psf_log_printf (psf, " Sample Size : %d\n", comm_fmt->sampleSize) ; + + subformat = s_bitwidth_to_subformat (comm_fmt->sampleSize) ; + + psf->endian = SF_ENDIAN_BIG ; + + switch (comm_fmt->encoding) + { case NONE_MARKER : + psf->sf.format = (SF_FORMAT_AIFF | subformat) ; + break ; + + case twos_MARKER : + case in24_MARKER : + case in32_MARKER : + psf->sf.format = (SF_ENDIAN_BIG | SF_FORMAT_AIFF | subformat) ; + break ; + + case sowt_MARKER : + case ni24_MARKER : + case ni32_MARKER : + psf->endian = SF_ENDIAN_LITTLE ; + psf->sf.format = (SF_ENDIAN_LITTLE | SF_FORMAT_AIFF | subformat) ; + break ; + + case fl32_MARKER : + case FL32_MARKER : + psf->sf.format = (SF_FORMAT_AIFF | SF_FORMAT_FLOAT) ; + break ; + + case ulaw_MARKER : + case ULAW_MARKER : + psf->sf.format = (SF_FORMAT_AIFF | SF_FORMAT_ULAW) ; + break ; + + case alaw_MARKER : + case ALAW_MARKER : + psf->sf.format = (SF_FORMAT_AIFF | SF_FORMAT_ALAW) ; + break ; + + case fl64_MARKER : + case FL64_MARKER : + psf->sf.format = (SF_FORMAT_AIFF | SF_FORMAT_DOUBLE) ; + break ; + + case raw_MARKER : + psf->sf.format = (SF_FORMAT_AIFF | SF_FORMAT_PCM_U8) ; + break ; + + case DWVW_MARKER : + psf->sf.format = SF_FORMAT_AIFF ; + switch (comm_fmt->sampleSize) + { case 12 : + psf->sf.format |= SF_FORMAT_DWVW_12 ; + break ; + case 16 : + psf->sf.format |= SF_FORMAT_DWVW_16 ; + break ; + case 24 : + psf->sf.format |= SF_FORMAT_DWVW_24 ; + break ; + + default : + psf->sf.format |= SF_FORMAT_DWVW_N ; + break ; + } ; + break ; + + case GSM_MARKER : + psf->sf.format = SF_FORMAT_AIFF ; + psf->sf.format = (SF_FORMAT_AIFF | SF_FORMAT_GSM610) ; + break ; + + + case ima4_MARKER : + psf->endian = SF_ENDIAN_BIG ; + psf->sf.format = (SF_FORMAT_AIFF | SF_FORMAT_IMA_ADPCM) ; + break ; + + default : + psf_log_printf (psf, "AIFC : Unimplemented format : %M\n", comm_fmt->encoding) ; + error = SFE_UNIMPLEMENTED ; + } ; + + if (! psf->u.scbuf [0]) + psf_log_printf (psf, " Encoding : %M\n", comm_fmt->encoding) ; + else + psf_log_printf (psf, " Encoding : %M => %s\n", comm_fmt->encoding, psf->u.scbuf) ; + + return error ; +} /* aiff_read_comm_chunk */ + + +static int +aiff_write_header (SF_PRIVATE *psf, int calc_length) +{ sf_count_t current ; + AIFF_PRIVATE *paiff ; + unsigned char comm_sample_rate [10], comm_zero_bytes [2] = { 0, 0 } ; + unsigned int comm_type, comm_size, comm_encoding, comm_frames ; + int k, endian ; + short bit_width ; + + if ((paiff = psf->container_data) == NULL) + return SFE_INTERNAL ; + + current = psf_ftell (psf) ; + + if (calc_length) + { psf->filelength = psf_get_filelen (psf) ; + + psf->datalength = psf->filelength - psf->dataoffset ; + if (psf->dataend) + psf->datalength -= psf->filelength - psf->dataend ; + + if (psf->bytewidth > 0) + psf->sf.frames = psf->datalength / (psf->bytewidth * psf->sf.channels) ; + } ; + + if (psf->mode == SFM_RDWR && psf->dataoffset > 0) + { /* Assuming here that the header has already been written and just + ** needs to be corrected for new data length. That means that we + ** only change the length fields of the FORM and SSND chunks ; + ** everything else can be skipped over. + */ + + /* First write new FORM chunk. */ + psf->headindex = 0 ; + psf_fseek (psf, 0, SEEK_SET) ; + + psf_binheader_writef (psf, "Etm8", FORM_MARKER, psf->filelength - 8) ; + psf_fwrite (psf->header, psf->headindex, 1, psf) ; + + /* Now write frame count field of COMM chunk header. */ + psf->headindex = 0 ; + psf_fseek (psf, paiff->comm_offset + 10, SEEK_SET) ; + + psf_binheader_writef (psf, "Et8", psf->sf.frames) ; + psf_fwrite (psf->header, psf->headindex, 1, psf) ; + + /* Now write new SSND chunk header. */ + psf->headindex = 0 ; + psf_fseek (psf, paiff->ssnd_offset, SEEK_SET) ; + + psf_binheader_writef (psf, "Etm8", SSND_MARKER, psf->datalength + SIZEOF_SSND_CHUNK) ; + psf_fwrite (psf->header, psf->headindex, 1, psf) ; + + if (current < psf->dataoffset) + psf_fseek (psf, psf->dataoffset, SEEK_SET) ; + else if (current > 0) + psf_fseek (psf, current, SEEK_SET) ; + + return 0 ; + } ; + + endian = psf->sf.format & SF_FORMAT_ENDMASK ; + if (CPU_IS_LITTLE_ENDIAN && endian == SF_ENDIAN_CPU) + endian = SF_ENDIAN_LITTLE ; + + /* Standard value here. */ + bit_width = psf->bytewidth * 8 ; + comm_frames = (psf->sf.frames > 0xFFFFFFFF) ? 0xFFFFFFFF : psf->sf.frames ; + + switch (psf->sf.format & SF_FORMAT_SUBMASK) + { case SF_FORMAT_PCM_S8 : + case SF_FORMAT_PCM_16 : + case SF_FORMAT_PCM_24 : + case SF_FORMAT_PCM_32 : + switch (endian) + { case SF_ENDIAN_BIG : + psf->endian = SF_ENDIAN_BIG ; + comm_type = AIFC_MARKER ; + comm_size = SIZEOF_AIFC_COMM ; + comm_encoding = twos_MARKER ; + break ; + + case SF_ENDIAN_LITTLE : + psf->endian = SF_ENDIAN_LITTLE ; + comm_type = AIFC_MARKER ; + comm_size = SIZEOF_AIFC_COMM ; + comm_encoding = sowt_MARKER ; + break ; + + default : /* SF_ENDIAN_FILE */ + psf->endian = SF_ENDIAN_BIG ; + comm_type = AIFF_MARKER ; + comm_size = SIZEOF_AIFF_COMM ; + comm_encoding = 0 ; + break ; + } ; + break ; + + case SF_FORMAT_FLOAT : /* Big endian floating point. */ + psf->endian = SF_ENDIAN_BIG ; + comm_type = AIFC_MARKER ; + comm_size = SIZEOF_AIFC_COMM ; + comm_encoding = FL32_MARKER ; /* Use 'FL32' because its easier to read. */ + break ; + + case SF_FORMAT_DOUBLE : /* Big endian double precision floating point. */ + psf->endian = SF_ENDIAN_BIG ; + comm_type = AIFC_MARKER ; + comm_size = SIZEOF_AIFC_COMM ; + comm_encoding = FL64_MARKER ; /* Use 'FL64' because its easier to read. */ + break ; + + case SF_FORMAT_ULAW : + psf->endian = SF_ENDIAN_BIG ; + comm_type = AIFC_MARKER ; + comm_size = SIZEOF_AIFC_COMM ; + comm_encoding = ulaw_MARKER ; + break ; + + case SF_FORMAT_ALAW : + psf->endian = SF_ENDIAN_BIG ; + comm_type = AIFC_MARKER ; + comm_size = SIZEOF_AIFC_COMM ; + comm_encoding = alaw_MARKER ; + break ; + + case SF_FORMAT_PCM_U8 : + psf->endian = SF_ENDIAN_BIG ; + comm_type = AIFC_MARKER ; + comm_size = SIZEOF_AIFC_COMM ; + comm_encoding = raw_MARKER ; + break ; + + case SF_FORMAT_DWVW_12 : + psf->endian = SF_ENDIAN_BIG ; + comm_type = AIFC_MARKER ; + comm_size = SIZEOF_AIFC_COMM ; + comm_encoding = DWVW_MARKER ; + + /* Override standard value here.*/ + bit_width = 12 ; + break ; + + case SF_FORMAT_DWVW_16 : + psf->endian = SF_ENDIAN_BIG ; + comm_type = AIFC_MARKER ; + comm_size = SIZEOF_AIFC_COMM ; + comm_encoding = DWVW_MARKER ; + + /* Override standard value here.*/ + bit_width = 16 ; + break ; + + case SF_FORMAT_DWVW_24 : + psf->endian = SF_ENDIAN_BIG ; + comm_type = AIFC_MARKER ; + comm_size = SIZEOF_AIFC_COMM ; + comm_encoding = DWVW_MARKER ; + + /* Override standard value here.*/ + bit_width = 24 ; + break ; + + case SF_FORMAT_GSM610 : + psf->endian = SF_ENDIAN_BIG ; + comm_type = AIFC_MARKER ; + comm_size = SIZEOF_AIFC_COMM ; + comm_encoding = GSM_MARKER ; + + /* Override standard value here.*/ + bit_width = 16 ; + break ; + + case SF_FORMAT_IMA_ADPCM : + psf->endian = SF_ENDIAN_BIG ; + comm_type = AIFC_MARKER ; + comm_size = SIZEOF_AIFC_COMM ; + comm_encoding = ima4_MARKER ; + + /* Override standard value here.*/ + bit_width = 16 ; + comm_frames = psf->sf.frames / AIFC_IMA4_SAMPLES_PER_BLOCK ; + break ; + + default : return SFE_BAD_OPEN_FORMAT ; + } ; + + /* Reset the current header length to zero. */ + psf->header [0] = 0 ; + psf->headindex = 0 ; + psf_fseek (psf, 0, SEEK_SET) ; + + psf_binheader_writef (psf, "Etm8", FORM_MARKER, psf->filelength - 8) ; + + /* Write AIFF/AIFC marker and COM chunk. */ + if (comm_type == AIFC_MARKER) + /* AIFC must have an FVER chunk. */ + psf_binheader_writef (psf, "Emm44m4", comm_type, FVER_MARKER, 4, 0xA2805140, COMM_MARKER, comm_size) ; + else + psf_binheader_writef (psf, "Emm4", comm_type, COMM_MARKER, comm_size) ; + + paiff->comm_offset = psf->headindex - 8 ; + + memset (comm_sample_rate, 0, sizeof (comm_sample_rate)) ; + uint2tenbytefloat (psf->sf.samplerate, comm_sample_rate) ; + + psf_binheader_writef (psf, "Et242", psf->sf.channels, comm_frames, bit_width) ; + psf_binheader_writef (psf, "b", comm_sample_rate, sizeof (comm_sample_rate)) ; + + /* AIFC chunks have some extra data. */ + if (comm_type == AIFC_MARKER) + psf_binheader_writef (psf, "mb", comm_encoding, comm_zero_bytes, sizeof (comm_zero_bytes)) ; + + if (psf->instrument != NULL) + { MARK_ID_POS m [4] ; + INST_CHUNK ch ; + unsigned short ct = 0 ; + + memset (m, 0, sizeof (m)) ; + memset (&ch, 0, sizeof (ch)) ; + + ch.baseNote = psf->instrument->basenote ; + ch.detune = psf->instrument->detune ; + ch.lowNote = psf->instrument->key_lo ; + ch.highNote = psf->instrument->key_hi ; + ch.lowVelocity = psf->instrument->velocity_lo ; + ch.highVelocity = psf->instrument->velocity_hi ; + ch.gain = psf->instrument->gain ; + if (psf->instrument->loops [0].mode != SF_LOOP_NONE) + { ch.sustain_loop.playMode = 1 ; + ch.sustain_loop.beginLoop = ct ; + m [0].markerID = ct++ ; + m [0].position = psf->instrument->loops [0].start ; + ch.sustain_loop.endLoop = ct ; + m [1].markerID = ct++ ; + m [1].position = psf->instrument->loops [0].end ; + } ; + if (psf->instrument->loops [1].mode != SF_LOOP_NONE) + { ch.release_loop.playMode = 1 ; + ch.release_loop.beginLoop = ct ; + m [2].markerID = ct++ ; + m [2].position = psf->instrument->loops [1].start ; + ch.release_loop.endLoop = ct ; + m [3].markerID = ct++ ; + m [3].position = psf->instrument->loops [1].end ; + } + else + { ch.release_loop.playMode = 0 ; + ch.release_loop.beginLoop = 0 ; + ch.release_loop.endLoop = 0 ; + } ; + + psf_binheader_writef (psf, "Em4111111", INST_MARKER, SIZEOF_INST_CHUNK, ch.baseNote, ch.detune, + ch.lowNote, ch.highNote, ch.lowVelocity, ch.highVelocity) ; + psf_binheader_writef (psf, "2222222", ch.gain, ch.sustain_loop.playMode, + ch.sustain_loop.beginLoop, ch.sustain_loop.endLoop, ch.release_loop.playMode, + ch.release_loop.beginLoop, ch.release_loop.endLoop) ; + + if (ct == 2) + psf_binheader_writef (psf, "Em42241b241b", + MARK_MARKER, 2 + 2 * (2 + 4 + 1 + 9), 2, + m [0].markerID, m [0].position, 8, "beg loop", make_size_t (9), + m [1].markerID, m [1].position, 8, "end loop", make_size_t (9)) ; + else if (ct == 4) + psf_binheader_writef (psf, "Em42 241b 241b 241b 241b", + MARK_MARKER, 2 + 4 * (2 + 4 + 1 + 9), 4, + m [0].markerID, m [0].position, 8, "beg loop", make_size_t (9), + m [1].markerID, m [1].position, 8, "end loop", make_size_t (9), + m [2].markerID, m [2].position, 8, "beg loop", make_size_t (9), + m [3].markerID, m [3].position, 8, "end loop", make_size_t (9)) ; + } ; + + if (psf->str_flags & SF_STR_LOCATE_START) + aiff_write_strings (psf, SF_STR_LOCATE_START) ; + + if (psf->peak_info != NULL && psf->peak_info->peak_loc == SF_PEAK_START) + { psf_binheader_writef (psf, "Em4", PEAK_MARKER, AIFF_PEAK_CHUNK_SIZE (psf->sf.channels)) ; + psf_binheader_writef (psf, "E44", 1, time (NULL)) ; + for (k = 0 ; k < psf->sf.channels ; k++) + psf_binheader_writef (psf, "Eft8", (float) psf->peak_info->peaks [k].value, psf->peak_info->peaks [k].position) ; + } ; + + /* Write SSND chunk. */ + paiff->ssnd_offset = psf->headindex ; + psf_binheader_writef (psf, "Etm844", SSND_MARKER, psf->datalength + SIZEOF_SSND_CHUNK, 0, 0) ; + + /* Header construction complete so write it out. */ + psf_fwrite (psf->header, psf->headindex, 1, psf) ; + + if (psf->error) + return psf->error ; + + psf->dataoffset = psf->headindex ; + + if (current < psf->dataoffset) + psf_fseek (psf, psf->dataoffset, SEEK_SET) ; + else if (current > 0) + psf_fseek (psf, current, SEEK_SET) ; + + return psf->error ; +} /* aiff_write_header */ + +static int +aiff_write_tailer (SF_PRIVATE *psf) +{ int k ; + + /* Reset the current header length to zero. */ + psf->header [0] = 0 ; + psf->headindex = 0 ; + + psf->dataend = psf_fseek (psf, 0, SEEK_END) ; + + /* Make sure tailer data starts at even byte offset. Pad if necessary. */ + if (psf->dataend % 2 == 1) + { psf_fwrite (psf->header, 1, 1, psf) ; + psf->dataend ++ ; + } ; + + if (psf->peak_info != NULL && psf->peak_info->peak_loc == SF_PEAK_END) + { psf_binheader_writef (psf, "Em4", PEAK_MARKER, AIFF_PEAK_CHUNK_SIZE (psf->sf.channels)) ; + psf_binheader_writef (psf, "E44", 1, time (NULL)) ; + for (k = 0 ; k < psf->sf.channels ; k++) + psf_binheader_writef (psf, "Eft8", (float) psf->peak_info->peaks [k].value, psf->peak_info->peaks [k].position) ; + } ; + + if (psf->str_flags & SF_STR_LOCATE_END) + aiff_write_strings (psf, SF_STR_LOCATE_END) ; + + /* Write the tailer. */ + if (psf->headindex > 0) + psf_fwrite (psf->header, psf->headindex, 1, psf) ; + + return 0 ; +} /* aiff_write_tailer */ + +static void +aiff_write_strings (SF_PRIVATE *psf, int location) +{ int k ; + + for (k = 0 ; k < SF_MAX_STRINGS ; k++) + { if (psf->strings [k].type == 0) + break ; + + if (psf->strings [k].flags != location) + continue ; + + switch (psf->strings [k].type) + { case SF_STR_SOFTWARE : + psf_binheader_writef (psf, "EmS", APPL_MARKER, psf->strings [k].str) ; + break ; + + case SF_STR_TITLE : + psf_binheader_writef (psf, "EmS", NAME_MARKER, psf->strings [k].str) ; + break ; + + case SF_STR_COPYRIGHT : + psf_binheader_writef (psf, "EmS", c_MARKER, psf->strings [k].str) ; + break ; + + case SF_STR_ARTIST : + psf_binheader_writef (psf, "EmS", AUTH_MARKER, psf->strings [k].str) ; + break ; + + case SF_STR_COMMENT : + psf_binheader_writef (psf, "EmS", ANNO_MARKER, psf->strings [k].str) ; + break ; + + /* + case SF_STR_DATE : + psf_binheader_writef (psf, "Ems", ICRD_MARKER, psf->strings [k].str) ; + break ; + */ + } ; + } ; + + return ; +} /* aiff_write_strings */ + +static int +aiff_command (SF_PRIVATE * UNUSED (psf), int UNUSED (command), void * UNUSED (data), int UNUSED (datasize)) +{ + return 0 ; +} /* aiff_command */ + +static const char* +get_loop_mode_str (short mode) +{ switch (mode) + { case 0 : return "none" ; + case 1 : return "forward" ; + case 2 : return "backward" ; + } ; + + return "*** unknown" ; +} /* get_loop_mode_str */ + +static short +get_loop_mode (short mode) +{ switch (mode) + { case 0 : return SF_LOOP_NONE ; + case 1 : return SF_LOOP_FORWARD ; + case 2 : return SF_LOOP_BACKWARD ; + } ; + + return SF_LOOP_NONE ; +} /* get_loop_mode */ + +/*========================================================================================== +** Rough hack at converting from 80 bit IEEE float in AIFF header to an int and +** back again. It assumes that all sample rates are between 1 and 800MHz, which +** should be OK as other sound file formats use a 32 bit integer to store sample +** rate. +** There is another (probably better) version in the source code to the SoX but it +** has a copyright which probably prevents it from being allowable as GPL/LGPL. +*/ + +static int +tenbytefloat2int (unsigned char *bytes) +{ int val = 3 ; + + if (bytes [0] & 0x80) /* Negative number. */ + return 0 ; + + if (bytes [0] <= 0x3F) /* Less than 1. */ + return 1 ; + + if (bytes [0] > 0x40) /* Way too big. */ + return 0x4000000 ; + + if (bytes [0] == 0x40 && bytes [1] > 0x1C) /* Too big. */ + return 800000000 ; + + /* Ok, can handle it. */ + + val = (bytes [2] << 23) | (bytes [3] << 15) | (bytes [4] << 7) | (bytes [5] >> 1) ; + + val >>= (29 - bytes [1]) ; + + return val ; +} /* tenbytefloat2int */ + +static void +uint2tenbytefloat (unsigned int num, unsigned char *bytes) +{ unsigned int mask = 0x40000000 ; + int count ; + + if (num <= 1) + { bytes [0] = 0x3F ; + bytes [1] = 0xFF ; + bytes [2] = 0x80 ; + return ; + } ; + + bytes [0] = 0x40 ; + + if (num >= mask) + { bytes [1] = 0x1D ; + return ; + } ; + + for (count = 0 ; count <= 32 ; count ++) + { if (num & mask) + break ; + mask >>= 1 ; + } ; + + num <<= count + 1 ; + bytes [1] = 29 - count ; + bytes [2] = (num >> 24) & 0xFF ; + bytes [3] = (num >> 16) & 0xFF ; + bytes [4] = (num >> 8) & 0xFF ; + bytes [5] = num & 0xFF ; + +} /* uint2tenbytefloat */ + +static int +aiff_read_basc_chunk (SF_PRIVATE * psf, int datasize) +{ const char * type_str ; + basc_CHUNK bc ; + int count ; + + count = psf_binheader_readf (psf, "E442", &bc.version, &bc.numBeats, &bc.rootNote) ; + count += psf_binheader_readf (psf, "E222", &bc.scaleType, &bc.sigNumerator, &bc.sigDenominator) ; + count += psf_binheader_readf (psf, "E2j", &bc.loopType, datasize - sizeof (bc)) ; + + psf_log_printf (psf, " Version ? : %u\n Num Beats : %u\n Root Note : 0x%x\n", + bc.version, bc.numBeats, bc.rootNote) ; + + switch (bc.scaleType) + { case basc_SCALE_MINOR : + type_str = "MINOR" ; + break ; + case basc_SCALE_MAJOR : + type_str = "MAJOR" ; + break ; + case basc_SCALE_NEITHER : + type_str = "NEITHER" ; + break ; + case basc_SCALE_BOTH : + type_str = "BOTH" ; + break ; + default : + type_str = "!!WRONG!!" ; + break ; + } ; + + psf_log_printf (psf, " ScaleType : 0x%x (%s)\n", bc.scaleType, type_str) ; + psf_log_printf (psf, " Time Sig : %d/%d\n", bc.sigNumerator, bc.sigDenominator) ; + + switch (bc.loopType) + { case basc_TYPE_ONE_SHOT : + type_str = "One Shot" ; + break ; + case basc_TYPE_LOOP : + type_str = "Loop" ; + break ; + default: + type_str = "!!WRONG!!" ; + break ; + } ; + + psf_log_printf (psf, " Loop Type : 0x%x (%s)\n", bc.loopType, type_str) ; + + if ((psf->loop_info = calloc (1, sizeof (SF_LOOP_INFO))) == NULL) + return SFE_MALLOC_FAILED ; + + psf->loop_info->time_sig_num = bc.sigNumerator ; + psf->loop_info->time_sig_den = bc.sigDenominator ; + psf->loop_info->loop_mode = (bc.loopType == basc_TYPE_ONE_SHOT) ? SF_LOOP_NONE : SF_LOOP_FORWARD ; + psf->loop_info->num_beats = bc.numBeats ; + + /* Can always be recalculated from other known fields. */ + psf->loop_info->bpm = (1.0 / psf->sf.frames) * psf->sf.samplerate + * ((bc.numBeats * 4.0) / bc.sigDenominator) * 60.0 ; + psf->loop_info->root_key = bc.rootNote ; + + if (count < datasize) + psf_binheader_readf (psf, "j", datasize - count) ; + + return 0 ; +} /* aiff_read_basc_chunk */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 7dec56ca-d6f2-48cf-863b-a72e7e17a5d9 +*/ diff --git a/src/alaw.c b/src/alaw.c new file mode 100644 index 00000000..a2d27cb5 --- /dev/null +++ b/src/alaw.c @@ -0,0 +1,544 @@ +/* +** Copyright (C) 1999-2005 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sndfile.h" +#include "float_cast.h" +#include "common.h" + +static sf_count_t alaw_read_alaw2s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ; +static sf_count_t alaw_read_alaw2i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ; +static sf_count_t alaw_read_alaw2f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ; +static sf_count_t alaw_read_alaw2d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ; + +static sf_count_t alaw_write_s2alaw (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ; +static sf_count_t alaw_write_i2alaw (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ; +static sf_count_t alaw_write_f2alaw (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ; +static sf_count_t alaw_write_d2alaw (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ; + +static void alaw2s_array (unsigned char *buffer, int count, short *ptr) ; +static void alaw2i_array (unsigned char *buffer, int count, int *ptr) ; +static void alaw2f_array (unsigned char *buffer, int count, float *ptr, float normfact) ; +static void alaw2d_array (unsigned char *buffer, int count, double *ptr, double normfact) ; + +static void s2alaw_array (const short *buffer, int count, unsigned char *ptr) ; +static void i2alaw_array (const int *buffer, int count, unsigned char *ptr) ; +static void f2alaw_array (const float *buffer, int count, unsigned char *ptr, float normfact) ; +static void d2alaw_array (const double *buffer, int count, unsigned char *ptr, double normfact) ; + + +int +alaw_init (SF_PRIVATE *psf) +{ + if (psf->mode == SFM_READ || psf->mode == SFM_RDWR) + { psf->read_short = alaw_read_alaw2s ; + psf->read_int = alaw_read_alaw2i ; + psf->read_float = alaw_read_alaw2f ; + psf->read_double = alaw_read_alaw2d ; + } ; + + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + { psf->write_short = alaw_write_s2alaw ; + psf->write_int = alaw_write_i2alaw ; + psf->write_float = alaw_write_f2alaw ; + psf->write_double = alaw_write_d2alaw ; + } ; + + psf->bytewidth = 1 ; + psf->blockwidth = psf->sf.channels ; + + if (psf->filelength > psf->dataoffset) + psf->datalength = (psf->dataend) ? psf->dataend - psf->dataoffset : psf->filelength - psf->dataoffset ; + else + psf->datalength = 0 ; + + psf->sf.frames = psf->datalength / psf->blockwidth ; + + return 0 ; +} /* alaw_init */ + +/*============================================================================== + * Private static functions and data. + */ + +static +short alaw_decode [256] = +{ -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736, + -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784, + -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368, + -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392, + -22016, -20992, -24064, -23040, -17920, -16896, -19968, -18944, + -30208, -29184, -32256, -31232, -26112, -25088, -28160, -27136, + -11008, -10496, -12032, -11520, -8960, -8448, -9984, -9472, + -15104, -14592, -16128, -15616, -13056, -12544, -14080, -13568, + -344, -328, -376, -360, -280, -264, -312, -296, + -472, -456, -504, -488, -408, -392, -440, -424, + -88, -72, -120, -104, -24, -8, -56, -40, + -216, -200, -248, -232, -152, -136, -184, -168, + -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184, + -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696, + -688, -656, -752, -720, -560, -528, -624, -592, + -944, -912, -1008, -976, -816, -784, -880, -848, + 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736, + 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784, + 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368, + 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392, + 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944, + 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136, + 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472, + 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568, + 344, 328, 376, 360, 280, 264, 312, 296, + 472, 456, 504, 488, 408, 392, 440, 424, + 88, 72, 120, 104, 24, 8, 56, 40, + 216, 200, 248, 232, 152, 136, 184, 168, + 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184, + 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696, + 688, 656, 752, 720, 560, 528, 624, 592, + 944, 912, 1008, 976, 816, 784, 880, 848 +} ; /* alaw_decode */ + +static +unsigned char alaw_encode [2048 + 1] = +{ 0xd5, 0xd4, 0xd7, 0xd6, 0xd1, 0xd0, 0xd3, 0xd2, 0xdd, 0xdc, 0xdf, 0xde, + 0xd9, 0xd8, 0xdb, 0xda, 0xc5, 0xc4, 0xc7, 0xc6, 0xc1, 0xc0, 0xc3, 0xc2, + 0xcd, 0xcc, 0xcf, 0xce, 0xc9, 0xc8, 0xcb, 0xca, 0xf5, 0xf5, 0xf4, 0xf4, + 0xf7, 0xf7, 0xf6, 0xf6, 0xf1, 0xf1, 0xf0, 0xf0, 0xf3, 0xf3, 0xf2, 0xf2, + 0xfd, 0xfd, 0xfc, 0xfc, 0xff, 0xff, 0xfe, 0xfe, 0xf9, 0xf9, 0xf8, 0xf8, + 0xfb, 0xfb, 0xfa, 0xfa, 0xe5, 0xe5, 0xe5, 0xe5, 0xe4, 0xe4, 0xe4, 0xe4, + 0xe7, 0xe7, 0xe7, 0xe7, 0xe6, 0xe6, 0xe6, 0xe6, 0xe1, 0xe1, 0xe1, 0xe1, + 0xe0, 0xe0, 0xe0, 0xe0, 0xe3, 0xe3, 0xe3, 0xe3, 0xe2, 0xe2, 0xe2, 0xe2, + 0xed, 0xed, 0xed, 0xed, 0xec, 0xec, 0xec, 0xec, 0xef, 0xef, 0xef, 0xef, + 0xee, 0xee, 0xee, 0xee, 0xe9, 0xe9, 0xe9, 0xe9, 0xe8, 0xe8, 0xe8, 0xe8, + 0xeb, 0xeb, 0xeb, 0xeb, 0xea, 0xea, 0xea, 0xea, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0xb5, 0xb5, 0xb5, 0xb5, + 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, + 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, + 0xb5, 0xb5, 0xb5, 0xb5, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, + 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, + 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, + 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb6, 0xb6, 0xb6, 0xb6, + 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, + 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, + 0xb6, 0xb6, 0xb6, 0xb6, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, + 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, + 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, + 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, + 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, + 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb3, 0xb3, 0xb3, 0xb3, + 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, + 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, + 0xb3, 0xb3, 0xb3, 0xb3, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, + 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, + 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, + 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbc, 0xbc, 0xbc, 0xbc, + 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, + 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, + 0xbc, 0xbc, 0xbc, 0xbc, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, + 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, + 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xb9, 0xb9, 0xb9, 0xb9, + 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, + 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, + 0xb9, 0xb9, 0xb9, 0xb9, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, + 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, + 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, + 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xba, 0xba, 0xba, 0xba, + 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, + 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, + 0xba, 0xba, 0xba, 0xba, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x2a +} ; /* alaw_encode */ + +static inline void +alaw2s_array (unsigned char *buffer, int count, short *ptr) +{ while (--count >= 0) + ptr [count] = alaw_decode [(int) buffer [count]] ; +} /* alaw2s_array */ + +static inline void +alaw2i_array (unsigned char *buffer, int count, int *ptr) +{ while (--count >= 0) + ptr [count] = alaw_decode [(int) buffer [count]] << 16 ; +} /* alaw2i_array */ + +static inline void +alaw2f_array (unsigned char *buffer, int count, float *ptr, float normfact) +{ while (--count >= 0) + ptr [count] = normfact * alaw_decode [(int) buffer [count]] ; +} /* alaw2f_array */ + +static inline void +alaw2d_array (unsigned char *buffer, int count, double *ptr, double normfact) +{ while (--count >= 0) + ptr [count] = normfact * alaw_decode [(int) buffer [count]] ; +} /* alaw2d_array */ + +static inline void +s2alaw_array (const short *ptr, int count, unsigned char *buffer) +{ while (--count >= 0) + { if (ptr [count] >= 0) + buffer [count] = alaw_encode [ptr [count] / 16] ; + else + buffer [count] = 0x7F & alaw_encode [ptr [count] / -16] ; + } ; +} /* s2alaw_array */ + +static inline void +i2alaw_array (const int *ptr, int count, unsigned char *buffer) +{ while (--count >= 0) + { if (ptr [count] >= 0) + buffer [count] = alaw_encode [ptr [count] >> (16 + 4)] ; + else + buffer [count] = 0x7F & alaw_encode [- ptr [count] >> (16 + 4)] ; + } ; +} /* i2alaw_array */ + +static inline void +f2alaw_array (const float *ptr, int count, unsigned char *buffer, float normfact) +{ while (--count >= 0) + { if (ptr [count] >= 0) + buffer [count] = alaw_encode [lrintf (normfact * ptr [count])] ; + else + buffer [count] = 0x7F & alaw_encode [- lrintf (normfact * ptr [count])] ; + } ; +} /* f2alaw_array */ + +static inline void +d2alaw_array (const double *ptr, int count, unsigned char *buffer, double normfact) +{ while (--count >= 0) + { if (ptr [count] >= 0) + buffer [count] = alaw_encode [lrint (normfact * ptr [count])] ; + else + buffer [count] = 0x7F & alaw_encode [- lrint (normfact * ptr [count])] ; + } ; +} /* d2alaw_array */ + +/*============================================================================== +*/ + +static sf_count_t +alaw_read_alaw2s (SF_PRIVATE *psf, short *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + + bufferlen = ARRAY_LEN (psf->u.ucbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.ucbuf, 1, bufferlen, psf) ; + alaw2s_array (psf->u.ucbuf, readcount, ptr + total) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* alaw_read_alaw2s */ + +static sf_count_t +alaw_read_alaw2i (SF_PRIVATE *psf, int *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + + bufferlen = ARRAY_LEN (psf->u.ucbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.ucbuf, 1, bufferlen, psf) ; + alaw2i_array (psf->u.ucbuf, readcount, ptr + total) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* alaw_read_alaw2i */ + +static sf_count_t +alaw_read_alaw2f (SF_PRIVATE *psf, float *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + float normfact ; + + normfact = (psf->norm_float == SF_TRUE) ? 1.0 / ((float) 0x8000) : 1.0 ; + + bufferlen = ARRAY_LEN (psf->u.ucbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.ucbuf, 1, bufferlen, psf) ; + alaw2f_array (psf->u.ucbuf, readcount, ptr + total, normfact) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* alaw_read_alaw2f */ + +static sf_count_t +alaw_read_alaw2d (SF_PRIVATE *psf, double *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + double normfact ; + + normfact = (psf->norm_double) ? 1.0 / ((double) 0x8000) : 1.0 ; + bufferlen = ARRAY_LEN (psf->u.ucbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.ucbuf, 1, bufferlen, psf) ; + alaw2d_array (psf->u.ucbuf, readcount, ptr + total, normfact) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* alaw_read_alaw2d */ + +/*============================================================================================= +*/ + +static sf_count_t +alaw_write_s2alaw (SF_PRIVATE *psf, const short *ptr, sf_count_t len) +{ int bufferlen, writecount ; + sf_count_t total = 0 ; + + bufferlen = ARRAY_LEN (psf->u.ucbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + s2alaw_array (ptr + total, bufferlen, psf->u.ucbuf) ; + writecount = psf_fwrite (psf->u.ucbuf, 1, bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* alaw_write_s2alaw */ + +static sf_count_t +alaw_write_i2alaw (SF_PRIVATE *psf, const int *ptr, sf_count_t len) +{ int bufferlen, writecount ; + sf_count_t total = 0 ; + + bufferlen = ARRAY_LEN (psf->u.ucbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + i2alaw_array (ptr + total, bufferlen, psf->u.ucbuf) ; + writecount = psf_fwrite (psf->u.ucbuf, 1, bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* alaw_write_i2alaw */ + +static sf_count_t +alaw_write_f2alaw (SF_PRIVATE *psf, const float *ptr, sf_count_t len) +{ int bufferlen, writecount ; + sf_count_t total = 0 ; + float normfact ; + + normfact = (psf->norm_float == SF_TRUE) ? (1.0 * 0x7FFF) / 16.0 : 1.0 / 16 ; + + bufferlen = ARRAY_LEN (psf->u.ucbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + f2alaw_array (ptr + total, bufferlen, psf->u.ucbuf, normfact) ; + writecount = psf_fwrite (psf->u.ucbuf, 1, bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* alaw_write_f2alaw */ + +static sf_count_t +alaw_write_d2alaw (SF_PRIVATE *psf, const double *ptr, sf_count_t len) +{ int bufferlen, writecount ; + sf_count_t total = 0 ; + double normfact ; + + normfact = (psf->norm_double) ? (1.0 * 0x7FFF) / 16.0 : 1.0 / 16.0 ; + + bufferlen = ARRAY_LEN (psf->u.ucbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + d2alaw_array (ptr + total, bufferlen, psf->u.ucbuf, normfact) ; + writecount = psf_fwrite (psf->u.ucbuf, 1, bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* alaw_write_d2alaw */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 289ccfc2-42a6-4f1f-a29f-4dcc9bfa8752 +*/ diff --git a/src/au.c b/src/au.c new file mode 100644 index 00000000..3a5f93bd --- /dev/null +++ b/src/au.c @@ -0,0 +1,453 @@ +/* +** Copyright (C) 1999-2004 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include +#include + +#include "sndfile.h" +#include "sfendian.h" +#include "common.h" + +/*------------------------------------------------------------------------------ +** Macros to handle big/little endian issues. +*/ + +#define DOTSND_MARKER (MAKE_MARKER ('.', 's', 'n', 'd')) +#define DNSDOT_MARKER (MAKE_MARKER ('d', 'n', 's', '.')) + +#define AU_DATA_OFFSET 24 + +/*------------------------------------------------------------------------------ +** Known AU file encoding types. +*/ + +enum +{ AU_ENCODING_ULAW_8 = 1, /* 8-bit u-law samples */ + AU_ENCODING_PCM_8 = 2, /* 8-bit linear samples */ + AU_ENCODING_PCM_16 = 3, /* 16-bit linear samples */ + AU_ENCODING_PCM_24 = 4, /* 24-bit linear samples */ + AU_ENCODING_PCM_32 = 5, /* 32-bit linear samples */ + + AU_ENCODING_FLOAT = 6, /* floating-point samples */ + AU_ENCODING_DOUBLE = 7, /* double-precision float samples */ + AU_ENCODING_INDIRECT = 8, /* fragmented sampled data */ + AU_ENCODING_NESTED = 9, /* ? */ + AU_ENCODING_DSP_CORE = 10, /* DSP program */ + AU_ENCODING_DSP_DATA_8 = 11, /* 8-bit fixed-point samples */ + AU_ENCODING_DSP_DATA_16 = 12, /* 16-bit fixed-point samples */ + AU_ENCODING_DSP_DATA_24 = 13, /* 24-bit fixed-point samples */ + AU_ENCODING_DSP_DATA_32 = 14, /* 32-bit fixed-point samples */ + + AU_ENCODING_DISPLAY = 16, /* non-audio display data */ + AU_ENCODING_MULAW_SQUELCH = 17, /* ? */ + AU_ENCODING_EMPHASIZED = 18, /* 16-bit linear with emphasis */ + AU_ENCODING_NEXT = 19, /* 16-bit linear with compression (NEXT) */ + AU_ENCODING_COMPRESSED_EMPHASIZED = 20, /* A combination of the two above */ + AU_ENCODING_DSP_COMMANDS = 21, /* Music Kit DSP commands */ + AU_ENCODING_DSP_COMMANDS_SAMPLES = 22, /* ? */ + + AU_ENCODING_ADPCM_G721_32 = 23, /* G721 32 kbs ADPCM - 4 bits per sample. */ + AU_ENCODING_ADPCM_G722 = 24, /* G722 64 kbs ADPCM */ + AU_ENCODING_ADPCM_G723_24 = 25, /* G723 24 kbs ADPCM - 3 bits per sample. */ + AU_ENCODING_ADPCM_G723_40 = 26, /* G723 40 kbs ADPCM - 5 bits per sample. */ + + AU_ENCODING_ALAW_8 = 27 +} ; + +/*------------------------------------------------------------------------------ +** Typedefs. +*/ + +typedef struct +{ int dataoffset ; + int datasize ; + int encoding ; + int samplerate ; + int channels ; +} AU_FMT ; + + +/*------------------------------------------------------------------------------ +** Private static functions. +*/ + +static int au_close (SF_PRIVATE *psf) ; + +static int au_format_to_encoding (int format) ; + +static int au_write_header (SF_PRIVATE *psf, int calc_length) ; +static int au_read_header (SF_PRIVATE *psf) ; + +/*------------------------------------------------------------------------------ +** Public function. +*/ + +int +au_open (SF_PRIVATE *psf) +{ int subformat ; + int error = 0 ; + + if (psf->mode == SFM_READ || (psf->mode == SFM_RDWR && psf->filelength > 0)) + { if ((error = au_read_header (psf))) + return error ; + } ; + + if ((psf->sf.format & SF_FORMAT_TYPEMASK) != SF_FORMAT_AU) + return SFE_BAD_OPEN_FORMAT ; + + subformat = psf->sf.format & SF_FORMAT_SUBMASK ; + + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + { psf->endian = psf->sf.format & SF_FORMAT_ENDMASK ; + if (CPU_IS_LITTLE_ENDIAN && psf->endian == SF_ENDIAN_CPU) + psf->endian = SF_ENDIAN_LITTLE ; + else if (psf->endian != SF_ENDIAN_LITTLE) + psf->endian = SF_ENDIAN_BIG ; + + if (au_write_header (psf, SF_FALSE)) + return psf->error ; + + psf->write_header = au_write_header ; + } ; + + psf->container_close = au_close ; + + psf->blockwidth = psf->bytewidth * psf->sf.channels ; + + switch (subformat) + { case SF_FORMAT_ULAW : /* 8-bit Ulaw encoding. */ + ulaw_init (psf) ; + break ; + + case SF_FORMAT_PCM_S8 : /* 8-bit linear PCM. */ + error = pcm_init (psf) ; + break ; + + case SF_FORMAT_PCM_16 : /* 16-bit linear PCM. */ + case SF_FORMAT_PCM_24 : /* 24-bit linear PCM */ + case SF_FORMAT_PCM_32 : /* 32-bit linear PCM. */ + error = pcm_init (psf) ; + break ; + + case SF_FORMAT_ALAW : /* 8-bit Alaw encoding. */ + alaw_init (psf) ; + break ; + + /* Lite remove start */ + case SF_FORMAT_FLOAT : /* 32-bit floats. */ + error = float32_init (psf) ; + break ; + + case SF_FORMAT_DOUBLE : /* 64-bit double precision floats. */ + error = double64_init (psf) ; + break ; + + case SF_FORMAT_G721_32 : + error = g72x_init (psf) ; + psf->sf.seekable = SF_FALSE ; + break ; + + case SF_FORMAT_G723_24 : + error = g72x_init (psf) ; + psf->sf.seekable = SF_FALSE ; + break ; + + case SF_FORMAT_G723_40 : + error = g72x_init (psf) ; + psf->sf.seekable = SF_FALSE ; + break ; + /* Lite remove end */ + + default : break ; + } ; + + return error ; +} /* au_open */ + +/*------------------------------------------------------------------------------ +*/ + +static int +au_close (SF_PRIVATE *psf) +{ + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + au_write_header (psf, SF_TRUE) ; + + return 0 ; +} /* au_close */ + +static int +au_write_header (SF_PRIVATE *psf, int calc_length) +{ sf_count_t current ; + int encoding, datalength ; + + if (psf->pipeoffset > 0) + return 0 ; + + current = psf_ftell (psf) ; + + if (calc_length) + { psf->filelength = psf_get_filelen (psf) ; + + psf->datalength = psf->filelength - psf->dataoffset ; + if (psf->dataend) + psf->datalength -= psf->filelength - psf->dataend ; + + psf->sf.frames = psf->datalength / (psf->bytewidth * psf->sf.channels) ; + } ; + + encoding = au_format_to_encoding (psf->sf.format & SF_FORMAT_SUBMASK) ; + if (! encoding) + return (psf->error = SFE_BAD_OPEN_FORMAT) ; + + /* Reset the current header length to zero. */ + psf->header [0] = 0 ; + psf->headindex = 0 ; + + /* + ** Only attempt to seek if we are not writng to a pipe. If we are + ** writing to a pipe we shouldn't be here anyway. + */ + if (psf->is_pipe == SF_FALSE) + psf_fseek (psf, 0, SEEK_SET) ; + + /* + ** AU format files allow a datalength value of -1 if the datalength + ** is not know at the time the header is written. + ** Also use this value of -1 if the datalength > 2 gigabytes. + */ + if (psf->datalength < 0 || psf->datalength > 0x7FFFFFFF) + datalength = -1 ; + else + datalength = (int) (psf->datalength & 0x7FFFFFFF) ; + + if (psf->endian == SF_ENDIAN_BIG) + { psf_binheader_writef (psf, "Em4", DOTSND_MARKER, AU_DATA_OFFSET) ; + psf_binheader_writef (psf, "E4444", datalength, encoding, psf->sf.samplerate, psf->sf.channels) ; + } + else if (psf->endian == SF_ENDIAN_LITTLE) + { psf_binheader_writef (psf, "em4", DNSDOT_MARKER, AU_DATA_OFFSET) ; + psf_binheader_writef (psf, "e4444", datalength, encoding, psf->sf.samplerate, psf->sf.channels) ; + } + else + return (psf->error = SFE_BAD_OPEN_FORMAT) ; + + /* Header construction complete so write it out. */ + psf_fwrite (psf->header, psf->headindex, 1, psf) ; + + if (psf->error) + return psf->error ; + + psf->dataoffset = psf->headindex ; + + if (current > 0) + psf_fseek (psf, current, SEEK_SET) ; + + return psf->error ; +} /* au_write_header */ + +static int +au_format_to_encoding (int format) +{ + switch (format) + { case SF_FORMAT_PCM_S8 : return AU_ENCODING_PCM_8 ; + case SF_FORMAT_PCM_16 : return AU_ENCODING_PCM_16 ; + case SF_FORMAT_PCM_24 : return AU_ENCODING_PCM_24 ; + case SF_FORMAT_PCM_32 : return AU_ENCODING_PCM_32 ; + + case SF_FORMAT_FLOAT : return AU_ENCODING_FLOAT ; + case SF_FORMAT_DOUBLE : return AU_ENCODING_DOUBLE ; + + case SF_FORMAT_ULAW : return AU_ENCODING_ULAW_8 ; + case SF_FORMAT_ALAW : return AU_ENCODING_ALAW_8 ; + + case SF_FORMAT_G721_32 : return AU_ENCODING_ADPCM_G721_32 ; + case SF_FORMAT_G723_24 : return AU_ENCODING_ADPCM_G723_24 ; + case SF_FORMAT_G723_40 : return AU_ENCODING_ADPCM_G723_40 ; + + default : break ; + } ; + return 0 ; +} /* au_format_to_encoding */ + +static int +au_read_header (SF_PRIVATE *psf) +{ AU_FMT au_fmt ; + int marker, dword ; + + memset (&au_fmt, 0, sizeof (au_fmt)) ; + psf_binheader_readf (psf, "pm", 0, &marker) ; + psf_log_printf (psf, "%M\n", marker) ; + + if (marker == DOTSND_MARKER) + { psf->endian = SF_ENDIAN_BIG ; + + psf_binheader_readf (psf, "E44444", &(au_fmt.dataoffset), &(au_fmt.datasize), + &(au_fmt.encoding), &(au_fmt.samplerate), &(au_fmt.channels)) ; + } + else if (marker == DNSDOT_MARKER) + { psf->endian = SF_ENDIAN_LITTLE ; + psf_binheader_readf (psf, "e44444", &(au_fmt.dataoffset), &(au_fmt.datasize), + &(au_fmt.encoding), &(au_fmt.samplerate), &(au_fmt.channels)) ; + } + else + return SFE_AU_NO_DOTSND ; + + psf_log_printf (psf, " Data Offset : %d\n", au_fmt.dataoffset) ; + + if (psf->fileoffset > 0 && au_fmt.datasize == -1) + { psf_log_printf (psf, " Data Size : -1\n") ; + return SFE_AU_EMBED_BAD_LEN ; + } ; + + if (psf->fileoffset > 0) + { psf->filelength = au_fmt.dataoffset + au_fmt.datasize ; + psf_log_printf (psf, " Data Size : %d\n", au_fmt.datasize) ; + } + else if (au_fmt.datasize == -1 || au_fmt.dataoffset + au_fmt.datasize == psf->filelength) + psf_log_printf (psf, " Data Size : %d\n", au_fmt.datasize) ; + else if (au_fmt.dataoffset + au_fmt.datasize < psf->filelength) + { psf->filelength = au_fmt.dataoffset + au_fmt.datasize ; + psf_log_printf (psf, " Data Size : %d\n", au_fmt.datasize) ; + } + else + { dword = psf->filelength - au_fmt.dataoffset ; + psf_log_printf (psf, " Data Size : %d (should be %d)\n", au_fmt.datasize, dword) ; + au_fmt.datasize = dword ; + } ; + + psf->dataoffset = au_fmt.dataoffset ; + psf->datalength = psf->filelength - psf->dataoffset ; + + if (psf_ftell (psf) < psf->dataoffset) + psf_binheader_readf (psf, "j", psf->dataoffset - psf_ftell (psf)) ; + + psf->sf.samplerate = au_fmt.samplerate ; + psf->sf.channels = au_fmt.channels ; + + /* Only fill in type major. */ + if (psf->endian == SF_ENDIAN_BIG) + psf->sf.format = SF_FORMAT_AU ; + else if (psf->endian == SF_ENDIAN_LITTLE) + psf->sf.format = SF_ENDIAN_LITTLE | SF_FORMAT_AU ; + + psf_log_printf (psf, " Encoding : %d => ", au_fmt.encoding) ; + + psf->sf.format = psf->sf.format & SF_FORMAT_ENDMASK ; + + switch (au_fmt.encoding) + { case AU_ENCODING_ULAW_8 : + psf->sf.format |= SF_FORMAT_AU | SF_FORMAT_ULAW ; + psf->bytewidth = 1 ; /* Before decoding */ + psf_log_printf (psf, "8-bit ISDN u-law\n") ; + break ; + + case AU_ENCODING_PCM_8 : + psf->sf.format |= SF_FORMAT_AU | SF_FORMAT_PCM_S8 ; + psf->bytewidth = 1 ; + psf_log_printf (psf, "8-bit linear PCM\n") ; + break ; + + case AU_ENCODING_PCM_16 : + psf->sf.format |= SF_FORMAT_AU | SF_FORMAT_PCM_16 ; + psf->bytewidth = 2 ; + psf_log_printf (psf, "16-bit linear PCM\n") ; + break ; + + case AU_ENCODING_PCM_24 : + psf->sf.format |= SF_FORMAT_AU | SF_FORMAT_PCM_24 ; + psf->bytewidth = 3 ; + psf_log_printf (psf, "24-bit linear PCM\n") ; + break ; + + case AU_ENCODING_PCM_32 : + psf->sf.format |= SF_FORMAT_AU | SF_FORMAT_PCM_32 ; + psf->bytewidth = 4 ; + psf_log_printf (psf, "32-bit linear PCM\n") ; + break ; + + case AU_ENCODING_FLOAT : + psf->sf.format |= SF_FORMAT_AU | SF_FORMAT_FLOAT ; + psf->bytewidth = 4 ; + psf_log_printf (psf, "32-bit float\n") ; + break ; + + case AU_ENCODING_DOUBLE : + psf->sf.format |= SF_FORMAT_AU | SF_FORMAT_DOUBLE ; + psf->bytewidth = 8 ; + psf_log_printf (psf, "64-bit double precision float\n") ; + break ; + + case AU_ENCODING_ALAW_8 : + psf->sf.format |= SF_FORMAT_AU | SF_FORMAT_ALAW ; + psf->bytewidth = 1 ; /* Before decoding */ + psf_log_printf (psf, "8-bit ISDN A-law\n") ; + break ; + + case AU_ENCODING_ADPCM_G721_32 : + psf->sf.format |= SF_FORMAT_AU | SF_FORMAT_G721_32 ; + psf->bytewidth = 0 ; + psf_log_printf (psf, "G721 32kbs ADPCM\n") ; + break ; + + case AU_ENCODING_ADPCM_G723_24 : + psf->sf.format |= SF_FORMAT_AU | SF_FORMAT_G723_24 ; + psf->bytewidth = 0 ; + psf_log_printf (psf, "G723 24kbs ADPCM\n") ; + break ; + + case AU_ENCODING_ADPCM_G723_40 : + psf->sf.format |= SF_FORMAT_AU | SF_FORMAT_G723_40 ; + psf->bytewidth = 0 ; + psf_log_printf (psf, "G723 40kbs ADPCM\n") ; + break ; + + case AU_ENCODING_ADPCM_G722 : + psf_log_printf (psf, "G722 64 kbs ADPCM (unsupported)\n") ; + break ; + + case AU_ENCODING_NEXT : + psf_log_printf (psf, "Weird NeXT encoding format (unsupported)\n") ; + break ; + + default : + psf_log_printf (psf, "Unknown!!\n") ; + break ; + } ; + + psf_log_printf (psf, " Sample Rate : %d\n", au_fmt.samplerate) ; + psf_log_printf (psf, " Channels : %d\n", au_fmt.channels) ; + + psf->blockwidth = psf->sf.channels * psf->bytewidth ; + + if (! psf->sf.frames && psf->blockwidth) + psf->sf.frames = (psf->filelength - psf->dataoffset) / psf->blockwidth ; + + return 0 ; +} /* au_read_header */ +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 31f691b1-cde9-4ed2-9469-6bca60fb9cd0 +*/ diff --git a/src/audio_detect.c b/src/audio_detect.c new file mode 100644 index 00000000..5a1e436a --- /dev/null +++ b/src/audio_detect.c @@ -0,0 +1,106 @@ +/* +** Copyright (C) 1999-2006 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + + +#include "sfconfig.h" + +#include +#include + +#if HAVE_UNISTD_H +#include +#endif + +#include +#include + +#include "common.h" +#include "sfendian.h" + +typedef struct +{ int le_float ; + int be_float ; + int le_int_24_32 ; + int be_int_24_32 ; +} VOTE ; + + +static void vote_for_format (VOTE * vote, const unsigned char * data, int datalen) ; + +int +audio_detect (SF_PRIVATE * psf, AUDIO_DETECT *ad, const unsigned char * data, int datalen) +{ VOTE vote ; + + if (psf == NULL) + return 0 ; + + if (ad == NULL || datalen < 256) + return 0 ; + + vote_for_format (&vote, data, datalen) ; + + psf_log_printf (psf, "audio_detect :\n" + " le_float : %d\n" + " be_float : %d\n" + " le_int_24_32 : %d\n" + " be_int_24_32 : %d\n", + vote.le_float, vote.be_float, vote.le_int_24_32, vote.be_int_24_32) ; + + if (0) puts (psf->logbuffer) ; + + if (ad->endianness == SF_ENDIAN_LITTLE && vote.le_float > (3 * datalen) / 4) + { /* Almost certainly 32 bit floats. */ + return SF_FORMAT_FLOAT ; + } ; + + if (ad->endianness == SF_ENDIAN_LITTLE && vote.le_int_24_32 > (3 * datalen) / 4) + { /* Almost certainly 24 bit data stored in 32 bit ints. */ + return SF_FORMAT_PCM_32 ; + } ; + + return 0 ; +} /* data_detect */ + +static void +vote_for_format (VOTE * vote, const unsigned char * data, int datalen) +{ + int k ; + + memset (vote, 0, sizeof (VOTE)) ; + + datalen -= datalen % 4 ; + + for (k = 0 ; k < datalen ; k ++) + { if ((k % 4) == 0) + { if (data [k] == 0 && data [k + 1] != 0) + vote->le_int_24_32 += 4 ; + + if (data [2] != 0 && data [3] == 0) + vote->le_int_24_32 += 4 ; + + if (data [0] != 0 && data [3] > 0x43 && data [3] < 0x4B) + vote->le_float += 4 ; + + if (data [3] != 0 && data [0] > 0x43 && data [0] < 0x4B) + vote->be_float += 4 ; + } ; + } ; + + return ; +} /* vote_for_format */ + diff --git a/src/avr.c b/src/avr.c new file mode 100644 index 00000000..ad02c048 --- /dev/null +++ b/src/avr.c @@ -0,0 +1,254 @@ +/* +** Copyright (C) 2004-2006 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include + +#include "sndfile.h" +#include "sfendian.h" +#include "common.h" + +#define TWOBIT_MARKER (MAKE_MARKER ('2', 'B', 'I', 'T')) +#define AVR_HDR_SIZE 128 + +#define SFE_AVR_X 666 + +/* +** From: hyc@hanauma.Jpl.Nasa.Gov (Howard Chu) +** +** A lot of PD software exists to play Mac .snd files on the ST. One other +** format that seems pretty popular (used by a number of commercial packages) +** is the AVR format (from Audio Visual Research). This format has a 128 byte +** header that looks like this (its actually packed, but thats not portable): +*/ + +typedef struct +{ int marker ; /* 2BIT */ + char name [8] ; /* null-padded sample name */ + short mono ; /* 0 = mono, 0xffff = stereo */ + short rez ; /* 8 = 8 bit, 16 = 16 bit */ + short sign ; /* 0 = unsigned, 0xffff = signed */ + + short loop ; /* 0 = no loop, 0xffff = looping sample */ + short midi ; /* 0xffff = no MIDI note assigned, */ + /* 0xffXX = single key note assignment */ + /* 0xLLHH = key split, low/hi note */ + int srate ; /* sample frequency in hertz */ + int frames ; /* sample length in bytes or words (see rez) */ + int lbeg ; /* offset to start of loop in bytes or words. */ + /* set to zero if unused */ + int lend ; /* offset to end of loop in bytes or words. */ + /* set to sample length if unused */ + short res1 ; /* Reserved, MIDI keyboard split */ + short res2 ; /* Reserved, sample compression */ + short res3 ; /* Reserved */ + char ext [20] ; /* Additional filename space, used if (name[7] != 0) */ + char user [64] ; /* User defined. Typically ASCII message */ +} AVR_HEADER ; + +/*------------------------------------------------------------------------------ +** Private static functions. +*/ + +static int avr_close (SF_PRIVATE *psf) ; + +static int avr_read_header (SF_PRIVATE *psf) ; +static int avr_write_header (SF_PRIVATE *psf, int calc_length) ; + +/*------------------------------------------------------------------------------ +** Public function. +*/ + +int +avr_open (SF_PRIVATE *psf) +{ int error = 0 ; + + if (psf->mode == SFM_READ || (psf->mode == SFM_RDWR && psf->filelength > 0)) + { if ((error = avr_read_header (psf))) + return error ; + } ; + + if ((psf->sf.format & SF_FORMAT_TYPEMASK) != SF_FORMAT_AVR) + return SFE_BAD_OPEN_FORMAT ; + + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + { psf->endian = psf->sf.format & SF_FORMAT_ENDMASK ; + psf->endian = SF_ENDIAN_BIG ; + + if (avr_write_header (psf, SF_FALSE)) + return psf->error ; + + psf->write_header = avr_write_header ; + } ; + + psf->container_close = avr_close ; + + psf->blockwidth = psf->bytewidth * psf->sf.channels ; + + error = pcm_init (psf) ; + + return error ; +} /* avr_open */ + +static int +avr_read_header (SF_PRIVATE *psf) +{ AVR_HEADER hdr ; + + memset (&hdr, 0, sizeof (hdr)) ; + + psf_binheader_readf (psf, "pmb", 0, &hdr.marker, &hdr.name, sizeof (hdr.name)) ; + psf_log_printf (psf, "%M\n", hdr.marker) ; + + if (hdr.marker != TWOBIT_MARKER) + return SFE_AVR_X ; + + psf_log_printf (psf, " Name : %s\n", hdr.name) ; + + psf_binheader_readf (psf, "E22222", &hdr.mono, &hdr.rez, &hdr.sign, &hdr.loop, &hdr.midi) ; + + psf->sf.channels = (hdr.mono & 1) + 1 ; + + psf_log_printf (psf, " Channels : %d\n Bit width : %d\n Signed : %s\n", + (hdr.mono & 1) + 1, hdr.rez, hdr.sign ? "yes" : "no") ; + + switch ((hdr.rez << 16) + (hdr.sign & 1)) + { case ((8 << 16) + 0) : + psf->sf.format = SF_FORMAT_AVR | SF_FORMAT_PCM_U8 ; + psf->bytewidth = 1 ; + break ; + + case ((8 << 16) + 1) : + psf->sf.format = SF_FORMAT_AVR | SF_FORMAT_PCM_S8 ; + psf->bytewidth = 1 ; + break ; + + case ((16 << 16) + 1) : + psf->sf.format = SF_FORMAT_AVR | SF_FORMAT_PCM_16 ; + psf->bytewidth = 2 ; + break ; + + default : + psf_log_printf (psf, "Error : bad rez/sign combination.\n") ; + return SFE_AVR_X ; + } ; + + psf_binheader_readf (psf, "E4444", &hdr.srate, &hdr.frames, &hdr.lbeg, &hdr.lend) ; + + psf->sf.frames = hdr.frames ; + psf->sf.samplerate = hdr.srate ; + + psf_log_printf (psf, " Frames : %D\n", psf->sf.frames) ; + psf_log_printf (psf, " Sample rate : %d\n", psf->sf.samplerate) ; + + psf_binheader_readf (psf, "E222", &hdr.res1, &hdr.res2, &hdr.res3) ; + psf_binheader_readf (psf, "bb", hdr.ext, sizeof (hdr.ext), hdr.user, sizeof (hdr.user)) ; + + psf_log_printf (psf, " Ext : %s\n User : %s\n", hdr.ext, hdr.user) ; + + psf->endian = SF_ENDIAN_BIG ; + + psf->dataoffset = AVR_HDR_SIZE ; + psf->datalength = hdr.frames * (hdr.rez / 8) ; + + if (psf->fileoffset > 0) + psf->filelength = AVR_HDR_SIZE + psf->datalength ; + + if (psf_ftell (psf) != psf->dataoffset) + psf_binheader_readf (psf, "j", psf->dataoffset - psf_ftell (psf)) ; + + psf->blockwidth = psf->sf.channels * psf->bytewidth ; + + if (psf->sf.frames == 0 && psf->blockwidth) + psf->sf.frames = (psf->filelength - psf->dataoffset) / psf->blockwidth ; + + return 0 ; +} /* avr_read_header */ + +static int +avr_write_header (SF_PRIVATE *psf, int calc_length) +{ sf_count_t current ; + int sign ; + + if (psf->pipeoffset > 0) + return 0 ; + + current = psf_ftell (psf) ; + + if (calc_length) + { psf->filelength = psf_get_filelen (psf) ; + + psf->datalength = psf->filelength - psf->dataoffset ; + if (psf->dataend) + psf->datalength -= psf->filelength - psf->dataend ; + + psf->sf.frames = psf->datalength / (psf->bytewidth * psf->sf.channels) ; + } ; + + /* Reset the current header length to zero. */ + psf->header [0] = 0 ; + psf->headindex = 0 ; + + /* + ** Only attempt to seek if we are not writng to a pipe. If we are + ** writing to a pipe we shouldn't be here anyway. + */ + if (psf->is_pipe == SF_FALSE) + psf_fseek (psf, 0, SEEK_SET) ; + + psf_binheader_writef (psf, "Emz22", TWOBIT_MARKER, make_size_t (8), + psf->sf.channels == 2 ? 0xFFFF : 0, psf->bytewidth * 8) ; + + sign = ((psf->sf.format & SF_FORMAT_SUBMASK) == SF_FORMAT_PCM_U8) ? 0 : 0xFFFF ; + + psf_binheader_writef (psf, "E222", sign, 0, 0xFFFF) ; + psf_binheader_writef (psf, "E4444", psf->sf.samplerate, psf->sf.frames, 0, 0) ; + + psf_binheader_writef (psf, "E222zz", 0, 0, 0, make_size_t (20), make_size_t (64)) ; + + /* Header construction complete so write it out. */ + psf_fwrite (psf->header, psf->headindex, 1, psf) ; + + if (psf->error) + return psf->error ; + + psf->dataoffset = psf->headindex ; + + if (current > 0) + psf_fseek (psf, current, SEEK_SET) ; + + return psf->error ; +} /* avr_write_header */ + +static int +avr_close (SF_PRIVATE *psf) +{ + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + avr_write_header (psf, SF_TRUE) ; + + return 0 ; +} /* avr_close */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 0823d454-f39a-4a28-a776-607f1ef33b52 +*/ diff --git a/src/binheader_writef_check.py b/src/binheader_writef_check.py new file mode 100755 index 00000000..422d2854 --- /dev/null +++ b/src/binheader_writef_check.py @@ -0,0 +1,114 @@ +#!/usr/bin/python2.4 + +# Copyright (C) 2006 Erik de Castro Lopo +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the author nor the names of any contributors may be used +# to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import re, string, sys + +_whitespace_re = re.compile ("\s+", re.MULTILINE) + +def find_binheader_writefs (data): + lst = re.findall ('psf_binheader_writef\s*\(\s*[a-zA-Z_]+\s*,\s*\"[^;]+;', data, re.MULTILINE) + return [_whitespace_re.sub (" ", x) for x in lst] + +def find_format_string (s): + fmt = re.search ('"([^"]+)"', s) + if not fmt: + print "Bad format in :\n\n\t%s\n\n" % s + sys.exit (1) + fmt = fmt.groups () + if len (fmt) != 1: + print "Bad format in :\n\n\t%s\n\n" % s + sys.exit (1) + return _whitespace_re.sub ("", fmt [0]) + +def get_param_list (data): + dlist = re.search ("\((.+)\)\s*;", data) + dlist = dlist.groups ()[0] + dlist = string.split (dlist, ",") + dlist = [string.strip (x) for x in dlist] + return dlist [2:] + +def handle_file (fname): + errors = 0 + data = open (fname, "r").read () + + writefs = find_binheader_writefs (data) + for item in writefs: + fmt = find_format_string (item) + params = get_param_list (item) + param_index = 0 + + # print item + + for ch in fmt: + if ch in 'Eet ': + continue + + # print " param [%d] %c : %s" % (param_index, ch, params [param_index]) + + if ch != 'b': + param_index += 1 + continue + + # print item + # print " param [%d] %c : %s <-> %s" % (param_index, ch, params [param_index], params [param_index + 1]) + + if string.find (params [param_index + 1], "sizeof") < 0 \ + and string.find (params [param_index + 1], "make_size_t") < 0 \ + and string.find (params [param_index + 1], "strlen") < 0: + if errors == 0: print + print "\n%s :" % fname + print " param [%d] %c : %s <-> %s" % (param_index, ch, params [param_index], params [param_index + 1]) + print " %s" % item + errors += 1 + param_index += 2 + + return errors + +#=============================================================================== + +if len (sys.argv) > 1: + print "%s\n binheader_writef_check :" % sys.argv [0], + sys.stdout.flush () + errors = 0 + for fname in sys.argv [1:]: + errors += handle_file (fname) + if errors > 0: + print "\nErrors : %d\n" % errors + sys.exit (1) + +print "ok" + +# Do not edit or modify anything in this comment block. +# The following line is a file identity tag for the GNU Arch +# revision control system. +# +# arch-tag: 4ed34789-925a-4135-af90-2e51523ca1ce diff --git a/src/broadcast.c b/src/broadcast.c new file mode 100644 index 00000000..e3322777 --- /dev/null +++ b/src/broadcast.c @@ -0,0 +1,89 @@ +/* +** Copyright (C) 2006 Paul Davis +** Copyright (C) 2006 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include + +#include "common.h" + +/* +** Allocate and initialize a broadcast info structure. +*/ + +SF_BROADCAST_INFO* +broadcast_info_alloc (void) +{ SF_BROADCAST_INFO* bext ; + + if ((bext = calloc (1, sizeof (SF_BROADCAST_INFO))) == NULL) + return NULL ; + + return bext ; +} /* broadcast_info_alloc */ + +int +broadcast_info_copy (SF_BROADCAST_INFO* dst, SF_BROADCAST_INFO* src) +{ memcpy (dst, src, sizeof (SF_BROADCAST_INFO)) ; + + /* Currently writing this version. */ + dst->version = 1 ; + + return SF_TRUE ; +} /* broadcast_info_copy */ + +int +broadcast_add_coding_history (SF_BROADCAST_INFO* bext, unsigned int channels, unsigned int samplerate) +{ char chnstr [16] ; + int count ; + + switch (channels) + { case 0 : + return SF_FALSE ; + + case 1 : + strncpy (chnstr, "mono", sizeof (chnstr)) ; + break ; + + case 2 : + strncpy (chnstr, "stereo", sizeof (chnstr)) ; + break ; + + default : + LSF_SNPRINTF (chnstr, sizeof (chnstr), "%uchn", channels) ; + break ; + } + + count = LSF_SNPRINTF (bext->coding_history, sizeof (bext->coding_history), "F=%u,A=PCM,M=%s,W=24,T=%s-%s", samplerate, chnstr, PACKAGE, VERSION) ; + + if (count >= SIGNED_SIZEOF (bext->coding_history)) + bext->coding_history_size = sizeof (bext->coding_history) ; + else + { count += count & 1 ; + bext->coding_history_size = count ; + } ; + + return SF_TRUE ; +} /* broadcast_add_coding_history */ + +/* +** Do not edit or modify anything in this comment block. +** The following line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 4b3b69c7-d710-4424-9da0-5048534a0beb +*/ diff --git a/src/caf.c b/src/caf.c new file mode 100644 index 00000000..7a1e5c97 --- /dev/null +++ b/src/caf.c @@ -0,0 +1,538 @@ +/* +** Copyright (C) 2005, 2006 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include +#include + +#include "sndfile.h" +#include "sfendian.h" +#include "float_cast.h" +#include "common.h" + +/*------------------------------------------------------------------------------ +** Macros to handle big/little endian issues. +*/ + +#define aac_MARKER MAKE_MARKER ('a', 'a', 'c', ' ') +#define alac_MARKER MAKE_MARKER ('a', 'l', 'a', 'c') +#define alaw_MARKER MAKE_MARKER ('a', 'l', 'a', 'w') +#define caff_MARKER MAKE_MARKER ('c', 'a', 'f', 'f') +#define chan_MARKER MAKE_MARKER ('c', 'h', 'a', 'n') +#define data_MARKER MAKE_MARKER ('d', 'a', 't', 'a') +#define desc_MARKER MAKE_MARKER ('d', 'e', 's', 'c') +#define edct_MARKER MAKE_MARKER ('e', 'd', 'c', 't') +#define free_MARKER MAKE_MARKER ('f', 'r', 'e', 'e') +#define ima4_MARKER MAKE_MARKER ('i', 'm', 'a', '4') +#define info_MARKER MAKE_MARKER ('i', 'n', 'f', 'o') +#define inst_MARKER MAKE_MARKER ('i', 'n', 's', 't') +#define kuki_MARKER MAKE_MARKER ('k', 'u', 'k', 'i') +#define lpcm_MARKER MAKE_MARKER ('l', 'p', 'c', 'm') +#define mark_MARKER MAKE_MARKER ('m', 'a', 'r', 'k') +#define midi_MARKER MAKE_MARKER ('m', 'i', 'd', 'i') +#define mp1_MARKER MAKE_MARKER ('.', 'm', 'p', '1') +#define mp2_MARKER MAKE_MARKER ('.', 'm', 'p', '2') +#define mp3_MARKER MAKE_MARKER ('.', 'm', 'p', '3') +#define ovvw_MARKER MAKE_MARKER ('o', 'v', 'v', 'w') +#define pakt_MARKER MAKE_MARKER ('p', 'a', 'k', 't') +#define peak_MARKER MAKE_MARKER ('p', 'e', 'a', 'k') +#define regn_MARKER MAKE_MARKER ('r', 'e', 'g', 'n') +#define strg_MARKER MAKE_MARKER ('s', 't', 'r', 'g') +#define umid_MARKER MAKE_MARKER ('u', 'm', 'i', 'd') +#define uuid_MARKER MAKE_MARKER ('u', 'u', 'i', 'd') +#define ulaw_MARKER MAKE_MARKER ('u', 'l', 'a', 'w') +#define MAC3_MARKER MAKE_MARKER ('M', 'A', 'C', '3') +#define MAC6_MARKER MAKE_MARKER ('M', 'A', 'C', '6') + +#define CAF_PEAK_CHUNK_SIZE(ch) ((int) (sizeof (int) + ch * (sizeof (float) + 8))) + +#define SFE_CAF_NOT_CAF 666 +#define SFE_CAF_NO_DESC 667 +#define SFE_CAF_BAD_PEAK 668 + +/*------------------------------------------------------------------------------ +** Typedefs. +*/ + +typedef struct +{ unsigned char srate [8] ; + unsigned int fmt_id ; + unsigned int fmt_flags ; + unsigned int pkt_bytes ; + unsigned int pkt_frames ; + unsigned int channels_per_frame ; + unsigned int bits_per_chan ; +} DESC_CHUNK ; + +/*------------------------------------------------------------------------------ +** Private static functions. +*/ + +static int caf_close (SF_PRIVATE *psf) ; +static int caf_read_header (SF_PRIVATE *psf) ; +static int caf_write_header (SF_PRIVATE *psf, int calc_length) ; + +/*------------------------------------------------------------------------------ +** Public function. +*/ + +int +caf_open (SF_PRIVATE *psf) +{ int subformat, format, error = 0 ; + + if (psf->mode == SFM_READ || (psf->mode == SFM_RDWR && psf->filelength > 0)) + { if ((error = caf_read_header (psf))) + return error ; + } ; + + subformat = psf->sf.format & SF_FORMAT_SUBMASK ; + + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + { if (psf->is_pipe) + return SFE_NO_PIPE_WRITE ; + + format = psf->sf.format & SF_FORMAT_TYPEMASK ; + if (format != SF_FORMAT_CAF) + return SFE_BAD_OPEN_FORMAT ; + + psf->blockwidth = psf->bytewidth * psf->sf.channels ; + + if (psf->mode != SFM_RDWR || psf->filelength < 44) + { psf->filelength = 0 ; + psf->datalength = 0 ; + psf->dataoffset = 0 ; + psf->sf.frames = 0 ; + } ; + + psf->str_flags = SF_STR_ALLOW_START ; + + /* + ** By default, add the peak chunk to floating point files. Default behaviour + ** can be switched off using sf_command (SFC_SET_PEAK_CHUNK, SF_FALSE). + */ + if (psf->mode == SFM_WRITE && (subformat == SF_FORMAT_FLOAT || subformat == SF_FORMAT_DOUBLE)) + { if ((psf->peak_info = peak_info_calloc (psf->sf.channels)) == NULL) + return SFE_MALLOC_FAILED ; + psf->peak_info->peak_loc = SF_PEAK_START ; + } ; + + if ((error = caf_write_header (psf, SF_FALSE)) != 0) + return error ; + + psf->write_header = caf_write_header ; + } ; + + psf->container_close = caf_close ; + /*psf->command = caf_command ;*/ + + switch (subformat) + { case SF_FORMAT_PCM_S8 : + case SF_FORMAT_PCM_16 : + case SF_FORMAT_PCM_24 : + case SF_FORMAT_PCM_32 : + error = pcm_init (psf) ; + break ; + + case SF_FORMAT_ULAW : + error = ulaw_init (psf) ; + break ; + + case SF_FORMAT_ALAW : + error = alaw_init (psf) ; + break ; + + /* Lite remove start */ + case SF_FORMAT_FLOAT : + error = float32_init (psf) ; + break ; + + case SF_FORMAT_DOUBLE : + error = double64_init (psf) ; + break ; + /* Lite remove end */ + + default : + return SFE_UNSUPPORTED_ENCODING ; + } ; + + return error ; +} /* caf_open */ + +static int +caf_close (SF_PRIVATE *psf) +{ + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + caf_write_header (psf, SF_TRUE) ; + + return 0 ; +} /* caf_close */ + +/*------------------------------------------------------------------------------ +*/ + +static int +decode_desc_chunk (SF_PRIVATE *psf, const DESC_CHUNK *desc) +{ int format ; + + psf->sf.channels = desc->channels_per_frame ; + + format = SF_FORMAT_CAF | (psf->endian == SF_ENDIAN_LITTLE ? SF_ENDIAN_LITTLE : 0) ; + + if (desc->fmt_id == lpcm_MARKER && desc->fmt_flags & 1) + { /* Floating point data. */ + if (desc->bits_per_chan == 32 && desc->pkt_bytes == 4 * desc->channels_per_frame) + { psf->bytewidth = 4 ; + return format | SF_FORMAT_FLOAT ; + } ; + if (desc->bits_per_chan == 64 && desc->pkt_bytes == 8 * desc->channels_per_frame) + { psf->bytewidth = 8 ; + return format | SF_FORMAT_DOUBLE ; + } ; + } ; + + if ((desc->fmt_flags & 1) != 0) + { psf_log_printf (psf, "**** Ooops, 'desc' chunk suggests float data, but other info invalid.\n") ; + return 0 ; + } ; + + if (desc->fmt_id == lpcm_MARKER) + { /* Integer data. */ + if (desc->bits_per_chan == 32 && desc->pkt_bytes == 4 * desc->channels_per_frame) + { psf->bytewidth = 4 ; + return format | SF_FORMAT_PCM_32 ; + } ; + if (desc->bits_per_chan == 24 && desc->pkt_bytes == 3 * desc->channels_per_frame) + { psf->bytewidth = 3 ; + return format | SF_FORMAT_PCM_24 ; + } ; + if (desc->bits_per_chan == 16 && desc->pkt_bytes == 2 * desc->channels_per_frame) + { psf->bytewidth = 2 ; + return format | SF_FORMAT_PCM_16 ; + } ; + if (desc->bits_per_chan == 8 && desc->pkt_bytes == 1 * desc->channels_per_frame) + { psf->bytewidth = 1 ; + return format | SF_FORMAT_PCM_S8 ; + } ; + } ; + + if (desc->fmt_id == alaw_MARKER && desc->bits_per_chan == 8) + { psf->bytewidth = 1 ; + return format | SF_FORMAT_ALAW ; + } ; + + if (desc->fmt_id == ulaw_MARKER && desc->bits_per_chan == 8) + { psf->bytewidth = 1 ; + return format | SF_FORMAT_ULAW ; + } ; + + return 0 ; +} /* decode_desc_chunk */ + +static int +caf_read_header (SF_PRIVATE *psf) +{ DESC_CHUNK desc ; + sf_count_t chunk_size ; + double srate ; + short version, flags ; + int marker, k, have_data = 0 ; + + memset (&desc, 0, sizeof (desc)) ; + + /* Set position to start of file to begin reading header. */ + psf_binheader_readf (psf, "pmE2E2", 0, &marker, &version, &flags) ; + psf_log_printf (psf, "%M\n Version : %d\n Flags : %x\n", marker, version, flags) ; + if (marker != caff_MARKER) + return SFE_CAF_NOT_CAF ; + + psf_binheader_readf (psf, "mE8b", &marker, &chunk_size, psf->u.ucbuf, 8) ; + srate = double64_be_read (psf->u.ucbuf) ; + LSF_SNPRINTF (psf->u.cbuf, sizeof (psf->u.cbuf), "%5.3f", srate) ; + psf_log_printf (psf, "%M : %D\n Sample rate : %s\n", marker, chunk_size, psf->u.cbuf) ; + if (marker != desc_MARKER) + return SFE_CAF_NO_DESC ; + + if (chunk_size < SIGNED_SIZEOF (DESC_CHUNK)) + { psf_log_printf (psf, "**** Chunk size too small. Should be > 32 bytes.\n") ; + return SFE_MALFORMED_FILE ; + } ; + + psf->sf.samplerate = lrint (srate) ; + + psf_binheader_readf (psf, "mE44444", &desc.fmt_id, &desc.fmt_flags, &desc.pkt_bytes, &desc.pkt_frames, + &desc.channels_per_frame, &desc.bits_per_chan) ; + psf_log_printf (psf, " Format id : %M\n Format flags : %x\n Bytes / packet : %u\n" + " Frames / packet : %u\n Channels / frame : %u\n Bits / channel : %u\n", + desc.fmt_id, desc.fmt_flags, desc.pkt_bytes, desc.pkt_frames, desc.channels_per_frame, desc.bits_per_chan) ; + + if (chunk_size > SIGNED_SIZEOF (DESC_CHUNK)) + psf_binheader_readf (psf, "j", (int) (chunk_size - sizeof (DESC_CHUNK))) ; + + psf->sf.channels = desc.channels_per_frame ; + + while (have_data == 0 && psf_ftell (psf) < psf->filelength - SIGNED_SIZEOF (marker)) + { psf_binheader_readf (psf, "mE8", &marker, &chunk_size) ; + + switch (marker) + { case peak_MARKER : + psf_log_printf (psf, "%M : %D\n", marker, chunk_size) ; + if (chunk_size != CAF_PEAK_CHUNK_SIZE (psf->sf.channels)) + { psf_binheader_readf (psf, "j", (int) chunk_size) ; + psf_log_printf (psf, "*** File PEAK chunk %D should be %d.\n", chunk_size, CAF_PEAK_CHUNK_SIZE (psf->sf.channels)) ; + return SFE_CAF_BAD_PEAK ; + } ; + + if ((psf->peak_info = peak_info_calloc (psf->sf.channels)) == NULL) + return SFE_MALLOC_FAILED ; + + /* read in rest of PEAK chunk. */ + psf_binheader_readf (psf, "E4", & (psf->peak_info->edit_number)) ; + psf_log_printf (psf, " edit count : %d\n", psf->peak_info->edit_number) ; + + psf_log_printf (psf, " Ch Position Value\n") ; + for (k = 0 ; k < psf->sf.channels ; k++) + { sf_count_t position ; + float value ; + + psf_binheader_readf (psf, "Ef8", &value, &position) ; + psf->peak_info->peaks [k].value = value ; + psf->peak_info->peaks [k].position = position ; + + LSF_SNPRINTF (psf->u.cbuf, sizeof (psf->u.cbuf), " %2d %-12ld %g\n", k, (long) position, value) ; + psf_log_printf (psf, psf->u.cbuf) ; + } ; + + psf->peak_info->peak_loc = SF_PEAK_START ; + break ; + + case free_MARKER : + psf_log_printf (psf, "%M : %D\n", marker, chunk_size) ; + psf_binheader_readf (psf, "j", (int) chunk_size) ; + break ; + + case data_MARKER : + psf_log_printf (psf, "%M : %D\n", marker, chunk_size) ; + psf_binheader_readf (psf, "E4", &k) ; + psf_log_printf (psf, " edit : %u\n", k) ; + have_data = 1 ; + break ; + + default : + psf_log_printf (psf, " %M : %D (skipped)\n", marker, chunk_size) ; + psf_binheader_readf (psf, "j", (int) chunk_size) ; + break ; + } ; + } ; + + if (have_data == 0) + { psf_log_printf (psf, "**** Error, could not find 'data' chunk.\n") ; + return SFE_MALFORMED_FILE ; + } ; + + psf_log_printf (psf, "End\n") ; + + psf->dataoffset = psf_ftell (psf) ; + psf->datalength = psf->filelength - psf->dataoffset ; + psf->endian = (desc.fmt_flags & 2) ? SF_ENDIAN_LITTLE : SF_ENDIAN_BIG ; + + if ((psf->sf.format = decode_desc_chunk (psf, &desc)) == 0) + return SFE_UNSUPPORTED_ENCODING ; + + if (psf->bytewidth > 0) + psf->sf.frames = psf->datalength / psf->bytewidth ; + + return 0 ; +} /* caf_read_header */ + +/*------------------------------------------------------------------------------ +*/ + +static int +caf_write_header (SF_PRIVATE *psf, int calc_length) +{ DESC_CHUNK desc ; + sf_count_t current, free_len ; + int subformat ; + + memset (&desc, 0, sizeof (desc)) ; + + current = psf_ftell (psf) ; + + if (calc_length) + { psf->filelength = psf_get_filelen (psf) ; + + psf->datalength = psf->filelength - psf->dataoffset ; + + if (psf->dataend) + psf->datalength -= psf->filelength - psf->dataend ; + + if (psf->bytewidth > 0) + psf->sf.frames = psf->datalength / (psf->bytewidth * psf->sf.channels) ; + } ; + + /* Reset the current header length to zero. */ + psf->header [0] = 0 ; + psf->headindex = 0 ; + psf_fseek (psf, 0, SEEK_SET) ; + + /* 'caff' marker, version and flags. */ + psf_binheader_writef (psf, "Em22", caff_MARKER, 1, 0) ; + + /* 'desc' marker and chunk size. */ + psf_binheader_writef (psf, "Em8", desc_MARKER, (sf_count_t) (sizeof (DESC_CHUNK))) ; + + double64_be_write (1.0 * psf->sf.samplerate, psf->u.ucbuf) ; + psf_binheader_writef (psf, "b", psf->u.ucbuf, make_size_t (8)) ; + + subformat = psf->sf.format & SF_FORMAT_SUBMASK ; + + psf->endian = psf->sf.format & SF_FORMAT_ENDMASK ; + + if (CPU_IS_BIG_ENDIAN && (psf->endian == 0 || psf->endian == SF_ENDIAN_CPU)) + psf->endian = SF_ENDIAN_BIG ; + else if (CPU_IS_LITTLE_ENDIAN && (psf->endian == SF_ENDIAN_LITTLE || psf->endian == SF_ENDIAN_CPU)) + psf->endian = SF_ENDIAN_LITTLE ; + + if (psf->endian == SF_ENDIAN_LITTLE) + desc.fmt_flags = 2 ; + else + psf->endian = SF_ENDIAN_BIG ; + + /* initial section (same for all, it appears) */ + switch (subformat) + { case SF_FORMAT_PCM_S8 : + desc.fmt_id = lpcm_MARKER ; + psf->bytewidth = 1 ; + desc.pkt_bytes = psf->bytewidth * psf->sf.channels ; + desc.pkt_frames = 1 ; + desc.channels_per_frame = psf->sf.channels ; + desc.bits_per_chan = 8 ; + break ; + + case SF_FORMAT_PCM_16 : + desc.fmt_id = lpcm_MARKER ; + psf->bytewidth = 2 ; + desc.pkt_bytes = psf->bytewidth * psf->sf.channels ; + desc.pkt_frames = 1 ; + desc.channels_per_frame = psf->sf.channels ; + desc.bits_per_chan = 16 ; + break ; + + case SF_FORMAT_PCM_24 : + psf->bytewidth = 3 ; + desc.pkt_bytes = psf->bytewidth * psf->sf.channels ; + desc.pkt_frames = 1 ; + desc.channels_per_frame = psf->sf.channels ; + desc.bits_per_chan = 24 ; + desc.fmt_id = lpcm_MARKER ; + break ; + + case SF_FORMAT_PCM_32 : + desc.fmt_id = lpcm_MARKER ; + psf->bytewidth = 4 ; + desc.pkt_bytes = psf->bytewidth * psf->sf.channels ; + desc.pkt_frames = 1 ; + desc.channels_per_frame = psf->sf.channels ; + desc.bits_per_chan = 32 ; + break ; + + case SF_FORMAT_FLOAT : + desc.fmt_id = lpcm_MARKER ; + desc.fmt_flags |= 1 ; + psf->bytewidth = 4 ; + desc.pkt_bytes = psf->bytewidth * psf->sf.channels ; + desc.pkt_frames = 1 ; + desc.channels_per_frame = psf->sf.channels ; + desc.bits_per_chan = 32 ; + break ; + + case SF_FORMAT_DOUBLE : + desc.fmt_id = lpcm_MARKER ; + desc.fmt_flags |= 1 ; + psf->bytewidth = 8 ; + desc.pkt_bytes = psf->bytewidth * psf->sf.channels ; + desc.pkt_frames = 1 ; + desc.channels_per_frame = psf->sf.channels ; + desc.bits_per_chan = 64 ; + break ; + + case SF_FORMAT_ALAW : + desc.fmt_id = alaw_MARKER ; + psf->bytewidth = 1 ; + desc.pkt_bytes = psf->bytewidth * psf->sf.channels ; + desc.pkt_frames = 1 ; + desc.channels_per_frame = psf->sf.channels ; + desc.bits_per_chan = 8 ; + break ; + + case SF_FORMAT_ULAW : + desc.fmt_id = ulaw_MARKER ; + psf->bytewidth = 1 ; + desc.pkt_bytes = psf->bytewidth * psf->sf.channels ; + desc.pkt_frames = 1 ; + desc.channels_per_frame = psf->sf.channels ; + desc.bits_per_chan = 8 ; + break ; + + default : + return SFE_UNIMPLEMENTED ; + } ; + + psf_binheader_writef (psf, "mE44444", desc.fmt_id, desc.fmt_flags, desc.pkt_bytes, desc.pkt_frames, desc.channels_per_frame, desc.bits_per_chan) ; + +#if 0 + if (psf->str_flags & SF_STR_LOCATE_START) + caf_write_strings (psf, SF_STR_LOCATE_START) ; +#endif + + if (psf->peak_info != NULL) + { int k ; + psf_binheader_writef (psf, "Em84", peak_MARKER, (sf_count_t) CAF_PEAK_CHUNK_SIZE (psf->sf.channels), psf->peak_info->edit_number) ; + for (k = 0 ; k < psf->sf.channels ; k++) + psf_binheader_writef (psf, "Ef8", (float) psf->peak_info->peaks [k].value, psf->peak_info->peaks [k].position) ; + } ; + + /* Add free chunk so that the actual audio data starts at a multiple 0x1000. */ + free_len = 0x1000 - psf->headindex - 16 - 12 ; + while (free_len < 0) + free_len += 0x1000 ; + psf_binheader_writef (psf, "Em8z", free_MARKER, free_len, (int) free_len) ; + + psf_binheader_writef (psf, "Em84", data_MARKER, psf->datalength, 0) ; + + psf_fwrite (psf->header, psf->headindex, 1, psf) ; + if (psf->error) + return psf->error ; + + psf->dataoffset = psf->headindex ; + if (current < psf->dataoffset) + psf_fseek (psf, psf->dataoffset, SEEK_SET) ; + else if (current > 0) + psf_fseek (psf, current, SEEK_SET) ; + + return psf->error ; +} /* caf_write_header */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 65883e65-bd3c-4618-9241-d3c02fd630bd +*/ diff --git a/src/command.c b/src/command.c new file mode 100644 index 00000000..d2b697b4 --- /dev/null +++ b/src/command.c @@ -0,0 +1,363 @@ +/* +** Copyright (C) 2001-2004 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include + +#include "sndfile.h" +#include "common.h" + +static SF_FORMAT_INFO const simple_formats [] = +{ + { SF_FORMAT_AIFF | SF_FORMAT_PCM_16, + "AIFF (Apple/SGI 16 bit PCM)", "aiff" + }, + + { SF_FORMAT_AIFF | SF_FORMAT_FLOAT, + "AIFF (Apple/SGI 32 bit float)", "aifc" + }, + + { SF_FORMAT_AIFF | SF_FORMAT_PCM_S8, + "AIFF (Apple/SGI 8 bit PCM)", "aiff" + }, + + { SF_FORMAT_AU | SF_FORMAT_PCM_16, + "AU (Sun/Next 16 bit PCM)", "au" + }, + + { SF_FORMAT_AU | SF_FORMAT_ULAW, + "AU (Sun/Next 8-bit u-law)", "au" + }, + + { SF_FORMAT_CAF | SF_FORMAT_PCM_16, + "CAF (Apple 16 bit PCM)", "caf" + }, + + { SF_FORMAT_FLAC | SF_FORMAT_PCM_16, + "FLAC 16 bit", "flac" + }, + + { SF_FORMAT_RAW | SF_FORMAT_VOX_ADPCM, + "OKI Dialogic VOX ADPCM", "vox" + }, + + { SF_FORMAT_WAV | SF_FORMAT_PCM_16, + "WAV (Microsoft 16 bit PCM)", "wav" + }, + + { SF_FORMAT_WAV | SF_FORMAT_FLOAT, + "WAV (Microsoft 32 bit float)", "wav" + }, + + { SF_FORMAT_WAV | SF_FORMAT_IMA_ADPCM, + "WAV (Microsoft 4 bit IMA ADPCM)", "wav" + }, + + { SF_FORMAT_WAV | SF_FORMAT_MS_ADPCM, + "WAV (Microsoft 4 bit MS ADPCM)", "wav" + }, + + { SF_FORMAT_WAV | SF_FORMAT_PCM_U8, + "WAV (Microsoft 8 bit PCM)", "wav" + }, + +} ; /* simple_formats */ + +int +psf_get_format_simple_count (void) +{ return (sizeof (simple_formats) / sizeof (SF_FORMAT_INFO)) ; +} /* psf_get_format_simple_count */ + +int +psf_get_format_simple (SF_FORMAT_INFO *data) +{ int indx ; + + if (data->format < 0 || data->format >= (SIGNED_SIZEOF (simple_formats) / SIGNED_SIZEOF (SF_FORMAT_INFO))) + return SFE_BAD_CONTROL_CMD ; + + indx = data->format ; + memcpy (data, &(simple_formats [indx]), SIGNED_SIZEOF (SF_FORMAT_INFO)) ; + + return 0 ; +} /* psf_get_format_simple */ + +/*============================================================================ +** Major format info. +*/ + +static SF_FORMAT_INFO const major_formats [] = +{ + { SF_FORMAT_AIFF, "AIFF (Apple/SGI)", "aiff" }, + { SF_FORMAT_AU, "AU (Sun/NeXT)", "au" }, + { SF_FORMAT_AVR, "AVR (Audio Visual Research)", "avr" }, + { SF_FORMAT_CAF, "CAF (Apple Core Audio File)", "caf" }, + { SF_FORMAT_FLAC, "FLAC (FLAC Lossless Audio Codec)", "flac" }, + { SF_FORMAT_HTK, "HTK (HMM Tool Kit)", "htk" }, + { SF_FORMAT_SVX, "IFF (Amiga IFF/SVX8/SV16)", "iff" }, + { SF_FORMAT_MAT4, "MAT4 (GNU Octave 2.0 / Matlab 4.2)", "mat" }, + { SF_FORMAT_MAT5, "MAT5 (GNU Octave 2.1 / Matlab 5.0)", "mat" }, + { SF_FORMAT_PAF, "PAF (Ensoniq PARIS)", "paf" }, + { SF_FORMAT_PVF, "PVF (Portable Voice Format)", "pvf" }, + { SF_FORMAT_RAW, "RAW (header-less)", "raw" }, + { SF_FORMAT_SD2, "SD2 (Sound Designer II)", "sd2" }, + { SF_FORMAT_SDS, "SDS (Midi Sample Dump Standard)", "sds" }, + { SF_FORMAT_IRCAM, "SF (Berkeley/IRCAM/CARL)", "sf" }, + { SF_FORMAT_VOC, "VOC (Creative Labs)", "voc" }, + { SF_FORMAT_W64, "W64 (SoundFoundry WAVE 64)", "w64" }, + { SF_FORMAT_WAV, "WAV (Microsoft)", "wav" }, + { SF_FORMAT_NIST, "WAV (NIST Sphere)", "wav" }, + { SF_FORMAT_WAVEX, "WAVEX (Microsoft)", "wav" }, + { SF_FORMAT_XI, "XI (FastTracker 2)", "xi" }, + +} ; /* major_formats */ + +int +psf_get_format_major_count (void) +{ return (sizeof (major_formats) / sizeof (SF_FORMAT_INFO)) ; +} /* psf_get_format_major_count */ + +int +psf_get_format_major (SF_FORMAT_INFO *data) +{ int indx ; + + if (data->format < 0 || data->format >= (SIGNED_SIZEOF (major_formats) / SIGNED_SIZEOF (SF_FORMAT_INFO))) + return SFE_BAD_CONTROL_CMD ; + + indx = data->format ; + memcpy (data, &(major_formats [indx]), SIGNED_SIZEOF (SF_FORMAT_INFO)) ; + + return 0 ; +} /* psf_get_format_major */ + +/*============================================================================ +** Subtype format info. +*/ + +static SF_FORMAT_INFO subtype_formats [] = +{ + { SF_FORMAT_PCM_S8, "Signed 8 bit PCM", NULL }, + { SF_FORMAT_PCM_16, "Signed 16 bit PCM", NULL }, + { SF_FORMAT_PCM_24, "Signed 24 bit PCM", NULL }, + { SF_FORMAT_PCM_32, "Signed 32 bit PCM", NULL }, + + { SF_FORMAT_PCM_U8, "Unsigned 8 bit PCM", NULL }, + + { SF_FORMAT_FLOAT, "32 bit float", NULL }, + { SF_FORMAT_DOUBLE, "64 bit float", NULL }, + + { SF_FORMAT_ULAW, "U-Law", NULL }, + { SF_FORMAT_ALAW, "A-Law", NULL }, + { SF_FORMAT_IMA_ADPCM, "IMA ADPCM", NULL }, + { SF_FORMAT_MS_ADPCM, "Microsoft ADPCM", NULL }, + + { SF_FORMAT_GSM610, "GSM 6.10", NULL }, + + { SF_FORMAT_G721_32, "32kbs G721 ADPCM", NULL }, + { SF_FORMAT_G723_24, "24kbs G723 ADPCM", NULL }, + + { SF_FORMAT_DWVW_12, "12 bit DWVW", NULL }, + { SF_FORMAT_DWVW_16, "16 bit DWVW", NULL }, + { SF_FORMAT_DWVW_24, "24 bit DWVW", NULL }, + { SF_FORMAT_VOX_ADPCM, "VOX ADPCM", "vox" }, + + { SF_FORMAT_DPCM_16, "16 bit DPCM", NULL }, + { SF_FORMAT_DPCM_8, "8 bit DPCM", NULL } +} ; /* subtype_formats */ + +int +psf_get_format_subtype_count (void) +{ return (sizeof (subtype_formats) / sizeof (SF_FORMAT_INFO)) ; +} /* psf_get_format_subtype_count */ + +int +psf_get_format_subtype (SF_FORMAT_INFO *data) +{ int indx ; + + if (data->format < 0 || data->format >= (SIGNED_SIZEOF (subtype_formats) / SIGNED_SIZEOF (SF_FORMAT_INFO))) + return SFE_BAD_CONTROL_CMD ; + + indx = data->format ; + memcpy (data, &(subtype_formats [indx]), sizeof (SF_FORMAT_INFO)) ; + + return 0 ; +} /* psf_get_format_subtype */ + +/*============================================================================== +*/ + +int +psf_get_format_info (SF_FORMAT_INFO *data) +{ int k, format ; + + if (data->format & SF_FORMAT_TYPEMASK) + { format = data->format & SF_FORMAT_TYPEMASK ; + + for (k = 0 ; k < (SIGNED_SIZEOF (major_formats) / SIGNED_SIZEOF (SF_FORMAT_INFO)) ; k++) + { if (format == major_formats [k].format) + { memcpy (data, &(major_formats [k]), sizeof (SF_FORMAT_INFO)) ; + return 0 ; + } ; + } ; + } + else if (data->format & SF_FORMAT_SUBMASK) + { format = data->format & SF_FORMAT_SUBMASK ; + + for (k = 0 ; k < (SIGNED_SIZEOF (subtype_formats) / SIGNED_SIZEOF (SF_FORMAT_INFO)) ; k++) + { if (format == subtype_formats [k].format) + { memcpy (data, &(subtype_formats [k]), sizeof (SF_FORMAT_INFO)) ; + return 0 ; + } ; + } ; + } ; + + memset (data, 0, sizeof (SF_FORMAT_INFO)) ; + + return SFE_BAD_CONTROL_CMD ; +} /* psf_get_format_info */ + +/*============================================================================== +*/ + +double +psf_calc_signal_max (SF_PRIVATE *psf, int normalize) +{ sf_count_t position ; + double max_val, temp, *data ; + int k, len, readcount, save_state ; + + /* If the file is not seekable, there is nothing we can do. */ + if (! psf->sf.seekable) + { psf->error = SFE_NOT_SEEKABLE ; + return 0.0 ; + } ; + + if (! psf->read_double) + { psf->error = SFE_UNIMPLEMENTED ; + return 0.0 ; + } ; + + save_state = sf_command ((SNDFILE*) psf, SFC_GET_NORM_DOUBLE, NULL, 0) ; + sf_command ((SNDFILE*) psf, SFC_SET_NORM_DOUBLE, NULL, normalize) ; + + /* Brute force. Read the whole file and find the biggest sample. */ + /* Get current position in file */ + position = sf_seek ((SNDFILE*) psf, 0, SEEK_CUR) ; + /* Go to start of file. */ + sf_seek ((SNDFILE*) psf, 0, SEEK_SET) ; + + data = psf->u.dbuf ; + len = ARRAY_LEN (psf->u.dbuf) ; + + for (readcount = 1, max_val = 0.0 ; readcount > 0 ; /* nothing */) + { readcount = sf_read_double ((SNDFILE*) psf, data, len) ; + for (k = 0 ; k < readcount ; k++) + { temp = fabs (data [k]) ; + max_val = temp > max_val ? temp : max_val ; + } ; + } ; + + /* Return to SNDFILE to original state. */ + sf_seek ((SNDFILE*) psf, position, SEEK_SET) ; + sf_command ((SNDFILE*) psf, SFC_SET_NORM_DOUBLE, NULL, save_state) ; + + return max_val ; +} /* psf_calc_signal_max */ + +int +psf_calc_max_all_channels (SF_PRIVATE *psf, double *peaks, int normalize) +{ sf_count_t position ; + double temp, *data ; + int k, len, readcount, save_state ; + int chan ; + + /* If the file is not seekable, there is nothing we can do. */ + if (! psf->sf.seekable) + return (psf->error = SFE_NOT_SEEKABLE) ; + + if (! psf->read_double) + return (psf->error = SFE_UNIMPLEMENTED) ; + + save_state = sf_command ((SNDFILE*) psf, SFC_GET_NORM_DOUBLE, NULL, 0) ; + sf_command ((SNDFILE*) psf, SFC_SET_NORM_DOUBLE, NULL, normalize) ; + + memset (peaks, 0, sizeof (double) * psf->sf.channels) ; + + /* Brute force. Read the whole file and find the biggest sample for each channel. */ + position = sf_seek ((SNDFILE*) psf, 0, SEEK_CUR) ; /* Get current position in file */ + sf_seek ((SNDFILE*) psf, 0, SEEK_SET) ; /* Go to start of file. */ + + len = ARRAY_LEN (psf->u.dbuf) ; + + data = psf->u.dbuf ; + + chan = 0 ; + readcount = len ; + while (readcount > 0) + { readcount = sf_read_double ((SNDFILE*) psf, data, len) ; + for (k = 0 ; k < readcount ; k++) + { temp = fabs (data [k]) ; + peaks [chan] = temp > peaks [chan] ? temp : peaks [chan] ; + chan = (chan + 1) % psf->sf.channels ; + } ; + } ; + + sf_seek ((SNDFILE*) psf, position, SEEK_SET) ; /* Return to original position. */ + + sf_command ((SNDFILE*) psf, SFC_SET_NORM_DOUBLE, NULL, save_state) ; + + return 0 ; +} /* psf_calc_max_all_channels */ + +int +psf_get_signal_max (SF_PRIVATE *psf, double *peak) +{ int k ; + + if (psf->peak_info == NULL) + return SF_FALSE ; + + peak [0] = psf->peak_info->peaks [0].value ; + + for (k = 1 ; k < psf->sf.channels ; k++) + peak [0] = SF_MAX (peak [0], psf->peak_info->peaks [k].value) ; + + return SF_TRUE ; +} /* psf_get_signal_max */ + +int +psf_get_max_all_channels (SF_PRIVATE *psf, double *peaks) +{ int k ; + + if (psf->peak_info == NULL) + return SF_FALSE ; + + for (k = 0 ; k < psf->sf.channels ; k++) + peaks [k] = psf->peak_info->peaks [k].value ; + + return SF_TRUE ; +} /* psf_get_max_all_channels */ + + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 0aae0d9d-ab2b-4d70-ade3-47a534666f8e +*/ diff --git a/src/common.c b/src/common.c new file mode 100644 index 00000000..8e861469 --- /dev/null +++ b/src/common.c @@ -0,0 +1,1291 @@ +/* +** Copyright (C) 1999-2006 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include +#include +#include +#include + +#include "sndfile.h" +#include "sfendian.h" +#include "common.h" + +/*----------------------------------------------------------------------------------------------- +** psf_log_printf allows libsndfile internal functions to print to an internal logbuffer which +** can later be displayed. +** The format specifiers are as for printf but without the field width and other modifiers. +** Printing is performed to the logbuffer char array of the SF_PRIVATE struct. +** Printing is done in such a way as to guarantee that the log never overflows the end of the +** logbuffer array. +*/ + +static inline void +log_putchar (SF_PRIVATE *psf, char ch) +{ if (psf->logindex < SIGNED_SIZEOF (psf->logbuffer) - 1) + { psf->logbuffer [psf->logindex++] = ch ; + psf->logbuffer [psf->logindex] = 0 ; + } ; + return ; +} /* log_putchar */ + +void +psf_log_printf (SF_PRIVATE *psf, const char *format, ...) +{ va_list ap ; + unsigned int u ; + int d, tens, shift, width, width_specifier, left_align ; + char c, *strptr, istr [5], lead_char, sign_char ; + + va_start (ap, format) ; + + while ((c = *format++)) + { if (c != '%') + { log_putchar (psf, c) ; + continue ; + } ; + + if (format [0] == '%') /* Handle %% */ + { log_putchar (psf, '%') ; + format ++ ; + continue ; + } ; + + sign_char = 0 ; + left_align = SF_FALSE ; + while (1) + { switch (format [0]) + { case ' ' : + case '+' : + sign_char = format [0] ; + format ++ ; + continue ; + + case '-' : + left_align = SF_TRUE ; + format ++ ; + continue ; + + default : break ; + } ; + + break ; + } ; + + if (format [0] == 0) + break ; + + lead_char = ' ' ; + if (format [0] == '0') + lead_char = '0' ; + + width_specifier = 0 ; + while ((c = *format++) && isdigit (c)) + width_specifier = width_specifier * 10 + (c - '0') ; + + switch (c) + { case 0 : /* NULL character. */ + va_end (ap) ; + return ; + + case 's': /* string */ + strptr = va_arg (ap, char *) ; + if (strptr == NULL) + break ; + width_specifier -= strlen (strptr) ; + if (left_align == SF_FALSE) + while (width_specifier -- > 0) + log_putchar (psf, ' ') ; + while (*strptr) + log_putchar (psf, *strptr++) ; + while (width_specifier -- > 0) + log_putchar (psf, ' ') ; + break ; + + case 'd': /* int */ + d = va_arg (ap, int) ; + + if (d < 0) + { d = -d ; + sign_char = '-' ; + if (lead_char != '0' && left_align == SF_FALSE) + width_specifier -- ; + } ; + + tens = 1 ; + width = 1 ; + while (d / tens >= 10) + { tens *= 10 ; + width ++ ; + } ; + + width_specifier -= width ; + + if (sign_char == ' ') + { log_putchar (psf, ' ') ; + width_specifier -- ; + } ; + + if (left_align == SF_FALSE && lead_char != '0') + { if (sign_char == '+') + width_specifier -- ; + + while (width_specifier -- > 0) + log_putchar (psf, lead_char) ; + } ; + + if (sign_char == '+' || sign_char == '-') + { log_putchar (psf, sign_char) ; + width_specifier -- ; + } ; + + if (left_align == SF_FALSE) + while (width_specifier -- > 0) + log_putchar (psf, lead_char) ; + + while (tens > 0) + { log_putchar (psf, '0' + d / tens) ; + d %= tens ; + tens /= 10 ; + } ; + + while (width_specifier -- > 0) + log_putchar (psf, lead_char) ; + break ; + + case 'D': /* sf_count_t */ + { sf_count_t D, Tens ; + + D = va_arg (ap, sf_count_t) ; + + if (D == 0) + { while (-- width_specifier > 0) + log_putchar (psf, lead_char) ; + log_putchar (psf, '0') ; + break ; + } + if (D < 0) + { log_putchar (psf, '-') ; + D = -D ; + } ; + Tens = 1 ; + width = 1 ; + while (D / Tens >= 10) + { Tens *= 10 ; + width ++ ; + } ; + + while (width_specifier > width) + { log_putchar (psf, lead_char) ; + width_specifier-- ; + } ; + + while (Tens > 0) + { log_putchar (psf, '0' + D / Tens) ; + D %= Tens ; + Tens /= 10 ; + } ; + } ; + break ; + + case 'u': /* unsigned int */ + u = va_arg (ap, unsigned int) ; + + tens = 1 ; + width = 1 ; + while (u / tens >= 10) + { tens *= 10 ; + width ++ ; + } ; + + width_specifier -= width ; + + if (sign_char == ' ') + { log_putchar (psf, ' ') ; + width_specifier -- ; + } ; + + if (left_align == SF_FALSE && lead_char != '0') + { if (sign_char == '+') + width_specifier -- ; + + while (width_specifier -- > 0) + log_putchar (psf, lead_char) ; + } ; + + if (sign_char == '+' || sign_char == '-') + { log_putchar (psf, sign_char) ; + width_specifier -- ; + } ; + + if (left_align == SF_FALSE) + while (width_specifier -- > 0) + log_putchar (psf, lead_char) ; + + while (tens > 0) + { log_putchar (psf, '0' + u / tens) ; + u %= tens ; + tens /= 10 ; + } ; + + while (width_specifier -- > 0) + log_putchar (psf, lead_char) ; + break ; + + case 'c': /* char */ + c = va_arg (ap, int) & 0xFF ; + log_putchar (psf, c) ; + break ; + + case 'x': /* hex */ + case 'X': /* hex */ + d = va_arg (ap, int) ; + + if (d == 0) + { while (--width_specifier > 0) + log_putchar (psf, lead_char) ; + log_putchar (psf, '0') ; + break ; + } ; + shift = 28 ; + width = (width_specifier < 8) ? 8 : width_specifier ; + while (! ((0xF << shift) & d)) + { shift -= 4 ; + width -- ; + } ; + + while (width > 0 && width_specifier > width) + { log_putchar (psf, lead_char) ; + width_specifier-- ; + } ; + + while (shift >= 0) + { c = (d >> shift) & 0xF ; + log_putchar (psf, (c > 9) ? c + 'A' - 10 : c + '0') ; + shift -= 4 ; + } ; + break ; + + case 'M': /* int2str */ + d = va_arg (ap, int) ; + if (CPU_IS_LITTLE_ENDIAN) + { istr [0] = d & 0xFF ; + istr [1] = (d >> 8) & 0xFF ; + istr [2] = (d >> 16) & 0xFF ; + istr [3] = (d >> 24) & 0xFF ; + } + else + { istr [3] = d & 0xFF ; + istr [2] = (d >> 8) & 0xFF ; + istr [1] = (d >> 16) & 0xFF ; + istr [0] = (d >> 24) & 0xFF ; + } ; + istr [4] = 0 ; + strptr = istr ; + while (*strptr) + { c = *strptr++ ; + log_putchar (psf, c) ; + } ; + break ; + + default : + log_putchar (psf, '*') ; + log_putchar (psf, c) ; + log_putchar (psf, '*') ; + break ; + } /* switch */ + } /* while */ + + va_end (ap) ; + return ; +} /* psf_log_printf */ + +/*----------------------------------------------------------------------------------------------- +** ASCII header printf functions. +** Some formats (ie NIST) use ascii text in their headers. +** Format specifiers are the same as the standard printf specifiers (uses vsnprintf). +** If this generates a compile error on any system, the author should be notified +** so an alternative vsnprintf can be provided. +*/ + +void +psf_asciiheader_printf (SF_PRIVATE *psf, const char *format, ...) +{ va_list argptr ; + int maxlen ; + char *start ; + + maxlen = strlen ((char*) psf->header) ; + start = ((char*) psf->header) + maxlen ; + maxlen = sizeof (psf->header) - maxlen ; + + va_start (argptr, format) ; + LSF_VSNPRINTF (start, maxlen, format, argptr) ; + va_end (argptr) ; + + /* Make sure the string is properly terminated. */ + start [maxlen - 1] = 0 ; + + psf->headindex = strlen ((char*) psf->header) ; + + return ; +} /* psf_asciiheader_printf */ + +/*----------------------------------------------------------------------------------------------- +** Binary header writing functions. Returns number of bytes written. +** +** Format specifiers for psf_binheader_writef are as follows +** m - marker - four bytes - no endian manipulation +** +** e - all following numerical values will be little endian +** E - all following numerical values will be big endian +** +** t - all following O types will be truncated to 4 bytes +** T - switch off truncation of all following O types +** +** 1 - single byte value +** 2 - two byte value +** 3 - three byte value +** 4 - four byte value +** 8 - eight byte value (sometimes written as 4 bytes) +** +** s - string preceded by a four byte length +** S - string including null terminator +** f - floating point data +** d - double precision floating point data +** h - 16 binary bytes value +** +** b - binary data (see below) +** z - zero bytes (ses below) +** j - jump forwards or backwards +** +** To write a word followed by an int (both little endian) use: +** psf_binheader_writef ("e24", wordval, longval) ; +** +** To write binary data use: +** psf_binheader_writef ("b", &bindata, sizeof (bindata)) ; +** +** To write N zero bytes use: +** NOTE: due to platform issues (ie x86-64) you should cast the +** argument to size_t or ensure the variable type is size_t. +** psf_binheader_writef ("z", N) ; +*/ + +/* These macros may seem a bit messy but do prevent problems with processors which +** seg. fault when asked to write an int or short to a non-int/short aligned address. +*/ + +static inline void +header_put_byte (SF_PRIVATE *psf, char x) +{ if (psf->headindex < SIGNED_SIZEOF (psf->header) - 1) + psf->header [psf->headindex++] = x ; +} /* header_put_byte */ + +#if (CPU_IS_BIG_ENDIAN == 1) +static inline void +header_put_marker (SF_PRIVATE *psf, int x) +{ if (psf->headindex < SIGNED_SIZEOF (psf->header) - 4) + { psf->header [psf->headindex++] = (x >> 24) ; + psf->header [psf->headindex++] = (x >> 16) ; + psf->header [psf->headindex++] = (x >> 8) ; + psf->header [psf->headindex++] = x ; + } ; +} /* header_put_marker */ + +#elif (CPU_IS_LITTLE_ENDIAN == 1) +static inline void +header_put_marker (SF_PRIVATE *psf, int x) +{ if (psf->headindex < SIGNED_SIZEOF (psf->header) - 4) + { psf->header [psf->headindex++] = x ; + psf->header [psf->headindex++] = (x >> 8) ; + psf->header [psf->headindex++] = (x >> 16) ; + psf->header [psf->headindex++] = (x >> 24) ; + } ; +} /* header_put_marker */ + +#else +# error "Cannot determine endian-ness of processor." +#endif + + +static inline void +header_put_be_short (SF_PRIVATE *psf, int x) +{ if (psf->headindex < SIGNED_SIZEOF (psf->header) - 2) + { psf->header [psf->headindex++] = (x >> 8) ; + psf->header [psf->headindex++] = x ; + } ; +} /* header_put_be_short */ + +static inline void +header_put_le_short (SF_PRIVATE *psf, int x) +{ if (psf->headindex < SIGNED_SIZEOF (psf->header) - 2) + { psf->header [psf->headindex++] = x ; + psf->header [psf->headindex++] = (x >> 8) ; + } ; +} /* header_put_le_short */ + +static inline void +header_put_be_3byte (SF_PRIVATE *psf, int x) +{ if (psf->headindex < SIGNED_SIZEOF (psf->header) - 3) + { psf->header [psf->headindex++] = (x >> 16) ; + psf->header [psf->headindex++] = (x >> 8) ; + psf->header [psf->headindex++] = x ; + } ; +} /* header_put_be_3byte */ + +static inline void +header_put_le_3byte (SF_PRIVATE *psf, int x) +{ if (psf->headindex < SIGNED_SIZEOF (psf->header) - 3) + { psf->header [psf->headindex++] = x ; + psf->header [psf->headindex++] = (x >> 8) ; + psf->header [psf->headindex++] = (x >> 16) ; + } ; +} /* header_put_le_3byte */ + +static inline void +header_put_be_int (SF_PRIVATE *psf, int x) +{ if (psf->headindex < SIGNED_SIZEOF (psf->header) - 4) + { psf->header [psf->headindex++] = (x >> 24) ; + psf->header [psf->headindex++] = (x >> 16) ; + psf->header [psf->headindex++] = (x >> 8) ; + psf->header [psf->headindex++] = x ; + } ; +} /* header_put_be_int */ + +static inline void +header_put_le_int (SF_PRIVATE *psf, int x) +{ if (psf->headindex < SIGNED_SIZEOF (psf->header) - 4) + { psf->header [psf->headindex++] = x ; + psf->header [psf->headindex++] = (x >> 8) ; + psf->header [psf->headindex++] = (x >> 16) ; + psf->header [psf->headindex++] = (x >> 24) ; + } ; +} /* header_put_le_int */ + +#if (SIZEOF_SF_COUNT_T == 4) + +static inline void +header_put_be_8byte (SF_PRIVATE *psf, sf_count_t x) +{ if (psf->headindex < SIGNED_SIZEOF (psf->header) - 8) + { psf->header [psf->headindex++] = 0 ; + psf->header [psf->headindex++] = 0 ; + psf->header [psf->headindex++] = 0 ; + psf->header [psf->headindex++] = 0 ; + psf->header [psf->headindex++] = (x >> 24) ; + psf->header [psf->headindex++] = (x >> 16) ; + psf->header [psf->headindex++] = (x >> 8) ; + psf->header [psf->headindex++] = x ; + } ; +} /* header_put_be_8byte */ + +static inline void +header_put_le_8byte (SF_PRIVATE *psf, sf_count_t x) +{ if (psf->headindex < SIGNED_SIZEOF (psf->header) - 8) + { psf->header [psf->headindex++] = x ; + psf->header [psf->headindex++] = (x >> 8) ; + psf->header [psf->headindex++] = (x >> 16) ; + psf->header [psf->headindex++] = (x >> 24) ; + psf->header [psf->headindex++] = 0 ; + psf->header [psf->headindex++] = 0 ; + psf->header [psf->headindex++] = 0 ; + psf->header [psf->headindex++] = 0 ; + } ; +} /* header_put_le_8byte */ + +#elif (SIZEOF_SF_COUNT_T == 8) + +static inline void +header_put_be_8byte (SF_PRIVATE *psf, sf_count_t x) +{ if (psf->headindex < SIGNED_SIZEOF (psf->header) - 8) + { psf->header [psf->headindex++] = (x >> 56) ; + psf->header [psf->headindex++] = (x >> 48) ; + psf->header [psf->headindex++] = (x >> 40) ; + psf->header [psf->headindex++] = (x >> 32) ; + psf->header [psf->headindex++] = (x >> 24) ; + psf->header [psf->headindex++] = (x >> 16) ; + psf->header [psf->headindex++] = (x >> 8) ; + psf->header [psf->headindex++] = x ; + } ; +} /* header_put_be_8byte */ + +static inline void +header_put_le_8byte (SF_PRIVATE *psf, sf_count_t x) +{ if (psf->headindex < SIGNED_SIZEOF (psf->header) - 8) + { psf->header [psf->headindex++] = x ; + psf->header [psf->headindex++] = (x >> 8) ; + psf->header [psf->headindex++] = (x >> 16) ; + psf->header [psf->headindex++] = (x >> 24) ; + psf->header [psf->headindex++] = (x >> 32) ; + psf->header [psf->headindex++] = (x >> 40) ; + psf->header [psf->headindex++] = (x >> 48) ; + psf->header [psf->headindex++] = (x >> 56) ; + } ; +} /* header_put_le_8byte */ + +#else +#error "SIZEOF_SF_COUNT_T is not defined." +#endif + +int +psf_binheader_writef (SF_PRIVATE *psf, const char *format, ...) +{ va_list argptr ; + sf_count_t countdata ; + unsigned long longdata ; + unsigned int data ; + float floatdata ; + double doubledata ; + void *bindata ; + size_t size ; + char c, *strptr ; + int count = 0, trunc_8to4 ; + + trunc_8to4 = SF_FALSE ; + + va_start (argptr, format) ; + + while ((c = *format++)) + { switch (c) + { case ' ' : /* Do nothing. Just used to space out format string. */ + break ; + + case 'e' : /* All conversions are now from LE to host. */ + psf->rwf_endian = SF_ENDIAN_LITTLE ; + break ; + + case 'E' : /* All conversions are now from BE to host. */ + psf->rwf_endian = SF_ENDIAN_BIG ; + break ; + + case 't' : /* All 8 byte values now get written as 4 bytes. */ + trunc_8to4 = SF_TRUE ; + break ; + + case 'T' : /* All 8 byte values now get written as 8 bytes. */ + trunc_8to4 = SF_FALSE ; + break ; + + case 'm' : + data = va_arg (argptr, unsigned int) ; + header_put_marker (psf, data) ; + count += 4 ; + break ; + + case '1' : + data = va_arg (argptr, unsigned int) ; + header_put_byte (psf, data) ; + count += 1 ; + break ; + + case '2' : + data = va_arg (argptr, unsigned int) ; + if (psf->rwf_endian == SF_ENDIAN_BIG) + { header_put_be_short (psf, data) ; + } + else + { header_put_le_short (psf, data) ; + } ; + count += 2 ; + break ; + + case '3' : /* tribyte */ + data = va_arg (argptr, unsigned int) ; + if (psf->rwf_endian == SF_ENDIAN_BIG) + { header_put_be_3byte (psf, data) ; + } + else + { header_put_le_3byte (psf, data) ; + } ; + count += 3 ; + break ; + + case '4' : + data = va_arg (argptr, unsigned int) ; + if (psf->rwf_endian == SF_ENDIAN_BIG) + { header_put_be_int (psf, data) ; + } + else + { header_put_le_int (psf, data) ; + } ; + count += 4 ; + break ; + + case '8' : + countdata = va_arg (argptr, sf_count_t) ; + if (psf->rwf_endian == SF_ENDIAN_BIG && trunc_8to4 == SF_FALSE) + { header_put_be_8byte (psf, countdata) ; + count += 8 ; + } + else if (psf->rwf_endian == SF_ENDIAN_LITTLE && trunc_8to4 == SF_FALSE) + { header_put_le_8byte (psf, countdata) ; + count += 8 ; + } + else if (psf->rwf_endian == SF_ENDIAN_BIG && trunc_8to4 == SF_TRUE) + { longdata = countdata & 0xFFFFFFFF ; + header_put_be_int (psf, longdata) ; + count += 4 ; + } + else if (psf->rwf_endian == SF_ENDIAN_LITTLE && trunc_8to4 == SF_TRUE) + { longdata = countdata & 0xFFFFFFFF ; + header_put_le_int (psf, longdata) ; + count += 4 ; + } + break ; + + case 'f' : + /* Floats are passed as doubles. Is this always true? */ + floatdata = (float) va_arg (argptr, double) ; + if (psf->rwf_endian == SF_ENDIAN_BIG) + float32_be_write (floatdata, psf->header + psf->headindex) ; + else + float32_le_write (floatdata, psf->header + psf->headindex) ; + psf->headindex += 4 ; + count += 4 ; + break ; + + case 'd' : + doubledata = va_arg (argptr, double) ; + if (psf->rwf_endian == SF_ENDIAN_BIG) + double64_be_write (doubledata, psf->header + psf->headindex) ; + else + double64_le_write (doubledata, psf->header + psf->headindex) ; + psf->headindex += 8 ; + count += 8 ; + break ; + + case 's' : + /* Write a C string (guaranteed to have a zero terminator). */ + strptr = va_arg (argptr, char *) ; + size = strlen (strptr) + 1 ; + size += (size & 1) ; + if (psf->rwf_endian == SF_ENDIAN_BIG) + header_put_be_int (psf, size) ; + else + header_put_le_int (psf, size) ; + memcpy (&(psf->header [psf->headindex]), strptr, size) ; + psf->headindex += size ; + psf->header [psf->headindex - 1] = 0 ; + count += 4 + size ; + break ; + + case 'S' : + /* + ** Write an AIFF style string (no zero terminator but possibly + ** an extra pad byte if the string length is odd). + */ + strptr = va_arg (argptr, char *) ; + size = strlen (strptr) ; + if (psf->rwf_endian == SF_ENDIAN_BIG) + header_put_be_int (psf, size) ; + else + header_put_le_int (psf, size) ; + memcpy (&(psf->header [psf->headindex]), strptr, size + 1) ; + size += (size & 1) ; + psf->headindex += size ; + psf->header [psf->headindex] = 0 ; + count += 4 + size ; + break ; + + case 'b' : + bindata = va_arg (argptr, void *) ; + size = va_arg (argptr, size_t) ; + memcpy (&(psf->header [psf->headindex]), bindata, size) ; + psf->headindex += size ; + count += size ; + break ; + + case 'z' : + size = va_arg (argptr, size_t) ; + count += size ; + while (size) + { psf->header [psf->headindex] = 0 ; + psf->headindex ++ ; + size -- ; + } ; + break ; + + case 'h' : + bindata = va_arg (argptr, void *) ; + memcpy (&(psf->header [psf->headindex]), bindata, 16) ; + psf->headindex += 16 ; + count += 16 ; + break ; + + case 'j' : + size = va_arg (argptr, size_t) ; + psf->headindex += size ; + count = size ; + break ; + + default : + psf_log_printf (psf, "*** Invalid format specifier `%c'\n", c) ; + psf->error = SFE_INTERNAL ; + break ; + } ; + } ; + + va_end (argptr) ; + return count ; +} /* psf_binheader_writef */ + +/*----------------------------------------------------------------------------------------------- +** Binary header reading functions. Returns number of bytes read. +** +** Format specifiers are the same as for header write function above with the following +** additions: +** +** p - jump a given number of position from start of file. +** +** If format is NULL, psf_binheader_readf returns the current offset. +*/ + +#if (CPU_IS_BIG_ENDIAN == 1) +#define GET_MARKER(ptr) ( ((ptr) [0] << 24) | ((ptr) [1] << 16) | \ + ((ptr) [2] << 8) | ((ptr) [3]) ) + +#elif (CPU_IS_LITTLE_ENDIAN == 1) +#define GET_MARKER(ptr) ( ((ptr) [0]) | ((ptr) [1] << 8) | \ + ((ptr) [2] << 16) | ((ptr) [3] << 24) ) + +#else +# error "Cannot determine endian-ness of processor." +#endif + +#define GET_LE_SHORT(ptr) ( ((ptr) [1] << 8) | ((ptr) [0]) ) +#define GET_BE_SHORT(ptr) ( ((ptr) [0] << 8) | ((ptr) [1]) ) + +#define GET_LE_3BYTE(ptr) ( ((ptr) [2] << 16) | ((ptr) [1] << 8) | ((ptr) [0]) ) +#define GET_BE_3BYTE(ptr) ( ((ptr) [0] << 16) | ((ptr) [1] << 8) | ((ptr) [2]) ) + +#define GET_LE_INT(ptr) ( ((ptr) [3] << 24) | ((ptr) [2] << 16) | \ + ((ptr) [1] << 8) | ((ptr) [0]) ) + +#define GET_BE_INT(ptr) ( ((ptr) [0] << 24) | ((ptr) [1] << 16) | \ + ((ptr) [2] << 8) | ((ptr) [3]) ) + +#define GET_LE_8BYTE(ptr) ( (((sf_count_t) (ptr) [7]) << 56) | (((sf_count_t) (ptr) [6]) << 48) | \ + (((sf_count_t) (ptr) [5]) << 40) | (((sf_count_t) (ptr) [4]) << 32) | \ + (((sf_count_t) (ptr) [3]) << 24) | (((sf_count_t) (ptr) [2]) << 16) | \ + (((sf_count_t) (ptr) [1]) << 8 ) | ((ptr) [0])) + +#define GET_BE_8BYTE(ptr) ( (((sf_count_t) (ptr) [0]) << 56) | (((sf_count_t) (ptr) [1]) << 48) | \ + (((sf_count_t) (ptr) [2]) << 40) | (((sf_count_t) (ptr) [3]) << 32) | \ + (((sf_count_t) (ptr) [4]) << 24) | (((sf_count_t) (ptr) [5]) << 16) | \ + (((sf_count_t) (ptr) [6]) << 8 ) | ((ptr) [7])) + + + +static int +header_read (SF_PRIVATE *psf, void *ptr, int bytes) +{ int count = 0 ; + + if (psf->headindex >= SIGNED_SIZEOF (psf->header)) + { memset (ptr, 0, SIGNED_SIZEOF (psf->header) - psf->headindex) ; + + /* This is the best that we can do. */ + psf_fseek (psf, bytes, SEEK_CUR) ; + return bytes ; + } ; + + if (psf->headindex + bytes > SIGNED_SIZEOF (psf->header)) + { int most ; + + most = SIGNED_SIZEOF (psf->header) - psf->headindex ; + psf_fread (psf->header + psf->headend, 1, most, psf) ; + memset ((char *) ptr + most, 0, bytes - most) ; + + psf_fseek (psf, bytes - most, SEEK_CUR) ; + return bytes ; + } ; + + if (psf->headindex + bytes > psf->headend) + { count = psf_fread (psf->header + psf->headend, 1, bytes - (psf->headend - psf->headindex), psf) ; + if (count != bytes - (int) (psf->headend - psf->headindex)) + { psf_log_printf (psf, "Error : psf_fread returned short count.\n") ; + return 0 ; + } ; + psf->headend += count ; + } ; + + memcpy (ptr, psf->header + psf->headindex, bytes) ; + psf->headindex += bytes ; + + return bytes ; +} /* header_read */ + +static void +header_seek (SF_PRIVATE *psf, sf_count_t position, int whence) +{ + + switch (whence) + { case SEEK_SET : + if (position > SIGNED_SIZEOF (psf->header)) + { /* Too much header to cache so just seek instead. */ + psf_fseek (psf, position, whence) ; + return ; + } ; + if (position > psf->headend) + psf->headend += psf_fread (psf->header + psf->headend, 1, position - psf->headend, psf) ; + psf->headindex = position ; + break ; + + case SEEK_CUR : + if (psf->headindex + position < 0) + break ; + + if (psf->headindex >= SIGNED_SIZEOF (psf->header)) + { psf_fseek (psf, position, whence) ; + return ; + } ; + + if (psf->headindex + position <= psf->headend) + { psf->headindex += position ; + break ; + } ; + + if (psf->headindex + position > SIGNED_SIZEOF (psf->header)) + { /* Need to jump this without caching it. */ + psf->headindex = psf->headend ; + psf_fseek (psf, position, SEEK_CUR) ; + break ; + } ; + + psf->headend += psf_fread (psf->header + psf->headend, 1, position - (psf->headend - psf->headindex), psf) ; + psf->headindex = psf->headend ; + break ; + + case SEEK_END : + default : + psf_log_printf (psf, "Bad whence param in header_seek().\n") ; + break ; + } ; + + return ; +} /* header_seek */ + +static int +header_gets (SF_PRIVATE *psf, char *ptr, int bufsize) +{ + int k ; + + for (k = 0 ; k < bufsize - 1 ; k++) + { if (psf->headindex < psf->headend) + { ptr [k] = psf->header [psf->headindex] ; + psf->headindex ++ ; + } + else + { psf->headend += psf_fread (psf->header + psf->headend, 1, 1, psf) ; + ptr [k] = psf->header [psf->headindex] ; + psf->headindex = psf->headend ; + } ; + + if (ptr [k] == '\n') + break ; + } ; + + ptr [k] = 0 ; + + return k ; +} /* header_gets */ + +int +psf_binheader_readf (SF_PRIVATE *psf, char const *format, ...) +{ va_list argptr ; + sf_count_t *countptr, countdata ; + unsigned char *ucptr, sixteen_bytes [16] ; + unsigned int *intptr, intdata ; + unsigned short *shortptr ; + char *charptr ; + float *floatptr ; + double *doubleptr ; + char c ; + int byte_count = 0, count ; + + if (! format) + return psf_ftell (psf) ; + + va_start (argptr, format) ; + + while ((c = *format++)) + { switch (c) + { case 'e' : /* All conversions are now from LE to host. */ + psf->rwf_endian = SF_ENDIAN_LITTLE ; + break ; + + case 'E' : /* All conversions are now from BE to host. */ + psf->rwf_endian = SF_ENDIAN_BIG ; + break ; + + case 'm' : + intptr = va_arg (argptr, unsigned int*) ; + ucptr = (unsigned char*) intptr ; + byte_count += header_read (psf, ucptr, sizeof (int)) ; + *intptr = GET_MARKER (ucptr) ; + break ; + + case 'h' : + intptr = va_arg (argptr, unsigned int*) ; + ucptr = (unsigned char*) intptr ; + byte_count += header_read (psf, sixteen_bytes, sizeof (sixteen_bytes)) ; + { int k ; + intdata = 0 ; + for (k = 0 ; k < 16 ; k++) + intdata ^= sixteen_bytes [k] << k ; + } + *intptr = intdata ; + break ; + + case '1' : + charptr = va_arg (argptr, char*) ; + *charptr = 0 ; + byte_count += header_read (psf, charptr, sizeof (char)) ; + break ; + + case '2' : + shortptr = va_arg (argptr, unsigned short*) ; + *shortptr = 0 ; + ucptr = (unsigned char*) shortptr ; + byte_count += header_read (psf, ucptr, sizeof (short)) ; + if (psf->rwf_endian == SF_ENDIAN_BIG) + *shortptr = GET_BE_SHORT (ucptr) ; + else + *shortptr = GET_LE_SHORT (ucptr) ; + break ; + + case '3' : + intptr = va_arg (argptr, unsigned int*) ; + *intptr = 0 ; + byte_count += header_read (psf, sixteen_bytes, 3) ; + if (psf->rwf_endian == SF_ENDIAN_BIG) + *intptr = GET_BE_3BYTE (sixteen_bytes) ; + else + *intptr = GET_LE_3BYTE (sixteen_bytes) ; + break ; + + case '4' : + intptr = va_arg (argptr, unsigned int*) ; + *intptr = 0 ; + ucptr = (unsigned char*) intptr ; + byte_count += header_read (psf, ucptr, sizeof (int)) ; + if (psf->rwf_endian == SF_ENDIAN_BIG) + *intptr = GET_BE_INT (ucptr) ; + else + *intptr = GET_LE_INT (ucptr) ; + break ; + + case '8' : + countptr = va_arg (argptr, sf_count_t *) ; + *countptr = 0 ; + byte_count += header_read (psf, sixteen_bytes, 8) ; + if (psf->rwf_endian == SF_ENDIAN_BIG) + countdata = GET_BE_8BYTE (sixteen_bytes) ; + else + countdata = GET_LE_8BYTE (sixteen_bytes) ; + *countptr = countdata ; + break ; + + case 'f' : /* Float conversion */ + floatptr = va_arg (argptr, float *) ; + *floatptr = 0.0 ; + byte_count += header_read (psf, floatptr, sizeof (float)) ; + if (psf->rwf_endian == SF_ENDIAN_BIG) + *floatptr = float32_be_read ((unsigned char*) floatptr) ; + else + *floatptr = float32_le_read ((unsigned char*) floatptr) ; + break ; + + case 'd' : /* double conversion */ + doubleptr = va_arg (argptr, double *) ; + *doubleptr = 0.0 ; + byte_count += header_read (psf, doubleptr, sizeof (double)) ; + if (psf->rwf_endian == SF_ENDIAN_BIG) + *doubleptr = double64_be_read ((unsigned char*) doubleptr) ; + else + *doubleptr = double64_le_read ((unsigned char*) doubleptr) ; + break ; + + case 's' : + psf_log_printf (psf, "Format conversion 's' not implemented yet.\n") ; + /* + strptr = va_arg (argptr, char *) ; + size = strlen (strptr) + 1 ; + size += (size & 1) ; + longdata = H2LE_INT (size) ; + get_int (psf, longdata) ; + memcpy (&(psf->header [psf->headindex]), strptr, size) ; + psf->headindex += size ; + */ + break ; + + case 'b' : + charptr = va_arg (argptr, char*) ; + count = va_arg (argptr, int) ; + if (count > 0) + byte_count += header_read (psf, charptr, count) ; + break ; + + case 'G' : + charptr = va_arg (argptr, char*) ; + count = va_arg (argptr, int) ; + if (count > 0) + byte_count += header_gets (psf, charptr, count) ; + break ; + + case 'z' : + psf_log_printf (psf, "Format conversion 'z' not implemented yet.\n") ; + /* + size = va_arg (argptr, size_t) ; + while (size) + { psf->header [psf->headindex] = 0 ; + psf->headindex ++ ; + size -- ; + } ; + */ + break ; + + case 'p' : + /* Get the seek position first. */ + count = va_arg (argptr, int) ; + header_seek (psf, count, SEEK_SET) ; + byte_count = count ; + break ; + + case 'j' : + /* Get the seek position first. */ + count = va_arg (argptr, int) ; + header_seek (psf, count, SEEK_CUR) ; + byte_count += count ; + break ; + + default : + psf_log_printf (psf, "*** Invalid format specifier `%c'\n", c) ; + psf->error = SFE_INTERNAL ; + break ; + } ; + } ; + + va_end (argptr) ; + + return byte_count ; +} /* psf_binheader_readf */ + +/*----------------------------------------------------------------------------------------------- +*/ + +sf_count_t +psf_default_seek (SF_PRIVATE *psf, int UNUSED (mode), sf_count_t samples_from_start) +{ sf_count_t position, retval ; + + if (! (psf->blockwidth && psf->dataoffset >= 0)) + { psf->error = SFE_BAD_SEEK ; + return PSF_SEEK_ERROR ; + } ; + + if (! psf->sf.seekable) + { psf->error = SFE_NOT_SEEKABLE ; + return PSF_SEEK_ERROR ; + } ; + + position = psf->dataoffset + psf->blockwidth * samples_from_start ; + + if ((retval = psf_fseek (psf, position, SEEK_SET)) != position) + { psf->error = SFE_SEEK_FAILED ; + return PSF_SEEK_ERROR ; + } ; + + return samples_from_start ; +} /* psf_default_seek */ + +/*----------------------------------------------------------------------------------------------- +*/ + +void +psf_hexdump (const void *ptr, int len) +{ const char *data ; + char ascii [17] ; + int k, m ; + + if ((data = ptr) == NULL) + return ; + if (len <= 0) + return ; + + puts ("") ; + for (k = 0 ; k < len ; k += 16) + { memset (ascii, ' ', sizeof (ascii)) ; + + printf ("%08X: ", k) ; + for (m = 0 ; m < 16 && k + m < len ; m++) + { printf (m == 8 ? " %02X " : "%02X ", data [k + m] & 0xFF) ; + ascii [m] = isprint (data [k + m]) ? data [k + m] : '.' ; + } ; + + if (m <= 8) printf (" ") ; + for ( ; m < 16 ; m++) printf (" ") ; + + ascii [16] = 0 ; + printf (" %s\n", ascii) ; + } ; + + puts ("") ; +} /* psf_hexdump */ + +void +psf_log_SF_INFO (SF_PRIVATE *psf) +{ psf_log_printf (psf, "---------------------------------\n") ; + + psf_log_printf (psf, " Sample rate : %d\n", psf->sf.samplerate) ; + psf_log_printf (psf, " Frames : %D\n", psf->sf.frames) ; + psf_log_printf (psf, " Channels : %d\n", psf->sf.channels) ; + + psf_log_printf (psf, " Format : 0x%X\n", psf->sf.format) ; + psf_log_printf (psf, " Sections : %d\n", psf->sf.sections) ; + psf_log_printf (psf, " Seekable : %s\n", psf->sf.seekable ? "TRUE" : "FALSE") ; + + psf_log_printf (psf, "---------------------------------\n") ; +} /* psf_dump_SFINFO */ + +/*======================================================================================== +*/ + +void* +psf_memset (void *s, int c, sf_count_t len) +{ char *ptr ; + int setcount ; + + ptr = (char *) s ; + + while (len > 0) + { setcount = (len > 0x10000000) ? 0x10000000 : (int) len ; + + memset (ptr, c, setcount) ; + + ptr += setcount ; + len -= setcount ; + } ; + + return s ; +} /* psf_memset */ + +SF_INSTRUMENT * +psf_instrument_alloc (void) +{ SF_INSTRUMENT *instr ; + + instr = calloc (1, sizeof (SF_INSTRUMENT)) ; + + if (instr == NULL) + return NULL ; + + /* Set non-zero default values. */ + instr->basenote = -1 ; + instr->velocity_lo = -1 ; + instr->velocity_hi = -1 ; + instr->key_lo = -1 ; + instr->key_hi = -1 ; + + return instr ; +} /* psf_instrument_alloc */ + +void +psf_sanitize_string (char * cptr, int len) +{ + do + { + len -- ; + cptr [len] = isprint (cptr [len]) ? cptr [len] : '.' ; + } + while (len > 0) ; +} /* psf_sanitize_string */ + +void +psf_get_date_str (char *str, int maxlen) +{ time_t current ; + struct tm timedata, *tmptr ; + + time (¤t) ; + +#if defined (HAVE_GMTIME_R) + /* If the re-entrant version is available, use it. */ + tmptr = gmtime_r (¤t, &timedata) ; +#elif defined (HAVE_GMTIME) + /* Otherwise use the standard one and copy the data to local storage. */ + tmptr = gmtime (¤t) ; + memcpy (&timedata, tmptr, sizeof (timedata)) ; +#else + tmptr = NULL ; +#endif + + if (tmptr) + LSF_SNPRINTF (str, maxlen, "%4d-%02d-%02d %02d:%02d:%02d UTC", + 1900 + timedata.tm_year, timedata.tm_mon, timedata.tm_mday, + timedata.tm_hour, timedata.tm_min, timedata.tm_sec) ; + else + LSF_SNPRINTF (str, maxlen, "Unknown date") ; + + return ; +} /* psf_get_date_str */ + +int +subformat_to_bytewidth (int format) +{ + switch (format) + { case SF_FORMAT_PCM_U8 : + case SF_FORMAT_PCM_S8 : + return 1 ; + case SF_FORMAT_PCM_16 : + return 2 ; + case SF_FORMAT_PCM_24 : + return 3 ; + case SF_FORMAT_PCM_32 : + case SF_FORMAT_FLOAT : + return 4 ; + case SF_FORMAT_DOUBLE : + return 8 ; + } ; + + return 0 ; +} /* subformat_to_bytewidth */ + +int +s_bitwidth_to_subformat (int bits) +{ static int array [] = + { SF_FORMAT_PCM_S8, SF_FORMAT_PCM_16, SF_FORMAT_PCM_24, SF_FORMAT_PCM_32 + } ; + + if (bits < 8 || bits > 32) + return 0 ; + + return array [((bits + 7) / 8) - 1] ; +} /* bitwidth_to_subformat */ + +int +u_bitwidth_to_subformat (int bits) +{ static int array [] = + { SF_FORMAT_PCM_U8, SF_FORMAT_PCM_16, SF_FORMAT_PCM_24, SF_FORMAT_PCM_32 + } ; + + if (bits < 8 || bits > 32) + return 0 ; + + return array [((bits + 7) / 8) - 1] ; +} /* bitwidth_to_subformat */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 33e9795e-f717-461a-9feb-65d083a56395 +*/ diff --git a/src/common.h b/src/common.h new file mode 100644 index 00000000..5ba2cc67 --- /dev/null +++ b/src/common.h @@ -0,0 +1,813 @@ +/* +** Copyright (C) 1999-2006 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef SNDFILE_COMMON_H +#define SNDFILE_COMMON_H + +#include "sfconfig.h" + +#include + +#if HAVE_STDINT_H +#include +#endif + +#ifndef SNDFILE_H +#include "sndfile.h" +#elif HAVE_INTTYPES_H +#include +#endif + +#ifdef __cplusplus +#error "This code is not designed to be compiled with a C++ compiler." +#endif + +#if (SIZEOF_LONG == 8) +# define SF_PLATFORM_S64(x) x##l +#elif COMPILER_IS_GCC +# define SF_PLATFORM_S64(x) x##ll +#elif OS_IS_WIN32 +# define SF_PLATFORM_S64(x) x##I64 +#else +# error "Don't know how to define a 64 bit integer constant." +#endif + + + +#ifdef UNUSED +#elif defined (__GNUC__) +# define UNUSED(x) UNUSED_ ## x __attribute__ ((unused)) +#elif defined (__LCLINT__) +# define UNUSED(x) /*@unused@*/ x +#else +# define UNUSED(x) x +#endif + +#ifdef __GNUC__ +# define WARN_UNUSED __attribute__ ((warn_unused_result)) +#else +# define WARN_UNUSED +#endif + +#define SF_BUFFER_LEN (8192*2) +#define SF_FILENAME_LEN (512) +#define SF_SYSERR_LEN (256) +#define SF_MAX_STRINGS (16) +#define SF_STR_BUFFER_LEN (8192) +#define SF_HEADER_LEN (4100 + SF_STR_BUFFER_LEN) + +#define PSF_SEEK_ERROR ((sf_count_t) -1) + + +#define BITWIDTH2BYTES(x) (((x) + 7) / 8) + +/* For some reason sizeof returns an unsigned value which causes +** a warning when that value is added or subtracted from a signed +** value. Use SIGNED_SIZEOF instead. +*/ +#define SIGNED_SIZEOF(x) ((int) sizeof (x)) + +#define ARRAY_LEN(x) ((int) (sizeof (x) / sizeof ((x) [0]))) + +#if (COMPILER_IS_GCC == 1) +#define SF_MAX(x,y) ({ \ + typeof(x) _x = (x); \ + typeof(y) _y = (y); \ + (void) (&_x == &_y); \ + _x > _y ? _x : _y; }) + +#define SF_MIN(x,y) ({ \ + typeof(x) _x = (x); \ + typeof(y) _y = (y); \ + (void) (&_x == &_y); \ + _x < _y ? _x : _y; }) +#else +#define SF_MAX(a,b) ((a) > (b) ? (a) : (b)) +#define SF_MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +enum +{ /* PEAK chunk location. */ + SF_PEAK_START = 42, + SF_PEAK_END = 43, + + /* PEAK chunk location. */ + SF_SCALE_MAX = 52, + SF_SCALE_MIN = 53, + + /* str_flags values. */ + SF_STR_ALLOW_START = 0x0100, + SF_STR_ALLOW_END = 0x0200, + + /* Location of strings. */ + SF_STR_LOCATE_START = 0x0400, + SF_STR_LOCATE_END = 0x0800, + + SFD_TYPEMASK = 0x0FFFFFFF +} ; + +#define SFM_MASK (SFM_READ | SFM_WRITE | SFM_RDWR) +#define SFM_UNMASK (~SFM_MASK) + +/*--------------------------------------------------------------------------------------- +** Formats that may be supported at some time in the future. +** When support is finalised, these values move to src/sndfile.h. +*/ + +enum +{ /* Work in progress. */ + + /* Formats supported read only. */ + SF_FORMAT_TXW = 0x4030000, /* Yamaha TX16 sampler file */ + SF_FORMAT_DWD = 0x4040000, /* DiamondWare Digirized */ + + /* Following are detected but not supported. */ + SF_FORMAT_OGG = 0x4090000, + + SF_FORMAT_REX = 0x40A0000, /* Propellorheads Rex/Rcy */ + SF_FORMAT_REX2 = 0x40D0000, /* Propellorheads Rex2 */ + SF_FORMAT_KRZ = 0x40E0000, /* Kurzweil sampler file */ + SF_FORMAT_WMA = 0x4100000, /* Windows Media Audio. */ + SF_FORMAT_SHN = 0x4110000, /* Shorten. */ + + /* Unsupported encodings. */ + SF_FORMAT_VORBIS = 0x1001, + + SF_FORMAT_SVX_FIB = 0x1020, /* SVX Fibonacci Delta encoding. */ + SF_FORMAT_SVX_EXP = 0x1021, /* SVX Exponential Delta encoding. */ + + SF_FORMAT_PCM_N = 0x1030 +} ; + +/*--------------------------------------------------------------------------------------- +** PEAK_CHUNK - This chunk type is common to both AIFF and WAVE files although their +** endian encodings are different. +*/ + +typedef struct +{ double value ; /* signed value of peak */ + sf_count_t position ; /* the sample frame for the peak */ +} PEAK_POS ; + +typedef struct +{ /* libsndfile internal : write a PEAK chunk at the start or end of the file? */ + int peak_loc ; + + /* WAV/AIFF */ + unsigned int version ; /* version of the PEAK chunk */ + unsigned int timestamp ; /* secs since 1/1/1970 */ + + /* CAF */ + unsigned int edit_number ; + +#if HAVE_FLEXIBLE_ARRAY + /* the per channel peak info */ + PEAK_POS peaks [] ; +#else + /* + ** This is not ISO compliant C. It works on some compilers which + ** don't support the ISO standard flexible struct array which is + ** used above. If your compiler doesn't like this I suggest you find + ** youself a 1999 ISO C standards compilant compiler. GCC-3.X is + ** highly recommended. + */ + PEAK_POS peaks [0] ; +#endif +} PEAK_INFO ; + +static inline PEAK_INFO * +peak_info_calloc (int channels) +{ return calloc (1, sizeof (PEAK_INFO) + channels * sizeof (PEAK_POS)) ; +} /* peak_info_calloc */ + +typedef struct +{ int type ; + int flags ; + char *str ; +} STR_DATA ; + +static inline size_t +make_size_t (int x) +{ return (size_t) x ; +} /* size_t_of_int */ + +/*======================================================================================= +** SF_PRIVATE stuct - a pointer to this struct is passed back to the caller of the +** sf_open_XXXX functions. The caller however has no knowledge of the struct's +** contents. +*/ + +typedef struct sf_private_tag +{ /* Force the compiler to double align the start of buffer. */ + union + { double dbuf [SF_BUFFER_LEN / sizeof (double)] ; +#if (defined (SIZEOF_INT64_T) && (SIZEOF_INT64_T == 8)) + int64_t lbuf [SF_BUFFER_LEN / sizeof (int64_t)] ; +#else + long lbuf [SF_BUFFER_LEN / sizeof (double)] ; +#endif + float fbuf [SF_BUFFER_LEN / sizeof (float)] ; + int ibuf [SF_BUFFER_LEN / sizeof (int)] ; + short sbuf [SF_BUFFER_LEN / sizeof (short)] ; + char cbuf [SF_BUFFER_LEN / sizeof (char)] ; + signed char scbuf [SF_BUFFER_LEN / sizeof (signed char)] ; + unsigned char ucbuf [SF_BUFFER_LEN / sizeof (signed char)] ; + } u ; + + char filepath [SF_FILENAME_LEN] ; + char rsrcpath [SF_FILENAME_LEN] ; + char directory [SF_FILENAME_LEN] ; + char filename [SF_FILENAME_LEN / 4] ; + + char syserr [SF_SYSERR_LEN] ; + + /* logbuffer and logindex should only be changed within the logging functions + ** of common.c + */ + char logbuffer [SF_BUFFER_LEN] ; + unsigned char header [SF_HEADER_LEN] ; /* Must be unsigned */ + int rwf_endian ; /* Header endian-ness flag. */ + + /* Storage and housekeeping data for adding/reading strings from + ** sound files. + */ + STR_DATA strings [SF_MAX_STRINGS] ; + char str_storage [SF_STR_BUFFER_LEN] ; + char *str_end ; + int str_flags ; + + /* Guard value. If this changes the buffers above have overflowed. */ + int Magick ; + + /* Index variables for maintaining logbuffer and header above. */ + int logindex ; + int headindex, headend ; + int has_text ; + int do_not_close_descriptor ; + +#if USE_WINDOWS_API + /* + ** These fields can only be used in src/file_io.c. + ** They are basically the same as a windows file HANDLE. + */ + void *hfile, *hrsrc, *hsaved ; +#else + /* These fields can only be used in src/file_io.c. */ + int filedes, rsrcdes, savedes ; +#endif + + int error ; + + int mode ; /* Open mode : SFM_READ, SFM_WRITE or SFM_RDWR. */ + int endian ; /* File endianness : SF_ENDIAN_LITTLE or SF_ENDIAN_BIG. */ + int float_endswap ; /* Need to endswap float32s? */ + + /* + ** Maximum float value for calculating the multiplier for + ** float/double to short/int conversions. + */ + int float_int_mult ; + float float_max ; + + /* Vairables for handling pipes. */ + int is_pipe ; /* True if file is a pipe. */ + sf_count_t pipeoffset ; /* Number of bytes read from a pipe. */ + + /* True if clipping must be performed on float->int conversions. */ + int add_clipping ; + + SF_INFO sf ; + + int have_written ; /* Has a single write been done to the file? */ + PEAK_INFO *peak_info ; + + /* Loop Info */ + SF_LOOP_INFO *loop_info ; + SF_INSTRUMENT *instrument ; + + /* Broadcast (EBU) Info */ + SF_BROADCAST_INFO *broadcast_info ; + + /* Channel map data (if present) : an array of ints. */ + int *channel_map ; + + sf_count_t filelength ; /* Overall length of (embedded) file. */ + sf_count_t fileoffset ; /* Offset in number of bytes from beginning of file. */ + + sf_count_t rsrclength ; /* Length of the resource fork (if it exists). */ + + sf_count_t dataoffset ; /* Offset in number of bytes from beginning of file. */ + sf_count_t datalength ; /* Length in bytes of the audio data. */ + sf_count_t dataend ; /* Offset to file tailer. */ + + int blockwidth ; /* Size in bytes of one set of interleaved samples. */ + int bytewidth ; /* Size in bytes of one sample (one channel). */ + + void *dither ; + void *interleave ; + + int last_op ; /* Last operation; either SFM_READ or SFM_WRITE */ + sf_count_t read_current ; + sf_count_t write_current ; + + void *container_data ; /* This is a pointer to dynamically allocated file + ** container format specific data. + */ + + void *codec_data ; /* This is a pointer to dynamically allocated file + ** codec format specific data. + */ + + SF_DITHER_INFO write_dither ; + SF_DITHER_INFO read_dither ; + + int norm_double ; + int norm_float ; + + int auto_header ; + + int ieee_replace ; + + /* For ambisonic commands */ + int wavex_ambisonic ; + + /* A set of file specific function pointers */ + sf_count_t (*read_short) (struct sf_private_tag*, short *ptr, sf_count_t len) ; + sf_count_t (*read_int) (struct sf_private_tag*, int *ptr, sf_count_t len) ; + sf_count_t (*read_float) (struct sf_private_tag*, float *ptr, sf_count_t len) ; + sf_count_t (*read_double) (struct sf_private_tag*, double *ptr, sf_count_t len) ; + + sf_count_t (*write_short) (struct sf_private_tag*, const short *ptr, sf_count_t len) ; + sf_count_t (*write_int) (struct sf_private_tag*, const int *ptr, sf_count_t len) ; + sf_count_t (*write_float) (struct sf_private_tag*, const float *ptr, sf_count_t len) ; + sf_count_t (*write_double) (struct sf_private_tag*, const double *ptr, sf_count_t len) ; + + sf_count_t (*seek) (struct sf_private_tag*, int mode, sf_count_t samples_from_start) ; + int (*write_header) (struct sf_private_tag*, int calc_length) ; + int (*command) (struct sf_private_tag*, int command, void *data, int datasize) ; + + /* + ** Separate close functions for the codec and the container. + ** The codec close function is always called first. + */ + int (*codec_close) (struct sf_private_tag*) ; + int (*container_close) (struct sf_private_tag*) ; + + char *format_desc ; + + /* Virtual I/O functions. */ + int virtual_io ; + SF_VIRTUAL_IO vio ; + void *vio_user_data ; +} SF_PRIVATE ; + + + +enum +{ SFE_NO_ERROR = SF_ERR_NO_ERROR, + SFE_BAD_OPEN_FORMAT = SF_ERR_UNRECOGNISED_FORMAT, + SFE_SYSTEM = SF_ERR_SYSTEM, + SFE_MALFORMED_FILE = SF_ERR_MALFORMED_FILE, + SFE_UNSUPPORTED_ENCODING = SF_ERR_UNSUPPORTED_ENCODING, + + SFE_BAD_FILE, + SFE_BAD_FILE_READ, + SFE_OPEN_FAILED, + SFE_BAD_SNDFILE_PTR, + SFE_BAD_SF_INFO_PTR, + SFE_BAD_SF_INCOMPLETE, + SFE_BAD_FILE_PTR, + SFE_BAD_INT_PTR, + SFE_BAD_STAT_SIZE, + SFE_MALLOC_FAILED, + SFE_UNIMPLEMENTED, + SFE_BAD_READ_ALIGN, + SFE_BAD_WRITE_ALIGN, + SFE_UNKNOWN_FORMAT, + SFE_NOT_READMODE, + SFE_NOT_WRITEMODE, + SFE_BAD_MODE_RW, + SFE_BAD_SF_INFO, + SFE_BAD_OFFSET, + SFE_NO_EMBED_SUPPORT, + SFE_NO_EMBEDDED_RDWR, + SFE_NO_PIPE_WRITE, + + SFE_INTERNAL, + SFE_BAD_CONTROL_CMD, + SFE_BAD_ENDIAN, + SFE_CHANNEL_COUNT, + SFE_BAD_RDWR_FORMAT, + + SFE_BAD_VIRTUAL_IO, + + SFE_INTERLEAVE_MODE, + SFE_INTERLEAVE_SEEK, + SFE_INTERLEAVE_READ, + + SFE_BAD_SEEK, + SFE_NOT_SEEKABLE, + SFE_AMBIGUOUS_SEEK, + SFE_WRONG_SEEK, + SFE_SEEK_FAILED, + + SFE_BAD_OPEN_MODE, + SFE_OPEN_PIPE_RDWR, + SFE_RDWR_POSITION, + SFE_RDWR_BAD_HEADER, + + SFE_STR_NO_SUPPORT, + SFE_STR_NOT_WRITE, + SFE_STR_MAX_DATA, + SFE_STR_MAX_COUNT, + SFE_STR_BAD_TYPE, + SFE_STR_NO_ADD_END, + SFE_STR_BAD_STRING, + SFE_STR_WEIRD, + + SFE_WAV_NO_RIFF, + SFE_WAV_NO_WAVE, + SFE_WAV_NO_FMT, + SFE_WAV_FMT_SHORT, + SFE_WAV_BAD_FACT, + SFE_WAV_BAD_PEAK, + SFE_WAV_PEAK_B4_FMT, + SFE_WAV_BAD_FORMAT, + SFE_WAV_BAD_BLOCKALIGN, + SFE_WAV_NO_DATA, + SFE_WAV_BAD_LIST, + SFE_WAV_ADPCM_NOT4BIT, + SFE_WAV_ADPCM_CHANNELS, + SFE_WAV_GSM610_FORMAT, + SFE_WAV_UNKNOWN_CHUNK, + SFE_WAV_WVPK_DATA, + + SFE_AIFF_NO_FORM, + SFE_AIFF_AIFF_NO_FORM, + SFE_AIFF_COMM_NO_FORM, + SFE_AIFF_SSND_NO_COMM, + SFE_AIFF_UNKNOWN_CHUNK, + SFE_AIFF_COMM_CHUNK_SIZE, + SFE_AIFF_BAD_COMM_CHUNK, + SFE_AIFF_PEAK_B4_COMM, + SFE_AIFF_BAD_PEAK, + SFE_AIFF_NO_SSND, + SFE_AIFF_NO_DATA, + SFE_AIFF_RW_SSND_NOT_LAST, + + SFE_AU_UNKNOWN_FORMAT, + SFE_AU_NO_DOTSND, + SFE_AU_EMBED_BAD_LEN, + + SFE_RAW_READ_BAD_SPEC, + SFE_RAW_BAD_BITWIDTH, + SFE_RAW_BAD_FORMAT, + + SFE_PAF_NO_MARKER, + SFE_PAF_VERSION, + SFE_PAF_UNKNOWN_FORMAT, + SFE_PAF_SHORT_HEADER, + + SFE_SVX_NO_FORM, + SFE_SVX_NO_BODY, + SFE_SVX_NO_DATA, + SFE_SVX_BAD_COMP, + SFE_SVX_BAD_NAME_LENGTH, + + SFE_NIST_BAD_HEADER, + SFE_NIST_CRLF_CONVERISON, + SFE_NIST_BAD_ENCODING, + + SFE_VOC_NO_CREATIVE, + SFE_VOC_BAD_FORMAT, + SFE_VOC_BAD_VERSION, + SFE_VOC_BAD_MARKER, + SFE_VOC_BAD_SECTIONS, + SFE_VOC_MULTI_SAMPLERATE, + SFE_VOC_MULTI_SECTION, + SFE_VOC_MULTI_PARAM, + SFE_VOC_SECTION_COUNT, + SFE_VOC_NO_PIPE, + + SFE_IRCAM_NO_MARKER, + SFE_IRCAM_BAD_CHANNELS, + SFE_IRCAM_UNKNOWN_FORMAT, + + SFE_W64_64_BIT, + SFE_W64_NO_RIFF, + SFE_W64_NO_WAVE, + SFE_W64_NO_FMT, + SFE_W64_NO_DATA, + SFE_W64_FMT_SHORT, + SFE_W64_FMT_TOO_BIG, + SFE_W64_ADPCM_NOT4BIT, + SFE_W64_ADPCM_CHANNELS, + SFE_W64_GSM610_FORMAT, + + SFE_MAT4_BAD_NAME, + SFE_MAT4_NO_SAMPLERATE, + SFE_MAT4_ZERO_CHANNELS, + + SFE_MAT5_BAD_ENDIAN, + SFE_MAT5_NO_BLOCK, + SFE_MAT5_SAMPLE_RATE, + SFE_MAT5_ZERO_CHANNELS, + + SFE_PVF_NO_PVF1, + SFE_PVF_BAD_HEADER, + SFE_PVF_BAD_BITWIDTH, + + SFE_DWVW_BAD_BITWIDTH, + SFE_G72X_NOT_MONO, + + SFE_XI_BAD_HEADER, + SFE_XI_EXCESS_SAMPLES, + SFE_XI_NO_PIPE, + + SFE_HTK_NO_PIPE, + + SFE_SDS_NOT_SDS, + SFE_SDS_BAD_BIT_WIDTH, + + SFE_SD2_FD_DISALLOWED, + SFE_SD2_BAD_DATA_OFFSET, + SFE_SD2_BAD_MAP_OFFSET, + SFE_SD2_BAD_DATA_LENGTH, + SFE_SD2_BAD_MAP_LENGTH, + SFE_SD2_BAD_RSRC, + SFE_SD2_BAD_SAMPLE_SIZE, + + SFE_FLAC_BAD_HEADER, + SFE_FLAC_NEW_DECODER, + SFE_FLAC_INIT_DECODER, + SFE_FLAC_LOST_SYNC, + SFE_FLAC_BAD_SAMPLE_RATE, + SFE_FLAC_UNKOWN_ERROR, + + SFE_WVE_NOT_WVE, + SFE_WVE_NO_PIPE, + + SFE_MAX_ERROR /* This must be last in list. */ +} ; + +int subformat_to_bytewidth (int format) ; +int s_bitwidth_to_subformat (int bits) ; +int u_bitwidth_to_subformat (int bits) ; + +/* Functions for reading and writing floats and doubles on processors +** with non-IEEE floats/doubles. +*/ +float float32_be_read (unsigned char *cptr) ; +float float32_le_read (unsigned char *cptr) ; +void float32_be_write (float in, unsigned char *out) ; +void float32_le_write (float in, unsigned char *out) ; + +double double64_be_read (unsigned char *cptr) ; +double double64_le_read (unsigned char *cptr) ; +void double64_be_write (double in, unsigned char *out) ; +void double64_le_write (double in, unsigned char *out) ; + +/* Functions for writing to the internal logging buffer. */ + +void psf_log_printf (SF_PRIVATE *psf, const char *format, ...) ; +void psf_log_SF_INFO (SF_PRIVATE *psf) ; + +void psf_hexdump (const void *ptr, int len) ; + +/* Functions used when writing file headers. */ + +int psf_binheader_writef (SF_PRIVATE *psf, const char *format, ...) ; +void psf_asciiheader_printf (SF_PRIVATE *psf, const char *format, ...) ; + +/* Functions used when reading file headers. */ + +int psf_binheader_readf (SF_PRIVATE *psf, char const *format, ...) ; + +/* Functions used in the write function for updating the peak chunk. */ + +void peak_update_short (SF_PRIVATE *psf, short *ptr, size_t items) ; +void peak_update_int (SF_PRIVATE *psf, int *ptr, size_t items) ; +void peak_update_double (SF_PRIVATE *psf, double *ptr, size_t items) ; + +/* Functions defined in command.c. */ + +int psf_get_format_simple_count (void) ; +int psf_get_format_simple (SF_FORMAT_INFO *data) ; + +int psf_get_format_info (SF_FORMAT_INFO *data) ; + +int psf_get_format_major_count (void) ; +int psf_get_format_major (SF_FORMAT_INFO *data) ; + +int psf_get_format_subtype_count (void) ; +int psf_get_format_subtype (SF_FORMAT_INFO *data) ; + +void psf_generate_format_desc (SF_PRIVATE *psf) ; + +double psf_calc_signal_max (SF_PRIVATE *psf, int normalize) ; +int psf_calc_max_all_channels (SF_PRIVATE *psf, double *peaks, int normalize) ; + +int psf_get_signal_max (SF_PRIVATE *psf, double *peak) ; +int psf_get_max_all_channels (SF_PRIVATE *psf, double *peaks) ; + +/* Functions in strings.c. */ + +const char* psf_get_string (SF_PRIVATE *psf, int str_type) ; +int psf_set_string (SF_PRIVATE *psf, int str_type, const char *str) ; +int psf_store_string (SF_PRIVATE *psf, int str_type, const char *str) ; + +/* Default seek function. Use for PCM and float encoded data. */ +sf_count_t psf_default_seek (SF_PRIVATE *psf, int mode, sf_count_t samples_from_start) ; + +int macos_guess_file_type (SF_PRIVATE *psf, const char *filename) ; + +/*------------------------------------------------------------------------------------ +** File I/O functions which will allow access to large files (> 2 Gig) on +** some 32 bit OSes. Implementation in file_io.c. +*/ + +int psf_fopen (SF_PRIVATE *psf, const char *pathname, int flags) ; +int psf_set_stdio (SF_PRIVATE *psf, int mode) ; +int psf_file_valid (SF_PRIVATE *psf) ; +void psf_set_file (SF_PRIVATE *psf, int fd) ; +void psf_init_files (SF_PRIVATE *psf) ; +void psf_use_rsrc (SF_PRIVATE *psf, int on_off) ; + +sf_count_t psf_fseek (SF_PRIVATE *psf, sf_count_t offset, int whence) ; +sf_count_t psf_fread (void *ptr, sf_count_t bytes, sf_count_t count, SF_PRIVATE *psf) ; +sf_count_t psf_fwrite (const void *ptr, sf_count_t bytes, sf_count_t count, SF_PRIVATE *psf) ; +sf_count_t psf_fgets (char *buffer, sf_count_t bufsize, SF_PRIVATE *psf) ; +sf_count_t psf_ftell (SF_PRIVATE *psf) ; +sf_count_t psf_get_filelen (SF_PRIVATE *psf) ; + +void psf_fsync (SF_PRIVATE *psf) ; + +int psf_is_pipe (SF_PRIVATE *psf) ; + +int psf_ftruncate (SF_PRIVATE *psf, sf_count_t len) ; +int psf_fclose (SF_PRIVATE *psf) ; + +/* Open and close the resource fork of a file. */ +int psf_open_rsrc (SF_PRIVATE *psf, int mode) ; +int psf_close_rsrc (SF_PRIVATE *psf) ; + +/* +void psf_fclearerr (SF_PRIVATE *psf) ; +int psf_ferror (SF_PRIVATE *psf) ; +*/ + +/*------------------------------------------------------------------------------------ +** Functions for reading and writing different file formats. +*/ + +int aiff_open (SF_PRIVATE *psf) ; +int au_open (SF_PRIVATE *psf) ; +int avr_open (SF_PRIVATE *psf) ; +int htk_open (SF_PRIVATE *psf) ; +int ircam_open (SF_PRIVATE *psf) ; +int mat4_open (SF_PRIVATE *psf) ; +int mat5_open (SF_PRIVATE *psf) ; +int nist_open (SF_PRIVATE *psf) ; +int paf_open (SF_PRIVATE *psf) ; +int pvf_open (SF_PRIVATE *psf) ; +int raw_open (SF_PRIVATE *psf) ; +int sd2_open (SF_PRIVATE *psf) ; +int sds_open (SF_PRIVATE *psf) ; +int svx_open (SF_PRIVATE *psf) ; +int voc_open (SF_PRIVATE *psf) ; +int w64_open (SF_PRIVATE *psf) ; +int wav_open (SF_PRIVATE *psf) ; +int xi_open (SF_PRIVATE *psf) ; +int flac_open (SF_PRIVATE *psf) ; +int caf_open (SF_PRIVATE *psf) ; + +/* In progress. Do not currently work. */ + +int mpeg_open (SF_PRIVATE *psf) ; +int ogg_open (SF_PRIVATE *psf) ; +int rx2_open (SF_PRIVATE *psf) ; +int txw_open (SF_PRIVATE *psf) ; +int wve_open (SF_PRIVATE *psf) ; +int dwd_open (SF_PRIVATE *psf) ; + +int macbinary3_open (SF_PRIVATE *psf) ; + +/*------------------------------------------------------------------------------------ +** Init functions for a number of common data encodings. +*/ + +int pcm_init (SF_PRIVATE *psf) ; +int ulaw_init (SF_PRIVATE *psf) ; +int alaw_init (SF_PRIVATE *psf) ; +int float32_init (SF_PRIVATE *psf) ; +int double64_init (SF_PRIVATE *psf) ; +int dwvw_init (SF_PRIVATE *psf, int bitwidth) ; +int gsm610_init (SF_PRIVATE *psf) ; +int vox_adpcm_init (SF_PRIVATE *psf) ; +int flac_init (SF_PRIVATE *psf) ; +int g72x_init (SF_PRIVATE * psf) ; + +int dither_init (SF_PRIVATE *psf, int mode) ; + +int wav_w64_ima_init (SF_PRIVATE *psf, int blockalign, int samplesperblock) ; +int wav_w64_msadpcm_init (SF_PRIVATE *psf, int blockalign, int samplesperblock) ; + +int aiff_ima_init (SF_PRIVATE *psf, int blockalign, int samplesperblock) ; + +int interleave_init (SF_PRIVATE *psf) ; + +/*------------------------------------------------------------------------------------ +** Other helper functions. +*/ + +void *psf_memset (void *s, int c, sf_count_t n) ; + +SF_INSTRUMENT * psf_instrument_alloc (void) ; + +void psf_sanitize_string (char * cptr, int len) ; + +/* Generate the current date as a string. */ +void psf_get_date_str (char *str, int maxlen) ; + +SF_BROADCAST_INFO* broadcast_info_alloc (void) ; +int broadcast_info_copy (SF_BROADCAST_INFO* dst, SF_BROADCAST_INFO* src) ; +int broadcast_add_coding_history (SF_BROADCAST_INFO* bext, unsigned int channels, unsigned int samplerate) ; + +typedef struct +{ int channels ; + int endianness ; +} AUDIO_DETECT ; + +int audio_detect (SF_PRIVATE * psf, AUDIO_DETECT *ad, const unsigned char * data, int datalen) ; + + +/*------------------------------------------------------------------------------------ +** Here's how we fix systems which don't snprintf / vsnprintf. +** Systems without these functions should use the +*/ + +#if USE_WINDOWS_API +#define LSF_SNPRINTF _snprintf +#elif (HAVE_SNPRINTF && ! FORCE_MISSING_SNPRINTF) +#define LSF_SNPRINTF snprintf +#else +int missing_snprintf (char *str, size_t n, char const *fmt, ...) ; +#define LSF_SNPRINTF missing_snprintf +#endif + +#if USE_WINDOWS_API +#define LSF_VSNPRINTF _vsnprintf +#elif (HAVE_VSNPRINTF && ! FORCE_MISSING_SNPRINTF) +#define LSF_VSNPRINTF vsnprintf +#else +int missing_vsnprintf (char *str, size_t n, const char *fmt, ...) ; +#define LSF_VSNPRINTF missing_vsnprintf +#endif + +/*------------------------------------------------------------------------------------ +** Extra commands for sf_command(). Not for public use yet. +*/ + +enum +{ SFC_TEST_AIFF_ADD_INST_CHUNK = 0x2000, + SFC_TEST_WAV_ADD_INFO_CHUNK = 0x2010 +} ; + +/* +** Maybe, one day, make these functions or something like them, public. +** +** Buffer to buffer dithering. Pointer in and out are allowed to point +** to the same buffer for in-place dithering. +*/ + +#if 0 +int sf_dither_short (const SF_DITHER_INFO *dither, const short *in, short *out, int count) ; +int sf_dither_int (const SF_DITHER_INFO *dither, const int *in, int *out, int count) ; +int sf_dither_float (const SF_DITHER_INFO *dither, const float *in, float *out, int count) ; +int sf_dither_double (const SF_DITHER_INFO *dither, const double *in, double *out, int count) ; +#endif + +#endif /* SNDFILE_COMMON_H */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 7b45c0ee-5835-4a18-a4ef-994e4cd95b67 +*/ diff --git a/src/create_symbols_file.py b/src/create_symbols_file.py new file mode 100755 index 00000000..8d60df81 --- /dev/null +++ b/src/create_symbols_file.py @@ -0,0 +1,153 @@ +#!/usr/bin/python + +# Copyright (C) 2003-2007 Erik de Castro Lopo +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the author nor the names of any contributors may be used +# to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import re, sys + +#---------------------------------------------------------------- +# These are all of the public functions exported from libsndfile. +# +# Its important not to change the order they are listed in or +# the ordinal values in the second column. + +ALL_SYMBOLS = ( + ( "sf_command", 1 ), + ( "sf_open", 2 ), + ( "sf_close", 3 ), + ( "sf_seek", 4 ), + ( "sf_error", 7 ), + ( "sf_perror", 8 ), + ( "sf_error_str", 9 ), + ( "sf_error_number", 10 ), + ( "sf_format_check", 11 ), + ( "sf_read_raw", 16 ), + ( "sf_readf_short", 17 ), + ( "sf_readf_int", 18 ), + ( "sf_readf_float", 19 ), + ( "sf_readf_double", 20 ), + ( "sf_read_short", 21 ), + ( "sf_read_int", 22 ), + ( "sf_read_float", 23 ), + ( "sf_read_double", 24 ), + ( "sf_write_raw", 32 ), + ( "sf_writef_short", 33 ), + ( "sf_writef_int", 34 ), + ( "sf_writef_float", 35 ), + ( "sf_writef_double", 36 ), + ( "sf_write_short", 37 ), + ( "sf_write_int", 38 ), + ( "sf_write_float", 39 ), + ( "sf_write_double", 40 ), + ( "sf_strerror", 50 ), + ( "sf_get_string", 60 ), + ( "sf_set_string", 61 ), + ( "sf_get_info", 68 ), + ( "sf_open_fd", 70 ), + ( "sf_open_virtual", 80 ), + ( "sf_write_sync", 90 ) + ) + +#------------------------------------------------------------------------------- + +def linux_symbols (progname, version): + print "# Auto-generated by %s\n" %progname + print "libsndfile.so.%s" % version + print "{" + print " global:" + for name, ordinal in ALL_SYMBOLS: + print " %s ;" % name + print " local:" + print " * ;" + print "} ;" + print + return + +def darwin_symbols (progname, version): + print "# Auto-generated by %s\n" %progname + for name, ordinal in ALL_SYMBOLS: + print "_%s" % name + print + return + +def win32_symbols (progname, version, name): + print "; Auto-generated by %s\n" %progname + print "LIBRARY %s-%s.dll" % (name, re.sub ("\..*", "", version)) + print "EXPORTS\n" + for name, ordinal in ALL_SYMBOLS: + print "%-20s @%s" % (name, ordinal) + print + return + +def no_symbols (os_name): + print + print "No known way of restricting exported symbols on '%s'." % os_name + print "If you know a way, please contact the author." + print + return + +#------------------------------------------------------------------------------- + +progname = re.sub (".*[\\/]", "", sys.argv [0]) + +if len (sys.argv) != 3: + print + print "Usage : %s ." % progname + print + print " Currently supported values for target OS are:" + print " linux" + print " darwin (ie MacOSX)" + print " win32 (ie wintendo)" + print " cygwin (Cygwin on wintendo)" + print + sys.exit (1) + +os_name = sys.argv [1] +version = re.sub ("\.[a-z0-9]+$", "", sys.argv [2]) + +if os_name == "linux": + linux_symbols (progname, version) +elif os_name == "darwin": + darwin_symbols (progname, version) +elif os_name == "win32": + win32_symbols (progname, version, "libsndfile") +elif os_name == "cygwin": + win32_symbols (progname, version, "cygsndfile") +else: + no_symbols (os_name) + +sys.exit (0) + +# Do not edit or modify anything in this comment block. +# The arch-tag line is a file identity tag for the GNU Arch +# revision control system. +# +# arch-tag: 5814f35c-318f-4023-a0c3-d9cf7c9e5f6c + diff --git a/src/dither.c b/src/dither.c new file mode 100644 index 00000000..50b5ba29 --- /dev/null +++ b/src/dither.c @@ -0,0 +1,533 @@ +/* +** Copyright (C) 2003,2005 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include + +#include "sndfile.h" +#include "sfendian.h" +#include "common.h" + +/*============================================================================ +** Rule number 1 is to only apply dither when going from a larger bitwidth +** to a smaller bitwidth. This can happen on both read and write. +** +** Need to apply dither on all conversions marked X below. +** +** Dither on write: +** +** Input +** | short int float double +** --------+----------------------------------------------- +** O 8 bit | X X X X +** u 16 bit | none X X X +** t 24 bit | none X X X +** p 32 bit | none none X X +** u float | none none none none +** t double | none none none none +** +** Dither on read: +** +** Input +** O | 8 bit 16 bit 24 bit 32 bit float double +** u --------+------------------------------------------------- +** t short | none none X X X X +** p int | none none none X X X +** u float | none none none none none none +** t double | none none none none none none +*/ + +#define SFE_DITHER_BAD_PTR 666 +#define SFE_DITHER_BAD_TYPE 667 + +typedef struct +{ int read_short_dither_bits, read_int_dither_bits ; + int write_short_dither_bits, write_int_dither_bits ; + double read_float_dither_scale, read_double_dither_bits ; + double write_float_dither_scale, write_double_dither_bits ; + + sf_count_t (*read_short) (SF_PRIVATE *psf, short *ptr, sf_count_t len) ; + sf_count_t (*read_int) (SF_PRIVATE *psf, int *ptr, sf_count_t len) ; + sf_count_t (*read_float) (SF_PRIVATE *psf, float *ptr, sf_count_t len) ; + sf_count_t (*read_double) (SF_PRIVATE *psf, double *ptr, sf_count_t len) ; + + sf_count_t (*write_short) (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ; + sf_count_t (*write_int) (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ; + sf_count_t (*write_float) (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ; + sf_count_t (*write_double) (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ; + + double buffer [SF_BUFFER_LEN / sizeof (double)] ; +} DITHER_DATA ; + +static sf_count_t dither_read_short (SF_PRIVATE *psf, short *ptr, sf_count_t len) ; +static sf_count_t dither_read_int (SF_PRIVATE *psf, int *ptr, sf_count_t len) ; + +static sf_count_t dither_write_short (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ; +static sf_count_t dither_write_int (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ; +static sf_count_t dither_write_float (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ; +static sf_count_t dither_write_double (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ; + +int +dither_init (SF_PRIVATE *psf, int mode) +{ DITHER_DATA *pdither ; + + pdither = psf->dither ; /* This may be NULL. */ + + /* Turn off dither on read. */ + if (mode == SFM_READ && psf->read_dither.type == SFD_NO_DITHER) + { if (pdither == NULL) + return 0 ; /* Dither is already off, so just return. */ + + if (pdither->read_short) + psf->read_short = pdither->read_short ; + if (pdither->read_int) + psf->read_int = pdither->read_int ; + if (pdither->read_float) + psf->read_float = pdither->read_float ; + if (pdither->read_double) + psf->read_double = pdither->read_double ; + return 0 ; + } ; + + /* Turn off dither on write. */ + if (mode == SFM_WRITE && psf->write_dither.type == SFD_NO_DITHER) + { if (pdither == NULL) + return 0 ; /* Dither is already off, so just return. */ + + if (pdither->write_short) + psf->write_short = pdither->write_short ; + if (pdither->write_int) + psf->write_int = pdither->write_int ; + if (pdither->write_float) + psf->write_float = pdither->write_float ; + if (pdither->write_double) + psf->write_double = pdither->write_double ; + return 0 ; + } ; + + /* Turn on dither on read if asked. */ + if (mode == SFM_READ && psf->read_dither.type != 0) + { if (pdither == NULL) + pdither = psf->dither = calloc (1, sizeof (DITHER_DATA)) ; + if (pdither == NULL) + return SFE_MALLOC_FAILED ; + + switch (psf->sf.format & SF_FORMAT_SUBMASK) + { case SF_FORMAT_DOUBLE : + case SF_FORMAT_FLOAT : + pdither->read_int = psf->read_int ; + psf->read_int = dither_read_int ; + + case SF_FORMAT_PCM_32 : + case SF_FORMAT_PCM_24 : + case SF_FORMAT_PCM_16 : + case SF_FORMAT_PCM_S8 : + case SF_FORMAT_PCM_U8 : + pdither->read_short = psf->read_short ; + psf->read_short = dither_read_short ; + + default : break ; + } ; + } ; + + /* Turn on dither on write if asked. */ + if (mode == SFM_WRITE && psf->write_dither.type != 0) + { if (pdither == NULL) + pdither = psf->dither = calloc (1, sizeof (DITHER_DATA)) ; + if (pdither == NULL) + return SFE_MALLOC_FAILED ; + + switch (psf->sf.format & SF_FORMAT_SUBMASK) + { case SF_FORMAT_DOUBLE : + case SF_FORMAT_FLOAT : + pdither->write_int = psf->write_int ; + psf->write_int = dither_write_int ; + + case SF_FORMAT_PCM_32 : + case SF_FORMAT_PCM_24 : + case SF_FORMAT_PCM_16 : + case SF_FORMAT_PCM_S8 : + case SF_FORMAT_PCM_U8 : + + default : break ; + } ; + + pdither->write_short = psf->write_short ; + psf->write_short = dither_write_short ; + + pdither->write_int = psf->write_int ; + psf->write_int = dither_write_int ; + + pdither->write_float = psf->write_float ; + psf->write_float = dither_write_float ; + + pdither->write_double = psf->write_double ; + psf->write_double = dither_write_double ; + } ; + + return 0 ; +} /* dither_init */ + +/*============================================================================== +*/ + +static void dither_short (const short *in, short *out, int frames, int channels) ; +static void dither_int (const int *in, int *out, int frames, int channels) ; + +static void dither_float (const float *in, float *out, int frames, int channels) ; +static void dither_double (const double *in, double *out, int frames, int channels) ; + +static sf_count_t +dither_read_short (SF_PRIVATE * UNUSED (psf), short * UNUSED (ptr), sf_count_t len) +{ + return len ; +} /* dither_read_short */ + +static sf_count_t +dither_read_int (SF_PRIVATE * UNUSED (psf), int * UNUSED (ptr), sf_count_t len) +{ + return len ; +} /* dither_read_int */ + +/*------------------------------------------------------------------------------ +*/ + +static sf_count_t +dither_write_short (SF_PRIVATE *psf, const short *ptr, sf_count_t len) +{ DITHER_DATA *pdither ; + int bufferlen, writecount, thiswrite ; + sf_count_t total = 0 ; + + if ((pdither = psf->dither) == NULL) + { psf->error = SFE_DITHER_BAD_PTR ; + return 0 ; + } ; + + switch (psf->sf.format & SF_FORMAT_SUBMASK) + { case SF_FORMAT_PCM_S8 : + case SF_FORMAT_PCM_U8 : + case SF_FORMAT_DPCM_8 : + break ; + + default : + return pdither->write_short (psf, ptr, len) ; + } ; + + bufferlen = sizeof (pdither->buffer) / sizeof (short) ; + + while (len > 0) + { writecount = (len >= bufferlen) ? bufferlen : (int) len ; + writecount /= psf->sf.channels ; + writecount *= psf->sf.channels ; + + dither_short (ptr, (short*) pdither->buffer, writecount / psf->sf.channels, psf->sf.channels) ; + + thiswrite = pdither->write_short (psf, (short*) pdither->buffer, writecount) ; + total += thiswrite ; + len -= thiswrite ; + if (thiswrite < writecount) + break ; + } ; + + return total ; +} /* dither_write_short */ + +static sf_count_t +dither_write_int (SF_PRIVATE *psf, const int *ptr, sf_count_t len) +{ DITHER_DATA *pdither ; + int bufferlen, writecount, thiswrite ; + sf_count_t total = 0 ; + + if ((pdither = psf->dither) == NULL) + { psf->error = SFE_DITHER_BAD_PTR ; + return 0 ; + } ; + + switch (psf->sf.format & SF_FORMAT_SUBMASK) + { case SF_FORMAT_PCM_S8 : + case SF_FORMAT_PCM_U8 : + case SF_FORMAT_PCM_16 : + case SF_FORMAT_PCM_24 : + + case SF_FORMAT_DPCM_8 : + case SF_FORMAT_DPCM_16 : + break ; + + default : + return pdither->write_int (psf, ptr, len) ; + } ; + + + bufferlen = sizeof (pdither->buffer) / sizeof (int) ; + + while (len > 0) + { writecount = (len >= bufferlen) ? bufferlen : (int) len ; + writecount /= psf->sf.channels ; + writecount *= psf->sf.channels ; + + dither_int (ptr, (int*) pdither->buffer, writecount / psf->sf.channels, psf->sf.channels) ; + + thiswrite = pdither->write_int (psf, (int*) pdither->buffer, writecount) ; + total += thiswrite ; + len -= thiswrite ; + if (thiswrite < writecount) + break ; + } ; + + return total ; +} /* dither_write_int */ + +static sf_count_t +dither_write_float (SF_PRIVATE *psf, const float *ptr, sf_count_t len) +{ DITHER_DATA *pdither ; + int bufferlen, writecount, thiswrite ; + sf_count_t total = 0 ; + + if ((pdither = psf->dither) == NULL) + { psf->error = SFE_DITHER_BAD_PTR ; + return 0 ; + } ; + + switch (psf->sf.format & SF_FORMAT_SUBMASK) + { case SF_FORMAT_PCM_S8 : + case SF_FORMAT_PCM_U8 : + case SF_FORMAT_PCM_16 : + case SF_FORMAT_PCM_24 : + + case SF_FORMAT_DPCM_8 : + case SF_FORMAT_DPCM_16 : + break ; + + default : + return pdither->write_float (psf, ptr, len) ; + } ; + + bufferlen = sizeof (pdither->buffer) / sizeof (float) ; + + while (len > 0) + { writecount = (len >= bufferlen) ? bufferlen : (float) len ; + writecount /= psf->sf.channels ; + writecount *= psf->sf.channels ; + + dither_float (ptr, (float*) pdither->buffer, writecount / psf->sf.channels, psf->sf.channels) ; + + thiswrite = pdither->write_float (psf, (float*) pdither->buffer, writecount) ; + total += thiswrite ; + len -= thiswrite ; + if (thiswrite < writecount) + break ; + } ; + + return total ; +} /* dither_write_float */ + +static sf_count_t +dither_write_double (SF_PRIVATE *psf, const double *ptr, sf_count_t len) +{ DITHER_DATA *pdither ; + int bufferlen, writecount, thiswrite ; + sf_count_t total = 0 ; + + if ((pdither = psf->dither) == NULL) + { psf->error = SFE_DITHER_BAD_PTR ; + return 0 ; + } ; + + switch (psf->sf.format & SF_FORMAT_SUBMASK) + { case SF_FORMAT_PCM_S8 : + case SF_FORMAT_PCM_U8 : + case SF_FORMAT_PCM_16 : + case SF_FORMAT_PCM_24 : + + case SF_FORMAT_DPCM_8 : + case SF_FORMAT_DPCM_16 : + break ; + + default : + return pdither->write_double (psf, ptr, len) ; + } ; + + + bufferlen = sizeof (pdither->buffer) / sizeof (double) ; + + while (len > 0) + { writecount = (len >= bufferlen) ? bufferlen : (double) len ; + writecount /= psf->sf.channels ; + writecount *= psf->sf.channels ; + + dither_double (ptr, (double*) pdither->buffer, writecount / psf->sf.channels, psf->sf.channels) ; + + thiswrite = pdither->write_double (psf, (double*) pdither->buffer, writecount) ; + total += thiswrite ; + len -= thiswrite ; + if (thiswrite < writecount) + break ; + } ; + + return total ; +} /* dither_write_double */ + +/*============================================================================== +*/ + +static void +dither_short (const short *in, short *out, int frames, int channels) +{ int ch, k ; + + for (ch = 0 ; ch < channels ; ch++) + for (k = ch ; k < channels * frames ; k += channels) + out [k] = in [k] ; + +} /* dither_short */ + +static void +dither_int (const int *in, int *out, int frames, int channels) +{ int ch, k ; + + for (ch = 0 ; ch < channels ; ch++) + for (k = ch ; k < channels * frames ; k += channels) + out [k] = in [k] ; + +} /* dither_int */ + +static void +dither_float (const float *in, float *out, int frames, int channels) +{ int ch, k ; + + for (ch = 0 ; ch < channels ; ch++) + for (k = ch ; k < channels * frames ; k += channels) + out [k] = in [k] ; + +} /* dither_float */ + +static void +dither_double (const double *in, double *out, int frames, int channels) +{ int ch, k ; + + for (ch = 0 ; ch < channels ; ch++) + for (k = ch ; k < channels * frames ; k += channels) + out [k] = in [k] ; + +} /* dither_double */ + +/*============================================================================== +*/ +#if 0 + +/* +** Not made public because this (maybe) requires storage of state information. +** +** Also maybe need separate state info for each channel!!!! +*/ + +int +DO_NOT_USE_sf_dither_short (const SF_DITHER_INFO *dither, const short *in, short *out, int frames, int channels) +{ int ch, k ; + + if (! dither) + return SFE_DITHER_BAD_PTR ; + + switch (dither->type & SFD_TYPEMASK) + { case SFD_WHITE : + case SFD_TRIANGULAR_PDF : + for (ch = 0 ; ch < channels ; ch++) + for (k = ch ; k < channels * frames ; k += channels) + out [k] = in [k] ; + break ; + + default : + return SFE_DITHER_BAD_TYPE ; + } ; + + return 0 ; +} /* DO_NOT_USE_sf_dither_short */ + +int +DO_NOT_USE_sf_dither_int (const SF_DITHER_INFO *dither, const int *in, int *out, int frames, int channels) +{ int ch, k ; + + if (! dither) + return SFE_DITHER_BAD_PTR ; + + switch (dither->type & SFD_TYPEMASK) + { case SFD_WHITE : + case SFD_TRIANGULAR_PDF : + for (ch = 0 ; ch < channels ; ch++) + for (k = ch ; k < channels * frames ; k += channels) + out [k] = in [k] ; + break ; + + default : + return SFE_DITHER_BAD_TYPE ; + } ; + + return 0 ; +} /* DO_NOT_USE_sf_dither_int */ + +int +DO_NOT_USE_sf_dither_float (const SF_DITHER_INFO *dither, const float *in, float *out, int frames, int channels) +{ int ch, k ; + + if (! dither) + return SFE_DITHER_BAD_PTR ; + + switch (dither->type & SFD_TYPEMASK) + { case SFD_WHITE : + case SFD_TRIANGULAR_PDF : + for (ch = 0 ; ch < channels ; ch++) + for (k = ch ; k < channels * frames ; k += channels) + out [k] = in [k] ; + break ; + + default : + return SFE_DITHER_BAD_TYPE ; + } ; + + return 0 ; +} /* DO_NOT_USE_sf_dither_float */ + +int +DO_NOT_USE_sf_dither_double (const SF_DITHER_INFO *dither, const double *in, double *out, int frames, int channels) +{ int ch, k ; + + if (! dither) + return SFE_DITHER_BAD_PTR ; + + switch (dither->type & SFD_TYPEMASK) + { case SFD_WHITE : + case SFD_TRIANGULAR_PDF : + for (ch = 0 ; ch < channels ; ch++) + for (k = ch ; k < channels * frames ; k += channels) + out [k] = in [k] ; + break ; + + default : + return SFE_DITHER_BAD_TYPE ; + } ; + + return 0 ; +} /* DO_NOT_USE_sf_dither_double */ + +#endif +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 673fad58-5314-421c-9144-9d54bfdf104c +*/ diff --git a/src/double64.c b/src/double64.c new file mode 100644 index 00000000..5bcaf5c6 --- /dev/null +++ b/src/double64.c @@ -0,0 +1,1042 @@ +/* +** Copyright (C) 1999-2006 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include +#include + +#include "sndfile.h" +#include "sfendian.h" +#include "common.h" +#include "float_cast.h" + +#if CPU_IS_LITTLE_ENDIAN + #define DOUBLE64_READ double64_le_read + #define DOUBLE64_WRITE double64_le_write +#elif CPU_IS_BIG_ENDIAN + #define DOUBLE64_READ double64_be_read + #define DOUBLE64_WRITE double64_be_write +#endif + +/* A 32 number which will not overflow when multiplied by sizeof (double). */ +#define SENSIBLE_LEN (0x8000000) + +/*-------------------------------------------------------------------------------------------- +** Processor floating point capabilities. double64_get_capability () returns one of the +** latter three values. +*/ + +enum +{ DOUBLE_UNKNOWN = 0x00, + DOUBLE_CAN_RW_LE = 0x23, + DOUBLE_CAN_RW_BE = 0x34, + DOUBLE_BROKEN_LE = 0x45, + DOUBLE_BROKEN_BE = 0x56 +} ; + +/*-------------------------------------------------------------------------------------------- +** Prototypes for private functions. +*/ + +static sf_count_t host_read_d2s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ; +static sf_count_t host_read_d2i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ; +static sf_count_t host_read_d2f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ; +static sf_count_t host_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ; + +static sf_count_t host_write_s2d (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ; +static sf_count_t host_write_i2d (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ; +static sf_count_t host_write_f2d (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ; +static sf_count_t host_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ; + +static void double64_peak_update (SF_PRIVATE *psf, const double *buffer, int count, sf_count_t indx) ; + +static int double64_get_capability (SF_PRIVATE *psf) ; + +static sf_count_t replace_read_d2s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ; +static sf_count_t replace_read_d2i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ; +static sf_count_t replace_read_d2f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ; +static sf_count_t replace_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ; + +static sf_count_t replace_write_s2d (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ; +static sf_count_t replace_write_i2d (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ; +static sf_count_t replace_write_f2d (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ; +static sf_count_t replace_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ; + +static void d2bd_read (double *buffer, int count) ; +static void bd2d_write (double *buffer, int count) ; + +/*-------------------------------------------------------------------------------------------- +** Exported functions. +*/ + +int +double64_init (SF_PRIVATE *psf) +{ static int double64_caps ; + + double64_caps = double64_get_capability (psf) ; + + psf->blockwidth = sizeof (double) * psf->sf.channels ; + + if (psf->mode == SFM_READ || psf->mode == SFM_RDWR) + { switch (psf->endian + double64_caps) + { case (SF_ENDIAN_BIG + DOUBLE_CAN_RW_BE) : + psf->float_endswap = SF_FALSE ; + psf->read_short = host_read_d2s ; + psf->read_int = host_read_d2i ; + psf->read_float = host_read_d2f ; + psf->read_double = host_read_d ; + break ; + + case (SF_ENDIAN_LITTLE + DOUBLE_CAN_RW_LE) : + psf->float_endswap = SF_FALSE ; + psf->read_short = host_read_d2s ; + psf->read_int = host_read_d2i ; + psf->read_float = host_read_d2f ; + psf->read_double = host_read_d ; + break ; + + case (SF_ENDIAN_BIG + DOUBLE_CAN_RW_LE) : + psf->float_endswap = SF_TRUE ; + psf->read_short = host_read_d2s ; + psf->read_int = host_read_d2i ; + psf->read_float = host_read_d2f ; + psf->read_double = host_read_d ; + break ; + + case (SF_ENDIAN_LITTLE + DOUBLE_CAN_RW_BE) : + psf->float_endswap = SF_TRUE ; + psf->read_short = host_read_d2s ; + psf->read_int = host_read_d2i ; + psf->read_float = host_read_d2f ; + psf->read_double = host_read_d ; + break ; + + /* When the CPU is not IEEE compatible. */ + case (SF_ENDIAN_BIG + DOUBLE_BROKEN_BE) : + psf->float_endswap = SF_FALSE ; + psf->read_short = replace_read_d2s ; + psf->read_int = replace_read_d2i ; + psf->read_float = replace_read_d2f ; + psf->read_double = replace_read_d ; + break ; + + case (SF_ENDIAN_LITTLE + DOUBLE_BROKEN_LE) : + psf->float_endswap = SF_FALSE ; + psf->read_short = replace_read_d2s ; + psf->read_int = replace_read_d2i ; + psf->read_float = replace_read_d2f ; + psf->read_double = replace_read_d ; + break ; + + case (SF_ENDIAN_BIG + DOUBLE_BROKEN_LE) : + psf->float_endswap = SF_TRUE ; + psf->read_short = replace_read_d2s ; + psf->read_int = replace_read_d2i ; + psf->read_float = replace_read_d2f ; + psf->read_double = replace_read_d ; + break ; + + case (SF_ENDIAN_LITTLE + DOUBLE_BROKEN_BE) : + psf->float_endswap = SF_TRUE ; + psf->read_short = replace_read_d2s ; + psf->read_int = replace_read_d2i ; + psf->read_float = replace_read_d2f ; + psf->read_double = replace_read_d ; + break ; + + default : break ; + } ; + } ; + + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + { switch (psf->endian + double64_caps) + { case (SF_ENDIAN_LITTLE + DOUBLE_CAN_RW_LE) : + psf->float_endswap = SF_FALSE ; + psf->write_short = host_write_s2d ; + psf->write_int = host_write_i2d ; + psf->write_float = host_write_f2d ; + psf->write_double = host_write_d ; + break ; + + case (SF_ENDIAN_BIG + DOUBLE_CAN_RW_BE) : + psf->float_endswap = SF_FALSE ; + psf->write_short = host_write_s2d ; + psf->write_int = host_write_i2d ; + psf->write_float = host_write_f2d ; + psf->write_double = host_write_d ; + break ; + + case (SF_ENDIAN_BIG + DOUBLE_CAN_RW_LE) : + psf->float_endswap = SF_TRUE ; + psf->write_short = host_write_s2d ; + psf->write_int = host_write_i2d ; + psf->write_float = host_write_f2d ; + psf->write_double = host_write_d ; + break ; + + case (SF_ENDIAN_LITTLE + DOUBLE_CAN_RW_BE) : + psf->float_endswap = SF_TRUE ; + psf->write_short = host_write_s2d ; + psf->write_int = host_write_i2d ; + psf->write_float = host_write_f2d ; + psf->write_double = host_write_d ; + break ; + + /* When the CPU is not IEEE compatible. */ + case (SF_ENDIAN_LITTLE + DOUBLE_BROKEN_LE) : + psf->float_endswap = SF_FALSE ; + psf->write_short = replace_write_s2d ; + psf->write_int = replace_write_i2d ; + psf->write_float = replace_write_f2d ; + psf->write_double = replace_write_d ; + break ; + + case (SF_ENDIAN_BIG + DOUBLE_BROKEN_BE) : + psf->float_endswap = SF_FALSE ; + psf->write_short = replace_write_s2d ; + psf->write_int = replace_write_i2d ; + psf->write_float = replace_write_f2d ; + psf->write_double = replace_write_d ; + break ; + + case (SF_ENDIAN_BIG + DOUBLE_BROKEN_LE) : + psf->float_endswap = SF_TRUE ; + psf->write_short = replace_write_s2d ; + psf->write_int = replace_write_i2d ; + psf->write_float = replace_write_f2d ; + psf->write_double = replace_write_d ; + break ; + + case (SF_ENDIAN_LITTLE + DOUBLE_BROKEN_BE) : + psf->float_endswap = SF_TRUE ; + psf->write_short = replace_write_s2d ; + psf->write_int = replace_write_i2d ; + psf->write_float = replace_write_f2d ; + psf->write_double = replace_write_d ; + break ; + + default : break ; + } ; + } ; + + if (psf->filelength > psf->dataoffset) + { psf->datalength = (psf->dataend > 0) ? psf->dataend - psf->dataoffset : + psf->filelength - psf->dataoffset ; + } + else + psf->datalength = 0 ; + + psf->sf.frames = psf->datalength / psf->blockwidth ; + + return 0 ; +} /* double64_init */ + +/*---------------------------------------------------------------------------- +** From : http://www.hpcf.cam.ac.uk/fp_formats.html +** +** 64 bit double precision layout (big endian) +** Sign bit 0 +** Exponent bits 1-11 +** Mantissa bits 12-63 +** Exponent Offset 1023 +** +** double single +** +** +INF 7FF0000000000000 7F800000 +** -INF FFF0000000000000 FF800000 +** NaN 7FF0000000000001 7F800001 +** to to +** 7FFFFFFFFFFFFFFF 7FFFFFFF +** and and +** FFF0000000000001 FF800001 +** to to +** FFFFFFFFFFFFFFFF FFFFFFFF +** +OVER 7FEFFFFFFFFFFFFF 7F7FFFFF +** -OVER FFEFFFFFFFFFFFFF FF7FFFFF +** +UNDER 0010000000000000 00800000 +** -UNDER 8010000000000000 80800000 +*/ + +double +double64_be_read (unsigned char *cptr) +{ int exponent, negative, upper, lower ; + double dvalue ; + + negative = (cptr [0] & 0x80) ? 1 : 0 ; + exponent = ((cptr [0] & 0x7F) << 4) | ((cptr [1] >> 4) & 0xF) ; + + /* Might not have a 64 bit long, so load the mantissa into a double. */ + upper = (((cptr [1] & 0xF) << 24) | (cptr [2] << 16) | (cptr [3] << 8) | cptr [4]) ; + lower = (cptr [5] << 16) | (cptr [6] << 8) | cptr [7] ; + + if (exponent == 0 && upper == 0 && lower == 0) + return 0.0 ; + + dvalue = upper + lower / ((double) 0x1000000) ; + dvalue += 0x10000000 ; + + exponent = exponent - 0x3FF ; + + dvalue = dvalue / ((double) 0x10000000) ; + + if (negative) + dvalue *= -1 ; + + if (exponent > 0) + dvalue *= (1 << exponent) ; + else if (exponent < 0) + dvalue /= (1 << abs (exponent)) ; + + return dvalue ; +} /* double64_be_read */ + +double +double64_le_read (unsigned char *cptr) +{ int exponent, negative, upper, lower ; + double dvalue ; + + negative = (cptr [7] & 0x80) ? 1 : 0 ; + exponent = ((cptr [7] & 0x7F) << 4) | ((cptr [6] >> 4) & 0xF) ; + + /* Might not have a 64 bit long, so load the mantissa into a double. */ + upper = ((cptr [6] & 0xF) << 24) | (cptr [5] << 16) | (cptr [4] << 8) | cptr [3] ; + lower = (cptr [2] << 16) | (cptr [1] << 8) | cptr [0] ; + + if (exponent == 0 && upper == 0 && lower == 0) + return 0.0 ; + + dvalue = upper + lower / ((double) 0x1000000) ; + dvalue += 0x10000000 ; + + exponent = exponent - 0x3FF ; + + dvalue = dvalue / ((double) 0x10000000) ; + + if (negative) + dvalue *= -1 ; + + if (exponent > 0) + dvalue *= (1 << exponent) ; + else if (exponent < 0) + dvalue /= (1 << abs (exponent)) ; + + return dvalue ; +} /* double64_le_read */ + +void +double64_be_write (double in, unsigned char *out) +{ int exponent, mantissa ; + + memset (out, 0, sizeof (double)) ; + + if (fabs (in) < 1e-30) + return ; + + if (in < 0.0) + { in *= -1.0 ; + out [0] |= 0x80 ; + } ; + + in = frexp (in, &exponent) ; + + exponent += 1022 ; + + out [0] |= (exponent >> 4) & 0x7F ; + out [1] |= (exponent << 4) & 0xF0 ; + + in *= 0x20000000 ; + mantissa = lrint (floor (in)) ; + + out [1] |= (mantissa >> 24) & 0xF ; + out [2] = (mantissa >> 16) & 0xFF ; + out [3] = (mantissa >> 8) & 0xFF ; + out [4] = mantissa & 0xFF ; + + in = fmod (in, 1.0) ; + in *= 0x1000000 ; + mantissa = lrint (floor (in)) ; + + out [5] = (mantissa >> 16) & 0xFF ; + out [6] = (mantissa >> 8) & 0xFF ; + out [7] = mantissa & 0xFF ; + + return ; +} /* double64_be_write */ + +void +double64_le_write (double in, unsigned char *out) +{ int exponent, mantissa ; + + memset (out, 0, sizeof (double)) ; + + if (fabs (in) < 1e-30) + return ; + + if (in < 0.0) + { in *= -1.0 ; + out [7] |= 0x80 ; + } ; + + in = frexp (in, &exponent) ; + + exponent += 1022 ; + + out [7] |= (exponent >> 4) & 0x7F ; + out [6] |= (exponent << 4) & 0xF0 ; + + in *= 0x20000000 ; + mantissa = lrint (floor (in)) ; + + out [6] |= (mantissa >> 24) & 0xF ; + out [5] = (mantissa >> 16) & 0xFF ; + out [4] = (mantissa >> 8) & 0xFF ; + out [3] = mantissa & 0xFF ; + + in = fmod (in, 1.0) ; + in *= 0x1000000 ; + mantissa = lrint (floor (in)) ; + + out [2] = (mantissa >> 16) & 0xFF ; + out [1] = (mantissa >> 8) & 0xFF ; + out [0] = mantissa & 0xFF ; + + return ; +} /* double64_le_write */ + +/*============================================================================================== +** Private functions. +*/ + +static void +double64_peak_update (SF_PRIVATE *psf, const double *buffer, int count, sf_count_t indx) +{ int chan ; + int k, position ; + float fmaxval ; + + for (chan = 0 ; chan < psf->sf.channels ; chan++) + { fmaxval = fabs (buffer [chan]) ; + position = 0 ; + for (k = chan ; k < count ; k += psf->sf.channels) + if (fmaxval < fabs (buffer [k])) + { fmaxval = fabs (buffer [k]) ; + position = k ; + } ; + + if (fmaxval > psf->peak_info->peaks [chan].value) + { psf->peak_info->peaks [chan].value = fmaxval ; + psf->peak_info->peaks [chan].position = psf->write_current + indx + (position / psf->sf.channels) ; + } ; + } ; + + return ; +} /* double64_peak_update */ + +static int +double64_get_capability (SF_PRIVATE *psf) +{ union + { double d ; + unsigned char c [8] ; + } data ; + + data.d = 1.234567890123456789 ; /* Some abitrary value. */ + + if (! psf->ieee_replace) + { /* If this test is true ints and floats are compatible and little endian. */ + if (data.c [0] == 0xfb && data.c [1] == 0x59 && data.c [2] == 0x8c && data.c [3] == 0x42 && + data.c [4] == 0xca && data.c [5] == 0xc0 && data.c [6] == 0xf3 && data.c [7] == 0x3f) + return DOUBLE_CAN_RW_LE ; + + /* If this test is true ints and floats are compatible and big endian. */ + if (data.c [0] == 0x3f && data.c [1] == 0xf3 && data.c [2] == 0xc0 && data.c [3] == 0xca && + data.c [4] == 0x42 && data.c [5] == 0x8c && data.c [6] == 0x59 && data.c [7] == 0xfb) + return DOUBLE_CAN_RW_BE ; + } ; + + /* Doubles are broken. Don't expect reading or writing to be fast. */ + psf_log_printf (psf, "Using IEEE replacement code for double.\n") ; + + return (CPU_IS_LITTLE_ENDIAN) ? DOUBLE_BROKEN_LE : DOUBLE_BROKEN_BE ; +} /* double64_get_capability */ + +/*======================================================================================= +*/ + +static void +d2s_array (const double *src, int count, short *dest, double scale) +{ while (--count >= 0) + { dest [count] = lrint (scale * src [count]) ; + } ; +} /* d2s_array */ + +static void +d2s_clip_array (const double *src, int count, short *dest, double scale) +{ while (--count >= 0) + { double tmp = scale * src [count] ; + + if (CPU_CLIPS_POSITIVE == 0 && tmp > 32767.0) + dest [count] = SHRT_MAX ; + else if (CPU_CLIPS_NEGATIVE == 0 && tmp < -32768.0) + dest [count] = SHRT_MIN ; + else + dest [count] = lrint (tmp) ; + } ; +} /* d2s_clip_array */ + +static void +d2i_array (const double *src, int count, int *dest, double scale) +{ while (--count >= 0) + { dest [count] = lrint (scale * src [count]) ; + } ; +} /* d2i_array */ + +static void +d2i_clip_array (const double *src, int count, int *dest, double scale) +{ while (--count >= 0) + { float tmp = scale * src [count] ; + + if (CPU_CLIPS_POSITIVE == 0 && tmp > (1.0 * INT_MAX)) + dest [count] = INT_MAX ; + else if (CPU_CLIPS_NEGATIVE == 0 && tmp < (-1.0 * INT_MAX)) + dest [count] = INT_MIN ; + else + dest [count] = lrint (tmp) ; + } ; +} /* d2i_clip_array */ + +static inline void +d2f_array (const double *src, int count, float *dest) +{ while (--count >= 0) + { dest [count] = src [count] ; + } ; +} /* d2f_array */ + +static inline void +s2d_array (const short *src, double *dest, int count) +{ while (--count >= 0) + { dest [count] = src [count] ; + } ; +} /* s2d_array */ + +static inline void +i2d_array (const int *src, double *dest, int count) +{ while (--count >= 0) + { dest [count] = src [count] ; + } ; +} /* i2d_array */ + +static inline void +f2d_array (const float *src, double *dest, int count) +{ while (--count >= 0) + { dest [count] = src [count] ; + } ; +} /* f2d_array */ + +/*---------------------------------------------------------------------------------------------- +*/ + +static sf_count_t +host_read_d2s (SF_PRIVATE *psf, short *ptr, sf_count_t len) +{ void (*convert) (const double *, int, short *, double) ; + int bufferlen, readcount ; + sf_count_t total = 0 ; + double scale ; + + convert = (psf->add_clipping) ? d2s_clip_array : d2s_array ; + bufferlen = ARRAY_LEN (psf->u.dbuf) ; + scale = (psf->float_int_mult == 0) ? 1.0 : 0x7FFF / psf->float_max ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.dbuf, sizeof (double), bufferlen, psf) ; + + if (psf->float_endswap == SF_TRUE) + endswap_double_array (psf->u.dbuf, readcount) ; + + convert (psf->u.dbuf, readcount, ptr + total, scale) ; + total += readcount ; + len -= readcount ; + if (readcount < bufferlen) + break ; + } ; + + return total ; +} /* host_read_d2s */ + +static sf_count_t +host_read_d2i (SF_PRIVATE *psf, int *ptr, sf_count_t len) +{ void (*convert) (const double *, int, int *, double) ; + int bufferlen, readcount ; + sf_count_t total = 0 ; + double scale ; + + convert = (psf->add_clipping) ? d2i_clip_array : d2i_array ; + bufferlen = ARRAY_LEN (psf->u.dbuf) ; + scale = (psf->float_int_mult == 0) ? 1.0 : 0x7FFFFFFF / psf->float_max ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.dbuf, sizeof (double), bufferlen, psf) ; + + if (psf->float_endswap == SF_TRUE) + endswap_double_array (psf->u.dbuf, bufferlen) ; + + convert (psf->u.dbuf, readcount, ptr + total, scale) ; + total += readcount ; + len -= readcount ; + if (readcount < bufferlen) + break ; + } ; + + return total ; +} /* host_read_d2i */ + +static sf_count_t +host_read_d2f (SF_PRIVATE *psf, float *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + + bufferlen = ARRAY_LEN (psf->u.dbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.dbuf, sizeof (double), bufferlen, psf) ; + + if (psf->float_endswap == SF_TRUE) + endswap_double_array (psf->u.dbuf, bufferlen) ; + + d2f_array (psf->u.dbuf, readcount, ptr + total) ; + total += readcount ; + len -= readcount ; + if (readcount < bufferlen) + break ; + } ; + + return total ; +} /* host_read_d2f */ + +static sf_count_t +host_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len) +{ int bufferlen ; + sf_count_t readcount, total = 0 ; + + readcount = psf_fread (ptr, sizeof (double), len, psf) ; + + if (psf->float_endswap != SF_TRUE) + return readcount ; + + /* If the read length was sensible, endswap output in one go. */ + if (readcount < SENSIBLE_LEN) + { endswap_double_array (ptr, readcount) ; + return readcount ; + } ; + + bufferlen = SENSIBLE_LEN ; + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + + endswap_double_array (ptr + total, bufferlen) ; + + total += bufferlen ; + len -= bufferlen ; + } ; + + return total ; +} /* host_read_d */ + +static sf_count_t +host_write_s2d (SF_PRIVATE *psf, const short *ptr, sf_count_t len) +{ int bufferlen, writecount ; + sf_count_t total = 0 ; + + bufferlen = ARRAY_LEN (psf->u.dbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + + s2d_array (ptr + total, psf->u.dbuf, bufferlen) ; + + if (psf->peak_info) + double64_peak_update (psf, psf->u.dbuf, bufferlen, total / psf->sf.channels) ; + + if (psf->float_endswap == SF_TRUE) + endswap_double_array (psf->u.dbuf, bufferlen) ; + + writecount = psf_fwrite (psf->u.dbuf, sizeof (double), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* host_write_s2d */ + +static sf_count_t +host_write_i2d (SF_PRIVATE *psf, const int *ptr, sf_count_t len) +{ int bufferlen, writecount ; + sf_count_t total = 0 ; + + bufferlen = ARRAY_LEN (psf->u.dbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + i2d_array (ptr + total, psf->u.dbuf, bufferlen) ; + + if (psf->peak_info) + double64_peak_update (psf, psf->u.dbuf, bufferlen, total / psf->sf.channels) ; + + if (psf->float_endswap == SF_TRUE) + endswap_double_array (psf->u.dbuf, bufferlen) ; + + writecount = psf_fwrite (psf->u.dbuf, sizeof (double), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* host_write_i2d */ + +static sf_count_t +host_write_f2d (SF_PRIVATE *psf, const float *ptr, sf_count_t len) +{ int bufferlen, writecount ; + sf_count_t total = 0 ; + + bufferlen = ARRAY_LEN (psf->u.dbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + f2d_array (ptr + total, psf->u.dbuf, bufferlen) ; + + if (psf->peak_info) + double64_peak_update (psf, psf->u.dbuf, bufferlen, total / psf->sf.channels) ; + + if (psf->float_endswap == SF_TRUE) + endswap_double_array (psf->u.dbuf, bufferlen) ; + + writecount = psf_fwrite (psf->u.dbuf, sizeof (double), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* host_write_f2d */ + +static sf_count_t +host_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len) +{ int bufferlen, writecount ; + sf_count_t total = 0 ; + + if (psf->peak_info) + double64_peak_update (psf, ptr, len, 0) ; + + if (psf->float_endswap != SF_TRUE) + return psf_fwrite (ptr, sizeof (double), len, psf) ; + + bufferlen = ARRAY_LEN (psf->u.dbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + + endswap_double_copy (psf->u.dbuf, ptr + total, bufferlen) ; + + writecount = psf_fwrite (psf->u.dbuf, sizeof (double), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* host_write_d */ + +/*======================================================================================= +*/ + +static sf_count_t +replace_read_d2s (SF_PRIVATE *psf, short *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + double scale ; + + bufferlen = ARRAY_LEN (psf->u.dbuf) ; + scale = (psf->float_int_mult == 0) ? 1.0 : 0x7FFF / psf->float_max ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.dbuf, sizeof (double), bufferlen, psf) ; + + if (psf->float_endswap == SF_TRUE) + endswap_double_array (psf->u.dbuf, bufferlen) ; + + d2bd_read (psf->u.dbuf, bufferlen) ; + + d2s_array (psf->u.dbuf, readcount, ptr + total, scale) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* replace_read_d2s */ + +static sf_count_t +replace_read_d2i (SF_PRIVATE *psf, int *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + double scale ; + + bufferlen = ARRAY_LEN (psf->u.dbuf) ; + scale = (psf->float_int_mult == 0) ? 1.0 : 0x7FFFFFFF / psf->float_max ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.dbuf, sizeof (double), bufferlen, psf) ; + + if (psf->float_endswap == SF_TRUE) + endswap_double_array (psf->u.dbuf, bufferlen) ; + + d2bd_read (psf->u.dbuf, bufferlen) ; + + d2i_array (psf->u.dbuf, readcount, ptr + total, scale) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* replace_read_d2i */ + +static sf_count_t +replace_read_d2f (SF_PRIVATE *psf, float *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + + bufferlen = ARRAY_LEN (psf->u.dbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.dbuf, sizeof (double), bufferlen, psf) ; + + if (psf->float_endswap == SF_TRUE) + endswap_double_array (psf->u.dbuf, bufferlen) ; + + d2bd_read (psf->u.dbuf, bufferlen) ; + + memcpy (ptr + total, psf->u.dbuf, bufferlen * sizeof (double)) ; + + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* replace_read_d2f */ + +static sf_count_t +replace_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + + /* FIXME : This is probably nowhere near optimal. */ + bufferlen = ARRAY_LEN (psf->u.dbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.dbuf, sizeof (double), bufferlen, psf) ; + + if (psf->float_endswap == SF_TRUE) + endswap_double_array (psf->u.dbuf, readcount) ; + + d2bd_read (psf->u.dbuf, readcount) ; + + memcpy (ptr + total, psf->u.dbuf, readcount * sizeof (double)) ; + + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* replace_read_d */ + +static sf_count_t +replace_write_s2d (SF_PRIVATE *psf, const short *ptr, sf_count_t len) +{ int bufferlen, writecount ; + sf_count_t total = 0 ; + + bufferlen = ARRAY_LEN (psf->u.dbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + s2d_array (ptr + total, psf->u.dbuf, bufferlen) ; + + if (psf->peak_info) + double64_peak_update (psf, psf->u.dbuf, bufferlen, total / psf->sf.channels) ; + + bd2d_write (psf->u.dbuf, bufferlen) ; + + if (psf->float_endswap == SF_TRUE) + endswap_double_array (psf->u.dbuf, bufferlen) ; + + writecount = psf_fwrite (psf->u.dbuf, sizeof (double), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* replace_write_s2d */ + +static sf_count_t +replace_write_i2d (SF_PRIVATE *psf, const int *ptr, sf_count_t len) +{ int bufferlen, writecount ; + sf_count_t total = 0 ; + + bufferlen = ARRAY_LEN (psf->u.dbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + i2d_array (ptr + total, psf->u.dbuf, bufferlen) ; + + if (psf->peak_info) + double64_peak_update (psf, psf->u.dbuf, bufferlen, total / psf->sf.channels) ; + + bd2d_write (psf->u.dbuf, bufferlen) ; + + if (psf->float_endswap == SF_TRUE) + endswap_double_array (psf->u.dbuf, bufferlen) ; + + writecount = psf_fwrite (psf->u.dbuf, sizeof (double), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* replace_write_i2d */ + +static sf_count_t +replace_write_f2d (SF_PRIVATE *psf, const float *ptr, sf_count_t len) +{ int bufferlen, writecount ; + sf_count_t total = 0 ; + + bufferlen = ARRAY_LEN (psf->u.dbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + f2d_array (ptr + total, psf->u.dbuf, bufferlen) ; + + bd2d_write (psf->u.dbuf, bufferlen) ; + + if (psf->float_endswap == SF_TRUE) + endswap_double_array (psf->u.dbuf, bufferlen) ; + + writecount = psf_fwrite (psf->u.dbuf, sizeof (double), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* replace_write_f2d */ + +static sf_count_t +replace_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len) +{ int bufferlen, writecount ; + sf_count_t total = 0 ; + + /* FIXME : This is probably nowhere near optimal. */ + if (psf->peak_info) + double64_peak_update (psf, ptr, len, 0) ; + + bufferlen = ARRAY_LEN (psf->u.dbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + + memcpy (psf->u.dbuf, ptr + total, bufferlen * sizeof (double)) ; + + bd2d_write (psf->u.dbuf, bufferlen) ; + + if (psf->float_endswap == SF_TRUE) + endswap_double_array (psf->u.dbuf, bufferlen) ; + + writecount = psf_fwrite (psf->u.dbuf, sizeof (double), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* replace_write_d */ + +/*---------------------------------------------------------------------------------------------- +*/ + +static void +d2bd_read (double *buffer, int count) +{ while (--count >= 0) + { buffer [count] = DOUBLE64_READ ((unsigned char *) (buffer + count)) ; + } ; +} /* d2bd_read */ + +static void +bd2d_write (double *buffer, int count) +{ while (--count >= 0) + { DOUBLE64_WRITE (buffer [count], (unsigned char*) (buffer + count)) ; + } ; +} /* bd2d_write */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 4ee243b7-8c7a-469b-869c-e9aa0ee3b77f +*/ diff --git a/src/dwd.c b/src/dwd.c new file mode 100644 index 00000000..5db7fbbd --- /dev/null +++ b/src/dwd.c @@ -0,0 +1,208 @@ +/* +** Copyright (C) 2002-2004 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include +#include + +#include "sndfile.h" +#include "sfendian.h" +#include "common.h" + +#if (ENABLE_EXPERIMENTAL_CODE == 0) + +int +dwd_open (SF_PRIVATE *psf) +{ if (psf) + return SFE_UNIMPLEMENTED ; + return 0 ; +} /* dwd_open */ + +#else + +/*------------------------------------------------------------------------------ +** Macros to handle big/little endian issues. +*/ + +#define SFE_DWD_NO_DWD 1666 +#define SFE_DWD_BAND_BIT_WIDTH 1667 +#define SFE_DWD_COMPRESSION 1668 + +#define DWD_IDENTIFIER "DiamondWare Digitized\n\0\x1a" +#define DWD_IDENTIFIER_LEN 24 + +#define DWD_HEADER_LEN 57 + +/*------------------------------------------------------------------------------ +** Typedefs. +*/ + +/*------------------------------------------------------------------------------ +** Private static functions. +*/ + +static int dwd_read_header (SF_PRIVATE *psf) ; + +static int dwd_close (SF_PRIVATE *psf) ; + +/*------------------------------------------------------------------------------ +** Public function. +*/ + +int +dwd_open (SF_PRIVATE *psf) +{ int subformat, error = 0 ; + + if (psf->mode == SFM_READ || (psf->mode == SFM_RDWR && psf->filelength > 0)) + { if ((error = dwd_read_header (psf))) + return error ; + } ; + + if ((psf->sf.format & SF_FORMAT_TYPEMASK) != SF_FORMAT_DWD) + return SFE_BAD_OPEN_FORMAT ; + + subformat = psf->sf.format & SF_FORMAT_SUBMASK ; + + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + { + /*-psf->endian = psf->sf.format & SF_FORMAT_ENDMASK ; + if (CPU_IS_LITTLE_ENDIAN && psf->endian == SF_ENDIAN_CPU) + psf->endian = SF_ENDIAN_LITTLE ; + else if (psf->endian != SF_ENDIAN_LITTLE) + psf->endian = SF_ENDIAN_BIG ; + + if (! (encoding = dwd_write_header (psf, SF_FALSE))) + return psf->error ; + + psf->write_header = dwd_write_header ; + -*/ + } ; + + psf->container_close = dwd_close ; + + /*-psf->blockwidth = psf->bytewidth * psf->sf.channels ;-*/ + + return error ; +} /* dwd_open */ + +/*------------------------------------------------------------------------------ +*/ + +static int +dwd_close (SF_PRIVATE * UNUSED (psf)) +{ + return 0 ; +} /* dwd_close */ + +/* This struct contains all the fields of interest om the DWD header, but does not +** do so in the same order and layout as the actual file, header. +** No assumptions are made about the packing of this struct. +*/ +typedef struct +{ unsigned char major, minor, compression, channels, bitwidth ; + unsigned short srate, maxval ; + unsigned int id, datalen, frames, offset ; +} DWD_HEADER ; + +static int +dwd_read_header (SF_PRIVATE *psf) +{ DWD_HEADER dwdh ; + + memset (psf->u.cbuf, 0, sizeof (psf->u.cbuf)) ; + /* Set position to start of file to begin reading header. */ + psf_binheader_readf (psf, "pb", 0, psf->u.cbuf, DWD_IDENTIFIER_LEN) ; + + if (memcmp (psf->u.cbuf, DWD_IDENTIFIER, DWD_IDENTIFIER_LEN) != 0) + return SFE_DWD_NO_DWD ; + + psf_log_printf (psf, "Read only : DiamondWare Digitized (.dwd)\n", psf->u.cbuf) ; + + psf_binheader_readf (psf, "11", &dwdh.major, &dwdh.minor) ; + psf_binheader_readf (psf, "e4j1", &dwdh.id, 1, &dwdh.compression) ; + psf_binheader_readf (psf, "e211", &dwdh.srate, &dwdh.channels, &dwdh.bitwidth) ; + psf_binheader_readf (psf, "e24", &dwdh.maxval, &dwdh.datalen) ; + psf_binheader_readf (psf, "e44", &dwdh.frames, &dwdh.offset) ; + + psf_log_printf (psf, " Version Major : %d\n Version Minor : %d\n Unique ID : %08X\n", + dwdh.major, dwdh.minor, dwdh.id) ; + psf_log_printf (psf, " Compression : %d => ", dwdh.compression) ; + + if (dwdh.compression != 0) + { psf_log_printf (psf, "Unsupported compression\n") ; + return SFE_DWD_COMPRESSION ; + } + else + psf_log_printf (psf, "None\n") ; + + psf_log_printf (psf, " Sample Rate : %d\n Channels : %d\n" + " Bit Width : %d\n", + dwdh.srate, dwdh.channels, dwdh.bitwidth) ; + + switch (dwdh.bitwidth) + { case 8 : + psf->sf.format = SF_FORMAT_DWD | SF_FORMAT_PCM_S8 ; + psf->bytewidth = 1 ; + break ; + + case 16 : + psf->sf.format = SF_FORMAT_DWD | SF_FORMAT_PCM_16 ; + psf->bytewidth = 2 ; + break ; + + default : + psf_log_printf (psf, "*** Bad bit width %d\n", dwdh.bitwidth) ; + return SFE_DWD_BAND_BIT_WIDTH ; + } ; + + if (psf->filelength != dwdh.offset + dwdh.datalen) + { psf_log_printf (psf, " Data Length : %d (should be %D)\n", dwdh.datalen, psf->filelength - dwdh.offset) ; + dwdh.datalen = (unsigned int) (psf->filelength - dwdh.offset) ; + } + else + psf_log_printf (psf, " Data Length : %d\n", dwdh.datalen) ; + + psf_log_printf (psf, " Max Value : %d\n", dwdh.maxval) ; + psf_log_printf (psf, " Frames : %d\n", dwdh.frames) ; + psf_log_printf (psf, " Data Offset : %d\n", dwdh.offset) ; + + psf->datalength = dwdh.datalen ; + psf->dataoffset = dwdh.offset ; + + psf->endian = SF_ENDIAN_LITTLE ; + + psf->sf.samplerate = dwdh.srate ; + psf->sf.channels = dwdh.channels ; + psf->sf.sections = 1 ; + + return pcm_init (psf) ; +} /* dwd_read_header */ + +/*------------------------------------------------------------------------------ +*/ + +#endif +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: a5e1d2a6-a840-4039-a0e7-e1a43eb05a4f +*/ diff --git a/src/dwvw.c b/src/dwvw.c new file mode 100644 index 00000000..6076276d --- /dev/null +++ b/src/dwvw.c @@ -0,0 +1,669 @@ +/* +** Copyright (C) 2002-2005 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/*=========================================================================== +** Delta Word Variable Width +** +** This decoder and encoder were implemented using information found in this +** document : http://home.swbell.net/rubywand/R011SNDFMTS.TXT +** +** According to the document, the algorithm "was invented 1991 by Magnus +** Lidstrom and is copyright 1993 by NuEdge Development". +*/ + +#include "sfconfig.h" + +#include +#include +#include + +#include "sndfile.h" +#include "sfendian.h" +#include "float_cast.h" +#include "common.h" + +typedef struct +{ int dwm_maxsize, bit_width, max_delta, span ; + int samplecount ; + int bit_count, bits, last_delta_width, last_sample ; + struct + { int index, end ; + unsigned char buffer [256] ; + } b ; +} DWVW_PRIVATE ; + +/*============================================================================================ +*/ + +static sf_count_t dwvw_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ; +static sf_count_t dwvw_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ; +static sf_count_t dwvw_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ; +static sf_count_t dwvw_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ; + +static sf_count_t dwvw_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ; +static sf_count_t dwvw_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ; +static sf_count_t dwvw_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ; +static sf_count_t dwvw_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ; + +static sf_count_t dwvw_seek (SF_PRIVATE *psf, int mode, sf_count_t offset) ; +static int dwvw_close (SF_PRIVATE *psf) ; + +static int dwvw_decode_data (SF_PRIVATE *psf, DWVW_PRIVATE *pdwvw, int *ptr, int len) ; +static int dwvw_decode_load_bits (SF_PRIVATE *psf, DWVW_PRIVATE *pdwvw, int bit_count) ; + +static int dwvw_encode_data (SF_PRIVATE *psf, DWVW_PRIVATE *pdwvw, const int *ptr, int len) ; +static void dwvw_encode_store_bits (SF_PRIVATE *psf, DWVW_PRIVATE *pdwvw, int data, int new_bits) ; +static void dwvw_read_reset (DWVW_PRIVATE *pdwvw) ; + +/*============================================================================================ +** DWVW initialisation function. +*/ + +int +dwvw_init (SF_PRIVATE *psf, int bitwidth) +{ DWVW_PRIVATE *pdwvw ; + + if (psf->codec_data != NULL) + { psf_log_printf (psf, "*** psf->codec_data is not NULL.\n") ; + return SFE_INTERNAL ; + } ; + + if (bitwidth > 24) + return SFE_DWVW_BAD_BITWIDTH ; + + if (psf->mode == SFM_RDWR) + return SFE_BAD_MODE_RW ; + + if ((pdwvw = calloc (1, sizeof (DWVW_PRIVATE))) == NULL) + return SFE_MALLOC_FAILED ; + + psf->codec_data = (void*) pdwvw ; + + pdwvw->bit_width = bitwidth ; + pdwvw->dwm_maxsize = bitwidth / 2 ; + pdwvw->max_delta = 1 << (bitwidth - 1) ; + pdwvw->span = 1 << bitwidth ; + + dwvw_read_reset (pdwvw) ; + + if (psf->mode == SFM_READ) + { psf->read_short = dwvw_read_s ; + psf->read_int = dwvw_read_i ; + psf->read_float = dwvw_read_f ; + psf->read_double = dwvw_read_d ; + } ; + + if (psf->mode == SFM_WRITE) + { psf->write_short = dwvw_write_s ; + psf->write_int = dwvw_write_i ; + psf->write_float = dwvw_write_f ; + psf->write_double = dwvw_write_d ; + } ; + + psf->codec_close = dwvw_close ; + psf->seek = dwvw_seek ; + + /* FIXME : This is bogus. */ + psf->sf.frames = SF_COUNT_MAX ; + psf->datalength = psf->sf.frames ; + /* EMXIF : This is bogus. */ + + return 0 ; +} /* dwvw_init */ + +/*-------------------------------------------------------------------------------------------- +*/ + +static int +dwvw_close (SF_PRIVATE *psf) +{ DWVW_PRIVATE *pdwvw ; + + if (psf->codec_data == NULL) + return 0 ; + pdwvw = (DWVW_PRIVATE*) psf->codec_data ; + + if (psf->mode == SFM_WRITE) + { static int last_values [12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } ; + + /* Write 8 zero samples to fully flush output. */ + dwvw_encode_data (psf, pdwvw, last_values, 12) ; + + /* Write the last buffer worth of data to disk. */ + psf_fwrite (pdwvw->b.buffer, 1, pdwvw->b.index, psf) ; + + if (psf->write_header) + psf->write_header (psf, SF_TRUE) ; + } ; + + return 0 ; +} /* dwvw_close */ + +static sf_count_t +dwvw_seek (SF_PRIVATE *psf, int UNUSED (mode), sf_count_t offset) +{ DWVW_PRIVATE *pdwvw ; + + if (! psf->codec_data) + { psf->error = SFE_INTERNAL ; + return PSF_SEEK_ERROR ; + } ; + + pdwvw = (DWVW_PRIVATE*) psf->codec_data ; + + if (offset == 0) + { psf_fseek (psf, psf->dataoffset, SEEK_SET) ; + dwvw_read_reset (pdwvw) ; + return 0 ; + } ; + + psf->error = SFE_BAD_SEEK ; + return PSF_SEEK_ERROR ; +} /* dwvw_seek */ + + +/*============================================================================== +*/ + +static sf_count_t +dwvw_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len) +{ DWVW_PRIVATE *pdwvw ; + int *iptr ; + int k, bufferlen, readcount = 0, count ; + sf_count_t total = 0 ; + + if (! psf->codec_data) + return 0 ; + pdwvw = (DWVW_PRIVATE*) psf->codec_data ; + + iptr = psf->u.ibuf ; + bufferlen = ARRAY_LEN (psf->u.ibuf) ; + while (len > 0) + { readcount = (len >= bufferlen) ? bufferlen : len ; + count = dwvw_decode_data (psf, pdwvw, iptr, readcount) ; + for (k = 0 ; k < readcount ; k++) + ptr [total + k] = iptr [k] >> 16 ; + + total += count ; + len -= readcount ; + if (count != readcount) + break ; + } ; + + return total ; +} /* dwvw_read_s */ + +static sf_count_t +dwvw_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len) +{ DWVW_PRIVATE *pdwvw ; + int readcount, count ; + sf_count_t total = 0 ; + + if (! psf->codec_data) + return 0 ; + pdwvw = (DWVW_PRIVATE*) psf->codec_data ; + + while (len > 0) + { readcount = (len > 0x10000000) ? 0x10000000 : (int) len ; + + count = dwvw_decode_data (psf, pdwvw, ptr, readcount) ; + + total += count ; + len -= count ; + + if (count != readcount) + break ; + } ; + + return total ; +} /* dwvw_read_i */ + +static sf_count_t +dwvw_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len) +{ DWVW_PRIVATE *pdwvw ; + int *iptr ; + int k, bufferlen, readcount = 0, count ; + sf_count_t total = 0 ; + float normfact ; + + if (! psf->codec_data) + return 0 ; + pdwvw = (DWVW_PRIVATE*) psf->codec_data ; + + normfact = (psf->norm_float == SF_TRUE) ? 1.0 / ((float) 0x80000000) : 1.0 ; + + iptr = psf->u.ibuf ; + bufferlen = ARRAY_LEN (psf->u.ibuf) ; + while (len > 0) + { readcount = (len >= bufferlen) ? bufferlen : len ; + count = dwvw_decode_data (psf, pdwvw, iptr, readcount) ; + for (k = 0 ; k < readcount ; k++) + ptr [total + k] = normfact * (float) (iptr [k]) ; + + total += count ; + len -= readcount ; + if (count != readcount) + break ; + } ; + + return total ; +} /* dwvw_read_f */ + +static sf_count_t +dwvw_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len) +{ DWVW_PRIVATE *pdwvw ; + int *iptr ; + int k, bufferlen, readcount = 0, count ; + sf_count_t total = 0 ; + double normfact ; + + if (! psf->codec_data) + return 0 ; + pdwvw = (DWVW_PRIVATE*) psf->codec_data ; + + normfact = (psf->norm_double == SF_TRUE) ? 1.0 / ((double) 0x80000000) : 1.0 ; + + iptr = psf->u.ibuf ; + bufferlen = ARRAY_LEN (psf->u.ibuf) ; + while (len > 0) + { readcount = (len >= bufferlen) ? bufferlen : len ; + count = dwvw_decode_data (psf, pdwvw, iptr, readcount) ; + for (k = 0 ; k < readcount ; k++) + ptr [total + k] = normfact * (double) (iptr [k]) ; + + total += count ; + len -= readcount ; + if (count != readcount) + break ; + } ; + + return total ; +} /* dwvw_read_d */ + +static int +dwvw_decode_data (SF_PRIVATE *psf, DWVW_PRIVATE *pdwvw, int *ptr, int len) +{ int count ; + int delta_width_modifier, delta_width, delta_negative, delta, sample ; + + /* Restore state from last decode call. */ + delta_width = pdwvw->last_delta_width ; + sample = pdwvw->last_sample ; + + for (count = 0 ; count < len ; count++) + { /* If bit_count parameter is zero get the delta_width_modifier. */ + delta_width_modifier = dwvw_decode_load_bits (psf, pdwvw, -1) ; + + /* Check for end of input bit stream. Break loop if end. */ + if (delta_width_modifier < 0) + break ; + + if (delta_width_modifier && dwvw_decode_load_bits (psf, pdwvw, 1)) + delta_width_modifier = - delta_width_modifier ; + + /* Calculate the current word width. */ + delta_width = (delta_width + delta_width_modifier + pdwvw->bit_width) % pdwvw->bit_width ; + + /* Load the delta. */ + delta = 0 ; + if (delta_width) + { delta = dwvw_decode_load_bits (psf, pdwvw, delta_width - 1) | (1 << (delta_width - 1)) ; + delta_negative = dwvw_decode_load_bits (psf, pdwvw, 1) ; + if (delta == pdwvw->max_delta - 1) + delta += dwvw_decode_load_bits (psf, pdwvw, 1) ; + if (delta_negative) + delta = -delta ; + } ; + + /* Calculate the sample */ + sample += delta ; + + if (sample >= pdwvw->max_delta) + sample -= pdwvw->span ; + else if (sample < - pdwvw->max_delta) + sample += pdwvw->span ; + + /* Store the sample justifying to the most significant bit. */ + ptr [count] = sample << (32 - pdwvw->bit_width) ; + + if (pdwvw->b.end == 0 && pdwvw->bit_count == 0) + break ; + } ; + + pdwvw->last_delta_width = delta_width ; + pdwvw->last_sample = sample ; + + pdwvw->samplecount += count ; + + return count ; +} /* dwvw_decode_data */ + +static int +dwvw_decode_load_bits (SF_PRIVATE *psf, DWVW_PRIVATE *pdwvw, int bit_count) +{ int output = 0, get_dwm = SF_FALSE ; + + /* + ** Depending on the value of parameter bit_count, either get the + ** required number of bits (ie bit_count > 0) or the + ** delta_width_modifier (otherwise). + */ + + if (bit_count < 0) + { get_dwm = SF_TRUE ; + /* modify bit_count to ensure we have enought bits for finding dwm. */ + bit_count = pdwvw->dwm_maxsize ; + } ; + + /* Load bits in bit reseviour. */ + while (pdwvw->bit_count < bit_count) + { if (pdwvw->b.index >= pdwvw->b.end) + { pdwvw->b.end = psf_fread (pdwvw->b.buffer, 1, sizeof (pdwvw->b.buffer), psf) ; + pdwvw->b.index = 0 ; + } ; + + /* Check for end of input stream. */ + if (bit_count < 8 && pdwvw->b.end == 0) + return -1 ; + + pdwvw->bits = (pdwvw->bits << 8) ; + + if (pdwvw->b.index < pdwvw->b.end) + { pdwvw->bits |= pdwvw->b.buffer [pdwvw->b.index] ; + pdwvw->b.index ++ ; + } ; + pdwvw->bit_count += 8 ; + } ; + + /* If asked to get bits do so. */ + if (! get_dwm) + { output = (pdwvw->bits >> (pdwvw->bit_count - bit_count)) & ((1 << bit_count) - 1) ; + pdwvw->bit_count -= bit_count ; + return output ; + } ; + + /* Otherwise must have been asked to get delta_width_modifier. */ + while (output < (pdwvw->dwm_maxsize)) + { pdwvw->bit_count -= 1 ; + if (pdwvw->bits & (1 << pdwvw->bit_count)) + break ; + output += 1 ; + } ; + + return output ; +} /* dwvw_decode_load_bits */ + +static void +dwvw_read_reset (DWVW_PRIVATE *pdwvw) +{ pdwvw->samplecount = 0 ; + pdwvw->b.index = 0 ; + pdwvw->b.end = 0 ; + pdwvw->bit_count = 0 ; + pdwvw->bits = 0 ; + pdwvw->last_delta_width = 0 ; + pdwvw->last_sample = 0 ; +} /* dwvw_read_reset */ + +static void +dwvw_encode_store_bits (SF_PRIVATE *psf, DWVW_PRIVATE *pdwvw, int data, int new_bits) +{ int byte ; + + /* Shift the bits into the resevoir. */ + pdwvw->bits = (pdwvw->bits << new_bits) | (data & ((1 << new_bits) - 1)) ; + pdwvw->bit_count += new_bits ; + + /* Transfer bit to buffer. */ + while (pdwvw->bit_count >= 8) + { byte = pdwvw->bits >> (pdwvw->bit_count - 8) ; + pdwvw->bit_count -= 8 ; + pdwvw->b.buffer [pdwvw->b.index] = byte & 0xFF ; + pdwvw->b.index ++ ; + } ; + + if (pdwvw->b.index > SIGNED_SIZEOF (pdwvw->b.buffer) - 4) + { psf_fwrite (pdwvw->b.buffer, 1, pdwvw->b.index, psf) ; + pdwvw->b.index = 0 ; + } ; + + return ; +} /* dwvw_encode_store_bits */ + +#if 0 +/* Debigging routine. */ +static void +dump_bits (DWVW_PRIVATE *pdwvw) +{ int k, mask ; + + for (k = 0 ; k < 10 && k < pdwvw->b.index ; k++) + { mask = 0x80 ; + while (mask) + { putchar (mask & pdwvw->b.buffer [k] ? '1' : '0') ; + mask >>= 1 ; + } ; + putchar (' ') ; + } + + for (k = pdwvw->bit_count - 1 ; k >= 0 ; k --) + putchar (pdwvw->bits & (1 << k) ? '1' : '0') ; + + putchar ('\n') ; +} /* dump_bits */ +#endif + +#define HIGHEST_BIT(x,count) \ + { int y = x ; \ + (count) = 0 ; \ + while (y) \ + { (count) ++ ; \ + y >>= 1 ; \ + } ; \ + } ; + +static int +dwvw_encode_data (SF_PRIVATE *psf, DWVW_PRIVATE *pdwvw, const int *ptr, int len) +{ int count ; + int delta_width_modifier, delta, delta_negative, delta_width, extra_bit ; + + for (count = 0 ; count < len ; count++) + { delta = (ptr [count] >> (32 - pdwvw->bit_width)) - pdwvw->last_sample ; + + /* Calculate extra_bit if needed. */ + extra_bit = -1 ; + delta_negative = 0 ; + if (delta < -pdwvw->max_delta) + delta = pdwvw->max_delta + (delta % pdwvw->max_delta) ; + else if (delta == -pdwvw->max_delta) + { extra_bit = 1 ; + delta_negative = 1 ; + delta = pdwvw->max_delta - 1 ; + } + else if (delta > pdwvw->max_delta) + { delta_negative = 1 ; + delta = pdwvw->span - delta ; + delta = abs (delta) ; + } + else if (delta == pdwvw->max_delta) + { extra_bit = 1 ; + delta = pdwvw->max_delta - 1 ; + } + else if (delta < 0) + { delta_negative = 1 ; + delta = abs (delta) ; + } ; + + if (delta == pdwvw->max_delta - 1 && extra_bit == -1) + extra_bit = 0 ; + + /* Find width in bits of delta */ + HIGHEST_BIT (delta, delta_width) ; + + /* Calculate the delta_width_modifier */ + delta_width_modifier = (delta_width - pdwvw->last_delta_width) % pdwvw->bit_width ; + if (delta_width_modifier > pdwvw->dwm_maxsize) + delta_width_modifier -= pdwvw->bit_width ; + if (delta_width_modifier < -pdwvw->dwm_maxsize) + delta_width_modifier += pdwvw->bit_width ; + + /* Write delta_width_modifier zeros, followed by terminating '1'. */ + dwvw_encode_store_bits (psf, pdwvw, 0, abs (delta_width_modifier)) ; + if (abs (delta_width_modifier) != pdwvw->dwm_maxsize) + dwvw_encode_store_bits (psf, pdwvw, 1, 1) ; + + /* Write delta_width_modifier sign. */ + if (delta_width_modifier < 0) + dwvw_encode_store_bits (psf, pdwvw, 1, 1) ; + if (delta_width_modifier > 0) + dwvw_encode_store_bits (psf, pdwvw, 0, 1) ; + + /* Write delta and delta sign bit. */ + if (delta_width) + { dwvw_encode_store_bits (psf, pdwvw, delta, abs (delta_width) - 1) ; + dwvw_encode_store_bits (psf, pdwvw, (delta_negative ? 1 : 0), 1) ; + } ; + + /* Write extra bit!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + if (extra_bit >= 0) + dwvw_encode_store_bits (psf, pdwvw, extra_bit, 1) ; + + pdwvw->last_sample = ptr [count] >> (32 - pdwvw->bit_width) ; + pdwvw->last_delta_width = delta_width ; + } ; + + pdwvw->samplecount += count ; + + return count ; +} /* dwvw_encode_data */ + +static sf_count_t +dwvw_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len) +{ DWVW_PRIVATE *pdwvw ; + int *iptr ; + int k, bufferlen, writecount = 0, count ; + sf_count_t total = 0 ; + + if (! psf->codec_data) + return 0 ; + pdwvw = (DWVW_PRIVATE*) psf->codec_data ; + + iptr = psf->u.ibuf ; + bufferlen = ARRAY_LEN (psf->u.ibuf) ; + while (len > 0) + { writecount = (len >= bufferlen) ? bufferlen : len ; + for (k = 0 ; k < writecount ; k++) + iptr [k] = ptr [total + k] << 16 ; + count = dwvw_encode_data (psf, pdwvw, iptr, writecount) ; + + total += count ; + len -= writecount ; + if (count != writecount) + break ; + } ; + + return total ; +} /* dwvw_write_s */ + +static sf_count_t +dwvw_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t len) +{ DWVW_PRIVATE *pdwvw ; + int writecount, count ; + sf_count_t total = 0 ; + + if (! psf->codec_data) + return 0 ; + pdwvw = (DWVW_PRIVATE*) psf->codec_data ; + + while (len > 0) + { writecount = (len > 0x10000000) ? 0x10000000 : (int) len ; + + count = dwvw_encode_data (psf, pdwvw, ptr, writecount) ; + + total += count ; + len -= count ; + + if (count != writecount) + break ; + } ; + + return total ; +} /* dwvw_write_i */ + +static sf_count_t +dwvw_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len) +{ DWVW_PRIVATE *pdwvw ; + int *iptr ; + int k, bufferlen, writecount = 0, count ; + sf_count_t total = 0 ; + float normfact ; + + if (! psf->codec_data) + return 0 ; + pdwvw = (DWVW_PRIVATE*) psf->codec_data ; + + normfact = (psf->norm_float == SF_TRUE) ? (1.0 * 0x7FFFFFFF) : 1.0 ; + + iptr = psf->u.ibuf ; + bufferlen = ARRAY_LEN (psf->u.ibuf) ; + while (len > 0) + { writecount = (len >= bufferlen) ? bufferlen : len ; + for (k = 0 ; k < writecount ; k++) + iptr [k] = lrintf (normfact * ptr [total + k]) ; + count = dwvw_encode_data (psf, pdwvw, iptr, writecount) ; + + total += count ; + len -= writecount ; + if (count != writecount) + break ; + } ; + + return total ; +} /* dwvw_write_f */ + +static sf_count_t +dwvw_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len) +{ DWVW_PRIVATE *pdwvw ; + int *iptr ; + int k, bufferlen, writecount = 0, count ; + sf_count_t total = 0 ; + double normfact ; + + if (! psf->codec_data) + return 0 ; + pdwvw = (DWVW_PRIVATE*) psf->codec_data ; + + normfact = (psf->norm_double == SF_TRUE) ? (1.0 * 0x7FFFFFFF) : 1.0 ; + + iptr = psf->u.ibuf ; + bufferlen = ARRAY_LEN (psf->u.ibuf) ; + while (len > 0) + { writecount = (len >= bufferlen) ? bufferlen : len ; + for (k = 0 ; k < writecount ; k++) + iptr [k] = lrint (normfact * ptr [total + k]) ; + count = dwvw_encode_data (psf, pdwvw, iptr, writecount) ; + + total += count ; + len -= writecount ; + if (count != writecount) + break ; + } ; + + return total ; +} /* dwvw_write_d */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 1ca09552-b01f-4d7f-9bcf-612f834fe41d +*/ diff --git a/src/file_io.c b/src/file_io.c new file mode 100644 index 00000000..123cf93b --- /dev/null +++ b/src/file_io.c @@ -0,0 +1,1537 @@ +/* +** Copyright (C) 2002-2005 Erik de Castro Lopo +** Copyright (C) 2003 Ross Bencina +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* +** The file is split into three sections as follows: +** - The top section (USE_WINDOWS_API == 0) for Linux, Unix and MacOSX +** systems (including Cygwin). +** - The middle section (USE_WINDOWS_API == 1) for microsoft windows +** (including MinGW) using the native windows API. +** - A legacy windows section which attempted to work around grevious +** bugs in microsoft's POSIX implementation. +*/ + +/* +** The header file sfconfig.h MUST be included before the others to ensure +** that large file support is enabled correctly on Unix systems. +*/ + +#include "sfconfig.h" + +#include +#include + +#if HAVE_UNISTD_H +#include +#endif + +#if (HAVE_DECL_S_IRGRP == 0) +#include +#endif + +#include +#include +#include +#include + +#include "sndfile.h" +#include "common.h" + +#define SENSIBLE_SIZE (0x40000000) + +static void psf_log_syserr (SF_PRIVATE *psf, int error) ; + +#if (USE_WINDOWS_API == 0) + +/*------------------------------------------------------------------------------ +** Win32 stuff at the bottom of the file. Unix and other sensible OSes here. +*/ + +static int psf_close_fd (int fd) ; +static int psf_open_fd (const char * path, int mode) ; +static sf_count_t psf_get_filelen_fd (int fd) ; + +int +psf_fopen (SF_PRIVATE *psf, const char *pathname, int open_mode) +{ + psf->error = 0 ; + psf->filedes = psf_open_fd (pathname, open_mode) ; + + if (psf->filedes == - SFE_BAD_OPEN_MODE) + { psf->error = SFE_BAD_OPEN_MODE ; + psf->filedes = -1 ; + return psf->error ; + } ; + + if (psf->filedes == -1) + psf_log_syserr (psf, errno) ; + + psf->mode = open_mode ; + + return psf->error ; +} /* psf_fopen */ + +int +psf_fclose (SF_PRIVATE *psf) +{ int retval ; + + if (psf->virtual_io) + return 0 ; + + if (psf->do_not_close_descriptor) + { psf->filedes = -1 ; + return 0 ; + } ; + + if ((retval = psf_close_fd (psf->filedes)) == -1) + psf_log_syserr (psf, errno) ; + + psf->filedes = -1 ; + + return retval ; +} /* psf_fclose */ + +int +psf_open_rsrc (SF_PRIVATE *psf, int open_mode) +{ + if (psf->rsrcdes > 0) + return 0 ; + + /* Test for MacOSX style resource fork on HPFS or HPFS+ filesystems. */ + LSF_SNPRINTF (psf->rsrcpath, sizeof (psf->rsrcpath), "%s/rsrc", psf->filepath) ; + psf->error = SFE_NO_ERROR ; + if ((psf->rsrcdes = psf_open_fd (psf->rsrcpath, open_mode)) >= 0) + { psf->rsrclength = psf_get_filelen_fd (psf->rsrcdes) ; + if (psf->rsrclength > 0 || (open_mode & SFM_WRITE)) + return SFE_NO_ERROR ; + psf_close_fd (psf->rsrcdes) ; + psf->rsrcdes = -1 ; + } ; + + if (psf->rsrcdes == - SFE_BAD_OPEN_MODE) + { psf->error = SFE_BAD_OPEN_MODE ; + return psf->error ; + } ; + + /* + ** Now try for a resource fork stored as a separate file in the same + ** directory, but preceded with a dot underscore. + */ + LSF_SNPRINTF (psf->rsrcpath, sizeof (psf->rsrcpath), "%s._%s", psf->directory, psf->filename) ; + psf->error = SFE_NO_ERROR ; + if ((psf->rsrcdes = psf_open_fd (psf->rsrcpath, open_mode)) >= 0) + { psf->rsrclength = psf_get_filelen_fd (psf->rsrcdes) ; + return SFE_NO_ERROR ; + } ; + + /* + ** Now try for a resource fork stored in a separate file in the + ** .AppleDouble/ directory. + */ + LSF_SNPRINTF (psf->rsrcpath, sizeof (psf->rsrcpath), "%s.AppleDouble/%s", psf->directory, psf->filename) ; + psf->error = SFE_NO_ERROR ; + if ((psf->rsrcdes = psf_open_fd (psf->rsrcpath, open_mode)) >= 0) + { psf->rsrclength = psf_get_filelen_fd (psf->rsrcdes) ; + return SFE_NO_ERROR ; + } ; + + /* No resource file found. */ + if (psf->rsrcdes == -1) + psf_log_syserr (psf, errno) ; + + psf->rsrcdes = -1 ; + + return psf->error ; +} /* psf_open_rsrc */ + +sf_count_t +psf_get_filelen (SF_PRIVATE *psf) +{ sf_count_t filelen ; + + if (psf->virtual_io) + return psf->vio.get_filelen (psf->vio_user_data) ; + + filelen = psf_get_filelen_fd (psf->filedes) ; + + if (filelen == -1) + { psf_log_syserr (psf, errno) ; + return (sf_count_t) -1 ; + } ; + + if (filelen == -SFE_BAD_STAT_SIZE) + { psf->error = SFE_BAD_STAT_SIZE ; + return (sf_count_t) -1 ; + } ; + + switch (psf->mode) + { case SFM_WRITE : + filelen = filelen - psf->fileoffset ; + break ; + + case SFM_READ : + if (psf->fileoffset > 0 && psf->filelength > 0) + filelen = psf->filelength ; + break ; + + case SFM_RDWR : + /* + ** Cannot open embedded files SFM_RDWR so we don't need to + ** subtract psf->fileoffset. We already have the answer we + ** need. + */ + break ; + + default : + /* Shouldn't be here, so return error. */ + filelen = -1 ; + } ; + + return filelen ; +} /* psf_get_filelen */ + +int +psf_close_rsrc (SF_PRIVATE *psf) +{ + if (psf->rsrcdes >= 0) + psf_close_fd (psf->rsrcdes) ; + psf->rsrcdes = -1 ; + return 0 ; +} /* psf_close_rsrc */ + +int +psf_set_stdio (SF_PRIVATE *psf, int mode) +{ int error = 0 ; + + switch (mode) + { case SFM_RDWR : + error = SFE_OPEN_PIPE_RDWR ; + break ; + + case SFM_READ : + psf->filedes = 0 ; + break ; + + case SFM_WRITE : + psf->filedes = 1 ; + break ; + + default : + error = SFE_BAD_OPEN_MODE ; + break ; + } ; + psf->filelength = 0 ; + + return error ; +} /* psf_set_stdio */ + +void +psf_set_file (SF_PRIVATE *psf, int fd) +{ psf->filedes = fd ; +} /* psf_set_file */ + +int +psf_file_valid (SF_PRIVATE *psf) +{ return (psf->filedes >= 0) ? SF_TRUE : SF_FALSE ; +} /* psf_set_file */ + +sf_count_t +psf_fseek (SF_PRIVATE *psf, sf_count_t offset, int whence) +{ sf_count_t new_position ; + + if (psf->virtual_io) + return psf->vio.seek (offset, whence, psf->vio_user_data) ; + + switch (whence) + { case SEEK_SET : + offset += psf->fileoffset ; + break ; + + case SEEK_END : + if (psf->mode == SFM_WRITE) + { new_position = lseek (psf->filedes, offset, whence) ; + + if (new_position < 0) + psf_log_syserr (psf, errno) ; + + return new_position - psf->fileoffset ; + } ; + + /* Transform SEEK_END into a SEEK_SET, ie find the file + ** length add the requested offset (should be <= 0) to + ** get the offset wrt the start of file. + */ + whence = SEEK_SET ; + offset = lseek (psf->filedes, 0, SEEK_END) + offset ; + break ; + + default : + /* No need to do anything about SEEK_CUR. */ + break ; + } ; + + new_position = lseek (psf->filedes, offset, whence) ; + + if (new_position < 0) + psf_log_syserr (psf, errno) ; + + new_position -= psf->fileoffset ; + + return new_position ; +} /* psf_fseek */ + +sf_count_t +psf_fread (void *ptr, sf_count_t bytes, sf_count_t items, SF_PRIVATE *psf) +{ sf_count_t total = 0 ; + ssize_t count ; + + if (psf->virtual_io) + return psf->vio.read (ptr, bytes*items, psf->vio_user_data) / bytes ; + + items *= bytes ; + + /* Do this check after the multiplication above. */ + if (items <= 0) + return 0 ; + + while (items > 0) + { /* Break the read down to a sensible size. */ + count = (items > SENSIBLE_SIZE) ? SENSIBLE_SIZE : (ssize_t) items ; + + count = read (psf->filedes, ((char*) ptr) + total, (size_t) count) ; + + if (count == -1) + { if (errno == EINTR) + continue ; + + psf_log_syserr (psf, errno) ; + break ; + } ; + + if (count == 0) + break ; + + total += count ; + items -= count ; + } ; + + if (psf->is_pipe) + psf->pipeoffset += total ; + + return total / bytes ; +} /* psf_fread */ + +sf_count_t +psf_fwrite (const void *ptr, sf_count_t bytes, sf_count_t items, SF_PRIVATE *psf) +{ sf_count_t total = 0 ; + ssize_t count ; + + if (psf->virtual_io) + return psf->vio.write (ptr, bytes*items, psf->vio_user_data) / bytes ; + + items *= bytes ; + + /* Do this check after the multiplication above. */ + if (items <= 0) + return 0 ; + + while (items > 0) + { /* Break the writes down to a sensible size. */ + count = (items > SENSIBLE_SIZE) ? SENSIBLE_SIZE : items ; + + count = write (psf->filedes, ((const char*) ptr) + total, count) ; + + if (count == -1) + { if (errno == EINTR) + continue ; + + psf_log_syserr (psf, errno) ; + break ; + } ; + + if (count == 0) + break ; + + total += count ; + items -= count ; + } ; + + if (psf->is_pipe) + psf->pipeoffset += total ; + + return total / bytes ; +} /* psf_fwrite */ + +sf_count_t +psf_ftell (SF_PRIVATE *psf) +{ sf_count_t pos ; + + if (psf->virtual_io) + return psf->vio.tell (psf->vio_user_data) ; + + if (psf->is_pipe) + return psf->pipeoffset ; + + pos = lseek (psf->filedes, 0, SEEK_CUR) ; + + if (pos == ((sf_count_t) -1)) + { psf_log_syserr (psf, errno) ; + return -1 ; + } ; + + return pos - psf->fileoffset ; +} /* psf_ftell */ + +static int +psf_close_fd (int fd) +{ int retval ; + + while ((retval = close (fd)) == -1 && errno == EINTR) + /* Do nothing. */ ; + + return retval ; +} /* psf_close_fd */ + +sf_count_t +psf_fgets (char *buffer, sf_count_t bufsize, SF_PRIVATE *psf) +{ sf_count_t k = 0 ; + sf_count_t count ; + + while (k < bufsize - 1) + { count = read (psf->filedes, &(buffer [k]), 1) ; + + if (count == -1) + { if (errno == EINTR) + continue ; + + psf_log_syserr (psf, errno) ; + break ; + } ; + + if (count == 0 || buffer [k++] == '\n') + break ; + } ; + + buffer [k] = 0 ; + + return k ; +} /* psf_fgets */ + +int +psf_is_pipe (SF_PRIVATE *psf) +{ struct stat statbuf ; + + if (psf->virtual_io) + return SF_FALSE ; + + if (fstat (psf->filedes, &statbuf) == -1) + { psf_log_syserr (psf, errno) ; + /* Default to maximum safety. */ + return SF_TRUE ; + } ; + + if (S_ISFIFO (statbuf.st_mode) || S_ISSOCK (statbuf.st_mode)) + return SF_TRUE ; + + return SF_FALSE ; +} /* psf_is_pipe */ + +static sf_count_t +psf_get_filelen_fd (int fd) +{ struct stat statbuf ; + + /* + ** Sanity check. + ** If everything is OK, this will be optimised out. + */ + if (sizeof (statbuf.st_size) == 4 && sizeof (sf_count_t) == 8) + return (sf_count_t) -SFE_BAD_STAT_SIZE ; + + if (fstat (fd, &statbuf) == -1) + return (sf_count_t) -1 ; + + return statbuf.st_size ; +} /* psf_get_filelen_fd */ + +int +psf_ftruncate (SF_PRIVATE *psf, sf_count_t len) +{ int retval ; + + /* Returns 0 on success, non-zero on failure. */ + if (len < 0) + return -1 ; + + if ((sizeof (off_t) < sizeof (sf_count_t)) && len > 0x7FFFFFFF) + return -1 ; + + retval = ftruncate (psf->filedes, len) ; + + if (retval == -1) + psf_log_syserr (psf, errno) ; + + return retval ; +} /* psf_ftruncate */ + +void +psf_init_files (SF_PRIVATE *psf) +{ psf->filedes = -1 ; + psf->rsrcdes = -1 ; + psf->savedes = -1 ; +} /* psf_init_files */ + +void +psf_use_rsrc (SF_PRIVATE *psf, int on_off) +{ + if (on_off) + { if (psf->filedes != psf->rsrcdes) + { psf->savedes = psf->filedes ; + psf->filedes = psf->rsrcdes ; + } ; + } + else if (psf->filedes == psf->rsrcdes) + psf->filedes = psf->savedes ; + + return ; +} /* psf_use_rsrc */ + +static int +psf_open_fd (const char * pathname, int open_mode) +{ int fd, oflag, mode ; + + /* + ** Sanity check. If everything is OK, this test and the printfs will + ** be optimised out. This is meant to catch the problems caused by + ** "sfconfig.h" being included after . + */ + if (sizeof (off_t) != sizeof (sf_count_t)) + { puts ("\n\n*** Fatal error : sizeof (off_t) != sizeof (sf_count_t)") ; + puts ("*** This means that libsndfile was not configured correctly.\n") ; + exit (1) ; + } ; + + switch (open_mode) + { case SFM_READ : + oflag = O_RDONLY ; + mode = 0 ; + break ; + + case SFM_WRITE : + oflag = O_WRONLY | O_CREAT | O_TRUNC ; + mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ; + break ; + + case SFM_RDWR : + oflag = O_RDWR | O_CREAT ; + mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ; + break ; + + default : + return - SFE_BAD_OPEN_MODE ; + break ; + } ; + +#if OS_IS_WIN32 + /* For Cygwin. */ + oflag |= O_BINARY ; +#endif + + if (mode == 0) + fd = open (pathname, oflag) ; + else + fd = open (pathname, oflag, mode) ; + + return fd ; +} /* psf_open_fd */ + +static void +psf_log_syserr (SF_PRIVATE *psf, int error) +{ + /* Only log an error if no error has been set yet. */ + if (psf->error == 0) + { psf->error = SFE_SYSTEM ; + LSF_SNPRINTF (psf->syserr, sizeof (psf->syserr), "System error : %s.", strerror (error)) ; + } ; + + return ; +} /* psf_log_syserr */ + +void +psf_fsync (SF_PRIVATE *psf) +{ +#if HAVE_FSYNC + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + fsync (psf->filedes) ; +#else + psf = NULL ; +#endif +} /* psf_fsync */ + +#elif USE_WINDOWS_API + +/* Win32 file i/o functions implemented using native Win32 API */ + +#include +#include + +#ifndef HAVE_SSIZE_T +typedef long ssize_t ; +#endif + +static int psf_close_handle (HANDLE handle) ; +static HANDLE psf_open_handle (const char * path, int mode) ; +static sf_count_t psf_get_filelen_handle (HANDLE handle) ; + +/* USE_WINDOWS_API */ int +psf_fopen (SF_PRIVATE *psf, const char *pathname, int open_mode) +{ + psf->error = 0 ; + psf->hfile = psf_open_handle (pathname, open_mode) ; + + if (psf->hfile == NULL) + psf_log_syserr (psf, GetLastError ()) ; + + psf->mode = open_mode ; + + return psf->error ; +} /* psf_fopen */ + +/* USE_WINDOWS_API */ int +psf_fclose (SF_PRIVATE *psf) +{ int retval ; + + if (psf->virtual_io) + return 0 ; + + if (psf->do_not_close_descriptor) + { psf->hfile = NULL ; + return 0 ; + } ; + + if ((retval = psf_close_handle (psf->hfile)) == -1) + psf_log_syserr (psf, GetLastError ()) ; + + psf->hfile = NULL ; + + return retval ; +} /* psf_fclose */ + +/* USE_WINDOWS_API */ int +psf_open_rsrc (SF_PRIVATE *psf, int open_mode) +{ + if (psf->hrsrc != NULL) + return 0 ; + + /* Test for MacOSX style resource fork on HPFS or HPFS+ filesystems. */ + LSF_SNPRINTF (psf->rsrcpath, sizeof (psf->rsrcpath), "%s/rsrc", psf->filepath) ; + psf->error = SFE_NO_ERROR ; + if ((psf->hrsrc = psf_open_handle (psf->rsrcpath, open_mode)) != NULL) + { psf->rsrclength = psf_get_filelen_handle (psf->hrsrc) ; + return SFE_NO_ERROR ; + } ; + + /* + ** Now try for a resource fork stored as a separate file in the same + ** directory, but preceded with a dot underscore. + */ + LSF_SNPRINTF (psf->rsrcpath, sizeof (psf->rsrcpath), "%s._%s", psf->directory, psf->filename) ; + psf->error = SFE_NO_ERROR ; + if ((psf->hrsrc = psf_open_handle (psf->rsrcpath, open_mode)) != NULL) + { psf->rsrclength = psf_get_filelen_handle (psf->hrsrc) ; + return SFE_NO_ERROR ; + } ; + + /* + ** Now try for a resource fork stored in a separate file in the + ** .AppleDouble/ directory. + */ + LSF_SNPRINTF (psf->rsrcpath, sizeof (psf->rsrcpath), "%s.AppleDouble/%s", psf->directory, psf->filename) ; + psf->error = SFE_NO_ERROR ; + if ((psf->hrsrc = psf_open_handle (psf->rsrcpath, open_mode)) != NULL) + { psf->rsrclength = psf_get_filelen_handle (psf->hrsrc) ; + return SFE_NO_ERROR ; + } ; + + /* No resource file found. */ + if (psf->hrsrc == NULL) + psf_log_syserr (psf, GetLastError ()) ; + + psf->hrsrc = NULL ; + + return psf->error ; +} /* psf_open_rsrc */ + +/* USE_WINDOWS_API */ sf_count_t +psf_get_filelen (SF_PRIVATE *psf) +{ sf_count_t filelen ; + + if (psf->virtual_io) + return psf->vio.get_filelen (psf->vio_user_data) ; + + filelen = psf_get_filelen_handle (psf->hfile) ; + + if (filelen == -1) + { psf_log_syserr (psf, errno) ; + return (sf_count_t) -1 ; + } ; + + if (filelen == -SFE_BAD_STAT_SIZE) + { psf->error = SFE_BAD_STAT_SIZE ; + return (sf_count_t) -1 ; + } ; + + switch (psf->mode) + { case SFM_WRITE : + filelen = filelen - psf->fileoffset ; + break ; + + case SFM_READ : + if (psf->fileoffset > 0 && psf->filelength > 0) + filelen = psf->filelength ; + break ; + + case SFM_RDWR : + /* + ** Cannot open embedded files SFM_RDWR so we don't need to + ** subtract psf->fileoffset. We already have the answer we + ** need. + */ + break ; + + default : + /* Shouldn't be here, so return error. */ + filelen = -1 ; + } ; + + return filelen ; +} /* psf_get_filelen */ + +/* USE_WINDOWS_API */ void +psf_init_files (SF_PRIVATE *psf) +{ psf->hfile = NULL ; + psf->hrsrc = NULL ; + psf->hsaved = NULL ; +} /* psf_init_files */ + +/* USE_WINDOWS_API */ void +psf_use_rsrc (SF_PRIVATE *psf, int on_off) +{ + if (on_off) + { if (psf->hfile != psf->hrsrc) + { psf->hsaved = psf->hfile ; + psf->hfile = psf->hrsrc ; + } ; + } + else if (psf->hfile == psf->hrsrc) + psf->hfile = psf->hsaved ; + + return ; +} /* psf_use_rsrc */ + +/* USE_WINDOWS_API */ static HANDLE +psf_open_handle (const char * pathname, int open_mode) +{ DWORD dwDesiredAccess ; + DWORD dwShareMode ; + DWORD dwCreationDistribution ; + HANDLE handle ; + + switch (open_mode) + { case SFM_READ : + dwDesiredAccess = GENERIC_READ ; + dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE ; + dwCreationDistribution = OPEN_EXISTING ; + break ; + + case SFM_WRITE : + dwDesiredAccess = GENERIC_WRITE ; + dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE ; + dwCreationDistribution = CREATE_ALWAYS ; + break ; + + case SFM_RDWR : + dwDesiredAccess = GENERIC_READ | GENERIC_WRITE ; + dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE ; + dwCreationDistribution = OPEN_ALWAYS ; + break ; + + default : + return NULL ; + } ; + + handle = CreateFile ( + pathname, /* pointer to name of the file */ + dwDesiredAccess, /* access (read-write) mode */ + dwShareMode, /* share mode */ + 0, /* pointer to security attributes */ + dwCreationDistribution, /* how to create */ + FILE_ATTRIBUTE_NORMAL, /* file attributes (could use FILE_FLAG_SEQUENTIAL_SCAN) */ + NULL /* handle to file with attributes to copy */ + ) ; + + if (handle == INVALID_HANDLE_VALUE) + return NULL ; + + return handle ; +} /* psf_open_handle */ + +/* USE_WINDOWS_API */ static void +psf_log_syserr (SF_PRIVATE *psf, int error) +{ LPVOID lpMsgBuf ; + + /* Only log an error if no error has been set yet. */ + if (psf->error == 0) + { psf->error = SFE_SYSTEM ; + + FormatMessage ( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + error, + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &lpMsgBuf, + 0, + NULL + ) ; + + LSF_SNPRINTF (psf->syserr, sizeof (psf->syserr), "System error : %s", lpMsgBuf) ; + LocalFree (lpMsgBuf) ; + } ; + + return ; +} /* psf_log_syserr */ + + +/* USE_WINDOWS_API */ int +psf_close_rsrc (SF_PRIVATE *psf) +{ + if (psf->hrsrc != NULL) + psf_close_handle (psf->hrsrc) ; + psf->hrsrc = NULL ; + return 0 ; +} /* psf_close_rsrc */ + + +/* USE_WINDOWS_API */ int +psf_set_stdio (SF_PRIVATE *psf, int mode) +{ HANDLE handle = NULL ; + int error = 0 ; + + switch (mode) + { case SFM_RDWR : + error = SFE_OPEN_PIPE_RDWR ; + break ; + + case SFM_READ : + handle = GetStdHandle (STD_INPUT_HANDLE) ; + psf->do_not_close_descriptor = 1 ; + break ; + + case SFM_WRITE : + handle = GetStdHandle (STD_OUTPUT_HANDLE) ; + psf->do_not_close_descriptor = 1 ; + break ; + + default : + error = SFE_BAD_OPEN_MODE ; + break ; + } ; + + psf->hfile = handle ; + psf->filelength = 0 ; + + return error ; +} /* psf_set_stdio */ + +/* USE_WINDOWS_API */ void +psf_set_file (SF_PRIVATE *psf, int fd) +{ HANDLE handle ; + long osfhandle ; + + osfhandle = _get_osfhandle (fd) ; + handle = (HANDLE) osfhandle ; + + psf->hfile = handle ; +} /* psf_set_file */ + +/* USE_WINDOWS_API */ int +psf_file_valid (SF_PRIVATE *psf) +{ if (psf->hfile == NULL) + return SF_FALSE ; + if (psf->hfile == INVALID_HANDLE_VALUE) + return SF_FALSE ; + return SF_TRUE ; +} /* psf_set_file */ + +/* USE_WINDOWS_API */ sf_count_t +psf_fseek (SF_PRIVATE *psf, sf_count_t offset, int whence) +{ sf_count_t new_position ; + LONG lDistanceToMove, lDistanceToMoveHigh ; + DWORD dwMoveMethod ; + DWORD dwResult, dwError ; + + if (psf->virtual_io) + return psf->vio.seek (offset, whence, psf->vio_user_data) ; + + switch (whence) + { case SEEK_SET : + offset += psf->fileoffset ; + dwMoveMethod = FILE_BEGIN ; + break ; + + case SEEK_END : + dwMoveMethod = FILE_END ; + break ; + + default : + dwMoveMethod = FILE_CURRENT ; + break ; + } ; + + lDistanceToMove = (DWORD) (offset & 0xFFFFFFFF) ; + lDistanceToMoveHigh = (DWORD) ((offset >> 32) & 0xFFFFFFFF) ; + + dwResult = SetFilePointer (psf->hfile, lDistanceToMove, &lDistanceToMoveHigh, dwMoveMethod) ; + + if (dwResult == 0xFFFFFFFF) + dwError = GetLastError () ; + else + dwError = NO_ERROR ; + + if (dwError != NO_ERROR) + { psf_log_syserr (psf, dwError) ; + return -1 ; + } ; + + new_position = (dwResult + ((__int64) lDistanceToMoveHigh << 32)) - psf->fileoffset ; + + return new_position ; +} /* psf_fseek */ + +/* USE_WINDOWS_API */ sf_count_t +psf_fread (void *ptr, sf_count_t bytes, sf_count_t items, SF_PRIVATE *psf) +{ sf_count_t total = 0 ; + ssize_t count ; + DWORD dwNumberOfBytesRead ; + + if (psf->virtual_io) + return psf->vio.read (ptr, bytes*items, psf->vio_user_data) / bytes ; + + items *= bytes ; + + /* Do this check after the multiplication above. */ + if (items <= 0) + return 0 ; + + while (items > 0) + { /* Break the writes down to a sensible size. */ + count = (items > SENSIBLE_SIZE) ? SENSIBLE_SIZE : (ssize_t) items ; + + if (ReadFile (psf->hfile, ((char*) ptr) + total, count, &dwNumberOfBytesRead, 0) == 0) + { psf_log_syserr (psf, GetLastError ()) ; + break ; + } + else + count = dwNumberOfBytesRead ; + + if (count == 0) + break ; + + total += count ; + items -= count ; + } ; + + if (psf->is_pipe) + psf->pipeoffset += total ; + + return total / bytes ; +} /* psf_fread */ + +/* USE_WINDOWS_API */ sf_count_t +psf_fwrite (const void *ptr, sf_count_t bytes, sf_count_t items, SF_PRIVATE *psf) +{ sf_count_t total = 0 ; + ssize_t count ; + DWORD dwNumberOfBytesWritten ; + + if (psf->virtual_io) + return psf->vio.write (ptr, bytes * items, psf->vio_user_data) / bytes ; + + items *= bytes ; + + /* Do this check after the multiplication above. */ + if (items <= 0) + return 0 ; + + while (items > 0) + { /* Break the writes down to a sensible size. */ + count = (items > SENSIBLE_SIZE) ? SENSIBLE_SIZE : (ssize_t) items ; + + if (WriteFile (psf->hfile, ((const char*) ptr) + total, count, &dwNumberOfBytesWritten, 0) == 0) + { psf_log_syserr (psf, GetLastError ()) ; + break ; + } + else + count = dwNumberOfBytesWritten ; + + if (count == 0) + break ; + + total += count ; + items -= count ; + } ; + + if (psf->is_pipe) + psf->pipeoffset += total ; + + return total / bytes ; +} /* psf_fwrite */ + +/* USE_WINDOWS_API */ sf_count_t +psf_ftell (SF_PRIVATE *psf) +{ sf_count_t pos ; + LONG lDistanceToMoveLow, lDistanceToMoveHigh ; + DWORD dwResult, dwError ; + + if (psf->virtual_io) + return psf->vio.tell (psf->vio_user_data) ; + + if (psf->is_pipe) + return psf->pipeoffset ; + + lDistanceToMoveLow = 0 ; + lDistanceToMoveHigh = 0 ; + + dwResult = SetFilePointer (psf->hfile, lDistanceToMoveLow, &lDistanceToMoveHigh, FILE_CURRENT) ; + + if (dwResult == 0xFFFFFFFF) + dwError = GetLastError () ; + else + dwError = NO_ERROR ; + + if (dwError != NO_ERROR) + { psf_log_syserr (psf, dwError) ; + return -1 ; + } ; + + pos = (dwResult + ((__int64) lDistanceToMoveHigh << 32)) ; + + return pos - psf->fileoffset ; +} /* psf_ftell */ + +/* USE_WINDOWS_API */ static int +psf_close_handle (HANDLE handle) +{ if (CloseHandle (handle) == 0) + return -1 ; + + return 0 ; +} /* psf_close_handle */ + +/* USE_WINDOWS_API */ sf_count_t +psf_fgets (char *buffer, sf_count_t bufsize, SF_PRIVATE *psf) +{ sf_count_t k = 0 ; + sf_count_t count ; + DWORD dwNumberOfBytesRead ; + + while (k < bufsize - 1) + { if (ReadFile (psf->hfile, &(buffer [k]), 1, &dwNumberOfBytesRead, 0) == 0) + { psf_log_syserr (psf, GetLastError ()) ; + break ; + } + else + { count = dwNumberOfBytesRead ; + /* note that we only check for '\n' not other line endings such as CRLF */ + if (count == 0 || buffer [k++] == '\n') + break ; + } ; + } ; + + buffer [k] = 0 ; + + return k ; +} /* psf_fgets */ + +/* USE_WINDOWS_API */ int +psf_is_pipe (SF_PRIVATE *psf) +{ + if (psf->virtual_io) + return SF_FALSE ; + + if (GetFileType (psf->hfile) == FILE_TYPE_DISK) + return SF_FALSE ; + + /* Default to maximum safety. */ + return SF_TRUE ; +} /* psf_is_pipe */ + +/* USE_WINDOWS_API */ sf_count_t +psf_get_filelen_handle (HANDLE handle) +{ sf_count_t filelen ; + DWORD dwFileSizeLow, dwFileSizeHigh, dwError = NO_ERROR ; + + dwFileSizeLow = GetFileSize (handle, &dwFileSizeHigh) ; + + if (dwFileSizeLow == 0xFFFFFFFF) + dwError = GetLastError () ; + + if (dwError != NO_ERROR) + return (sf_count_t) -1 ; + + filelen = dwFileSizeLow + ((__int64) dwFileSizeHigh << 32) ; + + return filelen ; +} /* psf_get_filelen_handle */ + +/* USE_WINDOWS_API */ void +psf_fsync (SF_PRIVATE *psf) +{ FlushFileBuffers (psf->hfile) ; +} /* psf_fsync */ + + +/* USE_WINDOWS_API */ int +psf_ftruncate (SF_PRIVATE *psf, sf_count_t len) +{ int retval = 0 ; + LONG lDistanceToMoveLow, lDistanceToMoveHigh ; + DWORD dwResult, dwError = NO_ERROR ; + + /* This implementation trashes the current file position. + ** should it save and restore it? what if the current position is past + ** the new end of file? + */ + + /* Returns 0 on success, non-zero on failure. */ + if (len < 0) + return 1 ; + + lDistanceToMoveLow = (DWORD) (len & 0xFFFFFFFF) ; + lDistanceToMoveHigh = (DWORD) ((len >> 32) & 0xFFFFFFFF) ; + + dwResult = SetFilePointer (psf->hfile, lDistanceToMoveLow, &lDistanceToMoveHigh, FILE_BEGIN) ; + + if (dwResult == 0xFFFFFFFF) + dwError = GetLastError () ; + + if (dwError != NO_ERROR) + { retval = -1 ; + psf_log_syserr (psf, dwError) ; + } + else + { /* Note: when SetEndOfFile is used to extend a file, the contents of the + ** new portion of the file is undefined. This is unlike chsize(), + ** which guarantees that the new portion of the file will be zeroed. + ** Not sure if this is important or not. + */ + if (SetEndOfFile (psf->hfile) == 0) + { retval = -1 ; + psf_log_syserr (psf, GetLastError ()) ; + } ; + } ; + + return retval ; +} /* psf_ftruncate */ + + +#else +/* Win32 file i/o functions implemented using Unix-style file i/o API */ + +/* Win32 has a 64 file offset seek function: +** +** __int64 _lseeki64 (int handle, __int64 offset, int origin) ; +** +** It also has a 64 bit fstat function: +** +** int fstati64 (int, struct _stati64) ; +** +** but the fscking thing doesn't work!!!!! The file size parameter returned +** by this function is only valid up until more data is written at the end of +** the file. That makes this function completely 100% useless. +*/ + +#include +#include + +#ifndef HAVE_SSIZE_T +typedef long ssize_t ; +#endif + +/* Win32 */ int +psf_fopen (SF_PRIVATE *psf, const char *pathname, int open_mode) +{ int oflag, mode ; + + switch (open_mode) + { case SFM_READ : + oflag = O_RDONLY | O_BINARY ; + mode = 0 ; + break ; + + case SFM_WRITE : + oflag = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY ; + mode = S_IRUSR | S_IWUSR | S_IRGRP ; + break ; + + case SFM_RDWR : + oflag = O_RDWR | O_CREAT | O_BINARY ; + mode = S_IRUSR | S_IWUSR | S_IRGRP ; + break ; + + default : + psf->error = SFE_BAD_OPEN_MODE ; + return -1 ; + break ; + } ; + + if (mode == 0) + psf->filedes = open (pathname, oflag) ; + else + psf->filedes = open (pathname, oflag, mode) ; + + if (psf->filedes == -1) + psf_log_syserr (psf, errno) ; + + return psf->filedes ; +} /* psf_fopen */ + +/* Win32 */ sf_count_t +psf_fseek (SF_PRIVATE *psf, sf_count_t offset, int whence) +{ sf_count_t new_position ; + + if (psf->virtual_io) + return psf->vio.seek (offset, whence, psf->vio_user_data) ; + + switch (whence) + { case SEEK_SET : + offset += psf->fileoffset ; + break ; + + case SEEK_END : + if (psf->mode == SFM_WRITE) + { new_position = _lseeki64 (psf->filedes, offset, whence) ; + + if (new_position < 0) + psf_log_syserr (psf, errno) ; + + return new_position - psf->fileoffset ; + } ; + + /* Transform SEEK_END into a SEEK_SET, ie find the file + ** length add the requested offset (should be <= 0) to + ** get the offset wrt the start of file. + */ + whence = SEEK_SET ; + offset = _lseeki64 (psf->filedes, 0, SEEK_END) + offset ; + break ; + + default : + /* No need to do anything about SEEK_CUR. */ + break ; + } ; + + /* + ** Bypass weird Win32-ism if necessary. + ** _lseeki64() returns an "invalid parameter" error if called with the + ** offset == 0 and whence == SEEK_CUR. + *** Use the _telli64() function instead. + */ + if (offset == 0 && whence == SEEK_CUR) + new_position = _telli64 (psf->filedes) ; + else + new_position = _lseeki64 (psf->filedes, offset, whence) ; + + if (new_position < 0) + psf_log_syserr (psf, errno) ; + + new_position -= psf->fileoffset ; + + return new_position ; +} /* psf_fseek */ + +/* Win32 */ sf_count_t +psf_fread (void *ptr, sf_count_t bytes, sf_count_t items, SF_PRIVATE *psf) +{ sf_count_t total = 0 ; + ssize_t count ; + + if (psf->virtual_io) + return psf->vio.read (ptr, bytes*items, psf->vio_user_data) / bytes ; + + items *= bytes ; + + /* Do this check after the multiplication above. */ + if (items <= 0) + return 0 ; + + while (items > 0) + { /* Break the writes down to a sensible size. */ + count = (items > SENSIBLE_SIZE) ? SENSIBLE_SIZE : (ssize_t) items ; + + count = read (psf->filedes, ((char*) ptr) + total, (size_t) count) ; + + if (count == -1) + { if (errno == EINTR) + continue ; + + psf_log_syserr (psf, errno) ; + break ; + } ; + + if (count == 0) + break ; + + total += count ; + items -= count ; + } ; + + return total / bytes ; +} /* psf_fread */ + +/* Win32 */ sf_count_t +psf_fwrite (const void *ptr, sf_count_t bytes, sf_count_t items, SF_PRIVATE *psf) +{ sf_count_t total = 0 ; + ssize_t count ; + + if (psf->virtual_io) + return psf->vio.write (ptr, bytes*items, psf->vio_user_data) / bytes ; + + items *= bytes ; + + /* Do this check after the multiplication above. */ + if (items <= 0) + return 0 ; + + while (items > 0) + { /* Break the writes down to a sensible size. */ + count = (items > SENSIBLE_SIZE) ? SENSIBLE_SIZE : items ; + + count = write (psf->filedes, ((const char*) ptr) + total, count) ; + + if (count == -1) + { if (errno == EINTR) + continue ; + + psf_log_syserr (psf, errno) ; + break ; + } ; + + if (count == 0) + break ; + + total += count ; + items -= count ; + } ; + + return total / bytes ; +} /* psf_fwrite */ + +/* Win32 */ sf_count_t +psf_ftell (SF_PRIVATE *psf) +{ sf_count_t pos ; + + if (psf->virtual_io) + return psf->vio.tell (psf->vio_user_data) ; + + pos = _telli64 (psf->filedes) ; + + if (pos == ((sf_count_t) -1)) + { psf_log_syserr (psf, errno) ; + return -1 ; + } ; + + return pos - psf->fileoffset ; +} /* psf_ftell */ + +/* Win32 */ int +psf_fclose (SF_PRIVATE *psf) +{ int retval ; + + while ((retval = close (psf->filedes)) == -1 && errno == EINTR) + /* Do nothing. */ ; + + if (retval == -1) + psf_log_syserr (psf, errno) ; + + psf->filedes = -1 ; + + return retval ; +} /* psf_fclose */ + +/* Win32 */ sf_count_t +psf_fgets (char *buffer, sf_count_t bufsize, SF_PRIVATE *psf) +{ sf_count_t k = 0 ; + sf_count_t count ; + + while (k < bufsize - 1) + { count = read (psf->filedes, &(buffer [k]), 1) ; + + if (count == -1) + { if (errno == EINTR) + continue ; + + psf_log_syserr (psf, errno) ; + break ; + } ; + + if (count == 0 || buffer [k++] == '\n') + break ; + } ; + + buffer [k] = 0 ; + + return k ; +} /* psf_fgets */ + +/* Win32 */ int +psf_is_pipe (SF_PRIVATE *psf) +{ struct stat statbuf ; + + if (psf->virtual_io) + return SF_FALSE ; + + /* Not sure if this works. */ + if (fstat (psf->filedes, &statbuf) == -1) + { psf_log_syserr (psf, errno) ; + /* Default to maximum safety. */ + return SF_TRUE ; + } ; + + /* These macros are defined in Win32/unistd.h. */ + if (S_ISFIFO (statbuf.st_mode) || S_ISSOCK (statbuf.st_mode)) + return SF_TRUE ; + + return SF_FALSE ; +} /* psf_checkpipe */ + +/* Win32 */ sf_count_t +psf_get_filelen (SF_PRIVATE *psf) +{ +#if 0 + /* + ** Windoze is SOOOOO FUCKED!!!!!!! + ** This code should work but doesn't. Why? + ** Code below does work. + */ + struct _stati64 statbuf ; + + if (_fstati64 (psf->filedes, &statbuf)) + { psf_log_syserr (psf, errno) ; + return (sf_count_t) -1 ; + } ; + + return statbuf.st_size ; +#else + sf_count_t current, filelen ; + + if (psf->virtual_io) + return psf->vio.get_filelen (psf->vio_user_data) ; + + if ((current = _telli64 (psf->filedes)) < 0) + { psf_log_syserr (psf, errno) ; + return (sf_count_t) -1 ; + } ; + + /* + ** Lets face it, windoze if FUBAR!!! + ** + ** For some reason, I have to call _lseeki64() TWICE to get to the + ** end of the file. + ** + ** This might have been avoided if windows had implemented the POSIX + ** standard function fsync() but NO, that would have been too easy. + ** + ** I am VERY close to saying that windoze will no longer be supported + ** by libsndfile and changing the license to GPL at the same time. + */ + + _lseeki64 (psf->filedes, 0, SEEK_END) ; + + if ((filelen = _lseeki64 (psf->filedes, 0, SEEK_END)) < 0) + { psf_log_syserr (psf, errno) ; + return (sf_count_t) -1 ; + } ; + + if (filelen > current) + _lseeki64 (psf->filedes, current, SEEK_SET) ; + + switch (psf->mode) + { case SFM_WRITE : + filelen = filelen - psf->fileoffset ; + break ; + + case SFM_READ : + if (psf->fileoffset > 0 && psf->filelength > 0) + filelen = psf->filelength ; + break ; + + case SFM_RDWR : + /* + ** Cannot open embedded files SFM_RDWR so we don't need to + ** subtract psf->fileoffset. We already have the answer we + ** need. + */ + break ; + + default : + filelen = 0 ; + } ; + + return filelen ; +#endif +} /* psf_get_filelen */ + +/* Win32 */ int +psf_ftruncate (SF_PRIVATE *psf, sf_count_t len) +{ int retval ; + + /* Returns 0 on success, non-zero on failure. */ + if (len < 0) + return 1 ; + + /* The global village idiots at micorsoft decided to implement + ** nearly all the required 64 bit file offset functions except + ** for one, truncate. The fscking morons! + ** + ** This is not 64 bit file offset clean. Somone needs to clean + ** this up. + */ + if (len > 0x7FFFFFFF) + return -1 ; + + retval = chsize (psf->filedes, len) ; + + if (retval == -1) + psf_log_syserr (psf, errno) ; + + return retval ; +} /* psf_ftruncate */ + + +static void +psf_log_syserr (SF_PRIVATE *psf, int error) +{ + /* Only log an error if no error has been set yet. */ + if (psf->error == 0) + { psf->error = SFE_SYSTEM ; + LSF_SNPRINTF (psf->syserr, sizeof (psf->syserr), "System error : %s", strerror (error)) ; + } ; + + return ; +} /* psf_log_syserr */ + +#endif + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 749740d7-ecc7-47bd-8cf7-600f31d32e6d +*/ diff --git a/src/flac.c b/src/flac.c new file mode 100644 index 00000000..e596b63a --- /dev/null +++ b/src/flac.c @@ -0,0 +1,1112 @@ +/* +** Copyright (C) 2004, 2005 Erik de Castro Lopo +** Copyright (C) 2004 Tobias Gehrig +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY ; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program ; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include +#include +#include + +#include "sndfile.h" +#include "common.h" + + +#include "FLAC/include/FLAC/stream_decoder.h" +#include "FLAC/include/FLAC/stream_encoder.h" + +#include "sfendian.h" +#include "float_cast.h" + +/*------------------------------------------------------------------------------ +** Private static functions. +*/ + +#define ENC_BUFFER_SIZE 4096 + +typedef enum +{ PFLAC_PCM_SHORT = 0, + PFLAC_PCM_INT = 1, + PFLAC_PCM_FLOAT = 2, + PFLAC_PCM_DOUBLE = 3 +} PFLAC_PCM ; + +typedef struct +{ + FLAC__StreamDecoder *fsd ; + FLAC__StreamEncoder *fse ; + + PFLAC_PCM pcmtype ; + void* ptr ; + unsigned pos, len, remain ; + + const FLAC__int32 * const * wbuffer ; + FLAC__int32 * rbuffer [FLAC__MAX_CHANNELS] ; + + FLAC__int32* encbuffer ; + unsigned bufferpos ; + + const FLAC__Frame *frame ; + FLAC__bool bufferbackup ; +} FLAC_PRIVATE ; + +static sf_count_t flac_seek (SF_PRIVATE *psf, int mode, sf_count_t offset) ; +static int flac_close (SF_PRIVATE *psf) ; + +static int flac_enc_init (SF_PRIVATE *psf) ; +static int flac_read_header (SF_PRIVATE *psf) ; + +static sf_count_t flac_read_flac2s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ; +static sf_count_t flac_read_flac2i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ; +static sf_count_t flac_read_flac2f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ; +static sf_count_t flac_read_flac2d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ; + +static sf_count_t flac_write_s2flac (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ; +static sf_count_t flac_write_i2flac (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ; +static sf_count_t flac_write_f2flac (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ; +static sf_count_t flac_write_d2flac (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ; + +static void f2flac8_array (const float *src, FLAC__int32 *dest, int count, int normalize) ; +static void f2flac16_array (const float *src, FLAC__int32 *dest, int count, int normalize) ; +static void f2flac24_array (const float *src, FLAC__int32 *dest, int count, int normalize) ; +static void f2flac8_clip_array (const float *src, FLAC__int32 *dest, int count, int normalize) ; +static void f2flac16_clip_array (const float *src, FLAC__int32 *dest, int count, int normalize) ; +static void f2flac24_clip_array (const float *src, FLAC__int32 *dest, int count, int normalize) ; +static void d2flac8_array (const double *src, FLAC__int32 *dest, int count, int normalize) ; +static void d2flac16_array (const double *src, FLAC__int32 *dest, int count, int normalize) ; +static void d2flac24_array (const double *src, FLAC__int32 *dest, int count, int normalize) ; +static void d2flac8_clip_array (const double *src, FLAC__int32 *dest, int count, int normalize) ; +static void d2flac16_clip_array (const double *src, FLAC__int32 *dest, int count, int normalize) ; +static void d2flac24_clip_array (const double *src, FLAC__int32 *dest, int count, int normalize) ; + +static int flac_command (SF_PRIVATE *psf, int command, void *data, int datasize) ; + +/* Decoder Callbacks */ +static FLAC__StreamDecoderReadStatus sf_flac_read_callback (const FLAC__StreamDecoder *decoder, FLAC__byte buffer [], size_t *bytes, void *client_data) ; +static FLAC__StreamDecoderSeekStatus sf_flac_seek_callback (const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data) ; +static FLAC__StreamDecoderTellStatus sf_flac_tell_callback (const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data) ; +static FLAC__StreamDecoderLengthStatus sf_flac_length_callback (const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data) ; +static FLAC__bool sf_flac_eof_callback (const FLAC__StreamDecoder *decoder, void *client_data) ; +static FLAC__StreamDecoderWriteStatus sf_flac_write_callback (const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer [], void *client_data) ; +static void sf_flac_meta_callback (const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) ; +static void sf_flac_error_callback (const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) ; + +/* Encoder Callbacks */ +static FLAC__StreamEncoderSeekStatus sf_flac_enc_seek_callback (const FLAC__StreamEncoder *encoder, FLAC__uint64 absolute_byte_offset, void *client_data) ; +static FLAC__StreamEncoderTellStatus sf_flac_enc_tell_callback (const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_byte_offset, void *client_data) ; +static FLAC__StreamEncoderWriteStatus sf_flac_enc_write_callback (const FLAC__StreamEncoder *encoder, const FLAC__byte buffer [], size_t bytes, unsigned samples, unsigned current_frame, void *client_data) ; + +static const int legal_sample_rates [] = +{ 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000 +} ; + +static void +s2flac8_array (const short *src, FLAC__int32 *dest, int count) +{ while (--count >= 0) + dest [count] = src [count] >> 8 ; +} /* s2flac8_array */ + +static void +s2flac16_array (const short *src, FLAC__int32 *dest, int count) +{ while (--count >= 0) + dest [count] = src [count] ; +} /* s2flac16_array */ + +static void +s2flac24_array (const short *src, FLAC__int32 *dest, int count) +{ while (--count >= 0) + dest [count] = src [count] << 8 ; +} /* s2flac24_array */ + +static void +i2flac8_array (const int *src, FLAC__int32 *dest, int count) +{ while (--count >= 0) + dest [count] = src [count] >> 24 ; +} /* i2flac8_array */ + +static void +i2flac16_array (const int *src, FLAC__int32 *dest, int count) +{ + while (--count >= 0) + dest [count] = src [count] >> 16 ; +} /* i2flac16_array */ + +static void +i2flac24_array (const int *src, FLAC__int32 *dest, int count) +{ while (--count >= 0) + dest [count] = src [count] >> 8 ; +} /* i2flac24_array */ + +static sf_count_t +flac_buffer_copy (SF_PRIVATE *psf) +{ FLAC_PRIVATE* pflac = (FLAC_PRIVATE*) psf->codec_data ; + const FLAC__Frame *frame = pflac->frame ; + const FLAC__int32* const *buffer = pflac->wbuffer ; + unsigned i = 0, j, offset ; + + if (pflac->ptr == NULL) + { /* + ** Not sure why this code is here and not elsewhere. + ** Removing it causes valgrind errors. + */ + pflac->bufferbackup = SF_TRUE ; + for (i = 0 ; i < frame->header.channels ; i++) + { if (pflac->rbuffer [i] == NULL) + pflac->rbuffer [i] = calloc (frame->header.blocksize, sizeof (FLAC__int32)) ; + memcpy (pflac->rbuffer [i], buffer [i], frame->header.blocksize * sizeof (FLAC__int32)) ; + } ; + pflac->wbuffer = (const FLAC__int32* const*) pflac->rbuffer ; + + return 0 ; + } ; + + switch (pflac->pcmtype) + { case PFLAC_PCM_SHORT : + { short *retpcm = ((short*) pflac->ptr) ; + int shift = 16 - frame->header.bits_per_sample ; + if (shift < 0) + { shift = abs (shift) ; + for (i = 0 ; i < frame->header.blocksize && pflac->remain > 0 ; i++) + { offset = pflac->pos + i * frame->header.channels ; + for (j = 0 ; j < frame->header.channels ; j++) + retpcm [offset + j] = buffer [j][pflac->bufferpos] >> shift ; + pflac->remain -= frame->header.channels ; + pflac->bufferpos++ ; + } + } + else + { for (i = 0 ; i < frame->header.blocksize && pflac->remain > 0 ; i++) + { offset = pflac->pos + i * frame->header.channels ; + + if (pflac->bufferpos >= frame->header.blocksize) + break ; + + for (j = 0 ; j < frame->header.channels ; j++) + retpcm [offset + j] = (buffer [j][pflac->bufferpos]) << shift ; + + pflac->remain -= frame->header.channels ; + pflac->bufferpos++ ; + } ; + } ; + } ; + break ; + + case PFLAC_PCM_INT : + { int *retpcm = ((int*) pflac->ptr) ; + int shift = 32 - frame->header.bits_per_sample ; + for (i = 0 ; i < frame->header.blocksize && pflac->remain > 0 ; i++) + { offset = pflac->pos + i * frame->header.channels ; + + if (pflac->bufferpos >= frame->header.blocksize) + break ; + + for (j = 0 ; j < frame->header.channels ; j++) + retpcm [offset + j] = buffer [j][pflac->bufferpos] << shift ; + pflac->remain -= frame->header.channels ; + pflac->bufferpos++ ; + } ; + } ; + break ; + + case PFLAC_PCM_FLOAT : + { float *retpcm = ((float*) pflac->ptr) ; + float norm = (psf->norm_float == SF_TRUE) ? 1.0 / (1 << (frame->header.bits_per_sample - 1)) : 1.0 ; + + for (i = 0 ; i < frame->header.blocksize && pflac->remain > 0 ; i++) + { offset = pflac->pos + i * frame->header.channels ; + + if (pflac->bufferpos >= frame->header.blocksize) + break ; + + for (j = 0 ; j < frame->header.channels ; j++) + retpcm [offset + j] = buffer [j][pflac->bufferpos] * norm ; + pflac->remain -= frame->header.channels ; + pflac->bufferpos++ ; + } ; + } ; + break ; + + case PFLAC_PCM_DOUBLE : + { double *retpcm = ((double*) pflac->ptr) ; + double norm = (psf->norm_double == SF_TRUE) ? 1.0 / (1 << (frame->header.bits_per_sample - 1)) : 1.0 ; + + for (i = 0 ; i < frame->header.blocksize && pflac->remain > 0 ; i++) + { offset = pflac->pos + i * frame->header.channels ; + + if (pflac->bufferpos >= frame->header.blocksize) + break ; + + for (j = 0 ; j < frame->header.channels ; j++) + retpcm [offset + j] = buffer [j][pflac->bufferpos] * norm ; + pflac->remain -= frame->header.channels ; + pflac->bufferpos++ ; + } ; + } ; + break ; + + default : + return 0 ; + } ; + + offset = i * frame->header.channels ; + pflac->pos += i * frame->header.channels ; + + return offset ; +} /* flac_buffer_copy */ + + +static FLAC__StreamDecoderReadStatus +sf_flac_read_callback (const FLAC__StreamDecoder * UNUSED (decoder), FLAC__byte buffer [], size_t *bytes, void *client_data) +{ SF_PRIVATE *psf = (SF_PRIVATE*) client_data ; + + *bytes = psf_fread (buffer, 1, *bytes, psf) ; + if (*bytes > 0 && psf->error == 0) + return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE ; + + return FLAC__STREAM_DECODER_READ_STATUS_ABORT ; +} /* sf_flac_read_callback */ + +static FLAC__StreamDecoderSeekStatus +sf_flac_seek_callback (const FLAC__StreamDecoder * UNUSED (decoder), FLAC__uint64 absolute_byte_offset, void *client_data) +{ SF_PRIVATE *psf = (SF_PRIVATE*) client_data ; + + psf_fseek (psf, absolute_byte_offset, SEEK_SET) ; + if (psf->error) + return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR ; + + return FLAC__STREAM_DECODER_SEEK_STATUS_OK ; +} /* sf_flac_seek_callback */ + +static FLAC__StreamDecoderTellStatus +sf_flac_tell_callback (const FLAC__StreamDecoder * UNUSED (decoder), FLAC__uint64 *absolute_byte_offset, void *client_data) +{ SF_PRIVATE *psf = (SF_PRIVATE*) client_data ; + + *absolute_byte_offset = psf_ftell (psf) ; + if (psf->error) + return FLAC__STREAM_DECODER_TELL_STATUS_ERROR ; + + return FLAC__STREAM_DECODER_TELL_STATUS_OK ; +} /* sf_flac_tell_callback */ + +static FLAC__StreamDecoderLengthStatus +sf_flac_length_callback (const FLAC__StreamDecoder * UNUSED (decoder), FLAC__uint64 *stream_length, void *client_data) +{ SF_PRIVATE *psf = (SF_PRIVATE*) client_data ; + + if ((*stream_length = psf->filelength) == 0) + return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR ; + + return FLAC__STREAM_DECODER_LENGTH_STATUS_OK ; +} /* sf_flac_length_callback */ + +static FLAC__bool +sf_flac_eof_callback (const FLAC__StreamDecoder *UNUSED (decoder), void *client_data) +{ SF_PRIVATE *psf = (SF_PRIVATE*) client_data ; + + if (psf_ftell (psf) == psf->filelength) + return SF_TRUE ; + + return SF_FALSE ; +} /* sf_flac_eof_callback */ + +static FLAC__StreamDecoderWriteStatus +sf_flac_write_callback (const FLAC__StreamDecoder * UNUSED (decoder), const FLAC__Frame *frame, const FLAC__int32 * const buffer [], void *client_data) +{ SF_PRIVATE *psf = (SF_PRIVATE*) client_data ; + FLAC_PRIVATE* pflac = (FLAC_PRIVATE*) psf->codec_data ; + + pflac->frame = frame ; + pflac->bufferpos = 0 ; + + pflac->bufferbackup = SF_FALSE ; + pflac->wbuffer = buffer ; + + flac_buffer_copy (psf) ; + + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE ; +} /* sf_flac_write_callback */ + +static void +sf_flac_meta_callback (const FLAC__StreamDecoder * UNUSED (decoder), const FLAC__StreamMetadata *metadata, void *client_data) +{ SF_PRIVATE *psf = (SF_PRIVATE*) client_data ; + + switch (metadata->type) + { case FLAC__METADATA_TYPE_STREAMINFO : + psf->sf.channels = metadata->data.stream_info.channels ; + psf->sf.samplerate = metadata->data.stream_info.sample_rate ; + psf->sf.frames = metadata->data.stream_info.total_samples ; + + switch (metadata->data.stream_info.bits_per_sample) + { case 8 : + psf->sf.format |= SF_FORMAT_PCM_S8 ; + break ; + case 16 : + psf->sf.format |= SF_FORMAT_PCM_16 ; + break ; + case 24 : + psf->sf.format |= SF_FORMAT_PCM_24 ; + break ; + default : + psf_log_printf (psf, "sf_flac_meta_callback : bits_per_sample %d not yet implemented.\n", metadata->data.stream_info.bits_per_sample) ; + break ; + } ; + break ; + + default : + psf_log_printf (psf, "sf_flac_meta_callback : metadata-type %d not yet implemented.\n", metadata->type) ; + break ; + } ; + + return ; +} /* sf_flac_meta_callback */ + +static void +sf_flac_error_callback (const FLAC__StreamDecoder * UNUSED (decoder), FLAC__StreamDecoderErrorStatus status, void *client_data) +{ SF_PRIVATE *psf = (SF_PRIVATE*) client_data ; + + psf_log_printf (psf, "ERROR : %s\n", FLAC__StreamDecoderErrorStatusString [status]) ; + + switch (status) + { case FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC : + psf->error = SFE_FLAC_LOST_SYNC ; + break ; + case FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER : + psf->error = SFE_FLAC_BAD_HEADER ; + break ; + default : + psf->error = SFE_FLAC_UNKOWN_ERROR ; + break ; + } ; + + return ; +} /* sf_flac_error_callback */ + +static FLAC__StreamEncoderSeekStatus +sf_flac_enc_seek_callback (const FLAC__StreamEncoder * UNUSED (encoder), FLAC__uint64 absolute_byte_offset, void *client_data) +{ SF_PRIVATE *psf = (SF_PRIVATE*) client_data ; + + psf_fseek (psf, absolute_byte_offset, SEEK_SET) ; + if (psf->error) + return FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR ; + + return FLAC__STREAM_ENCODER_SEEK_STATUS_OK ; +} /* sf_flac_enc_seek_callback */ + +static FLAC__StreamEncoderTellStatus +sf_flac_enc_tell_callback (const FLAC__StreamEncoder *UNUSED (encoder), FLAC__uint64 *absolute_byte_offset, void *client_data) +{ SF_PRIVATE *psf = (SF_PRIVATE*) client_data ; + + *absolute_byte_offset = psf_ftell (psf) ; + if (psf->error) + return FLAC__STREAM_ENCODER_TELL_STATUS_ERROR ; + + return FLAC__STREAM_ENCODER_TELL_STATUS_OK ; +} /* sf_flac_enc_tell_callback */ + +static FLAC__StreamEncoderWriteStatus +sf_flac_enc_write_callback (const FLAC__StreamEncoder * UNUSED (encoder), const FLAC__byte buffer [], size_t bytes, unsigned UNUSED (samples), unsigned UNUSED (current_frame), void *client_data) +{ SF_PRIVATE *psf = (SF_PRIVATE*) client_data ; + + if (psf_fwrite (buffer, 1, bytes, psf) == (sf_count_t) bytes && psf->error == 0) + return FLAC__STREAM_ENCODER_WRITE_STATUS_OK ; + + return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR ; +} /* sf_flac_enc_write_callback */ + +/*------------------------------------------------------------------------------ +** Public function. +*/ + +int +flac_open (SF_PRIVATE *psf) +{ int subformat ; + int error = 0 ; + + FLAC_PRIVATE* pflac = calloc (1, sizeof (FLAC_PRIVATE)) ; + psf->codec_data = pflac ; + + if (psf->mode == SFM_RDWR) + return SFE_BAD_RDWR_FORMAT ; + + if (psf->mode == SFM_READ) + { if ((error = flac_read_header (psf))) + return error ; + } ; + + subformat = psf->sf.format & SF_FORMAT_SUBMASK ; + + if (psf->mode == SFM_WRITE) + { if ((psf->sf.format & SF_FORMAT_TYPEMASK) != SF_FORMAT_FLAC) + return SFE_BAD_OPEN_FORMAT ; + + psf->endian = SF_ENDIAN_BIG ; + psf->sf.seekable = 0 ; + + if ((error = flac_enc_init (psf))) + return error ; + } ; + + psf->datalength = psf->filelength ; + psf->dataoffset = 0 ; + psf->blockwidth = 0 ; + psf->bytewidth = 1 ; + + psf->container_close = flac_close ; + psf->seek = flac_seek ; + psf->command = flac_command ; + + psf->blockwidth = psf->bytewidth * psf->sf.channels ; + + switch (subformat) + { case SF_FORMAT_PCM_S8 : /* 8-bit FLAC. */ + case SF_FORMAT_PCM_16 : /* 16-bit FLAC. */ + case SF_FORMAT_PCM_24 : /* 24-bit FLAC. */ + error = flac_init (psf) ; + break ; + + default : return SFE_UNIMPLEMENTED ; + } ; + + return error ; +} /* flac_open */ + +/*------------------------------------------------------------------------------ +*/ + +static int +flac_close (SF_PRIVATE *psf) +{ FLAC_PRIVATE* pflac ; + int k ; + + if ((pflac = (FLAC_PRIVATE*) psf->codec_data) == NULL) + return 0 ; + + if (psf->mode == SFM_WRITE) + { FLAC__stream_encoder_finish (pflac->fse) ; + FLAC__stream_encoder_delete (pflac->fse) ; + + if (pflac->encbuffer) + free (pflac->encbuffer) ; + } ; + + if (psf->mode == SFM_READ) + { FLAC__stream_decoder_finish (pflac->fsd) ; + FLAC__stream_decoder_delete (pflac->fsd) ; + } ; + + for (k = 0 ; k < ARRAY_LEN (pflac->rbuffer) ; k++) + free (pflac->rbuffer [k]) ; + + free (pflac) ; + psf->codec_data = NULL ; + + return 0 ; +} /* flac_close */ + +static int +flac_enc_init (SF_PRIVATE *psf) +{ FLAC_PRIVATE* pflac = (FLAC_PRIVATE*) psf->codec_data ; + unsigned bps ; + int k, found ; + + found = 0 ; + for (k = 0 ; k < ARRAY_LEN (legal_sample_rates) ; k++) + if (psf->sf.samplerate == legal_sample_rates [k]) + { found = 1 ; + break ; + } ; + + if (found == 0) + return SFE_FLAC_BAD_SAMPLE_RATE ; + + psf_fseek (psf, 0, SEEK_SET) ; + + switch (psf->sf.format & SF_FORMAT_SUBMASK) + { case SF_FORMAT_PCM_S8 : + bps = 8 ; + break ; + case SF_FORMAT_PCM_16 : + bps = 16 ; + break ; + case SF_FORMAT_PCM_24 : + bps = 24 ; + break ; + + default : + bps = 0 ; + break ; + } ; + + if ((pflac->fse = FLAC__stream_encoder_new ()) == NULL) + return SFE_FLAC_NEW_DECODER ; + FLAC__stream_encoder_set_channels (pflac->fse, psf->sf.channels) ; + FLAC__stream_encoder_set_sample_rate (pflac->fse, psf->sf.samplerate) ; + FLAC__stream_encoder_set_bits_per_sample (pflac->fse, bps) ; + + if ((bps = FLAC__stream_encoder_init_stream (pflac->fse, sf_flac_enc_write_callback, sf_flac_enc_seek_callback, sf_flac_enc_tell_callback, NULL, psf)) != FLAC__STREAM_DECODER_INIT_STATUS_OK) + { psf_log_printf (psf, "Error : FLAC encoder init returned error : %s\n", FLAC__StreamEncoderInitStatusString [bps]) ; + return SFE_FLAC_INIT_DECODER ; + } ; + + if (psf->error == 0) + psf->dataoffset = psf_ftell (psf) ; + pflac->encbuffer = calloc (ENC_BUFFER_SIZE, sizeof (FLAC__int32)) ; + + return psf->error ; +} /* flac_enc_init */ + +static int +flac_read_header (SF_PRIVATE *psf) +{ FLAC_PRIVATE* pflac = (FLAC_PRIVATE*) psf->codec_data ; + + psf_fseek (psf, 0, SEEK_SET) ; + if ((pflac->fsd = FLAC__stream_decoder_new ()) == NULL) + return SFE_FLAC_NEW_DECODER ; + + if (FLAC__stream_decoder_init_stream (pflac->fsd, sf_flac_read_callback, sf_flac_seek_callback, sf_flac_tell_callback, sf_flac_length_callback, sf_flac_eof_callback, sf_flac_write_callback, sf_flac_meta_callback, sf_flac_error_callback, psf) != FLAC__STREAM_DECODER_INIT_STATUS_OK) + return SFE_FLAC_INIT_DECODER ; + + FLAC__stream_decoder_process_until_end_of_metadata (pflac->fsd) ; + + if (psf->error == 0) + { FLAC__uint64 position ; + + FLAC__stream_decoder_get_decode_position (pflac->fsd, &position) ; + psf->dataoffset = position ; + } ; + + return psf->error ; +} /* flac_read_header */ + +static int +flac_command (SF_PRIVATE * UNUSED (psf), int UNUSED (command), void * UNUSED (data), int UNUSED (datasize)) +{ + return 0 ; +} /* flac_command */ + +int +flac_init (SF_PRIVATE *psf) +{ + if (psf->mode == SFM_RDWR) + return SFE_BAD_MODE_RW ; + + if (psf->mode == SFM_READ) + { psf->read_short = flac_read_flac2s ; + psf->read_int = flac_read_flac2i ; + psf->read_float = flac_read_flac2f ; + psf->read_double = flac_read_flac2d ; + } ; + + if (psf->mode == SFM_WRITE) + { psf->write_short = flac_write_s2flac ; + psf->write_int = flac_write_i2flac ; + psf->write_float = flac_write_f2flac ; + psf->write_double = flac_write_d2flac ; + } ; + + psf->bytewidth = 1 ; + psf->blockwidth = psf->sf.channels ; + + if (psf->filelength > psf->dataoffset) + psf->datalength = (psf->dataend) ? psf->dataend - psf->dataoffset : psf->filelength - psf->dataoffset ; + else + psf->datalength = 0 ; + + return 0 ; +} /* flac_init */ + +static unsigned +flac_read_loop (SF_PRIVATE *psf, unsigned len) +{ FLAC_PRIVATE* pflac = (FLAC_PRIVATE*) psf->codec_data ; + + pflac->pos = 0 ; + pflac->len = len ; + pflac->remain = len ; + if (pflac->frame != NULL && pflac->bufferpos < pflac->frame->header.blocksize) + flac_buffer_copy (psf) ; + + while (pflac->pos < pflac->len) + { if (FLAC__stream_decoder_process_single (pflac->fsd) == 0) + break ; + if (FLAC__stream_decoder_get_state (pflac->fsd) >= FLAC__STREAM_DECODER_END_OF_STREAM) + break ; + } ; + + pflac->ptr = NULL ; + + return pflac->pos ; +} /* flac_read_loop */ + +static sf_count_t +flac_read_flac2s (SF_PRIVATE *psf, short *ptr, sf_count_t len) +{ FLAC_PRIVATE* pflac = (FLAC_PRIVATE*) psf->codec_data ; + sf_count_t total = 0, current ; + unsigned readlen ; + + pflac->pcmtype = PFLAC_PCM_SHORT ; + + while (total < len) + { pflac->ptr = ptr + total ; + readlen = (len - total > 0x1000000) ? 0x1000000 : (unsigned) (len - total) ; + current = flac_read_loop (psf, readlen) ; + if (current == 0) + break ; + total += current ; + } ; + + return total ; +} /* flac_read_flac2s */ + +static sf_count_t +flac_read_flac2i (SF_PRIVATE *psf, int *ptr, sf_count_t len) +{ FLAC_PRIVATE* pflac = (FLAC_PRIVATE*) psf->codec_data ; + sf_count_t total = 0, current ; + unsigned readlen ; + + pflac->pcmtype = PFLAC_PCM_INT ; + + while (total < len) + { pflac->ptr = ptr + total ; + readlen = (len - total > 0x1000000) ? 0x1000000 : (unsigned) (len - total) ; + current = flac_read_loop (psf, readlen) ; + if (current == 0) + break ; + total += current ; + } ; + + return total ; +} /* flac_read_flac2i */ + +static sf_count_t +flac_read_flac2f (SF_PRIVATE *psf, float *ptr, sf_count_t len) +{ FLAC_PRIVATE* pflac = (FLAC_PRIVATE*) psf->codec_data ; + sf_count_t total = 0, current ; + unsigned readlen ; + + pflac->pcmtype = PFLAC_PCM_FLOAT ; + + while (total < len) + { pflac->ptr = ptr + total ; + readlen = (len - total > 0x1000000) ? 0x1000000 : (unsigned) (len - total) ; + current = flac_read_loop (psf, readlen) ; + if (current == 0) + break ; + total += current ; + } ; + + return total ; +} /* flac_read_flac2f */ + +static sf_count_t +flac_read_flac2d (SF_PRIVATE *psf, double *ptr, sf_count_t len) +{ FLAC_PRIVATE* pflac = (FLAC_PRIVATE*) psf->codec_data ; + sf_count_t total = 0, current ; + unsigned readlen ; + + pflac->pcmtype = PFLAC_PCM_DOUBLE ; + + while (total < len) + { pflac->ptr = ptr + total ; + readlen = (len - total > 0x1000000) ? 0x1000000 : (unsigned) (len - total) ; + current = flac_read_loop (psf, readlen) ; + if (current == 0) + break ; + total += current ; + } ; + + return total ; +} /* flac_read_flac2d */ + +static sf_count_t +flac_write_s2flac (SF_PRIVATE *psf, const short *ptr, sf_count_t len) +{ FLAC_PRIVATE* pflac = (FLAC_PRIVATE*) psf->codec_data ; + void (*convert) (const short *, FLAC__int32 *, int) ; + int bufferlen, writecount, thiswrite ; + sf_count_t total = 0 ; + FLAC__int32* buffer = pflac->encbuffer ; + + switch (psf->sf.format & SF_FORMAT_SUBMASK) + { case SF_FORMAT_PCM_S8 : + convert = s2flac8_array ; + break ; + case SF_FORMAT_PCM_16 : + convert = s2flac16_array ; + break ; + case SF_FORMAT_PCM_24 : + convert = s2flac24_array ; + break ; + default : + return -1 ; + } ; + + bufferlen = ENC_BUFFER_SIZE / (sizeof (FLAC__int32) * psf->sf.channels) ; + bufferlen *= psf->sf.channels ; + + while (len > 0) + { writecount = (len >= bufferlen) ? bufferlen : (int) len ; + convert (ptr + total, buffer, writecount) ; + if (FLAC__stream_encoder_process_interleaved (pflac->fse, buffer, writecount/psf->sf.channels)) + thiswrite = writecount ; + else + break ; + total += thiswrite ; + if (thiswrite < writecount) + break ; + + len -= thiswrite ; + } ; + + return total ; +} /* flac_write_s2flac */ + +static sf_count_t +flac_write_i2flac (SF_PRIVATE *psf, const int *ptr, sf_count_t len) +{ FLAC_PRIVATE* pflac = (FLAC_PRIVATE*) psf->codec_data ; + void (*convert) (const int *, FLAC__int32 *, int) ; + int bufferlen, writecount, thiswrite ; + sf_count_t total = 0 ; + FLAC__int32* buffer = pflac->encbuffer ; + + switch (psf->sf.format & SF_FORMAT_SUBMASK) + { case SF_FORMAT_PCM_S8 : + convert = i2flac8_array ; + break ; + case SF_FORMAT_PCM_16 : + convert = i2flac16_array ; + break ; + case SF_FORMAT_PCM_24 : + convert = i2flac24_array ; + break ; + default : + return -1 ; + } ; + + bufferlen = ENC_BUFFER_SIZE / (sizeof (FLAC__int32) * psf->sf.channels) ; + bufferlen *= psf->sf.channels ; + + while (len > 0) + { writecount = (len >= bufferlen) ? bufferlen : (int) len ; + convert (ptr + total, buffer, writecount) ; + if (FLAC__stream_encoder_process_interleaved (pflac->fse, buffer, writecount/psf->sf.channels)) + thiswrite = writecount ; + else + break ; + total += thiswrite ; + if (thiswrite < writecount) + break ; + + len -= thiswrite ; + } ; + + return total ; +} /* flac_write_i2flac */ + +static sf_count_t +flac_write_f2flac (SF_PRIVATE *psf, const float *ptr, sf_count_t len) +{ FLAC_PRIVATE* pflac = (FLAC_PRIVATE*) psf->codec_data ; + void (*convert) (const float *, FLAC__int32 *, int, int) ; + int bufferlen, writecount, thiswrite ; + sf_count_t total = 0 ; + FLAC__int32* buffer = pflac->encbuffer ; + + switch (psf->sf.format & SF_FORMAT_SUBMASK) + { case SF_FORMAT_PCM_S8 : + convert = (psf->add_clipping) ? f2flac8_clip_array : f2flac8_array ; + break ; + case SF_FORMAT_PCM_16 : + convert = (psf->add_clipping) ? f2flac16_clip_array : f2flac16_array ; + break ; + case SF_FORMAT_PCM_24 : + convert = (psf->add_clipping) ? f2flac24_clip_array : f2flac24_array ; + break ; + default : + return -1 ; + } ; + + bufferlen = ENC_BUFFER_SIZE / (sizeof (FLAC__int32) * psf->sf.channels) ; + bufferlen *= psf->sf.channels ; + + while (len > 0) + { writecount = (len >= bufferlen) ? bufferlen : (int) len ; + convert (ptr + total, buffer, writecount, psf->norm_float) ; + if (FLAC__stream_encoder_process_interleaved (pflac->fse, buffer, writecount/psf->sf.channels)) + thiswrite = writecount ; + else + break ; + total += thiswrite ; + if (thiswrite < writecount) + break ; + + len -= thiswrite ; + } ; + + return total ; +} /* flac_write_f2flac */ + +static void +f2flac8_clip_array (const float *src, FLAC__int32 *dest, int count, int normalize) +{ float normfact, scaled_value ; + + normfact = normalize ? (8.0 * 0x10) : 1.0 ; + + while (--count >= 0) + { scaled_value = src [count] * normfact ; + if (CPU_CLIPS_POSITIVE == 0 && scaled_value >= (1.0 * 0x7F)) + { dest [count] = 0x7F ; + continue ; + } ; + if (CPU_CLIPS_NEGATIVE == 0 && scaled_value <= (-8.0 * 0x10)) + { dest [count] = 0x80 ; + continue ; + } ; + dest [count] = lrintf (scaled_value) ; + } ; + + return ; +} /* f2flac8_clip_array */ + +static void +f2flac16_clip_array (const float *src, FLAC__int32 *dest, int count, int normalize) +{ + float normfact, scaled_value ; + + normfact = normalize ? (8.0 * 0x1000) : 1.0 ; + + while (--count >= 0) { + scaled_value = src [count] * normfact ; + if (CPU_CLIPS_POSITIVE == 0 && scaled_value >= (1.0 * 0x7FFF)) { + dest [count] = 0x7FFF ; + continue ; + } + if (CPU_CLIPS_NEGATIVE == 0 && scaled_value <= (-8.0 * 0x1000)) { + dest [count] = 0x8000 ; + continue ; + } + dest [count] = lrintf (scaled_value) ; + } +} /* f2flac16_clip_array */ + +static void +f2flac24_clip_array (const float *src, FLAC__int32 *dest, int count, int normalize) +{ float normfact, scaled_value ; + + normfact = normalize ? (8.0 * 0x100000) : 1.0 ; + + while (--count >= 0) + { scaled_value = src [count] * normfact ; + if (CPU_CLIPS_POSITIVE == 0 && scaled_value >= (1.0 * 0x7FFFFF)) + { dest [count] = 0x7FFFFF ; + continue ; + } ; + + if (CPU_CLIPS_NEGATIVE == 0 && scaled_value <= (-8.0 * 0x100000)) + { dest [count] = 0x800000 ; + continue ; + } + dest [count] = lrintf (scaled_value) ; + } ; + + return ; +} /* f2flac24_clip_array */ + +static void +f2flac8_array (const float *src, FLAC__int32 *dest, int count, int normalize) +{ float normfact = normalize ? (1.0 * 0x7F) : 1.0 ; + + while (--count >= 0) + dest [count] = lrintf (src [count] * normfact) ; +} /* f2flac8_array */ + +static void +f2flac16_array (const float *src, FLAC__int32 *dest, int count, int normalize) +{ float normfact = normalize ? (1.0 * 0x7FFF) : 1.0 ; + + while (--count >= 0) + dest [count] = lrintf (src [count] * normfact) ; +} /* f2flac16_array */ + +static void +f2flac24_array (const float *src, FLAC__int32 *dest, int count, int normalize) +{ float normfact = normalize ? (1.0 * 0x7FFFFF) : 1.0 ; + + while (--count >= 0) + dest [count] = lrintf (src [count] * normfact) ; +} /* f2flac24_array */ + +static sf_count_t +flac_write_d2flac (SF_PRIVATE *psf, const double *ptr, sf_count_t len) +{ FLAC_PRIVATE* pflac = (FLAC_PRIVATE*) psf->codec_data ; + void (*convert) (const double *, FLAC__int32 *, int, int) ; + int bufferlen, writecount, thiswrite ; + sf_count_t total = 0 ; + FLAC__int32* buffer = pflac->encbuffer ; + + switch (psf->sf.format & SF_FORMAT_SUBMASK) + { case SF_FORMAT_PCM_S8 : + convert = (psf->add_clipping) ? d2flac8_clip_array : d2flac8_array ; + break ; + case SF_FORMAT_PCM_16 : + convert = (psf->add_clipping) ? d2flac16_clip_array : d2flac16_array ; + break ; + case SF_FORMAT_PCM_24 : + convert = (psf->add_clipping) ? d2flac24_clip_array : d2flac24_array ; + break ; + default : + return -1 ; + } ; + + bufferlen = ENC_BUFFER_SIZE / (sizeof (FLAC__int32) * psf->sf.channels) ; + bufferlen *= psf->sf.channels ; + + while (len > 0) + { writecount = (len >= bufferlen) ? bufferlen : (int) len ; + convert (ptr + total, buffer, writecount, psf->norm_double) ; + if (FLAC__stream_encoder_process_interleaved (pflac->fse, buffer, writecount/psf->sf.channels)) + thiswrite = writecount ; + else + break ; + total += thiswrite ; + if (thiswrite < writecount) + break ; + + len -= thiswrite ; + } ; + + return total ; +} /* flac_write_d2flac */ + +static void +d2flac8_clip_array (const double *src, FLAC__int32 *dest, int count, int normalize) +{ double normfact, scaled_value ; + + normfact = normalize ? (8.0 * 0x10) : 1.0 ; + + while (--count >= 0) + { scaled_value = src [count] * normfact ; + if (CPU_CLIPS_POSITIVE == 0 && scaled_value >= (1.0 * 0x7F)) + { dest [count] = 0x7F ; + continue ; + } ; + if (CPU_CLIPS_NEGATIVE == 0 && scaled_value <= (-8.0 * 0x10)) + { dest [count] = 0x80 ; + continue ; + } ; + dest [count] = lrint (scaled_value) ; + } ; + + return ; +} /* d2flac8_clip_array */ + +static void +d2flac16_clip_array (const double *src, FLAC__int32 *dest, int count, int normalize) +{ double normfact, scaled_value ; + + normfact = normalize ? (8.0 * 0x1000) : 1.0 ; + + while (--count >= 0) + { scaled_value = src [count] * normfact ; + if (CPU_CLIPS_POSITIVE == 0 && scaled_value >= (1.0 * 0x7FFF)) + { dest [count] = 0x7FFF ; + continue ; + } ; + if (CPU_CLIPS_NEGATIVE == 0 && scaled_value <= (-8.0 * 0x1000)) + { dest [count] = 0x8000 ; + continue ; + } ; + dest [count] = lrint (scaled_value) ; + } ; + + return ; +} /* d2flac16_clip_array */ + +static void +d2flac24_clip_array (const double *src, FLAC__int32 *dest, int count, int normalize) +{ double normfact, scaled_value ; + + normfact = normalize ? (8.0 * 0x100000) : 1.0 ; + + while (--count >= 0) + { scaled_value = src [count] * normfact ; + if (CPU_CLIPS_POSITIVE == 0 && scaled_value >= (1.0 * 0x7FFFFF)) + { dest [count] = 0x7FFFFF ; + continue ; + } ; + if (CPU_CLIPS_NEGATIVE == 0 && scaled_value <= (-8.0 * 0x100000)) + { dest [count] = 0x800000 ; + continue ; + } ; + dest [count] = lrint (scaled_value) ; + } ; + + return ; +} /* d2flac24_clip_array */ + +static void +d2flac8_array (const double *src, FLAC__int32 *dest, int count, int normalize) +{ double normfact = normalize ? (1.0 * 0x7F) : 1.0 ; + + while (--count >= 0) + dest [count] = lrint (src [count] * normfact) ; +} /* d2flac8_array */ + +static void +d2flac16_array (const double *src, FLAC__int32 *dest, int count, int normalize) +{ double normfact = normalize ? (1.0 * 0x7FFF) : 1.0 ; + + while (--count >= 0) + dest [count] = lrint (src [count] * normfact) ; +} /* d2flac16_array */ + +static void +d2flac24_array (const double *src, FLAC__int32 *dest, int count, int normalize) +{ double normfact = normalize ? (1.0 * 0x7FFFFF) : 1.0 ; + + while (--count >= 0) + dest [count] = lrint (src [count] * normfact) ; +} /* d2flac24_array */ + +static sf_count_t +flac_seek (SF_PRIVATE *psf, int UNUSED (mode), sf_count_t offset) +{ FLAC_PRIVATE* pflac = (FLAC_PRIVATE*) psf->codec_data ; + + if (pflac == NULL) + return 0 ; + + if (psf->dataoffset < 0) + { psf->error = SFE_BAD_SEEK ; + return ((sf_count_t) -1) ; + } ; + + pflac->frame = NULL ; + + if (psf->mode == SFM_READ) + { FLAC__uint64 position ; + if (FLAC__stream_decoder_seek_absolute (pflac->fsd, offset)) + { FLAC__stream_decoder_get_decode_position (pflac->fsd, &position) ; + return offset ; + } ; + + return ((sf_count_t) -1) ; + } ; + + /* Seeking in write mode not yet supported. */ + psf->error = SFE_BAD_SEEK ; + + return ((sf_count_t) -1) ; +} /* flac_seek */ + diff --git a/src/float32.c b/src/float32.c new file mode 100644 index 00000000..b3e87253 --- /dev/null +++ b/src/float32.c @@ -0,0 +1,995 @@ +/* +** Copyright (C) 1999-2006 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include +#include + +#include "sndfile.h" +#include "sfendian.h" +#include "common.h" +#include "float_cast.h" + +#if CPU_IS_LITTLE_ENDIAN + #define FLOAT32_READ float32_le_read + #define FLOAT32_WRITE float32_le_write +#elif CPU_IS_BIG_ENDIAN + #define FLOAT32_READ float32_be_read + #define FLOAT32_WRITE float32_be_write +#endif + +/*-------------------------------------------------------------------------------------------- +** Processor floating point capabilities. float32_get_capability () returns one of the +** latter four values. +*/ + +enum +{ FLOAT_UNKNOWN = 0x00, + FLOAT_CAN_RW_LE = 0x12, + FLOAT_CAN_RW_BE = 0x23, + FLOAT_BROKEN_LE = 0x34, + FLOAT_BROKEN_BE = 0x45 +} ; + +/*-------------------------------------------------------------------------------------------- +** Prototypes for private functions. +*/ + +static sf_count_t host_read_f2s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ; +static sf_count_t host_read_f2i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ; +static sf_count_t host_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ; +static sf_count_t host_read_f2d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ; + +static sf_count_t host_write_s2f (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ; +static sf_count_t host_write_i2f (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ; +static sf_count_t host_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ; +static sf_count_t host_write_d2f (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ; + +static void float32_peak_update (SF_PRIVATE *psf, const float *buffer, int count, sf_count_t indx) ; + +static sf_count_t replace_read_f2s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ; +static sf_count_t replace_read_f2i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ; +static sf_count_t replace_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ; +static sf_count_t replace_read_f2d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ; + +static sf_count_t replace_write_s2f (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ; +static sf_count_t replace_write_i2f (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ; +static sf_count_t replace_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ; +static sf_count_t replace_write_d2f (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ; + +static void bf2f_array (float *buffer, int count) ; +static void f2bf_array (float *buffer, int count) ; + +static int float32_get_capability (SF_PRIVATE *psf) ; + +/*-------------------------------------------------------------------------------------------- +** Exported functions. +*/ + +int +float32_init (SF_PRIVATE *psf) +{ static int float_caps ; + + float_caps = float32_get_capability (psf) ; + + psf->blockwidth = sizeof (float) * psf->sf.channels ; + + if (psf->mode == SFM_READ || psf->mode == SFM_RDWR) + { switch (psf->endian + float_caps) + { case (SF_ENDIAN_BIG + FLOAT_CAN_RW_BE) : + psf->float_endswap = SF_FALSE ; + psf->read_short = host_read_f2s ; + psf->read_int = host_read_f2i ; + psf->read_float = host_read_f ; + psf->read_double = host_read_f2d ; + break ; + + case (SF_ENDIAN_LITTLE + FLOAT_CAN_RW_LE) : + psf->float_endswap = SF_FALSE ; + psf->read_short = host_read_f2s ; + psf->read_int = host_read_f2i ; + psf->read_float = host_read_f ; + psf->read_double = host_read_f2d ; + break ; + + case (SF_ENDIAN_BIG + FLOAT_CAN_RW_LE) : + psf->float_endswap = SF_TRUE ; + psf->read_short = host_read_f2s ; + psf->read_int = host_read_f2i ; + psf->read_float = host_read_f ; + psf->read_double = host_read_f2d ; + break ; + + case (SF_ENDIAN_LITTLE + FLOAT_CAN_RW_BE) : + psf->float_endswap = SF_TRUE ; + psf->read_short = host_read_f2s ; + psf->read_int = host_read_f2i ; + psf->read_float = host_read_f ; + psf->read_double = host_read_f2d ; + break ; + + /* When the CPU is not IEEE compatible. */ + case (SF_ENDIAN_BIG + FLOAT_BROKEN_LE) : + psf->float_endswap = SF_TRUE ; + psf->read_short = replace_read_f2s ; + psf->read_int = replace_read_f2i ; + psf->read_float = replace_read_f ; + psf->read_double = replace_read_f2d ; + break ; + + case (SF_ENDIAN_LITTLE + FLOAT_BROKEN_LE) : + psf->float_endswap = SF_FALSE ; + psf->read_short = replace_read_f2s ; + psf->read_int = replace_read_f2i ; + psf->read_float = replace_read_f ; + psf->read_double = replace_read_f2d ; + break ; + + case (SF_ENDIAN_BIG + FLOAT_BROKEN_BE) : + psf->float_endswap = SF_FALSE ; + psf->read_short = replace_read_f2s ; + psf->read_int = replace_read_f2i ; + psf->read_float = replace_read_f ; + psf->read_double = replace_read_f2d ; + break ; + + case (SF_ENDIAN_LITTLE + FLOAT_BROKEN_BE) : + psf->float_endswap = SF_TRUE ; + psf->read_short = replace_read_f2s ; + psf->read_int = replace_read_f2i ; + psf->read_float = replace_read_f ; + psf->read_double = replace_read_f2d ; + break ; + + default : break ; + } ; + } ; + + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + { switch (psf->endian + float_caps) + { case (SF_ENDIAN_LITTLE + FLOAT_CAN_RW_LE) : + psf->float_endswap = SF_FALSE ; + psf->write_short = host_write_s2f ; + psf->write_int = host_write_i2f ; + psf->write_float = host_write_f ; + psf->write_double = host_write_d2f ; + break ; + + case (SF_ENDIAN_BIG + FLOAT_CAN_RW_BE) : + psf->float_endswap = SF_FALSE ; + psf->write_short = host_write_s2f ; + psf->write_int = host_write_i2f ; + psf->write_float = host_write_f ; + psf->write_double = host_write_d2f ; + break ; + + case (SF_ENDIAN_BIG + FLOAT_CAN_RW_LE) : + psf->float_endswap = SF_TRUE ; + psf->write_short = host_write_s2f ; + psf->write_int = host_write_i2f ; + psf->write_float = host_write_f ; + psf->write_double = host_write_d2f ; + break ; + + case (SF_ENDIAN_LITTLE + FLOAT_CAN_RW_BE) : + psf->float_endswap = SF_TRUE ; + psf->write_short = host_write_s2f ; + psf->write_int = host_write_i2f ; + psf->write_float = host_write_f ; + psf->write_double = host_write_d2f ; + break ; + + /* When the CPU is not IEEE compatible. */ + case (SF_ENDIAN_BIG + FLOAT_BROKEN_LE) : + psf->float_endswap = SF_TRUE ; + psf->write_short = replace_write_s2f ; + psf->write_int = replace_write_i2f ; + psf->write_float = replace_write_f ; + psf->write_double = replace_write_d2f ; + break ; + + case (SF_ENDIAN_LITTLE + FLOAT_BROKEN_LE) : + psf->float_endswap = SF_FALSE ; + psf->write_short = replace_write_s2f ; + psf->write_int = replace_write_i2f ; + psf->write_float = replace_write_f ; + psf->write_double = replace_write_d2f ; + break ; + + case (SF_ENDIAN_BIG + FLOAT_BROKEN_BE) : + psf->float_endswap = SF_FALSE ; + psf->write_short = replace_write_s2f ; + psf->write_int = replace_write_i2f ; + psf->write_float = replace_write_f ; + psf->write_double = replace_write_d2f ; + break ; + + case (SF_ENDIAN_LITTLE + FLOAT_BROKEN_BE) : + psf->float_endswap = SF_TRUE ; + psf->write_short = replace_write_s2f ; + psf->write_int = replace_write_i2f ; + psf->write_float = replace_write_f ; + psf->write_double = replace_write_d2f ; + break ; + + default : break ; + } ; + } ; + + if (psf->filelength > psf->dataoffset) + { psf->datalength = (psf->dataend > 0) ? psf->dataend - psf->dataoffset : + psf->filelength - psf->dataoffset ; + } + else + psf->datalength = 0 ; + + psf->sf.frames = psf->datalength / psf->blockwidth ; + + return 0 ; +} /* float32_init */ + +float +float32_be_read (unsigned char *cptr) +{ int exponent, mantissa, negative ; + float fvalue ; + + negative = cptr [0] & 0x80 ; + exponent = ((cptr [0] & 0x7F) << 1) | ((cptr [1] & 0x80) ? 1 : 0) ; + mantissa = ((cptr [1] & 0x7F) << 16) | (cptr [2] << 8) | (cptr [3]) ; + + if (! (exponent || mantissa)) + return 0.0 ; + + mantissa |= 0x800000 ; + exponent = exponent ? exponent - 127 : 0 ; + + fvalue = mantissa ? ((float) mantissa) / ((float) 0x800000) : 0.0 ; + + if (negative) + fvalue *= -1 ; + + if (exponent > 0) + fvalue *= (1 << exponent) ; + else if (exponent < 0) + fvalue /= (1 << abs (exponent)) ; + + return fvalue ; +} /* float32_be_read */ + +float +float32_le_read (unsigned char *cptr) +{ int exponent, mantissa, negative ; + float fvalue ; + + negative = cptr [3] & 0x80 ; + exponent = ((cptr [3] & 0x7F) << 1) | ((cptr [2] & 0x80) ? 1 : 0) ; + mantissa = ((cptr [2] & 0x7F) << 16) | (cptr [1] << 8) | (cptr [0]) ; + + if (! (exponent || mantissa)) + return 0.0 ; + + mantissa |= 0x800000 ; + exponent = exponent ? exponent - 127 : 0 ; + + fvalue = mantissa ? ((float) mantissa) / ((float) 0x800000) : 0.0 ; + + if (negative) + fvalue *= -1 ; + + if (exponent > 0) + fvalue *= (1 << exponent) ; + else if (exponent < 0) + fvalue /= (1 << abs (exponent)) ; + + return fvalue ; +} /* float32_le_read */ + +void +float32_le_write (float in, unsigned char *out) +{ int exponent, mantissa, negative = 0 ; + + memset (out, 0, sizeof (int)) ; + + if (fabs (in) < 1e-30) + return ; + + if (in < 0.0) + { in *= -1.0 ; + negative = 1 ; + } ; + + in = frexp (in, &exponent) ; + + exponent += 126 ; + + in *= (float) 0x1000000 ; + mantissa = (((int) in) & 0x7FFFFF) ; + + if (negative) + out [3] |= 0x80 ; + + if (exponent & 0x01) + out [2] |= 0x80 ; + + out [0] = mantissa & 0xFF ; + out [1] = (mantissa >> 8) & 0xFF ; + out [2] |= (mantissa >> 16) & 0x7F ; + out [3] |= (exponent >> 1) & 0x7F ; + + return ; +} /* float32_le_write */ + +void +float32_be_write (float in, unsigned char *out) +{ int exponent, mantissa, negative = 0 ; + + memset (out, 0, sizeof (int)) ; + + if (fabs (in) < 1e-30) + return ; + + if (in < 0.0) + { in *= -1.0 ; + negative = 1 ; + } ; + + in = frexp (in, &exponent) ; + + exponent += 126 ; + + in *= (float) 0x1000000 ; + mantissa = (((int) in) & 0x7FFFFF) ; + + if (negative) + out [0] |= 0x80 ; + + if (exponent & 0x01) + out [1] |= 0x80 ; + + out [3] = mantissa & 0xFF ; + out [2] = (mantissa >> 8) & 0xFF ; + out [1] |= (mantissa >> 16) & 0x7F ; + out [0] |= (exponent >> 1) & 0x7F ; + + return ; +} /* float32_be_write */ + +/*============================================================================================== +** Private functions. +*/ + +static void +float32_peak_update (SF_PRIVATE *psf, const float *buffer, int count, sf_count_t indx) +{ int chan ; + int k, position ; + float fmaxval ; + + for (chan = 0 ; chan < psf->sf.channels ; chan++) + { fmaxval = fabs (buffer [chan]) ; + position = 0 ; + for (k = chan ; k < count ; k += psf->sf.channels) + if (fmaxval < fabs (buffer [k])) + { fmaxval = fabs (buffer [k]) ; + position = k ; + } ; + + if (fmaxval > psf->peak_info->peaks [chan].value) + { psf->peak_info->peaks [chan].value = fmaxval ; + psf->peak_info->peaks [chan].position = psf->write_current + indx + (position / psf->sf.channels) ; + } ; + } ; + + return ; +} /* float32_peak_update */ + +static int +float32_get_capability (SF_PRIVATE *psf) +{ union + { float f ; + int i ; + unsigned char c [4] ; + } data ; + + data.f = (float) 1.23456789 ; /* Some abitrary value. */ + + if (! psf->ieee_replace) + { /* If this test is true ints and floats are compatible and little endian. */ + if (data.c [0] == 0x52 && data.c [1] == 0x06 && data.c [2] == 0x9e && data.c [3] == 0x3f) + return FLOAT_CAN_RW_LE ; + + /* If this test is true ints and floats are compatible and big endian. */ + if (data.c [3] == 0x52 && data.c [2] == 0x06 && data.c [1] == 0x9e && data.c [0] == 0x3f) + return FLOAT_CAN_RW_BE ; + } ; + + /* Floats are broken. Don't expect reading or writing to be fast. */ + psf_log_printf (psf, "Using IEEE replacement code for float.\n") ; + + return (CPU_IS_LITTLE_ENDIAN) ? FLOAT_BROKEN_LE : FLOAT_BROKEN_BE ; +} /* float32_get_capability */ + +/*======================================================================================= +*/ + +static void +f2s_array (const float *src, int count, short *dest, float scale) +{ + while (--count >= 0) + { dest [count] = lrintf (scale * src [count]) ; + } ; +} /* f2s_array */ + +static void +f2s_clip_array (const float *src, int count, short *dest, float scale) +{ while (--count >= 0) + { float tmp = scale * src [count] ; + + if (CPU_CLIPS_POSITIVE == 0 && tmp > 32767.0) + dest [count] = SHRT_MAX ; + else if (CPU_CLIPS_NEGATIVE == 0 && tmp < 32768.0) + dest [count] = SHRT_MIN ; + else + dest [count] = lrintf (tmp) ; + } ; +} /* f2s_clip_array */ + +static inline void +f2i_array (const float *src, int count, int *dest, float scale) +{ while (--count >= 0) + { dest [count] = lrintf (scale * src [count]) ; + } ; +} /* f2i_array */ + +static inline void +f2i_clip_array (const float *src, int count, int *dest, float scale) +{ while (--count >= 0) + { float tmp = scale * src [count] ; + + if (CPU_CLIPS_POSITIVE == 0 && tmp > (1.0 * INT_MAX)) + dest [count] = INT_MAX ; + else if (CPU_CLIPS_NEGATIVE == 0 && tmp < (-1.0 * INT_MAX)) + dest [count] = INT_MIN ; + else + dest [count] = lrintf (tmp) ; + } ; +} /* f2i_clip_array */ + +static inline void +f2d_array (const float *src, int count, double *dest) +{ while (--count >= 0) + { dest [count] = src [count] ; + } ; +} /* f2d_array */ + +static inline void +s2f_array (const short *src, float *dest, int count) +{ while (--count >= 0) + { dest [count] = src [count] ; + } ; + +} /* s2f_array */ + +static inline void +i2f_array (const int *src, float *dest, int count) +{ while (--count >= 0) + { dest [count] = src [count] ; + } ; +} /* i2f_array */ + +static inline void +d2f_array (const double *src, float *dest, int count) +{ while (--count >= 0) + { dest [count] = src [count] ; + } ; +} /* d2f_array */ + +/*---------------------------------------------------------------------------------------------- +*/ + +static sf_count_t +host_read_f2s (SF_PRIVATE *psf, short *ptr, sf_count_t len) +{ void (*convert) (const float *, int, short *, float) ; + int bufferlen, readcount ; + sf_count_t total = 0 ; + float scale ; + + convert = (psf->add_clipping) ? f2s_clip_array : f2s_array ; + bufferlen = ARRAY_LEN (psf->u.fbuf) ; + scale = (psf->float_int_mult == 0) ? 1.0 : 0x7FFF / psf->float_max ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.fbuf, sizeof (float), bufferlen, psf) ; + +/* Fix me : Need lef2s_array */ + if (psf->float_endswap == SF_TRUE) + endswap_int_array (psf->u.ibuf, bufferlen) ; + + f2s_array (psf->u.fbuf, readcount, ptr + total, scale) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* host_read_f2s */ + +static sf_count_t +host_read_f2i (SF_PRIVATE *psf, int *ptr, sf_count_t len) +{ void (*convert) (const float *, int, int *, float) ; + int bufferlen, readcount ; + sf_count_t total = 0 ; + float scale ; + + convert = (psf->add_clipping) ? f2i_clip_array : f2i_array ; + bufferlen = ARRAY_LEN (psf->u.fbuf) ; + scale = (psf->float_int_mult == 0) ? 1.0 : 0x7FFFFFFF / psf->float_max ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.fbuf, sizeof (float), bufferlen, psf) ; + + if (psf->float_endswap == SF_TRUE) + endswap_int_array (psf->u.ibuf, bufferlen) ; + + convert (psf->u.fbuf, readcount, ptr + total, scale) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* host_read_f2i */ + +static sf_count_t +host_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + + if (psf->float_endswap != SF_TRUE) + return psf_fread (ptr, sizeof (float), len, psf) ; + + bufferlen = ARRAY_LEN (psf->u.fbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.fbuf, sizeof (float), bufferlen, psf) ; + + endswap_int_copy ((int*) (ptr + total), psf->u.ibuf, readcount) ; + + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* host_read_f */ + +static sf_count_t +host_read_f2d (SF_PRIVATE *psf, double *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + + bufferlen = ARRAY_LEN (psf->u.fbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.fbuf, sizeof (float), bufferlen, psf) ; + + if (psf->float_endswap == SF_TRUE) + endswap_int_array (psf->u.ibuf, bufferlen) ; + +/* Fix me : Need lef2d_array */ + f2d_array (psf->u.fbuf, readcount, ptr + total) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* host_read_f2d */ + +static sf_count_t +host_write_s2f (SF_PRIVATE *psf, const short *ptr, sf_count_t len) +{ int bufferlen, writecount ; + sf_count_t total = 0 ; + + bufferlen = ARRAY_LEN (psf->u.fbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + s2f_array (ptr + total, psf->u.fbuf, bufferlen) ; + + if (psf->peak_info) + float32_peak_update (psf, psf->u.fbuf, bufferlen, total / psf->sf.channels) ; + + if (psf->float_endswap == SF_TRUE) + endswap_int_array (psf->u.ibuf, bufferlen) ; + + writecount = psf_fwrite (psf->u.fbuf, sizeof (float), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* host_write_s2f */ + +static sf_count_t +host_write_i2f (SF_PRIVATE *psf, const int *ptr, sf_count_t len) +{ int bufferlen, writecount ; + sf_count_t total = 0 ; + + bufferlen = ARRAY_LEN (psf->u.fbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + i2f_array (ptr + total, psf->u.fbuf, bufferlen) ; + + if (psf->peak_info) + float32_peak_update (psf, psf->u.fbuf, bufferlen, total / psf->sf.channels) ; + + if (psf->float_endswap == SF_TRUE) + endswap_int_array (psf->u.ibuf, bufferlen) ; + + writecount = psf_fwrite (psf->u.fbuf, sizeof (float) , bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* host_write_i2f */ + +static sf_count_t +host_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len) +{ int bufferlen, writecount ; + sf_count_t total = 0 ; + + if (psf->peak_info) + float32_peak_update (psf, ptr, len, 0) ; + + if (psf->float_endswap != SF_TRUE) + return psf_fwrite (ptr, sizeof (float), len, psf) ; + + bufferlen = ARRAY_LEN (psf->u.fbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + + endswap_int_copy (psf->u.ibuf, (const int*) (ptr + total), bufferlen) ; + + writecount = psf_fwrite (psf->u.fbuf, sizeof (float), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* host_write_f */ + +static sf_count_t +host_write_d2f (SF_PRIVATE *psf, const double *ptr, sf_count_t len) +{ int bufferlen, writecount ; + sf_count_t total = 0 ; + + bufferlen = ARRAY_LEN (psf->u.fbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + + d2f_array (ptr + total, psf->u.fbuf, bufferlen) ; + + if (psf->peak_info) + float32_peak_update (psf, psf->u.fbuf, bufferlen, total / psf->sf.channels) ; + + if (psf->float_endswap == SF_TRUE) + endswap_int_array (psf->u.ibuf, bufferlen) ; + + writecount = psf_fwrite (psf->u.fbuf, sizeof (float), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* host_write_d2f */ + +/*======================================================================================= +*/ + +static sf_count_t +replace_read_f2s (SF_PRIVATE *psf, short *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + float scale ; + + bufferlen = ARRAY_LEN (psf->u.fbuf) ; + scale = (psf->float_int_mult == 0) ? 1.0 : 0x7FFF / psf->float_max ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.fbuf, sizeof (float), bufferlen, psf) ; + + if (psf->float_endswap == SF_TRUE) + endswap_int_array (psf->u.ibuf, bufferlen) ; + + bf2f_array (psf->u.fbuf, bufferlen) ; + + f2s_array (psf->u.fbuf, readcount, ptr + total, scale) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* replace_read_f2s */ + +static sf_count_t +replace_read_f2i (SF_PRIVATE *psf, int *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + float scale ; + + bufferlen = ARRAY_LEN (psf->u.fbuf) ; + scale = (psf->float_int_mult == 0) ? 1.0 : 0x7FFF / psf->float_max ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.fbuf, sizeof (float), bufferlen, psf) ; + + if (psf->float_endswap == SF_TRUE) + endswap_int_array (psf->u.ibuf, bufferlen) ; + + bf2f_array (psf->u.fbuf, bufferlen) ; + + f2i_array (psf->u.fbuf, readcount, ptr + total, scale) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* replace_read_f2i */ + +static sf_count_t +replace_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + + /* FIX THIS */ + + bufferlen = ARRAY_LEN (psf->u.fbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.fbuf, sizeof (float), bufferlen, psf) ; + + if (psf->float_endswap == SF_TRUE) + endswap_int_array (psf->u.ibuf, bufferlen) ; + + bf2f_array (psf->u.fbuf, bufferlen) ; + + memcpy (ptr + total, psf->u.fbuf, bufferlen * sizeof (float)) ; + + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* replace_read_f */ + +static sf_count_t +replace_read_f2d (SF_PRIVATE *psf, double *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + + bufferlen = ARRAY_LEN (psf->u.fbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.fbuf, sizeof (float), bufferlen, psf) ; + + if (psf->float_endswap == SF_TRUE) + endswap_int_array (psf->u.ibuf, bufferlen) ; + + bf2f_array (psf->u.fbuf, bufferlen) ; + + f2d_array (psf->u.fbuf, readcount, ptr + total) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* replace_read_f2d */ + +static sf_count_t +replace_write_s2f (SF_PRIVATE *psf, const short *ptr, sf_count_t len) +{ int bufferlen, writecount ; + sf_count_t total = 0 ; + + bufferlen = ARRAY_LEN (psf->u.fbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + s2f_array (ptr + total, psf->u.fbuf, bufferlen) ; + + if (psf->peak_info) + float32_peak_update (psf, psf->u.fbuf, bufferlen, total / psf->sf.channels) ; + + f2bf_array (psf->u.fbuf, bufferlen) ; + + if (psf->float_endswap == SF_TRUE) + endswap_int_array (psf->u.ibuf, bufferlen) ; + + writecount = psf_fwrite (psf->u.fbuf, sizeof (float), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* replace_write_s2f */ + +static sf_count_t +replace_write_i2f (SF_PRIVATE *psf, const int *ptr, sf_count_t len) +{ int bufferlen, writecount ; + sf_count_t total = 0 ; + + bufferlen = ARRAY_LEN (psf->u.fbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + i2f_array (ptr + total, psf->u.fbuf, bufferlen) ; + + if (psf->peak_info) + float32_peak_update (psf, psf->u.fbuf, bufferlen, total / psf->sf.channels) ; + + f2bf_array (psf->u.fbuf, bufferlen) ; + + if (psf->float_endswap == SF_TRUE) + endswap_int_array (psf->u.ibuf, bufferlen) ; + + writecount = psf_fwrite (psf->u.fbuf, sizeof (float), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* replace_write_i2f */ + +static sf_count_t +replace_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len) +{ int bufferlen, writecount ; + sf_count_t total = 0 ; + + /* FIX THIS */ + if (psf->peak_info) + float32_peak_update (psf, ptr, len, 0) ; + + bufferlen = ARRAY_LEN (psf->u.fbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + + memcpy (psf->u.fbuf, ptr + total, bufferlen * sizeof (float)) ; + + f2bf_array (psf->u.fbuf, bufferlen) ; + + if (psf->float_endswap == SF_TRUE) + endswap_int_array (psf->u.ibuf, bufferlen) ; + + writecount = psf_fwrite (psf->u.fbuf, sizeof (float) , bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* replace_write_f */ + +static sf_count_t +replace_write_d2f (SF_PRIVATE *psf, const double *ptr, sf_count_t len) +{ int bufferlen, writecount ; + sf_count_t total = 0 ; + + bufferlen = ARRAY_LEN (psf->u.fbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + d2f_array (ptr + total, psf->u.fbuf, bufferlen) ; + + if (psf->peak_info) + float32_peak_update (psf, psf->u.fbuf, bufferlen, total / psf->sf.channels) ; + + f2bf_array (psf->u.fbuf, bufferlen) ; + + if (psf->float_endswap == SF_TRUE) + endswap_int_array (psf->u.ibuf, bufferlen) ; + + writecount = psf_fwrite (psf->u.fbuf, sizeof (float), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* replace_write_d2f */ + +/*---------------------------------------------------------------------------------------------- +*/ + +static void +bf2f_array (float *buffer, int count) +{ while (--count >= 0) + { buffer [count] = FLOAT32_READ ((unsigned char *) (buffer + count)) ; + } ; +} /* bf2f_array */ + +static void +f2bf_array (float *buffer, int count) +{ while (--count >= 0) + { FLOAT32_WRITE (buffer [count], (unsigned char*) (buffer + count)) ; + } ; +} /* f2bf_array */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: b6c34917-488c-4145-9648-f4371fc4c889 +*/ diff --git a/src/float_cast.h b/src/float_cast.h new file mode 100644 index 00000000..099670a3 --- /dev/null +++ b/src/float_cast.h @@ -0,0 +1,262 @@ +/* +** Copyright (C) 2001-2004 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* Version 1.4 */ + +#ifndef FLOAT_CAST_HEADER +#define FLOAT_CAST_HEADER + +/*============================================================================ +** On Intel Pentium processors (especially PIII and probably P4), converting +** from float to int is very slow. To meet the C specs, the code produced by +** most C compilers targeting Pentium needs to change the FPU rounding mode +** before the float to int conversion is performed. +** +** Changing the FPU rounding mode causes the FPU pipeline to be flushed. It +** is this flushing of the pipeline which is so slow. +** +** Fortunately the ISO C99 specifications define the functions lrint, lrintf, +** llrint and llrintf which fix this problem as a side effect. +** +** On Unix-like systems, the configure process should have detected the +** presence of these functions. If they weren't found we have to replace them +** here with a standard C cast. +*/ + +/* +** The C99 prototypes for lrint and lrintf are as follows: +** +** long int lrintf (float x) ; +** long int lrint (double x) ; +*/ + +#include "sfconfig.h" + +/* +** The presence of the required functions are detected during the configure +** process and the values HAVE_LRINT and HAVE_LRINTF are set accordingly in +** the sfconfig.h file. +*/ + +#define HAVE_LRINT_REPLACEMENT 0 + +#if (HAVE_LRINT && HAVE_LRINTF) + + /* + ** These defines enable functionality introduced with the 1999 ISO C + ** standard. They must be defined before the inclusion of math.h to + ** engage them. If optimisation is enabled, these functions will be + ** inlined. With optimisation switched off, you have to link in the + ** maths library using -lm. + */ + + #define _ISOC9X_SOURCE 1 + #define _ISOC99_SOURCE 1 + + #define __USE_ISOC9X 1 + #define __USE_ISOC99 1 + + #include + +#elif (defined (__CYGWIN__)) + + #include + + #undef HAVE_LRINT_REPLACEMENT + #define HAVE_LRINT_REPLACEMENT 1 + + #undef lrint + #undef lrintf + + #define lrint double2int + #define lrintf float2int + + /* + ** The native CYGWIN lrint and lrintf functions are buggy: + ** http://sourceware.org/ml/cygwin/2005-06/msg00153.html + ** http://sourceware.org/ml/cygwin/2005-09/msg00047.html + ** and slow. + ** These functions (pulled from the Public Domain MinGW math.h header) + ** replace the native versions. + */ + + static inline long double2int (double in) + { long retval ; + + __asm__ __volatile__ + ( "fistpl %0" + : "=m" (retval) + : "t" (in) + : "st" + ) ; + + return retval ; + } /* double2int */ + + static inline long float2int (float in) + { long retval ; + + __asm__ __volatile__ + ( "fistpl %0" + : "=m" (retval) + : "t" (in) + : "st" + ) ; + + return retval ; + } /* float2int */ + +#elif (defined (WIN32) || defined (_WIN32)) + + #undef HAVE_LRINT_REPLACEMENT + #define HAVE_LRINT_REPLACEMENT 1 + + #include + + /* + ** Win32 doesn't seem to have these functions. + ** Therefore implement inline versions of these functions here. + */ + + __inline long int + lrint (double flt) + { int intgr ; + + _asm + { fld flt + fistp intgr + } ; + + return intgr ; + } + + __inline long int + lrintf (float flt) + { int intgr ; + + _asm + { fld flt + fistp intgr + } ; + + return intgr ; + } + +#elif (defined (__MWERKS__) && defined (macintosh)) + + /* This MacOS 9 solution was provided by Stephane Letz */ + + #undef HAVE_LRINT_REPLACEMENT + #define HAVE_LRINT_REPLACEMENT 1 + #include + + #undef lrint + #undef lrintf + + #define lrint double2int + #define lrintf float2int + + inline int + float2int (register float in) + { long res [2] ; + + asm + { fctiw in, in + stfd in, res + } + return res [1] ; + } /* float2int */ + + inline int + double2int (register double in) + { long res [2] ; + + asm + { fctiw in, in + stfd in, res + } + return res [1] ; + } /* double2int */ + +#elif (defined (__MACH__) && defined (__APPLE__)) + + /* For Apple MacOSX. */ + + #undef HAVE_LRINT_REPLACEMENT + #define HAVE_LRINT_REPLACEMENT 1 + #include + + #undef lrint + #undef lrintf + + #define lrint double2int + #define lrintf float2int + + inline static long + float2int (register float in) + { int res [2] ; + + __asm__ __volatile__ + ( "fctiw %1, %1\n\t" + "stfd %1, %0" + : "=m" (res) /* Output */ + : "f" (in) /* Input */ + : "memory" + ) ; + + return res [1] ; + } /* lrintf */ + + inline static long + double2int (register double in) + { int res [2] ; + + __asm__ __volatile__ + ( "fctiw %1, %1\n\t" + "stfd %1, %0" + : "=m" (res) /* Output */ + : "f" (in) /* Input */ + : "memory" + ) ; + + return res [1] ; + } /* lrint */ + +#else + #ifndef __sgi + #warning "Don't have the functions lrint() and lrintf()." + #warning "Replacing these functions with a standard C cast." + #endif + + #include + + #define lrint(dbl) ((long) (dbl)) + #define lrintf(flt) ((long) (flt)) + +#endif + + +#endif /* FLOAT_CAST_HEADER */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 42db1693-ff61-4051-bac1-e4d24c4e30b7 +*/ diff --git a/src/g72x.c b/src/g72x.c new file mode 100644 index 00000000..a33a926f --- /dev/null +++ b/src/g72x.c @@ -0,0 +1,611 @@ +/* +** Copyright (C) 1999-2005 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include + +#include "sndfile.h" +#include "sfendian.h" +#include "float_cast.h" +#include "common.h" +#include "G72x/g72x.h" + +/* This struct is private to the G72x code. */ +struct g72x_state ; +typedef struct g72x_state G72x_STATE ; + +typedef struct +{ /* Private data. Don't mess with it. */ + struct g72x_state * private ; + + /* Public data. Read only. */ + int blocksize, samplesperblock, bytesperblock ; + + /* Public data. Read and write. */ + int blocks_total, block_curr, sample_curr ; + unsigned char block [G72x_BLOCK_SIZE] ; + short samples [G72x_BLOCK_SIZE] ; +} G72x_PRIVATE ; + +static int psf_g72x_decode_block (SF_PRIVATE *psf, G72x_PRIVATE *pg72x) ; +static int psf_g72x_encode_block (SF_PRIVATE *psf, G72x_PRIVATE *pg72x) ; + +static sf_count_t g72x_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ; +static sf_count_t g72x_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ; +static sf_count_t g72x_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ; +static sf_count_t g72x_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ; + +static sf_count_t g72x_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ; +static sf_count_t g72x_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ; +static sf_count_t g72x_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ; +static sf_count_t g72x_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ; + +static sf_count_t g72x_seek (SF_PRIVATE *psf, int mode, sf_count_t offset) ; + +static int g72x_close (SF_PRIVATE *psf) ; + + +/*============================================================================================ +** WAV G721 Reader initialisation function. +*/ + +int +g72x_init (SF_PRIVATE * psf) +{ G72x_PRIVATE *pg72x ; + int bitspersample, bytesperblock, codec ; + + if (psf->codec_data != NULL) + { psf_log_printf (psf, "*** psf->codec_data is not NULL.\n") ; + return SFE_INTERNAL ; + } ; + + psf->sf.seekable = SF_FALSE ; + + if (psf->sf.channels != 1) + return SFE_G72X_NOT_MONO ; + + if ((pg72x = calloc (1, sizeof (G72x_PRIVATE))) == NULL) + return SFE_MALLOC_FAILED ; + + psf->codec_data = (void*) pg72x ; + + pg72x->block_curr = 0 ; + pg72x->sample_curr = 0 ; + + switch (psf->sf.format & SF_FORMAT_SUBMASK) + { case SF_FORMAT_G721_32 : + codec = G721_32_BITS_PER_SAMPLE ; + bytesperblock = G721_32_BYTES_PER_BLOCK ; + bitspersample = G721_32_BITS_PER_SAMPLE ; + break ; + + case SF_FORMAT_G723_24: + codec = G723_24_BITS_PER_SAMPLE ; + bytesperblock = G723_24_BYTES_PER_BLOCK ; + bitspersample = G723_24_BITS_PER_SAMPLE ; + break ; + + case SF_FORMAT_G723_40: + codec = G723_40_BITS_PER_SAMPLE ; + bytesperblock = G723_40_BYTES_PER_BLOCK ; + bitspersample = G723_40_BITS_PER_SAMPLE ; + break ; + + default : return SFE_UNIMPLEMENTED ; + } ; + + psf->blockwidth = psf->bytewidth = 1 ; + + psf->filelength = psf_get_filelen (psf) ; + if (psf->filelength < psf->dataoffset) + psf->filelength = psf->dataoffset ; + + psf->datalength = psf->filelength - psf->dataoffset ; + if (psf->dataend > 0) + psf->datalength -= psf->filelength - psf->dataend ; + + if (psf->mode == SFM_READ) + { pg72x->private = g72x_reader_init (codec, &(pg72x->blocksize), &(pg72x->samplesperblock)) ; + if (pg72x->private == NULL) + return SFE_MALLOC_FAILED ; + + pg72x->bytesperblock = bytesperblock ; + + psf->read_short = g72x_read_s ; + psf->read_int = g72x_read_i ; + psf->read_float = g72x_read_f ; + psf->read_double = g72x_read_d ; + + psf->seek = g72x_seek ; + + if (psf->datalength % pg72x->blocksize) + { psf_log_printf (psf, "*** Odd psf->datalength (%D) should be a multiple of %d\n", psf->datalength, pg72x->blocksize) ; + pg72x->blocks_total = (psf->datalength / pg72x->blocksize) + 1 ; + } + else + pg72x->blocks_total = psf->datalength / pg72x->blocksize ; + + psf->sf.frames = pg72x->blocks_total * pg72x->samplesperblock ; + + psf_g72x_decode_block (psf, pg72x) ; + } + else if (psf->mode == SFM_WRITE) + { pg72x->private = g72x_writer_init (codec, &(pg72x->blocksize), &(pg72x->samplesperblock)) ; + if (pg72x->private == NULL) + return SFE_MALLOC_FAILED ; + + pg72x->bytesperblock = bytesperblock ; + + psf->write_short = g72x_write_s ; + psf->write_int = g72x_write_i ; + psf->write_float = g72x_write_f ; + psf->write_double = g72x_write_d ; + + if (psf->datalength % pg72x->blocksize) + pg72x->blocks_total = (psf->datalength / pg72x->blocksize) + 1 ; + else + pg72x->blocks_total = psf->datalength / pg72x->blocksize ; + + if (psf->datalength > 0) + psf->sf.frames = (8 * psf->datalength) / bitspersample ; + + if ((psf->sf.frames * bitspersample) / 8 != psf->datalength) + psf_log_printf (psf, "*** Warning : weird psf->datalength.\n") ; + } ; + + psf->codec_close = g72x_close ; + + return 0 ; +} /* g72x_init */ + +/*============================================================================================ +** G721 Read Functions. +*/ + +static int +psf_g72x_decode_block (SF_PRIVATE *psf, G72x_PRIVATE *pg72x) +{ int k ; + + pg72x->block_curr ++ ; + pg72x->sample_curr = 0 ; + + if (pg72x->block_curr > pg72x->blocks_total) + { memset (pg72x->samples, 0, G72x_BLOCK_SIZE * sizeof (short)) ; + return 1 ; + } ; + + if ((k = psf_fread (pg72x->block, 1, pg72x->bytesperblock, psf)) != pg72x->bytesperblock) + psf_log_printf (psf, "*** Warning : short read (%d != %d).\n", k, pg72x->bytesperblock) ; + + pg72x->blocksize = k ; + g72x_decode_block (pg72x->private, pg72x->block, pg72x->samples) ; + + return 1 ; +} /* psf_g72x_decode_block */ + +static int +g72x_read_block (SF_PRIVATE *psf, G72x_PRIVATE *pg72x, short *ptr, int len) +{ int count, total = 0, indx = 0 ; + + while (indx < len) + { if (pg72x->block_curr > pg72x->blocks_total) + { memset (&(ptr [indx]), 0, (len - indx) * sizeof (short)) ; + return total ; + } ; + + if (pg72x->sample_curr >= pg72x->samplesperblock) + psf_g72x_decode_block (psf, pg72x) ; + + count = pg72x->samplesperblock - pg72x->sample_curr ; + count = (len - indx > count) ? count : len - indx ; + + memcpy (&(ptr [indx]), &(pg72x->samples [pg72x->sample_curr]), count * sizeof (short)) ; + indx += count ; + pg72x->sample_curr += count ; + total = indx ; + } ; + + return total ; +} /* g72x_read_block */ + +static sf_count_t +g72x_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len) +{ G72x_PRIVATE *pg72x ; + int readcount, count ; + sf_count_t total = 0 ; + + if (psf->codec_data == NULL) + return 0 ; + pg72x = (G72x_PRIVATE*) psf->codec_data ; + + while (len > 0) + { readcount = (len > 0x10000000) ? 0x10000000 : (int) len ; + + count = g72x_read_block (psf, pg72x, ptr, readcount) ; + + total += count ; + len -= count ; + + if (count != readcount) + break ; + } ; + + return total ; +} /* g72x_read_s */ + +static sf_count_t +g72x_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len) +{ G72x_PRIVATE *pg72x ; + short *sptr ; + int k, bufferlen, readcount = 0, count ; + sf_count_t total = 0 ; + + if (psf->codec_data == NULL) + return 0 ; + pg72x = (G72x_PRIVATE*) psf->codec_data ; + + sptr = psf->u.sbuf ; + bufferlen = SF_BUFFER_LEN / sizeof (short) ; + while (len > 0) + { readcount = (len >= bufferlen) ? bufferlen : len ; + count = g72x_read_block (psf, pg72x, sptr, readcount) ; + + for (k = 0 ; k < readcount ; k++) + ptr [total + k] = sptr [k] << 16 ; + + total += count ; + len -= readcount ; + if (count != readcount) + break ; + } ; + + return total ; +} /* g72x_read_i */ + +static sf_count_t +g72x_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len) +{ G72x_PRIVATE *pg72x ; + short *sptr ; + int k, bufferlen, readcount = 0, count ; + sf_count_t total = 0 ; + float normfact ; + + if (psf->codec_data == NULL) + return 0 ; + pg72x = (G72x_PRIVATE*) psf->codec_data ; + + normfact = (psf->norm_float == SF_TRUE) ? 1.0 / ((float) 0x8000) : 1.0 ; + + sptr = psf->u.sbuf ; + bufferlen = SF_BUFFER_LEN / sizeof (short) ; + while (len > 0) + { readcount = (len >= bufferlen) ? bufferlen : len ; + count = g72x_read_block (psf, pg72x, sptr, readcount) ; + for (k = 0 ; k < readcount ; k++) + ptr [total + k] = normfact * sptr [k] ; + + total += count ; + len -= readcount ; + if (count != readcount) + break ; + } ; + + return total ; +} /* g72x_read_f */ + +static sf_count_t +g72x_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len) +{ G72x_PRIVATE *pg72x ; + short *sptr ; + int k, bufferlen, readcount = 0, count ; + sf_count_t total = 0 ; + double normfact ; + + if (psf->codec_data == NULL) + return 0 ; + pg72x = (G72x_PRIVATE*) psf->codec_data ; + + normfact = (psf->norm_double == SF_TRUE) ? 1.0 / ((double) 0x8000) : 1.0 ; + + sptr = psf->u.sbuf ; + bufferlen = SF_BUFFER_LEN / sizeof (short) ; + while (len > 0) + { readcount = (len >= bufferlen) ? bufferlen : len ; + count = g72x_read_block (psf, pg72x, sptr, readcount) ; + for (k = 0 ; k < readcount ; k++) + ptr [total + k] = normfact * (double) (sptr [k]) ; + + total += count ; + len -= readcount ; + if (count != readcount) + break ; + } ; + + return total ; +} /* g72x_read_d */ + +static sf_count_t +g72x_seek (SF_PRIVATE *psf, int UNUSED (mode), sf_count_t UNUSED (offset)) +{ + psf_log_printf (psf, "seek unsupported\n") ; + + /* No simple solution. To do properly, would need to seek + ** to start of file and decode everything up to seek position. + ** Maybe implement SEEK_SET to 0 only? + */ + return 0 ; + +/* +** G72x_PRIVATE *pg72x ; +** int newblock, newsample, sample_curr ; +** +** if (psf->codec_data == NULL) +** return 0 ; +** pg72x = (G72x_PRIVATE*) psf->codec_data ; +** +** if (! (psf->datalength && psf->dataoffset)) +** { psf->error = SFE_BAD_SEEK ; +** return PSF_SEEK_ERROR ; +** } ; +** +** sample_curr = (8 * psf->datalength) / G721_32_BITS_PER_SAMPLE ; +** +** switch (whence) +** { case SEEK_SET : +** if (offset < 0 || offset > sample_curr) +** { psf->error = SFE_BAD_SEEK ; +** return PSF_SEEK_ERROR ; +** } ; +** newblock = offset / pg72x->samplesperblock ; +** newsample = offset % pg72x->samplesperblock ; +** break ; +** +** case SEEK_CUR : +** if (psf->current + offset < 0 || psf->current + offset > sample_curr) +** { psf->error = SFE_BAD_SEEK ; +** return PSF_SEEK_ERROR ; +** } ; +** newblock = (8 * (psf->current + offset)) / pg72x->samplesperblock ; +** newsample = (8 * (psf->current + offset)) % pg72x->samplesperblock ; +** break ; +** +** case SEEK_END : +** if (offset > 0 || sample_curr + offset < 0) +** { psf->error = SFE_BAD_SEEK ; +** return PSF_SEEK_ERROR ; +** } ; +** newblock = (sample_curr + offset) / pg72x->samplesperblock ; +** newsample = (sample_curr + offset) % pg72x->samplesperblock ; +** break ; +** +** default : +** psf->error = SFE_BAD_SEEK ; +** return PSF_SEEK_ERROR ; +** } ; +** +** if (psf->mode == SFM_READ) +** { psf_fseek (psf, psf->dataoffset + newblock * pg72x->blocksize, SEEK_SET) ; +** pg72x->block_curr = newblock ; +** psf_g72x_decode_block (psf, pg72x) ; +** pg72x->sample_curr = newsample ; +** } +** else +** { /+* What to do about write??? *+/ +** psf->error = SFE_BAD_SEEK ; +** return PSF_SEEK_ERROR ; +** } ; +** +** psf->current = newblock * pg72x->samplesperblock + newsample ; +** return psf->current ; +** +*/ +} /* g72x_seek */ + +/*========================================================================================== +** G72x Write Functions. +*/ + +static int +psf_g72x_encode_block (SF_PRIVATE *psf, G72x_PRIVATE *pg72x) +{ int k ; + + /* Encode the samples. */ + g72x_encode_block (pg72x->private, pg72x->samples, pg72x->block) ; + + /* Write the block to disk. */ + if ((k = psf_fwrite (pg72x->block, 1, pg72x->blocksize, psf)) != pg72x->blocksize) + psf_log_printf (psf, "*** Warning : short write (%d != %d).\n", k, pg72x->blocksize) ; + + pg72x->sample_curr = 0 ; + pg72x->block_curr ++ ; + + /* Set samples to zero for next block. */ + memset (pg72x->samples, 0, G72x_BLOCK_SIZE * sizeof (short)) ; + + return 1 ; +} /* psf_g72x_encode_block */ + +static int +g72x_write_block (SF_PRIVATE *psf, G72x_PRIVATE *pg72x, const short *ptr, int len) +{ int count, total = 0, indx = 0 ; + + while (indx < len) + { count = pg72x->samplesperblock - pg72x->sample_curr ; + + if (count > len - indx) + count = len - indx ; + + memcpy (&(pg72x->samples [pg72x->sample_curr]), &(ptr [indx]), count * sizeof (short)) ; + indx += count ; + pg72x->sample_curr += count ; + total = indx ; + + if (pg72x->sample_curr >= pg72x->samplesperblock) + psf_g72x_encode_block (psf, pg72x) ; + } ; + + return total ; +} /* g72x_write_block */ + +static sf_count_t +g72x_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len) +{ G72x_PRIVATE *pg72x ; + int writecount, count ; + sf_count_t total = 0 ; + + if (psf->codec_data == NULL) + return 0 ; + pg72x = (G72x_PRIVATE*) psf->codec_data ; + + while (len > 0) + { writecount = (len > 0x10000000) ? 0x10000000 : (int) len ; + + count = g72x_write_block (psf, pg72x, ptr, writecount) ; + + total += count ; + len -= count ; + if (count != writecount) + break ; + } ; + + return total ; +} /* g72x_write_s */ + +static sf_count_t +g72x_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t len) +{ G72x_PRIVATE *pg72x ; + short *sptr ; + int k, bufferlen, writecount = 0, count ; + sf_count_t total = 0 ; + + if (psf->codec_data == NULL) + return 0 ; + pg72x = (G72x_PRIVATE*) psf->codec_data ; + + sptr = psf->u.sbuf ; + bufferlen = ((SF_BUFFER_LEN / psf->blockwidth) * psf->blockwidth) / sizeof (short) ; + while (len > 0) + { writecount = (len >= bufferlen) ? bufferlen : len ; + for (k = 0 ; k < writecount ; k++) + sptr [k] = ptr [total + k] >> 16 ; + count = g72x_write_block (psf, pg72x, sptr, writecount) ; + + total += count ; + len -= writecount ; + if (count != writecount) + break ; + } ; + return total ; +} /* g72x_write_i */ + +static sf_count_t +g72x_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len) +{ G72x_PRIVATE *pg72x ; + short *sptr ; + int k, bufferlen, writecount = 0, count ; + sf_count_t total = 0 ; + float normfact ; + + if (psf->codec_data == NULL) + return 0 ; + pg72x = (G72x_PRIVATE*) psf->codec_data ; + + normfact = (psf->norm_float == SF_TRUE) ? (1.0 * 0x8000) : 1.0 ; + + sptr = psf->u.sbuf ; + bufferlen = ((SF_BUFFER_LEN / psf->blockwidth) * psf->blockwidth) / sizeof (short) ; + while (len > 0) + { writecount = (len >= bufferlen) ? bufferlen : len ; + for (k = 0 ; k < writecount ; k++) + sptr [k] = lrintf (normfact * ptr [total + k]) ; + count = g72x_write_block (psf, pg72x, sptr, writecount) ; + + total += count ; + len -= writecount ; + if (count != writecount) + break ; + } ; + + return total ; +} /* g72x_write_f */ + +static sf_count_t +g72x_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len) +{ G72x_PRIVATE *pg72x ; + short *sptr ; + int k, bufferlen, writecount = 0, count ; + sf_count_t total = 0 ; + double normfact ; + + if (psf->codec_data == NULL) + return 0 ; + pg72x = (G72x_PRIVATE*) psf->codec_data ; + + normfact = (psf->norm_double == SF_TRUE) ? (1.0 * 0x8000) : 1.0 ; + + sptr = psf->u.sbuf ; + bufferlen = ((SF_BUFFER_LEN / psf->blockwidth) * psf->blockwidth) / sizeof (short) ; + while (len > 0) + { writecount = (len >= bufferlen) ? bufferlen : len ; + for (k = 0 ; k < writecount ; k++) + sptr [k] = lrint (normfact * ptr [total + k]) ; + count = g72x_write_block (psf, pg72x, sptr, writecount) ; + + total += count ; + len -= writecount ; + if (count != writecount) + break ; + } ; + + return total ; +} /* g72x_write_d */ + +static int +g72x_close (SF_PRIVATE *psf) +{ G72x_PRIVATE *pg72x ; + + pg72x = (G72x_PRIVATE*) psf->codec_data ; + + if (psf->mode == SFM_WRITE) + { /* If a block has been partially assembled, write it out + ** as the final block. + */ + + if (pg72x->sample_curr && pg72x->sample_curr < G72x_BLOCK_SIZE) + psf_g72x_encode_block (psf, pg72x) ; + + if (psf->write_header) + psf->write_header (psf, SF_FALSE) ; + } ; + + /* Only free the pointer allocated by g72x_(reader|writer)_init. */ + free (pg72x->private) ; + + return 0 ; +} /* g72x_close */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 3cc5439e-7247-486b-b2e6-11a4affa5744 +*/ diff --git a/src/gsm610.c b/src/gsm610.c new file mode 100644 index 00000000..748f2d4f --- /dev/null +++ b/src/gsm610.c @@ -0,0 +1,626 @@ +/* +** Copyright (C) 1999-2006 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include + +#include "sndfile.h" +#include "sfendian.h" +#include "float_cast.h" +#include "common.h" +#include "wav_w64.h" +#include "GSM610/gsm.h" + +#define GSM610_BLOCKSIZE 33 +#define GSM610_SAMPLES 160 + +typedef struct gsm610_tag +{ int blocks ; + int blockcount, samplecount ; + int samplesperblock, blocksize ; + + int (*decode_block) (SF_PRIVATE *psf, struct gsm610_tag *pgsm610) ; + int (*encode_block) (SF_PRIVATE *psf, struct gsm610_tag *pgsm610) ; + + short samples [WAV_W64_GSM610_SAMPLES] ; + unsigned char block [WAV_W64_GSM610_BLOCKSIZE] ; + + /* Damn I hate typedef-ed pointers; yes, gsm is a pointer type. */ + gsm gsm_data ; +} GSM610_PRIVATE ; + +static sf_count_t gsm610_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ; +static sf_count_t gsm610_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ; +static sf_count_t gsm610_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ; +static sf_count_t gsm610_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ; + +static sf_count_t gsm610_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ; +static sf_count_t gsm610_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ; +static sf_count_t gsm610_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ; +static sf_count_t gsm610_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ; + +static int gsm610_read_block (SF_PRIVATE *psf, GSM610_PRIVATE *pgsm610, short *ptr, int len) ; +static int gsm610_write_block (SF_PRIVATE *psf, GSM610_PRIVATE *pgsm610, const short *ptr, int len) ; + +static int gsm610_decode_block (SF_PRIVATE *psf, GSM610_PRIVATE *pgsm610) ; +static int gsm610_encode_block (SF_PRIVATE *psf, GSM610_PRIVATE *pgsm610) ; + +static int gsm610_wav_decode_block (SF_PRIVATE *psf, GSM610_PRIVATE *pgsm610) ; +static int gsm610_wav_encode_block (SF_PRIVATE *psf, GSM610_PRIVATE *pgsm610) ; + +static sf_count_t gsm610_seek (SF_PRIVATE *psf, int mode, sf_count_t offset) ; + +static int gsm610_close (SF_PRIVATE *psf) ; + +/*============================================================================================ +** WAV GSM610 initialisation function. +*/ + +int +gsm610_init (SF_PRIVATE *psf) +{ GSM610_PRIVATE *pgsm610 ; + int true_flag = 1 ; + + if (psf->codec_data != NULL) + { psf_log_printf (psf, "*** psf->codec_data is not NULL.\n") ; + return SFE_INTERNAL ; + } ; + + if (psf->mode == SFM_RDWR) + return SFE_BAD_MODE_RW ; + + psf->sf.seekable = SF_FALSE ; + + if ((pgsm610 = calloc (1, sizeof (GSM610_PRIVATE))) == NULL) + return SFE_MALLOC_FAILED ; + + psf->codec_data = pgsm610 ; + + memset (pgsm610, 0, sizeof (GSM610_PRIVATE)) ; + +/*============================================================ + +Need separate gsm_data structs for encode and decode. + +============================================================*/ + + if ((pgsm610->gsm_data = gsm_create ()) == NULL) + return SFE_MALLOC_FAILED ; + + switch (psf->sf.format & SF_FORMAT_TYPEMASK) + { case SF_FORMAT_WAV : + case SF_FORMAT_WAVEX : + case SF_FORMAT_W64 : + gsm_option (pgsm610->gsm_data, GSM_OPT_WAV49, &true_flag) ; + + pgsm610->encode_block = gsm610_wav_encode_block ; + pgsm610->decode_block = gsm610_wav_decode_block ; + + pgsm610->samplesperblock = WAV_W64_GSM610_SAMPLES ; + pgsm610->blocksize = WAV_W64_GSM610_BLOCKSIZE ; + break ; + + case SF_FORMAT_AIFF : + case SF_FORMAT_RAW : + pgsm610->encode_block = gsm610_encode_block ; + pgsm610->decode_block = gsm610_decode_block ; + + pgsm610->samplesperblock = GSM610_SAMPLES ; + pgsm610->blocksize = GSM610_BLOCKSIZE ; + break ; + + default : + return SFE_INTERNAL ; + break ; + } ; + + if (psf->mode == SFM_READ) + { if (psf->datalength % pgsm610->blocksize == 0) + pgsm610->blocks = psf->datalength / pgsm610->blocksize ; + else if (psf->datalength % pgsm610->blocksize == 1 && pgsm610->blocksize == GSM610_BLOCKSIZE) + { /* + ** Weird AIFF specific case. + ** AIFF chunks must be at an even offset from the start of file and + ** GSM610_BLOCKSIZE is odd which can result in an odd length SSND + ** chunk. The SSND chunk then gets padded on write which means that + ** when it is read the datalength is too big by 1. + */ + pgsm610->blocks = psf->datalength / pgsm610->blocksize ; + } + else + { psf_log_printf (psf, "*** Warning : data chunk seems to be truncated.\n") ; + pgsm610->blocks = psf->datalength / pgsm610->blocksize + 1 ; + } ; + + psf->sf.frames = pgsm610->samplesperblock * pgsm610->blocks ; + + pgsm610->decode_block (psf, pgsm610) ; /* Read first block. */ + + psf->read_short = gsm610_read_s ; + psf->read_int = gsm610_read_i ; + psf->read_float = gsm610_read_f ; + psf->read_double = gsm610_read_d ; + } ; + + if (psf->mode == SFM_WRITE) + { pgsm610->blockcount = 0 ; + pgsm610->samplecount = 0 ; + + psf->write_short = gsm610_write_s ; + psf->write_int = gsm610_write_i ; + psf->write_float = gsm610_write_f ; + psf->write_double = gsm610_write_d ; + } ; + + psf->codec_close = gsm610_close ; + + psf->seek = gsm610_seek ; + + psf->filelength = psf_get_filelen (psf) ; + psf->datalength = psf->filelength - psf->dataoffset ; + + return 0 ; +} /* gsm610_init */ + +/*============================================================================================ +** GSM 6.10 Read Functions. +*/ + +static int +gsm610_wav_decode_block (SF_PRIVATE *psf, GSM610_PRIVATE *pgsm610) +{ int k ; + + pgsm610->blockcount ++ ; + pgsm610->samplecount = 0 ; + + if (pgsm610->blockcount > pgsm610->blocks) + { memset (pgsm610->samples, 0, WAV_W64_GSM610_SAMPLES * sizeof (short)) ; + return 1 ; + } ; + + if ((k = psf_fread (pgsm610->block, 1, WAV_W64_GSM610_BLOCKSIZE, psf)) != WAV_W64_GSM610_BLOCKSIZE) + psf_log_printf (psf, "*** Warning : short read (%d != %d).\n", k, WAV_W64_GSM610_BLOCKSIZE) ; + + if (gsm_decode (pgsm610->gsm_data, pgsm610->block, pgsm610->samples) < 0) + { psf_log_printf (psf, "Error from gsm_decode() on frame : %d\n", pgsm610->blockcount) ; + return 0 ; + } ; + + if (gsm_decode (pgsm610->gsm_data, pgsm610->block + (WAV_W64_GSM610_BLOCKSIZE + 1) / 2, pgsm610->samples + WAV_W64_GSM610_SAMPLES / 2) < 0) + { psf_log_printf (psf, "Error from gsm_decode() on frame : %d.5\n", pgsm610->blockcount) ; + return 0 ; + } ; + + return 1 ; +} /* gsm610_wav_decode_block */ + +static int +gsm610_decode_block (SF_PRIVATE *psf, GSM610_PRIVATE *pgsm610) +{ int k ; + + pgsm610->blockcount ++ ; + pgsm610->samplecount = 0 ; + + if (pgsm610->blockcount > pgsm610->blocks) + { memset (pgsm610->samples, 0, GSM610_SAMPLES * sizeof (short)) ; + return 1 ; + } ; + + if ((k = psf_fread (pgsm610->block, 1, GSM610_BLOCKSIZE, psf)) != GSM610_BLOCKSIZE) + psf_log_printf (psf, "*** Warning : short read (%d != %d).\n", k, GSM610_BLOCKSIZE) ; + + if (gsm_decode (pgsm610->gsm_data, pgsm610->block, pgsm610->samples) < 0) + { psf_log_printf (psf, "Error from gsm_decode() on frame : %d\n", pgsm610->blockcount) ; + return 0 ; + } ; + + return 1 ; +} /* gsm610_decode_block */ + +static int +gsm610_read_block (SF_PRIVATE *psf, GSM610_PRIVATE *pgsm610, short *ptr, int len) +{ int count, total = 0, indx = 0 ; + + while (indx < len) + { if (pgsm610->blockcount >= pgsm610->blocks && pgsm610->samplecount >= pgsm610->samplesperblock) + { memset (&(ptr [indx]), 0, (len - indx) * sizeof (short)) ; + return total ; + } ; + + if (pgsm610->samplecount >= pgsm610->samplesperblock) + pgsm610->decode_block (psf, pgsm610) ; + + count = pgsm610->samplesperblock - pgsm610->samplecount ; + count = (len - indx > count) ? count : len - indx ; + + memcpy (&(ptr [indx]), &(pgsm610->samples [pgsm610->samplecount]), count * sizeof (short)) ; + indx += count ; + pgsm610->samplecount += count ; + total = indx ; + } ; + + return total ; +} /* gsm610_read_block */ + +static sf_count_t +gsm610_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len) +{ GSM610_PRIVATE *pgsm610 ; + int readcount, count ; + sf_count_t total = 0 ; + + if (psf->codec_data == NULL) + return 0 ; + pgsm610 = (GSM610_PRIVATE*) psf->codec_data ; + + while (len > 0) + { readcount = (len > 0x10000000) ? 0x1000000 : (int) len ; + + count = gsm610_read_block (psf, pgsm610, ptr, readcount) ; + + total += count ; + len -= count ; + + if (count != readcount) + break ; + } ; + + return total ; +} /* gsm610_read_s */ + +static sf_count_t +gsm610_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len) +{ GSM610_PRIVATE *pgsm610 ; + short *sptr ; + int k, bufferlen, readcount = 0, count ; + sf_count_t total = 0 ; + + if (psf->codec_data == NULL) + return 0 ; + pgsm610 = (GSM610_PRIVATE*) psf->codec_data ; + + sptr = psf->u.sbuf ; + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + while (len > 0) + { readcount = (len >= bufferlen) ? bufferlen : len ; + count = gsm610_read_block (psf, pgsm610, sptr, readcount) ; + for (k = 0 ; k < readcount ; k++) + ptr [total + k] = sptr [k] << 16 ; + + total += count ; + len -= readcount ; + } ; + return total ; +} /* gsm610_read_i */ + +static sf_count_t +gsm610_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len) +{ GSM610_PRIVATE *pgsm610 ; + short *sptr ; + int k, bufferlen, readcount = 0, count ; + sf_count_t total = 0 ; + float normfact ; + + if (psf->codec_data == NULL) + return 0 ; + pgsm610 = (GSM610_PRIVATE*) psf->codec_data ; + + normfact = (psf->norm_float == SF_TRUE) ? 1.0 / ((float) 0x8000) : 1.0 ; + + sptr = psf->u.sbuf ; + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + while (len > 0) + { readcount = (len >= bufferlen) ? bufferlen : len ; + count = gsm610_read_block (psf, pgsm610, sptr, readcount) ; + for (k = 0 ; k < readcount ; k++) + ptr [total + k] = normfact * sptr [k] ; + + total += count ; + len -= readcount ; + } ; + return total ; +} /* gsm610_read_f */ + +static sf_count_t +gsm610_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len) +{ GSM610_PRIVATE *pgsm610 ; + short *sptr ; + int k, bufferlen, readcount = 0, count ; + sf_count_t total = 0 ; + double normfact ; + + normfact = (psf->norm_double == SF_TRUE) ? 1.0 / ((double) 0x8000) : 1.0 ; + + if (psf->codec_data == NULL) + return 0 ; + pgsm610 = (GSM610_PRIVATE*) psf->codec_data ; + + sptr = psf->u.sbuf ; + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + while (len > 0) + { readcount = (len >= bufferlen) ? bufferlen : len ; + count = gsm610_read_block (psf, pgsm610, sptr, readcount) ; + for (k = 0 ; k < readcount ; k++) + ptr [total + k] = normfact * sptr [k] ; + + total += count ; + len -= readcount ; + } ; + return total ; +} /* gsm610_read_d */ + +static sf_count_t +gsm610_seek (SF_PRIVATE *psf, int UNUSED (mode), sf_count_t offset) +{ GSM610_PRIVATE *pgsm610 ; + int newblock, newsample ; + + if (psf->codec_data == NULL) + return 0 ; + pgsm610 = (GSM610_PRIVATE*) psf->codec_data ; + + if (psf->dataoffset < 0) + { psf->error = SFE_BAD_SEEK ; + return PSF_SEEK_ERROR ; + } ; + + if (offset == 0) + { int true_flag = 1 ; + + psf_fseek (psf, psf->dataoffset, SEEK_SET) ; + pgsm610->blockcount = 0 ; + + gsm_init (pgsm610->gsm_data) ; + if ((psf->sf.format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV || + (psf->sf.format & SF_FORMAT_TYPEMASK) == SF_FORMAT_W64) + gsm_option (pgsm610->gsm_data, GSM_OPT_WAV49, &true_flag) ; + + pgsm610->decode_block (psf, pgsm610) ; + pgsm610->samplecount = 0 ; + return 0 ; + } ; + + if (offset < 0 || offset > pgsm610->blocks * pgsm610->samplesperblock) + { psf->error = SFE_BAD_SEEK ; + return PSF_SEEK_ERROR ; + } ; + + newblock = offset / pgsm610->samplesperblock ; + newsample = offset % pgsm610->samplesperblock ; + + if (psf->mode == SFM_READ) + { if (psf->read_current != newblock * pgsm610->samplesperblock + newsample) + { psf_fseek (psf, psf->dataoffset + newblock * pgsm610->samplesperblock, SEEK_SET) ; + pgsm610->blockcount = newblock ; + pgsm610->decode_block (psf, pgsm610) ; + pgsm610->samplecount = newsample ; + } ; + + return newblock * pgsm610->samplesperblock + newsample ; + } ; + + /* What to do about write??? */ + psf->error = SFE_BAD_SEEK ; + return PSF_SEEK_ERROR ; +} /* gsm610_seek */ + +/*========================================================================================== +** GSM 6.10 Write Functions. +*/ + +static int +gsm610_encode_block (SF_PRIVATE *psf, GSM610_PRIVATE *pgsm610) +{ int k ; + + /* Encode the samples. */ + gsm_encode (pgsm610->gsm_data, pgsm610->samples, pgsm610->block) ; + + /* Write the block to disk. */ + if ((k = psf_fwrite (pgsm610->block, 1, GSM610_BLOCKSIZE, psf)) != GSM610_BLOCKSIZE) + psf_log_printf (psf, "*** Warning : short write (%d != %d).\n", k, GSM610_BLOCKSIZE) ; + + pgsm610->samplecount = 0 ; + pgsm610->blockcount ++ ; + + /* Set samples to zero for next block. */ + memset (pgsm610->samples, 0, WAV_W64_GSM610_SAMPLES * sizeof (short)) ; + + return 1 ; +} /* gsm610_encode_block */ + +static int +gsm610_wav_encode_block (SF_PRIVATE *psf, GSM610_PRIVATE *pgsm610) +{ int k ; + + /* Encode the samples. */ + gsm_encode (pgsm610->gsm_data, pgsm610->samples, pgsm610->block) ; + gsm_encode (pgsm610->gsm_data, pgsm610->samples+WAV_W64_GSM610_SAMPLES/2, pgsm610->block+WAV_W64_GSM610_BLOCKSIZE/2) ; + + /* Write the block to disk. */ + if ((k = psf_fwrite (pgsm610->block, 1, WAV_W64_GSM610_BLOCKSIZE, psf)) != WAV_W64_GSM610_BLOCKSIZE) + psf_log_printf (psf, "*** Warning : short write (%d != %d).\n", k, WAV_W64_GSM610_BLOCKSIZE) ; + + pgsm610->samplecount = 0 ; + pgsm610->blockcount ++ ; + + /* Set samples to zero for next block. */ + memset (pgsm610->samples, 0, WAV_W64_GSM610_SAMPLES * sizeof (short)) ; + + return 1 ; +} /* gsm610_wav_encode_block */ + +static int +gsm610_write_block (SF_PRIVATE *psf, GSM610_PRIVATE *pgsm610, const short *ptr, int len) +{ int count, total = 0, indx = 0 ; + + while (indx < len) + { count = pgsm610->samplesperblock - pgsm610->samplecount ; + + if (count > len - indx) + count = len - indx ; + + memcpy (&(pgsm610->samples [pgsm610->samplecount]), &(ptr [indx]), count * sizeof (short)) ; + indx += count ; + pgsm610->samplecount += count ; + total = indx ; + + if (pgsm610->samplecount >= pgsm610->samplesperblock) + pgsm610->encode_block (psf, pgsm610) ; + } ; + + return total ; +} /* gsm610_write_block */ + +static sf_count_t +gsm610_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len) +{ GSM610_PRIVATE *pgsm610 ; + int writecount, count ; + sf_count_t total = 0 ; + + if (psf->codec_data == NULL) + return 0 ; + pgsm610 = (GSM610_PRIVATE*) psf->codec_data ; + + while (len > 0) + { writecount = (len > 0x10000000) ? 0x10000000 : (int) len ; + + count = gsm610_write_block (psf, pgsm610, ptr, writecount) ; + + total += count ; + len -= count ; + + if (count != writecount) + break ; + } ; + + return total ; +} /* gsm610_write_s */ + +static sf_count_t +gsm610_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t len) +{ GSM610_PRIVATE *pgsm610 ; + short *sptr ; + int k, bufferlen, writecount = 0, count ; + sf_count_t total = 0 ; + + if (psf->codec_data == NULL) + return 0 ; + pgsm610 = (GSM610_PRIVATE*) psf->codec_data ; + + sptr = psf->u.sbuf ; + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + while (len > 0) + { writecount = (len >= bufferlen) ? bufferlen : len ; + for (k = 0 ; k < writecount ; k++) + sptr [k] = ptr [total + k] >> 16 ; + count = gsm610_write_block (psf, pgsm610, sptr, writecount) ; + + total += count ; + len -= writecount ; + } ; + return total ; +} /* gsm610_write_i */ + +static sf_count_t +gsm610_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len) +{ GSM610_PRIVATE *pgsm610 ; + short *sptr ; + int k, bufferlen, writecount = 0, count ; + sf_count_t total = 0 ; + float normfact ; + + if (psf->codec_data == NULL) + return 0 ; + pgsm610 = (GSM610_PRIVATE*) psf->codec_data ; + + normfact = (psf->norm_float == SF_TRUE) ? (1.0 * 0x7FFF) : 1.0 ; + + sptr = psf->u.sbuf ; + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + while (len > 0) + { writecount = (len >= bufferlen) ? bufferlen : len ; + for (k = 0 ; k < writecount ; k++) + sptr [k] = lrintf (normfact * ptr [total + k]) ; + count = gsm610_write_block (psf, pgsm610, sptr, writecount) ; + + total += count ; + len -= writecount ; + } ; + return total ; +} /* gsm610_write_f */ + +static sf_count_t +gsm610_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len) +{ GSM610_PRIVATE *pgsm610 ; + short *sptr ; + int k, bufferlen, writecount = 0, count ; + sf_count_t total = 0 ; + double normfact ; + + if (psf->codec_data == NULL) + return 0 ; + pgsm610 = (GSM610_PRIVATE*) psf->codec_data ; + + normfact = (psf->norm_double == SF_TRUE) ? (1.0 * 0x7FFF) : 1.0 ; + + sptr = psf->u.sbuf ; + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + while (len > 0) + { writecount = (len >= bufferlen) ? bufferlen : len ; + for (k = 0 ; k < writecount ; k++) + sptr [k] = lrint (normfact * ptr [total + k]) ; + count = gsm610_write_block (psf, pgsm610, sptr, writecount) ; + + total += count ; + len -= writecount ; + } ; + return total ; +} /* gsm610_write_d */ + +static int +gsm610_close (SF_PRIVATE *psf) +{ GSM610_PRIVATE *pgsm610 ; + + if (psf->codec_data == NULL) + return 0 ; + + pgsm610 = (GSM610_PRIVATE*) psf->codec_data ; + + if (psf->mode == SFM_WRITE) + { /* If a block has been partially assembled, write it out + ** as the final block. + */ + + if (pgsm610->samplecount && pgsm610->samplecount < pgsm610->samplesperblock) + pgsm610->encode_block (psf, pgsm610) ; + } ; + + if (pgsm610->gsm_data) + gsm_destroy (pgsm610->gsm_data) ; + + return 0 ; +} /* gsm610_close */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 8575187d-af4f-4acf-b9dd-6ff705628345 +*/ diff --git a/src/htk.c b/src/htk.c new file mode 100644 index 00000000..716868b5 --- /dev/null +++ b/src/htk.c @@ -0,0 +1,225 @@ +/* +** Copyright (C) 2002-2004 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include +#include + +#include "sndfile.h" +#include "sfendian.h" +#include "common.h" + +/*------------------------------------------------------------------------------ +** Macros to handle big/little endian issues. +*/ + +#define SFE_HTK_BAD_FILE_LEN 1666 +#define SFE_HTK_NOT_WAVEFORM 1667 + +/*------------------------------------------------------------------------------ +** Private static functions. +*/ + +static int htk_close (SF_PRIVATE *psf) ; + +static int htk_write_header (SF_PRIVATE *psf, int calc_length) ; +static int htk_read_header (SF_PRIVATE *psf) ; + +/*------------------------------------------------------------------------------ +** Public function. +*/ + +int +htk_open (SF_PRIVATE *psf) +{ int subformat ; + int error = 0 ; + + if (psf->is_pipe) + return SFE_HTK_NO_PIPE ; + + if (psf->mode == SFM_READ || (psf->mode == SFM_RDWR && psf->filelength > 0)) + { if ((error = htk_read_header (psf))) + return error ; + } ; + + subformat = psf->sf.format & SF_FORMAT_SUBMASK ; + + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + { if ((psf->sf.format & SF_FORMAT_TYPEMASK) != SF_FORMAT_HTK) + return SFE_BAD_OPEN_FORMAT ; + + psf->endian = SF_ENDIAN_BIG ; + + if (htk_write_header (psf, SF_FALSE)) + return psf->error ; + + psf->write_header = htk_write_header ; + } ; + + psf->container_close = htk_close ; + + psf->blockwidth = psf->bytewidth * psf->sf.channels ; + + switch (subformat) + { case SF_FORMAT_PCM_16 : /* 16-bit linear PCM. */ + error = pcm_init (psf) ; + break ; + + default : break ; + } ; + + return error ; +} /* htk_open */ + +/*------------------------------------------------------------------------------ +*/ + +static int +htk_close (SF_PRIVATE *psf) +{ + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + htk_write_header (psf, SF_TRUE) ; + + return 0 ; +} /* htk_close */ + +static int +htk_write_header (SF_PRIVATE *psf, int calc_length) +{ sf_count_t current ; + int sample_count, sample_period ; + + current = psf_ftell (psf) ; + + if (calc_length) + psf->filelength = psf_get_filelen (psf) ; + + /* Reset the current header length to zero. */ + psf->header [0] = 0 ; + psf->headindex = 0 ; + psf_fseek (psf, 0, SEEK_SET) ; + + if (psf->filelength > 12) + sample_count = (psf->filelength - 12) / 2 ; + else + sample_count = 0 ; + + sample_period = 10000000 / psf->sf.samplerate ; + + psf_binheader_writef (psf, "E444", sample_count, sample_period, 0x20000) ; + + /* Header construction complete so write it out. */ + psf_fwrite (psf->header, psf->headindex, 1, psf) ; + + if (psf->error) + return psf->error ; + + psf->dataoffset = psf->headindex ; + + if (current > 0) + psf_fseek (psf, current, SEEK_SET) ; + + return psf->error ; +} /* htk_write_header */ + +/* +** Found the following info in a comment block within Bill Schottstaedt's +** sndlib library. +** +** HTK format files consist of a contiguous sequence of samples preceded by a +** header. Each sample is a vector of either 2-byte integers or 4-byte floats. +** 2-byte integers are used for compressed forms as described below and for +** vector quantised data as described later in section 5.11. HTK format data +** files can also be used to store speech waveforms as described in section 5.8. +** +** The HTK file format header is 12 bytes long and contains the following data +** nSamples -- number of samples in file (4-byte integer) +** sampPeriod -- sample period in 100ns units (4-byte integer) +** sampSize -- number of bytes per sample (2-byte integer) +** parmKind -- a code indicating the sample kind (2-byte integer) +** +** The parameter kind consists of a 6 bit code representing the basic +** parameter kind plus additional bits for each of the possible qualifiers. +** The basic parameter kind codes are +** +** 0 WAVEFORM sampled waveform +** 1 LPC linear prediction filter coefficients +** 2 LPREFC linear prediction reflection coefficients +** 3 LPCEPSTRA LPC cepstral coefficients +** 4 LPDELCEP LPC cepstra plus delta coefficients +** 5 IREFC LPC reflection coef in 16 bit integer format +** 6 MFCC mel-frequency cepstral coefficients +** 7 FBANK log mel-filter bank channel outputs +** 8 MELSPEC linear mel-filter bank channel outputs +** 9 USER user defined sample kind +** 10 DISCRETE vector quantised data +** +** and the bit-encoding for the qualifiers (in octal) is +** _E 000100 has energy +** _N 000200 absolute energy suppressed +** _D 000400 has delta coefficients +** _A 001000 has acceleration coefficients +** _C 002000 is compressed +** _Z 004000 has zero mean static coef. +** _K 010000 has CRC checksum +** _O 020000 has 0'th cepstral coef. +*/ + +static int +htk_read_header (SF_PRIVATE *psf) +{ int sample_count, sample_period, marker ; + + psf_binheader_readf (psf, "pE444", 0, &sample_count, &sample_period, &marker) ; + + if (2 * sample_count + 12 != psf->filelength) + return SFE_HTK_BAD_FILE_LEN ; + + if (marker != 0x20000) + return SFE_HTK_NOT_WAVEFORM ; + + psf->sf.channels = 1 ; + psf->sf.samplerate = 10000000 / sample_period ; + + psf_log_printf (psf, "HTK Waveform file\n Sample Count : %d\n Sample Period : %d => %d Hz\n", + sample_count, sample_period, psf->sf.samplerate) ; + + psf->sf.format = SF_FORMAT_HTK | SF_FORMAT_PCM_16 ; + psf->bytewidth = 2 ; + + /* HTK always has a 12 byte header. */ + psf->dataoffset = 12 ; + psf->endian = SF_ENDIAN_BIG ; + + psf->datalength = psf->filelength - psf->dataoffset ; + + psf->blockwidth = psf->sf.channels * psf->bytewidth ; + + if (! psf->sf.frames && psf->blockwidth) + psf->sf.frames = (psf->filelength - psf->dataoffset) / psf->blockwidth ; + + return 0 ; +} /* htk_read_header */ +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: c350e972-082e-4c20-8934-03391a723560 +*/ diff --git a/src/ima_adpcm.c b/src/ima_adpcm.c new file mode 100644 index 00000000..49ffabe3 --- /dev/null +++ b/src/ima_adpcm.c @@ -0,0 +1,976 @@ +/* +** Copyright (C) 1999-2005 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include + +#include "sndfile.h" +#include "sfendian.h" +#include "float_cast.h" +#include "common.h" + +typedef struct IMA_ADPCM_PRIVATE_tag +{ int (*decode_block) (SF_PRIVATE *psf, struct IMA_ADPCM_PRIVATE_tag *pima) ; + int (*encode_block) (SF_PRIVATE *psf, struct IMA_ADPCM_PRIVATE_tag *pima) ; + + int channels, blocksize, samplesperblock, blocks ; + int blockcount, samplecount ; + int previous [2] ; + int stepindx [2] ; + unsigned char *block ; + short *samples ; +#if HAVE_FLEXIBLE_ARRAY + short data [] ; /* ISO C99 struct flexible array. */ +#else + short data [0] ; /* This is a hack and might not work. */ +#endif +} IMA_ADPCM_PRIVATE ; + +/*============================================================================================ +** Predefined IMA ADPCM data. +*/ + +static int ima_indx_adjust [16] = +{ -1, -1, -1, -1, /* +0 - +3, decrease the step size */ + 2, 4, 6, 8, /* +4 - +7, increase the step size */ + -1, -1, -1, -1, /* -0 - -3, decrease the step size */ + 2, 4, 6, 8, /* -4 - -7, increase the step size */ +} ; + +static int ima_step_size [89] = +{ 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, + 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230, + 253, 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, 876, 963, + 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, + 3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493, 10442, + 11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, + 32767 +} ; + +static int ima_reader_init (SF_PRIVATE *psf, int blockalign, int samplesperblock) ; +static int ima_writer_init (SF_PRIVATE *psf, int blockalign) ; + +static int ima_read_block (SF_PRIVATE *psf, IMA_ADPCM_PRIVATE *pima, short *ptr, int len) ; +static int ima_write_block (SF_PRIVATE *psf, IMA_ADPCM_PRIVATE *pima, const short *ptr, int len) ; + +static sf_count_t ima_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ; +static sf_count_t ima_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ; +static sf_count_t ima_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ; +static sf_count_t ima_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ; + +static sf_count_t ima_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ; +static sf_count_t ima_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ; +static sf_count_t ima_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ; +static sf_count_t ima_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ; + +static sf_count_t ima_seek (SF_PRIVATE *psf, int mode, sf_count_t offset) ; + +static int ima_close (SF_PRIVATE *psf) ; + +static int wav_w64_ima_decode_block (SF_PRIVATE *psf, IMA_ADPCM_PRIVATE *pima) ; +static int wav_w64_ima_encode_block (SF_PRIVATE *psf, IMA_ADPCM_PRIVATE *pima) ; + +/*-static int aiff_ima_reader_init (SF_PRIVATE *psf, int blockalign, int samplesperblock) ;-*/ +static int aiff_ima_decode_block (SF_PRIVATE *psf, IMA_ADPCM_PRIVATE *pima) ; +static int aiff_ima_encode_block (SF_PRIVATE *psf, IMA_ADPCM_PRIVATE *pima) ; + + +/*============================================================================================ +** IMA ADPCM Reader initialisation function. +*/ + +int +wav_w64_ima_init (SF_PRIVATE *psf, int blockalign, int samplesperblock) +{ int error ; + + if (psf->codec_data != NULL) + { psf_log_printf (psf, "*** psf->codec_data is not NULL.\n") ; + return SFE_INTERNAL ; + } ; + + if (psf->mode == SFM_RDWR) + return SFE_BAD_MODE_RW ; + + if (psf->mode == SFM_READ) + if ((error = ima_reader_init (psf, blockalign, samplesperblock))) + return error ; + + if (psf->mode == SFM_WRITE) + if ((error = ima_writer_init (psf, blockalign))) + return error ; + + psf->codec_close = ima_close ; + psf->seek = ima_seek ; + + return 0 ; +} /* wav_w64_ima_init */ + +int +aiff_ima_init (SF_PRIVATE *psf, int blockalign, int samplesperblock) +{ int error ; + + if (psf->mode == SFM_RDWR) + return SFE_BAD_MODE_RW ; + + if (psf->mode == SFM_READ) + if ((error = ima_reader_init (psf, blockalign, samplesperblock))) + return error ; + + if (psf->mode == SFM_WRITE) + if ((error = ima_writer_init (psf, blockalign))) + return error ; + + psf->codec_close = ima_close ; + + return 0 ; +} /* aiff_ima_init */ + +static int +ima_close (SF_PRIVATE *psf) +{ IMA_ADPCM_PRIVATE *pima ; + + pima = (IMA_ADPCM_PRIVATE*) psf->codec_data ; + + if (psf->mode == SFM_WRITE) + { /* If a block has been partially assembled, write it out + ** as the final block. + */ + if (pima->samplecount && pima->samplecount < pima->samplesperblock) + pima->encode_block (psf, pima) ; + + psf->sf.frames = pima->samplesperblock * pima->blockcount / psf->sf.channels ; + } ; + + return 0 ; +} /* ima_close */ + +/*============================================================================================ +** IMA ADPCM Read Functions. +*/ + +static int +ima_reader_init (SF_PRIVATE *psf, int blockalign, int samplesperblock) +{ IMA_ADPCM_PRIVATE *pima ; + int pimasize, count ; + + if (psf->mode != SFM_READ) + return SFE_BAD_MODE_RW ; + + pimasize = sizeof (IMA_ADPCM_PRIVATE) + blockalign * psf->sf.channels + 3 * psf->sf.channels * samplesperblock ; + + if (! (pima = malloc (pimasize))) + return SFE_MALLOC_FAILED ; + + psf->codec_data = (void*) pima ; + + memset (pima, 0, pimasize) ; + + pima->samples = pima->data ; + pima->block = (unsigned char*) (pima->data + samplesperblock * psf->sf.channels) ; + + pima->channels = psf->sf.channels ; + pima->blocksize = blockalign ; + pima->samplesperblock = samplesperblock ; + + psf->filelength = psf_get_filelen (psf) ; + psf->datalength = (psf->dataend) ? psf->dataend - psf->dataoffset : + psf->filelength - psf->dataoffset ; + + if (psf->datalength % pima->blocksize) + pima->blocks = psf->datalength / pima->blocksize + 1 ; + else + pima->blocks = psf->datalength / pima->blocksize ; + + switch (psf->sf.format & SF_FORMAT_TYPEMASK) + { case SF_FORMAT_WAV : + case SF_FORMAT_W64 : + count = 2 * (pima->blocksize - 4 * pima->channels) / pima->channels + 1 ; + + if (pima->samplesperblock != count) + psf_log_printf (psf, "*** Warning : samplesperblock should be %d.\n", count) ; + + pima->decode_block = wav_w64_ima_decode_block ; + + psf->sf.frames = pima->samplesperblock * pima->blocks ; + break ; + + case SF_FORMAT_AIFF : + psf_log_printf (psf, "still need to check block count\n") ; + pima->decode_block = aiff_ima_decode_block ; + psf->sf.frames = pima->samplesperblock * pima->blocks / pima->channels ; + break ; + + default : + psf_log_printf (psf, "ima_reader_init: bad psf->sf.format\n") ; + return SFE_INTERNAL ; + break ; + } ; + + pima->decode_block (psf, pima) ; /* Read first block. */ + + psf->read_short = ima_read_s ; + psf->read_int = ima_read_i ; + psf->read_float = ima_read_f ; + psf->read_double = ima_read_d ; + + return 0 ; +} /* ima_reader_init */ + +static int +aiff_ima_decode_block (SF_PRIVATE *psf, IMA_ADPCM_PRIVATE *pima) +{ unsigned char *blockdata ; + int chan, k, diff, bytecode ; + short step, stepindx, predictor, *sampledata ; + +static int count = 0 ; +count ++ ; + + pima->blockcount += pima->channels ; + pima->samplecount = 0 ; + + if (pima->blockcount > pima->blocks) + { memset (pima->samples, 0, pima->samplesperblock * pima->channels * sizeof (short)) ; + return 1 ; + } ; + + if ((k = psf_fread (pima->block, 1, pima->blocksize * pima->channels, psf)) != pima->blocksize * pima->channels) + psf_log_printf (psf, "*** Warning : short read (%d != %d).\n", k, pima->blocksize) ; + + /* Read and check the block header. */ + for (chan = 0 ; chan < pima->channels ; chan++) + { blockdata = pima->block + chan * 34 ; + sampledata = pima->samples + chan ; + + predictor = (blockdata [0] << 8) | (blockdata [1] & 0x80) ; + stepindx = blockdata [1] & 0x7F ; + +{ +if (count < 5) +printf ("\nchan: %d predictor: %d stepindx: %d (%d)\n", + chan, predictor, stepindx, ima_step_size [stepindx]) ; +} + /* FIXME : Do this a better way. */ + if (stepindx < 0) stepindx = 0 ; + else if (stepindx > 88) stepindx = 88 ; + + /* + ** Pull apart the packed 4 bit samples and store them in their + ** correct sample positions. + */ + for (k = 0 ; k < pima->blocksize - 2 ; k++) + { bytecode = blockdata [k + 2] ; + sampledata [pima->channels * (2 * k + 0)] = bytecode & 0xF ; + sampledata [pima->channels * (2 * k + 1)] = (bytecode >> 4) & 0xF ; + } ; + + /* Decode the encoded 4 bit samples. */ + for (k = 0 ; k < pima->samplesperblock ; k ++) + { step = ima_step_size [stepindx] ; + + bytecode = pima->samples [pima->channels * k + chan] ; + + stepindx += ima_indx_adjust [bytecode] ; + + if (stepindx < 0) stepindx = 0 ; + else if (stepindx > 88) stepindx = 88 ; + + diff = step >> 3 ; + if (bytecode & 1) diff += step >> 2 ; + if (bytecode & 2) diff += step >> 1 ; + if (bytecode & 4) diff += step ; + if (bytecode & 8) diff = -diff ; + + predictor += diff ; + + pima->samples [pima->channels * k + chan] = predictor ; + } ; + } ; + +if (count < 5) +{ + for (k = 0 ; k < 10 ; k++) + printf ("% 7d,", pima->samples [k]) ; + puts ("") ; +} + + return 1 ; +} /* aiff_ima_decode_block */ + +static int +aiff_ima_encode_block (SF_PRIVATE *psf, IMA_ADPCM_PRIVATE *pima) +{ int chan, k, step, diff, vpdiff, blockindx, indx ; + short bytecode, mask ; + +static int count = 0 ; +if (0 && count == 0) +{ pima->samples [0] = 0 ; + printf ("blocksize : %d\n", pima->blocksize) ; + printf ("pima->stepindx [0] : %d\n", pima->stepindx [0]) ; + } +count ++ ; + + /* Encode the block header. */ + for (chan = 0 ; chan < pima->channels ; chan ++) + { blockindx = chan * pima->blocksize ; + + pima->block [blockindx] = (pima->samples [chan] >> 8) & 0xFF ; + pima->block [blockindx + 1] = (pima->samples [chan] & 0x80) + (pima->stepindx [chan] & 0x7F) ; + + pima->previous [chan] = pima->samples [chan] ; + } ; + + /* Encode second and later samples for every block as a 4 bit value. */ + for (k = pima->channels ; k < (pima->samplesperblock * pima->channels) ; k ++) + { chan = (pima->channels > 1) ? (k % 2) : 0 ; + + diff = pima->samples [k] - pima->previous [chan] ; + + bytecode = 0 ; + step = ima_step_size [pima->stepindx [chan]] ; + vpdiff = step >> 3 ; + if (diff < 0) + { bytecode = 8 ; + diff = -diff ; + } ; + mask = 4 ; + while (mask) + { if (diff >= step) + { bytecode |= mask ; + diff -= step ; + vpdiff += step ; + } ; + step >>= 1 ; + mask >>= 1 ; + } ; + + if (bytecode & 8) + pima->previous [chan] -= vpdiff ; + else + pima->previous [chan] += vpdiff ; + + if (pima->previous [chan] > 32767) + pima->previous [chan] = 32767 ; + else if (pima->previous [chan] < -32768) + pima->previous [chan] = -32768 ; + + pima->stepindx [chan] += ima_indx_adjust [bytecode] ; + if (pima->stepindx [chan] < 0) + pima->stepindx [chan] = 0 ; + else if (pima->stepindx [chan] > 88) + pima->stepindx [chan] = 88 ; + + pima->samples [k] = bytecode ; + } ; + + /* Pack the 4 bit encoded samples. */ + + for (chan = 0 ; chan < pima->channels ; chan ++) + { for (indx = pima->channels ; indx < pima->channels * pima->samplesperblock ; indx += 2 * pima->channels) + { blockindx = chan * pima->blocksize + 2 + indx / 2 ; + +if (0 && count ++ < 5) + printf ("chan: %d blockindx: %3d indx: %3d\n", chan, blockindx, indx) ; + + pima->block [blockindx] = pima->samples [indx] & 0x0F ; + pima->block [blockindx] |= (pima->samples [indx + pima->channels] << 4) & 0xF0 ; + } ; + } ; + + /* Write the block to disk. */ + + if ((k = psf_fwrite (pima->block, 1, pima->channels * pima->blocksize, psf)) != pima->channels * pima->blocksize) + psf_log_printf (psf, "*** Warning : short write (%d != %d).\n", k, pima->channels * pima->blocksize) ; + + memset (pima->samples, 0, pima->channels * pima->samplesperblock * sizeof (short)) ; + pima->samplecount = 0 ; + pima->blockcount ++ ; + + return 1 ; +} /* aiff_ima_encode_block */ + +static int +wav_w64_ima_decode_block (SF_PRIVATE *psf, IMA_ADPCM_PRIVATE *pima) +{ int chan, k, current, blockindx, indx, indxstart, diff ; + short step, bytecode, stepindx [2] ; + + pima->blockcount ++ ; + pima->samplecount = 0 ; + + if (pima->blockcount > pima->blocks) + { memset (pima->samples, 0, pima->samplesperblock * pima->channels * sizeof (short)) ; + return 1 ; + } ; + + if ((k = psf_fread (pima->block, 1, pima->blocksize, psf)) != pima->blocksize) + psf_log_printf (psf, "*** Warning : short read (%d != %d).\n", k, pima->blocksize) ; + + /* Read and check the block header. */ + + for (chan = 0 ; chan < pima->channels ; chan++) + { current = pima->block [chan*4] | (pima->block [chan*4+1] << 8) ; + if (current & 0x8000) + current -= 0x10000 ; + + stepindx [chan] = pima->block [chan*4+2] ; + if (stepindx [chan] < 0) + stepindx [chan] = 0 ; + else if (stepindx [chan] > 88) + stepindx [chan] = 88 ; + + if (pima->block [chan*4+3] != 0) + psf_log_printf (psf, "IMA ADPCM synchronisation error.\n") ; + + pima->samples [chan] = current ; + } ; + + /* + ** Pull apart the packed 4 bit samples and store them in their + ** correct sample positions. + */ + + blockindx = 4 * pima->channels ; + + indxstart = pima->channels ; + while (blockindx < pima->blocksize) + { for (chan = 0 ; chan < pima->channels ; chan++) + { indx = indxstart + chan ; + for (k = 0 ; k < 4 ; k++) + { bytecode = pima->block [blockindx++] ; + pima->samples [indx] = bytecode & 0x0F ; + indx += pima->channels ; + pima->samples [indx] = (bytecode >> 4) & 0x0F ; + indx += pima->channels ; + } ; + } ; + indxstart += 8 * pima->channels ; + } ; + + /* Decode the encoded 4 bit samples. */ + + for (k = pima->channels ; k < (pima->samplesperblock * pima->channels) ; k ++) + { chan = (pima->channels > 1) ? (k % 2) : 0 ; + + bytecode = pima->samples [k] & 0xF ; + + step = ima_step_size [stepindx [chan]] ; + current = pima->samples [k - pima->channels] ; + + diff = step >> 3 ; + if (bytecode & 1) + diff += step >> 2 ; + if (bytecode & 2) + diff += step >> 1 ; + if (bytecode & 4) + diff += step ; + if (bytecode & 8) + diff = -diff ; + + current += diff ; + + if (current > 32767) + current = 32767 ; + else if (current < -32768) + current = -32768 ; + + stepindx [chan] += ima_indx_adjust [bytecode] ; + + if (stepindx [chan] < 0) + stepindx [chan] = 0 ; + else if (stepindx [chan] > 88) + stepindx [chan] = 88 ; + + pima->samples [k] = current ; + } ; + + return 1 ; +} /* wav_w64_ima_decode_block */ + +static int +wav_w64_ima_encode_block (SF_PRIVATE *psf, IMA_ADPCM_PRIVATE *pima) +{ int chan, k, step, diff, vpdiff, blockindx, indx, indxstart ; + short bytecode, mask ; + + /* Encode the block header. */ + for (chan = 0 ; chan < pima->channels ; chan++) + { pima->block [chan*4] = pima->samples [chan] & 0xFF ; + pima->block [chan*4+1] = (pima->samples [chan] >> 8) & 0xFF ; + + pima->block [chan*4+2] = pima->stepindx [chan] ; + pima->block [chan*4+3] = 0 ; + + pima->previous [chan] = pima->samples [chan] ; + } ; + + /* Encode the samples as 4 bit. */ + + for (k = pima->channels ; k < (pima->samplesperblock * pima->channels) ; k ++) + { chan = (pima->channels > 1) ? (k % 2) : 0 ; + + diff = pima->samples [k] - pima->previous [chan] ; + + bytecode = 0 ; + step = ima_step_size [pima->stepindx [chan]] ; + vpdiff = step >> 3 ; + if (diff < 0) + { bytecode = 8 ; + diff = -diff ; + } ; + mask = 4 ; + while (mask) + { if (diff >= step) + { bytecode |= mask ; + diff -= step ; + vpdiff += step ; + } ; + step >>= 1 ; + mask >>= 1 ; + } ; + + if (bytecode & 8) + pima->previous [chan] -= vpdiff ; + else + pima->previous [chan] += vpdiff ; + + if (pima->previous [chan] > 32767) + pima->previous [chan] = 32767 ; + else if (pima->previous [chan] < -32768) + pima->previous [chan] = -32768 ; + + pima->stepindx [chan] += ima_indx_adjust [bytecode] ; + if (pima->stepindx [chan] < 0) + pima->stepindx [chan] = 0 ; + else if (pima->stepindx [chan] > 88) + pima->stepindx [chan] = 88 ; + + pima->samples [k] = bytecode ; + } ; + + /* Pack the 4 bit encoded samples. */ + + blockindx = 4 * pima->channels ; + + indxstart = pima->channels ; + while (blockindx < pima->blocksize) + { for (chan = 0 ; chan < pima->channels ; chan++) + { indx = indxstart + chan ; + for (k = 0 ; k < 4 ; k++) + { pima->block [blockindx] = pima->samples [indx] & 0x0F ; + indx += pima->channels ; + pima->block [blockindx] |= (pima->samples [indx] << 4) & 0xF0 ; + indx += pima->channels ; + blockindx ++ ; + } ; + } ; + indxstart += 8 * pima->channels ; + } ; + + /* Write the block to disk. */ + + if ((k = psf_fwrite (pima->block, 1, pima->blocksize, psf)) != pima->blocksize) + psf_log_printf (psf, "*** Warning : short write (%d != %d).\n", k, pima->blocksize) ; + + memset (pima->samples, 0, pima->samplesperblock * sizeof (short)) ; + pima->samplecount = 0 ; + pima->blockcount ++ ; + + return 1 ; +} /* wav_w64_ima_encode_block */ + +static int +ima_read_block (SF_PRIVATE *psf, IMA_ADPCM_PRIVATE *pima, short *ptr, int len) +{ int count, total = 0, indx = 0 ; + + while (indx < len) + { if (pima->blockcount >= pima->blocks && pima->samplecount >= pima->samplesperblock) + { memset (&(ptr [indx]), 0, (size_t) ((len - indx) * sizeof (short))) ; + return total ; + } ; + + if (pima->samplecount >= pima->samplesperblock) + pima->decode_block (psf, pima) ; + + count = (pima->samplesperblock - pima->samplecount) * pima->channels ; + count = (len - indx > count) ? count : len - indx ; + + memcpy (&(ptr [indx]), &(pima->samples [pima->samplecount * pima->channels]), count * sizeof (short)) ; + indx += count ; + pima->samplecount += count / pima->channels ; + total = indx ; + } ; + + return total ; +} /* ima_read_block */ + +static sf_count_t +ima_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len) +{ IMA_ADPCM_PRIVATE *pima ; + int readcount, count ; + sf_count_t total = 0 ; + + if (! psf->codec_data) + return 0 ; + pima = (IMA_ADPCM_PRIVATE*) psf->codec_data ; + + while (len > 0) + { readcount = (len > 0x10000000) ? 0x10000000 : (int) len ; + + count = ima_read_block (psf, pima, ptr, readcount) ; + + total += count ; + len -= count ; + if (count != readcount) + break ; + } ; + + return total ; +} /* ima_read_s */ + +static sf_count_t +ima_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len) +{ IMA_ADPCM_PRIVATE *pima ; + short *sptr ; + int k, bufferlen, readcount, count ; + sf_count_t total = 0 ; + + if (! psf->codec_data) + return 0 ; + pima = (IMA_ADPCM_PRIVATE*) psf->codec_data ; + + sptr = psf->u.sbuf ; + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + while (len > 0) + { readcount = (len >= bufferlen) ? bufferlen : (int) len ; + count = ima_read_block (psf, pima, sptr, readcount) ; + for (k = 0 ; k < readcount ; k++) + ptr [total + k] = ((int) sptr [k]) << 16 ; + total += count ; + len -= readcount ; + if (count != readcount) + break ; + } ; + + return total ; +} /* ima_read_i */ + +static sf_count_t +ima_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len) +{ IMA_ADPCM_PRIVATE *pima ; + short *sptr ; + int k, bufferlen, readcount, count ; + sf_count_t total = 0 ; + float normfact ; + + if (! psf->codec_data) + return 0 ; + pima = (IMA_ADPCM_PRIVATE*) psf->codec_data ; + + normfact = (psf->norm_float == SF_TRUE) ? 1.0 / ((float) 0x8000) : 1.0 ; + + sptr = psf->u.sbuf ; + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + while (len > 0) + { readcount = (len >= bufferlen) ? bufferlen : (int) len ; + count = ima_read_block (psf, pima, sptr, readcount) ; + for (k = 0 ; k < readcount ; k++) + ptr [total + k] = normfact * (float) (sptr [k]) ; + total += count ; + len -= readcount ; + if (count != readcount) + break ; + } ; + + return total ; +} /* ima_read_f */ + +static sf_count_t +ima_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len) +{ IMA_ADPCM_PRIVATE *pima ; + short *sptr ; + int k, bufferlen, readcount, count ; + sf_count_t total = 0 ; + double normfact ; + + if (! psf->codec_data) + return 0 ; + pima = (IMA_ADPCM_PRIVATE*) psf->codec_data ; + + normfact = (psf->norm_double == SF_TRUE) ? 1.0 / ((double) 0x8000) : 1.0 ; + + sptr = psf->u.sbuf ; + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + while (len > 0) + { readcount = (len >= bufferlen) ? bufferlen : (int) len ; + count = ima_read_block (psf, pima, sptr, readcount) ; + for (k = 0 ; k < readcount ; k++) + ptr [total + k] = normfact * (double) (sptr [k]) ; + total += count ; + len -= readcount ; + if (count != readcount) + break ; + } ; + + return total ; +} /* ima_read_d */ + +static sf_count_t +ima_seek (SF_PRIVATE *psf, int mode, sf_count_t offset) +{ IMA_ADPCM_PRIVATE *pima ; + int newblock, newsample ; + + if (! psf->codec_data) + return 0 ; + pima = (IMA_ADPCM_PRIVATE*) psf->codec_data ; + + if (psf->datalength < 0 || psf->dataoffset < 0) + { psf->error = SFE_BAD_SEEK ; + return PSF_SEEK_ERROR ; + } ; + + if (offset == 0) + { psf_fseek (psf, psf->dataoffset, SEEK_SET) ; + pima->blockcount = 0 ; + pima->decode_block (psf, pima) ; + pima->samplecount = 0 ; + return 0 ; + } ; + + if (offset < 0 || offset > pima->blocks * pima->samplesperblock) + { psf->error = SFE_BAD_SEEK ; + return PSF_SEEK_ERROR ; + } ; + + newblock = offset / pima->samplesperblock ; + newsample = offset % pima->samplesperblock ; + + if (mode == SFM_READ) + { psf_fseek (psf, psf->dataoffset + newblock * pima->blocksize, SEEK_SET) ; + pima->blockcount = newblock ; + pima->decode_block (psf, pima) ; + pima->samplecount = newsample ; + } + else + { /* What to do about write??? */ + psf->error = SFE_BAD_SEEK ; + return PSF_SEEK_ERROR ; + } ; + + return newblock * pima->samplesperblock + newsample ; +} /* ima_seek */ + +/*========================================================================================== +** IMA ADPCM Write Functions. +*/ + +static int +ima_writer_init (SF_PRIVATE *psf, int blockalign) +{ IMA_ADPCM_PRIVATE *pima ; + int samplesperblock ; + unsigned int pimasize ; + + if (psf->mode != SFM_WRITE) + return SFE_BAD_MODE_RW ; + + samplesperblock = 2 * (blockalign - 4 * psf->sf.channels) / psf->sf.channels + 1 ; + + pimasize = sizeof (IMA_ADPCM_PRIVATE) + blockalign + 3 * psf->sf.channels * samplesperblock ; + + if ((pima = calloc (1, pimasize)) == NULL) + return SFE_MALLOC_FAILED ; + + psf->codec_data = (void*) pima ; + + pima->channels = psf->sf.channels ; + pima->blocksize = blockalign ; + pima->samplesperblock = samplesperblock ; + + pima->block = (unsigned char*) pima->data ; + pima->samples = (short*) (pima->data + blockalign) ; + + pima->samplecount = 0 ; + + switch (psf->sf.format & SF_FORMAT_TYPEMASK) + { case SF_FORMAT_WAV : + case SF_FORMAT_W64 : + pima->encode_block = wav_w64_ima_encode_block ; + break ; + + case SF_FORMAT_AIFF : + pima->encode_block = aiff_ima_encode_block ; + break ; + + default : + psf_log_printf (psf, "ima_reader_init: bad psf->sf.format\n") ; + return SFE_INTERNAL ; + break ; + } ; + + psf->write_short = ima_write_s ; + psf->write_int = ima_write_i ; + psf->write_float = ima_write_f ; + psf->write_double = ima_write_d ; + + return 0 ; +} /* ima_writer_init */ + +/*========================================================================================== +*/ + +static int +ima_write_block (SF_PRIVATE *psf, IMA_ADPCM_PRIVATE *pima, const short *ptr, int len) +{ int count, total = 0, indx = 0 ; + + while (indx < len) + { count = (pima->samplesperblock - pima->samplecount) * pima->channels ; + + if (count > len - indx) + count = len - indx ; + + memcpy (&(pima->samples [pima->samplecount * pima->channels]), &(ptr [total]), count * sizeof (short)) ; + indx += count ; + pima->samplecount += count / pima->channels ; + total = indx ; + + if (pima->samplecount >= pima->samplesperblock) + pima->encode_block (psf, pima) ; + } ; + + return total ; +} /* ima_write_block */ + +static sf_count_t +ima_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len) +{ IMA_ADPCM_PRIVATE *pima ; + int writecount, count ; + sf_count_t total = 0 ; + + if (! psf->codec_data) + return 0 ; + pima = (IMA_ADPCM_PRIVATE*) psf->codec_data ; + + while (len) + { writecount = (len > 0x10000000) ? 0x10000000 : (int) len ; + + count = ima_write_block (psf, pima, ptr, writecount) ; + + total += count ; + len -= count ; + if (count != writecount) + break ; + } ; + + return total ; +} /* ima_write_s */ + +static sf_count_t +ima_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t len) +{ IMA_ADPCM_PRIVATE *pima ; + short *sptr ; + int k, bufferlen, writecount, count ; + sf_count_t total = 0 ; + + if (! psf->codec_data) + return 0 ; + pima = (IMA_ADPCM_PRIVATE*) psf->codec_data ; + + sptr = psf->u.sbuf ; + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + while (len > 0) + { writecount = (len >= bufferlen) ? bufferlen : (int) len ; + for (k = 0 ; k < writecount ; k++) + sptr [k] = ptr [total + k] >> 16 ; + count = ima_write_block (psf, pima, sptr, writecount) ; + total += count ; + len -= writecount ; + if (count != writecount) + break ; + } ; + + return total ; +} /* ima_write_i */ + +static sf_count_t +ima_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len) +{ IMA_ADPCM_PRIVATE *pima ; + short *sptr ; + int k, bufferlen, writecount, count ; + sf_count_t total = 0 ; + float normfact ; + + if (! psf->codec_data) + return 0 ; + pima = (IMA_ADPCM_PRIVATE*) psf->codec_data ; + + normfact = (psf->norm_float == SF_TRUE) ? (1.0 * 0x7FFF) : 1.0 ; + + sptr = psf->u.sbuf ; + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + while (len > 0) + { writecount = (len >= bufferlen) ? bufferlen : (int) len ; + for (k = 0 ; k < writecount ; k++) + sptr [k] = lrintf (normfact * ptr [total + k]) ; + count = ima_write_block (psf, pima, sptr, writecount) ; + total += count ; + len -= writecount ; + if (count != writecount) + break ; + } ; + + return total ; +} /* ima_write_f */ + +static sf_count_t +ima_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len) +{ IMA_ADPCM_PRIVATE *pima ; + short *sptr ; + int k, bufferlen, writecount, count ; + sf_count_t total = 0 ; + double normfact ; + + if (! psf->codec_data) + return 0 ; + pima = (IMA_ADPCM_PRIVATE*) psf->codec_data ; + + normfact = (psf->norm_double == SF_TRUE) ? (1.0 * 0x7FFF) : 1.0 ; + + sptr = psf->u.sbuf ; + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + while (len > 0) + { writecount = (len >= bufferlen) ? bufferlen : (int) len ; + for (k = 0 ; k < writecount ; k++) + sptr [k] = lrint (normfact * ptr [total + k]) ; + count = ima_write_block (psf, pima, sptr, writecount) ; + total += count ; + len -= writecount ; + if (count != writecount) + break ; + } ; + + return total ; +} /* ima_write_d */ + + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 75a54b82-ad18-4758-9933-64e00a7f24e0 +*/ diff --git a/src/interleave.c b/src/interleave.c new file mode 100644 index 00000000..7c18bd46 --- /dev/null +++ b/src/interleave.c @@ -0,0 +1,306 @@ +/* +** Copyright (C) 2002-2004 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfendian.h" + +#include + +#include "sndfile.h" +#include "common.h" + +#define INTERLEAVE_CHANNELS 6 + +typedef struct +{ double buffer [SF_BUFFER_LEN / sizeof (double)] ; + + sf_count_t channel_len ; + + sf_count_t (*read_short) (SF_PRIVATE*, short *ptr, sf_count_t len) ; + sf_count_t (*read_int) (SF_PRIVATE*, int *ptr, sf_count_t len) ; + sf_count_t (*read_float) (SF_PRIVATE*, float *ptr, sf_count_t len) ; + sf_count_t (*read_double) (SF_PRIVATE*, double *ptr, sf_count_t len) ; + +} INTERLEAVE_DATA ; + + + +static sf_count_t interleave_read_short (SF_PRIVATE *psf, short *ptr, sf_count_t len) ; +static sf_count_t interleave_read_int (SF_PRIVATE *psf, int *ptr, sf_count_t len) ; +static sf_count_t interleave_read_float (SF_PRIVATE *psf, float *ptr, sf_count_t len) ; +static sf_count_t interleave_read_double (SF_PRIVATE *psf, double *ptr, sf_count_t len) ; + +static sf_count_t interleave_seek (SF_PRIVATE*, int mode, sf_count_t samples_from_start) ; + + + + +int +interleave_init (SF_PRIVATE *psf) +{ INTERLEAVE_DATA *pdata ; + + if (psf->mode != SFM_READ) + return SFE_INTERLEAVE_MODE ; + + if (psf->interleave) + { psf_log_printf (psf, "*** Weird, already have interleave.\n") ; + return 666 ; + } ; + + /* Free this in sf_close() function. */ + if (! (pdata = malloc (sizeof (INTERLEAVE_DATA)))) + return SFE_MALLOC_FAILED ; + +puts ("interleave_init") ; + + psf->interleave = pdata ; + + /* Save the existing methods. */ + pdata->read_short = psf->read_short ; + pdata->read_int = psf->read_int ; + pdata->read_float = psf->read_float ; + pdata->read_double = psf->read_double ; + + pdata->channel_len = psf->sf.frames * psf->bytewidth ; + + /* Insert our new methods. */ + psf->read_short = interleave_read_short ; + psf->read_int = interleave_read_int ; + psf->read_float = interleave_read_float ; + psf->read_double = interleave_read_double ; + + psf->seek = interleave_seek ; + + return 0 ; +} /* pcm_interleave_init */ + +/*------------------------------------------------------------------------------ +*/ + +static sf_count_t +interleave_read_short (SF_PRIVATE *psf, short *ptr, sf_count_t len) +{ INTERLEAVE_DATA *pdata ; + sf_count_t offset, templen ; + int chan, count, k ; + short *inptr, *outptr ; + + if (! (pdata = psf->interleave)) + return 0 ; + + inptr = (short*) pdata->buffer ; + + for (chan = 0 ; chan < psf->sf.channels ; chan++) + { outptr = ptr + chan ; + + offset = psf->dataoffset + chan * psf->bytewidth * psf->read_current ; + + if (psf_fseek (psf, offset, SEEK_SET) != offset) + { psf->error = SFE_INTERLEAVE_SEEK ; + return 0 ; + } ; + + templen = len / psf->sf.channels ; + + while (templen > 0) + { if (templen > SIGNED_SIZEOF (pdata->buffer) / SIGNED_SIZEOF (short)) + count = SIGNED_SIZEOF (pdata->buffer) / SIGNED_SIZEOF (short) ; + else + count = (int) templen ; + + if (pdata->read_short (psf, inptr, count) != count) + { psf->error = SFE_INTERLEAVE_READ ; + return 0 ; + } ; + + for (k = 0 ; k < count ; k++) + { *outptr = inptr [k] ; + outptr += psf->sf.channels ; + } ; + + templen -= count ; + } ; + } ; + + return len ; +} /* interleave_read_short */ + +static sf_count_t +interleave_read_int (SF_PRIVATE *psf, int *ptr, sf_count_t len) +{ INTERLEAVE_DATA *pdata ; + sf_count_t offset, templen ; + int chan, count, k ; + int *inptr, *outptr ; + + if (! (pdata = psf->interleave)) + return 0 ; + + inptr = (int*) pdata->buffer ; + + for (chan = 0 ; chan < psf->sf.channels ; chan++) + { outptr = ptr + chan ; + + offset = psf->dataoffset + chan * psf->bytewidth * psf->read_current ; + + if (psf_fseek (psf, offset, SEEK_SET) != offset) + { psf->error = SFE_INTERLEAVE_SEEK ; + return 0 ; + } ; + + templen = len / psf->sf.channels ; + + while (templen > 0) + { if (templen > SIGNED_SIZEOF (pdata->buffer) / SIGNED_SIZEOF (int)) + count = SIGNED_SIZEOF (pdata->buffer) / SIGNED_SIZEOF (int) ; + else + count = (int) templen ; + + if (pdata->read_int (psf, inptr, count) != count) + { psf->error = SFE_INTERLEAVE_READ ; + return 0 ; + } ; + + for (k = 0 ; k < count ; k++) + { *outptr = inptr [k] ; + outptr += psf->sf.channels ; + } ; + + templen -= count ; + } ; + } ; + + return len ; +} /* interleave_read_int */ + +static sf_count_t +interleave_read_float (SF_PRIVATE *psf, float *ptr, sf_count_t len) +{ INTERLEAVE_DATA *pdata ; + sf_count_t offset, templen ; + int chan, count, k ; + float *inptr, *outptr ; + + if (! (pdata = psf->interleave)) + return 0 ; + + inptr = (float*) pdata->buffer ; + + for (chan = 0 ; chan < psf->sf.channels ; chan++) + { outptr = ptr + chan ; + + offset = psf->dataoffset + pdata->channel_len * chan + psf->read_current * psf->bytewidth ; + +/*-printf ("chan : %d read_current : %6lld offset : %6lld\n", chan, psf->read_current, offset) ;-*/ + + if (psf_fseek (psf, offset, SEEK_SET) != offset) + { psf->error = SFE_INTERLEAVE_SEEK ; +/*-puts ("interleave_seek error") ; exit (1) ;-*/ + return 0 ; + } ; + + templen = len / psf->sf.channels ; + + while (templen > 0) + { if (templen > SIGNED_SIZEOF (pdata->buffer) / SIGNED_SIZEOF (float)) + count = SIGNED_SIZEOF (pdata->buffer) / SIGNED_SIZEOF (float) ; + else + count = (int) templen ; + + if (pdata->read_float (psf, inptr, count) != count) + { psf->error = SFE_INTERLEAVE_READ ; +/*-puts ("interleave_read error") ; exit (1) ;-*/ + return 0 ; + } ; + + for (k = 0 ; k < count ; k++) + { *outptr = inptr [k] ; + outptr += psf->sf.channels ; + } ; + + templen -= count ; + } ; + } ; + + return len ; +} /* interleave_read_float */ + +static sf_count_t +interleave_read_double (SF_PRIVATE *psf, double *ptr, sf_count_t len) +{ INTERLEAVE_DATA *pdata ; + sf_count_t offset, templen ; + int chan, count, k ; + double *inptr, *outptr ; + + if (! (pdata = psf->interleave)) + return 0 ; + + inptr = (double*) pdata->buffer ; + + for (chan = 0 ; chan < psf->sf.channels ; chan++) + { outptr = ptr + chan ; + + offset = psf->dataoffset + chan * psf->bytewidth * psf->read_current ; + + if (psf_fseek (psf, offset, SEEK_SET) != offset) + { psf->error = SFE_INTERLEAVE_SEEK ; + return 0 ; + } ; + + templen = len / psf->sf.channels ; + + while (templen > 0) + { if (templen > SIGNED_SIZEOF (pdata->buffer) / SIGNED_SIZEOF (double)) + count = SIGNED_SIZEOF (pdata->buffer) / SIGNED_SIZEOF (double) ; + else + count = (int) templen ; + + if (pdata->read_double (psf, inptr, count) != count) + { psf->error = SFE_INTERLEAVE_READ ; + return 0 ; + } ; + + for (k = 0 ; k < count ; k++) + { *outptr = inptr [k] ; + outptr += psf->sf.channels ; + } ; + + templen -= count ; + } ; + } ; + + return len ; +} /* interleave_read_double */ + +/*------------------------------------------------------------------------------ +*/ + +static sf_count_t +interleave_seek (SF_PRIVATE *psf, int mode, sf_count_t samples_from_start) +{ psf = psf ; mode = mode ; + + /* + ** Do nothing here. This is a place holder to prevent the default + ** seek function from being called. + */ + + return samples_from_start ; +} /* interleave_seek */ +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 82314e13-0225-4408-a2f2-e6dab3f38904 +*/ diff --git a/src/ircam.c b/src/ircam.c new file mode 100644 index 00000000..7df38484 --- /dev/null +++ b/src/ircam.c @@ -0,0 +1,329 @@ +/* +** Copyright (C) 2001-2004 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include +#include + +#include "sndfile.h" +#include "sfendian.h" +#include "common.h" + +/*------------------------------------------------------------------------------ +** Macros to handle big/little endian issues. +*/ + +/* The IRCAM magic number is weird in that one byte in the number can have +** values of 0x1, 0x2, 0x03 or 0x04. Hence the need for a marker and a mask. +*/ + +#define IRCAM_BE_MASK (MAKE_MARKER (0xFF, 0xFF, 0x00, 0xFF)) +#define IRCAM_BE_MARKER (MAKE_MARKER (0x64, 0xA3, 0x00, 0x00)) + +#define IRCAM_LE_MASK (MAKE_MARKER (0xFF, 0x00, 0xFF, 0xFF)) +#define IRCAM_LE_MARKER (MAKE_MARKER (0x00, 0x00, 0xA3, 0x64)) + +#define IRCAM_02B_MARKER (MAKE_MARKER (0x64, 0xA3, 0x02, 0x00)) +#define IRCAM_03L_MARKER (MAKE_MARKER (0x64, 0xA3, 0x03, 0x00)) + +#define IRCAM_DATA_OFFSET (1024) + +/*------------------------------------------------------------------------------ +** Typedefs. +*/ + +enum +{ IRCAM_PCM_16 = 0x00002, + IRCAM_FLOAT = 0x00004, + IRCAM_ALAW = 0x10001, + IRCAM_ULAW = 0x20001, + IRCAM_PCM_32 = 0x40004 +} ; + + +/*------------------------------------------------------------------------------ +** Private static functions. +*/ + +static int ircam_close (SF_PRIVATE *psf) ; +static int ircam_write_header (SF_PRIVATE *psf, int calc_length) ; +static int ircam_read_header (SF_PRIVATE *psf) ; + +static int get_encoding (int subformat) ; + +static const char* get_encoding_str (int encoding) ; + +/*------------------------------------------------------------------------------ +** Public function. +*/ + +int +ircam_open (SF_PRIVATE *psf) +{ int subformat ; + int error = SFE_NO_ERROR ; + + if (psf->mode == SFM_READ || (psf->mode == SFM_RDWR && psf->filelength > 0)) + { if ((error = ircam_read_header (psf))) + return error ; + } ; + + subformat = psf->sf.format & SF_FORMAT_SUBMASK ; + + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + { if ((psf->sf.format & SF_FORMAT_TYPEMASK) != SF_FORMAT_IRCAM) + return SFE_BAD_OPEN_FORMAT ; + + psf->endian = psf->sf.format & SF_FORMAT_ENDMASK ; + if (psf->endian == 0 || psf->endian == SF_ENDIAN_CPU) + psf->endian = (CPU_IS_BIG_ENDIAN) ? SF_ENDIAN_BIG : SF_ENDIAN_LITTLE ; + + psf->dataoffset = IRCAM_DATA_OFFSET ; + + if ((error = ircam_write_header (psf, SF_FALSE))) + return error ; + + psf->write_header = ircam_write_header ; + } ; + + psf->container_close = ircam_close ; + + switch (subformat) + { case SF_FORMAT_ULAW : /* 8-bit Ulaw encoding. */ + error = ulaw_init (psf) ; + break ; + + case SF_FORMAT_ALAW : /* 8-bit Alaw encoding. */ + error = alaw_init (psf) ; + break ; + + case SF_FORMAT_PCM_16 : /* 16-bit linear PCM. */ + case SF_FORMAT_PCM_32 : /* 32-bit linear PCM. */ + error = pcm_init (psf) ; + break ; + + case SF_FORMAT_FLOAT : /* 32-bit linear PCM. */ + error = float32_init (psf) ; + break ; + + default : break ; + } ; + + return error ; +} /* ircam_open */ + +/*------------------------------------------------------------------------------ +*/ + +static int +ircam_read_header (SF_PRIVATE *psf) +{ unsigned int marker, encoding ; + float samplerate ; + int error = SFE_NO_ERROR ; + + psf_binheader_readf (psf, "epmf44", 0, &marker, &samplerate, &(psf->sf.channels), &encoding) ; + + if (((marker & IRCAM_BE_MASK) != IRCAM_BE_MARKER) && ((marker & IRCAM_LE_MASK) != IRCAM_LE_MARKER)) + { psf_log_printf (psf, "marker: 0x%X\n", marker) ; + return SFE_IRCAM_NO_MARKER ; + } ; + + psf->endian = SF_ENDIAN_LITTLE ; + + if (psf->sf.channels > 256) + { psf_binheader_readf (psf, "Epmf44", 0, &marker, &samplerate, &(psf->sf.channels), &encoding) ; + + /* Sanity checking for endian-ness detection. */ + if (psf->sf.channels > 256) + { psf_log_printf (psf, "marker: 0x%X\n", marker) ; + return SFE_IRCAM_BAD_CHANNELS ; + } ; + + psf->endian = SF_ENDIAN_BIG ; + } ; + + psf_log_printf (psf, "marker: 0x%X\n", marker) ; + + psf->sf.samplerate = (int) samplerate ; + + psf_log_printf (psf, " Sample Rate : %d\n" + " Channels : %d\n" + " Encoding : %X => %s\n", psf->sf.samplerate, psf->sf.channels, encoding, get_encoding_str (encoding)) ; + + switch (encoding) + { case IRCAM_PCM_16 : + psf->bytewidth = 2 ; + psf->blockwidth = psf->sf.channels * psf->bytewidth ; + + psf->sf.format = SF_FORMAT_IRCAM | SF_FORMAT_PCM_16 ; + break ; + + case IRCAM_PCM_32 : + psf->bytewidth = 4 ; + psf->blockwidth = psf->sf.channels * psf->bytewidth ; + + psf->sf.format = SF_FORMAT_IRCAM | SF_FORMAT_PCM_32 ; + break ; + + case IRCAM_FLOAT : + psf->bytewidth = 4 ; + psf->blockwidth = psf->sf.channels * psf->bytewidth ; + + psf->sf.format = SF_FORMAT_IRCAM | SF_FORMAT_FLOAT ; + break ; + + case IRCAM_ALAW : + psf->bytewidth = 1 ; + psf->blockwidth = psf->sf.channels * psf->bytewidth ; + + psf->sf.format = SF_FORMAT_IRCAM | SF_FORMAT_ALAW ; + break ; + + case IRCAM_ULAW : + psf->bytewidth = 1 ; + psf->blockwidth = psf->sf.channels * psf->bytewidth ; + + psf->sf.format = SF_FORMAT_IRCAM | SF_FORMAT_ULAW ; + break ; + + default : + error = SFE_IRCAM_UNKNOWN_FORMAT ; + break ; + } ; + + if (psf->endian == SF_ENDIAN_BIG) + psf->sf.format |= SF_ENDIAN_BIG ; + else + psf->sf.format |= SF_ENDIAN_LITTLE ; + + if (error) + return error ; + + psf->dataoffset = IRCAM_DATA_OFFSET ; + psf->datalength = psf->filelength - psf->dataoffset ; + + if (psf->sf.frames == 0 && psf->blockwidth) + psf->sf.frames = psf->datalength / psf->blockwidth ; + + psf_log_printf (psf, " Samples : %d\n", psf->sf.frames) ; + + psf_binheader_readf (psf, "p", IRCAM_DATA_OFFSET) ; + + return 0 ; +} /* ircam_read_header */ + +static int +ircam_close (SF_PRIVATE *psf) +{ + psf_log_printf (psf, "close\n") ; + + return 0 ; +} /* ircam_close */ + +static int +ircam_write_header (SF_PRIVATE *psf, int UNUSED (calc_length)) +{ int encoding ; + float samplerate ; + sf_count_t current ; + + if (psf->pipeoffset > 0) + return 0 ; + + current = psf_ftell (psf) ; + + /* This also sets psf->endian. */ + encoding = get_encoding (psf->sf.format & SF_FORMAT_SUBMASK) ; + + if (encoding == 0) + return SFE_BAD_OPEN_FORMAT ; + + /* Reset the current header length to zero. */ + psf->header [0] = 0 ; + psf->headindex = 0 ; + + if (psf->is_pipe == SF_FALSE) + psf_fseek (psf, 0, SEEK_SET) ; + + samplerate = psf->sf.samplerate ; + + switch (psf->endian) + { case SF_ENDIAN_BIG : + psf_binheader_writef (psf, "Emf", IRCAM_02B_MARKER, samplerate) ; + psf_binheader_writef (psf, "E44", psf->sf.channels, encoding) ; + break ; + + case SF_ENDIAN_LITTLE : + psf_binheader_writef (psf, "emf", IRCAM_03L_MARKER, samplerate) ; + psf_binheader_writef (psf, "e44", psf->sf.channels, encoding) ; + break ; + + default : return SFE_BAD_OPEN_FORMAT ; + } ; + + psf_binheader_writef (psf, "z", (size_t) (IRCAM_DATA_OFFSET - psf->headindex)) ; + + /* Header construction complete so write it out. */ + psf_fwrite (psf->header, psf->headindex, 1, psf) ; + + if (psf->error) + return psf->error ; + + if (current > 0) + psf_fseek (psf, current, SEEK_SET) ; + + return psf->error ; +} /* ircam_write_header */ + +static int +get_encoding (int subformat) +{ switch (subformat) + { case SF_FORMAT_PCM_16 : return IRCAM_PCM_16 ; + case SF_FORMAT_PCM_32 : return IRCAM_PCM_32 ; + + case SF_FORMAT_FLOAT : return IRCAM_FLOAT ; + + case SF_FORMAT_ULAW : return IRCAM_ULAW ; + case SF_FORMAT_ALAW : return IRCAM_ALAW ; + + default : break ; + } ; + + return 0 ; +} /* get_encoding */ + +static const char* +get_encoding_str (int encoding) +{ switch (encoding) + { case IRCAM_PCM_16 : return "16 bit PCM" ; + case IRCAM_FLOAT : return "32 bit float" ; + case IRCAM_ALAW : return "A law" ; + case IRCAM_ULAW : return "u law" ; + case IRCAM_PCM_32 : return "32 bit PCM" ; + } ; + return "Unknown encoding" ; +} /* get_encoding_str */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: f2714ab8-f286-4c94-9740-edaf673a1c71 +*/ diff --git a/src/macbinary3.c b/src/macbinary3.c new file mode 100644 index 00000000..66fe64d1 --- /dev/null +++ b/src/macbinary3.c @@ -0,0 +1,52 @@ +/* +** Copyright (C) 2003-2005 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include + +#include "sndfile.h" +#include "sfendian.h" +#include "common.h" + +#if (OS_IS_MACOSX == 1) + +int +macbinary3_open (SF_PRIVATE * UNUSED (psf)) +{ + return 0 ; +} /* macbinary3_open */ + +#else + +int +macbinary3_open (SF_PRIVATE * UNUSED (psf)) +{ + return 0 ; +} /* macbinary3_open */ + +#endif /* OS_IS_MACOSX */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: c397a7d7-1a31-4349-9684-bd29ef06211e +*/ diff --git a/src/macos.c b/src/macos.c new file mode 100644 index 00000000..c2ca5932 --- /dev/null +++ b/src/macos.c @@ -0,0 +1,58 @@ +/* +** Copyright (C) 2003-2005 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include + +#include "sndfile.h" +#include "sfendian.h" +#include "common.h" + +#define STR_MARKER MAKE_MARKER ('S', 'T', 'R', ' ') + +int +macos_guess_file_type (SF_PRIVATE * psf, const char *filename) +{ static char rsrc_name [1024] ; + struct stat statbuf ; + + snprintf (rsrc_name, sizeof (rsrc_name), "%s/rsrc", filename) ; + + /* If there is no resource fork, just return. */ + if (stat (rsrc_name, &statbuf) != 0) + { psf_log_printf (psf, "No resource fork.\n") ; + return 0 ; + } ; + + if (statbuf.st_size == 0) + { psf_log_printf (psf, "Have zero size resource fork.\n") ; + return 0 ; + } ; + + return 0 ; +} /* macos_guess_file_type */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 5fbf66d7-9547-442a-9c73-92fd164f3a95 +*/ diff --git a/src/mat4.c b/src/mat4.c new file mode 100644 index 00000000..3ead0328 --- /dev/null +++ b/src/mat4.c @@ -0,0 +1,395 @@ +/* +** Copyright (C) 2002-2006 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include +#include + +#include "sndfile.h" +#include "sfendian.h" +#include "common.h" +#include "float_cast.h" + +/*------------------------------------------------------------------------------ +** Information on how to decode and encode this file was obtained in a PDF +** file which I found on http://www.wotsit.org/. +** Also did a lot of testing with GNU Octave but do not have access to +** Matlab (tm) and so could not test it there. +*/ + +/*------------------------------------------------------------------------------ +** Macros to handle big/little endian issues. +*/ + +#define MAT4_BE_DOUBLE (MAKE_MARKER (0, 0, 0x03, 0xE8)) +#define MAT4_LE_DOUBLE (MAKE_MARKER (0, 0, 0, 0)) + +#define MAT4_BE_FLOAT (MAKE_MARKER (0, 0, 0x03, 0xF2)) +#define MAT4_LE_FLOAT (MAKE_MARKER (0x0A, 0, 0, 0)) + +#define MAT4_BE_PCM_32 (MAKE_MARKER (0, 0, 0x03, 0xFC)) +#define MAT4_LE_PCM_32 (MAKE_MARKER (0x14, 0, 0, 0)) + +#define MAT4_BE_PCM_16 (MAKE_MARKER (0, 0, 0x04, 0x06)) +#define MAT4_LE_PCM_16 (MAKE_MARKER (0x1E, 0, 0, 0)) + +/* Can't see any reason to ever implement this. */ +#define MAT4_BE_PCM_U8 (MAKE_MARKER (0, 0, 0x04, 0x1A)) +#define MAT4_LE_PCM_U8 (MAKE_MARKER (0x32, 0, 0, 0)) + +/*------------------------------------------------------------------------------ +** Private static functions. +*/ + +static int mat4_close (SF_PRIVATE *psf) ; + +static int mat4_format_to_encoding (int format, int endian) ; + +static int mat4_write_header (SF_PRIVATE *psf, int calc_length) ; +static int mat4_read_header (SF_PRIVATE *psf) ; + +static const char * mat4_marker_to_str (int marker) ; + +/*------------------------------------------------------------------------------ +** Public function. +*/ + +int +mat4_open (SF_PRIVATE *psf) +{ int subformat, error = 0 ; + + if (psf->mode == SFM_READ || (psf->mode == SFM_RDWR && psf->filelength > 0)) + { if ((error = mat4_read_header (psf))) + return error ; + } ; + + if ((psf->sf.format & SF_FORMAT_TYPEMASK) != SF_FORMAT_MAT4) + return SFE_BAD_OPEN_FORMAT ; + + subformat = psf->sf.format & SF_FORMAT_SUBMASK ; + + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + { if (psf->is_pipe) + return SFE_NO_PIPE_WRITE ; + + psf->endian = psf->sf.format & SF_FORMAT_ENDMASK ; + if (CPU_IS_LITTLE_ENDIAN && (psf->endian == SF_ENDIAN_CPU || psf->endian == 0)) + psf->endian = SF_ENDIAN_LITTLE ; + else if (CPU_IS_BIG_ENDIAN && (psf->endian == SF_ENDIAN_CPU || psf->endian == 0)) + psf->endian = SF_ENDIAN_BIG ; + + if ((error = mat4_write_header (psf, SF_FALSE))) + return error ; + + psf->write_header = mat4_write_header ; + } ; + + psf->container_close = mat4_close ; + + psf->blockwidth = psf->bytewidth * psf->sf.channels ; + + switch (subformat) + { case SF_FORMAT_PCM_16 : + case SF_FORMAT_PCM_32 : + error = pcm_init (psf) ; + break ; + + case SF_FORMAT_FLOAT : + error = float32_init (psf) ; + break ; + + case SF_FORMAT_DOUBLE : + error = double64_init (psf) ; + break ; + + default : break ; + } ; + + if (error) + return error ; + + return error ; +} /* mat4_open */ + +/*------------------------------------------------------------------------------ +*/ + +static int +mat4_close (SF_PRIVATE *psf) +{ + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + mat4_write_header (psf, SF_TRUE) ; + + return 0 ; +} /* mat4_close */ + +/*------------------------------------------------------------------------------ +*/ + +static int +mat4_write_header (SF_PRIVATE *psf, int calc_length) +{ sf_count_t current ; + int encoding ; + double samplerate ; + + current = psf_ftell (psf) ; + + if (calc_length) + { psf->filelength = psf_get_filelen (psf) ; + + psf->datalength = psf->filelength - psf->dataoffset ; + if (psf->dataend) + psf->datalength -= psf->filelength - psf->dataend ; + + psf->sf.frames = psf->datalength / (psf->bytewidth * psf->sf.channels) ; + } ; + + encoding = mat4_format_to_encoding (psf->sf.format & SF_FORMAT_SUBMASK, psf->endian) ; + + if (encoding == -1) + return SFE_BAD_OPEN_FORMAT ; + + /* Reset the current header length to zero. */ + psf->header [0] = 0 ; + psf->headindex = 0 ; + psf_fseek (psf, 0, SEEK_SET) ; + + /* Need sample rate as a double for writing to the header. */ + samplerate = psf->sf.samplerate ; + + if (psf->endian == SF_ENDIAN_BIG) + { psf_binheader_writef (psf, "Em444", MAT4_BE_DOUBLE, 1, 1, 0) ; + psf_binheader_writef (psf, "E4bd", 11, "samplerate", make_size_t (11), samplerate) ; + psf_binheader_writef (psf, "tEm484", encoding, psf->sf.channels, psf->sf.frames, 0) ; + psf_binheader_writef (psf, "E4b", 9, "wavedata", make_size_t (9)) ; + } + else if (psf->endian == SF_ENDIAN_LITTLE) + { psf_binheader_writef (psf, "em444", MAT4_LE_DOUBLE, 1, 1, 0) ; + psf_binheader_writef (psf, "e4bd", 11, "samplerate", make_size_t (11), samplerate) ; + psf_binheader_writef (psf, "tem484", encoding, psf->sf.channels, psf->sf.frames, 0) ; + psf_binheader_writef (psf, "e4b", 9, "wavedata", make_size_t (9)) ; + } + else + return SFE_BAD_OPEN_FORMAT ; + + /* Header construction complete so write it out. */ + psf_fwrite (psf->header, psf->headindex, 1, psf) ; + + if (psf->error) + return psf->error ; + + psf->dataoffset = psf->headindex ; + + if (current > 0) + psf_fseek (psf, current, SEEK_SET) ; + + return psf->error ; +} /* mat4_write_header */ + +static int +mat4_read_header (SF_PRIVATE *psf) +{ int marker, rows, cols, imag ; + unsigned namesize ; + double value ; + const char *marker_str ; + char name [64] ; + + psf_binheader_readf (psf, "pm", 0, &marker) ; + + /* MAT4 file must start with a double for the samplerate. */ + if (marker == MAT4_BE_DOUBLE) + { psf->endian = psf->rwf_endian = SF_ENDIAN_BIG ; + marker_str = "big endian double" ; + } + else if (marker == MAT4_LE_DOUBLE) + { psf->endian = psf->rwf_endian = SF_ENDIAN_LITTLE ; + marker_str = "little endian double" ; + } + else + return SFE_UNIMPLEMENTED ; + + psf_log_printf (psf, "GNU Octave 2.0 / MATLAB v4.2 format\nMarker : %s\n", marker_str) ; + + psf_binheader_readf (psf, "444", &rows, &cols, &imag) ; + + psf_log_printf (psf, " Rows : %d\n Cols : %d\n Imag : %s\n", rows, cols, imag ? "True" : "False") ; + + psf_binheader_readf (psf, "4", &namesize) ; + + if (namesize >= SIGNED_SIZEOF (name)) + return SFE_MAT4_BAD_NAME ; + + psf_binheader_readf (psf, "b", name, namesize) ; + name [namesize] = 0 ; + + psf_log_printf (psf, " Name : %s\n", name) ; + + psf_binheader_readf (psf, "d", &value) ; + + LSF_SNPRINTF (psf->u.cbuf, sizeof (psf->u.cbuf), " Value : %f\n", value) ; + psf_log_printf (psf, psf->u.cbuf) ; + + if ((rows != 1) || (cols != 1)) + return SFE_MAT4_NO_SAMPLERATE ; + + psf->sf.samplerate = lrint (value) ; + + /* Now write out the audio data. */ + + psf_binheader_readf (psf, "m", &marker) ; + + psf_log_printf (psf, "Marker : %s\n", mat4_marker_to_str (marker)) ; + + psf_binheader_readf (psf, "444", &rows, &cols, &imag) ; + + psf_log_printf (psf, " Rows : %d\n Cols : %d\n Imag : %s\n", rows, cols, imag ? "True" : "False") ; + + psf_binheader_readf (psf, "4", &namesize) ; + + if (namesize >= SIGNED_SIZEOF (name)) + return SFE_MAT4_BAD_NAME ; + + psf_binheader_readf (psf, "b", name, namesize) ; + name [namesize] = 0 ; + + psf_log_printf (psf, " Name : %s\n", name) ; + + psf->dataoffset = psf_ftell (psf) ; + + if (rows == 0 && cols == 0) + { psf_log_printf (psf, "*** Error : zero channel count.\n") ; + return SFE_MAT4_ZERO_CHANNELS ; + } ; + + psf->sf.channels = rows ; + psf->sf.frames = cols ; + + psf->sf.format = psf->endian | SF_FORMAT_MAT4 ; + switch (marker) + { case MAT4_BE_DOUBLE : + case MAT4_LE_DOUBLE : + psf->sf.format |= SF_FORMAT_DOUBLE ; + psf->bytewidth = 8 ; + break ; + + case MAT4_BE_FLOAT : + case MAT4_LE_FLOAT : + psf->sf.format |= SF_FORMAT_FLOAT ; + psf->bytewidth = 4 ; + break ; + + case MAT4_BE_PCM_32 : + case MAT4_LE_PCM_32 : + psf->sf.format |= SF_FORMAT_PCM_32 ; + psf->bytewidth = 4 ; + break ; + + case MAT4_BE_PCM_16 : + case MAT4_LE_PCM_16 : + psf->sf.format |= SF_FORMAT_PCM_16 ; + psf->bytewidth = 2 ; + break ; + + default : + psf_log_printf (psf, "*** Error : Bad marker %08X\n", marker) ; + return SFE_UNIMPLEMENTED ; + } ; + + if ((psf->filelength - psf->dataoffset) < psf->sf.channels * psf->sf.frames * psf->bytewidth) + { psf_log_printf (psf, "*** File seems to be truncated. %D <--> %D\n", + psf->filelength - psf->dataoffset, psf->sf.channels * psf->sf.frames * psf->bytewidth) ; + } + else if ((psf->filelength - psf->dataoffset) > psf->sf.channels * psf->sf.frames * psf->bytewidth) + psf->dataend = psf->dataoffset + rows * cols * psf->bytewidth ; + + psf->datalength = psf->filelength - psf->dataoffset - psf->dataend ; + + psf->sf.sections = 1 ; + + return 0 ; +} /* mat4_read_header */ + +static int +mat4_format_to_encoding (int format, int endian) +{ + switch (format | endian) + { case (SF_FORMAT_PCM_16 | SF_ENDIAN_BIG) : + return MAT4_BE_PCM_16 ; + + case (SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE) : + return MAT4_LE_PCM_16 ; + + case (SF_FORMAT_PCM_32 | SF_ENDIAN_BIG) : + return MAT4_BE_PCM_32 ; + + case (SF_FORMAT_PCM_32 | SF_ENDIAN_LITTLE) : + return MAT4_LE_PCM_32 ; + + case (SF_FORMAT_FLOAT | SF_ENDIAN_BIG) : + return MAT4_BE_FLOAT ; + + case (SF_FORMAT_FLOAT | SF_ENDIAN_LITTLE) : + return MAT4_LE_FLOAT ; + + case (SF_FORMAT_DOUBLE | SF_ENDIAN_BIG) : + return MAT4_BE_DOUBLE ; + + case (SF_FORMAT_DOUBLE | SF_ENDIAN_LITTLE) : + return MAT4_LE_DOUBLE ; + + default : break ; + } ; + + return -1 ; +} /* mat4_format_to_encoding */ + +static const char * +mat4_marker_to_str (int marker) +{ static char str [32] ; + + switch (marker) + { + case MAT4_BE_PCM_16 : return "big endian 16 bit PCM" ; + case MAT4_LE_PCM_16 : return "little endian 16 bit PCM" ; + + case MAT4_BE_PCM_32 : return "big endian 32 bit PCM" ; + case MAT4_LE_PCM_32 : return "little endian 32 bit PCM" ; + + + case MAT4_BE_FLOAT : return "big endian float" ; + case MAT4_LE_FLOAT : return "big endian float" ; + + case MAT4_BE_DOUBLE : return "big endian double" ; + case MAT4_LE_DOUBLE : return "little endian double" ; + } ; + + /* This is a little unsafe but is really only for debugging. */ + str [sizeof (str) - 1] = 0 ; + LSF_SNPRINTF (str, sizeof (str) - 1, "%08X", marker) ; + return str ; +} /* mat4_marker_to_str */ +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: f7e5f5d6-fc39-452e-bc4a-59627116ff59 +*/ diff --git a/src/mat5.c b/src/mat5.c new file mode 100644 index 00000000..ecd0039e --- /dev/null +++ b/src/mat5.c @@ -0,0 +1,507 @@ +/* +** Copyright (C) 2002-2006 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include +#include + +#include "sndfile.h" +#include "sfendian.h" +#include "common.h" +#include "float_cast.h" + +/*------------------------------------------------------------------------------ +** Information on how to decode and encode this file was obtained in a PDF +** file which I found on http://www.wotsit.org/. +** Also did a lot of testing with GNU Octave but do not have access to +** Matlab (tm) and so could not test it there. +*/ + +/*------------------------------------------------------------------------------ +** Macros to handle big/little endian issues. +*/ + +#define MATL_MARKER (MAKE_MARKER ('M', 'A', 'T', 'L')) + +#define IM_MARKER (('I' << 8) + 'M') +#define MI_MARKER (('M' << 8) + 'I') + +/*------------------------------------------------------------------------------ +** Enums and typedefs. +*/ + +enum +{ MAT5_TYPE_SCHAR = 0x1, + MAT5_TYPE_UCHAR = 0x2, + MAT5_TYPE_INT16 = 0x3, + MAT5_TYPE_UINT16 = 0x4, + MAT5_TYPE_INT32 = 0x5, + MAT5_TYPE_UINT32 = 0x6, + MAT5_TYPE_FLOAT = 0x7, + MAT5_TYPE_DOUBLE = 0x9, + MAT5_TYPE_ARRAY = 0xE, + + MAT5_TYPE_COMP_USHORT = 0x00020004, + MAT5_TYPE_COMP_UINT = 0x00040006 +} ; + +typedef struct +{ sf_count_t size ; + int rows, cols ; + char name [32] ; +} MAT5_MATRIX ; + +/*------------------------------------------------------------------------------ +** Private static functions. +*/ + +static int mat5_close (SF_PRIVATE *psf) ; + +static int mat5_write_header (SF_PRIVATE *psf, int calc_length) ; +static int mat5_read_header (SF_PRIVATE *psf) ; + +/*------------------------------------------------------------------------------ +** Public function. +*/ + +int +mat5_open (SF_PRIVATE *psf) +{ int subformat, error = 0 ; + + if (psf->mode == SFM_READ || (psf->mode == SFM_RDWR && psf->filelength > 0)) + { if ((error = mat5_read_header (psf))) + return error ; + } ; + + if ((psf->sf.format & SF_FORMAT_TYPEMASK) != SF_FORMAT_MAT5) + return SFE_BAD_OPEN_FORMAT ; + + subformat = psf->sf.format & SF_FORMAT_SUBMASK ; + + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + { if (psf->is_pipe) + return SFE_NO_PIPE_WRITE ; + + psf->endian = psf->sf.format & SF_FORMAT_ENDMASK ; + if (CPU_IS_LITTLE_ENDIAN && (psf->endian == SF_ENDIAN_CPU || psf->endian == 0)) + psf->endian = SF_ENDIAN_LITTLE ; + else if (CPU_IS_BIG_ENDIAN && (psf->endian == SF_ENDIAN_CPU || psf->endian == 0)) + psf->endian = SF_ENDIAN_BIG ; + + if ((error = mat5_write_header (psf, SF_FALSE))) + return error ; + + psf->write_header = mat5_write_header ; + } ; + + psf->container_close = mat5_close ; + + psf->blockwidth = psf->bytewidth * psf->sf.channels ; + + switch (subformat) + { case SF_FORMAT_PCM_U8 : + case SF_FORMAT_PCM_16 : + case SF_FORMAT_PCM_32 : + error = pcm_init (psf) ; + break ; + + case SF_FORMAT_FLOAT : + error = float32_init (psf) ; + break ; + + case SF_FORMAT_DOUBLE : + error = double64_init (psf) ; + break ; + + default : break ; + } ; + + return error ; +} /* mat5_open */ + +/*------------------------------------------------------------------------------ +*/ + +static int +mat5_close (SF_PRIVATE *psf) +{ + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + mat5_write_header (psf, SF_TRUE) ; + + return 0 ; +} /* mat5_close */ + +/*------------------------------------------------------------------------------ +*/ + +static int +mat5_write_header (SF_PRIVATE *psf, int calc_length) +{ static const char *filename = "MATLAB 5.0 MAT-file, written by " PACKAGE "-" VERSION ", " ; + static const char *sr_name = "samplerate\0\0\0\0\0\0\0\0\0\0\0" ; + static const char *wd_name = "wavedata\0" ; + sf_count_t current, datasize ; + int encoding ; + + current = psf_ftell (psf) ; + + if (calc_length) + { psf_fseek (psf, 0, SEEK_END) ; + psf->filelength = psf_ftell (psf) ; + psf_fseek (psf, 0, SEEK_SET) ; + + psf->datalength = psf->filelength - psf->dataoffset ; + if (psf->dataend) + psf->datalength -= psf->filelength - psf->dataend ; + + psf->sf.frames = psf->datalength / (psf->bytewidth * psf->sf.channels) ; + } ; + + switch (psf->sf.format & SF_FORMAT_SUBMASK) + { case SF_FORMAT_PCM_U8 : + encoding = MAT5_TYPE_UCHAR ; + break ; + + case SF_FORMAT_PCM_16 : + encoding = MAT5_TYPE_INT16 ; + break ; + + case SF_FORMAT_PCM_32 : + encoding = MAT5_TYPE_INT32 ; + break ; + + case SF_FORMAT_FLOAT : + encoding = MAT5_TYPE_FLOAT ; + break ; + + case SF_FORMAT_DOUBLE : + encoding = MAT5_TYPE_DOUBLE ; + break ; + + default : + return SFE_BAD_OPEN_FORMAT ; + } ; + + /* Reset the current header length to zero. */ + psf->header [0] = 0 ; + psf->headindex = 0 ; + psf_fseek (psf, 0, SEEK_SET) ; + + psf_get_date_str (psf->u.cbuf, sizeof (psf->u.scbuf)) ; + psf_binheader_writef (psf, "bb", filename, strlen (filename), psf->u.cbuf, strlen (psf->u.cbuf) + 1) ; + + memset (psf->u.scbuf, ' ', 124 - psf->headindex) ; + psf_binheader_writef (psf, "b", psf->u.scbuf, make_size_t (124 - psf->headindex)) ; + + psf->rwf_endian = psf->endian ; + + if (psf->rwf_endian == SF_ENDIAN_BIG) + psf_binheader_writef (psf, "2b", 0x0100, "MI", make_size_t (2)) ; + else + psf_binheader_writef (psf, "2b", 0x0100, "IM", make_size_t (2)) ; + + psf_binheader_writef (psf, "444444", MAT5_TYPE_ARRAY, 64, MAT5_TYPE_UINT32, 8, 6, 0) ; + psf_binheader_writef (psf, "4444", MAT5_TYPE_INT32, 8, 1, 1) ; + psf_binheader_writef (psf, "44b", MAT5_TYPE_SCHAR, strlen (sr_name), sr_name, make_size_t (16)) ; + + if (psf->sf.samplerate > 0xFFFF) + psf_binheader_writef (psf, "44", MAT5_TYPE_COMP_UINT, psf->sf.samplerate) ; + else + { unsigned short samplerate = psf->sf.samplerate ; + + psf_binheader_writef (psf, "422", MAT5_TYPE_COMP_USHORT, samplerate, 0) ; + } ; + + datasize = psf->sf.frames * psf->sf.channels * psf->bytewidth ; + + psf_binheader_writef (psf, "t484444", MAT5_TYPE_ARRAY, datasize + 64, MAT5_TYPE_UINT32, 8, 6, 0) ; + psf_binheader_writef (psf, "t4448", MAT5_TYPE_INT32, 8, psf->sf.channels, psf->sf.frames) ; + psf_binheader_writef (psf, "44b", MAT5_TYPE_SCHAR, strlen (wd_name), wd_name, strlen (wd_name)) ; + + datasize = psf->sf.frames * psf->sf.channels * psf->bytewidth ; + if (datasize > 0x7FFFFFFF) + datasize = 0x7FFFFFFF ; + + psf_binheader_writef (psf, "t48", encoding, datasize) ; + + /* Header construction complete so write it out. */ + psf_fwrite (psf->header, psf->headindex, 1, psf) ; + + if (psf->error) + return psf->error ; + + psf->dataoffset = psf->headindex ; + + if (current > 0) + psf_fseek (psf, current, SEEK_SET) ; + + return psf->error ; +} /* mat5_write_header */ + +static int +mat5_read_header (SF_PRIVATE *psf) +{ char name [32] ; + short version, endian ; + int type, size, flags1, flags2, rows, cols ; + + psf_binheader_readf (psf, "pb", 0, psf->u.cbuf, 124) ; + + psf->u.scbuf [125] = 0 ; + + if (strlen (psf->u.cbuf) >= 124) + return SFE_UNIMPLEMENTED ; + + if (strstr (psf->u.cbuf, "MATLAB 5.0 MAT-file") == psf->u.cbuf) + psf_log_printf (psf, "%s\n", psf->u.scbuf) ; + + + psf_binheader_readf (psf, "E22", &version, &endian) ; + + if (endian == MI_MARKER) + { psf->endian = psf->rwf_endian = SF_ENDIAN_BIG ; + if (CPU_IS_LITTLE_ENDIAN) version = ENDSWAP_SHORT (version) ; + } + else if (endian == IM_MARKER) + { psf->endian = psf->rwf_endian = SF_ENDIAN_LITTLE ; + if (CPU_IS_BIG_ENDIAN) version = ENDSWAP_SHORT (version) ; + } + else + return SFE_MAT5_BAD_ENDIAN ; + + if ((CPU_IS_LITTLE_ENDIAN && endian == IM_MARKER) || + (CPU_IS_BIG_ENDIAN && endian == MI_MARKER)) + version = ENDSWAP_SHORT (version) ; + + psf_log_printf (psf, "Version : 0x%04X\n", version) ; + psf_log_printf (psf, "Endian : 0x%04X => %s\n", endian, + (psf->endian == SF_ENDIAN_LITTLE) ? "Little" : "Big") ; + + /*========================================================*/ + psf_binheader_readf (psf, "44", &type, &size) ; + psf_log_printf (psf, "Block\n Type : %X Size : %d\n", type, size) ; + + if (type != MAT5_TYPE_ARRAY) + return SFE_MAT5_NO_BLOCK ; + + psf_binheader_readf (psf, "44", &type, &size) ; + psf_log_printf (psf, " Type : %X Size : %d\n", type, size) ; + + if (type != MAT5_TYPE_UINT32) + return SFE_MAT5_NO_BLOCK ; + + psf_binheader_readf (psf, "44", &flags1, &flags2) ; + psf_log_printf (psf, " Flg1 : %X Flg2 : %d\n", flags1, flags2) ; + + psf_binheader_readf (psf, "44", &type, &size) ; + psf_log_printf (psf, " Type : %X Size : %d\n", type, size) ; + + if (type != MAT5_TYPE_INT32) + return SFE_MAT5_NO_BLOCK ; + + psf_binheader_readf (psf, "44", &rows, &cols) ; + psf_log_printf (psf, " Rows : %X Cols : %d\n", rows, cols) ; + + if (rows != 1 || cols != 1) + return SFE_MAT5_SAMPLE_RATE ; + + psf_binheader_readf (psf, "4", &type) ; + + if (type == MAT5_TYPE_SCHAR) + { psf_binheader_readf (psf, "4", &size) ; + psf_log_printf (psf, " Type : %X Size : %d\n", type, size) ; + if (size > SIGNED_SIZEOF (name) - 1) + { psf_log_printf (psf, "Error : Bad name length.\n") ; + return SFE_MAT5_NO_BLOCK ; + } ; + + psf_binheader_readf (psf, "bj", name, size, (8 - (size % 8)) % 8) ; + name [size] = 0 ; + } + else if ((type & 0xFFFF) == MAT5_TYPE_SCHAR) + { size = type >> 16 ; + if (size > 4) + { psf_log_printf (psf, "Error : Bad name length.\n") ; + return SFE_MAT5_NO_BLOCK ; + } ; + + psf_log_printf (psf, " Type : %X\n", type) ; + psf_binheader_readf (psf, "4", &name) ; + name [size] = 0 ; + } + else + return SFE_MAT5_NO_BLOCK ; + + psf_log_printf (psf, " Name : %s\n", name) ; + + /*-----------------------------------------*/ + + psf_binheader_readf (psf, "44", &type, &size) ; + + switch (type) + { case MAT5_TYPE_DOUBLE : + { double samplerate ; + + psf_binheader_readf (psf, "d", &samplerate) ; + LSF_SNPRINTF (name, sizeof (name), "%f\n", samplerate) ; + psf_log_printf (psf, " Val : %s\n", name) ; + + psf->sf.samplerate = lrint (samplerate) ; + } ; + break ; + + case MAT5_TYPE_COMP_USHORT : + { unsigned short samplerate ; + + psf_binheader_readf (psf, "j2j", -4, &samplerate, 2) ; + psf_log_printf (psf, " Val : %u\n", samplerate) ; + psf->sf.samplerate = samplerate ; + } + break ; + + case MAT5_TYPE_COMP_UINT : + psf_log_printf (psf, " Val : %u\n", size) ; + psf->sf.samplerate = size ; + break ; + + default : + psf_log_printf (psf, " Type : %X Size : %d ***\n", type, size) ; + return SFE_MAT5_SAMPLE_RATE ; + } ; + + /*-----------------------------------------*/ + + + psf_binheader_readf (psf, "44", &type, &size) ; + psf_log_printf (psf, " Type : %X Size : %d\n", type, size) ; + + if (type != MAT5_TYPE_ARRAY) + return SFE_MAT5_NO_BLOCK ; + + psf_binheader_readf (psf, "44", &type, &size) ; + psf_log_printf (psf, " Type : %X Size : %d\n", type, size) ; + + if (type != MAT5_TYPE_UINT32) + return SFE_MAT5_NO_BLOCK ; + + psf_binheader_readf (psf, "44", &flags1, &flags2) ; + psf_log_printf (psf, " Flg1 : %X Flg2 : %d\n", flags1, flags2) ; + + psf_binheader_readf (psf, "44", &type, &size) ; + psf_log_printf (psf, " Type : %X Size : %d\n", type, size) ; + + if (type != MAT5_TYPE_INT32) + return SFE_MAT5_NO_BLOCK ; + + psf_binheader_readf (psf, "44", &rows, &cols) ; + psf_log_printf (psf, " Rows : %X Cols : %d\n", rows, cols) ; + + psf_binheader_readf (psf, "4", &type) ; + + if (type == MAT5_TYPE_SCHAR) + { psf_binheader_readf (psf, "4", &size) ; + psf_log_printf (psf, " Type : %X Size : %d\n", type, size) ; + if (size > SIGNED_SIZEOF (name) - 1) + { psf_log_printf (psf, "Error : Bad name length.\n") ; + return SFE_MAT5_NO_BLOCK ; + } ; + + psf_binheader_readf (psf, "bj", name, size, (8 - (size % 8)) % 8) ; + name [size] = 0 ; + } + else if ((type & 0xFFFF) == MAT5_TYPE_SCHAR) + { size = type >> 16 ; + if (size > 4) + { psf_log_printf (psf, "Error : Bad name length.\n") ; + return SFE_MAT5_NO_BLOCK ; + } ; + + psf_log_printf (psf, " Type : %X\n", type) ; + psf_binheader_readf (psf, "4", &name) ; + name [size] = 0 ; + } + else + return SFE_MAT5_NO_BLOCK ; + + psf_log_printf (psf, " Name : %s\n", name) ; + + psf_binheader_readf (psf, "44", &type, &size) ; + psf_log_printf (psf, " Type : %X Size : %d\n", type, size) ; + + /*++++++++++++++++++++++++++++++++++++++++++++++++++*/ + + if (rows == 0 && cols == 0) + { psf_log_printf (psf, "*** Error : zero channel count.\n") ; + return SFE_MAT5_ZERO_CHANNELS ; + } ; + + psf->sf.channels = rows ; + psf->sf.frames = cols ; + + psf->sf.format = psf->endian | SF_FORMAT_MAT5 ; + + switch (type) + { case MAT5_TYPE_DOUBLE : + psf_log_printf (psf, "Data type : double\n") ; + psf->sf.format |= SF_FORMAT_DOUBLE ; + psf->bytewidth = 8 ; + break ; + + case MAT5_TYPE_FLOAT : + psf_log_printf (psf, "Data type : float\n") ; + psf->sf.format |= SF_FORMAT_FLOAT ; + psf->bytewidth = 4 ; + break ; + + case MAT5_TYPE_INT32 : + psf_log_printf (psf, "Data type : 32 bit PCM\n") ; + psf->sf.format |= SF_FORMAT_PCM_32 ; + psf->bytewidth = 4 ; + break ; + + case MAT5_TYPE_INT16 : + psf_log_printf (psf, "Data type : 16 bit PCM\n") ; + psf->sf.format |= SF_FORMAT_PCM_16 ; + psf->bytewidth = 2 ; + break ; + + case MAT5_TYPE_UCHAR : + psf_log_printf (psf, "Data type : unsigned 8 bit PCM\n") ; + psf->sf.format |= SF_FORMAT_PCM_U8 ; + psf->bytewidth = 1 ; + break ; + + default : + psf_log_printf (psf, "*** Error : Bad marker %08X\n", type) ; + return SFE_UNIMPLEMENTED ; + } ; + + psf->dataoffset = psf_ftell (psf) ; + psf->datalength = psf->filelength - psf->dataoffset ; + + return 0 ; +} /* mat5_read_header */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: dfdb6742-b2be-4be8-b390-d0c674e8bc8e +*/ diff --git a/src/ms_adpcm.c b/src/ms_adpcm.c new file mode 100644 index 00000000..0fc3403a --- /dev/null +++ b/src/ms_adpcm.c @@ -0,0 +1,834 @@ +/* +** Copyright (C) 1999-2005 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include + +#include "sndfile.h" +#include "sfendian.h" +#include "float_cast.h" +#include "common.h" +#include "wav_w64.h" + +/* These required here because we write the header in this file. */ + +#define RIFF_MARKER (MAKE_MARKER ('R', 'I', 'F', 'F')) +#define WAVE_MARKER (MAKE_MARKER ('W', 'A', 'V', 'E')) +#define fmt_MARKER (MAKE_MARKER ('f', 'm', 't', ' ')) +#define fact_MARKER (MAKE_MARKER ('f', 'a', 'c', 't')) +#define data_MARKER (MAKE_MARKER ('d', 'a', 't', 'a')) + +#define WAVE_FORMAT_MS_ADPCM 0x0002 + +typedef struct +{ int channels, blocksize, samplesperblock, blocks, dataremaining ; + int blockcount ; + sf_count_t samplecount ; + short *samples ; + unsigned char *block ; +#if HAVE_FLEXIBLE_ARRAY + short dummydata [] ; /* ISO C99 struct flexible array. */ +#else + short dummydata [0] ; /* This is a hack an might not work. */ +#endif +} MSADPCM_PRIVATE ; + +/*============================================================================================ +** MS ADPCM static data and functions. +*/ + +static int AdaptationTable [] = +{ 230, 230, 230, 230, 307, 409, 512, 614, + 768, 614, 512, 409, 307, 230, 230, 230 +} ; + +/* TODO : The first 7 coef's are are always hardcode and must + appear in the actual WAVE file. They should be read in + in case a sound program added extras to the list. */ + +static int AdaptCoeff1 [MSADPCM_ADAPT_COEFF_COUNT] = +{ 256, 512, 0, 192, 240, 460, 392 +} ; + +static int AdaptCoeff2 [MSADPCM_ADAPT_COEFF_COUNT] = +{ 0, -256, 0, 64, 0, -208, -232 +} ; + +/*============================================================================================ +** MS ADPCM Block Layout. +** ====================== +** Block is usually 256, 512 or 1024 bytes depending on sample rate. +** For a mono file, the block is laid out as follows: +** byte purpose +** 0 block predictor [0..6] +** 1,2 initial idelta (positive) +** 3,4 sample 1 +** 5,6 sample 0 +** 7..n packed bytecodes +** +** For a stereo file, the block is laid out as follows: +** byte purpose +** 0 block predictor [0..6] for left channel +** 1 block predictor [0..6] for right channel +** 2,3 initial idelta (positive) for left channel +** 4,5 initial idelta (positive) for right channel +** 6,7 sample 1 for left channel +** 8,9 sample 1 for right channel +** 10,11 sample 0 for left channel +** 12,13 sample 0 for right channel +** 14..n packed bytecodes +*/ + +/*============================================================================================ +** Static functions. +*/ + +static int msadpcm_decode_block (SF_PRIVATE *psf, MSADPCM_PRIVATE *pms) ; +static sf_count_t msadpcm_read_block (SF_PRIVATE *psf, MSADPCM_PRIVATE *pms, short *ptr, int len) ; + +static int msadpcm_encode_block (SF_PRIVATE *psf, MSADPCM_PRIVATE *pms) ; +static sf_count_t msadpcm_write_block (SF_PRIVATE *psf, MSADPCM_PRIVATE *pms, const short *ptr, int len) ; + +static sf_count_t msadpcm_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ; +static sf_count_t msadpcm_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ; +static sf_count_t msadpcm_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ; +static sf_count_t msadpcm_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ; + +static sf_count_t msadpcm_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ; +static sf_count_t msadpcm_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ; +static sf_count_t msadpcm_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ; +static sf_count_t msadpcm_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ; + +static sf_count_t msadpcm_seek (SF_PRIVATE *psf, int mode, sf_count_t offset) ; +static int msadpcm_close (SF_PRIVATE *psf) ; + +static void choose_predictor (unsigned int channels, short *data, int *bpred, int *idelta) ; + +/*============================================================================================ +** MS ADPCM Read Functions. +*/ + +int +wav_w64_msadpcm_init (SF_PRIVATE *psf, int blockalign, int samplesperblock) +{ MSADPCM_PRIVATE *pms ; + unsigned int pmssize ; + int count ; + + if (psf->codec_data != NULL) + { psf_log_printf (psf, "*** psf->codec_data is not NULL.\n") ; + return SFE_INTERNAL ; + } ; + + if (psf->mode == SFM_WRITE) + samplesperblock = 2 + 2 * (blockalign - 7 * psf->sf.channels) / psf->sf.channels ; + + pmssize = sizeof (MSADPCM_PRIVATE) + blockalign + 3 * psf->sf.channels * samplesperblock ; + + if (! (psf->codec_data = malloc (pmssize))) + return SFE_MALLOC_FAILED ; + pms = (MSADPCM_PRIVATE*) psf->codec_data ; + memset (pms, 0, pmssize) ; + + pms->samples = pms->dummydata ; + pms->block = (unsigned char*) (pms->dummydata + psf->sf.channels * samplesperblock) ; + + pms->channels = psf->sf.channels ; + pms->blocksize = blockalign ; + pms->samplesperblock = samplesperblock ; + + if (psf->mode == SFM_READ) + { pms->dataremaining = psf->datalength ; + + if (psf->datalength % pms->blocksize) + pms->blocks = psf->datalength / pms->blocksize + 1 ; + else + pms->blocks = psf->datalength / pms->blocksize ; + + count = 2 * (pms->blocksize - 6 * pms->channels) / pms->channels ; + if (pms->samplesperblock != count) + psf_log_printf (psf, "*** Warning : samplesperblock shoud be %d.\n", count) ; + + psf->sf.frames = (psf->datalength / pms->blocksize) * pms->samplesperblock ; + + psf_log_printf (psf, " bpred idelta\n") ; + + msadpcm_decode_block (psf, pms) ; + + psf->read_short = msadpcm_read_s ; + psf->read_int = msadpcm_read_i ; + psf->read_float = msadpcm_read_f ; + psf->read_double = msadpcm_read_d ; + } ; + + if (psf->mode == SFM_WRITE) + { pms->samples = pms->dummydata ; + + pms->samplecount = 0 ; + + psf->write_short = msadpcm_write_s ; + psf->write_int = msadpcm_write_i ; + psf->write_float = msadpcm_write_f ; + psf->write_double = msadpcm_write_d ; + } ; + + psf->codec_close = msadpcm_close ; + psf->seek = msadpcm_seek ; + + return 0 ; +} /* wav_w64_msadpcm_init */ + +static int +msadpcm_decode_block (SF_PRIVATE *psf, MSADPCM_PRIVATE *pms) +{ int chan, k, blockindx, sampleindx ; + short bytecode, bpred [2], chan_idelta [2] ; + + int predict ; + int current ; + int idelta ; + + pms->blockcount ++ ; + pms->samplecount = 0 ; + + if (pms->blockcount > pms->blocks) + { memset (pms->samples, 0, pms->samplesperblock * pms->channels) ; + return 1 ; + } ; + + if ((k = psf_fread (pms->block, 1, pms->blocksize, psf)) != pms->blocksize) + psf_log_printf (psf, "*** Warning : short read (%d != %d).\n", k, pms->blocksize) ; + + /* Read and check the block header. */ + + if (pms->channels == 1) + { bpred [0] = pms->block [0] ; + + if (bpred [0] >= 7) + psf_log_printf (psf, "MS ADPCM synchronisation error (%d).\n", bpred [0]) ; + + chan_idelta [0] = pms->block [1] | (pms->block [2] << 8) ; + chan_idelta [1] = 0 ; + + psf_log_printf (psf, "(%d) (%d)\n", bpred [0], chan_idelta [0]) ; + + pms->samples [1] = pms->block [3] | (pms->block [4] << 8) ; + pms->samples [0] = pms->block [5] | (pms->block [6] << 8) ; + blockindx = 7 ; + } + else + { bpred [0] = pms->block [0] ; + bpred [1] = pms->block [1] ; + + if (bpred [0] >= 7 || bpred [1] >= 7) + psf_log_printf (psf, "MS ADPCM synchronisation error (%d %d).\n", bpred [0], bpred [1]) ; + + chan_idelta [0] = pms->block [2] | (pms->block [3] << 8) ; + chan_idelta [1] = pms->block [4] | (pms->block [5] << 8) ; + + psf_log_printf (psf, "(%d, %d) (%d, %d)\n", bpred [0], bpred [1], chan_idelta [0], chan_idelta [1]) ; + + pms->samples [2] = pms->block [6] | (pms->block [7] << 8) ; + pms->samples [3] = pms->block [8] | (pms->block [9] << 8) ; + + pms->samples [0] = pms->block [10] | (pms->block [11] << 8) ; + pms->samples [1] = pms->block [12] | (pms->block [13] << 8) ; + + blockindx = 14 ; + } ; + + /*-------------------------------------------------------- + This was left over from a time when calculations were done + as ints rather than shorts. Keep this around as a reminder + in case I ever find a file which decodes incorrectly. + + if (chan_idelta [0] & 0x8000) + chan_idelta [0] -= 0x10000 ; + if (chan_idelta [1] & 0x8000) + chan_idelta [1] -= 0x10000 ; + --------------------------------------------------------*/ + + /* Pull apart the packed 4 bit samples and store them in their + ** correct sample positions. + */ + + sampleindx = 2 * pms->channels ; + while (blockindx < pms->blocksize) + { bytecode = pms->block [blockindx++] ; + pms->samples [sampleindx++] = (bytecode >> 4) & 0x0F ; + pms->samples [sampleindx++] = bytecode & 0x0F ; + } ; + + /* Decode the encoded 4 bit samples. */ + + for (k = 2 * pms->channels ; k < (pms->samplesperblock * pms->channels) ; k ++) + { chan = (pms->channels > 1) ? (k % 2) : 0 ; + + bytecode = pms->samples [k] & 0xF ; + + /* Compute next Adaptive Scale Factor (ASF) */ + idelta = chan_idelta [chan] ; + chan_idelta [chan] = (AdaptationTable [bytecode] * idelta) >> 8 ; /* => / 256 => FIXED_POINT_ADAPTATION_BASE == 256 */ + if (chan_idelta [chan] < 16) + chan_idelta [chan] = 16 ; + if (bytecode & 0x8) + bytecode -= 0x10 ; + + predict = ((pms->samples [k - pms->channels] * AdaptCoeff1 [bpred [chan]]) + + (pms->samples [k - 2 * pms->channels] * AdaptCoeff2 [bpred [chan]])) >> 8 ; /* => / 256 => FIXED_POINT_COEFF_BASE == 256 */ + current = (bytecode * idelta) + predict ; + + if (current > 32767) + current = 32767 ; + else if (current < -32768) + current = -32768 ; + + pms->samples [k] = current ; + } ; + + return 1 ; +} /* msadpcm_decode_block */ + +static sf_count_t +msadpcm_read_block (SF_PRIVATE *psf, MSADPCM_PRIVATE *pms, short *ptr, int len) +{ int count, total = 0, indx = 0 ; + + while (indx < len) + { if (pms->blockcount >= pms->blocks && pms->samplecount >= pms->samplesperblock) + { memset (&(ptr [indx]), 0, (size_t) ((len - indx) * sizeof (short))) ; + return total ; + } ; + + if (pms->samplecount >= pms->samplesperblock) + msadpcm_decode_block (psf, pms) ; + + count = (pms->samplesperblock - pms->samplecount) * pms->channels ; + count = (len - indx > count) ? count : len - indx ; + + memcpy (&(ptr [indx]), &(pms->samples [pms->samplecount * pms->channels]), count * sizeof (short)) ; + indx += count ; + pms->samplecount += count / pms->channels ; + total = indx ; + } ; + + return total ; +} /* msadpcm_read_block */ + +static sf_count_t +msadpcm_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len) +{ MSADPCM_PRIVATE *pms ; + int readcount, count ; + sf_count_t total = 0 ; + + if (! psf->codec_data) + return 0 ; + pms = (MSADPCM_PRIVATE*) psf->codec_data ; + + while (len > 0) + { readcount = (len > 0x10000000) ? 0x10000000 : (int) len ; + + count = msadpcm_read_block (psf, pms, ptr, readcount) ; + + total += count ; + len -= count ; + if (count != readcount) + break ; + } ; + + return total ; +} /* msadpcm_read_s */ + +static sf_count_t +msadpcm_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len) +{ MSADPCM_PRIVATE *pms ; + short *sptr ; + int k, bufferlen, readcount = 0, count ; + sf_count_t total = 0 ; + + if (! psf->codec_data) + return 0 ; + pms = (MSADPCM_PRIVATE*) psf->codec_data ; + + sptr = psf->u.sbuf ; + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + while (len > 0) + { readcount = (len >= bufferlen) ? bufferlen : len ; + count = msadpcm_read_block (psf, pms, sptr, readcount) ; + for (k = 0 ; k < readcount ; k++) + ptr [total + k] = sptr [k] << 16 ; + total += count ; + len -= readcount ; + if (count != readcount) + break ; + } ; + return total ; +} /* msadpcm_read_i */ + +static sf_count_t +msadpcm_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len) +{ MSADPCM_PRIVATE *pms ; + short *sptr ; + int k, bufferlen, readcount = 0, count ; + sf_count_t total = 0 ; + float normfact ; + + if (! psf->codec_data) + return 0 ; + pms = (MSADPCM_PRIVATE*) psf->codec_data ; + + normfact = (psf->norm_float == SF_TRUE) ? 1.0 / ((float) 0x8000) : 1.0 ; + sptr = psf->u.sbuf ; + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + while (len > 0) + { readcount = (len >= bufferlen) ? bufferlen : len ; + count = msadpcm_read_block (psf, pms, sptr, readcount) ; + for (k = 0 ; k < readcount ; k++) + ptr [total + k] = normfact * (float) (sptr [k]) ; + total += count ; + len -= readcount ; + if (count != readcount) + break ; + } ; + return total ; +} /* msadpcm_read_f */ + +static sf_count_t +msadpcm_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len) +{ MSADPCM_PRIVATE *pms ; + short *sptr ; + int k, bufferlen, readcount = 0, count ; + sf_count_t total = 0 ; + double normfact ; + + normfact = (psf->norm_double == SF_TRUE) ? 1.0 / ((double) 0x8000) : 1.0 ; + + if (! psf->codec_data) + return 0 ; + pms = (MSADPCM_PRIVATE*) psf->codec_data ; + + sptr = psf->u.sbuf ; + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + while (len > 0) + { readcount = (len >= bufferlen) ? bufferlen : len ; + count = msadpcm_read_block (psf, pms, sptr, readcount) ; + for (k = 0 ; k < readcount ; k++) + ptr [total + k] = normfact * (double) (sptr [k]) ; + total += count ; + len -= readcount ; + if (count != readcount) + break ; + } ; + return total ; +} /* msadpcm_read_d */ + +static sf_count_t +msadpcm_seek (SF_PRIVATE *psf, int mode, sf_count_t offset) +{ MSADPCM_PRIVATE *pms ; + int newblock, newsample ; + + if (! psf->codec_data) + return 0 ; + pms = (MSADPCM_PRIVATE*) psf->codec_data ; + + if (psf->datalength < 0 || psf->dataoffset < 0) + { psf->error = SFE_BAD_SEEK ; + return PSF_SEEK_ERROR ; + } ; + + if (offset == 0) + { psf_fseek (psf, psf->dataoffset, SEEK_SET) ; + pms->blockcount = 0 ; + msadpcm_decode_block (psf, pms) ; + pms->samplecount = 0 ; + return 0 ; + } ; + + if (offset < 0 || offset > pms->blocks * pms->samplesperblock) + { psf->error = SFE_BAD_SEEK ; + return PSF_SEEK_ERROR ; + } ; + + newblock = offset / pms->samplesperblock ; + newsample = offset % pms->samplesperblock ; + + if (mode == SFM_READ) + { psf_fseek (psf, psf->dataoffset + newblock * pms->blocksize, SEEK_SET) ; + pms->blockcount = newblock ; + msadpcm_decode_block (psf, pms) ; + pms->samplecount = newsample ; + } + else + { /* What to do about write??? */ + psf->error = SFE_BAD_SEEK ; + return PSF_SEEK_ERROR ; + } ; + + return newblock * pms->samplesperblock + newsample ; +} /* msadpcm_seek */ + +/*========================================================================================== +** MS ADPCM Write Functions. +*/ + +void +msadpcm_write_adapt_coeffs (SF_PRIVATE *psf) +{ int k ; + + for (k = 0 ; k < MSADPCM_ADAPT_COEFF_COUNT ; k++) + psf_binheader_writef (psf, "22", AdaptCoeff1 [k], AdaptCoeff2 [k]) ; +} /* msadpcm_write_adapt_coeffs */ + +/*========================================================================================== +*/ + +static int +msadpcm_encode_block (SF_PRIVATE *psf, MSADPCM_PRIVATE *pms) +{ unsigned int blockindx ; + unsigned char byte ; + int chan, k, predict, bpred [2], idelta [2], errordelta, newsamp ; + + choose_predictor (pms->channels, pms->samples, bpred, idelta) ; + + /* Write the block header. */ + + if (pms->channels == 1) + { pms->block [0] = bpred [0] ; + pms->block [1] = idelta [0] & 0xFF ; + pms->block [2] = idelta [0] >> 8 ; + pms->block [3] = pms->samples [1] & 0xFF ; + pms->block [4] = pms->samples [1] >> 8 ; + pms->block [5] = pms->samples [0] & 0xFF ; + pms->block [6] = pms->samples [0] >> 8 ; + + blockindx = 7 ; + byte = 0 ; + + /* Encode the samples as 4 bit. */ + + for (k = 2 ; k < pms->samplesperblock ; k++) + { predict = (pms->samples [k-1] * AdaptCoeff1 [bpred [0]] + pms->samples [k-2] * AdaptCoeff2 [bpred [0]]) >> 8 ; + errordelta = (pms->samples [k] - predict) / idelta [0] ; + if (errordelta < -8) + errordelta = -8 ; + else if (errordelta > 7) + errordelta = 7 ; + newsamp = predict + (idelta [0] * errordelta) ; + if (newsamp > 32767) + newsamp = 32767 ; + else if (newsamp < -32768) + newsamp = -32768 ; + if (errordelta < 0) + errordelta += 0x10 ; + + byte = (byte << 4) | (errordelta & 0xF) ; + if (k % 2) + { pms->block [blockindx++] = byte ; + byte = 0 ; + } ; + + idelta [0] = (idelta [0] * AdaptationTable [errordelta]) >> 8 ; + if (idelta [0] < 16) + idelta [0] = 16 ; + pms->samples [k] = newsamp ; + } ; + } + else + { /* Stereo file. */ + pms->block [0] = bpred [0] ; + pms->block [1] = bpred [1] ; + + pms->block [2] = idelta [0] & 0xFF ; + pms->block [3] = idelta [0] >> 8 ; + pms->block [4] = idelta [1] & 0xFF ; + pms->block [5] = idelta [1] >> 8 ; + + pms->block [6] = pms->samples [2] & 0xFF ; + pms->block [7] = pms->samples [2] >> 8 ; + pms->block [8] = pms->samples [3] & 0xFF ; + pms->block [9] = pms->samples [3] >> 8 ; + + pms->block [10] = pms->samples [0] & 0xFF ; + pms->block [11] = pms->samples [0] >> 8 ; + pms->block [12] = pms->samples [1] & 0xFF ; + pms->block [13] = pms->samples [1] >> 8 ; + + blockindx = 14 ; + byte = 0 ; + chan = 1 ; + + for (k = 4 ; k < 2 * pms->samplesperblock ; k++) + { chan = k & 1 ; + + predict = (pms->samples [k-2] * AdaptCoeff1 [bpred [chan]] + pms->samples [k-4] * AdaptCoeff2 [bpred [chan]]) >> 8 ; + errordelta = (pms->samples [k] - predict) / idelta [chan] ; + + + if (errordelta < -8) + errordelta = -8 ; + else if (errordelta > 7) + errordelta = 7 ; + newsamp = predict + (idelta [chan] * errordelta) ; + if (newsamp > 32767) + newsamp = 32767 ; + else if (newsamp < -32768) + newsamp = -32768 ; + if (errordelta < 0) + errordelta += 0x10 ; + + byte = (byte << 4) | (errordelta & 0xF) ; + + if (chan) + { pms->block [blockindx++] = byte ; + byte = 0 ; + } ; + + idelta [chan] = (idelta [chan] * AdaptationTable [errordelta]) >> 8 ; + if (idelta [chan] < 16) + idelta [chan] = 16 ; + pms->samples [k] = newsamp ; + } ; + } ; + + /* Write the block to disk. */ + + if ((k = psf_fwrite (pms->block, 1, pms->blocksize, psf)) != pms->blocksize) + psf_log_printf (psf, "*** Warning : short write (%d != %d).\n", k, pms->blocksize) ; + + memset (pms->samples, 0, pms->samplesperblock * sizeof (short)) ; + + pms->blockcount ++ ; + pms->samplecount = 0 ; + + return 1 ; +} /* msadpcm_encode_block */ + +static sf_count_t +msadpcm_write_block (SF_PRIVATE *psf, MSADPCM_PRIVATE *pms, const short *ptr, int len) +{ int count, total = 0, indx = 0 ; + + while (indx < len) + { count = (pms->samplesperblock - pms->samplecount) * pms->channels ; + + if (count > len - indx) + count = len - indx ; + + memcpy (&(pms->samples [pms->samplecount * pms->channels]), &(ptr [total]), count * sizeof (short)) ; + indx += count ; + pms->samplecount += count / pms->channels ; + total = indx ; + + if (pms->samplecount >= pms->samplesperblock) + msadpcm_encode_block (psf, pms) ; + } ; + + return total ; +} /* msadpcm_write_block */ + +static sf_count_t +msadpcm_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len) +{ MSADPCM_PRIVATE *pms ; + int writecount, count ; + sf_count_t total = 0 ; + + if (! psf->codec_data) + return 0 ; + pms = (MSADPCM_PRIVATE*) psf->codec_data ; + + while (len > 0) + { writecount = (len > 0x10000000) ? 0x10000000 : (int) len ; + + count = msadpcm_write_block (psf, pms, ptr, writecount) ; + + total += count ; + len -= count ; + if (count != writecount) + break ; + } ; + + return total ; +} /* msadpcm_write_s */ + +static sf_count_t +msadpcm_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t len) +{ MSADPCM_PRIVATE *pms ; + short *sptr ; + int k, bufferlen, writecount, count ; + sf_count_t total = 0 ; + + if (! psf->codec_data) + return 0 ; + pms = (MSADPCM_PRIVATE*) psf->codec_data ; + + sptr = psf->u.sbuf ; + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + while (len > 0) + { writecount = (len >= bufferlen) ? bufferlen : len ; + for (k = 0 ; k < writecount ; k++) + sptr [k] = ptr [total + k] >> 16 ; + count = msadpcm_write_block (psf, pms, sptr, writecount) ; + total += count ; + len -= writecount ; + if (count != writecount) + break ; + } ; + return total ; +} /* msadpcm_write_i */ + +static sf_count_t +msadpcm_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len) +{ MSADPCM_PRIVATE *pms ; + short *sptr ; + int k, bufferlen, writecount, count ; + sf_count_t total = 0 ; + float normfact ; + + if (! psf->codec_data) + return 0 ; + pms = (MSADPCM_PRIVATE*) psf->codec_data ; + + normfact = (psf->norm_float == SF_TRUE) ? (1.0 * 0x7FFF) : 1.0 ; + + sptr = psf->u.sbuf ; + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + while (len > 0) + { writecount = (len >= bufferlen) ? bufferlen : len ; + for (k = 0 ; k < writecount ; k++) + sptr [k] = lrintf (normfact * ptr [total + k]) ; + count = msadpcm_write_block (psf, pms, sptr, writecount) ; + total += count ; + len -= writecount ; + if (count != writecount) + break ; + } ; + return total ; +} /* msadpcm_write_f */ + +static sf_count_t +msadpcm_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len) +{ MSADPCM_PRIVATE *pms ; + short *sptr ; + int k, bufferlen, writecount, count ; + sf_count_t total = 0 ; + double normfact ; + + normfact = (psf->norm_double == SF_TRUE) ? (1.0 * 0x7FFF) : 1.0 ; + + if (! psf->codec_data) + return 0 ; + pms = (MSADPCM_PRIVATE*) psf->codec_data ; + + sptr = psf->u.sbuf ; + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + while (len > 0) + { writecount = (len >= bufferlen) ? bufferlen : len ; + for (k = 0 ; k < writecount ; k++) + sptr [k] = lrint (normfact * ptr [total + k]) ; + count = msadpcm_write_block (psf, pms, sptr, writecount) ; + total += count ; + len -= writecount ; + if (count != writecount) + break ; + } ; + return total ; +} /* msadpcm_write_d */ + +/*======================================================================================== +*/ + +static int +msadpcm_close (SF_PRIVATE *psf) +{ MSADPCM_PRIVATE *pms ; + + pms = (MSADPCM_PRIVATE*) psf->codec_data ; + + if (psf->mode == SFM_WRITE) + { /* Now we know static int for certain the length of the file we can + ** re-write the header. + */ + + if (pms->samplecount && pms->samplecount < pms->samplesperblock) + msadpcm_encode_block (psf, pms) ; + } ; + + return 0 ; +} /* msadpcm_close */ + +/*======================================================================================== +** Static functions. +*/ + +/*---------------------------------------------------------------------------------------- +** Choosing the block predictor. +** Each block requires a predictor and an idelta for each channel. +** The predictor is in the range [0..6] which is an indx into the two AdaptCoeff tables. +** The predictor is chosen by trying all of the possible predictors on a small set of +** samples at the beginning of the block. The predictor with the smallest average +** abs (idelta) is chosen as the best predictor for this block. +** The value of idelta is chosen to to give a 4 bit code value of +/- 4 (approx. half the +** max. code value). If the average abs (idelta) is zero, the sixth predictor is chosen. +** If the value of idelta is less then 16 it is set to 16. +** +** Microsoft uses an IDELTA_COUNT (number of sample pairs used to choose best predictor) +** value of 3. The best possible results would be obtained by using all the samples to +** choose the predictor. +*/ + +#define IDELTA_COUNT 3 + +static void +choose_predictor (unsigned int channels, short *data, int *block_pred, int *idelta) +{ unsigned int chan, k, bpred, idelta_sum, best_bpred, best_idelta ; + + for (chan = 0 ; chan < channels ; chan++) + { best_bpred = best_idelta = 0 ; + + for (bpred = 0 ; bpred < 7 ; bpred++) + { idelta_sum = 0 ; + for (k = 2 ; k < 2 + IDELTA_COUNT ; k++) + idelta_sum += abs (data [k * channels] - ((data [(k - 1) * channels] * AdaptCoeff1 [bpred] + data [(k - 2) * channels] * AdaptCoeff2 [bpred]) >> 8)) ; + idelta_sum /= (4 * IDELTA_COUNT) ; + + if (bpred == 0 || idelta_sum < best_idelta) + { best_bpred = bpred ; + best_idelta = idelta_sum ; + } ; + + if (! idelta_sum) + { best_bpred = bpred ; + best_idelta = 16 ; + break ; + } ; + + } ; /* for bpred ... */ + if (best_idelta < 16) + best_idelta = 16 ; + + block_pred [chan] = best_bpred ; + idelta [chan] = best_idelta ; + } ; + + return ; +} /* choose_predictor */ +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: a98908a3-5305-4935-872b-77d6a86c330f +*/ diff --git a/src/new.c b/src/new.c new file mode 100644 index 00000000..475e92b3 --- /dev/null +++ b/src/new.c @@ -0,0 +1,123 @@ +/* +** Copyright (C) 2002-2004 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include +#include + +#include "sndfile.h" +#include "sfendian.h" +#include "common.h" + +#if (ENABLE_EXPERIMENTAL_CODE == 0) + +int +new_open (SF_PRIVATE *psf) +{ if (psf) + return SFE_UNIMPLEMENTED ; + return (psf && 0) ; +} /* new_open */ + +#else + +/*------------------------------------------------------------------------------ +** Macros to handle big/little endian issues. +*/ + +/*------------------------------------------------------------------------------ +** Typedefs. +*/ + +/*------------------------------------------------------------------------------ +** Private static functions. +*/ + +static int new_read_header (SF_PRIVATE *psf) ; + +/*------------------------------------------------------------------------------ +** Public function. +*/ + +int +new_open (SF_PRIVATE *psf) +{ int subformat, error = 0 ; + + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + return SFE_UNIMPLEMENTED ; + + if ((error = new_read_header (psf))) + return error ; + + if ((psf->sf.format & SF_FORMAT_TYPEMASK) != SF_FORMAT_WVE) + return SFE_BAD_OPEN_FORMAT ; + + subformat = psf->sf.format & SF_FORMAT_SUBMASK ; + + return error ; +} /* new_open */ + +/*------------------------------------------------------------------------------ +*/ + +static int +new_read_header (SF_PRIVATE *psf) +{ int marker ; + + /* Set position to start of file to begin reading header. */ + psf_binheader_readf (psf, "pm", 0, &marker) ; + if (marker != ALAW_MARKER) + return SFE_WVE_NOT_WVE ; + + psf_binheader_readf (psf, "m", &marker) ; + if (marker != SOUN_MARKER) + return SFE_WVE_NOT_WVE ; + + psf_binheader_readf (psf, "m", &marker) ; + if (marker != DFIL_MARKER) + return SFE_WVE_NOT_WVE ; + + psf_log_printf (psf, "Read only : Psion Alaw\n" + " Sample Rate : 8000\n" + " Channels : 1\n" + " Encoding : A-law\n") ; + + psf->dataoffset = 0x20 ; + psf->datalength = psf->filelength - psf->dataoffset ; + + psf->sf.format = SF_FORMAT_WVE | SF_FORMAT_ALAW ; + psf->sf.samplerate = 8000 ; + psf->sf.frames = psf->datalength ; + psf->sf.channels = 1 ; + + return alaw_init (psf) ; +} /* new_read_header */ + +/*------------------------------------------------------------------------------ +*/ + +#endif +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 98c77ff6-81a6-440d-bad1-4597f51df956 +*/ diff --git a/src/nist.c b/src/nist.c new file mode 100644 index 00000000..2ab20dbf --- /dev/null +++ b/src/nist.c @@ -0,0 +1,367 @@ +/* +** Copyright (C) 1999-2004 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* +** Some of the information used to read NIST files was gleaned from +** reading the code of Bill Schottstaedt's sndlib library +** ftp://ccrma-ftp.stanford.edu/pub/Lisp/sndlib.tar.gz +** However, no code from that package was used. +*/ + +#include "sfconfig.h" + +#include +#include +#include +#include + +#include "sndfile.h" +#include "sfendian.h" +#include "common.h" + +/*------------------------------------------------------------------------------ +*/ + +#define NIST_HEADER_LENGTH 1024 + +/*------------------------------------------------------------------------------ +** Private static functions. +*/ + +static int nist_close (SF_PRIVATE *psf) ; +static int nist_write_header (SF_PRIVATE *psf, int calc_length) ; +static int nist_read_header (SF_PRIVATE *psf) ; + +/*------------------------------------------------------------------------------ +*/ + +int +nist_open (SF_PRIVATE *psf) +{ int error ; + + if (psf->mode == SFM_READ || (psf->mode == SFM_RDWR && psf->filelength > 0)) + { if ((error = nist_read_header (psf))) + return error ; + } ; + + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + { if (psf->is_pipe) + return SFE_NO_PIPE_WRITE ; + + if ((psf->sf.format & SF_FORMAT_TYPEMASK) != SF_FORMAT_NIST) + return SFE_BAD_OPEN_FORMAT ; + + psf->endian = psf->sf.format & SF_FORMAT_ENDMASK ; + if (psf->endian == 0 || psf->endian == SF_ENDIAN_CPU) + psf->endian = (CPU_IS_BIG_ENDIAN) ? SF_ENDIAN_BIG : SF_ENDIAN_LITTLE ; + + psf->blockwidth = psf->bytewidth * psf->sf.channels ; + psf->sf.frames = 0 ; + + if ((error = nist_write_header (psf, SF_FALSE))) + return error ; + + psf->write_header = nist_write_header ; + } ; + + psf->container_close = nist_close ; + + switch (psf->sf.format & SF_FORMAT_SUBMASK) + { case SF_FORMAT_PCM_S8 : + error = pcm_init (psf) ; + break ; + + case SF_FORMAT_PCM_16 : + case SF_FORMAT_PCM_24 : + case SF_FORMAT_PCM_32 : + error = pcm_init (psf) ; + break ; + + case SF_FORMAT_ULAW : + error = ulaw_init (psf) ; + break ; + + case SF_FORMAT_ALAW : + error = alaw_init (psf) ; + break ; + + default : error = SFE_UNIMPLEMENTED ; + break ; + } ; + + return error ; +} /* nist_open */ + +/*------------------------------------------------------------------------------ +*/ + +static char bad_header [] = +{ 'N', 'I', 'S', 'T', '_', '1', 'A', 0x0d, 0x0a, + ' ', ' ', ' ', '1', '0', '2', '4', 0x0d, 0x0a, + 0 +} ; + + static int +nist_read_header (SF_PRIVATE *psf) +{ char *psf_header ; + int bitwidth = 0, bytes = 0, count, encoding ; + char str [64], *cptr ; + long samples ; + + psf->sf.format = SF_FORMAT_NIST ; + + psf_header = psf->u.cbuf ; + + if (sizeof (psf->header) <= NIST_HEADER_LENGTH) + return SFE_INTERNAL ; + + /* Go to start of file and read in the whole header. */ + psf_binheader_readf (psf, "pb", 0, psf_header, NIST_HEADER_LENGTH) ; + + /* Header is a string, so make sure it is null terminated. */ + psf_header [NIST_HEADER_LENGTH] = 0 ; + + /* Now trim the header after the end marker. */ + if ((cptr = strstr (psf_header, "end_head"))) + { cptr += strlen ("end_head") + 1 ; + cptr [0] = 0 ; + } ; + + if (strstr (psf_header, bad_header) == psf_header) + return SFE_NIST_CRLF_CONVERISON ; + + /* Make sure its a NIST file. */ + if (strstr (psf_header, "NIST_1A\n") != psf_header) + { psf_log_printf (psf, "Not a NIST file.\n") ; + return SFE_NIST_BAD_HEADER ; + } ; + + if (sscanf (psf_header, "NIST_1A\n%d\n", &count) == 1) + psf->dataoffset = count ; + else + { psf_log_printf (psf, "*** Suspicious header length.\n") ; + psf->dataoffset = NIST_HEADER_LENGTH ; + } ; + + /* Determine sample encoding, start by assuming PCM. */ + encoding = SF_FORMAT_PCM_U8 ; + if ((cptr = strstr (psf_header, "sample_coding -s"))) + { sscanf (cptr, "sample_coding -s%d %63s", &count, str) ; + + if (strcmp (str, "pcm") == 0) + encoding = SF_FORMAT_PCM_U8 ; + else if (strcmp (str, "alaw") == 0) + encoding = SF_FORMAT_ALAW ; + else if ((strcmp (str, "ulaw") == 0) || (strcmp (str, "mu-law") == 0)) + encoding = SF_FORMAT_ULAW ; + else + { psf_log_printf (psf, "*** Unknown encoding : %s\n", str) ; + encoding = 0 ; + } ; + } ; + + if ((cptr = strstr (psf_header, "channel_count -i "))) + sscanf (cptr, "channel_count -i %d", &(psf->sf.channels)) ; + + if ((cptr = strstr (psf_header, "sample_rate -i "))) + sscanf (cptr, "sample_rate -i %d", &(psf->sf.samplerate)) ; + + if ((cptr = strstr (psf_header, "sample_count -i "))) + { sscanf (psf_header, "sample_count -i %ld", &samples) ; + psf->sf.frames = samples ; + } ; + + if ((cptr = strstr (psf_header, "sample_n_bytes -i "))) + sscanf (cptr, "sample_n_bytes -i %d", &(psf->bytewidth)) ; + + /* Default endian-ness (for 8 bit, u-law, A-law. */ + psf->endian = (CPU_IS_BIG_ENDIAN) ? SF_ENDIAN_BIG : SF_ENDIAN_LITTLE ; + + /* This is where we figure out endian-ness. */ + if ((cptr = strstr (psf_header, "sample_byte_format -s"))) + { sscanf (cptr, "sample_byte_format -s%d %8s", &bytes, str) ; + if (bytes > 1) + { if (psf->bytewidth == 0) + psf->bytewidth = bytes ; + else if (psf->bytewidth != bytes) + { psf_log_printf (psf, "psf->bytewidth (%d) != bytes (%d)\n", psf->bytewidth, bytes) ; + return SFE_NIST_BAD_ENCODING ; + } ; + + if (strstr (str, "01") == str) + psf->endian = SF_ENDIAN_LITTLE ; + else if (strstr (str, "10")) + psf->endian = SF_ENDIAN_BIG ; + else + { psf_log_printf (psf, "Weird endian-ness : %s\n", str) ; + return SFE_NIST_BAD_ENCODING ; + } ; + } ; + + psf->sf.format |= psf->endian ; + } ; + + if ((cptr = strstr (psf_header, "sample_sig_bits -i "))) + sscanf (cptr, "sample_sig_bits -i %d", &bitwidth) ; + + if (strstr (psf_header, "channels_interleaved -s5 FALSE")) + { psf_log_printf (psf, "Non-interleaved data unsupported.\n", str) ; + return SFE_NIST_BAD_ENCODING ; + } ; + + psf->blockwidth = psf->sf.channels * psf->bytewidth ; + psf->datalength = psf->filelength - psf->dataoffset ; + + psf_fseek (psf, psf->dataoffset, SEEK_SET) ; + + if (encoding == SF_FORMAT_PCM_U8) + { switch (psf->bytewidth) + { case 1 : + psf->sf.format |= SF_FORMAT_PCM_S8 ; + break ; + + case 2 : + psf->sf.format |= SF_FORMAT_PCM_16 ; + break ; + + case 3 : + psf->sf.format |= SF_FORMAT_PCM_24 ; + break ; + + case 4 : + psf->sf.format |= SF_FORMAT_PCM_32 ; + break ; + + default : break ; + } ; + } + else if (encoding != 0) + psf->sf.format |= encoding ; + else + return SFE_UNIMPLEMENTED ; + + return 0 ; +} /* nist_read_header */ + +static int +nist_close (SF_PRIVATE *psf) +{ + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + nist_write_header (psf, SF_TRUE) ; + + return 0 ; +} /* nist_close */ + +/*========================================================================= +*/ + +static int +nist_write_header (SF_PRIVATE *psf, int calc_length) +{ const char *end_str ; + long samples ; + sf_count_t current ; + + current = psf_ftell (psf) ; + + if (calc_length) + { psf->filelength = psf_get_filelen (psf) ; + + psf->datalength = psf->filelength - psf->dataoffset ; + + if (psf->dataend) + psf->datalength -= psf->filelength - psf->dataend ; + + if (psf->bytewidth > 0) + psf->sf.frames = psf->datalength / (psf->bytewidth * psf->sf.channels) ; + } ; + + if (psf->endian == SF_ENDIAN_BIG) + end_str = "10" ; + else if (psf->endian == SF_ENDIAN_LITTLE) + end_str = "01" ; + else + end_str = "error" ; + + /* Clear the whole header. */ + memset (psf->header, 0, sizeof (psf->header)) ; + psf->headindex = 0 ; + + psf_fseek (psf, 0, SEEK_SET) ; + + psf_asciiheader_printf (psf, "NIST_1A\n 1024\n") ; + psf_asciiheader_printf (psf, "channel_count -i %d\n", psf->sf.channels) ; + psf_asciiheader_printf (psf, "sample_rate -i %d\n", psf->sf.samplerate) ; + + switch (psf->sf.format & SF_FORMAT_SUBMASK) + { case SF_FORMAT_PCM_S8 : + psf_asciiheader_printf (psf, "sample_coding -s3 pcm\n") ; + psf_asciiheader_printf (psf, "sample_n_bytes -i 1\n" + "sample_sig_bits -i 8\n") ; + break ; + + case SF_FORMAT_PCM_16 : + case SF_FORMAT_PCM_24 : + case SF_FORMAT_PCM_32 : + psf_asciiheader_printf (psf, "sample_n_bytes -i %d\n", psf->bytewidth) ; + psf_asciiheader_printf (psf, "sample_sig_bits -i %d\n", psf->bytewidth * 8) ; + psf_asciiheader_printf (psf, "sample_coding -s3 pcm\n" + "sample_byte_format -s%d %s\n", psf->bytewidth, end_str) ; + break ; + + case SF_FORMAT_ALAW : + psf_asciiheader_printf (psf, "sample_coding -s4 alaw\n") ; + psf_asciiheader_printf (psf, "sample_n_bytes -s1 1\n") ; + break ; + + case SF_FORMAT_ULAW : + psf_asciiheader_printf (psf, "sample_coding -s4 ulaw\n") ; + psf_asciiheader_printf (psf, "sample_n_bytes -s1 1\n") ; + break ; + + default : return SFE_UNIMPLEMENTED ; + } ; + + psf->dataoffset = NIST_HEADER_LENGTH ; + + /* Fix this */ + samples = psf->sf.frames ; + psf_asciiheader_printf (psf, "sample_count -i %ld\n", samples) ; + psf_asciiheader_printf (psf, "end_head\n") ; + + /* Zero fill to dataoffset. */ + psf_binheader_writef (psf, "z", (size_t) (NIST_HEADER_LENGTH - psf->headindex)) ; + + psf_fwrite (psf->header, psf->headindex, 1, psf) ; + + if (psf->error) + return psf->error ; + + if (current > 0) + psf_fseek (psf, current, SEEK_SET) ; + + return psf->error ; +} /* nist_write_header */ + + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: b45ed85d-9e22-4ad9-b78c-4b58b67152a8 +*/ diff --git a/src/ogg.c b/src/ogg.c new file mode 100644 index 00000000..4bcc5952 --- /dev/null +++ b/src/ogg.c @@ -0,0 +1,44 @@ +/* +** Copyright (C) 2002-2004 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY ; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program ; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include +#include + +#include "sndfile.h" +#include "sfendian.h" +#include "common.h" + +int +ogg_open (SF_PRIVATE *psf) +{ if (psf) + return SFE_UNIMPLEMENTED ; + return 0 ; +} /* ogg_open */ + + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 9ff1fe9c-629e-4e9c-9ef5-3d0eb1e427a0 +*/ diff --git a/src/paf.c b/src/paf.c new file mode 100644 index 00000000..8d52e1c3 --- /dev/null +++ b/src/paf.c @@ -0,0 +1,838 @@ +/* +** Copyright (C) 1999-2005 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include +#include +#include + +#include "sndfile.h" +#include "sfendian.h" +#include "float_cast.h" +#include "common.h" + +/*------------------------------------------------------------------------------ +** Macros to handle big/little endian issues. +*/ + +#define FAP_MARKER (MAKE_MARKER ('f', 'a', 'p', ' ')) +#define PAF_MARKER (MAKE_MARKER (' ', 'p', 'a', 'f')) + +/*------------------------------------------------------------------------------ +** Other defines. +*/ + +#define PAF_HEADER_LENGTH 2048 + +#define PAF24_SAMPLES_PER_BLOCK 10 +#define PAF24_BLOCK_SIZE 32 + +/*------------------------------------------------------------------------------ +** Typedefs. +*/ + +typedef struct +{ int version ; + int endianness ; + int samplerate ; + int format ; + int channels ; + int source ; +} PAF_FMT ; + +typedef struct +{ int max_blocks, channels, samplesperblock, blocksize ; + int read_block, write_block, read_count, write_count ; + sf_count_t sample_count ; + int *samples ; + unsigned char *block ; +#if HAVE_FLEXIBLE_ARRAY + int data [] ; /* ISO C99 struct flexible array. */ +#else + int data [1] ; /* This is a hack and may not work. */ +#endif +} PAF24_PRIVATE ; + +/*------------------------------------------------------------------------------ +** Private static functions. +*/ + +static int paf24_init (SF_PRIVATE *psf) ; + +static int paf_read_header (SF_PRIVATE *psf) ; +static int paf_write_header (SF_PRIVATE *psf, int calc_length) ; + +static sf_count_t paf24_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ; +static sf_count_t paf24_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ; +static sf_count_t paf24_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ; +static sf_count_t paf24_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ; + +static sf_count_t paf24_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ; +static sf_count_t paf24_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ; +static sf_count_t paf24_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ; +static sf_count_t paf24_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ; + +static sf_count_t paf24_seek (SF_PRIVATE *psf, int mode, sf_count_t offset) ; + +enum +{ PAF_PCM_16 = 0, + PAF_PCM_24 = 1, + PAF_PCM_S8 = 2 +} ; + +/*------------------------------------------------------------------------------ +** Public function. +*/ + +int +paf_open (SF_PRIVATE *psf) +{ int subformat, error, endian ; + + psf->dataoffset = PAF_HEADER_LENGTH ; + + if (psf->mode == SFM_READ || (psf->mode == SFM_RDWR && psf->filelength > 0)) + { if ((error = paf_read_header (psf))) + return error ; + } ; + + subformat = psf->sf.format & SF_FORMAT_SUBMASK ; + + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + { if ((psf->sf.format & SF_FORMAT_TYPEMASK) != SF_FORMAT_PAF) + return SFE_BAD_OPEN_FORMAT ; + + endian = psf->sf.format & SF_FORMAT_ENDMASK ; + + /* PAF is by default big endian. */ + psf->endian = SF_ENDIAN_BIG ; + + if (endian == SF_ENDIAN_LITTLE || (CPU_IS_LITTLE_ENDIAN && (endian == SF_ENDIAN_CPU))) + psf->endian = SF_ENDIAN_LITTLE ; + + if ((error = paf_write_header (psf, SF_FALSE))) + return error ; + + psf->write_header = paf_write_header ; + } ; + + switch (subformat) + { case SF_FORMAT_PCM_S8 : + psf->bytewidth = 1 ; + error = pcm_init (psf) ; + break ; + + case SF_FORMAT_PCM_16 : + psf->bytewidth = 2 ; + error = pcm_init (psf) ; + break ; + + case SF_FORMAT_PCM_24 : + /* No bytewidth because of whacky 24 bit encoding. */ + error = paf24_init (psf) ; + break ; + + default : return SFE_PAF_UNKNOWN_FORMAT ; + } ; + + return error ; +} /* paf_open */ + +/*------------------------------------------------------------------------------ +*/ + +static int +paf_read_header (SF_PRIVATE *psf) +{ PAF_FMT paf_fmt ; + int marker ; + + memset (&paf_fmt, 0, sizeof (paf_fmt)) ; + psf_binheader_readf (psf, "pm", 0, &marker) ; + + psf_log_printf (psf, "Signature : '%M'\n", marker) ; + + if (marker == PAF_MARKER) + { psf_binheader_readf (psf, "E444444", &(paf_fmt.version), &(paf_fmt.endianness), + &(paf_fmt.samplerate), &(paf_fmt.format), &(paf_fmt.channels), &(paf_fmt.source)) ; + } + else if (marker == FAP_MARKER) + { psf_binheader_readf (psf, "e444444", &(paf_fmt.version), &(paf_fmt.endianness), + &(paf_fmt.samplerate), &(paf_fmt.format), &(paf_fmt.channels), &(paf_fmt.source)) ; + } + else + return SFE_PAF_NO_MARKER ; + + psf_log_printf (psf, "Version : %d\n", paf_fmt.version) ; + + if (paf_fmt.version != 0) + { psf_log_printf (psf, "*** Bad version number. should be zero.\n") ; + return SFE_PAF_VERSION ; + } ; + + psf_log_printf (psf, "Sample Rate : %d\n", paf_fmt.samplerate) ; + psf_log_printf (psf, "Channels : %d\n", paf_fmt.channels) ; + + psf_log_printf (psf, "Endianness : %d => ", paf_fmt.endianness) ; + if (paf_fmt.endianness) + { psf_log_printf (psf, "Little\n", paf_fmt.endianness) ; + psf->endian = SF_ENDIAN_LITTLE ; + } + else + { psf_log_printf (psf, "Big\n", paf_fmt.endianness) ; + psf->endian = SF_ENDIAN_BIG ; + } ; + + if (psf->filelength < PAF_HEADER_LENGTH) + return SFE_PAF_SHORT_HEADER ; + + psf->datalength = psf->filelength - psf->dataoffset ; + + psf_binheader_readf (psf, "p", (int) psf->dataoffset) ; + + psf->sf.samplerate = paf_fmt.samplerate ; + psf->sf.channels = paf_fmt.channels ; + + /* Only fill in type major. */ + psf->sf.format = SF_FORMAT_PAF ; + + psf_log_printf (psf, "Format : %d => ", paf_fmt.format) ; + + /* PAF is by default big endian. */ + psf->sf.format |= paf_fmt.endianness ? SF_ENDIAN_LITTLE : SF_ENDIAN_BIG ; + + switch (paf_fmt.format) + { case PAF_PCM_S8 : + psf_log_printf (psf, "8 bit linear PCM\n") ; + psf->bytewidth = 1 ; + + psf->sf.format |= SF_FORMAT_PCM_S8 ; + + psf->blockwidth = psf->bytewidth * psf->sf.channels ; + psf->sf.frames = psf->datalength / psf->blockwidth ; + break ; + + case PAF_PCM_16 : + psf_log_printf (psf, "16 bit linear PCM\n") ; + psf->bytewidth = 2 ; + + psf->sf.format |= SF_FORMAT_PCM_16 ; + + psf->blockwidth = psf->bytewidth * psf->sf.channels ; + psf->sf.frames = psf->datalength / psf->blockwidth ; + break ; + + case PAF_PCM_24 : + psf_log_printf (psf, "24 bit linear PCM\n") ; + psf->bytewidth = 3 ; + + psf->sf.format |= SF_FORMAT_PCM_24 ; + + psf->blockwidth = 0 ; + psf->sf.frames = PAF24_SAMPLES_PER_BLOCK * psf->datalength / + (PAF24_BLOCK_SIZE * psf->sf.channels) ; + break ; + + default : psf_log_printf (psf, "Unknown\n") ; + return SFE_PAF_UNKNOWN_FORMAT ; + break ; + } ; + + psf_log_printf (psf, "Source : %d => ", paf_fmt.source) ; + + switch (paf_fmt.source) + { case 1 : psf_log_printf (psf, "Analog Recording\n") ; + break ; + case 2 : psf_log_printf (psf, "Digital Transfer\n") ; + break ; + case 3 : psf_log_printf (psf, "Multi-track Mixdown\n") ; + break ; + case 5 : psf_log_printf (psf, "Audio Resulting From DSP Processing\n") ; + break ; + default : psf_log_printf (psf, "Unknown\n") ; + break ; + } ; + + return 0 ; +} /* paf_read_header */ + +static int +paf_write_header (SF_PRIVATE *psf, int UNUSED (calc_length)) +{ int paf_format ; + + /* PAF header already written so no need to re-write. */ + if (psf_ftell (psf) >= PAF_HEADER_LENGTH) + return 0 ; + + psf->dataoffset = PAF_HEADER_LENGTH ; + + switch (psf->sf.format & SF_FORMAT_SUBMASK) + { case SF_FORMAT_PCM_S8 : + paf_format = PAF_PCM_S8 ; + break ; + + case SF_FORMAT_PCM_16 : + paf_format = PAF_PCM_16 ; + break ; + + case SF_FORMAT_PCM_24 : + paf_format = PAF_PCM_24 ; + break ; + + default : return SFE_PAF_UNKNOWN_FORMAT ; + } ; + + /* Reset the current header length to zero. */ + psf->header [0] = 0 ; + psf->headindex = 0 ; + + if (psf->endian == SF_ENDIAN_BIG) + { /* Marker, version, endianness, samplerate */ + psf_binheader_writef (psf, "Em444", PAF_MARKER, 0, 0, psf->sf.samplerate) ; + /* format, channels, source */ + psf_binheader_writef (psf, "E444", paf_format, psf->sf.channels, 0) ; + } + else if (psf->endian == SF_ENDIAN_LITTLE) + { /* Marker, version, endianness, samplerate */ + psf_binheader_writef (psf, "em444", FAP_MARKER, 0, 1, psf->sf.samplerate) ; + /* format, channels, source */ + psf_binheader_writef (psf, "e444", paf_format, psf->sf.channels, 0) ; + } ; + + /* Zero fill to dataoffset. */ + psf_binheader_writef (psf, "z", (size_t) (psf->dataoffset - psf->headindex)) ; + + psf_fwrite (psf->header, psf->headindex, 1, psf) ; + + return psf->error ; +} /* paf_write_header */ + +/*=============================================================================== +** 24 bit PAF files have a really weird encoding. +** For a mono file, 10 samples (each being 3 bytes) are packed into a 32 byte +** block. The 8 ints in this 32 byte block are then endian swapped (as ints) +** if necessary before being written to disk. +** For a stereo file, blocks of 10 samples from the same channel are encoded +** into 32 bytes as for the mono case. The 32 byte blocks are then interleaved +** on disk. +** Reading has to reverse the above process :-). +** Weird!!! +** +** The code below attempts to gain efficiency while maintaining readability. +*/ + +static int paf24_read_block (SF_PRIVATE *psf, PAF24_PRIVATE *ppaf24) ; +static int paf24_write_block (SF_PRIVATE *psf, PAF24_PRIVATE *ppaf24) ; +static int paf24_close (SF_PRIVATE *psf) ; + + +static int +paf24_init (SF_PRIVATE *psf) +{ PAF24_PRIVATE *ppaf24 ; + int paf24size ; + + paf24size = sizeof (PAF24_PRIVATE) + psf->sf.channels * + (PAF24_BLOCK_SIZE + PAF24_SAMPLES_PER_BLOCK * sizeof (int)) ; + + /* + ** Not exatly sure why this needs to be here but the tests + ** fail without it. + */ + psf->last_op = 0 ; + + if (! (psf->codec_data = malloc (paf24size))) + return SFE_MALLOC_FAILED ; + + ppaf24 = (PAF24_PRIVATE*) psf->codec_data ; + memset (ppaf24, 0, paf24size) ; + + ppaf24->channels = psf->sf.channels ; + ppaf24->samples = ppaf24->data ; + ppaf24->block = (unsigned char*) (ppaf24->data + PAF24_SAMPLES_PER_BLOCK * ppaf24->channels) ; + + ppaf24->blocksize = PAF24_BLOCK_SIZE * ppaf24->channels ; + ppaf24->samplesperblock = PAF24_SAMPLES_PER_BLOCK ; + + if (psf->mode == SFM_READ || psf->mode == SFM_RDWR) + { paf24_read_block (psf, ppaf24) ; /* Read first block. */ + + psf->read_short = paf24_read_s ; + psf->read_int = paf24_read_i ; + psf->read_float = paf24_read_f ; + psf->read_double = paf24_read_d ; + } ; + + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + { psf->write_short = paf24_write_s ; + psf->write_int = paf24_write_i ; + psf->write_float = paf24_write_f ; + psf->write_double = paf24_write_d ; + } ; + + psf->seek = paf24_seek ; + psf->container_close = paf24_close ; + + psf->filelength = psf_get_filelen (psf) ; + psf->datalength = psf->filelength - psf->dataoffset ; + + if (psf->datalength % PAF24_BLOCK_SIZE) + { if (psf->mode == SFM_READ) + psf_log_printf (psf, "*** Warning : file seems to be truncated.\n") ; + ppaf24->max_blocks = psf->datalength / ppaf24->blocksize + 1 ; + } + else + ppaf24->max_blocks = psf->datalength / ppaf24->blocksize ; + + ppaf24->read_block = 0 ; + if (psf->mode == SFM_RDWR) + ppaf24->write_block = ppaf24->max_blocks ; + else + ppaf24->write_block = 0 ; + + psf->sf.frames = ppaf24->samplesperblock * ppaf24->max_blocks ; + ppaf24->sample_count = psf->sf.frames ; + + return 0 ; +} /* paf24_init */ + +static sf_count_t +paf24_seek (SF_PRIVATE *psf, int mode, sf_count_t offset) +{ PAF24_PRIVATE *ppaf24 ; + int newblock, newsample ; + + if (psf->codec_data == NULL) + { psf->error = SFE_INTERNAL ; + return PSF_SEEK_ERROR ; + } ; + + ppaf24 = (PAF24_PRIVATE*) psf->codec_data ; + + if (mode == SFM_READ && ppaf24->write_count > 0) + paf24_write_block (psf, ppaf24) ; + + newblock = offset / ppaf24->samplesperblock ; + newsample = offset % ppaf24->samplesperblock ; + + switch (mode) + { case SFM_READ : + if (psf->last_op == SFM_WRITE && ppaf24->write_count) + paf24_write_block (psf, ppaf24) ; + + psf_fseek (psf, psf->dataoffset + newblock * ppaf24->blocksize, SEEK_SET) ; + ppaf24->read_block = newblock ; + paf24_read_block (psf, ppaf24) ; + ppaf24->read_count = newsample ; + break ; + + case SFM_WRITE : + if (offset > ppaf24->sample_count) + { psf->error = SFE_BAD_SEEK ; + return PSF_SEEK_ERROR ; + } ; + + if (psf->last_op == SFM_WRITE && ppaf24->write_count) + paf24_write_block (psf, ppaf24) ; + + psf_fseek (psf, psf->dataoffset + newblock * ppaf24->blocksize, SEEK_SET) ; + ppaf24->write_block = newblock ; + paf24_read_block (psf, ppaf24) ; + ppaf24->write_count = newsample ; + break ; + + default : + psf->error = SFE_BAD_SEEK ; + return PSF_SEEK_ERROR ; + } ; + + return newblock * ppaf24->samplesperblock + newsample ; +} /* paf24_seek */ + +static int +paf24_close (SF_PRIVATE *psf) +{ PAF24_PRIVATE *ppaf24 ; + + if (psf->codec_data == NULL) + return 0 ; + + ppaf24 = (PAF24_PRIVATE*) psf->codec_data ; + + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + { if (ppaf24->write_count > 0) + paf24_write_block (psf, ppaf24) ; + } ; + + return 0 ; +} /* paf24_close */ + +/*--------------------------------------------------------------------------- +*/ +static int +paf24_read_block (SF_PRIVATE *psf, PAF24_PRIVATE *ppaf24) +{ int k, channel ; + unsigned char *cptr ; + + ppaf24->read_block ++ ; + ppaf24->read_count = 0 ; + + if (ppaf24->read_block * ppaf24->samplesperblock > ppaf24->sample_count) + { memset (ppaf24->samples, 0, ppaf24->samplesperblock * ppaf24->channels) ; + return 1 ; + } ; + + /* Read the block. */ + if ((k = psf_fread (ppaf24->block, 1, ppaf24->blocksize, psf)) != ppaf24->blocksize) + psf_log_printf (psf, "*** Warning : short read (%d != %d).\n", k, ppaf24->blocksize) ; + + + if (CPU_IS_LITTLE_ENDIAN) + { /* Do endian swapping if necessary. */ + if (psf->endian == SF_ENDIAN_BIG) + endswap_int_array (ppaf24->data, 8 * ppaf24->channels) ; + + /* Unpack block. */ + for (k = 0 ; k < PAF24_SAMPLES_PER_BLOCK * ppaf24->channels ; k++) + { channel = k % ppaf24->channels ; + cptr = ppaf24->block + PAF24_BLOCK_SIZE * channel + 3 * (k / ppaf24->channels) ; + ppaf24->samples [k] = (cptr [0] << 8) | (cptr [1] << 16) | (cptr [2] << 24) ; + } ; + } + else + { /* Do endian swapping if necessary. */ + if (psf->endian == SF_ENDIAN_BIG) + endswap_int_array (ppaf24->data, 8 * ppaf24->channels) ; + + /* Unpack block. */ + for (k = 0 ; k < PAF24_SAMPLES_PER_BLOCK * ppaf24->channels ; k++) + { channel = k % ppaf24->channels ; + cptr = ppaf24->block + PAF24_BLOCK_SIZE * channel + 3 * (k / ppaf24->channels) ; + ppaf24->samples [k] = (cptr [0] << 8) | (cptr [1] << 16) | (cptr [2] << 24) ; + } ; + } ; + + return 1 ; +} /* paf24_read_block */ + +static int +paf24_read (SF_PRIVATE *psf, PAF24_PRIVATE *ppaf24, int *ptr, int len) +{ int count, total = 0 ; + + while (total < len) + { if (ppaf24->read_block * ppaf24->samplesperblock >= ppaf24->sample_count) + { memset (&(ptr [total]), 0, (len - total) * sizeof (int)) ; + return total ; + } ; + + if (ppaf24->read_count >= ppaf24->samplesperblock) + paf24_read_block (psf, ppaf24) ; + + count = (ppaf24->samplesperblock - ppaf24->read_count) * ppaf24->channels ; + count = (len - total > count) ? count : len - total ; + + memcpy (&(ptr [total]), &(ppaf24->samples [ppaf24->read_count * ppaf24->channels]), count * sizeof (int)) ; + total += count ; + ppaf24->read_count += count / ppaf24->channels ; + } ; + + return total ; +} /* paf24_read */ + +static sf_count_t +paf24_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len) +{ PAF24_PRIVATE *ppaf24 ; + int *iptr ; + int k, bufferlen, readcount, count ; + sf_count_t total = 0 ; + + if (psf->codec_data == NULL) + return 0 ; + ppaf24 = (PAF24_PRIVATE*) psf->codec_data ; + + iptr = psf->u.ibuf ; + bufferlen = ARRAY_LEN (psf->u.ibuf) ; + while (len > 0) + { readcount = (len >= bufferlen) ? bufferlen : len ; + count = paf24_read (psf, ppaf24, iptr, readcount) ; + for (k = 0 ; k < readcount ; k++) + ptr [total + k] = iptr [k] >> 16 ; + total += count ; + len -= readcount ; + } ; + return total ; +} /* paf24_read_s */ + +static sf_count_t +paf24_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len) +{ PAF24_PRIVATE *ppaf24 ; + int total ; + + if (psf->codec_data == NULL) + return 0 ; + ppaf24 = (PAF24_PRIVATE*) psf->codec_data ; + + total = paf24_read (psf, ppaf24, ptr, len) ; + + return total ; +} /* paf24_read_i */ + +static sf_count_t +paf24_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len) +{ PAF24_PRIVATE *ppaf24 ; + int *iptr ; + int k, bufferlen, readcount, count ; + sf_count_t total = 0 ; + float normfact ; + + if (psf->codec_data == NULL) + return 0 ; + ppaf24 = (PAF24_PRIVATE*) psf->codec_data ; + + normfact = (psf->norm_float == SF_TRUE) ? (1.0 / 0x80000000) : (1.0 / 0x100) ; + + iptr = psf->u.ibuf ; + bufferlen = ARRAY_LEN (psf->u.ibuf) ; + while (len > 0) + { readcount = (len >= bufferlen) ? bufferlen : len ; + count = paf24_read (psf, ppaf24, iptr, readcount) ; + for (k = 0 ; k < readcount ; k++) + ptr [total + k] = normfact * iptr [k] ; + total += count ; + len -= readcount ; + } ; + return total ; +} /* paf24_read_f */ + +static sf_count_t +paf24_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len) +{ PAF24_PRIVATE *ppaf24 ; + int *iptr ; + int k, bufferlen, readcount, count ; + sf_count_t total = 0 ; + double normfact ; + + if (psf->codec_data == NULL) + return 0 ; + ppaf24 = (PAF24_PRIVATE*) psf->codec_data ; + + normfact = (psf->norm_double == SF_TRUE) ? (1.0 / 0x80000000) : (1.0 / 0x100) ; + + iptr = psf->u.ibuf ; + bufferlen = ARRAY_LEN (psf->u.ibuf) ; + while (len > 0) + { readcount = (len >= bufferlen) ? bufferlen : len ; + count = paf24_read (psf, ppaf24, iptr, readcount) ; + for (k = 0 ; k < readcount ; k++) + ptr [total + k] = normfact * iptr [k] ; + total += count ; + len -= readcount ; + } ; + return total ; +} /* paf24_read_d */ + +/*--------------------------------------------------------------------------- +*/ + +static int +paf24_write_block (SF_PRIVATE *psf, PAF24_PRIVATE *ppaf24) +{ int k, nextsample, channel ; + unsigned char *cptr ; + + /* First pack block. */ + + if (CPU_IS_LITTLE_ENDIAN) + { for (k = 0 ; k < PAF24_SAMPLES_PER_BLOCK * ppaf24->channels ; k++) + { channel = k % ppaf24->channels ; + cptr = ppaf24->block + PAF24_BLOCK_SIZE * channel + 3 * (k / ppaf24->channels) ; + nextsample = ppaf24->samples [k] >> 8 ; + cptr [0] = nextsample ; + cptr [1] = nextsample >> 8 ; + cptr [2] = nextsample >> 16 ; + } ; + + /* Do endian swapping if necessary. */ + if (psf->endian == SF_ENDIAN_BIG) + endswap_int_array (ppaf24->data, 8 * ppaf24->channels) ; + } + else if (CPU_IS_BIG_ENDIAN) + { /* This is correct. */ + for (k = 0 ; k < PAF24_SAMPLES_PER_BLOCK * ppaf24->channels ; k++) + { channel = k % ppaf24->channels ; + cptr = ppaf24->block + PAF24_BLOCK_SIZE * channel + 3 * (k / ppaf24->channels) ; + nextsample = ppaf24->samples [k] >> 8 ; + cptr [0] = nextsample ; + cptr [1] = nextsample >> 8 ; + cptr [2] = nextsample >> 16 ; + } ; + if (psf->endian == SF_ENDIAN_BIG) + endswap_int_array (ppaf24->data, 8 * ppaf24->channels) ; + } ; + + /* Write block to disk. */ + if ((k = psf_fwrite (ppaf24->block, 1, ppaf24->blocksize, psf)) != ppaf24->blocksize) + psf_log_printf (psf, "*** Warning : short write (%d != %d).\n", k, ppaf24->blocksize) ; + + if (ppaf24->sample_count < ppaf24->write_block * ppaf24->samplesperblock + ppaf24->write_count) + ppaf24->sample_count = ppaf24->write_block * ppaf24->samplesperblock + ppaf24->write_count ; + + if (ppaf24->write_count == ppaf24->samplesperblock) + { ppaf24->write_block ++ ; + ppaf24->write_count = 0 ; + } ; + + return 1 ; +} /* paf24_write_block */ + +static int +paf24_write (SF_PRIVATE *psf, PAF24_PRIVATE *ppaf24, const int *ptr, int len) +{ int count, total = 0 ; + + while (total < len) + { count = (ppaf24->samplesperblock - ppaf24->write_count) * ppaf24->channels ; + + if (count > len - total) + count = len - total ; + + memcpy (&(ppaf24->samples [ppaf24->write_count * ppaf24->channels]), &(ptr [total]), count * sizeof (int)) ; + total += count ; + ppaf24->write_count += count / ppaf24->channels ; + + if (ppaf24->write_count >= ppaf24->samplesperblock) + paf24_write_block (psf, ppaf24) ; + } ; + + return total ; +} /* paf24_write */ + +static sf_count_t +paf24_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len) +{ PAF24_PRIVATE *ppaf24 ; + int *iptr ; + int k, bufferlen, writecount = 0, count ; + sf_count_t total = 0 ; + + if (psf->codec_data == NULL) + return 0 ; + ppaf24 = (PAF24_PRIVATE*) psf->codec_data ; + + iptr = psf->u.ibuf ; + bufferlen = ARRAY_LEN (psf->u.ibuf) ; + while (len > 0) + { writecount = (len >= bufferlen) ? bufferlen : len ; + for (k = 0 ; k < writecount ; k++) + iptr [k] = ptr [total + k] << 16 ; + count = paf24_write (psf, ppaf24, iptr, writecount) ; + total += count ; + len -= writecount ; + if (count != writecount) + break ; + } ; + return total ; +} /* paf24_write_s */ + +static sf_count_t +paf24_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t len) +{ PAF24_PRIVATE *ppaf24 ; + int writecount, count ; + sf_count_t total = 0 ; + + if (psf->codec_data == NULL) + return 0 ; + ppaf24 = (PAF24_PRIVATE*) psf->codec_data ; + + while (len > 0) + { writecount = (len > 0x10000000) ? 0x10000000 : (int) len ; + + count = paf24_write (psf, ppaf24, ptr, writecount) ; + + total += count ; + len -= count ; + if (count != writecount) + break ; + } ; + + return total ; +} /* paf24_write_i */ + +static sf_count_t +paf24_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len) +{ PAF24_PRIVATE *ppaf24 ; + int *iptr ; + int k, bufferlen, writecount = 0, count ; + sf_count_t total = 0 ; + float normfact ; + + if (psf->codec_data == NULL) + return 0 ; + ppaf24 = (PAF24_PRIVATE*) psf->codec_data ; + + normfact = (psf->norm_float == SF_TRUE) ? (1.0 * 0x7FFFFFFF) : (1.0 / 0x100) ; + + iptr = psf->u.ibuf ; + bufferlen = ARRAY_LEN (psf->u.ibuf) ; + while (len > 0) + { writecount = (len >= bufferlen) ? bufferlen : len ; + for (k = 0 ; k < writecount ; k++) + iptr [k] = lrintf (normfact * ptr [total + k]) ; + count = paf24_write (psf, ppaf24, iptr, writecount) ; + total += count ; + len -= writecount ; + if (count != writecount) + break ; + } ; + + return total ; +} /* paf24_write_f */ + +static sf_count_t +paf24_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len) +{ PAF24_PRIVATE *ppaf24 ; + int *iptr ; + int k, bufferlen, writecount = 0, count ; + sf_count_t total = 0 ; + double normfact ; + + if (psf->codec_data == NULL) + return 0 ; + ppaf24 = (PAF24_PRIVATE*) psf->codec_data ; + + normfact = (psf->norm_double == SF_TRUE) ? (1.0 * 0x7FFFFFFF) : (1.0 / 0x100) ; + + iptr = psf->u.ibuf ; + bufferlen = ARRAY_LEN (psf->u.ibuf) ; + while (len > 0) + { writecount = (len >= bufferlen) ? bufferlen : len ; + for (k = 0 ; k < writecount ; k++) + iptr [k] = lrint (normfact * ptr [total+k]) ; + count = paf24_write (psf, ppaf24, iptr, writecount) ; + total += count ; + len -= writecount ; + if (count != writecount) + break ; + } ; + + return total ; +} /* paf24_write_d */ + + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 477a5308-451e-4bbd-bab4-fab6caa4e884 +*/ diff --git a/src/pcm.c b/src/pcm.c new file mode 100644 index 00000000..e3dd5a5d --- /dev/null +++ b/src/pcm.c @@ -0,0 +1,2901 @@ +/* +** Copyright (C) 1999-2005 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include "sndfile.h" +#include "sfendian.h" +#include "float_cast.h" +#include "common.h" + +/* Need to be able to handle 3 byte (24 bit) integers. So defined a +** type and use SIZEOF_TRIBYTE instead of (tribyte). +*/ + +typedef void tribyte ; + +#define SIZEOF_TRIBYTE 3 + +static sf_count_t pcm_read_sc2s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ; +static sf_count_t pcm_read_uc2s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ; +static sf_count_t pcm_read_bes2s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ; +static sf_count_t pcm_read_les2s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ; +static sf_count_t pcm_read_bet2s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ; +static sf_count_t pcm_read_let2s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ; +static sf_count_t pcm_read_bei2s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ; +static sf_count_t pcm_read_lei2s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ; + +static sf_count_t pcm_read_sc2i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ; +static sf_count_t pcm_read_uc2i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ; +static sf_count_t pcm_read_bes2i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ; +static sf_count_t pcm_read_les2i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ; +static sf_count_t pcm_read_bet2i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ; +static sf_count_t pcm_read_let2i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ; +static sf_count_t pcm_read_bei2i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ; +static sf_count_t pcm_read_lei2i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ; + +static sf_count_t pcm_read_sc2f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ; +static sf_count_t pcm_read_uc2f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ; +static sf_count_t pcm_read_bes2f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ; +static sf_count_t pcm_read_les2f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ; +static sf_count_t pcm_read_bet2f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ; +static sf_count_t pcm_read_let2f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ; +static sf_count_t pcm_read_bei2f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ; +static sf_count_t pcm_read_lei2f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ; + +static sf_count_t pcm_read_sc2d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ; +static sf_count_t pcm_read_uc2d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ; +static sf_count_t pcm_read_bes2d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ; +static sf_count_t pcm_read_les2d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ; +static sf_count_t pcm_read_bet2d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ; +static sf_count_t pcm_read_let2d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ; +static sf_count_t pcm_read_bei2d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ; +static sf_count_t pcm_read_lei2d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ; + +static sf_count_t pcm_write_s2sc (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ; +static sf_count_t pcm_write_s2uc (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ; +static sf_count_t pcm_write_s2bes (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ; +static sf_count_t pcm_write_s2les (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ; +static sf_count_t pcm_write_s2bet (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ; +static sf_count_t pcm_write_s2let (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ; +static sf_count_t pcm_write_s2bei (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ; +static sf_count_t pcm_write_s2lei (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ; + +static sf_count_t pcm_write_i2sc (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ; +static sf_count_t pcm_write_i2uc (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ; +static sf_count_t pcm_write_i2bes (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ; +static sf_count_t pcm_write_i2les (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ; +static sf_count_t pcm_write_i2bet (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ; +static sf_count_t pcm_write_i2let (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ; +static sf_count_t pcm_write_i2bei (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ; +static sf_count_t pcm_write_i2lei (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ; + +static sf_count_t pcm_write_f2sc (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ; +static sf_count_t pcm_write_f2uc (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ; +static sf_count_t pcm_write_f2bes (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ; +static sf_count_t pcm_write_f2les (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ; +static sf_count_t pcm_write_f2bet (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ; +static sf_count_t pcm_write_f2let (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ; +static sf_count_t pcm_write_f2bei (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ; +static sf_count_t pcm_write_f2lei (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ; + +static sf_count_t pcm_write_d2sc (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ; +static sf_count_t pcm_write_d2uc (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ; +static sf_count_t pcm_write_d2bes (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ; +static sf_count_t pcm_write_d2les (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ; +static sf_count_t pcm_write_d2bet (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ; +static sf_count_t pcm_write_d2let (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ; +static sf_count_t pcm_write_d2bei (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ; +static sf_count_t pcm_write_d2lei (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ; + +/*----------------------------------------------------------------------------------------------- +*/ + +enum +{ /* Char type for 8 bit files. */ + SF_CHARS_SIGNED = 200, + SF_CHARS_UNSIGNED = 201 +} ; + +/*----------------------------------------------------------------------------------------------- +*/ + +int +pcm_init (SF_PRIVATE *psf) +{ int chars = 0 ; + + if (psf->bytewidth == 0 || psf->sf.channels == 0) + { psf_log_printf (psf, "pcm_init : internal error : bytewitdh = %d, channels = %d\n", psf->bytewidth, psf->sf.channels) ; + return SFE_INTERNAL ; + } ; + + psf->blockwidth = psf->bytewidth * psf->sf.channels ; + + if ((psf->sf.format & SF_FORMAT_SUBMASK) == SF_FORMAT_PCM_S8) + chars = SF_CHARS_SIGNED ; + else if ((psf->sf.format & SF_FORMAT_SUBMASK) == SF_FORMAT_PCM_U8) + chars = SF_CHARS_UNSIGNED ; + + if (psf->mode == SFM_READ || psf->mode == SFM_RDWR) + { switch (psf->bytewidth * 0x10000 + psf->endian + chars) + { case (0x10000 + SF_ENDIAN_BIG + SF_CHARS_SIGNED) : + case (0x10000 + SF_ENDIAN_LITTLE + SF_CHARS_SIGNED) : + psf->read_short = pcm_read_sc2s ; + psf->read_int = pcm_read_sc2i ; + psf->read_float = pcm_read_sc2f ; + psf->read_double = pcm_read_sc2d ; + break ; + case (0x10000 + SF_ENDIAN_BIG + SF_CHARS_UNSIGNED) : + case (0x10000 + SF_ENDIAN_LITTLE + SF_CHARS_UNSIGNED) : + psf->read_short = pcm_read_uc2s ; + psf->read_int = pcm_read_uc2i ; + psf->read_float = pcm_read_uc2f ; + psf->read_double = pcm_read_uc2d ; + break ; + + case (2 * 0x10000 + SF_ENDIAN_BIG) : + psf->read_short = pcm_read_bes2s ; + psf->read_int = pcm_read_bes2i ; + psf->read_float = pcm_read_bes2f ; + psf->read_double = pcm_read_bes2d ; + break ; + case (3 * 0x10000 + SF_ENDIAN_BIG) : + psf->read_short = pcm_read_bet2s ; + psf->read_int = pcm_read_bet2i ; + psf->read_float = pcm_read_bet2f ; + psf->read_double = pcm_read_bet2d ; + break ; + case (4 * 0x10000 + SF_ENDIAN_BIG) : + psf->read_short = pcm_read_bei2s ; + psf->read_int = pcm_read_bei2i ; + psf->read_float = pcm_read_bei2f ; + psf->read_double = pcm_read_bei2d ; + break ; + + case (2 * 0x10000 + SF_ENDIAN_LITTLE) : + psf->read_short = pcm_read_les2s ; + psf->read_int = pcm_read_les2i ; + psf->read_float = pcm_read_les2f ; + psf->read_double = pcm_read_les2d ; + break ; + case (3 * 0x10000 + SF_ENDIAN_LITTLE) : + psf->read_short = pcm_read_let2s ; + psf->read_int = pcm_read_let2i ; + psf->read_float = pcm_read_let2f ; + psf->read_double = pcm_read_let2d ; + break ; + case (4 * 0x10000 + SF_ENDIAN_LITTLE) : + psf->read_short = pcm_read_lei2s ; + psf->read_int = pcm_read_lei2i ; + psf->read_float = pcm_read_lei2f ; + psf->read_double = pcm_read_lei2d ; + break ; + default : + psf_log_printf (psf, "pcm.c returning SFE_UNIMPLEMENTED\n") ; + return SFE_UNIMPLEMENTED ; + } ; + } ; + + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + { switch (psf->bytewidth * 0x10000 + psf->endian + chars) + { case (0x10000 + SF_ENDIAN_BIG + SF_CHARS_SIGNED) : + case (0x10000 + SF_ENDIAN_LITTLE + SF_CHARS_SIGNED) : + psf->write_short = pcm_write_s2sc ; + psf->write_int = pcm_write_i2sc ; + psf->write_float = pcm_write_f2sc ; + psf->write_double = pcm_write_d2sc ; + break ; + case (0x10000 + SF_ENDIAN_BIG + SF_CHARS_UNSIGNED) : + case (0x10000 + SF_ENDIAN_LITTLE + SF_CHARS_UNSIGNED) : + psf->write_short = pcm_write_s2uc ; + psf->write_int = pcm_write_i2uc ; + psf->write_float = pcm_write_f2uc ; + psf->write_double = pcm_write_d2uc ; + break ; + + case (2 * 0x10000 + SF_ENDIAN_BIG) : + psf->write_short = pcm_write_s2bes ; + psf->write_int = pcm_write_i2bes ; + psf->write_float = pcm_write_f2bes ; + psf->write_double = pcm_write_d2bes ; + break ; + + case (3 * 0x10000 + SF_ENDIAN_BIG) : + psf->write_short = pcm_write_s2bet ; + psf->write_int = pcm_write_i2bet ; + psf->write_float = pcm_write_f2bet ; + psf->write_double = pcm_write_d2bet ; + break ; + + case (4 * 0x10000 + SF_ENDIAN_BIG) : + psf->write_short = pcm_write_s2bei ; + psf->write_int = pcm_write_i2bei ; + psf->write_float = pcm_write_f2bei ; + psf->write_double = pcm_write_d2bei ; + break ; + + case (2 * 0x10000 + SF_ENDIAN_LITTLE) : + psf->write_short = pcm_write_s2les ; + psf->write_int = pcm_write_i2les ; + psf->write_float = pcm_write_f2les ; + psf->write_double = pcm_write_d2les ; + break ; + + case (3 * 0x10000 + SF_ENDIAN_LITTLE) : + psf->write_short = pcm_write_s2let ; + psf->write_int = pcm_write_i2let ; + psf->write_float = pcm_write_f2let ; + psf->write_double = pcm_write_d2let ; + break ; + + case (4 * 0x10000 + SF_ENDIAN_LITTLE) : + psf->write_short = pcm_write_s2lei ; + psf->write_int = pcm_write_i2lei ; + psf->write_float = pcm_write_f2lei ; + psf->write_double = pcm_write_d2lei ; + break ; + + default : + psf_log_printf (psf, "pcm.c returning SFE_UNIMPLEMENTED\n") ; + return SFE_UNIMPLEMENTED ; + } ; + + } ; + + if (psf->filelength > psf->dataoffset) + { psf->datalength = (psf->dataend > 0) ? psf->dataend - psf->dataoffset : + psf->filelength - psf->dataoffset ; + } + else + psf->datalength = 0 ; + + psf->sf.frames = psf->datalength / psf->blockwidth ; + + return 0 ; +} /* pcm_init */ + +/*============================================================================== +*/ + +static inline void +sc2s_array (signed char *src, int count, short *dest) +{ while (--count >= 0) + { dest [count] = src [count] << 8 ; + } ; +} /* sc2s_array */ + +static inline void +uc2s_array (unsigned char *src, int count, short *dest) +{ while (--count >= 0) + { dest [count] = (((short) src [count]) - 0x80) << 8 ; + } ; +} /* uc2s_array */ + +static inline void +let2s_array (tribyte *src, int count, short *dest) +{ unsigned char *ucptr ; + + ucptr = ((unsigned char*) src) + 3 * count ; + while (--count >= 0) + { ucptr -= 3 ; + dest [count] = LET2H_SHORT_PTR (ucptr) ; + } ; +} /* let2s_array */ + +static inline void +bet2s_array (tribyte *src, int count, short *dest) +{ unsigned char *ucptr ; + + ucptr = ((unsigned char*) src) + 3 * count ; + while (--count >= 0) + { ucptr -= 3 ; + dest [count] = BET2H_SHORT_PTR (ucptr) ; + } ; +} /* bet2s_array */ + +static inline void +lei2s_array (int *src, int count, short *dest) +{ int value ; + + while (--count >= 0) + { value = LEI2H_INT (src [count]) ; + dest [count] = value >> 16 ; + } ; +} /* lei2s_array */ + +static inline void +bei2s_array (int *src, int count, short *dest) +{ int value ; + + while (--count >= 0) + { value = BEI2H_INT (src [count]) ; + dest [count] = value >> 16 ; + } ; +} /* bei2s_array */ + +/*-------------------------------------------------------------------------- +*/ + +static inline void +sc2i_array (signed char *src, int count, int *dest) +{ while (--count >= 0) + { dest [count] = ((int) src [count]) << 24 ; + } ; +} /* sc2i_array */ + +static inline void +uc2i_array (unsigned char *src, int count, int *dest) +{ while (--count >= 0) + { dest [count] = (((int) src [count]) - 128) << 24 ; + } ; +} /* uc2i_array */ + +static inline void +bes2i_array (short *src, int count, int *dest) +{ short value ; + + while (--count >= 0) + { value = BES2H_SHORT (src [count]) ; + dest [count] = value << 16 ; + } ; +} /* bes2i_array */ + +static inline void +les2i_array (short *src, int count, int *dest) +{ short value ; + + while (--count >= 0) + { value = LES2H_SHORT (src [count]) ; + dest [count] = value << 16 ; + } ; +} /* les2i_array */ + +static inline void +bet2i_array (tribyte *src, int count, int *dest) +{ unsigned char *ucptr ; + + ucptr = ((unsigned char*) src) + 3 * count ; + while (--count >= 0) + { ucptr -= 3 ; + dest [count] = BET2H_INT_PTR (ucptr) ; + } ; +} /* bet2i_array */ + +static inline void +let2i_array (tribyte *src, int count, int *dest) +{ unsigned char *ucptr ; + + ucptr = ((unsigned char*) src) + 3 * count ; + while (--count >= 0) + { ucptr -= 3 ; + dest [count] = LET2H_INT_PTR (ucptr) ; + } ; +} /* let2i_array */ + +/*-------------------------------------------------------------------------- +*/ + +static inline void +sc2f_array (signed char *src, int count, float *dest, float normfact) +{ while (--count >= 0) + dest [count] = ((float) src [count]) * normfact ; +} /* sc2f_array */ + +static inline void +uc2f_array (unsigned char *src, int count, float *dest, float normfact) +{ while (--count >= 0) + dest [count] = (((int) src [count]) - 128) * normfact ; +} /* uc2f_array */ + +static inline void +les2f_array (short *src, int count, float *dest, float normfact) +{ short value ; + + while (--count >= 0) + { value = src [count] ; + value = LES2H_SHORT (value) ; + dest [count] = ((float) value) * normfact ; + } ; +} /* les2f_array */ + +static inline void +bes2f_array (short *src, int count, float *dest, float normfact) +{ short value ; + + while (--count >= 0) + { value = src [count] ; + value = BES2H_SHORT (value) ; + dest [count] = ((float) value) * normfact ; + } ; +} /* bes2f_array */ + +static inline void +let2f_array (tribyte *src, int count, float *dest, float normfact) +{ unsigned char *ucptr ; + int value ; + + ucptr = ((unsigned char*) src) + 3 * count ; + while (--count >= 0) + { ucptr -= 3 ; + value = LET2H_INT_PTR (ucptr) ; + dest [count] = ((float) value) * normfact ; + } ; +} /* let2f_array */ + +static inline void +bet2f_array (tribyte *src, int count, float *dest, float normfact) +{ unsigned char *ucptr ; + int value ; + + ucptr = ((unsigned char*) src) + 3 * count ; + while (--count >= 0) + { ucptr -= 3 ; + value = BET2H_INT_PTR (ucptr) ; + dest [count] = ((float) value) * normfact ; + } ; +} /* bet2f_array */ + +static inline void +lei2f_array (int *src, int count, float *dest, float normfact) +{ int value ; + + while (--count >= 0) + { value = src [count] ; + value = LEI2H_INT (value) ; + dest [count] = ((float) value) * normfact ; + } ; +} /* lei2f_array */ + +static inline void +bei2f_array (int *src, int count, float *dest, float normfact) +{ int value ; + + while (--count >= 0) + { value = src [count] ; + value = BEI2H_INT (value) ; + dest [count] = ((float) value) * normfact ; + } ; +} /* bei2f_array */ + +/*-------------------------------------------------------------------------- +*/ + +static inline void +sc2d_array (signed char *src, int count, double *dest, double normfact) +{ while (--count >= 0) + dest [count] = ((double) src [count]) * normfact ; +} /* sc2d_array */ + +static inline void +uc2d_array (unsigned char *src, int count, double *dest, double normfact) +{ while (--count >= 0) + dest [count] = (((int) src [count]) - 128) * normfact ; +} /* uc2d_array */ + +static inline void +les2d_array (short *src, int count, double *dest, double normfact) +{ short value ; + + while (--count >= 0) + { value = src [count] ; + value = LES2H_SHORT (value) ; + dest [count] = ((double) value) * normfact ; + } ; +} /* les2d_array */ + +static inline void +bes2d_array (short *src, int count, double *dest, double normfact) +{ short value ; + + while (--count >= 0) + { value = src [count] ; + value = BES2H_SHORT (value) ; + dest [count] = ((double) value) * normfact ; + } ; +} /* bes2d_array */ + +static inline void +let2d_array (tribyte *src, int count, double *dest, double normfact) +{ unsigned char *ucptr ; + int value ; + + ucptr = ((unsigned char*) src) + 3 * count ; + while (--count >= 0) + { ucptr -= 3 ; + value = LET2H_INT_PTR (ucptr) ; + dest [count] = ((double) value) * normfact ; + } ; +} /* let2d_array */ + +static inline void +bet2d_array (tribyte *src, int count, double *dest, double normfact) +{ unsigned char *ucptr ; + int value ; + + ucptr = ((unsigned char*) src) + 3 * count ; + while (--count >= 0) + { ucptr -= 3 ; + value = (ucptr [0] << 24) | (ucptr [1] << 16) | (ucptr [2] << 8) ; + dest [count] = ((double) value) * normfact ; + } ; +} /* bet2d_array */ + +static inline void +lei2d_array (int *src, int count, double *dest, double normfact) +{ int value ; + + while (--count >= 0) + { value = src [count] ; + value = LEI2H_INT (value) ; + dest [count] = ((double) value) * normfact ; + } ; +} /* lei2d_array */ + +static inline void +bei2d_array (int *src, int count, double *dest, double normfact) +{ int value ; + + while (--count >= 0) + { value = src [count] ; + value = BEI2H_INT (value) ; + dest [count] = ((double) value) * normfact ; + } ; +} /* bei2d_array */ + +/*-------------------------------------------------------------------------- +*/ + +static inline void +s2sc_array (const short *src, signed char *dest, int count) +{ while (--count >= 0) + dest [count] = src [count] >> 8 ; +} /* s2sc_array */ + +static inline void +s2uc_array (const short *src, unsigned char *dest, int count) +{ while (--count >= 0) + dest [count] = (src [count] >> 8) + 0x80 ; +} /* s2uc_array */ + +static inline void +s2let_array (const short *src, tribyte *dest, int count) +{ unsigned char *ucptr ; + + ucptr = ((unsigned char*) dest) + 3 * count ; + while (--count >= 0) + { ucptr -= 3 ; + ucptr [0] = 0 ; + ucptr [1] = src [count] ; + ucptr [2] = src [count] >> 8 ; + } ; +} /* s2let_array */ + +static inline void +s2bet_array (const short *src, tribyte *dest, int count) +{ unsigned char *ucptr ; + + ucptr = ((unsigned char*) dest) + 3 * count ; + while (--count >= 0) + { ucptr -= 3 ; + ucptr [2] = 0 ; + ucptr [1] = src [count] ; + ucptr [0] = src [count] >> 8 ; + } ; +} /* s2bet_array */ + +static inline void +s2lei_array (const short *src, int *dest, int count) +{ unsigned char *ucptr ; + + ucptr = ((unsigned char*) dest) + 4 * count ; + while (--count >= 0) + { ucptr -= 4 ; + ucptr [0] = 0 ; + ucptr [1] = 0 ; + ucptr [2] = src [count] ; + ucptr [3] = src [count] >> 8 ; + } ; +} /* s2lei_array */ + +static inline void +s2bei_array (const short *src, int *dest, int count) +{ unsigned char *ucptr ; + + ucptr = ((unsigned char*) dest) + 4 * count ; + while (--count >= 0) + { ucptr -= 4 ; + ucptr [0] = src [count] >> 8 ; + ucptr [1] = src [count] ; + ucptr [2] = 0 ; + ucptr [3] = 0 ; + } ; +} /* s2bei_array */ + +/*-------------------------------------------------------------------------- +*/ + +static inline void +i2sc_array (const int *src, signed char *dest, int count) +{ while (--count >= 0) + dest [count] = (src [count] >> 24) ; +} /* i2sc_array */ + +static inline void +i2uc_array (const int *src, unsigned char *dest, int count) +{ while (--count >= 0) + dest [count] = ((src [count] >> 24) + 128) ; +} /* i2uc_array */ + +static inline void +i2bes_array (const int *src, short *dest, int count) +{ unsigned char *ucptr ; + + ucptr = ((unsigned char*) dest) + 2 * count ; + while (--count >= 0) + { ucptr -= 2 ; + ucptr [0] = src [count] >> 24 ; + ucptr [1] = src [count] >> 16 ; + } ; +} /* i2bes_array */ + +static inline void +i2les_array (const int *src, short *dest, int count) +{ unsigned char *ucptr ; + + ucptr = ((unsigned char*) dest) + 2 * count ; + while (--count >= 0) + { ucptr -= 2 ; + ucptr [0] = src [count] >> 16 ; + ucptr [1] = src [count] >> 24 ; + } ; +} /* i2les_array */ + +static inline void +i2let_array (const int *src, tribyte *dest, int count) +{ unsigned char *ucptr ; + int value ; + + ucptr = ((unsigned char*) dest) + 3 * count ; + while (--count >= 0) + { ucptr -= 3 ; + value = src [count] >> 8 ; + ucptr [0] = value ; + ucptr [1] = value >> 8 ; + ucptr [2] = value >> 16 ; + } ; +} /* i2let_array */ + +static inline void +i2bet_array (const int *src, tribyte *dest, int count) +{ unsigned char *ucptr ; + int value ; + + ucptr = ((unsigned char*) dest) + 3 * count ; + while (--count >= 0) + { ucptr -= 3 ; + value = src [count] >> 8 ; + ucptr [2] = value ; + ucptr [1] = value >> 8 ; + ucptr [0] = value >> 16 ; + } ; +} /* i2bet_array */ + +/*=============================================================================================== +*/ + +static sf_count_t +pcm_read_sc2s (SF_PRIVATE *psf, short *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + + bufferlen = ARRAY_LEN (psf->u.scbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.scbuf, sizeof (signed char), bufferlen, psf) ; + sc2s_array (psf->u.scbuf, readcount, ptr + total) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* pcm_read_sc2s */ + +static sf_count_t +pcm_read_uc2s (SF_PRIVATE *psf, short *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + + bufferlen = ARRAY_LEN (psf->u.ucbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.ucbuf, sizeof (unsigned char), bufferlen, psf) ; + uc2s_array (psf->u.ucbuf, readcount, ptr + total) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* pcm_read_uc2s */ + +static sf_count_t +pcm_read_bes2s (SF_PRIVATE *psf, short *ptr, sf_count_t len) +{ int total ; + + total = psf_fread (ptr, sizeof (short), len, psf) ; + if (CPU_IS_LITTLE_ENDIAN) + endswap_short_array (ptr, len) ; + + return total ; +} /* pcm_read_bes2s */ + +static sf_count_t +pcm_read_les2s (SF_PRIVATE *psf, short *ptr, sf_count_t len) +{ int total ; + + total = psf_fread (ptr, sizeof (short), len, psf) ; + if (CPU_IS_BIG_ENDIAN) + endswap_short_array (ptr, len) ; + + return total ; +} /* pcm_read_les2s */ + +static sf_count_t +pcm_read_bet2s (SF_PRIVATE *psf, short *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + + bufferlen = sizeof (psf->u.ucbuf) / SIZEOF_TRIBYTE ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.ucbuf, SIZEOF_TRIBYTE, bufferlen, psf) ; + bet2s_array ((tribyte*) (psf->u.ucbuf), readcount, ptr + total) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* pcm_read_bet2s */ + +static sf_count_t +pcm_read_let2s (SF_PRIVATE *psf, short *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + + bufferlen = sizeof (psf->u.ucbuf) / SIZEOF_TRIBYTE ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.ucbuf, SIZEOF_TRIBYTE, bufferlen, psf) ; + let2s_array ((tribyte*) (psf->u.ucbuf), readcount, ptr + total) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* pcm_read_let2s */ + +static sf_count_t +pcm_read_bei2s (SF_PRIVATE *psf, short *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + + bufferlen = ARRAY_LEN (psf->u.ibuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.ibuf, sizeof (int), bufferlen, psf) ; + bei2s_array (psf->u.ibuf, readcount, ptr + total) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* pcm_read_bei2s */ + +static sf_count_t +pcm_read_lei2s (SF_PRIVATE *psf, short *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + + bufferlen = ARRAY_LEN (psf->u.ibuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.ibuf, sizeof (int), bufferlen, psf) ; + lei2s_array (psf->u.ibuf, readcount, ptr + total) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* pcm_read_lei2s */ + +/*----------------------------------------------------------------------------------------------- +*/ + +static sf_count_t +pcm_read_sc2i (SF_PRIVATE *psf, int *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + + bufferlen = ARRAY_LEN (psf->u.scbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.scbuf, sizeof (signed char), bufferlen, psf) ; + sc2i_array (psf->u.scbuf, readcount, ptr + total) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* pcm_read_sc2i */ + +static sf_count_t +pcm_read_uc2i (SF_PRIVATE *psf, int *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + + bufferlen = ARRAY_LEN (psf->u.ucbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.ucbuf, sizeof (unsigned char), bufferlen, psf) ; + uc2i_array (psf->u.ucbuf, readcount, ptr + total) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* pcm_read_uc2i */ + +static sf_count_t +pcm_read_bes2i (SF_PRIVATE *psf, int *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.sbuf, sizeof (short), bufferlen, psf) ; + bes2i_array (psf->u.sbuf, readcount, ptr + total) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* pcm_read_bes2i */ + +static sf_count_t +pcm_read_les2i (SF_PRIVATE *psf, int *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.sbuf, sizeof (short), bufferlen, psf) ; + les2i_array (psf->u.sbuf, readcount, ptr + total) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* pcm_read_les2i */ + +static sf_count_t +pcm_read_bet2i (SF_PRIVATE *psf, int *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + + bufferlen = sizeof (psf->u.ucbuf) / SIZEOF_TRIBYTE ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.ucbuf, SIZEOF_TRIBYTE, bufferlen, psf) ; + bet2i_array ((tribyte*) (psf->u.ucbuf), readcount, ptr + total) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* pcm_read_bet2i */ + +static sf_count_t +pcm_read_let2i (SF_PRIVATE *psf, int *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + + bufferlen = sizeof (psf->u.ucbuf) / SIZEOF_TRIBYTE ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.ucbuf, SIZEOF_TRIBYTE, bufferlen, psf) ; + let2i_array ((tribyte*) (psf->u.ucbuf), readcount, ptr + total) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* pcm_read_let2i */ + +static sf_count_t +pcm_read_bei2i (SF_PRIVATE *psf, int *ptr, sf_count_t len) +{ int total ; + + total = psf_fread (ptr, sizeof (int), len, psf) ; + if (CPU_IS_LITTLE_ENDIAN) + endswap_int_array (ptr, len) ; + + return total ; +} /* pcm_read_bei2i */ + +static sf_count_t +pcm_read_lei2i (SF_PRIVATE *psf, int *ptr, sf_count_t len) +{ int total ; + + total = psf_fread (ptr, sizeof (int), len, psf) ; + if (CPU_IS_BIG_ENDIAN) + endswap_int_array (ptr, len) ; + + return total ; +} /* pcm_read_lei2i */ + +/*----------------------------------------------------------------------------------------------- +*/ + +static sf_count_t +pcm_read_sc2f (SF_PRIVATE *psf, float *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + float normfact ; + + normfact = (psf->norm_float == SF_TRUE) ? 1.0 / ((float) 0x80) : 1.0 ; + + bufferlen = ARRAY_LEN (psf->u.scbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.scbuf, sizeof (signed char), bufferlen, psf) ; + sc2f_array (psf->u.scbuf, readcount, ptr + total, normfact) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* pcm_read_sc2f */ + +static sf_count_t +pcm_read_uc2f (SF_PRIVATE *psf, float *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + float normfact ; + + normfact = (psf->norm_float == SF_TRUE) ? 1.0 / ((float) 0x80) : 1.0 ; + + bufferlen = ARRAY_LEN (psf->u.ucbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.ucbuf, sizeof (unsigned char), bufferlen, psf) ; + uc2f_array (psf->u.ucbuf, readcount, ptr + total, normfact) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* pcm_read_uc2f */ + +static sf_count_t +pcm_read_bes2f (SF_PRIVATE *psf, float *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + float normfact ; + + normfact = (psf->norm_float == SF_TRUE) ? 1.0 / ((float) 0x8000) : 1.0 ; + + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.sbuf, sizeof (short), bufferlen, psf) ; + bes2f_array (psf->u.sbuf, readcount, ptr + total, normfact) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* pcm_read_bes2f */ + +static sf_count_t +pcm_read_les2f (SF_PRIVATE *psf, float *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + float normfact ; + + normfact = (psf->norm_float == SF_TRUE) ? 1.0 / ((float) 0x8000) : 1.0 ; + + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.sbuf, sizeof (short), bufferlen, psf) ; + les2f_array (psf->u.sbuf, readcount, ptr + total, normfact) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* pcm_read_les2f */ + +static sf_count_t +pcm_read_bet2f (SF_PRIVATE *psf, float *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + float normfact ; + + /* Special normfactor because tribyte value is read into an int. */ + normfact = (psf->norm_float == SF_TRUE) ? 1.0 / ((float) 0x80000000) : 1.0 / 256.0 ; + + bufferlen = sizeof (psf->u.ucbuf) / SIZEOF_TRIBYTE ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.ucbuf, SIZEOF_TRIBYTE, bufferlen, psf) ; + bet2f_array ((tribyte*) (psf->u.ucbuf), readcount, ptr + total, normfact) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* pcm_read_bet2f */ + +static sf_count_t +pcm_read_let2f (SF_PRIVATE *psf, float *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + float normfact ; + + /* Special normfactor because tribyte value is read into an int. */ + normfact = (psf->norm_float == SF_TRUE) ? 1.0 / ((float) 0x80000000) : 1.0 / 256.0 ; + + bufferlen = sizeof (psf->u.ucbuf) / SIZEOF_TRIBYTE ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.ucbuf, SIZEOF_TRIBYTE, bufferlen, psf) ; + let2f_array ((tribyte*) (psf->u.ucbuf), readcount, ptr + total, normfact) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* pcm_read_let2f */ + +static sf_count_t +pcm_read_bei2f (SF_PRIVATE *psf, float *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + float normfact ; + + normfact = (psf->norm_float == SF_TRUE) ? 1.0 / ((float) 0x80000000) : 1.0 ; + + bufferlen = ARRAY_LEN (psf->u.ibuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.ibuf, sizeof (int), bufferlen, psf) ; + bei2f_array (psf->u.ibuf, readcount, ptr + total, normfact) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* pcm_read_bei2f */ + +static sf_count_t +pcm_read_lei2f (SF_PRIVATE *psf, float *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + float normfact ; + + normfact = (psf->norm_float == SF_TRUE) ? 1.0 / ((float) 0x80000000) : 1.0 ; + + bufferlen = ARRAY_LEN (psf->u.ibuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.ibuf, sizeof (int), bufferlen, psf) ; + lei2f_array (psf->u.ibuf, readcount, ptr + total, normfact) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* pcm_read_lei2f */ + +/*----------------------------------------------------------------------------------------------- +*/ + +static sf_count_t +pcm_read_sc2d (SF_PRIVATE *psf, double *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + double normfact ; + + normfact = (psf->norm_double == SF_TRUE) ? 1.0 / ((double) 0x80) : 1.0 ; + + bufferlen = ARRAY_LEN (psf->u.scbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.scbuf, sizeof (signed char), bufferlen, psf) ; + sc2d_array (psf->u.scbuf, readcount, ptr + total, normfact) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* pcm_read_sc2d */ + +static sf_count_t +pcm_read_uc2d (SF_PRIVATE *psf, double *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + double normfact ; + + normfact = (psf->norm_double == SF_TRUE) ? 1.0 / ((double) 0x80) : 1.0 ; + + bufferlen = ARRAY_LEN (psf->u.ucbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.ucbuf, sizeof (unsigned char), bufferlen, psf) ; + uc2d_array (psf->u.ucbuf, readcount, ptr + total, normfact) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* pcm_read_uc2d */ + +static sf_count_t +pcm_read_bes2d (SF_PRIVATE *psf, double *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + double normfact ; + + normfact = (psf->norm_double == SF_TRUE) ? 1.0 / ((double) 0x8000) : 1.0 ; + + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.sbuf, sizeof (short), bufferlen, psf) ; + bes2d_array (psf->u.sbuf, readcount, ptr + total, normfact) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* pcm_read_bes2d */ + +static sf_count_t +pcm_read_les2d (SF_PRIVATE *psf, double *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + double normfact ; + + normfact = (psf->norm_double == SF_TRUE) ? 1.0 / ((double) 0x8000) : 1.0 ; + + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.sbuf, sizeof (short), bufferlen, psf) ; + les2d_array (psf->u.sbuf, readcount, ptr + total, normfact) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* pcm_read_les2d */ + +static sf_count_t +pcm_read_bet2d (SF_PRIVATE *psf, double *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + double normfact ; + + normfact = (psf->norm_double == SF_TRUE) ? 1.0 / ((double) 0x80000000) : 1.0 / 256.0 ; + + bufferlen = sizeof (psf->u.ucbuf) / SIZEOF_TRIBYTE ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.ucbuf, SIZEOF_TRIBYTE, bufferlen, psf) ; + bet2d_array ((tribyte*) (psf->u.ucbuf), readcount, ptr + total, normfact) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* pcm_read_bet2d */ + +static sf_count_t +pcm_read_let2d (SF_PRIVATE *psf, double *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + double normfact ; + + /* Special normfactor because tribyte value is read into an int. */ + normfact = (psf->norm_double == SF_TRUE) ? 1.0 / ((double) 0x80000000) : 1.0 / 256.0 ; + + bufferlen = sizeof (psf->u.ucbuf) / SIZEOF_TRIBYTE ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.ucbuf, SIZEOF_TRIBYTE, bufferlen, psf) ; + let2d_array ((tribyte*) (psf->u.ucbuf), readcount, ptr + total, normfact) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* pcm_read_let2d */ + +static sf_count_t +pcm_read_bei2d (SF_PRIVATE *psf, double *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + double normfact ; + + normfact = (psf->norm_double == SF_TRUE) ? 1.0 / ((double) 0x80000000) : 1.0 ; + + bufferlen = ARRAY_LEN (psf->u.ibuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.ibuf, sizeof (int), bufferlen, psf) ; + bei2d_array (psf->u.ibuf, readcount, ptr + total, normfact) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* pcm_read_bei2d */ + +static sf_count_t +pcm_read_lei2d (SF_PRIVATE *psf, double *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + double normfact ; + + normfact = (psf->norm_double == SF_TRUE) ? 1.0 / ((double) 0x80000000) : 1.0 ; + + bufferlen = ARRAY_LEN (psf->u.ibuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.ibuf, sizeof (int), bufferlen, psf) ; + lei2d_array (psf->u.ibuf, readcount, ptr + total, normfact) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* pcm_read_lei2d */ + +/*=============================================================================================== +**----------------------------------------------------------------------------------------------- +**=============================================================================================== +*/ + +static sf_count_t +pcm_write_s2sc (SF_PRIVATE *psf, const short *ptr, sf_count_t len) +{ int bufferlen, writecount ; + sf_count_t total = 0 ; + + bufferlen = ARRAY_LEN (psf->u.scbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + s2sc_array (ptr + total, psf->u.scbuf, bufferlen) ; + writecount = psf_fwrite (psf->u.scbuf, sizeof (signed char), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* pcm_write_s2sc */ + +static sf_count_t +pcm_write_s2uc (SF_PRIVATE *psf, const short *ptr, sf_count_t len) +{ int bufferlen, writecount ; + sf_count_t total = 0 ; + + bufferlen = ARRAY_LEN (psf->u.ucbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + s2uc_array (ptr + total, psf->u.ucbuf, bufferlen) ; + writecount = psf_fwrite (psf->u.ucbuf, sizeof (unsigned char), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* pcm_write_s2uc */ + +static sf_count_t +pcm_write_s2bes (SF_PRIVATE *psf, const short *ptr, sf_count_t len) +{ int bufferlen, writecount ; + sf_count_t total = 0 ; + + if (CPU_IS_BIG_ENDIAN) + return psf_fwrite (ptr, sizeof (short), len, psf) ; + else + + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + endswap_short_copy (psf->u.sbuf, ptr + total, bufferlen) ; + writecount = psf_fwrite (psf->u.sbuf, sizeof (short), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* pcm_write_s2bes */ + +static sf_count_t +pcm_write_s2les (SF_PRIVATE *psf, const short *ptr, sf_count_t len) +{ int bufferlen, writecount ; + sf_count_t total = 0 ; + + if (CPU_IS_LITTLE_ENDIAN) + return psf_fwrite (ptr, sizeof (short), len, psf) ; + + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + endswap_short_copy (psf->u.sbuf, ptr + total, bufferlen) ; + writecount = psf_fwrite (psf->u.sbuf, sizeof (short), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* pcm_write_s2les */ + +static sf_count_t +pcm_write_s2bet (SF_PRIVATE *psf, const short *ptr, sf_count_t len) +{ int bufferlen, writecount ; + sf_count_t total = 0 ; + + bufferlen = sizeof (psf->u.ucbuf) / SIZEOF_TRIBYTE ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + s2bet_array (ptr + total, (tribyte*) (psf->u.ucbuf), bufferlen) ; + writecount = psf_fwrite (psf->u.ucbuf, SIZEOF_TRIBYTE, bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* pcm_write_s2bet */ + +static sf_count_t +pcm_write_s2let (SF_PRIVATE *psf, const short *ptr, sf_count_t len) +{ int bufferlen, writecount ; + sf_count_t total = 0 ; + + bufferlen = sizeof (psf->u.ucbuf) / SIZEOF_TRIBYTE ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + s2let_array (ptr + total, (tribyte*) (psf->u.ucbuf), bufferlen) ; + writecount = psf_fwrite (psf->u.ucbuf, SIZEOF_TRIBYTE, bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* pcm_write_s2let */ + +static sf_count_t +pcm_write_s2bei (SF_PRIVATE *psf, const short *ptr, sf_count_t len) +{ int bufferlen, writecount ; + sf_count_t total = 0 ; + + bufferlen = ARRAY_LEN (psf->u.ibuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + s2bei_array (ptr + total, psf->u.ibuf, bufferlen) ; + writecount = psf_fwrite (psf->u.ibuf, sizeof (int), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* pcm_write_s2bei */ + +static sf_count_t +pcm_write_s2lei (SF_PRIVATE *psf, const short *ptr, sf_count_t len) +{ int bufferlen, writecount ; + sf_count_t total = 0 ; + + bufferlen = ARRAY_LEN (psf->u.ibuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + s2lei_array (ptr + total, psf->u.ibuf, bufferlen) ; + writecount = psf_fwrite (psf->u.ibuf, sizeof (int), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* pcm_write_s2lei */ + +/*----------------------------------------------------------------------------------------------- +*/ + +static sf_count_t +pcm_write_i2sc (SF_PRIVATE *psf, const int *ptr, sf_count_t len) +{ int bufferlen, writecount ; + sf_count_t total = 0 ; + + bufferlen = ARRAY_LEN (psf->u.scbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + i2sc_array (ptr + total, psf->u.scbuf, bufferlen) ; + writecount = psf_fwrite (psf->u.scbuf, sizeof (signed char), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* pcm_write_i2sc */ + +static sf_count_t +pcm_write_i2uc (SF_PRIVATE *psf, const int *ptr, sf_count_t len) +{ int bufferlen, writecount ; + sf_count_t total = 0 ; + + bufferlen = ARRAY_LEN (psf->u.ucbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + i2uc_array (ptr + total, psf->u.ucbuf, bufferlen) ; + writecount = psf_fwrite (psf->u.ucbuf, sizeof (signed char), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* pcm_write_i2uc */ + +static sf_count_t +pcm_write_i2bes (SF_PRIVATE *psf, const int *ptr, sf_count_t len) +{ int bufferlen, writecount ; + sf_count_t total = 0 ; + + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + i2bes_array (ptr + total, psf->u.sbuf, bufferlen) ; + writecount = psf_fwrite (psf->u.sbuf, sizeof (short), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* pcm_write_i2bes */ + +static sf_count_t +pcm_write_i2les (SF_PRIVATE *psf, const int *ptr, sf_count_t len) +{ int bufferlen, writecount ; + sf_count_t total = 0 ; + + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + i2les_array (ptr + total, psf->u.sbuf, bufferlen) ; + writecount = psf_fwrite (psf->u.sbuf, sizeof (short), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* pcm_write_i2les */ + +static sf_count_t +pcm_write_i2bet (SF_PRIVATE *psf, const int *ptr, sf_count_t len) +{ int bufferlen, writecount ; + sf_count_t total = 0 ; + + bufferlen = sizeof (psf->u.ucbuf) / SIZEOF_TRIBYTE ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + i2bet_array (ptr + total, (tribyte*) (psf->u.ucbuf), bufferlen) ; + writecount = psf_fwrite (psf->u.ucbuf, SIZEOF_TRIBYTE, bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* pcm_write_i2bet */ + +static sf_count_t +pcm_write_i2let (SF_PRIVATE *psf, const int *ptr, sf_count_t len) +{ int bufferlen, writecount ; + sf_count_t total = 0 ; + + bufferlen = sizeof (psf->u.ucbuf) / SIZEOF_TRIBYTE ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + i2let_array (ptr + total, (tribyte*) (psf->u.ucbuf), bufferlen) ; + writecount = psf_fwrite (psf->u.ucbuf, SIZEOF_TRIBYTE, bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* pcm_write_i2les */ + +static sf_count_t +pcm_write_i2bei (SF_PRIVATE *psf, const int *ptr, sf_count_t len) +{ int bufferlen, writecount ; + sf_count_t total = 0 ; + + if (CPU_IS_BIG_ENDIAN) + return psf_fwrite (ptr, sizeof (int), len, psf) ; + + bufferlen = ARRAY_LEN (psf->u.ibuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + endswap_int_copy (psf->u.ibuf, ptr + total, bufferlen) ; + writecount = psf_fwrite (psf->u.ibuf, sizeof (int), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* pcm_write_i2bei */ + +static sf_count_t +pcm_write_i2lei (SF_PRIVATE *psf, const int *ptr, sf_count_t len) +{ int bufferlen, writecount ; + sf_count_t total = 0 ; + + if (CPU_IS_LITTLE_ENDIAN) + return psf_fwrite (ptr, sizeof (int), len, psf) ; + + bufferlen = ARRAY_LEN (psf->u.ibuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + endswap_int_copy (psf->u.ibuf, ptr + total, bufferlen) ; + writecount = psf_fwrite (psf->u.ibuf, sizeof (int), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* pcm_write_i2lei */ + +/*------------------------------------------------------------------------------ +**============================================================================== +**------------------------------------------------------------------------------ +*/ + +static void +f2sc_array (const float *src, signed char *dest, int count, int normalize) +{ float normfact ; + + normfact = normalize ? (1.0 * 0x7F) : 1.0 ; + + while (--count >= 0) + { dest [count] = lrintf (src [count] * normfact) ; + } ; +} /* f2sc_array */ + +static void +f2sc_clip_array (const float *src, signed char *dest, int count, int normalize) +{ float normfact, scaled_value ; + + normfact = normalize ? (8.0 * 0x10000000) : (1.0 * 0x1000000) ; + + while (--count >= 0) + { scaled_value = src [count] * normfact ; + if (CPU_CLIPS_POSITIVE == 0 && scaled_value >= (1.0 * 0x7FFFFFFF)) + { dest [count] = 127 ; + continue ; + } ; + if (CPU_CLIPS_NEGATIVE == 0 && scaled_value <= (-8.0 * 0x10000000)) + { dest [count] = -128 ; + continue ; + } ; + + dest [count] = lrintf (scaled_value) >> 24 ; + } ; +} /* f2sc_clip_array */ + +static sf_count_t +pcm_write_f2sc (SF_PRIVATE *psf, const float *ptr, sf_count_t len) +{ void (*convert) (const float *, signed char *, int, int) ; + int bufferlen, writecount ; + sf_count_t total = 0 ; + + convert = (psf->add_clipping) ? f2sc_clip_array : f2sc_array ; + bufferlen = ARRAY_LEN (psf->u.scbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + convert (ptr + total, psf->u.scbuf, bufferlen, psf->norm_float) ; + writecount = psf_fwrite (psf->u.scbuf, sizeof (signed char), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* pcm_write_f2sc */ + +/*============================================================================== +*/ + +static void +f2uc_array (const float *src, unsigned char *dest, int count, int normalize) +{ float normfact ; + + normfact = normalize ? (1.0 * 0x7F) : 1.0 ; + + while (--count >= 0) + { dest [count] = lrintf (src [count] * normfact) + 128 ; + } ; +} /* f2uc_array */ + +static void +f2uc_clip_array (const float *src, unsigned char *dest, int count, int normalize) +{ float normfact, scaled_value ; + + normfact = normalize ? (8.0 * 0x10000000) : (1.0 * 0x1000000) ; + + while (--count >= 0) + { scaled_value = src [count] * normfact ; + if (CPU_CLIPS_POSITIVE == 0 && scaled_value >= (1.0 * 0x7FFFFFFF)) + { dest [count] = 0xFF ; + continue ; + } ; + if (CPU_CLIPS_NEGATIVE == 0 && scaled_value <= (-8.0 * 0x10000000)) + { dest [count] = 0 ; + continue ; + } ; + + dest [count] = (lrintf (scaled_value) >> 24) + 128 ; + } ; +} /* f2uc_clip_array */ + +static sf_count_t +pcm_write_f2uc (SF_PRIVATE *psf, const float *ptr, sf_count_t len) +{ void (*convert) (const float *, unsigned char *, int, int) ; + int bufferlen, writecount ; + sf_count_t total = 0 ; + + convert = (psf->add_clipping) ? f2uc_clip_array : f2uc_array ; + bufferlen = ARRAY_LEN (psf->u.ucbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + convert (ptr + total, psf->u.ucbuf, bufferlen, psf->norm_float) ; + writecount = psf_fwrite (psf->u.ucbuf, sizeof (unsigned char), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* pcm_write_f2uc */ + +/*============================================================================== +*/ + +static void +f2bes_array (const float *src, short *dest, int count, int normalize) +{ unsigned char *ucptr ; + float normfact ; + short value ; + + normfact = normalize ? (1.0 * 0x7FFF) : 1.0 ; + ucptr = ((unsigned char*) dest) + 2 * count ; + + while (--count >= 0) + { ucptr -= 2 ; + value = lrintf (src [count] * normfact) ; + ucptr [1] = value ; + ucptr [0] = value >> 8 ; + } ; +} /* f2bes_array */ + +static void +f2bes_clip_array (const float *src, short *dest, int count, int normalize) +{ unsigned char *ucptr ; + float normfact, scaled_value ; + int value ; + + normfact = normalize ? (8.0 * 0x10000000) : (1.0 * 0x10000) ; + ucptr = ((unsigned char*) dest) + 2 * count ; + + while (--count >= 0) + { ucptr -= 2 ; + scaled_value = src [count] * normfact ; + if (CPU_CLIPS_POSITIVE == 0 && scaled_value >= (1.0 * 0x7FFFFFFF)) + { ucptr [1] = 0xFF ; + ucptr [0] = 0x7F ; + continue ; + } ; + if (CPU_CLIPS_NEGATIVE == 0 && scaled_value <= (-8.0 * 0x10000000)) + { ucptr [1] = 0x00 ; + ucptr [0] = 0x80 ; + continue ; + } ; + + value = lrintf (scaled_value) ; + ucptr [1] = value >> 16 ; + ucptr [0] = value >> 24 ; + } ; +} /* f2bes_clip_array */ + +static sf_count_t +pcm_write_f2bes (SF_PRIVATE *psf, const float *ptr, sf_count_t len) +{ void (*convert) (const float *, short *t, int, int) ; + int bufferlen, writecount ; + sf_count_t total = 0 ; + + convert = (psf->add_clipping) ? f2bes_clip_array : f2bes_array ; + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + convert (ptr + total, psf->u.sbuf, bufferlen, psf->norm_float) ; + writecount = psf_fwrite (psf->u.sbuf, sizeof (short), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* pcm_write_f2bes */ + +/*============================================================================== +*/ + +static void +f2les_array (const float *src, short *dest, int count, int normalize) +{ unsigned char *ucptr ; + float normfact ; + int value ; + + normfact = normalize ? (1.0 * 0x7FFF) : 1.0 ; + ucptr = ((unsigned char*) dest) + 2 * count ; + + while (--count >= 0) + { ucptr -= 2 ; + value = lrintf (src [count] * normfact) ; + ucptr [0] = value ; + ucptr [1] = value >> 8 ; + } ; +} /* f2les_array */ + +static void +f2les_clip_array (const float *src, short *dest, int count, int normalize) +{ unsigned char *ucptr ; + float normfact, scaled_value ; + int value ; + + normfact = normalize ? (8.0 * 0x10000000) : (1.0 * 0x10000) ; + ucptr = ((unsigned char*) dest) + 2 * count ; + + while (--count >= 0) + { ucptr -= 2 ; + scaled_value = src [count] * normfact ; + if (CPU_CLIPS_POSITIVE == 0 && scaled_value >= (1.0 * 0x7FFFFFFF)) + { ucptr [0] = 0xFF ; + ucptr [1] = 0x7F ; + continue ; + } ; + if (CPU_CLIPS_NEGATIVE == 0 && scaled_value <= (-8.0 * 0x10000000)) + { ucptr [0] = 0x00 ; + ucptr [1] = 0x80 ; + continue ; + } ; + + value = lrintf (scaled_value) ; + ucptr [0] = value >> 16 ; + ucptr [1] = value >> 24 ; + } ; +} /* f2les_clip_array */ + +static sf_count_t +pcm_write_f2les (SF_PRIVATE *psf, const float *ptr, sf_count_t len) +{ void (*convert) (const float *, short *t, int, int) ; + int bufferlen, writecount ; + sf_count_t total = 0 ; + + convert = (psf->add_clipping) ? f2les_clip_array : f2les_array ; + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + convert (ptr + total, psf->u.sbuf, bufferlen, psf->norm_float) ; + writecount = psf_fwrite (psf->u.sbuf, sizeof (short), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* pcm_write_f2les */ + +/*============================================================================== +*/ + +static void +f2let_array (const float *src, tribyte *dest, int count, int normalize) +{ unsigned char *ucptr ; + float normfact ; + int value ; + + normfact = normalize ? (1.0 * 0x7FFFFF) : 1.0 ; + ucptr = ((unsigned char*) dest) + 3 * count ; + + while (--count >= 0) + { ucptr -= 3 ; + value = lrintf (src [count] * normfact) ; + ucptr [0] = value ; + ucptr [1] = value >> 8 ; + ucptr [2] = value >> 16 ; + } ; +} /* f2let_array */ + +static void +f2let_clip_array (const float *src, tribyte *dest, int count, int normalize) +{ unsigned char *ucptr ; + float normfact, scaled_value ; + int value ; + + normfact = normalize ? (8.0 * 0x10000000) : (1.0 * 0x100) ; + ucptr = ((unsigned char*) dest) + 3 * count ; + + while (--count >= 0) + { ucptr -= 3 ; + scaled_value = src [count] * normfact ; + if (CPU_CLIPS_POSITIVE == 0 && scaled_value >= (1.0 * 0x7FFFFFFF)) + { ucptr [0] = 0xFF ; + ucptr [1] = 0xFF ; + ucptr [2] = 0x7F ; + continue ; + } ; + if (CPU_CLIPS_NEGATIVE == 0 && scaled_value <= (-8.0 * 0x10000000)) + { ucptr [0] = 0x00 ; + ucptr [1] = 0x00 ; + ucptr [2] = 0x80 ; + continue ; + } ; + + value = lrintf (scaled_value) ; + ucptr [0] = value >> 8 ; + ucptr [1] = value >> 16 ; + ucptr [2] = value >> 24 ; + } ; +} /* f2let_clip_array */ + +static sf_count_t +pcm_write_f2let (SF_PRIVATE *psf, const float *ptr, sf_count_t len) +{ void (*convert) (const float *, tribyte *, int, int) ; + int bufferlen, writecount ; + sf_count_t total = 0 ; + + convert = (psf->add_clipping) ? f2let_clip_array : f2let_array ; + bufferlen = sizeof (psf->u.ucbuf) / SIZEOF_TRIBYTE ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + convert (ptr + total, (tribyte*) (psf->u.ucbuf), bufferlen, psf->norm_float) ; + writecount = psf_fwrite (psf->u.ucbuf, SIZEOF_TRIBYTE, bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* pcm_write_f2let */ + +/*============================================================================== +*/ + +static void +f2bet_array (const float *src, tribyte *dest, int count, int normalize) +{ unsigned char *ucptr ; + float normfact ; + int value ; + + normfact = normalize ? (1.0 * 0x7FFFFF) : 1.0 ; + ucptr = ((unsigned char*) dest) + 3 * count ; + + while (--count >= 0) + { ucptr -= 3 ; + value = lrintf (src [count] * normfact) ; + ucptr [0] = value >> 16 ; + ucptr [1] = value >> 8 ; + ucptr [2] = value ; + } ; +} /* f2bet_array */ + +static void +f2bet_clip_array (const float *src, tribyte *dest, int count, int normalize) +{ unsigned char *ucptr ; + float normfact, scaled_value ; + int value ; + + normfact = normalize ? (8.0 * 0x10000000) : (1.0 * 0x100) ; + ucptr = ((unsigned char*) dest) + 3 * count ; + + while (--count >= 0) + { ucptr -= 3 ; + scaled_value = src [count] * normfact ; + if (CPU_CLIPS_POSITIVE == 0 && scaled_value >= (1.0 * 0x7FFFFFFF)) + { ucptr [0] = 0x7F ; + ucptr [1] = 0xFF ; + ucptr [2] = 0xFF ; + continue ; + } ; + if (CPU_CLIPS_NEGATIVE == 0 && scaled_value <= (-8.0 * 0x10000000)) + { ucptr [0] = 0x80 ; + ucptr [1] = 0x00 ; + ucptr [2] = 0x00 ; + continue ; + } ; + + value = lrint (scaled_value) ; + ucptr [0] = value >> 24 ; + ucptr [1] = value >> 16 ; + ucptr [2] = value >> 8 ; + } ; +} /* f2bet_clip_array */ + +static sf_count_t +pcm_write_f2bet (SF_PRIVATE *psf, const float *ptr, sf_count_t len) +{ void (*convert) (const float *, tribyte *, int, int) ; + int bufferlen, writecount ; + sf_count_t total = 0 ; + + convert = (psf->add_clipping) ? f2bet_clip_array : f2bet_array ; + bufferlen = sizeof (psf->u.ucbuf) / SIZEOF_TRIBYTE ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + convert (ptr + total, (tribyte*) (psf->u.ucbuf), bufferlen, psf->norm_float) ; + writecount = psf_fwrite (psf->u.ucbuf, SIZEOF_TRIBYTE, bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* pcm_write_f2bet */ + +/*============================================================================== +*/ + +static void +f2bei_array (const float *src, int *dest, int count, int normalize) +{ unsigned char *ucptr ; + float normfact ; + int value ; + + normfact = normalize ? (1.0 * 0x7FFFFFFF) : 1.0 ; + ucptr = ((unsigned char*) dest) + 4 * count ; + while (--count >= 0) + { ucptr -= 4 ; + value = lrintf (src [count] * normfact) ; + ucptr [0] = value >> 24 ; + ucptr [1] = value >> 16 ; + ucptr [2] = value >> 8 ; + ucptr [3] = value ; + } ; +} /* f2bei_array */ + +static void +f2bei_clip_array (const float *src, int *dest, int count, int normalize) +{ unsigned char *ucptr ; + float normfact, scaled_value ; + int value ; + + normfact = normalize ? (8.0 * 0x10000000) : 1.0 ; + ucptr = ((unsigned char*) dest) + 4 * count ; + + while (--count >= 0) + { ucptr -= 4 ; + scaled_value = src [count] * normfact ; + if (CPU_CLIPS_POSITIVE == 0 && scaled_value >= 1.0 * 0x7FFFFFFF) + { ucptr [0] = 0x7F ; + ucptr [1] = 0xFF ; + ucptr [2] = 0xFF ; + ucptr [3] = 0xFF ; + continue ; + } ; + if (CPU_CLIPS_NEGATIVE == 0 && scaled_value <= (-8.0 * 0x10000000)) + { ucptr [0] = 0x80 ; + ucptr [1] = 0x00 ; + ucptr [2] = 0x00 ; + ucptr [3] = 0x00 ; + continue ; + } ; + + value = lrintf (scaled_value) ; + ucptr [0] = value >> 24 ; + ucptr [1] = value >> 16 ; + ucptr [2] = value >> 8 ; + ucptr [3] = value ; + } ; +} /* f2bei_clip_array */ + +static sf_count_t +pcm_write_f2bei (SF_PRIVATE *psf, const float *ptr, sf_count_t len) +{ void (*convert) (const float *, int *, int, int) ; + int bufferlen, writecount ; + sf_count_t total = 0 ; + + convert = (psf->add_clipping) ? f2bei_clip_array : f2bei_array ; + bufferlen = ARRAY_LEN (psf->u.ibuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + convert (ptr + total, psf->u.ibuf, bufferlen, psf->norm_float) ; + writecount = psf_fwrite (psf->u.ibuf, sizeof (int), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* pcm_write_f2bei */ + +/*============================================================================== +*/ + +static void +f2lei_array (const float *src, int *dest, int count, int normalize) +{ unsigned char *ucptr ; + float normfact ; + int value ; + + normfact = normalize ? (1.0 * 0x7FFFFFFF) : 1.0 ; + ucptr = ((unsigned char*) dest) + 4 * count ; + + while (--count >= 0) + { ucptr -= 4 ; + value = lrintf (src [count] * normfact) ; + ucptr [0] = value ; + ucptr [1] = value >> 8 ; + ucptr [2] = value >> 16 ; + ucptr [3] = value >> 24 ; + } ; +} /* f2lei_array */ + +static void +f2lei_clip_array (const float *src, int *dest, int count, int normalize) +{ unsigned char *ucptr ; + float normfact, scaled_value ; + int value ; + + normfact = normalize ? (8.0 * 0x10000000) : 1.0 ; + ucptr = ((unsigned char*) dest) + 4 * count ; + + while (--count >= 0) + { ucptr -= 4 ; + scaled_value = src [count] * normfact ; + if (CPU_CLIPS_POSITIVE == 0 && scaled_value >= (1.0 * 0x7FFFFFFF)) + { ucptr [0] = 0xFF ; + ucptr [1] = 0xFF ; + ucptr [2] = 0xFF ; + ucptr [3] = 0x7F ; + continue ; + } ; + if (CPU_CLIPS_NEGATIVE == 0 && scaled_value <= (-8.0 * 0x10000000)) + { ucptr [0] = 0x00 ; + ucptr [1] = 0x00 ; + ucptr [2] = 0x00 ; + ucptr [3] = 0x80 ; + continue ; + } ; + + value = lrintf (scaled_value) ; + ucptr [0] = value ; + ucptr [1] = value >> 8 ; + ucptr [2] = value >> 16 ; + ucptr [3] = value >> 24 ; + } ; +} /* f2lei_clip_array */ + +static sf_count_t +pcm_write_f2lei (SF_PRIVATE *psf, const float *ptr, sf_count_t len) +{ void (*convert) (const float *, int *, int, int) ; + int bufferlen, writecount ; + sf_count_t total = 0 ; + + convert = (psf->add_clipping) ? f2lei_clip_array : f2lei_array ; + bufferlen = ARRAY_LEN (psf->u.ibuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + convert (ptr + total, psf->u.ibuf, bufferlen, psf->norm_float) ; + writecount = psf_fwrite (psf->u.ibuf, sizeof (int), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* pcm_write_f2lei */ + +/*============================================================================== +*/ + +static void +d2sc_array (const double *src, signed char *dest, int count, int normalize) +{ double normfact ; + + normfact = normalize ? (1.0 * 0x7F) : 1.0 ; + + while (--count >= 0) + { dest [count] = lrint (src [count] * normfact) ; + } ; +} /* d2sc_array */ + +static void +d2sc_clip_array (const double *src, signed char *dest, int count, int normalize) +{ double normfact, scaled_value ; + + normfact = normalize ? (8.0 * 0x10000000) : (1.0 * 0x1000000) ; + + while (--count >= 0) + { scaled_value = src [count] * normfact ; + if (CPU_CLIPS_POSITIVE == 0 && scaled_value >= (1.0 * 0x7FFFFFFF)) + { dest [count] = 127 ; + continue ; + } ; + if (CPU_CLIPS_NEGATIVE == 0 && scaled_value <= (-8.0 * 0x10000000)) + { dest [count] = -128 ; + continue ; + } ; + + dest [count] = lrintf (scaled_value) >> 24 ; + } ; +} /* d2sc_clip_array */ + +static sf_count_t +pcm_write_d2sc (SF_PRIVATE *psf, const double *ptr, sf_count_t len) +{ void (*convert) (const double *, signed char *, int, int) ; + int bufferlen, writecount ; + sf_count_t total = 0 ; + + convert = (psf->add_clipping) ? d2sc_clip_array : d2sc_array ; + bufferlen = ARRAY_LEN (psf->u.scbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + convert (ptr + total, psf->u.scbuf, bufferlen, psf->norm_double) ; + writecount = psf_fwrite (psf->u.scbuf, sizeof (signed char), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* pcm_write_d2sc */ + +/*============================================================================== +*/ + +static void +d2uc_array (const double *src, unsigned char *dest, int count, int normalize) +{ double normfact ; + + normfact = normalize ? (1.0 * 0x7F) : 1.0 ; + + while (--count >= 0) + { dest [count] = lrint (src [count] * normfact) + 128 ; + } ; +} /* d2uc_array */ + +static void +d2uc_clip_array (const double *src, unsigned char *dest, int count, int normalize) +{ double normfact, scaled_value ; + + normfact = normalize ? (8.0 * 0x10000000) : (1.0 * 0x1000000) ; + + while (--count >= 0) + { scaled_value = src [count] * normfact ; + if (CPU_CLIPS_POSITIVE == 0 && scaled_value >= (1.0 * 0x7FFFFFFF)) + { dest [count] = 255 ; + continue ; + } ; + if (CPU_CLIPS_NEGATIVE == 0 && scaled_value <= (-8.0 * 0x10000000)) + { dest [count] = 0 ; + continue ; + } ; + + dest [count] = (lrint (src [count] * normfact) >> 24) + 128 ; + } ; +} /* d2uc_clip_array */ + +static sf_count_t +pcm_write_d2uc (SF_PRIVATE *psf, const double *ptr, sf_count_t len) +{ void (*convert) (const double *, unsigned char *, int, int) ; + int bufferlen, writecount ; + sf_count_t total = 0 ; + + convert = (psf->add_clipping) ? d2uc_clip_array : d2uc_array ; + bufferlen = ARRAY_LEN (psf->u.ucbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + convert (ptr + total, psf->u.ucbuf, bufferlen, psf->norm_double) ; + writecount = psf_fwrite (psf->u.ucbuf, sizeof (unsigned char), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* pcm_write_d2uc */ + +/*============================================================================== +*/ + +static void +d2bes_array (const double *src, short *dest, int count, int normalize) +{ unsigned char *ucptr ; + short value ; + double normfact ; + + normfact = normalize ? (1.0 * 0x7FFF) : 1.0 ; + ucptr = ((unsigned char*) dest) + 2 * count ; + + while (--count >= 0) + { ucptr -= 2 ; + value = lrint (src [count] * normfact) ; + ucptr [1] = value ; + ucptr [0] = value >> 8 ; + } ; +} /* d2bes_array */ + +static void +d2bes_clip_array (const double *src, short *dest, int count, int normalize) +{ unsigned char *ucptr ; + double normfact, scaled_value ; + int value ; + + normfact = normalize ? (8.0 * 0x10000000) : (1.0 * 0x10000) ; + ucptr = ((unsigned char*) dest) + 2 * count ; + + while (--count >= 0) + { ucptr -= 2 ; + scaled_value = src [count] * normfact ; + if (CPU_CLIPS_POSITIVE == 0 && scaled_value >= (1.0 * 0x7FFFFFFF)) + { ucptr [1] = 0xFF ; + ucptr [0] = 0x7F ; + continue ; + } ; + if (CPU_CLIPS_NEGATIVE == 0 && scaled_value <= (-8.0 * 0x10000000)) + { ucptr [1] = 0x00 ; + ucptr [0] = 0x80 ; + continue ; + } ; + + value = lrint (scaled_value) ; + ucptr [1] = value >> 16 ; + ucptr [0] = value >> 24 ; + } ; +} /* d2bes_clip_array */ + +static sf_count_t +pcm_write_d2bes (SF_PRIVATE *psf, const double *ptr, sf_count_t len) +{ void (*convert) (const double *, short *, int, int) ; + int bufferlen, writecount ; + sf_count_t total = 0 ; + + convert = (psf->add_clipping) ? d2bes_clip_array : d2bes_array ; + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + convert (ptr + total, psf->u.sbuf, bufferlen, psf->norm_double) ; + writecount = psf_fwrite (psf->u.sbuf, sizeof (short), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* pcm_write_d2bes */ + +/*============================================================================== +*/ + +static void +d2les_array (const double *src, short *dest, int count, int normalize) +{ unsigned char *ucptr ; + short value ; + double normfact ; + + normfact = normalize ? (1.0 * 0x7FFF) : 1.0 ; + ucptr = ((unsigned char*) dest) + 2 * count ; + + while (--count >= 0) + { ucptr -= 2 ; + value = lrint (src [count] * normfact) ; + ucptr [0] = value ; + ucptr [1] = value >> 8 ; + } ; +} /* d2les_array */ + +static void +d2les_clip_array (const double *src, short *dest, int count, int normalize) +{ unsigned char *ucptr ; + int value ; + double normfact, scaled_value ; + + normfact = normalize ? (8.0 * 0x10000000) : (1.0 * 0x10000) ; + ucptr = ((unsigned char*) dest) + 2 * count ; + + while (--count >= 0) + { ucptr -= 2 ; + scaled_value = src [count] * normfact ; + if (CPU_CLIPS_POSITIVE == 0 && scaled_value >= (1.0 * 0x7FFFFFFF)) + { ucptr [0] = 0xFF ; + ucptr [1] = 0x7F ; + continue ; + } ; + if (CPU_CLIPS_NEGATIVE == 0 && scaled_value <= (-8.0 * 0x10000000)) + { ucptr [0] = 0x00 ; + ucptr [1] = 0x80 ; + continue ; + } ; + + value = lrint (scaled_value) ; + ucptr [0] = value >> 16 ; + ucptr [1] = value >> 24 ; + } ; +} /* d2les_clip_array */ + +static sf_count_t +pcm_write_d2les (SF_PRIVATE *psf, const double *ptr, sf_count_t len) +{ void (*convert) (const double *, short *, int, int) ; + int bufferlen, writecount ; + sf_count_t total = 0 ; + + convert = (psf->add_clipping) ? d2les_clip_array : d2les_array ; + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + convert (ptr + total, psf->u.sbuf, bufferlen, psf->norm_double) ; + writecount = psf_fwrite (psf->u.sbuf, sizeof (short), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* pcm_write_d2les */ + +/*============================================================================== +*/ + +static void +d2let_array (const double *src, tribyte *dest, int count, int normalize) +{ unsigned char *ucptr ; + int value ; + double normfact ; + + normfact = normalize ? (1.0 * 0x7FFFFF) : 1.0 ; + ucptr = ((unsigned char*) dest) + 3 * count ; + + while (--count >= 0) + { ucptr -= 3 ; + value = lrint (src [count] * normfact) ; + ucptr [0] = value ; + ucptr [1] = value >> 8 ; + ucptr [2] = value >> 16 ; + } ; +} /* d2let_array */ + +static void +d2let_clip_array (const double *src, tribyte *dest, int count, int normalize) +{ unsigned char *ucptr ; + int value ; + double normfact, scaled_value ; + + normfact = normalize ? (8.0 * 0x10000000) : (1.0 * 0x100) ; + ucptr = ((unsigned char*) dest) + 3 * count ; + + while (--count >= 0) + { ucptr -= 3 ; + scaled_value = src [count] * normfact ; + if (CPU_CLIPS_POSITIVE == 0 && scaled_value >= (1.0 * 0x7FFFFFFF)) + { ucptr [0] = 0xFF ; + ucptr [1] = 0xFF ; + ucptr [2] = 0x7F ; + continue ; + } ; + if (CPU_CLIPS_NEGATIVE == 0 && scaled_value <= (-8.0 * 0x10000000)) + { ucptr [0] = 0x00 ; + ucptr [1] = 0x00 ; + ucptr [2] = 0x80 ; + continue ; + } ; + + value = lrint (scaled_value) ; + ucptr [0] = value >> 8 ; + ucptr [1] = value >> 16 ; + ucptr [2] = value >> 24 ; + } ; +} /* d2let_clip_array */ + +static sf_count_t +pcm_write_d2let (SF_PRIVATE *psf, const double *ptr, sf_count_t len) +{ void (*convert) (const double *, tribyte *, int, int) ; + int bufferlen, writecount ; + sf_count_t total = 0 ; + + convert = (psf->add_clipping) ? d2let_clip_array : d2let_array ; + bufferlen = sizeof (psf->u.ucbuf) / SIZEOF_TRIBYTE ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + convert (ptr + total, (tribyte*) (psf->u.ucbuf), bufferlen, psf->norm_double) ; + writecount = psf_fwrite (psf->u.ucbuf, SIZEOF_TRIBYTE, bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* pcm_write_d2let */ + +/*============================================================================== +*/ + +static void +d2bet_array (const double *src, tribyte *dest, int count, int normalize) +{ unsigned char *ucptr ; + int value ; + double normfact ; + + normfact = normalize ? (1.0 * 0x7FFFFF) : 1.0 ; + ucptr = ((unsigned char*) dest) + 3 * count ; + + while (--count >= 0) + { ucptr -= 3 ; + value = lrint (src [count] * normfact) ; + ucptr [2] = value ; + ucptr [1] = value >> 8 ; + ucptr [0] = value >> 16 ; + } ; +} /* d2bet_array */ + +static void +d2bet_clip_array (const double *src, tribyte *dest, int count, int normalize) +{ unsigned char *ucptr ; + int value ; + double normfact, scaled_value ; + + normfact = normalize ? (8.0 * 0x10000000) : (1.0 * 0x100) ; + ucptr = ((unsigned char*) dest) + 3 * count ; + + while (--count >= 0) + { ucptr -= 3 ; + scaled_value = src [count] * normfact ; + if (CPU_CLIPS_POSITIVE == 0 && scaled_value >= (1.0 * 0x7FFFFFFF)) + { ucptr [2] = 0xFF ; + ucptr [1] = 0xFF ; + ucptr [0] = 0x7F ; + continue ; + } ; + if (CPU_CLIPS_NEGATIVE == 0 && scaled_value <= (-8.0 * 0x10000000)) + { ucptr [2] = 0x00 ; + ucptr [1] = 0x00 ; + ucptr [0] = 0x80 ; + continue ; + } ; + + value = lrint (scaled_value) ; + ucptr [2] = value >> 8 ; + ucptr [1] = value >> 16 ; + ucptr [0] = value >> 24 ; + } ; +} /* d2bet_clip_array */ + +static sf_count_t +pcm_write_d2bet (SF_PRIVATE *psf, const double *ptr, sf_count_t len) +{ void (*convert) (const double *, tribyte *, int, int) ; + int bufferlen, writecount ; + sf_count_t total = 0 ; + + convert = (psf->add_clipping) ? d2bet_clip_array : d2bet_array ; + bufferlen = sizeof (psf->u.ucbuf) / SIZEOF_TRIBYTE ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + convert (ptr + total, (tribyte*) (psf->u.ucbuf), bufferlen, psf->norm_double) ; + writecount = psf_fwrite (psf->u.ucbuf, SIZEOF_TRIBYTE, bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* pcm_write_d2bet */ + +/*============================================================================== +*/ + +static void +d2bei_array (const double *src, int *dest, int count, int normalize) +{ unsigned char *ucptr ; + int value ; + double normfact ; + + normfact = normalize ? (1.0 * 0x7FFFFFFF) : 1.0 ; + ucptr = ((unsigned char*) dest) + 4 * count ; + + while (--count >= 0) + { ucptr -= 4 ; + value = lrint (src [count] * normfact) ; + ucptr [0] = value >> 24 ; + ucptr [1] = value >> 16 ; + ucptr [2] = value >> 8 ; + ucptr [3] = value ; + } ; +} /* d2bei_array */ + +static void +d2bei_clip_array (const double *src, int *dest, int count, int normalize) +{ unsigned char *ucptr ; + int value ; + double normfact, scaled_value ; + + normfact = normalize ? (8.0 * 0x10000000) : 1.0 ; + ucptr = ((unsigned char*) dest) + 4 * count ; + + while (--count >= 0) + { ucptr -= 4 ; + scaled_value = src [count] * normfact ; + if (CPU_CLIPS_POSITIVE == 0 && scaled_value >= (1.0 * 0x7FFFFFFF)) + { ucptr [3] = 0xFF ; + ucptr [2] = 0xFF ; + ucptr [1] = 0xFF ; + ucptr [0] = 0x7F ; + continue ; + } ; + if (CPU_CLIPS_NEGATIVE == 0 && scaled_value <= (-8.0 * 0x10000000)) + { ucptr [3] = 0x00 ; + ucptr [2] = 0x00 ; + ucptr [1] = 0x00 ; + ucptr [0] = 0x80 ; + continue ; + } ; + + value = lrint (scaled_value) ; + ucptr [0] = value >> 24 ; + ucptr [1] = value >> 16 ; + ucptr [2] = value >> 8 ; + ucptr [3] = value ; + } ; +} /* d2bei_clip_array */ + +static sf_count_t +pcm_write_d2bei (SF_PRIVATE *psf, const double *ptr, sf_count_t len) +{ void (*convert) (const double *, int *, int, int) ; + int bufferlen, writecount ; + sf_count_t total = 0 ; + + convert = (psf->add_clipping) ? d2bei_clip_array : d2bei_array ; + bufferlen = ARRAY_LEN (psf->u.ibuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + convert (ptr + total, psf->u.ibuf, bufferlen, psf->norm_double) ; + writecount = psf_fwrite (psf->u.ibuf, sizeof (int), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* pcm_write_d2bei */ + +/*============================================================================== +*/ + +static void +d2lei_array (const double *src, int *dest, int count, int normalize) +{ unsigned char *ucptr ; + int value ; + double normfact ; + + normfact = normalize ? (1.0 * 0x7FFFFFFF) : 1.0 ; + ucptr = ((unsigned char*) dest) + 4 * count ; + + while (--count >= 0) + { ucptr -= 4 ; + value = lrint (src [count] * normfact) ; + ucptr [0] = value ; + ucptr [1] = value >> 8 ; + ucptr [2] = value >> 16 ; + ucptr [3] = value >> 24 ; + } ; +} /* d2lei_array */ + +static void +d2lei_clip_array (const double *src, int *dest, int count, int normalize) +{ unsigned char *ucptr ; + int value ; + double normfact, scaled_value ; + + normfact = normalize ? (8.0 * 0x10000000) : 1.0 ; + ucptr = ((unsigned char*) dest) + 4 * count ; + + while (--count >= 0) + { ucptr -= 4 ; + scaled_value = src [count] * normfact ; + if (CPU_CLIPS_POSITIVE == 0 && scaled_value >= (1.0 * 0x7FFFFFFF)) + { ucptr [0] = 0xFF ; + ucptr [1] = 0xFF ; + ucptr [2] = 0xFF ; + ucptr [3] = 0x7F ; + continue ; + } ; + if (CPU_CLIPS_NEGATIVE == 0 && scaled_value <= (-8.0 * 0x10000000)) + { ucptr [0] = 0x00 ; + ucptr [1] = 0x00 ; + ucptr [2] = 0x00 ; + ucptr [3] = 0x80 ; + continue ; + } ; + + value = lrint (scaled_value) ; + ucptr [0] = value ; + ucptr [1] = value >> 8 ; + ucptr [2] = value >> 16 ; + ucptr [3] = value >> 24 ; + } ; +} /* d2lei_clip_array */ + +static sf_count_t +pcm_write_d2lei (SF_PRIVATE *psf, const double *ptr, sf_count_t len) +{ void (*convert) (const double *, int *, int, int) ; + int bufferlen, writecount ; + sf_count_t total = 0 ; + + convert = (psf->add_clipping) ? d2lei_clip_array : d2lei_array ; + bufferlen = ARRAY_LEN (psf->u.ibuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + convert (ptr + total, psf->u.ibuf, bufferlen, psf->norm_double) ; + writecount = psf_fwrite (psf->u.ibuf, sizeof (int), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* pcm_write_d2lei */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: d8bc7c0e-1e2f-4ff3-a28f-10ce1fbade3b +*/ diff --git a/src/pvf.c b/src/pvf.c new file mode 100644 index 00000000..b759dbb2 --- /dev/null +++ b/src/pvf.c @@ -0,0 +1,195 @@ +/* +** Copyright (C) 2002-2004 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include +#include + +#include "sndfile.h" +#include "sfendian.h" +#include "common.h" + +/*------------------------------------------------------------------------------ +** Macros to handle big/little endian issues. +*/ + +#define PVF1_MARKER (MAKE_MARKER ('P', 'V', 'F', '1')) + +/*------------------------------------------------------------------------------ +** Private static functions. +*/ + +static int pvf_close (SF_PRIVATE *psf) ; + +static int pvf_write_header (SF_PRIVATE *psf, int calc_length) ; +static int pvf_read_header (SF_PRIVATE *psf) ; + +/*------------------------------------------------------------------------------ +** Public function. +*/ + +int +pvf_open (SF_PRIVATE *psf) +{ int subformat ; + int error = 0 ; + + if (psf->mode == SFM_READ || (psf->mode == SFM_RDWR && psf->filelength > 0)) + { if ((error = pvf_read_header (psf))) + return error ; + } ; + + subformat = psf->sf.format & SF_FORMAT_SUBMASK ; + + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + { if ((psf->sf.format & SF_FORMAT_TYPEMASK) != SF_FORMAT_PVF) + return SFE_BAD_OPEN_FORMAT ; + + psf->endian = SF_ENDIAN_BIG ; + + if (pvf_write_header (psf, SF_FALSE)) + return psf->error ; + + psf->write_header = pvf_write_header ; + } ; + + psf->container_close = pvf_close ; + + psf->blockwidth = psf->bytewidth * psf->sf.channels ; + + switch (subformat) + { case SF_FORMAT_PCM_S8 : /* 8-bit linear PCM. */ + case SF_FORMAT_PCM_16 : /* 16-bit linear PCM. */ + case SF_FORMAT_PCM_32 : /* 32-bit linear PCM. */ + error = pcm_init (psf) ; + break ; + + default : break ; + } ; + + return error ; +} /* pvf_open */ + +/*------------------------------------------------------------------------------ +*/ + +static int +pvf_close (SF_PRIVATE * UNUSED (psf)) +{ + return 0 ; +} /* pvf_close */ + +static int +pvf_write_header (SF_PRIVATE *psf, int UNUSED (calc_length)) +{ sf_count_t current ; + + if (psf->pipeoffset > 0) + return 0 ; + + current = psf_ftell (psf) ; + + /* Reset the current header length to zero. */ + psf->header [0] = 0 ; + psf->headindex = 0 ; + + if (psf->is_pipe == SF_FALSE) + psf_fseek (psf, 0, SEEK_SET) ; + + LSF_SNPRINTF ((char*) psf->header, sizeof (psf->header), "PVF1\n%d %d %d\n", + psf->sf.channels, psf->sf.samplerate, psf->bytewidth * 8) ; + + psf->headindex = strlen ((char*) psf->header) ; + + /* Header construction complete so write it out. */ + psf_fwrite (psf->header, psf->headindex, 1, psf) ; + + if (psf->error) + return psf->error ; + + psf->dataoffset = psf->headindex ; + + if (current > 0) + psf_fseek (psf, current, SEEK_SET) ; + + return psf->error ; +} /* pvf_write_header */ + +static int +pvf_read_header (SF_PRIVATE *psf) +{ char buffer [32] ; + int marker, channels, samplerate, bitwidth ; + + psf_binheader_readf (psf, "pmj", 0, &marker, 1) ; + psf_log_printf (psf, "%M\n", marker) ; + + if (marker != PVF1_MARKER) + return SFE_PVF_NO_PVF1 ; + + /* Grab characters up until a newline which is replaced by an EOS. */ + psf_binheader_readf (psf, "G", buffer, sizeof (buffer)) ; + + if (sscanf (buffer, "%d %d %d", &channels, &samplerate, &bitwidth) != 3) + return SFE_PVF_BAD_HEADER ; + + psf_log_printf (psf, " Channels : %d\n Sample rate : %d\n Bit width : %d\n", + channels, samplerate, bitwidth) ; + + psf->sf.channels = channels ; + psf->sf.samplerate = samplerate ; + + switch (bitwidth) + { case 8 : + psf->sf.format = SF_FORMAT_PVF | SF_FORMAT_PCM_S8 ; + psf->bytewidth = 1 ; + break ; + + case 16 : + psf->sf.format = SF_FORMAT_PVF | SF_FORMAT_PCM_16 ; + psf->bytewidth = 2 ; + break ; + case 32 : + psf->sf.format = SF_FORMAT_PVF | SF_FORMAT_PCM_32 ; + psf->bytewidth = 4 ; + break ; + + default : + return SFE_PVF_BAD_BITWIDTH ; + } ; + + psf->dataoffset = psf_ftell (psf) ; + psf_log_printf (psf, " Data Offset : %D\n", psf->dataoffset) ; + + psf->endian = SF_ENDIAN_BIG ; + + psf->datalength = psf->filelength - psf->dataoffset ; + psf->blockwidth = psf->sf.channels * psf->bytewidth ; + + if (! psf->sf.frames && psf->blockwidth) + psf->sf.frames = (psf->filelength - psf->dataoffset) / psf->blockwidth ; + + return 0 ; +} /* pvf_read_header */ +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 20a26761-8bc1-41d7-b1f3-9793bf3d9864 +*/ diff --git a/src/raw.c b/src/raw.c new file mode 100644 index 00000000..65be491f --- /dev/null +++ b/src/raw.c @@ -0,0 +1,111 @@ +/* +** Copyright (C) 1999-2004 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include + +#include "sndfile.h" +#include "common.h" + +/*------------------------------------------------------------------------------ +** Public function. +*/ + +int +raw_open (SF_PRIVATE *psf) +{ int subformat, error = SFE_NO_ERROR ; + + subformat = psf->sf.format & SF_FORMAT_SUBMASK ; + + psf->endian = psf->sf.format & SF_FORMAT_ENDMASK ; + + if (CPU_IS_BIG_ENDIAN && (psf->endian == 0 || psf->endian == SF_ENDIAN_CPU)) + psf->endian = SF_ENDIAN_BIG ; + else if (CPU_IS_LITTLE_ENDIAN && (psf->endian == 0 || psf->endian == SF_ENDIAN_CPU)) + psf->endian = SF_ENDIAN_LITTLE ; + + psf->blockwidth = psf->bytewidth * psf->sf.channels ; + psf->dataoffset = 0 ; + psf->datalength = psf->filelength ; + + switch (subformat) + { case SF_FORMAT_PCM_S8 : + error = pcm_init (psf) ; + break ; + + case SF_FORMAT_PCM_U8 : + error = pcm_init (psf) ; + break ; + + case SF_FORMAT_PCM_16 : + case SF_FORMAT_PCM_24 : + case SF_FORMAT_PCM_32 : + error = pcm_init (psf) ; + break ; + + case SF_FORMAT_ULAW : + error = ulaw_init (psf) ; + break ; + + case SF_FORMAT_ALAW : + error = alaw_init (psf) ; + break ; + + case SF_FORMAT_GSM610 : + error = gsm610_init (psf) ; + break ; + + /* Lite remove start */ + case SF_FORMAT_FLOAT : + error = float32_init (psf) ; + break ; + + case SF_FORMAT_DOUBLE : + error = double64_init (psf) ; + break ; + + case SF_FORMAT_DWVW_12 : + error = dwvw_init (psf, 12) ; + break ; + + case SF_FORMAT_DWVW_16 : + error = dwvw_init (psf, 16) ; + break ; + + case SF_FORMAT_DWVW_24 : + error = dwvw_init (psf, 24) ; + break ; + + case SF_FORMAT_VOX_ADPCM : + error = vox_adpcm_init (psf) ; + break ; + /* Lite remove end */ + + default : return SFE_BAD_OPEN_FORMAT ; + } ; + + return error ; +} /* raw_open */ +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: f0066de7-d6ce-4f36-a1e0-e475c07d4e1a +*/ diff --git a/src/rx2.c b/src/rx2.c new file mode 100644 index 00000000..b3a3090c --- /dev/null +++ b/src/rx2.c @@ -0,0 +1,326 @@ +/* +** Copyright (C) 2001-2004 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include +#include + +#include "sndfile.h" +#include "sfendian.h" +#include "common.h" + +#if (ENABLE_EXPERIMENTAL_CODE == 0) + +int +rx2_open (SF_PRIVATE *psf) +{ if (psf) + return SFE_UNIMPLEMENTED ; + return 0 ; +} /* rx2_open */ + +#else + +/*------------------------------------------------------------------------------ + * Macros to handle big/little endian issues. +*/ + +#define CAT_MARKER (MAKE_MARKER ('C', 'A', 'T', ' ')) +#define GLOB_MARKER (MAKE_MARKER ('G', 'L', 'O', 'B')) + +#define RECY_MARKER (MAKE_MARKER ('R', 'E', 'C', 'Y')) + +#define SLCL_MARKER (MAKE_MARKER ('S', 'L', 'C', 'L')) +#define SLCE_MARKER (MAKE_MARKER ('S', 'L', 'C', 'E')) + +#define DEVL_MARKER (MAKE_MARKER ('D', 'E', 'V', 'L')) +#define TRSH_MARKER (MAKE_MARKER ('T', 'R', 'S', 'H')) + +#define EQ_MARKER (MAKE_MARKER ('E', 'Q', ' ', ' ')) +#define COMP_MARKER (MAKE_MARKER ('C', 'O', 'M', 'P')) + +#define SINF_MARKER (MAKE_MARKER ('S', 'I', 'N', 'F')) +#define SDAT_MARKER (MAKE_MARKER ('S', 'D', 'A', 'T')) + +/*------------------------------------------------------------------------------ + * Typedefs for file chunks. +*/ + + +/*------------------------------------------------------------------------------ + * Private static functions. +*/ +static int rx2_close (SF_PRIVATE *psf) ; + +/*------------------------------------------------------------------------------ +** Public functions. +*/ + +int +rx2_open (SF_PRIVATE *psf) +{ static const char *marker_type [4] = + { "Original Enabled", "Enabled Hidden", + "Additional/PencilTool", "Disabled" + } ; + + int error, marker, length, glob_offset, slce_count, frames ; + + int sdat_length = 0, slce_total = 0 ; + + int n_channels ; + + + /* So far only doing read. */ + + psf_binheader_readf (psf, "Epm4", 0, &marker, &length) ; + + if (marker != CAT_MARKER) + { psf_log_printf (psf, "length : %d\n", length) ; + return -1000 ; + } ; + + if (length != psf->filelength - 8) + psf_log_printf (psf, "%M : %d (should be %d)\n", marker, length, psf->filelength - 8) ; + else + psf_log_printf (psf, "%M : %d\n", marker, length) ; + + /* 'REX2' marker */ + psf_binheader_readf (psf, "m", &marker) ; + psf_log_printf (psf, "%M", marker) ; + + /* 'HEAD' marker */ + psf_binheader_readf (psf, "m", &marker) ; + psf_log_printf (psf, "%M\n", marker) ; + + /* Grab 'GLOB' offset. */ + psf_binheader_readf (psf, "E4", &glob_offset) ; + glob_offset += 0x14 ; /* Add the current file offset. */ + + /* Jump to offset 0x30 */ + psf_binheader_readf (psf, "p", 0x30) ; + + /* Get name length */ + length = 0 ; + psf_binheader_readf (psf, "1", &length) ; + if (length >= SIGNED_SIZEOF (psf->u.cbuf)) + { psf_log_printf (psf, " Text : %d *** Error : Too sf_count_t!\n") ; + return -1001 ; + } + + memset (psf->u.cbuf, 0, sizeof (psf->u.cbuf)) ; + psf_binheader_readf (psf, "b", psf->u.cbuf, length) ; + psf_log_printf (psf, " Text : \"%s\"\n", psf->u.cbuf) ; + + /* Jump to GLOB offset position. */ + if (glob_offset & 1) + glob_offset ++ ; + + psf_binheader_readf (psf, "p", glob_offset) ; + + slce_count = 0 ; + /* GLOB */ + while (1) + { psf_binheader_readf (psf, "m", &marker) ; + + if (marker != SLCE_MARKER && slce_count > 0) + { psf_log_printf (psf, " SLCE count : %d\n", slce_count) ; + slce_count = 0 ; + } + switch (marker) + { case GLOB_MARKER: + psf_binheader_readf (psf, "E4", &length) ; + psf_log_printf (psf, " %M : %d\n", marker, length) ; + psf_binheader_readf (psf, "j", length) ; + break ; + + case RECY_MARKER: + psf_binheader_readf (psf, "E4", &length) ; + psf_log_printf (psf, " %M : %d\n", marker, length) ; + psf_binheader_readf (psf, "j", (length+1) & 0xFFFFFFFE) ; /* ?????? */ + break ; + + case CAT_MARKER: + psf_binheader_readf (psf, "E4", &length) ; + psf_log_printf (psf, " %M : %d\n", marker, length) ; + /*-psf_binheader_readf (psf, "j", length) ;-*/ + break ; + + case DEVL_MARKER: + psf_binheader_readf (psf, "mE4", &marker, &length) ; + psf_log_printf (psf, " DEVL%M : %d\n", marker, length) ; + if (length & 1) + length ++ ; + psf_binheader_readf (psf, "j", length) ; + break ; + + case EQ_MARKER: + case COMP_MARKER: + psf_binheader_readf (psf, "E4", &length) ; + psf_log_printf (psf, " %M : %d\n", marker, length) ; + /* This is weird!!!! why make this (length - 1) */ + if (length & 1) + length ++ ; + psf_binheader_readf (psf, "j", length) ; + break ; + + case SLCL_MARKER: + psf_log_printf (psf, " %M\n (Offset, Next Offset, Type)\n", marker) ; + slce_count = 0 ; + break ; + + case SLCE_MARKER: + { int len [4], indx ; + + psf_binheader_readf (psf, "E4444", &len [0], &len [1], &len [2], &len [3]) ; + + indx = ((len [3] & 0x0000FFFF) >> 8) & 3 ; + + if (len [2] == 1) + { if (indx != 1) + indx = 3 ; /* 2 cases, where next slice offset = 1 -> disabled & enabled/hidden */ + + psf_log_printf (psf, " %M : (%6d, ?: 0x%X, %s)\n", marker, len [1], (len [3] & 0xFFFF0000) >> 16, marker_type [indx]) ; + } + else + { slce_total += len [2] ; + + psf_log_printf (psf, " %M : (%6d, SLCE_next_ofs:%d, ?: 0x%X, %s)\n", marker, len [1], len [2], (len [3] & 0xFFFF0000) >> 16, marker_type [indx]) ; + } ; + + slce_count ++ ; + } ; + break ; + + case SINF_MARKER: + psf_binheader_readf (psf, "E4", &length) ; + psf_log_printf (psf, " %M : %d\n", marker, length) ; + + psf_binheader_readf (psf, "E2", &n_channels) ; + n_channels = (n_channels & 0x0000FF00) >> 8 ; + psf_log_printf (psf, " Channels : %d\n", n_channels) ; + + psf_binheader_readf (psf, "E44", &psf->sf.samplerate, &frames) ; + psf->sf.frames = frames ; + psf_log_printf (psf, " Sample Rate : %d\n", psf->sf.samplerate) ; + psf_log_printf (psf, " Frames : %D\n", psf->sf.frames) ; + + psf_binheader_readf (psf, "E4", &length) ; + psf_log_printf (psf, " ??????????? : %d\n", length) ; + + psf_binheader_readf (psf, "E4", &length) ; + psf_log_printf (psf, " ??????????? : %d\n", length) ; + break ; + + case SDAT_MARKER: + psf_binheader_readf (psf, "E4", &length) ; + + sdat_length = length ; + + /* Get the current offset. */ + psf->dataoffset = psf_binheader_readf (psf, NULL) ; + + if (psf->dataoffset + length != psf->filelength) + psf_log_printf (psf, " %M : %d (should be %d)\n", marker, length, psf->dataoffset + psf->filelength) ; + else + psf_log_printf (psf, " %M : %d\n", marker, length) ; + break ; + + default : + psf_log_printf (psf, "Unknown marker : 0x%X %M", marker, marker) ; + return -1003 ; + break ; + } ; + + /* SDAT always last marker in file. */ + if (marker == SDAT_MARKER) + break ; + } ; + + puts (psf->logbuffer) ; + puts ("-----------------------------------") ; + + printf ("SDAT length : %d\n", sdat_length) ; + printf ("SLCE count : %d\n", slce_count) ; + + /* Hack for zero slice count. */ + if (slce_count == 0 && slce_total == 1) + slce_total = frames ; + + printf ("SLCE samples : %d\n", slce_total) ; + + /* Two bytes per sample. */ + printf ("Comp Ratio : %f:1\n", (2.0 * slce_total * n_channels) / sdat_length) ; + + puts (" ") ; + + psf->logbuffer [0] = 0 ; + + /* OK, have the header although not too sure what it all means. */ + + psf->endian = SF_ENDIAN_BIG ; + + psf->datalength = psf->filelength - psf->dataoffset ; + + if (psf_fseek (psf, psf->dataoffset, SEEK_SET)) + return SFE_BAD_SEEK ; + + psf->sf.format = (SF_FORMAT_REX2 | SF_FORMAT_DWVW_12) ; + + psf->sf.channels = 1 ; + psf->bytewidth = 2 ; + psf->blockwidth = psf->sf.channels * psf->bytewidth ; + + if ((error = dwvw_init (psf, 16))) + return error ; + + psf->container_close = rx2_close ; + + if (! psf->sf.frames && psf->blockwidth) + psf->sf.frames = psf->datalength / psf->blockwidth ; + + /* All done. */ + + return 0 ; +} /* rx2_open */ + +/*------------------------------------------------------------------------------ +*/ + +static int +rx2_close (SF_PRIVATE *psf) +{ + if (psf->mode == SFM_WRITE) + { /* Now we know for certain the length of the file we can re-write + ** correct values for the FORM, 8SVX and BODY chunks. + */ + + } ; + + return 0 ; +} /* rx2_close */ + +#endif +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 7366e813-9fee-4d1f-881e-e4a691469370 +*/ diff --git a/src/sd2.c b/src/sd2.c new file mode 100644 index 00000000..675807ba --- /dev/null +++ b/src/sd2.c @@ -0,0 +1,624 @@ +/* +** Copyright (C) 2001-2006 Erik de Castro Lopo +** Copyright (C) 2004 Paavo Jumppanen +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* +** The sd2 support implemented in this file was partially sponsored +** (financially) by Paavo Jumppanen. +*/ + +/* +** Documentation on the Mac resource fork was obtained here : +** http://developer.apple.com/documentation/mac/MoreToolbox/MoreToolbox-99.html +*/ + +#include "sfconfig.h" + +#include +#include +#include +#include + +#include "sndfile.h" +#include "sfendian.h" +#include "common.h" + +/*------------------------------------------------------------------------------ + * Markers. +*/ + +#define Sd2f_MARKER MAKE_MARKER ('S', 'd', '2', 'f') +#define Sd2a_MARKER MAKE_MARKER ('S', 'd', '2', 'a') +#define ALCH_MARKER MAKE_MARKER ('A', 'L', 'C', 'H') +#define lsf1_MARKER MAKE_MARKER ('l', 's', 'f', '1') + +#define STR_MARKER MAKE_MARKER ('S', 'T', 'R', ' ') +#define sdML_MARKER MAKE_MARKER ('s', 'd', 'M', 'L') + +enum +{ RSRC_STR = 111, + RSRC_BIN +} ; + +typedef struct +{ unsigned char * rsrc_data ; + int rsrc_len ; + + int data_offset, data_length ; + int map_offset, map_length ; + + int type_count, type_offset ; + int item_offset ; + + int str_index, str_count ; + + int string_offset ; + + /* All the above just to get these three. */ + int sample_size, sample_rate, channels ; +} SD2_RSRC ; + +typedef struct +{ int type ; + int id ; + char name [32] ; + char value [32] ; + int value_len ; +} STR_RSRC ; + +/*------------------------------------------------------------------------------ + * Private static functions. +*/ + +static int sd2_close (SF_PRIVATE *psf) ; + +static int sd2_parse_rsrc_fork (SF_PRIVATE *psf) ; +static int parse_str_rsrc (SF_PRIVATE *psf, SD2_RSRC * rsrc) ; + +static int sd2_write_rsrc_fork (SF_PRIVATE *psf, int calc_length) ; + +/*------------------------------------------------------------------------------ +** Public functions. +*/ + +int +sd2_open (SF_PRIVATE *psf) +{ int subformat, error = 0, valid ; + + /* SD2 is always big endian. */ + psf->endian = SF_ENDIAN_BIG ; + + if (psf->mode == SFM_READ || (psf->mode == SFM_RDWR && psf->rsrclength > 0)) + { psf_use_rsrc (psf, SF_TRUE) ; + valid = psf_file_valid (psf) ; + psf_use_rsrc (psf, SF_FALSE) ; + if (! valid) + { psf_log_printf (psf, "sd2_open : psf->rsrcdes < 0\n") ; + return SFE_SD2_BAD_RSRC ; + } ; + + error = sd2_parse_rsrc_fork (psf) ; + + if (error) + goto error_cleanup ; + } ; + + if ((psf->sf.format & SF_FORMAT_TYPEMASK) != SF_FORMAT_SD2) + { error = SFE_BAD_OPEN_FORMAT ; + goto error_cleanup ; + } ; + + subformat = psf->sf.format & SF_FORMAT_SUBMASK ; + psf->dataoffset = 0 ; + + /* Only open and write the resource in RDWR mode is its current length is zero. */ + if (psf->mode == SFM_WRITE || (psf->mode == SFM_RDWR && psf->rsrclength == 0)) + { psf_open_rsrc (psf, psf->mode) ; + + error = sd2_write_rsrc_fork (psf, SF_FALSE) ; + + if (error) + goto error_cleanup ; + + /* Not needed. */ + psf->write_header = NULL ; + } ; + + psf->container_close = sd2_close ; + + psf->blockwidth = psf->bytewidth * psf->sf.channels ; + + switch (subformat) + { case SF_FORMAT_PCM_S8 : /* 8-bit linear PCM. */ + case SF_FORMAT_PCM_16 : /* 16-bit linear PCM. */ + case SF_FORMAT_PCM_24 : /* 24-bit linear PCM */ + error = pcm_init (psf) ; + break ; + + default : + error = SFE_UNIMPLEMENTED ; + break ; + } ; + + psf_fseek (psf, psf->dataoffset, SEEK_SET) ; + +error_cleanup: + + /* Close the resource fork regardless. We won't need it again. */ + psf_close_rsrc (psf) ; + + return error ; +} /* sd2_open */ + +/*------------------------------------------------------------------------------ +*/ + +static int +sd2_close (SF_PRIVATE *psf) +{ + if (psf->mode == SFM_WRITE) + { /* Now we know for certain the audio_length of the file we can re-write + ** correct values for the FORM, 8SVX and BODY chunks. + */ + + } ; + + return 0 ; +} /* sd2_close */ + +/*------------------------------------------------------------------------------ +*/ + +static inline void +write_char (unsigned char * data, int offset, char value) +{ data [offset] = value ; +} /* write_char */ + +static inline void +write_short (unsigned char * data, int offset, short value) +{ data [offset] = value >> 8 ; + data [offset + 1] = value ; +} /* write_char */ + +static inline void +write_int (unsigned char * data, int offset, int value) +{ data [offset] = value >> 24 ; + data [offset + 1] = value >> 16 ; + data [offset + 2] = value >> 8 ; + data [offset + 3] = value ; +} /* write_int */ + +static inline void +write_marker (unsigned char * data, int offset, int value) +{ + if (CPU_IS_BIG_ENDIAN) + { data [offset] = value >> 24 ; + data [offset + 1] = value >> 16 ; + data [offset + 2] = value >> 8 ; + data [offset + 3] = value ; + } + else + { data [offset] = value ; + data [offset + 1] = value >> 8 ; + data [offset + 2] = value >> 16 ; + data [offset + 3] = value >> 24 ; + } ; +} /* write_marker */ + +static void +write_str (unsigned char * data, int offset, char * buffer, int buffer_len) +{ memcpy (data + offset, buffer, buffer_len) ; +} /* write_str */ + +static int +sd2_write_rsrc_fork (SF_PRIVATE *psf, int UNUSED (calc_length)) +{ SD2_RSRC rsrc ; + STR_RSRC str_rsrc [] = + { { RSRC_STR, 1000, "_sample-size", "", 0 }, + { RSRC_STR, 1001, "_sample-rate", "", 0 }, + { RSRC_STR, 1002, "_channels", "", 0 }, + { RSRC_BIN, 1000, "_Markers", "", 8 } + } ; + + int k, str_offset, data_offset, next_str ; + + psf_use_rsrc (psf, SF_TRUE) ; + + memset (&rsrc, 0, sizeof (rsrc)) ; + + rsrc.sample_rate = psf->sf.samplerate ; + rsrc.sample_size = psf->bytewidth ; + rsrc.channels = psf->sf.channels ; + + rsrc.rsrc_data = psf->header ; + rsrc.rsrc_len = sizeof (psf->header) ; + memset (rsrc.rsrc_data, 0xea, rsrc.rsrc_len) ; + + LSF_SNPRINTF (str_rsrc [0].value, sizeof (str_rsrc [0].value), "_%d", rsrc.sample_size) ; + LSF_SNPRINTF (str_rsrc [1].value, sizeof (str_rsrc [1].value), "_%d.000000", rsrc.sample_rate) ; + LSF_SNPRINTF (str_rsrc [2].value, sizeof (str_rsrc [2].value), "_%d", rsrc.channels) ; + + for (k = 0 ; k < ARRAY_LEN (str_rsrc) ; k++) + { if (str_rsrc [k].value_len == 0) + { str_rsrc [k].value_len = strlen (str_rsrc [k].value) ; + str_rsrc [k].value [0] = str_rsrc [k].value_len - 1 ; + } ; + + /* Turn name string into a pascal string. */ + str_rsrc [k].name [0] = strlen (str_rsrc [k].name) - 1 ; + } ; + + rsrc.data_offset = 0x100 ; + + /* + ** Calculate data length : + ** length of strings, plus the length of the sdML chunk. + */ + rsrc.data_length = 0 ; + for (k = 0 ; k < ARRAY_LEN (str_rsrc) ; k++) + rsrc.data_length += str_rsrc [k].value_len + 4 ; + + rsrc.map_offset = rsrc.data_offset + rsrc.data_length ; + + /* Very start of resource fork. */ + write_int (rsrc.rsrc_data, 0, rsrc.data_offset) ; + write_int (rsrc.rsrc_data, 4, rsrc.map_offset) ; + write_int (rsrc.rsrc_data, 8, rsrc.data_length) ; + + write_char (rsrc.rsrc_data, 0x30, strlen (psf->filename)) ; + write_str (rsrc.rsrc_data, 0x31, psf->filename, strlen (psf->filename)) ; + + write_short (rsrc.rsrc_data, 0x50, 0) ; + write_marker (rsrc.rsrc_data, 0x52, Sd2f_MARKER) ; + write_marker (rsrc.rsrc_data, 0x56, lsf1_MARKER) ; + + /* Very start of resource map. */ + write_int (rsrc.rsrc_data, rsrc.map_offset + 0, rsrc.data_offset) ; + write_int (rsrc.rsrc_data, rsrc.map_offset + 4, rsrc.map_offset) ; + write_int (rsrc.rsrc_data, rsrc.map_offset + 8, rsrc.data_length) ; + + /* These I don't currently understand. */ + if (1) + { write_char (rsrc.rsrc_data, rsrc.map_offset+ 16, 1) ; + /* Next resource map. */ + write_int (rsrc.rsrc_data, rsrc.map_offset + 17, 0x12345678) ; + /* File ref number. */ + write_short (rsrc.rsrc_data, rsrc.map_offset + 21, 0xabcd) ; + /* Fork attributes. */ + write_short (rsrc.rsrc_data, rsrc.map_offset + 23, 0) ; + } ; + + /* Resource type offset. */ + rsrc.type_offset = rsrc.map_offset + 30 ; + write_short (rsrc.rsrc_data, rsrc.map_offset + 24, rsrc.type_offset - rsrc.map_offset - 2) ; + + /* Type index max. */ + rsrc.type_count = 2 ; + write_short (rsrc.rsrc_data, rsrc.map_offset + 28, rsrc.type_count - 1) ; + + rsrc.item_offset = rsrc.type_offset + rsrc.type_count * 8 ; + + rsrc.str_count = ARRAY_LEN (str_rsrc) ; + rsrc.string_offset = rsrc.item_offset + (rsrc.str_count + 1) * 12 - rsrc.map_offset ; + write_short (rsrc.rsrc_data, rsrc.map_offset + 26, rsrc.string_offset) ; + + /* Write 'STR ' resource type. */ + rsrc.str_count = 3 ; + write_marker (rsrc.rsrc_data, rsrc.type_offset, STR_MARKER) ; + write_short (rsrc.rsrc_data, rsrc.type_offset + 4, rsrc.str_count - 1) ; + write_short (rsrc.rsrc_data, rsrc.type_offset + 6, 0x12) ; + + /* Write 'sdML' resource type. */ + write_marker (rsrc.rsrc_data, rsrc.type_offset + 8, sdML_MARKER) ; + write_short (rsrc.rsrc_data, rsrc.type_offset + 12, 0) ; + write_short (rsrc.rsrc_data, rsrc.type_offset + 14, 0x36) ; + + str_offset = rsrc.map_offset + rsrc.string_offset ; + next_str = 0 ; + data_offset = rsrc.data_offset ; + for (k = 0 ; k < ARRAY_LEN (str_rsrc) ; k++) + { write_str (rsrc.rsrc_data, str_offset, str_rsrc [k].name, strlen (str_rsrc [k].name)) ; + + write_short (rsrc.rsrc_data, rsrc.item_offset + k * 12, str_rsrc [k].id) ; + write_short (rsrc.rsrc_data, rsrc.item_offset + k * 12 + 2, next_str) ; + + str_offset += strlen (str_rsrc [k].name) ; + next_str += strlen (str_rsrc [k].name) ; + + write_int (rsrc.rsrc_data, rsrc.item_offset + k * 12 + 4, data_offset - rsrc.data_offset) ; + + write_int (rsrc.rsrc_data, data_offset, str_rsrc [k].value_len) ; + write_str (rsrc.rsrc_data, data_offset + 4, str_rsrc [k].value, str_rsrc [k].value_len) ; + data_offset += 4 + str_rsrc [k].value_len ; + } ; + + /* Finally, calculate and set map length. */ + rsrc.map_length = str_offset - rsrc.map_offset ; + write_int (rsrc.rsrc_data, 12, rsrc.map_length) ; + write_int (rsrc.rsrc_data, rsrc.map_offset + 12, rsrc.map_length) ; + + rsrc.rsrc_len = rsrc.map_offset + rsrc.map_length ; + + psf_fwrite (rsrc.rsrc_data, rsrc.rsrc_len, 1, psf) ; + + psf_use_rsrc (psf, SF_FALSE) ; + + if (psf->error) + return psf->error ; + + return 0 ; +} /* sd2_write_rsrc_fork */ + +/*------------------------------------------------------------------------------ +*/ + +static inline int +read_char (const unsigned char * data, int offset) +{ return data [offset] ; +} /* read_char */ + +static inline int +read_short (const unsigned char * data, int offset) +{ return (data [offset] << 8) + data [offset + 1] ; +} /* read_short */ + +static inline int +read_int (const unsigned char * data, int offset) +{ return (data [offset] << 24) + (data [offset + 1] << 16) + (data [offset + 2] << 8) + data [offset + 3] ; +} /* read_int */ + +static inline int +read_marker (const unsigned char * data, int offset) +{ + if (CPU_IS_BIG_ENDIAN) + return (data [offset] << 24) + (data [offset + 1] << 16) + (data [offset + 2] << 8) + data [offset + 3] ; + else if (CPU_IS_LITTLE_ENDIAN) + return data [offset] + (data [offset + 1] << 8) + (data [offset + 2] << 16) + (data [offset + 3] << 24) ; + else + return 0x666 ; +} /* read_marker */ + +static void +read_str (const unsigned char * data, int offset, char * buffer, int buffer_len) +{ int k ; + + memset (buffer, 0, buffer_len) ; + + for (k = 0 ; k < buffer_len - 1 ; k++) + { if (isprint (data [offset + k]) == 0) + return ; + buffer [k] = data [offset + k] ; + } ; + return ; +} /* read_str */ + +static int +sd2_parse_rsrc_fork (SF_PRIVATE *psf) +{ SD2_RSRC rsrc ; + int k, marker, error = 0 ; + + psf_use_rsrc (psf, SF_TRUE) ; + + memset (&rsrc, 0, sizeof (rsrc)) ; + + rsrc.rsrc_len = psf_get_filelen (psf) ; + psf_log_printf (psf, "Resource length : %d (0x%04X)\n", rsrc.rsrc_len, rsrc.rsrc_len) ; + + if (rsrc.rsrc_len > SIGNED_SIZEOF (psf->header)) + rsrc.rsrc_data = calloc (1, rsrc.rsrc_len) ; + else + rsrc.rsrc_data = psf->header ; + + /* Read in the whole lot. */ + psf_fread (rsrc.rsrc_data, rsrc.rsrc_len, 1, psf) ; + + /* Reset the header storage because we have changed to the rsrcdes. */ + psf->headindex = psf->headend = rsrc.rsrc_len ; + + rsrc.data_offset = read_int (rsrc.rsrc_data, 0) ; + rsrc.map_offset = read_int (rsrc.rsrc_data, 4) ; + rsrc.data_length = read_int (rsrc.rsrc_data, 8) ; + rsrc.map_length = read_int (rsrc.rsrc_data, 12) ; + + if (rsrc.data_offset == 0x51607 && rsrc.map_offset == 0x20000) + { psf_log_printf (psf, "Trying offset of 0x52 bytes.\n") ; + rsrc.data_offset = read_int (rsrc.rsrc_data, 0x52 + 0) + 0x52 ; + rsrc.map_offset = read_int (rsrc.rsrc_data, 0x52 + 4) + 0x52 ; + rsrc.data_length = read_int (rsrc.rsrc_data, 0x52 + 8) ; + rsrc.map_length = read_int (rsrc.rsrc_data, 0x52 + 12) ; + } ; + + psf_log_printf (psf, " data offset : 0x%04X\n map offset : 0x%04X\n" + " data length : 0x%04X\n map length : 0x%04X\n", + rsrc.data_offset, rsrc.map_offset, rsrc.data_length, rsrc.map_length) ; + + if (rsrc.data_offset > rsrc.rsrc_len) + { psf_log_printf (psf, "Error : rsrc.data_offset (%d, 0x%x) > len\n", rsrc.data_offset, rsrc.data_offset) ; + error = SFE_SD2_BAD_DATA_OFFSET ; + goto parse_rsrc_fork_cleanup ; + } ; + + if (rsrc.map_offset > rsrc.rsrc_len) + { psf_log_printf (psf, "Error : rsrc.map_offset > len\n") ; + error = SFE_SD2_BAD_MAP_OFFSET ; + goto parse_rsrc_fork_cleanup ; + } ; + + if (rsrc.data_length > rsrc.rsrc_len) + { psf_log_printf (psf, "Error : rsrc.data_length > len\n") ; + error = SFE_SD2_BAD_DATA_LENGTH ; + goto parse_rsrc_fork_cleanup ; + } ; + + if (rsrc.map_length > rsrc.rsrc_len) + { psf_log_printf (psf, "Error : rsrc.map_length > len\n") ; + error = SFE_SD2_BAD_MAP_LENGTH ; + goto parse_rsrc_fork_cleanup ; + } ; + + if (rsrc.data_offset + rsrc.data_length != rsrc.map_offset || rsrc.map_offset + rsrc.map_length != rsrc.rsrc_len) + { psf_log_printf (psf, "Error : This does not look like a MacOSX resource fork.\n") ; + error = SFE_SD2_BAD_RSRC ; + goto parse_rsrc_fork_cleanup ; + } ; + + rsrc.string_offset = rsrc.map_offset + read_short (rsrc.rsrc_data, rsrc.map_offset + 26) ; + if (rsrc.string_offset > rsrc.rsrc_len) + { psf_log_printf (psf, "Bad string offset (%d).\n", rsrc.string_offset) ; + error = SFE_SD2_BAD_RSRC ; + goto parse_rsrc_fork_cleanup ; + } ; + + rsrc.type_offset = rsrc.map_offset + 30 ; + + rsrc.type_count = read_short (rsrc.rsrc_data, rsrc.map_offset + 28) + 1 ; + if (rsrc.type_count < 1) + { psf_log_printf (psf, "Bad type count.\n") ; + error = SFE_SD2_BAD_RSRC ; + goto parse_rsrc_fork_cleanup ; + } ; + + rsrc.item_offset = rsrc.type_offset + rsrc.type_count * 8 ; + if (rsrc.item_offset < 0 || rsrc.item_offset > rsrc.rsrc_len) + { psf_log_printf (psf, "Bad item offset (%d).\n", rsrc.item_offset) ; + error = SFE_SD2_BAD_RSRC ; + goto parse_rsrc_fork_cleanup ; + } ; + + rsrc.str_index = -1 ; + for (k = 0 ; k < rsrc.type_count ; k ++) + { marker = read_marker (rsrc.rsrc_data, rsrc.type_offset + k * 8) ; + + if (marker == STR_MARKER) + { rsrc.str_index = k ; + rsrc.str_count = read_short (rsrc.rsrc_data, rsrc.type_offset + k * 8 + 4) + 1 ; + error = parse_str_rsrc (psf, &rsrc) ; + goto parse_rsrc_fork_cleanup ; + } ; + } ; + + psf_log_printf (psf, "No 'STR ' resource.\n") ; + error = SFE_SD2_BAD_RSRC ; + +parse_rsrc_fork_cleanup : + + psf_use_rsrc (psf, SF_FALSE) ; + + if ((void *) rsrc.rsrc_data < (void *) psf || (void *) rsrc.rsrc_data > (void *) (psf + 1)) + free (rsrc.rsrc_data) ; + + return error ; +} /* sd2_parse_rsrc_fork */ + +static int +parse_str_rsrc (SF_PRIVATE *psf, SD2_RSRC * rsrc) +{ char name [32], value [32] ; + int k, str_offset, data_offset, data_len, rsrc_id ; + + psf_log_printf (psf, "Finding parameters :\n") ; + + str_offset = rsrc->string_offset ; + psf_log_printf (psf, " Name Offset RsrcId dlen slen Value\n") ; + + for (k = 0 ; k < rsrc->str_count ; k++) + { int slen ; + + slen = read_char (rsrc->rsrc_data, str_offset) ; + read_str (rsrc->rsrc_data, str_offset + 1, name, SF_MIN (SIGNED_SIZEOF (name), slen + 1)) ; + str_offset += slen + 1 ; + + rsrc_id = read_short (rsrc->rsrc_data, rsrc->item_offset + k * 12) ; + + data_offset = rsrc->data_offset + read_int (rsrc->rsrc_data, rsrc->item_offset + k * 12 + 4) ; + if (data_offset < 0 || data_offset > rsrc->rsrc_len) + { psf_log_printf (psf, "Bad data offset (%d)\n", data_offset) ; + return SFE_SD2_BAD_DATA_OFFSET ; + } ; + + data_len = read_int (rsrc->rsrc_data, data_offset) ; + if (data_len < 0 || data_len > rsrc->rsrc_len) + { psf_log_printf (psf, "Bad data length (%d).\n", data_len) ; + return SFE_SD2_BAD_RSRC ; + } ; + + slen = read_char (rsrc->rsrc_data, data_offset + 4) ; + read_str (rsrc->rsrc_data, data_offset + 5, value, SF_MIN (SIGNED_SIZEOF (value), slen + 1)) ; + + psf_log_printf (psf, " %-12s 0x%04x %4d %2d %2d '%s'\n", name, data_offset, rsrc_id, data_len, slen, value) ; + + if (strcmp (name, "sample-size") == 0 && rsrc->sample_size == 0) + rsrc->sample_size = strtol (value, NULL, 10) ; + else if (strcmp (name, "sample-rate") == 0 && rsrc->sample_rate == 0) + rsrc->sample_rate = strtol (value, NULL, 10) ; + else if (strcmp (name, "channels") == 0 && rsrc->channels == 0) + rsrc->channels = strtol (value, NULL, 10) ; + } ; + + if (rsrc->sample_rate <= 4 && rsrc->sample_size > 4) + { int temp ; + + psf_log_printf (psf, "Geez!! Looks like sample rate and sample size got switched.\nCorrecting this screw up.\n") ; + temp = rsrc->sample_rate ; + rsrc->sample_rate = rsrc->sample_size ; + rsrc->sample_size = temp ; + } ; + + if (rsrc->sample_rate < 0) + { psf_log_printf (psf, "Bad sample rate (%d)\n", rsrc->sample_rate) ; + return SFE_SD2_BAD_RSRC ; + } ; + + if (rsrc->channels < 0) + { psf_log_printf (psf, "Bad channel count (%d)\n", rsrc->channels) ; + return SFE_SD2_BAD_RSRC ; + } ; + + psf->sf.samplerate = rsrc->sample_rate ; + psf->sf.channels = rsrc->channels ; + psf->bytewidth = rsrc->sample_size ; + + switch (rsrc->sample_size) + { case 1 : + psf->sf.format = SF_FORMAT_SD2 | SF_FORMAT_PCM_S8 ; + break ; + + case 2 : + psf->sf.format = SF_FORMAT_SD2 | SF_FORMAT_PCM_16 ; + break ; + + case 3 : + psf->sf.format = SF_FORMAT_SD2 | SF_FORMAT_PCM_24 ; + break ; + + default : + psf_log_printf (psf, "Bad sample size (%d)\n", rsrc->sample_size) ; + return SFE_SD2_BAD_SAMPLE_SIZE ; + } ; + + psf_log_printf (psf, "ok\n") ; + + return 0 ; +} /* parse_str_rsrc */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 1ee183e5-6b9f-4c2c-bd0a-24f35595cefc +*/ diff --git a/src/sds.c b/src/sds.c new file mode 100644 index 00000000..710d2117 --- /dev/null +++ b/src/sds.c @@ -0,0 +1,993 @@ +/* +** Copyright (C) 2002-2005 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include +#include +#include + +#include "sndfile.h" +#include "sfendian.h" +#include "common.h" +#include "float_cast.h" + +/*------------------------------------------------------------------------------ +*/ + +#define SDS_DATA_OFFSET 0x15 +#define SDS_BLOCK_SIZE 127 + +#define SDS_AUDIO_BYTES_PER_BLOCK 120 + +#define SDS_3BYTE_TO_INT_DECODE(x) (((x) & 0x7F) | (((x) & 0x7F00) >> 1) | (((x) & 0x7F0000) >> 2)) +#define SDS_INT_TO_3BYTE_ENCODE(x) (((x) & 0x7F) | (((x) << 1) & 0x7F00) | (((x) << 2) & 0x7F0000)) + +/*------------------------------------------------------------------------------ +** Typedefs. +*/ + +typedef struct tag_SDS_PRIVATE +{ int bitwidth, frames ; + int samplesperblock, total_blocks ; + + int (*reader) (SF_PRIVATE *psf, struct tag_SDS_PRIVATE *psds) ; + int (*writer) (SF_PRIVATE *psf, struct tag_SDS_PRIVATE *psds) ; + + int read_block, read_count ; + unsigned char read_data [SDS_BLOCK_SIZE] ; + int read_samples [SDS_BLOCK_SIZE / 2] ; /* Maximum samples per block */ + + int write_block, write_count ; + unsigned char write_data [SDS_BLOCK_SIZE] ; + int write_samples [SDS_BLOCK_SIZE / 2] ; /* Maximum samples per block */ +} SDS_PRIVATE ; + +/*------------------------------------------------------------------------------ +** Private static functions. +*/ + +static int sds_close (SF_PRIVATE *psf) ; + +static int sds_write_header (SF_PRIVATE *psf, int calc_length) ; +static int sds_read_header (SF_PRIVATE *psf, SDS_PRIVATE *psds) ; + +static int sds_init (SF_PRIVATE *psf, SDS_PRIVATE *psds) ; + +static sf_count_t sds_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ; +static sf_count_t sds_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ; +static sf_count_t sds_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ; +static sf_count_t sds_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ; + +static sf_count_t sds_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ; +static sf_count_t sds_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ; +static sf_count_t sds_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ; +static sf_count_t sds_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ; + +static sf_count_t sds_seek (SF_PRIVATE *psf, int mode, sf_count_t offset) ; + +static int sds_2byte_read (SF_PRIVATE *psf, SDS_PRIVATE *psds) ; +static int sds_3byte_read (SF_PRIVATE *psf, SDS_PRIVATE *psds) ; +static int sds_4byte_read (SF_PRIVATE *psf, SDS_PRIVATE *psds) ; + +static int sds_read (SF_PRIVATE *psf, SDS_PRIVATE *psds, int *iptr, int readcount) ; + +static int sds_2byte_write (SF_PRIVATE *psf, SDS_PRIVATE *psds) ; +static int sds_3byte_write (SF_PRIVATE *psf, SDS_PRIVATE *psds) ; +static int sds_4byte_write (SF_PRIVATE *psf, SDS_PRIVATE *psds) ; + +static int sds_write (SF_PRIVATE *psf, SDS_PRIVATE *psds, const int *iptr, int writecount) ; + +/*------------------------------------------------------------------------------ +** Public function. +*/ + +int +sds_open (SF_PRIVATE *psf) +{ SDS_PRIVATE *psds ; + int error = 0 ; + + /* Hmmmm, need this here to pass update_header_test. */ + psf->sf.frames = 0 ; + + if (! (psds = calloc (1, sizeof (SDS_PRIVATE)))) + return SFE_MALLOC_FAILED ; + psf->codec_data = psds ; + + if (psf->mode == SFM_READ || (psf->mode == SFM_RDWR && psf->filelength > 0)) + { if ((error = sds_read_header (psf, psds))) + return error ; + } ; + + if ((psf->sf.format & SF_FORMAT_TYPEMASK) != SF_FORMAT_SDS) + return SFE_BAD_OPEN_FORMAT ; + + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + { if (sds_write_header (psf, SF_FALSE)) + return psf->error ; + + psf->write_header = sds_write_header ; + + psf_fseek (psf, SDS_DATA_OFFSET, SEEK_SET) ; + } ; + + if ((error = sds_init (psf, psds)) != 0) + return error ; + + psf->seek = sds_seek ; + psf->container_close = sds_close ; + + psf->blockwidth = 0 ; + + return error ; +} /* sds_open */ + +/*------------------------------------------------------------------------------ +*/ + +static int +sds_close (SF_PRIVATE *psf) +{ + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + { SDS_PRIVATE *psds ; + + if ((psds = (SDS_PRIVATE *) psf->codec_data) == NULL) + { psf_log_printf (psf, "*** Bad psf->codec_data ptr.\n") ; + return SFE_INTERNAL ; + } ; + + if (psds->write_count > 0) + { memset (&(psds->write_data [psds->write_count]), 0, (psds->samplesperblock - psds->write_count) * sizeof (int)) ; + psds->writer (psf, psds) ; + } ; + + sds_write_header (psf, SF_TRUE) ; + } ; + + return 0 ; +} /* sds_close */ + +static int +sds_init (SF_PRIVATE *psf, SDS_PRIVATE *psds) +{ + if (psds->bitwidth < 8 || psds->bitwidth > 28) + return (psf->error = SFE_SDS_BAD_BIT_WIDTH) ; + + if (psds->bitwidth < 14) + { psds->reader = sds_2byte_read ; + psds->writer = sds_2byte_write ; + psds->samplesperblock = SDS_AUDIO_BYTES_PER_BLOCK / 2 ; + } + else if (psds->bitwidth < 21) + { psds->reader = sds_3byte_read ; + psds->writer = sds_3byte_write ; + psds->samplesperblock = SDS_AUDIO_BYTES_PER_BLOCK / 3 ; + } + else + { psds->reader = sds_4byte_read ; + psds->writer = sds_4byte_write ; + psds->samplesperblock = SDS_AUDIO_BYTES_PER_BLOCK / 4 ; + } ; + + if (psf->mode == SFM_READ || psf->mode == SFM_RDWR) + { psf->read_short = sds_read_s ; + psf->read_int = sds_read_i ; + psf->read_float = sds_read_f ; + psf->read_double = sds_read_d ; + + /* Read first block. */ + psds->reader (psf, psds) ; + } ; + + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + { psf->write_short = sds_write_s ; + psf->write_int = sds_write_i ; + psf->write_float = sds_write_f ; + psf->write_double = sds_write_d ; + } ; + + return 0 ; +} /* sds_init */ + +static int +sds_read_header (SF_PRIVATE *psf, SDS_PRIVATE *psds) +{ unsigned char channel, bitwidth, loop_type, byte ; + unsigned short sample_no, marker ; + unsigned int samp_period, data_length, sustain_loop_start, sustain_loop_end ; + int bytesread, blockcount ; + + /* Set position to start of file to begin reading header. */ + bytesread = psf_binheader_readf (psf, "pE211", 0, &marker, &channel, &byte) ; + + if (marker != 0xF07E || byte != 0x01) + return SFE_SDS_NOT_SDS ; + + psf_log_printf (psf, "Midi Sample Dump Standard (.sds)\nF07E\n Midi Channel : %d\n", channel) ; + + bytesread += psf_binheader_readf (psf, "e213", &sample_no, &bitwidth, &samp_period) ; + + sample_no = SDS_3BYTE_TO_INT_DECODE (sample_no) ; + samp_period = SDS_3BYTE_TO_INT_DECODE (samp_period) ; + + psds->bitwidth = bitwidth ; + + psf->sf.samplerate = 1000000000 / samp_period ; + + psf_log_printf (psf, " Sample Number : %d\n" + " Bit Width : %d\n" + " Sample Rate : %d\n", + sample_no, psds->bitwidth, psf->sf.samplerate) ; + + bytesread += psf_binheader_readf (psf, "e3331", &data_length, &sustain_loop_start, &sustain_loop_end, &loop_type) ; + + data_length = SDS_3BYTE_TO_INT_DECODE (data_length) ; + + sustain_loop_start = SDS_3BYTE_TO_INT_DECODE (sustain_loop_start) ; + sustain_loop_end = SDS_3BYTE_TO_INT_DECODE (sustain_loop_end) ; + + psf_log_printf (psf, " Sustain Loop\n" + " Start : %d\n" + " End : %d\n" + " Loop Type : %d\n", + sustain_loop_start, sustain_loop_end, loop_type) ; + + psf->dataoffset = SDS_DATA_OFFSET ; + psf->datalength = psf->filelength - psf->dataoffset ; + + if (data_length != psf->filelength - psf->dataoffset) + { psf_log_printf (psf, " Datalength : %d (truncated data??? %d)\n", data_length, psf->filelength - psf->dataoffset) ; + data_length = psf->filelength - psf->dataoffset ; + } + else + psf_log_printf (psf, " Datalength : %d\n", data_length) ; + + bytesread += psf_binheader_readf (psf, "1", &byte) ; + if (byte != 0xF7) + psf_log_printf (psf, "bad end : %X\n", byte & 0xFF) ; + + for (blockcount = 0 ; bytesread < psf->filelength ; blockcount++) + { + bytesread += psf_fread (&marker, 1, 2, psf) ; + + if (marker == 0) + break ; + + psf_fseek (psf, SDS_BLOCK_SIZE - 2, SEEK_CUR) ; + bytesread += SDS_BLOCK_SIZE - 2 ; + } ; + + psf_log_printf (psf, "\nBlocks : %d\n", blockcount) ; + psds->total_blocks = blockcount ; + + psds->samplesperblock = SDS_AUDIO_BYTES_PER_BLOCK / ((psds->bitwidth + 6) / 7) ; + psf_log_printf (psf, "Samples/Block : %d\n", psds->samplesperblock) ; + + psf_log_printf (psf, "Frames : %d\n", blockcount * psds->samplesperblock) ; + + psf->sf.frames = blockcount * psds->samplesperblock ; + psds->frames = blockcount * psds->samplesperblock ; + + /* Always Mono */ + psf->sf.channels = 1 ; + psf->sf.sections = 1 ; + + /* + ** Lie to the user about PCM bit width. Always round up to + ** the next multiple of 8. + */ + switch ((psds->bitwidth + 7) / 8) + { case 1 : + psf->sf.format = SF_FORMAT_SDS | SF_FORMAT_PCM_S8 ; + break ; + + case 2 : + psf->sf.format = SF_FORMAT_SDS | SF_FORMAT_PCM_16 ; + break ; + + case 3 : + psf->sf.format = SF_FORMAT_SDS | SF_FORMAT_PCM_24 ; + break ; + + case 4 : + psf->sf.format = SF_FORMAT_SDS | SF_FORMAT_PCM_32 ; + break ; + + default : + psf_log_printf (psf, "*** Weird byte width (%d)\n", (psds->bitwidth + 7) / 8) ; + return SFE_SDS_BAD_BIT_WIDTH ; + } ; + + psf_fseek (psf, SDS_DATA_OFFSET, SEEK_SET) ; + + return 0 ; +} /* sds_read_header */ + +static int +sds_write_header (SF_PRIVATE *psf, int calc_length) +{ SDS_PRIVATE *psds ; + sf_count_t current ; + int samp_period, data_length, sustain_loop_start, sustain_loop_end ; + unsigned char loop_type = 0 ; + + if ((psds = (SDS_PRIVATE *) psf->codec_data) == NULL) + { psf_log_printf (psf, "*** Bad psf->codec_data ptr.\n") ; + return SFE_INTERNAL ; + } ; + + if (psf->pipeoffset > 0) + return 0 ; + + current = psf_ftell (psf) ; + + if (calc_length) + psf->sf.frames = psds->total_blocks * psds->samplesperblock + psds->write_count ; + + if (psds->write_count > 0) + { int current_count = psds->write_count ; + int current_block = psds->write_block ; + + psds->writer (psf, psds) ; + + psf_fseek (psf, -1 * SDS_BLOCK_SIZE, SEEK_CUR) ; + + psds->write_count = current_count ; + psds->write_block = current_block ; + } ; + + /* Reset the current header length to zero. */ + psf->header [0] = 0 ; + psf->headindex = 0 ; + + if (psf->is_pipe == SF_FALSE) + psf_fseek (psf, 0, SEEK_SET) ; + + psf_binheader_writef (psf, "E211", 0xF07E, 0, 1) ; + + switch (psf->sf.format & SF_FORMAT_SUBMASK) + { case SF_FORMAT_PCM_S8 : + psds->bitwidth = 8 ; + break ; + case SF_FORMAT_PCM_16 : + psds->bitwidth = 16 ; + break ; + case SF_FORMAT_PCM_24 : + psds->bitwidth = 24 ; + break ; + default: + return SFE_SDS_BAD_BIT_WIDTH ; + } ; + + samp_period = SDS_INT_TO_3BYTE_ENCODE (1000000000 / psf->sf.samplerate) ; + + psf_binheader_writef (psf, "e213", 0, psds->bitwidth, samp_period) ; + + data_length = SDS_INT_TO_3BYTE_ENCODE (psds->total_blocks * SDS_BLOCK_SIZE) ; + sustain_loop_start = SDS_INT_TO_3BYTE_ENCODE (0) ; + sustain_loop_end = SDS_INT_TO_3BYTE_ENCODE (psf->sf.frames) ; + + psf_binheader_writef (psf, "e33311", data_length, sustain_loop_start, sustain_loop_end, loop_type, 0xF7) ; + + /* Header construction complete so write it out. */ + psf_fwrite (psf->header, psf->headindex, 1, psf) ; + + if (psf->error) + return psf->error ; + + psf->dataoffset = psf->headindex ; + psf->datalength = psds->write_block * SDS_BLOCK_SIZE ; + + if (current > 0) + psf_fseek (psf, current, SEEK_SET) ; + + return psf->error ; +} /* sds_write_header */ + + +/*------------------------------------------------------------------------------ +*/ + +static int +sds_2byte_read (SF_PRIVATE *psf, SDS_PRIVATE *psds) +{ unsigned char *ucptr, checksum ; + unsigned int sample ; + int k ; + + psds->read_block ++ ; + psds->read_count = 0 ; + + if (psds->read_block * psds->samplesperblock > psds->frames) + { memset (psds->read_samples, 0, psds->samplesperblock * sizeof (int)) ; + return 1 ; + } ; + + if ((k = psf_fread (psds->read_data, 1, SDS_BLOCK_SIZE, psf)) != SDS_BLOCK_SIZE) + psf_log_printf (psf, "*** Warning : short read (%d != %d).\n", k, SDS_BLOCK_SIZE) ; + + if (psds->read_data [0] != 0xF0) + { printf ("Error A : %02X\n", psds->read_data [0] & 0xFF) ; + } ; + + checksum = psds->read_data [1] ; + if (checksum != 0x7E) + { printf ("Error 1 : %02X\n", checksum & 0xFF) ; + } + + for (k = 2 ; k < SDS_BLOCK_SIZE - 3 ; k ++) + checksum ^= psds->read_data [k] ; + + checksum &= 0x7F ; + + if (checksum != psds->read_data [SDS_BLOCK_SIZE - 2]) + { psf_log_printf (psf, "Block %d : checksum is %02X should be %02X\n", psds->read_data [4], checksum, psds->read_data [SDS_BLOCK_SIZE - 2]) ; + } ; + + ucptr = psds->read_data + 5 ; + for (k = 0 ; k < 120 ; k += 2) + { sample = (ucptr [k] << 25) + (ucptr [k + 1] << 18) ; + psds->read_samples [k / 2] = (int) (sample - 0x80000000) ; + } ; + + return 1 ; +} /* sds_2byte_read */ + +static int +sds_3byte_read (SF_PRIVATE *psf, SDS_PRIVATE *psds) +{ unsigned char *ucptr, checksum ; + unsigned int sample ; + int k ; + + psds->read_block ++ ; + psds->read_count = 0 ; + + if (psds->read_block * psds->samplesperblock > psds->frames) + { memset (psds->read_samples, 0, psds->samplesperblock * sizeof (int)) ; + return 1 ; + } ; + + if ((k = psf_fread (psds->read_data, 1, SDS_BLOCK_SIZE, psf)) != SDS_BLOCK_SIZE) + psf_log_printf (psf, "*** Warning : short read (%d != %d).\n", k, SDS_BLOCK_SIZE) ; + + if (psds->read_data [0] != 0xF0) + { printf ("Error A : %02X\n", psds->read_data [0] & 0xFF) ; + } ; + + checksum = psds->read_data [1] ; + if (checksum != 0x7E) + { printf ("Error 1 : %02X\n", checksum & 0xFF) ; + } + + for (k = 2 ; k < SDS_BLOCK_SIZE - 3 ; k ++) + checksum ^= psds->read_data [k] ; + + checksum &= 0x7F ; + + if (checksum != psds->read_data [SDS_BLOCK_SIZE - 2]) + { psf_log_printf (psf, "Block %d : checksum is %02X should be %02X\n", psds->read_data [4], checksum, psds->read_data [SDS_BLOCK_SIZE - 2]) ; + } ; + + ucptr = psds->read_data + 5 ; + for (k = 0 ; k < 120 ; k += 3) + { sample = (ucptr [k] << 25) + (ucptr [k + 1] << 18) + (ucptr [k + 2] << 11) ; + psds->read_samples [k / 3] = (int) (sample - 0x80000000) ; + } ; + + return 1 ; +} /* sds_3byte_read */ + +static int +sds_4byte_read (SF_PRIVATE *psf, SDS_PRIVATE *psds) +{ unsigned char *ucptr, checksum ; + unsigned int sample ; + int k ; + + psds->read_block ++ ; + psds->read_count = 0 ; + + if (psds->read_block * psds->samplesperblock > psds->frames) + { memset (psds->read_samples, 0, psds->samplesperblock * sizeof (int)) ; + return 1 ; + } ; + + if ((k = psf_fread (psds->read_data, 1, SDS_BLOCK_SIZE, psf)) != SDS_BLOCK_SIZE) + psf_log_printf (psf, "*** Warning : short read (%d != %d).\n", k, SDS_BLOCK_SIZE) ; + + if (psds->read_data [0] != 0xF0) + { printf ("Error A : %02X\n", psds->read_data [0] & 0xFF) ; + } ; + + checksum = psds->read_data [1] ; + if (checksum != 0x7E) + { printf ("Error 1 : %02X\n", checksum & 0xFF) ; + } + + for (k = 2 ; k < SDS_BLOCK_SIZE - 3 ; k ++) + checksum ^= psds->read_data [k] ; + + checksum &= 0x7F ; + + if (checksum != psds->read_data [SDS_BLOCK_SIZE - 2]) + { psf_log_printf (psf, "Block %d : checksum is %02X should be %02X\n", psds->read_data [4], checksum, psds->read_data [SDS_BLOCK_SIZE - 2]) ; + } ; + + ucptr = psds->read_data + 5 ; + for (k = 0 ; k < 120 ; k += 4) + { sample = (ucptr [k] << 25) + (ucptr [k + 1] << 18) + (ucptr [k + 2] << 11) + (ucptr [k + 3] << 4) ; + psds->read_samples [k / 4] = (int) (sample - 0x80000000) ; + } ; + + return 1 ; +} /* sds_4byte_read */ + + +static sf_count_t +sds_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len) +{ SDS_PRIVATE *psds ; + int *iptr ; + int k, bufferlen, readcount, count ; + sf_count_t total = 0 ; + + if (psf->codec_data == NULL) + return 0 ; + psds = (SDS_PRIVATE*) psf->codec_data ; + + iptr = psf->u.ibuf ; + bufferlen = ARRAY_LEN (psf->u.ibuf) ; + while (len > 0) + { readcount = (len >= bufferlen) ? bufferlen : len ; + count = sds_read (psf, psds, iptr, readcount) ; + for (k = 0 ; k < readcount ; k++) + ptr [total + k] = iptr [k] >> 16 ; + total += count ; + len -= readcount ; + } ; + + return total ; +} /* sds_read_s */ + +static sf_count_t +sds_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len) +{ SDS_PRIVATE *psds ; + int total ; + + if (psf->codec_data == NULL) + return 0 ; + psds = (SDS_PRIVATE*) psf->codec_data ; + + total = sds_read (psf, psds, ptr, len) ; + + return total ; +} /* sds_read_i */ + +static sf_count_t +sds_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len) +{ SDS_PRIVATE *psds ; + int *iptr ; + int k, bufferlen, readcount, count ; + sf_count_t total = 0 ; + float normfact ; + + if (psf->codec_data == NULL) + return 0 ; + psds = (SDS_PRIVATE*) psf->codec_data ; + + if (psf->norm_float == SF_TRUE) + normfact = 1.0 / 0x80000000 ; + else + normfact = 1.0 / (1 << psds->bitwidth) ; + + iptr = psf->u.ibuf ; + bufferlen = ARRAY_LEN (psf->u.ibuf) ; + while (len > 0) + { readcount = (len >= bufferlen) ? bufferlen : len ; + count = sds_read (psf, psds, iptr, readcount) ; + for (k = 0 ; k < readcount ; k++) + ptr [total + k] = normfact * iptr [k] ; + total += count ; + len -= readcount ; + } ; + + return total ; +} /* sds_read_f */ + +static sf_count_t +sds_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len) +{ SDS_PRIVATE *psds ; + int *iptr ; + int k, bufferlen, readcount, count ; + sf_count_t total = 0 ; + double normfact ; + + if (psf->codec_data == NULL) + return 0 ; + psds = (SDS_PRIVATE*) psf->codec_data ; + + if (psf->norm_double == SF_TRUE) + normfact = 1.0 / 0x80000000 ; + else + normfact = 1.0 / (1 << psds->bitwidth) ; + + iptr = psf->u.ibuf ; + bufferlen = ARRAY_LEN (psf->u.ibuf) ; + while (len > 0) + { readcount = (len >= bufferlen) ? bufferlen : len ; + count = sds_read (psf, psds, iptr, readcount) ; + for (k = 0 ; k < readcount ; k++) + ptr [total + k] = normfact * iptr [k] ; + total += count ; + len -= readcount ; + } ; + + return total ; +} /* sds_read_d */ + +static int +sds_read (SF_PRIVATE *psf, SDS_PRIVATE *psds, int *ptr, int len) +{ int count, total = 0 ; + + while (total < len) + { if (psds->read_block * psds->samplesperblock >= psds->frames) + { memset (&(ptr [total]), 0, (len - total) * sizeof (int)) ; + return total ; + } ; + + if (psds->read_count >= psds->samplesperblock) + psds->reader (psf, psds) ; + + count = (psds->samplesperblock - psds->read_count) ; + count = (len - total > count) ? count : len - total ; + + memcpy (&(ptr [total]), &(psds->read_samples [psds->read_count]), count * sizeof (int)) ; + total += count ; + psds->read_count += count ; + } ; + + return total ; +} /* sds_read */ + +/*============================================================================== +*/ + +static sf_count_t +sds_seek (SF_PRIVATE *psf, int mode, sf_count_t seek_from_start) +{ SDS_PRIVATE *psds ; + sf_count_t file_offset ; + int newblock, newsample ; + + if ((psds = psf->codec_data) == NULL) + { psf->error = SFE_INTERNAL ; + return PSF_SEEK_ERROR ; + } ; + + if (psf->datalength < 0 || psf->dataoffset < 0) + { psf->error = SFE_BAD_SEEK ; + return PSF_SEEK_ERROR ; + } ; + + if (seek_from_start < 0 || seek_from_start > psf->sf.frames) + { psf->error = SFE_BAD_SEEK ; + return PSF_SEEK_ERROR ; + } ; + + if (mode == SFM_READ && psds->write_count > 0) + psds->writer (psf, psds) ; + + newblock = seek_from_start / psds->samplesperblock ; + newsample = seek_from_start % psds->samplesperblock ; + + switch (mode) + { case SFM_READ : + if (newblock > psds->total_blocks) + { psf->error = SFE_BAD_SEEK ; + return PSF_SEEK_ERROR ; + } ; + + file_offset = psf->dataoffset + newblock * SDS_BLOCK_SIZE ; + + if (psf_fseek (psf, file_offset, SEEK_SET) != file_offset) + { psf->error = SFE_SEEK_FAILED ; + return PSF_SEEK_ERROR ; + } ; + + psds->read_block = newblock ; + psds->reader (psf, psds) ; + psds->read_count = newsample ; + break ; + + case SFM_WRITE : + if (newblock > psds->total_blocks) + { psf->error = SFE_BAD_SEEK ; + return PSF_SEEK_ERROR ; + } ; + + file_offset = psf->dataoffset + newblock * SDS_BLOCK_SIZE ; + + if (psf_fseek (psf, file_offset, SEEK_SET) != file_offset) + { psf->error = SFE_SEEK_FAILED ; + return PSF_SEEK_ERROR ; + } ; + + psds->write_block = newblock ; + psds->reader (psf, psds) ; + psds->write_count = newsample ; + break ; + + default : + psf->error = SFE_BAD_SEEK ; + return PSF_SEEK_ERROR ; + break ; + } ; + + return seek_from_start ; +} /* sds_seek */ + +/*============================================================================== +*/ + +static int +sds_2byte_write (SF_PRIVATE *psf, SDS_PRIVATE *psds) +{ unsigned char *ucptr, checksum ; + unsigned int sample ; + int k ; + + psds->write_data [0] = 0xF0 ; + psds->write_data [1] = 0x7E ; + psds->write_data [2] = 0 ; /* Channel number */ + psds->write_data [3] = psds->write_block & 0x7F ; /* Packet number */ + + ucptr = psds->write_data + 5 ; + for (k = 0 ; k < 120 ; k += 2) + { sample = psds->write_samples [k / 2] ; + sample += 0x80000000 ; + ucptr [k] = (sample >> 25) & 0x7F ; + ucptr [k + 1] = (sample >> 18) & 0x7F ; + } ; + + checksum = psds->write_data [1] ; + for (k = 2 ; k < SDS_BLOCK_SIZE - 3 ; k ++) + checksum ^= psds->write_data [k] ; + checksum &= 0x7F ; + + psds->write_data [SDS_BLOCK_SIZE - 2] = checksum ; + psds->write_data [SDS_BLOCK_SIZE - 1] = 0xF7 ; + + if ((k = psf_fwrite (psds->write_data, 1, SDS_BLOCK_SIZE, psf)) != SDS_BLOCK_SIZE) + psf_log_printf (psf, "*** Warning : psf_fwrite (%d != %d).\n", k, SDS_BLOCK_SIZE) ; + + psds->write_block ++ ; + psds->write_count = 0 ; + + if (psds->write_block > psds->total_blocks) + psds->total_blocks = psds->write_block ; + psds->frames = psds->total_blocks * psds->samplesperblock ; + + return 1 ; +} /* sds_2byte_write */ + +static int +sds_3byte_write (SF_PRIVATE *psf, SDS_PRIVATE *psds) +{ unsigned char *ucptr, checksum ; + unsigned int sample ; + int k ; + + psds->write_data [0] = 0xF0 ; + psds->write_data [1] = 0x7E ; + psds->write_data [2] = 0 ; /* Channel number */ + psds->write_data [3] = psds->write_block & 0x7F ; /* Packet number */ + + ucptr = psds->write_data + 5 ; + for (k = 0 ; k < 120 ; k += 3) + { sample = psds->write_samples [k / 3] ; + sample += 0x80000000 ; + ucptr [k] = (sample >> 25) & 0x7F ; + ucptr [k + 1] = (sample >> 18) & 0x7F ; + ucptr [k + 2] = (sample >> 11) & 0x7F ; + } ; + + checksum = psds->write_data [1] ; + for (k = 2 ; k < SDS_BLOCK_SIZE - 3 ; k ++) + checksum ^= psds->write_data [k] ; + checksum &= 0x7F ; + + psds->write_data [SDS_BLOCK_SIZE - 2] = checksum ; + psds->write_data [SDS_BLOCK_SIZE - 1] = 0xF7 ; + + if ((k = psf_fwrite (psds->write_data, 1, SDS_BLOCK_SIZE, psf)) != SDS_BLOCK_SIZE) + psf_log_printf (psf, "*** Warning : psf_fwrite (%d != %d).\n", k, SDS_BLOCK_SIZE) ; + + psds->write_block ++ ; + psds->write_count = 0 ; + + if (psds->write_block > psds->total_blocks) + psds->total_blocks = psds->write_block ; + psds->frames = psds->total_blocks * psds->samplesperblock ; + + return 1 ; +} /* sds_3byte_write */ + +static int +sds_4byte_write (SF_PRIVATE *psf, SDS_PRIVATE *psds) +{ unsigned char *ucptr, checksum ; + unsigned int sample ; + int k ; + + psds->write_data [0] = 0xF0 ; + psds->write_data [1] = 0x7E ; + psds->write_data [2] = 0 ; /* Channel number */ + psds->write_data [3] = psds->write_block & 0x7F ; /* Packet number */ + + ucptr = psds->write_data + 5 ; + for (k = 0 ; k < 120 ; k += 4) + { sample = psds->write_samples [k / 4] ; + sample += 0x80000000 ; + ucptr [k] = (sample >> 25) & 0x7F ; + ucptr [k + 1] = (sample >> 18) & 0x7F ; + ucptr [k + 2] = (sample >> 11) & 0x7F ; + ucptr [k + 3] = (sample >> 4) & 0x7F ; + } ; + + checksum = psds->write_data [1] ; + for (k = 2 ; k < SDS_BLOCK_SIZE - 3 ; k ++) + checksum ^= psds->write_data [k] ; + checksum &= 0x7F ; + + psds->write_data [SDS_BLOCK_SIZE - 2] = checksum ; + psds->write_data [SDS_BLOCK_SIZE - 1] = 0xF7 ; + + if ((k = psf_fwrite (psds->write_data, 1, SDS_BLOCK_SIZE, psf)) != SDS_BLOCK_SIZE) + psf_log_printf (psf, "*** Warning : psf_fwrite (%d != %d).\n", k, SDS_BLOCK_SIZE) ; + + psds->write_block ++ ; + psds->write_count = 0 ; + + if (psds->write_block > psds->total_blocks) + psds->total_blocks = psds->write_block ; + psds->frames = psds->total_blocks * psds->samplesperblock ; + + return 1 ; +} /* sds_4byte_write */ + +static sf_count_t +sds_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len) +{ SDS_PRIVATE *psds ; + int *iptr ; + int k, bufferlen, writecount, count ; + sf_count_t total = 0 ; + + if (psf->codec_data == NULL) + return 0 ; + psds = (SDS_PRIVATE*) psf->codec_data ; + + iptr = psf->u.ibuf ; + bufferlen = ARRAY_LEN (psf->u.ibuf) ; + while (len > 0) + { writecount = (len >= bufferlen) ? bufferlen : len ; + for (k = 0 ; k < writecount ; k++) + iptr [k] = ptr [total + k] << 16 ; + count = sds_write (psf, psds, iptr, writecount) ; + total += count ; + len -= writecount ; + } ; + + return total ; +} /* sds_write_s */ + +static sf_count_t +sds_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t len) +{ SDS_PRIVATE *psds ; + int total ; + + if (psf->codec_data == NULL) + return 0 ; + psds = (SDS_PRIVATE*) psf->codec_data ; + + total = sds_write (psf, psds, ptr, len) ; + + return total ; +} /* sds_write_i */ + +static sf_count_t +sds_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len) +{ SDS_PRIVATE *psds ; + int *iptr ; + int k, bufferlen, writecount, count ; + sf_count_t total = 0 ; + float normfact ; + + if (psf->codec_data == NULL) + return 0 ; + psds = (SDS_PRIVATE*) psf->codec_data ; + + if (psf->norm_float == SF_TRUE) + normfact = 1.0 * 0x80000000 ; + else + normfact = 1.0 * (1 << psds->bitwidth) ; + + iptr = psf->u.ibuf ; + bufferlen = ARRAY_LEN (psf->u.ibuf) ; + while (len > 0) + { writecount = (len >= bufferlen) ? bufferlen : len ; + for (k = 0 ; k < writecount ; k++) + iptr [k] = normfact * ptr [total + k] ; + count = sds_write (psf, psds, iptr, writecount) ; + total += count ; + len -= writecount ; + } ; + + return total ; +} /* sds_write_f */ + +static sf_count_t +sds_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len) +{ SDS_PRIVATE *psds ; + int *iptr ; + int k, bufferlen, writecount, count ; + sf_count_t total = 0 ; + double normfact ; + + if (psf->codec_data == NULL) + return 0 ; + psds = (SDS_PRIVATE*) psf->codec_data ; + + if (psf->norm_double == SF_TRUE) + normfact = 1.0 * 0x80000000 ; + else + normfact = 1.0 * (1 << psds->bitwidth) ; + + iptr = psf->u.ibuf ; + bufferlen = ARRAY_LEN (psf->u.ibuf) ; + while (len > 0) + { writecount = (len >= bufferlen) ? bufferlen : len ; + for (k = 0 ; k < writecount ; k++) + iptr [k] = normfact * ptr [total + k] ; + count = sds_write (psf, psds, iptr, writecount) ; + total += count ; + len -= writecount ; + } ; + + return total ; +} /* sds_write_d */ + +static int +sds_write (SF_PRIVATE *psf, SDS_PRIVATE *psds, const int *ptr, int len) +{ int count, total = 0 ; + + while (total < len) + { count = psds->samplesperblock - psds->write_count ; + if (count > len - total) + count = len - total ; + + memcpy (&(psds->write_samples [psds->write_count]), &(ptr [total]), count * sizeof (int)) ; + total += count ; + psds->write_count += count ; + + if (psds->write_count >= psds->samplesperblock) + psds->writer (psf, psds) ; + } ; + + return total ; +} /* sds_write */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: d5d26aa3-368c-4ca6-bb85-377e5a2578cc +*/ diff --git a/src/sf_unistd.h b/src/sf_unistd.h new file mode 100644 index 00000000..f24ae67e --- /dev/null +++ b/src/sf_unistd.h @@ -0,0 +1,67 @@ +/* +** Copyright (C) 2002-2004 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* Some defines that microsoft 'forgot' to implement. */ + +#ifndef S_IRWXU +#define S_IRWXU 0000700 /* rwx, owner */ +#endif + +#ifndef S_IRUSR +#define S_IRUSR 0000400 /* read permission, owner */ +#endif + +#ifndef S_IWUSR +#define S_IWUSR 0000200 /* write permission, owner */ +#endif + +#ifndef S_IXUSR +#define S_IXUSR 0000100 /* execute/search permission, owner */ +#endif + +#define S_IRWXG 0000070 /* rwx, group */ +#define S_IRGRP 0000040 /* read permission, group */ +#define S_IWGRP 0000020 /* write permission, grougroup */ +#define S_IXGRP 0000010 /* execute/search permission, group */ + +#define S_IRWXO 0000007 /* rwx, other */ +#define S_IROTH 0000004 /* read permission, other */ +#define S_IWOTH 0000002 /* write permission, other */ +#define S_IXOTH 0000001 /* execute/search permission, other */ + +#ifndef S_ISFIFO +#define S_ISFIFO(mode) (((mode) & _S_IFMT) == _S_IFIFO) +#endif + +#ifndef S_ISREG +#define S_ISREG(mode) (((mode) & _S_IFREG) == _S_IFREG) +#endif + +/* +** Don't know if these are still needed. +** +** #define _IFMT _S_IFMT +** #define _IFREG _S_IFREG +*/ +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 253aea6d-6299-46fd-8d06-bc5f6224c8fe +*/ diff --git a/src/sfconfig.h b/src/sfconfig.h new file mode 100644 index 00000000..f12df6d7 --- /dev/null +++ b/src/sfconfig.h @@ -0,0 +1,108 @@ +/* +** Copyright (C) 2005 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* +** Autoconf leaves many config parameters undefined. +** Here we change then from being undefined to defining them to 0. +** This allows things like: +** +** #if HAVE_CONFIG_PARAM +** +** and +** +** if (HAVE_CONFIG_PARAM) +** do_something () ; +*/ + +#ifndef SFCONFIG_H +#define SFCONFIG_H + +/* Include the Autoconf generated file. */ +#include "config.h" + +/* Now fiddle the values. */ + +#ifndef HAVE_ALSA_ASOUNDLIB_H +#define HAVE_ALSA_ASOUNDLIB_H 0 +#endif + +#ifndef HAVE_BYTESWAP_H +#define HAVE_BYTESWAP_H 0 +#endif + +#ifndef HAVE_DECL_S_IRGRP +#define HAVE_DECL_S_IRGRP 0 +#endif + +#ifndef HAVE_ENDIAN_H +#define HAVE_ENDIAN_H 0 +#endif + +#ifndef HAVE_FSYNC +#define HAVE_FSYNC 0 +#endif + +#ifndef HAVE_LOCALE_H +#define HAVE_LOCALE_H 0 +#endif + +#ifndef HAVE_LRINT +#define HAVE_LRINT 0 +#endif + +#ifndef HAVE_LRINTF +#define HAVE_LRINTF 0 +#endif + +#ifndef HAVE_MMAP +#define HAVE_MMAP 0 +#endif + +#ifndef HAVE_PREAD +#define HAVE_PREAD 0 +#endif + +#ifndef HAVE_PWRITE +#define HAVE_PWRITE 0 +#endif + +#ifndef HAVE_SETLOCALE +#define HAVE_SETLOCALE 0 +#endif + +#ifndef HAVE_SQLITE3 +#define HAVE_SQLITE3 0 +#endif + +#ifndef HAVE_STDINT_H +#define HAVE_STDINT_H 0 +#endif + +#ifndef HAVE_UNISTD_H +#define HAVE_UNISTD_H 0 +#endif + +#endif + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 2df2316e-8f9d-4860-bba7-f3c16c63eed3 +*/ diff --git a/src/sfendian.h b/src/sfendian.h new file mode 100644 index 00000000..651616b8 --- /dev/null +++ b/src/sfendian.h @@ -0,0 +1,261 @@ +/* +** Copyright (C) 1999-2006 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef SFENDIAN_INCLUDED +#define SFENDIAN_INCLUDED + +#include "sfconfig.h" + +#if HAVE_STDINT_H +#include +#elif HAVE_INTTYPES_H +#include +#endif + +#if (defined (SIZEOF_INT64_T) && (SIZEOF_INT64_T == 8)) +/* Good, we have int64_t. */ +#elif (defined (SIZEOF_LONG_LONG) && (SIZEOF_LONG_LONG == 8)) +typedef long long int64_t ; +#elif (defined (SIZEOF_LONG) && (SIZEOF_LONG == 8)) +typedef long int64_t ; +#elif (defined (WIN32) || defined (_WIN32)) +typedef __int64 int64_t ; +#else +#error "No 64 bit integer type." +#endif + +#if HAVE_BYTESWAP_H + +#include + +#define ENDSWAP_SHORT(x) ((short) bswap_16 (x)) +#define ENDSWAP_INT(x) ((int) bswap_32 (x)) + +#else + +#define ENDSWAP_SHORT(x) ((((x) >> 8) & 0xFF) + (((x) & 0xFF) << 8)) +#define ENDSWAP_INT(x) ((((x) >> 24) & 0xFF) + (((x) >> 8) & 0xFF00) + (((x) & 0xFF00) << 8) + (((x) & 0xFF) << 24)) + +#endif + +/* +** Many file types (ie WAV, AIFF) use sets of four consecutive bytes as a +** marker indicating different sections of the file. +** The following MAKE_MARKER macro allows th creation of integer constants +** for these markers. +*/ + +#if (CPU_IS_LITTLE_ENDIAN == 1) + #define MAKE_MARKER(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((d) << 24)) +#elif (CPU_IS_BIG_ENDIAN == 1) + #define MAKE_MARKER(a,b,c,d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) +#else + #error "Target CPU endian-ness unknown. May need to hand edit src/sfconfig.h" +#endif + +/* +** Macros to handle reading of data of a specific endian-ness into host endian +** shorts and ints. The single input is an unsigned char* pointer to the start +** of the object. There are two versions of each macro as we need to deal with +** both big and little endian CPUs. +*/ + +#if (CPU_IS_LITTLE_ENDIAN == 1) + #define LES2H_SHORT(x) (x) + #define LEI2H_INT(x) (x) + + #define BES2H_SHORT(x) ENDSWAP_SHORT (x) + #define BEI2H_INT(x) ENDSWAP_INT (x) + + #define H2BE_SHORT(x) ENDSWAP_SHORT (x) + #define H2BE_INT(x) ENDSWAP_INT (x) + +#elif (CPU_IS_BIG_ENDIAN == 1) + #define LES2H_SHORT(x) ENDSWAP_SHORT (x) + #define LEI2H_INT(x) ENDSWAP_INT (x) + + #define BES2H_SHORT(x) (x) + #define BEI2H_INT(x) (x) + + #define H2LE_SHORT(x) ENDSWAP_SHORT (x) + #define H2LE_INT(x) ENDSWAP_INT (x) + +#else + #error "Target CPU endian-ness unknown. May need to hand edit src/sfconfig.h" +#endif + +#define LET2H_SHORT_PTR(x) ((x) [1] + ((x) [2] << 8)) +#define LET2H_INT_PTR(x) (((x) [0] << 8) + ((x) [1] << 16) + ((x) [2] << 24)) + +#define BET2H_SHORT_PTR(x) (((x) [0] << 8) + (x) [1]) +#define BET2H_INT_PTR(x) (((x) [0] << 24) + ((x) [1] << 16) + ((x) [2] << 8)) + +/*----------------------------------------------------------------------------------------------- +** Generic functions for performing endian swapping on integer arrays. +*/ + +static inline void +endswap_short_array (short *ptr, int len) +{ short temp ; + + while (--len >= 0) + { temp = ptr [len] ; + ptr [len] = ENDSWAP_SHORT (temp) ; + } ; +} /* endswap_short_array */ + +static inline void +endswap_short_copy (short *dest, const short *src, int len) +{ + while (--len >= 0) + { dest [len] = ENDSWAP_SHORT (src [len]) ; + } ; +} /* endswap_short_copy */ + +static inline void +endswap_int_array (int *ptr, int len) +{ int temp ; + + while (--len >= 0) + { temp = ptr [len] ; + ptr [len] = ENDSWAP_INT (temp) ; + } ; +} /* endswap_int_array */ + +static inline void +endswap_int_copy (int *dest, const int *src, int len) +{ + while (--len >= 0) + { dest [len] = ENDSWAP_INT (src [len]) ; + } ; +} /* endswap_int_copy */ + +/*======================================================================================== +*/ + +#if (HAVE_BYTESWAP_H && defined (SIZEOF_INT64_T) && (SIZEOF_INT64_T == 8)) + +static inline void +endswap_int64_t_array (int64_t *ptr, int len) +{ int64_t value ; + + while (--len >= 0) + { value = ptr [len] ; + ptr [len] = bswap_64 (value) ; + } ; +} /* endswap_int64_t_array */ + +static inline void +endswap_int64_t_copy (int64_t *dest, const int64_t *src, int len) +{ int64_t value ; + + while (--len >= 0) + { value = src [len] ; + dest [len] = bswap_64 (value) ; + } ; +} /* endswap_int64_t_copy */ + +#else + +static inline void +endswap_int64_t_array (int64_t *ptr, int len) +{ unsigned char *ucptr, temp ; + + ucptr = (unsigned char *) ptr ; + ucptr += 8 * len ; + while (--len >= 0) + { ucptr -= 8 ; + + temp = ucptr [0] ; + ucptr [0] = ucptr [7] ; + ucptr [7] = temp ; + + temp = ucptr [1] ; + ucptr [1] = ucptr [6] ; + ucptr [6] = temp ; + + temp = ucptr [2] ; + ucptr [2] = ucptr [5] ; + ucptr [5] = temp ; + + temp = ucptr [3] ; + ucptr [3] = ucptr [4] ; + ucptr [4] = temp ; + } ; +} /* endswap_int64_t_array */ + +static inline void +endswap_int64_t_copy (int64_t *dest, const int64_t *src, int len) +{ const unsigned char *psrc ; + unsigned char *pdest ; + + if (dest == src) + { endswap_int64_t_array (dest, len) ; + return ; + } ; + + psrc = ((const unsigned char *) src) + 8 * len ; + pdest = ((unsigned char *) dest) + 8 * len ; + while (--len >= 0) + { psrc -= 8 ; + pdest -= 8 ; + + pdest [0] = psrc [7] ; + pdest [2] = psrc [5] ; + pdest [4] = psrc [3] ; + pdest [6] = psrc [1] ; + pdest [7] = psrc [0] ; + pdest [1] = psrc [6] ; + pdest [3] = psrc [4] ; + pdest [5] = psrc [2] ; + } ; +} /* endswap_int64_t_copy */ + +#endif + +/* A couple of wrapper functions. */ + +static inline void +endswap_float_array (float *ptr, int len) +{ endswap_int_array ((void *) ptr, len) ; +} /* endswap_float_array */ + +static inline void +endswap_double_array (double *ptr, int len) +{ endswap_int64_t_array ((void *) ptr, len) ; +} /* endswap_double_array */ + +static inline void +endswap_float_copy (float *dest, const float *src, int len) +{ endswap_int_copy ((int *) dest, (const int *) src, len) ; +} /* endswap_float_copy */ + +static inline void +endswap_double_copy (double *dest, const double *src, int len) +{ endswap_int64_t_copy ((int64_t *) dest, (const int64_t *) src, len) ; +} /* endswap_double_copy */ + +#endif /* SFENDIAN_INCLUDED */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: f0c5cd54-42d3-4237-90ec-11fe24995de7 +*/ diff --git a/src/sndfile.c b/src/sndfile.c new file mode 100644 index 00000000..4ae5bd06 --- /dev/null +++ b/src/sndfile.c @@ -0,0 +1,2756 @@ +/* +** Copyright (C) 1999-2007 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include + +#include "sndfile.h" +#include "sfendian.h" +#include "common.h" + +#define SNDFILE_MAGICK 0x1234C0DE + +#ifdef __APPLE__ + /* + ** Detect if a compile for a universal binary is being attempted and barf if it is. + ** See the URL below for the rationale. + */ + #ifdef __BIG_ENDIAN__ + #if (CPU_IS_LITTLE_ENDIAN == 1) + #error "Universal binary compile detected. See http://www.mega-nerd.com/libsndfile/FAQ.html#Q018" + #endif + #endif + + #ifdef __LITTLE_ENDIAN__ + #if (CPU_IS_BIG_ENDIAN == 1) + #error "Universal binary compile detected. See http://www.mega-nerd.com/libsndfile/FAQ.html#Q018" + #endif + #endif +#endif + + +typedef struct +{ int error ; + const char *str ; +} ErrorStruct ; + +static +ErrorStruct SndfileErrors [] = +{ + /* Public error values and their associated strings. */ + { SF_ERR_NO_ERROR , "No Error." }, + { SF_ERR_UNRECOGNISED_FORMAT , "File opened for read. Format not recognised." }, + { SF_ERR_SYSTEM , "System error." /* Often replaced. */ }, + { SF_ERR_MALFORMED_FILE , "Supported file format but file is malformed." }, + { SF_ERR_UNSUPPORTED_ENCODING , "Supported file format but unsupported encoding." }, + + /* Private error values and their associated strings. */ + { SFE_BAD_FILE , "File does not exist or is not a regular file (possibly a pipe?)." }, + { SFE_BAD_FILE_READ , "File exists but no data could be read." }, + { SFE_OPEN_FAILED , "Could not open file." }, + { SFE_BAD_SNDFILE_PTR , "Not a valid SNDFILE* pointer." }, + { SFE_BAD_SF_INFO_PTR , "NULL SF_INFO pointer passed to libsndfile." }, + { SFE_BAD_SF_INCOMPLETE , "SF_PRIVATE struct incomplete and end of header parsing." }, + { SFE_BAD_FILE_PTR , "Bad FILE pointer." }, + { SFE_BAD_INT_PTR , "Internal error, Bad pointer." }, + { SFE_BAD_STAT_SIZE , "Error : software was misconfigured at compile time (sizeof statbuf.st_size)." }, + + { SFE_MALLOC_FAILED , "Internal malloc () failed." }, + { SFE_UNIMPLEMENTED , "File contains data in an unimplemented format." }, + { SFE_BAD_READ_ALIGN , "Attempt to read a non-integer number of channels." }, + { SFE_BAD_WRITE_ALIGN , "Attempt to write a non-integer number of channels." }, + { SFE_UNKNOWN_FORMAT , "File contains data in an unknown format." }, + { SFE_NOT_READMODE , "Read attempted on file currently open for write." }, + { SFE_NOT_WRITEMODE , "Write attempted on file currently open for read." }, + { SFE_BAD_MODE_RW , "This file format does not support read/write mode." }, + { SFE_BAD_SF_INFO , "Internal error : SF_INFO struct incomplete." }, + { SFE_BAD_OFFSET , "Error : supplied offset beyond end of file." }, + { SFE_NO_EMBED_SUPPORT , "Error : embedding not supported for this file format." }, + { SFE_NO_EMBEDDED_RDWR , "Error : cannot open embedded file read/write." }, + { SFE_NO_PIPE_WRITE , "Error : this file format does not support pipe write." }, + { SFE_BAD_RDWR_FORMAT , "Error : File format cannot be opened for RDWR." }, + { SFE_BAD_VIRTUAL_IO , "Error : bad pointer on SF_VIRTUAL_IO struct." }, + + { SFE_INTERLEAVE_MODE , "Attempt to write to file with non-interleaved data." }, + { SFE_INTERLEAVE_SEEK , "Bad karma in seek during interleave read operation." }, + { SFE_INTERLEAVE_READ , "Bad karma in read during interleave read operation." }, + + { SFE_INTERNAL , "Unspecified internal error." }, + { SFE_BAD_CONTROL_CMD , "Bad command passed to function sf_command()." }, + { SFE_BAD_ENDIAN , "Bad endian-ness. Try default endian-ness" }, + { SFE_CHANNEL_COUNT , "Too many channels specified." }, + + { SFE_BAD_SEEK , "Internal psf_fseek() failed." }, + { SFE_NOT_SEEKABLE , "Seek attempted on unseekable file type." }, + { SFE_AMBIGUOUS_SEEK , "Error : combination of file open mode and seek command is ambiguous." }, + { SFE_WRONG_SEEK , "Error : invalid seek parameters." }, + { SFE_SEEK_FAILED , "Error : parameters OK, but psf_seek() failed." }, + + { SFE_BAD_OPEN_MODE , "Error : bad mode parameter for file open." }, + { SFE_OPEN_PIPE_RDWR , "Error : attempt toopen a pipe in read/write mode." }, + { SFE_RDWR_POSITION , "Error on RDWR position (cryptic)." }, + { SFE_RDWR_BAD_HEADER , "Error : Cannot open file in read/write mode due to string data in header." }, + + { SFE_STR_NO_SUPPORT , "Error : File type does not support string data." }, + { SFE_STR_NOT_WRITE , "Error : Trying to set a string when file is not in write mode." }, + { SFE_STR_MAX_DATA , "Error : Maximum string data storage reached." }, + { SFE_STR_MAX_COUNT , "Error : Maximum string data count reached." }, + { SFE_STR_BAD_TYPE , "Error : Bad string data type." }, + { SFE_STR_NO_ADD_END , "Error : file type does not support strings added at end of file." }, + { SFE_STR_BAD_STRING , "Error : bad string." }, + { SFE_STR_WEIRD , "Error : Weird string error." }, + + { SFE_WAV_NO_RIFF , "Error in WAV file. No 'RIFF' chunk marker." }, + { SFE_WAV_NO_WAVE , "Error in WAV file. No 'WAVE' chunk marker." }, + { SFE_WAV_NO_FMT , "Error in WAV file. No 'fmt ' chunk marker." }, + { SFE_WAV_FMT_SHORT , "Error in WAV file. Short 'fmt ' chunk." }, + + { SFE_WAV_BAD_FACT , "Error in WAV file. 'fact' chunk out of place." }, + { SFE_WAV_BAD_PEAK , "Error in WAV file. Bad 'PEAK' chunk." }, + { SFE_WAV_PEAK_B4_FMT , "Error in WAV file. 'PEAK' chunk found before 'fmt ' chunk." }, + + { SFE_WAV_BAD_FORMAT , "Error in WAV file. Errors in 'fmt ' chunk." }, + { SFE_WAV_BAD_BLOCKALIGN , "Error in WAV file. Block alignment in 'fmt ' chunk is incorrect." }, + { SFE_WAV_NO_DATA , "Error in WAV file. No 'data' chunk marker." }, + { SFE_WAV_BAD_LIST , "Error in WAV file. Malformed LIST chunk." }, + { SFE_WAV_UNKNOWN_CHUNK , "Error in WAV file. File contains an unknown chunk marker." }, + { SFE_WAV_WVPK_DATA , "Error in WAV file. Data is in WAVPACK format." }, + + { SFE_WAV_ADPCM_NOT4BIT , "Error in ADPCM WAV file. Invalid bit width." }, + { SFE_WAV_ADPCM_CHANNELS , "Error in ADPCM WAV file. Invalid number of channels." }, + { SFE_WAV_GSM610_FORMAT , "Error in GSM610 WAV file. Invalid format chunk." }, + + { SFE_AIFF_NO_FORM , "Error in AIFF file, bad 'FORM' marker." }, + { SFE_AIFF_AIFF_NO_FORM , "Error in AIFF file, 'AIFF' marker without 'FORM'." }, + { SFE_AIFF_COMM_NO_FORM , "Error in AIFF file, 'COMM' marker without 'FORM'." }, + { SFE_AIFF_SSND_NO_COMM , "Error in AIFF file, 'SSND' marker without 'COMM'." }, + { SFE_AIFF_UNKNOWN_CHUNK , "Error in AIFF file, unknown chunk." }, + { SFE_AIFF_COMM_CHUNK_SIZE, "Error in AIFF file, bad 'COMM' chunk size." }, + { SFE_AIFF_BAD_COMM_CHUNK , "Error in AIFF file, bad 'COMM' chunk." }, + { SFE_AIFF_PEAK_B4_COMM , "Error in AIFF file. 'PEAK' chunk found before 'COMM' chunk." }, + { SFE_AIFF_BAD_PEAK , "Error in AIFF file. Bad 'PEAK' chunk." }, + { SFE_AIFF_NO_SSND , "Error in AIFF file, bad 'SSND' chunk." }, + { SFE_AIFF_NO_DATA , "Error in AIFF file, no sound data." }, + { SFE_AIFF_RW_SSND_NOT_LAST, "Error in AIFF file, RDWR only possible if SSND chunk at end of file." }, + + { SFE_AU_UNKNOWN_FORMAT , "Error in AU file, unknown format." }, + { SFE_AU_NO_DOTSND , "Error in AU file, missing '.snd' or 'dns.' marker." }, + { SFE_AU_EMBED_BAD_LEN , "Embedded AU file with unknown length." }, + + { SFE_RAW_READ_BAD_SPEC , "Error while opening RAW file for read. Must specify format and channels.\n" + "Possibly trying to open unsupported format." + }, + { SFE_RAW_BAD_BITWIDTH , "Error. RAW file bitwidth must be a multiple of 8." }, + { SFE_RAW_BAD_FORMAT , "Error. Bad format field in SF_INFO struct when openning a RAW file for read." }, + + { SFE_PAF_NO_MARKER , "Error in PAF file, no marker." }, + { SFE_PAF_VERSION , "Error in PAF file, bad version." }, + { SFE_PAF_UNKNOWN_FORMAT , "Error in PAF file, unknown format." }, + { SFE_PAF_SHORT_HEADER , "Error in PAF file. File shorter than minimal header." }, + + { SFE_SVX_NO_FORM , "Error in 8SVX / 16SV file, no 'FORM' marker." }, + { SFE_SVX_NO_BODY , "Error in 8SVX / 16SV file, no 'BODY' marker." }, + { SFE_SVX_NO_DATA , "Error in 8SVX / 16SV file, no sound data." }, + { SFE_SVX_BAD_COMP , "Error in 8SVX / 16SV file, unsupported compression format." }, + { SFE_SVX_BAD_NAME_LENGTH , "Error in 8SVX / 16SV file, NAME chunk too long." }, + + { SFE_NIST_BAD_HEADER , "Error in NIST file, bad header." }, + { SFE_NIST_CRLF_CONVERISON, "Error : NIST file damaged by Windows CR -> CRLF conversion process." }, + { SFE_NIST_BAD_ENCODING , "Error in NIST file, unsupported compression format." }, + + { SFE_VOC_NO_CREATIVE , "Error in VOC file, no 'Creative Voice File' marker." }, + { SFE_VOC_BAD_FORMAT , "Error in VOC file, bad format." }, + { SFE_VOC_BAD_VERSION , "Error in VOC file, bad version number." }, + { SFE_VOC_BAD_MARKER , "Error in VOC file, bad marker in file." }, + { SFE_VOC_BAD_SECTIONS , "Error in VOC file, incompatible VOC sections." }, + { SFE_VOC_MULTI_SAMPLERATE, "Error in VOC file, more than one sample rate defined." }, + { SFE_VOC_MULTI_SECTION , "Unimplemented VOC file feature, file contains multiple sound sections." }, + { SFE_VOC_MULTI_PARAM , "Error in VOC file, file contains multiple bit or channel widths." }, + { SFE_VOC_SECTION_COUNT , "Error in VOC file, too many sections." }, + { SFE_VOC_NO_PIPE , "Error : not able to operate on VOC files over a pipe." }, + + { SFE_IRCAM_NO_MARKER , "Error in IRCAM file, bad IRCAM marker." }, + { SFE_IRCAM_BAD_CHANNELS , "Error in IRCAM file, bad channel count." }, + { SFE_IRCAM_UNKNOWN_FORMAT, "Error in IRCAM file, unknow encoding format." }, + + { SFE_W64_64_BIT , "Error in W64 file, file contains 64 bit offset." }, + + { SFE_W64_NO_RIFF , "Error in W64 file. No 'riff' chunk marker." }, + { SFE_W64_NO_WAVE , "Error in W64 file. No 'wave' chunk marker." }, + { SFE_W64_NO_FMT , "Error in W64 file. No 'fmt ' chunk marker." }, + { SFE_W64_NO_DATA , "Error in W64 file. No 'data' chunk marker." }, + + { SFE_W64_FMT_SHORT , "Error in W64 file. Short 'fmt ' chunk." }, + { SFE_W64_FMT_TOO_BIG , "Error in W64 file. 'fmt ' chunk too large." }, + + { SFE_W64_ADPCM_NOT4BIT , "Error in ADPCM W64 file. Invalid bit width." }, + { SFE_W64_ADPCM_CHANNELS , "Error in ADPCM W64 file. Invalid number of channels." }, + { SFE_W64_GSM610_FORMAT , "Error in GSM610 W64 file. Invalid format chunk." }, + + { SFE_MAT4_BAD_NAME , "Error in MAT4 file. No variable name." }, + { SFE_MAT4_NO_SAMPLERATE , "Error in MAT4 file. No sample rate." }, + { SFE_MAT4_ZERO_CHANNELS , "Error in MAT4 file. Channel count is zero." }, + + { SFE_MAT5_BAD_ENDIAN , "Error in MAT5 file. Not able to determine endian-ness." }, + { SFE_MAT5_NO_BLOCK , "Error in MAT5 file. Bad block structure." }, + { SFE_MAT5_SAMPLE_RATE , "Error in MAT5 file. Not able to determine sample rate." }, + { SFE_MAT5_ZERO_CHANNELS , "Error in MAT5 file. Channel count is zero." }, + + { SFE_PVF_NO_PVF1 , "Error in PVF file. No PVF1 marker." }, + { SFE_PVF_BAD_HEADER , "Error in PVF file. Bad header." }, + { SFE_PVF_BAD_BITWIDTH , "Error in PVF file. Bad bit width." }, + + { SFE_XI_BAD_HEADER , "Error in XI file. Bad header." }, + { SFE_XI_EXCESS_SAMPLES , "Error in XI file. Excess samples in file." }, + { SFE_XI_NO_PIPE , "Error : not able to operate on XI files over a pipe." }, + + { SFE_HTK_NO_PIPE , "Error : not able to operate on HTK files over a pipe." }, + + { SFE_SDS_NOT_SDS , "Error : not an SDS file." }, + { SFE_SDS_BAD_BIT_WIDTH , "Error : bad bit width for SDS file." }, + + { SFE_SD2_FD_DISALLOWED , "Error : cannot open SD2 file without a file name." }, + { SFE_SD2_BAD_DATA_OFFSET , "Error : bad data offset." }, + { SFE_SD2_BAD_MAP_OFFSET , "Error : bad map offset." }, + { SFE_SD2_BAD_DATA_LENGTH , "Error : bad data length." }, + { SFE_SD2_BAD_MAP_LENGTH , "Error : bad map length." }, + { SFE_SD2_BAD_RSRC , "Error : bad resource fork." }, + { SFE_SD2_BAD_SAMPLE_SIZE , "Error : bad sample size." }, + + { SFE_FLAC_BAD_HEADER , "Error : bad flac header." }, + { SFE_FLAC_NEW_DECODER , "Error : problem while creating flac decoder." }, + { SFE_FLAC_INIT_DECODER , "Error : problem while initialization of the flac decoder." }, + { SFE_FLAC_LOST_SYNC , "Error : flac decoder lost sync." }, + { SFE_FLAC_BAD_SAMPLE_RATE, "Error : flac does not support this sample rate." }, + { SFE_FLAC_UNKOWN_ERROR , "Error : unkown error in flac decoder." }, + + { SFE_WVE_NOT_WVE , "Error : not a WVE file." }, + { SFE_WVE_NO_PIPE , "Error : not able to operate on WVE files over a pipe." }, + + { SFE_DWVW_BAD_BITWIDTH , "Error : Bad bit width for DWVW encoding. Must be 12, 16 or 24." }, + { SFE_G72X_NOT_MONO , "Error : G72x encoding does not support more than 1 channel." }, + + { SFE_MAX_ERROR , "Maximum error number." }, + { SFE_MAX_ERROR + 1 , NULL } +} ; + +/*------------------------------------------------------------------------------ +*/ + +static int format_from_extension (SF_PRIVATE *psf) ; +static int guess_file_type (SF_PRIVATE *psf) ; +static int validate_sfinfo (SF_INFO *sfinfo) ; +static int validate_psf (SF_PRIVATE *psf) ; +static void save_header_info (SF_PRIVATE *psf) ; +static void copy_filename (SF_PRIVATE *psf, const char *path) ; +static int psf_close (SF_PRIVATE *psf) ; +static SNDFILE * psf_open_file (SF_PRIVATE *psf, int mode, SF_INFO *sfinfo) ; + +static int try_resource_fork (SF_PRIVATE * psf, int mode) ; + +/*------------------------------------------------------------------------------ +** Private (static) variables. +*/ + +static int sf_errno = 0 ; +static char sf_logbuffer [SF_BUFFER_LEN] = { 0 } ; +static char sf_syserr [SF_SYSERR_LEN] = { 0 } ; + +/*------------------------------------------------------------------------------ +*/ + +#define VALIDATE_SNDFILE_AND_ASSIGN_PSF(a,b,c) \ + { if ((a) == NULL) \ + { sf_errno = SFE_BAD_SNDFILE_PTR ; \ + return 0 ; \ + } ; \ + (b) = (SF_PRIVATE*) (a) ; \ + if ((b)->virtual_io == SF_FALSE && \ + psf_file_valid (b) == 0) \ + { (b)->error = SFE_BAD_FILE_PTR ; \ + return 0 ; \ + } ; \ + if ((b)->Magick != SNDFILE_MAGICK) \ + { (b)->error = SFE_BAD_SNDFILE_PTR ; \ + return 0 ; \ + } ; \ + if (c) (b)->error = 0 ; \ + } + +/*------------------------------------------------------------------------------ +** Public functions. +*/ + +SNDFILE* +sf_open (const char *path, int mode, SF_INFO *sfinfo) +{ SF_PRIVATE *psf ; + + if ((psf = calloc (1, sizeof (SF_PRIVATE))) == NULL) + { sf_errno = SFE_MALLOC_FAILED ; + return NULL ; + } ; + + memset (psf, 0, sizeof (SF_PRIVATE)) ; + psf_init_files (psf) ; + + psf_log_printf (psf, "File : %s\n", path) ; + + copy_filename (psf, path) ; + + if (strcmp (path, "-") == 0) + psf_set_stdio (psf, mode) ; + else + psf_fopen (psf, path, mode) ; + + return psf_open_file (psf, mode, sfinfo) ; +} /* sf_open */ + +SNDFILE* +sf_open_fd (int fd, int mode, SF_INFO *sfinfo, int close_desc) +{ SF_PRIVATE *psf ; + + if ((sfinfo->format & SF_FORMAT_TYPEMASK) == SF_FORMAT_SD2) + { sf_errno = SFE_SD2_FD_DISALLOWED ; + return NULL ; + } ; + + if ((psf = calloc (1, sizeof (SF_PRIVATE))) == NULL) + { sf_errno = SFE_MALLOC_FAILED ; + return NULL ; + } ; + + psf_init_files (psf) ; + + psf_set_file (psf, fd) ; + psf->is_pipe = psf_is_pipe (psf) ; + psf->fileoffset = psf_ftell (psf) ; + + if (! close_desc) + psf->do_not_close_descriptor = SF_TRUE ; + + return psf_open_file (psf, mode, sfinfo) ; +} /* sf_open_fd */ + +SNDFILE* +sf_open_virtual (SF_VIRTUAL_IO *sfvirtual, int mode, SF_INFO *sfinfo, void *user_data) +{ SF_PRIVATE *psf ; + + /* Make sure we have a valid set ot virtual pointers. */ + if (sfvirtual->get_filelen == NULL || sfvirtual->seek == NULL || sfvirtual->tell == NULL) + { sf_errno = SFE_BAD_VIRTUAL_IO ; + LSF_SNPRINTF (sf_logbuffer, sizeof (sf_logbuffer), "Bad vio_get_filelen / vio_seek / vio_tell in SF_VIRTUAL_IO struct.\n") ; + return NULL ; + } ; + + if ((mode == SFM_READ || mode == SFM_RDWR) && sfvirtual->read == NULL) + { sf_errno = SFE_BAD_VIRTUAL_IO ; + LSF_SNPRINTF (sf_logbuffer, sizeof (sf_logbuffer), "Bad vio_read in SF_VIRTUAL_IO struct.\n") ; + return NULL ; + } ; + + if ((mode == SFM_WRITE || mode == SFM_RDWR) && sfvirtual->write == NULL) + { sf_errno = SFE_BAD_VIRTUAL_IO ; + LSF_SNPRINTF (sf_logbuffer, sizeof (sf_logbuffer), "Bad vio_write in SF_VIRTUAL_IO struct.\n") ; + return NULL ; + } ; + + if ((psf = calloc (1, sizeof (SF_PRIVATE))) == NULL) + { sf_errno = SFE_MALLOC_FAILED ; + return NULL ; + } ; + + psf_init_files (psf) ; + + psf->virtual_io = SF_TRUE ; + psf->vio = *sfvirtual ; + psf->vio_user_data = user_data ; + + psf->mode = mode ; + + return psf_open_file (psf, mode, sfinfo) ; +} /* sf_open_virtual */ + +int +sf_close (SNDFILE *sndfile) +{ SF_PRIVATE *psf ; + + VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ; + + return psf_close (psf) ; +} /* sf_close */ + +void +sf_write_sync (SNDFILE *sndfile) +{ SF_PRIVATE *psf ; + + if ((psf = (SF_PRIVATE *) sndfile) == NULL) + return ; + + psf_fsync (psf) ; + + return ; +} /* sf_write_sync */ + +/*============================================================================== +*/ + +const char* +sf_error_number (int errnum) +{ static const char *bad_errnum = + "No error defined for this error number. This is a bug in libsndfile." ; + int k ; + + if (errnum == SFE_MAX_ERROR) + return SndfileErrors [0].str ; + + if (errnum < 0 || errnum > SFE_MAX_ERROR) + { /* This really shouldn't happen in release versions. */ + printf ("Not a valid error number (%d).\n", errnum) ; + return bad_errnum ; + } ; + + for (k = 0 ; SndfileErrors [k].str ; k++) + if (errnum == SndfileErrors [k].error) + return SndfileErrors [k].str ; + + return bad_errnum ; +} /* sf_error_number */ + +const char* +sf_strerror (SNDFILE *sndfile) +{ SF_PRIVATE *psf = NULL ; + int errnum ; + + if (sndfile == NULL) + { errnum = sf_errno ; + if (errnum == SFE_SYSTEM && sf_syserr [0]) + return sf_syserr ; + } + else + { psf = (SF_PRIVATE *) sndfile ; + + if (psf->Magick != SNDFILE_MAGICK) + return "sf_strerror : Bad magic number." ; + + errnum = psf->error ; + + if (errnum == SFE_SYSTEM && psf->syserr [0]) + return psf->syserr ; + } ; + + return sf_error_number (errnum) ; +} /* sf_strerror */ + +/*------------------------------------------------------------------------------ +*/ + +int +sf_error (SNDFILE *sndfile) +{ SF_PRIVATE *psf ; + + if (sndfile == NULL) + { if (sf_errno != 0) + return sf_errno ; + return 0 ; + } ; + + VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 0) ; + + if (psf->error) + return psf->error ; + + return 0 ; +} /* sf_error */ + +/*------------------------------------------------------------------------------ +*/ + +int +sf_perror (SNDFILE *sndfile) +{ SF_PRIVATE *psf ; + int errnum ; + + if (sndfile == NULL) + { errnum = sf_errno ; + } + else + { VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 0) ; + errnum = psf->error ; + } ; + + fprintf (stderr, "%s\n", sf_error_number (errnum)) ; + return SFE_NO_ERROR ; +} /* sf_perror */ + + +/*------------------------------------------------------------------------------ +*/ + +int +sf_error_str (SNDFILE *sndfile, char *str, size_t maxlen) +{ SF_PRIVATE *psf ; + int errnum ; + + if (str == NULL) + return SFE_INTERNAL ; + + if (sndfile == NULL) + errnum = sf_errno ; + else + { VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 0) ; + errnum = psf->error ; + } ; + + LSF_SNPRINTF (str, maxlen, "%s", sf_error_number (errnum)) ; + + return SFE_NO_ERROR ; +} /* sf_error_str */ + +/*============================================================================== +*/ + +int +sf_format_check (const SF_INFO *info) +{ int subformat, endian ; + + subformat = info->format & SF_FORMAT_SUBMASK ; + endian = info->format & SF_FORMAT_ENDMASK ; + + /* This is the place where each file format can check if the suppiled + ** SF_INFO struct is valid. + ** Return 0 on failure, 1 ons success. + */ + + if (info->channels < 1 || info->channels > 256) + return 0 ; + + if (info->samplerate < 0) + return 0 ; + + switch (info->format & SF_FORMAT_TYPEMASK) + { case SF_FORMAT_WAV : + case SF_FORMAT_WAVEX : + /* WAV now allows both endian, RIFF or RIFX (little or big respectively) */ + if (subformat == SF_FORMAT_PCM_U8 || subformat == SF_FORMAT_PCM_16) + return 1 ; + if (subformat == SF_FORMAT_PCM_24 || subformat == SF_FORMAT_PCM_32) + return 1 ; + if ((subformat == SF_FORMAT_IMA_ADPCM || subformat == SF_FORMAT_MS_ADPCM) && info->channels <= 2) + return 1 ; + if (subformat == SF_FORMAT_GSM610 && info->channels == 1) + return 1 ; + if (subformat == SF_FORMAT_G721_32 && info->channels == 1) + return 1 ; + if (subformat == SF_FORMAT_ULAW || subformat == SF_FORMAT_ALAW) + return 1 ; + if (subformat == SF_FORMAT_FLOAT || subformat == SF_FORMAT_DOUBLE) + return 1 ; + break ; + + case SF_FORMAT_AIFF : + /* AIFF does allow both endian-nesses for PCM data.*/ + if (subformat == SF_FORMAT_PCM_16 || subformat == SF_FORMAT_PCM_24 || subformat == SF_FORMAT_PCM_32) + return 1 ; + /* Other encodings. Check for endian-ness. */ + if (endian == SF_ENDIAN_LITTLE || endian == SF_ENDIAN_CPU) + return 0 ; + if (subformat == SF_FORMAT_PCM_U8 || subformat == SF_FORMAT_PCM_S8) + return 1 ; + if (subformat == SF_FORMAT_FLOAT || subformat == SF_FORMAT_DOUBLE) + return 1 ; + if (subformat == SF_FORMAT_ULAW || subformat == SF_FORMAT_ALAW) + return 1 ; + if ((subformat == SF_FORMAT_DWVW_12 || subformat == SF_FORMAT_DWVW_16 || + subformat == SF_FORMAT_DWVW_24) && info-> channels == 1) + return 1 ; + if (subformat == SF_FORMAT_GSM610 && info->channels == 1) + return 1 ; + if (subformat == SF_FORMAT_IMA_ADPCM && (info->channels == 1 || info->channels == 2)) + return 1 ; + break ; + + case SF_FORMAT_AU : + if (subformat == SF_FORMAT_PCM_S8 || subformat == SF_FORMAT_PCM_16) + return 1 ; + if (subformat == SF_FORMAT_PCM_24 || subformat == SF_FORMAT_PCM_32) + return 1 ; + if (subformat == SF_FORMAT_ULAW || subformat == SF_FORMAT_ALAW) + return 1 ; + if (subformat == SF_FORMAT_FLOAT || subformat == SF_FORMAT_DOUBLE) + return 1 ; + if (subformat == SF_FORMAT_G721_32 && info->channels == 1) + return 1 ; + if (subformat == SF_FORMAT_G723_24 && info->channels == 1) + return 1 ; + if (subformat == SF_FORMAT_G723_40 && info->channels == 1) + return 1 ; + break ; + + case SF_FORMAT_CAF : + if (subformat == SF_FORMAT_PCM_S8 || subformat == SF_FORMAT_PCM_16) + return 1 ; + if (subformat == SF_FORMAT_PCM_24 || subformat == SF_FORMAT_PCM_32) + return 1 ; + if (subformat == SF_FORMAT_ULAW || subformat == SF_FORMAT_ALAW) + return 1 ; + if (subformat == SF_FORMAT_FLOAT || subformat == SF_FORMAT_DOUBLE) + return 1 ; + break ; + + case SF_FORMAT_RAW : + if (subformat == SF_FORMAT_PCM_U8 || subformat == SF_FORMAT_PCM_S8 || subformat == SF_FORMAT_PCM_16) + return 1 ; + if (subformat == SF_FORMAT_PCM_24 || subformat == SF_FORMAT_PCM_32) + return 1 ; + if (subformat == SF_FORMAT_FLOAT || subformat == SF_FORMAT_DOUBLE) + return 1 ; + if (subformat == SF_FORMAT_ALAW || subformat == SF_FORMAT_ULAW) + return 1 ; + if ((subformat == SF_FORMAT_DWVW_12 || subformat == SF_FORMAT_DWVW_16 || + subformat == SF_FORMAT_DWVW_24) && info-> channels == 1) + return 1 ; + if (subformat == SF_FORMAT_GSM610 && info->channels == 1) + return 1 ; + if (subformat == SF_FORMAT_VOX_ADPCM && info->channels == 1) + return 1 ; + break ; + + case SF_FORMAT_PAF : + if (subformat == SF_FORMAT_PCM_S8 || subformat == SF_FORMAT_PCM_16) + return 1 ; + if (subformat == SF_FORMAT_PCM_24 || subformat == SF_FORMAT_PCM_32) + return 1 ; + break ; + + case SF_FORMAT_SVX : + /* SVX currently does not support more than one channel for write. + ** Read will allow more than one channel but only allow one here. + */ + if (info->channels != 1) + return 0 ; + /* Always big endian. */ + if (endian == SF_ENDIAN_LITTLE || endian == SF_ENDIAN_CPU) + return 0 ; + + if ((subformat == SF_FORMAT_PCM_S8 || subformat == SF_FORMAT_PCM_16) && info->channels == 1) + return 1 ; + break ; + + case SF_FORMAT_NIST : + if (subformat == SF_FORMAT_PCM_S8 || subformat == SF_FORMAT_PCM_16) + return 1 ; + if (subformat == SF_FORMAT_PCM_24 || subformat == SF_FORMAT_PCM_32) + return 1 ; + if (subformat == SF_FORMAT_ULAW || subformat == SF_FORMAT_ALAW) + return 1 ; + break ; + + case SF_FORMAT_IRCAM : + if (subformat == SF_FORMAT_PCM_16 || subformat == SF_FORMAT_PCM_24 || subformat == SF_FORMAT_PCM_32) + return 1 ; + if (subformat == SF_FORMAT_ULAW || subformat == SF_FORMAT_ALAW || subformat == SF_FORMAT_FLOAT) + return 1 ; + break ; + + case SF_FORMAT_VOC : + /* VOC is strictly little endian. */ + if (endian == SF_ENDIAN_BIG || endian == SF_ENDIAN_CPU) + return 0 ; + if (subformat == SF_FORMAT_PCM_U8 || subformat == SF_FORMAT_PCM_16) + return 1 ; + if (subformat == SF_FORMAT_ULAW || subformat == SF_FORMAT_ALAW) + return 1 ; + break ; + + case SF_FORMAT_W64 : + /* W64 is strictly little endian. */ + if (endian == SF_ENDIAN_BIG || endian == SF_ENDIAN_CPU) + return 0 ; + if (subformat == SF_FORMAT_PCM_U8 || subformat == SF_FORMAT_PCM_16) + return 1 ; + if (subformat == SF_FORMAT_PCM_24 || subformat == SF_FORMAT_PCM_32) + return 1 ; + if ((subformat == SF_FORMAT_IMA_ADPCM || subformat == SF_FORMAT_MS_ADPCM) && info->channels <= 2) + return 1 ; + if (subformat == SF_FORMAT_GSM610 && info->channels == 1) + return 1 ; + if (subformat == SF_FORMAT_ULAW || subformat == SF_FORMAT_ALAW) + return 1 ; + if (subformat == SF_FORMAT_FLOAT || subformat == SF_FORMAT_DOUBLE) + return 1 ; + break ; + + case SF_FORMAT_MAT4 : + if (subformat == SF_FORMAT_PCM_16 || subformat == SF_FORMAT_PCM_32) + return 1 ; + if (subformat == SF_FORMAT_FLOAT || subformat == SF_FORMAT_DOUBLE) + return 1 ; + break ; + + case SF_FORMAT_MAT5 : + if (subformat == SF_FORMAT_PCM_U8 || subformat == SF_FORMAT_PCM_16 || subformat == SF_FORMAT_PCM_32) + return 1 ; + if (subformat == SF_FORMAT_FLOAT || subformat == SF_FORMAT_DOUBLE) + return 1 ; + break ; + + case SF_FORMAT_PVF : + if (subformat == SF_FORMAT_PCM_S8 || subformat == SF_FORMAT_PCM_16 || subformat == SF_FORMAT_PCM_32) + return 1 ; + break ; + + case SF_FORMAT_XI : + if (info->channels != 1) + return 0 ; + if (subformat == SF_FORMAT_DPCM_8 || subformat == SF_FORMAT_DPCM_16) + return 1 ; + break ; + + case SF_FORMAT_HTK : + /* HTK is strictly big endian. */ + if (endian == SF_ENDIAN_LITTLE || endian == SF_ENDIAN_CPU) + return 0 ; + if (info->channels != 1) + return 0 ; + if (subformat == SF_FORMAT_PCM_16) + return 1 ; + break ; + + case SF_FORMAT_SDS : + /* SDS is strictly big endian. */ + if (endian == SF_ENDIAN_LITTLE || endian == SF_ENDIAN_CPU) + return 0 ; + if (info->channels != 1) + return 0 ; + if (subformat == SF_FORMAT_PCM_S8 || subformat == SF_FORMAT_PCM_16 || subformat == SF_FORMAT_PCM_24) + return 1 ; + break ; + + case SF_FORMAT_AVR : + /* SDS is strictly big endian. */ + if (endian == SF_ENDIAN_LITTLE || endian == SF_ENDIAN_CPU) + return 0 ; + if (info->channels > 2) + return 0 ; + if (subformat == SF_FORMAT_PCM_U8 || subformat == SF_FORMAT_PCM_S8 || subformat == SF_FORMAT_PCM_16) + return 1 ; + break ; + + case SF_FORMAT_FLAC : + /* FLAC can't do more than 8 channels. */ + if (info->channels > 8) + return 0 ; + if (subformat == SF_FORMAT_PCM_S8 || subformat == SF_FORMAT_PCM_16 || subformat == SF_FORMAT_PCM_24) + return 1 ; + break ; + + case SF_FORMAT_SD2 : + /* SD2 is strictly big endian. */ + if (endian == SF_ENDIAN_LITTLE || endian == SF_ENDIAN_CPU) + return 0 ; + if (subformat == SF_FORMAT_PCM_S8 || subformat == SF_FORMAT_PCM_16 || subformat == SF_FORMAT_PCM_24) + return 1 ; + break ; + + case SF_FORMAT_WVE : + /* WVE is strictly big endian. */ + if (endian == SF_ENDIAN_BIG || endian == SF_ENDIAN_CPU) + return 0 ; + if (info->channels > 1) + return 0 ; + if (subformat == SF_FORMAT_ALAW) + return 1 ; + break ; + + default : break ; + } ; + + return 0 ; +} /* sf_format_check */ + +/*------------------------------------------------------------------------------ +*/ + +int +sf_command (SNDFILE *sndfile, int command, void *data, int datasize) +{ SF_PRIVATE *psf = NULL ; + int old_value ; + + /* This set of commands do not need the sndfile parameter. */ + switch (command) + { case SFC_GET_LIB_VERSION : + if (data == NULL) + return (psf->error = SFE_BAD_CONTROL_CMD) ; + if (ENABLE_EXPERIMENTAL_CODE) + LSF_SNPRINTF (data, datasize, "%s-%s-exp", PACKAGE_NAME, PACKAGE_VERSION) ; + else + LSF_SNPRINTF (data, datasize, "%s-%s", PACKAGE_NAME, PACKAGE_VERSION) ; + return strlen (data) ; + + case SFC_GET_SIMPLE_FORMAT_COUNT : + if (data == NULL || datasize != SIGNED_SIZEOF (int)) + return (sf_errno = SFE_BAD_CONTROL_CMD) ; + *((int*) data) = psf_get_format_simple_count () ; + return 0 ; + + case SFC_GET_SIMPLE_FORMAT : + if (data == NULL || datasize != SIGNED_SIZEOF (SF_FORMAT_INFO)) + return (sf_errno = SFE_BAD_CONTROL_CMD) ; + return psf_get_format_simple (data) ; + + case SFC_GET_FORMAT_MAJOR_COUNT : + if (data == NULL || datasize != SIGNED_SIZEOF (int)) + return (sf_errno = SFE_BAD_CONTROL_CMD) ; + *((int*) data) = psf_get_format_major_count () ; + return 0 ; + + case SFC_GET_FORMAT_MAJOR : + if (data == NULL || datasize != SIGNED_SIZEOF (SF_FORMAT_INFO)) + return (sf_errno = SFE_BAD_CONTROL_CMD) ; + return psf_get_format_major (data) ; + + case SFC_GET_FORMAT_SUBTYPE_COUNT : + if (data == NULL || datasize != SIGNED_SIZEOF (int)) + return (sf_errno = SFE_BAD_CONTROL_CMD) ; + *((int*) data) = psf_get_format_subtype_count () ; + return 0 ; + + case SFC_GET_FORMAT_SUBTYPE : + if (data == NULL || datasize != SIGNED_SIZEOF (SF_FORMAT_INFO)) + return (sf_errno = SFE_BAD_CONTROL_CMD) ; + return psf_get_format_subtype (data) ; + + case SFC_GET_FORMAT_INFO : + if (data == NULL || datasize != SIGNED_SIZEOF (SF_FORMAT_INFO)) + return (sf_errno = SFE_BAD_CONTROL_CMD) ; + return psf_get_format_info (data) ; + } ; + + if (sndfile == NULL && command == SFC_GET_LOG_INFO) + { if (data == NULL) + return (psf->error = SFE_BAD_CONTROL_CMD) ; + LSF_SNPRINTF (data, datasize, "%s", sf_logbuffer) ; + return strlen (data) ; + } ; + + VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ; + + switch (command) + { case SFC_SET_NORM_FLOAT : + old_value = psf->norm_float ; + psf->norm_float = (datasize) ? SF_TRUE : SF_FALSE ; + return old_value ; + + case SFC_SET_NORM_DOUBLE : + old_value = psf->norm_double ; + psf->norm_double = (datasize) ? SF_TRUE : SF_FALSE ; + return old_value ; + + case SFC_GET_NORM_FLOAT : + return psf->norm_float ; + + case SFC_GET_NORM_DOUBLE : + return psf->norm_double ; + + case SFC_SET_SCALE_FLOAT_INT_READ : + old_value = psf->float_int_mult ; + + psf->float_int_mult = (datasize != 0) ? SF_TRUE : SF_FALSE ; + if (psf->float_int_mult && psf->float_max < 0.0) + psf->float_max = psf_calc_signal_max (psf, SF_FALSE) ; + return old_value ; + + case SFC_SET_ADD_PEAK_CHUNK : + { int format = psf->sf.format & SF_FORMAT_TYPEMASK ; + + /* Only WAV and AIFF support the PEAK chunk. */ + if (format != SF_FORMAT_WAV && format != SF_FORMAT_WAVEX && format != SF_FORMAT_AIFF) + return SF_FALSE ; + + format = psf->sf.format & SF_FORMAT_SUBMASK ; + + /* Only files containg the following data types support the PEAK chunk. */ + if (format != SF_FORMAT_FLOAT && format != SF_FORMAT_DOUBLE) + return SF_FALSE ; + + } ; + /* Can only do this is in SFM_WRITE mode. */ + if (psf->mode != SFM_WRITE) + return SF_FALSE ; + /* If data has already been written this must fail. */ + if (psf->have_written) + return SF_FALSE ; + /* Everything seems OK, so set psf->has_peak and re-write header. */ + if (datasize == SF_FALSE && psf->peak_info != NULL) + { free (psf->peak_info) ; + psf->peak_info = NULL ; + } + else if (psf->peak_info == NULL) + { psf->peak_info = peak_info_calloc (psf->sf.channels) ; + if (psf->peak_info != NULL) + psf->peak_info->peak_loc = SF_PEAK_START ; + } ; + + if (psf->write_header) + psf->write_header (psf, SF_TRUE) ; + return datasize ; + + case SFC_GET_LOG_INFO : + if (data == NULL) + return SFE_BAD_CONTROL_CMD ; + LSF_SNPRINTF (data, datasize, "%s", psf->logbuffer) ; + break ; + + case SFC_CALC_SIGNAL_MAX : + if (data == NULL || datasize != sizeof (double)) + return (psf->error = SFE_BAD_CONTROL_CMD) ; + *((double*) data) = psf_calc_signal_max (psf, SF_FALSE) ; + break ; + + case SFC_CALC_NORM_SIGNAL_MAX : + if (data == NULL || datasize != sizeof (double)) + return (psf->error = SFE_BAD_CONTROL_CMD) ; + *((double*) data) = psf_calc_signal_max (psf, SF_TRUE) ; + break ; + + case SFC_CALC_MAX_ALL_CHANNELS : + if (data == NULL || datasize != SIGNED_SIZEOF (double) * psf->sf.channels) + return (psf->error = SFE_BAD_CONTROL_CMD) ; + return psf_calc_max_all_channels (psf, (double*) data, SF_FALSE) ; + + case SFC_CALC_NORM_MAX_ALL_CHANNELS : + if (data == NULL || datasize != SIGNED_SIZEOF (double) * psf->sf.channels) + return (psf->error = SFE_BAD_CONTROL_CMD) ; + return psf_calc_max_all_channels (psf, (double*) data, SF_TRUE) ; + + case SFC_GET_SIGNAL_MAX : + if (data == NULL || datasize != sizeof (double)) + { psf->error = SFE_BAD_CONTROL_CMD ; + return SF_FALSE ; + } ; + return psf_get_signal_max (psf, (double *) data) ; + + case SFC_GET_MAX_ALL_CHANNELS : + if (data == NULL || datasize != SIGNED_SIZEOF (double) * psf->sf.channels) + { psf->error = SFE_BAD_CONTROL_CMD ; + return SF_FALSE ; + } ; + return psf_get_max_all_channels (psf, (double*) data) ; + + case SFC_UPDATE_HEADER_NOW : + if (psf->write_header) + psf->write_header (psf, SF_TRUE) ; + break ; + + case SFC_SET_UPDATE_HEADER_AUTO : + psf->auto_header = datasize ? SF_TRUE : SF_FALSE ; + return psf->auto_header ; + break ; + + case SFC_SET_ADD_DITHER_ON_WRITE : + case SFC_SET_ADD_DITHER_ON_READ : + /* + ** FIXME ! + ** These are obsolete. Just return. + ** Remove some time after version 1.0.8. + */ + break ; + + case SFC_SET_DITHER_ON_WRITE : + if (data == NULL || datasize != SIGNED_SIZEOF (SF_DITHER_INFO)) + return (psf->error = SFE_BAD_CONTROL_CMD) ; + memcpy (&psf->write_dither, data, sizeof (psf->write_dither)) ; + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + dither_init (psf, SFM_WRITE) ; + break ; + + case SFC_SET_DITHER_ON_READ : + if (data == NULL || datasize != SIGNED_SIZEOF (SF_DITHER_INFO)) + return (psf->error = SFE_BAD_CONTROL_CMD) ; + memcpy (&psf->read_dither, data, sizeof (psf->read_dither)) ; + if (psf->mode == SFM_READ || psf->mode == SFM_RDWR) + dither_init (psf, SFM_READ) ; + break ; + + case SFC_FILE_TRUNCATE : + if (psf->mode != SFM_WRITE && psf->mode != SFM_RDWR) + return SF_TRUE ; + if (datasize != sizeof (sf_count_t)) + return SF_TRUE ; + { sf_count_t position ; + + position = *((sf_count_t*) data) ; + + if (sf_seek (sndfile, position, SEEK_SET) != position) + return SF_TRUE ; + + psf->sf.frames = position ; + + position = psf_fseek (psf, 0, SEEK_CUR) ; + + return psf_ftruncate (psf, position) ; + } ; + break ; + + case SFC_SET_RAW_START_OFFSET : + if (data == NULL || datasize != sizeof (sf_count_t)) + return (psf->error = SFE_BAD_CONTROL_CMD) ; + if ((psf->sf.format & SF_FORMAT_TYPEMASK) != SF_FORMAT_RAW) + return (psf->error = SFE_BAD_CONTROL_CMD) ; + + psf->dataoffset = *((sf_count_t*) data) ; + sf_seek (sndfile, 0, SEEK_CUR) ; + break ; + + case SFC_GET_EMBED_FILE_INFO : + if (data == NULL || datasize != sizeof (SF_EMBED_FILE_INFO)) + return (psf->error = SFE_BAD_CONTROL_CMD) ; + + ((SF_EMBED_FILE_INFO*) data)->offset = psf->fileoffset ; + ((SF_EMBED_FILE_INFO*) data)->length = psf->filelength ; + break ; + + /* Lite remove start */ + case SFC_TEST_IEEE_FLOAT_REPLACE : + psf->ieee_replace = (datasize) ? SF_TRUE : SF_FALSE ; + if ((psf->sf.format & SF_FORMAT_SUBMASK) == SF_FORMAT_FLOAT) + float32_init (psf) ; + else if ((psf->sf.format & SF_FORMAT_SUBMASK) == SF_FORMAT_DOUBLE) + double64_init (psf) ; + else + return (psf->error = SFE_BAD_CONTROL_CMD) ; + break ; + /* Lite remove end */ + + case SFC_SET_CLIPPING : + psf->add_clipping = (datasize) ? SF_TRUE : SF_FALSE ; + return psf->add_clipping ; + + case SFC_GET_CLIPPING : + return psf->add_clipping ; + + case SFC_GET_LOOP_INFO : + if (datasize != sizeof (SF_LOOP_INFO) || data == NULL) + return SF_FALSE ; + if (psf->loop_info == NULL) + return SF_FALSE ; + memcpy (data, psf->loop_info, sizeof (SF_LOOP_INFO)) ; + return SF_TRUE ; + + case SFC_SET_BROADCAST_INFO : + { int format = psf->sf.format & SF_FORMAT_TYPEMASK ; + + /* Only WAV supports the BEXT (Broadcast) chunk. */ + if (format != SF_FORMAT_WAV && format != SF_FORMAT_WAVEX) + return SF_FALSE ; + } ; + + /* Only makes sense in SFM_WRITE or SFM_RDWR mode. */ + if ((psf->mode != SFM_WRITE) && (psf->mode != SFM_RDWR)) + return SF_FALSE ; + /* If data has already been written this must fail. */ + if (psf->have_written) + return SF_FALSE ; + + if (psf->broadcast_info == NULL) + psf->broadcast_info = broadcast_info_alloc () ; + + broadcast_info_copy (psf->broadcast_info, data) ; + broadcast_add_coding_history (psf->broadcast_info, psf->sf.channels, psf->sf.samplerate) ; + + if (psf->write_header) + psf->write_header (psf, SF_TRUE) ; + return SF_TRUE ; + + case SFC_GET_BROADCAST_INFO : + if (datasize != sizeof (SF_BROADCAST_INFO) || data == NULL) + return SF_FALSE ; + if (psf->broadcast_info == NULL) + return SF_FALSE ; + return broadcast_info_copy (data, psf->broadcast_info) ; + + case SFC_GET_INSTRUMENT : + if (datasize != sizeof (SF_INSTRUMENT) || data == NULL) + return SF_FALSE ; + if (psf->instrument == NULL) + return SF_FALSE ; + memcpy (data, psf->instrument, sizeof (SF_INSTRUMENT)) ; + return SF_TRUE ; + + case SFC_SET_INSTRUMENT : + /* If data has already been written this must fail. */ + if (psf->have_written) + return SF_FALSE ; + if (datasize != sizeof (SF_INSTRUMENT) || data == NULL) + return SF_FALSE ; + if (psf->instrument == NULL && (psf->instrument = psf_instrument_alloc ()) == NULL) + { psf->error = SFE_MALLOC_FAILED ; + return SF_FALSE ; + } ; + memcpy (psf->instrument, data, sizeof (SF_INSTRUMENT)) ; + return SF_TRUE ; + + default : + /* Must be a file specific command. Pass it on. */ + if (psf->command) + return psf->command (psf, command, data, datasize) ; + + psf_log_printf (psf, "*** sf_command : cmd = 0x%X\n", command) ; + return (psf->error = SFE_BAD_CONTROL_CMD) ; + } ; + + return 0 ; +} /* sf_command */ + +/*------------------------------------------------------------------------------ +*/ + +sf_count_t +sf_seek (SNDFILE *sndfile, sf_count_t offset, int whence) +{ SF_PRIVATE *psf ; + sf_count_t seek_from_start = 0, retval ; + + VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ; + + if (! psf->sf.seekable) + { psf->error = SFE_NOT_SEEKABLE ; + return PSF_SEEK_ERROR ; + } ; + + /* If the whence parameter has a mode ORed in, check to see that + ** it makes sense. + */ + if (((whence & SFM_MASK) == SFM_WRITE && psf->mode == SFM_READ) || + ((whence & SFM_MASK) == SFM_READ && psf->mode == SFM_WRITE)) + { psf->error = SFE_WRONG_SEEK ; + return PSF_SEEK_ERROR ; + } ; + + /* Convert all SEEK_CUR and SEEK_END into seek_from_start to be + ** used with SEEK_SET. + */ + switch (whence) + { /* The SEEK_SET behaviour is independant of mode. */ + case SEEK_SET : + case SEEK_SET | SFM_READ : + case SEEK_SET | SFM_WRITE : + case SEEK_SET | SFM_RDWR : + seek_from_start = offset ; + break ; + + /* The SEEK_CUR is a little more tricky. */ + case SEEK_CUR : + if (offset == 0) + { if (psf->mode == SFM_READ) + return psf->read_current ; + if (psf->mode == SFM_WRITE) + return psf->write_current ; + } ; + if (psf->mode == SFM_READ) + seek_from_start = psf->read_current + offset ; + else if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + seek_from_start = psf->write_current + offset ; + else + psf->error = SFE_AMBIGUOUS_SEEK ; + break ; + + case SEEK_CUR | SFM_READ : + if (offset == 0) + return psf->read_current ; + seek_from_start = psf->read_current + offset ; + break ; + + case SEEK_CUR | SFM_WRITE : + if (offset == 0) + return psf->write_current ; + seek_from_start = psf->write_current + offset ; + break ; + + /* The SEEK_END */ + case SEEK_END : + case SEEK_END | SFM_READ : + case SEEK_END | SFM_WRITE : + seek_from_start = psf->sf.frames + offset ; + break ; + + default : + psf->error = SFE_BAD_SEEK ; + break ; + } ; + + if (psf->error) + return PSF_SEEK_ERROR ; + + if (seek_from_start < 0 || seek_from_start > psf->sf.frames) + { psf->error = SFE_BAD_SEEK ; + return PSF_SEEK_ERROR ; + } ; + + if (psf->seek) + { int new_mode = (whence & SFM_MASK) ? (whence & SFM_MASK) : psf->mode ; + + retval = psf->seek (psf, new_mode, seek_from_start) ; + + switch (new_mode) + { case SFM_READ : + psf->read_current = retval ; + break ; + case SFM_WRITE : + psf->write_current = retval ; + break ; + case SFM_RDWR : + psf->read_current = retval ; + psf->write_current = retval ; + new_mode = SFM_READ ; + break ; + } ; + + psf->last_op = new_mode ; + + return retval ; + } ; + + psf->error = SFE_AMBIGUOUS_SEEK ; + return PSF_SEEK_ERROR ; +} /* sf_seek */ + +/*------------------------------------------------------------------------------ +*/ + +int +sf_get_info (SNDFILE * sndfile, SF_INFO * info) +{ SF_PRIVATE *psf ; + + if (info == NULL) + return SF_FALSE ; + if ((psf = (SF_PRIVATE*) sndfile) == NULL) + return SF_FALSE ; + if (psf->Magick != SNDFILE_MAGICK) + return SF_FALSE ; + + /* Need to correct psf->sf.frames ???? */ + memcpy (info, &psf->sf, sizeof (SF_INFO)) ; + + return SF_TRUE ; +} /* sf_get_info */ + +const char* +sf_get_string (SNDFILE *sndfile, int str_type) +{ SF_PRIVATE *psf ; + + if ((psf = (SF_PRIVATE*) sndfile) == NULL) + return NULL ; + if (psf->Magick != SNDFILE_MAGICK) + return NULL ; + + return psf_get_string (psf, str_type) ; +} /* sf_get_string */ + +int +sf_set_string (SNDFILE *sndfile, int str_type, const char* str) +{ SF_PRIVATE *psf ; + + VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ; + + return psf_set_string (psf, str_type, str) ; +} /* sf_get_string */ + +/*============================================================================== +*/ + +sf_count_t +sf_read_raw (SNDFILE *sndfile, void *ptr, sf_count_t bytes) +{ SF_PRIVATE *psf ; + sf_count_t count ; + int bytewidth, blockwidth ; + + VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ; + + bytewidth = (psf->bytewidth > 0) ? psf->bytewidth : 1 ; + blockwidth = (psf->blockwidth > 0) ? psf->blockwidth : 1 ; + + if (psf->mode == SFM_WRITE) + { psf->error = SFE_NOT_READMODE ; + return 0 ; + } ; + + if (bytes < 0 || psf->read_current >= psf->datalength) + { psf_memset (ptr, 0, bytes) ; + return 0 ; + } ; + + if (bytes % (psf->sf.channels * bytewidth)) + { psf->error = SFE_BAD_READ_ALIGN ; + return 0 ; + } ; + + count = psf_fread (ptr, 1, bytes, psf) ; + + if (count < bytes) + psf_memset (((char*) ptr) + count, 0, bytes - count) ; + + psf->read_current += count / blockwidth ; + + psf->last_op = SFM_READ ; + + return count ; +} /* sf_read_raw */ + +/*------------------------------------------------------------------------------ +*/ + +sf_count_t +sf_read_short (SNDFILE *sndfile, short *ptr, sf_count_t len) +{ SF_PRIVATE *psf ; + sf_count_t count, extra ; + + VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ; + + if (psf->mode == SFM_WRITE) + { psf->error = SFE_NOT_READMODE ; + return 0 ; + } ; + + if (len % psf->sf.channels) + { psf->error = SFE_BAD_READ_ALIGN ; + return 0 ; + } ; + + if (len <= 0 || psf->read_current >= psf->sf.frames) + { psf_memset (ptr, 0, len * sizeof (short)) ; + return 0 ; /* End of file. */ + } ; + + if (psf->read_short == NULL || psf->seek == NULL) + { psf->error = SFE_UNIMPLEMENTED ; + return 0 ; + } ; + + if (psf->last_op != SFM_READ) + if (psf->seek (psf, SFM_READ, psf->read_current) < 0) + return 0 ; + + count = psf->read_short (psf, ptr, len) ; + + if (psf->read_current + count / psf->sf.channels > psf->sf.frames) + { count = (psf->sf.frames - psf->read_current) * psf->sf.channels ; + extra = len - count ; + psf_memset (ptr + count, 0, extra * sizeof (short)) ; + psf->read_current = psf->sf.frames ; + } ; + + psf->read_current += count / psf->sf.channels ; + + psf->last_op = SFM_READ ; + + if (psf->read_current > psf->sf.frames) + { count = psf->sf.channels * (psf->read_current - psf->sf.frames) ; + psf->read_current = psf->sf.frames ; + } ; + + return count ; +} /* sf_read_short */ + +sf_count_t +sf_readf_short (SNDFILE *sndfile, short *ptr, sf_count_t frames) +{ SF_PRIVATE *psf ; + sf_count_t count, extra ; + + VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ; + + if (psf->mode == SFM_WRITE) + { psf->error = SFE_NOT_READMODE ; + return 0 ; + } ; + + if (frames <= 0 || psf->read_current >= psf->sf.frames) + { psf_memset (ptr, 0, frames * psf->sf.channels * sizeof (short)) ; + return 0 ; /* End of file. */ + } ; + + if (psf->read_short == NULL || psf->seek == NULL) + { psf->error = SFE_UNIMPLEMENTED ; + return 0 ; + } ; + + if (psf->last_op != SFM_READ) + if (psf->seek (psf, SFM_READ, psf->read_current) < 0) + return 0 ; + + count = psf->read_short (psf, ptr, frames * psf->sf.channels) ; + + if (psf->read_current + count / psf->sf.channels > psf->sf.frames) + { count = (psf->sf.frames - psf->read_current) * psf->sf.channels ; + extra = frames * psf->sf.channels - count ; + psf_memset (ptr + count, 0, extra * sizeof (short)) ; + psf->read_current = psf->sf.frames ; + } ; + + psf->read_current += count / psf->sf.channels ; + + psf->last_op = SFM_READ ; + + if (psf->read_current > psf->sf.frames) + { count = psf->sf.channels * (psf->read_current - psf->sf.frames) ; + psf->read_current = psf->sf.frames ; + } ; + + return count / psf->sf.channels ; +} /* sf_readf_short */ + +/*------------------------------------------------------------------------------ +*/ + +sf_count_t +sf_read_int (SNDFILE *sndfile, int *ptr, sf_count_t len) +{ SF_PRIVATE *psf ; + sf_count_t count, extra ; + + VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ; + + if (psf->mode == SFM_WRITE) + { psf->error = SFE_NOT_READMODE ; + return 0 ; + } ; + + if (len % psf->sf.channels) + { psf->error = SFE_BAD_READ_ALIGN ; + return 0 ; + } ; + + if (len <= 0 || psf->read_current >= psf->sf.frames) + { psf_memset (ptr, 0, len * sizeof (int)) ; + return 0 ; + } ; + + if (psf->read_int == NULL || psf->seek == NULL) + { psf->error = SFE_UNIMPLEMENTED ; + return 0 ; + } ; + + if (psf->last_op != SFM_READ) + if (psf->seek (psf, SFM_READ, psf->read_current) < 0) + return 0 ; + + count = psf->read_int (psf, ptr, len) ; + + if (psf->read_current + count / psf->sf.channels > psf->sf.frames) + { count = (psf->sf.frames - psf->read_current) * psf->sf.channels ; + extra = len - count ; + psf_memset (ptr + count, 0, extra * sizeof (int)) ; + psf->read_current = psf->sf.frames ; + } ; + + psf->read_current += count / psf->sf.channels ; + + psf->last_op = SFM_READ ; + + if (psf->read_current > psf->sf.frames) + { count = psf->sf.channels * (psf->read_current - psf->sf.frames) ; + psf->read_current = psf->sf.frames ; + } ; + + return count ; +} /* sf_read_int */ + +sf_count_t +sf_readf_int (SNDFILE *sndfile, int *ptr, sf_count_t frames) +{ SF_PRIVATE *psf ; + sf_count_t count, extra ; + + VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ; + + if (psf->mode == SFM_WRITE) + { psf->error = SFE_NOT_READMODE ; + return 0 ; + } ; + + if (frames <= 0 || psf->read_current >= psf->sf.frames) + { psf_memset (ptr, 0, frames * psf->sf.channels * sizeof (int)) ; + return 0 ; + } ; + + if (psf->read_int == NULL || psf->seek == NULL) + { psf->error = SFE_UNIMPLEMENTED ; + return 0 ; + } ; + + if (psf->last_op != SFM_READ) + if (psf->seek (psf, SFM_READ, psf->read_current) < 0) + return 0 ; + + count = psf->read_int (psf, ptr, frames * psf->sf.channels) ; + + if (psf->read_current + count / psf->sf.channels > psf->sf.frames) + { count = (psf->sf.frames - psf->read_current) * psf->sf.channels ; + extra = frames * psf->sf.channels - count ; + psf_memset (ptr + count, 0, extra * sizeof (int)) ; + psf->read_current = psf->sf.frames ; + } ; + + psf->read_current += count / psf->sf.channels ; + + psf->last_op = SFM_READ ; + + if (psf->read_current > psf->sf.frames) + { count = psf->sf.channels * (psf->read_current - psf->sf.frames) ; + psf->read_current = psf->sf.frames ; + } ; + + return count / psf->sf.channels ; +} /* sf_readf_int */ + +/*------------------------------------------------------------------------------ +*/ + +sf_count_t +sf_read_float (SNDFILE *sndfile, float *ptr, sf_count_t len) +{ SF_PRIVATE *psf ; + sf_count_t count, extra ; + + VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ; + + if (psf->mode == SFM_WRITE) + { psf->error = SFE_NOT_READMODE ; + return 0 ; + } ; + + if (len % psf->sf.channels) + { psf->error = SFE_BAD_READ_ALIGN ; + return 0 ; + } ; + + if (len <= 0 || psf->read_current >= psf->sf.frames) + { psf_memset (ptr, 0, len * sizeof (float)) ; + return 0 ; + } ; + + if (psf->read_float == NULL || psf->seek == NULL) + { psf->error = SFE_UNIMPLEMENTED ; + return 0 ; + } ; + + if (psf->last_op != SFM_READ) + if (psf->seek (psf, SFM_READ, psf->read_current) < 0) + return 0 ; + + count = psf->read_float (psf, ptr, len) ; + + if (psf->read_current + count / psf->sf.channels > psf->sf.frames) + { count = (psf->sf.frames - psf->read_current) * psf->sf.channels ; + extra = len - count ; + psf_memset (ptr + count, 0, extra * sizeof (float)) ; + psf->read_current = psf->sf.frames ; + } ; + + psf->read_current += count / psf->sf.channels ; + + psf->last_op = SFM_READ ; + + if (psf->read_current > psf->sf.frames) + { count = psf->sf.channels * (psf->read_current - psf->sf.frames) ; + psf->read_current = psf->sf.frames ; + } ; + + return count ; +} /* sf_read_float */ + +sf_count_t +sf_readf_float (SNDFILE *sndfile, float *ptr, sf_count_t frames) +{ SF_PRIVATE *psf ; + sf_count_t count, extra ; + + VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ; + + if (psf->mode == SFM_WRITE) + { psf->error = SFE_NOT_READMODE ; + return 0 ; + } ; + + if (frames <= 0 || psf->read_current >= psf->sf.frames) + { psf_memset (ptr, 0, frames * psf->sf.channels * sizeof (float)) ; + return 0 ; + } ; + + if (psf->read_float == NULL || psf->seek == NULL) + { psf->error = SFE_UNIMPLEMENTED ; + return 0 ; + } ; + + if (psf->last_op != SFM_READ) + if (psf->seek (psf, SFM_READ, psf->read_current) < 0) + return 0 ; + + count = psf->read_float (psf, ptr, frames * psf->sf.channels) ; + + if (psf->read_current + count / psf->sf.channels > psf->sf.frames) + { count = (psf->sf.frames - psf->read_current) * psf->sf.channels ; + extra = frames * psf->sf.channels - count ; + psf_memset (ptr + count, 0, extra * sizeof (float)) ; + psf->read_current = psf->sf.frames ; + } ; + + psf->read_current += count / psf->sf.channels ; + + psf->last_op = SFM_READ ; + + if (psf->read_current > psf->sf.frames) + { count = psf->sf.channels * (psf->read_current - psf->sf.frames) ; + psf->read_current = psf->sf.frames ; + } ; + + return count / psf->sf.channels ; +} /* sf_readf_float */ + +/*------------------------------------------------------------------------------ +*/ + +sf_count_t +sf_read_double (SNDFILE *sndfile, double *ptr, sf_count_t len) +{ SF_PRIVATE *psf ; + sf_count_t count, extra ; + + VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ; + + if (psf->mode == SFM_WRITE) + { psf->error = SFE_NOT_READMODE ; + return 0 ; + } ; + + if (len % psf->sf.channels) + { psf->error = SFE_BAD_READ_ALIGN ; + return 0 ; + } ; + + if (len <= 0 || psf->read_current >= psf->sf.frames) + { psf_memset (ptr, 0, len * sizeof (double)) ; + return 0 ; + } ; + + if (psf->read_double == NULL || psf->seek == NULL) + { psf->error = SFE_UNIMPLEMENTED ; + return 0 ; + } ; + + if (psf->last_op != SFM_READ) + if (psf->seek (psf, SFM_READ, psf->read_current) < 0) + return 0 ; + + count = psf->read_double (psf, ptr, len) ; + + if (psf->read_current + count / psf->sf.channels > psf->sf.frames) + { count = (psf->sf.frames - psf->read_current) * psf->sf.channels ; + extra = len - count ; + psf_memset (ptr + count, 0, extra * sizeof (double)) ; + psf->read_current = psf->sf.frames ; + } ; + + psf->read_current += count / psf->sf.channels ; + + psf->last_op = SFM_READ ; + + if (psf->read_current > psf->sf.frames) + { count = psf->sf.channels * (psf->read_current - psf->sf.frames) ; + psf->read_current = psf->sf.frames ; + } ; + + return count ; +} /* sf_read_double */ + +sf_count_t +sf_readf_double (SNDFILE *sndfile, double *ptr, sf_count_t frames) +{ SF_PRIVATE *psf ; + sf_count_t count, extra ; + + VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ; + + if (psf->mode == SFM_WRITE) + { psf->error = SFE_NOT_READMODE ; + return 0 ; + } ; + + if (frames <= 0 || psf->read_current >= psf->sf.frames) + { psf_memset (ptr, 0, frames * psf->sf.channels * sizeof (double)) ; + return 0 ; + } ; + + if (psf->read_double == NULL || psf->seek == NULL) + { psf->error = SFE_UNIMPLEMENTED ; + return 0 ; + } ; + + if (psf->last_op != SFM_READ) + if (psf->seek (psf, SFM_READ, psf->read_current) < 0) + return 0 ; + + count = psf->read_double (psf, ptr, frames * psf->sf.channels) ; + + if (psf->read_current + count / psf->sf.channels > psf->sf.frames) + { count = (psf->sf.frames - psf->read_current) * psf->sf.channels ; + extra = frames * psf->sf.channels - count ; + psf_memset (ptr + count, 0, extra * sizeof (double)) ; + psf->read_current = psf->sf.frames ; + } ; + + psf->read_current += count / psf->sf.channels ; + + psf->last_op = SFM_READ ; + + if (psf->read_current > psf->sf.frames) + { count = psf->sf.channels * (psf->read_current - psf->sf.frames) ; + psf->read_current = psf->sf.frames ; + } ; + + return count / psf->sf.channels ; +} /* sf_readf_double */ + +/*------------------------------------------------------------------------------ +*/ + +sf_count_t +sf_write_raw (SNDFILE *sndfile, const void *ptr, sf_count_t len) +{ SF_PRIVATE *psf ; + sf_count_t count ; + int bytewidth, blockwidth ; + + VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ; + + bytewidth = (psf->bytewidth > 0) ? psf->bytewidth : 1 ; + blockwidth = (psf->blockwidth > 0) ? psf->blockwidth : 1 ; + + if (psf->mode == SFM_READ) + { psf->error = SFE_NOT_WRITEMODE ; + return 0 ; + } ; + + if (len % (psf->sf.channels * bytewidth)) + { psf->error = SFE_BAD_WRITE_ALIGN ; + return 0 ; + } ; + + if (psf->have_written == SF_FALSE && psf->write_header != NULL) + psf->write_header (psf, SF_FALSE) ; + psf->have_written = SF_TRUE ; + + count = psf_fwrite (ptr, 1, len, psf) ; + + psf->write_current += count / blockwidth ; + + if (psf->write_current > psf->sf.frames) + psf->sf.frames = psf->write_current ; + + psf->last_op = SFM_WRITE ; + + return count ; +} /* sf_write_raw */ + +/*------------------------------------------------------------------------------ +*/ + +sf_count_t +sf_write_short (SNDFILE *sndfile, const short *ptr, sf_count_t len) +{ SF_PRIVATE *psf ; + sf_count_t count ; + + VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ; + + if (psf->mode == SFM_READ) + { psf->error = SFE_NOT_WRITEMODE ; + return 0 ; + } ; + + if (len % psf->sf.channels) + { psf->error = SFE_BAD_WRITE_ALIGN ; + return 0 ; + } ; + + if (psf->write_short == NULL || psf->seek == NULL) + { psf->error = SFE_UNIMPLEMENTED ; + return 0 ; + } ; + + if (psf->last_op != SFM_WRITE) + if (psf->seek (psf, SFM_WRITE, psf->write_current) < 0) + return 0 ; + + if (psf->have_written == SF_FALSE && psf->write_header != NULL) + psf->write_header (psf, SF_FALSE) ; + psf->have_written = SF_TRUE ; + + count = psf->write_short (psf, ptr, len) ; + + psf->write_current += count / psf->sf.channels ; + + psf->last_op = SFM_WRITE ; + + if (psf->auto_header && psf->write_header != NULL) + psf->write_header (psf, SF_TRUE) ; + + if (psf->write_current > psf->sf.frames) + psf->sf.frames = psf->write_current ; + + return count ; +} /* sf_write_short */ + +sf_count_t +sf_writef_short (SNDFILE *sndfile, const short *ptr, sf_count_t frames) +{ SF_PRIVATE *psf ; + sf_count_t count ; + + VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ; + + if (psf->mode == SFM_READ) + { psf->error = SFE_NOT_WRITEMODE ; + return 0 ; + } ; + + if (psf->write_short == NULL || psf->seek == NULL) + { psf->error = SFE_UNIMPLEMENTED ; + return 0 ; + } ; + + if (psf->last_op != SFM_WRITE) + if (psf->seek (psf, SFM_WRITE, psf->write_current) < 0) + return 0 ; + + if (psf->have_written == SF_FALSE && psf->write_header != NULL) + psf->write_header (psf, SF_FALSE) ; + psf->have_written = SF_TRUE ; + + count = psf->write_short (psf, ptr, frames * psf->sf.channels) ; + + psf->write_current += count / psf->sf.channels ; + + psf->last_op = SFM_WRITE ; + + if (psf->auto_header && psf->write_header != NULL) + psf->write_header (psf, SF_TRUE) ; + + if (psf->write_current > psf->sf.frames) + psf->sf.frames = psf->write_current ; + + return count / psf->sf.channels ; +} /* sf_writef_short */ + +/*------------------------------------------------------------------------------ +*/ + +sf_count_t +sf_write_int (SNDFILE *sndfile, const int *ptr, sf_count_t len) +{ SF_PRIVATE *psf ; + sf_count_t count ; + + VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ; + + if (psf->mode == SFM_READ) + { psf->error = SFE_NOT_WRITEMODE ; + return 0 ; + } ; + + if (len % psf->sf.channels) + { psf->error = SFE_BAD_WRITE_ALIGN ; + return 0 ; + } ; + + if (psf->write_int == NULL || psf->seek == NULL) + { psf->error = SFE_UNIMPLEMENTED ; + return 0 ; + } ; + + if (psf->last_op != SFM_WRITE) + if (psf->seek (psf, SFM_WRITE, psf->write_current) < 0) + return 0 ; + + if (psf->have_written == SF_FALSE && psf->write_header != NULL) + psf->write_header (psf, SF_FALSE) ; + psf->have_written = SF_TRUE ; + + count = psf->write_int (psf, ptr, len) ; + + psf->write_current += count / psf->sf.channels ; + + psf->last_op = SFM_WRITE ; + + if (psf->auto_header && psf->write_header != NULL) + psf->write_header (psf, SF_TRUE) ; + + if (psf->write_current > psf->sf.frames) + psf->sf.frames = psf->write_current ; + + return count ; +} /* sf_write_int */ + +sf_count_t +sf_writef_int (SNDFILE *sndfile, const int *ptr, sf_count_t frames) +{ SF_PRIVATE *psf ; + sf_count_t count ; + + VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ; + + if (psf->mode == SFM_READ) + { psf->error = SFE_NOT_WRITEMODE ; + return 0 ; + } ; + + if (psf->write_int == NULL || psf->seek == NULL) + { psf->error = SFE_UNIMPLEMENTED ; + return 0 ; + } ; + + if (psf->last_op != SFM_WRITE) + if (psf->seek (psf, SFM_WRITE, psf->write_current) < 0) + return 0 ; + + if (psf->have_written == SF_FALSE && psf->write_header != NULL) + psf->write_header (psf, SF_FALSE) ; + psf->have_written = SF_TRUE ; + + count = psf->write_int (psf, ptr, frames * psf->sf.channels) ; + + psf->write_current += count / psf->sf.channels ; + + psf->last_op = SFM_WRITE ; + + if (psf->auto_header && psf->write_header != NULL) + psf->write_header (psf, SF_TRUE) ; + + if (psf->write_current > psf->sf.frames) + psf->sf.frames = psf->write_current ; + + return count / psf->sf.channels ; +} /* sf_writef_int */ + +/*------------------------------------------------------------------------------ +*/ + +sf_count_t +sf_write_float (SNDFILE *sndfile, const float *ptr, sf_count_t len) +{ SF_PRIVATE *psf ; + sf_count_t count ; + + VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ; + + if (psf->mode == SFM_READ) + { psf->error = SFE_NOT_WRITEMODE ; + return 0 ; + } ; + + if (len % psf->sf.channels) + { psf->error = SFE_BAD_WRITE_ALIGN ; + return 0 ; + } ; + + if (psf->write_float == NULL || psf->seek == NULL) + { psf->error = SFE_UNIMPLEMENTED ; + return 0 ; + } ; + + if (psf->last_op != SFM_WRITE) + if (psf->seek (psf, SFM_WRITE, psf->write_current) < 0) + return 0 ; + + if (psf->have_written == SF_FALSE && psf->write_header != NULL) + psf->write_header (psf, SF_FALSE) ; + psf->have_written = SF_TRUE ; + + count = psf->write_float (psf, ptr, len) ; + + psf->write_current += count / psf->sf.channels ; + + psf->last_op = SFM_WRITE ; + + if (psf->auto_header && psf->write_header != NULL) + psf->write_header (psf, SF_TRUE) ; + + if (psf->write_current > psf->sf.frames) + psf->sf.frames = psf->write_current ; + + return count ; +} /* sf_write_float */ + +sf_count_t +sf_writef_float (SNDFILE *sndfile, const float *ptr, sf_count_t frames) +{ SF_PRIVATE *psf ; + sf_count_t count ; + + VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ; + + if (psf->mode == SFM_READ) + { psf->error = SFE_NOT_WRITEMODE ; + return 0 ; + } ; + + if (psf->write_float == NULL || psf->seek == NULL) + { psf->error = SFE_UNIMPLEMENTED ; + return 0 ; + } ; + + if (psf->last_op != SFM_WRITE) + if (psf->seek (psf, SFM_WRITE, psf->write_current) < 0) + return 0 ; + + if (psf->have_written == SF_FALSE && psf->write_header != NULL) + psf->write_header (psf, SF_FALSE) ; + psf->have_written = SF_TRUE ; + + count = psf->write_float (psf, ptr, frames * psf->sf.channels) ; + + psf->write_current += count / psf->sf.channels ; + + psf->last_op = SFM_WRITE ; + + if (psf->auto_header && psf->write_header != NULL) + psf->write_header (psf, SF_TRUE) ; + + if (psf->write_current > psf->sf.frames) + psf->sf.frames = psf->write_current ; + + return count / psf->sf.channels ; +} /* sf_writef_float */ + +/*------------------------------------------------------------------------------ +*/ + +sf_count_t +sf_write_double (SNDFILE *sndfile, const double *ptr, sf_count_t len) +{ SF_PRIVATE *psf ; + sf_count_t count ; + + VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ; + + if (psf->mode == SFM_READ) + { psf->error = SFE_NOT_WRITEMODE ; + return 0 ; + } ; + + if (len % psf->sf.channels) + { psf->error = SFE_BAD_WRITE_ALIGN ; + return 0 ; + } ; + + if (psf->write_double == NULL || psf->seek == NULL) + { psf->error = SFE_UNIMPLEMENTED ; + return 0 ; + } ; + + if (psf->last_op != SFM_WRITE) + if (psf->seek (psf, SFM_WRITE, psf->write_current) < 0) + return 0 ; + + if (psf->have_written == SF_FALSE && psf->write_header != NULL) + psf->write_header (psf, SF_FALSE) ; + psf->have_written = SF_TRUE ; + + count = psf->write_double (psf, ptr, len) ; + + psf->write_current += count / psf->sf.channels ; + + psf->last_op = SFM_WRITE ; + + if (psf->auto_header && psf->write_header != NULL) + psf->write_header (psf, SF_TRUE) ; + + if (psf->write_current > psf->sf.frames) + psf->sf.frames = psf->write_current ; + + return count ; +} /* sf_write_double */ + +sf_count_t +sf_writef_double (SNDFILE *sndfile, const double *ptr, sf_count_t frames) +{ SF_PRIVATE *psf ; + sf_count_t count ; + + VALIDATE_SNDFILE_AND_ASSIGN_PSF (sndfile, psf, 1) ; + + if (psf->mode == SFM_READ) + { psf->error = SFE_NOT_WRITEMODE ; + return 0 ; + } ; + + if (psf->write_double == NULL || psf->seek == NULL) + { psf->error = SFE_UNIMPLEMENTED ; + return 0 ; + } ; + + if (psf->last_op != SFM_WRITE) + if (psf->seek (psf, SFM_WRITE, psf->write_current) < 0) + return 0 ; + + if (psf->have_written == SF_FALSE && psf->write_header != NULL) + psf->write_header (psf, SF_FALSE) ; + psf->have_written = SF_TRUE ; + + count = psf->write_double (psf, ptr, frames * psf->sf.channels) ; + + psf->write_current += count / psf->sf.channels ; + + psf->last_op = SFM_WRITE ; + + if (psf->auto_header && psf->write_header != NULL) + psf->write_header (psf, SF_TRUE) ; + + if (psf->write_current > psf->sf.frames) + psf->sf.frames = psf->write_current ; + + return count / psf->sf.channels ; +} /* sf_writef_double */ + +/*========================================================================= +** Private functions. +*/ + +static int +try_resource_fork (SF_PRIVATE * psf, int mode) +{ + if (psf_open_rsrc (psf, mode) != 0) + return 0 ; + + /* More checking here. */ + psf_log_printf (psf, "Resource fork : %s\n", psf->rsrcpath) ; + + return SF_FORMAT_SD2 ; +} /* try_resource_fork */ + +static int +format_from_extension (SF_PRIVATE *psf) +{ char *cptr ; + char buffer [16] ; + int format = 0 ; + + if (psf->filename == NULL) + return 0 ; + + if ((cptr = strrchr (psf->filename, '.')) == NULL) + return 0 ; + + cptr ++ ; + if (strlen (cptr) > sizeof (buffer) - 1) + return 0 ; + + strncpy (buffer, cptr, sizeof (buffer)) ; + buffer [sizeof (buffer) - 1] = 0 ; + + /* Convert everything in the buffer to lower case. */ + cptr = buffer ; + while (*cptr) + { *cptr = tolower (*cptr) ; + cptr ++ ; + } ; + + cptr = buffer ; + + if (strcmp (cptr, "au") == 0) + { psf->sf.channels = 1 ; + psf->sf.samplerate = 8000 ; + format = SF_FORMAT_RAW | SF_FORMAT_ULAW ; + } + else if (strcmp (cptr, "snd") == 0) + { psf->sf.channels = 1 ; + psf->sf.samplerate = 8000 ; + format = SF_FORMAT_RAW | SF_FORMAT_ULAW ; + } + else if (strcmp (cptr, "vox") == 0) + { psf->sf.channels = 1 ; + psf->sf.samplerate = 8000 ; + format = SF_FORMAT_RAW | SF_FORMAT_VOX_ADPCM ; + } + else if (strcmp (cptr, "gsm") == 0) + { psf->sf.channels = 1 ; + psf->sf.samplerate = 8000 ; + format = SF_FORMAT_RAW | SF_FORMAT_GSM610 ; + } + + /* For RAW files, make sure the dataoffset if set correctly. */ + if ((format & SF_FORMAT_TYPEMASK) == SF_FORMAT_RAW) + psf->dataoffset = 0 ; + + return format ; +} /* format_from_extension */ + +static int +guess_file_type (SF_PRIVATE *psf) +{ int buffer [3], format ; + + if (psf_binheader_readf (psf, "b", &buffer, SIGNED_SIZEOF (buffer)) != SIGNED_SIZEOF (buffer)) + { psf->error = SFE_BAD_FILE_READ ; + return 0 ; + } ; + + if ((buffer [0] == MAKE_MARKER ('R', 'I', 'F', 'F') || buffer [0] == MAKE_MARKER ('R', 'I', 'F', 'X')) + && buffer [2] == MAKE_MARKER ('W', 'A', 'V', 'E')) + return SF_FORMAT_WAV ; + + if (buffer [0] == MAKE_MARKER ('F', 'O', 'R', 'M')) + { if (buffer [2] == MAKE_MARKER ('A', 'I', 'F', 'F') || buffer [2] == MAKE_MARKER ('A', 'I', 'F', 'C')) + return SF_FORMAT_AIFF ; + if (buffer [2] == MAKE_MARKER ('8', 'S', 'V', 'X') || buffer [2] == MAKE_MARKER ('1', '6', 'S', 'V')) + return SF_FORMAT_SVX ; + return 0 ; + } ; + + if (buffer [0] == MAKE_MARKER ('.', 's', 'n', 'd') || buffer [0] == MAKE_MARKER ('d', 'n', 's', '.')) + return SF_FORMAT_AU ; + + if ((buffer [0] == MAKE_MARKER ('f', 'a', 'p', ' ') || buffer [0] == MAKE_MARKER (' ', 'p', 'a', 'f'))) + return SF_FORMAT_PAF ; + + if (buffer [0] == MAKE_MARKER ('N', 'I', 'S', 'T')) + return SF_FORMAT_NIST ; + + if (buffer [0] == MAKE_MARKER ('C', 'r', 'e', 'a') && buffer [1] == MAKE_MARKER ('t', 'i', 'v', 'e')) + return SF_FORMAT_VOC ; + + if ((buffer [0] & MAKE_MARKER (0xFF, 0xFF, 0xF8, 0xFF)) == MAKE_MARKER (0x64, 0xA3, 0x00, 0x00) || + (buffer [0] & MAKE_MARKER (0xFF, 0xF8, 0xFF, 0xFF)) == MAKE_MARKER (0x00, 0x00, 0xA3, 0x64)) + return SF_FORMAT_IRCAM ; + + if (buffer [0] == MAKE_MARKER ('r', 'i', 'f', 'f')) + return SF_FORMAT_W64 ; + + if (buffer [0] == MAKE_MARKER (0, 0, 0x03, 0xE8) && buffer [1] == MAKE_MARKER (0, 0, 0, 1) && + buffer [2] == MAKE_MARKER (0, 0, 0, 1)) + return SF_FORMAT_MAT4 ; + + if (buffer [0] == MAKE_MARKER (0, 0, 0, 0) && buffer [1] == MAKE_MARKER (1, 0, 0, 0) && + buffer [2] == MAKE_MARKER (1, 0, 0, 0)) + return SF_FORMAT_MAT4 ; + + if (buffer [0] == MAKE_MARKER ('M', 'A', 'T', 'L') && buffer [1] == MAKE_MARKER ('A', 'B', ' ', '5')) + return SF_FORMAT_MAT5 ; + + if (buffer [0] == MAKE_MARKER ('P', 'V', 'F', '1')) + return SF_FORMAT_PVF ; + + if (buffer [0] == MAKE_MARKER ('E', 'x', 't', 'e') && buffer [1] == MAKE_MARKER ('n', 'd', 'e', 'd') && + buffer [2] == MAKE_MARKER (' ', 'I', 'n', 's')) + return SF_FORMAT_XI ; + + if (buffer [0] == MAKE_MARKER ('c', 'a', 'f', 'f') && buffer [2] == MAKE_MARKER ('d', 'e', 's', 'c')) + return SF_FORMAT_CAF ; + + if (ENABLE_EXPERIMENTAL_CODE && buffer [0] == MAKE_MARKER ('O', 'g', 'g', 'S')) + return SF_FORMAT_OGG ; + + if (buffer [0] == MAKE_MARKER ('A', 'L', 'a', 'w') && buffer [1] == MAKE_MARKER ('S', 'o', 'u', 'n') + && buffer [2] == MAKE_MARKER ('d', 'F', 'i', 'l')) + return SF_FORMAT_WVE ; + + if (buffer [0] == MAKE_MARKER ('D', 'i', 'a', 'm') && buffer [1] == MAKE_MARKER ('o', 'n', 'd', 'W') + && buffer [2] == MAKE_MARKER ('a', 'r', 'e', ' ')) + return SF_FORMAT_DWD ; + + if (buffer [0] == MAKE_MARKER ('L', 'M', '8', '9') || buffer [0] == MAKE_MARKER ('5', '3', 0, 0)) + return SF_FORMAT_TXW ; + + if ((buffer [0] & MAKE_MARKER (0xFF, 0xFF, 0x80, 0xFF)) == MAKE_MARKER (0xF0, 0x7E, 0, 0x01)) + return SF_FORMAT_SDS ; + + if (buffer [0] == MAKE_MARKER ('C', 'A', 'T', ' ') && buffer [2] == MAKE_MARKER ('R', 'E', 'X', '2')) + return SF_FORMAT_REX2 ; + + if (buffer [0] == MAKE_MARKER (0x30, 0x26, 0xB2, 0x75) && buffer [1] == MAKE_MARKER (0x8E, 0x66, 0xCF, 0x11)) + return 0 /*-SF_FORMAT_WMA-*/ ; + + /* HMM (Hidden Markov Model) Tool Kit. */ + if (2 * BEI2H_INT (buffer [0]) + 12 == psf->filelength && buffer [2] == MAKE_MARKER (0, 2, 0, 0)) + return SF_FORMAT_HTK ; + + if (buffer [0] == MAKE_MARKER ('f', 'L', 'a', 'C')) + return SF_FORMAT_FLAC ; + + /* Turtle Beach SMP 16-bit */ + if (buffer [0] == MAKE_MARKER ('S', 'O', 'U', 'N') && buffer [1] == MAKE_MARKER ('D', ' ', 'S', 'A')) + return 0 ; + + if (buffer [0] == MAKE_MARKER ('S', 'Y', '8', '0') || buffer [0] == MAKE_MARKER ('S', 'Y', '8', '5')) + return 0 ; + + if (buffer [0] == MAKE_MARKER ('a', 'j', 'k', 'g')) + return 0 /*-SF_FORMAT_SHN-*/ ; + + if (buffer [0] == MAKE_MARKER ('2', 'B', 'I', 'T')) + return SF_FORMAT_AVR ; + + /* This must be the second last one. */ + if (psf->filelength > 0 && (format = try_resource_fork (psf, SFM_READ)) != 0) + return format ; + + return 0 ; +} /* guess_file_type */ + + +static int +validate_sfinfo (SF_INFO *sfinfo) +{ if (sfinfo->samplerate < 1) + return 0 ; + if (sfinfo->frames < 0) + return 0 ; + if (sfinfo->channels < 1) + return 0 ; + if ((sfinfo->format & SF_FORMAT_TYPEMASK) == 0) + return 0 ; + if ((sfinfo->format & SF_FORMAT_SUBMASK) == 0) + return 0 ; + if (sfinfo->sections < 1) + return 0 ; + return 1 ; +} /* validate_sfinfo */ + +static int +validate_psf (SF_PRIVATE *psf) +{ + if (psf->datalength < 0) + { psf_log_printf (psf, "Invalid SF_PRIVATE field : datalength == %D.\n", psf->datalength) ; + return 0 ; + } ; + if (psf->dataoffset < 0) + { psf_log_printf (psf, "Invalid SF_PRIVATE field : dataoffset == %D.\n", psf->dataoffset) ; + return 0 ; + } ; + if (psf->blockwidth && psf->blockwidth != psf->sf.channels * psf->bytewidth) + { psf_log_printf (psf, "Invalid SF_PRIVATE field : channels * bytewidth == %d.\n", + psf->sf.channels * psf->bytewidth) ; + return 0 ; + } ; + return 1 ; +} /* validate_psf */ + +static void +save_header_info (SF_PRIVATE *psf) +{ LSF_SNPRINTF (sf_logbuffer, sizeof (sf_logbuffer), "%s", psf->logbuffer) ; +} /* save_header_info */ + +static void +copy_filename (SF_PRIVATE *psf, const char *path) +{ const char *ccptr ; + char *cptr ; + + LSF_SNPRINTF (psf->filepath, sizeof (psf->filepath), "%s", path) ; + if ((ccptr = strrchr (path, '/')) || (ccptr = strrchr (path, '\\'))) + ccptr ++ ; + else + ccptr = path ; + + LSF_SNPRINTF (psf->filename, sizeof (psf->filename), "%s", ccptr) ; + + /* Now grab the directory. */ + LSF_SNPRINTF (psf->directory, sizeof (psf->directory), "%s", path) ; + if ((cptr = strrchr (psf->directory, '/')) || (cptr = strrchr (psf->directory, '\\'))) + cptr [1] = 0 ; + else + psf->directory [0] = 0 ; + + return ; +} /* copy_filename */ + +/*============================================================================== +*/ + +static int +psf_close (SF_PRIVATE *psf) +{ int error ; + + if (psf->codec_close) + error = psf->codec_close (psf) ; + if (psf->container_close) + error = psf->container_close (psf) ; + + psf_fclose (psf) ; + psf_close_rsrc (psf) ; + + if (psf->container_data) + free (psf->container_data) ; + + if (psf->codec_data) + free (psf->codec_data) ; + + if (psf->interleave) + free (psf->interleave) ; + + if (psf->dither) + free (psf->dither) ; + + if (psf->peak_info) + free (psf->peak_info) ; + + if (psf->broadcast_info) + free (psf->broadcast_info) ; + + if (psf->loop_info) + free (psf->loop_info) ; + + if (psf->instrument) + free (psf->instrument) ; + + if (psf->channel_map) + free (psf->channel_map) ; + + if (psf->format_desc) + { memset (psf->format_desc, 0, strlen (psf->format_desc)) ; + free (psf->format_desc) ; + } ; + + memset (psf, 0, sizeof (SF_PRIVATE)) ; + free (psf) ; + + return 0 ; +} /* psf_close */ + +static SNDFILE * +psf_open_file (SF_PRIVATE *psf, int mode, SF_INFO *sfinfo) +{ int error, format ; + + sf_errno = error = 0 ; + sf_logbuffer [0] = 0 ; + + if (psf->error) + { error = psf->error ; + goto error_exit ; + } ; + + if (mode != SFM_READ && mode != SFM_WRITE && mode != SFM_RDWR) + { error = SFE_BAD_OPEN_MODE ; + goto error_exit ; + } ; + + if (sfinfo == NULL) + { error = SFE_BAD_SF_INFO_PTR ; + goto error_exit ; + } ; + + /* Zero out these fields. */ + sfinfo->frames = 0 ; + sfinfo->sections = 0 ; + sfinfo->seekable = 0 ; + + if (mode == SFM_READ) + { if ((sfinfo->format & SF_FORMAT_TYPEMASK) == SF_FORMAT_RAW) + { if (sf_format_check (sfinfo) == 0) + { error = SFE_RAW_BAD_FORMAT ; + goto error_exit ; + } ; + } + else + memset (sfinfo, 0, sizeof (SF_INFO)) ; + } ; + + memcpy (&(psf->sf), sfinfo, sizeof (SF_INFO)) ; + + psf->Magick = SNDFILE_MAGICK ; + psf->norm_float = SF_TRUE ; + psf->norm_double = SF_TRUE ; + psf->mode = mode ; + psf->dataoffset = -1 ; + psf->datalength = -1 ; + psf->read_current = -1 ; + psf->write_current = -1 ; + psf->auto_header = SF_FALSE ; + psf->rwf_endian = SF_ENDIAN_LITTLE ; + psf->seek = psf_default_seek ; + psf->float_int_mult = 0 ; + psf->float_max = -1.0 ; + + /* + ** File formats that support ambisonic should override this default + ** and set it to SF_AMBISONIC_NONE. + */ + psf->wavex_ambisonic = 0 ; + psf->sf.sections = 1 ; + + psf->is_pipe = psf_is_pipe (psf) ; + + if (psf->is_pipe) + { psf->sf.seekable = SF_FALSE ; + psf->filelength = SF_COUNT_MAX ; + } + else + { psf->sf.seekable = SF_TRUE ; + + /* File is open, so get the length. */ + psf->filelength = psf_get_filelen (psf) ; + } ; + + if (psf->fileoffset > 0) + { switch (psf->mode) + { case SFM_READ : + if (psf->filelength < 44) + { psf_log_printf (psf, "Short filelength: %D (fileoffset: %D)\n", psf->filelength, psf->fileoffset) ; + error = SFE_BAD_OFFSET ; + goto error_exit ; + } ; + break ; + + case SFM_WRITE : + psf->fileoffset = 0 ; + psf_fseek (psf, 0, SEEK_END) ; + psf->fileoffset = psf_ftell (psf) ; + break ; + + case SFM_RDWR : + error = SFE_NO_EMBEDDED_RDWR ; + goto error_exit ; + } ; + + psf_log_printf (psf, "Embedded file offset : %D\n", psf->fileoffset) ; + } ; + + if (psf->filelength == SF_COUNT_MAX) + psf_log_printf (psf, "Length : unknown\n") ; + else + psf_log_printf (psf, "Length : %D\n", psf->filelength) ; + + if (mode == SFM_WRITE || (mode == SFM_RDWR && psf->filelength == 0)) + { /* If the file is being opened for write or RDWR and the file is currently + ** empty, then the SF_INFO struct must contain valid data. + */ + if (sf_format_check (&(psf->sf)) == 0) + { error = SFE_BAD_OPEN_FORMAT ; + goto error_exit ; + } ; + } + else if ((psf->sf.format & SF_FORMAT_TYPEMASK) != SF_FORMAT_RAW) + { /* If type RAW has not been specified then need to figure out file type. */ + psf->sf.format = guess_file_type (psf) ; + + if (psf->sf.format == 0) + psf->sf.format = format_from_extension (psf) ; + } ; + + /* Prevent unnecessary seeks */ + psf->last_op = psf->mode ; + + /* Set bytewidth if known. */ + switch (psf->sf.format & SF_FORMAT_SUBMASK) + { case SF_FORMAT_PCM_S8 : + case SF_FORMAT_PCM_U8 : + case SF_FORMAT_ULAW : + case SF_FORMAT_ALAW : + case SF_FORMAT_DPCM_8 : + psf->bytewidth = 1 ; + break ; + + case SF_FORMAT_PCM_16 : + case SF_FORMAT_DPCM_16 : + psf->bytewidth = 2 ; + break ; + + case SF_FORMAT_PCM_24 : + psf->bytewidth = 3 ; + break ; + + case SF_FORMAT_PCM_32 : + case SF_FORMAT_FLOAT : + psf->bytewidth = 4 ; + break ; + + case SF_FORMAT_DOUBLE : + psf->bytewidth = 8 ; + break ; + } ; + + /* Call the initialisation function for the relevant file type. */ + switch (psf->sf.format & SF_FORMAT_TYPEMASK) + { case SF_FORMAT_WAV : + case SF_FORMAT_WAVEX : + error = wav_open (psf) ; + break ; + + case SF_FORMAT_AIFF : + error = aiff_open (psf) ; + break ; + + case SF_FORMAT_AU : + error = au_open (psf) ; + break ; + + case SF_FORMAT_RAW : + error = raw_open (psf) ; + break ; + + case SF_FORMAT_W64 : + error = w64_open (psf) ; + break ; + + /* Lite remove start */ + case SF_FORMAT_PAF : + error = paf_open (psf) ; + break ; + + case SF_FORMAT_SVX : + error = svx_open (psf) ; + break ; + + case SF_FORMAT_NIST : + error = nist_open (psf) ; + break ; + + case SF_FORMAT_IRCAM : + error = ircam_open (psf) ; + break ; + + case SF_FORMAT_VOC : + error = voc_open (psf) ; + break ; + + case SF_FORMAT_SDS : + error = sds_open (psf) ; + break ; + + case SF_FORMAT_OGG : + error = ogg_open (psf) ; + break ; + + case SF_FORMAT_TXW : + error = txw_open (psf) ; + break ; + + case SF_FORMAT_WVE : + error = wve_open (psf) ; + break ; + + case SF_FORMAT_DWD : + error = dwd_open (psf) ; + break ; + + case SF_FORMAT_MAT4 : + error = mat4_open (psf) ; + break ; + + case SF_FORMAT_MAT5 : + error = mat5_open (psf) ; + break ; + + case SF_FORMAT_PVF : + error = pvf_open (psf) ; + break ; + + case SF_FORMAT_XI : + error = xi_open (psf) ; + break ; + + case SF_FORMAT_HTK : + error = htk_open (psf) ; + break ; + + case SF_FORMAT_SD2 : + error = sd2_open (psf) ; + break ; + + case SF_FORMAT_REX2 : + error = rx2_open (psf) ; + break ; + + case SF_FORMAT_AVR : + error = avr_open (psf) ; + break ; + + case SF_FORMAT_FLAC : + error = flac_open (psf) ; + break ; + + case SF_FORMAT_CAF : + error = caf_open (psf) ; + break ; + + /* Lite remove end */ + + default : + error = SFE_UNKNOWN_FORMAT ; + } ; + + if (error) + goto error_exit ; + + /* For now, check whether embedding is supported. */ + format = psf->sf.format & SF_FORMAT_TYPEMASK ; + if (psf->fileoffset > 0 && + (format != SF_FORMAT_WAV) && (format != SF_FORMAT_WAVEX) && + (format != SF_FORMAT_AIFF) && (format != SF_FORMAT_AU) + ) + { error = SFE_NO_EMBED_SUPPORT ; + goto error_exit ; + } ; + + if (psf->fileoffset > 0) + psf_log_printf (psf, "Embedded file length : %D\n", psf->filelength) ; + + if (mode == SFM_RDWR && sf_format_check (&(psf->sf)) == 0) + { error = SFE_BAD_RDWR_FORMAT ; + goto error_exit ; + } ; + + if (validate_sfinfo (&(psf->sf)) == 0) + { psf_log_SF_INFO (psf) ; + save_header_info (psf) ; + error = SFE_BAD_SF_INFO ; + goto error_exit ; + } ; + + if (validate_psf (psf) == 0) + { save_header_info (psf) ; + error = SFE_INTERNAL ; + goto error_exit ; + } ; + + psf->read_current = 0 ; + psf->write_current = (psf->mode == SFM_RDWR) ? psf->sf.frames : 0 ; + + memcpy (sfinfo, &(psf->sf), sizeof (SF_INFO)) ; + + memcpy (sfinfo, &(psf->sf), sizeof (SF_INFO)) ; + + return (SNDFILE *) psf ; + +error_exit : + sf_errno = error ; + + if (error == SFE_SYSTEM) + LSF_SNPRINTF (sf_syserr, sizeof (sf_syserr), "%s", psf->syserr) ; + LSF_SNPRINTF (sf_logbuffer, sizeof (sf_logbuffer), "%s", psf->logbuffer) ; + + switch (error) + { case SF_ERR_SYSTEM : + case SF_ERR_UNSUPPORTED_ENCODING : + case SFE_UNIMPLEMENTED : + break ; + + case SFE_RAW_BAD_FORMAT : + break ; + + default : + if (psf->mode == SFM_READ) + { psf_log_printf (psf, "Parse error : %s\n", sf_error_number (error)) ; + error = SF_ERR_MALFORMED_FILE ; + } ; + } ; + + psf_close (psf) ; + return NULL ; +} /* psf_open_file */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: cd4f9e91-a8ec-4154-9bf6-fe4b8c69a615 +*/ diff --git a/src/sndfile.h.in b/src/sndfile.h.in new file mode 100644 index 00000000..2f55e878 --- /dev/null +++ b/src/sndfile.h.in @@ -0,0 +1,602 @@ +/* +** Copyright (C) 1999-2007 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* +** sndfile.h -- system-wide definitions +** +** API documentation is in the doc/ directory of the source code tarball +** and at http://www.mega-nerd.com/libsndfile/api.html. +*/ + +#ifndef SNDFILE_H +#define SNDFILE_H + +/* This is the version 1.0.X header file. */ +#define SNDFILE_1 + +#include + +/* For the Metrowerks CodeWarrior Pro Compiler (mainly MacOS) */ + +#if (defined (__MWERKS__)) +#include +#else +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* The following file types can be read and written. +** A file type would consist of a major type (ie SF_FORMAT_WAV) bitwise +** ORed with a minor type (ie SF_FORMAT_PCM). SF_FORMAT_TYPEMASK and +** SF_FORMAT_SUBMASK can be used to separate the major and minor file +** types. +*/ + +enum +{ /* Major formats. */ + SF_FORMAT_WAV = 0x010000, /* Microsoft WAV format (little endian default). */ + SF_FORMAT_AIFF = 0x020000, /* Apple/SGI AIFF format (big endian). */ + SF_FORMAT_AU = 0x030000, /* Sun/NeXT AU format (big endian). */ + SF_FORMAT_RAW = 0x040000, /* RAW PCM data. */ + SF_FORMAT_PAF = 0x050000, /* Ensoniq PARIS file format. */ + SF_FORMAT_SVX = 0x060000, /* Amiga IFF / SVX8 / SV16 format. */ + SF_FORMAT_NIST = 0x070000, /* Sphere NIST format. */ + SF_FORMAT_VOC = 0x080000, /* VOC files. */ + SF_FORMAT_IRCAM = 0x0A0000, /* Berkeley/IRCAM/CARL */ + SF_FORMAT_W64 = 0x0B0000, /* Sonic Foundry's 64 bit RIFF/WAV */ + SF_FORMAT_MAT4 = 0x0C0000, /* Matlab (tm) V4.2 / GNU Octave 2.0 */ + SF_FORMAT_MAT5 = 0x0D0000, /* Matlab (tm) V5.0 / GNU Octave 2.1 */ + SF_FORMAT_PVF = 0x0E0000, /* Portable Voice Format */ + SF_FORMAT_XI = 0x0F0000, /* Fasttracker 2 Extended Instrument */ + SF_FORMAT_HTK = 0x100000, /* HMM Tool Kit format */ + SF_FORMAT_SDS = 0x110000, /* Midi Sample Dump Standard */ + SF_FORMAT_AVR = 0x120000, /* Audio Visual Research */ + SF_FORMAT_WAVEX = 0x130000, /* MS WAVE with WAVEFORMATEX */ + SF_FORMAT_SD2 = 0x160000, /* Sound Designer 2 */ + SF_FORMAT_FLAC = 0x170000, /* FLAC lossless file format */ + SF_FORMAT_CAF = 0x180000, /* Core Audio File format */ + SF_FORMAT_WVE = 0x190000, /* Psion WVE format */ + + /* Subtypes from here on. */ + + SF_FORMAT_PCM_S8 = 0x0001, /* Signed 8 bit data */ + SF_FORMAT_PCM_16 = 0x0002, /* Signed 16 bit data */ + SF_FORMAT_PCM_24 = 0x0003, /* Signed 24 bit data */ + SF_FORMAT_PCM_32 = 0x0004, /* Signed 32 bit data */ + + SF_FORMAT_PCM_U8 = 0x0005, /* Unsigned 8 bit data (WAV and RAW only) */ + + SF_FORMAT_FLOAT = 0x0006, /* 32 bit float data */ + SF_FORMAT_DOUBLE = 0x0007, /* 64 bit float data */ + + SF_FORMAT_ULAW = 0x0010, /* U-Law encoded. */ + SF_FORMAT_ALAW = 0x0011, /* A-Law encoded. */ + SF_FORMAT_IMA_ADPCM = 0x0012, /* IMA ADPCM. */ + SF_FORMAT_MS_ADPCM = 0x0013, /* Microsoft ADPCM. */ + + SF_FORMAT_GSM610 = 0x0020, /* GSM 6.10 encoding. */ + SF_FORMAT_VOX_ADPCM = 0x0021, /* OKI / Dialogix ADPCM */ + + SF_FORMAT_G721_32 = 0x0030, /* 32kbs G721 ADPCM encoding. */ + SF_FORMAT_G723_24 = 0x0031, /* 24kbs G723 ADPCM encoding. */ + SF_FORMAT_G723_40 = 0x0032, /* 40kbs G723 ADPCM encoding. */ + + SF_FORMAT_DWVW_12 = 0x0040, /* 12 bit Delta Width Variable Word encoding. */ + SF_FORMAT_DWVW_16 = 0x0041, /* 16 bit Delta Width Variable Word encoding. */ + SF_FORMAT_DWVW_24 = 0x0042, /* 24 bit Delta Width Variable Word encoding. */ + SF_FORMAT_DWVW_N = 0x0043, /* N bit Delta Width Variable Word encoding. */ + + SF_FORMAT_DPCM_8 = 0x0050, /* 8 bit differential PCM (XI only) */ + SF_FORMAT_DPCM_16 = 0x0051, /* 16 bit differential PCM (XI only) */ + + /* Endian-ness options. */ + + SF_ENDIAN_FILE = 0x00000000, /* Default file endian-ness. */ + SF_ENDIAN_LITTLE = 0x10000000, /* Force little endian-ness. */ + SF_ENDIAN_BIG = 0x20000000, /* Force big endian-ness. */ + SF_ENDIAN_CPU = 0x30000000, /* Force CPU endian-ness. */ + + SF_FORMAT_SUBMASK = 0x0000FFFF, + SF_FORMAT_TYPEMASK = 0x0FFF0000, + SF_FORMAT_ENDMASK = 0x30000000 +} ; + +/* +** The following are the valid command numbers for the sf_command() +** interface. The use of these commands is documented in the file +** command.html in the doc directory of the source code distribution. +*/ + +enum +{ SFC_GET_LIB_VERSION = 0x1000, + SFC_GET_LOG_INFO = 0x1001, + + SFC_GET_NORM_DOUBLE = 0x1010, + SFC_GET_NORM_FLOAT = 0x1011, + SFC_SET_NORM_DOUBLE = 0x1012, + SFC_SET_NORM_FLOAT = 0x1013, + SFC_SET_SCALE_FLOAT_INT_READ = 0x1014, + + SFC_GET_SIMPLE_FORMAT_COUNT = 0x1020, + SFC_GET_SIMPLE_FORMAT = 0x1021, + + SFC_GET_FORMAT_INFO = 0x1028, + + SFC_GET_FORMAT_MAJOR_COUNT = 0x1030, + SFC_GET_FORMAT_MAJOR = 0x1031, + SFC_GET_FORMAT_SUBTYPE_COUNT = 0x1032, + SFC_GET_FORMAT_SUBTYPE = 0x1033, + + SFC_CALC_SIGNAL_MAX = 0x1040, + SFC_CALC_NORM_SIGNAL_MAX = 0x1041, + SFC_CALC_MAX_ALL_CHANNELS = 0x1042, + SFC_CALC_NORM_MAX_ALL_CHANNELS = 0x1043, + SFC_GET_SIGNAL_MAX = 0x1044, + SFC_GET_MAX_ALL_CHANNELS = 0x1045, + + SFC_SET_ADD_PEAK_CHUNK = 0x1050, + + SFC_UPDATE_HEADER_NOW = 0x1060, + SFC_SET_UPDATE_HEADER_AUTO = 0x1061, + + SFC_FILE_TRUNCATE = 0x1080, + + SFC_SET_RAW_START_OFFSET = 0x1090, + + SFC_SET_DITHER_ON_WRITE = 0x10A0, + SFC_SET_DITHER_ON_READ = 0x10A1, + + SFC_GET_DITHER_INFO_COUNT = 0x10A2, + SFC_GET_DITHER_INFO = 0x10A3, + + SFC_GET_EMBED_FILE_INFO = 0x10B0, + + SFC_SET_CLIPPING = 0x10C0, + SFC_GET_CLIPPING = 0x10C1, + + SFC_GET_INSTRUMENT = 0x10D0, + SFC_SET_INSTRUMENT = 0x10D1, + + SFC_GET_LOOP_INFO = 0x10E0, + + SFC_GET_BROADCAST_INFO = 0x10F0, + SFC_SET_BROADCAST_INFO = 0x10F1, + + SFC_GET_CHANNEL_MAP_INFO = 0x1100, + SFC_SET_CHANNEL_MAP_INFO = 0x1101, + + /* Support for Wavex Ambisonics Format */ + SFC_WAVEX_SET_AMBISONIC = 0x1200, + SFC_WAVEX_GET_AMBISONIC = 0x1201, + + /* Following commands for testing only. */ + SFC_TEST_IEEE_FLOAT_REPLACE = 0x6001, + + /* + ** SFC_SET_ADD_* values are deprecated and will disappear at some + ** time in the future. They are guaranteed to be here up to and + ** including version 1.0.8 to avoid breakage of existng software. + ** They currently do nothing and will continue to do nothing. + */ + SFC_SET_ADD_DITHER_ON_WRITE = 0x1070, + SFC_SET_ADD_DITHER_ON_READ = 0x1071 +} ; + + +/* +** String types that can be set and read from files. Not all file types +** support this and even the file types which support one, may not support +** all string types. +*/ + +enum +{ SF_STR_TITLE = 0x01, + SF_STR_COPYRIGHT = 0x02, + SF_STR_SOFTWARE = 0x03, + SF_STR_ARTIST = 0x04, + SF_STR_COMMENT = 0x05, + SF_STR_DATE = 0x06 +} ; + +/* +** Use the following as the start and end index when doing metadata +** transcoding. +*/ + +#define SF_STR_FIRST SF_STR_TITLE +#define SF_STR_LAST SF_STR_DATE + +enum +{ /* True and false */ + SF_FALSE = 0, + SF_TRUE = 1, + + /* Modes for opening files. */ + SFM_READ = 0x10, + SFM_WRITE = 0x20, + SFM_RDWR = 0x30, + + SF_AMBISONIC_NONE = 0x40, + SF_AMBISONIC_B_FORMAT = 0x41 +} ; + +/* Public error values. These are guaranteed to remain unchanged for the duration +** of the library major version number. +** There are also a large number of private error numbers which are internal to +** the library which can change at any time. +*/ + +enum +{ SF_ERR_NO_ERROR = 0, + SF_ERR_UNRECOGNISED_FORMAT = 1, + SF_ERR_SYSTEM = 2, + SF_ERR_MALFORMED_FILE = 3, + SF_ERR_UNSUPPORTED_ENCODING = 4 +} ; + + +/* Channel map values (used with SFC_SET/GET_CHANNEL_MAP). +*/ + +enum +{ SF_CHANNEL_MAP_INVALID = 0, + SF_CHANNEL_MAP_MONO = 1, + SF_CHANNEL_MAP_LEFT, + SF_CHANNEL_MAP_RIGHT, + SF_CHANNEL_MAP_CENTER, + SF_CHANNEL_MAP_FRONT_LEFT, + SF_CHANNEL_MAP_FRONT_RIGHT, + SF_CHANNEL_MAP_FRONT_CENTER, + SF_CHANNEL_MAP_REAR_CENTER, + SF_CHANNEL_MAP_REAR_LEFT, + SF_CHANNEL_MAP_REAR_RIGHT, + SF_CHANNEL_MAP_LFE, + SF_CHANNEL_MAP_FRONT_LEFT_OF_CENTER, + SF_CHANNEL_MAP_FRONT_RIGHT_OF_CENTER, + SF_CHANNEL_MAP_SIDE_LEFT, + SF_CHANNEL_MAP_SIDE_RIGHT, + SF_CHANNEL_MAP_TOP_CENTER, + SF_CHANNEL_MAP_TOP_FRONT_LEFT, + SF_CHANNEL_MAP_TOP_FRONT_RIGHT, + SF_CHANNEL_MAP_TOP_FRONT_CENTER, + SF_CHANNEL_MAP_TOP_REAR_LEFT, + SF_CHANNEL_MAP_TOP_REAR_RIGHT, + SF_CHANNEL_MAP_TOP_REAR_CENTER +} ; + + +/* A SNDFILE* pointer can be passed around much like stdio.h's FILE* pointer. */ + +typedef struct SNDFILE_tag SNDFILE ; + +/* The following typedef is system specific and is defined when libsndfile is. +** compiled. sf_count_t can be one of loff_t (Linux), off_t (*BSD), +** off64_t (Solaris), __int64_t (Win32) etc. +*/ + +typedef @TYPEOF_SF_COUNT_T@ sf_count_t ; + +#define SF_COUNT_MAX @SF_COUNT_MAX@ + +/* A pointer to a SF_INFO structure is passed to sf_open_read () and filled in. +** On write, the SF_INFO structure is filled in by the user and passed into +** sf_open_write (). +*/ + +struct SF_INFO +{ sf_count_t frames ; /* Used to be called samples. Changed to avoid confusion. */ + int samplerate ; + int channels ; + int format ; + int sections ; + int seekable ; +} ; + +typedef struct SF_INFO SF_INFO ; + +/* The SF_FORMAT_INFO struct is used to retrieve information about the sound +** file formats libsndfile supports using the sf_command () interface. +** +** Using this interface will allow applications to support new file formats +** and encoding types when libsndfile is upgraded, without requiring +** re-compilation of the application. +** +** Please consult the libsndfile documentation (particularly the information +** on the sf_command () interface) for examples of its use. +*/ + +typedef struct +{ int format ; + const char *name ; + const char *extension ; +} SF_FORMAT_INFO ; + +/* +** Enums and typedefs for adding dither on read and write. +** See the html documentation for sf_command(), SFC_SET_DITHER_ON_WRITE +** and SFC_SET_DITHER_ON_READ. +*/ + +enum +{ SFD_DEFAULT_LEVEL = 0, + SFD_CUSTOM_LEVEL = 0x40000000, + + SFD_NO_DITHER = 500, + SFD_WHITE = 501, + SFD_TRIANGULAR_PDF = 502 +} ; + +typedef struct +{ int type ; + double level ; + const char *name ; +} SF_DITHER_INFO ; + +/* Struct used to retrieve information about a file embedded within a +** larger file. See SFC_GET_EMBED_FILE_INFO. +*/ + +typedef struct +{ sf_count_t offset ; + sf_count_t length ; +} SF_EMBED_FILE_INFO ; + +/* +** Structs used to retrieve music sample information from a file. +*/ + +enum +{ /* + ** The loop mode field in SF_INSTRUMENT will be one of the following. + */ + SF_LOOP_NONE = 800, + SF_LOOP_FORWARD, + SF_LOOP_BACKWARD, + SF_LOOP_ALTERNATING +} ; + +typedef struct +{ int gain ; + char basenote, detune ; + char velocity_lo, velocity_hi ; + char key_lo, key_hi ; + int loop_count ; + + struct + { int mode ; + unsigned int start ; + unsigned int end ; + unsigned int count ; + } loops [16] ; /* make variable in a sensible way */ +} SF_INSTRUMENT ; + + + +/* Struct used to retrieve loop information from a file.*/ +typedef struct +{ + short time_sig_num ; /* any positive integer > 0 */ + short time_sig_den ; /* any positive power of 2 > 0 */ + int loop_mode ; /* see SF_LOOP enum */ + + int num_beats ; /* this is NOT the amount of quarter notes !!!*/ + /* a full bar of 4/4 is 4 beats */ + /* a full bar of 7/8 is 7 beats */ + + float bpm ; /* suggestion, as it can be calculated using other fields:*/ + /* file's lenght, file's sampleRate and our time_sig_den*/ + /* -> bpms are always the amount of _quarter notes_ per minute */ + + int root_key ; /* MIDI note, or -1 for None */ + int future [6] ; +} SF_LOOP_INFO ; + + +/* Struct used to retrieve broadcast (EBU) information from a file. +** Strongly (!) based on EBU "bext" chunk format used in Broadcast WAVE. +*/ +typedef struct +{ char description [256] ; + char originator [32] ; + char originator_reference [32] ; + char origination_date [10] ; + char origination_time [8] ; + int time_reference_low ; + int time_reference_high ; + short version ; + char umid [64] ; + char reserved [190] ; + unsigned int coding_history_size ; + char coding_history [256] ; +} SF_BROADCAST_INFO ; + +typedef sf_count_t (*sf_vio_get_filelen) (void *user_data) ; +typedef sf_count_t (*sf_vio_seek) (sf_count_t offset, int whence, void *user_data) ; +typedef sf_count_t (*sf_vio_read) (void *ptr, sf_count_t count, void *user_data) ; +typedef sf_count_t (*sf_vio_write) (const void *ptr, sf_count_t count, void *user_data) ; +typedef sf_count_t (*sf_vio_tell) (void *user_data) ; + +struct SF_VIRTUAL_IO +{ sf_vio_get_filelen get_filelen ; + sf_vio_seek seek ; + sf_vio_read read ; + sf_vio_write write ; + sf_vio_tell tell ; +} ; + +typedef struct SF_VIRTUAL_IO SF_VIRTUAL_IO ; + +/* Open the specified file for read, write or both. On error, this will +** return a NULL pointer. To find the error number, pass a NULL SNDFILE +** to sf_perror () or sf_error_str (). +** All calls to sf_open() should be matched with a call to sf_close(). +*/ + +SNDFILE* sf_open (const char *path, int mode, SF_INFO *sfinfo) ; + +/* Use the existing file descriptor to create a SNDFILE object. If close_desc +** is TRUE, the file descriptor will be closed when sf_close() is called. If +** it is FALSE, the descritor will not be closed. +** When passed a descriptor like this, the library will assume that the start +** of file header is at the current file offset. This allows sound files within +** larger container files to be read and/or written. +** On error, this will return a NULL pointer. To find the error number, pass a +** NULL SNDFILE to sf_perror () or sf_error_str (). +** All calls to sf_open_fd() should be matched with a call to sf_close(). + +*/ + +SNDFILE* sf_open_fd (int fd, int mode, SF_INFO *sfinfo, int close_desc) ; + +SNDFILE* sf_open_virtual (SF_VIRTUAL_IO *sfvirtual, int mode, SF_INFO *sfinfo, void *user_data) ; + +/* sf_error () returns a error number which can be translated to a text +** string using sf_error_number(). +*/ + +int sf_error (SNDFILE *sndfile) ; + +/* sf_strerror () returns to the caller a pointer to the current error message for +** the given SNDFILE. +*/ + +const char* sf_strerror (SNDFILE *sndfile) ; + +/* sf_error_number () allows the retrieval of the error string for each internal +** error number. +** +*/ + +const char* sf_error_number (int errnum) ; + +/* The following three error functions are deprecated but they will remain in the +** library for the forseeable future. The function sf_strerror() should be used +** in their place. +*/ + +int sf_perror (SNDFILE *sndfile) ; +int sf_error_str (SNDFILE *sndfile, char* str, size_t len) ; + + +/* Return TRUE if fields of the SF_INFO struct are a valid combination of values. */ + +int sf_command (SNDFILE *sndfile, int command, void *data, int datasize) ; + +/* Return TRUE if fields of the SF_INFO struct are a valid combination of values. */ + +int sf_format_check (const SF_INFO *info) ; + +/* Seek within the waveform data chunk of the SNDFILE. sf_seek () uses +** the same values for whence (SEEK_SET, SEEK_CUR and SEEK_END) as +** stdio.h function fseek (). +** An offset of zero with whence set to SEEK_SET will position the +** read / write pointer to the first data sample. +** On success sf_seek returns the current position in (multi-channel) +** samples from the start of the file. +** Please see the libsndfile documentation for moving the read pointer +** separately from the write pointer on files open in mode SFM_RDWR. +** On error all of these functions return -1. +*/ + +sf_count_t sf_seek (SNDFILE *sndfile, sf_count_t frames, int whence) ; + +/* Retrieve the SF_INFO struct for the given SNDFILE. Returns SF_FALSE on +** failure, SF_TRUE otherwise. +*/ + +int sf_get_info (SNDFILE * sndfile, SF_INFO * info) ; + +/* Functions for retrieving and setting string data within sound files. +** Not all file types support this features; AIFF and WAV do. For both +** functions, the str_type parameter must be one of the SF_STR_* values +** defined above. +** On error, sf_set_string() returns non-zero while sf_get_string() +** returns NULL. +*/ + +int sf_set_string (SNDFILE *sndfile, int str_type, const char* str) ; + +const char* sf_get_string (SNDFILE *sndfile, int str_type) ; + +/* Functions for reading/writing the waveform data of a sound file. +*/ + +sf_count_t sf_read_raw (SNDFILE *sndfile, void *ptr, sf_count_t bytes) ; +sf_count_t sf_write_raw (SNDFILE *sndfile, const void *ptr, sf_count_t bytes) ; + +/* Functions for reading and writing the data chunk in terms of frames. +** The number of items actually read/written = frames * number of channels. +** sf_xxxx_raw read/writes the raw data bytes from/to the file +** sf_xxxx_short passes data in the native short format +** sf_xxxx_int passes data in the native int format +** sf_xxxx_float passes data in the native float format +** sf_xxxx_double passes data in the native double format +** All of these read/write function return number of frames read/written. +*/ + +sf_count_t sf_readf_short (SNDFILE *sndfile, short *ptr, sf_count_t frames) ; +sf_count_t sf_writef_short (SNDFILE *sndfile, const short *ptr, sf_count_t frames) ; + +sf_count_t sf_readf_int (SNDFILE *sndfile, int *ptr, sf_count_t frames) ; +sf_count_t sf_writef_int (SNDFILE *sndfile, const int *ptr, sf_count_t frames) ; + +sf_count_t sf_readf_float (SNDFILE *sndfile, float *ptr, sf_count_t frames) ; +sf_count_t sf_writef_float (SNDFILE *sndfile, const float *ptr, sf_count_t frames) ; + +sf_count_t sf_readf_double (SNDFILE *sndfile, double *ptr, sf_count_t frames) ; +sf_count_t sf_writef_double (SNDFILE *sndfile, const double *ptr, sf_count_t frames) ; + +/* Functions for reading and writing the data chunk in terms of items. +** Otherwise similar to above. +** All of these read/write function return number of items read/written. +*/ + +sf_count_t sf_read_short (SNDFILE *sndfile, short *ptr, sf_count_t items) ; +sf_count_t sf_write_short (SNDFILE *sndfile, const short *ptr, sf_count_t items) ; + +sf_count_t sf_read_int (SNDFILE *sndfile, int *ptr, sf_count_t items) ; +sf_count_t sf_write_int (SNDFILE *sndfile, const int *ptr, sf_count_t items) ; + +sf_count_t sf_read_float (SNDFILE *sndfile, float *ptr, sf_count_t items) ; +sf_count_t sf_write_float (SNDFILE *sndfile, const float *ptr, sf_count_t items) ; + +sf_count_t sf_read_double (SNDFILE *sndfile, double *ptr, sf_count_t items) ; +sf_count_t sf_write_double (SNDFILE *sndfile, const double *ptr, sf_count_t items) ; + +/* Close the SNDFILE and clean up all memory allocations associated with this +** file. +** Returns 0 on success, or an error number. +*/ + +int sf_close (SNDFILE *sndfile) ; + +/* If the file is opened SFM_WRITE or SFM_RDWR, call fsync() on the file +** to force the writing of data to disk. If the file is opened SFM_READ +** no action is taken. +*/ + +void sf_write_sync (SNDFILE *sndfile) ; + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* SNDFILE_H */ diff --git a/src/sndfile.hh b/src/sndfile.hh new file mode 100644 index 00000000..dcf2773b --- /dev/null +++ b/src/sndfile.hh @@ -0,0 +1,351 @@ +/* +** Copyright (C) 2005,2006 Erik de Castro Lopo +** +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the author nor the names of any contributors may be used +** to endorse or promote products derived from this software without +** specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* +** The above modified BSD style license (GPL and LGPL compatible) applies to +** this file. It does not apply to libsndfile itself which is released under +** the GNU LGPL or the libsndfile test suite which is released under the GNU +** GPL. +** This means that this header file can be used under this modified BSD style +** license, but the LGPL still holds for the libsndfile library itself. +*/ + +/* +** sndfile.hh -- A lightweight C++ wrapper for the libsndfile API. +** +** All the methods are inlines and all functionality is contained in this +** file. There is no separate implementation file. +** +** API documentation is in the doc/ directory of the source code tarball +** and at http://www.mega-nerd.com/libsndfile/api.html. +*/ + +#ifndef SNDFILE_HH +#define SNDFILE_HH + +#include + +#include +#include // for std::nothrow + +class SndfileHandle +{ private : + struct SNDFILE_ref + { SNDFILE_ref (void) ; + ~SNDFILE_ref (void) ; + + SNDFILE *sf ; + SF_INFO sfinfo ; + int ref ; + } ; + + SNDFILE_ref *p ; + + public : + /* Default constructor */ + SndfileHandle (void) : p (NULL) {} ; + SndfileHandle (const char *path, int mode = SFM_READ, + int format = 0, int channels = 0, int samplerate = 0) ; + SndfileHandle (std::string const & path, int mode = SFM_READ, + int format = 0, int channels = 0, int samplerate = 0) ; + ~SndfileHandle (void) ; + + SndfileHandle (const SndfileHandle &orig) ; + SndfileHandle & operator = (const SndfileHandle &rhs) ; + + /* Mainly for debugging/testing. */ + int refCount (void) const { return (p == NULL) ? 0 : p->ref ; } + + operator bool () const { return (p != NULL) ; } + + bool operator == (const SndfileHandle &rhs) const { return (p == rhs.p) ; } + + sf_count_t frames (void) const { return p ? p->sfinfo.frames : 0 ; } + int format (void) const { return p ? p->sfinfo.format : 0 ; } + int channels (void) const { return p ? p->sfinfo.channels : 0 ; } + int samplerate (void) const { return p ? p->sfinfo.samplerate : 0 ; } + + int error (void) const ; + const char * strError (void) const ; + + int command (int cmd, void *data, int datasize) ; + + sf_count_t seek (sf_count_t frames, int whence) ; + + void writeSync (void) ; + + int setString (int str_type, const char* str) ; + + const char* getString (int str_type) const ; + + static int formatCheck (int format, int channels, int samplerate) ; + + sf_count_t read (short *ptr, sf_count_t items) ; + sf_count_t read (int *ptr, sf_count_t items) ; + sf_count_t read (float *ptr, sf_count_t items) ; + sf_count_t read (double *ptr, sf_count_t items) ; + + sf_count_t write (const short *ptr, sf_count_t items) ; + sf_count_t write (const int *ptr, sf_count_t items) ; + sf_count_t write (const float *ptr, sf_count_t items) ; + sf_count_t write (const double *ptr, sf_count_t items) ; + + sf_count_t readf (short *ptr, sf_count_t frames) ; + sf_count_t readf (int *ptr, sf_count_t frames) ; + sf_count_t readf (float *ptr, sf_count_t frames) ; + sf_count_t readf (double *ptr, sf_count_t frames) ; + + sf_count_t writef (const short *ptr, sf_count_t frames) ; + sf_count_t writef (const int *ptr, sf_count_t frames) ; + sf_count_t writef (const float *ptr, sf_count_t frames) ; + sf_count_t writef (const double *ptr, sf_count_t frames) ; + + sf_count_t readRaw (void *ptr, sf_count_t bytes) ; + sf_count_t writeRaw (const void *ptr, sf_count_t bytes) ; + +} ; + +/*============================================================================== +** Nothing but implementation below. +*/ + +inline +SndfileHandle::SNDFILE_ref::SNDFILE_ref (void) +: ref (1) +{} + +inline +SndfileHandle::SNDFILE_ref::~SNDFILE_ref (void) +{ if (sf != NULL) sf_close (sf) ; } + +inline +SndfileHandle::SndfileHandle (const char *path, int mode, int fmt, int chans, int srate) +: p (NULL) +{ + p = new (std::nothrow) SNDFILE_ref () ; + + if (p != NULL) + { p->ref = 1 ; + + p->sfinfo.frames = 0 ; + p->sfinfo.channels = chans ; + p->sfinfo.format = fmt ; + p->sfinfo.samplerate = srate ; + p->sfinfo.sections = 0 ; + p->sfinfo.seekable = 0 ; + + if ((p->sf = sf_open (path, mode, &p->sfinfo)) == NULL) + { delete p ; + p = NULL ; + } ; + } ; +} /* SndfileHandle const char * constructor */ + +inline +SndfileHandle::SndfileHandle (std::string const & path, int mode, int fmt, int chans, int srate) +: p (NULL) +{ + p = new (std::nothrow) SNDFILE_ref () ; + + if (p != NULL) + { p->ref = 1 ; + + p->sfinfo.frames = 0 ; + p->sfinfo.channels = chans ; + p->sfinfo.format = fmt ; + p->sfinfo.samplerate = srate ; + p->sfinfo.sections = 0 ; + p->sfinfo.seekable = 0 ; + + if ((p->sf = sf_open (path.c_str (), mode, &p->sfinfo)) == NULL) + { delete p ; + p = NULL ; + } ; + } ; +} /* SndfileHandle std::string constructor */ + +inline +SndfileHandle::~SndfileHandle (void) +{ if (p != NULL && --p->ref == 0) + delete p ; +} /* SndfileHandle destructor */ + + +inline +SndfileHandle::SndfileHandle (const SndfileHandle &orig) +: p (orig.p) +{ if (p != NULL) + ++p->ref ; +} /* SndfileHandle copy constructor */ + +inline SndfileHandle & +SndfileHandle::operator = (const SndfileHandle &rhs) +{ + if (&rhs == this) + return *this ; + if (p != NULL && --p->ref == 0) + delete p ; + + p = rhs.p ; + if (p != NULL) + ++p->ref ; + + return *this ; +} /* SndfileHandle assignment operator */ + +inline int +SndfileHandle::error (void) const +{ return sf_error (p->sf) ; } + +inline const char * +SndfileHandle::strError (void) const +{ return sf_strerror (p->sf) ; } + +inline int +SndfileHandle::command (int cmd, void *data, int datasize) +{ return sf_command (p->sf, cmd, data, datasize) ; } + +inline sf_count_t +SndfileHandle::seek (sf_count_t frame_count, int whence) +{ return sf_seek (p->sf, frame_count, whence) ; } + +inline void +SndfileHandle::writeSync (void) +{ sf_write_sync (p->sf) ; } + +inline int +SndfileHandle::setString (int str_type, const char* str) +{ return sf_set_string (p->sf, str_type, str) ; } + +inline const char* +SndfileHandle::getString (int str_type) const +{ return sf_get_string (p->sf, str_type) ; } + +inline int +SndfileHandle::formatCheck(int fmt, int chans, int srate) +{ + SF_INFO sfinfo ; + + sfinfo.frames = 0 ; + sfinfo.channels = chans ; + sfinfo.format = fmt ; + sfinfo.samplerate = srate ; + sfinfo.sections = 0 ; + sfinfo.seekable = 0 ; + + return sf_format_check (&sfinfo) ; +} + +/*---------------------------------------------------------------------*/ + +inline sf_count_t +SndfileHandle::read (short *ptr, sf_count_t items) +{ return sf_read_short (p->sf, ptr, items) ; } + +inline sf_count_t +SndfileHandle::read (int *ptr, sf_count_t items) +{ return sf_read_int (p->sf, ptr, items) ; } + +inline sf_count_t +SndfileHandle::read (float *ptr, sf_count_t items) +{ return sf_read_float (p->sf, ptr, items) ; } + +inline sf_count_t +SndfileHandle::read (double *ptr, sf_count_t items) +{ return sf_read_double (p->sf, ptr, items) ; } + +inline sf_count_t +SndfileHandle::write (const short *ptr, sf_count_t items) +{ return sf_write_short (p->sf, ptr, items) ; } + +inline sf_count_t +SndfileHandle::write (const int *ptr, sf_count_t items) +{ return sf_write_int (p->sf, ptr, items) ; } + +inline sf_count_t +SndfileHandle::write (const float *ptr, sf_count_t items) +{ return sf_write_float (p->sf, ptr, items) ; } + +inline sf_count_t +SndfileHandle::write (const double *ptr, sf_count_t items) +{ return sf_write_double (p->sf, ptr, items) ; } + +inline sf_count_t +SndfileHandle::readf (short *ptr, sf_count_t frame_count) +{ return sf_readf_short (p->sf, ptr, frame_count) ; } + +inline sf_count_t +SndfileHandle::readf (int *ptr, sf_count_t frame_count) +{ return sf_readf_int (p->sf, ptr, frame_count) ; } + +inline sf_count_t +SndfileHandle::readf (float *ptr, sf_count_t frame_count) +{ return sf_readf_float (p->sf, ptr, frame_count) ; } + +inline sf_count_t +SndfileHandle::readf (double *ptr, sf_count_t frame_count) +{ return sf_readf_double (p->sf, ptr, frame_count) ; } + +inline sf_count_t +SndfileHandle::writef (const short *ptr, sf_count_t frame_count) +{ return sf_writef_short (p->sf, ptr, frame_count) ; } + +inline sf_count_t +SndfileHandle::writef (const int *ptr, sf_count_t frame_count) +{ return sf_writef_int (p->sf, ptr, frame_count) ; } + +inline sf_count_t +SndfileHandle::writef (const float *ptr, sf_count_t frame_count) +{ return sf_writef_float (p->sf, ptr, frame_count) ; } + +inline sf_count_t +SndfileHandle::writef (const double *ptr, sf_count_t frame_count) +{ return sf_writef_double (p->sf, ptr, frame_count) ; } + +inline sf_count_t +SndfileHandle::readRaw (void *ptr, sf_count_t bytes) +{ return sf_read_raw (p->sf, ptr, bytes) ; } + +inline sf_count_t +SndfileHandle::writeRaw (const void *ptr, sf_count_t bytes) +{ return sf_write_raw (p->sf, ptr, bytes) ; } + + +#endif /* SNDFILE_HH */ + +/* +** Do not edit or modify anything in this comment block. +** The following line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: a0e9d996-73d7-47c4-a78d-79a3232a9eef +*/ diff --git a/src/strings.c b/src/strings.c new file mode 100644 index 00000000..2433f9b0 --- /dev/null +++ b/src/strings.c @@ -0,0 +1,204 @@ +/* +** Copyright (C) 2001-2004 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include + +#include "sndfile.h" +#include "common.h" + +#define STRINGS_DEBUG 0 +#if STRINGS_DEBUG +static void hexdump (void *data, int len) ; +#endif + +int +psf_store_string (SF_PRIVATE *psf, int str_type, const char *str) +{ static char lsf_name [] = PACKAGE "-" VERSION ; + static char bracket_name [] = " (" PACKAGE "-" VERSION ")" ; + int k, str_len, len_remaining, str_flags ; + + if (str == NULL) + return SFE_STR_BAD_STRING ; + + str_len = strlen (str) ; + + /* A few extra checks for write mode. */ + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + { if ((psf->str_flags & SF_STR_ALLOW_START) == 0) + return SFE_STR_NO_SUPPORT ; + if ((psf->str_flags & SF_STR_ALLOW_END) == 0) + return SFE_STR_NO_SUPPORT ; + /* Only allow zero length strings for software. */ + if (str_type != SF_STR_SOFTWARE && str_len == 0) + return SFE_STR_BAD_STRING ; + } ; + + /* Determine flags */ + str_flags = SF_STR_LOCATE_START ; + if (psf->have_written) + { if ((psf->str_flags & SF_STR_ALLOW_END) == 0) + return SFE_STR_NO_ADD_END ; + str_flags = SF_STR_LOCATE_END ; + } ; + + /* Find next free slot in table. */ + for (k = 0 ; k < SF_MAX_STRINGS ; k++) + if (psf->strings [k].type == 0) + break ; + + /* More sanity checking. */ + if (k >= SF_MAX_STRINGS) + return SFE_STR_MAX_COUNT ; + + if (k == 0 && psf->str_end != NULL) + { psf_log_printf (psf, "SFE_STR_WEIRD : k == 0 && psf->str_end != NULL\n") ; + return SFE_STR_WEIRD ; + } ; + + if (k != 0 && psf->str_end == NULL) + { psf_log_printf (psf, "SFE_STR_WEIRD : k != 0 && psf->str_end == NULL\n") ; + return SFE_STR_WEIRD ; + } ; + + /* Special case for the first string. */ + if (k == 0) + psf->str_end = psf->str_storage ; + + +#if STRINGS_DEBUG + psf_log_printf (psf, "str_storage : %X\n", (int) psf->str_storage) ; + psf_log_printf (psf, "str_end : %X\n", (int) psf->str_end) ; + psf_log_printf (psf, "sizeof (str_storage) : %d\n", SIGNED_SIZEOF (psf->str_storage)) ; +#endif + + len_remaining = SIGNED_SIZEOF (psf->str_storage) - (psf->str_end - psf->str_storage) ; + + if (len_remaining < str_len + 2) + return SFE_STR_MAX_DATA ; + + switch (str_type) + { case SF_STR_SOFTWARE : + /* In write mode, want to append libsndfile-version to string. */ + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + { psf->strings [k].type = str_type ; + psf->strings [k].str = psf->str_end ; + psf->strings [k].flags = str_flags ; + + memcpy (psf->str_end, str, str_len + 1) ; + psf->str_end += str_len ; + + /* + ** If the supplied string does not already contain a + ** libsndfile-X.Y.Z component, then add it. + */ + if (strstr (str, PACKAGE) == NULL && len_remaining > (int) (strlen (bracket_name) + str_len + 2)) + { if (strlen (str) == 0) + strncat (psf->str_end, lsf_name, len_remaining) ; + else + strncat (psf->str_end, bracket_name, len_remaining) ; + psf->str_end += strlen (psf->str_end) ; + } ; + + /* Plus one to catch string terminator. */ + psf->str_end += 1 ; + break ; + } ; + + /* Fall though if not write mode. */ + + case SF_STR_TITLE : + case SF_STR_COPYRIGHT : + case SF_STR_ARTIST : + case SF_STR_COMMENT : + case SF_STR_DATE : + psf->strings [k].type = str_type ; + psf->strings [k].str = psf->str_end ; + psf->strings [k].flags = str_flags ; + + /* Plus one to catch string terminator. */ + memcpy (psf->str_end, str, str_len + 1) ; + psf->str_end += str_len + 1 ; + break ; + + default : + return SFE_STR_BAD_TYPE ; + } ; + + psf->str_flags |= (psf->have_written) ? SF_STR_LOCATE_END : SF_STR_LOCATE_START ; + +#if STRINGS_DEBUG + hexdump (psf->str_storage, 300) ; +#endif + + return 0 ; +} /* psf_store_string */ + +int +psf_set_string (SF_PRIVATE *psf, int str_type, const char *str) +{ if (psf->mode == SFM_READ) + return SFE_STR_NOT_WRITE ; + + return psf_store_string (psf, str_type, str) ; +} /* psf_set_string */ + +const char* +psf_get_string (SF_PRIVATE *psf, int str_type) +{ int k ; + + for (k = 0 ; k < SF_MAX_STRINGS ; k++) + if (str_type == psf->strings [k].type) + return psf->strings [k].str ; + + return NULL ; +} /* psf_get_string */ + +#if STRINGS_DEBUG + +#include +static void +hexdump (void *data, int len) +{ unsigned char *ptr ; + int k ; + + ptr = data ; + + puts ("---------------------------------------------------------") ; + while (len >= 16) + { for (k = 0 ; k < 16 ; k++) + printf ("%02X ", ptr [k] & 0xFF) ; + printf (" ") ; + for (k = 0 ; k < 16 ; k++) + printf ("%c", isprint (ptr [k]) ? ptr [k] : '.') ; + puts ("") ; + ptr += 16 ; + len -= 16 ; + } ; +} /* hexdump */ + +#endif +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 04393aa1-9389-46fe-baf2-58a7bd544fd6 +*/ diff --git a/src/svx.c b/src/svx.c new file mode 100644 index 00000000..bc44fe5d --- /dev/null +++ b/src/svx.c @@ -0,0 +1,413 @@ +/* +** Copyright (C) 1999-2004 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include +#include + +#include "sndfile.h" +#include "sfendian.h" +#include "common.h" + + +/*------------------------------------------------------------------------------ + * Macros to handle big/little endian issues. +*/ + +#define FORM_MARKER (MAKE_MARKER ('F', 'O', 'R', 'M')) +#define SVX8_MARKER (MAKE_MARKER ('8', 'S', 'V', 'X')) +#define SV16_MARKER (MAKE_MARKER ('1', '6', 'S', 'V')) +#define VHDR_MARKER (MAKE_MARKER ('V', 'H', 'D', 'R')) +#define BODY_MARKER (MAKE_MARKER ('B', 'O', 'D', 'Y')) + +#define ATAK_MARKER (MAKE_MARKER ('A', 'T', 'A', 'K')) +#define RLSE_MARKER (MAKE_MARKER ('R', 'L', 'S', 'E')) + +#define c_MARKER (MAKE_MARKER ('(', 'c', ')', ' ')) +#define NAME_MARKER (MAKE_MARKER ('N', 'A', 'M', 'E')) +#define AUTH_MARKER (MAKE_MARKER ('A', 'U', 'T', 'H')) +#define ANNO_MARKER (MAKE_MARKER ('A', 'N', 'N', 'O')) +#define CHAN_MARKER (MAKE_MARKER ('C', 'H', 'A', 'N')) + +/*------------------------------------------------------------------------------ + * Typedefs for file chunks. +*/ + +typedef struct +{ unsigned int oneShotHiSamples, repeatHiSamples, samplesPerHiCycle ; + unsigned short samplesPerSec ; + unsigned char octave, compression ; + unsigned int volume ; +} VHDR_CHUNK ; + +enum { + HAVE_FORM = 0x01, + + HAVE_SVX = 0x02, + HAVE_VHDR = 0x04, + HAVE_BODY = 0x08 +} ; + +/*------------------------------------------------------------------------------ + * Private static functions. +*/ + +static int svx_close (SF_PRIVATE *psf) ; +static int svx_write_header (SF_PRIVATE *psf, int calc_length) ; +static int svx_read_header (SF_PRIVATE *psf) ; + +/*------------------------------------------------------------------------------ +** Public function. +*/ + +int +svx_open (SF_PRIVATE *psf) +{ int error ; + + if (psf->mode == SFM_READ || (psf->mode == SFM_RDWR && psf->filelength > 0)) + { if ((error = svx_read_header (psf))) + return error ; + + psf->endian = SF_ENDIAN_BIG ; /* All SVX files are big endian. */ + + psf->blockwidth = psf->sf.channels * psf->bytewidth ; + if (psf->blockwidth) + psf->sf.frames = psf->datalength / psf->blockwidth ; + + psf_fseek (psf, psf->dataoffset, SEEK_SET) ; + } ; + + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + { if (psf->is_pipe) + return SFE_NO_PIPE_WRITE ; + + if ((psf->sf.format & SF_FORMAT_TYPEMASK) != SF_FORMAT_SVX) + return SFE_BAD_OPEN_FORMAT ; + + psf->endian = psf->sf.format & SF_FORMAT_ENDMASK ; + + if (psf->endian == SF_ENDIAN_LITTLE || (CPU_IS_LITTLE_ENDIAN && psf->endian == SF_ENDIAN_CPU)) + return SFE_BAD_ENDIAN ; + + psf->endian = SF_ENDIAN_BIG ; /* All SVX files are big endian. */ + + error = svx_write_header (psf, SF_FALSE) ; + if (error) + return error ; + + psf->write_header = svx_write_header ; + } ; + + psf->container_close = svx_close ; + + if ((error = pcm_init (psf))) + return error ; + + return 0 ; +} /* svx_open */ + +/*------------------------------------------------------------------------------ +*/ + +static int +svx_read_header (SF_PRIVATE *psf) +{ VHDR_CHUNK vhdr ; + unsigned int FORMsize, vhdrsize, dword, marker ; + int filetype = 0, parsestage = 0, done = 0 ; + int bytecount = 0, channels ; + + if (psf->filelength > SF_PLATFORM_S64 (0xffffffff)) + psf_log_printf (psf, "Warning : filelength > 0xffffffff. This is bad!!!!\n") ; + + memset (&vhdr, 0, sizeof (vhdr)) ; + psf_binheader_readf (psf, "p", 0) ; + + /* Set default number of channels. Currently can't handle stereo SVX files. */ + psf->sf.channels = 1 ; + + psf->sf.format = SF_FORMAT_SVX ; + + while (! done) + { psf_binheader_readf (psf, "m", &marker) ; + switch (marker) + { case FORM_MARKER : + if (parsestage) + return SFE_SVX_NO_FORM ; + + psf_binheader_readf (psf, "E4", &FORMsize) ; + + if (FORMsize != psf->filelength - 2 * sizeof (dword)) + { dword = psf->filelength - 2 * sizeof (dword) ; + psf_log_printf (psf, "FORM : %d (should be %d)\n", FORMsize, dword) ; + FORMsize = dword ; + } + else + psf_log_printf (psf, "FORM : %d\n", FORMsize) ; + parsestage |= HAVE_FORM ; + break ; + + case SVX8_MARKER : + case SV16_MARKER : + if (! (parsestage & HAVE_FORM)) + return SFE_SVX_NO_FORM ; + filetype = marker ; + psf_log_printf (psf, " %M\n", marker) ; + parsestage |= HAVE_SVX ; + break ; + + case VHDR_MARKER : + if (! (parsestage & (HAVE_FORM | HAVE_SVX))) + return SFE_SVX_NO_FORM ; + + psf_binheader_readf (psf, "E4", &vhdrsize) ; + + psf_log_printf (psf, " VHDR : %d\n", vhdrsize) ; + + psf_binheader_readf (psf, "E4442114", &(vhdr.oneShotHiSamples), &(vhdr.repeatHiSamples), + &(vhdr.samplesPerHiCycle), &(vhdr.samplesPerSec), &(vhdr.octave), &(vhdr.compression), + &(vhdr.volume)) ; + + psf_log_printf (psf, " OneShotHiSamples : %d\n", vhdr.oneShotHiSamples) ; + psf_log_printf (psf, " RepeatHiSamples : %d\n", vhdr.repeatHiSamples) ; + psf_log_printf (psf, " samplesPerHiCycle : %d\n", vhdr.samplesPerHiCycle) ; + psf_log_printf (psf, " Sample Rate : %d\n", vhdr.samplesPerSec) ; + psf_log_printf (psf, " Octave : %d\n", vhdr.octave) ; + + psf_log_printf (psf, " Compression : %d => ", vhdr.compression) ; + + switch (vhdr.compression) + { case 0 : psf_log_printf (psf, "None.\n") ; + break ; + case 1 : psf_log_printf (psf, "Fibonacci delta\n") ; + break ; + case 2 : psf_log_printf (psf, "Exponential delta\n") ; + break ; + } ; + + psf_log_printf (psf, " Volume : %d\n", vhdr.volume) ; + + psf->sf.samplerate = vhdr.samplesPerSec ; + + if (filetype == SVX8_MARKER) + { psf->sf.format |= SF_FORMAT_PCM_S8 ; + psf->bytewidth = 1 ; + } + else if (filetype == SV16_MARKER) + { psf->sf.format |= SF_FORMAT_PCM_16 ; + psf->bytewidth = 2 ; + } ; + + parsestage |= HAVE_VHDR ; + break ; + + case BODY_MARKER : + if (! (parsestage & HAVE_VHDR)) + return SFE_SVX_NO_BODY ; + + psf_binheader_readf (psf, "E4", &dword) ; + psf->datalength = dword ; + + psf->dataoffset = psf_ftell (psf) ; + + if (psf->datalength > psf->filelength - psf->dataoffset) + { psf_log_printf (psf, " BODY : %D (should be %D)\n", psf->datalength, psf->filelength - psf->dataoffset) ; + psf->datalength = psf->filelength - psf->dataoffset ; + } + else + psf_log_printf (psf, " BODY : %D\n", psf->datalength) ; + + parsestage |= HAVE_BODY ; + + if (! psf->sf.seekable) + break ; + + psf_fseek (psf, psf->datalength, SEEK_CUR) ; + break ; + + case NAME_MARKER : + if (! (parsestage & HAVE_SVX)) + return SFE_SVX_NO_FORM ; + + psf_binheader_readf (psf, "E4", &dword) ; + + psf_log_printf (psf, " %M : %d\n", marker, dword) ; + + if (strlen (psf->filename) != dword) + { if (dword > sizeof (psf->filename) - 1) + return SFE_SVX_BAD_NAME_LENGTH ; + + psf_binheader_readf (psf, "b", psf->filename, dword) ; + psf->filename [dword] = 0 ; + } + else + psf_binheader_readf (psf, "j", dword) ; + break ; + + case ANNO_MARKER : + if (! (parsestage & HAVE_SVX)) + return SFE_SVX_NO_FORM ; + + psf_binheader_readf (psf, "E4", &dword) ; + + psf_log_printf (psf, " %M : %d\n", marker, dword) ; + + psf_binheader_readf (psf, "j", dword) ; + break ; + + case CHAN_MARKER : + if (! (parsestage & HAVE_SVX)) + return SFE_SVX_NO_FORM ; + + psf_binheader_readf (psf, "E4", &dword) ; + + psf_log_printf (psf, " %M : %d\n", marker, dword) ; + + bytecount += psf_binheader_readf (psf, "E4", &channels) ; + psf->sf.channels = channels ; + + psf_log_printf (psf, " Channels : %d\n", channels) ; + + psf_binheader_readf (psf, "j", dword - bytecount) ; + break ; + + + case AUTH_MARKER : + case c_MARKER : + if (! (parsestage & HAVE_SVX)) + return SFE_SVX_NO_FORM ; + + psf_binheader_readf (psf, "E4", &dword) ; + + psf_log_printf (psf, " %M : %d\n", marker, dword) ; + + psf_binheader_readf (psf, "j", dword) ; + break ; + + default : + if (isprint ((marker >> 24) & 0xFF) && isprint ((marker >> 16) & 0xFF) + && isprint ((marker >> 8) & 0xFF) && isprint (marker & 0xFF)) + { psf_binheader_readf (psf, "E4", &dword) ; + + psf_log_printf (psf, "%M : %d (unknown marker)\n", marker, dword) ; + + psf_binheader_readf (psf, "j", dword) ; + break ; + } ; + if ((dword = psf_ftell (psf)) & 0x03) + { psf_log_printf (psf, " Unknown chunk marker at position %d. Resynching.\n", dword - 4) ; + + psf_binheader_readf (psf, "j", -3) ; + break ; + } ; + psf_log_printf (psf, "*** Unknown chunk marker : %X. Exiting parser.\n", marker) ; + done = 1 ; + } ; /* switch (marker) */ + + if (! psf->sf.seekable && (parsestage & HAVE_BODY)) + break ; + + if (psf_ftell (psf) >= psf->filelength - SIGNED_SIZEOF (dword)) + break ; + } ; /* while (1) */ + + if (vhdr.compression) + return SFE_SVX_BAD_COMP ; + + if (psf->dataoffset <= 0) + return SFE_SVX_NO_DATA ; + + return 0 ; +} /* svx_read_header */ + +static int +svx_close (SF_PRIVATE *psf) +{ + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + svx_write_header (psf, SF_TRUE) ; + + return 0 ; +} /* svx_close */ + +static int +svx_write_header (SF_PRIVATE *psf, int calc_length) +{ static char annotation [] = "libsndfile by Erik de Castro Lopo\0\0\0" ; + sf_count_t current ; + + current = psf_ftell (psf) ; + + if (calc_length) + { psf->filelength = psf_get_filelen (psf) ; + + psf->datalength = psf->filelength - psf->dataoffset ; + + if (psf->dataend) + psf->datalength -= psf->filelength - psf->dataend ; + + psf->sf.frames = psf->datalength / (psf->bytewidth * psf->sf.channels) ; + } ; + + psf->header [0] = 0 ; + psf->headindex = 0 ; + psf_fseek (psf, 0, SEEK_SET) ; + + /* FORM marker and FORM size. */ + psf_binheader_writef (psf, "Etm8", FORM_MARKER, (psf->filelength < 8) ? + psf->filelength * 0 : psf->filelength - 8) ; + + psf_binheader_writef (psf, "m", (psf->bytewidth == 1) ? SVX8_MARKER : SV16_MARKER) ; + + /* VHDR chunk. */ + psf_binheader_writef (psf, "Em4", VHDR_MARKER, sizeof (VHDR_CHUNK)) ; + /* VHDR : oneShotHiSamples, repeatHiSamples, samplesPerHiCycle */ + psf_binheader_writef (psf, "E444", psf->sf.frames, 0, 0) ; + /* VHDR : samplesPerSec, octave, compression */ + psf_binheader_writef (psf, "E211", psf->sf.samplerate, 1, 0) ; + /* VHDR : volume */ + psf_binheader_writef (psf, "E4", (psf->bytewidth == 1) ? 0xFF : 0xFFFF) ; + + /* Filename and annotation strings. */ + psf_binheader_writef (psf, "Emsms", NAME_MARKER, psf->filename, ANNO_MARKER, annotation) ; + + /* BODY marker and size. */ + psf_binheader_writef (psf, "Etm8", BODY_MARKER, (psf->datalength < 0) ? + psf->datalength * 0 : psf->datalength) ; + + psf_fwrite (psf->header, psf->headindex, 1, psf) ; + + if (psf->error) + return psf->error ; + + psf->dataoffset = psf->headindex ; + + if (current > 0) + psf_fseek (psf, current, SEEK_SET) ; + + return psf->error ; +} /* svx_write_header */ + + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: a80ab6fb-7d75-4d32-a6b0-0061a3f05d95 +*/ diff --git a/src/test_audio_detect.c b/src/test_audio_detect.c new file mode 100644 index 00000000..c4f15cf3 --- /dev/null +++ b/src/test_audio_detect.c @@ -0,0 +1,126 @@ +/* +** Copyright (C) 2007 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include + +#if HAVE_UNISTD_H +#include +#endif + +#include +#include + +#include "common.h" +#include "sfendian.h" + + +static void test_audio_detect (void) ; + +int +main (void) +{ + test_audio_detect () ; + + return 0 ; +} /* main */ + +/*============================================================================== +** Test data. +*/ + +static unsigned char float_le_mono [] = +{ 0x36, 0x86, 0x21, 0x44, 0xB5, 0xB4, 0x49, 0x44, 0xA2, 0xC0, 0x71, 0x44, 0x7B, 0xD1, 0x8C, 0x44, + 0x54, 0xAA, 0xA0, 0x44, 0x60, 0x67, 0xB4, 0x44, 0x22, 0x05, 0xC8, 0x44, 0x29, 0x80, 0xDB, 0x44, + 0x04, 0xD5, 0xEE, 0x44, 0x27, 0x00, 0x01, 0x45, 0x50, 0x7F, 0x0A, 0x45, 0x53, 0xE6, 0x13, 0x45, + 0x85, 0x33, 0x1D, 0x45, 0x43, 0x65, 0x26, 0x45, 0xEC, 0x79, 0x2F, 0x45, 0xE3, 0x6F, 0x38, 0x45, + 0x98, 0x45, 0x41, 0x45, 0x77, 0xF9, 0x49, 0x45, 0xF6, 0x89, 0x52, 0x45, 0x8F, 0xF5, 0x5A, 0x45, + 0xC9, 0x3A, 0x63, 0x45, 0x28, 0x58, 0x6B, 0x45, 0x3C, 0x4C, 0x73, 0x45, 0x9F, 0x15, 0x7B, 0x45, + 0x75, 0x59, 0x81, 0x45, 0x64, 0x11, 0x85, 0x45, 0xF1, 0xB1, 0x88, 0x45, 0x78, 0x3A, 0x8C, 0x45, + 0x58, 0xAA, 0x8F, 0x45, 0xF2, 0x00, 0x93, 0x45, 0xB2, 0x3D, 0x96, 0x45, 0x01, 0x60, 0x99, 0x45, + 0x50, 0x67, 0x9C, 0x45, 0x15, 0x53, 0x9F, 0x45, 0xCC, 0x22, 0xA2, 0x45, 0xF0, 0xD5, 0xA4, 0x45, + 0x07, 0x6C, 0xA7, 0x45, 0x9C, 0xE4, 0xA9, 0x45, 0x3D, 0x3F, 0xAC, 0x45, 0x7A, 0x7B, 0xAE, 0x45, + 0xF2, 0x98, 0xB0, 0x45, 0x3C, 0x97, 0xB2, 0x45, 0x02, 0x76, 0xB4, 0x45, 0xEC, 0x34, 0xB6, 0x45, + 0xA8, 0xD3, 0xB7, 0x45, 0xEB, 0x51, 0xB9, 0x45, 0x6F, 0xAF, 0xBA, 0x45, 0xF5, 0xEB, 0xBB, 0x45, + 0x41, 0x07, 0xBD, 0x45, 0x21, 0x01, 0xBE, 0x45, 0x64, 0xD9, 0xBE, 0x45, 0xE3, 0x8F, 0xBF, 0x45, + 0x7E, 0x24, 0xC0, 0x45, 0x15, 0x97, 0xC0, 0x45, 0x92, 0xE7, 0xC0, 0x45, 0xE8, 0x15, 0xC1, 0x45, + 0x7E, 0x24, 0xC0, 0x45, 0x15, 0x97, 0xC0, 0x45, 0x92, 0xE7, 0xC0, 0x45, 0xE8, 0x15, 0xC1, 0x45, + 0x7E, 0x24, 0xC0, 0x45, 0x15, 0x97, 0xC0, 0x45, 0x92, 0xE7, 0xC0, 0x45, 0xE8, 0x15, 0xC1, 0x45, +} ; + +static unsigned char int24_32_le_stereo [] = +{ + 0x00, 0xE7, 0xFB, 0xFF, 0x00, 0x7C, 0xFD, 0xFF, 0x00, 0xA2, 0xFC, 0xFF, 0x00, 0x2B, 0xFC, 0xFF, + 0x00, 0xF3, 0xFD, 0xFF, 0x00, 0x19, 0xFB, 0xFF, 0x00, 0xA5, 0xFE, 0xFF, 0x00, 0x8D, 0xFA, 0xFF, + 0x00, 0x91, 0xFF, 0xFF, 0x00, 0xB5, 0xFA, 0xFF, 0x00, 0x91, 0x00, 0x00, 0x00, 0x5E, 0xFB, 0xFF, + 0x00, 0xD9, 0x01, 0x00, 0x00, 0x82, 0xFB, 0xFF, 0x00, 0xDF, 0x03, 0x00, 0x00, 0x44, 0xFC, 0xFF, + 0x00, 0x1C, 0x05, 0x00, 0x00, 0x77, 0xFC, 0xFF, 0x00, 0x8D, 0x06, 0x00, 0x00, 0x4F, 0xFC, 0xFF, + 0x00, 0x84, 0x07, 0x00, 0x00, 0x74, 0xFC, 0xFF, 0x00, 0x98, 0x08, 0x00, 0x00, 0x33, 0xFD, 0xFF, + 0x00, 0xB9, 0x09, 0x00, 0x00, 0x48, 0xFF, 0xFF, 0x00, 0xD1, 0x0A, 0x00, 0x00, 0x10, 0x02, 0x00, + 0x00, 0x28, 0x0C, 0x00, 0x00, 0xA2, 0x05, 0x00, 0x00, 0xA7, 0x0C, 0x00, 0x00, 0x45, 0x08, 0x00, + 0x00, 0x44, 0x0D, 0x00, 0x00, 0x1A, 0x0A, 0x00, 0x00, 0x65, 0x0D, 0x00, 0x00, 0x51, 0x0B, 0x00, + 0x00, 0x8B, 0x0D, 0x00, 0x00, 0x18, 0x0B, 0x00, 0x00, 0x37, 0x0E, 0x00, 0x00, 0x24, 0x0B, 0x00, + 0x00, 0x00, 0x0F, 0x00, 0x00, 0xDD, 0x0A, 0x00, 0x00, 0x83, 0x10, 0x00, 0x00, 0x31, 0x0A, 0x00, + 0x00, 0x07, 0x12, 0x00, 0x00, 0xC0, 0x08, 0x00, 0x00, 0xF7, 0x12, 0x00, 0x00, 0x47, 0x06, 0x00, + 0x00, 0xDD, 0x12, 0x00, 0x00, 0x6A, 0x03, 0x00, 0x00, 0xD5, 0x11, 0x00, 0x00, 0x99, 0x00, 0x00, + 0x00, 0x01, 0x10, 0x00, 0x00, 0xC5, 0xFE, 0xFF, 0x00, 0xF4, 0x0D, 0x00, 0x00, 0x97, 0xFD, 0xFF, + 0x00, 0x62, 0x0B, 0x00, 0x00, 0x75, 0xFC, 0xFF, 0x00, 0xE9, 0x08, 0x00, 0x00, 0xC0, 0xFB, 0xFF, + 0x00, 0x80, 0x06, 0x00, 0x00, 0x3C, 0xFB, 0xFF, 0x00, 0xDA, 0x03, 0x00, 0x00, 0xE4, 0xFA, 0xFF, + 0x00, 0xEB, 0x01, 0x00, 0x00, 0x21, 0xFB, 0xFF, 0x00, 0x20, 0x00, 0x00, 0x00, 0xE7, 0xFB, 0xFF, +} ; + + +static void +test_audio_detect (void) +{ + SF_PRIVATE psf ; + AUDIO_DETECT ad ; + int errors = 0 ; + + printf (" test_audio_detect : ") ; + fflush (stdout) ; + + memset (&psf, 0, sizeof (psf)) ; + + ad.endianness = SF_ENDIAN_LITTLE ; + ad.channels = 1 ; + if (audio_detect (&psf, &ad, float_le_mono, sizeof (float_le_mono)) != SF_FORMAT_FLOAT) + { if (errors == 0) puts ("\nFailed tests :\n") ; + puts (" float_le_mono") ; + errors ++ ; + } ; + + ad.endianness = SF_ENDIAN_LITTLE ; + ad.channels = 2 ; + if (audio_detect (&psf, &ad, int24_32_le_stereo, sizeof (int24_32_le_stereo)) != SF_FORMAT_PCM_32) + { if (errors == 0) puts ("\nFailed tests :\n") ; + puts (" int24_32_le_stereo") ; + errors ++ ; + } ; + + if (errors != 0) + { printf ("\n Errors : %d\n\n", errors) ; + exit (1) ; + } ; + + puts ("ok") ; + + return ; +} /* test_audio_detect */ diff --git a/src/test_conversions.c b/src/test_conversions.c new file mode 100644 index 00000000..e49edd8e --- /dev/null +++ b/src/test_conversions.c @@ -0,0 +1,101 @@ +/* +** Copyright (C) 2006,2007 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include +#include +#include + +#include "common.h" + +/* +** This is a bit rough, but it is the nicest way to do it. +*/ + +#define cmp_test(line,ival,tval,str) \ + if (ival != tval) \ + { printf (str, line, ival, tval) ; \ + exit (1) ; \ + } ; + +static void conversion_test (char endian) ; + +int +main (void) +{ + conversion_test ('E') ; + conversion_test ('e') ; + return 0 ; +} /* main */ + +static void +conversion_test (char endian) +{ + SF_PRIVATE sf_private, *psf ; + const char * filename = "conversion.bin" ; + long long i64 = SF_PLATFORM_S64 (0x0123456789abcdef), t64 = 0 ; + char format_str [16] ; + char i8 = 12, t8 = 0 ; + short i16 = 0x123, t16 = 0 ; + int i24 = 0x23456, t24 = 0 ; + int i32 = 0x0a0b0c0d, t32 = 0 ; + int bytes ; + + snprintf (format_str, sizeof (format_str), "%c12348", endian) ; + + printf (" conversion_test_%-8s : ", endian == 'e' ? "le" : "be") ; + fflush (stdout) ; + + psf = &sf_private ; + memset (psf, 0, sizeof (sf_private)) ; + + if (psf_fopen (psf, filename, SFM_WRITE) != 0) + { printf ("\n\nError : failed to open file '%s' for write.\n\n", filename) ; + exit (1) ; + } ; + + psf_binheader_writef (psf, format_str, i8, i16, i24, i32, i64) ; + psf_fwrite (psf->header, 1, psf->headindex, psf) ; + psf_fclose (psf) ; + + memset (psf, 0, sizeof (sf_private)) ; + if (psf_fopen (psf, filename, SFM_READ) != 0) + { printf ("\n\nError : failed to open file '%s' for read.\n\n", filename) ; + exit (1) ; + } ; + + bytes = psf_binheader_readf (psf, format_str, &t8, &t16, &t24, &t32, &t64) ; + psf_fclose (psf) ; + + if (bytes != 18) + { printf ("\n\nLine %d : read %d bytes.\n\n", __LINE__, bytes) ; + exit (1) ; + } ; + + cmp_test (__LINE__, i8, t8, "\n\nLine %d : 8 bit int failed %d -> %d.\n\n") ; + cmp_test (__LINE__, i16, t16, "\n\nLine %d : 16 bit int failed 0x%x -> 0x%x.\n\n") ; + cmp_test (__LINE__, i24, t24, "\n\nLine %d : 24 bit int failed 0x%x -> 0x%x.\n\n") ; + cmp_test (__LINE__, i32, t32, "\n\nLine %d : 32 bit int failed 0x%x -> 0x%x.\n\n") ; + cmp_test (__LINE__, i64, t64, "\n\nLine %d : 64 bit int failed 0x%llx -> 0x%llx.\n\n") ; + + remove (filename) ; + puts ("ok") ; +} /* conversion_test */ diff --git a/src/test_endswap.def b/src/test_endswap.def new file mode 100644 index 00000000..5fbc49b7 --- /dev/null +++ b/src/test_endswap.def @@ -0,0 +1,28 @@ +autogen definitions test_endswap.tpl; + +int_type = { + name = short ; + value = 0x3210 ; + format = FMT_SHORT ; + } ; + +int_type = { + name = int ; + value = 0x76543210 ; + format = FMT_INT ; + } ; + +int_type = { + name = int64_t ; + value = "0x0807050540302010LL" ; + format = FMT_INT64 ; + } ; + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 098c07e0-2d3f-4f62-ad93-f3f4b91c5211 +*/ + diff --git a/src/test_endswap.tpl b/src/test_endswap.tpl new file mode 100644 index 00000000..e3265b37 --- /dev/null +++ b/src/test_endswap.tpl @@ -0,0 +1,135 @@ +[+ AutoGen5 template c +] +/* +** Copyright (C) 2002-2005 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include + +#if HAVE_UNISTD_H +#include +#endif + +#include +#include + +#include "common.h" +#include "sfendian.h" + +#define FMT_SHORT "0x%04x\n" +#define FMT_INT "0x%08x\n" + +#if SIZEOF_INT64_T == SIZEOF_LONG +#define FMT_INT64 "0x%016lx\n" +#else +#define FMT_INT64 "0x%016llx\n" +#endif + +[+ FOR int_type ++]static void test_endswap_[+ (get "name") +] (void) ; +[+ ENDFOR int_type ++] + +int +main (void) +{ +[+ FOR int_type ++] test_endswap_[+ (get "name") +] () ; +[+ ENDFOR int_type ++] + return 0 ; +} /* main */ + +/*============================================================================== +** Actual test functions. +*/ + +[+ FOR int_type +] +static void +dump_[+ (get "name") +]_array (const char * name, [+ (get "name") +] * data, int datalen) +{ int k ; + + printf ("%-6s : ", name) ; + for (k = 0 ; k < datalen ; k++) + printf ([+ (get "format") +], data [k]) ; + putchar ('\n') ; +} /* dump_[+ (get "name") +]_array */ + +static void +test_endswap_[+ (get "name") +] (void) +{ [+ (get "name") +] orig [4], first [4], second [4] ; + int k ; + + printf (" %-24s : ", "test_endswap_[+ (get "name") +]") ; + fflush (stdout) ; + + for (k = 0 ; k < ARRAY_LEN (orig) ; k++) + orig [k] = [+ (get "value") +] + k ; + + endswap_[+ (get "name") +]_copy (first, orig, ARRAY_LEN (first)) ; + endswap_[+ (get "name") +]_copy (second, first, ARRAY_LEN (second)) ; + + if (memcmp (orig, first, sizeof (orig)) == 0) + { printf ("\n\nLine %d : test 1 : these two array should not be the same:\n\n", __LINE__) ; + dump_[+ (get "name") +]_array ("orig", orig, ARRAY_LEN (orig)) ; + dump_[+ (get "name") +]_array ("first", first, ARRAY_LEN (first)) ; + exit (1) ; + } ; + + if (memcmp (orig, second, sizeof (orig)) != 0) + { printf ("\n\nLine %d : test 2 : these two array should be the same:\n\n", __LINE__) ; + dump_[+ (get "name") +]_array ("orig", orig, ARRAY_LEN (orig)) ; + dump_[+ (get "name") +]_array ("second", second, ARRAY_LEN (second)) ; + exit (1) ; + } ; + + endswap_[+ (get "name") +]_array (first, ARRAY_LEN (first)) ; + + if (memcmp (orig, first, sizeof (orig)) != 0) + { printf ("\n\nLine %d : test 3 : these two array should be the same:\n\n", __LINE__) ; + dump_[+ (get "name") +]_array ("orig", orig, ARRAY_LEN (orig)) ; + dump_[+ (get "name") +]_array ("first", first, ARRAY_LEN (first)) ; + exit (1) ; + } ; + + endswap_[+ (get "name") +]_copy (first, orig, ARRAY_LEN (first)) ; + endswap_[+ (get "name") +]_copy (first, first, ARRAY_LEN (first)) ; + + if (memcmp (orig, first, sizeof (orig)) != 0) + { printf ("\n\nLine %d : test 4 : these two array should be the same:\n\n", __LINE__) ; + dump_[+ (get "name") +]_array ("orig", orig, ARRAY_LEN (orig)) ; + dump_[+ (get "name") +]_array ("first", first, ARRAY_LEN (first)) ; + exit (1) ; + } ; + + puts ("ok") ; +} /* test_endswap_[+ (get "name") +] */ +[+ ENDFOR int_type ++] + + +[+ COMMENT +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: fd8887e8-8202-4f30-a419-0cf01a0e799b +*/ ++] diff --git a/src/test_file_io.c b/src/test_file_io.c new file mode 100644 index 00000000..1d7f5914 --- /dev/null +++ b/src/test_file_io.c @@ -0,0 +1,448 @@ +/* +** Copyright (C) 2002-2006 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include + +#if HAVE_UNISTD_H +#include +#endif + +#include +#include + +#include "common.h" + +static void make_data (int *data, int len, int seed) ; + +static void file_open_test (const char *filename) ; +static void file_read_write_test (const char *filename) ; +static void file_truncate_test (const char *filename) ; + +static void test_open_or_die (SF_PRIVATE *psf, int linenum) ; +static void test_close_or_die (SF_PRIVATE *psf, int linenum) ; + +static void test_write_or_die (SF_PRIVATE *psf, void *data, sf_count_t bytes, sf_count_t items, sf_count_t new_position, int linenum) ; +static void test_read_or_die (SF_PRIVATE *psf, void *data, sf_count_t bytes, sf_count_t items, sf_count_t new_position, int linenum) ; +static void test_equal_or_die (int *array1, int *array2, int len, int linenum) ; +static void test_seek_or_die (SF_PRIVATE *psf, sf_count_t offset, int whence, sf_count_t new_position, int linenum) ; + + + +int +main (void) +{ const char *filename = "file_io.dat" ; + + file_open_test (filename) ; + file_read_write_test (filename) ; + file_truncate_test (filename) ; + + unlink (filename) ; + + return 0 ; +} /* main */ + +/*============================================================================== +** Actual test functions. +*/ + +static void +file_open_test (const char *filename) +{ SF_PRIVATE sf_data, *psf ; + int error ; + + printf (" %-24s : ", "file_open_test") ; + fflush (stdout) ; + + memset (&sf_data, 0, sizeof (sf_data)) ; + psf = &sf_data ; + + /* Ensure that the file doesn't already exist. */ + if (unlink (filename) != 0 && errno != ENOENT) + { printf ("\n\nLine %d: unlink failed (%d) : %s\n\n", __LINE__, errno, strerror (errno)) ; + exit (1) ; + } ; + + strncpy (psf->filename, filename, sizeof (psf->filename)) ; + + /* Test that open for read fails if the file doesn't exist. */ + error = psf_fopen (psf, psf->filename, SFM_READ) ; + if (error == 0) + { printf ("\n\nLine %d: psf_fopen() should have failed.\n\n", __LINE__) ; + exit (1) ; + } ; + + /* Reset error to zero. */ + psf->error = SFE_NO_ERROR ; + + /* Test file open in write mode. */ + psf->mode = SFM_WRITE ; + test_open_or_die (psf, __LINE__) ; + + test_close_or_die (psf, __LINE__) ; + + unlink (psf->filename) ; + + /* Test file open in read/write mode for a non-existant file. */ + psf->mode = SFM_RDWR ; + test_open_or_die (psf, __LINE__) ; + + test_close_or_die (psf, __LINE__) ; + + /* Test file open in read/write mode for an existing file. */ + psf->mode = SFM_RDWR ; + test_open_or_die (psf, __LINE__) ; + + test_close_or_die (psf, __LINE__) ; + + unlink (psf->filename) ; + puts ("ok") ; +} /* file_open_test */ + +static void +file_read_write_test (const char *filename) +{ static int data_out [512] ; + static int data_in [512] ; + + SF_PRIVATE sf_data, *psf ; + sf_count_t retval ; + + /* + ** Open a new file and write two blocks of data to the file. After each + ** write, test that psf_get_filelen() returns the new length. + */ + + printf (" %-24s : ", "file_write_test") ; + fflush (stdout) ; + + memset (&sf_data, 0, sizeof (sf_data)) ; + psf = &sf_data ; + strncpy (psf->filename, filename, sizeof (psf->filename)) ; + + /* Test file open in write mode. */ + psf->mode = SFM_WRITE ; + test_open_or_die (psf, __LINE__) ; + + make_data (data_out, ARRAY_LEN (data_out), 1) ; + test_write_or_die (psf, data_out, sizeof (data_out [0]), ARRAY_LEN (data_out), sizeof (data_out), __LINE__) ; + + if ((retval = psf_get_filelen (psf)) != sizeof (data_out)) + { printf ("\n\nLine %d: file length after write is not correct (%ld should be %d).\n\n", __LINE__, (long) retval, (int) sizeof (data_out)) ; + if (retval == 0) + printf ("An fsync() may be necessary before fstat() in psf_get_filelen().\n\n") ; + exit (1) ; + } ; + + make_data (data_out, ARRAY_LEN (data_out), 2) ; + test_write_or_die (psf, data_out, ARRAY_LEN (data_out), sizeof (data_out [0]), 2 * sizeof (data_out), __LINE__) ; + + if ((retval = psf_get_filelen (psf)) != 2 * sizeof (data_out)) + { printf ("\n\nLine %d: file length after write is not correct. (%ld should be %d)\n\n", __LINE__, (long) retval, 2 * ((int) sizeof (data_out))) ; + exit (1) ; + } ; + + test_close_or_die (psf, __LINE__) ; + puts ("ok") ; + + /* + ** Now open the file in read mode, check the file length and check + ** that the data is correct. + */ + + printf (" %-24s : ", "file_read_test") ; + fflush (stdout) ; + + /* Test file open in write mode. */ + psf->mode = SFM_READ ; + test_open_or_die (psf, __LINE__) ; + + make_data (data_out, ARRAY_LEN (data_out), 1) ; + test_read_or_die (psf, data_in, 1, sizeof (data_in), sizeof (data_in), __LINE__) ; + test_equal_or_die (data_out, data_in, ARRAY_LEN (data_out), __LINE__) ; + + make_data (data_out, ARRAY_LEN (data_out), 2) ; + test_read_or_die (psf, data_in, sizeof (data_in [0]), ARRAY_LEN (data_in), 2 * sizeof (data_in), __LINE__) ; + test_equal_or_die (data_out, data_in, ARRAY_LEN (data_out), __LINE__) ; + + test_close_or_die (psf, __LINE__) ; + + puts ("ok") ; + + /* + ** Open the file in read/write mode, seek around a bit and then seek to + ** the end of the file and write another block of data (3rd block). Then + ** go back and check that all three blocks are correct. + */ + + printf (" %-24s : ", "file_seek_test") ; + fflush (stdout) ; + + /* Test file open in read/write mode. */ + psf->mode = SFM_RDWR ; + test_open_or_die (psf, __LINE__) ; + + test_seek_or_die (psf, 0, SEEK_SET, 0, __LINE__) ; + test_seek_or_die (psf, 0, SEEK_END, 2 * SIGNED_SIZEOF (data_out), __LINE__) ; + test_seek_or_die (psf, -1 * SIGNED_SIZEOF (data_out), SEEK_CUR, (sf_count_t) sizeof (data_out), __LINE__) ; + + test_seek_or_die (psf, SIGNED_SIZEOF (data_out), SEEK_CUR, 2 * SIGNED_SIZEOF (data_out), __LINE__) ; + make_data (data_out, ARRAY_LEN (data_out), 3) ; + test_write_or_die (psf, data_out, sizeof (data_out [0]), ARRAY_LEN (data_out), 3 * sizeof (data_out), __LINE__) ; + + test_seek_or_die (psf, 0, SEEK_SET, 0, __LINE__) ; + make_data (data_out, ARRAY_LEN (data_out), 1) ; + test_read_or_die (psf, data_in, 1, sizeof (data_in), sizeof (data_in), __LINE__) ; + test_equal_or_die (data_out, data_in, ARRAY_LEN (data_out), __LINE__) ; + + test_seek_or_die (psf, 2 * SIGNED_SIZEOF (data_out), SEEK_SET, 2 * SIGNED_SIZEOF (data_out), __LINE__) ; + make_data (data_out, ARRAY_LEN (data_out), 3) ; + test_read_or_die (psf, data_in, 1, sizeof (data_in), 3 * sizeof (data_in), __LINE__) ; + test_equal_or_die (data_out, data_in, ARRAY_LEN (data_out), __LINE__) ; + + test_seek_or_die (psf, SIGNED_SIZEOF (data_out), SEEK_SET, SIGNED_SIZEOF (data_out), __LINE__) ; + make_data (data_out, ARRAY_LEN (data_out), 2) ; + test_read_or_die (psf, data_in, 1, sizeof (data_in), 2 * sizeof (data_in), __LINE__) ; + test_equal_or_die (data_out, data_in, ARRAY_LEN (data_out), __LINE__) ; + + test_close_or_die (psf, __LINE__) ; + puts ("ok") ; + + /* + ** Now test operations with a non-zero psf->fileoffset field. This field + ** sets an artificial file start positions so that a seek to the start of + ** the file will actually be a seek to the value given by psf->fileoffset. + */ + + printf (" %-24s : ", "file_offset_test") ; + fflush (stdout) ; + + /* Test file open in read/write mode. */ + psf->mode = SFM_RDWR ; + psf->fileoffset = sizeof (data_out [0]) * ARRAY_LEN (data_out) ; + test_open_or_die (psf, __LINE__) ; + + if ((retval = psf_get_filelen (psf)) != 3 * sizeof (data_out)) + { printf ("\n\nLine %d: file length after write is not correct. (%ld should be %d)\n\n", __LINE__, (long) retval, 3 * ((int) sizeof (data_out))) ; + exit (1) ; + } ; + + test_seek_or_die (psf, SIGNED_SIZEOF (data_out), SEEK_SET, SIGNED_SIZEOF (data_out), __LINE__) ; + make_data (data_out, ARRAY_LEN (data_out), 5) ; + test_write_or_die (psf, data_out, sizeof (data_out [0]), ARRAY_LEN (data_out), 2 * sizeof (data_out), __LINE__) ; + test_close_or_die (psf, __LINE__) ; + + /* final test with psf->fileoffset == 0. */ + + psf->mode = SFM_RDWR ; + psf->fileoffset = 0 ; + test_open_or_die (psf, __LINE__) ; + + if ((retval = psf_get_filelen (psf)) != 3 * sizeof (data_out)) + { printf ("\n\nLine %d: file length after write is not correct. (%ld should be %d)\n\n", __LINE__, (long) retval, 3 * ((int) sizeof (data_out))) ; + exit (1) ; + } ; + + make_data (data_out, ARRAY_LEN (data_out), 1) ; + test_read_or_die (psf, data_in, 1, sizeof (data_in), sizeof (data_in), __LINE__) ; + test_equal_or_die (data_out, data_in, ARRAY_LEN (data_out), __LINE__) ; + + make_data (data_out, ARRAY_LEN (data_out), 2) ; + test_read_or_die (psf, data_in, 1, sizeof (data_in), 2 * sizeof (data_in), __LINE__) ; + test_equal_or_die (data_out, data_in, ARRAY_LEN (data_out), __LINE__) ; + + make_data (data_out, ARRAY_LEN (data_out), 5) ; + test_read_or_die (psf, data_in, 1, sizeof (data_in), 3 * sizeof (data_in), __LINE__) ; + test_equal_or_die (data_out, data_in, ARRAY_LEN (data_out), __LINE__) ; + + test_close_or_die (psf, __LINE__) ; + + puts ("ok") ; +} /* file_read_write_test */ + +static void +file_truncate_test (const char *filename) +{ SF_PRIVATE sf_data, *psf ; + unsigned char buffer [256] ; + int k ; + + /* + ** Open a new file and write two blocks of data to the file. After each + ** write, test that psf_get_filelen() returns the new length. + */ + + printf (" %-24s : ", "file_truncate_test") ; + fflush (stdout) ; + + memset (&sf_data, 0, sizeof (sf_data)) ; + memset (buffer, 0xEE, sizeof (buffer)) ; + + psf = &sf_data ; + strncpy (psf->filename, filename, sizeof (psf->filename)) ; + + /* + ** Open the file write mode, write 0xEE data and then extend the file + ** using truncate (the extended data should be 0x00). + */ + psf->mode = SFM_WRITE ; + test_open_or_die (psf, __LINE__) ; + test_write_or_die (psf, buffer, sizeof (buffer) / 2, 1, sizeof (buffer) / 2, __LINE__) ; + psf_ftruncate (psf, sizeof (buffer)) ; + test_close_or_die (psf, __LINE__) ; + + /* Open the file in read mode and check the data. */ + psf->mode = SFM_READ ; + test_open_or_die (psf, __LINE__) ; + test_read_or_die (psf, buffer, sizeof (buffer), 1, sizeof (buffer), __LINE__) ; + test_close_or_die (psf, __LINE__) ; + + for (k = 0 ; k < SIGNED_SIZEOF (buffer) / 2 ; k++) + if (buffer [k] != 0xEE) + { printf ("\n\nLine %d : buffer [%d] = %d (should be 0xEE)\n\n", __LINE__, k, buffer [k]) ; + exit (1) ; + } ; + + for (k = SIGNED_SIZEOF (buffer) / 2 ; k < SIGNED_SIZEOF (buffer) ; k++) + if (buffer [k] != 0) + { printf ("\n\nLine %d : buffer [%d] = %d (should be 0)\n\n", __LINE__, k, buffer [k]) ; + exit (1) ; + } ; + + /* Open the file in read/write and shorten the file using truncate. */ + psf->mode = SFM_RDWR ; + test_open_or_die (psf, __LINE__) ; + psf_ftruncate (psf, sizeof (buffer) / 4) ; + test_close_or_die (psf, __LINE__) ; + + /* Check the file length. */ + psf->mode = SFM_READ ; + test_open_or_die (psf, __LINE__) ; + test_seek_or_die (psf, 0, SEEK_END, SIGNED_SIZEOF (buffer) / 4, __LINE__) ; + test_close_or_die (psf, __LINE__) ; + + puts ("ok") ; +} /* file_truncate_test */ + +/*============================================================================== +** Testing helper functions. +*/ + +static void +test_open_or_die (SF_PRIVATE *psf, int linenum) +{ int error ; + + /* Test that open for read fails if the file doesn't exist. */ + error = psf_fopen (psf, psf->filename, psf->mode) ; + if (error) + { printf ("\n\nLine %d: psf_fopen() failed : %s\n\n", linenum, strerror (errno)) ; + exit (1) ; + } ; + +} /* test_open_or_die */ + +static void +test_close_or_die (SF_PRIVATE *psf, int linenum) +{ + psf_fclose (psf) ; + if (psf_file_valid (psf)) + { printf ("\n\nLine %d: psf->filedes should not be valid.\n\n", linenum) ; + exit (1) ; + } ; + +} /* test_close_or_die */ + +static void +test_write_or_die (SF_PRIVATE *psf, void *data, sf_count_t bytes, sf_count_t items, sf_count_t new_position, int linenum) +{ sf_count_t retval ; + + retval = psf_fwrite (data, bytes, items, psf) ; + if (retval != items) + { printf ("\n\nLine %d: psf_write() returned %ld (should be %ld)\n\n", linenum, (long) retval, (long) items) ; + exit (1) ; + } ; + + if ((retval = psf_ftell (psf)) != new_position) + { printf ("\n\nLine %d: file length after write is not correct. (%ld should be %ld)\n\n", linenum, (long) retval, (long) new_position) ; + exit (1) ; + } ; + + return ; +} /* test_write_or_die */ + +static void +test_read_or_die (SF_PRIVATE *psf, void *data, sf_count_t bytes, sf_count_t items, sf_count_t new_position, int linenum) +{ sf_count_t retval ; + + retval = psf_fread (data, bytes, items, psf) ; + if (retval != items) + { printf ("\n\nLine %d: psf_write() returned %ld (should be %ld)\n\n", linenum, (long) retval, (long) items) ; + exit (1) ; + } ; + + if ((retval = psf_ftell (psf)) != new_position) + { printf ("\n\nLine %d: file length after write is not correct. (%ld should be %ld)\n\n", linenum, (long) retval, (long) new_position) ; + exit (1) ; + } ; + + return ; +} /* test_write_or_die */ + +static void +test_seek_or_die (SF_PRIVATE *psf, sf_count_t offset, int whence, sf_count_t new_position, int linenum) +{ sf_count_t retval ; + + retval = psf_fseek (psf, offset, whence) ; + + if (retval != new_position) + { printf ("\n\nLine %d: psf_fseek() failed. New position is %ld (should be %ld).\n\n", + linenum, (long) retval, (long) new_position) ; + exit (1) ; + } ; + +} /* test_seek_or_die */ + +static void +test_equal_or_die (int *array1, int *array2, int len, int linenum) +{ int k ; + + for (k = 0 ; k < len ; k++) + if (array1 [k] != array2 [k]) + printf ("\n\nLine %d: error at index %d (%d != %d).\n\n", + linenum, k, array1 [k], array2 [k]) ; + + return ; +} /* test_equal_or_die */ + +static void +make_data (int *data, int len, int seed) +{ int k ; + + srand (seed * 3333333 + 14756123) ; + + for (k = 0 ; k < len ; k++) + data [k] = rand () ; + +} /* make_data */ +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 0a21fb93-78dd-4f72-8338-4bca9e77b8c8 +*/ diff --git a/src/test_log_printf.c b/src/test_log_printf.c new file mode 100644 index 00000000..0221b6f5 --- /dev/null +++ b/src/test_log_printf.c @@ -0,0 +1,133 @@ +/* +** Copyright (C) 2003-2005 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include +#include +#include + +#include "common.h" + +#define CMP_0_ARGS(line,err,fmt) \ + { psf->logindex = 0 ; \ + LSF_SNPRINTF (buffer, sizeof (buffer), (fmt)) ; \ + psf_log_printf (psf, (fmt)) ; \ + err += compare_strings_or_die (line, fmt, buffer, psf->logbuffer) ; \ + } + +#define CMP_2_ARGS(line,err,fmt,a) \ + { psf->logindex = 0 ; \ + LSF_SNPRINTF (buffer, sizeof (buffer), (fmt), (a), (a)) ; \ + psf_log_printf (psf, (fmt), (a), (a)) ; \ + err += compare_strings_or_die (line, fmt, buffer, psf->logbuffer) ; \ + } + +#define CMP_4_ARGS(line,err,fmt,a) \ + { psf->logindex = 0 ; \ + LSF_SNPRINTF (buffer, sizeof (buffer), (fmt), (a), (a), (a), (a)) ; \ + psf_log_printf (psf, (fmt), (a), (a), (a), (a)) ; \ + err += compare_strings_or_die (line, fmt, buffer, psf->logbuffer) ; \ + } + +#define CMP_5_ARGS(line,err,fmt,a) \ + { psf->logindex = 0 ; \ + LSF_SNPRINTF (buffer, sizeof (buffer), (fmt), (a), (a), (a), (a), (a)) ; \ + psf_log_printf (psf, (fmt), (a), (a), (a), (a), (a)) ; \ + err += compare_strings_or_die (line, fmt, buffer, psf->logbuffer) ; \ + } + +#define CMP_6_ARGS(line,err,fmt,a) \ + { psf->logindex = 0 ; \ + LSF_SNPRINTF (buffer, sizeof (buffer), (fmt), (a), (a), (a), (a), (a), (a)) ; \ + psf_log_printf (psf, (fmt), (a), (a), (a), (a), (a), (a)) ; \ + err += compare_strings_or_die (line, fmt, buffer, psf->logbuffer) ; \ + } + +static int compare_strings_or_die (int linenum, const char *fmt, const char* s1, const char* s3) ; + +int +main (void) +{ static char buffer [2048] ; + SF_PRIVATE sf_private, *psf ; + int k, errors = 0 ; + int int_values [] = { 0, 1, 12, 123, 1234, 123456, -1, -12, -123, -1234, -123456 } ; + + printf (" %-24s : ", "psf_log_printf_test") ; + fflush (stdout) ; + + psf = &sf_private ; + memset (psf, 0, sizeof (sf_private)) ; + + CMP_0_ARGS (__LINE__, errors, " ->%%<- ") ; + + /* Test printing of ints. */ + for (k = 0 ; k < ARRAY_LEN (int_values) ; k++) + CMP_6_ARGS (__LINE__, errors, "int A : %d, % d, %4d, % 4d, %04d, % 04d", int_values [k]) ; + + for (k = 0 ; k < ARRAY_LEN (int_values) ; k++) + CMP_5_ARGS (__LINE__, errors, "int B : %+d, %+4d, %+04d, %-d, %-4d", int_values [k]) ; + + for (k = 0 ; k < ARRAY_LEN (int_values) ; k++) + CMP_2_ARGS (__LINE__, errors, "int C : %- d, %- 4d", int_values [k]) ; + + /* Test printing of unsigned ints. */ + for (k = 0 ; k < ARRAY_LEN (int_values) ; k++) + CMP_4_ARGS (__LINE__, errors, "D : %u, %4u, %04u, %0u", int_values [k]) ; + + /* Test printing of hex ints. */ + for (k = 0 ; k < ARRAY_LEN (int_values) ; k++) + CMP_4_ARGS (__LINE__, errors, "E : %X, %4X, %04X, %0X", int_values [k]) ; + + /* Test printing of strings. */ + CMP_4_ARGS (__LINE__, errors, "B %s, %3s, %8s, %-8s", "str") ; + + if (errors) + { puts ("\nExiting due to errors.\n") ; + exit (1) ; + } ; + + puts ("ok") ; + + return 0 ; +} /* main */ + +static int +compare_strings_or_die (int linenum, const char *fmt, const char* s1, const char* s2) +{ int errors = 0 ; +/*-puts (s1) ;puts (s2) ;-*/ + + if (strcmp (s1, s2) != 0) + { printf ("\n\nLine %d: string compare mismatch:\n\t", linenum) ; + printf ("\"%s\"\n", fmt) ; + printf ("\t\"%s\"\n\t\"%s\"\n", s1, s2) ; + errors ++ ; + } ; + + return errors ; +} /* compare_strings_or_die */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 45147310-868b-400a-97e8-cc0a572a6270 +*/ diff --git a/src/txw.c b/src/txw.c new file mode 100644 index 00000000..336b462e --- /dev/null +++ b/src/txw.c @@ -0,0 +1,379 @@ +/* +** Copyright (C) 2002-2004 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/*=========================================================================== +** Yamaha TX16 Sampler Files. +** +** This header parser was written using information from the SoX source code +** and trial and error experimentation. The code here however is all original. +*/ + +#include "sfconfig.h" + +#include +#include +#include +#include + +#include "sndfile.h" +#include "sfendian.h" +#include "common.h" + +#if (ENABLE_EXPERIMENTAL_CODE == 0) + +int +txw_open (SF_PRIVATE *psf) +{ if (psf) + return SFE_UNIMPLEMENTED ; + return 0 ; +} /* txw_open */ + +#else + +/*------------------------------------------------------------------------------ +** Markers. +*/ + +#define TXW_DATA_OFFSET 32 + +#define TXW_LOOPED 0x49 +#define TXW_NO_LOOP 0xC9 + +/*------------------------------------------------------------------------------ +** Private static functions. +*/ + +static int txw_read_header (SF_PRIVATE *psf) ; + +static sf_count_t txw_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ; +static sf_count_t txw_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ; +static sf_count_t txw_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ; +static sf_count_t txw_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ; + +static sf_count_t txw_seek (SF_PRIVATE *psf, int mode, sf_count_t offset) ; + +/*------------------------------------------------------------------------------ +** Public functions. +*/ + +/* + * ftp://ftp.t0.or.at/pub/sound/tx16w/samples.yamaha + * ftp://ftp.t0.or.at/pub/sound/tx16w/faq/tx16w.tec + * http://www.t0.or.at/~mpakesch/tx16w/ + * + * from tx16w.c sox 12.15: (7-Oct-98) (Mark Lakata and Leigh Smith) + * char filetype[6] "LM8953" + * nulls[10], + * dummy_aeg[6] + * format 0x49 = looped, 0xC9 = non-looped + * sample_rate 1 = 33 kHz, 2 = 50 kHz, 3 = 16 kHz + * atc_length[3] if sample rate 0, [2]&0xfe = 6: 33kHz, 0x10:50, 0xf6: 16, + * depending on [5] but to heck with it + * rpt_length[3] (these are for looped samples, attack and loop lengths) + * unused[2] + */ + +typedef struct +{ unsigned char format, srate, sr2, sr3 ; + unsigned short srhash ; + unsigned int attacklen, repeatlen ; +} TXW_HEADER ; + +#define ERROR_666 666 + +int +txw_open (SF_PRIVATE *psf) +{ int error ; + + if (psf->mode != SFM_READ) + return SFE_UNIMPLEMENTED ; + + if ((error = txw_read_header (psf))) + return error ; + + if (psf_fseek (psf, psf->dataoffset, SEEK_SET) != psf->dataoffset) + return SFE_BAD_SEEK ; + + psf->read_short = txw_read_s ; + psf->read_int = txw_read_i ; + psf->read_float = txw_read_f ; + psf->read_double = txw_read_d ; + + psf->seek = txw_seek ; + + return 0 ; +} /* txw_open */ + +/*------------------------------------------------------------------------------ +*/ + +static int +txw_read_header (SF_PRIVATE *psf) +{ TXW_HEADER txwh ; + char *strptr ; + + memset (&txwh, 0, sizeof (txwh)) ; + memset (psf->u.cbuf, 0, sizeof (psf->u.cbuf)) ; + psf_binheader_readf (psf, "pb", 0, psf->u.cbuf, 16) ; + + if (memcmp (psf->u.cbuf, "LM8953\0\0\0\0\0\0\0\0\0\0", 16) != 0) + return ERROR_666 ; + + psf_log_printf (psf, "Read only : Yamaha TX-16 Sampler (.txw)\nLM8953\n") ; + + /* Jump 6 bytes (dummp_aeg), read format, read sample rate. */ + psf_binheader_readf (psf, "j11", 6, &txwh.format, &txwh.srate) ; + + /* 8 bytes (atc_length[3], rpt_length[3], unused[2]). */ + psf_binheader_readf (psf, "e33j", &txwh.attacklen, &txwh.repeatlen, 2) ; + txwh.sr2 = (txwh.attacklen >> 16) & 0xFE ; + txwh.sr3 = (txwh.repeatlen >> 16) & 0xFE ; + txwh.attacklen &= 0x1FFFF ; + txwh.repeatlen &= 0x1FFFF ; + + switch (txwh.format) + { case TXW_LOOPED : + strptr = "looped" ; + break ; + + case TXW_NO_LOOP : + strptr = "non-looped" ; + break ; + + default : + psf_log_printf (psf, " Format : 0x%02x => ?????\n", txwh.format) ; + return ERROR_666 ; + } ; + + psf_log_printf (psf, " Format : 0x%02X => %s\n", txwh.format, strptr) ; + + strptr = NULL ; + + switch (txwh.srate) + { case 1 : + psf->sf.samplerate = 33333 ; + break ; + + case 2 : + psf->sf.samplerate = 50000 ; + break ; + + case 3 : + psf->sf.samplerate = 16667 ; + break ; + + default : + /* This is ugly and braindead. */ + txwh.srhash = ((txwh.sr2 & 0xFE) << 8) | (txwh.sr3 & 0xFE) ; + switch (txwh.srhash) + { case ((0x6 << 8) | 0x52) : + psf->sf.samplerate = 33333 ; + break ; + + case ((0x10 << 8) | 0x52) : + psf->sf.samplerate = 50000 ; + break ; + + case ((0xF6 << 8) | 0x52) : + psf->sf.samplerate = 166667 ; + break ; + + default : + strptr = " Sample Rate : Unknown : forcing to 33333\n" ; + psf->sf.samplerate = 33333 ; + break ; + } ; + } ; + + + if (strptr) + psf_log_printf (psf, strptr) ; + else if (txwh.srhash) + psf_log_printf (psf, " Sample Rate : %d (0x%X) => %d\n", txwh.srate, txwh.srhash, psf->sf.samplerate) ; + else + psf_log_printf (psf, " Sample Rate : %d => %d\n", txwh.srate, psf->sf.samplerate) ; + + if (txwh.format == TXW_LOOPED) + { psf_log_printf (psf, " Attack Len : %d\n", txwh.attacklen) ; + psf_log_printf (psf, " Repeat Len : %d\n", txwh.repeatlen) ; + } ; + + psf->dataoffset = TXW_DATA_OFFSET ; + psf->datalength = psf->filelength - TXW_DATA_OFFSET ; + psf->sf.frames = 2 * psf->datalength / 3 ; + + + if (psf->datalength % 3 == 1) + psf_log_printf (psf, "*** File seems to be truncated, %d extra bytes.\n", + (int) (psf->datalength % 3)) ; + + if (txwh.attacklen + txwh.repeatlen > psf->sf.frames) + psf_log_printf (psf, "*** File has been truncated.\n") ; + + psf->sf.format = SF_FORMAT_TXW | SF_FORMAT_PCM_16 ; + psf->sf.channels = 1 ; + psf->sf.sections = 1 ; + psf->sf.seekable = SF_TRUE ; + + return 0 ; +} /* txw_read_header */ + +static sf_count_t +txw_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len) +{ unsigned char *ucptr ; + short sample ; + int k, bufferlen, readcount, count ; + sf_count_t total = 0 ; + + bufferlen = sizeof (psf->u.cbuf) / 3 ; + bufferlen -= (bufferlen & 1) ; + while (len > 0) + { readcount = (len >= bufferlen) ? bufferlen : len ; + count = psf_fread (psf->u.cbuf, 3, readcount, psf) ; + + ucptr = psf->u.ucbuf ; + for (k = 0 ; k < readcount ; k += 2) + { sample = (ucptr [0] << 8) | (ucptr [1] & 0xF0) ; + ptr [total + k] = sample ; + sample = (ucptr [2] << 8) | ((ucptr [1] & 0xF) << 4) ; + ptr [total + k + 1] = sample ; + ucptr += 3 ; + } ; + + total += count ; + len -= readcount ; + } ; + + return total ; +} /* txw_read_s */ + +static sf_count_t +txw_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len) +{ unsigned char *ucptr ; + short sample ; + int k, bufferlen, readcount, count ; + sf_count_t total = 0 ; + + bufferlen = sizeof (psf->u.cbuf) / 3 ; + bufferlen -= (bufferlen & 1) ; + while (len > 0) + { readcount = (len >= bufferlen) ? bufferlen : len ; + count = psf_fread (psf->u.cbuf, 3, readcount, psf) ; + + ucptr = psf->u.ucbuf ; + for (k = 0 ; k < readcount ; k += 2) + { sample = (ucptr [0] << 8) | (ucptr [1] & 0xF0) ; + ptr [total + k] = sample << 16 ; + sample = (ucptr [2] << 8) | ((ucptr [1] & 0xF) << 4) ; + ptr [total + k + 1] = sample << 16 ; + ucptr += 3 ; + } ; + + total += count ; + len -= readcount ; + } ; + + return total ; +} /* txw_read_i */ + +static sf_count_t +txw_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len) +{ unsigned char *ucptr ; + short sample ; + int k, bufferlen, readcount, count ; + sf_count_t total = 0 ; + float normfact ; + + if (psf->norm_float == SF_TRUE) + normfact = 1.0 / 0x8000 ; + else + normfact = 1.0 / 0x10 ; + + bufferlen = sizeof (psf->u.cbuf) / 3 ; + bufferlen -= (bufferlen & 1) ; + while (len > 0) + { readcount = (len >= bufferlen) ? bufferlen : len ; + count = psf_fread (psf->u.cbuf, 3, readcount, psf) ; + + ucptr = psf->u.ucbuf ; + for (k = 0 ; k < readcount ; k += 2) + { sample = (ucptr [0] << 8) | (ucptr [1] & 0xF0) ; + ptr [total + k] = normfact * sample ; + sample = (ucptr [2] << 8) | ((ucptr [1] & 0xF) << 4) ; + ptr [total + k + 1] = normfact * sample ; + ucptr += 3 ; + } ; + + total += count ; + len -= readcount ; + } ; + + return total ; +} /* txw_read_f */ + +static sf_count_t +txw_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len) +{ unsigned char *ucptr ; + short sample ; + int k, bufferlen, readcount, count ; + sf_count_t total = 0 ; + double normfact ; + + if (psf->norm_double == SF_TRUE) + normfact = 1.0 / 0x8000 ; + else + normfact = 1.0 / 0x10 ; + + bufferlen = sizeof (psf->u.cbuf) / 3 ; + bufferlen -= (bufferlen & 1) ; + while (len > 0) + { readcount = (len >= bufferlen) ? bufferlen : len ; + count = psf_fread (psf->u.cbuf, 3, readcount, psf) ; + + ucptr = psf->u.ucbuf ; + for (k = 0 ; k < readcount ; k += 2) + { sample = (ucptr [0] << 8) | (ucptr [1] & 0xF0) ; + ptr [total + k] = normfact * sample ; + sample = (ucptr [2] << 8) | ((ucptr [1] & 0xF) << 4) ; + ptr [total + k + 1] = normfact * sample ; + ucptr += 3 ; + } ; + + total += count ; + len -= readcount ; + } ; + + return total ; +} /* txw_read_d */ + +static sf_count_t +txw_seek (SF_PRIVATE *psf, int mode, sf_count_t offset) +{ if (psf && mode) + return offset ; + + return 0 ; +} /* txw_seek */ + +#endif +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 4d0ba7af-b1c5-46b4-a900-7c6f59fd9a89 +*/ diff --git a/src/ulaw.c b/src/ulaw.c new file mode 100644 index 00000000..e476c0a7 --- /dev/null +++ b/src/ulaw.c @@ -0,0 +1,1047 @@ +/* +** Copyright (C) 1999-2005 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sndfile.h" +#include "float_cast.h" +#include "common.h" + +static sf_count_t ulaw_read_ulaw2s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ; +static sf_count_t ulaw_read_ulaw2i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ; +static sf_count_t ulaw_read_ulaw2f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ; +static sf_count_t ulaw_read_ulaw2d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ; + +static sf_count_t ulaw_write_s2ulaw (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ; +static sf_count_t ulaw_write_i2ulaw (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ; +static sf_count_t ulaw_write_f2ulaw (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ; +static sf_count_t ulaw_write_d2ulaw (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ; + +int +ulaw_init (SF_PRIVATE *psf) +{ + if (psf->mode == SFM_READ || psf->mode == SFM_RDWR) + { psf->read_short = ulaw_read_ulaw2s ; + psf->read_int = ulaw_read_ulaw2i ; + psf->read_float = ulaw_read_ulaw2f ; + psf->read_double = ulaw_read_ulaw2d ; + } ; + + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + { psf->write_short = ulaw_write_s2ulaw ; + psf->write_int = ulaw_write_i2ulaw ; + psf->write_float = ulaw_write_f2ulaw ; + psf->write_double = ulaw_write_d2ulaw ; + } ; + + psf->bytewidth = 1 ; + psf->blockwidth = psf->sf.channels ; + + if (psf->filelength > psf->dataoffset) + psf->datalength = (psf->dataend) ? psf->dataend - psf->dataoffset : + psf->filelength - psf->dataoffset ; + else + psf->datalength = 0 ; + + psf->sf.frames = psf->datalength / psf->blockwidth ; + + return 0 ; +} /* ulaw_init */ + +/*============================================================================== +*/ + +static short ulaw_decode [256] = +{ -32124, -31100, -30076, -29052, -28028, -27004, -25980, -24956, + -23932, -22908, -21884, -20860, -19836, -18812, -17788, -16764, + -15996, -15484, -14972, -14460, -13948, -13436, -12924, -12412, + -11900, -11388, -10876, -10364, -9852, -9340, -8828, -8316, + -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140, + -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092, + -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004, + -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980, + -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436, + -1372, -1308, -1244, -1180, -1116, -1052, -988, -924, + -876, -844, -812, -780, -748, -716, -684, -652, + -620, -588, -556, -524, -492, -460, -428, -396, + -372, -356, -340, -324, -308, -292, -276, -260, + -244, -228, -212, -196, -180, -164, -148, -132, + -120, -112, -104, -96, -88, -80, -72, -64, + -56, -48, -40, -32, -24, -16, -8, 0, + + 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956, + 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764, + 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412, + 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316, + 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140, + 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092, + 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004, + 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980, + 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436, + 1372, 1308, 1244, 1180, 1116, 1052, 988, 924, + 876, 844, 812, 780, 748, 716, 684, 652, + 620, 588, 556, 524, 492, 460, 428, 396, + 372, 356, 340, 324, 308, 292, 276, 260, + 244, 228, 212, 196, 180, 164, 148, 132, + 120, 112, 104, 96, 88, 80, 72, 64, + 56, 48, 40, 32, 24, 16, 8, 0 +} ; + +static +unsigned char ulaw_encode [8193] = +{ 0xff, 0xfe, 0xfe, 0xfd, 0xfd, 0xfc, 0xfc, 0xfb, 0xfb, 0xfa, 0xfa, 0xf9, + 0xf9, 0xf8, 0xf8, 0xf7, 0xf7, 0xf6, 0xf6, 0xf5, 0xf5, 0xf4, 0xf4, 0xf3, + 0xf3, 0xf2, 0xf2, 0xf1, 0xf1, 0xf0, 0xf0, 0xef, 0xef, 0xef, 0xef, 0xee, + 0xee, 0xee, 0xee, 0xed, 0xed, 0xed, 0xed, 0xec, 0xec, 0xec, 0xec, 0xeb, + 0xeb, 0xeb, 0xeb, 0xea, 0xea, 0xea, 0xea, 0xe9, 0xe9, 0xe9, 0xe9, 0xe8, + 0xe8, 0xe8, 0xe8, 0xe7, 0xe7, 0xe7, 0xe7, 0xe6, 0xe6, 0xe6, 0xe6, 0xe5, + 0xe5, 0xe5, 0xe5, 0xe4, 0xe4, 0xe4, 0xe4, 0xe3, 0xe3, 0xe3, 0xe3, 0xe2, + 0xe2, 0xe2, 0xe2, 0xe1, 0xe1, 0xe1, 0xe1, 0xe0, 0xe0, 0xe0, 0xe0, 0xdf, + 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xde, 0xde, 0xde, 0xde, 0xde, + 0xde, 0xde, 0xde, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdc, + 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, + 0xdb, 0xdb, 0xdb, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xd9, + 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, + 0xd8, 0xd8, 0xd8, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd6, + 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, + 0xd5, 0xd5, 0xd5, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd3, + 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, + 0xd2, 0xd2, 0xd2, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd0, + 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, + 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xce, + 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, + 0xce, 0xce, 0xce, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcb, + 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, + 0xcb, 0xcb, 0xcb, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, + 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, + 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc8, + 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, + 0xc8, 0xc8, 0xc8, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, + 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc5, + 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, + 0xc5, 0xc5, 0xc5, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, + 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, + 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc2, + 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, + 0xc2, 0xc2, 0xc2, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, + 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xbf, + 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, + 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, + 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbe, 0xbe, 0xbe, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, + 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, + 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbc, + 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, + 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, + 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbb, 0xbb, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, + 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, + 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb9, + 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, + 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, + 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, + 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, + 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, + 0xb8, 0xb8, 0xb8, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, + 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, + 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb6, + 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, + 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, + 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, + 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, + 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, + 0xb5, 0xb5, 0xb5, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb3, + 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, + 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, + 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, + 0xb2, 0xb2, 0xb2, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, + 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, + 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb0, + 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, + 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, + 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00 +} ; + +static inline void +ulaw2s_array (unsigned char *buffer, int count, short *ptr) +{ while (--count >= 0) + ptr [count] = ulaw_decode [(int) buffer [count]] ; +} /* ulaw2s_array */ + +static inline void +ulaw2i_array (unsigned char *buffer, int count, int *ptr) +{ while (--count >= 0) + ptr [count] = ulaw_decode [buffer [count]] << 16 ; +} /* ulaw2i_array */ + +static inline void +ulaw2f_array (unsigned char *buffer, int count, float *ptr, float normfact) +{ while (--count >= 0) + ptr [count] = normfact * ulaw_decode [(int) buffer [count]] ; +} /* ulaw2f_array */ + +static inline void +ulaw2d_array (const unsigned char *buffer, int count, double *ptr, double normfact) +{ while (--count >= 0) + ptr [count] = normfact * ulaw_decode [(int) buffer [count]] ; +} /* ulaw2d_array */ + +static inline void +s2ulaw_array (const short *ptr, int count, unsigned char *buffer) +{ while (--count >= 0) + { if (ptr [count] >= 0) + buffer [count] = ulaw_encode [ptr [count] / 4] ; + else + buffer [count] = 0x7F & ulaw_encode [ptr [count] / -4] ; + } ; +} /* s2ulaw_array */ + +static inline void +i2ulaw_array (const int *ptr, int count, unsigned char *buffer) +{ while (--count >= 0) + { if (ptr [count] >= 0) + buffer [count] = ulaw_encode [ptr [count] >> (16 + 2)] ; + else + buffer [count] = 0x7F & ulaw_encode [-ptr [count] >> (16 + 2)] ; + } ; +} /* i2ulaw_array */ + +static inline void +f2ulaw_array (const float *ptr, int count, unsigned char *buffer, float normfact) +{ while (--count >= 0) + { if (ptr [count] >= 0) + buffer [count] = ulaw_encode [lrintf (normfact * ptr [count])] ; + else + buffer [count] = 0x7F & ulaw_encode [- lrintf (normfact * ptr [count])] ; + } ; +} /* f2ulaw_array */ + +static inline void +d2ulaw_array (const double *ptr, int count, unsigned char *buffer, double normfact) +{ while (--count >= 0) + { if (ptr [count] >= 0) + buffer [count] = ulaw_encode [lrint (normfact * ptr [count])] ; + else + buffer [count] = 0x7F & ulaw_encode [- lrint (normfact * ptr [count])] ; + } ; +} /* d2ulaw_array */ + +/*============================================================================== +*/ + +static sf_count_t +ulaw_read_ulaw2s (SF_PRIVATE *psf, short *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + + bufferlen = ARRAY_LEN (psf->u.ucbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.ucbuf, 1, bufferlen, psf) ; + ulaw2s_array (psf->u.ucbuf, readcount, ptr + total) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* ulaw_read_ulaw2s */ + +static sf_count_t +ulaw_read_ulaw2i (SF_PRIVATE *psf, int *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + + bufferlen = ARRAY_LEN (psf->u.ucbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.ucbuf, 1, bufferlen, psf) ; + ulaw2i_array (psf->u.ucbuf, readcount, ptr + total) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* ulaw_read_ulaw2i */ + +static sf_count_t +ulaw_read_ulaw2f (SF_PRIVATE *psf, float *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + float normfact ; + + normfact = (psf->norm_float == SF_TRUE) ? 1.0 / ((float) 0x8000) : 1.0 ; + + bufferlen = ARRAY_LEN (psf->u.ucbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.ucbuf, 1, bufferlen, psf) ; + ulaw2f_array (psf->u.ucbuf, readcount, ptr + total, normfact) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* ulaw_read_ulaw2f */ + +static sf_count_t +ulaw_read_ulaw2d (SF_PRIVATE *psf, double *ptr, sf_count_t len) +{ int bufferlen, readcount ; + sf_count_t total = 0 ; + double normfact ; + + normfact = (psf->norm_double) ? 1.0 / ((double) 0x8000) : 1.0 ; + bufferlen = ARRAY_LEN (psf->u.ucbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.ucbuf, 1, bufferlen, psf) ; + ulaw2d_array (psf->u.ucbuf, readcount, ptr + total, normfact) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* ulaw_read_ulaw2d */ + +/*============================================================================================= +*/ + +static sf_count_t +ulaw_write_s2ulaw (SF_PRIVATE *psf, const short *ptr, sf_count_t len) +{ int bufferlen, writecount ; + sf_count_t total = 0 ; + + bufferlen = ARRAY_LEN (psf->u.ucbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + s2ulaw_array (ptr + total, bufferlen, psf->u.ucbuf) ; + writecount = psf_fwrite (psf->u.ucbuf, 1, bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* ulaw_write_s2ulaw */ + +static sf_count_t +ulaw_write_i2ulaw (SF_PRIVATE *psf, const int *ptr, sf_count_t len) +{ int bufferlen, writecount ; + sf_count_t total = 0 ; + + bufferlen = ARRAY_LEN (psf->u.ucbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + i2ulaw_array (ptr + total, bufferlen, psf->u.ucbuf) ; + writecount = psf_fwrite (psf->u.ucbuf, 1, bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* ulaw_write_i2ulaw */ + +static sf_count_t +ulaw_write_f2ulaw (SF_PRIVATE *psf, const float *ptr, sf_count_t len) +{ int bufferlen, writecount ; + sf_count_t total = 0 ; + float normfact ; + + /* Factor in a divide by 4. */ + normfact = (psf->norm_float == SF_TRUE) ? (0.25 * 0x7FFF) : 0.25 ; + + bufferlen = ARRAY_LEN (psf->u.ucbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + f2ulaw_array (ptr + total, bufferlen, psf->u.ucbuf, normfact) ; + writecount = psf_fwrite (psf->u.ucbuf, 1, bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* ulaw_write_f2ulaw */ + +static sf_count_t +ulaw_write_d2ulaw (SF_PRIVATE *psf, const double *ptr, sf_count_t len) +{ int bufferlen, writecount ; + sf_count_t total = 0 ; + double normfact ; + + /* Factor in a divide by 4. */ + normfact = (psf->norm_double) ? (0.25 * 0x7FFF) : 0.25 ; + + bufferlen = ARRAY_LEN (psf->u.ucbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + d2ulaw_array (ptr + total, bufferlen, psf->u.ucbuf, normfact) ; + writecount = psf_fwrite (psf->u.ucbuf, 1, bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* ulaw_write_d2ulaw */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 655cc790-f058-45e8-89c9-86967cccc37e +*/ diff --git a/src/voc.c b/src/voc.c new file mode 100644 index 00000000..de7abf2f --- /dev/null +++ b/src/voc.c @@ -0,0 +1,878 @@ +/* +** Copyright (C) 2001-2006 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* RANT: +** The VOC file format is the most brain damaged format I have yet had to deal +** with. No one programmer could have bee stupid enough to put this together. +** Instead it looks like a series of manic, dyslexic assembly language programmers +** hacked it to fit their needs. +** Utterly woeful. +*/ + +#include "sfconfig.h" + +#include +#include +#include + +#include "sndfile.h" +#include "sfendian.h" +#include "common.h" + + +/*------------------------------------------------------------------------------ + * Typedefs for file chunks. +*/ + +#define VOC_MAX_SECTIONS 200 + +enum +{ VOC_TERMINATOR = 0, + VOC_SOUND_DATA = 1, + VOC_SOUND_CONTINUE = 2, + VOC_SILENCE = 3, + VOC_MARKER = 4, + VOC_ASCII = 5, + VOC_REPEAT = 6, + VOC_END_REPEAT = 7, + VOC_EXTENDED = 8, + VOC_EXTENDED_II = 9 +} ; + +typedef struct +{ int samples ; + int offset ; /* Offset of zero => silence. */ +} SND_DATA_BLOCKS ; + +typedef struct +{ unsigned int sections, section_types ; + int samplerate, channels, bitwidth ; + SND_DATA_BLOCKS blocks [VOC_MAX_SECTIONS] ; +} VOC_DATA ; + +/*------------------------------------------------------------------------------ + * Private static functions. +*/ + +static int voc_close (SF_PRIVATE *psf) ; +static int voc_write_header (SF_PRIVATE *psf, int calc_length) ; +static int voc_read_header (SF_PRIVATE *psf) ; + +static const char* voc_encoding2str (int encoding) ; + +#if 0 + +/* These functions would be required for files with more than one VOC_SOUND_DATA +** segment. Not sure whether to bother implementing this. +*/ + +static int voc_multi_init (SF_PRIVATE *psf, VOC_DATA *pvoc) ; + +static int voc_multi_read_uc2s (SF_PRIVATE *psf, short *ptr, int len) ; +static int voc_multi_read_les2s (SF_PRIVATE *psf, short *ptr, int len) ; + +static int voc_multi_read_uc2i (SF_PRIVATE *psf, int *ptr, int len) ; +static int voc_multi_read_les2i (SF_PRIVATE *psf, int *ptr, int len) ; + +static int voc_multi_read_uc2f (SF_PRIVATE *psf, float *ptr, int len) ; +static int voc_multi_read_les2f (SF_PRIVATE *psf, float *ptr, int len) ; + +static int voc_multi_read_uc2d (SF_PRIVATE *psf, double *ptr, int len) ; +static int voc_multi_read_les2d (SF_PRIVATE *psf, double *ptr, int len) ; +#endif + +/*------------------------------------------------------------------------------ +** Public function. +*/ + +int +voc_open (SF_PRIVATE *psf) +{ int subformat, error = 0 ; + + if (psf->is_pipe) + return SFE_VOC_NO_PIPE ; + + if (psf->mode == SFM_READ || (psf->mode == SFM_RDWR && psf->filelength > 0)) + { if ((error = voc_read_header (psf))) + return error ; + } ; + + subformat = psf->sf.format & SF_FORMAT_SUBMASK ; + + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + { if ((psf->sf.format & SF_FORMAT_TYPEMASK) != SF_FORMAT_VOC) + return SFE_BAD_OPEN_FORMAT ; + + psf->endian = SF_ENDIAN_LITTLE ; + + if ((error = voc_write_header (psf, SF_FALSE))) + return error ; + + psf->write_header = voc_write_header ; + } ; + + psf->blockwidth = psf->bytewidth * psf->sf.channels ; + + psf->container_close = voc_close ; + + switch (subformat) + { case SF_FORMAT_PCM_U8 : + case SF_FORMAT_PCM_16 : + error = pcm_init (psf) ; + break ; + + case SF_FORMAT_ALAW : + error = alaw_init (psf) ; + break ; + + case SF_FORMAT_ULAW : + error = ulaw_init (psf) ; + break ; + + default : return SFE_UNIMPLEMENTED ; + } ; + + return error ; +} /* voc_open */ + +/*------------------------------------------------------------------------------ +*/ + +static int +voc_read_header (SF_PRIVATE *psf) +{ VOC_DATA *pvoc ; + char creative [20] ; + unsigned char block_type, rate_byte ; + short version, checksum, encoding, dataoffset ; + int offset ; + + /* Set position to start of file to begin reading header. */ + offset = psf_binheader_readf (psf, "pb", 0, creative, SIGNED_SIZEOF (creative)) ; + + if (creative [sizeof (creative) - 1] != 0x1A) + return SFE_VOC_NO_CREATIVE ; + + /* Terminate the string. */ + creative [sizeof (creative) - 1] = 0 ; + + if (strcmp ("Creative Voice File", creative)) + return SFE_VOC_NO_CREATIVE ; + + psf_log_printf (psf, "%s\n", creative) ; + + offset += psf_binheader_readf (psf, "e222", &dataoffset, &version, &checksum) ; + + psf->dataoffset = dataoffset ; + + psf_log_printf (psf, "dataoffset : %d\n" + "version : 0x%X\n" + "checksum : 0x%X\n", psf->dataoffset, version, checksum) ; + + if (version != 0x010A && version != 0x0114) + return SFE_VOC_BAD_VERSION ; + + if (! (psf->codec_data = malloc (sizeof (VOC_DATA)))) + return SFE_MALLOC_FAILED ; + + pvoc = (VOC_DATA*) psf->codec_data ; + + memset (pvoc, 0, sizeof (VOC_DATA)) ; + + /* Set the default encoding now. */ + psf->sf.format = SF_FORMAT_VOC ; /* Major format */ + encoding = SF_FORMAT_PCM_U8 ; /* Minor format */ + psf->endian = SF_ENDIAN_LITTLE ; + + while (1) + { offset += psf_binheader_readf (psf, "1", &block_type) ; + + switch (block_type) + { case VOC_ASCII : + { int size ; + + offset += psf_binheader_readf (psf, "e3", &size) ; + + psf_log_printf (psf, " ASCII : %d\n", size) ; + + offset += psf_binheader_readf (psf, "b", psf->header, size) ; + psf->header [size] = 0 ; + psf_log_printf (psf, " text : %s\n", psf->header) ; + } ; + continue ; + + case VOC_SOUND_DATA : + case VOC_EXTENDED : + case VOC_EXTENDED_II : + break ; + + default : psf_log_printf (psf, "*** Weird block marker (%d)\n", block_type) ; + } ; + + break ; + } ; + + if (block_type == VOC_SOUND_DATA) + { unsigned char compression ; + int size ; + + offset += psf_binheader_readf (psf, "e311", &size, &rate_byte, &compression) ; + + psf->sf.samplerate = 1000000 / (256 - (rate_byte & 0xFF)) ; + + psf_log_printf (psf, " Sound Data : %d\n sr : %d => %dHz\n comp : %d\n", + size, rate_byte, psf->sf.samplerate, compression) ; + + if (offset + size - 1 > psf->filelength) + { psf_log_printf (psf, "Seems to be a truncated file.\n") ; + psf_log_printf (psf, "offset: %d size: %d sum: %d filelength: %D\n", offset, size, offset + size, psf->filelength) ; + return SFE_VOC_BAD_SECTIONS ; + } + else if (offset + size - 1 < psf->filelength) + { psf_log_printf (psf, "Seems to be a multi-segment file (#1).\n") ; + psf_log_printf (psf, "offset: %d size: %d sum: %d filelength: %D\n", offset, size, offset + size, psf->filelength) ; + return SFE_VOC_BAD_SECTIONS ; + } ; + + psf->dataoffset = offset ; + psf->dataend = psf->filelength - 1 ; + + psf->sf.channels = 1 ; + psf->bytewidth = 1 ; + + psf->sf.format = SF_FORMAT_VOC | SF_FORMAT_PCM_U8 ; + + return 0 ; + } ; + + if (block_type == VOC_EXTENDED) + { unsigned char pack, stereo, compression ; + unsigned short rate_short ; + int size ; + + offset += psf_binheader_readf (psf, "e3211", &size, &rate_short, &pack, &stereo) ; + + psf_log_printf (psf, " Extended : %d\n", size) ; + if (size == 4) + psf_log_printf (psf, " size : 4\n") ; + else + psf_log_printf (psf, " size : %d (should be 4)\n", size) ; + + psf_log_printf (psf, " pack : %d\n" + " stereo : %s\n", pack, (stereo ? "yes" : "no")) ; + + if (stereo) + { psf->sf.channels = 2 ; + psf->sf.samplerate = 128000000 / (65536 - rate_short) ; + } + else + { psf->sf.channels = 1 ; + psf->sf.samplerate = 256000000 / (65536 - rate_short) ; + } ; + + psf_log_printf (psf, " sr : %d => %dHz\n", (rate_short & 0xFFFF), psf->sf.samplerate) ; + + offset += psf_binheader_readf (psf, "1", &block_type) ; + + if (block_type != VOC_SOUND_DATA) + { psf_log_printf (psf, "*** Expecting VOC_SOUND_DATA section.\n") ; + return SFE_VOC_BAD_FORMAT ; + } ; + + offset += psf_binheader_readf (psf, "e311", &size, &rate_byte, &compression) ; + + psf_log_printf (psf, " Sound Data : %d\n" + " sr : %d\n" + " comp : %d\n", size, rate_byte, compression) ; + + + if (offset + size - 1 > psf->filelength) + { psf_log_printf (psf, "Seems to be a truncated file.\n") ; + psf_log_printf (psf, "offset: %d size: %d sum: %d filelength: %D\n", offset, size, offset + size, psf->filelength) ; + return SFE_VOC_BAD_SECTIONS ; + } + else if (offset + size - 1 < psf->filelength) + { psf_log_printf (psf, "Seems to be a multi-segment file (#2).\n") ; + psf_log_printf (psf, "offset: %d size: %d sum: %d filelength: %D\n", offset, size, offset + size, psf->filelength) ; + return SFE_VOC_BAD_SECTIONS ; + } ; + + psf->dataoffset = offset ; + psf->dataend = psf->filelength - 1 ; + + psf->bytewidth = 1 ; + + psf->sf.format = SF_FORMAT_VOC | SF_FORMAT_PCM_U8 ; + + return 0 ; + } + + if (block_type == VOC_EXTENDED_II) + { unsigned char bitwidth, channels ; + int size, fourbytes ; + + offset += psf_binheader_readf (psf, "e341124", &size, &psf->sf.samplerate, + &bitwidth, &channels, &encoding, &fourbytes) ; + + if (size * 2 == psf->filelength - 39) + { int temp_size = psf->filelength - 31 ; + + psf_log_printf (psf, " Extended II : %d (SoX bug: should be %d)\n", size, temp_size) ; + size = temp_size ; + } + else + psf_log_printf (psf, " Extended II : %d\n", size) ; + + psf_log_printf (psf, " sample rate : %d\n" + " bit width : %d\n" + " channels : %d\n", psf->sf.samplerate, bitwidth, channels) ; + + if (bitwidth == 16 && encoding == 0) + { encoding = 4 ; + psf_log_printf (psf, " encoding : 0 (SoX bug: should be 4 for 16 bit signed PCM)\n") ; + } + else + psf_log_printf (psf, " encoding : %d => %s\n", encoding, voc_encoding2str (encoding)) ; + + + psf_log_printf (psf, " fourbytes : %X\n", fourbytes) ; + + psf->sf.channels = channels ; + + psf->dataoffset = offset ; + psf->dataend = psf->filelength - 1 ; + + if (size + 31 == psf->filelength + 1) + { /* Hack for reading files produced using + ** sf_command (SFC_UPDATE_HEADER_NOW). + */ + psf_log_printf (psf, "Missing zero byte at end of file.\n") ; + size = psf->filelength - 30 ; + psf->dataend = 0 ; + } + else if (size + 31 > psf->filelength) + { psf_log_printf (psf, "Seems to be a truncated file.\n") ; + size = psf->filelength - 31 ; + } + else if (size + 31 < psf->filelength) + psf_log_printf (psf, "Seems to be a multi-segment file (#3).\n") ; + + switch (encoding) + { case 0 : + psf->sf.format = SF_FORMAT_VOC | SF_FORMAT_PCM_U8 ; + psf->bytewidth = 1 ; + break ; + + case 4 : + psf->sf.format = SF_FORMAT_VOC | SF_FORMAT_PCM_16 ; + psf->bytewidth = 2 ; + break ; + + case 6 : + psf->sf.format = SF_FORMAT_VOC | SF_FORMAT_ALAW ; + psf->bytewidth = 1 ; + break ; + + case 7 : + psf->sf.format = SF_FORMAT_VOC | SF_FORMAT_ULAW ; + psf->bytewidth = 1 ; + break ; + + default : /* Unknown */ + return SFE_UNKNOWN_FORMAT ; + break ; + } ; + + } ; + + return 0 ; +} /* voc_read_header */ + +/*==================================================================================== +*/ + +static int +voc_write_header (SF_PRIVATE *psf, int calc_length) +{ sf_count_t current ; + int rate_const, subformat ; + + current = psf_ftell (psf) ; + + if (calc_length) + { psf->filelength = psf_get_filelen (psf) ; + + psf->datalength = psf->filelength - psf->dataoffset ; + if (psf->dataend) + psf->datalength -= psf->filelength - psf->dataend ; + + psf->sf.frames = psf->datalength / (psf->bytewidth * psf->sf.channels) ; + } ; + + subformat = psf->sf.format & SF_FORMAT_SUBMASK ; + /* Reset the current header length to zero. */ + psf->header [0] = 0 ; + psf->headindex = 0 ; + psf_fseek (psf, 0, SEEK_SET) ; + + /* VOC marker and 0x1A byte. */ + psf_binheader_writef (psf, "eb1", "Creative Voice File", make_size_t (19), 0x1A) ; + + /* Data offset, version and other. */ + psf_binheader_writef (psf, "e222", 26, 0x0114, 0x111F) ; + + /* Use same logic as SOX. + ** If the file is mono 8 bit data, use VOC_SOUND_DATA. + ** If the file is mono 16 bit data, use VOC_EXTENED. + ** Otherwise use VOC_EXTENED_2. + */ + + if (subformat == SF_FORMAT_PCM_U8 && psf->sf.channels == 1) + { /* samplerate = 1000000 / (256 - rate_const) ; */ + rate_const = 256 - 1000000 / psf->sf.samplerate ; + + /* First type marker, length, rate_const and compression */ + psf_binheader_writef (psf, "e1311", VOC_SOUND_DATA, (int) (psf->datalength + 1), rate_const, 0) ; + } + else if (subformat == SF_FORMAT_PCM_U8 && psf->sf.channels == 2) + { /* sample_rate = 128000000 / (65536 - rate_short) ; */ + rate_const = 65536 - 128000000 / psf->sf.samplerate ; + + /* First write the VOC_EXTENDED section + ** marker, length, rate_const and compression + */ + psf_binheader_writef (psf, "e13211", VOC_EXTENDED, 4, rate_const, 0, 1) ; + + /* samplerate = 1000000 / (256 - rate_const) ; */ + rate_const = 256 - 1000000 / psf->sf.samplerate ; + + /* Now write the VOC_SOUND_DATA section + ** marker, length, rate_const and compression + */ + psf_binheader_writef (psf, "e1311", VOC_SOUND_DATA, (int) (psf->datalength + 1), rate_const, 0) ; + } + else + { int length ; + + if (psf->sf.channels < 1 || psf->sf.channels > 2) + return SFE_CHANNEL_COUNT ; + + switch (subformat) + { case SF_FORMAT_PCM_U8 : + psf->bytewidth = 1 ; + length = psf->sf.frames * psf->sf.channels * psf->bytewidth + 12 ; + /* Marker, length, sample rate, bitwidth, stereo flag, encoding and fourt zero bytes. */ + psf_binheader_writef (psf, "e1341124", VOC_EXTENDED_II, length, psf->sf.samplerate, 16, psf->sf.channels, 4, 0) ; + break ; + + case SF_FORMAT_PCM_16 : + psf->bytewidth = 2 ; + length = psf->sf.frames * psf->sf.channels * psf->bytewidth + 12 ; + /* Marker, length, sample rate, bitwidth, stereo flag, encoding and fourt zero bytes. */ + psf_binheader_writef (psf, "e1341124", VOC_EXTENDED_II, length, psf->sf.samplerate, 16, psf->sf.channels, 4, 0) ; + break ; + + case SF_FORMAT_ALAW : + psf->bytewidth = 1 ; + length = psf->sf.frames * psf->sf.channels * psf->bytewidth + 12 ; + psf_binheader_writef (psf, "e1341124", VOC_EXTENDED_II, length, psf->sf.samplerate, 8, psf->sf.channels, 6, 0) ; + break ; + + case SF_FORMAT_ULAW : + psf->bytewidth = 1 ; + length = psf->sf.frames * psf->sf.channels * psf->bytewidth + 12 ; + psf_binheader_writef (psf, "e1341124", VOC_EXTENDED_II, length, psf->sf.samplerate, 8, psf->sf.channels, 7, 0) ; + break ; + + default : return SFE_UNIMPLEMENTED ; + } ; + } ; + + psf_fwrite (psf->header, psf->headindex, 1, psf) ; + + if (psf->error) + return psf->error ; + + psf->dataoffset = psf->headindex ; + + if (current > 0) + psf_fseek (psf, current, SEEK_SET) ; + + return psf->error ; +} /* voc_write_header */ + +static int +voc_close (SF_PRIVATE *psf) +{ + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + { /* Now we know for certain the length of the file we can re-write + ** correct values for the FORM, 8SVX and BODY chunks. + */ + unsigned byte = VOC_TERMINATOR ; + + + psf_fseek (psf, 0, SEEK_END) ; + + /* Write terminator */ + psf_fwrite (&byte, 1, 1, psf) ; + + voc_write_header (psf, SF_TRUE) ; + } ; + + return 0 ; +} /* voc_close */ + +static const char* +voc_encoding2str (int encoding) +{ + switch (encoding) + { case 0 : return "8 bit unsigned PCM" ; + case 4 : return "16 bit signed PCM" ; + case 6 : return "A-law" ; + case 7 : return "u-law" ; + default : break ; + } + return "*** Unknown ***" ; +} /* voc_encoding2str */ + +/*==================================================================================== +*/ + +#if 0 +static int +voc_multi_init (SF_PRIVATE *psf, VOC_DATA *pvoc) +{ + psf->sf.frames = 0 ; + + if (pvoc->bitwidth == 8) + { psf->read_short = voc_multi_read_uc2s ; + psf->read_int = voc_multi_read_uc2i ; + psf->read_float = voc_multi_read_uc2f ; + psf->read_double = voc_multi_read_uc2d ; + return 0 ; + } ; + + if (pvoc->bitwidth == 16) + { psf->read_short = voc_multi_read_les2s ; + psf->read_int = voc_multi_read_les2i ; + psf->read_float = voc_multi_read_les2f ; + psf->read_double = voc_multi_read_les2d ; + return 0 ; + } ; + + psf_log_printf (psf, "Error : bitwith != 8 && bitwidth != 16.\n") ; + + return SFE_UNIMPLEMENTED ; +} /* voc_multi_read_int */ + +/*------------------------------------------------------------------------------------ +*/ + +static int +voc_multi_read_uc2s (SF_PRIVATE *psf, short *ptr, int len) +{ + + return 0 ; +} /* voc_multi_read_uc2s */ + +static int +voc_multi_read_les2s (SF_PRIVATE *psf, short *ptr, int len) +{ + + return 0 ; +} /* voc_multi_read_les2s */ + + +static int +voc_multi_read_uc2i (SF_PRIVATE *psf, int *ptr, int len) +{ + + return 0 ; +} /* voc_multi_read_uc2i */ + +static int +voc_multi_read_les2i (SF_PRIVATE *psf, int *ptr, int len) +{ + + return 0 ; +} /* voc_multi_read_les2i */ + + +static int +voc_multi_read_uc2f (SF_PRIVATE *psf, float *ptr, int len) +{ + + return 0 ; +} /* voc_multi_read_uc2f */ + +static int +voc_multi_read_les2f (SF_PRIVATE *psf, float *ptr, int len) +{ + + return 0 ; +} /* voc_multi_read_les2f */ + + +static int +voc_multi_read_uc2d (SF_PRIVATE *psf, double *ptr, int len) +{ + + return 0 ; +} /* voc_multi_read_uc2d */ + +static int +voc_multi_read_les2d (SF_PRIVATE *psf, double *ptr, int len) +{ + + return 0 ; +} /* voc_multi_read_les2d */ + +#endif + +/*------------------------------------------------------------------------------------ + +Creative Voice (VOC) file format +-------------------------------- + +~From: galt@dsd.es.com + +(byte numbers are hex!) + + HEADER (bytes 00-19) + Series of DATA BLOCKS (bytes 1A+) [Must end w/ Terminator Block] + +- --------------------------------------------------------------- + +HEADER: +======= + byte # Description + ------ ------------------------------------------ + 00-12 "Creative Voice File" + 13 1A (eof to abort printing of file) + 14-15 Offset of first datablock in .voc file (std 1A 00 + in Intel Notation) + 16-17 Version number (minor,major) (VOC-HDR puts 0A 01) + 18-19 1's Comp of Ver. # + 1234h (VOC-HDR puts 29 11) + +- --------------------------------------------------------------- + +DATA BLOCK: +=========== + + Data Block: TYPE(1-byte), SIZE(3-bytes), INFO(0+ bytes) + NOTE: Terminator Block is an exception -- it has only the TYPE byte. + + TYPE Description Size (3-byte int) Info + ---- ----------- ----------------- ----------------------- + 00 Terminator (NONE) (NONE) + 01 Sound data 2+length of data * + 02 Sound continue length of data Voice Data + 03 Silence 3 ** + 04 Marker 2 Marker# (2 bytes) + 05 ASCII length of string null terminated string + 06 Repeat 2 Count# (2 bytes) + 07 End repeat 0 (NONE) + 08 Extended 4 *** + + *Sound Info Format: + --------------------- + 00 Sample Rate + 01 Compression Type + 02+ Voice Data + + **Silence Info Format: + ---------------------------- + 00-01 Length of silence - 1 + 02 Sample Rate + + + ***Extended Info Format: + --------------------- + 00-01 Time Constant: Mono: 65536 - (256000000/sample_rate) + Stereo: 65536 - (25600000/(2*sample_rate)) + 02 Pack + 03 Mode: 0 = mono + 1 = stereo + + + Marker# -- Driver keeps the most recent marker in a status byte + Count# -- Number of repetitions + 1 + Count# may be 1 to FFFE for 0 - FFFD repetitions + or FFFF for endless repetitions + Sample Rate -- SR byte = 256-(1000000/sample_rate) + Length of silence -- in units of sampling cycle + Compression Type -- of voice data + 8-bits = 0 + 4-bits = 1 + 2.6-bits = 2 + 2-bits = 3 + Multi DAC = 3+(# of channels) [interesting-- + this isn't in the developer's manual] + + +--------------------------------------------------------------------------------- +Addendum submitted by Votis Kokavessis: + +After some experimenting with .VOC files I found out that there is a Data Block +Type 9, which is not covered in the VOC.TXT file. Here is what I was able to discover +about this block type: + + +TYPE: 09 +SIZE: 12 + length of data +INFO: 12 (twelve) bytes + +INFO STRUCTURE: + +Bytes 0-1: (Word) Sample Rate (e.g. 44100) +Bytes 2-3: zero (could be that bytes 0-3 are a DWord for Sample Rate) +Byte 4: Sample Size in bits (e.g. 16) +Byte 5: Number of channels (e.g. 1 for mono, 2 for stereo) +Byte 6: Unknown (equal to 4 in all files I examined) +Bytes 7-11: zero + + +-------------------------------------------------------------------------------------*/ + +/*===================================================================================== +**===================================================================================== +**===================================================================================== +**===================================================================================== +*/ + +/*------------------------------------------------------------------------ +The following is taken from the Audio File Formats FAQ dated 2-Jan-1995 +and submitted by Guido van Rossum . +-------------------------------------------------------------------------- +Creative Voice (VOC) file format +-------------------------------- + +From: galt@dsd.es.com + +(byte numbers are hex!) + + HEADER (bytes 00-19) + Series of DATA BLOCKS (bytes 1A+) [Must end w/ Terminator Block] + +- --------------------------------------------------------------- + +HEADER: +------- + byte # Description + ------ ------------------------------------------ + 00-12 "Creative Voice File" + 13 1A (eof to abort printing of file) + 14-15 Offset of first datablock in .voc file (std 1A 00 + in Intel Notation) + 16-17 Version number (minor,major) (VOC-HDR puts 0A 01) + 18-19 2's Comp of Ver. # + 1234h (VOC-HDR puts 29 11) + +- --------------------------------------------------------------- + +DATA BLOCK: +----------- + + Data Block: TYPE(1-byte), SIZE(3-bytes), INFO(0+ bytes) + NOTE: Terminator Block is an exception -- it has only the TYPE byte. + + TYPE Description Size (3-byte int) Info + ---- ----------- ----------------- ----------------------- + 00 Terminator (NONE) (NONE) + 01 Sound data 2+length of data * + 02 Sound continue length of data Voice Data + 03 Silence 3 ** + 04 Marker 2 Marker# (2 bytes) + 05 ASCII length of string null terminated string + 06 Repeat 2 Count# (2 bytes) + 07 End repeat 0 (NONE) + 08 Extended 4 *** + + *Sound Info Format: **Silence Info Format: + --------------------- ---------------------------- + 00 Sample Rate 00-01 Length of silence - 1 + 01 Compression Type 02 Sample Rate + 02+ Voice Data + + ***Extended Info Format: + --------------------- + 00-01 Time Constant: Mono: 65536 - (256000000/sample_rate) + Stereo: 65536 - (25600000/(2*sample_rate)) + 02 Pack + 03 Mode: 0 = mono + 1 = stereo + + + Marker# -- Driver keeps the most recent marker in a status byte + Count# -- Number of repetitions + 1 + Count# may be 1 to FFFE for 0 - FFFD repetitions + or FFFF for endless repetitions + Sample Rate -- SR byte = 256-(1000000/sample_rate) + Length of silence -- in units of sampling cycle + Compression Type -- of voice data + 8-bits = 0 + 4-bits = 1 + 2.6-bits = 2 + 2-bits = 3 + Multi DAC = 3+(# of channels) [interesting-- + this isn't in the developer's manual] + +Detailed description of new data blocks (VOC files version 1.20 and above): + + (Source is fax from Barry Boone at Creative Labs, 405/742-6622) + +BLOCK 8 - digitized sound attribute extension, must preceed block 1. + Used to define stereo, 8 bit audio + BYTE bBlockID; // = 8 + BYTE nBlockLen[3]; // 3 byte length + WORD wTimeConstant; // time constant = same as block 1 + BYTE bPackMethod; // same as in block 1 + BYTE bVoiceMode; // 0-mono, 1-stereo + + Data is stored left, right + +BLOCK 9 - data block that supersedes blocks 1 and 8. + Used for stereo, 16 bit. + + BYTE bBlockID; // = 9 + BYTE nBlockLen[3]; // length 12 plus length of sound + DWORD dwSamplesPerSec; // samples per second, not time const. + BYTE bBitsPerSample; // e.g., 8 or 16 + BYTE bChannels; // 1 for mono, 2 for stereo + WORD wFormat; // see below + BYTE reserved[4]; // pad to make block w/o data + // have a size of 16 bytes + + Valid values of wFormat are: + + 0x0000 8-bit unsigned PCM + 0x0001 Creative 8-bit to 4-bit ADPCM + 0x0002 Creative 8-bit to 3-bit ADPCM + 0x0003 Creative 8-bit to 2-bit ADPCM + 0x0004 16-bit signed PCM + 0x0006 CCITT a-Law + 0x0007 CCITT u-Law + 0x02000 Creative 16-bit to 4-bit ADPCM + + Data is stored left, right + +------------------------------------------------------------------------*/ +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 40a50167-a81c-463a-9e1d-3282ff84e09d +*/ diff --git a/src/vox_adpcm.c b/src/vox_adpcm.c new file mode 100644 index 00000000..11f333f4 --- /dev/null +++ b/src/vox_adpcm.c @@ -0,0 +1,537 @@ +/* +** Copyright (C) 2002-2005 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* +** This is the OKI / Dialogic ADPCM encoder/decoder. It converts from +** 12 bit linear sample data to a 4 bit ADPCM. +** +** Implemented from the description found here: +** +** http://www.comptek.ru:8100/telephony/tnotes/tt1-13.html +** +** and compared against the encoder/decoder found here: +** +** http://ibiblio.org/pub/linux/apps/sound/convert/vox.tar.gz +*/ + +#include "sfconfig.h" + +#include +#include +#include + +#include "sndfile.h" +#include "sfendian.h" +#include "float_cast.h" +#include "common.h" + +#define VOX_DATA_LEN 2048 +#define PCM_DATA_LEN (VOX_DATA_LEN *2) + +typedef struct +{ short last ; + short step_index ; + + int vox_bytes, pcm_samples ; + + unsigned char vox_data [VOX_DATA_LEN] ; + short pcm_data [PCM_DATA_LEN] ; +} VOX_ADPCM_PRIVATE ; + +static int vox_adpcm_encode_block (VOX_ADPCM_PRIVATE *pvox) ; +static int vox_adpcm_decode_block (VOX_ADPCM_PRIVATE *pvox) ; + +static short vox_adpcm_decode (char code, VOX_ADPCM_PRIVATE *pvox) ; +static char vox_adpcm_encode (short samp, VOX_ADPCM_PRIVATE *pvox) ; + +static sf_count_t vox_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ; +static sf_count_t vox_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ; +static sf_count_t vox_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ; +static sf_count_t vox_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ; + +static sf_count_t vox_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ; +static sf_count_t vox_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ; +static sf_count_t vox_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ; +static sf_count_t vox_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ; + +static int vox_read_block (SF_PRIVATE *psf, VOX_ADPCM_PRIVATE *pvox, short *ptr, int len) ; + +/*============================================================================================ +** Predefined OKI ADPCM encoder/decoder tables. +*/ + +static short step_size_table [49] = +{ 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, 60, + 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, + 230, 253, 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, + 724, 796, 876, 963, 1060, 1166, 1282, 1408, 1552 +} ; /* step_size_table */ + +static short step_adjust_table [8] = +{ -1, -1, -1, -1, 2, 4, 6, 8 +} ; /* step_adjust_table */ + +/*------------------------------------------------------------------------------ +*/ + +int +vox_adpcm_init (SF_PRIVATE *psf) +{ VOX_ADPCM_PRIVATE *pvox = NULL ; + + if (psf->mode == SFM_RDWR) + return SFE_BAD_MODE_RW ; + + if (psf->mode == SFM_WRITE && psf->sf.channels != 1) + return SFE_CHANNEL_COUNT ; + + if ((pvox = malloc (sizeof (VOX_ADPCM_PRIVATE))) == NULL) + return SFE_MALLOC_FAILED ; + + psf->codec_data = (void*) pvox ; + memset (pvox, 0, sizeof (VOX_ADPCM_PRIVATE)) ; + + if (psf->mode == SFM_WRITE) + { psf->write_short = vox_write_s ; + psf->write_int = vox_write_i ; + psf->write_float = vox_write_f ; + psf->write_double = vox_write_d ; + } + else + { psf_log_printf (psf, "Header-less OKI Dialogic ADPCM encoded file.\n") ; + psf_log_printf (psf, "Setting up for 8kHz, mono, Vox ADPCM.\n") ; + + psf->read_short = vox_read_s ; + psf->read_int = vox_read_i ; + psf->read_float = vox_read_f ; + psf->read_double = vox_read_d ; + } ; + + /* Standard sample rate chennels etc. */ + if (psf->sf.samplerate < 1) + psf->sf.samplerate = 8000 ; + psf->sf.channels = 1 ; + + psf->sf.frames = psf->filelength * 2 ; + + psf->sf.seekable = SF_FALSE ; + + /* Seek back to start of data. */ + if (psf_fseek (psf, 0 , SEEK_SET) == -1) + return SFE_BAD_SEEK ; + + return 0 ; +} /* vox_adpcm_init */ + +/*------------------------------------------------------------------------------ +*/ + +static char +vox_adpcm_encode (short samp, VOX_ADPCM_PRIVATE *pvox) +{ short code ; + short diff, error, stepsize ; + + stepsize = step_size_table [pvox->step_index] ; + code = 0 ; + + diff = samp - pvox->last ; + if (diff < 0) + { code = 0x08 ; + error = -diff ; + } + else + error = diff ; + + if (error >= stepsize) + { code = code | 0x04 ; + error -= stepsize ; + } ; + + if (error >= stepsize / 2) + { code = code | 0x02 ; + error -= stepsize / 2 ; + } ; + + if (error >= stepsize / 4) + code = code | 0x01 ; + + /* + ** To close the feedback loop, the deocder is used to set the + ** estimate of last sample and in doing so, also set the step_index. + */ + pvox->last = vox_adpcm_decode (code, pvox) ; + + return code ; +} /* vox_adpcm_encode */ + +static short +vox_adpcm_decode (char code, VOX_ADPCM_PRIVATE *pvox) +{ short diff, error, stepsize, samp ; + + stepsize = step_size_table [pvox->step_index] ; + + error = stepsize / 8 ; + + if (code & 0x01) + error += stepsize / 4 ; + + if (code & 0x02) + error += stepsize / 2 ; + + if (code & 0x04) + error += stepsize ; + + diff = (code & 0x08) ? -error : error ; + samp = pvox->last + diff ; + + /* + ** Apply clipping. + */ + if (samp > 2048) + samp = 2048 ; + if (samp < -2048) + samp = -2048 ; + + pvox->last = samp ; + pvox->step_index += step_adjust_table [code & 0x7] ; + + if (pvox->step_index < 0) + pvox->step_index = 0 ; + if (pvox->step_index > 48) + pvox->step_index = 48 ; + + return samp ; +} /* vox_adpcm_decode */ + +static int +vox_adpcm_encode_block (VOX_ADPCM_PRIVATE *pvox) +{ unsigned char code ; + int j, k ; + + /* If data_count is odd, add an extra zero valued sample. */ + if (pvox->pcm_samples & 1) + pvox->pcm_data [pvox->pcm_samples++] = 0 ; + + for (j = k = 0 ; k < pvox->pcm_samples ; j++) + { code = vox_adpcm_encode (pvox->pcm_data [k++] / 16, pvox) << 4 ; + code |= vox_adpcm_encode (pvox->pcm_data [k++] / 16, pvox) ; + pvox->vox_data [j] = code ; + } ; + + pvox->vox_bytes = j ; + + return 0 ; +} /* vox_adpcm_encode_block */ + +static int +vox_adpcm_decode_block (VOX_ADPCM_PRIVATE *pvox) +{ unsigned char code ; + int j, k ; + + for (j = k = 0 ; j < pvox->vox_bytes ; j++) + { code = pvox->vox_data [j] ; + pvox->pcm_data [k++] = 16 * vox_adpcm_decode ((code >> 4) & 0x0f, pvox) ; + pvox->pcm_data [k++] = 16 * vox_adpcm_decode (code & 0x0f, pvox) ; + } ; + + pvox->pcm_samples = k ; + + return 0 ; +} /* vox_adpcm_decode_block */ + +/*============================================================================== +*/ + +static int +vox_read_block (SF_PRIVATE *psf, VOX_ADPCM_PRIVATE *pvox, short *ptr, int len) +{ int indx = 0, k ; + + while (indx < len) + { pvox->vox_bytes = (len - indx > PCM_DATA_LEN) ? VOX_DATA_LEN : (len - indx + 1) / 2 ; + + if ((k = psf_fread (pvox->vox_data, 1, pvox->vox_bytes, psf)) != pvox->vox_bytes) + { if (psf_ftell (psf) + k != psf->filelength) + psf_log_printf (psf, "*** Warning : short read (%d != %d).\n", k, pvox->vox_bytes) ; + if (k == 0) + break ; + } ; + + pvox->vox_bytes = k ; + + vox_adpcm_decode_block (pvox) ; + + memcpy (&(ptr [indx]), pvox->pcm_data, pvox->pcm_samples * sizeof (short)) ; + indx += pvox->pcm_samples ; + } ; + + return indx ; +} /* vox_read_block */ + + +static sf_count_t +vox_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len) +{ VOX_ADPCM_PRIVATE *pvox ; + int readcount, count ; + sf_count_t total = 0 ; + + if (! psf->codec_data) + return 0 ; + pvox = (VOX_ADPCM_PRIVATE*) psf->codec_data ; + + while (len > 0) + { readcount = (len > 0x10000000) ? 0x10000000 : (int) len ; + + count = vox_read_block (psf, pvox, ptr, readcount) ; + + total += count ; + len -= count ; + if (count != readcount) + break ; + } ; + + return total ; +} /* vox_read_s */ + +static sf_count_t +vox_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len) +{ VOX_ADPCM_PRIVATE *pvox ; + short *sptr ; + int k, bufferlen, readcount, count ; + sf_count_t total = 0 ; + + if (! psf->codec_data) + return 0 ; + pvox = (VOX_ADPCM_PRIVATE*) psf->codec_data ; + + sptr = psf->u.sbuf ; + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + while (len > 0) + { readcount = (len >= bufferlen) ? bufferlen : (int) len ; + count = vox_read_block (psf, pvox, sptr, readcount) ; + for (k = 0 ; k < readcount ; k++) + ptr [total + k] = ((int) sptr [k]) << 16 ; + total += count ; + len -= readcount ; + if (count != readcount) + break ; + } ; + + return total ; +} /* vox_read_i */ + +static sf_count_t +vox_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len) +{ VOX_ADPCM_PRIVATE *pvox ; + short *sptr ; + int k, bufferlen, readcount, count ; + sf_count_t total = 0 ; + float normfact ; + + if (! psf->codec_data) + return 0 ; + pvox = (VOX_ADPCM_PRIVATE*) psf->codec_data ; + + normfact = (psf->norm_float == SF_TRUE) ? 1.0 / ((float) 0x8000) : 1.0 ; + + sptr = psf->u.sbuf ; + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + while (len > 0) + { readcount = (len >= bufferlen) ? bufferlen : (int) len ; + count = vox_read_block (psf, pvox, sptr, readcount) ; + for (k = 0 ; k < readcount ; k++) + ptr [total + k] = normfact * (float) (sptr [k]) ; + total += count ; + len -= readcount ; + if (count != readcount) + break ; + } ; + + return total ; +} /* vox_read_f */ + +static sf_count_t +vox_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len) +{ VOX_ADPCM_PRIVATE *pvox ; + short *sptr ; + int k, bufferlen, readcount, count ; + sf_count_t total = 0 ; + double normfact ; + + if (! psf->codec_data) + return 0 ; + pvox = (VOX_ADPCM_PRIVATE*) psf->codec_data ; + + normfact = (psf->norm_double == SF_TRUE) ? 1.0 / ((double) 0x8000) : 1.0 ; + + sptr = psf->u.sbuf ; + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + while (len > 0) + { readcount = (len >= bufferlen) ? bufferlen : (int) len ; + count = vox_read_block (psf, pvox, sptr, readcount) ; + for (k = 0 ; k < readcount ; k++) + ptr [total + k] = normfact * (double) (sptr [k]) ; + total += count ; + len -= readcount ; + if (count != readcount) + break ; + } ; + + return total ; +} /* vox_read_d */ + +/*------------------------------------------------------------------------------ +*/ + +static int +vox_write_block (SF_PRIVATE *psf, VOX_ADPCM_PRIVATE *pvox, const short *ptr, int len) +{ int indx = 0, k ; + + while (indx < len) + { pvox->pcm_samples = (len - indx > PCM_DATA_LEN) ? PCM_DATA_LEN : len - indx ; + + memcpy (pvox->pcm_data, &(ptr [indx]), pvox->pcm_samples * sizeof (short)) ; + + vox_adpcm_encode_block (pvox) ; + + if ((k = psf_fwrite (pvox->vox_data, 1, pvox->vox_bytes, psf)) != pvox->vox_bytes) + psf_log_printf (psf, "*** Warning : short read (%d != %d).\n", k, pvox->vox_bytes) ; + + indx += pvox->pcm_samples ; + } ; + + return indx ; +} /* vox_write_block */ + +static sf_count_t +vox_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len) +{ VOX_ADPCM_PRIVATE *pvox ; + int writecount, count ; + sf_count_t total = 0 ; + + if (! psf->codec_data) + return 0 ; + pvox = (VOX_ADPCM_PRIVATE*) psf->codec_data ; + + while (len) + { writecount = (len > 0x10000000) ? 0x10000000 : (int) len ; + + count = vox_write_block (psf, pvox, ptr, writecount) ; + + total += count ; + len -= count ; + if (count != writecount) + break ; + } ; + + return total ; +} /* vox_write_s */ + +static sf_count_t +vox_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t len) +{ VOX_ADPCM_PRIVATE *pvox ; + short *sptr ; + int k, bufferlen, writecount, count ; + sf_count_t total = 0 ; + + if (! psf->codec_data) + return 0 ; + pvox = (VOX_ADPCM_PRIVATE*) psf->codec_data ; + + sptr = psf->u.sbuf ; + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + while (len > 0) + { writecount = (len >= bufferlen) ? bufferlen : (int) len ; + for (k = 0 ; k < writecount ; k++) + sptr [k] = ptr [total + k] >> 16 ; + count = vox_write_block (psf, pvox, sptr, writecount) ; + total += count ; + len -= writecount ; + if (count != writecount) + break ; + } ; + + return total ; +} /* vox_write_i */ + +static sf_count_t +vox_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len) +{ VOX_ADPCM_PRIVATE *pvox ; + short *sptr ; + int k, bufferlen, writecount, count ; + sf_count_t total = 0 ; + float normfact ; + + if (! psf->codec_data) + return 0 ; + pvox = (VOX_ADPCM_PRIVATE*) psf->codec_data ; + + normfact = (psf->norm_float == SF_TRUE) ? (1.0 * 0x7FFF) : 1.0 ; + + sptr = psf->u.sbuf ; + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + while (len > 0) + { writecount = (len >= bufferlen) ? bufferlen : (int) len ; + for (k = 0 ; k < writecount ; k++) + sptr [k] = lrintf (normfact * ptr [total + k]) ; + count = vox_write_block (psf, pvox, sptr, writecount) ; + total += count ; + len -= writecount ; + if (count != writecount) + break ; + } ; + + return total ; +} /* vox_write_f */ + +static sf_count_t +vox_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len) +{ VOX_ADPCM_PRIVATE *pvox ; + short *sptr ; + int k, bufferlen, writecount, count ; + sf_count_t total = 0 ; + double normfact ; + + if (! psf->codec_data) + return 0 ; + pvox = (VOX_ADPCM_PRIVATE*) psf->codec_data ; + + normfact = (psf->norm_double == SF_TRUE) ? (1.0 * 0x7FFF) : 1.0 ; + + sptr = psf->u.sbuf ; + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + while (len > 0) + { writecount = (len >= bufferlen) ? bufferlen : (int) len ; + for (k = 0 ; k < writecount ; k++) + sptr [k] = lrint (normfact * ptr [total + k]) ; + count = vox_write_block (psf, pvox, sptr, writecount) ; + total += count ; + len -= writecount ; + if (count != writecount) + break ; + } ; + + return total ; +} /* vox_write_d */ + + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: e15e97fe-ff9d-4b46-a489-7059fb2d0b1e +*/ diff --git a/src/w64.c b/src/w64.c new file mode 100644 index 00000000..1fd7ddea --- /dev/null +++ b/src/w64.c @@ -0,0 +1,587 @@ +/* +** Copyright (C) 1999-2007 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include +#include + +#include "sndfile.h" +#include "sfendian.h" +#include "common.h" +#include "wav_w64.h" + +/*------------------------------------------------------------------------------ +** W64 files use 16 byte markers as opposed to the four byte marker of +** WAV files. +** For comparison purposes, an integer is required, so make an integer +** hash for the 16 bytes using MAKE_HASH16 macro, but also create a 16 +** byte array containing the complete 16 bytes required when writing the +** header. +*/ + +#define MAKE_HASH16(x0,x1,x2,x3,x4,x5,x6,x7,x8,x9,xa,xb,xc,xd,xe,xf) \ + ( (x0) ^ ((x1) << 1) ^ ((x2) << 2) ^ ((x3) << 3) ^ \ + ((x4) << 4) ^ ((x5) << 5) ^ ((x6) << 6) ^ ((x7) << 7) ^ \ + ((x8) << 8) ^ ((x9) << 9) ^ ((xa) << 10) ^ ((xb) << 11) ^ \ + ((xc) << 12) ^ ((xd) << 13) ^ ((xe) << 14) ^ ((xf) << 15) ) + +#define MAKE_MARKER16(name,x0,x1,x2,x3,x4,x5,x6,x7,x8,x9,xa,xb,xc,xd,xe,xf) \ + static unsigned char name [16] = { (x0), (x1), (x2), (x3), (x4), (x5), \ + (x6), (x7), (x8), (x9), (xa), (xb), (xc), (xd), (xe), (xf) } + +#define riff_HASH16 MAKE_HASH16 ('r', 'i', 'f', 'f', 0x2E, 0x91, 0xCF, 0x11, 0xA5, \ + 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00) + +#define wave_HASH16 MAKE_HASH16 ('w', 'a', 'v', 'e', 0xF3, 0xAC, 0xD3, 0x11, \ + 0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A) + +#define fmt_HASH16 MAKE_HASH16 ('f', 'm', 't', ' ', 0xF3, 0xAC, 0xD3, 0x11, \ + 0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A) + +#define fact_HASH16 MAKE_HASH16 ('f', 'a', 'c', 't', 0xF3, 0xAC, 0xD3, 0x11, \ + 0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A) + +#define data_HASH16 MAKE_HASH16 ('d', 'a', 't', 'a', 0xF3, 0xAC, 0xD3, 0x11, \ + 0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A) + +#define ACID_HASH16 MAKE_HASH16 (0x6D, 0x07, 0x1C, 0xEA, 0xA3, 0xEF, 0x78, 0x4C, \ + 0x90, 0x57, 0x7F, 0x79, 0xEE, 0x25, 0x2A, 0xAE) + +MAKE_MARKER16 (riff_MARKER16, 'r', 'i', 'f', 'f', 0x2E, 0x91, 0xCF, 0x11, + 0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00) ; + + +MAKE_MARKER16 (wave_MARKER16, 'w', 'a', 'v', 'e', 0xF3, 0xAC, 0xD3, 0x11, + 0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A) ; + +MAKE_MARKER16 (fmt_MARKER16, 'f', 'm', 't', ' ', 0xF3, 0xAC, 0xD3, 0x11, + 0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A) ; + +MAKE_MARKER16 (fact_MARKER16, 'f', 'a', 'c', 't', 0xF3, 0xAC, 0xD3, 0x11, + 0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A) ; + +MAKE_MARKER16 (data_MARKER16, 'd', 'a', 't', 'a', 0xF3, 0xAC, 0xD3, 0x11, + 0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A) ; + +enum +{ HAVE_riff = 0x01, + HAVE_wave = 0x02, + HAVE_fmt = 0x04, + HAVE_fact = 0x08, + HAVE_data = 0x20 +} ; + +/*------------------------------------------------------------------------------ + * Private static functions. + */ + +static int w64_read_header (SF_PRIVATE *psf, int *blockalign, int *framesperblock) ; +static int w64_write_header (SF_PRIVATE *psf, int calc_length) ; +static int w64_close (SF_PRIVATE *psf) ; + +/*------------------------------------------------------------------------------ +** Public function. +*/ + +int +w64_open (SF_PRIVATE *psf) +{ WAV_PRIVATE * wpriv ; + int subformat, error, blockalign = 0, framesperblock = 0 ; + + if ((wpriv = calloc (1, sizeof (WAV_PRIVATE))) == NULL) + return SFE_MALLOC_FAILED ; + psf->container_data = wpriv ; + + if (psf->mode == SFM_READ || (psf->mode == SFM_RDWR &&psf->filelength > 0)) + { if ((error = w64_read_header (psf, &blockalign, &framesperblock))) + return error ; + } ; + + if ((psf->sf.format & SF_FORMAT_TYPEMASK) != SF_FORMAT_W64) + return SFE_BAD_OPEN_FORMAT ; + + subformat = psf->sf.format & SF_FORMAT_SUBMASK ; + + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + { if (psf->is_pipe) + return SFE_NO_PIPE_WRITE ; + + psf->endian = SF_ENDIAN_LITTLE ; /* All W64 files are little endian. */ + + psf->blockwidth = psf->bytewidth * psf->sf.channels ; + + if (subformat == SF_FORMAT_IMA_ADPCM || subformat == SF_FORMAT_MS_ADPCM) + { blockalign = wav_w64_srate2blocksize (psf->sf.samplerate * psf->sf.channels) ; + framesperblock = -1 ; + + /* FIXME : This block must go */ + psf->filelength = SF_COUNT_MAX ; + psf->datalength = psf->filelength ; + if (psf->sf.frames <= 0) + psf->sf.frames = (psf->blockwidth) ? psf->filelength / psf->blockwidth : psf->filelength ; + /* EMXIF : This block must go */ + } ; + + if ((error = w64_write_header (psf, SF_FALSE))) + return error ; + + psf->write_header = w64_write_header ; + } ; + + psf->container_close = w64_close ; + + switch (subformat) + { case SF_FORMAT_PCM_U8 : + error = pcm_init (psf) ; + break ; + + case SF_FORMAT_PCM_16 : + case SF_FORMAT_PCM_24 : + case SF_FORMAT_PCM_32 : + error = pcm_init (psf) ; + break ; + + case SF_FORMAT_ULAW : + error = ulaw_init (psf) ; + break ; + + case SF_FORMAT_ALAW : + error = alaw_init (psf) ; + break ; + + /* Lite remove start */ + case SF_FORMAT_FLOAT : + error = float32_init (psf) ; + break ; + + case SF_FORMAT_DOUBLE : + error = double64_init (psf) ; + break ; + + case SF_FORMAT_IMA_ADPCM : + error = wav_w64_ima_init (psf, blockalign, framesperblock) ; + break ; + + case SF_FORMAT_MS_ADPCM : + error = wav_w64_msadpcm_init (psf, blockalign, framesperblock) ; + break ; + /* Lite remove end */ + + case SF_FORMAT_GSM610 : + error = gsm610_init (psf) ; + break ; + + default : return SFE_UNIMPLEMENTED ; + } ; + + return error ; +} /* w64_open */ + +/*========================================================================= +** Private functions. +*/ + +static int +w64_read_header (SF_PRIVATE *psf, int *blockalign, int *framesperblock) +{ WAV_PRIVATE *wpriv ; + WAV_FMT *wav_fmt ; + int dword = 0, marker, format = 0 ; + sf_count_t chunk_size, bytesread = 0 ; + int parsestage = 0, error, done = 0 ; + + if ((wpriv = psf->container_data) == NULL) + return SFE_INTERNAL ; + wav_fmt = &wpriv->wav_fmt ; + + /* Set position to start of file to begin reading header. */ + psf_binheader_readf (psf, "p", 0) ; + + while (! done) + { /* Read the 4 byte marker and jump 12 bytes. */ + bytesread += psf_binheader_readf (psf, "h", &marker) ; + chunk_size = 0 ; + + switch (marker) + { case riff_HASH16 : + if (parsestage) + return SFE_W64_NO_RIFF ; + + bytesread += psf_binheader_readf (psf, "e8", &chunk_size) ; + + if (psf->filelength < chunk_size) + psf_log_printf (psf, "riff : %D (should be %D)\n", chunk_size, psf->filelength) ; + else + psf_log_printf (psf, "riff : %D\n", chunk_size) ; + + parsestage |= HAVE_riff ; + break ; + + case ACID_HASH16: + psf_log_printf (psf, "Looks like an ACID file. Exiting.\n") ; + return SFE_UNIMPLEMENTED ; + + case wave_HASH16 : + if ((parsestage & HAVE_riff) != HAVE_riff) + return SFE_W64_NO_WAVE ; + psf_log_printf (psf, "wave\n") ; + parsestage |= HAVE_wave ; + break ; + + case fmt_HASH16 : + if ((parsestage & (HAVE_riff | HAVE_wave)) != (HAVE_riff | HAVE_wave)) + return SFE_W64_NO_FMT ; + + bytesread += psf_binheader_readf (psf, "e8", &chunk_size) ; + psf_log_printf (psf, " fmt : %D\n", chunk_size) ; + + /* size of 16 byte marker and 8 byte chunk_size value. */ + chunk_size -= 24 ; + + if ((error = wav_w64_read_fmt_chunk (psf, (int) chunk_size))) + return error ; + + if (chunk_size % 8) + psf_binheader_readf (psf, "j", 8 - (chunk_size % 8)) ; + + format = wav_fmt->format ; + parsestage |= HAVE_fmt ; + break ; + + case fact_HASH16: + { sf_count_t frames ; + + psf_binheader_readf (psf, "e88", &chunk_size, &frames) ; + psf_log_printf (psf, " fact : %D\n frames : %D\n", + chunk_size, frames) ; + } ; + break ; + + + case data_HASH16 : + if ((parsestage & (HAVE_riff | HAVE_wave | HAVE_fmt)) != (HAVE_riff | HAVE_wave | HAVE_fmt)) + return SFE_W64_NO_DATA ; + + psf_binheader_readf (psf, "e8", &chunk_size) ; + + psf->dataoffset = psf_ftell (psf) ; + + psf->datalength = chunk_size - 24 ; + + if (chunk_size % 8) + chunk_size += 8 - (chunk_size % 8) ; + + psf_log_printf (psf, "data : %D\n", chunk_size) ; + + parsestage |= HAVE_data ; + + if (! psf->sf.seekable) + break ; + + /* Seek past data and continue reading header. */ + psf_fseek (psf, chunk_size, SEEK_CUR) ; + break ; + + default : + if (psf_ftell (psf) & 0x0F) + { psf_log_printf (psf, " Unknown chunk marker at position %d. Resynching.\n", dword - 4) ; + psf_binheader_readf (psf, "j", -3) ; + break ; + } ; + psf_log_printf (psf, "*** Unknown chunk marker : %X. Exiting parser.\n", marker) ; + done = SF_TRUE ; + break ; + } ; /* switch (dword) */ + + if (psf->sf.seekable == 0 && (parsestage & HAVE_data)) + break ; + + if (psf_ftell (psf) >= (psf->filelength - (2 * SIGNED_SIZEOF (dword)))) + break ; + } ; /* while (1) */ + + if (! psf->dataoffset) + return SFE_W64_NO_DATA ; + + psf->endian = SF_ENDIAN_LITTLE ; /* All WAV files are little endian. */ + + if (psf_ftell (psf) != psf->dataoffset) + psf_fseek (psf, psf->dataoffset, SEEK_SET) ; + + if (psf->blockwidth) + { if (psf->filelength - psf->dataoffset < psf->datalength) + psf->sf.frames = (psf->filelength - psf->dataoffset) / psf->blockwidth ; + else + psf->sf.frames = psf->datalength / psf->blockwidth ; + } ; + + switch (format) + { case WAVE_FORMAT_PCM : + case WAVE_FORMAT_EXTENSIBLE : + /* extensible might be FLOAT, MULAW, etc as well! */ + psf->sf.format = SF_FORMAT_W64 | u_bitwidth_to_subformat (psf->bytewidth * 8) ; + break ; + + case WAVE_FORMAT_MULAW : + psf->sf.format = (SF_FORMAT_W64 | SF_FORMAT_ULAW) ; + break ; + + case WAVE_FORMAT_ALAW : + psf->sf.format = (SF_FORMAT_W64 | SF_FORMAT_ALAW) ; + break ; + + case WAVE_FORMAT_MS_ADPCM : + psf->sf.format = (SF_FORMAT_W64 | SF_FORMAT_MS_ADPCM) ; + *blockalign = wav_fmt->msadpcm.blockalign ; + *framesperblock = wav_fmt->msadpcm.samplesperblock ; + break ; + + case WAVE_FORMAT_IMA_ADPCM : + psf->sf.format = (SF_FORMAT_W64 | SF_FORMAT_IMA_ADPCM) ; + *blockalign = wav_fmt->ima.blockalign ; + *framesperblock = wav_fmt->ima.samplesperblock ; + break ; + + case WAVE_FORMAT_GSM610 : + psf->sf.format = (SF_FORMAT_W64 | SF_FORMAT_GSM610) ; + break ; + + case WAVE_FORMAT_IEEE_FLOAT : + psf->sf.format = SF_FORMAT_W64 ; + psf->sf.format |= (psf->bytewidth == 8) ? SF_FORMAT_DOUBLE : SF_FORMAT_FLOAT ; + break ; + + default : return SFE_UNIMPLEMENTED ; + } ; + + return 0 ; +} /* w64_read_header */ + +static int +w64_write_header (SF_PRIVATE *psf, int calc_length) +{ sf_count_t fmt_size, current ; + size_t fmt_pad = 0 ; + int subformat, add_fact_chunk = SF_FALSE ; + + current = psf_ftell (psf) ; + + if (calc_length) + { psf->filelength = psf_get_filelen (psf) ; + + psf->datalength = psf->filelength - psf->dataoffset ; + if (psf->dataend) + psf->datalength -= psf->filelength - psf->dataend ; + + if (psf->bytewidth) + psf->sf.frames = psf->datalength / (psf->bytewidth * psf->sf.channels) ; + } ; + + /* Reset the current header length to zero. */ + psf->header [0] = 0 ; + psf->headindex = 0 ; + psf_fseek (psf, 0, SEEK_SET) ; + + /* riff marker, length, wave and 'fmt ' markers. */ + psf_binheader_writef (psf, "eh8hh", riff_MARKER16, psf->filelength - 8, wave_MARKER16, fmt_MARKER16) ; + + subformat = psf->sf.format & SF_FORMAT_SUBMASK ; + + switch (subformat) + { case SF_FORMAT_PCM_U8 : + case SF_FORMAT_PCM_16 : + case SF_FORMAT_PCM_24 : + case SF_FORMAT_PCM_32 : + fmt_size = 24 + 2 + 2 + 4 + 4 + 2 + 2 ; + fmt_pad = (size_t) (8 - (fmt_size & 0x7)) ; + fmt_size += fmt_pad ; + + /* fmt : format, channels, samplerate */ + psf_binheader_writef (psf, "e8224", fmt_size, WAVE_FORMAT_PCM, psf->sf.channels, psf->sf.samplerate) ; + /* fmt : bytespersec */ + psf_binheader_writef (psf, "e4", psf->sf.samplerate * psf->bytewidth * psf->sf.channels) ; + /* fmt : blockalign, bitwidth */ + psf_binheader_writef (psf, "e22", psf->bytewidth * psf->sf.channels, psf->bytewidth * 8) ; + break ; + + case SF_FORMAT_FLOAT : + case SF_FORMAT_DOUBLE : + fmt_size = 24 + 2 + 2 + 4 + 4 + 2 + 2 ; + fmt_pad = (size_t) (8 - (fmt_size & 0x7)) ; + fmt_size += fmt_pad ; + + /* fmt : format, channels, samplerate */ + psf_binheader_writef (psf, "e8224", fmt_size, WAVE_FORMAT_IEEE_FLOAT, psf->sf.channels, psf->sf.samplerate) ; + /* fmt : bytespersec */ + psf_binheader_writef (psf, "e4", psf->sf.samplerate * psf->bytewidth * psf->sf.channels) ; + /* fmt : blockalign, bitwidth */ + psf_binheader_writef (psf, "e22", psf->bytewidth * psf->sf.channels, psf->bytewidth * 8) ; + + add_fact_chunk = SF_TRUE ; + break ; + + case SF_FORMAT_ULAW : + fmt_size = 24 + 2 + 2 + 4 + 4 + 2 + 2 ; + fmt_pad = (size_t) (8 - (fmt_size & 0x7)) ; + fmt_size += fmt_pad ; + + /* fmt : format, channels, samplerate */ + psf_binheader_writef (psf, "e8224", fmt_size, WAVE_FORMAT_MULAW, psf->sf.channels, psf->sf.samplerate) ; + /* fmt : bytespersec */ + psf_binheader_writef (psf, "e4", psf->sf.samplerate * psf->bytewidth * psf->sf.channels) ; + /* fmt : blockalign, bitwidth */ + psf_binheader_writef (psf, "e22", psf->bytewidth * psf->sf.channels, 8) ; + + add_fact_chunk = SF_TRUE ; + break ; + + case SF_FORMAT_ALAW : + fmt_size = 24 + 2 + 2 + 4 + 4 + 2 + 2 ; + fmt_pad = (size_t) (8 - (fmt_size & 0x7)) ; + fmt_size += fmt_pad ; + + /* fmt : format, channels, samplerate */ + psf_binheader_writef (psf, "e8224", fmt_size, WAVE_FORMAT_ALAW, psf->sf.channels, psf->sf.samplerate) ; + /* fmt : bytespersec */ + psf_binheader_writef (psf, "e4", psf->sf.samplerate * psf->bytewidth * psf->sf.channels) ; + /* fmt : blockalign, bitwidth */ + psf_binheader_writef (psf, "e22", psf->bytewidth * psf->sf.channels, 8) ; + + add_fact_chunk = SF_TRUE ; + break ; + + /* Lite remove start */ + case SF_FORMAT_IMA_ADPCM : + { int blockalign, framesperblock, bytespersec ; + + blockalign = wav_w64_srate2blocksize (psf->sf.samplerate * psf->sf.channels) ; + framesperblock = 2 * (blockalign - 4 * psf->sf.channels) / psf->sf.channels + 1 ; + bytespersec = (psf->sf.samplerate * blockalign) / framesperblock ; + + /* fmt chunk. */ + fmt_size = 24 + 2 + 2 + 4 + 4 + 2 + 2 + 2 + 2 ; + fmt_pad = (size_t) (8 - (fmt_size & 0x7)) ; + fmt_size += fmt_pad ; + + /* fmt : size, WAV format type, channels. */ + psf_binheader_writef (psf, "e822", fmt_size, WAVE_FORMAT_IMA_ADPCM, psf->sf.channels) ; + + /* fmt : samplerate, bytespersec. */ + psf_binheader_writef (psf, "e44", psf->sf.samplerate, bytespersec) ; + + /* fmt : blockalign, bitwidth, extrabytes, framesperblock. */ + psf_binheader_writef (psf, "e2222", blockalign, 4, 2, framesperblock) ; + } ; + + add_fact_chunk = SF_TRUE ; + break ; + + case SF_FORMAT_MS_ADPCM : + { int blockalign, framesperblock, bytespersec, extrabytes ; + + blockalign = wav_w64_srate2blocksize (psf->sf.samplerate * psf->sf.channels) ; + framesperblock = 2 + 2 * (blockalign - 7 * psf->sf.channels) / psf->sf.channels ; + bytespersec = (psf->sf.samplerate * blockalign) / framesperblock ; + + /* fmt chunk. */ + extrabytes = 2 + 2 + MSADPCM_ADAPT_COEFF_COUNT * (2 + 2) ; + fmt_size = 24 + 2 + 2 + 4 + 4 + 2 + 2 + 2 + extrabytes ; + fmt_pad = (size_t) (8 - (fmt_size & 0x7)) ; + fmt_size += fmt_pad ; + + /* fmt : size, W64 format type, channels. */ + psf_binheader_writef (psf, "e822", fmt_size, WAVE_FORMAT_MS_ADPCM, psf->sf.channels) ; + + /* fmt : samplerate, bytespersec. */ + psf_binheader_writef (psf, "e44", psf->sf.samplerate, bytespersec) ; + + /* fmt : blockalign, bitwidth, extrabytes, framesperblock. */ + psf_binheader_writef (psf, "e22222", blockalign, 4, extrabytes, framesperblock, 7) ; + + msadpcm_write_adapt_coeffs (psf) ; + } ; + + add_fact_chunk = SF_TRUE ; + break ; + /* Lite remove end */ + + case SF_FORMAT_GSM610 : + { int bytespersec ; + + bytespersec = (psf->sf.samplerate * WAV_W64_GSM610_BLOCKSIZE) / WAV_W64_GSM610_SAMPLES ; + + /* fmt chunk. */ + fmt_size = 24 + 2 + 2 + 4 + 4 + 2 + 2 + 2 + 2 ; + fmt_pad = (size_t) (8 - (fmt_size & 0x7)) ; + fmt_size += fmt_pad ; + + /* fmt : size, WAV format type, channels. */ + psf_binheader_writef (psf, "e822", fmt_size, WAVE_FORMAT_GSM610, psf->sf.channels) ; + + /* fmt : samplerate, bytespersec. */ + psf_binheader_writef (psf, "e44", psf->sf.samplerate, bytespersec) ; + + /* fmt : blockalign, bitwidth, extrabytes, framesperblock. */ + psf_binheader_writef (psf, "e2222", WAV_W64_GSM610_BLOCKSIZE, 0, 2, WAV_W64_GSM610_SAMPLES) ; + } ; + + add_fact_chunk = SF_TRUE ; + break ; + + default : return SFE_UNIMPLEMENTED ; + } ; + + /* Pad to 8 bytes with zeros. */ + if (fmt_pad > 0) + psf_binheader_writef (psf, "z", fmt_pad) ; + + if (add_fact_chunk) + psf_binheader_writef (psf, "eh88", fact_MARKER16, (sf_count_t) (16 + 8 + 8), psf->sf.frames) ; + + psf_binheader_writef (psf, "eh8", data_MARKER16, psf->datalength + 24) ; + psf_fwrite (psf->header, psf->headindex, 1, psf) ; + + if (psf->error) + return psf->error ; + + psf->dataoffset = psf->headindex ; + + if (current > 0) + psf_fseek (psf, current, SEEK_SET) ; + + return psf->error ; +} /* w64_write_header */ + +static int +w64_close (SF_PRIVATE *psf) +{ + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + w64_write_header (psf, SF_TRUE) ; + + return 0 ; +} /* w64_close */ + + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 9aa4e141-538a-4dd9-99c9-b3f0f2dd4f4a +*/ diff --git a/src/wav.c b/src/wav.c new file mode 100644 index 00000000..09d4ce09 --- /dev/null +++ b/src/wav.c @@ -0,0 +1,1673 @@ +/* +** Copyright (C) 1999-2007 Erik de Castro Lopo +** Copyright (C) 2004-2005 David Viens +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include +#include +#include + +#include "sndfile.h" +#include "sfendian.h" +#include "common.h" +#include "wav_w64.h" + +/*------------------------------------------------------------------------------ + * Macros to handle big/little endian issues. + */ + +#define RIFF_MARKER (MAKE_MARKER ('R', 'I', 'F', 'F')) +#define RIFX_MARKER (MAKE_MARKER ('R', 'I', 'F', 'X')) +#define WAVE_MARKER (MAKE_MARKER ('W', 'A', 'V', 'E')) +#define fmt_MARKER (MAKE_MARKER ('f', 'm', 't', ' ')) +#define data_MARKER (MAKE_MARKER ('d', 'a', 't', 'a')) +#define fact_MARKER (MAKE_MARKER ('f', 'a', 'c', 't')) +#define PEAK_MARKER (MAKE_MARKER ('P', 'E', 'A', 'K')) + +#define cue_MARKER (MAKE_MARKER ('c', 'u', 'e', ' ')) +#define LIST_MARKER (MAKE_MARKER ('L', 'I', 'S', 'T')) +#define slnt_MARKER (MAKE_MARKER ('s', 'l', 'n', 't')) +#define wavl_MARKER (MAKE_MARKER ('w', 'a', 'v', 'l')) +#define INFO_MARKER (MAKE_MARKER ('I', 'N', 'F', 'O')) +#define plst_MARKER (MAKE_MARKER ('p', 'l', 's', 't')) +#define adtl_MARKER (MAKE_MARKER ('a', 'd', 't', 'l')) +#define labl_MARKER (MAKE_MARKER ('l', 'a', 'b', 'l')) +#define ltxt_MARKER (MAKE_MARKER ('l', 't', 'x', 't')) +#define note_MARKER (MAKE_MARKER ('n', 'o', 't', 'e')) +#define smpl_MARKER (MAKE_MARKER ('s', 'm', 'p', 'l')) +#define bext_MARKER (MAKE_MARKER ('b', 'e', 'x', 't')) +#define levl_MARKER (MAKE_MARKER ('l', 'e', 'v', 'l')) +#define MEXT_MARKER (MAKE_MARKER ('M', 'E', 'X', 'T')) +#define DISP_MARKER (MAKE_MARKER ('D', 'I', 'S', 'P')) +#define acid_MARKER (MAKE_MARKER ('a', 'c', 'i', 'd')) +#define strc_MARKER (MAKE_MARKER ('s', 't', 'r', 'c')) +#define PAD_MARKER (MAKE_MARKER ('P', 'A', 'D', ' ')) +#define afsp_MARKER (MAKE_MARKER ('a', 'f', 's', 'p')) +#define clm_MARKER (MAKE_MARKER ('c', 'l', 'm', ' ')) +#define elmo_MARKER (MAKE_MARKER ('e', 'l', 'm', 'o')) +#define cart_MARKER (MAKE_MARKER ('c', 'a', 'r', 't')) + +#define ISFT_MARKER (MAKE_MARKER ('I', 'S', 'F', 'T')) +#define ICRD_MARKER (MAKE_MARKER ('I', 'C', 'R', 'D')) +#define ICOP_MARKER (MAKE_MARKER ('I', 'C', 'O', 'P')) +#define IARL_MARKER (MAKE_MARKER ('I', 'A', 'R', 'L')) +#define IART_MARKER (MAKE_MARKER ('I', 'A', 'R', 'T')) +#define INAM_MARKER (MAKE_MARKER ('I', 'N', 'A', 'M')) +#define IENG_MARKER (MAKE_MARKER ('I', 'E', 'N', 'G')) +#define IART_MARKER (MAKE_MARKER ('I', 'A', 'R', 'T')) +#define ICOP_MARKER (MAKE_MARKER ('I', 'C', 'O', 'P')) +#define IPRD_MARKER (MAKE_MARKER ('I', 'P', 'R', 'D')) +#define ISRC_MARKER (MAKE_MARKER ('I', 'S', 'R', 'C')) +#define ISBJ_MARKER (MAKE_MARKER ('I', 'S', 'B', 'J')) +#define ICMT_MARKER (MAKE_MARKER ('I', 'C', 'M', 'T')) + +/* Weird WAVPACK marker which can show up at the start of the DATA section. */ +#define wvpk_MARKER (MAKE_MARKER ('w', 'v', 'p', 'k')) +#define OggS_MARKER (MAKE_MARKER ('O', 'g', 'g', 'S')) + +#define WAV_PEAK_CHUNK_SIZE(ch) (2 * sizeof (int) + ch * (sizeof (float) + sizeof (int))) +#define WAV_BEXT_CHUNK_SIZE 602 + +enum +{ HAVE_RIFF = 0x01, + HAVE_WAVE = 0x02, + HAVE_fmt = 0x04, + HAVE_fact = 0x08, + HAVE_PEAK = 0x10, + HAVE_data = 0x20, + HAVE_other = 0x80000000 +} ; + + + +/* known WAVEFORMATEXTENSIBLE GUIDS */ +static const EXT_SUBFORMAT MSGUID_SUBTYPE_PCM = +{ 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } +} ; + +static const EXT_SUBFORMAT MSGUID_SUBTYPE_MS_ADPCM = +{ 0x00000002, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } +} ; + +static const EXT_SUBFORMAT MSGUID_SUBTYPE_IEEE_FLOAT = +{ 0x00000003, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } +} ; + +static const EXT_SUBFORMAT MSGUID_SUBTYPE_ALAW = +{ 0x00000006, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } +} ; + +static const EXT_SUBFORMAT MSGUID_SUBTYPE_MULAW = +{ 0x00000007, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } +} ; + +/* +** the next two are from +** http://dream.cs.bath.ac.uk/researchdev/wave-ex/bformat.html +*/ +static const EXT_SUBFORMAT MSGUID_SUBTYPE_AMBISONIC_B_FORMAT_PCM = +{ 0x00000001, 0x0721, 0x11d3, { 0x86, 0x44, 0xC8, 0xC1, 0xCA, 0x00, 0x00, 0x00 } +} ; + +static const EXT_SUBFORMAT MSGUID_SUBTYPE_AMBISONIC_B_FORMAT_IEEE_FLOAT = +{ 0x00000003, 0x0721, 0x11d3, { 0x86, 0x44, 0xC8, 0xC1, 0xCA, 0x00, 0x00, 0x00 } +} ; + + +#if 0 +/* maybe interesting one day to read the following through sf_read_raw */ +/* http://www.bath.ac.uk/~masrwd/pvocex/pvocex.html */ +static const EXT_SUBFORMAT MSGUID_SUBTYPE_PVOCEX = +{ 0x8312B9C2, 0x2E6E, 0x11d4, { 0xA8, 0x24, 0xDE, 0x5B, 0x96, 0xC3, 0xAB, 0x21 } +} ; +#endif + +/*------------------------------------------------------------------------------ +** Private static functions. +*/ + +static int wav_read_header (SF_PRIVATE *psf, int *blockalign, int *framesperblock) ; +static int wav_write_header (SF_PRIVATE *psf, int calc_length) ; + +static int wavex_write_header (SF_PRIVATE *psf, int calc_length) ; + +static int wav_write_tailer (SF_PRIVATE *psf) ; +static void wav_write_strings (SF_PRIVATE *psf, int location) ; +static int wav_command (SF_PRIVATE *psf, int command, void *data, int datasize) ; +static int wav_close (SF_PRIVATE *psf) ; + +static int wav_subchunk_parse (SF_PRIVATE *psf, int chunk) ; +static int wav_read_smpl_chunk (SF_PRIVATE *psf, unsigned int chunklen) ; +static int wav_read_acid_chunk (SF_PRIVATE *psf, unsigned int chunklen) ; +static int wav_read_bext_chunk (SF_PRIVATE *psf, unsigned int chunklen) ; +static int wav_write_bext_chunk (SF_PRIVATE *psf) ; + +/*------------------------------------------------------------------------------ +** Public function. +*/ + +int +wav_open (SF_PRIVATE *psf) +{ WAV_PRIVATE * wpriv ; + int format, subformat, error, blockalign = 0, framesperblock = 0 ; + + if ((wpriv = calloc (1, sizeof (WAV_PRIVATE))) == NULL) + return SFE_MALLOC_FAILED ; + psf->container_data = wpriv ; + + if (psf->mode == SFM_READ || (psf->mode == SFM_RDWR && psf->filelength > 0)) + { if ((error = wav_read_header (psf, &blockalign, &framesperblock))) + return error ; + } ; + + subformat = psf->sf.format & SF_FORMAT_SUBMASK ; + + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + { if (psf->is_pipe) + return SFE_NO_PIPE_WRITE ; + + psf->wavex_ambisonic = SF_AMBISONIC_NONE ; + + format = psf->sf.format & SF_FORMAT_TYPEMASK ; + if (format != SF_FORMAT_WAV && format != SF_FORMAT_WAVEX) + return SFE_BAD_OPEN_FORMAT ; + + psf->blockwidth = psf->bytewidth * psf->sf.channels ; + + /* RIFF WAVs are little-endian, RIFX WAVs are big-endian, default to little */ + psf->endian = psf->sf.format & SF_FORMAT_ENDMASK ; + if (CPU_IS_BIG_ENDIAN && psf->endian == SF_ENDIAN_CPU) + psf->endian = SF_ENDIAN_BIG ; + else if (psf->endian != SF_ENDIAN_BIG) + psf->endian = SF_ENDIAN_LITTLE ; + + if (psf->mode != SFM_RDWR || psf->filelength < 44) + { psf->filelength = 0 ; + psf->datalength = 0 ; + psf->dataoffset = 0 ; + psf->sf.frames = 0 ; + } ; + + if (subformat == SF_FORMAT_IMA_ADPCM || subformat == SF_FORMAT_MS_ADPCM) + { blockalign = wav_w64_srate2blocksize (psf->sf.samplerate * psf->sf.channels) ; + framesperblock = -1 ; /* Corrected later. */ + } ; + + psf->str_flags = SF_STR_ALLOW_START | SF_STR_ALLOW_END ; + + /* By default, add the peak chunk to floating point files. Default behaviour + ** can be switched off using sf_command (SFC_SET_PEAK_CHUNK, SF_FALSE). + */ + if (psf->mode == SFM_WRITE && (subformat == SF_FORMAT_FLOAT || subformat == SF_FORMAT_DOUBLE)) + { if ((psf->peak_info = peak_info_calloc (psf->sf.channels)) == NULL) + return SFE_MALLOC_FAILED ; + psf->peak_info->peak_loc = SF_PEAK_START ; + } ; + + psf->write_header = (format == SF_FORMAT_WAV) ? wav_write_header : wavex_write_header ; + } ; + + psf->container_close = wav_close ; + psf->command = wav_command ; + + switch (subformat) + { case SF_FORMAT_PCM_U8 : + case SF_FORMAT_PCM_16 : + case SF_FORMAT_PCM_24 : + case SF_FORMAT_PCM_32 : + error = pcm_init (psf) ; + break ; + + case SF_FORMAT_ULAW : + error = ulaw_init (psf) ; + break ; + + case SF_FORMAT_ALAW : + error = alaw_init (psf) ; + break ; + + /* Lite remove start */ + case SF_FORMAT_FLOAT : + error = float32_init (psf) ; + break ; + + case SF_FORMAT_DOUBLE : + error = double64_init (psf) ; + break ; + + case SF_FORMAT_IMA_ADPCM : + error = wav_w64_ima_init (psf, blockalign, framesperblock) ; + break ; + + case SF_FORMAT_MS_ADPCM : + error = wav_w64_msadpcm_init (psf, blockalign, framesperblock) ; + break ; + + case SF_FORMAT_G721_32 : + error = g72x_init (psf) ; + break ; + /* Lite remove end */ + + case SF_FORMAT_GSM610 : + error = gsm610_init (psf) ; + break ; + + default : return SFE_UNIMPLEMENTED ; + } ; + + if (psf->mode == SFM_WRITE || (psf->mode == SFM_RDWR && psf->filelength == 0)) + return psf->write_header (psf, SF_FALSE) ; + + return error ; +} /* wav_open */ + +/*========================================================================= +** Private functions. +*/ + +static int +wav_read_header (SF_PRIVATE *psf, int *blockalign, int *framesperblock) +{ WAV_PRIVATE *wpriv ; + WAV_FMT *wav_fmt ; + FACT_CHUNK fact_chunk ; + unsigned dword = 0, marker, RIFFsize, done = 0 ; + int parsestage = 0, error, format = 0 ; + char *cptr ; + + if (psf->filelength > SF_PLATFORM_S64 (0xffffffff)) + psf_log_printf (psf, "Warning : filelength > 0xffffffff. This is bad!!!!\n") ; + + if ((wpriv = psf->container_data) == NULL) + return SFE_INTERNAL ; + wav_fmt = &wpriv->wav_fmt ; + + /* Set position to start of file to begin reading header. */ + psf_binheader_readf (psf, "p", 0) ; + + while (! done) + { psf_binheader_readf (psf, "m", &marker) ; + + switch (marker) + { case RIFF_MARKER : + case RIFX_MARKER : + if (parsestage) + return SFE_WAV_NO_RIFF ; + + parsestage |= HAVE_RIFF ; + + /* RIFX signifies big-endian format for all header and data + ** to prevent lots of code copying here, we'll set the psf->rwf_endian + ** flag once here, and never specify endian-ness for all other header ops + */ + if (marker == RIFF_MARKER) + psf->rwf_endian = SF_ENDIAN_LITTLE ; + else + psf->rwf_endian = SF_ENDIAN_BIG ; + + psf_binheader_readf (psf, "4", &RIFFsize) ; + + if (psf->fileoffset > 0 && psf->filelength > RIFFsize + 8) + { /* Set file length. */ + psf->filelength = RIFFsize + 8 ; + if (marker == RIFF_MARKER) + psf_log_printf (psf, "RIFF : %u\n", RIFFsize) ; + else + psf_log_printf (psf, "RIFX : %u\n", RIFFsize) ; + } + else if (psf->filelength < RIFFsize + 2 * SIGNED_SIZEOF (dword)) + { if (marker == RIFF_MARKER) + psf_log_printf (psf, "RIFF : %u (should be %D)\n", RIFFsize, psf->filelength - 2 * SIGNED_SIZEOF (dword)) ; + else + psf_log_printf (psf, "RIFX : %u (should be %D)\n", RIFFsize, psf->filelength - 2 * SIGNED_SIZEOF (dword)) ; + + RIFFsize = dword ; + } + else + { if (marker == RIFF_MARKER) + psf_log_printf (psf, "RIFF : %u\n", RIFFsize) ; + else + psf_log_printf (psf, "RIFX : %u\n", RIFFsize) ; + } ; + break ; + + case WAVE_MARKER : + if ((parsestage & HAVE_RIFF) != HAVE_RIFF) + return SFE_WAV_NO_WAVE ; + parsestage |= HAVE_WAVE ; + + psf_log_printf (psf, "WAVE\n") ; + break ; + + case fmt_MARKER : + if ((parsestage & (HAVE_RIFF | HAVE_WAVE)) != (HAVE_RIFF | HAVE_WAVE)) + return SFE_WAV_NO_FMT ; + + /* If this file has a SECOND fmt chunk, I don't want to know about it. */ + if (parsestage & HAVE_fmt) + break ; + + parsestage |= HAVE_fmt ; + + psf_binheader_readf (psf, "4", &dword) ; + psf_log_printf (psf, "fmt : %d\n", dword) ; + + if ((error = wav_w64_read_fmt_chunk (psf, dword))) + return error ; + + format = wav_fmt->format ; + break ; + + case data_MARKER : + if ((parsestage & (HAVE_RIFF | HAVE_WAVE | HAVE_fmt)) != (HAVE_RIFF | HAVE_WAVE | HAVE_fmt)) + return SFE_WAV_NO_DATA ; + + if (psf->mode == SFM_RDWR && (parsestage & HAVE_other) != 0) + return SFE_RDWR_BAD_HEADER ; + + parsestage |= HAVE_data ; + + psf_binheader_readf (psf, "4", &dword) ; + + psf->datalength = dword ; + psf->dataoffset = psf_ftell (psf) ; + + if (dword == 0 && RIFFsize == 8 && psf->filelength > 44) + { psf_log_printf (psf, "*** Looks like a WAV file which wasn't closed properly. Fixing it.\n") ; + psf->datalength = psf->filelength - psf->dataoffset ; + } ; + + if (psf->datalength > psf->filelength - psf->dataoffset) + { psf_log_printf (psf, "data : %D (should be %D)\n", psf->datalength, psf->filelength - psf->dataoffset) ; + psf->datalength = psf->filelength - psf->dataoffset ; + } + else + psf_log_printf (psf, "data : %D\n", psf->datalength) ; + + /* Only set dataend if there really is data at the end. */ + if (psf->datalength + psf->dataoffset < psf->filelength) + psf->dataend = psf->datalength + psf->dataoffset ; + + if (format == WAVE_FORMAT_MS_ADPCM && psf->datalength % 2) + { psf->datalength ++ ; + psf_log_printf (psf, "*** Data length odd. Increasing it by 1.\n") ; + } ; + + if (! psf->sf.seekable) + break ; + + /* Seek past data and continue reading header. */ + psf_fseek (psf, psf->datalength, SEEK_CUR) ; + + if (psf_ftell (psf) != psf->datalength + psf->dataoffset) + psf_log_printf (psf, "*** psf_fseek past end error ***\n", dword, psf->dataoffset + psf->datalength) ; + break ; + + case fact_MARKER : + if ((parsestage & (HAVE_RIFF | HAVE_WAVE)) != (HAVE_RIFF | HAVE_WAVE)) + return SFE_WAV_BAD_FACT ; + + parsestage |= HAVE_fact ; + + if ((parsestage & HAVE_fmt) != HAVE_fmt) + psf_log_printf (psf, "*** Should have 'fmt ' chunk before 'fact'\n") ; + + psf_binheader_readf (psf, "44", &dword, & (fact_chunk.frames)) ; + + if (dword > SIGNED_SIZEOF (fact_chunk)) + psf_binheader_readf (psf, "j", (int) (dword - SIGNED_SIZEOF (fact_chunk))) ; + + if (dword) + psf_log_printf (psf, "%M : %d\n", marker, dword) ; + else + psf_log_printf (psf, "%M : %d (should not be zero)\n", marker, dword) ; + + psf_log_printf (psf, " frames : %d\n", fact_chunk.frames) ; + break ; + + case PEAK_MARKER : + if ((parsestage & (HAVE_RIFF | HAVE_WAVE | HAVE_fmt)) != (HAVE_RIFF | HAVE_WAVE | HAVE_fmt)) + return SFE_WAV_PEAK_B4_FMT ; + + parsestage |= HAVE_PEAK ; + + psf_binheader_readf (psf, "4", &dword) ; + + psf_log_printf (psf, "%M : %d\n", marker, dword) ; + if (dword != WAV_PEAK_CHUNK_SIZE (psf->sf.channels)) + { psf_binheader_readf (psf, "j", dword) ; + psf_log_printf (psf, "*** File PEAK chunk size doesn't fit with number of channels (%d).\n", psf->sf.channels) ; + return SFE_WAV_BAD_PEAK ; + } ; + + if ((psf->peak_info = peak_info_calloc (psf->sf.channels)) == NULL) + return SFE_MALLOC_FAILED ; + + /* read in rest of PEAK chunk. */ + psf_binheader_readf (psf, "44", & (psf->peak_info->version), & (psf->peak_info->timestamp)) ; + + if (psf->peak_info->version != 1) + psf_log_printf (psf, " version : %d *** (should be version 1)\n", psf->peak_info->version) ; + else + psf_log_printf (psf, " version : %d\n", psf->peak_info->version) ; + + psf_log_printf (psf, " time stamp : %d\n", psf->peak_info->timestamp) ; + psf_log_printf (psf, " Ch Position Value\n") ; + + cptr = psf->u.cbuf ; + for (dword = 0 ; dword < (unsigned) psf->sf.channels ; dword++) + { float value ; + unsigned int position ; + psf_binheader_readf (psf, "f4", &value, &position) ; + psf->peak_info->peaks [dword].value = value ; + psf->peak_info->peaks [dword].position = position ; + + LSF_SNPRINTF (cptr, sizeof (psf->u.cbuf), " %2d %-12ld %g\n", + dword, (long) psf->peak_info->peaks [dword].position, psf->peak_info->peaks [dword].value) ; + cptr [sizeof (psf->u.cbuf) - 1] = 0 ; + psf_log_printf (psf, cptr) ; + } ; + + psf->peak_info->peak_loc = ((parsestage & HAVE_data) == 0) ? SF_PEAK_START : SF_PEAK_END ; + break ; + + case cue_MARKER : + parsestage |= HAVE_other ; + + { unsigned bytesread, cue_count ; + int id, position, chunk_id, chunk_start, block_start, offset ; + + bytesread = psf_binheader_readf (psf, "44", &dword, &cue_count) ; + bytesread -= 4 ; /* Remove bytes for first dword. */ + psf_log_printf (psf, "%M : %u\n", marker, dword) ; + + if (cue_count > 10) + { psf_log_printf (psf, " Count : %d (skipping)\n", cue_count) ; + psf_binheader_readf (psf, "j", cue_count * 24) ; + break ; + } ; + + psf_log_printf (psf, " Count : %d\n", cue_count) ; + + while (cue_count) + { bytesread += psf_binheader_readf (psf, "444444", &id, &position, + &chunk_id, &chunk_start, &block_start, &offset) ; + psf_log_printf (psf, " Cue ID : %2d" + " Pos : %5u Chunk : %M" + " Chk Start : %d Blk Start : %d" + " Offset : %5d\n", + id, position, chunk_id, chunk_start, block_start, offset) ; + cue_count -- ; + } ; + + if (bytesread != dword) + { psf_log_printf (psf, "**** Chunk size weirdness (%d != %d)\n", dword, bytesread) ; + psf_binheader_readf (psf, "j", dword - bytesread) ; + } ; + } ; + break ; + + case smpl_MARKER : + parsestage |= HAVE_other ; + + psf_binheader_readf (psf, "4", &dword) ; + psf_log_printf (psf, "smpl : %u\n", dword) ; + + if ((error = wav_read_smpl_chunk (psf, dword))) + return error ; + break ; + + case acid_MARKER : + parsestage |= HAVE_other ; + + psf_binheader_readf (psf, "4", &dword) ; + psf_log_printf (psf, "acid : %u\n", dword) ; + + if ((error = wav_read_acid_chunk (psf, dword))) + return error ; + break ; + + case INFO_MARKER : + case LIST_MARKER : + parsestage |= HAVE_other ; + + if ((error = wav_subchunk_parse (psf, marker)) != 0) + return error ; + break ; + + case bext_MARKER : + parsestage |= HAVE_other ; + + psf_binheader_readf (psf, "4", &dword) ; + if (dword < WAV_BEXT_CHUNK_SIZE) + psf_log_printf (psf, "bext : %u (should be >= %d)\n", dword, WAV_BEXT_CHUNK_SIZE) ; + else + psf_log_printf (psf, "bext : %u\n", dword) ; + + if ((error = wav_read_bext_chunk (psf, dword))) + return error ; + break ; + + case strc_MARKER : /* Multiple of 32 bytes. */ + + case afsp_MARKER : + case clm_MARKER : + case elmo_MARKER : + case cart_MARKER : + case levl_MARKER : + case plst_MARKER : + case DISP_MARKER : + case MEXT_MARKER : + case PAD_MARKER : + parsestage |= HAVE_other ; + + psf_binheader_readf (psf, "4", &dword) ; + psf_log_printf (psf, "%M : %u\n", marker, dword) ; + dword += (dword & 1) ; + psf_binheader_readf (psf, "j", dword) ; + break ; + + default : + parsestage |= HAVE_other ; + if (isprint ((marker >> 24) & 0xFF) && isprint ((marker >> 16) & 0xFF) + && isprint ((marker >> 8) & 0xFF) && isprint (marker & 0xFF)) + { psf_binheader_readf (psf, "4", &dword) ; + psf_log_printf (psf, "*** %M : %d (unknown marker)\n", marker, dword) ; + psf_binheader_readf (psf, "j", dword) ; + break ; + } ; + if (psf_ftell (psf) & 0x03) + { psf_log_printf (psf, " Unknown chunk marker at position %d. Resynching.\n", dword - 4) ; + psf_binheader_readf (psf, "j", -3) ; + break ; + } ; + psf_log_printf (psf, "*** Unknown chunk marker (%X) at position %D. Exiting parser.\n", marker, psf_ftell (psf) - 4) ; + done = SF_TRUE ; + break ; + } ; /* switch (dword) */ + + if (! psf->sf.seekable && (parsestage & HAVE_data)) + break ; + + if (psf_ftell (psf) >= psf->filelength - SIGNED_SIZEOF (dword)) + { psf_log_printf (psf, "End\n") ; + break ; + } ; + } ; /* while (1) */ + + if (! psf->dataoffset) + return SFE_WAV_NO_DATA ; + + /* WAVs can be little or big endian */ + psf->endian = psf->rwf_endian ; + + psf_fseek (psf, psf->dataoffset, SEEK_SET) ; + + if (psf->is_pipe == 0) + { /* + ** Check for 'wvpk' at the start of the DATA section. Not able to + ** handle this. + */ + psf_binheader_readf (psf, "4", &marker) ; + if (marker == wvpk_MARKER || marker == OggS_MARKER) + return SFE_WAV_WVPK_DATA ; + } ; + + /* Seek to start of DATA section. */ + psf_fseek (psf, psf->dataoffset, SEEK_SET) ; + + if (psf->blockwidth) + { if (psf->filelength - psf->dataoffset < psf->datalength) + psf->sf.frames = (psf->filelength - psf->dataoffset) / psf->blockwidth ; + else + psf->sf.frames = psf->datalength / psf->blockwidth ; + } ; + + switch (format) + { case WAVE_FORMAT_EXTENSIBLE : + if (psf->sf.format == (SF_FORMAT_WAVEX | SF_FORMAT_MS_ADPCM)) + { *blockalign = wav_fmt->msadpcm.blockalign ; + *framesperblock = wav_fmt->msadpcm.samplesperblock ; + } ; + break ; + + case WAVE_FORMAT_PCM : + psf->sf.format = SF_FORMAT_WAV | u_bitwidth_to_subformat (psf->bytewidth * 8) ; + break ; + + case WAVE_FORMAT_MULAW : + case IBM_FORMAT_MULAW : + psf->sf.format = (SF_FORMAT_WAV | SF_FORMAT_ULAW) ; + break ; + + case WAVE_FORMAT_ALAW : + case IBM_FORMAT_ALAW : + psf->sf.format = (SF_FORMAT_WAV | SF_FORMAT_ALAW) ; + break ; + + case WAVE_FORMAT_MS_ADPCM : + psf->sf.format = (SF_FORMAT_WAV | SF_FORMAT_MS_ADPCM) ; + *blockalign = wav_fmt->msadpcm.blockalign ; + *framesperblock = wav_fmt->msadpcm.samplesperblock ; + break ; + + case WAVE_FORMAT_IMA_ADPCM : + psf->sf.format = (SF_FORMAT_WAV | SF_FORMAT_IMA_ADPCM) ; + *blockalign = wav_fmt->ima.blockalign ; + *framesperblock = wav_fmt->ima.samplesperblock ; + break ; + + case WAVE_FORMAT_GSM610 : + psf->sf.format = (SF_FORMAT_WAV | SF_FORMAT_GSM610) ; + break ; + + case WAVE_FORMAT_IEEE_FLOAT : + psf->sf.format = SF_FORMAT_WAV ; + psf->sf.format |= (psf->bytewidth == 8) ? SF_FORMAT_DOUBLE : SF_FORMAT_FLOAT ; + break ; + + case WAVE_FORMAT_G721_ADPCM : + psf->sf.format = SF_FORMAT_WAV | SF_FORMAT_G721_32 ; + break ; + + default : return SFE_UNIMPLEMENTED ; + } ; + + if (wpriv->fmt_is_broken) + wav_w64_analyze (psf) ; + + /* Only set the format endian-ness if its non-standard big-endian. */ + if (psf->endian == SF_ENDIAN_BIG) + psf->sf.format |= SF_ENDIAN_BIG ; + + return 0 ; +} /* wav_read_header */ + +static int +wav_write_header (SF_PRIVATE *psf, int calc_length) +{ sf_count_t current ; + int fmt_size, k, subformat, add_fact_chunk = SF_FALSE ; + + current = psf_ftell (psf) ; + + if (calc_length) + { psf->filelength = psf_get_filelen (psf) ; + + psf->datalength = psf->filelength - psf->dataoffset ; + + if (psf->dataend) + psf->datalength -= psf->filelength - psf->dataend ; + + if (psf->bytewidth > 0) + psf->sf.frames = psf->datalength / (psf->bytewidth * psf->sf.channels) ; + } ; + + /* Reset the current header length to zero. */ + psf->header [0] = 0 ; + psf->headindex = 0 ; + psf_fseek (psf, 0, SEEK_SET) ; + + /* + ** RIFX signifies big-endian format for all header and data. + ** To prevent lots of code copying here, we'll set the psf->rwf_endian flag + ** once here, and never specify endian-ness for all other header operations. + */ + + /* RIFF/RIFX marker, length, WAVE and 'fmt ' markers. */ + + if (psf->endian == SF_ENDIAN_LITTLE) + psf_binheader_writef (psf, "etm8", RIFF_MARKER, (psf->filelength < 8) ? 8 : psf->filelength - 8) ; + else + psf_binheader_writef (psf, "Etm8", RIFX_MARKER, (psf->filelength < 8) ? 8 : psf->filelength - 8) ; + + /* WAVE and 'fmt ' markers. */ + psf_binheader_writef (psf, "mm", WAVE_MARKER, fmt_MARKER) ; + + subformat = psf->sf.format & SF_FORMAT_SUBMASK ; + + switch (subformat) + { case SF_FORMAT_PCM_U8 : + case SF_FORMAT_PCM_16 : + case SF_FORMAT_PCM_24 : + case SF_FORMAT_PCM_32 : + fmt_size = 2 + 2 + 4 + 4 + 2 + 2 ; + + /* fmt : format, channels, samplerate */ + psf_binheader_writef (psf, "4224", fmt_size, WAVE_FORMAT_PCM, psf->sf.channels, psf->sf.samplerate) ; + /* fmt : bytespersec */ + psf_binheader_writef (psf, "4", psf->sf.samplerate * psf->bytewidth * psf->sf.channels) ; + /* fmt : blockalign, bitwidth */ + psf_binheader_writef (psf, "22", psf->bytewidth * psf->sf.channels, psf->bytewidth * 8) ; + break ; + + case SF_FORMAT_FLOAT : + case SF_FORMAT_DOUBLE : + fmt_size = 2 + 2 + 4 + 4 + 2 + 2 ; + + /* fmt : format, channels, samplerate */ + psf_binheader_writef (psf, "4224", fmt_size, WAVE_FORMAT_IEEE_FLOAT, psf->sf.channels, psf->sf.samplerate) ; + /* fmt : bytespersec */ + psf_binheader_writef (psf, "4", psf->sf.samplerate * psf->bytewidth * psf->sf.channels) ; + /* fmt : blockalign, bitwidth */ + psf_binheader_writef (psf, "22", psf->bytewidth * psf->sf.channels, psf->bytewidth * 8) ; + + add_fact_chunk = SF_TRUE ; + break ; + + case SF_FORMAT_ULAW : + fmt_size = 2 + 2 + 4 + 4 + 2 + 2 ; + + /* fmt : format, channels, samplerate */ + psf_binheader_writef (psf, "4224", fmt_size, WAVE_FORMAT_MULAW, psf->sf.channels, psf->sf.samplerate) ; + /* fmt : bytespersec */ + psf_binheader_writef (psf, "4", psf->sf.samplerate * psf->bytewidth * psf->sf.channels) ; + /* fmt : blockalign, bitwidth */ + psf_binheader_writef (psf, "22", psf->bytewidth * psf->sf.channels, 8) ; + + add_fact_chunk = SF_TRUE ; + break ; + + case SF_FORMAT_ALAW : + fmt_size = 2 + 2 + 4 + 4 + 2 + 2 ; + + /* fmt : format, channels, samplerate */ + psf_binheader_writef (psf, "4224", fmt_size, WAVE_FORMAT_ALAW, psf->sf.channels, psf->sf.samplerate) ; + /* fmt : bytespersec */ + psf_binheader_writef (psf, "4", psf->sf.samplerate * psf->bytewidth * psf->sf.channels) ; + /* fmt : blockalign, bitwidth */ + psf_binheader_writef (psf, "22", psf->bytewidth * psf->sf.channels, 8) ; + + add_fact_chunk = SF_TRUE ; + break ; + + /* Lite remove start */ + case SF_FORMAT_IMA_ADPCM : + { int blockalign, framesperblock, bytespersec ; + + blockalign = wav_w64_srate2blocksize (psf->sf.samplerate * psf->sf.channels) ; + framesperblock = 2 * (blockalign - 4 * psf->sf.channels) / psf->sf.channels + 1 ; + bytespersec = (psf->sf.samplerate * blockalign) / framesperblock ; + + /* fmt chunk. */ + fmt_size = 2 + 2 + 4 + 4 + 2 + 2 + 2 + 2 ; + + /* fmt : size, WAV format type, channels, samplerate, bytespersec */ + psf_binheader_writef (psf, "42244", fmt_size, WAVE_FORMAT_IMA_ADPCM, + psf->sf.channels, psf->sf.samplerate, bytespersec) ; + + /* fmt : blockalign, bitwidth, extrabytes, framesperblock. */ + psf_binheader_writef (psf, "2222", blockalign, 4, 2, framesperblock) ; + } ; + + add_fact_chunk = SF_TRUE ; + break ; + + case SF_FORMAT_MS_ADPCM : + { int blockalign, framesperblock, bytespersec, extrabytes ; + + blockalign = wav_w64_srate2blocksize (psf->sf.samplerate * psf->sf.channels) ; + framesperblock = 2 + 2 * (blockalign - 7 * psf->sf.channels) / psf->sf.channels ; + bytespersec = (psf->sf.samplerate * blockalign) / framesperblock ; + + /* fmt chunk. */ + extrabytes = 2 + 2 + MSADPCM_ADAPT_COEFF_COUNT * (2 + 2) ; + fmt_size = 2 + 2 + 4 + 4 + 2 + 2 + 2 + extrabytes ; + + /* fmt : size, WAV format type, channels. */ + psf_binheader_writef (psf, "422", fmt_size, WAVE_FORMAT_MS_ADPCM, psf->sf.channels) ; + + /* fmt : samplerate, bytespersec. */ + psf_binheader_writef (psf, "44", psf->sf.samplerate, bytespersec) ; + + /* fmt : blockalign, bitwidth, extrabytes, framesperblock. */ + psf_binheader_writef (psf, "22222", blockalign, 4, extrabytes, framesperblock, 7) ; + + msadpcm_write_adapt_coeffs (psf) ; + } ; + + add_fact_chunk = SF_TRUE ; + break ; + + + case SF_FORMAT_G721_32 : + /* fmt chunk. */ + fmt_size = 2 + 2 + 4 + 4 + 2 + 2 + 2 + 2 ; + + /* fmt : size, WAV format type, channels, samplerate, bytespersec */ + psf_binheader_writef (psf, "42244", fmt_size, WAVE_FORMAT_G721_ADPCM, + psf->sf.channels, psf->sf.samplerate, psf->sf.samplerate * psf->sf.channels / 2) ; + + /* fmt : blockalign, bitwidth, extrabytes, auxblocksize. */ + psf_binheader_writef (psf, "2222", 64, 4, 2, 0) ; + + add_fact_chunk = SF_TRUE ; + break ; + + /* Lite remove end */ + + case SF_FORMAT_GSM610 : + { int blockalign, framesperblock, bytespersec ; + + blockalign = WAV_W64_GSM610_BLOCKSIZE ; + framesperblock = WAV_W64_GSM610_SAMPLES ; + bytespersec = (psf->sf.samplerate * blockalign) / framesperblock ; + + /* fmt chunk. */ + fmt_size = 2 + 2 + 4 + 4 + 2 + 2 + 2 + 2 ; + + /* fmt : size, WAV format type, channels. */ + psf_binheader_writef (psf, "422", fmt_size, WAVE_FORMAT_GSM610, psf->sf.channels) ; + + /* fmt : samplerate, bytespersec. */ + psf_binheader_writef (psf, "44", psf->sf.samplerate, bytespersec) ; + + /* fmt : blockalign, bitwidth, extrabytes, framesperblock. */ + psf_binheader_writef (psf, "2222", blockalign, 0, 2, framesperblock) ; + } ; + + add_fact_chunk = SF_TRUE ; + break ; + + default : return SFE_UNIMPLEMENTED ; + } ; + + if (add_fact_chunk) + psf_binheader_writef (psf, "tm48", fact_MARKER, 4, psf->sf.frames) ; + + if (psf->str_flags & SF_STR_LOCATE_START) + wav_write_strings (psf, SF_STR_LOCATE_START) ; + + if (psf->peak_info != NULL && psf->peak_info->peak_loc == SF_PEAK_START) + { psf_binheader_writef (psf, "m4", PEAK_MARKER, WAV_PEAK_CHUNK_SIZE (psf->sf.channels)) ; + psf_binheader_writef (psf, "44", 1, time (NULL)) ; + for (k = 0 ; k < psf->sf.channels ; k++) + psf_binheader_writef (psf, "ft8", (float) psf->peak_info->peaks [k].value, psf->peak_info->peaks [k].position) ; + } ; + + if (psf->broadcast_info != NULL) + wav_write_bext_chunk (psf) ; + + if (psf->instrument != NULL) + { int tmp ; + double dtune = (double) (0x40000000) / 25.0 ; + + psf_binheader_writef (psf, "m4", smpl_MARKER, 9 * 4 + psf->instrument->loop_count * 6 * 4) ; + psf_binheader_writef (psf, "44", 0, 0) ; /* Manufacturer zero is everyone */ + tmp = (int) (1.0e9 / psf->sf.samplerate) ; /* Sample period in nano seconds */ + psf_binheader_writef (psf, "44", tmp, psf->instrument->basenote) ; + tmp = (unsigned int) (psf->instrument->detune * dtune + 0.5) ; + psf_binheader_writef (psf, "4", tmp) ; + psf_binheader_writef (psf, "44", 0, 0) ; /* SMTPE format */ + psf_binheader_writef (psf, "44", psf->instrument->loop_count, 0) ; + + for (tmp = 0 ; tmp < psf->instrument->loop_count ; tmp++) + { int type ; + + type = psf->instrument->loops [tmp].mode ; + type = (type == SF_LOOP_FORWARD ? 0 : type==SF_LOOP_BACKWARD ? 2 : type == SF_LOOP_ALTERNATING ? 1 : 32) ; + + psf_binheader_writef (psf, "44", tmp, type) ; + psf_binheader_writef (psf, "44", psf->instrument->loops [tmp].start, psf->instrument->loops [tmp].end) ; + psf_binheader_writef (psf, "44", 0, psf->instrument->loops [tmp].count) ; + } ; + } ; + + psf_binheader_writef (psf, "tm8", data_MARKER, psf->datalength) ; + psf_fwrite (psf->header, psf->headindex, 1, psf) ; + if (psf->error) + return psf->error ; + + psf->dataoffset = psf->headindex ; + + if (current < psf->dataoffset) + psf_fseek (psf, psf->dataoffset, SEEK_SET) ; + else if (current > 0) + psf_fseek (psf, current, SEEK_SET) ; + + return psf->error ; +} /* wav_write_header */ + + + +static int +wavex_write_header (SF_PRIVATE *psf, int calc_length) +{ sf_count_t current ; + int fmt_size, k, subformat, add_fact_chunk = SF_FALSE ; + + current = psf_ftell (psf) ; + + if (calc_length) + { psf->filelength = psf_get_filelen (psf) ; + + psf->datalength = psf->filelength - psf->dataoffset ; + + if (psf->dataend) + psf->datalength -= psf->filelength - psf->dataend ; + + if (psf->bytewidth > 0) + psf->sf.frames = psf->datalength / (psf->bytewidth * psf->sf.channels) ; + } ; + + + /* Reset the current header length to zero. */ + psf->header [0] = 0 ; + psf->headindex = 0 ; + psf_fseek (psf, 0, SEEK_SET) ; + + /* RIFX signifies big-endian format for all header and data + ** to prevent lots of code copying here, we'll set the psf->rwf_endian + ** flag once here, and never specify endian-ness for all other header ops + */ + + /* RIFF marker, length, WAVE and 'fmt ' markers. */ + + if (psf->endian == SF_ENDIAN_LITTLE) + { if (psf->filelength < 8) + psf_binheader_writef (psf, "tm8", RIFF_MARKER, 8) ; + else + psf_binheader_writef (psf, "tm8", RIFF_MARKER, psf->filelength - 8) ; + } + else + { if (psf->filelength < 8) + psf_binheader_writef (psf, "Etm8", RIFX_MARKER, 8) ; + else + psf_binheader_writef (psf, "Etm8", RIFX_MARKER, psf->filelength - 8) ; + } ; + + /* WAVE and 'fmt ' markers. */ + psf_binheader_writef (psf, "mm", WAVE_MARKER, fmt_MARKER) ; + + subformat = psf->sf.format & SF_FORMAT_SUBMASK ; + + /* initial section (same for all, it appears) */ + switch (subformat) + { case SF_FORMAT_PCM_U8 : + case SF_FORMAT_PCM_16 : + case SF_FORMAT_PCM_24 : + case SF_FORMAT_PCM_32 : + case SF_FORMAT_FLOAT : + case SF_FORMAT_DOUBLE : + case SF_FORMAT_ULAW : + case SF_FORMAT_ALAW : + fmt_size = 2 + 2 + 4 + 4 + 2 + 2 + 2 + 2 + 4 + 4 + 2 + 2 + 8 ; + + /* fmt : format, channels, samplerate */ + psf_binheader_writef (psf, "4224", fmt_size, WAVE_FORMAT_EXTENSIBLE, psf->sf.channels, psf->sf.samplerate) ; + /* fmt : bytespersec */ + psf_binheader_writef (psf, "4", psf->sf.samplerate * psf->bytewidth * psf->sf.channels) ; + /* fmt : blockalign, bitwidth */ + psf_binheader_writef (psf, "22", psf->bytewidth * psf->sf.channels, psf->bytewidth * 8) ; + + /* cbSize 22 is sizeof (WAVEFORMATEXTENSIBLE) - sizeof (WAVEFORMATEX) */ + psf_binheader_writef (psf, "2", 22) ; + + /* wValidBitsPerSample, for our use same as bitwidth as we use it fully */ + psf_binheader_writef (psf, "2", psf->bytewidth * 8) ; + + /* For an Ambisonic file set the channel mask to zero. + ** Otherwise use a default based on the channel count. + */ + if (psf->wavex_ambisonic) + psf_binheader_writef (psf, "4", 0) ; + else + { /* + ** Ok some liberty is taken here to use the most commonly used channel masks + ** instead of "no mapping". If you really want to use "no mapping" for 8 channels and less + ** please don't use wavex. (otherwise we'll have to create a new SF_COMMAND) + */ + switch (psf->sf.channels) + { case 1 : /* center channel mono */ + psf_binheader_writef (psf, "4", 0x4) ; + break ; + + case 2 : /* front left and right */ + psf_binheader_writef (psf, "4", 0x1 | 0x2) ; + break ; + + case 4 : /* Quad */ + psf_binheader_writef (psf, "4", 0x1 | 0x2 | 0x10 | 0x20) ; + break ; + + case 6 : /* 5.1 */ + psf_binheader_writef (psf, "4", 0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20) ; + break ; + + case 8 : /* 7.1 */ + psf_binheader_writef (psf, "4", 0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20 | 0x40 | 0x80) ; + break ; + + default : /* 0 when in doubt , use direct out, ie NO mapping*/ + psf_binheader_writef (psf, "4", 0x0) ; + break ; + } ; + } ; + break ; + + case SF_FORMAT_MS_ADPCM : /* Todo, GUID exists might have different header as per wav_write_header */ + default : + return SFE_UNIMPLEMENTED ; + } ; + + /* GUID section, different for each */ + + switch (subformat) + { case SF_FORMAT_PCM_U8 : + case SF_FORMAT_PCM_16 : + case SF_FORMAT_PCM_24 : + case SF_FORMAT_PCM_32 : + wavex_write_guid (psf, psf->wavex_ambisonic ? &MSGUID_SUBTYPE_AMBISONIC_B_FORMAT_PCM + : &MSGUID_SUBTYPE_PCM) ; + break ; + + case SF_FORMAT_FLOAT : + case SF_FORMAT_DOUBLE : + wavex_write_guid (psf, psf->wavex_ambisonic ? &MSGUID_SUBTYPE_AMBISONIC_B_FORMAT_IEEE_FLOAT + : &MSGUID_SUBTYPE_IEEE_FLOAT) ; + add_fact_chunk = SF_TRUE ; + break ; + + case SF_FORMAT_ULAW : + wavex_write_guid (psf, &MSGUID_SUBTYPE_MULAW) ; + add_fact_chunk = SF_TRUE ; + break ; + + case SF_FORMAT_ALAW : + wavex_write_guid (psf, &MSGUID_SUBTYPE_ALAW) ; + add_fact_chunk = SF_TRUE ; + break ; + + case SF_FORMAT_MS_ADPCM : /* todo, GUID exists */ + return SFE_UNIMPLEMENTED ; + wavex_write_guid (psf, &MSGUID_SUBTYPE_MS_ADPCM) ; + add_fact_chunk = SF_TRUE ; + break ; + + default : return SFE_UNIMPLEMENTED ; + } ; + + if (add_fact_chunk) + psf_binheader_writef (psf, "tm48", fact_MARKER, 4, psf->sf.frames) ; + + if (psf->str_flags & SF_STR_LOCATE_START) + wav_write_strings (psf, SF_STR_LOCATE_START) ; + + if (psf->peak_info != NULL && psf->peak_info->peak_loc == SF_PEAK_START) + { psf_binheader_writef (psf, "m4", PEAK_MARKER, WAV_PEAK_CHUNK_SIZE (psf->sf.channels)) ; + psf_binheader_writef (psf, "44", 1, time (NULL)) ; + for (k = 0 ; k < psf->sf.channels ; k++) + psf_binheader_writef (psf, "ft8", (float) psf->peak_info->peaks [k].value, psf->peak_info->peaks [k].position) ; + } ; + + psf_binheader_writef (psf, "tm8", data_MARKER, psf->datalength) ; + psf_fwrite (psf->header, psf->headindex, 1, psf) ; + if (psf->error) + return psf->error ; + + psf->dataoffset = psf->headindex ; + + if (current < psf->dataoffset) + psf_fseek (psf, psf->dataoffset, SEEK_SET) ; + else if (current > 0) + psf_fseek (psf, current, SEEK_SET) ; + + return psf->error ; +} /* wavex_write_header */ + + + +static int +wav_write_tailer (SF_PRIVATE *psf) +{ int k ; + + /* Reset the current header buffer length to zero. */ + psf->header [0] = 0 ; + psf->headindex = 0 ; + + psf->dataend = psf_fseek (psf, 0, SEEK_END) ; + + /* Add a PEAK chunk if requested. */ + if (psf->peak_info != NULL && psf->peak_info->peak_loc == SF_PEAK_END) + { psf_binheader_writef (psf, "m4", PEAK_MARKER, WAV_PEAK_CHUNK_SIZE (psf->sf.channels)) ; + psf_binheader_writef (psf, "44", 1, time (NULL)) ; + for (k = 0 ; k < psf->sf.channels ; k++) + psf_binheader_writef (psf, "f4", psf->peak_info->peaks [k].value, psf->peak_info->peaks [k].position) ; + } ; + + if (psf->str_flags & SF_STR_LOCATE_END) + wav_write_strings (psf, SF_STR_LOCATE_END) ; + + /* Write the tailer. */ + if (psf->headindex > 0) + psf_fwrite (psf->header, psf->headindex, 1, psf) ; + + return 0 ; +} /* wav_write_tailer */ + +static void +wav_write_strings (SF_PRIVATE *psf, int location) +{ int k, prev_head_index, saved_head_index ; + + prev_head_index = psf->headindex + 4 ; + + psf_binheader_writef (psf, "m4m", LIST_MARKER, 0xBADBAD, INFO_MARKER) ; + + for (k = 0 ; k < SF_MAX_STRINGS ; k++) + { if (psf->strings [k].type == 0) + break ; + if (psf->strings [k].flags != location) + continue ; + + switch (psf->strings [k].type) + { case SF_STR_SOFTWARE : + psf_binheader_writef (psf, "ms", ISFT_MARKER, psf->strings [k].str) ; + break ; + + case SF_STR_TITLE : + psf_binheader_writef (psf, "ms", INAM_MARKER, psf->strings [k].str) ; + break ; + + case SF_STR_COPYRIGHT : + psf_binheader_writef (psf, "ms", ICOP_MARKER, psf->strings [k].str) ; + break ; + + case SF_STR_ARTIST : + psf_binheader_writef (psf, "ms", IART_MARKER, psf->strings [k].str) ; + break ; + + case SF_STR_COMMENT : + psf_binheader_writef (psf, "ms", ICMT_MARKER, psf->strings [k].str) ; + break ; + + case SF_STR_DATE : + psf_binheader_writef (psf, "ms", ICRD_MARKER, psf->strings [k].str) ; + break ; + } ; + } ; + + saved_head_index = psf->headindex ; + psf->headindex = prev_head_index ; + psf_binheader_writef (psf, "4", saved_head_index - prev_head_index - 4) ; + psf->headindex = saved_head_index ; + +} /* wav_write_strings */ + +static int +wav_close (SF_PRIVATE *psf) +{ + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + { wav_write_tailer (psf) ; + + psf->write_header (psf, SF_TRUE) ; + } ; + + return 0 ; +} /* wav_close */ + +static int +wav_command (SF_PRIVATE *psf, int command, void * UNUSED (data), int datasize) +{ + switch (command) + { case SFC_WAVEX_SET_AMBISONIC : + if ((psf->sf.format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAVEX) + { if (datasize == SF_AMBISONIC_NONE) + psf->wavex_ambisonic = SF_AMBISONIC_NONE ; + else if (datasize == SF_AMBISONIC_B_FORMAT) + psf->wavex_ambisonic = SF_AMBISONIC_B_FORMAT ; + else + return 0 ; + } ; + return psf->wavex_ambisonic ; + + case SFC_WAVEX_GET_AMBISONIC : + return psf->wavex_ambisonic ; + + default : + break ; + } ; + + return 0 ; +} /* wav_command */ + +static int +wav_subchunk_parse (SF_PRIVATE *psf, int chunk) +{ sf_count_t current_pos ; + char *cptr ; + int dword, bytesread, length ; + + current_pos = psf_fseek (psf, 0, SEEK_CUR) ; + + bytesread = psf_binheader_readf (psf, "4", &length) ; + + if (length <= 8) + { /* This case is for broken files generated by PEAK. */ + psf_log_printf (psf, "%M : %d (weird length)\n", chunk, length) ; + psf_binheader_readf (psf, "mj", &chunk, length - 4) ; + psf_log_printf (psf, " %M\n", chunk) ; + return 0 ; + } ; + + if (psf->headindex + length > SIGNED_SIZEOF (psf->header)) + { psf_log_printf (psf, "%M : %d (too long)\n", chunk, length) ; + psf_binheader_readf (psf, "j", length) ; + return 0 ; + } ; + + if (current_pos + length > psf->filelength) + { psf_log_printf (psf, "%M : %d (should be %d)\n", chunk, length, (int) (psf->filelength - current_pos)) ; + length = psf->filelength - current_pos ; + } + else + psf_log_printf (psf, "%M : %d\n", chunk, length) ; + + while (bytesread < length) + { bytesread += psf_binheader_readf (psf, "m", &chunk) ; + + switch (chunk) + { case adtl_MARKER : + case INFO_MARKER : + /* These markers don't contain anything. */ + psf_log_printf (psf, " %M\n", chunk) ; + break ; + + case data_MARKER: + psf_log_printf (psf, " %M inside a LIST block??? Backing out.\n", chunk) ; + /* Jump back four bytes and return to caller. */ + psf_binheader_readf (psf, "j", -4) ; + return 0 ; + + case ISFT_MARKER : + case ICOP_MARKER : + case IARL_MARKER : + case IART_MARKER : + case ICMT_MARKER : + case ICRD_MARKER : + case IENG_MARKER : + + case INAM_MARKER : + case IPRD_MARKER : + case ISBJ_MARKER : + case ISRC_MARKER : + bytesread += psf_binheader_readf (psf, "4", &dword) ; + dword += (dword & 1) ; + if (dword < 0 || dword > SIGNED_SIZEOF (psf->u.cbuf)) + { psf_log_printf (psf, " *** %M : %d (too big)\n", chunk, dword) ; + psf_binheader_readf (psf, "j", dword) ; + break ; + } ; + + cptr = psf->u.cbuf ; + psf_binheader_readf (psf, "b", cptr, dword) ; + bytesread += dword ; + cptr [dword - 1] = 0 ; + psf_log_printf (psf, " %M : %s\n", chunk, cptr) ; + break ; + + case labl_MARKER : + { int mark_id ; + + bytesread += psf_binheader_readf (psf, "44", &dword, &mark_id) ; + dword -= 4 ; + dword += (dword & 1) ; + if (dword < 1 || dword > SIGNED_SIZEOF (psf->u.cbuf)) + { psf_log_printf (psf, " *** %M : %d (too big)\n", chunk, dword) ; + psf_binheader_readf (psf, "j", dword) ; + break ; + } ; + + cptr = psf->u.cbuf ; + psf_binheader_readf (psf, "b", cptr, dword) ; + bytesread += dword ; + cptr [dword - 1] = 0 ; + psf_log_printf (psf, " %M : %d : %s\n", chunk, mark_id, cptr) ; + } ; + break ; + + + case DISP_MARKER : + case ltxt_MARKER : + case note_MARKER : + bytesread += psf_binheader_readf (psf, "4", &dword) ; + dword += (dword & 1) ; + psf_binheader_readf (psf, "j", dword) ; + bytesread += dword ; + psf_log_printf (psf, " %M : %d\n", chunk, dword) ; + break ; + + default : + psf_binheader_readf (psf, "4", &dword) ; + bytesread += sizeof (dword) ; + dword += (dword & 1) ; + psf_binheader_readf (psf, "j", dword) ; + bytesread += dword ; + psf_log_printf (psf, " *** %M : %d\n", chunk, dword) ; + if (dword > length) + return 0 ; + break ; + } ; + + switch (chunk) + { case ISFT_MARKER : + psf_store_string (psf, SF_STR_SOFTWARE, psf->u.cbuf) ; + break ; + case ICOP_MARKER : + psf_store_string (psf, SF_STR_COPYRIGHT, psf->u.cbuf) ; + break ; + case INAM_MARKER : + psf_store_string (psf, SF_STR_TITLE, psf->u.cbuf) ; + break ; + case IART_MARKER : + psf_store_string (psf, SF_STR_ARTIST, psf->u.cbuf) ; + break ; + case ICMT_MARKER : + psf_store_string (psf, SF_STR_COMMENT, psf->u.cbuf) ; + break ; + case ICRD_MARKER : + psf_store_string (psf, SF_STR_DATE, psf->u.cbuf) ; + break ; + } ; + } ; + + current_pos = psf_fseek (psf, 0, SEEK_CUR) - current_pos ; + + if (current_pos - 4 != length) + psf_log_printf (psf, "**** Bad chunk length %d sbould be %D\n", length, current_pos - 4) ; + + return 0 ; +} /* wav_subchunk_parse */ + +static int +wav_read_smpl_chunk (SF_PRIVATE *psf, unsigned int chunklen) +{ unsigned int bytesread = 0, dword, sampler_data, loop_count ; + unsigned int note, start, end, type = -1, count ; + int j, k ; + + chunklen += (chunklen & 1) ; + + bytesread += psf_binheader_readf (psf, "4", &dword) ; + psf_log_printf (psf, " Manufacturer : %X\n", dword) ; + + bytesread += psf_binheader_readf (psf, "4", &dword) ; + psf_log_printf (psf, " Product : %u\n", dword) ; + + bytesread += psf_binheader_readf (psf, "4", &dword) ; + psf_log_printf (psf, " Period : %u nsec\n", dword) ; + + bytesread += psf_binheader_readf (psf, "4", ¬e) ; + psf_log_printf (psf, " Midi Note : %u\n", note) ; + + bytesread += psf_binheader_readf (psf, "4", &dword) ; + if (dword != 0) + { LSF_SNPRINTF (psf->u.cbuf, sizeof (psf->u.cbuf), "%f", + (1.0 * 0x80000000) / ((unsigned int) dword)) ; + psf_log_printf (psf, " Pitch Fract. : %s\n", psf->u.cbuf) ; + } + else + psf_log_printf (psf, " Pitch Fract. : 0\n") ; + + bytesread += psf_binheader_readf (psf, "4", &dword) ; + psf_log_printf (psf, " SMPTE Format : %u\n", dword) ; + + bytesread += psf_binheader_readf (psf, "4", &dword) ; + LSF_SNPRINTF (psf->u.cbuf, sizeof (psf->u.cbuf), "%02d:%02d:%02d %02d", + (dword >> 24) & 0x7F, (dword >> 16) & 0x7F, (dword >> 8) & 0x7F, dword & 0x7F) ; + psf_log_printf (psf, " SMPTE Offset : %s\n", psf->u.cbuf) ; + + bytesread += psf_binheader_readf (psf, "4", &loop_count) ; + psf_log_printf (psf, " Loop Count : %u\n", loop_count) ; + + /* Sampler Data holds the number of data bytes after the CUE chunks which + ** is not actually CUE data. Display value after CUE data. + */ + bytesread += psf_binheader_readf (psf, "4", &sampler_data) ; + + if ((psf->instrument = psf_instrument_alloc ()) == NULL) + return SFE_MALLOC_FAILED ; + + psf->instrument->loop_count = loop_count ; + + for (j = 0 ; loop_count > 0 && chunklen - bytesread >= 24 ; j ++) + { bytesread += psf_binheader_readf (psf, "4", &dword) ; + psf_log_printf (psf, " Cue ID : %2u", dword) ; + + bytesread += psf_binheader_readf (psf, "4", &type) ; + psf_log_printf (psf, " Type : %2u", type) ; + + bytesread += psf_binheader_readf (psf, "4", &start) ; + psf_log_printf (psf, " Start : %5u", start) ; + + bytesread += psf_binheader_readf (psf, "4", &end) ; + psf_log_printf (psf, " End : %5u", end) ; + + bytesread += psf_binheader_readf (psf, "4", &dword) ; + psf_log_printf (psf, " Fraction : %5u", dword) ; + + bytesread += psf_binheader_readf (psf, "4", &count) ; + psf_log_printf (psf, " Count : %5u\n", count) ; + + if (j < ARRAY_LEN (psf->instrument->loops)) + { psf->instrument->loops [j].start = start ; + psf->instrument->loops [j].end = end ; + psf->instrument->loops [j].count = count ; + + switch (type) + { case 0 : + psf->instrument->loops [j].mode = SF_LOOP_FORWARD ; + break ; + case 1 : + psf->instrument->loops [j].mode = SF_LOOP_ALTERNATING ; + break ; + case 2 : + psf->instrument->loops [j].mode = SF_LOOP_BACKWARD ; + break ; + default: + psf->instrument->loops [j].mode = SF_LOOP_NONE ; + break ; + } ; + } ; + + loop_count -- ; + } ; + + if (chunklen - bytesread == 0) + { if (sampler_data != 0) + psf_log_printf (psf, " Sampler Data : %u (should be 0)\n", sampler_data) ; + else + psf_log_printf (psf, " Sampler Data : %u\n", sampler_data) ; + } + else + { if (sampler_data != chunklen - bytesread) + { psf_log_printf (psf, " Sampler Data : %u (should have been %u)\n", sampler_data, chunklen - bytesread) ; + sampler_data = chunklen - bytesread ; + } + else + psf_log_printf (psf, " Sampler Data : %u\n", sampler_data) ; + + psf_log_printf (psf, " ") ; + for (k = 0 ; k < (int) sampler_data ; k++) + { char ch ; + + if (k > 0 && (k % 20) == 0) + psf_log_printf (psf, "\n ") ; + + bytesread += psf_binheader_readf (psf, "1", &ch) ; + psf_log_printf (psf, "%02X ", ch & 0xFF) ; + } ; + + psf_log_printf (psf, "\n") ; + } ; + + psf->instrument->basenote = note ; + psf->instrument->gain = 1 ; + psf->instrument->velocity_lo = psf->instrument->key_lo = 0 ; + psf->instrument->velocity_hi = psf->instrument->key_hi = 127 ; + + return 0 ; +} /* wav_read_smpl_chunk */ + +/* +** The acid chunk goes a little something like this: +** +** 4 bytes 'acid' +** 4 bytes (int) length of chunk starting at next byte +** +** 4 bytes (int) type of file: +** this appears to be a bit mask,however some combinations +** are probably impossible and/or qualified as "errors" +** +** 0x01 On: One Shot Off: Loop +** 0x02 On: Root note is Set Off: No root +** 0x04 On: Stretch is On, Off: Strech is OFF +** 0x08 On: Disk Based Off: Ram based +** 0x10 On: ?????????? Off: ????????? (Acidizer puts that ON) +** +** 2 bytes (short) root note +** if type 0x10 is OFF : [C,C#,(...),B] -> [0x30 to 0x3B] +** if type 0x10 is ON : [C,C#,(...),B] -> [0x3C to 0x47] +** (both types fit on same MIDI pitch albeit different octaves, so who cares) +** +** 2 bytes (short) ??? always set to 0x8000 +** 4 bytes (float) ??? seems to be always 0 +** 4 bytes (int) number of beats +** 2 bytes (short) meter denominator //always 4 in SF/ACID +** 2 bytes (short) meter numerator //always 4 in SF/ACID +** //are we sure about the order?? usually its num/denom +** 4 bytes (float) tempo +** +*/ + +static int +wav_read_acid_chunk (SF_PRIVATE *psf, unsigned int chunklen) +{ unsigned int bytesread = 0 ; + int beats, flags ; + short rootnote, q1, meter_denom, meter_numer ; + float q2, tempo ; + + chunklen += (chunklen & 1) ; + + bytesread += psf_binheader_readf (psf, "422f", &flags, &rootnote, &q1, &q2) ; + + LSF_SNPRINTF (psf->u.cbuf, sizeof (psf->u.cbuf), "%f", q2) ; + + psf_log_printf (psf, " Flags : 0x%04x (%s,%s,%s,%s,%s)\n", flags, + (flags & 0x01) ? "OneShot" : "Loop", + (flags & 0x02) ? "RootNoteValid" : "RootNoteInvalid", + (flags & 0x04) ? "StretchOn" : "StretchOff", + (flags & 0x08) ? "DiskBased" : "RAMBased", + (flags & 0x10) ? "??On" : "??Off") ; + + psf_log_printf (psf, " Root note : 0x%x\n ???? : 0x%04x\n ???? : %s\n", + rootnote, q1, psf->u.cbuf) ; + + bytesread += psf_binheader_readf (psf, "422f", &beats, &meter_denom, &meter_numer, &tempo) ; + LSF_SNPRINTF (psf->u.cbuf, sizeof (psf->u.cbuf), "%f", tempo) ; + psf_log_printf (psf, " Beats : %d\n Meter : %d/%d\n Tempo : %s\n", + beats, meter_numer, meter_denom, psf->u.cbuf) ; + + psf_binheader_readf (psf, "j", chunklen - bytesread) ; + + if ((psf->loop_info = calloc (1, sizeof (SF_LOOP_INFO))) == NULL) + return SFE_MALLOC_FAILED ; + + psf->loop_info->time_sig_num = meter_numer ; + psf->loop_info->time_sig_den = meter_denom ; + psf->loop_info->loop_mode = (flags & 0x01) ? SF_LOOP_NONE : SF_LOOP_FORWARD ; + psf->loop_info->num_beats = beats ; + psf->loop_info->bpm = tempo ; + psf->loop_info->root_key = (flags & 0x02) ? rootnote : -1 ; + + return 0 ; +} /* wav_read_acid_chunk */ + +int +wav_read_bext_chunk (SF_PRIVATE *psf, unsigned int chunksize) +{ + SF_BROADCAST_INFO* b ; + unsigned int bytes = 0 ; + + if ((psf->broadcast_info = calloc (1, sizeof (SF_BROADCAST_INFO))) == NULL) + { psf->error = SFE_MALLOC_FAILED ; + return psf->error ; + } ; + + b = psf->broadcast_info ; + + bytes += psf_binheader_readf (psf, "b", b->description, sizeof (b->description)) ; + bytes += psf_binheader_readf (psf, "b", b->originator, sizeof (b->originator)) ; + bytes += psf_binheader_readf (psf, "b", b->originator_reference, sizeof (b->originator_reference)) ; + bytes += psf_binheader_readf (psf, "b", b->origination_date, sizeof (b->origination_date)) ; + bytes += psf_binheader_readf (psf, "b", b->origination_time, sizeof (b->origination_time)) ; + bytes += psf_binheader_readf (psf, "442", &b->time_reference_low, &b->time_reference_high, &b->version) ; + bytes += psf_binheader_readf (psf, "bj", &b->umid, sizeof (b->umid), 190) ; + + if (chunksize > WAV_BEXT_CHUNK_SIZE) + { /* File has coding history data. */ + + b->coding_history_size = chunksize - WAV_BEXT_CHUNK_SIZE ; + + if (b->coding_history_size > SIGNED_SIZEOF (b->coding_history)) + b->coding_history_size = SIGNED_SIZEOF (b->coding_history) ; + + /* We do not parse the coding history */ + bytes += psf_binheader_readf (psf, "b", b->coding_history, b->coding_history_size) ; + b->coding_history [sizeof (b->coding_history) - 1] = 0 ; + } ; + + if (bytes < chunksize) + psf_binheader_readf (psf, "j", chunksize - bytes) ; + + return 0 ; +} /* wav_read_bext_chunk */ + +static int +wav_write_bext_chunk (SF_PRIVATE *psf) +{ SF_BROADCAST_INFO *b ; + + if ((b = psf->broadcast_info) == NULL) + return -1 ; + + psf_binheader_writef (psf, "m4", bext_MARKER, WAV_BEXT_CHUNK_SIZE + b->coding_history_size) ; + + /* + ** Note that it is very important the the field widths of the SF_BROADCAST_INFO + ** struct match those for the bext chunk fields. + */ + + psf_binheader_writef (psf, "b", b->description, sizeof (b->description)) ; + psf_binheader_writef (psf, "b", b->originator, sizeof (b->originator)) ; + psf_binheader_writef (psf, "b", b->originator_reference, sizeof (b->originator_reference)) ; + psf_binheader_writef (psf, "b", b->origination_date, sizeof (b->origination_date)) ; + psf_binheader_writef (psf, "b", b->origination_time, sizeof (b->origination_time)) ; + psf_binheader_writef (psf, "442", b->time_reference_low, b->time_reference_high, b->version) ; + psf_binheader_writef (psf, "b", b->umid, sizeof (b->umid)) ; + psf_binheader_writef (psf, "z", make_size_t (190)) ; + + if (b->coding_history_size > 0) + psf_binheader_writef (psf, "b", b->coding_history, make_size_t (b->coding_history_size)) ; + + return 0 ; +} /* wav_write_bext_chunk */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 9c551689-a1d8-4905-9f56-26a204374f18 +*/ diff --git a/src/wav_w64.c b/src/wav_w64.c new file mode 100644 index 00000000..cec6a9b7 --- /dev/null +++ b/src/wav_w64.c @@ -0,0 +1,578 @@ +/* +** Copyright (C) 1999-2007 Erik de Castro Lopo +** Copyright (C) 2004-2005 David Viens +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include +#include + +#include "sndfile.h" +#include "sfendian.h" +#include "common.h" +#include "wav_w64.h" + +/* Known WAVEFORMATEXTENSIBLE GUIDS. */ +static const EXT_SUBFORMAT MSGUID_SUBTYPE_PCM = +{ 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } +} ; + +static const EXT_SUBFORMAT MSGUID_SUBTYPE_MS_ADPCM = +{ 0x00000002, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } +} ; + +static const EXT_SUBFORMAT MSGUID_SUBTYPE_IEEE_FLOAT = +{ 0x00000003, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } +} ; + +static const EXT_SUBFORMAT MSGUID_SUBTYPE_ALAW = +{ 0x00000006, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } +} ; + +static const EXT_SUBFORMAT MSGUID_SUBTYPE_MULAW = +{ 0x00000007, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } +} ; + +/* +** the next two are from +** http://dream.cs.bath.ac.uk/researchdev/wave-ex/bformat.html +*/ + +static const EXT_SUBFORMAT MSGUID_SUBTYPE_AMBISONIC_B_FORMAT_PCM = +{ 0x00000001, 0x0721, 0x11d3, { 0x86, 0x44, 0xC8, 0xC1, 0xCA, 0x00, 0x00, 0x00 } +} ; + +static const EXT_SUBFORMAT MSGUID_SUBTYPE_AMBISONIC_B_FORMAT_IEEE_FLOAT = +{ 0x00000003, 0x0721, 0x11d3, { 0x86, 0x44, 0xC8, 0xC1, 0xCA, 0x00, 0x00, 0x00 } +} ; + + +#if 0 +/* maybe interesting one day to read the following through sf_read_raw */ +/* http://www.bath.ac.uk/~masrwd/pvocex/pvocex.html */ +static const EXT_SUBFORMAT MSGUID_SUBTYPE_PVOCEX = +{ 0x8312B9C2, 0x2E6E, 0x11d4, { 0xA8, 0x24, 0xDE, 0x5B, 0x96, 0xC3, 0xAB, 0x21 } +} ; +#endif + +/*------------------------------------------------------------------------------ + * Private static functions. + */ + +static int +wavex_write_guid_equal (const EXT_SUBFORMAT * first, const EXT_SUBFORMAT * second) +{ return !memcmp (first, second, sizeof (EXT_SUBFORMAT)) ; +} /* wavex_write_guid_equal */ + + + +int +wav_w64_read_fmt_chunk (SF_PRIVATE *psf, int fmtsize) +{ WAV_PRIVATE * wpriv ; + WAV_FMT *wav_fmt ; + int bytesread, k, bytespersec = 0 ; + + if ((wpriv = psf->container_data) == NULL) + return SFE_INTERNAL ; + wav_fmt = &wpriv->wav_fmt ; + + memset (wav_fmt, 0, sizeof (WAV_FMT)) ; + + if (fmtsize < 16) + return SFE_WAV_FMT_SHORT ; + + /* assume psf->rwf_endian is already properly set */ + + /* Read the minimal WAV file header here. */ + bytesread = psf_binheader_readf (psf, "224422", + &(wav_fmt->format), &(wav_fmt->min.channels), + &(wav_fmt->min.samplerate), &(wav_fmt->min.bytespersec), + &(wav_fmt->min.blockalign), &(wav_fmt->min.bitwidth)) ; + + psf_log_printf (psf, " Format : 0x%X => %s\n", wav_fmt->format, wav_w64_format_str (wav_fmt->format)) ; + psf_log_printf (psf, " Channels : %d\n", wav_fmt->min.channels) ; + psf_log_printf (psf, " Sample Rate : %d\n", wav_fmt->min.samplerate) ; + psf_log_printf (psf, " Block Align : %d\n", wav_fmt->min.blockalign) ; + + if (wav_fmt->format == WAVE_FORMAT_PCM && wav_fmt->min.bitwidth == 24 && + wav_fmt->min.blockalign == 4 * wav_fmt->min.channels) + { psf_log_printf (psf, " Bit Width : 24\n") ; + + psf_log_printf (psf, "\n" + " Ambiguous information in 'fmt ' chunk. Possibile file types:\n" + " 0) Invalid IEEE float file generated by Syntrillium's Cooledit!\n" + " 1) File generated by ALSA's arecord containing 24 bit samples in 32 bit containers.\n" + " 2) 24 bit file with incorrect Block Align value.\n" + "\n") ; + + wpriv->fmt_is_broken = 1 ; + } + else if (wav_fmt->min.bitwidth == 0) + { switch (wav_fmt->format) + { case WAVE_FORMAT_GSM610 : + case WAVE_FORMAT_IPP_ITU_G_723_1 : + psf_log_printf (psf, " Bit Width : %d\n", wav_fmt->min.bitwidth) ; + break ; + default : + psf_log_printf (psf, " Bit Width : %d (should not be 0)\n", wav_fmt->min.bitwidth) ; + } + } + else + { switch (wav_fmt->format) + { case WAVE_FORMAT_GSM610 : + case WAVE_FORMAT_IPP_ITU_G_723_1 : + psf_log_printf (psf, " Bit Width : %d (should be 0)\n", wav_fmt->min.bitwidth) ; + break ; + default : + psf_log_printf (psf, " Bit Width : %d\n", wav_fmt->min.bitwidth) ; + } + } ; + + psf->sf.samplerate = wav_fmt->min.samplerate ; + psf->sf.frames = 0 ; /* Correct this when reading data chunk. */ + psf->sf.channels = wav_fmt->min.channels ; + + switch (wav_fmt->format) + { case WAVE_FORMAT_PCM : + case WAVE_FORMAT_IEEE_FLOAT : + bytespersec = wav_fmt->min.samplerate * wav_fmt->min.blockalign ; + if (wav_fmt->min.bytespersec != (unsigned) bytespersec) + psf_log_printf (psf, " Bytes/sec : %d (should be %d)\n", wav_fmt->min.bytespersec, bytespersec) ; + else + psf_log_printf (psf, " Bytes/sec : %d\n", wav_fmt->min.bytespersec) ; + + psf->bytewidth = BITWIDTH2BYTES (wav_fmt->min.bitwidth) ; + break ; + + case WAVE_FORMAT_ALAW : + case WAVE_FORMAT_MULAW : + if (wav_fmt->min.bytespersec / wav_fmt->min.blockalign != wav_fmt->min.samplerate) + psf_log_printf (psf, " Bytes/sec : %d (should be %d)\n", wav_fmt->min.bytespersec, wav_fmt->min.samplerate * wav_fmt->min.blockalign) ; + else + psf_log_printf (psf, " Bytes/sec : %d\n", wav_fmt->min.bytespersec) ; + + psf->bytewidth = 1 ; + if (fmtsize >= 18) + { bytesread += psf_binheader_readf (psf, "2", &(wav_fmt->size20.extrabytes)) ; + psf_log_printf (psf, " Extra Bytes : %d\n", wav_fmt->size20.extrabytes) ; + } ; + break ; + + case WAVE_FORMAT_IMA_ADPCM : + if (wav_fmt->min.bitwidth != 4) + return SFE_WAV_ADPCM_NOT4BIT ; + if (wav_fmt->min.channels < 1 || wav_fmt->min.channels > 2) + return SFE_WAV_ADPCM_CHANNELS ; + + bytesread += + psf_binheader_readf (psf, "22", &(wav_fmt->ima.extrabytes), &(wav_fmt->ima.samplesperblock)) ; + + bytespersec = (wav_fmt->ima.samplerate * wav_fmt->ima.blockalign) / wav_fmt->ima.samplesperblock ; + if (wav_fmt->ima.bytespersec != (unsigned) bytespersec) + psf_log_printf (psf, " Bytes/sec : %d (should be %d)\n", wav_fmt->ima.bytespersec, bytespersec) ; + else + psf_log_printf (psf, " Bytes/sec : %d\n", wav_fmt->ima.bytespersec) ; + + psf->bytewidth = 2 ; + psf_log_printf (psf, " Extra Bytes : %d\n", wav_fmt->ima.extrabytes) ; + psf_log_printf (psf, " Samples/Block : %d\n", wav_fmt->ima.samplesperblock) ; + break ; + + case WAVE_FORMAT_MS_ADPCM : + if (wav_fmt->msadpcm.bitwidth != 4) + return SFE_WAV_ADPCM_NOT4BIT ; + if (wav_fmt->msadpcm.channels < 1 || wav_fmt->msadpcm.channels > 2) + return SFE_WAV_ADPCM_CHANNELS ; + + bytesread += + psf_binheader_readf (psf, "222", &(wav_fmt->msadpcm.extrabytes), + &(wav_fmt->msadpcm.samplesperblock), &(wav_fmt->msadpcm.numcoeffs)) ; + + bytespersec = (wav_fmt->min.samplerate * wav_fmt->min.blockalign) / wav_fmt->msadpcm.samplesperblock ; + if (wav_fmt->min.bytespersec == (unsigned) bytespersec) + psf_log_printf (psf, " Bytes/sec : %d\n", wav_fmt->min.bytespersec) ; + else if (wav_fmt->min.bytespersec == (wav_fmt->min.samplerate / wav_fmt->msadpcm.samplesperblock) * wav_fmt->min.blockalign) + psf_log_printf (psf, " Bytes/sec : %d (should be %d (MS BUG!))\n", wav_fmt->min.bytespersec, bytespersec) ; + else + psf_log_printf (psf, " Bytes/sec : %d (should be %d)\n", wav_fmt->min.bytespersec, bytespersec) ; + + + psf->bytewidth = 2 ; + psf_log_printf (psf, " Extra Bytes : %d\n", wav_fmt->msadpcm.extrabytes) ; + psf_log_printf (psf, " Samples/Block : %d\n", wav_fmt->msadpcm.samplesperblock) ; + if (wav_fmt->msadpcm.numcoeffs > SIGNED_SIZEOF (MS_ADPCM_WAV_FMT) / SIGNED_SIZEOF (int)) + { psf_log_printf (psf, " No. of Coeffs : %d ****\n", wav_fmt->msadpcm.numcoeffs) ; + wav_fmt->msadpcm.numcoeffs = SIGNED_SIZEOF (MS_ADPCM_WAV_FMT) / SIGNED_SIZEOF (int) ; + } + else + psf_log_printf (psf, " No. of Coeffs : %d\n", wav_fmt->msadpcm.numcoeffs) ; + + psf_log_printf (psf, " Index Coeffs1 Coeffs2\n") ; + for (k = 0 ; k < wav_fmt->msadpcm.numcoeffs ; k++) + { bytesread += + psf_binheader_readf (psf, "22", &(wav_fmt->msadpcm.coeffs [k].coeff1), &(wav_fmt->msadpcm.coeffs [k].coeff2)) ; + LSF_SNPRINTF (psf->u.cbuf, sizeof (psf->u.cbuf), " %2d %7d %7d\n", k, wav_fmt->msadpcm.coeffs [k].coeff1, wav_fmt->msadpcm.coeffs [k].coeff2) ; + psf_log_printf (psf, psf->u.cbuf) ; + } ; + break ; + + case WAVE_FORMAT_GSM610 : + if (wav_fmt->gsm610.channels != 1 || wav_fmt->gsm610.blockalign != 65) + return SFE_WAV_GSM610_FORMAT ; + + bytesread += + psf_binheader_readf (psf, "22", &(wav_fmt->gsm610.extrabytes), &(wav_fmt->gsm610.samplesperblock)) ; + + if (wav_fmt->gsm610.samplesperblock != 320) + return SFE_WAV_GSM610_FORMAT ; + + bytespersec = (wav_fmt->gsm610.samplerate * wav_fmt->gsm610.blockalign) / wav_fmt->gsm610.samplesperblock ; + if (wav_fmt->gsm610.bytespersec != (unsigned) bytespersec) + psf_log_printf (psf, " Bytes/sec : %d (should be %d)\n", wav_fmt->gsm610.bytespersec, bytespersec) ; + else + psf_log_printf (psf, " Bytes/sec : %d\n", wav_fmt->gsm610.bytespersec) ; + + psf->bytewidth = 2 ; + psf_log_printf (psf, " Extra Bytes : %d\n", wav_fmt->gsm610.extrabytes) ; + psf_log_printf (psf, " Samples/Block : %d\n", wav_fmt->gsm610.samplesperblock) ; + break ; + + case WAVE_FORMAT_EXTENSIBLE : + if (wav_fmt->ext.bytespersec / wav_fmt->ext.blockalign != wav_fmt->ext.samplerate) + psf_log_printf (psf, " Bytes/sec : %d (should be %d)\n", wav_fmt->ext.bytespersec, wav_fmt->ext.samplerate * wav_fmt->ext.blockalign) ; + else + psf_log_printf (psf, " Bytes/sec : %d\n", wav_fmt->ext.bytespersec) ; + + bytesread += + psf_binheader_readf (psf, "224", &(wav_fmt->ext.extrabytes), &(wav_fmt->ext.validbits), + &(wav_fmt->ext.channelmask)) ; + + psf_log_printf (psf, " Valid Bits : %d\n", wav_fmt->ext.validbits) ; + psf_log_printf (psf, " Channel Mask : 0x%X\n", wav_fmt->ext.channelmask) ; + + bytesread += + psf_binheader_readf (psf, "422", &(wav_fmt->ext.esf.esf_field1), &(wav_fmt->ext.esf.esf_field2), + &(wav_fmt->ext.esf.esf_field3)) ; + + /* compare the esf_fields with each known GUID? and print? */ + psf_log_printf (psf, " Subformat\n") ; + psf_log_printf (psf, " esf_field1 : 0x%X\n", wav_fmt->ext.esf.esf_field1) ; + psf_log_printf (psf, " esf_field2 : 0x%X\n", wav_fmt->ext.esf.esf_field2) ; + psf_log_printf (psf, " esf_field3 : 0x%X\n", wav_fmt->ext.esf.esf_field3) ; + psf_log_printf (psf, " esf_field4 : ") ; + for (k = 0 ; k < 8 ; k++) + { bytesread += psf_binheader_readf (psf, "1", &(wav_fmt->ext.esf.esf_field4 [k])) ; + psf_log_printf (psf, "0x%X ", wav_fmt->ext.esf.esf_field4 [k] & 0xFF) ; + } ; + psf_log_printf (psf, "\n") ; + psf->bytewidth = BITWIDTH2BYTES (wav_fmt->ext.bitwidth) ; + + /* Compare GUIDs for known ones. */ + if (wavex_write_guid_equal (&wav_fmt->ext.esf, &MSGUID_SUBTYPE_PCM) + || wavex_write_guid_equal (&wav_fmt->ext.esf, &MSGUID_SUBTYPE_AMBISONIC_B_FORMAT_PCM)) + { psf->sf.format = SF_FORMAT_WAVEX | u_bitwidth_to_subformat (psf->bytewidth * 8) ; + psf_log_printf (psf, " format : pcm\n") ; + } + else if (wavex_write_guid_equal (&wav_fmt->ext.esf, &MSGUID_SUBTYPE_MS_ADPCM)) + { psf->sf.format = (SF_FORMAT_WAVEX | SF_FORMAT_MS_ADPCM) ; + psf_log_printf (psf, " format : ms adpcm\n") ; + } + else if (wavex_write_guid_equal (&wav_fmt->ext.esf, &MSGUID_SUBTYPE_IEEE_FLOAT) + || wavex_write_guid_equal (&wav_fmt->ext.esf, &MSGUID_SUBTYPE_AMBISONIC_B_FORMAT_IEEE_FLOAT)) + { psf->sf.format = SF_FORMAT_WAVEX | ((psf->bytewidth == 8) ? SF_FORMAT_DOUBLE : SF_FORMAT_FLOAT) ; + psf_log_printf (psf, " format : IEEE float\n") ; + } + else if (wavex_write_guid_equal (&wav_fmt->ext.esf, &MSGUID_SUBTYPE_ALAW)) + { psf->sf.format = (SF_FORMAT_WAVEX | SF_FORMAT_ALAW) ; + psf_log_printf (psf, " format : A-law\n") ; + } + else if (wavex_write_guid_equal (&wav_fmt->ext.esf, &MSGUID_SUBTYPE_MULAW)) + { psf->sf.format = (SF_FORMAT_WAVEX | SF_FORMAT_ULAW) ; + psf_log_printf (psf, " format : u-law\n") ; + } + else + return SFE_UNIMPLEMENTED ; + + psf->wavex_ambisonic = wavex_write_guid_equal (&wav_fmt->ext.esf, &MSGUID_SUBTYPE_AMBISONIC_B_FORMAT_PCM) + || wavex_write_guid_equal (&wav_fmt->ext.esf, &MSGUID_SUBTYPE_AMBISONIC_B_FORMAT_IEEE_FLOAT) ; + break ; + + case WAVE_FORMAT_G721_ADPCM : + psf_log_printf (psf, " Bytes/sec : %d\n", wav_fmt->g72x.bytespersec) ; + if (fmtsize >= 20) + { bytesread += psf_binheader_readf (psf, "22", &(wav_fmt->g72x.extrabytes), &(wav_fmt->g72x.auxblocksize)) ; + if (wav_fmt->g72x.extrabytes == 0) + psf_log_printf (psf, " Extra Bytes : %d (should be 2)\n", wav_fmt->g72x.extrabytes) ; + else + psf_log_printf (psf, " Extra Bytes : %d\n", wav_fmt->g72x.extrabytes) ; + psf_log_printf (psf, " Aux Blk Size : %d\n", wav_fmt->g72x.auxblocksize) ; + } + else if (fmtsize == 18) + { bytesread += psf_binheader_readf (psf, "2", &(wav_fmt->g72x.extrabytes)) ; + psf_log_printf (psf, " Extra Bytes : %d%s\n", wav_fmt->g72x.extrabytes, wav_fmt->g72x.extrabytes != 0 ? " (should be 0)" : "") ; + } + else + psf_log_printf (psf, "*** 'fmt ' chunk should be bigger than this!\n") ; + break ; + + default : + psf_log_printf (psf, "*** No 'fmt ' chunk dumper for this format!\n") ; + break ; + } ; + + if (bytesread > fmtsize) + { psf_log_printf (psf, "*** wav_w64_read_fmt_chunk (bytesread > fmtsize)\n") ; + return SFE_W64_FMT_SHORT ; + } + else + psf_binheader_readf (psf, "j", fmtsize - bytesread) ; + + psf->blockwidth = wav_fmt->min.channels * psf->bytewidth ; + + return 0 ; +} /* wav_w64_read_fmt_chunk */ + +void +wavex_write_guid (SF_PRIVATE *psf, const EXT_SUBFORMAT * subformat) +{ + psf_binheader_writef (psf, "422b", subformat->esf_field1, + subformat->esf_field2, subformat->esf_field3, + subformat->esf_field4, make_size_t (8)) ; +} /* wavex_write_guid */ + +void +wav_w64_analyze (SF_PRIVATE *psf) +{ AUDIO_DETECT ad ; + int format = 0 ; + + if (psf->is_pipe) + { psf_log_printf (psf, "*** Error : Reading from a pipe. Can't analyze data section to figure out real data format.\n\n") ; + return ; + } ; + + psf_log_printf (psf, "---------------------------------------------------\n" + "Format is known to be broken. Using detection code.\n") ; + + /* Code goes here. */ + ad.endianness = SF_ENDIAN_LITTLE ; + ad.channels = psf->sf.channels ; + + psf_fseek (psf, 3 * 4 * 50, SEEK_SET) ; + + while (psf_fread (psf->u.ucbuf, 1, 4096, psf) == 4096) + { format = audio_detect (psf, &ad, psf->u.ucbuf, 4096) ; + if (format != 0) + break ; + } ; + + /* Seek to start of DATA section. */ + psf_fseek (psf, psf->dataoffset, SEEK_SET) ; + + if (format == 0) + { psf_log_printf (psf, "wav_w64_analyze : detection failed.\n") ; + return ; + } ; + + switch (format) + { case SF_FORMAT_PCM_32 : + case SF_FORMAT_FLOAT : + psf_log_printf (psf, "wav_w64_analyze : found format : 0x%X\n", format) ; + psf->sf.format = (psf->sf.format & ~SF_FORMAT_SUBMASK) + format ; + psf->bytewidth = 4 ; + psf->blockwidth = psf->sf.channels * psf->bytewidth ; + break ; + + case SF_FORMAT_PCM_24 : + psf_log_printf (psf, "wav_w64_analyze : found format : 0x%X\n", format) ; + psf->sf.format = (psf->sf.format & ~SF_FORMAT_SUBMASK) + format ; + psf->bytewidth = 3 ; + psf->blockwidth = psf->sf.channels * psf->bytewidth ; + break ; + + default : + psf_log_printf (psf, "wav_w64_analyze : unhandled format : 0x%X\n", format) ; + break ; + } ; + + return ; +} /* wav_w64_analyze */ + +/*============================================================================== +*/ + +typedef struct +{ int ID ; + const char *name ; +} WAV_FORMAT_DESC ; + +#define STR(x) #x +#define FORMAT_TYPE(x) { x, STR (x) } + +static WAV_FORMAT_DESC wave_descs [] = +{ FORMAT_TYPE (WAVE_FORMAT_PCM), + FORMAT_TYPE (WAVE_FORMAT_MS_ADPCM), + FORMAT_TYPE (WAVE_FORMAT_IEEE_FLOAT), + FORMAT_TYPE (WAVE_FORMAT_VSELP), + FORMAT_TYPE (WAVE_FORMAT_IBM_CVSD), + FORMAT_TYPE (WAVE_FORMAT_ALAW), + FORMAT_TYPE (WAVE_FORMAT_MULAW), + FORMAT_TYPE (WAVE_FORMAT_OKI_ADPCM), + FORMAT_TYPE (WAVE_FORMAT_IMA_ADPCM), + FORMAT_TYPE (WAVE_FORMAT_MEDIASPACE_ADPCM), + FORMAT_TYPE (WAVE_FORMAT_SIERRA_ADPCM), + FORMAT_TYPE (WAVE_FORMAT_G723_ADPCM), + FORMAT_TYPE (WAVE_FORMAT_DIGISTD), + FORMAT_TYPE (WAVE_FORMAT_DIGIFIX), + FORMAT_TYPE (WAVE_FORMAT_DIALOGIC_OKI_ADPCM), + FORMAT_TYPE (WAVE_FORMAT_MEDIAVISION_ADPCM), + FORMAT_TYPE (WAVE_FORMAT_CU_CODEC), + FORMAT_TYPE (WAVE_FORMAT_YAMAHA_ADPCM), + FORMAT_TYPE (WAVE_FORMAT_SONARC), + FORMAT_TYPE (WAVE_FORMAT_DSPGROUP_TRUESPEECH), + FORMAT_TYPE (WAVE_FORMAT_ECHOSC1), + FORMAT_TYPE (WAVE_FORMAT_AUDIOFILE_AF36), + FORMAT_TYPE (WAVE_FORMAT_APTX), + FORMAT_TYPE (WAVE_FORMAT_AUDIOFILE_AF10), + FORMAT_TYPE (WAVE_FORMAT_PROSODY_1612), + FORMAT_TYPE (WAVE_FORMAT_LRC), + FORMAT_TYPE (WAVE_FORMAT_DOLBY_AC2), + FORMAT_TYPE (WAVE_FORMAT_GSM610), + FORMAT_TYPE (WAVE_FORMAT_MSNAUDIO), + FORMAT_TYPE (WAVE_FORMAT_ANTEX_ADPCME), + FORMAT_TYPE (WAVE_FORMAT_CONTROL_RES_VQLPC), + FORMAT_TYPE (WAVE_FORMAT_DIGIREAL), + FORMAT_TYPE (WAVE_FORMAT_DIGIADPCM), + FORMAT_TYPE (WAVE_FORMAT_CONTROL_RES_CR10), + FORMAT_TYPE (WAVE_FORMAT_NMS_VBXADPCM), + FORMAT_TYPE (WAVE_FORMAT_ROLAND_RDAC), + FORMAT_TYPE (WAVE_FORMAT_ECHOSC3), + FORMAT_TYPE (WAVE_FORMAT_ROCKWELL_ADPCM), + FORMAT_TYPE (WAVE_FORMAT_ROCKWELL_DIGITALK), + FORMAT_TYPE (WAVE_FORMAT_XEBEC), + FORMAT_TYPE (WAVE_FORMAT_G721_ADPCM), + FORMAT_TYPE (WAVE_FORMAT_G728_CELP), + FORMAT_TYPE (WAVE_FORMAT_MSG723), + FORMAT_TYPE (WAVE_FORMAT_MPEG), + FORMAT_TYPE (WAVE_FORMAT_RT24), + FORMAT_TYPE (WAVE_FORMAT_PAC), + FORMAT_TYPE (WAVE_FORMAT_MPEGLAYER3), + FORMAT_TYPE (WAVE_FORMAT_LUCENT_G723), + FORMAT_TYPE (WAVE_FORMAT_CIRRUS), + FORMAT_TYPE (WAVE_FORMAT_ESPCM), + FORMAT_TYPE (WAVE_FORMAT_VOXWARE), + FORMAT_TYPE (WAVE_FORMAT_CANOPUS_ATRAC), + FORMAT_TYPE (WAVE_FORMAT_G726_ADPCM), + FORMAT_TYPE (WAVE_FORMAT_G722_ADPCM), + FORMAT_TYPE (WAVE_FORMAT_DSAT), + FORMAT_TYPE (WAVE_FORMAT_DSAT_DISPLAY), + FORMAT_TYPE (WAVE_FORMAT_VOXWARE_BYTE_ALIGNED), + FORMAT_TYPE (WAVE_FORMAT_VOXWARE_AC8), + FORMAT_TYPE (WAVE_FORMAT_VOXWARE_AC10), + FORMAT_TYPE (WAVE_FORMAT_VOXWARE_AC16), + FORMAT_TYPE (WAVE_FORMAT_VOXWARE_AC20), + FORMAT_TYPE (WAVE_FORMAT_VOXWARE_RT24), + FORMAT_TYPE (WAVE_FORMAT_VOXWARE_RT29), + FORMAT_TYPE (WAVE_FORMAT_VOXWARE_RT29HW), + FORMAT_TYPE (WAVE_FORMAT_VOXWARE_VR12), + FORMAT_TYPE (WAVE_FORMAT_VOXWARE_VR18), + FORMAT_TYPE (WAVE_FORMAT_VOXWARE_TQ40), + FORMAT_TYPE (WAVE_FORMAT_SOFTSOUND), + FORMAT_TYPE (WAVE_FORMAT_VOXARE_TQ60), + FORMAT_TYPE (WAVE_FORMAT_MSRT24), + FORMAT_TYPE (WAVE_FORMAT_G729A), + FORMAT_TYPE (WAVE_FORMAT_MVI_MV12), + FORMAT_TYPE (WAVE_FORMAT_DF_G726), + FORMAT_TYPE (WAVE_FORMAT_DF_GSM610), + FORMAT_TYPE (WAVE_FORMAT_ONLIVE), + FORMAT_TYPE (WAVE_FORMAT_SBC24), + FORMAT_TYPE (WAVE_FORMAT_DOLBY_AC3_SPDIF), + FORMAT_TYPE (WAVE_FORMAT_ZYXEL_ADPCM), + FORMAT_TYPE (WAVE_FORMAT_PHILIPS_LPCBB), + FORMAT_TYPE (WAVE_FORMAT_PACKED), + FORMAT_TYPE (WAVE_FORMAT_RHETOREX_ADPCM), + FORMAT_TYPE (IBM_FORMAT_MULAW), + FORMAT_TYPE (IBM_FORMAT_ALAW), + FORMAT_TYPE (IBM_FORMAT_ADPCM), + FORMAT_TYPE (WAVE_FORMAT_VIVO_G723), + FORMAT_TYPE (WAVE_FORMAT_VIVO_SIREN), + FORMAT_TYPE (WAVE_FORMAT_DIGITAL_G723), + FORMAT_TYPE (WAVE_FORMAT_CREATIVE_ADPCM), + FORMAT_TYPE (WAVE_FORMAT_CREATIVE_FASTSPEECH8), + FORMAT_TYPE (WAVE_FORMAT_CREATIVE_FASTSPEECH10), + FORMAT_TYPE (WAVE_FORMAT_QUARTERDECK), + FORMAT_TYPE (WAVE_FORMAT_FM_TOWNS_SND), + FORMAT_TYPE (WAVE_FORMAT_BZV_DIGITAL), + FORMAT_TYPE (WAVE_FORMAT_VME_VMPCM), + FORMAT_TYPE (WAVE_FORMAT_OLIGSM), + FORMAT_TYPE (WAVE_FORMAT_OLIADPCM), + FORMAT_TYPE (WAVE_FORMAT_OLICELP), + FORMAT_TYPE (WAVE_FORMAT_OLISBC), + FORMAT_TYPE (WAVE_FORMAT_OLIOPR), + FORMAT_TYPE (WAVE_FORMAT_LH_CODEC), + FORMAT_TYPE (WAVE_FORMAT_NORRIS), + FORMAT_TYPE (WAVE_FORMAT_SOUNDSPACE_MUSICOMPRESS), + FORMAT_TYPE (WAVE_FORMAT_DVM), + FORMAT_TYPE (WAVE_FORMAT_INTERWAV_VSC112), + FORMAT_TYPE (WAVE_FORMAT_IPP_ITU_G_723_1), + FORMAT_TYPE (WAVE_FORMAT_EXTENSIBLE), +} ; + +char const* +wav_w64_format_str (int k) +{ int lower, upper, mid ; + + lower = -1 ; + upper = sizeof (wave_descs) / sizeof (WAV_FORMAT_DESC) ; + + /* binary search */ + if ((wave_descs [0].ID <= k) && (k <= wave_descs [upper - 1].ID)) + { + while (lower + 1 < upper) + { mid = (upper + lower) / 2 ; + + if (k == wave_descs [mid].ID) + return wave_descs [mid].name ; + if (k < wave_descs [mid].ID) + upper = mid ; + else + lower = mid ; + } ; + } ; + + return "Unknown format" ; +} /* wav_w64_format_str */ + +int +wav_w64_srate2blocksize (int srate_chan_product) +{ if (srate_chan_product < 12000) + return 256 ; + if (srate_chan_product < 23000) + return 512 ; + if (srate_chan_product < 44000) + return 1024 ; + return 2048 ; +} /* srate2blocksize */ +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 43c1b1dd-8abd-43da-a8cd-44da914b64a5 +*/ diff --git a/src/wav_w64.h b/src/wav_w64.h new file mode 100644 index 00000000..bb738fda --- /dev/null +++ b/src/wav_w64.h @@ -0,0 +1,293 @@ +/* +** Copyright (C) 1999-2007 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* This file contains definitions commong to WAV and W64 files. */ + + +#ifndef WAV_W64_H_INCLUDED +#define WAV_W64_H_INCLUDED + +/*------------------------------------------------------------------------------ +** List of known WAV format tags +*/ + +enum +{ + /* keep sorted for wav_w64_format_str() */ + WAVE_FORMAT_UNKNOWN = 0x0000, /* Microsoft Corporation */ + WAVE_FORMAT_PCM = 0x0001, /* Microsoft PCM format */ + WAVE_FORMAT_MS_ADPCM = 0x0002, /* Microsoft ADPCM */ + WAVE_FORMAT_IEEE_FLOAT = 0x0003, /* Micrososft 32 bit float format */ + WAVE_FORMAT_VSELP = 0x0004, /* Compaq Computer Corporation */ + WAVE_FORMAT_IBM_CVSD = 0x0005, /* IBM Corporation */ + WAVE_FORMAT_ALAW = 0x0006, /* Microsoft Corporation */ + WAVE_FORMAT_MULAW = 0x0007, /* Microsoft Corporation */ + WAVE_FORMAT_OKI_ADPCM = 0x0010, /* OKI */ + WAVE_FORMAT_IMA_ADPCM = 0x0011, /* Intel Corporation */ + WAVE_FORMAT_MEDIASPACE_ADPCM = 0x0012, /* Videologic */ + WAVE_FORMAT_SIERRA_ADPCM = 0x0013, /* Sierra Semiconductor Corp */ + WAVE_FORMAT_G723_ADPCM = 0x0014, /* Antex Electronics Corporation */ + WAVE_FORMAT_DIGISTD = 0x0015, /* DSP Solutions, Inc. */ + WAVE_FORMAT_DIGIFIX = 0x0016, /* DSP Solutions, Inc. */ + WAVE_FORMAT_DIALOGIC_OKI_ADPCM = 0x0017, /* Dialogic Corporation */ + WAVE_FORMAT_MEDIAVISION_ADPCM = 0x0018, /* Media Vision, Inc. */ + WAVE_FORMAT_CU_CODEC = 0x0019, /* Hewlett-Packard Company */ + WAVE_FORMAT_YAMAHA_ADPCM = 0x0020, /* Yamaha Corporation of America */ + WAVE_FORMAT_SONARC = 0x0021, /* Speech Compression */ + WAVE_FORMAT_DSPGROUP_TRUESPEECH = 0x0022, /* DSP Group, Inc */ + WAVE_FORMAT_ECHOSC1 = 0x0023, /* Echo Speech Corporation */ + WAVE_FORMAT_AUDIOFILE_AF36 = 0x0024, /* Audiofile, Inc. */ + WAVE_FORMAT_APTX = 0x0025, /* Audio Processing Technology */ + WAVE_FORMAT_AUDIOFILE_AF10 = 0x0026, /* Audiofile, Inc. */ + WAVE_FORMAT_PROSODY_1612 = 0x0027, /* Aculab plc */ + WAVE_FORMAT_LRC = 0x0028, /* Merging Technologies S.A. */ + WAVE_FORMAT_DOLBY_AC2 = 0x0030, /* Dolby Laboratories */ + WAVE_FORMAT_GSM610 = 0x0031, /* Microsoft Corporation */ + WAVE_FORMAT_MSNAUDIO = 0x0032, /* Microsoft Corporation */ + WAVE_FORMAT_ANTEX_ADPCME = 0x0033, /* Antex Electronics Corporation */ + WAVE_FORMAT_CONTROL_RES_VQLPC = 0x0034, /* Control Resources Limited */ + WAVE_FORMAT_DIGIREAL = 0x0035, /* DSP Solutions, Inc. */ + WAVE_FORMAT_DIGIADPCM = 0x0036, /* DSP Solutions, Inc. */ + WAVE_FORMAT_CONTROL_RES_CR10 = 0x0037, /* Control Resources Limited */ + WAVE_FORMAT_NMS_VBXADPCM = 0x0038, /* Natural MicroSystems */ + WAVE_FORMAT_ROLAND_RDAC = 0x0039, /* Roland */ + WAVE_FORMAT_ECHOSC3 = 0x003A, /* Echo Speech Corporation */ + WAVE_FORMAT_ROCKWELL_ADPCM = 0x003B, /* Rockwell International */ + WAVE_FORMAT_ROCKWELL_DIGITALK = 0x003C, /* Rockwell International */ + WAVE_FORMAT_XEBEC = 0x003D, /* Xebec Multimedia Solutions Limited */ + WAVE_FORMAT_G721_ADPCM = 0x0040, /* Antex Electronics Corporation */ + WAVE_FORMAT_G728_CELP = 0x0041, /* Antex Electronics Corporation */ + WAVE_FORMAT_MSG723 = 0x0042, /* Microsoft Corporation */ + WAVE_FORMAT_MPEG = 0x0050, /* Microsoft Corporation */ + WAVE_FORMAT_RT24 = 0x0052, /* InSoft Inc. */ + WAVE_FORMAT_PAC = 0x0053, /* InSoft Inc. */ + WAVE_FORMAT_MPEGLAYER3 = 0x0055, /* MPEG 3 Layer 1 */ + WAVE_FORMAT_LUCENT_G723 = 0x0059, /* Lucent Technologies */ + WAVE_FORMAT_CIRRUS = 0x0060, /* Cirrus Logic */ + WAVE_FORMAT_ESPCM = 0x0061, /* ESS Technology */ + WAVE_FORMAT_VOXWARE = 0x0062, /* Voxware Inc */ + WAVE_FORMAT_CANOPUS_ATRAC = 0x0063, /* Canopus, Co., Ltd. */ + WAVE_FORMAT_G726_ADPCM = 0x0064, /* APICOM */ + WAVE_FORMAT_G722_ADPCM = 0x0065, /* APICOM */ + WAVE_FORMAT_DSAT = 0x0066, /* Microsoft Corporation */ + WAVE_FORMAT_DSAT_DISPLAY = 0x0067, /* Microsoft Corporation */ + WAVE_FORMAT_VOXWARE_BYTE_ALIGNED = 0x0069, /* Voxware Inc. */ + WAVE_FORMAT_VOXWARE_AC8 = 0x0070, /* Voxware Inc. */ + WAVE_FORMAT_VOXWARE_AC10 = 0x0071, /* Voxware Inc. */ + WAVE_FORMAT_VOXWARE_AC16 = 0x0072, /* Voxware Inc. */ + WAVE_FORMAT_VOXWARE_AC20 = 0x0073, /* Voxware Inc. */ + WAVE_FORMAT_VOXWARE_RT24 = 0x0074, /* Voxware Inc. */ + WAVE_FORMAT_VOXWARE_RT29 = 0x0075, /* Voxware Inc. */ + WAVE_FORMAT_VOXWARE_RT29HW = 0x0076, /* Voxware Inc. */ + WAVE_FORMAT_VOXWARE_VR12 = 0x0077, /* Voxware Inc. */ + WAVE_FORMAT_VOXWARE_VR18 = 0x0078, /* Voxware Inc. */ + WAVE_FORMAT_VOXWARE_TQ40 = 0x0079, /* Voxware Inc. */ + WAVE_FORMAT_SOFTSOUND = 0x0080, /* Softsound, Ltd. */ + WAVE_FORMAT_VOXARE_TQ60 = 0x0081, /* Voxware Inc. */ + WAVE_FORMAT_MSRT24 = 0x0082, /* Microsoft Corporation */ + WAVE_FORMAT_G729A = 0x0083, /* AT&T Laboratories */ + WAVE_FORMAT_MVI_MV12 = 0x0084, /* Motion Pixels */ + WAVE_FORMAT_DF_G726 = 0x0085, /* DataFusion Systems (Pty) (Ltd) */ + WAVE_FORMAT_DF_GSM610 = 0x0086, /* DataFusion Systems (Pty) (Ltd) */ + /* removed because duplicate */ + /* WAVE_FORMAT_ISIAUDIO = 0x0088, */ /* Iterated Systems, Inc. */ + WAVE_FORMAT_ONLIVE = 0x0089, /* OnLive! Technologies, Inc. */ + WAVE_FORMAT_SBC24 = 0x0091, /* Siemens Business Communications Systems */ + WAVE_FORMAT_DOLBY_AC3_SPDIF = 0x0092, /* Sonic Foundry */ + WAVE_FORMAT_ZYXEL_ADPCM = 0x0097, /* ZyXEL Communications, Inc. */ + WAVE_FORMAT_PHILIPS_LPCBB = 0x0098, /* Philips Speech Processing */ + WAVE_FORMAT_PACKED = 0x0099, /* Studer Professional Audio AG */ + WAVE_FORMAT_RHETOREX_ADPCM = 0x0100, /* Rhetorex, Inc. */ + + /* removed because of the following */ + /* WAVE_FORMAT_IRAT = 0x0101,*/ /* BeCubed Software Inc. */ + + /* these three are unofficial */ + IBM_FORMAT_MULAW = 0x0101, /* IBM mu-law format */ + IBM_FORMAT_ALAW = 0x0102, /* IBM a-law format */ + IBM_FORMAT_ADPCM = 0x0103, /* IBM AVC Adaptive Differential PCM format */ + + WAVE_FORMAT_VIVO_G723 = 0x0111, /* Vivo Software */ + WAVE_FORMAT_VIVO_SIREN = 0x0112, /* Vivo Software */ + WAVE_FORMAT_DIGITAL_G723 = 0x0123, /* Digital Equipment Corporation */ + WAVE_FORMAT_CREATIVE_ADPCM = 0x0200, /* Creative Labs, Inc */ + WAVE_FORMAT_CREATIVE_FASTSPEECH8 = 0x0202, /* Creative Labs, Inc */ + WAVE_FORMAT_CREATIVE_FASTSPEECH10 = 0x0203, /* Creative Labs, Inc */ + WAVE_FORMAT_QUARTERDECK = 0x0220, /* Quarterdeck Corporation */ + WAVE_FORMAT_FM_TOWNS_SND = 0x0300, /* Fujitsu Corporation */ + WAVE_FORMAT_BZV_DIGITAL = 0x0400, /* Brooktree Corporation */ + WAVE_FORMAT_VME_VMPCM = 0x0680, /* AT&T Labs, Inc. */ + WAVE_FORMAT_OLIGSM = 0x1000, /* Ing C. Olivetti & C., S.p.A. */ + WAVE_FORMAT_OLIADPCM = 0x1001, /* Ing C. Olivetti & C., S.p.A. */ + WAVE_FORMAT_OLICELP = 0x1002, /* Ing C. Olivetti & C., S.p.A. */ + WAVE_FORMAT_OLISBC = 0x1003, /* Ing C. Olivetti & C., S.p.A. */ + WAVE_FORMAT_OLIOPR = 0x1004, /* Ing C. Olivetti & C., S.p.A. */ + WAVE_FORMAT_LH_CODEC = 0x1100, /* Lernout & Hauspie */ + WAVE_FORMAT_NORRIS = 0x1400, /* Norris Communications, Inc. */ + /* removed because duplicate */ + /* WAVE_FORMAT_ISIAUDIO = 0x1401, */ /* AT&T Labs, Inc. */ + WAVE_FORMAT_SOUNDSPACE_MUSICOMPRESS = 0x1500, /* AT&T Labs, Inc. */ + WAVE_FORMAT_DVM = 0x2000, /* FAST Multimedia AG */ + WAVE_FORMAT_INTERWAV_VSC112 = 0x7150, /* ????? */ + + WAVE_FORMAT_IPP_ITU_G_723_1 = 0x7230, /* Intel Performance Primitives g723 codec. */ + + WAVE_FORMAT_EXTENSIBLE = 0xFFFE +} ; + +typedef struct +{ unsigned short format ; + unsigned short channels ; + unsigned int samplerate ; + unsigned int bytespersec ; + unsigned short blockalign ; + unsigned short bitwidth ; +} MIN_WAV_FMT ; + +typedef struct +{ unsigned short format ; + unsigned short channels ; + unsigned int samplerate ; + unsigned int bytespersec ; + unsigned short blockalign ; + unsigned short bitwidth ; + unsigned short extrabytes ; + unsigned short dummy ; +} WAV_FMT_SIZE20 ; + +typedef struct +{ unsigned short format ; + unsigned short channels ; + unsigned int samplerate ; + unsigned int bytespersec ; + unsigned short blockalign ; + unsigned short bitwidth ; + unsigned short extrabytes ; + unsigned short samplesperblock ; + unsigned short numcoeffs ; + struct + { short coeff1 ; + short coeff2 ; + } coeffs [7] ; +} MS_ADPCM_WAV_FMT ; + +typedef struct +{ unsigned short format ; + unsigned short channels ; + unsigned int samplerate ; + unsigned int bytespersec ; + unsigned short blockalign ; + unsigned short bitwidth ; + unsigned short extrabytes ; + unsigned short samplesperblock ; +} IMA_ADPCM_WAV_FMT ; + +typedef struct +{ unsigned short format ; + unsigned short channels ; + unsigned int samplerate ; + unsigned int bytespersec ; + unsigned short blockalign ; + unsigned short bitwidth ; + unsigned short extrabytes ; + unsigned short auxblocksize ; +} G72x_ADPCM_WAV_FMT ; + + +typedef struct +{ unsigned short format ; + unsigned short channels ; + unsigned int samplerate ; + unsigned int bytespersec ; + unsigned short blockalign ; + unsigned short bitwidth ; + unsigned short extrabytes ; + unsigned short samplesperblock ; +} GSM610_WAV_FMT ; + +typedef struct +{ unsigned int esf_field1 ; + unsigned short esf_field2 ; + unsigned short esf_field3 ; + char esf_field4 [8] ; +} EXT_SUBFORMAT ; + +typedef struct +{ unsigned short format ; + unsigned short channels ; + unsigned int samplerate ; + unsigned int bytespersec ; + unsigned short blockalign ; + unsigned short bitwidth ; + unsigned short extrabytes ; + unsigned short validbits ; + unsigned int channelmask ; + EXT_SUBFORMAT esf ; +} EXTENSIBLE_WAV_FMT ; + +typedef union +{ unsigned short format ; + MIN_WAV_FMT min ; + IMA_ADPCM_WAV_FMT ima ; + MS_ADPCM_WAV_FMT msadpcm ; + G72x_ADPCM_WAV_FMT g72x ; + EXTENSIBLE_WAV_FMT ext ; + GSM610_WAV_FMT gsm610 ; + WAV_FMT_SIZE20 size20 ; + char padding [512] ; +} WAV_FMT ; + +typedef struct +{ int frames ; +} FACT_CHUNK ; + +typedef struct +{ /* Set to true when 'fmt ' chunk is ambiguous.*/ + int fmt_is_broken ; + WAV_FMT wav_fmt ; +} WAV_PRIVATE ; + +#define WAV_W64_GSM610_BLOCKSIZE 65 +#define WAV_W64_GSM610_SAMPLES 320 + +/*------------------------------------------------------------------------------------ +** Functions defined in wav_ms_adpcm.c +*/ + +#define MSADPCM_ADAPT_COEFF_COUNT 7 + +void msadpcm_write_adapt_coeffs (SF_PRIVATE *psf) ; + +/*------------------------------------------------------------------------------------ +** Functions defined in wav_w64.c +*/ + +int wav_w64_srate2blocksize (int srate_chan_product) ; +char const* wav_w64_format_str (int k) ; +int wav_w64_read_fmt_chunk (SF_PRIVATE *psf, int fmtsize) ; +void wavex_write_guid (SF_PRIVATE *psf, const EXT_SUBFORMAT * subformat) ; +void wav_w64_analyze (SF_PRIVATE *psf) ; + +#endif +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 877fde12-9be3-4a31-8a5a-fdae39958613 +*/ diff --git a/src/wve.c b/src/wve.c new file mode 100644 index 00000000..b4bde602 --- /dev/null +++ b/src/wve.c @@ -0,0 +1,209 @@ +/* +** Copyright (C) 2002-2007 Erik de Castro Lopo +** Copyright (C) 2007 Reuben Thomas +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include +#include + +#include "sndfile.h" +#include "sfendian.h" +#include "common.h" + +/*------------------------------------------------------------------------------ +** Macros to handle big/little endian issues, and other magic numbers. +*/ + +#define ALAW_MARKER MAKE_MARKER ('A', 'L', 'a', 'w') +#define SOUN_MARKER MAKE_MARKER ('S', 'o', 'u', 'n') +#define DFIL_MARKER MAKE_MARKER ('d', 'F', 'i', 'l') +#define ESSN_MARKER MAKE_MARKER ('e', '*', '*', '\0') +#define PSION_VERSION ((unsigned short) 3856) +#define PSION_DATAOFFSET 0x20 + +/*------------------------------------------------------------------------------ +** Private static functions. +*/ + +static int wve_read_header (SF_PRIVATE *psf) ; +static int wve_write_header (SF_PRIVATE *psf, int calc_length) ; +static int wve_close (SF_PRIVATE *psf) ; + +/*------------------------------------------------------------------------------ +** Public function. +*/ + +int +wve_open (SF_PRIVATE *psf) +{ int error = 0 ; + + if (psf->is_pipe) + return SFE_WVE_NO_PIPE ; + + if (psf->mode == SFM_READ || (psf->mode == SFM_RDWR && psf->filelength > 0)) + { if ((error = wve_read_header (psf))) + return error ; + } ; + + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + { if ((psf->sf.format & SF_FORMAT_TYPEMASK) != SF_FORMAT_WVE) + return SFE_BAD_OPEN_FORMAT ; + + psf->endian = SF_ENDIAN_BIG ; + + if ((error = wve_write_header (psf, SF_FALSE))) + return error ; + + psf->write_header = wve_write_header ; + } ; + + psf->blockwidth = psf->bytewidth * psf->sf.channels ; + + psf->container_close = wve_close ; + + error = alaw_init (psf) ; + + return error ; +} /* wve_open */ + +/*------------------------------------------------------------------------------ +*/ + +static int +wve_read_header (SF_PRIVATE *psf) +{ int marker ; + unsigned short version, padding, repeats, trash ; + unsigned datalength ; + + /* Set position to start of file to begin reading header. */ + psf_binheader_readf (psf, "pm", 0, &marker) ; + if (marker != ALAW_MARKER) + { psf_log_printf (psf, "Could not find '%M'\n", ALAW_MARKER) ; + return SFE_WVE_NOT_WVE ; + } ; + + psf_binheader_readf (psf, "m", &marker) ; + if (marker != SOUN_MARKER) + { psf_log_printf (psf, "Could not find '%M'\n", SOUN_MARKER) ; + return SFE_WVE_NOT_WVE ; + } ; + + psf_binheader_readf (psf, "m", &marker) ; + if (marker != DFIL_MARKER) + { psf_log_printf (psf, "Could not find '%M'\n", DFIL_MARKER) ; + return SFE_WVE_NOT_WVE ; + } ; + + psf_binheader_readf (psf, "m", &marker) ; + if (marker != ESSN_MARKER) + { psf_log_printf (psf, "Could not find '%M'\n", ESSN_MARKER) ; + return SFE_WVE_NOT_WVE ; + } ; + + psf_binheader_readf (psf, "E2", &version) ; + + psf_log_printf (psf, "Psion Palmtop Alaw (.wve)\n" + " Sample Rate : 8000\n" + " Channels : 1\n" + " Encoding : A-law\n") ; + + if (version != PSION_VERSION) + psf_log_printf (psf, "Psion version %d should be %d\n", version, PSION_VERSION) ; + + psf_binheader_readf (psf, "E4", &datalength) ; + psf->dataoffset = PSION_DATAOFFSET ; + if (datalength != psf->filelength - psf->dataoffset) + { psf->datalength = psf->filelength - psf->dataoffset ; + psf_log_printf (psf, "Data length %d should be %D\n", datalength, psf->datalength) ; + } + else + psf->datalength = datalength ; + + psf_binheader_readf (psf, "E22222", &padding, &repeats, &trash, &trash, &trash) ; + + psf->sf.format = SF_FORMAT_WVE | SF_FORMAT_ALAW ; + psf->sf.samplerate = 8000 ; + psf->sf.frames = psf->datalength ; + psf->sf.channels = 1 ; + + return SFE_NO_ERROR ; +} /* wve_read_header */ + +/*------------------------------------------------------------------------------ +*/ + +static int +wve_write_header (SF_PRIVATE *psf, int calc_length) +{ sf_count_t current ; + unsigned datalen ; + + current = psf_ftell (psf) ; + + if (calc_length) + { psf->filelength = psf_get_filelen (psf) ; + + psf->datalength = psf->filelength - psf->dataoffset ; + if (psf->dataend) + psf->datalength -= psf->filelength - psf->dataend ; + + psf->sf.frames = psf->datalength / (psf->bytewidth * psf->sf.channels) ; + } ; + + /* Reset the current header length to zero. */ + psf->header [0] = 0 ; + psf->headindex = 0 ; + psf_fseek (psf, 0, SEEK_SET) ; + + /* Write header. */ + datalen = psf->datalength ; + psf_binheader_writef (psf, "Emmmm", ALAW_MARKER, SOUN_MARKER, DFIL_MARKER, ESSN_MARKER) ; + psf_binheader_writef (psf, "E2422222", PSION_VERSION, datalen, 0, 0, 0, 0, 0) ; + psf_fwrite (psf->header, psf->headindex, 1, psf) ; + + if (psf->sf.channels != 1) + return SFE_CHANNEL_COUNT ; + + if (psf->error) + return psf->error ; + + psf->dataoffset = psf->headindex ; + + if (current > 0) + psf_fseek (psf, current, SEEK_SET) ; + + return psf->error ; +} /* wve_write_header */ + +/*------------------------------------------------------------------------------ +*/ + +static int +wve_close (SF_PRIVATE *psf) +{ + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + { /* Now we know for certain the length of the file we can re-write + ** the header. + */ + wve_write_header (psf, SF_TRUE) ; + } ; + + return 0 ; +} /* wve_close */ diff --git a/src/xi.c b/src/xi.c new file mode 100644 index 00000000..a4b27157 --- /dev/null +++ b/src/xi.c @@ -0,0 +1,1200 @@ +/* +** Copyright (C) 2003-2006 Erik de Castro Lopo +** +** This program 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 program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include +#include +#include + +#include "sndfile.h" +#include "sfendian.h" +#include "common.h" +#include "float_cast.h" + +#define MAX_XI_SAMPLES 16 + +/*------------------------------------------------------------------------------ +** Private static functions and tyepdefs. +*/ + +typedef struct +{ /* Warning, this filename is NOT nul terminated. */ + char filename [22] ; + char software [20] ; + char sample_name [22] ; + + int loop_begin, loop_end ; + int sample_flags ; + + /* Data for encoder and decoder. */ + short last_16 ; +} XI_PRIVATE ; + +static int xi_close (SF_PRIVATE *psf) ; +static int xi_write_header (SF_PRIVATE *psf, int calc_length) ; +static int xi_read_header (SF_PRIVATE *psf) ; +static int dpcm_init (SF_PRIVATE *psf) ; + + +static sf_count_t dpcm_seek (SF_PRIVATE *psf, int mode, sf_count_t offset) ; + +/*------------------------------------------------------------------------------ +** Public function. +*/ + +int +xi_open (SF_PRIVATE *psf) +{ XI_PRIVATE *pxi ; + int subformat, error = 0 ; + + if (psf->is_pipe) + return SFE_XI_NO_PIPE ; + + if (psf->codec_data) + pxi = psf->codec_data ; + else if ((pxi = calloc (1, sizeof (XI_PRIVATE))) == NULL) + return SFE_MALLOC_FAILED ; + + psf->codec_data = pxi ; + + if (psf->mode == SFM_READ || (psf->mode == SFM_RDWR && psf->filelength > 0)) + { if ((error = xi_read_header (psf))) + return error ; + } ; + + subformat = psf->sf.format & SF_FORMAT_SUBMASK ; + + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + { if ((psf->sf.format & SF_FORMAT_TYPEMASK) != SF_FORMAT_XI) + return SFE_BAD_OPEN_FORMAT ; + + psf->endian = SF_ENDIAN_LITTLE ; + psf->sf.channels = 1 ; /* Always mono */ + psf->sf.samplerate = 44100 ; /* Always */ + + /* Set up default instrument and software name. */ + memcpy (pxi->filename, "Default Name ", sizeof (pxi->filename)) ; + memcpy (pxi->software, PACKAGE "-" VERSION " ", sizeof (pxi->software)) ; + + memset (pxi->sample_name, 0, sizeof (pxi->sample_name)) ; + LSF_SNPRINTF (pxi->sample_name, sizeof (pxi->sample_name), "%s", "Sample #1") ; + + pxi->sample_flags = (subformat == SF_FORMAT_DPCM_16) ? 16 : 0 ; + + if (xi_write_header (psf, SF_FALSE)) + return psf->error ; + + psf->write_header = xi_write_header ; + } ; + + psf->container_close = xi_close ; + psf->seek = dpcm_seek ; + + psf->sf.seekable = SF_FALSE ; + + psf->blockwidth = psf->bytewidth * psf->sf.channels ; + + switch (subformat) + { case SF_FORMAT_DPCM_8 : /* 8-bit differential PCM. */ + case SF_FORMAT_DPCM_16 : /* 16-bit differential PCM. */ + error = dpcm_init (psf) ; + break ; + + default : break ; + } ; + + return error ; +} /* xi_open */ + +/*------------------------------------------------------------------------------ +*/ + +static int +xi_close (SF_PRIVATE * UNUSED (psf)) +{ + return 0 ; +} /* xi_close */ + +/*============================================================================== +*/ + +static sf_count_t dpcm_read_dsc2s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ; +static sf_count_t dpcm_read_dsc2i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ; +static sf_count_t dpcm_read_dsc2f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ; +static sf_count_t dpcm_read_dsc2d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ; + +static sf_count_t dpcm_write_s2dsc (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ; +static sf_count_t dpcm_write_i2dsc (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ; +static sf_count_t dpcm_write_f2dsc (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ; +static sf_count_t dpcm_write_d2dsc (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ; + +static sf_count_t dpcm_read_dles2s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ; +static sf_count_t dpcm_read_dles2i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ; +static sf_count_t dpcm_read_dles2f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ; +static sf_count_t dpcm_read_dles2d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ; + +static sf_count_t dpcm_write_s2dles (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ; +static sf_count_t dpcm_write_i2dles (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ; +static sf_count_t dpcm_write_f2dles (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ; +static sf_count_t dpcm_write_d2dles (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ; + +static int +dpcm_init (SF_PRIVATE *psf) +{ if (psf->bytewidth == 0 || psf->sf.channels == 0) + return SFE_INTERNAL ; + + psf->blockwidth = psf->bytewidth * psf->sf.channels ; + + if (psf->mode == SFM_READ || psf->mode == SFM_RDWR) + { switch (psf->bytewidth) + { case 1 : + psf->read_short = dpcm_read_dsc2s ; + psf->read_int = dpcm_read_dsc2i ; + psf->read_float = dpcm_read_dsc2f ; + psf->read_double = dpcm_read_dsc2d ; + break ; + case 2 : + psf->read_short = dpcm_read_dles2s ; + psf->read_int = dpcm_read_dles2i ; + psf->read_float = dpcm_read_dles2f ; + psf->read_double = dpcm_read_dles2d ; + break ; + default : + psf_log_printf (psf, "dpcm_init() returning SFE_UNIMPLEMENTED\n") ; + return SFE_UNIMPLEMENTED ; + } ; + } ; + + if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR) + { switch (psf->bytewidth) + { case 1 : + psf->write_short = dpcm_write_s2dsc ; + psf->write_int = dpcm_write_i2dsc ; + psf->write_float = dpcm_write_f2dsc ; + psf->write_double = dpcm_write_d2dsc ; + break ; + case 2 : + psf->write_short = dpcm_write_s2dles ; + psf->write_int = dpcm_write_i2dles ; + psf->write_float = dpcm_write_f2dles ; + psf->write_double = dpcm_write_d2dles ; + break ; + default : + psf_log_printf (psf, "dpcm_init() returning SFE_UNIMPLEMENTED\n") ; + return SFE_UNIMPLEMENTED ; + } ; + } ; + + psf->filelength = psf_get_filelen (psf) ; + psf->datalength = (psf->dataend) ? psf->dataend - psf->dataoffset : + psf->filelength - psf->dataoffset ; + psf->sf.frames = psf->datalength / psf->blockwidth ; + + return 0 ; +} /* dpcm_init */ + +/*============================================================================== +*/ + +static sf_count_t +dpcm_seek (SF_PRIVATE *psf, int mode, sf_count_t offset) +{ XI_PRIVATE *pxi ; + int total, bufferlen, len ; + + if ((pxi = psf->codec_data) == NULL) + return SFE_INTERNAL ; + + if (psf->datalength < 0 || psf->dataoffset < 0) + { psf->error = SFE_BAD_SEEK ; + return PSF_SEEK_ERROR ; + } ; + + if (offset == 0) + { psf_fseek (psf, psf->dataoffset, SEEK_SET) ; + pxi->last_16 = 0 ; + return 0 ; + } ; + + if (offset < 0 || offset > psf->sf.frames) + { psf->error = SFE_BAD_SEEK ; + return PSF_SEEK_ERROR ; + } ; + + if (mode != SFM_READ) + { /* What to do about write??? */ + psf->error = SFE_BAD_SEEK ; + return PSF_SEEK_ERROR ; + } ; + + psf_fseek (psf, psf->dataoffset, SEEK_SET) ; + + if ((psf->sf.format & SF_FORMAT_SUBMASK) == SF_FORMAT_DPCM_16) + { total = offset ; + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + while (total > 0) + { len = (total > bufferlen) ? bufferlen : total ; + total -= dpcm_read_dles2s (psf, psf->u.sbuf, len) ; + } ; + } + else + { total = offset ; + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + while (total > 0) + { len = (total > bufferlen) ? bufferlen : total ; + total -= dpcm_read_dsc2s (psf, psf->u.sbuf, len) ; + } ; + } ; + + return offset ; +} /* dpcm_seek */ + + +static int +xi_write_header (SF_PRIVATE *psf, int UNUSED (calc_length)) +{ XI_PRIVATE *pxi ; + sf_count_t current ; + const char *string ; + + if ((pxi = psf->codec_data) == NULL) + return SFE_INTERNAL ; + + current = psf_ftell (psf) ; + + /* Reset the current header length to zero. */ + psf->header [0] = 0 ; + psf->headindex = 0 ; + psf_fseek (psf, 0, SEEK_SET) ; + + string = "Extended Instrument: " ; + psf_binheader_writef (psf, "b", string, strlen (string)) ; + psf_binheader_writef (psf, "b1", pxi->filename, sizeof (pxi->filename), 0x1A) ; + + /* Write software version and two byte XI version. */ + psf_binheader_writef (psf, "eb2", pxi->software, sizeof (pxi->software), (1 << 8) + 2) ; + + /* + ** Jump note numbers (96), volume envelope (48), pan envelope (48), + ** volume points (1), pan points (1) + */ + psf_binheader_writef (psf, "z", (size_t) (96 + 48 + 48 + 1 + 1)) ; + + /* Jump volume loop (3 bytes), pan loop (3), envelope flags (3), vibrato (3) + ** fade out (2), 22 unknown bytes, and then write sample_count (2 bytes). + */ + psf_binheader_writef (psf, "ez2z2", (size_t) (4 * 3), 0x1234, make_size_t (22), 1) ; + + pxi->loop_begin = 0 ; + pxi->loop_end = 0 ; + + psf_binheader_writef (psf, "et844", psf->sf.frames, pxi->loop_begin, pxi->loop_end) ; + + /* volume, fine tune, flags, pan, note, namelen */ + psf_binheader_writef (psf, "111111", 128, 0, pxi->sample_flags, 128, 0, strlen (pxi->sample_name)) ; + + psf_binheader_writef (psf, "b", pxi->sample_name, sizeof (pxi->sample_name)) ; + + + + + + /* Header construction complete so write it out. */ + psf_fwrite (psf->header, psf->headindex, 1, psf) ; + + if (psf->error) + return psf->error ; + + psf->dataoffset = psf->headindex ; + + if (current > 0) + psf_fseek (psf, current, SEEK_SET) ; + + return psf->error ; +} /* xi_write_header */ + +static int +xi_read_header (SF_PRIVATE *psf) +{ char buffer [64], name [32] ; + short version, fade_out, sample_count ; + int k, loop_begin, loop_end ; + int sample_sizes [MAX_XI_SAMPLES] ; + + psf_binheader_readf (psf, "pb", 0, buffer, 21) ; + + memset (sample_sizes, 0, sizeof (sample_sizes)) ; + + buffer [20] = 0 ; + if (strcmp (buffer, "Extended Instrument:") != 0) + return SFE_XI_BAD_HEADER ; + + memset (buffer, 0, sizeof (buffer)) ; + psf_binheader_readf (psf, "b", buffer, 23) ; + + if (buffer [22] != 0x1A) + return SFE_XI_BAD_HEADER ; + + buffer [22] = 0 ; + psf_log_printf (psf, "Extended Instrument : %s\n", buffer) ; + + psf_binheader_readf (psf, "be2", buffer, 20, &version) ; + buffer [19] = 0 ; + psf_log_printf (psf, "Software : %s\nVersion : %d.%02d\n", buffer, version / 256, version % 256) ; + + /* Jump note numbers (96), volume envelope (48), pan envelope (48), + ** volume points (1), pan points (1) + */ + psf_binheader_readf (psf, "j", 96 + 48 + 48 + 1 + 1) ; + + psf_binheader_readf (psf, "b", buffer, 12) ; + psf_log_printf (psf, "Volume Loop\n sustain : %u\n begin : %u\n end : %u\n", + buffer [0], buffer [1], buffer [2]) ; + psf_log_printf (psf, "Pan Loop\n sustain : %u\n begin : %u\n end : %u\n", + buffer [3], buffer [4], buffer [5]) ; + psf_log_printf (psf, "Envelope Flags\n volume : 0x%X\n pan : 0x%X\n", + buffer [6] & 0xFF, buffer [7] & 0xFF) ; + + psf_log_printf (psf, "Vibrato\n type : %u\n sweep : %u\n depth : %u\n rate : %u\n", + buffer [8], buffer [9], buffer [10], buffer [11]) ; + + /* + ** Read fade_out then jump reserved (2 bytes) and ???? (20 bytes) and + ** sample_count. + */ + psf_binheader_readf (psf, "e2j2", &fade_out, 2 + 20, &sample_count) ; + psf_log_printf (psf, "Fade out : %d\n", fade_out) ; + + /* XI file can contain up to 16 samples. */ + if (sample_count > MAX_XI_SAMPLES) + return SFE_XI_EXCESS_SAMPLES ; + + if (psf->instrument == NULL && (psf->instrument = psf_instrument_alloc ()) == NULL) + return SFE_MALLOC_FAILED ; + + /* Log all data for each sample. */ + for (k = 0 ; k < sample_count ; k++) + { psf_binheader_readf (psf, "e444", &(sample_sizes [k]), &loop_begin, &loop_end) ; + + /* Read 5 know bytes, 1 unknown byte and 22 name bytes. */ + psf_binheader_readf (psf, "bb", buffer, 6, name, 22) ; + name [21] = 0 ; + + psf_log_printf (psf, "Sample #%d\n name : %s\n", k + 1, name) ; + + psf_log_printf (psf, " size : %d\n", sample_sizes [k]) ; + + + + psf_log_printf (psf, " loop\n begin : %d\n end : %d\n", loop_begin, loop_end) ; + + psf_log_printf (psf, " volume : %u\n f. tune : %d\n flags : 0x%02X ", + buffer [0] & 0xFF, buffer [1] & 0xFF, buffer [2] & 0xFF) ; + + psf_log_printf (psf, " (") ; + if (buffer [2] & 1) + psf_log_printf (psf, " Loop") ; + if (buffer [2] & 2) + psf_log_printf (psf, " PingPong") ; + psf_log_printf (psf, (buffer [2] & 16) ? " 16bit" : " 8bit") ; + psf_log_printf (psf, " )\n") ; + + psf_log_printf (psf, " pan : %u\n note : %d\n namelen : %d\n", + buffer [3] & 0xFF, buffer [4], buffer [5]) ; + + if (k != 0) + continue ; + + if (buffer [2] & 16) + { psf->sf.format = SF_FORMAT_XI | SF_FORMAT_DPCM_16 ; + psf->bytewidth = 2 ; + } + else + { psf->sf.format = SF_FORMAT_XI | SF_FORMAT_DPCM_8 ; + psf->bytewidth = 1 ; + } ; + } ; + + while (sample_count > 1 && sample_sizes [sample_count - 1] == 0) + sample_count -- ; + + /* Currently, we can only handle 1 sample per file. */ + + if (sample_count > 2) + { psf_log_printf (psf, "*** Sample count is less than 16 but more than 1.\n") ; + psf_log_printf (psf, " sample count : %d sample_sizes [%d] : %d\n", + sample_count, sample_count - 1, sample_sizes [sample_count - 1]) ; + return SFE_XI_EXCESS_SAMPLES ; + } ; + + psf->dataoffset = psf_fseek (psf, 0, SEEK_CUR) ; + psf_log_printf (psf, "Data Offset : %D\n", psf->dataoffset) ; + + psf->datalength = sample_sizes [0] ; + + if (psf->dataoffset + psf->datalength > psf->filelength) + { psf_log_printf (psf, "*** File seems to be truncated. Should be at least %D bytes long.\n", + psf->dataoffset + sample_sizes [0]) ; + psf->datalength = psf->filelength - psf->dataoffset ; + } ; + + if (psf_fseek (psf, psf->dataoffset, SEEK_SET) != psf->dataoffset) + return SFE_BAD_SEEK ; + + psf->endian = SF_ENDIAN_LITTLE ; + psf->sf.channels = 1 ; /* Always mono */ + psf->sf.samplerate = 44100 ; /* Always */ + + psf->blockwidth = psf->sf.channels * psf->bytewidth ; + + if (! psf->sf.frames && psf->blockwidth) + psf->sf.frames = (psf->filelength - psf->dataoffset) / psf->blockwidth ; + + psf->instrument->basenote = 0 ; + psf->instrument->gain = 1 ; + psf->instrument->velocity_lo = psf->instrument->key_lo = 0 ; + psf->instrument->velocity_hi = psf->instrument->key_hi = 127 ; + + return 0 ; +} /* xi_read_header */ + +/*============================================================================== +*/ + +static void dsc2s_array (XI_PRIVATE *pxi, signed char *src, int count, short *dest) ; +static void dsc2i_array (XI_PRIVATE *pxi, signed char *src, int count, int *dest) ; +static void dsc2f_array (XI_PRIVATE *pxi, signed char *src, int count, float *dest, float normfact) ; +static void dsc2d_array (XI_PRIVATE *pxi, signed char *src, int count, double *dest, double normfact) ; + +static void dles2s_array (XI_PRIVATE *pxi, short *src, int count, short *dest) ; +static void dles2i_array (XI_PRIVATE *pxi, short *src, int count, int *dest) ; +static void dles2f_array (XI_PRIVATE *pxi, short *src, int count, float *dest, float normfact) ; +static void dles2d_array (XI_PRIVATE *pxi, short *src, int count, double *dest, double normfact) ; + +static sf_count_t +dpcm_read_dsc2s (SF_PRIVATE *psf, short *ptr, sf_count_t len) +{ XI_PRIVATE *pxi ; + int bufferlen, readcount ; + sf_count_t total = 0 ; + + if ((pxi = psf->codec_data) == NULL) + return 0 ; + + bufferlen = ARRAY_LEN (psf->u.ucbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.scbuf, sizeof (signed char), bufferlen, psf) ; + dsc2s_array (pxi, psf->u.scbuf, readcount, ptr + total) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* dpcm_read_dsc2s */ + +static sf_count_t +dpcm_read_dsc2i (SF_PRIVATE *psf, int *ptr, sf_count_t len) +{ XI_PRIVATE *pxi ; + int bufferlen, readcount ; + sf_count_t total = 0 ; + + if ((pxi = psf->codec_data) == NULL) + return 0 ; + + bufferlen = ARRAY_LEN (psf->u.ucbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.scbuf, sizeof (signed char), bufferlen, psf) ; + dsc2i_array (pxi, psf->u.scbuf, readcount, ptr + total) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* dpcm_read_dsc2i */ + +static sf_count_t +dpcm_read_dsc2f (SF_PRIVATE *psf, float *ptr, sf_count_t len) +{ XI_PRIVATE *pxi ; + int bufferlen, readcount ; + sf_count_t total = 0 ; + float normfact ; + + if ((pxi = psf->codec_data) == NULL) + return 0 ; + + normfact = (psf->norm_float == SF_TRUE) ? 1.0 / ((float) 0x80) : 1.0 ; + + bufferlen = ARRAY_LEN (psf->u.ucbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.scbuf, sizeof (signed char), bufferlen, psf) ; + dsc2f_array (pxi, psf->u.scbuf, readcount, ptr + total, normfact) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* dpcm_read_dsc2f */ + +static sf_count_t +dpcm_read_dsc2d (SF_PRIVATE *psf, double *ptr, sf_count_t len) +{ XI_PRIVATE *pxi ; + int bufferlen, readcount ; + sf_count_t total = 0 ; + double normfact ; + + if ((pxi = psf->codec_data) == NULL) + return 0 ; + + normfact = (psf->norm_double == SF_TRUE) ? 1.0 / ((double) 0x80) : 1.0 ; + + bufferlen = ARRAY_LEN (psf->u.ucbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.scbuf, sizeof (signed char), bufferlen, psf) ; + dsc2d_array (pxi, psf->u.scbuf, readcount, ptr + total, normfact) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* dpcm_read_dsc2d */ + +/*------------------------------------------------------------------------------ +*/ + +static sf_count_t +dpcm_read_dles2s (SF_PRIVATE *psf, short *ptr, sf_count_t len) +{ XI_PRIVATE *pxi ; + int bufferlen, readcount ; + sf_count_t total = 0 ; + + if ((pxi = psf->codec_data) == NULL) + return 0 ; + + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.sbuf, sizeof (short), bufferlen, psf) ; + dles2s_array (pxi, psf->u.sbuf, readcount, ptr + total) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* dpcm_read_dles2s */ + +static sf_count_t +dpcm_read_dles2i (SF_PRIVATE *psf, int *ptr, sf_count_t len) +{ XI_PRIVATE *pxi ; + int bufferlen, readcount ; + sf_count_t total = 0 ; + + if ((pxi = psf->codec_data) == NULL) + return 0 ; + + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.sbuf, sizeof (short), bufferlen, psf) ; + dles2i_array (pxi, psf->u.sbuf, readcount, ptr + total) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* dpcm_read_dles2i */ + +static sf_count_t +dpcm_read_dles2f (SF_PRIVATE *psf, float *ptr, sf_count_t len) +{ XI_PRIVATE *pxi ; + int bufferlen, readcount ; + sf_count_t total = 0 ; + float normfact ; + + if ((pxi = psf->codec_data) == NULL) + return 0 ; + + normfact = (psf->norm_float == SF_TRUE) ? 1.0 / ((float) 0x8000) : 1.0 ; + + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.sbuf, sizeof (short), bufferlen, psf) ; + dles2f_array (pxi, psf->u.sbuf, readcount, ptr + total, normfact) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* dpcm_read_dles2f */ + +static sf_count_t +dpcm_read_dles2d (SF_PRIVATE *psf, double *ptr, sf_count_t len) +{ XI_PRIVATE *pxi ; + int bufferlen, readcount ; + sf_count_t total = 0 ; + double normfact ; + + if ((pxi = psf->codec_data) == NULL) + return 0 ; + + normfact = (psf->norm_double == SF_TRUE) ? 1.0 / ((double) 0x8000) : 1.0 ; + + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + readcount = psf_fread (psf->u.sbuf, sizeof (short), bufferlen, psf) ; + dles2d_array (pxi, psf->u.sbuf, readcount, ptr + total, normfact) ; + total += readcount ; + if (readcount < bufferlen) + break ; + len -= readcount ; + } ; + + return total ; +} /* dpcm_read_dles2d */ + +/*============================================================================== +*/ + +static void s2dsc_array (XI_PRIVATE *pxi, const short *src, signed char *dest, int count) ; +static void i2dsc_array (XI_PRIVATE *pxi, const int *src, signed char *dest, int count) ; +static void f2dsc_array (XI_PRIVATE *pxi, const float *src, signed char *dest, int count, float normfact) ; +static void d2dsc_array (XI_PRIVATE *pxi, const double *src, signed char *dest, int count, double normfact) ; + +static void s2dles_array (XI_PRIVATE *pxi, const short *src, short *dest, int count) ; +static void i2dles_array (XI_PRIVATE *pxi, const int *src, short *dest, int count) ; +static void f2dles_array (XI_PRIVATE *pxi, const float *src, short *dest, int count, float normfact) ; +static void d2dles_array (XI_PRIVATE *pxi, const double *src, short *dest, int count, double normfact) ; + + +static sf_count_t +dpcm_write_s2dsc (SF_PRIVATE *psf, const short *ptr, sf_count_t len) +{ XI_PRIVATE *pxi ; + int bufferlen, writecount ; + sf_count_t total = 0 ; + + if ((pxi = psf->codec_data) == NULL) + return 0 ; + + bufferlen = ARRAY_LEN (psf->u.ucbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + s2dsc_array (pxi, ptr + total, psf->u.scbuf, bufferlen) ; + writecount = psf_fwrite (psf->u.scbuf, sizeof (signed char), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* dpcm_write_s2dsc */ + +static sf_count_t +dpcm_write_i2dsc (SF_PRIVATE *psf, const int *ptr, sf_count_t len) +{ XI_PRIVATE *pxi ; + int bufferlen, writecount ; + sf_count_t total = 0 ; + + if ((pxi = psf->codec_data) == NULL) + return 0 ; + + bufferlen = ARRAY_LEN (psf->u.ucbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + i2dsc_array (pxi, ptr + total, psf->u.scbuf, bufferlen) ; + writecount = psf_fwrite (psf->u.scbuf, sizeof (signed char), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* dpcm_write_i2dsc */ + +static sf_count_t +dpcm_write_f2dsc (SF_PRIVATE *psf, const float *ptr, sf_count_t len) +{ XI_PRIVATE *pxi ; + int bufferlen, writecount ; + sf_count_t total = 0 ; + float normfact ; + + if ((pxi = psf->codec_data) == NULL) + return 0 ; + + normfact = (psf->norm_float == SF_TRUE) ? (1.0 * 0x7F) : 1.0 ; + + bufferlen = ARRAY_LEN (psf->u.ucbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + f2dsc_array (pxi, ptr + total, psf->u.scbuf, bufferlen, normfact) ; + writecount = psf_fwrite (psf->u.scbuf, sizeof (signed char), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* dpcm_write_f2dsc */ + +static sf_count_t +dpcm_write_d2dsc (SF_PRIVATE *psf, const double *ptr, sf_count_t len) +{ XI_PRIVATE *pxi ; + int bufferlen, writecount ; + sf_count_t total = 0 ; + double normfact ; + + if ((pxi = psf->codec_data) == NULL) + return 0 ; + + normfact = (psf->norm_double == SF_TRUE) ? (1.0 * 0x7F) : 1.0 ; + + bufferlen = ARRAY_LEN (psf->u.ucbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + d2dsc_array (pxi, ptr + total, psf->u.scbuf, bufferlen, normfact) ; + writecount = psf_fwrite (psf->u.scbuf, sizeof (signed char), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* dpcm_write_d2dsc */ + + +static sf_count_t +dpcm_write_s2dles (SF_PRIVATE *psf, const short *ptr, sf_count_t len) +{ XI_PRIVATE *pxi ; + int bufferlen, writecount ; + sf_count_t total = 0 ; + + if ((pxi = psf->codec_data) == NULL) + return 0 ; + + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + s2dles_array (pxi, ptr + total, psf->u.sbuf, bufferlen) ; + writecount = psf_fwrite (psf->u.sbuf, sizeof (short), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* dpcm_write_s2dles */ + +static sf_count_t +dpcm_write_i2dles (SF_PRIVATE *psf, const int *ptr, sf_count_t len) +{ XI_PRIVATE *pxi ; + int bufferlen, writecount ; + sf_count_t total = 0 ; + + if ((pxi = psf->codec_data) == NULL) + return 0 ; + + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + i2dles_array (pxi, ptr + total, psf->u.sbuf, bufferlen) ; + writecount = psf_fwrite (psf->u.sbuf, sizeof (short), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* dpcm_write_i2dles */ + +static sf_count_t +dpcm_write_f2dles (SF_PRIVATE *psf, const float *ptr, sf_count_t len) +{ XI_PRIVATE *pxi ; + int bufferlen, writecount ; + sf_count_t total = 0 ; + float normfact ; + + if ((pxi = psf->codec_data) == NULL) + return 0 ; + + normfact = (psf->norm_float == SF_TRUE) ? (1.0 * 0x7FFF) : 1.0 ; + + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + f2dles_array (pxi, ptr + total, psf->u.sbuf, bufferlen, normfact) ; + writecount = psf_fwrite (psf->u.sbuf, sizeof (short), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* dpcm_write_f2dles */ + +static sf_count_t +dpcm_write_d2dles (SF_PRIVATE *psf, const double *ptr, sf_count_t len) +{ XI_PRIVATE *pxi ; + int bufferlen, writecount ; + sf_count_t total = 0 ; + double normfact ; + + if ((pxi = psf->codec_data) == NULL) + return 0 ; + + normfact = (psf->norm_double == SF_TRUE) ? (1.0 * 0x7FFF) : 1.0 ; + + bufferlen = ARRAY_LEN (psf->u.sbuf) ; + + while (len > 0) + { if (len < bufferlen) + bufferlen = (int) len ; + d2dles_array (pxi, ptr + total, psf->u.sbuf, bufferlen, normfact) ; + writecount = psf_fwrite (psf->u.sbuf, sizeof (short), bufferlen, psf) ; + total += writecount ; + if (writecount < bufferlen) + break ; + len -= writecount ; + } ; + + return total ; +} /* dpcm_write_d2dles */ + + +/*============================================================================== +*/ + +static void +dsc2s_array (XI_PRIVATE *pxi, signed char *src, int count, short *dest) +{ signed char last_val ; + int k ; + + last_val = pxi->last_16 >> 8 ; + + for (k = 0 ; k < count ; k++) + { last_val += src [k] ; + dest [k] = last_val << 8 ; + } ; + + pxi->last_16 = last_val << 8 ; +} /* dsc2s_array */ + +static void +dsc2i_array (XI_PRIVATE *pxi, signed char *src, int count, int *dest) +{ signed char last_val ; + int k ; + + last_val = pxi->last_16 >> 8 ; + + for (k = 0 ; k < count ; k++) + { last_val += src [k] ; + dest [k] = last_val << 24 ; + } ; + + pxi->last_16 = last_val << 8 ; +} /* dsc2i_array */ + +static void +dsc2f_array (XI_PRIVATE *pxi, signed char *src, int count, float *dest, float normfact) +{ signed char last_val ; + int k ; + + last_val = pxi->last_16 >> 8 ; + + for (k = 0 ; k < count ; k++) + { last_val += src [k] ; + dest [k] = last_val * normfact ; + } ; + + pxi->last_16 = last_val << 8 ; +} /* dsc2f_array */ + +static void +dsc2d_array (XI_PRIVATE *pxi, signed char *src, int count, double *dest, double normfact) +{ signed char last_val ; + int k ; + + last_val = pxi->last_16 >> 8 ; + + for (k = 0 ; k < count ; k++) + { last_val += src [k] ; + dest [k] = last_val * normfact ; + } ; + + pxi->last_16 = last_val << 8 ; +} /* dsc2d_array */ + +/*------------------------------------------------------------------------------ +*/ + +static void +s2dsc_array (XI_PRIVATE *pxi, const short *src, signed char *dest, int count) +{ signed char last_val, current ; + int k ; + + last_val = pxi->last_16 >> 8 ; + + for (k = 0 ; k < count ; k++) + { current = src [k] >> 8 ; + dest [k] = current - last_val ; + last_val = current ; + } ; + + pxi->last_16 = last_val << 8 ; +} /* s2dsc_array */ + +static void +i2dsc_array (XI_PRIVATE *pxi, const int *src, signed char *dest, int count) +{ signed char last_val, current ; + int k ; + + last_val = pxi->last_16 >> 8 ; + + for (k = 0 ; k < count ; k++) + { current = src [k] >> 24 ; + dest [k] = current - last_val ; + last_val = current ; + } ; + + pxi->last_16 = last_val << 8 ; +} /* i2dsc_array */ + +static void +f2dsc_array (XI_PRIVATE *pxi, const float *src, signed char *dest, int count, float normfact) +{ signed char last_val, current ; + int k ; + + last_val = pxi->last_16 >> 8 ; + + for (k = 0 ; k < count ; k++) + { current = lrintf (src [k] * normfact) ; + dest [k] = current - last_val ; + last_val = current ; + } ; + + pxi->last_16 = last_val << 8 ; +} /* f2dsc_array */ + +static void +d2dsc_array (XI_PRIVATE *pxi, const double *src, signed char *dest, int count, double normfact) +{ signed char last_val, current ; + int k ; + + last_val = pxi->last_16 >> 8 ; + + for (k = 0 ; k < count ; k++) + { current = lrint (src [k] * normfact) ; + dest [k] = current - last_val ; + last_val = current ; + } ; + + pxi->last_16 = last_val << 8 ; +} /* d2dsc_array */ + +/*============================================================================== +*/ + +static void +dles2s_array (XI_PRIVATE *pxi, short *src, int count, short *dest) +{ short last_val ; + int k ; + + last_val = pxi->last_16 ; + + for (k = 0 ; k < count ; k++) + { last_val += LES2H_SHORT (src [k]) ; + dest [k] = last_val ; + } ; + + pxi->last_16 = last_val ; +} /* dles2s_array */ + +static void +dles2i_array (XI_PRIVATE *pxi, short *src, int count, int *dest) +{ short last_val ; + int k ; + + last_val = pxi->last_16 ; + + for (k = 0 ; k < count ; k++) + { last_val += LES2H_SHORT (src [k]) ; + dest [k] = last_val << 16 ; + } ; + + pxi->last_16 = last_val ; +} /* dles2i_array */ + +static void +dles2f_array (XI_PRIVATE *pxi, short *src, int count, float *dest, float normfact) +{ short last_val ; + int k ; + + last_val = pxi->last_16 ; + + for (k = 0 ; k < count ; k++) + { last_val += LES2H_SHORT (src [k]) ; + dest [k] = last_val * normfact ; + } ; + + pxi->last_16 = last_val ; +} /* dles2f_array */ + +static void +dles2d_array (XI_PRIVATE *pxi, short *src, int count, double *dest, double normfact) +{ short last_val ; + int k ; + + last_val = pxi->last_16 ; + + for (k = 0 ; k < count ; k++) + { last_val += LES2H_SHORT (src [k]) ; + dest [k] = last_val * normfact ; + } ; + + pxi->last_16 = last_val ; +} /* dles2d_array */ + +/*------------------------------------------------------------------------------ +*/ + +static void +s2dles_array (XI_PRIVATE *pxi, const short *src, short *dest, int count) +{ short diff, last_val ; + int k ; + + last_val = pxi->last_16 ; + + for (k = 0 ; k < count ; k++) + { diff = src [k] - last_val ; + dest [k] = LES2H_SHORT (diff) ; + last_val = src [k] ; + } ; + + pxi->last_16 = last_val ; +} /* s2dles_array */ + +static void +i2dles_array (XI_PRIVATE *pxi, const int *src, short *dest, int count) +{ short diff, last_val ; + int k ; + + last_val = pxi->last_16 ; + + for (k = 0 ; k < count ; k++) + { diff = (src [k] >> 16) - last_val ; + dest [k] = LES2H_SHORT (diff) ; + last_val = src [k] >> 16 ; + } ; + + pxi->last_16 = last_val ; +} /* i2dles_array */ + +static void +f2dles_array (XI_PRIVATE *pxi, const float *src, short *dest, int count, float normfact) +{ short diff, last_val, current ; + int k ; + + last_val = pxi->last_16 ; + + for (k = 0 ; k < count ; k++) + { current = lrintf (src [k] * normfact) ; + diff = current - last_val ; + dest [k] = LES2H_SHORT (diff) ; + last_val = current ; + } ; + + pxi->last_16 = last_val ; +} /* f2dles_array */ + +static void +d2dles_array (XI_PRIVATE *pxi, const double *src, short *dest, int count, double normfact) +{ short diff, last_val, current ; + int k ; + + last_val = pxi->last_16 ; + + for (k = 0 ; k < count ; k++) + { current = lrint (src [k] * normfact) ; + diff = current - last_val ; + dest [k] = LES2H_SHORT (diff) ; + last_val = current ; + } ; + + pxi->last_16 = last_val ; +} /* d2dles_array */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 1ab2dbe0-29af-4d80-9c6f-cb21b67521bc +*/ diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 00000000..4be379ff --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,412 @@ +## Process this file with automake to produce Makefile.in + +if ENABLE_TEST_COVERAGE +CPP_TEST = +else +CPP_TEST = cpp_test +endif + +noinst_PROGRAMS = sfversion floating_point_test write_read_test \ + lossy_comp_test error_test ulaw_test alaw_test dwvw_test \ + peak_chunk_test command_test stdin_test stdout_test stdio_test \ + pcm_test headerless_test pipe_test benchmark header_test misc_test \ + raw_test string_test open_fail_test multi_file_test dither_test \ + scale_clip_test win32_test fix_this aiff_rw_test virtual_io_test \ + locale_test largefile_test win32_ordinal_test $(CPP_TEST) + +SNDFILEDIR = ../src +INCLUDES = -I$(srcdir)/$(SNDFILEDIR) +noinst_HEADERS = dft_cmp.h utils.h + +autogen_sources = write_read_test.tpl write_read_test.def \ + pcm_test.tpl pcm_test.def \ + header_test.tpl header_test.def \ + utils.tpl utils.def \ + scale_clip_test.tpl scale_clip_test.def \ + pipe_test.tpl pipe_test.def \ + floating_point_test.tpl floating_point_test.def \ + benchmark.tpl benchmark.def + +EXTRA_DIST = $(autogen_sources) + +#=============================================================================== + +sfversion_SOURCES = sfversion.c +sfversion_LDADD = $(SNDFILEDIR)/libsndfile.la + +write_read_test_SOURCES = utils.c write_read_test.c +write_read_test_LDADD = $(SNDFILEDIR)/libsndfile.la + +lossy_comp_test_SOURCES = utils.c lossy_comp_test.c +lossy_comp_test_LDADD = $(SNDFILEDIR)/libsndfile.la + +fix_this_SOURCES = utils.c fix_this.c +fix_this_LDADD = $(SNDFILEDIR)/libsndfile.la + +error_test_SOURCES = error_test.c utils.c +error_test_LDADD = $(SNDFILEDIR)/libsndfile.la + +ulaw_test_SOURCES = utils.c ulaw_test.c +ulaw_test_LDADD = $(SNDFILEDIR)/libsndfile.la + +alaw_test_SOURCES = utils.c alaw_test.c +alaw_test_LDADD = $(SNDFILEDIR)/libsndfile.la + +aiff_rw_test_SOURCES = utils.c aiff_rw_test.c +aiff_rw_test_LDADD = $(SNDFILEDIR)/libsndfile.la + +command_test_SOURCES = command_test.c utils.c +command_test_LDADD = $(SNDFILEDIR)/libsndfile.la + +locale_test_SOURCES = locale_test.c utils.c +locale_test_LDADD = $(SNDFILEDIR)/libsndfile.la + +largefile_test_SOURCES = largefile_test.c utils.c +largefile_test_LDADD = $(SNDFILEDIR)/libsndfile.la + +pcm_test_SOURCES = pcm_test.c utils.c +pcm_test_LDADD = $(SNDFILEDIR)/libsndfile.la + +headerless_test_SOURCES = utils.c headerless_test.c +headerless_test_LDADD = $(SNDFILEDIR)/libsndfile.la + +stdin_test_SOURCES = stdin_test.c utils.c +stdin_test_LDADD = $(SNDFILEDIR)/libsndfile.la + +stdout_test_SOURCES = stdout_test.c +stdout_test_LDADD = $(SNDFILEDIR)/libsndfile.la + +stdio_test_SOURCES = stdio_test.c utils.c +stdio_test_LDADD = $(SNDFILEDIR)/libsndfile.la + +pipe_test_SOURCES = pipe_test.c utils.c +pipe_test_LDADD = $(SNDFILEDIR)/libsndfile.la + +benchmark_SOURCES = benchmark.c +benchmark_LDADD = $(SNDFILEDIR)/libsndfile.la + +header_test_SOURCES = header_test.c utils.c +header_test_LDADD = $(SNDFILEDIR)/libsndfile.la + +misc_test_SOURCES = misc_test.c utils.c +misc_test_LDADD = $(SNDFILEDIR)/libsndfile.la + +raw_test_SOURCES = raw_test.c utils.c +raw_test_LDADD = $(SNDFILEDIR)/libsndfile.la + +string_test_SOURCES = string_test.c utils.c +string_test_LDADD = $(SNDFILEDIR)/libsndfile.la + +dither_test_SOURCES = dither_test.c utils.c +dither_test_LDADD = $(SNDFILEDIR)/libsndfile.la + +open_fail_test_SOURCES = open_fail_test.c utils.c +open_fail_test_LDADD = $(SNDFILEDIR)/libsndfile.la + +multi_file_test_SOURCES = multi_file_test.c utils.c +multi_file_test_LDADD = $(SNDFILEDIR)/libsndfile.la + +virtual_io_test_SOURCES = virtual_io_test.c utils.c +virtual_io_test_LDADD = $(SNDFILEDIR)/libsndfile.la + +win32_test_SOURCES = win32_test.c +win32_test_LDADD = + +win32_ordinal_test_SOURCES = win32_ordinal_test.c utils.c +win32_ordinal_test_LDADD = $(SNDFILEDIR)/libsndfile.la + +cpp_test_SOURCES = cpp_test.cc utils.c +cpp_test_LDADD = $(SNDFILEDIR)/libsndfile.la + +# Lite remove start +dwvw_test_SOURCES = utils.c dwvw_test.c +dwvw_test_LDADD = $(SNDFILEDIR)/libsndfile.la + +floating_point_test_SOURCES = utils.c dft_cmp.c floating_point_test.c +floating_point_test_LDADD = $(SNDFILEDIR)/libsndfile.la + +peak_chunk_test_SOURCES = peak_chunk_test.c utils.c +peak_chunk_test_LDADD = $(SNDFILEDIR)/libsndfile.la + +scale_clip_test_SOURCES = scale_clip_test.c utils.c +scale_clip_test_LDADD = $(SNDFILEDIR)/libsndfile.la +# Lite remove end + +#=============================================================================== + +write_read_test.c: write_read_test.def write_read_test.tpl + autogen --writable write_read_test.def + +pcm_test.c: pcm_test.def pcm_test.tpl + autogen --writable pcm_test.def + +header_test.c: header_test.def header_test.tpl + autogen --writable header_test.def + +utils.c: utils.def utils.tpl + autogen --writable utils.def + +scale_clip_test.c: scale_clip_test.def scale_clip_test.tpl + autogen --writable scale_clip_test.def + +pipe_test.c: pipe_test.def pipe_test.tpl + autogen --writable pipe_test.def + +floating_point_test.c: floating_point_test.def floating_point_test.tpl + autogen --writable floating_point_test.def + +benchmark.c: benchmark.def benchmark.tpl + autogen --writable benchmark.def + +genfiles : write_read_test.c pcm_test.c header_test.c utils.c \ + scale_clip_test.c pipe_test.c floating_point_test.c benchmark.c + +#=============================================================================== + +check: generic-tests wav-tests aiff-tests au-tests caf-tests raw-tests \ + paf-tests svx-tests nist-tests ircam-tests voc-tests w64-tests mat4-tests \ + mat5-tests pvf-tests xi-tests htk-tests avr-tests sds-tests sd2-tests \ + flac-tests caf-tests wve-tests io-tests + +generic-tests : error_test ulaw_test alaw_test command_test floating_point_test \ + pcm_test win32_ordinal_test $(CPP_TEST) + uname -a + ./error_test + ./pcm_test + ./ulaw_test + ./alaw_test + ./dwvw_test + ./command_test ver + ./command_test norm + ./command_test format + ./command_test peak + ./command_test trunc + ./command_test inst + ./command_test bext + ./floating_point_test + ./scale_clip_test + ./headerless_test + ./locale_test + ./win32_ordinal_test +if ENABLE_TEST_COVERAGE + @echo "cpp_test not under test coverage" +else + ./cpp_test +endif + @echo "----------------------------------------------------------------------" + @echo " `./sfversion` passed common tests." + @echo "----------------------------------------------------------------------" + +wav-tests: write_read_test lossy_comp_test peak_chunk_test header_test misc_test multi_file_test + ./write_read_test wav + ./lossy_comp_test wav_ima + ./lossy_comp_test wav_msadpcm + ./lossy_comp_test wav_ulaw + ./lossy_comp_test wav_alaw + ./lossy_comp_test wav_gsm610 + ./lossy_comp_test wav_g721 + ./peak_chunk_test wav + ./header_test wav + ./misc_test wav + ./string_test wav + ./multi_file_test wav + ./open_fail_test wav + @echo "----------------------------------------------------------------------" + @echo " `./sfversion` passed tests on WAV files." + @echo "----------------------------------------------------------------------" + +aiff-tests: write_read_test lossy_comp_test peak_chunk_test header_test misc_test aiff_rw_test + ./write_read_test aiff + ./lossy_comp_test aiff_ulaw + ./lossy_comp_test aiff_alaw + ./lossy_comp_test aiff_gsm610 + @echo "==========================" + @echo "./lossy_comp_test aiff_ima" + @echo "==========================" + ./peak_chunk_test aiff + ./header_test aiff + ./misc_test aiff + ./string_test aiff + ./multi_file_test aiff + ./aiff_rw_test + @echo "----------------------------------------------------------------------" + @echo " `./sfversion` passed tests on AIFF files." + @echo "----------------------------------------------------------------------" + +caf-tests: write_read_test lossy_comp_test header_test misc_test + ./write_read_test caf + ./lossy_comp_test caf_ulaw + ./lossy_comp_test caf_alaw + ./header_test caf + ./misc_test caf + @echo "----------------------------------------------------------------------" + @echo " `./sfversion` passed tests on CAF files." + @echo "----------------------------------------------------------------------" + +au-tests: write_read_test lossy_comp_test header_test misc_test + ./write_read_test au + ./lossy_comp_test au_ulaw + ./lossy_comp_test au_alaw + ./lossy_comp_test au_g721 + ./lossy_comp_test au_g723 + ./header_test au + ./misc_test au + ./multi_file_test wav + @echo "----------------------------------------------------------------------" + @echo " `./sfversion` passed tests on AU files." + @echo "----------------------------------------------------------------------" + +raw-tests: write_read_test lossy_comp_test raw_test + ./write_read_test raw + ./lossy_comp_test raw_ulaw + ./lossy_comp_test raw_alaw + ./lossy_comp_test raw_gsm610 + ./lossy_comp_test vox_adpcm + ./raw_test + @echo "----------------------------------------------------------------------" + @echo " `./sfversion` passed tests on RAW (header-less) files." + @echo "----------------------------------------------------------------------" + +w64-tests: write_read_test lossy_comp_test misc_test + ./write_read_test w64 + ./lossy_comp_test w64_ima + ./lossy_comp_test w64_msadpcm + ./lossy_comp_test w64_ulaw + ./lossy_comp_test w64_alaw + ./lossy_comp_test w64_gsm610 + ./header_test w64 + ./misc_test w64 + @echo "----------------------------------------------------------------------" + @echo " `./sfversion` passed tests on W64 files." + @echo "----------------------------------------------------------------------" + +wve-tests: lossy_comp_test + ./lossy_comp_test wve + @echo "----------------------------------------------------------------------" + @echo " `./sfversion` passed tests on WVE files." + @echo "----------------------------------------------------------------------" + +# Lite remove start +paf-tests: write_read_test misc_test + ./write_read_test paf + ./header_test paf + ./misc_test paf + @echo "----------------------------------------------------------------------" + @echo " `./sfversion` passed tests on PAF files." + @echo "----------------------------------------------------------------------" + +svx-tests: write_read_test misc_test + ./write_read_test svx + ./header_test svx + ./misc_test svx + @echo "----------------------------------------------------------------------" + @echo " `./sfversion` passed tests on SVX files." + @echo "----------------------------------------------------------------------" + +nist-tests: write_read_test lossy_comp_test misc_test + ./write_read_test nist + ./lossy_comp_test nist_ulaw + ./lossy_comp_test nist_alaw + ./header_test nist + ./misc_test nist + @echo "----------------------------------------------------------------------" + @echo " `./sfversion` passed tests on NIST files." + @echo "----------------------------------------------------------------------" + +ircam-tests: write_read_test lossy_comp_test misc_test + ./write_read_test ircam + ./lossy_comp_test ircam_ulaw + ./lossy_comp_test ircam_alaw + ./header_test ircam + ./misc_test ircam + @echo "----------------------------------------------------------------------" + @echo " `./sfversion` passed tests on IRCAM files." + @echo "----------------------------------------------------------------------" + +voc-tests: write_read_test lossy_comp_test misc_test + ./write_read_test voc + ./lossy_comp_test voc_ulaw + ./lossy_comp_test voc_alaw + ./header_test voc + ./misc_test voc + @echo "----------------------------------------------------------------------" + @echo " `./sfversion` passed tests on VOC files." + @echo "----------------------------------------------------------------------" + +mat4-tests: write_read_test misc_test + ./write_read_test mat4 + ./header_test mat4 + ./misc_test mat4 + @echo "----------------------------------------------------------------------" + @echo " `./sfversion` passed tests on MAT4 files." + @echo "----------------------------------------------------------------------" + +mat5-tests: write_read_test misc_test + ./write_read_test mat5 + ./header_test mat5 + ./misc_test mat5 + @echo "----------------------------------------------------------------------" + @echo " `./sfversion` passed tests on MAT5 files." + @echo "----------------------------------------------------------------------" + +pvf-tests: write_read_test misc_test + ./write_read_test pvf + ./header_test pvf + ./misc_test pvf + @echo "----------------------------------------------------------------------" + @echo " `./sfversion` passed tests on PVF files." + @echo "----------------------------------------------------------------------" + +xi-tests: lossy_comp_test + ./lossy_comp_test xi_dpcm + @echo "----------------------------------------------------------------------" + @echo " `./sfversion` passed tests on XI files." + @echo "----------------------------------------------------------------------" + +htk-tests: write_read_test misc_test + ./write_read_test htk + ./header_test htk + ./misc_test htk + @echo "----------------------------------------------------------------------" + @echo " `./sfversion` passed tests on HTK files." + @echo "----------------------------------------------------------------------" + +avr-tests: write_read_test misc_test + ./write_read_test avr + ./header_test avr + ./misc_test avr + @echo "----------------------------------------------------------------------" + @echo " `./sfversion` passed tests on AVR files." + @echo "----------------------------------------------------------------------" + +sds-tests: write_read_test misc_test + ./write_read_test sds + ./header_test sds + ./misc_test sds + @echo "----------------------------------------------------------------------" + @echo " `./sfversion` passed tests on SDS files." + @echo "----------------------------------------------------------------------" + +sd2-tests: write_read_test + ./write_read_test sd2 + @echo "----------------------------------------------------------------------" + @echo " `./sfversion` passed tests on SD2 files." + @echo "----------------------------------------------------------------------" + +flac-tests: write_read_test + ./write_read_test flac + @echo "----------------------------------------------------------------------" + @echo " `./sfversion` passed tests on FLAC files." + @echo "----------------------------------------------------------------------" + +# Lite remove end + +io-tests: stdio_test stdin_test stdout_test pipe_test + ./stdio_test + ./pipe_test + ./virtual_io_test + @echo "----------------------------------------------------------------------" + @echo " `./sfversion` passed stdio/pipe/vio tests." + @echo "----------------------------------------------------------------------" + + diff --git a/tests/aiff_rw_test.c b/tests/aiff_rw_test.c new file mode 100644 index 00000000..b15707df --- /dev/null +++ b/tests/aiff_rw_test.c @@ -0,0 +1,169 @@ +/* +** Copyright (C) 2003,2004 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + + +#include +#include +#include +#include +#include + +#include + +#include "utils.h" + + +static unsigned char aifc_data [] = +{ 'F' , 'O' , 'R' , 'M' , + 0x00, 0x00, 0x01, 0xE8, /* FORM length */ + + 'A' , 'I' , 'F' , 'C' , + 0x43, 0x4F, 0x4D, 0x4D, /* COMM */ + 0x00, 0x00, 0x00, 0x26, /* COMM length */ + 0x00, 0x01, 0x00, 0x00, 0x00, 0xAE, 0x00, 0x10, 0x40, 0x0D, 0xAC, 0x44, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x4F, 0x4E, 0x45, 0x0D, 'N' , + 'o' , 't' , ' ' , 'c' , 'o' , 'm' , 'p' , 'r' , 'e' , 's' , 's' , 'e' , + 'd' , 0x00, + + 'F' , 'V' , 'E' , 'R' , 0x00, 0x00, 0x00, 0x04, 0xA2, 0x80, 0x51, 0x40, + + /* A 'MARK' chunk. */ + 'M' , 'A' , 'R' , 'K' , 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 'A' , + 0x00, 0x02, 0x00, 0x00, 0x11, 0x3A, 0x02, 'B' , 'C' , 0x00, + 0x00, 0x03, 0x00, 0x00, 0x22, 0x74, 0x03, 'D' , 'E' , 'F', + 0x00, 0x04, 0x00, 0x00, 0x33, 0xAE, 0x04, 'G' , 'H' , 'I', 'J' , 0x00, + 0x00, 0x05, 0x00, 0x00, 0x44, 0xE8, 0x05, 'K' , 'L' , 'M', 'N' , 'O' , + + 'S' , 'S' , 'N' , 'D' , + 0x00, 0x00, 0x01, 0x64, /* SSND length */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xE0, 0xFF, 0xDB, 0xFF, 0xD0, 0xFF, 0xD5, 0xFF, 0xD6, 0xFF, 0xD0, + 0xFF, 0xBF, 0xFF, 0xBE, 0xFF, 0xB9, 0xFF, 0xC8, 0xFF, 0xBF, 0xFF, 0xD5, + 0xFF, 0xC3, 0xFF, 0xBF, 0xFF, 0xB3, 0xFF, 0xBE, 0xFF, 0xB4, 0xFF, 0xAD, + 0xFF, 0xAC, 0xFF, 0xAF, 0xFF, 0xB9, 0xFF, 0xB3, 0xFF, 0xA4, 0xFF, 0xA5, + 0xFF, 0x93, 0xFF, 0x95, 0xFF, 0x97, 0xFF, 0x98, 0xFF, 0x99, 0xFF, 0x9E, + 0xFF, 0x90, 0xFF, 0x80, 0xFF, 0x81, 0xFF, 0x7C, 0xFF, 0x80, 0xFF, 0x7C, + 0xFF, 0x72, 0xFF, 0x72, 0xFF, 0x6C, 0xFF, 0x75, 0xFF, 0x6E, 0xFF, 0x6F, + 0xFF, 0x66, 0xFF, 0x62, 0xFF, 0x5C, 0xFF, 0x64, 0xFF, 0x50, 0xFF, 0x56, + 0xFF, 0x56, 0xFF, 0x4A, 0xFF, 0x4A, 0xFF, 0x49, 0xFF, 0x44, 0xFF, 0x49, + 0xFF, 0x3B, 0xFF, 0x3F, 0xFF, 0x48, 0xFF, 0x46, 0xFF, 0x42, 0xFF, 0x49, + 0xFF, 0x43, 0xFF, 0x36, 0xFF, 0x40, 0xFF, 0x35, 0xFF, 0x3F, 0xFF, 0x36, + 0xFF, 0x37, 0xFF, 0x2E, 0xFF, 0x23, 0xFF, 0x23, 0xFF, 0x21, 0xFF, 0x1F, + 0xFF, 0x25, 0xFF, 0x2C, 0xFF, 0x1E, 0xFF, 0x22, 0xFF, 0x24, 0xFF, 0x2B, + 0xFF, 0x35, 0xFF, 0x27, 0xFF, 0x2E, 0xFF, 0x21, 0xFF, 0x18, 0xFF, 0x21, + 0xFF, 0x20, 0xFF, 0x0F, 0xFF, 0x21, 0xFF, 0x1A, 0xFF, 0x10, 0xFF, 0x09, + 0xFF, 0x1E, 0xFF, 0x19, 0xFF, 0x21, 0xFF, 0x13, 0xFF, 0x1B, 0xFF, 0x18, + 0xFF, 0x21, 0xFF, 0x0F, 0xFF, 0x1A, 0xFF, 0x16, 0xFF, 0x21, 0xFF, 0x1B, + 0xFF, 0x1B, 0xFF, 0x23, 0xFF, 0x1A, 0xFF, 0x21, 0xFF, 0x26, 0xFF, 0x23, + 0xFF, 0x26, 0xFF, 0x27, 0xFF, 0x30, 0xFF, 0x27, 0xFF, 0x2F, 0xFF, 0x28, + 0xFF, 0x2C, 0xFF, 0x27, 0xFF, 0x33, 0xFF, 0x29, 0xFF, 0x33, 0xFF, 0x3A, + 0xFF, 0x42, 0xFF, 0x3B, 0xFF, 0x4D, 0xFF, 0x4B, 0xFF, 0x4D, 0xFF, 0x4A, + 0xFF, 0x67, 0xFF, 0x77, 0xFF, 0x73, 0xFF, 0x7B, 0xFF, 0xDE, 0xFF, 0xAD, + 0x00, 0x4A, 0x00, 0x63, 0xEC, 0x8C, 0x03, 0xBB, 0x0E, 0xE4, 0x08, 0xF2, + 0x00, 0x70, 0xE3, 0xD1, 0xE5, 0xE4, 0x01, 0x6E, 0x0A, 0x67, 0x1C, 0x74, + 0xF8, 0x8E, 0x10, 0x7B, 0xEA, 0x3C, 0x09, 0x87, 0x1B, 0x24, 0xEF, 0x05, + 0x17, 0x76, 0x0D, 0x5B, 0x02, 0x43, 0xF5, 0xEF, 0x0C, 0x1D, 0xF7, 0x61, + 0x05, 0x95, 0x0B, 0xC2, 0xF1, 0x69, 0x1A, 0xA1, 0xEC, 0x75, 0xF4, 0x11, + 0x13, 0x4F, 0x13, 0x71, 0xFA, 0x33, 0xEC, 0x32, 0xC8, 0xCF, 0x05, 0xB0, + 0x0B, 0x61, 0x33, 0x19, 0xCE, 0x37, 0xEF, 0xD4, 0x21, 0x9D, 0xFA, 0xAE, +} ; + +static void rw_test (const char *filename) ; + +int +main (void) +{ const char *filename = "rw.aifc" ; + + print_test_name ("aiff_rw_test", filename) ; + + dump_data_to_file (filename, aifc_data, sizeof (aifc_data)) ; + + rw_test (filename) ; + + unlink (filename) ; + + puts ("ok") ; + return 0 ; +} /* main */ + +/*============================================================================== +*/ + +static void +rw_test (const char *filename) +{ SNDFILE *file ; + SF_INFO sfinfo_rd, sfinfo_rw ; + + memset (&sfinfo_rd, 0, sizeof (sfinfo_rd)) ; + memset (&sfinfo_rw, 0, sizeof (sfinfo_rw)) ; + + /* Open the file in read only mode and fill in the SF_INFO struct. */ + if ((file = sf_open (filename, SFM_READ, &sfinfo_rd)) == NULL) + { printf ("\n\nLine %d : sf_open SFM_READ failed : %s\n\n", __LINE__, sf_strerror (NULL)) ; + exit (1) ; + } ; + check_log_buffer_or_die (file, __LINE__) ; + sf_close (file) ; + + /* Now open read/write and close the file. */ + if ((file = sf_open (filename, SFM_RDWR, &sfinfo_rw)) == NULL) + { printf ("\n\nLine %d : sf_open SFM_RDWR failed : %s\n\n", __LINE__, sf_strerror (NULL)) ; + exit (1) ; + } ; + check_log_buffer_or_die (file, __LINE__) ; + sf_close (file) ; + + /* Open again as read only again and fill in a new SF_INFO struct. */ + memset (&sfinfo_rw, 0, sizeof (sfinfo_rw)) ; + if ((file = sf_open (filename, SFM_READ, &sfinfo_rw)) == NULL) + { printf ("\n\nLine %d : sf_open SFM_RDWR failed : %s\n\n", __LINE__, sf_strerror (NULL)) ; + exit (1) ; + } ; + check_log_buffer_or_die (file, __LINE__) ; + sf_close (file) ; + + /* Now compare the two. */ + if (sfinfo_rd.format != sfinfo_rw.format) + { printf ("\n\nLine %d : channel count mismatch (0x%08X != 0x%08X).\n\n", __LINE__, + sfinfo_rd.format, sfinfo_rw.format) ; + exit (1) ; + } ; + + if (sfinfo_rd.channels != sfinfo_rw.channels) + { printf ("\n\nLine %d : channel count mismatch (%d != %d).\n\n", __LINE__, + sfinfo_rd.channels, sfinfo_rw.channels) ; + exit (1) ; + } ; + + if (sfinfo_rd.frames != sfinfo_rw.frames) + { printf ("\n\nLine %d : frame count mismatch (%ld != %ld).\n\n", __LINE__, + SF_COUNT_TO_LONG (sfinfo_rd.frames), SF_COUNT_TO_LONG (sfinfo_rw.frames)) ; + exit (1) ; + } ; + + return ; +} /* rw_test */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 12561248-1ad1-4ba6-941c-029f1333c080 +*/ diff --git a/tests/alaw_test.c b/tests/alaw_test.c new file mode 100644 index 00000000..2c0788b6 --- /dev/null +++ b/tests/alaw_test.c @@ -0,0 +1,250 @@ +/* +** Copyright (C) 1999-2004 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include + +#if HAVE_UNISTD_H +#include +#endif + +#include + +#include "utils.h" + +#define BUFFER_SIZE (65536) + +static unsigned char alaw_encode (int sample) ; +static int alaw_decode (unsigned int alawbyte) ; + +static short short_buffer [BUFFER_SIZE] ; +static unsigned char alaw_buffer [BUFFER_SIZE] ; + +int +main (void) +{ SNDFILE *file ; + SF_INFO sfinfo ; + const char *filename ; + int k ; + + filename = "test.raw" ; + + sfinfo.format = SF_FORMAT_RAW | SF_FORMAT_ALAW ; + sfinfo.samplerate = 44100 ; + sfinfo.frames = 123456789 ; /* Wrong length. Library should correct this on sf_close. */ + sfinfo.channels = 1 ; + + if ((file = sf_open (filename, SFM_WRITE, &sfinfo)) == NULL) + { printf ("sf_open_write failed with error : ") ; + fflush (stdout) ; + puts (sf_strerror (NULL)) ; + exit (1) ; + } ; + + /* Generate a file containing all possible 16 bit sample values + ** and write it to disk as alaw encoded.frames. + */ + + for (k = 0 ; k < 0x10000 ; k++) + short_buffer [k] = k & 0xFFFF ; + + sf_write_short (file, short_buffer, BUFFER_SIZE) ; + sf_close (file) ; + + /* Now open that file and compare the alaw encoded sample values + ** with what they should be. + */ + + if ((file = sf_open (filename, SFM_READ, &sfinfo)) == NULL) + { printf ("sf_open_write failed with error : ") ; + puts (sf_strerror (NULL)) ; + exit (1) ; + } ; + + check_log_buffer_or_die (file, __LINE__) ; + + if (sf_read_raw (file, alaw_buffer, BUFFER_SIZE) != BUFFER_SIZE) + { printf ("sf_read_raw : ") ; + puts (sf_strerror (file)) ; + exit (1) ; + } ; + + for (k = 0 ; k < 0x10000 ; k++) + if (alaw_encode (short_buffer [k]) != alaw_buffer [k]) + { printf ("Encoder error : sample #%d (0x%02X should be 0x%02X)\n", k, alaw_buffer [k], alaw_encode (short_buffer [k])) ; + exit (1) ; + } ; + + sf_close (file) ; + + printf (" alaw_test : encoder ... ok\n") ; + + /* Now generate a file containing all possible 8 bit encoded + ** sample values and write it to disk as alaw encoded.frames. + */ + + if (! (file = sf_open (filename, SFM_WRITE, &sfinfo))) + { printf ("sf_open_write failed with error : ") ; + puts (sf_strerror (NULL)) ; + exit (1) ; + } ; + + for (k = 0 ; k < 256 ; k++) + alaw_buffer [k] = k & 0xFF ; + + sf_write_raw (file, alaw_buffer, 256) ; + sf_close (file) ; + + /* Now open that file and compare the alaw decoded sample values + ** with what they should be. + */ + + if (! (file = sf_open (filename, SFM_READ, &sfinfo))) + { printf ("sf_open_write failed with error : ") ; + puts (sf_strerror (NULL)) ; + exit (1) ; + } ; + + check_log_buffer_or_die (file, __LINE__) ; + + if (sf_read_short (file, short_buffer, 256) != 256) + { printf ("sf_read_short : ") ; + puts (sf_strerror (file)) ; + exit (1) ; + } ; + + + for (k = 0 ; k < 256 ; k++) + if (short_buffer [k] != alaw_decode (alaw_buffer [k])) + { printf ("Decoder error : sample #%d (0x%02X should be 0x%02X)\n", k, short_buffer [k], alaw_decode (alaw_buffer [k])) ; + exit (1) ; + } ; + + sf_close (file) ; + + printf (" alaw_test : decoder ... ok\n") ; + + unlink (filename) ; + + return 0 ; +} /* main */ + + +/*================================================================================= +** The following routines came from the sox-12.15 (Sound eXcahcnge) distribution. +** +** This code is not compiled into libsndfile. It is only used to test the +** libsndfile lookup tables for correctness. +** +** I have included the original authors comments. +*/ + +/* +** A-law routines by Graeme W. Gill. +** Date: 93/5/7 +** +** References: +** 1) CCITT Recommendation G.711 +** +*/ + +#define ACLIP 31744 + +static +unsigned char alaw_encode (int sample) +{ static int exp_lut [128] = + { 1, 1, 2, 2, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7 + } ; + + int sign, exponent, mantissa ; + unsigned char Alawbyte ; + + /* Get the sample into sign-magnitude. */ + sign = ((~sample) >> 8) & 0x80 ; /* set aside the sign */ + if (sign == 0) + sample = -sample ; /* get magnitude */ + if (sample > ACLIP) + sample = ACLIP ; /* clip the magnitude */ + + /* Convert from 16 bit linear to ulaw. */ + if (sample >= 256) + { exponent = exp_lut [(sample >> 8) & 0x7F] ; + mantissa = ( sample >> ( exponent + 3 ) ) & 0x0F ; + Alawbyte = ((exponent << 4) | mantissa) ; + } + else + Alawbyte = (sample >> 4) ; + + Alawbyte ^= (sign ^ 0x55) ; + + return Alawbyte ; +} /* alaw_encode */ + +static +int alaw_decode (unsigned int Alawbyte) +{ static int exp_lut [8] = { 0, 264, 528, 1056, 2112, 4224, 8448, 16896 } ; + int sign, exponent, mantissa, sample ; + + Alawbyte ^= 0x55 ; + sign = (Alawbyte & 0x80) ; + Alawbyte &= 0x7f ; /* get magnitude */ + if (Alawbyte >= 16) + { exponent = (Alawbyte >> 4 ) & 0x07 ; + mantissa = Alawbyte & 0x0F ; + sample = exp_lut [exponent] + (mantissa << ( exponent + 3 )) ; + } + else + sample = (Alawbyte << 4) + 8 ; + if (sign == 0) + sample = -sample ; + + return sample ; +} /* alaw_decode */ + + + + + + + + + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: d3fb5eae-b3a4-4c5b-90ab-e3daa64c4a57 +*/ diff --git a/tests/benchmark-0.0.28 b/tests/benchmark-0.0.28 new file mode 100644 index 00000000..2d2b06f9 --- /dev/null +++ b/tests/benchmark-0.0.28 @@ -0,0 +1,40 @@ +erikd@coltrane > tests/benchmark +Benchmarking libsndfile-0.0.28 +------------------------------ +Each test takes a little over 5 seconds. + + Raw write PCM_16 : 30660117 samples per sec + Raw read PCM_16 : 62788982 samples per sec + +Native endian I/O : + Write short to PCM_16 : 83.37% of raw write + Read short from PCM_16 : 83.17% of raw read + Write int to PCM_24 : 30.78% of raw write + Read int from PCM_24 : 32.96% of raw read + Write int to PCM_32 : 42.05% of raw write + Read int from PCM_32 : 41.11% of raw read + Write float to PCM_16 : 17.75% of raw write + Read float from PCM_16 : 43.27% of raw read + Write float to PCM_24 : 15.30% of raw write + Read float from PCM_24 : 28.09% of raw read + Write float to PCM_32 : 14.55% of raw write + Read float from PCM_32 : 34.65% of raw read + Write float to FLOAT : 28.98% of raw write + Read float from FLOAT : 56.71% of raw read + +Endian swapped I/O : + Write short to PCM_16 : 43.39% of raw write + Read short from PCM_16 : 49.12% of raw read + Write int to PCM_24 : 29.65% of raw write + Read int from PCM_24 : 33.66% of raw read + Write int to PCM_32 : 19.62% of raw write + Read int from PCM_32 : 21.97% of raw read + Write float to PCM_16 : 17.63% of raw write + Read float from PCM_16 : 31.43% of raw read + Write float to PCM_24 : 14.91% of raw write + Read float from PCM_24 : 27.99% of raw read + Write float to PCM_32 : 13.69% of raw write + Read float from PCM_32 : 22.23% of raw read + Write float to FLOAT : 19.25% of raw write + Read float from FLOAT : 25.66% of raw read + diff --git a/tests/benchmark-1.0.0 b/tests/benchmark-1.0.0 new file mode 100644 index 00000000..29228364 --- /dev/null +++ b/tests/benchmark-1.0.0 @@ -0,0 +1,35 @@ +Benchmarking libsndfile-1.0.0 +----------------------------- +Each test takes a little over 5 seconds. + + Raw write PCM_16 : 31084269 samples per sec + Raw read PCM_16 : 63597065 samples per sec + +Native endian I/O : + Write short to PCM_16 : 83.19% of raw write + Read short from PCM_16 : 82.93% of raw read + Write int to PCM_24 : 31.12% of raw write + Read int from PCM_24 : 37.90% of raw read + Write float to PCM_16 : 37.00% of raw write + Read float from PCM_16 : 45.53% of raw read + Write float to PCM_24 : 29.08% of raw write + Read float from PCM_24 : 28.48% of raw read + Write float to PCM_32 : 22.08% of raw write + Read float from PCM_32 : 31.21% of raw read + Write float to FLOAT : 28.70% of raw write + Read float from FLOAT : 56.32% of raw read + +Endian swapped I/O : + Write short to PCM_16 : 22.08% of raw write + Read short from PCM_16 : 23.20% of raw read + Write int to PCM_24 : 30.96% of raw write + Read int from PCM_24 : 37.76% of raw read + Write float to PCM_16 : 35.82% of raw write + Read float from PCM_16 : 22.61% of raw read + Write float to PCM_24 : 27.70% of raw write + Read float from PCM_24 : 28.37% of raw read + Write float to PCM_32 : 20.77% of raw write + Read float from PCM_32 : 23.46% of raw read + Write float to FLOAT : 15.03% of raw write + Read float from FLOAT : 15.43% of raw read + diff --git a/tests/benchmark-1.0.0rc2 b/tests/benchmark-1.0.0rc2 new file mode 100644 index 00000000..77022467 --- /dev/null +++ b/tests/benchmark-1.0.0rc2 @@ -0,0 +1,31 @@ +Benchmarking libsndfile-1.0.0rc2 +-------------------------------- +Each test takes a little over 5 seconds. + + Raw write PCM_16 : 31638069 samples per sec + Raw read PCM_16 : 62788982 samples per sec + +Native endian I/O : + Write short to PCM_16 : 82.37% of raw write + Read short from PCM_16 : 82.17% of raw read + Write int to PCM_24 : 30.80% of raw write + Read int from PCM_24 : 37.95% of raw read + Write float to PCM_16 : 36.22% of raw write + Read float from PCM_16 : 23.32% of raw read + Write float to PCM_24 : 28.41% of raw write + Read float from PCM_24 : 28.41% of raw read + Write float to FLOAT : 28.41% of raw write + Read float from FLOAT : 57.50% of raw read + +Endian swapped I/O : + Write short to PCM_16 : 21.73% of raw write + Read short from PCM_16 : 23.37% of raw read + Write int to PCM_24 : 31.02% of raw write + Read int from PCM_24 : 38.24% of raw read + Write float to PCM_16 : 35.51% of raw write + Read float from PCM_16 : 19.16% of raw read + Write float to PCM_24 : 27.37% of raw write + Read float from PCM_24 : 28.74% of raw read + Write float to FLOAT : 15.11% of raw write + Read float from FLOAT : 15.60% of raw read + diff --git a/tests/benchmark-1.0.6pre10-coltrane b/tests/benchmark-1.0.6pre10-coltrane new file mode 100644 index 00000000..12f71a8a --- /dev/null +++ b/tests/benchmark-1.0.6pre10-coltrane @@ -0,0 +1,39 @@ +Benchmarking libsndfile-1.0.6pre10 +---------------------------------- +Each test takes a little over 5 seconds. + + Raw write PCM_16 : 28845961 samples per sec + Raw read PCM_16 : 63471874 samples per sec + +Native endian I/O : + Write short to PCM_16 : 86.21% of raw write + Read short from PCM_16 : 82.60% of raw read + Write int to PCM_24 : 34.89% of raw write + Read int from PCM_24 : 37.26% of raw read + Write int to PCM_32 : 43.36% of raw write + Read int from PCM_32 : 41.30% of raw read + Write float to PCM_16 : 43.02% of raw write + Read float from PCM_16 : 43.99% of raw read + Write float to PCM_24 : 32.72% of raw write + Read float from PCM_24 : 28.21% of raw read + Write float to PCM_32 : 25.92% of raw write + Read float from PCM_32 : 30.98% of raw read + Write float to FLOAT : 46.65% of raw write + Read float from FLOAT : 56.66% of raw read + +Endian swapped I/O : + Write short to PCM_16 : 54.53% of raw write + Read short from PCM_16 : 56.32% of raw read + Write int to PCM_24 : 35.28% of raw write + Read int from PCM_24 : 37.33% of raw read + Write int to PCM_32 : 26.21% of raw write + Read int from PCM_32 : 23.51% of raw read + Write float to PCM_16 : 41.39% of raw write + Read float from PCM_16 : 23.56% of raw read + Write float to PCM_24 : 30.86% of raw write + Read float from PCM_24 : 28.27% of raw read + Write float to PCM_32 : 23.83% of raw write + Read float from PCM_32 : 20.54% of raw read + Write float to FLOAT : 27.26% of raw write + Read float from FLOAT : 29.04% of raw read + diff --git a/tests/benchmark-1.0.6pre10-miles b/tests/benchmark-1.0.6pre10-miles new file mode 100644 index 00000000..fffdb846 --- /dev/null +++ b/tests/benchmark-1.0.6pre10-miles @@ -0,0 +1,39 @@ +Benchmarking libsndfile-1.0.6pre10 +---------------------------------- +Each test takes a little over 5 seconds. + + Raw write PCM_16 : 40092612 samples per sec + Raw read PCM_16 : 42382563 samples per sec + +Native endian I/O : + Write short to PCM_16 : 61.90% of raw write + Read short from PCM_16 : 100.20% of raw read + Write int to PCM_24 : 28.69% of raw write + Read int from PCM_24 : 33.62% of raw read + Write int to PCM_32 : 31.14% of raw write + Read int from PCM_32 : 51.04% of raw read + Write float to PCM_16 : 25.57% of raw write + Read float from PCM_16 : 28.17% of raw read + Write float to PCM_24 : 23.59% of raw write + Read float from PCM_24 : 24.14% of raw read + Write float to PCM_32 : 18.00% of raw write + Read float from PCM_32 : 22.59% of raw read + Write float to FLOAT : 31.32% of raw write + Read float from FLOAT : 51.54% of raw read + +Endian swapped I/O : + Write short to PCM_16 : 42.81% of raw write + Read short from PCM_16 : 54.58% of raw read + Write int to PCM_24 : 29.28% of raw write + Read int from PCM_24 : 33.43% of raw read + Write int to PCM_32 : 22.21% of raw write + Read int from PCM_32 : 27.24% of raw read + Write float to PCM_16 : 25.76% of raw write + Read float from PCM_16 : 26.84% of raw read + Write float to PCM_24 : 23.71% of raw write + Read float from PCM_24 : 24.10% of raw read + Write float to PCM_32 : 18.47% of raw write + Read float from PCM_32 : 21.45% of raw read + Write float to FLOAT : 22.46% of raw write + Read float from FLOAT : 29.72% of raw read + diff --git a/tests/benchmark-latest-coltrane b/tests/benchmark-latest-coltrane new file mode 100644 index 00000000..97ce29a8 --- /dev/null +++ b/tests/benchmark-latest-coltrane @@ -0,0 +1,75 @@ +erikd@coltrane > cat tests/benchmark-0.0.28 +Benchmarking libsndfile-0.0.28 +------------------------------ +Each test takes a little over 5 seconds. + + Raw write PCM_16 : 31022959 samples per sec + Raw read PCM_16 : 63471874 samples per sec + +Native endian I/O : + Write short to PCM_16 : 83.19% of raw write + Read short from PCM_16 : 82.28% of raw read + Write int to PCM_24 : 30.81% of raw write + Read int from PCM_24 : 32.92% of raw read + Write float to PCM_16 : 17.70% of raw write + Read float from PCM_16 : 43.64% of raw read + Write float to PCM_24 : 15.09% of raw write + Read float from PCM_24 : 27.79% of raw read + Write float to PCM_32 : 14.32% of raw write + Read float from PCM_32 : 34.42% of raw read + Write float to FLOAT : 28.64% of raw write + Read float from FLOAT : 56.77% of raw read + +Endian swapped I/O : + Write short to PCM_16 : 44.04% of raw write + Read short from PCM_16 : 49.46% of raw read + Write int to PCM_24 : 28.92% of raw write + Read int from PCM_24 : 33.10% of raw read + Write float to PCM_16 : 17.30% of raw write + Read float from PCM_16 : 31.46% of raw read + Write float to PCM_24 : 14.62% of raw write + Read float from PCM_24 : 27.64% of raw read + Write float to PCM_32 : 13.65% of raw write + Read float from PCM_32 : 22.41% of raw read + Write float to FLOAT : 19.13% of raw write + Read float from FLOAT : 26.21% of raw read + +erikd@coltrane > tests/benchmark +Benchmarking libsndfile-1.0.0 +----------------------------- +Each test takes a little over 5 seconds. + + Raw write PCM_16 : 29884416 samples per sec + Raw read PCM_16 : 63347175 samples per sec + +Native endian I/O : + Write short to PCM_16 : 88.24% of raw write + Read short from PCM_16 : 82.76% of raw read + Write int to PCM_24 : 34.95% of raw write + Read int from PCM_24 : 37.17% of raw read + Write int to PCM_32 : 43.86% of raw write + Read int from PCM_32 : 41.22% of raw read + Write float to PCM_16 : 42.07% of raw write + Read float from PCM_16 : 44.25% of raw read + Write float to PCM_24 : 32.43% of raw write + Read float from PCM_24 : 28.93% of raw read + Write float to PCM_32 : 25.60% of raw write + Read float from PCM_32 : 31.10% of raw read + Write float to FLOAT : 45.55% of raw write + Read float from FLOAT : 57.41% of raw read + +Endian swapped I/O : + Write short to PCM_16 : 43.46% of raw write + Read short from PCM_16 : 43.99% of raw read + Write int to PCM_24 : 35.09% of raw write + Read int from PCM_24 : 37.34% of raw read + Write int to PCM_32 : 24.05% of raw write + Read int from PCM_32 : 19.74% of raw read + Write float to PCM_16 : 40.25% of raw write + Read float from PCM_16 : 32.15% of raw read + Write float to PCM_24 : 31.02% of raw write + Read float from PCM_24 : 28.82% of raw read + Write float to PCM_32 : 23.54% of raw write + Read float from PCM_32 : 23.65% of raw read + Write float to FLOAT : 24.87% of raw write + Read float from FLOAT : 20.28% of raw read diff --git a/tests/benchmark.def b/tests/benchmark.def new file mode 100644 index 00000000..5702d6b5 --- /dev/null +++ b/tests/benchmark.def @@ -0,0 +1,25 @@ +autogen definitions benchmark.tpl; + +data_type = { + type_name = short ; + multiplier = "32700.0" ; + }; + +data_type = { + type_name = int ; + multiplier = "32700.0 * (1<<16)" ; + }; + +data_type = { + type_name = float ; + multiplier = "1.0" ; + }; + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 5cadd2d0-dd74-4067-9d75-781a36d6bf15 +*/ + diff --git a/tests/benchmark.tpl b/tests/benchmark.tpl new file mode 100644 index 00000000..d5961c97 --- /dev/null +++ b/tests/benchmark.tpl @@ -0,0 +1,361 @@ +[+ AutoGen5 template c +] +/* +** Copyright (C) 2002-2004 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include + +#if HAVE_UNISTD_H +#include +#endif + +#if (HAVE_DECL_S_IRGRP == 0) +#include +#endif + +#include +#include +#include +#include +#include + +#include + +#ifndef M_PI +#define M_PI 3.14159265358979323846264338 +#endif + +#if (defined (WIN32) || defined (_WIN32)) + #define WRITE_FLAGS (O_WRONLY | O_CREAT | O_TRUNC | O_BINARY) + #define READ_FLAGS (O_RDONLY | O_BINARY) + #define WRITE_PERMS 0777 +#else + #define WRITE_FLAGS (O_WRONLY | O_CREAT | O_TRUNC) + #define READ_FLAGS (O_RDONLY) + #define WRITE_PERMS (S_IRUSR | S_IWUSR | S_IRGRP) +#endif + +#define BUFFER_SIZE (1<<18) +#define BLOCK_COUNT (30) +#define TEST_DURATION (5) /* 5 Seconds. */ + +typedef struct +{ double write_rate ; + double read_rate ; +} PERF_STATS ; + +static void *data = NULL ; + +static void calc_raw_performance (PERF_STATS *stats) ; + +[+ FOR data_type ++]static void calc_[+ (get "type_name") +]_performance (int format, double read_rate, double write_rate) ; +[+ ENDFOR data_type ++] + +static int cpu_is_big_endian (void) ; + +static const char* get_subtype_str (int subtype) ; + +int +main (int argc, char *argv []) +{ PERF_STATS stats ; + char buffer [256] = "Benchmarking " ; + int format_major ; + + if (! (data = malloc (BUFFER_SIZE * sizeof (double)))) + { perror ("Error : malloc failed") ; + exit (1) ; + } ; + + sf_command (NULL, SFC_GET_LIB_VERSION, buffer + strlen (buffer), sizeof (buffer) - strlen (buffer)) ; + + puts (buffer) ; + memset (buffer, '-', strlen (buffer)) ; + puts (buffer) ; + printf ("Each test takes a little over %d seconds.\n\n", TEST_DURATION) ; + + calc_raw_performance (&stats) ; + + if (argc < 2 || strcmp ("--native-only", argv [1]) == 0) + { puts ("\nNative endian I/O :") ; + format_major = cpu_is_big_endian () ? SF_FORMAT_AIFF : SF_FORMAT_WAV ; + + calc_short_performance (format_major | SF_FORMAT_PCM_16, stats.read_rate, stats.write_rate) ; + calc_int_performance (format_major | SF_FORMAT_PCM_24, stats.read_rate, stats.write_rate) ; + calc_int_performance (format_major | SF_FORMAT_PCM_32, stats.read_rate, stats.write_rate) ; + calc_float_performance (format_major | SF_FORMAT_PCM_16, stats.read_rate, stats.write_rate) ; + calc_float_performance (format_major | SF_FORMAT_PCM_24, stats.read_rate, stats.write_rate) ; + calc_float_performance (format_major | SF_FORMAT_PCM_32, stats.read_rate, stats.write_rate) ; + calc_float_performance (format_major | SF_FORMAT_FLOAT , stats.read_rate, stats.write_rate) ; + } ; + + if (argc < 2 || strcmp ("--swap-only", argv [1]) == 0) + { puts ("\nEndian swapped I/O :") ; + format_major = cpu_is_big_endian () ? SF_FORMAT_WAV : SF_FORMAT_AIFF ; + + calc_short_performance (format_major | SF_FORMAT_PCM_16, stats.read_rate, stats.write_rate) ; + calc_int_performance (format_major | SF_FORMAT_PCM_24, stats.read_rate, stats.write_rate) ; + calc_int_performance (format_major | SF_FORMAT_PCM_32, stats.read_rate, stats.write_rate) ; + calc_float_performance (format_major | SF_FORMAT_PCM_16, stats.read_rate, stats.write_rate) ; + calc_float_performance (format_major | SF_FORMAT_PCM_24, stats.read_rate, stats.write_rate) ; + calc_float_performance (format_major | SF_FORMAT_PCM_32, stats.read_rate, stats.write_rate) ; + calc_float_performance (format_major | SF_FORMAT_FLOAT , stats.read_rate, stats.write_rate) ; + } ; + + puts ("") ; + + free (data) ; + + return 0 ; +} /* main */ + +/*============================================================================== +*/ + +static void +calc_raw_performance (PERF_STATS *stats) +{ clock_t start_clock, clock_time ; + int fd, k, byte_count, retval, op_count ; + const char *filename ; + + filename = "benchmark.dat" ; + + byte_count = BUFFER_SIZE * sizeof (short) ; + + /* Collect write stats */ + printf (" Raw write PCM_16 : ") ; + fflush (stdout) ; + + clock_time = 0 ; + op_count = 0 ; + start_clock = clock () ; + + while (clock_time < (CLOCKS_PER_SEC * TEST_DURATION)) + { if ((fd = open (filename, WRITE_FLAGS, WRITE_PERMS)) < 0) + { printf ("Error : not able to open file : %s\n", filename) ; + perror ("") ; + exit (1) ; + } ; + + for (k = 0 ; k < BLOCK_COUNT ; k++) + { if ((retval = write (fd, data, byte_count)) != byte_count) + { printf ("Error : write returned %d (should have been %d)\n", retval, byte_count) ; + exit (1) ; + } ; + } ; + + close (fd) ; + + clock_time = clock () - start_clock ; + op_count ++ ; + } ; + + stats->write_rate = (1.0 * BUFFER_SIZE) * BLOCK_COUNT * op_count ; + stats->write_rate *= (1.0 * CLOCKS_PER_SEC) / clock_time ; + printf ("%10.0f samples per sec\n", stats->write_rate) ; + + /* Collect read stats */ + printf (" Raw read PCM_16 : ") ; + fflush (stdout) ; + + clock_time = 0 ; + op_count = 0 ; + start_clock = clock () ; + + while (clock_time < (CLOCKS_PER_SEC * TEST_DURATION)) + { if ((fd = open (filename, READ_FLAGS)) < 0) + { printf ("Error : not able to open file : %s\n", filename) ; + perror ("") ; + exit (1) ; + } ; + + for (k = 0 ; k < BLOCK_COUNT ; k++) + { if ((retval = read (fd, data, byte_count)) != byte_count) + { printf ("Error : write returned %d (should have been %d)\n", retval, byte_count) ; + exit (1) ; + } ; + } ; + + close (fd) ; + + clock_time = clock () - start_clock ; + op_count ++ ; + } ; + + stats->read_rate = (1.0 * BUFFER_SIZE) * BLOCK_COUNT * op_count ; + stats->read_rate *= (1.0 * CLOCKS_PER_SEC) / clock_time ; + printf ("%10.0f samples per sec\n", stats->read_rate) ; + + unlink (filename) ; +} /* calc_raw_performance */ + +/*------------------------------------------------------------------------------ +*/ + +[+ FOR data_type ++]static void +calc_[+ (get "type_name") +]_performance (int format, double read_rate, double write_rate) +{ SNDFILE *file ; + SF_INFO sfinfo ; + clock_t start_clock, clock_time ; + double performance ; + int k, item_count, retval, op_count ; + const char* subtype ; + [+ (get "type_name") +] *[+ (get "type_name") +]_data ; + const char *filename ; + + filename = "benchmark.dat" ; + subtype = get_subtype_str (format & SF_FORMAT_SUBMASK) ; + + [+ (get "type_name") +]_data = data ; + item_count = BUFFER_SIZE ; + for (k = 0 ; k < item_count ; k++) + [+ (get "type_name") +]_data [k] = [+ (get "multiplier") +] * sin (2 * M_PI * k / 32000.0) ; + + /* Collect write stats */ + printf (" Write %-5s to %s : ", "[+ (get "type_name") +]", subtype) ; + fflush (stdout) ; + + sfinfo.channels = 1 ; + sfinfo.format = format ; + sfinfo.frames = 1 ; + sfinfo.samplerate = 32000 ; + + clock_time = 0 ; + op_count = 0 ; + start_clock = clock () ; + + while (clock_time < (CLOCKS_PER_SEC * TEST_DURATION)) + { if (! (file = sf_open (filename, SFM_WRITE, &sfinfo))) + { printf ("Error : not able to open file : %s\n", filename) ; + perror ("") ; + exit (1) ; + } ; + + /* Turn off the addition of a PEAK chunk. */ + sf_command (file, SFC_SET_ADD_PEAK_CHUNK, NULL, SF_FALSE) ; + + for (k = 0 ; k < BLOCK_COUNT ; k++) + { if ((retval = sf_write_[+ (get "type_name") +] (file, [+ (get "type_name") +]_data, item_count)) != item_count) + { printf ("Error : sf_write_short returned %d (should have been %d)\n", retval, item_count) ; + exit (1) ; + } ; + } ; + + sf_close (file) ; + + clock_time = clock () - start_clock ; + op_count ++ ; + } ; + + performance = (1.0 * BUFFER_SIZE) * BLOCK_COUNT * op_count ; + performance *= (1.0 * CLOCKS_PER_SEC) / clock_time ; + printf ("%6.2f%% of raw write\n", 100.0 * performance / write_rate) ; + + /* Collect read stats */ + printf (" Read %-5s from %s : ", "[+ (get "type_name") +]", subtype) ; + fflush (stdout) ; + + clock_time = 0 ; + op_count = 0 ; + start_clock = clock () ; + + while (clock_time < (CLOCKS_PER_SEC * TEST_DURATION)) + { if (! (file = sf_open (filename, SFM_READ, &sfinfo))) + { printf ("Error : not able to open file : %s\n", filename) ; + perror ("") ; + exit (1) ; + } ; + + for (k = 0 ; k < BLOCK_COUNT ; k++) + { if ((retval = sf_read_[+ (get "type_name") +] (file, [+ (get "type_name") +]_data, item_count)) != item_count) + { printf ("Error : write returned %d (should have been %d)\n", retval, item_count) ; + exit (1) ; + } ; + } ; + + sf_close (file) ; + + clock_time = clock () - start_clock ; + op_count ++ ; + } ; + + performance = (1.0 * item_count) * BLOCK_COUNT * op_count ; + performance *= (1.0 * CLOCKS_PER_SEC) / clock_time ; + printf ("%6.2f%% of raw read\n", 100.0 * performance / read_rate) ; + + unlink (filename) ; + +} /* calc_[+ (get "type_name") +]_performance */ +[+ ENDFOR data_type ++] + +/*============================================================================== +*/ + +static int +cpu_is_big_endian (void) +{ unsigned char *cptr ; + int endtest ; + + endtest = 0x12345678 ; + + cptr = (unsigned char*) (&endtest) ; + + if (cptr [0] == 0x12 && cptr [1] == 0x34 && cptr [3] == 0x78) + return SF_TRUE ; + + return SF_FALSE ; +} /* cpu_is_big_endian */ + +static const char* +get_subtype_str (int subtype) +{ switch (subtype) + { case SF_FORMAT_PCM_16 : + return "PCM_16" ; + + case SF_FORMAT_PCM_24 : + return "PCM_24" ; + + case SF_FORMAT_PCM_32 : + return "PCM_32" ; + + case SF_FORMAT_FLOAT : + return "FLOAT " ; + + case SF_FORMAT_DOUBLE : + return "DOUBLE" ; + + default : break ; + } ; + + return "UNKNOWN" ; +} /* get_subtype_str */ + +[+ COMMENT + + Do not edit or modify anything in this comment block. + The following line is a file identity tag for the GNU Arch + revision control system. + + arch-tag: 64ccb3fb-c61d-42d7-b14f-0ec3ba303f88 + ++] diff --git a/tests/command_test.c b/tests/command_test.c new file mode 100644 index 00000000..357a713f --- /dev/null +++ b/tests/command_test.c @@ -0,0 +1,880 @@ +/* +** Copyright (C) 2001-2006 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include +#include + +#if HAVE_UNISTD_H +#include +#endif + +#include + +#include + +#include "utils.h" + +#define BUFFER_LEN (1<<10) +#define LOG_BUFFER_SIZE 1024 + +static void float_norm_test (const char *filename) ; +static void double_norm_test (const char *filename) ; +static void format_tests (void) ; +static void calc_peak_test (int filetype, const char *filename) ; +static void truncate_test (const char *filename, int filetype) ; +static void instrument_test (const char *filename, int filetype) ; +static void channel_map_test (const char *filename, int filetype) ; +static void broadcast_test (const char *filename, int filetype) ; + +/* Force the start of this buffer to be double aligned. Sparc-solaris will +** choke if its not. +*/ + +static int int_data [BUFFER_LEN] ; +static float float_data [BUFFER_LEN] ; +static double double_data [BUFFER_LEN] ; + +int +main (int argc, char *argv []) +{ int do_all = 0 ; + int test_count = 0 ; + + if (argc != 2) + { printf ("Usage : %s \n", argv [0]) ; + printf (" Where is one of the following:\n") ; + printf (" ver - test sf_command (SFC_GETLIB_VERSION)\n") ; + printf (" norm - test floating point normalisation\n") ; + printf (" format - test format string commands\n") ; + printf (" peak - test peak calculation\n") ; + printf (" trunc - test file truncation\n") ; + printf (" inst - test set/get of SF_INSTRUMENT.\n") ; + printf (" chanmap - test set/get of channel map data..\n") ; + printf (" all - perform all tests\n") ; + exit (1) ; + } ; + + do_all =! strcmp (argv [1], "all") ; + + if (do_all || strcmp (argv [1], "ver") == 0) + { char buffer [128] ; + buffer [0] = 0 ; + sf_command (NULL, SFC_GET_LIB_VERSION, buffer, sizeof (buffer)) ; + if (strlen (buffer) < 1) + { printf ("Line %d: could not retrieve lib version.\n", __LINE__) ; + exit (1) ; + } ; + test_count++ ; + } ; + + if (do_all || strcmp (argv [1], "norm") == 0) + { /* Preliminary float/double normalisation tests. More testing + ** is done in the program 'floating_point_test'. + */ + float_norm_test ("float.wav") ; + double_norm_test ("double.wav") ; + test_count++ ; + } ; + + if (do_all || strcmp (argv [1], "peak") == 0) + { calc_peak_test (SF_ENDIAN_BIG | SF_FORMAT_RAW, "be-peak.raw") ; + calc_peak_test (SF_ENDIAN_LITTLE | SF_FORMAT_RAW, "le-peak.raw") ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "format")) + { format_tests () ; + test_count++ ; + } ; + + if (do_all || strcmp (argv [1], "trunc") == 0) + { truncate_test ("truncate.raw", SF_FORMAT_RAW | SF_FORMAT_PCM_32) ; + truncate_test ("truncate.au" , SF_FORMAT_AU | SF_FORMAT_PCM_16) ; + test_count++ ; + } ; + + if (do_all || strcmp (argv [1], "inst") == 0) + { instrument_test ("instrument.wav", SF_FORMAT_WAV | SF_FORMAT_PCM_16) ; + instrument_test ("instrument.aiff" , SF_FORMAT_AIFF | SF_FORMAT_PCM_24) ; + /*-instrument_test ("instrument.xi", SF_FORMAT_XI | SF_FORMAT_DPCM_16) ;-*/ + test_count++ ; + } ; + + if (do_all || strcmp (argv [1], "chanmap") == 0) + { channel_map_test ("chanmap.wav", SF_FORMAT_WAV | SF_FORMAT_PCM_16) ; + channel_map_test ("chanmap.aiff" , SF_FORMAT_AIFF | SF_FORMAT_PCM_24) ; + /*-instrument_test ("instrument.xi", SF_FORMAT_XI | SF_FORMAT_DPCM_16) ;-*/ + test_count++ ; + } ; + + if (do_all || strcmp (argv [1], "bext") == 0) + { broadcast_test ("broadcast.wav", SF_FORMAT_WAV | SF_FORMAT_PCM_16) ; + test_count++ ; + } ; + + if (test_count == 0) + { printf ("Mono : ************************************\n") ; + printf ("Mono : * No '%s' test defined.\n", argv [1]) ; + printf ("Mono : ************************************\n") ; + return 1 ; + } ; + + return 0 ; +} /* main */ + +/*============================================================================================ +** Here are the test functions. +*/ + +static void +float_norm_test (const char *filename) +{ SNDFILE *file ; + SF_INFO sfinfo ; + unsigned int k ; + + print_test_name ("float_norm_test", filename) ; + + sfinfo.samplerate = 44100 ; + sfinfo.format = (SF_FORMAT_RAW | SF_FORMAT_PCM_16) ; + sfinfo.channels = 1 ; + sfinfo.frames = BUFFER_LEN ; + + /* Create float_data with all values being less than 1.0. */ + for (k = 0 ; k < BUFFER_LEN / 2 ; k++) + float_data [k] = (k + 5) / (2.0 * BUFFER_LEN) ; + for (k = BUFFER_LEN / 2 ; k < BUFFER_LEN ; k++) + float_data [k] = (k + 5) ; + + if (! (file = sf_open (filename, SFM_WRITE, &sfinfo))) + { printf ("Line %d: sf_open_write failed with error : ", __LINE__) ; + fflush (stdout) ; + puts (sf_strerror (NULL)) ; + exit (1) ; + } ; + + /* Normalisation is on by default so no need to do anything here. */ + + if ((k = sf_write_float (file, float_data, BUFFER_LEN / 2)) != BUFFER_LEN / 2) + { printf ("Line %d: sf_write_float failed with short write (%d ->%d)\n", __LINE__, BUFFER_LEN, k) ; + exit (1) ; + } ; + + /* Turn normalisation off. */ + sf_command (file, SFC_SET_NORM_FLOAT, NULL, SF_FALSE) ; + + if ((k = sf_write_float (file, float_data + BUFFER_LEN / 2, BUFFER_LEN / 2)) != BUFFER_LEN / 2) + { printf ("Line %d: sf_write_float failed with short write (%d ->%d)\n", __LINE__, BUFFER_LEN, k) ; + exit (1) ; + } ; + + sf_close (file) ; + + /* sfinfo struct should still contain correct data. */ + if (! (file = sf_open (filename, SFM_READ, &sfinfo))) + { printf ("Line %d: sf_open_read failed with error : ", __LINE__) ; + fflush (stdout) ; + puts (sf_strerror (NULL)) ; + exit (1) ; + } ; + + if (sfinfo.format != (SF_FORMAT_RAW | SF_FORMAT_PCM_16)) + { printf ("Line %d: Returned format incorrect (0x%08X => 0x%08X).\n", __LINE__, (SF_FORMAT_RAW | SF_FORMAT_PCM_16), sfinfo.format) ; + exit (1) ; + } ; + + if (sfinfo.frames != BUFFER_LEN) + { printf ("\n\nLine %d: Incorrect number of.frames in file. (%d => %ld)\n", __LINE__, BUFFER_LEN, SF_COUNT_TO_LONG (sfinfo.frames)) ; + exit (1) ; + } ; + + if (sfinfo.channels != 1) + { printf ("Line %d: Incorrect number of channels in file.\n", __LINE__) ; + exit (1) ; + } ; + + /* Read float_data and check that it is normalised (ie default). */ + if ((k = sf_read_float (file, float_data, BUFFER_LEN)) != BUFFER_LEN) + { printf ("\n\nLine %d: sf_read_float failed with short read (%d ->%d)\n", __LINE__, BUFFER_LEN, k) ; + exit (1) ; + } ; + + for (k = 0 ; k < BUFFER_LEN ; k++) + if (float_data [k] >= 1.0) + { printf ("\n\nLine %d: float_data [%d] == %f which is greater than 1.0\n", __LINE__, k, float_data [k]) ; + exit (1) ; + } ; + + /* Seek to start of file, turn normalisation off, read float_data and check again. */ + sf_seek (file, 0, SEEK_SET) ; + sf_command (file, SFC_SET_NORM_FLOAT, NULL, SF_FALSE) ; + + if ((k = sf_read_float (file, float_data, BUFFER_LEN)) != BUFFER_LEN) + { printf ("\n\nLine %d: sf_read_float failed with short read (%d ->%d)\n", __LINE__, BUFFER_LEN, k) ; + exit (1) ; + } ; + + for (k = 0 ; k < BUFFER_LEN ; k++) + if (float_data [k] < 1.0) + { printf ("\n\nLine %d: float_data [%d] == %f which is less than 1.0\n", __LINE__, k, float_data [k]) ; + exit (1) ; + } ; + + /* Seek to start of file, turn normalisation on, read float_data and do final check. */ + sf_seek (file, 0, SEEK_SET) ; + sf_command (file, SFC_SET_NORM_FLOAT, NULL, SF_TRUE) ; + + if ((k = sf_read_float (file, float_data, BUFFER_LEN)) != BUFFER_LEN) + { printf ("\n\nLine %d: sf_read_float failed with short read (%d ->%d)\n", __LINE__, BUFFER_LEN, k) ; + exit (1) ; + } ; + + for (k = 0 ; k < BUFFER_LEN ; k++) + if (float_data [k] > 1.0) + { printf ("\n\nLine %d: float_data [%d] == %f which is greater than 1.0\n", __LINE__, k, float_data [k]) ; + exit (1) ; + } ; + + + sf_close (file) ; + + unlink (filename) ; + + printf ("ok\n") ; +} /* float_norm_test */ + +static void +double_norm_test (const char *filename) +{ SNDFILE *file ; + SF_INFO sfinfo ; + unsigned int k ; + + print_test_name ("double_norm_test", filename) ; + + sfinfo.samplerate = 44100 ; + sfinfo.format = (SF_FORMAT_RAW | SF_FORMAT_PCM_16) ; + sfinfo.channels = 1 ; + sfinfo.frames = BUFFER_LEN ; + + /* Create double_data with all values being less than 1.0. */ + for (k = 0 ; k < BUFFER_LEN / 2 ; k++) + double_data [k] = (k + 5) / (2.0 * BUFFER_LEN) ; + for (k = BUFFER_LEN / 2 ; k < BUFFER_LEN ; k++) + double_data [k] = (k + 5) ; + + if (! (file = sf_open (filename, SFM_WRITE, &sfinfo))) + { printf ("Line %d: sf_open_write failed with error : ", __LINE__) ; + fflush (stdout) ; + puts (sf_strerror (NULL)) ; + exit (1) ; + } ; + + /* Normailsation is on by default so no need to do anything here. */ + /*-sf_command (file, "set-norm-double", "true", 0) ;-*/ + + if ((k = sf_write_double (file, double_data, BUFFER_LEN / 2)) != BUFFER_LEN / 2) + { printf ("Line %d: sf_write_double failed with short write (%d ->%d)\n", __LINE__, BUFFER_LEN, k) ; + exit (1) ; + } ; + + /* Turn normalisation off. */ + sf_command (file, SFC_SET_NORM_DOUBLE, NULL, SF_FALSE) ; + + if ((k = sf_write_double (file, double_data + BUFFER_LEN / 2, BUFFER_LEN / 2)) != BUFFER_LEN / 2) + { printf ("Line %d: sf_write_double failed with short write (%d ->%d)\n", __LINE__, BUFFER_LEN, k) ; + exit (1) ; + } ; + + sf_close (file) ; + + if (! (file = sf_open (filename, SFM_READ, &sfinfo))) + { printf ("Line %d: sf_open_read failed with error : ", __LINE__) ; + fflush (stdout) ; + puts (sf_strerror (NULL)) ; + exit (1) ; + } ; + + if (sfinfo.format != (SF_FORMAT_RAW | SF_FORMAT_PCM_16)) + { printf ("Line %d: Returned format incorrect (0x%08X => 0x%08X).\n", __LINE__, (SF_FORMAT_RAW | SF_FORMAT_PCM_16), sfinfo.format) ; + exit (1) ; + } ; + + if (sfinfo.frames != BUFFER_LEN) + { printf ("\n\nLine %d: Incorrect number of.frames in file. (%d => %ld)\n", __LINE__, BUFFER_LEN, SF_COUNT_TO_LONG (sfinfo.frames)) ; + exit (1) ; + } ; + + if (sfinfo.channels != 1) + { printf ("Line %d: Incorrect number of channels in file.\n", __LINE__) ; + exit (1) ; + } ; + + /* Read double_data and check that it is normalised (ie default). */ + if ((k = sf_read_double (file, double_data, BUFFER_LEN)) != BUFFER_LEN) + { printf ("\n\nLine %d: sf_read_double failed with short read (%d ->%d)\n", __LINE__, BUFFER_LEN, k) ; + exit (1) ; + } ; + + for (k = 0 ; k < BUFFER_LEN ; k++) + if (double_data [k] >= 1.0) + { printf ("\n\nLine %d: double_data [%d] == %f which is greater than 1.0\n", __LINE__, k, double_data [k]) ; + exit (1) ; + } ; + + /* Seek to start of file, turn normalisation off, read double_data and check again. */ + sf_seek (file, 0, SEEK_SET) ; + sf_command (file, SFC_SET_NORM_DOUBLE, NULL, SF_FALSE) ; + + if ((k = sf_read_double (file, double_data, BUFFER_LEN)) != BUFFER_LEN) + { printf ("\n\nLine %d: sf_read_double failed with short read (%d ->%d)\n", __LINE__, BUFFER_LEN, k) ; + exit (1) ; + } ; + + for (k = 0 ; k < BUFFER_LEN ; k++) + if (double_data [k] < 1.0) + { printf ("\n\nLine %d: double_data [%d] == %f which is less than 1.0\n", __LINE__, k, double_data [k]) ; + exit (1) ; + } ; + + /* Seek to start of file, turn normalisation on, read double_data and do final check. */ + sf_seek (file, 0, SEEK_SET) ; + sf_command (file, SFC_SET_NORM_DOUBLE, NULL, SF_TRUE) ; + + if ((k = sf_read_double (file, double_data, BUFFER_LEN)) != BUFFER_LEN) + { printf ("\n\nLine %d: sf_read_double failed with short read (%d ->%d)\n", __LINE__, BUFFER_LEN, k) ; + exit (1) ; + } ; + + for (k = 0 ; k < BUFFER_LEN ; k++) + if (double_data [k] > 1.0) + { printf ("\n\nLine %d: double_data [%d] == %f which is greater than 1.0\n", __LINE__, k, double_data [k]) ; + exit (1) ; + } ; + + + sf_close (file) ; + + unlink (filename) ; + + printf ("ok\n") ; +} /* double_norm_test */ + +static void +format_tests (void) +{ SF_FORMAT_INFO format_info ; + SF_INFO sfinfo ; + const char *last_name ; + int k, count ; + + print_test_name ("format_tests", "(null)") ; + + /* Clear out SF_INFO struct and set channels > 0. */ + memset (&sfinfo, 0, sizeof (sfinfo)) ; + sfinfo.channels = 1 ; + + /* First test simple formats. */ + + sf_command (NULL, SFC_GET_SIMPLE_FORMAT_COUNT, &count, sizeof (int)) ; + + if (count < 0 || count > 30) + { printf ("Line %d: Weird count.\n", __LINE__) ; + exit (1) ; + } ; + + format_info.format = 0 ; + sf_command (NULL, SFC_GET_SIMPLE_FORMAT, &format_info, sizeof (format_info)) ; + + last_name = format_info.name ; + for (k = 1 ; k < count ; k ++) + { format_info.format = k ; + sf_command (NULL, SFC_GET_SIMPLE_FORMAT, &format_info, sizeof (format_info)) ; + if (strcmp (last_name, format_info.name) >= 0) + { printf ("\n\nLine %d: format names out of sequence `%s' < `%s'.\n", __LINE__, last_name, format_info.name) ; + exit (1) ; + } ; + sfinfo.format = format_info.format ; + + if (! sf_format_check (&sfinfo)) + { printf ("\n\nLine %d: sf_format_check failed.\n", __LINE__) ; + printf (" Name : %s\n", format_info.name) ; + printf (" Format : 0x%X\n", sfinfo.format) ; + printf (" Channels : 0x%X\n", sfinfo.channels) ; + printf (" Sample Rate : 0x%X\n", sfinfo.samplerate) ; + exit (1) ; + } ; + last_name = format_info.name ; + } ; + format_info.format = 666 ; + sf_command (NULL, SFC_GET_SIMPLE_FORMAT, &format_info, sizeof (format_info)) ; + + /* Now test major formats. */ + sf_command (NULL, SFC_GET_FORMAT_MAJOR_COUNT, &count, sizeof (int)) ; + + if (count < 0 || count > 30) + { printf ("Line %d: Weird count.\n", __LINE__) ; + exit (1) ; + } ; + + format_info.format = 0 ; + sf_command (NULL, SFC_GET_FORMAT_MAJOR, &format_info, sizeof (format_info)) ; + + last_name = format_info.name ; + for (k = 1 ; k < count ; k ++) + { format_info.format = k ; + sf_command (NULL, SFC_GET_FORMAT_MAJOR, &format_info, sizeof (format_info)) ; + if (strcmp (last_name, format_info.name) >= 0) + { printf ("\n\nLine %d: format names out of sequence (%d) `%s' < `%s'.\n", __LINE__, k, last_name, format_info.name) ; + exit (1) ; + } ; + + last_name = format_info.name ; + } ; + format_info.format = 666 ; + sf_command (NULL, SFC_GET_FORMAT_MAJOR, &format_info, sizeof (format_info)) ; + + /* Now test subtype formats. */ + sf_command (NULL, SFC_GET_FORMAT_SUBTYPE_COUNT, &count, sizeof (int)) ; + + if (count < 0 || count > 30) + { printf ("Line %d: Weird count.\n", __LINE__) ; + exit (1) ; + } ; + + format_info.format = 0 ; + sf_command (NULL, SFC_GET_FORMAT_SUBTYPE, &format_info, sizeof (format_info)) ; + + last_name = format_info.name ; + for (k = 1 ; k < count ; k ++) + { format_info.format = k ; + sf_command (NULL, SFC_GET_FORMAT_SUBTYPE, &format_info, sizeof (format_info)) ; + } ; + format_info.format = 666 ; + sf_command (NULL, SFC_GET_FORMAT_SUBTYPE, &format_info, sizeof (format_info)) ; + + + printf ("ok\n") ; +} /* format_tests */ + +static void +calc_peak_test (int filetype, const char *filename) +{ SNDFILE *file ; + SF_INFO sfinfo ; + int k, format ; + double peak ; + + print_test_name ("calc_peak_test", filename) ; + + format = (filetype | SF_FORMAT_PCM_16) ; + + sfinfo.samplerate = 44100 ; + sfinfo.format = format ; + sfinfo.channels = 1 ; + sfinfo.frames = BUFFER_LEN ; + + /* Create double_data with max value of 0.5. */ + for (k = 0 ; k < BUFFER_LEN ; k++) + double_data [k] = (k + 1) / (2.0 * BUFFER_LEN) ; + + file = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, SF_TRUE, __LINE__) ; + + test_write_double_or_die (file, 0, double_data, BUFFER_LEN, __LINE__) ; + + sf_close (file) ; + + file = test_open_file_or_die (filename, SFM_READ, &sfinfo, SF_TRUE, __LINE__) ; + + if (sfinfo.format != format) + { printf ("Line %d: Returned format incorrect (0x%08X => 0x%08X).\n", __LINE__, format, sfinfo.format) ; + exit (1) ; + } ; + + if (sfinfo.frames != BUFFER_LEN) + { printf ("\n\nLine %d: Incorrect number of.frames in file. (%d => %ld)\n", __LINE__, BUFFER_LEN, SF_COUNT_TO_LONG (sfinfo.frames)) ; + exit (1) ; + } ; + + if (sfinfo.channels != 1) + { printf ("Line %d: Incorrect number of channels in file.\n", __LINE__) ; + exit (1) ; + } ; + + sf_command (file, SFC_CALC_SIGNAL_MAX, &peak, sizeof (peak)) ; + if (fabs (peak - (1 << 14)) > 1.0) + { printf ("Line %d : Peak value should be %d (is %f).\n", __LINE__, (1 << 14), peak) ; + exit (1) ; + } ; + + sf_command (file, SFC_CALC_NORM_SIGNAL_MAX, &peak, sizeof (peak)) ; + if (fabs (peak - 0.5) > 4e-5) + { printf ("Line %d : Peak value should be %f (is %f).\n", __LINE__, 0.5, peak) ; + exit (1) ; + } ; + + sf_close (file) ; + + format = (filetype | SF_FORMAT_FLOAT) ; + sfinfo.samplerate = 44100 ; + sfinfo.format = format ; + sfinfo.channels = 1 ; + sfinfo.frames = BUFFER_LEN ; + + /* Create double_data with max value of 0.5. */ + for (k = 0 ; k < BUFFER_LEN ; k++) + double_data [k] = (k + 1) / (2.0 * BUFFER_LEN) ; + + file = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, SF_TRUE, __LINE__) ; + + test_write_double_or_die (file, 0, double_data, BUFFER_LEN, __LINE__) ; + + sf_close (file) ; + + file = test_open_file_or_die (filename, SFM_READ, &sfinfo, SF_TRUE, __LINE__) ; + + if (sfinfo.format != format) + { printf ("Line %d: Returned format incorrect (0x%08X => 0x%08X).\n", __LINE__, format, sfinfo.format) ; + exit (1) ; + } ; + + if (sfinfo.frames != BUFFER_LEN) + { printf ("\n\nLine %d: Incorrect number of.frames in file. (%d => %ld)\n", __LINE__, BUFFER_LEN, SF_COUNT_TO_LONG (sfinfo.frames)) ; + exit (1) ; + } ; + + if (sfinfo.channels != 1) + { printf ("Line %d: Incorrect number of channels in file.\n", __LINE__) ; + exit (1) ; + } ; + + sf_command (file, SFC_CALC_SIGNAL_MAX, &peak, sizeof (peak)) ; + if (fabs (peak - 0.5) > 1e-5) + { printf ("Line %d : Peak value should be %f (is %f).\n", __LINE__, 0.5, peak) ; + exit (1) ; + } ; + + sf_command (file, SFC_CALC_NORM_SIGNAL_MAX, &peak, sizeof (peak)) ; + if (fabs (peak - 0.5) > 1e-5) + { printf ("Line %d : Peak value should be %f (is %f).\n", __LINE__, 0.5, peak) ; + exit (1) ; + } ; + + sf_close (file) ; + + unlink (filename) ; + + printf ("ok\n") ; +} /* calc_peak_test */ + +static void +truncate_test (const char *filename, int filetype) +{ SNDFILE *file ; + SF_INFO sfinfo ; + sf_count_t len ; + + print_test_name ("truncate_test", filename) ; + + sfinfo.samplerate = 11025 ; + sfinfo.format = filetype ; + sfinfo.channels = 2 ; + + file = test_open_file_or_die (filename, SFM_RDWR, &sfinfo, SF_TRUE, __LINE__) ; + + test_write_int_or_die (file, 0, int_data, BUFFER_LEN, __LINE__) ; + + len = 100 ; + if (sf_command (file, SFC_FILE_TRUNCATE, &len, sizeof (len))) + { printf ("Line %d: sf_command (SFC_FILE_TRUNCATE) returned error.\n", __LINE__) ; + exit (1) ; + } ; + + test_seek_or_die (file, 0, SEEK_CUR, len, 2, __LINE__) ; + test_seek_or_die (file, 0, SEEK_END, len, 2, __LINE__) ; + + sf_close (file) ; + + unlink (filename) ; + puts ("ok") ; +} /* truncate_test */ + +static void +instrument_test (const char *filename, int filetype) +{ static SF_INSTRUMENT write_inst = + { 2, /* gain */ + 3, /* detune */ + 4, /* basenote */ + 5, 6, /* key low and high */ + 7, 8, /* velocity low and high */ + 2, /* loop_count */ + { { 801, 2, 3, 0 }, + { 801, 3, 4, 0 }, + } + } ; + SF_INSTRUMENT read_inst ; + SNDFILE *file ; + SF_INFO sfinfo ; + + print_test_name ("instrument_test", filename) ; + + sfinfo.samplerate = 11025 ; + sfinfo.format = filetype ; + sfinfo.channels = 1 ; + + file = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, SF_TRUE, __LINE__) ; + if (sf_command (file, SFC_SET_INSTRUMENT, &write_inst, sizeof (write_inst)) == SF_FALSE) + { printf ("\n\nLine %d : sf_command (SFC_SET_INSTRUMENT) failed.\n\n", __LINE__) ; + exit (1) ; + } ; + test_write_double_or_die (file, 0, double_data, BUFFER_LEN, __LINE__) ; + sf_close (file) ; + + memset (&read_inst, 0, sizeof (read_inst)) ; + + file = test_open_file_or_die (filename, SFM_READ, &sfinfo, SF_TRUE, __LINE__) ; + if (sf_command (file, SFC_GET_INSTRUMENT, &read_inst, sizeof (read_inst)) == SF_FALSE) + { printf ("\n\nLine %d : sf_command (SFC_GET_INSTRUMENT) failed.\n\n", __LINE__) ; + exit (1) ; + return ; + } ; + check_log_buffer_or_die (file, __LINE__) ; + sf_close (file) ; + + if ((filetype & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV) + { /* + ** For all the fields that WAV doesn't support, modify the + ** write_inst struct to hold the default value that the WAV + ** module should hold. + */ + write_inst.detune = 0 ; + write_inst.key_lo = write_inst.velocity_lo = 0 ; + write_inst.key_hi = write_inst.velocity_hi = 127 ; + write_inst.gain = 1 ; + } ; + + if ((filetype & SF_FORMAT_TYPEMASK) == SF_FORMAT_XI) + { /* + ** For all the fields that XI doesn't support, modify the + ** write_inst struct to hold the default value that the XI + ** module should hold. + */ + write_inst.basenote = 0 ; + write_inst.detune = 0 ; + write_inst.key_lo = write_inst.velocity_lo = 0 ; + write_inst.key_hi = write_inst.velocity_hi = 127 ; + write_inst.gain = 1 ; + } ; + + if (memcmp (&write_inst, &read_inst, sizeof (write_inst)) != 0) + { printf ("\n\nLine %d : instrument comparison failed.\n\n", __LINE__) ; + printf ("W Base Note : %u\n" + " Detune : %u\n" + " Low Note : %u\tHigh Note : %u\n" + " Low Vel. : %u\tHigh Vel. : %u\n" + " Gain : %d\tCount : %d\n" + " mode : %d\n" + " start : %d\tend : %d\tcount :%d\n" + " mode : %d\n" + " start : %d\tend : %d\tcount :%d\n\n", + write_inst.basenote, + write_inst.detune, + write_inst.key_lo, write_inst.key_hi, + write_inst.velocity_lo, write_inst.velocity_hi, + write_inst.gain, write_inst.loop_count, + write_inst.loops [0].mode, write_inst.loops [0].start, + write_inst.loops [0].end, write_inst.loops [0].count, + write_inst.loops [1].mode, write_inst.loops [1].start, + write_inst.loops [1].end, write_inst.loops [1].count) ; + printf ("R Base Note : %u\n" + " Detune : %u\n" + " Low Note : %u\tHigh Note : %u\n" + " Low Vel. : %u\tHigh Vel. : %u\n" + " Gain : %d\tCount : %d\n" + " mode : %d\n" + " start : %d\tend : %d\tcount :%d\n" + " mode : %d\n" + " start : %d\tend : %d\tcount :%d\n\n", + read_inst.basenote, + read_inst.detune, + read_inst.key_lo, read_inst.key_hi, + read_inst.velocity_lo, read_inst.velocity_hi, + read_inst.gain, read_inst.loop_count, + read_inst.loops [0].mode, read_inst.loops [0].start, + read_inst.loops [0].end, read_inst.loops [0].count, + read_inst.loops [1].mode, read_inst.loops [1].start, + read_inst.loops [1].end, read_inst.loops [1].count) ; + + if ((filetype & SF_FORMAT_TYPEMASK) != SF_FORMAT_XI) + exit (1) ; + } ; + + unlink (filename) ; + puts ("ok") ; +} /* instrument_test */ + +static void +broadcast_test (const char *filename, int filetype) +{ static SF_BROADCAST_INFO bc_write, bc_read ; + SNDFILE *file ; + SF_INFO sfinfo ; + int errors = 0 ; + + print_test_name ("broadcast_test", filename) ; + + sfinfo.samplerate = 11025 ; + sfinfo.format = filetype ; + sfinfo.channels = 1 ; + + memset (&bc_write, 0, sizeof (bc_write)) ; + + snprintf (bc_write.description, sizeof (bc_write.description), "Test description") ; + snprintf (bc_write.originator, sizeof (bc_write.originator), "Test originator") ; + snprintf (bc_write.originator_reference, sizeof (bc_write.originator_reference), "%08x-%08x", (unsigned int) time (NULL), (unsigned int) (~ time (NULL))) ; + snprintf (bc_write.origination_date, sizeof (bc_write.origination_date), "%d/%02d/%02d", 2006, 3, 30) ; + snprintf (bc_write.origination_time, sizeof (bc_write.origination_time), "%02d:%02d:%02d", 20, 27, 0) ; + snprintf (bc_write.umid, sizeof (bc_write.umid), "Some umid") ; + bc_write.coding_history_size = 0 ; + + file = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, SF_TRUE, __LINE__) ; + if (sf_command (file, SFC_SET_BROADCAST_INFO, &bc_write, sizeof (bc_write)) == SF_FALSE) + { printf ("\n\nLine %d : sf_command (SFC_SET_BROADCAST_INFO) failed.\n\n", __LINE__) ; + exit (1) ; + } ; + test_write_double_or_die (file, 0, double_data, BUFFER_LEN, __LINE__) ; + sf_close (file) ; + + memset (&bc_read, 0, sizeof (bc_read)) ; + + file = test_open_file_or_die (filename, SFM_READ, &sfinfo, SF_TRUE, __LINE__) ; + if (sf_command (file, SFC_GET_BROADCAST_INFO, &bc_read, sizeof (bc_read)) == SF_FALSE) + { printf ("\n\nLine %d : sf_command (SFC_SET_BROADCAST_INFO) failed.\n\n", __LINE__) ; + exit (1) ; + return ; + } ; + check_log_buffer_or_die (file, __LINE__) ; + sf_close (file) ; + + if (bc_read.version != 1) + { printf ("\n\nLine %d : Read bad version number %d.\n\n", __LINE__, bc_read.version) ; + exit (1) ; + return ; + } ; + + bc_read.version = bc_write.version = 0 ; + + if (memcmp (bc_write.description, bc_read.description, sizeof (bc_write.description)) != 0) + { printf ("\n\nLine %d : description mismatch :\n\twrite : '%s'\n\tread : '%s'\n\n", __LINE__, bc_write.description, bc_read.description) ; + errors ++ ; + } ; + + if (memcmp (bc_write.originator, bc_read.originator, sizeof (bc_write.originator)) != 0) + { printf ("\n\nLine %d : originator mismatch :\n\twrite : '%s'\n\tread : '%s'\n\n", __LINE__, bc_write.originator, bc_read.originator) ; + errors ++ ; + } ; + + if (memcmp (bc_write.originator_reference, bc_read.originator_reference, sizeof (bc_write.originator_reference)) != 0) + { printf ("\n\nLine %d : originator_reference mismatch :\n\twrite : '%s'\n\tread : '%s'\n\n", __LINE__, bc_write.originator_reference, bc_read.originator_reference) ; + errors ++ ; + } ; + + if (memcmp (bc_write.origination_date, bc_read.origination_date, sizeof (bc_write.origination_date)) != 0) + { printf ("\n\nLine %d : origination_date mismatch :\n\twrite : '%s'\n\tread : '%s'\n\n", __LINE__, bc_write.origination_date, bc_read.origination_date) ; + errors ++ ; + } ; + + if (memcmp (bc_write.origination_time, bc_read.origination_time, sizeof (bc_write.origination_time)) != 0) + { printf ("\n\nLine %d : origination_time mismatch :\n\twrite : '%s'\n\tread : '%s'\n\n", __LINE__, bc_write.origination_time, bc_read.origination_time) ; + errors ++ ; + } ; + + if (memcmp (bc_write.umid, bc_read.umid, sizeof (bc_write.umid)) != 0) + { printf ("\n\nLine %d : umid mismatch :\n\twrite : '%s'\n\tread : '%s'\n\n", __LINE__, bc_write.umid, bc_read.umid) ; + errors ++ ; + } ; + + if (errors) + exit (1) ; + + unlink (filename) ; + puts ("ok") ; +} /* broadcast_test */ + +static void +channel_map_test (const char *filename, int filetype) +{ SNDFILE *file ; + SF_INFO sfinfo ; + int channel_map_read [4], channel_map_write [4] = + { SF_CHANNEL_MAP_FRONT_LEFT, SF_CHANNEL_MAP_FRONT_CENTER, + SF_CHANNEL_MAP_REAR_LEFT, SF_CHANNEL_MAP_REAR_RIGHT + } ; + + print_test_name ("channel_map_test", filename) ; + + memset (&sfinfo, 0, sizeof (sfinfo)) ; + sfinfo.samplerate = 11025 ; + sfinfo.format = filetype ; + sfinfo.channels = ARRAY_LEN (channel_map_read) ; + + /* Write file without channel map. */ + file = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, SF_TRUE, __LINE__) ; + test_write_double_or_die (file, 0, double_data, BUFFER_LEN, __LINE__) ; + sf_close (file) ; + + /* Read file making sure no channel map exists. */ + file = test_open_file_or_die (filename, SFM_READ, &sfinfo, SF_TRUE, __LINE__) ; + exit_if_true ( + sf_command (file, SFC_GET_CHANNEL_MAP_INFO, channel_map_read, sizeof (channel_map_read)) != SF_FALSE, + "\n\nLine %d : sf_command (SFC_GET_CHANNEL_MAP_INFO) should have failed.\n\n", __LINE__ + ) ; + check_log_buffer_or_die (file, __LINE__) ; + sf_close (file) ; + + /* Write file with a channel map. */ + file = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, SF_TRUE, __LINE__) ; + test_write_double_or_die (file, 0, double_data, BUFFER_LEN, __LINE__) ; + exit_if_true ( + sf_command (file, SFC_SET_CHANNEL_MAP_INFO, channel_map_write, sizeof (channel_map_write)) == SF_FALSE, + "\n\nLine %d : sf_command (SFC_SET_CHANNEL_MAP_INFO) failed.\n\n", __LINE__ + ) ; + sf_close (file) ; + + /* Read file making sure no channel map exists. */ + file = test_open_file_or_die (filename, SFM_READ, &sfinfo, SF_TRUE, __LINE__) ; + exit_if_true ( + sf_command (file, SFC_GET_CHANNEL_MAP_INFO, channel_map_read, sizeof (channel_map_read)) != SF_TRUE, + "\n\nLine %d : sf_command (SFC_GET_CHANNEL_MAP_INFO) failed.\n\n", __LINE__ + ) ; + check_log_buffer_or_die (file, __LINE__) ; + sf_close (file) ; + + exit_if_true ( + memcmp (channel_map_read, channel_map_write, sizeof (channel_map_read)) != 0, + "\n\nLine %d : Channel map read does not match channel map written.\n\n", __LINE__ + ) ; + + unlink (filename) ; + puts ("ok") ; +} /* channel_map_test */ + + +/* +** Do not edit or modify anything in this comment block. +** The following line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 59e5d452-8dae-45aa-99aa-b78dc0deba1c +*/ diff --git a/tests/cpp_crack_test.cc b/tests/cpp_crack_test.cc new file mode 100644 index 00000000..0115ebb3 --- /dev/null +++ b/tests/cpp_crack_test.cc @@ -0,0 +1,144 @@ +/* +** Copyright (C) 2006 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include +#include + +#include + +#include "utils.h" + +static short sbuffer [100] ; +static int ibuffer [100] ; +static float fbuffer [100] ; +static double dbuffer [100] ; + +static void +create_file (const std::string& filename, int format) +{ Sndfile::Handle file (filename, SFM_WRITE, format, 2, 48000) ; + + setString (file, SF_STR_TITLE, filename) ; + + /* Item write. */ + write (file, sbuffer, ARRAY_LEN (sbuffer)) ; + write (file, ibuffer, ARRAY_LEN (ibuffer)) ; + write (file, fbuffer, ARRAY_LEN (fbuffer)) ; + write (file, dbuffer, ARRAY_LEN (dbuffer)) ; + + /* Frame write. */ + writef (file, sbuffer, ARRAY_LEN (sbuffer) / channels (file)) ; + writef (file, ibuffer, ARRAY_LEN (ibuffer) / channels (file)) ; + writef (file, fbuffer, ARRAY_LEN (fbuffer) / channels (file)) ; + writef (file, dbuffer, ARRAY_LEN (dbuffer) / channels (file)) ; + + /* + ** An explicit close() call is not necessary as the + ** Sndfile::Handle destructor closes the file anyway. + */ +} /* create_file */ + +static void +read_file (const std::string& filename, int frmt) +{ Sndfile::Handle file ; + std::string title ; + sf_count_t count ; + + file = Sndfile::open (filename) ; + + if (format (file) != frmt) + { printf ("\n\n%s %d : Error : format 0x%08x should be 0x%08x.\n\n", __func__, __LINE__, format (file), frmt) ; + exit (1) ; + } ; + + if (channels (file) != 2) + { printf ("\n\n%s %d : Error : channels %d should be 2.\n\n", __func__, __LINE__, channels (file)) ; + exit (1) ; + } ; + + if (frames (file) != ARRAY_LEN (sbuffer) * 4) + { printf ("\n\n%s %d : Error : frames %ld should be %d.\n\n", __func__, __LINE__, + SF_COUNT_TO_LONG (frames (file)), ARRAY_LEN (sbuffer) * 4 / 2) ; + exit (1) ; + } ; + + title = getString (file, SF_STR_TITLE) ; + + if (title == "") + { printf ("\n\n%s %d : Error : No title.\n\n", __func__, __LINE__) ; + exit (1) ; + } ; + + if (filename != title) + { printf ("\n\n%s %d : Error : title '%s' should be '%s'\n\n", __func__, __LINE__, title.c_str(), filename.c_str()) ; + exit (1) ; + } ; + + /* Item read. */ + read (file, sbuffer, ARRAY_LEN (sbuffer)) ; + read (file, ibuffer, ARRAY_LEN (ibuffer)) ; + read (file, fbuffer, ARRAY_LEN (fbuffer)) ; + read (file, dbuffer, ARRAY_LEN (dbuffer)) ; + + /* Frame read. */ + readf (file, sbuffer, ARRAY_LEN (sbuffer) / channels (file)) ; + readf (file, ibuffer, ARRAY_LEN (ibuffer) / channels (file)) ; + readf (file, fbuffer, ARRAY_LEN (fbuffer) / channels (file)) ; + readf (file, dbuffer, ARRAY_LEN (dbuffer) / channels (file)) ; + + count = seek (file, frames (file) - 10, SEEK_SET) ; + if (count != frames (file) - 10) + { printf ("\n\n%s %d : Error : offset (%ld) should be %ld\n\n", __func__, __LINE__, + SF_COUNT_TO_LONG (count), SF_COUNT_TO_LONG (frames (file) - 10)) ; + exit (1) ; + } ; + + count = read (file, sbuffer, ARRAY_LEN (sbuffer)) ; + if (count != 10 * channels (file)) + { printf ("\n\n%s %d : Error : count (%ld) should be %ld\n\n", __func__, __LINE__, + SF_COUNT_TO_LONG (count), SF_COUNT_TO_LONG (10 * channels (file))) ; + exit (1) ; + } ; + + /* + ** An explicit close() call is not necessary as the + ** Sndfile::Handle destructor closes the file anyway. + */ +} /* create_file */ + +int +main (void) +{ const char * filename = "cpp_test.wav" ; + + print_test_name ("CeePlusPlus test", filename) ; + + create_file (filename, SF_FORMAT_WAV | SF_FORMAT_PCM_16) ; + read_file (filename, SF_FORMAT_WAV | SF_FORMAT_PCM_16) ; + + remove (filename) ; + puts ("ok") ; + return 0 ; +} /* main */ + +/* +** Do not edit or modify anything in this comment block. +** The following line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 9600906c-fd77-4fdd-a25e-c21a1776ef1f +*/ diff --git a/tests/cpp_test.cc b/tests/cpp_test.cc new file mode 100644 index 00000000..9108aa1d --- /dev/null +++ b/tests/cpp_test.cc @@ -0,0 +1,197 @@ +/* +** Copyright (C) 2006 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include +#include + +#include + +#include "utils.h" + +static short sbuffer [100] ; +static int ibuffer [100] ; +static float fbuffer [100] ; +static double dbuffer [100] ; + +static void +create_file (const char * filename, int format) +{ SndfileHandle file ; + + if (file.refCount () != 0) + { printf ("\n\n%s %d : Error : Reference count (%d) should be zero.\n\n", __func__, __LINE__, file.refCount ()) ; + exit (1) ; + } ; + + file = SndfileHandle (filename, SFM_WRITE, format, 2, 48000) ; + + if (file.refCount () != 1) + { printf ("\n\n%s %d : Error : Reference count (%d) should be 1.\n\n", __func__, __LINE__, file.refCount ()) ; + exit (1) ; + } ; + + file.setString (SF_STR_TITLE, filename) ; + + /* Item write. */ + file.write (sbuffer, ARRAY_LEN (sbuffer)) ; + file.write (ibuffer, ARRAY_LEN (ibuffer)) ; + file.write (fbuffer, ARRAY_LEN (fbuffer)) ; + file.write (dbuffer, ARRAY_LEN (dbuffer)) ; + + /* Frame write. */ + file.writef (sbuffer, ARRAY_LEN (sbuffer) / file.channels ()) ; + file.writef (ibuffer, ARRAY_LEN (ibuffer) / file.channels ()) ; + file.writef (fbuffer, ARRAY_LEN (fbuffer) / file.channels ()) ; + file.writef (dbuffer, ARRAY_LEN (dbuffer) / file.channels ()) ; + + /* RAII takes care of the SndfileHandle. */ +} /* create_file */ + +static void +check_title (const SndfileHandle & file, const char * filename) +{ const char *title = NULL ; + + title = file.getString (SF_STR_TITLE) ; + + if (title == NULL) + { printf ("\n\n%s %d : Error : No title.\n\n", __func__, __LINE__) ; + exit (1) ; + } ; + + if (strcmp (filename, title) != 0) + { printf ("\n\n%s %d : Error : title '%s' should be '%s'\n\n", __func__, __LINE__, title, filename) ; + exit (1) ; + } ; + + return ; +} /* check_title */ + +static void +read_file (const char * filename, int format) +{ SndfileHandle file ; + sf_count_t count ; + + if (file) + { printf ("\n\n%s %d : Error : should not be here.\n\n", __func__, __LINE__) ; + exit (1) ; + } ; + + file = SndfileHandle (filename) ; + + if (1) + { SndfileHandle file2 = file ; + + if (file.refCount () != 2 || file2.refCount () != 2) + { printf ("\n\n%s %d : Error : Reference count (%d) should be two.\n\n", __func__, __LINE__, file.refCount ()) ; + exit (1) ; + } ; + } ; + + if (file.refCount () != 1) + { printf ("\n\n%s %d : Error : Reference count (%d) should be one.\n\n", __func__, __LINE__, file.refCount ()) ; + exit (1) ; + } ; + + if (! file) + { printf ("\n\n%s %d : Error : should not be here.\n\n", __func__, __LINE__) ; + exit (1) ; + } ; + + if (file.format () != format) + { printf ("\n\n%s %d : Error : format 0x%08x should be 0x%08x.\n\n", __func__, __LINE__, file.format (), format) ; + exit (1) ; + } ; + + if (file.channels () != 2) + { printf ("\n\n%s %d : Error : channels %d should be 2.\n\n", __func__, __LINE__, file.channels ()) ; + exit (1) ; + } ; + + if (file.frames () != ARRAY_LEN (sbuffer) * 4) + { printf ("\n\n%s %d : Error : frames %ld should be %lu.\n\n", __func__, __LINE__, + SF_COUNT_TO_LONG (file.frames ()), (long unsigned int) ARRAY_LEN (sbuffer) * 4 / 2) ; + exit (1) ; + } ; + + switch (format & SF_FORMAT_TYPEMASK) + { case SF_FORMAT_AU : + break ; + + default : + check_title (file, filename) ; + break ; + } ; + + /* Item read. */ + file.read (sbuffer, ARRAY_LEN (sbuffer)) ; + file.read (ibuffer, ARRAY_LEN (ibuffer)) ; + file.read (fbuffer, ARRAY_LEN (fbuffer)) ; + file.read (dbuffer, ARRAY_LEN (dbuffer)) ; + + /* Frame read. */ + file.readf (sbuffer, ARRAY_LEN (sbuffer) / file.channels ()) ; + file.readf (ibuffer, ARRAY_LEN (ibuffer) / file.channels ()) ; + file.readf (fbuffer, ARRAY_LEN (fbuffer) / file.channels ()) ; + file.readf (dbuffer, ARRAY_LEN (dbuffer) / file.channels ()) ; + + count = file.seek (file.frames () - 10, SEEK_SET) ; + if (count != file.frames () - 10) + { printf ("\n\n%s %d : Error : offset (%ld) should be %ld\n\n", __func__, __LINE__, + SF_COUNT_TO_LONG (count), SF_COUNT_TO_LONG (file.frames () - 10)) ; + exit (1) ; + } ; + + count = file.read (sbuffer, ARRAY_LEN (sbuffer)) ; + if (count != 10 * file.channels ()) + { printf ("\n\n%s %d : Error : count (%ld) should be %ld\n\n", __func__, __LINE__, + SF_COUNT_TO_LONG (count), SF_COUNT_TO_LONG (10 * file.channels ())) ; + exit (1) ; + } ; + + /* RAII takes care of the SndfileHandle. */ +} /* read_file */ + +static void +ceeplusplus_test (const char *filename, int format) +{ + print_test_name ("ceeplusplus_test", filename) ; + + create_file (filename, format) ; + read_file (filename, format) ; + + remove (filename) ; + puts ("ok") ; +} /* ceeplusplus_test */ + +int +main (void) +{ + ceeplusplus_test ("cpp_test.wav", SF_FORMAT_WAV | SF_FORMAT_PCM_16) ; + ceeplusplus_test ("cpp_test.aiff", SF_FORMAT_AIFF | SF_FORMAT_PCM_S8) ; + ceeplusplus_test ("cpp_test.au", SF_FORMAT_AU | SF_FORMAT_FLOAT) ; + + return 0 ; +} /* main */ + +/* +** Do not edit or modify anything in this comment block. +** The following line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 06e48ee6-b19d-4453-9999-a5cf2d7bf0b6 +*/ diff --git a/tests/dft_cmp.c b/tests/dft_cmp.c new file mode 100644 index 00000000..cf803385 --- /dev/null +++ b/tests/dft_cmp.c @@ -0,0 +1,137 @@ +/* +** Copyright (C) 2002-2004 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include +#include + +#include "dft_cmp.h" +#include "utils.h" + +#ifndef M_PI +#define M_PI 3.14159265358979323846264338 +#endif + +#define DFT_SPEC_LENGTH (DFT_DATA_LENGTH / 2) + +static void dft_magnitude (const double *data, double *spectrum) ; +static double calc_max_spectral_difference (double *spec1, double *spec2) ; + +/*-------------------------------------------------------------------------------- +** Public functions. +*/ + +double +dft_cmp (int linenum, double *orig, double *test, int len, double target_snr, int allow_exit) +{ static double orig_spec [DFT_SPEC_LENGTH] ; + static double test_spec [DFT_SPEC_LENGTH] ; + double snr ; + + if (! orig || ! test) + { printf ("Error (line % d) : dft_cmp : Bad input arrays.\n", linenum) ; + return 1 ; + } ; + + if (len != DFT_DATA_LENGTH) + { printf ("Error (line % d) : dft_cmp : Bad input array length.\n", linenum) ; + return 1 ; + } ; + + dft_magnitude (orig, orig_spec) ; + dft_magnitude (test, test_spec) ; + + snr = calc_max_spectral_difference (orig_spec, test_spec) ; + + if (snr > target_snr) + { printf ("\n\nLine %d: Actual SNR (% 4.1f) > target SNR (% 4.1f).\n\n", linenum, snr, target_snr) ; + oct_save_double (orig, test, len) ; + if (allow_exit) + exit (1) ; + } ; + + if (snr < -500.0) + snr = -500.0 ; + + return snr ; +} /* dft_cmp */ + +/*-------------------------------------------------------------------------------- +** Quick dirty calculation of magnitude spectrum for real valued data using +** Discrete Fourier Transform. Since the data is real, the DFT is only +** calculated for positive frequencies. +*/ + +static void +dft_magnitude (const double *data, double *spectrum) +{ static double cos_angle [DFT_DATA_LENGTH] = { 0.0 } ; + static double sin_angle [DFT_DATA_LENGTH] ; + + double real_part, imag_part ; + int k, n ; + + /* If sine and cosine tables haven't been initialised, do so. */ + if (cos_angle [0] == 0.0) + for (n = 0 ; n < DFT_DATA_LENGTH ; n++) + { cos_angle [n] = cos (2.0 * M_PI * n / DFT_DATA_LENGTH) ; + sin_angle [n] = -1.0 * sin (2.0 * M_PI * n / DFT_DATA_LENGTH) ; + } ; + + /* DFT proper. Since the data is real, only generate a half spectrum. */ + for (k = 1 ; k < DFT_SPEC_LENGTH ; k++) + { real_part = 0.0 ; + imag_part = 0.0 ; + + for (n = 0 ; n < DFT_DATA_LENGTH ; n++) + { real_part += data [n] * cos_angle [(k * n) % DFT_DATA_LENGTH] ; + imag_part += data [n] * sin_angle [(k * n) % DFT_DATA_LENGTH] ; + } ; + + spectrum [k] = sqrt (real_part * real_part + imag_part * imag_part) ; + } ; + + spectrum [k] = 0.0 ; + + spectrum [0] = spectrum [1] = spectrum [2] = spectrum [3] = spectrum [4] = 0.0 ; + + return ; +} /* dft_magnitude */ + +static double +calc_max_spectral_difference (double *orig, double *test) +{ double orig_max = 0.0, max_diff = 0.0 ; + int k ; + + for (k = 0 ; k < DFT_SPEC_LENGTH ; k++) + { if (orig_max < orig [k]) + orig_max = orig [k] ; + if (max_diff < fabs (orig [k] - test [k])) + max_diff = fabs (orig [k] - test [k]) ; + } ; + + if (max_diff < 1e-25) + return -500.0 ; + + return 20.0 * log10 (max_diff / orig_max) ; +} /* calc_max_spectral_difference */ +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: cba7ffe2-bafe-44bd-b57a-15def3408410 +*/ diff --git a/tests/dft_cmp.h b/tests/dft_cmp.h new file mode 100644 index 00000000..1fb63c5c --- /dev/null +++ b/tests/dft_cmp.h @@ -0,0 +1,30 @@ +/* +** Copyright (C) 2002-2004 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + + +#define DFT_DATA_LENGTH (2048) + +double dft_cmp (int linenum, double *orig, double *test, int len, double tolerance, int allow_exit) ; + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 10eac7b2-04ef-4d86-b7c0-8f1de57b12a0 +*/ diff --git a/tests/dither_test.c b/tests/dither_test.c new file mode 100644 index 00000000..5aae3722 --- /dev/null +++ b/tests/dither_test.c @@ -0,0 +1,192 @@ +/* +** Copyright (C) 2003,2004 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + + + +#include +#include +#include +#include +#include + +#include + +#include "utils.h" + +#define BUFFER_LEN (1<<16) +#define LOG_BUFFER_SIZE 1024 + +static void dither_test (const char *filename, int filetype) ; + +/* Force the start of this buffer to be double aligned. Sparc-solaris will +** choke if its not. +*/ +static short data_out [BUFFER_LEN] ; + +int +main (int argc, char *argv []) +{ int do_all = 0 ; + int test_count = 0 ; + + if (argc != 2) + { printf ("Usage : %s \n", argv [0]) ; + printf (" Where is one of the following:\n") ; + printf (" wav - test WAV file peak chunk\n") ; + printf (" aiff - test AIFF file PEAK chunk\n") ; + printf (" all - perform all tests\n") ; + exit (1) ; + } ; + + do_all=!strcmp (argv [1], "all") ; + + if (do_all || ! strcmp (argv [1], "wav")) + { dither_test ("dither.wav", SF_FORMAT_WAV | SF_FORMAT_PCM_U8) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "aiff")) + { dither_test ("dither.aiff", SF_FORMAT_AIFF | SF_FORMAT_PCM_S8) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "au")) + { dither_test ("dither.au", SF_FORMAT_AU | SF_FORMAT_PCM_S8) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "svx")) + { dither_test ("dither.svx", SF_FORMAT_SVX | SF_FORMAT_PCM_S8) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "nist")) + { dither_test ("dither.nist", SF_FORMAT_NIST | SF_FORMAT_PCM_S8) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "paf")) + { dither_test ("dither.paf", SF_FORMAT_PAF | SF_FORMAT_PCM_S8) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "ircam")) + { dither_test ("dither.ircam", SF_FORMAT_IRCAM | SF_FORMAT_PCM_S8) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "voc")) + { dither_test ("dither.voc", SF_FORMAT_VOC | SF_FORMAT_PCM_S8) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "w64")) + { dither_test ("dither.w64", SF_FORMAT_W64 | SF_FORMAT_PCM_S8) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "mat4")) + { dither_test ("dither.mat4", SF_FORMAT_MAT4 | SF_FORMAT_PCM_S8) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "mat5")) + { dither_test ("dither.mat5", SF_FORMAT_MAT5 | SF_FORMAT_PCM_S8) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "pvf")) + { dither_test ("dither.pvf", SF_FORMAT_PVF | SF_FORMAT_PCM_S8) ; + test_count++ ; + } ; + + if (test_count == 0) + { printf ("Mono : ************************************\n") ; + printf ("Mono : * No '%s' test defined.\n", argv [1]) ; + printf ("Mono : ************************************\n") ; + return 1 ; + } ; + + return 0 ; +} /* main */ + + +/*============================================================================================ +** Here are the test functions. +*/ + +static void +dither_test (const char *filename, int filetype) +{ SNDFILE *file ; + SF_INFO sfinfo ; + SF_DITHER_INFO dither ; + int frames ; + + filetype = filetype ; + + print_test_name ("dither_test", filename) ; + + sfinfo.samplerate = 44100 ; + sfinfo.format = filetype ; + sfinfo.channels = 1 ; + sfinfo.frames = 0 ; + + frames = BUFFER_LEN / sfinfo.channels ; + + file = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, SF_TRUE, __LINE__) ; + + /* Check for old version of the dither API. */ + if (sf_command (file, SFC_SET_DITHER_ON_WRITE, NULL, SF_TRUE) == 0) + { printf ("\n\nLine %d: Should have an error here but don't.\n\n", __LINE__) ; + exit (1) ; + } ; + + memset (&dither, 0, sizeof (dither)) ; + dither.type = SFD_WHITE ; + dither.level = 0 ; + + if (sf_command (file, SFC_SET_DITHER_ON_WRITE, &dither, sizeof (dither)) != 0) + { printf ("\n\nLine %d: sf_command (SFC_SET_DITHER_ON_WRITE) returned error : %s\n\n", + __LINE__, sf_strerror (file)) ; + exit (1) ; + } ; + + /* Write data to file. */ + test_write_short_or_die (file, 0, data_out, BUFFER_LEN, __LINE__) ; + test_seek_or_die (file, 0, SEEK_SET, 0, sfinfo.channels, __LINE__) ; + + sf_close (file) ; + + file = test_open_file_or_die (filename, SFM_READ, &sfinfo, SF_TRUE, __LINE__) ; + + if (sfinfo.frames != BUFFER_LEN) + { printf ("\n\nLine %d: Bad frame count %d (should be %d)\n\n", __LINE__, (int) sfinfo.frames, BUFFER_LEN) ; + } ; + + sf_close (file) ; + /*-unlink (filename) ;-*/ + + puts ("ok") ; +} /* dither_test */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: ad688dd9-5211-48ef-b82d-089eafb1e7ad +*/ diff --git a/tests/dwvw_test.c b/tests/dwvw_test.c new file mode 100644 index 00000000..a4d9d15b --- /dev/null +++ b/tests/dwvw_test.c @@ -0,0 +1,119 @@ +/* +** Copyright (C) 2002-2004 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include + +#if HAVE_UNISTD_H +#include +#endif + +#include + +#include + +#include "utils.h" + +#define BUFFER_SIZE (10000) + +#ifndef M_PI +#define M_PI 3.14159265358979323846264338 +#endif + +static void dwvw_test (const char *filename, int format, int bit_width) ; + +int +main (void) +{ + dwvw_test ("dwvw12.raw", SF_FORMAT_RAW | SF_FORMAT_DWVW_12, 12) ; + dwvw_test ("dwvw16.raw", SF_FORMAT_RAW | SF_FORMAT_DWVW_16, 16) ; + dwvw_test ("dwvw24.raw", SF_FORMAT_RAW | SF_FORMAT_DWVW_24, 24) ; + + return 0 ; +} /* main */ + +static void +dwvw_test (const char *filename, int format, int bit_width) +{ static int write_buf [BUFFER_SIZE] ; + static int read_buf [BUFFER_SIZE] ; + + SNDFILE *file ; + SF_INFO sfinfo ; + double value ; + int k, bit_mask ; + + srand (123456) ; + + /* Only want to grab the top bit_width bits. */ + bit_mask = (-1 << (32 - bit_width)) ; + + print_test_name ("dwvw_test", filename) ; + + sfinfo.format = format ; + sfinfo.samplerate = 44100 ; + sfinfo.frames = -1 ; /* Unknown! */ + sfinfo.channels = 1 ; + + file = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, SF_TRUE, __LINE__) ; + + /* Generate random.frames. */ + k = 0 ; + for (k = 0 ; k < BUFFER_SIZE / 2 ; k++) + { value = 0x7FFFFFFF * sin (123.0 / sfinfo.samplerate * 2 * k * M_PI) ; + write_buf [k] = bit_mask & lrint (value) ; + } ; + + for ( ; k < BUFFER_SIZE ; k++) + write_buf [k] = bit_mask & ((rand () << 11) ^ (rand () >> 11)) ; + + sf_write_int (file, write_buf, BUFFER_SIZE) ; + sf_close (file) ; + + file = test_open_file_or_die (filename, SFM_READ, &sfinfo, SF_TRUE, __LINE__) ; + + if ((k = sf_read_int (file, read_buf, BUFFER_SIZE)) != BUFFER_SIZE) + { printf ("Error (line %d) : Only read %d/%d.frames.\n", __LINE__, k, BUFFER_SIZE) ; + exit (1) ; + } + + for (k = 0 ; k < BUFFER_SIZE ; k++) + { if (read_buf [k] != write_buf [k]) + { printf ("Error (line %d) : %d != %d at position %d/%d\n", __LINE__, + write_buf [k] >> (32 - bit_width), read_buf [k] >> (32 - bit_width), + k, BUFFER_SIZE) ; + oct_save_int (write_buf, read_buf, BUFFER_SIZE) ; + exit (1) ; + } ; + } ; + + sf_close (file) ; + + unlink (filename) ; + printf ("ok\n") ; + + return ; +} /* dwvw_test */ +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 9e7731c3-43cd-4de5-b539-1ce6fa11d937 +*/ diff --git a/tests/error_test.c b/tests/error_test.c new file mode 100644 index 00000000..a2c696aa --- /dev/null +++ b/tests/error_test.c @@ -0,0 +1,113 @@ +/* +** Copyright (C) 1999-2004 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include + +#if HAVE_UNISTD_H +#include +#endif + +#include + +#include "utils.h" + +#define BUFFER_SIZE (1<<15) +#define SHORT_BUFFER (256) + +static void error_number_test (void) ; +static void error_value_test (void) ; + +int +main (void) +{ error_number_test () ; + error_value_test () ; + + return 0 ; +} /* main */ + +static void +error_number_test (void) +{ const char *noerror, *errstr ; + int k ; + + print_test_name ("error_number_test", "") ; + + noerror = sf_error_number (0) ; + + for (k = 1 ; k < 300 ; k++) + { errstr = sf_error_number (k) ; + + /* Test for termination condition. */ + if (errstr == noerror) + break ; + + /* Test for error. */ + if (strstr (errstr, "This is a bug in libsndfile.")) + exit (1) ; + } ; + + + puts ("ok") ; + return ; +} /* error_number_test */ + +static void +error_value_test (void) +{ static unsigned char aiff_data [0x1b0] = + { 'F' , 'O' , 'R' , 'M' , + 0x00, 0x00, 0x01, 0xA8, /* FORM length */ + + 'A' , 'I' , 'F' , 'C' , + } ; + + const char *filename = "error.aiff" ; + SNDFILE *file ; + SF_INFO sfinfo ; + int error_num ; + + print_test_name ("error_value_test", filename) ; + + dump_data_to_file (filename, aiff_data, sizeof (aiff_data)) ; + + file = sf_open (filename, SFM_READ, &sfinfo) ; + if (file != NULL) + { printf ("\n\nLine %d : Should not have been able to open this file.\n\n", __LINE__) ; + exit (1) ; + } ; + + if ((error_num = sf_error (NULL)) <= 1 || error_num > 300) + { printf ("\n\nLine %d : Should not have had an error number of %d.\n\n", __LINE__, error_num) ; + exit (1) ; + } ; + + remove (filename) ; + puts ("ok") ; + return ; +} /* error_value_test */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 799eba74-b505-49d9-89a6-22a7f51a31b4 +*/ diff --git a/tests/fix_this.c b/tests/fix_this.c new file mode 100644 index 00000000..c0575a5a --- /dev/null +++ b/tests/fix_this.c @@ -0,0 +1,331 @@ +/* +** Copyright (C) 1999-2004 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include +#include +#include +#include +#include + +#include + +#include "utils.h" + +#define BUFFER_SIZE (1<<14) /* Should be (1<<14) */ +#define SAMPLE_RATE (11025) + +#ifndef M_PI +#define M_PI 3.14159265358979323846264338 +#endif + +static void lcomp_test_int (const char *str, const char *filename, int filetype, double margin) ; + +static int error_function (double data, double orig, double margin) ; +static int decay_response (int k) ; + +static void gen_signal_double (double *data, double scale, int datalen) ; + +/* Force the start of these buffers to be double aligned. Sparc-solaris will +** choke if they are not. +*/ + +typedef union +{ double d [BUFFER_SIZE + 1]; + int i [BUFFER_SIZE + 1]; +} BUFFER ; + +static BUFFER data_buffer ; +static BUFFER orig_buffer ; + +int +main (void) +{ const char *filename = "test.au" ; + + lcomp_test_int ("au_g721", filename, SF_ENDIAN_BIG | SF_FORMAT_AU | SF_FORMAT_G721_32, 0.06) ; + + return 0 ; +} /* main */ + +/*============================================================================================ +** Here are the test functions. +*/ + +static void +lcomp_test_int (const char *str, const char *filename, int filetype, double margin) +{ SNDFILE *file ; + SF_INFO sfinfo ; + int k, m, *orig, *data, sum_abs ; + long datalen, seekpos ; + double scale ; + + printf ("\nThis is program is not part of the libsndfile test suite.\n\n") ; + + printf (" lcomp_test_int : %s ... ", str) ; + fflush (stdout) ; + + datalen = BUFFER_SIZE ; + + scale = 1.0 * 0x10000 ; + + data = data_buffer.i ; + orig = orig_buffer.i ; + + gen_signal_double (orig_buffer.d, 32000.0 * scale, datalen) ; + for (k = 0 ; k < datalen ; k++) + orig [k] = orig_buffer.d [k] ; + + + sfinfo.samplerate = SAMPLE_RATE ; + sfinfo.frames = 123456789 ; /* Ridiculous value. */ + sfinfo.channels = 1 ; + sfinfo.format = filetype ; + + if (! (file = sf_open (filename, SFM_WRITE, &sfinfo))) + { printf ("sf_open_write failed with error : ") ; + puts (sf_strerror (NULL)) ; + exit (1) ; + } ; + + if ((k = sf_writef_int (file, orig, datalen)) != datalen) + { printf ("sf_writef_int failed with short write (%ld => %d).\n", datalen, k) ; + exit (1) ; + } ; + sf_close (file) ; + + memset (data, 0, datalen * sizeof (int)) ; + + if ((filetype & SF_FORMAT_TYPEMASK) != SF_FORMAT_RAW) + memset (&sfinfo, 0, sizeof (sfinfo)) ; + + if (! (file = sf_open (filename, SFM_READ, &sfinfo))) + { printf ("sf_open_read failed with error : ") ; + puts (sf_strerror (NULL)) ; + exit (1) ; + } ; + + if ((sfinfo.format & (SF_FORMAT_TYPEMASK | SF_FORMAT_SUBMASK)) != (filetype & (SF_FORMAT_TYPEMASK | SF_FORMAT_SUBMASK))) + { printf ("Line %d: Returned format incorrect (0x%08X => 0x%08X).\n", __LINE__, filetype, sfinfo.format) ; + exit (1) ; + } ; + + if (sfinfo.frames < datalen) + { printf ("Too few.frames in file. (%ld should be a little more than %ld)\n", datalen, SF_COUNT_TO_LONG (sfinfo.frames)) ; + exit (1) ; + } ; + + if (sfinfo.frames > (datalen + datalen / 2)) + { printf ("Too many.frames in file. (%ld should be a little more than %ld)\n", datalen, SF_COUNT_TO_LONG (sfinfo.frames)) ; + exit (1) ; + } ; + + if (sfinfo.channels != 1) + { printf ("Incorrect number of channels in file.\n") ; + exit (1) ; + } ; + + check_log_buffer_or_die (file, __LINE__) ; + + if ((k = sf_readf_int (file, data, datalen)) != datalen) + { printf ("Line %d: short read (%d should be %ld).\n", __LINE__, k, datalen) ; + exit (1) ; + } ; + + sum_abs = 0 ; + for (k = 0 ; k < datalen ; k++) + { if (error_function (data [k] / scale, orig [k] / scale, margin)) + { printf ("Line %d: Incorrect sample (#%d : %f should be %f).\n", __LINE__, k, data [k] / scale, orig [k] / scale) ; + oct_save_int (orig, data, datalen) ; + exit (1) ; + } ; + sum_abs = abs (sum_abs + abs (data [k])) ; + } ; + + if (sum_abs < 1.0) + { printf ("Line %d: Signal is all zeros.\n", __LINE__) ; + exit (1) ; + } ; + + if ((k = sf_readf_int (file, data, datalen)) != sfinfo.frames - datalen) + { printf ("Line %d: Incorrect read length (%ld should be %d).\n", __LINE__, SF_COUNT_TO_LONG (sfinfo.frames - datalen), k) ; + exit (1) ; + } ; + + /* This check is only for block based encoders which must append silence + ** to the end of a file so as to fill out a block. + */ + if ((sfinfo.format & SF_FORMAT_SUBMASK) != SF_FORMAT_MS_ADPCM) + for (k = 0 ; k < sfinfo.frames - datalen ; k++) + if (abs (data [k] / scale) > decay_response (k)) + { printf ("Line %d : Incorrect sample B (#%d : abs (%d) should be < %d).\n", __LINE__, k, data [k], decay_response (k)) ; + exit (1) ; + } ; + + if (! sfinfo.seekable) + { printf ("ok\n") ; + return ; + } ; + + /* Now test sf_seek function. */ + + if ((k = sf_seek (file, 0, SEEK_SET)) != 0) + { printf ("Line %d: Seek to start of file failed (%d).\n", __LINE__, k) ; + exit (1) ; + } ; + + for (m = 0 ; m < 3 ; m++) + { if ((k = sf_readf_int (file, data, 11)) != 11) + { printf ("Line %d: Incorrect read length (11 => %d).\n", __LINE__, k) ; + exit (1) ; + } ; + + for (k = 0 ; k < 11 ; k++) + if (error_function (data [k] / scale, orig [k + m * 11] / scale, margin)) + { printf ("Line %d: Incorrect sample (m = %d) (#%d : %d => %d).\n", __LINE__, m, k + m * 11, orig [k + m * 11], data [k]) ; + for (m = 0 ; m < 1 ; m++) + printf ("%d ", data [m]) ; + printf ("\n") ; + exit (1) ; + } ; + } ; + + seekpos = BUFFER_SIZE / 10 ; + + /* Check seek from start of file. */ + if ((k = sf_seek (file, seekpos, SEEK_SET)) != seekpos) + { printf ("Seek to start of file + %ld failed (%d).\n", seekpos, k) ; + exit (1) ; + } ; + + if ((k = sf_readf_int (file, data, 1)) != 1) + { printf ("Line %d: sf_readf_int (file, data, 1) returned %d.\n", __LINE__, k) ; + exit (1) ; + } ; + + if (error_function ((double) data [0], (double) orig [seekpos], margin)) + { printf ("Line %d: sf_seek (SEEK_SET) followed by sf_readf_int failed (%d, %d).\n", __LINE__, orig [1], data [0]) ; + exit (1) ; + } ; + + if ((k = sf_seek (file, 0, SEEK_CUR)) != seekpos + 1) + { printf ("Line %d: sf_seek (SEEK_CUR) with 0 offset failed (%d should be %ld)\n", __LINE__, k, seekpos + 1) ; + exit (1) ; + } ; + + seekpos = sf_seek (file, 0, SEEK_CUR) + BUFFER_SIZE / 5 ; + k = sf_seek (file, BUFFER_SIZE / 5, SEEK_CUR) ; + sf_readf_int (file, data, 1) ; + if (error_function ((double) data [0], (double) orig [seekpos], margin) || k != seekpos) + { printf ("Line %d: sf_seek (forwards, SEEK_CUR) followed by sf_readf_int failed (%d, %d) (%d, %ld).\n", __LINE__, data [0], orig [seekpos], k, seekpos + 1) ; + exit (1) ; + } ; + + seekpos = sf_seek (file, 0, SEEK_CUR) - 20 ; + /* Check seek backward from current position. */ + k = sf_seek (file, -20, SEEK_CUR) ; + sf_readf_int (file, data, 1) ; + if (error_function ((double) data [0], (double) orig [seekpos], margin) || k != seekpos) + { printf ("sf_seek (backwards, SEEK_CUR) followed by sf_readf_int failed (%d, %d) (%d, %ld).\n", data [0], orig [seekpos], k, seekpos) ; + exit (1) ; + } ; + + /* Check that read past end of file returns number of items. */ + sf_seek (file, (int) sfinfo.frames, SEEK_SET) ; + + if ((k = sf_readf_int (file, data, datalen)) != 0) + { printf ("Line %d: Return value from sf_readf_int past end of file incorrect (%d).\n", __LINE__, k) ; + exit (1) ; + } ; + + /* Check seek backward from end. */ + if ((k = sf_seek (file, 5 - (int) sfinfo.frames, SEEK_END)) != 5) + { printf ("sf_seek (SEEK_END) returned %d instead of %d.\n", k, 5) ; + exit (1) ; + } ; + + sf_readf_int (file, data, 1) ; + if (error_function (data [0] / scale, orig [5] / scale, margin)) + { printf ("Line %d: sf_seek (SEEK_END) followed by sf_readf_short failed (%d should be %d).\n", __LINE__, data [0], orig [5]) ; + exit (1) ; + } ; + + sf_close (file) ; + + printf ("ok\n") ; +} /* lcomp_test_int */ + +/*======================================================================================== +** Auxiliary functions +*/ + +#define SIGNAL_MAXVAL 30000.0 +#define DECAY_COUNT 800 + +static int +decay_response (int k) +{ if (k < 1) + return (int) (1.2 * SIGNAL_MAXVAL) ; + if (k > DECAY_COUNT) + return 0 ; + return (int) (1.2 * SIGNAL_MAXVAL * (DECAY_COUNT - k) / (1.0 * DECAY_COUNT)) ; +} /* decay_response */ + +static void +gen_signal_double (double *data, double scale, int datalen) +{ int k, ramplen ; + double amp = 0.0 ; + + ramplen = datalen / 18 ; + + for (k = 0 ; k < datalen ; k++) + { if (k <= ramplen) + amp = scale * k / ((double) ramplen) ; + else if (k > datalen - ramplen) + amp = scale * (datalen - k) / ((double) ramplen) ; + + data [k] = amp * (0.4 * sin (33.3 * 2.0 * M_PI * ((double) (k + 1)) / ((double) SAMPLE_RATE)) + + 0.3 * cos (201.1 * 2.0 * M_PI * ((double) (k + 1)) / ((double) SAMPLE_RATE))) ; + } ; + + return ; +} /* gen_signal_double */ + +static int +error_function (double data, double orig, double margin) +{ double error ; + + if (fabs (orig) <= 500.0) + error = fabs (fabs (data) - fabs (orig)) / 2000.0 ; + else if (fabs (orig) <= 1000.0) + error = fabs (data - orig) / 3000.0 ; + else + error = fabs (data - orig) / fabs (orig) ; + + if (error > margin) + { printf ("\n\n*******************\nError : %f\n", error) ; + return 1 ; + } ; + return 0 ; +} /* error_function */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 368e2777-2848-4e16-a77f-4db841377c73 +*/ diff --git a/tests/floating_point_test.def b/tests/floating_point_test.def new file mode 100644 index 00000000..91046435 --- /dev/null +++ b/tests/floating_point_test.def @@ -0,0 +1,40 @@ +autogen definitions floating_point_test.tpl; + +endian_type = { + end_name = little ; + end_type = SF_ENDIAN_LITTLE ; + } ; + +endian_type = { + end_name = big ; + end_type = SF_ENDIAN_BIG ; + } ; + +float_type = { + float_name = float ; + minor_type = SF_FORMAT_FLOAT ; + } ; + +float_type = { + float_name = double ; + minor_type = SF_FORMAT_DOUBLE ; + } ; + +int_type = { + int_name = short ; + int_max = 0x7FFF ; + } ; + +int_type = { + int_name = int ; + int_max = 0x7FFFFFFF ; + } ; + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 053997cb-74d9-424e-aabf-3ad7622e3eee +*/ + diff --git a/tests/floating_point_test.tpl b/tests/floating_point_test.tpl new file mode 100644 index 00000000..c6ad2b19 --- /dev/null +++ b/tests/floating_point_test.tpl @@ -0,0 +1,347 @@ +[+ AutoGen5 template c +] +/* +** Copyright (C) 1999-2005 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include +#include + +#if HAVE_UNISTD_H +#include +#endif + +#include + +#include "dft_cmp.h" +#include "utils.h" + +#include "float_cast.h" + +#define SAMPLE_RATE 16000 + +static void float_scaled_test (const char *filename, int allow_exit, int replace_float, int filetype, double target_snr) ; +static void double_scaled_test (const char *filename, int allow_exit, int replace_float, int filetype, double target_snr) ; + +[+ FOR float_type +][+ FOR int_type +][+ FOR endian_type ++]static void [+ (get "float_name") +]_[+ (get "int_name") +]_[+ (get "end_name") +]_test (const char * filename) ; +[+ ENDFOR endian_type +][+ ENDFOR int_type +][+ ENDFOR float_type ++] + +static double double_data [DFT_DATA_LENGTH] ; +static double test_data [DFT_DATA_LENGTH] ; + +static float float_data [DFT_DATA_LENGTH] ; +static double double_data [DFT_DATA_LENGTH] ; +static short short_data [DFT_DATA_LENGTH] ; +static int int_data [DFT_DATA_LENGTH] ; + +int +main (int argc, char *argv []) +{ int allow_exit = 1 ; + + if (argc == 2 && ! strstr (argv [1], "no-exit")) + allow_exit = 0 ; + +#if ((HAVE_LRINTF == 0) && (HAVE_LRINT_REPLACEMENT == 0)) + puts ("*** Cannot run this test on this platform because it lacks lrintf().") ; + exit (0) ; +#endif + + /* Float tests. */ + float_scaled_test ("float.raw", allow_exit, SF_FALSE, SF_ENDIAN_LITTLE | SF_FORMAT_RAW | SF_FORMAT_FLOAT, -163.0) ; + + /* Test both signed and unsigned 8 bit files. */ + float_scaled_test ("pcm_s8.raw", allow_exit, SF_FALSE, SF_FORMAT_RAW | SF_FORMAT_PCM_S8, -39.0) ; + float_scaled_test ("pcm_u8.raw", allow_exit, SF_FALSE, SF_FORMAT_RAW | SF_FORMAT_PCM_U8, -39.0) ; + + float_scaled_test ("pcm_16.raw", allow_exit, SF_FALSE, SF_ENDIAN_BIG | SF_FORMAT_RAW | SF_FORMAT_PCM_16, -87.0) ; + float_scaled_test ("pcm_24.raw", allow_exit, SF_FALSE, SF_ENDIAN_LITTLE | SF_FORMAT_RAW | SF_FORMAT_PCM_24, -138.0) ; + float_scaled_test ("pcm_32.raw", allow_exit, SF_FALSE, SF_ENDIAN_BIG | SF_FORMAT_RAW | SF_FORMAT_PCM_32, -163.0) ; + + float_scaled_test ("ulaw.raw", allow_exit, SF_FALSE, SF_FORMAT_RAW | SF_FORMAT_ULAW, -50.0) ; + float_scaled_test ("alaw.raw", allow_exit, SF_FALSE, SF_FORMAT_RAW | SF_FORMAT_ALAW, -49.0) ; + + float_scaled_test ("ima_adpcm.wav", allow_exit, SF_FALSE, SF_FORMAT_WAV | SF_FORMAT_IMA_ADPCM, -47.0) ; + float_scaled_test ("ms_adpcm.wav" , allow_exit, SF_FALSE, SF_FORMAT_WAV | SF_FORMAT_MS_ADPCM, -40.0) ; + float_scaled_test ("gsm610.raw" , allow_exit, SF_FALSE, SF_FORMAT_RAW | SF_FORMAT_GSM610, -33.0) ; + + float_scaled_test ("g721_32.au", allow_exit, SF_FALSE, SF_FORMAT_AU | SF_FORMAT_G721_32, -34.0) ; + float_scaled_test ("g723_24.au", allow_exit, SF_FALSE, SF_FORMAT_AU | SF_FORMAT_G723_24, -34.0) ; + float_scaled_test ("g723_40.au", allow_exit, SF_FALSE, SF_FORMAT_AU | SF_FORMAT_G723_40, -40.0) ; + + /* PAF files do not use the same encoding method for 24 bit PCM data as other file + ** formats so we need to explicitly test it here. + */ + float_scaled_test ("le_paf_24.paf", allow_exit, SF_FALSE, SF_ENDIAN_LITTLE | SF_FORMAT_PAF | SF_FORMAT_PCM_24, -149.0) ; + float_scaled_test ("be_paf_24.paf", allow_exit, SF_FALSE, SF_ENDIAN_BIG | SF_FORMAT_PAF | SF_FORMAT_PCM_24, -149.0) ; + + float_scaled_test ("dwvw_12.raw", allow_exit, SF_FALSE, SF_FORMAT_RAW | SF_FORMAT_DWVW_12, -64.0) ; + float_scaled_test ("dwvw_16.raw", allow_exit, SF_FALSE, SF_FORMAT_RAW | SF_FORMAT_DWVW_16, -92.0) ; + float_scaled_test ("dwvw_24.raw", allow_exit, SF_FALSE, SF_FORMAT_RAW | SF_FORMAT_DWVW_24, -151.0) ; + + float_scaled_test ("adpcm.vox", allow_exit, SF_FALSE, SF_FORMAT_RAW | SF_FORMAT_VOX_ADPCM, -40.0) ; + + float_scaled_test ("dpcm_16.xi", allow_exit, SF_FALSE, SF_FORMAT_XI | SF_FORMAT_DPCM_16, -90.0) ; + float_scaled_test ("dpcm_8.xi" , allow_exit, SF_FALSE, SF_FORMAT_XI | SF_FORMAT_DPCM_8 , -41.0) ; + + float_scaled_test ("pcm_s8.sds", allow_exit, SF_FALSE, SF_FORMAT_SDS | SF_FORMAT_PCM_S8, -90.0) ; + float_scaled_test ("pcm_16.sds", allow_exit, SF_FALSE, SF_FORMAT_SDS | SF_FORMAT_PCM_16, -140.0) ; + float_scaled_test ("pcm_24.sds", allow_exit, SF_FALSE, SF_FORMAT_SDS | SF_FORMAT_PCM_24, -170.0) ; + + float_scaled_test ("flac_8.flac", allow_exit, SF_FALSE, SF_FORMAT_FLAC | SF_FORMAT_PCM_S8, -39.0) ; + float_scaled_test ("flac_16.flac", allow_exit, SF_FALSE, SF_FORMAT_FLAC | SF_FORMAT_PCM_16, -87.0) ; + float_scaled_test ("flac_24.flac", allow_exit, SF_FALSE, SF_FORMAT_FLAC | SF_FORMAT_PCM_24, -138.0) ; + + float_scaled_test ("replace_float.raw", allow_exit, SF_TRUE, SF_ENDIAN_LITTLE | SF_FORMAT_RAW | SF_FORMAT_FLOAT, -163.0) ; + + /*============================================================================== + ** Double tests. + */ + + double_scaled_test ("double.raw", allow_exit, SF_FALSE, SF_FORMAT_RAW | SF_FORMAT_DOUBLE, -300.0) ; + + /* Test both signed (AIFF) and unsigned (WAV) 8 bit files. */ + double_scaled_test ("pcm_s8.raw", allow_exit, SF_FALSE, SF_FORMAT_RAW | SF_FORMAT_PCM_S8, -39.0) ; + double_scaled_test ("pcm_u8.raw", allow_exit, SF_FALSE, SF_FORMAT_RAW | SF_FORMAT_PCM_U8, -39.0) ; + + double_scaled_test ("pcm_16.raw", allow_exit, SF_FALSE, SF_FORMAT_RAW | SF_FORMAT_PCM_16, -87.0) ; + double_scaled_test ("pcm_24.raw", allow_exit, SF_FALSE, SF_FORMAT_RAW | SF_FORMAT_PCM_24, -135.0) ; + double_scaled_test ("pcm_32.raw", allow_exit, SF_FALSE, SF_FORMAT_RAW | SF_FORMAT_PCM_32, -184.0) ; + + double_scaled_test ("ulaw.raw", allow_exit, SF_FALSE, SF_FORMAT_RAW | SF_FORMAT_ULAW, -50.0) ; + double_scaled_test ("alaw.raw", allow_exit, SF_FALSE, SF_FORMAT_RAW | SF_FORMAT_ALAW, -49.0) ; + + double_scaled_test ("ima_adpcm.wav", allow_exit, SF_FALSE, SF_FORMAT_WAV | SF_FORMAT_IMA_ADPCM, -47.0) ; + double_scaled_test ("ms_adpcm.wav" , allow_exit, SF_FALSE, SF_FORMAT_WAV | SF_FORMAT_MS_ADPCM, -40.0) ; + double_scaled_test ("gsm610.raw" , allow_exit, SF_FALSE, SF_FORMAT_RAW | SF_FORMAT_GSM610, -33.0) ; + + double_scaled_test ("g721_32.au", allow_exit, SF_FALSE, SF_FORMAT_AU | SF_FORMAT_G721_32, -34.0) ; + double_scaled_test ("g723_24.au", allow_exit, SF_FALSE, SF_FORMAT_AU | SF_FORMAT_G723_24, -34.0) ; + double_scaled_test ("g723_40.au", allow_exit, SF_FALSE, SF_FORMAT_AU | SF_FORMAT_G723_40, -40.0) ; + + /* 24 bit PCM PAF files tested here. */ + double_scaled_test ("be_paf_24.paf", allow_exit, SF_FALSE, SF_ENDIAN_BIG | SF_FORMAT_PAF | SF_FORMAT_PCM_24, -151.0) ; + double_scaled_test ("le_paf_24.paf", allow_exit, SF_FALSE, SF_ENDIAN_LITTLE | SF_FORMAT_PAF | SF_FORMAT_PCM_24, -151.0) ; + + double_scaled_test ("dwvw_12.raw", allow_exit, SF_FALSE, SF_FORMAT_RAW | SF_FORMAT_DWVW_12, -64.0) ; + double_scaled_test ("dwvw_16.raw", allow_exit, SF_FALSE, SF_FORMAT_RAW | SF_FORMAT_DWVW_16, -92.0) ; + double_scaled_test ("dwvw_24.raw", allow_exit, SF_FALSE, SF_FORMAT_RAW | SF_FORMAT_DWVW_24, -151.0) ; + + double_scaled_test ("adpcm.vox" , allow_exit, SF_FALSE, SF_FORMAT_RAW | SF_FORMAT_VOX_ADPCM, -40.0) ; + + double_scaled_test ("dpcm_16.xi", allow_exit, SF_FALSE, SF_FORMAT_XI | SF_FORMAT_DPCM_16, -90.0) ; + double_scaled_test ("dpcm_8.xi" , allow_exit, SF_FALSE, SF_FORMAT_XI | SF_FORMAT_DPCM_8 , -42.0) ; + + double_scaled_test ("pcm_s8.sds", allow_exit, SF_FALSE, SF_FORMAT_SDS | SF_FORMAT_PCM_S8, -90.0) ; + double_scaled_test ("pcm_16.sds", allow_exit, SF_FALSE, SF_FORMAT_SDS | SF_FORMAT_PCM_16, -140.0) ; + double_scaled_test ("pcm_24.sds", allow_exit, SF_FALSE, SF_FORMAT_SDS | SF_FORMAT_PCM_24, -180.0) ; + + double_scaled_test ("flac_8.flac", allow_exit, SF_FALSE, SF_FORMAT_FLAC | SF_FORMAT_PCM_S8, -39.0) ; + double_scaled_test ("flac_16.flac", allow_exit, SF_FALSE, SF_FORMAT_FLAC | SF_FORMAT_PCM_16, -87.0) ; + double_scaled_test ("flac_24.flac", allow_exit, SF_FALSE, SF_FORMAT_FLAC | SF_FORMAT_PCM_24, -138.0) ; + + double_scaled_test ("replace_double.raw", allow_exit, SF_TRUE, SF_FORMAT_RAW | SF_FORMAT_DOUBLE, -300.0) ; + + putchar ('\n') ; + /* Float int tests. */ +[+ FOR float_type +][+ FOR int_type +][+ FOR endian_type ++] [+ (get "float_name") +]_[+ (get "int_name") +]_[+ (get "end_name") +]_test ("[+ (get "float_name") +]_[+ (get "int_name") +]_[+ (get "end_name") +].au") ; +[+ ENDFOR endian_type +][+ ENDFOR int_type +][+ ENDFOR float_type ++] + + return 0 ; +} /* main */ + +/*============================================================================================ + * Here are the test functions. + */ + +static void +float_scaled_test (const char *filename, int allow_exit, int replace_float, int filetype, double target_snr) +{ static float float_orig [DFT_DATA_LENGTH] ; + static float float_test [DFT_DATA_LENGTH] ; + + SNDFILE *file ; + SF_INFO sfinfo ; + int k ; + double snr ; + + print_test_name ("float_scaled_test", filename) ; + + gen_windowed_sine_double (double_data, DFT_DATA_LENGTH, 1.0) ; + + for (k = 0 ; k < DFT_DATA_LENGTH ; k++) + float_orig [k] = double_data [k] ; + + sfinfo.samplerate = SAMPLE_RATE ; + sfinfo.frames = DFT_DATA_LENGTH ; + sfinfo.channels = 1 ; + sfinfo.format = filetype ; + + file = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, SF_TRUE, __LINE__) ; + sf_command (file, SFC_TEST_IEEE_FLOAT_REPLACE, NULL, replace_float) ; + + test_write_float_or_die (file, 0, float_orig, DFT_DATA_LENGTH, __LINE__) ; + + sf_close (file) ; + + memset (float_test, 0, sizeof (float_test)) ; + + file = test_open_file_or_die (filename, SFM_READ, &sfinfo, SF_TRUE, __LINE__) ; + sf_command (file, SFC_TEST_IEEE_FLOAT_REPLACE, NULL, replace_float) ; + + exit_if_true (sfinfo.format != filetype, "\n\nLine %d: Returned format incorrect (0x%08X => 0x%08X).\n", __LINE__, filetype, sfinfo.format) ; + exit_if_true (sfinfo.frames < DFT_DATA_LENGTH, "\n\nLine %d: Incorrect number of frames in file (too short). (%ld should be %d)\n", __LINE__, SF_COUNT_TO_LONG (sfinfo.frames), DFT_DATA_LENGTH) ; + exit_if_true (sfinfo.channels != 1, "\n\nLine %d: Incorrect number of channels in file.\n", __LINE__) ; + + check_log_buffer_or_die (file, __LINE__) ; + + test_read_float_or_die (file, 0, float_test, DFT_DATA_LENGTH, __LINE__) ; + + sf_close (file) ; + + for (k = 0 ; k < DFT_DATA_LENGTH ; k++) + test_data [k] = float_test [k] ; + + snr = dft_cmp (__LINE__, double_data, test_data, DFT_DATA_LENGTH, target_snr, allow_exit) ; + + exit_if_true (snr > target_snr, "% 6.1fdB SNR\n\n Error : should be better than % 6.1fdB\n\n", snr, target_snr) ; + + printf ("% 6.1fdB SNR ... ok\n", snr) ; + + unlink (filename) ; + + return ; +} /* float_scaled_test */ + +static void +double_scaled_test (const char *filename, int allow_exit, int replace_float, int filetype, double target_snr) +{ SNDFILE *file ; + SF_INFO sfinfo ; + double snr ; + + print_test_name ("double_scaled_test", filename) ; + + gen_windowed_sine_double (double_data, DFT_DATA_LENGTH, 0.95) ; + + sfinfo.samplerate = SAMPLE_RATE ; + sfinfo.frames = DFT_DATA_LENGTH ; + sfinfo.channels = 1 ; + sfinfo.format = filetype ; + + file = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, SF_TRUE, __LINE__) ; + sf_command (file, SFC_TEST_IEEE_FLOAT_REPLACE, NULL, replace_float) ; + + test_write_double_or_die (file, 0, double_data, DFT_DATA_LENGTH, __LINE__) ; + + sf_close (file) ; + + memset (test_data, 0, sizeof (test_data)) ; + + file = test_open_file_or_die (filename, SFM_READ, &sfinfo, SF_TRUE, __LINE__) ; + sf_command (file, SFC_TEST_IEEE_FLOAT_REPLACE, NULL, replace_float) ; + + exit_if_true (sfinfo.format != filetype, "\n\nLine %d: Returned format incorrect (0x%08X => 0x%08X).\n", __LINE__, filetype, sfinfo.format) ; + exit_if_true (sfinfo.frames < DFT_DATA_LENGTH, "\n\nLine %d: Incorrect number of frames in file (too short). (%ld should be %d)\n", __LINE__, SF_COUNT_TO_LONG (sfinfo.frames), DFT_DATA_LENGTH) ; + exit_if_true (sfinfo.channels != 1, "\n\nLine %d: Incorrect number of channels in file.\n", __LINE__) ; + + check_log_buffer_or_die (file, __LINE__) ; + + test_read_double_or_die (file, 0, test_data, DFT_DATA_LENGTH, __LINE__) ; + + sf_close (file) ; + + snr = dft_cmp (__LINE__, double_data, test_data, DFT_DATA_LENGTH, target_snr, allow_exit) ; + + exit_if_true (snr > target_snr, "% 6.1fdB SNR\n\n Error : should be better than % 6.1fdB\n\n", snr, target_snr) ; + + printf ("% 6.1fdB SNR ... ok\n", snr) ; + + unlink (filename) ; + + return ; +} /* double_scaled_test */ + +/*============================================================================== +*/ + +[+ FOR float_type +][+ FOR int_type +][+ FOR endian_type ++] +static void +[+ (get "float_name") +]_[+ (get "int_name") +]_[+ (get "end_name") +]_test (const char * filename) +{ SNDFILE *file ; + SF_INFO sfinfo ; + unsigned k, max ; + + print_test_name ("[+ (get "float_name") +]_[+ (get "int_name") +]_[+ (get "end_name") +]_test", filename) ; + + gen_windowed_sine_[+ (get "float_name") +] ([+ (get "float_name") +]_data, ARRAY_LEN ([+ (get "float_name") +]_data), 0.98) ; + + sfinfo.samplerate = SAMPLE_RATE ; + sfinfo.frames = ARRAY_LEN ([+ (get "int_name") +]_data) ; + sfinfo.channels = 1 ; + sfinfo.format = [+ (get "end_type") +] | SF_FORMAT_AU | [+ (get "minor_type") +] ; + + file = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, SF_TRUE, __LINE__) ; + test_write_[+ (get "float_name") +]_or_die (file, 0, [+ (get "float_name") +]_data, ARRAY_LEN ([+ (get "float_name") +]_data), __LINE__) ; + sf_close (file) ; + + file = test_open_file_or_die (filename, SFM_READ, &sfinfo, SF_TRUE, __LINE__) ; + + if (sfinfo.frames != ARRAY_LEN ([+ (get "float_name") +]_data)) + { printf ("\n\nLine %d: Incorrect number of frames in file (too short). (%ld should be %d)\n", __LINE__, SF_COUNT_TO_LONG (sfinfo.frames), DFT_DATA_LENGTH) ; + exit (1) ; + } ; + + if (sfinfo.channels != 1) + { printf ("\n\nLine %d: Incorrect number of channels in file.\n", __LINE__) ; + exit (1) ; + } ; + + sf_command (file, SFC_SET_SCALE_FLOAT_INT_READ, NULL, SF_TRUE) ; + + test_read_[+ (get "int_name") +]_or_die (file, 0, [+ (get "int_name") +]_data, ARRAY_LEN ([+ (get "int_name") +]_data), __LINE__) ; + sf_close (file) ; + + max = 0 ; + for (k = 0 ; k < ARRAY_LEN ([+ (get "int_name") +]_data) ; k++) + if (abs ([+ (get "int_name") +]_data [k]) > max) + max = abs ([+ (get "int_name") +]_data [k]) ; + + if (1.0 * abs (max - [+ (get "int_max") +]) / [+ (get "int_max") +] > 0.01) + { printf ("\n\nLine %d: Bad maximum (%d should be %d).\n\n", __LINE__, max, [+ (get "int_max") +]) ; + exit (1) ; + } ; + + unlink (filename) ; + puts ("ok") ; +} /* [+ (get "float_name") +]_[+ (get "int_name") +]_[+ (get "end_name") +]_test */ +[+ ENDFOR endian_type +][+ ENDFOR int_type +][+ ENDFOR float_type +] + +[+ COMMENT + + Do not edit or modify anything in this comment block. + The arch-tag line is a file identity tag for the GNU Arch + revision control system. + + arch-tag: c1043a35-f0aa-44af-9f7f-f193c67f140d + ++] diff --git a/tests/header_test.def b/tests/header_test.def new file mode 100644 index 00000000..4a3fd8f9 --- /dev/null +++ b/tests/header_test.def @@ -0,0 +1,30 @@ +autogen definitions header_test.tpl; + +data_type = { + name = "short" ; + format = "SF_FORMAT_PCM_16" ; + } ; + +data_type = { + name = "int" ; + format = "SF_FORMAT_PCM_32" ; + } ; + +data_type = { + name = "float" ; + format = "SF_FORMAT_FLOAT" ; + } ; + +data_type = { + name = "double" ; + format = "SF_FORMAT_DOUBLE" ; + } ; + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 436e1f37-a274-4377-b8fa-b2e2fc29f85b +*/ + diff --git a/tests/header_test.tpl b/tests/header_test.tpl new file mode 100644 index 00000000..38f7a374 --- /dev/null +++ b/tests/header_test.tpl @@ -0,0 +1,498 @@ +[+ AutoGen5 template c +] +/* +** Copyright (C) 2001-2005 Erik de Castro Lopo +** +** This program is free software ; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation ; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY ; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program ; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include + +#include +#include + +#if HAVE_UNISTD_H +#include +#endif + +#if (HAVE_DECL_S_IRGRP == 0) +#include +#endif + +#if (defined (WIN32) || defined (_WIN32)) +#include +#include +#endif + +#include + +#include "utils.h" + +#define BUFFER_LEN (1<<10) +#define LOG_BUFFER_SIZE 1024 + +static void update_header_test (const char *filename, int typemajor) ; + +[+ FOR data_type ++]static void update_seek_[+ (get "name") +]_test (const char *filename, int filetype) ; +[+ ENDFOR data_type ++] + +static void extra_header_test (const char *filename, int filetype) ; + +/* Force the start of this buffer to be double aligned. Sparc-solaris will +** choke if its not. +*/ +static int data_out [BUFFER_LEN] ; +static int data_in [BUFFER_LEN] ; + +int +main (int argc, char *argv []) +{ int do_all = 0 ; + int test_count = 0 ; + + if (argc != 2) + { printf ("Usage : %s \n", argv [0]) ; + printf (" Where is one of the following:\n") ; + printf (" wav - test WAV file peak chunk\n") ; + printf (" aiff - test AIFF file PEAK chunk\n") ; + printf (" all - perform all tests\n") ; + exit (1) ; + } ; + + do_all=!strcmp (argv [1], "all") ; + + if (do_all || ! strcmp (argv [1], "wav")) + { update_header_test ("header.wav", SF_FORMAT_WAV) ; + update_seek_short_test ("header_short.wav", SF_FORMAT_WAV) ; + update_seek_int_test ("header_int.wav", SF_FORMAT_WAV) ; + update_seek_float_test ("header_float.wav", SF_FORMAT_WAV) ; + update_seek_double_test ("header_double.wav", SF_FORMAT_WAV) ; + extra_header_test ("extra.wav", SF_FORMAT_WAV) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "aiff")) + { update_header_test ("header.aiff", SF_FORMAT_AIFF) ; + update_seek_short_test ("header_short.aiff", SF_FORMAT_AIFF) ; + update_seek_int_test ("header_int.aiff", SF_FORMAT_AIFF) ; + update_seek_float_test ("header_float.aiff", SF_FORMAT_AIFF) ; + update_seek_double_test ("header_double.aiff", SF_FORMAT_AIFF) ; + extra_header_test ("extra.aiff", SF_FORMAT_AIFF) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "au")) + { update_header_test ("header.au", SF_FORMAT_AU) ; + update_seek_short_test ("header_short.au", SF_FORMAT_AU) ; + update_seek_int_test ("header_int.au", SF_FORMAT_AU) ; + update_seek_float_test ("header_float.au", SF_FORMAT_AU) ; + update_seek_double_test ("header_double.au", SF_FORMAT_AU) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "caf")) + { update_header_test ("header.caf", SF_FORMAT_CAF) ; + update_seek_short_test ("header_short.caf", SF_FORMAT_CAF) ; + update_seek_int_test ("header_int.caf", SF_FORMAT_CAF) ; + update_seek_float_test ("header_float.caf", SF_FORMAT_CAF) ; + update_seek_double_test ("header_double.caf", SF_FORMAT_CAF) ; + /* extra_header_test ("extra.caf", SF_FORMAT_CAF) ; */ + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "nist")) + { update_header_test ("header.nist", SF_FORMAT_NIST) ; + update_seek_short_test ("header_short.nist", SF_FORMAT_NIST) ; + update_seek_int_test ("header_int.nist", SF_FORMAT_NIST) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "paf")) + { update_header_test ("header.paf", SF_FORMAT_PAF) ; + update_seek_short_test ("header_short.paf", SF_FORMAT_PAF) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "ircam")) + { update_header_test ("header.ircam", SF_FORMAT_IRCAM) ; + update_seek_short_test ("header_short.ircam", SF_FORMAT_IRCAM) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "w64")) + { update_header_test ("header.w64", SF_FORMAT_W64) ; + update_seek_short_test ("header_short.w64", SF_FORMAT_W64) ; + update_seek_int_test ("header_int.w64", SF_FORMAT_W64) ; + update_seek_float_test ("header_float.w64", SF_FORMAT_W64) ; + update_seek_double_test ("header_double.w64", SF_FORMAT_W64) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "mat4")) + { update_header_test ("header.mat4", SF_FORMAT_MAT4) ; + update_seek_short_test ("header_short.mat4", SF_FORMAT_MAT4) ; + update_seek_int_test ("header_int.mat4", SF_FORMAT_MAT4) ; + update_seek_float_test ("header_float.mat4", SF_FORMAT_MAT4) ; + update_seek_double_test ("header_double.mat4", SF_FORMAT_MAT4) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "mat5")) + { update_header_test ("header.mat5", SF_FORMAT_MAT5) ; + update_seek_short_test ("header_short.mat5", SF_FORMAT_MAT5) ; + update_seek_int_test ("header_int.mat5", SF_FORMAT_MAT5) ; + update_seek_float_test ("header_float.mat5", SF_FORMAT_MAT5) ; + update_seek_double_test ("header_double.mat5", SF_FORMAT_MAT5) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "pvf")) + { update_header_test ("header.pvf", SF_FORMAT_PVF) ; + update_seek_short_test ("header_short.pvf", SF_FORMAT_PVF) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "avr")) + { update_header_test ("header.avr", SF_FORMAT_AVR) ; + update_seek_short_test ("header_short.avr", SF_FORMAT_AVR) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "htk")) + { update_header_test ("header.htk", SF_FORMAT_HTK) ; + update_seek_short_test ("header_short.htk", SF_FORMAT_HTK) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "svx")) + { update_header_test ("header.svx", SF_FORMAT_SVX) ; + update_seek_short_test ("header_short.svx", SF_FORMAT_SVX) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "voc")) + { update_header_test ("header.voc", SF_FORMAT_VOC) ; + /*-update_seek_short_test ("header_short.voc", SF_FORMAT_VOC) ;-*/ + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "sds")) + { update_header_test ("header.sds", SF_FORMAT_SDS) ; + /*-update_seek_short_test ("header_short.sds", SF_FORMAT_SDS) ;-*/ + test_count++ ; + } ; + + if (test_count == 0) + { printf ("Mono : ************************************\n") ; + printf ("Mono : * No '%s' test defined.\n", argv [1]) ; + printf ("Mono : ************************************\n") ; + return 1 ; + } ; + + return 0 ; +} /* main */ + + +/*============================================================================================ +** Here are the test functions. +*/ + +static void +update_header_sub (const char *filename, int typemajor, int write_mode) +{ SNDFILE *outfile, *infile ; + SF_INFO sfinfo ; + int k, frames ; + + sfinfo.samplerate = 44100 ; + sfinfo.format = (typemajor | SF_FORMAT_PCM_16) ; + sfinfo.channels = 1 ; + sfinfo.frames = 0 ; + + frames = BUFFER_LEN / sfinfo.channels ; + + outfile = test_open_file_or_die (filename, write_mode, &sfinfo, SF_TRUE, __LINE__) ; + + for (k = 0 ; k < BUFFER_LEN ; k++) + data_out [k] = k + 1 ; + test_write_int_or_die (outfile, 0, data_out, BUFFER_LEN, __LINE__) ; + + if (typemajor != SF_FORMAT_HTK) + { /* The HTK header is not correct when the file is first written. */ + infile = test_open_file_or_die (filename, SFM_READ, &sfinfo, SF_TRUE, __LINE__) ; + sf_close (infile) ; + } ; + + sf_command (outfile, SFC_UPDATE_HEADER_NOW, NULL, 0) ; + + /* + ** Open file and check log buffer for an error. If header update failed + ** the the log buffer will contain errors. + */ + infile = test_open_file_or_die (filename, SFM_READ, &sfinfo, SF_TRUE, __LINE__) ; + check_log_buffer_or_die (infile, __LINE__) ; + + if (sfinfo.frames < BUFFER_LEN || sfinfo.frames > BUFFER_LEN + 50) + { printf ("\n\nLine %d : Incorrect sample count (%ld should be %d)\n", __LINE__, SF_COUNT_TO_LONG (sfinfo.frames), BUFFER_LEN) ; + dump_log_buffer (infile) ; + exit (1) ; + } ; + + test_read_int_or_die (infile, 0, data_in, BUFFER_LEN, __LINE__) ; + for (k = 0 ; k < BUFFER_LEN ; k++) + if (data_out [k] != k + 1) + printf ("Error : line %d\n", __LINE__) ; + + sf_close (infile) ; + + /* Set auto update on. */ + sf_command (outfile, SFC_SET_UPDATE_HEADER_AUTO, NULL, SF_TRUE) ; + + /* Write more data_out. */ + for (k = 0 ; k < BUFFER_LEN ; k++) + data_out [k] = k + 2 ; + test_write_int_or_die (outfile, 0, data_out, BUFFER_LEN, __LINE__) ; + + /* Open file again and make sure no errors in log buffer. */ + infile = test_open_file_or_die (filename, SFM_READ, &sfinfo, SF_TRUE, __LINE__) ; + check_log_buffer_or_die (infile, __LINE__) ; + + if (sfinfo.frames < 2 * BUFFER_LEN || sfinfo.frames > 2 * BUFFER_LEN + 50) + { printf ("\n\nLine %d : Incorrect sample count (%ld should be %d)\n", __LINE__, SF_COUNT_TO_LONG (sfinfo.frames), 2 * BUFFER_LEN) ; + dump_log_buffer (infile) ; + exit (1) ; + } ; + + sf_close (infile) ; + + sf_close (outfile) ; + + unlink (filename) ; +} /* update_header_sub */ + +static void +update_header_test (const char *filename, int typemajor) +{ + print_test_name ("update_header_test", filename) ; + +#if 0 /*-(OS_IS_WIN32 == 0)-*/ + if (typemajor == SF_FORMAT_PAF) + { /* + ** I think this is a bug in the win32 file I/O code in src/file_io.c. + ** I didn't write that code and I don't have the time to debug and + ** fix it. Patches will gladly be accepted. Erik + */ + puts ("doesn't work on win32") ; + return ; + } ; +#endif + + update_header_sub (filename, typemajor, SFM_WRITE) ; + update_header_sub (filename, typemajor, SFM_RDWR) ; + + unlink (filename) ; + puts ("ok") ; +} /* update_header_test */ + +/*============================================================================== +*/ + +[+ FOR data_type ++]static void +update_seek_[+ (get "name") +]_test (const char *filename, int filetype) +{ SNDFILE *outfile, *infile ; + SF_INFO sfinfo ; + sf_count_t frames ; + [+ (get "name") +] buffer [8] ; + int k ; + + print_test_name ("update_seek_[+ (get "name") +]_test", filename) ; + + memset (buffer, 0, sizeof (buffer)) ; + + /* Create sound outfile with no data. */ + sfinfo.format = filetype | [+ (get "format") +] ; + sfinfo.samplerate = 48000 ; + sfinfo.channels = 2 ; + + if (sf_format_check (&sfinfo) == SF_FALSE) + sfinfo.channels = 1 ; + + outfile = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, SF_TRUE, __LINE__) ; + sf_close (outfile) ; + + /* Open again for read/write. */ + outfile = test_open_file_or_die (filename, SFM_RDWR, &sfinfo, SF_TRUE, __LINE__) ; + + /* + ** In auto header update mode, seeking to the end of the file with + ** SEEK_SET will fail from the 2nd seek on. seeking to 0, SEEK_END + ** will seek to 0 anyway + */ + if (sf_command (outfile, SFC_SET_UPDATE_HEADER_AUTO, NULL, SF_TRUE) == 0) + { printf ("\n\nError : sf_command (SFC_SET_UPDATE_HEADER_AUTO) return error : %s\n\n", sf_strerror (outfile)) ; + exit (1) ; + } ; + + /* Now write some frames. */ + frames = ARRAY_LEN (buffer) / sfinfo.channels ; + + for (k = 0 ; k < 6 ; k++) + { test_seek_or_die (outfile, k * frames, SEEK_SET, k * frames, sfinfo.channels, __LINE__) ; + test_seek_or_die (outfile, 0, SEEK_END, k * frames, sfinfo.channels, __LINE__) ; + + /* Open file again and make sure no errors in log buffer. */ + infile = test_open_file_or_die (filename, SFM_READ, &sfinfo, SF_TRUE, __LINE__) ; + check_log_buffer_or_die (infile, __LINE__) ; + sf_close (infile) ; + + if (sfinfo.frames != k * frames) + { printf ("\n\nLine %d : Incorrect sample count (%ld should be %ld)\n", __LINE__, SF_COUNT_TO_LONG (sfinfo.frames), SF_COUNT_TO_LONG (k + frames)) ; + dump_log_buffer (infile) ; + exit (1) ; + } ; + + if ((k & 1) == 0) + test_write_[+ (get "name") +]_or_die (outfile, k, buffer, sfinfo.channels * frames, __LINE__) ; + else + test_writef_[+ (get "name") +]_or_die (outfile, k, buffer, frames, __LINE__) ; + } ; + + sf_close (outfile) ; + unlink (filename) ; + + puts ("ok") ; + return ; +} /* update_seek_[+ (get "name") +]_test */ +[+ ENDFOR data_type ++] + + + + + +static void +extra_header_test (const char *filename, int filetype) +{ SNDFILE *outfile, *infile ; + SF_INFO sfinfo ; + sf_count_t frames ; + short buffer [8] ; + int k = 0 ; + + print_test_name ("extra_header_test", filename) ; + + sfinfo.samplerate = 44100 ; + sfinfo.format = (filetype | SF_FORMAT_PCM_16) ; + sfinfo.channels = 1 ; + + memset (buffer, 0xA0, sizeof (buffer)) ; + + /* Now write some frames. */ + frames = ARRAY_LEN (buffer) / sfinfo.channels ; + + /* Test the file with extra header data. */ + outfile = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, SF_TRUE, [+ (tpl-file-line "%2$d") +]) ; + sf_set_string (outfile, SF_STR_TITLE, filename) ; + test_writef_short_or_die (outfile, k, buffer, frames, [+ (tpl-file-line "%2$d") +]) ; + sf_set_string (outfile, SF_STR_COPYRIGHT, "(c) 1980 Erik") ; + sf_close (outfile) ; + +#if 1 + /* + ** Erik de Castro Lopo May 23 2004. + ** + ** This file has extra string data in the header and therefore cannot + ** currently be opened in SFM_RDWR mode. This is fixable, but its in + ** a part of the code I don't want to fiddle with until the Ogg/Vorbis + ** integration is done. + */ + + if ((infile = sf_open (filename, SFM_RDWR, &sfinfo)) != NULL) + { printf ("\n\nError : should not be able to open this file in SFM_RDWR.\n\n") ; + exit (1) ; + } ; + + unlink (filename) ; + puts ("ok") ; + return ; +#else + + hexdump_file (filename, 0, 100000) ; + + /* Open again for read/write. */ + outfile = test_open_file_or_die (filename, SFM_RDWR, &sfinfo, [+ (tpl-file-line "%2$d") +]) ; + + /* + ** In auto header update mode, seeking to the end of the file with + ** SEEK_SET will fail from the 2nd seek on. seeking to 0, SEEK_END + ** will seek to 0 anyway + */ + if (sf_command (outfile, SFC_SET_UPDATE_HEADER_AUTO, NULL, SF_TRUE) == 0) + { printf ("\n\nError : sf_command (SFC_SET_UPDATE_HEADER_AUTO) return error : %s\n\n", sf_strerror (outfile)) ; + exit (1) ; + } ; + + /* Now write some frames. */ + frames = ARRAY_LEN (buffer) / sfinfo.channels ; + + for (k = 1 ; k < 6 ; k++) + { + printf ("\n*** pass %d\n", k) ; + memset (buffer, 0xA0 + k, sizeof (buffer)) ; + + + test_seek_or_die (outfile, k * frames, SEEK_SET, k * frames, sfinfo.channels, [+ (tpl-file-line "%2$d") +]) ; + test_seek_or_die (outfile, 0, SEEK_END, k * frames, sfinfo.channels, [+ (tpl-file-line "%2$d") +]) ; + + /* Open file again and make sure no errors in log buffer. */ + if (0) + { infile = test_open_file_or_die (filename, SFM_READ, &sfinfo, [+ (tpl-file-line "%2$d") +]) ; + check_log_buffer_or_die (infile, [+ (tpl-file-line "%2$d") +]) ; + sf_close (infile) ; + } ; + + if (sfinfo.frames != k * frames) + { printf ("\n\nLine %d : Incorrect sample count (%ld should be %ld)\n", [+ (tpl-file-line "%2$d") +], SF_COUNT_TO_LONG (sfinfo.frames), SF_COUNT_TO_LONG (k + frames)) ; + dump_log_buffer (infile) ; + exit (1) ; + } ; + + if ((k & 1) == 0) + test_write_short_or_die (outfile, k, buffer, sfinfo.channels * frames, [+ (tpl-file-line "%2$d") +]) ; + else + test_writef_short_or_die (outfile, k, buffer, frames, [+ (tpl-file-line "%2$d") +]) ; + hexdump_file (filename, 0, 100000) ; + } ; + + sf_close (outfile) ; + unlink (filename) ; + + puts ("ok") ; + return ; +#endif +} /* extra_header_test */ + +[+ COMMENT + + Do not edit or modify anything in this comment block. + The arch-tag line is a file identity tag for the GNU Arch + revision control system. + + arch-tag: ba02b7d6-8a89-45f3-aad2-a50390f52af5 + ++] diff --git a/tests/headerless_test.c b/tests/headerless_test.c new file mode 100644 index 00000000..55957308 --- /dev/null +++ b/tests/headerless_test.c @@ -0,0 +1,187 @@ +/* +** Copyright (C) 1999-2004 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include +#include + +#if HAVE_UNISTD_H +#include +#endif + +#include + +#include "utils.h" + +#define BUFFER_SIZE (2000) + +static void old_test (void) ; +static void headerless_test (const char * filename, int format, int expected) ; + +int +main (void) +{ + old_test () ; + + headerless_test ("raw.vox", SF_FORMAT_VOX_ADPCM, SF_FORMAT_RAW | SF_FORMAT_VOX_ADPCM) ; + headerless_test ("raw.gsm", SF_FORMAT_GSM610, SF_FORMAT_RAW | SF_FORMAT_GSM610) ; + + headerless_test ("raw.snd", SF_FORMAT_ULAW, SF_FORMAT_RAW | SF_FORMAT_ULAW) ; + headerless_test ("raw.au" , SF_FORMAT_ULAW, SF_FORMAT_RAW | SF_FORMAT_ULAW) ; + + return 0 ; +} /* main */ + +static void +headerless_test (const char * filename, int format, int expected) +{ static short buffer [BUFFER_SIZE] ; + SNDFILE *file ; + SF_INFO sfinfo ; + int k ; + + format &= SF_FORMAT_SUBMASK ; + + print_test_name (__func__, filename) ; + + for (k = 0 ; k < BUFFER_SIZE ; k++) + buffer [k] = k ; + + sfinfo.samplerate = 8000 ; + sfinfo.frames = 0 ; + sfinfo.channels = 1 ; + sfinfo.format = SF_FORMAT_RAW | format ; + + file = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, SF_TRUE, __LINE__) ; + + if ((k = sf_write_short (file, buffer, BUFFER_SIZE)) != BUFFER_SIZE) + { printf ("Line %d: sf_write_short failed with short write (%d => %d).\n", __LINE__, BUFFER_SIZE, k) ; + fflush (stdout) ; + puts (sf_strerror (file)) ; + exit (1) ; + } ; + + sf_close (file) ; + + memset (buffer, 0, sizeof (buffer)) ; + + /* We should be able to detect these so clear sfinfo. */ + memset (&sfinfo, 0, sizeof (sfinfo)) ; + + file = test_open_file_or_die (filename, SFM_READ, &sfinfo, SF_TRUE, __LINE__) ; + + if (sfinfo.format != expected) + { printf ("Line %d: Returned format incorrect (0x%08X => 0x%08X).\n", __LINE__, expected, sfinfo.format) ; + exit (1) ; + } ; + + if (sfinfo.frames < BUFFER_SIZE) + { printf ("Line %d: Incorrect number of.frames in file. (%d => %ld)\n", __LINE__, BUFFER_SIZE, SF_COUNT_TO_LONG (sfinfo.frames)) ; + exit (1) ; + } ; + + if (sfinfo.channels != 1) + { printf ("Line %d: Incorrect number of channels in file.\n", __LINE__) ; + exit (1) ; + } ; + + check_log_buffer_or_die (file, __LINE__) ; + + printf ("ok\n") ; + unlink (filename) ; +} /* headerless_test */ + +static void +old_test (void) +{ static short buffer [BUFFER_SIZE] ; + SNDFILE *file ; + SF_INFO sfinfo ; + int k, filetype ; + const char *filename = "headerless.wav" ; + + print_test_name (__func__, "") ; + + for (k = 0 ; k < BUFFER_SIZE ; k++) + buffer [k] = k ; + + filetype = SF_FORMAT_WAV | SF_FORMAT_PCM_16 ; + + sfinfo.samplerate = 32000 ; + sfinfo.frames = 123456789 ; /* Wrong length. Library should correct this on sf_close. */ + sfinfo.channels = 1 ; + sfinfo.format = filetype ; + + file = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, SF_TRUE, __LINE__) ; + + if ((k = sf_write_short (file, buffer, BUFFER_SIZE)) != BUFFER_SIZE) + { printf ("Line %d: sf_write_short failed with short write (%d => %d).\n", __LINE__, BUFFER_SIZE, k) ; + fflush (stdout) ; + puts (sf_strerror (file)) ; + exit (1) ; + } ; + + sf_close (file) ; + + memset (buffer, 0, sizeof (buffer)) ; + + /* Read as RAW but get the bit width and endian-ness correct. */ + sfinfo.format = filetype = SF_ENDIAN_LITTLE | SF_FORMAT_RAW | SF_FORMAT_PCM_16 ; + + file = test_open_file_or_die (filename, SFM_READ, &sfinfo, SF_TRUE, __LINE__) ; + + if (sfinfo.format != filetype) + { printf ("Line %d: Returned format incorrect (0x%08X => 0x%08X).\n", __LINE__, filetype, sfinfo.format) ; + exit (1) ; + } ; + + if (sfinfo.frames < BUFFER_SIZE) + { printf ("Line %d: Incorrect number of.frames in file. (%d => %ld)\n", __LINE__, BUFFER_SIZE, SF_COUNT_TO_LONG (sfinfo.frames)) ; + exit (1) ; + } ; + + if (sfinfo.channels != 1) + { printf ("Line %d: Incorrect number of channels in file.\n", __LINE__) ; + exit (1) ; + } ; + + check_log_buffer_or_die (file, __LINE__) ; + + if ((k = sf_read_short (file, buffer, BUFFER_SIZE)) != BUFFER_SIZE) + { printf ("Line %d: short read (%d).\n", __LINE__, k) ; + exit (1) ; + } ; + + for (k = 0 ; k < BUFFER_SIZE - 22 ; k++) + if (buffer [k + 22] != k) + { printf ("Line %d: Incorrect sample (#%d : 0x%x => 0x%x).\n", __LINE__, k, k, buffer [k]) ; + exit (1) ; + } ; + + printf ("ok\n") ; + unlink (filename) ; +} /* old_test */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 0820bc1a-c396-4e66-9997-096999b0bc40 +*/ diff --git a/tests/largefile_test.c b/tests/largefile_test.c new file mode 100644 index 00000000..8fb005ac --- /dev/null +++ b/tests/largefile_test.c @@ -0,0 +1,90 @@ +/* +** Copyright (C) 2006 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include + +#if HAVE_UNISTD_H +#include +#endif + +#include + +#include "utils.h" + +#define BUFFER_LEN (1024 * 1024) +#define BUFFER_COUNT (768) + +static void largefile_test (int filetype, const char * filename) ; + +int +main (void) +{ + largefile_test (SF_FORMAT_WAV, "largefile.wav") ; + largefile_test (SF_FORMAT_AIFF, "largefile.aiff") ; + + return 0 ; +} /* main */ + +static void +largefile_test (int filetype, const char * filename) +{ static float data [BUFFER_LEN] ; + SNDFILE *file ; + SF_INFO sfinfo ; + int k ; + + print_test_name ("largefile_test", filename) ; + + sfinfo.samplerate = 44100 ; + sfinfo.channels = 2 ; + sfinfo.frames = 0 ; + sfinfo.format = (filetype | SF_FORMAT_PCM_32) ; + + file = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, SF_TRUE, __LINE__) ; + + for (k = 0 ; k < BUFFER_COUNT ; k++) + test_write_float_or_die (file, k, data, BUFFER_LEN, __LINE__) ; + + sf_close (file) ; + + file = test_open_file_or_die (filename, SFM_READ, &sfinfo, SF_TRUE, __LINE__) ; + + if ((sfinfo.frames * sfinfo.channels) / BUFFER_LEN != BUFFER_COUNT) + { printf ("\n\nLine %d : bad frame count.\n", __LINE__) ; + exit (1) ; + } ; + + sf_close (file) ; + + unlink (filename) ; + puts ("ok") ; + + + return ; +} /* largefile_test */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 4ef10b9a-3356-4b92-8b7b-c60c0c4123fe +*/ diff --git a/tests/locale_test.c b/tests/locale_test.c new file mode 100644 index 00000000..4c76f568 --- /dev/null +++ b/tests/locale_test.c @@ -0,0 +1,114 @@ +/* +** Copyright (C) 2005 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" +#include "sndfile.h" + +#include +#include +#include + +#if HAVE_UNISTD_H +#include +#endif + +#if HAVE_LOCALE_H +#include +#endif + +#include "utils.h" + +typedef struct +{ const char *locale ; + const char *filename ; + int width ; +} LOCALE_DATA ; + +static void locale_test (const char * locname, const char * filename, int width) ; + +int +main (void) +{ LOCALE_DATA ldata [] = + { { "de_DE", "F\303\274\303\237e.au", 7 }, + { "en_AU", "kangaroo.au", 11 }, + { "POSIX", "posix.au", 8 }, + { "pt_PT", "concei\303\247\303\243o.au", 12 }, + { "ja_JP", "\343\201\212\343\201\257\343\202\210\343\201\206\343\201\224\343\201\226\343\201\204\343\201\276\343\201\231.au", 21 }, + { "vi_VN", "qu\341\273\221c ng\341\273\257.au", 11 }, + + { NULL, NULL, 0 } + } ; + int k ; + + for (k = 0 ; ldata [k].locale != NULL ; k++) + locale_test (ldata [k].locale, ldata [k].filename, ldata [k].width) ; + + return 0 ; +} /* main */ + +static void +locale_test (const char * locname, const char * filename, int width) +{ +#if (HAVE_LOCALE_H == 0 || HAVE_SETLOCALE == 0) + locname = filename = NULL ; + width = 0 ; + return ; +#else + const short wdata [] = { 1, 2, 3, 4, 5, 6, 7, 8 } ; + short rdata [ARRAY_LEN (wdata)] ; + const char *old_locale ; + SNDFILE *file ; + SF_INFO sfinfo ; + + /* Grab the old locale. */ + old_locale = setlocale (LC_ALL, NULL) ; + + if (setlocale (LC_ALL, locname) == NULL) + return ; + + printf (" locale_test : %-6s %s%*c : ", locname, filename, 28 - width, ' ') ; + fflush (stdout) ; + + sfinfo.format = SF_FORMAT_AU | SF_FORMAT_PCM_16 ; + sfinfo.channels = 1 ; + sfinfo.samplerate = 44100 ; + + file = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, 0, __LINE__) ; + test_write_short_or_die (file, 0, wdata, ARRAY_LEN (wdata), __LINE__) ; + sf_close (file) ; + + file = test_open_file_or_die (filename, SFM_READ, &sfinfo, 0, __LINE__) ; + test_read_short_or_die (file, 0, rdata, ARRAY_LEN (rdata), __LINE__) ; + sf_close (file) ; + + /* Restore old locale. */ + setlocale (LC_ALL, old_locale) ; + + unlink (filename) ; + puts ("ok") ; +#endif +} /* locale_test */ + + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 087b25a3-03a2-4195-acd2-23fbbc489021 +*/ diff --git a/tests/lossy_comp_test.c b/tests/lossy_comp_test.c new file mode 100644 index 00000000..fdd6d03a --- /dev/null +++ b/tests/lossy_comp_test.c @@ -0,0 +1,2194 @@ +/* +** Copyright (C) 1999-2005 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include +#include + +#if HAVE_UNISTD_H +#include +#endif + +#include + +#include "utils.h" + +#define BUFFER_SIZE (1<<14) /* Should be (1<<14) */ +#define SAMPLE_RATE 11025 + +#ifndef M_PI +#define M_PI 3.14159265358979323846264338 +#endif + +#define LCT_MAX(x,y) ((x) > (y) ? (x) : (y)) + +static void lcomp_test_short (const char *filename, int filetype, int chan, double margin) ; +static void lcomp_test_int (const char *filename, int filetype, int chan, double margin) ; +static void lcomp_test_float (const char *filename, int filetype, int chan, double margin) ; +static void lcomp_test_double (const char *filename, int filetype, int chan, double margin) ; + +static void sdlcomp_test_short (const char *filename, int filetype, int chan, double margin) ; +static void sdlcomp_test_int (const char *filename, int filetype, int chan, double margin) ; +static void sdlcomp_test_float (const char *filename, int filetype, int chan, double margin) ; +static void sdlcomp_test_double (const char *filename, int filetype, int chan, double margin) ; + +static int error_function (double data, double orig, double margin) ; +static int decay_response (int k) ; + +static void gen_signal_double (double *data, double scale, int channels, int datalen) ; + +static void smoothed_diff_short (short *data, unsigned int datalen) ; +static void smoothed_diff_int (int *data, unsigned int datalen) ; +static void smoothed_diff_float (float *data, unsigned int datalen) ; +static void smoothed_diff_double (double *data, unsigned int datalen) ; + +static void check_comment (SNDFILE * file, int format, int lineno) ; + +/* +** Force the start of these buffers to be double aligned. Sparc-solaris will +** choke if they are not. +*/ +typedef union +{ double d [BUFFER_SIZE + 1] ; + float f [BUFFER_SIZE + 1] ; + int i [BUFFER_SIZE + 1] ; + short s [BUFFER_SIZE + 1] ; + char c [BUFFER_SIZE + 1] ; +} BUFFER ; + +static BUFFER data_buffer ; +static BUFFER orig_buffer ; +static BUFFER smooth_buffer ; + +static const char *long_comment = + "This is really quite a long comment. It is designed to be long enough " + "to screw up the encoders and decoders if the file container format does " + "not handle things correctly. If everything is working correctly, the " + "decoder will only decode the actual audio data, and not this string at " + "the end of the file." ; + +int +main (int argc, char *argv []) +{ int do_all = 0 ; + int test_count = 0 ; + + if (argc != 2) + { printf ("Usage : %s \n", argv [0]) ; + printf (" Where is one of the following:\n") ; + printf (" wav_ima - test IMA ADPCM WAV file functions\n") ; + printf (" wav_msadpcm - test MS ADPCM WAV file functions\n") ; + printf (" wav_gsm610 - test GSM 6.10 WAV file functions\n") ; + printf (" wav_ulaw - test u-law WAV file functions\n") ; + printf (" wav_alaw - test A-law WAV file functions\n") ; + printf (" wve - test Psion WVE file functions\n") ; + printf (" all - perform all tests\n") ; + exit (1) ; + } ; + + do_all = ! strcmp (argv [1], "all") ; + + if (strcmp (argv [1], "wav_pcm") == 0) + { /* This is just a sanity test for PCM encoding. */ + lcomp_test_short ("pcm.wav", SF_FORMAT_WAV | SF_FORMAT_PCM_16, 2, 0.00001) ; + lcomp_test_int ("pcm.wav", SF_FORMAT_WAV | SF_FORMAT_PCM_16, 2, 0.00001) ; + lcomp_test_short ("pcm.rifx", SF_ENDIAN_BIG | SF_FORMAT_WAV | SF_FORMAT_PCM_16, 2, 0.00001) ; + lcomp_test_int ("pcm.rifx", SF_ENDIAN_BIG | SF_FORMAT_WAV | SF_FORMAT_PCM_16, 2, 0.00001) ; + /* Lite remove start */ + lcomp_test_float ("pcm.wav", SF_FORMAT_WAV | SF_FORMAT_PCM_16, 2, 0.005) ; + lcomp_test_double ("pcm.wav", SF_FORMAT_WAV | SF_FORMAT_PCM_16, 2, 0.005) ; + /* Lite remove end */ + test_count++ ; + } ; + + /* For all the rest, if the file format supports more than 1 channel, use stereo. */ + /* Lite remove start */ + if (do_all || strcmp (argv [1], "wav_ima") == 0) + { lcomp_test_short ("ima.wav", SF_FORMAT_WAV | SF_FORMAT_IMA_ADPCM, 2, 0.18) ; + lcomp_test_int ("ima.wav", SF_FORMAT_WAV | SF_FORMAT_IMA_ADPCM, 2, 0.18) ; + lcomp_test_float ("ima.wav", SF_FORMAT_WAV | SF_FORMAT_IMA_ADPCM, 2, 0.18) ; + lcomp_test_double ("ima.wav", SF_FORMAT_WAV | SF_FORMAT_IMA_ADPCM, 2, 0.18) ; + + lcomp_test_short ("ima.rifx", SF_ENDIAN_BIG | SF_FORMAT_WAV | SF_FORMAT_IMA_ADPCM, 2, 0.18) ; + lcomp_test_int ("ima.rifx", SF_ENDIAN_BIG | SF_FORMAT_WAV | SF_FORMAT_IMA_ADPCM, 2, 0.18) ; + lcomp_test_float ("ima.rifx", SF_ENDIAN_BIG | SF_FORMAT_WAV | SF_FORMAT_IMA_ADPCM, 2, 0.18) ; + lcomp_test_double ("ima.rifx", SF_ENDIAN_BIG | SF_FORMAT_WAV | SF_FORMAT_IMA_ADPCM, 2, 0.18) ; + + sdlcomp_test_short ("ima.wav", SF_FORMAT_WAV | SF_FORMAT_IMA_ADPCM, 2, 0.18) ; + sdlcomp_test_int ("ima.wav", SF_FORMAT_WAV | SF_FORMAT_IMA_ADPCM, 2, 0.18) ; + sdlcomp_test_float ("ima.wav", SF_FORMAT_WAV | SF_FORMAT_IMA_ADPCM, 2, 0.18) ; + sdlcomp_test_double ("ima.wav", SF_FORMAT_WAV | SF_FORMAT_IMA_ADPCM, 2, 0.18) ; + test_count++ ; + } ; + + if (do_all || strcmp (argv [1], "wav_msadpcm") == 0) + { lcomp_test_short ("msadpcm.wav", SF_FORMAT_WAV | SF_FORMAT_MS_ADPCM, 2, 0.36) ; + lcomp_test_int ("msadpcm.wav", SF_FORMAT_WAV | SF_FORMAT_MS_ADPCM, 2, 0.36) ; + lcomp_test_float ("msadpcm.wav", SF_FORMAT_WAV | SF_FORMAT_MS_ADPCM, 2, 0.36) ; + lcomp_test_double ("msadpcm.wav", SF_FORMAT_WAV | SF_FORMAT_MS_ADPCM, 2, 0.36) ; + + lcomp_test_short ("msadpcm.rifx", SF_ENDIAN_BIG | SF_FORMAT_WAV | SF_FORMAT_MS_ADPCM, 2, 0.36) ; + lcomp_test_int ("msadpcm.rifx", SF_ENDIAN_BIG | SF_FORMAT_WAV | SF_FORMAT_MS_ADPCM, 2, 0.36) ; + lcomp_test_float ("msadpcm.rifx", SF_ENDIAN_BIG | SF_FORMAT_WAV | SF_FORMAT_MS_ADPCM, 2, 0.36) ; + lcomp_test_double ("msadpcm.rifx", SF_ENDIAN_BIG | SF_FORMAT_WAV | SF_FORMAT_MS_ADPCM, 2, 0.36) ; + + sdlcomp_test_short ("msadpcm.wav", SF_FORMAT_WAV | SF_FORMAT_MS_ADPCM, 2, 0.36) ; + sdlcomp_test_int ("msadpcm.wav", SF_FORMAT_WAV | SF_FORMAT_MS_ADPCM, 2, 0.36) ; + sdlcomp_test_float ("msadpcm.wav", SF_FORMAT_WAV | SF_FORMAT_MS_ADPCM, 2, 0.36) ; + sdlcomp_test_double ("msadpcm.wav", SF_FORMAT_WAV | SF_FORMAT_MS_ADPCM, 2, 0.36) ; + + test_count++ ; + } ; + + if (do_all || strcmp (argv [1], "wav_g721") == 0) + { printf ("**** Fix this later : error bound should be 0.06 ****\n") ; + lcomp_test_short ("g721.wav", SF_FORMAT_WAV | SF_FORMAT_G721_32, 1, 0.7) ; + lcomp_test_int ("g721.wav", SF_FORMAT_WAV | SF_FORMAT_G721_32, 1, 0.7) ; + + lcomp_test_short ("g721.rifx", SF_ENDIAN_BIG | SF_FORMAT_WAV | SF_FORMAT_G721_32, 1, 0.7) ; + lcomp_test_int ("g721.rifx", SF_ENDIAN_BIG | SF_FORMAT_WAV | SF_FORMAT_G721_32, 1, 0.7) ; + + test_count++ ; + } ; + /* Lite remove end */ + + if (do_all || strcmp (argv [1], "wav_ulaw") == 0) + { lcomp_test_short ("ulaw.wav", SF_FORMAT_WAV | SF_FORMAT_ULAW, 2, 0.04) ; + lcomp_test_int ("ulaw.wav", SF_FORMAT_WAV | SF_FORMAT_ULAW, 2, 0.04) ; + + lcomp_test_short ("ulaw.rifx", SF_ENDIAN_BIG | SF_FORMAT_WAV | SF_FORMAT_ULAW, 2, 0.04) ; + lcomp_test_int ("ulaw.rifx", SF_ENDIAN_BIG | SF_FORMAT_WAV | SF_FORMAT_ULAW, 2, 0.04) ; + + /* Lite remove start */ + lcomp_test_float ("ulaw.wav", SF_FORMAT_WAV | SF_FORMAT_ULAW, 2, 0.04) ; + lcomp_test_double ("ulaw.wav", SF_FORMAT_WAV | SF_FORMAT_ULAW, 2, 0.04) ; + /* Lite remove end */ + test_count++ ; + } ; + + if (do_all || strcmp (argv [1], "wav_alaw") == 0) + { lcomp_test_short ("alaw.wav", SF_FORMAT_WAV | SF_FORMAT_ALAW, 2, 0.04) ; + lcomp_test_int ("alaw.wav", SF_FORMAT_WAV | SF_FORMAT_ALAW, 2, 0.04) ; + /* Lite remove start */ + lcomp_test_float ("alaw.wav", SF_FORMAT_WAV | SF_FORMAT_ALAW, 2, 0.04) ; + lcomp_test_double ("alaw.wav", SF_FORMAT_WAV | SF_FORMAT_ALAW, 2, 0.04) ; + /* Lite remove end */ + test_count++ ; + } ; + + if (do_all || strcmp (argv [1], "wav_gsm610") == 0) + { /* Don't do lcomp_test_XXX as the errors are too big. */ + sdlcomp_test_short ("gsm610.wav", SF_FORMAT_WAV | SF_FORMAT_GSM610, 1, 0.24) ; + sdlcomp_test_int ("gsm610.wav", SF_FORMAT_WAV | SF_FORMAT_GSM610, 1, 0.24) ; + + sdlcomp_test_short ("gsm610.rifx", SF_ENDIAN_BIG | SF_FORMAT_WAV | SF_FORMAT_GSM610, 1, 0.24) ; + sdlcomp_test_int ("gsm610.rifx", SF_ENDIAN_BIG | SF_FORMAT_WAV | SF_FORMAT_GSM610, 1, 0.24) ; + + /* Lite remove start */ + sdlcomp_test_float ("gsm610.wav", SF_FORMAT_WAV | SF_FORMAT_GSM610, 1, 0.24) ; + sdlcomp_test_double ("gsm610.wav", SF_FORMAT_WAV | SF_FORMAT_GSM610, 1, 0.24) ; + /* Lite remove end */ + test_count++ ; + } ; + + if (do_all || strcmp (argv [1], "aiff_ulaw") == 0) + { lcomp_test_short ("ulaw.aiff", SF_FORMAT_AIFF | SF_FORMAT_ULAW, 2, 0.04) ; + lcomp_test_int ("ulaw.aiff", SF_FORMAT_AIFF | SF_FORMAT_ULAW, 2, 0.04) ; + /* Lite remove start */ + lcomp_test_float ("ulaw.aiff", SF_FORMAT_AIFF | SF_FORMAT_ULAW, 2, 0.04) ; + lcomp_test_double ("ulaw.aiff", SF_FORMAT_AIFF | SF_FORMAT_ULAW, 2, 0.04) ; + /* Lite remove end */ + test_count++ ; + } ; + + if (do_all || strcmp (argv [1], "aiff_alaw") == 0) + { lcomp_test_short ("alaw.aiff", SF_FORMAT_AIFF | SF_FORMAT_ALAW, 2, 0.04) ; + lcomp_test_int ("alaw.aiff", SF_FORMAT_AIFF | SF_FORMAT_ALAW, 2, 0.04) ; + /* Lite remove start */ + lcomp_test_float ("alaw.aiff", SF_FORMAT_AIFF | SF_FORMAT_ALAW, 2, 0.04) ; + lcomp_test_double ("alaw.aiff", SF_FORMAT_AIFF | SF_FORMAT_ALAW, 2, 0.04) ; + /* Lite remove end */ + test_count++ ; + } ; + + if (do_all || strcmp (argv [1], "aiff_gsm610") == 0) + { /* Don't do lcomp_test_XXX as the errors are too big. */ + sdlcomp_test_short ("gsm610.aiff", SF_FORMAT_AIFF | SF_FORMAT_GSM610, 1, 0.24) ; + sdlcomp_test_int ("gsm610.aiff", SF_FORMAT_AIFF | SF_FORMAT_GSM610, 1, 0.24) ; + /* Lite remove start */ + sdlcomp_test_float ("gsm610.aiff", SF_FORMAT_AIFF | SF_FORMAT_GSM610, 1, 0.24) ; + sdlcomp_test_double ("gsm610.aiff", SF_FORMAT_AIFF | SF_FORMAT_GSM610, 1, 0.24) ; + /* Lite remove end */ + test_count++ ; + } ; + + if (strcmp (argv [1], "aiff_ima") == 0) + { lcomp_test_short ("ima.aiff", SF_FORMAT_AIFF | SF_FORMAT_IMA_ADPCM, 2, 0.18) ; + lcomp_test_int ("ima.aiff", SF_FORMAT_AIFF | SF_FORMAT_IMA_ADPCM, 2, 0.18) ; + /* Lite remove start */ + lcomp_test_float ("ima.aiff", SF_FORMAT_AIFF | SF_FORMAT_IMA_ADPCM, 2, 0.18) ; + lcomp_test_double ("ima.aiff", SF_FORMAT_AIFF | SF_FORMAT_IMA_ADPCM, 2, 0.18) ; + /* Lite remove end */ + } ; + + if (do_all || strcmp (argv [1], "au_ulaw") == 0) + { lcomp_test_short ("ulaw.au", SF_ENDIAN_BIG | SF_FORMAT_AU | SF_FORMAT_ULAW, 2, 0.04) ; + lcomp_test_int ("ulaw.au", SF_ENDIAN_LITTLE | SF_FORMAT_AU | SF_FORMAT_ULAW, 2, 0.04) ; + /* Lite remove start */ + lcomp_test_float ("ulaw.au", SF_ENDIAN_LITTLE | SF_FORMAT_AU | SF_FORMAT_ULAW, 2, 0.04) ; + lcomp_test_double ("ulaw.au", SF_ENDIAN_BIG | SF_FORMAT_AU | SF_FORMAT_ULAW, 2, 0.04) ; + /* Lite remove end */ + test_count++ ; + } ; + + if (do_all || strcmp (argv [1], "au_alaw") == 0) + { lcomp_test_short ("alaw.au", SF_ENDIAN_LITTLE | SF_FORMAT_AU | SF_FORMAT_ALAW, 2, 0.04) ; + lcomp_test_int ("alaw.au", SF_ENDIAN_BIG | SF_FORMAT_AU | SF_FORMAT_ALAW, 2, 0.04) ; + /* Lite remove start */ + lcomp_test_float ("alaw.au", SF_ENDIAN_BIG | SF_FORMAT_AU | SF_FORMAT_ALAW, 2, 0.04) ; + lcomp_test_double ("alaw.au", SF_ENDIAN_LITTLE | SF_FORMAT_AU | SF_FORMAT_ALAW, 2, 0.04) ; + /* Lite remove end */ + test_count++ ; + } ; + + /* Lite remove start */ + if (do_all || strcmp (argv [1], "au_g721") == 0) + { printf ("**** Fix this later : error bound should be 0.06 ****\n") ; + lcomp_test_short ("g721.au", SF_ENDIAN_LITTLE | SF_FORMAT_AU | SF_FORMAT_G721_32, 1, 0.7) ; + lcomp_test_int ("g721.au", SF_ENDIAN_BIG | SF_FORMAT_AU | SF_FORMAT_G721_32, 1, 0.7) ; + lcomp_test_float ("g721.au", SF_ENDIAN_LITTLE | SF_FORMAT_AU | SF_FORMAT_G721_32, 1, 0.7) ; + lcomp_test_double ("g721.au", SF_ENDIAN_BIG | SF_FORMAT_AU | SF_FORMAT_G721_32, 1, 0.7) ; + +/*- sdlcomp_test_short ("g721.au", SF_ENDIAN_BIG | SF_FORMAT_AU | SF_FORMAT_G721_32, 1, 0.07) ; + sdlcomp_test_int ("g721.au", SF_ENDIAN_LITTLE | SF_FORMAT_AU | SF_FORMAT_G721_32, 1, 0.07) ; + sdlcomp_test_float ("g721.au", SF_ENDIAN_BIG | SF_FORMAT_AU | SF_FORMAT_G721_32, 1, 0.07) ; + sdlcomp_test_double ("g721.au", SF_ENDIAN_LITTLE | SF_FORMAT_AU | SF_FORMAT_G721_32, 1, 0.12) ; +-*/ + test_count++ ; + } ; + + if (do_all || strcmp (argv [1], "au_g723") == 0) + { printf ("**** Fix this later : error bound should be 0.16 ****\n") ; + lcomp_test_short ("g723_24.au", SF_ENDIAN_LITTLE | SF_FORMAT_AU | SF_FORMAT_G723_24, 1, 0.7) ; + lcomp_test_int ("g723_24.au", SF_ENDIAN_BIG | SF_FORMAT_AU | SF_FORMAT_G723_24, 1, 0.7) ; + lcomp_test_float ("g723_24.au", SF_ENDIAN_LITTLE | SF_FORMAT_AU | SF_FORMAT_G723_24, 1, 0.7) ; + lcomp_test_double ("g723_24.au", SF_ENDIAN_BIG | SF_FORMAT_AU | SF_FORMAT_G723_24, 1, 0.7) ; + + lcomp_test_short ("g723_40.au", SF_ENDIAN_LITTLE | SF_FORMAT_AU | SF_FORMAT_G723_40, 1, 0.85) ; + lcomp_test_int ("g723_40.au", SF_ENDIAN_BIG | SF_FORMAT_AU | SF_FORMAT_G723_40, 1, 0.84) ; + lcomp_test_float ("g723_40.au", SF_ENDIAN_LITTLE | SF_FORMAT_AU | SF_FORMAT_G723_40, 1, 0.86) ; + lcomp_test_double ("g723_40.au", SF_ENDIAN_BIG | SF_FORMAT_AU | SF_FORMAT_G723_40, 1, 0.86) ; + +/*- sdlcomp_test_short ("g723.au", SF_ENDIAN_BIG | SF_FORMAT_AU | SF_FORMAT_G723_24, 1, 0.15) ; + sdlcomp_test_int ("g723.au", SF_ENDIAN_LITTLE | SF_FORMAT_AU | SF_FORMAT_G723_24, 1, 0.15) ; + sdlcomp_test_float ("g723.au", SF_ENDIAN_BIG | SF_FORMAT_AU | SF_FORMAT_G723_24, 1, 0.15) ; + sdlcomp_test_double ("g723.au", SF_ENDIAN_LITTLE | SF_FORMAT_AU | SF_FORMAT_G723_24, 1, 0.15) ; +-*/ + test_count++ ; + } ; + /* Lite remove end */ + + if (do_all || strcmp (argv [1], "caf_ulaw") == 0) + { lcomp_test_short ("ulaw.caf", SF_FORMAT_CAF | SF_FORMAT_ULAW, 2, 0.04) ; + lcomp_test_int ("ulaw.caf", SF_FORMAT_CAF | SF_FORMAT_ULAW, 2, 0.04) ; + /* Lite remove start */ + lcomp_test_float ("ulaw.caf", SF_FORMAT_CAF | SF_FORMAT_ULAW, 2, 0.04) ; + lcomp_test_double ("ulaw.caf", SF_FORMAT_CAF | SF_FORMAT_ULAW, 2, 0.04) ; + /* Lite remove end */ + test_count++ ; + } ; + + if (do_all || strcmp (argv [1], "caf_alaw") == 0) + { lcomp_test_short ("alaw.caf", SF_FORMAT_CAF | SF_FORMAT_ALAW, 2, 0.04) ; + lcomp_test_int ("alaw.caf", SF_FORMAT_CAF | SF_FORMAT_ALAW, 2, 0.04) ; + /* Lite remove start */ + lcomp_test_float ("alaw.caf", SF_FORMAT_CAF | SF_FORMAT_ALAW, 2, 0.04) ; + lcomp_test_double ("alaw.caf", SF_FORMAT_CAF | SF_FORMAT_ALAW, 2, 0.04) ; + /* Lite remove end */ + test_count++ ; + } ; + + + if (do_all || strcmp (argv [1], "raw_ulaw") == 0) + { lcomp_test_short ("ulaw.raw", SF_ENDIAN_LITTLE | SF_FORMAT_RAW | SF_FORMAT_ULAW, 2, 0.04) ; + lcomp_test_int ("ulaw.raw", SF_ENDIAN_BIG | SF_FORMAT_RAW | SF_FORMAT_ULAW, 2, 0.04) ; + /* Lite remove start */ + lcomp_test_float ("ulaw.raw", SF_ENDIAN_LITTLE | SF_FORMAT_RAW | SF_FORMAT_ULAW, 2, 0.04) ; + lcomp_test_double ("ulaw.raw", SF_ENDIAN_BIG | SF_FORMAT_RAW | SF_FORMAT_ULAW, 2, 0.04) ; + /* Lite remove end */ + test_count++ ; + } ; + + if (do_all || strcmp (argv [1], "raw_alaw") == 0) + { lcomp_test_short ("alaw.raw", SF_ENDIAN_LITTLE | SF_FORMAT_RAW | SF_FORMAT_ALAW, 2, 0.04) ; + lcomp_test_int ("alaw.raw", SF_ENDIAN_BIG | SF_FORMAT_RAW | SF_FORMAT_ALAW, 2, 0.04) ; + /* Lite remove start */ + lcomp_test_float ("alaw.raw", SF_ENDIAN_LITTLE | SF_FORMAT_RAW | SF_FORMAT_ALAW, 2, 0.04) ; + lcomp_test_double ("alaw.raw", SF_ENDIAN_BIG | SF_FORMAT_RAW | SF_FORMAT_ALAW, 2, 0.04) ; + /* Lite remove end */ + test_count++ ; + } ; + + if (do_all || strcmp (argv [1], "raw_gsm610") == 0) + { /* Don't do lcomp_test_XXX as the errors are too big. */ + sdlcomp_test_short ("raw.gsm", SF_FORMAT_RAW | SF_FORMAT_GSM610, 1, 0.24) ; + sdlcomp_test_int ("raw.gsm", SF_FORMAT_RAW | SF_FORMAT_GSM610, 1, 0.24) ; + sdlcomp_test_float ("raw.gsm", SF_FORMAT_RAW | SF_FORMAT_GSM610, 1, 0.24) ; + sdlcomp_test_double ("raw.gsm", SF_FORMAT_RAW | SF_FORMAT_GSM610, 1, 0.24) ; + test_count++ ; + } ; + + /* Lite remove start */ + if (do_all || strcmp (argv [1], "ircam_ulaw") == 0) + { lcomp_test_short ("ulaw.ircam", SF_ENDIAN_LITTLE | SF_FORMAT_IRCAM | SF_FORMAT_ULAW, 2, 0.04) ; + lcomp_test_int ("ulaw.ircam", SF_ENDIAN_BIG | SF_FORMAT_IRCAM | SF_FORMAT_ULAW, 2, 0.04) ; + lcomp_test_float ("ulaw.ircam", SF_ENDIAN_LITTLE | SF_FORMAT_IRCAM | SF_FORMAT_ULAW, 2, 0.04) ; + lcomp_test_double ("ulaw.ircam", SF_ENDIAN_BIG | SF_FORMAT_IRCAM | SF_FORMAT_ULAW, 2, 0.04) ; + test_count++ ; + } ; + + if (do_all || strcmp (argv [1], "ircam_alaw") == 0) + { lcomp_test_short ("alaw.ircam", SF_ENDIAN_LITTLE | SF_FORMAT_IRCAM | SF_FORMAT_ALAW, 2, 0.04) ; + lcomp_test_int ("alaw.ircam", SF_ENDIAN_BIG | SF_FORMAT_IRCAM | SF_FORMAT_ALAW, 2, 0.04) ; + lcomp_test_float ("alaw.ircam", SF_ENDIAN_LITTLE | SF_FORMAT_IRCAM | SF_FORMAT_ALAW, 2, 0.04) ; + lcomp_test_double ("alaw.ircam", SF_ENDIAN_BIG | SF_FORMAT_IRCAM | SF_FORMAT_ALAW, 2, 0.04) ; + test_count++ ; + } ; + + if (do_all || strcmp (argv [1], "nist_ulaw") == 0) + { lcomp_test_short ("ulaw.nist", SF_ENDIAN_LITTLE | SF_FORMAT_NIST | SF_FORMAT_ULAW, 2, 0.04) ; + lcomp_test_int ("ulaw.nist", SF_ENDIAN_BIG | SF_FORMAT_NIST | SF_FORMAT_ULAW, 2, 0.04) ; + lcomp_test_float ("ulaw.nist", SF_ENDIAN_LITTLE | SF_FORMAT_NIST | SF_FORMAT_ULAW, 2, 0.04) ; + lcomp_test_double ("ulaw.nist", SF_ENDIAN_BIG | SF_FORMAT_NIST | SF_FORMAT_ULAW, 2, 0.04) ; + test_count++ ; + } ; + + if (do_all || strcmp (argv [1], "nist_alaw") == 0) + { lcomp_test_short ("alaw.nist", SF_ENDIAN_LITTLE | SF_FORMAT_NIST | SF_FORMAT_ALAW, 2, 0.04) ; + lcomp_test_int ("alaw.nist", SF_ENDIAN_BIG | SF_FORMAT_NIST | SF_FORMAT_ALAW, 2, 0.04) ; + lcomp_test_float ("alaw.nist", SF_ENDIAN_LITTLE | SF_FORMAT_NIST | SF_FORMAT_ALAW, 2, 0.04) ; + lcomp_test_double ("alaw.nist", SF_ENDIAN_BIG | SF_FORMAT_NIST | SF_FORMAT_ALAW, 2, 0.04) ; + test_count++ ; + } ; + + if (do_all || strcmp (argv [1], "voc_ulaw") == 0) + { lcomp_test_short ("ulaw.voc", SF_FORMAT_VOC | SF_FORMAT_ULAW, 2, 0.04) ; + lcomp_test_int ("ulaw.voc", SF_FORMAT_VOC | SF_FORMAT_ULAW, 2, 0.04) ; + lcomp_test_float ("ulaw.voc", SF_FORMAT_VOC | SF_FORMAT_ULAW, 2, 0.04) ; + lcomp_test_double ("ulaw.voc", SF_FORMAT_VOC | SF_FORMAT_ULAW, 2, 0.04) ; + test_count++ ; + } ; + + if (do_all || strcmp (argv [1], "voc_alaw") == 0) + { lcomp_test_short ("alaw.voc", SF_FORMAT_VOC | SF_FORMAT_ALAW, 2, 0.04) ; + lcomp_test_int ("alaw.voc", SF_FORMAT_VOC | SF_FORMAT_ALAW, 2, 0.04) ; + lcomp_test_float ("alaw.voc", SF_FORMAT_VOC | SF_FORMAT_ALAW, 2, 0.04) ; + lcomp_test_double ("alaw.voc", SF_FORMAT_VOC | SF_FORMAT_ALAW, 2, 0.04) ; + test_count++ ; + } ; + /* Lite remove end */ + + if (do_all || strcmp (argv [1], "w64_ulaw") == 0) + { lcomp_test_short ("ulaw.w64", SF_FORMAT_W64 | SF_FORMAT_ULAW, 2, 0.04) ; + lcomp_test_int ("ulaw.w64", SF_FORMAT_W64 | SF_FORMAT_ULAW, 2, 0.04) ; + /* Lite remove start */ + lcomp_test_float ("ulaw.w64", SF_FORMAT_W64 | SF_FORMAT_ULAW, 2, 0.04) ; + lcomp_test_double ("ulaw.w64", SF_FORMAT_W64 | SF_FORMAT_ULAW, 2, 0.04) ; + /* Lite remove end */ + test_count++ ; + } ; + + if (do_all || strcmp (argv [1], "w64_alaw") == 0) + { lcomp_test_short ("alaw.w64", SF_FORMAT_W64 | SF_FORMAT_ALAW, 2, 0.04) ; + lcomp_test_int ("alaw.w64", SF_FORMAT_W64 | SF_FORMAT_ALAW, 2, 0.04) ; + /* Lite remove start */ + lcomp_test_float ("alaw.w64", SF_FORMAT_W64 | SF_FORMAT_ALAW, 2, 0.04) ; + lcomp_test_double ("alaw.w64", SF_FORMAT_W64 | SF_FORMAT_ALAW, 2, 0.04) ; + /* Lite remove end */ + test_count++ ; + } ; + + /* Lite remove start */ + if (do_all || strcmp (argv [1], "w64_ima") == 0) + { lcomp_test_short ("ima.w64", SF_FORMAT_W64 | SF_FORMAT_IMA_ADPCM, 2, 0.18) ; + lcomp_test_int ("ima.w64", SF_FORMAT_W64 | SF_FORMAT_IMA_ADPCM, 2, 0.18) ; + lcomp_test_float ("ima.w64", SF_FORMAT_W64 | SF_FORMAT_IMA_ADPCM, 2, 0.18) ; + lcomp_test_double ("ima.w64", SF_FORMAT_W64 | SF_FORMAT_IMA_ADPCM, 2, 0.18) ; + + sdlcomp_test_short ("ima.w64", SF_FORMAT_W64 | SF_FORMAT_IMA_ADPCM, 2, 0.18) ; + sdlcomp_test_int ("ima.w64", SF_FORMAT_W64 | SF_FORMAT_IMA_ADPCM, 2, 0.18) ; + sdlcomp_test_float ("ima.w64", SF_FORMAT_W64 | SF_FORMAT_IMA_ADPCM, 2, 0.18) ; + sdlcomp_test_double ("ima.w64", SF_FORMAT_W64 | SF_FORMAT_IMA_ADPCM, 2, 0.18) ; + test_count++ ; + } ; + + if (do_all || strcmp (argv [1], "w64_msadpcm") == 0) + { lcomp_test_short ("msadpcm.w64", SF_FORMAT_W64 | SF_FORMAT_MS_ADPCM, 2, 0.36) ; + lcomp_test_int ("msadpcm.w64", SF_FORMAT_W64 | SF_FORMAT_MS_ADPCM, 2, 0.36) ; + lcomp_test_float ("msadpcm.w64", SF_FORMAT_W64 | SF_FORMAT_MS_ADPCM, 2, 0.36) ; + lcomp_test_double ("msadpcm.w64", SF_FORMAT_W64 | SF_FORMAT_MS_ADPCM, 2, 0.36) ; + + sdlcomp_test_short ("msadpcm.w64", SF_FORMAT_W64 | SF_FORMAT_MS_ADPCM, 2, 0.36) ; + sdlcomp_test_int ("msadpcm.w64", SF_FORMAT_W64 | SF_FORMAT_MS_ADPCM, 2, 0.36) ; + sdlcomp_test_float ("msadpcm.w64", SF_FORMAT_W64 | SF_FORMAT_MS_ADPCM, 2, 0.36) ; + sdlcomp_test_double ("msadpcm.w64", SF_FORMAT_W64 | SF_FORMAT_MS_ADPCM, 2, 0.36) ; + test_count++ ; + } ; + + if (do_all || strcmp (argv [1], "wve") == 0) + { lcomp_test_short ("psion.wve", SF_FORMAT_WVE | SF_FORMAT_ALAW, 1, 0.04) ; + lcomp_test_int ("psion.wve", SF_FORMAT_WVE | SF_FORMAT_ALAW, 1, 0.04) ; + /* Lite remove start */ + lcomp_test_float ("psion.wve", SF_FORMAT_WVE | SF_FORMAT_ALAW, 1, 0.04) ; + lcomp_test_double ("psion.wve", SF_FORMAT_WVE | SF_FORMAT_ALAW, 1, 0.04) ; + /* Lite remove end */ + test_count++ ; + } ; + + /* Lite remove end */ + + if (do_all || strcmp (argv [1], "w64_gsm610") == 0) + { /* Don't do lcomp_test_XXX as the errors are too big. */ + sdlcomp_test_short ("gsm610.w64", SF_FORMAT_W64 | SF_FORMAT_GSM610, 1, 0.2) ; + sdlcomp_test_int ("gsm610.w64", SF_FORMAT_W64 | SF_FORMAT_GSM610, 1, 0.2) ; + /* Lite remove start */ + sdlcomp_test_float ("gsm610.w64", SF_FORMAT_W64 | SF_FORMAT_GSM610, 1, 0.2) ; + sdlcomp_test_double ("gsm610.w64", SF_FORMAT_W64 | SF_FORMAT_GSM610, 1, 0.2) ; + /* Lite remove end */ + test_count++ ; + } ; + + /* Lite remove start */ + if (do_all || strcmp (argv [1], "vox_adpcm") == 0) + { lcomp_test_short ("adpcm.vox", SF_FORMAT_RAW | SF_FORMAT_VOX_ADPCM, 1, 0.17) ; + lcomp_test_int ("adpcm.vox", SF_FORMAT_RAW | SF_FORMAT_VOX_ADPCM, 1, 0.17) ; + lcomp_test_float ("adpcm.vox", SF_FORMAT_RAW | SF_FORMAT_VOX_ADPCM, 1, 0.17) ; + lcomp_test_double ("adpcm.vox", SF_FORMAT_RAW | SF_FORMAT_VOX_ADPCM, 1, 0.17) ; + + sdlcomp_test_short ("adpcm.vox", SF_FORMAT_RAW | SF_FORMAT_VOX_ADPCM, 1, 0.072) ; + sdlcomp_test_int ("adpcm.vox", SF_FORMAT_RAW | SF_FORMAT_VOX_ADPCM, 1, 0.072) ; + sdlcomp_test_float ("adpcm.vox", SF_FORMAT_RAW | SF_FORMAT_VOX_ADPCM, 1, 0.072) ; + sdlcomp_test_double ("adpcm.vox", SF_FORMAT_RAW | SF_FORMAT_VOX_ADPCM, 1, 0.072) ; + test_count++ ; + } ; + + if (do_all || strcmp (argv [1], "xi_dpcm") == 0) + { lcomp_test_short ("8bit.xi", SF_FORMAT_XI | SF_FORMAT_DPCM_8, 1, 0.25) ; + lcomp_test_int ("8bit.xi", SF_FORMAT_XI | SF_FORMAT_DPCM_8, 1, 0.25) ; + + lcomp_test_short ("16bit.xi", SF_FORMAT_XI | SF_FORMAT_DPCM_16, 1, 0.002) ; + lcomp_test_int ("16bit.xi", SF_FORMAT_XI | SF_FORMAT_DPCM_16, 1, 0.002) ; + lcomp_test_float ("16bit.xi", SF_FORMAT_XI | SF_FORMAT_DPCM_16, 1, 0.002) ; + lcomp_test_double ("16bit.xi", SF_FORMAT_XI | SF_FORMAT_DPCM_16, 1, 0.002) ; + test_count++ ; + } ; + /* Lite remove end */ + + if (test_count == 0) + { printf ("************************************\n") ; + printf ("* No '%s' test defined.\n", argv [1]) ; + printf ("************************************\n") ; + return 1 ; + } ; + + return 0 ; +} /* main */ + +/*============================================================================================ +** Here are the test functions. +*/ + +static void +lcomp_test_short (const char *filename, int filetype, int channels, double margin) +{ SNDFILE *file ; + SF_INFO sfinfo ; + int k, m, seekpos, half_max_abs ; + long datalen ; + short *orig, *data ; + + print_test_name ("lcomp_test_short", filename) ; + + datalen = BUFFER_SIZE / channels ; + + data = data_buffer.s ; + orig = orig_buffer.s ; + + gen_signal_double (orig_buffer.d, 32000.0, channels, datalen) ; + for (k = 0 ; k < channels * datalen ; k++) + orig [k] = (short) (orig_buffer.d [k]) ; + + sfinfo.samplerate = SAMPLE_RATE ; + sfinfo.frames = 123456789 ; /* Ridiculous value. */ + sfinfo.channels = channels ; + sfinfo.format = filetype ; + + file = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, SF_FALSE, __LINE__) ; + test_writef_short_or_die (file, 0, orig, datalen, __LINE__) ; + sf_set_string (file, SF_STR_COMMENT, long_comment) ; + sf_close (file) ; + + memset (data, 0, datalen * sizeof (short)) ; + + if ((filetype & SF_FORMAT_TYPEMASK) != SF_FORMAT_RAW) + memset (&sfinfo, 0, sizeof (sfinfo)) ; + + file = test_open_file_or_die (filename, SFM_READ, &sfinfo, SF_FALSE, __LINE__) ; + + if ((sfinfo.format & (SF_FORMAT_TYPEMASK | SF_FORMAT_SUBMASK)) != (filetype & (SF_FORMAT_TYPEMASK | SF_FORMAT_SUBMASK))) + { printf ("\n\nLine %d: Returned format incorrect (0x%08X => 0x%08X).\n", __LINE__, filetype, sfinfo.format) ; + exit (1) ; + } ; + + if (sfinfo.frames < datalen / channels) + { printf ("Too few frames in file. (%ld should be a little more than %ld)\n", SF_COUNT_TO_LONG (sfinfo.frames), datalen) ; + exit (1) ; + } ; + + if (sfinfo.frames > (datalen + datalen / 20)) + { printf ("Too many frames in file. (%ld should be a little more than %ld)\n", SF_COUNT_TO_LONG (sfinfo.frames), datalen) ; + exit (1) ; + } ; + + if (sfinfo.channels != channels) + { printf ("Incorrect number of channels in file.\n") ; + exit (1) ; + } ; + + check_log_buffer_or_die (file, __LINE__) ; + + check_comment (file, filetype, __LINE__) ; + + test_readf_short_or_die (file, 0, data, datalen, __LINE__) ; + + half_max_abs = 0 ; + for (k = 0 ; k < datalen ; k++) + { if (error_function (data [k], orig [k], margin)) + { printf ("\n\nLine %d: Incorrect sample A (#%d : %d should be %d).\n", __LINE__, k, data [k], orig [k]) ; + oct_save_short (orig, data, datalen) ; + exit (1) ; + } ; + half_max_abs = LCT_MAX (half_max_abs, abs (data [k] / 2)) ; + } ; + + if (half_max_abs < 1.0) + { printf ("\n\nLine %d: Signal is all zeros.\n", __LINE__) ; + exit (1) ; + } ; + + if ((k = sf_readf_short (file, data, datalen)) != sfinfo.frames - datalen) + { printf ("\n\nLine %d: Incorrect read length (%ld should be %d).\n", __LINE__, + SF_COUNT_TO_LONG (channels * sfinfo.frames - datalen), k) ; + exit (1) ; + } ; + + /* This check is only for block based encoders which must append silence + ** to the end of a file so as to fill out a block. + */ + for (k = 0 ; k < sfinfo.frames - datalen ; k++) + if (abs (data [channels * k]) > decay_response (channels * k)) + { printf ("\n\nLine %d : Incorrect sample B (#%d : abs (%d) should be < %d).\n", __LINE__, channels * k, data [channels * k], decay_response (channels * k)) ; + exit (1) ; + } ; + + if (! sfinfo.seekable) + { sf_close (file) ; + unlink (filename) ; + printf ("ok\n") ; + return ; + } ; + + /* Now test sf_seek function. */ + + if ((k = sf_seek (file, 0, SEEK_SET)) != 0) + { printf ("\n\nLine %d: Seek to start of file failed (%d).\n", __LINE__, k) ; + exit (1) ; + } ; + + for (m = 0 ; m < 3 ; m++) + { test_readf_short_or_die (file, m, data, 11, __LINE__) ; + + for (k = 0 ; k < channels * 11 ; k++) + if (error_function ((double) data [k], (double) orig [k + channels * m * 11], margin)) + { printf ("\n\nLine %d: Incorrect sample (m = %d) (#%d : %d => %d).\n", __LINE__, m, k + channels * m * 11, orig [k + channels * m * 11], data [k]) ; + for (m = 0 ; m < channels ; m++) + printf ("%d ", data [m]) ; + printf ("\n") ; + exit (1) ; + } ; + } ; + + seekpos = BUFFER_SIZE / 10 ; + + /* Check seek from start of file. */ + if ((k = sf_seek (file, seekpos, SEEK_SET)) != seekpos) + { printf ("Seek to start of file + %d failed (%d).\n", seekpos, k) ; + exit (1) ; + } ; + + test_readf_short_or_die (file, 0, data, 1, __LINE__) ; + + if (error_function ((double) data [0], (double) orig [seekpos * channels], margin)) + { printf ("\n\nLine %d: sf_seek (SEEK_SET) followed by sf_readf_short failed (%d, %d).\n", __LINE__, orig [1], data [0]) ; + exit (1) ; + } ; + + if ((k = sf_seek (file, 0, SEEK_CUR)) != seekpos + 1) + { printf ("\n\nLine %d: sf_seek (SEEK_CUR) with 0 offset failed (%d should be %d)\n", __LINE__, k, seekpos + 1) ; + exit (1) ; + } ; + + seekpos = sf_seek (file, 0, SEEK_CUR) + BUFFER_SIZE / 5 ; + k = sf_seek (file, BUFFER_SIZE / 5, SEEK_CUR) ; + test_readf_short_or_die (file, 0, data, 1, __LINE__) ; + if (error_function ((double) data [0], (double) orig [seekpos * channels], margin) || k != seekpos) + { printf ("\n\nLine %d: sf_seek (forwards, SEEK_CUR) followed by sf_readf_short failed (%d, %d) (%d, %d).\n", __LINE__, data [0], orig [seekpos * channels], k, seekpos + 1) ; + oct_save_short (orig, data, datalen) ; + exit (1) ; + } ; + + seekpos = sf_seek (file, 0, SEEK_CUR) - 20 ; + /* Check seek backward from current position. */ + k = sf_seek (file, -20, SEEK_CUR) ; + test_readf_short_or_die (file, 0, data, 1, __LINE__) ; + if (error_function ((double) data [0], (double) orig [seekpos * channels], margin) || k != seekpos) + { printf ("sf_seek (backwards, SEEK_CUR) followed by sf_readf_short failed (%d, %d) (%d, %d).\n", data [0], orig [seekpos * channels], k, seekpos) ; + exit (1) ; + } ; + + /* Check that read past end of file returns number of items. */ + sf_seek (file, (short) sfinfo.frames, SEEK_SET) ; + + if ((k = sf_readf_short (file, data, datalen)) != 0) + { printf ("\n\nLine %d: Return value from sf_readf_short past end of file incorrect (%d).\n", __LINE__, k) ; + exit (1) ; + } ; + + /* Check seek backward from end. */ + if ((k = sf_seek (file, 5 - (short) sfinfo.frames, SEEK_END)) != 5) + { printf ("sf_seek (SEEK_END) returned %d instead of %d.\n", k, 5) ; + exit (1) ; + } ; + + test_readf_short_or_die (file, 0, data, channels, __LINE__) ; + if (error_function ((double) data [0], (double) orig [5], margin)) + { printf ("\n\nLine %d: sf_seek (SEEK_END) followed by sf_readf_short failed (%d should be %d).\n", __LINE__, data [0], orig [5]) ; + exit (1) ; + } ; + + sf_close (file) ; + + unlink (filename) ; + printf ("ok\n") ; +} /* lcomp_test_short */ + +/*-------------------------------------------------------------------------------------------- +*/ + +static void +lcomp_test_int (const char *filename, int filetype, int channels, double margin) +{ SNDFILE *file ; + SF_INFO sfinfo ; + int k, m, *orig, *data, half_max_abs ; + long datalen, seekpos ; + double scale ; + + print_test_name ("lcomp_test_int", filename) ; + + datalen = BUFFER_SIZE / channels ; + + scale = 1.0 * 0x10000 ; + + data = data_buffer.i ; + orig = orig_buffer.i ; + + gen_signal_double (orig_buffer.d, 32000.0 * scale, channels, datalen) ; + for (k = 0 ; k < channels * datalen ; k++) + orig [k] = orig_buffer.d [k] ; + + sfinfo.samplerate = SAMPLE_RATE ; + sfinfo.frames = 123456789 ; /* Ridiculous value. */ + sfinfo.channels = channels ; + sfinfo.format = filetype ; + + file = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, SF_FALSE, __LINE__) ; + test_writef_int_or_die (file, 0, orig, datalen, __LINE__) ; + sf_set_string (file, SF_STR_COMMENT, long_comment) ; + sf_close (file) ; + + memset (data, 0, datalen * sizeof (int)) ; + + if ((filetype & SF_FORMAT_TYPEMASK) != SF_FORMAT_RAW) + memset (&sfinfo, 0, sizeof (sfinfo)) ; + + file = test_open_file_or_die (filename, SFM_READ, &sfinfo, SF_FALSE, __LINE__) ; + + if ((sfinfo.format & (SF_FORMAT_TYPEMASK | SF_FORMAT_SUBMASK)) != (filetype & (SF_FORMAT_TYPEMASK | SF_FORMAT_SUBMASK))) + { printf ("\n\nLine %d: Returned format incorrect (0x%08X => 0x%08X).\n", __LINE__, filetype, sfinfo.format) ; + exit (1) ; + } ; + + if (sfinfo.frames < datalen / channels) + { printf ("Too few.frames in file. (%ld should be a little more than %ld)\n", datalen, SF_COUNT_TO_LONG (sfinfo.frames)) ; + exit (1) ; + } ; + + if (sfinfo.frames > (datalen + datalen / 20)) + { printf ("Too many.frames in file. (%ld should be a little more than %ld)\n", datalen, SF_COUNT_TO_LONG (sfinfo.frames)) ; + exit (1) ; + } ; + + if (sfinfo.channels != channels) + { printf ("Incorrect number of channels in file.\n") ; + exit (1) ; + } ; + + check_log_buffer_or_die (file, __LINE__) ; + + check_comment (file, filetype, __LINE__) ; + + test_readf_int_or_die (file, 0, data, datalen, __LINE__) ; + + half_max_abs = 0 ; + for (k = 0 ; k < datalen ; k++) + { if (error_function (data [k] / scale, orig [k] / scale, margin)) + { printf ("\n\nLine %d: Incorrect sample (#%d : %f should be %f).\n", __LINE__, k, data [k] / scale, orig [k] / scale) ; + oct_save_int (orig, data, datalen) ; + exit (1) ; + } ; + half_max_abs = LCT_MAX (half_max_abs, abs (data [k] / 2)) ; + } ; + + if (half_max_abs < 1.0) + { printf ("\n\nLine %d: Signal is all zeros (%d, 0x%X).\n", __LINE__, half_max_abs, half_max_abs) ; + exit (1) ; + } ; + + if ((k = sf_readf_int (file, data, datalen)) != sfinfo.frames - datalen) + { printf ("\n\nLine %d: Incorrect read length (%ld should be %d).\n", __LINE__, + SF_COUNT_TO_LONG (channels * sfinfo.frames - datalen), k) ; + exit (1) ; + } ; + + /* This check is only for block based encoders which must append silence + ** to the end of a file so as to fill out a block. + */ + if ((sfinfo.format & SF_FORMAT_SUBMASK) != SF_FORMAT_MS_ADPCM) + for (k = 0 ; k < sfinfo.frames - datalen ; k++) + if (abs (data [channels * k] / scale) > decay_response (channels * k)) + { printf ("\n\nLine %d : Incorrect sample B (#%d : abs (%d) should be < %d).\n", __LINE__, channels * k, data [channels * k], decay_response (channels * k)) ; + exit (1) ; + } ; + + if (! sfinfo.seekable) + { sf_close (file) ; + unlink (filename) ; + printf ("ok\n") ; + return ; + } ; + + /* Now test sf_seek function. */ + + if ((k = sf_seek (file, 0, SEEK_SET)) != 0) + { printf ("\n\nLine %d: Seek to start of file failed (%d).\n", __LINE__, k) ; + exit (1) ; + } ; + + for (m = 0 ; m < 3 ; m++) + { test_readf_int_or_die (file, m, data, 11, __LINE__) ; + + for (k = 0 ; k < channels * 11 ; k++) + if (error_function (data [k] / scale, orig [k + channels * m * 11] / scale, margin)) + { printf ("\n\nLine %d: Incorrect sample (m = %d) (#%d : %d => %d).\n", __LINE__, m, k + channels * m * 11, orig [k + channels * m * 11], data [k]) ; + for (m = 0 ; m < channels ; m++) + printf ("%d ", data [m]) ; + printf ("\n") ; + exit (1) ; + } ; + } ; + + seekpos = BUFFER_SIZE / 10 ; + + /* Check seek from start of file. */ + if ((k = sf_seek (file, seekpos, SEEK_SET)) != seekpos) + { printf ("Seek to start of file + %ld failed (%d).\n", seekpos, k) ; + exit (1) ; + } ; + + test_readf_int_or_die (file, 0, data, 1, __LINE__) ; + + if (error_function ((double) data [0], (double) orig [seekpos * channels], margin)) + { printf ("\n\nLine %d: sf_seek (SEEK_SET) followed by sf_readf_int failed (%d, %d).\n", __LINE__, orig [1], data [0]) ; + exit (1) ; + } ; + + if ((k = sf_seek (file, 0, SEEK_CUR)) != seekpos + 1) + { printf ("\n\nLine %d: sf_seek (SEEK_CUR) with 0 offset failed (%d should be %ld)\n", __LINE__, k, seekpos + 1) ; + exit (1) ; + } ; + + seekpos = sf_seek (file, 0, SEEK_CUR) + BUFFER_SIZE / 5 ; + k = sf_seek (file, BUFFER_SIZE / 5, SEEK_CUR) ; + test_readf_int_or_die (file, 0, data, 1, __LINE__) ; + if (error_function ((double) data [0], (double) orig [seekpos * channels], margin) || k != seekpos) + { printf ("\n\nLine %d: sf_seek (forwards, SEEK_CUR) followed by sf_readf_int failed (%d, %d) (%d, %ld).\n", __LINE__, data [0], orig [seekpos * channels], k, seekpos + 1) ; + exit (1) ; + } ; + + seekpos = sf_seek (file, 0, SEEK_CUR) - 20 ; + /* Check seek backward from current position. */ + k = sf_seek (file, -20, SEEK_CUR) ; + test_readf_int_or_die (file, 0, data, 1, __LINE__) ; + if (error_function ((double) data [0], (double) orig [seekpos * channels], margin) || k != seekpos) + { printf ("sf_seek (backwards, SEEK_CUR) followed by sf_readf_int failed (%d, %d) (%d, %ld).\n", data [0], orig [seekpos * channels], k, seekpos) ; + exit (1) ; + } ; + + /* Check that read past end of file returns number of items. */ + sf_seek (file, (int) sfinfo.frames, SEEK_SET) ; + + if ((k = sf_readf_int (file, data, datalen)) != 0) + { printf ("\n\nLine %d: Return value from sf_readf_int past end of file incorrect (%d).\n", __LINE__, k) ; + exit (1) ; + } ; + + /* Check seek backward from end. */ + if ((k = sf_seek (file, 5 - (int) sfinfo.frames, SEEK_END)) != 5) + { printf ("sf_seek (SEEK_END) returned %d instead of %d.\n", k, 5) ; + exit (1) ; + } ; + + test_readf_int_or_die (file, 0, data, channels, __LINE__) ; + if (error_function (data [0] / scale, orig [5] / scale, margin)) + { printf ("\n\nLine %d: sf_seek (SEEK_END) followed by sf_readf_short failed (%d should be %d).\n", __LINE__, data [0], orig [5]) ; + exit (1) ; + } ; + + sf_close (file) ; + + unlink (filename) ; + printf ("ok\n") ; +} /* lcomp_test_int */ + +/*-------------------------------------------------------------------------------------------- +*/ + +static void +lcomp_test_float (const char *filename, int filetype, int channels, double margin) +{ SNDFILE *file ; + SF_INFO sfinfo ; + int k, m, seekpos ; + long datalen ; + float *orig, *data ; + double half_max_abs ; + + print_test_name ("lcomp_test_float", filename) ; + + datalen = BUFFER_SIZE / channels ; + + data = data_buffer.f ; + orig = orig_buffer.f ; + + gen_signal_double (orig_buffer.d, 32000.0, channels, datalen) ; + for (k = 0 ; k < channels * datalen ; k++) + orig [k] = (float) (orig_buffer.d [k]) ; + + sfinfo.samplerate = SAMPLE_RATE ; + sfinfo.frames = 123456789 ; /* Ridiculous value. */ + sfinfo.channels = channels ; + sfinfo.format = filetype ; + + file = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, SF_FALSE, __LINE__) ; + sf_command (file, SFC_SET_NORM_FLOAT, NULL, SF_FALSE) ; + test_writef_float_or_die (file, 0, orig, datalen, __LINE__) ; + sf_set_string (file, SF_STR_COMMENT, long_comment) ; + sf_close (file) ; + + memset (data, 0, datalen * sizeof (float)) ; + + if ((filetype & SF_FORMAT_TYPEMASK) != SF_FORMAT_RAW) + memset (&sfinfo, 0, sizeof (sfinfo)) ; + + file = test_open_file_or_die (filename, SFM_READ, &sfinfo, SF_FALSE, __LINE__) ; + + if ((sfinfo.format & (SF_FORMAT_TYPEMASK | SF_FORMAT_SUBMASK)) != (filetype & (SF_FORMAT_TYPEMASK | SF_FORMAT_SUBMASK))) + { printf ("\n\nLine %d: Returned format incorrect (0x%08X => 0x%08X).\n", __LINE__, filetype, sfinfo.format) ; + exit (1) ; + } ; + + if (sfinfo.frames < datalen / channels) + { printf ("Too few.frames in file. (%ld should be a little more than %ld)\n", datalen, SF_COUNT_TO_LONG (sfinfo.frames)) ; + exit (1) ; + } ; + + if (sfinfo.frames > (datalen + datalen / 20)) + { printf ("Too many.frames in file. (%ld should be a little more than %ld)\n", datalen, SF_COUNT_TO_LONG (sfinfo.frames)) ; + exit (1) ; + } ; + + if (sfinfo.channels != channels) + { printf ("Incorrect number of channels in file.\n") ; + exit (1) ; + } ; + + check_comment (file, filetype, __LINE__) ; + + sf_command (file, SFC_SET_NORM_FLOAT, NULL, SF_FALSE) ; + + check_log_buffer_or_die (file, __LINE__) ; + + check_comment (file, filetype, __LINE__) ; + + sf_command (file, SFC_SET_NORM_FLOAT, NULL, SF_FALSE) ; + + test_readf_float_or_die (file, 0, data, datalen, __LINE__) ; + + half_max_abs = 0.0 ; + for (k = 0 ; k < datalen ; k++) + { if (error_function ((double) data [k], (double) orig [k], margin)) + { printf ("\n\nLine %d: Incorrect sample A (#%d : %f should be %f).\n", __LINE__, k, data [k], orig [k]) ; + oct_save_float (orig, data, datalen) ; + exit (1) ; + } ; + half_max_abs = LCT_MAX (half_max_abs, fabs (0.5 * data [k])) ; + } ; + + if (half_max_abs < 1.0) + { printf ("\n\nLine %d: Signal is all zeros.\n", __LINE__) ; + exit (1) ; + } ; + + if ((k = sf_readf_float (file, data, datalen)) != sfinfo.frames - datalen) + { printf ("\n\nLine %d: Incorrect read length (%ld should be %d).\n", __LINE__, + SF_COUNT_TO_LONG (channels * sfinfo.frames - datalen), k) ; + exit (1) ; + } ; + + /* This check is only for block based encoders which must append silence + ** to the end of a file so as to fill out a block. + */ + if ((sfinfo.format & SF_FORMAT_SUBMASK) != SF_FORMAT_MS_ADPCM) + for (k = 0 ; k < sfinfo.frames - datalen ; k++) + if (abs (data [channels * k]) > decay_response (channels * k)) + { printf ("\n\nLine %d : Incorrect sample B (#%d : abs (%f) should be < %d).\n", __LINE__, channels * k, data [channels * k], decay_response (channels * k)) ; + exit (1) ; + } ; + + if (! sfinfo.seekable) + { sf_close (file) ; + unlink (filename) ; + printf ("ok\n") ; + return ; + } ; + + /* Now test sf_seek function. */ + + if ((k = sf_seek (file, 0, SEEK_SET)) != 0) + { printf ("\n\nLine %d: Seek to start of file failed (%d).\n", __LINE__, k) ; + exit (1) ; + } ; + + for (m = 0 ; m < 3 ; m++) + { test_readf_float_or_die (file, 0, data, 11, __LINE__) ; + + for (k = 0 ; k < channels * 11 ; k++) + if (error_function ((double) data [k], (double) orig [k + channels * m * 11], margin)) + { printf ("\n\nLine %d: Incorrect sample (m = %d) (#%d : %f => %f).\n", __LINE__, m, k + channels * m * 11, orig [k + channels * m * 11], data [k]) ; + for (m = 0 ; m < channels ; m++) + printf ("%f ", data [m]) ; + printf ("\n") ; + exit (1) ; + } ; + } ; + + seekpos = BUFFER_SIZE / 10 ; + + /* Check seek from start of file. */ + if ((k = sf_seek (file, seekpos, SEEK_SET)) != seekpos) + { printf ("Seek to start of file + %d failed (%d).\n", seekpos, k) ; + exit (1) ; + } ; + + test_readf_float_or_die (file, 0, data, 1, __LINE__) ; + + if (error_function ((double) data [0], (double) orig [seekpos * channels], margin)) + { printf ("\n\nLine %d: sf_seek (SEEK_SET) followed by sf_readf_float failed (%f, %f).\n", __LINE__, orig [1], data [0]) ; + exit (1) ; + } ; + + if ((k = sf_seek (file, 0, SEEK_CUR)) != seekpos + 1) + { printf ("\n\nLine %d: sf_seek (SEEK_CUR) with 0 offset failed (%d should be %d)\n", __LINE__, k, seekpos + 1) ; + exit (1) ; + } ; + + seekpos = sf_seek (file, 0, SEEK_CUR) + BUFFER_SIZE / 5 ; + k = sf_seek (file, BUFFER_SIZE / 5, SEEK_CUR) ; + test_readf_float_or_die (file, 0, data, 1, __LINE__) ; + if (error_function ((double) data [0], (double) orig [seekpos * channels], margin) || k != seekpos) + { printf ("\n\nLine %d: sf_seek (forwards, SEEK_CUR) followed by sf_readf_float failed (%f, %f) (%d, %d).\n", __LINE__, data [0], orig [seekpos * channels], k, seekpos + 1) ; + exit (1) ; + } ; + + seekpos = sf_seek (file, 0, SEEK_CUR) - 20 ; + /* Check seek backward from current position. */ + k = sf_seek (file, -20, SEEK_CUR) ; + test_readf_float_or_die (file, 0, data, 1, __LINE__) ; + if (error_function ((double) data [0], (double) orig [seekpos * channels], margin) || k != seekpos) + { printf ("sf_seek (backwards, SEEK_CUR) followed by sf_readf_float failed (%f, %f) (%d, %d).\n", data [0], orig [seekpos * channels], k, seekpos) ; + exit (1) ; + } ; + + /* Check that read past end of file returns number of items. */ + sf_seek (file, (float) sfinfo.frames, SEEK_SET) ; + + if ((k = sf_readf_float (file, data, datalen)) != 0) + { printf ("\n\nLine %d: Return value from sf_readf_float past end of file incorrect (%d).\n", __LINE__, k) ; + exit (1) ; + } ; + + /* Check seek backward from end. */ + if ((k = sf_seek (file, 5 - (float) sfinfo.frames, SEEK_END)) != 5) + { printf ("sf_seek (SEEK_END) returned %d instead of %d.\n", k, 5) ; + exit (1) ; + } ; + + test_readf_float_or_die (file, 0, data, channels, __LINE__) ; + if (error_function ((double) data [0], (double) orig [5], margin)) + { printf ("\n\nLine %d: sf_seek (SEEK_END) followed by sf_readf_short failed (%f should be %f).\n", __LINE__, data [0], orig [5]) ; + exit (1) ; + } ; + + sf_close (file) ; + + unlink (filename) ; + printf ("ok\n") ; +} /* lcomp_test_float */ + +/*-------------------------------------------------------------------------------------------- +*/ + +static void +lcomp_test_double (const char *filename, int filetype, int channels, double margin) +{ SNDFILE *file ; + SF_INFO sfinfo ; + int k, m, seekpos ; + long datalen ; + double *orig, *data ; + double half_max_abs ; + + print_test_name ("lcomp_test_double", filename) ; + + datalen = BUFFER_SIZE / channels ; + + data = data_buffer.d ; + orig = orig_buffer.d ; + + gen_signal_double (orig_buffer.d, 32000.0, channels, datalen) ; + for (k = 0 ; k < channels * datalen ; k++) + orig [k] = (double) (orig_buffer.d [k]) ; + + sfinfo.samplerate = SAMPLE_RATE ; + sfinfo.frames = 123456789 ; /* Ridiculous value. */ + sfinfo.channels = channels ; + sfinfo.format = filetype ; + + file = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, SF_FALSE, __LINE__) ; + sf_command (file, SFC_SET_NORM_DOUBLE, NULL, SF_FALSE) ; + test_writef_double_or_die (file, 0, orig, datalen, __LINE__) ; + sf_set_string (file, SF_STR_COMMENT, long_comment) ; + sf_close (file) ; + + memset (data, 0, datalen * sizeof (double)) ; + + if ((filetype & SF_FORMAT_TYPEMASK) != SF_FORMAT_RAW) + memset (&sfinfo, 0, sizeof (sfinfo)) ; + + file = test_open_file_or_die (filename, SFM_READ, &sfinfo, SF_FALSE, __LINE__) ; + + if ((sfinfo.format & (SF_FORMAT_TYPEMASK | SF_FORMAT_SUBMASK)) != (filetype & (SF_FORMAT_TYPEMASK | SF_FORMAT_SUBMASK))) + { printf ("\n\nLine %d: Returned format incorrect (0x%08X => 0x%08X).\n", __LINE__, filetype, sfinfo.format) ; + exit (1) ; + } ; + + if (sfinfo.frames < datalen / channels) + { printf ("Too few.frames in file. (%ld should be a little more than %ld)\n", datalen, SF_COUNT_TO_LONG (sfinfo.frames)) ; + exit (1) ; + } ; + + if (sfinfo.frames > (datalen + datalen / 20)) + { printf ("Too many.frames in file. (%ld should be a little more than %ld)\n", datalen, SF_COUNT_TO_LONG (sfinfo.frames)) ; + exit (1) ; + } ; + + if (sfinfo.channels != channels) + { printf ("Incorrect number of channels in file.\n") ; + exit (1) ; + } ; + + check_comment (file, filetype, __LINE__) ; + + sf_command (file, SFC_SET_NORM_DOUBLE, NULL, SF_FALSE) ; + + check_log_buffer_or_die (file, __LINE__) ; + + check_comment (file, filetype, __LINE__) ; + + sf_command (file, SFC_SET_NORM_DOUBLE, NULL, SF_FALSE) ; + + test_readf_double_or_die (file, 0, data, datalen, __LINE__) ; + + half_max_abs = 0.0 ; + for (k = 0 ; k < datalen ; k++) + { if (error_function ((double) data [k], (double) orig [k], margin)) + { printf ("\n\nLine %d: Incorrect sample A (#%d : %f should be %f).\n", __LINE__, k, data [k], orig [k]) ; + oct_save_double (orig, data, datalen) ; + exit (1) ; + } ; + half_max_abs = LCT_MAX (half_max_abs, abs (0.5 * data [k])) ; + } ; + + if (half_max_abs < 1.0) + { printf ("\n\nLine %d: Signal is all zeros.\n", __LINE__) ; + exit (1) ; + } ; + + if ((k = sf_readf_double (file, data, datalen)) != sfinfo.frames - datalen) + { printf ("\n\nLine %d: Incorrect read length (%ld should be %d).\n", __LINE__, + SF_COUNT_TO_LONG (channels * sfinfo.frames - datalen), k) ; + exit (1) ; + } ; + + /* This check is only for block based encoders which must append silence + ** to the end of a file so as to fill out a block. + */ + if ((sfinfo.format & SF_FORMAT_SUBMASK) != SF_FORMAT_MS_ADPCM) + for (k = 0 ; k < sfinfo.frames - datalen ; k++) + if (abs (data [channels * k]) > decay_response (channels * k)) + { printf ("\n\nLine %d : Incorrect sample B (#%d : abs (%f) should be < %d).\n", __LINE__, channels * k, data [channels * k], decay_response (channels * k)) ; + exit (1) ; + } ; + + if (! sfinfo.seekable) + { sf_close (file) ; + unlink (filename) ; + printf ("ok\n") ; + return ; + } ; + + /* Now test sf_seek function. */ + + if ((k = sf_seek (file, 0, SEEK_SET)) != 0) + { printf ("\n\nLine %d: Seek to start of file failed (%d).\n", __LINE__, k) ; + exit (1) ; + } ; + + for (m = 0 ; m < 3 ; m++) + { test_readf_double_or_die (file, m, data, 11, __LINE__) ; + + for (k = 0 ; k < channels * 11 ; k++) + if (error_function ((double) data [k], (double) orig [k + channels * m * 11], margin)) + { printf ("\n\nLine %d: Incorrect sample (m = %d) (#%d : %f => %f).\n", __LINE__, m, k + channels * m * 11, orig [k + channels * m * 11], data [k]) ; + for (m = 0 ; m < channels ; m++) + printf ("%f ", data [m]) ; + printf ("\n") ; + exit (1) ; + } ; + } ; + + seekpos = BUFFER_SIZE / 10 ; + + /* Check seek from start of file. */ + if ((k = sf_seek (file, seekpos, SEEK_SET)) != seekpos) + { printf ("Seek to start of file + %d failed (%d).\n", seekpos, k) ; + exit (1) ; + } ; + + test_readf_double_or_die (file, 0, data, 1, __LINE__) ; + + if (error_function ((double) data [0], (double) orig [seekpos * channels], margin)) + { printf ("\n\nLine %d: sf_seek (SEEK_SET) followed by sf_readf_double failed (%f, %f).\n", __LINE__, orig [1], data [0]) ; + exit (1) ; + } ; + + if ((k = sf_seek (file, 0, SEEK_CUR)) != seekpos + 1) + { printf ("\n\nLine %d: sf_seek (SEEK_CUR) with 0 offset failed (%d should be %d)\n", __LINE__, k, seekpos + 1) ; + exit (1) ; + } ; + + seekpos = sf_seek (file, 0, SEEK_CUR) + BUFFER_SIZE / 5 ; + k = sf_seek (file, BUFFER_SIZE / 5, SEEK_CUR) ; + test_readf_double_or_die (file, 0, data, 1, __LINE__) ; + if (error_function ((double) data [0], (double) orig [seekpos * channels], margin) || k != seekpos) + { printf ("\n\nLine %d: sf_seek (forwards, SEEK_CUR) followed by sf_readf_double failed (%f, %f) (%d, %d).\n", __LINE__, data [0], orig [seekpos * channels], k, seekpos + 1) ; + exit (1) ; + } ; + + seekpos = sf_seek (file, 0, SEEK_CUR) - 20 ; + /* Check seek backward from current position. */ + k = sf_seek (file, -20, SEEK_CUR) ; + test_readf_double_or_die (file, 0, data, 1, __LINE__) ; + if (error_function ((double) data [0], (double) orig [seekpos * channels], margin) || k != seekpos) + { printf ("sf_seek (backwards, SEEK_CUR) followed by sf_readf_double failed (%f, %f) (%d, %d).\n", data [0], orig [seekpos * channels], k, seekpos) ; + exit (1) ; + } ; + + /* Check that read past end of file returns number of items. */ + sf_seek (file, (double) sfinfo.frames, SEEK_SET) ; + + if ((k = sf_readf_double (file, data, datalen)) != 0) + { printf ("\n\nLine %d: Return value from sf_readf_double past end of file incorrect (%d).\n", __LINE__, k) ; + exit (1) ; + } ; + + /* Check seek backward from end. */ + if ((k = sf_seek (file, 5 - (double) sfinfo.frames, SEEK_END)) != 5) + { printf ("sf_seek (SEEK_END) returned %d instead of %d.\n", k, 5) ; + exit (1) ; + } ; + + test_readf_double_or_die (file, 0, data, channels, __LINE__) ; + if (error_function ((double) data [0], (double) orig [5], margin)) + { printf ("\n\nLine %d: sf_seek (SEEK_END) followed by sf_readf_short failed (%f should be %f).\n", __LINE__, data [0], orig [5]) ; + exit (1) ; + } ; + + sf_close (file) ; + + unlink (filename) ; + printf ("ok\n") ; +} /* lcomp_test_double */ + +/*======================================================================================== +** Smoothed differential loss compression tests. +*/ + +static void +sdlcomp_test_short (const char *filename, int filetype, int channels, double margin) +{ SNDFILE *file ; + SF_INFO sfinfo ; + int k, m, seekpos, half_max_abs ; + long datalen ; + short *orig, *data, *smooth ; + +channels = 1 ; + print_test_name ("sdlcomp_test_short", filename) ; + + datalen = BUFFER_SIZE ; + + orig = orig_buffer.s ; + data = data_buffer.s ; + smooth = smooth_buffer.s ; + + gen_signal_double (orig_buffer.d, 32000.0, channels, datalen) ; + for (k = 0 ; k < datalen ; k++) + orig [k] = (short) (orig_buffer.d [k]) ; + + sfinfo.samplerate = SAMPLE_RATE ; + sfinfo.frames = 123456789 ; /* Ridiculous value. */ + sfinfo.channels = channels ; + sfinfo.format = filetype ; + + file = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, SF_FALSE, __LINE__) ; + test_write_short_or_die (file, 0, orig, datalen, __LINE__) ; + sf_set_string (file, SF_STR_COMMENT, long_comment) ; + sf_close (file) ; + + memset (data, 0, datalen * sizeof (short)) ; + + if ((filetype & SF_FORMAT_TYPEMASK) != SF_FORMAT_RAW) + memset (&sfinfo, 0, sizeof (sfinfo)) ; + + file = test_open_file_or_die (filename, SFM_READ, &sfinfo, SF_FALSE, __LINE__) ; + + if (sfinfo.format != filetype) + { printf ("\n\nLine %d: Returned format incorrect (0x%08X => 0x%08X).\n", __LINE__, filetype, sfinfo.format) ; + exit (1) ; + } ; + + if (sfinfo.frames < datalen / channels) + { printf ("Too few.frames in file. (%ld should be a little more than %ld)\n", datalen, SF_COUNT_TO_LONG (sfinfo.frames)) ; + exit (1) ; + } ; + + if (sfinfo.frames > (datalen + 400)) + { printf ("Too many.frames in file. (%ld should be a little more than %ld)\n", SF_COUNT_TO_LONG (sfinfo.frames), datalen) ; + exit (1) ; + } ; + + if (sfinfo.channels != channels) + { printf ("Incorrect number of channels in file.\n") ; + exit (1) ; + } ; + + check_comment (file, filetype, __LINE__) ; + + sf_command (file, SFC_SET_NORM_FLOAT, NULL, SF_FALSE) ; + + check_log_buffer_or_die (file, __LINE__) ; + + test_readf_short_or_die (file, 0, data, datalen, __LINE__) ; + + memcpy (smooth, orig, datalen * sizeof (short)) ; + smoothed_diff_short (data, datalen) ; + smoothed_diff_short (smooth, datalen) ; + + half_max_abs = 0.0 ; + for (k = 0 ; k < datalen ; k++) + { if (error_function ((double) data [k], (double) smooth [k], margin)) + { printf ("\n\nLine %d: Incorrect sample (#%d : %d should be %d).\n", __LINE__, k, data [k], smooth [k]) ; + oct_save_short (orig, smooth, datalen) ; + exit (1) ; + } ; + half_max_abs = LCT_MAX (half_max_abs, abs (0.5 * data [k])) ; + } ; + + if (half_max_abs < 1) + { printf ("\n\nLine %d: Signal is all zeros.\n", __LINE__) ; + exit (1) ; + } ; + + if ((k = sf_read_short (file, data, datalen)) != sfinfo.frames - datalen) + { printf ("\n\nLine %d: Incorrect read length (%d should be %ld).\n", __LINE__, k, SF_COUNT_TO_LONG (sfinfo.frames - datalen)) ; + exit (1) ; + } ; + + if ((sfinfo.format & SF_FORMAT_SUBMASK) != SF_FORMAT_MS_ADPCM && + (sfinfo.format & SF_FORMAT_SUBMASK) != SF_FORMAT_GSM610) + for (k = 0 ; k < sfinfo.frames - datalen ; k++) + if (abs (data [k]) > decay_response (k)) + { printf ("\n\nLine %d: Incorrect sample (#%ld : abs (%d) should be < %d).\n", __LINE__, datalen + k, data [k], decay_response (k)) ; + exit (1) ; + } ; + + /* Now test sf_seek function. */ + if (sfinfo.seekable) + { if ((k = sf_seek (file, 0, SEEK_SET)) != 0) + { printf ("\n\nLine %d: Seek to start of file failed (%d).\n", __LINE__, k) ; + exit (1) ; + } ; + + for (m = 0 ; m < 3 ; m++) + { test_readf_short_or_die (file, m, data, datalen / 7, __LINE__) ; + + smoothed_diff_short (data, datalen / 7) ; + memcpy (smooth, orig + m * datalen / 7, datalen / 7 * sizeof (short)) ; + smoothed_diff_short (smooth, datalen / 7) ; + + for (k = 0 ; k < datalen / 7 ; k++) + if (error_function ((double) data [k], (double) smooth [k], margin)) + { printf ("Incorrect sample C (#%d (%ld) : %d => %d).\n", k, k + m * (datalen / 7), smooth [k], data [k]) ; + for (m = 0 ; m < 10 ; m++) + printf ("%d ", data [k]) ; + printf ("\n") ; + exit (1) ; + } ; + } ; /* for (m = 0 ; m < 3 ; m++) */ + + seekpos = BUFFER_SIZE / 10 ; + + /* Check seek from start of file. */ + if ((k = sf_seek (file, seekpos, SEEK_SET)) != seekpos) + { printf ("Seek to start of file + %d failed (%d).\n", seekpos, k) ; + exit (1) ; + } ; + test_readf_short_or_die (file, 0, data, 1, __LINE__) ; + + if (error_function ((double) data [0], (double) orig [seekpos * channels], margin)) + { printf ("sf_seek (SEEK_SET) followed by sf_read_short failed (%d, %d).\n", orig [1], data [0]) ; + exit (1) ; + } ; + + if ((k = sf_seek (file, 0, SEEK_CUR)) != seekpos + 1) + { printf ("sf_seek (SEEK_CUR) with 0 offset failed (%d should be %d)\n", k, seekpos + 1) ; + exit (1) ; + } ; + + seekpos = sf_seek (file, 0, SEEK_CUR) + BUFFER_SIZE / 5 ; + k = sf_seek (file, BUFFER_SIZE / 5, SEEK_CUR) ; + test_readf_short_or_die (file, 0, data, channels, __LINE__) ; + if (error_function ((double) data [0], (double) orig [seekpos * channels], margin) || k != seekpos) + { printf ("sf_seek (forwards, SEEK_CUR) followed by sf_read_short failed (%d, %d) (%d, %d).\n", data [0], orig [seekpos * channels], k, seekpos + 1) ; + exit (1) ; + } ; + + seekpos = sf_seek (file, 0, SEEK_CUR) - 20 ; + /* Check seek backward from current position. */ + k = sf_seek (file, -20, SEEK_CUR) ; + test_readf_short_or_die (file, 0, data, channels, __LINE__) ; + if (error_function ((double) data [0], (double) orig [seekpos * channels], margin) || k != seekpos) + { printf ("sf_seek (backwards, SEEK_CUR) followed by sf_read_short failed (%d, %d) (%d, %d).\n", data [0], orig [seekpos * channels], k, seekpos) ; + exit (1) ; + } ; + + /* Check that read past end of file returns number of items. */ + sf_seek (file, (int) sfinfo.frames, SEEK_SET) ; + + if ((k = sf_read_short (file, data, datalen)) != 0) + { printf ("\n\nLine %d: Return value from sf_read_short past end of file incorrect (%d).\n", __LINE__, k) ; + exit (1) ; + } ; + + /* Check seek backward from end. */ + + if ((k = sf_seek (file, 5 - (int) sfinfo.frames, SEEK_END)) != 5) + { printf ("sf_seek (SEEK_END) returned %d instead of %d.\n", k, 5) ; + exit (1) ; + } ; + + test_read_short_or_die (file, 0, data, channels, __LINE__) ; + if (error_function ((double) data [0], (double) orig [5], margin)) + { printf ("sf_seek (SEEK_END) followed by sf_read_short failed (%d should be %d).\n", data [0], orig [5]) ; + exit (1) ; + } ; + } /* if (sfinfo.seekable) */ + + sf_close (file) ; + + unlink (filename) ; + printf ("ok\n") ; +} /* sdlcomp_test_short */ + +static void +sdlcomp_test_int (const char *filename, int filetype, int channels, double margin) +{ SNDFILE *file ; + SF_INFO sfinfo ; + int k, m, seekpos, half_max_abs ; + long datalen ; + int *orig, *data, *smooth ; + double scale ; + +channels = 1 ; + + print_test_name ("sdlcomp_test_int", filename) ; + + datalen = BUFFER_SIZE ; + scale = 1.0 * 0x10000 ; + + orig = orig_buffer.i ; + data = data_buffer.i ; + smooth = smooth_buffer.i ; + + gen_signal_double (orig_buffer.d, 32000.0 * scale, channels, datalen) ; + for (k = 0 ; k < datalen ; k++) + orig [k] = (int) (orig_buffer.d [k]) ; + + sfinfo.samplerate = SAMPLE_RATE ; + sfinfo.frames = 123456789 ; /* Ridiculous value. */ + sfinfo.channels = channels ; + sfinfo.format = filetype ; + + file = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, SF_FALSE, __LINE__) ; + test_writef_int_or_die (file, 0, orig, datalen, __LINE__) ; + sf_set_string (file, SF_STR_COMMENT, long_comment) ; + sf_close (file) ; + + memset (data, 0, datalen * sizeof (int)) ; + + if ((filetype & SF_FORMAT_TYPEMASK) != SF_FORMAT_RAW) + memset (&sfinfo, 0, sizeof (sfinfo)) ; + + file = test_open_file_or_die (filename, SFM_READ, &sfinfo, SF_FALSE, __LINE__) ; + + if (sfinfo.format != filetype) + { printf ("Returned format incorrect (0x%08X => 0x%08X).\n", filetype, sfinfo.format) ; + exit (1) ; + } ; + + if (sfinfo.frames < datalen / channels) + { printf ("Too few.frames in file. (%ld should be a little more than %ld)\n", datalen, SF_COUNT_TO_LONG (sfinfo.frames)) ; + exit (1) ; + } ; + + if (sfinfo.frames > (datalen + 400)) + { printf ("Too many.frames in file. (%ld should be a little more than %ld)\n", SF_COUNT_TO_LONG (sfinfo.frames), datalen) ; + exit (1) ; + } ; + + if (sfinfo.channels != channels) + { printf ("Incorrect number of channels in file.\n") ; + exit (1) ; + } ; + + check_log_buffer_or_die (file, __LINE__) ; + + test_readf_int_or_die (file, 0, data, datalen, __LINE__) ; + + memcpy (smooth, orig, datalen * sizeof (int)) ; + smoothed_diff_int (data, datalen) ; + smoothed_diff_int (smooth, datalen) ; + + half_max_abs = abs (data [0] >> 16) ; + for (k = 1 ; k < datalen ; k++) + { if (error_function (data [k] / scale, smooth [k] / scale, margin)) + { printf ("\n\nLine %d: Incorrect sample (#%d : %d should be %d).\n", __LINE__, k, data [k], smooth [k]) ; + oct_save_int (orig, smooth, datalen) ; + exit (1) ; + } ; + half_max_abs = LCT_MAX (half_max_abs, abs (data [k] / 2)) ; + } ; + + if (half_max_abs < 1) + { printf ("\n\nLine %d: Signal is all zeros.\n", __LINE__) ; + exit (1) ; + } ; + + if ((k = sf_readf_int (file, data, datalen)) != sfinfo.frames - datalen) + { printf ("\n\nLine %d: Incorrect read length (%d should be %ld).\n", __LINE__, k, SF_COUNT_TO_LONG (sfinfo.frames - datalen)) ; + exit (1) ; + } ; + + if ((sfinfo.format & SF_FORMAT_SUBMASK) != SF_FORMAT_IMA_ADPCM && + (sfinfo.format & SF_FORMAT_SUBMASK) != SF_FORMAT_MS_ADPCM && + (sfinfo.format & SF_FORMAT_SUBMASK) != SF_FORMAT_GSM610 && + (sfinfo.format & SF_FORMAT_SUBMASK) != SF_FORMAT_G721_32 && + (sfinfo.format & SF_FORMAT_SUBMASK) != SF_FORMAT_G723_24) + for (k = 0 ; k < sfinfo.frames - datalen ; k++) + if (abs (data [k]) > decay_response (k)) + { printf ("\n\nLine %d: Incorrect sample (#%ld : abs (%d) should be < %d).\n", __LINE__, datalen + k, data [k], decay_response (k)) ; + exit (1) ; + } ; + + /* Now test sf_seek function. */ + if (sfinfo.seekable) + { if ((k = sf_seek (file, 0, SEEK_SET)) != 0) + { printf ("\n\nLine %d: Seek to start of file failed (%d).\n", __LINE__, k) ; + exit (1) ; + } ; + + for (m = 0 ; m < 3 ; m++) + { test_readf_int_or_die (file, m, data, datalen / 7, __LINE__) ; + + smoothed_diff_int (data, datalen / 7) ; + memcpy (smooth, orig + m * datalen / 7, datalen / 7 * sizeof (int)) ; + smoothed_diff_int (smooth, datalen / 7) ; + + for (k = 0 ; k < datalen / 7 ; k++) + if (error_function (data [k] / scale, smooth [k] / scale, margin)) + { printf ("\n\nLine %d: Incorrect sample (#%d (%ld) : %d => %d).\n", __LINE__, k, k + m * (datalen / 7), smooth [k], data [k]) ; + for (m = 0 ; m < 10 ; m++) + printf ("%d ", data [k]) ; + printf ("\n") ; + exit (1) ; + } ; + } ; /* for (m = 0 ; m < 3 ; m++) */ + + seekpos = BUFFER_SIZE / 10 ; + + /* Check seek from start of file. */ + if ((k = sf_seek (file, seekpos, SEEK_SET)) != seekpos) + { printf ("Seek to start of file + %d failed (%d).\n", seekpos, k) ; + exit (1) ; + } ; + test_readf_int_or_die (file, 0, data, 1, __LINE__) ; + + if (error_function ((double) data [0], (double) orig [seekpos * channels], margin)) + { printf ("sf_seek (SEEK_SET) followed by sf_readf_int failed (%d, %d).\n", orig [1], data [0]) ; + exit (1) ; + } ; + + if ((k = sf_seek (file, 0, SEEK_CUR)) != seekpos + 1) + { printf ("sf_seek (SEEK_CUR) with 0 offset failed (%d should be %d)\n", k, seekpos + 1) ; + exit (1) ; + } ; + + seekpos = sf_seek (file, 0, SEEK_CUR) + BUFFER_SIZE / 5 ; + k = sf_seek (file, BUFFER_SIZE / 5, SEEK_CUR) ; + test_readf_int_or_die (file, 0, data, 1, __LINE__) ; + if (error_function ((double) data [0], (double) orig [seekpos * channels], margin) || k != seekpos) + { printf ("sf_seek (forwards, SEEK_CUR) followed by sf_readf_int failed (%d, %d) (%d, %d).\n", data [0], orig [seekpos * channels], k, seekpos + 1) ; + exit (1) ; + } ; + + seekpos = sf_seek (file, 0, SEEK_CUR) - 20 ; + /* Check seek backward from current position. */ + k = sf_seek (file, -20, SEEK_CUR) ; + test_readf_int_or_die (file, 0, data, 1, __LINE__) ; + if (error_function ((double) data [0], (double) orig [seekpos * channels], margin) || k != seekpos) + { printf ("sf_seek (backwards, SEEK_CUR) followed by sf_readf_int failed (%d, %d) (%d, %d).\n", data [0], orig [seekpos * channels], k, seekpos) ; + exit (1) ; + } ; + + /* Check that read past end of file returns number of items. */ + sf_seek (file, (int) sfinfo.frames, SEEK_SET) ; + + if ((k = sf_readf_int (file, data, datalen)) != 0) + { printf ("\n\nLine %d: Return value from sf_readf_int past end of file incorrect (%d).\n", __LINE__, k) ; + exit (1) ; + } ; + + /* Check seek backward from end. */ + + if ((k = sf_seek (file, 5 - (int) sfinfo.frames, SEEK_END)) != 5) + { printf ("sf_seek (SEEK_END) returned %d instead of %d.\n", k, 5) ; + exit (1) ; + } ; + + test_readf_int_or_die (file, 0, data, 1, __LINE__) ; + if (error_function (data [0] / scale, orig [5] / scale, margin)) + { printf ("\n\nLine %d: sf_seek (SEEK_END) followed by sf_readf_int failed (%d should be %d).\n", __LINE__, data [0], orig [5]) ; + exit (1) ; + } ; + } /* if (sfinfo.seekable) */ + + sf_close (file) ; + + unlink (filename) ; + printf ("ok\n") ; +} /* sdlcomp_test_int */ + +static void +sdlcomp_test_float (const char *filename, int filetype, int channels, double margin) +{ SNDFILE *file ; + SF_INFO sfinfo ; + int k, m, seekpos ; + long datalen ; + float *orig, *data, *smooth ; + double half_max_abs ; + +channels = 1 ; + + print_test_name ("sdlcomp_test_float", filename) ; + +printf ("** fix this ** ") ; + + datalen = BUFFER_SIZE ; + + orig = orig_buffer.f ; + data = data_buffer.f ; + smooth = smooth_buffer.f ; + + gen_signal_double (orig_buffer.d, 32000.0, channels, datalen) ; + for (k = 0 ; k < datalen ; k++) + orig [k] = (int) (orig_buffer.d [k]) ; + + sfinfo.samplerate = SAMPLE_RATE ; + sfinfo.frames = 123456789 ; /* Ridiculous value. */ + sfinfo.channels = channels ; + sfinfo.format = filetype ; + + file = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, SF_FALSE, __LINE__) ; + sf_command (file, SFC_SET_NORM_FLOAT, NULL, SF_FALSE) ; + test_write_float_or_die (file, 0, orig, datalen, __LINE__) ; + sf_set_string (file, SF_STR_COMMENT, long_comment) ; + sf_close (file) ; + + memset (data, 0, datalen * sizeof (float)) ; + + if ((filetype & SF_FORMAT_TYPEMASK) != SF_FORMAT_RAW) + memset (&sfinfo, 0, sizeof (sfinfo)) ; + + file = test_open_file_or_die (filename, SFM_READ, &sfinfo, SF_FALSE, __LINE__) ; + + if ((sfinfo.format & (SF_FORMAT_TYPEMASK | SF_FORMAT_SUBMASK)) != (filetype & (SF_FORMAT_TYPEMASK | SF_FORMAT_SUBMASK))) + { printf ("\n\nLine %d: Returned format incorrect (0x%08X => 0x%08X).\n", __LINE__, filetype, sfinfo.format) ; + exit (1) ; + } ; + + if (sfinfo.frames < datalen / channels) + { printf ("Too few.frames in file. (%ld should be a little more than %ld)\n", datalen, SF_COUNT_TO_LONG (sfinfo.frames)) ; + exit (1) ; + } ; + + if (sfinfo.frames > (datalen + 400)) + { printf ("Too many.frames in file. (%ld should be a little more than %ld)\n", SF_COUNT_TO_LONG (sfinfo.frames), datalen) ; + exit (1) ; + } ; + + if (sfinfo.channels != channels) + { printf ("Incorrect number of channels in file.\n") ; + exit (1) ; + } ; + + check_comment (file, filetype, __LINE__) ; + + sf_command (file, SFC_SET_NORM_FLOAT, NULL, SF_FALSE) ; + + check_log_buffer_or_die (file, __LINE__) ; + + test_read_float_or_die (file, 0, data, datalen, __LINE__) ; + + memcpy (smooth, orig, datalen * sizeof (float)) ; + smoothed_diff_float (data, datalen) ; + smoothed_diff_float (smooth, datalen) ; + + half_max_abs = fabs (data [0]) ; + for (k = 1 ; k < datalen ; k++) + { if (error_function (data [k], smooth [k], margin)) + { printf ("\n\nLine %d: Incorrect sample (#%d : %d should be %d).\n", __LINE__, k, (int) data [k], (int) smooth [k]) ; + oct_save_float (orig, smooth, datalen) ; + exit (1) ; + } ; + half_max_abs = LCT_MAX (half_max_abs, abs (0.5 * data [k])) ; + } ; + + if (half_max_abs <= 0.0) + { printf ("\n\nLine %d: Signal is all zeros.\n", __LINE__) ; + printf ("half_max_abs : % 10.6f\n", half_max_abs) ; + exit (1) ; + } ; + + if ((k = sf_read_float (file, data, datalen)) != sfinfo.frames - datalen) + { printf ("\n\nLine %d: Incorrect read length (%d should be %ld).\n", __LINE__, k, SF_COUNT_TO_LONG (sfinfo.frames - datalen)) ; + exit (1) ; + } ; + + if ((sfinfo.format & SF_FORMAT_SUBMASK) != SF_FORMAT_MS_ADPCM && + (sfinfo.format & SF_FORMAT_SUBMASK) != SF_FORMAT_GSM610) + for (k = 0 ; k < sfinfo.frames - datalen ; k++) + if (abs (data [k]) > decay_response (k)) + { printf ("\n\nLine %d: Incorrect sample (#%ld : abs (%d) should be < %d).\n", __LINE__, datalen + k, (int) data [k], (int) decay_response (k)) ; + exit (1) ; + } ; + + /* Now test sf_seek function. */ + if (sfinfo.seekable) + { if ((k = sf_seek (file, 0, SEEK_SET)) != 0) + { printf ("\n\nLine %d: Seek to start of file failed (%d).\n", __LINE__, k) ; + exit (1) ; + } ; + + for (m = 0 ; m < 3 ; m++) + { test_read_float_or_die (file, 0, data, datalen / 7, __LINE__) ; + + smoothed_diff_float (data, datalen / 7) ; + memcpy (smooth, orig + m * datalen / 7, datalen / 7 * sizeof (float)) ; + smoothed_diff_float (smooth, datalen / 7) ; + + for (k = 0 ; k < datalen / 7 ; k++) + if (error_function ((float) data [k], (float) smooth [k], margin)) + { printf ("Incorrect sample C (#%d (%ld) : %d => %d).\n", k, k + m * (datalen / 7), (int) smooth [k], (int) data [k]) ; + for (m = 0 ; m < 10 ; m++) + printf ("%d ", (int) data [k]) ; + printf ("\n") ; + exit (1) ; + } ; + } ; /* for (m = 0 ; m < 3 ; m++) */ + + seekpos = BUFFER_SIZE / 10 ; + + /* Check seek from start of file. */ + if ((k = sf_seek (file, seekpos, SEEK_SET)) != seekpos) + { printf ("Seek to start of file + %d failed (%d).\n", seekpos, k) ; + exit (1) ; + } ; + test_read_float_or_die (file, 0, data, 1, __LINE__) ; + + if (error_function ((float) data [0], (float) orig [seekpos * channels], margin)) + { printf ("sf_seek (SEEK_SET) followed by sf_read_float failed (%d, %d).\n", (int) orig [1], (int) data [0]) ; + exit (1) ; + } ; + + if ((k = sf_seek (file, 0, SEEK_CUR)) != seekpos + 1) + { printf ("sf_seek (SEEK_CUR) with 0 offset failed (%d should be %d)\n", k, seekpos + 1) ; + exit (1) ; + } ; + + seekpos = sf_seek (file, 0, SEEK_CUR) + BUFFER_SIZE / 5 ; + k = sf_seek (file, BUFFER_SIZE / 5, SEEK_CUR) ; + test_read_float_or_die (file, 0, data, channels, __LINE__) ; + if (error_function ((float) data [0], (float) orig [seekpos * channels], margin) || k != seekpos) + { printf ("sf_seek (forwards, SEEK_CUR) followed by sf_read_float failed (%d, %d) (%d, %d).\n", (int) data [0], (int) orig [seekpos * channels], k, seekpos + 1) ; + exit (1) ; + } ; + + seekpos = sf_seek (file, 0, SEEK_CUR) - 20 ; + /* Check seek backward from current position. */ + k = sf_seek (file, -20, SEEK_CUR) ; + test_read_float_or_die (file, 0, data, channels, __LINE__) ; + if (error_function ((float) data [0], (float) orig [seekpos * channels], margin) || k != seekpos) + { printf ("sf_seek (backwards, SEEK_CUR) followed by sf_read_float failed (%d, %d) (%d, %d).\n", (int) data [0], (int) orig [seekpos * channels], k, seekpos) ; + exit (1) ; + } ; + + /* Check that read past end of file returns number of items. */ + sf_seek (file, (int) sfinfo.frames, SEEK_SET) ; + + if ((k = sf_read_float (file, data, datalen)) != 0) + { printf ("\n\nLine %d: Return value from sf_read_float past end of file incorrect (%d).\n", __LINE__, k) ; + exit (1) ; + } ; + + /* Check seek backward from end. */ + + if ((k = sf_seek (file, 5 - (int) sfinfo.frames, SEEK_END)) != 5) + { printf ("sf_seek (SEEK_END) returned %d instead of %d.\n", k, 5) ; + exit (1) ; + } ; + + test_read_float_or_die (file, 0, data, channels, __LINE__) ; + if (error_function ((float) data [0], (float) orig [5], margin)) + { printf ("sf_seek (SEEK_END) followed by sf_read_float failed (%d should be %d).\n", (int) data [0], (int) orig [5]) ; + exit (1) ; + } ; + } /* if (sfinfo.seekable) */ + + sf_close (file) ; + + unlink (filename) ; + printf ("ok\n") ; +} /* sdlcomp_test_float */ + +static void +sdlcomp_test_double (const char *filename, int filetype, int channels, double margin) +{ SNDFILE *file ; + SF_INFO sfinfo ; + int k, m, seekpos ; + long datalen ; + double *orig, *data, *smooth, half_max_abs ; + +channels = 1 ; + print_test_name ("sdlcomp_test_double", filename) ; + + datalen = BUFFER_SIZE ; + + orig = orig_buffer.d ; + data = data_buffer.d ; + smooth = smooth_buffer.d ; + + gen_signal_double (orig_buffer.d, 32000.0, channels, datalen) ; + + sfinfo.samplerate = SAMPLE_RATE ; + sfinfo.frames = 123456789 ; /* Ridiculous value. */ + sfinfo.channels = channels ; + sfinfo.format = filetype ; + + file = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, SF_FALSE, __LINE__) ; + sf_command (file, SFC_SET_NORM_DOUBLE, NULL, SF_FALSE) ; + test_write_double_or_die (file, 0, orig, datalen, __LINE__) ; + sf_set_string (file, SF_STR_COMMENT, long_comment) ; + sf_close (file) ; + + memset (data, 0, datalen * sizeof (double)) ; + + if ((filetype & SF_FORMAT_TYPEMASK) != SF_FORMAT_RAW) + memset (&sfinfo, 0, sizeof (sfinfo)) ; + + file = test_open_file_or_die (filename, SFM_READ, &sfinfo, SF_FALSE, __LINE__) ; + + if (sfinfo.format != filetype) + { printf ("Returned format incorrect (0x%08X => 0x%08X).\n", filetype, sfinfo.format) ; + exit (1) ; + } ; + + if (sfinfo.frames < datalen / channels) + { printf ("Too few.frames in file. (%ld should be a little more than %ld)\n", datalen, SF_COUNT_TO_LONG (sfinfo.frames)) ; + exit (1) ; + } ; + + if (sfinfo.frames > (datalen + 400)) + { printf ("Too many.frames in file. (%ld should be a little more than %ld)\n", SF_COUNT_TO_LONG (sfinfo.frames), datalen) ; + exit (1) ; + } ; + + if (sfinfo.channels != channels) + { printf ("Incorrect number of channels in file.\n") ; + exit (1) ; + } ; + + check_comment (file, filetype, __LINE__) ; + + check_comment (file, filetype, __LINE__) ; + + sf_command (file, SFC_SET_NORM_DOUBLE, NULL, SF_FALSE) ; + + check_log_buffer_or_die (file, __LINE__) ; + + test_read_double_or_die (file, 0, data, datalen, __LINE__) ; + + memcpy (smooth, orig, datalen * sizeof (double)) ; + smoothed_diff_double (data, datalen) ; + smoothed_diff_double (smooth, datalen) ; + + half_max_abs = 0.0 ; + for (k = 0 ; k < datalen ; k++) + { if (error_function (data [k], smooth [k], margin)) + { printf ("\n\nLine %d: Incorrect sample (#%d : %d should be %d).\n", __LINE__, k, (int) data [k], (int) smooth [k]) ; + oct_save_double (orig, smooth, datalen) ; + exit (1) ; + } ; + half_max_abs = LCT_MAX (half_max_abs, 0.5 * fabs (data [k])) ; + } ; + + if (half_max_abs < 1.0) + { printf ("\n\nLine %d: Signal is all zeros.\n", __LINE__) ; + exit (1) ; + } ; + + if ((k = sf_read_double (file, data, datalen)) != sfinfo.frames - datalen) + { printf ("\n\nLine %d: Incorrect read length (%d should be %ld).\n", __LINE__, k, SF_COUNT_TO_LONG (sfinfo.frames - datalen)) ; + exit (1) ; + } ; + + if ((sfinfo.format & SF_FORMAT_SUBMASK) != SF_FORMAT_MS_ADPCM && + (sfinfo.format & SF_FORMAT_SUBMASK) != SF_FORMAT_GSM610) + for (k = 0 ; k < sfinfo.frames - datalen ; k++) + if (abs (data [k]) > decay_response (k)) + { printf ("\n\nLine %d: Incorrect sample (#%ld : abs (%d) should be < %d).\n", __LINE__, datalen + k, (int) data [k], (int) decay_response (k)) ; + exit (1) ; + } ; + + /* Now test sf_seek function. */ + if (sfinfo.seekable) + { if ((k = sf_seek (file, 0, SEEK_SET)) != 0) + { printf ("\n\nLine %d: Seek to start of file failed (%d).\n", __LINE__, k) ; + exit (1) ; + } ; + + for (m = 0 ; m < 3 ; m++) + { test_read_double_or_die (file, m, data, datalen / 7, __LINE__) ; + + smoothed_diff_double (data, datalen / 7) ; + memcpy (smooth, orig + m * datalen / 7, datalen / 7 * sizeof (double)) ; + smoothed_diff_double (smooth, datalen / 7) ; + + for (k = 0 ; k < datalen / 7 ; k++) + if (error_function ((double) data [k], (double) smooth [k], margin)) + { printf ("Incorrect sample C (#%d (%ld) : %d => %d).\n", k, k + m * (datalen / 7), (int) smooth [k], (int) data [k]) ; + for (m = 0 ; m < 10 ; m++) + printf ("%d ", (int) data [k]) ; + printf ("\n") ; + exit (1) ; + } ; + } ; /* for (m = 0 ; m < 3 ; m++) */ + + seekpos = BUFFER_SIZE / 10 ; + + /* Check seek from start of file. */ + if ((k = sf_seek (file, seekpos, SEEK_SET)) != seekpos) + { printf ("Seek to start of file + %d failed (%d).\n", seekpos, k) ; + exit (1) ; + } ; + test_read_double_or_die (file, 0, data, 1, __LINE__) ; + + if (error_function ((double) data [0], (double) orig [seekpos * channels], margin)) + { printf ("sf_seek (SEEK_SET) followed by sf_read_double failed (%d, %d).\n", (int) orig [1], (int) data [0]) ; + exit (1) ; + } ; + + if ((k = sf_seek (file, 0, SEEK_CUR)) != seekpos + 1) + { printf ("sf_seek (SEEK_CUR) with 0 offset failed (%d should be %d)\n", k, seekpos + 1) ; + exit (1) ; + } ; + + seekpos = sf_seek (file, 0, SEEK_CUR) + BUFFER_SIZE / 5 ; + k = sf_seek (file, BUFFER_SIZE / 5, SEEK_CUR) ; + test_read_double_or_die (file, 0, data, 1, __LINE__) ; + if (error_function ((double) data [0], (double) orig [seekpos * channels], margin) || k != seekpos) + { printf ("sf_seek (forwards, SEEK_CUR) followed by sf_read_double failed (%d, %d) (%d, %d).\n", (int) data [0], (int) orig [seekpos * channels], k, seekpos + 1) ; + exit (1) ; + } ; + + seekpos = sf_seek (file, 0, SEEK_CUR) - 20 ; + /* Check seek backward from current position. */ + k = sf_seek (file, -20, SEEK_CUR) ; + test_read_double_or_die (file, 0, data, 1, __LINE__) ; + if (error_function ((double) data [0], (double) orig [seekpos * channels], margin) || k != seekpos) + { printf ("sf_seek (backwards, SEEK_CUR) followed by sf_read_double failed (%d, %d) (%d, %d).\n", (int) data [0], (int) orig [seekpos * channels], k, seekpos) ; + exit (1) ; + } ; + + /* Check that read past end of file returns number of items. */ + sf_seek (file, (int) sfinfo.frames, SEEK_SET) ; + + if ((k = sf_read_double (file, data, datalen)) != 0) + { printf ("\n\nLine %d: Return value from sf_read_double past end of file incorrect (%d).\n", __LINE__, k) ; + exit (1) ; + } ; + + /* Check seek backward from end. */ + + if ((k = sf_seek (file, 5 - (int) sfinfo.frames, SEEK_END)) != 5) + { printf ("sf_seek (SEEK_END) returned %d instead of %d.\n", k, 5) ; + exit (1) ; + } ; + + test_read_double_or_die (file, 0, data, 1, __LINE__) ; + if (error_function ((double) data [0], (double) orig [5], margin)) + { printf ("sf_seek (SEEK_END) followed by sf_read_double failed (%d should be %d).\n", (int) data [0], (int) orig [5]) ; + exit (1) ; + } ; + } /* if (sfinfo.seekable) */ + + sf_close (file) ; + + unlink (filename) ; + printf ("ok\n") ; +} /* sdlcomp_test_double */ + +/*======================================================================================== +** Auxiliary functions +*/ + +#define SIGNAL_MAXVAL 30000.0 +#define DECAY_COUNT 1000 + +static int +decay_response (int k) +{ if (k < 1) + return (int) (1.2 * SIGNAL_MAXVAL) ; + if (k > DECAY_COUNT) + return 0 ; + return (int) (1.2 * SIGNAL_MAXVAL * (DECAY_COUNT - k) / (1.0 * DECAY_COUNT)) ; +} /* decay_response */ + +static void +gen_signal_double (double *data, double scale, int channels, int datalen) +{ int k, ramplen ; + double amp = 0.0 ; + + ramplen = DECAY_COUNT ; + + if (channels == 1) + { for (k = 0 ; k < datalen ; k++) + { if (k <= ramplen) + amp = scale * k / ((double) ramplen) ; + else if (k > datalen - ramplen) + amp = scale * (datalen - k) / ((double) ramplen) ; + +/*-printf ("%3d : %g\n", k, amp) ;-*/ + + data [k] = amp * (0.4 * sin (33.3 * 2.0 * M_PI * ((double) (k+1)) / ((double) SAMPLE_RATE)) + + 0.3 * cos (201.1 * 2.0 * M_PI * ((double) (k+1)) / ((double) SAMPLE_RATE))) ; + } ; + } + else + { for (k = 0 ; k < datalen ; k ++) + { if (k <= ramplen) + amp = scale * k / ((double) ramplen) ; + else if (k > datalen - ramplen) + amp = scale * (datalen - k) / ((double) ramplen) ; + + data [2 * k] = amp * (0.4 * sin (33.3 * 2.0 * M_PI * ((double) (k+1)) / ((double) SAMPLE_RATE)) + + 0.3 * cos (201.1 * 2.0 * M_PI * ((double) (k+1)) / ((double) SAMPLE_RATE))) ; + data [2 * k + 1] = amp * (0.4 * sin (55.5 * 2.0 * M_PI * ((double) (k+1)) / ((double) SAMPLE_RATE)) + + 0.3 * cos (201.1 * 2.0 * M_PI * ((double) (k+1)) / ((double) SAMPLE_RATE))) ; + } ; + } ; + + return ; +} /* gen_signal_double */ + +static int +error_function (double data, double orig, double margin) +{ double error ; + + if (fabs (orig) <= 500.0) + error = fabs (fabs (data) - fabs (orig)) / 2000.0 ; + else if (fabs (orig) <= 1000.0) + error = fabs (data - orig) / 3000.0 ; + else + error = fabs (data - orig) / fabs (orig) ; + + if (error > margin) + { printf ("\n\n*******************\nError : %f\n", error) ; + return 1 ; + } ; + return 0 ; +} /* error_function */ + +static void +smoothed_diff_short (short *data, unsigned int datalen) +{ unsigned int k ; + double memory = 0.0 ; + + /* Calculate the smoothed sample-to-sample difference. */ + for (k = 0 ; k < datalen - 1 ; k++) + { memory = 0.7 * memory + (1 - 0.7) * (double) (data [k+1] - data [k]) ; + data [k] = (short) memory ; + } ; + data [datalen-1] = data [datalen-2] ; + +} /* smoothed_diff_short */ + +static void +smoothed_diff_int (int *data, unsigned int datalen) +{ unsigned int k ; + double memory = 0.0 ; + + /* Calculate the smoothed sample-to-sample difference. */ + for (k = 0 ; k < datalen - 1 ; k++) + { memory = 0.7 * memory + (1 - 0.7) * (double) (data [k+1] - data [k]) ; + data [k] = (int) memory ; + } ; + data [datalen-1] = data [datalen-2] ; + +} /* smoothed_diff_int */ + +static void +smoothed_diff_float (float *data, unsigned int datalen) +{ unsigned int k ; + float memory = 0.0 ; + + /* Calculate the smoothed sample-to-sample difference. */ + for (k = 0 ; k < datalen - 1 ; k++) + { memory = 0.7 * memory + (1 - 0.7) * (data [k+1] - data [k]) ; + data [k] = memory ; + } ; + data [datalen-1] = data [datalen-2] ; + +} /* smoothed_diff_float */ + +static void +smoothed_diff_double (double *data, unsigned int datalen) +{ unsigned int k ; + double memory = 0.0 ; + + /* Calculate the smoothed sample-to-sample difference. */ + for (k = 0 ; k < datalen - 1 ; k++) + { memory = 0.7 * memory + (1 - 0.7) * (data [k+1] - data [k]) ; + data [k] = memory ; + } ; + data [datalen-1] = data [datalen-2] ; + +} /* smoothed_diff_double */ + +static void +check_comment (SNDFILE * file, int format, int lineno) +{ const char *comment ; + + switch (format & SF_FORMAT_TYPEMASK) + { case SF_FORMAT_AIFF : + case SF_FORMAT_WAV : + case SF_FORMAT_WAVEX : + break ; + default : + return ; + } ; + + comment = sf_get_string (file, SF_STR_COMMENT) ; + if (comment == NULL) + { printf ("\n\nLine %d : File does not contain a comment string.\n\n", lineno) ; + exit (1) ; + } ; + + if (strcmp (comment, long_comment) != 0) + { printf ("\n\nLine %d : File comment does not match comment written.\n\n", lineno) ; + exit (1) ; + } ; + + return ; +} /* check_comment */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 5eb86888-3311-48b8-920e-c2a0393b7ad2 +*/ diff --git a/tests/misc_test.c b/tests/misc_test.c new file mode 100644 index 00000000..71aa186c --- /dev/null +++ b/tests/misc_test.c @@ -0,0 +1,365 @@ +/* +** Copyright (C) 2001-2005 Erik de Castro Lopo +** +** This program is free software ; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation ; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY ; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program ; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include +#include + +#include +#include + +#if HAVE_UNISTD_H +#include +#endif + +#if (HAVE_DECL_S_IRGRP == 0) +#include +#endif + +#if (defined (WIN32) || defined (_WIN32)) +#include +#include +#endif + +#include + +#include "utils.h" + +#define BUFFER_LEN (1<<10) +#define LOG_BUFFER_SIZE 1024 + +static void zero_data_test (const char *filename, int typemajor) ; +static void filesystem_full_test (int typemajor) ; +static void permission_test (const char *filename, int typemajor) ; + +int +main (int argc, char *argv []) +{ int do_all = 0 ; + int test_count = 0 ; + + if (argc != 2) + { printf ("Usage : %s \n", argv [0]) ; + printf (" Where is one of the following:\n") ; + printf (" wav - test WAV file peak chunk\n") ; + printf (" aiff - test AIFF file PEAK chunk\n") ; + printf (" all - perform all tests\n") ; + exit (1) ; + } ; + + do_all=!strcmp (argv [1], "all") ; + + if (do_all || ! strcmp (argv [1], "wav")) + { zero_data_test ("zerolen.wav", SF_FORMAT_WAV) ; + filesystem_full_test (SF_FORMAT_WAV) ; + permission_test ("readonly.wav", SF_FORMAT_WAV) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "aiff")) + { zero_data_test ("zerolen.aiff", SF_FORMAT_AIFF) ; + filesystem_full_test (SF_FORMAT_AIFF) ; + permission_test ("readonly.aiff", SF_FORMAT_AIFF) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "au")) + { zero_data_test ("zerolen.au", SF_FORMAT_AU) ; + filesystem_full_test (SF_FORMAT_AU) ; + permission_test ("readonly.au", SF_FORMAT_AU) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "caf")) + { zero_data_test ("zerolen.caf", SF_FORMAT_CAF) ; + filesystem_full_test (SF_FORMAT_CAF) ; + permission_test ("readonly.caf", SF_FORMAT_CAF) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "svx")) + { zero_data_test ("zerolen.svx", SF_FORMAT_SVX) ; + filesystem_full_test (SF_FORMAT_SVX) ; + permission_test ("readonly.svx", SF_FORMAT_SVX) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "nist")) + { zero_data_test ("zerolen.nist", SF_FORMAT_NIST) ; + filesystem_full_test (SF_FORMAT_NIST) ; + permission_test ("readonly.nist", SF_FORMAT_NIST) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "paf")) + { zero_data_test ("zerolen.paf", SF_FORMAT_PAF) ; + filesystem_full_test (SF_FORMAT_PAF) ; + permission_test ("readonly.paf", SF_FORMAT_PAF) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "ircam")) + { zero_data_test ("zerolen.ircam", SF_FORMAT_IRCAM) ; + filesystem_full_test (SF_FORMAT_IRCAM) ; + permission_test ("readonly.ircam", SF_FORMAT_IRCAM) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "voc")) + { zero_data_test ("zerolen.voc", SF_FORMAT_VOC) ; + filesystem_full_test (SF_FORMAT_VOC) ; + permission_test ("readonly.voc", SF_FORMAT_VOC) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "w64")) + { zero_data_test ("zerolen.w64", SF_FORMAT_W64) ; + filesystem_full_test (SF_FORMAT_W64) ; + permission_test ("readonly.w64", SF_FORMAT_W64) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "mat4")) + { zero_data_test ("zerolen.mat4", SF_FORMAT_MAT4) ; + filesystem_full_test (SF_FORMAT_MAT4) ; + permission_test ("readonly.mat4", SF_FORMAT_MAT4) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "mat5")) + { zero_data_test ("zerolen.mat5", SF_FORMAT_MAT5) ; + filesystem_full_test (SF_FORMAT_MAT5) ; + permission_test ("readonly.mat5", SF_FORMAT_MAT5) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "pvf")) + { zero_data_test ("zerolen.pvf", SF_FORMAT_PVF) ; + filesystem_full_test (SF_FORMAT_PVF) ; + permission_test ("readonly.pvf", SF_FORMAT_PVF) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "htk")) + { zero_data_test ("zerolen.htk", SF_FORMAT_HTK) ; + filesystem_full_test (SF_FORMAT_HTK) ; + permission_test ("readonly.htk", SF_FORMAT_HTK) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "avr")) + { zero_data_test ("zerolen.avr", SF_FORMAT_AVR) ; + filesystem_full_test (SF_FORMAT_AVR) ; + permission_test ("readonly.avr", SF_FORMAT_AVR) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "sds")) + { zero_data_test ("zerolen.sds", SF_FORMAT_SDS) ; + filesystem_full_test (SF_FORMAT_SDS) ; + permission_test ("readonly.sds", SF_FORMAT_SDS) ; + test_count++ ; + } ; + + if (test_count == 0) + { printf ("Mono : ************************************\n") ; + printf ("Mono : * No '%s' test defined.\n", argv [1]) ; + printf ("Mono : ************************************\n") ; + return 1 ; + } ; + + return 0 ; +} /* main */ + + +/*============================================================================================ +** Here are the test functions. +*/ + +static void +zero_data_test (const char *filename, int typemajor) +{ SNDFILE *file ; + SF_INFO sfinfo ; + int frames ; + + print_test_name ("zero_data_test", filename) ; + + sfinfo.samplerate = 44100 ; + sfinfo.format = (typemajor | SF_FORMAT_PCM_16) ; + sfinfo.channels = 1 ; + sfinfo.frames = 0 ; + + frames = BUFFER_LEN / sfinfo.channels ; + + file = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, SF_TRUE, __LINE__) ; + + sf_close (file) ; + + memset (&sfinfo, 0, sizeof (sfinfo)) ; + + file = test_open_file_or_die (filename, SFM_READ, &sfinfo, SF_TRUE, __LINE__) ; + + sf_close (file) ; + + unlink (filename) ; + puts ("ok") ; +} /* zero_data_test */ + +static void +filesystem_full_test (int typemajor) +{ SNDFILE *file ; + SF_INFO sfinfo ; + struct stat buf ; + + const char *filename = "/dev/full", *errorstr ; + int frames ; + +#if (defined (WIN32) || defined (_WIN32)) + /* Can't run this test on Win32 so return. */ + return ; +#endif + + /* Make sure errno is zero before doing anything else. */ + errno = 0 ; + + print_test_name ("filesystem_full_test", filename) ; + + if (stat (filename, &buf) != 0) + { puts ("/dev/full missing") ; + return ; + } ; + + if (S_ISCHR (buf.st_mode) == 0 && S_ISBLK (buf.st_mode) == 0) + { puts ("/dev/full is not a device file") ; + return ; + } ; + + sfinfo.samplerate = 44100 ; + sfinfo.format = (typemajor | SF_FORMAT_PCM_16) ; + sfinfo.channels = 1 ; + sfinfo.frames = 0 ; + + frames = BUFFER_LEN / sfinfo.channels ; + + if ((file = sf_open (filename, SFM_WRITE, &sfinfo)) != NULL) + { printf ("\n\nLine %d : Error, file should not have openned.\n", __LINE__ - 1) ; + exit (1) ; + } ; + + errorstr = sf_strerror (file) ; + + if (strstr (errorstr, " space ") == NULL || strstr (errorstr, "device") == NULL) + { printf ("\n\nLine %d : Error bad error string : %s.\n", __LINE__ - 1, errorstr) ; + exit (1) ; + } ; + + puts ("ok") ; +} /* filesystem_full_test */ + +static void +permission_test (const char *filename, int typemajor) +{ +#if (OS_IS_WIN32) + /* Avoid compiler warnings. */ + filename = filename ; + typemajor = typemajor ; + + /* Can't run this test on Win32 so return. */ + return ; +#else + + FILE *textfile ; + SNDFILE *file ; + SF_INFO sfinfo ; + const char *errorstr ; + int frames ; + + /* Make sure errno is zero before doing anything else. */ + errno = 0 ; + + if (getuid () == 0) + { /* If running as root bypass this test. + ** Root is allowed to open a readonly file for write. + */ + return ; + } ; + + print_test_name ("permission_test", filename) ; + + if (access (filename, F_OK) == 0) + { chmod (filename, S_IWUSR) ; + unlink (filename) ; + } ; + + if ((textfile = fopen (filename, "w")) == NULL) + { printf ("\n\nLine %d : not able to open text file for write.\n", __LINE__) ; + exit (1) ; + } ; + + fprintf (textfile, "This is a read only file.\n") ; + fclose (textfile) ; + + if (chmod (filename, S_IRUSR | S_IRGRP)) + { printf ("\n\nLine %d : chmod failed", __LINE__) ; + fflush (stdout) ; + perror ("") ; + exit (1) ; + } ; + + sfinfo.samplerate = 44100 ; + sfinfo.format = (typemajor | SF_FORMAT_PCM_16) ; + sfinfo.channels = 1 ; + sfinfo.frames = 0 ; + + frames = BUFFER_LEN / sfinfo.channels ; + + if ((file = sf_open (filename, SFM_WRITE, &sfinfo)) != NULL) + { printf ("\n\nLine %d : Error, file should not have opened.\n", __LINE__ - 1) ; + exit (1) ; + } ; + + errorstr = sf_strerror (file) ; + + if (strstr (errorstr, "ermission denied") == NULL) + { printf ("\n\nLine %d : Error bad error string : %s.\n", __LINE__ - 1, errorstr) ; + exit (1) ; + } ; + + if (chmod (filename, S_IWUSR | S_IWGRP)) + { printf ("\n\nLine %d : chmod failed", __LINE__) ; + fflush (stdout) ; + perror ("") ; + exit (1) ; + } ; + + unlink (filename) ; + + puts ("ok") ; + +#endif +} /* permission_test */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: efc6c227-8881-4a1d-8680-0d1255975267 +*/ diff --git a/tests/multi_file_test.c b/tests/multi_file_test.c new file mode 100644 index 00000000..66623379 --- /dev/null +++ b/tests/multi_file_test.c @@ -0,0 +1,258 @@ +/* +** Copyright (C) 1999-2004 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include + +#if HAVE_UNISTD_H +#include +#endif + +#if (HAVE_DECL_S_IRGRP == 0) +#include +#endif + +#include +#include +#include +#include +#include + +#include + +#include "utils.h" + +#define DATA_LENGTH (512) + +static off_t get_file_length (int fd) ; +static void write_file_at_end (int fd, int filetype, int channels, int file_num) ; + +static void multi_file_test (const char *filename, int *formats, int format_count) ; + +static short data [DATA_LENGTH] ; + +static int wav_formats [] = +{ SF_FORMAT_WAV | SF_FORMAT_PCM_16, + SF_FORMAT_WAV | SF_FORMAT_PCM_24, + SF_FORMAT_WAV | SF_FORMAT_ULAW, + SF_FORMAT_WAV | SF_FORMAT_ALAW, + /* Lite remove start */ + SF_FORMAT_WAV | SF_FORMAT_IMA_ADPCM, + SF_FORMAT_WAV | SF_FORMAT_MS_ADPCM, + /* Lite remove end */ + /*-SF_FORMAT_WAV | SF_FORMAT_GSM610 Doesn't work yet. -*/ +} ; + +static int aiff_formats [] = +{ SF_FORMAT_AIFF | SF_FORMAT_PCM_16, + SF_FORMAT_AIFF | SF_FORMAT_PCM_24, + SF_FORMAT_AIFF | SF_FORMAT_ULAW, + SF_FORMAT_AIFF | SF_FORMAT_ALAW +} ; + +static int au_formats [] = +{ SF_FORMAT_AU | SF_FORMAT_PCM_16, + SF_FORMAT_AU | SF_FORMAT_PCM_24, + SF_FORMAT_AU | SF_FORMAT_ULAW, + SF_FORMAT_AU | SF_FORMAT_ALAW +} ; + +static int verbose = SF_FALSE ; + +int +main (int argc, char **argv) +{ int do_all = 0 ; + int test_count = 0 ; + + if (argc == 3 && strcmp (argv [2], "-v") == 0) + { verbose = SF_TRUE ; + argc -- ; + } ; + + if (argc != 2) + { printf ("Usage : %s \n", argv [0]) ; + printf (" Where is one of the following:\n") ; + printf (" wav - test WAV file functions (little endian)\n") ; + printf (" aiff - test AIFF file functions (big endian)\n") ; + printf (" au - test AU file functions\n") ; +#if 0 + printf (" svx - test 8SVX/16SV file functions\n") ; + printf (" nist - test NIST Sphere file functions\n") ; + printf (" ircam - test IRCAM file functions\n") ; + printf (" voc - Create Voice file functions\n") ; + printf (" w64 - Sonic Foundry's W64 file functions\n") ; +#endif + printf (" all - perform all tests\n") ; + exit (1) ; + } ; + + do_all = !strcmp (argv [1], "all") ; + + if (do_all || ! strcmp (argv [1], "wav")) + { multi_file_test ("multi_wav.dat", wav_formats, ARRAY_LEN (wav_formats)) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "aiff")) + { multi_file_test ("multi_aiff.dat", aiff_formats, ARRAY_LEN (aiff_formats)) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "au")) + { multi_file_test ("multi_au.dat", au_formats, ARRAY_LEN (au_formats)) ; + test_count++ ; + } ; + + return 0 ; +} /* main */ + +/*====================================================================================== +*/ + +static void +multi_file_test (const char *filename, int *formats, int format_count) +{ SNDFILE *sndfile ; + SF_INFO sfinfo ; + SF_EMBED_FILE_INFO embed_info ; + off_t file_length ; + int fd, k, file_count = 0 ; + + print_test_name ("multi_file_test", filename) ; + + unlink (filename) ; + + if ((fd = open (filename, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP )) < 0) + { printf ("\n\nLine %d: open failed : %s\n", __LINE__, strerror (errno)) ; + exit (1) ; + } ; + + write (fd, "1234", 4) ; + + for (k = 0 ; k < format_count ; k++) + write_file_at_end (fd, formats [k], 2, k) ; + + file_length = get_file_length (fd) ; + + embed_info.offset = 4 ; + embed_info.length = 0 ; + + file_count = 0 ; + + while (embed_info.offset + embed_info.length < file_length) + { + file_count ++ ; + + if (verbose) + { puts ("\n------------------------------------") ; + printf ("This offset : %ld\n", SF_COUNT_TO_LONG (embed_info.offset + embed_info.length)) ; + } ; + + if (lseek (fd, embed_info.offset + embed_info.length, SEEK_SET) < 0) + { printf ("\n\nLine %d: lseek failed : %s\n", __LINE__, strerror (errno)) ; + exit (1) ; + } ; + + memset (&sfinfo, 0, sizeof (sfinfo)) ; + if ((sndfile = sf_open_fd (fd, SFM_READ, &sfinfo, SF_FALSE)) == NULL) + { printf ("\n\nLine %d: sf_open_fd failed\n", __LINE__) ; + printf ("Embedded file number : %d offset : %ld\n", file_count, SF_COUNT_TO_LONG (embed_info.offset)) ; + puts (sf_strerror (sndfile)) ; + dump_log_buffer (sndfile) ; + exit (1) ; + } ; + + sf_command (sndfile, SFC_GET_EMBED_FILE_INFO, &embed_info, sizeof (embed_info)) ; + + sf_close (sndfile) ; + + if (verbose) + printf ("\nNext offset : %ld\nNext length : %ld\n", SF_COUNT_TO_LONG (embed_info.offset), SF_COUNT_TO_LONG (embed_info.length)) ; + } ; + + if (file_count != format_count) + { printf ("\n\nLine %d: file count (%d) not equal to %d.\n\n", __LINE__, file_count, format_count) ; + printf ("Embedded file number : %d\n", file_count) ; + exit (1) ; + } ; + + close (fd) ; + unlink (filename) ; + printf ("ok\n") ; + + return ; +} /* multi_file_test */ + +/*====================================================================================== +*/ + +static void +write_file_at_end (int fd, int filetype, int channels, int file_num) +{ SNDFILE *sndfile ; + SF_INFO sfinfo ; + + int frames, k ; + + lseek (fd, 0, SEEK_END) ; + + for (k = 0 ; k < DATA_LENGTH ; k++) + data [k] = k ; + + frames = DATA_LENGTH / channels ; + + sfinfo.format = filetype ; + sfinfo.channels = channels ; + sfinfo.samplerate = 44100 ; + + if ((sndfile = sf_open_fd (fd, SFM_WRITE, &sfinfo, SF_FALSE)) == NULL) + { printf ("\n\nLine %d: sf_open_fd failed\n", __LINE__) ; + printf ("Embedded file number : %d\n", file_num) ; + puts (sf_strerror (sndfile)) ; + dump_log_buffer (sndfile) ; + exit (1) ; + } ; + + if (sf_writef_short (sndfile, data, frames) != frames) + { printf ("\n\nLine %d: short write\n", __LINE__) ; + printf ("Embedded file number : %d\n", file_num) ; + exit (1) ; + } ; + + sf_close (sndfile) ; +} /* write_file_at_end */ + +static off_t +get_file_length (int fd) +{ struct stat statbuf ; + + if (fstat (fd, &statbuf) == -1) + { printf ("\n\nError : fstat error : %s\n\n", strerror (errno)) ; + exit (1) ; + } ; + + return statbuf.st_size ; +} /* get_file_length */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: c1a09b0e-57c9-4306-b69e-a865d44eecb3 +*/ diff --git a/tests/open_fail_test.c b/tests/open_fail_test.c new file mode 100644 index 00000000..620f3bad --- /dev/null +++ b/tests/open_fail_test.c @@ -0,0 +1,81 @@ +/* +** Copyright (C) 2003,2004 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include + +#if HAVE_UNISTD_H +#include +#endif + +#include + +#include "utils.h" + +int +main (void) +{ SNDFILE *sndfile ; + SF_INFO sfinfo ; + + FILE *bad_file ; + const char *bad_wav = "bad_wav.wav" ; + const char bad_data [] = "RIFF WAVEfmt " ; + + print_test_name ("open_fail_test", bad_wav) ; + + memset (&sfinfo, 0, sizeof (sfinfo)) ; + + sndfile = sf_open ("let's hope this file doesn't exist", SFM_READ, &sfinfo) ; + + if (sndfile) + { printf ("Line %d: should not have received a valid SNDFILE* pointer.\n", __LINE__) ; + exit (1) ; + } ; + + if ((bad_file = fopen (bad_wav, "w")) == NULL) + { printf ("Line %d: fopen returned NULL.\n", __LINE__) ; + exit (1) ; + } ; + + fwrite (bad_data, sizeof (bad_data), 1, bad_file) ; + fclose (bad_file) ; + + sndfile = sf_open (bad_wav, SFM_READ, &sfinfo) ; + + if (sndfile) + { printf ("Line %d: should not have received a valid SNDFILE* pointer.\n", __LINE__) ; + exit (1) ; + } ; + + unlink (bad_wav) ; + puts ("ok") ; + + return 0 ; +} /* main */ + + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 24440323-00b1-4e4b-87c5-0e3b7e9605e9 +*/ diff --git a/tests/pcm_test.def b/tests/pcm_test.def new file mode 100644 index 00000000..1deb99d7 --- /dev/null +++ b/tests/pcm_test.def @@ -0,0 +1,42 @@ +autogen definitions pcm_test.tpl; + +data_type = { + name = "bits_8" ; + item_count = 127 ; + short_func = "((k * ((k % 2) ? 1 : -1)) << 8)" ; + int_func = "((k * ((k % 2) ? 1 : -1)) << 24)" ; + float_func = "(k * ((k % 2) ? 1 : -1))" ; + } ; + +data_type = { + name = "bits_16" ; + item_count = 1024 ; + short_func = "(k * ((k % 2) ? 3 : -3))" ; + int_func = "((k * ((k % 2) ? 3 : -3)) << 16)" ; + float_func = "(k * ((k % 2) ? 3 : -3))" ; + } ; + +data_type = { + name = "bits_24" ; + item_count = 1024 ; + short_func = "(k * ((k % 2) ? 3 : -3))" ; + int_func = "((k * ((k % 2) ? 3333 : -3333)) << 8)" ; + float_func = "(k * ((k % 2) ? 3333 : -3333))" ; + } ; + +data_type = { + name = "bits_32" ; + item_count = 1024 ; + short_func = "(k * ((k % 2) ? 3 : -3))" ; + int_func = "(k * ((k % 2) ? 333333 : -333333))" ; + float_func = "(k * ((k % 2) ? 333333 : -333333))" ; + } ; + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: a939b77e-4175-460b-a393-fb38012ded7c +*/ + diff --git a/tests/pcm_test.tpl b/tests/pcm_test.tpl new file mode 100644 index 00000000..dfe7a51d --- /dev/null +++ b/tests/pcm_test.tpl @@ -0,0 +1,940 @@ +[+ AutoGen5 template c +] +/* +** Copyright (C) 1999-2004 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include + +#if HAVE_UNISTD_H +#include +#endif + +#include + +#include "float_cast.h" +#include "utils.h" + +#define BUFFER_SIZE (1<<12) + +static void lrintf_test (void) ; + +[+ FOR data_type ++]static void pcm_test_[+ (get "name") +] (const char *filename, int filetype, int hash) ; +[+ ENDFOR data_type ++] +static void pcm_test_float (const char *filename, int filetype, int hash, int replace_float) ; +static void pcm_test_double (const char *filename, int filetype, int hash, int replace_float) ; + +typedef union +{ double d [BUFFER_SIZE + 1]; + float f [BUFFER_SIZE + 1]; + int i [BUFFER_SIZE + 1]; + short s [BUFFER_SIZE + 1]; +} BUFFER ; + +/* Data written to the file. */ +static BUFFER data_out ; + +/* Data read back from the file. */ +static BUFFER data_in ; + +int +main (void) +{ + lrintf_test () ; + + pcm_test_bits_8 ("pcm-s8.raw", SF_FORMAT_RAW | SF_FORMAT_PCM_S8, 0x9ae33814) ; + pcm_test_bits_8 ("pcm-u8.raw", SF_FORMAT_RAW | SF_FORMAT_PCM_U8, 0x651d4694) ; + + pcm_test_bits_16 ("le-pcm16.raw", SF_ENDIAN_LITTLE | SF_FORMAT_RAW | SF_FORMAT_PCM_16, 0x16866fa0) ; + pcm_test_bits_16 ("be-pcm16.raw", SF_ENDIAN_BIG | SF_FORMAT_RAW | SF_FORMAT_PCM_16, 0xc571826c) ; + + pcm_test_bits_24 ("le-pcm24.raw", SF_ENDIAN_LITTLE | SF_FORMAT_RAW | SF_FORMAT_PCM_24, 0x658e4bb6) ; + pcm_test_bits_24 ("be-pcm24.raw", SF_ENDIAN_BIG | SF_FORMAT_RAW | SF_FORMAT_PCM_24, 0xbf8cde4a) ; + + pcm_test_bits_32 ("le-pcm32.raw", SF_ENDIAN_LITTLE | SF_FORMAT_RAW | SF_FORMAT_PCM_32, 0x04c84a70) ; + pcm_test_bits_32 ("be-pcm32.raw", SF_ENDIAN_BIG | SF_FORMAT_RAW | SF_FORMAT_PCM_32, 0x069c84f6) ; + + /* Lite remove start */ + pcm_test_float ("le-float.raw", SF_ENDIAN_LITTLE | SF_FORMAT_RAW | SF_FORMAT_FLOAT, 0xbb836603, SF_FALSE) ; + pcm_test_float ("be-float.raw", SF_ENDIAN_BIG | SF_FORMAT_RAW | SF_FORMAT_FLOAT, 0x903cd8fc, SF_FALSE) ; + + pcm_test_double ("le-double.raw", SF_ENDIAN_LITTLE | SF_FORMAT_RAW | SF_FORMAT_DOUBLE, 0xbf84448e, SF_FALSE) ; + pcm_test_double ("be-double.raw", SF_ENDIAN_BIG | SF_FORMAT_RAW | SF_FORMAT_DOUBLE, 0xaf3d9fb5, SF_FALSE) ; + + puts ("Test IEEE replacement code.") ; + + pcm_test_float ("le-float.raw", SF_ENDIAN_LITTLE | SF_FORMAT_RAW | SF_FORMAT_FLOAT, 0xbb836603, SF_TRUE) ; + pcm_test_float ("be-float.raw", SF_ENDIAN_BIG | SF_FORMAT_RAW | SF_FORMAT_FLOAT, 0x903cd8fc, SF_TRUE) ; + + pcm_test_double ("le-double.raw", SF_ENDIAN_LITTLE | SF_FORMAT_RAW | SF_FORMAT_DOUBLE, 0xbf84448e, SF_TRUE) ; + pcm_test_double ("be-double.raw", SF_ENDIAN_BIG | SF_FORMAT_RAW | SF_FORMAT_DOUBLE, 0xaf3d9fb5, SF_TRUE) ; + /* Lite remove end */ + + return 0 ; +} /* main */ + +/*============================================================================================ +** Here are the test functions. +*/ + +static void +lrintf_test (void) +{ int k, items ; + float *float_data ; + int *int_data ; + + print_test_name ("lrintf_test", "") ; + + items = 1024 ; + + float_data = data_out.f ; + int_data = data_in.i ; + + for (k = 0 ; k < items ; k++) + float_data [k] = (k * ((k % 2) ? 333333.0 : -333333.0)) ; + + for (k = 0 ; k < items ; k++) + int_data [k] = lrintf (float_data [k]) ; + + for (k = 0 ; k < items ; k++) + if (fabs (int_data [k] - float_data [k]) > 1.0) + { printf ("\n\nLine %d: float : Incorrect sample (#%d : %f => %d).\n", __LINE__, k, float_data [k], int_data [k]) ; + exit (1) ; + } ; + + printf ("ok\n") ; +} /* lrintf_test */ + +[+ FOR data_type ++]static void +pcm_test_[+ (get "name") +] (const char *filename, int filetype, int hash) +{ SNDFILE *file ; + SF_INFO sfinfo ; + int k, items, zero_count ; + short *short_out, *short_in ; + int *int_out, *int_in ; + /* Lite remove start */ + float *float_out, *float_in ; + double *double_out, *double_in ; + /* Lite remove end */ + + print_test_name ("pcm_test_[+ (get "name") +]", filename) ; + + items = [+ (get "item_count") +] ; + + short_out = data_out.s ; + short_in = data_in.s ; + + zero_count = 0 ; + for (k = 0 ; k < items ; k++) + { short_out [k] = [+ (get "short_func") +] ; + zero_count = short_out [k] ? zero_count : zero_count + 1 ; + } ; + + if (zero_count > items / 4) + { printf ("\n\nLine %d: too many zeros.\n", __LINE__) ; + exit (1) ; + } ; + + sfinfo.samplerate = 44100 ; + sfinfo.frames = 123456789 ; /* Wrong length. Library should correct this on sf_close. */ + sfinfo.channels = 1 ; + sfinfo.format = filetype ; + + file = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, SF_TRUE, __LINE__) ; + + test_write_short_or_die (file, 0, short_out, items, __LINE__) ; + + sf_close (file) ; + + memset (short_in, 0, items * sizeof (short)) ; + + file = test_open_file_or_die (filename, SFM_READ, &sfinfo, SF_TRUE, __LINE__) ; + + if (sfinfo.format != filetype) + { printf ("\n\nLine %d: Returned format incorrect (0x%08X => 0x%08X).\n", __LINE__, filetype, sfinfo.format) ; + exit (1) ; + } ; + + if (sfinfo.frames != items) + { printf ("\n\nLine %d: Incorrect number of frames in file. (%d => %ld)\n", __LINE__, items, SF_COUNT_TO_LONG (sfinfo.frames)) ; + exit (1) ; + } ; + + if (sfinfo.channels != 1) + { printf ("\n\nLine %d: Incorrect number of channels in file.\n", __LINE__) ; + exit (1) ; + } ; + + check_log_buffer_or_die (file, __LINE__) ; + + test_read_short_or_die (file, 0, short_in, items, __LINE__) ; + + for (k = 0 ; k < items ; k++) + if (short_out [k] != short_in [k]) + { printf ("\n\nLine %d: Incorrect sample (#%d : 0x%x => 0x%x).\n", __LINE__, k, short_out [k], short_in [k]) ; + exit (1) ; + } ; + + sf_close (file) ; + + /* Finally, check the file hash. */ + check_file_hash_or_die (filename, hash, __LINE__) ; + + /*-------------------------------------------------------------------------- + ** Test sf_read/write_int () + */ + zero_count = 0 ; + + int_out = data_out.i ; + int_in = data_in.i ; + for (k = 0 ; k < items ; k++) + { int_out [k] = [+ (get "int_func") +] ; + zero_count = int_out [k] ? zero_count : zero_count + 1 ; + } ; + + if (zero_count > items / 4) + { printf ("\n\nLine %d: too many zeros.\n", __LINE__) ; + exit (1) ; + } ; + + sfinfo.samplerate = 44100 ; + sfinfo.frames = 123456789 ; /* Wrong length. Library should correct this on sf_close. */ + sfinfo.channels = 1 ; + sfinfo.format = filetype ; + + file = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, SF_TRUE, __LINE__) ; + + test_write_int_or_die (file, 0, int_out, items, __LINE__) ; + + sf_close (file) ; + + memset (int_in, 0, items * sizeof (int)) ; + + file = test_open_file_or_die (filename, SFM_READ, &sfinfo, SF_TRUE, __LINE__) ; + + if (sfinfo.format != filetype) + { printf ("\n\nLine %d: Returned format incorrect (0x%08X => 0x%08X).\n", __LINE__, filetype, sfinfo.format) ; + exit (1) ; + } ; + + if (sfinfo.frames != items) + { printf ("\n\nLine %d: Incorrect number of frames in file. (%d => %ld)\n", __LINE__, items, SF_COUNT_TO_LONG (sfinfo.frames)) ; + exit (1) ; + } ; + + if (sfinfo.channels != 1) + { printf ("\n\nLine %d: Incorrect number of channels in file.\n", __LINE__) ; + exit (1) ; + } ; + + check_log_buffer_or_die (file, __LINE__) ; + + test_read_int_or_die (file, 0, int_in, items, __LINE__) ; + + for (k = 0 ; k < items ; k++) + if (int_out [k] != int_in [k]) + { printf ("\n\nLine %d: int : Incorrect sample (#%d : 0x%x => 0x%x).\n", __LINE__, k, int_out [k], int_in [k]) ; + exit (1) ; + } ; + + sf_close (file) ; + + /* Lite remove start */ + /*-------------------------------------------------------------------------- + ** Test sf_read/write_float () + */ + zero_count = 0 ; + + float_out = data_out.f ; + float_in = data_in.f ; + for (k = 0 ; k < items ; k++) + { float_out [k] = [+ (get "float_func") +] ; + zero_count = (fabs (float_out [k]) > 1e-10) ? zero_count : zero_count + 1 ; + } ; + + if (zero_count > items / 4) + { printf ("\n\nLine %d: too many zeros (%d/%d).\n", __LINE__, zero_count, items) ; + exit (1) ; + } ; + + sfinfo.samplerate = 44100 ; + sfinfo.frames = 123456789 ; /* Wrong length. Library should correct this on sf_close. */ + sfinfo.channels = 1 ; + sfinfo.format = filetype ; + + file = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, SF_TRUE, __LINE__) ; + + sf_command (file, SFC_SET_NORM_FLOAT, NULL, SF_FALSE) ; + + test_write_float_or_die (file, 0, float_out, items, __LINE__) ; + + sf_close (file) ; + + memset (float_in, 0, items * sizeof (float)) ; + + file = test_open_file_or_die (filename, SFM_READ, &sfinfo, SF_TRUE, __LINE__) ; + + if (sfinfo.format != filetype) + { printf ("\n\nLine %d: Returned format incorrect (0x%08X => 0x%08X).\n", __LINE__, filetype, sfinfo.format) ; + exit (1) ; + } ; + + if (sfinfo.frames != items) + { printf ("\n\nLine %d: Incorrect number of frames in file. (%d => %ld)\n", __LINE__, items, SF_COUNT_TO_LONG (sfinfo.frames)) ; + exit (1) ; + } ; + + if (sfinfo.channels != 1) + { printf ("\n\nLine %d: Incorrect number of channels in file.\n", __LINE__) ; + exit (1) ; + } ; + + check_log_buffer_or_die (file, __LINE__) ; + + sf_command (file, SFC_SET_NORM_FLOAT, NULL, SF_FALSE) ; + + test_read_float_or_die (file, 0, float_in, items, __LINE__) ; + + for (k = 0 ; k < items ; k++) + if (fabs (float_out [k] - float_in [k]) > 1e-10) + { printf ("\n\nLine %d: float : Incorrect sample (#%d : %f => %f).\n", __LINE__, k, (double) float_out [k], (double) float_in [k]) ; + exit (1) ; + break ; + } ; + + sf_close (file) ; + + /*-------------------------------------------------------------------------- + ** Test sf_read/write_double () + */ + zero_count = 0 ; + + double_out = data_out.d ; + double_in = data_in.d ; + for (k = 0 ; k < items ; k++) + { double_out [k] = [+ (get "float_func") +] ; + zero_count = (fabs (double_out [k]) > 1e-10) ? zero_count : zero_count + 1 ; + } ; + + if (zero_count > items / 4) + { printf ("\n\nLine %d: too many zeros (%d/%d).\n", __LINE__, zero_count, items) ; + exit (1) ; + } ; + + sfinfo.samplerate = 44100 ; + sfinfo.frames = 123456789 ; /* Wrong length. Library should correct this on sf_close. */ + sfinfo.channels = 1 ; + sfinfo.format = filetype ; + + file = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, SF_TRUE, __LINE__) ; + + sf_command (file, SFC_SET_NORM_DOUBLE, NULL, SF_FALSE) ; + + test_write_double_or_die (file, 0, double_out, items, __LINE__) ; + + sf_close (file) ; + + memset (double_in, 0, items * sizeof (double)) ; + + file = test_open_file_or_die (filename, SFM_READ, &sfinfo, SF_TRUE, __LINE__) ; + + if (sfinfo.format != filetype) + { printf ("\n\nLine %d: Returned format incorrect (0x%08X => 0x%08X).\n", __LINE__, filetype, sfinfo.format) ; + exit (1) ; + } ; + + if (sfinfo.frames != items) + { printf ("\n\nLine %d: Incorrect number of frames in file. (%d => %ld)\n", __LINE__, items, SF_COUNT_TO_LONG (sfinfo.frames)) ; + exit (1) ; + } ; + + if (sfinfo.channels != 1) + { printf ("\n\nLine %d: Incorrect number of channels in file.\n", __LINE__) ; + exit (1) ; + } ; + + check_log_buffer_or_die (file, __LINE__) ; + + sf_command (file, SFC_SET_NORM_DOUBLE, NULL, SF_FALSE) ; + + test_read_double_or_die (file, 0, double_in, items, __LINE__) ; + + for (k = 0 ; k < items ; k++) + if (fabs (double_out [k] - double_in [k]) > 1e-10) + { printf ("\n\nLine %d: double : Incorrect sample (#%d : %f => %f).\n", __LINE__, k, double_out [k], double_in [k]) ; + exit (1) ; + } ; + + sf_close (file) ; + /* Lite remove end */ + unlink (filename) ; + + puts ("ok") ; +} /* pcm_test_[+ (get "name") +] */ + +[+ ENDFOR data_type ++] + +/*============================================================================== +*/ + +static void +pcm_test_float (const char *filename, int filetype, int hash, int replace_float) +{ SNDFILE *file ; + SF_INFO sfinfo ; + int k, items, frames ; + int sign ; + double *data, error ; + + print_test_name ("pcm_test_float", filename) ; + + items = BUFFER_SIZE ; + + data = data_out.d ; + for (sign = 1, k = 0 ; k < items ; k++) + { data [k] = ((double) (k * sign)) / 100.0 ; + sign = (sign > 0) ? -1 : 1 ; + } ; + + sfinfo.samplerate = 44100 ; + sfinfo.frames = items ; + sfinfo.channels = 1 ; + sfinfo.format = filetype ; + + file = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, SF_TRUE, __LINE__) ; + sf_command (file, SFC_TEST_IEEE_FLOAT_REPLACE, NULL, replace_float) ; + if (replace_float && string_in_log_buffer (file, "Using IEEE replacement") == 0) + { printf ("\n\nLine %d : Float replacement code not working.\n\n", __LINE__) ; + dump_log_buffer (file) ; + exit (1) ; + } ; + + test_write_double_or_die (file, 0, data, items, __LINE__) ; + + sf_close (file) ; + + check_file_hash_or_die (filename, hash, __LINE__) ; + + memset (data, 0, items * sizeof (double)) ; + + if ((filetype & SF_FORMAT_TYPEMASK) != SF_FORMAT_RAW) + memset (&sfinfo, 0, sizeof (sfinfo)) ; + + file = test_open_file_or_die (filename, SFM_READ, &sfinfo, SF_TRUE, __LINE__) ; + sf_command (file, SFC_TEST_IEEE_FLOAT_REPLACE, NULL, replace_float) ; + if (replace_float && string_in_log_buffer (file, "Using IEEE replacement") == 0) + { printf ("\n\nLine %d : Float replacement code not working.\n\n", __LINE__) ; + dump_log_buffer (file) ; + exit (1) ; + } ; + + if (sfinfo.format != filetype) + { printf ("\n\nError (%s:%d) Mono : Returned format incorrect (0x%08X => 0x%08X).\n", __FILE__, __LINE__, filetype, sfinfo.format) ; + exit (1) ; + } ; + + if (sfinfo.frames != items) + { printf ("\n\nError (%s:%d) Mono : Incorrect number of frames in file. (%d => %ld)\n", __FILE__, __LINE__, items, SF_COUNT_TO_LONG (sfinfo.frames)) ; + exit (1) ; + } ; + + if (sfinfo.channels != 1) + { printf ("\n\nError (%s:%d) Mono : Incorrect number of channels in file.\n", __FILE__, __LINE__) ; + exit (1) ; + } ; + + check_log_buffer_or_die (file, __LINE__) ; + + test_read_double_or_die (file, 0, data, items, __LINE__) ; + + for (sign = -1, k = 0 ; k < items ; k++) + { error = fabs (data [k] - ((double) k) / 100.0 * (sign *= -1)) ; + if (fabs (data [k]) > 1e-100 && fabs (error / data [k]) > 1e-5) + { printf ("\n\nError (%s:%d) Mono : Incorrect sample (#%d : %f => %f).\n", __FILE__, __LINE__, k, ((double) k) / 100.0, data [k]) ; + exit (1) ; + } ; + } ; + + /* Seek to start of file. */ + test_seek_or_die (file, 0, SEEK_SET, 0, sfinfo.channels, __LINE__) ; + + test_read_double_or_die (file, 0, data, 4, __LINE__) ; + for (k = 0 ; k < 4 ; k++) + { error = fabs (data [k] - ((double) k) / 100.0 * (sign *= -1)) ; + if (fabs (data [k]) > 1e-100 && fabs (error / data [k]) > 1e-5) + { printf ("\n\nError (%s:%d) Mono : Incorrect sample (#%d : %f => %f).\n", __FILE__, __LINE__, k, ((double) k) / 100.0, data [k]) ; + exit (1) ; + } ; + } ; + + /* Seek to offset from start of file. */ + test_seek_or_die (file, 10, SEEK_SET, 10, sfinfo.channels, __LINE__) ; + + test_read_double_or_die (file, 0, data + 10, 4, __LINE__) ; + for (k = 10 ; k < 14 ; k++) + { error = fabs (data [k] - ((double) k) / 100.0 * (sign *= -1)) ; + if (fabs (data [k]) > 1e-100 && fabs (error / data [k]) > 1e-5) + { printf ("\n\nError (%s:%d) Mono : Incorrect sample (#%d : %f => %f).\n", __FILE__, __LINE__, k, ((double) k) / 100.0, data [k]) ; + exit (1) ; + } ; + } ; + + /* Seek to offset from current position. */ + test_seek_or_die (file, 6, SEEK_CUR, 20, sfinfo.channels, __LINE__) ; + + test_read_double_or_die (file, 0, data + 20, 4, __LINE__) ; + for (k = 20 ; k < 24 ; k++) + { error = fabs (data [k] - ((double) k) / 100.0 * (sign *= -1)) ; + if (fabs (data [k]) > 1e-100 && fabs (error / data [k]) > 1e-5) + { printf ("\n\nError (%s:%d) Mono : Incorrect sample (#%d : %f => %f).\n", __FILE__, __LINE__, k, ((double) k) / 100.0, data [k]) ; + exit (1) ; + } ; + } ; + + /* Seek to offset from end of file. */ + test_seek_or_die (file, -1 * (sfinfo.frames - 10), SEEK_END, 10, sfinfo.channels, __LINE__) ; + + test_read_double_or_die (file, 0, data + 10, 4, __LINE__) ; + for (k = 10 ; k < 14 ; k++) + { error = fabs (data [k] - ((double) k) / 100.0 * (sign *= -1)) ; + if (fabs (data [k]) > 1e-100 && fabs (error / data [k]) > 1e-5) + { printf ("\n\nError (%s:%d) Mono : Incorrect sample (#%d : %f => %f).\n", __FILE__, __LINE__, k, ((double) k) / 100.0, data [k]) ; + exit (1) ; + } ; + } ; + + sf_close (file) ; + + /* Now test Stereo. */ + + if ((filetype & SF_FORMAT_TYPEMASK) == SF_FORMAT_SVX) /* SVX is mono only */ + { printf ("ok\n") ; + return ; + } ; + + items = BUFFER_SIZE ; + + data = data_out.d ; + for (sign = -1, k = 0 ; k < items ; k++) + data [k] = ((double) k) / 100.0 * (sign *= -1) ; + + sfinfo.samplerate = 44100 ; + sfinfo.frames = items ; + sfinfo.channels = 2 ; + sfinfo.format = filetype ; + + frames = items / sfinfo.channels ; + + file = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, SF_TRUE, __LINE__) ; + sf_command (file, SFC_TEST_IEEE_FLOAT_REPLACE, NULL, replace_float) ; + if (replace_float && string_in_log_buffer (file, "Using IEEE replacement") == 0) + { printf ("\n\nLine %d : Float replacement code not working.\n\n", __LINE__) ; + dump_log_buffer (file) ; + exit (1) ; + } ; + + test_writef_double_or_die (file, 0, data, frames, __LINE__) ; + + sf_close (file) ; + + check_file_hash_or_die (filename, hash, __LINE__) ; + + memset (data, 0, items * sizeof (double)) ; + + if ((filetype & SF_FORMAT_TYPEMASK) != SF_FORMAT_RAW) + memset (&sfinfo, 0, sizeof (sfinfo)) ; + + file = test_open_file_or_die (filename, SFM_READ, &sfinfo, SF_TRUE, __LINE__) ; + sf_command (file, SFC_TEST_IEEE_FLOAT_REPLACE, NULL, replace_float) ; + if (replace_float && string_in_log_buffer (file, "Using IEEE replacement") == 0) + { printf ("\n\nLine %d : Float replacement code not working.\n\n", __LINE__) ; + dump_log_buffer (file) ; + exit (1) ; + } ; + + if (sfinfo.format != filetype) + { printf ("\n\nError (%s:%d) Stereo : Returned format incorrect (0x%08X => 0x%08X).\n", __FILE__, __LINE__, filetype, sfinfo.format) ; + exit (1) ; + } ; + + if (sfinfo.frames != frames) + { printf ("\n\nError (%s:%d) Stereo : Incorrect number of frames in file. (%d => %ld)\n", __FILE__, __LINE__, frames, SF_COUNT_TO_LONG (sfinfo.frames)) ; + exit (1) ; + } ; + + if (sfinfo.channels != 2) + { printf ("\n\nError (%s:%d) Stereo : Incorrect number of channels in file.\n", __FILE__, __LINE__) ; + exit (1) ; + } ; + + check_log_buffer_or_die (file, __LINE__) ; + + test_readf_double_or_die (file, 0, data, frames, __LINE__) ; + for (sign = -1, k = 0 ; k < items ; k++) + { error = fabs (data [k] - ((double) k) / 100.0 * (sign *= -1)) ; + if (fabs (data [k]) > 1e-100 && fabs (error / data [k]) > 1e-5) + { printf ("\n\nError (%s:%d) Stereo : Incorrect sample (#%d : %f => %f).\n", __FILE__, __LINE__, k, ((double) k) / 100.0, data [k]) ; + exit (1) ; + } ; + } ; + + /* Seek to start of file. */ + test_seek_or_die (file, 0, SEEK_SET, 0, sfinfo.channels, __LINE__) ; + + test_readf_double_or_die (file, 0, data, 4, __LINE__) ; + for (k = 0 ; k < 4 ; k++) + { error = fabs (data [k] - ((double) k) / 100.0 * (sign *= -1)) ; + if (fabs (data [k]) > 1e-100 && fabs (error / data [k]) > 1e-5) + { printf ("\n\nError (%s:%d) Stereo : Incorrect sample (#%d : %f => %f).\n", __FILE__, __LINE__, k, ((double) k) / 100.0, data [k]) ; + exit (1) ; + } ; + } ; + + /* Seek to offset from start of file. */ + test_seek_or_die (file, 10, SEEK_SET, 10, sfinfo.channels, __LINE__) ; + + test_readf_double_or_die (file, 0, data + 20, 2, __LINE__) ; + for (k = 20 ; k < 24 ; k++) + { error = fabs (data [k] - ((double) k) / 100.0 * (sign *= -1)) ; + if (fabs (data [k]) > 1e-100 && fabs (error / data [k]) > 1e-5) + { printf ("\n\nError (%s:%d) Stereo : Incorrect sample (#%d : %f => %f).\n", __FILE__, __LINE__, k, ((double) k) / 100.0, data [k]) ; + exit (1) ; + } ; + } ; + + /* Seek to offset from current position. */ + test_seek_or_die (file, 8, SEEK_CUR, 20, sfinfo.channels, __LINE__) ; + + test_readf_double_or_die (file, 0, data + 40, 2, __LINE__) ; + for (k = 40 ; k < 44 ; k++) + { error = fabs (data [k] - ((double) k) / 100.0 * (sign *= -1)) ; + if (fabs (data [k]) > 1e-100 && fabs (error / data [k]) > 1e-5) + { printf ("\n\nError (%s:%d) Stereo : Incorrect sample (#%d : %f => %f).\n", __FILE__, __LINE__, k, ((double) k) / 100.0, data [k]) ; + exit (1) ; + } ; + } ; + + /* Seek to offset from end of file. */ + test_seek_or_die (file, -1 * (sfinfo.frames - 10), SEEK_END, 10, sfinfo.channels, __LINE__) ; + + test_readf_double_or_die (file, 0, data + 20, 2, __LINE__) ; + for (k = 20 ; k < 24 ; k++) + { error = fabs (data [k] - ((double) k) / 100.0 * (sign *= -1)) ; + if (fabs (data [k]) > 1e-100 && fabs (error / data [k]) > 1e-5) + { printf ("\n\nError (%s:%d) Stereo : Incorrect sample (#%d : %f => %f).\n", __FILE__, __LINE__, k, ((double) k) / 100.0, data [k]) ; + exit (1) ; + } ; + } ; + + sf_close (file) ; + + printf ("ok\n") ; + unlink (filename) ; +} /* pcm_test_float */ + +static void +pcm_test_double (const char *filename, int filetype, int hash, int replace_float) +{ SNDFILE *file ; + SF_INFO sfinfo ; + int k, items, frames ; + int sign ; + double *data, error ; + + /* This is the best test routine. Other should be brought up to this standard. */ + + print_test_name ("pcm_test_double", filename) ; + + items = BUFFER_SIZE ; + + data = data_out.d ; + for (sign = 1, k = 0 ; k < items ; k++) + { data [k] = ((double) (k * sign)) / 100.0 ; + sign = (sign > 0) ? -1 : 1 ; + } ; + + sfinfo.samplerate = 44100 ; + sfinfo.frames = items ; + sfinfo.channels = 1 ; + sfinfo.format = filetype ; + + file = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, SF_TRUE, __LINE__) ; + sf_command (file, SFC_TEST_IEEE_FLOAT_REPLACE, NULL, replace_float) ; + if (replace_float && string_in_log_buffer (file, "Using IEEE replacement") == 0) + { printf ("\n\nLine %d : Float replacement code not working.\n\n", __LINE__) ; + dump_log_buffer (file) ; + exit (1) ; + } ; + + test_write_double_or_die (file, 0, data, items, __LINE__) ; + + sf_close (file) ; + +#if (defined (WIN32) || defined (_WIN32)) + /* File hashing on Win32 fails due to slighty different + ** calculated values of the sin() function. + */ + hash = hash ; /* Avoid compiler warning. */ +#else + check_file_hash_or_die (filename, hash, __LINE__) ; +#endif + + memset (data, 0, items * sizeof (double)) ; + + if ((filetype & SF_FORMAT_TYPEMASK) != SF_FORMAT_RAW) + memset (&sfinfo, 0, sizeof (sfinfo)) ; + + file = test_open_file_or_die (filename, SFM_READ, &sfinfo, SF_TRUE, __LINE__) ; + sf_command (file, SFC_TEST_IEEE_FLOAT_REPLACE, NULL, replace_float) ; + if (replace_float && string_in_log_buffer (file, "Using IEEE replacement") == 0) + { printf ("\n\nLine %d : Float replacement code not working.\n\n", __LINE__) ; + dump_log_buffer (file) ; + exit (1) ; + } ; + + if (sfinfo.format != filetype) + { printf ("\n\nError (%s:%d) Mono : Returned format incorrect (0x%08X => 0x%08X).\n", __FILE__, __LINE__, filetype, sfinfo.format) ; + exit (1) ; + } ; + + if (sfinfo.frames != items) + { printf ("\n\nError (%s:%d) Mono : Incorrect number of frames in file. (%d => %ld)\n", __FILE__, __LINE__, items, SF_COUNT_TO_LONG (sfinfo.frames)) ; + exit (1) ; + } ; + + if (sfinfo.channels != 1) + { printf ("\n\nError (%s:%d) Mono : Incorrect number of channels in file.\n", __FILE__, __LINE__) ; + exit (1) ; + } ; + + check_log_buffer_or_die (file, __LINE__) ; + + test_read_double_or_die (file, 0, data, items, __LINE__) ; + + for (sign = -1, k = 0 ; k < items ; k++) + { error = fabs (data [k] - ((double) k) / 100.0 * (sign *= -1)) ; + if (fabs (data [k]) > 1e-100 && fabs (error / data [k]) > 1e-5) + { printf ("\n\nError (%s:%d) Mono : Incorrect sample (#%d : %f => %f).\n", __FILE__, __LINE__, k, ((double) k) / 100.0, data [k]) ; + exit (1) ; + } ; + } ; + + /* Seek to start of file. */ + test_seek_or_die (file, 0, SEEK_SET, 0, sfinfo.channels, __LINE__) ; + + test_read_double_or_die (file, 0, data, 4, __LINE__) ; + for (k = 0 ; k < 4 ; k++) + { error = fabs (data [k] - ((double) k) / 100.0 * (sign *= -1)) ; + if (fabs (data [k]) > 1e-100 && fabs (error / data [k]) > 1e-5) + { printf ("\n\nError (%s:%d) Mono : Incorrect sample (#%d : %f => %f).\n", __FILE__, __LINE__, k, ((double) k) / 100.0, data [k]) ; + exit (1) ; + } ; + } ; + + /* Seek to offset from start of file. */ + test_seek_or_die (file, 10, SEEK_SET, 10, sfinfo.channels, __LINE__) ; + + test_read_double_or_die (file, 0, data + 10, 4, __LINE__) ; + + test_seek_or_die (file, 0, SEEK_CUR, 14, sfinfo.channels, __LINE__) ; + + for (k = 10 ; k < 14 ; k++) + { error = fabs (data [k] - ((double) k) / 100.0 * (sign *= -1)) ; + if (fabs (data [k]) > 1e-100 && fabs (error / data [k]) > 1e-5) + { printf ("\n\nError (%s:%d) Mono : Incorrect sample (#%d : %f => %f).\n", __FILE__, __LINE__, k, ((double) k) / 100.0, data [k]) ; + exit (1) ; + } ; + } ; + + /* Seek to offset from current position. */ + test_seek_or_die (file, 6, SEEK_CUR, 20, sfinfo.channels, __LINE__) ; + + test_read_double_or_die (file, 0, data + 20, 4, __LINE__) ; + for (k = 20 ; k < 24 ; k++) + { error = fabs (data [k] - ((double) k) / 100.0 * (sign *= -1)) ; + if (fabs (data [k]) > 1e-100 && fabs (error / data [k]) > 1e-5) + { printf ("\n\nError (%s:%d) Mono : Incorrect sample (#%d : %f => %f).\n", __FILE__, __LINE__, k, ((double) k) / 100.0, data [k]) ; + exit (1) ; + } ; + } ; + + /* Seek to offset from end of file. */ + test_seek_or_die (file, -1 * (sfinfo.frames - 10), SEEK_END, 10, sfinfo.channels, __LINE__) ; + + test_read_double_or_die (file, 0, data + 10, 4, __LINE__) ; + for (k = 10 ; k < 14 ; k++) + { error = fabs (data [k] - ((double) k) / 100.0 * (sign *= -1)) ; + if (fabs (data [k]) > 1e-100 && fabs (error / data [k]) > 1e-5) + { printf ("\n\nError (%s:%d) Mono : Incorrect sample (#%d : %f => %f).\n", __FILE__, __LINE__, k, ((double) k) / 100.0, data [k]) ; + exit (1) ; + } ; + } ; + + sf_close (file) ; + + /* Now test Stereo. */ + + if ((filetype & SF_FORMAT_TYPEMASK) == SF_FORMAT_SVX) /* SVX is mono only */ + { printf ("ok\n") ; + return ; + } ; + + items = BUFFER_SIZE ; + + data = data_out.d ; + for (sign = -1, k = 0 ; k < items ; k++) + data [k] = ((double) k) / 100.0 * (sign *= -1) ; + + sfinfo.samplerate = 44100 ; + sfinfo.frames = items ; + sfinfo.channels = 2 ; + sfinfo.format = filetype ; + + frames = items / sfinfo.channels ; + + file = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, SF_TRUE, __LINE__) ; + sf_command (file, SFC_TEST_IEEE_FLOAT_REPLACE, NULL, replace_float) ; + if (replace_float && string_in_log_buffer (file, "Using IEEE replacement") == 0) + { printf ("\n\nLine %d : Float replacement code not working.\n\n", __LINE__) ; + dump_log_buffer (file) ; + exit (1) ; + } ; + + test_writef_double_or_die (file, 0, data, frames, __LINE__) ; + + sf_close (file) ; + +#if (defined (WIN32) || defined (_WIN32)) + /* File hashing on Win32 fails due to slighty different + ** calculated values. + */ + hash = hash ; /* Avoid compiler warning. */ +#else + check_file_hash_or_die (filename, hash, __LINE__) ; +#endif + + memset (data, 0, items * sizeof (double)) ; + + if ((filetype & SF_FORMAT_TYPEMASK) != SF_FORMAT_RAW) + memset (&sfinfo, 0, sizeof (sfinfo)) ; + + file = test_open_file_or_die (filename, SFM_READ, &sfinfo, SF_TRUE, __LINE__) ; + sf_command (file, SFC_TEST_IEEE_FLOAT_REPLACE, NULL, replace_float) ; + if (replace_float && string_in_log_buffer (file, "Using IEEE replacement") == 0) + { printf ("\n\nLine %d : Float replacement code not working.\n\n", __LINE__) ; + dump_log_buffer (file) ; + exit (1) ; + } ; + + if (sfinfo.format != filetype) + { printf ("\n\nError (%s:%d) Stereo : Returned format incorrect (0x%08X => 0x%08X).\n", __FILE__, __LINE__, filetype, sfinfo.format) ; + exit (1) ; + } ; + + if (sfinfo.frames != frames) + { printf ("\n\nError (%s:%d) Stereo : Incorrect number of frames in file. (%d => %ld)\n", __FILE__, __LINE__, frames, SF_COUNT_TO_LONG (sfinfo.frames)) ; + exit (1) ; + } ; + + if (sfinfo.channels != 2) + { printf ("\n\nError (%s:%d) Stereo : Incorrect number of channels in file.\n", __FILE__, __LINE__) ; + exit (1) ; + } ; + + check_log_buffer_or_die (file, __LINE__) ; + + test_readf_double_or_die (file, 0, data, frames, __LINE__) ; + + for (sign = -1, k = 0 ; k < items ; k++) + { error = fabs (data [k] - ((double) k) / 100.0 * (sign *= -1)) ; + if (fabs (data [k]) > 1e-100 && fabs (error / data [k]) > 1e-5) + { printf ("\n\nError (%s:%d) Stereo : Incorrect sample (#%d : %f => %f).\n", __FILE__, __LINE__, k, ((double) k) / 100.0, data [k]) ; + exit (1) ; + } ; + } ; + + /* Seek to start of file. */ + test_seek_or_die (file, 0, SEEK_SET, 0, sfinfo.channels, __LINE__) ; + + test_read_double_or_die (file, 0, data, 4, __LINE__) ; + for (k = 0 ; k < 4 ; k++) + { error = fabs (data [k] - ((double) k) / 100.0 * (sign *= -1)) ; + if (fabs (data [k]) > 1e-100 && fabs (error / data [k]) > 1e-5) + { printf ("\n\nError (%s:%d) Stereo : Incorrect sample (#%d : %f => %f).\n", __FILE__, __LINE__, k, ((double) k) / 100.0, data [k]) ; + exit (1) ; + } ; + } ; + + /* Seek to offset from start of file. */ + test_seek_or_die (file, 10, SEEK_SET, 10, sfinfo.channels, __LINE__) ; + + test_read_double_or_die (file, 0, data + 10, 4, __LINE__) ; + for (k = 20 ; k < 24 ; k++) + { error = fabs (data [k] - ((double) k) / 100.0 * (sign *= -1)) ; + if (fabs (data [k]) > 1e-100 && fabs (error / data [k]) > 1e-5) + { printf ("\n\nError (%s:%d) Stereo : Incorrect sample (#%d : %f => %f).\n", __FILE__, __LINE__, k, ((double) k) / 100.0, data [k]) ; + exit (1) ; + } ; + } ; + + /* Seek to offset from current position. */ + test_seek_or_die (file, 8, SEEK_CUR, 20, sfinfo.channels, __LINE__) ; + + test_readf_double_or_die (file, 0, data + 40, 4, __LINE__) ; + for (k = 40 ; k < 44 ; k++) + { error = fabs (data [k] - ((double) k) / 100.0 * (sign *= -1)) ; + if (fabs (data [k]) > 1e-100 && fabs (error / data [k]) > 1e-5) + { printf ("\n\nError (%s:%d) Stereo : Incorrect sample (#%d : %f => %f).\n", __FILE__, __LINE__, k, ((double) k) / 100.0, data [k]) ; + exit (1) ; + } ; + } ; + + /* Seek to offset from end of file. */ + test_seek_or_die (file, -1 * (sfinfo.frames -10), SEEK_END, 10, sfinfo.channels, __LINE__) ; + + test_readf_double_or_die (file, 0, data + 20, 4, __LINE__) ; + for (k = 20 ; k < 24 ; k++) + { error = fabs (data [k] - ((double) k) / 100.0 * (sign *= -1)) ; + if (fabs (data [k]) > 1e-100 && fabs (error / data [k]) > 1e-5) + { printf ("\n\nError (%s:%d) Stereo : Incorrect sample (#%d : %f => %f).\n", __FILE__, __LINE__, k, ((double) k) / 100.0, data [k]) ; + exit (1) ; + } ; + } ; + + sf_close (file) ; + + printf ("ok\n") ; + unlink (filename) ; +} /* pcm_test_double */ + +/*============================================================================== +*/ + +[+ COMMENT + + Do not edit or modify anything in this comment block. + The following line is a file identity tag for the GNU Arch + revision control system. + + arch-tag: cad1443b-99d9-414e-883f-178817600d40 + ++] diff --git a/tests/peak_chunk_test.c b/tests/peak_chunk_test.c new file mode 100644 index 00000000..3782dbf5 --- /dev/null +++ b/tests/peak_chunk_test.c @@ -0,0 +1,289 @@ +/* +** Copyright (C) 2001-2004 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include +#include + +#if HAVE_UNISTD_H +#include +#endif + +#include + +#include "utils.h" + +#define BUFFER_LEN (1<<15) +#define LOG_BUFFER_SIZE 1024 + + +static void test_float_peak (const char *filename, int filetype) ; + +static void check_logged_peaks (char *buffer) ; + +/* Force the start of this buffer to be double aligned. Sparc-solaris will +** choke if its not. +*/ +static double data [BUFFER_LEN] ; +static char log_buffer [LOG_BUFFER_SIZE] ; + +int +main (int argc, char *argv []) +{ int do_all = 0 ; + int test_count = 0 ; + + if (argc != 2) + { printf ("Usage : %s \n", argv [0]) ; + printf (" Where is one of the following:\n") ; + printf (" wav - test WAV file peak chunk\n") ; + printf (" aiff - test AIFF file PEAK chunk\n") ; + printf (" all - perform all tests\n") ; + exit (1) ; + } ; + + do_all=!strcmp (argv [1], "all") ; + + if (do_all || ! strcmp (argv [1], "wav")) + { test_float_peak ("peak_float.wav", SF_FORMAT_WAV | SF_FORMAT_FLOAT) ; + test_float_peak ("peak_float.wavex", SF_FORMAT_WAVEX | SF_FORMAT_FLOAT) ; + test_float_peak ("peak_float.rifx", SF_ENDIAN_BIG | SF_FORMAT_WAV | SF_FORMAT_FLOAT) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "aiff")) + { test_float_peak ("peak_float.aiff", SF_FORMAT_AIFF | SF_FORMAT_FLOAT) ; + test_count++ ; + } ; + + if (test_count == 0) + { printf ("Mono : ************************************\n") ; + printf ("Mono : * No '%s' test defined.\n", argv [1]) ; + printf ("Mono : ************************************\n") ; + return 1 ; + } ; + + return 0 ; +} /* main */ + +/*============================================================================================ +** Here are the test functions. +*/ + +static void +test_float_peak (const char *filename, int filetype) +{ SNDFILE *file ; + SF_INFO sfinfo ; + int k, frames, count ; + + print_test_name ("test_float_peak", filename) ; + + sfinfo.samplerate = 44100 ; + sfinfo.format = filetype ; + sfinfo.channels = 4 ; + sfinfo.frames = 0 ; + + frames = BUFFER_LEN / sfinfo.channels ; + + /* Create some random data with a peak value of 0.66. */ + for (k = 0 ; k < BUFFER_LEN ; k++) + data [k] = (rand () % 2000) / 3000.0 ; + + /* Insert some larger peaks a know locations. */ + data [4 * (frames / 8) + 0] = (frames / 8) * 0.01 ; /* First channel */ + data [4 * (frames / 6) + 1] = (frames / 6) * 0.01 ; /* Second channel */ + data [4 * (frames / 4) + 2] = (frames / 4) * 0.01 ; /* Third channel */ + data [4 * (frames / 2) + 3] = (frames / 2) * 0.01 ; /* Fourth channel */ + + /* Write a file with PEAK chunks. */ + file = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, 0, __LINE__) ; + + /* Try to confuse the header writer by adding a removing the PEAK chunk. */ + sf_command (file, SFC_SET_ADD_PEAK_CHUNK, NULL, SF_TRUE) ; + sf_command (file, SFC_SET_ADD_PEAK_CHUNK, NULL, SF_FALSE) ; + sf_command (file, SFC_SET_ADD_PEAK_CHUNK, NULL, SF_TRUE) ; + + /* Write the data in four passed. The data is designed so that peaks will + ** be written in the different calls to sf_write_double (). + */ + for (count = 0 ; count < 4 ; count ++) + test_write_double_or_die (file, 0, data + count * BUFFER_LEN / 4, BUFFER_LEN / 4, BUFFER_LEN / 4) ; + + sf_close (file) ; + + file = test_open_file_or_die (filename, SFM_READ, &sfinfo, 0, __LINE__) ; + + if (sfinfo.format != filetype) + { printf ("\n\nLine %d: Returned format incorrect (0x%08X => 0x%08X).\n", __LINE__, filetype, sfinfo.format) ; + exit (1) ; + } ; + + if (sfinfo.frames != frames) + { printf ("\n\nLine %d: Incorrect number of frames in file. (%d => %ld)\n", __LINE__, frames, (long) sfinfo.frames) ; + exit (1) ; + } ; + + if (sfinfo.channels != 4) + { printf ("\n\nLine %d: Incorrect number of channels in file.\n", __LINE__) ; + exit (1) ; + } ; + + /* Check these two commands. */ + if (sf_command (file, SFC_GET_SIGNAL_MAX, data, sizeof (double)) == SF_FALSE) + { printf ("\n\nLine %d: Command should have returned SF_TRUE.\n", __LINE__) ; + exit (1) ; + } ; + + if (fabs (data [0] - (frames / 2) * 0.01) > 0.01) + { printf ("\n\nLine %d: Bad peak value (%f should be %f) for command SFC_GET_SIGNAL_MAX.\n", __LINE__, data [0], (frames / 2) * 0.01) ; + exit (1) ; + } ; + + if (sf_command (file, SFC_GET_MAX_ALL_CHANNELS, data, sizeof (double) * sfinfo.channels) == SF_FALSE) + { printf ("\n\nLine %d: Command should have returned SF_TRUE.\n", __LINE__) ; + exit (1) ; + } ; + + if (fabs (data [3] - (frames / 2) * 0.01) > 0.01) + { printf ("\n\nLine %d: Bad peak value (%f should be %f) for command SFC_GET_MAX_ALL_CHANNELS.\n", __LINE__, data [0], (frames / 2) * 0.01) ; + exit (1) ; + } ; + + /* Get the log buffer data. */ + log_buffer [0] = 0 ; + sf_command (file, SFC_GET_LOG_INFO, log_buffer, LOG_BUFFER_SIZE) ; + + if (strlen (log_buffer) == 0) + { printf ("\n\nLine %d: Empty log buffer,\n", __LINE__) ; + exit (1) ; + } ; + + check_logged_peaks (log_buffer) ; + + sf_close (file) ; + + /* Write a file ***without*** PEAK chunks. */ + file = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, 0, __LINE__) ; + + /* Try to confuse the header writer by adding a removing the PEAK chunk. */ + sf_command (file, SFC_SET_ADD_PEAK_CHUNK, NULL, SF_FALSE) ; + sf_command (file, SFC_SET_ADD_PEAK_CHUNK, NULL, SF_TRUE) ; + sf_command (file, SFC_SET_ADD_PEAK_CHUNK, NULL, SF_FALSE) ; + + /* Write the data in four passed. The data is designed so that peaks will + ** be written in the different calls to sf_write_double (). + */ + for (count = 0 ; count < 4 ; count ++) + test_write_double_or_die (file, 0, data + count * BUFFER_LEN / 4, BUFFER_LEN / 4, BUFFER_LEN / 4) ; + + sf_close (file) ; + + file = test_open_file_or_die (filename, SFM_READ, &sfinfo, 0, __LINE__) ; + + /* Check these two commands. */ + if (sf_command (file, SFC_GET_SIGNAL_MAX, data, sizeof (double))) + { printf ("\n\nLine %d: Command should have returned SF_FALSE.\n", __LINE__) ; + exit (1) ; + } ; + + if (sf_command (file, SFC_GET_MAX_ALL_CHANNELS, data, sizeof (double) * sfinfo.channels)) + { printf ("\n\nLine %d: Command should have returned SF_FALSE.\n", __LINE__) ; + exit (1) ; + } ; + + /* Get the log buffer data. */ + log_buffer [0] = 0 ; + sf_command (file, SFC_GET_LOG_INFO, log_buffer, LOG_BUFFER_SIZE) ; + + if (strlen (log_buffer) == 0) + { printf ("\n\nLine %d: Empty log buffer,\n", __LINE__) ; + exit (1) ; + } ; + + if (strstr (log_buffer, "PEAK :") != NULL) + { printf ("\n\nLine %d: Should not have a PEAK chunk in this file.\n\n", __LINE__) ; + puts (log_buffer) ; + exit (1) ; + } ; + + sf_close (file) ; + + unlink (filename) ; + printf ("ok\n") ; +} /* test_float_peak */ + +static void +check_logged_peaks (char *buffer) +{ char *cptr ; + int k, chan, channel_count, position ; + float value ; + + if (strstr (buffer, "should") || strstr (buffer, "*")) + { printf ("\n\nLine %d: Something wrong in buffer. Dumping.\n", __LINE__) ; + puts (buffer) ; + exit (1) ; + } ; + + if (! (cptr = strstr (buffer, "Channels")) || sscanf (cptr, "Channels : %d", &channel_count) != 1) + { printf ("\n\nLine %d: Couldn't find channel count.\n", __LINE__) ; + exit (1) ; + } ; + + if (channel_count != 4) + { printf ("\n\nLine %d: Wrong channel count (4 ->%d).\n", __LINE__, channel_count) ; + exit (1) ; + } ; + + if (! (cptr = strstr (buffer, "Ch Position Value"))) + { printf ("\n\nLine %d: Can't find PEAK data.\n", __LINE__) ; + exit (1) ; + } ; + + for (k = 0 ; k < channel_count ; k++) + { if (! (cptr = strchr (cptr, '\n'))) + { printf ("\n\nLine %d: Got lost.\n", __LINE__) ; + exit (1) ; + } ; + if (sscanf (cptr, "%d %d %f", &chan, &position, &value) != 3) + { printf ("\n\nLine %d: sscanf failed.\n", __LINE__) ; + exit (1) ; + } ; + if (position == 0) + { printf ("\n\nLine %d: peak position for channel %d should not be at offset 0.\n", __LINE__, chan) ; + printf (buffer) ; + exit (1) ; + } ; + if (chan != k || fabs ((position) * 0.01 - value) > 1e-6) + { printf ("\n\nLine %d: Error : peak value incorrect!\n", __LINE__) ; + printf (buffer) ; + printf ("\n\nLine %d: %d %f %f\n", __LINE__, chan, position * 0.01, value) ; + exit (1) ; + } ; + cptr ++ ; /* Move past current newline. */ + } ; + +} /* check_logged_peaks */ +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: f10ca506-5808-4393-9d58-e3ec201fb7ee +*/ diff --git a/tests/pipe_test.def b/tests/pipe_test.def new file mode 100644 index 00000000..e1487d93 --- /dev/null +++ b/tests/pipe_test.def @@ -0,0 +1,22 @@ +autogen definitions pipe_test.tpl; + +data_type = { + type_name = short ; + }; + +data_type = { + type_name = float ; + }; + +data_type = { + type_name = double ; + }; + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 5bbde7f4-71e4-4f3e-bdae-a1cb27cdf338 +*/ + diff --git a/tests/pipe_test.tpl b/tests/pipe_test.tpl new file mode 100644 index 00000000..95680cb9 --- /dev/null +++ b/tests/pipe_test.tpl @@ -0,0 +1,382 @@ +[+ AutoGen5 template c +] +/* +** Copyright (C) 2001-2004 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/*========================================================================== +** This is a test program which tests reading from and writing to pipes. +*/ + +#include "sfconfig.h" + +#include +#include +#include + +#if (OS_IS_WIN32) + +int +main (void) +{ + puts (" pipe_test : this test doesn't work on win32.") ; + return 0 ; +} /* main */ + +#else + +#if HAVE_UNISTD_H +#include +#endif + +#include +#include +#include + +#include + +#include "utils.h" + +typedef struct +{ int format ; + const char *ext ; +} FILETYPE ; + +static int file_exists (const char *filename) ; +static void useek_pipe_rw_test (int filetype, const char *ext) ; +static void pipe_read_test (int filetype, const char *ext) ; +static void pipe_write_test (const char *ext) ; +static void pipe_test_others (FILETYPE*, FILETYPE*) ; + +static FILETYPE read_write_types [] = +{ { SF_FORMAT_RAW , "raw" }, + { SF_FORMAT_AU , "au" }, + /* Lite remove start */ + { SF_FORMAT_PAF , "paf" }, + { SF_FORMAT_IRCAM , "ircam" }, + { SF_FORMAT_PVF , "pvf" }, + /* Lite remove end */ + { 0 , NULL } +} ; + +static FILETYPE read_only_types [] = +{ { SF_FORMAT_RAW , "raw" }, + { SF_FORMAT_AU , "au" }, + { SF_FORMAT_AIFF , "aiff" }, + { SF_FORMAT_WAV , "wav" }, + { SF_FORMAT_W64 , "w64" }, + /* Lite remove start */ + { SF_FORMAT_PAF , "paf" }, + { SF_FORMAT_NIST , "nist" }, + { SF_FORMAT_IRCAM , "ircam" }, + { SF_FORMAT_MAT4 , "mat4" }, + { SF_FORMAT_MAT5 , "mat5" }, + { SF_FORMAT_SVX , "svx" }, + { SF_FORMAT_PVF , "pvf" }, + /* Lite remove end */ + { 0 , NULL } +} ; + +int +main (void) +{ int k ; + + if (file_exists ("libsndfile.spec.in")) + chdir ("tests") ; + + for (k = 0 ; read_only_types [k].format ; k++) + pipe_read_test (read_only_types [k].format, read_only_types [k].ext) ; + + for (k = 0 ; read_write_types [k].format ; k++) + pipe_write_test (read_write_types [k].ext) ; + + for (k = 0 ; read_write_types [k].format ; k++) + useek_pipe_rw_test (read_write_types [k].format, read_write_types [k].ext) ; + + if (0) + pipe_test_others (read_write_types, read_only_types) ; + + return 0 ; +} /* main */ + +/*============================================================================== +*/ + +static void +pipe_read_test (int filetype, const char *ext) +{ static short data [PIPE_TEST_LEN] ; + static char buffer [256] ; + static char filename [256] ; + + SNDFILE *outfile ; + SF_INFO sfinfo ; + int k, retval ; + + snprintf (filename, sizeof (filename), "pipe_in.%s", ext) ; + print_test_name ("pipe_read_test", filename) ; + + sfinfo.format = filetype | SF_FORMAT_PCM_16 ; + sfinfo.channels = 1 ; + sfinfo.samplerate = 44100 ; + + for (k = 0 ; k < PIPE_TEST_LEN ; k++) + data [k] = PIPE_INDEX (k) ; + + outfile = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, SF_TRUE, __LINE__) ; + test_writef_short_or_die (outfile, 0, data, PIPE_TEST_LEN, __LINE__) ; + sf_close (outfile) ; + + snprintf (buffer, sizeof (buffer), "cat %s | ./stdin_test %s ", filename, ext) ; + if ((retval = system (buffer)) != 0) + { retval = WEXITSTATUS (retval) ; + printf ("\n\n Line %d : pipe test returned error for file type \"%s\".\n\n", __LINE__, ext) ; + exit (retval) ; + } ; + + unlink (filename) ; + puts ("ok") ; + + return ; +} /* pipe_read_test */ + +static void +pipe_write_test (const char *ext) +{ static char buffer [256] ; + + int retval ; + + print_test_name ("pipe_write_test", ext) ; + + snprintf (buffer, sizeof (buffer), "./stdout_test %s | ./stdin_test %s ", ext, ext) ; + if ((retval = system (buffer))) + { retval = WEXITSTATUS (retval) ; + printf ("\n\n Line %d : pipe test returned error file type \"%s\".\n\n", __LINE__, ext) ; + exit (retval) ; + } ; + + puts ("ok") ; + + return ; +} /* pipe_write_test */ + +/*============================================================================== +*/ + +[+ FOR data_type +] +static void +useek_pipe_rw_[+ (get "type_name") +] (const char * ext, SF_INFO * psfinfo_write, SF_INFO * psfinfo_read) +{ static [+ (get "type_name") +] buffer [PIPE_TEST_LEN] ; + static [+ (get "type_name") +] data [PIPE_TEST_LEN] ; + SNDFILE *outfile ; + SNDFILE *infile_piped ; + + int k, status = 0 ; + int pipefd [2] ; + pid_t pida ; + + for (k = 0 ; k < PIPE_TEST_LEN ; k++) + data [k] = PIPE_INDEX (k) ; + + /* + ** Create the pipe. + */ + pipe (pipefd) ; + + /* + ** Attach the write end of the pipe to be written to. + */ + if ((outfile = sf_open_fd (pipefd [1], SFM_WRITE, psfinfo_write, SF_TRUE)) == NULL) + { printf ("\n\n%s %d : unable to create unseekable pipe for write type \"%s\".\n", __func__, __LINE__, ext) ; + printf ("\t%s\n\n", sf_strerror (outfile)) ; + exit (1) ; + } ; + + if (sf_error (outfile) != SF_ERR_NO_ERROR) + { printf ("\n\n%s %d : unable to open unseekable pipe for write type \"%s\".\n\n", __func__, __LINE__, ext) ; + exit (1) ; + } ; + + /* + ** Attach the read end of the pipe to be read from. + */ + if ((infile_piped = sf_open_fd (pipefd [0], SFM_READ, psfinfo_read, SF_TRUE)) == NULL) + { printf ("\n\n%s %d : unable to create unseekable pipe for read type. \"%s\".\n\n", __func__, __LINE__, ext) ; + exit (1) ; + } ; + + if (sf_error (infile_piped) != SF_ERR_NO_ERROR) + { printf ("\n\n%s %d : unable to open unseekable pipe for read type \"%s\".\n\n", __func__, __LINE__, ext) ; + exit (1) ; + } ; + + /* Fork a child process that will write directly into the pipe. */ + if ((pida = fork ()) == 0) /* child process */ + { test_writef_[+ (get "type_name") +]_or_die (outfile, 0, data, PIPE_TEST_LEN, __LINE__) ; + exit (0) ; + } ; + + /* In the parent process, read from the pipe and compare what is read + ** to what is written, if they match everything went as planned. + */ + test_readf_[+ (get "type_name") +]_or_die (infile_piped, 0, buffer, PIPE_TEST_LEN, __LINE__) ; + if (memcmp (buffer, data, sizeof (buffer)) != 0) + { printf ("\n\n%s %d : unseekable pipe test failed for file type \"%s\".\n\n", __func__, __LINE__, ext) ; + exit (1) ; + } ; + + /* Wait for the child process to return. */ + waitpid (pida, &status, 0) ; + status = WEXITSTATUS (status) ; + sf_close (outfile) ; + sf_close (infile_piped) ; + + if (status != 0) + { printf ("\n\n%s %d : status of child process is %d for file type %s.\n\n", __func__, __LINE__, status, ext) ; + exit (1) ; + } ; + + return ; +} /* useek_pipe_rw_[+ (get "type_name") +] */ + +[+ ENDFOR data_type +] + + +static void +useek_pipe_rw_test (int filetype, const char *ext) +{ SF_INFO sfinfo_write ; + SF_INFO sfinfo_read ; + + print_test_name ("useek_pipe_rw_test", ext) ; + + /* + ** Setup the INFO structures for the filetype we will be + ** working with. + */ + sfinfo_write.format = filetype | SF_FORMAT_PCM_16 ; + sfinfo_write.channels = 1 ; + sfinfo_write.samplerate = 44100 ; + + + sfinfo_read.format = 0 ; + if (filetype == SF_FORMAT_RAW) + { sfinfo_read.format = filetype | SF_FORMAT_PCM_16 ; + sfinfo_read.channels = 1 ; + sfinfo_read.samplerate = 44100 ; + } ; + + useek_pipe_rw_short (ext, &sfinfo_write, &sfinfo_read) ; + + sfinfo_read.format = sfinfo_write.format = filetype | SF_FORMAT_FLOAT ; + if (sf_format_check (&sfinfo_read) != 0) + useek_pipe_rw_float (ext, &sfinfo_write, &sfinfo_read) ; + + sfinfo_read.format = sfinfo_write.format = filetype | SF_FORMAT_DOUBLE ; + if (sf_format_check (&sfinfo_read) != 0) + useek_pipe_rw_double (ext, &sfinfo_write, &sfinfo_read) ; + + puts ("ok") ; + return ; +} /* useek_pipe_rw_test */ + + + +static void +pipe_test_others (FILETYPE* list1, FILETYPE* list2) +{ SF_FORMAT_INFO info ; + int k, m, major_count, in_list ; + + print_test_name ("pipe_test_others", "") ; + + sf_command (NULL, SFC_GET_FORMAT_MAJOR_COUNT, &major_count, sizeof (int)) ; + + for (k = 0 ; k < major_count ; k++) + { info.format = k ; + + sf_command (NULL, SFC_GET_FORMAT_MAJOR, &info, sizeof (info)) ; + + in_list = SF_FALSE ; + for (m = 0 ; list1 [m].format ; m++) + if (info.format == list1 [m].format) + in_list = SF_TRUE ; + + for (m = 0 ; list2 [m].format ; m++) + if (info.format == list2 [m].format) + in_list = SF_TRUE ; + + if (in_list) + continue ; + + printf ("%s %x\n", info.name, info.format) ; + + if (1) + { static short data [PIPE_TEST_LEN] ; + static char buffer [256] ; + static const char *filename = "pipe_in.dat" ; + + SNDFILE *outfile ; + SF_INFO sfinfo ; + int retval ; + + sfinfo.format = info.format | SF_FORMAT_PCM_16 ; + sfinfo.channels = 1 ; + sfinfo.samplerate = 44100 ; + + outfile = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, SF_TRUE, __LINE__) ; + test_writef_short_or_die (outfile, 0, data, PIPE_TEST_LEN, __LINE__) ; + sf_close (outfile) ; + + snprintf (buffer, sizeof (buffer), "cat %s | ./stdin_test %s %d ", filename, info.extension, PIPE_TEST_LEN) ; + if ((retval = system (buffer)) == 0) + { retval = WEXITSTATUS (retval) ; + printf ("\n\n Line %d : pipe test should have returned error file type \"%s\" but didn't.\n\n", __LINE__, info.name) ; + exit (1) ; + } ; + + unlink (filename) ; + } ; + } ; + + + puts ("ok") ; + + return ; +} /* pipe_test_others */ + + +/*============================================================================== +*/ + +static int +file_exists (const char *filename) +{ struct stat buf ; + + if (stat (filename, &buf)) + return 0 ; + + return 1 ; +} /* file_exists */ + +#endif + +[+ COMMENT + + Do not edit or modify anything in this comment block. + The arch-tag line is a file identity tag for the GNU Arch + revision control system. + + arch-tag: 968ad8d5-e724-41fe-8209-fa1578569dfe + ++] diff --git a/tests/raw_test.c b/tests/raw_test.c new file mode 100644 index 00000000..0741f798 --- /dev/null +++ b/tests/raw_test.c @@ -0,0 +1,195 @@ +/* +** Copyright (C) 2002-2004 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include +#include + +#if HAVE_UNISTD_H +#include +#endif + +#include + +#include "utils.h" + +#define BUFFER_LEN (1<<10) +#define LOG_BUFFER_SIZE 1024 + +static void raw_offset_test (const char *filename, int typeminor) ; +static void bad_raw_test (void) ; + +/* Force the start of this buffer to be double aligned. Sparc-solaris will +** choke if its not. +*/ +static short data [BUFFER_LEN] ; + +int +main (void) +{ + raw_offset_test ("offset.raw", SF_FORMAT_PCM_16) ; + bad_raw_test () ; + + return 0 ; +} /* main */ + +/*============================================================================================ +** Here are the test functions. +*/ + +static void +raw_offset_test (const char *filename, int typeminor) +{ SNDFILE *sndfile ; + SF_INFO sfinfo ; + sf_count_t start ; + int k, frames ; + + print_test_name ("raw_offset_test", filename) ; + + sfinfo.samplerate = 44100 ; + sfinfo.format = SF_FORMAT_RAW | typeminor ; + sfinfo.channels = 1 ; + sfinfo.frames = 0 ; + + frames = BUFFER_LEN / sfinfo.channels ; + + sndfile = test_open_file_or_die (filename, SFM_RDWR, &sfinfo, SF_TRUE, __LINE__) ; + + start = 0 ; + sf_command (sndfile, SFC_FILE_TRUNCATE, &start, sizeof (start)) ; + + for (k = 0 ; k < BUFFER_LEN ; k++) + data [k] = k ; + test_write_short_or_die (sndfile, 0, data, BUFFER_LEN, __LINE__) ; + + sf_close (sndfile) ; + + sndfile = test_open_file_or_die (filename, SFM_READ, &sfinfo, SF_TRUE, __LINE__) ; + check_log_buffer_or_die (sndfile, __LINE__) ; + + if (abs (BUFFER_LEN - sfinfo.frames) > 1) + { printf ("\n\nLine %d : Incorrect sample count (%ld should be %d)\n", __LINE__, SF_COUNT_TO_LONG (sfinfo.frames), BUFFER_LEN) ; + dump_log_buffer (sndfile) ; + exit (1) ; + } ; + + memset (data, 0 , sizeof (data)) ; + test_read_short_or_die (sndfile, 0, data, BUFFER_LEN, __LINE__) ; + for (k = 0 ; k < BUFFER_LEN ; k++) + if (data [k] != k) + printf ("Error : line %d\n", __LINE__) ; + + /* Set dataoffset to 2 bytes from beginning of file. */ + start = 2 ; + sf_command (sndfile, SFC_SET_RAW_START_OFFSET, &start, sizeof (start)) ; + + /* Seek to new start */ + test_seek_or_die (sndfile, 0, SEEK_SET, 0, sfinfo.channels, __LINE__) ; + + memset (data, 0 , sizeof (data)) ; + test_read_short_or_die (sndfile, 0, data, BUFFER_LEN - 1, __LINE__) ; + for (k = 0 ; k < BUFFER_LEN - 1 ; k++) + if (data [k] != k + 1) + { printf ("Error : line %d\n", __LINE__) ; + exit (1) ; + } ; + + /* Set dataoffset to 4 bytes from beginning of file. */ + start = 4 ; + sf_command (sndfile, SFC_SET_RAW_START_OFFSET, &start, sizeof (start)) ; + + /* Seek to new start */ + test_seek_or_die (sndfile, 0, SEEK_SET, 0, sfinfo.channels, __LINE__) ; + + memset (data, 0 , sizeof (data)) ; + test_read_short_or_die (sndfile, 0, data, BUFFER_LEN - 2, __LINE__) ; + for (k = 0 ; k < BUFFER_LEN - 2 ; k++) + if (data [k] != k + 2) + { printf ("Error : line %d\n", __LINE__) ; + exit (1) ; + } ; + + /* Set dataoffset back to 0 bytes from beginning of file. */ + start = 0 ; + sf_command (sndfile, SFC_SET_RAW_START_OFFSET, &start, sizeof (start)) ; + + /* Seek to new start */ + test_seek_or_die (sndfile, 0, SEEK_SET, 0, sfinfo.channels, __LINE__) ; + + memset (data, 0 , sizeof (data)) ; + test_read_short_or_die (sndfile, 0, data, BUFFER_LEN, __LINE__) ; + for (k = 0 ; k < BUFFER_LEN ; k++) + if (data [k] != k) + { printf ("Error : line %d\n", __LINE__) ; + exit (1) ; + } ; + + sf_close (sndfile) ; + unlink (filename) ; + + puts ("ok") ; +} /* raw_offset_test */ + +static void +bad_raw_test (void) +{ FILE *textfile ; + SNDFILE *file ; + SF_INFO sfinfo ; + const char *errorstr, *filename = "bad.raw" ; + + print_test_name ("bad_raw_test", filename) ; + + if ((textfile = fopen (filename, "w")) == NULL) + { printf ("\n\nLine %d : not able to open text file for write.\n", __LINE__) ; + exit (1) ; + } ; + + fprintf (textfile, "This is not a valid file.\n") ; + fclose (textfile) ; + + sfinfo.samplerate = 44100 ; + sfinfo.format = SF_FORMAT_RAW | 0xABCD ; + sfinfo.channels = 1 ; + + if ((file = sf_open (filename, SFM_READ, &sfinfo)) != NULL) + { printf ("\n\nLine %d : Error, file should not have opened.\n", __LINE__ - 1) ; + exit (1) ; + } ; + + errorstr = sf_strerror (file) ; + + if (strstr (errorstr, "Bad format field in SF_INFO struct") == NULL) + { printf ("\n\nLine %d : Error bad error string : %s.\n", __LINE__ - 1, errorstr) ; + exit (1) ; + } ; + + unlink (filename) ; + + puts ("ok") ; +} /* bad_raw_test */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 3dccee22-b0bc-4a1f-966c-8ae96f0921ae +*/ diff --git a/tests/scale_clip_test.def b/tests/scale_clip_test.def new file mode 100644 index 00000000..91e55e6f --- /dev/null +++ b/tests/scale_clip_test.def @@ -0,0 +1,62 @@ +autogen definitions scale_clip_test.tpl; + +float_type = { + float_type_name = "float" ; + float_short_name = "flt" ; + float_upper_name = "FLOAT" ; + } ; + +float_type = { + float_type_name = "double" ; + float_short_name = "dbl" ; + float_upper_name = "DOUBLE" ; + } ; + + + +int_type = { + int_type_name = "short" ; + int_short_name = "s" ; + int_max_value = 0x7FFFF ; + } ; + +int_type = { + int_type_name = "int" ; + int_short_name = "i" ; + int_max_value = 0x7FFFFFFF ; + } ; + + + +data_type = { + name = "16" ; + bit_count = 16 ; + error_val = "1.0 / 0x8000" ; + } ; + +data_type = { + name = "24" ; + bit_count = 24 ; + error_val = "1.0 / 0x800000" ; + } ; + +data_type = { + name = "32" ; + bit_count = 32 ; + error_val = "1.0 / 0x80000000" ; + } ; + +data_type = { + name = "08" ; + bit_count = 8 ; + error_val = "1.0 / 0x80" ; + } ; + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 4938e5b6-6f4c-4c69-8b92-f6c6d7a6b0ec +*/ + diff --git a/tests/scale_clip_test.tpl b/tests/scale_clip_test.tpl new file mode 100644 index 00000000..45e4cec8 --- /dev/null +++ b/tests/scale_clip_test.tpl @@ -0,0 +1,339 @@ +[+ AutoGen5 template c +] +/* +** Copyright (C) 1999-2006 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include + +#if HAVE_UNISTD_H +#include +#endif + +#include + +#include + +#include "utils.h" + +#ifndef M_PI +#define M_PI 3.14159265358979323846264338 +#endif + +#define HALF_BUFFER_SIZE (1 << 12) +#define BUFFER_SIZE (2 * HALF_BUFFER_SIZE) + +#define SINE_AMP 1.1 +#define MAX_ERROR 0.0202 + +[+ FOR float_type +] +[+ FOR data_type ++]static void [+ (get "float_short_name") +]_scale_clip_test_[+ (get "name") +] (const char *filename, int filetype, float maxval) ; +[+ ENDFOR data_type ++][+ ENDFOR float_type +] + +[+ FOR float_type +] +[+ FOR int_type ++]static void [+ (get "float_type_name") +]_[+ (get "int_type_name") +]_clip_test (const char *filename, int filetype) ; +[+ ENDFOR int_type ++][+ ENDFOR float_type +] + +typedef union +{ double dbl [BUFFER_SIZE] ; + float flt [BUFFER_SIZE] ; + int i [BUFFER_SIZE] ; + short s [BUFFER_SIZE] ; +} BUFFER ; + +/* Data buffer. */ +static BUFFER buffer_out ; +static BUFFER buffer_in ; + +int +main (void) +{ + flt_scale_clip_test_08 ("scale_clip_s8.au", SF_FORMAT_AU | SF_FORMAT_PCM_S8, 1.0 * 0x80) ; + flt_scale_clip_test_08 ("scale_clip_u8.wav", SF_FORMAT_WAV | SF_FORMAT_PCM_U8, 1.0 * 0x80) ; + + dbl_scale_clip_test_08 ("scale_clip_s8.au", SF_FORMAT_AU | SF_FORMAT_PCM_S8, 1.0 * 0x80) ; + dbl_scale_clip_test_08 ("scale_clip_u8.wav", SF_FORMAT_WAV | SF_FORMAT_PCM_U8, 1.0 * 0x80) ; + + /* + ** Now use SF_FORMAT_AU where possible because it allows both + ** big and little endian files. + */ + + flt_scale_clip_test_16 ("scale_clip_be16.au", SF_ENDIAN_BIG | SF_FORMAT_AU | SF_FORMAT_PCM_16, 1.0 * 0x8000) ; + flt_scale_clip_test_16 ("scale_clip_le16.au", SF_ENDIAN_LITTLE | SF_FORMAT_AU | SF_FORMAT_PCM_16, 1.0 * 0x8000) ; + flt_scale_clip_test_24 ("scale_clip_be24.au", SF_ENDIAN_BIG | SF_FORMAT_AU | SF_FORMAT_PCM_24, 1.0 * 0x800000) ; + flt_scale_clip_test_24 ("scale_clip_le24.au", SF_ENDIAN_LITTLE | SF_FORMAT_AU | SF_FORMAT_PCM_24, 1.0 * 0x800000) ; + flt_scale_clip_test_32 ("scale_clip_be32.au", SF_ENDIAN_BIG | SF_FORMAT_AU | SF_FORMAT_PCM_32, 1.0 * 0x80000000) ; + flt_scale_clip_test_32 ("scale_clip_le32.au", SF_ENDIAN_LITTLE | SF_FORMAT_AU | SF_FORMAT_PCM_32, 1.0 * 0x80000000) ; + + dbl_scale_clip_test_16 ("scale_clip_be16.au", SF_ENDIAN_BIG | SF_FORMAT_AU | SF_FORMAT_PCM_16, 1.0 * 0x8000) ; + dbl_scale_clip_test_16 ("scale_clip_le16.au", SF_ENDIAN_LITTLE | SF_FORMAT_AU | SF_FORMAT_PCM_16, 1.0 * 0x8000) ; + dbl_scale_clip_test_24 ("scale_clip_be24.au", SF_ENDIAN_BIG | SF_FORMAT_AU | SF_FORMAT_PCM_24, 1.0 * 0x800000) ; + dbl_scale_clip_test_24 ("scale_clip_le24.au", SF_ENDIAN_LITTLE | SF_FORMAT_AU | SF_FORMAT_PCM_24, 1.0 * 0x800000) ; + dbl_scale_clip_test_32 ("scale_clip_be32.au", SF_ENDIAN_BIG | SF_FORMAT_AU | SF_FORMAT_PCM_32, 1.0 * 0x80000000) ; + dbl_scale_clip_test_32 ("scale_clip_le32.au", SF_ENDIAN_LITTLE | SF_FORMAT_AU | SF_FORMAT_PCM_32, 1.0 * 0x80000000) ; + + float_int_clip_test ("flt_int.au" , SF_ENDIAN_LITTLE | SF_FORMAT_AU | SF_FORMAT_FLOAT) ; + float_short_clip_test ("flt_short.au" , SF_ENDIAN_BIG | SF_FORMAT_AU | SF_FORMAT_FLOAT) ; + double_int_clip_test ("dbl_int.au" , SF_ENDIAN_LITTLE | SF_FORMAT_AU | SF_FORMAT_DOUBLE) ; + double_short_clip_test ("dbl_short.au" , SF_ENDIAN_BIG | SF_FORMAT_AU | SF_FORMAT_DOUBLE) ; + + return 0 ; +} /* main */ + +/*============================================================================================ +** Here are the test functions. +*/ + +[+ FOR float_type +] +[+ FOR data_type ++]static void +[+ (get "float_short_name") +]_scale_clip_test_[+ (get "name") +] (const char *filename, int filetype, float maxval) +{ SNDFILE *file ; + SF_INFO sfinfo ; + int k ; + [+ (get "float_type_name") +] *data_out, *data_in ; + double diff, clip_max_diff ; + + print_test_name ("[+ (get "float_short_name") +]_scale_clip_test_[+ (get "name") +]", filename) ; + + data_out = buffer_out.[+ (get "float_short_name") +] ; + data_in = buffer_in.[+ (get "float_short_name") +] ; + + for (k = 0 ; k < HALF_BUFFER_SIZE ; k++) + { data_out [k] = 1.2 * sin (2 * M_PI * k / HALF_BUFFER_SIZE) ; + data_out [k + HALF_BUFFER_SIZE] = data_out [k] * maxval ; + } ; + + sfinfo.samplerate = 44100 ; + sfinfo.frames = 123456789 ; /* Wrong length. Library should correct this on sf_close. */ + sfinfo.channels = 1 ; + sfinfo.format = filetype ; + + /* + ** Write two versions of the data: + ** normalized and clipped + ** un-normalized and clipped. + */ + + file = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, SF_TRUE, __LINE__) ; + sf_command (file, SFC_SET_CLIPPING, NULL, SF_TRUE) ; + test_write_[+ (get "float_type_name") +]_or_die (file, 0, data_out, HALF_BUFFER_SIZE, __LINE__) ; + sf_command (file, SFC_SET_NORM_[+ (get "float_upper_name") +], NULL, SF_FALSE) ; + test_write_[+ (get "float_type_name") +]_or_die (file, 0, data_out + HALF_BUFFER_SIZE, HALF_BUFFER_SIZE, __LINE__) ; + sf_close (file) ; + + memset (&buffer_in, 0, sizeof (buffer_in)) ; + + file = test_open_file_or_die (filename, SFM_READ, &sfinfo, SF_TRUE, __LINE__) ; + + sfinfo.format &= (SF_FORMAT_TYPEMASK | SF_FORMAT_SUBMASK) ; + + if (sfinfo.format != (filetype & (SF_FORMAT_TYPEMASK | SF_FORMAT_SUBMASK))) + { printf ("\n\nLine %d: Returned format incorrect (0x%08X => 0x%08X).\n\n", __LINE__, filetype, sfinfo.format) ; + exit (1) ; + } ; + + if (sfinfo.frames != BUFFER_SIZE) + { printf ("\n\nLine %d: Incorrect number of frames in file (%d => %ld).\n\n", __LINE__, BUFFER_SIZE, SF_COUNT_TO_LONG (sfinfo.frames)) ; + exit (1) ; + } ; + + if (sfinfo.channels != 1) + { printf ("\n\nLine %d: Incorrect number of channels in file.\n\n", __LINE__) ; + exit (1) ; + } ; + + check_log_buffer_or_die (file, __LINE__) ; + + test_read_[+ (get "float_type_name") +]_or_die (file, 0, data_in, HALF_BUFFER_SIZE, __LINE__) ; + sf_command (file, SFC_SET_NORM_[+ (get "float_upper_name") +], NULL, SF_FALSE) ; + test_read_[+ (get "float_type_name") +]_or_die (file, 0, data_in + HALF_BUFFER_SIZE, HALF_BUFFER_SIZE, __LINE__) ; + sf_close (file) ; + + /* Check normalized version. */ + clip_max_diff = 0.0 ; + for (k = 0 ; k < HALF_BUFFER_SIZE ; k++) + { if (fabs (data_in [k]) > 1.0) + { printf ("\n\nLine %d: Input sample %d/%d (%f) has not been clipped.\n\n", __LINE__, k, BUFFER_SIZE, data_in [k]) ; + exit (1) ; + } ; + + if (data_out [k] * data_in [k] < 0.0) + { printf ("\n\nLine %d: Data wrap around at index %d/%d.\n\n", __LINE__, k, BUFFER_SIZE) ; + exit (1) ; + } ; + + if (fabs (data_out [k]) > 1.0) + continue ; + + diff = fabs (data_out [k] - data_in [k]) ; + if (diff > clip_max_diff) + clip_max_diff = diff ; + } ; + + if (clip_max_diff < 1e-20) + { printf ("\n\nLine %d: Clipping difference (%e) too small (normalized).\n\n", __LINE__, clip_max_diff) ; + exit (1) ; + } ; + + if (clip_max_diff > [+ (get "error_val") +]) + { printf ("\n\nLine %d: Clipping difference (%e) too large (normalized).\n\n", __LINE__, clip_max_diff) ; + exit (1) ; + } ; + + /* Check the un-normalized data. */ + clip_max_diff = 0.0 ; + for (k = HALF_BUFFER_SIZE ; k < BUFFER_SIZE ; k++) + { if (fabs (data_in [k]) > maxval) + { printf ("\n\nLine %d: Input sample %d/%d (%f) has not been clipped.\n\n", __LINE__, k, BUFFER_SIZE, data_in [k]) ; + exit (1) ; + } ; + + if (data_out [k] * data_in [k] < 0.0) + { printf ("\n\nLine %d: Data wrap around at index %d/%d.\n\n", __LINE__, k, BUFFER_SIZE) ; + exit (1) ; + } ; + + if (fabs (data_out [k]) > maxval) + continue ; + + diff = fabs (data_out [k] - data_in [k]) ; + if (diff > clip_max_diff) + clip_max_diff = diff ; + } ; + + if (clip_max_diff < 1e-20) + { printf ("\n\nLine %d: Clipping difference (%e) too small (un-normalized).\n\n", __LINE__, clip_max_diff) ; + exit (1) ; + } ; + + if (clip_max_diff > 1.0) + { printf ("\n\nLine %d: Clipping difference (%e) too large (un-normalised).\n\n", __LINE__, clip_max_diff) ; + exit (1) ; + } ; + + printf ("ok\n") ; + unlink (filename) ; +} /* [+ (get "float_short_name") +]_scale_clip_test_[+ (get "name") +] */ + +[+ ENDFOR data_type ++] +[+ ENDFOR float_type +] + +/*============================================================================== +*/ + +[+ FOR float_type +] +[+ FOR int_type ++]static void [+ (get "float_type_name") +]_[+ (get "int_type_name") +]_clip_test (const char *filename, int filetype) +{ SNDFILE *file ; + SF_INFO sfinfo ; + [+ (get "float_type_name") +] *data_out ; + [+ (get "int_type_name") +] *data_in, max_value ; + int k ; + + print_test_name ("[+ (get "float_type_name") +]_[+ (get "int_type_name") +]_clip_test", filename) ; + + data_out = buffer_out.[+ (get "float_short_name") +] ; + data_in = buffer_in.[+ (get "int_short_name") +] ; + + for (k = 0 ; k < BUFFER_SIZE ; k++) + data_out [k] = 0.995 * sin (4 * M_PI * k / BUFFER_SIZE) ; + data_out [BUFFER_SIZE / 8] = 1.0 ; + data_out [3 * BUFFER_SIZE / 8] = -1.000000001 ; + data_out [5 * BUFFER_SIZE / 8] = 1.0 ; + data_out [7 * BUFFER_SIZE / 8] = -1.000000001 ; + + memset (&sfinfo, 0, sizeof (sfinfo)) ; + sfinfo.samplerate = 44100 ; + sfinfo.frames = 123456789 ; /* Wrong length. Library should correct this on sf_close. */ + sfinfo.channels = 1 ; + sfinfo.format = filetype ; + + /* Save unclipped data to the file. */ + file = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, SF_TRUE, __LINE__) ; + test_write_[+ (get "float_type_name") +]_or_die (file, 0, data_out, BUFFER_SIZE, __LINE__) ; + sf_close (file) ; + + memset (&sfinfo, 0, sizeof (sfinfo)) ; + + file = test_open_file_or_die (filename, SFM_READ, &sfinfo, SF_TRUE, __LINE__) ; + sf_command (file, SFC_SET_SCALE_FLOAT_INT_READ, NULL, SF_TRUE) ; + + sfinfo.format &= (SF_FORMAT_TYPEMASK | SF_FORMAT_SUBMASK) ; + + if (sfinfo.format != (filetype & (SF_FORMAT_TYPEMASK | SF_FORMAT_SUBMASK))) + { printf ("\n\nLine %d: Returned format incorrect (0x%08X => 0x%08X).\n\n", __LINE__, filetype, sfinfo.format) ; + exit (1) ; + } ; + + if (sfinfo.frames != BUFFER_SIZE) + { printf ("\n\nLine %d: Incorrect number of frames in file (%d => %ld).\n\n", __LINE__, BUFFER_SIZE, SF_COUNT_TO_LONG (sfinfo.frames)) ; + exit (1) ; + } ; + + if (sfinfo.channels != 1) + { printf ("\n\nLine %d: Incorrect number of channels in file.\n\n", __LINE__) ; + exit (1) ; + } ; + + check_log_buffer_or_die (file, __LINE__) ; + + sf_command (file, SFC_SET_CLIPPING, NULL, SF_TRUE) ; + test_read_[+ (get "int_type_name") +]_or_die (file, 0, data_in, BUFFER_SIZE, __LINE__) ; + /*-sf_command (file, SFC_SET_NORM_[+ (get "float_upper_name") +], NULL, SF_FALSE) ;-*/ + sf_close (file) ; + + /* Check the first half. */ + max_value = 0 ; + for (k = 0 ; k < sfinfo.frames ; k++) + { /* Check if data_out has different sign from data_in. */ + if ((data_out [k] < 0.0 && data_in [k] > 0) || (data_out [k] > 0.0 && data_in [k] < 0)) + { printf ("\n\nLine %d: Data wrap around at index %d/%d (%f -> %d).\n\n", __LINE__, k, BUFFER_SIZE, data_out [k], data_in [k]) ; + exit (1) ; + } ; + max_value = (max_value > abs (data_in [k])) ? max_value : abs (data_in [k]) ; + } ; + + unlink (filename) ; + puts ("ok") ; +} /* [+ (get "float_type_name") +]_[+ (get "int_type_name") +]_clip_test */ +[+ ENDFOR int_type ++][+ ENDFOR float_type +] + + + + + + +[+ COMMENT + + Do not edit or modify anything in this comment block. + The following line is a file identity tag for the GNU Arch + revision control system. + + arch-tag: dc1613fe-2985-462c-a0b4-62143a7570e8 + ++] diff --git a/tests/sftest.c b/tests/sftest.c new file mode 100644 index 00000000..ab68f135 --- /dev/null +++ b/tests/sftest.c @@ -0,0 +1,73 @@ +/* +** Copyright (C) 1999-2004 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include + +#if HAVE_UNISTD_H +#include +#endif + +#include + +#define BUFFER_SIZE (1024) + + +static short buffer [BUFFER_SIZE] ; + +int +main (int argc, char *argv []) +{ SNDFILE *file ; + SF_INFO sfinfo ; + int k, count, max = 0, total = 0 ; + + if (argc < 2) + { printf ("Expecting input file name.\n") ; + return 0 ; + } ; + + if (! (file = sf_open (argv [1], SFM_READ, &sfinfo))) + { printf ("sf_open_read failed with error : ") ; + puts (sf_strerror (NULL)) ; + exit (1) ; + } ; + + while ((count = sf_read_short (file, buffer, BUFFER_SIZE))) + { for (k = 0 ; k < count ; k++) + if (abs (buffer [k]) > max) + max = abs (buffer [k]) ; + total += count ; + } ; + + printf ("Total : %d\n", total) ; + printf ("Maximun value : %d\n", max) ; + + sf_close (file) ; + + return 0 ; +} /* main */ + + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 43654fba-c130-4870-992b-9d4a390a6964 +*/ diff --git a/tests/sfversion.c b/tests/sfversion.c new file mode 100644 index 00000000..80e98163 --- /dev/null +++ b/tests/sfversion.c @@ -0,0 +1,49 @@ +/* +** Copyright (C) 1999-2004 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include + +#if HAVE_UNISTD_H +#include +#endif + +#include + +#define BUFFER_SIZE (256) + +static char strbuffer [BUFFER_SIZE] ; + +int +main (void) +{ sf_command (NULL, SFC_GET_LIB_VERSION, strbuffer, sizeof (strbuffer)) ; + + printf ("%s", strbuffer) ; + + return 0 ; +} /* main */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 53b0b14d-a52b-4094-b510-df91e4947574 +*/ diff --git a/tests/stdin_test.c b/tests/stdin_test.c new file mode 100644 index 00000000..8f4db30c --- /dev/null +++ b/tests/stdin_test.c @@ -0,0 +1,204 @@ +/* +** Copyright (C) 2001-2004 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include +#include + +#if HAVE_UNISTD_H +#include +#endif + +#include + +#include "utils.h" + +#define BUFFER_LEN (1<<16) + +static void stdin_test (int typemajor, int count) ; + +int +main (int argc, char *argv []) +{ int do_all = 0, test_count = 0 ; + + if (BUFFER_LEN < PIPE_TEST_LEN) + { fprintf (stderr, "Error : BUFFER_LEN < PIPE_TEST_LEN.\n\n") ; + exit (1) ; + } ; + + if (argc != 2) + { fprintf (stderr, "This program cannot be run by itself. It needs\n") ; + fprintf (stderr, "to be run from the stdio_test program.\n") ; + exit (1) ; + } ; + + do_all = ! strcmp (argv [1], "all") ; + + if (do_all || ! strcmp (argv [1], "raw")) + { stdin_test (SF_FORMAT_RAW, PIPE_TEST_LEN) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "wav")) + { stdin_test (SF_FORMAT_WAV, PIPE_TEST_LEN) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "aiff")) + { stdin_test (SF_FORMAT_AIFF, PIPE_TEST_LEN) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "au")) + { stdin_test (SF_FORMAT_AU, PIPE_TEST_LEN) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "paf")) + { stdin_test (SF_FORMAT_PAF, PIPE_TEST_LEN) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "svx")) + { stdin_test (SF_FORMAT_SVX, PIPE_TEST_LEN) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "nist")) + { stdin_test (SF_FORMAT_NIST, PIPE_TEST_LEN) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "ircam")) + { stdin_test (SF_FORMAT_IRCAM, PIPE_TEST_LEN) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "voc")) + { stdin_test (SF_FORMAT_VOC, PIPE_TEST_LEN) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "w64")) + { stdin_test (SF_FORMAT_W64, PIPE_TEST_LEN) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "mat4")) + { stdin_test (SF_FORMAT_MAT4, PIPE_TEST_LEN) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "mat5")) + { stdin_test (SF_FORMAT_MAT5, PIPE_TEST_LEN) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "pvf")) + { stdin_test (SF_FORMAT_PVF, PIPE_TEST_LEN) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "htk")) + { stdin_test (SF_FORMAT_HTK, PIPE_TEST_LEN) ; + test_count++ ; + } ; + + if (test_count == 0) + { fprintf (stderr, "************************************\n") ; + fprintf (stderr, "* No '%s' test defined.\n", argv [1]) ; + fprintf (stderr, "************************************\n") ; + return 1 ; + } ; + + return 0 ; +} /* main */ + +static void +stdin_test (int typemajor, int count) +{ static short data [BUFFER_LEN] ; + + SNDFILE *file ; + SF_INFO sfinfo ; + int k, total ; + + if (typemajor == SF_FORMAT_RAW) + { sfinfo.samplerate = 44100 ; + sfinfo.format = SF_FORMAT_RAW | SF_FORMAT_PCM_16 ; + sfinfo.channels = 1 ; + sfinfo.frames = 0 ; + } + else + sfinfo.format = 0 ; + + if (! (file = sf_open ("-", SFM_READ, &sfinfo))) + { fprintf (stderr, "sf_open_read failed with error : ") ; + puts (sf_strerror (NULL)) ; + dump_log_buffer (NULL) ; + exit (1) ; + } ; + + if ((sfinfo.format & SF_FORMAT_TYPEMASK) != typemajor) + { fprintf (stderr, "\n\nError : File type doesn't match.\n") ; + exit (1) ; + } ; + + if (sfinfo.samplerate != 44100) + { fprintf (stderr, "\n\nError : Sample rate (%d) should be 44100\n", sfinfo.samplerate) ; + exit (1) ; + } ; + + if (sfinfo.channels != 1) + { fprintf (stderr, "\n\nError : Channels (%d) should be 1\n", sfinfo.channels) ; + exit (1) ; + } ; + + if (sfinfo.frames < count) + { fprintf (stderr, "\n\nError : Sample count (%ld) should be %d\n", (long) sfinfo.frames, count) ; + exit (1) ; + } ; + + total = 0 ; + while ((k = sf_read_short (file, data + total, BUFFER_LEN - total)) > 0) + total += k ; + + if (total != count) + { fprintf (stderr, "\n\nError : Expected %d frames, read %d.\n", count, total) ; + exit (1) ; + } ; + + for (k = 0 ; k < total ; k++) + if (data [k] != PIPE_INDEX (k)) + { printf ("\n\nError : data [%d] == %d, should have been %d.\n\n", k, data [k], k) ; + exit (1) ; + } ; + + sf_close (file) ; + + return ; +} /* stdin_test */ +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 5e470e3e-743e-4bbc-a588-ec883843c938 +*/ diff --git a/tests/stdio_test.c b/tests/stdio_test.c new file mode 100644 index 00000000..1d177fc3 --- /dev/null +++ b/tests/stdio_test.c @@ -0,0 +1,159 @@ +/* +** Copyright (C) 2001-2004 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/*========================================================================== +** This is a test program which tests reading from stdin and writing to +** stdout. +*/ + +#include "sfconfig.h" + +#include +#include +#include + +#if HAVE_UNISTD_H +#include +#endif + +#include +#include + +#if HAVE_SYS_WAIT_H +#include +#endif + +#include "utils.h" + +#if (OS_IS_WIN32) + +int +main (void) +{ + puts (" stdio_test : this test doesn't work on win32.") ; + return 0 ; +} /* main */ + +#else + +#ifndef WIFEXITED +#define WIFEXITED(s) (((s) & 0xff) == 0) +#endif +#ifndef WEXITSTATUS +#define WEXITSTATUS(s) (((s) & 0xff00) >> 8) +#endif + + +static size_t file_length (const char *filename) ; +static int file_exists (const char *filename) ; +static void stdio_test (const char *filetype) ; + +static const char *filetypes [] = +{ "raw", "wav", "aiff", "au", "paf", "svx", "nist", "ircam", + "voc", "w64", "mat4", "mat5", "pvf", + NULL +} ; + +int +main (void) +{ int k ; + + if (file_exists ("libsndfile.spec.in")) + chdir ("tests") ; + + for (k = 0 ; filetypes [k] ; k++) + stdio_test (filetypes [k]) ; + + return 0 ; +} /* main */ + + +static void +stdio_test (const char *filetype) +{ static char buffer [256] ; + + int file_size, retval ; + + print_test_name ("stdio_test", filetype) ; + + snprintf (buffer, sizeof (buffer), "./stdout_test %s > stdio.%s", filetype, filetype) ; + if ((retval = system (buffer))) + { retval = WIFEXITED (retval) ? WEXITSTATUS (retval) : 1 ; + printf ("%s : %s", buffer, (strerror (retval))) ; + exit (1) ; + } ; + + snprintf (buffer, sizeof (buffer), "stdio.%s", filetype) ; + if ((file_size = file_length (buffer)) < PIPE_TEST_LEN) + { printf ("\n Error : test file '%s' too small (%d).\n\n", buffer, file_size) ; + exit (1) ; + } ; + + snprintf (buffer, sizeof (buffer), "./stdin_test %s < stdio.%s", filetype, filetype) ; + if ((retval = system (buffer))) + { retval = WIFEXITED (retval) ? WEXITSTATUS (retval) : 1 ; + printf ("%s : %s", buffer, (strerror (retval))) ; + exit (1) ; + } ; + + snprintf (buffer, sizeof (buffer), "rm stdio.%s", filetype) ; + if ((retval = system (buffer))) + { retval = WIFEXITED (retval) ? WEXITSTATUS (retval) : 1 ; + printf ("%s : %s", buffer, (strerror (retval))) ; + exit (1) ; + } ; + + puts ("ok") ; + + return ; +} /* stdio_test */ + + + + +static size_t +file_length (const char *filename) +{ struct stat buf ; + + if (stat (filename, &buf)) + { perror (filename) ; + exit (1) ; + } ; + + return buf.st_size ; +} /* file_length */ + +static int +file_exists (const char *filename) +{ struct stat buf ; + + if (stat (filename, &buf)) + return 0 ; + + return 1 ; +} /* file_exists */ + +#endif + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: f46d84fd-d37b-4d08-b1ba-80f2f1e0cfb9 +*/ diff --git a/tests/stdout_test.c b/tests/stdout_test.c new file mode 100644 index 00000000..65343dd7 --- /dev/null +++ b/tests/stdout_test.c @@ -0,0 +1,167 @@ +/* +** Copyright (C) 2001-2004 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include +#include + +#if HAVE_UNISTD_H +#include +#endif + +#include + +#include "utils.h" + +static void stdout_test (int typemajor, int count) ; + +int +main (int argc, char *argv []) +{ int do_all, test_count = 0 ; + + if (argc != 2) + { fprintf (stderr, "This program cannot be run by itself. It needs\n") ; + fprintf (stderr, "to be run from the stdio_test program.\n") ; + exit (1) ; + } ; + + do_all =! strcmp (argv [1], "all") ; + + if (do_all || ! strcmp (argv [1], "raw")) + { stdout_test (SF_FORMAT_RAW, PIPE_TEST_LEN) ; + test_count ++ ; + } ; + + if (do_all || ! strcmp (argv [1], "wav")) + { stdout_test (SF_FORMAT_WAV, PIPE_TEST_LEN) ; + test_count ++ ; + } ; + + if (do_all || ! strcmp (argv [1], "aiff")) + { stdout_test (SF_FORMAT_AIFF, PIPE_TEST_LEN) ; + test_count ++ ; + } ; + + if (do_all || ! strcmp (argv [1], "au")) + { stdout_test (SF_FORMAT_AU, PIPE_TEST_LEN) ; + test_count ++ ; + } ; + + if (do_all || ! strcmp (argv [1], "paf")) + { stdout_test (SF_FORMAT_PAF, PIPE_TEST_LEN) ; + test_count ++ ; + } ; + + if (do_all || ! strcmp (argv [1], "svx")) + { stdout_test (SF_FORMAT_SVX, PIPE_TEST_LEN) ; + test_count ++ ; + } ; + + if (do_all || ! strcmp (argv [1], "nist")) + { stdout_test (SF_FORMAT_NIST, PIPE_TEST_LEN) ; + test_count ++ ; + } ; + + if (do_all || ! strcmp (argv [1], "ircam")) + { stdout_test (SF_FORMAT_IRCAM, PIPE_TEST_LEN) ; + test_count ++ ; + } ; + + if (do_all || ! strcmp (argv [1], "voc")) + { stdout_test (SF_FORMAT_VOC, PIPE_TEST_LEN) ; + test_count ++ ; + } ; + + if (do_all || ! strcmp (argv [1], "w64")) + { stdout_test (SF_FORMAT_W64, PIPE_TEST_LEN) ; + test_count ++ ; + } ; + + if (do_all || ! strcmp (argv [1], "mat4")) + { stdout_test (SF_FORMAT_MAT4, PIPE_TEST_LEN) ; + test_count ++ ; + } ; + + if (do_all || ! strcmp (argv [1], "mat5")) + { stdout_test (SF_FORMAT_MAT5, PIPE_TEST_LEN) ; + test_count ++ ; + } ; + + if (do_all || ! strcmp (argv [1], "pvf")) + { stdout_test (SF_FORMAT_PVF, PIPE_TEST_LEN) ; + test_count ++ ; + } ; + + if (test_count == 0) + { fprintf (stderr, "************************************\n") ; + fprintf (stderr, "* No '%s' test defined.\n", argv [1]) ; + fprintf (stderr, "************************************\n") ; + return 1 ; + } ; + + return 0 ; +} /* main */ + +static void +stdout_test (int typemajor, int count) +{ static short data [PIPE_TEST_LEN] ; + + SNDFILE *file ; + SF_INFO sfinfo ; + int k, total, this_write ; + + sfinfo.samplerate = 44100 ; + sfinfo.format = (typemajor | SF_FORMAT_PCM_16) ; + sfinfo.channels = 1 ; + sfinfo.frames = 0 ; + + /* Create some random data. */ + for (k = 0 ; k < PIPE_TEST_LEN ; k++) + data [k] = PIPE_INDEX (k) ; + + if ((file = sf_open ("-", SFM_WRITE, &sfinfo)) == NULL) + { fprintf (stderr, "sf_open_write failed with error : ") ; + fprintf (stderr, "%s\n", sf_strerror (NULL)) ; + exit (1) ; + } ; + + total = 0 ; + + while (total < count) + { this_write = (count - total > 1024) ? 1024 : count - total ; + if ((k = sf_write_short (file, data + total, this_write)) != this_write) + { fprintf (stderr, "sf_write_short # %d failed with short write (%d -> %d)\n", count, this_write, k) ; + exit (1) ; + } ; + total += k ; + } ; + + sf_close (file) ; + + return ; +} /* stdout_test */ +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 660c3f79-a459-4c7c-899b-b360b7403403 +*/ diff --git a/tests/string_test.c b/tests/string_test.c new file mode 100644 index 00000000..09d7e010 --- /dev/null +++ b/tests/string_test.c @@ -0,0 +1,227 @@ +/* +** Copyright (C) 2003,2004 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include +#include + +#if HAVE_UNISTD_H +#include +#endif + +#include + +#include "utils.h" + +#define BUFFER_LEN (1 << 10) +#define LOG_BUFFER_SIZE 1024 + +static void string_test (const char *filename, int typemajor) ; + +static int libsndfile_str_count (const char * cptr) ; + +int +main (int argc, char *argv []) +{ int do_all = 0 ; + int test_count = 0 ; + + if (argc != 2) + { printf ("Usage : %s \n", argv [0]) ; + printf (" Where is one of the following:\n") ; + printf (" wav - test adding strings to WAV files\n") ; + printf (" aiff - test adding strings to AIFF files\n") ; + printf (" all - perform all tests\n") ; + exit (1) ; + } ; + + do_all =! strcmp (argv [1], "all") ; + + if (do_all || ! strcmp (argv [1], "wav")) + { string_test ("strings.wav", SF_FORMAT_WAV) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "aiff")) + { string_test ("strings.aiff", SF_FORMAT_AIFF) ; + test_count++ ; + } ; + + if (test_count == 0) + { printf ("Mono : ************************************\n") ; + printf ("Mono : * No '%s' test defined.\n", argv [1]) ; + printf ("Mono : ************************************\n") ; + return 1 ; + } ; + + return 0 ; +} /* main */ + + +/*============================================================================================ +** Here are the test functions. +*/ + +static const char + software [] = "software (libsndfile-X.Y.Z)", + artist [] = "The Artist", + copyright [] = "Copyright (c) 2001 The Artist", + comment [] = "Comment goes here!!!", + date [] = "2001/01/27" ; + +static short data_out [BUFFER_LEN] ; + +static void +string_test (const char *filename, int typemajor) +{ const char *cptr ; + SNDFILE *file ; + SF_INFO sfinfo ; + int frames, errors = 0 ; + + typemajor = typemajor ; + + print_test_name ("string_test", filename) ; + + sfinfo.samplerate = 44100 ; + sfinfo.channels = 1 ; + sfinfo.frames = 0 ; + + switch (typemajor) + { case SF_FORMAT_WAV : + sfinfo.format = (typemajor | SF_FORMAT_PCM_U8) ; + break ; + + default : + sfinfo.format = (typemajor | SF_FORMAT_PCM_S8) ; + break ; + } ; + + frames = BUFFER_LEN / sfinfo.channels ; + + file = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, SF_TRUE, __LINE__) ; + + /* Write stuff at start of file. */ + sf_set_string (file, SF_STR_TITLE, filename) ; + sf_set_string (file, SF_STR_SOFTWARE, software) ; + sf_set_string (file, SF_STR_ARTIST, artist) ; + + /* Write data to file. */ + test_write_short_or_die (file, 0, data_out, BUFFER_LEN, __LINE__) ; + test_seek_or_die (file, 0, SEEK_SET, 0, sfinfo.channels, __LINE__) ; + + /* Write more stuff at end of file. */ + sf_set_string (file, SF_STR_COPYRIGHT, copyright) ; + sf_set_string (file, SF_STR_COMMENT, comment) ; + sf_set_string (file, SF_STR_DATE, date) ; + + sf_close (file) ; + + file = test_open_file_or_die (filename, SFM_READ, &sfinfo, SF_TRUE, __LINE__) ; + + check_log_buffer_or_die (file, __LINE__) ; + + if (sfinfo.frames != BUFFER_LEN) + { printf ("***** Bad frame count %d (should be %d)\n\n", (int) sfinfo.frames, BUFFER_LEN) ; + errors ++ ; + } ; + + cptr = sf_get_string (file, SF_STR_TITLE) ; + if (cptr == NULL || strcmp (filename, cptr) != 0) + { if (errors++ == 0) + puts ("\n") ; + printf (" Bad filename : %s\n", cptr) ; + } ; + + cptr = sf_get_string (file, SF_STR_COPYRIGHT) ; + if (cptr == NULL || strcmp (copyright, cptr) != 0) + { if (errors++ == 0) + puts ("\n") ; + printf (" Bad copyright : %s\n", cptr) ; + } ; + + cptr = sf_get_string (file, SF_STR_SOFTWARE) ; + if (cptr == NULL || strstr (cptr, software) != cptr) + { if (errors++ == 0) + puts ("\n") ; + printf (" Bad software : %s\n", cptr) ; + } ; + + if (libsndfile_str_count (cptr) != 1) + { if (errors++ == 0) + puts ("\n") ; + printf (" Bad software : %s\n", cptr) ; + } ; + + cptr = sf_get_string (file, SF_STR_ARTIST) ; + if (cptr == NULL || strcmp (artist, cptr) != 0) + { if (errors++ == 0) + puts ("\n") ; + printf (" Bad artist : %s\n", cptr) ; + } ; + + cptr = sf_get_string (file, SF_STR_COMMENT) ; + if (cptr == NULL || strcmp (comment, cptr) != 0) + { if (errors++ == 0) + puts ("\n") ; + printf (" Bad comment : %s\n", cptr) ; + } ; + + if (typemajor != SF_FORMAT_AIFF) + { cptr = sf_get_string (file, SF_STR_DATE) ; + if (cptr == NULL || strcmp (date, cptr) != 0) + { if (errors++ == 0) + puts ("\n") ; + printf (" Bad date : %s\n", cptr) ; + } ; + } ; + + if (errors > 0) + { printf ("\n*** Error count : %d ***\n\n", errors) ; + dump_log_buffer (file) ; + exit (1) ; + } ; + + sf_close (file) ; + unlink (filename) ; + + puts ("ok") ; +} /* string_test */ + +static int +libsndfile_str_count (const char * str) +{ const char * cptr ; + + if ((cptr = strstr (str, "libsndfile")) == NULL) + return 0 ; + + if ((cptr = strstr (cptr + 1, "libsndfile")) == NULL) + return 1 ; + + return 2 ; +} /* libsndfile_str_count */ + + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 0260b3db-c250-4244-95a0-d288a913729a +*/ diff --git a/tests/ulaw_test.c b/tests/ulaw_test.c new file mode 100644 index 00000000..1b73a130 --- /dev/null +++ b/tests/ulaw_test.c @@ -0,0 +1,262 @@ +/* +** Copyright (C) 1999-2004 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include + +#if HAVE_UNISTD_H +#include +#endif + +#include + +#include "utils.h" + +#define BUFFER_SIZE (65536) + +static unsigned char ulaw_encode (int sample) ; +static int ulaw_decode (unsigned int ulawbyte) ; + +static short short_buffer [BUFFER_SIZE] ; +static unsigned char ulaw_buffer [BUFFER_SIZE] ; + +int +main (void) +{ SNDFILE *file ; + SF_INFO sfinfo ; + const char *filename ; + int k ; + + filename = "test.raw" ; + + sfinfo.format = SF_FORMAT_RAW | SF_FORMAT_ULAW ; + sfinfo.samplerate = 44100 ; + sfinfo.frames = 123456789 ; + sfinfo.channels = 1 ; + + if ((file = sf_open (filename, SFM_WRITE, &sfinfo)) == NULL) + { printf ("sf_open_write failed with error : ") ; + fflush (stdout) ; + puts (sf_strerror (NULL)) ; + exit (1) ; + } ; + + /* Generate a file containing all possible 16 bit sample values + ** and write it to disk as ulaw encoded.frames. + */ + + for (k = 0 ; k < 0x10000 ; k++) + short_buffer [k] = k & 0xFFFF ; + + sf_write_short (file, short_buffer, BUFFER_SIZE) ; + sf_close (file) ; + + /* Now open that file and compare the ulaw encoded sample values + ** with what they should be. + */ + + if ((file = sf_open (filename, SFM_READ, &sfinfo)) == NULL) + { printf ("sf_open_write failed with error : ") ; + puts (sf_strerror (NULL)) ; + exit (1) ; + } ; + + check_log_buffer_or_die (file, __LINE__) ; + + if (sf_read_raw (file, ulaw_buffer, BUFFER_SIZE) != BUFFER_SIZE) + { printf ("sf_read_raw : ") ; + puts (sf_strerror (file)) ; + exit (1) ; + } ; + + for (k = 0 ; k < 0x10000 ; k++) + if (ulaw_encode (short_buffer [k]) != ulaw_buffer [k]) + { printf ("Encoder error : sample #%d (0x%02X should be 0x%02X)\n", k, ulaw_buffer [k], ulaw_encode (short_buffer [k])) ; + exit (1) ; + } ; + + sf_close (file) ; + + printf (" ulaw_test : encoder ... ok\n") ; + + /* Now generate a file containing all possible 8 bit encoded + ** sample values and write it to disk as ulaw encoded.frames. + */ + + if (! (file = sf_open (filename, SFM_WRITE, &sfinfo))) + { printf ("sf_open_write failed with error : ") ; + puts (sf_strerror (NULL)) ; + exit (1) ; + } ; + + for (k = 0 ; k < 256 ; k++) + ulaw_buffer [k] = k & 0xFF ; + + sf_write_raw (file, ulaw_buffer, 256) ; + sf_close (file) ; + + /* Now open that file and compare the ulaw decoded sample values + ** with what they should be. + */ + + if (! (file = sf_open (filename, SFM_READ, &sfinfo))) + { printf ("sf_open_write failed with error : ") ; + puts (sf_strerror (NULL)) ; + exit (1) ; + } ; + + check_log_buffer_or_die (file, __LINE__) ; + + if (sf_read_short (file, short_buffer, 256) != 256) + { printf ("sf_read_short : ") ; + puts (sf_strerror (file)) ; + exit (1) ; + } ; + + + for (k = 0 ; k < 256 ; k++) + if (short_buffer [k] != ulaw_decode (ulaw_buffer [k])) + { printf ("Decoder error : sample #%d (0x%04X should be 0x%04X)\n", k, short_buffer [k], ulaw_decode (ulaw_buffer [k])) ; + exit (1) ; + } ; + + sf_close (file) ; + + printf (" ulaw_test : decoder ... ok\n") ; + + unlink (filename) ; + + return 0 ; +} /* main */ + + +/*================================================================================= +** The following routines came from the sox-12.15 (Sound eXcahcnge) distribution. +** +** This code is not compiled into libsndfile. It is only used to test the +** libsndfile lookup tables for correctness. +** +** I have included the original authors comments. +*/ + +/* +** This routine converts from linear to ulaw. +** +** Craig Reese: IDA/Supercomputing Research Center +** Joe Campbell: Department of Defense +** 29 September 1989 +** +** References: +** 1) CCITT Recommendation G.711 (very difficult to follow) +** 2) "A New Digital Technique for Implementation of Any +** Continuous PCM Companding Law," Villeret, Michel, +** et al. 1973 IEEE Int. Conf. on Communications, Vol 1, +** 1973, pg. 11.12-11.17 +** 3) MIL-STD-188-113,"Interoperability and Performance Standards +** for Analog-to_Digital Conversion Techniques," +** 17 February 1987 +** +** Input: Signed 16 bit linear sample +** Output: 8 bit ulaw sample +*/ + +#define uBIAS 0x84 /* define the add-in bias for 16 bit.frames */ +#define uCLIP 32635 + +static +unsigned char ulaw_encode (int sample) +{ static int exp_lut [256] = + { 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 + } ; + + int sign, exponent, mantissa ; + unsigned char ulawbyte ; + + /* Get the sample into sign-magnitude. */ + sign = (sample >> 8) & 0x80 ; /* set aside the sign */ + if ( sign != 0 ) + sample = -sample ; /* get magnitude */ + if ( sample > uCLIP ) + sample = uCLIP ; /* clip the magnitude */ + + /* Convert from 16 bit linear to ulaw. */ + sample = sample + uBIAS ; + exponent = exp_lut [( sample >> 7 ) & 0xFF] ; + mantissa = (sample >> ( exponent + 3 ) ) & 0x0F ; + ulawbyte = ~ (sign | ( exponent << 4 ) | mantissa) ; + + return ulawbyte ; +} /* ulaw_encode */ + + +/* +** This routine converts from ulaw to 16 bit linear. +** +** Craig Reese: IDA/Supercomputing Research Center +** 29 September 1989 +** +** References: +** 1) CCITT Recommendation G.711 (very difficult to follow) +** 2) MIL-STD-188-113,"Interoperability and Performance Standards +** for Analog-to_Digital Conversion Techniques," +** 17 February 1987 +** +** Input: 8 bit ulaw sample +** Output: signed 16 bit linear sample +*/ + +static +int ulaw_decode (unsigned int ulawbyte) +{ static int exp_lut [8] = { 0, 132, 396, 924, 1980, 4092, 8316, 16764 } ; + int sign, exponent, mantissa, sample ; + + ulawbyte = ~ ulawbyte ; + sign = (ulawbyte & 0x80) ; + exponent = (ulawbyte >> 4) & 0x07 ; + mantissa = ulawbyte & 0x0F ; + sample = exp_lut [exponent] + (mantissa << (exponent + 3)) ; + if (sign != 0) + sample = -sample ; + + return sample ; +} /* ulaw_decode */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: e764cee1-5a9a-480b-a4ca-34d9b57dea6f +*/ diff --git a/tests/utils.def b/tests/utils.def new file mode 100644 index 00000000..ed2d120c --- /dev/null +++ b/tests/utils.def @@ -0,0 +1,59 @@ +autogen definitions utils.tpl; + +float_type = { + name = float ; + }; + +float_type = { + name = double ; + }; + +/*----------------------------------*/ + +io_type = { + io_element = short ; + format_str = "\"% d\\n\"" ; + }; + +io_type = { + io_element = int ; + format_str = "\"% d\\n\"" ; + }; + +io_type = { + io_element = float ; + format_str = "\"% g\\n\"" ; + }; + +io_type = { + io_element = double ; + format_str = "\"% g\\n\"" ; + }; + +read_op = { + op_element = read ; + count_name = items ; + }; + +read_op = { + op_element = readf ; + count_name = frames ; + }; + +write_op = { + op_element = write ; + count_name = items ; + }; + +write_op = { + op_element = writef ; + count_name = frames ; + }; +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 735898a2-711e-42c2-aaf1-354b20c07ac7 +*/ + diff --git a/tests/utils.tpl b/tests/utils.tpl new file mode 100644 index 00000000..bc15423d --- /dev/null +++ b/tests/utils.tpl @@ -0,0 +1,704 @@ +[+ AutoGen5 template h c +] +/* +** Copyright (C) 2002-2005 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* +** Utility functions to make writing the test suite easier. +** +** The .c and .h files were generated automagically with Autogen from +** the files utils.def and utils.tpl. +*/ + +[+ CASE (suffix) +] +[+ == h +] + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include + +#define SF_COUNT_TO_LONG(x) ((long) (x)) +#define ARRAY_LEN(x) ((int) (sizeof (x)) / (sizeof ((x) [0]))) +#define SIGNED_SIZEOF(x) ((int64_t) (sizeof (x))) + +#define PIPE_INDEX(x) ((x) + 500) +#define PIPE_TEST_LEN 12345 + +#if (defined (WIN32) || defined (_WIN32)) +#define snprintf _snprintf +#endif + +[+ FOR float_type ++]void gen_windowed_sine_[+ (get "name") +] ([+ (get "name") +] *data, int len, double maximum) ; +[+ ENDFOR float_type ++] + +void check_file_hash_or_die (const char *filename, unsigned int target_hash, int line_num) ; + +void print_test_name (const char *test, const char *filename) ; + +void dump_data_to_file (const char *filename, void *data, unsigned int datalen) ; + +static inline void +exit_if_true (int test, const char *format, ...) +{ if (test) + { va_list argptr ; + va_start (argptr, format) ; + vprintf (format, argptr) ; + va_end (argptr) ; + exit (1) ; + } ; +} /* exit_if_true */ + +/* +** Functions for saving two vectors of data in an ascii text file which +** can then be loaded into GNU octave for comparison. +*/ + +[+ FOR io_type ++]int oct_save_[+ (get "io_element") +] ([+ (get "io_element") +] *a, [+ (get "io_element") +] *b, int len) ; +[+ ENDFOR io_type ++] + +void delete_file (int format, const char *filename) ; + +void count_open_files (void) ; +void increment_open_file_count (void) ; +void check_open_file_count_or_die (int lineno) ; + +#ifdef SNDFILE_H + +void dump_log_buffer (SNDFILE *file) ; +void check_log_buffer_or_die (SNDFILE *file, int line_num) ; +int string_in_log_buffer (SNDFILE *file, const char *s) ; +void hexdump_file (const char * filename, sf_count_t offset, sf_count_t length) ; + + +SNDFILE *test_open_file_or_die + (const char *filename, int mode, SF_INFO *sfinfo, int allow_fd, int line_num) ; + +void test_read_write_position_or_die + (SNDFILE *file, int line_num, int pass, sf_count_t read_pos, sf_count_t write_pos) ; + +void test_seek_or_die + (SNDFILE *file, sf_count_t offset, int whence, sf_count_t new_pos, int channels, int line_num) ; + +[+ FOR read_op +] +[+ FOR io_type ++]void test_[+ (get "op_element") +]_[+ (get "io_element") +]_or_die + (SNDFILE *file, int pass, [+ (get "io_element") +] *test, sf_count_t [+ (get "count_name") +], int line_num) ; +[+ ENDFOR io_type +][+ ENDFOR read_op +] + +[+ FOR write_op +] +[+ FOR io_type ++]void test_[+ (get "op_element") +]_[+ (get "io_element") +]_or_die + (SNDFILE *file, int pass, const [+ (get "io_element") +] *test, sf_count_t [+ (get "count_name") +], int line_num) ; +[+ ENDFOR io_type +][+ ENDFOR write_op +] + +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +[+ == c +] + +#include "sfconfig.h" + +#include +#include + +#if HAVE_UNISTD_H +#include +#endif + +#if (HAVE_DECL_S_IRGRP == 0) +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include + +#include "utils.h" + +#ifndef M_PI +#define M_PI 3.14159265358979323846264338 +#endif + +#define LOG_BUFFER_SIZE 2048 + +[+ FOR float_type +] +void +gen_windowed_sine_[+ (get "name") +] ([+ (get "name") +] *data, int len, double maximum) +{ int k ; + + memset (data, 0, len * sizeof ([+ (get "name") +])) ; + /* + ** Choose a frequency of 1/32 so that it aligns perfectly with a DFT + ** bucket to minimise spreading of energy over more than one bucket. + ** Also do not want to make the frequency too high as some of the + ** codecs (ie gsm610) have a quite severe high frequency roll off. + */ + len /= 2 ; + + for (k = 0 ; k < len ; k++) + { data [k] = sin (2.0 * k * M_PI * 1.0 / 32.0 + 0.4) ; + + /* Apply Hanning Window. */ + data [k] *= maximum * (0.5 - 0.5 * cos (2.0 * M_PI * k / ((len) - 1))) ; + } + + return ; +} /* gen_windowed_sine_[+ (get "name") +] */ +[+ ENDFOR float_type +] + +void +check_file_hash_or_die (const char *filename, unsigned int target_hash, int line_num) +{ static unsigned char buffer [2048] ; + unsigned int hash1, hash2 ; + FILE *file ; + int k, read_count ; + + memset (buffer, 0xEE, sizeof (buffer)) ; + + /* The 'b' in the mode string means binary for Win32. */ + if (! (file = fopen (filename, "rb"))) + { printf ("\n\nLine %d: could not open file '%s'\n\n", line_num, filename) ; + exit (1) ; + } ; + + hash1 = hash2 = 0 ; + + while ((read_count = fread (buffer, 1, sizeof (buffer), file))) + { for (k = 0 ; k < read_count ; k++) + { hash1 = hash1 + buffer [k] ; + hash2 = hash2 ^ (buffer [k] << (k % 25)) ; + } ; + } ; + + fclose (file) ; + + hash1 += hash2 ; + + if (target_hash == 0) + { printf (" 0x%08x ", hash1) ; + return ; + } ; + + if (hash1 != target_hash) + { printf ("\n\nLine %d: incorrect hash value 0x%08x should be 0x%08x\n\n", line_num, hash1, target_hash) ; + exit (1) ; + } + + return ; +} /* check_file_hash_or_die */ + +void +print_test_name (const char *test, const char *filename) +{ int count ; + + if (test == NULL || filename == NULL) + { printf (__FILE__ ": bad test of filename parameter.\n") ; + exit (1) ; + } ; + + printf (" %-25s : %s ", test, filename) ; + + count = 24 - strlen (filename) ; + while (count -- > 0) + putchar ('.') ; + putchar (' ') ; + + fflush (stdout) ; +} /* print_test_name */ + +void +dump_data_to_file (const char *filename, void *data, unsigned int datalen) +{ FILE *file ; + + if ((file = fopen (filename, "wb")) == NULL) + { printf ("\n\nLine %d : could not open file : %s\n\n", __LINE__, filename) ; + exit (1) ; + } ; + + if (fwrite (data, 1, datalen, file) != datalen) + { printf ("\n\nLine %d : fwrite failed.\n\n", __LINE__) ; + exit (1) ; + } ; + + fclose (file) ; + +} /* dump_data_to_file */ + +/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +*/ + +static char octfilename [] = "error.dat" ; + +[+ FOR io_type ++]int +oct_save_[+ (get "io_element") +] ([+ (get "io_element") +] *a, [+ (get "io_element") +] *b, int len) +{ FILE *file ; + int k ; + + if (! (file = fopen (octfilename, "w"))) + return 1 ; + + fprintf (file, "# Not created by Octave\n") ; + + fprintf (file, "# name: a\n") ; + fprintf (file, "# type: matrix\n") ; + fprintf (file, "# rows: %d\n", len) ; + fprintf (file, "# columns: 1\n") ; + + for (k = 0 ; k < len ; k++) + fprintf (file, [+ (get "format_str") +], a [k]) ; + + fprintf (file, "# name: b\n") ; + fprintf (file, "# type: matrix\n") ; + fprintf (file, "# rows: %d\n", len) ; + fprintf (file, "# columns: 1\n") ; + + for (k = 0 ; k < len ; k++) + fprintf (file, [+ (get "format_str") +], b [k]) ; + + fclose (file) ; + return 0 ; +} /* oct_save_[+ (get "io_element") +] */ +[+ ENDFOR io_type ++] + +void +check_log_buffer_or_die (SNDFILE *file, int line_num) +{ static char buffer [LOG_BUFFER_SIZE] ; + int count ; + + memset (buffer, 0, LOG_BUFFER_SIZE) ; + + /* Get the log buffer data. */ + count = sf_command (file, SFC_GET_LOG_INFO, buffer, LOG_BUFFER_SIZE) ; + + if (LOG_BUFFER_SIZE - count < 2) + { printf ("\n\nLine %d : Possible long log buffer.\n", line_num) ; + exit (1) ; + } + + /* Look for "Should" */ + if (strstr (buffer, "ould")) + { printf ("\n\nLine %d : Log buffer contains `ould'. Dumping.\n", line_num) ; + puts (buffer) ; + exit (1) ; + } ; + + /* Look for "**" */ + if (strstr (buffer, "*")) + { printf ("\n\nLine %d : Log buffer contains `*'. Dumping.\n", line_num) ; + puts (buffer) ; + exit (1) ; + } ; + + /* Look for "Should" */ + if (strstr (buffer, "nknown marker")) + { printf ("\n\nLine %d : Log buffer contains `nknown marker'. Dumping.\n", line_num) ; + puts (buffer) ; + exit (1) ; + } ; + + return ; +} /* check_log_buffer_or_die */ + +int +string_in_log_buffer (SNDFILE *file, const char *s) +{ static char buffer [LOG_BUFFER_SIZE] ; + int count ; + + memset (buffer, 0, LOG_BUFFER_SIZE) ; + + /* Get the log buffer data. */ + count = sf_command (file, SFC_GET_LOG_INFO, buffer, LOG_BUFFER_SIZE) ; + + if (LOG_BUFFER_SIZE - count < 2) + { printf ("Possible long log buffer.\n") ; + exit (1) ; + } + + /* Look for string */ + return strstr (buffer, s) ? SF_TRUE : SF_FALSE ; +} /* string_in_log_buffer */ + +void +hexdump_file (const char * filename, sf_count_t offset, sf_count_t length) +{ + FILE * file ; + char buffer [16] ; + int k, m, ch, readcount ; + + if (length > 1000000) + { printf ("\n\nError : length (%ld) too long.\n\n", SF_COUNT_TO_LONG (offset)) ; + exit (1) ; + } ; + + if ((file = fopen (filename, "r")) == NULL) + { printf ("\n\nError : hexdump_file (%s) could not open file for read.\n\n", filename) ; + exit (1) ; + } ; + + if (fseek (file, offset, SEEK_SET) != 0) + { printf ("\n\nError : fseek(file, %ld, SEEK_SET) failed : %s\n\n", SF_COUNT_TO_LONG (offset), strerror (errno)) ; + exit (1) ; + } ; + + puts ("\n\n") ; + + for (k = 0 ; k < length ; k+= sizeof (buffer)) + { readcount = fread (buffer, 1, sizeof (buffer), file) ; + + printf ("%08lx : ", SF_COUNT_TO_LONG (offset + k)) ; + + for (m = 0 ; m < readcount ; m++) + printf ("%02x ", buffer [m] & 0xFF) ; + + for (m = readcount ; m < SIGNED_SIZEOF (buffer) ; m++) + printf (" ") ; + + printf (" ") ; + for (m = 0 ; m < readcount ; m++) + { ch = isprint (buffer [m]) ? buffer [m] : '.' ; + putchar (ch) ; + } ; + + if (readcount < SIGNED_SIZEOF (buffer)) + break ; + + putchar ('\n') ; + } ; + + puts ("\n") ; + + fclose (file) ; +} /* hexdump_file */ + +void +dump_log_buffer (SNDFILE *file) +{ static char buffer [LOG_BUFFER_SIZE] ; + int count ; + + memset (buffer, 0, LOG_BUFFER_SIZE) ; + + /* Get the log buffer data. */ + count = sf_command (file, SFC_GET_LOG_INFO, buffer, LOG_BUFFER_SIZE) ; + + if (strlen (buffer) < 1) + puts ("Log buffer empty.\n") ; + else + puts (buffer) ; + + return ; +} /* dump_log_buffer */ + +SNDFILE * +test_open_file_or_die (const char *filename, int mode, SF_INFO *sfinfo, int allow_fd, int line_num) +{ static int count = 0 ; + + SNDFILE *file ; + const char *modestr, *func_name ; + int oflags = 0, omode = 0 ; + + /* + ** Need to test both sf_open() and sf_open_fd(). + ** Do so alternately. + */ + switch (mode) + { case SFM_READ : + modestr = "SFM_READ" ; + oflags = O_RDONLY ; + omode = 0 ; + break ; + + case SFM_WRITE : + modestr = "SFM_WRITE" ; + oflags = O_WRONLY | O_CREAT | O_TRUNC ; + omode = S_IRUSR | S_IWUSR | S_IRGRP ; + break ; + + case SFM_RDWR : + modestr = "SFM_RDWR" ; + oflags = O_RDWR | O_CREAT ; + omode = S_IRUSR | S_IWUSR | S_IRGRP ; + break ; + default : + printf ("\n\nLine %d: Bad mode.\n", line_num) ; + fflush (stdout) ; + exit (1) ; + } ; + +#if (defined (WIN32) || defined (_WIN32)) + /* Stupid fscking windows. */ + oflags |= O_BINARY ; +#endif + + if (allow_fd && ((++count) & 1) == 1) + { int fd ; + + if (omode == 0) + fd = open (filename, oflags) ; + else + fd = open (filename, oflags, omode) ; + + if (fd < 0) + { perror ("open") ; + exit (1) ; + } ; + + func_name = "sf_open_fd" ; + file = sf_open_fd (fd, mode, sfinfo, SF_TRUE) ; + } + else + { func_name = "sf_open" ; + file = sf_open (filename, mode, sfinfo) ; + } ; + + if (file == NULL) + { printf ("\n\nLine %d: %s (%s) failed : %s\n\n", line_num, func_name, modestr, sf_strerror (NULL)) ; + dump_log_buffer (file) ; + exit (1) ; + } ; + + return file ; +} /* test_open_file_or_die */ + +void +test_read_write_position_or_die (SNDFILE *file, int line_num, int pass, sf_count_t read_pos, sf_count_t write_pos) +{ sf_count_t pos ; + + /* Check the current read position. */ + if (read_pos >= 0 && (pos = sf_seek (file, 0, SEEK_CUR | SFM_READ)) != read_pos) + { printf ("\n\nLine %d ", line_num) ; + if (pass > 0) + printf ("(pass %d): ", pass) ; + printf ("Read position (%ld) should be %ld.\n", SF_COUNT_TO_LONG (pos), SF_COUNT_TO_LONG (read_pos)) ; + exit (1) ; + } ; + + /* Check the current write position. */ + if (write_pos >= 0 && (pos = sf_seek (file, 0, SEEK_CUR | SFM_WRITE)) != write_pos) + { printf ("\n\nLine %d", line_num) ; + if (pass > 0) + printf (" (pass %d)", pass) ; + printf (" : Write position (%ld) should be %ld.\n", + SF_COUNT_TO_LONG (pos), SF_COUNT_TO_LONG (write_pos)) ; + exit (1) ; + } ; + + return ; +} /* test_read_write_position */ + +void +test_seek_or_die (SNDFILE *file, sf_count_t offset, int whence, sf_count_t new_pos, int channels, int line_num) +{ sf_count_t position ; + const char *channel_name, *whence_name ; + + switch (whence) + { case SEEK_SET : + whence_name = "SEEK_SET" ; + break ; + case SEEK_CUR : + whence_name = "SEEK_CUR" ; + break ; + case SEEK_END : + whence_name = "SEEK_END" ; + break ; + + /* SFM_READ */ + case SEEK_SET | SFM_READ : + whence_name = "SFM_READ | SEEK_SET" ; + break ; + case SEEK_CUR | SFM_READ : + whence_name = "SFM_READ | SEEK_CUR" ; + break ; + case SEEK_END | SFM_READ : + whence_name = "SFM_READ | SEEK_END" ; + break ; + + /* SFM_WRITE */ + case SEEK_SET | SFM_WRITE : + whence_name = "SFM_WRITE | SEEK_SET" ; + break ; + case SEEK_CUR | SFM_WRITE : + whence_name = "SFM_WRITE | SEEK_CUR" ; + break ; + case SEEK_END | SFM_WRITE : + whence_name = "SFM_WRITE | SEEK_END" ; + break ; + + default : + printf ("\n\nLine %d: bad whence parameter.\n", line_num) ; + exit (1) ; + } ; + + channel_name = (channels == 1) ? "Mono" : "Stereo" ; + + if ((position = sf_seek (file, offset, whence)) != new_pos) + { printf ("\n\nLine %d : %s : sf_seek (file, %ld, %s) returned %ld (should be %ld).\n\n", + line_num, channel_name, SF_COUNT_TO_LONG (offset), whence_name, + SF_COUNT_TO_LONG (position), SF_COUNT_TO_LONG (new_pos)) ; + exit (1) ; + } ; + +} /* test_seek_or_die */ + +[+ FOR read_op +] +[+ FOR io_type +] +void +test_[+ (get "op_element") +]_[+ (get "io_element") +]_or_die (SNDFILE *file, int pass, [+ (get "io_element") +] *test, sf_count_t [+ (get "count_name") +], int line_num) +{ sf_count_t count ; + + if ((count = sf_[+ (get "op_element") +]_[+ (get "io_element") +] (file, test, [+ (get "count_name") +])) != [+ (get "count_name") +]) + { printf ("\n\nLine %d", line_num) ; + if (pass > 0) + printf (" (pass %d)", pass) ; + printf (" : sf_[+ (get "op_element") +]_[+ (get "io_element") +] failed with short [+ (get "op_element") +] (%ld => %ld).\n", + SF_COUNT_TO_LONG ([+ (get "count_name") +]), SF_COUNT_TO_LONG (count)) ; + fflush (stdout) ; + puts (sf_strerror (file)) ; + exit (1) ; + } ; + + return ; +} /* test_[+ (get "op_element") +]_[+ (get "io_element") +] */ +[+ ENDFOR io_type +][+ ENDFOR read_op +] + +[+ FOR write_op +] +[+ FOR io_type +] +void +test_[+ (get "op_element") +]_[+ (get "io_element") +]_or_die (SNDFILE *file, int pass, const [+ (get "io_element") +] *test, sf_count_t [+ (get "count_name") +], int line_num) +{ sf_count_t count ; + + if ((count = sf_[+ (get "op_element") +]_[+ (get "io_element") +] (file, test, [+ (get "count_name") +])) != [+ (get "count_name") +]) + { printf ("\n\nLine %d", line_num) ; + if (pass > 0) + printf (" (pass %d)", pass) ; + printf (" : sf_[+ (get "op_element") +]_[+ (get "io_element") +] failed with short [+ (get "op_element") +] (%ld => %ld).\n", + SF_COUNT_TO_LONG ([+ (get "count_name") +]), SF_COUNT_TO_LONG (count)) ; + fflush (stdout) ; + puts (sf_strerror (file)) ; + exit (1) ; + } ; + + return ; +} /* test_[+ (get "op_element") +]_[+ (get "io_element") +] */ +[+ ENDFOR io_type +][+ ENDFOR write_op +] + +void +delete_file (int format, const char *filename) +{ char rsrc_name [512], *fname ; + + unlink (filename) ; + + if ((format & SF_FORMAT_TYPEMASK) != SF_FORMAT_SD2) + return ; + + /* + ** Now try for a resource fork stored as a separate file. + ** Grab the un-adulterated filename again. + */ + snprintf (rsrc_name, sizeof (rsrc_name), "%s", filename) ; + + if ((fname = strrchr (rsrc_name, '/')) != NULL) + fname ++ ; + else if ((fname = strrchr (rsrc_name, '\\')) != NULL) + fname ++ ; + else + fname = rsrc_name ; + + memmove (fname + 2, fname, strlen (fname) + 1) ; + fname [0] = '.' ; + fname [1] = '_' ; + + unlink (rsrc_name) ; +} /* delete_file */ + +static int allowed_open_files = -1 ; + +void +count_open_files (void) +{ +#if (defined (WIN32) || defined (_WIN32)) + return ; +#else + int k, count = 0 ; + struct stat statbuf ; + + if (allowed_open_files > 0) + return ; + + for (k = 0 ; k < 1024 ; k++) + if (fstat (k, &statbuf) == 0) + count ++ ; + + allowed_open_files = count ; +#endif +} /* count_open_files */ + +void +increment_open_file_count (void) +{ allowed_open_files ++ ; +} /* increment_open_file_count */ + +void +check_open_file_count_or_die (int lineno) +{ +#if (defined (WIN32) || defined (_WIN32)) + lineno = 0 ; + return ; +#else + int k, count = 0 ; + struct stat statbuf ; + + if (allowed_open_files < 0) + count_open_files () ; + + for (k = 0 ; k < 1024 ; k++) + if (fstat (k, &statbuf) == 0) + count ++ ; + + if (count > allowed_open_files) + { printf ("\nLine %d : number of open files (%d) > allowed (%d).\n\n", lineno, count, allowed_open_files) ; + exit (1) ; + } ; +#endif +} /* check_open_file_count_or_die */ + +[+ ESAC +] + +[+ COMMENT + + Do not edit or modify anything in this comment block. + The following line is a file identity tag for the GNU Arch + revision control system. + + arch-tag: b1183d5d-ebd4-4bc5-af50-60d774d6b1f5 + ++] diff --git a/tests/virtual_io_test.c b/tests/virtual_io_test.c new file mode 100644 index 00000000..90fb6db2 --- /dev/null +++ b/tests/virtual_io_test.c @@ -0,0 +1,238 @@ +/* +** Copyright (C) 1999-2005 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "utils.h" + +static void vio_test (const char *fname, int format) ; + +int +main (void) +{ + vio_test ("vio_pcm16.wav", SF_FORMAT_WAV | SF_FORMAT_PCM_16) ; + vio_test ("vio_pcm24.aiff", SF_FORMAT_AIFF | SF_FORMAT_PCM_24) ; + vio_test ("vio_float.au", SF_FORMAT_AU | SF_FORMAT_FLOAT) ; + + return 0 ; +} /* main */ + +/*============================================================================== +*/ + +typedef struct +{ sf_count_t offset, length ; + unsigned char data [16 * 1024] ; +} VIO_DATA ; + +static sf_count_t +vfget_filelen (void *user_data) +{ VIO_DATA *vf = (VIO_DATA *) user_data ; + + return vf->length ; +} /* vfget_filelen */ + +static sf_count_t +vfseek (sf_count_t offset, int whence, void *user_data) +{ VIO_DATA *vf = (VIO_DATA *) user_data ; + + switch (whence) + { case SEEK_SET : + vf->offset = offset ; + break ; + + case SEEK_CUR : + vf->offset = vf->offset + offset ; + break ; + + case SEEK_END : + vf->offset = vf->length + offset ; + break ; + default : + break ; + } ; + + return vf->offset ; +} /* vfseek */ + +static sf_count_t +vfread (void *ptr, sf_count_t count, void *user_data) +{ VIO_DATA *vf = (VIO_DATA *) user_data ; + + /* + ** This will brack badly for files over 2Gig in length, but + ** is sufficient for testing. + */ + if (vf->offset + count > vf->length) + count = vf->length - vf->offset ; + + memcpy (ptr, vf->data + vf->offset, count) ; + vf->offset += count ; + + return count ; +} /* vfread */ + +static sf_count_t +vfwrite (const void *ptr, sf_count_t count, void *user_data) +{ VIO_DATA *vf = (VIO_DATA *) user_data ; + + /* + ** This will break badly for files over 2Gig in length, but + ** is sufficient for testing. + */ + if (vf->offset >= SIGNED_SIZEOF (vf->data)) + return 0 ; + + if (vf->offset + count > SIGNED_SIZEOF (vf->data)) + count = sizeof (vf->data) - vf->offset ; + + memcpy (vf->data + vf->offset, ptr, (size_t) count) ; + vf->offset += count ; + + if (vf->offset > vf->length) + vf->length = vf->offset ; + + return count ; +} /* vfwrite */ + +static sf_count_t +vftell (void *user_data) +{ VIO_DATA *vf = (VIO_DATA *) user_data ; + + return vf->offset ; +} /* vftell */ + + +/*============================================================================== +*/ + +static void +gen_short_data (short * data, int len, int start) +{ int k ; + + for (k = 0 ; k < len ; k++) + data [k] = start + k ; +} /* gen_short_data */ + + +static void +check_short_data (short * data, int len, int start, int line) +{ int k ; + + for (k = 0 ; k < len ; k++) + if (data [k] != start + k) + { printf ("\n\nLine %d : data [%d] = %d (should be %d).\n\n", line, k, data [k], start + k) ; + exit (1) ; + } ; +} /* gen_short_data */ + +/*------------------------------------------------------------------------------ +*/ + +static void +vio_test (const char *fname, int format) +{ static VIO_DATA vio_data ; + static short data [256] ; + + SF_VIRTUAL_IO vio ; + SNDFILE * file ; + SF_INFO sfinfo ; + + print_test_name ("virtual i/o test", fname) ; + + /* Set up pointers to the locally defined functions. */ + vio.get_filelen = vfget_filelen ; + vio.seek = vfseek ; + vio.read = vfread ; + vio.write = vfwrite ; + vio.tell = vftell ; + + /* Set virtual file offset and length to zero. */ + vio_data.offset = 0 ; + vio_data.length = 0 ; + + memset (&sfinfo, 0, sizeof (sfinfo)) ; + sfinfo.format = format ; + sfinfo.channels = 2 ; + sfinfo.samplerate = 44100 ; + + if ((file = sf_open_virtual (&vio, SFM_WRITE, &sfinfo, &vio_data)) == NULL) + { printf ("\n\nLine %d : sf_open_write failed with error : ", __LINE__) ; + fflush (stdout) ; + puts (sf_strerror (NULL)) ; + exit (1) ; + } ; + + gen_short_data (data, ARRAY_LEN (data), 0) ; + sf_write_short (file, data, ARRAY_LEN (data)) ; + + gen_short_data (data, ARRAY_LEN (data), 1) ; + sf_write_short (file, data, ARRAY_LEN (data)) ; + + gen_short_data (data, ARRAY_LEN (data), 2) ; + sf_write_short (file, data, ARRAY_LEN (data)) ; + + sf_close (file) ; + + /* Now test read. */ + memset (&sfinfo, 0, sizeof (sfinfo)) ; + + vio_data.offset = 0 ; + + if ((file = sf_open_virtual (&vio, SFM_READ, &sfinfo, &vio_data)) == NULL) + { printf ("\n\nLine %d : sf_open_write failed with error : ", __LINE__) ; + fflush (stdout) ; + puts (sf_strerror (NULL)) ; + + dump_data_to_file (fname, vio_data.data, vio_data.length) ; + exit (1) ; + } ; + + + sf_read_short (file, data, ARRAY_LEN (data)) ; + check_short_data (data, ARRAY_LEN (data), 0, __LINE__) ; + + sf_read_short (file, data, ARRAY_LEN (data)) ; + check_short_data (data, ARRAY_LEN (data), 1, __LINE__) ; + + sf_read_short (file, data, ARRAY_LEN (data)) ; + check_short_data (data, ARRAY_LEN (data), 2, __LINE__) ; + + sf_close (file) ; + + puts ("ok") ; +} /* vio_test */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 5569fa52-15dd-496f-ab47-1fd42d76195d +*/ diff --git a/tests/win32_ordinal_test.c b/tests/win32_ordinal_test.c new file mode 100644 index 00000000..9719dbf0 --- /dev/null +++ b/tests/win32_ordinal_test.c @@ -0,0 +1,146 @@ +/* +** Copyright (C) 2006, 2007 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" +#include "sndfile.h" + +#include +#include + +#if HAVE_UNISTD_H +#include +#endif + +#if (HAVE_DECL_S_IRGRP == 0) +#include +#endif + +#include +#include +#include +#include + +#include "utils.h" + +#if (defined (WIN32) || defined (_WIN32) || defined (__CYGWIN__)) +#define TEST_WIN32 1 +#else +#define TEST_WIN32 0 +#endif + +#if TEST_WIN32 +#include + +#ifdef __CYGWIN__ +#define DLL_NAME "cygsndfile" +#else +#define DLL_NAME "libsndfile" +#endif + +static const char * locations [] = +{ "../src/", "src/", "../src/.libs/", "src/.libs/", + NULL +} ; /* locations. */ + +static int +test_ordinal (HMODULE hmod, const char * func_name, int ordinal) +{ char *lpmsg ; + void *name, *ord ; + + print_test_name ("win32_ordinal_test", func_name) ; + + ord = GetProcAddress (hmod, (LPSTR) ordinal) ; + if ((name = GetProcAddress (hmod, func_name)) == NULL) + { FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError (), + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpmsg, 0, NULL) ; + /*-puts (lpmsg) ;-*/ + } ; + + if (name != NULL && ord != NULL && name == ord) + { puts ("ok") ; + return 0 ; + } ; + + puts ("fail") ; + return 1 ; +} /* test_ordinal */ + +static void +win32_ordinal_test (void) +{ static char buffer [1024] ; + static char func_name [1024] ; + HMODULE hmod = NULL ; + FILE * file = NULL ; + int k, ordinal, errors = 0 ; + + for (k = 0 ; locations [k] != NULL ; k++) + { snprintf (buffer, sizeof (buffer), "%s/%s.def", locations [k], DLL_NAME) ; + if ((file = fopen (buffer, "r")) != NULL) + break ; + } ; + + if (file == NULL) + { puts ("\n\nError : cannot open DEF file.\n") ; + exit (1) ; + } ; + + for (k = 0 ; locations [k] != NULL ; k++) + { snprintf (buffer, sizeof (buffer), "%s/%s-1.dll", locations [k], DLL_NAME) ; + if ((hmod = (HMODULE) LoadLibrary (buffer)) != NULL) + break ; + } ; + + if (hmod == NULL) + { puts ("\n\nError : cannot load DLL.\n") ; + exit (1) ; + } ; + + while (fgets (buffer, sizeof (buffer), file) != NULL) + { func_name [0] = 0 ; + ordinal = 0 ; + + if (sscanf (buffer, "%s @%d", func_name, &ordinal) != 2) + continue ; + + errors += test_ordinal (hmod, func_name, ordinal) ; + } ; + + FreeLibrary (hmod) ; + + fclose (file) ; + + if (errors > 0) + { printf ("\n\nErrors : %d\n\n", errors) ; + exit (1) ; + } ; + + return ; +} /* win32_ordinal_test */ + +#endif + +int +main (void) +{ +#if (TEST_WIN32 && WIN32_TARGET_DLL) + win32_ordinal_test () ; +#endif + + return 0 ; +} /* main */ + diff --git a/tests/win32_test.c b/tests/win32_test.c new file mode 100644 index 00000000..ba903f10 --- /dev/null +++ b/tests/win32_test.c @@ -0,0 +1,235 @@ +/* +** Copyright (C) 2001-2004 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" +#include "sndfile.h" + +#include +#include + +#if HAVE_UNISTD_H +#include +#endif + +#if (HAVE_DECL_S_IRGRP == 0) +#include +#endif + +#include +#include +#include +#include +#include + +#define SIGNED_SIZEOF(x) ((int) sizeof (x)) + +#if defined (__CYGWIN__) + + #define LSEEK lseek + #define FSTAT fstat + + typedef struct stat STATBUF ; + typedef off_t INT64 ; + + static char dir_cmd [] = "ls -l" ; + + #define COMPILE_FULL_TEST 1 +#elif (defined (WIN32) || defined (_WIN32)) + + #define LSEEK _lseeki64 + #define FSTAT _fstati64 + + typedef struct _stati64 STATBUF ; + typedef __int64 INT64 ; + + static char dir_cmd [] = "dir" ; + + #define COMPILE_FULL_TEST 1 +#elif defined (linux) + + #define LSEEK lseek + #define FSTAT fstat + + typedef struct stat STATBUF ; + typedef sf_count_t INT64 ; + + #define O_BINARY 0 + static char dir_cmd [] = "ls -l" ; + + #define COMPILE_FULL_TEST 1 +#else + #define COMPILE_FULL_TEST 0 +#endif + +#if COMPILE_FULL_TEST +static void show_fstat_error (void) ; +static void show_lseek_error (void) ; + +int +main (void) +{ puts ("\n\n\n\n" + "This program shows up some errors in the Win32 implementation of\n" + "a couple of POSIX API functions. It can also be compiled on Linux\n" + "(which works correctly) just to provide a sanity check.\n" + ) ; + + show_fstat_error () ; + show_lseek_error () ; + + puts ("\n\n") ; + + return 0 ; +} /* main */ + +static void +show_fstat_error (void) +{ static const char *filename = "fstat.dat" ; + static char data [256] ; + + STATBUF statbuf ; + int fd, mode, flags ; + + if (sizeof (statbuf.st_size) != sizeof (INT64)) + { printf ("\n\nLine %d: Error, sizeof (statbuf.st_size) != 8.\n\n", __LINE__) ; + return ; + } ; + + puts ("\n64 bit fstat() test.\n--------------------") ; + + printf ("0) Create a file, write %d bytes and close it.\n", SIGNED_SIZEOF (data)) ; + mode = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY ; + flags = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ; + if ((fd = open (filename, mode, flags)) < 0) + { printf ("\n\nLine %d: open() failed : %s\n\n", __LINE__, strerror (errno)) ; + return ; + } ; + write (fd, data, sizeof (data)) ; + close (fd) ; + + printf ("1) Re-open file in read/write mode and write another %d bytes at the end.\n", SIGNED_SIZEOF (data)) ; + mode = O_RDWR | O_BINARY ; + flags = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ; + if ((fd = open (filename, mode, flags)) < 0) + { printf ("\n\nLine %d: open() failed : %s\n\n", __LINE__, strerror (errno)) ; + return ; + } ; + LSEEK (fd, 0, SEEK_END) ; + write (fd, data, sizeof (data)) ; + + printf ("2) Now use system (\"%s %s\") to show the file length.\n\n", dir_cmd, filename) ; + sprintf (data, "%s %s", dir_cmd, filename) ; + system (data) ; + puts ("") ; + + printf ("3) Now use fstat() to get the file length.\n") ; + if (FSTAT (fd, &statbuf) != 0) + { printf ("\n\nLine %d: fstat() returned error : %s\n", __LINE__, strerror (errno)) ; + return ; + } ; + + printf ("4) According to fstat(), the file length is %ld, ", (long) statbuf.st_size) ; + + close (fd) ; + + if (statbuf.st_size != 2 * sizeof (data)) + printf ("but thats just plain ***WRONG***.\n\n") ; + else + { printf ("which is correct.\n\n") ; + unlink (filename) ; + } ; + +} /* show_fstat_error */ + +static void +show_lseek_error (void) +{ static const char *filename = "fstat.dat" ; + static char data [256] ; + + INT64 retval ; + int fd, mode, flags ; + + puts ("\n64 bit lseek() test.\n--------------------") ; + + printf ("0) Create a file, write %d bytes and close it.\n", SIGNED_SIZEOF (data)) ; + mode = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY ; + flags = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ; + if ((fd = open (filename, mode, flags)) < 0) + { printf ("\n\nLine %d: open() failed : %s\n\n", __LINE__, strerror (errno)) ; + return ; + } ; + write (fd, data, sizeof (data)) ; + close (fd) ; + + printf ("1) Re-open file in read/write mode and write another %d bytes at the end.\n", SIGNED_SIZEOF (data)) ; + mode = O_RDWR | O_BINARY ; + flags = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ; + if ((fd = open (filename, mode, flags)) < 0) + { printf ("\n\nLine %d: open() failed : %s\n\n", __LINE__, strerror (errno)) ; + return ; + } ; + + LSEEK (fd, 0, SEEK_END) ; + write (fd, data, sizeof (data)) ; + + printf ("2) Now use system (\"%s %s\") to show the file length.\n\n", dir_cmd, filename) ; + sprintf (data, "%s %s", dir_cmd, filename) ; + system (data) ; + puts ("") ; + + printf ("3) Now use lseek() to go to the end of the file.\n") ; + retval = LSEEK (fd, 0, SEEK_END) ; + + printf ("4) We are now at position %ld, ", (long) retval) ; + + close (fd) ; + + if (retval != 2 * sizeof (data)) + printf ("but thats just plain ***WRONG***.\n\n") ; + else + { printf ("which is correct.\n\n") ; + unlink (filename) ; + } ; + +} /* show_lseek_error */ + +#else + +int +main (void) +{ puts ("\n" + "This program shows up some errors in the Win32 implementation of\n" + "a couple of POSIX API functions. It can also be compiled on Linux\n" + "and under Cygwin32 (which both work correctly) just to provide a \n" + "sanity check.\n" + "Unfortunately, it does not compile on this platform." + ) ; + + return 0 ; +} /* main */ + +#endif + + + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 228b9a18-0555-46d9-b9e6-2b37ce048702 +*/ diff --git a/tests/write_read_test.def b/tests/write_read_test.def new file mode 100644 index 00000000..524e86a9 --- /dev/null +++ b/tests/write_read_test.def @@ -0,0 +1,73 @@ +autogen definitions write_read_test.tpl; + +data_type = { + type_name = char ; + data_type = short ; + data_field = s ; + error_func = CHAR_ERROR ; + format_char = "0x%X" ; + max_val = "32000.0" ; + max_error = "255" ; + } ; + +data_type = { + type_name = short ; + data_type = short ; + data_field = s ; + error_func = INT_ERROR ; + format_char = "0x%X" ; + max_val = "32000.0" ; + max_error = "0" ; + } ; + +data_type = { + type_name = "24bit" ; + data_type = int ; + data_field = i ; + error_func = TRIBYTE_ERROR ; + format_char = "0x%X" ; + max_val = "(1.0 * 0x7F000000)" ; + max_error = "256" ; + } ; + +data_type = { + type_name = int ; + data_type = int ; + data_field = i ; + error_func = INT_ERROR ; + format_char = "0x%X" ; + max_val = "(1.0 * 0x7F000000)" ; + max_error = "0" ; + } ; + +/* Lite remove start */ + +data_type = { + type_name = float ; + data_type = float ; + data_field = f ; + error_func = FLOAT_ERROR ; + format_char = "%g" ; + max_val = "1.0" ; + max_error = "0" ; + } ; + +data_type = { + type_name = double ; + data_type = double ; + data_field = d ; + error_func = FLOAT_ERROR ; + format_char = "%g" ; + max_val = "1.0" ; + max_error = "0" ; + } ; + +/* Lite remove end */ +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 624d498a-9ea1-46b5-a386-c1c2261cfce3 +*/ + diff --git a/tests/write_read_test.tpl b/tests/write_read_test.tpl new file mode 100644 index 00000000..0100b3b8 --- /dev/null +++ b/tests/write_read_test.tpl @@ -0,0 +1,1026 @@ +[+ AutoGen5 template c +] +/* +** Copyright (C) 1999-2005 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "sfconfig.h" + +#include +#include +#include + +#if HAVE_UNISTD_H +#include +#endif + +#include + +#if (defined (WIN32) || defined (_WIN32)) +#include +static int truncate (const char *filename, int ignored) ; +#endif + +#include + +#include "utils.h" + +#define SAMPLE_RATE 11025 +#define DATA_LENGTH (1<<12) + +#define SILLY_WRITE_COUNT (234) + +[+ FOR data_type ++]static void pcm_test_[+ (get "type_name") +] (const char *str, int format, int long_file_okz) ; +[+ ENDFOR data_type ++] +static void empty_file_test (const char *filename, int format) ; + +typedef union +{ double d [DATA_LENGTH] ; + float f [DATA_LENGTH] ; + int i [DATA_LENGTH] ; + short s [DATA_LENGTH] ; + char c [DATA_LENGTH] ; +} BUFFER ; + +static BUFFER orig_data ; +static BUFFER test_data ; + +int +main (int argc, char **argv) +{ int do_all = 0 ; + int test_count = 0 ; + + count_open_files () ; + + if (argc != 2) + { printf ("Usage : %s \n", argv [0]) ; + printf (" Where is one of the following:\n") ; + printf (" wav - test WAV file functions (little endian)\n") ; + printf (" aiff - test AIFF file functions (big endian)\n") ; + printf (" au - test AU file functions\n") ; + printf (" avr - test AVR file functions\n") ; + printf (" caf - test CAF file functions\n") ; + printf (" raw - test RAW header-less PCM file functions\n") ; + printf (" paf - test PAF file functions\n") ; + printf (" svx - test 8SVX/16SV file functions\n") ; + printf (" nist - test NIST Sphere file functions\n") ; + printf (" ircam - test IRCAM file functions\n") ; + printf (" voc - Create Voice file functions\n") ; + printf (" w64 - Sonic Foundry's W64 file functions\n") ; + printf (" flac - test FLAC file functions\n") ; + printf (" all - perform all tests\n") ; + exit (1) ; + } ; + + do_all = !strcmp (argv [1], "all") ; + + if (do_all || ! strcmp (argv [1], "wav")) + { pcm_test_char ("char.wav" , SF_FORMAT_WAV | SF_FORMAT_PCM_U8, SF_FALSE) ; + pcm_test_short ("short.wav" , SF_FORMAT_WAV | SF_FORMAT_PCM_16, SF_FALSE) ; + pcm_test_24bit ("24bit.wav" , SF_FORMAT_WAV | SF_FORMAT_PCM_24, SF_FALSE) ; + pcm_test_int ("int.wav" , SF_FORMAT_WAV | SF_FORMAT_PCM_32, SF_FALSE) ; + + pcm_test_char ("char.rifx" , SF_ENDIAN_BIG | SF_FORMAT_WAV | SF_FORMAT_PCM_U8, SF_FALSE) ; + pcm_test_short ("short.rifx" , SF_ENDIAN_BIG | SF_FORMAT_WAV | SF_FORMAT_PCM_16, SF_FALSE) ; + pcm_test_24bit ("24bit.rifx" , SF_ENDIAN_BIG | SF_FORMAT_WAV | SF_FORMAT_PCM_24, SF_FALSE) ; + pcm_test_int ("int.rifx" , SF_ENDIAN_BIG | SF_FORMAT_WAV | SF_FORMAT_PCM_32, SF_FALSE) ; + + pcm_test_24bit ("24bit.wavex" , SF_FORMAT_WAVEX | SF_FORMAT_PCM_24, SF_FALSE) ; + pcm_test_int ("int.wavex" , SF_FORMAT_WAVEX | SF_FORMAT_PCM_32, SF_FALSE) ; + + /* Lite remove start */ + pcm_test_float ("float.wav" , SF_FORMAT_WAV | SF_FORMAT_FLOAT , SF_FALSE) ; + pcm_test_double ("double.wav" , SF_FORMAT_WAV | SF_FORMAT_DOUBLE, SF_FALSE) ; + + pcm_test_float ("float.rifx" , SF_ENDIAN_BIG | SF_FORMAT_WAV | SF_FORMAT_FLOAT , SF_FALSE) ; + pcm_test_double ("double.rifx" , SF_ENDIAN_BIG | SF_FORMAT_WAV | SF_FORMAT_DOUBLE, SF_FALSE) ; + + pcm_test_float ("float.wavex" , SF_FORMAT_WAVEX | SF_FORMAT_FLOAT , SF_FALSE) ; + pcm_test_double ("double.wavex" , SF_FORMAT_WAVEX | SF_FORMAT_DOUBLE, SF_FALSE) ; + /* Lite remove end */ + + empty_file_test ("empty_char.wav", SF_FORMAT_WAV | SF_FORMAT_PCM_U8) ; + empty_file_test ("empty_short.wav", SF_FORMAT_WAV | SF_FORMAT_PCM_16) ; + empty_file_test ("empty_float.wav", SF_FORMAT_WAV | SF_FORMAT_FLOAT) ; + + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "aiff")) + { pcm_test_char ("char_u8.aiff" , SF_FORMAT_AIFF | SF_FORMAT_PCM_U8, SF_FALSE) ; + pcm_test_char ("char_s8.aiff" , SF_FORMAT_AIFF | SF_FORMAT_PCM_S8, SF_FALSE) ; + pcm_test_short ("short.aiff" , SF_FORMAT_AIFF | SF_FORMAT_PCM_16, SF_FALSE) ; + pcm_test_24bit ("24bit.aiff" , SF_FORMAT_AIFF | SF_FORMAT_PCM_24, SF_FALSE) ; + pcm_test_int ("int.aiff" , SF_FORMAT_AIFF | SF_FORMAT_PCM_32, SF_FALSE) ; + + pcm_test_short ("short_sowt.aifc" , SF_ENDIAN_LITTLE | SF_FORMAT_AIFF | SF_FORMAT_PCM_16, SF_FALSE) ; + pcm_test_24bit ("24bit_sowt.aifc" , SF_ENDIAN_LITTLE | SF_FORMAT_AIFF | SF_FORMAT_PCM_24, SF_FALSE) ; + pcm_test_int ("int_sowt.aifc" , SF_ENDIAN_LITTLE | SF_FORMAT_AIFF | SF_FORMAT_PCM_32, SF_FALSE) ; + + pcm_test_short ("short_twos.aifc" , SF_ENDIAN_BIG | SF_FORMAT_AIFF | SF_FORMAT_PCM_16, SF_FALSE) ; + pcm_test_24bit ("24bit_twos.aifc" , SF_ENDIAN_BIG | SF_FORMAT_AIFF | SF_FORMAT_PCM_24, SF_FALSE) ; + pcm_test_int ("int_twos.aifc" , SF_ENDIAN_BIG | SF_FORMAT_AIFF | SF_FORMAT_PCM_32, SF_FALSE) ; + + /* Lite remove start */ + pcm_test_short ("dwvw16.aifc", SF_FORMAT_AIFF | SF_FORMAT_DWVW_16, SF_TRUE) ; + pcm_test_24bit ("dwvw24.aifc", SF_FORMAT_AIFF | SF_FORMAT_DWVW_24, SF_TRUE) ; + + pcm_test_float ("float.aifc" , SF_FORMAT_AIFF | SF_FORMAT_FLOAT , SF_FALSE) ; + pcm_test_double ("double.aifc" , SF_FORMAT_AIFF | SF_FORMAT_DOUBLE, SF_FALSE) ; + /* Lite remove end */ + + empty_file_test ("empty_char.aiff", SF_FORMAT_AIFF | SF_FORMAT_PCM_U8) ; + empty_file_test ("empty_short.aiff", SF_FORMAT_AIFF | SF_FORMAT_PCM_16) ; + empty_file_test ("empty_float.aiff", SF_FORMAT_AIFF | SF_FORMAT_FLOAT) ; + + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "au")) + { pcm_test_char ("char.au" , SF_FORMAT_AU | SF_FORMAT_PCM_S8, SF_FALSE) ; + pcm_test_short ("short.au" , SF_FORMAT_AU | SF_FORMAT_PCM_16, SF_FALSE) ; + pcm_test_24bit ("24bit.au" , SF_FORMAT_AU | SF_FORMAT_PCM_24, SF_FALSE) ; + pcm_test_int ("int.au" , SF_FORMAT_AU | SF_FORMAT_PCM_32, SF_FALSE) ; + /* Lite remove start */ + pcm_test_float ("float.au" , SF_FORMAT_AU | SF_FORMAT_FLOAT , SF_FALSE) ; + pcm_test_double ("double.au", SF_FORMAT_AU | SF_FORMAT_DOUBLE, SF_FALSE) ; + /* Lite remove end */ + + pcm_test_char ("char_le.au" , SF_ENDIAN_LITTLE | SF_FORMAT_AU | SF_FORMAT_PCM_S8, SF_FALSE) ; + pcm_test_short ("short_le.au" , SF_ENDIAN_LITTLE | SF_FORMAT_AU | SF_FORMAT_PCM_16, SF_FALSE) ; + pcm_test_24bit ("24bit_le.au" , SF_ENDIAN_LITTLE | SF_FORMAT_AU | SF_FORMAT_PCM_24, SF_FALSE) ; + pcm_test_int ("int_le.au" , SF_ENDIAN_LITTLE | SF_FORMAT_AU | SF_FORMAT_PCM_32, SF_FALSE) ; + /* Lite remove start */ + pcm_test_float ("float_le.au" , SF_ENDIAN_LITTLE | SF_FORMAT_AU | SF_FORMAT_FLOAT , SF_FALSE) ; + pcm_test_double ("double_le.au" , SF_ENDIAN_LITTLE | SF_FORMAT_AU | SF_FORMAT_DOUBLE, SF_FALSE) ; + /* Lite remove end */ + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "caf")) + { pcm_test_char ("char.caf" , SF_FORMAT_CAF | SF_FORMAT_PCM_S8, SF_FALSE) ; + pcm_test_short ("short.caf" , SF_FORMAT_CAF | SF_FORMAT_PCM_16, SF_FALSE) ; + pcm_test_24bit ("24bit.caf" , SF_FORMAT_CAF | SF_FORMAT_PCM_24, SF_FALSE) ; + pcm_test_int ("int.caf" , SF_FORMAT_CAF | SF_FORMAT_PCM_32, SF_FALSE) ; + /* Lite remove start */ + pcm_test_float ("float.caf" , SF_FORMAT_CAF | SF_FORMAT_FLOAT , SF_FALSE) ; + pcm_test_double ("double.caf" , SF_FORMAT_CAF | SF_FORMAT_DOUBLE, SF_FALSE) ; + /* Lite remove end */ + + pcm_test_short ("short_le.caf" , SF_ENDIAN_LITTLE | SF_FORMAT_CAF | SF_FORMAT_PCM_16, SF_FALSE) ; + pcm_test_24bit ("24bit_le.caf" , SF_ENDIAN_LITTLE | SF_FORMAT_CAF | SF_FORMAT_PCM_24, SF_FALSE) ; + pcm_test_int ("int_le.caf" , SF_ENDIAN_LITTLE | SF_FORMAT_CAF | SF_FORMAT_PCM_32, SF_FALSE) ; + /* Lite remove start */ + pcm_test_float ("float_le.caf" , SF_ENDIAN_LITTLE | SF_FORMAT_CAF | SF_FORMAT_FLOAT , SF_FALSE) ; + pcm_test_double ("double_le.caf", SF_ENDIAN_LITTLE | SF_FORMAT_CAF | SF_FORMAT_DOUBLE, SF_FALSE) ; + /* Lite remove end */ + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "raw")) + { pcm_test_char ("char_s8.raw" , SF_FORMAT_RAW | SF_FORMAT_PCM_S8, SF_FALSE) ; + pcm_test_char ("char_u8.raw" , SF_FORMAT_RAW | SF_FORMAT_PCM_U8, SF_FALSE) ; + + pcm_test_short ("short_le.raw" , SF_ENDIAN_LITTLE | SF_FORMAT_RAW | SF_FORMAT_PCM_16, SF_FALSE) ; + pcm_test_short ("short_be.raw" , SF_ENDIAN_BIG | SF_FORMAT_RAW | SF_FORMAT_PCM_16, SF_FALSE) ; + pcm_test_24bit ("24bit_le.raw" , SF_ENDIAN_LITTLE | SF_FORMAT_RAW | SF_FORMAT_PCM_24, SF_FALSE) ; + pcm_test_24bit ("24bit_be.raw" , SF_ENDIAN_BIG | SF_FORMAT_RAW | SF_FORMAT_PCM_24, SF_FALSE) ; + pcm_test_int ("int_le.raw" , SF_ENDIAN_LITTLE | SF_FORMAT_RAW | SF_FORMAT_PCM_32, SF_FALSE) ; + pcm_test_int ("int_be.raw" , SF_ENDIAN_BIG | SF_FORMAT_RAW | SF_FORMAT_PCM_32, SF_FALSE) ; + + /* Lite remove start */ + pcm_test_float ("float_le.raw" , SF_ENDIAN_LITTLE | SF_FORMAT_RAW | SF_FORMAT_FLOAT , SF_FALSE) ; + pcm_test_float ("float_be.raw" , SF_ENDIAN_BIG | SF_FORMAT_RAW | SF_FORMAT_FLOAT , SF_FALSE) ; + + pcm_test_double ("double_le.raw", SF_ENDIAN_LITTLE | SF_FORMAT_RAW | SF_FORMAT_DOUBLE, SF_FALSE) ; + pcm_test_double ("double_be.raw", SF_ENDIAN_BIG | SF_FORMAT_RAW | SF_FORMAT_DOUBLE, SF_FALSE) ; + /* Lite remove end */ + test_count++ ; + } ; + + /* Lite remove start */ + if (do_all || ! strcmp (argv [1], "paf")) + { pcm_test_char ("char_le.paf", SF_ENDIAN_LITTLE | SF_FORMAT_PAF | SF_FORMAT_PCM_S8, SF_FALSE) ; + pcm_test_char ("char_be.paf", SF_ENDIAN_BIG | SF_FORMAT_PAF | SF_FORMAT_PCM_S8, SF_FALSE) ; + pcm_test_short ("short_le.paf", SF_ENDIAN_LITTLE | SF_FORMAT_PAF | SF_FORMAT_PCM_16, SF_FALSE) ; + pcm_test_short ("short_be.paf", SF_ENDIAN_BIG | SF_FORMAT_PAF | SF_FORMAT_PCM_16, SF_FALSE) ; + pcm_test_24bit ("24bit_le.paf", SF_ENDIAN_LITTLE | SF_FORMAT_PAF | SF_FORMAT_PCM_24, SF_TRUE) ; + pcm_test_24bit ("24bit_be.paf", SF_ENDIAN_BIG | SF_FORMAT_PAF | SF_FORMAT_PCM_24, SF_TRUE) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "svx")) + { pcm_test_char ("char.svx" , SF_FORMAT_SVX | SF_FORMAT_PCM_S8, SF_FALSE) ; + pcm_test_short ("short.svx", SF_FORMAT_SVX | SF_FORMAT_PCM_16, SF_FALSE) ; + + empty_file_test ("empty_char.svx", SF_FORMAT_SVX | SF_FORMAT_PCM_S8) ; + empty_file_test ("empty_short.svx", SF_FORMAT_SVX | SF_FORMAT_PCM_16) ; + + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "nist")) + { pcm_test_short ("short_le.nist", SF_ENDIAN_LITTLE | SF_FORMAT_NIST | SF_FORMAT_PCM_16, SF_FALSE) ; + pcm_test_short ("short_be.nist", SF_ENDIAN_BIG | SF_FORMAT_NIST | SF_FORMAT_PCM_16, SF_FALSE) ; + pcm_test_24bit ("24bit_le.nist", SF_ENDIAN_LITTLE | SF_FORMAT_NIST | SF_FORMAT_PCM_24, SF_FALSE) ; + pcm_test_24bit ("24bit_be.nist", SF_ENDIAN_BIG | SF_FORMAT_NIST | SF_FORMAT_PCM_24, SF_FALSE) ; + pcm_test_int ("int_le.nist" , SF_ENDIAN_LITTLE | SF_FORMAT_NIST | SF_FORMAT_PCM_32, SF_FALSE) ; + pcm_test_int ("int_be.nist" , SF_ENDIAN_BIG | SF_FORMAT_NIST | SF_FORMAT_PCM_32, SF_FALSE) ; + + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "ircam")) + { pcm_test_short ("short_be.ircam" , SF_ENDIAN_BIG | SF_FORMAT_IRCAM | SF_FORMAT_PCM_16, SF_FALSE) ; + pcm_test_short ("short_le.ircam" , SF_ENDIAN_LITTLE | SF_FORMAT_IRCAM | SF_FORMAT_PCM_16, SF_FALSE) ; + pcm_test_int ("int_be.ircam" , SF_ENDIAN_BIG | SF_FORMAT_IRCAM | SF_FORMAT_PCM_32, SF_FALSE) ; + pcm_test_int ("int_le.ircam" , SF_ENDIAN_LITTLE | SF_FORMAT_IRCAM | SF_FORMAT_PCM_32, SF_FALSE) ; + pcm_test_float ("float_be.ircam" , SF_ENDIAN_BIG | SF_FORMAT_IRCAM | SF_FORMAT_FLOAT , SF_FALSE) ; + pcm_test_float ("float_le.ircam" , SF_ENDIAN_LITTLE | SF_FORMAT_IRCAM | SF_FORMAT_FLOAT , SF_FALSE) ; + + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "voc")) + { pcm_test_char ("char.voc" , SF_FORMAT_VOC | SF_FORMAT_PCM_U8, SF_FALSE) ; + pcm_test_short ("short.voc", SF_FORMAT_VOC | SF_FORMAT_PCM_16, SF_FALSE) ; + + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "mat4")) + { pcm_test_short ("short_be.mat4" , SF_ENDIAN_BIG | SF_FORMAT_MAT4 | SF_FORMAT_PCM_16, SF_FALSE) ; + pcm_test_short ("short_le.mat4" , SF_ENDIAN_LITTLE | SF_FORMAT_MAT4 | SF_FORMAT_PCM_16, SF_FALSE) ; + pcm_test_int ("int_be.mat4" , SF_ENDIAN_BIG | SF_FORMAT_MAT4 | SF_FORMAT_PCM_32, SF_FALSE) ; + pcm_test_int ("int_le.mat4" , SF_ENDIAN_LITTLE | SF_FORMAT_MAT4 | SF_FORMAT_PCM_32, SF_FALSE) ; + pcm_test_float ("float_be.mat4" , SF_ENDIAN_BIG | SF_FORMAT_MAT4 | SF_FORMAT_FLOAT , SF_FALSE) ; + pcm_test_float ("float_le.mat4" , SF_ENDIAN_LITTLE | SF_FORMAT_MAT4 | SF_FORMAT_FLOAT , SF_FALSE) ; + pcm_test_double ("double_be.mat4" , SF_ENDIAN_BIG | SF_FORMAT_MAT4 | SF_FORMAT_DOUBLE, SF_FALSE) ; + pcm_test_double ("double_le.mat4" , SF_ENDIAN_LITTLE | SF_FORMAT_MAT4 | SF_FORMAT_DOUBLE, SF_FALSE) ; + + empty_file_test ("empty_short.mat4", SF_FORMAT_MAT4 | SF_FORMAT_PCM_16) ; + empty_file_test ("empty_float.mat4", SF_FORMAT_MAT4 | SF_FORMAT_FLOAT) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "mat5")) + { pcm_test_char ("char_be.mat5" , SF_ENDIAN_BIG | SF_FORMAT_MAT5 | SF_FORMAT_PCM_U8, SF_FALSE) ; + pcm_test_char ("char_le.mat5" , SF_ENDIAN_LITTLE | SF_FORMAT_MAT5 | SF_FORMAT_PCM_U8, SF_FALSE) ; + pcm_test_short ("short_be.mat5" , SF_ENDIAN_BIG | SF_FORMAT_MAT5 | SF_FORMAT_PCM_16, SF_FALSE) ; + pcm_test_short ("short_le.mat5" , SF_ENDIAN_LITTLE | SF_FORMAT_MAT5 | SF_FORMAT_PCM_16, SF_FALSE) ; + pcm_test_int ("int_be.mat5" , SF_ENDIAN_BIG | SF_FORMAT_MAT5 | SF_FORMAT_PCM_32, SF_FALSE) ; + pcm_test_int ("int_le.mat5" , SF_ENDIAN_LITTLE | SF_FORMAT_MAT5 | SF_FORMAT_PCM_32, SF_FALSE) ; + pcm_test_float ("float_be.mat5" , SF_ENDIAN_BIG | SF_FORMAT_MAT5 | SF_FORMAT_FLOAT , SF_FALSE) ; + pcm_test_float ("float_le.mat5" , SF_ENDIAN_LITTLE | SF_FORMAT_MAT5 | SF_FORMAT_FLOAT , SF_FALSE) ; + pcm_test_double ("double_be.mat5" , SF_ENDIAN_BIG | SF_FORMAT_MAT5 | SF_FORMAT_DOUBLE, SF_FALSE) ; + pcm_test_double ("double_le.mat5" , SF_ENDIAN_LITTLE | SF_FORMAT_MAT5 | SF_FORMAT_DOUBLE, SF_FALSE) ; + + increment_open_file_count () ; + + empty_file_test ("empty_char.mat5", SF_FORMAT_MAT5 | SF_FORMAT_PCM_U8) ; + empty_file_test ("empty_short.mat5", SF_FORMAT_MAT5 | SF_FORMAT_PCM_16) ; + empty_file_test ("empty_float.mat5", SF_FORMAT_MAT5 | SF_FORMAT_FLOAT) ; + + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "pvf")) + { pcm_test_char ("char.pvf" , SF_FORMAT_PVF | SF_FORMAT_PCM_S8, SF_FALSE) ; + pcm_test_short ("short.pvf", SF_FORMAT_PVF | SF_FORMAT_PCM_16, SF_FALSE) ; + pcm_test_int ("int.pvf" , SF_FORMAT_PVF | SF_FORMAT_PCM_32, SF_FALSE) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "htk")) + { pcm_test_short ("short.htk", SF_FORMAT_HTK | SF_FORMAT_PCM_16, SF_FALSE) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "avr")) + { pcm_test_char ("char_u8.avr" , SF_FORMAT_AVR | SF_FORMAT_PCM_U8, SF_FALSE) ; + pcm_test_char ("char_s8.avr" , SF_FORMAT_AVR | SF_FORMAT_PCM_S8, SF_FALSE) ; + pcm_test_short ("short.avr" , SF_FORMAT_AVR | SF_FORMAT_PCM_16, SF_FALSE) ; + test_count++ ; + } ; + /* Lite remove end */ + + if (do_all || ! strcmp (argv [1], "w64")) + { pcm_test_char ("char.w64" , SF_FORMAT_W64 | SF_FORMAT_PCM_U8, SF_FALSE) ; + pcm_test_short ("short.w64" , SF_FORMAT_W64 | SF_FORMAT_PCM_16, SF_FALSE) ; + pcm_test_24bit ("24bit.w64" , SF_FORMAT_W64 | SF_FORMAT_PCM_24, SF_FALSE) ; + pcm_test_int ("int.w64" , SF_FORMAT_W64 | SF_FORMAT_PCM_32, SF_FALSE) ; + /* Lite remove start */ + pcm_test_float ("float.w64" , SF_FORMAT_W64 | SF_FORMAT_FLOAT , SF_FALSE) ; + pcm_test_double ("double.w64" , SF_FORMAT_W64 | SF_FORMAT_DOUBLE, SF_FALSE) ; + /* Lite remove end */ + + empty_file_test ("empty_char.w64", SF_FORMAT_W64 | SF_FORMAT_PCM_U8) ; + empty_file_test ("empty_short.w64", SF_FORMAT_W64 | SF_FORMAT_PCM_16) ; + empty_file_test ("empty_float.w64", SF_FORMAT_W64 | SF_FORMAT_FLOAT) ; + + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "sds")) + { pcm_test_char ("char.sds" , SF_FORMAT_SDS | SF_FORMAT_PCM_S8, SF_TRUE) ; + pcm_test_short ("short.sds" , SF_FORMAT_SDS | SF_FORMAT_PCM_16, SF_TRUE) ; + pcm_test_24bit ("24bit.sds" , SF_FORMAT_SDS | SF_FORMAT_PCM_24, SF_TRUE) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "sd2")) + { pcm_test_char ("char.sd2" , SF_FORMAT_SD2 | SF_FORMAT_PCM_S8, SF_TRUE) ; + pcm_test_short ("short.sd2" , SF_FORMAT_SD2 | SF_FORMAT_PCM_16, SF_TRUE) ; + pcm_test_24bit ("24bit.sd2" , SF_FORMAT_SD2 | SF_FORMAT_PCM_24, SF_TRUE) ; + test_count++ ; + } ; + + if (do_all || ! strcmp (argv [1], "flac")) + { pcm_test_char ("char.flac" , SF_FORMAT_FLAC | SF_FORMAT_PCM_S8, SF_TRUE) ; + pcm_test_short ("short.flac" , SF_FORMAT_FLAC | SF_FORMAT_PCM_16, SF_TRUE) ; + pcm_test_24bit ("24bit.flac" , SF_FORMAT_FLAC | SF_FORMAT_PCM_24, SF_TRUE) ; + test_count++ ; + } ; + + if (test_count == 0) + { printf ("Mono : ************************************\n") ; + printf ("Mono : * No '%s' test defined.\n", argv [1]) ; + printf ("Mono : ************************************\n") ; + return 1 ; + } ; + + /* Only open file descriptors should be stdin, stdout and stderr. */ + check_open_file_count_or_die (__LINE__) ; + + return 0 ; +} /* main */ + +/*============================================================================================ +** Helper functions and macros. +*/ + +static void create_short_file (const char *filename) ; + +#define CHAR_ERROR(x,y) (abs ((x) - (y)) > 255) +#define INT_ERROR(x,y) (((x) - (y)) != 0) +#define TRIBYTE_ERROR(x,y) (abs ((x) - (y)) > 255) +#define FLOAT_ERROR(x,y) (fabs ((x) - (y)) > 1e-5) + +#define CONVERT_DATA(k,len,new,orig) \ + { for ((k) = 0 ; (k) < (len) ; (k) ++) \ + (new) [k] = (orig) [k] ; \ + } + +[+ FOR data_type ++] +/*====================================================================================== +*/ + +static void mono_[+ (get "type_name") +]_test (const char *filename, int format, int long_file_ok, int allow_fd) ; +static void stereo_[+ (get "type_name") +]_test (const char *filename, int format, int long_file_ok, int allow_fd) ; +static void mono_rdwr_[+ (get "type_name") +]_test (const char *filename, int format, int long_file_ok, int allow_fd) ; +static void new_rdwr_[+ (get "type_name") +]_test (const char *filename, int format, int allow_fd) ; + +static void +pcm_test_[+ (get "type_name") +] (const char *filename, int format, int long_file_ok) +{ SF_INFO sfinfo ; + [+ (get "data_type") +] *orig, *test ; + int k, items, allow_fd ; + + /* Sd2 files cannot be opened from an existing file descriptor. */ + allow_fd = ((format & SF_FORMAT_TYPEMASK) == SF_FORMAT_SD2) ? SF_FALSE : SF_TRUE ; + + print_test_name ("pcm_test_[+ (get "type_name") +]", filename) ; + + sfinfo.samplerate = 44100 ; + sfinfo.frames = SILLY_WRITE_COUNT ; /* Wrong length. Library should correct this on sf_close. */ + sfinfo.channels = 1 ; + sfinfo.format = format ; + + gen_windowed_sine_double (orig_data.d, DATA_LENGTH, [+ (get "max_val") +]) ; + + orig = orig_data.[+ (get "data_field") +] ; + test = test_data.[+ (get "data_field") +] ; + + /* Make this a macro so gdb steps over it in one go. */ + CONVERT_DATA (k, DATA_LENGTH, orig, orig_data.d) ; + + items = DATA_LENGTH ; + + /* Some test broken out here. */ + + mono_[+ (get "type_name") +]_test (filename, format, long_file_ok, allow_fd) ; + + /* Sub format DWVW does not allow seeking. */ + if ((format & SF_FORMAT_SUBMASK) == SF_FORMAT_DWVW_16 || + (format & SF_FORMAT_SUBMASK) == SF_FORMAT_DWVW_24) + { unlink (filename) ; + printf ("no seek : ok\n") ; + return ; + } ; + + if ((format & SF_FORMAT_TYPEMASK) != SF_FORMAT_FLAC) + mono_rdwr_[+ (get "type_name") +]_test (filename, format, long_file_ok, allow_fd) ; + + /* If the format doesn't support stereo we're done. */ + sfinfo.channels = 2 ; + if (sf_format_check (&sfinfo) == 0) + { unlink (filename) ; + puts ("no stereo : ok") ; + return ; + } ; + + stereo_[+ (get "type_name") +]_test (filename, format, long_file_ok, allow_fd) ; + + /* New read/write test. Not sure if this is needed yet. */ + + if ((format & SF_FORMAT_TYPEMASK) != SF_FORMAT_PAF && + (format & SF_FORMAT_TYPEMASK) != SF_FORMAT_VOC && + (format & SF_FORMAT_TYPEMASK) != SF_FORMAT_FLAC) + new_rdwr_[+ (get "type_name") +]_test (filename, format, allow_fd) ; + + delete_file (format, filename) ; + + puts ("ok") ; + return ; +} /* pcm_test_[+ (get "type_name") +] */ + +static void +mono_[+ (get "type_name") +]_test (const char *filename, int format, int long_file_ok, int allow_fd) +{ SNDFILE *file ; + SF_INFO sfinfo ; + [+ (get "data_type") +] *orig, *test ; + sf_count_t count ; + int k, items ; + + sfinfo.samplerate = 44100 ; + sfinfo.frames = SILLY_WRITE_COUNT ; /* Wrong length. Library should correct this on sf_close. */ + sfinfo.channels = 1 ; + sfinfo.format = format ; + + orig = orig_data.[+ (get "data_field") +] ; + test = test_data.[+ (get "data_field") +] ; + + items = DATA_LENGTH ; + + file = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, allow_fd, __LINE__) ; + + sf_set_string (file, SF_STR_ARTIST, "Your name here") ; + + test_write_[+ (get "data_type") +]_or_die (file, 0, orig, items, __LINE__) ; + sf_write_sync (file) ; + test_write_[+ (get "data_type") +]_or_die (file, 0, orig, items, __LINE__) ; + sf_write_sync (file) ; + + /* Add non-audio data after the audio. */ + sf_set_string (file, SF_STR_COPYRIGHT, "Copyright (c) 2003") ; + + sf_close (file) ; + + memset (test, 0, items * sizeof ([+ (get "data_type") +])) ; + + if ((format & SF_FORMAT_TYPEMASK) != SF_FORMAT_RAW) + memset (&sfinfo, 0, sizeof (sfinfo)) ; + + file = test_open_file_or_die (filename, SFM_READ, &sfinfo, allow_fd, __LINE__) ; + + if (sfinfo.format != format) + { printf ("\n\nLine %d : Mono : Returned format incorrect (0x%08X => 0x%08X).\n", __LINE__, format, sfinfo.format) ; + exit (1) ; + } ; + + if (sfinfo.frames < 2 * items) + { printf ("\n\nLine %d : Mono : Incorrect number of frames in file (too short). (%ld should be %d)\n", __LINE__, SF_COUNT_TO_LONG (sfinfo.frames), items) ; + exit (1) ; + } ; + + if (! long_file_ok && sfinfo.frames > 2 * items) + { printf ("\n\nLine %d : Mono : Incorrect number of frames in file (too long). (%ld should be %d)\n", __LINE__, SF_COUNT_TO_LONG (sfinfo.frames), items) ; + exit (1) ; + } ; + + if (sfinfo.channels != 1) + { printf ("\n\nLine %d : Mono : Incorrect number of channels in file.\n", __LINE__) ; + exit (1) ; + } ; + + check_log_buffer_or_die (file, __LINE__) ; + + test_read_[+ (get "data_type") +]_or_die (file, 0, test, items, __LINE__) ; + for (k = 0 ; k < items ; k++) + if ([+ (get "error_func") +] (orig [k], test [k])) + { printf ("\n\nLine %d: Mono : Incorrect sample A (#%d : [+ (get "format_char") +] => [+ (get "format_char") +]).\n", __LINE__, k, orig [k], test [k]) ; + exit (1) ; + } ; + + /* Seek to start of file. */ + test_seek_or_die (file, 0, SEEK_SET, 0, sfinfo.channels, __LINE__) ; + + test_read_[+ (get "data_type") +]_or_die (file, 0, test, 4, __LINE__) ; + for (k = 0 ; k < 4 ; k++) + if ([+ (get "error_func") +] (orig [k], test [k])) + { printf ("\n\nLine %d : Mono : Incorrect sample A (#%d : [+ (get "format_char") +] => [+ (get "format_char") +]).\n", __LINE__, k, orig [k], test [k]) ; + exit (1) ; + } ; + + if ((format & SF_FORMAT_SUBMASK) == SF_FORMAT_DWVW_16 || + (format & SF_FORMAT_SUBMASK) == SF_FORMAT_DWVW_24) + { sf_close (file) ; + unlink (filename) ; + printf ("no seek : ") ; + return ; + } ; + + /* Seek to offset from start of file. */ + test_seek_or_die (file, items + 10, SEEK_SET, items + 10, sfinfo.channels, __LINE__) ; + + test_read_[+ (get "data_type") +]_or_die (file, 0, test + 10, 4, __LINE__) ; + for (k = 10 ; k < 14 ; k++) + if ([+ (get "error_func") +] (orig [k], test [k])) + { printf ("\n\nLine %d : Mono : Incorrect sample A (#%d : [+ (get "format_char") +] => [+ (get "format_char") +]).\n", __LINE__, k, test [k], orig [k]) ; + exit (1) ; + } ; + + /* Seek to offset from current position. */ + test_seek_or_die (file, 6, SEEK_CUR, items + 20, sfinfo.channels, __LINE__) ; + + test_read_[+ (get "data_type") +]_or_die (file, 0, test + 20, 4, __LINE__) ; + for (k = 20 ; k < 24 ; k++) + if ([+ (get "error_func") +] (orig [k], test [k])) + { printf ("\n\nLine %d : Mono : Incorrect sample A (#%d : [+ (get "format_char") +] => [+ (get "format_char") +]).\n", __LINE__, k, test [k], orig [k]) ; + exit (1) ; + } ; + + /* Seek to offset from end of file. */ + test_seek_or_die (file, -1 * (sfinfo.frames - 10), SEEK_END, 10, sfinfo.channels, __LINE__) ; + + test_read_[+ (get "data_type") +]_or_die (file, 0, test + 10, 4, __LINE__) ; + for (k = 10 ; k < 14 ; k++) + if ([+ (get "error_func") +] (orig [k], test [k])) + { printf ("\n\nLine %d : Mono : Incorrect sample D (#%d : [+ (get "format_char") +] => [+ (get "format_char") +]).\n", __LINE__, k, test [k], orig [k]) ; + exit (1) ; + } ; + + /* Check read past end of file followed by sf_seek (sndfile, 0, SEEK_CUR). */ + test_seek_or_die (file, 0, SEEK_SET, 0, sfinfo.channels, __LINE__) ; + + count = 0 ; + while (count < sfinfo.frames) + count += sf_read_[+ (get "data_type") +] (file, test, 311) ; + + /* Check that no error has occurred. */ + if (sf_error (file)) + { printf ("\n\nLine %d : Mono : error where there shouldn't have been one.\n", __LINE__) ; + puts (sf_strerror (file)) ; + exit (1) ; + } ; + + /* Check that we haven't read beyond EOF. */ + if (count > sfinfo.frames) + { printf ("\n\nLines %d : read past end of file (%ld should be %ld)\n", __LINE__, (long) count, (long) sfinfo.frames) ; + exit (1) ; + } ; + + test_seek_or_die (file, 0, SEEK_CUR, sfinfo.frames, sfinfo.channels, __LINE__) ; + + sf_close (file) ; + +} /* mono_[+ (get "type_name") +]_test */ + +static void +stereo_[+ (get "type_name") +]_test (const char *filename, int format, int long_file_ok, int allow_fd) +{ SNDFILE *file ; + SF_INFO sfinfo ; + [+ (get "data_type") +] *orig, *test ; + int k, items, frames ; + + sfinfo.samplerate = 44100 ; + sfinfo.frames = SILLY_WRITE_COUNT ; /* Wrong length. Library should correct this on sf_close. */ + sfinfo.channels = 2 ; + sfinfo.format = format ; + + gen_windowed_sine_double (orig_data.d, DATA_LENGTH, [+ (get "max_val") +]) ; + + orig = orig_data.[+ (get "data_field") +] ; + test = test_data.[+ (get "data_field") +] ; + + /* Make this a macro so gdb steps over it in one go. */ + CONVERT_DATA (k, DATA_LENGTH, orig, orig_data.d) ; + + items = DATA_LENGTH ; + frames = items / sfinfo.channels ; + + file = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, allow_fd, __LINE__) ; + + sf_set_string (file, SF_STR_ARTIST, "Your name here") ; + + test_writef_[+ (get "data_type") +]_or_die (file, 0, orig, frames, __LINE__) ; + + sf_set_string (file, SF_STR_COPYRIGHT, "Copyright (c) 2003") ; + + sf_close (file) ; + + memset (test, 0, items * sizeof ([+ (get "data_type") +])) ; + + if ((format & SF_FORMAT_TYPEMASK) != SF_FORMAT_RAW) + memset (&sfinfo, 0, sizeof (sfinfo)) ; + + file = test_open_file_or_die (filename, SFM_READ, &sfinfo, allow_fd, __LINE__) ; + + if (sfinfo.format != format) + { printf ("\n\nLine %d : Stereo : Returned format incorrect (0x%08X => 0x%08X).\n", + __LINE__, format, sfinfo.format) ; + exit (1) ; + } ; + + if (sfinfo.frames < frames) + { printf ("\n\nLine %d : Stereo : Incorrect number of frames in file (too short). (%ld should be %d)\n", + __LINE__, SF_COUNT_TO_LONG (sfinfo.frames), frames) ; + exit (1) ; + } ; + + if (! long_file_ok && sfinfo.frames > frames) + { printf ("\n\nLine %d : Stereo : Incorrect number of frames in file (too long). (%ld should be %d)\n", + __LINE__, SF_COUNT_TO_LONG (sfinfo.frames), frames) ; + exit (1) ; + } ; + + if (sfinfo.channels != 2) + { printf ("\n\nLine %d : Stereo : Incorrect number of channels in file.\n", __LINE__) ; + exit (1) ; + } ; + + check_log_buffer_or_die (file, __LINE__) ; + + test_readf_[+ (get "data_type") +]_or_die (file, 0, test, frames, __LINE__) ; + for (k = 0 ; k < items ; k++) + if ([+ (get "error_func") +] (test [k], orig [k])) + { printf ("\n\nLine %d : Stereo : Incorrect sample (#%d : [+ (get "format_char") +] => [+ (get "format_char") +]).\n", __LINE__, k, orig [k], test [k]) ; + exit (1) ; + } ; + + /* Seek to start of file. */ + test_seek_or_die (file, 0, SEEK_SET, 0, sfinfo.channels, __LINE__) ; + + test_readf_[+ (get "data_type") +]_or_die (file, 0, test, 2, __LINE__) ; + for (k = 0 ; k < 4 ; k++) + if ([+ (get "error_func") +] (test [k], orig [k])) + { printf ("\n\nLine %d : Stereo : Incorrect sample (#%d : [+ (get "format_char") +] => [+ (get "format_char") +]).\n", __LINE__, k, orig [k], test [k]) ; + exit (1) ; + } ; + + /* Seek to offset from start of file. */ + test_seek_or_die (file, 10, SEEK_SET, 10, sfinfo.channels, __LINE__) ; + + /* Check for errors here. */ + if (sf_error (file)) + { printf ("Line %d: Should NOT return an error.\n", __LINE__) ; + puts (sf_strerror (file)) ; + exit (1) ; + } ; + + if (sf_read_[+ (get "data_type") +] (file, test, 1) > 0) + { printf ("Line %d: Should return 0.\n", __LINE__) ; + exit (1) ; + } ; + + if (! sf_error (file)) + { printf ("Line %d: Should return an error.\n", __LINE__) ; + exit (1) ; + } ; + /*-----------------------*/ + + test_readf_[+ (get "data_type") +]_or_die (file, 0, test + 10, 2, __LINE__) ; + for (k = 20 ; k < 24 ; k++) + if ([+ (get "error_func") +] (test [k], orig [k])) + { printf ("\n\nLine %d : Stereo : Incorrect sample (#%d : [+ (get "format_char") +] => [+ (get "format_char") +]).\n", __LINE__, k, orig [k], test [k]) ; + exit (1) ; + } ; + + /* Seek to offset from current position. */ + test_seek_or_die (file, 8, SEEK_CUR, 20, sfinfo.channels, __LINE__) ; + + test_readf_[+ (get "data_type") +]_or_die (file, 0, test + 20, 2, __LINE__) ; + for (k = 40 ; k < 44 ; k++) + if ([+ (get "error_func") +] (test [k], orig [k])) + { printf ("\n\nLine %d : Stereo : Incorrect sample (#%d : [+ (get "format_char") +] => [+ (get "format_char") +]).\n", __LINE__, k, orig [k], test [k]) ; + exit (1) ; + } ; + + /* Seek to offset from end of file. */ + test_seek_or_die (file, -1 * (sfinfo.frames - 10), SEEK_END, 10, sfinfo.channels, __LINE__) ; + + test_readf_[+ (get "data_type") +]_or_die (file, 0, test + 20, 2, __LINE__) ; + for (k = 20 ; k < 24 ; k++) + if ([+ (get "error_func") +] (test [k], orig [k])) + { printf ("\n\nLine %d : Stereo : Incorrect sample (#%d : [+ (get "format_char") +] => [+ (get "format_char") +]).\n", __LINE__, k, orig [k], test [k]) ; + exit (1) ; + } ; + + sf_close (file) ; +} /* stereo_[+ (get "type_name") +]_test */ + +static void +mono_rdwr_[+ (get "type_name") +]_test (const char *filename, int format, int long_file_ok, int allow_fd) +{ SNDFILE *file ; + SF_INFO sfinfo ; + [+ (get "data_type") +] *orig, *test ; + int k, pass ; + + orig = orig_data.[+ (get "data_field") +] ; + test = test_data.[+ (get "data_field") +] ; + + sfinfo.samplerate = SAMPLE_RATE ; + sfinfo.frames = DATA_LENGTH ; + sfinfo.channels = 1 ; + sfinfo.format = format ; + + if ((format & SF_FORMAT_TYPEMASK) == SF_FORMAT_RAW + || (format & SF_FORMAT_TYPEMASK) == SF_FORMAT_AU + || (format & SF_FORMAT_TYPEMASK) == SF_FORMAT_SD2) + unlink (filename) ; + else + { /* Create a short file. */ + create_short_file (filename) ; + + /* Opening a already existing short file (ie invalid header) RDWR is disallowed. + ** If this returns a valif pointer sf_open() screwed up. + */ + if ((file = sf_open (filename, SFM_RDWR, &sfinfo))) + { printf ("\n\nLine %d: sf_open should (SFM_RDWR) have failed but didn't.\n", __LINE__) ; + exit (1) ; + } ; + + /* Truncate the file to zero bytes. */ + if (truncate (filename, 0) < 0) + { printf ("\n\nLine %d: truncate (%s) failed", __LINE__, filename) ; + perror (NULL) ; + exit (1) ; + } ; + } ; + + /* Opening a zero length file RDWR is allowed, but the SF_INFO struct must contain + ** all the usual data required when opening the file in WRITE mode. + */ + sfinfo.samplerate = SAMPLE_RATE ; + sfinfo.frames = DATA_LENGTH ; + sfinfo.channels = 1 ; + sfinfo.format = format ; + + file = test_open_file_or_die (filename, SFM_RDWR, &sfinfo, allow_fd, __LINE__) ; + + /* Do 3 writes followed by reads. After each, check the data and the current + ** read and write offsets. + */ + for (pass = 1 ; pass <= 3 ; pass ++) + { orig [20] = pass * 2 ; + + /* Write some data. */ + test_write_[+ (get "data_type") +]_or_die (file, pass, orig, DATA_LENGTH, __LINE__) ; + + test_read_write_position_or_die (file, __LINE__, pass, (pass - 1) * DATA_LENGTH, pass * DATA_LENGTH) ; + + /* Read what we just wrote. */ + test_read_[+ (get "data_type") +]_or_die (file, 0, test, DATA_LENGTH, __LINE__) ; + + /* Check the data. */ + for (k = 0 ; k < DATA_LENGTH ; k++) + if ([+ (get "error_func") +] (orig [k], test [k])) + { printf ("\n\nLine %d (pass %d): Error at sample %d ([+ (get "format_char") +] => [+ (get "format_char") +]).\n", __LINE__, pass, k, orig [k], test [k]) ; + oct_save_[+ (get "data_type") +] (orig, test, DATA_LENGTH) ; + exit (1) ; + } ; + + test_read_write_position_or_die (file, __LINE__, pass, pass * DATA_LENGTH, pass * DATA_LENGTH) ; + } ; /* for (pass ...) */ + + sf_close (file) ; + + /* Open the file again to check the data. */ + file = test_open_file_or_die (filename, SFM_RDWR, &sfinfo, allow_fd, __LINE__) ; + + if (sfinfo.format != format) + { printf ("\n\nLine %d : Returned format incorrect (0x%08X => 0x%08X).\n", __LINE__, format, sfinfo.format) ; + exit (1) ; + } ; + + if (sfinfo.frames < 3 * DATA_LENGTH) + { printf ("\n\nLine %d : Not enough frames in file. (%ld < %d)\n", __LINE__, SF_COUNT_TO_LONG (sfinfo.frames), 3 * DATA_LENGTH ) ; + exit (1) ; + } + + if (! long_file_ok && sfinfo.frames != 3 * DATA_LENGTH) + { printf ("\n\nLine %d : Incorrect number of frames in file. (%ld should be %d)\n", __LINE__, SF_COUNT_TO_LONG (sfinfo.frames), 3 * DATA_LENGTH ) ; + exit (1) ; + } ; + + if (sfinfo.channels != 1) + { printf ("\n\nLine %d : Incorrect number of channels in file.\n", __LINE__) ; + exit (1) ; + } ; + + if (! long_file_ok) + test_read_write_position_or_die (file, __LINE__, 0, 0, 3 * DATA_LENGTH) ; + else + test_seek_or_die (file, 3 * DATA_LENGTH, SFM_WRITE | SEEK_SET, 3 * DATA_LENGTH, sfinfo.channels, __LINE__) ; + + for (pass = 1 ; pass <= 3 ; pass ++) + { orig [20] = pass * 2 ; + + test_read_write_position_or_die (file, __LINE__, pass, (pass - 1) * DATA_LENGTH, 3 * DATA_LENGTH) ; + + /* Read what we just wrote. */ + test_read_[+ (get "data_type") +]_or_die (file, pass, test, DATA_LENGTH, __LINE__) ; + + /* Check the data. */ + for (k = 0 ; k < DATA_LENGTH ; k++) + if ([+ (get "error_func") +] (orig [k], test [k])) + { printf ("\n\nLine %d (pass %d): Error at sample %d ([+ (get "format_char") +] => [+ (get "format_char") +]).\n", __LINE__, pass, k, orig [k], test [k]) ; + oct_save_[+ (get "data_type") +] (orig, test, DATA_LENGTH) ; + exit (1) ; + } ; + + } ; /* for (pass ...) */ + + sf_close (file) ; +} /* mono_rdwr_[+ (get "data_type") +]_test */ + +static void +new_rdwr_[+ (get "type_name") +]_test (const char *filename, int format, int allow_fd) +{ SNDFILE *wfile, *rwfile ; + SF_INFO sfinfo ; + [+ (get "data_type") +] *orig, *test ; + int items, frames ; + + orig = orig_data.[+ (get "data_field") +] ; + test = test_data.[+ (get "data_field") +] ; + + sfinfo.samplerate = 44100 ; + sfinfo.frames = SILLY_WRITE_COUNT ; /* Wrong length. Library should correct this on sf_close. */ + sfinfo.channels = 2 ; + sfinfo.format = format ; + + items = DATA_LENGTH ; + frames = items / sfinfo.channels ; + + wfile = test_open_file_or_die (filename, SFM_WRITE, &sfinfo, allow_fd, __LINE__) ; + sf_command (wfile, SFC_SET_UPDATE_HEADER_AUTO, NULL, SF_TRUE) ; + test_writef_[+ (get "data_type") +]_or_die (wfile, 1, orig, frames, __LINE__) ; + sf_write_sync (wfile) ; + test_writef_[+ (get "data_type") +]_or_die (wfile, 2, orig, frames, __LINE__) ; + sf_write_sync (wfile) ; + + rwfile = test_open_file_or_die (filename, SFM_RDWR, &sfinfo, allow_fd, __LINE__) ; + if (sfinfo.frames != 2 * frames) + { printf ("\n\nLine %d : incorrect number of frames in file (%ld shold be %d)\n\n", __LINE__, SF_COUNT_TO_LONG (sfinfo.frames), frames) ; + exit (1) ; + } ; + + test_writef_[+ (get "data_type") +]_or_die (wfile, 3, orig, frames, __LINE__) ; + + test_readf_[+ (get "data_type") +]_or_die (rwfile, 1, test, frames, __LINE__) ; + test_readf_[+ (get "data_type") +]_or_die (rwfile, 2, test, frames, __LINE__) ; + + sf_close (wfile) ; + sf_close (rwfile) ; +} /* new_rdwr_[+ (get "type_name") +]_test */ + +[+ ENDFOR data_type +] + +/*---------------------------------------------------------------------------------------- +*/ + +static void +empty_file_test (const char *filename, int format) +{ SNDFILE *file ; + SF_INFO info ; + int allow_fd ; + + /* Sd2 files cannot be opened from an existing file descriptor. */ + allow_fd = ((format & SF_FORMAT_TYPEMASK) == SF_FORMAT_SD2) ? SF_FALSE : SF_TRUE ; + + print_test_name ("empty_file_test", filename) ; + + unlink (filename) ; + + info.samplerate = 48000 ; + info.channels = 2 ; + info.format = format ; + + if (sf_format_check (&info) == SF_FALSE) + { info.channels = 1 ; + if (sf_format_check (&info) == SF_FALSE) + { puts ("invalid file format") ; + return ; + } ; + } ; + + /* Create an empty file. */ + file = test_open_file_or_die (filename, SFM_WRITE, &info, allow_fd, __LINE__) ; + sf_close (file) ; + + /* Open for read and check the length. */ + file = test_open_file_or_die (filename, SFM_READ, &info, allow_fd, __LINE__) ; + + if (SF_COUNT_TO_LONG (info.frames) != 0) + { printf ("\n\nError : frame count (%ld) should be zero.\n", SF_COUNT_TO_LONG (info.frames)) ; + exit (1) ; + } ; + + sf_close (file) ; + + /* Open for read/write and check the length. */ + file = test_open_file_or_die (filename, SFM_RDWR, &info, allow_fd, __LINE__) ; + + if (SF_COUNT_TO_LONG (info.frames) != 0) + { printf ("\n\nError : frame count (%ld) should be zero.\n", SF_COUNT_TO_LONG (info.frames)) ; + exit (1) ; + } ; + + sf_close (file) ; + + /* Open for read and check the length. */ + file = test_open_file_or_die (filename, SFM_READ, &info, allow_fd, __LINE__) ; + + if (SF_COUNT_TO_LONG (info.frames) != 0) + { printf ("\n\nError : frame count (%ld) should be zero.\n", SF_COUNT_TO_LONG (info.frames)) ; + exit (1) ; + } ; + + sf_close (file) ; + + check_open_file_count_or_die (__LINE__) ; + + unlink (filename) ; + puts ("ok") ; + + return ; +} /* empty_file_test */ + + +/*---------------------------------------------------------------------------------------- +*/ + +static void +create_short_file (const char *filename) +{ FILE *file ; + + if (! (file = fopen (filename, "w"))) + { printf ("create_short_file : fopen (%s, \"w\") failed.", filename) ; + fflush (stdout) ; + perror (NULL) ; + exit (1) ; + } ; + + fprintf (file, "This is the file data.\n") ; + + fclose (file) ; +} /* create_short_file */ + +#if (defined (WIN32) || defined (__WIN32)) + +/* Win32 does not have truncate (nor does it have the POSIX function ftruncate). +** Hack somethng up here to over come this. This function can only truncate to a +** length of zero. +*/ + +static int +truncate (const char *filename, int ignored) +{ int fd ; + + ignored = 0 ; + + if ((fd = open (filename, O_RDWR | O_TRUNC | O_BINARY)) < 0) + return 0 ; + + close (fd) ; + + return 0 ; +} /* truncate */ + +#endif + +[+ COMMENT + + Do not edit or modify anything in this comment block. + The following line is a file identity tag for the GNU Arch + revision control system. + + arch-tag: 4187de93-d434-41a2-93a9-4f6e2995b5c1 + ++]