diff options
Diffstat (limited to 'plugins/dsp_soundtouch')
25 files changed, 7650 insertions, 0 deletions
diff --git a/plugins/dsp_soundtouch/Makefile b/plugins/dsp_soundtouch/Makefile new file mode 100644 index 00000000..742ed3b7 --- /dev/null +++ b/plugins/dsp_soundtouch/Makefile @@ -0,0 +1,52 @@ +CC=gcc +CXX=g++ + +OUT=ddb_dsp_soundtouch.so + +soundtouch_path=soundtouch + +CFLAGS+=-Wall -g -D_GNU_SOURCE -I$(soundtouch_path)/include -I../../include -std=c99 -fPIC -msse2 +CXXFLAGS+=-Wall -g -D_GNU_SOURCE -I$(soundtouch_path)/include -I../../include -fPIC -msse2 + +LDFLAGS+=-shared -lm + +SOURCES=plugin.c + +CXX_SOURCES=st.cpp\ +$(soundtouch_path)/source/SoundTouch/AAFilter.cpp\ +$(soundtouch_path)/source/SoundTouch/BPMDetect.cpp\ +$(soundtouch_path)/source/SoundTouch/cpu_detect_x86_gcc.cpp\ +$(soundtouch_path)/source/SoundTouch/FIFOSampleBuffer.cpp\ +$(soundtouch_path)/source/SoundTouch/FIRFilter.cpp\ +$(soundtouch_path)/source/SoundTouch/mmx_optimized.cpp\ +$(soundtouch_path)/source/SoundTouch/PeakFinder.cpp\ +$(soundtouch_path)/source/SoundTouch/RateTransposer.cpp\ +$(soundtouch_path)/source/SoundTouch/SoundTouch.cpp\ +$(soundtouch_path)/source/SoundTouch/sse_optimized.cpp\ +$(soundtouch_path)/source/SoundTouch/TDStretch.cpp + +HEADERS=st.h\ +$(soundtouch_path)/include/BPMDetect.h\ +$(soundtouch_path)/include/FIFOSampleBuffer.h\ +$(soundtouch_path)/include/FIFOSamplePipe.h\ +$(soundtouch_path)/include/soundtouch_config.h\ +$(soundtouch_path)/include/SoundTouch.h\ +$(soundtouch_path)/include/STTypes.h + +CXX_OBJECTS=$(CXX_SOURCES:.cpp=.o) +OBJECTS=$(SOURCES:.c=.o) + +all: $(SOURCES) $(OUT) + +$(OUT): $(OBJECTS) $(CXX_OBJECTS) + $(CXX) $(LDFLAGS) $(OBJECTS) $(CXX_OBJECTS) -o $@ + +.c.o: $(HEADERS) + $(CC) $(CFLAGS) $< -c -o $@ + +.cpp.o: $(HEADERS) + $(CXX) $(CXXFLAGS) $< -c -o $@ + +clean: + rm $(OBJECTS) $(CXX_OBJECTS) $(OUT) + diff --git a/plugins/dsp_soundtouch/plugin.c b/plugins/dsp_soundtouch/plugin.c new file mode 100644 index 00000000..62fc9a2a --- /dev/null +++ b/plugins/dsp_soundtouch/plugin.c @@ -0,0 +1,258 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include "../../deadbeef.h" +#include "st.h" + +enum { + ST_PARAM_TEMPO, + ST_PARAM_PITCH, + ST_PARAM_RATE, + ST_PARAM_USE_AA_FILTER, + ST_PARAM_AA_FILTER_LENGTH, + ST_PARAM_USE_QUICKSEEK, + ST_PARAM_SEQUENCE_MS, + ST_PARAM_SEEKWINDOW_MS, + ST_PARAM_COUNT +}; + +static DB_functions_t *deadbeef; +static DB_dsp_t plugin; + +typedef struct { + ddb_dsp_context_t ctx; + void *st; + float tempo; + float pitch; + float rate; + int use_aa_filter; + int aa_filter_length; + int use_quickseek; + int sequence_ms; + int seekwindow_ms; + int changed; +} ddb_soundtouch_t; + +ddb_dsp_context_t* +st_open (void) { + ddb_soundtouch_t *st = malloc (sizeof (ddb_soundtouch_t)); + DDB_INIT_DSP_CONTEXT (st,ddb_soundtouch_t,&plugin); + st->st = st_alloc (); + st->changed = 1; + st->tempo = 0; + st->rate = 0; + st->pitch = 0; + st->use_aa_filter = 0; + st->aa_filter_length = 32; + st->use_quickseek = 0; + st->sequence_ms = 82; + st->seekwindow_ms = 28; + return (ddb_dsp_context_t *)st; +} + +void +st_close (ddb_dsp_context_t *_src) { + ddb_soundtouch_t *st = (ddb_soundtouch_t *)_src; + if (st->st) { + st_free (st->st); + } + free (st); +} + +void +st_reset (ddb_dsp_context_t *_src) { + ddb_soundtouch_t *st = (ddb_soundtouch_t *)_src; + st_clear (st->st); +} + +int +st_process (ddb_dsp_context_t *_src, float *samples, int nframes, ddb_waveformat_t *fmt) { + ddb_soundtouch_t *st = (ddb_soundtouch_t *)_src; + if (st->changed) { + st_set_rate_change (st->st, st->rate); + st_set_pitch_semi_tones (st->st, st->pitch); + st_set_tempo_change (st->st, st->tempo); + st_set_setting (st->st, SETTING_USE_AA_FILTER, st->use_aa_filter); + st_set_setting (st->st, SETTING_AA_FILTER_LENGTH, st->aa_filter_length); + st_set_setting (st->st, SETTING_USE_QUICKSEEK, st->use_quickseek); + st_set_setting (st->st, SETTING_SEQUENCE_MS, st->sequence_ms); + st_set_setting (st->st, SETTING_SEEKWINDOW_MS, st->seekwindow_ms); + st->changed = 0; + } + + st_set_sample_rate (st->st, fmt->samplerate); + st_set_channels (st->st, fmt->channels); + + st_put_samples (st->st, samples, nframes); + int nout = 0; + int nmax = nframes * 24; + int n = 0; + do { + n = st_receive_samples (st->st, samples, nmax); + nmax -= n; + samples += n * fmt->channels; + nout += n; + } while (n != 0); + + return nout; +} + +const char * +st_get_param_name (int p) { + switch (p) { + case ST_PARAM_TEMPO: + return "Tempo"; + case ST_PARAM_PITCH: + return "Pitch"; + case ST_PARAM_RATE: + return "Playback Rate"; + case ST_PARAM_USE_AA_FILTER: + return "Use AA Filter"; + case ST_PARAM_AA_FILTER_LENGTH: + return "AA Filter Length"; + case ST_PARAM_USE_QUICKSEEK: + return "Use Quickseek"; + case ST_PARAM_SEQUENCE_MS: + return "Time Stretch Sequence Length (ms)"; + case ST_PARAM_SEEKWINDOW_MS: + return "Time Stretch Seek Window Length (ms)"; + default: + fprintf (stderr, "st_param_name: invalid param index (%d)\n", p); + } + return NULL; +} + +int +st_num_params (void) { + return ST_PARAM_COUNT; +} + +void +st_set_param (ddb_dsp_context_t *ctx, int p, const char *val) { + ddb_soundtouch_t *st = (ddb_soundtouch_t *)ctx; + switch (p) { + case ST_PARAM_TEMPO: + st->tempo = atof (val); + st->changed = 1; + break; + case ST_PARAM_PITCH: + st->pitch = atof (val); + st->changed = 1; + break; + case ST_PARAM_RATE: + st->rate = atof (val); + st->changed = 1; + break; + case ST_PARAM_USE_AA_FILTER: + st->use_aa_filter = atoi (val); + st->changed = 1; + break; + case ST_PARAM_AA_FILTER_LENGTH: + st->aa_filter_length = atoi (val); + st->changed = 1; + break; + case ST_PARAM_USE_QUICKSEEK: + st->use_quickseek = atoi (val); + st->changed = 1; + break; + case ST_PARAM_SEQUENCE_MS: + st->sequence_ms = atoi (val); + break; + case ST_PARAM_SEEKWINDOW_MS: + st->seekwindow_ms = atoi (val); + break; + default: + fprintf (stderr, "st_param: invalid param index (%d)\n", p); + } +} + +void +st_get_param (ddb_dsp_context_t *ctx, int p, char *val, int sz) { + ddb_soundtouch_t *st = (ddb_soundtouch_t *)ctx; + switch (p) { + case ST_PARAM_TEMPO: + snprintf (val, sizeof (val), "%f", st->tempo); + break; + case ST_PARAM_PITCH: + snprintf (val, sizeof (val), "%f", st->pitch); + break; + case ST_PARAM_RATE: + snprintf (val, sizeof (val), "%f", st->rate); + break; + case ST_PARAM_USE_AA_FILTER: + snprintf (val, sizeof (val), "%d", st->use_aa_filter); + break; + case ST_PARAM_AA_FILTER_LENGTH: + snprintf (val, sizeof (val), "%d", st->aa_filter_length); + break; + case ST_PARAM_USE_QUICKSEEK: + snprintf (val, sizeof (val), "%d", st->use_quickseek); + break; + case ST_PARAM_SEQUENCE_MS: + snprintf (val, sizeof (val), "%d", st->sequence_ms); + break; + case ST_PARAM_SEEKWINDOW_MS: + snprintf (val, sizeof (val), "%d", st->seekwindow_ms); + break; + default: + fprintf (stderr, "st_get_param: invalid param index (%d)\n", p); + } +} + +static const char settings_dlg[] = + "property \"Tempo Change (%)\" spinbtn[-200,200,1] 0 0;\n" + "property \"Pitch Change (semi-tones)\" spinbtn[-24,24,1] 1 0;\n" + "property \"Rate Change (%)\" spinbtn[-200,200,1] 2 0;\n" + "property \"Use AA Filter\" checkbox 3 0;\n" + "property \"AA Filter Length\" spinbtn[16,64,1] 4 32;\n" + "property \"Use Quickseek\" checkbox 5 0;\n" + "property \"Time Stretch Sequence Length (ms)\" spinbtn[10,500,1] 6 82;\n" + "property \"Time Stretch Seek Window Length (ms)\" spinbtn[10,500,1] 7 28;\n" +; + +static DB_dsp_t plugin = { + .plugin.api_vmajor = DB_API_VERSION_MAJOR, + .plugin.api_vminor = DB_API_VERSION_MINOR, + .open = st_open, + .close = st_close, + .process = st_process, + .plugin.version_major = 0, + .plugin.version_minor = 1, + .plugin.type = DB_PLUGIN_DSP, + .plugin.id = "soundtouch", + .plugin.name = "Soundtouch", + .plugin.descr = "Tempo/Pitch/Rate changer using SoundTouch Library (http://www.surina.net/soundtouch)", + .plugin.author = "Alexey Yakovenko", + .plugin.email = "waker@users.sf.net", + .plugin.website = "http://deadbeef.sf.net", + .num_params = st_num_params, + .get_param_name = st_get_param_name, + .set_param = st_set_param, + .get_param = st_get_param, + .reset = st_reset, + .configdialog = settings_dlg, +}; + +DB_plugin_t * +ddb_dsp_soundtouch_load (DB_functions_t *f) { + deadbeef = f; + return &plugin.plugin; +} diff --git a/plugins/dsp_soundtouch/soundtouch/COPYING.TXT b/plugins/dsp_soundtouch/soundtouch/COPYING.TXT new file mode 100644 index 00000000..5b2161be --- /dev/null +++ b/plugins/dsp_soundtouch/soundtouch/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 diff --git a/plugins/dsp_soundtouch/soundtouch/README.html b/plugins/dsp_soundtouch/soundtouch/README.html new file mode 100644 index 00000000..15a34c86 --- /dev/null +++ b/plugins/dsp_soundtouch/soundtouch/README.html @@ -0,0 +1,752 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+ <meta http-equiv="Content-Type"
+ content="text/html; charset=windows-1252">
+ <meta http-equiv="Content-Language" content="en-us">
+ <meta name="author" content="Olli Parviainen">
+ <meta name="description"
+ content="Readme file for SoundTouch audio processing library">
+ <meta name="GENERATOR" content="Microsoft FrontPage 4.0">
+ <meta name="ProgId" content="FrontPage.Editor.Document">
+ <title>SoundTouch library README</title>
+ <style>
+<!--
+.normal { font-family: Arial }
+-->
+ </style>
+</head>
+<body class="normal">
+<hr>
+<h1>SoundTouch audio processing library v1.5.0
+</h1>
+<p class="normal">SoundTouch library Copyright (c) Olli
+Parviainen 2002-2009 </p>
+<hr>
+<h2>1. Introduction </h2>
+<p>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.:</p>
+<ul>
+ <li>Sound tempo can be increased or decreased while
+maintaining the original pitch</li>
+ <li>Sound pitch can be increased or decreased while
+maintaining the original tempo </li>
+ <li>Change playback rate that affects both tempo
+and pitch at the same time </li>
+ <li>Choose any combination of tempo/pitch/rate</li>
+</ul>
+<h3>1.1 Contact information </h3>
+<p>Author email: oparviai 'at' iki.fi </p>
+<p>SoundTouch WWW page: <a href="http://www.surina.net/soundtouch">http://www.surina.net/soundtouch</a></p>
+<hr>
+<h2>2. Compiling SoundTouch</h2>
+<p>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.</p>
+<h3>2.1. Building in Microsoft Windows</h3>
+<p>Project files for Microsoft Visual C++ 6.0 and Visual C++ .NET are
+supplied with the source code package. </p>
+<p> Please notice that SoundTouch
+library uses processor-specific optimizations for Pentium III and AMD
+processors. Visual Studio .NET and later versions supports the required
+instructions by default, but Visual Studio 6.0 requires a processor pack upgrade
+to be installed in order to support these optimizations. The processor pack upgrade can be downloaded from
+Microsoft site at this URL:</p>
+<p><a href="http://msdn.microsoft.com/en-us/vstudio/aa718349.aspx">http://msdn.microsoft.com/en-us/vstudio/aa718349.aspx</a></p>
+<p>If the above URL is unavailable or removed, go
+to <a href="http://msdn.microsoft.com/">http://msdn.microsoft.com</a>
+and perform a search with keywords "processor pack". </p>
+<p>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.
+</p>
+<h3>2.2. Building in Gnu platforms</h3>
+<p>The SoundTouch library can be compiled in
+practically any platform supporting GNU compiler (GCC) tools.
+SoundTouch have been tested with gcc version 3.3.4., but it
+shouldn't be very specific about the gcc version. Assembler-level
+performance optimizations for GNU platform are currently available in
+x86 platforms only, they are automatically disabled and replaced with
+standard C routines in other processor platforms.</p>
+<p>To build and install the binaries, run the
+following commands in the SoundTouch/ directory:</p>
+<table border="0" cellpadding="0" cellspacing="4">
+ <tbody>
+ <tr valign="top">
+ <td>
+ <pre>./configure -</pre>
+ </td>
+ <td>
+ <p>Configures the SoundTouch package for the local
+environment.</p>
+ </td>
+ </tr>
+ <tr valign="top">
+ <td>
+ <pre>make -</pre>
+ </td>
+ <td>
+ <p>Builds the SoundTouch library &
+SoundStretch utility.</p>
+ </td>
+ </tr>
+ <tr valign="top">
+ <td>
+ <pre>make install -</pre>
+ </td>
+ <td>
+ <p>Installs the SoundTouch & BPM libraries
+to <b>/usr/local/lib</b> and SoundStretch utility to <b>/usr/local/bin</b>.
+Please notice that 'root' privileges may be required to install the
+binaries to the destination locations.</p>
+ </td>
+ </tr>
+ </tbody>
+</table>
+<h4><b>2.2.1 Required GNU tools</b> </h4>
+<p> 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, in Ubuntu Linux these can be acquired and
+installed with the following command:</p>
+<pre><b>sudo apt-get install <font SIZE="2">automake autoconf libtool build-essential</font></b></pre>
+<h4><b>2.2.2 Problems with GCC compiler compatibility</b></h4>
+<p>At the release time the SoundTouch package has been tested to compile in
+GNU/Linux platform. However, in past it's happened that new gcc versions aren't
+necessarily compatible with the assembler settings used in the optimized
+routines. <b>If you have problems getting the
+SoundTouch library compiled, try the workaround of disabling the optimizations</b>
+by editing the file "include/STTypes.h" and removing the following
+definition there:</p>
+<blockquote>
+ <pre>#define ALLOW_OPTIMIZATIONS 1</pre>
+</blockquote>
+<h4><b>2.2.3 Problems with configure script or build process</b> </h4>
+<p>Incompatibilities between various GNU toolchain versions may cause errors when running the "configure" script or building the source
+codes, if your GNU tool versions are not compatible with the versions used for
+preparing the SoundTouch kit. </p>
+<p>To resolve the issue, regenerate the configure scripts with your local tool
+set by running
+the "<b>./bootstrap</b>" script included in the SoundTouch source code
+kit. After that, run the <b>configure</b> script and <b>make</b> as usually.</p>
+<h4><b>2.2.4 Compiler issues with non-x86 processors</b></h4>
+<p>SoundTouch library works also on non-x86 processors.</p>
+<p>However, in case that you get compiler errors when trying to compile for non-Intel processor, edit the file
+"<b>source\SoundTouch\Makefile.am</b>" and remove the "<b>-msse2</b>"
+flag on the <b>AM_CXXFLAGS </b>line:</p>
+<pre><b>AM_CXXFLAGS=-O3 -fcheck-new -I../../include # Note: -msse2 flag removed!</b></pre>
+<p>After that, run "<b>./bootstrap</b>" script, and then run <b>configure</b>
+and <b>make</b> again.</p>
+<hr>
+<h2>3. About implementation & Usage tips</h2>
+<h3>3.1. Supported sample data formats</h3>
+<p>The sample data format can be chosen
+between 16bit signed integer and 32bit floating point values, the
+default is 32bit floating point. </p>
+
+<p>
+In Windows environment, the sample data format is chosen
+in file "STTypes.h" by choosing one of the following
+defines:</p>
+<ul>
+ <li><span style="font-weight: bold;">#define INTEGER_SAMPLES</span>
+for 16bit signed
+integer</li>
+ <li><span style="font-weight: bold;">#define FLOAT_SAMPLES</span> for
+32bit floating point</li>
+</ul>
+<p>
+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:
+<blockquote>
+<pre>./configure --enable-integer-samples</pre>
+</blockquote>
+
+<p>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.</p>
+<p>Sample rates between 8000-48000H are
+supported.</p>
+<h3>3.2. Processing latency</h3>
+<p>The processing and latency constraints of
+the SoundTouch library are:</p>
+<ul>
+ <li>Input/output processing latency for the
+SoundTouch processor is around 100 ms. This is when time-stretching is
+used. If the rate transposing effect alone is used, the latency
+requirement
+is much shorter, see section 'About algorithms'.</li>
+ <li>Processing CD-quality sound (16bit stereo
+sound with 44100H sample rate) in real-time or faster is possible
+starting from processors equivalent to Intel Pentium 133Mh or better,
+if using the "quick" processing algorithm. If not using the "quick"
+mode or
+if floating point sample data are being used, several times more CPU
+power is typically required.</li>
+</ul>
+<h3>3.3. About algorithms</h3>
+<p>SoundTouch provides three seemingly
+independent effects: tempo, pitch and playback rate control.
+These three controls are implemented as combination of two primary
+effects, <em>sample rate transposing</em> and <em>time-stretching</em>.</p>
+<p><em>Sample rate transposing</em> 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.</p>
+<p><em>Time-stretching </em>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.</p>
+<p>Sample rate transposing and time-stretching
+are then used together to produce the tempo, pitch and rate
+controls:</p>
+<ul>
+ <li><strong>'Tempo'</strong> control is
+implemented purely by time-stretching.</li>
+ <li><strong>'Rate</strong>' control is implemented
+purely by sample rate transposing.</li>
+ <li><strong>'Pitch</strong>' control is
+implemented as a combination of time-stretching and sample rate
+transposing. For example, to increase pitch the audio stream is first
+time-stretched to longer duration (without affecting pitch) and then
+transposed back to original duration by sample rate transposing, which
+simultaneously reduces duration and increases pitch. The result is
+original duration but increased pitch.</li>
+</ul>
+<h3>3.4 Tuning the algorithm parameters</h3>
+<p>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.</p>
+<p>The time-stretch algorithm default
+parameter values are set by the following #defines in file "TDStretch.h":</p>
+<blockquote>
+ <pre>#define DEFAULT_SEQUENCE_MS AUTOMATIC
+#define DEFAULT_SEEKWINDOW_MS AUTOMATIC
+#define DEFAULT_OVERLAP_MS 8</pre>
+</blockquote>
+<p>These parameters affect to the time-stretch
+algorithm as follows:</p>
+<ul>
+ <li><strong>DEFAULT_SEQUENCE_MS</strong>: This is
+the default length of a single processing sequence in milliseconds
+which determines the how the original sound is chopped in
+the time-stretch algorithm. Larger values mean fewer sequences
+are used in processing. In principle a larger value sounds better when
+slowing down the tempo, but worse when increasing the tempo and vice
+versa. <br>
+ <br>
+ By default, this setting value is calculated automatically according to
+ tempo value.<br>
+ </li>
+ <li><strong>DEFAULT_SEEKWINDOW_MS</strong>: The seeking window
+default length in milliseconds is for the algorithm that seeks the best
+possible overlapping location. This determines from how
+wide a sample "window" the algorithm can use to find an optimal mixing
+location when the sound sequences are to be linked back together. <br>
+ <br>
+The bigger this window setting is, the higher the possibility to find a
+better mixing position becomes, but at the same time large values may
+cause a "drifting" sound artifact because neighboring sequences can be
+chosen at more uneven intervals. If there's a disturbing artifact that
+sounds as if a constant frequency was drifting around, try reducing
+this setting.<br>
+ <br>
+ By default, this setting value is calculated automatically according to
+ tempo value.<br>
+ </li>
+ <li><strong>DEFAULT_OVERLAP_MS</strong>: Overlap
+length in milliseconds. When the sound sequences are mixed back
+together to form again a continuous sound stream, this parameter
+defines how much the ends of the consecutive sequences will overlap with each other.<br>
+ <br>
+ This shouldn't be that critical parameter. If you reduce the
+DEFAULT_SEQUENCE_MS setting by a large amount, you might wish to try a
+smaller value on this.</li>
+</ul>
+<p>Notice that these parameters can also be
+set during execution time with functions "<strong>TDStretch::setParameters()</strong>"
+and "<strong>SoundTouch::setSetting()</strong>".</p>
+<p>The table below summaries how the
+parameters can be adjusted for different applications:</p>
+<table border="1">
+ <tbody>
+ <tr>
+ <td valign="top"><strong>Parameter name</strong></td>
+ <td valign="top"><strong>Default value
+magnitude</strong></td>
+ <td valign="top"><strong>Larger value
+affects...</strong></td>
+ <td valign="top"><strong>Smaller value
+affects...</strong></td>
+ <td valign="top"><strong>Effect to CPU burden</strong></td>
+ </tr>
+ <tr>
+ <td valign="top">
+ <pre>SEQUENCE_MS</pre>
+ </td>
+ <td valign="top">Default value is relatively
+large, chosen for slowing down music tempo</td>
+ <td valign="top">Larger value is usually
+better for slowing down tempo. Growing the value decelerates the
+"echoing" artifact when slowing down the tempo.</td>
+ <td valign="top">Smaller value might be better
+for speeding up tempo. Reducing the value accelerates the "echoing"
+artifact when slowing down the tempo </td>
+ <td valign="top">Increasing the parameter
+value reduces computation burden</td>
+ </tr>
+ <tr>
+ <td valign="top">
+ <pre>SEEKWINDOW_MS</pre>
+ </td>
+ <td valign="top">Default value is relatively
+large, chosen for slowing down music tempo</td>
+ <td valign="top">Larger value eases finding a
+good mixing position, but may cause a "drifting" artifact</td>
+ <td valign="top">Smaller reduce possibility to
+find a good mixing position, but reduce the "drifting" artifact.</td>
+ <td valign="top">Increasing the parameter
+value increases computation burden</td>
+ </tr>
+ <tr>
+ <td valign="top">
+ <pre>OVERLAP_MS</pre>
+ </td>
+ <td valign="top">Default value is relatively
+large, chosen to suit with above parameters.</td>
+ <td valign="top"> </td>
+ <td valign="top">If you reduce the "sequence
+ms" setting, you might wish to try a smaller value.</td>
+ <td valign="top">Increasing the parameter
+value increases computation burden</td>
+ </tr>
+ </tbody>
+</table>
+<h3>3.5 Performance Optimizations </h3>
+<p><strong>General optimizations:</strong></p>
+<p>The time-stretch routine has a 'quick' mode
+that substantially speeds up the algorithm but may degrade the
+sound quality by a small amount. This mode is activated by
+calling SoundTouch::setSetting() function with parameter id
+of SETTING_USE_QUICKSEEK and value "1", i.e. </p>
+<blockquote>
+ <p>setSetting(SETTING_USE_QUICKSEEK, 1);</p>
+</blockquote>
+<p><strong>CPU-specific optimizations:</strong></p>
+<ul>
+ <li>Intel MMX optimized routines are used with
+compatible CPUs when 16bit integer sample type is used. MMX optimizations are available both in Win32 and Gnu/x86 platforms.
+Compatible processors are Intel PentiumMMX and later; AMD K6-2, Athlon
+and later. </li>
+ <li>Intel SSE optimized routines are used with
+compatible CPUs when floating point sample type is used. SSE optimizations are currently implemented for Win32 platform only.
+Processors compatible with SSE extension are Intel processors starting
+from Pentium-III, and AMD processors starting from Athlon XP. </li>
+ <li>AMD 3DNow! optimized routines are used with
+compatible CPUs when floating point sample type is used, but SSE
+extension isn't supported . 3DNow! optimizations are currently
+implemented for Win32 platform only. These optimizations are used in
+AMD K6-2 and Athlon (classic) CPU's; better performing SSE routines are
+used with AMD processor starting from Athlon XP. </li>
+</ul>
+<hr>
+<h2><a name="SoundStretch"></a>4. SoundStretch audio processing utility
+</h2>
+<p>SoundStretch audio processing utility<br>
+Copyright (c) Olli Parviainen 2002-2009</p>
+<p>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.</p>
+<h3>4.1. SoundStretch Usage Instructions</h3>
+<p>SoundStretch Usage syntax:</p>
+<blockquote>
+ <pre>soundstretch infilename outfilename [switches]</pre>
+</blockquote>
+<p>Where: </p>
+<table border="0" cellpadding="2" width="100%">
+ <tbody>
+ <tr>
+ <td valign="top">
+ <pre>"infilename"</pre>
+ </td>
+ <td valign="top">Name of the input sound
+data file (in .WAV audio file format). Give "stdin" as filename to use
+ standard input pipe. </td>
+ </tr>
+ <tr>
+ <td valign="top">
+ <pre>"outfilename"</pre>
+ </td>
+ <td valign="top">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.</td>
+ </tr>
+ <tr>
+ <td valign="top">
+ <pre> [switches]</pre>
+ </td>
+ <td valign="top">Are one or more control
+switches.</td>
+ </tr>
+ </tbody>
+</table>
+<p>Available control switches are:</p>
+<table border="0" cellpadding="2" width="100%">
+ <tbody>
+ <tr>
+ <td valign="top">
+ <pre>-tempo=n </pre>
+ </td>
+ <td valign="top">Change the sound tempo by n
+percents (n = -95.0 .. +5000.0 %) </td>
+ </tr>
+ <tr>
+ <td valign="top">
+ <pre>-pitch=n</pre>
+ </td>
+ <td valign="top">Change the sound pitch by n
+semitones (n = -60.0 .. + 60.0 semitones) </td>
+ </tr>
+ <tr>
+ <td valign="top">
+ <pre>-rate=n</pre>
+ </td>
+ <td valign="top">Change the sound playback rate by
+n percents (n = -95.0 .. +5000.0 %) </td>
+ </tr>
+ <tr>
+ <td valign="top">
+ <pre>-bpm=n</pre>
+ </td>
+ <td valign="top">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. </td>
+ </tr>
+ <tr>
+ <td valign="top">
+ <pre>-quick</pre>
+ </td>
+ <td valign="top">Use quicker tempo change
+algorithm. Gains speed but loses sound quality. </td>
+ </tr>
+ <tr>
+ <td valign="top">
+ <pre>-naa</pre>
+ </td>
+ <td valign="top">Don't use anti-alias
+filtering in sample rate transposing. Gains speed but loses sound
+quality. </td>
+ </tr>
+ <tr>
+ <td valign="top">
+ <pre>-license</pre>
+ </td>
+ <td valign="top">Displays the program license
+text (LGPL)</td>
+ </tr>
+ </tbody>
+</table>
+<p>Notes:</p>
+<ul>
+ <li>To use standard input/output pipes for processing, give "stdin"
+ and "stdout" as input/output filenames correspondingly. The
+ standard input/output pipes will still carry the audio data in .wav audio
+ file format.</li>
+ <li>The numerical switches allow both integer (e.g. "-tempo=123") and decimal (e.g.
+"-tempo=123.45") numbers.</li>
+ <li>The "-naa" and/or "-quick" switches can be
+used to reduce CPU usage while compromising some sound quality </li>
+ <li>The BPM detection algorithm works by detecting
+repeating bass or drum patterns at low frequencies of <250Hz. A
+ lower-than-expected BPM figure may be reported for music with uneven or
+ complex bass patterns. </li>
+</ul>
+<h3>4.2. SoundStretch usage examples </h3>
+<p><strong>Example 1</strong></p>
+<p>The following command increases tempo of
+the sound file "originalfile.wav" by 12.5% and stores result to file "destinationfile.wav":</p>
+<blockquote>
+ <pre>soundstretch originalfile.wav destinationfile.wav -tempo=12.5</pre>
+</blockquote>
+<p><strong>Example 2</strong></p>
+<p>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":</p>
+<blockquote>
+ <pre>soundstretch orig.wav dest.wav -pitch=-2</pre>
+</blockquote>
+<p><strong>Example 3</strong></p>
+<p>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:</p>
+<blockquote>
+ <pre>soundstretch orig.wav stdout -tempo=-25.3 -pitch=1.5</pre>
+</blockquote>
+<p><strong>Example 4</strong></p>
+<p>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":</p>
+<blockquote>
+ <pre>soundstretch orig.wav dest.wav -bpm=100</pre>
+</blockquote>
+<p><strong>Example 5</strong></p>
+<p>The following command reads .wav sound data from standard input pipe and
+estimates the BPM rate:</p>
+<blockquote>
+ <pre>soundstretch stdin -bpm</pre>
+</blockquote>
+<hr>
+<h2>5. Change History</h2>
+<h3>5.1. SoundTouch library Change History </h3>
+
+<p><strong>1.5.0:</strong></p>
+<ul>
+<li>Added normalization to correlation calculation and improvement automatic seek/sequence parameter calculation to improve sound quality</li>
+
+<li>Bugfixes:
+ <ul>
+ <li>Fixed negative array indexing in quick seek algorithm</li>
+ <li>FIR autoalias filter running too far in processing buffer</li>
+ <li>Check against zero sample count in rate transposing</li>
+ <li>Fix for x86-64 support: Removed pop/push instructions from the cpu detection algorithm. </li>
+ <li>Check against empty buffers in FIFOSampleBuffer</li>
+ <li>Other minor fixes & code cleanup</li>
+ </ul>
+</li>
+
+<li>Fixes in compilation scripts for non-Intel platforms</li>
+<li>Added Dynamic-Link-Library (DLL) version of SoundTouch library build,
+ provided with Delphi/Pascal wrapper for calling the dll routines</li>
+<li>Added #define PREVENT_CLICK_AT_RATE_CROSSOVER that prevents a click artifact
+ when crossing the nominal pitch from either positive to negative side or vice
+ versa</li>
+
+</ul>
+
+<p><strong>1.4.1:</strong></p>
+<ul>
+<li>Fixed a buffer overflow bug in BPM detect algorithm routines if processing
+ more than 2048 samples at one call </li>
+
+</ul>
+
+<p><strong>1.4.0:</strong></p>
+<ul>
+<li>Improved sound quality by automatic calculation of time stretch algorithm
+ processing parameters according to tempo setting</li>
+<li>Moved BPM detection routines from SoundStretch application into SoundTouch
+ library</li>
+<li>Bugfixes: Usage of uninitialied variables, GNU build scripts, compiler errors
+ due to 'const' keyword mismatch.</li>
+<li>Source code cleanup</li>
+
+</ul>
+
+<p><strong>v1.3.1:
+</strong></p>
+<ul>
+<li>Changed static class declaration to GCC 4.x compiler compatible syntax.</li>
+<li>Enabled MMX/SSE-optimized routines also for GCC compilers. Earlier
+the MMX/SSE-optimized routines were written in compiler-specific inline
+assembler, now these routines are migrated to use compiler intrinsic
+syntax which allows compiling the same MMX/SSE-optimized source code with
+both Visual C++ and GCC compilers. </li>
+<li>Set floating point as the default sample format and added switch to
+the GNU configure script for selecting the other sample format.</li>
+
+</ul>
+
+<p><strong>v1.3.0:
+</strong></p>
+<ul>
+ <li>Fixed tempo routine output duration inaccuracy due to rounding
+error </li>
+ <li>Implemented separate processing routines for integer and
+floating arithmetic to allow improvements to floating point routines
+(earlier used algorithms mostly optimized for integer arithmetic also
+for floating point samples) </li>
+ <li>Fixed a bug that distorts sound if sample rate changes during the
+sound stream </li>
+ <li>Fixed a memory leak that appeared in MMX/SSE/3DNow! optimized
+routines </li>
+ <li>Reduced redundant code pieces in MMX/SSE/3DNow! optimized
+routines vs. the standard C routines.</li>
+ <li>MMX routine incompatibility with new gcc compiler versions </li>
+ <li>Other miscellaneous bug fixes </li>
+</ul>
+<p><strong>v1.2.1: </strong></p>
+<ul>
+ <li>Added automake/autoconf scripts for GNU
+platforms (in courtesy of David Durham)</li>
+ <li>Fixed SCALE overflow bug in rate transposer
+routine.</li>
+ <li>Fixed 64bit address space bugs.</li>
+ <li>Created a 'soundtouch' namespace for
+SAMPLETYPE definitions.</li>
+</ul>
+<p><strong>v1.2.0: </strong></p>
+<ul>
+ <li>Added support for 32bit floating point sample
+data type with SSE/3DNow! optimizations for Win32 platform (SSE/3DNow! optimizations currently not supported in GCC environment)</li>
+ <li>Replaced 'make-gcc' script for GNU environment
+by master Makefile</li>
+ <li>Added time-stretch routine configurability to
+SoundTouch main class</li>
+ <li>Bugfixes</li>
+</ul>
+<p><strong>v1.1.1: </strong></p>
+<ul>
+ <li>Moved SoundTouch under lesser GPL license (LGPL). This allows using SoundTouch library in programs that aren't
+released under GPL license. </li>
+ <li>Changed MMX routine organiation so that MMX optimized routines are now implemented in classes that are derived from
+the basic classes having the standard non-mmx routines. </li>
+ <li>MMX routines to support gcc version 3. </li>
+ <li>Replaced windows makefiles by script using the .dsw files </li>
+</ul>
+<p><strong>v1.01: </strong></p>
+<ul>
+ <li>"mmx_gcc.cpp": Added "using namespace std" and
+removed "return 0" from a function with void return value to fix
+compiler errors when compiling the library in Solaris environment. </li>
+ <li>Moved file "FIFOSampleBuffer.h" to "include"
+directory to allow accessing the FIFOSampleBuffer class from external
+files. </li>
+</ul>
+<p><strong>v1.0: </strong></p>
+<ul>
+ <li>Initial release </li>
+</ul>
+<p> </p>
+<h3>5.2. SoundStretch application Change
+History </h3>
+
+<p><strong>1.4.0:</strong></p>
+<ul>
+<li>Moved BPM detection routines from SoundStretch application into SoundTouch
+ library</li>
+<li>Allow using standard input/output pipes as audio processing input/output
+ streams</li>
+
+</ul>
+
+<p><strong>v1.3.0:</strong></p>
+<ul>
+ <li>Simplified accessing WAV files with floating
+point sample format.
+ </li>
+</ul>
+<p><strong>v1.2.1: </strong></p>
+<ul>
+ <li>Fixed 64bit address space bugs.</li>
+</ul>
+<p><strong>v1.2.0: </strong></p>
+<ul>
+ <li>Added support for 32bit floating point sample
+data type</li>
+ <li>Restructured the BPM routines into separate
+library</li>
+ <li>Fixed big-endian conversion bugs in WAV file
+routines (hopefully :)</li>
+</ul>
+<p><strong>v1.1.1: </strong></p>
+<ul>
+ <li>Fixed bugs in WAV file reading & added
+byte-order conversion for big-endian processors. </li>
+ <li>Moved SoundStretch source code under 'example'
+directory to highlight difference from SoundTouch stuff. </li>
+ <li>Replaced windows makefiles by script using the .dsw files </li>
+ <li>Output file name isn't required if output
+isn't desired (e.g. if using the switch '-bpm' in plain format only) </li>
+</ul>
+<p><strong>v1.1:</strong></p>
+<ul>
+ <li>Fixed "Release" settings in Microsoft Visual
+C++ project file (.dsp) </li>
+ <li>Added beats-per-minute (BPM) detection routine
+and command-line switch "-bpm" </li>
+</ul>
+<p><strong>v1.01: </strong></p>
+<ul>
+ <li>Initial release </li>
+</ul>
+<hr>
+<h2 >6. Acknowledgements </h2>
+<p >Kudos for these people who have contributed to development or submitted
+bugfixes since
+SoundTouch v1.3.1: </p>
+<ul>
+ <li>Arthur A</li>
+ <li>Richard Ash</li>
+ <li>Stanislav Brabec</li>
+ <li>Christian Budde</li>
+ <li>Brian Cameron</li>
+ <li>Jason Champion</li>
+ <li>Patrick Colis</li>
+ <li>Justin Frankel</li>
+ <li>Jason Garland</li>
+ <li>Takashi Iwai</li>
+ <li>Paulo Pizarro</li>
+ <li>RJ Ryan</li>
+ <li>John Sheehy</li>
+</ul>
+<p >Moral greetings to all other contributors and users also!</p>
+<hr>
+<h2 >7. LICENSE </h2>
+<p>SoundTouch audio processing library<br>
+Copyright (c) Olli Parviainen</p>
+<p>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.</p>
+<p>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.</p>
+<p>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</p>
+<hr>
+<!--
+$Id: README.html 81 2009-12-28 20:51:18Z oparviai $
+-->
+</body>
+</html>
diff --git a/plugins/dsp_soundtouch/soundtouch/include/BPMDetect.h b/plugins/dsp_soundtouch/soundtouch/include/BPMDetect.h new file mode 100644 index 00000000..4def43f1 --- /dev/null +++ b/plugins/dsp_soundtouch/soundtouch/include/BPMDetect.h @@ -0,0 +1,161 @@ +////////////////////////////////////////////////////////////////////////////////
+///
+/// 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
+///
+////////////////////////////////////////////////////////////////////////////////
+//
+// Last changed : $Date: 2009-02-21 18:00:14 +0200 (Sat, 21 Feb 2009) $
+// File revision : $Revision: 4 $
+//
+// $Id: BPMDetect.h 63 2009-02-21 16:00:14Z oparviai $
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// 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 "STTypes.h"
+#include "FIFOSampleBuffer.h"
+
+namespace soundtouch
+{
+
+/// Minimum allowed BPM rate. Used to restrict accepted result above a reasonable limit.
+#define MIN_BPM 29
+
+/// Maximum allowed BPM rate. Used to restrict accepted result below a reasonable limit.
+#define MAX_BPM 230
+
+
+/// Class for calculating BPM rate for audio data.
+class BPMDetect
+{
+protected:
+ /// Auto-correlation accumulator bins.
+ float *xcorr;
+
+ /// Amplitude envelope sliding average approximation level accumulator
+ float envelopeAccu;
+
+ /// RMS volume sliding average approximation level accumulator
+ float RMSVolumeAccu;
+
+ /// 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;
+
+ /// FIFO-buffer for decimated processing samples.
+ soundtouch::FIFOSampleBuffer *buffer;
+
+ /// 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
+ );
+
+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();
+};
+
+}
+
+#endif // _BPMDetect_H_
diff --git a/plugins/dsp_soundtouch/soundtouch/include/FIFOSampleBuffer.h b/plugins/dsp_soundtouch/soundtouch/include/FIFOSampleBuffer.h new file mode 100644 index 00000000..76cbf951 --- /dev/null +++ b/plugins/dsp_soundtouch/soundtouch/include/FIFOSampleBuffer.h @@ -0,0 +1,174 @@ +////////////////////////////////////////////////////////////////////////////////
+///
+/// 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
+///
+////////////////////////////////////////////////////////////////////////////////
+//
+// Last changed : $Date: 2009-02-21 18:00:14 +0200 (Sat, 21 Feb 2009) $
+// File revision : $Revision: 4 $
+//
+// $Id: FIFOSampleBuffer.h 63 2009-02-21 16:00:14Z oparviai $
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// 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 succesfully 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);
+
+ /// Returns nonzero if there aren't any samples available for outputting.
+ virtual int isEmpty() const;
+
+ /// Clears all the samples.
+ virtual void clear();
+};
+
+}
+
+#endif
diff --git a/plugins/dsp_soundtouch/soundtouch/include/FIFOSamplePipe.h b/plugins/dsp_soundtouch/soundtouch/include/FIFOSamplePipe.h new file mode 100644 index 00000000..b5fc3b77 --- /dev/null +++ b/plugins/dsp_soundtouch/soundtouch/include/FIFOSamplePipe.h @@ -0,0 +1,221 @@ +////////////////////////////////////////////////////////////////////////////////
+///
+/// '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
+///
+////////////////////////////////////////////////////////////////////////////////
+//
+// Last changed : $Date: 2009-04-13 16:18:48 +0300 (Mon, 13 Apr 2009) $
+// File revision : $Revision: 4 $
+//
+// $Id: FIFOSamplePipe.h 69 2009-04-13 13:18:48Z oparviai $
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// 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 <assert.h>
+#include <stdlib.h>
+#include "STTypes.h"
+
+namespace soundtouch
+{
+
+/// Abstract base class for FIFO (first-in-first-out) sample processing classes.
+class FIFOSamplePipe
+{
+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;
+};
+
+
+
+/// 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();
+ }
+};
+
+}
+
+#endif
diff --git a/plugins/dsp_soundtouch/soundtouch/include/STTypes.h b/plugins/dsp_soundtouch/soundtouch/include/STTypes.h new file mode 100644 index 00000000..c09a45f9 --- /dev/null +++ b/plugins/dsp_soundtouch/soundtouch/include/STTypes.h @@ -0,0 +1,149 @@ +////////////////////////////////////////////////////////////////////////////////
+///
+/// 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
+///
+////////////////////////////////////////////////////////////////////////////////
+//
+// Last changed : $Date: 2009-05-17 14:30:57 +0300 (Sun, 17 May 2009) $
+// File revision : $Revision: 3 $
+//
+// $Id: STTypes.h 70 2009-05-17 11:30:57Z oparviai $
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// 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;
+
+#ifdef __GNUC__
+ // In GCC, include soundtouch_config.h made by config scritps
+ #include "soundtouch_config.h"
+#endif
+
+#ifndef _WINDEF_
+ // if these aren't defined already by Windows headers, define now
+
+ typedef int BOOL;
+
+ #define FALSE 0
+ #define TRUE 1
+
+#endif // _WINDEF_
+
+
+namespace soundtouch
+{
+
+/// Activate these undef's to overrule the possible sampletype
+/// setting inherited from some other header file:
+//#undef INTEGER_SAMPLES
+//#undef FLOAT_SAMPLES
+
+#if !(INTEGER_SAMPLES || 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 INTEGER_SAMPLES 1 //< 16bit integer samples
+ #define FLOAT_SAMPLES 1 //< 32bit float samples
+
+ #endif
+
+ #if (WIN32 || __i386__ || __x86_64__)
+ /// 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 ALLOW_X86_OPTIMIZATIONS 0
+
+ #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 ALLOW_NONEXACT_SIMD_OPTIMIZATION 1
+
+
+ #ifdef 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 FLOAT_SAMPLES
+ // check that only one sample type is defined
+ #error "conflicting sample types defined"
+ #endif // FLOAT_SAMPLES
+
+ #ifdef ALLOW_X86_OPTIMIZATIONS
+ // Allow MMX optimizations
+ #define ALLOW_MMX 1
+ #endif
+
+ #else
+
+ // floating point samples
+ typedef float SAMPLETYPE;
+ // data type for sample accumulation: Use double to utilize full precision.
+ typedef double LONG_SAMPLETYPE;
+
+ #ifdef ALLOW_X86_OPTIMIZATIONS
+ // Allow 3DNow! and SSE optimizations
+ #if WIN32
+ #define ALLOW_3DNOW 1
+ #endif
+
+ #define ALLOW_SSE 1
+ #endif
+
+ #endif // INTEGER_SAMPLES
+};
+
+
+// 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 PREVENT_CLICK_AT_RATE_CROSSOVER 1
+
+#endif
diff --git a/plugins/dsp_soundtouch/soundtouch/include/SoundTouch.h b/plugins/dsp_soundtouch/soundtouch/include/SoundTouch.h new file mode 100644 index 00000000..0e042d3c --- /dev/null +++ b/plugins/dsp_soundtouch/soundtouch/include/SoundTouch.h @@ -0,0 +1,252 @@ +//////////////////////////////////////////////////////////////////////////////
+///
+/// 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
+///
+////////////////////////////////////////////////////////////////////////////////
+//
+// Last changed : $Date: 2009-12-28 22:10:14 +0200 (Mon, 28 Dec 2009) $
+// File revision : $Revision: 4 $
+//
+// $Id: SoundTouch.h 78 2009-12-28 20:10:14Z oparviai $
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// 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 "1.5.0"
+
+/// SoundTouch library version id
+#define SOUNDTOUCH_VERSION_ID (10500)
+
+//
+// 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
+
+
+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.
+ float virtualRate;
+
+ /// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.
+ float virtualTempo;
+
+ /// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.
+ float virtualPitch;
+
+ /// Flag: Has sample rate been set?
+ BOOL bSrateSet;
+
+ /// 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'
+ float rate;
+
+ /// Effective 'tempo' value calculated from 'virtualRate', 'virtualTempo' and 'virtualPitch'
+ float 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(float newRate);
+
+ /// Sets new tempo control value. Normal tempo = 1.0, smaller values
+ /// represent slower tempo, larger faster tempo.
+ void setTempo(float newTempo);
+
+ /// Sets new rate control value as a difference in percents compared
+ /// to the original rate (-50 .. +100 %)
+ void setRateChange(float newRate);
+
+ /// Sets new tempo control value as a difference in percents compared
+ /// to the original tempo (-50 .. +100 %)
+ void setTempoChange(float newTempo);
+
+ /// Sets new pitch control value. Original pitch = 1.0, smaller values
+ /// represent lower pitches, larger values higher pitch.
+ void setPitch(float newPitch);
+
+ /// Sets pitch change in octaves compared to the original pitch
+ /// (-1.00 .. +1.00)
+ void setPitchOctaves(float newPitch);
+
+ /// Sets pitch change in semi-tones compared to the original pitch
+ /// (-12 .. +12)
+ void setPitchSemiTones(int newPitch);
+ void setPitchSemiTones(float newPitch);
+
+ /// Sets the number of channels, 1 = mono, 2 = stereo
+ void setChannels(uint numChannels);
+
+ /// Sets sample rate.
+ void setSampleRate(uint srate);
+
+ /// 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.
+ );
+
+ /// 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 succesfully 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;
+
+
+ /// 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/plugins/dsp_soundtouch/soundtouch/include/soundtouch_config.h b/plugins/dsp_soundtouch/soundtouch/include/soundtouch_config.h new file mode 100644 index 00000000..4097691b --- /dev/null +++ b/plugins/dsp_soundtouch/soundtouch/include/soundtouch_config.h @@ -0,0 +1,88 @@ +/* include/soundtouch_config.h. Generated from soundtouch_config.h.in by configure. */ +/* include/soundtouch_config.h.in. Generated from configure.ac by autoheader. */ + +/* Use Float as Sample type */ +#define FLOAT_SAMPLES 1 + +/* Define to 1 if you have the <dlfcn.h> header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you have the <inttypes.h> header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `m' library (-lm). */ +#define HAVE_LIBM 1 + +/* Define to 1 if your system has a GNU libc compatible `malloc' function, and + to 0 otherwise. */ +#define HAVE_MALLOC 1 + +/* Define to 1 if you have the <memory.h> header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the <stdint.h> header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the <stdlib.h> header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the <strings.h> header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the <string.h> header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the <sys/types.h> header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the <unistd.h> header file. */ +#define HAVE_UNISTD_H 1 + +/* Use Integer as Sample type */ +/* #undef INTEGER_SAMPLES */ + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#define LT_OBJDIR ".libs/" + +/* Name of package */ +#define PACKAGE "soundtouch" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "http://www.surina.net/soundtouch" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "SoundTouch" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "SoundTouch 1.4.0" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "soundtouch" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "1.4.0" + +/* Define as the return type of signal handlers (`int' or `void'). */ +#define RETSIGTYPE void + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Version number of package */ +#define VERSION "1.4.0" + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +/* #undef inline */ +#endif + +/* Define to rpl_malloc if the replacement function should be used. */ +/* #undef malloc */ diff --git a/plugins/dsp_soundtouch/soundtouch/source/SoundTouch/3dnow_win.cpp b/plugins/dsp_soundtouch/soundtouch/source/SoundTouch/3dnow_win.cpp new file mode 100644 index 00000000..f0a9d7ec --- /dev/null +++ b/plugins/dsp_soundtouch/soundtouch/source/SoundTouch/3dnow_win.cpp @@ -0,0 +1,349 @@ +////////////////////////////////////////////////////////////////////////////////
+///
+/// Win32 version of the AMD 3DNow! optimized routines for AMD K6-2/Athlon
+/// processors. All 3DNow! optimized functions have been gathered into this
+/// single source code file, regardless to their class or original source code
+/// file, in order to ease porting the library to other compiler and processor
+/// platforms.
+///
+/// By the way; the performance gain depends heavily on the CPU generation: On
+/// K6-2 these routines provided speed-up of even 2.4 times, while on Athlon the
+/// difference to the original routines stayed at unremarkable 8%! Such a small
+/// improvement on Athlon is due to 3DNow can perform only two operations in
+/// parallel, and obviously also the Athlon FPU is doing a very good job with
+/// the standard C floating point routines! Here these routines are anyway,
+/// although it might not be worth the effort to convert these to GCC platform,
+/// for Athlon CPU at least. The situation is different regarding the SSE
+/// optimizations though, thanks to the four parallel operations of SSE that
+/// already make a difference.
+///
+/// This file is to be compiled in Windows platform with Microsoft Visual C++
+/// Compiler. Please see '3dnow_gcc.cpp' for the gcc compiler version for all
+/// GNU platforms (if file supplied).
+///
+/// NOTICE: If using Visual Studio 6.0, you'll need to install the "Visual C++
+/// 6.0 processor pack" update to support 3DNow! instruction set. The update is
+/// available for download at Microsoft Developers Network, see here:
+/// http://msdn.microsoft.com/en-us/vstudio/aa718349.aspx
+///
+/// If the above URL is expired or removed, go to "http://msdn.microsoft.com" and
+/// perform a search with keywords "processor pack".
+///
+/// Author : Copyright (c) Olli Parviainen
+/// Author e-mail : oparviai 'at' iki.fi
+/// SoundTouch WWW: http://www.surina.net/soundtouch
+///
+////////////////////////////////////////////////////////////////////////////////
+//
+// Last changed : $Date: 2009-02-21 18:00:14 +0200 (Sat, 21 Feb 2009) $
+// File revision : $Revision: 4 $
+//
+// $Id: 3dnow_win.cpp 63 2009-02-21 16:00:14Z oparviai $
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// 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
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#include "cpu_detect.h"
+#include "STTypes.h"
+
+#ifndef WIN32
+#error "wrong platform - this source code file is exclusively for Win32 platform"
+#endif
+
+using namespace soundtouch;
+
+#ifdef ALLOW_3DNOW
+// 3DNow! routines available only with float sample type
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// implementation of 3DNow! optimized functions of class 'TDStretch3DNow'
+//
+//////////////////////////////////////////////////////////////////////////////
+
+#include "TDStretch.h"
+
+
+// Calculates cross correlation of two buffers
+double TDStretch3DNow::calcCrossCorrStereo(const float *pV1, const float *pV2) const
+{
+ int overlapLengthLocal = overlapLength;
+ float corr = 0;
+
+ // Calculates the cross-correlation value between 'pV1' and 'pV2' vectors
+ /*
+ c-pseudocode:
+
+ corr = 0;
+ for (i = 0; i < overlapLength / 4; i ++)
+ {
+ corr += pV1[0] * pV2[0];
+ pV1[1] * pV2[1];
+ pV1[2] * pV2[2];
+ pV1[3] * pV2[3];
+ pV1[4] * pV2[4];
+ pV1[5] * pV2[5];
+ pV1[6] * pV2[6];
+ pV1[7] * pV2[7];
+
+ pV1 += 8;
+ pV2 += 8;
+ }
+ */
+
+ _asm
+ {
+ // give prefetch hints to CPU of what data are to be needed soonish.
+ // give more aggressive hints on pV1 as that changes more between different calls
+ // while pV2 stays the same.
+ prefetch [pV1]
+ prefetch [pV2]
+ prefetch [pV1 + 32]
+
+ mov eax, dword ptr pV2
+ mov ebx, dword ptr pV1
+
+ pxor mm0, mm0
+
+ mov ecx, overlapLengthLocal
+ shr ecx, 2 // div by four
+
+ loop1:
+ movq mm1, [eax]
+ prefetch [eax + 32] // give a prefetch hint to CPU what data are to be needed soonish
+ pfmul mm1, [ebx]
+ prefetch [ebx + 64] // give a prefetch hint to CPU what data are to be needed soonish
+
+ movq mm2, [eax + 8]
+ pfadd mm0, mm1
+ pfmul mm2, [ebx + 8]
+
+ movq mm3, [eax + 16]
+ pfadd mm0, mm2
+ pfmul mm3, [ebx + 16]
+
+ movq mm4, [eax + 24]
+ pfadd mm0, mm3
+ pfmul mm4, [ebx + 24]
+
+ add eax, 32
+ pfadd mm0, mm4
+ add ebx, 32
+
+ dec ecx
+ jnz loop1
+
+ // add halfs of mm0 together and return the result.
+ // note: mm1 is used as a dummy parameter only, we actually don't care about it's value
+ pfacc mm0, mm1
+ movd corr, mm0
+ femms
+ }
+
+ return corr;
+}
+
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// implementation of 3DNow! optimized functions of class 'FIRFilter'
+//
+//////////////////////////////////////////////////////////////////////////////
+
+#include "FIRFilter.h"
+
+FIRFilter3DNow::FIRFilter3DNow() : FIRFilter()
+{
+ filterCoeffsUnalign = NULL;
+ filterCoeffsAlign = NULL;
+}
+
+
+FIRFilter3DNow::~FIRFilter3DNow()
+{
+ delete[] filterCoeffsUnalign;
+ filterCoeffsUnalign = NULL;
+ filterCoeffsAlign = NULL;
+}
+
+
+// (overloaded) Calculates filter coefficients for 3DNow! routine
+void FIRFilter3DNow::setCoefficients(const float *coeffs, uint newLength, uint uResultDivFactor)
+{
+ uint i;
+ float fDivider;
+
+ FIRFilter::setCoefficients(coeffs, newLength, uResultDivFactor);
+
+ // Scale the filter coefficients so that it won't be necessary to scale the filtering result
+ // also rearrange coefficients suitably for 3DNow!
+ // Ensure that filter coeffs array is aligned to 16-byte boundary
+ delete[] filterCoeffsUnalign;
+ filterCoeffsUnalign = new float[2 * newLength + 4];
+ filterCoeffsAlign = (float *)(((uint)filterCoeffsUnalign + 15) & (uint)-16);
+
+ fDivider = (float)resultDivider;
+
+ // rearrange the filter coefficients for mmx routines
+ for (i = 0; i < newLength; i ++)
+ {
+ filterCoeffsAlign[2 * i + 0] =
+ filterCoeffsAlign[2 * i + 1] = coeffs[i + 0] / fDivider;
+ }
+}
+
+
+// 3DNow!-optimized version of the filter routine for stereo sound
+uint FIRFilter3DNow::evaluateFilterStereo(float *dest, const float *src, uint numSamples) const
+{
+ float *filterCoeffsLocal = filterCoeffsAlign;
+ uint count = (numSamples - length) & (uint)-2;
+ uint lengthLocal = length / 4;
+
+ assert(length != 0);
+ assert(count % 2 == 0);
+
+ /* original code:
+
+ double suml1, suml2;
+ double sumr1, sumr2;
+ uint i, j;
+
+ for (j = 0; j < count; j += 2)
+ {
+ const float *ptr;
+
+ suml1 = sumr1 = 0.0;
+ suml2 = sumr2 = 0.0;
+ ptr = src;
+ filterCoeffsLocal = filterCoeffs;
+ for (i = 0; i < lengthLocal; i ++)
+ {
+ // unroll loop for efficiency.
+
+ suml1 += ptr[0] * filterCoeffsLocal[0] +
+ ptr[2] * filterCoeffsLocal[2] +
+ ptr[4] * filterCoeffsLocal[4] +
+ ptr[6] * filterCoeffsLocal[6];
+
+ sumr1 += ptr[1] * filterCoeffsLocal[1] +
+ ptr[3] * filterCoeffsLocal[3] +
+ ptr[5] * filterCoeffsLocal[5] +
+ ptr[7] * filterCoeffsLocal[7];
+
+ suml2 += ptr[8] * filterCoeffsLocal[0] +
+ ptr[10] * filterCoeffsLocal[2] +
+ ptr[12] * filterCoeffsLocal[4] +
+ ptr[14] * filterCoeffsLocal[6];
+
+ sumr2 += ptr[9] * filterCoeffsLocal[1] +
+ ptr[11] * filterCoeffsLocal[3] +
+ ptr[13] * filterCoeffsLocal[5] +
+ ptr[15] * filterCoeffsLocal[7];
+
+ ptr += 16;
+ filterCoeffsLocal += 8;
+ }
+ dest[0] = (float)suml1;
+ dest[1] = (float)sumr1;
+ dest[2] = (float)suml2;
+ dest[3] = (float)sumr2;
+
+ src += 4;
+ dest += 4;
+ }
+
+ */
+ _asm
+ {
+ mov eax, dword ptr dest
+ mov ebx, dword ptr src
+ mov edx, count
+ shr edx, 1
+
+ loop1:
+ // "outer loop" : during each round 2*2 output samples are calculated
+ prefetch [ebx] // give a prefetch hint to CPU what data are to be needed soonish
+ prefetch [filterCoeffsLocal] // give a prefetch hint to CPU what data are to be needed soonish
+
+ mov esi, ebx
+ mov edi, filterCoeffsLocal
+ pxor mm0, mm0
+ pxor mm1, mm1
+ mov ecx, lengthLocal
+
+ loop2:
+ // "inner loop" : during each round four FIR filter taps are evaluated for 2*2 output samples
+ movq mm2, [edi]
+ movq mm3, mm2
+ prefetch [edi + 32] // give a prefetch hint to CPU what data are to be needed soonish
+ pfmul mm2, [esi]
+ prefetch [esi + 32] // give a prefetch hint to CPU what data are to be needed soonish
+ pfmul mm3, [esi + 8]
+
+ movq mm4, [edi + 8]
+ movq mm5, mm4
+ pfadd mm0, mm2
+ pfmul mm4, [esi + 8]
+ pfadd mm1, mm3
+ pfmul mm5, [esi + 16]
+
+ movq mm2, [edi + 16]
+ movq mm6, mm2
+ pfadd mm0, mm4
+ pfmul mm2, [esi + 16]
+ pfadd mm1, mm5
+ pfmul mm6, [esi + 24]
+
+ movq mm3, [edi + 24]
+ movq mm7, mm3
+ pfadd mm0, mm2
+ pfmul mm3, [esi + 24]
+ pfadd mm1, mm6
+ pfmul mm7, [esi + 32]
+ add esi, 32
+ pfadd mm0, mm3
+ add edi, 32
+ pfadd mm1, mm7
+
+ dec ecx
+ jnz loop2
+
+ movq [eax], mm0
+ add ebx, 16
+ movq [eax + 8], mm1
+ add eax, 16
+
+ dec edx
+ jnz loop1
+
+ femms
+ }
+
+ return count;
+}
+
+
+#endif // ALLOW_3DNOW
diff --git a/plugins/dsp_soundtouch/soundtouch/source/SoundTouch/AAFilter.cpp b/plugins/dsp_soundtouch/soundtouch/source/SoundTouch/AAFilter.cpp new file mode 100644 index 00000000..96abda49 --- /dev/null +++ b/plugins/dsp_soundtouch/soundtouch/source/SoundTouch/AAFilter.cpp @@ -0,0 +1,184 @@ +////////////////////////////////////////////////////////////////////////////////
+///
+/// FIR low-pass (anti-alias) filter with filter coefficient design routine and
+/// MMX optimization.
+///
+/// Anti-alias filter is used to prevent folding of high frequencies when
+/// transposing the sample rate with interpolation.
+///
+/// Author : Copyright (c) Olli Parviainen
+/// Author e-mail : oparviai 'at' iki.fi
+/// SoundTouch WWW: http://www.surina.net/soundtouch
+///
+////////////////////////////////////////////////////////////////////////////////
+//
+// Last changed : $Date: 2009-01-11 13:34:24 +0200 (Sun, 11 Jan 2009) $
+// File revision : $Revision: 4 $
+//
+// $Id: AAFilter.cpp 45 2009-01-11 11:34:24Z oparviai $
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// 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
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#include <memory.h>
+#include <assert.h>
+#include <math.h>
+#include <stdlib.h>
+#include "AAFilter.h"
+#include "FIRFilter.h"
+
+using namespace soundtouch;
+
+#define PI 3.141592655357989
+#define TWOPI (2 * PI)
+
+/*****************************************************************************
+ *
+ * Implementation of the class 'AAFilter'
+ *
+ *****************************************************************************/
+
+AAFilter::AAFilter(uint len)
+{
+ pFIR = FIRFilter::newInstance();
+ cutoffFreq = 0.5;
+ setLength(len);
+}
+
+
+
+AAFilter::~AAFilter()
+{
+ delete pFIR;
+}
+
+
+
+// Sets new anti-alias filter cut-off edge frequency, scaled to
+// sampling frequency (nyquist frequency = 0.5).
+// The filter will cut frequencies higher than the given frequency.
+void AAFilter::setCutoffFreq(double newCutoffFreq)
+{
+ cutoffFreq = newCutoffFreq;
+ calculateCoeffs();
+}
+
+
+
+// Sets number of FIR filter taps
+void AAFilter::setLength(uint newLength)
+{
+ length = newLength;
+ calculateCoeffs();
+}
+
+
+
+// Calculates coefficients for a low-pass FIR filter using Hamming window
+void AAFilter::calculateCoeffs()
+{
+ uint i;
+ double cntTemp, temp, tempCoeff,h, w;
+ double fc2, wc;
+ double scaleCoeff, sum;
+ double *work;
+ SAMPLETYPE *coeffs;
+
+ assert(length >= 2);
+ assert(length % 4 == 0);
+ assert(cutoffFreq >= 0);
+ assert(cutoffFreq <= 0.5);
+
+ work = new double[length];
+ coeffs = new SAMPLETYPE[length];
+
+ fc2 = 2.0 * cutoffFreq;
+ wc = PI * fc2;
+ tempCoeff = TWOPI / (double)length;
+
+ sum = 0;
+ for (i = 0; i < length; i ++)
+ {
+ cntTemp = (double)i - (double)(length / 2);
+
+ temp = cntTemp * wc;
+ if (temp != 0)
+ {
+ h = fc2 * sin(temp) / temp; // sinc function
+ }
+ else
+ {
+ h = 1.0;
+ }
+ w = 0.54 + 0.46 * cos(tempCoeff * cntTemp); // hamming window
+
+ temp = w * h;
+ work[i] = temp;
+
+ // calc net sum of coefficients
+ sum += temp;
+ }
+
+ // ensure the sum of coefficients is larger than zero
+ assert(sum > 0);
+
+ // ensure we've really designed a lowpass filter...
+ assert(work[length/2] > 0);
+ assert(work[length/2 + 1] > -1e-6);
+ assert(work[length/2 - 1] > -1e-6);
+
+ // Calculate a scaling coefficient in such a way that the result can be
+ // divided by 16384
+ scaleCoeff = 16384.0f / sum;
+
+ for (i = 0; i < length; i ++)
+ {
+ // scale & round to nearest integer
+ temp = work[i] * scaleCoeff;
+ temp += (temp >= 0) ? 0.5 : -0.5;
+ // ensure no overfloods
+ assert(temp >= -32768 && temp <= 32767);
+ coeffs[i] = (SAMPLETYPE)temp;
+ }
+
+ // Set coefficients. Use divide factor 14 => divide result by 2^14 = 16384
+ pFIR->setCoefficients(coeffs, length, 14);
+
+ delete[] work;
+ delete[] coeffs;
+}
+
+
+// Applies the filter to the given sequence of samples.
+// Note : The amount of outputted samples is by value of 'filter length'
+// smaller than the amount of input samples.
+uint AAFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels) const
+{
+ return pFIR->evaluate(dest, src, numSamples, numChannels);
+}
+
+
+uint AAFilter::getLength() const
+{
+ return pFIR->getLength();
+}
diff --git a/plugins/dsp_soundtouch/soundtouch/source/SoundTouch/BPMDetect.cpp b/plugins/dsp_soundtouch/soundtouch/source/SoundTouch/BPMDetect.cpp new file mode 100644 index 00000000..405f514b --- /dev/null +++ b/plugins/dsp_soundtouch/soundtouch/source/SoundTouch/BPMDetect.cpp @@ -0,0 +1,308 @@ +////////////////////////////////////////////////////////////////////////////////
+///
+/// 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.
+/// - Inputted 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
+///
+////////////////////////////////////////////////////////////////////////////////
+//
+// Last changed : $Date: 2009-02-21 18:00:14 +0200 (Sat, 21 Feb 2009) $
+// File revision : $Revision: 4 $
+//
+// $Id: BPMDetect.cpp 63 2009-02-21 16:00:14Z oparviai $
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// 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
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#include <math.h>
+#include <assert.h>
+#include <string.h>
+#include "FIFOSampleBuffer.h"
+#include "PeakFinder.h"
+#include "BPMDetect.h"
+
+using namespace soundtouch;
+
+#define INPUT_BLOCK_SAMPLES 2048
+#define DECIMATED_BLOCK_SAMPLES 256
+
+/// decay constant for calculating RMS volume sliding average approximation
+/// (time constant is about 10 sec)
+const float avgdecay = 0.99986f;
+
+/// Normalization coefficient for calculating RMS sliding average approximation.
+const float avgnorm = (1 - avgdecay);
+
+
+
+BPMDetect::BPMDetect(int numChannels, int aSampleRate)
+{
+ this->sampleRate = aSampleRate;
+ this->channels = numChannels;
+
+ decimateSum = 0;
+ decimateCount = 0;
+
+ envelopeAccu = 0;
+
+ // Initialize RMS volume accumulator to RMS level of 3000 (out of 32768) that's
+ // a typical RMS signal level value for song data. This value is then adapted
+ // to the actual level during processing.
+#ifdef INTEGER_SAMPLES
+ // integer samples
+ RMSVolumeAccu = (3000 * 3000) / avgnorm;
+#else
+ // float samples, scaled to range [-1..+1[
+ RMSVolumeAccu = (0.092f * 0.092f) / avgnorm;
+#endif
+
+ // choose decimation factor so that result is approx. 500 Hz
+ decimateBy = sampleRate / 500;
+ assert(decimateBy > 0);
+ assert(INPUT_BLOCK_SAMPLES < decimateBy * DECIMATED_BLOCK_SAMPLES);
+
+ // Calculate window length & starting item according to desired min & max bpms
+ windowLen = (60 * sampleRate) / (decimateBy * MIN_BPM);
+ windowStart = (60 * sampleRate) / (decimateBy * MAX_BPM);
+
+ assert(windowLen > windowStart);
+
+ // allocate new working objects
+ xcorr = new float[windowLen];
+ memset(xcorr, 0, windowLen * sizeof(float));
+
+ // allocate processing buffer
+ buffer = new FIFOSampleBuffer();
+ // we do processing in mono mode
+ buffer->setChannels(1);
+ buffer->clear();
+}
+
+
+
+BPMDetect::~BPMDetect()
+{
+ delete[] xcorr;
+ delete buffer;
+}
+
+
+
+/// convert to mono, low-pass filter & decimate to about 500 Hz.
+/// return number of outputted samples.
+///
+/// Decimation is used to remove the unnecessary frequencies and thus to reduce
+/// the amount of data needed to be processed as calculating autocorrelation
+/// function is a very-very heavy operation.
+///
+/// Anti-alias filtering is done simply by averaging the samples. This is really a
+/// poor-man's anti-alias filtering, but it's not so critical in this kind of application
+/// (it'd also be difficult to design a high-quality filter with steep cut-off at very
+/// narrow band)
+int BPMDetect::decimate(SAMPLETYPE *dest, const SAMPLETYPE *src, int numsamples)
+{
+ int count, outcount;
+ LONG_SAMPLETYPE out;
+
+ assert(channels > 0);
+ assert(decimateBy > 0);
+ outcount = 0;
+ for (count = 0; count < numsamples; count ++)
+ {
+ int j;
+
+ // convert to mono and accumulate
+ for (j = 0; j < channels; j ++)
+ {
+ decimateSum += src[j];
+ }
+ src += j;
+
+ decimateCount ++;
+ if (decimateCount >= decimateBy)
+ {
+ // Store every Nth sample only
+ out = (LONG_SAMPLETYPE)(decimateSum / (decimateBy * channels));
+ decimateSum = 0;
+ decimateCount = 0;
+#ifdef INTEGER_SAMPLES
+ // check ranges for sure (shouldn't actually be necessary)
+ if (out > 32767)
+ {
+ out = 32767;
+ }
+ else if (out < -32768)
+ {
+ out = -32768;
+ }
+#endif // INTEGER_SAMPLES
+ dest[outcount] = (SAMPLETYPE)out;
+ outcount ++;
+ }
+ }
+ return outcount;
+}
+
+
+
+// Calculates autocorrelation function of the sample history buffer
+void BPMDetect::updateXCorr(int process_samples)
+{
+ int offs;
+ SAMPLETYPE *pBuffer;
+
+ assert(buffer->numSamples() >= (uint)(process_samples + windowLen));
+
+ pBuffer = buffer->ptrBegin();
+ for (offs = windowStart; offs < windowLen; offs ++)
+ {
+ LONG_SAMPLETYPE sum;
+ int i;
+
+ sum = 0;
+ for (i = 0; i < process_samples; i ++)
+ {
+ sum += pBuffer[i] * pBuffer[i + offs]; // scaling the sub-result shouldn't be necessary
+ }
+// xcorr[offs] *= xcorr_decay; // decay 'xcorr' here with suitable coefficients
+ // if it's desired that the system adapts automatically to
+ // various bpms, e.g. in processing continouos music stream.
+ // The 'xcorr_decay' should be a value that's smaller than but
+ // close to one, and should also depend on 'process_samples' value.
+
+ xcorr[offs] += (float)sum;
+ }
+}
+
+
+
+// Calculates envelope of the sample data
+void BPMDetect::calcEnvelope(SAMPLETYPE *samples, int numsamples)
+{
+ const float decay = 0.7f; // decay constant for smoothing the envelope
+ const float norm = (1 - decay);
+
+ int i;
+ LONG_SAMPLETYPE out;
+ float val;
+
+ for (i = 0; i < numsamples; i ++)
+ {
+ // calc average RMS volume
+ RMSVolumeAccu *= avgdecay;
+ val = (float)fabs((float)samples[i]);
+ RMSVolumeAccu += val * val;
+
+ // cut amplitudes that are below 2 times average RMS volume
+ // (we're interested in peak values, not the silent moments)
+ val -= 2 * (float)sqrt(RMSVolumeAccu * avgnorm);
+ val = (val > 0) ? val : 0;
+
+ // smooth amplitude envelope
+ envelopeAccu *= decay;
+ envelopeAccu += val;
+ out = (LONG_SAMPLETYPE)(envelopeAccu * norm);
+
+#ifdef INTEGER_SAMPLES
+ // cut peaks (shouldn't be necessary though)
+ if (out > 32767) out = 32767;
+#endif // INTEGER_SAMPLES
+ samples[i] = (SAMPLETYPE)out;
+ }
+}
+
+
+
+void BPMDetect::inputSamples(const SAMPLETYPE *samples, int numSamples)
+{
+ SAMPLETYPE decimated[DECIMATED_BLOCK_SAMPLES];
+
+ // iterate so that max INPUT_BLOCK_SAMPLES processed per iteration
+ while (numSamples > 0)
+ {
+ int block;
+ int decSamples;
+
+ block = (numSamples > INPUT_BLOCK_SAMPLES) ? INPUT_BLOCK_SAMPLES : numSamples;
+
+ // decimate. note that converts to mono at the same time
+ decSamples = decimate(decimated, samples, block);
+ samples += block * channels;
+ numSamples -= block;
+
+ // envelope new samples and add them to buffer
+ calcEnvelope(decimated, decSamples);
+ buffer->putSamples(decimated, decSamples);
+ }
+
+ // when the buffer has enought samples for processing...
+ if ((int)buffer->numSamples() > windowLen)
+ {
+ int processLength;
+
+ // how many samples are processed
+ processLength = (int)buffer->numSamples() - windowLen;
+
+ // ... calculate autocorrelations for oldest samples...
+ updateXCorr(processLength);
+ // ... and remove them from the buffer
+ buffer->receiveSamples(processLength);
+ }
+}
+
+
+
+float BPMDetect::getBpm()
+{
+ double peakPos;
+ PeakFinder peakFinder;
+
+ // find peak position
+ peakPos = peakFinder.detectPeak(xcorr, windowStart, windowLen);
+
+ assert(decimateBy != 0);
+ if (peakPos < 1e-6) return 0.0; // detection failed.
+
+ // calculate BPM
+ return (float)(60.0 * (((double)sampleRate / (double)decimateBy) / peakPos));
+}
diff --git a/plugins/dsp_soundtouch/soundtouch/source/SoundTouch/FIFOSampleBuffer.cpp b/plugins/dsp_soundtouch/soundtouch/source/SoundTouch/FIFOSampleBuffer.cpp new file mode 100644 index 00000000..01f64b08 --- /dev/null +++ b/plugins/dsp_soundtouch/soundtouch/source/SoundTouch/FIFOSampleBuffer.cpp @@ -0,0 +1,262 @@ +////////////////////////////////////////////////////////////////////////////////
+///
+/// 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
+/// outputted samples from the buffer, as well as grows the buffer size
+/// whenever necessary.
+///
+/// Author : Copyright (c) Olli Parviainen
+/// Author e-mail : oparviai 'at' iki.fi
+/// SoundTouch WWW: http://www.surina.net/soundtouch
+///
+////////////////////////////////////////////////////////////////////////////////
+//
+// Last changed : $Date: 2009-02-27 19:24:42 +0200 (Fri, 27 Feb 2009) $
+// File revision : $Revision: 4 $
+//
+// $Id: FIFOSampleBuffer.cpp 68 2009-02-27 17:24:42Z oparviai $
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// 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
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#include <stdlib.h>
+#include <memory.h>
+#include <string.h>
+#include <assert.h>
+#include <stdexcept>
+
+#include "FIFOSampleBuffer.h"
+
+using namespace soundtouch;
+
+// Constructor
+FIFOSampleBuffer::FIFOSampleBuffer(int numChannels)
+{
+ assert(numChannels > 0);
+ sizeInBytes = 0; // reasonable initial value
+ buffer = NULL;
+ bufferUnaligned = NULL;
+ samplesInBuffer = 0;
+ bufferPos = 0;
+ channels = (uint)numChannels;
+ ensureCapacity(32); // allocate initial capacity
+}
+
+
+// destructor
+FIFOSampleBuffer::~FIFOSampleBuffer()
+{
+ delete[] bufferUnaligned;
+ bufferUnaligned = NULL;
+ buffer = NULL;
+}
+
+
+// Sets number of channels, 1 = mono, 2 = stereo
+void FIFOSampleBuffer::setChannels(int numChannels)
+{
+ uint usedBytes;
+
+ assert(numChannels > 0);
+ usedBytes = channels * samplesInBuffer;
+ channels = (uint)numChannels;
+ samplesInBuffer = usedBytes / channels;
+}
+
+
+// if output location pointer 'bufferPos' isn't zero, 'rewinds' the buffer and
+// zeroes this pointer by copying samples from the 'bufferPos' pointer
+// location on to the beginning of the buffer.
+void FIFOSampleBuffer::rewind()
+{
+ if (buffer && bufferPos)
+ {
+ memmove(buffer, ptrBegin(), sizeof(SAMPLETYPE) * channels * samplesInBuffer);
+ bufferPos = 0;
+ }
+}
+
+
+// Adds 'numSamples' pcs of samples from the 'samples' memory position to
+// the sample buffer.
+void FIFOSampleBuffer::putSamples(const SAMPLETYPE *samples, uint nSamples)
+{
+ memcpy(ptrEnd(nSamples), samples, sizeof(SAMPLETYPE) * nSamples * channels);
+ samplesInBuffer += nSamples;
+}
+
+
+// Increases the 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!
+void FIFOSampleBuffer::putSamples(uint nSamples)
+{
+ uint req;
+
+ req = samplesInBuffer + nSamples;
+ ensureCapacity(req);
+ samplesInBuffer += nSamples;
+}
+
+
+// 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!
+//
+// Parameter 'slackCapacity' tells the function how much free capacity (in
+// terms of samples) there _at least_ should be, in order to the caller to
+// succesfully insert all the required samples to the buffer. When necessary,
+// the function grows the buffer size to comply with this requirement.
+//
+// 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 *FIFOSampleBuffer::ptrEnd(uint slackCapacity)
+{
+ ensureCapacity(samplesInBuffer + slackCapacity);
+ return buffer + samplesInBuffer * channels;
+}
+
+
+// Returns a pointer to the beginning of the currently non-outputted samples.
+// This function is provided for accessing the output samples directly.
+// Please be careful!
+//
+// When using this function to output samples, also remember to 'remove' the
+// outputted samples from the buffer by calling the
+// 'receiveSamples(numSamples)' function
+SAMPLETYPE *FIFOSampleBuffer::ptrBegin()
+{
+ assert(buffer);
+ return buffer + bufferPos * channels;
+}
+
+
+// Ensures that the buffer has enought capacity, i.e. space for _at least_
+// 'capacityRequirement' number of samples. The buffer is grown in steps of
+// 4 kilobytes to eliminate the need for frequently growing up the buffer,
+// as well as to round the buffer size up to the virtual memory page size.
+void FIFOSampleBuffer::ensureCapacity(uint capacityRequirement)
+{
+ SAMPLETYPE *tempUnaligned, *temp;
+
+ if (capacityRequirement > getCapacity())
+ {
+ // enlarge the buffer in 4kbyte steps (round up to next 4k boundary)
+ sizeInBytes = (capacityRequirement * channels * sizeof(SAMPLETYPE) + 4095) & (uint)-4096;
+ assert(sizeInBytes % 2 == 0);
+ tempUnaligned = new SAMPLETYPE[sizeInBytes / sizeof(SAMPLETYPE) + 16 / sizeof(SAMPLETYPE)];
+ if (tempUnaligned == NULL)
+ {
+ throw std::runtime_error("Couldn't allocate memory!\n");
+ }
+ // Align the buffer to begin at 16byte cache line boundary for optimal performance
+ temp = (SAMPLETYPE *)(((ulong)tempUnaligned + 15) & (ulong)-16);
+ if (samplesInBuffer)
+ {
+ memcpy(temp, ptrBegin(), samplesInBuffer * channels * sizeof(SAMPLETYPE));
+ }
+ delete[] bufferUnaligned;
+ buffer = temp;
+ bufferUnaligned = tempUnaligned;
+ bufferPos = 0;
+ }
+ else
+ {
+ // simply rewind the buffer (if necessary)
+ rewind();
+ }
+}
+
+
+// Returns the current buffer capacity in terms of samples
+uint FIFOSampleBuffer::getCapacity() const
+{
+ return sizeInBytes / (channels * sizeof(SAMPLETYPE));
+}
+
+
+// Returns the number of samples currently in the buffer
+uint FIFOSampleBuffer::numSamples() const
+{
+ return samplesInBuffer;
+}
+
+
+// Output samples from beginning of the sample buffer. Copies demanded number
+// of samples to output and removes them from the sample buffer. If there
+// are less than 'numsample' samples in the buffer, returns all available.
+//
+// Returns number of samples copied.
+uint FIFOSampleBuffer::receiveSamples(SAMPLETYPE *output, uint maxSamples)
+{
+ uint num;
+
+ num = (maxSamples > samplesInBuffer) ? samplesInBuffer : maxSamples;
+
+ memcpy(output, ptrBegin(), channels * sizeof(SAMPLETYPE) * num);
+ return receiveSamples(num);
+}
+
+
+// Removes samples from the beginning of the sample buffer without copying them
+// anywhere. Used to reduce the number of samples in the buffer, when accessing
+// the sample buffer with the 'ptrBegin' function.
+uint FIFOSampleBuffer::receiveSamples(uint maxSamples)
+{
+ if (maxSamples >= samplesInBuffer)
+ {
+ uint temp;
+
+ temp = samplesInBuffer;
+ samplesInBuffer = 0;
+ return temp;
+ }
+
+ samplesInBuffer -= maxSamples;
+ bufferPos += maxSamples;
+
+ return maxSamples;
+}
+
+
+// Returns nonzero if the sample buffer is empty
+int FIFOSampleBuffer::isEmpty() const
+{
+ return (samplesInBuffer == 0) ? 1 : 0;
+}
+
+
+// Clears the sample buffer
+void FIFOSampleBuffer::clear()
+{
+ samplesInBuffer = 0;
+ bufferPos = 0;
+}
diff --git a/plugins/dsp_soundtouch/soundtouch/source/SoundTouch/FIRFilter.cpp b/plugins/dsp_soundtouch/soundtouch/source/SoundTouch/FIRFilter.cpp new file mode 100644 index 00000000..231263ad --- /dev/null +++ b/plugins/dsp_soundtouch/soundtouch/source/SoundTouch/FIRFilter.cpp @@ -0,0 +1,269 @@ +////////////////////////////////////////////////////////////////////////////////
+///
+/// General FIR digital filter routines with MMX optimization.
+///
+/// Note : MMX optimized functions reside in a separate, platform-specific file,
+/// e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'
+///
+/// Author : Copyright (c) Olli Parviainen
+/// Author e-mail : oparviai 'at' iki.fi
+/// SoundTouch WWW: http://www.surina.net/soundtouch
+///
+////////////////////////////////////////////////////////////////////////////////
+//
+// Last changed : $Date: 2009-02-25 19:13:51 +0200 (Wed, 25 Feb 2009) $
+// File revision : $Revision: 4 $
+//
+// $Id: FIRFilter.cpp 67 2009-02-25 17:13:51Z oparviai $
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// 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
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#include <memory.h>
+#include <assert.h>
+#include <math.h>
+#include <stdlib.h>
+#include <stdexcept>
+#include "FIRFilter.h"
+#include "cpu_detect.h"
+
+using namespace soundtouch;
+
+/*****************************************************************************
+ *
+ * Implementation of the class 'FIRFilter'
+ *
+ *****************************************************************************/
+
+FIRFilter::FIRFilter()
+{
+ resultDivFactor = 0;
+ resultDivider = 0;
+ length = 0;
+ lengthDiv8 = 0;
+ filterCoeffs = NULL;
+}
+
+
+FIRFilter::~FIRFilter()
+{
+ delete[] filterCoeffs;
+}
+
+// Usual C-version of the filter routine for stereo sound
+uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const
+{
+ uint i, j, end;
+ LONG_SAMPLETYPE suml, sumr;
+#ifdef FLOAT_SAMPLES
+ // when using floating point samples, use a scaler instead of a divider
+ // because division is much slower operation than multiplying.
+ double dScaler = 1.0 / (double)resultDivider;
+#endif
+
+ assert(length != 0);
+ assert(src != NULL);
+ assert(dest != NULL);
+ assert(filterCoeffs != NULL);
+
+ end = 2 * (numSamples - length);
+
+ for (j = 0; j < end; j += 2)
+ {
+ const SAMPLETYPE *ptr;
+
+ suml = sumr = 0;
+ ptr = src + j;
+
+ for (i = 0; i < length; i += 4)
+ {
+ // loop is unrolled by factor of 4 here for efficiency
+ suml += ptr[2 * i + 0] * filterCoeffs[i + 0] +
+ ptr[2 * i + 2] * filterCoeffs[i + 1] +
+ ptr[2 * i + 4] * filterCoeffs[i + 2] +
+ ptr[2 * i + 6] * filterCoeffs[i + 3];
+ sumr += ptr[2 * i + 1] * filterCoeffs[i + 0] +
+ ptr[2 * i + 3] * filterCoeffs[i + 1] +
+ ptr[2 * i + 5] * filterCoeffs[i + 2] +
+ ptr[2 * i + 7] * filterCoeffs[i + 3];
+ }
+
+#ifdef INTEGER_SAMPLES
+ suml >>= resultDivFactor;
+ sumr >>= resultDivFactor;
+ // saturate to 16 bit integer limits
+ suml = (suml < -32768) ? -32768 : (suml > 32767) ? 32767 : suml;
+ // saturate to 16 bit integer limits
+ sumr = (sumr < -32768) ? -32768 : (sumr > 32767) ? 32767 : sumr;
+#else
+ suml *= dScaler;
+ sumr *= dScaler;
+#endif // INTEGER_SAMPLES
+ dest[j] = (SAMPLETYPE)suml;
+ dest[j + 1] = (SAMPLETYPE)sumr;
+ }
+ return numSamples - length;
+}
+
+
+
+
+// Usual C-version of the filter routine for mono sound
+uint FIRFilter::evaluateFilterMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const
+{
+ uint i, j, end;
+ LONG_SAMPLETYPE sum;
+#ifdef FLOAT_SAMPLES
+ // when using floating point samples, use a scaler instead of a divider
+ // because division is much slower operation than multiplying.
+ double dScaler = 1.0 / (double)resultDivider;
+#endif
+
+
+ assert(length != 0);
+
+ end = numSamples - length;
+ for (j = 0; j < end; j ++)
+ {
+ sum = 0;
+ for (i = 0; i < length; i += 4)
+ {
+ // loop is unrolled by factor of 4 here for efficiency
+ sum += src[i + 0] * filterCoeffs[i + 0] +
+ src[i + 1] * filterCoeffs[i + 1] +
+ src[i + 2] * filterCoeffs[i + 2] +
+ src[i + 3] * filterCoeffs[i + 3];
+ }
+#ifdef INTEGER_SAMPLES
+ sum >>= resultDivFactor;
+ // saturate to 16 bit integer limits
+ sum = (sum < -32768) ? -32768 : (sum > 32767) ? 32767 : sum;
+#else
+ sum *= dScaler;
+#endif // INTEGER_SAMPLES
+ dest[j] = (SAMPLETYPE)sum;
+ src ++;
+ }
+ return end;
+}
+
+
+// Set filter coeffiecients and length.
+//
+// Throws an exception if filter length isn't divisible by 8
+void FIRFilter::setCoefficients(const SAMPLETYPE *coeffs, uint newLength, uint uResultDivFactor)
+{
+ assert(newLength > 0);
+ if (newLength % 8) throw std::runtime_error("FIR filter length not divisible by 8");
+
+ lengthDiv8 = newLength / 8;
+ length = lengthDiv8 * 8;
+ assert(length == newLength);
+
+ resultDivFactor = uResultDivFactor;
+ resultDivider = (SAMPLETYPE)::pow(2.0, (int)resultDivFactor);
+
+ delete[] filterCoeffs;
+ filterCoeffs = new SAMPLETYPE[length];
+ memcpy(filterCoeffs, coeffs, length * sizeof(SAMPLETYPE));
+}
+
+
+uint FIRFilter::getLength() const
+{
+ return length;
+}
+
+
+
+// Applies the filter to the given sequence of samples.
+//
+// Note : The amount of outputted samples is by value of 'filter_length'
+// smaller than the amount of input samples.
+uint FIRFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels) const
+{
+ assert(numChannels == 1 || numChannels == 2);
+
+ assert(length > 0);
+ assert(lengthDiv8 * 8 == length);
+ if (numSamples < length) return 0;
+ if (numChannels == 2)
+ {
+ return evaluateFilterStereo(dest, src, numSamples);
+ } else {
+ return evaluateFilterMono(dest, src, numSamples);
+ }
+}
+
+
+
+// Operator 'new' is overloaded so that it automatically creates a suitable instance
+// depending on if we've a MMX-capable CPU available or not.
+void * FIRFilter::operator new(size_t s)
+{
+ // Notice! don't use "new FIRFilter" directly, use "newInstance" to create a new instance instead!
+ throw std::runtime_error("Error in FIRFilter::new: Don't use 'new FIRFilter', use 'newInstance' member instead!");
+ return NULL;
+}
+
+
+FIRFilter * FIRFilter::newInstance()
+{
+ uint uExtensions;
+
+ uExtensions = detectCPUextensions();
+
+ // Check if MMX/SSE/3DNow! instruction set extensions supported by CPU
+
+#ifdef ALLOW_MMX
+ // MMX routines available only with integer sample types
+ if (uExtensions & SUPPORT_MMX)
+ {
+ return ::new FIRFilterMMX;
+ }
+ else
+#endif // ALLOW_MMX
+
+#ifdef ALLOW_SSE
+ if (uExtensions & SUPPORT_SSE)
+ {
+ // SSE support
+ return ::new FIRFilterSSE;
+ }
+ else
+#endif // ALLOW_SSE
+
+#ifdef ALLOW_3DNOW
+ if (uExtensions & SUPPORT_3DNOW)
+ {
+ // 3DNow! support
+ return ::new FIRFilter3DNow;
+ }
+ else
+#endif // ALLOW_3DNOW
+
+ {
+ // ISA optimizations not supported, use plain C version
+ return ::new FIRFilter;
+ }
+}
diff --git a/plugins/dsp_soundtouch/soundtouch/source/SoundTouch/PeakFinder.cpp b/plugins/dsp_soundtouch/soundtouch/source/SoundTouch/PeakFinder.cpp new file mode 100644 index 00000000..03f60bfa --- /dev/null +++ b/plugins/dsp_soundtouch/soundtouch/source/SoundTouch/PeakFinder.cpp @@ -0,0 +1,239 @@ +////////////////////////////////////////////////////////////////////////////////
+///
+/// Peak detection routine.
+///
+/// The routine detects highest value on an array of values and calculates the
+/// precise peak location as a mass-center of the 'hump' around the peak value.
+///
+/// Author : Copyright (c) Olli Parviainen
+/// Author e-mail : oparviai 'at' iki.fi
+/// SoundTouch WWW: http://www.surina.net/soundtouch
+///
+////////////////////////////////////////////////////////////////////////////////
+//
+// Last changed : $Date: 2009-02-21 18:00:14 +0200 (Sat, 21 Feb 2009) $
+// File revision : $Revision: 4 $
+//
+// $Id: PeakFinder.cpp 63 2009-02-21 16:00:14Z oparviai $
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// 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
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#include <math.h>
+#include <assert.h>
+
+#include "PeakFinder.h"
+
+using namespace soundtouch;
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+
+
+PeakFinder::PeakFinder()
+{
+ minPos = maxPos = 0;
+}
+
+
+// Finds 'ground level' of a peak hump by starting from 'peakpos' and proceeding
+// to direction defined by 'direction' until next 'hump' after minimum value will
+// begin
+int PeakFinder::findGround(const float *data, int peakpos, int direction) const
+{
+ float refvalue;
+ int lowpos;
+ int pos;
+ int climb_count;
+ float delta;
+
+ climb_count = 0;
+ refvalue = data[peakpos];
+ lowpos = peakpos;
+
+ pos = peakpos;
+
+ while ((pos > minPos) && (pos < maxPos))
+ {
+ int prevpos;
+
+ prevpos = pos;
+ pos += direction;
+
+ // calculate derivate
+ delta = data[pos] - data[prevpos];
+ if (delta <= 0)
+ {
+ // going downhill, ok
+ if (climb_count)
+ {
+ climb_count --; // decrease climb count
+ }
+
+ // check if new minimum found
+ if (data[pos] < refvalue)
+ {
+ // new minimum found
+ lowpos = pos;
+ refvalue = data[pos];
+ }
+ }
+ else
+ {
+ // going uphill, increase climbing counter
+ climb_count ++;
+ if (climb_count > 5) break; // we've been climbing too long => it's next uphill => quit
+ }
+ }
+ return lowpos;
+}
+
+
+// Find offset where the value crosses the given level, when starting from 'peakpos' and
+// proceeds to direction defined in 'direction'
+int PeakFinder::findCrossingLevel(const float *data, float level, int peakpos, int direction) const
+{
+ float peaklevel;
+ int pos;
+
+ peaklevel = data[peakpos];
+ assert(peaklevel >= level);
+ pos = peakpos;
+ while ((pos >= minPos) && (pos < maxPos))
+ {
+ if (data[pos + direction] < level) return pos; // crossing found
+ pos += direction;
+ }
+ return -1; // not found
+}
+
+
+// Calculates the center of mass location of 'data' array items between 'firstPos' and 'lastPos'
+double PeakFinder::calcMassCenter(const float *data, int firstPos, int lastPos) const
+{
+ int i;
+ float sum;
+ float wsum;
+
+ sum = 0;
+ wsum = 0;
+ for (i = firstPos; i <= lastPos; i ++)
+ {
+ sum += (float)i * data[i];
+ wsum += data[i];
+ }
+
+ if (wsum < 1e-6) return 0;
+ return sum / wsum;
+}
+
+
+
+/// get exact center of peak near given position by calculating local mass of center
+double PeakFinder::getPeakCenter(const float *data, int peakpos) const
+{
+ float peakLevel; // peak level
+ int crosspos1, crosspos2; // position where the peak 'hump' crosses cutting level
+ float cutLevel; // cutting value
+ float groundLevel; // ground level of the peak
+ int gp1, gp2; // bottom positions of the peak 'hump'
+
+ // find ground positions.
+ gp1 = findGround(data, peakpos, -1);
+ gp2 = findGround(data, peakpos, 1);
+
+ groundLevel = max(data[gp1], data[gp2]);
+ peakLevel = data[peakpos];
+
+ if (groundLevel < 1e-6) return 0; // ground level too small => detection failed
+ if ((peakLevel / groundLevel) < 1.3) return 0; // peak less than 30% of the ground level => no good peak detected
+
+ // calculate 70%-level of the peak
+ cutLevel = 0.70f * peakLevel + 0.30f * groundLevel;
+ // find mid-level crossings
+ crosspos1 = findCrossingLevel(data, cutLevel, peakpos, -1);
+ crosspos2 = findCrossingLevel(data, cutLevel, peakpos, 1);
+
+ if ((crosspos1 < 0) || (crosspos2 < 0)) return 0; // no crossing, no peak..
+
+ // calculate mass center of the peak surroundings
+ return calcMassCenter(data, crosspos1, crosspos2);
+}
+
+
+
+double PeakFinder::detectPeak(const float *data, int aminPos, int amaxPos)
+{
+
+ int i;
+ int peakpos; // position of peak level
+ double highPeak, peak;
+
+ this->minPos = aminPos;
+ this->maxPos = amaxPos;
+
+ // find absolute peak
+ peakpos = minPos;
+ peak = data[minPos];
+ for (i = minPos + 1; i < maxPos; i ++)
+ {
+ if (data[i] > peak)
+ {
+ peak = data[i];
+ peakpos = i;
+ }
+ }
+
+ // Calculate exact location of the highest peak mass center
+ highPeak = getPeakCenter(data, peakpos);
+ peak = highPeak;
+
+ // Now check if the highest peak were in fact harmonic of the true base beat peak
+ // - sometimes the highest peak can be Nth harmonic of the true base peak yet
+ // just a slightly higher than the true base
+ for (i = 2; i < 10; i ++)
+ {
+ double peaktmp, tmp;
+ int i1,i2;
+
+ peakpos = (int)(highPeak / (double)i + 0.5f);
+ if (peakpos < minPos) break;
+
+ // calculate mass-center of possible base peak
+ peaktmp = getPeakCenter(data, peakpos);
+
+ // now compare to highest detected peak
+ i1 = (int)(highPeak + 0.5);
+ i2 = (int)(peaktmp + 0.5);
+ tmp = 2 * (data[i2] - data[i1]) / (data[i2] + data[i1]);
+ if (fabs(tmp) < 0.1)
+ {
+ // The highest peak is harmonic of almost as high base peak,
+ // thus use the base peak instead
+ peak = peaktmp;
+ }
+ }
+
+ return peak;
+}
+
+
diff --git a/plugins/dsp_soundtouch/soundtouch/source/SoundTouch/RateTransposer.cpp b/plugins/dsp_soundtouch/soundtouch/source/SoundTouch/RateTransposer.cpp new file mode 100644 index 00000000..7e0b277d --- /dev/null +++ b/plugins/dsp_soundtouch/soundtouch/source/SoundTouch/RateTransposer.cpp @@ -0,0 +1,628 @@ +////////////////////////////////////////////////////////////////////////////////
+///
+/// Sample rate transposer. Changes sample rate by using linear interpolation
+/// together with anti-alias filtering (first order interpolation with anti-
+/// alias filtering should be quite adequate for this application)
+///
+/// Author : Copyright (c) Olli Parviainen
+/// Author e-mail : oparviai 'at' iki.fi
+/// SoundTouch WWW: http://www.surina.net/soundtouch
+///
+////////////////////////////////////////////////////////////////////////////////
+//
+// Last changed : $Date: 2009-10-31 16:37:24 +0200 (Sat, 31 Oct 2009) $
+// File revision : $Revision: 4 $
+//
+// $Id: RateTransposer.cpp 74 2009-10-31 14:37:24Z oparviai $
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// 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
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#include <memory.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdexcept>
+#include "RateTransposer.h"
+#include "AAFilter.h"
+
+using namespace std;
+using namespace soundtouch;
+
+
+/// A linear samplerate transposer class that uses integer arithmetics.
+/// for the transposing.
+class RateTransposerInteger : public RateTransposer
+{
+protected:
+ int iSlopeCount;
+ int iRate;
+ SAMPLETYPE sPrevSampleL, sPrevSampleR;
+
+ virtual void resetRegisters();
+
+ virtual uint transposeStereo(SAMPLETYPE *dest,
+ const SAMPLETYPE *src,
+ uint numSamples);
+ virtual uint transposeMono(SAMPLETYPE *dest,
+ const SAMPLETYPE *src,
+ uint numSamples);
+
+public:
+ RateTransposerInteger();
+ virtual ~RateTransposerInteger();
+
+ /// Sets new target rate. Normal rate = 1.0, smaller values represent slower
+ /// rate, larger faster rates.
+ virtual void setRate(float newRate);
+
+};
+
+
+/// A linear samplerate transposer class that uses floating point arithmetics
+/// for the transposing.
+class RateTransposerFloat : public RateTransposer
+{
+protected:
+ float fSlopeCount;
+ SAMPLETYPE sPrevSampleL, sPrevSampleR;
+
+ virtual void resetRegisters();
+
+ virtual uint transposeStereo(SAMPLETYPE *dest,
+ const SAMPLETYPE *src,
+ uint numSamples);
+ virtual uint transposeMono(SAMPLETYPE *dest,
+ const SAMPLETYPE *src,
+ uint numSamples);
+
+public:
+ RateTransposerFloat();
+ virtual ~RateTransposerFloat();
+};
+
+
+
+
+// Operator 'new' is overloaded so that it automatically creates a suitable instance
+// depending on if we've a MMX/SSE/etc-capable CPU available or not.
+void * RateTransposer::operator new(size_t s)
+{
+ throw runtime_error("Error in RateTransoser::new: don't use \"new TDStretch\" directly, use \"newInstance\" to create a new instance instead!");
+ return NULL;
+}
+
+
+RateTransposer *RateTransposer::newInstance()
+{
+#ifdef INTEGER_SAMPLES
+ return ::new RateTransposerInteger;
+#else
+ return ::new RateTransposerFloat;
+#endif
+}
+
+
+// Constructor
+RateTransposer::RateTransposer() : FIFOProcessor(&outputBuffer)
+{
+ numChannels = 2;
+ bUseAAFilter = TRUE;
+ fRate = 0;
+
+ // Instantiates the anti-alias filter with default tap length
+ // of 32
+ pAAFilter = new AAFilter(32);
+}
+
+
+
+RateTransposer::~RateTransposer()
+{
+ delete pAAFilter;
+}
+
+
+
+/// Enables/disables the anti-alias filter. Zero to disable, nonzero to enable
+void RateTransposer::enableAAFilter(BOOL newMode)
+{
+ bUseAAFilter = newMode;
+}
+
+
+/// Returns nonzero if anti-alias filter is enabled.
+BOOL RateTransposer::isAAFilterEnabled() const
+{
+ return bUseAAFilter;
+}
+
+
+AAFilter *RateTransposer::getAAFilter()
+{
+ return pAAFilter;
+}
+
+
+
+// Sets new target iRate. Normal iRate = 1.0, smaller values represent slower
+// iRate, larger faster iRates.
+void RateTransposer::setRate(float newRate)
+{
+ double fCutoff;
+
+ fRate = newRate;
+
+ // design a new anti-alias filter
+ if (newRate > 1.0f)
+ {
+ fCutoff = 0.5f / newRate;
+ }
+ else
+ {
+ fCutoff = 0.5f * newRate;
+ }
+ pAAFilter->setCutoffFreq(fCutoff);
+}
+
+
+// Outputs as many samples of the 'outputBuffer' as possible, and if there's
+// any room left, outputs also as many of the incoming samples as possible.
+// The goal is to drive the outputBuffer empty.
+//
+// It's allowed for 'output' and 'input' parameters to point to the same
+// memory position.
+/*
+void RateTransposer::flushStoreBuffer()
+{
+ if (storeBuffer.isEmpty()) return;
+
+ outputBuffer.moveSamples(storeBuffer);
+}
+*/
+
+
+// Adds 'nSamples' pcs of samples from the 'samples' memory position into
+// the input of the object.
+void RateTransposer::putSamples(const SAMPLETYPE *samples, uint nSamples)
+{
+ processSamples(samples, nSamples);
+}
+
+
+
+// Transposes up the sample rate, causing the observed playback 'rate' of the
+// sound to decrease
+void RateTransposer::upsample(const SAMPLETYPE *src, uint nSamples)
+{
+ uint count, sizeTemp, num;
+
+ // If the parameter 'uRate' value is smaller than 'SCALE', first transpose
+ // the samples and then apply the anti-alias filter to remove aliasing.
+
+ // First check that there's enough room in 'storeBuffer'
+ // (+16 is to reserve some slack in the destination buffer)
+ sizeTemp = (uint)((float)nSamples / fRate + 16.0f);
+
+ // Transpose the samples, store the result into the end of "storeBuffer"
+ count = transpose(storeBuffer.ptrEnd(sizeTemp), src, nSamples);
+ storeBuffer.putSamples(count);
+
+ // Apply the anti-alias filter to samples in "store output", output the
+ // result to "dest"
+ num = storeBuffer.numSamples();
+ count = pAAFilter->evaluate(outputBuffer.ptrEnd(num),
+ storeBuffer.ptrBegin(), num, (uint)numChannels);
+ outputBuffer.putSamples(count);
+
+ // Remove the processed samples from "storeBuffer"
+ storeBuffer.receiveSamples(count);
+}
+
+
+// Transposes down the sample rate, causing the observed playback 'rate' of the
+// sound to increase
+void RateTransposer::downsample(const SAMPLETYPE *src, uint nSamples)
+{
+ uint count, sizeTemp;
+
+ // If the parameter 'uRate' value is larger than 'SCALE', first apply the
+ // anti-alias filter to remove high frequencies (prevent them from folding
+ // over the lover frequencies), then transpose.
+
+ // Add the new samples to the end of the storeBuffer
+ storeBuffer.putSamples(src, nSamples);
+
+ // Anti-alias filter the samples to prevent folding and output the filtered
+ // data to tempBuffer. Note : because of the FIR filter length, the
+ // filtering routine takes in 'filter_length' more samples than it outputs.
+ assert(tempBuffer.isEmpty());
+ sizeTemp = storeBuffer.numSamples();
+
+ count = pAAFilter->evaluate(tempBuffer.ptrEnd(sizeTemp),
+ storeBuffer.ptrBegin(), sizeTemp, (uint)numChannels);
+
+ if (count == 0) return;
+
+ // Remove the filtered samples from 'storeBuffer'
+ storeBuffer.receiveSamples(count);
+
+ // Transpose the samples (+16 is to reserve some slack in the destination buffer)
+ sizeTemp = (uint)((float)nSamples / fRate + 16.0f);
+ count = transpose(outputBuffer.ptrEnd(sizeTemp), tempBuffer.ptrBegin(), count);
+ outputBuffer.putSamples(count);
+}
+
+
+// Transposes sample rate by applying anti-alias filter to prevent folding.
+// Returns amount of samples returned in the "dest" buffer.
+// The maximum amount of samples that can be returned at a time is set by
+// the 'set_returnBuffer_size' function.
+void RateTransposer::processSamples(const SAMPLETYPE *src, uint nSamples)
+{
+ uint count;
+ uint sizeReq;
+
+ if (nSamples == 0) return;
+ assert(pAAFilter);
+
+ // If anti-alias filter is turned off, simply transpose without applying
+ // the filter
+ if (bUseAAFilter == FALSE)
+ {
+ sizeReq = (uint)((float)nSamples / fRate + 1.0f);
+ count = transpose(outputBuffer.ptrEnd(sizeReq), src, nSamples);
+ outputBuffer.putSamples(count);
+ return;
+ }
+
+ // Transpose with anti-alias filter
+ if (fRate < 1.0f)
+ {
+ upsample(src, nSamples);
+ }
+ else
+ {
+ downsample(src, nSamples);
+ }
+}
+
+
+// Transposes the sample rate of the given samples using linear interpolation.
+// Returns the number of samples returned in the "dest" buffer
+inline uint RateTransposer::transpose(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples)
+{
+ if (numChannels == 2)
+ {
+ return transposeStereo(dest, src, nSamples);
+ }
+ else
+ {
+ return transposeMono(dest, src, nSamples);
+ }
+}
+
+
+// Sets the number of channels, 1 = mono, 2 = stereo
+void RateTransposer::setChannels(int nChannels)
+{
+ assert(nChannels > 0);
+ if (numChannels == nChannels) return;
+
+ assert(nChannels == 1 || nChannels == 2);
+ numChannels = nChannels;
+
+ storeBuffer.setChannels(numChannels);
+ tempBuffer.setChannels(numChannels);
+ outputBuffer.setChannels(numChannels);
+
+ // Inits the linear interpolation registers
+ resetRegisters();
+}
+
+
+// Clears all the samples in the object
+void RateTransposer::clear()
+{
+ outputBuffer.clear();
+ storeBuffer.clear();
+}
+
+
+// Returns nonzero if there aren't any samples available for outputting.
+int RateTransposer::isEmpty() const
+{
+ int res;
+
+ res = FIFOProcessor::isEmpty();
+ if (res == 0) return 0;
+ return storeBuffer.isEmpty();
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// RateTransposerInteger - integer arithmetic implementation
+//
+
+/// fixed-point interpolation routine precision
+#define SCALE 65536
+
+// Constructor
+RateTransposerInteger::RateTransposerInteger() : RateTransposer()
+{
+ // Notice: use local function calling syntax for sake of clarity,
+ // to indicate the fact that C++ constructor can't call virtual functions.
+ RateTransposerInteger::resetRegisters();
+ RateTransposerInteger::setRate(1.0f);
+}
+
+
+RateTransposerInteger::~RateTransposerInteger()
+{
+}
+
+
+void RateTransposerInteger::resetRegisters()
+{
+ iSlopeCount = 0;
+ sPrevSampleL =
+ sPrevSampleR = 0;
+}
+
+
+
+// Transposes the sample rate of the given samples using linear interpolation.
+// 'Mono' version of the routine. Returns the number of samples returned in
+// the "dest" buffer
+uint RateTransposerInteger::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples)
+{
+ unsigned int i, used;
+ LONG_SAMPLETYPE temp, vol1;
+
+ if (nSamples == 0) return 0; // no samples, no work
+
+ used = 0;
+ i = 0;
+
+ // Process the last sample saved from the previous call first...
+ while (iSlopeCount <= SCALE)
+ {
+ vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount);
+ temp = vol1 * sPrevSampleL + iSlopeCount * src[0];
+ dest[i] = (SAMPLETYPE)(temp / SCALE);
+ i++;
+ iSlopeCount += iRate;
+ }
+ // now always (iSlopeCount > SCALE)
+ iSlopeCount -= SCALE;
+
+ while (1)
+ {
+ while (iSlopeCount > SCALE)
+ {
+ iSlopeCount -= SCALE;
+ used ++;
+ if (used >= nSamples - 1) goto end;
+ }
+ vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount);
+ temp = src[used] * vol1 + iSlopeCount * src[used + 1];
+ dest[i] = (SAMPLETYPE)(temp / SCALE);
+
+ i++;
+ iSlopeCount += iRate;
+ }
+end:
+ // Store the last sample for the next round
+ sPrevSampleL = src[nSamples - 1];
+
+ return i;
+}
+
+
+// Transposes the sample rate of the given samples using linear interpolation.
+// 'Stereo' version of the routine. Returns the number of samples returned in
+// the "dest" buffer
+uint RateTransposerInteger::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples)
+{
+ unsigned int srcPos, i, used;
+ LONG_SAMPLETYPE temp, vol1;
+
+ if (nSamples == 0) return 0; // no samples, no work
+
+ used = 0;
+ i = 0;
+
+ // Process the last sample saved from the sPrevSampleLious call first...
+ while (iSlopeCount <= SCALE)
+ {
+ vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount);
+ temp = vol1 * sPrevSampleL + iSlopeCount * src[0];
+ dest[2 * i] = (SAMPLETYPE)(temp / SCALE);
+ temp = vol1 * sPrevSampleR + iSlopeCount * src[1];
+ dest[2 * i + 1] = (SAMPLETYPE)(temp / SCALE);
+ i++;
+ iSlopeCount += iRate;
+ }
+ // now always (iSlopeCount > SCALE)
+ iSlopeCount -= SCALE;
+
+ while (1)
+ {
+ while (iSlopeCount > SCALE)
+ {
+ iSlopeCount -= SCALE;
+ used ++;
+ if (used >= nSamples - 1) goto end;
+ }
+ srcPos = 2 * used;
+ vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount);
+ temp = src[srcPos] * vol1 + iSlopeCount * src[srcPos + 2];
+ dest[2 * i] = (SAMPLETYPE)(temp / SCALE);
+ temp = src[srcPos + 1] * vol1 + iSlopeCount * src[srcPos + 3];
+ dest[2 * i + 1] = (SAMPLETYPE)(temp / SCALE);
+
+ i++;
+ iSlopeCount += iRate;
+ }
+end:
+ // Store the last sample for the next round
+ sPrevSampleL = src[2 * nSamples - 2];
+ sPrevSampleR = src[2 * nSamples - 1];
+
+ return i;
+}
+
+
+// Sets new target iRate. Normal iRate = 1.0, smaller values represent slower
+// iRate, larger faster iRates.
+void RateTransposerInteger::setRate(float newRate)
+{
+ iRate = (int)(newRate * SCALE + 0.5f);
+ RateTransposer::setRate(newRate);
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// RateTransposerFloat - floating point arithmetic implementation
+//
+//////////////////////////////////////////////////////////////////////////////
+
+// Constructor
+RateTransposerFloat::RateTransposerFloat() : RateTransposer()
+{
+ // Notice: use local function calling syntax for sake of clarity,
+ // to indicate the fact that C++ constructor can't call virtual functions.
+ RateTransposerFloat::resetRegisters();
+ RateTransposerFloat::setRate(1.0f);
+}
+
+
+RateTransposerFloat::~RateTransposerFloat()
+{
+}
+
+
+void RateTransposerFloat::resetRegisters()
+{
+ fSlopeCount = 0;
+ sPrevSampleL =
+ sPrevSampleR = 0;
+}
+
+
+
+// Transposes the sample rate of the given samples using linear interpolation.
+// 'Mono' version of the routine. Returns the number of samples returned in
+// the "dest" buffer
+uint RateTransposerFloat::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples)
+{
+ unsigned int i, used;
+
+ used = 0;
+ i = 0;
+
+ // Process the last sample saved from the previous call first...
+ while (fSlopeCount <= 1.0f)
+ {
+ dest[i] = (SAMPLETYPE)((1.0f - fSlopeCount) * sPrevSampleL + fSlopeCount * src[0]);
+ i++;
+ fSlopeCount += fRate;
+ }
+ fSlopeCount -= 1.0f;
+
+ if (nSamples > 1)
+ {
+ while (1)
+ {
+ while (fSlopeCount > 1.0f)
+ {
+ fSlopeCount -= 1.0f;
+ used ++;
+ if (used >= nSamples - 1) goto end;
+ }
+ dest[i] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[used] + fSlopeCount * src[used + 1]);
+ i++;
+ fSlopeCount += fRate;
+ }
+ }
+end:
+ // Store the last sample for the next round
+ sPrevSampleL = src[nSamples - 1];
+
+ return i;
+}
+
+
+// Transposes the sample rate of the given samples using linear interpolation.
+// 'Mono' version of the routine. Returns the number of samples returned in
+// the "dest" buffer
+uint RateTransposerFloat::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples)
+{
+ unsigned int srcPos, i, used;
+
+ if (nSamples == 0) return 0; // no samples, no work
+
+ used = 0;
+ i = 0;
+
+ // Process the last sample saved from the sPrevSampleLious call first...
+ while (fSlopeCount <= 1.0f)
+ {
+ dest[2 * i] = (SAMPLETYPE)((1.0f - fSlopeCount) * sPrevSampleL + fSlopeCount * src[0]);
+ dest[2 * i + 1] = (SAMPLETYPE)((1.0f - fSlopeCount) * sPrevSampleR + fSlopeCount * src[1]);
+ i++;
+ fSlopeCount += fRate;
+ }
+ // now always (iSlopeCount > 1.0f)
+ fSlopeCount -= 1.0f;
+
+ if (nSamples > 1)
+ {
+ while (1)
+ {
+ while (fSlopeCount > 1.0f)
+ {
+ fSlopeCount -= 1.0f;
+ used ++;
+ if (used >= nSamples - 1) goto end;
+ }
+ srcPos = 2 * used;
+
+ dest[2 * i] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[srcPos]
+ + fSlopeCount * src[srcPos + 2]);
+ dest[2 * i + 1] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[srcPos + 1]
+ + fSlopeCount * src[srcPos + 3]);
+
+ i++;
+ fSlopeCount += fRate;
+ }
+ }
+end:
+ // Store the last sample for the next round
+ sPrevSampleL = src[2 * nSamples - 2];
+ sPrevSampleR = src[2 * nSamples - 1];
+
+ return i;
+}
diff --git a/plugins/dsp_soundtouch/soundtouch/source/SoundTouch/SoundTouch.cpp b/plugins/dsp_soundtouch/soundtouch/source/SoundTouch/SoundTouch.cpp new file mode 100644 index 00000000..aa7ac028 --- /dev/null +++ b/plugins/dsp_soundtouch/soundtouch/source/SoundTouch/SoundTouch.cpp @@ -0,0 +1,480 @@ +//////////////////////////////////////////////////////////////////////////////
+///
+/// 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
+///
+////////////////////////////////////////////////////////////////////////////////
+//
+// Last changed : $Date: 2009-05-19 07:57:30 +0300 (Tue, 19 May 2009) $
+// File revision : $Revision: 4 $
+//
+// $Id: SoundTouch.cpp 73 2009-05-19 04:57:30Z oparviai $
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// 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
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#include <assert.h>
+#include <stdlib.h>
+#include <memory.h>
+#include <math.h>
+#include <stdexcept>
+#include <stdio.h>
+
+#include "SoundTouch.h"
+#include "TDStretch.h"
+#include "RateTransposer.h"
+#include "cpu_detect.h"
+
+using namespace soundtouch;
+
+/// test if two floating point numbers are equal
+#define TEST_FLOAT_EQUAL(a, b) (fabs(a - b) < 1e-10)
+
+
+/// Print library version string for autoconf
+extern "C" void soundtouch_ac_test()
+{
+ printf("SoundTouch Version: %s\n",SOUNDTOUCH_VERSION);
+}
+
+
+SoundTouch::SoundTouch()
+{
+ // Initialize rate transposer and tempo changer instances
+
+ pRateTransposer = RateTransposer::newInstance();
+ pTDStretch = TDStretch::newInstance();
+
+ setOutPipe(pTDStretch);
+
+ rate = tempo = 0;
+
+ virtualPitch =
+ virtualRate =
+ virtualTempo = 1.0;
+
+ calcEffectiveRateAndTempo();
+
+ channels = 0;
+ bSrateSet = FALSE;
+}
+
+
+
+SoundTouch::~SoundTouch()
+{
+ delete pRateTransposer;
+ delete pTDStretch;
+}
+
+
+
+/// Get SoundTouch library version string
+const char *SoundTouch::getVersionString()
+{
+ static const char *_version = SOUNDTOUCH_VERSION;
+
+ return _version;
+}
+
+
+/// Get SoundTouch library version Id
+uint SoundTouch::getVersionId()
+{
+ return SOUNDTOUCH_VERSION_ID;
+}
+
+
+// Sets the number of channels, 1 = mono, 2 = stereo
+void SoundTouch::setChannels(uint numChannels)
+{
+ if (numChannels != 1 && numChannels != 2)
+ {
+ throw std::runtime_error("Illegal number of channels");
+ }
+ channels = numChannels;
+ pRateTransposer->setChannels((int)numChannels);
+ pTDStretch->setChannels((int)numChannels);
+}
+
+
+
+// Sets new rate control value. Normal rate = 1.0, smaller values
+// represent slower rate, larger faster rates.
+void SoundTouch::setRate(float newRate)
+{
+ virtualRate = newRate;
+ calcEffectiveRateAndTempo();
+}
+
+
+
+// Sets new rate control value as a difference in percents compared
+// to the original rate (-50 .. +100 %)
+void SoundTouch::setRateChange(float newRate)
+{
+ virtualRate = 1.0f + 0.01f * newRate;
+ calcEffectiveRateAndTempo();
+}
+
+
+
+// Sets new tempo control value. Normal tempo = 1.0, smaller values
+// represent slower tempo, larger faster tempo.
+void SoundTouch::setTempo(float newTempo)
+{
+ virtualTempo = newTempo;
+ calcEffectiveRateAndTempo();
+}
+
+
+
+// Sets new tempo control value as a difference in percents compared
+// to the original tempo (-50 .. +100 %)
+void SoundTouch::setTempoChange(float newTempo)
+{
+ virtualTempo = 1.0f + 0.01f * newTempo;
+ calcEffectiveRateAndTempo();
+}
+
+
+
+// Sets new pitch control value. Original pitch = 1.0, smaller values
+// represent lower pitches, larger values higher pitch.
+void SoundTouch::setPitch(float newPitch)
+{
+ virtualPitch = newPitch;
+ calcEffectiveRateAndTempo();
+}
+
+
+
+// Sets pitch change in octaves compared to the original pitch
+// (-1.00 .. +1.00)
+void SoundTouch::setPitchOctaves(float newPitch)
+{
+ virtualPitch = (float)exp(0.69314718056f * newPitch);
+ calcEffectiveRateAndTempo();
+}
+
+
+
+// Sets pitch change in semi-tones compared to the original pitch
+// (-12 .. +12)
+void SoundTouch::setPitchSemiTones(int newPitch)
+{
+ setPitchOctaves((float)newPitch / 12.0f);
+}
+
+
+
+void SoundTouch::setPitchSemiTones(float newPitch)
+{
+ setPitchOctaves(newPitch / 12.0f);
+}
+
+
+// Calculates 'effective' rate and tempo values from the
+// nominal control values.
+void SoundTouch::calcEffectiveRateAndTempo()
+{
+ float oldTempo = tempo;
+ float oldRate = rate;
+
+ tempo = virtualTempo / virtualPitch;
+ rate = virtualPitch * virtualRate;
+
+ if (!TEST_FLOAT_EQUAL(rate,oldRate)) pRateTransposer->setRate(rate);
+ if (!TEST_FLOAT_EQUAL(tempo, oldTempo)) pTDStretch->setTempo(tempo);
+
+#ifndef PREVENT_CLICK_AT_RATE_CROSSOVER
+ if (rate <= 1.0f)
+ {
+ if (output != pTDStretch)
+ {
+ FIFOSamplePipe *tempoOut;
+
+ assert(output == pRateTransposer);
+ // move samples in the current output buffer to the output of pTDStretch
+ tempoOut = pTDStretch->getOutput();
+ tempoOut->moveSamples(*output);
+ // move samples in pitch transposer's store buffer to tempo changer's input
+ pTDStretch->moveSamples(*pRateTransposer->getStore());
+
+ output = pTDStretch;
+ }
+ }
+ else
+#endif
+ {
+ if (output != pRateTransposer)
+ {
+ FIFOSamplePipe *transOut;
+
+ assert(output == pTDStretch);
+ // move samples in the current output buffer to the output of pRateTransposer
+ transOut = pRateTransposer->getOutput();
+ transOut->moveSamples(*output);
+ // move samples in tempo changer's input to pitch transposer's input
+ pRateTransposer->moveSamples(*pTDStretch->getInput());
+
+ output = pRateTransposer;
+ }
+ }
+}
+
+
+// Sets sample rate.
+void SoundTouch::setSampleRate(uint srate)
+{
+ bSrateSet = TRUE;
+ // set sample rate, leave other tempo changer parameters as they are.
+ pTDStretch->setParameters((int)srate);
+}
+
+
+// Adds 'numSamples' pcs of samples from the 'samples' memory position into
+// the input of the object.
+void SoundTouch::putSamples(const SAMPLETYPE *samples, uint nSamples)
+{
+ if (bSrateSet == FALSE)
+ {
+ throw std::runtime_error("SoundTouch : Sample rate not defined");
+ }
+ else if (channels == 0)
+ {
+ throw std::runtime_error("SoundTouch : Number of channels not defined");
+ }
+
+ // Transpose the rate of the new samples if necessary
+ /* Bypass the nominal setting - can introduce a click in sound when tempo/pitch control crosses the nominal value...
+ if (rate == 1.0f)
+ {
+ // The rate value is same as the original, simply evaluate the tempo changer.
+ assert(output == pTDStretch);
+ if (pRateTransposer->isEmpty() == 0)
+ {
+ // yet flush the last samples in the pitch transposer buffer
+ // (may happen if 'rate' changes from a non-zero value to zero)
+ pTDStretch->moveSamples(*pRateTransposer);
+ }
+ pTDStretch->putSamples(samples, nSamples);
+ }
+ */
+#ifndef PREVENT_CLICK_AT_RATE_CROSSOVER
+ else if (rate <= 1.0f)
+ {
+ // transpose the rate down, output the transposed sound to tempo changer buffer
+ assert(output == pTDStretch);
+ pRateTransposer->putSamples(samples, nSamples);
+ pTDStretch->moveSamples(*pRateTransposer);
+ }
+ else
+#endif
+ {
+ // evaluate the tempo changer, then transpose the rate up,
+ assert(output == pRateTransposer);
+ pTDStretch->putSamples(samples, nSamples);
+ pRateTransposer->moveSamples(*pTDStretch);
+ }
+}
+
+
+// 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 SoundTouch::flush()
+{
+ int i;
+ uint nOut;
+ SAMPLETYPE buff[128];
+
+ nOut = numSamples();
+
+ memset(buff, 0, 128 * sizeof(SAMPLETYPE));
+ // "Push" the last active samples out from the processing pipeline by
+ // feeding blank samples into the processing pipeline until new,
+ // processed samples appear in the output (not however, more than
+ // 8ksamples in any case)
+ for (i = 0; i < 128; i ++)
+ {
+ putSamples(buff, 64);
+ if (numSamples() != nOut) break; // new samples have appeared in the output!
+ }
+
+ // Clear working buffers
+ pRateTransposer->clear();
+ pTDStretch->clearInput();
+ // yet leave the 'tempoChanger' output intouched as that's where the
+ // flushed samples are!
+}
+
+
+// Changes a setting controlling the processing system behaviour. See the
+// 'SETTING_...' defines for available setting ID's.
+BOOL SoundTouch::setSetting(int settingId, int value)
+{
+ int sampleRate, sequenceMs, seekWindowMs, overlapMs;
+
+ // read current tdstretch routine parameters
+ pTDStretch->getParameters(&sampleRate, &sequenceMs, &seekWindowMs, &overlapMs);
+
+ switch (settingId)
+ {
+ case SETTING_USE_AA_FILTER :
+ // enables / disabless anti-alias filter
+ pRateTransposer->enableAAFilter((value != 0) ? TRUE : FALSE);
+ return TRUE;
+
+ case SETTING_AA_FILTER_LENGTH :
+ // sets anti-alias filter length
+ pRateTransposer->getAAFilter()->setLength(value);
+ return TRUE;
+
+ case SETTING_USE_QUICKSEEK :
+ // enables / disables tempo routine quick seeking algorithm
+ pTDStretch->enableQuickSeek((value != 0) ? TRUE : FALSE);
+ return TRUE;
+
+ case SETTING_SEQUENCE_MS:
+ // change time-stretch sequence duration parameter
+ pTDStretch->setParameters(sampleRate, value, seekWindowMs, overlapMs);
+ return TRUE;
+
+ case SETTING_SEEKWINDOW_MS:
+ // change time-stretch seek window length parameter
+ pTDStretch->setParameters(sampleRate, sequenceMs, value, overlapMs);
+ return TRUE;
+
+ case SETTING_OVERLAP_MS:
+ // change time-stretch overlap length parameter
+ pTDStretch->setParameters(sampleRate, sequenceMs, seekWindowMs, value);
+ return TRUE;
+
+ default :
+ return FALSE;
+ }
+}
+
+
+// Reads a setting controlling the processing system behaviour. See the
+// 'SETTING_...' defines for available setting ID's.
+//
+// Returns the setting value.
+int SoundTouch::getSetting(int settingId) const
+{
+ int temp;
+
+ switch (settingId)
+ {
+ case SETTING_USE_AA_FILTER :
+ return (uint)pRateTransposer->isAAFilterEnabled();
+
+ case SETTING_AA_FILTER_LENGTH :
+ return pRateTransposer->getAAFilter()->getLength();
+
+ case SETTING_USE_QUICKSEEK :
+ return (uint) pTDStretch->isQuickSeekEnabled();
+
+ case SETTING_SEQUENCE_MS:
+ pTDStretch->getParameters(NULL, &temp, NULL, NULL);
+ return temp;
+
+ case SETTING_SEEKWINDOW_MS:
+ pTDStretch->getParameters(NULL, NULL, &temp, NULL);
+ return temp;
+
+ case SETTING_OVERLAP_MS:
+ pTDStretch->getParameters(NULL, NULL, NULL, &temp);
+ return temp;
+
+ default :
+ return 0;
+ }
+}
+
+
+// Clears all the samples in the object's output and internal processing
+// buffers.
+void SoundTouch::clear()
+{
+ pRateTransposer->clear();
+ pTDStretch->clear();
+}
+
+
+
+/// Returns number of samples currently unprocessed.
+uint SoundTouch::numUnprocessedSamples() const
+{
+ FIFOSamplePipe * psp;
+ if (pTDStretch)
+ {
+ psp = pTDStretch->getInput();
+ if (psp)
+ {
+ return psp->numSamples();
+ }
+ }
+ return 0;
+}
diff --git a/plugins/dsp_soundtouch/soundtouch/source/SoundTouch/TDStretch.cpp b/plugins/dsp_soundtouch/soundtouch/source/SoundTouch/TDStretch.cpp new file mode 100644 index 00000000..232133b5 --- /dev/null +++ b/plugins/dsp_soundtouch/soundtouch/source/SoundTouch/TDStretch.cpp @@ -0,0 +1,1045 @@ +////////////////////////////////////////////////////////////////////////////////
+///
+/// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo
+/// while maintaining the original pitch by using a time domain WSOLA-like
+/// method with several performance-increasing tweaks.
+///
+/// Note : MMX optimized functions reside in a separate, platform-specific
+/// file, e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'
+///
+/// Author : Copyright (c) Olli Parviainen
+/// Author e-mail : oparviai 'at' iki.fi
+/// SoundTouch WWW: http://www.surina.net/soundtouch
+///
+////////////////////////////////////////////////////////////////////////////////
+//
+// Last changed : $Date: 2009-12-28 21:27:04 +0200 (Mon, 28 Dec 2009) $
+// File revision : $Revision: 1.12 $
+//
+// $Id: TDStretch.cpp 77 2009-12-28 19:27:04Z oparviai $
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// 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
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#include <string.h>
+#include <limits.h>
+#include <assert.h>
+#include <math.h>
+#include <float.h>
+#include <stdexcept>
+
+#include "STTypes.h"
+#include "cpu_detect.h"
+#include "TDStretch.h"
+
+#include <stdio.h>
+
+using namespace soundtouch;
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+
+
+/*****************************************************************************
+ *
+ * Constant definitions
+ *
+ *****************************************************************************/
+
+// Table for the hierarchical mixing position seeking algorithm
+static const short _scanOffsets[5][24]={
+ { 124, 186, 248, 310, 372, 434, 496, 558, 620, 682, 744, 806,
+ 868, 930, 992, 1054, 1116, 1178, 1240, 1302, 1364, 1426, 1488, 0},
+ {-100, -75, -50, -25, 25, 50, 75, 100, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ { -20, -15, -10, -5, 5, 10, 15, 20, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ { -4, -3, -2, -1, 1, 2, 3, 4, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ { 121, 114, 97, 114, 98, 105, 108, 32, 104, 99, 117, 111,
+ 116, 100, 110, 117, 111, 115, 0, 0, 0, 0, 0, 0}};
+
+/*****************************************************************************
+ *
+ * Implementation of the class 'TDStretch'
+ *
+ *****************************************************************************/
+
+
+TDStretch::TDStretch() : FIFOProcessor(&outputBuffer)
+{
+ bQuickSeek = FALSE;
+ channels = 2;
+
+ pMidBuffer = NULL;
+ pRefMidBufferUnaligned = NULL;
+ overlapLength = 0;
+
+ bAutoSeqSetting = TRUE;
+ bAutoSeekSetting = TRUE;
+
+// outDebt = 0;
+ skipFract = 0;
+
+ tempo = 1.0f;
+ setParameters(44100, DEFAULT_SEQUENCE_MS, DEFAULT_SEEKWINDOW_MS, DEFAULT_OVERLAP_MS);
+ setTempo(1.0f);
+
+ clear();
+}
+
+
+
+TDStretch::~TDStretch()
+{
+ delete[] pMidBuffer;
+ delete[] pRefMidBufferUnaligned;
+}
+
+
+
+// Sets routine control parameters. These control are certain time constants
+// defining how the sound is stretched to the desired duration.
+//
+// 'sampleRate' = sample rate of the sound
+// 'sequenceMS' = one processing sequence length in milliseconds (default = 82 ms)
+// 'seekwindowMS' = seeking window length for scanning the best overlapping
+// position (default = 28 ms)
+// 'overlapMS' = overlapping length (default = 12 ms)
+
+void TDStretch::setParameters(int aSampleRate, int aSequenceMS,
+ int aSeekWindowMS, int aOverlapMS)
+{
+ // accept only positive parameter values - if zero or negative, use old values instead
+ if (aSampleRate > 0) this->sampleRate = aSampleRate;
+ if (aOverlapMS > 0) this->overlapMs = aOverlapMS;
+
+ if (aSequenceMS > 0)
+ {
+ this->sequenceMs = aSequenceMS;
+ bAutoSeqSetting = FALSE;
+ }
+ else if (aSequenceMS == 0)
+ {
+ // if zero, use automatic setting
+ bAutoSeqSetting = TRUE;
+ }
+
+ if (aSeekWindowMS > 0)
+ {
+ this->seekWindowMs = aSeekWindowMS;
+ bAutoSeekSetting = FALSE;
+ }
+ else if (aSeekWindowMS == 0)
+ {
+ // if zero, use automatic setting
+ bAutoSeekSetting = TRUE;
+ }
+
+ calcSeqParameters();
+
+ calculateOverlapLength(overlapMs);
+
+ // set tempo to recalculate 'sampleReq'
+ setTempo(tempo);
+
+}
+
+
+
+/// Get routine control parameters, see setParameters() function.
+/// Any of the parameters to this function can be NULL, in such case corresponding parameter
+/// value isn't returned.
+void TDStretch::getParameters(int *pSampleRate, int *pSequenceMs, int *pSeekWindowMs, int *pOverlapMs) const
+{
+ if (pSampleRate)
+ {
+ *pSampleRate = sampleRate;
+ }
+
+ if (pSequenceMs)
+ {
+ *pSequenceMs = (bAutoSeqSetting) ? (USE_AUTO_SEQUENCE_LEN) : sequenceMs;
+ }
+
+ if (pSeekWindowMs)
+ {
+ *pSeekWindowMs = (bAutoSeekSetting) ? (USE_AUTO_SEEKWINDOW_LEN) : seekWindowMs;
+ }
+
+ if (pOverlapMs)
+ {
+ *pOverlapMs = overlapMs;
+ }
+}
+
+
+// Overlaps samples in 'midBuffer' with the samples in 'pInput'
+void TDStretch::overlapMono(SAMPLETYPE *pOutput, const SAMPLETYPE *pInput) const
+{
+ int i, itemp;
+
+ for (i = 0; i < overlapLength ; i ++)
+ {
+ itemp = overlapLength - i;
+ pOutput[i] = (pInput[i] * i + pMidBuffer[i] * itemp ) / overlapLength; // >> overlapDividerBits;
+ }
+}
+
+
+
+void TDStretch::clearMidBuffer()
+{
+ memset(pMidBuffer, 0, 2 * sizeof(SAMPLETYPE) * overlapLength);
+}
+
+
+void TDStretch::clearInput()
+{
+ inputBuffer.clear();
+ clearMidBuffer();
+}
+
+
+// Clears the sample buffers
+void TDStretch::clear()
+{
+ outputBuffer.clear();
+ clearInput();
+}
+
+
+
+// Enables/disables the quick position seeking algorithm. Zero to disable, nonzero
+// to enable
+void TDStretch::enableQuickSeek(BOOL enable)
+{
+ bQuickSeek = enable;
+}
+
+
+// Returns nonzero if the quick seeking algorithm is enabled.
+BOOL TDStretch::isQuickSeekEnabled() const
+{
+ return bQuickSeek;
+}
+
+
+// Seeks for the optimal overlap-mixing position.
+int TDStretch::seekBestOverlapPosition(const SAMPLETYPE *refPos)
+{
+ if (channels == 2)
+ {
+ // stereo sound
+ if (bQuickSeek)
+ {
+ return seekBestOverlapPositionStereoQuick(refPos);
+ }
+ else
+ {
+ return seekBestOverlapPositionStereo(refPos);
+ }
+ }
+ else
+ {
+ // mono sound
+ if (bQuickSeek)
+ {
+ return seekBestOverlapPositionMonoQuick(refPos);
+ }
+ else
+ {
+ return seekBestOverlapPositionMono(refPos);
+ }
+ }
+}
+
+
+
+
+// Overlaps samples in 'midBuffer' with the samples in 'pInputBuffer' at position
+// of 'ovlPos'.
+inline void TDStretch::overlap(SAMPLETYPE *pOutput, const SAMPLETYPE *pInput, uint ovlPos) const
+{
+ if (channels == 2)
+ {
+ // stereo sound
+ overlapStereo(pOutput, pInput + 2 * ovlPos);
+ } else {
+ // mono sound.
+ overlapMono(pOutput, pInput + ovlPos);
+ }
+}
+
+
+
+
+// Seeks for the optimal overlap-mixing position. The 'stereo' version of the
+// routine
+//
+// The best position is determined as the position where the two overlapped
+// sample sequences are 'most alike', in terms of the highest cross-correlation
+// value over the overlapping period
+int TDStretch::seekBestOverlapPositionStereo(const SAMPLETYPE *refPos)
+{
+ int bestOffs;
+ double bestCorr, corr;
+ int i;
+
+ // Slopes the amplitudes of the 'midBuffer' samples
+ precalcCorrReferenceStereo();
+
+ bestCorr = FLT_MIN;
+ bestOffs = 0;
+
+ // Scans for the best correlation value by testing each possible position
+ // over the permitted range.
+ for (i = 0; i < seekLength; i ++)
+ {
+ // Calculates correlation value for the mixing position corresponding
+ // to 'i'
+ corr = (double)calcCrossCorrStereo(refPos + 2 * i, pRefMidBuffer);
+ // heuristic rule to slightly favour values close to mid of the range
+ double tmp = (double)(2 * i - seekLength) / (double)seekLength;
+ corr = ((corr + 0.1) * (1.0 - 0.25 * tmp * tmp));
+
+ // Checks for the highest correlation value
+ if (corr > bestCorr)
+ {
+ bestCorr = corr;
+ bestOffs = i;
+ }
+ }
+ // clear cross correlation routine state if necessary (is so e.g. in MMX routines).
+ clearCrossCorrState();
+
+ return bestOffs;
+}
+
+
+// Seeks for the optimal overlap-mixing position. The 'stereo' version of the
+// routine
+//
+// The best position is determined as the position where the two overlapped
+// sample sequences are 'most alike', in terms of the highest cross-correlation
+// value over the overlapping period
+int TDStretch::seekBestOverlapPositionStereoQuick(const SAMPLETYPE *refPos)
+{
+ int j;
+ int bestOffs;
+ double bestCorr, corr;
+ int scanCount, corrOffset, tempOffset;
+
+ // Slopes the amplitude of the 'midBuffer' samples
+ precalcCorrReferenceStereo();
+
+ bestCorr = FLT_MIN;
+ bestOffs = _scanOffsets[0][0];
+ corrOffset = 0;
+ tempOffset = 0;
+
+ // Scans for the best correlation value using four-pass hierarchical search.
+ //
+ // The look-up table 'scans' has hierarchical position adjusting steps.
+ // In first pass the routine searhes for the highest correlation with
+ // relatively coarse steps, then rescans the neighbourhood of the highest
+ // correlation with better resolution and so on.
+ for (scanCount = 0;scanCount < 4; scanCount ++)
+ {
+ j = 0;
+ while (_scanOffsets[scanCount][j])
+ {
+ tempOffset = corrOffset + _scanOffsets[scanCount][j];
+ if (tempOffset >= seekLength) break;
+
+ // Calculates correlation value for the mixing position corresponding
+ // to 'tempOffset'
+ corr = (double)calcCrossCorrStereo(refPos + 2 * tempOffset, pRefMidBuffer);
+ // heuristic rule to slightly favour values close to mid of the range
+ double tmp = (double)(2 * tempOffset - seekLength) / seekLength;
+ corr = ((corr + 0.1) * (1.0 - 0.25 * tmp * tmp));
+
+ // Checks for the highest correlation value
+ if (corr > bestCorr)
+ {
+ bestCorr = corr;
+ bestOffs = tempOffset;
+ }
+ j ++;
+ }
+ corrOffset = bestOffs;
+ }
+ // clear cross correlation routine state if necessary (is so e.g. in MMX routines).
+ clearCrossCorrState();
+
+ return bestOffs;
+}
+
+
+
+// Seeks for the optimal overlap-mixing position. The 'mono' version of the
+// routine
+//
+// The best position is determined as the position where the two overlapped
+// sample sequences are 'most alike', in terms of the highest cross-correlation
+// value over the overlapping period
+int TDStretch::seekBestOverlapPositionMono(const SAMPLETYPE *refPos)
+{
+ int bestOffs;
+ double bestCorr, corr;
+ int tempOffset;
+ const SAMPLETYPE *compare;
+
+ // Slopes the amplitude of the 'midBuffer' samples
+ precalcCorrReferenceMono();
+
+ bestCorr = FLT_MIN;
+ bestOffs = 0;
+
+ // Scans for the best correlation value by testing each possible position
+ // over the permitted range.
+ for (tempOffset = 0; tempOffset < seekLength; tempOffset ++)
+ {
+ compare = refPos + tempOffset;
+
+ // Calculates correlation value for the mixing position corresponding
+ // to 'tempOffset'
+ corr = (double)calcCrossCorrMono(pRefMidBuffer, compare);
+ // heuristic rule to slightly favour values close to mid of the range
+ double tmp = (double)(2 * tempOffset - seekLength) / seekLength;
+ corr = ((corr + 0.1) * (1.0 - 0.25 * tmp * tmp));
+
+ // Checks for the highest correlation value
+ if (corr > bestCorr)
+ {
+ bestCorr = corr;
+ bestOffs = tempOffset;
+ }
+ }
+ // clear cross correlation routine state if necessary (is so e.g. in MMX routines).
+ clearCrossCorrState();
+
+ return bestOffs;
+}
+
+
+// Seeks for the optimal overlap-mixing position. The 'mono' version of the
+// routine
+//
+// The best position is determined as the position where the two overlapped
+// sample sequences are 'most alike', in terms of the highest cross-correlation
+// value over the overlapping period
+int TDStretch::seekBestOverlapPositionMonoQuick(const SAMPLETYPE *refPos)
+{
+ int j;
+ int bestOffs;
+ double bestCorr, corr;
+ int scanCount, corrOffset, tempOffset;
+
+ // Slopes the amplitude of the 'midBuffer' samples
+ precalcCorrReferenceMono();
+
+ bestCorr = FLT_MIN;
+ bestOffs = _scanOffsets[0][0];
+ corrOffset = 0;
+ tempOffset = 0;
+
+ // Scans for the best correlation value using four-pass hierarchical search.
+ //
+ // The look-up table 'scans' has hierarchical position adjusting steps.
+ // In first pass the routine searhes for the highest correlation with
+ // relatively coarse steps, then rescans the neighbourhood of the highest
+ // correlation with better resolution and so on.
+ for (scanCount = 0;scanCount < 4; scanCount ++)
+ {
+ j = 0;
+ while (_scanOffsets[scanCount][j])
+ {
+ tempOffset = corrOffset + _scanOffsets[scanCount][j];
+ if (tempOffset >= seekLength) break;
+
+ // Calculates correlation value for the mixing position corresponding
+ // to 'tempOffset'
+ corr = (double)calcCrossCorrMono(refPos + tempOffset, pRefMidBuffer);
+ // heuristic rule to slightly favour values close to mid of the range
+ double tmp = (double)(2 * tempOffset - seekLength) / seekLength;
+ corr = ((corr + 0.1) * (1.0 - 0.25 * tmp * tmp));
+
+ // Checks for the highest correlation value
+ if (corr > bestCorr)
+ {
+ bestCorr = corr;
+ bestOffs = tempOffset;
+ }
+ j ++;
+ }
+ corrOffset = bestOffs;
+ }
+ // clear cross correlation routine state if necessary (is so e.g. in MMX routines).
+ clearCrossCorrState();
+
+ return bestOffs;
+}
+
+
+/// clear cross correlation routine state if necessary
+void TDStretch::clearCrossCorrState()
+{
+ // default implementation is empty.
+}
+
+
+/// Calculates processing sequence length according to tempo setting
+void TDStretch::calcSeqParameters()
+{
+ // Adjust tempo param according to tempo, so that variating processing sequence length is used
+ // at varius tempo settings, between the given low...top limits
+ #define AUTOSEQ_TEMPO_LOW 0.5 // auto setting low tempo range (-50%)
+ #define AUTOSEQ_TEMPO_TOP 2.0 // auto setting top tempo range (+100%)
+
+ // sequence-ms setting values at above low & top tempo
+ #define AUTOSEQ_AT_MIN 125.0
+ #define AUTOSEQ_AT_MAX 50.0
+ #define AUTOSEQ_K ((AUTOSEQ_AT_MAX - AUTOSEQ_AT_MIN) / (AUTOSEQ_TEMPO_TOP - AUTOSEQ_TEMPO_LOW))
+ #define AUTOSEQ_C (AUTOSEQ_AT_MIN - (AUTOSEQ_K) * (AUTOSEQ_TEMPO_LOW))
+
+ // seek-window-ms setting values at above low & top tempo
+ #define AUTOSEEK_AT_MIN 25.0
+ #define AUTOSEEK_AT_MAX 15.0
+ #define AUTOSEEK_K ((AUTOSEEK_AT_MAX - AUTOSEEK_AT_MIN) / (AUTOSEQ_TEMPO_TOP - AUTOSEQ_TEMPO_LOW))
+ #define AUTOSEEK_C (AUTOSEEK_AT_MIN - (AUTOSEEK_K) * (AUTOSEQ_TEMPO_LOW))
+
+ #define CHECK_LIMITS(x, mi, ma) (((x) < (mi)) ? (mi) : (((x) > (ma)) ? (ma) : (x)))
+
+ double seq, seek;
+
+ if (bAutoSeqSetting)
+ {
+ seq = AUTOSEQ_C + AUTOSEQ_K * tempo;
+ seq = CHECK_LIMITS(seq, AUTOSEQ_AT_MAX, AUTOSEQ_AT_MIN);
+ sequenceMs = (int)(seq + 0.5);
+ }
+
+ if (bAutoSeekSetting)
+ {
+ seek = AUTOSEEK_C + AUTOSEEK_K * tempo;
+ seek = CHECK_LIMITS(seek, AUTOSEEK_AT_MAX, AUTOSEEK_AT_MIN);
+ seekWindowMs = (int)(seek + 0.5);
+ }
+
+ // Update seek window lengths
+ seekWindowLength = (sampleRate * sequenceMs) / 1000;
+ if (seekWindowLength < 2 * overlapLength)
+ {
+ seekWindowLength = 2 * overlapLength;
+ }
+ seekLength = (sampleRate * seekWindowMs) / 1000;
+}
+
+
+
+// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower
+// tempo, larger faster tempo.
+void TDStretch::setTempo(float newTempo)
+{
+ int intskip;
+
+ tempo = newTempo;
+
+ // Calculate new sequence duration
+ calcSeqParameters();
+
+ // Calculate ideal skip length (according to tempo value)
+ nominalSkip = tempo * (seekWindowLength - overlapLength);
+ intskip = (int)(nominalSkip + 0.5f);
+
+ // Calculate how many samples are needed in the 'inputBuffer' to
+ // process another batch of samples
+ //sampleReq = max(intskip + overlapLength, seekWindowLength) + seekLength / 2;
+ sampleReq = max(intskip + overlapLength, seekWindowLength) + seekLength;
+}
+
+
+
+// Sets the number of channels, 1 = mono, 2 = stereo
+void TDStretch::setChannels(int numChannels)
+{
+ assert(numChannels > 0);
+ if (channels == numChannels) return;
+ assert(numChannels == 1 || numChannels == 2);
+
+ channels = numChannels;
+ inputBuffer.setChannels(channels);
+ outputBuffer.setChannels(channels);
+}
+
+
+// nominal tempo, no need for processing, just pass the samples through
+// to outputBuffer
+/*
+void TDStretch::processNominalTempo()
+{
+ assert(tempo == 1.0f);
+
+ if (bMidBufferDirty)
+ {
+ // If there are samples in pMidBuffer waiting for overlapping,
+ // do a single sliding overlapping with them in order to prevent a
+ // clicking distortion in the output sound
+ if (inputBuffer.numSamples() < overlapLength)
+ {
+ // wait until we've got overlapLength input samples
+ return;
+ }
+ // Mix the samples in the beginning of 'inputBuffer' with the
+ // samples in 'midBuffer' using sliding overlapping
+ overlap(outputBuffer.ptrEnd(overlapLength), inputBuffer.ptrBegin(), 0);
+ outputBuffer.putSamples(overlapLength);
+ inputBuffer.receiveSamples(overlapLength);
+ clearMidBuffer();
+ // now we've caught the nominal sample flow and may switch to
+ // bypass mode
+ }
+
+ // Simply bypass samples from input to output
+ outputBuffer.moveSamples(inputBuffer);
+}
+*/
+
+#include <stdio.h>
+
+// Processes as many processing frames of the samples 'inputBuffer', store
+// the result into 'outputBuffer'
+void TDStretch::processSamples()
+{
+ int ovlSkip, offset;
+ int temp;
+
+ /* Removed this small optimization - can introduce a click to sound when tempo setting
+ crosses the nominal value
+ if (tempo == 1.0f)
+ {
+ // tempo not changed from the original, so bypass the processing
+ processNominalTempo();
+ return;
+ }
+ */
+
+ // Process samples as long as there are enough samples in 'inputBuffer'
+ // to form a processing frame.
+// while ((int)inputBuffer.numSamples() >= sampleReq - (outDebt / 4))
+ while ((int)inputBuffer.numSamples() >= sampleReq)
+ {
+ // If tempo differs from the normal ('SCALE'), scan for the best overlapping
+ // position
+ offset = seekBestOverlapPosition(inputBuffer.ptrBegin());
+
+ // Mix the samples in the 'inputBuffer' at position of 'offset' with the
+ // samples in 'midBuffer' using sliding overlapping
+ // ... first partially overlap with the end of the previous sequence
+ // (that's in 'midBuffer')
+ overlap(outputBuffer.ptrEnd((uint)overlapLength), inputBuffer.ptrBegin(), (uint)offset);
+ outputBuffer.putSamples((uint)overlapLength);
+
+ // ... then copy sequence samples from 'inputBuffer' to output:
+ temp = (seekLength / 2 - offset);
+
+ // compensate cumulated output length diff vs. ideal output
+// temp -= outDebt / 4;
+
+ // update ideal vs. true output difference
+// outDebt += temp;
+
+ // length of sequence
+// temp += (seekWindowLength - 2 * overlapLength);
+ temp = (seekWindowLength - 2 * overlapLength);
+
+ // crosscheck that we don't have buffer overflow...
+ if ((int)inputBuffer.numSamples() < (offset + temp + overlapLength * 2))
+ {
+ continue; // just in case, shouldn't really happen
+ }
+
+ outputBuffer.putSamples(inputBuffer.ptrBegin() + channels * (offset + overlapLength), (uint)temp);
+
+ // Copies the end of the current sequence from 'inputBuffer' to
+ // 'midBuffer' for being mixed with the beginning of the next
+ // processing sequence and so on
+ assert((offset + temp + overlapLength * 2) <= (int)inputBuffer.numSamples());
+ memcpy(pMidBuffer, inputBuffer.ptrBegin() + channels * (offset + temp + overlapLength),
+ channels * sizeof(SAMPLETYPE) * overlapLength);
+
+ // Remove the processed samples from the input buffer. Update
+ // the difference between integer & nominal skip step to 'skipFract'
+ // in order to prevent the error from accumulating over time.
+ skipFract += nominalSkip; // real skip size
+ ovlSkip = (int)skipFract; // rounded to integer skip
+ skipFract -= ovlSkip; // maintain the fraction part, i.e. real vs. integer skip
+ inputBuffer.receiveSamples((uint)ovlSkip);
+ }
+}
+
+
+// Adds 'numsamples' pcs of samples from the 'samples' memory position into
+// the input of the object.
+void TDStretch::putSamples(const SAMPLETYPE *samples, uint nSamples)
+{
+ // Add the samples into the input buffer
+ inputBuffer.putSamples(samples, nSamples);
+ // Process the samples in input buffer
+ processSamples();
+}
+
+
+
+/// Set new overlap length parameter & reallocate RefMidBuffer if necessary.
+void TDStretch::acceptNewOverlapLength(int newOverlapLength)
+{
+ int prevOvl;
+
+ assert(newOverlapLength >= 0);
+ prevOvl = overlapLength;
+ overlapLength = newOverlapLength;
+
+ if (overlapLength > prevOvl)
+ {
+ delete[] pMidBuffer;
+ delete[] pRefMidBufferUnaligned;
+
+ pMidBuffer = new SAMPLETYPE[overlapLength * 2];
+ clearMidBuffer();
+
+ pRefMidBufferUnaligned = new SAMPLETYPE[2 * overlapLength + 16 / sizeof(SAMPLETYPE)];
+ // ensure that 'pRefMidBuffer' is aligned to 16 byte boundary for efficiency
+ pRefMidBuffer = (SAMPLETYPE *)((((ulong)pRefMidBufferUnaligned) + 15) & (ulong)-16);
+ }
+}
+
+
+// Operator 'new' is overloaded so that it automatically creates a suitable instance
+// depending on if we've a MMX/SSE/etc-capable CPU available or not.
+void * TDStretch::operator new(size_t s)
+{
+ // Notice! don't use "new TDStretch" directly, use "newInstance" to create a new instance instead!
+ throw std::runtime_error("Error in TDStretch::new: Don't use 'new TDStretch' directly, use 'newInstance' member instead!");
+ return NULL;
+}
+
+
+TDStretch * TDStretch::newInstance()
+{
+ uint uExtensions;
+
+ uExtensions = detectCPUextensions();
+
+ // Check if MMX/SSE/3DNow! instruction set extensions supported by CPU
+
+#ifdef ALLOW_MMX
+ // MMX routines available only with integer sample types
+ if (uExtensions & SUPPORT_MMX)
+ {
+ return ::new TDStretchMMX;
+ }
+ else
+#endif // ALLOW_MMX
+
+
+#ifdef ALLOW_SSE
+ if (uExtensions & SUPPORT_SSE)
+ {
+ // SSE support
+ return ::new TDStretchSSE;
+ }
+ else
+#endif // ALLOW_SSE
+
+
+#ifdef ALLOW_3DNOW
+ if (uExtensions & SUPPORT_3DNOW)
+ {
+ // 3DNow! support
+ return ::new TDStretch3DNow;
+ }
+ else
+#endif // ALLOW_3DNOW
+
+ {
+ // ISA optimizations not supported, use plain C version
+ return ::new TDStretch;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Integer arithmetics specific algorithm implementations.
+//
+//////////////////////////////////////////////////////////////////////////////
+
+#ifdef INTEGER_SAMPLES
+
+// Slopes the amplitude of the 'midBuffer' samples so that cross correlation
+// is faster to calculate
+void TDStretch::precalcCorrReferenceStereo()
+{
+ int i, cnt2;
+ int temp, temp2;
+
+ for (i=0 ; i < (int)overlapLength ;i ++)
+ {
+ temp = i * (overlapLength - i);
+ cnt2 = i * 2;
+
+ temp2 = (pMidBuffer[cnt2] * temp) / slopingDivider;
+ pRefMidBuffer[cnt2] = (short)(temp2);
+ temp2 = (pMidBuffer[cnt2 + 1] * temp) / slopingDivider;
+ pRefMidBuffer[cnt2 + 1] = (short)(temp2);
+ }
+}
+
+
+// Slopes the amplitude of the 'midBuffer' samples so that cross correlation
+// is faster to calculate
+void TDStretch::precalcCorrReferenceMono()
+{
+ int i;
+ long temp;
+ long temp2;
+
+ for (i=0 ; i < (int)overlapLength ;i ++)
+ {
+ temp = i * (overlapLength - i);
+ temp2 = (pMidBuffer[i] * temp) / slopingDivider;
+ pRefMidBuffer[i] = (short)temp2;
+ }
+}
+
+
+// Overlaps samples in 'midBuffer' with the samples in 'input'. The 'Stereo'
+// version of the routine.
+void TDStretch::overlapStereo(short *poutput, const short *input) const
+{
+ int i;
+ short temp;
+ int cnt2;
+
+ for (i = 0; i < overlapLength ; i ++)
+ {
+ temp = (short)(overlapLength - i);
+ cnt2 = 2 * i;
+ poutput[cnt2] = (input[cnt2] * i + pMidBuffer[cnt2] * temp ) / overlapLength;
+ poutput[cnt2 + 1] = (input[cnt2 + 1] * i + pMidBuffer[cnt2 + 1] * temp ) / overlapLength;
+ }
+}
+
+// Calculates the x having the closest 2^x value for the given value
+static int _getClosest2Power(double value)
+{
+ return (int)(log(value) / log(2.0) + 0.5);
+}
+
+
+/// Calculates overlap period length in samples.
+/// Integer version rounds overlap length to closest power of 2
+/// for a divide scaling operation.
+void TDStretch::calculateOverlapLength(int aoverlapMs)
+{
+ int newOvl;
+
+ assert(aoverlapMs >= 0);
+
+ // calculate overlap length so that it's power of 2 - thus it's easy to do
+ // integer division by right-shifting. Term "-1" at end is to account for
+ // the extra most significatnt bit left unused in result by signed multiplication
+ overlapDividerBits = _getClosest2Power((sampleRate * aoverlapMs) / 1000.0) - 1;
+ if (overlapDividerBits > 9) overlapDividerBits = 9;
+ if (overlapDividerBits < 3) overlapDividerBits = 3;
+ newOvl = (int)pow(2.0, (int)overlapDividerBits + 1); // +1 => account for -1 above
+
+ acceptNewOverlapLength(newOvl);
+
+ // calculate sloping divider so that crosscorrelation operation won't
+ // overflow 32-bit register. Max. sum of the crosscorrelation sum without
+ // divider would be 2^30*(N^3-N)/3, where N = overlap length
+ slopingDivider = (newOvl * newOvl - 1) / 3;
+}
+
+
+long TDStretch::calcCrossCorrMono(const short *mixingPos, const short *compare) const
+{
+ long corr;
+ long norm;
+ int i;
+
+ corr = norm = 0;
+ for (i = 1; i < overlapLength; i ++)
+ {
+ corr += (mixingPos[i] * compare[i]) >> overlapDividerBits;
+ norm += (mixingPos[i] * mixingPos[i]) >> overlapDividerBits;
+ }
+
+ // Normalize result by dividing by sqrt(norm) - this step is easiest
+ // done using floating point operation
+ if (norm == 0) norm = 1; // to avoid div by zero
+ return (long)((double)corr * SHRT_MAX / sqrt((double)norm));
+}
+
+
+long TDStretch::calcCrossCorrStereo(const short *mixingPos, const short *compare) const
+{
+ long corr;
+ long norm;
+ int i;
+
+ corr = norm = 0;
+ for (i = 2; i < 2 * overlapLength; i += 2)
+ {
+ corr += (mixingPos[i] * compare[i] +
+ mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBits;
+ norm += (mixingPos[i] * mixingPos[i] + mixingPos[i + 1] * mixingPos[i + 1]) >> overlapDividerBits;
+ }
+
+ // Normalize result by dividing by sqrt(norm) - this step is easiest
+ // done using floating point operation
+ if (norm == 0) norm = 1; // to avoid div by zero
+ return (long)((double)corr * SHRT_MAX / sqrt((double)norm));
+}
+
+#endif // INTEGER_SAMPLES
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Floating point arithmetics specific algorithm implementations.
+//
+
+#ifdef FLOAT_SAMPLES
+
+
+// Slopes the amplitude of the 'midBuffer' samples so that cross correlation
+// is faster to calculate
+void TDStretch::precalcCorrReferenceStereo()
+{
+ int i, cnt2;
+ float temp;
+
+ for (i=0 ; i < (int)overlapLength ;i ++)
+ {
+ temp = (float)i * (float)(overlapLength - i);
+ cnt2 = i * 2;
+ pRefMidBuffer[cnt2] = (float)(pMidBuffer[cnt2] * temp);
+ pRefMidBuffer[cnt2 + 1] = (float)(pMidBuffer[cnt2 + 1] * temp);
+ }
+}
+
+
+// Slopes the amplitude of the 'midBuffer' samples so that cross correlation
+// is faster to calculate
+void TDStretch::precalcCorrReferenceMono()
+{
+ int i;
+ float temp;
+
+ for (i=0 ; i < (int)overlapLength ;i ++)
+ {
+ temp = (float)i * (float)(overlapLength - i);
+ pRefMidBuffer[i] = (float)(pMidBuffer[i] * temp);
+ }
+}
+
+
+// Overlaps samples in 'midBuffer' with the samples in 'pInput'
+void TDStretch::overlapStereo(float *pOutput, const float *pInput) const
+{
+ int i;
+ int cnt2;
+ float fTemp;
+ float fScale;
+ float fi;
+
+ fScale = 1.0f / (float)overlapLength;
+
+ for (i = 0; i < (int)overlapLength ; i ++)
+ {
+ fTemp = (float)(overlapLength - i) * fScale;
+ fi = (float)i * fScale;
+ cnt2 = 2 * i;
+ pOutput[cnt2 + 0] = pInput[cnt2 + 0] * fi + pMidBuffer[cnt2 + 0] * fTemp;
+ pOutput[cnt2 + 1] = pInput[cnt2 + 1] * fi + pMidBuffer[cnt2 + 1] * fTemp;
+ }
+}
+
+
+/// Calculates overlapInMsec period length in samples.
+void TDStretch::calculateOverlapLength(int overlapInMsec)
+{
+ int newOvl;
+
+ assert(overlapInMsec >= 0);
+ newOvl = (sampleRate * overlapInMsec) / 1000;
+ if (newOvl < 16) newOvl = 16;
+
+ // must be divisible by 8
+ newOvl -= newOvl % 8;
+
+ acceptNewOverlapLength(newOvl);
+}
+
+
+
+double TDStretch::calcCrossCorrMono(const float *mixingPos, const float *compare) const
+{
+ double corr;
+ double norm;
+ int i;
+
+ corr = norm = 0;
+ for (i = 1; i < overlapLength; i ++)
+ {
+ corr += mixingPos[i] * compare[i];
+ norm += mixingPos[i] * mixingPos[i];
+ }
+
+ if (norm < 1e-9) norm = 1.0; // to avoid div by zero
+ return corr / sqrt(norm);
+}
+
+
+double TDStretch::calcCrossCorrStereo(const float *mixingPos, const float *compare) const
+{
+ double corr;
+ double norm;
+ int i;
+
+ corr = norm = 0;
+ for (i = 2; i < 2 * overlapLength; i += 2)
+ {
+ corr += mixingPos[i] * compare[i] +
+ mixingPos[i + 1] * compare[i + 1];
+ norm += mixingPos[i] * mixingPos[i] +
+ mixingPos[i + 1] * mixingPos[i + 1];
+ }
+
+ if (norm < 1e-9) norm = 1.0; // to avoid div by zero
+ return corr / sqrt(norm);
+}
+
+#endif // FLOAT_SAMPLES
diff --git a/plugins/dsp_soundtouch/soundtouch/source/SoundTouch/cpu_detect_x86_gcc.cpp b/plugins/dsp_soundtouch/soundtouch/source/SoundTouch/cpu_detect_x86_gcc.cpp new file mode 100644 index 00000000..b0d0a693 --- /dev/null +++ b/plugins/dsp_soundtouch/soundtouch/source/SoundTouch/cpu_detect_x86_gcc.cpp @@ -0,0 +1,135 @@ +////////////////////////////////////////////////////////////////////////////////
+///
+/// Generic version of the x86 CPU extension detection routine.
+///
+/// This file is for GNU & other non-Windows compilers, see 'cpu_detect_x86_win.cpp'
+/// for the Microsoft compiler version.
+///
+/// Author : Copyright (c) Olli Parviainen
+/// Author e-mail : oparviai 'at' iki.fi
+/// SoundTouch WWW: http://www.surina.net/soundtouch
+///
+////////////////////////////////////////////////////////////////////////////////
+//
+// Last changed : $Date: 2009-02-25 19:13:51 +0200 (Wed, 25 Feb 2009) $
+// File revision : $Revision: 4 $
+//
+// $Id: cpu_detect_x86_gcc.cpp 67 2009-02-25 17:13:51Z oparviai $
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// 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
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#include <stdexcept>
+#include <string>
+#include "cpu_detect.h"
+#include "STTypes.h"
+
+using namespace std;
+
+#include <stdio.h>
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// processor instructions extension detection routines
+//
+//////////////////////////////////////////////////////////////////////////////
+
+// Flag variable indicating whick ISA extensions are disabled (for debugging)
+static uint _dwDisabledISA = 0x00; // 0xffffffff; //<- use this to disable all extensions
+
+// Disables given set of instruction extensions. See SUPPORT_... defines.
+void disableExtensions(uint dwDisableMask)
+{
+ _dwDisabledISA = dwDisableMask;
+}
+
+
+
+/// Checks which instruction set extensions are supported by the CPU.
+uint detectCPUextensions(void)
+{
+#if (!(ALLOW_X86_OPTIMIZATIONS) || !(__GNUC__))
+
+ return 0; // always disable extensions on non-x86 platforms.
+
+#else
+ uint res = 0;
+
+ if (_dwDisabledISA == 0xffffffff) return 0;
+
+ asm volatile(
+ "\n\txor %%esi, %%esi" // clear %%esi = result register
+ // check if 'cpuid' instructions is available by toggling eflags bit 21
+
+ "\n\tpushf" // save eflags to stack
+ "\n\tmovl (%%esp), %%eax" // load eax from stack (with eflags)
+ "\n\tmovl %%eax, %%ecx" // save the original eflags values to ecx
+ "\n\txor $0x00200000, %%eax" // toggle bit 21
+ "\n\tmovl %%eax, (%%esp)" // store toggled eflags to stack
+ "\n\tpopf" // load eflags from stack
+ "\n\tpushf" // save updated eflags to stack
+ "\n\tmovl (%%esp), %%eax" // load eax from stack
+ "\n\tpopf" // pop stack to restore esp
+ "\n\txor %%edx, %%edx" // clear edx for defaulting no mmx
+ "\n\tcmp %%ecx, %%eax" // compare to original eflags values
+ "\n\tjz end" // jumps to 'end' if cpuid not present
+ // cpuid instruction available, test for presence of mmx instructions
+
+ "\n\tmovl $1, %%eax"
+ "\n\tcpuid"
+ "\n\ttest $0x00800000, %%edx"
+ "\n\tjz end" // branch if MMX not available
+
+ "\n\tor $0x01, %%esi" // otherwise add MMX support bit
+
+ "\n\ttest $0x02000000, %%edx"
+ "\n\tjz test3DNow" // branch if SSE not available
+
+ "\n\tor $0x08, %%esi" // otherwise add SSE support bit
+
+ "\n\ttest3DNow:"
+ // test for precense of AMD extensions
+ "\n\tmov $0x80000000, %%eax"
+ "\n\tcpuid"
+ "\n\tcmp $0x80000000, %%eax"
+ "\n\tjbe end" // branch if no AMD extensions detected
+
+ // test for precense of 3DNow! extension
+ "\n\tmov $0x80000001, %%eax"
+ "\n\tcpuid"
+ "\n\ttest $0x80000000, %%edx"
+ "\n\tjz end" // branch if 3DNow! not detected
+
+ "\n\tor $0x02, %%esi" // otherwise add 3DNow support bit
+
+ "\n\tend:"
+
+ "\n\tmov %%esi, %0"
+
+ : "=r" (res)
+ : /* no inputs */
+ : "%edx", "%eax", "%ecx", "%esi" );
+
+ return res & ~_dwDisabledISA;
+#endif
+}
diff --git a/plugins/dsp_soundtouch/soundtouch/source/SoundTouch/cpu_detect_x86_win.cpp b/plugins/dsp_soundtouch/soundtouch/source/SoundTouch/cpu_detect_x86_win.cpp new file mode 100644 index 00000000..c6c54246 --- /dev/null +++ b/plugins/dsp_soundtouch/soundtouch/source/SoundTouch/cpu_detect_x86_win.cpp @@ -0,0 +1,129 @@ +////////////////////////////////////////////////////////////////////////////////
+///
+/// Win32 version of the x86 CPU detect routine.
+///
+/// This file is to be compiled in Windows platform with Microsoft Visual C++
+/// Compiler. Please see 'cpu_detect_x86_gcc.cpp' for the gcc compiler version
+/// for all GNU platforms.
+///
+/// Author : Copyright (c) Olli Parviainen
+/// Author e-mail : oparviai 'at' iki.fi
+/// SoundTouch WWW: http://www.surina.net/soundtouch
+///
+////////////////////////////////////////////////////////////////////////////////
+//
+// Last changed : $Date: 2009-02-13 18:22:48 +0200 (Fri, 13 Feb 2009) $
+// File revision : $Revision: 4 $
+//
+// $Id: cpu_detect_x86_win.cpp 62 2009-02-13 16:22:48Z oparviai $
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// 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
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#include "cpu_detect.h"
+
+#ifndef WIN32
+#error wrong platform - this source code file is exclusively for Win32 platform
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// processor instructions extension detection routines
+//
+//////////////////////////////////////////////////////////////////////////////
+
+// Flag variable indicating whick ISA extensions are disabled (for debugging)
+static uint _dwDisabledISA = 0x00; // 0xffffffff; //<- use this to disable all extensions
+
+
+// Disables given set of instruction extensions. See SUPPORT_... defines.
+void disableExtensions(uint dwDisableMask)
+{
+ _dwDisabledISA = dwDisableMask;
+}
+
+
+
+/// Checks which instruction set extensions are supported by the CPU.
+uint detectCPUextensions(void)
+{
+ uint res = 0;
+
+ if (_dwDisabledISA == 0xffffffff) return 0;
+
+ _asm
+ {
+ ; check if 'cpuid' instructions is available by toggling eflags bit 21
+ ;
+ xor esi, esi ; clear esi = result register
+
+ pushfd ; save eflags to stack
+ mov eax,dword ptr [esp] ; load eax from stack (with eflags)
+ mov ecx, eax ; save the original eflags values to ecx
+ xor eax, 0x00200000 ; toggle bit 21
+ mov dword ptr [esp],eax ; store toggled eflags to stack
+ popfd ; load eflags from stack
+
+ pushfd ; save updated eflags to stack
+ mov eax,dword ptr [esp] ; load eax from stack
+ popfd ; pop stack to restore stack pointer
+
+ xor edx, edx ; clear edx for defaulting no mmx
+ cmp eax, ecx ; compare to original eflags values
+ jz end ; jumps to 'end' if cpuid not present
+
+ ; cpuid instruction available, test for presence of mmx instructions
+ mov eax, 1
+ cpuid
+ test edx, 0x00800000
+ jz end ; branch if MMX not available
+
+ or esi, SUPPORT_MMX ; otherwise add MMX support bit
+
+ test edx, 0x02000000
+ jz test3DNow ; branch if SSE not available
+
+ or esi, SUPPORT_SSE ; otherwise add SSE support bit
+
+ test3DNow:
+ ; test for precense of AMD extensions
+ mov eax, 0x80000000
+ cpuid
+ cmp eax, 0x80000000
+ jbe end ; branch if no AMD extensions detected
+
+ ; test for precense of 3DNow! extension
+ mov eax, 0x80000001
+ cpuid
+ test edx, 0x80000000
+ jz end ; branch if 3DNow! not detected
+
+ or esi, SUPPORT_3DNOW ; otherwise add 3DNow support bit
+
+ end:
+
+ mov res, esi
+ }
+
+ return res & ~_dwDisabledISA;
+}
diff --git a/plugins/dsp_soundtouch/soundtouch/source/SoundTouch/mmx_optimized.cpp b/plugins/dsp_soundtouch/soundtouch/source/SoundTouch/mmx_optimized.cpp new file mode 100644 index 00000000..539ee57c --- /dev/null +++ b/plugins/dsp_soundtouch/soundtouch/source/SoundTouch/mmx_optimized.cpp @@ -0,0 +1,320 @@ +////////////////////////////////////////////////////////////////////////////////
+///
+/// MMX optimized routines. All MMX optimized functions have been gathered into
+/// this single source code file, regardless to their class or original source
+/// code file, in order to ease porting the library to other compiler and
+/// processor platforms.
+///
+/// The MMX-optimizations are programmed using MMX compiler intrinsics that
+/// are supported both by Microsoft Visual C++ and GCC compilers, so this file
+/// should compile with both toolsets.
+///
+/// NOTICE: If using Visual Studio 6.0, you'll need to install the "Visual C++
+/// 6.0 processor pack" update to support compiler intrinsic syntax. The update
+/// is available for download at Microsoft Developers Network, see here:
+/// http://msdn.microsoft.com/en-us/vstudio/aa718349.aspx
+///
+/// Author : Copyright (c) Olli Parviainen
+/// Author e-mail : oparviai 'at' iki.fi
+/// SoundTouch WWW: http://www.surina.net/soundtouch
+///
+////////////////////////////////////////////////////////////////////////////////
+//
+// Last changed : $Date: 2009-10-31 16:53:23 +0200 (Sat, 31 Oct 2009) $
+// File revision : $Revision: 4 $
+//
+// $Id: mmx_optimized.cpp 75 2009-10-31 14:53:23Z oparviai $
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// 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
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#include "STTypes.h"
+
+#ifdef ALLOW_MMX
+// MMX routines available only with integer sample type
+
+#if !(WIN32 || __i386__ || __x86_64__)
+#error "wrong platform - this source code file is exclusively for x86 platforms"
+#endif
+
+using namespace soundtouch;
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// implementation of MMX optimized functions of class 'TDStretchMMX'
+//
+//////////////////////////////////////////////////////////////////////////////
+
+#include "TDStretch.h"
+#include <mmintrin.h>
+#include <limits.h>
+#include <math.h>
+
+
+// Calculates cross correlation of two buffers
+long TDStretchMMX::calcCrossCorrStereo(const short *pV1, const short *pV2) const
+{
+ const __m64 *pVec1, *pVec2;
+ __m64 shifter;
+ __m64 accu, normaccu;
+ long corr, norm;
+ int i;
+
+ pVec1 = (__m64*)pV1;
+ pVec2 = (__m64*)pV2;
+
+ shifter = _m_from_int(overlapDividerBits);
+ normaccu = accu = _mm_setzero_si64();
+
+ // Process 4 parallel sets of 2 * stereo samples each during each
+ // round to improve CPU-level parallellization.
+ for (i = 0; i < overlapLength / 8; i ++)
+ {
+ __m64 temp, temp2;
+
+ // dictionary of instructions:
+ // _m_pmaddwd : 4*16bit multiply-add, resulting two 32bits = [a0*b0+a1*b1 ; a2*b2+a3*b3]
+ // _mm_add_pi32 : 2*32bit add
+ // _m_psrad : 32bit right-shift
+
+ temp = _mm_add_pi32(_mm_madd_pi16(pVec1[0], pVec2[0]),
+ _mm_madd_pi16(pVec1[1], pVec2[1]));
+ temp2 = _mm_add_pi32(_mm_madd_pi16(pVec1[0], pVec1[0]),
+ _mm_madd_pi16(pVec1[1], pVec1[1]));
+ accu = _mm_add_pi32(accu, _mm_sra_pi32(temp, shifter));
+ normaccu = _mm_add_pi32(normaccu, _mm_sra_pi32(temp2, shifter));
+
+ temp = _mm_add_pi32(_mm_madd_pi16(pVec1[2], pVec2[2]),
+ _mm_madd_pi16(pVec1[3], pVec2[3]));
+ temp2 = _mm_add_pi32(_mm_madd_pi16(pVec1[2], pVec1[2]),
+ _mm_madd_pi16(pVec1[3], pVec1[3]));
+ accu = _mm_add_pi32(accu, _mm_sra_pi32(temp, shifter));
+ normaccu = _mm_add_pi32(normaccu, _mm_sra_pi32(temp2, shifter));
+
+ pVec1 += 4;
+ pVec2 += 4;
+ }
+
+ // copy hi-dword of mm0 to lo-dword of mm1, then sum mmo+mm1
+ // and finally store the result into the variable "corr"
+
+ accu = _mm_add_pi32(accu, _mm_srli_si64(accu, 32));
+ corr = _m_to_int(accu);
+
+ normaccu = _mm_add_pi32(normaccu, _mm_srli_si64(normaccu, 32));
+ norm = _m_to_int(normaccu);
+
+ // Clear MMS state
+ _m_empty();
+
+ // Normalize result by dividing by sqrt(norm) - this step is easiest
+ // done using floating point operation
+ if (norm == 0) norm = 1; // to avoid div by zero
+ return (long)((double)corr * USHRT_MAX / sqrt((double)norm));
+ // Note: Warning about the missing EMMS instruction is harmless
+ // as it'll be called elsewhere.
+}
+
+
+
+void TDStretchMMX::clearCrossCorrState()
+{
+ // Clear MMS state
+ _m_empty();
+ //_asm EMMS;
+}
+
+
+
+// MMX-optimized version of the function overlapStereo
+void TDStretchMMX::overlapStereo(short *output, const short *input) const
+{
+ const __m64 *pVinput, *pVMidBuf;
+ __m64 *pVdest;
+ __m64 mix1, mix2, adder, shifter;
+ int i;
+
+ pVinput = (const __m64*)input;
+ pVMidBuf = (const __m64*)pMidBuffer;
+ pVdest = (__m64*)output;
+
+ // mix1 = mixer values for 1st stereo sample
+ // mix1 = mixer values for 2nd stereo sample
+ // adder = adder for updating mixer values after each round
+
+ mix1 = _mm_set_pi16(0, overlapLength, 0, overlapLength);
+ adder = _mm_set_pi16(1, -1, 1, -1);
+ mix2 = _mm_add_pi16(mix1, adder);
+ adder = _mm_add_pi16(adder, adder);
+
+ // Overlaplength-division by shifter. "+1" is to account for "-1" deduced in
+ // overlapDividerBits calculation earlier.
+ shifter = _m_from_int(overlapDividerBits + 1);
+
+ for (i = 0; i < overlapLength / 4; i ++)
+ {
+ __m64 temp1, temp2;
+
+ // load & shuffle data so that input & mixbuffer data samples are paired
+ temp1 = _mm_unpacklo_pi16(pVMidBuf[0], pVinput[0]); // = i0l m0l i0r m0r
+ temp2 = _mm_unpackhi_pi16(pVMidBuf[0], pVinput[0]); // = i1l m1l i1r m1r
+
+ // temp = (temp .* mix) >> shifter
+ temp1 = _mm_sra_pi32(_mm_madd_pi16(temp1, mix1), shifter);
+ temp2 = _mm_sra_pi32(_mm_madd_pi16(temp2, mix2), shifter);
+ pVdest[0] = _mm_packs_pi32(temp1, temp2); // pack 2*2*32bit => 4*16bit
+
+ // update mix += adder
+ mix1 = _mm_add_pi16(mix1, adder);
+ mix2 = _mm_add_pi16(mix2, adder);
+
+ // --- second round begins here ---
+
+ // load & shuffle data so that input & mixbuffer data samples are paired
+ temp1 = _mm_unpacklo_pi16(pVMidBuf[1], pVinput[1]); // = i2l m2l i2r m2r
+ temp2 = _mm_unpackhi_pi16(pVMidBuf[1], pVinput[1]); // = i3l m3l i3r m3r
+
+ // temp = (temp .* mix) >> shifter
+ temp1 = _mm_sra_pi32(_mm_madd_pi16(temp1, mix1), shifter);
+ temp2 = _mm_sra_pi32(_mm_madd_pi16(temp2, mix2), shifter);
+ pVdest[1] = _mm_packs_pi32(temp1, temp2); // pack 2*2*32bit => 4*16bit
+
+ // update mix += adder
+ mix1 = _mm_add_pi16(mix1, adder);
+ mix2 = _mm_add_pi16(mix2, adder);
+
+ pVinput += 2;
+ pVMidBuf += 2;
+ pVdest += 2;
+ }
+
+ _m_empty(); // clear MMS state
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// implementation of MMX optimized functions of class 'FIRFilter'
+//
+//////////////////////////////////////////////////////////////////////////////
+
+#include "FIRFilter.h"
+
+
+FIRFilterMMX::FIRFilterMMX() : FIRFilter()
+{
+ filterCoeffsUnalign = NULL;
+}
+
+
+FIRFilterMMX::~FIRFilterMMX()
+{
+ delete[] filterCoeffsUnalign;
+}
+
+
+// (overloaded) Calculates filter coefficients for MMX routine
+void FIRFilterMMX::setCoefficients(const short *coeffs, uint newLength, uint uResultDivFactor)
+{
+ uint i;
+ FIRFilter::setCoefficients(coeffs, newLength, uResultDivFactor);
+
+ // Ensure that filter coeffs array is aligned to 16-byte boundary
+ delete[] filterCoeffsUnalign;
+ filterCoeffsUnalign = new short[2 * newLength + 8];
+ filterCoeffsAlign = (short *)(((ulong)filterCoeffsUnalign + 15) & -16);
+
+ // rearrange the filter coefficients for mmx routines
+ for (i = 0;i < length; i += 4)
+ {
+ filterCoeffsAlign[2 * i + 0] = coeffs[i + 0];
+ filterCoeffsAlign[2 * i + 1] = coeffs[i + 2];
+ filterCoeffsAlign[2 * i + 2] = coeffs[i + 0];
+ filterCoeffsAlign[2 * i + 3] = coeffs[i + 2];
+
+ filterCoeffsAlign[2 * i + 4] = coeffs[i + 1];
+ filterCoeffsAlign[2 * i + 5] = coeffs[i + 3];
+ filterCoeffsAlign[2 * i + 6] = coeffs[i + 1];
+ filterCoeffsAlign[2 * i + 7] = coeffs[i + 3];
+ }
+}
+
+
+
+// mmx-optimized version of the filter routine for stereo sound
+uint FIRFilterMMX::evaluateFilterStereo(short *dest, const short *src, uint numSamples) const
+{
+ // Create stack copies of the needed member variables for asm routines :
+ uint i, j;
+ __m64 *pVdest = (__m64*)dest;
+
+ if (length < 2) return 0;
+
+ for (i = 0; i < (numSamples - length) / 2; i ++)
+ {
+ __m64 accu1;
+ __m64 accu2;
+ const __m64 *pVsrc = (const __m64*)src;
+ const __m64 *pVfilter = (const __m64*)filterCoeffsAlign;
+
+ accu1 = accu2 = _mm_setzero_si64();
+ for (j = 0; j < lengthDiv8 * 2; j ++)
+ {
+ __m64 temp1, temp2;
+
+ temp1 = _mm_unpacklo_pi16(pVsrc[0], pVsrc[1]); // = l2 l0 r2 r0
+ temp2 = _mm_unpackhi_pi16(pVsrc[0], pVsrc[1]); // = l3 l1 r3 r1
+
+ accu1 = _mm_add_pi32(accu1, _mm_madd_pi16(temp1, pVfilter[0])); // += l2*f2+l0*f0 r2*f2+r0*f0
+ accu1 = _mm_add_pi32(accu1, _mm_madd_pi16(temp2, pVfilter[1])); // += l3*f3+l1*f1 r3*f3+r1*f1
+
+ temp1 = _mm_unpacklo_pi16(pVsrc[1], pVsrc[2]); // = l4 l2 r4 r2
+
+ accu2 = _mm_add_pi32(accu2, _mm_madd_pi16(temp2, pVfilter[0])); // += l3*f2+l1*f0 r3*f2+r1*f0
+ accu2 = _mm_add_pi32(accu2, _mm_madd_pi16(temp1, pVfilter[1])); // += l4*f3+l2*f1 r4*f3+r2*f1
+
+ // accu1 += l2*f2+l0*f0 r2*f2+r0*f0
+ // += l3*f3+l1*f1 r3*f3+r1*f1
+
+ // accu2 += l3*f2+l1*f0 r3*f2+r1*f0
+ // l4*f3+l2*f1 r4*f3+r2*f1
+
+ pVfilter += 2;
+ pVsrc += 2;
+ }
+ // accu >>= resultDivFactor
+ accu1 = _mm_srai_pi32(accu1, resultDivFactor);
+ accu2 = _mm_srai_pi32(accu2, resultDivFactor);
+
+ // pack 2*2*32bits => 4*16 bits
+ pVdest[0] = _mm_packs_pi32(accu1, accu2);
+ src += 4;
+ pVdest ++;
+ }
+
+ _m_empty(); // clear emms state
+
+ return (numSamples & 0xfffffffe) - length;
+}
+
+#endif // ALLOW_MMX
diff --git a/plugins/dsp_soundtouch/soundtouch/source/SoundTouch/sse_optimized.cpp b/plugins/dsp_soundtouch/soundtouch/source/SoundTouch/sse_optimized.cpp new file mode 100644 index 00000000..7659be68 --- /dev/null +++ b/plugins/dsp_soundtouch/soundtouch/source/SoundTouch/sse_optimized.cpp @@ -0,0 +1,510 @@ +////////////////////////////////////////////////////////////////////////////////
+///
+/// SSE optimized routines for Pentium-III, Athlon-XP and later CPUs. All SSE
+/// optimized functions have been gathered into this single source
+/// code file, regardless to their class or original source code file, in order
+/// to ease porting the library to other compiler and processor platforms.
+///
+/// The SSE-optimizations are programmed using SSE compiler intrinsics that
+/// are supported both by Microsoft Visual C++ and GCC compilers, so this file
+/// should compile with both toolsets.
+///
+/// NOTICE: If using Visual Studio 6.0, you'll need to install the "Visual C++
+/// 6.0 processor pack" update to support SSE instruction set. The update is
+/// available for download at Microsoft Developers Network, see here:
+/// http://msdn.microsoft.com/en-us/vstudio/aa718349.aspx
+///
+/// If the above URL is expired or removed, go to "http://msdn.microsoft.com" and
+/// perform a search with keywords "processor pack".
+///
+/// Author : Copyright (c) Olli Parviainen
+/// Author e-mail : oparviai 'at' iki.fi
+/// SoundTouch WWW: http://www.surina.net/soundtouch
+///
+////////////////////////////////////////////////////////////////////////////////
+//
+// Last changed : $Date: 2009-12-28 22:32:57 +0200 (Mon, 28 Dec 2009) $
+// File revision : $Revision: 4 $
+//
+// $Id: sse_optimized.cpp 80 2009-12-28 20:32:57Z oparviai $
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// 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
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#include "cpu_detect.h"
+#include "STTypes.h"
+
+using namespace soundtouch;
+
+#ifdef ALLOW_SSE
+
+// SSE routines available only with float sample type
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// implementation of SSE optimized functions of class 'TDStretchSSE'
+//
+//////////////////////////////////////////////////////////////////////////////
+
+#include "TDStretch.h"
+#include <xmmintrin.h>
+#include <math.h>
+
+// Calculates cross correlation of two buffers
+double TDStretchSSE::calcCrossCorrStereo(const float *pV1, const float *pV2) const
+{
+ int i;
+ const float *pVec1;
+ const __m128 *pVec2;
+ __m128 vSum, vNorm;
+
+ // Note. It means a major slow-down if the routine needs to tolerate
+ // unaligned __m128 memory accesses. It's way faster if we can skip
+ // unaligned slots and use _mm_load_ps instruction instead of _mm_loadu_ps.
+ // This can mean up to ~ 10-fold difference (incl. part of which is
+ // due to skipping every second round for stereo sound though).
+ //
+ // Compile-time define ALLOW_NONEXACT_SIMD_OPTIMIZATION is provided
+ // for choosing if this little cheating is allowed.
+
+#ifdef ALLOW_NONEXACT_SIMD_OPTIMIZATION
+ // Little cheating allowed, return valid correlation only for
+ // aligned locations, meaning every second round for stereo sound.
+
+ #define _MM_LOAD _mm_load_ps
+
+ if (((ulong)pV1) & 15) return -1e50; // skip unaligned locations
+
+#else
+ // No cheating allowed, use unaligned load & take the resulting
+ // performance hit.
+ #define _MM_LOAD _mm_loadu_ps
+#endif
+
+ // ensure overlapLength is divisible by 8
+ assert((overlapLength % 8) == 0);
+
+ // Calculates the cross-correlation value between 'pV1' and 'pV2' vectors
+ // Note: pV2 _must_ be aligned to 16-bit boundary, pV1 need not.
+ pVec1 = (const float*)pV1;
+ pVec2 = (const __m128*)pV2;
+ vSum = vNorm = _mm_setzero_ps();
+
+ // Unroll the loop by factor of 4 * 4 operations
+ for (i = 0; i < overlapLength / 8; i ++)
+ {
+ __m128 vTemp;
+ // vSum += pV1[0..3] * pV2[0..3]
+ vTemp = _MM_LOAD(pVec1);
+ vSum = _mm_add_ps(vSum, _mm_mul_ps(vTemp ,pVec2[0]));
+ vNorm = _mm_add_ps(vNorm, _mm_mul_ps(vTemp ,vTemp));
+
+ // vSum += pV1[4..7] * pV2[4..7]
+ vTemp = _MM_LOAD(pVec1 + 4);
+ vSum = _mm_add_ps(vSum, _mm_mul_ps(vTemp, pVec2[1]));
+ vNorm = _mm_add_ps(vNorm, _mm_mul_ps(vTemp ,vTemp));
+
+ // vSum += pV1[8..11] * pV2[8..11]
+ vTemp = _MM_LOAD(pVec1 + 8);
+ vSum = _mm_add_ps(vSum, _mm_mul_ps(vTemp, pVec2[2]));
+ vNorm = _mm_add_ps(vNorm, _mm_mul_ps(vTemp ,vTemp));
+
+ // vSum += pV1[12..15] * pV2[12..15]
+ vTemp = _MM_LOAD(pVec1 + 12);
+ vSum = _mm_add_ps(vSum, _mm_mul_ps(vTemp, pVec2[3]));
+ vNorm = _mm_add_ps(vNorm, _mm_mul_ps(vTemp ,vTemp));
+
+ pVec1 += 16;
+ pVec2 += 4;
+ }
+
+ // return value = vSum[0] + vSum[1] + vSum[2] + vSum[3]
+ float *pvNorm = (float*)&vNorm;
+ double norm = sqrt(pvNorm[0] + pvNorm[1] + pvNorm[2] + pvNorm[3]);
+ if (norm < 1e-9) norm = 1.0; // to avoid div by zero
+
+ float *pvSum = (float*)&vSum;
+ return (double)(pvSum[0] + pvSum[1] + pvSum[2] + pvSum[3]) / norm;
+
+ /* This is approximately corresponding routine in C-language yet without normalization:
+ double corr, norm;
+ uint i;
+
+ // Calculates the cross-correlation value between 'pV1' and 'pV2' vectors
+ corr = norm = 0.0;
+ for (i = 0; i < overlapLength / 8; i ++)
+ {
+ corr += pV1[0] * pV2[0] +
+ pV1[1] * pV2[1] +
+ pV1[2] * pV2[2] +
+ pV1[3] * pV2[3] +
+ pV1[4] * pV2[4] +
+ pV1[5] * pV2[5] +
+ pV1[6] * pV2[6] +
+ pV1[7] * pV2[7] +
+ pV1[8] * pV2[8] +
+ pV1[9] * pV2[9] +
+ pV1[10] * pV2[10] +
+ pV1[11] * pV2[11] +
+ pV1[12] * pV2[12] +
+ pV1[13] * pV2[13] +
+ pV1[14] * pV2[14] +
+ pV1[15] * pV2[15];
+
+ for (j = 0; j < 15; j ++) norm += pV1[j] * pV1[j];
+
+ pV1 += 16;
+ pV2 += 16;
+ }
+ return corr / sqrt(norm);
+ */
+
+ /* This is a bit outdated, corresponding routine in assembler. This may be teeny-weeny bit
+ faster than intrinsic version, but more difficult to maintain & get compiled on multiple
+ platforms.
+
+ uint overlapLengthLocal = overlapLength;
+ float corr;
+
+ _asm
+ {
+ // Very important note: data in 'pV2' _must_ be aligned to
+ // 16-byte boundary!
+
+ // give prefetch hints to CPU of what data are to be needed soonish
+ // give more aggressive hints on pV1 as that changes while pV2 stays
+ // same between runs
+ prefetcht0 [pV1]
+ prefetcht0 [pV2]
+ prefetcht0 [pV1 + 32]
+
+ mov eax, dword ptr pV1
+ mov ebx, dword ptr pV2
+
+ xorps xmm0, xmm0
+
+ mov ecx, overlapLengthLocal
+ shr ecx, 3 // div by eight
+
+ loop1:
+ prefetcht0 [eax + 64] // give a prefetch hint to CPU what data are to be needed soonish
+ prefetcht0 [ebx + 32] // give a prefetch hint to CPU what data are to be needed soonish
+ movups xmm1, [eax]
+ mulps xmm1, [ebx]
+ addps xmm0, xmm1
+
+ movups xmm2, [eax + 16]
+ mulps xmm2, [ebx + 16]
+ addps xmm0, xmm2
+
+ prefetcht0 [eax + 96] // give a prefetch hint to CPU what data are to be needed soonish
+ prefetcht0 [ebx + 64] // give a prefetch hint to CPU what data are to be needed soonish
+
+ movups xmm3, [eax + 32]
+ mulps xmm3, [ebx + 32]
+ addps xmm0, xmm3
+
+ movups xmm4, [eax + 48]
+ mulps xmm4, [ebx + 48]
+ addps xmm0, xmm4
+
+ add eax, 64
+ add ebx, 64
+
+ dec ecx
+ jnz loop1
+
+ // add the four floats of xmm0 together and return the result.
+
+ movhlps xmm1, xmm0 // move 3 & 4 of xmm0 to 1 & 2 of xmm1
+ addps xmm1, xmm0
+ movaps xmm2, xmm1
+ shufps xmm2, xmm2, 0x01 // move 2 of xmm2 as 1 of xmm2
+ addss xmm2, xmm1
+ movss corr, xmm2
+ }
+
+ return (double)corr;
+ */
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// implementation of SSE optimized functions of class 'FIRFilter'
+//
+//////////////////////////////////////////////////////////////////////////////
+
+#include "FIRFilter.h"
+
+FIRFilterSSE::FIRFilterSSE() : FIRFilter()
+{
+ filterCoeffsAlign = NULL;
+ filterCoeffsUnalign = NULL;
+}
+
+
+FIRFilterSSE::~FIRFilterSSE()
+{
+ delete[] filterCoeffsUnalign;
+ filterCoeffsAlign = NULL;
+ filterCoeffsUnalign = NULL;
+}
+
+
+// (overloaded) Calculates filter coefficients for SSE routine
+void FIRFilterSSE::setCoefficients(const float *coeffs, uint newLength, uint uResultDivFactor)
+{
+ uint i;
+ float fDivider;
+
+ FIRFilter::setCoefficients(coeffs, newLength, uResultDivFactor);
+
+ // Scale the filter coefficients so that it won't be necessary to scale the filtering result
+ // also rearrange coefficients suitably for 3DNow!
+ // Ensure that filter coeffs array is aligned to 16-byte boundary
+ delete[] filterCoeffsUnalign;
+ filterCoeffsUnalign = new float[2 * newLength + 4];
+ filterCoeffsAlign = (float *)(((unsigned long)filterCoeffsUnalign + 15) & (ulong)-16);
+
+ fDivider = (float)resultDivider;
+
+ // rearrange the filter coefficients for mmx routines
+ for (i = 0; i < newLength; i ++)
+ {
+ filterCoeffsAlign[2 * i + 0] =
+ filterCoeffsAlign[2 * i + 1] = coeffs[i + 0] / fDivider;
+ }
+}
+
+
+
+// SSE-optimized version of the filter routine for stereo sound
+uint FIRFilterSSE::evaluateFilterStereo(float *dest, const float *source, uint numSamples) const
+{
+ int count = (int)((numSamples - length) & (uint)-2);
+ int j;
+
+ assert(count % 2 == 0);
+
+ if (count < 2) return 0;
+
+ assert(source != NULL);
+ assert(dest != NULL);
+ assert((length % 8) == 0);
+ assert(filterCoeffsAlign != NULL);
+ assert(((ulong)filterCoeffsAlign) % 16 == 0);
+
+ // filter is evaluated for two stereo samples with each iteration, thus use of 'j += 2'
+ for (j = 0; j < count; j += 2)
+ {
+ const float *pSrc;
+ const __m128 *pFil;
+ __m128 sum1, sum2;
+ uint i;
+
+ pSrc = (const float*)source; // source audio data
+ pFil = (const __m128*)filterCoeffsAlign; // filter coefficients. NOTE: Assumes coefficients
+ // are aligned to 16-byte boundary
+ sum1 = sum2 = _mm_setzero_ps();
+
+ for (i = 0; i < length / 8; i ++)
+ {
+ // Unroll loop for efficiency & calculate filter for 2*2 stereo samples
+ // at each pass
+
+ // sum1 is accu for 2*2 filtered stereo sound data at the primary sound data offset
+ // sum2 is accu for 2*2 filtered stereo sound data for the next sound sample offset.
+
+ sum1 = _mm_add_ps(sum1, _mm_mul_ps(_mm_loadu_ps(pSrc) , pFil[0]));
+ sum2 = _mm_add_ps(sum2, _mm_mul_ps(_mm_loadu_ps(pSrc + 2), pFil[0]));
+
+ sum1 = _mm_add_ps(sum1, _mm_mul_ps(_mm_loadu_ps(pSrc + 4), pFil[1]));
+ sum2 = _mm_add_ps(sum2, _mm_mul_ps(_mm_loadu_ps(pSrc + 6), pFil[1]));
+
+ sum1 = _mm_add_ps(sum1, _mm_mul_ps(_mm_loadu_ps(pSrc + 8) , pFil[2]));
+ sum2 = _mm_add_ps(sum2, _mm_mul_ps(_mm_loadu_ps(pSrc + 10), pFil[2]));
+
+ sum1 = _mm_add_ps(sum1, _mm_mul_ps(_mm_loadu_ps(pSrc + 12), pFil[3]));
+ sum2 = _mm_add_ps(sum2, _mm_mul_ps(_mm_loadu_ps(pSrc + 14), pFil[3]));
+
+ pSrc += 16;
+ pFil += 4;
+ }
+
+ // Now sum1 and sum2 both have a filtered 2-channel sample each, but we still need
+ // to sum the two hi- and lo-floats of these registers together.
+
+ // post-shuffle & add the filtered values and store to dest.
+ _mm_storeu_ps(dest, _mm_add_ps(
+ _mm_shuffle_ps(sum1, sum2, _MM_SHUFFLE(1,0,3,2)), // s2_1 s2_0 s1_3 s1_2
+ _mm_shuffle_ps(sum1, sum2, _MM_SHUFFLE(3,2,1,0)) // s2_3 s2_2 s1_1 s1_0
+ ));
+ source += 4;
+ dest += 4;
+ }
+
+ // Ideas for further improvement:
+ // 1. If it could be guaranteed that 'source' were always aligned to 16-byte
+ // boundary, a faster aligned '_mm_load_ps' instruction could be used.
+ // 2. If it could be guaranteed that 'dest' were always aligned to 16-byte
+ // boundary, a faster '_mm_store_ps' instruction could be used.
+
+ return (uint)count;
+
+ /* original routine in C-language. please notice the C-version has differently
+ organized coefficients though.
+ double suml1, suml2;
+ double sumr1, sumr2;
+ uint i, j;
+
+ for (j = 0; j < count; j += 2)
+ {
+ const float *ptr;
+ const float *pFil;
+
+ suml1 = sumr1 = 0.0;
+ suml2 = sumr2 = 0.0;
+ ptr = src;
+ pFil = filterCoeffs;
+ for (i = 0; i < lengthLocal; i ++)
+ {
+ // unroll loop for efficiency.
+
+ suml1 += ptr[0] * pFil[0] +
+ ptr[2] * pFil[2] +
+ ptr[4] * pFil[4] +
+ ptr[6] * pFil[6];
+
+ sumr1 += ptr[1] * pFil[1] +
+ ptr[3] * pFil[3] +
+ ptr[5] * pFil[5] +
+ ptr[7] * pFil[7];
+
+ suml2 += ptr[8] * pFil[0] +
+ ptr[10] * pFil[2] +
+ ptr[12] * pFil[4] +
+ ptr[14] * pFil[6];
+
+ sumr2 += ptr[9] * pFil[1] +
+ ptr[11] * pFil[3] +
+ ptr[13] * pFil[5] +
+ ptr[15] * pFil[7];
+
+ ptr += 16;
+ pFil += 8;
+ }
+ dest[0] = (float)suml1;
+ dest[1] = (float)sumr1;
+ dest[2] = (float)suml2;
+ dest[3] = (float)sumr2;
+
+ src += 4;
+ dest += 4;
+ }
+ */
+
+
+ /* Similar routine in assembly, again obsoleted due to maintainability
+ _asm
+ {
+ // Very important note: data in 'src' _must_ be aligned to
+ // 16-byte boundary!
+ mov edx, count
+ mov ebx, dword ptr src
+ mov eax, dword ptr dest
+ shr edx, 1
+
+ loop1:
+ // "outer loop" : during each round 2*2 output samples are calculated
+
+ // give prefetch hints to CPU of what data are to be needed soonish
+ prefetcht0 [ebx]
+ prefetcht0 [filterCoeffsLocal]
+
+ mov esi, ebx
+ mov edi, filterCoeffsLocal
+ xorps xmm0, xmm0
+ xorps xmm1, xmm1
+ mov ecx, lengthLocal
+
+ loop2:
+ // "inner loop" : during each round eight FIR filter taps are evaluated for 2*2 samples
+ prefetcht0 [esi + 32] // give a prefetch hint to CPU what data are to be needed soonish
+ prefetcht0 [edi + 32] // give a prefetch hint to CPU what data are to be needed soonish
+
+ movups xmm2, [esi] // possibly unaligned load
+ movups xmm3, [esi + 8] // possibly unaligned load
+ mulps xmm2, [edi]
+ mulps xmm3, [edi]
+ addps xmm0, xmm2
+ addps xmm1, xmm3
+
+ movups xmm4, [esi + 16] // possibly unaligned load
+ movups xmm5, [esi + 24] // possibly unaligned load
+ mulps xmm4, [edi + 16]
+ mulps xmm5, [edi + 16]
+ addps xmm0, xmm4
+ addps xmm1, xmm5
+
+ prefetcht0 [esi + 64] // give a prefetch hint to CPU what data are to be needed soonish
+ prefetcht0 [edi + 64] // give a prefetch hint to CPU what data are to be needed soonish
+
+ movups xmm6, [esi + 32] // possibly unaligned load
+ movups xmm7, [esi + 40] // possibly unaligned load
+ mulps xmm6, [edi + 32]
+ mulps xmm7, [edi + 32]
+ addps xmm0, xmm6
+ addps xmm1, xmm7
+
+ movups xmm4, [esi + 48] // possibly unaligned load
+ movups xmm5, [esi + 56] // possibly unaligned load
+ mulps xmm4, [edi + 48]
+ mulps xmm5, [edi + 48]
+ addps xmm0, xmm4
+ addps xmm1, xmm5
+
+ add esi, 64
+ add edi, 64
+ dec ecx
+ jnz loop2
+
+ // Now xmm0 and xmm1 both have a filtered 2-channel sample each, but we still need
+ // to sum the two hi- and lo-floats of these registers together.
+
+ movhlps xmm2, xmm0 // xmm2 = xmm2_3 xmm2_2 xmm0_3 xmm0_2
+ movlhps xmm2, xmm1 // xmm2 = xmm1_1 xmm1_0 xmm0_3 xmm0_2
+ shufps xmm0, xmm1, 0xe4 // xmm0 = xmm1_3 xmm1_2 xmm0_1 xmm0_0
+ addps xmm0, xmm2
+
+ movaps [eax], xmm0
+ add ebx, 16
+ add eax, 16
+
+ dec edx
+ jnz loop1
+ }
+ */
+}
+
+#endif // ALLOW_SSE
diff --git a/plugins/dsp_soundtouch/st.cpp b/plugins/dsp_soundtouch/st.cpp new file mode 100644 index 00000000..2ef2db15 --- /dev/null +++ b/plugins/dsp_soundtouch/st.cpp @@ -0,0 +1,112 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#include <SoundTouch.h> +#include "st.h" + +using namespace soundtouch; + +void* +st_alloc (void) { + return new SoundTouch (); +} + +void +st_free (void *st) { + delete (SoundTouch *)st; +} + +void +st_set_rate (void *st, float r) { + ((SoundTouch *)st)->setRate (r); +} + +void +st_set_tempo (void *st, float t) { + ((SoundTouch *)st)->setTempo (t); +} + +void +st_set_rate_change (void *st, float r) { + ((SoundTouch *)st)->setRateChange (r); +} + +void +st_set_tempo_change (void *st, float t) { + ((SoundTouch *)st)->setTempoChange (t); +} + +void +st_set_pitch (void *st, float p) { + ((SoundTouch *)st)->setPitch (p); +} + +void +st_set_pitch_octaves (void *st, float po) { + ((SoundTouch *)st)->setPitchOctaves (po); +} + +void +st_set_pitch_semi_tones (void *st, float p) { + ((SoundTouch *)st)->setPitchSemiTones (p); +} + +void +st_set_channels (void *st, int c) { + ((SoundTouch *)st)->setChannels (c); +} + +void +st_set_sample_rate (void *st, int r) { + ((SoundTouch *)st)->setSampleRate (r); +} + +void +st_flush (void *st) { + ((SoundTouch *)st)->flush (); +} + +void +st_put_samples (void *st, float *samples, int nsamples) { + ((SoundTouch *)st)->putSamples (samples, nsamples); +} + +void +st_clear (void *st) { + ((SoundTouch *)st)->clear (); +} + +void +st_set_setting (void *st, int id, int value) { + ((SoundTouch *)st)->setSetting (id, value); +} + +int +st_get_setting (void *st, int id) { + return ((SoundTouch *)st)->getSetting (id); +} + +unsigned int +st_num_unprocessed_samples (void *st) { + return ((SoundTouch *)st)->numUnprocessedSamples (); +} + +unsigned int +st_receive_samples (void *st, float *out, unsigned int max_samples) { + return ((SoundTouch *)st)->receiveSamples (out, max_samples); +} diff --git a/plugins/dsp_soundtouch/st.h b/plugins/dsp_soundtouch/st.h new file mode 100644 index 00000000..bd50f72a --- /dev/null +++ b/plugins/dsp_soundtouch/st.h @@ -0,0 +1,115 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#ifndef __ST_H +#define __ST_H + +////////////////////////////////// +// C-wrapper for soundtouch class + +/// 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 + +#ifdef __cplusplus +extern "C" { +#endif + +void* +st_alloc (void); + +void +st_free (void *st); + +void +st_set_rate (void *st, float r); + +void +st_set_tempo (void *st, float t); + +void +st_set_rate_change (void *st, float r); + +void +st_set_tempo_change (void *st, float t); + +void +st_set_pitch (void *st, float p); + +void +st_set_pitch_octaves (void *st, float po); + +void +st_set_pitch_semi_tones (void *st, float p); + +void +st_set_channels (void *st, int c); + +void +st_set_sample_rate (void *st, int r); + +void +st_flush (void *st); + +void +st_put_samples (void *st, float *samples, int nsamples); + +void +st_clear (void *st); + +void +st_set_setting (void *st, int id, int value); + +int +st_get_setting (void *st, int id); + +unsigned int +st_num_unprocessed_samples (void *st); + +unsigned int +st_receive_samples (void *st, float *out, unsigned int max_samples); + +#ifdef __cplusplus +} +#endif + +#endif |