pax_global_header00006660000000000000000000000064152273426370014525gustar00rootroot0000000000000052 comment=19504462aef3d444aa09b9cfcad0e2146889fb3b knik0-faac-1950446/000077500000000000000000000000001522734263700136275ustar00rootroot00000000000000knik0-faac-1950446/.github/000077500000000000000000000000001522734263700151675ustar00rootroot00000000000000knik0-faac-1950446/.github/dependabot.yml000066400000000000000000000002631522734263700200200ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" groups: actions: patterns: - "*" knik0-faac-1950446/.github/workflows/000077500000000000000000000000001522734263700172245ustar00rootroot00000000000000knik0-faac-1950446/.github/workflows/master.yml000066400000000000000000000067121522734263700212500ustar00rootroot00000000000000name: Continuous Integration on: push: branches: [ "master" ] tags-ignore: ['*'] paths-ignore: ['**.md'] pull_request: branches: [ "master" ] paths-ignore: ['**.md'] jobs: build: name: ${{ matrix.config.name }} runs-on: ${{ matrix.config.os }} defaults: run: shell: ${{ matrix.config.shell }} strategy: fail-fast: false matrix: config: - { name: Linux GCC, os: ubuntu-latest, compiler: gcc, shell: bash, } - { name: Linux GCC (SBR Decimation), os: ubuntu-latest, compiler: gcc, shell: bash, options: -Dsbr-decimation=4, } - { name: macOS Clang, os: macos-latest, compiler: clang, shell: bash, } - { name: MSYS2 UCRT64, os: windows-latest, compiler: gcc, shell: 'msys2 {0}', msystem: ucrt64, msys-env: mingw-w64-ucrt-x86_64, } - { name: Windows MSVC, os: windows-latest, compiler: cl, shell: pwsh, } steps: - name: Install dependencies (Ubuntu) if: runner.os == 'Linux' run: sudo apt install meson - name: Install dependencies (macOS) if: runner.os == 'macOS' env: HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: 1 run: | brew update || true brew install \ meson - name: Install dependencies (MSYS2) if: matrix.config.shell == 'msys2 {0}' uses: msys2/setup-msys2@v2.32.0 with: msystem: ${{ matrix.config.msystem }} update: false install: >- ${{ matrix.config.msys-env }}-meson ${{ matrix.config.msys-env }}-gcc - name: Install dependencies (MSVC) if: matrix.config.compiler == 'cl' run: | pip install meson ninja - name: Set up MSVC if: matrix.config.compiler == 'cl' uses: ilammy/msvc-dev-cmd@v1 with: arch: x64 - uses: actions/checkout@v7 - name: Build (MSYS2) if: matrix.config.shell == 'msys2 {0}' env: CC: ${{ matrix.config.compiler }} run: | mkdir -p build temp cd build meson setup .. -Dprefix=$(realpath ../temp) ${{ matrix.config.options }} meson install - name: Build (General) if: matrix.config.shell != 'msys2 {0}' env: CC: ${{ matrix.config.compiler }} run: | meson setup build -Dprefix="${{ github.workspace }}/temp" ${{ matrix.config.options }} meson install -C build - name: Upload artifacts (Windows) uses: actions/upload-artifact@v7 if: runner.os == 'Windows' with: name: faac-${{ github.sha }}-${{ matrix.config.name }} path: temp/* cppcheck: name: Cppcheck runs-on: ubuntu-latest steps: - name: Install dependencies run: | sudo apt-get update sudo apt-get install cppcheck - uses: actions/checkout@v7 - name: Run cppcheck shell: bash run: | cppcheck --version cppcheck --error-exitcode=1 -j4 -q --check-level=exhaustive -Iinclude/ libfaac/ frontend/ knik0-faac-1950446/.gitignore000066400000000000000000000006121522734263700156160ustar00rootroot00000000000000*.o *.so *.a *.ncb *.plg *.aps *.opt *.aac *.wav *.mp4 *.m4a *.lo Makefile* *.deps *.libs *.la *~ /frontend/faac /aclocal.m4 /autom4te.cache /compile /config.guess /config.h /config.h.in /config.log /config.status /config.sub /configure /depcomp /install-sh /libtool /ltmain.sh /missing /stamp-h1 .vs/ /project/msvc/bin/ /project/msvc/intermediate/ *.user /libfaac/win32_ver.h /libfaac/faac.pc knik0-faac-1950446/AUTHORS000066400000000000000000000024211522734263700146760ustar00rootroot00000000000000Currently maintained by knik . The following list contains the user names of all FAAC developers with their first appearance in the ChangeLog: 1999-12-13 lenox (Tony Lenox) 1999-12-13 menno 2000-02-07 oxygene2000/oxygene (?) 2000-02-22 thebard (wmilas@rarcoa.com) 2000-07-22 prkoat (?) 2001-03-18 xfhobbes (?) 2001-04-27 flyingfox (?) 2001-09-21 eraser (?) 2002-08-07 knik (Krzysztof Nikiel) 2003-08-02 stux (Stuart Espey) 2003-09-07 ca5e (Janne Hyvärinen) 2004-03-17 danchr (Dan Christiansen) 2004-07-04 corrados (Volker Fischer) 2012-04-08 arcen (Arcen@github) Furthermore there are other contributors from the SourceForge mailing list and trackers as well as from the web forum or via email whose patches were checked in by members of the FAAC project. Among them are Antonio Foranna (Winamp and CoolEdit plugins), Bill May, Stephen Shultz (sms00), Jordan Breeding, Brandon Forehand, Andrew Voznytsa, Måns Rullgård, RageOMatic and Ivan Dimkovic. You can find their names and/or aliases in the ChangeLog most of the time. Question marks mean that neither the real name nor the email address is known. If you think your name should also be in this list or know who some of the early developers are, please email me: hans-juergen.bardenhagen@arcor.de knik0-faac-1950446/COPYING000066400000000000000000000634631522734263700146760ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 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, see . 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 Moe Ghoul, President of Vice That's all there is to it! knik0-faac-1950446/ChangeLog000066400000000000000000000265521522734263700154130ustar00rootroot000000000000002.0 (Jul 20, 2026) [ Fabian Greffrath ] * Remove obsolete pkg-config template * Restrict LTO to the shared library only [ Orbis Security AI ] * filtbank: Fix an out-of-bounds `memcpy` in `FilterBank()` that used `FRAME_LEN` instead of `BLOCK_LEN_LONG` for the overlap buffer copies [ Nils Schimmelmann ] * Harden input parsing and guard against allocation failures. * Replace the classic `faacEnc*` C API with a new `faac_*` API in `faac.h`: parameters supplied once to `faac_encoder_open()`, uniform `faac_status` error reporting, resolved-property queries, and library-owned AudioSpecificConfig. The legacy `faacEnc*` surface and `faaccfg.h` are removed; the encoder core they exposed is now internal. SONAME bumped to 1 (libfaac.so.1). `faac.h` now defines `FAAC_VERSION_MAJOR`/`MINOR`/`PATCH`, tracking the SONAME, so callers can detect the new API at compile time (`FAAC_VERSION_MAJOR >= 1`). * frontend: Port `faac` and the Win32 GUI to the new API; this fixes the AudioSpecificConfig memory leak on the MP4 output path. * Add HE-AAC v1 (AAC-LC core + Spectral Band Replication) encoding via `FAAC_OBJ_HE_AAC_V1`, or `FAAC_OBJ_AUTO`, which picks LC or HE-AAC from the bitrate. The SBR core runs dual-rate (half-rate core, 2048-sample frame); `faac_encoder_get_info()` reports the full output rate and frame size so callers need no SBR-specific handling. CLI: `--object-type lc|he-aac-v1|auto`. * stereo: Add Mixed Mode joint stereo (`--joint 3`) as the new default: per scale-factor band, picks IS above 5.5 kHz, M/S where channels are correlated, or L/R otherwise. Fix a midside band-silencing bug that produced L=R=0 on decode, guard against negative rounding before sqrt, and keep IS available at low sample rates. * quantize: Restore short-window masking penalty, floor masking targets in quiet bands, drop stale tonal scaling in bmask, and keep `global_gain` a valid regular scalefactor -- fixing energy drops, quiet-band artifacts, and a decoder out-of-range error. * tns: Cap TNS filter order to 12 to fix AAC-LC decoder rejections and audio dropouts in FFmpeg/FAAD. * Retire the never-implemented MAIN/SSR/LTP object types across filtbank, frame, and the frontend, and collapse the single-model psychoacoustic vtable into direct calls. * Fix a lookahead regression that had truncated the encoder's lookahead to an effective 1-frame delay, causing pre-echo artifacts and broken stereo coherence; then simplify the lookahead into a unified per-channel FIFO and energy timeline, replacing the ad hoc sampleBuff/next*SampleBuff/overlapBuff/prevSamples buffers. * Relicense: rewrite stereo, quantize, channels, bitstream, filtbank, tns, huffdata, huff2, and the MP4 output code as original work under LGPL-2.1-or-later, and drop the matching MPEG reference-code preamble from README.md, --license, and docs/. Every source file is now free software. Update the license header itself from GNU Library GPL to Lesser GPL wording. * fft: Replace the radix-2 FFT with radix-4 decimation-in-frequency as the sole implementation and precompute MDCT twiddles, for ~18% faster encode versus the radix-2 baseline. * Standardize libfaac on single-precision floating point: drop the `floating-point` Meson build option and `faac_real.h`, and use `float`/`sinf`/`cosf`/`sqrtf` directly throughout the per-sample hot path. One-time init-time table generators (FFT/MDCT/SBR twiddles, window shapes, quantizer limits) keep `double` precision, where it's free. Benchmarked MOS-neutral versus the prior double-precision path. * blockswitch: Move short-window detection to the time domain. * Performance passes: reuse a shared work buffer for filtbank's MDCT scratch, and optimize TNS and stereo IS-gate hot paths. * frame: Buffer encoder input so any input chunk size works. * bitstream: Stop embedding the libfaac version string. * build: Fix compiler warnings and transition to C11. * frontend: Add a `--creation-time` flag for configurable MP4 timestamps, vendor mingw-w64 wingetopt as a minimal-diff getopt fallback for MSVC builds, and keep TNS off by default in the GUI to match the CLI. 1.50 (Apr 16, 2026) [ Fabian Greffrath ] * Build quantize_sse library with hidden symbols * Don't call `exit()` from the library * Define some more `default_options` in Meson build * Fix some warnings in `frontend/maingui.c` * Default to `buildtype=release` in Meson build * Initialize `data` variable in `huffcode()` * Remove the faulty `HAVE_STRCASECMP` check and other pointless header checks * CI: Let Meson build set `CFLAGS` [ Nils Schimmelmann ] * Disallow PNS in MPEG-2 mode to match spec * frontend: Change default to MPEG-4 as MPEG-2 is obsolete * Add `max-channels` option to change `MAX_CHANNELS` from 64 * Fix PNS to stay on in `JOINT_MS` for low-bitrate accuracy * Replace redundant `cpe`, `sce`, and `lfe` boolean flags in `ChannelInfo` with an `enum` * Reduce `MAX_CHANNELS` to 8 for ADTS compliance * tns: Prevent division-by-zero or instability on highly correlated signals * Implement MOS-optimized bandwidth calculation * Fine-tune rate controller to restore bitrate accuracy * Fix channel-specific group metadata in joint-stereo * Enable link-time optimization (LTO) in Meson build * stereo: Fix undefined behaviour in intensity stereo for degenerate bands * Fix scalefactor delta overflows to comply with AAC spec (ISO 14496-3) * quantize: Pre-clamp scalefactor deltas to fix encoder/decoder mismatch 1.40 (Mar 17, 2026) [ tatsuz ] * Fix compilation with Visual Studio [ enWILLYado ] * Fix SIMD calls [ David Fort ] * Add meson as build system [ KokonoeTooru ] * Meson: Install faac executable [ Fabian Greffrath ] * CI: fix artifact path * Remove autoconf/automake build system * Remove plugins stuff, unmaintained for decades * Remove MSVC projects, deprecated in favour of meson * Do not check for unused headers * Generalize OS check in CI rule * Remove redundant defines * Make `faacgui` usable again * Report library version on version mismatch * Maintain library SONAME version 0.0.0 * Build both static and shared library by default * Properly declare symbol visibility as hidden [ Nils Schimmelmann ] * Add options to disable DRM and opt for single floating-point precision * CI: add native MSVC build and fix Windows compatibility * Remove dead code * Optimize FFT reordering by combining real and imaginary passes * Optimize FFT performance * CI: update GitHub Actions and add Dependabot configuration * Refactor quantizer for portable C and extensible SIMD dispatch * Optimize memory allocation and unify MDCT implementation * Optimize MDCT by removing conditional branches and hoisting logic * Rrefactor the hot path for writing bits to the output stream * Remove Digital Radio Mondiale (DRM) support 1.31.1 (Mar 03, 2025) [ Fabian Greffrath ] * Reduce autoconf requirement back down to 2.69 * Reformat configure.ac for the ac2ver.exe hack to work * Upload Windows artifacts 1.31 (Feb 28, 2025) [ Tatsuya Suzuki ] * updated Visual Studio projects to VS 2019 * main.c : progress report keeps alignment when encoding a large file * project/msvc : cleanup, fixes and changes [ Christopher Degawa ] * frontend: fix out-of-root build [ Fabian Greffrath ] * add a pkg-config file * avoid division by zero * Remove #pragma pack from faacEncStruct (thanks Locutus of Borg) * Reduce memory (thanks xnvi) + Remove `BwpInfo bwpInfo` from `struct CoderInfo` + Remove `nextSampleBuff[]` and `next2SampleBuff[]` from `struct faacEncStruct` * tns: Initialize `rArray[]` variable * update autoconf.ac and friends * do not pass add-stdcall-alias option to clang * always build both the regular and the DRM library * fix cppcheck warnings * add some basic CI * fix compilation with clang on Windows * Append INSTALL to README [ Steve Fosdick ] * Add album artist and various sort order MP4 tags [ Aaron Boxer ] * set libfaac library name to "faac" rather than "libfaac" [ Frank ] * Error C2065 (add #define _USE_MATH_DEFINES) [ pine3ree ] * Allow genre-id up to 255 1.30 (Oct 16, 2019) [ Robert Kausch ] * Fix Joint Stereo coding [ Krzysztof Nikiel ] * Code cleanup and compilation fixes * Fix endianness conversion functions [ Takashi Yoshi ] * Fix compilation with GCC < 4.6 * Fix compilation on big endian systems [ Fabian Greffrath ] * Fix division by zero errors * Fix compilation with GCC <= 4.7.3 * Change pointer type for proper arithmetics * Fix logic error in compiler detection * Revert back to some more generic SSE2 code * Fix a memory leak * Fix some cppcheck warnings in the Cfaac code * Check index ranges before dereferencing book arrays (CVE-2018-19886) * Clean up stdint.h header inclusions * Consistently use stdint.h types [ Michael Fink ] * Update Visual Studio 2017 projects * Add stdint.h header inclusions * Port over ac2ver tool from faad2 to generate PACKAGE_VERSION when compiling with Visual Studio [ Eugène Filin ] * Memory allocations redefined 1.29.9.2 (Nov 14, 2017) * fixed max MP4 bitrate calculation 1.29.9 (Nov 2, 2017) * initial version of PNS coding * Intensity Stereo coding * more speed improvements * finer bandwidth/cutoff setting (doesn't work with PNS) * fixed more bugs * rewritten mid/side coding * fixed bug in blockswitch 1.29.8 (Oct 15, 2017) * changed default mode to ABR 64kbps/channel * implemented short windows grouping * new huffman coder * SSE quantizer * modified functioning of ABR mode (-b option) * improved autotools support * allowed even higher bitrates, including ADTS * framebuffer size bug fixed * removed broken object types: Main and LTP * some mp4 atoms fixed for better compalibility * stdin seek bug fixed 1.29.7 (Aug 22, 2017) * quantizer tweaked to better support high bitrates * allow higher quality for mp4 files (-q 5000 max) * removed mid frequency boost (it turned out useless) * cutoff frequency adjusted to fully utilize upper band * --help-long option removed, it was just an alias for -H * -b option bug fixed 1.29.6 (Aug 18, 2017) - quality related bug fixed 1.29.5 (Aug 17, 2017) - updated manpage - check if output file exists; --overwrite option added - help/usage options reorganized - block switching is now tuned to match quality/bitrate - give more quality to lower frequancies (less to highs) - quantizer updated again 1.29.4 (Aug 14, 2017) - rewritten quantizer: faster, diffrerent quality, different bitrates - ftyp atom: set brands like itunes does, shoud be more compatible with picky tools - new option(--tag) to add named tags (iTunes '----') - faster and better short/long window type switch - Don't build DRM(Digital Radio Mondiale) by default. Use ./configure --enable-drm to build DRM version. - fixed bugs * rounding in QuantizeReflectionCoeffs (tns.c) * use +60 value for scalefactor. * use clipped diff instead of original value (huffman.c) 1.29.3 (Jul 21, 2017) - MP4 tag options improved - fixed MP4 'meta' atom bug - new option to set verbosity (-v0 to silence output) 1.29.2 (Jul 17, 2017) - new mp4 output code 1.29 (Jul 4, 2017) - various patches applied and bugs fixed 1.28 (Feb 5, 2009) * Prevent out of range scalefactors * Updated to latest mpeg4ip mp4 file format library * Added -s option to make the encoder output optimized mp4 layout * Improved JPEG detection for album art * Lot's of compilation issues solved knik0-faac-1950446/NEWS000066400000000000000000000003041522734263700143230ustar00rootroot000000000000002017: FAAC thread on hydrogenaudio: https://hydrogenaud.io/index.php/topic,114363.0.html FAAC mirrored on github: https://github.com/knik0/faac FAAC download page: http://faac.sourceforge.net/ knik0-faac-1950446/README.md000066400000000000000000000036261522734263700151150ustar00rootroot00000000000000# FAAC Freeware Advanced Audio Coder FAAC is an open-source, dependency-free AAC encoder aimed at embedded and pipeline use cases where footprint and throughput matter as much as quality. ### Key features: - MPEG-4 AAC-LC and HE-AAC v1 (SBR) profiles - Sample rates from 8 kHz to 96 kHz, supporting mono up to 5.1 multichannel - VBR and ABR rate control with a bit reservoir - Advanced encoding tools: Dynamic block-switching, PNS, and TNS - Flexible output options: Raw ADTS, ADIF bitstream ## Copyrights FAAC is free software, licensed under the GNU Lesser General Public License (LGPL), version 2.1 or later: ``` FAAC - Freeware Advanced Audio Coder Copyright (C) 1999-2001, Menno Bakker Copyright (C) 2002-2017, Krzysztof Nikiel Copyright (C) 2004, Dan Villiom P. Christiansen Copyright (C) 2005-2026, Fabian Greffrath Copyright (C) 2026, Nils Schimmelmann 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. ``` > **Important:** The use of this software may require the payment of patent royalties. You need to consider this issue before you start building derivative works. We are not warranting or indemnifying you in any way for patent royalities! **YOU ARE SOLELY RESPONSIBLE FOR YOUR OWN ACTIONS!** ## Compiling Instructions 1. Make sure you have recent versions of meson and ninja installed. 2. cd to FAAC source dir 3. Run: ```bash mkdir -p build cd build meson setup .. meson install ``` knik0-faac-1950446/TODO000066400000000000000000000007001522734263700143140ustar00rootroot00000000000000- optimize codebook list (section_data) via joint/DP optimization - optimize CPE windows grouping jointly across channel pairs - Add adaptive bit reservoir control - add PCE (Program Config Element) or 4-bit AudioSpecificConfig - HE-AAC v1: SBR noise floor / invf mode from within-band tonality - HE-AAC v1: Move numEnvelopes/eff_amp_res/tEnv to SBRChannel for independent borders - HE-AAC v1: Use dedicated noise Huffman tables in write_sbr_noise knik0-faac-1950446/docs/000077500000000000000000000000001522734263700145575ustar00rootroot00000000000000knik0-faac-1950446/docs/faac.1000066400000000000000000000126171522734263700155420ustar00rootroot00000000000000.TH FAAC 1 "2026-06-25" "1.50.0" "Free Advanced Audio Coder" .SH NAME faac \- open source MPEG-4 and MPEG-2 AAC encoder .SH SYNOPSIS .B faac .RI [ options ] .RI [\-o\ outfile ] .I infiles .RI ... .PP .RI < infiles > and/or .RI < outfile > can be "\-", which means stdin/stdout. .SH DESCRIPTION .B FAAC is an open source MPEG-4 and MPEG-2 AAC encoder, it is licensed under the LGPL license. Note that the quality of .B FAAC is not up to par with the currently best AAC encoders available. .SH FEATURES .TP * Portable .TP * Fast .TP * AAC-LC and HE-AAC v1 (SBR) support .SH HELP OPTIONS .TP .BR -h Short help on using FAAC .TP .BR -H Description of all options for FAAC .TP .BR --license License terms for FAAC. .TP .BR --help-qual Quality-related options .TP .BR --help-io Input/output options .TP .BR --help-mp4 MP4 specific options Quality-related options .TP .BR --help-advanced Advanced options, only for testing purposes .SH QUALITY-RELATED OPTIONS .TP .BR --object-type\ <\fItype\fP> Force the AAC object type: .B lc (Low Complexity), .B he-aac-v1 (High-Efficiency AAC v1, i.e. AAC-LC plus Spectral Band Replication), or .B auto (default), which selects LC or HE-AAC v1 from the bitrate and sample rate. HE-AAC v1 targets low bitrates; its core is coded at half the input sample rate and reconstructed to full rate by SBR in the decoder. .TP .BR --joint\ <\fImode\fP> Joint stereo coding mode: .B 0 (off, independent left/right), .B 1 (Mid/Side), .B 2 (Intensity Stereo), or .B 3 (Mixed Mode, dynamic per-band Mid/Side and Intensity Stereo). Default is .BR 3 . .TP .BR --pns\ <\fIlevel\fP> Perceptual Noise Substitution level, 0 to 10; .B 0 disables PNS. .TP .BR -q\ <\fIquality\fP> Set encoding quality. Set default variable bitrate (VBR) quality level in percent. max. 5000, min. 10. default: 100, averages at approx. 120 kbps VBR for a normal stereo input file with 16 bit and 44.1 kHz sample rate .TP .BR -b\ <\fIbitrate\fP> Set average bitrate (ABR) to approximately kbps. max. ~500 (stereo) .TP .BR -c\ <\fIfreq\fP> Set the bandwidth in Hz. The actual frequency is adjusted to maximize upper spectral band usage. .SH INPUT/OUTPUT OPTIONS .TP .BR -o\ <\fIfilename\fP> Set output file to X (only for one input file) only for one input file; you can use *.aac, *.mp4, *.m4a or *.m4b as file extension, and the file format will be set automatically to ADTS or MP4). .TP .BR - Use stdin/stdout. If you simply use a hyphen/minus sign instead of a filename, FAAC can encode directly from stdin, thus enabling piping from other applications and utilities. The same works for stdout as well, so FAAC can pipe its output to other apps such as a server. .TP .BR -v\ <\fIvebose\fP> Verbosity level (-v0 is quiet mode) .TP .BR -r Use RAW AAC output file. Generate raw AAC bitstream (i.e. without any headers). Not advised!!!, RAW AAC files are practically useless!!! .TP .BR -P Raw PCM input mode (default 44100Hz 16bit stereo). Raw PCM input mode (default: off, i.e. expecting a WAV header; necessary for input files or bitstreams without a header; using only -P assumes the default values for -R, -B and -C in the input file). .TP .BR -R\ <\fIsamplerate\fP> Raw PCM input rate. Raw PCM input sample rate in Hz (default: 44100 Hz, max. 96 kHz) .TP .BR -B\ <\fIsamplebits\fP> Raw PCM input sample size (8, 16 (default), 24 or 32bits). Raw PCM input sample size (default: 16, also possible 8, 24, 32 bit fixed or float input). .TP .BR -C\ <\fIchannels\fP> Raw PCM input channels. Raw PCM input channels (default: 2, max. 33 + 1 LFE). .TP .BR -X Raw PCM swap input bytes Raw PCM swap input bytes (default: bigendian). .TP .BR -I\ <\fIC[,LFE]\fP> Input channel config, default is 3,4 (Center third, LF fourth) Input multichannel configuration (default: 3,4 which means Center is third and LFE is fourth like in 5.1 WAV, so you only have to specify a different position of these two mono channels in your multichannel input files if they haven't been reordered already). .TP .BR --ignorelength Ignore wav length from header (useful with files over 4 GB) .TP .BR --overwrite Overwrite existing output file .SH MP4 SPECIFIC OPTIONS .TP .BR -w Wrap AAC data in MP4 container. (default for *.mp4, *.m4a and *.m4b) .TP .BR --tag\ <\fItagname,tagvalue\fP> Add named tag (iTunes '----') .TP .BR --artist\ <\fIname\fP> Set artist name .TP .BR --composer\ <\fIname\fP> Set composer name .TP .BR --title\ <\fIname\fP> Set title/track name .TP .BR --genre\ <\fInumber\fP> Set genre number .TP .BR --album\ <\fIname\fP> Set album/performer .TP .BR --compilation Mark as compilation .TP .BR --track\ <\fInumber/total\fP> Set track number .TP .BR --disc\ <\fInumber/total\fP> Set disc number .TP .BR --year\ <\fInumber\fP> Set year .TP .BR --cover-art\ <\fIfilename\fP> Read cover art from file X Supported image formats are GIF, JPEG, and PNG. .TP .BR --comment\ <\fIstring\fP> Set comment .SH ADVANCED OPTIONS, ONLY FOR TESTING PURPOSES .TP .BR --tns Enable coding of TNS, temporal noise shaping. .TP .BR --no-tns Disable coding of TNS, temporal noise shaping. .TP .BR --mpeg-vers\ \fIX\fP Force AAC MPEG version, X can be 2 or 4 .TP .BR --shortctl\ \fIX\fP Enforce block type (0 = both (default); 1 = no short; 2 = no long). .SH AUTHORS .B FAAC was written by M. Bakker. .PP .nh 2 Developed and maintained by Krzysztof Nikiel . .PP This manpage was written by Fabian Greffrath for the Debian Unofficial project (but may be used by others, of course). .nh 1 knik0-faac-1950446/docs/faac.html000066400000000000000000000123531522734263700163430ustar00rootroot00000000000000 FAAC - Freeware Advanced Audio Coder

FAAC - Freeware Advanced Audio Coder


Contents

  • Usage
  • options
  • notes
  • Copyrights
  • Patents
  • People


  • Usage

    faac [options] <infile> <outfile>

    Options:
  • -a X Set average bitrate to approximately X kbps per channel (i.e. using -a 64 averages at 128 kbps/stereo).
  • -c <bandwidth> Set the bandwidth in Hz (default value depends on sample rate)
  • -q <quality> Set quantizer quality (default: 100, averages at approx. 128 kbps VBR for a normal stereo input file at 16 bit and 44.1 kHz sample rate).
  • --tns Enable TNS coding.
  • --notns Disable TNS coding.
  • -n Disable mid/side coding.
  • -m X AAC MPEG version, X can be 2 or 4 (default: MPEG-2, so for the sake of interoperability with non-standard compliant players like QuickTime 6 you should set it to "4").
  • -o X AAC object type, X can be LC, MAIN or LTP (default: LC, for the same reason as with the MPEG version don't use Main or LTP).
  • -r RAW AAC output file (i.e. without ADTS headers).
  • -P Raw PCM input mode.
  • -R Raw PCM input sample rate in Hz (default: 44100 Hz).
  • -B Raw PCM input bit depth (default: 16 bits, also possible 8 bits).
  • -C Raw PCM input channels (default: 2).
  • - <stdin> If you simply use a hyphen/minus sign instead of an input file name, FAAC can encode directly from stdin, thus enabling piping within other applications like foobar2000 or mp4live.

    Note: VBR output bitrate depends on -q AND -c, so you should only vary the default setting -q 100 -c 16000 if you know what you're doing and/or want to experiment with other cutoff frequencies at a given quality setting.

    The ABR setting with -a is an approximate average bitrate that does not use a bit reservoir, i.e -a 64 and -q 100 at 44.1 kHz will result in exactly the same output file.

    The following list should give some orientation for useful -q and -c settings, based on FAAC v1.17. The resulting VBR bitrates are referring to an average sounding stereo file with 16bit, 44.1 kHz, i.e. ct_reference.wav in this case. Multiplexing these AAC files to MP4 with e.g. mp4creator will result in a ~3 kbps lower bitrate because of the stripped ADTS headers:

    -q 130 -c 22000 -m 4 (~218 kbps)
    -q 120 -c 20000 -m 4 (~194 kbps)
    -q 110 -c 18000 -m 4 (~158 kbps)
    -q 100 -c 16000 -m 4 (~129 kbps)
    -q 90 -c 14000 -m 4 (~103 kbps)
    -q 80 -c 12000 -m 4 (~79 kbps)
    -q 70 -c 10000 -m 4 (~62 kbps)

    The added -m 4 switch does not change the bitrate or sound of course, but is recommended for most AAC/MP4 players that use an updated FAAD2-based plugin from this year (Winamp 2.x, foobar2000 etc.) or can't decode MPEG-2 AAC LC files like QuickTime 6. Philips Expanium users should not use this switch, because their CD portable does not know MPEG-4 AAC files.



    Copyrights

    FAAC - Freeware Advanced Audio Coder
    FAAC is free software, licensed under the GNU Lesser General Public License (LGPL), version 2.1 or later:
    FAAC - Freeware Advanced Audio Coder
    Copyright © 1999-2001, Menno Bakker
    Copyright © 2002-2017, Krzysztof Nikiel
    Copyright © 2004, Dan Villiom P. Christiansen
    Copyright © 2005-2026, Fabian Greffrath
    Copyright © 2026, Nils Schimmelmann
    
    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.
    

    Patents

    Please note that the use of this software may require the payment of patent royalties. You need to consider this issue before you start building derivative works. We are not warranting or indemnifying you in any way for patent royalities! YOU ARE SOLELY RESPONSIBLE FOR YOUR OWN ACTIONS!



    People

    Currently maintained by M. Bakker (menno)


    Other contributors:
    Tony Lenox
    RageOMatic
    thebard
    Ivan Dimkovic
    Krzysztof Nikiel (knik)
    Janne Hyvärinen (ca5e)
    Stuart Espey (stux)
    Andrew Voznytsa
    Måns Rullgård
    Bill May


    FAAC is free software, licensed under the GNU Lesser General Public License (LGPL), version 2.1 or later.
    knik0-faac-1950446/docs/libfaac.html000066400000000000000000000242571522734263700170400ustar00rootroot00000000000000 FAAC - ISO/MPEG 2/4 AAC Encoder Library

    FAAC - ISO/MPEG 2/4 AAC Encoder Library

    Contents

  • Scope
  • Interface description
  • API (faac.h)
  • Calling sequence
  • Error handling
  • Function reference
  • Porting from the legacy faacEnc* API
  • Scope

    This document describes the interface and usage of the FAAC - ISO/MPEG 2/4 AAC Encoder Library Developed for the Freeware Advanced Audio Coding project.

    Interface description

    The ISO/MPEG 2/4 AAC Encoder Library provides a high-level interface for encoding MPEG2 and MPEG4 ISO AAC files. A single header file is provided for usage in C/C++ programs:

    faac.h: the faac_* API — function prototypes and types.

    The encoder core resides in a statically linkable library called libfaac.lib (Microsoft Windows) or libfaac.a (UNIX). There are various example programs that show how to use the library.

    API (faac.h)

    The API supplies all encoder parameters once, up front, to faac_encoder_open(). The encoder therefore never exists in a half-configured state, and every derived quantity (frame size, output-buffer bound, effective sample rate, resolved object type) is known and queryable the instant open returns. Every fallible call returns a faac_status code; fixed-width integer types and width-pinned enums keep the ABI identical across platforms.
    Calling sequence
  • Zero-initialize a faac_params with faac_params_init(). This is mandatory — it stamps struct_size and clears padding, which is how the struct stays compatible as it grows in future releases.
  • Set the fields you care about (at minimum sample_rate and num_channels), then call faac_encoder_open().
  • Fill a faac_encoder_info with faac_encoder_get_info() (set its struct_size first) and size your buffers from it: input PCM holds info.frame_samples × num_channels samples; the output buffer must be at least info.max_output_bytes. The same struct also reports the resolved sample rate, object type, and rate-control settings. If you write a raw stream, fetch the AudioSpecificConfig with faac_encoder_asc() (library-owned — do not free it).
  • Call faac_encoder_encode() for each block of input; it emits at most one frame per call and reports the byte count via an out-parameter.
  • Pass in_samples == 0 to flush; keep calling until bytes_written is 0.
  • Call faac_encoder_close(), passing the address of your handle; it is set to NULL on success.
  • Error handling
    Every fallible entry point returns a faac_status. FAAC_OK is 0 and all error codes are negative, so status < 0 tests for failure. faac_strerror() maps any status to a static human-readable string (never NULL). Notable codes: FAAC_ERR_INVALID_ARGUMENT (NULL, bad struct_size, or an out-of-range field), FAAC_ERR_UNSUPPORTED (an object type this build does not implement, or a request with no ASC), FAAC_ERR_OUTPUT_TOO_SMALL, and FAAC_ERR_INPUT_OVERFLOW.
    Function reference
    /* library-global facts: compiled channel ceiling, version/copyright */
    faac_status faac_get_library_info(faac_library_info *out);
    
    faac_status faac_params_init(faac_params *p);
    faac_status faac_encoder_open(const faac_params *p, faac_encoder **out);
    faac_status faac_encoder_close(faac_encoder **enc);   /* sets *enc = NULL */
    
    /* resolved properties, valid after open; set out.struct_size first */
    faac_status faac_encoder_get_info(faac_encoder *enc, faac_encoder_info *out);
    
    /* AudioSpecificConfig: library-owned, valid until close, do NOT free */
    faac_status faac_encoder_asc(faac_encoder *enc, const uint8_t **buf, uint32_t *len);
    
    faac_status faac_encoder_encode(faac_encoder *enc,
                                    const void *in, uint32_t in_samples,
                                    uint8_t *out, uint32_t out_cap,
                                    uint32_t *bytes_written);
    
    const char *faac_strerror(faac_status status);
    
    The object_type field of faac_params uses MPEG-4 Audio Object Type numbers: FAAC_OBJ_LOW (2) is AAC-LC and FAAC_OBJ_HE_AAC_V1 (5) is HE-AAC v1 (AAC-LC core + SBR). FAAC_OBJ_AUTO (0) lets the library choose LC or HE-AAC from the bitrate. FAAC_OBJ_HE_AAC_V2 (29) is defined but not implemented; requesting it returns FAAC_ERR_UNSUPPORTED.

    For an HE-AAC encoder the SBR core runs at half the input rate and codes a 2048-sample frame, so faac_encoder_get_info() reports frame_samples == 2048 and sample_rate == the full (un-halved) output rate. Buffer sizing and rate reporting therefore stay correct without the caller knowing any SBR internals.

    Porting from the legacy faacEnc* API

    The classic faacEnc* surface (faacEncOpen, faacEncGetCurrentConfiguration/faacEncSetConfiguration, faacEncEncode, faacEncClose, faacEncGetVersion, faacEncGetDecoderSpecificInfo, and the faacEncConfiguration struct from faaccfg.h) has been removed. There is no compatibility shim — callers must move to faac_*. The changes are mechanical:
  • Open sequence. The old two-step open (faacEncOpen() to get a handle plus inputSamples/maxOutputBytes out-params, then faacEncGetCurrentConfiguration() / faacEncSetConfiguration() to apply options after the fact) collapses into one step: build a faac_params, fill in the fields you care about, and call faac_encoder_open() once. There is no live-pointer configuration struct to mutate after opening.
  • Buffer sizing. Where you previously read inputSamples and maxOutputBytes from faacEncOpen(), call faac_encoder_get_info() after faac_encoder_open() and use info.frame_samples × num_channels and info.max_output_bytes instead.
  • Version info. faacEncGetVersion() is replaced by faac_get_library_info(), which also reports max_channels; the removed name/copyright write-only config fields are gone along with it.
  • Decoder-specific info. faacEncGetDecoderSpecificInfo() is replaced by faac_encoder_asc(). The buffer is still library-owned and valid until close — do not free it.
  • Encoding loop. faacEncEncode() becomes faac_encoder_encode() with the byte count returned via an out-parameter (bytes_written) instead of the return value; the return value is now a faac_status. Flush and end-of-stream detection are unchanged: pass in_samples == 0 to flush, and keep calling until bytes_written is 0.
  • Close. faacEncClose(hEncoder) becomes faac_encoder_close(&enc) — pass the address of your handle, not the handle itself; the library NULLs it on success.
  • Return values. Where the old API returned NULL handles or -1 on error, every fallible faac_* call returns a faac_status; test status < 0 and use faac_strerror() for a message.
  • Configuration field renames. faacEncConfiguration fields map onto faac_params fields as follows:
  • aacObjectTypeobject_type (now uses FAAC_OBJ_* constants, numbered per the MPEG-4 AOT registry, rather than the old MAIN/LOW/SSR/LTP enum — MAIN, SSR, and LTP are retired, unimplemented object types).
  • mpegVersionmpeg_version (FAAC_MPEG4 / FAAC_MPEG2).
  • jointmodejoint_mode (FAAC_JOINT_*). allowMidside was a compatibility alias sharing storage with jointmode in a union; it has no separate replacement, use joint_mode directly.
  • useLfeuse_lfe; useTnsuse_tns (now bool).
  • shortctlshort_control (FAAC_SHORTCTL_*).
  • bitRatebit_rate; bandWidthbandwidth; quantqualquant_quality; pnslevelpns_level (same units and semantics).
  • outputFormatoutput_format (FAAC_STREAM_*); inputFormatinput_format (FAAC_INPUT_*).
  • channel_map[64]channel_map plus an explicit channel_map_count (the old fixed-size array was implicitly sized by numChannels).
  • version, name, and copyright were unused or library-populated informational fields in the legacy config struct; their closest analogs are faac_params.struct_size (for version-style compatibility checks) and faac_library_info.version/copyright (queried via faac_get_library_info()), not a faac_params field.
  • Struct growth. Always call faac_params_init() before setting any fields, and set struct_size on any struct you pass to a query function (faac_encoder_get_info(), faac_get_library_info()). This is how the ABI stays stable as these structs grow in later releases; the legacy config struct had no equivalent versioning.

  • FAAC is free software, licensed under the GNU Lesser General Public License (LGPL), version 2.1 or later.
    Copyright © 1999-2001, Menno Bakker · Copyright © 2002-2017, Krzysztof Nikiel · Copyright © 2004, Dan Villiom P. Christiansen · Copyright © 2005-2026, Fabian Greffrath · Copyright © 2026, Nils Schimmelmann
    knik0-faac-1950446/docs/libfaac.pdf000066400000000000000000000616711522734263700166460ustar00rootroot00000000000000%PDF-1.2 %âãÏÓ 12 0 obj << /Length 13 0 R /Filter /FlateDecode >> stream H‰•“ÝnÛ0 …ŸÀïÀËt€e’²~¼;7MŠ6`F¯z“Únš¡K 7«±·Ÿ$ˋӤ( †lQG‡ÉË*1(%Œ¨®ÿtkH²%‚EÁì6î¤[ÕnªftÕ¯D VáÏUx»]AVÛ!BOB1©©JB¤¿~IQ0¡ŒZÈLQkY–ó(¨?ªH2XªÃÚègYß÷båÔÁˆ¨w¿³ °¨r áÚ‚CÈøôP\÷Ée•H×ðšét§¾f·ç§ï\zVOÓ{¯¦"â•T1mVŒHÖùÿd”ÐmÖûggü¹í^ÚFÄÙ,Þ÷ä0ü%½ü› endstream endobj 13 0 obj 461 endobj 4 0 obj << /Type /Page /Parent 5 0 R /Resources << /Font << /F0 6 0 R /F1 8 0 R /F2 10 0 R >> /ProcSet 2 0 R >> /Contents 12 0 R >> endobj 17 0 obj << /Length 18 0 R /Filter /FlateDecode >> stream H‰ÕWMoÛ8ýþ<¶ÓäðûØuâ"@] Þ=õ¢UdC‹@NeE÷ׯDÒiÉŽÉÁ6#žJïÍ›yüm9Q ¤V¡åÝ„ ú·\£ÉlAÂÆT_¬&S©ªOiõ5Zþ@à#Zþ3„ýÏý[}‹©–Ú… A¦@EXejCX}¼ ™ ”0_‹P_kñéÓÜ”}+Rf)µŸ•ä¾äÃã×ÙïÜF0ã¨._¤›§¬D_ò¿Ë¤ü‰þ¢˜ØrT‚¯6[PD)– ,‡²ŒCøÒS ¬~JÉÐV™€h—Ò6Õ&i>;u‡Õ”ĘF#Là DBXe¾)vY±ÛÚH¥Ú V›‚©ƒ¦Tt¾"&Ô²"Š)Æ¥6|°lJ· ‘Q€9öÊ¡\2€Å$‰éÇç~lª!Í£V§ér=ð²ùr¶`mš«G7,FŽiæ cø xÇC"Žîyç m‚DÜGQÑ#”p±ç¡ £4PÛRê¹Q­ÝK…mܰƒÙ¸’ ‹zÀpEÃCÓÍKæ„ÒœTÃbt–Û¡½VNÓ]Ÿðnm°÷Ó»…6¨ò{ 3cK•÷Pé£\%i†ž²mZæ/»|S8Ì€tËåµùÒŸ+ªU7êü½4ÀÌ›MÕöiv*fxäþÜ&ë_Y " ¥'XS&ÁñbQëÆK3sƒ½#нû€ž1,õþUœû,®Á›¡yòüœk´Í¾ï³"u,3¦.ÁÞ™LLçzƒ¥qŽ‚¶:Ä-6QB `Ê%.öEZO!Tf«¬²*7°+¯_ò×/ ¾+ ‘.ñ.Ù%h»+÷én_fÛÓ[˜ c‹âì ³áBwo£óJ8Fò·®j±N#ÙáaxØð€ixe.¯ §1~ äu'PT®QÇ%Ò½þýròˆXª endstream endobj 18 0 obj 1044 endobj 14 0 obj << /Type /Page /Parent 5 0 R /Resources << /Font << /F0 6 0 R /F1 8 0 R /F2 10 0 R /F3 15 0 R >> /ProcSet 2 0 R >> /Contents 17 0 R >> endobj 20 0 obj << /Length 21 0 R /Filter /FlateDecode >> stream H‰•”ÝnÛ0 …ŸÀïÀËíŠHY’}™5É0`ÃÔØ½k+©‹Í.”¤ÅÞ~úsc§ºÁ€aˆÔ'òÊŸêLs&AK¦%@½É8øÇ [í8hVUD.°Ï8C¥ÝWëÂP?Ãñê‡L2’aeÞ.ʰTeLB³†„rNÉCŠðÇÇ”œ3B.‹abíÖë›TÿKDJj÷VEB~¹ý¾úöcûhU€Ço‡v쌅¯ýmìø‰Œ*J´Õ‘)’Q– + š7# _¥**&“<0(;¥TrÔŒ.ñIyôÂê2œ襘7I±*ïÝR|.p!þ,Åa8*5Çܶ㣉>â; <ؘ£|K<ÙÀ Qʸµ¾ïÐíù·NÐP¥.Öøm©"ï»*î3ÇÖöwæ§{ýp2vß´š¡ƒó±9÷>§ÊEéox!¨ðgNŧe¯¾`S?tÕ+FO<ý2…—!z.¬X¸“¿º®O-^&Å?`l°à× É+sJz·Ãåܺ«îAàñBGÜÆ<™_n&:Ø6¨¿³Æ<7ÖÀº{j†ÖEÖç®áfìúáv|0í‰EM¤¸þ¼vc[gè°ÿH endstream endobj 21 0 obj 467 endobj 19 0 obj << /Type /Page /Parent 5 0 R /Resources << /Font << /F0 6 0 R /F1 8 0 R /F2 10 0 R >> /ProcSet 2 0 R >> /Contents 20 0 R >> endobj 23 0 obj << /Length 24 0 R /Filter /FlateDecode >> stream H‰•UÉnÛ0üÿÃ;6LsÅÜÒ,E€¦-Pw9äÂÈ”­V‘ I‰›¿/7m­Ó 0¼€zΛ™G¿]/$F¤@R¬/Ü«ÙÂbuA"¥(µòF$‘öWfÃúoø ¬,¢Â¯\úOû‘4IC ø:)A„1EYúæŽ%KŒ(Á,baJIĺ>?¿ˆ€Éÿ"æ)eþ·Lx„¼ùüquûéêÐUeõÆ4ð¾¸otó _ ÂŽ$4¢­® ‚*‚,=,ãtÚô’0f…M¸B^Øa=QŽÙ’HDE¤@œŒ2õˆ+eÓ–XààœšK™I=-ADð4Ö%T†²›ª3M®3ÓfM±ïŠº [í‘ÿ·-qGzIÄ1%–Î-ÕŸHD"Âæõμ.ô¾©Ÿ K 4ìŠí.XHåÌDLeú·Ñ“ú¢G(ŽÙ)Í“)cŒ±¡·ù4„d‘„ Å S^7`Ù¢Ú‚k‚®61‡˜Ì 2®úö9UÊíáNßw^”¦Eà4 t"†Õœˆ"½ǰæuYÖÇbg´“ÎAAÑöÒm<ÕÇVoíj«‹ÓS÷lÛè‡ö,š¨™ÅGRëJzuâžóYYN¹šE5×:CÑ?õÂÈX¡ðè’ì•:ƒü±Ê\ ï®îž÷¦ Ù¤ì5ân@—„÷C5á~lm*6'd0M‘1³&F4«i}<­²ÚNwE¦ËòÊ¢ú©ï­e ²[6›ñ¤ñI† Q‰cÐì6¯˜ý†»7~W‡vWð&û„ñ4Žôm‘5u[ç|+ªM}ˆ’átÌæ’þ†µ×gº;› ž‘îù¤ü8ëP|êý—7ßÃ`¨q(Ýt“«þp?VjÝ ³‘&h:~zÞLÉÈûI7EýØ‚ ÷›þ9`êsâUÇ)‡ýÒ{ëY?Ðítí®>€{wµ#cWQÈ3Æ£¤/ñj½ø k »„ endstream endobj 24 0 obj 747 endobj 22 0 obj << /Type /Page /Parent 5 0 R /Resources << /Font << /F0 6 0 R /F1 8 0 R /F2 10 0 R >> /ProcSet 2 0 R >> /Contents 23 0 R >> endobj 30 0 obj << /Length 31 0 R /Filter /FlateDecode >> stream H‰ÅWÙŽÛ6ýÿ'@Ä!).Rû4uf‚-&@Ü>Í -Ó3*dÑÕÃýúrÕâ%nú @` ÉË{Î=÷\ê—ÕB È€`P0VØÍ+XÜ?! `žb¶ 1æWa–ÁêîØ;°úkÁ aî/ÜÿfâŒg~ p[Èd iji”ÄmIíõ~K‚ Á( ±!8ÄzzxX†€ü[#âÔ¥T¸ß‚Óò×ÏÏ÷¿züÈ=6üc]èjÀo庑Íü‰!rá0'!ÚýCN˜§%†M)™‚NJm–œFjÍI°åKdî¨;”¥ÓÜ©¿Ì–dÎ)JñŒÓéˆ0ͦQþhå«òÕ!7#%"5{"Æ4)À)ÌCš9'³4!öYd#Ë(bzA>)Oƒ„`F¸ß´”UUÖ¯ U÷ª.|Þã[ažƒ HØ¥²ØGȈ¤þè“n€²5¶7Ú‚¯Ë®í%w-èÞØêªÒ»Xœ¤ÊÖߋ̭4Þj1aʧÜìd½‘nŽ?¹í<;m Zñ›l+áÁ°S0¹˜‰ü…pq­ÿPžÎµrEº.y1ô!1+[’ÐiÌêgTŸIßË‚ð Ãy8½•²0}ô¼WõËÝË» aE6‰t^-B"RBÙr}Q¦Uh̲n;iËqÔ=¨•Ú@Ÿ'ð&Ë)sOð¤#¿Ïb͈t+mÔÕ Ðô¾+uݾwÒµ6jâ”|2Ë4ÐþQu˾iTÝ-u½-_û`_bÌßÂLÌ»FÚ[m¡HÎ6Ö0/\Ô˜Þ8e'öáõBqžÒÁ#1áA¡à½‡®ëê#èÔØ£tMmÑÕr¬YºîʺWÖ(MÙ) Mt×F½Y©€…/ûM©Ï­ÃÍøìÕúçÕØ"#¦4$`¼É/¦q•±ßÿáÍ`£·[Å|ËøaöÝ{q$gÑG\³øI2%çM‚ÿ›Fè˜â¹L‚±º\pphCf;ŽâQ2ëcg˜¶eíTý>’gÀÎs6<ÌwXø´…OÒiB§º§$¿jYévÄ”‹¯<`0œ9'ñkÂÈq£Œ©kûÎ0MzúÔ jÉÙíÑý¸Zü i'¦Ò endstream endobj 31 0 obj 1219 endobj 25 0 obj << /Type /Page /Parent 5 0 R /Resources << /Font 34 0 R /ProcSet 2 0 R >> /Contents 30 0 R >> endobj 34 0 obj << /F0 6 0 R /F1 8 0 R /F2 10 0 R /F4 26 0 R /F5 28 0 R /F6 32 0 R >> endobj 38 0 obj << /Length 39 0 R /Filter /FlateDecode >> stream H‰ÝWÛNãF~‚¼Ã\†ª1szÅrh‘hA»i¯¸1É„¸Jlä8lÙ§ïœlÏØN“E+$È?ÿáûþã§ùDÀ„ÁÁ˜_M 0?Õ3˜œß@ )1Ö_¬&0A\èO ý5˜S~æOX‚™ýÏ•ý­¿MPÊS'¬D„ µÌ¬1æÈ &AâuAŒ‘×usqqéòïÕˆˆuia? N½ÊÛ/÷ç¿?\ÿ ð9Fýu±(—ªwùS•Uoà/”@«qìµß €PÂ1s°4j ÅaÐ3 ‰ñ’ÓZýdð©}j¥$ô9c†’SHP„i(¢#‚´Ž¤Ü Þì‹E—¨ÔJUªX( 9ª}†Rª…fXt¾S€H’B*Zß%Ç‘ï rÒú˜ ZÛœ!ïcØSs[äužmòo™ á\yï!O^¡ŒRÒQOC „¹´"ÓpcîüÔ¥9ÚUÓ4N‹Ád÷(˜·„40×ûª¯Ùfï‡Û…é»JЀ´×âÍ„¿(‚Ù¾l{ÛÚnÒ ¸]éñÿV¯s½.=—šß¯•Ùœ\ø.² ÆÒ>ÚŽ·H8÷ÿøóîÎ+°Oÿ£H1£­ÇŒø÷ù®é¤”¦%Ž3 ²Xª¥ã‹í®ƒRÆDŸd¬í='\dø=Y:v‘]nÊ]—Œ=ȨkÇé¡üú¿®1Ô¥U¿CÒ.Ž§Þ¿×2_öï0Ü÷\Tô¦úû¯°è2H1jdÅ2bÀŸk7M*báSÜuR?ÌM0 ŽÓ1¤§ÖÒ¸Óm!X„Ü5 [ôá2sR?Ö`¶,š†Z£ç³µÆî×~±5”yczÛzs± ÇÝ ²Y‹¯s-*ÇÇæåñ¼¸žOþѳ endstream endobj 39 0 obj 1428 endobj 35 0 obj << /Type /Page /Parent 5 0 R /Resources << /Font 42 0 R /ProcSet 2 0 R >> /Contents 38 0 R >> endobj 42 0 obj << /F0 6 0 R /F1 8 0 R /F2 10 0 R /F4 26 0 R /F5 28 0 R /F6 32 0 R /F7 36 0 R /F8 40 0 R >> endobj 45 0 obj << /Length 46 0 R /Filter /FlateDecode >> stream H‰ÕWÙnÛFýýÃ<ÚA9ž}Až/©1`£@¿ÐÒÈf“E"p¿¾ÃY¨á"[vœ…ïÜõÜsï|º^H9JÀõéîSßÅÑ9jMˆ}±^ ˆ…´OKû\ò\ÿ³àp÷Ï©û¶o!VByàDH"1Á<Õ’9Ú™÷"‚#t!BpÐu~||Š×jÄÔ¹´tÏR° òâêËÑŸ—gŸ9b SV.«•©ÁÅm×Oà/ ‘S‡ ÚŽÎ1À Â}Z¢ZÊHtFí¼L…ÔÚ“ ` bÒíiARßm6½9µu¿·€””© ÄÇuL˜ÄÔ ÅX–U¹.îÚ:oŠªôÚWmF}†%á0z/&r,#ß!öõQpŒ ŠM•‹mùØPÍ:Ï—ÖõϦ9iëÚ”ÍIêÿÍÁÍ¡·Ø8ES+Úº’aB@ié|Ìã—uÕTÍÓ£ñØæp’¥q™L†I0 ذðåñ {ÚÆ7ˆê²©¼//|pXŒí’x>ÂËŠdMÉô¨ŒDHOvÓMiªRå8ößd"Î÷“Áų—œ¶C^àöŸàƒRÌË—ýÎpïh(¤Þðåԓ蟙ˆ .á´×â:M Ýq m7˜&/Ê ÈÛUQExvWÏ[·‚a«™Û#¦—ª_€¤N-Žä”žÝY¹‡÷g׋ÿñÞq endstream endobj 46 0 obj 1246 endobj 43 0 obj << /Type /Page /Parent 44 0 R /Resources << /Font 47 0 R /ProcSet 2 0 R >> /Contents 45 0 R >> endobj 47 0 obj << /F0 6 0 R /F1 8 0 R /F4 26 0 R /F5 28 0 R /F6 32 0 R /F7 36 0 R >> endobj 49 0 obj << /Length 50 0 R /Filter /FlateDecode >> stream H‰•UÛnÛ8ýÿÃ<¦‹˜!)Q”“&Y,°EŠÖèS^h™Ž¹P¤@¢’M¿¾Ã›,'v ¶a‘3gΜ9sµYIJHA¤Ø\¯(¸Wÿ«‹[ ’Tçø`¿¢„Õø6/pV~‚Í?+A¸ðÿ\ûO|JXY”áø#|v„0ÎÄ<ÊÚÉ\úpdM g4‹±(ç,ƺ½¼ü¿‘eRíË"!ÿú~wñåëÍŸÀ/rpáoÚºÛéþ6Û^õ¯ðƒêñ‚Çh· #–6Ëù¼è5ce‘WD$”,§LTáÂæ ¡·˜·ÛójÌõøÔèL‹ï§Ñ^û½îÏÁÌá›luHEaÍxìŸ,EéãU‘…$ö˜¤×µ6Ïz炇â2‡1•Wœ”Gh)Oèõp¾xþv™ÌŒ£šQ‘`R#1­CS«¦ÛEº8ÏÚ‰P¸¢«æ0öJÕØ­»'ÝÞŸÝòA*êXXÓÄÅ{@TðX É *B¨sP4AYò(<É<é0¯"ü®}pçíAÙYצ~ SûËY=¾ÝÅÔz!‹L=+Ó¨m£ ܵµ†×n„ƒzä`‡B{Êò¨÷÷ý9mOä%yb†Q± _¼O'`¨ò ^Œ=ÀOÝOý©2 š+-wñjUÆ«Gé¢PÎ}«÷Í8 ÷Ôwµ*^ñ•ˆ·ý­NÙ¸ç…\²)Ze'6Å&Ü…¥’ÏÃv£fÒß•|¦›%ðáКe3CȪ4 ,—Qa_;ÓZ”Ά‚­OgÖQèøÜ;Ø^«GØ)«lœ+Ä“ï-ÁáÏ“ì)/£ì£‹ Œ­ëìÄ ÙüŒ¶Â]ÔMB=ªï%¯VÏFqòßÔ–³!9Y¤wœI‰ÎMkr( ‘¿‘¦ÛAL&ä“v©²¥Âp—œ4zÙe¦‰e¬”¼]CË›÷™<ÁïVï[VÓõ,–óMÛ±oÝÂé2ÿ#$#YþkAƚ¥ý{ ­~P{2:yšvgjœaÔélìu˜÷ù6Ãe¶EU GaN™]¦|f>Ébc‹Ã”%]wøWÿb´ÊK‡cé.7ª]‡YÚΆL›?–åi朖)~„A“Ø×ÿ îf³ú?œ> /ProcSet 2 0 R >> /Contents 49 0 R >> endobj 52 0 obj << /Length 53 0 R /Filter /FlateDecode >> stream H‰ÅW[OãFþü‡y¤1sOûD¹tWbT¢Ý—¾˜d¼ 6²¥lÕÿÞ¹šÛQÒÕŠ L|æ|g¾sÿm~$`Æ€`™`Ì/Ž 0?Í^A 2)1Ö/VG0C\è§…~ æÏàXþæ_ŽX†™ýæÂ~ê·ÊyîD€Á‘H†0b±–™!Þ‰Ì`†$^Äy]Wggç^!ÿ¯±&-ì³àÔ«|wsúáöòw€O)0ê/«E½T ¸.yŸP­:ı×vz…BÇÌÑÔŠãKÏ0$ÆJNµú$Èð%r{ÔÊIl;w`Æ%)§ „ÓXD;†z1L±»(º´]³]tÛFµ Q+Õ¨j¡ƒ¿ò³ c†©eq†Åë(@$Ë!ý$ÇÉ2ä,̇ZæBÄ2r“±šUQ,´7ÎëjU®·MÑ•uåôR8 ¹ Å3™©¦ÃØoÒòB­Êªì\øˆˆ_’ç¥ãÞ1aÃ0µì!ìñy7:´Y8é¾Ô'»—'µT+ï8°“í™!::‚ówð$c}bá$Sþ¶’|Ÿ&nî"Àp-)ïãÍ>jÙmÕ–ëJ-AYuàñI­?©¦Õæþêù"„ ¼Ô²PW$›ÀÒÝÜQ‹n®¹óh¹ÈöƒY¡„y¨;ˆÙÇØfS?(—m¹ì±àDœÁ …øf,$†a>·mÕõ*‘©„9©¢!îšWíwÅ7B(X†û˜î6uµ÷e÷GÑ…;1|HX8©ÔUŠà*ÅÄ•î‹jù¹\vJr+'•Þj5ÿ–5OÄ2d}Îã>œ§²üü<ñµË}‰³¨µA‘XWÝv»1¡û“€`¡MŒ:Ô°FB‰yZ#ÛES>õ…ˆO€ ë •‰"rÐA-/ˆö•IßñçM½]?€î¡l_[x©·`QT`ñPTk¥ß* \ÛöôÈj³—Ь2&T.b‡döœû¯ke’ëŽH¥Å@W¥Ú,[ç´©„"A>œ^±‘ÛÒ¨øs±k\ƒ’¤£Å®IÇH&­>*óŽ6‘b#zب-AŽú†‚$ÞWÀÎf_BεÓïµ³KíòÆÛ*yïAƒ5Ð) F¦ÆD0"ˆ‚ñré­¯=$ŽÆ1$CRÉâcb1Ïݱãl8¼îèúW”¼o½·œ|Ò€íÙœíÏ+“¦G¢—ƹx 3™×˜©„ºÒ¿+SZ¾›­jñN’Ö„WjF~ÆÆÙûî¤ugÑÑ]¾2[E(á'ý8»rÐF®o>Gñ´p§ÿvþZb·„ ·!lQ”›£Q§d\ÏoièG,s̾uÀÚú"’Í5žâ\ámCS… &+³AÈžr̉¯\wJÇi ùpM2 Æ,ú3g$2Èv²m[¬mP?–ËScЭ¬Ö'ZÃJû²ªCSåQ 5Î÷¥ÙΘ·| ÄùJŽF—áM"ƒæøv®b<).nv1Æ©Š|ä$HrÄ¡”dì$ bkÉõÕ¥**µÉB¡qÙ5òPîmE¡,”,=¯¬6Åè¿UÝvûôT7L=¿Nk:¤sÌi|óÕùã‡v ù¿¸Õͺ±WõºáŒžËÇVO,50σ!9aÓ^¼‹2Gÿ—ùš ™Qº!¾]ÐT­ß™ì1 ÷÷V+“tÖSEsoÅ»ZÏœ¶æhõzÆUzk2-xÒÓU«ôô»´!#s9ÞK[­ß|<ÍÚëiÓà³Ù]†{÷8,¯[ „y{,\ó‘ÑúP?`_jþ*·ÀYÓ=šß}sl"ò‹Šs9?ú,oé$ endstream endobj 53 0 obj 1415 endobj 51 0 obj << /Type /Page /Parent 44 0 R /Resources << /Font 54 0 R /ProcSet 2 0 R >> /Contents 52 0 R >> endobj 54 0 obj << /F0 6 0 R /F1 8 0 R /F2 10 0 R /F4 26 0 R /F5 28 0 R /F6 32 0 R >> endobj 6 0 obj << /Type /Font /Subtype /TrueType /Name /F0 /BaseFont /Verdana,Italic /FirstChar 32 /LastChar 255 /Widths [ 352 394 459 818 636 1076 727 269 454 454 636 818 364 454 364 454 636 636 636 636 636 636 636 636 636 636 454 454 818 818 818 545 1000 683 686 698 766 632 575 775 751 421 455 693 557 843 748 787 603 787 695 684 616 732 683 990 685 615 685 454 454 454 818 636 636 601 623 521 623 596 352 622 633 274 344 587 274 973 633 607 623 623 427 521 394 633 591 818 592 591 525 635 454 635 818 1000 636 1000 269 636 459 818 636 636 636 1519 684 454 1070 1000 685 1000 1000 269 269 459 459 545 636 1000 636 977 521 454 980 1000 525 615 352 394 636 636 636 636 454 636 636 1000 545 645 818 454 1000 636 542 818 542 542 636 642 636 364 636 542 545 645 1000 1000 1000 545 683 683 683 683 683 683 989 698 632 632 632 632 421 421 421 421 766 748 787 787 787 787 787 818 787 732 732 732 732 615 605 620 601 601 601 601 601 601 955 521 596 596 596 596 274 274 274 274 612 633 607 607 607 607 607 818 607 633 633 633 633 591 623 591 ] /Encoding /WinAnsiEncoding /FontDescriptor 7 0 R >> endobj 7 0 obj << /Type /FontDescriptor /FontName /Verdana,Italic /Flags 96 /FontBBox [ -250 -210 1830 1005 ] /MissingWidth 456 /StemV 92 /StemH 92 /ItalicAngle -11 /CapHeight 1005 /XHeight 503 /Ascent 1005 /Descent -210 /Leading 215 /MaxWidth 1525 /AvgWidth 508 >> endobj 8 0 obj << /Type /Font /Subtype /TrueType /Name /F1 /BaseFont /Verdana /FirstChar 32 /LastChar 255 /Widths [ 352 394 459 818 636 1076 727 269 454 454 636 818 364 454 364 454 636 636 636 636 636 636 636 636 636 636 454 454 818 818 818 545 1000 684 686 698 771 632 575 775 751 421 455 693 557 843 748 787 603 787 695 684 616 732 684 989 685 615 685 454 454 454 818 636 636 601 623 521 623 596 352 623 633 274 344 592 274 973 633 607 623 623 427 521 394 633 592 818 592 592 525 635 454 635 818 1000 636 1000 269 636 459 818 636 636 636 1521 684 454 1070 1000 685 1000 1000 269 269 459 459 545 636 1000 636 977 521 454 981 1000 525 615 352 394 636 636 636 636 454 636 636 1000 545 645 818 454 1000 636 542 818 542 542 636 642 636 364 636 542 545 645 1000 1000 1000 545 684 684 684 684 684 684 984 698 632 632 632 632 421 421 421 421 775 748 787 787 787 787 787 818 787 732 732 732 732 615 605 620 601 601 601 601 601 601 955 521 596 596 596 596 274 274 274 274 612 633 607 607 607 607 607 818 607 633 633 633 633 592 623 592 ] /Encoding /WinAnsiEncoding /FontDescriptor 9 0 R >> endobj 9 0 obj << /Type /FontDescriptor /FontName /Verdana /Flags 32 /FontBBox [ -250 -210 1822 1005 ] /MissingWidth 453 /StemV 92 /StemH 92 /ItalicAngle 0 /CapHeight 1005 /XHeight 503 /Ascent 1005 /Descent -210 /Leading 215 /MaxWidth 1518 /AvgWidth 508 >> endobj 10 0 obj << /Type /Font /Subtype /TrueType /Name /F2 /BaseFont /Verdana,Bold /FirstChar 32 /LastChar 255 /Widths [ 342 402 587 867 711 1272 862 332 543 543 711 867 361 480 361 689 711 711 711 711 711 711 711 711 711 711 402 402 867 867 867 617 964 776 762 724 830 683 650 811 837 546 555 771 637 948 847 850 733 850 782 710 682 812 764 1128 764 737 692 543 689 543 867 711 711 668 699 588 699 664 422 699 712 342 403 671 342 1058 712 687 699 699 497 593 456 712 650 979 669 651 597 711 543 711 867 1000 711 1000 332 711 587 1049 711 711 711 1777 710 543 1135 1000 692 1000 1000 332 332 587 587 711 711 1000 711 964 593 543 1068 1000 597 737 342 402 711 711 711 711 543 711 711 964 598 850 867 480 964 711 587 867 598 598 711 721 711 361 711 598 598 850 1182 1182 1182 617 776 776 776 776 776 776 1094 724 683 683 683 683 546 546 546 546 830 847 850 850 850 850 850 867 850 812 812 812 812 737 735 713 668 668 668 668 668 668 1018 588 664 664 664 664 342 342 342 342 679 712 687 687 687 687 687 867 687 712 712 712 712 651 699 651 ] /Encoding /WinAnsiEncoding /FontDescriptor 11 0 R >> endobj 11 0 obj << /Type /FontDescriptor /FontName /Verdana,Bold /Flags 16416 /FontBBox [ -250 -210 2129 1005 ] /MissingWidth 542 /StemV 181 /StemH 181 /ItalicAngle 0 /CapHeight 1005 /XHeight 503 /Ascent 1005 /Descent -210 /Leading 215 /MaxWidth 1774 /AvgWidth 568 >> endobj 15 0 obj << /Type /Font /Subtype /TrueType /Name /F3 /BaseFont /TimesNewRoman /FirstChar 32 /LastChar 255 /Widths [ 250 333 408 500 500 833 778 180 333 333 500 564 250 333 250 278 500 500 500 500 500 500 500 500 500 500 278 278 564 564 564 444 921 722 667 667 722 611 556 722 722 333 389 722 611 889 722 722 556 722 667 556 611 722 722 944 722 722 611 333 278 333 469 500 333 444 500 444 500 444 333 500 500 278 278 500 278 778 500 500 500 500 333 389 278 500 500 722 500 500 444 480 200 480 541 778 500 778 333 500 444 1000 500 500 333 1000 556 333 889 778 611 778 778 333 333 444 444 350 500 1000 333 980 389 333 722 778 444 722 250 333 500 500 500 500 200 500 333 760 276 500 564 333 760 500 400 549 300 300 333 576 453 250 333 300 310 500 750 750 750 444 722 722 722 722 722 722 889 667 611 611 611 611 333 333 333 333 722 722 722 722 722 722 722 564 722 722 722 722 722 722 556 500 444 444 444 444 444 444 667 444 444 444 444 444 278 278 278 278 500 500 500 500 500 500 500 549 500 500 500 500 500 500 500 500 ] /Encoding /WinAnsiEncoding /FontDescriptor 16 0 R >> endobj 16 0 obj << /Type /FontDescriptor /FontName /TimesNewRoman /Flags 34 /FontBBox [ -250 -216 1166 1000 ] /MissingWidth 324 /StemV 73 /StemH 73 /ItalicAngle 0 /CapHeight 891 /XHeight 446 /Ascent 891 /Descent -216 /Leading 149 /MaxWidth 972 /AvgWidth 401 >> endobj 26 0 obj << /Type /Font /Subtype /TrueType /Name /F4 /BaseFont /Arial,BoldItalic /FirstChar 32 /LastChar 255 /Widths [ 278 333 474 556 556 889 722 238 333 333 389 584 278 333 278 278 556 556 556 556 556 556 556 556 556 556 333 333 584 584 584 611 975 722 722 722 722 667 611 778 722 278 556 722 611 833 722 778 667 778 722 667 611 722 667 944 667 667 611 333 278 333 584 556 333 556 611 556 611 556 333 611 611 278 278 556 278 889 611 611 611 611 389 556 333 611 556 778 556 556 500 389 280 389 584 750 556 750 278 556 500 1000 556 556 333 1000 667 333 1000 750 611 750 750 278 278 500 500 350 556 1000 333 1000 556 333 944 750 500 667 278 333 556 556 556 556 280 556 333 737 370 556 584 333 737 552 400 549 333 333 333 576 556 278 333 333 365 556 834 834 834 611 722 722 722 722 722 722 1000 722 667 667 667 667 278 278 278 278 722 722 778 778 778 778 778 584 778 722 722 722 722 667 667 611 556 556 556 556 556 556 889 556 556 556 556 556 278 278 278 278 611 611 611 611 611 611 611 549 611 611 611 611 611 556 611 556 ] /Encoding /WinAnsiEncoding /FontDescriptor 27 0 R >> endobj 27 0 obj << /Type /FontDescriptor /FontName /Arial,BoldItalic /Flags 16480 /FontBBox [ -250 -212 1174 1000 ] /MissingWidth 326 /StemV 153 /StemH 153 /ItalicAngle -11 /CapHeight 905 /XHeight 453 /Ascent 905 /Descent -212 /Leading 150 /MaxWidth 978 /AvgWidth 479 >> endobj 28 0 obj << /Type /Font /Subtype /TrueType /Name /F5 /BaseFont /Symbol /FirstChar 30 /LastChar 255 /Widths [ 600 600 250 333 713 500 549 833 778 439 333 333 500 549 250 549 250 278 500 500 500 500 500 500 500 500 500 500 278 278 549 549 549 444 549 722 667 722 612 611 763 603 722 333 631 722 686 889 722 722 768 741 556 592 611 690 439 768 645 795 611 333 863 333 658 500 500 631 549 549 494 439 521 411 603 329 603 549 549 576 521 549 549 521 549 603 439 576 713 686 493 686 494 480 200 480 549 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 620 247 549 167 713 500 753 753 753 753 1042 987 603 987 603 400 549 411 549 549 713 494 460 549 549 549 549 1000 603 1000 658 823 686 795 987 768 768 823 768 768 713 713 713 713 713 713 713 768 713 790 790 890 823 549 250 713 603 603 1042 987 603 987 603 494 329 790 790 786 713 384 384 384 384 384 384 494 494 494 494 600 329 274 686 686 686 384 384 384 384 384 384 494 494 494 600 ] /FontDescriptor 29 0 R >> endobj 29 0 obj << /Type /FontDescriptor /FontName /Symbol /Flags 6 /FontBBox [ -250 -220 1249 1005 ] /MissingWidth 333 /StemV 109 /StemH 109 /ItalicAngle 0 /CapHeight 1005 /XHeight 503 /Ascent 1005 /Descent -220 /Leading 225 /MaxWidth 1041 /AvgWidth 600 >> endobj 32 0 obj << /Type /Font /Subtype /TrueType /Name /F6 /BaseFont /CourierNew,Bold /FirstChar 32 /LastChar 255 /Widths [ 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 ] /Encoding /WinAnsiEncoding /FontDescriptor 33 0 R >> endobj 33 0 obj << /Type /FontDescriptor /FontName /CourierNew,Bold /Flags 16418 /FontBBox [ -250 -300 716 1000 ] /MissingWidth 597 /StemV 191 /StemH 191 /ItalicAngle 0 /CapHeight 833 /XHeight 417 /Ascent 833 /Descent -300 /Leading 133 /MaxWidth 597 /AvgWidth 600 >> endobj 36 0 obj << /Type /Font /Subtype /TrueType /Name /F7 /BaseFont /Arial,Bold /FirstChar 32 /LastChar 255 /Widths [ 278 333 474 556 556 889 722 238 333 333 389 584 278 333 278 278 556 556 556 556 556 556 556 556 556 556 333 333 584 584 584 611 975 722 722 722 722 667 611 778 722 278 556 722 611 833 722 778 667 778 722 667 611 722 667 944 667 667 611 333 278 333 584 556 333 556 611 556 611 556 333 611 611 278 278 556 278 889 611 611 611 611 389 556 333 611 556 778 556 556 500 389 280 389 584 750 556 750 278 556 500 1000 556 556 333 1000 667 333 1000 750 611 750 750 278 278 500 500 350 556 1000 333 1000 556 333 944 750 500 667 278 333 556 556 556 556 280 556 333 737 370 556 584 333 737 552 400 549 333 333 333 576 556 278 333 333 365 556 834 834 834 611 722 722 722 722 722 722 1000 722 667 667 667 667 278 278 278 278 722 722 778 778 778 778 778 584 778 722 722 722 722 667 667 611 556 556 556 556 556 556 889 556 556 556 556 556 278 278 278 278 611 611 611 611 611 611 611 549 611 611 611 611 611 556 611 556 ] /Encoding /WinAnsiEncoding /FontDescriptor 37 0 R >> endobj 37 0 obj << /Type /FontDescriptor /FontName /Arial,Bold /Flags 16416 /FontBBox [ -250 -212 1165 1000 ] /MissingWidth 323 /StemV 153 /StemH 153 /ItalicAngle 0 /CapHeight 905 /XHeight 453 /Ascent 905 /Descent -212 /Leading 150 /MaxWidth 971 /AvgWidth 479 >> endobj 40 0 obj << /Type /Font /Subtype /TrueType /Name /F8 /BaseFont /TrebuchetMS /FirstChar 32 /LastChar 255 /Widths [ 301 367 325 524 524 600 706 160 367 367 367 524 367 367 367 524 524 524 524 524 524 524 524 524 524 524 367 367 524 524 524 367 771 590 566 598 613 536 525 676 654 278 477 576 506 709 638 674 558 676 582 481 581 648 587 852 557 570 550 367 355 367 524 524 524 525 557 495 557 545 370 502 546 285 367 504 295 830 546 537 557 557 389 405 396 546 490 744 501 493 475 367 524 367 524 500 524 500 367 388 524 734 459 459 524 913 481 367 993 500 550 500 500 367 367 524 524 524 367 734 524 635 405 367 924 500 475 570 301 367 524 524 524 570 524 454 524 713 367 524 524 367 713 524 524 524 451 454 524 546 524 367 524 451 367 524 814 814 814 367 590 590 590 590 590 590 867 598 536 536 536 536 278 278 278 278 613 638 674 674 674 674 674 524 657 648 648 648 648 570 556 546 525 525 525 525 525 525 873 495 545 545 545 545 285 285 285 285 549 546 537 537 537 537 537 524 545 546 546 546 546 493 553 493 ] /Encoding /WinAnsiEncoding /FontDescriptor 41 0 R >> endobj 41 0 obj << /Type /FontDescriptor /FontName /TrebuchetMS /Flags 32 /FontBBox [ -250 -222 1104 1000 ] /MissingWidth 340 /StemV 83 /StemH 83 /ItalicAngle 0 /CapHeight 939 /XHeight 470 /Ascent 939 /Descent -222 /Leading 161 /MaxWidth 920 /AvgWidth 454 >> endobj 2 0 obj [ /PDF /Text ] endobj 5 0 obj << /Kids [4 0 R 14 0 R 19 0 R 22 0 R 25 0 R 35 0 R ] /Count 6 /Type /Pages /Parent 55 0 R >> endobj 44 0 obj << /Kids [43 0 R 48 0 R 51 0 R ] /Count 3 /Type /Pages /Parent 55 0 R >> endobj 55 0 obj << /Kids [5 0 R 44 0 R ] /Count 9 /Type /Pages /MediaBox [ 0 0 596 842 ] >> endobj 1 0 obj << /Creator /CreationDate (D:20010518230136) /Title /Author /Producer (Acrobat PDFWriter 5.0 for Windows NT) >> endobj 3 0 obj << /Pages 55 0 R /Type /Catalog >> endobj xref 0 56 0000000000 65535 f 0000023881 00000 n 0000023561 00000 n 0000024205 00000 n 0000000578 00000 n 0000023592 00000 n 0000011365 00000 n 0000012468 00000 n 0000012734 00000 n 0000013830 00000 n 0000014087 00000 n 0000015193 00000 n 0000000019 00000 n 0000000558 00000 n 0000001863 00000 n 0000015461 00000 n 0000016552 00000 n 0000000720 00000 n 0000001842 00000 n 0000002583 00000 n 0000002018 00000 n 0000002563 00000 n 0000003571 00000 n 0000002726 00000 n 0000003551 00000 n 0000005032 00000 n 0000016813 00000 n 0000017910 00000 n 0000018181 00000 n 0000019247 00000 n 0000003714 00000 n 0000005011 00000 n 0000019505 00000 n 0000020595 00000 n 0000005142 00000 n 0000006755 00000 n 0000020862 00000 n 0000021953 00000 n 0000005228 00000 n 0000006734 00000 n 0000022216 00000 n 0000023302 00000 n 0000006865 00000 n 0000008318 00000 n 0000023700 00000 n 0000006973 00000 n 0000008297 00000 n 0000008429 00000 n 0000009498 00000 n 0000008515 00000 n 0000009478 00000 n 0000011168 00000 n 0000009654 00000 n 0000011147 00000 n 0000011279 00000 n 0000023789 00000 n trailer << /Size 56 /Root 3 0 R /Info 1 0 R /ID [<41022e51d44c0d7cc8d6eac38733ab06><41022e51d44c0d7cc8d6eac38733ab06>] >> startxref 24255 %%EOF knik0-faac-1950446/docs/meson.build000066400000000000000000000000261522734263700167170ustar00rootroot00000000000000install_man('faac.1') knik0-faac-1950446/frontend/000077500000000000000000000000001522734263700154465ustar00rootroot00000000000000knik0-faac-1950446/frontend/faac.ico000066400000000000000000000226761522734263700170510ustar00rootroot0000000000000000 ¨%(0` ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿ$ÿÿ,ÿÿ1ÿÿ6ÿÿ8ÿÿ9ÿÿ9ÿÿ8ÿÿ6ÿÿ1ÿÿ,ÿÿ$ÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ%ÿÿ1ÿÿ;ÿÿAÿÿGÿÿKÿÿNÿÿPÿÿQÿÿQÿÿPÿÿNÿÿKÿÿGÿÿAÿÿ;ÿÿ1ÿÿ%ÿÿÿÿÿÿÿÿÿÿøÿÿÿÿÿþÿ(üÿ7ýÿBÿÿJÿÿQÿÿWÿÿ\ÿÿ_ÿÿbÿÿeÿÿfÿÿfÿÿeÿÿbÿÿ_ÿÿ\ÿÿWÿÿQÿÿJýÿBüÿ7þÿ(ÿÿÿÿøÿÿÿÿÿþÿÿÿ ÿÿ"ôÿ7ØÿKÈÿ[ÎÿcçÿcûÿeÿÿiÿÿmÿÿrÿÿtÿÿvÿÿvÿÿvÿÿvÿÿtÿÿrÿÿmÿÿiûÿeçÿcÎÿcÈÿ[ØÿKôÿ7ÿÿ"ÿÿ þÿÿÿÿÿÿÿÿÿÿÿ,îÿA±ÿ_ƒÿ~rÿ‘yÿ—–ÿÏÿùÿ{ÿÿ~ÿÿÿÿƒÿÿ…ÿÿ†ÿÿ†ÿÿ…ÿÿƒÿÿÿÿ~ùÿ{Ïÿ–ÿyÿ—rÿ‘ƒÿ~±ÿ_îÿAÿÿ,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ3÷ÿE¶ÿdwÿŒTÿ«Bÿ¾FÿÂ`ÿ¸ÿ¦Ùÿ’ýÿŒÿÿÿÿ‘ÿÿ“ÿÿ”ÿÿ”ÿÿ“ÿÿ‘ÿÿýÿŒÙÿ’ÿ¦`ÿ¸FÿÂBÿ¾Tÿ«wÿŒ¶ÿd÷ÿEÿÿ3ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ6ÿÿFÙÿ^ÿ†Yÿ¬5ÿÌ ÿá"ÿã=ÿÔkÿÁ¬ÿ¬òÿœÿÿœÿÿžÿÿ ÿÿ¡ÿÿ¡ÿÿ ÿÿžÿÿœòÿœ¬ÿ¬kÿÁ=ÿÔ"ÿã ÿá5ÿÌYÿ¬ÿ†Ùÿ^ÿÿFÿÿ6ÿÿÿÿÿÿÿÿÿÿÿÿ6ÿÿH÷ÿY¹ÿwtÿ FÿÄ!ÿãÿîÿñ&ÿèSÿÔŽÿÀÛÿ­ýÿ¨ÿÿªÿÿ«ÿÿ¬ÿÿ¬ÿÿ«ÿÿªýÿ¨Ûÿ­ŽÿÀSÿÔ&ÿèÿñÿî!ÿãFÿÄtÿ ¹ÿw÷ÿYÿÿHÿÿ6ÿÿÿÿÿÿÿÿÿÿÿÿ3ÿÿGÿÿWèÿk ÿŽdÿ³5ÿÖÿë,ÿä ÿìÿñ@ÿâyÿÏÁÿ¾úÿ´ÿÿµÿÿ¶ÿÿ·ÿÿ·ÿÿ¶ÿÿµúÿ´Áÿ¾yÿÏ@ÿâÿñ ÿì,ÿäÿë5ÿÖdÿ³ ÿŽèÿkÿÿWÿÿGÿÿ3ÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ,ÿÿCÿÿUÿÿd×ÿ|ÿ¡WÿÃ)ÿã!ÿëDÿÝ6ÿåÿô2ÿëiÿÛ¬ÿÌòÿ¿ÿÿ¿ÿÿÀÿÿÁÿÿÁÿÿÀÿÿ¿òÿ¿¬ÿÌiÿÛ2ÿëÿô6ÿåDÿÝ!ÿë)ÿãWÿÃÿ¡×ÿ|ÿÿdÿÿUÿÿCÿÿ,ÿÿ ÿÿÿÿÿÿÿÿ"ÿÿ>ÿÿQÿÿaøÿqÃÿŒÿ°KÿÐ"ÿé+ÿéYÿÙJÿáÿõ'ÿò[ÿä›ÿ×äÿÊþÿÈÿÿÉÿÿÊÿÿÊÿÿÉþÿÈäÿÊ›ÿ×[ÿä'ÿòÿõJÿáYÿÙ+ÿé"ÿéKÿÐÿ°ÃÿŒøÿqÿÿaÿÿQÿÿ>ÿÿ"ÿÿÿÿÿÿÿÿÿÿ5ÿÿKÿÿ]ÿÿkðÿ}±ÿœsÿ½?ÿÛ"ÿí:ÿæmÿ×]ÿß$ÿóÿöNÿëÿßÔÿÕýÿÐÿÿÒÿÿÒÿÿÒÿÿÒýÿÐÔÿÕÿßNÿëÿö$ÿó]ÿßmÿ×:ÿæ"ÿí?ÿÛsÿ½±ÿœðÿ}ÿÿkÿÿ]ÿÿKÿÿ5ÿÿÿÿÿÿÿÿÿÿÿÿ(ÿÿBÿÿVÿÿgÿÿuåÿ‰¡ÿ©gÿÈ4ÿã&ÿîLÿãÿ×oÿß3ÿñÿùBÿñ€ÿçÅÿÞûÿØÿÿÙÿÿÚÿÿÚÿÿÙûÿØÅÿÞ€ÿçBÿñÿù3ÿñoÿßÿ×Lÿã&ÿî4ÿãgÿÈ¡ÿ©åÿ‰ÿÿuÿÿgÿÿVÿÿBÿÿ(ÿÿÿÿÿÿÿÿÿÿ7ÿÿMÿÿ_ÿÿoýÿ~Øÿ•”ÿµ]ÿÑ.ÿé-ÿí]ÿá“ÿ×ÿàBÿïÿú7ÿõtÿí¶ÿåõÿàÿÿàÿÿáÿÿáÿÿàõÿà¶ÿåtÿí7ÿõÿúBÿïÿà“ÿ×]ÿá-ÿí.ÿé]ÿÑ”ÿµØÿ•ýÿ~ÿÿoÿÿ_ÿÿMÿÿ7ÿÿÿÿÿÿÿÿÿÿ%ÿÿAÿÿWÿÿhÿÿwøÿ†Èÿ ˆÿ¿RÿÚ+ÿí7ÿìoÿà¥ÿØÿàQÿïÿú.ÿøhÿòªÿìîÿæÿÿæÿÿçÿÿçÿÿæîÿæªÿìhÿò.ÿøÿúQÿïÿà¥ÿØoÿà7ÿì+ÿíRÿÚˆÿ¿Èÿ øÿ†ÿÿwÿÿhÿÿWÿÿAÿÿ%ÿÿÿÿÿÿÿÿ ÿÿ1ÿÿJÿÿ^ÿÿoÿÿ~óÿޏÿª|ÿÈGÿá+ÿïFÿêÿß¶ÿÙŸÿá_ÿï ÿû&ÿú]ÿõžÿðäÿìþÿìÿÿíÿÿíþÿìäÿìžÿð]ÿõ&ÿú ÿû_ÿïŸÿá¶ÿÙÿßFÿê+ÿïGÿá|ÿȸÿªóÿŽÿÿ~ÿÿoÿÿ^ÿÿJÿÿ1ÿÿ ÿÿÿÿÿÿÿÿ;ÿÿMÿÿYÿÿpÿþ„éÿ–ªÿ³rþÎ<ÿæ)ÿîSÿæÿßÇÿÛ­ÿälÿð,ÿúÿüRþø”ÿôÝÿðþÿðÿÿñÿÿðÿÿð×ÿñ“ÿôRÿøÿü,ÿúlþð°ÿãÏÿØ’ÿÞTþè-ÿï=ÿæqÿÍ­ÿ°õÿÿÿ{ÿÿkÿÿ\ÿÿNÿÿ;ÿÿÿÿÿÿ$úõCG¶x‚|‚äE¹|§ÿÿ…Ýÿžÿ¼dÿÔ!JÀíi_9ýEt~ñžúà×ÿܼÿåyÿð9ÿùÿýIÿúTybüp…,ýøóõN¯lúp.ýíãõËÿõˆÿøHÿúÿý9ÿù|ÿï£Æìq‰*øE€}ïfÿå3ÿï5ÿégÍÝQ…fßpˆ,æ{‚ëx…ág–=½1Ì¡qÿÿ=ÿÿ#ëÿÿÿ,üôHP®g‡wÿO¯jµÿÿˆÎÿ¥ÿÂYÿÛ=Þîe]Býo~/ùªÐäçÿÜÉÿæ†ÿðEÿùÿþ Bìü~pÿcŽEý÷úø1Í£ú‹sÿ;¼ûÂÿø}ÿú>ÿûÿþEÿù†üð>—‰ó„zý6™ëvÿã>ÿí=Nö|süƒx ûp…+èf•>Ön0Ö‚{ ó†x÷bœGŽ ÷î.ëÿ“ÿ¾ÿ>ÝöTM¯m“‡xÿN±l¶þÿŒ¾ÿ­„ÿÉOÿá1ÿïAU…÷…wþ0°£éøÿÜÖÿæ“ÿñQÿùÿþ3Gžþˆpÿ*¦®üñýûüøû}€ÿl‹3ÿºÿúrÿû2ÿýÿþRÿù Šæóc‡DúmŒ2÷ ¶åâ€ÿã7c˜ò„qÿhq9ö#‚¼Ô ¶ë² êê—òåˆèÔ|f—>¸r…'½ ¶éB“ÿpÿ‰ÿT¦÷hHwž‡xÿM°n¸ôÿ‘¯ÿ³xÿÎEÿå2ÿï"Z¾ïus#ûh’;öA¹…í@¬ˆò>ŠŠø>gŒü9B”þne0ÿzsÿ ©íýëÿüÿÿýL±oþ†vÿB‘ƒþ¿ŠòÒÿíU‰_ú}uÿáþ4ÿþrÿý´ÿýòÿýòÿý´úýQzdþlÿôþPÚüzxÿX Y÷ÿÿäÿÿÞÿÿÖ1ââ„}ýQjgò@öé;ÿäcÿΓÿ±ˆÿªcÿ­Hÿ°Qý•fÿdpÿwÿuÿW]ù‰9[“LJwÿCvÑzÿ¶QÿÓ6ÿæNÿá€ÿÙ»ÿÓóÿÐûô×f˜?ôe™@ø¿Æðw‡!üRceþñþ*ÿþgÿü¨ÿüëÿüëÿü¨þüoÅý†qÿ:Jþ.Y©ýƒ‚ ý*±ðÿÿâÿÿÜÿÿÔà××i:òxqû&R·ë6ÿåQÿÓ{ÿ¸oÿ³Nÿ¸Hò²&b¶žuúXwÿfÿ „ÿHkù{;b޾‡vÿGjyÚcö EìÛ =íå bìÜ’ñÔÑýÌûÿËÿÿÐDºé{ƒü]˜Nø‚ ý$b»ú$ÿý!ÿþ[ÿüÿûßÿùßÿùÿû_õünf0ÿum%ÿ]fPþnŠ1ûáÿèÿÿßÿÿÚÿÿÒþÿÉ9³“Ý„{ükl5ø%F¹ë GçÜ eéÅ ZæÁHØÇ[gTÝkt4Ê „ìKfÿ ÿ–ÿ6{÷k@l†´‹vÿrn)ó]jQë\bSó\dSô`qLòG‘yã ÜçÉÿÿÆÿÿÎäÎÝs‹(øpÿm3ülã÷/ÿüÿþPÿûÿùÔÿ÷Ôÿ÷ÿùRúû:C‘þ’{ÿ„wÿDœ~õðÿãÿÿÜÿÿ×ÿÿÏÿÿÇåÿÄ?šˆÞzyø‚t ými3÷akHígi>ïrú‚v ÷TzaŸ•ò7ÿÿÿ²ÿ ‘øX7w—wt ísq'ðso'ôsr'ôtx&ów|óZœVáèàÃÿÿÂÿÿËÿÿÑM²oëtŒ%ù7™—òyöó:ÿùÿþDÿú„ÿ÷ÇÿôÇÿô„ÿ÷Dÿúäþnm0þ_}Lú³ÔìöÿßÿÿÙÿÿÒÿÿËÿÿÃøÿ¼Ëÿ¼ŽÏÍKrsãfm?ïpn+ómn2íXnZÒ$t»““ÿS³ÿ ÿûõÿæÿ ·ÿ?ÿipÿˆ[ÿ¦Uÿ·cÿ»…ÿ·¹ÿ±ïÿ®ÿÿµÿÿ¿ÿÿÇÿÿÍÿÿÒÿÿ×Êÿä„ÿïDÿ÷ÿý8ÿúwÿöºÿñºÿñwÿö8ÿúÿý?ÿ÷„ÿîÇÿäûþÚÿÿÓÿÿÍÿÿÇÿÿ¿ÿÿµéÿ±¶ÿµ…ÿ¹dÿ»Uÿ·\ÿ§qÿ‹ÿi·ÿ?æÿ õÿÿÿÿÿîÿ&¿ÿQžÿsŒÿމÿž˜ÿ¥¸ÿ¥ãÿ¤þÿ¨ÿÿ°ÿÿ¹ÿÿÁÿÿÈÿÿÎÿÿÔÐÿÞŽÿêNÿôÿü-ÿúiÿô­ÿî­ÿîiÿô-ÿúÿüNÿôŽÿêÐÿÞÿÿÔÿÿÎÿÿÈÿÿÁÿÿ¹ÿÿ°þÿ¨ãÿ¤¸ÿ¥˜ÿ¥‰ÿžŒÿŽžÿs¿ÿQîÿ&ÿÿÿÿÿÿÿÿùÿ8ÜÿUËÿmÉÿ~Öÿ‰îÿÿÿ–ÿÿ ÿÿªÿÿ³ÿÿºÿÿÁÿÿÈÿÿÍÛÿ×™ÿåYÿñÿû#ÿú\ÿóŸÿëŸÿë\ÿó#ÿúÿûYÿñ™ÿåÛÿ×ÿÿÍÿÿÈÿÿÁÿÿºÿÿ³ÿÿªÿÿ ÿÿ–îÿÖÿ‰Éÿ~ËÿmÜÿUùÿ8ÿÿÿÿýÿÿÿÿÿ(ÿÿAÿÿVþÿhÿÿuÿÿ‚ÿÿŽÿÿ™ÿÿ¢ÿÿ«ÿÿ³ÿÿºÿÿÁÿÿÆåÿФÿßbÿì$ÿøÿûOÿòÿéÿéOÿòÿû$ÿøbÿì¤ÿßåÿÐÿÿÆÿÿÁÿÿºÿÿ³ÿÿ«ÿÿ¢ÿÿ™ÿÿŽÿÿ‚ÿÿuþÿhÿÿVÿÿAÿÿ(ÿÿýÿÿÿÿÿÿÿÿÿ5ÿÿKÿÿ]ÿÿlÿÿyÿÿ†ÿÿÿÿ›ÿÿ£ÿÿ«ÿÿ³ÿÿ¹ÿÿ¾ðÿƯÿÖmÿæ/ÿõÿüAÿò€ÿç€ÿçAÿòÿü/ÿõmÿæ¯ÿÖðÿÆÿÿ¾ÿÿ¹ÿÿ³ÿÿ«ÿÿ£ÿÿ›ÿÿÿÿ†ÿÿyÿÿlÿÿ]ÿÿKÿÿ5ÿÿÿÿÿÿÿÿÿÿ"ÿÿ>ÿÿQÿÿaÿÿpÿÿ|ÿÿ‡ÿÿ‘ÿÿ›ÿÿ¢ÿÿªÿÿ°ÿÿ¶øÿ¼»ÿÍwÿÞ9ÿð ÿû3ÿòoÿåoÿå3ÿò ÿû9ÿðwÿÞ»ÿÍøÿ¼ÿÿ¶ÿÿ°ÿÿªÿÿ¢ÿÿ›ÿÿ‘ÿÿ‡ÿÿ|ÿÿpÿÿaÿÿQÿÿ>ÿÿ"ÿÿÿÿÿÿÿÿ ÿÿ,ÿÿCÿÿUÿÿdÿÿqÿÿ}ÿÿ‡ÿÿÿÿ™ÿÿ ÿÿ§ÿÿ¬ÿÿ±ÈÿÁÿÖDÿéÿú&ÿô^ÿä^ÿä&ÿôÿúDÿéÿÖÈÿÁÿÿ±ÿÿ¬ÿÿ§ÿÿ ÿÿ™ÿÿÿÿ‡ÿÿ}ÿÿqÿÿdÿÿUÿÿCÿÿ,ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿ3ÿÿGÿÿWÿÿeÿÿqÿÿ|ÿÿ†ÿÿŽÿÿ–ÿÿÿÿ£ÿÿ§Öÿ¶ÿÌOÿáÿöÿöLÿäLÿäÿöÿöOÿáÿÌÖÿ¶ÿÿ§ÿÿ£ÿÿÿÿ–ÿÿŽÿÿ†ÿÿ|ÿÿqÿÿeÿÿWÿÿGÿÿ3ÿÿÿÿÿÿÿÿÿÿÿÿ6ÿÿHÿÿWÿÿdÿÿpÿÿyÿÿ‚ÿÿŠÿÿ’ÿÿ˜ÿÿœæÿ§›ÿ¿Zÿ×$ÿî ÿù9ÿæ9ÿæ ÿù$ÿîZÿ×›ÿ¿æÿ§ÿÿœÿÿ˜ÿÿ’ÿÿŠÿÿ‚ÿÿyÿÿpÿÿdÿÿWÿÿHÿÿ6ÿÿÿÿÿÿÿÿÿÿÿÿ6ÿÿGÿÿUÿÿaÿÿlÿÿuÿÿ~ÿÿ…ÿÿ‹ÿÿõÿ—¬ÿ¯gÿÊ1ÿä ÿú&ÿê&ÿê ÿú1ÿägÿʬÿ¯õÿ—ÿÿÿÿ‹ÿÿ…ÿÿ~ÿÿuÿÿlÿÿaÿÿUÿÿGÿÿ6ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ3ÿÿCÿÿQÿÿ]ÿÿgÿÿoÿÿwÿÿ~ÿÿƒÿÿˆÁÿwÿ»?ÿ×ÿòÿôÿôÿò?ÿ×wÿ»Áÿÿÿˆÿÿƒÿÿ~ÿÿwÿÿoÿÿgÿÿ]ÿÿQÿÿCÿÿ3ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ,ÿÿ>ÿÿKÿÿVÿÿ_ÿÿhÿÿoÿÿtÿÿyÝÿ†Œÿ¦PÿÄ$ÿá ÿö ÿö$ÿáPÿÄŒÿ¦Ýÿ†ÿÿyÿÿtÿÿoÿÿhÿÿ_ÿÿVÿÿKÿÿ>ÿÿ,ÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ"ÿÿ5ÿÿBÿÿMÿÿWÿÿ^ÿÿdÿÿiûÿn¬ÿ‰iÿª>ÿÅ&ÿÙ&ÿÙ>ÿÅiÿª¬ÿ‰ûÿnÿÿiÿÿdÿÿ^ÿÿWÿÿMÿÿBÿÿ5ÿÿ"ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ(ÿÿ7ÿÿAÿÿJÿÿQÿÿWÿÿ[ãÿf”ÿ„dÿžNÿ­Nÿ­dÿž”ÿ„ãÿfÿÿ[ÿÿWÿÿQÿÿJÿÿAÿÿ7ÿÿ(ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ%ÿÿ1ÿÿ;ÿÿAÿÿGÿÿKäÿT¨ÿh‰ÿv‰ÿv¨ÿhäÿTÿÿKÿÿGÿÿAÿÿ;ÿÿ1ÿÿ%ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿ$ÿÿ,ÿÿ1þÿ6úÿ9õÿ;õÿ;úÿ9þÿ6ÿÿ1ÿÿ,ÿÿ$ÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿàÿÿÿþÿÿøÿÿàÿÿ€ÿÿÿþü?øððààÀÀ€€€€€€€ÀÀààððøü?þÿÿÿ€ÿÿàÿÿøÿÿþÿÿÿàÿÿknik0-faac-1950446/frontend/faacgui.rc000066400000000000000000000110111522734263700173650ustar00rootroot00000000000000//Microsoft Developer Studio generated resource script. // #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include "afxres.h" ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // English (U.S.) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) #ifdef _WIN32 LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US #pragma code_page(1252) #endif //_WIN32 #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE DISCARDABLE BEGIN "resource.h\0" END 2 TEXTINCLUDE DISCARDABLE BEGIN "#include ""afxres.h""\r\n" "\0" END 3 TEXTINCLUDE DISCARDABLE BEGIN "\r\n" "\0" END #endif // APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Dialog // IDD_MAINDIALOG DIALOGEX 0, 0, 264, 200 STYLE DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "FAAC GUI" FONT 8, "MS Sans Serif" BEGIN PUSHBUTTON "...",IDC_SELECT_INPUTFILE,225,12,19,14,0, WS_EX_STATICEDGE PUSHBUTTON "...",IDC_SELECT_OUTPUTFILE,225,33,19,14,WS_DISABLED, WS_EX_STATICEDGE COMBOBOX IDC_JOINTMODE,14,126,71,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP LTEXT "Joint Stereo",IDC_STATIC,14,117,56,8 LTEXT "Quantizer\nquality", IDC_STATIC, 98, 73, 44, 18 EDITTEXT IDC_QUALITY, 138, 76, 30, 12, ES_AUTOHSCROLL CONTROL "", IDC_STATIC, "static", SS_SUNKEN, 98, 94, 70, 30 CONTROL "Set bandwidth", IDC_BWCTL, "Button", BS_AUTOCHECKBOX | WS_TABSTOP, 102, 96, 62, 12 EDITTEXT IDC_BANDWIDTH,102,109,34,12,ES_AUTOHSCROLL | WS_DISABLED | WS_TABSTOP LTEXT "Hz", IDC_STATIC, 140, 111, 10, 8 DEFPUSHBUTTON "Encode",IDOK,200,162,50,14,WS_DISABLED PUSHBUTTON "Quit",IDCANCEL,200,180,50,14 LTEXT "",IDC_TIME,7,161,190,20 LTEXT "", IDC_COMPILEDATE, 20, 186, 150, 8 EDITTEXT IDC_INPUTFILENAME,61,12,158,14,ES_AUTOHSCROLL EDITTEXT IDC_OUTPUTFILENAME,61,33,158,14,ES_AUTOHSCROLL | WS_DISABLED LTEXT "Input File:",IDC_STATIC,18,13,38,8 LTEXT "Output File:",IDC_STATIC,18,34,37,8 CONTROL "",IDC_STATIC,"Static",SS_BLACKFRAME | SS_SUNKEN,7,54, 250,1 LTEXT "-",IDC_INPUTPARAMS,32,66,55,8 LTEXT "In:",IDC_STATIC,14,66,12,8 CONTROL "Progress1",IDC_PROGRESS,"msctls_progress32",WS_BORDER,7, 145,250,10 GROUPBOX "Output Format",IDC_STATIC,93,62,164,77 CONTROL "Use RAW output",IDC_USERAW,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,14,93,71,10 CONTROL "Use TNS",IDC_USETNS,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,14,82,71,10 COMBOBOX IDC_MPEGVERSION,183,82,67,63,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP COMBOBOX IDC_OBJECTTYPE,183,109,67,59,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP LTEXT "MPEG Version",IDC_STATIC,183,73,56,8 LTEXT "AAC Object Type",IDC_STATIC,183,100,56,8 CONTROL "Use LFE channel",IDC_USELFE,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_DISABLED | WS_TABSTOP,14,104,71,10 END ///////////////////////////////////////////////////////////////////////////// // // DESIGNINFO // #ifdef APSTUDIO_INVOKED GUIDELINES DESIGNINFO DISCARDABLE BEGIN IDD_MAINDIALOG, DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 257 TOPMARGIN, 7 BOTTOMMARGIN, 178 END END #endif // APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Dialog Info // IDD_MAINDIALOG DLGINIT BEGIN IDC_MPEGVERSION, 0x403, 1, 0, "\000", 0 END #endif // English (U.S.) resources ///////////////////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED knik0-faac-1950446/frontend/getopt.c000066400000000000000000000367751522734263700171360ustar00rootroot00000000000000/* $OpenBSD: getopt_long.c,v 1.23 2007/10/31 12:34:57 chl Exp $ */ /* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */ /* * Vendored from https://github.com/alex85k/wingetopt src/getopt.c, * commit 98ea94f3d77890678da28230aa156b225cc14974, kept diff-minimal * against upstream (see commit history for the local changes). */ /* * Copyright (c) 2002 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Sponsored in part by the Defense Advanced Research Projects * Agency (DARPA) and Air Force Research Laboratory, Air Force * Materiel Command, USAF, under agreement number F39502-99-1-0512. */ /*- * Copyright (c) 2000 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Dieter Baron and Thomas Klausner. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 #include #include #include "getopt.h" #include #include #ifdef _WIN32 #include #endif #define REPLACE_GETOPT /* use this getopt as the system getopt(3) */ #ifdef REPLACE_GETOPT int opterr = 1; /* if error message should be printed */ int optind = 1; /* index into parent argv vector */ int optopt = '?'; /* character checked for validity */ #undef optreset /* see getopt.h */ #define optreset __mingw_optreset int optreset; /* reset getopt */ char *optarg; /* argument associated with option */ #endif #define PRINT_ERROR ((opterr) && (*options != ':')) #define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */ #define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */ #define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */ /* return values */ #define BADCH (int)'?' #define BADARG ((*options == ':') ? (int)':' : (int)'?') #define INORDER (int)1 #ifndef __CYGWIN__ #define __progname __argv[0] #else extern char __declspec(dllimport) *__progname; #endif #ifdef __CYGWIN__ static char EMSG[] = ""; #else #define EMSG "" #endif static int getopt_internal(int, char * const *, const char *, const struct option *, int *, int); static int parse_long_options(char * const *, const char *, const struct option *, int *, int); static int gcd(int, int); static void permute_args(int, int, int, char * const *); static char *place = EMSG; /* option letter processing */ /* XXX: set optreset to 1 rather than these two */ static int nonopt_start = -1; /* first non option argument (for permute) */ static int nonopt_end = -1; /* first option after non options (for permute) */ /* Error messages */ static const char recargchar[] = "option requires an argument -- %c"; static const char recargstring[] = "option requires an argument -- %s"; static const char ambig[] = "ambiguous option -- %.*s"; static const char noarg[] = "option doesn't take an argument -- %.*s"; static const char illoptchar[] = "unknown option -- %c"; static const char illoptstring[] = "unknown option -- %s"; static void _vwarnx(const char *fmt,va_list ap) { (void)fprintf(stderr,"%s: ",__progname); if (fmt != NULL) (void)vfprintf(stderr,fmt,ap); (void)fprintf(stderr,"\n"); } static void warnx(const char *fmt,...) { va_list ap; va_start(ap,fmt); _vwarnx(fmt,ap); va_end(ap); } /* * Compute the greatest common divisor of a and b. */ static int gcd(int a, int b) { int c; c = a % b; while (c != 0) { a = b; b = c; c = a % b; } return (b); } /* * Exchange the block from nonopt_start to nonopt_end with the block * from nonopt_end to opt_end (keeping the same order of arguments * in each block). */ static void permute_args(int panonopt_start, int panonopt_end, int opt_end, char * const *nargv) { int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; char *swap; /* * compute lengths of blocks and number and size of cycles */ nnonopts = panonopt_end - panonopt_start; nopts = opt_end - panonopt_end; ncycle = gcd(nnonopts, nopts); cyclelen = (opt_end - panonopt_start) / ncycle; for (i = 0; i < ncycle; i++) { cstart = panonopt_end+i; pos = cstart; for (j = 0; j < cyclelen; j++) { if (pos >= panonopt_end) pos -= nnonopts; else pos += nopts; swap = nargv[pos]; /* LINTED const cast */ ((char **) nargv)[pos] = nargv[cstart]; /* LINTED const cast */ ((char **)nargv)[cstart] = swap; } } } /* * parse_long_options -- * Parse long options in argc/argv argument vector. * Returns -1 if short_too is set and the option does not match long_options. */ static int parse_long_options(char * const *nargv, const char *options, const struct option *long_options, int *idx, int short_too) { char *current_argv, *has_equal; size_t current_argv_len; int i, ambiguous, match; #define IDENTICAL_INTERPRETATION(_x, _y) \ (long_options[(_x)].has_arg == long_options[(_y)].has_arg && \ long_options[(_x)].flag == long_options[(_y)].flag && \ long_options[(_x)].val == long_options[(_y)].val) current_argv = place; match = -1; ambiguous = 0; optind++; if ((has_equal = strchr(current_argv, '=')) != NULL) { /* argument found (--option=arg) */ current_argv_len = has_equal - current_argv; has_equal++; } else current_argv_len = strlen(current_argv); for (i = 0; long_options[i].name; i++) { /* find matching long option */ if (strncmp(current_argv, long_options[i].name, current_argv_len)) continue; if (strlen(long_options[i].name) == current_argv_len) { /* exact match */ match = i; ambiguous = 0; break; } /* * If this is a known short option, don't allow * a partial match of a single character. */ if (short_too && current_argv_len == 1) continue; if (match == -1) /* partial match */ match = i; else if (!IDENTICAL_INTERPRETATION(i, match)) ambiguous = 1; } if (ambiguous) { /* ambiguous abbreviation */ if (PRINT_ERROR) warnx(ambig, (int)current_argv_len, current_argv); optopt = 0; return (BADCH); } if (match != -1) { /* option found */ if (long_options[match].has_arg == no_argument && has_equal) { if (PRINT_ERROR) warnx(noarg, (int)current_argv_len, current_argv); /* * XXX: GNU sets optopt to val regardless of flag */ if (long_options[match].flag == NULL) optopt = long_options[match].val; else optopt = 0; return (BADARG); } if (long_options[match].has_arg == required_argument || long_options[match].has_arg == optional_argument) { if (has_equal) optarg = has_equal; else if (long_options[match].has_arg == required_argument) { /* * optional argument doesn't use next nargv */ optarg = nargv[optind++]; } } if ((long_options[match].has_arg == required_argument) && (optarg == NULL)) { /* * Missing argument; leading ':' indicates no error * should be generated. */ if (PRINT_ERROR) warnx(recargstring, current_argv); /* * XXX: GNU sets optopt to val regardless of flag */ if (long_options[match].flag == NULL) optopt = long_options[match].val; else optopt = 0; --optind; return (BADARG); } } else { /* unknown option */ if (short_too) { --optind; return (-1); } if (PRINT_ERROR) warnx(illoptstring, current_argv); optopt = 0; return (BADCH); } if (idx) *idx = match; if (long_options[match].flag) { *long_options[match].flag = long_options[match].val; return (0); } else return (long_options[match].val); #undef IDENTICAL_INTERPRETATION } /* * getopt_internal -- * Parse argc/argv argument vector. Called by user level routines. */ static int getopt_internal(int nargc, char * const *nargv, const char *options, const struct option *long_options, int *idx, int flags) { const char *oli; /* option letter list index */ int optchar, short_too; static int posixly_correct = -1; if (options == NULL) return (-1); /* * XXX Some GNU programs (like cvs) set optind to 0 instead of * XXX using optreset. Work around this braindamage. */ if (optind == 0) optind = optreset = 1; /* * Disable GNU extensions if POSIXLY_CORRECT is set or options * string begins with a '+'. * * CV, 2009-12-14: Check POSIXLY_CORRECT anew if optind == 0 or * optreset != 0 for GNU compatibility. */ if (posixly_correct == -1 || optreset != 0) posixly_correct = (getenv("POSIXLY_CORRECT") != NULL); if (*options == '-') flags |= FLAG_ALLARGS; else if (posixly_correct || *options == '+') flags &= ~FLAG_PERMUTE; if (*options == '+' || *options == '-') options++; optarg = NULL; if (optreset) nonopt_start = nonopt_end = -1; start: if (optreset || !*place) { /* update scanning pointer */ optreset = 0; if (optind >= nargc) { /* end of argument vector */ place = EMSG; if (nonopt_end != -1) { /* do permutation, if we have to */ permute_args(nonopt_start, nonopt_end, optind, nargv); optind -= nonopt_end - nonopt_start; } else if (nonopt_start != -1) { /* * If we skipped non-options, set optind * to the first of them. */ optind = nonopt_start; } nonopt_start = nonopt_end = -1; return (-1); } if (*(place = nargv[optind]) != '-' || (place[1] == '\0' && strchr(options, '-') == NULL)) { place = EMSG; /* found non-option */ if (flags & FLAG_ALLARGS) { /* * GNU extension: * return non-option as argument to option 1 */ optarg = nargv[optind++]; return (INORDER); } if (!(flags & FLAG_PERMUTE)) { /* * If no permutation wanted, stop parsing * at first non-option. */ return (-1); } /* do permutation */ if (nonopt_start == -1) nonopt_start = optind; else if (nonopt_end != -1) { permute_args(nonopt_start, nonopt_end, optind, nargv); nonopt_start = optind - (nonopt_end - nonopt_start); nonopt_end = -1; } optind++; /* process next argument */ goto start; } if (nonopt_start != -1 && nonopt_end == -1) nonopt_end = optind; /* * If we have "-" do nothing, if "--" we are done. */ if (place[1] != '\0' && *++place == '-' && place[1] == '\0') { optind++; place = EMSG; /* * We found an option (--), so if we skipped * non-options, we have to permute. */ if (nonopt_end != -1) { permute_args(nonopt_start, nonopt_end, optind, nargv); optind -= nonopt_end - nonopt_start; } nonopt_start = nonopt_end = -1; return (-1); } } /* * Check long options if: * 1) we were passed some * 2) the arg is not just "-" * 3) either the arg starts with -- we are getopt_long_only() */ if (long_options != NULL && place != nargv[optind] && (*place == '-' || (flags & FLAG_LONGONLY))) { short_too = 0; if (*place == '-') place++; /* --foo long option */ else if (*place != ':' && strchr(options, *place) != NULL) short_too = 1; /* could be short option too */ optchar = parse_long_options(nargv, options, long_options, idx, short_too); if (optchar != -1) { place = EMSG; return (optchar); } } if ((optchar = (int)*place++) == (int)':' || (optchar == (int)'-' && *place != '\0') || (oli = strchr(options, optchar)) == NULL) { /* * If the user specified "-" and '-' isn't listed in * options, return -1 (non-option) as per POSIX. * Otherwise, it is an unknown option character (or ':'). */ if (optchar == (int)'-' && *place == '\0') return (-1); if (!*place) ++optind; if (PRINT_ERROR) warnx(illoptchar, optchar); optopt = optchar; return (BADCH); } if (long_options != NULL && optchar == 'W' && oli[1] == ';') { /* -W long-option */ if (*place) /* no space */ /* NOTHING */; else if (++optind >= nargc) { /* no arg */ place = EMSG; if (PRINT_ERROR) warnx(recargchar, optchar); optopt = optchar; return (BADARG); } else /* white space */ place = nargv[optind]; optchar = parse_long_options(nargv, options, long_options, idx, 0); place = EMSG; return (optchar); } if (*++oli != ':') { /* doesn't take argument */ if (!*place) ++optind; } else { /* takes (optional) argument */ optarg = NULL; if (*place) /* no white space */ optarg = place; else if (oli[1] != ':') { /* arg not optional */ if (++optind >= nargc) { /* no arg */ place = EMSG; if (PRINT_ERROR) warnx(recargchar, optchar); optopt = optchar; return (BADARG); } else optarg = nargv[optind]; } place = EMSG; ++optind; } /* dump back option letter */ return (optchar); } #ifdef REPLACE_GETOPT /* * getopt -- * Parse argc/argv argument vector. * * [eventually this will replace the BSD getopt] */ WINGETOPT_API int getopt(int nargc, char * const *nargv, const char *options) { /* * We don't pass FLAG_PERMUTE to getopt_internal() since * the BSD getopt(3) (unlike GNU) has never done this. * * Furthermore, since many privileged programs call getopt() * before dropping privileges it makes sense to keep things * as simple (and bug-free) as possible. */ return (getopt_internal(nargc, nargv, options, NULL, NULL, 0)); } #endif /* REPLACE_GETOPT */ /* * getopt_long -- * Parse argc/argv argument vector. */ WINGETOPT_API int getopt_long(int nargc, char * const *nargv, const char *options, const struct option *long_options, int *idx) { return (getopt_internal(nargc, nargv, options, long_options, idx, FLAG_PERMUTE)); } /* * getopt_long_only -- * Parse argc/argv argument vector. */ WINGETOPT_API int getopt_long_only(int nargc, char * const *nargv, const char *options, const struct option *long_options, int *idx) { return (getopt_internal(nargc, nargv, options, long_options, idx, FLAG_PERMUTE|FLAG_LONGONLY)); } knik0-faac-1950446/frontend/getopt.h000066400000000000000000000070751522734263700171320ustar00rootroot00000000000000#ifndef __GETOPT_H__ /** * DISCLAIMER * This file has no copyright assigned and is placed in the Public Domain. * This file is a part of the w64 mingw-runtime package. * * The w64 mingw-runtime package and its code is distributed in the hope that it * will be useful but WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESSED OR * IMPLIED ARE HEREBY DISCLAIMED. This includes but is not limited to * warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ /* * Vendored from https://github.com/alex85k/wingetopt src/getopt.h, * commit 98ea94f3d77890678da28230aa156b225cc14974, kept diff-minimal * against upstream (see commit history for the local changes). */ #define __GETOPT_H__ /* All the headers include this file. */ #ifdef __MINGW32__ #include #endif #if defined( WINGETOPT_SHARED_LIB ) # if defined( BUILDING_WINGETOPT_DLL ) # define WINGETOPT_API __declspec(dllexport) # else # define WINGETOPT_API __declspec(dllimport) # endif #else # define WINGETOPT_API #endif #ifdef __cplusplus extern "C" { #endif WINGETOPT_API extern int optind; /* index of first non-option in argv */ WINGETOPT_API extern int optopt; /* single option character, as parsed */ WINGETOPT_API extern int opterr; /* flag to enable built-in diagnostics... */ /* (user may set to zero, to suppress) */ WINGETOPT_API extern char *optarg; /* pointer to argument of current option */ WINGETOPT_API extern int getopt(int nargc, char * const *nargv, const char *options); #ifdef _BSD_SOURCE /* * BSD adds the non-standard `optreset' feature, for reinitialisation * of `getopt' parsing. We support this feature, for applications which * proclaim their BSD heritage, before including this header; however, * to maintain portability, developers are advised to avoid it. */ # define optreset __mingw_optreset WINGETOPT_API extern int optreset; #endif #ifdef __cplusplus } #endif /* * POSIX requires the `getopt' API to be specified in `unistd.h'; * thus, `unistd.h' includes this header. However, we do not want * to expose the `getopt_long' or `getopt_long_only' APIs, when * included in this manner. Thus, close the standard __GETOPT_H__ * declarations block, and open an additional __GETOPT_LONG_H__ * specific block, only when *not* __UNISTD_H_SOURCED__, in which * to declare the extended API. */ #endif /* !defined(__GETOPT_H__) */ #if !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__) #define __GETOPT_LONG_H__ #ifdef __cplusplus extern "C" { #endif struct option /* specification for a long form option... */ { const char *name; /* option name, without leading hyphens */ int has_arg; /* does it take an argument? */ int *flag; /* where to save its status, or NULL */ int val; /* its associated status value */ }; enum /* permitted values for its `has_arg' field... */ { no_argument = 0, /* option never takes an argument */ required_argument, /* option always requires an argument */ optional_argument /* option may take an argument */ }; WINGETOPT_API extern int getopt_long(int nargc, char * const *nargv, const char *options, const struct option *long_options, int *idx); WINGETOPT_API extern int getopt_long_only(int nargc, char * const *nargv, const char *options, const struct option *long_options, int *idx); /* * Previous MinGW implementation had... */ #ifndef HAVE_DECL_GETOPT /* * ...for the long form API only; keep this for compatibility. */ # define HAVE_DECL_GETOPT 1 #endif #ifdef __cplusplus } #endif #endif /* !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__) */ knik0-faac-1950446/frontend/icon.rc000066400000000000000000000000601522734263700167200ustar00rootroot00000000000000#include "resource.h" ID_ICON1 ICON "faac.ico" knik0-faac-1950446/frontend/input.c000066400000000000000000000257211522734263700167600ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Copyright (C) 2002 Krzysztof Nikiel * * 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #ifdef _WIN32 #include #include #endif #include "input.h" #define SWAP32(x) (((x & 0xff) << 24) | ((x & 0xff00) << 8) \ | ((x & 0xff0000) >> 8) | ((x & 0xff000000) >> 24)) #define SWAP16(x) (((x & 0xff) << 8) | ((x & 0xff00) >> 8)) #ifdef WORDS_BIGENDIAN # define UINT32(x) SWAP32(x) # define UINT16(x) SWAP16(x) #else # define UINT32(x) (x) # define UINT16(x) (x) #endif typedef struct { uint32_t label; /* 'RIFF' */ uint32_t length; /* Length of rest of file */ uint32_t chunk_type; /* 'WAVE' */ } riff_t; typedef struct { uint32_t label; uint32_t len; } riffsub_t; #ifdef _MSC_VER #pragma pack(push, 1) #endif #define WAVE_FORMAT_PCM 1 #define WAVE_FORMAT_FLOAT 3 #define WAVE_FORMAT_EXTENSIBLE 0xfffe struct WAVEFORMATEX { uint16_t wFormatTag; uint16_t nChannels; uint32_t nSamplesPerSec; uint32_t nAvgBytesPerSec; uint16_t nBlockAlign; uint16_t wBitsPerSample; uint16_t cbSize; } #ifdef __GNUC__ __attribute__((packed)) #endif ; struct WAVEFORMATEXTENSIBLE { struct WAVEFORMATEX Format; union { uint16_t wValidBitsPerSample; // bits of precision uint16_t wSamplesPerBlock; // valid if wBitsPerSample==0 uint16_t wReserved; // If neither applies, set to zero. } Samples; uint32_t dwChannelMask; // which channels are present in stream unsigned char SubFormat[16]; // guid } #ifdef __GNUC__ __attribute__((packed)) #endif ; #ifdef _MSC_VER #pragma pack(pop) #endif static unsigned char waveformat_pcm_guid[16] = { WAVE_FORMAT_PCM,0,0,0, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }; static void unsuperr(const char *name) { fprintf(stderr, "%s: file format not supported\n", name); } static void seekcur(FILE *f, int ofs) { if (ofs < 0) return; if (fseek(f, ofs, SEEK_CUR) != 0) { /* fseek fails on non-seekable streams (stdin/pipes); fall back to reading and discarding bytes one at a time */ while (ofs--) { if (fgetc(f) == EOF) break; } } } static int seekchunk(FILE *f, riffsub_t *riffsub, char *name) { int skipped; for(skipped = 0; skipped < 10; skipped++) { if (fread(riffsub, 1, sizeof(*riffsub), f) != sizeof(*riffsub)) return 0; riffsub->len = UINT32(riffsub->len); if (riffsub->len & 1) riffsub->len++; if (!memcmp(&(riffsub->label), name, 4)) return 1; seekcur(f, riffsub->len); } return 0; } pcmfile_t *wav_open_read(const char *name, int rawinput) { FILE *wave_f; riff_t riff; riffsub_t riffsub = {0}; struct WAVEFORMATEXTENSIBLE wave; char *riffl = "RIFF"; char *wavel = "WAVE"; char *fmtl = "fmt "; char *datal = "data"; int fmtsize; pcmfile_t *sndf; int dostdin = 0; if (!strcmp(name, "-")) { #ifdef _WIN32 _setmode(_fileno(stdin), O_BINARY); #endif wave_f = stdin; dostdin = 1; } else if (!(wave_f = fopen(name, "rb"))) { perror(name); return NULL; } if (!rawinput) // header input { if (fread(&riff, 1, sizeof(riff), wave_f) != sizeof(riff)) return NULL; if (memcmp(&(riff.label), riffl, 4)) return NULL; if (memcmp(&(riff.chunk_type), wavel, 4)) return NULL; if (!seekchunk(wave_f, &riffsub, fmtl)) return NULL; if (memcmp(&(riffsub.label), fmtl, 4)) return NULL; memset(&wave, 0, sizeof(wave)); fmtsize = (riffsub.len < sizeof(wave)) ? riffsub.len : sizeof(wave); // check if format is at least 16 bytes long if (fmtsize < 16) return NULL; if (fread(&wave, 1, fmtsize, wave_f) != (size_t)fmtsize) return NULL; seekcur(wave_f, riffsub.len - fmtsize); if (!seekchunk(wave_f, &riffsub, datal)) return NULL; if (UINT16(wave.Format.wFormatTag) != WAVE_FORMAT_PCM && UINT16(wave.Format.wFormatTag) != WAVE_FORMAT_FLOAT) { if (UINT16(wave.Format.wFormatTag) == WAVE_FORMAT_EXTENSIBLE) { if (UINT16(wave.Format.cbSize) < 22) // struct too small return NULL; if (memcmp(wave.SubFormat, waveformat_pcm_guid, 16)) { waveformat_pcm_guid[0] = WAVE_FORMAT_FLOAT; if (memcmp(wave.SubFormat, waveformat_pcm_guid, 16)) { unsuperr(name); return NULL; } } } else { unsuperr(name); return NULL; } } } sndf = (pcmfile_t*)malloc(sizeof(*sndf)); if (!sndf) { if (wave_f != stdin) fclose(wave_f); return NULL; } memset(sndf, 0, sizeof(*sndf)); sndf->f = wave_f; if (UINT16(wave.Format.wFormatTag) == WAVE_FORMAT_FLOAT) { sndf->isfloat = 1; } else { sndf->isfloat = (wave.SubFormat[0] == WAVE_FORMAT_FLOAT); } if (rawinput) { sndf->bigendian = 1; if (dostdin) sndf->samples = 0; else { fseek(sndf->f, 0 , SEEK_END); sndf->samples = ftell(sndf->f); rewind(sndf->f); } } else { sndf->bigendian = 0; sndf->channels = UINT16(wave.Format.nChannels); sndf->samplebytes = UINT16(wave.Format.wBitsPerSample) / 8; sndf->samplerate = UINT32(wave.Format.nSamplesPerSec); /* channel/sample-width bounds guard against a corrupt header (e.g. a bogus huge channel count) driving an oversized allocation downstream */ if (sndf->channels < 1 || sndf->channels > 64 || sndf->samplebytes < 1 || sndf->samplebytes > 4) { if (wave_f != stdin) fclose(wave_f); free(sndf); return NULL; } sndf->samples = riffsub.len / (sndf->samplebytes * sndf->channels); } #ifdef WORDS_BIGENDIAN sndf->swap = !sndf->bigendian; #else sndf->swap = sndf->bigendian; #endif return sndf; } static void chan_remap(int32_t *buf, int channels, int blocks, int *map) { int i; int32_t *tmp = (int32_t*)malloc(channels * sizeof(int32_t)); if (!tmp) return; for (i = 0; i < blocks; i++) { int chn; memcpy(tmp, buf + i * channels, sizeof(int32_t) * channels); for (chn = 0; chn < channels; chn++) buf[i * channels + chn] = tmp[map[chn]]; } free(tmp); } size_t wav_read_float32(pcmfile_t *sndf, float *buf, size_t num, int *map) { size_t cnt; size_t isize; char *bufi; if ((sndf->samplebytes > 4) || (sndf->samplebytes < 1)) return 0; isize = num * sndf->samplebytes; bufi = (char*)(buf + num); bufi -= isize; isize = fread(bufi, 1, isize, sndf->f); isize /= sndf->samplebytes; // perform in-place conversion cnt = (num < isize) ? num : isize; if (sndf->isfloat) { if (sndf->samplebytes == 4) { for (size_t i = 0; i < cnt; i++) buf[i] *= 32768.0f; } else { return 0; } } else { switch (sndf->samplebytes) { case 1: { uint8_t *in = (uint8_t*)bufi; for (size_t i = 0; i < cnt; i++) buf[i] = ((float)in[i] - 128.0f) * 256.0f; } break; case 2: { int16_t *in = (int16_t*)bufi; int swap = sndf->swap; if (swap) { for (size_t i = 0; i < cnt; i++) buf[i] = (float)SWAP16(in[i]); } else { for (size_t i = 0; i < cnt; i++) buf[i] = (float)in[i]; } } break; case 3: { uint8_t *in = (uint8_t*)bufi; if (!sndf->bigendian) { for (size_t i = 0; i < cnt; i++) { int s = in[3*i] | (in[3*i+1] << 8) | (in[3*i+2] << 16); if (s & 0x800000) s |= 0xff000000; buf[i] = (float)s / 256.0f; } } else { for (size_t i = 0; i < cnt; i++) { int s = (in[3*i] << 16) | (in[3*i+1] << 8) | in[3*i+2]; if (s & 0x800000) s |= 0xff000000; buf[i] = (float)s / 256.0f; } } } break; case 4: { int32_t *in = (int32_t*)bufi; int swap = sndf->swap; if (swap) { for (size_t i = 0; i < cnt; i++) buf[i] = (float)SWAP32(in[i]) / 65536.0f; } else { for (size_t i = 0; i < cnt; i++) buf[i] = (float)in[i] / 65536.0f; } } break; default: return 0; } } if (map) chan_remap((int32_t *)buf, sndf->channels, cnt / sndf->channels, map); return cnt; } size_t wav_read_int24(pcmfile_t *sndf, int32_t *buf, size_t num, int *map) { int size; int i; uint8_t *bufi; if ((sndf->samplebytes > 4) || (sndf->samplebytes < 1)) return 0; bufi = (uint8_t *)buf + sizeof(*buf) * num - sndf->samplebytes * (num - 1) - sizeof(*buf); size = fread(bufi, sndf->samplebytes, num, sndf->f); // convert to 24 bit // fix endianness switch (sndf->samplebytes) { case 1: /* this is endian clean */ for (i = 0; i < size; i++) buf[i] = (bufi[i] - 128) * 65536; break; case 2: { int swap = sndf->swap; int16_t *in = (int16_t *)bufi; if (swap) { for (i = 0; i < size; i++) buf[i] = ((uint32_t)SWAP16(in[i])) << 8; } else { for (i = 0; i < size; i++) buf[i] = ((int32_t)in[i]) << 8; } } break; case 3: if (!sndf->bigendian) { for (i = 0; i < size; i++) { int s = bufi[3 * i] | (bufi[3 * i + 1] << 8) | (bufi[3 * i + 2] << 16); if (s & 0x800000) s |= 0xff000000; buf[i] = s; } } else // big endian input { for (i = 0; i < size; i++) { int s = (bufi[3 * i] << 16) | (bufi[3 * i + 1] << 8) | bufi[3 * i + 2]; if (s & 0x800000) s |= 0xff000000; buf[i] = s; } } break; case 4: { int swap = sndf->swap; if (swap) { for (i = 0; i < size; i++) buf[i] = SWAP32(buf[i]); } } break; } if (map) chan_remap(buf, sndf->channels, size / sndf->channels, map); return size; } int wav_close(pcmfile_t *sndf) { int i = fclose(sndf->f); free(sndf); return i; } knik0-faac-1950446/frontend/input.h000066400000000000000000000023261522734263700167610ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Copyright (C) 2002 Krzysztof Nikiel * * 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. */ #ifndef _INPUT_H #define _INPUT_H #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #ifdef __cplusplus extern "C" { #endif typedef struct { FILE *f; int channels; int samplebytes; int samplerate; int samples; int bigendian; int swap; int isfloat; } pcmfile_t; pcmfile_t *wav_open_read(const char *path, int rawchans); size_t wav_read_float32(pcmfile_t *sndf, float *buf, size_t num, int *map); size_t wav_read_int24(pcmfile_t *sndf, int32_t *buf, size_t num, int *map); int wav_close(pcmfile_t *file); #ifdef __cplusplus } #endif #endif /* _INPUT_H */ knik0-faac-1950446/frontend/main.c000066400000000000000000001227611522734263700165470ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Copyright (C) 2001 Menno Bakker * Copyright (C) 2002-2017 Krzysztof Nikiel * Copyright (C) 2004 Dan Villiom P. Christiansen * * 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef _WIN32 #include #include #else #include #endif /* the BSD derivatives don't define __unix__ */ #if defined(__APPLE__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__bsdi__) #define __unix__ #endif #ifdef __unix__ #include #include #endif #include #include #include #include #include #include #include #ifdef HAVE_GETOPT_H # include #else # include "getopt.h" # include "getopt.c" #endif #include "mp4write.h" #ifdef _WIN32 # undef stderr # define stderr stdout #endif #include "input.h" #include #define FALSE 0 #define TRUE 1 enum flags { SHORTCTL_FLAG = 300, MPEGVERS_FLAG, ARTIST_FLAG, ARTIST_SORT_FLAG, TITLE_FLAG, GENRE_FLAG, ALBUM_FLAG, ALBUM_SORT_FLAG, ALBUM_ARTIST_FLAG, ALBUM_ARTIST_SORT_FLAG, TRACK_FLAG, DISC_FLAG, YEAR_FLAG, COVER_ART_FLAG, COMMENT_FLAG, WRITER_FLAG, WRITER_SORT_FLAG, TAG_FLAG, CREATION_TIME_FLAG, HELP_QUAL, HELP_IO, HELP_MP4, HELP_ADVANCED, OPT_JOINT, OPT_PNS, OBJTYPE_FLAG }; typedef struct { char *shorthelp; char *longhelp; } help_t; const char *usage = "Usage: %s [options] infile\n\n"; static help_t help_qual[] = { {"-q \tSet encoding quality.\n", "\t\tSet default variable bitrate (VBR) quantizer quality in percent.\n" "\t\tmax. 5000, min. 10.\n" "\t\tdefault: 100, averages at approx. 120 kbps VBR for a normal\n" "\t\tstereo input file with 16 bit and 44.1 kHz sample rate\n" }, {"-b \tSet average bitrate to x kbps. (ABR)\n", "\t\tSet average bitrate (ABR) to approximately kbps.\n" "\t\tmax. ~500 (stereo)\n"}, {"-c \tSet the bandwidth in Hz.\n", "\t\tThe actual frequency is adjusted to maximize upper spectral band\n" "\t\tusage.\n"}, {NULL, NULL} }; static help_t help_io[] = { {"-o \tSet output file to X (only for one input file)\n", "\t\tonly for one input file; you can use *.aac, *.mp4, *.m4a or\n" "\t\t*.m4b as file extension, and the file format will be set\n" "\t\tautomatically to ADTS or MP4).\n"}, {"-\t\tUse stdin/stdout\n", "\t\tIf you simply use a hyphen/minus sign instead\n" "\t\tof a filename, FAAC can encode directly from stdin,\n" "\t\tthus enabling piping from other applications and utilities. The\n" "\t\tsame works for stdout as well, so FAAC can pipe its output to\n" "\t\tother apps such as a server.\n"}, {"-v \t\tverbosity level (-v0 is quiet mode)\n", NULL}, {"-r\t\tUse RAW AAC output file.\n", "\t\tGenerate raw AAC bitstream (i.e. without any headers).\n" "\t\tNot advised!!!, RAW AAC files are practically useless!!!\n"}, {"-P\t\tRaw PCM input mode (default 44100Hz 16bit stereo).\n", "\t\tRaw PCM input mode (default: off, i.e. expecting a WAV header;\n" "\t\tnecessary for input files or bitstreams without a header; using\n" "\t\tonly -P assumes the default values for -R, -B and -C in the\n" "\t\tinput file).\n"}, {"-R \tRaw PCM input rate.\n", "\t\tRaw PCM input sample rate in Hz (default: 44100 Hz, max. 96 kHz)\n"}, {"-B \tRaw PCM input sample size (8, 16 (default), 24 or 32bits).\n", "\t\tRaw PCM input sample size (default: 16, also possible 8, 24, 32\n" "\t\tbit fixed or float input).\n"}, {"-C \tRaw PCM input channels.\n", "\t\tRaw PCM input channels (default: 2, max. 8).\n"}, {"-X\t\tRaw PCM swap input bytes\n", "\t\tRaw PCM swap input bytes (default: bigendian).\n"}, {"-I \tInput channel config, default is 3,4 (Center third, LF fourth)\n", "\t\tInput multichannel configuration (default: 3,4 which means\n" "\t\tCenter is third and LFE is fourth like in 5.1 WAV, so you only\n" "\t\thave to specify a different position of these two mono channels\n" "\t\tin your multichannel input files if they haven't been reordered\n" "\t\talready).\n"}, {"--ignorelength\tIgnore wav length from header (useful with files over 4 GB)\n", NULL}, {"--overwrite\t\tOverwrite existing output file", NULL}, {NULL, NULL} }; static help_t help_mp4[] = { {"-w\tWrap AAC data in MP4 container (default for *.mp4, *.m4a and *.m4b)\n", NULL}, {"--tag Add named tag (iTunes '----')\n", NULL}, {"--artist \tSet artist name\n", NULL}, {"--artistsort \tSet artist sort order\n", NULL}, {"--composer \tSet composer name\n", NULL}, {"--composersort \tSet composer sort order\n", NULL}, {"--title \tSet title/track name\n", NULL}, {"--genre \tSet genre number\n", NULL}, {"--album \tSet album/performer\n", NULL}, {"--albumartist \tSet album artist\n", NULL}, {"--albumartistsort \tSet album artist sort order\n", NULL}, {"--albumsort \tSet album sort order\n", NULL}, {"--compilation\tMark as compilation\n", NULL}, {"--track \tSet track number\n", NULL}, {"--disc \tSet disc number\n", NULL}, {"--year \tSet year\n", NULL}, {"--cover-art \tRead cover art from file X\n", "\t\tSupported image formats are GIF, JPEG, and PNG.\n"}, {"--comment \tSet comment\n", NULL}, {"--creation-time \tSet creation/modification time (auto, now, or timestamp)\n", NULL}, {NULL, NULL} }; static help_t help_advanced[] = { {"--tns \tEnable coding of TNS, temporal noise shaping.\n", NULL}, {"--no-tns\tDisable coding of TNS, temporal noise shaping.\n", NULL}, {"--joint 0\tDisable joint stereo coding.\n", NULL}, {"--joint 1\tUse Mid/Side coding.\n", NULL}, {"--joint 2\tUse Intensity Stereo coding.\n", NULL}, {"--joint 3\tUse Mixed Mode (dynamic M/S and IS) coding (default).\n", NULL}, {"--pns <0 .. 10>\tPNS level; 0=disabled.\n", NULL}, {"--mpeg-vers X\tForce AAC MPEG version, X can be 2 or 4\n", NULL}, {"--object-type X\tForce AAC object type: lc, he-aac-v1, or auto (default)\n", NULL}, {"--shortctl X\tEnforce block type (0 = both (default); 1 = no short; 2 = no\n" "\t\tlong).\n", NULL}, {NULL, NULL} }; static struct { int id; char *name; char *option; help_t *help; } g_help[] = { {HELP_QUAL, "Quality-related options", "--help-qual", help_qual}, {HELP_IO, "Input/output options", "--help-io", help_io}, {HELP_MP4, "MP4 specific options", "--help-mp4", help_mp4}, {HELP_ADVANCED, "Advanced options, only for testing purposes", "--help-advanced", help_advanced}, {0} }; char *license = "\nPlease note that the use of this software may require the payment of patent\n" "royalties. You need to consider this issue before you start building derivative\n" "works. We are not warranting or indemnifying you in any way for patent\n" "royalities! YOU ARE SOLELY RESPONSIBLE FOR YOUR OWN ACTIONS!\n" "\n" "FAAC is free software, licensed under the GNU Lesser General Public\n" "License (LGPL), version 2.1 or later:\n" "\n" "FAAC - Freeware Advanced Audio Coder\n" "Copyright (C) 1999-2001, Menno Bakker\n" "Copyright (C) 2002-2017, Krzysztof Nikiel\n" "Copyright (C) 2004, Dan Villiom P. Christiansen\n" "Copyright (C) 2005-2026, Fabian Greffrath\n" "Copyright (C) 2026, Nils Schimmelmann\n" "\n" "This library is free software; you can redistribute it and/or\n" "modify it under the terms of the GNU Lesser General Public\n" "License as published by the Free Software Foundation; either\n" "version 2.1 of the License, or (at your option) any later version.\n" "\n" "This library is distributed in the hope that it will be useful,\n" "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n" "Lesser General Public License for more details.\n" "\n"; #ifndef min #define min(a,b) ( (a) < (b) ? (a) : (b) ) #endif /* globals */ char *progName; #ifndef _WIN32 volatile int running = 1; #endif enum container_format { NO_CONTAINER, MP4_CONTAINER, }; #ifndef _WIN32 void signal_handler(int signal) { (void)signal; running = 0; } #endif static void help0(help_t *h, int l) { int cnt; for (cnt = 0; h[cnt].shorthelp; cnt++) { printf(" %s", h[cnt].shorthelp); if (l && h[cnt].longhelp) printf("%s", h[cnt].longhelp); } printf("\n\n"); } static void help(int mode) { int cnt; static const char *name = "faac"; printf(usage, name); switch (mode) { case '?': case 'h': case 'H': printf("Help options:\n" "\t-h\t\tShort help on using FAAC\n" "\t-H\t\tDescription of all options for FAAC.\n" "\t--license\tLicense terms for FAAC.\n"); for (cnt = 0; g_help[cnt].id; cnt++) printf("\t%s\t%s\n", g_help[cnt].option, g_help[cnt].name); if (mode == 'h') { for (cnt = 0; cnt < 2; cnt++) { printf("%s:\n", g_help[cnt].name); help0(g_help[cnt].help, 0); } } if (mode == 'H') { for (cnt = 0; cnt < g_help[cnt].id; cnt++) { printf("%s:\n", g_help[cnt].name); help0(g_help[cnt].help, 1); } } break; default: for (cnt = 0; g_help[cnt].id; cnt++) if (g_help[cnt].id == mode) { printf("%s:\n", g_help[cnt].name); help0(g_help[cnt].help, 1); break; } break; } } static int check_image_header(const char *buf) { if (!strncmp(buf, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8)) return 1; /* PNG */ else if (!strncmp(buf, "\xFF\xD8\xFF\xE0", 4) || !strncmp(buf, "\xFF\xD8\xFF\xE1", 4)) return 1; /* JPEG */ else if (!strncmp(buf, "GIF87a", 6) || !strncmp(buf, "GIF89a", 6)) return 1; /* GIF */ else return 0; } static int *mkChanMap(int channels, int center, int lf) { int *map; int inpos; int outpos; if (!center && !lf) return NULL; if (channels < 3) return NULL; if (lf > 0) lf--; else lf = channels - 1; // default AAC position if (center > 0) center--; else center = 0; // default AAC position map = malloc(channels * sizeof(map[0])); if (!map) return NULL; memset(map, 0, channels * sizeof(map[0])); outpos = 0; if ((center >= 0) && (center < channels)) map[outpos++] = center; inpos = 0; for (; outpos < (channels - 1) && inpos < channels; inpos++) { if (inpos == center) continue; if (inpos == lf) continue; map[outpos++] = inpos; } if (outpos < channels) { if ((lf >= 0) && (lf < channels)) map[outpos] = lf; else if (inpos < channels) map[outpos] = inpos; } return map; } #define fprintf if(verbose)fprintf int main(int argc, char *argv[]) { int frames, currentFrame; faac_encoder *hEncoder = NULL; pcmfile_t *infile = NULL; unsigned long samplesInput, maxBytesOutput, totalBytesWritten = 0; faac_params params; faac_status fstatus; enum faac_mpeg_version mpegVersion = FAAC_MPEG4; enum faac_object_type objectType = FAAC_OBJ_AUTO; int jointmode = -1; int pnslevel = -1; static int useTns = 0; enum container_format container = NO_CONTAINER; enum faac_stream_format stream = FAAC_STREAM_ADTS; int cutOff = -1; int bitRate = 0; unsigned long quantqual = 0; int chanC = 3; int chanLF = 4; char *audioFileName = NULL; char *aacFileName = NULL; char *aacFileExt = NULL; int aacFileNameGiven = 0; float *pcmbuf; int *chanmap = NULL; unsigned char *bitbuf; int samplesRead = 0; const char *dieMessage = NULL; int rawChans = 0; // disabled by default int rawBits = 16; int rawRate = 44100; int rawEndian = 1; int shortctl = FAAC_SHORTCTL_NORMAL; FILE *outfile = NULL; unsigned int ntracks = 0, trackno = 0; unsigned int ndiscs = 0, discno = 0; static int compilation = 0; const char *artist = NULL, *artistsort = NULL, *title = NULL, *album = NULL, *albumartist = NULL, *albumartistsort = NULL, *albumsort = NULL, *year = NULL, *comment = NULL, *composer = NULL, *composersort = NULL, *tagname = 0, *tagval = 0, *creation_time_str = NULL; int genre = 0; uint8_t *artData = NULL; uint64_t artSize = 0; uint64_t encoded_samples = 0; unsigned int frameSize; uint64_t input_samples = 0; const char *faac_id_string; const char *faac_copyright_string; static int ignorelen = 0; int verbose = 1; static int overwrite = 0; #ifndef _WIN32 // install signal handler signal(SIGINT, signal_handler); signal(SIGTERM, signal_handler); #endif // get faac version faac_library_info libinfo = { .struct_size = sizeof(libinfo) }; faac_get_library_info(&libinfo); faac_id_string = libinfo.version; faac_copyright_string = libinfo.copyright; if (!strcmp(faac_id_string, PACKAGE_VERSION)) { fprintf(stderr, "Freeware Advanced Audio Coder\nFAAC %s\n\n", faac_id_string); } else { fprintf(stderr, __FILE__ "(%d): wrong libfaac version " "(expected %s, found %s)\n", __LINE__, PACKAGE_VERSION, faac_id_string); return 1; } /* begin process command line */ progName = argv[0]; if (argc < 2) { help('?'); return 1; } while (1) { static struct option long_options[] = { {"help", 0, 0, 'h'}, {"help-qual", 0, 0, HELP_QUAL}, {"help-io", 0, 0, HELP_IO}, {"help-mp4", 0, 0, HELP_MP4}, {"help-advanced", 0, 0, HELP_ADVANCED}, {"raw", 0, 0, 'r'}, {"joint", required_argument, 0, OPT_JOINT}, {"pns", required_argument, 0, OPT_PNS}, {"cutoff", 1, 0, 'c'}, {"quality", 1, 0, 'q'}, {"pcmraw", 0, 0, 'P'}, {"pcmsamplerate", 1, 0, 'R'}, {"pcmsamplebits", 1, 0, 'B'}, {"pcmchannels", 1, 0, 'C'}, {"shortctl", 1, 0, SHORTCTL_FLAG}, {"tns", 0, &useTns, 1}, {"no-tns", 0, &useTns, 0}, {"mpeg-version", 1, 0, MPEGVERS_FLAG}, {"object-type", 1, 0, OBJTYPE_FLAG}, {"license", 0, 0, 'L'}, {"createmp4", 0, 0, 'w'}, {"artist", 1, 0, ARTIST_FLAG}, {"artistsort", 1, 0, ARTIST_SORT_FLAG}, {"title", 1, 0, TITLE_FLAG}, {"album", 1, 0, ALBUM_FLAG}, {"albumartist", 1, 0, ALBUM_ARTIST_FLAG}, {"albumartistsort", 1, 0, ALBUM_ARTIST_SORT_FLAG}, {"albumsort", 1, 0, ALBUM_SORT_FLAG}, {"track", 1, 0, TRACK_FLAG}, {"disc", 1, 0, DISC_FLAG}, {"genre", 1, 0, GENRE_FLAG}, {"year", 1, 0, YEAR_FLAG}, {"cover-art", 1, 0, COVER_ART_FLAG}, {"comment", 1, 0, COMMENT_FLAG}, {"composer", 1, 0, WRITER_FLAG}, {"composersort", 1, 0, WRITER_SORT_FLAG}, {"compilation", 0, &compilation, 1}, {"pcmswapbytes", 0, 0, 'X'}, {"ignorelength", 0, &ignorelen, 1}, {"tag", 1, 0, TAG_FLAG}, {"overwrite", 0, &overwrite, 1}, {"creation-time", 1, 0, CREATION_TIME_FLAG}, {0, 0, 0, 0} }; int c = -1; int option_index = 0; c = getopt_long(argc, argv, "Hhb:m:o:rnc:q:PR:B:C:I:Xwv:", long_options, &option_index); if (c == -1) break; if (!c) continue; switch (c) { case 'o': { int l = strlen(optarg); aacFileName = malloc(l + 1); if (!aacFileName) { fprintf(stderr, "out of memory\n"); return 1; } memcpy(aacFileName, optarg, l); aacFileName[l] = '\0'; aacFileNameGiven = 1; } break; case 'r': { stream = FAAC_STREAM_RAW; break; } case 'c': { unsigned int i; if (sscanf(optarg, "%u", &i) > 0) { cutOff = i; } break; } case 'b': { unsigned int i; if (sscanf(optarg, "%u", &i) > 0) { bitRate = 1000 * i; } break; } case 'q': { unsigned int i; if (sscanf(optarg, "%u", &i) > 0) { if (i > 0) quantqual = i; } break; } case 'I': sscanf(optarg, "%d,%d", &chanC, &chanLF); break; case 'P': rawChans = 2; // enable raw input break; case 'R': { unsigned int i; if (sscanf(optarg, "%u", &i) > 0) { rawRate = i; rawChans = (rawChans > 0) ? rawChans : 2; } break; } case 'B': { unsigned int i; if (sscanf(optarg, "%u", &i) > 0) { if (i > 32) i = 32; if (i < 8) i = 8; rawBits = i; rawChans = (rawChans > 0) ? rawChans : 2; } break; } case 'C': { unsigned int i; if (sscanf(optarg, "%u", &i) > 0) rawChans = i; break; } case 'w': container = MP4_CONTAINER; break; case ARTIST_FLAG: artist = optarg; break; case ARTIST_SORT_FLAG: artistsort = optarg; break; case WRITER_FLAG: composer = optarg; break; case WRITER_SORT_FLAG: composersort = optarg; break; case TITLE_FLAG: title = optarg; break; case ALBUM_FLAG: album = optarg; break; case ALBUM_ARTIST_FLAG: albumartist = optarg; break; case ALBUM_ARTIST_SORT_FLAG: albumartistsort = optarg; break; case ALBUM_SORT_FLAG: albumsort = optarg; break; case TRACK_FLAG: if (sscanf(optarg, "%u/%u", &trackno, &ntracks) < 1) dieMessage = "Wrong track number.\n"; break; case DISC_FLAG: if (sscanf(optarg, "%u/%u", &discno, &ndiscs) < 1) dieMessage = "Wrong disc number.\n"; break; case GENRE_FLAG: genre = atoi(optarg); if ((genre < 0) || (genre > 255)) dieMessage = "Genre number out of range.\n"; genre++; break; case YEAR_FLAG: year = optarg; break; case COMMENT_FLAG: comment = optarg; break; case TAG_FLAG: tagname = optarg; if (!(tagval = strchr(optarg, ','))) dieMessage = "Missing tag value.\n"; else *(char *)tagval++ = 0; if (!dieMessage && mp4_add_custom_tag(tagname, tagval)) dieMessage = "Couldn't add tag (out of memory).\n"; break; case CREATION_TIME_FLAG: creation_time_str = optarg; break; case COVER_ART_FLAG: { FILE *artFile = fopen(optarg, "rb"); if (artFile) { uint64_t r; fseek(artFile, 0, SEEK_END); artSize = ftell(artFile); artData = malloc(artSize); if (!artData) { fprintf(stderr, "out of memory\n"); fclose(artFile); return 1; } fseek(artFile, 0, SEEK_SET); clearerr(artFile); r = fread(artData, artSize, 1, artFile); if (r != 1) { dieMessage = "Error reading cover art file!\n"; free(artData); artData = NULL; } else if (artSize < 12 || !check_image_header((const char *) artData)) { /* the above expression checks the image signature */ dieMessage = "Unsupported cover image file format!\n"; free(artData); artData = NULL; } fclose(artFile); } else { dieMessage = "Error opening cover art file!\n"; } break; } case SHORTCTL_FLAG: shortctl = atoi(optarg); break; case MPEGVERS_FLAG: switch (atoi(optarg)) { case 2: mpegVersion = FAAC_MPEG2; break; case 4: mpegVersion = FAAC_MPEG4; break; default: dieMessage = "Unrecognised MPEG version!\n"; } break; case OBJTYPE_FLAG: if (!strcmp(optarg, "lc")) objectType = FAAC_OBJ_LOW; else if (!strcmp(optarg, "he-aac-v1")) objectType = FAAC_OBJ_HE_AAC_V1; else if (!strcmp(optarg, "auto")) objectType = FAAC_OBJ_AUTO; else dieMessage = "Unrecognised object type (use lc, he-aac-v1, or auto)!\n"; break; case 'L': fprintf(stderr, "%s", faac_copyright_string); dieMessage = license; break; case 'X': rawEndian = 0; break; case 'v': verbose = atoi(optarg); break; case HELP_QUAL: case HELP_IO: case HELP_MP4: case HELP_ADVANCED: case 'H': case 'h': help(c); return 1; break; case OPT_JOINT: jointmode = atoi(optarg); break; case OPT_PNS: pnslevel = atoi(optarg); break; case '?': default: help('?'); return 1; break; } } /* check that we have at least one non-option arguments */ if (!dieMessage && (argc - optind) > 1 && aacFileNameGiven) dieMessage = "Cannot encode several input files to one output file.\n"; if (argc - optind < 1 || dieMessage) { fprintf(stderr, dieMessage, progName, progName, progName, progName); return 1; } while (argc - optind > 0) { /* get the input file name */ audioFileName = argv[optind++]; } /* generate the output file name, if necessary */ if (!aacFileNameGiven) { char *t = strrchr(audioFileName, '.'); int l = t ? strlen(audioFileName) - strlen(t) : strlen(audioFileName); aacFileExt = container == MP4_CONTAINER ? ".m4a" : ".aac"; aacFileName = malloc(l + 1 + 4); if (!aacFileName) { fprintf(stderr, "out of memory\n"); return 1; } memcpy(aacFileName, audioFileName, l); memcpy(aacFileName + l, aacFileExt, 4); aacFileName[l + 4] = '\0'; } else { aacFileExt = strrchr(aacFileName, '.'); if (aacFileExt && (!strcmp(".m4a", aacFileExt) || !strcmp(".m4b", aacFileExt) || !strcmp(".mp4", aacFileExt))) container = MP4_CONTAINER; } /* open the audio input file */ if (rawChans > 0) // use raw input { infile = wav_open_read(audioFileName, 1); if (infile) { infile->bigendian = rawEndian; infile->channels = rawChans; infile->samplebytes = rawBits / 8; infile->samplerate = rawRate; infile->samples /= (infile->channels * infile->samplebytes); } } else // header input infile = wav_open_read(audioFileName, 0); if (infile == NULL) { fprintf(stderr, "Couldn't open input file %s\n", audioFileName); return 1; } if (container != MP4_CONTAINER && (ntracks || trackno || artist || artistsort || title || album || albumartist || albumartistsort || albumsort || year || artData || genre || comment || discno || ndiscs || composer || composersort || compilation)) { fprintf(stderr, "Metadata requires MP4 output!\n"); return 1; } if (container == MP4_CONTAINER) { mpegVersion = FAAC_MPEG4; stream = FAAC_STREAM_RAW; } if (cutOff <= 0) { if (cutOff < 0) // default cutOff = 0; else // disabled cutOff = infile->samplerate / 2; } if (cutOff > (infile->samplerate / 2)) cutOff = infile->samplerate / 2; if (shortctl == FAAC_SHORTCTL_NOSHORT) { fprintf(stderr, "disabling short blocks\n"); } else if (shortctl == FAAC_SHORTCTL_NOLONG) { fprintf(stderr, "disabling long blocks\n"); } if (pnslevel > 0 && mpegVersion == FAAC_MPEG2) { fprintf(stderr, "PNS not allowed in MPEG-2 mode, disabling PNS\n"); pnslevel = 0; } /* put the options into the parameter struct and open the encoder */ faac_params_init(¶ms); params.sample_rate = infile->samplerate; params.num_channels = infile->channels; params.mpeg_version = mpegVersion; params.object_type = objectType; params.joint_mode = (jointmode >= 0) ? (enum faac_joint_mode)jointmode : params.joint_mode; params.use_lfe = (infile->channels >= 6); params.use_tns = useTns ? true : false; params.short_control = (enum faac_shortctl_mode)shortctl; if (pnslevel >= 0) params.pns_level = pnslevel; if (quantqual > 0) { params.quant_quality = quantqual; params.bit_rate = 0; } if (bitRate) params.bit_rate = bitRate / infile->channels; params.bandwidth = cutOff; params.output_format = stream; params.input_format = FAAC_INPUT_FLOAT; /* Reject too many channels with a specific message, rather than the generic * "invalid argument" that faac_encoder_open would otherwise return. */ { faac_library_info libinfo = { .struct_size = sizeof(libinfo) }; faac_get_library_info(&libinfo); if ((unsigned)infile->channels > libinfo.max_channels) { fprintf(stderr, "Input file %s has %u channels, but this build of " "libfaac supports at most %u.\n", audioFileName, (unsigned)infile->channels, libinfo.max_channels); wav_close(infile); return 1; } } fstatus = faac_encoder_open(¶ms, &hEncoder); if (fstatus != FAAC_OK) { fprintf(stderr, "Couldn't open encoder instance for input file %s: %s\n", audioFileName, faac_strerror(fstatus)); wav_close(infile); return 1; } /* buffer sizes and resolved settings come from the now-configured encoder */ faac_encoder_info info; info.struct_size = sizeof(info); faac_encoder_get_info(hEncoder, &info); samplesInput = (unsigned long)info.frame_samples * infile->channels; maxBytesOutput = info.max_output_bytes; frameSize = samplesInput / infile->channels; pcmbuf = (float *) malloc(samplesInput * sizeof(float)); bitbuf = (unsigned char *) malloc(maxBytesOutput * sizeof(unsigned char)); if (!pcmbuf || !bitbuf) { fprintf(stderr, "out of memory\n"); return 1; } chanmap = mkChanMap(infile->channels, chanC, chanLF); if (chanmap) { fprintf(stderr, "Remapping input channels: Center=%d, LFE=%d\n", chanC, chanLF); } /* AUTO may have resolved to LC or HE-AAC; read back the decision. */ objectType = info.object_type; /* initialize MP4 creation */ if (container == MP4_CONTAINER) { if (!strcmp(aacFileName, "-")) { fprintf(stderr, "cannot encode MP4 to stdout\n"); return 1; } if (mp4_open(aacFileName, overwrite)) { fprintf(stderr, "Couldn't create output file %s\n", aacFileName); return 1; } mp4_set_format(infile->samplerate, infile->channels, infile->samplebytes * 8); } else { /* open the aac output file */ if (!strcmp(aacFileName, "-")) { outfile = stdout; } else { outfile = fopen(aacFileName, "wb"); } if (!outfile) { fprintf(stderr, "Couldn't create output file %s\n", aacFileName); return 1; } } /* report the effective settings the encoder resolved */ cutOff = info.bandwidth; quantqual = info.quant_quality; bitRate = info.bit_rate; int resolvedPns = info.pns_level; if (bitRate) { fprintf(stderr, "Initial quantization quality: %ld\n", quantqual); fprintf(stderr, "Average bitrate: %d kbps/channel\n", (bitRate + 500) / 1000); } else fprintf(stderr, "Quantization quality: %ld\n", quantqual); fprintf(stderr, "Bandwidth: %d Hz\n", cutOff); if (resolvedPns > 0) fprintf(stderr, "PNS level: %d\n", resolvedPns); fprintf(stderr, "Object type: %s", (objectType == FAAC_OBJ_HE_AAC_V1) ? "HE-AAC v1" : "Low Complexity"); fprintf(stderr, " (MPEG-%d)", (mpegVersion == FAAC_MPEG4) ? 4 : 2); if (params.use_tns) fprintf(stderr, " + TNS"); switch(params.joint_mode) { case FAAC_JOINT_MS: fprintf(stderr, " + M/S"); break; case FAAC_JOINT_IS: fprintf(stderr, " + IS"); break; case FAAC_JOINT_MIXED: fprintf(stderr, " + Mixed"); break; default: break; } if (resolvedPns > 0) fprintf(stderr, " + PNS"); fprintf(stderr, "\n"); fprintf(stderr, "Container format: "); switch (container) { case NO_CONTAINER: switch (stream) { case FAAC_STREAM_RAW: fprintf(stderr, "Headerless AAC (RAW)\n"); break; case FAAC_STREAM_ADTS: fprintf(stderr, "Transport Stream (ADTS)\n"); break; default: break; } break; case MP4_CONTAINER: fprintf(stderr, "MPEG-4 File Format (MP4)\n"); break; } int showcnt = 0; #ifdef _WIN32 long begin = GetTickCount(); #endif if (infile->samples) frames = ((infile->samples + frameSize - 1) / frameSize) + 1; else frames = 0; currentFrame = 0; fprintf(stderr, "Encoding %s to %s\n", audioFileName, aacFileName); if (frames != 0) { fprintf(stderr, " frame | bitrate | elapsed/estim | " "play/CPU | ETA\n"); } else { fprintf(stderr, " frame | elapsed | play/CPU\n"); } /* encoding loop */ #ifdef _WIN32 for (;;) #else while (running) #endif { int bytesWritten; if (!ignorelen) { if (input_samples < (uint64_t)infile->samples || infile->samples == 0) samplesRead = wav_read_float32(infile, pcmbuf, samplesInput, chanmap); else samplesRead = 0; if (input_samples + (samplesRead / infile->channels) > (uint64_t)infile->samples && infile->samples != 0) samplesRead = (infile->samples - input_samples) * infile->channels; } else samplesRead = wav_read_float32(infile, pcmbuf, samplesInput, chanmap); input_samples += samplesRead / infile->channels; /* call the actual encoding routine */ { uint32_t nbytes = 0; fstatus = faac_encoder_encode(hEncoder, pcmbuf, (uint32_t)samplesRead, bitbuf, (uint32_t)maxBytesOutput, &nbytes); bytesWritten = (fstatus == FAAC_OK) ? (int)nbytes : -1; } if (bytesWritten) { currentFrame++; showcnt--; totalBytesWritten += bytesWritten; } if ((showcnt <= 0) || !bytesWritten) { double timeused; #ifdef __unix__ struct rusage usage; #endif #ifdef _WIN32 char percent[MAX_PATH + 20]; timeused = (GetTickCount() - begin) * 1e-3; #else #ifdef __unix__ if (getrusage(RUSAGE_SELF, &usage) == 0) { timeused = (double) usage.ru_utime.tv_sec + (double) usage.ru_utime.tv_usec * 1e-6; } else timeused = 0; #else timeused = (double) clock() * (1.0 / CLOCKS_PER_SEC); #endif #endif if (currentFrame && (timeused > 0.1)) { showcnt += 50; if (frames != 0) { fprintf(stderr, "\r%7d/%-7d (%3d%%) | %5.1f | %6.1f/%-6.1f | %7.2fx | %.1f ", currentFrame, frames, currentFrame * 100 / frames, ((double) totalBytesWritten * 8.0 / 1000.0) / ((double) infile->samples / infile->samplerate * currentFrame / frames), timeused, timeused * frames / currentFrame, ((double)frameSize * currentFrame / infile->samplerate) / timeused, timeused * (frames - currentFrame) / currentFrame); } else { fprintf(stderr, "\r %7d | %7.1f | %7.2fx ", currentFrame, timeused, ((double)frameSize * currentFrame / infile->samplerate) / timeused); } fflush(stderr); #ifdef _WIN32 if (frames != 0) { snprintf(percent, sizeof(percent), "%.2f%% encoding %s", 100.0 * currentFrame / frames, audioFileName); SetConsoleTitle(percent); } #endif } } /* all done, bail out */ if (!samplesRead && !bytesWritten) break; if (bytesWritten < 0) { fprintf(stderr, "faac_encoder_encode() failed: %s\n", faac_strerror(fstatus)); break; } if (bytesWritten > 0) { uint64_t frame_samples = input_samples - encoded_samples; if (frame_samples > frameSize) frame_samples = frameSize; if (container == MP4_CONTAINER) { if (mp4_write_frame(bitbuf, (uint32_t)bytesWritten, (uint32_t)frame_samples)) { fprintf(stderr, "mp4_write_frame() failed\n"); break; } } else fwrite(bitbuf, 1, bytesWritten, outfile); encoded_samples += frame_samples; } } fprintf(stderr, "\n"); if (container == MP4_CONTAINER) { char *version_string = malloc(strlen(faac_id_string) + 6); if (!version_string) { fprintf(stderr, "out of memory\n"); return 1; } const uint8_t *ascData = NULL; uint32_t ascSize = 0; faac_encoder_asc(hEncoder, &ascData, &ascSize); mp4_set_decoder_config((unsigned char *)ascData, ascSize); snprintf(version_string, strlen(faac_id_string) + 6, "FAAC %s", faac_id_string); mp4_set_encoder(version_string); #define SETTAG(id, x) if(x) mp4_set_tag(id, x) SETTAG(MP4TAG_ARTIST, artist); SETTAG(MP4TAG_ARTISTSORT, artistsort); SETTAG(MP4TAG_COMPOSER, composer); SETTAG(MP4TAG_COMPOSERSORT, composersort); SETTAG(MP4TAG_TITLE, title); SETTAG(MP4TAG_ALBUM, album); SETTAG(MP4TAG_ALBUMARTIST, albumartist); SETTAG(MP4TAG_ALBUMARTISTSORT, albumartistsort); SETTAG(MP4TAG_ALBUMSORT, albumsort); SETTAG(MP4TAG_YEAR, year); SETTAG(MP4TAG_COMMENT, comment); #undef SETTAG if (trackno) mp4_set_track(trackno, ntracks); if (discno) mp4_set_disc(discno, ndiscs); if (compilation) mp4_set_compilation(compilation); if (genre) mp4_set_genre(genre); if (artData && artSize) mp4_set_cover(artData, (int)artSize); { uint32_t final_creation_time = 0; if (creation_time_str) { if (!strcmp(creation_time_str, "auto")) { if (strcmp(audioFileName, "-") == 0) { fprintf(stderr, "cannot use --creation-time auto with stdin, defaulting to 0\n"); } else { struct stat st; if (stat(audioFileName, &st) == 0) { final_creation_time = (uint32_t)st.st_mtime; } else { fprintf(stderr, "couldn't stat() input file %s, defaulting to 0\n", audioFileName); } } } else if (!strcmp(creation_time_str, "now")) { final_creation_time = (uint32_t)time(NULL); } else { char *endptr; errno = 0; final_creation_time = (uint32_t)strtoul(creation_time_str, &endptr, 10); if (errno != 0 || *endptr != '\0') { fprintf(stderr, "invalid creation time %s, defaulting to 0\n", creation_time_str); final_creation_time = 0; } } } else { const char *sde = getenv("SOURCE_DATE_EPOCH"); if (sde) { char *endptr; errno = 0; final_creation_time = (uint32_t)strtoul(sde, &endptr, 10); if (errno != 0 || *endptr != '\0') { fprintf(stderr, "invalid SOURCE_DATE_EPOCH %s, ignoring\n", sde); final_creation_time = 0; } } } mp4_set_creation_time(final_creation_time); } if (mp4_finish()) fprintf(stderr, "mp4_finish() failed: output file may be incomplete\n"); mp4_close(); free(version_string); if (verbose >= 2) { fprintf(stderr, "%u frames\n", mp4_frame_count()); fprintf(stderr, "%u output samples\n", mp4_sample_count()); fprintf(stderr, "max bitrate: %u\n", mp4_max_bitrate()); fprintf(stderr, "avg bitrate: %u\n", mp4_avg_bitrate()); fprintf(stderr, "max frame size: %u\n", mp4_max_frame_size()); } } else { fclose(outfile); } faac_encoder_close(&hEncoder); wav_close(infile); if (artData) free(artData); if (pcmbuf) free(pcmbuf); if (bitbuf) free(bitbuf); if (aacFileNameGiven) free(aacFileName); if (chanmap) free(chanmap); return 0; } knik0-faac-1950446/frontend/maingui.c000066400000000000000000000354241522734263700172530ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Copyright (C) 2001 Menno Bakker * * 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. */ #include #include #include #include #include "input.h" #include #include "resource.h" static HINSTANCE hInstance; static char inputFilename[_MAX_PATH], outputFilename[_MAX_PATH]; static BOOL Encoding = FALSE; static BOOL SelectFileName(HWND hParent, char *filename, BOOL forReading) { OPENFILENAME ofn; ofn.lStructSize = sizeof(OPENFILENAME); ofn.hwndOwner = hParent; ofn.hInstance = hInstance; ofn.nFilterIndex = 0; ofn.lpstrFileTitle = NULL; ofn.nMaxFileTitle = 31; filename [0] = 0x00; ofn.lpstrFile = (LPSTR)filename; ofn.nMaxFile = _MAX_PATH; ofn.lpstrInitialDir = NULL; ofn.lpstrCustomFilter = NULL; ofn.nMaxCustFilter = 0; ofn.nFileOffset = 0; ofn.nFileExtension = 0; ofn.lCustData = 0; ofn.lpfnHook = NULL; ofn.lpTemplateName = NULL; if (forReading) { char filters[] = { "Wave Files (*.wav)\0*.wav\0" \ "AIFF Files (*.aif;*.aiff;*.aifc)\0*.aif;*.aiff;*.aifc\0" \ "AU Files (*.au)\0*.au\0" \ "All Files (*.*)\0*.*\0\0" }; ofn.lpstrFilter = filters; ofn.lpstrDefExt = "wav"; ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY; ofn.lpstrTitle = "Select Source File"; return GetOpenFileName (&ofn); } else { char filters [] = { "AAC Files (*.aac)\0*.aac\0" \ "All Files (*.*)\0*.*\0\0" }; ofn.lpstrFilter = filters; ofn.lpstrDefExt = "aac"; ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY; ofn.lpstrTitle = "Select Output File"; return GetSaveFileName(&ofn); } } static void AwakeDialogControls(HWND hWnd) { char szTemp[64]; pcmfile_t *infile = NULL; unsigned int sampleRate, numChannels; char *pExt; if ((infile = wav_open_read(inputFilename, 0)) == NULL) return; /* determine input file parameters */ sampleRate = infile->samplerate; numChannels = infile->channels; wav_close(infile); SetDlgItemText (hWnd, IDC_INPUTFILENAME, inputFilename); strncpy(outputFilename, inputFilename, sizeof(outputFilename) - 5); outputFilename[sizeof(outputFilename) - 5] = '\0'; pExt = strrchr(outputFilename, '.'); if (pExt == NULL) lstrcat(outputFilename, ".aac"); else lstrcpy(pExt, ".aac"); EnableWindow(GetDlgItem(hWnd, IDC_OUTPUTFILENAME), TRUE); EnableWindow(GetDlgItem(hWnd, IDC_SELECT_OUTPUTFILE), TRUE); SetDlgItemText(hWnd, IDC_OUTPUTFILENAME, outputFilename); wsprintf(szTemp, "%iHz %ich", sampleRate, numChannels); SetDlgItemText(hWnd, IDC_INPUTPARAMS, szTemp); EnableWindow(GetDlgItem(hWnd, IDOK), TRUE); } static DWORD WINAPI EncodeFile(LPVOID pParam) { HWND hWnd = (HWND) pParam; pcmfile_t *infile = NULL; GetDlgItemText(hWnd, IDC_INPUTFILENAME, inputFilename, sizeof(inputFilename)); GetDlgItemText(hWnd, IDC_OUTPUTFILENAME, outputFilename, sizeof(outputFilename)); /* open the input file */ if ((infile = wav_open_read(inputFilename, 0)) != NULL) { /* determine input file parameters */ unsigned int sampleRate = infile->samplerate; unsigned int numChannels = infile->channels; unsigned long inputSamples; unsigned long maxOutputBytes; /* set up parameters and open the encoder */ faac_params params; faac_encoder *hEncoder = NULL; char szTemp[256]; faac_params_init(¶ms); params.sample_rate = sampleRate; params.num_channels = numChannels; params.input_format = FAAC_INPUT_32BIT; /* wav_read_int24 -> 24-in-32 int */ { HWND hOT = GetDlgItem(hWnd, IDC_OBJECTTYPE); LRESULT sel = SendMessage(hOT, CB_GETCURSEL, 0, 0); LRESULT data = (sel != CB_ERR) ? SendMessage(hOT, CB_GETITEMDATA, (WPARAM)sel, 0) : CB_ERR; params.object_type = (data != CB_ERR) ? (enum faac_object_type)data : FAAC_OBJ_AUTO; } { LRESULT mode = SendMessage(GetDlgItem(hWnd, IDC_JOINTMODE), CB_GETCURSEL, 0, 0); params.joint_mode = (mode == CB_ERR) ? FAAC_JOINT_MIXED : (enum faac_joint_mode)mode; } params.use_tns = IsDlgButtonChecked(hWnd, IDC_USETNS) == BST_CHECKED; params.use_lfe = IsDlgButtonChecked(hWnd, IDC_USELFE) == BST_CHECKED; params.output_format = IsDlgButtonChecked(hWnd, IDC_USERAW) == BST_CHECKED ? FAAC_STREAM_RAW : FAAC_STREAM_ADTS; params.mpeg_version = (enum faac_mpeg_version) SendMessage(GetDlgItem(hWnd, IDC_MPEGVERSION), CB_GETCURSEL, 0, 0); GetDlgItemText(hWnd, IDC_QUALITY, szTemp, sizeof(szTemp)); params.quant_quality = atoi(szTemp); params.bit_rate = 0; /* quality-driven; no bitrate control in this dialog */ if (IsDlgButtonChecked(hWnd, IDC_BWCTL) == BST_CHECKED) { GetDlgItemText(hWnd, IDC_BANDWIDTH, szTemp, sizeof(szTemp)); params.bandwidth = atoi(szTemp); } if (faac_encoder_open(¶ms, &hEncoder) == FAAC_OK) { HANDLE hOutfile; faac_encoder_info info; info.struct_size = sizeof(info); faac_encoder_get_info(hEncoder, &info); inputSamples = (unsigned long)info.frame_samples * numChannels; maxOutputBytes = info.max_output_bytes; sprintf(szTemp, "%u", info.quant_quality); SetDlgItemText(hWnd, IDC_QUALITY, szTemp); sprintf(szTemp, "%u", info.bandwidth); SetDlgItemText(hWnd, IDC_BANDWIDTH, szTemp); /* open the output file */ hOutfile = CreateFile(outputFilename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hOutfile != INVALID_HANDLE_VALUE) { UINT startTime = GetTickCount(), lastUpdated = 50; DWORD totalBytesRead = 0; unsigned int bytesInput = 0; DWORD numberOfBytesWritten = 0; int *pcmbuf; unsigned char *bitbuf; char HeaderText[50]; char Percentage[5]; pcmbuf = (int*)LocalAlloc(0, inputSamples*sizeof(int)); bitbuf = (unsigned char*)LocalAlloc(0, maxOutputBytes*sizeof(unsigned char)); SendDlgItemMessage(hWnd, IDC_PROGRESS, PBM_SETRANGE, 0, MAKELPARAM(0, 1024)); SendDlgItemMessage(hWnd, IDC_PROGRESS, PBM_SETPOS, 0, 0); for ( ;; ) { int bytesWritten; UINT timeElapsed, timeEncoded; bytesInput = wav_read_int24(infile, pcmbuf, inputSamples, NULL) * sizeof(int); SendDlgItemMessage (hWnd, IDC_PROGRESS, PBM_SETPOS, (unsigned long)((float)totalBytesRead * 1024.0f / (infile->samples*sizeof(int)*numChannels)), 0); /* Percentage for Dialog Output */ _itoa((int)((float)totalBytesRead * 100.0f / (infile->samples*sizeof(int)*numChannels)),Percentage,10); lstrcpy(HeaderText,"FAAC GUI: "); lstrcat(HeaderText,Percentage); lstrcat(HeaderText,"%"); SendMessage(hWnd,WM_SETTEXT,0,(LPARAM)HeaderText); totalBytesRead += bytesInput; timeElapsed = (GetTickCount () - startTime) / 10; timeEncoded = 100.0 * totalBytesRead / (sampleRate * numChannels * sizeof (int)); if (timeElapsed > (lastUpdated + 20)) { float factor; unsigned timeLeft; lastUpdated = timeElapsed; factor = (float) timeEncoded / (float) (timeElapsed ? timeElapsed : 1); timeLeft = 10.0 * infile->samples / sampleRate / factor - 0.1 * timeElapsed; sprintf(szTemp, "Playing time: %2.2i:%04.1f\tEncoding time: %2.2i:%04.1f\n" "Play/enc factor: %.2f\tEstimated time left: %2.2i:%04.1f", timeEncoded / 6000, 0.01 * (timeEncoded % 6000), timeElapsed / 6000, 0.01 * (timeElapsed % 6000), factor, timeLeft / 600, 0.1 * (timeLeft % 600) ); SetDlgItemText(hWnd, IDC_TIME, szTemp); } /* call the actual encoding routine */ { uint32_t nbytes = 0; faac_status st = faac_encoder_encode(hEncoder, pcmbuf, (uint32_t)(bytesInput/sizeof(int)), bitbuf, (uint32_t)maxOutputBytes, &nbytes); bytesWritten = (st == FAAC_OK) ? (int)nbytes : -1; } /* Stop Pressed */ if ( !Encoding ) break; /* all done, bail out */ if (!bytesInput && !bytesWritten) break; if (bytesWritten < 0) { MessageBox (hWnd, "faac_encoder_encode failed!", "Error", MB_OK | MB_ICONSTOP); break; } WriteFile(hOutfile, bitbuf, bytesWritten, &numberOfBytesWritten, NULL); } CloseHandle(hOutfile); if (pcmbuf) LocalFree(pcmbuf); if (bitbuf) LocalFree(bitbuf); } faac_encoder_close(&hEncoder); } wav_close(infile); MessageBeep(1); SendDlgItemMessage(hWnd, IDC_PROGRESS, PBM_SETPOS, 0, 0); } else { MessageBox(hWnd, "Couldn't open input file!", "Error", MB_OK | MB_ICONSTOP); } SendMessage(hWnd,WM_SETTEXT,0,(LPARAM)"FAAC GUI"); Encoding = FALSE; SetDlgItemText(hWnd, IDOK, "Encode"); return 0; } static BOOL WINAPI DialogProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_INITDIALOG: { faac_library_info libinfo = { .struct_size = sizeof(libinfo) }; char txt[100]; faac_get_library_info(&libinfo); sprintf(txt, "libfaac version %s", libinfo.version ? libinfo.version : "?"); SetDlgItemText(hWnd, IDC_COMPILEDATE, txt); } inputFilename[0] = 0x00; SendMessage(GetDlgItem(hWnd, IDC_MPEGVERSION), CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)"MPEG4"); SendMessage(GetDlgItem(hWnd, IDC_MPEGVERSION), CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)"MPEG2"); SendMessage(GetDlgItem(hWnd, IDC_MPEGVERSION), CB_SETCURSEL, 0, 0); { HWND hOT = GetDlgItem(hWnd, IDC_OBJECTTYPE); LRESULT idx; idx = SendMessage(hOT, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)"Auto"); SendMessage(hOT, CB_SETITEMDATA, idx, (LPARAM)FAAC_OBJ_AUTO); idx = SendMessage(hOT, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)"Low Complexity"); SendMessage(hOT, CB_SETITEMDATA, idx, (LPARAM)FAAC_OBJ_LOW); idx = SendMessage(hOT, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)"HE-AAC v1"); SendMessage(hOT, CB_SETITEMDATA, idx, (LPARAM)FAAC_OBJ_HE_AAC_V1); SendMessage(hOT, CB_SETCURSEL, 0, 0); } SendMessage(GetDlgItem(hWnd, IDC_JOINTMODE), CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)"None"); SendMessage(GetDlgItem(hWnd, IDC_JOINTMODE), CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)"M/S"); SendMessage(GetDlgItem(hWnd, IDC_JOINTMODE), CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)"IS"); SendMessage(GetDlgItem(hWnd, IDC_JOINTMODE), CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)"Mixed"); SendMessage(GetDlgItem(hWnd, IDC_JOINTMODE), CB_SETCURSEL, 3, 0); CheckDlgButton(hWnd, IDC_USELFE, FALSE); CheckDlgButton(hWnd, IDC_USERAW, FALSE); CheckDlgButton(hWnd, IDC_USETNS, FALSE); SetDlgItemText(hWnd, IDC_QUALITY, "100"); SetDlgItemText(hWnd, IDC_BANDWIDTH, "0"); DragAcceptFiles(hWnd, TRUE); return TRUE; case WM_DROPFILES: if (DragQueryFile((HDROP) wParam, 0, (LPSTR) inputFilename, _MAX_PATH - 1)) AwakeDialogControls(hWnd); DragFinish((HDROP) wParam); return FALSE; case WM_COMMAND: switch (wParam) { case IDOK: if ( !Encoding ) { DWORD retval; CreateThread(NULL,0,EncodeFile,hWnd,0,&retval); Encoding = TRUE; SetDlgItemText(hWnd, IDOK, "Stop"); } else { Encoding = FALSE; SetDlgItemText(hWnd, IDOK, "Encode"); } return TRUE; case IDCANCEL: EndDialog(hWnd, TRUE); return TRUE; case IDC_SELECT_INPUTFILE: if (SelectFileName(hWnd, inputFilename, TRUE)) AwakeDialogControls(hWnd); break; case IDC_SELECT_OUTPUTFILE: if (SelectFileName(hWnd, outputFilename, FALSE)) { SetDlgItemText(hWnd, IDC_OUTPUTFILENAME, outputFilename); } break; case IDC_BWCTL: switch (IsDlgButtonChecked(hWnd, IDC_BWCTL)) { case BST_CHECKED: EnableWindow(GetDlgItem(hWnd, IDC_BANDWIDTH), TRUE); //SetDlgItemText(hWnd, IDC_BANDWIDTH, "0"); break; case BST_UNCHECKED: EnableWindow(GetDlgItem(hWnd, IDC_BANDWIDTH), FALSE); //SetDlgItemText(hWnd, IDC_BANDWIDTH, ""); break; } break; case MAKEWPARAM(IDC_OBJECTTYPE, CBN_SELCHANGE): { HWND hOT = GetDlgItem(hWnd, IDC_OBJECTTYPE); HWND hMPG = GetDlgItem(hWnd, IDC_MPEGVERSION); LRESULT sel = SendMessage(hOT, CB_GETCURSEL, 0, 0); LRESULT data = (sel != CB_ERR) ? SendMessage(hOT, CB_GETITEMDATA, (WPARAM)sel, 0) : CB_ERR; if (data == (LRESULT)FAAC_OBJ_HE_AAC_V1) { SendMessage(hMPG, CB_SETCURSEL, 0, 0); EnableWindow(hMPG, FALSE); } else { EnableWindow(hMPG, TRUE); } break; } } break; } return FALSE; } int WINAPI WinMain (HINSTANCE hInst, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { hInstance = hInst; return DialogBox(hInstance, MAKEINTRESOURCE (IDD_MAINDIALOG), NULL, (DLGPROC) DialogProc); } knik0-faac-1950446/frontend/meson.build000066400000000000000000000011071522734263700176070ustar00rootroot00000000000000faac = executable('faac', ['main.c', 'input.c', 'mp4write.c', 'input.h', 'mp4write.h'], include_directories: ['..', '../include'], link_with: libfaac, install: true ) if target_machine.system() == 'windows' windows = import('windows') resource_src = windows.compile_resources('faacgui.rc', 'icon.rc') faacgui = executable('faacgui', ['input.c', 'input.h', 'maingui.c', 'resource.h'] + resource_src, include_directories: ['..', '../include'], link_with: libfaac, win_subsystem: 'windows', install: true ) endif knik0-faac-1950446/frontend/mp4write.c000066400000000000000000000542651522734263700174010ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Copyright (C) 2026 Nils Schimmelmann * * 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. */ #include #include #include #ifdef _WIN32 #include #define access _access #define W_OK 2 #else #include #endif #include "mp4write.h" #if defined(__has_builtin) #if __has_builtin(__builtin_bswap32) && __has_builtin(__builtin_bswap16) #define MP4_HAVE_BSWAP_BUILTINS 1 #endif #elif defined(__GNUC__) #define MP4_HAVE_BSWAP_BUILTINS 1 #endif #if defined(MP4_HAVE_BSWAP_BUILTINS) #define BSWAP32 __builtin_bswap32 #define BSWAP16 __builtin_bswap16 #elif defined(_MSC_VER) #define BSWAP32 _byteswap_ulong #define BSWAP16 _byteswap_ushort #else static inline uint32_t BSWAP32(uint32_t x) { return (x >> 24) | ((x >> 8) & 0xff00) | ((x << 8) & 0xff0000) | (x << 24); } static inline uint16_t BSWAP16(uint16_t x) { return (uint16_t)((x >> 8) | (x << 8)); } #endif enum { MP4_EPOCH_OFFSET = 2082844800, /* seconds from 1904-01-01 to 1970-01-01 */ /* identity transform: required by the spec even though audio-only files never use it; fixed-point format differs per field */ MP4_FP1616_ONE = 0x00010000, /* unity in 16.16 fixed point: rate, matrix a/d */ MP4_FP0230_ONE = 0x40000000, /* unity in 2.30 fixed point: matrix w */ MP4_FP0808_ONE = 0x0100, /* unity in 8.8 fixed point: volume */ MP4_DESC_HDR = 5, /* descriptor tag byte + 4-byte expandable size, see put_descriptor() */ /* iTunes 'data' atom type codes */ ITUNES_DATA_BINARY = 0, ITUNES_DATA_TEXT = 1, ITUNES_DATA_UINT8 = 0x15, ITUNES_DATA_IMAGE = 0x0d, /* DecoderConfigDescriptor fixed fields (ISO/IEC 14496-1) */ MP4_OBJECT_TYPE_AUDIO_ISO_14496_3 = 0x40, MP4_STREAM_TYPE_AUDIO = 0x15, /* streamType=5 (audio) << 2 | upStream=0 | reserved=1 */ MP4_DECODER_BUFFER_SIZE = 6144, /* bufferSizeDB, arbitrary but generous for one AAC frame */ MP4_TRACK_ID = 1, /* single-track file: 'trak' and 'tkhd' both hardcode this */ MP4_NEXT_TRACK_ID = 2, /* mvhd's hint for the next trak ID a future edit would use */ MP4_URL_SELF_CONTAINED = 1, /* dref 'url ' flags bit: media data lives in this file, no external ref */ MP4_IO_BUFSIZE = 65536, /* stdio buffer for the mdat write path, see mp4_open() */ }; static struct { uint32_t samplerate; uint32_t samples; uint32_t channels; uint32_t bits; uint16_t buffersize; struct { uint32_t max; uint32_t avg; uint32_t size; uint32_t samples; } bitrate; uint32_t framesamples; struct { uint32_t *data; uint32_t ents; uint32_t bufsize; } frame; struct { const uint8_t *data; unsigned long size; } asc; FILE *fout; uint32_t mdatofs; uint32_t mdatsize; uint32_t creation_time; const char *encoder; const char *tags[MP4TAG_COUNT]; uint8_t compilation; uint32_t trackno; uint32_t ntracks; uint32_t discno; uint32_t ndiscs; int genre; struct { const uint8_t *data; int size; } cover; struct { const char *name; const char *value; } *custom; int customcnt; int customcap; } g_mp4 = { 0 }; /* Atom trees assembled all at once (ftyp/free in mp4_open, moov in mp4_finish) are built here so end_atom() can patch sizes with a memcpy instead of an lseek round trip. mdat audio bytes stream straight to g_mp4.fout instead, since they arrive incrementally during encoding and can be far larger than is worth buffering. */ static uint8_t *g_membuf = NULL; static size_t g_mempos = 0; static size_t g_memcap = 0; /* Set whenever mem_write() can't grow g_membuf to fit a write and drops it instead; checked by mp4_finish() so a truncated moov atom is never mistaken for a successfully written one. */ static int g_mem_error = 0; static inline void mem_write(const void *data, size_t size) { if (g_membuf) { if (g_mempos + size > g_memcap) { size_t new_cap = g_memcap ? g_memcap * 2 : 1024; /* cap growth so a bogus/huge write request can't spin the doubling loop forever or overflow new_cap */ while (g_mempos + size > new_cap && new_cap < (1UL << 31)) new_cap *= 2; if (g_mempos + size > new_cap) { g_mem_error = 1; return; } void *tmp = realloc(g_membuf, new_cap); if (!tmp) { free(g_membuf); g_membuf = NULL; g_mem_error = 1; return; } g_membuf = (uint8_t *)tmp; g_memcap = new_cap; } memcpy(g_membuf + g_mempos, data, size); g_mempos += size; } else if (g_mp4.fout && !g_mem_error) { if (fwrite(data, 1, size, g_mp4.fout) != size) g_mem_error = 1; } } static inline void put_u32(uint32_t val) { #ifndef WORDS_BIGENDIAN val = BSWAP32(val); #endif if (g_membuf && g_mempos + 4 <= g_memcap) { memcpy(g_membuf + g_mempos, &val, 4); g_mempos += 4; } else { mem_write(&val, 4); } } static inline void put_u16(uint16_t val) { #ifndef WORDS_BIGENDIAN val = BSWAP16(val); #endif if (g_membuf && g_mempos + 2 <= g_memcap) { memcpy(g_membuf + g_mempos, &val, 2); g_mempos += 2; } else { mem_write(&val, 2); } } static inline void put_u8(uint8_t val) { mem_write(&val, 1); } static inline void put_data(const void *data, size_t size) { mem_write(data, size); } /* An atom's size field comes before its contents but isn't known until the contents (and any nested atoms) are written, so reserve it as 0 here and let end_atom() backpatch the real value once it's known. */ static inline long start_atom(const char *name) { long pos = g_membuf ? (long)g_mempos : (g_mp4.fout ? ftell(g_mp4.fout) : 0); put_u32(0); put_data(name, 4); return pos; } static inline void end_atom(long pos) { if (g_membuf) { uint32_t size = (uint32_t)(g_mempos - pos); #ifndef WORDS_BIGENDIAN size = BSWAP32(size); #endif memcpy(g_membuf + pos, &size, 4); } else if (g_mp4.fout) { long curr = ftell(g_mp4.fout); fseek(g_mp4.fout, pos, SEEK_SET); put_u32((uint32_t)(curr - pos)); fseek(g_mp4.fout, curr, SEEK_SET); } } void mp4_set_creation_time(uint32_t t) { if (t == 0) g_mp4.creation_time = 0; else g_mp4.creation_time = t + MP4_EPOCH_OFFSET; } /* creation/modification time is informational only; the spec (14496-12) never * requires a real clock. Emit 0 ("unknown") by default so encodes are * byte-reproducible, matching common muxer practice. */ static uint32_t get_mp4_time(void) { return g_mp4.creation_time; } /* MPEG-4 descriptor sizes are a base-128 varint with a continuation bit, but always emitted here as the full 4-byte form (continuation bit set on all but the last byte) since some parsers assume that fixed width rather than the shorter encodings the spec also permits. */ static void put_descriptor(uint8_t tag, uint32_t size) { uint8_t buf[5]; buf[0] = tag; buf[1] = ((size >> 21) & 0x7f) | 0x80; buf[2] = ((size >> 14) & 0x7f) | 0x80; buf[3] = ((size >> 7) & 0x7f) | 0x80; buf[4] = (size & 0x7f); mem_write(buf, 5); } /* Only resets per-output-file write state (frame table, mdat bookkeeping, bitrate accumulators). Tag/format config set by the caller, which may happen before or after mp4_open() depending on the option, is left untouched. */ static void reset_write_state(void) { free(g_mp4.frame.data); g_mp4.frame.data = NULL; g_mp4.frame.ents = 0; g_mp4.frame.bufsize = 0; g_mp4.framesamples = 0; g_mp4.samples = 0; g_mp4.buffersize = 0; g_mp4.mdatofs = 0; g_mp4.mdatsize = 0; memset(&g_mp4.bitrate, 0, sizeof(g_mp4.bitrate)); } int mp4_open(const char *path, int overwrite) { mp4_close(); /* in case of a retry after a failed previous mp4_open() */ reset_write_state(); g_mem_error = 0; if (!overwrite && access(path, 0) == 0) return 1; g_mp4.fout = fopen(path, "wb"); if (!g_mp4.fout) return 1; setvbuf(g_mp4.fout, NULL, _IOFBF, MP4_IO_BUFSIZE); g_mp4.frame.bufsize = 1024; g_mp4.frame.data = (uint32_t *)malloc(g_mp4.frame.bufsize * sizeof(uint32_t)); if (!g_mp4.frame.data) return 1; g_mempos = 0; g_memcap = 1024; g_membuf = (uint8_t *)malloc(g_memcap); if (!g_membuf) { free(g_mp4.frame.data); g_mp4.frame.data = NULL; return 1; } long ftyp = start_atom("ftyp"); put_data("M4A \0\0\0\0M4A mp42isom", 20); end_atom(ftyp); fwrite(g_membuf, 1, g_mempos, g_mp4.fout); free(g_membuf); g_membuf = NULL; /* mdat's size isn't known until every frame has been written, so its header goes out now as a placeholder and gets patched in mp4_finish(). stco also needs mdatofs to point past this header at the first audio byte, which is why it's recorded here rather than computed later. */ g_mp4.mdatofs = (uint32_t)ftell(g_mp4.fout) + 8; put_u32(0); put_data("mdat", 4); return g_mem_error ? 1 : 0; } void mp4_set_format(uint32_t samplerate, uint32_t channels, uint32_t bits) { g_mp4.samplerate = samplerate; g_mp4.channels = channels; g_mp4.bits = bits; } void mp4_set_decoder_config(const uint8_t *asc, unsigned long size) { g_mp4.asc.data = asc; g_mp4.asc.size = size; } void mp4_set_encoder(const char *value) { g_mp4.encoder = value; } void mp4_set_tag(mp4_tag_id_t id, const char *value) { if (id < MP4TAG_COUNT) g_mp4.tags[id] = value; } void mp4_set_genre(int genre) { g_mp4.genre = genre; } void mp4_set_compilation(int flag) { g_mp4.compilation = (uint8_t)flag; } void mp4_set_track(uint32_t num, uint32_t total) { g_mp4.trackno = num; g_mp4.ntracks = total; } void mp4_set_disc(uint32_t num, uint32_t total) { g_mp4.discno = num; g_mp4.ndiscs = total; } void mp4_set_cover(const uint8_t *data, int size) { g_mp4.cover.data = data; g_mp4.cover.size = size; } int mp4_add_custom_tag(const char *name, const char *value) { if (g_mp4.customcnt >= g_mp4.customcap) { int new_cap = g_mp4.customcap ? g_mp4.customcap * 2 : 8; void *tmp = realloc(g_mp4.custom, new_cap * sizeof(*g_mp4.custom)); if (!tmp) return -1; g_mp4.custom = tmp; g_mp4.customcap = new_cap; } g_mp4.custom[g_mp4.customcnt].name = name; g_mp4.custom[g_mp4.customcnt].value = value; g_mp4.customcnt++; return 0; } int mp4_write_frame(const uint8_t *data, uint32_t size, uint32_t samples) { if (!g_mp4.fout) return -1; if (fwrite(data, 1, size, g_mp4.fout) != size) return -1; g_mp4.mdatsize += size; g_mp4.samples += samples; /* only count frames at the established frame length toward the bitrate window, so a shorter trailing frame doesn't skew it */ if (g_mp4.framesamples <= samples) { g_mp4.bitrate.size += size; g_mp4.bitrate.samples += samples; if (g_mp4.bitrate.samples >= g_mp4.samplerate) { uint32_t br = (uint32_t)((uint64_t)8 * g_mp4.bitrate.size * g_mp4.samplerate / g_mp4.bitrate.samples); if (g_mp4.bitrate.max < br) g_mp4.bitrate.max = br; g_mp4.bitrate.size = 0; g_mp4.bitrate.samples = 0; } g_mp4.framesamples = samples; } if (g_mp4.frame.ents >= g_mp4.frame.bufsize) { uint32_t new_cap = g_mp4.frame.bufsize ? g_mp4.frame.bufsize * 2 : 1024; /* bound the frame table so an unreasonably long encode can't grow this without limit or overflow new_cap * sizeof(uint32_t) */ if (new_cap > (1UL << 30)) return -1; uint32_t *tmp = (uint32_t *)realloc(g_mp4.frame.data, new_cap * sizeof(uint32_t)); if (!tmp) return -1; g_mp4.frame.data = tmp; g_mp4.frame.bufsize = new_cap; } g_mp4.frame.data[g_mp4.frame.ents++] = size; if (g_mp4.buffersize < (uint16_t)size) g_mp4.buffersize = (uint16_t)size; return 0; } static void put_tag(const char *name, const char *data) { if (!data) return; long box = start_atom(name); long data_box = start_atom("data"); put_u32(ITUNES_DATA_TEXT); put_u32(0); put_data(data, strlen(data)); end_atom(data_box); end_atom(box); } static void put_tag_u8(const char *name, uint8_t val) { long box = start_atom(name); long data_box = start_atom("data"); put_u32(ITUNES_DATA_UINT8); put_u32(0); put_u8(val); end_atom(data_box); end_atom(box); } static void put_tag_genre(uint16_t genre) { long box = start_atom("gnre"); long data_box = start_atom("data"); put_u32(ITUNES_DATA_BINARY); put_u32(0); put_u16(genre); end_atom(data_box); end_atom(box); } static void put_tag_index(const char *name, uint16_t num, uint16_t total) { long box = start_atom(name); long data_box = start_atom("data"); put_u32(ITUNES_DATA_BINARY); put_u32(0); put_u16(0); put_u16(num); put_u16(total); put_u16(0); end_atom(data_box); end_atom(box); } static void put_tag_image(const uint8_t *data, int size) { long box = start_atom("covr"); long data_box = start_atom("data"); put_u32(ITUNES_DATA_IMAGE); put_u32(0); put_data(data, size); end_atom(data_box); end_atom(box); } static void put_tag_ext(const char *mean, const char *name, const char *val) { long box = start_atom("----"); long mean_box = start_atom("mean"); put_u32(0); put_data(mean, strlen(mean)); end_atom(mean_box); long name_box = start_atom("name"); put_u32(0); put_data(name, strlen(name)); end_atom(name_box); long data_box = start_atom("data"); put_u32(ITUNES_DATA_TEXT); put_u32(0); put_data(val, strlen(val)); end_atom(data_box); end_atom(box); } /* leading \xa9 marks an atom as iTunes-style "plain text" metadata, distinct from the freeform '----' atoms used by mp4_add_custom_tag() */ static const char *tag_atom_names[MP4TAG_COUNT] = { [MP4TAG_ARTIST] = "\xa9" "ART", [MP4TAG_ARTISTSORT] = "soar", [MP4TAG_COMPOSER] = "\xa9" "wrt", [MP4TAG_COMPOSERSORT] = "soco", [MP4TAG_TITLE] = "\xa9" "nam", [MP4TAG_ALBUM] = "\xa9" "alb", [MP4TAG_ALBUMARTIST] = "aART", [MP4TAG_ALBUMARTISTSORT] = "soaa", [MP4TAG_ALBUMSORT] = "soal", [MP4TAG_YEAR] = "\xa9" "day", [MP4TAG_COMMENT] = "\xa9" "cmt", }; /* Returns 0 on success, 1 on failure (mirroring mp4_open()'s convention). */ int mp4_finish(void) { if (!g_mp4.fout) return 1; g_mem_error = 0; /* now that all frames are written, go back and fill in the mdat size placeholder left by mp4_open() */ long pos = ftell(g_mp4.fout); fseek(g_mp4.fout, g_mp4.mdatofs - 8, SEEK_SET); put_u32(g_mp4.mdatsize + 8); fseek(g_mp4.fout, pos, SEEK_SET); if (g_mem_error) return 1; g_mp4.bitrate.avg = (uint32_t)((uint64_t)8 * g_mp4.mdatsize * g_mp4.samplerate / (g_mp4.samples ? g_mp4.samples : 1)); /* a file shorter than one second never crosses the sample-count threshold in mp4_write_frame, so bitrate.max would otherwise still be 0 here */ if (!g_mp4.bitrate.max) g_mp4.bitrate.max = g_mp4.bitrate.avg; g_mempos = 0; g_memcap = 65536 + (size_t)g_mp4.frame.ents * 4; g_membuf = (uint8_t *)malloc(g_memcap); if (!g_membuf) return 1; long moov = start_atom("moov"); long mvhd = start_atom("mvhd"); uint32_t now = get_mp4_time(); put_u32(0); put_u32(now); put_u32(now); put_u32(g_mp4.samplerate); put_u32(g_mp4.samples); put_u32(MP4_FP1616_ONE); put_u16(MP4_FP0808_ONE); put_u16(0); put_u32(0); put_u32(0); put_u32(MP4_FP1616_ONE); put_u32(0); put_u32(0); put_u32(0); put_u32(MP4_FP1616_ONE); put_u32(0); put_u32(0); put_u32(0); put_u32(MP4_FP0230_ONE); put_u32(0); put_u32(0); put_u32(0); put_u32(0); put_u32(0); put_u32(0); put_u32(MP4_NEXT_TRACK_ID); end_atom(mvhd); long trak = start_atom("trak"); long tkhd = start_atom("tkhd"); put_u32(1); put_u32(now); put_u32(now); put_u32(MP4_TRACK_ID); put_u32(0); put_u32(g_mp4.samples); put_u32(0); put_u32(0); put_u16(0); put_u16(0); put_u16(MP4_FP0808_ONE); put_u16(0); put_u32(MP4_FP1616_ONE); put_u32(0); put_u32(0); put_u32(0); put_u32(MP4_FP1616_ONE); put_u32(0); put_u32(0); put_u32(0); put_u32(MP4_FP0230_ONE); put_u32(0); put_u32(0); end_atom(tkhd); long mdia = start_atom("mdia"); long mdhd = start_atom("mdhd"); put_u32(0); put_u32(now); put_u32(now); put_u32(g_mp4.samplerate); put_u32(g_mp4.samples); put_u16(0); put_u16(0); end_atom(mdhd); long hdlr = start_atom("hdlr"); put_u32(0); put_u32(0); put_data("soun", 4); put_u32(0); put_u32(0); put_u32(0); put_u8(0); end_atom(hdlr); long minf = start_atom("minf"); long smhd = start_atom("smhd"); put_u32(0); put_u16(0); put_u16(0); end_atom(smhd); long dinf = start_atom("dinf"); long dref = start_atom("dref"); put_u32(0); put_u32(1); long url = start_atom("url "); put_u32(MP4_URL_SELF_CONTAINED); end_atom(url); end_atom(dref); end_atom(dinf); long stbl = start_atom("stbl"); long stsd = start_atom("stsd"); put_u32(0); put_u32(1); long mp4a = start_atom("mp4a"); put_u8(0); put_u8(0); put_u8(0); put_u8(0); put_u8(0); put_u8(0); put_u16(1); put_u32(0); put_u32(0); put_u16(g_mp4.channels); put_u16(g_mp4.bits); put_u16(0); put_u16(0); put_u16(g_mp4.samplerate); put_u16(0); long esds = start_atom("esds"); put_u32(0); /* ES descriptor's declared size must cover its own fixed fields plus every nested descriptor including their headers (DecoderConfig: 13 fixed + DecSpecificInfo; SLConfig: 1 fixed byte) */ put_descriptor(3, 3 + MP4_DESC_HDR + 13 + MP4_DESC_HDR + g_mp4.asc.size + MP4_DESC_HDR + 1); put_u16(0); put_u8(0); put_descriptor(4, 13 + MP4_DESC_HDR + g_mp4.asc.size); put_u8(MP4_OBJECT_TYPE_AUDIO_ISO_14496_3); put_u8(MP4_STREAM_TYPE_AUDIO); put_u8((uint8_t)(MP4_DECODER_BUFFER_SIZE >> 16)); put_u8((uint8_t)(MP4_DECODER_BUFFER_SIZE >> 8)); put_u8((uint8_t)MP4_DECODER_BUFFER_SIZE); put_u32(g_mp4.bitrate.max); put_u32(g_mp4.bitrate.avg); put_descriptor(5, g_mp4.asc.size); put_data(g_mp4.asc.data, g_mp4.asc.size); put_descriptor(6, 1); put_u8(2); end_atom(esds); end_atom(mp4a); end_atom(stsd); long stts = start_atom("stts"); put_u32(0); put_u32(1); put_u32(g_mp4.frame.ents); put_u32(g_mp4.framesamples); end_atom(stts); long stsc = start_atom("stsc"); put_u32(0); put_u32(1); put_u32(1); put_u32(g_mp4.frame.ents); put_u32(1); end_atom(stsc); long stsz = start_atom("stsz"); put_u32(0); put_u32(0); put_u32(g_mp4.frame.ents); if (g_mp4.frame.ents) { /* written by hand instead of looping put_u32() per entry: this table has one entry per encoded frame, so for a long file it's the hottest loop in mp4_finish() */ size_t stsz_size = (size_t)g_mp4.frame.ents * 4; if (g_mempos + stsz_size > g_memcap) { size_t new_cap = g_memcap ? g_memcap * 2 : 1024; while (g_mempos + stsz_size > new_cap && new_cap < (1UL << 31)) new_cap *= 2; void *tmp = (g_mempos + stsz_size > new_cap) ? NULL : realloc(g_membuf, new_cap); if (!tmp) { free(g_membuf); g_membuf = NULL; return 1; } g_membuf = (uint8_t *)tmp; g_memcap = new_cap; } uint8_t *p = g_membuf + g_mempos; #ifdef WORDS_BIGENDIAN memcpy(p, g_mp4.frame.data, stsz_size); #else for (uint32_t i = 0; i < g_mp4.frame.ents; i++) { uint32_t val = BSWAP32(g_mp4.frame.data[i]); memcpy(p + i * 4, &val, 4); } #endif g_mempos += stsz_size; } end_atom(stsz); long stco = start_atom("stco"); put_u32(0); put_u32(1); put_u32(g_mp4.mdatofs); end_atom(stco); end_atom(stbl); end_atom(minf); end_atom(mdia); end_atom(trak); long udta = start_atom("udta"); long meta = start_atom("meta"); put_u32(0); long hdlr2 = start_atom("hdlr"); put_u32(0); put_u32(0); put_data("mdirappl", 8); put_u32(0); put_u32(0); put_u8(0); end_atom(hdlr2); long ilst = start_atom("ilst"); put_tag("\xa9" "too", g_mp4.encoder); for (int i = 0; i < MP4TAG_COUNT; i++) put_tag(tag_atom_names[i], g_mp4.tags[i]); if (g_mp4.genre) put_tag_genre((uint16_t)g_mp4.genre); if (g_mp4.compilation) put_tag_u8("cpil", g_mp4.compilation); if (g_mp4.trackno) put_tag_index("trkn", (uint16_t)g_mp4.trackno, (uint16_t)g_mp4.ntracks); if (g_mp4.discno) put_tag_index("disk", (uint16_t)g_mp4.discno, (uint16_t)g_mp4.ndiscs); if (g_mp4.cover.data) put_tag_image(g_mp4.cover.data, g_mp4.cover.size); for (int i = 0; i < g_mp4.customcnt; i++) put_tag_ext("faac", g_mp4.custom[i].name, g_mp4.custom[i].value); end_atom(ilst); end_atom(meta); end_atom(udta); end_atom(moov); int ok = !g_mem_error && fwrite(g_membuf, 1, g_mempos, g_mp4.fout) == g_mempos; free(g_membuf); g_membuf = NULL; return ok ? 0 : 1; } int mp4_close(void) { if (g_mp4.fout) { fclose(g_mp4.fout); g_mp4.fout = NULL; } free(g_mp4.frame.data); g_mp4.frame.data = NULL; free(g_membuf); g_membuf = NULL; return 0; } uint32_t mp4_frame_count(void) { return g_mp4.frame.ents; } uint32_t mp4_sample_count(void) { return g_mp4.samples; } uint32_t mp4_max_bitrate(void) { return g_mp4.bitrate.max; } uint32_t mp4_avg_bitrate(void) { return g_mp4.bitrate.avg; } uint32_t mp4_max_frame_size(void) { return g_mp4.buffersize; } knik0-faac-1950446/frontend/mp4write.h000066400000000000000000000034701522734263700173760ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Copyright (C) 2026 Nils Schimmelmann * * 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. */ #ifndef MP4WRITE_H #define MP4WRITE_H #include typedef enum { MP4TAG_ARTIST, MP4TAG_ARTISTSORT, MP4TAG_COMPOSER, MP4TAG_COMPOSERSORT, MP4TAG_TITLE, MP4TAG_ALBUM, MP4TAG_ALBUMARTIST, MP4TAG_ALBUMARTISTSORT, MP4TAG_ALBUMSORT, MP4TAG_YEAR, MP4TAG_COMMENT, MP4TAG_COUNT } mp4_tag_id_t; int mp4_open(const char *path, int overwrite); void mp4_set_creation_time(uint32_t t); void mp4_set_format(uint32_t samplerate, uint32_t channels, uint32_t bits); void mp4_set_decoder_config(const uint8_t *asc, unsigned long size); void mp4_set_encoder(const char *value); void mp4_set_tag(mp4_tag_id_t id, const char *value); void mp4_set_genre(int genre); void mp4_set_compilation(int flag); void mp4_set_track(uint32_t num, uint32_t total); void mp4_set_disc(uint32_t num, uint32_t total); void mp4_set_cover(const uint8_t *data, int size); int mp4_add_custom_tag(const char *name, const char *value); int mp4_write_frame(const uint8_t *data, uint32_t size, uint32_t samples); int mp4_finish(void); int mp4_close(void); uint32_t mp4_frame_count(void); uint32_t mp4_sample_count(void); uint32_t mp4_max_bitrate(void); uint32_t mp4_avg_bitrate(void); uint32_t mp4_max_frame_size(void); #endif knik0-faac-1950446/frontend/resource.h000066400000000000000000000023701522734263700174500ustar00rootroot00000000000000//{{NO_DEPENDENCIES}} // Microsoft Developer Studio generated include file. // Used by faacgui.rc // #define ID_ICON1 200 #define IDD_MAINDIALOG 102 #define IDC_INPUTFILENAME 1000 #define IDC_SELECT_INPUTFILE 1001 #define IDC_OUTPUTFILENAME 1002 #define IDC_SELECT_OUTPUTFILE 1003 #define IDC_INPUTPARAMS 1004 #define IDC_PROGRESS 1006 #define IDC_JOINTMODE 1007 #define IDC_TIME 1008 #define IDC_BANDWIDTH 1009 #define IDC_QUALITY 1010 #define IDC_USERAW 1011 #define IDC_USETNS 1012 #define IDC_USELFE2 1013 #define IDC_USELFE 1013 #define IDC_BWCTL 1014 #define IDC_COMPILEDATE 1018 #define IDC_MPEGVERSION 1020 #define IDC_OBJECTTYPE 1021 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 104 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1022 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif knik0-faac-1950446/include/000077500000000000000000000000001522734263700152525ustar00rootroot00000000000000knik0-faac-1950446/include/faac.h000066400000000000000000000325011522734263700163160ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Copyright (C) 2026 Nils Schimmelmann * * 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. */ /* * libfaac encoder API. * * This is the only public surface for the encoder. The legacy faacEnc* API * (faacEncOpen/faacEncGetCurrentConfiguration/faacEncSetConfiguration/ * faacEncEncode/faacEncClose) has been removed; see docs/libfaac.html for a * porting guide. * * Design summary (see docs/libfaac.html for the full narrative): * - Parameters are supplied once, up front, to faac_encoder_open(), so the * encoder never exists in a half-configured state and all derived values * (frame size, output-buffer bound, effective sample rate, resolved object * type) are known and queryable the instant open() returns. * - Every fallible call returns a faac_status; faac_strerror() maps a status * to a human-readable string. * - Fixed-width integer types and width-pinned enums keep the ABI identical * across LP64/LLP64 platforms and regardless of -fshort-enums. * - faac_params grows only additively: new releases append named fields and * grow sizeof(faac_params); callers MUST zero-initialize via * faac_params_init() so struct_size lets the library reconcile versions. */ #ifndef FAAC_H #define FAAC_H #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ #include #include #include /* * Tracks the libfaac SONAME, not the project release version. Lets callers * tell this faac_* API (SONAME >= 1) apart from the removed legacy faacEnc* * one (SONAME 0): * * #if defined(FAAC_VERSION_MAJOR) && (FAAC_VERSION_MAJOR >= 1) */ #define FAAC_VERSION_MAJOR 1 #define FAAC_VERSION_MINOR 0 #define FAAC_VERSION_PATCH 0 #define FAAC_VERSION_HEX \ ((FAAC_VERSION_MAJOR << 16) | (FAAC_VERSION_MINOR << 8) | FAAC_VERSION_PATCH) /* Export/visibility marker. Shared with ; guarded so including both * headers is harmless. */ #if !defined(FAACAPI) && defined(__GNUC__) && (__GNUC__ >= 4) # if defined(_WIN32) # define FAACAPI __stdcall __declspec(dllexport) # else # define FAACAPI __attribute__((visibility("default"))) # endif #endif #ifndef FAACAPI # define FAACAPI #endif /* Opaque encoder handle */ typedef struct faac_encoder faac_encoder; /* * Result codes. All values are negative except FAAC_OK so a caller can test * `status < 0` for failure. The FAAC_STATUS_MAX sentinel pins the enum to a * 32-bit underlying type for a stable ABI under -fshort-enums. */ typedef enum faac_status { FAAC_OK = 0, FAAC_ERR_INVALID_ARGUMENT = -1, /* NULL pointer, bad struct_size, or bad enum/field value */ FAAC_ERR_UNSUPPORTED = -2, /* object type not implemented, or MPEG-2 ASC requested */ FAAC_ERR_NO_MEMORY = -3, /* allocation failed */ FAAC_ERR_OUTPUT_TOO_SMALL = -4, /* output buffer smaller than max_output_bytes */ FAAC_ERR_INPUT_OVERFLOW = -5, /* in_samples exceeded the per-call frame capacity */ FAAC_ERR_INTERNAL = -6, /* unexpected failure inside the encoder core */ FAAC_STATUS_MAX = 0x7fffffff } faac_status; /* * AAC object type, numbered per the MPEG-4 Audio Object Type (AOT) registry so * a single collision-free namespace covers current and future profiles. * FAAC_OBJ_AUTO lets the library pick AAC-LC or HE-AAC by bitrate. */ enum faac_object_type { FAAC_OBJ_AUTO = 0, /* let the library choose LC or HE-AAC by bitrate */ FAAC_OBJ_LOW = 2, /* AAC-LC */ FAAC_OBJ_HE_AAC_V1 = 5, /* AAC-LC + SBR */ FAAC_OBJ_HE_AAC_V2 = 29, /* AAC-LC + SBR + PS (reserved, unimplemented) */ /* AOT 23 (LD), 39 (ELD), 42 (xHE-AAC/USAC) are intentionally NOT reserved * here; each would need its own additive configuration surface. */ FAAC_OBJ_MAX = 0x7fffffff }; enum faac_mpeg_version { FAAC_MPEG4 = 0, FAAC_MPEG2 = 1, FAAC_MPEG_MAX = 0x7fffffff }; enum faac_joint_mode { FAAC_JOINT_NONE = 0, /* independent (L/R) stereo */ FAAC_JOINT_MS, /* mid/side */ FAAC_JOINT_IS, /* intensity */ FAAC_JOINT_MIXED, /* per-band mix of M/S and intensity */ FAAC_JOINT_MAX = 0x7fffffff }; enum faac_shortctl_mode { FAAC_SHORTCTL_NORMAL = 0, /* let block switching decide */ FAAC_SHORTCTL_NOSHORT, /* force long blocks only */ FAAC_SHORTCTL_NOLONG, /* force short blocks only */ FAAC_SHORTCTL_MAX = 0x7fffffff }; enum faac_stream_format { FAAC_STREAM_RAW = 0, /* raw AAC frames (needs out-of-band ASC) */ FAAC_STREAM_ADTS = 1, /* self-framing ADTS */ FAAC_STREAM_MAX = 0x7fffffff }; /* Interpretation of the interleaved PCM handed to faac_encoder_encode(). */ enum faac_input_format { FAAC_INPUT_NULL = 0, /* invalid / unset */ FAAC_INPUT_16BIT, /* native-endian int16 */ FAAC_INPUT_24BIT, /* native-endian int24 in 24 bits (unimpl.) */ FAAC_INPUT_32BIT, /* native-endian int24 in 32 bits */ FAAC_INPUT_FLOAT, /* 32-bit float */ FAAC_INPUT_MAX = 0x7fffffff }; /* * Encoder parameters, supplied once to faac_encoder_open(). * * ALWAYS initialize with faac_params_init() before setting fields: it zeroes * the whole struct (including padding) and stamps struct_size, which is how the * library stays compatible as this struct grows. Construct one from scratch and * open() will reject it. The struct only ever grows by appending named fields. */ typedef struct faac_params { uint32_t struct_size; /* set by faac_params_init to sizeof(faac_params) */ uint32_t sample_rate; /* input/output sample rate in Hz (required) */ uint32_t num_channels; /* channel count, 1..max_channels (required) */ enum faac_mpeg_version mpeg_version; enum faac_object_type object_type; enum faac_joint_mode joint_mode; bool use_lfe; /* treat the last channel as LFE (>= 6 ch) */ bool use_tns; /* temporal noise shaping */ uint8_t reserved[2]; /* explicit pad; must remain 0 */ uint32_t bit_rate; /* target bits/sec PER CHANNEL; 0 = use quant_quality */ uint32_t bandwidth; /* cutoff in Hz; 0 = derive from bit_rate */ uint32_t quant_quality; /* quantizer quality; 0 = derive from bit_rate */ enum faac_stream_format output_format; enum faac_input_format input_format; enum faac_shortctl_mode short_control; int32_t pns_level; /* perceptual noise substitution, 0..10 (0 = off) */ const int32_t *channel_map; /* optional reorder table, num_channels entries; * NULL = identity. Caller-owned; copied by open(). */ uint32_t channel_map_count; /* entries in channel_map (0 if NULL) */ uint32_t reserved_trailing; /* explicit pad to 8-byte boundary; must be 0 */ } faac_params; /* * Resolved encoder properties, filled by faac_encoder_get_info(). All values are * final: the object type is resolved (FAAC_OBJ_AUTO becomes a concrete type), * auto-derived rate-control defaults are filled in, and buffer sizes account for * the object type (e.g. HE-AAC's 2048-sample frame and full output rate). Set * struct_size to sizeof(faac_encoder_info) before the call; the struct grows * only by appending fields, so a newer library stays compatible with an older * caller's smaller struct. */ typedef struct faac_encoder_info { uint32_t struct_size; /* set by caller to sizeof(faac_encoder_info) */ uint32_t frame_samples; /* samples/channel per full frame (1024 LC, 2048 HE) */ uint32_t max_output_bytes; /* upper bound on one encode() call's output */ uint32_t sample_rate; /* nominal full output rate in Hz (HE: extended rate) */ enum faac_object_type object_type; /* concrete type in effect (AUTO resolved) */ uint32_t bit_rate; /* resolved bits/sec per channel (0 if quality-driven) */ uint32_t bandwidth; /* resolved cutoff in Hz */ uint32_t quant_quality; /* resolved quantizer quality */ int32_t pns_level; /* resolved PNS level, 0..10 */ } faac_encoder_info; /* * Library-global facts, independent of any encoder instance: the compiled * channel ceiling, the version/copyright strings, and the SBR analysis density. * These reflect build-time options (max-channels, sbr-decimation) that the * header alone cannot report, so they are queried at runtime. Set * out->struct_size to sizeof(faac_library_info) before the call; the struct * grows only by appending fields, so a newer library stays compatible with an * older caller's smaller struct. version/copyright are static and library-owned; * do not free them. */ typedef struct faac_library_info { uint32_t struct_size; /* set by caller to sizeof(faac_library_info) */ uint32_t max_channels; /* highest num_channels this build accepts */ const char *version; /* library version string (library-owned) */ const char *copyright; /* library copyright string (library-owned) */ uint32_t sbr_decimation; /* HE-AAC SBR analysis density: every Nth slot * is analysed (1 = full quality). Build-time. */ } faac_library_info; FAACAPI faac_status faac_get_library_info(faac_library_info *out); /* Zero-initialize *p and fill in library defaults and struct_size. Returns * FAAC_ERR_INVALID_ARGUMENT if p is NULL. */ FAACAPI faac_status faac_params_init(faac_params *p); /* * Create an encoder from a fully-specified faac_params. On success writes the * new handle to *out and returns FAAC_OK. On failure *out is set to NULL and a * negative status describes why (e.g. FAAC_ERR_UNSUPPORTED for an object type * this build does not implement). The faac_params is consumed by this call and * need not outlive it. */ FAACAPI faac_status faac_encoder_open(const faac_params *p, faac_encoder **out); /* * Destroy an encoder. Pass the address of your handle; on success the handle is * set to NULL to help guard against use-after-free. Passing a pointer to a NULL * handle is a no-op that returns FAAC_OK. */ FAACAPI faac_status faac_encoder_close(faac_encoder **enc); /* * Query the resolved encoder properties. Set out->struct_size to * sizeof(faac_encoder_info) first; the library fills the fields (writing at most * out->struct_size bytes, so a newer library is safe against an older caller's * struct) and updates struct_size to the number of bytes populated. Returns * FAAC_ERR_INVALID_ARGUMENT if enc or out is NULL, or struct_size is too small. * Use these values to size the input/output buffers and to report the effective * configuration back to the user. */ FAACAPI faac_status faac_encoder_get_info(faac_encoder *enc, faac_encoder_info *out); /* * Return the AudioSpecificConfig (a.k.a. decoder-specific info) for this * encoder via *buf / *len. The buffer is library-owned and valid until the * encoder is closed; do NOT free it. Returns FAAC_ERR_UNSUPPORTED for * configurations that have no ASC (e.g. MPEG-2). */ FAACAPI faac_status faac_encoder_asc(faac_encoder *enc, const uint8_t **buf, uint32_t *len); /* * Encode. `in` points to `in_samples` interleaved PCM samples (total across all * channels, i.e. samples-per-channel * num_channels), formatted per * input_format; it may be any count from 0 to frame_samples*num_channels. The * library buffers internally and emits at most one frame per call, writing up * to out_cap bytes to `out` and the byte count to *bytes_written (which may be * 0 while priming, accumulating, or flushing). Pass in_samples == 0 to flush at * end of stream; keep calling until *bytes_written is 0. */ FAACAPI faac_status faac_encoder_encode(faac_encoder *enc, const void *in, uint32_t in_samples, uint8_t *out, uint32_t out_cap, uint32_t *bytes_written); /* Human-readable, static description of a status code. Never returns NULL. */ FAACAPI const char *faac_strerror(faac_status status); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* FAAC_H */ knik0-faac-1950446/include/meson.build000066400000000000000000000000341522734263700174110ustar00rootroot00000000000000install_headers(['faac.h']) knik0-faac-1950446/libfaac/000077500000000000000000000000001522734263700152105ustar00rootroot00000000000000knik0-faac-1950446/libfaac/bitstream.c000066400000000000000000000061601522734263700173510ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Copyright (C) 2026 Nils Schimmelmann * * 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. */ #include "bitstream.h" #include "util.h" #include #include #include BitStream *OpenBitStream(uint32_t size, uint8_t *buffer) { BitStream *bs = (BitStream *)AllocMemory(sizeof(BitStream)); if (!bs) return NULL; InitBitStream(bs, buffer, size); return bs; } int CloseBitStream(BitStream *bs) { if (!bs) return 0; int bytes = (int)((bs->currentBit + 7) >> 3); FreeMemory(bs); return bytes; } void InitBitStream(BitStream *bs, uint8_t *buffer, uint32_t size) { bs->data = buffer; bs->size = size; bs->currentBit = 0; if (buffer) memset(buffer, 0, size); } /* Packs the low `numBits` bits of `data` into `bs`, MSB-first. Returns 0, * or -1 if the write would run past the buffer. Most fields fit in a * single byte, so that case short-circuits the general fill-from-the-end * loop below rather than paying its per-iteration bookkeeping. */ int PutBit(BitStream *bs, uint32_t data, int numBits) { if (numBits <= 0) return 0; uint32_t start = bs->currentBit; uint32_t end = start + (uint32_t)numBits; if (end > bs->size * 8) return -1; bs->currentBit = end; if (numBits < 32) data &= (1U << numBits) - 1; if ((start >> 3) == ((end - 1) >> 3)) { uint32_t bitsAvailable = ((end - 1) & 7) + 1; uint32_t shift = 8 - bitsAvailable; /* PutBit ORs bits into place; it never zeroes them first, so every * caller-supplied buffer must already be zeroed (OpenBitStream does * this). A stale/reused buffer that skips OpenBitStream will corrupt * silently in release builds -- catch it here in debug builds. */ assert((bs->data[start >> 3] & (((1U << (uint32_t)numBits) - 1) << shift)) == 0); bs->data[start >> 3] |= (uint8_t)(data << shift); return 0; } uint32_t bitPos = end; while (numBits > 0) { uint32_t byteIndex = (bitPos - 1) >> 3; uint32_t bitsAvailable = ((bitPos - 1) & 7) + 1; uint32_t take = (uint32_t)numBits < bitsAvailable ? (uint32_t)numBits : bitsAvailable; uint32_t shift = 8 - bitsAvailable; assert((bs->data[byteIndex] & (((1U << take) - 1) << shift)) == 0); bs->data[byteIndex] |= (uint8_t)((data & ((1U << take) - 1)) << shift); data >>= take; numBits -= (int)take; bitPos -= take; } return 0; } int ByteAlign(BitStream *bs) { int bits = (8 - (int)(bs->currentBit & 7)) & 7; if (bits > 0) { PutBit(bs, 0, bits); } return bits; } knik0-faac-1950446/libfaac/bitstream.h000066400000000000000000000103451522734263700173560ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Copyright (C) 2026 Nils Schimmelmann * * 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. */ #ifndef BITSTREAM_H #define BITSTREAM_H #ifdef __cplusplus extern "C" { #endif #include /** * ADTS Constants (ISO/IEC 14496-3) */ enum { ADTS_MAX_FRAME_SIZE = (1 << 13) - 1, ADTS_FRAMESIZE = 1 << 13, /* Legacy name compatibility */ ADTS_HEADER_SIZE = 7 /* 56 bits */ }; /** * @struct BitStream * @brief Manages a bit-oriented buffer for AAC bitstream generation. */ typedef struct BitStream { uint8_t *data; /**< Pointer to the start of the bitstream buffer */ uint32_t size; /**< Size of the buffer in bytes */ uint32_t currentBit; /**< Current write position in bits */ } BitStream; BitStream *OpenBitStream(uint32_t size, uint8_t *buffer); int CloseBitStream(BitStream *bs); /* Non-allocating alternative to OpenBitStream: initializes *bs to write into * a caller-owned buffer (zeroing it, same as OpenBitStream) without the * malloc/free of a heap BitStream. For scratch streams built over a * reused/persistent buffer (e.g. a cache rebuilt once per frame) -- PutBit * ORs bits into place and never zeroes them itself, so any such buffer must * be zeroed before (re)use or stale bits from a prior, longer write leak * through. */ void InitBitStream(BitStream *bs, uint8_t *buffer, uint32_t size); int PutBit(BitStream *bs, uint32_t data, int numBits); int ByteAlign(BitStream *bs); /* Batches small field writes into a register and flushes whole bytes as * they fill, instead of touching the buffer on every write -- for hot * loops (e.g. spectral coefficients) where PutBit's per-call overhead * dominates. Bracket a self-contained span with no interleaved PutBit * calls on the same stream: bs->currentBit is stale until AccumEnd. */ typedef struct BitAccumulator { uint64_t bits; /**< pending bits, left-justified in the register */ uint8_t *out; /**< next byte to emit into bs->data */ uint8_t *limit; /**< bs->data + bs->size (one-past-end guard) */ BitStream *bs; /**< owning stream; currentBit is stale until AccumEnd */ int fill; /**< pending bit count (0..7 between calls) */ int overflow; /**< sticky: set if a store hit the buffer limit */ } BitAccumulator; static inline void AccumBegin(BitAccumulator *a, BitStream *bs) { uint32_t bytePos = bs->currentBit >> 3; a->bs = bs; a->out = bs->data + bytePos; a->limit = bs->data + bs->size; a->fill = (int)(bs->currentBit & 7); a->overflow = 0; /* Preload the in-progress byte so its already-written high bits are * preserved; its low (8-fill) bits are 0 (the buffer starts zeroed and * we only ever move forward), so this seed is exact. */ a->bits = a->fill ? ((uint64_t)(*a->out) << 56) : 0; } static inline void AccumPutBits(BitAccumulator *a, uint32_t value, int numBits) { if (numBits <= 0) return; if (numBits < 32) value &= (1U << numBits) - 1; a->bits |= (uint64_t)value << (64 - a->fill - numBits); a->fill += numBits; while (a->fill >= 8) { if (a->out < a->limit) *a->out = (uint8_t)(a->bits >> 56); else a->overflow = 1; a->out++; a->bits <<= 8; a->fill -= 8; } } static inline int AccumEnd(BitAccumulator *a) { if (a->fill > 0) { if (a->out < a->limit) *a->out = (uint8_t)(a->bits >> 56); else a->overflow = 1; /* out is not advanced: these bits are unfinished, a later PutBit * on the same stream will OR the rest of the byte in. */ } a->bs->currentBit = (uint32_t)((a->out - a->bs->data) * 8 + a->fill); return a->overflow ? -1 : 0; } #ifdef __cplusplus } #endif #endif /* BITSTREAM_H */ knik0-faac-1950446/libfaac/blockswitch.c000066400000000000000000000173461522734263700177030ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Copyright (C) 2002 Krzysztof Nikiel * * 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. */ #include #include #include #include #include "blockswitch.h" #include "coder.h" #include "util.h" #include "faac_internal.h" #include "frame.h" typedef float psyfloat; /* The high-pass energy timeline is held as one contiguous array of per-sub-block energies rather than separate prev/curr/next arrays, so PsyCheckShort's +-2 sub-block lookahead is a single sliding index instead of three-way stitching. It holds three 2-frame energy windows back to back: PREV, CUR and the one lookahead window NEXT. (Energy windows are 2 frames wide, which is why a single "next" window consumes the two-frames-ahead sample slot in the input FIFO.) */ #define SUBBLOCKS_PER_FRAME 8 #define ENG_WIN_PREV (0 * SUBBLOCKS_PER_FRAME) #define ENG_WIN_CUR (1 * SUBBLOCKS_PER_FRAME) #define ENG_WIN_NEXT (2 * SUBBLOCKS_PER_FRAME) typedef struct { psyfloat eng[3 * SUBBLOCKS_PER_FRAME]; } psydata_t; /* The high-pass first difference (d[n]=x[n]-x[n-1]) de-weights bass, whose * broadband energy would otherwise mask HF attacks and false-trigger short * blocks on stationary music; what's left tracks the band where pre-echo is * audible. A relative energy jump between sub-blocks past this threshold is a * transient. */ #define PSY_TD_THRESH (0.5f) static void PsyCheckShort(PsyInfo * psyInfo) { enum {PREVS = 2, NEXTS = 2}; psydata_t *psydata = (psydata_t *)psyInfo->data; int win; float lasteng = (float)psydata->eng[ENG_WIN_CUR - PREVS]; /* start at PREVS before current */ psyInfo->block_type = ONLY_LONG_WINDOW; /* Search for transients across the current frame and its immediate temporal context. The search range is [curr-2, curr+9]. */ for (win = 1; win < PREVS + SUBBLOCKS_PER_FRAME + NEXTS; win++) { float eng = (float)psydata->eng[ENG_WIN_CUR - PREVS + win]; float toteng = (eng < lasteng) ? eng : lasteng; float volchg = fabsf(eng - lasteng); /* Relative energy jump indicates a transient. IEEE divide handles silence cases. */ if (volchg / toteng > PSY_TD_THRESH) { psyInfo->block_type = ONLY_SHORT_WINDOW; break; } lasteng = eng; } } void PsyInit(GlobalPsyInfo * gpsyInfo, PsyInfo * psyInfo, unsigned int numChannels, unsigned int sampleRate) { unsigned int channel; int size; gpsyInfo->sampleRate = (float) sampleRate; for (channel = 0; channel < numChannels; channel++) { psydata_t *psydata = (psydata_t *)AllocMemory(sizeof(psydata_t)); if (!psydata) return; memset(psydata, 0, sizeof(psydata_t)); psyInfo[channel].data = psydata; } size = BLOCK_LEN_LONG; for (channel = 0; channel < numChannels; channel++) { psyInfo[channel].size = size; } size = BLOCK_LEN_SHORT; for (channel = 0; channel < numChannels; channel++) psyInfo[channel].sizeS = size; } void PsyEnd(PsyInfo * psyInfo, unsigned int numChannels) { unsigned int channel; for (channel = 0; channel < numChannels; channel++) { if (psyInfo[channel].data) FreeMemory(psyInfo[channel].data); } } /* Do psychoacoustical analysis */ void PsyCalculate(AACElement * elements, int numElements, PsyInfo * psyInfo, unsigned int numChannels ) { if (elements == NULL) { for (unsigned int channel = 0; channel < numChannels; channel++) PsyCheckShort(&psyInfo[channel]); return; } for (int e = 0; e < numElements; e++) { AACElement *elem = &elements[e]; switch (elem->type) { case ID_SCE: PsyCheckShort(&psyInfo[elem->channels[0]]); break; case ID_CPE: PsyCheckShort(&psyInfo[elem->channels[0]]); PsyCheckShort(&psyInfo[elem->channels[1]]); break; case ID_LFE: psyInfo[elem->channels[0]].block_type = ONLY_LONG_WINDOW; break; default: break; } } } void PsyBufferUpdate(GlobalPsyInfo * gpsyInfo, PsyInfo * psyInfo, float * restrict p_lookahead1, float * restrict p_lookahead2) { int win; float * restrict transBuff = gpsyInfo->sharedWorkBuffLong; psydata_t *psydata = (psydata_t *)psyInfo->data; /* Shift the energy windows down by one frame: PREV<-CUR, CUR<-NEXT, freeing the NEXT region for the freshly-computed lookahead window below. */ memmove(psydata->eng, psydata->eng + SUBBLOCKS_PER_FRAME, 2 * SUBBLOCKS_PER_FRAME * sizeof(psyfloat)); /* Assembly of the newest 2048-sample window for energy analysis */ memcpy(transBuff, p_lookahead1, BLOCK_LEN_LONG * sizeof(float)); memcpy(transBuff + BLOCK_LEN_LONG, p_lookahead2, BLOCK_LEN_LONG * sizeof(float)); for (win = 0; win < SUBBLOCKS_PER_FRAME; win++) { /* seg[-1] is in bounds (seg starts >= 448 samples in), so the first * difference carries across the sub-block boundary instead of resetting. */ float *seg = transBuff + (win * BLOCK_LEN_SHORT) + (BLOCK_LEN_LONG - BLOCK_LEN_SHORT) / 2; float e = 0.0f; int l, n = 2 * psyInfo->sizeS; for (l = 0; l < n; l++) { float d = seg[l] - seg[l - 1]; e += d * d; } psydata->eng[ENG_WIN_NEXT + win] = (psyfloat)e; } } void BlockSwitch(struct faacEncStruct *hEncoder, CoderInfo * coderInfo, PsyInfo * psyInfo, unsigned int numChannels) { unsigned int channel; int desire = ONLY_LONG_WINDOW; /* Shared transient override for HE-AAC path. * Core delay alignment: SbrAnalyze runs on frame N full-rate; core * block-switch for frame N audio is emitted at a delay. Alignment logic * uses the FIFO. */ if (hEncoder->config.aacObjectType == HE_V1 && SbrContextIsAnalysisValid(hEncoder->sbrContext)) { for (channel = 0; channel < numChannels; channel++) { /* Alignment: the core frame being coded now lags the freshest SBR * analysis by LOOKAHEAD_DEPTH frames; FIFO index 0 holds that frame's * decision (FIFO sized SBR_DETECT_FIFO so [0] is LOOKAHEAD_DEPTH back). */ int wantShort = SbrContextGetWantShort(hEncoder->sbrContext, (int)channel, 0); if (wantShort) psyInfo[channel].block_type = ONLY_SHORT_WINDOW; else psyInfo[channel].block_type = ONLY_LONG_WINDOW; } } /* Use the same block type for all channels If there is 1 channel that wants a short block, use a short block on all channels. */ for (channel = 0; channel < numChannels; channel++) { if (psyInfo[channel].block_type == ONLY_SHORT_WINDOW) desire = ONLY_SHORT_WINDOW; } for (channel = 0; channel < numChannels; channel++) { int lasttype = coderInfo[channel].block_type; if (desire == ONLY_SHORT_WINDOW || coderInfo[channel].desired_block_type == ONLY_SHORT_WINDOW) { if (lasttype == ONLY_LONG_WINDOW || lasttype == SHORT_LONG_WINDOW) coderInfo[channel].block_type = LONG_SHORT_WINDOW; else coderInfo[channel].block_type = ONLY_SHORT_WINDOW; } else { if (lasttype == ONLY_SHORT_WINDOW || lasttype == LONG_SHORT_WINDOW) coderInfo[channel].block_type = SHORT_LONG_WINDOW; else coderInfo[channel].block_type = ONLY_LONG_WINDOW; } coderInfo[channel].desired_block_type = desire; } } knik0-faac-1950446/libfaac/blockswitch.h000066400000000000000000000031461522734263700177010ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Copyright (C) 2001 Menno Bakker * * 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. */ #ifndef BLOCKSWITCH_H #define BLOCKSWITCH_H #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ #include "coder.h" #include "channels.h" struct faacEncStruct; typedef struct { int size; int sizeS; int block_type; void *data; } PsyInfo; typedef struct { float sampleRate; /* shared work buffers */ float *sharedWorkBuffLong; /* Used for 2048-sample windows (filtbank, psy, tns, mdct) */ } GlobalPsyInfo; void PsyInit (GlobalPsyInfo *gpsyInfo, PsyInfo *psyInfo, unsigned int numChannels, unsigned int sampleRate); void PsyEnd (PsyInfo *psyInfo, unsigned int numChannels); void PsyCalculate (AACElement *elements, int numElements, PsyInfo *psyInfo, unsigned int numChannels); void PsyBufferUpdate (GlobalPsyInfo * gpsyInfo, PsyInfo * psyInfo, float * restrict p_lookahead1, float * restrict p_lookahead2); void BlockSwitch (struct faacEncStruct *hEncoder, CoderInfo *coderInfo, PsyInfo *psyInfo, unsigned int numChannels); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* BLOCKSWITCH_H */ knik0-faac-1950446/libfaac/channels.c000066400000000000000000000252361522734263700171570ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Copyright (C) 2026 Nils Schimmelmann * * 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. */ #include "channels.h" #include "huff2.h" #include "frame.h" #include "sbr.h" #include #include _Static_assert(TNS_MAX_FILTERS == (1 << LEN_TNS_NFILTL), "coder.h's TnsWindowData.tnsFilter[] bound must match the LEN_TNS_NFILTL bitstream field width"); /** * Maps input channels to AAC elements (SCE, CPE, LFE), per ISO/IEC 14496-3's * channel configuration table: one SCE up front unless exactly 2 channels * remain, then as many CPEs as fit two at a time, then a trailing odd * channel becomes an LFE (if enabled) or a final SCE. */ int InitElements(AACElement * __restrict elements, int *numElements, int numChannels, bool useLfe) { uint8_t sceTag = 0; uint8_t cpeTag = 0; uint8_t lfeTag = 0; int currentElem = 0; int currentCh = 0; int channelsRemaining = numChannels; memset(elements, 0, sizeof(AACElement) * MAX_CHANNELS); // Initial SCE for Config 1, 3, 4, 5, 6, 7 if (channelsRemaining != 2 && channelsRemaining > 0) { elements[currentElem].type = ID_SCE; elements[currentElem].tag = sceTag++; elements[currentElem].channels[0] = currentCh++; elements[currentElem].channels[1] = -1; currentElem++; channelsRemaining--; } // CPE groups while (channelsRemaining > 1) { elements[currentElem].type = ID_CPE; elements[currentElem].tag = cpeTag++; elements[currentElem].channels[0] = currentCh++; elements[currentElem].channels[1] = currentCh++; currentElem++; channelsRemaining -= 2; } // Residual SCE or LFE if (channelsRemaining == 1) { if (useLfe) { elements[currentElem].type = ID_LFE; elements[currentElem].tag = lfeTag++; } else { elements[currentElem].type = ID_SCE; elements[currentElem].tag = sceTag++; } elements[currentElem].channels[0] = currentCh++; elements[currentElem].channels[1] = -1; currentElem++; } *numElements = currentElem; return 0; } static int WriteICSInfo(BitStream *bs, CoderInfo *coder, bool writeFlag) { if (writeFlag) { PutBit(bs, 0, LEN_ICS_RESERV); PutBit(bs, coder->block_type, LEN_WIN_SEQ); PutBit(bs, coder->window_shape, LEN_WIN_SH); } int bits = LEN_ICS_RESERV + LEN_WIN_SEQ + LEN_WIN_SH; if (coder->block_type == ONLY_SHORT_WINDOW) { if (writeFlag) { PutBit(bs, coder->sfbn, LEN_MAX_SFBS); int grouping_bits = 0; int tmp[MAX_SHORT_WINDOWS], index = 0; for (int i = 0; i < coder->groups.n; i++) for (int j = 0; j < coder->groups.len[i]; j++) tmp[index++] = i; for (int i = 1; i < MAX_SHORT_WINDOWS; i++) { grouping_bits <<= 1; if (tmp[i] == tmp[i-1]) grouping_bits++; } PutBit(bs, grouping_bits, MAX_SHORT_WINDOWS - 1); } bits += LEN_MAX_SFBS + (MAX_SHORT_WINDOWS - 1); } else { if (writeFlag) { PutBit(bs, coder->sfbn, LEN_MAX_SFBL); PutBit(bs, 0, LEN_PRED_PRES); } bits += LEN_MAX_SFBL + LEN_PRED_PRES; } return bits; } static int WriteICS(BitStream *bs, CoderInfo *coder, bool commonWindow, bool writeFlag) { if (writeFlag) PutBit(bs, coder->global_gain, LEN_GLOB_GAIN); int bits = LEN_GLOB_GAIN; if (!commonWindow) bits += WriteICSInfo(bs, coder, writeFlag); bits += writebooks(coder, bs, writeFlag); bits += writesf(coder, bs, writeFlag); if (writeFlag) PutBit(bs, 0, LEN_PULSE_PRES); bits += LEN_PULSE_PRES; TnsInfo *tns = &coder->tnsInfo; if (writeFlag) PutBit(bs, tns->tnsDataPresent, LEN_TNS_PRES); bits += LEN_TNS_PRES; /* TNS is long-only (see tns.c): tnsDataPresent is never set for * ONLY_SHORT_WINDOW, so there's exactly one window's worth of TNS data * to write, always at the long-window field widths. */ if (tns->tnsDataPresent) { TnsWindowData *win = &tns->windowData; if (writeFlag) PutBit(bs, win->numFilters, LEN_TNS_NFILTL); bits += LEN_TNS_NFILTL; if (win->numFilters > 0) { if (writeFlag) PutBit(bs, win->coefResolution - DEF_TNS_RES_OFFSET, LEN_TNS_COEFF_RES); bits += LEN_TNS_COEFF_RES; for (int f = 0; f < win->numFilters; f++) { TnsFilterData *flt = &win->tnsFilter[f]; if (writeFlag) { PutBit(bs, flt->length, LEN_TNS_LENGTHL); PutBit(bs, flt->order, LEN_TNS_ORDERL); } bits += LEN_TNS_LENGTHL + LEN_TNS_ORDERL; if (flt->order > 0) { if (writeFlag) { PutBit(bs, flt->direction, LEN_TNS_DIRECTION); PutBit(bs, flt->coefCompress, LEN_TNS_COMPRESS); } bits += LEN_TNS_DIRECTION + LEN_TNS_COMPRESS; int res = win->coefResolution - flt->coefCompress; for (int i = 1; i <= flt->order; i++) { if (writeFlag) PutBit(bs, flt->index[i] & ((1 << res) - 1), res); bits += res; } } } } } if (writeFlag) PutBit(bs, 0, LEN_GAIN_PRES); bits += LEN_GAIN_PRES; if (writeFlag) { BitAccumulator acc = {0}; AccumBegin(&acc, bs); for (int i = 0; i < coder->datacnt; i++) { if (coder->s[i].len > 0) { AccumPutBits(&acc, (uint32_t)coder->s[i].data, coder->s[i].len); bits += coder->s[i].len; } } AccumEnd(&acc); } else { for (int i = 0; i < coder->datacnt; i++) bits += coder->s[i].len; } return bits; } int WriteElement(BitStream *bs, AACElement *elem, CoderInfo *coder, bool writeFlag) { if (writeFlag) { PutBit(bs, elem->type, LEN_SE_ID); PutBit(bs, elem->tag, LEN_TAG); } int bits = LEN_SE_ID + LEN_TAG; switch (elem->type) { case ID_SCE: case ID_LFE: bits += WriteICS(bs, &coder[elem->channels[0]], false, writeFlag); break; case ID_CPE: if (writeFlag) PutBit(bs, elem->common_window, LEN_COM_WIN); bits += LEN_COM_WIN; if (elem->common_window) { bits += WriteICSInfo(bs, &coder[elem->channels[0]], writeFlag); if (writeFlag) { PutBit(bs, elem->msInfo.is_present, LEN_MASK_PRES); if (elem->msInfo.is_present == 1) { int n = coder[elem->channels[0]].groups.n * coder[elem->channels[0]].sfbn; for (int i = 0; i < n; i++) PutBit(bs, elem->msInfo.ms_used[i], LEN_MASK); } } bits += LEN_MASK_PRES; if (elem->msInfo.is_present == 1) bits += coder[elem->channels[0]].groups.n * coder[elem->channels[0]].sfbn * LEN_MASK; } bits += WriteICS(bs, &coder[elem->channels[0]], elem->common_window, writeFlag); bits += WriteICS(bs, &coder[elem->channels[1]], elem->common_window, writeFlag); break; default: break; } return bits; } static int WriteADTSHeader(struct faacEncStruct *hEncoder, BitStream *bs, bool writeFlag) { if (writeFlag) { PutBit(bs, 0xFFF, LEN_ADTS_SYNC); PutBit(bs, hEncoder->config.mpegVersion, LEN_ADTS_ID); PutBit(bs, 0, LEN_ADTS_LAYER); PutBit(bs, 1, LEN_ADTS_ABSENT); /* profile: always LC. HE-AAC's core is LC too; SBR is implicit via fill element. */ PutBit(bs, LOW - 1, LEN_ADTS_PROFILE); PutBit(bs, hEncoder->sampleRateIdx, LEN_ADTS_FREQ); PutBit(bs, 0, LEN_ADTS_PRIV); PutBit(bs, hEncoder->numChannels, LEN_ADTS_CH_CFG); PutBit(bs, 0, LEN_ADTS_ORIG); PutBit(bs, 0, LEN_ADTS_HOME); PutBit(bs, 0, LEN_ADTS_COPY_ID); PutBit(bs, 0, LEN_ADTS_COPY_ST); PutBit(bs, hEncoder->usedBytes, LEN_ADTS_FRAME); PutBit(bs, 0x7FF, LEN_ADTS_FULL); PutBit(bs, 0, LEN_ADTS_BLOCKS); } return 56; } static int WriteAACFillBits(BitStream *bs, int numBits, bool writeFlag) { int left = numBits; while (left >= (LEN_SE_ID + 4)) { if (writeFlag) PutBit(bs, ID_FIL, LEN_SE_ID); left -= LEN_SE_ID; int bc = (left / 8 < 15) ? (left / 8) : 15; if (writeFlag) PutBit(bs, bc, 4); left -= 4; if (bc == 15) { int esc = (left / 8 - 14 < 255) ? (left / 8 - 14) : 255; if (writeFlag) PutBit(bs, esc, 8); left -= 8; bc = 14 + esc; } if (writeFlag) for (int i = 0; i < bc; i++) PutBit(bs, 0, 8); left -= bc * 8; } return left; } static int BuildFrame(struct faacEncStruct *hEncoder, CoderInfo *coder, AACElement *elems, int nElems, BitStream *bs, bool write) { int bits = 0; if (hEncoder->config.outputFormat == 1) bits += WriteADTSHeader(hEncoder, bs, write); for (int i = 0; i < nElems; i++) bits += WriteElement(bs, &elems[i], coder, write); int f = (bits < (8 - LEN_SE_ID)) ? (8 - LEN_SE_ID - bits) : 0; f += 6; bits += (f - WriteAACFillBits(bs, f, write)); /* HE-AAC: SBR payload rides in a fill element (EXT_SBR_DATA) */ bits += SbrContextGetBits(hEncoder->sbrContext, write ? bs : NULL, (int)hEncoder->numChannels, (int)hEncoder->config.aacObjectType, write); if (write) PutBit(bs, ID_END, LEN_SE_ID); bits += LEN_SE_ID; int pad = (8 - (bits & 7)) & 7; if (write) for (int i = 0; i < pad; i++) PutBit(bs, 0, 1); return bits + pad; } int WriteBitstream(struct faacEncStruct *hEncoder, CoderInfo *coder, AACElement *elems, int nElems, BitStream *bs) { int bits = BuildFrame(hEncoder, coder, elems, nElems, bs, false); if (bits < 0) return -1; hEncoder->usedBytes = (bits + 7) >> 3; if (hEncoder->usedBytes > bs->size) return -1; if (hEncoder->usedBytes > ADTS_MAX_FRAME_SIZE) return -1; return BuildFrame(hEncoder, coder, elems, nElems, bs, true); } knik0-faac-1950446/libfaac/channels.h000066400000000000000000000062251522734263700171610ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Copyright (C) 2026 Nils Schimmelmann * * 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. */ #ifndef CHANNEL_H #define CHANNEL_H #include #include #include "bitstream.h" #include "coder.h" #ifdef __cplusplus extern "C" { #endif /* Forward declaration for core struct to avoid circular dependency */ struct faacEncStruct; /** * AAC Syntax Element identifiers (ISO/IEC 14496-3, Table 4.1). */ typedef enum { ID_SCE = 0, /**< Single Channel Element */ ID_CPE = 1, /**< Channel Pair Element */ ID_CCE = 2, /**< Coupling Channel Element */ ID_LFE = 3, /**< Low Frequency Element */ ID_DSE = 4, /**< Data Stream Element */ ID_PCE = 5, /**< Program Config Element */ ID_FIL = 6, /**< Fill Element */ ID_END = 7 /**< Terminator */ } ElementID; /** * Bitstream field lengths (ISO/IEC 14496-3). */ enum { LEN_SE_ID = 3, LEN_TAG = 4, LEN_COM_WIN = 1, LEN_ICS_RESERV = 1, LEN_WIN_SEQ = 2, LEN_WIN_SH = 1, LEN_MAX_SFBL = 6, LEN_MAX_SFBS = 4, LEN_GLOB_GAIN = 8, LEN_PRED_PRES = 1, LEN_MASK_PRES = 2, LEN_MASK = 1, LEN_PULSE_PRES = 1, LEN_TNS_PRES = 1, LEN_GAIN_PRES = 1, LEN_TNS_NFILTL = 2, LEN_TNS_NFILTS = 1, LEN_TNS_COEFF_RES = 1, LEN_TNS_LENGTHL = 6, LEN_TNS_LENGTHS = 4, LEN_TNS_ORDERL = 5, LEN_TNS_ORDERS = 3, LEN_TNS_DIRECTION = 1, LEN_TNS_COMPRESS = 1, LEN_ADTS_SYNC = 12, LEN_ADTS_ID = 1, LEN_ADTS_LAYER = 2, LEN_ADTS_ABSENT = 1, LEN_ADTS_PROFILE = 2, LEN_ADTS_FREQ = 4, LEN_ADTS_PRIV = 1, LEN_ADTS_CH_CFG = 3, LEN_ADTS_ORIG = 1, LEN_ADTS_HOME = 1, LEN_ADTS_COPY_ID = 1, LEN_ADTS_COPY_ST = 1, LEN_ADTS_FRAME = 13, LEN_ADTS_FULL = 11, LEN_ADTS_BLOCKS = 2 }; typedef struct { bool is_present; uint8_t ms_used[MAX_SCFAC_BANDS]; } MSInfo; typedef struct { ElementID type; uint8_t tag; int channels[2]; bool common_window; MSInfo msInfo; } AACElement; int InitElements(AACElement * __restrict elements, int *numElements, int numChannels, bool useLfe); int WriteElement(BitStream *bs, AACElement *elem, CoderInfo *coder, bool writeFlag); int WriteBitstream(struct faacEncStruct* hEncoder, CoderInfo *coderInfo, AACElement *elements, int numElements, BitStream *bitStream); #ifdef __cplusplus } #endif #endif /* CHANNEL_H */ knik0-faac-1950446/libfaac/coder.h000066400000000000000000000064511522734263700164630ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Copyright (C) 2001 Menno Bakker * * 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. */ #ifndef CODER_H #define CODER_H #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ #define FRAME_LEN 1024 #define BLOCK_LEN_LONG 1024 #define BLOCK_LEN_SHORT 128 #define NSFB_LONG 51 #define NSFB_SHORT 15 #define MAX_SHORT_WINDOWS 8 #define MAX_SCFAC_BANDS ((NSFB_SHORT+1)*MAX_SHORT_WINDOWS) enum WINDOW_TYPE { ONLY_LONG_WINDOW, LONG_SHORT_WINDOW, ONLY_SHORT_WINDOW, SHORT_LONG_WINDOW }; #define TNS_MAX_ORDER 12 #define DEF_TNS_COEFF_THRESH 0.1f #define DEF_TNS_COEFF_RES 4 #define DEF_TNS_RES_OFFSET 3 /* Bound on TnsWindowData.tnsFilter[]. Must stay in sync with the bitstream * field width LEN_TNS_NFILTL (channels.h) -- checked by a _Static_assert in * channels.c, since this header can't include channels.h without a cycle. */ #define TNS_MAX_FILTERS 4 typedef struct { int order; /* Filter order */ int direction; /* Filtering direction */ int coefCompress; /* Are coeffs compressed? */ int length; /* Length, in bands */ float aCoeffs[TNS_MAX_ORDER+1]; /* LPC (AR) coefficients */ int index[TNS_MAX_ORDER+1]; /* Quantized reflection-coeff indices */ } TnsFilterData; typedef struct { int numFilters; /* Number of filters */ int coefResolution; /* Coefficient resolution */ TnsFilterData tnsFilter[TNS_MAX_FILTERS]; /* TNS filters */ } TnsWindowData; typedef struct { int tnsDataPresent; int tnsMinBandNumberLong; int tnsMaxBandsLong; int tnsNumSwbLong; /* full swb count for the sample rate (decoder's num_swb) */ TnsWindowData windowData; /* long-only: one window per frame, not per-short-window */ } TnsInfo; typedef struct CoderInfo { int window_shape; int prev_window_shape; int block_type; int desired_block_type; int global_gain; int sf[MAX_SCFAC_BANDS]; int book[MAX_SCFAC_BANDS]; int bandcnt; int sfbn; int sfb_offset[NSFB_LONG + 1]; struct { int n; int len[MAX_SHORT_WINDOWS]; } groups; /* worst case: one codeword with two escapes per two spectral lines */ #define DATASIZE (3*FRAME_LEN/2) struct { int data; int len; } s[DATASIZE]; int datacnt; TnsInfo tnsInfo; } CoderInfo; typedef struct { unsigned long sampling_rate; /* the following entries are for this sampling rate */ int num_cb_long; int num_cb_short; int cb_width_long[NSFB_LONG]; int cb_width_short[NSFB_SHORT]; } SR_INFO; /* Scalefactor-band layout per sampling_rate_index, shared by frame.c and sbr.c. */ extern SR_INFO srInfo[12 + 1]; #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* CODER_H */ knik0-faac-1950446/libfaac/cpu_compute.c000066400000000000000000000031641522734263700177030ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Copyright (C) 2026 Nils Schimmelmann * * 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "cpu_compute.h" #if defined(SSE2_ARCH) # ifdef _MSC_VER # include # elif defined(__GNUC__) || defined(__clang__) # include # endif #endif CPUCaps get_cpu_caps(void) { CPUCaps caps = CPU_CAP_NONE; #if defined(SSE2_ARCH) unsigned int eax = 0, ebx = 0, ecx = 0, edx = 0; unsigned int max_leaf = 0; # ifdef _MSC_VER int cpu_info[4] = {0}; __cpuid(cpu_info, 0); max_leaf = (unsigned int)cpu_info[0]; # elif defined(__GNUC__) || defined(__clang__) __cpuid(0, max_leaf, ebx, ecx, edx); # endif if (max_leaf >= 1) { # ifdef _MSC_VER __cpuid(cpu_info, 1); eax = (unsigned int)cpu_info[0]; ebx = (unsigned int)cpu_info[1]; ecx = (unsigned int)cpu_info[2]; edx = (unsigned int)cpu_info[3]; # elif defined(__GNUC__) || defined(__clang__) __get_cpuid(1, &eax, &ebx, &ecx, &edx); # endif if (edx & (1 << 26)) // SSE2 caps |= CPU_CAP_SSE2; } #endif return caps; } knik0-faac-1950446/libfaac/cpu_compute.h000066400000000000000000000015551522734263700177120ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Copyright (C) 2026 Nils Schimmelmann * * 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. */ #ifndef CPU_COMPUTE_H #define CPU_COMPUTE_H #if defined(_M_X64) || defined(__x86_64__) || defined(_M_IX86) || defined(__i386__) # define SSE2_ARCH #endif typedef enum { CPU_CAP_NONE = 0, CPU_CAP_SSE2 = (1 << 0) } CPUCaps; CPUCaps get_cpu_caps(void); #endif knik0-faac-1950446/libfaac/faac.c000066400000000000000000000324461522734263700162570ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Copyright (C) 2026 Nils Schimmelmann * * 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. */ /* * The public faac_* encoder API (see include/faac.h). * * This is a thin facade over the private encoder core in frame.c (declared in * faac_internal.h): it supplies the params-at-open entry point, uniform * faac_status error reporting, resolved-property queries, and library-owned * ASC. The opaque faac_encoder* is the core's faacEncStruct*. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "frame.h" #include "bitstream.h" #include "sbr.h" /* The public enums are width-pinned to 32 bits by their FAAC_*_MAX sentinels; * verify the compiler honored that so the ABI matches the documented layout. */ _Static_assert(sizeof(enum faac_status) == 4, "faac_status must be 32-bit"); _Static_assert(sizeof(enum faac_object_type) == 4, "faac_object_type must be 32-bit"); _Static_assert(sizeof(enum faac_mpeg_version) == 4, "faac_mpeg_version must be 32-bit"); _Static_assert(sizeof(enum faac_joint_mode) == 4, "faac_joint_mode must be 32-bit"); _Static_assert(sizeof(enum faac_shortctl_mode) == 4, "faac_shortctl_mode must be 32-bit"); _Static_assert(sizeof(enum faac_stream_format) == 4, "faac_stream_format must be 32-bit"); _Static_assert(sizeof(enum faac_input_format) == 4, "faac_input_format must be 32-bit"); /* The modern enums mirror the legacy numeric values one-for-one, so parameter * translation is a plain field copy. Guard that assumption. */ _Static_assert((int)FAAC_MPEG4 == MPEG4 && (int)FAAC_MPEG2 == MPEG2, "mpeg version drift"); _Static_assert((int)FAAC_OBJ_AUTO == AUTO && (int)FAAC_OBJ_LOW == LOW && (int)FAAC_OBJ_HE_AAC_V1 == HE_V1, "object type drift"); _Static_assert((int)FAAC_JOINT_NONE == JOINT_NONE && (int)FAAC_JOINT_MIXED == JOINT_MIXED, "joint mode drift"); _Static_assert((int)FAAC_SHORTCTL_NORMAL == SHORTCTL_NORMAL && (int)FAAC_SHORTCTL_NOLONG == SHORTCTL_NOLONG, "shortctl drift"); _Static_assert((int)FAAC_STREAM_RAW == RAW_STREAM && (int)FAAC_STREAM_ADTS == ADTS_STREAM, "stream format drift"); _Static_assert((int)FAAC_INPUT_NULL == INPUT_NULL && (int)FAAC_INPUT_16BIT == INPUT_16BIT && (int)FAAC_INPUT_24BIT == INPUT_24BIT && (int)FAAC_INPUT_32BIT == INPUT_32BIT && (int)FAAC_INPUT_FLOAT == INPUT_FLOAT, "input format drift"); /* faac_encoder* and faacEncHandle are the same underlying object. */ static inline faacEncStruct *unwrap(faac_encoder *enc) { return (faacEncStruct *)enc; } FAACAPI faac_status faac_get_library_info(faac_library_info *out) { faac_library_info info; char *vid = NULL, *vcopy = NULL; uint32_t caller_size, n; if (!out) return FAAC_ERR_INVALID_ARGUMENT; caller_size = out->struct_size; if (caller_size < sizeof(info.struct_size)) return FAAC_ERR_INVALID_ARGUMENT; faacEncGetVersion(&vid, &vcopy); memset(&info, 0, sizeof(info)); info.max_channels = (uint32_t)MAX_CHANNELS; info.version = vid; info.copyright = vcopy; info.sbr_decimation = (uint32_t)FAAC_SBR_DECIMATION; /* Write at most the caller's struct_size so a newer library cannot overrun * an older, smaller faac_library_info; report the byte count actually set. */ n = caller_size < (uint32_t)sizeof(info) ? caller_size : (uint32_t)sizeof(info); info.struct_size = n; memcpy(out, &info, n); return FAAC_OK; } FAACAPI faac_status faac_params_init(faac_params *p) { if (!p) return FAAC_ERR_INVALID_ARGUMENT; memset(p, 0, sizeof(*p)); p->struct_size = (uint32_t)sizeof(faac_params); p->mpeg_version = FAAC_MPEG4; p->object_type = FAAC_OBJ_LOW; p->joint_mode = FAAC_JOINT_MIXED; p->use_lfe = false; p->use_tns = false; p->bit_rate = 64000; /* per channel; 0 would defer to quant_quality */ p->bandwidth = 0; /* derive from bit_rate */ p->quant_quality = 0; /* derive from bit_rate */ p->output_format = FAAC_STREAM_ADTS; p->input_format = FAAC_INPUT_16BIT; p->short_control = FAAC_SHORTCTL_NORMAL; p->pns_level = 4; return FAAC_OK; } /* Validate the enumerated/range fields the caller supplied. Returns FAAC_OK, * FAAC_ERR_INVALID_ARGUMENT for out-of-range values, or FAAC_ERR_UNSUPPORTED * for values that are valid but not implemented in this build. */ static faac_status validate_params(const faac_params *p) { switch (p->mpeg_version) { case FAAC_MPEG4: case FAAC_MPEG2: break; default: return FAAC_ERR_INVALID_ARGUMENT; } switch (p->object_type) { case FAAC_OBJ_AUTO: case FAAC_OBJ_LOW: case FAAC_OBJ_HE_AAC_V1: break; case FAAC_OBJ_HE_AAC_V2: return FAAC_ERR_UNSUPPORTED; /* parametric stereo not implemented */ default: return FAAC_ERR_INVALID_ARGUMENT; } switch (p->joint_mode) { case FAAC_JOINT_NONE: case FAAC_JOINT_MS: case FAAC_JOINT_IS: case FAAC_JOINT_MIXED: break; default: return FAAC_ERR_INVALID_ARGUMENT; } switch (p->short_control) { case FAAC_SHORTCTL_NORMAL: case FAAC_SHORTCTL_NOSHORT: case FAAC_SHORTCTL_NOLONG: break; default: return FAAC_ERR_INVALID_ARGUMENT; } switch (p->output_format) { case FAAC_STREAM_RAW: case FAAC_STREAM_ADTS: break; default: return FAAC_ERR_INVALID_ARGUMENT; } switch (p->input_format) { case FAAC_INPUT_16BIT: case FAAC_INPUT_32BIT: case FAAC_INPUT_FLOAT: break; case FAAC_INPUT_24BIT: return FAAC_ERR_UNSUPPORTED; /* 24-in-24 not implemented */ default: return FAAC_ERR_INVALID_ARGUMENT; } if (p->sample_rate == 0) return FAAC_ERR_INVALID_ARGUMENT; if (p->num_channels < 1 || p->num_channels > (uint32_t)MAX_CHANNELS) return FAAC_ERR_INVALID_ARGUMENT; if (p->pns_level < 0 || p->pns_level > 10) return FAAC_ERR_INVALID_ARGUMENT; if (p->channel_map) { uint32_t i; if (p->channel_map_count < p->num_channels) return FAAC_ERR_INVALID_ARGUMENT; for (i = 0; i < p->num_channels; i++) if (p->channel_map[i] < 0 || (uint32_t)p->channel_map[i] >= p->num_channels) return FAAC_ERR_INVALID_ARGUMENT; } /* reserved padding must be zero so future fields can claim it safely */ if (p->reserved[0] || p->reserved[1] || p->reserved_trailing) return FAAC_ERR_INVALID_ARGUMENT; return FAAC_OK; } FAACAPI faac_status faac_encoder_open(const faac_params *p, faac_encoder **out) { faacEncStruct *h; faacEncConfiguration *cfg; unsigned long inSamples = 0, maxOut = 0; faac_status st; if (!out) return FAAC_ERR_INVALID_ARGUMENT; *out = NULL; if (!p) return FAAC_ERR_INVALID_ARGUMENT; /* struct_size lets the library reconcile a caller compiled against a * different header revision. A caller must be at least as new as this * build's struct (older/garbage/zero is rejected); newer callers are read * only through the fields this build knows. */ if (p->struct_size < sizeof(faac_params)) return FAAC_ERR_INVALID_ARGUMENT; st = validate_params(p); if (st != FAAC_OK) return st; h = (faacEncStruct *)faacEncOpen(p->sample_rate, p->num_channels, &inSamples, &maxOut); if (!h) return FAAC_ERR_NO_MEMORY; /* Translate params onto the handle's own configuration and run it through * the shared core validation/derivation in faacEncApplyConfig. */ cfg = &h->config; cfg->mpegVersion = (unsigned int)p->mpeg_version; cfg->aacObjectType = (unsigned int)p->object_type; /* LOW / HE_V1 / AUTO */ cfg->jointmode = (unsigned int)p->joint_mode; cfg->useLfe = p->use_lfe ? 1 : 0; cfg->useTns = p->use_tns ? 1 : 0; cfg->bitRate = p->bit_rate; cfg->bandWidth = p->bandwidth; cfg->quantqual = p->quant_quality; cfg->outputFormat = (unsigned int)p->output_format; cfg->inputFormat = (unsigned int)p->input_format; cfg->shortctl = (int)p->short_control; cfg->pnslevel = p->pns_level; if (p->channel_map) { uint32_t i; for (i = 0; i < p->num_channels; i++) cfg->channel_map[i] = p->channel_map[i]; } /* else: faacEncOpen already installed the identity map */ if (!faacEncApplyConfig(h, cfg)) { faacEncClose((faacEncHandle)h); return FAAC_ERR_INVALID_ARGUMENT; } *out = (faac_encoder *)h; return FAAC_OK; } FAACAPI faac_status faac_encoder_close(faac_encoder **enc) { if (!enc) return FAAC_ERR_INVALID_ARGUMENT; if (*enc) { faacEncClose((faacEncHandle)unwrap(*enc)); *enc = NULL; } return FAAC_OK; } FAACAPI faac_status faac_encoder_get_info(faac_encoder *enc, faac_encoder_info *out) { faac_encoder_info info; faacEncStruct *h; uint32_t caller_size, n; if (!enc || !out) return FAAC_ERR_INVALID_ARGUMENT; caller_size = out->struct_size; if (caller_size < sizeof(info.struct_size)) return FAAC_ERR_INVALID_ARGUMENT; h = unwrap(enc); memset(&info, 0, sizeof(info)); info.frame_samples = faacFrameSamples(h); info.max_output_bytes = (uint32_t)ADTS_FRAMESIZE; /* The handle holds the halved core rate for HE-AAC; report the rate the * decoder reconstructs. */ info.sample_rate = (uint32_t)SbrContextGetFullRate(h->sbrContext, h->sampleRate); info.object_type = (enum faac_object_type)h->config.aacObjectType; info.bit_rate = (uint32_t)h->config.bitRate; info.bandwidth = (uint32_t)h->config.bandWidth; info.quant_quality = (uint32_t)h->config.quantqual; info.pns_level = (int32_t)h->config.pnslevel; /* Write at most the caller's struct_size so a newer library cannot overrun * an older, smaller faac_encoder_info; report the byte count actually set. */ n = caller_size < (uint32_t)sizeof(info) ? caller_size : (uint32_t)sizeof(info); info.struct_size = n; memcpy(out, &info, n); return FAAC_OK; } FAACAPI faac_status faac_encoder_asc(faac_encoder *enc, const uint8_t **buf, uint32_t *len) { faacEncStruct *h; if (!enc || !buf || !len) return FAAC_ERR_INVALID_ARGUMENT; h = unwrap(enc); if (!h->ascCache) { int rc = faacEncGetDecoderSpecificInfo((faacEncHandle)h, &h->ascCache, &h->ascCacheLen); if (rc != 0) { h->ascCache = NULL; h->ascCacheLen = 0; /* -2 is "MPEG-2 has no ASC"; everything else is an allocation or * argument failure. */ return (rc == -2) ? FAAC_ERR_UNSUPPORTED : FAAC_ERR_NO_MEMORY; } } *buf = h->ascCache; *len = (uint32_t)h->ascCacheLen; return FAAC_OK; } FAACAPI faac_status faac_encoder_encode(faac_encoder *enc, const void *in, uint32_t in_samples, uint8_t *out, uint32_t out_cap, uint32_t *bytes_written) { faacEncStruct *h; int rc; if (!enc || !out || !bytes_written) return FAAC_ERR_INVALID_ARGUMENT; *bytes_written = 0; if (in_samples && !in) return FAAC_ERR_INVALID_ARGUMENT; h = unwrap(enc); if (out_cap < (uint32_t)ADTS_FRAMESIZE) return FAAC_ERR_OUTPUT_TOO_SMALL; if (in_samples > faacFrameSamples(h) * h->numChannels) return FAAC_ERR_INPUT_OVERFLOW; /* faacEncEncode reinterprets the input bytes per the configured * input_format; the int32_t* parameter is historical and does not imply an * int32 layout. */ rc = faacEncEncode((faacEncHandle)h, (int32_t *)(uintptr_t)in, in_samples, out, out_cap); /* A too-small output buffer was already rejected above, so a negative * return here is an unexpected core fault, not a buffer-size problem -- * don't disguise it as one or the caller will grow the buffer and retry. */ if (rc < 0) return FAAC_ERR_INTERNAL; *bytes_written = (uint32_t)rc; return FAAC_OK; } FAACAPI const char *faac_strerror(faac_status status) { switch (status) { case FAAC_OK: return "success"; case FAAC_ERR_INVALID_ARGUMENT: return "invalid argument"; case FAAC_ERR_UNSUPPORTED: return "unsupported configuration"; case FAAC_ERR_NO_MEMORY: return "out of memory"; case FAAC_ERR_OUTPUT_TOO_SMALL: return "output buffer too small"; case FAAC_ERR_INPUT_OVERFLOW: return "input sample count too large"; case FAAC_ERR_INTERNAL: return "internal encoder error"; case FAAC_STATUS_MAX: break; } return "unknown error"; } knik0-faac-1950446/libfaac/faac_internal.h000066400000000000000000000057151522734263700201570ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Copyright (C) 2001 Menno Bakker * * 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. */ /* * Private encoder core. What was historically the public faacEnc* C API is now * an internal implementation detail behind the sole public API, faac_* in * . These declarations are not installed and, lacking FAACAPI, not * exported -- the facade in faac_encoder.c is their only caller. */ #ifndef FAAC_INTERNAL_H #define FAAC_INTERNAL_H #include #define FAAC_CFG_VERSION 106 /* MPEG version */ enum { MPEG4 = 0, MPEG2 = 1 }; /* AAC object types this build implements, numbered per the MPEG-4 AOT * registry (mirrors the public FAAC_OBJ_* enum in ). AUTO defers the * choice between LOW and HE_V1 to faacEncApplyConfig. */ enum { AUTO = 0, LOW = 2, HE_V1 = 5 }; /* PCM input sample format. Named distinctly from the public faac_input_format * enumerators () so the facade can include both headers; faac.c * _Static_asserts the two enumerations agree value-for-value. */ enum { INPUT_NULL = 0, INPUT_16BIT, INPUT_24BIT, INPUT_32BIT, INPUT_FLOAT }; /* Block-type control */ enum { SHORTCTL_NORMAL = 0, SHORTCTL_NOSHORT = 1, SHORTCTL_NOLONG = 2 }; enum stream_format { RAW_STREAM = 0, ADTS_STREAM = 1 }; enum { JOINT_NONE = 0, JOINT_MS, JOINT_IS, JOINT_MIXED }; typedef struct faacEncConfiguration { unsigned int mpegVersion; unsigned int aacObjectType; unsigned int jointmode; unsigned int useLfe; unsigned int useTns; unsigned long bitRate; /* per channel */ unsigned int bandWidth; unsigned long quantqual; unsigned int outputFormat; /* 0 = raw, 1 = ADTS */ unsigned int inputFormat; int shortctl; int channel_map[64]; /* MAX_CHANNELS entries; identity by default */ int pnslevel; } faacEncConfiguration, *faacEncConfigurationPtr; typedef void *faacEncHandle; int faacEncGetVersion(char **id, char **copyright); int faacEncGetDecoderSpecificInfo(faacEncHandle hEncoder, unsigned char **ppBuffer, unsigned long *pSizeOfDecoderSpecificInfo); faacEncHandle faacEncOpen(unsigned long sampleRate, unsigned int numChannels, unsigned long *inputSamples, unsigned long *maxOutputBytes); int faacEncEncode(faacEncHandle hEncoder, int32_t *inputBuffer, unsigned int samplesInput, unsigned char *outputBuffer, unsigned int bufferSize); int faacEncClose(faacEncHandle hEncoder); #endif /* FAAC_INTERNAL_H */ knik0-faac-1950446/libfaac/fft.c000066400000000000000000000234431522734263700161410ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Copyright (C) 2002 Krzysztof Nikiel * * 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. */ #include #include #include "fft.h" #include "util.h" #define LOGM_SHORT 6 /* logm for the 256-sample short block MDCT */ #define LOGM_LONG FFT_MAXLOGM /* logm for the 2048-sample long block MDCT */ void fft_initialize(FFT_Tables *fft_tables) { int i; fft_tables->costbl = AllocMemory((FFT_MAXLOGM + 1) * sizeof(fft_tables->costbl[0])); fft_tables->negsintbl = AllocMemory((FFT_MAXLOGM + 1) * sizeof(fft_tables->negsintbl[0])); fft_tables->reordertbl = AllocMemory((FFT_MAXLOGM + 1) * sizeof(fft_tables->reordertbl[0])); if (!fft_tables->costbl || !fft_tables->negsintbl || !fft_tables->reordertbl) { if (fft_tables->costbl) FreeMemory(fft_tables->costbl); if (fft_tables->negsintbl) FreeMemory(fft_tables->negsintbl); if (fft_tables->reordertbl) FreeMemory(fft_tables->reordertbl); fft_tables->costbl = NULL; fft_tables->negsintbl = NULL; fft_tables->reordertbl = NULL; return; } for (i = 0; i < FFT_MAXLOGM + 1; i++) { fft_tables->costbl[i] = NULL; fft_tables->negsintbl[i] = NULL; fft_tables->reordertbl[i] = NULL; } for (i = 0; i < FFT_MAXLOGM + 1; i++) { fft_tables->mdct_cos[i] = NULL; fft_tables->mdct_sin[i] = NULL; } /* Precompute MDCT pre/post-twiddles for both block sizes now, so the per-frame twiddle loop is a table lookup instead of a cos/sin recurrence. */ { static const int logms[2] = { LOGM_SHORT, LOGM_LONG }; int t; for (t = 0; t < 2; t++) { int logm = logms[t]; int size = 1 << logm; double freq = 2.0 * M_PI_DOUBLE / (double)(4 << logm); fftfloat *c = AllocMemory(size * sizeof(fftfloat)); fftfloat *s = AllocMemory(size * sizeof(fftfloat)); if (!c || !s) { if (c) FreeMemory(c); if (s) FreeMemory(s); continue; } for (i = 0; i < size; i++) { double theta = freq * ((double)i + 0.125); c[i] = (fftfloat)cos(theta); s[i] = (fftfloat)sin(theta); } fft_tables->mdct_cos[logm] = c; fft_tables->mdct_sin[logm] = s; } } } void fft_terminate(FFT_Tables *fft_tables) { int i; for (i = 0; i < FFT_MAXLOGM + 1; i++) { if (fft_tables->costbl[i] != NULL) FreeMemory(fft_tables->costbl[i]); if (fft_tables->negsintbl[i] != NULL) FreeMemory(fft_tables->negsintbl[i]); if (fft_tables->reordertbl[i] != NULL) FreeMemory(fft_tables->reordertbl[i]); } for (i = 0; i < FFT_MAXLOGM + 1; i++) { if (fft_tables->mdct_cos[i] != NULL) FreeMemory(fft_tables->mdct_cos[i]); if (fft_tables->mdct_sin[i] != NULL) FreeMemory(fft_tables->mdct_sin[i]); fft_tables->mdct_cos[i] = NULL; fft_tables->mdct_sin[i] = NULL; } FreeMemory(fft_tables->costbl); FreeMemory(fft_tables->negsintbl); FreeMemory(fft_tables->reordertbl); fft_tables->costbl = NULL; fft_tables->negsintbl = NULL; fft_tables->reordertbl = NULL; } /* Radix-4 DIF. Swapping the 2nd/3rd butterfly outputs yields plain * bit-reversed order at the end, avoiding a digit-reversal permutation. * logm=9 (512) isn't a power of 4, so it ends with one radix-2 stage. */ static void check_tables_radix4(FFT_Tables *fft_tables, int logm) { if (fft_tables->costbl[logm] == NULL) { int size = 1 << logm; int i; /* one table serves all stages: stage k needs W_N^{k<<2k'} via tw_idx below */ fft_tables->costbl[logm] = AllocMemory(size * sizeof(*(fft_tables->costbl[0]))); fft_tables->negsintbl[logm] = AllocMemory(size * sizeof(*(fft_tables->negsintbl[0]))); if (!fft_tables->costbl[logm] || !fft_tables->negsintbl[logm]) { if (fft_tables->costbl[logm]) FreeMemory(fft_tables->costbl[logm]); if (fft_tables->negsintbl[logm]) FreeMemory(fft_tables->negsintbl[logm]); fft_tables->costbl[logm] = fft_tables->negsintbl[logm] = NULL; return; } for (i = 0; i < size; i++) { double theta = 2.0 * M_PI_DOUBLE * (double)i / (double)size; fft_tables->costbl[logm][i] = (fftfloat)cos(theta); fft_tables->negsintbl[logm][i] = (fftfloat)-sin(theta); } } } static void radix4_dif_proc( float * restrict xr, float * restrict xi, int logm, const fftfloat * restrict costbl, const fftfloat * restrict sintbl) { int n = 1 << logm; int n2 = n; int n1; int i, j, k; for (k = 0; k < (logm >> 1); k++) { n1 = n2; n2 >>= 2; for (i = 0; i < n; i += n1) { float * restrict r1p = xr + i; float * restrict r2p = xr + i + n2; float * restrict r3p = xr + i + 2*n2; float * restrict r4p = xr + i + 3*n2; float * restrict i1p = xi + i; float * restrict i2p = xi + i + n2; float * restrict i3p = xi + i + 2*n2; float * restrict i4p = xi + i + 3*n2; /* j=0 unrolled: skip the twiddle multiply, it's the identity here */ { float r1 = *r1p, i1 = *i1p; float r2 = *r2p, i2 = *i2p; float r3 = *r3p, i3 = *i3p; float r4 = *r4p, i4 = *i4p; float t1 = r1 + r3, t2 = i1 + i3; float t3 = r2 + r4, t4 = i2 + i4; float t5 = r1 - r3, t6 = i1 - i3; float t7 = r2 - r4, t8 = i2 - i4; *r1p = t1 + t3; *i1p = t2 + t4; *r3p = t5 + t8; *i3p = t6 - t7; *r2p = t1 - t3; *i2p = t2 - t4; *r4p = t5 - t8; *i4p = t6 + t7; r1p++; r2p++; r3p++; r4p++; i1p++; i2p++; i3p++; i4p++; } /* unit-stride pointers, not xr[i+j+...], so the compiler can vectorize this */ for (j = 1; j < n2; j++) { int tw_idx = j << (2 * k); const float c1 = (float)costbl[tw_idx]; const float s1 = (float)sintbl[tw_idx]; const float c2 = (float)costbl[2 * tw_idx]; const float s2 = (float)sintbl[2 * tw_idx]; const float c3 = (float)costbl[3 * tw_idx]; const float s3 = (float)sintbl[3 * tw_idx]; float r1 = *r1p, i1 = *i1p; float r2 = *r2p, i2 = *i2p; float r3 = *r3p, i3 = *i3p; float r4 = *r4p, i4 = *i4p; float t1 = r1 + r3, t2 = i1 + i3; float t3 = r2 + r4, t4 = i2 + i4; float t5 = r1 - r3, t6 = i1 - i3; float t7 = r2 - r4, t8 = i2 - i4; *r1p = t1 + t3; *i1p = t2 + t4; r1 = t1 - t3; i1 = t2 - t4; r2 = t5 + t8; i2 = t6 - t7; r3 = t5 - t8; i3 = t6 + t7; *r3p = r2 * c1 - i2 * s1; *i3p = r2 * s1 + i2 * c1; *r2p = r1 * c2 - i1 * s2; *i2p = r1 * s2 + i1 * c2; *r4p = r3 * c3 - i3 * s3; *i4p = r3 * s3 + i3 * c3; r1p++; r2p++; r3p++; r4p++; i1p++; i2p++; i3p++; i4p++; } } } /* odd logm: 4^k can't fill it, one radix-2 stage mops up the remainder */ if (logm & 1) { float * restrict r1p = xr; float * restrict r2p = xr + 1; float * restrict i1p = xi; float * restrict i2p = xi + 1; for (i = 0; i < n; i += 2) { float r1 = *r1p, i1 = *i1p; float r2 = *r2p, i2 = *i2p; *r1p = r1 + r2; *i1p = i1 + i2; *r2p = r1 - r2; *i2p = i1 - i2; r1p += 2; r2p += 2; i1p += 2; i2p += 2; } } } static void bit_reverse( float * restrict xr, float * restrict xi, int logm, const unsigned short * restrict r) { int i; int size = 1 << logm; for (i = 0; i < size; i++) { int j = (int)r[i]; if (j > i) { float tr = xr[i]; xr[i] = xr[j]; xr[j] = tr; float ti = xi[i]; xi[i] = xi[j]; xi[j] = ti; } } } void fft(FFT_Tables *fft_tables, float *xr, float *xi, int logm) { if (logm > FFT_MAXLOGM) return; if (logm < 1) return; check_tables_radix4(fft_tables, logm); if (fft_tables->reordertbl[logm] == NULL) { int size = 1 << logm; int i; fft_tables->reordertbl[logm] = AllocMemory(size * sizeof(*(fft_tables->reordertbl[0]))); if (!fft_tables->reordertbl[logm]) return; for (i = 0; i < size; i++) { int reversed = 0; int b; int tmp = i; for (b = 0; b < logm; b++) { reversed = (reversed << 1) | (tmp & 1); tmp >>= 1; } fft_tables->reordertbl[logm][i] = (unsigned short)reversed; } } radix4_dif_proc(xr, xi, logm, fft_tables->costbl[logm], fft_tables->negsintbl[logm]); bit_reverse(xr, xi, logm, fft_tables->reordertbl[logm]); } knik0-faac-1950446/libfaac/fft.h000066400000000000000000000025211522734263700161400ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Copyright (C) 2002 Krzysztof Nikiel * * 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. */ #ifndef _FFT_H_ #define _FFT_H_ #define FFT_MAXLOGM 9 typedef float fftfloat; typedef struct { fftfloat **costbl; fftfloat **negsintbl; unsigned short **reordertbl; /* MDCT pre/post-twiddle factors cos/sin(freq*(i+1/8)), one table pair per * transform size (indexed by the size's fft logm). Precomputing them * breaks the serial cos/sin recurrence that kept the MDCT twiddle loops * from vectorizing, and is more accurate than the recurrence. */ fftfloat *mdct_cos[FFT_MAXLOGM + 1]; fftfloat *mdct_sin[FFT_MAXLOGM + 1]; } FFT_Tables; void fft_initialize ( FFT_Tables *fft_tables ); void fft_terminate ( FFT_Tables *fft_tables ); void fft ( FFT_Tables *fft_tables, float *xr, float *xi, int logm ); #endif knik0-faac-1950446/libfaac/filtbank.c000066400000000000000000000261431522734263700171540ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Copyright (C) 2026 Nils Schimmelmann * * 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. */ #include #include #include #include #include "coder.h" #include "filtbank.h" #include "frame.h" #include "fft.h" #include "util.h" /* Sine and Kaiser-Bessel-Derived windows, ISO/IEC 13818-7 Annex 4.6.4. * KBD argument uses the product form i*(halfLen-i) (a difference-of- * squares factoring of the spec's (2i/halfLen-1)^2 shape term) so the * Bessel argument is one multiply instead of a subtract-then-square. * These tables are built once at encoder init, so the series and * normalization run in double (rounding to float only when stored into * win[]) for a correctly-rounded table at zero runtime cost. */ static double BesselI0(double x) { const double tolerance = DBL_EPSILON; double halfX = x * 0.5; double term = 1.0; double series = 1.0; int k = 1; do { double ratio = halfX / (double)k; term *= ratio * ratio; series += term; k++; } while (term > tolerance * series); return series; } static void FillSineWindow(float *win, int halfLen) { int i; for (i = 0; i < halfLen; i++) win[i] = (float)sin((M_PI_DOUBLE / (2 * halfLen)) * (i + 0.5)); } static void FillKbdWindow(float *win, int halfLen, double alpha) { const double omega = alpha * M_PI_DOUBLE / (double)halfLen; const double alpha2 = 4.0 * omega * omega; const int quarterLen = halfLen / 2; double shapeTerm[BLOCK_LEN_LONG / 2 + 1]; double weightedTotal = 0.0; double running = 0.0; double scale; int i; /* Symmetric around quarterLen, so interior terms count twice below. */ for (i = 0; i <= quarterLen; i++) { double symmetric = (double)i * (double)(halfLen - i) * alpha2; int isInterior = (i > 0) && (i < quarterLen); shapeTerm[i] = BesselI0(sqrt(symmetric)); weightedTotal += shapeTerm[i] * (isInterior ? 2 : 1); } scale = 1.0 / (weightedTotal + 1.0); for (i = 0; i <= quarterLen; i++) { running += shapeTerm[i]; win[i] = (float)sqrt(running * scale); } /* Past the midpoint, reuse the mirrored term instead of recomputing it. */ for (; i < halfLen; i++) { running += shapeTerm[halfLen - i]; win[i] = (float)sqrt(running * scale); } } typedef struct { float *sine; float *kbd; } WindowPair; static void BuildWindowPair(WindowPair *wp, int halfLen, double kbdAlpha) { FillSineWindow(wp->sine, halfLen); FillKbdWindow(wp->kbd, halfLen, kbdAlpha); } void FilterBankInit(faacEncStruct* hEncoder) { unsigned int channel; WindowPair longPair, shortPair; for (channel = 0; channel < hEncoder->numChannels; channel++) { hEncoder->freqBuff[channel] = (float*)AllocMemory(2*FRAME_LEN*sizeof(float)); if (!hEncoder->freqBuff[channel]) return; } hEncoder->sin_window_long = (float*)AllocMemory(BLOCK_LEN_LONG*sizeof(float)); hEncoder->sin_window_short = (float*)AllocMemory(BLOCK_LEN_SHORT*sizeof(float)); hEncoder->kbd_window_long = (float*)AllocMemory(BLOCK_LEN_LONG*sizeof(float)); hEncoder->kbd_window_short = (float*)AllocMemory(BLOCK_LEN_SHORT*sizeof(float)); if (!hEncoder->sin_window_long || !hEncoder->sin_window_short || !hEncoder->kbd_window_long || !hEncoder->kbd_window_short) return; longPair.sine = hEncoder->sin_window_long; longPair.kbd = hEncoder->kbd_window_long; shortPair.sine = hEncoder->sin_window_short; shortPair.kbd = hEncoder->kbd_window_short; BuildWindowPair(&longPair, BLOCK_LEN_LONG, 4.0); BuildWindowPair(&shortPair, BLOCK_LEN_SHORT, 6.0); hEncoder->gpsyInfo.sharedWorkBuffLong = (float*)AllocMemory(2*BLOCK_LEN_LONG*sizeof(float)); } void FilterBankEnd(faacEncStruct* hEncoder) { unsigned int channel; for (channel = 0; channel < hEncoder->numChannels; channel++) { if (hEncoder->freqBuff[channel]) FreeMemory(hEncoder->freqBuff[channel]); } if (hEncoder->sin_window_long) FreeMemory(hEncoder->sin_window_long); if (hEncoder->sin_window_short) FreeMemory(hEncoder->sin_window_short); if (hEncoder->kbd_window_long) FreeMemory(hEncoder->kbd_window_long); if (hEncoder->kbd_window_short) FreeMemory(hEncoder->kbd_window_short); if (hEncoder->gpsyInfo.sharedWorkBuffLong) FreeMemory(hEncoder->gpsyInfo.sharedWorkBuffLong); } /* Four ICS window sequences, ISO/IEC 13818-7 4.3.2.4. */ typedef struct { float *dst; const float *src; const float *win; int len; bool reverse; } WindowSeg; static void ApplyWindowSeg(const WindowSeg *seg) { int i; if (seg->reverse) { for (i = 0; i < seg->len; i++) seg->dst[i] = seg->src[i] * seg->win[seg->len - 1 - i]; } else { for (i = 0; i < seg->len; i++) seg->dst[i] = seg->src[i] * seg->win[i]; } } static void CopyFlat(float *dst, const float *src, int len) { memcpy(dst, src, len * sizeof(float)); } static void ZeroFlat(float *dst, int len) { SetMemory(dst, 0, len * sizeof(float)); } static const float *SelectWindow(faacEncStruct *hEncoder, int shape, bool isLong) { if (shape == KBD_WINDOW) return isLong ? hEncoder->kbd_window_long : hEncoder->kbd_window_short; return isLong ? hEncoder->sin_window_long : hEncoder->sin_window_short; } void FilterBank(faacEncStruct* hEncoder, CoderInfo *coderInfo, float * restrict p_prev_data, float * restrict p_in_data, float * restrict p_out_mdct) { float * restrict overlapBuf = hEncoder->gpsyInfo.sharedWorkBuffLong; int block_type = coderInfo->block_type; const float *leftWin, *rightWin; int k; /* Assemble the 2048-sample overlap window from the previous and current frame's time-domain samples. */ memcpy(overlapBuf, p_prev_data, BLOCK_LEN_LONG*sizeof(float)); memcpy(overlapBuf+BLOCK_LEN_LONG, p_in_data, BLOCK_LEN_LONG*sizeof(float)); /* isLong is a literal per case below, not carried in from before the switch, so SelectWindow's dispatch folds to a single compare. */ switch (block_type) { case ONLY_LONG_WINDOW: { WindowSeg left = { p_out_mdct, overlapBuf, SelectWindow(hEncoder, coderInfo->prev_window_shape, true), BLOCK_LEN_LONG, false }; WindowSeg right = { p_out_mdct+BLOCK_LEN_LONG, overlapBuf+BLOCK_LEN_LONG, SelectWindow(hEncoder, coderInfo->window_shape, true), BLOCK_LEN_LONG, true }; ApplyWindowSeg(&left); ApplyWindowSeg(&right); MDCT(&hEncoder->fft_tables, p_out_mdct, 2*BLOCK_LEN_LONG, hEncoder->gpsyInfo.sharedWorkBuffLong); break; } case LONG_SHORT_WINDOW: { WindowSeg left = { p_out_mdct, overlapBuf, SelectWindow(hEncoder, coderInfo->prev_window_shape, true), BLOCK_LEN_LONG, false }; WindowSeg right = { p_out_mdct+BLOCK_LEN_LONG+NFLAT_LS, overlapBuf+BLOCK_LEN_LONG+NFLAT_LS, SelectWindow(hEncoder, coderInfo->window_shape, false), BLOCK_LEN_SHORT, true }; ApplyWindowSeg(&left); CopyFlat(p_out_mdct+BLOCK_LEN_LONG, overlapBuf+BLOCK_LEN_LONG, NFLAT_LS); ApplyWindowSeg(&right); ZeroFlat(p_out_mdct+BLOCK_LEN_LONG+NFLAT_LS+BLOCK_LEN_SHORT, NFLAT_LS); MDCT(&hEncoder->fft_tables, p_out_mdct, 2*BLOCK_LEN_LONG, hEncoder->gpsyInfo.sharedWorkBuffLong); break; } case SHORT_LONG_WINDOW: { WindowSeg left = { p_out_mdct+NFLAT_LS, overlapBuf+NFLAT_LS, SelectWindow(hEncoder, coderInfo->prev_window_shape, false), BLOCK_LEN_SHORT, false }; WindowSeg right = { p_out_mdct+BLOCK_LEN_LONG, overlapBuf+BLOCK_LEN_LONG, SelectWindow(hEncoder, coderInfo->window_shape, true), BLOCK_LEN_LONG, true }; ZeroFlat(p_out_mdct, NFLAT_LS); ApplyWindowSeg(&left); CopyFlat(p_out_mdct+NFLAT_LS+BLOCK_LEN_SHORT, overlapBuf+NFLAT_LS+BLOCK_LEN_SHORT, NFLAT_LS); ApplyWindowSeg(&right); MDCT(&hEncoder->fft_tables, p_out_mdct, 2*BLOCK_LEN_LONG, hEncoder->gpsyInfo.sharedWorkBuffLong); break; } case ONLY_SHORT_WINDOW: { float *src = overlapBuf + NFLAT_LS; float *dst = p_out_mdct; leftWin = SelectWindow(hEncoder, coderInfo->prev_window_shape, false); rightWin = SelectWindow(hEncoder, coderInfo->window_shape, false); for (k = 0; k < MAX_SHORT_WINDOWS; k++) { WindowSeg left = { dst, src, leftWin, BLOCK_LEN_SHORT, false }; WindowSeg right = { dst+BLOCK_LEN_SHORT, src+BLOCK_LEN_SHORT, rightWin, BLOCK_LEN_SHORT, true }; ApplyWindowSeg(&left); ApplyWindowSeg(&right); MDCT(&hEncoder->fft_tables, dst, 2*BLOCK_LEN_SHORT, hEncoder->gpsyInfo.sharedWorkBuffLong); dst += BLOCK_LEN_SHORT; src += BLOCK_LEN_SHORT; leftWin = rightWin; } break; } } } void MDCT( FFT_Tables *fft_tables, float * restrict data, int N, float * restrict work ) { const int N2 = N >> 1; const int N4 = N >> 2; const int N8 = N >> 3; const int logm = (N == 2 * BLOCK_LEN_LONG) ? 9 : 6; const fftfloat * restrict cosT = fft_tables->mdct_cos[logm]; const fftfloat * restrict sinT = fft_tables->mdct_sin[logm]; float * restrict xr = work; float * restrict xi = work + N4; int i; /* Sign pattern flips at N/8 - the real input's symmetry folds differently on either side of that midpoint. */ for (i = 0; i < N8; i++) { int n1 = N2 - 1 - 2*i; int n2 = 2*i; float foldedRe = data[N4 + n1] + data[N + N4 - 1 - n1]; float foldedIm = data[N4 + n2] - data[N4 - 1 - n2]; xr[i] = foldedRe * cosT[i] + foldedIm * sinT[i]; xi[i] = foldedIm * cosT[i] - foldedRe * sinT[i]; } for (; i < N4; i++) { int n1 = N2 - 1 - 2*i; int n2 = 2*i; float foldedRe = data[N4 + n1] - data[N4 - 1 - n1]; float foldedIm = data[N4 + n2] + data[N + N4 - 1 - n2]; xr[i] = foldedRe * cosT[i] + foldedIm * sinT[i]; xi[i] = foldedIm * cosT[i] - foldedRe * sinT[i]; } fft( fft_tables, xr, xi, logm); /* Unfold N/4 complex FFT outputs into N real coefficients, one write per output quarter. */ for (i = 0; i < N4; i++) { int n2 = 2*i; float unfoldRe = 2.0f * (xr[i] * cosT[i] + xi[i] * sinT[i]); float unfoldIm = 2.0f * (xi[i] * cosT[i] - xr[i] * sinT[i]); data[n2] = -unfoldRe; data[N2 - 1 - n2] = unfoldIm; data[N2 + n2] = -unfoldIm; data[N - 1 - n2] = unfoldRe; } } knik0-faac-1950446/libfaac/filtbank.h000066400000000000000000000023321522734263700171530ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Copyright (C) 2001 Menno Bakker * * 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. */ #ifndef FILTBANK_H #define FILTBANK_H #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ #include "frame.h" #define NFLAT_LS 448 #define SINE_WINDOW 0 #define KBD_WINDOW 1 void FilterBankInit ( faacEncStruct* hEncoder ); void FilterBankEnd ( faacEncStruct* hEncoder ); void MDCT ( FFT_Tables *fft_tables, float * restrict data, int N, float * restrict work ); void FilterBank( faacEncStruct* hEncoder, CoderInfo *coderInfo, float * restrict p_prev_data, float * restrict p_in_data, float * restrict p_out_mdct); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* FILTBANK_H */ knik0-faac-1950446/libfaac/frame.c000066400000000000000000001033111522734263700164450ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Copyright (C) 2001 Menno Bakker * * 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. */ #include #include #include #include #include "frame.h" #include "coder.h" #include "channels.h" #include "bitstream.h" #include "filtbank.h" #include "quantize.h" #include "util.h" #include "tns.h" #include "stereo.h" #include "sbr.h" /* HE-AAC auto-mode thresholds; tuned via ViSQOL on a 49-clip corpus. */ #define HE_MIN_SAMPLE_RATE 32000 /* Fs/2 < 16 kHz below this → core too narrow for SBR */ #define HE_MIN_BITRATE_PER_CH 12000 /* below floor HE wins by an ever-widening margin */ #define HE_MAX_BITRATE_PER_CH 28000 /* above ceiling LC wins: SBR costs up to 1 MOS on transients */ #define HE_VBR_QUANTQUAL_MAX 60 /* quality ≤60 ≈ ≤100 kbps; HE saves bits */ #if (defined WIN32 || defined _WIN32 || defined WIN64 || defined _WIN64) && !defined(PACKAGE_VERSION) #include "win32_ver.h" #endif /* Rate control tuning constants */ #define RC_DEADBAND_THRESHOLD 0.05f /* +/- 5% deadband */ #define RC_DAMPING_FACTOR 0.6f /* Control loop damping */ static char *libfaacName = PACKAGE_VERSION; static char *libCopyright = "FAAC - Freeware Advanced Audio Coder (http://faac.sourceforge.net/)\n" " Copyright (C) 1999-2001, Menno Bakker\n" " Copyright (C) 2002-2017, Krzysztof Nikiel\n" " Copyright (C) 2004, Dan Villiom P. Christiansen\n" " Copyright (C) 2005-2026, Fabian Greffrath\n" " Copyright (C) 2026, Nils Schimmelmann\n"; static unsigned int CalcBandwidth(unsigned long bitRate, unsigned long sampleRate) { const unsigned int nyquist = sampleRate / 2; unsigned int bw; if (!bitRate) return nyquist; if (bitRate <= 16000) { /* Segment 1: Telephony (4kHz to 6kHz) */ bw = 4000 + (bitRate / 8); } else if (bitRate <= 32000) { /* Segment 2: Low-tier (6kHz to 11kHz) */ bw = 6000 + ((bitRate - 16000) * 5 / 16); } else if (bitRate <= 64000) { /* Segment 3: Mid-tier expansion (11kHz to 18.5kHz) */ bw = 11000 + ((bitRate - 32000) * 15 / 64); } else if (bitRate <= 128000) { /* Segment 4: High-fidelity catch-up (18.5kHz to 20kHz) */ bw = 18500 + ((bitRate - 64000) * 3 / 128); } else { /* Segment 5: Transparency plateau (20kHz+) */ bw = 20000 + ((bitRate - 128000) / 16); if (bw > 20000) bw = 20000; } /* Safety clamp to Shannon-Nyquist limit */ return (bw > nyquist) ? nyquist : bw; } /* Element-to-channel mapping is fixed for the session once InitElements has * run, so cache which channels are LFE here instead of rescanning * hEncoder->elements[] for every channel on every frame. */ static void RefreshLfeMap(faacEncStruct *hEncoder) { memset(hEncoder->isLfeChannel, 0, sizeof(hEncoder->isLfeChannel)); for (int e = 0; e < hEncoder->numElements; e++) { if (hEncoder->elements[e].type == ID_LFE) hEncoder->isLfeChannel[hEncoder->elements[e].channels[0]] = true; } } int faacEncGetVersion( char **faac_id_string, char **faac_copyright_string) { if (faac_id_string) *faac_id_string = libfaacName; if (faac_copyright_string) *faac_copyright_string = libCopyright; return FAAC_CFG_VERSION; } int faacEncGetDecoderSpecificInfo(faacEncHandle hpEncoder,unsigned char** ppBuffer,unsigned long* pSizeOfDecoderSpecificInfo) { faacEncStruct* hEncoder = (faacEncStruct*)hpEncoder; BitStream* pBitStream = NULL; if((hEncoder == NULL) || (ppBuffer == NULL) || (pSizeOfDecoderSpecificInfo == NULL)) { return -1; } if(hEncoder->config.mpegVersion == MPEG2){ return -2; /* not supported */ } if (hEncoder->config.aacObjectType == HE_V1 && hEncoder->sbrContext) { return SbrContextGetASC(hEncoder->sbrContext, hEncoder->sampleRateIdx, hEncoder->numChannels, ppBuffer, pSizeOfDecoderSpecificInfo); } *pSizeOfDecoderSpecificInfo = 2; *ppBuffer = (unsigned char *)malloc(2); if(*ppBuffer != NULL){ memset(*ppBuffer,0,*pSizeOfDecoderSpecificInfo); pBitStream = OpenBitStream((uint32_t)*pSizeOfDecoderSpecificInfo, *ppBuffer); if (!pBitStream) { free(*ppBuffer); *ppBuffer = NULL; return -3; } PutBit(pBitStream, hEncoder->config.aacObjectType, 5); PutBit(pBitStream, hEncoder->sampleRateIdx, 4); PutBit(pBitStream, hEncoder->numChannels, 4); CloseBitStream(pBitStream); return 0; } else { return -3; } } /* Configuration worker behind faac_encoder_open(): validates the config, * resolves AUTO/HE-AAC, and (re)initializes the encoder for it. Returns 1 on * success, 0 on failure. */ int faacEncApplyConfig(faacEncStruct* hEncoder, faacEncConfigurationPtr config) { int i; int maxqual = hEncoder->config.outputFormat ? MAXQUALADTS : MAXQUAL; hEncoder->config.jointmode = config->jointmode; hEncoder->config.useLfe = config->useLfe; hEncoder->config.useTns = config->useTns; hEncoder->config.aacObjectType = config->aacObjectType; hEncoder->config.mpegVersion = config->mpegVersion; hEncoder->config.outputFormat = config->outputFormat; hEncoder->config.inputFormat = config->inputFormat; hEncoder->config.shortctl = config->shortctl; assert((hEncoder->config.outputFormat == 0) || (hEncoder->config.outputFormat == 1)); /* If this handle was previously resolved to HE-AAC, restore the native Fs so * object-type resolution below always starts from a consistent base (needed * when a later call toggles between LC and HE-AAC). */ SbrContextRestoreRate(hEncoder->sbrContext, &hEncoder->sampleRate, &hEncoder->sampleRateIdx, &hEncoder->srInfo); switch( hEncoder->config.inputFormat ) { case INPUT_16BIT: case INPUT_32BIT: case INPUT_FLOAT: break; default: return 0; } /* Only LC, HE-AAC v1, and AUTO (which resolves to one of them) are * supported object types. */ if (hEncoder->config.aacObjectType != LOW && hEncoder->config.aacObjectType != HE_V1 && hEncoder->config.aacObjectType != AUTO) return 0; /* Check for correct bitrate */ if (!hEncoder->sampleRate || !hEncoder->numChannels) return 0; /* Clamp against the full (pre-downsample) rate: for an already-resolved * HE-AAC handle sampleRate is the halved core rate. */ { unsigned long fullRate = SbrContextGetFullRate(hEncoder->sbrContext, hEncoder->sampleRate); if (config->bitRate > (MaxBitrate(fullRate) / hEncoder->numChannels)) config->bitRate = MaxBitrate(fullRate) / hEncoder->numChannels; } /* Resolve AUTO to LC or HE-AAC. HE-AAC wins for low rates, but only * at Fs >= 32 kHz so the Fs/2 core stays >= 16 kHz; below that the * narrow-band core + SBR reconstruction collapses. */ if (hEncoder->config.aacObjectType == AUTO) { unsigned long rate_per_ch = config->bitRate; int rate_ok; if (rate_per_ch > 0) { /* Threshold scales with Fs: (3/4)*Fs - 4 kHz gives ~20 kbps at 32 kHz, * saturating at HE_MAX_BITRATE_PER_CH for Fs ≥ 44.1 kHz. */ unsigned int max_he_rate = (unsigned int)(hEncoder->sampleRate * 3 / 4 - 4000); if (max_he_rate > HE_MAX_BITRATE_PER_CH) max_he_rate = HE_MAX_BITRATE_PER_CH; rate_ok = (rate_per_ch >= HE_MIN_BITRATE_PER_CH && rate_per_ch <= max_he_rate); } else { rate_ok = (config->quantqual <= HE_VBR_QUANTQUAL_MAX); } hEncoder->config.aacObjectType = (rate_ok && hEncoder->sampleRate >= HE_MIN_SAMPLE_RATE) ? HE_V1 : LOW; config->aacObjectType = hEncoder->config.aacObjectType; } if (hEncoder->config.aacObjectType == HE_V1 && hEncoder->sampleRate < HE_MIN_SAMPLE_RATE) return 0; /* HE-AAC: encode the core as AAC-LC; SBR rebuilds the top octave. The core * runs dual-rate at Fs/2; the original rate is kept for SBR and the ASC. * (Single-rate SBR is not supported: decoders unconditionally reconstruct * the SBR band table from 2*core_rate, so a full-Fs core is undecodeable.) */ if (hEncoder->config.aacObjectType == HE_V1) { hEncoder->config.mpegVersion = MPEG4; if (!hEncoder->sbrContext) hEncoder->sbrContext = SbrContextInit(hEncoder->numChannels); if (!hEncoder->sbrContext) return 0; SbrContextResolveRate(hEncoder->sbrContext, &hEncoder->sampleRate, &hEncoder->sampleRateIdx, &hEncoder->srInfo); } /* Re-init TNS for new profile */ TnsInit(hEncoder); if (config->bitRate && !config->bandWidth) { config->bandWidth = CalcBandwidth(config->bitRate, hEncoder->sampleRate); if (!config->quantqual) { config->quantqual = (float)config->bitRate * hEncoder->numChannels / 1280; if (config->quantqual > DEFQUAL) config->quantqual = (config->quantqual - DEFQUAL) * 3.0f + DEFQUAL; } } if (!config->quantqual) config->quantqual = DEFQUAL; hEncoder->config.bitRate = config->bitRate; if (!config->bandWidth) { config->bandWidth = CalcBandwidth(config->bitRate, hEncoder->sampleRate); } hEncoder->config.bandWidth = config->bandWidth; // check bandwidth if (hEncoder->config.bandWidth < 100) hEncoder->config.bandWidth = 100; if (hEncoder->config.bandWidth > (hEncoder->sampleRate / 2)) hEncoder->config.bandWidth = hEncoder->sampleRate / 2; if (config->quantqual > (unsigned long)maxqual) config->quantqual = maxqual; if (config->quantqual < MINQUAL) config->quantqual = MINQUAL; hEncoder->config.quantqual = config->quantqual; if (config->mpegVersion == MPEG2) config->pnslevel = 0; if (config->pnslevel < 0) config->pnslevel = 0; if (config->pnslevel > 10) config->pnslevel = 10; hEncoder->aacquantCfg.pnslevel = config->pnslevel; /* set quantization quality */ hEncoder->aacquantCfg.quality = config->quantqual; if (hEncoder->config.aacObjectType == HE_V1) { SBRContext *sCtx = hEncoder->sbrContext; SbrContextUpdateConfig(sCtx, hEncoder->numChannels, hEncoder->config.bitRate * hEncoder->numChannels, &hEncoder->fft_tables); /* kx * Fs / (2*64): each QMF band is Fs/(2*SBR_QMF_BANDS_64) Hz wide. * Matching core bandwidth to the SBR crossover avoids a gap or overlap. */ hEncoder->config.bandWidth = SbrContextGetXOverBandwidth(sCtx); } else { if (hEncoder->sbrContext) { SbrContextEnd(hEncoder->sbrContext); hEncoder->sbrContext = NULL; } } /* Input FIFO: holds one frame plus up to one full incoming chunk of leftover. * HE-AAC frames are 2*FRAME_LEN (the dual-rate core runs at Fs/2), LC is * FRAME_LEN. Sizing covers the largest frame the resolved object type could * need so toggling SBR across SetConfiguration calls never reallocs. */ { unsigned int cap = 2 * faacFrameSamples(hEncoder); unsigned int channel; for (channel = 0; channel < hEncoder->numChannels; channel++) if (!hEncoder->inputFifo[channel]) { hEncoder->inputFifo[channel] = (float *)AllocMemory(cap * sizeof(float)); if (!hEncoder->inputFifo[channel]) return 0; } hEncoder->inputFifoCap = cap; hEncoder->inputFifoFill = 0; } CalcBW(&hEncoder->config.bandWidth, hEncoder->sampleRate, hEncoder->srInfo, &hEncoder->aacquantCfg); // reset psymodel PsyEnd(hEncoder->psyInfo, hEncoder->numChannels); PsyInit(&hEncoder->gpsyInfo, hEncoder->psyInfo, hEncoder->numChannels, hEncoder->sampleRate); /* load channel_map */ for( i = 0; i < MAX_CHANNELS; i++ ) hEncoder->config.channel_map[i] = config->channel_map[i]; InitElements(hEncoder->elements, &hEncoder->numElements, (int)hEncoder->numChannels, hEncoder->config.useLfe); RefreshLfeMap(hEncoder); return 1; } faacEncHandle faacEncOpen(unsigned long sampleRate, unsigned int numChannels, unsigned long *inputSamples, unsigned long *maxOutputBytes) { unsigned int channel; faacEncStruct* hEncoder; if (numChannels < 1 || numChannels > MAX_CHANNELS) return NULL; *inputSamples = FRAME_LEN*numChannels; *maxOutputBytes = ADTS_FRAMESIZE; hEncoder = (faacEncStruct*)AllocMemory(sizeof(faacEncStruct)); if (!hEncoder) return NULL; SetMemory(hEncoder, 0, sizeof(faacEncStruct)); hEncoder->numChannels = numChannels; hEncoder->sampleRate = sampleRate; hEncoder->sampleRateIdx = GetSRIndex(sampleRate); /* Initialize variables to default values */ hEncoder->frameNum = 0; hEncoder->flushFrame = 0; /* Default configuration */ hEncoder->config.mpegVersion = MPEG4; hEncoder->config.aacObjectType = LOW; hEncoder->config.jointmode = JOINT_MIXED; hEncoder->config.pnslevel = 4; hEncoder->config.useLfe = 1; hEncoder->config.useTns = 0; hEncoder->config.bitRate = 64000; hEncoder->config.bandWidth = CalcBandwidth(hEncoder->config.bitRate, sampleRate); hEncoder->config.quantqual = 0; hEncoder->config.shortctl = SHORTCTL_NORMAL; /* default channel map is straight-through */ for( channel = 0; channel < MAX_CHANNELS; channel++ ) hEncoder->config.channel_map[channel] = channel; hEncoder->config.outputFormat = ADTS_STREAM; /* be compatible with software which assumes 24bit in 32bit PCM */ hEncoder->config.inputFormat = INPUT_32BIT; /* find correct sampling rate depending parameters */ hEncoder->srInfo = &srInfo[hEncoder->sampleRateIdx]; for (channel = 0; channel < numChannels; channel++) { int buf; hEncoder->coderInfo[channel].prev_window_shape = SINE_WINDOW; hEncoder->coderInfo[channel].window_shape = SINE_WINDOW; hEncoder->coderInfo[channel].block_type = ONLY_LONG_WINDOW; hEncoder->coderInfo[channel].groups.n = 1; hEncoder->coderInfo[channel].groups.len[0] = 1; for (buf = 0; buf < 4; buf++) { hEncoder->audioFIFO[channel][buf] = (float*)AllocMemory(FRAME_LEN*sizeof(float)); if (!hEncoder->audioFIFO[channel][buf]) { faacEncClose(hEncoder); return NULL; } memset(hEncoder->audioFIFO[channel][buf], 0, FRAME_LEN*sizeof(float)); } } /* Initialize coder functions */ InitElements(hEncoder->elements, &hEncoder->numElements, (int)hEncoder->numChannels, (bool)hEncoder->config.useLfe); RefreshLfeMap(hEncoder); fft_initialize( &hEncoder->fft_tables ); PsyInit(&hEncoder->gpsyInfo, hEncoder->psyInfo, hEncoder->numChannels, hEncoder->sampleRate); FilterBankInit(hEncoder); TnsInit(hEncoder); QuantizeInit(); /* Return handle */ return hEncoder; } /* Append the caller's (interleaved) input to the per-channel input FIFO, * de-interleaving and converting to float once here so the rest of the * encoder is agnostic to the input format. samplesInput may be any count that * fits the FIFO; returns -1 on overflow or an invalid format. */ static int appendInputFifo(faacEncStruct *hEncoder, int32_t *inputBuffer, unsigned int samplesInput) { unsigned int numChannels = hEncoder->numChannels; unsigned int spch = samplesInput / numChannels; unsigned int channel, i; if (spch == 0) return 0; if (hEncoder->inputFifoFill + spch > hEncoder->inputFifoCap) return -1; for (channel = 0; channel < numChannels; channel++) { float *dst = hEncoder->inputFifo[channel] + hEncoder->inputFifoFill; switch (hEncoder->config.inputFormat) { case INPUT_16BIT: { short *src = (short *)inputBuffer + hEncoder->config.channel_map[channel]; for (i = 0; i < spch; i++) { dst[i] = (float)*src; src += numChannels; } break; } case INPUT_32BIT: { int32_t *src = (int32_t *)inputBuffer + hEncoder->config.channel_map[channel]; for (i = 0; i < spch; i++) { dst[i] = (1.0f/256) * (float)*src; src += numChannels; } break; } case INPUT_FLOAT: { float *src = (float *)inputBuffer + hEncoder->config.channel_map[channel]; for (i = 0; i < spch; i++) { dst[i] = (float)*src; src += numChannels; } break; } default: return -1; } } hEncoder->inputFifoFill += spch; return 0; } /* Drop n samples/channel from the front of the FIFO, shifting the leftover down. */ static void consumeInputFifo(faacEncStruct *hEncoder, unsigned int n) { unsigned int numChannels = hEncoder->numChannels; unsigned int channel, rem; if (n > hEncoder->inputFifoFill) n = hEncoder->inputFifoFill; rem = hEncoder->inputFifoFill - n; if (rem) for (channel = 0; channel < numChannels; channel++) memmove(hEncoder->inputFifo[channel], hEncoder->inputFifo[channel] + n, rem * sizeof(float)); hEncoder->inputFifoFill = rem; } int faacEncClose(faacEncHandle hpEncoder) { faacEncStruct* hEncoder = (faacEncStruct*)hpEncoder; unsigned int channel; if (!hEncoder) return 0; PsyEnd(hEncoder->psyInfo, hEncoder->numChannels); FilterBankEnd(hEncoder); fft_terminate(&hEncoder->fft_tables); for (channel = 0; channel < hEncoder->numChannels; channel++) { int buf; for (buf = 0; buf < 4; buf++) { if (hEncoder->audioFIFO[channel][buf]) FreeMemory(hEncoder->audioFIFO[channel][buf]); } if (hEncoder->inputFifo[channel]) FreeMemory (hEncoder->inputFifo[channel]); } if (hEncoder->ascCache) free(hEncoder->ascCache); if (hEncoder->sbrContext) { SbrContextEnd(hEncoder->sbrContext); hEncoder->sbrContext = NULL; } FreeMemory(hEncoder); return 0; } /* HE-AAC per-frame front end: take one assembled full-rate frame from the FIFO * front (realPerCh real samples/ch, the rest silence-padded), run SBR analysis * on it, then 2:1 downsample to produce the AAC-LC core signal. The FIFO is not * consumed here; the caller drops the frame after the core has read heHalfRate. * Cold path, kept out of the LC fast path. */ #if defined(__GNUC__) __attribute__((cold, noinline)) #endif static void doHEAACFrame(faacEncStruct *hEncoder, unsigned int realPerCh, float *heHalfRate[MAX_CHANNELS]) { SbrContextProcessFrame(hEncoder->sbrContext, hEncoder->numChannels, (int)realPerCh, hEncoder->inputFifo, heHalfRate); } int faacEncEncode(faacEncHandle hpEncoder, int32_t *inputBuffer, unsigned int samplesInput, unsigned char *outputBuffer, unsigned int bufferSize ) { faacEncStruct* hEncoder = (faacEncStruct*)hpEncoder; unsigned int channel; int sb, frameBytes; unsigned int offset; BitStream *bitStream; CoderInfo *coderInfo = hEncoder->coderInfo; unsigned int numChannels = hEncoder->numChannels; unsigned int useTns = hEncoder->config.useTns; unsigned int jointmode = hEncoder->config.jointmode; unsigned int shortctl = hEncoder->config.shortctl; int maxqual = hEncoder->config.outputFormat ? MAXQUALADTS : MAXQUAL; /* The input FIFO decouples the caller's chunk size from the encoder frame * size: append whatever we were handed, then emit at most one frame. A frame * is mult*FRAME_LEN samples/channel (mult==2 for HE-AAC, whose dual-rate core * runs at Fs/2; 1 for LC). While fewer than a full frame is * buffered we just return 0 without touching any per-frame state, so the * encoder behaves identically regardless of the caller's chunk size. */ unsigned int frameSamplesPerCh = faacFrameSamples(hEncoder); int flushing = (samplesInput == 0); int realPerCh; /* real (non-padded) input samples/ch in this frame */ if (samplesInput > 0) if (appendInputFifo(hEncoder, inputBuffer, samplesInput) < 0) return -1; if (hEncoder->inputFifoFill >= frameSamplesPerCh) realPerCh = (int)frameSamplesPerCh; /* full frame ready */ else if (flushing && hEncoder->inputFifoFill > 0) realPerCh = (int)hEncoder->inputFifoFill; /* final partial frame */ else if (flushing) realPerCh = 0; /* drain core lookahead */ else return 0; /* accumulating */ /* Increase frame number */ hEncoder->frameNum++; /* A pure (FIFO-empty) flush frame pushes silence to drain the core's * algorithmic delay; a final partial frame still carries real samples and is * counted like a data frame, matching the pre-FIFO behaviour. */ if (realPerCh == 0) hEncoder->flushFrame++; /* After LOOKAHEAD_DEPTH + 1 flush frames all samples have been encoded, return 0 bytes written */ if (hEncoder->flushFrame > (LOOKAHEAD_DEPTH + 1)) return 0; /* HE-AAC: run SBR + downsample first; the core then encodes heHalfRate. */ float *heHalfRate[MAX_CHANNELS] = {0}; if (realPerCh > 0 && hEncoder->config.aacObjectType == HE_V1 && SbrContextIsPresent(hEncoder->sbrContext)) doHEAACFrame(hEncoder, (unsigned int)realPerCh, heHalfRate); /* Update current sample buffers */ for (channel = 0; channel < numChannels; channel++) { float *tmp; tmp = hEncoder->audioFIFO[channel][FIFO_PAST]; hEncoder->audioFIFO[channel][FIFO_PAST] = hEncoder->audioFIFO[channel][FIFO_CURR]; hEncoder->audioFIFO[channel][FIFO_CURR] = hEncoder->audioFIFO[channel][FIFO_AHEAD1]; hEncoder->audioFIFO[channel][FIFO_AHEAD1] = hEncoder->audioFIFO[channel][FIFO_AHEAD2]; hEncoder->audioFIFO[channel][FIFO_AHEAD2] = tmp; if (realPerCh == 0) { /* start flushing*/ memset(hEncoder->audioFIFO[channel][FIFO_AHEAD2], 0, FRAME_LEN * sizeof(float)); } else if (hEncoder->config.aacObjectType == HE_V1 && heHalfRate[channel]) { /* core feeds on the SBR-downsampled signal, not the raw input */ memcpy(hEncoder->audioFIFO[channel][FIFO_AHEAD2], heHalfRate[channel], FRAME_LEN * sizeof(float)); } else { /* LC: take one frame from the FIFO front (already float), * silence-padding a short final frame. */ unsigned int spc = ((unsigned int)realPerCh < FRAME_LEN) ? (unsigned int)realPerCh : FRAME_LEN; memcpy(hEncoder->audioFIFO[channel][FIFO_AHEAD2], hEncoder->inputFifo[channel], spc * sizeof(float)); if (spc < FRAME_LEN) memset(hEncoder->audioFIFO[channel][FIFO_AHEAD2] + spc, 0, (FRAME_LEN - spc) * sizeof(float)); } /* LFE's block_type is always forced to ONLY_LONG_WINDOW in PsyCalculate, * so the transient analysis below would be discarded -- skip it. */ if (!hEncoder->isLfeChannel[channel]) { /* Shared detector replacement on HE: skip half-rate PsyBufferUpdate. */ if (hEncoder->config.aacObjectType != HE_V1 || !SbrContextIsAnalysisValid(hEncoder->sbrContext)) { PsyBufferUpdate(&hEncoder->gpsyInfo, &hEncoder->psyInfo[channel], hEncoder->audioFIFO[channel][FIFO_AHEAD1], hEncoder->audioFIFO[channel][FIFO_AHEAD2]); } } } /* Drop the consumed frame from the FIFO front (both the LC copy and the * HE doHEAACFrame read the leading frameSamplesPerCh samples). */ if (realPerCh > 0) consumeInputFifo(hEncoder, frameSamplesPerCh); if (hEncoder->frameNum <= LOOKAHEAD_DEPTH) /* Still filling up the buffers */ return 0; /* Psychoacoustics */ /* Shared detector replacement on HE: skip half-rate PsyCalculate. */ if (hEncoder->config.aacObjectType != HE_V1 || !SbrContextIsAnalysisValid(hEncoder->sbrContext)) PsyCalculate(hEncoder->elements, hEncoder->numElements, hEncoder->psyInfo, numChannels); BlockSwitch(hEncoder, coderInfo, hEncoder->psyInfo, numChannels); /* force block type */ if (shortctl == SHORTCTL_NOSHORT) { for (channel = 0; channel < numChannels; channel++) { coderInfo[channel].block_type = ONLY_LONG_WINDOW; } } else if ((hEncoder->frameNum <= (LOOKAHEAD_DEPTH + 1)) || (shortctl == SHORTCTL_NOLONG)) { for (channel = 0; channel < numChannels; channel++) { coderInfo[channel].block_type = ONLY_SHORT_WINDOW; } } /* AAC Filterbank, MDCT with overlap and add */ for (channel = 0; channel < numChannels; channel++) { FilterBank(hEncoder, &coderInfo[channel], hEncoder->audioFIFO[channel][FIFO_PAST], hEncoder->audioFIFO[channel][FIFO_CURR], hEncoder->freqBuff[channel]); } for (channel = 0; channel < numChannels; channel++) { if (coderInfo[channel].block_type == ONLY_SHORT_WINDOW) { coderInfo[channel].sfbn = hEncoder->aacquantCfg.max_cbs; offset = 0; for (sb = 0; sb < coderInfo[channel].sfbn; sb++) { coderInfo[channel].sfb_offset[sb] = offset; offset += hEncoder->srInfo->cb_width_short[sb]; } coderInfo[channel].sfb_offset[sb] = offset; BlocGroup(hEncoder->freqBuff[channel], coderInfo + channel, &hEncoder->aacquantCfg); } else { coderInfo[channel].sfbn = hEncoder->aacquantCfg.max_cbl; coderInfo[channel].groups.n = 1; coderInfo[channel].groups.len[0] = 1; offset = 0; for (sb = 0; sb < coderInfo[channel].sfbn; sb++) { coderInfo[channel].sfb_offset[sb] = offset; offset += hEncoder->srInfo->cb_width_long[sb]; } coderInfo[channel].sfb_offset[sb] = offset; } } /* Perform TNS analysis and filtering */ for (channel = 0; channel < numChannels; channel++) { if (!hEncoder->isLfeChannel[channel] && useTns) { TnsEncode(&(coderInfo[channel].tnsInfo), coderInfo[channel].sfbn, coderInfo[channel].block_type, coderInfo[channel].sfb_offset, hEncoder->freqBuff[channel]); } else { coderInfo[channel].tnsInfo.tnsDataPresent = 0; /* TNS not used for LFE */ } } for (int e = 0; e < hEncoder->numElements; e++) { // reduce LFE bandwidth if (hEncoder->elements[e].type == ID_LFE) { coderInfo[hEncoder->elements[e].channels[0]].sfbn = 3; } } /* Clear each channel's section state before AACstereo pre-loads intensity * bands and BlocQuant resolves the rest. */ for (channel = 0; channel < numChannels; channel++) ResetCoderSections(&coderInfo[channel]); AACstereo(coderInfo, hEncoder->elements, hEncoder->numElements, hEncoder->freqBuff, (float)hEncoder->aacquantCfg.quality/DEFQUAL, jointmode, hEncoder->sampleRate); for (channel = 0; channel < numChannels; channel++) { BlocQuant(&coderInfo[channel], hEncoder->freqBuff[channel], &(hEncoder->aacquantCfg)); } // fix max_sfb in CPE mode for (int e = 0; e < hEncoder->numElements; e++) { if (hEncoder->elements[e].type == ID_CPE) { CoderInfo *cil, *cir; cil = &coderInfo[hEncoder->elements[e].channels[0]]; cir = &coderInfo[hEncoder->elements[e].channels[1]]; cil->sfbn = cir->sfbn = max(cil->sfbn, cir->sfbn); } } /* Write the AAC bitstream */ bitStream = OpenBitStream(bufferSize, outputBuffer); if (!bitStream) return -1; if (WriteBitstream(hEncoder, coderInfo, hEncoder->elements, hEncoder->numElements, bitStream) < 0) return -1; /* Close the bitstream and return the number of bytes written */ frameBytes = CloseBitStream(bitStream); /* Adjust quality to get correct average bitrate */ if (hEncoder->config.bitRate) { int desbits = numChannels * (hEncoder->config.bitRate * FRAME_LEN) / hEncoder->sampleRate; int totalBits = frameBytes * 8; int sbrBits = 0; float fix; /* Exclude SBR's fixed overhead from the core budget so the rate * controller doesn't starve the core to pay for SBR. */ sbrBits = SbrContextGetBits(hEncoder->sbrContext, NULL, (int)numChannels, (int)hEncoder->config.aacObjectType, 0); if (totalBits > sbrBits) fix = (float)(desbits - sbrBits) / (float)(totalBits - sbrBits); else fix = 1.0f; if (fix < (1.0f - RC_DEADBAND_THRESHOLD)) { fix += RC_DEADBAND_THRESHOLD; } else if (fix > (1.0f + RC_DEADBAND_THRESHOLD)) { fix -= RC_DEADBAND_THRESHOLD; } else { fix = 1.0f; } /* Apply damping to the quality adjustment */ fix = (fix - 1.0f) * RC_DAMPING_FACTOR + 1.0f; hEncoder->aacquantCfg.quality *= fix; if (hEncoder->aacquantCfg.quality > maxqual) hEncoder->aacquantCfg.quality = maxqual; if (hEncoder->aacquantCfg.quality < MINQUAL) hEncoder->aacquantCfg.quality = MINQUAL; } return frameBytes; } /* Scalefactorband data table for 1024 transform length */ SR_INFO srInfo[12+1] = { { 96000, 41, 12, { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, 8, 12, 12, 12, 12, 12, 16, 16, 24, 28, 36, 44, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 },{ 4, 4, 4, 4, 4, 4, 8, 8, 8, 16, 28, 36 } }, { 88200, 41, 12, { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, 8, 12, 12, 12, 12, 12, 16, 16, 24, 28, 36, 44, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 },{ 4, 4, 4, 4, 4, 4, 8, 8, 8, 16, 28, 36 } }, { 64000, 47, 12, { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, 12, 12, 12, 16, 16, 16, 20, 24, 24, 28, 36, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40 },{ 4, 4, 4, 4, 4, 4, 8, 8, 8, 16, 28, 32 } }, { 48000, 49, 14, { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, 8, 8, 8, 12, 12, 12, 12, 16, 16, 20, 20, 24, 24, 28, 28, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 96 }, { 4, 4, 4, 4, 4, 8, 8, 8, 12, 12, 12, 16, 16, 16 } }, { 44100, 49, 14, { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, 8, 8, 8, 12, 12, 12, 12, 16, 16, 20, 20, 24, 24, 28, 28, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 96 }, { 4, 4, 4, 4, 4, 8, 8, 8, 12, 12, 12, 16, 16, 16 } }, { 32000, 51, 14, { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, 8, 8, 8, 12, 12, 12, 12, 16, 16, 20, 20, 24, 24, 28, 28, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32 },{ 4, 4, 4, 4, 4, 8, 8, 8, 12, 12, 12, 16, 16, 16 } }, { 24000, 47, 15, { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 12, 12, 12, 12, 16, 16, 16, 20, 20, 24, 24, 28, 28, 32, 36, 36, 40, 44, 48, 52, 52, 64, 64, 64, 64, 64 }, { 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 12, 12, 16, 16, 20 } }, { 22050, 47, 15, { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 12, 12, 12, 12, 16, 16, 16, 20, 20, 24, 24, 28, 28, 32, 36, 36, 40, 44, 48, 52, 52, 64, 64, 64, 64, 64 }, { 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 12, 12, 16, 16, 20 } }, { 16000, 43, 15, { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 12, 12, 12, 12, 12, 12, 12, 12, 12, 16, 16, 16, 16, 20, 20, 20, 24, 24, 28, 28, 32, 36, 40, 40, 44, 48, 52, 56, 60, 64, 64, 64 }, { 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 12, 12, 16, 20, 20 } }, { 12000, 43, 15, { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 12, 12, 12, 12, 12, 12, 12, 12, 12, 16, 16, 16, 16, 20, 20, 20, 24, 24, 28, 28, 32, 36, 40, 40, 44, 48, 52, 56, 60, 64, 64, 64 }, { 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 12, 12, 16, 20, 20 } }, { 11025, 43, 15, { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 12, 12, 12, 12, 12, 12, 12, 12, 12, 16, 16, 16, 16, 20, 20, 20, 24, 24, 28, 28, 32, 36, 40, 40, 44, 48, 52, 56, 60, 64, 64, 64 }, { 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 12, 12, 16, 20, 20 } }, { 8000, 40, 15, { 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 16, 16, 16, 16, 16, 16, 16, 20, 20, 20, 20, 24, 24, 24, 28, 28, 32, 36, 36, 40, 44, 48, 52, 56, 60, 64, 80 }, { 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, 12, 16, 20, 20 } }, { -1, 0, 0, {0}, {0} } }; knik0-faac-1950446/libfaac/frame.h000066400000000000000000000102431522734263700164530ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Copyright (C) 2001 Menno Bakker * * 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. */ #ifndef FRAME_H #define FRAME_H /* Input sample FIFO slots, each one frame (FRAME_LEN samples) wide, relative to the frame currently being coded (FIFO_CURR): one frame behind (FIFO_PAST, reused as the MDCT overlap) and two frames ahead. The two ahead slots are needed because the block-switch energy analysis works on 2-frame-wide windows and keeps one window of lookahead, whose far edge reaches two frames ahead. */ #define LOOKAHEAD_DEPTH 2 #define FIFO_PAST 0 #define FIFO_CURR 1 #define FIFO_AHEAD1 2 #define FIFO_AHEAD2 3 #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "faac_internal.h" #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ #include "coder.h" #include "channels.h" #include "blockswitch.h" #include "fft.h" #include "quantize.h" #include "sbr.h" typedef struct faacEncStruct { /* number of channels in AAC file */ unsigned int numChannels; /* samplerate of AAC file */ unsigned long sampleRate; unsigned int sampleRateIdx; unsigned int usedBytes; /* frame number */ unsigned int frameNum; unsigned int flushFrame; /* Scalefactorband data */ SR_INFO *srInfo; /* sample buffers: FIFO_PAST (MDCT overlap), FIFO_CURR, FIFO_AHEAD1, FIFO_AHEAD2 */ float *audioFIFO[MAX_CHANNELS][4]; /* Filterbank buffers */ float *sin_window_long; float *sin_window_short; float *kbd_window_long; float *kbd_window_short; float *freqBuff[MAX_CHANNELS]; /* Channel and Coder data for all channels */ CoderInfo coderInfo[MAX_CHANNELS]; /* Element-centric configuration */ AACElement elements[MAX_CHANNELS]; int numElements; bool isLfeChannel[MAX_CHANNELS]; /* per-channel LFE lookup, derived from elements[] whenever it changes */ /* Psychoacoustics data */ PsyInfo psyInfo[MAX_CHANNELS]; GlobalPsyInfo gpsyInfo; /* Configuration data */ faacEncConfiguration config; /* quantizer specific config */ AACQuantCfg aacquantCfg; /* FFT Tables */ FFT_Tables fft_tables; /* Input FIFO: decouples the caller's per-call chunk size from the encoder * frame size. faacEncEncode appends whatever it is handed (any count) and * emits one frame once a full frame (mult*FRAME_LEN samples/ch, mult = 2 for * HE-AAC, 1 for LC) has accumulated. Stores format-converted float. */ float *inputFifo[MAX_CHANNELS]; unsigned int inputFifoFill; /* samples per channel currently buffered */ unsigned int inputFifoCap; /* per-channel capacity in samples */ /* faac_* API: AudioSpecificConfig cached on first request and owned by the * handle (freed at close), so faac_encoder_asc() can hand back a pointer * the caller never frees. NULL until first built. */ unsigned char *ascCache; unsigned long ascCacheLen; /* HE-AAC / SBR state */ struct SBRContext *sbrContext; /* SBR analysis state and bitstream data */ } faacEncStruct; /* Configuration worker behind faac_encoder_open(): validates the config, * resolves AUTO/HE-AAC, and (re)initializes the encoder. Returns 1 on success, * 0 on failure. */ int faacEncApplyConfig(faacEncStruct* hEncoder, faacEncConfigurationPtr config); /* Samples/channel per full frame: HE-AAC's core runs dual-rate at Fs/2, so it * needs two FRAME_LENs of input to emit one frame at the full rate; LC needs one. */ static inline unsigned int faacFrameSamples(const faacEncStruct *hEncoder) { return (hEncoder->config.aacObjectType == HE_V1) ? 2 * FRAME_LEN : FRAME_LEN; } #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* FRAME_H */ knik0-faac-1950446/libfaac/huff2.c000066400000000000000000000273001522734263700163700ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Huffman coding per ISO/IEC 14496-3 * Copyright (C) 2026 Nils Schimmelmann * * 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. */ #include #include #include "coder.h" #include "huffdata.h" #include "huff2.h" #include "bitstream.h" #include "util.h" /* Escape coding for HCB_ESC as per ISO/IEC 14496-3. * Represents values |q| >= 16 by sending 16 plus an escape suffix. */ static int escape(int x, int *code) { if (x > MAX_HUFF_ESC_VAL) { fprintf(stderr, "Huffman escape value out of range: %d\n", x); return 0; } int preflen = 31 - CountLeadingZeros(x) - 4; int base = 1 << (preflen + 4); if (code) { /* Unary prefix: preflen 1s followed by a 0 */ *code = (1 << (preflen + 1)) - 2; /* Escape suffix is (preflen+4) bits: base starts at 16 (= 2^4), so the * value field is always at least 4 bits; each additional doubling adds one. */ *code = (*code << (preflen + 4)) | (x - base); } return (preflen + 1) + (preflen + 4); } static hcode16_t * const hmap[12] = { NULL, book01, book02, book03, book04, book05, book06, book07, book08, book09, book10, book11 }; /* Bitwise branchless non-zero check: returns 1 if x != 0, else 0. */ static inline int is_nonzero(int x) { return (int)(((unsigned int)x | (unsigned int)-x) >> 31); } /* Fast bit-length sizing for quantization trials. */ static int huffcode_size(int *qs, int len, int bnum) { hcode16_t *book = hmap[bnum]; int bits = 0; int i; switch (bnum) { case HCB_1: case HCB_2: for (i = 0; i < len; i += 4) { int idx = 40 + DIM_S4*DIM_S4*DIM_S4 * qs[i] + DIM_S4*DIM_S4 * qs[i+1] + DIM_S4 * qs[i+2] + qs[i+3]; bits += book[idx].len; } break; case HCB_3: case HCB_4: for (i = 0; i < len; i += 4) { int idx = DIM_M4*DIM_M4*DIM_M4 * abs(qs[i]) + DIM_M4*DIM_M4 * abs(qs[i+1]) + DIM_M4 * abs(qs[i+2]) + abs(qs[i+3]); bits += book[idx].len; /* Branchless sign-bit counting using bitwise logic */ bits += is_nonzero(qs[i]) + is_nonzero(qs[i+1]) + is_nonzero(qs[i+2]) + is_nonzero(qs[i+3]); } break; case HCB_5: case HCB_6: for (i = 0; i < len; i += 2) { int idx = 40 + DIM_S2 * qs[i] + qs[i+1]; bits += book[idx].len; } break; case HCB_7: case HCB_8: for (i = 0; i < len; i += 2) { int idx = DIM_M2_7 * abs(qs[i]) + abs(qs[i+1]); bits += book[idx].len; bits += is_nonzero(qs[i]) + is_nonzero(qs[i+1]); } break; case HCB_9: case HCB_10: for (i = 0; i < len; i += 2) { int idx = DIM_M2_12 * abs(qs[i]) + abs(qs[i+1]); bits += book[idx].len; bits += is_nonzero(qs[i]) + is_nonzero(qs[i+1]); } break; case HCB_ESC: for (i = 0; i < len; i += 2) { int x0 = abs(qs[i]), x1 = abs(qs[i+1]); int v0 = (x0 > LAV_ESC) ? LAV_ESC : x0; int v1 = (x1 > LAV_ESC) ? LAV_ESC : x1; int idx = DIM_ESC * v0 + v1; bits += book[idx].len; bits += is_nonzero(qs[i]) + is_nonzero(qs[i+1]); if (x0 >= LAV_ESC) bits += escape(x0, NULL); if (x1 >= LAV_ESC) bits += escape(x1, NULL); } break; default: break; } return bits; } /* Bitstream mutation function, called once per finalized frame. */ static void huffcode_write(int *qs, int len, int bnum, CoderInfo *coder) { hcode16_t *book = hmap[bnum]; int i, j; int datacnt = coder->datacnt; switch (bnum) { case HCB_1: case HCB_2: for (i = 0; i < len; i += 4) { int idx = 40 + DIM_S4*DIM_S4*DIM_S4 * qs[i] + DIM_S4*DIM_S4 * qs[i+1] + DIM_S4 * qs[i+2] + qs[i+3]; coder->s[datacnt].data = book[idx].data; coder->s[datacnt++].len = book[idx].len; } break; case HCB_3: case HCB_4: for (i = 0; i < len; i += 4) { int idx = DIM_M4*DIM_M4*DIM_M4 * abs(qs[i]) + DIM_M4*DIM_M4 * abs(qs[i+1]) + DIM_M4 * abs(qs[i+2]) + abs(qs[i+3]); int blen = book[idx].len; int data = book[idx].data; for (j = 0; j < 4; j++) { if (qs[i+j]) { blen++; data = (data << 1) | (qs[i+j] < 0); } } coder->s[datacnt].data = data; coder->s[datacnt++].len = blen; } break; case HCB_5: case HCB_6: for (i = 0; i < len; i += 2) { int idx = 40 + DIM_S2 * qs[i] + qs[i+1]; coder->s[datacnt].data = book[idx].data; coder->s[datacnt++].len = book[idx].len; } break; case HCB_7: case HCB_8: for (i = 0; i < len; i += 2) { int idx = DIM_M2_7 * abs(qs[i]) + abs(qs[i+1]); int blen = book[idx].len; int data = book[idx].data; for (j = 0; j < 2; j++) { if (qs[i+j]) { blen++; data = (data << 1) | (qs[i+j] < 0); } } coder->s[datacnt].data = data; coder->s[datacnt++].len = blen; } break; case HCB_9: case HCB_10: for (i = 0; i < len; i += 2) { int idx = DIM_M2_12 * abs(qs[i]) + abs(qs[i+1]); int blen = book[idx].len; int data = book[idx].data; for (j = 0; j < 2; j++) { if (qs[i+j]) { blen++; data = (data << 1) | (qs[i+j] < 0); } } coder->s[datacnt].data = data; coder->s[datacnt++].len = blen; } break; case HCB_ESC: for (i = 0; i < len; i += 2) { int x0 = abs(qs[i]), x1 = abs(qs[i+1]); int v0 = (x0 > LAV_ESC) ? LAV_ESC : x0; int v1 = (x1 > LAV_ESC) ? LAV_ESC : x1; int idx = DIM_ESC * v0 + v1; int blen = book[idx].len; int data = book[idx].data; if (qs[i]) { blen++; data = (data << 1) | (qs[i] < 0); } if (qs[i+1]) { blen++; data = (data << 1) | (qs[i+1] < 0); } coder->s[datacnt].data = data; coder->s[datacnt++].len = blen; if (x0 >= LAV_ESC) { int esc_code = 0; int esc_len = escape(x0, &esc_code); coder->s[datacnt].data = esc_code; coder->s[datacnt++].len = esc_len; } if (x1 >= LAV_ESC) { int esc_code = 0; int esc_len = escape(x1, &esc_code); coder->s[datacnt].data = esc_code; coder->s[datacnt++].len = esc_len; } } break; default: break; } coder->datacnt = datacnt; } /* Pick the codebook that minimizes the bit cost for a given band. */ int huffbook(CoderInfo *coder, int *qs, int len) { int i, maxq = 0; int bookmin = HCB_ZERO, lenmin = 0; for (i = 0; i < len; i++) { int q = abs(qs[i]); if (maxq < q) maxq = q; } if (maxq > 0) { /* Each spectral book covers values up to its LAV; select the range-pair * whose lower book just fits maxq, then pick the partner if it costs fewer * bits — both books in a pair cover the same amplitude range but use * different codeword assignments optimized for different spectral shapes. */ int pair_base; if (maxq <= LAV_1) pair_base = HCB_1; else if (maxq <= LAV_2) pair_base = HCB_3; else if (maxq <= LAV_4) pair_base = HCB_5; else if (maxq <= LAV_7) pair_base = HCB_7; else if (maxq <= LAV_12) pair_base = HCB_9; else pair_base = HCB_ESC; if (pair_base != HCB_ESC) { bookmin = pair_base; lenmin = huffcode_size(qs, len, bookmin); int len2 = huffcode_size(qs, len, bookmin + 1); if (len2 < lenmin) bookmin++; } else { bookmin = HCB_ESC; } huffcode_write(qs, len, bookmin, coder); } /* Record the chosen book at the current band slot, but do NOT advance bandcnt: the caller (BlocQuant in quantize.c) owns that increment after it has also stored the band's scalefactor. */ coder->book[coder->bandcnt] = bookmin; return 0; } /* Encode the section data (codebook indices and run lengths). */ int writebooks(CoderInfo *coder, BitStream *stream, int write) { int bits = 0; /* Section run field is 3 bits for short windows (max 7 windows/section) and * 5 bits for long windows (max 31 bands/section) — ISO 14496-3 §4.6.8.2. */ int max_run = (coder->block_type == ONLY_SHORT_WINDOW) ? 7 : 31; int run_bits = (coder->block_type == ONLY_SHORT_WINDOW) ? 3 : 5; int g; BitAccumulator acc = {0}; if (write) AccumBegin(&acc, stream); for (g = 0; g < coder->groups.n; g++) { int b = g * coder->sfbn; int end = b + coder->sfbn; while (b < end) { int book = coder->book[b]; int run = 0; while (b + run < end && coder->book[b + run] == book) run++; b += run; if (write) AccumPutBits(&acc, (uint32_t)book, 4); bits += 4; while (run >= max_run) { if (write) AccumPutBits(&acc, (uint32_t)max_run, run_bits); bits += run_bits; run -= max_run; } if (write) AccumPutBits(&acc, (uint32_t)run, run_bits); bits += run_bits; } } if (write) AccumEnd(&acc); return bits; } /* Encode scalefactor deltas using HCB_DELTA (book12). */ int writesf(CoderInfo *coder, BitStream *stream, int write) { int i, bits = 0; int lastsf = coder->global_gain; int lastis = 0; int lastpns = coder->global_gain - SF_PNS_OFFSET; int is_first_pns = 1; BitAccumulator acc = {0}; if (write) AccumBegin(&acc, stream); for (i = 0; i < coder->bandcnt; i++) { int book = coder->book[i]; int val = coder->sf[i]; int diff, code, len; if (book == HCB_ZERO || book == HCB_NONE) continue; if (book == HCB_INTENSITY || book == HCB_INTENSITY2) { diff = clamp_sf_diff(val - lastis); lastis += diff; } else if (book == HCB_PNS) { diff = val - lastpns; if (is_first_pns) { /* First PNS band is coded as an absolute 9-bit value (biased by 256) * because there is no prior PNS entry to delta from yet. */ if (write) AccumPutBits(&acc, (uint32_t)(diff + 256), 9); bits += 9; lastpns = val; is_first_pns = 0; continue; } diff = clamp_sf_diff(diff); lastpns += diff; } else { diff = clamp_sf_diff(val - lastsf); lastsf += diff; } code = book12[SF_DELTA + diff].data; len = book12[SF_DELTA + diff].len; if (write) AccumPutBits(&acc, (uint32_t)code, len); bits += len; } if (write) AccumEnd(&acc); return bits; } knik0-faac-1950446/libfaac/huff2.h000066400000000000000000000070251522734263700163770ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Huffman coding per ISO/IEC 14496-3 * Copyright (C) 2026 Nils Schimmelmann * * 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. */ #ifndef HUFF2_H #define HUFF2_H #include "bitstream.h" /* Huffman Codebooks */ enum { HCB_ZERO = 0, HCB_1, HCB_2, HCB_3, HCB_4, HCB_5, HCB_6, HCB_7, HCB_8, HCB_9, HCB_10, HCB_ESC = 11, HCB_DELTA = 12, /* scalefactor-delta book (book12) */ HCB_PNS = 13, HCB_INTENSITY2 = 14, HCB_INTENSITY = 15, HCB_NONE }; /* Spectral books come in range-pairs {base, base+1} sharing a tuple shape but a * different codeword table; the pair's reach is its Largest Absolute Value. */ enum { LAV_1 = 1, /* HCB_1, HCB_2 */ LAV_2 = 2, /* HCB_3, HCB_4 */ LAV_4 = 4, /* HCB_5, HCB_6 */ LAV_7 = 7, /* HCB_7, HCB_8 */ LAV_12 = 12, /* HCB_9, HCB_10 */ LAV_ESC = 16 /* HCB_ESC: |q| >= 16 spills into an escape suffix */ }; /* Radix of each book's positional codeword index (so the magic 27/9/13/17 read * as the powers they are). 4-tuples pack as radix^3..^0, 2-tuples as radix^1..^0. */ enum { DIM_S4 = 3, /* HCB_1, HCB_2: signed 4-tuple, 3^4 = 81 */ DIM_M4 = 3, /* HCB_3, HCB_4: mag 4-tuple, 3^4 = 81 */ DIM_S2 = 9, /* HCB_5, HCB_6: signed 2-tuple, 9^2 = 81 */ DIM_M2_7 = 8, /* HCB_7, HCB_8: mag 2-tuple, 8^2 = 64 */ DIM_M2_12 = 13, /* HCB_9, HCB_10: mag 2-tuple, 13^2 = 169 */ DIM_ESC = 17 /* HCB_ESC: mag 2-tuple, 17^2 = 289 */ }; /* Maximum value representable by HCB_ESC escape sequences. * Values >= 8192 would cause bitstream overflow/sync loss. */ #define MAX_HUFF_ESC_VAL 8191 /* Scalefactor step constants (ISO 14496-3 §8.3.4): one SF unit = 2^(1/4) in amplitude. * AMPL converts a log10 amplitude ratio to scalefactor index steps (= 4/log10(2)). * ENRG is AMPL/2 — use when the input ratio is energy (squared amplitude). */ #define SF_STEP_AMPL 13.287712379549461f #define SF_STEP_ENRG (SF_STEP_AMPL / 2.0f) /* Scalefactor Management */ enum { /* Baseline scalefactor value used in bitstream */ SF_OFFSET = 100, /* Minimum allowable scalefactor to prevent underflow */ SF_MIN = 10, /* PNS predictor initialization offset (starts at floor) */ SF_PNS_OFFSET = SF_OFFSET - SF_MIN, /* Max allowed difference between successive scalefactors (AAC spec) */ SF_DELTA = 60, /* Max absolute scalefactor / global_gain (8-bit bitstream field) */ SF_MAX_ABS = 255, }; /** * Restrict scalefactor delta to the spec-defined +/- SF_DELTA range. * This ensures the delta remains valid for HCB_DELTA Huffman encoding. */ static inline int clamp_sf_diff(int diff) { if (diff > SF_DELTA) return SF_DELTA; if (diff < -SF_DELTA) return -SF_DELTA; return diff; } /* Forward declaration for CoderInfo */ struct CoderInfo; int huffbook(struct CoderInfo *coder, int *qs, int len); int writebooks(struct CoderInfo *coder, BitStream *stream, int writeFlag); int writesf(struct CoderInfo *coder, BitStream *bitStream, int writeFlag); #endif /* HUFF2_H */ knik0-faac-1950446/libfaac/huffdata.c000066400000000000000000000317471522734263700171520ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Huffman codebook tables reproduced from ISO/IEC 14496-3 (non-copyrightable facts) * * 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. */ #include "huffdata.h" hcode16_t book01[81] = { {11,2040},{9,497},{11,2045},{10,1013},{7,104},{10,1008},{11,2039},{9,492}, {11,2037},{10,1009},{7,114},{10,1012},{7,116},{5,17},{7,118},{9,491}, {7,108},{10,1014},{11,2044},{9,481},{11,2033},{9,496},{7,97},{9,502}, {11,2034},{9,490},{11,2043},{9,498},{7,105},{9,493},{7,119},{5,23}, {7,111},{9,486},{7,100},{9,485},{7,103},{5,21},{7,98},{5,18}, {1,0},{5,20},{7,101},{5,22},{7,109},{9,489},{7,99},{9,484}, {7,107},{5,19},{7,113},{9,483},{7,112},{9,499},{11,2046},{9,487}, {11,2035},{9,495},{7,96},{9,494},{11,2032},{9,482},{11,2042},{10,1011}, {7,106},{9,488},{7,117},{5,16},{7,115},{9,500},{7,110},{10,1015}, {11,2038},{9,480},{11,2041},{10,1010},{7,102},{9,501},{11,2047},{9,503}, {11,2036}, }; hcode16_t book02[81] = { {9,499},{7,111},{9,509},{8,235},{6,35},{8,234},{9,503},{8,232}, {9,506},{8,242},{6,45},{7,112},{6,32},{5,6},{6,43},{7,110}, {6,40},{8,233},{9,505},{7,102},{8,248},{8,231},{6,27},{8,241}, {9,500},{7,107},{9,501},{8,236},{6,42},{7,108},{6,44},{5,10}, {6,39},{7,103},{6,26},{8,245},{6,36},{5,8},{6,31},{5,9}, {3,0},{5,7},{6,29},{5,11},{6,48},{8,239},{6,28},{7,100}, {6,30},{5,12},{6,41},{8,243},{6,47},{8,240},{9,508},{7,113}, {9,498},{8,244},{6,33},{8,230},{8,247},{7,104},{9,504},{8,238}, {6,34},{7,101},{6,49},{4,2},{6,38},{8,237},{6,37},{7,106}, {9,507},{7,114},{9,510},{7,105},{6,46},{8,246},{9,511},{7,109}, {9,502}, }; hcode16_t book03[81] = { {1,0},{4,9},{8,239},{4,11},{5,25},{8,240},{9,491},{9,486}, {10,1010},{4,10},{6,53},{9,495},{6,52},{6,55},{9,489},{9,493}, {9,487},{10,1011},{9,494},{10,1005},{13,8186},{9,492},{9,498},{11,2041}, {11,2040},{10,1016},{12,4088},{4,8},{6,56},{10,1014},{6,54},{7,117}, {10,1009},{10,1003},{10,1004},{12,4084},{5,24},{7,118},{11,2036},{6,57}, {7,116},{10,1007},{9,499},{9,500},{11,2038},{9,488},{10,1002},{13,8188}, {8,242},{9,497},{12,4091},{10,1013},{11,2035},{12,4092},{8,238},{10,1015}, {15,32766},{9,496},{11,2037},{15,32765},{13,8187},{14,16378},{16,65535},{8,241}, {10,1008},{14,16380},{9,490},{10,1006},{14,16379},{12,4086},{12,4090},{15,32764}, {11,2034},{12,4085},{16,65534},{10,1012},{11,2039},{15,32763},{12,4087},{12,4089}, {15,32762}, }; hcode16_t book04[81] = { {4,7},{5,22},{8,246},{5,24},{4,8},{8,239},{9,495},{8,243}, {11,2040},{5,25},{5,23},{8,237},{5,21},{4,1},{8,226},{8,240}, {7,112},{10,1008},{9,494},{8,241},{11,2042},{8,238},{8,228},{10,1010}, {11,2038},{10,1007},{11,2045},{4,5},{5,20},{8,242},{4,9},{4,4}, {8,229},{8,244},{8,232},{10,1012},{4,6},{4,2},{8,231},{4,3}, {4,0},{7,107},{8,227},{7,105},{9,499},{8,235},{8,230},{10,1014}, {7,110},{7,106},{9,500},{10,1004},{9,496},{10,1017},{8,245},{8,236}, {11,2043},{8,234},{7,111},{10,1015},{11,2041},{10,1011},{12,4095},{8,233}, {7,109},{10,1016},{7,108},{7,104},{9,501},{10,1006},{9,498},{11,2036}, {11,2039},{10,1009},{12,4094},{10,1005},{9,497},{11,2037},{11,2046},{10,1013}, {11,2044}, }; hcode16_t book05[81] = { {13,8191},{12,4087},{11,2036},{11,2024},{10,1009},{11,2030},{11,2041},{12,4088}, {13,8189},{12,4093},{11,2033},{10,1000},{9,488},{8,240},{9,492},{10,1006}, {11,2034},{12,4090},{12,4084},{10,1007},{9,498},{8,232},{7,112},{8,236}, {9,496},{10,1002},{11,2035},{11,2027},{9,491},{8,234},{5,26},{4,8}, {5,25},{8,238},{9,495},{11,2029},{10,1008},{8,242},{7,115},{4,11}, {1,0},{4,10},{7,113},{8,243},{11,2025},{11,2031},{9,494},{8,239}, {5,24},{4,9},{5,27},{8,235},{9,489},{11,2028},{11,2038},{10,1003}, {9,499},{8,237},{7,114},{8,233},{9,497},{10,1005},{11,2039},{12,4086}, {11,2032},{10,1001},{9,493},{8,241},{9,490},{10,1004},{11,2040},{12,4089}, {13,8188},{12,4092},{12,4085},{11,2026},{10,1011},{10,1010},{11,2037},{12,4091}, {13,8190}, }; hcode16_t book06[81] = { {11,2046},{10,1021},{9,497},{9,491},{9,500},{9,490},{9,496},{10,1020}, {11,2045},{10,1014},{9,485},{8,234},{7,108},{7,113},{7,104},{8,240}, {9,486},{10,1015},{9,499},{8,239},{6,50},{6,39},{6,40},{6,38}, {6,49},{8,235},{9,503},{9,488},{7,111},{6,46},{4,8},{4,4}, {4,6},{6,41},{7,107},{9,494},{9,495},{7,114},{6,45},{4,2}, {4,0},{4,3},{6,47},{7,115},{9,506},{9,487},{7,110},{6,43}, {4,7},{4,1},{4,5},{6,44},{7,109},{9,492},{9,505},{8,238}, {6,48},{6,36},{6,42},{6,37},{6,51},{8,236},{9,498},{10,1016}, {9,484},{8,237},{7,106},{7,112},{7,105},{7,116},{8,241},{10,1018}, {11,2047},{10,1017},{9,502},{9,493},{9,504},{9,489},{9,501},{10,1019}, {11,2044}, }; hcode16_t book07[64] = { {1,0},{3,5},{6,55},{7,116},{8,242},{9,491},{10,1005},{11,2039}, {3,4},{4,12},{6,53},{7,113},{8,236},{8,238},{9,494},{9,501}, {6,54},{6,52},{7,114},{8,234},{8,241},{9,489},{9,499},{10,1013}, {7,115},{7,112},{8,235},{8,240},{9,497},{9,496},{10,1004},{10,1018}, {8,243},{8,237},{9,488},{9,495},{10,1007},{10,1009},{10,1017},{11,2043}, {9,493},{8,239},{9,490},{9,498},{10,1011},{10,1016},{11,2041},{11,2044}, {10,1006},{9,492},{9,500},{10,1012},{10,1015},{11,2040},{12,4093},{12,4094}, {11,2038},{10,1008},{10,1010},{10,1014},{11,2042},{11,2045},{12,4092},{12,4095}, }; hcode16_t book08[64] = { {5,14},{4,5},{5,16},{6,48},{7,111},{8,241},{9,506},{10,1022}, {4,3},{3,0},{4,4},{5,18},{6,44},{7,106},{7,117},{8,248}, {5,15},{4,2},{4,6},{5,20},{6,46},{7,105},{7,114},{8,245}, {6,47},{5,17},{5,19},{6,42},{6,50},{7,108},{8,236},{8,250}, {7,113},{6,43},{6,45},{6,49},{7,109},{7,112},{8,242},{9,505}, {8,239},{7,104},{6,51},{7,107},{7,110},{8,238},{8,249},{10,1020}, {9,504},{7,116},{7,115},{8,237},{8,240},{8,246},{9,502},{9,509}, {10,1021},{8,243},{8,244},{8,247},{9,503},{9,507},{9,508},{10,1023}, }; hcode16_t book09[169] = { {1,0},{3,5},{6,55},{8,231},{9,478},{10,974},{10,985},{11,1992}, {11,1997},{12,4040},{12,4061},{13,8164},{13,8172},{3,4},{4,12},{6,53}, {7,114},{8,234},{8,237},{9,482},{10,977},{10,979},{10,992},{11,2008}, {12,4047},{12,4053},{6,54},{6,52},{7,113},{8,232},{8,236},{9,481}, {10,975},{10,989},{10,987},{11,2000},{12,4039},{12,4052},{12,4068},{8,230}, {7,112},{8,233},{9,477},{9,483},{10,978},{10,988},{11,1996},{11,1994}, {11,2014},{12,4056},{12,4074},{13,8155},{9,479},{8,235},{9,476},{9,486}, {10,981},{10,990},{11,1995},{11,2013},{11,2012},{12,4045},{12,4066},{12,4071}, {13,8161},{10,976},{9,480},{9,484},{10,982},{11,1989},{11,2001},{11,2011}, {12,4050},{11,2016},{12,4057},{12,4075},{13,8163},{13,8169},{11,1988},{9,485}, {10,983},{11,1990},{11,1999},{11,2010},{12,4043},{12,4058},{12,4067},{12,4073}, {13,8166},{13,8179},{13,8183},{11,2003},{10,984},{10,993},{11,2004},{11,2009}, {12,4051},{12,4062},{13,8157},{13,8153},{13,8162},{13,8170},{13,8177},{13,8182}, {11,2002},{10,980},{10,986},{11,1991},{11,2007},{11,2018},{12,4046},{12,4059}, {13,8152},{13,8174},{14,16368},{13,8180},{14,16370},{11,2017},{10,991},{11,1993}, {11,2006},{12,4042},{12,4048},{12,4069},{12,4070},{13,8171},{13,8175},{14,16371}, {14,16372},{14,16373},{12,4064},{11,1998},{11,2005},{12,4038},{12,4049},{12,4065}, {13,8160},{13,8168},{13,8176},{14,16369},{14,16376},{14,16374},{15,32764},{12,4072}, {11,2015},{12,4041},{12,4055},{12,4060},{13,8156},{13,8159},{13,8173},{13,8181}, {14,16377},{14,16379},{15,32765},{15,32766},{13,8167},{12,4044},{12,4054},{12,4063}, {13,8158},{13,8154},{13,8165},{13,8178},{14,16378},{14,16375},{14,16380},{14,16381}, {15,32767}, }; hcode16_t book10[169] = { {6,34},{5,8},{6,29},{6,38},{7,95},{8,211},{9,463},{10,976}, {10,983},{10,1005},{11,2032},{11,2038},{12,4093},{5,7},{4,0},{4,1}, {5,9},{6,32},{7,84},{7,96},{8,213},{8,220},{9,468},{10,973}, {10,990},{11,2023},{6,28},{4,2},{5,6},{5,12},{6,30},{6,40}, {7,91},{8,205},{8,217},{9,462},{9,476},{10,985},{10,1009},{6,37}, {5,11},{5,10},{5,13},{6,36},{7,87},{7,97},{8,204},{8,221}, {9,460},{9,478},{10,979},{10,999},{7,93},{6,33},{6,31},{6,35}, {6,39},{7,89},{7,100},{8,216},{8,223},{9,466},{9,482},{10,989}, {10,1006},{8,209},{7,85},{6,41},{7,86},{7,88},{7,98},{8,206}, {8,224},{8,226},{9,474},{10,980},{10,995},{11,2027},{9,457},{7,94}, {7,90},{7,92},{7,99},{8,202},{8,218},{9,455},{9,458},{9,480}, {10,987},{10,1000},{11,2028},{9,483},{8,210},{8,203},{8,208},{8,215}, {8,219},{9,454},{9,469},{9,472},{10,970},{10,986},{11,2026},{11,2033}, {9,481},{8,212},{8,207},{8,214},{8,222},{8,225},{9,464},{9,470}, {10,977},{10,981},{10,1010},{11,2030},{11,2043},{10,1001},{9,461},{9,456}, {9,459},{9,465},{9,471},{9,479},{10,975},{10,992},{10,1007},{11,2022}, {11,2040},{12,4090},{10,1003},{9,477},{9,467},{9,473},{9,475},{10,978}, {10,972},{10,988},{10,1002},{11,2029},{11,2035},{11,2041},{12,4089},{11,2034}, {10,974},{9,484},{10,971},{10,984},{10,982},{10,994},{10,997},{11,2024}, {11,2036},{11,2037},{11,2039},{12,4091},{11,2042},{10,1004},{10,991},{10,993}, {10,996},{10,998},{10,1008},{11,2025},{11,2031},{12,4088},{12,4094},{12,4092}, {12,4095}, }; hcode16_t book11[289] = { {4,0},{5,6},{6,25},{7,61},{8,156},{8,198},{9,423},{10,912}, {10,962},{10,991},{11,2022},{11,2035},{12,4091},{11,2028},{12,4090},{12,4094}, {10,910},{5,5},{4,1},{5,8},{6,20},{7,55},{7,66},{8,146}, {8,175},{9,401},{9,421},{9,437},{10,926},{10,960},{10,930},{10,973}, {11,2006},{8,174},{6,23},{5,7},{5,9},{6,24},{7,57},{7,64}, {8,142},{8,163},{8,184},{9,409},{9,428},{9,449},{10,945},{10,918}, {10,958},{10,970},{8,157},{7,60},{6,21},{6,22},{6,26},{7,59}, {7,68},{8,145},{8,165},{8,190},{9,406},{9,430},{9,441},{10,929}, {10,913},{10,933},{10,981},{8,148},{8,154},{7,54},{7,56},{7,58}, {7,65},{8,140},{8,155},{8,176},{8,195},{9,414},{9,427},{9,444}, {10,927},{10,911},{10,937},{10,975},{8,147},{8,191},{7,62},{7,63}, {7,67},{7,69},{8,158},{8,167},{8,185},{9,404},{9,418},{9,442}, {9,451},{10,934},{10,935},{10,955},{10,980},{8,159},{9,416},{8,143}, {8,141},{8,144},{8,152},{8,166},{8,182},{8,196},{9,415},{9,431}, {9,447},{10,921},{10,959},{10,948},{10,969},{10,999},{8,168},{9,438}, {8,171},{8,164},{8,170},{8,178},{8,194},{8,197},{9,408},{9,420}, {9,440},{10,908},{10,932},{10,964},{10,966},{10,989},{10,1000},{8,173}, {10,943},{9,402},{8,189},{8,188},{9,398},{9,407},{9,410},{9,419}, {9,433},{10,909},{10,920},{10,951},{10,979},{10,977},{10,987},{11,2013}, {8,180},{10,990},{9,425},{9,411},{9,412},{9,417},{9,426},{9,429}, {9,435},{10,907},{10,946},{10,952},{10,974},{10,993},{10,992},{11,2002}, {11,2021},{8,183},{11,2019},{9,443},{9,424},{9,422},{9,432},{9,434}, {9,439},{10,923},{10,922},{10,954},{10,949},{10,982},{11,2007},{10,996}, {11,2008},{11,2026},{8,186},{11,2024},{10,928},{9,445},{9,436},{10,906}, {9,452},{10,914},{10,938},{10,944},{10,956},{10,983},{11,2004},{11,2012}, {11,2011},{11,2005},{11,2032},{8,193},{11,2043},{10,968},{10,931},{10,917}, {10,925},{10,940},{10,942},{10,965},{10,984},{10,994},{10,998},{11,2020}, {11,2023},{11,2016},{11,2025},{11,2039},{9,400},{11,2034},{10,915},{9,446}, {9,448},{10,916},{10,919},{10,941},{10,963},{10,961},{10,978},{11,2010}, {11,2009},{11,2015},{11,2027},{11,2036},{11,2042},{9,405},{11,2040},{10,957}, {10,924},{10,939},{10,936},{10,947},{10,953},{10,976},{10,995},{10,997}, {11,2018},{11,2014},{11,2029},{11,2033},{11,2041},{11,2044},{9,403},{12,4093}, {10,988},{10,950},{10,967},{10,972},{10,971},{10,985},{10,986},{11,2003}, {11,2017},{11,2030},{11,2031},{11,2037},{11,2038},{12,4092},{12,4095},{9,413}, {9,450},{8,181},{8,161},{8,150},{8,151},{8,149},{8,153},{8,160}, {8,162},{8,172},{8,169},{8,177},{8,179},{8,187},{8,192},{9,399}, {5,4}, }; hcode32_t book12[2 * SF_DELTA + 1] = { {18,262120},{18,262118},{18,262119},{18,262117},{19,524277},{19,524273},{19,524269},{19,524278}, {19,524270},{19,524271},{19,524272},{19,524284},{19,524285},{19,524287},{19,524286},{19,524279}, {19,524280},{19,524283},{19,524281},{18,262116},{19,524282},{18,262115},{17,131055},{17,131056}, {16,65525},{17,131054},{16,65522},{16,65523},{16,65524},{16,65521},{15,32758},{15,32759}, {14,16377},{14,16373},{14,16375},{14,16371},{14,16374},{14,16370},{13,8183},{13,8181}, {12,4089},{12,4087},{12,4086},{11,2041},{12,4084},{11,2040},{10,1017},{10,1015}, {10,1013},{9,504},{9,503},{8,250},{8,248},{8,246},{7,121},{6,58}, {6,56},{5,26},{4,11},{3,4},{1,0},{4,10},{4,12},{5,27}, {6,57},{6,59},{7,120},{7,122},{8,247},{8,249},{9,502},{9,505}, {10,1012},{10,1014},{10,1016},{11,2037},{11,2036},{11,2038},{11,2039},{12,4085}, {12,4088},{13,8180},{13,8182},{13,8184},{14,16376},{14,16372},{16,65520},{15,32756}, {16,65526},{15,32757},{18,262114},{19,524249},{19,524250},{19,524251},{19,524252},{19,524253}, {19,524254},{19,524248},{19,524242},{19,524243},{19,524244},{19,524245},{19,524246},{19,524274}, {19,524255},{19,524263},{19,524264},{19,524265},{19,524266},{19,524267},{19,524262},{19,524256}, {19,524257},{19,524258},{19,524259},{19,524260},{19,524261},{19,524247},{19,524268},{19,524276}, {19,524275}, }; knik0-faac-1950446/libfaac/huffdata.h000066400000000000000000000025101522734263700171410ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Huffman codebook tables reproduced from ISO/IEC 14496-3 (non-copyrightable facts) * * 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. */ #ifndef HUFFDATA_H #define HUFFDATA_H #include "huff2.h" #include typedef struct { const uint16_t len; const uint16_t data; } hcode16_t; typedef struct { const uint32_t len : 8; /* lengths <= 19 */ const uint32_t data : 24; /* codes are <= 19 bits */ } hcode32_t; extern hcode16_t book01[81]; extern hcode16_t book02[81]; extern hcode16_t book03[81]; extern hcode16_t book04[81]; extern hcode16_t book05[81]; extern hcode16_t book06[81]; extern hcode16_t book07[64]; extern hcode16_t book08[64]; extern hcode16_t book09[169]; extern hcode16_t book10[169]; extern hcode16_t book11[289]; extern hcode32_t book12[2 * SF_DELTA + 1]; #endif /* HUFFDATA_H */ knik0-faac-1950446/libfaac/libfaac.def000066400000000000000000000005121522734263700172470ustar00rootroot00000000000000EXPORTS ; ; libfaac public API (faac.h) ; faac_get_library_info @1 faac_params_init @2 faac_encoder_open @3 faac_encoder_close @4 faac_encoder_get_info @5 faac_encoder_asc @6 faac_encoder_encode @7 faac_strerror @8 knik0-faac-1950446/libfaac/meson.build000066400000000000000000000050761522734263700173620ustar00rootroot00000000000000link_args = [] if host_machine.system() == 'windows' and meson.get_compiler('c').get_id() == 'gcc' # identifies mingw link_args += '-Wl,--add-stdcall-alias' endif quantize_simd_libs = [] if config_h.get('HAVE_SSE2') sse2_flag = (cc.get_argument_syntax() == 'msvc') ? ['/arch:SSE2'] : ['-msse2'] quantize_sse = static_library('quantize_sse', 'quantize_sse.c', include_directories: ['..', '../include'], c_args: c_args + sse2_flag, gnu_symbol_visibility: 'hidden' ) quantize_simd_libs += quantize_sse endif common_src = [ 'bitstream.c', 'bitstream.h', 'blockswitch.c', 'blockswitch.h', 'channels.c', 'channels.h', 'coder.h', 'cpu_compute.c', 'cpu_compute.h', 'faac.c', 'faac_internal.h', 'filtbank.c', 'filtbank.h', 'fft.c', 'fft.h', 'frame.c', 'frame.h', 'huff2.c', 'huff2.h', 'huffdata.c', 'huffdata.h', 'quantize.c', 'quantize.h', 'sbr.c', 'sbr.h', 'sbr_internal.h', 'sbr_bitstream.c', 'sbr_tables.c', 'sbr_tables.h', 'sbr_analysis.c', 'sbr_analysis.h', 'resample.c', 'resample.h', 'stereo.c', 'stereo.h', 'tns.c', 'tns.h', 'util.c', 'util.h', ] libfaac = disabler() common_args = { 'include_directories' : ['..', '../include'], 'c_args' : c_args, 'link_args' : link_args, 'dependencies' : [libm], 'link_with' : quantize_simd_libs, 'install' : true, 'gnu_symbol_visibility' : 'hidden', } default_lib = get_option('default_library') # LTO on the static archive ships unportable compiler bytecode, so keep it off. if default_lib == 'static' or default_lib == 'both' libfaac_static = static_library( 'faac', common_src, kwargs : common_args + { 'override_options' : ['b_lto=false'], }, ) endif if default_lib == 'shared' or default_lib == 'both' libfaac_shared = shared_library( 'faac', common_src, kwargs : common_args + { # Keep in sync with FAAC_VERSION_MAJOR/MINOR/PATCH in include/faac.h. 'version' : '1.0.0', 'vs_module_defs' : 'libfaac.def', 'override_options' : ['b_lto=true'], }, ) endif # Prefer the LTO shared library for the frontend and pkgconfig. if default_lib == 'static' libfaac = libfaac_static else libfaac = libfaac_shared endif pkgconfig = import('pkgconfig') pkgconfig.generate( libfaac, name: 'FAAC', description: 'Freeware Advanced Audio Coder', version: meson.project_version(), filebase: 'faac' ) knik0-faac-1950446/libfaac/quantize.c000066400000000000000000000346131522734263700172230ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Copyright (C) 2026 Nils Schimmelmann * * 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. */ #include #include #include #include #include #include "quantize.h" #include "huff2.h" #include "cpu_compute.h" typedef void (*QuantizeFunc)(const float * __restrict xr, int * __restrict xi, int n, float sfacfix); #if defined(HAVE_SSE2) extern void quantize_sse2(const float * __restrict xr, int * __restrict xi, int n, float sfacfix); #endif static void quantize_scalar(const float * __restrict xr, int * __restrict xi, int n, float sfacfix) { const float magic = MAGIC_NUMBER; int i; for (i = 0; i < n; i++) { float val = xr[i]; float tmp = fabsf(val) * sfacfix; tmp = sqrtf(tmp * sqrtf(tmp)); int q = (int)(tmp + magic); xi[i] = (val < 0) ? -q : q; } } static QuantizeFunc qfunc = quantize_scalar; static float sfstep; static float max_quant_limit; #define SF_CHAIN_UNSET INT_MIN void QuantizeInit(void) { #if defined(HAVE_SSE2) CPUCaps caps = get_cpu_caps(); if (caps & CPU_CAP_SSE2) qfunc = quantize_sse2; else #endif qfunc = quantize_scalar; sfstep = SF_STEP_AMPL; /* One-time constant: computed in double so the stored float is * correctly rounded, at zero runtime cost. */ max_quant_limit = (float)pow((double)MAX_HUFF_ESC_VAL + 1.0 - (double)MAGIC_NUMBER, 4.0/3.0); } /* sfac and gain are coupled; clamping one forces a recompute of the other. */ static float gain_with_overflow_clamp(int *sfac, float band_peak) { float gain = powf(10, *sfac / sfstep); if (band_peak > 0.0f && gain * band_peak > max_quant_limit) { gain = max_quant_limit / band_peak; *sfac = (int)floorf(log10f(gain) * sfstep); gain = powf(10, *sfac / sfstep); } return gain; } // masking target per scalefactor band: 0 marks a band inaudible #define SILENCE_RMS 0.4f // per-sample RMS gate for silence #define AVG_ENERGY_WEIGHT 0.2f // noise-like (average-energy) share of the target #define PEAK_ENERGY_WEIGHT 0.45f // tonal (peak-energy) share of the remainder #define SHORT_BLOCK_TIGHTEN 0.45f // short blocks get a tighter target per unit of energy #define LOUDNESS_EXPONENT 0.4f // Zwicker-ish loudness compression #define AVG_ENERGY_FLOOR_FRAC 0.0010f // -30 dB floor, keeps quiet bands from collapsing the target #define PEAK_ENERGY_FLOOR_FRAC 0.0050f // ~-23 dB floor, same purpose for peak energy typedef struct { float sum; /* energy summed across group windows */ float peak_amp; /* sqrt of the largest single-coefficient energy seen */ } BandEnergy; static void measure_band_energy(const CoderInfo * __restrict ci, const float * __restrict xr0, int gnum, BandEnergy * __restrict out) { int gsize = ci->groups.len[gnum]; int sfb; for (sfb = 0; sfb < ci->sfbn; sfb++) { int lo = ci->sfb_offset[sfb], hi = ci->sfb_offset[sfb + 1]; float sum = 0.0f, peak = 0.0f; int w; for (w = 0; w < gsize; w++) { const float *line = xr0 + w * BLOCK_LEN_SHORT + lo; int k; for (k = 0; k < hi - lo; k++) { float e = line[k] * line[k]; sum += e; if (e > peak) peak = e; } } out[sfb].sum = sum; out[sfb].peak_amp = sqrtf(peak); } } static float loudness(float energy_ratio) { return powf(energy_ratio, LOUDNESS_EXPONENT); } // masking sensitivity drops above ~4 kHz; de-emphasize bands toward Nyquist static float treble_rolloff(int lo, int hi, float inv_block_len) { return 10.0f / (1.0f + (float)(lo + hi) * inv_block_len); } static void derive_masking_targets(CoderInfo * __restrict ci, int gnum, float quality, const BandEnergy * __restrict be, float * __restrict target_out, float * __restrict avg_out) { int gsize = ci->groups.len[gnum]; int total_len = ci->sfb_offset[ci->sfbn]; float group_total = 0.0f; int sfb; for (sfb = 0; sfb < ci->sfbn; sfb++) group_total += be[sfb].sum; // whole group below the silence gate: force every band to a zero target if (group_total < (SILENCE_RMS * SILENCE_RMS) * (float)(gsize * total_len)) { for (sfb = 0; sfb < ci->sfbn; sfb++) { target_out[sfb] = 0.0f; avg_out[sfb] = 0.0f; } return; } int block_len = (ci->block_type == ONLY_SHORT_WINDOW) ? BLOCK_LEN_SHORT : BLOCK_LEN_LONG; float inv_block_len = 1.0f / (float)block_len; for (sfb = 0; sfb < ci->sfbn; sfb++) { int lo = ci->sfb_offset[sfb], hi = ci->sfb_offset[sfb + 1]; float avg = be[sfb].sum; float peak = be[sfb].peak_amp * be[sfb].peak_amp; float ref = (group_total * inv_block_len) * (hi - lo); float target; // floor before pow(): formula is monotonic, so this floors the output too if (avg < ref * AVG_ENERGY_FLOOR_FRAC) avg = ref * AVG_ENERGY_FLOOR_FRAC; if (peak < ref * PEAK_ENERGY_FLOOR_FRAC) peak = ref * PEAK_ENERGY_FLOOR_FRAC; target = AVG_ENERGY_WEIGHT * loudness(avg / ref) + (1.0f - AVG_ENERGY_WEIGHT) * PEAK_ENERGY_WEIGHT * loudness(peak / ref); if (ci->block_type == ONLY_SHORT_WINDOW) target *= SHORT_BLOCK_TIGHTEN; target *= treble_rolloff(lo, hi, inv_block_len); target_out[sfb] = target * quality; avg_out[sfb] = be[sfb].sum; } } // per-band codebook assignment: zero / PNS / regular+Huffman /* Re-derives gain after each clamp stage since scalefactor and gain are * coupled. Reports the final relative (bitstream-delta) and absolute * scalefactors. */ static float resolve_band_gain(int sfac, int sf_bias, float band_peak, int last_abs, int * __restrict out_sf_rel, int * __restrict out_sf_abs) { float gain = gain_with_overflow_clamp(&sfac, band_peak); int sf_rel = SF_OFFSET - sfac; int sf_abs = sf_bias + sf_rel; if (last_abs != SF_CHAIN_UNSET) { int wanted = sf_abs - last_abs; int allowed = clamp_sf_diff(wanted); if (allowed != wanted) { sf_abs = last_abs + allowed; sf_rel = sf_abs - sf_bias; sfac = SF_OFFSET - sf_rel; gain = gain_with_overflow_clamp(&sfac, band_peak); sf_rel = SF_OFFSET - sfac; sf_abs = sf_bias + sf_rel; } } if (sf_abs < 0 || sf_abs > SF_MAX_ABS) { sf_abs = (sf_abs < 0) ? 0 : SF_MAX_ABS; sf_rel = sf_abs - sf_bias; sfac = SF_OFFSET - sf_rel; gain = gain_with_overflow_clamp(&sfac, band_peak); sf_rel = SF_OFFSET - sfac; sf_abs = sf_bias + sf_rel; } *out_sf_rel = sf_rel; *out_sf_abs = sf_abs; return gain; } static void assign_band_codebooks(CoderInfo * __restrict ci, const float * __restrict xr0, const float * __restrict target, const float * __restrict bandenrg, const float * __restrict bandpeak, int gnum, int pnslevel, int * __restrict p_last_abs) { int gsize = ci->groups.len[gnum]; float pns_threshold = 0.1f * (float)pnslevel; int sb; for (sb = 0; sb < ci->sfbn && ci->bandcnt < MAX_SCFAC_BANDS; sb++) { int band = ci->bandcnt; if (ci->book[band] != HCB_NONE) { ci->bandcnt++; continue; } int lo = ci->sfb_offset[sb], hi = ci->sfb_offset[sb + 1]; int width = hi - lo; float avg_per_window = bandenrg[sb] / (float)gsize; float rms = sqrtf(avg_per_window / width); if (rms < SILENCE_RMS || target[sb] == 0.0f) { ci->book[band] = HCB_ZERO; ci->bandcnt++; continue; } /* PNS is fine inside TNS-covered bands -- the decoder's inverse * TNS filter shapes the substituted noise too. */ if (target[sb] < pns_threshold) { ci->book[band] = HCB_PNS; ci->sf[band] += lrintf(log10f(avg_per_window) * SF_STEP_ENRG); ci->bandcnt++; continue; } int sfac = lrintf(log10f(target[sb] / rms) * sfstep); int sf_rel = SF_OFFSET - sfac; int sf_bias = ci->sf[band]; if (sf_rel < SF_MIN) { ci->book[band] = HCB_ZERO; } else { int sf_abs; float gain = resolve_band_gain(sfac, sf_bias, bandpeak[sb], *p_last_abs, &sf_rel, &sf_abs); int xi[FRAME_LEN]; int win; for (win = 0; win < gsize; win++) qfunc(xr0 + win * BLOCK_LEN_SHORT + lo, xi + win * width, width, gain); huffbook(ci, xi, gsize * width); *p_last_abs = sf_abs; } ci->sf[ci->bandcnt++] += sf_rel; } } void ResetCoderSections(CoderInfo *coder) { int i, n = coder->groups.n * coder->sfbn; for (i = 0; i < n; i++) { coder->book[i] = HCB_NONE; coder->sf[i] = 0; } } int BlocQuant(CoderInfo * __restrict coder, float * __restrict xr, AACQuantCfg *aacquantCfg) { float target[MAX_SCFAC_BANDS], bandenrg[MAX_SCFAC_BANDS]; BandEnergy be[NSFB_LONG]; float bandpeak[MAX_SCFAC_BANDS]; int i, lastsf = SF_CHAIN_UNSET; float *gxr = xr; coder->bandcnt = coder->datacnt = 0; for (i = 0; i < coder->groups.n; i++) { int sfb; measure_band_energy(coder, gxr, i, be); for (sfb = 0; sfb < coder->sfbn; sfb++) bandpeak[sfb] = be[sfb].peak_amp; derive_masking_targets(coder, i, (float)aacquantCfg->quality / DEFQUAL, be, target, bandenrg); assign_band_codebooks(coder, gxr, target, bandenrg, bandpeak, i, aacquantCfg->pnslevel, &lastsf); gxr += coder->groups.len[i] * BLOCK_LEN_SHORT; } // global_gain must come from a regular band: it's an 8-bit bitstream field, // and intensity/PNS bands store stereo-position/noise-energy on a different // (possibly negative) scale that would truncate and desync the decoder. coder->global_gain = 0; for (i = 0; i < coder->bandcnt; i++) { int b = coder->book[i]; if (b && b != HCB_INTENSITY && b != HCB_INTENSITY2 && b != HCB_PNS) { coder->global_gain = coder->sf[i]; break; } } int lastis = 0, lastpns = coder->global_gain - SF_PNS_OFFSET; for (i = 0; i < coder->bandcnt; i++) { int b = coder->book[i]; if (b == HCB_INTENSITY || b == HCB_INTENSITY2) { int diff = clamp_sf_diff(coder->sf[i] - lastis); lastis += diff; coder->sf[i] = lastis; } else if (b == HCB_PNS) { int diff = clamp_sf_diff(coder->sf[i] - lastpns); lastpns += diff; coder->sf[i] = lastpns; } } return 1; } void CalcBW(unsigned *bw, int rate, SR_INFO *sr, AACQuantCfg *aacquantCfg) { int i, l = 0, max = *bw * (BLOCK_LEN_SHORT << 1) / rate; for (i = 0; i < sr->num_cb_short && l < max; i++) l += sr->cb_width_short[i]; aacquantCfg->max_cbs = i; if (aacquantCfg->pnslevel) *bw = (float)l * rate / (BLOCK_LEN_SHORT << 1); l = 0, max = *bw * (BLOCK_LEN_LONG << 1) / rate; for (i = 0; i < sr->num_cb_long && l < max; i++) l += sr->cb_width_long[i]; aacquantCfg->max_cbl = i; aacquantCfg->max_l = l; *bw = (float)l * rate / (BLOCK_LEN_LONG << 1); } // short-window grouping: keep spectrally-similar windows together so they // share scalefactors; a transient onset starts a fresh group instead #define GROUP_MIN_SFB 2 // bands below this are too coarse/DC-heavy to inform grouping #define GROUP_ONSET_RATIO 3.0f // running max/min energy ratio that counts as a transient static void window_band_energy(const CoderInfo * __restrict ci, const float * __restrict w, int from_sfb, int to_sfb, float * __restrict e_out) { int sfb; for (sfb = from_sfb; sfb < to_sfb; sfb++) { float e = 0.0f; int k; for (k = ci->sfb_offset[sfb]; k < ci->sfb_offset[sfb + 1]; k++) e += w[k] * w[k]; e_out[sfb] = e; } } void BlocGroup(float *xr, CoderInfo *coderInfo, AACQuantCfg *cfg) { if (coderInfo->block_type != ONLY_SHORT_WINDOW) { coderInfo->groups.n = 1; coderInfo->groups.len[0] = 1; return; } int maxsfb = cfg->max_cbs; int cutoff = cfg->max_l / 8; int active_bands = maxsfb - GROUP_MIN_SFB; int onset_quorum = (active_bands * 3) >> 2; float band_e[NSFB_SHORT], run_min[NSFB_SHORT], run_max[NSFB_SHORT]; int win, group_start = 0; coderInfo->groups.n = 0; for (win = 0; win < MAX_SHORT_WINDOWS; win++) { float *w = xr + win * BLOCK_LEN_SHORT; int k, sfb; for (k = cutoff; k < coderInfo->sfb_offset[maxsfb]; k++) w[k] = 0.0f; window_band_energy(coderInfo, w, GROUP_MIN_SFB, maxsfb, band_e); if (win == group_start) { for (sfb = GROUP_MIN_SFB; sfb < maxsfb; sfb++) run_min[sfb] = run_max[sfb] = band_e[sfb]; continue; } int onset_votes = 0; for (sfb = GROUP_MIN_SFB; sfb < maxsfb; sfb++) { if (band_e[sfb] < run_min[sfb]) run_min[sfb] = band_e[sfb]; if (band_e[sfb] > run_max[sfb]) run_max[sfb] = band_e[sfb]; if (run_max[sfb] > GROUP_ONSET_RATIO * run_min[sfb]) onset_votes++; } if (onset_votes > onset_quorum) { coderInfo->groups.len[coderInfo->groups.n++] = win - group_start; group_start = win; for (sfb = GROUP_MIN_SFB; sfb < maxsfb; sfb++) run_min[sfb] = run_max[sfb] = band_e[sfb]; } } coderInfo->groups.len[coderInfo->groups.n++] = MAX_SHORT_WINDOWS - group_start; } knik0-faac-1950446/libfaac/quantize.h000066400000000000000000000025241522734263700172240ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Copyright (C) 2026 Nils Schimmelmann * * 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. */ #ifndef QUANTIZE_H #define QUANTIZE_H #include "coder.h" typedef struct { float quality; int max_cbl; int max_cbs; int max_l; int pnslevel; } AACQuantCfg; /* Rounding bias for the x^(3/4) quantization: 0.4054f minimizes average * quantization error for a uniform distribution (ISO 14496-3 §8.3.5). */ #define MAGIC_NUMBER 0.4054f enum { DEFQUAL = 100, MAXQUAL = 5000, MAXQUALADTS = MAXQUAL, MINQUAL = 10, }; void ResetCoderSections(CoderInfo *coderInfo); int BlocQuant(CoderInfo *coderInfo, float *xr, AACQuantCfg *aacquantCfg); void CalcBW(unsigned *bw, int rate, SR_INFO *sr, AACQuantCfg *aacquantCfg); void BlocGroup(float *xr, CoderInfo *coderInfo, AACQuantCfg *aacquantCfg); void QuantizeInit(void); #endif knik0-faac-1950446/libfaac/quantize_sse.c000066400000000000000000000042711522734263700200720ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Copyright (C) 2026 Nils Schimmelmann * * 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "quantize.h" void quantize_sse2(const float * __restrict xr, int * __restrict xi, int n, float sfacfix) { const __m128 zero = _mm_setzero_ps(); const __m128 sfac = _mm_set1_ps(sfacfix); const __m128 magic = _mm_set1_ps(MAGIC_NUMBER); // Mask to strip the sign bit (0x7FFFFFFF) const __m128 abs_mask = _mm_castsi128_ps(_mm_set1_epi32(0x7FFFFFFF)); int cnt = 0; // Process 4 elements per iteration for (; cnt <= n - 4; cnt += 4) { __m128 x_orig = _mm_loadu_ps((const float*)&xr[cnt]); // Capture sign and Absolute value __m128 sign_mask = _mm_cmplt_ps(x_orig, zero); __m128 x = _mm_and_ps(x_orig, abs_mask); // Math: (x * sfac)^0.75 + magic // Logic: sqrt( (x*sfac) * sqrt(x*sfac) ) x = _mm_mul_ps(x, sfac); x = _mm_mul_ps(x, _mm_sqrt_ps(x)); x = _mm_sqrt_ps(x); x = _mm_add_ps(x, magic); // Convert to integer __m128i xi_vec = _mm_cvttps_epi32(x); // Bitwise Sign Fix: (val ^ mask) - mask __m128i m_int = _mm_castps_si128(sign_mask); xi_vec = _mm_sub_epi32(_mm_xor_si128(xi_vec, m_int), m_int); _mm_storeu_si128((__m128i*)&xi[cnt], xi_vec); } // Safe scalar remainder loop for widths not multiple of 4 for (; cnt < n; cnt++) { float val = xr[cnt]; float tmp = fabsf(val); tmp *= sfacfix; tmp = sqrtf(tmp * sqrtf(tmp)); int q = (int)(tmp + (float)MAGIC_NUMBER); xi[cnt] = (val < 0) ? -q : q; } } knik0-faac-1950446/libfaac/resample.c000066400000000000000000000070531522734263700171710ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Copyright (C) 2026 Nils Schimmelmann * * 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. */ #include #include "resample.h" #include "coder.h" #include "util.h" /* Equiripple half-band FIR for 2:1 decimation. * Leverages the zero-valued odd-indexed taps and symmetric even-indexed taps * to reduce the computational load by ~75% compared to a general FIR. * The passband is flat within 0.05 dB up to the SBR crossover region, * ensuring the core signal remains transparent before SBR reconstruction. */ #define HB_CENTER 0.5015570876767614f static const resfloat hb_even[RESAMPLE_FILTER_LEN / 2 + 1] = { -2.39042884e-03f, 2.03978735e-03f, -2.88625768e-03f, 3.94878764e-03f, -5.26747336e-03f, 6.89408424e-03f, -8.89782634e-03f, 1.13774798e-02f, -1.44815390e-02f, 1.84491642e-02f, -2.36937924e-02f, 3.10005784e-02f, -4.20596122e-02f, 6.12815300e-02f, -1.04870415e-01f, 3.18777389e-01f, 3.18777389e-01f, -1.04870415e-01f, 6.12815300e-02f, -4.20596122e-02f, 3.10005784e-02f, -2.36937924e-02f, 1.84491642e-02f, -1.44815390e-02f, 1.13774798e-02f, -8.89782634e-03f, 6.89408424e-03f, -5.26747336e-03f, 3.94878764e-03f, -2.88625768e-03f, 2.03978735e-03f, -2.39042884e-03f, }; Resampler *ResampleInit(int channels) { Resampler *r = (Resampler *)AllocMemory(sizeof(Resampler)); if (!r) return NULL; SetMemory(r, 0, sizeof(Resampler)); r->channels = channels; return r; } void ResampleEnd(Resampler *r) { FreeMemory(r); } /* The symmetric-fold gather below defeats autovectorization. */ int Resample(Resampler *r, int input_len) { int output_len = input_len / 2; const int H = RESAMPLE_FILTER_LEN - 1; /* 62 */ const int HALF = RESAMPLE_FILTER_LEN / 2; /* 31 */ int ch, i, j; for (ch = 0; ch < r->channels; ch++) { float * __restrict in = r->fullRate[ch]; float * __restrict out = r->halfRate[ch]; float * __restrict hist = r->buf[ch]; /* Fixed-size buffers to avoid VLA (MSVC portability): history + one * full-rate HE frame (2 * FRAME_LEN input samples). */ float combined[RESAMPLE_FILTER_LEN - 1 + 2 * FRAME_LEN]; memcpy(combined, hist, H * sizeof(float)); memcpy(combined + H, in, input_len * sizeof(float)); /* Exploit FIR symmetry to fold the tap-delay line before multiplication. */ for (i = 0; i < output_len; i++) { const float * __restrict c = combined + 2 * i; float a0 = 0, a1 = 0, a2 = 0, a3 = 0; for (j = 0; j < 16; j += 4) { a0 += hb_even[j + 0] * (c[2 * (j + 0)] + c[2 * (31 - j - 0)]); a1 += hb_even[j + 1] * (c[2 * (j + 1)] + c[2 * (31 - j - 1)]); a2 += hb_even[j + 2] * (c[2 * (j + 2)] + c[2 * (31 - j - 2)]); a3 += hb_even[j + 3] * (c[2 * (j + 3)] + c[2 * (31 - j - 3)]); } *out++ = (a0 + a1) + (a2 + a3) + HB_CENTER * combined[2 * i + HALF]; } memcpy(hist, combined + input_len, H * sizeof(float)); } return output_len; } knik0-faac-1950446/libfaac/resample.h000066400000000000000000000027711522734263700172000ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Copyright (C) 2026 Nils Schimmelmann * * 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. */ /* 2:1 FIR downsampler for HE-AAC core signal preparation. * Takes full-rate PCM (Fs) and produces half-rate PCM (Fs/2) * for the AAC-LC core encoder. */ #ifndef RESAMPLE_H #define RESAMPLE_H #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "coder.h" #ifdef __cplusplus extern "C" { #endif typedef float resfloat; #define RESAMPLE_FILTER_LEN 63 typedef struct Resampler { float buf [MAX_CHANNELS][RESAMPLE_FILTER_LEN]; /* FIR overlap state (carries between frames) */ float fullRate[MAX_CHANNELS][2 * FRAME_LEN]; /* full-rate input: caller fills, SBR reads, FIR consumes */ float halfRate[MAX_CHANNELS][FRAME_LEN]; /* downsampled output: written by Resample */ int channels; } Resampler; Resampler *ResampleInit(int channels); void ResampleEnd(Resampler *r); int Resample(Resampler *r, int input_len); #ifdef __cplusplus } #endif #endif /* RESAMPLE_H */ knik0-faac-1950446/libfaac/sbr.c000066400000000000000000000456421522734263700161550ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Copyright (C) 2026 Nils Schimmelmann * * 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. */ #include #include #include #include #include "sbr.h" #include "sbr_tables.h" #include "util.h" #include "sbr_analysis.h" #include "resample.h" #include "bitstream.h" #include "sbr_internal.h" #include "faac_internal.h" /* SBR master frequency band table (ISO/IEC 14496-3:2005 §4.6.18.3.2). kx/k2 are * spec-mandatory: the decoder reconstructs them from the sample rate alone, so * these must match its table exactly or the envelope band count desyncs. The * rate here is the full output rate (= 2*core), which is what the decoder uses. */ /* SBR start frequency (kx). Crossover alignment prevents aliasing/gaps. */ static int compute_kx(int sampleRate, int bs_start_freq) { int temp = (sampleRate < 32000) ? 3000 : (sampleRate < 64000) ? 4000 : 5000; int start_min = ((temp << 7) + (sampleRate >> 1)) / sampleRate; int row = (sampleRate <= 16000) ? 0 : (sampleRate <= 22050) ? 1 : (sampleRate <= 24000) ? 2 : (sampleRate <= 32000) ? 3 : (sampleRate <= 64000) ? 4 : 5; return clamp_int(start_min + sbr_offset[row][bs_start_freq & 15], 1, 63); } static int cmp_int16(const void *a, const void *b) { return (int)(*(const short *)a) - (int)(*(const short *)b); } /* SBR stop frequency (k2). Bark-scale distribution maximizes bit efficiency. */ static int compute_k2(int sampleRate, int kx, int bs_stop_freq) { if (bs_stop_freq == 14 || bs_stop_freq == 15) return 64; int temp = (sampleRate < 32000) ? 3000 : (sampleRate < 64000) ? 4000 : 5000; int stop_min = ((temp << 8) + (sampleRate >> 1)) / sampleRate; int k2; if (bs_stop_freq < 14) { short stop_dk[13]; float prod = (float)stop_min; int prev = stop_min; float base = powf(64.0f / (float)stop_min, (float)(1.0f / 13.0f)); for (int i = 0; i < 12; i++) { prod *= base; int present = (int)lrintf(prod); stop_dk[i] = (short)(present - prev); prev = present; } stop_dk[12] = (short)(64 - prev); qsort(stop_dk, 13, sizeof(short), cmp_int16); k2 = stop_min; for (int i = 0; i < bs_stop_freq; i++) k2 += stop_dk[i]; } else { k2 = 64; } int max_span = (sampleRate <= 32000) ? 48 : (sampleRate <= 44100) ? 35 : 32; return clamp_int(k2, kx + 1, kx + max_span > 64 ? 64 : kx + max_span); } /* Distribute QMF bands into SBR master bands using uniform dk-spacing. * Residual bands are merged into the first/last pairs to maintain a * monotonic frequency grid. */ static int build_freq_table(SBRInfo *sbr) { int kx = sbr->kx, k2 = sbr->k2, dk = sbr->dk; int n_master = clamp_int(((k2 - kx + (dk & 2)) >> dk) << 1, 1, SBR_MAX_BANDS); int f_master[SBR_MAX_BANDS + 1]; for (int k = 1; k <= n_master; k++) f_master[k] = dk; int k2diff = (k2 - kx) - n_master * dk; if (k2diff < 0) { f_master[1]--; if (k2diff < -1) f_master[2]--; } else if (k2diff > 0) f_master[n_master]++; f_master[0] = kx; for (int k = 1; k <= n_master; k++) f_master[k] += f_master[k - 1]; sbr->numBands = n_master; for (int b = 0; b <= n_master; b++) sbr->bandEdges[b] = f_master[b]; sbr->numNoiseBands = 1; return n_master; } SBRInfo *SbrInit(int channels, int sampleRate, unsigned long bitRate, FFT_Tables *fft_tables) { SBRInfo *sbr = (SBRInfo *)AllocMemory(sizeof(SBRInfo)); if (!sbr) return NULL; SetMemory(sbr, 0, sizeof(SBRInfo)); sbr->sbrPresent = 1; sbr->numChannels = channels; sbr->sampleRate = sampleRate; /* Pre-calculate twiddle factors for the FFT-based QMF analysis. * These coefficients rotate the subband indices into the odd-frequency * DFT space required by the SBR modulation kernel. */ for (int m = 0; m < SBR_QMF_BANDS_64; m++) { sbr->twidCos[m] = (float)cos(M_PI_DOUBLE * m / 64.0); sbr->twidSin[m] = (float)sin(M_PI_DOUBLE * m / 64.0); sbr->oddCos[m] = (float)cos(M_PI_DOUBLE * (2 * m + 1) / 128.0); sbr->oddSin[m] = (float)sin(M_PI_DOUBLE * (2 * m + 1) / 128.0); } /* Borrow the encoder's shared core FFT tables (same fft() routine, same * logm=6 size as the short-block MDCT). The core owns init/terminate; the * logm=6 table is built lazily on first use, single-threaded per encoder. */ sbr->fftTables = fft_tables; SbrUpdate(sbr, bitRate); return sbr; } /* Re-resolve SBR operational parameters (crossover, resolution) when the * bitrate or sample rate changes, avoiding handle reallocation. */ void SbrUpdate(SBRInfo *sbr, unsigned long bitRate) { int sampleRate = sbr->sampleRate; unsigned long rate_per_ch = bitRate / sbr->numChannels; sbr->bs_amp_res = (rate_per_ch < SBR_AMP_RES_BITRATE_BPS) ? 0 : 1; /* Target crossover near the core ceiling (~11.6 kHz) maximizes MOS. * Higher-order parametric reconstruction below 10 kHz is audible and * generally inferior to the bit-starved LC core. */ if (rate_per_ch <= SBR_COARSE_TABLE_BITRATE_BPS) { sbr->bs_start_freq = 15; sbr->bs_alter_scale = 1; sbr->dk = 2; } else { sbr->bs_start_freq = 15; sbr->bs_alter_scale = 0; sbr->dk = 1; } /* Stop frequency covers approximately 75% of the upper octave. */ sbr->bs_stop_freq = 10; sbr->bs_freq_res = 1; /* HIGH resolution */ sbr->bs_xover_band = 0; /* every master band is an SBR band; no low-res split */ sbr->numEnvelopes = 1; sbr->eff_amp_res = (sbr->numEnvelopes == 1) ? 0 : sbr->bs_amp_res; sbr->kx = compute_kx(sampleRate, sbr->bs_start_freq); sbr->k2 = compute_k2(sampleRate, sbr->kx, sbr->bs_stop_freq); build_freq_table(sbr); } void SbrEnd(SBRInfo *sbr) { if (!sbr) return; /* fftTables is borrowed from the encoder; the core terminates it. */ FreeMemory(sbr); } SBRContext *SbrContextInit(int channels) { SBRContext *sbrCtx = (SBRContext *)AllocMemory(sizeof(SBRContext)); if (sbrCtx) { SetMemory(sbrCtx, 0, sizeof(SBRContext)); sbrCtx->resampler = ResampleInit(channels); if (!sbrCtx->resampler) { FreeMemory(sbrCtx); return NULL; } } return sbrCtx; } void SbrContextEnd(SBRContext *sbrCtx) { if (!sbrCtx) return; if (sbrCtx->sbrInfo) { SbrEnd(sbrCtx->sbrInfo); } if (sbrCtx->resampler) { ResampleEnd(sbrCtx->resampler); } FreeMemory(sbrCtx); } int SbrContextGetASC(SBRContext *sbrCtx, int coreSRIdx, int channels, unsigned char** ppBuffer, unsigned long* pSize) { /* Explicit-hierarchy ASC: AAC-LC core wrapped with an SBR extension * (sync 0x2b7, type 5) carrying the full output rate. The core rate is * Fs/2 (dual-rate SBR); the extension declares the full output rate. */ *pSize = 5; *ppBuffer = (unsigned char *)malloc(5); if (*ppBuffer == NULL) return -3; memset(*ppBuffer, 0, 5); BitStream *pBitStream = OpenBitStream(5, *ppBuffer); PutBit(pBitStream, LOW, 5); /* core object type */ PutBit(pBitStream, coreSRIdx, 4); /* core rate (Fs/2, dual-rate) */ PutBit(pBitStream, channels, 4); PutBit(pBitStream, 0, 1); /* frameLengthFlag */ PutBit(pBitStream, 0, 1); /* dependsOnCoreCoder */ PutBit(pBitStream, 0, 1); /* extensionFlag */ PutBit(pBitStream, 0x2b7, 11); /* syncExtensionType */ PutBit(pBitStream, HE_V1, 5); /* extObjectType = SBR */ PutBit(pBitStream, 1, 1); /* sbrPresentFlag */ PutBit(pBitStream, sbrCtx->fullSampleRateIdx, 4); /* SBR output rate (2*core) */ CloseBitStream(pBitStream); return 0; } unsigned int SbrContextGetXOverBandwidth(SBRContext *sbrCtx) { if (!sbrCtx || !sbrCtx->sbrInfo) return 0; /* kx * Fs / (2*64): each QMF band is Fs/(2*SBR_QMF_BANDS_64) Hz wide. * Matching core bandwidth to the SBR crossover avoids a gap or overlap. */ return (unsigned int)((sbrCtx->sbrInfo->kx * sbrCtx->fullSampleRate) / (2 * SBR_QMF_BANDS_64)); } void SbrContextUpdateConfig(SBRContext *sCtx, int channels, unsigned long bitrate, FFT_Tables *fft_tables) { if (!sCtx) return; if (!sCtx->sbrInfo) sCtx->sbrInfo = SbrInit(channels, sCtx->fullSampleRate, bitrate, fft_tables); else SbrUpdate(sCtx->sbrInfo, bitrate); } void SbrContextProcessFrame(SBRContext *sCtx, int numChannels, int realPerCh, float *inputFifo[MAX_CHANNELS], float *heHalfRate[MAX_CHANNELS]) { unsigned int channel; Resampler *rs = sCtx->resampler; float *fullPtrs[MAX_CHANNELS]; for (channel = 0; channel < (unsigned int)numChannels; channel++) { float *fullRate = rs->fullRate[channel]; fullPtrs[channel] = fullRate; memcpy(fullRate, inputFifo[channel], realPerCh * sizeof(float)); /* Final partial frame: silence-pad the unfilled full-rate tail to * prevent the resampler from consuming stale data. SbrEncode reads * only [0, realPerCh), so it is unaffected. */ if (realPerCh < 2 * FRAME_LEN) memset(fullRate + realPerCh, 0, (2 * FRAME_LEN - realPerCh) * sizeof(float)); heHalfRate[channel] = rs->halfRate[channel]; } /* Shared signal analysis. */ SbrAnalyze(&sCtx->signalAnalysis, fullPtrs, numChannels, realPerCh, sCtx->sbrInfo); /* Update the transient FIFO. Shift down by one and push * the newest decision at SBR_DETECT_FIFO-1; index 0 stays aligned with the * core frame being coded (LOOKAHEAD_DEPTH frames behind this analysis). */ for (channel = 0; channel < (unsigned int)numChannels; channel++) { memmove(&sCtx->transientStrengthFIFO[channel][0], &sCtx->transientStrengthFIFO[channel][1], (SBR_DETECT_FIFO - 1) * sizeof(float)); sCtx->transientStrengthFIFO[channel][SBR_DETECT_FIFO - 1] = sCtx->signalAnalysis.ch[channel].transientStrength; memmove(&sCtx->wantShortFIFO[channel][0], &sCtx->wantShortFIFO[channel][1], (SBR_DETECT_FIFO - 1) * sizeof(int)); sCtx->wantShortFIFO[channel][SBR_DETECT_FIFO - 1] = sCtx->signalAnalysis.ch[channel].wantShort; } SbrEncode(sCtx->sbrInfo, fullPtrs, numChannels, realPerCh, &sCtx->signalAnalysis); /* Dual-rate decimation: produces the halved-rate core signal. */ Resample(rs, 2 * FRAME_LEN); } void SbrContextRestoreRate(SBRContext *sCtx, unsigned long *sampleRate, unsigned int *sampleRateIdx, SR_INFO **srInfoPtr) { if (sCtx && sCtx->fullSampleRate > 0) { *sampleRate = sCtx->fullSampleRate; *sampleRateIdx = sCtx->fullSampleRateIdx; *srInfoPtr = &srInfo[*sampleRateIdx]; sCtx->fullSampleRate = 0; } } unsigned long SbrContextGetFullRate(SBRContext *sCtx, unsigned long defaultRate) { return (sCtx && sCtx->fullSampleRate) ? sCtx->fullSampleRate : defaultRate; } /* Dual-rate SBR: the AAC core encodes at Fs/2 while SBR reconstructs the top * octave back to the full rate. Halve the core rate here; the full rate is kept * in the context for SBR and the ASC. */ void SbrContextResolveRate(SBRContext *sCtx, unsigned long *sampleRate, unsigned int *sampleRateIdx, SR_INFO **srInfoPtr) { if (sCtx->fullSampleRate == 0) { sCtx->fullSampleRate = *sampleRate; sCtx->fullSampleRateIdx = *sampleRateIdx; *sampleRate = *sampleRate / 2; *sampleRateIdx = GetSRIndex(*sampleRate); *srInfoPtr = &srInfo[*sampleRateIdx]; } } int SbrContextIsAnalysisValid(SBRContext *sCtx) { return sCtx ? sCtx->signalAnalysis.valid : 0; } int SbrContextGetWantShort(SBRContext *sCtx, int channel, int index) { if (sCtx && channel < MAX_CHANNELS && index < SBR_DETECT_FIFO) { return sCtx->wantShortFIFO[channel][index]; } return 0; } int SbrContextIsPresent(SBRContext *sCtx) { return (sCtx && sCtx->sbrInfo) ? 1 : 0; } /* Optimized log2 approximation for energy-to-decibel conversion. * Precision is sufficient for the 1.5/3.0 dB envelope quantizer. */ #define FAST_LOG2_A 1.3424f #define FAST_LOG2_B 0.3427f #define FAST_LOG2_MANT_NORM (1.0f / (1 << 23)) /* 23-bit mantissa → [0, 1) */ static inline float fast_log2(float x) { union { float f; int32_t i; } vx; vx.f = (float)x; int32_t exp = (vx.i >> 23) & 0xFF; float m = (float)(vx.i & 0x7FFFFF) * FAST_LOG2_MANT_NORM; return (float)(exp - 127) + (float)(m * (FAST_LOG2_A - FAST_LOG2_B * m)); } /* 64-band subband energy analysis using a 64-point complex FFT. * Leverages conjugate symmetry to extract two 64-point real-subsequence * DFTs from one complex transform, reducing FLOPs by ~50% compared to * a standard 128-point implementation. Phase info is discarded as the * SBR bitstream only transmits envelope magnitudes. */ #if defined(__GNUC__) __attribute__((hot)) #endif void SbrQmfAnalysis(SBRInfo *sbr, const float * restrict ovl_pos, float * restrict energy, int kx, int k2) { float xr[64], xi[64]; const sbrfloat * restrict p0 = qmf_c; const sbrfloat * restrict p1 = qmf_c + 1; for (int m = 0; m < 64; m++) { int n0 = 2 * m; float a = p0[0] * ovl_pos[639 - n0] + p0[128] * ovl_pos[511 - n0] + p0[256] * ovl_pos[383 - n0] + p0[384] * ovl_pos[255 - n0] + p0[512] * ovl_pos[127 - n0]; float b = p1[0] * ovl_pos[638 - n0] + p1[128] * ovl_pos[510 - n0] + p1[256] * ovl_pos[382 - n0] + p1[384] * ovl_pos[254 - n0] + p1[512] * ovl_pos[126 - n0]; /* c[m] = (a + j*b) * exp(-j*pi*m/64) */ xr[m] = a * sbr->twidCos[m] - b * sbr->twidSin[m]; xi[m] = -(a * sbr->twidSin[m] + b * sbr->twidCos[m]); p0 += 2; p1 += 2; } fft(sbr->fftTables, xr, xi, 6); for (int k = kx; k < k2; k++) { int kr = 63 - k; /* Separate the two real-subsequence DFTs by conjugate symmetry. */ float Ar = 0.5f * (xr[k] + xr[kr]); float Ai = 0.5f * (xi[kr] - xi[k]); float Br = -0.5f * (xi[k] + xi[kr]); float Bi = 0.5f * (xr[kr] - xr[k]); /* Sr = Ar + w_k_real * Br - w_k_imag * Bi * Si = Ai + w_k_real * Bi + w_k_imag * Br */ float wr = sbr->oddCos[k]; float wi = sbr->oddSin[k]; float Sr = Ar + wr * Br - wi * Bi; float Si = Ai + wr * Bi + wi * Br; energy[k] = Sr * Sr + Si * Si; } } static void sbr_adopt_envelope_grid(SBRInfo *sbr, struct SignalAnalysis *sa) { sbr->numEnvelopes = sa->numEnvelopes; sbr->frameClass = sa->frameClass; sbr->bsPointer = sa->bsPointer; for (int i = 0; i <= sa->numEnvelopes; i++) sbr->tEnv[i] = sa->tEnv[i]; sbr->eff_amp_res = (sbr->numEnvelopes == 1) ? 0 : sbr->bs_amp_res; } static void sbr_quantize_envelopes(SBRInfo *sbr, int nch, int sampled, struct SignalAnalysis *sa, float bandHalfE[2][2][SBR_QMF_BANDS_64]) { int n_env = sbr->numEnvelopes; for (int ch = 0; ch < nch; ch++) { int noise_level = SBR_NOISE_LEVEL_DEFAULT; sbr->ch[ch].invfMode = 3; int dlav = sbr->eff_amp_res ? SBR_ENV_DELTA_LIMIT_HIRES : SBR_ENV_DELTA_LIMIT_LORES; for (int e = 0; e < n_env; e++) { int prevLevel = -1; for (int b = 0; b < sbr->numBands; b++) { int k_lo = sbr->bandEdges[b], k_hi = sbr->bandEdges[b+1]; /* Weight energy by the number of QMF slots per envelope to * maintain normalized power levels across variable borders. */ int e_slots = (n_env == 1) ? sampled : sa->envSampled[e]; if (e_slots < 1) e_slots = 1; float E = 0; if (n_env == 1) { for (int k = k_lo; k < k_hi; k++) E += bandHalfE[ch][0][k] + bandHalfE[ch][1][k]; } else { for (int k = k_lo; k < k_hi; k++) E += bandHalfE[ch][e][k]; } E /= (float)(e_slots * (k_hi - k_lo)); float factor = sbr->eff_amp_res ? 1.0f : 2.0f; int level = lrintf(factor * (fast_log2(E + SBR_LOG_ENERGY_FLOOR) - SBR_ENV_LEVEL_LOG2_OFFSET)); int raw_level = clamp_int(level, 0, 127); if (prevLevel < 0) { raw_level = clamp_int(raw_level, 0, sbr->eff_amp_res ? 63 : 127); sbr->ch[ch].envData[e][b] = raw_level; prevLevel = raw_level; } else { int delta = clamp_int(raw_level - prevLevel, -dlav, dlav); sbr->ch[ch].envData[e][b] = delta; prevLevel += delta; } } } int n_q = n_env > 1 ? 2 : 1; for (int ne = 0; ne < n_q; ne++) { int prevNoise = -1; for (int nb = 0; nb < sbr->numNoiseBands; nb++) { if (prevNoise < 0) { sbr->ch[ch].noiseData[ne][nb] = noise_level; prevNoise = noise_level; } else { int delta = clamp_int(noise_level - prevNoise, -15, 15); sbr->ch[ch].noiseData[ne][nb] = delta; prevNoise += delta; } } } } } void SbrEncode(SBRInfo *sbr, float *timeDomain[MAX_CHANNELS], int numChannels, int numSamples, struct SignalAnalysis *sa) { int nch = clamp_int(numChannels, 1, 2); float bandHalfE[2][2][SBR_QMF_BANDS_64]; /* New frame: freeze the header-send decision now, before SbrWrite's write * pass (later, in the bitstream stage) mutates headerSent/frameCount. */ sbr->sendHeaderThisFrame = (!sbr->headerSent || (sbr->frameCount % SBR_HEADER_PERIOD == 0)); for (int ch = 0; ch < nch; ch++) { /* Use shared transient strength and accumulated energies from SbrAnalyze. */ memcpy(bandHalfE[ch][0], sa->ch[ch].bandHalfE[0], SBR_QMF_BANDS_64 * sizeof(float)); memcpy(bandHalfE[ch][1], sa->ch[ch].bandHalfE[1], SBR_QMF_BANDS_64 * sizeof(float)); memcpy(sbr->ch[ch].qmfOvl64, timeDomain[ch] + numSamples - SBR_QMF_OVL_LEN_64, SBR_QMF_OVL_LEN_64 * sizeof(float)); } sbr_adopt_envelope_grid(sbr, sa); sbr_quantize_envelopes(sbr, nch, sa->sampled, sa, bandHalfE); } /* SBR bitstream writer. Emits the SBR fill element payload into the bitstream. * Replays the write sequence into a counting sink during rate control to * ensure accurate bit budget allocation. */ knik0-faac-1950446/libfaac/sbr.h000066400000000000000000000113151522734263700161500ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Copyright (C) 2026 Nils Schimmelmann * * 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. */ #ifndef SBR_H #define SBR_H #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "coder.h" #include "fft.h" #include "sbr_analysis.h" /* Input sample FIFO slots, each one frame (FRAME_LEN samples) wide, relative to the frame currently being coded (FIFO_CURR): one frame behind (FIFO_PAST, reused as the MDCT overlap) and two frames ahead. The two ahead slots are needed because the block-switch energy analysis works on 2-frame-wide windows and keeps one window of lookahead, whose far edge reaches two frames ahead. */ #define LOOKAHEAD_DEPTH 2 #define FIFO_PAST 0 #define FIFO_CURR 1 #define FIFO_AHEAD1 2 #define FIFO_AHEAD2 3 /* Depth of the HE shared-detector decision FIFO. Sized so index 0 lines up with the core frame currently being coded: the core lags the freshest SBR analysis by LOOKAHEAD_DEPTH frames, so index 0 must be LOOKAHEAD_DEPTH entries behind the newest (one extra slot for the newest entry itself). */ #define SBR_DETECT_FIFO (LOOKAHEAD_DEPTH + 1) #ifdef __cplusplus extern "C" { #endif struct BitStream; #define SBR_QMF_BANDS_64 64 #define SBR_QMF_OVL_LEN_64 576 #define SBR_MAX_BANDS 64 #define SBR_MAX_ENVELOPES 2 #define SBR_MAX_NOISE_ENVELOPES 2 #define SBR_MAX_NOISE_BANDS 5 #define SBR_HEADER_PERIOD 30 /* SBR frame classes (ISO 14496-3:2009 §4.6.18.3, Table 4.80). */ #define SBR_FRAME_CLASS_FIXFIX 0 #define SBR_FRAME_CLASS_FIXVAR 1 #define SBR_FRAME_CLASS_VARFIX 2 #define SBR_FRAME_CLASS_VARVAR 3 /* Envelope time-slot resolution the decoder uses for an AAC-LC core frame * (FFmpeg/FAAD2 pass numTimeSlots=16). All bs_rel_bord/t_env values written in * a variable grid live in [0, SBR_NUM_TIME_SLOTS]. */ #define SBR_NUM_TIME_SLOTS 16 /* SBR extension types (ISO 14496-3 §4.6.18). */ #define SBR_EXT_TYPE_SBR 0xd #define SBR_EXT_TYPE_SBR_CRC 0xe /* Transient detection threshold (peak-to-mean power ratio). */ #define SBR_TRANSIENT_THRESH_DEFAULT (4.0f) /* div-by-zero guard for the peak/mean ratio in silence frames (~-150 dBFS^2). */ #define SBR_ENERGY_FLOOR (1e-15f) /* log2(0) guard in envelope quantization: -200 dBFS^2, below all SBR quantizer ranges. */ #define SBR_LOG_ENERGY_FLOOR (1e-20f) /* Default noise floor level (ISO 14496-3 §4.6.18.6.4). */ #define SBR_NOISE_LEVEL_DEFAULT 4 /* 6 = log2(64): normalises 64-band QMF energy to per-band level. ISO 14496-3 §4.6.18.6.3. */ #define SBR_ENV_LEVEL_LOG2_OFFSET (6.0f) /* Rate-dependent resolution thresholds. */ #define SBR_AMP_RES_BITRATE_BPS 20000u #define SBR_COARSE_TABLE_BITRATE_BPS 32000u /* Max delta-coded step for envelope data, per bs_amp_res grid (ISO 14496-3 * §4.6.18.3.6): the fine (amp_res=1) grid has half the step size of the coarse * grid, so its delta range must be roughly double to cover the same dB span. */ #define SBR_ENV_DELTA_LIMIT_HIRES 31 #define SBR_ENV_DELTA_LIMIT_LORES 60 typedef struct SBRInfo SBRInfo; struct SignalAnalysis; typedef struct SBRContext SBRContext; SBRContext *SbrContextInit(int channels); void SbrContextEnd(SBRContext *sbrCtx); int SbrContextGetASC(SBRContext *sbrCtx, int coreSRIdx, int channels, unsigned char** ppBuffer, unsigned long* pSize); unsigned int SbrContextGetXOverBandwidth(SBRContext *sbrCtx); void SbrContextUpdateConfig(SBRContext *sCtx, int channels, unsigned long bitrate, FFT_Tables *fft_tables); void SbrContextProcessFrame(SBRContext *sCtx, int numChannels, int realPerCh, float *inputFifo[MAX_CHANNELS], float *heHalfRate[MAX_CHANNELS]); int SbrContextIsPresent(SBRContext *sCtx); void SbrContextRestoreRate(SBRContext *sCtx, unsigned long *sampleRate, unsigned int *sampleRateIdx, SR_INFO **srInfo); unsigned long SbrContextGetFullRate(SBRContext *sCtx, unsigned long defaultRate); void SbrContextResolveRate(SBRContext *sCtx, unsigned long *sampleRate, unsigned int *sampleRateIdx, SR_INFO **srInfo); int SbrContextIsAnalysisValid(SBRContext *sCtx); int SbrContextGetWantShort(SBRContext *sCtx, int channel, int index); int SbrContextGetBits(SBRContext *sCtx, struct BitStream *bs, int channels, int aacObjectType, int writeFlag); #ifdef __cplusplus } #endif #endif knik0-faac-1950446/libfaac/sbr_analysis.c000066400000000000000000000147511522734263700200550ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Copyright (C) 2026 Nils Schimmelmann * * 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. */ #include "sbr_analysis.h" #include "sbr.h" #include "sbr_internal.h" #include "util.h" #include #ifdef HAVE_CONFIG_H #include "config.h" #endif /* Multi-pass signal analysis: transient detection, temporal grid selection, * and subband energy accumulation. hot keeps it vectorized under LTO despite * only being reached through the cold dispatcher; SbrQmfAnalysis is inlined * here (not split out) to stay under GCC's LTO auto-inline threshold. */ #if defined(__GNUC__) __attribute__((hot)) #endif void SbrAnalyze(SignalAnalysis *sa, float *fullPtrs[], int nch, int numSamples, struct SBRInfo *sbr) { int num_slots = numSamples / SBR_QMF_BANDS_64; int sampled = (num_slots - 1) / FAAC_SBR_DECIMATION + 1; float workspace[SBR_QMF_OVL_LEN_64 + 2 * FRAME_LEN]; sa->valid = 1; sa->numSlots = num_slots; sa->sampled = sampled; /* Pass 1: Time-domain transient detection. Identifies the temporal position * and strength of transients across all channels. */ for (int ch = 0; ch < nch; ch++) { float smax = 0.0f, ssum = 0.0f; int smax_idx = 0; float slot_hp_eng[128]; /* high-pass energy per slot (max slots = 2*1024/64 = 32) */ sa->ch[ch].wantShort = 0; float val_in = sa->ch[ch].lastVal; const float * restrict p_in = fullPtrs[ch]; for (int slot = 0; slot < num_slots; slot++) { float stot = 0.0f; float hp_stot = 0.0f; for (int n = 0; n < SBR_QMF_BANDS_64; n += 4) { float v0 = p_in[0], v1 = p_in[1], v2 = p_in[2], v3 = p_in[3]; stot += v0 * v0 + v1 * v1 + v2 * v2 + v3 * v3; float d0 = v0 - val_in, d1 = v1 - v0, d2 = v2 - v1, d3 = v3 - v2; hp_stot += d0 * d0 + d1 * d1 + d2 * d2 + d3 * d3; val_in = v3; p_in += 4; } if (slot < 128) slot_hp_eng[slot] = hp_stot; if (stot > smax) { smax = stot; smax_idx = slot; } ssum += stot; } sa->ch[ch].lastVal = val_in; sa->ch[ch].transientStrength = smax * (float)sampled / (ssum + SBR_ENERGY_FLOOR); sa->ch[ch].transientSlot = smax_idx; /* Evaluate relative energy jumps to inform block switching. */ float last_hp_eng = 0.0f; int have_last = 0; for (int slot = 0; slot < num_slots; slot++) { if (slot >= 128) break; float hp_eng = slot_hp_eng[slot]; if (have_last) { float toteng = (hp_eng < last_hp_eng) ? hp_eng : last_hp_eng; float volchg = (hp_eng > last_hp_eng) ? (hp_eng - last_hp_eng) : (last_hp_eng - hp_eng); /* PSY_TD_THRESH = 0.5 */ if (volchg > (0.5f * toteng)) { sa->ch[ch].wantShort = 1; break; } } last_hp_eng = hp_eng; have_last = 1; } } /* Choose the temporal grid based on the strongest transient. Synchronizes * envelope borders across all channels to maintain spatial imaging. */ float frameStrength = 0.0f; int frameSlot = 0; for (int ch = 0; ch < nch; ch++) { if (sa->ch[ch].transientStrength > frameStrength) { frameStrength = sa->ch[ch].transientStrength; frameSlot = sa->ch[ch].transientSlot; } } int split = num_slots; /* default: single envelope spans the whole frame */ if (frameStrength > SBR_TRANSIENT_THRESH_DEFAULT) { int Ts = (num_slots > 0) ? frameSlot * SBR_NUM_TIME_SLOTS / num_slots : 0; /* 0..16 */ int rel = clamp_int((Ts - 2) / 2, 0, 3); int innerSbr = 2 * rel + 2; /* {2,4,6,8} */ sa->numEnvelopes = 2; sa->frameClass = SBR_FRAME_CLASS_VARFIX; sa->tEnv[0] = 0; sa->tEnv[1] = innerSbr; sa->tEnv[2] = SBR_NUM_TIME_SLOTS; sa->bsPointer = 0; split = clamp_int(innerSbr * num_slots / SBR_NUM_TIME_SLOTS, 1, num_slots - 1); } else { sa->numEnvelopes = 1; sa->frameClass = SBR_FRAME_CLASS_FIXFIX; sa->tEnv[0] = 0; sa->tEnv[1] = SBR_NUM_TIME_SLOTS; sa->bsPointer = 0; } /* Count slots per envelope for power normalization. */ sa->envSampled[0] = sa->envSampled[1] = 0; for (int slot = 0; slot < num_slots; slot++) { #if FAAC_SBR_DECIMATION > 1 if (slot % FAAC_SBR_DECIMATION != 0) continue; #endif int h = (sa->numEnvelopes > 1 && slot >= split) ? 1 : 0; sa->envSampled[h]++; } if (sa->envSampled[0] < 1) sa->envSampled[0] = 1; if (sa->numEnvelopes > 1 && sa->envSampled[1] < 1) sa->envSampled[1] = 1; /* Pass 2: Subband analysis. Accumulates energy across QMF bands within * the selected temporal envelopes. */ /* Only [kx, k2) feeds the envelope quantizer; bands below kx are core-coded * and never read, so skip their post-FFT extraction and accumulation. */ int kx = sbr ? sbr->kx : 0; int kEnd = sbr ? sbr->k2 : SBR_QMF_BANDS_64; for (int ch = 0; ch < nch; ch++) { memset(sa->ch[ch].bandHalfE, 0, sizeof(sa->ch[ch].bandHalfE)); if (sbr) { memcpy(workspace, sbr->ch[ch].qmfOvl64, SBR_QMF_OVL_LEN_64 * sizeof(float)); memcpy(workspace + SBR_QMF_OVL_LEN_64, fullPtrs[ch], numSamples * sizeof(float)); for (int slot = 0; slot < num_slots; slot++) { #if FAAC_SBR_DECIMATION > 1 if (slot % FAAC_SBR_DECIMATION == 0) #endif { float slotEnergy[SBR_QMF_BANDS_64]; SbrQmfAnalysis(sbr, workspace + slot * SBR_QMF_BANDS_64, slotEnergy, kx, kEnd); int h = (sa->numEnvelopes > 1 && slot >= split) ? 1 : 0; float * restrict bE = sa->ch[ch].bandHalfE[h]; for (int k = kx; k < kEnd; k++) bE[k] += slotEnergy[k]; } } } } } knik0-faac-1950446/libfaac/sbr_analysis.h000066400000000000000000000031611522734263700200530ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Copyright (C) 2026 Nils Schimmelmann * * 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. */ #ifndef SBR_ANALYSIS_H #define SBR_ANALYSIS_H #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef __cplusplus extern "C" { #endif #ifndef SBR_QMF_BANDS_64 #define SBR_QMF_BANDS_64 64 #endif #ifndef SBR_MAX_ENVELOPES #define SBR_MAX_ENVELOPES 2 #endif #ifndef MAX_CHANNELS #define MAX_CHANNELS 64 #endif struct SBRInfo; typedef struct SignalAnalysisChannel { int transientSlot; float transientStrength; int wantShort; float lastVal; float bandHalfE[2][SBR_QMF_BANDS_64]; } SignalAnalysisChannel; typedef struct SignalAnalysis { int valid; int numSlots; int sampled; /* Frame envelope grid configuration. Synchronized across all channels. */ int frameClass; int numEnvelopes; int tEnv[SBR_MAX_ENVELOPES + 1]; int bsPointer; int envSampled[SBR_MAX_ENVELOPES]; SignalAnalysisChannel ch[MAX_CHANNELS]; } SignalAnalysis; void SbrAnalyze(SignalAnalysis *sa, float *fullPtrs[], int nch, int numSamples, struct SBRInfo *sbr); #ifdef __cplusplus } #endif #endif knik0-faac-1950446/libfaac/sbr_bitstream.c000066400000000000000000000227261522734263700202250ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Copyright (C) 2026 Nils Schimmelmann * * 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. */ #include #include "sbr.h" #include "sbr_internal.h" #include "sbr_tables.h" #include "bitstream.h" #include "channels.h" #include "util.h" #include "faac_internal.h" /* count-and-write helper: matches channels.c's WriteElement/WriteICS style -- * every write_sbr_* function below takes a `write` flag, always returns the * bit count, and only touches `bs` (which may be NULL) when `write` is set. */ static int put_huff(BitStream *bs, bool write, const SBRHuffEntry *table, int nsyms, int offset, int delta) { int sym = clamp_int(delta + offset, 0, nsyms - 1); if (write) PutBit(bs, table[sym].code, table[sym].len); return table[sym].len; } static int write_sbr_header(SBRInfo *sbr, BitStream *bs, bool write) { int bits = 0; #define WB(v,n) do { if (write) PutBit(bs,(v),(n)); bits += (n); } while(0) /* ISO 14496-3:2009 §4.6.18.5 sbr_header() (21 bits) */ WB(sbr->bs_amp_res, 1); /* bs_amp_res: 0=1.5dB, 1=3dB */ WB(sbr->bs_start_freq, 4); /* bs_start_freq: crossover index */ WB(sbr->bs_stop_freq, 4); /* bs_stop_freq: high-band ceil */ WB(sbr->bs_xover_band, 3); /* bs_xover_band: low-res split (0=none) */ WB(0, 2); /* bs_reserved */ WB(1, 1); /* bs_header_extra_1 = 1 (alter_scale present) */ WB(0, 1); /* bs_header_extra_2 = 0 (limiter fields absent) */ /* bs_header_extra_1 fields: */ WB(0, 2); /* bs_freq_scale = 0 (linear master table) */ WB(sbr->bs_alter_scale, 1); /* bs_alter_scale: 1=coarser at low bitrate */ WB(0, 2); /* bs_noise_bands = 0 (→ 1 noise band) */ #undef WB return bits; } /* Width of the transient pointer field, indexed by number of envelopes. */ static const int sbr_ceil_log2[] = { 0, 1, 2, 2, 3, 3 }; static int write_sbr_grid(SBRInfo *sbr, BitStream *bs, bool write) { int bits = 0; #define WB(v,n) do { if (write) PutBit(bs,(v),(n)); bits += (n); } while(0) if (sbr->frameClass == SBR_FRAME_CLASS_VARFIX) { /* VARFIX: variable leading borders, fixed trailing border. Mirrors the * inverse of FFmpeg read_sbr_grid()'s VARFIX case: t_env[0]=bs_var_bord_0, * each lead border adds 2*bs_rel+2, the trailing border is numTimeSlots * (not transmitted), then bs_pointer and per-envelope bs_freq_res. */ int num_env = sbr->numEnvelopes; WB(SBR_FRAME_CLASS_VARFIX, 2); WB(sbr->tEnv[0], 2); /* bs_var_bord_0 */ WB(num_env - 1, 2); /* bs_num_rel_0 */ for (int i = 0; i < num_env - 1; i++) WB((sbr->tEnv[i + 1] - sbr->tEnv[i] - 2) / 2, 2); /* bs_rel_bord */ WB(sbr->bsPointer, sbr_ceil_log2[num_env]); for (int i = 0; i < num_env; i++) /* bs_freq_res[1..num_env] */ WB(sbr->bs_freq_res, 1); } else { /* FIXFIX: equal-spaced borders, one bs_freq_res for all envelopes. */ WB(SBR_FRAME_CLASS_FIXFIX, 2); WB(sbr->numEnvelopes > 1 ? 1 : 0, 2); WB(sbr->bs_freq_res, 1); } #undef WB return bits; } static int write_sbr_dtdf(SBRInfo *sbr, BitStream *bs, bool write) { int n_q = sbr->numEnvelopes > 1 ? 2 : 1; int bits = sbr->numEnvelopes + n_q; if (write) for (int i = 0; i < bits; i++) PutBit(bs, 0, 1); return bits; } static int write_sbr_invf(SBRInfo *sbr, BitStream *bs, int ch, bool write) { if (write) for (int nb = 0; nb < sbr->numNoiseBands; nb++) PutBit(bs, sbr->ch[ch].invfMode, 2); return sbr->numNoiseBands * 2; } static int write_sbr_envelope(SBRInfo *sbr, BitStream *bs, int ch, bool write) { const SBRHuffEntry *table = sbr->eff_amp_res ? f_huff_env_3_0dB : f_huff_env_1_5dB; int nsyms = sbr->eff_amp_res ? F_HUFF_ENV_3_0DB_NSYMS : F_HUFF_ENV_1_5DB_NSYMS; int offset = sbr->eff_amp_res ? F_HUFF_ENV_3_0DB_OFFSET : F_HUFF_ENV_1_5DB_OFFSET; int bits = 0; for (int e = 0; e < sbr->numEnvelopes; e++) { for (int b = 0; b < sbr->numBands; b++) { int val = sbr->ch[ch].envData[e][b]; if (b == 0) { int first_bits = sbr->eff_amp_res ? 6 : 7; if (write) PutBit(bs, clamp_int(val, 0, (1 << first_bits) - 1), first_bits); bits += first_bits; } else { bits += put_huff(bs, write, table, nsyms, offset, val); } } } return bits; } static int write_sbr_noise(SBRInfo *sbr, BitStream *bs, int ch, bool write) { int n_q = sbr->numEnvelopes > 1 ? 2 : 1; int bits = 0; for (int ne = 0; ne < n_q; ne++) { for (int nb = 0; nb < sbr->numNoiseBands; nb++) { int val = sbr->ch[ch].noiseData[ne][nb]; if (nb == 0) { if (write) PutBit(bs, clamp_int(val, 0, 30), 5); bits += 5; } else { bits += put_huff(bs, write, f_huff_env_3_0dB, F_HUFF_ENV_3_0DB_NSYMS, F_HUFF_ENV_3_0DB_OFFSET, val); } } } return bits; } static int write_sbr_data(SBRInfo *sbr, BitStream *bs, int id_aac, bool write) { int bits = 0; #define WB(v,n) do { if (write) PutBit(bs,(v),(n)); bits += (n); } while(0) if (id_aac == ID_CPE) { WB(0, 1); WB(0, 1); /* bs_coupling=0, reserved */ bits += write_sbr_grid(sbr, bs, write); bits += write_sbr_grid(sbr, bs, write); bits += write_sbr_dtdf(sbr, bs, write); bits += write_sbr_dtdf(sbr, bs, write); bits += write_sbr_invf(sbr, bs, 0, write); bits += write_sbr_invf(sbr, bs, 1, write); bits += write_sbr_envelope(sbr, bs, 0, write); bits += write_sbr_envelope(sbr, bs, 1, write); bits += write_sbr_noise(sbr, bs, 0, write); bits += write_sbr_noise(sbr, bs, 1, write); WB(0, 1); WB(0, 1); WB(0, 1); /* add_harmonic / extended data flags */ } else { WB(0, 1); /* reserved */ bits += write_sbr_grid(sbr, bs, write); bits += write_sbr_dtdf(sbr, bs, write); bits += write_sbr_invf(sbr, bs, 0, write); bits += write_sbr_envelope(sbr, bs, 0, write); bits += write_sbr_noise(sbr, bs, 0, write); WB(0, 1); WB(0, 1); /* add_harmonic / extended data flags */ } #undef WB return bits; } /* Emit the full extension_payload body for EXT_SBR_DATA: the 4-bit extension * type, the 1-bit header flag, the optional header, and the channel data. */ static int emit_sbr_payload(SBRInfo *sbr, BitStream *bs, int id_aac, int sendHeader, bool write) { int bits = 0; #define WB(v,n) do { if (write) PutBit(bs,(v),(n)); bits += (n); } while(0) WB(SBR_EXT_TYPE_SBR, 4); WB(sendHeader, 1); #undef WB if (sendHeader) bits += write_sbr_header(sbr, bs, write); bits += write_sbr_data(sbr, bs, id_aac, write); return bits; } int SbrWrite(SBRInfo *sbr, BitStream *bs, int id_aac, int writeFlag) { if (!sbr || !sbr->sbrPresent) return 0; int sendHeader = sbr->sendHeaderThisFrame; /* The fill_element's cnt field must precede the payload in the bitstream, * so its size is needed before anything is written. Re-deriving it with a * dry (write=false) pass is cheap -- a few hundred fixed-width/Huffman * fields, not a hot loop -- so there's no need to cache the emitted bits * across a frame's several SbrWrite calls (BuildFrame's count and write * passes, plus frame.c's rate-control bit-accounting call): every call * just re-derives them from sbr's already-quantized envelope/noise data, * the same way channels.c's WriteElement/WriteICS do for the rest of the * frame. */ int payloadBits = emit_sbr_payload(sbr, NULL, id_aac, sendHeader, false); int fillBytes = (payloadBits + 7) / 8; int padBits = fillBytes * 8 - payloadBits; /* The fill_element count escapes through an 8-bit field, so a single * extension_payload tops out at 15 + 255 - 1 = 269 bytes. A larger SBR * payload would silently truncate esc_count and corrupt the boundary. */ assert(fillBytes <= 14 + 255); int totalBits = 0; #define WB(v,n) do { if (writeFlag) PutBit(bs,(v),(n)); totalBits += (n); } while(0) /* fill_element(): id, then 4-bit count with optional 8-bit escape. The * decoder reconstructs cnt = 15 + esc_count - 1, hence esc_count = N - 14. */ WB(ID_FIL, 3); if (fillBytes < 15) WB(fillBytes, 4); else { WB(15, 4); WB(fillBytes - 14, 8); } #undef WB if (writeFlag) emit_sbr_payload(sbr, bs, id_aac, sendHeader, true); totalBits += payloadBits; if (padBits > 0) { if (writeFlag) PutBit(bs, 0, padBits); totalBits += padBits; } if (writeFlag) { sbr->headerSent = 1; sbr->frameCount++; } return totalBits; } int SbrContextGetBits(SBRContext *sCtx, BitStream *bs, int channels, int aacObjectType, int writeFlag) { if (aacObjectType == HE_V1 && sCtx) { if (sCtx->sbrInfo) { int id_aac = (channels > 1) ? ID_CPE : ID_SCE; return SbrWrite(sCtx->sbrInfo, bs, id_aac, writeFlag); } } return 0; } knik0-faac-1950446/libfaac/sbr_internal.h000066400000000000000000000107041522734263700200450ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Copyright (C) 2026 Nils Schimmelmann * * 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. */ #ifndef SBR_INTERNAL_H #define SBR_INTERNAL_H #include "sbr.h" #include "sbr_analysis.h" #include "resample.h" /* Per-channel SBR state. Everything indexed [ch] in SBRInfo lives here. */ typedef struct SBRChannel { float qmfOvl64[SBR_QMF_OVL_LEN_64]; /* QMF overlap state (carries across frames) */ int envData [SBR_MAX_ENVELOPES][SBR_MAX_BANDS]; /* quantised envelope indices */ int noiseData[SBR_MAX_NOISE_ENVELOPES][SBR_MAX_NOISE_BANDS]; /* quantised noise floor indices */ int invfMode; /* bs_invf_mode (0–3) */ } SBRChannel; struct SBRInfo { int sbrPresent; int headerSent; int frameCount; int numChannels; int sampleRate; /* full output rate; the dual-rate core runs at sampleRate/2 */ /* --- frequency band configuration (set at init, constant per stream) --- */ int kx; int k2; int dk; /* master frequency table step (1 or 2 QMF bands) */ int numBands; int bandEdges[SBR_MAX_BANDS + 1]; int numNoiseBands; /* --- bitstream header fields --- */ int bs_amp_res; int bs_freq_res; /* envelope frequency resolution: 1 = HIGH (f_master) */ int bs_start_freq; int bs_stop_freq; int bs_xover_band; int bs_alter_scale; /* --- per-frame state --- */ int numEnvelopes; /* 1 or 2, set by transient detection in SbrEncode */ int eff_amp_res; /* forced to 0 for single-envelope FIXFIX (ISO 14496-3:2009 §4.6.18.3) */ /* Envelope time grid for the frame (frame-global, shared by both channels of * a CPE). frameClass selects FIXFIX or VARFIX; tEnv[0..numEnvelopes] are the * envelope borders in SBR time slots ([0, SBR_NUM_TIME_SLOTS]) and are only * emitted for the variable classes. bsPointer marks the transient envelope. */ int frameClass; int tEnv[SBR_MAX_ENVELOPES + 1]; int bsPointer; /* Whether SbrWrite should (re)send the sbr_header this frame. Frozen once * per frame (in SbrEncode) rather than recomputed in SbrWrite, since * headerSent/frameCount only advance on SbrWrite's real write pass, and * SbrWrite is called multiple times per frame (BuildFrame's count and * write passes, plus frame.c's rate-control bit-accounting call). */ int sendHeaderThisFrame; /* --- per-channel state --- */ SBRChannel ch[MAX_CHANNELS]; /* QMF analysis twiddle factors. */ float twidCos[SBR_QMF_BANDS_64]; float twidSin[SBR_QMF_BANDS_64]; float oddCos [SBR_QMF_BANDS_64]; float oddSin [SBR_QMF_BANDS_64]; FFT_Tables *fftTables; /* borrowed: the encoder's shared core FFT tables */ }; struct SBRContext { unsigned long fullSampleRate; unsigned int fullSampleRateIdx; SBRInfo *sbrInfo; struct Resampler *resampler; /* Shared signal analysis */ SignalAnalysis signalAnalysis; /* Shared-detector FIFO: holds the HE block-switch decision for the last SBR_DETECT_FIFO analyzed frames. Index 0 is the decision aligned to the core frame being coded now, which lags the freshest analysis by the core lookahead (LOOKAHEAD_DEPTH frames); newest sits at SBR_DETECT_FIFO-1. */ float transientStrengthFIFO[MAX_CHANNELS][SBR_DETECT_FIFO]; int wantShortFIFO[MAX_CHANNELS][SBR_DETECT_FIFO]; }; SBRInfo *SbrInit(int channels, int sampleRate, unsigned long bitRate, FFT_Tables *fft_tables); /* Recompute the bitrate-dependent band config without reallocating; lets * SetConfiguration adjust an existing handle. */ void SbrUpdate(SBRInfo *sbr, unsigned long bitRate); void SbrEnd(SBRInfo *sbr); void SbrQmfAnalysis(SBRInfo *sbr, const float * restrict ovl_pos, float * restrict energy, int kx, int k2); void SbrEncode(SBRInfo *sbr, float *timeDomain[MAX_CHANNELS], int numChannels, int numSamples, struct SignalAnalysis *sa); int SbrWrite(SBRInfo *sbr, struct BitStream *bs, int id_aac, int writeFlag); #endif knik0-faac-1950446/libfaac/sbr_tables.c000066400000000000000000000515611522734263700175040ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * SBR tables reproduced from ISO/IEC 14496-3:2005 (non-copyrightable facts) * * 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. */ #include "sbr_tables.h" const sbrfloat qmf_c[640] = { +0.0000000000e+00f, -5.5252865047e-04f, -5.6176925738e-04f, -4.9475180896e-04f, -4.8752279712e-04f, -4.8937912498e-04f, -5.0407143497e-04f, -5.2265642972e-04f, -5.4665656337e-04f, -5.6778025613e-04f, -5.8709304852e-04f, -6.1327473938e-04f, -6.3124935319e-04f, -6.5403333621e-04f, -6.7776907764e-04f, -6.9416146273e-04f, -7.1577364744e-04f, -7.2550431222e-04f, -7.4409418541e-04f, -7.4905980532e-04f, -7.6813719270e-04f, -7.7248485949e-04f, -7.8343322877e-04f, -7.7798694927e-04f, -7.8036647100e-04f, -7.8014496257e-04f, -7.7579773310e-04f, -7.6307935757e-04f, -7.5300014201e-04f, -7.3193571525e-04f, -7.2153919876e-04f, -6.9179375372e-04f, -6.6504150893e-04f, -6.3415949025e-04f, -5.9461189330e-04f, -5.5645763906e-04f, -5.1455722108e-04f, -4.6063254803e-04f, -4.0951214522e-04f, -3.5011758756e-04f, -2.8969811748e-04f, -2.0983373440e-04f, -1.4463809349e-04f, -6.1733440720e-05f, +1.3494974180e-05f, +1.0943831274e-04f, +2.0430170688e-04f, +2.9495311041e-04f, +4.0265402160e-04f, +5.1073884952e-04f, +6.2393761391e-04f, +7.4580258865e-04f, +8.6084433262e-04f, +9.8859883015e-04f, +1.1250155131e-03f, +1.2577884647e-03f, +1.3902494827e-03f, +1.5443219847e-03f, +1.6868083253e-03f, +1.8348265422e-03f, +1.9841140737e-03f, +2.1461583556e-03f, +2.3017254775e-03f, +2.4625616913e-03f, +2.6201758690e-03f, +2.7870464347e-03f, +2.9469447716e-03f, +3.1125420653e-03f, +3.2739613485e-03f, +3.4418874183e-03f, +3.6008268123e-03f, +3.7603922910e-03f, +3.9207432370e-03f, +4.0819753194e-03f, +4.2264269227e-03f, +4.3730719678e-03f, +4.5209852783e-03f, +4.6606460612e-03f, +4.7932560850e-03f, +4.9137603574e-03f, +5.0393022601e-03f, +5.1407353903e-03f, +5.2461166132e-03f, +5.3471681198e-03f, +5.4196775931e-03f, +5.4876040151e-03f, +5.5475714509e-03f, +5.5938023005e-03f, +5.6220643210e-03f, +5.6455196916e-03f, +5.6389199515e-03f, +5.6266114193e-03f, +5.5917128663e-03f, +5.5404363940e-03f, +5.4753783077e-03f, +5.3838975897e-03f, +5.2715758727e-03f, +5.1382275451e-03f, +4.9839687763e-03f, +4.8109469060e-03f, +4.6039530147e-03f, +4.3801861745e-03f, +4.1251642327e-03f, +3.8456408125e-03f, +3.5401246551e-03f, +3.2091885810e-03f, +2.8446757862e-03f, +2.4508540032e-03f, +2.0274176185e-03f, +1.5784682577e-03f, +1.0902329051e-03f, +5.8322642480e-04f, +2.7604519050e-05f, -5.4642808664e-04f, -1.1568135523e-03f, -1.8039472589e-03f, -2.4826723645e-03f, -3.1933778390e-03f, -3.9401124052e-03f, -4.7222596240e-03f, -5.5337211109e-03f, -6.3792293269e-03f, -7.2615816852e-03f, -8.1798233373e-03f, -9.1325329609e-03f, -1.0115021550e-02f, -1.1131554803e-02f, -1.2184999595e-02f, +1.3271822004e-02f, +1.4390466608e-02f, +1.5540555334e-02f, +1.6732471300e-02f, +1.7943338134e-02f, +1.9187243137e-02f, +2.0453179336e-02f, +2.1746755025e-02f, +2.3068016929e-02f, +2.4416099203e-02f, +2.5787584755e-02f, +2.7185942963e-02f, +2.8607217364e-02f, +3.0050265743e-02f, +3.1501760874e-02f, +3.2975408103e-02f, +3.4462094877e-02f, +3.5969756055e-02f, +3.7481285043e-02f, +3.9005367947e-02f, +4.0534917056e-02f, +4.2064909464e-02f, +4.3609754213e-02f, +4.5148840564e-02f, +4.6684302726e-02f, +4.8216572007e-02f, +4.9738575560e-02f, +5.1255615552e-02f, +5.2763074652e-02f, +5.4245276836e-02f, +5.5717364821e-02f, +5.7161645013e-02f, +5.8591568363e-02f, +5.9983748018e-02f, +6.1345517172e-02f, +6.2685780812e-02f, +6.3971589807e-02f, +6.5224710644e-02f, +6.6436751221e-02f, +6.7607598512e-02f, +6.8704382835e-02f, +6.9763024471e-02f, +7.0762871073e-02f, +7.1700267311e-02f, +7.2568258331e-02f, +7.3362025508e-02f, +7.4100364243e-02f, +7.4745255812e-02f, +7.5313733620e-02f, +7.5800835866e-02f, +7.6199247934e-02f, +7.6499217041e-02f, +7.6709349042e-02f, +7.6817397570e-02f, +7.6823001139e-02f, +7.6720492417e-02f, +7.6505071832e-02f, +7.6174832185e-02f, +7.5730575651e-02f, +7.5157625529e-02f, +7.4466439476e-02f, +7.3640600576e-02f, +7.2677464273e-02f, +7.1582636479e-02f, +7.0353307351e-02f, +6.8966401320e-02f, +6.7452502152e-02f, +6.5769066865e-02f, +6.3944480596e-02f, +6.1960277904e-02f, +5.9816657081e-02f, +5.7515269199e-02f, +5.5046003430e-02f, +5.2409382174e-02f, +4.9597867634e-02f, +4.6630330517e-02f, +4.3476878220e-02f, +4.0145827841e-02f, +3.6641811681e-02f, +3.2958393067e-02f, +2.9082400601e-02f, +2.5030756189e-02f, +2.0799707286e-02f, +1.6370125822e-02f, +1.1762383279e-02f, +6.9636862162e-03f, +1.9765601450e-03f, -3.2086896830e-03f, -8.5711749137e-03f, -1.4128882736e-02f, -1.9883412926e-02f, -2.5822728881e-02f, -3.1953127453e-02f, -3.8277657208e-02f, -4.4780682159e-02f, -5.1480417679e-02f, -5.8370532683e-02f, -6.5440985314e-02f, -7.2694330081e-02f, -8.0137293443e-02f, -8.7754753656e-02f, -9.5553335289e-02f, -1.0353295311e-01f, -1.1168269318e-01f, -1.2000779847e-01f, -1.2850028504e-01f, -1.3715517612e-01f, -1.4597664912e-01f, -1.5496070711e-01f, -1.6409588557e-01f, -1.7338081722e-01f, -1.8281725485e-01f, -1.9239667457e-01f, -2.0212501768e-01f, -2.1197358538e-01f, -2.2196526964e-01f, -2.3206908707e-01f, -2.4230168846e-01f, -2.5264803096e-01f, -2.6310532995e-01f, -2.7366340406e-01f, -2.8432141891e-01f, -2.9507167171e-01f, -3.0590985752e-01f, -3.1682789136e-01f, -3.2781137272e-01f, -3.3887226939e-01f, -3.4999141229e-01f, +3.6115899031e-01f, +3.7237955463e-01f, +3.8363500139e-01f, +3.9492117616e-01f, +4.0623176768e-01f, +4.1756968968e-01f, +4.2891199207e-01f, +4.4025537544e-01f, +4.5159965357e-01f, +4.6293080853e-01f, +4.7424532146e-01f, +4.8552530911e-01f, +4.9677082546e-01f, +5.0798175000e-01f, +5.1912349702e-01f, +5.3022408957e-01f, +5.4125534487e-01f, +5.5220512585e-01f, +5.6307891401e-01f, +5.7385241317e-01f, +5.8454032355e-01f, +5.9511230862e-01f, +6.0557835389e-01f, +6.1591099320e-01f, +6.2612426956e-01f, +6.3619801077e-01f, +6.4612696959e-01f, +6.5590163025e-01f, +6.6551398802e-01f, +6.7496631902e-01f, +6.8423532935e-01f, +6.9332823767e-01f, +7.0223887194e-01f, +7.1094104263e-01f, +7.1944626350e-01f, +7.2774489003e-01f, +7.3582117583e-01f, +7.4368278636e-01f, +7.5131374561e-01f, +7.5870807608e-01f, +7.6586748651e-01f, +7.7277808813e-01f, +7.7942875190e-01f, +7.8583531204e-01f, +7.9197358416e-01f, +7.9784664138e-01f, +8.0344857519e-01f, +8.0876950044e-01f, +8.1381912706e-01f, +8.1857760046e-01f, +8.2304198905e-01f, +8.2722753473e-01f, +8.3110384572e-01f, +8.3469373618e-01f, +8.3797173379e-01f, +8.4095413925e-01f, +8.4362382812e-01f, +8.4598184698e-01f, +8.4803157771e-01f, +8.4978051984e-01f, +8.5119715249e-01f, +8.5230470352e-01f, +8.5310209497e-01f, +8.5357205739e-01f, +8.5373856006e-01f, +8.5357205739e-01f, +8.5310209497e-01f, +8.5230470352e-01f, +8.5119715249e-01f, +8.4978051984e-01f, +8.4803157771e-01f, +8.4598184698e-01f, +8.4362382812e-01f, +8.4095413925e-01f, +8.3797173379e-01f, +8.3469373618e-01f, +8.3110384572e-01f, +8.2722753473e-01f, +8.2304198905e-01f, +8.1857760046e-01f, +8.1381912706e-01f, +8.0876950044e-01f, +8.0344857519e-01f, +7.9784664138e-01f, +7.9197358416e-01f, +7.8583531204e-01f, +7.7942875190e-01f, +7.7277808813e-01f, +7.6586748651e-01f, +7.5870807608e-01f, +7.5131374561e-01f, +7.4368278636e-01f, +7.3582117583e-01f, +7.2774489003e-01f, +7.1944626350e-01f, +7.1094104263e-01f, +7.0223887194e-01f, +6.9332823767e-01f, +6.8423532935e-01f, +6.7496631902e-01f, +6.6551398802e-01f, +6.5590163025e-01f, +6.4612696959e-01f, +6.3619801077e-01f, +6.2612426956e-01f, +6.1591099320e-01f, +6.0557835389e-01f, +5.9511230862e-01f, +5.8454032355e-01f, +5.7385241317e-01f, +5.6307891401e-01f, +5.5220512585e-01f, +5.4125534487e-01f, +5.3022408957e-01f, +5.1912349702e-01f, +5.0798175000e-01f, +4.9677082546e-01f, +4.8552530911e-01f, +4.7424532146e-01f, +4.6293080853e-01f, +4.5159965357e-01f, +4.4025537544e-01f, +4.2891199207e-01f, +4.1756968968e-01f, +4.0623176768e-01f, +3.9492117616e-01f, +3.8363500139e-01f, +3.7237955463e-01f, -3.6115899031e-01f, -3.4999141229e-01f, -3.3887226939e-01f, -3.2781137272e-01f, -3.1682789136e-01f, -3.0590985752e-01f, -2.9507167171e-01f, -2.8432141891e-01f, -2.7366340406e-01f, -2.6310532995e-01f, -2.5264803096e-01f, -2.4230168846e-01f, -2.3206908707e-01f, -2.2196526964e-01f, -2.1197358538e-01f, -2.0212501768e-01f, -1.9239667457e-01f, -1.8281725485e-01f, -1.7338081722e-01f, -1.6409588557e-01f, -1.5496070711e-01f, -1.4597664912e-01f, -1.3715517612e-01f, -1.2850028504e-01f, -1.2000779847e-01f, -1.1168269318e-01f, -1.0353295311e-01f, -9.5553335289e-02f, -8.7754753656e-02f, -8.0137293443e-02f, -7.2694330081e-02f, -6.5440985314e-02f, -5.8370532683e-02f, -5.1480417679e-02f, -4.4780682159e-02f, -3.8277657208e-02f, -3.1953127453e-02f, -2.5822728881e-02f, -1.9883412926e-02f, -1.4128882736e-02f, -8.5711749137e-03f, -3.2086896830e-03f, +1.9765601450e-03f, +6.9636862162e-03f, +1.1762383279e-02f, +1.6370125822e-02f, +2.0799707286e-02f, +2.5030756189e-02f, +2.9082400601e-02f, +3.2958393067e-02f, +3.6641811681e-02f, +4.0145827841e-02f, +4.3476878220e-02f, +4.6630330517e-02f, +4.9597867634e-02f, +5.2409382174e-02f, +5.5046003430e-02f, +5.7515269199e-02f, +5.9816657081e-02f, +6.1960277904e-02f, +6.3944480596e-02f, +6.5769066865e-02f, +6.7452502152e-02f, +6.8966401320e-02f, +7.0353307351e-02f, +7.1582636479e-02f, +7.2677464273e-02f, +7.3640600576e-02f, +7.4466439476e-02f, +7.5157625529e-02f, +7.5730575651e-02f, +7.6174832185e-02f, +7.6505071832e-02f, +7.6720492417e-02f, +7.6823001139e-02f, +7.6817397570e-02f, +7.6709349042e-02f, +7.6499217041e-02f, +7.6199247934e-02f, +7.5800835866e-02f, +7.5313733620e-02f, +7.4745255812e-02f, +7.4100364243e-02f, +7.3362025508e-02f, +7.2568258331e-02f, +7.1700267311e-02f, +7.0762871073e-02f, +6.9763024471e-02f, +6.8704382835e-02f, +6.7607598512e-02f, +6.6436751221e-02f, +6.5224710644e-02f, +6.3971589807e-02f, +6.2685780812e-02f, +6.1345517172e-02f, +5.9983748018e-02f, +5.8591568363e-02f, +5.7161645013e-02f, +5.5717364821e-02f, +5.4245276836e-02f, +5.2763074652e-02f, +5.1255615552e-02f, +4.9738575560e-02f, +4.8216572007e-02f, +4.6684302726e-02f, +4.5148840564e-02f, +4.3609754213e-02f, +4.2064909464e-02f, +4.0534917056e-02f, +3.9005367947e-02f, +3.7481285043e-02f, +3.5969756055e-02f, +3.4462094877e-02f, +3.2975408103e-02f, +3.1501760874e-02f, +3.0050265743e-02f, +2.8607217364e-02f, +2.7185942963e-02f, +2.5787584755e-02f, +2.4416099203e-02f, +2.3068016929e-02f, +2.1746755025e-02f, +2.0453179336e-02f, +1.9187243137e-02f, +1.7943338134e-02f, +1.6732471300e-02f, +1.5540555334e-02f, +1.4390466608e-02f, -1.3271822004e-02f, -1.2184999595e-02f, -1.1131554803e-02f, -1.0115021550e-02f, -9.1325329609e-03f, -8.1798233373e-03f, -7.2615816852e-03f, -6.3792293269e-03f, -5.5337211109e-03f, -4.7222596240e-03f, -3.9401124052e-03f, -3.1933778390e-03f, -2.4826723645e-03f, -1.8039472589e-03f, -1.1568135523e-03f, -5.4642808664e-04f, +2.7604519050e-05f, +5.8322642480e-04f, +1.0902329051e-03f, +1.5784682577e-03f, +2.0274176185e-03f, +2.4508540032e-03f, +2.8446757862e-03f, +3.2091885810e-03f, +3.5401246551e-03f, +3.8456408125e-03f, +4.1251642327e-03f, +4.3801861745e-03f, +4.6039530147e-03f, +4.8109469060e-03f, +4.9839687763e-03f, +5.1382275451e-03f, +5.2715758727e-03f, +5.3838975897e-03f, +5.4753783077e-03f, +5.5404363940e-03f, +5.5917128663e-03f, +5.6266114193e-03f, +5.6389199515e-03f, +5.6455196916e-03f, +5.6220643210e-03f, +5.5938023005e-03f, +5.5475714509e-03f, +5.4876040151e-03f, +5.4196775931e-03f, +5.3471681198e-03f, +5.2461166132e-03f, +5.1407353903e-03f, +5.0393022601e-03f, +4.9137603574e-03f, +4.7932560850e-03f, +4.6606460612e-03f, +4.5209852783e-03f, +4.3730719678e-03f, +4.2264269227e-03f, +4.0819753194e-03f, +3.9207432370e-03f, +3.7603922910e-03f, +3.6008268123e-03f, +3.4418874183e-03f, +3.2739613485e-03f, +3.1125420653e-03f, +2.9469447716e-03f, +2.7870464347e-03f, +2.6201758690e-03f, +2.4625616913e-03f, +2.3017254775e-03f, +2.1461583556e-03f, +1.9841140737e-03f, +1.8348265422e-03f, +1.6868083253e-03f, +1.5443219847e-03f, +1.3902494827e-03f, +1.2577884647e-03f, +1.1250155131e-03f, +9.8859883015e-04f, +8.6084433262e-04f, +7.4580258865e-04f, +6.2393761391e-04f, +5.1073884952e-04f, +4.0265402160e-04f, +2.9495311041e-04f, +2.0430170688e-04f, +1.0943831274e-04f, +1.3494974180e-05f, -6.1733440720e-05f, -1.4463809349e-04f, -2.0983373440e-04f, -2.8969811748e-04f, -3.5011758756e-04f, -4.0951214522e-04f, -4.6063254803e-04f, -5.1455722108e-04f, -5.5645763906e-04f, -5.9461189330e-04f, -6.3415949025e-04f, -6.6504150893e-04f, -6.9179375372e-04f, -7.2153919876e-04f, -7.3193571525e-04f, -7.5300014201e-04f, -7.6307935757e-04f, -7.7579773310e-04f, -7.8014496257e-04f, -7.8036647100e-04f, -7.7798694927e-04f, -7.8343322877e-04f, -7.7248485949e-04f, -7.6813719270e-04f, -7.4905980532e-04f, -7.4409418541e-04f, -7.2550431222e-04f, -7.1577364744e-04f, -6.9416146273e-04f, -6.7776907764e-04f, -6.5403333621e-04f, -6.3124935319e-04f, -6.1327473938e-04f, -5.8709304852e-04f, -5.6778025613e-04f, -5.4665656337e-04f, -5.2265642972e-04f, -5.0407143497e-04f, -4.8937912498e-04f, -4.8752279712e-04f, -4.9475180896e-04f, -5.6176925738e-04f, -5.5252865047e-04f, }; const signed char sbr_offset[6][16] = { /* 16000 */ {-8,-7,-6,-5,-4,-3,-2,-1, 0, 1, 2, 3, 4, 5, 6, 7}, /* 22050 */ {-5,-4,-3,-2,-1, 0, 1, 2, 3, 4, 5, 6, 7, 9,11,13}, /* 24000 */ {-5,-3,-2,-1, 0, 1, 2, 3, 4, 5, 6, 7, 9,11,13,16}, /* 32000 */ {-6,-4,-2,-1, 0, 1, 2, 3, 4, 5, 6, 7, 9,11,13,16}, /* 44-64k */ {-4,-2,-1, 0, 1, 2, 3, 4, 5, 6, 7, 9,11,13,16,20}, /* >64k */ {-2,-1, 0, 1, 2, 3, 4, 5, 6, 7, 9,11,13,16,20,24}, }; const SBRHuffEntry f_huff_env_1_5dB[F_HUFF_ENV_1_5DB_NSYMS] = { /* -60 */ { 0x0007ffe7u, 19 }, /* -59 */ { 0x0007ffe8u, 19 }, /* -58 */ { 0x000fffd2u, 20 }, /* -57 */ { 0x000fffd3u, 20 }, /* -56 */ { 0x000fffd4u, 20 }, /* -55 */ { 0x000fffd5u, 20 }, /* -54 */ { 0x000fffd6u, 20 }, /* -53 */ { 0x000fffd7u, 20 }, /* -52 */ { 0x000fffd8u, 20 }, /* -51 */ { 0x0007ffdau, 19 }, /* -50 */ { 0x000fffd9u, 20 }, /* -49 */ { 0x000fffdau, 20 }, /* -48 */ { 0x000fffdbu, 20 }, /* -47 */ { 0x000fffdcu, 20 }, /* -46 */ { 0x0007ffdbu, 19 }, /* -45 */ { 0x000fffddu, 20 }, /* -44 */ { 0x0007ffdcu, 19 }, /* -43 */ { 0x0007ffddu, 19 }, /* -42 */ { 0x000fffdeu, 20 }, /* -41 */ { 0x0003ffe4u, 18 }, /* -40 */ { 0x000fffdfu, 20 }, /* -39 */ { 0x000fffe0u, 20 }, /* -38 */ { 0x000fffe1u, 20 }, /* -37 */ { 0x0007ffdeu, 19 }, /* -36 */ { 0x000fffe2u, 20 }, /* -35 */ { 0x000fffe3u, 20 }, /* -34 */ { 0x000fffe4u, 20 }, /* -33 */ { 0x0007ffdfu, 19 }, /* -32 */ { 0x000fffe5u, 20 }, /* -31 */ { 0x0007ffe0u, 19 }, /* -30 */ { 0x0003ffe8u, 18 }, /* -29 */ { 0x0007ffe1u, 19 }, /* -28 */ { 0x0003ffe0u, 18 }, /* -27 */ { 0x0003ffe9u, 18 }, /* -26 */ { 0x0001ffefu, 17 }, /* -25 */ { 0x0003ffe5u, 18 }, /* -24 */ { 0x0001ffecu, 17 }, /* -23 */ { 0x0001ffedu, 17 }, /* -22 */ { 0x0001ffeeu, 17 }, /* -21 */ { 0x0000fff4u, 16 }, /* -20 */ { 0x0000fff3u, 16 }, /* -19 */ { 0x0000fff0u, 16 }, /* -18 */ { 0x00007ff7u, 15 }, /* -17 */ { 0x00007ff6u, 15 }, /* -16 */ { 0x00003ffau, 14 }, /* -15 */ { 0x00001ffau, 13 }, /* -14 */ { 0x00001ff9u, 13 }, /* -13 */ { 0x00000ffau, 12 }, /* -12 */ { 0x00000ff8u, 12 }, /* -11 */ { 0x000007f9u, 11 }, /* -10 */ { 0x000003fbu, 10 }, /* -9 */ { 0x000001fcu, 9 }, /* -8 */ { 0x000001fau, 9 }, /* -7 */ { 0x000000fbu, 8 }, /* -6 */ { 0x0000007cu, 7 }, /* -5 */ { 0x0000003cu, 6 }, /* -4 */ { 0x0000001cu, 5 }, /* -3 */ { 0x0000000cu, 4 }, /* -2 */ { 0x00000005u, 3 }, /* -1 */ { 0x00000001u, 2 }, /* 0 */ { 0x00000000u, 2 }, /* +1 */ { 0x00000004u, 3 }, /* +2 */ { 0x0000000du, 4 }, /* +3 */ { 0x0000001du, 5 }, /* +4 */ { 0x0000003du, 6 }, /* +5 */ { 0x000000fau, 8 }, /* +6 */ { 0x000000fcu, 8 }, /* +7 */ { 0x000001fbu, 9 }, /* +8 */ { 0x000003fau, 10 }, /* +9 */ { 0x000007f8u, 11 }, /* +10 */ { 0x000007fau, 11 }, /* +11 */ { 0x000007fbu, 11 }, /* +12 */ { 0x00000ff9u, 12 }, /* +13 */ { 0x00000ffbu, 12 }, /* +14 */ { 0x00001ff8u, 13 }, /* +15 */ { 0x00001ffbu, 13 }, /* +16 */ { 0x00003ff8u, 14 }, /* +17 */ { 0x00003ff9u, 14 }, /* +18 */ { 0x0000fff1u, 16 }, /* +19 */ { 0x0000fff2u, 16 }, /* +20 */ { 0x0001ffeau, 17 }, /* +21 */ { 0x0001ffebu, 17 }, /* +22 */ { 0x0003ffe1u, 18 }, /* +23 */ { 0x0003ffe2u, 18 }, /* +24 */ { 0x0003ffeau, 18 }, /* +25 */ { 0x0003ffe3u, 18 }, /* +26 */ { 0x0003ffe6u, 18 }, /* +27 */ { 0x0003ffe7u, 18 }, /* +28 */ { 0x0003ffebu, 18 }, /* +29 */ { 0x000fffe6u, 20 }, /* +30 */ { 0x0007ffe2u, 19 }, /* +31 */ { 0x000fffe7u, 20 }, /* +32 */ { 0x000fffe8u, 20 }, /* +33 */ { 0x000fffe9u, 20 }, /* +34 */ { 0x000fffeau, 20 }, /* +35 */ { 0x000fffebu, 20 }, /* +36 */ { 0x000fffecu, 20 }, /* +37 */ { 0x0007ffe3u, 19 }, /* +38 */ { 0x000fffedu, 20 }, /* +39 */ { 0x000fffeeu, 20 }, /* +40 */ { 0x000fffefu, 20 }, /* +41 */ { 0x000ffff0u, 20 }, /* +42 */ { 0x0007ffe4u, 19 }, /* +43 */ { 0x000ffff1u, 20 }, /* +44 */ { 0x0003ffecu, 18 }, /* +45 */ { 0x000ffff2u, 20 }, /* +46 */ { 0x000ffff3u, 20 }, /* +47 */ { 0x0007ffe5u, 19 }, /* +48 */ { 0x0007ffe6u, 19 }, /* +49 */ { 0x000ffff4u, 20 }, /* +50 */ { 0x000ffff5u, 20 }, /* +51 */ { 0x000ffff6u, 20 }, /* +52 */ { 0x000ffff7u, 20 }, /* +53 */ { 0x000ffff8u, 20 }, /* +54 */ { 0x000ffff9u, 20 }, /* +55 */ { 0x000ffffau, 20 }, /* +56 */ { 0x000ffffbu, 20 }, /* +57 */ { 0x000ffffcu, 20 }, /* +58 */ { 0x000ffffdu, 20 }, /* +59 */ { 0x000ffffeu, 20 }, /* +60 */ { 0x000fffffu, 20 }, }; const SBRHuffEntry f_huff_env_3_0dB[F_HUFF_ENV_3_0DB_NSYMS] = { /* -31 */ { 0x000ffff0u, 20 }, /* -30 */ { 0x000ffff1u, 20 }, /* -29 */ { 0x000ffff2u, 20 }, /* -28 */ { 0x000ffff3u, 20 }, /* -27 */ { 0x000ffff4u, 20 }, /* -26 */ { 0x000ffff5u, 20 }, /* -25 */ { 0x000ffff6u, 20 }, /* -24 */ { 0x0003fff3u, 18 }, /* -23 */ { 0x0007fff5u, 19 }, /* -22 */ { 0x0007ffeeu, 19 }, /* -21 */ { 0x0007ffefu, 19 }, /* -20 */ { 0x0007fff6u, 19 }, /* -19 */ { 0x0003fff4u, 18 }, /* -18 */ { 0x0003fff2u, 18 }, /* -17 */ { 0x000ffff7u, 20 }, /* -16 */ { 0x0007fff0u, 19 }, /* -15 */ { 0x0001fff5u, 17 }, /* -14 */ { 0x0003fff0u, 18 }, /* -13 */ { 0x0001fff4u, 17 }, /* -12 */ { 0x0000fff7u, 16 }, /* -11 */ { 0x0000fff6u, 16 }, /* -10 */ { 0x00007ff8u, 15 }, /* -9 */ { 0x00003ffbu, 14 }, /* -8 */ { 0x00000ffdu, 12 }, /* -7 */ { 0x000007fdu, 11 }, /* -6 */ { 0x000003fdu, 10 }, /* -5 */ { 0x000001fdu, 9 }, /* -4 */ { 0x000000fdu, 8 }, /* -3 */ { 0x0000003eu, 6 }, /* -2 */ { 0x0000000eu, 4 }, /* -1 */ { 0x00000002u, 2 }, /* 0 */ { 0x00000000u, 1 }, /* +1 */ { 0x00000006u, 3 }, /* +2 */ { 0x0000001eu, 5 }, /* +3 */ { 0x000000fcu, 8 }, /* +4 */ { 0x000001fcu, 9 }, /* +5 */ { 0x000003fcu, 10 }, /* +6 */ { 0x000007fcu, 11 }, /* +7 */ { 0x00000ffcu, 12 }, /* +8 */ { 0x00001ffcu, 13 }, /* +9 */ { 0x00003ffau, 14 }, /* +10 */ { 0x00007ff9u, 15 }, /* +11 */ { 0x00007ffau, 15 }, /* +12 */ { 0x0000fff8u, 16 }, /* +13 */ { 0x0000fff9u, 16 }, /* +14 */ { 0x0001fff6u, 17 }, /* +15 */ { 0x0001fff7u, 17 }, /* +16 */ { 0x0003fff5u, 18 }, /* +17 */ { 0x0003fff6u, 18 }, /* +18 */ { 0x0003fff1u, 18 }, /* +19 */ { 0x000ffff8u, 20 }, /* +20 */ { 0x0007fff1u, 19 }, /* +21 */ { 0x0007fff2u, 19 }, /* +22 */ { 0x0007fff3u, 19 }, /* +23 */ { 0x000ffff9u, 20 }, /* +24 */ { 0x0007fff7u, 19 }, /* +25 */ { 0x0007fff4u, 19 }, /* +26 */ { 0x000ffffau, 20 }, /* +27 */ { 0x000ffffbu, 20 }, /* +28 */ { 0x000ffffcu, 20 }, /* +29 */ { 0x000ffffdu, 20 }, /* +30 */ { 0x000ffffeu, 20 }, /* +31 */ { 0x000fffffu, 20 }, }; knik0-faac-1950446/libfaac/sbr_tables.h000066400000000000000000000025771522734263700175140ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * SBR tables reproduced from ISO/IEC 14496-3 (non-copyrightable facts) * * 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. */ /* SBR tables: QMF prototype filter, frequency-band offsets, Huffman tables. * All values are normative data from ISO/IEC 14496-3:2005. */ #ifndef SBR_TABLES_H #define SBR_TABLES_H #include #ifdef __cplusplus extern "C" { #endif typedef float sbrfloat; typedef struct { uint32_t code : 24; uint32_t len : 8; } SBRHuffEntry; #define F_HUFF_ENV_1_5DB_OFFSET 60 #define F_HUFF_ENV_1_5DB_NSYMS 121 #define F_HUFF_ENV_3_0DB_OFFSET 31 #define F_HUFF_ENV_3_0DB_NSYMS 63 extern const sbrfloat qmf_c[640]; extern const signed char sbr_offset[6][16]; extern const SBRHuffEntry f_huff_env_1_5dB[F_HUFF_ENV_1_5DB_NSYMS]; extern const SBRHuffEntry f_huff_env_3_0dB[F_HUFF_ENV_3_0DB_NSYMS]; #ifdef __cplusplus } #endif #endif /* SBR_TABLES_H */ knik0-faac-1950446/libfaac/stereo.c000066400000000000000000000302771522734263700166660ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Copyright (C) 2026 Nils Schimmelmann * * 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. */ #define _USE_MATH_DEFINES #include #include #include "stereo.h" #include "huff2.h" #include "util.h" #include "faac_internal.h" /* Intensity stereo applies only at and above this frequency; below it the ear * localizes from waveform detail, so panning the band would be audible. */ #define IS_START_FREQ_HZ 5500 /* Upper bound on the crossover, as a fraction of the sample rate (0.35*Fs = * 0.7*Nyquist), so the band stays well inside the coded spectrum. */ #define IS_FREQ_CAP_NUM 7 #define IS_FREQ_CAP_DEN 20 /* Pan, in SF_STEP_ENRG steps, beyond which the quieter channel is inaudible * and is dropped to HCB_ZERO rather than intensity-coded. */ #define IS_PAN_LIMIT 30 /* Accumulate channel energies and cross-correlation for a scale factor band. * Using three independent accumulators maximizes instruction-level parallelism * by avoiding read-after-write dependencies on the FPU pipeline. */ static inline void calculate_energies(const float * restrict sl0, const float * restrict sr0, int start, int len, int wstart, int wend, float * restrict el_out, float * restrict er_out, float * restrict elr_out) { float el = 0, er = 0, elr = 0; int win, i; for (win = wstart; win < wend; win++) { const float * restrict sl = sl0 + win * BLOCK_LEN_SHORT + start; const float * restrict sr = sr0 + win * BLOCK_LEN_SHORT + start; for (i = 0; i < len; i++) { float l = sl[i]; float r = sr[i]; el += l * l; er += r * r; elr += l * r; } } *el_out = el; *er_out = er; *elr_out = elr; } /* Fast memory-clearing utility for suppressed channels. */ static inline void apply_mute(float * restrict s0, int start, int len, int wstart, int wend) { int win; for (win = wstart; win < wend; win++) { float * restrict s = s0 + win * BLOCK_LEN_SHORT + start; memset(s, 0, len * sizeof(float)); } } /* When one component (mid or side) dominates, collapse both channels to that * component and zero the other — it costs no bits and the signal loss is masked. * Factor of 0.5 keeps the coded amplitude on the same scale as L/R. */ static inline void apply_ms(float * restrict sl0, float * restrict sr0, int start, int len, int wstart, int wend, int in_phase) { int win, i; if (in_phase) { for (win = wstart; win < wend; win++) { float * restrict sl = sl0 + win * BLOCK_LEN_SHORT + start; float * restrict sr = sr0 + win * BLOCK_LEN_SHORT + start; for (i = 0; i < len; i++) { sl[i] = 0.5f * (sl[i] + sr[i]); sr[i] = 0.0f; } } } else { for (win = wstart; win < wend; win++) { float * restrict sl = sl0 + win * BLOCK_LEN_SHORT + start; float * restrict sr = sr0 + win * BLOCK_LEN_SHORT + start; for (i = 0; i < len; i++) { sr[i] = 0.5f * (sl[i] - sr[i]); sl[i] = 0.0f; } } } } static inline void apply_is(float * restrict sl0, float * restrict sr0, int start, int len, int wstart, int wend, int in_phase, float vfix) { int win, i; if (in_phase) { for (win = wstart; win < wend; win++) { float * restrict sl = sl0 + win * BLOCK_LEN_SHORT + start; float * restrict sr = sr0 + win * BLOCK_LEN_SHORT + start; for (i = 0; i < len; i++) { sl[i] = (sl[i] + sr[i]) * vfix; sr[i] = 0.0f; } } } else { for (win = wstart; win < wend; win++) { float * restrict sl = sl0 + win * BLOCK_LEN_SHORT + start; float * restrict sr = sr0 + win * BLOCK_LEN_SHORT + start; for (i = 0; i < len; i++) { sl[i] = (sl[i] - sr[i]) * vfix; sr[i] = 0.0f; } } } } /* Unified CPE element processing. Consolidating joint stereo modes into a single * pass minimizes cache misses on spectral data and allows the compiler to * optimize the mode-specific branches using constant propagation. */ static inline int process_cpe(CoderInfo * restrict cl, CoderInfo * restrict cr, AACElement * restrict element, float * restrict sl0, float * restrict sr0, int * restrict sfcnt, int wstart, int wend, float thrmid, float inv_isthr, float thrside_sq, int is_start_sfb, int mode) { int sfb, sfmin = (cl->block_type == ONLY_SHORT_WINDOW) ? 1 : 8, msused = 0; const int * restrict sfb_offset = cl->sfb_offset; if (mode == JOINT_IS) { *sfcnt += sfmin; } else { for (sfb = 0; sfb < sfmin; sfb++) element->msInfo.ms_used[(*sfcnt)++] = 0; } for (sfb = sfmin; sfb < cl->sfbn; sfb++) { int start = sfb_offset[sfb], len = sfb_offset[sfb+1] - start; float el, er, elr; calculate_energies(sl0, sr0, start, len, wstart, wend, &el, &er, &elr); float es = el + er + 2.0f*elr; float ed = el + er - 2.0f*elr; float etot = el + er; if (es < 0) es = 0; if (ed < 0) ed = 0; if (etot <= 0) { if (mode != JOINT_IS) element->msInfo.ms_used[*sfcnt] = 0; (*sfcnt)++; continue; } /* Intensity Stereo check. The threshold formula is expanded from * (sqrt(L)+sqrt(R))^2 to (L+R + 2*sqrt(L*R)) to eliminate one square * root per band while maintaining identical decision margins. */ if ((mode == JOINT_IS || (mode == JOINT_MIXED && sfb >= is_start_sfb)) && el > 0 && er > 0) { float th = (el + er + 2.0f * sqrtf(el * er)) * inv_isthr; int hcb = (es >= th) ? HCB_INTENSITY : (ed >= th ? HCB_INTENSITY2 : HCB_NONE); if (hcb != HCB_NONE) { float inv_etot = 1.0f / etot; int sf = lrintf(log10f(el * inv_etot) * SF_STEP_ENRG); int pan = lrintf(log10f(er * inv_etot) * SF_STEP_ENRG) - sf; /* Extreme pan: drop the inaudible channel to HCB_ZERO instead of * intensity-coding it, keeping the band cheap for the quantizer. */ if (pan > IS_PAN_LIMIT) { cl->book[*sfcnt] = HCB_ZERO; if (mode != JOINT_IS) element->msInfo.ms_used[*sfcnt] = 0; (*sfcnt)++; continue; } if (pan < -IS_PAN_LIMIT) { cr->book[*sfcnt] = HCB_ZERO; if (mode != JOINT_IS) element->msInfo.ms_used[*sfcnt] = 0; (*sfcnt)++; continue; } cl->sf[*sfcnt] = sf; cr->sf[*sfcnt] = -pan; cr->book[*sfcnt] = hcb; float dom = (hcb == HCB_INTENSITY) ? es : ed; apply_is(sl0, sr0, start, len, wstart, wend, hcb == HCB_INTENSITY, sqrtf(etot / dom)); if (mode != JOINT_IS) element->msInfo.ms_used[*sfcnt] = 0; (*sfcnt)++; continue; } } /* Mid/Side Stereo and Muting checks */ int ms = 0; if (mode == JOINT_MS || mode == JOINT_MIXED) { /* M/S fires when min(L,R) * thrmid ≥ dominant component: the weaker channel * contributes enough to justify the transform overhead. 0.25 accounts for halving. */ float em = 0.25f * es, side = 0.25f * ed; if (min(el, er) * thrmid >= max(em, side)) { if (em * thrmid * 2.0f >= etot) { ms = 1; apply_ms(sl0, sr0, start, len, wstart, wend, 1); } else if (side * thrmid * 2.0f >= etot) { ms = 1; apply_ms(sl0, sr0, start, len, wstart, wend, 0); } } if (ms) { msused = 1; } else { /* Sparsity check: if one channel completely masks the other. */ if (el <= er * thrside_sq) { apply_mute(sl0, start, len, wstart, wend); } else if (er <= el * thrside_sq) { apply_mute(sr0, start, len, wstart, wend); } } element->msInfo.ms_used[*sfcnt] = ms; } (*sfcnt)++; } return msused; } void AACstereo(CoderInfo *coder, AACElement *elements, int numElements, float *s[MAX_CHANNELS], float quality, int mode, int sampleRate) { float inv_quality = 1.0f / quality; float thrmid = 1.0f, isthr = 1.0f, thrside = 0.0f; switch (mode) { case JOINT_MIXED: thrmid = (0.09f * 0.85f) * inv_quality; if (thrmid > 0.25f) thrmid = 0.25f; thrmid += 1.0f; isthr = 0.18f * inv_quality + 1.0f; if (isthr > M_SQRT2) isthr = M_SQRT2; thrside = 0.1f * inv_quality; if (thrside > 0.3f) thrside = 0.3f; break; case JOINT_MS: thrmid = (1.09f - 1.0f) * inv_quality; if (thrmid > 0.25f) thrmid = 0.25f; thrmid += 1.0f; thrside = 0.1f * inv_quality; if (thrside > 0.3f) thrside = 0.3f; break; case JOINT_IS: isthr = 0.18f * (inv_quality * inv_quality); isthr += 1.0f; if (isthr > M_SQRT2) isthr = M_SQRT2; break; default: return; } /* Pre-square thresholds so per-band energy comparisons need no sqrt. * Each threshold scales inversely with quality — higher quality encodes * apply stereo coding more conservatively, touching the signal less. */ thrmid *= thrmid; float inv_isthr = 1.0f / (isthr * isthr); float thrside_sq = thrside * thrside; for (int e = 0; e < numElements; e++) { AACElement *elem = &elements[e]; if (elem->type != ID_CPE) continue; int lch = elem->channels[0]; int rch = elem->channels[1]; elem->common_window = false; elem->msInfo.is_present = false; if (coder[lch].block_type != coder[rch].block_type || coder[lch].groups.n != coder[rch].groups.n) continue; int ok = 1; for (int g = 0; g < coder[lch].groups.n; g++) { if (coder[lch].groups.len[g] != coder[rch].groups.len[g]) { ok = 0; break; } } if (!ok) continue; elem->common_window = true; elem->msInfo.is_present = (mode == JOINT_MS); int start = 0, sfcnt = 0, is_start_sfb = coder[lch].sfbn, msused = 0; if (mode == JOINT_MIXED) { int mlen = (coder[lch].block_type == ONLY_SHORT_WINDOW) ? 2*BLOCK_LEN_SHORT : 2*BLOCK_LEN_LONG; int ifreq = IS_START_FREQ_HZ, cap = (sampleRate * IS_FREQ_CAP_NUM) / IS_FREQ_CAP_DEN; if (ifreq > cap) ifreq = cap; for (int sfb = 0; sfb < coder[lch].sfbn; sfb++) { if ((coder[lch].sfb_offset[sfb] * sampleRate) / mlen >= ifreq) { is_start_sfb = sfb; break; } } } for (int g = 0; g < coder[lch].groups.n; g++) { int end = start + coder[lch].groups.len[g]; msused |= process_cpe(coder+lch, coder+rch, elem, s[lch], s[rch], &sfcnt, start, end, thrmid, inv_isthr, thrside_sq, is_start_sfb, mode); start = end; } if (mode == JOINT_MIXED && msused) elem->msInfo.is_present = true; } } knik0-faac-1950446/libfaac/stereo.h000066400000000000000000000017301522734263700166630ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Copyright (C) 2026 Nils Schimmelmann * * 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. */ #ifndef STEREO_H #define STEREO_H #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "channels.h" #include "util.h" void AACstereo(CoderInfo *coder, AACElement *elements, int numElements, float *s[MAX_CHANNELS], float quality, int mode, int sampleRate ); #endif knik0-faac-1950446/libfaac/tns.c000066400000000000000000000313501522734263700161620ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Copyright (C) 2026 Nils Schimmelmann * * 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. */ #include #include #include "frame.h" #include "coder.h" #include "tns.h" #include "util.h" /* Per-sample-rate scalefactor-band range TNS is allowed to filter over, from * ISO/IEC 13818-7/14496-3's TNS tool tables (indexed by sampleRateIdx). Not * an original heuristic: this is the spec's fixed table. */ static const struct { unsigned char min; unsigned char max; } tns_sfb_range[12] = { {11, 31}, {12, 31}, {15, 34}, {16, 40}, {17, 42}, {20, 51}, {25, 46}, {26, 46}, {24, 42}, {28, 42}, {30, 42}, {31, 39} }; #define TNS_LPC_ORDER 8 /* fixed filter order; spec allows up to TNS_MAX_ORDER but higher orders rarely paid for themselves here */ #define TNS_GAIN_LIMIT 1.4f /* Levinson-Durbin prediction gain below this isn't worth the filter's bit cost */ #define TNS_GAIN_CLAMP 6.0f /* gain above this means a near-singular fit (e.g. a single strong tone); reject rather than risk an unstable filter */ #define TNS_MEASURED_GAIN 1.4f /* post-quantization re-check: same bar as TNS_GAIN_LIMIT, applied to the filter actually being transmitted */ /* Below this, a band's spectral energy is indistinguishable from float * rounding noise, so there's nothing real for TNS to whiten. Also reused * below as the floor for RMS-normalization and the LPC residual check, * rather than inventing a separate near-zero constant for each. */ #define TNS_MIN_ENERGY 1e-9f /* SFM (geomean/arithmean of per-band RMS) near 1.0 means the band is * noise-like, which PNS (quantize.c) is about to replace anyway -- skip * TNS's LPC work there; it only pays off on tonal/peaky bands. */ #define TNS_PNS_SFM_SKIP 0.85f static void calc_autocorr_f(int order, int length, const float * work, float * r) { int lag, i; for (lag = 0; lag <= order; lag++) { float acc = 0.0f; const float * p1 = work; const float * p2 = work + lag; int n = length - lag; for (i = 0; i < n; i++) acc += p1[i] * p2[i]; r[lag] = acc; } } /* Reflection coefficients are clamped to +-0.999, short of the unstable * +-1 limit, so the resulting filter is guaranteed stable regardless of * input. The returned prediction gain (r[0]/final residual error) lets * TnsEncode reject a bad fit before spending any bits on coefficients -- * free to report here, since the recursion already has both numbers. */ static float compute_lpc(int order, const float * r, float * k) { float a[TNS_MAX_ORDER + 1]; float err; int i, j; if (r[0] <= 0.0f) { for (i = 1; i <= order; i++) k[i] = 0.0f; return 1.0f; } err = r[0]; a[0] = 1.0f; for (i = 1; i <= order; i++) { float lambda = r[i]; float rc; int half; for (j = 1; j < i; j++) lambda += a[j] * r[i - j]; if (err <= 0.0f) { for (; i <= order; i++) k[i] = 0.0f; break; } rc = -lambda / err; if (rc > 0.999f) rc = 0.999f; else if (rc < -0.999f) rc = -0.999f; k[i] = rc; half = (i + 1) / 2; for (j = 1; j < half; j++) { float t1 = a[j]; float t2 = a[i - j]; a[j] = t1 + rc * t2; a[i - j] = t2 + rc * t1; } if (i % 2 == 0) a[i / 2] += rc * a[i / 2]; a[i] = rc; err *= (1.0f - rc * rc); if (err <= 0.0f) break; } /* err collapsing to ~0 means a (near-)perfect fit, which for real audio * means a degenerate input rather than a genuinely great filter. Return * a gain past TNS_GAIN_CLAMP so the caller's sanity check rejects it, * instead of dividing by ~0. */ if (err <= TNS_MIN_ENERGY) return TNS_GAIN_CLAMP + 1.0f; return r[0] / err; } /* Reflection coefficients live in (-1, 1); arcsine-warp them before * quantizing so equal code steps land closer to equal perceptual steps * near the +-1 ends, per the tns_data() coefficient law in the spec. */ static void quantize_coeffs(int order, int res, float * k, int * idx) { const float s_p = (float)(((1 << (res - 1)) - 0.5f) / (M_PI / 2)); const float s_n = (float)(((1 << (res - 1)) + 0.5f) / (M_PI / 2)); const int i_max = (1 << (res - 1)) - 1; const int i_min = -(1 << (res - 1)); int i; for (i = 1; i <= order; i++) { float val = k[i]; float s = (val >= 0.0f) ? s_p : s_n; int q = (int)(asinf(val) * s + ((val >= 0.0f) ? 0.5f : -0.5f)); if (q > i_max) q = i_max; else if (q < i_min) q = i_min; idx[i] = q; /* Re-derive k[] from the quantized index (not the original float) * so finalize_filter below builds the same filter the decoder will, * from the same lossy coefficients that actually get transmitted. */ s = (q >= 0) ? s_p : s_n; k[i] = sinf((float)q / s); } } /* filter_spec's recursion needs direct-form polynomial coefficients, not * reflection coefficients, so this runs the standard lattice-to-direct * step-up. Called after quantize_coeffs so the polynomial matches what's * actually transmitted, not the pre-quantization float values. */ static void finalize_filter(int order, const float * k, float * a) { int i, m; a[0] = 1.0f; for (m = 1; m <= order; m++) { float km = k[m]; int half = (m + 1) / 2; for (i = 1; i < half; i++) { float t1 = a[i]; float t2 = a[m - i]; a[i] = t1 + km * t2; a[m - i] = t2 + km * t1; } if (m % 2 == 0) a[m / 2] += km * a[m / 2]; a[m] = km; } } /* direction picks which end of the band the recursion runs from: TNS wants * the prediction to run towards the transient (so quantization noise piles * up where it'll be masked), and the transient can sit at either edge of * the analysis window. */ static void filter_spec(int length, int order, int direction, const float * a, float * spec) { float hist[BLOCK_LEN_LONG]; int i, j; memcpy(hist, spec, length * sizeof(float)); if (direction) { for (i = length - 1; i >= 0; i--) { float acc = hist[i]; int jmax = min(order, length - 1 - i); for (j = 1; j <= jmax; j++) acc += a[j] * hist[i + j]; spec[i] = acc; } } else { for (i = 0; i < length; i++) { float acc = hist[i]; int jmax = min(order, i); for (j = 1; j <= jmax; j++) acc += a[j] * hist[i - j]; spec[i] = acc; } } } void TnsInit(faacEncStruct* hEncoder) { unsigned int ch; int fs = hEncoder->sampleRateIdx; for (ch = 0; ch < hEncoder->numChannels; ch++) { TnsInfo *info = &hEncoder->coderInfo[ch].tnsInfo; info->tnsMaxBandsLong = tns_sfb_range[fs].max; info->tnsNumSwbLong = hEncoder->srInfo->num_cb_long; info->tnsMinBandNumberLong = tns_sfb_range[fs].min; } } void TnsEncode(TnsInfo* tnsInfo, int numBands, enum WINDOW_TYPE blockType, int* sfbOffsetTable, float* spec) { int b_start, b_stop, i_start, length; float *band, energy; float wspec[BLOCK_LEN_LONG]; float r[TNS_MAX_ORDER + 1] = {0}; float k[TNS_MAX_ORDER + 1] = {0}; float gain; TnsFilterData *filter; int order, limit, i; tnsInfo->tnsDataPresent = 0; tnsInfo->windowData.numFilters = 0; /* Short windows already have the temporal resolution to not need TNS. */ if (blockType == ONLY_SHORT_WINDOW) return; b_start = min(tnsInfo->tnsMinBandNumberLong, numBands); b_stop = min(tnsInfo->tnsMaxBandsLong, numBands); if (b_stop <= b_start) return; i_start = sfbOffsetTable[b_start]; length = sfbOffsetTable[b_stop] - i_start; if (length <= TNS_LPC_ORDER) return; band = spec + i_start; energy = 0.0f; for (i = 0; i < length; i++) energy += band[i] * band[i]; if (energy < TNS_MIN_ENERGY) return; /* Per-band RMS-normalize before autocorrelation, floored at 1% of the * loudest band's RMS. Un-normalized, Levinson-Durbin would fit whatever * band has the most energy and ignore quieter ones -- but pre-echo is * audible in quiet bands too, so the filter needs to whiten across the * whole range, not just the peak. */ { float maxrms = 0.0f, floorrms; float sum_rms = 0.0f, sum_log_rms = 0.0f; int nbands = b_stop - b_start; int b; for (b = b_start; b < b_stop; b++) { int s0 = sfbOffsetTable[b], s1 = sfbOffsetTable[b + 1]; float e = 0.0f, rms, rms_fl; for (i = s0; i < s1; i++) e += (float)(spec[i] * spec[i]); rms = sqrtf(e / (float)(s1 - s0)); if (rms > maxrms) maxrms = rms; /* rms_fl keeps logf() away from 0 for silent bands; folded into * the same loop as maxrms rather than a second pass. */ rms_fl = rms > TNS_MIN_ENERGY ? rms : TNS_MIN_ENERGY; sum_rms += rms_fl; sum_log_rms += logf(rms_fl); } /* Spectral flatness (geomean/arithmean of per-band RMS) near 1.0 * means the band is noise-like, which PNS (quantize.c) is about to * replace anyway -- skip the LPC work; it only pays off on * tonal/peaky bands. */ if (expf(sum_log_rms / (float)nbands) / (sum_rms / (float)nbands) > TNS_PNS_SFM_SKIP) return; floorrms = maxrms * 0.01f; if (floorrms < TNS_MIN_ENERGY) floorrms = TNS_MIN_ENERGY; for (b = b_start; b < b_stop; b++) { int s0 = sfbOffsetTable[b], s1 = sfbOffsetTable[b + 1]; float e = 0.0f, rms, wgt; for (i = s0; i < s1; i++) e += (float)(spec[i] * spec[i]); rms = sqrtf(e / (float)(s1 - s0)); wgt = 1.0f / (rms > floorrms ? rms : floorrms); for (i = s0; i < s1; i++) wspec[i - i_start] = (float)spec[i] * wgt; } } calc_autocorr_f(TNS_LPC_ORDER, length, wspec, r); gain = compute_lpc(TNS_LPC_ORDER, r, k); if (gain < TNS_GAIN_LIMIT || gain > TNS_GAIN_CLAMP) return; filter = &tnsInfo->windowData.tnsFilter[0]; quantize_coeffs(TNS_LPC_ORDER, DEF_TNS_COEFF_RES, k, filter->index); /* Drop trailing taps that quantized away to ~nothing: they cost bits * without changing what the filter does. */ order = TNS_LPC_ORDER; while (order > 0 && fabsf(k[order]) < (float)DEF_TNS_COEFF_THRESH) order--; if (order == 0) return; filter->order = order; filter->length = tnsInfo->tnsNumSwbLong - b_start; /* Direction is fixed rather than picked from a transient envelope; that * comes with FrameStrategy in a later commit. */ filter->direction = 0; /* Coefficients that all fit in one fewer bit each can be transmitted at * reduced resolution; the spec's coefCompress flag signals that. */ filter->coefCompress = 1; limit = 1 << (DEF_TNS_COEFF_RES - 2); for (i = 1; i <= order; i++) { if (filter->index[i] < -limit || filter->index[i] >= limit) { filter->coefCompress = 0; break; } } finalize_filter(order, k, filter->aCoeffs); /* compute_lpc's gain estimate was on the un-quantized coefficients; * quantization can erode it enough that the filter actually being * transmitted no longer pays for itself. Re-check on a trial run of the * real (quantized) filter before committing to writing it out. */ { float trial[BLOCK_LEN_LONG]; float orig_e = 0.0f, filt_e = 0.0f; memcpy(trial, wspec, length * sizeof(float)); filter_spec(length, order, filter->direction, filter->aCoeffs, trial); for (i = 0; i < length; i++) { orig_e += wspec[i] * wspec[i]; filt_e += trial[i] * trial[i]; } if (filt_e < TNS_MIN_ENERGY) filt_e = TNS_MIN_ENERGY; if (orig_e < TNS_MEASURED_GAIN * filt_e) return; } filter_spec(length, order, filter->direction, filter->aCoeffs, band); tnsInfo->windowData.numFilters = 1; tnsInfo->windowData.coefResolution = DEF_TNS_COEFF_RES; tnsInfo->tnsDataPresent = 1; } knik0-faac-1950446/libfaac/tns.h000066400000000000000000000025741522734263700161750ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Copyright (C) 2026 Nils Schimmelmann * * 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. */ /* * Temporal Noise Shaping (TNS): a predictive filter along the frequency axis * that reshapes quantization noise in time so it hides behind transients * instead of leaking out as pre-echo. Long-window only here; short windows * already have the temporal resolution to not need it. */ #ifndef TNS_H #define TNS_H #include "coder.h" #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ /* Latch the per-channel band limits from the sample rate's TNS tool table. */ void TnsInit(faacEncStruct* hEncoder); /* Analyse one channel and, if it pays off, whiten `spec` in place. */ void TnsEncode(TnsInfo* tnsInfo, int numBands, enum WINDOW_TYPE blockType, int* sfbOffsetTable, float* spec); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* TNS_H */ knik0-faac-1950446/libfaac/util.c000066400000000000000000000042731522734263700163370ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Copyright (C) 2001 Menno Bakker * * 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. */ #include "util.h" #include "coder.h" // FRAME_LEN #ifdef _MSC_VER #include #endif /* Returns the sample rate index */ int GetSRIndex(unsigned int sampleRate) { if (92017 <= sampleRate) return 0; if (75132 <= sampleRate) return 1; if (55426 <= sampleRate) return 2; if (46009 <= sampleRate) return 3; if (37566 <= sampleRate) return 4; if (27713 <= sampleRate) return 5; if (23004 <= sampleRate) return 6; if (18783 <= sampleRate) return 7; if (13856 <= sampleRate) return 8; if (11502 <= sampleRate) return 9; if (9391 <= sampleRate) return 10; return 11; } /* Returns the maximum bitrate for that sampling frequency */ unsigned int MaxBitrate(unsigned long sampleRate) { /* max ADTS frame size 8k */ return 0x2000 * 8 * (float)sampleRate/(float)FRAME_LEN; } /* Returns the minimum bitrate per channel for that sampling frequency */ unsigned int MinBitrate(void) { return 8000; } /* Portable CLZ (returns 32 for x==0); lets escape() get a magnitude's bit-length * in O(1) instead of the old shift-until-fits loop. */ int CountLeadingZeros(unsigned int x) { if (x == 0) return 32; #ifdef _MSC_VER unsigned long leading_zero; _BitScanReverse(&leading_zero, x); return 31 - leading_zero; #elif defined(__GNUC__) || defined(__clang__) return __builtin_clz(x); #else int n = 0; if (x <= 0x0000FFFF) { n += 16; x <<= 16; } if (x <= 0x00FFFFFF) { n += 8; x <<= 8; } if (x <= 0x0FFFFFFF) { n += 4; x <<= 4; } if (x <= 0x3FFFFFFF) { n += 2; x <<= 2; } if (x <= 0x7FFFFFFF) { n += 1; x <<= 1; } return n; #endif } knik0-faac-1950446/libfaac/util.h000066400000000000000000000033751522734263700163460ustar00rootroot00000000000000/* * FAAC - Freeware Advanced Audio Coder * Copyright (C) 2001 Menno Bakker * * 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. */ #ifndef UTIL_H #define UTIL_H #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ #include #include #ifndef max #define max(a, b) (((a) > (b)) ? (a) : (b)) #endif #ifndef min #define min(a, b) (((a) < (b)) ? (a) : (b)) #endif static inline int clamp_int(int x, int lo, int hi) { if (x < lo) return lo; if (x > hi) return hi; return x; } #ifndef M_PI #define M_PI 3.14159265358979323846f #endif #ifndef M_SQRT2 #define M_SQRT2 1.41421356237309504880f #endif /* Double-precision pi, for one-time table/window generation at encoder init * (FFT/MDCT twiddles, SBR QMF twiddles, MDCT window shapes) where the extra * precision is free — these run once, never in the per-sample hot path. */ #define M_PI_DOUBLE 3.14159265358979323846 /* Memory functions */ #define AllocMemory(size) malloc(size) #define FreeMemory(block) free(block) #define SetMemory(block, value, size) memset(block, value, size) int GetSRIndex(unsigned int sampleRate); unsigned int MaxBitrate(unsigned long sampleRate); unsigned int MinBitrate(void); int CountLeadingZeros(unsigned int x); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* UTIL_H */ knik0-faac-1950446/meson.build000066400000000000000000000023451522734263700157750ustar00rootroot00000000000000project('faac', 'c', version: '2.0.0', default_options: [ 'default_library=both', 'buildtype=release', 'warning_level=3', 'c_std=gnu11,c11', ]) add_global_arguments('-DHAVE_CONFIG_H', language: 'c') config_h = configuration_data() config_h.set_quoted('PACKAGE', meson.project_name()) config_h.set_quoted('PACKAGE_VERSION', meson.project_version()) cc = meson.get_compiler('c') foreach h: ['getopt.h', 'immintrin.h'] config_h.set('HAVE_' + h.underscorify().to_upper(), cc.has_header(h)) endforeach config_h.set('WORDS_BIGENDIAN', host_machine.endian() == 'big') libm = cc.find_library('m', required: false) cpu_family = target_machine.cpu_family() config_h.set('HAVE_SSE2', (cpu_family in ['x86', 'x86_64']) and config_h.get('HAVE_IMMINTRIN_H')) config_h.set('FAAC_SBR_DECIMATION', get_option('sbr-decimation')) config_h.set('MAX_CHANNELS', get_option('max-channels')) c_args = [] if cc.get_argument_syntax() == 'msvc' c_args += ['-DWIN32', '-DNDEBUG', '-D_WINDOWS', '-D_USRDLL', '-DLIBFAAC_DLL_EXPORTS'] endif configure_file( output: 'config.h', configuration: config_h ) subdir('docs') subdir('include') subdir('libfaac') if get_option('frontend') subdir('frontend') endif knik0-faac-1950446/meson_options.txt000066400000000000000000000016721522734263700172720ustar00rootroot00000000000000option('frontend', description: 'Build the frontend', type: 'boolean', value: true) # SBR spectral analysis density: analyse every Nth slot per frame. # 1 = full quality (every slot); higher values trade MOS for faster encode. # Max 8: above that, both halves of the frame are sampled but the gain is negligible. # Only affects HE-AAC; LC encodes are identical regardless of this setting. # 1: every slot -- reference quality (MOS d: 0) # 2: every 2nd -- ~20% faster HE-AAC encode; MOS d: -0.004 # 4: every 4th -- ~30% faster HE-AAC encode; MOS d: -0.015 # 8: every 8th -- ~36% faster HE-AAC encode; MOS d: -0.028 option('sbr-decimation', description: 'SBR spectral analysis density: analyse every Nth slot (1=full quality, max=8)', type: 'integer', min: 1, max: 8, value: 1) option('max-channels', description: 'Maximum number of channels', type: 'integer', min: 1, max: 8, value: 8)