diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..4362b49 --- /dev/null +++ b/COPYING @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0e844f9 --- /dev/null +++ b/Makefile @@ -0,0 +1,43 @@ +MAJOR := 0 +MINOR := 0 +PATCH := 0 + +PREFIX := /usr/local +BINDIR := bin +LIBDIR := lib +INCDIR := include + +LIBNAME := libopenaptx.so +SONAME := $(LIBNAME).$(MAJOR) +FILENAME := $(SONAME).$(MINOR).$(PATCH) + +UTILITIES := openaptxenc openaptxdec + +HEADERS := openaptx.h + +BUILD := $(FILENAME) $(SONAME) $(LIBNAME) $(UTILITIES) + +all: $(BUILD) + +install: $(BUILD) + mkdir -p $(DESTDIR)/$(PREFIX)/$(LIBDIR) + cp -a $(FILENAME) $(SONAME) $(LIBNAME) $(DESTDIR)/$(PREFIX)/$(LIBDIR) + mkdir -p $(DESTDIR)/$(PREFIX)/$(BINDIR) + cp -a $(UTILITIES) $(DESTDIR)/$(PREFIX)/$(BINDIR) + mkdir -p $(DESTDIR)/$(PREFIX)/$(INCDIR) + cp -a $(HEADERS) $(DESTDIR)/$(PREFIX)/$(INCDIR) + +$(FILENAME): openaptx.c $(HEADERS) + $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -I. -shared -fPIC -Wl,-soname,$(SONAME) -o $@ $< + +$(SONAME): $(FILENAME) + ln -sf $< $@ + +$(LIBNAME): $(SONAME) + ln -sf $< $@ + +%: %.c $(LIBNAME) $(HEADERS) + $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -I. -o $@ $< $(LIBNAME) + +clean: + $(RM) $(BUILD) diff --git a/README b/README new file mode 100644 index 0000000..67709ac --- /dev/null +++ b/README @@ -0,0 +1,30 @@ +This is Open Source implementation of Audio Processing Technology codec (aptX) +derived from ffmpeg 4.0 project and licensed under LGPLv2.1+. This codec is +mainly used in Bluetooth A2DP profile. + +It provides dynamic linked shared library libopenaptx.so and simple command line +utilities openaptxenc and openaptxdec for encoding and decoding operations. +There is support for aptX and aptX HD codec variants. Both variants operates on +a raw 24 bit signed stereo audio samples. aptX provides fixed compress ratio 6:1 +and aptX HD fixed compress ratio 4:1. + +For building and installing into system simply run: make install. For building +without installing run: LD_RUN_PATH='$ORIGIN' make + +Usage of command line utilities together with sox for resampling or playing: + +To convert Wave audio file sample.wav into aptX audio file sample.aptx run: + +$ sox sample.wav -t raw -r 44.1k -s -3 -c 2 - | openaptxenc > sample.aptx + +To convert aptX audio file sample.aptx into Wave audio file sample.wav run: + +$ openaptxdec < sample.aptx | sox -t raw -r 44.1k -s -3 -c 2 - sample.wav + +To convert MP3 audio file sample.mp3 into aptX HD audio file sample.aptxhd run: + +$ sox sample.mp3 -t raw -r 44.1k -s -3 -c 2 - | openaptxenc --hd > sample.aptxhd + +To play aptX HD audio file sample.aptxhd run: + +$ openaptxdec --hd < sample.aptxhd | play -t raw -r 44.1k -s -3 -c 2 - diff --git a/openaptx.c b/openaptx.c new file mode 100644 index 0000000..fc26483 --- /dev/null +++ b/openaptx.c @@ -0,0 +1,1113 @@ +/* + * Open Source implementation of Audio Processing Technology codec (aptX) + * Copyright (C) 2017 Aurelien Jacobs + * Copyright (C) 2018 Pali Rohár + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include + +#include + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a))) +#define DIFFSIGN(x,y) (((x)>(y)) - ((x)<(y))) + +/** + * Clip a signed integer into the -(2^p),(2^p-1) range. + * @param a value to clip + * @param p bit position to clip at + * @return clipped value + */ +static inline int clip_intp2(int a, int p) +{ + if (((unsigned)a + (1 << p)) & ~((2 << p) - 1)) + return (a >> 31) ^ ((1 << p) - 1); + else + return a; +} + +/** + * Clip a signed integer value into the amin-amax range. + * @param a value to clip + * @param amin minimum value of the clip range + * @param amax maximum value of the clip range + * @return clipped value + */ +static inline int clip(int a, int amin, int amax) +{ + if (a < amin) return amin; + else if (a > amax) return amax; + else return a; +} + +static inline int sign_extend(int val, unsigned bits) +{ + unsigned shift = 8 * sizeof(int) - bits; + union { unsigned u; int s; } v = { (unsigned) val << shift }; + return v.s >> shift; +} + +enum channels { + LEFT, + RIGHT, + NB_CHANNELS +}; + +enum subbands { + LF, // Low Frequency (0-5.5 kHz) + MLF, // Medium-Low Frequency (5.5-11kHz) + MHF, // Medium-High Frequency (11-16.5kHz) + HF, // High Frequency (16.5-22kHz) + NB_SUBBANDS +}; + +#define NB_FILTERS 2 +#define FILTER_TAPS 16 + +struct aptx_filter_signal { + int pos; + int32_t buffer[2*FILTER_TAPS]; +}; + +struct aptx_QMF_analysis { + struct aptx_filter_signal outer_filter_signal[NB_FILTERS]; + struct aptx_filter_signal inner_filter_signal[NB_FILTERS][NB_FILTERS]; +}; + +struct aptx_quantize { + int32_t quantized_sample; + int32_t quantized_sample_parity_change; + int32_t error; +}; + +struct aptx_invert_quantize { + int32_t quantization_factor; + int32_t factor_select; + int32_t reconstructed_difference; +}; + +struct aptx_prediction { + int32_t prev_sign[2]; + int32_t s_weight[2]; + int32_t d_weight[24]; + int32_t pos; + int32_t reconstructed_differences[48]; + int32_t previous_reconstructed_sample; + int32_t predicted_difference; + int32_t predicted_sample; +}; + +struct aptx_channel { + int32_t codeword_history; + int32_t dither_parity; + int32_t dither[NB_SUBBANDS]; + + struct aptx_QMF_analysis qmf; + struct aptx_quantize quantize[NB_SUBBANDS]; + struct aptx_invert_quantize invert_quantize[NB_SUBBANDS]; + struct aptx_prediction prediction[NB_SUBBANDS]; +} ; + +struct aptx_context { + int hd; + int32_t sync_idx; + struct aptx_channel channels[NB_CHANNELS]; +}; + + +static const int32_t quantize_intervals_LF[65] = { + -9948, 9948, 29860, 49808, 69822, 89926, 110144, 130502, + 151026, 171738, 192666, 213832, 235264, 256982, 279014, 301384, + 324118, 347244, 370790, 394782, 419250, 444226, 469742, 495832, + 522536, 549890, 577936, 606720, 636290, 666700, 698006, 730270, + 763562, 797958, 833538, 870398, 908640, 948376, 989740, 1032874, + 1077948, 1125150, 1174700, 1226850, 1281900, 1340196, 1402156, 1468282, + 1539182, 1615610, 1698514, 1789098, 1888944, 2000168, 2125700, 2269750, + 2438670, 2642660, 2899462, 3243240, 3746078, 4535138, 5664098, 7102424, + 8897462, +}; +static const int32_t invert_quantize_dither_factors_LF[65] = { + 9948, 9948, 9962, 9988, 10026, 10078, 10142, 10218, + 10306, 10408, 10520, 10646, 10784, 10934, 11098, 11274, + 11462, 11664, 11880, 12112, 12358, 12618, 12898, 13194, + 13510, 13844, 14202, 14582, 14988, 15422, 15884, 16380, + 16912, 17484, 18098, 18762, 19480, 20258, 21106, 22030, + 23044, 24158, 25390, 26760, 28290, 30008, 31954, 34172, + 36728, 39700, 43202, 47382, 52462, 58762, 66770, 77280, + 91642, 112348, 144452, 199326, 303512, 485546, 643414, 794914, + 1000124, +}; +static const int32_t quantize_dither_factors_LF[65] = { + 0, 4, 7, 10, 13, 16, 19, 22, + 26, 28, 32, 35, 38, 41, 44, 47, + 51, 54, 58, 62, 65, 70, 74, 79, + 84, 90, 95, 102, 109, 116, 124, 133, + 143, 154, 166, 180, 195, 212, 231, 254, + 279, 308, 343, 383, 430, 487, 555, 639, + 743, 876, 1045, 1270, 1575, 2002, 2628, 3591, + 5177, 8026, 13719, 26047, 45509, 39467, 37875, 51303, + 0, +}; +static const int16_t quantize_factor_select_offset_LF[65] = { + 0, -21, -19, -17, -15, -12, -10, -8, + -6, -4, -1, 1, 3, 6, 8, 10, + 13, 15, 18, 20, 23, 26, 29, 31, + 34, 37, 40, 43, 47, 50, 53, 57, + 60, 64, 68, 72, 76, 80, 85, 89, + 94, 99, 105, 110, 116, 123, 129, 136, + 144, 152, 161, 171, 182, 194, 207, 223, + 241, 263, 291, 328, 382, 467, 522, 522, + 522, +}; + + +static const int32_t quantize_intervals_MLF[9] = { + -89806, 89806, 278502, 494338, 759442, 1113112, 1652322, 2720256, 5190186, +}; +static const int32_t invert_quantize_dither_factors_MLF[9] = { + 89806, 89806, 98890, 116946, 148158, 205512, 333698, 734236, 1735696, +}; +static const int32_t quantize_dither_factors_MLF[9] = { + 0, 2271, 4514, 7803, 14339, 32047, 100135, 250365, 0, +}; +static const int16_t quantize_factor_select_offset_MLF[9] = { + 0, -14, 6, 29, 58, 96, 154, 270, 521, +}; + + +static const int32_t quantize_intervals_MHF[3] = { + -194080, 194080, 890562, +}; +static const int32_t invert_quantize_dither_factors_MHF[3] = { + 194080, 194080, 502402, +}; +static const int32_t quantize_dither_factors_MHF[3] = { + 0, 77081, 0, +}; +static const int16_t quantize_factor_select_offset_MHF[3] = { + 0, -33, 136, +}; + + +static const int32_t quantize_intervals_HF[5] = { + -163006, 163006, 542708, 1120554, 2669238, +}; +static const int32_t invert_quantize_dither_factors_HF[5] = { + 163006, 163006, 216698, 361148, 1187538, +}; +static const int32_t quantize_dither_factors_HF[5] = { + 0, 13423, 36113, 206598, 0, +}; +static const int16_t quantize_factor_select_offset_HF[5] = { + 0, -8, 33, 95, 262, +}; + + +static const int32_t hd_quantize_intervals_LF[257] = { + -2436, 2436, 7308, 12180, 17054, 21930, 26806, 31686, + 36566, 41450, 46338, 51230, 56124, 61024, 65928, 70836, + 75750, 80670, 85598, 90530, 95470, 100418, 105372, 110336, + 115308, 120288, 125278, 130276, 135286, 140304, 145334, 150374, + 155426, 160490, 165566, 170654, 175756, 180870, 185998, 191138, + 196294, 201466, 206650, 211850, 217068, 222300, 227548, 232814, + 238096, 243396, 248714, 254050, 259406, 264778, 270172, 275584, + 281018, 286470, 291944, 297440, 302956, 308496, 314056, 319640, + 325248, 330878, 336532, 342212, 347916, 353644, 359398, 365178, + 370986, 376820, 382680, 388568, 394486, 400430, 406404, 412408, + 418442, 424506, 430600, 436726, 442884, 449074, 455298, 461554, + 467844, 474168, 480528, 486922, 493354, 499820, 506324, 512866, + 519446, 526064, 532722, 539420, 546160, 552940, 559760, 566624, + 573532, 580482, 587478, 594520, 601606, 608740, 615920, 623148, + 630426, 637754, 645132, 652560, 660042, 667576, 675164, 682808, + 690506, 698262, 706074, 713946, 721876, 729868, 737920, 746036, + 754216, 762460, 770770, 779148, 787594, 796108, 804694, 813354, + 822086, 830892, 839774, 848736, 857776, 866896, 876100, 885386, + 894758, 904218, 913766, 923406, 933138, 942964, 952886, 962908, + 973030, 983254, 993582, 1004020, 1014566, 1025224, 1035996, 1046886, + 1057894, 1069026, 1080284, 1091670, 1103186, 1114838, 1126628, 1138558, + 1150634, 1162858, 1175236, 1187768, 1200462, 1213320, 1226346, 1239548, + 1252928, 1266490, 1280242, 1294188, 1308334, 1322688, 1337252, 1352034, + 1367044, 1382284, 1397766, 1413494, 1429478, 1445728, 1462252, 1479058, + 1496158, 1513562, 1531280, 1549326, 1567710, 1586446, 1605550, 1625034, + 1644914, 1665208, 1685932, 1707108, 1728754, 1750890, 1773542, 1796732, + 1820488, 1844840, 1869816, 1895452, 1921780, 1948842, 1976680, 2005338, + 2034868, 2065322, 2096766, 2129260, 2162880, 2197708, 2233832, 2271352, + 2310384, 2351050, 2393498, 2437886, 2484404, 2533262, 2584710, 2639036, + 2696578, 2757738, 2822998, 2892940, 2968278, 3049896, 3138912, 3236760, + 3345312, 3467068, 3605434, 3765154, 3952904, 4177962, 4452178, 4787134, + 5187290, 5647128, 6159120, 6720518, 7332904, 8000032, 8726664, 9518152, + 10380372, +}; +static const int32_t hd_invert_quantize_dither_factors_LF[257] = { + 2436, 2436, 2436, 2436, 2438, 2438, 2438, 2440, + 2442, 2442, 2444, 2446, 2448, 2450, 2454, 2456, + 2458, 2462, 2464, 2468, 2472, 2476, 2480, 2484, + 2488, 2492, 2498, 2502, 2506, 2512, 2518, 2524, + 2528, 2534, 2540, 2548, 2554, 2560, 2568, 2574, + 2582, 2588, 2596, 2604, 2612, 2620, 2628, 2636, + 2646, 2654, 2664, 2672, 2682, 2692, 2702, 2712, + 2722, 2732, 2742, 2752, 2764, 2774, 2786, 2798, + 2810, 2822, 2834, 2846, 2858, 2870, 2884, 2896, + 2910, 2924, 2938, 2952, 2966, 2980, 2994, 3010, + 3024, 3040, 3056, 3070, 3086, 3104, 3120, 3136, + 3154, 3170, 3188, 3206, 3224, 3242, 3262, 3280, + 3300, 3320, 3338, 3360, 3380, 3400, 3422, 3442, + 3464, 3486, 3508, 3532, 3554, 3578, 3602, 3626, + 3652, 3676, 3702, 3728, 3754, 3780, 3808, 3836, + 3864, 3892, 3920, 3950, 3980, 4010, 4042, 4074, + 4106, 4138, 4172, 4206, 4240, 4276, 4312, 4348, + 4384, 4422, 4460, 4500, 4540, 4580, 4622, 4664, + 4708, 4752, 4796, 4842, 4890, 4938, 4986, 5036, + 5086, 5138, 5192, 5246, 5300, 5358, 5416, 5474, + 5534, 5596, 5660, 5726, 5792, 5860, 5930, 6002, + 6074, 6150, 6226, 6306, 6388, 6470, 6556, 6644, + 6736, 6828, 6924, 7022, 7124, 7228, 7336, 7448, + 7562, 7680, 7802, 7928, 8058, 8192, 8332, 8476, + 8624, 8780, 8940, 9106, 9278, 9458, 9644, 9840, + 10042, 10252, 10472, 10702, 10942, 11194, 11458, 11734, + 12024, 12328, 12648, 12986, 13342, 13720, 14118, 14540, + 14990, 15466, 15976, 16520, 17102, 17726, 18398, 19124, + 19908, 20760, 21688, 22702, 23816, 25044, 26404, 27922, + 29622, 31540, 33720, 36222, 39116, 42502, 46514, 51334, + 57218, 64536, 73830, 85890, 101860, 123198, 151020, 183936, + 216220, 243618, 268374, 293022, 319362, 347768, 378864, 412626, 449596, +}; +static const int32_t hd_quantize_dither_factors_LF[256] = { + 0, 0, 0, 1, 0, 0, 1, 1, + 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 1, 1, 2, 2, 2, 1, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 3, + 2, 3, 2, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 4, 3, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 5, 4, 4, 5, + 4, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 6, 5, 5, 6, 5, 6, + 6, 6, 6, 6, 6, 6, 6, 7, + 6, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 8, 8, 8, 8, 8, 8, + 8, 9, 9, 9, 9, 9, 9, 9, + 10, 10, 10, 10, 10, 11, 11, 11, + 11, 11, 12, 12, 12, 12, 13, 13, + 13, 14, 14, 14, 15, 15, 15, 15, + 16, 16, 17, 17, 17, 18, 18, 18, + 19, 19, 20, 21, 21, 22, 22, 23, + 23, 24, 25, 26, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, + 39, 40, 42, 43, 45, 47, 49, 51, + 53, 55, 58, 60, 63, 66, 69, 73, + 76, 80, 85, 89, 95, 100, 106, 113, + 119, 128, 136, 146, 156, 168, 182, 196, + 213, 232, 254, 279, 307, 340, 380, 425, + 480, 545, 626, 724, 847, 1003, 1205, 1471, + 1830, 2324, 3015, 3993, 5335, 6956, 8229, 8071, + 6850, 6189, 6162, 6585, 7102, 7774, 8441, 9243, +}; +static const int16_t hd_quantize_factor_select_offset_LF[257] = { + 0, -22, -21, -21, -20, -20, -19, -19, + -18, -18, -17, -17, -16, -16, -15, -14, + -14, -13, -13, -12, -12, -11, -11, -10, + -10, -9, -9, -8, -7, -7, -6, -6, + -5, -5, -4, -4, -3, -3, -2, -1, + -1, 0, 0, 1, 1, 2, 2, 3, + 4, 4, 5, 5, 6, 6, 7, 8, + 8, 9, 9, 10, 11, 11, 12, 12, + 13, 14, 14, 15, 15, 16, 17, 17, + 18, 19, 19, 20, 20, 21, 22, 22, + 23, 24, 24, 25, 26, 26, 27, 28, + 28, 29, 30, 30, 31, 32, 33, 33, + 34, 35, 35, 36, 37, 38, 38, 39, + 40, 41, 41, 42, 43, 44, 44, 45, + 46, 47, 48, 48, 49, 50, 51, 52, + 52, 53, 54, 55, 56, 57, 58, 58, + 59, 60, 61, 62, 63, 64, 65, 66, + 67, 68, 69, 69, 70, 71, 72, 73, + 74, 75, 77, 78, 79, 80, 81, 82, + 83, 84, 85, 86, 87, 89, 90, 91, + 92, 93, 94, 96, 97, 98, 99, 101, + 102, 103, 105, 106, 107, 109, 110, 112, + 113, 115, 116, 118, 119, 121, 122, 124, + 125, 127, 129, 130, 132, 134, 136, 137, + 139, 141, 143, 145, 147, 149, 151, 153, + 155, 158, 160, 162, 164, 167, 169, 172, + 174, 177, 180, 182, 185, 188, 191, 194, + 197, 201, 204, 208, 211, 215, 219, 223, + 227, 232, 236, 241, 246, 251, 257, 263, + 269, 275, 283, 290, 298, 307, 317, 327, + 339, 352, 367, 384, 404, 429, 458, 494, + 522, 522, 522, 522, 522, 522, 522, 522, 522, +}; + + +static const int32_t hd_quantize_intervals_MLF[33] = { + -21236, 21236, 63830, 106798, 150386, 194832, 240376, 287258, + 335726, 386034, 438460, 493308, 550924, 611696, 676082, 744626, + 817986, 896968, 982580, 1076118, 1179278, 1294344, 1424504, 1574386, + 1751090, 1966260, 2240868, 2617662, 3196432, 4176450, 5658260, 7671068, + 10380372, +}; +static const int32_t hd_invert_quantize_dither_factors_MLF[33] = { + 21236, 21236, 21360, 21608, 21978, 22468, 23076, 23806, + 24660, 25648, 26778, 28070, 29544, 31228, 33158, 35386, + 37974, 41008, 44606, 48934, 54226, 60840, 69320, 80564, + 96140, 119032, 155576, 221218, 357552, 622468, 859344, 1153464, 1555840, +}; +static const int32_t hd_quantize_dither_factors_MLF[32] = { + 0, 31, 62, 93, 123, 152, 183, 214, + 247, 283, 323, 369, 421, 483, 557, 647, + 759, 900, 1082, 1323, 1654, 2120, 2811, 3894, + 5723, 9136, 16411, 34084, 66229, 59219, 73530, 100594, +}; +static const int16_t hd_quantize_factor_select_offset_MLF[33] = { + 0, -21, -16, -12, -7, -2, 3, 8, + 13, 19, 24, 30, 36, 43, 50, 57, + 65, 74, 83, 93, 104, 117, 131, 147, + 166, 189, 219, 259, 322, 427, 521, 521, 521, +}; + + +static const int32_t hd_quantize_intervals_MHF[9] = { + -95044, 95044, 295844, 528780, 821332, 1226438, 1890540, 3344850, 6450664, +}; +static const int32_t hd_invert_quantize_dither_factors_MHF[9] = { + 95044, 95044, 105754, 127180, 165372, 39736, 424366, 1029946, 2075866, +}; +static const int32_t hd_quantize_dither_factors_MHF[8] = { + 0, 2678, 5357, 9548, -31409, 96158, 151395, 261480, +}; +static const int16_t hd_quantize_factor_select_offset_MHF[9] = { + 0, -17, 5, 30, 62, 105, 177, 334, 518, +}; + + +static const int32_t hd_quantize_intervals_HF[17] = { + -45754, 45754, 138496, 234896, 337336, 448310, 570738, 708380, + 866534, 1053262, 1281958, 1577438, 1993050, 2665984, 3900982, 5902844, + 8897462, +}; +static const int32_t hd_invert_quantize_dither_factors_HF[17] = { + 45754, 45754, 46988, 49412, 53026, 57950, 64478, 73164, + 84988, 101740, 126958, 168522, 247092, 425842, 809154, 1192708, 1801910, +}; +static const int32_t hd_quantize_dither_factors_HF[16] = { + 0, 309, 606, 904, 1231, 1632, 2172, 2956, + 4188, 6305, 10391, 19643, 44688, 95828, 95889, 152301, +}; +static const int16_t hd_quantize_factor_select_offset_HF[17] = { + 0, -18, -8, 2, 13, 25, 38, 53, + 70, 90, 115, 147, 192, 264, 398, 521, 521, +}; + +struct aptx_tables { + const int32_t *quantize_intervals; + const int32_t *invert_quantize_dither_factors; + const int32_t *quantize_dither_factors; + const int16_t *quantize_factor_select_offset; + int tables_size; + int32_t factor_max; + int32_t prediction_order; +}; + +static const struct aptx_tables tables[2][NB_SUBBANDS] = { + { + [LF] = { quantize_intervals_LF, + invert_quantize_dither_factors_LF, + quantize_dither_factors_LF, + quantize_factor_select_offset_LF, + ARRAY_SIZE(quantize_intervals_LF), + 0x11FF, 24 }, + [MLF] = { quantize_intervals_MLF, + invert_quantize_dither_factors_MLF, + quantize_dither_factors_MLF, + quantize_factor_select_offset_MLF, + ARRAY_SIZE(quantize_intervals_MLF), + 0x14FF, 12 }, + [MHF] = { quantize_intervals_MHF, + invert_quantize_dither_factors_MHF, + quantize_dither_factors_MHF, + quantize_factor_select_offset_MHF, + ARRAY_SIZE(quantize_intervals_MHF), + 0x16FF, 6 }, + [HF] = { quantize_intervals_HF, + invert_quantize_dither_factors_HF, + quantize_dither_factors_HF, + quantize_factor_select_offset_HF, + ARRAY_SIZE(quantize_intervals_HF), + 0x15FF, 12 }, + }, + { + [LF] = { hd_quantize_intervals_LF, + hd_invert_quantize_dither_factors_LF, + hd_quantize_dither_factors_LF, + hd_quantize_factor_select_offset_LF, + ARRAY_SIZE(hd_quantize_intervals_LF), + 0x11FF, 24 }, + [MLF] = { hd_quantize_intervals_MLF, + hd_invert_quantize_dither_factors_MLF, + hd_quantize_dither_factors_MLF, + hd_quantize_factor_select_offset_MLF, + ARRAY_SIZE(hd_quantize_intervals_MLF), + 0x14FF, 12 }, + [MHF] = { hd_quantize_intervals_MHF, + hd_invert_quantize_dither_factors_MHF, + hd_quantize_dither_factors_MHF, + hd_quantize_factor_select_offset_MHF, + ARRAY_SIZE(hd_quantize_intervals_MHF), + 0x16FF, 6 }, + [HF] = { hd_quantize_intervals_HF, + hd_invert_quantize_dither_factors_HF, + hd_quantize_dither_factors_HF, + hd_quantize_factor_select_offset_HF, + ARRAY_SIZE(hd_quantize_intervals_HF), + 0x15FF, 12 }, + } +}; + +static const int16_t quantization_factors[32] = { + 2048, 2093, 2139, 2186, 2233, 2282, 2332, 2383, + 2435, 2489, 2543, 2599, 2656, 2714, 2774, 2834, + 2896, 2960, 3025, 3091, 3158, 3228, 3298, 3371, + 3444, 3520, 3597, 3676, 3756, 3838, 3922, 4008, +}; + + +/* Rounded right shift with optionnal clipping */ +#define RSHIFT_SIZE(size) \ +static inline int##size##_t rshift##size(int##size##_t value, int shift) \ +{ \ + int##size##_t rounding = (int##size##_t)1 << (shift - 1); \ + int##size##_t mask = ((int##size##_t)1 << (shift + 1)) - 1; \ + return ((value + rounding) >> shift) - ((value & mask) == rounding); \ +} \ +static inline int##size##_t rshift##size##_clip24(int##size##_t value, int shift) \ +{ \ + return clip_intp2(rshift##size(value, shift), 23); \ +} +RSHIFT_SIZE(32) +RSHIFT_SIZE(64) + + +static inline void aptx_update_codeword_history(struct aptx_channel *channel) +{ + int32_t cw = ((channel->quantize[0].quantized_sample & 3) << 0) + + ((channel->quantize[1].quantized_sample & 2) << 1) + + ((channel->quantize[2].quantized_sample & 1) << 3); + channel->codeword_history = (cw << 8) + (channel->codeword_history << 4); +} + +static void aptx_generate_dither(struct aptx_channel *channel) +{ + int subband; + int64_t m; + int32_t d; + + aptx_update_codeword_history(channel); + + m = (int64_t)5184443 * (channel->codeword_history >> 7); + d = (m << 2) + (m >> 22); + for (subband = 0; subband < NB_SUBBANDS; subband++) + channel->dither[subband] = d << (23 - 5*subband); + channel->dither_parity = (d >> 25) & 1; +} + +/* + * Convolution filter coefficients for the outer QMF of the QMF tree. + * The 2 sets are a mirror of each other. + */ +static const int32_t aptx_qmf_outer_coeffs[NB_FILTERS][FILTER_TAPS] = { + { + 730, -413, -9611, 43626, -121026, 269973, -585547, 2801966, + 697128, -160481, 27611, 8478, -10043, 3511, 688, -897, + }, + { + -897, 688, 3511, -10043, 8478, 27611, -160481, 697128, + 2801966, -585547, 269973, -121026, 43626, -9611, -413, 730, + }, +}; + +/* + * Convolution filter coefficients for the inner QMF of the QMF tree. + * The 2 sets are a mirror of each other. + */ +static const int32_t aptx_qmf_inner_coeffs[NB_FILTERS][FILTER_TAPS] = { + { + 1033, -584, -13592, 61697, -171156, 381799, -828088, 3962579, + 985888, -226954, 39048, 11990, -14203, 4966, 973, -1268, + }, + { + -1268, 973, 4966, -14203, 11990, 39048, -226954, 985888, + 3962579, -828088, 381799, -171156, 61697, -13592, -584, 1033, + }, +}; + +/* + * Push one sample into a circular signal buffer. + */ +static inline void aptx_qmf_filter_signal_push(struct aptx_filter_signal *signal, + int32_t sample) +{ + signal->buffer[signal->pos ] = sample; + signal->buffer[signal->pos+FILTER_TAPS] = sample; + signal->pos = (signal->pos + 1) & (FILTER_TAPS - 1); +} + +/* + * Compute the convolution of the signal with the coefficients, and reduce + * to 24 bits by applying the specified right shifting. + */ +static inline int32_t aptx_qmf_convolution(struct aptx_filter_signal *signal, + const int32_t coeffs[FILTER_TAPS], + int shift) +{ + int32_t *sig = &signal->buffer[signal->pos]; + int64_t e = 0; + int i; + + for (i = 0; i < FILTER_TAPS; i++) + e += (int64_t)sig[i] * (int64_t)coeffs[i]; + + return rshift64_clip24(e, shift); +} + +/* + * Half-band QMF analysis filter realized with a polyphase FIR filter. + * Split into 2 subbands and downsample by 2. + * So for each pair of samples that goes in, one sample goes out, + * split into 2 separate subbands. + */ +static inline void aptx_qmf_polyphase_analysis(struct aptx_filter_signal signal[NB_FILTERS], + const int32_t coeffs[NB_FILTERS][FILTER_TAPS], + int shift, + const int32_t samples[NB_FILTERS], + int32_t *low_subband_output, + int32_t *high_subband_output) +{ + int32_t subbands[NB_FILTERS]; + int i; + + for (i = 0; i < NB_FILTERS; i++) { + aptx_qmf_filter_signal_push(&signal[i], samples[NB_FILTERS-1-i]); + subbands[i] = aptx_qmf_convolution(&signal[i], coeffs[i], shift); + } + + *low_subband_output = clip_intp2(subbands[0] + subbands[1], 23); + *high_subband_output = clip_intp2(subbands[0] - subbands[1], 23); +} + +/* + * Two stage QMF analysis tree. + * Split 4 input samples into 4 subbands and downsample by 4. + * So for each group of 4 samples that goes in, one sample goes out, + * split into 4 separate subbands. + */ +static void aptx_qmf_tree_analysis(struct aptx_QMF_analysis *qmf, + const int32_t samples[4], + int32_t subband_samples[4]) +{ + int32_t intermediate_samples[4]; + int i; + + /* Split 4 input samples into 2 intermediate subbands downsampled to 2 samples */ + for (i = 0; i < 2; i++) + aptx_qmf_polyphase_analysis(qmf->outer_filter_signal, + aptx_qmf_outer_coeffs, 23, + &samples[2*i], + &intermediate_samples[0+i], + &intermediate_samples[2+i]); + + /* Split 2 intermediate subband samples into 4 final subbands downsampled to 1 sample */ + for (i = 0; i < 2; i++) + aptx_qmf_polyphase_analysis(qmf->inner_filter_signal[i], + aptx_qmf_inner_coeffs, 23, + &intermediate_samples[2*i], + &subband_samples[2*i+0], + &subband_samples[2*i+1]); +} + +/* + * Half-band QMF synthesis filter realized with a polyphase FIR filter. + * Join 2 subbands and upsample by 2. + * So for each 2 subbands sample that goes in, a pair of samples goes out. + */ +static inline void aptx_qmf_polyphase_synthesis(struct aptx_filter_signal signal[NB_FILTERS], + const int32_t coeffs[NB_FILTERS][FILTER_TAPS], + int shift, + int32_t low_subband_input, + int32_t high_subband_input, + int32_t samples[NB_FILTERS]) +{ + int32_t subbands[NB_FILTERS]; + int i; + + subbands[0] = low_subband_input + high_subband_input; + subbands[1] = low_subband_input - high_subband_input; + + for (i = 0; i < NB_FILTERS; i++) { + aptx_qmf_filter_signal_push(&signal[i], subbands[1-i]); + samples[i] = aptx_qmf_convolution(&signal[i], coeffs[i], shift); + } +} + +/* + * Two stage QMF synthesis tree. + * Join 4 subbands and upsample by 4. + * So for each 4 subbands sample that goes in, a group of 4 samples goes out. + */ +static void aptx_qmf_tree_synthesis(struct aptx_QMF_analysis *qmf, + int32_t subband_samples[4], + int32_t samples[4]) +{ + int32_t intermediate_samples[4]; + int i; + + /* Join 4 subbands into 2 intermediate subbands upsampled to 2 samples. */ + for (i = 0; i < 2; i++) + aptx_qmf_polyphase_synthesis(qmf->inner_filter_signal[i], + aptx_qmf_inner_coeffs, 22, + subband_samples[2*i+0], + subband_samples[2*i+1], + &intermediate_samples[2*i]); + + /* Join 2 samples from intermediate subbands upsampled to 4 samples. */ + for (i = 0; i < 2; i++) + aptx_qmf_polyphase_synthesis(qmf->outer_filter_signal, + aptx_qmf_outer_coeffs, 21, + intermediate_samples[0+i], + intermediate_samples[2+i], + &samples[2*i]); +} + + +static inline int32_t aptx_bin_search(int32_t value, int32_t factor, + const int32_t *intervals, int32_t nb_intervals) +{ + int32_t idx = 0; + int i; + + for (i = nb_intervals >> 1; i > 0; i >>= 1) + if ((int64_t)factor * (int64_t)intervals[idx + i] <= ((int64_t)value << 24)) + idx += i; + + return idx; +} + +static void aptx_quantize_difference(struct aptx_quantize *quantize, + int32_t sample_difference, + int32_t dither, + int32_t quantization_factor, + const struct aptx_tables *tables) +{ + const int32_t *intervals = tables->quantize_intervals; + int32_t quantized_sample, dithered_sample, parity_change; + int32_t d, mean, interval, inv, sample_difference_abs; + int64_t error; + + sample_difference_abs = sample_difference; + if (sample_difference_abs < 0) + sample_difference_abs = -sample_difference_abs; + if (sample_difference_abs > (1 << 23) - 1) + sample_difference_abs = (1 << 23) - 1; + + quantized_sample = aptx_bin_search(sample_difference_abs >> 4, + quantization_factor, + intervals, tables->tables_size); + + d = rshift32_clip24(((int64_t)dither * (int64_t)dither) >> 32, 7) - (1 << 23); + d = rshift64((int64_t)d * (int64_t)tables->quantize_dither_factors[quantized_sample], 23); + + intervals += quantized_sample; + mean = (intervals[1] + intervals[0]) / 2; + interval = (intervals[1] - intervals[0]) * (-(sample_difference < 0) | 1); + + dithered_sample = rshift64_clip24((int64_t)dither * (int64_t)interval + ((int64_t)clip_intp2(mean + d, 23) << 32), 32); + error = ((int64_t)sample_difference_abs << 20) - (int64_t)dithered_sample * (int64_t)quantization_factor; + quantize->error = rshift64(error, 23); + if (quantize->error < 0) + quantize->error = -quantize->error; + + parity_change = quantized_sample; + if (error < 0) + quantized_sample--; + else + parity_change--; + + inv = -(sample_difference < 0); + quantize->quantized_sample = quantized_sample ^ inv; + quantize->quantized_sample_parity_change = parity_change ^ inv; +} + +static void aptx_encode_channel(struct aptx_channel *channel, const int32_t samples[4], int hd) +{ + int32_t subband_samples[4]; + int subband; + aptx_qmf_tree_analysis(&channel->qmf, samples, subband_samples); + aptx_generate_dither(channel); + for (subband = 0; subband < NB_SUBBANDS; subband++) { + int32_t diff = clip_intp2(subband_samples[subband] - channel->prediction[subband].predicted_sample, 23); + aptx_quantize_difference(&channel->quantize[subband], diff, + channel->dither[subband], + channel->invert_quantize[subband].quantization_factor, + &tables[hd][subband]); + } +} + +static void aptx_decode_channel(struct aptx_channel *channel, int32_t samples[4]) +{ + int32_t subband_samples[4]; + int subband; + for (subband = 0; subband < NB_SUBBANDS; subband++) + subband_samples[subband] = channel->prediction[subband].previous_reconstructed_sample; + aptx_qmf_tree_synthesis(&channel->qmf, subband_samples, samples); +} + + +static void aptx_invert_quantization(struct aptx_invert_quantize *invert_quantize, + int32_t quantized_sample, int32_t dither, + const struct aptx_tables *tables) +{ + int32_t qr, idx, shift, factor_select; + + idx = (quantized_sample ^ -(quantized_sample < 0)) + 1; + qr = tables->quantize_intervals[idx] / 2; + if (quantized_sample < 0) + qr = -qr; + + qr = rshift64_clip24(((int64_t)qr<<32) + (int64_t)dither * (int64_t)tables->invert_quantize_dither_factors[idx], 32); + invert_quantize->reconstructed_difference = ((int64_t)invert_quantize->quantization_factor * (int64_t)qr) >> 19; + + /* update factor_select */ + factor_select = 32620 * invert_quantize->factor_select; + factor_select = rshift32(factor_select + (tables->quantize_factor_select_offset[idx] << 15), 15); + invert_quantize->factor_select = clip(factor_select, 0, tables->factor_max); + + /* update quantization factor */ + idx = (invert_quantize->factor_select & 0xFF) >> 3; + shift = (tables->factor_max - invert_quantize->factor_select) >> 8; + invert_quantize->quantization_factor = (quantization_factors[idx] << 11) >> shift; +} + +static int32_t *aptx_reconstructed_differences_update(struct aptx_prediction *prediction, + int32_t reconstructed_difference, + int order) +{ + int32_t *rd1 = prediction->reconstructed_differences, *rd2 = rd1 + order; + int p = prediction->pos; + + rd1[p] = rd2[p]; + prediction->pos = p = (p + 1) % order; + rd2[p] = reconstructed_difference; + return &rd2[p]; +} + +static void aptx_prediction_filtering(struct aptx_prediction *prediction, + int32_t reconstructed_difference, + int order) +{ + int32_t reconstructed_sample, predictor, srd0; + int32_t *reconstructed_differences; + int64_t predicted_difference = 0; + int i; + + reconstructed_sample = clip_intp2(reconstructed_difference + prediction->predicted_sample, 23); + predictor = clip_intp2(((int64_t)prediction->s_weight[0] * (int64_t)prediction->previous_reconstructed_sample + + (int64_t)prediction->s_weight[1] * (int64_t)reconstructed_sample) >> 22, 23); + prediction->previous_reconstructed_sample = reconstructed_sample; + + reconstructed_differences = aptx_reconstructed_differences_update(prediction, reconstructed_difference, order); + srd0 = DIFFSIGN(reconstructed_difference, 0) << 23; + for (i = 0; i < order; i++) { + int32_t srd = (reconstructed_differences[-i-1] >> 31) | 1; + prediction->d_weight[i] -= rshift32(prediction->d_weight[i] - srd*srd0, 8); + predicted_difference += (int64_t)reconstructed_differences[-i] * (int64_t)prediction->d_weight[i]; + } + + prediction->predicted_difference = clip_intp2(predicted_difference >> 22, 23); + prediction->predicted_sample = clip_intp2(predictor + prediction->predicted_difference, 23); +} + +static void aptx_process_subband(struct aptx_invert_quantize *invert_quantize, + struct aptx_prediction *prediction, + int32_t quantized_sample, int32_t dither, + const struct aptx_tables *tables) +{ + int32_t sign, same_sign[2], weight[2], sw1, range; + + aptx_invert_quantization(invert_quantize, quantized_sample, dither, tables); + + sign = DIFFSIGN(invert_quantize->reconstructed_difference, + -prediction->predicted_difference); + same_sign[0] = sign * prediction->prev_sign[0]; + same_sign[1] = sign * prediction->prev_sign[1]; + prediction->prev_sign[0] = prediction->prev_sign[1]; + prediction->prev_sign[1] = sign | 1; + + range = 0x100000; + sw1 = rshift32(-same_sign[1] * prediction->s_weight[1], 1); + sw1 = (clip(sw1, -range, range) & ~0xF) << 4; + + range = 0x300000; + weight[0] = 254 * prediction->s_weight[0] + 0x800000*same_sign[0] + sw1; + prediction->s_weight[0] = clip(rshift32(weight[0], 8), -range, range); + + range = 0x3C0000 - prediction->s_weight[0]; + weight[1] = 255 * prediction->s_weight[1] + 0xC00000*same_sign[1]; + prediction->s_weight[1] = clip(rshift32(weight[1], 8), -range, range); + + aptx_prediction_filtering(prediction, + invert_quantize->reconstructed_difference, + tables->prediction_order); +} + +static void aptx_invert_quantize_and_prediction(struct aptx_channel *channel, int hd) +{ + int subband; + for (subband = 0; subband < NB_SUBBANDS; subband++) + aptx_process_subband(&channel->invert_quantize[subband], + &channel->prediction[subband], + channel->quantize[subband].quantized_sample, + channel->dither[subband], + &tables[hd][subband]); +} + +static int32_t aptx_quantized_parity(struct aptx_channel *channel) +{ + int32_t parity = channel->dither_parity; + int subband; + + for (subband = 0; subband < NB_SUBBANDS; subband++) + parity ^= channel->quantize[subband].quantized_sample; + + return parity & 1; +} + +/* For each sample, ensure that the parity of all subbands of all channels + * is 0 except once every 8 samples where the parity is forced to 1. */ +static int aptx_check_parity(struct aptx_channel channels[NB_CHANNELS], int32_t *idx) +{ + int32_t parity = aptx_quantized_parity(&channels[LEFT]) + ^ aptx_quantized_parity(&channels[RIGHT]); + + int eighth = *idx == 7; + *idx = (*idx + 1) & 7; + + return parity ^ eighth; +} + +static void aptx_insert_sync(struct aptx_channel channels[NB_CHANNELS], int32_t *idx) +{ + if (aptx_check_parity(channels, idx)) { + int i; + struct aptx_channel *c; + static const int map[] = { 1, 2, 0, 3 }; + struct aptx_quantize *min = &channels[NB_CHANNELS-1].quantize[map[0]]; + for (c = &channels[NB_CHANNELS-1]; c >= channels; c--) + for (i = 0; i < NB_SUBBANDS; i++) + if (c->quantize[map[i]].error < min->error) + min = &c->quantize[map[i]]; + + /* Forcing the desired parity is done by offsetting by 1 the quantized + * sample from the subband featuring the smallest quantization error. */ + min->quantized_sample = min->quantized_sample_parity_change; + } +} + +static uint16_t aptx_pack_codeword(struct aptx_channel *channel) +{ + int32_t parity = aptx_quantized_parity(channel); + return (((channel->quantize[3].quantized_sample & 0x06) | parity) << 13) + | (((channel->quantize[2].quantized_sample & 0x03) ) << 11) + | (((channel->quantize[1].quantized_sample & 0x0F) ) << 7) + | (((channel->quantize[0].quantized_sample & 0x7F) ) << 0); +} + +static uint32_t aptxhd_pack_codeword(struct aptx_channel *channel) +{ + int32_t parity = aptx_quantized_parity(channel); + return (((channel->quantize[3].quantized_sample & 0x01E) | parity) << 19) + | (((channel->quantize[2].quantized_sample & 0x00F) ) << 15) + | (((channel->quantize[1].quantized_sample & 0x03F) ) << 9) + | (((channel->quantize[0].quantized_sample & 0x1FF) ) << 0); +} + +static void aptx_unpack_codeword(struct aptx_channel *channel, uint16_t codeword) +{ + channel->quantize[0].quantized_sample = sign_extend(codeword >> 0, 7); + channel->quantize[1].quantized_sample = sign_extend(codeword >> 7, 4); + channel->quantize[2].quantized_sample = sign_extend(codeword >> 11, 2); + channel->quantize[3].quantized_sample = sign_extend(codeword >> 13, 3); + channel->quantize[3].quantized_sample = (channel->quantize[3].quantized_sample & ~1) + | aptx_quantized_parity(channel); +} + +static void aptxhd_unpack_codeword(struct aptx_channel *channel, uint32_t codeword) +{ + channel->quantize[0].quantized_sample = sign_extend(codeword >> 0, 9); + channel->quantize[1].quantized_sample = sign_extend(codeword >> 9, 6); + channel->quantize[2].quantized_sample = sign_extend(codeword >> 15, 4); + channel->quantize[3].quantized_sample = sign_extend(codeword >> 19, 5); + channel->quantize[3].quantized_sample = (channel->quantize[3].quantized_sample & ~1) + | aptx_quantized_parity(channel); +} + +static void aptx_encode_samples(struct aptx_context *ctx, + const int32_t samples[NB_CHANNELS][4], + uint8_t *output) +{ + int channel; + for (channel = 0; channel < NB_CHANNELS; channel++) + aptx_encode_channel(&ctx->channels[channel], samples[channel], ctx->hd); + + aptx_insert_sync(ctx->channels, &ctx->sync_idx); + + for (channel = 0; channel < NB_CHANNELS; channel++) { + aptx_invert_quantize_and_prediction(&ctx->channels[channel], ctx->hd); + if (ctx->hd) { + uint32_t codeword = aptxhd_pack_codeword(&ctx->channels[channel]); + output[3*channel+0] = (codeword >> 16) & 0xFF; + output[3*channel+1] = (codeword >> 8) & 0xFF; + output[3*channel+2] = (codeword >> 0) & 0xFF; + } else { + uint16_t codeword = aptx_pack_codeword(&ctx->channels[channel]); + output[2*channel+0] = (codeword >> 8) & 0xFF; + output[2*channel+1] = (codeword >> 0) & 0xFF; + } + } +} + +static int aptx_decode_samples(struct aptx_context *ctx, + const uint8_t *input, + int32_t samples[NB_CHANNELS][4]) +{ + int channel, ret; + + for (channel = 0; channel < NB_CHANNELS; channel++) { + aptx_generate_dither(&ctx->channels[channel]); + + if (ctx->hd) + aptxhd_unpack_codeword(&ctx->channels[channel], + ((uint32_t)input[3*channel+0] << 16) | + ((uint32_t)input[3*channel+1] << 8) | + ((uint32_t)input[3*channel+2] << 0)); + else + aptx_unpack_codeword(&ctx->channels[channel], + ((uint16_t)input[2*channel+0] << 8) | + ((uint16_t)input[2*channel+1] << 0)); + aptx_invert_quantize_and_prediction(&ctx->channels[channel], ctx->hd); + } + + ret = aptx_check_parity(ctx->channels, &ctx->sync_idx); + + for (channel = 0; channel < NB_CHANNELS; channel++) + aptx_decode_channel(&ctx->channels[channel], samples[channel]); + + return ret; +} + + +struct aptx_context *aptx_init(int hd) +{ + struct aptx_context *ctx; + + ctx = malloc(sizeof(*ctx)); + if (!ctx) + return NULL; + + ctx->hd = hd ? 1 : 0; + + aptx_reset(ctx); + return ctx; +} + +void aptx_reset(struct aptx_context *ctx) +{ + int hd, chan, subband; + + hd = ctx->hd; + memset(ctx, 0, sizeof(*ctx)); + ctx->hd = hd; + + for (chan = 0; chan < NB_CHANNELS; chan++) { + struct aptx_channel *channel = &ctx->channels[chan]; + for (subband = 0; subband < NB_SUBBANDS; subband++) { + struct aptx_prediction *prediction = &channel->prediction[subband]; + prediction->prev_sign[0] = 1; + prediction->prev_sign[1] = 1; + } + } +} + +void aptx_finish(struct aptx_context *ctx) +{ + free(ctx); +} + +size_t aptx_encode(struct aptx_context *ctx, const unsigned char *input, size_t input_size, unsigned char *output, size_t output_size, size_t *written) +{ + int32_t samples[NB_CHANNELS][4]; + size_t ipos, opos; + int sample, channel; + int sample_size; + + sample_size = ctx->hd ? 6 : 4; + + for (ipos = 0, opos = 0; ipos + 3*NB_CHANNELS*4 <= input_size && opos + sample_size <= output_size; opos += sample_size) { + for (sample = 0; sample < 4; sample++) { + for (channel = 0; channel < NB_CHANNELS; channel++, ipos += 3) { + /* samples need to contain 24bit signed intger stored as 32bit signed integers */ + /* last int8_t --> uint32_t cast propagates sign bit for 32bit integer */ + samples[channel][sample] = ((uint32_t)input[ipos+0] << 0) | + ((uint32_t)input[ipos+1] << 8) | + ((uint32_t)(int8_t)input[ipos+2] << 16); + } + } + aptx_encode_samples(ctx, samples, output + opos); + } + + *written = opos; + return ipos; +} + +size_t aptx_decode(struct aptx_context *ctx, const unsigned char *input, size_t input_size, unsigned char *output, size_t output_size, size_t *written) +{ + int32_t samples[NB_CHANNELS][4]; + size_t ipos, opos; + int sample, channel; + int sample_size; + + sample_size = ctx->hd ? 6 : 4; + + for (ipos = 0, opos = 0; ipos + sample_size <= input_size && opos + 3*NB_CHANNELS*4 <= output_size; ipos += sample_size) { + if (aptx_decode_samples(ctx, input + ipos, samples)) + break; + for (sample = 0; sample < 4; sample++) { + for (channel = 0; channel < NB_CHANNELS; channel++, opos += 3) { + /* samples contain 24bit signed integers stored as 32bit signed integers */ + /* we do not need to care about negative integers specially as they have 24. bit set */ + output[opos+0] = ((uint32_t)samples[channel][sample] >> 0) & 0xFF; + output[opos+1] = ((uint32_t)samples[channel][sample] >> 8) & 0xFF; + output[opos+2] = ((uint32_t)samples[channel][sample] >> 16) & 0xFF; + } + } + } + + *written = opos; + return ipos; +} diff --git a/openaptx.h b/openaptx.h new file mode 100644 index 0000000..8d1c62f --- /dev/null +++ b/openaptx.h @@ -0,0 +1,84 @@ +/* + * Open Source implementation of Audio Processing Technology codec (aptX) + * Copyright (C) 2018 Pali Rohár + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef OPENAPTX_H +#define OPENAPTX_H + +#define OPENAPTX_MAJOR 0 +#define OPENAPTX_MINOR 0 +#define OPENAPTX_PATCH 0 + +struct aptx_context; + +/* + * Initialize context for aptX codec and reset it. + * hd = 0 process aptX codec + * hd = 1 process aptX HD codec + */ +struct aptx_context *aptx_init(int hd); + +/* + * Reset internal state, predictor and parity sync of aptX context. + * It is needed when going to encode or decode a new stream. + */ +void aptx_reset(struct aptx_context *ctx); + +/* + * Free aptX context initialized by aptx_init(). + */ +void aptx_finish(struct aptx_context *ctx); + +/* + * Encodes sequence of 4 raw 24bit signed stereo samples from input buffer with + * size input_len to aptX audio samples into output buffer with size output_len. + * Return value indicates processed length from input buffer and to written + * pointer is stored length of encoded aptX audio samples in output buffer. + * Therefore input buffer must contain sequence of the 24 bytes in format + * LLLRRRLLLRRRLLLRRRLLLRRR (L-left, R-right) and output buffer would contain + * encoded sequence of either four bytes (LLRR) of aptX or six bytes (LLLRRR) + * of aptX HD. Due to aptX parity check it is suggested to provide multiple of + * 8*4 raw input samples, therefore multiple of 8*24 bytes. + */ +size_t aptx_encode(struct aptx_context *ctx, + const unsigned char *input, + size_t input_len, + unsigned char *output, + size_t output_len, + size_t *written); + +/* + * Decodes aptX audio samples in input buffer with size input_len to sequence + * of raw 24bit signed stereo samples into output buffer with size output_len. + * Return value indicates processed length from input buffer and to written + * pointer is stored length of decoded output samples in output buffer. + * Input buffer must contain seqeunce of four bytes (LLRR) of aptX or six + * bytes (LLLRRR) of aptX HD samples and output buffer would contain decoded + * sequence of 24 bytes in format LLLRRRLLLRRRLLLRRRLLLRRR (L-left, R-right) + * for one aptX sample. Due to aptX parity check it is suggested to provide + * multiple of eight aptX samples, therefore multiple of 8*4 bytes for aptX + * reps. 8*6 bytes for aptX HD. + */ +size_t aptx_decode(struct aptx_context *ctx, + const unsigned char *input, + size_t input_len, + unsigned char *output, + size_t output_len, + size_t *written); + +#endif diff --git a/openaptxdec.c b/openaptxdec.c new file mode 100644 index 0000000..b65f076 --- /dev/null +++ b/openaptxdec.c @@ -0,0 +1,168 @@ +/* + * aptX decoder utility + * Copyright (C) 2018 Pali Rohár + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include + +#include + +static unsigned char input_buffer[512*8*6]; +static unsigned char output_buffer[512*8*3*2*4*6/4]; + +int main(int argc, char *argv[]) +{ + int i; + int hd; + size_t length; + size_t offset; + size_t sample_size; + size_t process_size; + size_t processed; + size_t written; + struct aptx_context *ctx; + unsigned int failed; + + hd = 0; + + for (i = 1; i < argc; ++i) { + if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) { + fprintf(stderr, "aptX decoder utility\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "This utility decodes aptX or aptX HD audio stream\n"); + fprintf(stderr, "from stdin to a raw 24 bit signed stereo on stdout\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "When input is damaged it tries to synchronize and recover\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Usage:\n"); + fprintf(stderr, " %s [options]\n", argv[0]); + fprintf(stderr, "\n"); + fprintf(stderr, "Options:\n"); + fprintf(stderr, " -h, --help Display this help\n"); + fprintf(stderr, " --hd Decode from aptX HD\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Examples:\n"); + fprintf(stderr, " %s < sample.aptx > sample.s24\n", argv[0]); + fprintf(stderr, " %s --hd < sample.aptxhd > sample.s24\n", argv[0]); + fprintf(stderr, " %s < sample.aptx | play -t raw -r 44.1k -s -3 -c 2 -\n", argv[0]); + return 1; + } else if (strcmp(argv[i], "--hd") == 0) { + hd = 1; + } else { + fprintf(stderr, "%s: Invalid option %s\n", argv[0], argv[i]); + return 1; + } + } + + /* every eight sample contains synchronization parity check */ + sample_size = 8 * (hd ? 6 : 4); + + ctx = aptx_init(hd); + if (!ctx) { + fprintf(stderr, "%s: Cannot initialize aptX encoder\n", argv[0]); + return 1; + } + + failed = 0; + offset = 0; + + /* Try to guess type of input stream based on the first six bytes + * Encoder produces fixed first sample because aptX predictor has fixed values */ + length = fread(input_buffer, 1, 6, stdin); + if (length >= 4 && memcmp(input_buffer, "\x4b\xbf\x4b\xbf", 4) == 0) { + if (hd) + fprintf(stderr, "%s: Input looks like aptX audio stream (not aptX HD), try without --hd\n", argv[0]); + } else if (length >= 6 && memcmp(input_buffer, "\x73\xbe\xff\x73\xbe\xff", 6) == 0) { + if (!hd) + fprintf(stderr, "%s: Input looks like aptX HD audio stream, try with --hd\n", argv[0]); + } else { + fprintf(stderr, "%s: Input does not look like aptX nor aptX HD audio stream\n", argv[0]); + } + + while (length > 0 || !feof(stdin)) { + /* For decoding we need at least eight samples for synchronization */ + if (length < sample_size && !feof(stdin)) { + if (length > 0) + memmove(input_buffer, input_buffer + offset, length); + offset = 0; + length += fread(input_buffer + length, 1, sizeof(input_buffer) - length, stdin); + if (ferror(stdin)) + fprintf(stderr, "%s: aptX encoding failed to read input data\n", argv[0]); + } + + process_size = length; + + /* Always process multiple of the 8 samples (expect last) for synchronization support */ + if (length >= sample_size) + process_size -= process_size % sample_size; + + /* When decoding previous samples failed, reset internal state, predictor and state of the synchronization parity */ + if (failed > 0) + aptx_reset(ctx); + + processed = aptx_decode(ctx, input_buffer + offset, process_size, output_buffer, sizeof(output_buffer), &written); + + if (processed > sample_size && failed > 0) { + fprintf(stderr, "%s: ... synchronization successful, dropped %u bytes\n", argv[0], failed); + failed = 0; + } + + /* If we have not decoded all supplied samples then decoding failed */ + if (processed != process_size) { + if (failed == 0) { + if (length < sample_size) + fprintf(stderr, "%s: aptX decoding stopped in the middle of the sample, dropped %u bytes\n", argv[0], (unsigned int)(length-processed)); + else + fprintf(stderr, "%s: aptX decoding failed, trying to synchronize ...\n", argv[0]); + } + if (length >= sample_size) + failed++; + else if (failed > 0) + failed += length; + if (processed <= sample_size) { + /* If we have not decoded at least 8 samples (with proper parity check) + * drop decoded buffer and try decoding again on next byte */ + processed = 1; + written = 0; + } + } else if (length < sample_size) { + fprintf(stderr, "%s: aptX decoding stopped in the middle of the sample\n", argv[0]); + } + + if (written > 0) { + if (fwrite(output_buffer, 1, written, stdout) != written) { + fprintf(stderr, "%s: aptX decoding failed to write decoded data\n", argv[0]); + failed = 0; + length = 0; + break; + } + } + + if (length < sample_size) + break; + + length -= processed; + offset += processed; + } + + if (failed > 0) + fprintf(stderr, "%s ... synchronization failed, dropped %u bytes\n", argv[0], failed); + + aptx_finish(ctx); + return 0; +} diff --git a/openaptxenc.c b/openaptxenc.c new file mode 100644 index 0000000..01eb041 --- /dev/null +++ b/openaptxenc.c @@ -0,0 +1,93 @@ +/* + * aptX encoder utility + * Copyright (C) 2018 Pali Rohár + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include + +#include + +static unsigned char input_buffer[512*8*3*2*4]; +static unsigned char output_buffer[512*8*6]; + +int main(int argc, char *argv[]) +{ + int i; + int hd; + size_t length; + size_t processed; + size_t written; + struct aptx_context *ctx; + + hd = 0; + + for (i = 1; i < argc; ++i) { + if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) { + fprintf(stderr, "aptX encoder utility\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "This utility encodes a raw 24 bit signed stereo\n"); + fprintf(stderr, "samples from stdin to aptX or aptX HD on stdout\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Usage:\n"); + fprintf(stderr, " %s [options]\n", argv[0]); + fprintf(stderr, "\n"); + fprintf(stderr, "Options:\n"); + fprintf(stderr, " -h, --help Display this help\n"); + fprintf(stderr, " --hd Encode to aptX HD\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Examples:\n"); + fprintf(stderr, " %s < sample.s24 > sample.aptx\n", argv[0]); + fprintf(stderr, " %s --hd < sample.s24 > sample.aptxhd\n", argv[0]); + fprintf(stderr, " sox sample.wav -t raw -r 44.1k -s -3 -c 2 - | %s > sample.aptx\n", argv[0]); + return 1; + } else if (strcmp(argv[i], "--hd") == 0) { + hd = 1; + } else { + fprintf(stderr, "%s: Invalid option %s\n", argv[0], argv[i]); + return 1; + } + } + + ctx = aptx_init(hd); + if (!ctx) { + fprintf(stderr, "%s: Cannot initialize aptX encoder\n", argv[0]); + return 1; + } + + while (!feof(stdin)) { + length = fread(input_buffer, 1, sizeof(input_buffer), stdin); + if (ferror(stdin)) + fprintf(stderr, "%s: aptX encoding failed to read input data\n", argv[0]); + if (length == 0) + break; + processed = aptx_encode(ctx, input_buffer, length, output_buffer, sizeof(output_buffer), &written); + if (processed != length) + fprintf(stderr, "%s: aptX encoding stopped in the middle of the sample, dropped %u bytes\n", argv[0], (unsigned int)(length-processed)); + else if (processed % (8*3*2*4)) + fprintf(stderr, "%s: aptX encoding stopped in the middle of the sample\n", argv[0]); + if (fwrite(output_buffer, 1, written, stdout) != written) { + fprintf(stderr, "%s: aptX encoding failed to write encoded data\n", argv[0]); + break; + } + if (processed != length) + break; + } + + aptx_finish(ctx); + return 0; +}