Initial commit as per SoundTouch v1.3.1

This commit is contained in:
oparviai 2008-02-10 16:24:28 +00:00
parent 90c3d74e04
commit 2b64de964c
55 changed files with 10506 additions and 0 deletions

458
COPYING.TXT Normal file
View File

@ -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 authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS

70
Makefile.am Normal file
View File

@ -0,0 +1,70 @@
## Process this file with automake to create Makefile.in
##
## $Id: Makefile.am 4 2008-02-10 16:07:12Z oparviai $
##
## Copyright (C) 2003 - David W. Durham
##
## This file is part of SoundTouch, an audio processing library for pitch/time adjustments
##
## SoundTouch is free software; you can redistribute it and/or modify it under the
## terms of the GNU General Public License as published by the Free Software
## Foundation; either version 2 of the License, or (at your option) any later
## version.
##
## SoundTouch is distributed in the hope that it will be useful, but WITHOUT ANY
## WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
## A PARTICULAR PURPOSE. See the GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License along with
## this program; if not, write to the Free Software Foundation, Inc., 59 Temple
## Place - Suite 330, Boston, MA 02111-1307, USA
## I used config/am_include.mk for common definitions
include $(top_srcdir)/config/am_include.mk
## Descend into SUBDIRS and run make. Look at the Makefile.am files in the
## subdirectories Start at frontend_fox/Makefile.am to see how everything works.
SUBDIRS=include source
# list files that are documentation to be packaged in a release tarball and installed
pkgdoc_DATA=COPYING.TXT README.html
# extra data files that are to be pacakged in a release tarball and installed into the data directory
#pkgdata_DATA=
# sets up for soundtouch.m4 to be installed
m4datadir=$(datadir)/aclocal
m4data_DATA=soundtouch.m4
## These extra files and directories will be included in the distribution. by
## using make-dist by default many common filenames are automatically included
## such as AUTHORS, COPYING, etc the bootstrap script really shouldn't be a part
## of a final package, but it is useful for developers who might want to make
## changes to the configure scripts or makefiles.
# NOTE: wouldn't have to list the .TXT file if it were named without the .TXT
EXTRA_DIST= \
soundtouch.m4 \
config/m4 \
bootstrap \
make-win.bat \
COPYING.TXT \
README.html
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = soundtouch-1.0.pc
## This removes stuff from the distribution which may be present
## from a cvs checkout or other build reasons
dist-hook:
rm -rf `find $(distdir) -type d -name CVS` # remove all CVS directories
echo rm -rf `find $(distdir) -type f -name \.\#\*` # CVS revision files left around for some reason
## This happens at 'make distclean'
#distcleancheck:
# rm -rf files-that-may-also-need-to-be-deleted-on-'make distclean'
# flag to aclocal where to find m4 macros for tests
ACLOCAL_AMFLAGS = -I config/m4

26
bootstrap Normal file
View File

@ -0,0 +1,26 @@
# $Id: bootstrap 4 2008-02-10 16:07:12Z oparviai $
#!/bin/sh
if [ "$1" = "--clean" ]
then
if [ -a Makefile ]
then
make maintainer-clean
elif [ -a configure ]
then
configure && $0 --clean
else
bootstrap && configure && $0 --clean
fi
rm -rf configure libtool aclocal.m4 `find . -name Makefile.in` autom4te*.cache config/config.guess config/config.h.in config/config.sub config/depcomp config/install-sh config/ltmain.sh config/missing config/mkinstalldirs config/stamp-h config/stamp-h.in
#gettextize files
#rm -f ABOUT-NLS config/config.rpath config/m4/codeset.m4 config/m4/gettext.m4 config/m4/glibc21.m4 config/m4/iconv.m4 config/m4/intdiv0.m4 config/m4/inttypes-pri.m4 config/m4/inttypes.m4 config/m4/inttypes_h.m4 config/m4/isc-posix.m4 config/m4/lcmessage.m4 config/m4/lib-ld.m4 config/m4/lib-link.m4 config/m4/lib-prefix.m4 config/m4/progtest.m4 config/m4/stdint_h.m4 config/m4/uintmax_t.m4 config/m4/ulonglong.m4 po/Makefile.in.in po/Rules-quot po/boldquot.sed po/en@boldquot.header po/en@quot.header po/insert-header.sin po/quot.sed po/remove-potcdate.sin
else
export AUTOMAKE="automake --add-missing --foreign"
autoreconf -fisv && rm -f `find . -name "*~"` && rm -f ChangeLog
exit $?
fi

3
config/README.TXT Normal file
View File

@ -0,0 +1,3 @@
Files in this directory are used by GNU autoconf/automake system.
These files aren't used/needed in the Windows environment.

51
config/am_include.mk Normal file
View File

@ -0,0 +1,51 @@
## vim:tw=78
## Process this file with automake to create Makefile.in
##
## $Id: am_include.mk 4 2008-02-10 16:07:12Z oparviai $
##
## Copyright (C) 2003 - David W. Durham
##
## This file is part of SoundTouch, an audio processing library for pitch/time adjustments
##
## SoundTouch is free software; you can redistribute it and/or modify it under the
## terms of the GNU General Public License as published by the Free Software
## Foundation; either version 2 of the License, or (at your option) any later
## version.
##
## SoundTouch is distributed in the hope that it will be useful, but WITHOUT ANY
## WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
## A PARTICULAR PURPOSE. See the GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License along with
## this program; if not, write to the Free Software Foundation, Inc., 59 Temple
## Place - Suite 330, Boston, MA 02111-1307, USA
## These are common definitions used in all Makefiles
## It is actually included when a makefile.am is coverted to Makefile.in
## by automake, so it's ok to have @MACROS@ that will be set by configure
## INCLUDES is automatically added to CXXFLAGS at compile time. The
## $(top_srcdir) macro is set by configure. It's important to use $(top_srcdir)
## in case a user decides to build in a separate directory from the base package
## directory. Using absolute, or relative paths is a bad idea.
INCLUDES=-I$(top_srcdir)/include
## CXXFLAGS is also automatically added to the $(CXX) macro at compile time, and
## is passed down to the children as well
## I list these here in case there needs to always be some flags passed to the compiler
CXXFLAGS=@CXXFLAGS@
## LDFLAGS will be added at link time
## note that the -l flags are set by the LIBS macro that is set by configure
## This line doesn't need to be here, configure will add LDFLAGS on it's own,
## but I'm leaving here as a place to add LDFLAGS if so desired
LDFLAGS=@LDFLAGS@
# doc directory
pkgdocdir=$(prefix)/doc/@PACKAGE@

139
configure.ac Normal file
View File

@ -0,0 +1,139 @@
dnl SoundTouch configure.ac, by David W. Durham
dnl
dnl $Id: configure.ac 4 2008-02-10 16:07:12Z oparviai $
dnl
dnl This file is part of SoundTouch, an audio processing library for pitch/time adjustments
dnl
dnl SoundTouch is free software; you can redistribute it and/or modify it under the
dnl terms of the GNU General Public License as published by the Free Software
dnl Foundation; either version 2 of the License, or (at your option) any later
dnl version.
dnl
dnl SoundTouch is distributed in the hope that it will be useful, but WITHOUT ANY
dnl WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
dnl FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
dnl details.
dnl
dnl You should have received a copy of the GNU General Public License along with
dnl this program; if not, write to the Free Software Foundation, Inc., 59 Temple
dnl Place - Suite 330, Boston, MA 02111-1307, USA
# Process this file with autoconf to produce a configure script.
AC_INIT(SoundTouch, 1.3.1, [http://www.surina.net/soundtouch])
AC_CONFIG_AUX_DIR(config)
AM_CONFIG_HEADER([include/soundtouch_config.h])
AM_INIT_AUTOMAKE
AC_DISABLE_SHARED dnl This makes libtool only build static libs
#AC_GNU_SOURCE dnl enable posix extensions in glibc
AC_LANG(C++)
dnl ############################################################################
dnl # Checks for programs #
dnl ############################################################################
CXXFLAGS= dnl AC_PROG_CXX automatically sets CXXFLAGS to -g -O2 if g++ is the C++ compiler and CXXFLAGS hasn't been set, so by setting CXXFLAGS, we can avoid this.
AC_PROG_CXX
#AC_PROG_AWK
AC_PROG_CC
AC_PROG_CPP
AC_PROG_CXXCPP
AC_PROG_INSTALL
#AC_PROG_LN_S
AC_PROG_MAKE_SET
AM_PROG_LIBTOOL dnl turn on using libtool
dnl ############################################################################
dnl # Checks for header files #
dnl ############################################################################
AC_HEADER_STDC
#AC_HEADER_SYS_WAIT
# add any others you want to check for here
#AC_CHECK_HEADERS([fcntl.h limits.h stddef.h stdlib.h string.h sys/ioctl.h sys/vfs.h unistd.h])
dnl ############################################################################
dnl # Checks for typedefs, structures, and compiler characteristics $
dnl ############################################################################
AC_C_CONST
AC_C_INLINE
#AC_TYPE_OFF_T
#AC_TYPE_SIZE_T
AC_ARG_ENABLE(integer-samples,
[AC_HELP_STRING([--enable-integer-samples],
[use integer samples instead of floats
[default=yes]])],,
[enable_integer_samples=no])
if test "x$enable_integer_samples" = "xyes"; then
echo "****** Integer sample type enabled ******"
AC_DEFINE(INTEGER_SAMPLES,1,[Use Integer as Sample type])
else
echo "****** Float sample type enabled ******"
AC_DEFINE(FLOAT_SAMPLES,1,[Use Float as Sample type])
fi
dnl ############################################################################
dnl # Checks for library functions/classes #
dnl ############################################################################
AC_FUNC_MALLOC
AC_TYPE_SIGNAL
dnl make -lm get added to the LIBS
AC_CHECK_LIB(m, sqrt,,AC_MSG_ERROR([compatible libc math library not found]))
dnl add whatever functions you might want to check for here
#AC_CHECK_FUNCS([floor ftruncate memmove memset mkdir modf pow realpath sqrt strchr strdup strerror strrchr strstr strtol])
dnl ############################################################################
dnl # Internationalization and Localization #
dnl ############################################################################
#AM_GNU_GETTEXT_VERSION([0.11.5])
#AM_GNU_GETTEXT([external])
dnl ############################################################################
dnl # Final #
dnl ############################################################################
AC_CONFIG_FILES([
Makefile
source/Makefile
source/SoundTouch/Makefile
source/example/Makefile
source/example/SoundStretch/Makefile
source/example/bpm/Makefile
include/Makefile
])
AC_OUTPUT(
soundtouch-1.0.pc
)
dnl use 'echo' to put stuff here if you want a message to the builder

159
include/BPMDetect.h Normal file
View File

@ -0,0 +1,159 @@
////////////////////////////////////////////////////////////////////////////////
///
/// 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: 2008-02-10 18:07:12 +0200 (su, 10 helmi 2008) $
// File revision : $Revision: 4 $
//
// $Id: BPMDetect.h 4 2008-02-10 16:07:12Z 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"
/// Minimum allowed BPM rate. Used to restrict accepted result above a reasonable limit.
#define MIN_BPM 45
/// 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;
/// Initialize the class for processing.
void init(int numChannels, int sampleRate);
/// 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(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_

174
include/FIFOSampleBuffer.h Normal file
View File

@ -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: 2008-02-10 18:07:12 +0200 (su, 10 helmi 2008) $
// File revision : $Revision: 4 $
//
// $Id: FIFOSampleBuffer.h 4 2008-02-10 16:07:12Z 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() const;
/// 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

217
include/FIFOSamplePipe.h Normal file
View File

@ -0,0 +1,217 @@
////////////////////////////////////////////////////////////////////////////////
///
/// '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: 2008-02-10 18:07:12 +0200 (su, 10 helmi 2008) $
// File revision : $Revision: 4 $
//
// $Id: FIFOSamplePipe.h 4 2008-02-10 16:07:12Z 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:
/// 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() const = 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() const
{
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

26
include/Makefile.am Normal file
View File

@ -0,0 +1,26 @@
## Process this file with automake to create Makefile.in
##
## $Id: Makefile.am 4 2008-02-10 16:07:12Z oparviai $
##
## Copyright (C) 2003 - David W. Durham
##
## This file is part of SoundTouch, an audio processing library for pitch/time adjustments
##
## SoundTouch is free software; you can redistribute it and/or modify it under the
## terms of the GNU General Public License as published by the Free Software
## Foundation; either version 2 of the License, or (at your option) any later
## version.
##
## SoundTouch is distributed in the hope that it will be useful, but WITHOUT ANY
## WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
## A PARTICULAR PURPOSE. See the GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License along with
## this program; if not, write to the Free Software Foundation, Inc., 59 Temple
## Place - Suite 330, Boston, MA 02111-1307, USA
## I used config/am_include.mk for common definitions
include $(top_srcdir)/config/am_include.mk
pkginclude_HEADERS=FIFOSampleBuffer.h FIFOSamplePipe.h SoundTouch.h STTypes.h BPMDetect.h soundtouch_config.h

144
include/STTypes.h Normal file
View File

@ -0,0 +1,144 @@
////////////////////////////////////////////////////////////////////////////////
///
/// 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: 2008-02-10 18:03:11 +0200 (su, 10 helmi 2008) $
// File revision : $Revision: 3 $
//
// $Id: STTypes.h 3 2008-02-10 16:03:11Z 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
/// Define this to allow CPU-specific assembler optimizations. Notice that
/// having this enabled on non-x86 platforms doesn't matter; the compiler can
/// drop unsupported extensions on different platforms automatically.
/// However, if you're having difficulties getting the optimized routines
/// compiled with your compler (e.g. some gcc compiler versions may be picky),
/// you may wish to disable the optimizations to make the library compile.
#define ALLOW_OPTIMIZATIONS 1
// 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_OPTIMIZATIONS
#if (WIN32 || __i386__ || __x86_64__)
// Allow MMX optimizations
#define ALLOW_MMX 1
#endif
#endif
#else
// floating point samples
typedef float SAMPLETYPE;
// data type for sample accumulation: Use double to utilize full precision.
typedef double LONG_SAMPLETYPE;
#ifdef ALLOW_OPTIMIZATIONS
// Allow 3DNow! and SSE optimizations
#if WIN32
#define ALLOW_3DNOW 1
#endif
#if (WIN32 || __i386__ || __x86_64__)
#define ALLOW_SSE 1
#endif
#endif
#endif // INTEGER_SAMPLES
};
#endif

252
include/SoundTouch.h Normal file
View File

@ -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: 2008-02-10 18:07:12 +0200 (su, 10 helmi 2008) $
// File revision : $Revision: 4 $
//
// $Id: SoundTouch.h 4 2008-02-10 16:07:12Z 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.3.1"
/// SoundTouch library version id
#define SOUNDTOUCH_VERSION_ID 010301
//
// 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

34
make-win.bat Normal file
View File

@ -0,0 +1,34 @@
@REM
@REM SoundTouch & SoundStretch Build script for Win32 platform
@REM
@REM You'll need Visual C++ 6.0 installed to compile - also execute the
@REM "vcvars32.bat" in VC install directotry before running this one.
@REM
@REM Copyright (c) Olli Parviainen
@REM File Created: 09/Sep/2003
@REM
@REM $Id: make-win.bat 4 2008-02-10 16:07:12Z oparviai $
@if "%MsDevDir%"=="" goto nodevdir
md bin
md lib
msdev source\SoundTouch\SoundTouch.dsw /MAKE ALL
msdev source\example\bpm\bpm.dsw /MAKE ALL
msdev source\example\SoundStretch\SoundStretch.dsw /MAKE "SoundStretch - Win32 Release"
goto end
:nodevdir
@echo off
echo *********************************************************************
echo **
echo ** ERROR: Visual Studio path not set.
echo **
echo ** Run "vcvars32.bat" from Visual Studio installation directory, e.g.
echo ** "\Program Files\Microsoft Visual Studio\VC98\Bin", then try again.
echo **
echo *********************************************************************
:end

11
soundtouch-1.0.pc.in Normal file
View File

@ -0,0 +1,11 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: SoundTouch
Description: SoundTouch is an open-source audio processing library for changing the Tempo, Pitch and Playback Rates of audio streams or files
Version: @VERSION@
Libs: -L${libdir} -lSoundTouch
Cflags: -I${includedir}/soundtouch

70
soundtouch.m4 Normal file
View File

@ -0,0 +1,70 @@
# m4 configure test script for the SoundTouch library
# (c)2003 David W. Durham
#
# $Id: soundtouch.m4 4 2008-02-10 16:07:12Z oparviai $
#
# This file can be included with other packages that need to test
# for libSoundTouch.
#
# It will #define HAVE_LIBSOUNDTOUCH iff the library is found
# It will AC_SUBST SOUNDTOUCH_LIBS and SOUNDTOUCH_CXXFLAGS as well
# It also defines some flags to the configure script for specifying
# the location to search for libSoundTouch
#
# A user of libSoundTouch should add @SOUNDTOUCH_LIBS@ and
# @SOUNDTOUCH_CXXFLAGS@ to the appropriate variables in his
# Makefile.am files
#
# This script works with autoconf-2.5x and automake-1.6 but I have
# not tested it with older versions.
dnl min version not supported yet
dnl AM_PATH_SOUNDTOUCH([MINMUM-VERSION, [(additional) ACTION-IF-FOUND] [, ACTION-IF-NOT-FOUND]]])
AH_TEMPLATE([HAVE_LIBSOUNDTOUCH], [defined by $0])
SOUNDTOUCH_CXXFLAGS=""
SOUNDTOUCH_LIBS=""
AC_DEFUN([AM_PATH_SOUNDTOUCH],[
AC_ARG_WITH(soundtouch-prefix,[ --with-soundtouch-prefix=DIR Prefix where SoundTouch was installed (optional)], [soundtouch_prefix="$withval"],[soundtouch_prefix=""])
AC_ARG_ENABLE(soundtouch-check,[ --disable-soundtouch-check Do not look for the SoundTouch Library],[enable_soundtouch_check="$enableval"],[enable_soundtouch_check="yes"])
if test "$enable_soundtouch_check" = "yes"
then
saved_CPPFLAGS="$CPPFLAGS"
saved_LDFLAGS="$LDFLAGS"
CPPFLAGS="$CPPFLAGS -I$soundtouch_prefix/include"
LDFLAGS="$LDFLAGS -L$soundtouch_prefix/lib"
dnl make sure SoundTouch.h header file exists
dnl could use AC_CHECK_HEADERS to check for all of them, but the supporting .h file names may change later
AC_CHECK_HEADER([soundtouch/SoundTouch.h],[
dnl SoundTouch.h found
dnl make sure libSoundTouch is linkable
AC_CHECK_LIB([SoundTouch],[soundtouch_ac_test],[
dnl libSoundTouch found
SOUNDTOUCH_CXXFLAGS="-I$soundtouch_prefix/include"
SOUNDTOUCH_LIBS="-L$soundtouch_prefix/lib -lSoundTouch"
AC_DEFINE([HAVE_LIBSOUNDTOUCH])
dnl run action-if-found
ifelse([$2], , :, [$2])
],[
dnl run action-if-not-found
ifelse([$3], , :, [$3])
])
],[
dnl run action-if-not-found
ifelse([$3], , :, [$3])
])
CPPFLAGS="$saved_CPPFLAGS"
LDFLAGS="$saved_LDFLAGS"
fi
AC_SUBST(SOUNDTOUCH_CXXFLAGS)
AC_SUBST(SOUNDTOUCH_LIBS)
])

28
source/Makefile.am Normal file
View File

@ -0,0 +1,28 @@
## Process this file with automake to create Makefile.in
##
## $Id: Makefile.am 4 2008-02-10 16:07:12Z oparviai $
##
## Copyright (C) 2003 - David W. Durham
##
## This file is part of SoundTouch, an audio processing library for pitch/time adjustments
##
## SoundTouch is free software; you can redistribute it and/or modify it under the
## terms of the GNU General Public License as published by the Free Software
## Foundation; either version 2 of the License, or (at your option) any later
## version.
##
## SoundTouch is distributed in the hope that it will be useful, but WITHOUT ANY
## WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
## A PARTICULAR PURPOSE. See the GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License along with
## this program; if not, write to the Free Software Foundation, Inc., 59 Temple
## Place - Suite 330, Boston, MA 02111-1307, USA
include $(top_srcdir)/config/am_include.mk
SUBDIRS=SoundTouch example
# set to something if you want other stuff to be included in the distribution tarball
#EXTRA_DIST=

View File

@ -0,0 +1,353 @@
////////////////////////////////////////////////////////////////////////////////
///
/// 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/vstudio/downloads/tools/ppack/default.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: 2008-02-10 18:07:12 +0200 (su, 10 helmi 2008) $
// File revision : $Revision: 4 $
//
// $Id: 3dnow_win.cpp 4 2008-02-10 16:07:12Z 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"
//#include <limits.h>
// these are declared in 'TDStretch.cpp'
// extern int scanOffsets[4][24];
// Calculates cross correlation of two buffers
double TDStretch3DNow::calcCrossCorrStereo(const float *pV1, const float *pV2) const
{
uint overlapLengthLocal = overlapLength;
float corr;
// 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, const 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

View File

@ -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: 2008-02-10 18:07:12 +0200 (su, 10 helmi 2008) $
// File revision : $Revision: 4 $
//
// $Id: AAFilter.cpp 4 2008-02-10 16:07:12Z 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(const 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();
}

View File

@ -0,0 +1,91 @@
////////////////////////////////////////////////////////////////////////////////
///
/// 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.
///
/// 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: 2008-02-10 18:07:12 +0200 (su, 10 helmi 2008) $
// File revision : $Revision: 4 $
//
// $Id: AAFilter.h 4 2008-02-10 16:07:12Z 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 AAFilter_H
#define AAFilter_H
#include "STTypes.h"
namespace soundtouch
{
class AAFilter
{
protected:
class FIRFilter *pFIR;
/// Low-pass filter cut-off frequency, negative = invalid
double cutoffFreq;
/// num of filter taps
uint length;
/// Calculate the FIR coefficients realizing the given cutoff-frequency
void calculateCoeffs();
public:
AAFilter(uint length);
~AAFilter();
/// Sets new anti-alias filter cut-off edge frequency, scaled to sampling
/// frequency (nyquist frequency = 0.5). The filter will cut off the
/// frequencies than that.
void setCutoffFreq(double newCutoffFreq);
/// Sets number of FIR filter taps, i.e. ~filter complexity
void setLength(uint newLength);
uint getLength() const;
/// 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 evaluate(SAMPLETYPE *dest,
const SAMPLETYPE *src,
uint numSamples,
uint numChannels) const;
};
}
#endif

View File

@ -0,0 +1,256 @@
////////////////////////////////////////////////////////////////////////////////
///
/// 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: 2008-02-10 18:07:12 +0200 (su, 10 helmi 2008) $
// File revision : $Revision: 4 $
//
// $Id: FIFOSampleBuffer.cpp 4 2008-02-10 16:07:12Z 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;
}
// 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() const
{
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");
}
temp = (SAMPLETYPE *)(((ulong)tempUnaligned + 15) & (ulong)-16);
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;
}

View File

@ -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: 2008-02-10 18:07:12 +0200 (su, 10 helmi 2008) $
// File revision : $Revision: 4 $
//
// $Id: FIRFilter.cpp 4 2008-02-10 16:07:12Z 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, 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;
}
}

View File

@ -0,0 +1,163 @@
////////////////////////////////////////////////////////////////////////////////
///
/// 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: 2008-02-10 18:07:12 +0200 (su, 10 helmi 2008) $
// File revision : $Revision: 4 $
//
// $Id: FIRFilter.h 4 2008-02-10 16:07:12Z 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 FIRFilter_H
#define FIRFilter_H
#include "STTypes.h"
namespace soundtouch
{
class FIRFilter
{
protected:
// Number of FIR filter taps
uint length;
// Number of FIR filter taps divided by 8
uint lengthDiv8;
// Result divider factor in 2^k format
uint resultDivFactor;
// Result divider value.
SAMPLETYPE resultDivider;
// Memory for filter coefficients
SAMPLETYPE *filterCoeffs;
virtual uint evaluateFilterStereo(SAMPLETYPE *dest,
const SAMPLETYPE *src,
uint numSamples) const;
virtual uint evaluateFilterMono(SAMPLETYPE *dest,
const SAMPLETYPE *src,
uint numSamples) const;
public:
FIRFilter();
virtual ~FIRFilter();
/// Operator 'new' is overloaded so that it automatically creates a suitable instance
/// depending on if we've a MMX-capable CPU available or not.
static void * operator new(size_t s);
static FIRFilter *newInstance();
/// 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.
///
/// \return Number of samples copied to 'dest'.
uint evaluate(SAMPLETYPE *dest,
const SAMPLETYPE *src,
uint numSamples,
uint numChannels) const;
uint getLength() const;
virtual void setCoefficients(const SAMPLETYPE *coeffs,
uint newLength,
uint uResultDivFactor);
};
// Optional subclasses that implement CPU-specific optimizations:
#ifdef ALLOW_MMX
/// Class that implements MMX optimized functions exclusive for 16bit integer samples type.
class FIRFilterMMX : public FIRFilter
{
protected:
short *filterCoeffsUnalign;
short *filterCoeffsAlign;
virtual uint evaluateFilterStereo(short *dest, const short *src, uint numSamples) const;
public:
FIRFilterMMX();
~FIRFilterMMX();
virtual void setCoefficients(const short *coeffs, uint newLength, uint uResultDivFactor);
};
#endif // ALLOW_MMX
#ifdef ALLOW_3DNOW
/// Class that implements 3DNow! optimized functions exclusive for floating point samples type.
class FIRFilter3DNow : public FIRFilter
{
protected:
float *filterCoeffsUnalign;
float *filterCoeffsAlign;
virtual uint evaluateFilterStereo(float *dest, const float *src, uint numSamples) const;
public:
FIRFilter3DNow();
~FIRFilter3DNow();
virtual void setCoefficients(const float *coeffs, uint newLength, uint uResultDivFactor);
};
#endif // ALLOW_3DNOW
#ifdef ALLOW_SSE
/// Class that implements SSE optimized functions exclusive for floating point samples type.
class FIRFilterSSE : public FIRFilter
{
protected:
float *filterCoeffsUnalign;
float *filterCoeffsAlign;
virtual uint evaluateFilterStereo(float *dest, const float *src, uint numSamples) const;
public:
FIRFilterSSE();
~FIRFilterSSE();
virtual void setCoefficients(const float *coeffs, uint newLength, uint uResultDivFactor);
};
#endif // ALLOW_SSE
}
#endif // FIRFilter_H

View File

@ -0,0 +1,42 @@
## Process this file with automake to create Makefile.in
##
## $Id: Makefile.am 4 2008-02-10 16:07:12Z oparviai $
##
## Copyright (C) 2003 - David W. Durham
##
## This file is part of SoundTouch, an audio processing library for pitch/time adjustments
##
## SoundTouch is free software; you can redistribute it and/or modify it under the
## terms of the GNU General Public License as published by the Free Software
## Foundation; either version 2 of the License, or (at your option) any later
## version.
##
## SoundTouch is distributed in the hope that it will be useful, but WITHOUT ANY
## WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
## A PARTICULAR PURPOSE. See the GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License along with
## this program; if not, write to the Free Software Foundation, Inc., 59 Temple
## Place - Suite 330, Boston, MA 02111-1307, USA
include $(top_srcdir)/config/am_include.mk
# set to something if you want other stuff to be included in the distribution tarball
EXTRA_DIST=3dnow_win.cpp cpu_detect_x86_win.cpp SoundTouch.dsp SoundTouch.dsw SoundTouch.sln SoundTouch.vcproj
noinst_HEADERS=AAFilter.h cpu_detect.h cpu_detect_x86_gcc.cpp FIRFilter.h RateTransposer.h TDStretch.h
lib_LTLIBRARIES=libSoundTouch.la
# the mmx_gcc.cpp and cpu_detect_x86_gcc.cpp may need to be conditionally included here from things discovered in configure.ac
libSoundTouch_la_SOURCES=AAFilter.cpp FIRFilter.cpp FIFOSampleBuffer.cpp mmx_optimized.cpp sse_optimized.cpp RateTransposer.cpp SoundTouch.cpp TDStretch.cpp cpu_detect_x86_gcc.cpp
# ??? test for -fcheck-new in configure.ac
# other compiler flags to add
AM_CXXFLAGS=-O3 -msse -fcheck-new -I../../include
# other linking flags to add
#libSoundTouch_la_LIBADD=

View File

@ -0,0 +1,622 @@
////////////////////////////////////////////////////////////////////////////////
///
/// 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: 2008-02-10 18:07:12 +0200 (su, 10 helmi 2008) $
// File revision : $Revision: 4 $
//
// $Id: RateTransposer.cpp 4 2008-02-10 16:07:12Z 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(const BOOL newMode)
{
bUseAAFilter = newMode;
}
/// Returns nonzero if anti-alias filter is enabled.
BOOL RateTransposer::isAAFilterEnabled() const
{
return bUseAAFilter;
}
AAFilter *RateTransposer::getAAFilter() const
{
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);
// 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;
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) goto end;
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) goto end;
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;
}

View File

@ -0,0 +1,158 @@
////////////////////////////////////////////////////////////////////////////////
///
/// 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).
///
/// Use either of the derived classes of 'RateTransposerInteger' or
/// 'RateTransposerFloat' for corresponding integer/floating point tranposing
/// algorithm implementation.
///
/// Author : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi
/// SoundTouch WWW: http://www.surina.net/soundtouch
///
////////////////////////////////////////////////////////////////////////////////
//
// Last changed : $Date: 2008-02-10 18:07:12 +0200 (su, 10 helmi 2008) $
// File revision : $Revision: 4 $
//
// $Id: RateTransposer.h 4 2008-02-10 16:07:12Z 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 RateTransposer_H
#define RateTransposer_H
#include "AAFilter.h"
#include "FIFOSamplePipe.h"
#include "FIFOSampleBuffer.h"
#include "STTypes.h"
namespace soundtouch
{
/// A common linear samplerate transposer class.
///
/// Note: Use function "RateTransposer::newInstance()" to create a new class
/// instance instead of the "new" operator; that function automatically
/// chooses a correct implementation depending on if integer or floating
/// arithmetics are to be used.
class RateTransposer : public FIFOProcessor
{
protected:
/// Anti-alias filter object
AAFilter *pAAFilter;
float fRate;
int numChannels;
/// Buffer for collecting samples to feed the anti-alias filter between
/// two batches
FIFOSampleBuffer storeBuffer;
/// Buffer for keeping samples between transposing & anti-alias filter
FIFOSampleBuffer tempBuffer;
/// Output sample buffer
FIFOSampleBuffer outputBuffer;
BOOL bUseAAFilter;
virtual void resetRegisters() = 0;
virtual uint transposeStereo(SAMPLETYPE *dest,
const SAMPLETYPE *src,
uint numSamples) = 0;
virtual uint transposeMono(SAMPLETYPE *dest,
const SAMPLETYPE *src,
uint numSamples) = 0;
uint transpose(SAMPLETYPE *dest,
const SAMPLETYPE *src,
uint numSamples);
void downsample(const SAMPLETYPE *src,
uint numSamples);
void upsample(const SAMPLETYPE *src,
uint numSamples);
/// 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 processSamples(const SAMPLETYPE *src,
uint numSamples);
public:
RateTransposer();
virtual ~RateTransposer();
/// Operator 'new' is overloaded so that it automatically creates a suitable instance
/// depending on if we're to use integer or floating point arithmetics.
static void *operator new(size_t s);
/// Use this function instead of "new" operator to create a new instance of this class.
/// This function automatically chooses a correct implementation, depending on if
/// integer ot floating point arithmetics are to be used.
static RateTransposer *newInstance();
/// Returns the output buffer object
FIFOSamplePipe *getOutput() { return &outputBuffer; };
/// Returns the store buffer object
FIFOSamplePipe *getStore() { return &storeBuffer; };
/// Return anti-alias filter object
AAFilter *getAAFilter() const;
/// Enables/disables the anti-alias filter. Zero to disable, nonzero to enable
void enableAAFilter(BOOL newMode);
/// Returns nonzero if anti-alias filter is enabled.
BOOL isAAFilterEnabled() const;
/// Sets new target rate. Normal rate = 1.0, smaller values represent slower
/// rate, larger faster rates.
virtual void setRate(float newRate);
/// Sets the number of channels, 1 = mono, 2 = stereo
void setChannels(int channels);
/// Adds 'numSamples' pcs of samples from the 'samples' memory position into
/// the input of the object.
void putSamples(const SAMPLETYPE *samples, uint numSamples);
/// Clears all the samples in the object
void clear();
/// Returns nonzero if there aren't any samples available for outputting.
int isEmpty() const;
};
}
#endif

View File

@ -0,0 +1,478 @@
//////////////////////////////////////////////////////////////////////////////
///
/// 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: 2008-02-10 18:07:12 +0200 (su, 10 helmi 2008) $
// File revision : $Revision: 4 $
//
// $Id: SoundTouch.cpp 4 2008-02-10 16:07:12Z 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
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(numChannels);
pTDStretch->setChannels(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);
if (rate > 1.0f)
{
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;
}
}
else
{
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;
}
}
}
// Sets sample rate.
void SoundTouch::setSampleRate(uint srate)
{
bSrateSet = TRUE;
// set sample rate, leave other tempo changer parameters as they are.
pTDStretch->setParameters(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);
}
*/
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
{
assert(rate > 1.0f);
// 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;
}

View File

@ -0,0 +1,176 @@
# Microsoft Developer Studio Project File - Name="SoundTouch" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Static Library" 0x0104
CFG=SoundTouch - Win32 Debug
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE
!MESSAGE NMAKE /f "SoundTouch.mak".
!MESSAGE
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE
!MESSAGE NMAKE /f "SoundTouch.mak" CFG="SoundTouch - Win32 Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "SoundTouch - Win32 Release" (based on "Win32 (x86) Static Library")
!MESSAGE "SoundTouch - Win32 Debug" (based on "Win32 (x86) Static Library")
!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""
# PROP Scc_LocalPath ""
CPP=cl.exe
RSC=rc.exe
!IF "$(CFG)" == "SoundTouch - Win32 Release"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Release"
# PROP BASE Intermediate_Dir "Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "Release"
# PROP Intermediate_Dir "Release"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
# ADD CPP /nologo /W3 /GX /Zi /O2 /I "..\..\include" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
# ADD BASE RSC /l 0x40b /d "NDEBUG"
# ADD RSC /l 0x40b /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LIB32=link.exe -lib
# ADD BASE LIB32 /nologo
# ADD LIB32 /nologo
# Begin Special Build Tool
SOURCE="$(InputPath)"
PostBuild_Cmds=copy .\Release\SoundTouch.lib ..\..\lib\
# End Special Build Tool
!ELSEIF "$(CFG)" == "SoundTouch - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "Debug"
# PROP BASE Intermediate_Dir "Debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "Debug"
# PROP Intermediate_Dir "Debug"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\include" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FR /YX /FD /GZ /c
# ADD BASE RSC /l 0x40b /d "_DEBUG"
# ADD RSC /l 0x40b /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LIB32=link.exe -lib
# ADD BASE LIB32 /nologo
# ADD LIB32 /nologo /out:"Debug\SoundTouchD.lib"
# Begin Special Build Tool
SOURCE="$(InputPath)"
PostBuild_Cmds=copy .\Debug\SoundTouchD.lib ..\..\lib\
# End Special Build Tool
!ENDIF
# Begin Target
# Name "SoundTouch - Win32 Release"
# Name "SoundTouch - Win32 Debug"
# Begin Group "Source Files"
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Source File
SOURCE=.\3dnow_win.cpp
# End Source File
# Begin Source File
SOURCE=.\AAFilter.cpp
# End Source File
# Begin Source File
SOURCE=.\cpu_detect_x86_win.cpp
# End Source File
# Begin Source File
SOURCE=.\FIFOSampleBuffer.cpp
# End Source File
# Begin Source File
SOURCE=.\FIRFilter.cpp
# End Source File
# Begin Source File
SOURCE=.\mmx_optimized.cpp
# End Source File
# Begin Source File
SOURCE=.\RateTransposer.cpp
# End Source File
# Begin Source File
SOURCE=.\SoundTouch.cpp
# End Source File
# Begin Source File
SOURCE=.\sse_optimized.cpp
# End Source File
# Begin Source File
SOURCE=.\TDStretch.cpp
# End Source File
# End Group
# Begin Group "Header Files"
# PROP Default_Filter "h;hpp;hxx;hm;inl"
# Begin Source File
SOURCE=.\AAFilter.h
# End Source File
# Begin Source File
SOURCE=.\cpu_detect.h
# End Source File
# Begin Source File
SOURCE=..\..\include\FIFOSampleBuffer.h
# End Source File
# Begin Source File
SOURCE=..\..\include\FIFOSamplePipe.h
# End Source File
# Begin Source File
SOURCE=.\FIRFilter.h
# End Source File
# Begin Source File
SOURCE=.\RateTransposer.h
# End Source File
# Begin Source File
SOURCE=..\..\include\SoundTouch.h
# End Source File
# Begin Source File
SOURCE=..\..\include\STTypes.h
# End Source File
# Begin Source File
SOURCE=.\TDStretch.h
# End Source File
# End Group
# End Target
# End Project

View File

@ -0,0 +1,31 @@
Microsoft Developer Studio Workspace File, Format Version 6.00
# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
#
# $Id: SoundTouch.dsw 4 2008-02-10 16:07:12Z oparviai $
###############################################################################
Project: "SoundTouch"=.\SoundTouch.dsp - Package Owner=<4>
Package=<5>
{{{
}}}
Package=<4>
{{{
}}}
###############################################################################
Global:
Package=<5>
{{{
}}}
Package=<3>
{{{
}}}
###############################################################################

View File

@ -0,0 +1,21 @@
Microsoft Visual Studio Solution File, Format Version 8.00
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SoundTouch", "SoundTouch.vcproj", "{0A626C77-0515-4131-AA80-E0BFFC479FEB}"
ProjectSection(ProjectDependencies) = postProject
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfiguration) = preSolution
Debug = Debug
Release = Release
EndGlobalSection
GlobalSection(ProjectConfiguration) = postSolution
{0A626C77-0515-4131-AA80-E0BFFC479FEB}.Debug.ActiveCfg = Debug|Win32
{0A626C77-0515-4131-AA80-E0BFFC479FEB}.Debug.Build.0 = Debug|Win32
{0A626C77-0515-4131-AA80-E0BFFC479FEB}.Release.ActiveCfg = Release|Win32
{0A626C77-0515-4131-AA80-E0BFFC479FEB}.Release.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
EndGlobalSection
GlobalSection(ExtensibilityAddIns) = postSolution
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,336 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="7.10"
Name="SoundTouch"
SccProjectName=""
SccLocalPath="">
<Platforms>
<Platform
Name="Win32"/>
</Platforms>
<Configurations>
<Configuration
Name="Release|Win32"
OutputDirectory=".\Release"
IntermediateDirectory=".\Release"
ConfigurationType="4"
UseOfMFC="0"
ATLMinimizesCRunTimeLibraryUsage="FALSE"
CharacterSet="2">
<Tool
Name="VCCLCompilerTool"
Optimization="2"
InlineFunctionExpansion="1"
AdditionalIncludeDirectories="..\..\include"
PreprocessorDefinitions="WIN32;NDEBUG;_LIB"
StringPooling="TRUE"
RuntimeLibrary="4"
EnableFunctionLevelLinking="TRUE"
UsePrecompiledHeader="2"
PrecompiledHeaderFile=".\Release/SoundTouch.pch"
AssemblerListingLocation=".\Release/"
ObjectFile=".\Release/"
ProgramDataBaseFileName=".\Release/"
WarningLevel="3"
SuppressStartupBanner="TRUE"
DebugInformationFormat="3"
CompileAs="0"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLibrarianTool"
OutputFile=".\Release\SoundTouch.lib"
SuppressStartupBanner="TRUE"/>
<Tool
Name="VCMIDLTool"/>
<Tool
Name="VCPostBuildEventTool"
CommandLine="copy .\Release\SoundTouch.lib ..\..\lib\"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="NDEBUG"
Culture="1035"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCXMLDataGeneratorTool"/>
<Tool
Name="VCManagedWrapperGeneratorTool"/>
<Tool
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
</Configuration>
<Configuration
Name="Debug|Win32"
OutputDirectory=".\Debug"
IntermediateDirectory=".\Debug"
ConfigurationType="4"
UseOfMFC="0"
ATLMinimizesCRunTimeLibraryUsage="FALSE"
CharacterSet="2">
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="..\..\include"
PreprocessorDefinitions="WIN32;_DEBUG;_LIB"
BasicRuntimeChecks="3"
RuntimeLibrary="5"
UsePrecompiledHeader="2"
PrecompiledHeaderFile=".\Debug/SoundTouch.pch"
AssemblerListingLocation=".\Debug/"
ObjectFile=".\Debug/"
ProgramDataBaseFileName=".\Debug/"
BrowseInformation="1"
WarningLevel="3"
SuppressStartupBanner="TRUE"
DebugInformationFormat="4"
CompileAs="0"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLibrarianTool"
OutputFile="Debug\SoundTouchD.lib"
SuppressStartupBanner="TRUE"/>
<Tool
Name="VCMIDLTool"/>
<Tool
Name="VCPostBuildEventTool"
CommandLine="copy .\Debug\SoundTouchD.lib ..\..\lib\"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="_DEBUG"
Culture="1035"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCXMLDataGeneratorTool"/>
<Tool
Name="VCManagedWrapperGeneratorTool"/>
<Tool
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat">
<File
RelativePath="3dnow_win.cpp">
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
Optimization="2"
AdditionalIncludeDirectories=""
PreprocessorDefinitions=""/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32">
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=""
PreprocessorDefinitions=""
BasicRuntimeChecks="3"
BrowseInformation="1"/>
</FileConfiguration>
</File>
<File
RelativePath="AAFilter.cpp">
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
Optimization="2"
AdditionalIncludeDirectories=""
PreprocessorDefinitions=""/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32">
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=""
PreprocessorDefinitions=""
BasicRuntimeChecks="3"
BrowseInformation="1"/>
</FileConfiguration>
</File>
<File
RelativePath="cpu_detect_x86_win.cpp">
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
Optimization="2"
AdditionalIncludeDirectories=""
PreprocessorDefinitions=""/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32">
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=""
PreprocessorDefinitions=""
BasicRuntimeChecks="3"
BrowseInformation="1"/>
</FileConfiguration>
</File>
<File
RelativePath="FIFOSampleBuffer.cpp">
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
Optimization="2"
AdditionalIncludeDirectories=""
PreprocessorDefinitions=""/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32">
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=""
PreprocessorDefinitions=""
BasicRuntimeChecks="3"
BrowseInformation="1"/>
</FileConfiguration>
</File>
<File
RelativePath="FIRFilter.cpp">
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
Optimization="2"
AdditionalIncludeDirectories=""
PreprocessorDefinitions=""/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32">
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=""
PreprocessorDefinitions=""
BasicRuntimeChecks="3"
BrowseInformation="1"/>
</FileConfiguration>
</File>
<File
RelativePath=".\mmx_optimized.cpp">
</File>
<File
RelativePath="RateTransposer.cpp">
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
Optimization="2"
AdditionalIncludeDirectories=""
PreprocessorDefinitions=""/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32">
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=""
PreprocessorDefinitions=""
BasicRuntimeChecks="3"
BrowseInformation="1"/>
</FileConfiguration>
</File>
<File
RelativePath="SoundTouch.cpp">
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
Optimization="2"
AdditionalIncludeDirectories=""
PreprocessorDefinitions=""/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32">
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=""
PreprocessorDefinitions=""
BasicRuntimeChecks="3"
BrowseInformation="1"/>
</FileConfiguration>
</File>
<File
RelativePath=".\sse_optimized.cpp">
</File>
<File
RelativePath="TDStretch.cpp">
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
Optimization="2"
AdditionalIncludeDirectories=""
PreprocessorDefinitions=""/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32">
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=""
PreprocessorDefinitions=""
BasicRuntimeChecks="3"
BrowseInformation="1"/>
</FileConfiguration>
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl">
<File
RelativePath="AAFilter.h">
</File>
<File
RelativePath="cpu_detect.h">
</File>
<File
RelativePath="..\..\include\FIFOSampleBuffer.h">
</File>
<File
RelativePath="..\..\include\FIFOSamplePipe.h">
</File>
<File
RelativePath="FIRFilter.h">
</File>
<File
RelativePath="RateTransposer.h">
</File>
<File
RelativePath="..\..\include\SoundTouch.h">
</File>
<File
RelativePath="..\..\include\STTypes.h">
</File>
<File
RelativePath="TDStretch.h">
</File>
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@ -0,0 +1,939 @@
////////////////////////////////////////////////////////////////////////////////
///
/// 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: 2008-02-10 18:07:12 +0200 (su, 10 helmi 2008) $
// File revision : $Revision: 4 $
//
// $Id: TDStretch.cpp 4 2008-02-10 16:07:12Z 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 <stdexcept>
#include "STTypes.h"
#include "cpu_detect.h"
#include "TDStretch.h"
using namespace soundtouch;
#ifndef min
//#define min(a,b) (((a) > (b)) ? (b) : (a))
#define max(a,b) (((a) < (b)) ? (b) : (a))
#endif
/*****************************************************************************
*
* Constant definitions
*
*****************************************************************************/
// Table for the hierarchical mixing position seeking algorithm
static int _scanOffsets[4][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}};
/*****************************************************************************
*
* Implementation of the class 'TDStretch'
*
*****************************************************************************/
TDStretch::TDStretch() : FIFOProcessor(&outputBuffer)
{
bQuickseek = FALSE;
channels = 2;
bMidBufferDirty = FALSE;
pMidBuffer = NULL;
pRefMidBufferUnaligned = NULL;
overlapLength = 0;
setParameters(44100, DEFAULT_SEQUENCE_MS, DEFAULT_SEEKWINDOW_MS, DEFAULT_OVERLAP_MS);
setTempo(1.0f);
}
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)
{
assert(aSampleRate >= 0);
assert(aSequenceMS >= 0);
assert(aSeekWindowMS >= 0);
assert(aOverlapMS >= 0);
this->sampleRate = aSampleRate;
this->sequenceMs = aSequenceMS;
this->seekWindowMs = aSeekWindowMS;
this->overlapMs = aOverlapMS;
seekLength = (sampleRate * seekWindowMs) / 1000;
seekWindowLength = (sampleRate * sequenceMs) / 1000;
maxOffset = seekLength;
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 = sequenceMs;
}
if (pSeekWindowMs)
{
*pSeekWindowMs = 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()
{
if (bMidBufferDirty)
{
memset(pMidBuffer, 0, 2 * sizeof(SAMPLETYPE) * overlapLength);
bMidBufferDirty = FALSE;
}
}
void TDStretch::clearInput()
{
inputBuffer.clear();
clearMidBuffer();
}
// Clears the sample buffers
void TDStretch::clear()
{
outputBuffer.clear();
inputBuffer.clear();
clearMidBuffer();
}
// 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;
LONG_SAMPLETYPE bestCorr, corr;
int i;
// Slopes the amplitudes of the 'midBuffer' samples
precalcCorrReferenceStereo();
bestCorr = INT_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 = calcCrossCorrStereo(refPos + 2 * i, pRefMidBuffer);
// 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;
LONG_SAMPLETYPE bestCorr, corr;
int scanCount, corrOffset, tempOffset;
// Slopes the amplitude of the 'midBuffer' samples
precalcCorrReferenceStereo();
bestCorr = INT_MIN;
bestOffs = 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 = calcCrossCorrStereo(refPos + 2 * tempOffset, pRefMidBuffer);
// 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;
LONG_SAMPLETYPE bestCorr, corr;
int tempOffset;
const SAMPLETYPE *compare;
// Slopes the amplitude of the 'midBuffer' samples
precalcCorrReferenceMono();
bestCorr = INT_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 = calcCrossCorrMono(pRefMidBuffer, compare);
// 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;
LONG_SAMPLETYPE bestCorr, corr;
int scanCount, corrOffset, tempOffset;
// Slopes the amplitude of the 'midBuffer' samples
precalcCorrReferenceMono();
bestCorr = INT_MIN;
bestOffs = 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 = calcCrossCorrMono(refPos + tempOffset, pRefMidBuffer);
// 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.
}
// 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 ideal skip length (according to tempo value)
nominalSkip = tempo * (seekWindowLength - overlapLength);
skipFract = 0;
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) + maxOffset;
}
// 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);
}
*/
// 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;
}
*/
if (bMidBufferDirty == FALSE)
{
// if midBuffer is empty, move the first samples of the input stream
// into it
if ((int)inputBuffer.numSamples() < overlapLength)
{
// wait until we've got overlapLength samples
return;
}
memcpy(pMidBuffer, inputBuffer.ptrBegin(), channels * overlapLength * sizeof(SAMPLETYPE));
inputBuffer.receiveSamples((uint)overlapLength);
bMidBufferDirty = TRUE;
}
// Process samples as long as there are enough samples in 'inputBuffer'
// to form a processing frame.
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 = (seekWindowLength - 2 * overlapLength);// & 0xfffffffe;
if (temp > 0)
{
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 + seekWindowLength <= (int)inputBuffer.numSamples());
memcpy(pMidBuffer, inputBuffer.ptrBegin() + channels * (offset + seekWindowLength - overlapLength),
channels * sizeof(SAMPLETYPE) * overlapLength);
bMidBufferDirty = TRUE;
// 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];
bMidBufferDirty = TRUE;
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 *output, const short *input) const
{
int i;
short temp;
uint cnt2;
for (i = 0; i < (int)overlapLength ; i ++)
{
temp = (short)(overlapLength - i);
cnt2 = 2 * i;
output[cnt2] = (input[cnt2] * i + pMidBuffer[cnt2] * temp ) / overlapLength;
output[cnt2 + 1] = (input[cnt2 + 1] * i + pMidBuffer[cnt2 + 1] * temp ) / overlapLength;
}
}
/// 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 overlapMs)
{
int newOvl;
assert(overlapMs >= 0);
overlapDividerBits = _getClosest2Power((sampleRate * overlapMs) / 1000.0);
if (overlapDividerBits > 9) overlapDividerBits = 9;
if (overlapDividerBits < 4) overlapDividerBits = 4;
newOvl = (int)pow(2, overlapDividerBits);
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;
uint i;
corr = 0;
for (i = 1; i < overlapLength; i ++)
{
corr += (mixingPos[i] * compare[i]) >> overlapDividerBits;
}
return corr;
}
long TDStretch::calcCrossCorrStereo(const short *mixingPos, const short *compare) const
{
long corr;
uint i;
corr = 0;
for (i = 2; i < 2 * overlapLength; i += 2)
{
corr += (mixingPos[i] * compare[i] +
mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBits;
}
return corr;
}
#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);
}
}
// SSE-optimized version of the function overlapStereo
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;
int i;
corr = 0;
for (i = 1; i < overlapLength; i ++)
{
corr += mixingPos[i] * compare[i];
}
return corr;
}
double TDStretch::calcCrossCorrStereo(const float *mixingPos, const float *compare) const
{
double corr;
int i;
corr = 0;
for (i = 2; i < 2 * overlapLength; i += 2)
{
corr += mixingPos[i] * compare[i] +
mixingPos[i + 1] * compare[i + 1];
}
return corr;
}
#endif // FLOAT_SAMPLES

View File

@ -0,0 +1,252 @@
////////////////////////////////////////////////////////////////////////////////
///
/// 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: 2008-02-10 18:07:12 +0200 (su, 10 helmi 2008) $
// File revision : $Revision: 4 $
//
// $Id: TDStretch.h 4 2008-02-10 16:07:12Z 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 TDStretch_H
#define TDStretch_H
#include "STTypes.h"
#include "RateTransposer.h"
#include "FIFOSamplePipe.h"
namespace soundtouch
{
// Default values for sound processing parameters:
/// Default length of a single processing sequence, in milliseconds. This determines to how
/// long sequences the original sound is chopped in the time-stretch algorithm.
///
/// The larger this value is, the lesser sequences are used in processing. In principle
/// a bigger value sounds better when slowing down tempo, but worse when increasing tempo
/// and vice versa.
///
/// Increasing this value reduces computational burden & vice versa.
#define DEFAULT_SEQUENCE_MS 82
/// Seeking window default 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.
///
/// The bigger this window setting is, the higher the possibility to find a better mixing
/// position will become, but at the same time large values may cause a "drifting" artifact
/// because consequent sequences will be taken at more uneven intervals.
///
/// If there's a disturbing artifact that sounds as if a constant frequency was drifting
/// around, try reducing this setting.
///
/// Increasing this value increases computational burden & vice versa.
#define DEFAULT_SEEKWINDOW_MS 14
/// 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.
///
/// 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.
///
/// Increasing this value increases computational burden & vice versa.
#define DEFAULT_OVERLAP_MS 12
/// Class that does the time-stretch (tempo change) effect for the processed
/// sound.
class TDStretch : public FIFOProcessor
{
protected:
int channels;
int sampleReq;
float tempo;
SAMPLETYPE *pMidBuffer;
SAMPLETYPE *pRefMidBuffer;
SAMPLETYPE *pRefMidBufferUnaligned;
int overlapLength;
int seekLength;
int seekWindowLength;
int maxOffset;
float nominalSkip;
float skipFract;
FIFOSampleBuffer outputBuffer;
FIFOSampleBuffer inputBuffer;
BOOL bQuickseek;
BOOL bMidBufferDirty;
int sampleRate;
int sequenceMs;
int seekWindowMs;
int overlapMs;
void acceptNewOverlapLength(int newOverlapLength);
virtual void clearCrossCorrState();
void calculateOverlapLength(int overlapMs);
virtual LONG_SAMPLETYPE calcCrossCorrStereo(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare) const;
virtual LONG_SAMPLETYPE calcCrossCorrMono(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare) const;
virtual int seekBestOverlapPositionStereo(const SAMPLETYPE *refPos);
virtual int seekBestOverlapPositionStereoQuick(const SAMPLETYPE *refPos);
virtual int seekBestOverlapPositionMono(const SAMPLETYPE *refPos);
virtual int seekBestOverlapPositionMonoQuick(const SAMPLETYPE *refPos);
int seekBestOverlapPosition(const SAMPLETYPE *refPos);
virtual void overlapStereo(SAMPLETYPE *output, const SAMPLETYPE *input) const;
virtual void overlapMono(SAMPLETYPE *output, const SAMPLETYPE *input) const;
void clearMidBuffer();
void overlap(SAMPLETYPE *output, const SAMPLETYPE *input, uint ovlPos) const;
void precalcCorrReferenceMono();
void precalcCorrReferenceStereo();
/// Changes the tempo of the given sound samples.
/// Returns amount of samples returned in the "output" buffer.
/// The maximum amount of samples that can be returned at a time is set by
/// the 'set_returnBuffer_size' function.
void processSamples();
public:
TDStretch();
virtual ~TDStretch();
/// 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.
static void *operator new(size_t s);
/// Use this function instead of "new" operator to create a new instance of this class.
/// This function automatically chooses a correct feature set depending on if the CPU
/// supports MMX/SSE/etc extensions.
static TDStretch *newInstance();
/// Returns the output buffer object
FIFOSamplePipe *getOutput() { return &outputBuffer; };
/// Returns the input buffer object
FIFOSamplePipe *getInput() { return &inputBuffer; };
/// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower
/// tempo, larger faster tempo.
void setTempo(float newTempo);
/// Returns nonzero if there aren't any samples available for outputting.
virtual void clear();
/// Clears the input buffer
void clearInput();
/// Sets the number of channels, 1 = mono, 2 = stereo
void setChannels(int numChannels);
/// Enables/disables the quick position seeking algorithm. Zero to disable,
/// nonzero to enable
void enableQuickSeek(BOOL enable);
/// Returns nonzero if the quick seeking algorithm is enabled.
BOOL isQuickSeekEnabled() const;
/// 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
/// 'seekwindowMS' = seeking window length for scanning the best overlapping
/// position
/// 'overlapMS' = overlapping length
void setParameters(int sampleRate, ///< Samplerate of sound being processed (Hz)
int sequenceMS = DEFAULT_SEQUENCE_MS, ///< Single processing sequence length (ms)
int seekwindowMS = DEFAULT_SEEKWINDOW_MS, ///< Offset seeking window length (ms)
int overlapMS = DEFAULT_OVERLAP_MS ///< Sequence overlapping length (ms)
);
/// 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 getParameters(int *pSampleRate, int *pSequenceMs, int *pSeekWindowMs, int *pOverlapMs) const;
/// Adds 'numsamples' pcs of samples from the 'samples' memory position into
/// the input of the object.
virtual void putSamples(
const SAMPLETYPE *samples, ///< Input sample data
uint numSamples ///< Number of samples in 'samples' so that one sample
///< contains both channels if stereo
);
};
// Implementation-specific class declarations:
#ifdef ALLOW_MMX
/// Class that implements MMX optimized routines for 16bit integer samples type.
class TDStretchMMX : public TDStretch
{
protected:
long calcCrossCorrStereo(const short *mixingPos, const short *compare) const;
virtual void overlapStereo(short *output, const short *input) const;
virtual void clearCrossCorrState();
};
#endif /// ALLOW_MMX
#ifdef ALLOW_3DNOW
/// Class that implements 3DNow! optimized routines for floating point samples type.
class TDStretch3DNow : public TDStretch
{
protected:
double calcCrossCorrStereo(const float *mixingPos, const float *compare) const;
};
#endif /// ALLOW_3DNOW
#ifdef ALLOW_SSE
/// Class that implements SSE optimized routines for floating point samples type.
class TDStretchSSE : public TDStretch
{
protected:
double calcCrossCorrStereo(const float *mixingPos, const float *compare) const;
};
#endif /// ALLOW_SSE
}
#endif /// TDStretch_H

View File

@ -0,0 +1,62 @@
////////////////////////////////////////////////////////////////////////////////
///
/// A header file for detecting the Intel MMX instructions set extension.
///
/// Please see 'mmx_win.cpp', 'mmx_cpp.cpp' and 'mmx_non_x86.cpp' for the
/// routine implementations for x86 Windows, x86 gnu version and non-x86
/// platforms, respectively.
///
/// Author : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi
/// SoundTouch WWW: http://www.surina.net/soundtouch
///
////////////////////////////////////////////////////////////////////////////////
//
// Last changed : $Date: 2008-02-10 18:07:12 +0200 (su, 10 helmi 2008) $
// File revision : $Revision: 4 $
//
// $Id: cpu_detect.h 4 2008-02-10 16:07:12Z 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 _CPU_DETECT_H_
#define _CPU_DETECT_H_
#include "STTypes.h"
#define SUPPORT_MMX 0x0001
#define SUPPORT_3DNOW 0x0002
#define SUPPORT_ALTIVEC 0x0004
#define SUPPORT_SSE 0x0008
#define SUPPORT_SSE2 0x0010
/// Checks which instruction set extensions are supported by the CPU.
///
/// \return A bitmask of supported extensions, see SUPPORT_... defines.
uint detectCPUextensions(void);
/// Disables given set of instruction extensions. See SUPPORT_... defines.
void disableExtensions(uint wDisableMask);
#endif // _CPU_DETECT_H_

View File

@ -0,0 +1,138 @@
////////////////////////////////////////////////////////////////////////////////
///
/// gcc version of the x86 CPU detect routine.
///
/// This file is to be compiled on any platform with the GNU C compiler.
/// Compiler. Please see 'cpu_detect_x86_win.cpp' for the x86 Windows version
/// of this file.
///
/// Author : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi
/// SoundTouch WWW: http://www.surina.net/soundtouch
///
////////////////////////////////////////////////////////////////////////////////
//
// Last changed : $Date: 2008-02-10 18:07:12 +0200 (su, 10 helmi 2008) $
// File revision : $Revision: 4 $
//
// $Id: cpu_detect_x86_gcc.cpp 4 2008-02-10 16:07:12Z 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"
#ifndef __GNUC__
#error wrong platform - this source code file is for the GNU C compiler.
#endif
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)
{
#ifndef __i386__
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\tpop %%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\tpush %%eax" // store toggled eflags to stack
"\n\tpopf" // load eflags from stack
"\n\tpushf" // save updated eflags to stack
"\n\tpop %%eax" // load from stack
"\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"
// movl $0x00800000, %edx // force enable MMX
"\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
}

View File

@ -0,0 +1,126 @@
////////////////////////////////////////////////////////////////////////////////
///
/// 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: 2008-02-10 18:07:12 +0200 (su, 10 helmi 2008) $
// File revision : $Revision: 4 $
//
// $Id: cpu_detect_x86_win.cpp 4 2008-02-10 16:07:12Z 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
pop eax ; load eax from stack (with eflags)
mov ecx, eax ; save the original eflags values to ecx
xor eax, 0x00200000 ; toggle bit 21
push eax ; store toggled eflags to stack
popfd ; load eflags from stack
pushfd ; save updated eflags to stack
pop eax ; load from stack
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;
}

View File

@ -0,0 +1,305 @@
////////////////////////////////////////////////////////////////////////////////
///
/// 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/vstudio/downloads/tools/ppack/default.aspx
///
/// Author : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi
/// SoundTouch WWW: http://www.surina.net/soundtouch
///
////////////////////////////////////////////////////////////////////////////////
//
// Last changed : $Date: 2008-02-10 18:07:12 +0200 (su, 10 helmi 2008) $
// File revision : $Revision: 4 $
//
// $Id: mmx_optimized.cpp 4 2008-02-10 16:07:12Z 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>
// Calculates cross correlation of two buffers
long TDStretchMMX::calcCrossCorrStereo(const short *pV1, const short *pV2) const
{
const __m64 *pVec1, *pVec2;
__m64 shifter;
__m64 accu;
long corr;
uint i;
pVec1 = (__m64*)pV1;
pVec2 = (__m64*)pV2;
shifter = _m_from_int(overlapDividerBits);
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;
// 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]));
accu = _mm_add_pi32(accu, _mm_sra_pi32(temp, shifter));
temp = _mm_add_pi32(_mm_madd_pi16(pVec1[2], pVec2[2]),
_mm_madd_pi16(pVec1[3], pVec2[3]));
accu = _mm_add_pi32(accu, _mm_sra_pi32(temp, 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);
// Clear MMS state
_m_empty();
return corr;
// 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;
uint 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);
shifter = _m_from_int(overlapDividerBits);
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 *)(((uint)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, const 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 / 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

View File

@ -0,0 +1,490 @@
////////////////////////////////////////////////////////////////////////////////
///
/// 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/vstudio/downloads/tools/ppack/default.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: 2008-02-10 18:07:12 +0200 (su, 10 helmi 2008) $
// File revision : $Revision: 4 $
//
// $Id: sse_optimized.cpp 4 2008-02-10 16:07:12Z 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>
// Calculates cross correlation of two buffers
double TDStretchSSE::calcCrossCorrStereo(const float *pV1, const float *pV2) const
{
int i;
__m128 vSum, *pVec2;
// 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.
pVec2 = (__m128*)pV2;
vSum = _mm_setzero_ps();
// Unroll the loop by factor of 4 * 4 operations
for (i = 0; i < overlapLength / 8; i ++)
{
// vSum += pV1[0..3] * pV2[0..3]
vSum = _mm_add_ps(vSum, _mm_mul_ps(_MM_LOAD(pV1),pVec2[0]));
// vSum += pV1[4..7] * pV2[4..7]
vSum = _mm_add_ps(vSum, _mm_mul_ps(_MM_LOAD(pV1 + 4), pVec2[1]));
// vSum += pV1[8..11] * pV2[8..11]
vSum = _mm_add_ps(vSum, _mm_mul_ps(_MM_LOAD(pV1 + 8), pVec2[2]));
// vSum += pV1[12..15] * pV2[12..15]
vSum = _mm_add_ps(vSum, _mm_mul_ps(_MM_LOAD(pV1 + 12), pVec2[3]));
pV1 += 16;
pVec2 += 4;
}
// return value = vSum[0] + vSum[1] + vSum[2] + vSum[3]
float *pvSum = (float*)&vSum;
return (double)(pvSum[0] + pvSum[1] + pvSum[2] + pvSum[3]);
/* This is approximately corresponding routine in C-language:
double corr;
uint i;
// Calculates the cross-correlation value between 'pV1' and 'pV2' vectors
corr = 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];
pV1 += 16;
pV2 += 16;
}
*/
/* This is 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 = source; // source audio data
pFil = (__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

View File

@ -0,0 +1,28 @@
## Process this file with automake to create Makefile.in
##
## $Id: Makefile.am 4 2008-02-10 16:07:12Z oparviai $
##
## Copyright (C) 2003 - David W. Durham
##
## This file is part of SoundTouch, an audio processing library for pitch/time adjustments
##
## SoundTouch is free software; you can redistribute it and/or modify it under the
## terms of the GNU General Public License as published by the Free Software
## Foundation; either version 2 of the License, or (at your option) any later
## version.
##
## SoundTouch is distributed in the hope that it will be useful, but WITHOUT ANY
## WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
## A PARTICULAR PURPOSE. See the GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License along with
## this program; if not, write to the Free Software Foundation, Inc., 59 Temple
## Place - Suite 330, Boston, MA 02111-1307, USA
include $(top_srcdir)/config/am_include.mk
SUBDIRS=bpm SoundStretch
# set to something if you want other stuff to be included in the distribution tarball
#EXTRA_DIST=

View File

@ -0,0 +1,54 @@
## Process this file with automake to create Makefile.in
##
## $Id: Makefile.am 4 2008-02-10 16:07:12Z oparviai $
##
## Copyright (C) 2003 - David W. Durham
##
## This file is part of SoundTouch, an audio processing library for pitch/time adjustments
##
## SoundTouch is free software; you can redistribute it and/or modify it under the
## terms of the GNU General Public License as published by the Free Software
## Foundation; either version 2 of the License, or (at your option) any later
## version.
##
## SoundTouch is distributed in the hope that it will be useful, but WITHOUT ANY
## WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
## A PARTICULAR PURPOSE. See the GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License along with
## this program; if not, write to the Free Software Foundation, Inc., 59 Temple
## Place - Suite 330, Boston, MA 02111-1307, USA
include $(top_srcdir)/config/am_include.mk
## bin_PROGRAMS is the macro that tells automake the name of the programs to
## install in the bin directory (/usr/local/bin) by default. By setting
## --prefix= at configure time the user can change this (eg: ./configure
## --prefix=/usr will install soundstretch under /usr/bin/soundstretch )
bin_PROGRAMS=soundstretch
noinst_HEADERS=RunParameters.h WavFile.h
# extra files to include in distrubution tarball
EXTRA_DIST=soundstretch.dsp soundstretch.dsw
## for every name listed under bin_PROGRAMS, you have a <prog>_SOURCES. This lists
## all the sources in the current directory that are used to build soundstretch.
soundstretch_SOURCES=main.cpp RunParameters.cpp WavFile.cpp
## soundstretch_LDADD is a list of extras to pass at link time. All the objects
## created by the above soundstretch_SOURCES are automatically linked in, so here I
## list object files from other directories as well as flags passed to the
## linker.
soundstretch_LDADD=../../SoundTouch/libSoundTouch.la ../bpm/libBPM.la -lm
## linker flags
soundstretch_LDFLAGS=-s
## additional compiler flags
soundstretch_CXXFLAGS=-O3 -I../bpm
#clean-local:
# -rm -f additional-files-to-remove-on-make-clean

View File

@ -0,0 +1,293 @@
////////////////////////////////////////////////////////////////////////////////
///
/// A class for parsing the 'soundstretch' application command line parameters
///
/// Author : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi
/// SoundTouch WWW: http://www.surina.net/soundtouch
///
////////////////////////////////////////////////////////////////////////////////
//
// Last changed : $Date: 2008-02-10 18:07:12 +0200 (su, 10 helmi 2008) $
// File revision : $Revision: 4 $
//
// $Id: RunParameters.cpp 4 2008-02-10 16:07:12Z 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 <stdlib.h>
#include "RunParameters.h"
using namespace std;
// Program usage instructions
static const char licenseText[] =
" LICENSE:\n"
" ========\n"
" \n"
" SoundTouch sound processing library\n"
" Copyright (c) Olli Parviainen\n"
" \n"
" This library is free software; you can redistribute it and/or\n"
" modify it under the terms of the GNU Lesser General Public\n"
" License as published by the Free Software Foundation; either\n"
" version 2.1 of the License, or (at your option) any later version.\n"
" \n"
" This library is distributed in the hope that it will be useful,\n"
" but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
" Lesser General Public License for more details.\n"
" \n"
" You should have received a copy of the GNU Lesser General Public\n"
" License along with this library; if not, write to the Free Software\n"
" Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n"
" \n"
"This application is distributed with full source codes; however, if you\n"
"didn't receive them, please visit the author's homepage (see the link above).";
static const char whatText[] =
"This application processes WAV audio files by modifying the sound tempo,\n"
"pitch and playback rate properties independently from each other.\n"
"\n";
static const char usage[] =
"Usage :\n"
" soundstretch infile.wav outfile.wav [switches]\n\n"
"Available switches are:\n"
" -tempo=n : Change sound tempo by n percents (n=-95..+5000 %)\n"
" -pitch=n : Change sound pitch by n semitones (n=-60..+60 semitones)\n"
" -rate=n : Change sound rate by n percents (n=-95..+5000 %)\n"
" -bpm=n : Detect the BPM rate of sound and adjust tempo to meet 'n' BPMs.\n"
" If '=n' is omitted, just detects the BPM rate.\n"
" -quick : Use quicker tempo change algorithm (gain speed, lose quality)\n"
" -naa : Don't use anti-alias filtering (gain speed, lose quality)\n"
" -license : Display the program license text (LGPL)\n";
// Converts a char into lower case
static int _toLowerCase(int c)
{
if (c >= 'A' && c <= 'Z')
{
c += 'a' - 'A';
}
return c;
}
// Constructor
RunParameters::RunParameters(const int nParams, const char *paramStr[])
{
int i;
int nFirstParam;
if (nParams < 3)
{
// Too few parameters
if (nParams > 1 && paramStr[1][0] == '-' &&
_toLowerCase(paramStr[1][1]) == 'l')
{
// '-license' switch
throwLicense();
}
string msg = whatText;
msg += usage;
throw runtime_error(msg.c_str());
}
inFileName = NULL;
outFileName = NULL;
tempoDelta = 0;
pitchDelta = 0;
rateDelta = 0;
quick = 0;
noAntiAlias = 0;
goalBPM = 0;
detectBPM = FALSE;
// Get input & output file names
inFileName = (char*)paramStr[1];
outFileName = (char*)paramStr[2];
if (outFileName[0] == '-')
{
// no outputfile name was given but parameters
outFileName = NULL;
nFirstParam = 2;
}
else
{
nFirstParam = 3;
}
// parse switch parameters
for (i = nFirstParam; i < nParams; i ++)
{
parseSwitchParam(paramStr[i]);
}
checkLimits();
}
// Checks parameter limits
void RunParameters::checkLimits()
{
if (tempoDelta < -95.0f)
{
tempoDelta = -95.0f;
}
else if (tempoDelta > 5000.0f)
{
tempoDelta = 5000.0f;
}
if (pitchDelta < -60.0f)
{
pitchDelta = -60.0f;
}
else if (pitchDelta > 60.0f)
{
pitchDelta = 60.0f;
}
if (rateDelta < -95.0f)
{
rateDelta = -95.0f;
}
else if (rateDelta > 5000.0f)
{
rateDelta = 5000.0f;
}
}
// Unknown switch parameter -- throws an exception with an error message
void RunParameters::throwIllegalParamExp(const string &str) const
{
string msg = "ERROR : Illegal parameter \"";
msg += str;
msg += "\".\n\n";
msg += usage;
throw runtime_error(msg.c_str());
}
void RunParameters::throwLicense() const
{
throw runtime_error(licenseText);
}
float RunParameters::parseSwitchValue(const string &str) const
{
int pos;
pos = str.find_first_of('=');
if (pos < 0)
{
// '=' missing
throwIllegalParamExp(str);
}
// Read numerical parameter value after '='
return (float)atof(str.substr(pos + 1).c_str());
}
// Interprets a single switch parameter string of format "-switch=xx"
// Valid switches are "-tempo=xx", "-pitch=xx" and "-rate=xx". Stores
// switch values into 'params' structure.
void RunParameters::parseSwitchParam(const string &str)
{
int upS;
if (str[0] != '-')
{
// leading hyphen missing => not a valid parameter
throwIllegalParamExp(str);
}
// Take the first character of switch name & change to lower case
upS = _toLowerCase(str[1]);
// interpret the switch name & operate accordingly
switch (upS)
{
case 't' :
// switch '-tempo=xx'
tempoDelta = parseSwitchValue(str);
break;
case 'p' :
// switch '-pitch=xx'
pitchDelta = parseSwitchValue(str);
break;
case 'r' :
// switch '-rate=xx'
rateDelta = parseSwitchValue(str);
break;
case 'b' :
// switch '-bpm=xx'
detectBPM = TRUE;
try
{
goalBPM = parseSwitchValue(str);
}
catch (runtime_error)
{
// illegal or missing bpm value => just calculate bpm
goalBPM = 0;
}
break;
case 'q' :
// switch '-quick'
quick = 1;
break;
case 'n' :
// switch '-naa'
noAntiAlias = 1;
break;
case 'l' :
// switch '-license'
throwLicense();
break;
default:
// unknown switch
throwIllegalParamExp(str);
}
}

View File

@ -0,0 +1,71 @@
////////////////////////////////////////////////////////////////////////////////
///
/// A class for parsing the 'soundstretch' application command line parameters
///
/// Author : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi
/// SoundTouch WWW: http://www.surina.net/soundtouch
///
////////////////////////////////////////////////////////////////////////////////
//
// Last changed : $Date: 2008-02-10 18:07:12 +0200 (su, 10 helmi 2008) $
// File revision : $Revision: 4 $
//
// $Id: RunParameters.h 4 2008-02-10 16:07:12Z 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 RUNPARAMETERS_H
#define RUNPARAMETERS_H
#include "STTypes.h"
#include <string>
using namespace std;
/// Parses command line parameters into program parameters
class RunParameters
{
private:
void throwIllegalParamExp(const string &str) const;
void throwLicense() const;
void parseSwitchParam(const string &str);
void checkLimits();
float parseSwitchValue(const string &str) const;
public:
char *inFileName;
char *outFileName;
float tempoDelta;
float pitchDelta;
float rateDelta;
int quick;
int noAntiAlias;
float goalBPM;
BOOL detectBPM;
RunParameters(const int nParams, const char *paramStr[]);
};
#endif

View File

@ -0,0 +1,711 @@
////////////////////////////////////////////////////////////////////////////////
///
/// Classes for easy reading & writing of WAV sound files.
///
/// For big-endian CPU, define _BIG_ENDIAN_ during compile-time to correctly
/// parse the WAV files with such processors.
///
/// Admittingly, more complete WAV reader routines may exist in public domain,
/// but the reason for 'yet another' one is that those generic WAV reader
/// libraries are exhaustingly large and cumbersome! Wanted to have something
/// simpler here, i.e. something that's not already larger than rest of the
/// SoundTouch/SoundStretch program...
///
/// Author : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi
/// SoundTouch WWW: http://www.surina.net/soundtouch
///
////////////////////////////////////////////////////////////////////////////////
//
// Last changed : $Date: 2008-02-10 18:07:12 +0200 (su, 10 helmi 2008) $
// File revision : $Revision: 4 $
//
// $Id: WavFile.cpp 4 2008-02-10 16:07:12Z 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 <stdio.h>
#include <stdexcept>
#include <string>
#include <assert.h>
#include <limits.h>
#include "WavFile.h"
using namespace std;
const static char riffStr[] = "RIFF";
const static char waveStr[] = "WAVE";
const static char fmtStr[] = "fmt ";
const static char dataStr[] = "data";
//////////////////////////////////////////////////////////////////////////////
//
// Helper functions for swapping byte order to correctly read/write WAV files
// with big-endian CPU's: Define compile-time definition _BIG_ENDIAN_ to
// turn-on the conversion if it appears necessary.
//
// For example, Intel x86 is little-endian and doesn't require conversion,
// while PowerPC of Mac's and many other RISC cpu's are big-endian.
#ifdef BYTE_ORDER
// In gcc compiler detect the byte order automatically
#if BYTE_ORDER == BIG_ENDIAN
// big-endian platform.
#define _BIG_ENDIAN_
#endif
#endif
#ifdef _BIG_ENDIAN_
// big-endian CPU, swap bytes in 16 & 32 bit words
// helper-function to swap byte-order of 32bit integer
static inline void _swap32(unsigned int &dwData)
{
dwData = ((dwData >> 24) & 0x000000FF) |
((dwData >> 8) & 0x0000FF00) |
((dwData << 8) & 0x00FF0000) |
((dwData << 24) & 0xFF000000);
}
// helper-function to swap byte-order of 16bit integer
static inline void _swap16(unsigned short &wData)
{
wData = ((wData >> 8) & 0x00FF) |
((wData << 8) & 0xFF00);
}
// helper-function to swap byte-order of buffer of 16bit integers
static inline void _swap16Buffer(unsigned short *pData, unsigned int dwNumWords)
{
unsigned long i;
for (i = 0; i < dwNumWords; i ++)
{
_swap16(pData[i]);
}
}
#else // BIG_ENDIAN
// little-endian CPU, WAV file is ok as such
// dummy helper-function
static inline void _swap32(unsigned int &dwData)
{
// do nothing
}
// dummy helper-function
static inline void _swap16(unsigned short &wData)
{
// do nothing
}
// dummy helper-function
static inline void _swap16Buffer(unsigned short *pData, unsigned int dwNumBytes)
{
// do nothing
}
#endif // BIG_ENDIAN
//////////////////////////////////////////////////////////////////////////////
//
// Class WavInFile
//
WavInFile::WavInFile(const char *fileName)
{
int hdrsOk;
// Try to open the file for reading
fptr = fopen(fileName, "rb");
if (fptr == NULL)
{
// didn't succeed
string msg = "Error : Unable to open file \"";
msg += fileName;
msg += "\" for reading.";
throw runtime_error(msg);
}
// Read the file headers
hdrsOk = readWavHeaders();
if (hdrsOk != 0)
{
// Something didn't match in the wav file headers
string msg = "File \"";
msg += fileName;
msg += "\" is corrupt or not a WAV file";
throw runtime_error(msg);
}
if (header.format.fixed != 1)
{
string msg = "File \"";
msg += fileName;
msg += "\" uses unsupported encoding.";
throw runtime_error(msg);
}
dataRead = 0;
}
WavInFile::~WavInFile()
{
close();
}
void WavInFile::rewind()
{
int hdrsOk;
fseek(fptr, 0, SEEK_SET);
hdrsOk = readWavHeaders();
assert(hdrsOk == 0);
dataRead = 0;
}
int WavInFile::checkCharTags()
{
// header.format.fmt should equal to 'fmt '
if (memcmp(fmtStr, header.format.fmt, 4) != 0) return -1;
// header.data.data_field should equal to 'data'
if (memcmp(dataStr, header.data.data_field, 4) != 0) return -1;
return 0;
}
int WavInFile::read(char *buffer, int maxElems)
{
int numBytes;
uint afterDataRead;
// ensure it's 8 bit format
if (header.format.bits_per_sample != 8)
{
throw runtime_error("Error: WavInFile::read(char*, int) works only with 8bit samples.");
}
assert(sizeof(char) == 1);
numBytes = maxElems;
afterDataRead = dataRead + numBytes;
if (afterDataRead > header.data.data_len)
{
// Don't read more samples than are marked available in header
numBytes = header.data.data_len - dataRead;
assert(numBytes >= 0);
}
numBytes = fread(buffer, 1, numBytes, fptr);
dataRead += numBytes;
return numBytes;
}
int WavInFile::read(short *buffer, int maxElems)
{
unsigned int afterDataRead;
int numBytes;
int numElems;
if (header.format.bits_per_sample == 8)
{
// 8 bit format
char *temp = new char[maxElems];
int i;
numElems = read(temp, maxElems);
// convert from 8 to 16 bit
for (i = 0; i < numElems; i ++)
{
buffer[i] = temp[i] << 8;
}
delete[] temp;
}
else
{
// 16 bit format
assert(header.format.bits_per_sample == 16);
assert(sizeof(short) == 2);
numBytes = maxElems * 2;
afterDataRead = dataRead + numBytes;
if (afterDataRead > header.data.data_len)
{
// Don't read more samples than are marked available in header
numBytes = header.data.data_len - dataRead;
assert(numBytes >= 0);
}
numBytes = fread(buffer, 1, numBytes, fptr);
dataRead += numBytes;
numElems = numBytes / 2;
// 16bit samples, swap byte order if necessary
_swap16Buffer((unsigned short *)buffer, numElems);
}
return numElems;
}
int WavInFile::read(float *buffer, int maxElems)
{
short *temp = new short[maxElems];
int num;
int i;
double fscale;
num = read(temp, maxElems);
fscale = 1.0 / 32768.0;
// convert to floats, scale to range [-1..+1[
for (i = 0; i < num; i ++)
{
buffer[i] = (float)(fscale * (double)temp[i]);
}
delete[] temp;
return num;
}
int WavInFile::eof() const
{
// return true if all data has been read or file eof has reached
return (dataRead == header.data.data_len || feof(fptr));
}
void WavInFile::close()
{
fclose(fptr);
fptr = NULL;
}
// test if character code is between a white space ' ' and little 'z'
static int isAlpha(char c)
{
return (c >= ' ' && c <= 'z') ? 1 : 0;
}
// test if all characters are between a white space ' ' and little 'z'
static int isAlphaStr(char *str)
{
int c;
c = str[0];
while (c)
{
if (isAlpha(c) == 0) return 0;
str ++;
c = str[0];
}
return 1;
}
int WavInFile::readRIFFBlock()
{
fread(&(header.riff), sizeof(WavRiff), 1, fptr);
// swap 32bit data byte order if necessary
_swap32((unsigned int &)header.riff.package_len);
// header.riff.riff_char should equal to 'RIFF');
if (memcmp(riffStr, header.riff.riff_char, 4) != 0) return -1;
// header.riff.wave should equal to 'WAVE'
if (memcmp(waveStr, header.riff.wave, 4) != 0) return -1;
return 0;
}
int WavInFile::readHeaderBlock()
{
char label[5];
string sLabel;
// lead label string
fread(label, 1, 4, fptr);
label[4] = 0;
if (isAlphaStr(label) == 0) return -1; // not a valid label
// Decode blocks according to their label
if (strcmp(label, fmtStr) == 0)
{
int nLen, nDump;
// 'fmt ' block
memcpy(header.format.fmt, fmtStr, 4);
// read length of the format field
fread(&nLen, sizeof(int), 1, fptr);
// swap byte order if necessary
_swap32((unsigned int &)nLen); // int format_len;
header.format.format_len = nLen;
// calculate how much length differs from expected
nDump = nLen - (sizeof(header.format) - 8);
// if format_len is larger than expected, read only as much data as we've space for
if (nDump > 0)
{
nLen = sizeof(header.format) - 8;
}
// read data
fread(&(header.format.fixed), nLen, 1, fptr);
// swap byte order if necessary
_swap16((unsigned short &)header.format.fixed); // short int fixed;
_swap16((unsigned short &)header.format.channel_number); // short int channel_number;
_swap32((unsigned int &)header.format.sample_rate); // int sample_rate;
_swap32((unsigned int &)header.format.byte_rate); // int byte_rate;
_swap16((unsigned short &)header.format.byte_per_sample); // short int byte_per_sample;
_swap16((unsigned short &)header.format.bits_per_sample); // short int bits_per_sample;
// if format_len is larger than expected, skip the extra data
if (nDump > 0)
{
fseek(fptr, nDump, SEEK_CUR);
}
return 0;
}
else if (strcmp(label, dataStr) == 0)
{
// 'data' block
memcpy(header.data.data_field, dataStr, 4);
fread(&(header.data.data_len), sizeof(uint), 1, fptr);
// swap byte order if necessary
_swap32((unsigned int &)header.data.data_len);
return 1;
}
else
{
uint len, i;
uint temp;
// unknown block
// read length
fread(&len, sizeof(len), 1, fptr);
// scan through the block
for (i = 0; i < len; i ++)
{
fread(&temp, 1, 1, fptr);
if (feof(fptr)) return -1; // unexpected eof
}
}
return 0;
}
int WavInFile::readWavHeaders()
{
int res;
memset(&header, 0, sizeof(header));
res = readRIFFBlock();
if (res) return 1;
// read header blocks until data block is found
do
{
// read header blocks
res = readHeaderBlock();
if (res < 0) return 1; // error in file structure
} while (res == 0);
// check that all required tags are legal
return checkCharTags();
}
uint WavInFile::getNumChannels() const
{
return header.format.channel_number;
}
uint WavInFile::getNumBits() const
{
return header.format.bits_per_sample;
}
uint WavInFile::getBytesPerSample() const
{
return getNumChannels() * getNumBits() / 8;
}
uint WavInFile::getSampleRate() const
{
return header.format.sample_rate;
}
uint WavInFile::getDataSizeInBytes() const
{
return header.data.data_len;
}
uint WavInFile::getNumSamples() const
{
return header.data.data_len / header.format.byte_per_sample;
}
uint WavInFile::getLengthMS() const
{
uint numSamples;
uint sampleRate;
numSamples = getNumSamples();
sampleRate = getSampleRate();
assert(numSamples < UINT_MAX / 1000);
return (1000 * numSamples / sampleRate);
}
//////////////////////////////////////////////////////////////////////////////
//
// Class WavOutFile
//
WavOutFile::WavOutFile(const char *fileName, int sampleRate, int bits, int channels)
{
bytesWritten = 0;
fptr = fopen(fileName, "wb");
if (fptr == NULL)
{
string msg = "Error : Unable to open file \"";
msg += fileName;
msg += "\" for writing.";
//pmsg = msg.c_str;
throw runtime_error(msg);
}
fillInHeader(sampleRate, bits, channels);
writeHeader();
}
WavOutFile::~WavOutFile()
{
close();
}
void WavOutFile::fillInHeader(uint sampleRate, uint bits, uint channels)
{
// fill in the 'riff' part..
// copy string 'RIFF' to riff_char
memcpy(&(header.riff.riff_char), riffStr, 4);
// package_len unknown so far
header.riff.package_len = 0;
// copy string 'WAVE' to wave
memcpy(&(header.riff.wave), waveStr, 4);
// fill in the 'format' part..
// copy string 'fmt ' to fmt
memcpy(&(header.format.fmt), fmtStr, 4);
header.format.format_len = 0x10;
header.format.fixed = 1;
header.format.channel_number = (short)channels;
header.format.sample_rate = sampleRate;
header.format.bits_per_sample = (short)bits;
header.format.byte_per_sample = (short)(bits * channels / 8);
header.format.byte_rate = header.format.byte_per_sample * sampleRate;
header.format.sample_rate = sampleRate;
// fill in the 'data' part..
// copy string 'data' to data_field
memcpy(&(header.data.data_field), dataStr, 4);
// data_len unknown so far
header.data.data_len = 0;
}
void WavOutFile::finishHeader()
{
// supplement the file length into the header structure
header.riff.package_len = bytesWritten + 36;
header.data.data_len = bytesWritten;
writeHeader();
}
void WavOutFile::writeHeader()
{
WavHeader hdrTemp;
// swap byte order if necessary
hdrTemp = header;
_swap32((unsigned int &)hdrTemp.riff.package_len);
_swap32((unsigned int &)hdrTemp.format.format_len);
_swap16((unsigned short &)hdrTemp.format.fixed);
_swap16((unsigned short &)hdrTemp.format.channel_number);
_swap32((unsigned int &)hdrTemp.format.sample_rate);
_swap32((unsigned int &)hdrTemp.format.byte_rate);
_swap16((unsigned short &)hdrTemp.format.byte_per_sample);
_swap16((unsigned short &)hdrTemp.format.bits_per_sample);
_swap32((unsigned int &)hdrTemp.data.data_len);
// write the supplemented header in the beginning of the file
fseek(fptr, 0, SEEK_SET);
fwrite(&hdrTemp, sizeof(hdrTemp), 1, fptr);
// jump back to the end of the file
fseek(fptr, 0, SEEK_END);
}
void WavOutFile::close()
{
finishHeader();
fclose(fptr);
fptr = NULL;
}
void WavOutFile::write(const char *buffer, int numElems)
{
int res;
if (header.format.bits_per_sample != 8)
{
throw runtime_error("Error: WavOutFile::write(const char*, int) accepts only 8bit samples.");
}
assert(sizeof(char) == 1);
res = fwrite(buffer, 1, numElems, fptr);
if (res != numElems)
{
throw runtime_error("Error while writing to a wav file.");
}
bytesWritten += numElems;
}
void WavOutFile::write(const short *buffer, int numElems)
{
int res;
// 16 bit samples
if (numElems < 1) return; // nothing to do
if (header.format.bits_per_sample == 8)
{
int i;
char *temp = new char[numElems];
// convert from 16bit format to 8bit format
for (i = 0; i < numElems; i ++)
{
temp[i] = buffer[i] >> 8;
}
// write in 8bit format
write(temp, numElems);
delete[] temp;
}
else
{
// 16bit format
unsigned short *pTemp = new unsigned short[numElems];
assert(header.format.bits_per_sample == 16);
// allocate temp buffer to swap byte order if necessary
memcpy(pTemp, buffer, numElems * 2);
_swap16Buffer(pTemp, numElems);
res = fwrite(pTemp, 2, numElems, fptr);
delete[] pTemp;
if (res != numElems)
{
throw runtime_error("Error while writing to a wav file.");
}
bytesWritten += 2 * numElems;
}
}
void WavOutFile::write(const float *buffer, int numElems)
{
int i;
short *temp = new short[numElems];
int iTemp;
// convert to 16 bit integer
for (i = 0; i < numElems; i ++)
{
// convert to integer
iTemp = (int)(32768.0f * buffer[i]);
// saturate
if (iTemp < -32768) iTemp = -32768;
if (iTemp > 32767) iTemp = 32767;
temp[i] = (short)iTemp;
}
write(temp, numElems);
delete[] temp;
}

View File

@ -0,0 +1,253 @@
////////////////////////////////////////////////////////////////////////////////
///
/// Classes for easy reading & writing of WAV sound files.
///
/// For big-endian CPU, define BIG_ENDIAN during compile-time to correctly
/// parse the WAV files with such processors.
///
/// Admittingly, more complete WAV reader routines may exist in public domain, but
/// the reason for 'yet another' one is that those generic WAV reader libraries are
/// exhaustingly large and cumbersome! Wanted to have something simpler here, i.e.
/// something that's not already larger than rest of the SoundTouch/SoundStretch program...
///
/// Author : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi
/// SoundTouch WWW: http://www.surina.net/soundtouch
///
////////////////////////////////////////////////////////////////////////////////
//
// Last changed : $Date: 2008-02-10 18:07:12 +0200 (su, 10 helmi 2008) $
// File revision : $Revision: 4 $
//
// $Id: WavFile.h 4 2008-02-10 16:07:12Z 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 WAVFILE_H
#define WAVFILE_H
#include <stdio.h>
#ifndef uint
typedef unsigned int uint;
#endif
/// WAV audio file 'riff' section header
typedef struct
{
char riff_char[4];
int package_len;
char wave[4];
} WavRiff;
/// WAV audio file 'format' section header
typedef struct
{
char fmt[4];
int format_len;
short fixed;
short channel_number;
int sample_rate;
int byte_rate;
short byte_per_sample;
short bits_per_sample;
} WavFormat;
/// WAV audio file 'data' section header
typedef struct
{
char data_field[4];
uint data_len;
} WavData;
/// WAV audio file header
typedef struct
{
WavRiff riff;
WavFormat format;
WavData data;
} WavHeader;
/// Class for reading WAV audio files.
class WavInFile
{
private:
/// File pointer.
FILE *fptr;
/// Counter of how many bytes of sample data have been read from the file.
uint dataRead;
/// WAV header information
WavHeader header;
/// Read WAV file headers.
/// \return zero if all ok, nonzero if file format is invalid.
int readWavHeaders();
/// Checks WAV file header tags.
/// \return zero if all ok, nonzero if file format is invalid.
int checkCharTags();
/// Reads a single WAV file header block.
/// \return zero if all ok, nonzero if file format is invalid.
int readHeaderBlock();
/// Reads WAV file 'riff' block
int readRIFFBlock();
public:
/// Constructor: Opens the given WAV file. If the file can't be opened,
/// throws 'runtime_error' exception.
WavInFile(const char *filename);
/// Destructor: Closes the file.
~WavInFile();
/// Close the file. Notice that file is automatically closed also when the
/// class instance is deleted.
void close();
/// Rewind to beginning of the file
void rewind();
/// Get sample rate.
uint getSampleRate() const;
/// Get number of bits per sample, i.e. 8 or 16.
uint getNumBits() const;
/// Get sample data size in bytes. Ahem, this should return same information as
/// 'getBytesPerSample'...
uint getDataSizeInBytes() const;
/// Get total number of samples in file.
uint getNumSamples() const;
/// Get number of bytes per audio sample (e.g. 16bit stereo = 4 bytes/sample)
uint getBytesPerSample() const;
/// Get number of audio channels in the file (1=mono, 2=stereo)
uint getNumChannels() const;
/// Get the audio file length in milliseconds
uint getLengthMS() const;
/// Reads audio samples from the WAV file. This routine works only for 8 bit samples.
/// Reads given number of elements from the file or if end-of-file reached, as many
/// elements as are left in the file.
///
/// \return Number of 8-bit integers read from the file.
int read(char *buffer, int maxElems);
/// Reads audio samples from the WAV file to 16 bit integer format. Reads given number
/// of elements from the file or if end-of-file reached, as many elements as are
/// left in the file.
///
/// \return Number of 16-bit integers read from the file.
int read(short *buffer, ///< Pointer to buffer where to read data.
int maxElems ///< Size of 'buffer' array (number of array elements).
);
/// Reads audio samples from the WAV file to floating point format, converting
/// sample values to range [-1,1[. Reads given number of elements from the file
/// or if end-of-file reached, as many elements as are left in the file.
///
/// \return Number of elements read from the file.
int read(float *buffer, ///< Pointer to buffer where to read data.
int maxElems ///< Size of 'buffer' array (number of array elements).
);
/// Check end-of-file.
///
/// \return Nonzero if end-of-file reached.
int eof() const;
};
/// Class for writing WAV audio files.
class WavOutFile
{
private:
/// Pointer to the WAV file
FILE *fptr;
/// WAV file header data.
WavHeader header;
/// Counter of how many bytes have been written to the file so far.
int bytesWritten;
/// Fills in WAV file header information.
void fillInHeader(const uint sampleRate, const uint bits, const uint channels);
/// Finishes the WAV file header by supplementing information of amount of
/// data written to file etc
void finishHeader();
/// Writes the WAV file header.
void writeHeader();
public:
/// Constructor: Creates a new WAV file. Throws a 'runtime_error' exception
/// if file creation fails.
WavOutFile(const char *fileName, ///< Filename
int sampleRate, ///< Sample rate (e.g. 44100 etc)
int bits, ///< Bits per sample (8 or 16 bits)
int channels ///< Number of channels (1=mono, 2=stereo)
);
/// Destructor: Finalizes & closes the WAV file.
~WavOutFile();
/// Write data to WAV file. This function works only with 8bit samples.
/// Throws a 'runtime_error' exception if writing to file fails.
void write(const char *buffer, ///< Pointer to sample data buffer.
int numElems ///< How many array items are to be written to file.
);
/// Write data to WAV file. Throws a 'runtime_error' exception if writing to
/// file fails.
void write(const short *buffer, ///< Pointer to sample data buffer.
int numElems ///< How many array items are to be written to file.
);
/// Write data to WAV file in floating point format, saturating sample values to range
/// [-1..+1[. Throws a 'runtime_error' exception if writing to file fails.
void write(const float *buffer, ///< Pointer to sample data buffer.
int numElems ///< How many array items are to be written to file.
);
/// Finalize & close the WAV file. Automatically supplements the WAV file header
/// information according to written data etc.
///
/// Notice that file is automatically closed also when the class instance is deleted.
void close();
};
#endif

View File

@ -0,0 +1,288 @@
////////////////////////////////////////////////////////////////////////////////
///
/// SoundStretch main routine.
///
/// Author : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi
/// SoundTouch WWW: http://www.surina.net/soundtouch
///
////////////////////////////////////////////////////////////////////////////////
//
// Last changed : $Date: 2008-02-10 18:07:12 +0200 (su, 10 helmi 2008) $
// File revision : $Revision: 4 $
//
// $Id: main.cpp 4 2008-02-10 16:07:12Z 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 <stdio.h>
#include "RunParameters.h"
#include "WavFile.h"
#include "SoundTouch.h"
#include "BPMDetect.h"
using namespace soundtouch;
using namespace std;
// Processing chunk size
#define BUFF_SIZE 2048
static const char _helloText[] =
"\n"
" SoundStretch v%s - Written by Olli Parviainen 2001 - 2006\n"
"==================================================================\n"
"author e-mail: <oparviai@iki.fi> - WWW: http://www.surina.net/soundtouch\n"
"\n"
"This program is subject to (L)GPL license. Run \"soundstretch -license\" for\n"
"more information.\n"
"\n";
static void openFiles(WavInFile **inFile, WavOutFile **outFile, const RunParameters *params)
{
int bits, samplerate, channels;
// open input file...
*inFile = new WavInFile(params->inFileName);
// ... open output file with same sound parameters
bits = (*inFile)->getNumBits();
samplerate = (*inFile)->getSampleRate();
channels = (*inFile)->getNumChannels();
if (params->outFileName)
{
*outFile = new WavOutFile(params->outFileName, samplerate, bits, channels);
}
else
{
*outFile = NULL;
}
}
// Sets the 'SoundTouch' object up according to input file sound format &
// command line parameters
static void setup(SoundTouch *pSoundTouch, const WavInFile *inFile, const RunParameters *params)
{
int sampleRate;
int channels;
sampleRate = inFile->getSampleRate();
channels = inFile->getNumChannels();
pSoundTouch->setSampleRate(sampleRate);
pSoundTouch->setChannels(channels);
pSoundTouch->setTempoChange(params->tempoDelta);
pSoundTouch->setPitchSemiTones(params->pitchDelta);
pSoundTouch->setRateChange(params->rateDelta);
pSoundTouch->setSetting(SETTING_USE_QUICKSEEK, params->quick);
pSoundTouch->setSetting(SETTING_USE_AA_FILTER, !params->noAntiAlias);
// print processing information
if (params->outFileName)
{
#ifdef INTEGER_SAMPLES
printf("Uses 16bit integer sample type in processing.\n\n");
#else
#ifndef FLOAT_SAMPLES
#error "Sampletype not defined"
#endif
printf("Uses 32bit floating point sample type in processing.\n\n");
#endif
// print processing information only if outFileName given i.e. some processing will happen
printf("Processing the file with the following changes:\n");
printf(" tempo change = %+g %%\n", params->tempoDelta);
printf(" pitch change = %+g semitones\n", params->pitchDelta);
printf(" rate change = %+g %%\n\n", params->rateDelta);
printf("Working...");
}
else
{
// outFileName not given
printf("Warning: output file name missing, won't output anything.\n\n");
}
fflush(stdout);
}
// Processes the sound
static void process(SoundTouch *pSoundTouch, WavInFile *inFile, WavOutFile *outFile)
{
int nSamples;
int nChannels;
int buffSizeSamples;
SAMPLETYPE sampleBuffer[BUFF_SIZE];
if ((inFile == NULL) || (outFile == NULL)) return; // nothing to do.
nChannels = inFile->getNumChannels();
buffSizeSamples = BUFF_SIZE / nChannels;
// Process samples read from the input file
while (inFile->eof() == 0)
{
int num;
// Read a chunk of samples from the input file
num = inFile->read(sampleBuffer, BUFF_SIZE);
nSamples = num / inFile->getNumChannels();
// Feed the samples into SoundTouch processor
pSoundTouch->putSamples(sampleBuffer, nSamples);
// Read ready samples from SoundTouch processor & write them output file.
// NOTES:
// - 'receiveSamples' doesn't necessarily return any samples at all
// during some rounds!
// - On the other hand, during some round 'receiveSamples' may have more
// ready samples than would fit into 'sampleBuffer', and for this reason
// the 'receiveSamples' call is iterated for as many times as it
// outputs samples.
do
{
nSamples = pSoundTouch->receiveSamples(sampleBuffer, buffSizeSamples);
outFile->write(sampleBuffer, nSamples * nChannels);
} while (nSamples != 0);
}
// Now the input file is processed, yet 'flush' few last samples that are
// hiding in the SoundTouch's internal processing pipeline.
pSoundTouch->flush();
do
{
nSamples = pSoundTouch->receiveSamples(sampleBuffer, buffSizeSamples);
outFile->write(sampleBuffer, nSamples * nChannels);
} while (nSamples != 0);
}
// Detect BPM rate of inFile and adjust tempo setting accordingly if necessary
static void detectBPM(WavInFile *inFile, RunParameters *params)
{
float bpmValue;
int nChannels;
BPMDetect bpm(inFile->getNumChannels(), inFile->getSampleRate());
SAMPLETYPE sampleBuffer[BUFF_SIZE];
// detect bpm rate
printf("Detecting BPM rate...");
fflush(stdout);
nChannels = inFile->getNumChannels();
// Process the 'inFile' in small blocks, repeat until whole file has
// been processed
while (inFile->eof() == 0)
{
int num, samples;
// Read sample data from input file
num = inFile->read(sampleBuffer, BUFF_SIZE);
// Enter the new samples to the bpm analyzer class
samples = num / nChannels;
bpm.inputSamples(sampleBuffer, samples);
}
// Now the whole song data has been analyzed. Read the resulting bpm.
bpmValue = bpm.getBpm();
printf("Done!\n");
// rewind the file after bpm detection
inFile->rewind();
if (bpmValue > 0)
{
printf("Detected BPM rate %.1f\n\n", bpmValue);
}
else
{
printf("Couldn't detect BPM rate.\n\n");
return;
}
if (params->goalBPM > 0)
{
// adjust tempo to given bpm
params->tempoDelta = (params->goalBPM / bpmValue - 1.0f) * 100.0f;
printf("The file will be converted to %.1f BPM\n\n", params->goalBPM);
}
}
int main(const int nParams, const char *paramStr[])
{
WavInFile *inFile;
WavOutFile *outFile;
RunParameters *params;
SoundTouch SoundTouch;
printf(_helloText, SoundTouch::getVersionString());
try
{
// Parse command line parameters
params = new RunParameters(nParams, paramStr);
// Open input & output files
openFiles(&inFile, &outFile, params);
if (params->detectBPM == TRUE)
{
// detect sound BPM (and adjust processing parameters
// accordingly if necessary)
detectBPM(inFile, params);
}
// Setup the 'SoundTouch' object for processing the sound
setup(&SoundTouch, inFile, params);
// Process the sound
process(&SoundTouch, inFile, outFile);
// Close WAV file handles & dispose of the objects
delete inFile;
delete outFile;
delete params;
printf("Done!\n");
}
catch (runtime_error &e)
{
// An exception occurred during processing, display an error message
printf("%s\n", e.what());
return -1;
}
return 0;
}

View File

@ -0,0 +1,137 @@
# Microsoft Developer Studio Project File - Name="soundstretch" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Console Application" 0x0103
CFG=soundstretch - Win32 Debug
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE
!MESSAGE NMAKE /f "soundstretch.mak".
!MESSAGE
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE
!MESSAGE NMAKE /f "soundstretch.mak" CFG="soundstretch - Win32 Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "soundstretch - Win32 Release" (based on "Win32 (x86) Console Application")
!MESSAGE "soundstretch - Win32 Debug" (based on "Win32 (x86) Console Application")
!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""
# PROP Scc_LocalPath ""
CPP=cl.exe
RSC=rc.exe
!IF "$(CFG)" == "soundstretch - Win32 Release"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Release"
# PROP BASE Intermediate_Dir "Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "Release"
# PROP Intermediate_Dir "Release"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD CPP /nologo /W3 /GX /Zi /O2 /I "..\..\..\include" /I "..\bpm" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FD /c
# SUBTRACT CPP /YX
# ADD BASE RSC /l 0x40b /d "NDEBUG"
# ADD RSC /l 0x40b /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
# ADD LINK32 SoundTouch.lib bpm.lib /nologo /subsystem:console /profile /map /debug /machine:I386 /libpath:"..\..\..\lib"
# Begin Special Build Tool
SOURCE="$(InputPath)"
PostBuild_Cmds=copy Release\soundstretch.exe ..\..\..\bin\
# End Special Build Tool
!ELSEIF "$(CFG)" == "soundstretch - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "Debug"
# PROP BASE Intermediate_Dir "Debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "Debug"
# PROP Intermediate_Dir "Debug"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
# ADD CPP /nologo /Zp16 /W3 /Gm /GX /ZI /Od /I "..\..\..\include" /I "..\bpm" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /FD /GZ /c
# SUBTRACT CPP /YX
# ADD BASE RSC /l 0x40b /d "_DEBUG"
# ADD RSC /l 0x40b /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
# ADD LINK32 SoundTouchD.lib bpmD.lib /nologo /subsystem:console /map /debug /machine:I386 /nodefaultlib:"libc" /out:"Debug/soundstretchD.exe" /pdbtype:sept /libpath:"..\..\..\lib"
# SUBTRACT LINK32 /pdb:none
# Begin Special Build Tool
SOURCE="$(InputPath)"
PostBuild_Cmds=copy Debug\soundstretchD.exe ..\..\..\bin\
# End Special Build Tool
!ENDIF
# Begin Target
# Name "soundstretch - Win32 Release"
# Name "soundstretch - Win32 Debug"
# Begin Group "Source Files"
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Source File
SOURCE=.\main.cpp
# End Source File
# Begin Source File
SOURCE=.\RunParameters.cpp
# End Source File
# Begin Source File
SOURCE=.\WavFile.cpp
# End Source File
# End Group
# Begin Group "Header Files"
# PROP Default_Filter "h;hpp;hxx;hm;inl"
# Begin Source File
SOURCE=.\RunParameters.h
# End Source File
# Begin Source File
SOURCE=..\..\..\include\SoundTouch.h
# End Source File
# Begin Source File
SOURCE=..\..\..\include\STTypes.h
# End Source File
# Begin Source File
SOURCE=.\WavFile.h
# End Source File
# End Group
# Begin Group "Resource Files"
# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
# End Group
# End Target
# End Project

View File

@ -0,0 +1,61 @@
Microsoft Developer Studio Workspace File, Format Version 6.00
# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
#
# $Id: soundstretch.dsw 4 2008-02-10 16:07:12Z oparviai $
###############################################################################
Project: "SoundTouch"=..\..\SoundTouch\SoundTouch.dsp - Package Owner=<4>
Package=<5>
{{{
}}}
Package=<4>
{{{
}}}
###############################################################################
Project: "bpm"=..\bpm\bpm.dsp - Package Owner=<4>
Package=<5>
{{{
}}}
Package=<4>
{{{
}}}
###############################################################################
Project: "soundstretch"=.\soundstretch.dsp - Package Owner=<4>
Package=<5>
{{{
}}}
Package=<4>
{{{
Begin Project Dependency
Project_Dep_Name SoundTouch
End Project Dependency
Begin Project Dependency
Project_Dep_Name bpm
End Project Dependency
}}}
###############################################################################
Global:
Package=<5>
{{{
}}}
Package=<3>
{{{
}}}
###############################################################################

View File

@ -0,0 +1,39 @@
Microsoft Visual Studio Solution File, Format Version 8.00
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SoundTouch", "..\..\SoundTouch\SoundTouch.vcproj", "{121A2AA5-7B1F-4871-8436-FB9C837F11D4}"
ProjectSection(ProjectDependencies) = postProject
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bpm", "..\bpm\bpm.vcproj", "{DA262114-26F8-4F52-BC51-111307A6C776}"
ProjectSection(ProjectDependencies) = postProject
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "soundstretch", "soundstretch.vcproj", "{5AACDFFA-D491-44B8-A332-DA7ACCAAF2AF}"
ProjectSection(ProjectDependencies) = postProject
{DA262114-26F8-4F52-BC51-111307A6C776} = {DA262114-26F8-4F52-BC51-111307A6C776}
{121A2AA5-7B1F-4871-8436-FB9C837F11D4} = {121A2AA5-7B1F-4871-8436-FB9C837F11D4}
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfiguration) = preSolution
Debug = Debug
Release = Release
EndGlobalSection
GlobalSection(ProjectConfiguration) = postSolution
{121A2AA5-7B1F-4871-8436-FB9C837F11D4}.Debug.ActiveCfg = Debug|Win32
{121A2AA5-7B1F-4871-8436-FB9C837F11D4}.Debug.Build.0 = Debug|Win32
{121A2AA5-7B1F-4871-8436-FB9C837F11D4}.Release.ActiveCfg = Release|Win32
{121A2AA5-7B1F-4871-8436-FB9C837F11D4}.Release.Build.0 = Release|Win32
{DA262114-26F8-4F52-BC51-111307A6C776}.Debug.ActiveCfg = Debug|Win32
{DA262114-26F8-4F52-BC51-111307A6C776}.Debug.Build.0 = Debug|Win32
{DA262114-26F8-4F52-BC51-111307A6C776}.Release.ActiveCfg = Release|Win32
{DA262114-26F8-4F52-BC51-111307A6C776}.Release.Build.0 = Release|Win32
{5AACDFFA-D491-44B8-A332-DA7ACCAAF2AF}.Debug.ActiveCfg = Debug|Win32
{5AACDFFA-D491-44B8-A332-DA7ACCAAF2AF}.Debug.Build.0 = Debug|Win32
{5AACDFFA-D491-44B8-A332-DA7ACCAAF2AF}.Release.ActiveCfg = Release|Win32
{5AACDFFA-D491-44B8-A332-DA7ACCAAF2AF}.Release.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
EndGlobalSection
GlobalSection(ExtensibilityAddIns) = postSolution
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,239 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="7.10"
Name="soundstretch"
SccProjectName=""
SccLocalPath="">
<Platforms>
<Platform
Name="Win32"/>
</Platforms>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory=".\Debug"
IntermediateDirectory=".\Debug"
ConfigurationType="1"
UseOfMFC="0"
ATLMinimizesCRunTimeLibraryUsage="FALSE"
CharacterSet="2">
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="..\..\..\include,..\bpm"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
BasicRuntimeChecks="3"
RuntimeLibrary="5"
StructMemberAlignment="5"
PrecompiledHeaderFile=".\Debug/soundstretch.pch"
AssemblerListingLocation=".\Debug/"
ObjectFile=".\Debug/"
ProgramDataBaseFileName=".\Debug/"
BrowseInformation="1"
WarningLevel="3"
SuppressStartupBanner="TRUE"
DebugInformationFormat="4"
CompileAs="0"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="SoundTouchD.lib bpmD.lib"
OutputFile="Debug/soundstretchD.exe"
LinkIncremental="2"
SuppressStartupBanner="TRUE"
AdditionalLibraryDirectories="..\..\..\lib"
IgnoreDefaultLibraryNames="libc"
GenerateDebugInformation="TRUE"
ProgramDatabaseFile=".\Debug/soundstretchD.pdb"
GenerateMapFile="TRUE"
MapFileName=".\Debug/soundstretchD.map"
SubSystem="1"
TargetMachine="1"/>
<Tool
Name="VCMIDLTool"
TypeLibraryName=".\Debug/soundstretch.tlb"
HeaderFileName=""/>
<Tool
Name="VCPostBuildEventTool"
CommandLine="copy Debug\soundstretchD.exe ..\..\..\bin\"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="_DEBUG"
Culture="1035"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCXMLDataGeneratorTool"/>
<Tool
Name="VCWebDeploymentTool"/>
<Tool
Name="VCManagedWrapperGeneratorTool"/>
<Tool
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory=".\Release"
IntermediateDirectory=".\Release"
ConfigurationType="1"
UseOfMFC="0"
ATLMinimizesCRunTimeLibraryUsage="FALSE"
CharacterSet="2">
<Tool
Name="VCCLCompilerTool"
Optimization="2"
InlineFunctionExpansion="1"
AdditionalIncludeDirectories="..\..\..\include,..\bpm"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
StringPooling="TRUE"
RuntimeLibrary="4"
EnableFunctionLevelLinking="TRUE"
PrecompiledHeaderFile=".\Release/soundstretch.pch"
AssemblerListingLocation=".\Release/"
ObjectFile=".\Release/"
ProgramDataBaseFileName=".\Release/"
WarningLevel="3"
SuppressStartupBanner="TRUE"
DebugInformationFormat="3"
CompileAs="0"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="SoundTouch.lib bpm.lib"
OutputFile=".\Release/soundstretch.exe"
LinkIncremental="1"
SuppressStartupBanner="TRUE"
AdditionalLibraryDirectories="..\..\..\lib"
GenerateDebugInformation="TRUE"
GenerateMapFile="TRUE"
MapFileName=".\Release/soundstretch.map"
SubSystem="1"
TargetMachine="1"/>
<Tool
Name="VCMIDLTool"
TypeLibraryName=".\Release/soundstretch.tlb"
HeaderFileName=""/>
<Tool
Name="VCPostBuildEventTool"
CommandLine="copy Release\soundstretch.exe ..\..\..\bin\"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="NDEBUG"
Culture="1035"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCXMLDataGeneratorTool"/>
<Tool
Name="VCWebDeploymentTool"/>
<Tool
Name="VCManagedWrapperGeneratorTool"/>
<Tool
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat">
<File
RelativePath="main.cpp">
<FileConfiguration
Name="Debug|Win32">
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=""
PreprocessorDefinitions=""
BasicRuntimeChecks="3"
BrowseInformation="1"/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
Optimization="2"
AdditionalIncludeDirectories=""
PreprocessorDefinitions=""/>
</FileConfiguration>
</File>
<File
RelativePath="RunParameters.cpp">
<FileConfiguration
Name="Debug|Win32">
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=""
PreprocessorDefinitions=""
BasicRuntimeChecks="3"
BrowseInformation="1"/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
Optimization="2"
AdditionalIncludeDirectories=""
PreprocessorDefinitions=""/>
</FileConfiguration>
</File>
<File
RelativePath="WavFile.cpp">
<FileConfiguration
Name="Debug|Win32">
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=""
PreprocessorDefinitions=""
BasicRuntimeChecks="3"
BrowseInformation="1"/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
Optimization="2"
AdditionalIncludeDirectories=""
PreprocessorDefinitions=""/>
</FileConfiguration>
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl">
<File
RelativePath="RunParameters.h">
</File>
<File
RelativePath="..\..\..\include\SoundTouch.h">
</File>
<File
RelativePath="..\..\..\include\STTypes.h">
</File>
<File
RelativePath="WavFile.h">
</File>
</Filter>
<Filter
Name="Resource Files"
Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe">
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@ -0,0 +1,311 @@
////////////////////////////////////////////////////////////////////////////////
///
/// 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: 2008-02-10 18:07:12 +0200 (su, 10 helmi 2008) $
// File revision : $Revision: 4 $
//
// $Id: BPMDetect.cpp 4 2008-02-10 16:07:12Z 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
typedef unsigned short ushort;
/// 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 sampleRate)
{
xcorr = NULL;
buffer = new FIFOSampleBuffer();
decimateSum = 0;
decimateCount = 0;
decimateBy = 0;
this->sampleRate = sampleRate;
this->channels = numChannels;
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
init(numChannels, sampleRate);
}
BPMDetect::~BPMDetect()
{
delete[] xcorr;
delete buffer;
}
/// 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(decimateBy != 0);
outcount = 0;
for (count = 0; count < numsamples; count ++)
{
decimateSum += src[count];
decimateCount ++;
if (decimateCount >= decimateBy)
{
// Store every Nth sample only
out = (LONG_SAMPLETYPE)(decimateSum / decimateBy);
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(SAMPLETYPE *samples, int numSamples)
{
SAMPLETYPE decimated[DECIMATED_BLOCK_SAMPLES];
// convert from stereo to mono if necessary
if (channels == 2)
{
int i;
for (i = 0; i < numSamples; i ++)
{
samples[i] = (samples[i * 2] + samples[i * 2 + 1]) / 2;
}
}
// decimate
numSamples = decimate(decimated, samples, numSamples);
// envelope new samples and add them to buffer
calcEnvelope(decimated, numSamples);
buffer->putSamples(decimated, numSamples);
// when the buffer has enought samples for processing...
if ((int)buffer->numSamples() > windowLen)
{
int processLength;
// how many samples are processed
processLength = buffer->numSamples() - windowLen;
// ... calculate autocorrelations for oldest samples...
updateXCorr(processLength);
// ... and remove them from the buffer
buffer->receiveSamples(processLength);
}
}
void BPMDetect::init(int numChannels, int sampleRate)
{
this->sampleRate = sampleRate;
// 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));
// we do processing in mono mode
buffer->setChannels(1);
buffer->clear();
}
float BPMDetect::getBpm()
{
float 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 60.0f * (((float)sampleRate / (float)decimateBy) / peakPos);
}

View File

@ -0,0 +1,40 @@
## Process this file with automake to create Makefile.in
##
## $Id: Makefile.am 4 2008-02-10 16:07:12Z oparviai $
##
## Copyright (C) 2003 - David W. Durham
##
## This file is part of SoundTouch, an audio processing library for pitch/time adjustments
##
## SoundTouch is free software; you can redistribute it and/or modify it under the
## terms of the GNU General Public License as published by the Free Software
## Foundation; either version 2 of the License, or (at your option) any later
## version.
##
## SoundTouch is distributed in the hope that it will be useful, but WITHOUT ANY
## WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
## A PARTICULAR PURPOSE. See the GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License along with
## this program; if not, write to the Free Software Foundation, Inc., 59 Temple
## Place - Suite 330, Boston, MA 02111-1307, USA
include $(top_srcdir)/config/am_include.mk
# set to something if you want other stuff to be included in the distribution tarball
EXTRA_DIST=bpm.dsp bpm.dsw
noinst_HEADERS=PeakFinder.h
lib_LTLIBRARIES=libBPM.la
libBPM_la_SOURCES=BPMDetect.cpp PeakFinder.cpp
# other compiler flags to add
AM_CXXFLAGS=-O3 -I../../../include
# other linking flags to add
#libBPM_la_LIBADD=

View File

@ -0,0 +1,191 @@
////////////////////////////////////////////////////////////////////////////////
///
/// 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: 2008-02-10 18:07:12 +0200 (su, 10 helmi 2008) $
// File revision : $Revision: 4 $
//
// $Id: PeakFinder.cpp 4 2008-02-10 16:07:12Z 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"
PeakFinder::PeakFinder()
{
}
// 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'
float 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];
}
return sum / wsum;
}
float PeakFinder::detectPeak(const float *data, int minPos, int maxPos)
{
#define max(x, y) (((x) > (y)) ? (x) : (y))
int i;
int peakpos; // position of peak level
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'
this->minPos = minPos;
this->maxPos = maxPos;
// find absolute peak
peakpos = minPos;
peakLevel = data[minPos];
for (i = minPos + 1; i < maxPos; i ++)
{
if (data[i] > peakLevel)
{
peakLevel = data[i];
peakpos = i;
}
}
// find ground positions.
gp1 = findGround(data, peakpos, -1);
gp2 = findGround(data, peakpos, 1);
groundLevel = max(data[gp1], data[gp2]);
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);
}

View File

@ -0,0 +1,85 @@
////////////////////////////////////////////////////////////////////////////////
///
/// 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: 2008-02-10 18:07:12 +0200 (su, 10 helmi 2008) $
// File revision : $Revision: 4 $
//
// $Id: PeakFinder.h 4 2008-02-10 16:07:12Z 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 _PeakFinder_H_
#define _PeakFinder_H_
class PeakFinder
{
protected:
/// Min, max allowed peak positions within the data vector
int minPos, maxPos;
/// Calculates the mass center between given vector items.
float calcMassCenter(const float *data, ///< Data vector.
int firstPos, ///< Index of first vector item beloging to the peak.
int lastPos ///< Index of last vector item beloging to the peak.
) const;
/// Finds the data vector index where the monotoniously decreasing signal crosses the
/// given level.
int findCrossingLevel(const float *data, ///< Data vector.
float level, ///< Goal crossing level.
int peakpos, ///< Peak position index within the data vector.
int direction /// Direction where to proceed from the peak: 1 = right, -1 = left.
) const;
/// Finds the 'ground' level, i.e. smallest level between two neighbouring peaks, to right-
/// or left-hand side of the given peak position.
int findGround(const float *data, /// Data vector.
int peakpos, /// Peak position index within the data vector.
int direction /// Direction where to proceed from the peak: 1 = right, -1 = left.
) const;
public:
/// Constructor.
PeakFinder();
/// Detect exact peak position of the data vector by finding the largest peak 'hump'
/// and calculating the mass-center location of the peak hump.
///
/// \return The exact mass-center location of the largest peak hump.
float detectPeak(const float *data, /// Data vector to be analyzed. The data vector has
/// to be at least 'maxPos' items long.
int minPos, ///< Min allowed peak location within the vector data.
int maxPos ///< Max allowed peak location within the vector data.
);
};
#endif // _PeakFinder_H_

128
source/example/bpm/bpm.dsp Normal file
View File

@ -0,0 +1,128 @@
# Microsoft Developer Studio Project File - Name="bpm" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Static Library" 0x0104
CFG=bpm - Win32 Debug
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE
!MESSAGE NMAKE /f "bpm.mak".
!MESSAGE
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE
!MESSAGE NMAKE /f "bpm.mak" CFG="bpm - Win32 Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "bpm - Win32 Release" (based on "Win32 (x86) Static Library")
!MESSAGE "bpm - Win32 Debug" (based on "Win32 (x86) Static Library")
!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""
# PROP Scc_LocalPath ""
CPP=cl.exe
RSC=rc.exe
!IF "$(CFG)" == "bpm - Win32 Release"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Release"
# PROP BASE Intermediate_Dir "Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "Release"
# PROP Intermediate_Dir "Release"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
# ADD CPP /nologo /W3 /GX /Zi /O2 /I "..\..\..\include" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
# ADD BASE RSC /l 0x40b /d "NDEBUG"
# ADD RSC /l 0x40b /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LIB32=link.exe -lib
# ADD BASE LIB32 /nologo
# ADD LIB32 /nologo
# Begin Special Build Tool
SOURCE="$(InputPath)"
PostBuild_Cmds=copy .\Release\bpm.lib ..\..\..\lib\
# End Special Build Tool
!ELSEIF "$(CFG)" == "bpm - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "Debug"
# PROP BASE Intermediate_Dir "Debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "Debug"
# PROP Intermediate_Dir "Debug"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\..\include" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FR /YX /FD /GZ /c
# ADD BASE RSC /l 0x40b /d "_DEBUG"
# ADD RSC /l 0x40b /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LIB32=link.exe -lib
# ADD BASE LIB32 /nologo
# ADD LIB32 /nologo /out:"Debug\bpmD.lib"
# Begin Special Build Tool
SOURCE="$(InputPath)"
PostBuild_Cmds=copy .\Debug\bpmD.lib ..\..\..\lib\
# End Special Build Tool
!ENDIF
# Begin Target
# Name "bpm - Win32 Release"
# Name "bpm - Win32 Debug"
# Begin Group "Source Files"
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Source File
SOURCE=.\BPMDetect.cpp
# End Source File
# Begin Source File
SOURCE=.\PeakFinder.cpp
# End Source File
# End Group
# Begin Group "Header Files"
# PROP Default_Filter "h;hpp;hxx;hm;inl"
# Begin Source File
SOURCE=..\..\..\include\BPMDetect.h
# End Source File
# Begin Source File
SOURCE=..\..\..\include\FIFOSampleBuffer.h
# End Source File
# Begin Source File
SOURCE=..\..\..\include\FIFOSamplePipe.h
# End Source File
# Begin Source File
SOURCE=.\PeakFinder.h
# End Source File
# Begin Source File
SOURCE=..\..\..\include\STTypes.h
# End Source File
# End Group
# End Target
# End Project

View File

@ -0,0 +1,31 @@
Microsoft Developer Studio Workspace File, Format Version 6.00
# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
#
# $Id: bpm.dsw 4 2008-02-10 16:07:12Z oparviai $
###############################################################################
Project: "bpm"=.\bpm.dsp - Package Owner=<4>
Package=<5>
{{{
}}}
Package=<4>
{{{
}}}
###############################################################################
Global:
Package=<5>
{{{
}}}
Package=<3>
{{{
}}}
###############################################################################

View File

@ -0,0 +1,192 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="7.10"
Name="bpm"
SccProjectName=""
SccLocalPath="">
<Platforms>
<Platform
Name="Win32"/>
</Platforms>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory=".\Debug"
IntermediateDirectory=".\Debug"
ConfigurationType="4"
UseOfMFC="0"
ATLMinimizesCRunTimeLibraryUsage="FALSE"
CharacterSet="2">
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="..\..\..\include"
PreprocessorDefinitions="WIN32;_DEBUG;_LIB"
BasicRuntimeChecks="3"
RuntimeLibrary="5"
UsePrecompiledHeader="2"
PrecompiledHeaderFile=".\Debug/bpm.pch"
AssemblerListingLocation=".\Debug/"
ObjectFile=".\Debug/"
ProgramDataBaseFileName=".\Debug/"
BrowseInformation="1"
WarningLevel="3"
SuppressStartupBanner="TRUE"
DebugInformationFormat="4"
CompileAs="0"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLibrarianTool"
OutputFile="Debug\bpmD.lib"
SuppressStartupBanner="TRUE"/>
<Tool
Name="VCMIDLTool"/>
<Tool
Name="VCPostBuildEventTool"
CommandLine="copy .\Debug\bpmD.lib ..\..\..\lib\"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="_DEBUG"
Culture="1035"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCXMLDataGeneratorTool"/>
<Tool
Name="VCManagedWrapperGeneratorTool"/>
<Tool
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory=".\Release"
IntermediateDirectory=".\Release"
ConfigurationType="4"
UseOfMFC="0"
ATLMinimizesCRunTimeLibraryUsage="FALSE"
CharacterSet="2">
<Tool
Name="VCCLCompilerTool"
Optimization="2"
InlineFunctionExpansion="1"
AdditionalIncludeDirectories="..\..\..\include"
PreprocessorDefinitions="WIN32;NDEBUG;_LIB"
StringPooling="TRUE"
RuntimeLibrary="4"
EnableFunctionLevelLinking="TRUE"
UsePrecompiledHeader="2"
PrecompiledHeaderFile=".\Release/bpm.pch"
AssemblerListingLocation=".\Release/"
ObjectFile=".\Release/"
ProgramDataBaseFileName=".\Release/"
WarningLevel="3"
SuppressStartupBanner="TRUE"
DebugInformationFormat="3"
CompileAs="0"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLibrarianTool"
OutputFile=".\Release\bpm.lib"
SuppressStartupBanner="TRUE"/>
<Tool
Name="VCMIDLTool"/>
<Tool
Name="VCPostBuildEventTool"
CommandLine="copy .\Release\bpm.lib ..\..\..\lib\"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="NDEBUG"
Culture="1035"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCXMLDataGeneratorTool"/>
<Tool
Name="VCManagedWrapperGeneratorTool"/>
<Tool
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat">
<File
RelativePath="BPMDetect.cpp">
<FileConfiguration
Name="Debug|Win32">
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=""
PreprocessorDefinitions=""
BasicRuntimeChecks="3"
BrowseInformation="1"/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
Optimization="2"
AdditionalIncludeDirectories=""
PreprocessorDefinitions=""/>
</FileConfiguration>
</File>
<File
RelativePath="PeakFinder.cpp">
<FileConfiguration
Name="Debug|Win32">
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=""
PreprocessorDefinitions=""
BasicRuntimeChecks="3"
BrowseInformation="1"/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
Optimization="2"
AdditionalIncludeDirectories=""
PreprocessorDefinitions=""/>
</FileConfiguration>
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl">
<File
RelativePath="..\..\..\include\BPMDetect.h">
</File>
<File
RelativePath="..\..\..\include\FIFOSampleBuffer.h">
</File>
<File
RelativePath="..\..\..\include\FIFOSamplePipe.h">
</File>
<File
RelativePath="PeakFinder.h">
</File>
<File
RelativePath="..\..\..\include\STTypes.h">
</File>
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>