From 5ebede5afeda5887f0e89877e437f8d0413035ac Mon Sep 17 00:00:00 2001 From: Eunhye Choi Date: Fri, 15 Mar 2019 18:32:10 +0900 Subject: [PATCH] Imported Upstream version 2.1.0 --- .gitignore | 40 + .vscode/tasks.json | 32 + COPYING.TXT | 458 ++++++++ Makefile.am | 65 ++ README.html | 914 ++++++++++++++++ bin/run_test | 21 + bin/run_test.cmd | 19 + bootstrap | 24 + config/README.TXT | 3 + config/am_include.mk | 26 + configure.ac | 261 +++++ createsrcpack | 16 + include/BPMDetect.h | 205 ++++ include/FIFOSampleBuffer.h | 177 ++++ include/FIFOSamplePipe.h | 230 ++++ include/Makefile.am | 22 + include/STTypes.h | 183 ++++ include/SoundTouch.h | 348 ++++++ include/soundtouch_config.h.in | 5 + make-win.bat | 40 + readme.md | 50 + soundtouch.m4 | 67 ++ soundtouch.pc.in | 11 + source/Android-lib/.classpath | 9 + source/Android-lib/.project | 33 + source/Android-lib/AndroidManifest.xml | 30 + source/Android-lib/README-SoundTouch-Android.html | 127 +++ source/Android-lib/jni/Android.mk | 51 + source/Android-lib/jni/Application.mk | 9 + source/Android-lib/jni/soundtouch-jni.cpp | 255 +++++ source/Android-lib/lint.xml | 3 + source/Android-lib/proguard-project.txt | 20 + source/Android-lib/project.properties | 14 + .../Android-lib/res/drawable-hdpi/ic_launcher.png | Bin 0 -> 7658 bytes .../Android-lib/res/drawable-mdpi/ic_launcher.png | Bin 0 -> 3777 bytes .../Android-lib/res/drawable-xhdpi/ic_launcher.png | Bin 0 -> 12516 bytes .../res/drawable-xxhdpi/ic_launcher.png | Bin 0 -> 24777 bytes source/Android-lib/res/layout/activity_example.xml | 140 +++ source/Android-lib/res/values/strings.xml | 7 + source/Android-lib/res/values/styles.xml | 20 + .../src/net/surina/ExampleActivity.java | 219 ++++ .../src/net/surina/soundtouch/SoundTouch.java | 79 ++ source/Makefile.am | 24 + source/SoundStretch/Makefile.am | 50 + source/SoundStretch/RunParameters.cpp | 291 +++++ source/SoundStretch/RunParameters.h | 65 ++ source/SoundStretch/WavFile.cpp | 986 +++++++++++++++++ source/SoundStretch/WavFile.h | 277 +++++ source/SoundStretch/main.cpp | 322 ++++++ source/SoundStretch/soundstretch.sln | 37 + source/SoundStretch/soundstretch.vcxproj | 337 ++++++ source/SoundTouch/AAFilter.cpp | 222 ++++ source/SoundTouch/AAFilter.h | 93 ++ source/SoundTouch/BPMDetect.cpp | 572 ++++++++++ source/SoundTouch/FIFOSampleBuffer.cpp | 267 +++++ source/SoundTouch/FIRFilter.cpp | 324 ++++++ source/SoundTouch/FIRFilter.h | 139 +++ source/SoundTouch/InterpolateCubic.cpp | 196 ++++ source/SoundTouch/InterpolateCubic.h | 63 ++ source/SoundTouch/InterpolateLinear.cpp | 296 ++++++ source/SoundTouch/InterpolateLinear.h | 88 ++ source/SoundTouch/InterpolateShannon.cpp | 181 ++++ source/SoundTouch/InterpolateShannon.h | 68 ++ source/SoundTouch/Makefile.am | 74 ++ source/SoundTouch/PeakFinder.cpp | 277 +++++ source/SoundTouch/PeakFinder.h | 90 ++ source/SoundTouch/RateTransposer.cpp | 307 ++++++ source/SoundTouch/RateTransposer.h | 163 +++ source/SoundTouch/SoundTouch.cpp | 538 ++++++++++ source/SoundTouch/SoundTouch.sln | 29 + source/SoundTouch/SoundTouch.vcxproj | 338 ++++++ source/SoundTouch/TDStretch.cpp | 1111 ++++++++++++++++++++ source/SoundTouch/TDStretch.h | 279 +++++ source/SoundTouch/cpu_detect.h | 55 + source/SoundTouch/cpu_detect_x86.cpp | 130 +++ source/SoundTouch/mmx_optimized.cpp | 396 +++++++ source/SoundTouch/sse_optimized.cpp | 365 +++++++ source/SoundTouchDLL/DllTest/DllTest.cpp | 114 ++ source/SoundTouchDLL/DllTest/DllTest.vcxproj | 180 ++++ source/SoundTouchDLL/SoundTouchDLL.cpp | 527 ++++++++++ source/SoundTouchDLL/SoundTouchDLL.h | 229 ++++ source/SoundTouchDLL/SoundTouchDLL.pas | 482 +++++++++ source/SoundTouchDLL/SoundTouchDLL.rc | 100 ++ source/SoundTouchDLL/SoundTouchDLL.sln | 50 + source/SoundTouchDLL/SoundTouchDLL.vcxproj | 269 +++++ source/SoundTouchDLL/make-gnu-dll.sh | 22 + source/SoundTouchDLL/resource.h | 15 + source/csharp-example/App.config | 6 + source/csharp-example/App.xaml | 9 + source/csharp-example/App.xaml.cs | 25 + source/csharp-example/MainWindow.xaml | 31 + source/csharp-example/MainWindow.xaml.cs | 258 +++++ source/csharp-example/NAudio-license.txt | 31 + source/csharp-example/NAudio-readme.txt | 92 ++ source/csharp-example/NAudio.dll | Bin 0 -> 475136 bytes source/csharp-example/Properties/AssemblyInfo.cs | 55 + .../Properties/Resources.Designer.cs | 71 ++ source/csharp-example/Properties/Resources.resx | 117 +++ .../csharp-example/Properties/Settings.Designer.cs | 30 + source/csharp-example/Properties/Settings.settings | 7 + source/csharp-example/README.txt | 9 + source/csharp-example/SoundProcessor.cs | 292 +++++ source/csharp-example/SoundTouch.cs | 681 ++++++++++++ source/csharp-example/SoundTouch.dll | Bin 0 -> 130560 bytes source/csharp-example/csharp-example.csproj | 123 +++ source/csharp-example/csharp-example.sln | 22 + 106 files changed, 17130 insertions(+) create mode 100644 .gitignore create mode 100644 .vscode/tasks.json create mode 100644 COPYING.TXT create mode 100644 Makefile.am create mode 100644 README.html create mode 100755 bin/run_test create mode 100644 bin/run_test.cmd create mode 100755 bootstrap create mode 100644 config/README.TXT create mode 100644 config/am_include.mk create mode 100644 configure.ac create mode 100755 createsrcpack create mode 100644 include/BPMDetect.h create mode 100644 include/FIFOSampleBuffer.h create mode 100644 include/FIFOSamplePipe.h create mode 100644 include/Makefile.am create mode 100644 include/STTypes.h create mode 100644 include/SoundTouch.h create mode 100644 include/soundtouch_config.h.in create mode 100644 make-win.bat create mode 100644 readme.md create mode 100644 soundtouch.m4 create mode 100644 soundtouch.pc.in create mode 100644 source/Android-lib/.classpath create mode 100644 source/Android-lib/.project create mode 100644 source/Android-lib/AndroidManifest.xml create mode 100644 source/Android-lib/README-SoundTouch-Android.html create mode 100644 source/Android-lib/jni/Android.mk create mode 100644 source/Android-lib/jni/Application.mk create mode 100644 source/Android-lib/jni/soundtouch-jni.cpp create mode 100644 source/Android-lib/lint.xml create mode 100644 source/Android-lib/proguard-project.txt create mode 100644 source/Android-lib/project.properties create mode 100644 source/Android-lib/res/drawable-hdpi/ic_launcher.png create mode 100644 source/Android-lib/res/drawable-mdpi/ic_launcher.png create mode 100644 source/Android-lib/res/drawable-xhdpi/ic_launcher.png create mode 100644 source/Android-lib/res/drawable-xxhdpi/ic_launcher.png create mode 100644 source/Android-lib/res/layout/activity_example.xml create mode 100644 source/Android-lib/res/values/strings.xml create mode 100644 source/Android-lib/res/values/styles.xml create mode 100644 source/Android-lib/src/net/surina/ExampleActivity.java create mode 100644 source/Android-lib/src/net/surina/soundtouch/SoundTouch.java create mode 100644 source/Makefile.am create mode 100644 source/SoundStretch/Makefile.am create mode 100644 source/SoundStretch/RunParameters.cpp create mode 100644 source/SoundStretch/RunParameters.h create mode 100644 source/SoundStretch/WavFile.cpp create mode 100644 source/SoundStretch/WavFile.h create mode 100644 source/SoundStretch/main.cpp create mode 100644 source/SoundStretch/soundstretch.sln create mode 100644 source/SoundStretch/soundstretch.vcxproj create mode 100644 source/SoundTouch/AAFilter.cpp create mode 100644 source/SoundTouch/AAFilter.h create mode 100644 source/SoundTouch/BPMDetect.cpp create mode 100644 source/SoundTouch/FIFOSampleBuffer.cpp create mode 100644 source/SoundTouch/FIRFilter.cpp create mode 100644 source/SoundTouch/FIRFilter.h create mode 100644 source/SoundTouch/InterpolateCubic.cpp create mode 100644 source/SoundTouch/InterpolateCubic.h create mode 100644 source/SoundTouch/InterpolateLinear.cpp create mode 100644 source/SoundTouch/InterpolateLinear.h create mode 100644 source/SoundTouch/InterpolateShannon.cpp create mode 100644 source/SoundTouch/InterpolateShannon.h create mode 100644 source/SoundTouch/Makefile.am create mode 100644 source/SoundTouch/PeakFinder.cpp create mode 100644 source/SoundTouch/PeakFinder.h create mode 100644 source/SoundTouch/RateTransposer.cpp create mode 100644 source/SoundTouch/RateTransposer.h create mode 100644 source/SoundTouch/SoundTouch.cpp create mode 100644 source/SoundTouch/SoundTouch.sln create mode 100644 source/SoundTouch/SoundTouch.vcxproj create mode 100644 source/SoundTouch/TDStretch.cpp create mode 100644 source/SoundTouch/TDStretch.h create mode 100644 source/SoundTouch/cpu_detect.h create mode 100644 source/SoundTouch/cpu_detect_x86.cpp create mode 100644 source/SoundTouch/mmx_optimized.cpp create mode 100644 source/SoundTouch/sse_optimized.cpp create mode 100644 source/SoundTouchDLL/DllTest/DllTest.cpp create mode 100644 source/SoundTouchDLL/DllTest/DllTest.vcxproj create mode 100644 source/SoundTouchDLL/SoundTouchDLL.cpp create mode 100644 source/SoundTouchDLL/SoundTouchDLL.h create mode 100644 source/SoundTouchDLL/SoundTouchDLL.pas create mode 100644 source/SoundTouchDLL/SoundTouchDLL.rc create mode 100644 source/SoundTouchDLL/SoundTouchDLL.sln create mode 100644 source/SoundTouchDLL/SoundTouchDLL.vcxproj create mode 100755 source/SoundTouchDLL/make-gnu-dll.sh create mode 100644 source/SoundTouchDLL/resource.h create mode 100644 source/csharp-example/App.config create mode 100644 source/csharp-example/App.xaml create mode 100644 source/csharp-example/App.xaml.cs create mode 100644 source/csharp-example/MainWindow.xaml create mode 100644 source/csharp-example/MainWindow.xaml.cs create mode 100644 source/csharp-example/NAudio-license.txt create mode 100644 source/csharp-example/NAudio-readme.txt create mode 100644 source/csharp-example/NAudio.dll create mode 100644 source/csharp-example/Properties/AssemblyInfo.cs create mode 100644 source/csharp-example/Properties/Resources.Designer.cs create mode 100644 source/csharp-example/Properties/Resources.resx create mode 100644 source/csharp-example/Properties/Settings.Designer.cs create mode 100644 source/csharp-example/Properties/Settings.settings create mode 100644 source/csharp-example/README.txt create mode 100644 source/csharp-example/SoundProcessor.cs create mode 100644 source/csharp-example/SoundTouch.cs create mode 100644 source/csharp-example/SoundTouch.dll create mode 100644 source/csharp-example/csharp-example.csproj create mode 100644 source/csharp-example/csharp-example.sln diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9def793 --- /dev/null +++ b/.gitignore @@ -0,0 +1,40 @@ +bin +lib + +# Win build files +*.dll +*.exe +*.lib + +# GNU build files +*.o +*.so* +*.lo +*.P* +*.la* +*.a +*.pc +*config* +Makefile +Makefile.in +.libs +aclocal.m4 +autom4te.cache +stamp-* +libtool +soundstretch + +# Files generated by MSVC +*.bsc +*.suo +*.sdf +*.filters +*.user +source/SoundTouch/Win32/ +source/SoundTouch/x64/ +source/SoundStretch/Win32/ +source/SoundStretch/x64/ +source/SoundTouchDll/Win32/ +source/SoundTouchDll/x64/ +source/SoundTouchDll/DllTest/Win32/ +source/SoundTouchDll/DllTest/x64/ diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..66abc34 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,32 @@ +{ + // This is build task definition file for MS VisualStudio Code. + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "echo", + "type": "shell", + "command": "echo Hello" + }, + { + "label": "configure", + "type": "shell", + "command": "./bootstrap && ./configure" + }, + { + "label": "build", + "type": "shell", + "command": "make -j4", + "problemMatcher": [ + "$gcc" + ] + }, + { + "label": "clean", + "type": "shell", + "command": "make clean", + "problemMatcher": [] + } + ] +} \ No newline at end of file diff --git a/COPYING.TXT b/COPYING.TXT new file mode 100644 index 0000000..bbd24e6 --- /dev/null +++ b/COPYING.TXT @@ -0,0 +1,458 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authoried 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 \ No newline at end of file diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..1394ce6 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,65 @@ +## Process this file with automake to create Makefile.in +## +## This file is part of SoundTouch, an audio processing library for pitch/time adjustments +## +## SoundTouch is free software; you can redistribute it and/or modify it under the +## terms of the GNU General Public License as published by the Free Software +## Foundation; either version 2 of the License, or (at your option) any later +## version. +## +## SoundTouch is distributed in the hope that it will be useful, but WITHOUT ANY +## WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +## A PARTICULAR PURPOSE. See the GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License along with +## this program; if not, write to the Free Software Foundation, Inc., 59 Temple +## Place - Suite 330, Boston, MA 02111-1307, USA + +## I used config/am_include.mk for common definitions +include $(top_srcdir)/config/am_include.mk + +## Descend into SUBDIRS and run make. Look at the Makefile.am files in the +## subdirectories Start at frontend_fox/Makefile.am to see how everything works. +SUBDIRS=include source + +# list files that are documentation to be packaged in a release tarball and installed +dist_doc_DATA=COPYING.TXT README.html + +# extra data files that are to be pacakged in a release tarball and installed into the data directory +#pkgdata_DATA= + +# sets up for soundtouch.m4 to be installed +m4datadir=$(datadir)/aclocal +m4data_DATA=soundtouch.m4 + +## These extra files and directories will be included in the distribution. by +## using make-dist by default many common filenames are automatically included +## such as AUTHORS, COPYING, etc the bootstrap script really shouldn't be a part +## of a final package, but it is useful for developers who might want to make +## changes to the configure scripts or makefiles. +# NOTE: wouldn't have to list the .TXT file if it were named without the .TXT +EXTRA_DIST= \ + soundtouch.m4 \ + config/m4 \ + bootstrap \ + make-win.bat \ + COPYING.TXT \ + README.html + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = soundtouch.pc + +## This removes stuff from the distribution which may be present +## from a cvs checkout or other build reasons +dist-hook: + rm -rf `find $(distdir) -type d -name CVS` # remove all CVS directories + echo rm -rf `find $(distdir) -type f -name \.\#\*` # CVS revision files left around for some reason + +## This happens at 'make distclean' +#distcleancheck: +# rm -rf files-that-may-also-need-to-be-deleted-on-'make distclean' + + +# flag to aclocal where to find m4 macros for tests +ACLOCAL_AMFLAGS = -I config/m4 +AUTOMAKE_OPTIONS = foreign diff --git a/README.html b/README.html new file mode 100644 index 0000000..9373432 --- /dev/null +++ b/README.html @@ -0,0 +1,914 @@ + + + + SoundTouch library README + + + + + + + +
+

SoundTouch audio processing library v2.1

+

SoundTouch library Copyright © Olli Parviainen 2001-2018

+
+

1. Introduction

+

SoundTouch is an open-source audio processing library that allows +changing the sound tempo, pitch and playback rate parameters +independently from each other, i.e.:

+ +

1.1 Contact information

+

Author email: oparviai 'at' iki.fi

+

SoundTouch WWW page: http://soundtouch.surina.net

+

SoundTouch git repository: https://gitlab.com/soundtouch/soundtouch.git

+
+

2. Compiling SoundTouch

+

Before compiling, notice that you can choose the sample data format if it's +desirable to use floating point sample data instead of 16bit integers. See +section "sample data format" for more information.

+

Also notice that SoundTouch can use OpenMP instructions for parallel +computation to accelerate the runtime processing speed in multi-core systems, +however, these improvements need to be separately enabled before compiling. See +OpenMP notes in Chapter 3 below.

+

2.1. Building in Microsoft Windows

+

Project files for Microsoft Visual C++ are supplied with the source +code package. Go to Microsoft WWW page to download + +Microsoft Visual Studio Express version for free. +

+

To build the binaries with Visual C++ compiler, either run +"make-win.bat" script, or open the appropriate project files in source +code directories with Visual Studio. The final executable will appear +under the "SoundTouch\bin" directory. If using the Visual Studio IDE +instead of the make-win.bat script, directories bin and lib may need to +be created manually to the SoundTouch package root for the final +executables. The make-win.bat script creates these directories +automatically.

+

C# example: The source code package includes also a C# example + application for Windows that shows how to invoke SoundTouch.dll + dynamic-load library for processing mp3 audio. +

OpenMP NOTE: If activating the OpenMP parallel computing in +the compilation, the target program will require additional vcomp dll library to +properly run. In Visual C++ 9.0 these libraries can be found in the following +folders.

+ +

In Visual Studio 2008, a SP1 version may be required for these libraries. In +other VC++ versions the required library will be expectedly found in similar +"redist" location.

+

Notice that as minor demonstration of a "dll hell" phenomenon both the 32-bit +and 64-bit version of vcomp90.dll have the same filename but different contents, +thus choose the proper version to allow the program start.

+

2.2. Building in Gnu platforms

+

The SoundTouch library compiles in practically any platform +supporting GNU compiler (GCC) tools. SoundTouch requires GCC version 4.3 or later.

+

To build and install the binaries, run the following commands in +/soundtouch directory:

+ + + + + + + + + + + + + + + + + + + +
+
./bootstrap  -
+
Creates "configure" file with +local autoconf/automake toolset.
+
+
./configure  -
+
+

Configures the SoundTouch package for the local environment. +Notice that "configure" file is not available before running the +"./bootstrap" command as above.
+

+
+
make         -
+
+

Builds the SoundTouch library & SoundStretch utility. You can + optionally add "-j" switch after "make" to speed up the compilation in + multi-core systems.

+
+
make install -
+
+

Installs the SoundTouch & BPM libraries to /usr/local/lib +and SoundStretch utility to /usr/local/bin. Please notice that +'root' privileges may be required to install the binaries to the +destination locations.

+
+

2.2.1 Required GNU tools

+

Bash shell, GNU C++ compiler, libtool, autoconf and automake tools +are required for compiling the SoundTouch library. These are usually +included with the GNU/Linux distribution, but if not, install these +packages first. For example, Ubuntu Linux can acquire and install +these with the following command:

+
sudo apt-get install automake autoconf libtool build-essential
+

2.2.2 Problems with GCC compiler compatibility

+

At the release time the SoundTouch package has been tested to +compile in GNU/Linux platform. However, If you have problems getting the +SoundTouch library compiled, try disabling optimizations that are specific for +x86 processors by running ./configure script with switch +

+
--enable-x86-optimizations=no
+
+ +Alternatively, if you don't use GNU Configure system, edit file "include/STTypes.h" +directly and remove the following definition:
+
#define SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS 1
+
+ +

2.2.3 Compiling Shared Library / DLL version in Cygwin

+

+ The GNU compilation does not automatically create a shared-library version of + SoundTouch (.so or .dll). If such is desired, then you can create it as follows + after running the usual compilation:

+
+
g++ -shared -static -DDLL_EXPORTS -I../../include -o SoundTouch.dll \
+     SoundTouchDLL.cpp ../SoundTouch/.libs/libSoundTouch.a
+sstrip SoundTouch.dll
+
+ +

2.3. Building in Android

+

Android compilation instructions are within the + source code package, see file "source/Android-lib/README-SoundTouch-Android.html" + in the source code package.

+

The Android compilation automatically builds separate .so library binaries +for ARM, X86 and MIPS processor architectures. For optimal device support, +include all these .so library binaries into the Android .apk application +package, so the target Android device can automatically choose the proper +library binary version to use.

+

The source/Android-lib folder includes also an Android +example application that processes WAV audio files using SoundTouch library in +Android devices.

+ +
+

3. About implementation & Usage tips

3.1. Supported sample data formats

+

The sample data format can be chosen between 16bit signed integer +and 32bit floating point values. The default is 32bit floating point format, +which will also provide slightly better sound quality over the integer format.

+

In Windows environment, the sample data format is chosen in file +"STTypes.h" by choosing one of the following defines:

+ +

In GNU environment, the floating sample format is used by default, +but integer sample format can be chosen by giving the following switch +to the configure script:

+
+
./configure --enable-integer-samples
+
+

The sample data can have either single (mono) or double (stereo) +audio channel. Stereo data is interleaved so that every other data +value is for left channel and every second for right channel. Notice +that while it'd be possible in theory to process stereo sound as two +separate mono channels, this isn't recommended because processing the +channels separately would result in losing the phase coherency between +the channels, which consequently would ruin the stereo effect.

+

Sample rates between 8000-48000H are supported.

+

3.2. Processing latency

+

The processing and latency constraints of the SoundTouch library are:

+ +

3.3. About algorithms

+

SoundTouch provides three seemingly independent effects: tempo, +pitch and playback rate control. These three controls are implemented +as combination of two primary effects, sample rate transposing +and time-stretching.

+

Sample rate transposing affects both the audio stream +duration and pitch. It's implemented simply by converting the original +audio sample stream to the desired duration by interpolating from +the original audio samples. In SoundTouch, linear interpolation with +anti-alias filtering is used. Theoretically a higher-order +interpolation provide better result than 1st order linear +interpolation, but in audio application linear interpolation together +with anti-alias filtering performs subjectively about as well as +higher-order filtering would.

+

Time-stretching means changing the audio stream duration +without affecting it's pitch. SoundTouch uses WSOLA-like +time-stretching routines that operate in the time domain. Compared to +sample rate transposing, time-stretching is a much heavier operation +and also requires a longer processing "window" of sound samples used by +the processing algorithm, thus increasing the algorithm input/output +latency. Typical i/o latency for the SoundTouch time-stretch algorithm +is around 100 ms.

+

Sample rate transposing and time-stretching are then used together +to produce the tempo, pitch and rate controls:

+ +

3.4 Tuning the algorithm parameters

+

The time-stretch algorithm has few parameters that can be tuned to +optimize sound quality for certain application. The current default +parameters have been chosen by iterative if-then analysis (read: "trial +and error") to obtain best subjective sound quality in pop/rock music +processing, but in applications processing different kind of sound the +default parameter set may result into a sub-optimal result.

+

The time-stretch algorithm default parameter values are set by the +following #defines in file "TDStretch.h":

+
+
#define DEFAULT_SEQUENCE_MS     AUTOMATIC
#define DEFAULT_SEEKWINDOW_MS AUTOMATIC
#define DEFAULT_OVERLAP_MS 8
+
+

These parameters affect to the time-stretch algorithm as follows:

+ +

Notice that these parameters can also be set during execution time +with functions "TDStretch::setParameters()" and "SoundTouch::setSetting()".

+

The table below summaries how the parameters can be adjusted for +different applications:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Parameter nameDefault value magnitudeLarger value affects...Smaller value affects...Effect to CPU burden
+
SEQUENCE_MS
+
Default value is relatively large, chosen for +slowing down music tempoLarger value is usually better for slowing down +tempo. Growing the value decelerates the "echoing" artifact when +slowing down the tempo.Smaller value might be better for speeding up +tempo. Reducing the value accelerates the "echoing" artifact when +slowing down the tempo Increasing the parameter value reduces +computation burden
+
SEEKWINDOW_MS
+
Default value is relatively large, chosen for +slowing down music tempoLarger value eases finding a good mixing +position, but may cause a "drifting" artifactSmaller reduce possibility to find a good mixing +position, but reduce the "drifting" artifact.Increasing the parameter value increases +computation burden
+
OVERLAP_MS
+
Default value is relatively large, chosen to +suit with above parameters.If you reduce the "sequence ms" setting, you +might wish to try a smaller value.Increasing the parameter value increases +computation burden
+

3.5 Performance Optimizations

+

General optimizations:

+

The time-stretch routine has a 'quick' mode that substantially +speeds up the algorithm but may slightly compromise the sound quality. +This mode is activated by calling SoundTouch::setSetting() +function with parameter id of SETTING_USE_QUICKSEEK and value +"1", i.e.

+
+

setSetting(SETTING_USE_QUICKSEEK, 1);

+
+

CPU-specific optimizations:

+

Intel x86 specific SIMD optimizations are implemented using compiler +intrinsics, providing about a 3x processing speedup for x86 compatible +processors vs. non-SIMD implementation:

+ +

3.5 OpenMP parallel computation

+

SoundTouch 1.9 onwards support running the algorithms parallel in several CPU +cores. Based on benchmark the experienced multi-core processing speed-up gain +ranges between +30% (on a high-spec dual-core x86 Windows PC) to 215% (on a moderately low-spec +quad-core ARM of Raspberry Pi2).

+

See an external blog article with more detailed discussion about the + +SoundTouch OpenMP optimization.

+

The parallel computing support is implemented using OpenMP spec 3.0 +instructions. These instructions are supported by Visual C++ 2008 and later, and +GCC v4.2 and later. Compilers that do not supporting OpenMP will ignore these +optimizations and routines will still work properly. Possible warnings about +unknown #pragmas are related to OpenMP support and can be safely ignored.

+

The OpenMP improvements are disabled by default, and need to be enabled by +developer during compile-time. Reason for this is that parallel processing adds +moderate runtime overhead in managing the multi-threading, so it may not be +necessary nor desirable in all applications. For example real-time processing +that is not constrained by CPU power will not benefit of speed-up provided by +the parallel processing, in the contrary it may increase power consumption due +to the increased overhead.

+

However, applications that run on low-spec multi-core CPUs and may otherwise +have possibly constrained performance will benefit of the OpenMP improvements. +This include for example multi-core embedded devices.

+

OpenMP parallel computation can be enabled before compiling SoundTouch +library as follows:

+ +
+

4. SoundStretch audio processing utility +

+

SoundStretch audio processing utility
+ Copyright (c) Olli Parviainen 2002-2015

+

SoundStretch is a simple command-line application that can change +tempo, pitch and playback rates of WAV sound files. This program is +intended primarily to demonstrate how the "SoundTouch" library can be +used to process sound in your own program, but it can as well be used +for processing sound files.

+

4.1. SoundStretch Usage Instructions

+

SoundStretch Usage syntax:

+
+
soundstretch infilename outfilename [switches]
+
+

Where:

+ + + + + + + + + + + + + + + +
+
"infilename"
+
Name of the input sound data file (in .WAV audio +file format). Give "stdin" as filename to use standard input pipe.
+
"outfilename"
+
Name of the output sound file where the +resulting sound is saved (in .WAV audio file format). This parameter +may be omitted if you don't want to save the output (e.g. when +only calculating BPM rate with '-bpm' switch). Give "stdout" as +filename to use standard output pipe.
+
[switches]
+
Are one or more control switches.
+

Available control switches are:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
-tempo=n 
+
Change the sound tempo by n percents (n = -95.0 +.. +5000.0 %)
+
-pitch=n
+
Change the sound pitch by n semitones (n = -60.0 +.. + 60.0 semitones)
+
-rate=n
+
Change the sound playback rate by n percents (n += -95.0 .. +5000.0 %)
+
-bpm=n
+
Detect the Beats-Per-Minute (BPM) rate of the +sound and adjust the tempo to meet 'n' BPMs. When this switch is +applied, the "-tempo" switch is ignored. If "=n" is omitted, i.e. +switch "-bpm" is used alone, then the BPM rate is estimated and +displayed, but tempo not adjusted according to the BPM value.
+
-quick
+
Use quicker tempo change algorithm. Gains speed +but loses sound quality.
+
-naa
+
Don't use anti-alias filtering in sample rate +transposing. Gains speed but loses sound quality.
+
-license
+
Displays the program license text (LGPL)
+

Notes:

+ +

4.2. SoundStretch usage examples

+

Example 1

+

The following command increases tempo of the sound file +"originalfile.wav" by 12.5% and stores result to file +"destinationfile.wav":

+
+
soundstretch originalfile.wav destinationfile.wav -tempo=12.5
+
+

Example 2

+

The following command decreases the sound pitch (key) of the sound +file "orig.wav" by two semitones and stores the result to file +"dest.wav":

+
+
soundstretch orig.wav dest.wav -pitch=-2
+
+

Example 3

+

The following command processes the file "orig.wav" by decreasing +the sound tempo by 25.3% and increasing the sound pitch (key) by 1.5 +semitones. Resulting .wav audio data is directed to standard output +pipe:

+
+
soundstretch orig.wav stdout -tempo=-25.3 -pitch=1.5
+
+

Example 4

+

The following command detects the BPM rate of the file "orig.wav" +and adjusts the tempo to match 100 beats per minute. Result is stored +to file "dest.wav":

+
+
soundstretch orig.wav dest.wav -bpm=100
+
+

Example 5

+

The following command reads .wav sound data from standard input pipe +and estimates the BPM rate:

+
+
soundstretch stdin -bpm
+
+

Example 6

+

The following command tunes song from original 440Hz tuning to 432Hz tuning: +this corresponds to lowering the pitch by -0.318 semitones:

+
+
soundstretch original.wav output.wav -pitch=-0.318
+
+
+

5. Change History

+

5.1. SoundTouch library Change History

+

2.1:

+ +

2.0:

+ +

1.9.2:

+ +

1.9.1:

+ +

1.9:

+ +

1.8.0:

+ +

1.7.1:

+ +

1.7.0:

+ +

1.6.0:

+ +

1.5.0:

+ +

1.4.1:

+ +

1.4.0:

+ +

1.3.1:

+ +

1.3.0:

+ +

1.2.1:

+ +

1.2.0:

+ +

1.1.1:

+ +

1.0.1:

+ +

1.0:

+ +

5.2. SoundStretch application Change History

+

1.9:

+ + +

1.7.0:

+ +

1.5.0:

+ +

1.4.0:

+ +

1.3.0:

+ +

1.2.1:

+ +

1.2.0:

+ +

1.1.1:

+ +

1.1:

+ +

1.01:

+ +
+

6. Acknowledgements

+

Kudos for these people who have contributed to development or +submitted bugfixes:

+ +

Moral greetings to all other contributors and users also!

+
+

7. LICENSE

+

SoundTouch audio processing library
+Copyright (c) Olli Parviainen

+

This library is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License version 2.1 +as published by the Free Software Foundation.

+

This library is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser +General Public License for more details.

+

You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

+

---

+

commercial license alternative also available, contact author for details.

+
+

README.html file updated in May-2018

+ + diff --git a/bin/run_test b/bin/run_test new file mode 100755 index 0000000..f7d0cf1 --- /dev/null +++ b/bin/run_test @@ -0,0 +1,21 @@ +SOUND_DIR=~/dev/test_sounds +OUT_DIR=./test_out +TEST_NAME=semmari +OUT_NAME=out +SS=./soundstretch +TEST_PARAM="-pitch=-3 -bpm" + +mkdir $OUT_DIR + +$SS $SOUND_DIR/$TEST_NAME-8b1.wav $OUT_DIR/$OUT_NAME-8b1.wav $TEST_PARAM +$SS $SOUND_DIR/$TEST_NAME-8b2.wav $OUT_DIR/$OUT_NAME-8b2.wav $TEST_PARAM + +$SS $SOUND_DIR/$TEST_NAME-16b1.wav $OUT_DIR/$OUT_NAME-16b1.wav $TEST_PARAM +$SS $SOUND_DIR/$TEST_NAME-16b2.wav $OUT_DIR/$OUT_NAME-16b2.wav $TEST_PARAM + +$SS $SOUND_DIR/$TEST_NAME-24b1.wav $OUT_DIR/$OUT_NAME-24b1.wav $TEST_PARAM +$SS $SOUND_DIR/$TEST_NAME-24b2.wav $OUT_DIR/$OUT_NAME-24b2.wav $TEST_PARAM + +$SS $SOUND_DIR/$TEST_NAME-32b1.wav $OUT_DIR/$OUT_NAME-32b1.wav $TEST_PARAM +$SS $SOUND_DIR/$TEST_NAME-32b2.wav $OUT_DIR/$OUT_NAME-32b2.wav $TEST_PARAM + diff --git a/bin/run_test.cmd b/bin/run_test.cmd new file mode 100644 index 0000000..0af8672 --- /dev/null +++ b/bin/run_test.cmd @@ -0,0 +1,19 @@ +set SOUND_DIR=d:\dev\test_sounds +set OUT_DIR=. +set TEST_NAME=semmari +set OUT_NAME=out +set SS=soundstretch +set TEST_PARAM=-pitch=-3 -bpm + +call %SS% %SOUND_DIR%\%TEST_NAME%-8b1.wav %OUT_DIR%\%OUT_NAME%-8b1.wav %TEST_PARAM% +call %SS% %SOUND_DIR%\%TEST_NAME%-8b2.wav %OUT_DIR%\%OUT_NAME%-8b2.wav %TEST_PARAM% + +call %SS% %SOUND_DIR%\%TEST_NAME%-16b1.wav %OUT_DIR%\%OUT_NAME%-16b1.wav %TEST_PARAM% +call %SS% %SOUND_DIR%\%TEST_NAME%-16b2.wav %OUT_DIR%\%OUT_NAME%-16b2.wav %TEST_PARAM% + +call %SS% %SOUND_DIR%\%TEST_NAME%-24b1.wav %OUT_DIR%\%OUT_NAME%-24b1.wav %TEST_PARAM% +call %SS% %SOUND_DIR%\%TEST_NAME%-24b2.wav %OUT_DIR%\%OUT_NAME%-24b2.wav %TEST_PARAM% + +call %SS% %SOUND_DIR%\%TEST_NAME%-32b1.wav %OUT_DIR%\%OUT_NAME%-32b1.wav %TEST_PARAM% +call %SS% %SOUND_DIR%\%TEST_NAME%-32b2.wav %OUT_DIR%\%OUT_NAME%-32b2.wav %TEST_PARAM% + diff --git a/bootstrap b/bootstrap new file mode 100755 index 0000000..67bcbea --- /dev/null +++ b/bootstrap @@ -0,0 +1,24 @@ +#!/bin/sh + +if [ "$1" = "--clean" ] +then + if [ -a Makefile ] + then + make maintainer-clean + elif [ -a configure ] + then + configure && $0 --clean + else + bootstrap && configure && $0 --clean + fi + + rm -rf configure libtool aclocal.m4 `find . -name Makefile.in` autom4te*.cache config/config.guess config/config.h.in config/config.sub config/depcomp config/install-sh config/ltmain.sh config/missing config/mkinstalldirs config/stamp-h config/stamp-h.in + + #gettextie files + #rm -f ABOUT-NLS config/config.rpath config/m4/codeset.m4 config/m4/gettext.m4 config/m4/glibc21.m4 config/m4/iconv.m4 config/m4/intdiv0.m4 config/m4/inttypes-pri.m4 config/m4/inttypes.m4 config/m4/inttypes_h.m4 config/m4/isc-posix.m4 config/m4/lcmessage.m4 config/m4/lib-ld.m4 config/m4/lib-link.m4 config/m4/lib-prefix.m4 config/m4/progtest.m4 config/m4/stdint_h.m4 config/m4/uintmax_t.m4 config/m4/ulonglong.m4 po/Makefile.in.in po/Rules-quot po/boldquot.sed po/en@boldquot.header po/en@quot.header po/insert-header.sin po/quot.sed po/remove-potcdate.sin + +else + export AUTOMAKE="automake --add-missing --foreign --copy" + autoreconf -fisv && rm -f `find . -name "*~"` && rm -f ChangeLog + exit $? +fi diff --git a/config/README.TXT b/config/README.TXT new file mode 100644 index 0000000..7c4f6e4 --- /dev/null +++ b/config/README.TXT @@ -0,0 +1,3 @@ +Files in this directory are used by GNU autoconf/automake system. +These files aren't used/needed in the Windows environment. + diff --git a/config/am_include.mk b/config/am_include.mk new file mode 100644 index 0000000..5d6bdfb --- /dev/null +++ b/config/am_include.mk @@ -0,0 +1,26 @@ +## vim:tw=78 +## Process this file with automake to create Makefile.in +## +## This file is part of SoundTouch, an audio processing library for pitch/time adjustments +## +## SoundTouch is free software; you can redistribute it and/or modify it under the +## terms of the GNU General Public License as published by the Free Software +## Foundation; either version 2 of the License, or (at your option) any later +## version. +## +## SoundTouch is distributed in the hope that it will be useful, but WITHOUT ANY +## WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +## A PARTICULAR PURPOSE. See the GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License along with +## this program; if not, write to the Free Software Foundation, Inc., 59 Temple +## Place - Suite 330, Boston, MA 02111-1307, USA + +## These are common definitions used in all Makefiles +## It is actually included when a makefile.am is converted to Makefile.in +## by automake, so it's ok to have @MACROS@ that will be set by configure + +AM_CPPFLAGS=-I$(top_srcdir)/include + +# doc directory +pkgdocdir=$(prefix)/doc/@PACKAGE@ diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..bd8023c --- /dev/null +++ b/configure.ac @@ -0,0 +1,261 @@ +dnl This file is part of SoundTouch, an audio processing library for pitch/time adjustments +dnl +dnl SoundTouch is free software; you can redistribute it and/or modify it under the +dnl terms of the GNU General Public License as published by the Free Software +dnl Foundation; either version 2 of the License, or (at your option) any later +dnl version. +dnl +dnl SoundTouch is distributed in the hope that it will be useful, but WITHOUT ANY +dnl WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +dnl FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +dnl details. +dnl +dnl You should have received a copy of the GNU General Public License along with +dnl this program; if not, write to the Free Software Foundation, Inc., 59 Temple +dnl Place - Suite 330, Boston, MA 02111-1307, USA +# Process this file with autoconf to produce a configure script. + +AC_INIT([SoundTouch], [2.0.0], [http://www.surina.net/soundtouch]) +dnl Default to libSoundTouch.so.$LIB_SONAME.0.0 +LIB_SONAME=1 +AC_SUBST(LIB_SONAME) + +AC_CONFIG_AUX_DIR(config) +AC_CONFIG_MACRO_DIR([config/m4]) +AM_CONFIG_HEADER([config.h include/soundtouch_config.h]) +AM_INIT_AUTOMAKE +AM_SILENT_RULES([yes]) +#AC_DISABLE_SHARED dnl This makes libtool only build static libs +AC_DISABLE_STATIC dnl This makes libtool only build shared libs +#AC_GNU_SOURCE dnl enable posix extensions in glibc + +AC_LANG(C++) + +# Set AR_FLAGS to avoid build warning "ar: `u' modifier ignored since `D' is the default (see `U')" +AR_FLAGS='cr' + + +dnl ############################################################################ +dnl # Checks for programs # +dnl ############################################################################ +AC_PROG_CXX +#AC_PROG_AWK +AC_PROG_CC +AC_PROG_CPP +AC_PROG_CXXCPP +AC_PROG_INSTALL +#AC_PROG_LN_S +AC_PROG_MAKE_SET + +AM_PROG_LIBTOOL dnl turn on using libtool + + + + +dnl ############################################################################ +dnl # Checks for header files # +dnl ############################################################################ +AC_HEADER_STDC +#AC_HEADER_SYS_WAIT +# add any others you want to check for here +AC_CHECK_HEADERS([cpuid.h]) + +if test "x$ac_cv_header_cpuid_h" = "xno"; then + AC_MSG_WARN([The cpuid.h file was not found therefore the x86 optimizations will be disabled.]) + AC_MSG_WARN([If using a x86 architecture and optimizations are desired then please install gcc (>= 4.3).]) + AC_MSG_WARN([If using a non-x86 architecture then this is expected and can be ignored.]) +fi + + +dnl ############################################################################ +dnl # Checks for typedefs, structures, and compiler characteristics $ +dnl ############################################################################ +AC_C_CONST +AC_C_INLINE +#AC_TYPE_OFF_T +#AC_TYPE_SIZE_T + + +AC_ARG_ENABLE(integer-samples, + [AC_HELP_STRING([--enable-integer-samples], + [use integer samples instead of floats +[default=no]])],, + [enable_integer_samples=no]) + + +AC_ARG_ENABLE(openmp, + [AC_HELP_STRING([--enable-openmp], + [use parallel multicore calculation through OpenMP [default=no]])],, + [enable_openmp=no]) + +# Let the user enable/disable the x86 optimizations. +# Useful when compiling on non-x86 architectures. +AC_ARG_ENABLE([x86-optimizations], + [AS_HELP_STRING([--enable-x86-optimizations], + [use MMX or SSE optimization +[default=yes]])],[enable_x86_optimizations="${enableval}"], + [enable_x86_optimizations=yes]) + +# Tell the Makefile.am if the user wants to disable optimizations. +# Makefile.am will enable them by default if support is available. +# Note: We check if optimizations are supported a few lines down. +AM_CONDITIONAL([X86_OPTIMIZATIONS], [test "x$enable_x86_optimizations" = "xyes"]) + + +if test "x$enable_integer_samples" = "xyes"; then + echo "****** Integer sample type enabled ******" + AC_DEFINE(SOUNDTOUCH_INTEGER_SAMPLES,1,[Use Integer as Sample type]) +else + echo "****** Float sample type enabled ******" + AC_DEFINE(SOUNDTOUCH_FLOAT_SAMPLES,1,[Use Float as Sample type]) +fi + + +if test "x$enable_openmp" = "xyes"; then + echo "****** openmp optimizations enabled ******" + AM_CXXFLAGS="-fopenmp $AM_CXXFLAGS" +else + echo "****** openmp optimizations disabled ******" +fi + + +# Check if optimizations are supported in the system at build time. +if test "x$enable_x86_optimizations" = "xyes" -a "x$ac_cv_header_cpuid_h" = "xyes"; then + echo "****** x86 optimizations enabled ******" + + original_saved_CXXFLAGS=$CXXFLAGS + have_mmx_intrinsics=no + CXXFLAGS="-mmmx -Winline $CXXFLAGS" + + # Check if the user can compile MMX code using intrinsics. + # GCC supports MMX intrinsics since version 3.3 + # A more recent GCC (>= 4.3) is recommended. + AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ + #if defined(__GNUC__) && (__GNUC__ < 3 || (__GNUC__ == 3 && __GNUC_MINOR__ < 3)) + #error "Need GCC >= 3.3 for MMX intrinsics" + #endif + #include + int main () { + __m64 loop = _mm_cvtsi32_si64 (1); + return _mm_cvtsi64_si32 (loop); + }]])],[have_mmx_intrinsics=yes]) + CXXFLAGS=$original_saved_CXXFLAGS + + # Inform the user if we did or did not find MMX support. + # + # If we enable optimization and integer samples we only require MMX. + # Disable optimizations in the SSTypes.h file if this is not the case. + if test "x$have_mmx_intrinsics" = "xyes" ; then + echo "****** MMX support found ******" + else + echo "****** No MMX support found ******" + if test "x$enable_integer_samples" = "xyes"; then + echo "****** Disabling optimizations. Using integer samples with no MMX support ******" + CPPFLAGS="-DSOUNDTOUCH_DISABLE_X86_OPTIMIZATIONS $CPPFLAGS" + fi + fi + + + # SSE support + original_saved_CXXFLAGS=$CXXFLAGS + have_sse_intrinsics=no + CXXFLAGS="-msse -Winline $CXXFLAGS" + + # Check if the user can compile SSE code using intrinsics. + # GCC supports SSE intrinsics since version 3.3 + # A more recent GCC (>= 4.3) is recommended. + AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ + #if defined(__GNUC__) && (__GNUC__ < 3 || (__GNUC__ == 3 && __GNUC_MINOR__ < 3)) + #error "Need GCC >= 3.3 for SSE intrinsics" + #endif + #include + int main () { + _mm_setzero_ps(); + return 0; + }]])],[have_sse_intrinsics=yes]) + CXXFLAGS=$original_saved_CXXFLAGS + + # Inform the user if we did or did not find SSE support. + # + # If we enable optimization and float samples we only require SSE. + # Disable optimizations in the SSTypes.h file if this is not the case. + if test "x$have_sse_intrinsics" = "xyes" ; then + echo "****** SSE support found ******" + else + echo "****** No SSE support found ******" + if test "x$enable_integer_samples" != "xyes"; then + echo "****** Disabling optimizations. Using float samples with no SSE support ******" + CPPFLAGS="-DSOUNDTOUCH_DISABLE_X86_OPTIMIZATIONS $CPPFLAGS" + fi + fi + +else + # Disable optimizations in SSTypes.h since the user requested it. + echo "****** x86 optimizations disabled ******" + CPPFLAGS="-DSOUNDTOUCH_DISABLE_X86_OPTIMIZATIONS $CPPFLAGS" +fi + +# Set AM_CXXFLAGS +AC_SUBST([AM_CXXFLAGS], [$AM_CXXFLAGS]) + +# Empty default CXXFLAGS so user can set them if desirable +#AC_SUBST([CXXFLAGS], [ ]) + + +# SSTypes.h by default enables optimizations. Those already got disabled if +# the user requested for it or if the system does not support them. +# +# Now tell the Makefile.am the optimizations that are supported. +# Note: +# Makefile.am already knows if the user asked for optimizations. We apply +# optimizations by default (if support is available) and then disable all of +# them if the user requested it. +AM_CONDITIONAL([HAVE_MMX], [test "x$have_mmx_intrinsics" = "xyes"]) +AM_CONDITIONAL([HAVE_SSE], [test "x$have_sse_intrinsics" = "xyes"]) + + +dnl ############################################################################ +dnl # Checks for library functions/classes # +dnl ############################################################################ +AC_FUNC_MALLOC +AC_TYPE_SIGNAL + +dnl make -lm get added to the LIBS +AC_CHECK_LIB(m, sqrt,,AC_MSG_ERROR([compatible libc math library not found])) + +dnl add whatever functions you might want to check for here +#AC_CHECK_FUNCS([floor ftruncate memmove memset mkdir modf pow realpath sqrt strchr strdup strerror strrchr strstr strtol]) + + + + + + + +dnl ############################################################################ +dnl # Internationaliation and Localiation # +dnl ############################################################################ +#AM_GNU_GETTEXT_VERSION([0.11.5]) +#AM_GNU_GETTEXT([external]) + + + + + +dnl ############################################################################ +dnl # Final # +dnl ############################################################################ + +AC_CONFIG_FILES([ + Makefile + source/Makefile + source/SoundTouch/Makefile + source/SoundStretch/Makefile + include/Makefile +]) + +AC_OUTPUT( + soundtouch.pc +) + +dnl use 'echo' to put stuff here if you want a message to the builder diff --git a/createsrcpack b/createsrcpack new file mode 100755 index 0000000..2f4b69a --- /dev/null +++ b/createsrcpack @@ -0,0 +1,16 @@ +# Helper script for building a source code release package + +rm -Rf soundtouch +rm soundtouch.zip +rm soundtouch.tar.gz +mkdir soundtouch +cp -R * soundtouch +cd soundtouch +rm -Rf soundtouch +rm -rf `find . -type d -name .svn` +rm createsrcpack +chmod u+x bootstrap +cd .. +zip -r9 soundtouch.zip soundtouch +tar -chf soundtouch.tar soundtouch +gzip soundtouch.tar diff --git a/include/BPMDetect.h b/include/BPMDetect.h new file mode 100644 index 0000000..8ece784 --- /dev/null +++ b/include/BPMDetect.h @@ -0,0 +1,205 @@ +//////////////////////////////////////////////////////////////////////////////// +/// +/// Beats-per-minute (BPM) detection routine. +/// +/// The beat detection algorithm works as follows: +/// - Use function 'inputSamples' to input a chunks of samples to the class for +/// analysis. It's a good idea to enter a large sound file or stream in smallish +/// chunks of around few kilosamples in order not to extinguish too much RAM memory. +/// - Input sound data is decimated to approx 500 Hz to reduce calculation burden, +/// which is basically ok as low (bass) frequencies mostly determine the beat rate. +/// Simple averaging is used for anti-alias filtering because the resulting signal +/// quality isn't of that high importance. +/// - Decimated sound data is enveloped, i.e. the amplitude shape is detected by +/// taking absolute value that's smoothed by sliding average. Signal levels that +/// are below a couple of times the general RMS amplitude level are cut away to +/// leave only notable peaks there. +/// - Repeating sound patterns (e.g. beats) are detected by calculating short-term +/// autocorrelation function of the enveloped signal. +/// - After whole sound data file has been analyzed as above, the bpm level is +/// detected by function 'getBpm' that finds the highest peak of the autocorrelation +/// function, calculates it's precise location and converts this reading to bpm's. +/// +/// Author : Copyright (c) Olli Parviainen +/// Author e-mail : oparviai 'at' iki.fi +/// SoundTouch WWW: http://www.surina.net/soundtouch +/// +//////////////////////////////////////////////////////////////////////////////// +// +// License : +// +// SoundTouch audio processing library +// Copyright (c) Olli Parviainen +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef _BPMDetect_H_ +#define _BPMDetect_H_ + +#include +#include "STTypes.h" +#include "FIFOSampleBuffer.h" + +namespace soundtouch +{ + + /// Minimum allowed BPM rate. Used to restrict accepted result above a reasonable limit. + #define MIN_BPM 45 + + /// Maximum allowed BPM rate range. Used for calculating algorithm parametrs + #define MAX_BPM_RANGE 200 + + /// Maximum allowed BPM rate range. Used to restrict accepted result below a reasonable limit. + #define MAX_BPM_VALID 190 + +//////////////////////////////////////////////////////////////////////////////// + + typedef struct + { + float pos; + float strength; + } BEAT; + + + class IIR2_filter + { + double coeffs[5]; + double prev[5]; + + public: + IIR2_filter(const double *lpf_coeffs); + float update(float x); + }; + + + /// Class for calculating BPM rate for audio data. + class BPMDetect + { + protected: + /// Auto-correlation accumulator bins. + float *xcorr; + + /// Sample average counter. + int decimateCount; + + /// Sample average accumulator for FIFO-like decimation. + soundtouch::LONG_SAMPLETYPE decimateSum; + + /// Decimate sound by this coefficient to reach approx. 500 Hz. + int decimateBy; + + /// Auto-correlation window length + int windowLen; + + /// Number of channels (1 = mono, 2 = stereo) + int channels; + + /// sample rate + int sampleRate; + + /// Beginning of auto-correlation window: Autocorrelation isn't being updated for + /// the first these many correlation bins. + int windowStart; + + /// window functions for data preconditioning + float *hamw; + float *hamw2; + + // beat detection variables + int pos; + int peakPos; + int beatcorr_ringbuffpos; + int init_scaler; + float peakVal; + float *beatcorr_ringbuff; + + /// FIFO-buffer for decimated processing samples. + soundtouch::FIFOSampleBuffer *buffer; + + /// Collection of detected beat positions + //BeatCollection beats; + std::vector beats; + + // 2nd order low-pass-filter + IIR2_filter beat_lpf; + + /// Updates auto-correlation function for given number of decimated samples that + /// are read from the internal 'buffer' pipe (samples aren't removed from the pipe + /// though). + void updateXCorr(int process_samples /// How many samples are processed. + ); + + /// Decimates samples to approx. 500 Hz. + /// + /// \return Number of output samples. + int decimate(soundtouch::SAMPLETYPE *dest, ///< Destination buffer + const soundtouch::SAMPLETYPE *src, ///< Source sample buffer + int numsamples ///< Number of source samples. + ); + + /// Calculates amplitude envelope for the buffer of samples. + /// Result is output to 'samples'. + void calcEnvelope(soundtouch::SAMPLETYPE *samples, ///< Pointer to input/output data buffer + int numsamples ///< Number of samples in buffer + ); + + /// remove constant bias from xcorr data + void removeBias(); + + // Detect individual beat positions + void updateBeatPos(int process_samples); + + + public: + /// Constructor. + BPMDetect(int numChannels, ///< Number of channels in sample data. + int sampleRate ///< Sample rate in Hz. + ); + + /// Destructor. + virtual ~BPMDetect(); + + /// Inputs a block of samples for analyzing: Envelopes the samples and then + /// updates the autocorrelation estimation. When whole song data has been input + /// in smaller blocks using this function, read the resulting bpm with 'getBpm' + /// function. + /// + /// Notice that data in 'samples' array can be disrupted in processing. + void inputSamples(const soundtouch::SAMPLETYPE *samples, ///< Pointer to input/working data buffer + int numSamples ///< Number of samples in buffer + ); + + /// Analyzes the results and returns the BPM rate. Use this function to read result + /// after whole song data has been input to the class by consecutive calls of + /// 'inputSamples' function. + /// + /// \return Beats-per-minute rate, or zero if detection failed. + float getBpm(); + + /// Get beat position arrays. Note: The array includes also really low beat detection values + /// in absence of clear strong beats. Consumer may wish to filter low values away. + /// - "pos" receive array of beat positions + /// - "values" receive array of beat detection strengths + /// - max_num indicates max.size of "pos" and "values" array. + /// + /// You can query a suitable array sized by calling this with NULL in "pos" & "values". + /// + /// \return number of beats in the arrays. + int getBeats(float *pos, float *strength, int max_num); + }; +} +#endif // _BPMDetect_H_ diff --git a/include/FIFOSampleBuffer.h b/include/FIFOSampleBuffer.h new file mode 100644 index 0000000..de298dd --- /dev/null +++ b/include/FIFOSampleBuffer.h @@ -0,0 +1,177 @@ +//////////////////////////////////////////////////////////////////////////////// +/// +/// A buffer class for temporarily storaging sound samples, operates as a +/// first-in-first-out pipe. +/// +/// Samples are added to the end of the sample buffer with the 'putSamples' +/// function, and are received from the beginning of the buffer by calling +/// the 'receiveSamples' function. The class automatically removes the +/// output samples from the buffer as well as grows the storage size +/// whenever necessary. +/// +/// Author : Copyright (c) Olli Parviainen +/// Author e-mail : oparviai 'at' iki.fi +/// SoundTouch WWW: http://www.surina.net/soundtouch +/// +//////////////////////////////////////////////////////////////////////////////// +// +// License : +// +// SoundTouch audio processing library +// Copyright (c) Olli Parviainen +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef FIFOSampleBuffer_H +#define FIFOSampleBuffer_H + +#include "FIFOSamplePipe.h" + +namespace soundtouch +{ + +/// Sample buffer working in FIFO (first-in-first-out) principle. The class takes +/// care of storage size adjustment and data moving during input/output operations. +/// +/// Notice that in case of stereo audio, one sample is considered to consist of +/// both channel data. +class FIFOSampleBuffer : public FIFOSamplePipe +{ +private: + /// Sample buffer. + SAMPLETYPE *buffer; + + // Raw unaligned buffer memory. 'buffer' is made aligned by pointing it to first + // 16-byte aligned location of this buffer + SAMPLETYPE *bufferUnaligned; + + /// Sample buffer size in bytes + uint sizeInBytes; + + /// How many samples are currently in buffer. + uint samplesInBuffer; + + /// Channels, 1=mono, 2=stereo. + uint channels; + + /// Current position pointer to the buffer. This pointer is increased when samples are + /// removed from the pipe so that it's necessary to actually rewind buffer (move data) + /// only new data when is put to the pipe. + uint bufferPos; + + /// Rewind the buffer by moving data from position pointed by 'bufferPos' to real + /// beginning of the buffer. + void rewind(); + + /// Ensures that the buffer has capacity for at least this many samples. + void ensureCapacity(uint capacityRequirement); + + /// Returns current capacity. + uint getCapacity() const; + +public: + + /// Constructor + FIFOSampleBuffer(int numChannels = 2 ///< Number of channels, 1=mono, 2=stereo. + ///< Default is stereo. + ); + + /// destructor + ~FIFOSampleBuffer(); + + /// Returns a pointer to the beginning of the output samples. + /// This function is provided for accessing the output samples directly. + /// Please be careful for not to corrupt the book-keeping! + /// + /// When using this function to output samples, also remember to 'remove' the + /// output samples from the buffer by calling the + /// 'receiveSamples(numSamples)' function + virtual SAMPLETYPE *ptrBegin(); + + /// Returns a pointer to the end of the used part of the sample buffer (i.e. + /// where the new samples are to be inserted). This function may be used for + /// inserting new samples into the sample buffer directly. Please be careful + /// not corrupt the book-keeping! + /// + /// When using this function as means for inserting new samples, also remember + /// to increase the sample count afterwards, by calling the + /// 'putSamples(numSamples)' function. + SAMPLETYPE *ptrEnd( + uint slackCapacity ///< How much free capacity (in samples) there _at least_ + ///< should be so that the caller can successfully insert the + ///< desired samples to the buffer. If necessary, the function + ///< grows the buffer size to comply with this requirement. + ); + + /// Adds 'numSamples' pcs of samples from the 'samples' memory position to + /// the sample buffer. + virtual void putSamples(const SAMPLETYPE *samples, ///< Pointer to samples. + uint numSamples ///< Number of samples to insert. + ); + + /// Adjusts the book-keeping to increase number of samples in the buffer without + /// copying any actual samples. + /// + /// This function is used to update the number of samples in the sample buffer + /// when accessing the buffer directly with 'ptrEnd' function. Please be + /// careful though! + virtual void putSamples(uint numSamples ///< Number of samples been inserted. + ); + + /// Output samples from beginning of the sample buffer. Copies requested samples to + /// output buffer and removes them from the sample buffer. If there are less than + /// 'numsample' samples in the buffer, returns all that available. + /// + /// \return Number of samples returned. + virtual uint receiveSamples(SAMPLETYPE *output, ///< Buffer where to copy output samples. + uint maxSamples ///< How many samples to receive at max. + ); + + /// Adjusts book-keeping so that given number of samples are removed from beginning of the + /// sample buffer without copying them anywhere. + /// + /// Used to reduce the number of samples in the buffer when accessing the sample buffer directly + /// with 'ptrBegin' function. + virtual uint receiveSamples(uint maxSamples ///< Remove this many samples from the beginning of pipe. + ); + + /// Returns number of samples currently available. + virtual uint numSamples() const; + + /// Sets number of channels, 1 = mono, 2 = stereo. + void setChannels(int numChannels); + + /// Get number of channels + int getChannels() + { + return channels; + } + + /// Returns nonzero if there aren't any samples available for outputting. + virtual int isEmpty() const; + + /// Clears all the samples. + virtual void clear(); + + /// allow trimming (downwards) amount of samples in pipeline. + /// Returns adjusted amount of samples + uint adjustAmountOfSamples(uint numSamples); +}; + +} + +#endif diff --git a/include/FIFOSamplePipe.h b/include/FIFOSamplePipe.h new file mode 100644 index 0000000..38ef31a --- /dev/null +++ b/include/FIFOSamplePipe.h @@ -0,0 +1,230 @@ +//////////////////////////////////////////////////////////////////////////////// +/// +/// 'FIFOSamplePipe' : An abstract base class for classes that manipulate sound +/// samples by operating like a first-in-first-out pipe: New samples are fed +/// into one end of the pipe with the 'putSamples' function, and the processed +/// samples are received from the other end with the 'receiveSamples' function. +/// +/// 'FIFOProcessor' : A base class for classes the do signal processing with +/// the samples while operating like a first-in-first-out pipe. When samples +/// are input with the 'putSamples' function, the class processes them +/// and moves the processed samples to the given 'output' pipe object, which +/// may be either another processing stage, or a fifo sample buffer object. +/// +/// Author : Copyright (c) Olli Parviainen +/// Author e-mail : oparviai 'at' iki.fi +/// SoundTouch WWW: http://www.surina.net/soundtouch +/// +//////////////////////////////////////////////////////////////////////////////// +// +// License : +// +// SoundTouch audio processing library +// Copyright (c) Olli Parviainen +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef FIFOSamplePipe_H +#define FIFOSamplePipe_H + +#include +#include +#include "STTypes.h" + +namespace soundtouch +{ + +/// Abstract base class for FIFO (first-in-first-out) sample processing classes. +class FIFOSamplePipe +{ +protected: + + bool verifyNumberOfChannels(int nChannels) const + { + if ((nChannels > 0) && (nChannels <= SOUNDTOUCH_MAX_CHANNELS)) + { + return true; + } + ST_THROW_RT_ERROR("Error: Illegal number of channels"); + return false; + } + +public: + // virtual default destructor + virtual ~FIFOSamplePipe() {} + + + /// Returns a pointer to the beginning of the output samples. + /// This function is provided for accessing the output samples directly. + /// Please be careful for not to corrupt the book-keeping! + /// + /// When using this function to output samples, also remember to 'remove' the + /// output samples from the buffer by calling the + /// 'receiveSamples(numSamples)' function + virtual SAMPLETYPE *ptrBegin() = 0; + + /// Adds 'numSamples' pcs of samples from the 'samples' memory position to + /// the sample buffer. + virtual void putSamples(const SAMPLETYPE *samples, ///< Pointer to samples. + uint numSamples ///< Number of samples to insert. + ) = 0; + + + // Moves samples from the 'other' pipe instance to this instance. + void moveSamples(FIFOSamplePipe &other ///< Other pipe instance where from the receive the data. + ) + { + int oNumSamples = other.numSamples(); + + putSamples(other.ptrBegin(), oNumSamples); + other.receiveSamples(oNumSamples); + }; + + /// Output samples from beginning of the sample buffer. Copies requested samples to + /// output buffer and removes them from the sample buffer. If there are less than + /// 'numsample' samples in the buffer, returns all that available. + /// + /// \return Number of samples returned. + virtual uint receiveSamples(SAMPLETYPE *output, ///< Buffer where to copy output samples. + uint maxSamples ///< How many samples to receive at max. + ) = 0; + + /// Adjusts book-keeping so that given number of samples are removed from beginning of the + /// sample buffer without copying them anywhere. + /// + /// Used to reduce the number of samples in the buffer when accessing the sample buffer directly + /// with 'ptrBegin' function. + virtual uint receiveSamples(uint maxSamples ///< Remove this many samples from the beginning of pipe. + ) = 0; + + /// Returns number of samples currently available. + virtual uint numSamples() const = 0; + + // Returns nonzero if there aren't any samples available for outputting. + virtual int isEmpty() const = 0; + + /// Clears all the samples. + virtual void clear() = 0; + + /// allow trimming (downwards) amount of samples in pipeline. + /// Returns adjusted amount of samples + virtual uint adjustAmountOfSamples(uint numSamples) = 0; + +}; + + +/// Base-class for sound processing routines working in FIFO principle. With this base +/// class it's easy to implement sound processing stages that can be chained together, +/// so that samples that are fed into beginning of the pipe automatically go through +/// all the processing stages. +/// +/// When samples are input to this class, they're first processed and then put to +/// the FIFO pipe that's defined as output of this class. This output pipe can be +/// either other processing stage or a FIFO sample buffer. +class FIFOProcessor :public FIFOSamplePipe +{ +protected: + /// Internal pipe where processed samples are put. + FIFOSamplePipe *output; + + /// Sets output pipe. + void setOutPipe(FIFOSamplePipe *pOutput) + { + assert(output == NULL); + assert(pOutput != NULL); + output = pOutput; + } + + /// Constructor. Doesn't define output pipe; it has to be set be + /// 'setOutPipe' function. + FIFOProcessor() + { + output = NULL; + } + + /// Constructor. Configures output pipe. + FIFOProcessor(FIFOSamplePipe *pOutput ///< Output pipe. + ) + { + output = pOutput; + } + + /// Destructor. + virtual ~FIFOProcessor() + { + } + + /// Returns a pointer to the beginning of the output samples. + /// This function is provided for accessing the output samples directly. + /// Please be careful for not to corrupt the book-keeping! + /// + /// When using this function to output samples, also remember to 'remove' the + /// output samples from the buffer by calling the + /// 'receiveSamples(numSamples)' function + virtual SAMPLETYPE *ptrBegin() + { + return output->ptrBegin(); + } + +public: + + /// Output samples from beginning of the sample buffer. Copies requested samples to + /// output buffer and removes them from the sample buffer. If there are less than + /// 'numsample' samples in the buffer, returns all that available. + /// + /// \return Number of samples returned. + virtual uint receiveSamples(SAMPLETYPE *outBuffer, ///< Buffer where to copy output samples. + uint maxSamples ///< How many samples to receive at max. + ) + { + return output->receiveSamples(outBuffer, maxSamples); + } + + /// Adjusts book-keeping so that given number of samples are removed from beginning of the + /// sample buffer without copying them anywhere. + /// + /// Used to reduce the number of samples in the buffer when accessing the sample buffer directly + /// with 'ptrBegin' function. + virtual uint receiveSamples(uint maxSamples ///< Remove this many samples from the beginning of pipe. + ) + { + return output->receiveSamples(maxSamples); + } + + /// Returns number of samples currently available. + virtual uint numSamples() const + { + return output->numSamples(); + } + + /// Returns nonzero if there aren't any samples available for outputting. + virtual int isEmpty() const + { + return output->isEmpty(); + } + + /// allow trimming (downwards) amount of samples in pipeline. + /// Returns adjusted amount of samples + virtual uint adjustAmountOfSamples(uint numSamples) + { + return output->adjustAmountOfSamples(numSamples); + } +}; + +} + +#endif diff --git a/include/Makefile.am b/include/Makefile.am new file mode 100644 index 0000000..d1b8238 --- /dev/null +++ b/include/Makefile.am @@ -0,0 +1,22 @@ +## Process this file with automake to create Makefile.in +## +## This file is part of SoundTouch, an audio processing library for pitch/time adjustments +## +## SoundTouch is free software; you can redistribute it and/or modify it under the +## terms of the GNU General Public License as published by the Free Software +## Foundation; either version 2 of the License, or (at your option) any later +## version. +## +## SoundTouch is distributed in the hope that it will be useful, but WITHOUT ANY +## WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +## A PARTICULAR PURPOSE. See the GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License along with +## this program; if not, write to the Free Software Foundation, Inc., 59 Temple +## Place - Suite 330, Boston, MA 02111-1307, USA + +## I used config/am_include.mk for common definitions +include $(top_srcdir)/config/am_include.mk + +pkginclude_HEADERS=FIFOSampleBuffer.h FIFOSamplePipe.h SoundTouch.h STTypes.h BPMDetect.h soundtouch_config.h + diff --git a/include/STTypes.h b/include/STTypes.h new file mode 100644 index 0000000..862505e --- /dev/null +++ b/include/STTypes.h @@ -0,0 +1,183 @@ +//////////////////////////////////////////////////////////////////////////////// +/// +/// Common type definitions for SoundTouch audio processing library. +/// +/// Author : Copyright (c) Olli Parviainen +/// Author e-mail : oparviai 'at' iki.fi +/// SoundTouch WWW: http://www.surina.net/soundtouch +/// +//////////////////////////////////////////////////////////////////////////////// +// +// License : +// +// SoundTouch audio processing library +// Copyright (c) Olli Parviainen +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef STTypes_H +#define STTypes_H + +typedef unsigned int uint; +typedef unsigned long ulong; + +// Patch for MinGW: on Win64 long is 32-bit +#ifdef _WIN64 + typedef unsigned long long ulongptr; +#else + typedef ulong ulongptr; +#endif + + +// Helper macro for aligning pointer up to next 16-byte boundary +#define SOUNDTOUCH_ALIGN_POINTER_16(x) ( ( (ulongptr)(x) + 15 ) & ~(ulongptr)15 ) + + +#if (defined(__GNUC__) && !defined(ANDROID)) + // In GCC, include soundtouch_config.h made by config scritps. + // Skip this in Android compilation that uses GCC but without configure scripts. + #include "soundtouch_config.h" +#endif + + +namespace soundtouch +{ + /// Max allowed number of channels + #define SOUNDTOUCH_MAX_CHANNELS 16 + + /// Activate these undef's to overrule the possible sampletype + /// setting inherited from some other header file: + //#undef SOUNDTOUCH_INTEGER_SAMPLES + //#undef SOUNDTOUCH_FLOAT_SAMPLES + + /// If following flag is defined, always uses multichannel processing + /// routines also for mono and stero sound. This is for routine testing + /// purposes; output should be same with either routines, yet disabling + /// the dedicated mono/stereo processing routines will result in slower + /// runtime performance so recommendation is to keep this off. + // #define USE_MULTICH_ALWAYS + + #if (defined(__SOFTFP__) && defined(ANDROID)) + // For Android compilation: Force use of Integer samples in case that + // compilation uses soft-floating point emulation - soft-fp is way too slow + #undef SOUNDTOUCH_FLOAT_SAMPLES + #define SOUNDTOUCH_INTEGER_SAMPLES 1 + #endif + + #if !(SOUNDTOUCH_INTEGER_SAMPLES || SOUNDTOUCH_FLOAT_SAMPLES) + + /// Choose either 32bit floating point or 16bit integer sampletype + /// by choosing one of the following defines, unless this selection + /// has already been done in some other file. + //// + /// Notes: + /// - In Windows environment, choose the sample format with the + /// following defines. + /// - In GNU environment, the floating point samples are used by + /// default, but integer samples can be chosen by giving the + /// following switch to the configure script: + /// ./configure --enable-integer-samples + /// However, if you still prefer to select the sample format here + /// also in GNU environment, then please #undef the INTEGER_SAMPLE + /// and FLOAT_SAMPLE defines first as in comments above. + //#define SOUNDTOUCH_INTEGER_SAMPLES 1 //< 16bit integer samples + #define SOUNDTOUCH_FLOAT_SAMPLES 1 //< 32bit float samples + + #endif + + #if (_M_IX86 || __i386__ || __x86_64__ || _M_X64) + /// Define this to allow X86-specific assembler/intrinsic optimizations. + /// Notice that library contains also usual C++ versions of each of these + /// these routines, so if you're having difficulties getting the optimized + /// routines compiled for whatever reason, you may disable these optimizations + /// to make the library compile. + + #define SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS 1 + + /// In GNU environment, allow the user to override this setting by + /// giving the following switch to the configure script: + /// ./configure --disable-x86-optimizations + /// ./configure --enable-x86-optimizations=no + #ifdef SOUNDTOUCH_DISABLE_X86_OPTIMIZATIONS + #undef SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS + #endif + #else + /// Always disable optimizations when not using a x86 systems. + #undef SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS + + #endif + + // If defined, allows the SIMD-optimized routines to take minor shortcuts + // for improved performance. Undefine to require faithfully similar SIMD + // calculations as in normal C implementation. + #define SOUNDTOUCH_ALLOW_NONEXACT_SIMD_OPTIMIZATION 1 + + + #ifdef SOUNDTOUCH_INTEGER_SAMPLES + // 16bit integer sample type + typedef short SAMPLETYPE; + // data type for sample accumulation: Use 32bit integer to prevent overflows + typedef long LONG_SAMPLETYPE; + + #ifdef SOUNDTOUCH_FLOAT_SAMPLES + // check that only one sample type is defined + #error "conflicting sample types defined" + #endif // SOUNDTOUCH_FLOAT_SAMPLES + + #ifdef SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS + // Allow MMX optimizations (not available in X64 mode) + #if (!_M_X64) + #define SOUNDTOUCH_ALLOW_MMX 1 + #endif + #endif + + #else + + // floating point samples + typedef float SAMPLETYPE; + // data type for sample accumulation: Use double to utilize full precision. + typedef double LONG_SAMPLETYPE; + + #ifdef SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS + // Allow SSE optimizations + #define SOUNDTOUCH_ALLOW_SSE 1 + #endif + + #endif // SOUNDTOUCH_INTEGER_SAMPLES + +}; + +// define ST_NO_EXCEPTION_HANDLING switch to disable throwing std exceptions: +// #define ST_NO_EXCEPTION_HANDLING 1 +#ifdef ST_NO_EXCEPTION_HANDLING + // Exceptions disabled. Throw asserts instead if enabled. + #include + #define ST_THROW_RT_ERROR(x) {assert((const char *)x);} +#else + // use c++ standard exceptions + #include + #include + #define ST_THROW_RT_ERROR(x) {throw std::runtime_error(x);} +#endif + +// When this #define is active, eliminates a clicking sound when the "rate" or "pitch" +// parameter setting crosses from value <1 to >=1 or vice versa during processing. +// Default is off as such crossover is untypical case and involves a slight sound +// quality compromise. +//#define SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER 1 + +#endif diff --git a/include/SoundTouch.h b/include/SoundTouch.h new file mode 100644 index 0000000..f2addc1 --- /dev/null +++ b/include/SoundTouch.h @@ -0,0 +1,348 @@ +////////////////////////////////////////////////////////////////////////////// +/// +/// SoundTouch - main class for tempo/pitch/rate adjusting routines. +/// +/// Notes: +/// - Initialize the SoundTouch object instance by setting up the sound stream +/// parameters with functions 'setSampleRate' and 'setChannels', then set +/// desired tempo/pitch/rate settings with the corresponding functions. +/// +/// - The SoundTouch class behaves like a first-in-first-out pipeline: The +/// samples that are to be processed are fed into one of the pipe by calling +/// function 'putSamples', while the ready processed samples can be read +/// from the other end of the pipeline with function 'receiveSamples'. +/// +/// - The SoundTouch processing classes require certain sized 'batches' of +/// samples in order to process the sound. For this reason the classes buffer +/// incoming samples until there are enough of samples available for +/// processing, then they carry out the processing step and consequently +/// make the processed samples available for outputting. +/// +/// - For the above reason, the processing routines introduce a certain +/// 'latency' between the input and output, so that the samples input to +/// SoundTouch may not be immediately available in the output, and neither +/// the amount of outputtable samples may not immediately be in direct +/// relationship with the amount of previously input samples. +/// +/// - The tempo/pitch/rate control parameters can be altered during processing. +/// Please notice though that they aren't currently protected by semaphores, +/// so in multi-thread application external semaphore protection may be +/// required. +/// +/// - This class utilizes classes 'TDStretch' for tempo change (without modifying +/// pitch) and 'RateTransposer' for changing the playback rate (that is, both +/// tempo and pitch in the same ratio) of the sound. The third available control +/// 'pitch' (change pitch but maintain tempo) is produced by a combination of +/// combining the two other controls. +/// +/// Author : Copyright (c) Olli Parviainen +/// Author e-mail : oparviai 'at' iki.fi +/// SoundTouch WWW: http://www.surina.net/soundtouch +/// +//////////////////////////////////////////////////////////////////////////////// +// +// License : +// +// SoundTouch audio processing library +// Copyright (c) Olli Parviainen +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef SoundTouch_H +#define SoundTouch_H + +#include "FIFOSamplePipe.h" +#include "STTypes.h" + +namespace soundtouch +{ + +/// Soundtouch library version string +#define SOUNDTOUCH_VERSION "2.1" + +/// SoundTouch library version id +#define SOUNDTOUCH_VERSION_ID (20100) + +// +// Available setting IDs for the 'setSetting' & 'get_setting' functions: + +/// Enable/disable anti-alias filter in pitch transposer (0 = disable) +#define SETTING_USE_AA_FILTER 0 + +/// Pitch transposer anti-alias filter length (8 .. 128 taps, default = 32) +#define SETTING_AA_FILTER_LENGTH 1 + +/// Enable/disable quick seeking algorithm in tempo changer routine +/// (enabling quick seeking lowers CPU utilization but causes a minor sound +/// quality compromising) +#define SETTING_USE_QUICKSEEK 2 + +/// Time-stretch algorithm single processing sequence length in milliseconds. This determines +/// to how long sequences the original sound is chopped in the time-stretch algorithm. +/// See "STTypes.h" or README for more information. +#define SETTING_SEQUENCE_MS 3 + +/// Time-stretch algorithm seeking window length in milliseconds for algorithm that finds the +/// best possible overlapping location. This determines from how wide window the algorithm +/// may look for an optimal joining location when mixing the sound sequences back together. +/// See "STTypes.h" or README for more information. +#define SETTING_SEEKWINDOW_MS 4 + +/// Time-stretch algorithm overlap length in milliseconds. When the chopped sound sequences +/// are mixed back together, to form a continuous sound stream, this parameter defines over +/// how long period the two consecutive sequences are let to overlap each other. +/// See "STTypes.h" or README for more information. +#define SETTING_OVERLAP_MS 5 + + +/// Call "getSetting" with this ID to query processing sequence size in samples. +/// This value gives approximate value of how many input samples you'll need to +/// feed into SoundTouch after initial buffering to get out a new batch of +/// output samples. +/// +/// This value does not include initial buffering at beginning of a new processing +/// stream, use SETTING_INITIAL_LATENCY to get the initial buffering size. +/// +/// Notices: +/// - This is read-only parameter, i.e. setSetting ignores this parameter +/// - This parameter value is not constant but change depending on +/// tempo/pitch/rate/samplerate settings. +#define SETTING_NOMINAL_INPUT_SEQUENCE 6 + + +/// Call "getSetting" with this ID to query nominal average processing output +/// size in samples. This value tells approcimate value how many output samples +/// SoundTouch outputs once it does DSP processing run for a batch of input samples. +/// +/// Notices: +/// - This is read-only parameter, i.e. setSetting ignores this parameter +/// - This parameter value is not constant but change depending on +/// tempo/pitch/rate/samplerate settings. +#define SETTING_NOMINAL_OUTPUT_SEQUENCE 7 + + +/// Call "getSetting" with this ID to query initial processing latency, i.e. +/// approx. how many samples you'll need to enter to SoundTouch pipeline before +/// you can expect to get first batch of ready output samples out. +/// +/// After the first output batch, you can then expect to get approx. +/// SETTING_NOMINAL_OUTPUT_SEQUENCE ready samples out for every +/// SETTING_NOMINAL_INPUT_SEQUENCE samples that you enter into SoundTouch. +/// +/// Example: +/// processing with parameter -tempo=5 +/// => initial latency = 5509 samples +/// input sequence = 4167 samples +/// output sequence = 3969 samples +/// +/// Accordingly, you can expect to feed in approx. 5509 samples at beginning of +/// the stream, and then you'll get out the first 3969 samples. After that, for +/// every approx. 4167 samples that you'll put in, you'll receive again approx. +/// 3969 samples out. +/// +/// This also means that average latency during stream processing is +/// INITIAL_LATENCY-OUTPUT_SEQUENCE/2, in the above example case 5509-3969/2 +/// = 3524 samples +/// +/// Notices: +/// - This is read-only parameter, i.e. setSetting ignores this parameter +/// - This parameter value is not constant but change depending on +/// tempo/pitch/rate/samplerate settings. +#define SETTING_INITIAL_LATENCY 8 + + +class SoundTouch : public FIFOProcessor +{ +private: + /// Rate transposer class instance + class RateTransposer *pRateTransposer; + + /// Time-stretch class instance + class TDStretch *pTDStretch; + + /// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters. + double virtualRate; + + /// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters. + double virtualTempo; + + /// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters. + double virtualPitch; + + /// Flag: Has sample rate been set? + bool bSrateSet; + + /// Accumulator for how many samples in total will be expected as output vs. samples put in, + /// considering current processing settings. + double samplesExpectedOut; + + /// Accumulator for how many samples in total have been read out from the processing so far + long samplesOutput; + + /// Calculates effective rate & tempo valuescfrom 'virtualRate', 'virtualTempo' and + /// 'virtualPitch' parameters. + void calcEffectiveRateAndTempo(); + +protected : + /// Number of channels + uint channels; + + /// Effective 'rate' value calculated from 'virtualRate', 'virtualTempo' and 'virtualPitch' + double rate; + + /// Effective 'tempo' value calculated from 'virtualRate', 'virtualTempo' and 'virtualPitch' + double tempo; + +public: + SoundTouch(); + virtual ~SoundTouch(); + + /// Get SoundTouch library version string + static const char *getVersionString(); + + /// Get SoundTouch library version Id + static uint getVersionId(); + + /// Sets new rate control value. Normal rate = 1.0, smaller values + /// represent slower rate, larger faster rates. + void setRate(double newRate); + + /// Sets new tempo control value. Normal tempo = 1.0, smaller values + /// represent slower tempo, larger faster tempo. + void setTempo(double newTempo); + + /// Sets new rate control value as a difference in percents compared + /// to the original rate (-50 .. +100 %) + void setRateChange(double newRate); + + /// Sets new tempo control value as a difference in percents compared + /// to the original tempo (-50 .. +100 %) + void setTempoChange(double newTempo); + + /// Sets new pitch control value. Original pitch = 1.0, smaller values + /// represent lower pitches, larger values higher pitch. + void setPitch(double newPitch); + + /// Sets pitch change in octaves compared to the original pitch + /// (-1.00 .. +1.00) + void setPitchOctaves(double newPitch); + + /// Sets pitch change in semi-tones compared to the original pitch + /// (-12 .. +12) + void setPitchSemiTones(int newPitch); + void setPitchSemiTones(double newPitch); + + /// Sets the number of channels, 1 = mono, 2 = stereo + void setChannels(uint numChannels); + + /// Sets sample rate. + void setSampleRate(uint srate); + + /// Get ratio between input and output audio durations, useful for calculating + /// processed output duration: if you'll process a stream of N samples, then + /// you can expect to get out N * getInputOutputSampleRatio() samples. + /// + /// This ratio will give accurate target duration ratio for a full audio track, + /// given that the the whole track is processed with same processing parameters. + /// + /// If this ratio is applied to calculate intermediate offsets inside a processing + /// stream, then this ratio is approximate and can deviate +- some tens of milliseconds + /// from ideal offset, yet by end of the audio stream the duration ratio will become + /// exact. + /// + /// Example: if processing with parameters "-tempo=15 -pitch=-3", the function + /// will return value 0.8695652... Now, if processing an audio stream whose duration + /// is exactly one million audio samples, then you can expect the processed + /// output duration be 0.869565 * 1000000 = 869565 samples. + double getInputOutputSampleRatio(); + + /// Flushes the last samples from the processing pipeline to the output. + /// Clears also the internal processing buffers. + // + /// Note: This function is meant for extracting the last samples of a sound + /// stream. This function may introduce additional blank samples in the end + /// of the sound stream, and thus it's not recommended to call this function + /// in the middle of a sound stream. + void flush(); + + /// Adds 'numSamples' pcs of samples from the 'samples' memory position into + /// the input of the object. Notice that sample rate _has_to_ be set before + /// calling this function, otherwise throws a runtime_error exception. + virtual void putSamples( + const SAMPLETYPE *samples, ///< Pointer to sample buffer. + uint numSamples ///< Number of samples in buffer. Notice + ///< that in case of stereo-sound a single sample + ///< contains data for both channels. + ); + + /// Output samples from beginning of the sample buffer. Copies requested samples to + /// output buffer and removes them from the sample buffer. If there are less than + /// 'numsample' samples in the buffer, returns all that available. + /// + /// \return Number of samples returned. + virtual uint receiveSamples(SAMPLETYPE *output, ///< Buffer where to copy output samples. + uint maxSamples ///< How many samples to receive at max. + ); + + /// Adjusts book-keeping so that given number of samples are removed from beginning of the + /// sample buffer without copying them anywhere. + /// + /// Used to reduce the number of samples in the buffer when accessing the sample buffer directly + /// with 'ptrBegin' function. + virtual uint receiveSamples(uint maxSamples ///< Remove this many samples from the beginning of pipe. + ); + + /// Clears all the samples in the object's output and internal processing + /// buffers. + virtual void clear(); + + /// Changes a setting controlling the processing system behaviour. See the + /// 'SETTING_...' defines for available setting ID's. + /// + /// \return 'true' if the setting was successfully changed + bool setSetting(int settingId, ///< Setting ID number. see SETTING_... defines. + int value ///< New setting value. + ); + + /// Reads a setting controlling the processing system behaviour. See the + /// 'SETTING_...' defines for available setting ID's. + /// + /// \return the setting value. + int getSetting(int settingId ///< Setting ID number, see SETTING_... defines. + ) const; + + /// Returns number of samples currently unprocessed. + virtual uint numUnprocessedSamples() const; + + /// Return number of channels + uint numChannels() const + { + return channels; + } + + /// Other handy functions that are implemented in the ancestor classes (see + /// classes 'FIFOProcessor' and 'FIFOSamplePipe') + /// + /// - receiveSamples() : Use this function to receive 'ready' processed samples from SoundTouch. + /// - numSamples() : Get number of 'ready' samples that can be received with + /// function 'receiveSamples()' + /// - isEmpty() : Returns nonzero if there aren't any 'ready' samples. + /// - clear() : Clears all samples from ready/processing buffers. +}; + +} +#endif diff --git a/include/soundtouch_config.h.in b/include/soundtouch_config.h.in new file mode 100644 index 0000000..74a9ccb --- /dev/null +++ b/include/soundtouch_config.h.in @@ -0,0 +1,5 @@ +/* Use Float as Sample type */ +#undef SOUNDTOUCH_FLOAT_SAMPLES + +/* Use Integer as Sample type */ +#undef SOUNDTOUCH_INTEGER_SAMPLES diff --git a/make-win.bat b/make-win.bat new file mode 100644 index 0000000..fbc1812 --- /dev/null +++ b/make-win.bat @@ -0,0 +1,40 @@ +@REM +@REM SoundTouch & SoundStretch Build script for Win32 platform +@REM +@REM You'll need Visual C++ 6.0 installed to compile - also execute the +@REM "vcvars32.bat" in VC install directotry before running this one. +@REM +@REM Copyright (c) Olli Parviainen +@REM + +@if "%DevEnvDir%"=="" goto nodevdir + +@rem devenv source\SoundStretch\SoundStretch.sln /upgrade +devenv source\SoundStretch\SoundStretch.sln /build "Debug|Win32" +devenv source\SoundStretch\SoundStretch.sln /build "Release|Win32" +devenv source\SoundStretch\SoundStretch.sln /build "Debug|x64" +devenv source\SoundStretch\SoundStretch.sln /build "Release|x64" + +@rem devenv source\SoundTouchDll\SoundTouchDll.sln /upgrade +devenv source\SoundTouchDll\SoundTouchDll.sln /build "Debug|Win32" +devenv source\SoundTouchDll\SoundTouchDll.sln /build "Release|Win32" +devenv source\SoundTouchDll\SoundTouchDll.sln /build "Debug|x64" +devenv source\SoundTouchDll\SoundTouchDll.sln /build "Release|x64" + +@goto end + + +:nodevdir + +@echo off +echo **************************************************************************** +echo ** +echo ** ERROR: Visual Studio path not set. +echo ** +echo ** Run "vsvars32.bat" or "vcvars32.bat" from Visual Studio installation dir, +echo ** e.g. "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin", +echo ** then try again. +echo ** +echo **************************************************************************** + +:end diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..d6f8ef0 --- /dev/null +++ b/readme.md @@ -0,0 +1,50 @@ +# SoundTouch library + +SoundTouch is an open-source audio processing library that allows changing the sound tempo, pitch and playback rate parameters independently from each other: +* Change **tempo** while maintaining the original pitch +* Change **pitch** while maintaining the original tempo +* Change **playback rate** that affects both tempo and pitch at the +same time +* Change any combination of tempo/pitch/rate + +Visit [SoundTouch website](https://www.surina.net/soundtouch) and see the [README file](README.html) for more information and audio examples. + +## Example + +Use SoundStretch example app for modifying wav audio files, for example as follows: + +``` +soundstretch my_original_file.wav output_file.wav -tempo=+15 -pitch=-3 +``` + +See the [README file](README.html) for more usage examples and instructions how to build SoundTouch + SoundStretch. + +Ready [SoundStretch application executables](https://www.surina.net/soundtouch/download.html) are available for download for Windows and Mac OS. + +## Language & Platforms + +SoundTouch is written in C++ and compiles in virtually any platform: +* Windows +* Mac OS +* Linux & Unices (including also Raspberry, Beaglebone, Yocto etc embedded Linux flavors) +* Android +* iOS +* embedded systems + +The source code package includes dynamic library import modules for C#, Java and Pascal/Delphi languages. + +## License + +SoundTouch is released under LGPL v2.1: + +This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation. + +This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +See [LGPL v2.1 full license text ](https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html) for details. + +-- + +Also commercial license free of GPL limitations available upon request diff --git a/soundtouch.m4 b/soundtouch.m4 new file mode 100644 index 0000000..7e7589b --- /dev/null +++ b/soundtouch.m4 @@ -0,0 +1,67 @@ +# m4 configure test script for the SoundTouch library +# +# This file can be included with other packages that need to test +# for libSoundTouch. +# +# It will #define HAVE_LIBSOUNDTOUCH iff the library is found +# It will AC_SUBST SOUNDTOUCH_LIBS and SOUNDTOUCH_CXXFLAGS as well +# It also defines some flags to the configure script for specifying +# the location to search for libSoundTouch +# +# A user of libSoundTouch should add @SOUNDTOUCH_LIBS@ and +# @SOUNDTOUCH_CXXFLAGS@ to the appropriate variables in his +# Makefile.am files +# +# This script works with autoconf-2.5x and automake-1.6 but I have +# not tested it with older versions. + + +dnl min version not supported yet +dnl AM_PATH_SOUNDTOUCH([MINMUM-VERSION, [(additional) ACTION-IF-FOUND] [, ACTION-IF-NOT-FOUND]]]) + +AH_TEMPLATE([HAVE_LIBSOUNDTOUCH], [defined by $0]) +SOUNDTOUCH_CXXFLAGS="" +SOUNDTOUCH_LIBS="" + +AC_DEFUN([AM_PATH_SOUNDTOUCH],[ + AC_ARG_WITH(soundtouch-prefix,[ --with-soundtouch-prefix=DIR Prefix where SoundTouch was installed (optional)], [soundtouch_prefix="$withval"],[soundtouch_prefix=""]) + + AC_ARG_ENABLE(soundtouch-check,[ --disable-soundtouch-check Do not look for the SoundTouch Library],[enable_soundtouch_check="$enableval"],[enable_soundtouch_check="yes"]) + + if test "$enable_soundtouch_check" = "yes" + then + saved_CPPFLAGS="$CPPFLAGS" + saved_LDFLAGS="$LDFLAGS" + + CPPFLAGS="$CPPFLAGS -I$soundtouch_prefix/include" + LDFLAGS="$LDFLAGS -L$soundtouch_prefix/lib" + + dnl make sure SoundTouch.h header file exists + dnl could use AC_CHECK_HEADERS to check for all of them, but the supporting .h file names may change later + AC_CHECK_HEADER([soundtouch/SoundTouch.h],[ + dnl SoundTouch.h found + dnl make sure libSoundTouch is linkable + AC_CHECK_LIB([SoundTouch],[soundtouch_ac_test],[ + dnl libSoundTouch found + SOUNDTOUCH_CXXFLAGS="-I$soundtouch_prefix/include" + SOUNDTOUCH_LIBS="-L$soundtouch_prefix/lib -lSoundTouch" + AC_DEFINE([HAVE_LIBSOUNDTOUCH]) + + dnl run action-if-found + ifelse([$2], , :, [$2]) + ],[ + dnl run action-if-not-found + ifelse([$3], , :, [$3]) + ]) + ],[ + dnl run action-if-not-found + ifelse([$3], , :, [$3]) + ]) + + CPPFLAGS="$saved_CPPFLAGS" + LDFLAGS="$saved_LDFLAGS" + fi + + AC_SUBST(SOUNDTOUCH_CXXFLAGS) + AC_SUBST(SOUNDTOUCH_LIBS) +]) diff --git a/soundtouch.pc.in b/soundtouch.pc.in new file mode 100644 index 0000000..03d0360 --- /dev/null +++ b/soundtouch.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: SoundTouch +Description: SoundTouch is an open-source audio processing library for changing the Tempo, Pitch and Playback Rates of audio streams or files +Version: @VERSION@ +Libs: -L${libdir} -lSoundTouch +Cflags: -I${includedir}/soundtouch + diff --git a/source/Android-lib/.classpath b/source/Android-lib/.classpath new file mode 100644 index 0000000..5176974 --- /dev/null +++ b/source/Android-lib/.classpath @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/source/Android-lib/.project b/source/Android-lib/.project new file mode 100644 index 0000000..1f328a8 --- /dev/null +++ b/source/Android-lib/.project @@ -0,0 +1,33 @@ + + + ExampleActivity + + + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + + com.android.ide.eclipse.adt.AndroidNature + org.eclipse.jdt.core.javanature + + diff --git a/source/Android-lib/AndroidManifest.xml b/source/Android-lib/AndroidManifest.xml new file mode 100644 index 0000000..b77e35e --- /dev/null +++ b/source/Android-lib/AndroidManifest.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + diff --git a/source/Android-lib/README-SoundTouch-Android.html b/source/Android-lib/README-SoundTouch-Android.html new file mode 100644 index 0000000..4a2c809 --- /dev/null +++ b/source/Android-lib/README-SoundTouch-Android.html @@ -0,0 +1,127 @@ + + + + SoundTouch in Android + + + + + + + +
+

SoundTouch in Android

+
+

Compiling SoundTouch for Android

+

SoundTouch source code package contains "Android-lib" example project that compiles SoundTouch + source codes into Android native library, and gives an example of JNI interface + for invoking + the native SoundTouch routines from an Android application written in Java.

+

Software prerequisites:

+
    +
  • Android SDK environment for developing your own Android application. Visit the Android developers' site + for more information about the Android SDK and developing Android applications.
  • +
  • Android NDK compiler kit for compiling native library binaries. The Android NDK + is + available for download at the Android developer tools site.
  • +
  • In case you're working in Windows environment, install + + Cygwin to run the Android NDK/SDK compiler scripts
  • +
  • Latest SoundTouch source code package available at + soundtouch.surina.net.
  • +
+

Hint: As installing and configuring all the components for an Android SDK/NDK + environment requires fair effort, it's good idea to create a dedicated Virtual + Machine environment for the Android development environment installation. + Having the Android developer environment setup in dedicated Virtual Machine + allows keeping all these settings isolated from your other PC operations, and + eases taking backup snapshots of your full development environment.

+

Compiling

+

+ To compile the SoundTouch library source codes into an Android native library, + open Cygwin/bash shell, go to directory "soundtouch/source/Android-lib/jni" and invoke the NDK + compiler with following command:

+
    $NDK/ndk-build
+

This will build binaries for all the supported Android platforms (arm-v5, arm-v7, X86, MIPS etc) of SoundTouch library, plus the JNI wrapper interface as discussed below. The target binaries will be built into the "libs" subdirectory. As long as all these .so binary library versions are included in the APK Application delivery package, the target Android device can choose the correct library version to use.

+

Notice that to allow Cygwin/bash to locate the NDK compile scripts, you + need to define the location of the NDK installation defined in environment + variable "NDK". That's easiest done by adding the NDK path definition at end of + your ~/.bash_profile file, for instance as follows:

+
    NDK=/cygdrive/d/Android/android-ndk-r6
+

Enabling OpenMP parallel computing mode

+

+ SoundTouch supports OpenMP for parallel computing in multi-core + environments, and these improvements can be enabled also in the Android + build. See the SoundTouch main README.html file for generic notes about the + OpenMP implementation.

+

+ To enable OpenMP mode in Android compilation, edit file Android.mk + and enable the "-fopenmp" flag in LOCAL_CFLAGS and LOCAL_LDFLAGS variables. + This is done by removing hash # from before the following lines in the + Android.mk file, before compiling the library:

+
    LOCAL_CFLAGS += -fopenmp
+   LOCAL_LDFLAGS += -fopenmp
+

OpenMP COMPATIBILITY NOTE: Android NDK has a threading issue +(at least until NDK v10) that causes the native library crash with fatal signal +11 if calling OpenMP-improved routines from a background thread. SoundTouch has +a workaround for this issue in soundtouch-jni.cpp, and this workaround requires +calling function SoundTouch.getVersionString() from the Android +application's main thread at least once before calling other SoundTouch +processing routines. See the SoundTouch Android example application and comments +in the soundtouch-jni.cpp source code file for more details.

+

+ SoundTouch performance in Android

+

+ See external blog articles for more discussion about the + + SoundTouch OpenMP implementation and the + + SoundTouch performance benchmark tests in Android environment.

+
+

+ Calling SoundTouch native routines from Android application

+

The NDK tools build the SoundTouch c++ routines into a native binary library, while + Android applications are written in Java language. To call the SoundTouch and other c/c++ + routines from an Android java application code, you'll need to use Java Native + Interface (JNI).

+

+ The SoundTouch source code package provides source code example how to + use JNI to call native c++ routines from a Java class, and provides source codes also for + a simple example Android application:

    +
  • ExampleActivity: This is simple Android example application that + utilizes SoundTouch native routines for processing WAV audio files. To build the example + application, use Eclipse Android SDK environment to import the "ExampleActivity" project in the "Android-lib" folder into the Eclipse workspace. +
  • Android-lib/jni/soundtouch-jni.cpp: This file contains c/c++ wrapper routines + for performing elementary audio file processing with adjusted tempo/pitch/speed parameters + from the Android application. The wrapper interface is not complete, but provides example + that is easy to extend when necessary. The NDK compiles this file along with the SoundTouch + routines into the native binary library.
  • +
  • Android-lib/src/net/surina/soundtouch/SoundTouch.java: This file implements + the Java interface class that loasd & accesses the JNI routines in the natively compiled library. + The example Android application uses this class as interface for processing audio files + with SoundTouch.
  • +
+

+ Feel free to examine and extend the provided cpp/java source code example file pair to + implement and integrate the desired SoundTouch library capabilities into your own Android application.

+
+

+ Android floating-point performance considerations

+

+ The make process will build dedicated binaries for each supported Android CPU hardware platform type. +

SoundTouch uses floating-point algorithms for ideal sound quality on all other platform than in the lowest-end ARMv5. That is because lowest-end Android devices are not guaranteed to + have floating-point hardware in their CPUs, so that the ARMv5 compilation uses by default software-emulation for floating-point calculations to allow running the binary executables also in low-end devices without floating-point hardware.

+ As floating point software-emulation is however several tens of times slower + than real hardware-level floating-point calculations, that would make running + floating-point-intensive applications such as SoundTouch infeasible in these low-end + devices. As workaround, the SoundTouch Android compilation builds the ARMv5 version using integer algorithm versions. The integer + algorithm version compromises the sound quality but provides good performance also + with low-end devices without hardware floating-point support in the CPU level.

+

When Android devices with more capable device is used, the device will automatically choose a proper library version for ideal sound quality.

+
+

Copyright © Olli Parviainen

+ + diff --git a/source/Android-lib/jni/Android.mk b/source/Android-lib/jni/Android.mk new file mode 100644 index 0000000..778ed89 --- /dev/null +++ b/source/Android-lib/jni/Android.mk @@ -0,0 +1,51 @@ +# Copyright (C) 2010 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +# *** Remember: Change -O0 into -O2 in add-applications.mk *** + +LOCAL_MODULE := soundtouch +LOCAL_SRC_FILES := soundtouch-jni.cpp ../../SoundTouch/AAFilter.cpp ../../SoundTouch/FIFOSampleBuffer.cpp \ + ../../SoundTouch/FIRFilter.cpp ../../SoundTouch/cpu_detect_x86.cpp \ + ../../SoundTouch/sse_optimized.cpp ../../SoundStretch/WavFile.cpp \ + ../../SoundTouch/RateTransposer.cpp ../../SoundTouch/SoundTouch.cpp \ + ../../SoundTouch/InterpolateCubic.cpp ../../SoundTouch/InterpolateLinear.cpp \ + ../../SoundTouch/InterpolateShannon.cpp ../../SoundTouch/TDStretch.cpp \ + ../../SoundTouch/BPMDetect.cpp ../../SoundTouch/PeakFinder.cpp + +# for native audio +LOCAL_SHARED_LIBRARIES += -lgcc +# --whole-archive -lgcc +# for logging +LOCAL_LDLIBS += -llog +# for native asset manager +#LOCAL_LDLIBS += -landroid + +# Custom Flags: +# -fvisibility=hidden : don't export all symbols +LOCAL_CFLAGS += -fvisibility=hidden -I ../../../include -fdata-sections -ffunction-sections + +# OpenMP mode : enable these flags to enable using OpenMP for parallel computation +#LOCAL_CFLAGS += -fopenmp +#LOCAL_LDFLAGS += -fopenmp + + +# Use ARM instruction set instead of Thumb for improved calculation performance in ARM CPUs +LOCAL_ARM_MODE := arm + +include $(BUILD_SHARED_LIBRARY) diff --git a/source/Android-lib/jni/Application.mk b/source/Android-lib/jni/Application.mk new file mode 100644 index 0000000..122fb12 --- /dev/null +++ b/source/Android-lib/jni/Application.mk @@ -0,0 +1,9 @@ +# +# Build library bilaries for all supported architectures +# + +APP_ABI := all #armeabi-v7a armeabi +APP_OPTIM := release +APP_STL := stlport_static +APP_CPPFLAGS := -fexceptions # -D SOUNDTOUCH_DISABLE_X86_OPTIMIZATIONS + diff --git a/source/Android-lib/jni/soundtouch-jni.cpp b/source/Android-lib/jni/soundtouch-jni.cpp new file mode 100644 index 0000000..6383fe4 --- /dev/null +++ b/source/Android-lib/jni/soundtouch-jni.cpp @@ -0,0 +1,255 @@ +//////////////////////////////////////////////////////////////////////////////// +/// +/// Example Interface class for SoundTouch native compilation +/// +/// Author : Copyright (c) Olli Parviainen +/// Author e-mail : oparviai 'at' iki.fi +/// WWW : http://www.surina.net +/// +//////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include + +using namespace std; + +#include "../../../include/SoundTouch.h" +#include "../source/SoundStretch/WavFile.h" + +#define LOGV(...) __android_log_print((int)ANDROID_LOG_INFO, "SOUNDTOUCH", __VA_ARGS__) +//#define LOGV(...) + + +// String for keeping possible c++ exception error messages. Notice that this isn't +// thread-safe but it's expected that exceptions are special situations that won't +// occur in several threads in parallel. +static string _errMsg = ""; + + +#define DLL_PUBLIC __attribute__ ((visibility ("default"))) +#define BUFF_SIZE 4096 + + +using namespace soundtouch; + + +// Set error message to return +static void _setErrmsg(const char *msg) +{ + _errMsg = msg; +} + + +#ifdef _OPENMP + +#include +extern pthread_key_t gomp_tls_key; +static void * _p_gomp_tls = NULL; + +/// Function to initialize threading for OpenMP. +/// +/// This is a workaround for bug in Android NDK v10 regarding OpenMP: OpenMP works only if +/// called from the Android App main thread because in the main thread the gomp_tls storage is +/// properly set, however, Android does not properly initialize gomp_tls storage for other threads. +/// Thus if OpenMP routines are invoked from some other thread than the main thread, +/// the OpenMP routine will crash the application due to NULL pointer access on uninitialized storage. +/// +/// This workaround stores the gomp_tls storage from main thread, and copies to other threads. +/// In order this to work, the Application main thread needws to call at least "getVersionString" +/// routine. +static int _init_threading(bool warn) +{ + void *ptr = pthread_getspecific(gomp_tls_key); + LOGV("JNI thread-specific TLS storage %ld", (long)ptr); + if (ptr == NULL) + { + LOGV("JNI set missing TLS storage to %ld", (long)_p_gomp_tls); + pthread_setspecific(gomp_tls_key, _p_gomp_tls); + } + else + { + LOGV("JNI store this TLS storage"); + _p_gomp_tls = ptr; + } + // Where critical, show warning if storage still not properly initialized + if ((warn) && (_p_gomp_tls == NULL)) + { + _setErrmsg("Error - OpenMP threading not properly initialized: Call SoundTouch.getVersionString() from the App main thread!"); + return -1; + } + return 0; +} + +#else +static int _init_threading(bool warn) +{ + // do nothing if not OpenMP build + return 0; +} +#endif + + +// Processes the sound file +static void _processFile(SoundTouch *pSoundTouch, const char *inFileName, const char *outFileName) +{ + int nSamples; + int nChannels; + int buffSizeSamples; + SAMPLETYPE sampleBuffer[BUFF_SIZE]; + + // open input file + WavInFile inFile(inFileName); + int sampleRate = inFile.getSampleRate(); + int bits = inFile.getNumBits(); + nChannels = inFile.getNumChannels(); + + // create output file + WavOutFile outFile(outFileName, sampleRate, bits, nChannels); + + pSoundTouch->setSampleRate(sampleRate); + pSoundTouch->setChannels(nChannels); + + assert(nChannels > 0); + buffSizeSamples = BUFF_SIZE / nChannels; + + // Process samples read from the input file + while (inFile.eof() == 0) + { + int num; + + // Read a chunk of samples from the input file + num = inFile.read(sampleBuffer, BUFF_SIZE); + nSamples = num / nChannels; + + // Feed the samples into SoundTouch processor + pSoundTouch->putSamples(sampleBuffer, nSamples); + + // Read ready samples from SoundTouch processor & write them output file. + // NOTES: + // - 'receiveSamples' doesn't necessarily return any samples at all + // during some rounds! + // - On the other hand, during some round 'receiveSamples' may have more + // ready samples than would fit into 'sampleBuffer', and for this reason + // the 'receiveSamples' call is iterated for as many times as it + // outputs samples. + do + { + nSamples = pSoundTouch->receiveSamples(sampleBuffer, buffSizeSamples); + outFile.write(sampleBuffer, nSamples * nChannels); + } while (nSamples != 0); + } + + // Now the input file is processed, yet 'flush' few last samples that are + // hiding in the SoundTouch's internal processing pipeline. + pSoundTouch->flush(); + do + { + nSamples = pSoundTouch->receiveSamples(sampleBuffer, buffSizeSamples); + outFile.write(sampleBuffer, nSamples * nChannels); + } while (nSamples != 0); +} + + + +extern "C" DLL_PUBLIC jstring Java_net_surina_soundtouch_SoundTouch_getVersionString(JNIEnv *env, jobject thiz) +{ + const char *verStr; + + LOGV("JNI call SoundTouch.getVersionString"); + + // Call example SoundTouch routine + verStr = SoundTouch::getVersionString(); + + /// gomp_tls storage bug workaround - see comments in _init_threading() function! + _init_threading(false); + + int threads = 0; + #pragma omp parallel + { + #pragma omp atomic + threads ++; + } + LOGV("JNI thread count %d", threads); + + // return version as string + return env->NewStringUTF(verStr); +} + + + +extern "C" DLL_PUBLIC jlong Java_net_surina_soundtouch_SoundTouch_newInstance(JNIEnv *env, jobject thiz) +{ + return (jlong)(new SoundTouch()); +} + + +extern "C" DLL_PUBLIC void Java_net_surina_soundtouch_SoundTouch_deleteInstance(JNIEnv *env, jobject thiz, jlong handle) +{ + SoundTouch *ptr = (SoundTouch*)handle; + delete ptr; +} + + +extern "C" DLL_PUBLIC void Java_net_surina_soundtouch_SoundTouch_setTempo(JNIEnv *env, jobject thiz, jlong handle, jfloat tempo) +{ + SoundTouch *ptr = (SoundTouch*)handle; + ptr->setTempo(tempo); +} + + +extern "C" DLL_PUBLIC void Java_net_surina_soundtouch_SoundTouch_setPitchSemiTones(JNIEnv *env, jobject thiz, jlong handle, jfloat pitch) +{ + SoundTouch *ptr = (SoundTouch*)handle; + ptr->setPitchSemiTones(pitch); +} + + +extern "C" DLL_PUBLIC void Java_net_surina_soundtouch_SoundTouch_setSpeed(JNIEnv *env, jobject thiz, jlong handle, jfloat speed) +{ + SoundTouch *ptr = (SoundTouch*)handle; + ptr->setRate(speed); +} + + +extern "C" DLL_PUBLIC jstring Java_net_surina_soundtouch_SoundTouch_getErrorString(JNIEnv *env, jobject thiz) +{ + jstring result = env->NewStringUTF(_errMsg.c_str()); + _errMsg.clear(); + + return result; +} + + +extern "C" DLL_PUBLIC int Java_net_surina_soundtouch_SoundTouch_processFile(JNIEnv *env, jobject thiz, jlong handle, jstring jinputFile, jstring joutputFile) +{ + SoundTouch *ptr = (SoundTouch*)handle; + + const char *inputFile = env->GetStringUTFChars(jinputFile, 0); + const char *outputFile = env->GetStringUTFChars(joutputFile, 0); + + LOGV("JNI process file %s", inputFile); + + /// gomp_tls storage bug workaround - see comments in _init_threading() function! + if (_init_threading(true)) return -1; + + try + { + _processFile(ptr, inputFile, outputFile); + } + catch (const runtime_error &e) + { + const char *err = e.what(); + // An exception occurred during processing, return the error message + LOGV("JNI exception in SoundTouch::processFile: %s", err); + _setErrmsg(err); + return -1; + } + + + env->ReleaseStringUTFChars(jinputFile, inputFile); + env->ReleaseStringUTFChars(joutputFile, outputFile); + + return 0; +} diff --git a/source/Android-lib/lint.xml b/source/Android-lib/lint.xml new file mode 100644 index 0000000..ee0eead --- /dev/null +++ b/source/Android-lib/lint.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/source/Android-lib/proguard-project.txt b/source/Android-lib/proguard-project.txt new file mode 100644 index 0000000..f2fe155 --- /dev/null +++ b/source/Android-lib/proguard-project.txt @@ -0,0 +1,20 @@ +# To enable ProGuard in your project, edit project.properties +# to define the proguard.config property as described in that file. +# +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in ${sdk.dir}/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the ProGuard +# include property in project.properties. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/source/Android-lib/project.properties b/source/Android-lib/project.properties new file mode 100644 index 0000000..4ab1256 --- /dev/null +++ b/source/Android-lib/project.properties @@ -0,0 +1,14 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system edit +# "ant.properties", and override values to adapt the script to your +# project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt + +# Project target. +target=android-19 diff --git a/source/Android-lib/res/drawable-hdpi/ic_launcher.png b/source/Android-lib/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..288b66551d1efd1f13dd06f20a67534d2df57946 GIT binary patch literal 7658 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 literal 0 HcmV?d00001 diff --git a/source/Android-lib/res/drawable-xhdpi/ic_launcher.png b/source/Android-lib/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..d4fb7cd9d868f1d7d9964f1686dcbc018ef9495a GIT binary patch literal 12516 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 literal 0 HcmV?d00001 diff --git a/source/Android-lib/res/layout/activity_example.xml b/source/Android-lib/res/layout/activity_example.xml new file mode 100644 index 0000000..c71c77d --- /dev/null +++ b/source/Android-lib/res/layout/activity_example.xml @@ -0,0 +1,140 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +