got the directory wrong again

git-svn-id: svn://localhost/ardour2/branches/2.0-ongoing@2769 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Paul Davis 2007-12-11 15:17:50 +00:00
parent 7d23cd1b31
commit c142508df6
48 changed files with 0 additions and 15875 deletions

View file

@ -1,280 +0,0 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
675 Mass Ave, Cambridge, MA 02139, USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, 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 or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
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 give any other recipients of the Program a copy of this License
along with the Program.
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 Program or any portion
of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
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 Program, 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 Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) 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; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, 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 executable. However, as a
special exception, the source code 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.
If distribution of executable or 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 counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program 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.
5. 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 Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program 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 to
this License.
7. 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 Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program 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 Program.
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.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program 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.
9. The Free Software Foundation may publish revised and/or new versions
of the 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 Program
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 Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, 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
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS

View file

@ -1,189 +0,0 @@
CXX := @CXX@
CXXFLAGS := -DUSE_PTHREADS -DHAVE_LIBSAMPLERATE -DHAVE_FFTW3 -DFFTW_DOUBLE_ONLY @CXXFLAGS@ @SRC_CFLAGS@ @SNDFILE_CFLAGS@ @FFTW_CFLAGS@ @Vamp_CFLAGS@ -Irubberband -Isrc $(OPTFLAGS)
LDFLAGS := @LDFLAGS@ -lpthread $(LDFLAGS)
LIBRARY_LIBS := @SRC_LIBS@ @FFTW_LIBS@
PROGRAM_LIBS := @SNDFILE_LIBS@ $(LIBRARY_LIBS)
VAMP_PLUGIN_LIBS := @Vamp_LIBS@ $(LIBRARY_LIBS)
LADSPA_PLUGIN_LIBS := $(LIBRARY_LIBS)
MKDIR := mkdir
AR := ar
DYNAMIC_LDFLAGS := -shared -Wl,-Bsymbolic
DYNAMIC_EXTENSION := .so
PROGRAM_TARGET := bin/rubberband
STATIC_TARGET := lib/librubberband.a
DYNAMIC_TARGET := lib/librubberband$(DYNAMIC_EXTENSION)
VAMP_TARGET := lib/vamp-rubberband$(DYNAMIC_EXTENSION)
LADSPA_TARGET := lib/ladspa-rubberband$(DYNAMIC_EXTENSION)
INSTALL_BINDIR := @prefix@/bin
INSTALL_INCDIR := @prefix@/include/rubberband
INSTALL_LIBDIR := @prefix@/lib
INSTALL_VAMPDIR := @prefix@/lib/vamp
INSTALL_LADSPADIR := @prefix@/lib/ladspa
INSTALL_PKGDIR := @prefix@/lib/pkgconfig
all: bin lib $(PROGRAM_TARGET) $(STATIC_TARGET) $(DYNAMIC_TARGET) $(VAMP_TARGET) $(LADSPA_TARGET)
PUBLIC_INCLUDES := \
rubberband/TimeStretcher.h \
rubberband/RubberBandStretcher.h
LIBRARY_INCLUDES := \
src/AudioCurve.h \
src/ConstantAudioCurve.h \
src/FFT.h \
src/HighFrequencyAudioCurve.h \
src/PercussiveAudioCurve.h \
src/Resampler.h \
src/RingBuffer.h \
src/Scavenger.h \
src/SpectralDifferenceAudioCurve.h \
src/StretchCalculator.h \
src/StretcherImpl.h \
src/StretcherChannelData.h \
src/Thread.h \
src/Window.h \
src/sysutils.h
LIBRARY_SOURCES := \
src/RubberBandStretcher.cpp \
src/ConstantAudioCurve.cpp \
src/HighFrequencyAudioCurve.cpp \
src/PercussiveAudioCurve.cpp \
src/AudioCurve.cpp \
src/Resampler.cpp \
src/SpectralDifferenceAudioCurve.cpp \
src/StretchCalculator.cpp \
src/StretcherImpl.cpp \
src/StretcherProcess.cpp \
src/StretcherChannelData.cpp \
src/FFT.cpp \
src/Thread.cpp \
src/sysutils.cpp
PROGRAM_SOURCES := \
src/main.cpp
VAMP_HEADERS := \
src/vamp/RubberBandVampPlugin.h
VAMP_SOURCES := \
src/vamp/RubberBandVampPlugin.cpp \
src/vamp/libmain.cpp
LADSPA_HEADERS := \
src/ladspa/RubberBandPitchShifter.h
LADSPA_SOURCES := \
src/ladspa/RubberBandPitchShifter.cpp \
src/ladspa/libmain.cpp
LIBRARY_OBJECTS := $(LIBRARY_SOURCES:.cpp=.o)
PROGRAM_OBJECTS := $(PROGRAM_SOURCES:.cpp=.o)
VAMP_OBJECTS := $(VAMP_SOURCES:.cpp=.o)
LADSPA_OBJECTS := $(LADSPA_SOURCES:.cpp=.o)
$(PROGRAM_TARGET): $(LIBRARY_OBJECTS) $(PROGRAM_OBJECTS)
$(CXX) -o $@ $^ $(PROGRAM_LIBS) $(PROGRAM_LIBS) $(LDFLAGS)
$(STATIC_TARGET): $(LIBRARY_OBJECTS)
$(AR) rsc $@ $^
$(DYNAMIC_TARGET): $(LIBRARY_OBJECTS)
$(CXX) $(DYNAMIC_LDFLAGS) $^ -o $@ $(LIBRARY_LIBS) $(LDFLAGS)
$(VAMP_TARGET): $(LIBRARY_OBJECTS) $(VAMP_OBJECTS)
$(CXX) $(DYNAMIC_LDFLAGS) -o $@ $^ $(VAMP_PLUGIN_LIBS) $(LDFLAGS)
$(LADSPA_TARGET): $(LIBRARY_OBJECTS) $(LADSPA_OBJECTS)
$(CXX) $(DYNAMIC_LDFLAGS) -o $@ $^ $(LADSPA_PLUGIN_LIBS) $(LDFLAGS)
bin:
$(MKDIR) $@
lib:
$(MKDIR) $@
install: all
$(MKDIR) -p $(INSTALL_BINDIR)
$(MKDIR) -p $(INSTALL_INCDIR)
$(MKDIR) -p $(INSTALL_LIBDIR)
$(MKDIR) -p $(INSTALL_VAMPDIR)
$(MKDIR) -p $(INSTALL_LADSPADIR)
cp $(PROGRAM_TARGET) $(INSTALL_BINDIR)
cp $(PUBLIC_INCLUDES) $(INSTALL_INCDIR)
cp $(STATIC_TARGET) $(INSTALL_LIBDIR)
cp $(DYNAMIC_TARGET) $(INSTALL_LIBDIR)
cp $(VAMP_TARGET) $(INSTALL_VAMPDIR)
cp src/vamp/vamp-rubberband.cat $(INSTALL_VAMPDIR)
cp $(LADSPA_TARGET) $(INSTALL_LADSPADIR)
cp src/ladspa/ladspa-rubberband.cat $(INSTALL_LADSPADIR)
sed "s,%PREFIX%,@prefix@," rubberband.pc.in \
> $(INSTALL_PKGDIR)/rubberband.pc
clean:
rm -f $(LIBRARY_OBJECTS) $(PROGRAM_OBJECTS) $(LADSPA_OBJECTS) $(VAMP_OBJECTS)
distclean: clean
rm -f $(PROGRAM_TARGET) $(STATIC_TARGET) $(DYNAMIC_TARGET) $(VAMP_TARGET) $(LADSPA_TARGET)
# DO NOT DELETE
src/AudioCurve.o: src/AudioCurve.h
src/ConstantAudioCurve.o: src/ConstantAudioCurve.h src/AudioCurve.h
src/FFT.o: src/FFT.h src/Thread.h
src/HighFrequencyAudioCurve.o: src/HighFrequencyAudioCurve.h src/AudioCurve.h
src/HighFrequencyAudioCurve.o: src/Window.h
src/main.o: src/sysutils.h
src/PercussiveAudioCurve.o: src/PercussiveAudioCurve.h src/AudioCurve.h
src/Resampler.o: src/Resampler.h
src/RubberBandStretcher.o: src/StretcherImpl.h src/Window.h src/Thread.h
src/RubberBandStretcher.o: src/RingBuffer.h src/Scavenger.h src/sysutils.h
src/RubberBandStretcher.o: src/FFT.h
src/SpectralDifferenceAudioCurve.o: src/SpectralDifferenceAudioCurve.h
src/SpectralDifferenceAudioCurve.o: src/AudioCurve.h src/Window.h
src/StretchCalculator.o: src/StretchCalculator.h
src/StretcherChannelData.o: src/StretcherChannelData.h src/StretcherImpl.h
src/StretcherChannelData.o: src/Window.h src/Thread.h src/RingBuffer.h
src/StretcherChannelData.o: src/Scavenger.h src/sysutils.h src/FFT.h
src/StretcherChannelData.o: src/Resampler.h
src/StretcherImpl.o: src/StretcherImpl.h src/Window.h src/Thread.h
src/StretcherImpl.o: src/RingBuffer.h src/Scavenger.h src/sysutils.h
src/StretcherImpl.o: src/FFT.h src/PercussiveAudioCurve.h src/AudioCurve.h
src/StretcherImpl.o: src/HighFrequencyAudioCurve.h
src/StretcherImpl.o: src/SpectralDifferenceAudioCurve.h
src/StretcherImpl.o: src/ConstantAudioCurve.h src/StretchCalculator.h
src/StretcherImpl.o: src/StretcherChannelData.h src/Resampler.h
src/StretcherProcess.o: src/StretcherImpl.h src/Window.h src/Thread.h
src/StretcherProcess.o: src/RingBuffer.h src/Scavenger.h src/sysutils.h
src/StretcherProcess.o: src/FFT.h src/PercussiveAudioCurve.h src/AudioCurve.h
src/StretcherProcess.o: src/HighFrequencyAudioCurve.h
src/StretcherProcess.o: src/ConstantAudioCurve.h src/StretchCalculator.h
src/StretcherProcess.o: src/StretcherChannelData.h src/Resampler.h
src/sysutils.o: src/sysutils.h
src/Thread.o: src/Thread.h
src/ConstantAudioCurve.o: src/AudioCurve.h
src/HighFrequencyAudioCurve.o: src/AudioCurve.h src/Window.h
src/PercussiveAudioCurve.o: src/AudioCurve.h
src/RingBuffer.o: src/Scavenger.h src/Thread.h src/sysutils.h
src/Scavenger.o: src/Thread.h src/sysutils.h
src/SpectralDifferenceAudioCurve.o: src/AudioCurve.h src/Window.h
src/StretcherChannelData.o: src/StretcherImpl.h src/Window.h src/Thread.h
src/StretcherChannelData.o: src/RingBuffer.h src/Scavenger.h src/sysutils.h
src/StretcherChannelData.o: src/FFT.h
src/StretcherImpl.o: src/Window.h src/Thread.h src/RingBuffer.h
src/StretcherImpl.o: src/Scavenger.h src/sysutils.h src/FFT.h
src/vamp/libmain.o: src/vamp/RubberBandVampPlugin.h
src/vamp/RubberBandVampPlugin.o: src/vamp/RubberBandVampPlugin.h
src/vamp/RubberBandVampPlugin.o: src/StretchCalculator.h
src/ladspa/libmain.o: src/ladspa/RubberBandPitchShifter.h src/RingBuffer.h
src/ladspa/libmain.o: src/Scavenger.h src/Thread.h src/sysutils.h
src/ladspa/RubberBandPitchShifter.o: src/ladspa/RubberBandPitchShifter.h
src/ladspa/RubberBandPitchShifter.o: src/RingBuffer.h src/Scavenger.h
src/ladspa/RubberBandPitchShifter.o: src/Thread.h src/sysutils.h
src/ladspa/RubberBandPitchShifter.o: src/RingBuffer.h src/Scavenger.h
src/ladspa/RubberBandPitchShifter.o: src/Thread.h src/sysutils.h

View file

@ -1,158 +0,0 @@
Rubber Band
===========
An audio time-stretching and pitch-shifting library and utility program.
Copyright 2007 Chris Cannam, cannam@all-day-breakfast.com.
Distributed under the GNU General Public License.
Rubber Band is a library and utility program that permits you to
change the tempo and pitch of an audio recording independently of one
another.
Attractive features
~~~~~~~~~~~~~~~~~~~
* High quality results suitable for musical use
Rubber Band is a phase-vocoder-based frequency domain time
stretcher with partial phase locking to peak frequencies and phase
resynchronisation at noisy transients. It is suitable for most
musical uses with its default settings, and has a range of options
for fine tuning.
* Real-time capable
In addition to the offline mode (for use in situations where all
audio data is available beforehand), Rubber Band supports a true
real-time, lock-free streaming mode, in which the time and pitch
scaling ratios may be dynamically adjusted during use.
* Sample-accurate duration adjustment
In offline mode, Rubber Band ensures that the output has exactly
the right number of samples for the given stretch ratio. (In
real-time mode Rubber Band aims to keep as closely as possible to
the exact ratio, although this depends on the audio material
itself.)
* Multiprocessor/multi-core support
Rubber Band's offline mode can take advantage of more than one
processor core if available, when processing data with two or more
audio channels.
* No job too big, or too small
Rubber Band is tuned so as to work well with the default settings
for any stretch ratio, from tiny deviations from the original
speed to very extreme stretches.
* Handy utilities included
The Rubber Band code includes a useful command-line time-stretch
and pitch shift utility (called simply rubberband), two LADSPA
pitch shifter plugins (Rubber Band Mono Pitch Shifter and Rubber
Band Stereo Pitch Shifter), and a Vamp audio analysis plugin which
may be used to inspect the stretch profile decisions Rubber Band
is taking.
* Free Software
Rubber Band is Free Software published under the GNU General
Public License.
Limitations
~~~~~~~~~~~
* Not especially fast
The algorithm used by Rubber Band is very processor intensive, and
Rubber Band is not the fastest implementation on earth.
* Not especially state of the art
Rubber Band employs well known algorithms which work well in many
situations, but it isn't "cutting edge" in any interesting sense.
* Relatively complex
While the fundamental algorithms in Rubber Band are not especially
complex, the implementation is complicated by the support for
multiple processing modes, exact sample precision, threading, and
other features that add to the flexibility of the API.
Compiling Rubber Band
---------------------
Rubber Band is supplied with build scripts that have been tested on
Linux platforms. It is also possible to build Rubber Band on other
platforms, including both POSIX platforms such as OS/X and non-POSIX
platforms such as Win32. There are some example Makefiles in the misc
directory, but if you're using a proprietary platform and you get
stuck I'm afraid you're on your own, unless you want to pay us...
To build Rubber Band you will also need libsndfile, libsamplerate,
FFTW3, the Vamp plugin SDK, the LADSPA plugin header, the pthread
library (except on Win32), and a C++ compiler. The code has been
tested with GCC 4.x and with the Intel C++ compiler.
Rubber Band comes with a simple autoconf script. Run
$ ./configure
$ make
to compile, and optionally
# make install
to install.
Using the Rubber Band utility
-----------------------------
The Rubber Band command-line utility builds as bin/rubberband. The
basic incantation is
$ rubberband -t <timeratio> -p <pitchratio> <infile.wav> <outfile.wav>
For example,
$ rubberband -t 1.5 -p 2.0 test.wav output.wav
stretches the file test.wav to 50% longer than its original duration,
shifts it up in pitch by one octave, and writes the output to output.wav.
Several further options are available: run "rubberband -h" for help.
In particular, different types of music may benefit from different
"crispness" options (-c <n> where <n> is from 0 to 5).
Using the Rubber Band library
-----------------------------
The Rubber Band library has a public API that consists of one C++
class, called RubberBandStretcher in the RubberBand namespace. You
should #include <rubberband/RubberBandStretcher.h> to use this class.
There is extensive documentation in the class header.
The source code for the command-line utility (src/main.cpp) provides a
good example of how to use Rubber Band in offline mode; the LADSPA
pitch shifter plugin (src/ladspa/RubberBandPitchShifter.cpp) may be
used as an example of Rubber Band in real-time mode.
IMPORTANT: Please ensure you have read and understood the licensing
terms for Rubber Band before using it in another application. This
library is provided under the GNU General Public License, which means
that any application that uses it must also be published under the GPL
or a compatible license (i.e. with its full source code also available
for modification and redistribution). See the file COPYING for more
details. Alternative commercial and proprietary licensing terms are
available; please contact the author if you are interested.

File diff suppressed because it is too large Load diff

View file

@ -1,38 +0,0 @@
AC_INIT(RubberBand, 0.1, cannam@all-day-breakfast.com)
AC_CONFIG_SRCDIR(src/StretcherImpl.h)
AC_PROG_CXX
AC_HEADER_STDC
AC_C_BIGENDIAN
PKG_CHECK_MODULES([SRC],[samplerate])
AC_SUBST(SRC_CFLAGS)
AC_SUBST(SRC_LIBS)
PKG_CHECK_MODULES([SNDFILE],[sndfile])
AC_SUBST(SNDFILE_CFLAGS)
AC_SUBST(SNDFILE_LIBS)
PKG_CHECK_MODULES([FFTW],[fftw3])
AC_SUBST(FFTW_CFLAGS)
AC_SUBST(FFTW_LIBS)
AC_CHECK_HEADERS(ladspa.h)
AC_CHECK_HEADERS(pthread.h)
PKG_CHECK_MODULES([Vamp],[vamp-sdk])
AC_SUBST(Vamp_CFLAGS)
AC_SUBST(Vamp_LIBS)
changequote(,)dnl
if test "x$GCC" = "xyes"; then
case " $CXXFLAGS " in
*[\ \ ]-fPIC\ -Wall[\ \ ]*) ;;
*) CXXFLAGS="$CXXFLAGS -fPIC -Wall" ;;
esac
fi
changequote([,])dnl
AC_OUTPUT([Makefile])

View file

@ -1,144 +0,0 @@
CXX = g++
CXXFLAGS = -isysroot /Developer/SDKs/MacOSX10.4u.sdk -O3 -arch i386 -arch ppc -msse -msse2 -I../include -I../vamp-plugin-sdk -Irubberband -Isrc
LDFLAGS = -L../lib -L../vamp-plugin-sdk/vamp-sdk
LIBRARY_LIBS = -lsamplerate -lfftw3 -lfftw3f
PROGRAM_LIBS = -lsndfile $(LIBRARY_LIBS)
VAMP_PLUGIN_LIBS = -lvamp-sdk $(LIBRARY_LIBS)
LADSPA_PLUGIN_LIBS = $(LIBRARY_LIBS)
MKDIR = mkdir
AR = ar
PROGRAM_TARGET := bin/rubberband
STATIC_TARGET := lib/librubberband.a
DYNAMIC_TARGET := lib/librubberband.dylib
VAMP_TARGET := lib/vamp-rubberband.dylib
LADSPA_TARGET := lib/ladspa-rubberband.dylib
#DYNAMIC_LDFLAGS := -shared -Wl,-Bsymbolic
DYNAMIC_LDFLAGS := -dynamiclib
all: bin lib $(PROGRAM_TARGET) $(STATIC_TARGET) $(DYNAMIC_TARGET) $(VAMP_TARGET) $(LADSPA_TARGET)
PUBLIC_INCLUDES := \
rubberband/TimeStretcher.h \
rubberband/RubberBandStretcher.h
LIBRARY_INCLUDES := \
src/AudioCurve.h \
src/ConstantAudioCurve.h \
src/FFT.h \
src/HighFrequencyAudioCurve.h \
src/PercussiveAudioCurve.h \
src/Resampler.h \
src/RingBuffer.h \
src/Scavenger.h \
src/StretchCalculator.h \
src/StretcherImpl.h \
src/StretcherChannelData.h \
src/Thread.h \
src/Window.h \
src/sysutils.h
LIBRARY_SOURCES := \
src/RubberBandStretcher.cpp \
src/ConstantAudioCurve.cpp \
src/HighFrequencyAudioCurve.cpp \
src/PercussiveAudioCurve.cpp \
src/AudioCurve.cpp \
src/Resampler.cpp \
src/StretchCalculator.cpp \
src/StretcherImpl.cpp \
src/StretcherProcess.cpp \
src/StretcherChannelData.cpp \
src/FFT.cpp \
src/Thread.cpp \
src/sysutils.cpp
PROGRAM_SOURCES := \
src/main.cpp
VAMP_HEADERS := \
src/vamp/RubberBandVampPlugin.h
VAMP_SOURCES := \
src/vamp/RubberBandVampPlugin.cpp \
src/vamp/libmain.cpp
LADSPA_HEADERS := \
src/ladspa/RubberBandPitchShifter.h
LADSPA_SOURCES := \
src/ladspa/RubberBandPitchShifter.cpp \
src/ladspa/libmain.cpp
LIBRARY_OBJECTS := $(LIBRARY_SOURCES:.cpp=.o)
PROGRAM_OBJECTS := $(PROGRAM_SOURCES:.cpp=.o)
VAMP_OBJECTS := $(VAMP_SOURCES:.cpp=.o)
LADSPA_OBJECTS := $(LADSPA_SOURCES:.cpp=.o)
$(PROGRAM_TARGET): $(LIBRARY_OBJECTS) $(PROGRAM_OBJECTS)
$(CXX) -o $@ $^ $(PROGRAM_LIBS) $(PROGRAM_LIBS) $(LDFLAGS)
$(STATIC_TARGET): $(LIBRARY_OBJECTS)
$(AR) rsc $@ $^
$(DYNAMIC_TARGET): $(LIBRARY_OBJECTS)
$(CXX) $(DYNAMIC_LDFLAGS) $^ -o $@ $(LIBRARY_LIBS) $(LDFLAGS)
$(VAMP_TARGET): $(LIBRARY_OBJECTS) $(VAMP_OBJECTS)
$(CXX) $(DYNAMIC_LDFLAGS) -o $@ $^ $(VAMP_PLUGIN_LIBS) $(LDFLAGS)
$(LADSPA_TARGET): $(LIBRARY_OBJECTS) $(LADSPA_OBJECTS)
$(CXX) $(DYNAMIC_LDFLAGS) -o $@ $^ $(LADSPA_PLUGIN_LIBS) $(LDFLAGS)
bin:
$(MKDIR) $@
lib:
$(MKDIR) $@
clean:
rm -f $(LIBRARY_OBJECTS) $(PROGRAM_OBJECTS) $(LADSPA_OBJECTS) $(VAMP_OBJECTS)
distclean: clean
rm -f $(PROGRAM_TARGET) $(STATIC_TARGET) $(DYNAMIC_TARGET) $(VAMP_TARGET) $(LADSPA_TARGET)
# DO NOT DELETE
src/AudioCurve.o: src/AudioCurve.h
src/ConstantAudioCurve.o: src/ConstantAudioCurve.h src/AudioCurve.h
src/FFT.o: src/FFT.h src/Thread.h
src/HighFrequencyAudioCurve.o: src/HighFrequencyAudioCurve.h src/AudioCurve.h
src/HighFrequencyAudioCurve.o: src/Window.h
src/main.o: rubberband/RubberBandStretcher.h rubberband/TimeStretcher.h
src/PercussiveAudioCurve.o: src/PercussiveAudioCurve.h src/AudioCurve.h
src/Resampler.o: src/Resampler.h
src/RubberBandStretcher.o: src/StretcherImpl.h
src/RubberBandStretcher.o: rubberband/RubberBandStretcher.h
src/RubberBandStretcher.o: rubberband/TimeStretcher.h src/Window.h
src/RubberBandStretcher.o: src/Thread.h src/RingBuffer.h src/Scavenger.h
src/RubberBandStretcher.o: src/FFT.h src/sysutils.h
src/StretchCalculator.o: src/StretchCalculator.h
src/StretcherChannelData.o: src/StretcherChannelData.h src/StretcherImpl.h
src/StretcherChannelData.o: rubberband/RubberBandStretcher.h
src/StretcherChannelData.o: rubberband/TimeStretcher.h src/Window.h
src/StretcherChannelData.o: src/Thread.h src/RingBuffer.h src/Scavenger.h
src/StretcherChannelData.o: src/FFT.h src/sysutils.h src/Resampler.h
src/StretcherImpl.o: src/StretcherImpl.h rubberband/RubberBandStretcher.h
src/StretcherImpl.o: rubberband/TimeStretcher.h src/Window.h src/Thread.h
src/StretcherImpl.o: src/RingBuffer.h src/Scavenger.h src/FFT.h
src/StretcherImpl.o: src/sysutils.h src/PercussiveAudioCurve.h
src/StretcherImpl.o: src/AudioCurve.h src/HighFrequencyAudioCurve.h
src/StretcherImpl.o: src/ConstantAudioCurve.h src/StretchCalculator.h
src/StretcherImpl.o: src/StretcherChannelData.h src/Resampler.h
src/StretcherProcess.o: src/StretcherImpl.h rubberband/RubberBandStretcher.h
src/StretcherProcess.o: rubberband/TimeStretcher.h src/Window.h src/Thread.h
src/StretcherProcess.o: src/RingBuffer.h src/Scavenger.h src/FFT.h
src/StretcherProcess.o: src/sysutils.h src/PercussiveAudioCurve.h
src/StretcherProcess.o: src/AudioCurve.h src/HighFrequencyAudioCurve.h
src/StretcherProcess.o: src/ConstantAudioCurve.h src/StretchCalculator.h
src/StretcherProcess.o: src/StretcherChannelData.h src/Resampler.h
src/sysutils.o: src/sysutils.h
src/Thread.o: src/Thread.h

View file

@ -1,10 +0,0 @@
prefix=%PREFIX%
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include
Name: rubberband
Version: 1.0
Description:
Libs: -L${libdir} -lrubberband
Cflags: -I${includedir}

View file

@ -1,410 +0,0 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band
An audio time-stretching and pitch-shifting library.
Copyright 2007 Chris Cannam.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#ifndef _RUBBERBANDSTRETCHER_H_
#define _RUBBERBANDSTRETCHER_H_
#include "TimeStretcher.h"
#include <vector>
namespace RubberBand
{
class RubberBandStretcher : public TimeStretcher
{
public:
/**
* Processing options for the timestretcher. The preferred
* options should normally be set in the constructor, as a bitwise
* OR of the option flags. The default value (DefaultOptions) is
* intended to give good results in most situations.
*
* 1. Flags prefixed \c OptionProcess determine how the timestretcher
* will be invoked. These options may not be changed after
* construction.
*
* \li \c OptionProcessOffline - Run the stretcher in offline
* mode. In this mode the input data needs to be provided
* twice, once to study(), which calculates a stretch profile
* for the audio, and once to process(), which stretches it.
*
* \li \c OptionProcessRealTime - Run the stretcher in real-time
* mode. In this mode only process() should be called, and the
* stretcher adjusts dynamically in response to the input audio.
*
* The Process setting is likely to depend on your architecture:
* non-real-time operation on seekable files: Offline; real-time
* or streaming operation: RealTime.
*
* 2. Flags prefixed \c OptionStretch control the profile used for
* variable timestretching. Rubber Band always adjusts the
* stretch profile to minimise stretching of busy broadband
* transient sounds, but the degree to which it does so is
* adjustable. These options may not be changed after
* construction.
*
* \li \c OptionStretchElastic - Only meaningful in offline
* mode, and the default in that mode. The audio will be
* stretched at a variable rate, aimed at preserving the quality
* of transient sounds as much as possible. The timings of low
* activity regions between transients may be less exact than
* when the precise flag is set.
*
* \li \c OptionStretchPrecise - Although still using a variable
* stretch rate, the audio will be stretched so as to maintain
* as close as possible to a linear stretch ratio throughout.
* Timing may be better than when using \c OptionStretchElastic, at
* slight cost to the sound quality of transients. This setting
* is always used when running in real-time mode.
*
* 3. Flags prefixed \c OptionTransients control the component
* frequency phase-reset mechanism that may be used at transient
* points to provide clarity and realism to percussion and other
* significant transient sounds. These options may be changed
* after construction when running in real-time mode, but not when
* running in offline mode.
*
* \li \c OptionTransientsCrisp - Reset component phases at the
* peak of each transient (the start of a significant note or
* percussive event). This, the default setting, usually
* results in a clear-sounding output; but it is not always
* consistent, and may cause interruptions in stable sounds
* present at the same time as transient events.
*
* \li \c OptionTransientsMixed - Reset component phases at the
* peak of each transient, outside a frequency range typical of
* musical fundamental frequencies. The results may be more
* regular for mixed stable and percussive notes than
* \c OptionTransientsCrisp, but with a "phasier" sound. The
* balance may sound very good for certain types of music and
* fairly bad for others.
*
* \li \c OptionTransientsSmooth - Do not reset component phases
* at any point. The results will be smoother and more regular
* but may be less clear than with either of the other
* transients flags.
*
* 4. Flags prefixed \c OptionPhase control the adjustment of
* component frequency phases from one analysis window to the next
* during non-transient segments. These options may be changed at
* any time.
*
* \li \c OptionPhaseAdaptive - Lock the adjustments of phase
* for frequencies close to peak frequencies to those of the
* peak, but reduce the degree of locking as the stretch ratio
* gets longer. This, the default setting, should give a good
* balance between clarity and smoothness in most situations.
*
* \li \c OptionPhasePeakLocked - Lock the adjustments of phase
* for frequencies close to peak frequencies to those of the
* peak. This should give a clear result in situations with
* relatively low stretch ratios, but a relatively metallic
* sound at longer stretches.
*
* \li \c OptionPhaseIndependent - Do not lock phase adjustments
* to peak frequencies. This usually results in a softer,
* phasier sound.
*
* 5. Flags prefixed \c OptionThreading control the threading
* model of the stretcher. These options may not be changed after
* construction.
*
* \li \c OptionThreadingAuto - Permit the stretcher to
* determine its own threading model. Usually this means using
* one processing thread per audio channel in offline mode if
* the stretcher is able to determine that more than one CPU is
* available, and one thread only in realtime mode.
*
* \li \c OptionThreadingNever - Never use more than one thread.
*
* \li \c OptionThreadingAlways - Use multiple threads in any
* situation where \c OptionThreadingAuto would do so, except omit
* the check for multiple CPUs and instead assume it to be true.
*
* 6. Flags prefixed \c OptionWindow control the window size for
* FFT processing. The window size actually used will depend on
* many factors, but it can be influenced. These options may not
* be changed after construction.
*
* \li \c OptionWindowStandard - Use the default window size.
* The actual size will vary depending on other parameters.
* This option is expected to produce better results than the
* other window options in most situations.
*
* \li \c OptionWindowShort - Use a shorter window. This may
* result in crisper sound for audio that depends strongly on
* its timing qualities.
*
* \li \c OptionWindowLong - Use a longer window. This is
* likely to result in a smoother sound at the expense of
* clarity and timing.
*/
typedef int Options;
static const int OptionProcessOffline = 0x00000000;
static const int OptionProcessRealTime = 0x00000001;
static const int OptionStretchElastic = 0x00000000;
static const int OptionStretchPrecise = 0x00000010;
static const int OptionTransientsCrisp = 0x00000000;
static const int OptionTransientsMixed = 0x00000100;
static const int OptionTransientsSmooth = 0x00000200;
static const int OptionPhaseAdaptive = 0x00000000;
static const int OptionPhasePeakLocked = 0x00001000;
static const int OptionPhaseIndependent = 0x00002000;
static const int OptionThreadingAuto = 0x00000000;
static const int OptionThreadingNever = 0x00010000;
static const int OptionThreadingAlways = 0x00020000;
static const int OptionWindowStandard = 0x00000000;
static const int OptionWindowShort = 0x00100000;
static const int OptionWindowLong = 0x00200000;
static const int DefaultOptions = 0x00000000;
static const int PercussiveOptions = OptionWindowShort | \
OptionPhaseIndependent;
/**
* Construct a time and pitch stretcher object to run at the given
* sample rate, with the given number of channels. Processing
* options and the time and pitch scaling ratios may be provided.
* The time and pitch ratios may be changed after construction,
* but most of the options may not. See the option documentation
* above for more details.
*/
RubberBandStretcher(size_t sampleRate,
size_t channels,
Options options = DefaultOptions,
double initialTimeRatio = 1.0,
double initialPitchScale = 1.0);
virtual ~RubberBandStretcher();
/**
* Reset the stretcher's internal buffers. The stretcher should
* subsequently behave as if it had just been constructed
* (although retaining the current time and pitch ratio).
*/
virtual void reset();
/**
* Set the time ratio for the stretcher. This is the ratio of
* stretched to unstretched duration -- not tempo. For example, a
* ratio of 2.0 would make the audio twice as long (i.e. halve the
* tempo); 0.5 would make it half as long (i.e. double the tempo);
* 1.0 would leave the duration unaffected.
*
* If the stretcher was constructed in Offline mode, the time
* ratio is fixed throughout operation; this function may be
* called any number of times between construction (or a call to
* reset()) and the first call to study() or process(), but may
* not be called after study() or process() has been called.
*
* If the stretcher was constructed in RealTime mode, the time
* ratio may be varied during operation; this function may be
* called at any time, so long as it is not called concurrently
* with process(). You should either call this function from the
* same thread as process(), or provide your own mutex or similar
* mechanism to ensure that setTimeRatio and process() cannot be
* run at once (there is no internal mutex for this purpose).
*/
virtual void setTimeRatio(double ratio);
/**
* Set the pitch scaling ratio for the stretcher. This is the
* ratio of target frequency to source frequency. For example, a
* ratio of 2.0 would shift up by one octave; 0.5 down by one
* octave; or 1.0 leave the pitch unaffected.
*
* To put this in musical terms, a pitch scaling ratio
* corresponding to a shift of S equal-tempered semitones (where S
* is positive for an upwards shift and negative for downwards) is
* pow(2.0, S / 12.0).
*
* If the stretcher was constructed in Offline mode, the pitch
* scaling ratio is fixed throughout operation; this function may
* be called any number of times between construction (or a call
* to reset()) and the first call to study() or process(), but may
* not be called after study() or process() has been called.
*
* If the stretcher was constructed in RealTime mode, the pitch
* scaling ratio may be varied during operation; this function may
* be called at any time, so long as it is not called concurrently
* with process(). You should either call this function from the
* same thread as process(), or provide your own mutex or similar
* mechanism to ensure that setPitchScale and process() cannot be
* run at once (there is no internal mutex for this purpose).
*/
virtual void setPitchScale(double scale);
/**
* Return the last time ratio value that was set (either on
* construction or with setTimeRatio()).
*/
virtual double getTimeRatio() const;
/**
* Return the last pitch scaling ratio value that was set (either
* on construction or with setPitchScale()).
*/
virtual double getPitchScale() const;
/**
* Return the processing latency of the stretcher. This is the
* number of audio samples that one would have to discard at the
* start of the output in order to ensure that the resulting audio
* aligned with the input audio at the start. In Offline mode,
* latency is automatically adjusted for and the result is zero.
* In RealTime mode, the latency may depend on the time and pitch
* ratio and other options.
*/
virtual size_t getLatency() const;
/**
* Change an OptionTransients configuration setting. This may be
* called at any time in RealTime mode. It may not be called in
* Offline mode (for which the transients option is fixed on
* construction).
*/
virtual void setTransientsOption(Options options);
/**
* Change an OptionPhase configuration setting. This may be
* called at any time in any mode.
*
* Note that if running multi-threaded in Offline mode, the change
* may not take effect immediately if processing is already under
* way when this function is called.
*/
virtual void setPhaseOption(Options options);
/**
* Tell the stretcher exactly how many input samples it will
* receive. This is only useful in Offline mode, when it allows
* the stretcher to ensure that the number of output samples is
* exactly correct. In RealTime mode no such guarantee is
* possible and this value is ignored.
*/
virtual void setExpectedInputDuration(size_t samples);
/**
* Ask the stretcher how many audio sample frames should be
* provided as input in order to ensure that some more output
* becomes available. Normal usage consists of querying this
* function, providing that number of samples to process(),
* reading the output using available() and retrieve(), and then
* repeating.
*
* Note that this value is only relevant to process(), not to
* study() (to which you may pass any number of samples at a time,
* and from which there is no output).
*/
virtual size_t getSamplesRequired() const;
/**
* Tell the stretcher the maximum number of sample frames that you
* will ever be passing in to a single process() call. If you
* don't call this function, the stretcher will assume that you
* never pass in more samples than getSamplesRequired() suggested
* you should. You should not pass in more samples than that
* unless you have called setMaxProcessSize first.
*
* This function may not be called after the first call to study()
* or process().
*
* Note that this value is only relevant to process(), not to
* study() (to which you may pass any number of samples at a time,
* and from which there is no output).
*/
virtual void setMaxProcessSize(size_t samples);
/**
* Provide a block of "samples" sample frames for the stretcher to
* study and calculate a stretch profile from.
*
* This is only meaningful in Offline mode, and is required if
* running in that mode. You should pass the entire input through
* study() before any process() calls are made, as a sequence of
* blocks in individual study() calls, or as a single large block.
*
* "input" should point to de-interleaved audio data with one
* float array per channel. "samples" supplies the number of
* audio sample frames available in "input". If "samples" is
* zero, "input" may be NULL.
*
* Set "final" to true if this is the last block of data that will
* be provided to study() before the first process() call.
*/
virtual void study(const float *const *input, size_t samples, bool final);
/**
* Provide a block of "samples" sample frames for processing.
* See also getSamplesRequired() and setMaxProcessSize().
*
* Set "final" to true if this is the last block of input data.
*/
virtual void process(const float *const *input, size_t samples, bool final);
/**
* Ask the stretcher how many audio sample frames of output data
* are available for reading (via retrieve()).
*
* This function returns 0 if no frames are available: this
* usually means more input data needs to be provided, but if the
* stretcher is running in threaded mode it may just mean that not
* enough data has yet been processed. Call getSamplesRequired()
* to discover whether more input is needed.
*
* This function returns -1 if all data has been fully processed
* and all output read, and the stretch process is now finished.
*/
virtual int available() const;
/**
* Obtain some processed output data from the stretcher. Up to
* "samples" samples will be stored in the output arrays (one per
* channel for de-interleaved audio data) pointed to by "output".
* The return value is the actual number of sample frames
* retrieved.
*/
virtual size_t retrieve(float *const *output, size_t samples) const;
virtual float getFrequencyCutoff(int n) const;
virtual void setFrequencyCutoff(int n, float f);
virtual size_t getInputIncrement() const;
virtual std::vector<int> getOutputIncrements() const; //!!! document particular meaning in RT mode
virtual std::vector<float> getPhaseResetCurve() const; //!!! document particular meaning in RT mode
virtual std::vector<int> getExactTimePoints() const; //!!! meaningless in RT mode
virtual size_t getChannelCount() const;
virtual void calculateStretch();
virtual void setDebugLevel(int level);
static void setDefaultDebugLevel(int level);
protected:
class Impl;
Impl *m_d;
};
}
#endif

View file

@ -1,58 +0,0 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band
An audio time-stretching and pitch-shifting library.
Copyright 2007 Chris Cannam.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#ifndef _RUBBERBAND_TIMESTRETCHER_H_
#define _RUBBERBAND_TIMESTRETCHER_H_
#include <sys/types.h>
namespace RubberBand
{
/**
* Base class for time stretchers. RubberBand currently provides only
* a single subclass implementation.
*
* @see RubberBandStretcher
*/
class TimeStretcher
{
public:
TimeStretcher(size_t sampleRate, size_t channels) :
m_sampleRate(sampleRate),
m_channels(channels)
{ }
virtual ~TimeStretcher()
{ }
virtual void reset() = 0;
virtual void setTimeRatio(double ratio) = 0;
virtual void setPitchScale(double scale) = 0;
virtual size_t getLatency() const = 0;
virtual void study(const float *const *input, size_t samples, bool final) = 0;
virtual size_t getSamplesRequired() const = 0;
virtual void process(const float *const *input, size_t samples, bool final) = 0;
virtual int available() const = 0;
virtual size_t retrieve(float *const *output, size_t samples) const = 0;
protected:
size_t m_sampleRate;
size_t m_channels;
};
}
#endif

View file

@ -1,31 +0,0 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band
An audio time-stretching and pitch-shifting library.
Copyright 2007 Chris Cannam.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#include "AudioCurve.h"
namespace RubberBand
{
AudioCurve::AudioCurve(size_t sampleRate, size_t windowSize) :
m_sampleRate(sampleRate),
m_windowSize(windowSize)
{
}
AudioCurve::~AudioCurve()
{
}
}

View file

@ -1,42 +0,0 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band
An audio time-stretching and pitch-shifting library.
Copyright 2007 Chris Cannam.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#ifndef _AUDIO_CURVE_H_
#define _AUDIO_CURVE_H_
#include <sys/types.h>
namespace RubberBand
{
class AudioCurve
{
public:
AudioCurve(size_t sampleRate, size_t windowSize);
virtual ~AudioCurve();
virtual void setWindowSize(size_t newSize) = 0;
virtual float process(float *mag, size_t increment) = 0;
virtual void reset() = 0;
protected:
size_t m_sampleRate;
size_t m_windowSize;
};
}
#endif

View file

@ -1,47 +0,0 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band
An audio time-stretching and pitch-shifting library.
Copyright 2007 Chris Cannam.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#include "ConstantAudioCurve.h"
namespace RubberBand
{
ConstantAudioCurve::ConstantAudioCurve(size_t sampleRate, size_t windowSize) :
AudioCurve(sampleRate, windowSize)
{
}
ConstantAudioCurve::~ConstantAudioCurve()
{
}
void
ConstantAudioCurve::reset()
{
}
void
ConstantAudioCurve::setWindowSize(size_t newSize)
{
m_windowSize = newSize;
}
float
ConstantAudioCurve::process(float *, size_t)
{
return 1.f;
}
}

View file

@ -1,37 +0,0 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band
An audio time-stretching and pitch-shifting library.
Copyright 2007 Chris Cannam.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#ifndef _CONSTANT_AUDIO_CURVE_H_
#define _CONSTANT_AUDIO_CURVE_H_
#include "AudioCurve.h"
namespace RubberBand
{
class ConstantAudioCurve : public AudioCurve
{
public:
ConstantAudioCurve(size_t sampleRate, size_t windowSize);
virtual ~ConstantAudioCurve();
virtual void setWindowSize(size_t newSize);
virtual float process(float *mag, size_t increment);
virtual void reset();
};
}
#endif

View file

@ -1,867 +0,0 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band
An audio time-stretching and pitch-shifting library.
Copyright 2007 Chris Cannam.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#include "FFT.h"
#include "Thread.h"
#include <fftw3.h>
#include <cmath>
#include <iostream>
#include <map>
#include <cstdio>
#include <vector>
namespace RubberBand {
class FFTImpl
{
public:
virtual ~FFTImpl() { }
virtual void initFloat() = 0;
virtual void initDouble() = 0;
virtual void forward(double *realIn, double *realOut, double *imagOut) = 0;
virtual void forwardPolar(double *realIn, double *magOut, double *phaseOut) = 0;
virtual void forwardMagnitude(double *realIn, double *magOut) = 0;
virtual void forward(float *realIn, float *realOut, float *imagOut) = 0;
virtual void forwardPolar(float *realIn, float *magOut, float *phaseOut) = 0;
virtual void forwardMagnitude(float *realIn, float *magOut) = 0;
virtual void inverse(double *realIn, double *imagIn, double *realOut) = 0;
virtual void inversePolar(double *magIn, double *phaseIn, double *realOut) = 0;
virtual void inverse(float *realIn, float *imagIn, float *realOut) = 0;
virtual void inversePolar(float *magIn, float *phaseIn, float *realOut) = 0;
virtual float *getFloatTimeBuffer() = 0;
virtual double *getDoubleTimeBuffer() = 0;
};
// Define FFTW_DOUBLE_ONLY to make all uses of FFTW functions be
// double-precision (so "float" FFTs are calculated by casting to
// doubles and using the double-precision FFTW function).
//
// Define FFTW_FLOAT_ONLY to make all uses of FFTW functions be
// single-precision (so "double" FFTs are calculated by casting to
// floats and using the single-precision FFTW function).
//
// Neither of these flags is terribly desirable -- FFTW_FLOAT_ONLY
// obviously loses you precision, and neither is handled in the most
// efficient way so any performance improvement will be small at best.
// The only real reason to define either flag would be to avoid
// linking against both fftw3 and fftw3f libraries.
//#define FFTW_DOUBLE_ONLY 1
//#define FFTW_FLOAT_ONLY 1
#ifdef FFTW_DOUBLE_ONLY
#ifdef FFTW_FLOAT_ONLY
// Can't meaningfully define both
#undef FFTW_DOUBLE_ONLY
#undef FFTW_FLOAT_ONLY
#else /* !FFTW_FLOAT_ONLY */
#define fftwf_complex fftw_complex
#define fftwf_plan fftw_plan
#define fftwf_plan_dft_r2c_1d fftw_plan_dft_r2c_1d
#define fftwf_plan_dft_c2r_1d fftw_plan_dft_c2r_1d
#define fftwf_destroy_plan fftw_destroy_plan
#define fftwf_malloc fftw_malloc
#define fftwf_free fftw_free
#define fftwf_execute fftw_execute
#define atan2f atan2
#define sqrtf sqrt
#define cosf cos
#define sinf sin
#endif /* !FFTW_FLOAT_ONLY */
#ifdef FFTW_FLOAT_ONLY
#define fftw_complex fftwf_complex
#define fftw_plan fftwf_plan
#define fftw_plan_dft_r2c_1d fftwf_plan_dft_r2c_1d
#define fftw_plan_dft_c2r_1d fftwf_plan_dft_c2r_1d
#define fftw_destroy_plan fftwf_destroy_plan
#define fftw_malloc fftwf_malloc
#define fftw_free fftwf_free
#define fftw_execute fftwf_execute
#define atan2 atan2f
#define sqrt sqrtf
#define cos cosf
#define sif sinf
#endif /* FFTW_FLOAT_ONLY */
class D_FFTW : public FFTImpl
{
public:
D_FFTW(unsigned int size) : m_fplanf(0)
#ifdef FFTW_DOUBLE_ONLY
, m_frb(0)
#endif
, m_dplanf(0)
#ifdef FFTW_FLOAT_ONLY
, m_drb(0)
#endif
, m_size(size)
{
}
~D_FFTW() {
if (m_fplanf) {
bool save = false;
m_extantMutex.lock();
if (m_extantf > 0 && --m_extantf == 0) save = true;
m_extantMutex.unlock();
if (save) saveWisdom('f');
fftwf_destroy_plan(m_fplanf);
fftwf_destroy_plan(m_fplani);
fftwf_free(m_fbuf);
fftwf_free(m_fpacked);
#ifdef FFTW_DOUBLE_ONLY
if (m_frb) fftw_free(m_frb);
#endif
}
if (m_dplanf) {
bool save = false;
m_extantMutex.lock();
if (m_extantd > 0 && --m_extantd == 0) save = true;
m_extantMutex.unlock();
if (save) saveWisdom('d');
fftw_destroy_plan(m_dplanf);
fftw_destroy_plan(m_dplani);
fftw_free(m_dbuf);
fftw_free(m_dpacked);
#ifdef FFTW_FLOAT_ONLY
if (m_drb) fftwf_free(m_drb);
#endif
}
}
void initFloat() {
if (m_fplanf) return;
bool load = false;
m_extantMutex.lock();
if (m_extantf++ == 0) load = true;
m_extantMutex.unlock();
#ifdef FFTW_DOUBLE_ONLY
if (load) loadWisdom('d');
m_fbuf = (double *)fftw_malloc(m_size * sizeof(double));
#else
if (load) loadWisdom('f');
m_fbuf = (float *)fftwf_malloc(m_size * sizeof(float));
#endif
m_fpacked = (fftwf_complex *)fftw_malloc
((m_size/2 + 1) * sizeof(fftwf_complex));
m_fplanf = fftwf_plan_dft_r2c_1d
(m_size, m_fbuf, m_fpacked, FFTW_MEASURE);
m_fplani = fftwf_plan_dft_c2r_1d
(m_size, m_fpacked, m_fbuf, FFTW_MEASURE);
}
void initDouble() {
if (m_dplanf) return;
bool load = false;
m_extantMutex.lock();
if (m_extantd++ == 0) load = true;
m_extantMutex.unlock();
#ifdef FFTW_FLOAT_ONLY
if (load) loadWisdom('f');
m_dbuf = (float *)fftwf_malloc(m_size * sizeof(float));
#else
if (load) loadWisdom('d');
m_dbuf = (double *)fftw_malloc(m_size * sizeof(double));
#endif
m_dpacked = (fftw_complex *)fftw_malloc
((m_size/2 + 1) * sizeof(fftw_complex));
m_dplanf = fftw_plan_dft_r2c_1d
(m_size, m_dbuf, m_dpacked, FFTW_MEASURE);
m_dplani = fftw_plan_dft_c2r_1d
(m_size, m_dpacked, m_dbuf, FFTW_MEASURE);
}
void loadWisdom(char type) { wisdom(false, type); }
void saveWisdom(char type) { wisdom(true, type); }
void wisdom(bool save, char type) {
#ifdef FFTW_DOUBLE_ONLY
if (type == 'f') return;
#endif
#ifdef FFTW_FLOAT_ONLY
if (type == 'd') return;
#endif
const char *home = getenv("HOME");
if (!home) return;
char fn[256];
snprintf(fn, 256, "%s/%s.%c", home, ".rubberband.wisdom", type);
FILE *f = fopen(fn, save ? "wb" : "rb");
if (!f) return;
if (save) {
switch (type) {
#ifdef FFTW_DOUBLE_ONLY
case 'f': break;
#else
case 'f': fftwf_export_wisdom_to_file(f); break;
#endif
#ifdef FFTW_FLOAT_ONLY
case 'd': break;
#else
case 'd': fftw_export_wisdom_to_file(f); break;
#endif
default: break;
}
} else {
switch (type) {
#ifdef FFTW_DOUBLE_ONLY
case 'f': break;
#else
case 'f': fftwf_import_wisdom_from_file(f); break;
#endif
#ifdef FFTW_FLOAT_ONLY
case 'd': break;
#else
case 'd': fftw_import_wisdom_from_file(f); break;
#endif
default: break;
}
}
fclose(f);
}
void packFloat(float *re, float *im) {
for (unsigned int i = 0; i <= m_size/2; ++i) {
m_fpacked[i][0] = re[i];
m_fpacked[i][1] = im[i];
}
}
void packDouble(double *re, double *im) {
for (unsigned int i = 0; i <= m_size/2; ++i) {
m_dpacked[i][0] = re[i];
m_dpacked[i][1] = im[i];
}
}
void unpackFloat(float *re, float *im) {
for (unsigned int i = 0; i <= m_size/2; ++i) {
re[i] = m_fpacked[i][0];
im[i] = m_fpacked[i][1];
}
}
void unpackDouble(double *re, double *im) {
for (unsigned int i = 0; i <= m_size/2; ++i) {
re[i] = m_dpacked[i][0];
im[i] = m_dpacked[i][1];
}
}
void forward(double *realIn, double *realOut, double *imagOut) {
if (!m_dplanf) initDouble();
#ifndef FFTW_FLOAT_ONLY
if (realIn != m_dbuf)
#endif
for (unsigned int i = 0; i < m_size; ++i) {
m_dbuf[i] = realIn[i];
}
fftw_execute(m_dplanf);
unpackDouble(realOut, imagOut);
}
void forwardPolar(double *realIn, double *magOut, double *phaseOut) {
if (!m_dplanf) initDouble();
#ifndef FFTW_FLOAT_ONLY
if (realIn != m_dbuf)
#endif
for (unsigned int i = 0; i < m_size; ++i) {
m_dbuf[i] = realIn[i];
}
fftw_execute(m_dplanf);
for (unsigned int i = 0; i <= m_size/2; ++i) {
magOut[i] = sqrt(m_dpacked[i][0] * m_dpacked[i][0] +
m_dpacked[i][1] * m_dpacked[i][1]);
}
for (unsigned int i = 0; i <= m_size/2; ++i) {
phaseOut[i] = atan2(m_dpacked[i][1], m_dpacked[i][0]);
}
}
void forwardMagnitude(double *realIn, double *magOut) {
if (!m_dplanf) initDouble();
#ifndef FFTW_FLOAT_ONLY
if (realIn != m_dbuf)
#endif
for (unsigned int i = 0; i < m_size; ++i) {
m_dbuf[i] = realIn[i];
}
fftw_execute(m_dplanf);
for (unsigned int i = 0; i <= m_size/2; ++i) {
magOut[i] = sqrt(m_dpacked[i][0] * m_dpacked[i][0] +
m_dpacked[i][1] * m_dpacked[i][1]);
}
}
void forward(float *realIn, float *realOut, float *imagOut) {
if (!m_fplanf) initFloat();
#ifndef FFTW_DOUBLE_ONLY
if (realIn != m_fbuf)
#endif
for (unsigned int i = 0; i < m_size; ++i) {
m_fbuf[i] = realIn[i];
}
fftwf_execute(m_fplanf);
unpackFloat(realOut, imagOut);
}
void forwardPolar(float *realIn, float *magOut, float *phaseOut) {
if (!m_fplanf) initFloat();
#ifndef FFTW_DOUBLE_ONLY
if (realIn != m_fbuf)
#endif
for (unsigned int i = 0; i < m_size; ++i) {
m_fbuf[i] = realIn[i];
}
fftwf_execute(m_fplanf);
for (unsigned int i = 0; i <= m_size/2; ++i) {
magOut[i] = sqrtf(m_fpacked[i][0] * m_fpacked[i][0] +
m_fpacked[i][1] * m_fpacked[i][1]);
}
for (unsigned int i = 0; i <= m_size/2; ++i) {
phaseOut[i] = atan2f(m_fpacked[i][1], m_fpacked[i][0]) ;
}
}
void forwardMagnitude(float *realIn, float *magOut) {
if (!m_fplanf) initFloat();
#ifndef FFTW_DOUBLE_ONLY
if (realIn != m_fbuf)
#endif
for (unsigned int i = 0; i < m_size; ++i) {
m_fbuf[i] = realIn[i];
}
fftwf_execute(m_fplanf);
for (unsigned int i = 0; i <= m_size/2; ++i) {
magOut[i] = sqrtf(m_fpacked[i][0] * m_fpacked[i][0] +
m_fpacked[i][1] * m_fpacked[i][1]);
}
}
void inverse(double *realIn, double *imagIn, double *realOut) {
if (!m_dplanf) initDouble();
packDouble(realIn, imagIn);
fftw_execute(m_dplani);
#ifndef FFTW_FLOAT_ONLY
if (realOut != m_dbuf)
#endif
for (unsigned int i = 0; i < m_size; ++i) {
realOut[i] = m_dbuf[i];
}
}
void inversePolar(double *magIn, double *phaseIn, double *realOut) {
if (!m_dplanf) initDouble();
for (unsigned int i = 0; i <= m_size/2; ++i) {
m_dpacked[i][0] = magIn[i] * cos(phaseIn[i]);
m_dpacked[i][1] = magIn[i] * sin(phaseIn[i]);
}
fftw_execute(m_dplani);
#ifndef FFTW_FLOAT_ONLY
if (realOut != m_dbuf)
#endif
for (unsigned int i = 0; i < m_size; ++i) {
realOut[i] = m_dbuf[i];
}
}
void inverse(float *realIn, float *imagIn, float *realOut) {
if (!m_fplanf) initFloat();
packFloat(realIn, imagIn);
fftwf_execute(m_fplani);
#ifndef FFTW_DOUBLE_ONLY
if (realOut != m_fbuf)
#endif
for (unsigned int i = 0; i < m_size; ++i) {
realOut[i] = m_fbuf[i];
}
}
void inversePolar(float *magIn, float *phaseIn, float *realOut) {
if (!m_fplanf) initFloat();
for (unsigned int i = 0; i <= m_size/2; ++i) {
m_fpacked[i][0] = magIn[i] * cosf(phaseIn[i]);
m_fpacked[i][1] = magIn[i] * sinf(phaseIn[i]);
}
fftwf_execute(m_fplani);
#ifndef FFTW_DOUBLE_ONLY
if (realOut != m_fbuf)
#endif
for (unsigned int i = 0; i < m_size; ++i) {
realOut[i] = m_fbuf[i];
}
}
float *getFloatTimeBuffer() {
initFloat();
#ifdef FFTW_DOUBLE_ONLY
if (!m_frb) m_frb = (float *)fftw_malloc(m_size * sizeof(float));
return m_frb;
#else
return m_fbuf;
#endif
}
double *getDoubleTimeBuffer() {
initDouble();
#ifdef FFTW_FLOAT_ONLY
if (!m_drb) m_drb = (double *)fftwf_malloc(m_size * sizeof(double));
return m_drb;
#else
return m_dbuf;
#endif
}
private:
fftwf_plan m_fplanf;
fftwf_plan m_fplani;
#ifdef FFTW_DOUBLE_ONLY
float *m_frb;
double *m_fbuf;
#else
float *m_fbuf;
#endif
fftwf_complex *m_fpacked;
fftw_plan m_dplanf;
fftw_plan m_dplani;
#ifdef FFTW_FLOAT_ONLY
float *m_dbuf;
double *m_drb;
#else
double *m_dbuf;
#endif
fftw_complex *m_dpacked;
unsigned int m_size;
static unsigned int m_extantf;
static unsigned int m_extantd;
static Mutex m_extantMutex;
};
unsigned int
D_FFTW::m_extantf = 0;
unsigned int
D_FFTW::m_extantd = 0;
Mutex
D_FFTW::m_extantMutex;
#endif
class D_Cross : public FFTImpl
{
public:
D_Cross(unsigned int size) : m_size(size), m_table(0), m_frb(0), m_drb(0) {
m_a = new double[size];
m_b = new double[size];
m_c = new double[size];
m_d = new double[size];
m_table = new int[m_size];
unsigned int bits;
unsigned int i, j, k, m;
for (i = 0; ; ++i) {
if (m_size & (1 << i)) {
bits = i;
break;
}
}
for (i = 0; i < m_size; ++i) {
m = i;
for (j = k = 0; j < bits; ++j) {
k = (k << 1) | (m & 1);
m >>= 1;
}
m_table[i] = k;
}
}
~D_Cross() {
delete[] m_table;
delete[] m_a;
delete[] m_b;
delete[] m_c;
delete[] m_d;
delete[] m_frb;
delete[] m_drb;
}
void initFloat() { }
void initDouble() { }
void forward(double *realIn, double *realOut, double *imagOut) {
basefft(false, realIn, 0, m_c, m_d);
for (size_t i = 0; i <= m_size/2; ++i) realOut[i] = m_c[i];
for (size_t i = 0; i <= m_size/2; ++i) imagOut[i] = m_d[i];
}
void forwardPolar(double *realIn, double *magOut, double *phaseOut) {
basefft(false, realIn, 0, m_c, m_d);
for (unsigned int i = 0; i <= m_size/2; ++i) {
magOut[i] = sqrt(m_c[i] * m_c[i] + m_d[i] * m_d[i]);
phaseOut[i] = atan2(m_d[i], m_c[i]) ;
}
}
void forwardMagnitude(double *realIn, double *magOut) {
basefft(false, realIn, 0, m_c, m_d);
for (unsigned int i = 0; i <= m_size/2; ++i) {
magOut[i] = sqrt(m_c[i] * m_c[i] + m_d[i] * m_d[i]);
}
}
void forward(float *realIn, float *realOut, float *imagOut) {
for (size_t i = 0; i < m_size; ++i) m_a[i] = realIn[i];
basefft(false, m_a, 0, m_c, m_d);
for (size_t i = 0; i <= m_size/2; ++i) realOut[i] = m_c[i];
for (size_t i = 0; i <= m_size/2; ++i) imagOut[i] = m_d[i];
}
void forwardPolar(float *realIn, float *magOut, float *phaseOut) {
for (size_t i = 0; i < m_size; ++i) m_a[i] = realIn[i];
basefft(false, m_a, 0, m_c, m_d);
for (unsigned int i = 0; i <= m_size/2; ++i) {
magOut[i] = sqrt(m_c[i] * m_c[i] + m_d[i] * m_d[i]);
phaseOut[i] = atan2(m_d[i], m_c[i]) ;
}
}
void forwardMagnitude(float *realIn, float *magOut) {
for (size_t i = 0; i < m_size; ++i) m_a[i] = realIn[i];
basefft(false, m_a, 0, m_c, m_d);
for (unsigned int i = 0; i <= m_size/2; ++i) {
magOut[i] = sqrt(m_c[i] * m_c[i] + m_d[i] * m_d[i]);
}
}
void inverse(double *realIn, double *imagIn, double *realOut) {
for (unsigned int i = 0; i <= m_size/2; ++i) {
double real = realIn[i];
double imag = imagIn[i];
m_a[i] = real;
m_b[i] = imag;
if (i > 0) {
m_a[m_size-i] = real;
m_b[m_size-i] = -imag;
}
}
basefft(true, m_a, m_b, realOut, m_d);
}
void inversePolar(double *magIn, double *phaseIn, double *realOut) {
for (unsigned int i = 0; i <= m_size/2; ++i) {
double real = magIn[i] * cos(phaseIn[i]);
double imag = magIn[i] * sin(phaseIn[i]);
m_a[i] = real;
m_b[i] = imag;
if (i > 0) {
m_a[m_size-i] = real;
m_b[m_size-i] = -imag;
}
}
basefft(true, m_a, m_b, realOut, m_d);
}
void inverse(float *realIn, float *imagIn, float *realOut) {
for (unsigned int i = 0; i <= m_size/2; ++i) {
float real = realIn[i];
float imag = imagIn[i];
m_a[i] = real;
m_b[i] = imag;
if (i > 0) {
m_a[m_size-i] = real;
m_b[m_size-i] = -imag;
}
}
basefft(true, m_a, m_b, m_c, m_d);
for (unsigned int i = 0; i < m_size; ++i) realOut[i] = m_c[i];
}
void inversePolar(float *magIn, float *phaseIn, float *realOut) {
for (unsigned int i = 0; i <= m_size/2; ++i) {
float real = magIn[i] * cosf(phaseIn[i]);
float imag = magIn[i] * sinf(phaseIn[i]);
m_a[i] = real;
m_b[i] = imag;
if (i > 0) {
m_a[m_size-i] = real;
m_b[m_size-i] = -imag;
}
}
basefft(true, m_a, m_b, m_c, m_d);
for (unsigned int i = 0; i < m_size; ++i) realOut[i] = m_c[i];
}
float *getFloatTimeBuffer() {
if (!m_frb) m_frb = new float[m_size];
return m_frb;
}
double *getDoubleTimeBuffer() {
if (!m_drb) m_drb = new double[m_size];
return m_drb;
}
private:
unsigned int m_size;
int *m_table;
float *m_frb;
double *m_drb;
double *m_a;
double *m_b;
double *m_c;
double *m_d;
void basefft(bool inverse, double *ri, double *ii, double *ro, double *io);
};
void
D_Cross::basefft(bool inverse, double *ri, double *ii, double *ro, double *io)
{
if (!ri || !ro || !io) return;
unsigned int i, j, k, m;
unsigned int blockSize, blockEnd;
double tr, ti;
double angle = 2.0 * M_PI;
if (inverse) angle = -angle;
const unsigned int n = m_size;
if (ii) {
for (i = 0; i < n; ++i) {
ro[m_table[i]] = ri[i];
io[m_table[i]] = ii[i];
}
} else {
for (i = 0; i < n; ++i) {
ro[m_table[i]] = ri[i];
io[m_table[i]] = 0.0;
}
}
blockEnd = 1;
for (blockSize = 2; blockSize <= n; blockSize <<= 1) {
double delta = angle / (double)blockSize;
double sm2 = -sin(-2 * delta);
double sm1 = -sin(-delta);
double cm2 = cos(-2 * delta);
double cm1 = cos(-delta);
double w = 2 * cm1;
double ar[3], ai[3];
for (i = 0; i < n; i += blockSize) {
ar[2] = cm2;
ar[1] = cm1;
ai[2] = sm2;
ai[1] = sm1;
for (j = i, m = 0; m < blockEnd; j++, m++) {
ar[0] = w * ar[1] - ar[2];
ar[2] = ar[1];
ar[1] = ar[0];
ai[0] = w * ai[1] - ai[2];
ai[2] = ai[1];
ai[1] = ai[0];
k = j + blockEnd;
tr = ar[0] * ro[k] - ai[0] * io[k];
ti = ar[0] * io[k] + ai[0] * ro[k];
ro[k] = ro[j] - tr;
io[k] = io[j] - ti;
ro[j] += tr;
io[j] += ti;
}
}
blockEnd = blockSize;
}
/* fftw doesn't rescale, so nor will we
if (inverse) {
double denom = (double)n;
for (i = 0; i < n; i++) {
ro[i] /= denom;
io[i] /= denom;
}
}
*/
}
int
FFT::m_method = -1;
FFT::FFT(unsigned int size)
{
if (size < 2) throw InvalidSize;
if (size & (size-1)) throw InvalidSize;
if (m_method == -1) {
m_method = 1;
}
switch (m_method) {
case 0:
d = new D_Cross(size);
break;
case 1:
// std::cerr << "FFT::FFT(" << size << "): using FFTW3 implementation"
// << std::endl;
d = new D_FFTW(size);
break;
default:
std::cerr << "FFT::FFT(" << size << "): WARNING: using slow built-in implementation"
<< std::endl;
d = new D_Cross(size);
break;
}
}
FFT::~FFT()
{
delete d;
}
void
FFT::forward(double *realIn, double *realOut, double *imagOut)
{
d->forward(realIn, realOut, imagOut);
}
void
FFT::forwardPolar(double *realIn, double *magOut, double *phaseOut)
{
d->forwardPolar(realIn, magOut, phaseOut);
}
void
FFT::forwardMagnitude(double *realIn, double *magOut)
{
d->forwardMagnitude(realIn, magOut);
}
void
FFT::forward(float *realIn, float *realOut, float *imagOut)
{
d->forward(realIn, realOut, imagOut);
}
void
FFT::forwardPolar(float *realIn, float *magOut, float *phaseOut)
{
d->forwardPolar(realIn, magOut, phaseOut);
}
void
FFT::forwardMagnitude(float *realIn, float *magOut)
{
d->forwardMagnitude(realIn, magOut);
}
void
FFT::inverse(double *realIn, double *imagIn, double *realOut)
{
d->inverse(realIn, imagIn, realOut);
}
void
FFT::inversePolar(double *magIn, double *phaseIn, double *realOut)
{
d->inversePolar(magIn, phaseIn, realOut);
}
void
FFT::inverse(float *realIn, float *imagIn, float *realOut)
{
d->inverse(realIn, imagIn, realOut);
}
void
FFT::inversePolar(float *magIn, float *phaseIn, float *realOut)
{
d->inversePolar(magIn, phaseIn, realOut);
}
void
FFT::initFloat()
{
d->initFloat();
}
void
FFT::initDouble()
{
d->initDouble();
}
float *
FFT::getFloatTimeBuffer()
{
return d->getFloatTimeBuffer();
}
double *
FFT::getDoubleTimeBuffer()
{
return d->getDoubleTimeBuffer();
}
void
FFT::tune()
{
}
}

View file

@ -1,76 +0,0 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band
An audio time-stretching and pitch-shifting library.
Copyright 2007 Chris Cannam.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#ifndef _RUBBERBAND_FFT_H_
#define _RUBBERBAND_FFT_H_
namespace RubberBand {
class FFTImpl;
/**
* Provide the basic FFT computations we need, using one of a set of
* candidate FFT implementations (depending on compile flags).
*
* Implements real->complex FFTs of power-of-two sizes only. Note
* that only the first half of the output signal is returned (the
* complex conjugates half is omitted), so the "complex" arrays need
* room for size/2+1 elements.
*
* Not thread safe: use a separate instance per thread.
*/
class FFT
{
public:
enum Exception { InvalidSize };
FFT(unsigned int size); // may throw InvalidSize
~FFT();
void forward(double *realIn, double *realOut, double *imagOut);
void forwardPolar(double *realIn, double *magOut, double *phaseOut);
void forwardMagnitude(double *realIn, double *magOut);
void forward(float *realIn, float *realOut, float *imagOut);
void forwardPolar(float *realIn, float *magOut, float *phaseOut);
void forwardMagnitude(float *realIn, float *magOut);
void inverse(double *realIn, double *imagIn, double *realOut);
void inversePolar(double *magIn, double *phaseIn, double *realOut);
void inverse(float *realIn, float *imagIn, float *realOut);
void inversePolar(float *magIn, float *phaseIn, float *realOut);
// Calling one or both of these is optional -- if neither is
// called, the first call to a forward or inverse method will call
// init(). You only need call these if you don't want to risk
// expensive allocations etc happening in forward or inverse.
void initFloat();
void initDouble();
float *getFloatTimeBuffer();
double *getDoubleTimeBuffer();
static void tune();
protected:
FFTImpl *d;
static int m_method;
};
}
#endif

View file

@ -1,53 +0,0 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band
An audio time-stretching and pitch-shifting library.
Copyright 2007 Chris Cannam.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#include "HighFrequencyAudioCurve.h"
namespace RubberBand
{
HighFrequencyAudioCurve::HighFrequencyAudioCurve(size_t sampleRate, size_t windowSize) :
AudioCurve(sampleRate, windowSize)
{
}
HighFrequencyAudioCurve::~HighFrequencyAudioCurve()
{
}
void
HighFrequencyAudioCurve::reset()
{
}
void
HighFrequencyAudioCurve::setWindowSize(size_t newSize)
{
m_windowSize = newSize;
}
float
HighFrequencyAudioCurve::process(float *mag, size_t increment)
{
float result = 0.0;
for (size_t n = 0; n <= m_windowSize / 2; ++n) {
result += mag[n] * n;
}
return result;
}
}

View file

@ -1,39 +0,0 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band
An audio time-stretching and pitch-shifting library.
Copyright 2007 Chris Cannam.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#ifndef _HIGHFREQUENCY_AUDIO_CURVE_H_
#define _HIGHFREQUENCY_AUDIO_CURVE_H_
#include "AudioCurve.h"
#include "Window.h"
namespace RubberBand
{
class HighFrequencyAudioCurve : public AudioCurve
{
public:
HighFrequencyAudioCurve(size_t sampleRate, size_t windowSize);
virtual ~HighFrequencyAudioCurve();
virtual void setWindowSize(size_t newSize);
virtual float process(float *mag, size_t increment);
virtual void reset();
};
}
#endif

View file

@ -1,78 +0,0 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band
An audio time-stretching and pitch-shifting library.
Copyright 2007 Chris Cannam.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#include "PercussiveAudioCurve.h"
#include <cmath>
namespace RubberBand
{
PercussiveAudioCurve::PercussiveAudioCurve(size_t sampleRate, size_t windowSize) :
AudioCurve(sampleRate, windowSize)
{
m_prevMag = new double[m_windowSize/2 + 1];
for (size_t i = 0; i <= m_windowSize/2; ++i) {
m_prevMag[i] = 0.f;
}
}
PercussiveAudioCurve::~PercussiveAudioCurve()
{
delete[] m_prevMag;
}
void
PercussiveAudioCurve::reset()
{
for (size_t i = 0; i <= m_windowSize/2; ++i) {
m_prevMag[i] = 0;
}
}
void
PercussiveAudioCurve::setWindowSize(size_t newSize)
{
delete[] m_prevMag;
m_windowSize = newSize;
m_prevMag = new double[m_windowSize/2 + 1];
reset();
}
float
PercussiveAudioCurve::process(float *mag, size_t increment)
{
static float threshold = pow(10, 0.3);
static float zeroThresh = pow(10, -16);
size_t count = 0;
size_t nonZeroCount = 0;
for (size_t n = 1; n <= m_windowSize / 2; ++n) {
float sqrmag = mag[n] * mag[n];
bool above = ((sqrmag / m_prevMag[n]) >= threshold);
if (above) ++count;
if (sqrmag > zeroThresh) ++nonZeroCount;
m_prevMag[n] = sqrmag;
}
if (nonZeroCount == 0) return 0;
else return float(count) / float(nonZeroCount);
}
}

View file

@ -1,41 +0,0 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band
An audio time-stretching and pitch-shifting library.
Copyright 2007 Chris Cannam.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#ifndef _PERCUSSIVE_AUDIO_CURVE_H_
#define _PERCUSSIVE_AUDIO_CURVE_H_
#include "AudioCurve.h"
namespace RubberBand
{
class PercussiveAudioCurve : public AudioCurve
{
public:
PercussiveAudioCurve(size_t sampleRate, size_t windowSize);
virtual ~PercussiveAudioCurve();
virtual void setWindowSize(size_t newSize);
virtual float process(float *mag, size_t increment);
virtual void reset();
protected:
double *m_prevMag;
};
}
#endif

View file

@ -1,169 +0,0 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band
An audio time-stretching and pitch-shifting library.
Copyright 2007 Chris Cannam.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#include "Resampler.h"
#include <cstdlib>
#include <cmath>
#include <iostream>
#include <samplerate.h>
namespace RubberBand {
class Resampler::D
{
public:
D(Quality quality, size_t channels, size_t maxBufferSize);
~D();
size_t resample(float **in, float **out,
size_t incount, float ratio, bool final);
void reset();
protected:
SRC_STATE *m_src;
float *m_iin;
float *m_iout;
size_t m_channels;
size_t m_iinsize;
size_t m_ioutsize;
};
Resampler::D::D(Quality quality, size_t channels, size_t maxBufferSize) :
m_src(0),
m_iin(0),
m_iout(0),
m_channels(channels),
m_iinsize(0),
m_ioutsize(0)
{
// std::cerr << "Resampler::Resampler: using libsamplerate implementation"
// << std::endl;
int err = 0;
m_src = src_new(quality == Best ? SRC_SINC_BEST_QUALITY :
quality == Fastest ? SRC_LINEAR :
SRC_SINC_FASTEST,
channels, &err);
//!!! check err, throw
if (maxBufferSize > 0 && m_channels > 1) {
//!!! alignment?
m_iinsize = maxBufferSize * m_channels;
m_ioutsize = maxBufferSize * m_channels * 2;
m_iin = (float *)malloc(m_iinsize * sizeof(float));
m_iout = (float *)malloc(m_ioutsize * sizeof(float));
}
}
Resampler::D::~D()
{
src_delete(m_src);
if (m_iinsize > 0) {
free(m_iin);
}
if (m_ioutsize > 0) {
free(m_iout);
}
}
size_t
Resampler::D::resample(float **in, float **out, size_t incount, float ratio,
bool final)
{
SRC_DATA data;
size_t outcount = lrintf(ceilf(incount * ratio));
if (m_channels == 1) {
data.data_in = *in;
data.data_out = *out;
} else {
if (incount * m_channels > m_iinsize) {
m_iinsize = incount * m_channels;
m_iin = (float *)realloc(m_iin, m_iinsize * sizeof(float));
}
if (outcount * m_channels > m_ioutsize) {
m_ioutsize = outcount * m_channels;
m_iout = (float *)realloc(m_iout, m_ioutsize * sizeof(float));
}
for (size_t i = 0; i < incount; ++i) {
for (size_t c = 0; c < m_channels; ++c) {
m_iin[i * m_channels + c] = in[c][i];
}
}
data.data_in = m_iin;
data.data_out = m_iout;
}
data.input_frames = incount;
data.output_frames = outcount;
data.src_ratio = ratio;
data.end_of_input = (final ? 1 : 0);
int err = src_process(m_src, &data);
//!!! check err, respond appropriately
if (m_channels > 1) {
for (int i = 0; i < data.output_frames_gen; ++i) {
for (size_t c = 0; c < m_channels; ++c) {
out[c][i] = m_iout[i * m_channels + c];
}
}
}
return data.output_frames_gen;
}
void
Resampler::D::reset()
{
src_reset(m_src);
}
} // end namespace
namespace RubberBand {
Resampler::Resampler(Quality quality, size_t channels, size_t maxBufferSize)
{
m_d = new D(quality, channels, maxBufferSize);
}
Resampler::~Resampler()
{
delete m_d;
}
size_t
Resampler::resample(float **in, float **out,
size_t incount, float ratio, bool final)
{
return m_d->resample(in, out, incount, ratio, final);
}
void
Resampler::reset()
{
m_d->reset();
}
}

View file

@ -1,48 +0,0 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band
An audio time-stretching and pitch-shifting library.
Copyright 2007 Chris Cannam.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#ifndef _RUBBERBAND_RESAMPLER_H_
#define _RUBBERBAND_RESAMPLER_H_
#include <sys/types.h>
namespace RubberBand {
class Resampler
{
public:
enum Quality { Best, FastestTolerable, Fastest };
/**
* Construct a resampler with the given quality level and channel
* count. maxBufferSize gives a bound on the maximum incount size
* that may be passed to the resample function before the
* resampler needs to reallocate its internal buffers.
*/
Resampler(Quality quality, size_t channels, size_t maxBufferSize = 0);
~Resampler();
size_t resample(float **in, float **out,
size_t incount, float ratio, bool final = false);
void reset();
protected:
class D;
D *m_d;
};
}
#endif

View file

@ -1,636 +0,0 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band
An audio time-stretching and pitch-shifting library.
Copyright 2007 Chris Cannam.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#ifndef _RUBBERBAND_RINGBUFFER_H_
#define _RUBBERBAND_RINGBUFFER_H_
#include <sys/types.h>
#ifndef _WIN32
#include <sys/mman.h>
#endif
#include "Scavenger.h"
//#define DEBUG_RINGBUFFER 1
#ifdef _WIN32
#define MLOCK(a,b) 1
#define MUNLOCK(a,b) 1
#else
#define MLOCK(a,b) ::mlock(a,b)
#define MUNLOCK(a,b) ::munlock(a,b)
#endif
#ifdef DEBUG_RINGBUFFER
#include <iostream>
#endif
namespace RubberBand {
/**
* RingBuffer implements a lock-free ring buffer for one writer and N
* readers, that is to be used to store a sample type T.
*/
template <typename T, int N = 1>
class RingBuffer
{
public:
/**
* Create a ring buffer with room to write n samples.
*
* Note that the internal storage size will actually be n+1
* samples, as one element is unavailable for administrative
* reasons. Since the ring buffer performs best if its size is a
* power of two, this means n should ideally be some power of two
* minus one.
*/
RingBuffer(size_t n);
virtual ~RingBuffer();
/**
* Return the total capacity of the ring buffer in samples.
* (This is the argument n passed to the constructor.)
*/
size_t getSize() const;
/**
* Resize the ring buffer. This also empties it; use resized()
* below if you do not want this to happen. Actually swaps in a
* new, larger buffer; the old buffer is scavenged after a seemly
* delay. Should be called from the write thread.
*/
void resize(size_t newSize);
/**
* Return a new ring buffer (allocated with "new" -- called must
* delete when no longer needed) of the given size, containing the
* same data as this one. If another thread reads from or writes
* to this buffer during the call, the results may be incomplete
* or inconsistent. If this buffer's data will not fit in the new
* size, the contents are undefined.
*/
RingBuffer<T, N> *resized(size_t newSize, int R = 0) const;
/**
* Lock the ring buffer into physical memory. Returns true
* for success.
*/
bool mlock();
/**
* Reset read and write pointers, thus emptying the buffer.
* Should be called from the write thread.
*/
void reset();
/**
* Return the amount of data available for reading by reader R, in
* samples.
*/
size_t getReadSpace(int R = 0) const;
/**
* Return the amount of space available for writing, in samples.
*/
size_t getWriteSpace() const;
/**
* Read n samples from the buffer, for reader R. If fewer than n
* are available, the remainder will be zeroed out. Returns the
* number of samples actually read.
*/
size_t read(T *destination, size_t n, int R = 0);
/**
* Read n samples from the buffer, for reader R, adding them to
* the destination. If fewer than n are available, the remainder
* will be left alone. Returns the number of samples actually
* read.
*/
size_t readAdding(T *destination, size_t n, int R = 0);
/**
* Read one sample from the buffer, for reader R. If no sample is
* available, this will silently return zero. Calling this
* repeatedly is obviously slower than calling read once, but it
* may be good enough if you don't want to allocate a buffer to
* read into.
*/
T readOne(int R = 0);
/**
* Read n samples from the buffer, if available, for reader R,
* without advancing the read pointer -- i.e. a subsequent read()
* or skip() will be necessary to empty the buffer. If fewer than
* n are available, the remainder will be zeroed out. Returns the
* number of samples actually read.
*/
size_t peek(T *destination, size_t n, int R = 0) const;
/**
* Read one sample from the buffer, if available, without
* advancing the read pointer -- i.e. a subsequent read() or
* skip() will be necessary to empty the buffer. Returns zero if
* no sample was available.
*/
T peekOne(int R = 0) const;
/**
* Pretend to read n samples from the buffer, for reader R,
* without actually returning them (i.e. discard the next n
* samples). Returns the number of samples actually available for
* discarding.
*/
size_t skip(size_t n, int R = 0);
/**
* Write n samples to the buffer. If insufficient space is
* available, not all samples may actually be written. Returns
* the number of samples actually written.
*/
size_t write(const T *source, size_t n);
/**
* Write n zero-value samples to the buffer. If insufficient
* space is available, not all zeros may actually be written.
* Returns the number of zeroes actually written.
*/
size_t zero(size_t n);
protected:
T *m_buffer;
volatile size_t m_writer;
volatile size_t m_readers[N];
size_t m_size;
bool m_mlocked;
static Scavenger<ScavengerArrayWrapper<T> > m_scavenger;
private:
RingBuffer(const RingBuffer &); // not provided
RingBuffer &operator=(const RingBuffer &); // not provided
};
template <typename T, int N>
Scavenger<ScavengerArrayWrapper<T> > RingBuffer<T, N>::m_scavenger;
template <typename T, int N>
RingBuffer<T, N>::RingBuffer(size_t n) :
m_buffer(new T[n + 1]),
m_writer(0),
m_size(n + 1),
m_mlocked(false)
{
#ifdef DEBUG_RINGBUFFER
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::RingBuffer(" << n << ")" << std::endl;
#endif
for (int i = 0; i < N; ++i) m_readers[i] = 0;
m_scavenger.scavenge();
}
template <typename T, int N>
RingBuffer<T, N>::~RingBuffer()
{
#ifdef DEBUG_RINGBUFFER
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::~RingBuffer" << std::endl;
#endif
if (m_mlocked) {
MUNLOCK((void *)m_buffer, m_size * sizeof(T));
}
delete[] m_buffer;
m_scavenger.scavenge();
}
template <typename T, int N>
size_t
RingBuffer<T, N>::getSize() const
{
#ifdef DEBUG_RINGBUFFER
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::getSize(): " << m_size-1 << std::endl;
#endif
return m_size - 1;
}
template <typename T, int N>
void
RingBuffer<T, N>::resize(size_t newSize)
{
#ifdef DEBUG_RINGBUFFER
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::resize(" << newSize << ")" << std::endl;
#endif
m_scavenger.scavenge();
if (m_mlocked) {
MUNLOCK((void *)m_buffer, m_size * sizeof(T));
}
m_scavenger.claim(new ScavengerArrayWrapper<T>(m_buffer));
reset();
m_buffer = new T[newSize + 1];
m_size = newSize + 1;
if (m_mlocked) {
if (MLOCK((void *)m_buffer, m_size * sizeof(T))) {
m_mlocked = false;
}
}
}
template <typename T, int N>
RingBuffer<T, N> *
RingBuffer<T, N>::resized(size_t newSize, int R) const
{
RingBuffer<T, N> *newBuffer = new RingBuffer<T, N>(newSize);
size_t w = m_writer;
size_t r = m_readers[R];
while (r != w) {
T value = m_buffer[r];
newBuffer->write(&value, 1);
if (++r == m_size) r = 0;
}
return newBuffer;
}
template <typename T, int N>
bool
RingBuffer<T, N>::mlock()
{
if (MLOCK((void *)m_buffer, m_size * sizeof(T))) return false;
m_mlocked = true;
return true;
}
template <typename T, int N>
void
RingBuffer<T, N>::reset()
{
#ifdef DEBUG_RINGBUFFER
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::reset" << std::endl;
#endif
m_writer = 0;
for (int i = 0; i < N; ++i) m_readers[i] = 0;
}
template <typename T, int N>
size_t
RingBuffer<T, N>::getReadSpace(int R) const
{
size_t writer = m_writer;
size_t reader = m_readers[R];
size_t space;
#ifdef DEBUG_RINGBUFFER
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::getReadSpace(" << R << "): reader " << reader << ", writer " << writer << std::endl;
#endif
if (writer > reader) space = writer - reader;
else if (writer < reader) space = (writer + m_size) - reader;
else space = 0;
#ifdef DEBUG_RINGBUFFER
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::getReadSpace(" << R << "): " << space << std::endl;
#endif
return space;
}
template <typename T, int N>
size_t
RingBuffer<T, N>::getWriteSpace() const
{
size_t space = 0;
for (int i = 0; i < N; ++i) {
size_t writer = m_writer;
size_t reader = m_readers[i];
size_t here = (reader + m_size - writer - 1);
if (here >= m_size) here -= m_size;
if (i == 0 || here < space) space = here;
}
#ifdef DEBUG_RINGBUFFER
size_t rs(getReadSpace()), rp(m_readers[0]);
std::cerr << "RingBuffer: write space " << space << ", read space "
<< rs << ", total " << (space + rs) << ", m_size " << m_size << std::endl;
std::cerr << "RingBuffer: reader " << rp << ", writer " << m_writer << std::endl;
#endif
#ifdef DEBUG_RINGBUFFER
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::getWriteSpace(): " << space << std::endl;
#endif
return space;
}
template <typename T, int N>
size_t
RingBuffer<T, N>::read(T *destination, size_t n, int R)
{
#ifdef DEBUG_RINGBUFFER
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::read(dest, " << n << ", " << R << ")" << std::endl;
#endif
size_t available = getReadSpace(R);
if (n > available) {
#ifdef DEBUG_RINGBUFFER
std::cerr << "WARNING: Only " << available << " samples available"
<< std::endl;
#endif
for (size_t i = available; i < n; ++i) {
destination[i] = 0;
}
n = available;
}
if (n == 0) return n;
size_t reader = m_readers[R];
size_t here = m_size - reader;
if (here >= n) {
for (size_t i = 0; i < n; ++i) {
destination[i] = (m_buffer + reader)[i];
}
} else {
for (size_t i = 0; i < here; ++i) {
destination[i] = (m_buffer + reader)[i];
}
for (size_t i = 0; i < (n - here); ++i) {
destination[i + here] = m_buffer[i];
}
}
reader += n;
while (reader >= m_size) reader -= m_size;
m_readers[R] = reader;
#ifdef DEBUG_RINGBUFFER
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::read: read " << n << ", reader now " << m_readers[R] << std::endl;
#endif
return n;
}
template <typename T, int N>
size_t
RingBuffer<T, N>::readAdding(T *destination, size_t n, int R)
{
#ifdef DEBUG_RINGBUFFER
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::readAdding(dest, " << n << ", " << R << ")" << std::endl;
#endif
size_t available = getReadSpace(R);
if (n > available) {
#ifdef DEBUG_RINGBUFFER
std::cerr << "WARNING: Only " << available << " samples available"
<< std::endl;
#endif
n = available;
}
if (n == 0) return n;
size_t reader = m_readers[R];
size_t here = m_size - reader;
if (here >= n) {
for (size_t i = 0; i < n; ++i) {
destination[i] += (m_buffer + reader)[i];
}
} else {
for (size_t i = 0; i < here; ++i) {
destination[i] += (m_buffer + reader)[i];
}
for (size_t i = 0; i < (n - here); ++i) {
destination[i + here] += m_buffer[i];
}
}
reader += n;
while (reader >= m_size) reader -= m_size;
m_readers[R] = reader;
return n;
}
template <typename T, int N>
T
RingBuffer<T, N>::readOne(int R)
{
#ifdef DEBUG_RINGBUFFER
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::readOne(" << R << ")" << std::endl;
#endif
if (m_writer == m_readers[R]) {
#ifdef DEBUG_RINGBUFFER
std::cerr << "WARNING: No sample available"
<< std::endl;
#endif
return 0;
}
size_t reader = m_readers[R];
T value = m_buffer[reader];
if (++reader == m_size) reader = 0;
m_readers[R] = reader;
return value;
}
template <typename T, int N>
size_t
RingBuffer<T, N>::peek(T *destination, size_t n, int R) const
{
#ifdef DEBUG_RINGBUFFER
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::peek(dest, " << n << ", " << R << ")" << std::endl;
#endif
size_t available = getReadSpace(R);
if (n > available) {
#ifdef DEBUG_RINGBUFFER
std::cerr << "WARNING: Only " << available << " samples available"
<< std::endl;
#endif
memset(destination + available, 0, (n - available) * sizeof(T));
n = available;
}
if (n == 0) return n;
size_t reader = m_readers[R];
size_t here = m_size - reader;
if (here >= n) {
for (size_t i = 0; i < n; ++i) {
destination[i] = (m_buffer + reader)[i];
}
} else {
for (size_t i = 0; i < here; ++i) {
destination[i] = (m_buffer + reader)[i];
}
for (size_t i = 0; i < (n - here); ++i) {
destination[i + here] = m_buffer[i];
}
}
#ifdef DEBUG_RINGBUFFER
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::peek: read " << n << std::endl;
#endif
return n;
}
template <typename T, int N>
T
RingBuffer<T, N>::peekOne(int R) const
{
#ifdef DEBUG_RINGBUFFER
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::peek(" << R << ")" << std::endl;
#endif
if (m_writer == m_readers[R]) {
#ifdef DEBUG_RINGBUFFER
std::cerr << "WARNING: No sample available"
<< std::endl;
#endif
return 0;
}
T value = m_buffer[m_readers[R]];
return value;
}
template <typename T, int N>
size_t
RingBuffer<T, N>::skip(size_t n, int R)
{
#ifdef DEBUG_RINGBUFFER
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::skip(" << n << ", " << R << ")" << std::endl;
#endif
size_t available = getReadSpace(R);
if (n > available) {
#ifdef DEBUG_RINGBUFFER
std::cerr << "WARNING: Only " << available << " samples available"
<< std::endl;
#endif
n = available;
}
if (n == 0) return n;
size_t reader = m_readers[R];
reader += n;
while (reader >= m_size) reader -= m_size;
m_readers[R] = reader;
return n;
}
template <typename T, int N>
size_t
RingBuffer<T, N>::write(const T *source, size_t n)
{
#ifdef DEBUG_RINGBUFFER
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::write(" << n << ")" << std::endl;
#endif
size_t available = getWriteSpace();
if (n > available) {
#ifdef DEBUG_RINGBUFFER
std::cerr << "WARNING: Only room for " << available << " samples"
<< std::endl;
#endif
n = available;
}
if (n == 0) return n;
size_t writer = m_writer;
size_t here = m_size - writer;
if (here >= n) {
for (size_t i = 0; i < n; ++i) {
(m_buffer + writer)[i] = source[i];
}
} else {
for (size_t i = 0; i < here; ++i) {
(m_buffer + writer)[i] = source[i];
}
for (size_t i = 0; i < (n - here); ++i) {
m_buffer[i] = (source + here)[i];
}
}
writer += n;
while (writer >= m_size) writer -= m_size;
m_writer = writer;
#ifdef DEBUG_RINGBUFFER
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::write: wrote " << n << ", writer now " << m_writer << std::endl;
#endif
return n;
}
template <typename T, int N>
size_t
RingBuffer<T, N>::zero(size_t n)
{
#ifdef DEBUG_RINGBUFFER
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::zero(" << n << ")" << std::endl;
#endif
size_t available = getWriteSpace();
if (n > available) {
#ifdef DEBUG_RINGBUFFER
std::cerr << "WARNING: Only room for " << available << " samples"
<< std::endl;
#endif
n = available;
}
if (n == 0) return n;
size_t writer = m_writer;
size_t here = m_size - writer;
if (here >= n) {
for (size_t i = 0; i < n; ++i) {
(m_buffer + writer)[i] = 0;
}
} else {
for (size_t i = 0; i < here; ++i) {
(m_buffer + writer)[i] = 0;
}
for (size_t i = 0; i < (n - here); ++i) {
m_buffer[i] = 0;
}
}
writer += n;
while (writer >= m_size) writer -= m_size;
m_writer = writer;
#ifdef DEBUG_RINGBUFFER
std::cerr << "writer -> " << m_writer << std::endl;
#endif
return n;
}
}
#endif // _RINGBUFFER_H_

View file

@ -1,189 +0,0 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band
An audio time-stretching and pitch-shifting library.
Copyright 2007 Chris Cannam.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#include "StretcherImpl.h"
namespace RubberBand {
RubberBandStretcher::RubberBandStretcher(size_t sampleRate,
size_t channels,
Options options,
double initialTimeRatio,
double initialPitchScale) :
TimeStretcher(sampleRate, channels),
m_d(new Impl(this, sampleRate, channels, options,
initialTimeRatio, initialPitchScale))
{
}
RubberBandStretcher::~RubberBandStretcher()
{
delete m_d;
}
void
RubberBandStretcher::reset()
{
m_d->reset();
}
void
RubberBandStretcher::setTimeRatio(double ratio)
{
m_d->setTimeRatio(ratio);
}
void
RubberBandStretcher::setPitchScale(double scale)
{
m_d->setPitchScale(scale);
}
double
RubberBandStretcher::getTimeRatio() const
{
return m_d->getTimeRatio();
}
double
RubberBandStretcher::getPitchScale() const
{
return m_d->getPitchScale();
}
size_t
RubberBandStretcher::getLatency() const
{
return m_d->getLatency();
}
void
RubberBandStretcher::setTransientsOption(Options options)
{
m_d->setTransientsOption(options);
}
void
RubberBandStretcher::setPhaseOption(Options options)
{
m_d->setPhaseOption(options);
}
void
RubberBandStretcher::setExpectedInputDuration(size_t samples)
{
m_d->setExpectedInputDuration(samples);
}
void
RubberBandStretcher::setMaxProcessSize(size_t samples)
{
m_d->setMaxProcessSize(samples);
}
size_t
RubberBandStretcher::getSamplesRequired() const
{
return m_d->getSamplesRequired();
}
void
RubberBandStretcher::study(const float *const *input, size_t samples,
bool final)
{
m_d->study(input, samples, final);
}
void
RubberBandStretcher::process(const float *const *input, size_t samples,
bool final)
{
m_d->process(input, samples, final);
}
int
RubberBandStretcher::available() const
{
return m_d->available();
}
size_t
RubberBandStretcher::retrieve(float *const *output, size_t samples) const
{
return m_d->retrieve(output, samples);
}
float
RubberBandStretcher::getFrequencyCutoff(int n) const
{
return m_d->getFrequencyCutoff(n);
}
void
RubberBandStretcher::setFrequencyCutoff(int n, float f)
{
m_d->setFrequencyCutoff(n, f);
}
size_t
RubberBandStretcher::getInputIncrement() const
{
return m_d->getInputIncrement();
}
std::vector<int>
RubberBandStretcher::getOutputIncrements() const
{
return m_d->getOutputIncrements();
}
std::vector<float>
RubberBandStretcher::getPhaseResetCurve() const
{
return m_d->getPhaseResetCurve();
}
std::vector<int>
RubberBandStretcher::getExactTimePoints() const
{
return m_d->getExactTimePoints();
}
size_t
RubberBandStretcher::getChannelCount() const
{
return m_d->getChannelCount();
}
void
RubberBandStretcher::calculateStretch()
{
m_d->calculateStretch();
}
void
RubberBandStretcher::setDebugLevel(int level)
{
m_d->setDebugLevel(level);
}
void
RubberBandStretcher::setDefaultDebugLevel(int level)
{
Impl::setDefaultDebugLevel(level);
}
}

View file

@ -1,199 +0,0 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band
An audio time-stretching and pitch-shifting library.
Copyright 2007 Chris Cannam.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#ifndef _RUBBERBAND_SCAVENGER_H_
#define _RUBBERBAND_SCAVENGER_H_
#include <vector>
#include <list>
#include <sys/time.h>
#include <iostream>
#include "Thread.h"
#include "sysutils.h"
namespace RubberBand {
/**
* A very simple class that facilitates running things like plugins
* without locking, by collecting unwanted objects and deleting them
* after a delay so as to be sure nobody's in the middle of using
* them. Requires scavenge() to be called regularly from a non-RT
* thread.
*
* This is currently not at all suitable for large numbers of objects
* -- it's just a quick hack for use with things like plugins.
*/
template <typename T>
class Scavenger
{
public:
Scavenger(int sec = 2, int defaultObjectListSize = 200);
~Scavenger();
/**
* Call from an RT thread etc., to pass ownership of t to us.
* Only one thread should be calling this on any given scavenger.
*/
void claim(T *t);
/**
* Call from a non-RT thread.
* Only one thread should be calling this on any given scavenger.
*/
void scavenge(bool clearNow = false);
protected:
typedef std::pair<T *, int> ObjectTimePair;
typedef std::vector<ObjectTimePair> ObjectTimeList;
ObjectTimeList m_objects;
int m_sec;
typedef std::list<T *> ObjectList;
ObjectList m_excess;
int m_lastExcess;
Mutex m_excessMutex;
void pushExcess(T *);
void clearExcess(int);
unsigned int m_claimed;
unsigned int m_scavenged;
};
/**
* A wrapper to permit arrays to be scavenged.
*/
template <typename T>
class ScavengerArrayWrapper
{
public:
ScavengerArrayWrapper(T *array) : m_array(array) { }
~ScavengerArrayWrapper() { delete[] m_array; }
private:
T *m_array;
};
template <typename T>
Scavenger<T>::Scavenger(int sec, int defaultObjectListSize) :
m_objects(ObjectTimeList(defaultObjectListSize)),
m_sec(sec),
m_claimed(0),
m_scavenged(0)
{
}
template <typename T>
Scavenger<T>::~Scavenger()
{
if (m_scavenged < m_claimed) {
for (size_t i = 0; i < m_objects.size(); ++i) {
ObjectTimePair &pair = m_objects[i];
if (pair.first != 0) {
T *ot = pair.first;
pair.first = 0;
delete ot;
++m_scavenged;
}
}
}
clearExcess(0);
}
template <typename T>
void
Scavenger<T>::claim(T *t)
{
// std::cerr << "Scavenger::claim(" << t << ")" << std::endl;
struct timeval tv;
(void)gettimeofday(&tv, 0);
int sec = tv.tv_sec;
for (size_t i = 0; i < m_objects.size(); ++i) {
ObjectTimePair &pair = m_objects[i];
if (pair.first == 0) {
pair.second = sec;
pair.first = t;
++m_claimed;
return;
}
}
std::cerr << "WARNING: Scavenger::claim(" << t << "): run out of slots, "
<< "using non-RT-safe method" << std::endl;
pushExcess(t);
}
template <typename T>
void
Scavenger<T>::scavenge(bool clearNow)
{
// std::cerr << "Scavenger::scavenge: scavenged " << m_scavenged << ", claimed " << m_claimed << std::endl;
if (m_scavenged >= m_claimed) return;
struct timeval tv;
(void)gettimeofday(&tv, 0);
int sec = tv.tv_sec;
for (size_t i = 0; i < m_objects.size(); ++i) {
ObjectTimePair &pair = m_objects[i];
if (clearNow ||
(pair.first != 0 && pair.second + m_sec < sec)) {
T *ot = pair.first;
pair.first = 0;
delete ot;
++m_scavenged;
}
}
if (sec > m_lastExcess + m_sec) {
clearExcess(sec);
}
}
template <typename T>
void
Scavenger<T>::pushExcess(T *t)
{
m_excessMutex.lock();
m_excess.push_back(t);
struct timeval tv;
(void)gettimeofday(&tv, 0);
m_lastExcess = tv.tv_sec;
m_excessMutex.unlock();
}
template <typename T>
void
Scavenger<T>::clearExcess(int sec)
{
m_excessMutex.lock();
for (typename ObjectList::iterator i = m_excess.begin();
i != m_excess.end(); ++i) {
delete *i;
}
m_excess.clear();
m_lastExcess = sec;
m_excessMutex.unlock();
}
}
#endif

View file

@ -1,64 +0,0 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band
An audio time-stretching and pitch-shifting library.
Copyright 2007 Chris Cannam.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#include "SpectralDifferenceAudioCurve.h"
namespace RubberBand
{
SpectralDifferenceAudioCurve::SpectralDifferenceAudioCurve(size_t sampleRate, size_t windowSize) :
AudioCurve(sampleRate, windowSize)
{
m_prevMag = new double[m_windowSize/2 + 1];
for (size_t i = 0; i <= m_windowSize/2; ++i) {
m_prevMag[i] = 0.f;
}
}
SpectralDifferenceAudioCurve::~SpectralDifferenceAudioCurve()
{
delete[] m_prevMag;
}
void
SpectralDifferenceAudioCurve::reset()
{
for (size_t i = 0; i <= m_windowSize/2; ++i) {
m_prevMag[i] = 0;
}
}
void
SpectralDifferenceAudioCurve::setWindowSize(size_t newSize)
{
m_windowSize = newSize;
}
float
SpectralDifferenceAudioCurve::process(float *mag, size_t increment)
{
float result = 0.0;
for (size_t n = 0; n <= m_windowSize / 2; ++n) {
result += sqrtf(fabsf((mag[n] * mag[n]) -
(m_prevMag[n] * m_prevMag[n])));
m_prevMag[n] = mag[n];
}
return result;
}
}

View file

@ -1,42 +0,0 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band
An audio time-stretching and pitch-shifting library.
Copyright 2007 Chris Cannam.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#ifndef _SPECTRALDIFFERENCE_AUDIO_CURVE_H_
#define _SPECTRALDIFFERENCE_AUDIO_CURVE_H_
#include "AudioCurve.h"
#include "Window.h"
namespace RubberBand
{
class SpectralDifferenceAudioCurve : public AudioCurve
{
public:
SpectralDifferenceAudioCurve(size_t sampleRate, size_t windowSize);
virtual ~SpectralDifferenceAudioCurve();
virtual void setWindowSize(size_t newSize);
virtual float process(float *mag, size_t increment);
virtual void reset();
protected:
double *m_prevMag;
};
}
#endif

View file

@ -1,790 +0,0 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band
An audio time-stretching and pitch-shifting library.
Copyright 2007 Chris Cannam.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#include "StretchCalculator.h"
#include <math.h>
#include <iostream>
#include <deque>
#include <set>
#include <cassert>
namespace RubberBand
{
StretchCalculator::StretchCalculator(size_t sampleRate,
size_t inputIncrement,
bool useHardPeaks) :
m_sampleRate(sampleRate),
m_increment(inputIncrement),
m_prevDf(0),
m_divergence(0),
m_recovery(0),
m_prevRatio(1.0),
m_transientAmnesty(0),
m_useHardPeaks(useHardPeaks)
{
// std::cerr << "StretchCalculator::StretchCalculator: useHardPeaks = " << useHardPeaks << std::endl;
}
StretchCalculator::~StretchCalculator()
{
}
std::vector<int>
StretchCalculator::calculate(double ratio, size_t inputDuration,
const std::vector<float> &phaseResetDf,
const std::vector<float> &stretchDf)
{
assert(phaseResetDf.size() == stretchDf.size());
m_lastPeaks = findPeaks(phaseResetDf);
std::vector<Peak> &peaks = m_lastPeaks;
size_t totalCount = phaseResetDf.size();
std::vector<int> increments;
size_t outputDuration = lrint(inputDuration * ratio);
if (m_debugLevel > 0) {
std::cerr << "StretchCalculator::calculate(): inputDuration " << inputDuration << ", ratio " << ratio << ", outputDuration " << outputDuration;
}
outputDuration = lrint((phaseResetDf.size() * m_increment) * ratio);
if (m_debugLevel > 0) {
std::cerr << " (rounded up to " << outputDuration << ")";
std::cerr << ", df size " << phaseResetDf.size() << std::endl;
}
std::vector<size_t> fixedAudioChunks;
for (size_t i = 0; i < peaks.size(); ++i) {
fixedAudioChunks.push_back
(lrint((double(peaks[i].chunk) * outputDuration) / totalCount));
}
if (m_debugLevel > 1) {
std::cerr << "have " << peaks.size() << " fixed positions" << std::endl;
}
size_t totalInput = 0, totalOutput = 0;
// For each region between two consecutive time sync points, we
// want to take the number of output chunks to be allocated and
// the detection function values within the range, and produce a
// series of increments that sum to the number of output chunks,
// such that each increment is displaced from the input increment
// by an amount inversely proportional to the magnitude of the
// stretch detection function at that input step.
size_t regionTotalChunks = 0;
for (size_t i = 0; i <= peaks.size(); ++i) {
size_t regionStart, regionStartChunk, regionEnd, regionEndChunk;
bool phaseReset = false;
if (i == 0) {
regionStartChunk = 0;
regionStart = 0;
} else {
regionStartChunk = peaks[i-1].chunk;
regionStart = fixedAudioChunks[i-1];
phaseReset = peaks[i-1].hard;
}
if (i == peaks.size()) {
regionEndChunk = totalCount;
regionEnd = outputDuration;
} else {
regionEndChunk = peaks[i].chunk;
regionEnd = fixedAudioChunks[i];
}
size_t regionDuration = regionEnd - regionStart;
regionTotalChunks += regionDuration;
std::vector<float> dfRegion;
for (size_t j = regionStartChunk; j != regionEndChunk; ++j) {
dfRegion.push_back(stretchDf[j]);
}
if (m_debugLevel > 1) {
std::cerr << "distributeRegion from " << regionStartChunk << " to " << regionEndChunk << " (chunks " << regionStart << " to " << regionEnd << ")" << std::endl;
}
dfRegion = smoothDF(dfRegion);
std::vector<int> regionIncrements = distributeRegion
(dfRegion, regionDuration, ratio, phaseReset);
size_t totalForRegion = 0;
for (size_t j = 0; j < regionIncrements.size(); ++j) {
int incr = regionIncrements[j];
if (j == 0 && phaseReset) increments.push_back(-incr);
else increments.push_back(incr);
if (incr > 0) totalForRegion += incr;
else totalForRegion += -incr;
totalInput += m_increment;
}
if (totalForRegion != regionDuration) {
std::cerr << "*** WARNING: distributeRegion returned wrong duration " << totalForRegion << ", expected " << regionDuration << std::endl;
}
totalOutput += totalForRegion;
}
if (m_debugLevel > 0) {
std::cerr << "total input increment = " << totalInput << " (= " << totalInput / m_increment << " chunks), output = " << totalOutput << ", ratio = " << double(totalOutput)/double(totalInput) << ", ideal output " << size_t(ceil(totalInput * ratio)) << std::endl;
std::cerr << "(region total = " << regionTotalChunks << ")" << std::endl;
}
return increments;
}
int
StretchCalculator::calculateSingle(double ratio,
size_t inputDurationSoFar,
float df)
{
bool isTransient = false;
// We want to ensure, as close as possible, that the phase reset
// points appear at _exactly_ the right audio frame numbers.
// In principle, the threshold depends on chunk size: larger chunk
// sizes need higher thresholds. Since chunk size depends on
// ratio, I suppose we could in theory calculate the threshold
// from the ratio directly. For the moment we're happy if it
// works well in common situations.
float transientThreshold = 0.35;
if (ratio > 1) transientThreshold = 0.25;
if (m_useHardPeaks && df > m_prevDf * 1.1 && df > transientThreshold) {
isTransient = true;
}
if (m_debugLevel > 2) {
std::cerr << "df = " << df << ", prevDf = " << m_prevDf
<< ", thresh = " << transientThreshold << std::endl;
}
m_prevDf = df;
if (isTransient && m_transientAmnesty == 0) {
if (m_debugLevel > 1) {
std::cerr << "StretchCalculator::calculateSingle: transient found at "
<< inputDurationSoFar << std::endl;
}
m_divergence += m_increment - (m_increment * ratio);
// as in offline mode, 0.05 sec approx min between transients
m_transientAmnesty =
lrint(ceil(double(m_sampleRate) / (20 * double(m_increment))));
m_recovery = m_divergence / ((m_sampleRate / 10.0) / m_increment);
return -m_increment;
}
if (m_prevRatio != ratio) {
m_recovery = m_divergence / ((m_sampleRate / 10.0) / m_increment);
m_prevRatio = ratio;
}
if (m_transientAmnesty > 0) --m_transientAmnesty;
int incr = lrint(m_increment * ratio - m_recovery);
if (m_debugLevel > 2 || (m_debugLevel > 1 && m_divergence != 0)) {
std::cerr << "divergence = " << m_divergence << ", recovery = " << m_recovery << ", incr = " << incr << ", ";
}
if (incr < lrint((m_increment * ratio) / 2)) {
incr = lrint((m_increment * ratio) / 2);
} else if (incr > lrint(m_increment * ratio * 2)) {
incr = lrint(m_increment * ratio * 2);
}
double divdiff = (m_increment * ratio) - incr;
if (m_debugLevel > 2 || (m_debugLevel > 1 && m_divergence != 0)) {
std::cerr << "divdiff = " << divdiff << std::endl;
}
double prevDivergence = m_divergence;
m_divergence -= divdiff;
if ((prevDivergence < 0 && m_divergence > 0) ||
(prevDivergence > 0 && m_divergence < 0)) {
m_recovery = m_divergence / ((m_sampleRate / 10.0) / m_increment);
}
return incr;
}
void
StretchCalculator::reset()
{
m_prevDf = 0;
m_divergence = 0;
}
std::vector<StretchCalculator::Peak>
StretchCalculator::findPeaks(const std::vector<float> &rawDf)
{
std::vector<float> df = smoothDF(rawDf);
// We distinguish between "soft" and "hard" peaks. A soft peak is
// simply the result of peak-picking on the smoothed onset
// detection function, and it represents any (strong-ish) onset.
// We aim to ensure always that soft peaks are placed at the
// correct position in time. A hard peak is where there is a very
// rapid rise in detection function, and it presumably represents
// a more broadband, noisy transient. For these we perform a
// phase reset (if in the appropriate mode), and we locate the
// reset at the first point where we notice enough of a rapid
// rise, rather than necessarily at the peak itself, in order to
// preserve the shape of the transient.
std::set<size_t> hardPeakCandidates;
std::set<size_t> softPeakCandidates;
if (m_useHardPeaks) {
// 0.05 sec approx min between hard peaks
size_t hardPeakAmnesty = lrint(ceil(double(m_sampleRate) /
(20 * double(m_increment))));
size_t prevHardPeak = 0;
if (m_debugLevel > 1) {
std::cerr << "hardPeakAmnesty = " << hardPeakAmnesty << std::endl;
}
for (size_t i = 1; i + 1 < df.size(); ++i) {
if (df[i] < 0.1) continue;
if (df[i] <= df[i-1] * 1.1) continue;
if (df[i] < 0.22) continue;
if (!hardPeakCandidates.empty() &&
i < prevHardPeak + hardPeakAmnesty) {
continue;
}
bool hard = (df[i] > 0.4);
if (hard && (m_debugLevel > 1)) {
std::cerr << "hard peak at " << i << ": " << df[i]
<< " > absolute " << 0.4
<< std::endl;
}
if (!hard) {
hard = (df[i] > df[i-1] * 1.4);
if (hard && (m_debugLevel > 1)) {
std::cerr << "hard peak at " << i << ": " << df[i]
<< " > prev " << df[i-1] << " * 1.4"
<< std::endl;
}
}
if (!hard && i > 1) {
hard = (df[i] > df[i-1] * 1.2 &&
df[i-1] > df[i-2] * 1.2);
if (hard && (m_debugLevel > 1)) {
std::cerr << "hard peak at " << i << ": " << df[i]
<< " > prev " << df[i-1] << " * 1.2 and "
<< df[i-1] << " > prev " << df[i-2] << " * 1.2"
<< std::endl;
}
}
if (!hard && i > 2) {
// have already established that df[i] > df[i-1] * 1.1
hard = (df[i] > 0.3 &&
df[i-1] > df[i-2] * 1.1 &&
df[i-2] > df[i-3] * 1.1);
if (hard && (m_debugLevel > 1)) {
std::cerr << "hard peak at " << i << ": " << df[i]
<< " > prev " << df[i-1] << " * 1.1 and "
<< df[i-1] << " > prev " << df[i-2] << " * 1.1 and "
<< df[i-2] << " > prev " << df[i-3] << " * 1.1"
<< std::endl;
}
}
if (!hard) continue;
// (df[i+1] > df[i] && df[i+1] > df[i-1] * 1.8) ||
// df[i] > 0.4) {
size_t peakLocation = i;
if (i + 1 < rawDf.size() &&
rawDf[i + 1] > rawDf[i] * 1.4) {
++peakLocation;
if (m_debugLevel > 1) {
std::cerr << "pushing hard peak forward to " << peakLocation << ": " << df[peakLocation] << " > " << df[peakLocation-1] << " * " << 1.4 << std::endl;
}
}
hardPeakCandidates.insert(peakLocation);
prevHardPeak = peakLocation;
}
}
size_t medianmaxsize = lrint(ceil(double(m_sampleRate) /
double(m_increment))); // 1 sec ish
if (m_debugLevel > 1) {
std::cerr << "mediansize = " << medianmaxsize << std::endl;
}
if (medianmaxsize < 7) {
medianmaxsize = 7;
if (m_debugLevel > 1) {
std::cerr << "adjusted mediansize = " << medianmaxsize << std::endl;
}
}
int minspacing = lrint(ceil(double(m_sampleRate) /
(20 * double(m_increment)))); // 0.05 sec ish
std::deque<float> medianwin;
std::vector<float> sorted;
int softPeakAmnesty = 0;
for (size_t i = 0; i < medianmaxsize/2; ++i) {
medianwin.push_back(0);
}
for (size_t i = 0; i < medianmaxsize/2 && i < df.size(); ++i) {
medianwin.push_back(df[i]);
}
size_t lastSoftPeak = 0;
for (size_t i = 0; i < df.size(); ++i) {
size_t mediansize = medianmaxsize;
if (medianwin.size() < mediansize) {
mediansize = medianwin.size();
}
size_t middle = medianmaxsize / 2;
if (middle >= mediansize) middle = mediansize-1;
size_t nextDf = i + mediansize - middle;
if (mediansize < 2) {
if (mediansize > medianmaxsize) { // absurd, but never mind that
medianwin.pop_front();
}
if (nextDf < df.size()) {
medianwin.push_back(df[nextDf]);
} else {
medianwin.push_back(0);
}
continue;
}
if (m_debugLevel > 2) {
// std::cerr << "have " << mediansize << " in median buffer" << std::endl;
}
sorted.clear();
for (size_t j = 0; j < mediansize; ++j) {
sorted.push_back(medianwin[j]);
}
std::sort(sorted.begin(), sorted.end());
size_t n = 90; // percentile above which we pick peaks
size_t index = (sorted.size() * n) / 100;
if (index >= sorted.size()) index = sorted.size()-1;
if (index == sorted.size()-1 && index > 0) --index;
float thresh = sorted[index];
// if (m_debugLevel > 2) {
// std::cerr << "medianwin[" << middle << "] = " << medianwin[middle] << ", thresh = " << thresh << std::endl;
// if (medianwin[middle] == 0.f) {
// std::cerr << "contents: ";
// for (size_t j = 0; j < medianwin.size(); ++j) {
// std::cerr << medianwin[j] << " ";
// }
// std::cerr << std::endl;
// }
// }
if (medianwin[middle] > thresh &&
medianwin[middle] > medianwin[middle-1] &&
medianwin[middle] > medianwin[middle+1] &&
softPeakAmnesty == 0) {
size_t maxindex = middle;
float maxval = medianwin[middle];
for (size_t j = middle+1; j < mediansize; ++j) {
if (medianwin[j] > maxval) {
maxval = medianwin[j];
maxindex = j;
} else if (medianwin[j] < medianwin[middle]) {
break;
}
}
size_t peak = i + maxindex - middle;
// std::cerr << "i = " << i << ", maxindex = " << maxindex << ", middle = " << middle << ", so peak at " << peak << std::endl;
if (softPeakCandidates.empty() || lastSoftPeak != peak) {
if (m_debugLevel > 1) {
std::cerr << "soft peak at " << peak << " ("
<< peak * m_increment << "): "
<< medianwin[middle] << " > "
<< thresh << " and "
<< medianwin[middle]
<< " > " << medianwin[middle-1] << " and "
<< medianwin[middle]
<< " > " << medianwin[middle+1]
<< std::endl;
}
if (peak >= df.size()) {
if (m_debugLevel > 2) {
std::cerr << "peak is beyond end" << std::endl;
}
} else {
softPeakCandidates.insert(peak);
lastSoftPeak = peak;
}
}
softPeakAmnesty = minspacing + maxindex - middle;
if (m_debugLevel > 2) {
std::cerr << "amnesty = " << softPeakAmnesty << std::endl;
}
} else if (softPeakAmnesty > 0) --softPeakAmnesty;
if (mediansize >= medianmaxsize) {
medianwin.pop_front();
}
if (nextDf < df.size()) {
medianwin.push_back(df[nextDf]);
} else {
medianwin.push_back(0);
}
}
std::vector<Peak> peaks;
while (!hardPeakCandidates.empty() || !softPeakCandidates.empty()) {
bool haveHardPeak = !hardPeakCandidates.empty();
bool haveSoftPeak = !softPeakCandidates.empty();
size_t hardPeak = (haveHardPeak ? *hardPeakCandidates.begin() : 0);
size_t softPeak = (haveSoftPeak ? *softPeakCandidates.begin() : 0);
Peak peak;
peak.hard = false;
peak.chunk = softPeak;
bool ignore = false;
if (haveHardPeak &&
(!haveSoftPeak || hardPeak <= softPeak)) {
if (m_debugLevel > 2) {
std::cerr << "Hard peak: " << hardPeak << std::endl;
}
peak.hard = true;
peak.chunk = hardPeak;
hardPeakCandidates.erase(hardPeakCandidates.begin());
} else {
if (m_debugLevel > 2) {
std::cerr << "Soft peak: " << softPeak << std::endl;
}
if (!peaks.empty() &&
peaks[peaks.size()-1].hard &&
peaks[peaks.size()-1].chunk + 3 >= softPeak) {
if (m_debugLevel > 2) {
std::cerr << "(ignoring, as we just had a hard peak)"
<< std::endl;
}
ignore = true;
}
}
if (haveSoftPeak && peak.chunk == softPeak) {
softPeakCandidates.erase(softPeakCandidates.begin());
}
if (!ignore) {
peaks.push_back(peak);
}
}
return peaks;
}
std::vector<float>
StretchCalculator::smoothDF(const std::vector<float> &df)
{
std::vector<float> smoothedDF;
for (size_t i = 0; i < df.size(); ++i) {
// three-value moving mean window for simple smoothing
float total = 0.f, count = 0;
if (i > 0) { total += df[i-1]; ++count; }
total += df[i]; ++count;
if (i+1 < df.size()) { total += df[i+1]; ++count; }
float mean = total / count;
smoothedDF.push_back(mean);
}
return smoothedDF;
}
std::vector<int>
StretchCalculator::distributeRegion(const std::vector<float> &dfIn,
size_t duration, float ratio, bool phaseReset)
{
std::vector<float> df(dfIn);
std::vector<int> increments;
// The peak for the stretch detection function may appear after
// the peak that we're using to calculate the start of the region.
// We don't want that. If we find a peak in the first half of
// the region, we should set all the values up to that point to
// the same value as the peak.
// (This might not be subtle enough, especially if the region is
// long -- we want a bound that corresponds to acoustic perception
// of the audible bounce.)
for (size_t i = 1; i < df.size()/2; ++i) {
if (df[i] < df[i-1]) {
if (m_debugLevel > 1) {
std::cerr << "stretch peak offset: " << i-1 << " (peak " << df[i-1] << ")" << std::endl;
}
for (size_t j = 0; j < i-1; ++j) {
df[j] = df[i-1];
}
break;
}
}
float maxDf = 0;
for (size_t i = 0; i < df.size(); ++i) {
if (i == 0 || df[i] > maxDf) maxDf = df[i];
}
// We want to try to ensure the last 100ms or so (if possible) are
// tending back towards the maximum df, so that the stretchiness
// reduces at the end of the stretched region.
int reducedRegion = lrint((0.1 * m_sampleRate) / m_increment);
if (reducedRegion > int(df.size()/5)) reducedRegion = df.size()/5;
for (int i = 0; i < reducedRegion; ++i) {
size_t index = df.size() - reducedRegion + i;
df[index] = df[index] + ((maxDf - df[index]) * i) / reducedRegion;
}
long toAllot = long(duration) - long(m_increment * df.size());
if (m_debugLevel > 1) {
std::cerr << "region of " << df.size() << " chunks, output duration " << duration << ", toAllot " << toAllot << std::endl;
}
size_t totalIncrement = 0;
// We place limits on the amount of displacement per chunk. if
// ratio < 0, no increment should be larger than increment*ratio
// or smaller than increment*ratio/2; if ratio > 0, none should be
// smaller than increment*ratio or larger than increment*ratio*2.
// We need to enforce this in the assignment of displacements to
// allotments, not by trying to respond if something turns out
// wrong.
// Note that the ratio is only provided to this function for the
// purposes of establishing this bound to the displacement.
// so if
// maxDisplacement / totalDisplacement > increment * ratio*2 - increment
// (for ratio > 1)
// or
// maxDisplacement / totalDisplacement < increment * ratio/2
// (for ratio < 1)
// then we need to adjust and accommodate
bool acceptableSquashRange = false;
double totalDisplacement = 0;
double maxDisplacement = 0; // min displacement will be 0 by definition
maxDf = 0;
float adj = 0;
while (!acceptableSquashRange) {
acceptableSquashRange = true;
calculateDisplacements(df, maxDf, totalDisplacement, maxDisplacement,
adj);
if (m_debugLevel > 1) {
std::cerr << "totalDisplacement " << totalDisplacement << ", max " << maxDisplacement << " (maxDf " << maxDf << ", df count " << df.size() << ")" << std::endl;
}
if (totalDisplacement == 0) {
// Not usually a problem, in fact
// std::cerr << "WARNING: totalDisplacement == 0 (duration " << duration << ", " << df.size() << " values in df)" << std::endl;
if (!df.empty() && adj == 0) {
acceptableSquashRange = false;
adj = 1;
}
continue;
}
int extremeIncrement = m_increment + lrint((toAllot * maxDisplacement) / totalDisplacement);
if (ratio < 1.0) {
if (extremeIncrement > lrint(ceil(m_increment * ratio))) {
std::cerr << "ERROR: extreme increment " << extremeIncrement << " > " << m_increment * ratio << " (this should not happen)" << std::endl;
} else if (extremeIncrement < (m_increment * ratio) / 2) {
if (m_debugLevel > 0) {
std::cerr << "WARNING: extreme increment " << extremeIncrement << " < " << (m_increment * ratio) / 2 << std::endl;
}
acceptableSquashRange = false;
}
} else {
if (extremeIncrement > m_increment * ratio * 2) {
if (m_debugLevel > 0) {
std::cerr << "WARNING: extreme increment " << extremeIncrement << " > " << m_increment * ratio * 2 << std::endl;
}
acceptableSquashRange = false;
} else if (extremeIncrement < lrint(floor(m_increment * ratio))) {
std::cerr << "ERROR: extreme increment " << extremeIncrement << " < " << m_increment * ratio << " (I thought this couldn't happen?)" << std::endl;
}
}
if (!acceptableSquashRange) {
// Need to make maxDisplacement smaller as a proportion of
// the total displacement, yet ensure that the
// displacements still sum to the total.
adj += maxDf/10;
}
}
for (size_t i = 0; i < df.size(); ++i) {
double displacement = maxDf - df[i];
if (displacement < 0) displacement -= adj;
else displacement += adj;
if (i == 0 && phaseReset) {
if (df.size() == 1) {
increments.push_back(duration);
totalIncrement += duration;
} else {
increments.push_back(m_increment);
totalIncrement += m_increment;
}
totalDisplacement -= displacement;
continue;
}
double theoreticalAllotment = 0;
if (totalDisplacement != 0) {
theoreticalAllotment = (toAllot * displacement) / totalDisplacement;
}
int allotment = lrint(theoreticalAllotment);
if (i + 1 == df.size()) allotment = toAllot;
int increment = m_increment + allotment;
if (increment <= 0) {
// this is a serious problem, the allocation is quite
// wrong if it allows increment to diverge so far from the
// input increment
std::cerr << "*** WARNING: increment " << increment << " <= 0, rounding to zero" << std::endl;
increment = 0;
allotment = increment - m_increment;
}
increments.push_back(increment);
totalIncrement += increment;
toAllot -= allotment;
totalDisplacement -= displacement;
if (m_debugLevel > 2) {
std::cerr << "df " << df[i] << ", smoothed " << df[i] << ", disp " << displacement << ", allot " << theoreticalAllotment << ", incr " << increment << ", remain " << toAllot << std::endl;
}
}
if (m_debugLevel > 2) {
std::cerr << "total increment: " << totalIncrement << ", left over: " << toAllot << " to allot, displacement " << totalDisplacement << std::endl;
}
if (totalIncrement != duration) {
std::cerr << "*** WARNING: calculated output duration " << totalIncrement << " != expected " << duration << std::endl;
}
return increments;
}
void
StretchCalculator::calculateDisplacements(const std::vector<float> &df,
float &maxDf,
double &totalDisplacement,
double &maxDisplacement,
float adj) const
{
totalDisplacement = maxDisplacement = 0;
maxDf = 0;
for (size_t i = 0; i < df.size(); ++i) {
if (i == 0 || df[i] > maxDf) maxDf = df[i];
}
for (size_t i = 0; i < df.size(); ++i) {
double displacement = maxDf - df[i];
if (displacement < 0) displacement -= adj;
else displacement += adj;
totalDisplacement += displacement;
if (i == 0 || displacement > maxDisplacement) {
maxDisplacement = displacement;
}
}
}
}

View file

@ -1,95 +0,0 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band
An audio time-stretching and pitch-shifting library.
Copyright 2007 Chris Cannam.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#ifndef _RUBBERBAND_STRETCH_CALCULATOR_H_
#define _RUBBERBAND_STRETCH_CALCULATOR_H_
#include <sys/types.h>
#include <vector>
namespace RubberBand
{
class StretchCalculator
{
public:
StretchCalculator(size_t sampleRate, size_t inputIncrement, bool useHardPeaks);
virtual ~StretchCalculator();
/**
* Calculate phase increments for a region of audio, given the
* overall target stretch ratio, input duration in audio samples,
* and the audio curves to use for identifying phase lock points
* (lockAudioCurve) and for allocating stretches to relatively
* less prominent points (stretchAudioCurve).
*/
virtual std::vector<int> calculate(double ratio, size_t inputDuration,
const std::vector<float> &lockAudioCurve,
const std::vector<float> &stretchAudioCurve);
/**
* Calculate the phase increment for a single audio block, given
* the overall target stretch ratio and the block's value on the
* phase-lock audio curve. State is retained between calls in the
* StretchCalculator object; call reset() to reset it. This uses
* a less sophisticated method than the offline calculate().
*/
virtual int calculateSingle(double ratio, size_t inputDurationSoFar,
float curveValue);
void setUseHardPeaks(bool use) { m_useHardPeaks = use; }
void reset();
void setDebugLevel(int level) { m_debugLevel = level; }
struct Peak {
size_t chunk;
bool hard;
};
std::vector<Peak> getLastCalculatedPeaks() const { return m_lastPeaks; }
std::vector<float> smoothDF(const std::vector<float> &df);
protected:
std::vector<Peak> findPeaks(const std::vector<float> &audioCurve);
std::vector<int> distributeRegion(const std::vector<float> &regionCurve,
size_t outputDuration, float ratio,
bool phaseReset);
void calculateDisplacements(const std::vector<float> &df,
float &maxDf,
double &totalDisplacement,
double &maxDisplacement,
float adj) const;
size_t m_sampleRate;
size_t m_blockSize;
size_t m_increment;
float m_prevDf;
double m_divergence;
float m_recovery;
float m_prevRatio;
int m_transientAmnesty; // only in RT mode; handled differently offline
int m_debugLevel;
bool m_useHardPeaks;
std::vector<Peak> m_lastPeaks;
};
}
#endif

View file

@ -1,271 +0,0 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
#include "StretcherChannelData.h"
#include "Resampler.h"
namespace RubberBand
{
RubberBandStretcher::Impl::ChannelData::ChannelData(size_t windowSize,
size_t outbufSize)
{
std::set<size_t> s;
construct(s, windowSize, outbufSize);
}
RubberBandStretcher::Impl::ChannelData::ChannelData(const std::set<size_t> &windowSizes,
size_t initialWindowSize,
size_t outbufSize)
{
construct(windowSizes, initialWindowSize, outbufSize);
}
void
RubberBandStretcher::Impl::ChannelData::construct(const std::set<size_t> &windowSizes,
size_t initialWindowSize,
size_t outbufSize)
{
size_t maxSize = initialWindowSize;
if (!windowSizes.empty()) {
// std::set is ordered by value
std::set<size_t>::const_iterator i = windowSizes.end();
maxSize = *--i;
}
if (windowSizes.find(initialWindowSize) == windowSizes.end()) {
if (initialWindowSize > maxSize) maxSize = initialWindowSize;
}
size_t realSize = maxSize/2 + 1; // size of the real "half" of freq data
// std::cerr << "ChannelData::construct([" << windowSizes.size() << "], " << maxSize << ", " << outbufSize << ")" << std::endl;
if (outbufSize < maxSize) outbufSize = maxSize;
inbuf = new RingBuffer<float>(maxSize);
outbuf = new RingBuffer<float>(outbufSize);
mag = new double[realSize];
phase = new double[realSize];
prevPhase = new double[realSize];
unwrappedPhase = new double[realSize];
freqPeak = new size_t[realSize];
accumulator = new float[maxSize];
windowAccumulator = new float[maxSize];
fltbuf = new float[maxSize];
for (std::set<size_t>::const_iterator i = windowSizes.begin();
i != windowSizes.end(); ++i) {
ffts[*i] = new FFT(*i);
ffts[*i]->initDouble();
}
if (windowSizes.find(initialWindowSize) == windowSizes.end()) {
ffts[initialWindowSize] = new FFT(initialWindowSize);
ffts[initialWindowSize]->initDouble();
}
fft = ffts[initialWindowSize];
dblbuf = fft->getDoubleTimeBuffer();
resampler = 0;
resamplebuf = 0;
resamplebufSize = 0;
reset();
for (size_t i = 0; i < realSize; ++i) {
mag[i] = 0.0;
phase[i] = 0.0;
prevPhase[i] = 0.0;
unwrappedPhase[i] = 0.0;
freqPeak[i] = 0;
}
for (size_t i = 0; i < initialWindowSize; ++i) {
dblbuf[i] = 0.0;
}
for (size_t i = 0; i < maxSize; ++i) {
accumulator[i] = 0.f;
windowAccumulator[i] = 0.f;
fltbuf[i] = 0.0;
}
}
void
RubberBandStretcher::Impl::ChannelData::setWindowSize(size_t windowSize)
{
size_t oldSize = inbuf->getSize();
size_t realSize = windowSize/2 + 1;
// std::cerr << "ChannelData::setWindowSize(" << windowSize << ") [from " << oldSize << "]" << std::endl;
if (oldSize >= windowSize) {
// no need to reallocate buffers, just reselect fft
//!!! we can't actually do this without locking against the
//process thread, can we? we need to zero the mag/phase
//buffers without interference
if (ffts.find(windowSize) == ffts.end()) {
//!!! this also requires a lock, but it shouldn't occur in
//RT mode with proper initialisation
ffts[windowSize] = new FFT(windowSize);
ffts[windowSize]->initDouble();
}
fft = ffts[windowSize];
dblbuf = fft->getDoubleTimeBuffer();
for (size_t i = 0; i < windowSize; ++i) {
dblbuf[i] = 0.0;
}
for (size_t i = 0; i < realSize; ++i) {
mag[i] = 0.0;
phase[i] = 0.0;
prevPhase[i] = 0.0;
unwrappedPhase[i] = 0.0;
freqPeak[i] = 0;
}
return;
}
//!!! at this point we need a lock in case a different client
//thread is calling process() -- we need this lock even if we
//aren't running in threaded mode ourselves -- if we're in RT
//mode, then the process call should trylock and fail if the lock
//is unavailable (since this should never normally be the case in
//general use in RT mode)
RingBuffer<float> *newbuf = inbuf->resized(windowSize);
delete inbuf;
inbuf = newbuf;
// We don't want to preserve data in these arrays
delete[] mag;
delete[] phase;
delete[] prevPhase;
delete[] unwrappedPhase;
delete[] freqPeak;
mag = new double[realSize];
phase = new double[realSize];
prevPhase = new double[realSize];
unwrappedPhase = new double[realSize];
freqPeak = new size_t[realSize];
delete[] fltbuf;
fltbuf = new float[windowSize];
// But we do want to preserve data in these
float *newAcc = new float[windowSize];
for (size_t i = 0; i < oldSize; ++i) newAcc[i] = accumulator[i];
delete[] accumulator;
accumulator = newAcc;
newAcc = new float[windowSize];
for (size_t i = 0; i < oldSize; ++i) newAcc[i] = windowAccumulator[i];
delete[] windowAccumulator;
windowAccumulator = newAcc;
//!!! and resampler?
for (size_t i = 0; i < realSize; ++i) {
mag[i] = 0.0;
phase[i] = 0.0;
prevPhase[i] = 0.0;
unwrappedPhase[i] = 0.0;
freqPeak[i] = 0;
}
for (size_t i = 0; i < windowSize; ++i) {
fltbuf[i] = 0.0;
}
for (size_t i = oldSize; i < windowSize; ++i) {
accumulator[i] = 0.f;
windowAccumulator[i] = 0.f;
}
if (ffts.find(windowSize) == ffts.end()) {
ffts[windowSize] = new FFT(windowSize);
ffts[windowSize]->initDouble();
}
fft = ffts[windowSize];
dblbuf = fft->getDoubleTimeBuffer();
for (size_t i = 0; i < windowSize; ++i) {
dblbuf[i] = 0.0;
}
}
void
RubberBandStretcher::Impl::ChannelData::setOutbufSize(size_t outbufSize)
{
size_t oldSize = outbuf->getSize();
// std::cerr << "ChannelData::setOutbufSize(" << outbufSize << ") [from " << oldSize << "]" << std::endl;
if (oldSize < outbufSize) {
//!!! at this point we need a lock in case a different client
//thread is calling process()
RingBuffer<float> *newbuf = outbuf->resized(outbufSize);
delete outbuf;
outbuf = newbuf;
}
}
RubberBandStretcher::Impl::ChannelData::~ChannelData()
{
delete resampler;
delete[] resamplebuf;
delete inbuf;
delete outbuf;
delete[] mag;
delete[] phase;
delete[] prevPhase;
delete[] unwrappedPhase;
delete[] freqPeak;
delete[] accumulator;
delete[] windowAccumulator;
delete[] fltbuf;
for (std::map<size_t, FFT *>::iterator i = ffts.begin();
i != ffts.end(); ++i) {
delete i->second;
}
}
void
RubberBandStretcher::Impl::ChannelData::reset()
{
inbuf->reset();
outbuf->reset();
if (resampler) resampler->reset();
accumulatorFill = 0;
prevIncrement = 0;
chunkCount = 0;
inCount = 0;
inputSize = -1;
outCount = 0;
draining = false;
outputComplete = false;
}
}

View file

@ -1,121 +0,0 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band
An audio time-stretching and pitch-shifting library.
Copyright 2007 Chris Cannam.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#ifndef _RUBBERBAND_STRETCHERCHANNELDATA_H_
#define _RUBBERBAND_STRETCHERCHANNELDATA_H_
#include "StretcherImpl.h"
#include <set>
namespace RubberBand
{
class Resampler;
class RubberBandStretcher::Impl::ChannelData
{
public:
/**
* Construct a ChannelData structure.
*
* The window size passed in here is the size for the FFT
* calculation, and most of the buffer sizes also depend on
* it. In practice it is always a power of two and except for
* very extreme stretches is always either 1024, 2048 or 4096.
*
* The outbuf size depends on other factors as well, including
* the pitch scale factor and any maximum processing block
* size specified by the user of the code.
*/
ChannelData(size_t windowSize, size_t outbufSize);
/**
* Construct a ChannelData structure that can process at
* different FFT sizes without requiring reallocation when the
* size changes. The size can subsequently be changed with a
* call to setWindowSize. Reallocation will only be necessary
* if setWindowSize is called with a value not equal to one of
* those passed in to the constructor.
*
* The outbufSize should be the maximum possible outbufSize to
* avoid reallocation, which will happen if setOutbufSize is
* called subsequently.
*/
ChannelData(const std::set<size_t> &windowSizes,
size_t initialWindowSize, size_t outbufSize);
~ChannelData();
/**
* Reset buffers
*/
void reset();
/**
* Set the FFT and buffer sizes from the given processing
* window size. If this ChannelData was constructed with a set
* of window sizes and the given window size here was among
* them, no reallocation will be required.
*/
void setWindowSize(size_t windowSize);
/**
* Set the outbufSize for the channel data. Reallocation will
* occur.
*/
void setOutbufSize(size_t outbufSize);
RingBuffer<float> *inbuf;
RingBuffer<float> *outbuf;
double *mag;
double *phase;
double *prevPhase;
double *unwrappedPhase;
size_t *freqPeak;
float *accumulator;
size_t accumulatorFill;
float *windowAccumulator;
float *fltbuf;
double *dblbuf; // owned by FFT object, only used for time domain FFT i/o
size_t prevIncrement; // only used in RT mode
size_t chunkCount;
size_t inCount;
long inputSize; // set only after known (when data ended); -1 previously
size_t outCount;
bool draining;
bool outputComplete;
FFT *fft;
std::map<size_t, FFT *> ffts;
Resampler *resampler;
float *resamplebuf;
size_t resamplebufSize;
private:
void construct(const std::set<size_t> &windowSizes,
size_t initialWindowSize, size_t outbufSize);
};
}
#endif

File diff suppressed because it is too large Load diff

View file

@ -1,194 +0,0 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band
An audio time-stretching and pitch-shifting library.
Copyright 2007 Chris Cannam.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#ifndef _RUBBERBAND_STRETCHERIMPL_H_
#define _RUBBERBAND_STRETCHERIMPL_H_
#include "RubberBandStretcher.h"
#include "Window.h"
#include "Thread.h"
#include "RingBuffer.h"
#include "FFT.h"
#include "sysutils.h"
#include <set>
namespace RubberBand
{
class AudioCurve;
class StretchCalculator;
class RubberBandStretcher::Impl
{
public:
Impl(RubberBandStretcher *stretcher,
size_t sampleRate, size_t channels, Options options,
double initialTimeRatio, double initialPitchScale);
~Impl();
void reset();
void setTimeRatio(double ratio);
void setPitchScale(double scale);
double getTimeRatio() const;
double getPitchScale() const;
size_t getLatency() const;
void setTransientsOption(Options);
void setPhaseOption(Options);
void setExpectedInputDuration(size_t samples);
void setMaxProcessSize(size_t samples);
size_t getSamplesRequired() const;
void study(const float *const *input, size_t samples, bool final);
void process(const float *const *input, size_t samples, bool final);
int available() const;
size_t retrieve(float *const *output, size_t samples) const;
float getFrequencyCutoff(int n) const;
void setFrequencyCutoff(int n, float f);
size_t getInputIncrement() const {
return m_increment;
}
std::vector<int> getOutputIncrements() const;
std::vector<float> getPhaseResetCurve() const;
std::vector<int> getExactTimePoints() const;
size_t getChannelCount() const {
return m_channels;
}
void calculateStretch();
void setDebugLevel(int level);
static void setDefaultDebugLevel(int level) { m_defaultDebugLevel = level; }
protected:
RubberBandStretcher *m_stretcher;
size_t m_channels;
size_t consumeChannel(size_t channel, const float *input, size_t samples);
void processChunks(size_t channel, bool &any, bool &last);
bool processOneChunk(); // across all channels, for real time use
bool processChunkForChannel(size_t channel, size_t phaseIncrement,
size_t shiftIncrement, bool phaseReset);
bool testInbufReadSpace(size_t channel);
void calculateIncrements(size_t &phaseIncrement,
size_t &shiftIncrement, bool &phaseReset);
bool getIncrements(size_t channel, size_t &phaseIncrement,
size_t &shiftIncrement, bool &phaseReset);
void analyseChunk(size_t channel);
void modifyChunk(size_t channel, size_t outputIncrement, bool phaseReset);
void synthesiseChunk(size_t channel);
void writeChunk(size_t channel, size_t shiftIncrement, bool last);
void calculateSizes();
void configure();
void reconfigure();
double getEffectiveRatio() const;
size_t roundUp(size_t value); // to next power of two
double m_timeRatio;
double m_pitchScale;
size_t m_windowSize;
size_t m_increment;
size_t m_outbufSize;
size_t m_maxProcessSize;
size_t m_expectedInputDuration;
bool m_threaded;
bool m_realtime;
Options m_options;
int m_debugLevel;
enum ProcessMode {
JustCreated,
Studying,
Processing,
Finished
};
ProcessMode m_mode;
std::map<size_t, Window<float> *> m_windows;
Window<float> *m_window;
FFT *m_studyFFT;
Condition m_spaceAvailable;
class ProcessThread : public Thread
{
public:
ProcessThread(Impl *s, size_t c);
void run();
void signalDataAvailable();
void abandon();
private:
Impl *m_s;
size_t m_channel;
Condition m_dataAvailable;
bool m_abandoning;
};
mutable Mutex m_threadSetMutex;
typedef std::set<ProcessThread *> ThreadSet;
ThreadSet m_threadSet;
size_t m_inputDuration;
std::vector<float> m_phaseResetDf;
std::vector<float> m_stretchDf;
class ChannelData;
std::vector<ChannelData *> m_channelData;
std::vector<int> m_outputIncrements;
mutable RingBuffer<int> m_lastProcessOutputIncrements;
mutable RingBuffer<float> m_lastProcessPhaseResetDf;
AudioCurve *m_phaseResetAudioCurve;
AudioCurve *m_stretchAudioCurve;
StretchCalculator *m_stretchCalculator;
float m_freq0;
float m_freq1;
float m_freq2;
size_t m_baseWindowSize;
float m_rateMultiple;
void writeOutput(RingBuffer<float> &to, float *from,
size_t qty, size_t &outCount, size_t theoreticalOut);
static int m_defaultDebugLevel;
static const size_t m_defaultIncrement;
static const size_t m_defaultWindowSize;
};
}
#endif

View file

@ -1,927 +0,0 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band
An audio time-stretching and pitch-shifting library.
Copyright 2007 Chris Cannam.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#include "StretcherImpl.h"
#include "PercussiveAudioCurve.h"
#include "HighFrequencyAudioCurve.h"
#include "ConstantAudioCurve.h"
#include "StretchCalculator.h"
#include "StretcherChannelData.h"
#include "Resampler.h"
#include <cassert>
#include <cmath>
#include <set>
#include <map>
using std::cerr;
using std::endl;
namespace RubberBand {
RubberBandStretcher::Impl::ProcessThread::ProcessThread(Impl *s, size_t c) :
m_s(s),
m_channel(c),
m_dataAvailable(std::string("data ") + char('A' + c)),
m_abandoning(false)
{ }
void
RubberBandStretcher::Impl::ProcessThread::run()
{
if (m_s->m_debugLevel > 1) {
cerr << "thread " << m_channel << " getting going" << endl;
}
ChannelData &cd = *m_s->m_channelData[m_channel];
while (cd.inputSize == -1 ||
cd.inbuf->getReadSpace() > 0) {
// if (cd.inputSize != -1) {
// cerr << "inputSize == " << cd.inputSize
// << ", readSpace == " << cd.inbuf->getReadSpace() << endl;
// }
bool any = false, last = false;
m_s->processChunks(m_channel, any, last);
if (last) break;
if (any) m_s->m_spaceAvailable.signal();
m_dataAvailable.lock();
if (!m_s->testInbufReadSpace(m_channel) && !m_abandoning) {
m_dataAvailable.wait();
} else {
m_dataAvailable.unlock();
}
if (m_abandoning) {
if (m_s->m_debugLevel > 1) {
cerr << "thread " << m_channel << " abandoning" << endl;
}
return;
}
}
bool any = false, last = false;
m_s->processChunks(m_channel, any, last);
m_s->m_spaceAvailable.signal();
if (m_s->m_debugLevel > 1) {
cerr << "thread " << m_channel << " done" << endl;
}
}
void
RubberBandStretcher::Impl::ProcessThread::signalDataAvailable()
{
m_dataAvailable.signal();
}
void
RubberBandStretcher::Impl::ProcessThread::abandon()
{
m_abandoning = true;
}
void
RubberBandStretcher::Impl::processChunks(size_t c, bool &any, bool &last)
{
// Process as many chunks as there are available on the input
// buffer for channel c. This requires that the increments have
// already been calculated.
ChannelData &cd = *m_channelData[c];
last = false;
any = false;
while (!last) {
if (!testInbufReadSpace(c)) {
// cerr << "not enough input" << endl;
break;
}
any = true;
if (!cd.draining) {
size_t got = cd.inbuf->peek(cd.fltbuf, m_windowSize);
assert(got == m_windowSize || cd.inputSize >= 0);
cd.inbuf->skip(m_increment);
analyseChunk(c);
}
bool phaseReset = false;
size_t phaseIncrement, shiftIncrement;
getIncrements(c, phaseIncrement, shiftIncrement, phaseReset);
last = processChunkForChannel(c, phaseIncrement, shiftIncrement, phaseReset);
cd.chunkCount++;
if (m_debugLevel > 2) {
cerr << "channel " << c << ": last = " << last << ", chunkCount = " << cd.chunkCount << endl;
}
}
}
bool
RubberBandStretcher::Impl::processOneChunk()
{
// Process a single chunk for all channels, provided there is
// enough data on each channel for at least one chunk. This is
// able to calculate increments as it goes along.
for (size_t c = 0; c < m_channels; ++c) {
if (!testInbufReadSpace(c)) return false;
ChannelData &cd = *m_channelData[c];
if (!cd.draining) {
size_t got = cd.inbuf->peek(cd.fltbuf, m_windowSize);
assert(got == m_windowSize || cd.inputSize >= 0);
cd.inbuf->skip(m_increment);
analyseChunk(c);
}
}
bool phaseReset = false;
size_t phaseIncrement, shiftIncrement;
if (!getIncrements(0, phaseIncrement, shiftIncrement, phaseReset)) {
calculateIncrements(phaseIncrement, shiftIncrement, phaseReset);
}
bool last = false;
for (size_t c = 0; c < m_channels; ++c) {
last = processChunkForChannel(c, phaseIncrement, shiftIncrement, phaseReset);
m_channelData[c]->chunkCount++;
}
return last;
}
bool
RubberBandStretcher::Impl::testInbufReadSpace(size_t c)
{
ChannelData &cd = *m_channelData[c];
RingBuffer<float> &inbuf = *cd.inbuf;
size_t rs = inbuf.getReadSpace();
if (rs < m_windowSize && !cd.draining) {
if (cd.inputSize == -1) {
// Not all the input data has been written to the inbuf
// (that's why the input size is not yet set). We can't
// process, because we don't have a full chunk of data, so
// our process chunk would contain some empty padding in
// its input -- and that would give incorrect output, as
// we know there is more input to come.
if (!m_threaded) {
// cerr << "WARNING: RubberBandStretcher: read space < chunk size ("
// << inbuf.getReadSpace() << " < " << m_windowSize
// << ") when not all input written, on processChunks for channel " << c << endl;
}
return false;
}
if (rs == 0) {
if (m_debugLevel > 1) {
cerr << "read space = 0, giving up" << endl;
}
return false;
} else if (rs < m_windowSize/2) {
if (m_debugLevel > 1) {
cerr << "read space = " << rs << ", setting draining true" << endl;
}
cd.draining = true;
}
}
return true;
}
bool
RubberBandStretcher::Impl::processChunkForChannel(size_t c,
size_t phaseIncrement,
size_t shiftIncrement,
bool phaseReset)
{
// Process a single chunk on a single channel. This assumes
// enough input data is available; caller must have tested this
// using e.g. testInbufReadSpace first. Return true if this is
// the last chunk on the channel.
if (phaseReset && (m_debugLevel > 1)) {
cerr << "processChunkForChannel: phase reset found, incrs "
<< phaseIncrement << ":" << shiftIncrement << endl;
}
ChannelData &cd = *m_channelData[c];
if (!cd.draining) {
// This is the normal processing case -- draining is only
// set when all the input has been used and we only need
// to write from the existing accumulator into the output.
// We know we have enough samples available in m_inbuf --
// this is usually m_windowSize, but we know that if fewer
// are available, it's OK to use zeroes for the rest
// (which the ring buffer will provide) because we've
// reached the true end of the data.
// We need to peek m_windowSize samples for processing, and
// then skip m_increment to advance the read pointer.
modifyChunk(c, phaseIncrement, phaseReset);
synthesiseChunk(c); // reads from cd.mag, cd.phase
if (m_debugLevel > 2) {
if (phaseReset) {
for (int i = 0; i < 10; ++i) {
cd.accumulator[i] = 1.2f - (i % 3) * 1.2f;
}
}
}
}
bool last = false;
if (cd.draining) {
if (m_debugLevel > 1) {
cerr << "draining: accumulator fill = " << cd.accumulatorFill << " (shiftIncrement = " << shiftIncrement << ")" << endl;
}
if (shiftIncrement == 0) {
cerr << "WARNING: draining: shiftIncrement == 0, can't handle that in this context: setting to " << m_increment << endl;
shiftIncrement = m_increment;
}
if (cd.accumulatorFill <= shiftIncrement) {
if (m_debugLevel > 1) {
cerr << "reducing shift increment from " << shiftIncrement
<< " to " << cd.accumulatorFill
<< " and marking as last" << endl;
}
shiftIncrement = cd.accumulatorFill;
last = true;
}
}
if (m_threaded) {
size_t required = shiftIncrement;
if (m_pitchScale != 1.0) {
required = int(required / m_pitchScale) + 1;
}
if (cd.outbuf->getWriteSpace() < required) {
if (m_debugLevel > 0) {
cerr << "Buffer overrun on output for channel " << c << endl;
}
//!!! The only correct thing we can do here is resize the
// buffer. We can't wait for the client thread to read
// some data out from the buffer so as to make more space,
// because the client thread is probably stuck in a
// process() call waiting for us to stow away enough input
// increments to allow the process() call to complete.
}
}
writeChunk(c, shiftIncrement, last);
return last;
}
void
RubberBandStretcher::Impl::calculateIncrements(size_t &phaseIncrementRtn,
size_t &shiftIncrementRtn,
bool &phaseReset)
{
// cerr << "calculateIncrements" << endl;
// Calculate the next upcoming phase and shift increment, on the
// basis that both channels are in sync. This is in contrast to
// getIncrements, which requires that all the increments have been
// calculated in advance but can then return increments
// corresponding to different chunks in different channels.
// Requires frequency domain representations of channel data in
// the mag and phase buffers in the channel.
// This function is only used in real-time mode.
phaseIncrementRtn = m_increment;
shiftIncrementRtn = m_increment;
phaseReset = false;
if (m_channels == 0) return;
ChannelData &cd = *m_channelData[0];
size_t bc = cd.chunkCount;
for (size_t c = 1; c < m_channels; ++c) {
if (m_channelData[c]->chunkCount != bc) {
cerr << "ERROR: RubberBandStretcher::Impl::calculateIncrements: Channels are not in sync" << endl;
return;
}
}
// Normally we would mix down the time-domain signal and apply a
// single FFT, or else mix down the Cartesian form of the
// frequency-domain signal. Both of those would be inefficient
// from this position. Fortunately, the onset detectors should
// work reasonably well (maybe even better?) if we just sum the
// magnitudes of the frequency-domain channel signals and forget
// about phase entirely. Normally we don't expect the channel
// phases to cancel each other, and broadband effects will still
// be apparent.
for (size_t i = 0; i <= m_windowSize/2; ++i) {
cd.fltbuf[i] = 0.0;
}
for (size_t c = 0; c < m_channels; ++c) {
for (size_t i = 0; i <= m_windowSize/2; ++i) {
cd.fltbuf[i] += m_channelData[c]->mag[i];
}
}
float df = m_phaseResetAudioCurve->process(cd.fltbuf, m_increment);
int incr = m_stretchCalculator->calculateSingle
(getEffectiveRatio(),
m_inputDuration, //!!! no, totally wrong... fortunately it doesn't matter atm
df);
m_lastProcessPhaseResetDf.write(&df, 1);
m_lastProcessOutputIncrements.write(&incr, 1);
if (incr < 0) {
phaseReset = true;
incr = -incr;
}
// The returned increment is the phase increment. The shift
// increment for one chunk is the same as the phase increment for
// the following chunk (see comment below). This means we don't
// actually know the shift increment until we see the following
// phase increment... which is a bit of a problem.
// This implies we should use this increment for the shift
// increment, and make the following phase increment the same as
// it. This means in RT mode we'll be one chunk later with our
// phase reset than we would be in non-RT mode. The sensitivity
// of the broadband onset detector may mean that this isn't a
// problem -- test it and see.
shiftIncrementRtn = incr;
if (cd.prevIncrement == 0) {
phaseIncrementRtn = shiftIncrementRtn;
} else {
phaseIncrementRtn = cd.prevIncrement;
}
cd.prevIncrement = shiftIncrementRtn;
}
bool
RubberBandStretcher::Impl::getIncrements(size_t channel,
size_t &phaseIncrementRtn,
size_t &shiftIncrementRtn,
bool &phaseReset)
{
if (channel >= m_channels) {
phaseIncrementRtn = m_increment;
shiftIncrementRtn = m_increment;
phaseReset = false;
return false;
}
// There are two relevant output increments here. The first is
// the phase increment which we use when recalculating the phases
// for the current chunk; the second is the shift increment used
// to determine how far to shift the processing buffer after
// writing the chunk. The shift increment for one chunk is the
// same as the phase increment for the following chunk.
// When an onset occurs for which we need to reset phases, the
// increment given will be negative.
// When we reset phases, the previous shift increment (and so
// current phase increments) must have been m_increment to ensure
// consistency.
// m_outputIncrements stores phase increments.
ChannelData &cd = *m_channelData[channel];
bool gotData = true;
if (cd.chunkCount >= m_outputIncrements.size()) {
// cerr << "WARNING: RubberBandStretcher::Impl::getIncrements:"
// << " chunk count " << cd.chunkCount << " >= "
// << m_outputIncrements.size() << endl;
if (m_outputIncrements.size() == 0) {
phaseIncrementRtn = m_increment;
shiftIncrementRtn = m_increment;
phaseReset = false;
return false;
} else {
cd.chunkCount = m_outputIncrements.size()-1;
gotData = false;
}
}
int phaseIncrement = m_outputIncrements[cd.chunkCount];
int shiftIncrement = phaseIncrement;
if (cd.chunkCount + 1 < m_outputIncrements.size()) {
shiftIncrement = m_outputIncrements[cd.chunkCount + 1];
}
if (phaseIncrement < 0) {
phaseIncrement = -phaseIncrement;
phaseReset = true;
}
if (shiftIncrement < 0) {
shiftIncrement = -shiftIncrement;
}
if (shiftIncrement >= int(m_windowSize)) {
cerr << "*** ERROR: RubberBandStretcher::Impl::processChunks: shiftIncrement " << shiftIncrement << " >= windowSize " << m_windowSize << " at " << cd.chunkCount << " (of " << m_outputIncrements.size() << ")" << endl;
shiftIncrement = m_windowSize;
}
phaseIncrementRtn = phaseIncrement;
shiftIncrementRtn = shiftIncrement;
if (cd.chunkCount == 0) phaseReset = true; // don't mess with the first chunk
return gotData;
}
void
RubberBandStretcher::Impl::analyseChunk(size_t channel)
{
size_t i;
ChannelData &cd = *m_channelData[channel];
// cd.fltbuf is known to contain m_windowSize samples
m_window->cut(cd.fltbuf);
for (i = 0; i < m_windowSize/2; ++i) {
cd.dblbuf[i] = cd.fltbuf[i + m_windowSize/2];
cd.dblbuf[i + m_windowSize/2] = cd.fltbuf[i];
}
cd.fft->forwardPolar(cd.dblbuf, cd.mag, cd.phase);
}
double mod(double x, double y) { return x - (y * floor(x / y)); }
double princarg(double a) { return mod(a + M_PI, -2 * M_PI) + M_PI; }
void
RubberBandStretcher::Impl::modifyChunk(size_t channel, size_t outputIncrement,
bool phaseReset)
{
ChannelData &cd = *m_channelData[channel];
if (phaseReset && m_debugLevel > 1) {
cerr << "phase reset: leaving phases unmodified" << endl;
}
size_t count = m_windowSize/2;
size_t pfp = 0;
double rate = m_stretcher->m_sampleRate;
if (!(m_options & OptionPhaseIndependent)) {
cd.freqPeak[0] = 0;
float freq0 = m_freq0;
float freq1 = m_freq1;
float freq2 = m_freq2;
// As the stretch ratio increases, so the frequency thresholds
// for phase lamination should increase. Beyond a ratio of
// about 1.5, the threshold should be about 1200Hz; beyond a
// ratio of 2, we probably want no lamination to happen at all
// by default. This calculation aims for more or less that.
// We only do this if the phase option is OptionPhaseAdaptive
// (the default), i.e. not Independent or PeakLocked.
if (!(m_options & OptionPhasePeakLocked)) {
float r = getEffectiveRatio();
if (r > 1) {
float rf0 = 600 + (600 * ((r-1)*(r-1)*(r-1)*2));
float f1ratio = freq1 / freq0;
float f2ratio = freq2 / freq0;
freq0 = std::max(freq0, rf0);
freq1 = freq0 * f1ratio;
freq2 = freq0 * f2ratio;
}
}
size_t limit0 = lrint((freq0 * m_windowSize) / rate);
size_t limit1 = lrint((freq1 * m_windowSize) / rate);
size_t limit2 = lrint((freq2 * m_windowSize) / rate);
size_t range = 0;
if (limit1 < limit0) limit1 = limit0;
if (limit2 < limit1) limit2 = limit1;
// cerr << "limit0 = " << limit0 << " limit1 = " << limit1 << " limit2 = " << limit2 << endl;
int peakCount = 0;
for (size_t i = 0; i <= count; ++i) {
double mag = cd.mag[i];
bool isPeak = true;
for (size_t j = 1; j <= range; ++j) {
if (mag < cd.mag[i-j]) {
isPeak = false;
break;
}
if (mag < cd.mag[i+j]) {
isPeak = false;
break;
}
}
if (isPeak) {
// i is a peak bin.
// The previous peak bin was at pfp; make freqPeak entries
// from pfp to half-way between pfp and i point at pfp, and
// those from the half-way mark to i point at i.
size_t halfway = (pfp + i) / 2;
if (halfway == pfp) halfway = pfp + 1;
for (size_t j = pfp + 1; j < halfway; ++j) {
cd.freqPeak[j] = pfp;
}
for (size_t j = halfway; j <= i; ++j) {
cd.freqPeak[j] = i;
}
pfp = i;
++peakCount;
}
if (i == limit0) range = 1;
if (i == limit1) range = 2;
if (i >= limit2) {
range = 3;
if (i + range + 1 > count) range = count - i;
}
}
// cerr << "peakCount = " << peakCount << endl;
cd.freqPeak[count-1] = count-1;
cd.freqPeak[count] = count;
}
double peakInPhase = 0.0;
double peakOutPhase = 0.0;
size_t p, pp;
for (size_t i = 0; i <= count; ++i) {
if (m_options & OptionPhaseIndependent) {
p = i;
pp = i-1;
} else {
p = cd.freqPeak[i];
pp = cd.freqPeak[i-1];
}
bool resetThis = phaseReset;
if (m_options & OptionTransientsMixed) {
size_t low = lrint((150 * m_windowSize) / rate);
size_t high = lrint((1000 * m_windowSize) / rate);
if (resetThis) {
if (i > low && i < high) resetThis = false;
}
}
if (!resetThis) {
if (i == 0 || p != pp) {
double omega = (2 * M_PI * m_increment * p) / m_windowSize;
double expectedPhase = cd.prevPhase[p] + omega;
double phaseError = princarg(cd.phase[p] - expectedPhase);
double phaseIncrement = (omega + phaseError) / m_increment;
double unwrappedPhase = cd.unwrappedPhase[p] +
outputIncrement * phaseIncrement;
cd.prevPhase[p] = cd.phase[p];
cd.phase[p] = unwrappedPhase;
cd.unwrappedPhase[p] = unwrappedPhase;
peakInPhase = cd.prevPhase[p];
peakOutPhase = unwrappedPhase;
}
if (i != p) {
double diffToPeak = peakInPhase - cd.phase[i];
double unwrappedPhase = peakOutPhase - diffToPeak;
cd.prevPhase[i] = cd.phase[i];
cd.phase[i] = unwrappedPhase;
cd.unwrappedPhase[i] = unwrappedPhase;
}
} else {
cd.prevPhase[i] = cd.phase[i];
cd.unwrappedPhase[i] = cd.phase[i];
}
}
}
void
RubberBandStretcher::Impl::synthesiseChunk(size_t channel)
{
ChannelData &cd = *m_channelData[channel];
cd.fft->inversePolar(cd.mag, cd.phase, cd.dblbuf);
for (size_t i = 0; i < m_windowSize/2; ++i) {
cd.fltbuf[i] = cd.dblbuf[i + m_windowSize/2];
cd.fltbuf[i + m_windowSize/2] = cd.dblbuf[i];
}
// our ffts produced unscaled results
for (size_t i = 0; i < m_windowSize; ++i) {
cd.fltbuf[i] = cd.fltbuf[i] / m_windowSize;
}
m_window->cut(cd.fltbuf);
for (size_t i = 0; i < m_windowSize; ++i) {
cd.accumulator[i] += cd.fltbuf[i];
}
cd.accumulatorFill = m_windowSize;
float fixed = m_window->getArea() * 1.5;
for (size_t i = 0; i < m_windowSize; ++i) {
float val = m_window->getValue(i);
cd.windowAccumulator[i] += val * fixed;
}
}
void
RubberBandStretcher::Impl::writeChunk(size_t channel, size_t shiftIncrement, bool last)
{
ChannelData &cd = *m_channelData[channel];
if (m_debugLevel > 2) {
cerr << "writeChunk(" << channel << ", " << shiftIncrement << ", " << last << ")" << endl;
}
for (int i = 0; i < shiftIncrement; ++i) {
if (cd.windowAccumulator[i] > 0.f) {
cd.accumulator[i] /= cd.windowAccumulator[i];
}
}
// for exact sample scaling (probably not meaningful if we
// were running in RT mode)
size_t theoreticalOut = 0;
if (cd.inputSize >= 0) {
theoreticalOut = lrint(cd.inputSize * m_timeRatio);
}
if (m_pitchScale != 1.0 && cd.resampler) {
size_t reqSize = int(ceil(shiftIncrement / m_pitchScale));
if (reqSize > cd.resamplebufSize) {
// This shouldn't normally happen -- the buffer is
// supposed to be initialised with enough space in the
// first place. But we retain this check in case the
// pitch scale has changed since then, or the stretch
// calculator has gone mad, or something.
cerr << "WARNING: RubberBandStretcher::Impl::writeChunk: resizing resampler buffer from "
<< cd.resamplebufSize << " to " << reqSize << endl;
cd.resamplebufSize = reqSize;
if (cd.resamplebuf) delete[] cd.resamplebuf;
cd.resamplebuf = new float[cd.resamplebufSize];
}
size_t outframes = cd.resampler->resample(&cd.accumulator,
&cd.resamplebuf,
shiftIncrement,
1.0 / m_pitchScale,
last);
writeOutput(*cd.outbuf, cd.resamplebuf,
outframes, cd.outCount, theoreticalOut);
} else {
writeOutput(*cd.outbuf, cd.accumulator,
shiftIncrement, cd.outCount, theoreticalOut);
}
for (size_t i = 0; i < m_windowSize - shiftIncrement; ++i) {
cd.accumulator[i] = cd.accumulator[i + shiftIncrement];
}
for (size_t i = m_windowSize - shiftIncrement; i < m_windowSize; ++i) {
cd.accumulator[i] = 0.0f;
}
for (size_t i = 0; i < m_windowSize - shiftIncrement; ++i) {
cd.windowAccumulator[i] = cd.windowAccumulator[i + shiftIncrement];
}
for (size_t i = m_windowSize - shiftIncrement; i < m_windowSize; ++i) {
cd.windowAccumulator[i] = 0.0f;
}
if (cd.accumulatorFill > shiftIncrement) {
cd.accumulatorFill -= shiftIncrement;
} else {
cd.accumulatorFill = 0;
if (cd.draining) {
if (m_debugLevel > 1) {
cerr << "RubberBandStretcher::Impl::processChunks: setting outputComplete to true" << endl;
}
cd.outputComplete = true;
}
}
}
void
RubberBandStretcher::Impl::writeOutput(RingBuffer<float> &to, float *from, size_t qty, size_t &outCount, size_t theoreticalOut)
{
// In non-RT mode, we don't want to write the first startSkip
// samples, because the first chunk is centred on the start of the
// output. In RT mode we didn't apply any pre-padding in
// configure(), so we don't want to remove any here.
size_t startSkip = 0;
if (!m_realtime) {
startSkip = lrintf((m_windowSize/2) / m_pitchScale);
}
if (outCount > startSkip) {
// this is the normal case
if (theoreticalOut > 0) {
if (m_debugLevel > 1) {
cerr << "theoreticalOut = " << theoreticalOut
<< ", outCount = " << outCount
<< ", startSkip = " << startSkip
<< ", qty = " << qty << endl;
}
if (outCount - startSkip <= theoreticalOut &&
outCount - startSkip + qty > theoreticalOut) {
qty = theoreticalOut - (outCount - startSkip);
if (m_debugLevel > 1) {
cerr << "reduce qty to " << qty << endl;
}
}
}
if (m_debugLevel > 2) {
cerr << "writing " << qty << endl;
}
size_t written = to.write(from, qty);
if (written < qty) {
cerr << "WARNING: RubberBandStretcher::Impl::writeOutput: "
<< "Buffer overrun on output: wrote " << written
<< " of " << qty << " samples" << endl;
}
outCount += written;
return;
}
// the rest of this is only used during the first startSkip samples
if (outCount + qty <= startSkip) {
if (m_debugLevel > 1) {
cerr << "qty = " << qty << ", startSkip = "
<< startSkip << ", outCount = " << outCount
<< ", discarding" << endl;
}
outCount += qty;
return;
}
size_t off = startSkip - outCount;
if (m_debugLevel > 1) {
cerr << "qty = " << qty << ", startSkip = "
<< startSkip << ", outCount = " << outCount
<< ", writing " << qty - off
<< " from start offset " << off << endl;
}
to.write(from + off, qty - off);
outCount += qty;
}
int
RubberBandStretcher::Impl::available() const
{
if (m_threaded) {
MutexLocker locker(&m_threadSetMutex);
if (m_channelData.empty()) return 0;
} else {
if (m_channelData.empty()) return 0;
}
if (!m_threaded) {
for (size_t c = 0; c < m_channels; ++c) {
if (m_channelData[c]->inputSize >= 0) {
// cerr << "available: m_done true" << endl;
if (m_channelData[c]->inbuf->getReadSpace() > 0) {
// cerr << "calling processChunks(" << c << ") from available" << endl;
//!!! do we ever actually do this? if so, this method should not be const
// ^^^ yes, we do sometimes -- e.g. when fed a very short file
bool any = false, last = false;
((RubberBandStretcher::Impl *)this)->processChunks(c, any, last);
}
}
}
}
size_t min = 0;
bool consumed = true;
bool haveResamplers = false;
for (size_t i = 0; i < m_channels; ++i) {
size_t availIn = m_channelData[i]->inbuf->getReadSpace();
size_t availOut = m_channelData[i]->outbuf->getReadSpace();
if (m_debugLevel > 2) {
cerr << "available on channel " << i << ": " << availOut << " (waiting: " << availIn << ")" << endl;
}
if (i == 0 || availOut < min) min = availOut;
if (!m_channelData[i]->outputComplete) consumed = false;
if (m_channelData[i]->resampler) haveResamplers = true;
}
if (min == 0 && consumed) return -1;
if (m_pitchScale == 1.0) return min;
if (haveResamplers) return min; // resampling has already happened
return int(floor(min / m_pitchScale));
}
size_t
RubberBandStretcher::Impl::retrieve(float *const *output, size_t samples) const
{
size_t got = samples;
for (size_t c = 0; c < m_channels; ++c) {
size_t gotHere = m_channelData[c]->outbuf->read(output[c], got);
if (gotHere < got) {
if (c > 0) {
if (m_debugLevel > 0) {
cerr << "RubberBandStretcher::Impl::retrieve: WARNING: channel imbalance detected" << endl;
}
}
got = gotHere;
}
}
return got;
}
}

View file

@ -1,530 +0,0 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band
An audio time-stretching and pitch-shifting library.
Copyright 2007 Chris Cannam.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#include "Thread.h"
#include <iostream>
#include <sys/time.h>
#include <time.h>
//#define DEBUG_THREAD 1
//#define DEBUG_MUTEX 1
//#define DEBUG_CONDITION 1
using std::cerr;
using std::endl;
using std::string;
namespace RubberBand
{
#ifdef _WIN32
Thread::Thread() :
m_id(0),
m_extant(false)
{
#ifdef DEBUG_THREAD
cerr << "THREAD DEBUG: Created thread object " << this << endl;
#endif
}
Thread::~Thread()
{
#ifdef DEBUG_THREAD
cerr << "THREAD DEBUG: Destroying thread object " << this << ", id " << m_id << endl;
#endif
if (m_extant) {
WaitForSingleObject(m_id, INFINITE);
}
#ifdef DEBUG_THREAD
cerr << "THREAD DEBUG: Destroyed thread object " << this << endl;
#endif
}
void
Thread::start()
{
m_id = CreateThread(NULL, 0, staticRun, this, 0, 0);
if (!m_id) {
cerr << "ERROR: thread creation failed" << endl;
exit(1);
} else {
#ifdef DEBUG_THREAD
cerr << "THREAD DEBUG: Created thread " << m_id << " for thread object " << this << endl;
#endif
m_extant = true;
}
}
void
Thread::wait()
{
if (m_extant) {
#ifdef DEBUG_THREAD
cerr << "THREAD DEBUG: Waiting on thread " << m_id << " for thread object " << this << endl;
#endif
WaitForSingleObject(m_id, INFINITE);
#ifdef DEBUG_THREAD
cerr << "THREAD DEBUG: Waited on thread " << m_id << " for thread object " << this << endl;
#endif
m_extant = false;
}
}
Thread::Id
Thread::id()
{
return m_id;
}
bool
Thread::threadingAvailable()
{
return true;
}
DWORD
Thread::staticRun(LPVOID arg)
{
Thread *thread = static_cast<Thread *>(arg);
#ifdef DEBUG_THREAD
cerr << "THREAD DEBUG: " << (void *)GetCurrentThreadId() << ": Running thread " << thread->m_id << " for thread object " << thread << endl;
#endif
thread->run();
return 0;
}
Mutex::Mutex() :
m_locked(false)
{
m_mutex = CreateMutex(NULL, FALSE, NULL);
#ifdef DEBUG_MUTEX
cerr << "MUTEX DEBUG: " << (void *)GetCurrentThreadId() << ": Initialised mutex " << &m_mutex << endl;
#endif
}
Mutex::~Mutex()
{
#ifdef DEBUG_MUTEX
cerr << "MUTEX DEBUG: " << (void *)GetCurrentThreadId() << ": Destroying mutex " << &m_mutex << endl;
#endif
CloseHandle(m_mutex);
}
void
Mutex::lock()
{
if (m_locked) {
cerr << "ERROR: Deadlock on mutex " << &m_mutex << endl;
}
#ifdef DEBUG_MUTEX
cerr << "MUTEX DEBUG: " << (void *)GetCurrentThreadId() << ": Want to lock mutex " << &m_mutex << endl;
#endif
WaitForSingleObject(m_mutex, INFINITE);
m_locked = true;
#ifdef DEBUG_MUTEX
cerr << "MUTEX DEBUG: " << (void *)GetCurrentThreadId() << ": Locked mutex " << &m_mutex << endl;
#endif
}
void
Mutex::unlock()
{
#ifdef DEBUG_MUTEX
cerr << "MUTEX DEBUG: " << (void *)GetCurrentThreadId() << ": Unlocking mutex " << &m_mutex << endl;
#endif
m_locked = false;
ReleaseMutex(m_mutex);
}
bool
Mutex::trylock()
{
DWORD result = WaitForSingleObject(m_mutex, 0);
if (result == WAIT_TIMEOUT || result == WAIT_FAILED) {
#ifdef DEBUG_MUTEX
cerr << "MUTEX DEBUG: " << (void *)GetCurrentThreadId() << ": Mutex " << &m_mutex << " unavailable" << endl;
#endif
return false;
} else {
m_locked = true;
#ifdef DEBUG_MUTEX
cerr << "MUTEX DEBUG: " << (void *)GetCurrentThreadId() << ": Locked mutex " << &m_mutex << " (from trylock)" << endl;
#endif
return true;
}
}
Condition::Condition(string name) :
m_name(name),
m_locked(false)
{
m_mutex = CreateMutex(NULL, FALSE, NULL);
m_condition = CreateEvent(NULL, FALSE, FALSE, NULL);
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Initialised condition " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
}
Condition::~Condition()
{
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Destroying condition " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
if (m_locked) ReleaseMutex(m_mutex);
CloseHandle(m_condition);
CloseHandle(m_mutex);
}
void
Condition::lock()
{
if (m_locked) {
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Already locked " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
return;
}
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Want to lock " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
WaitForSingleObject(m_mutex, INFINITE);
m_locked = true;
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Locked " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
}
void
Condition::unlock()
{
if (!m_locked) {
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Not locked " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
return;
}
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Unlocking " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
m_locked = false;
ReleaseMutex(m_mutex);
}
void
Condition::wait(int us)
{
if (!m_locked) lock();
if (us == 0) {
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Waiting on " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
SignalObjectAndWait(m_mutex, m_condition, INFINITE, FALSE);
WaitForSingleObject(m_mutex, INFINITE);
} else {
DWORD ms = us / 1000;
if (us > 0 && ms == 0) ms = 1;
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Timed waiting on " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
SignalObjectAndWait(m_mutex, m_condition, ms, FALSE);
WaitForSingleObject(m_mutex, INFINITE);
}
ReleaseMutex(m_mutex);
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Wait done on " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
m_locked = false;
}
void
Condition::signal()
{
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)GetCurrentThreadId() << ": Signalling " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
SetEvent(m_condition);
}
#else /* !_WIN32 */
Thread::Thread() :
m_id(0),
m_extant(false)
{
#ifdef DEBUG_THREAD
cerr << "THREAD DEBUG: Created thread object " << this << endl;
#endif
}
Thread::~Thread()
{
#ifdef DEBUG_THREAD
cerr << "THREAD DEBUG: Destroying thread object " << this << ", id " << m_id << endl;
#endif
if (m_extant) {
pthread_join(m_id, 0);
}
#ifdef DEBUG_THREAD
cerr << "THREAD DEBUG: Destroyed thread object " << this << endl;
#endif
}
void
Thread::start()
{
if (pthread_create(&m_id, 0, staticRun, this)) {
cerr << "ERROR: thread creation failed" << endl;
exit(1);
} else {
#ifdef DEBUG_THREAD
cerr << "THREAD DEBUG: Created thread " << m_id << " for thread object " << this << endl;
#endif
m_extant = true;
}
}
void
Thread::wait()
{
if (m_extant) {
#ifdef DEBUG_THREAD
cerr << "THREAD DEBUG: Waiting on thread " << m_id << " for thread object " << this << endl;
#endif
pthread_join(m_id, 0);
#ifdef DEBUG_THREAD
cerr << "THREAD DEBUG: Waited on thread " << m_id << " for thread object " << this << endl;
#endif
m_extant = false;
}
}
Thread::Id
Thread::id()
{
return m_id;
}
bool
Thread::threadingAvailable()
{
return true;
}
void *
Thread::staticRun(void *arg)
{
Thread *thread = static_cast<Thread *>(arg);
#ifdef DEBUG_THREAD
cerr << "THREAD DEBUG: " << (void *)pthread_self() << ": Running thread " << thread->m_id << " for thread object " << thread << endl;
#endif
thread->run();
return 0;
}
Mutex::Mutex() :
m_locked(false)
{
pthread_mutex_init(&m_mutex, 0);
#ifdef DEBUG_MUTEX
cerr << "MUTEX DEBUG: " << (void *)pthread_self() << ": Initialised mutex " << &m_mutex << endl;
#endif
}
Mutex::~Mutex()
{
#ifdef DEBUG_MUTEX
cerr << "MUTEX DEBUG: " << (void *)pthread_self() << ": Destroying mutex " << &m_mutex << endl;
#endif
pthread_mutex_destroy(&m_mutex);
}
void
Mutex::lock()
{
if (m_locked) {
cerr << "ERROR: Deadlock on mutex " << &m_mutex << endl;
}
#ifdef DEBUG_MUTEX
cerr << "MUTEX DEBUG: " << (void *)pthread_self() << ": Want to lock mutex " << &m_mutex << endl;
#endif
pthread_mutex_lock(&m_mutex);
m_locked = true;
#ifdef DEBUG_MUTEX
cerr << "MUTEX DEBUG: " << (void *)pthread_self() << ": Locked mutex " << &m_mutex << endl;
#endif
}
void
Mutex::unlock()
{
#ifdef DEBUG_MUTEX
cerr << "MUTEX DEBUG: " << (void *)pthread_self() << ": Unlocking mutex " << &m_mutex << endl;
#endif
m_locked = false;
pthread_mutex_unlock(&m_mutex);
}
bool
Mutex::trylock()
{
if (pthread_mutex_trylock(&m_mutex)) {
#ifdef DEBUG_MUTEX
cerr << "MUTEX DEBUG: " << (void *)pthread_self() << ": Mutex " << &m_mutex << " unavailable" << endl;
#endif
return false;
} else {
m_locked = true;
#ifdef DEBUG_MUTEX
cerr << "MUTEX DEBUG: " << (void *)pthread_self() << ": Locked mutex " << &m_mutex << " (from trylock)" << endl;
#endif
return true;
}
}
Condition::Condition(string name) :
m_locked(false),
m_name(name)
{
pthread_mutex_init(&m_mutex, 0);
pthread_cond_init(&m_condition, 0);
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Initialised condition " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
}
Condition::~Condition()
{
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Destroying condition " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
if (m_locked) pthread_mutex_unlock(&m_mutex);
pthread_cond_destroy(&m_condition);
pthread_mutex_destroy(&m_mutex);
}
void
Condition::lock()
{
if (m_locked) {
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Already locked " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
return;
}
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Want to lock " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
pthread_mutex_lock(&m_mutex);
m_locked = true;
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Locked " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
}
void
Condition::unlock()
{
if (!m_locked) {
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Not locked " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
return;
}
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Unlocking " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
m_locked = false;
pthread_mutex_unlock(&m_mutex);
}
void
Condition::wait(int us)
{
if (!m_locked) lock();
if (us == 0) {
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Waiting on " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
pthread_cond_wait(&m_condition, &m_mutex);
} else {
struct timeval now;
gettimeofday(&now, 0);
now.tv_usec += us;
while (now.tv_usec > 1000000) {
now.tv_usec -= 1000000;
++now.tv_sec;
}
struct timespec timeout;
timeout.tv_sec = now.tv_sec;
timeout.tv_nsec = now.tv_usec * 1000;
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Timed waiting on " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
pthread_cond_timedwait(&m_condition, &m_mutex, &timeout);
}
pthread_mutex_unlock(&m_mutex);
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Wait done on " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
m_locked = false;
}
void
Condition::signal()
{
#ifdef DEBUG_CONDITION
cerr << "CONDITION DEBUG: " << (void *)pthread_self() << ": Signalling " << &m_condition << " \"" << m_name << "\"" << endl;
#endif
pthread_cond_signal(&m_condition);
}
#endif /* !_WIN32 */
MutexLocker::MutexLocker(Mutex *mutex) :
m_mutex(mutex)
{
if (m_mutex) {
m_mutex->lock();
}
}
MutexLocker::~MutexLocker()
{
if (m_mutex) {
m_mutex->unlock();
}
}
}

View file

@ -1,131 +0,0 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band
An audio time-stretching and pitch-shifting library.
Copyright 2007 Chris Cannam.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#ifndef _RUBBERBAND_THREAD_H_
#define _RUBBERBAND_THREAD_H_
#ifdef _WIN32
#include <windows.h>
#else /* !_WIN32 */
#include <pthread.h>
#endif /* !_WIN32 */
#include <string>
namespace RubberBand
{
class Thread
{
public:
#ifdef _WIN32
typedef HANDLE Id;
#else
typedef pthread_t Id;
#endif
Thread();
virtual ~Thread();
Id id();
void start();
void wait();
static bool threadingAvailable();
protected:
virtual void run() = 0;
private:
#ifdef _WIN32
HANDLE m_id;
bool m_extant;
static DWORD WINAPI staticRun(LPVOID lpParam);
#else
pthread_t m_id;
bool m_extant;
static void *staticRun(void *);
#endif
};
class Mutex
{
public:
Mutex();
~Mutex();
void lock();
void unlock();
bool trylock();
private:
#ifdef _WIN32
HANDLE m_mutex;
bool m_locked;
#else
pthread_mutex_t m_mutex;
bool m_locked;
#endif
};
class MutexLocker
{
public:
MutexLocker(Mutex *);
~MutexLocker();
private:
Mutex *m_mutex;
};
class Condition
{
public:
Condition(std::string name);
~Condition();
// To wait on a condition, either simply call wait(), or call
// lock() and then wait() (perhaps testing some state in between).
// To signal a condition, call signal().
// Although any thread may signal on a given condition, only one
// thread should ever wait on any given condition object --
// otherwise there will be a race conditions in the logic that
// avoids the thread code having to track whether the condition's
// mutex is locked or not. If that is your requirement, this
// Condition wrapper is not for you.
void lock();
void unlock();
void wait(int us = 0);
void signal();
private:
#ifdef _WIN32
HANDLE m_mutex;
bool m_locked;
HANDLE m_condition;
std::string m_name;
#else
pthread_mutex_t m_mutex;
bool m_locked;
pthread_cond_t m_condition;
std::string m_name;
#endif
};
}
#endif

View file

@ -1,165 +0,0 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band
An audio time-stretching and pitch-shifting library.
Copyright 2007 Chris Cannam.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#ifndef _RUBBERBAND_WINDOW_H_
#define _RUBBERBAND_WINDOW_H_
#include <cmath>
#include <iostream>
#include <map>
namespace RubberBand {
enum WindowType {
RectangularWindow,
BartlettWindow,
HammingWindow,
HanningWindow,
BlackmanWindow,
GaussianWindow,
ParzenWindow,
NuttallWindow,
BlackmanHarrisWindow
};
template <typename T>
class Window
{
public:
/**
* Construct a windower of the given type.
*/
Window(WindowType type, size_t size) : m_type(type), m_size(size) { encache(); }
Window(const Window &w) : m_type(w.m_type), m_size(w.m_size) { encache(); }
Window &operator=(const Window &w) {
if (&w == this) return *this;
m_type = w.m_type;
m_size = w.m_size;
encache();
return *this;
}
virtual ~Window() { delete[] m_cache; }
void cut(T *src) const { cut(src, src); }
void cut(T *src, T *dst) const {
for (size_t i = 0; i < m_size; ++i) dst[i] = src[i] * m_cache[i];
}
T getArea() { return m_area; }
T getValue(size_t i) { return m_cache[i]; }
WindowType getType() const { return m_type; }
size_t getSize() const { return m_size; }
protected:
WindowType m_type;
size_t m_size;
T *m_cache;
T m_area;
void encache();
void cosinewin(T *, T, T, T, T);
};
template <typename T>
void Window<T>::encache()
{
int n = int(m_size);
T *mult = new T[n];
int i;
for (i = 0; i < n; ++i) mult[i] = 1.0;
switch (m_type) {
case RectangularWindow:
for (i = 0; i < n; ++i) {
mult[i] *= 0.5;
}
break;
case BartlettWindow:
for (i = 0; i < n/2; ++i) {
mult[i] *= (i / T(n/2));
mult[i + n/2] *= (1.0 - (i / T(n/2)));
}
break;
case HammingWindow:
cosinewin(mult, 0.54, 0.46, 0.0, 0.0);
break;
case HanningWindow:
cosinewin(mult, 0.50, 0.50, 0.0, 0.0);
break;
case BlackmanWindow:
cosinewin(mult, 0.42, 0.50, 0.08, 0.0);
break;
case GaussianWindow:
for (i = 0; i < n; ++i) {
mult[i] *= pow(2, - pow((i - (n-1)/2.0) / ((n-1)/2.0 / 3), 2));
}
break;
case ParzenWindow:
{
int N = n-1;
for (i = 0; i < N/4; ++i) {
T m = 2 * pow(1.0 - (T(N)/2 - i) / (T(N)/2), 3);
mult[i] *= m;
mult[N-i] *= m;
}
for (i = N/4; i <= N/2; ++i) {
int wn = i - N/2;
T m = 1.0 - 6 * pow(wn / (T(N)/2), 2) * (1.0 - abs(wn) / (T(N)/2));
mult[i] *= m;
mult[N-i] *= m;
}
break;
}
case NuttallWindow:
cosinewin(mult, 0.3635819, 0.4891775, 0.1365995, 0.0106411);
break;
case BlackmanHarrisWindow:
cosinewin(mult, 0.35875, 0.48829, 0.14128, 0.01168);
break;
}
m_cache = mult;
m_area = 0;
for (int i = 0; i < n; ++i) {
m_area += m_cache[i];
}
m_area /= n;
}
template <typename T>
void Window<T>::cosinewin(T *mult, T a0, T a1, T a2, T a3)
{
int n = int(m_size);
for (int i = 0; i < n; ++i) {
mult[i] *= (a0
- a1 * cos(2 * M_PI * i / n)
+ a2 * cos(4 * M_PI * i / n)
- a3 * cos(6 * M_PI * i / n));
}
}
}
#endif

View file

@ -1,408 +0,0 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band
An audio time-stretching and pitch-shifting library.
Copyright 2007 Chris Cannam.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#include "RubberBandPitchShifter.h"
#include "RubberBandStretcher.h"
#include <iostream>
#include <cmath>
using namespace RubberBand;
const char *const
RubberBandPitchShifter::portNamesMono[PortCountMono] =
{
"_latency",
"Cents",
"Semitones",
"Octaves",
"Crispness",
"Input",
"Output"
};
const char *const
RubberBandPitchShifter::portNamesStereo[PortCountStereo] =
{
"_latency",
"Cents",
"Semitones",
"Octaves",
"Crispness",
"Input L",
"Output L",
"Input R",
"Output R"
};
const LADSPA_PortDescriptor
RubberBandPitchShifter::portsMono[PortCountMono] =
{
LADSPA_PORT_OUTPUT | LADSPA_PORT_CONTROL,
LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL,
LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL,
LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL,
LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL,
LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO,
LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO
};
const LADSPA_PortDescriptor
RubberBandPitchShifter::portsStereo[PortCountStereo] =
{
LADSPA_PORT_OUTPUT | LADSPA_PORT_CONTROL,
LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL,
LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL,
LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL,
LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL,
LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO,
LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO,
LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO,
LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO
};
const LADSPA_PortRangeHint
RubberBandPitchShifter::hintsMono[PortCountMono] =
{
{ 0, 0, 0 },
{ LADSPA_HINT_DEFAULT_0 |
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE,
-100.0, 100.0 },
{ LADSPA_HINT_DEFAULT_0 |
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_INTEGER,
-12.0, 12.0 },
{ LADSPA_HINT_DEFAULT_0 |
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_INTEGER,
-4.0, 4.0 },
{ LADSPA_HINT_DEFAULT_MAXIMUM |
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_INTEGER,
0.0, 3.0 },
{ 0, 0, 0 },
{ 0, 0, 0 }
};
const LADSPA_PortRangeHint
RubberBandPitchShifter::hintsStereo[PortCountStereo] =
{
{ 0, 0, 0 },
{ LADSPA_HINT_DEFAULT_0 |
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE,
-100.0, 100.0 },
{ LADSPA_HINT_DEFAULT_0 |
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_INTEGER,
-12.0, 12.0 },
{ LADSPA_HINT_DEFAULT_0 |
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_INTEGER,
-4.0, 4.0 },
{ LADSPA_HINT_DEFAULT_MAXIMUM |
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_INTEGER,
0.0, 3.0 },
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 0, 0, 0 }
};
const LADSPA_Properties
RubberBandPitchShifter::properties = LADSPA_PROPERTY_HARD_RT_CAPABLE;
const LADSPA_Descriptor
RubberBandPitchShifter::ladspaDescriptorMono =
{
2979, // "Unique" ID
"rubberband-pitchshifter-mono", // Label
properties,
"Rubber Band Mono Pitch Shifter", // Name
"Chris Cannam",
"GPL",
PortCountMono,
portsMono,
portNamesMono,
hintsMono,
0, // Implementation data
instantiate,
connectPort,
activate,
run,
0, // Run adding
0, // Set run adding gain
deactivate,
cleanup
};
const LADSPA_Descriptor
RubberBandPitchShifter::ladspaDescriptorStereo =
{
9792, // "Unique" ID
"rubberband-pitchshifter-stereo", // Label
properties,
"Rubber Band Stereo Pitch Shifter", // Name
"Chris Cannam",
"GPL",
PortCountStereo,
portsStereo,
portNamesStereo,
hintsStereo,
0, // Implementation data
instantiate,
connectPort,
activate,
run,
0, // Run adding
0, // Set run adding gain
deactivate,
cleanup
};
const LADSPA_Descriptor *
RubberBandPitchShifter::getDescriptor(unsigned long index)
{
if (index == 0) return &ladspaDescriptorMono;
if (index == 1) return &ladspaDescriptorStereo;
else return 0;
}
RubberBandPitchShifter::RubberBandPitchShifter(int sampleRate, size_t channels) :
m_latency(0),
m_cents(0),
m_semitones(0),
m_octaves(0),
m_crispness(0),
m_ratio(1.0),
m_prevRatio(1.0),
m_currentCrispness(-1),
m_extraLatency(8192), //!!! this should be at least the maximum possible displacement from linear at input rates, divided by the pitch scale factor. It could be very large
m_stretcher(new RubberBandStretcher
(sampleRate, channels,
RubberBandStretcher::OptionProcessRealTime)),
m_sampleRate(sampleRate),
m_channels(channels)
{
for (size_t c = 0; c < m_channels; ++c) {
m_input[c] = 0;
m_output[c] = 0;
//!!! size must be at least max process size plus m_extraLatency:
m_outputBuffer[c] = new RingBuffer<float>(8092); //!!!
m_outputBuffer[c]->zero(m_extraLatency);
//!!! size must be at least max process size:
m_scratch[c] = new float[16384];//!!!
}
}
RubberBandPitchShifter::~RubberBandPitchShifter()
{
delete m_stretcher;
for (size_t c = 0; c < m_channels; ++c) {
delete m_outputBuffer[c];
delete[] m_scratch[c];
}
}
LADSPA_Handle
RubberBandPitchShifter::instantiate(const LADSPA_Descriptor *desc, unsigned long rate)
{
if (desc->PortCount == ladspaDescriptorMono.PortCount) {
return new RubberBandPitchShifter(rate, 1);
} else if (desc->PortCount == ladspaDescriptorStereo.PortCount) {
return new RubberBandPitchShifter(rate, 2);
}
return 0;
}
void
RubberBandPitchShifter::connectPort(LADSPA_Handle handle,
unsigned long port, LADSPA_Data *location)
{
RubberBandPitchShifter *shifter = (RubberBandPitchShifter *)handle;
float **ports[PortCountStereo] = {
&shifter->m_latency,
&shifter->m_cents,
&shifter->m_semitones,
&shifter->m_octaves,
&shifter->m_crispness,
&shifter->m_input[0],
&shifter->m_output[0],
&shifter->m_input[1],
&shifter->m_output[1]
};
*ports[port] = (float *)location;
}
void
RubberBandPitchShifter::activate(LADSPA_Handle handle)
{
RubberBandPitchShifter *shifter = (RubberBandPitchShifter *)handle;
shifter->updateRatio();
shifter->m_prevRatio = shifter->m_ratio;
shifter->m_stretcher->reset();
shifter->m_stretcher->setPitchScale(shifter->m_ratio);
}
void
RubberBandPitchShifter::run(LADSPA_Handle handle, unsigned long samples)
{
RubberBandPitchShifter *shifter = (RubberBandPitchShifter *)handle;
shifter->runImpl(samples);
}
void
RubberBandPitchShifter::updateRatio()
{
double oct = *m_octaves;
oct += *m_semitones / 12;
oct += *m_cents / 1200;
m_ratio = pow(2.0, oct);
}
void
RubberBandPitchShifter::updateCrispness()
{
if (!m_crispness) return;
int c = lrintf(*m_crispness);
if (c == m_currentCrispness) return;
if (c < 0 || c > 3) return;
RubberBandStretcher *s = m_stretcher;
switch (c) {
case 0:
s->setPhaseOption(RubberBandStretcher::OptionPhaseIndependent);
s->setTransientsOption(RubberBandStretcher::OptionTransientsSmooth);
break;
case 1:
s->setPhaseOption(RubberBandStretcher::OptionPhaseAdaptive);
s->setTransientsOption(RubberBandStretcher::OptionTransientsSmooth);
break;
case 2:
s->setPhaseOption(RubberBandStretcher::OptionPhaseAdaptive);
s->setTransientsOption(RubberBandStretcher::OptionTransientsMixed);
break;
case 3:
s->setPhaseOption(RubberBandStretcher::OptionPhaseAdaptive);
s->setTransientsOption(RubberBandStretcher::OptionTransientsCrisp);
break;
}
m_currentCrispness = c;
}
void
RubberBandPitchShifter::runImpl(unsigned long insamples)
{
// std::cerr << "RubberBandPitchShifter::runImpl(" << insamples << ")" << std::endl;
updateRatio();
if (m_ratio != m_prevRatio) {
m_stretcher->setPitchScale(m_ratio);
m_prevRatio = m_ratio;
}
if (m_latency) {
*m_latency = m_stretcher->getLatency() + m_extraLatency;
// std::cerr << "latency = " << *m_latency << std::endl;
}
updateCrispness();
int samples = insamples;
int processed = 0;
size_t outTotal = 0;
float *ptrs[2];
// We have to break up the input into chunks like this because
// insamples could be arbitrarily large
while (processed < samples) {
//!!! size_t:
int toCauseProcessing = m_stretcher->getSamplesRequired();
// std::cout << "to-cause: " << toCauseProcessing << ", remain = " << samples - processed;
int inchunk = std::min(samples - processed, toCauseProcessing);
for (size_t c = 0; c < m_channels; ++c) {
ptrs[c] = &(m_input[c][processed]);
}
m_stretcher->process(ptrs, inchunk, false);
processed += inchunk;
int avail = m_stretcher->available();
int writable = m_outputBuffer[0]->getWriteSpace();
int outchunk = std::min(avail, writable);
size_t actual = m_stretcher->retrieve(m_scratch, outchunk);
outTotal += actual;
// std::cout << ", avail: " << avail << ", outchunk = " << outchunk;
// if (actual != outchunk) std::cout << " (" << actual << ")";
// std::cout << std::endl;
outchunk = actual;
for (size_t c = 0; c < m_channels; ++c) {
if (int(m_outputBuffer[c]->getWriteSpace()) < outchunk) {
std::cerr << "RubberBandPitchShifter::runImpl: buffer overrun: chunk = " << outchunk << ", space = " << m_outputBuffer[c]->getWriteSpace() << std::endl;
}
m_outputBuffer[c]->write(m_scratch[c], outchunk);
}
}
// std::cout << "processed = " << processed << " in, " << outTotal << " out" << ", fill = " << m_outputBuffer[0]->getReadSpace() << " of " << m_outputBuffer[0]->getSize() << std::endl;
for (size_t c = 0; c < m_channels; ++c) {
int avail = m_outputBuffer[c]->getReadSpace();
// std::cout << "avail: " << avail << std::endl;
if (avail < samples && c == 0) {
std::cerr << "RubberBandPitchShifter::runImpl: buffer underrun: required = " << samples << ", available = " << avail << std::endl;
}
int chunk = std::min(avail, samples);
// std::cout << "out chunk: " << chunk << std::endl;
m_outputBuffer[c]->read(m_output[c], chunk);
}
static int minr = -1;
int avail = m_outputBuffer[0]->getReadSpace();
if (minr == -1 || (avail >= 0 && avail < minr)) {
std::cerr << "RubberBandPitchShifter::runImpl: new min remaining " << avail << " from " << minr << std::endl;
minr = avail;
}
}
void
RubberBandPitchShifter::deactivate(LADSPA_Handle handle)
{
activate(handle); // both functions just reset the plugin
}
void
RubberBandPitchShifter::cleanup(LADSPA_Handle handle)
{
delete (RubberBandPitchShifter *)handle;
}

View file

@ -1,95 +0,0 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band
An audio time-stretching and pitch-shifting library.
Copyright 2007 Chris Cannam.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#ifndef _RUBBERBAND_PITCH_SHIFTER_H_
#define _RUBBERBAND_PITCH_SHIFTER_H_
#include "ladspa.h"
#include "RingBuffer.h"
namespace RubberBand {
class RubberBandStretcher;
}
class RubberBandPitchShifter
{
public:
static const LADSPA_Descriptor *getDescriptor(unsigned long index);
protected:
RubberBandPitchShifter(int sampleRate, size_t channels);
~RubberBandPitchShifter();
enum {
LatencyPort = 0,
OctavesPort = 1,
SemitonesPort = 2,
CentsPort = 3,
CrispnessPort = 4,
InputPort1 = 5,
OutputPort1 = 6,
PortCountMono = OutputPort1 + 1,
InputPort2 = 7,
OutputPort2 = 8,
PortCountStereo = OutputPort2 + 1
};
static const char *const portNamesMono[PortCountMono];
static const LADSPA_PortDescriptor portsMono[PortCountMono];
static const LADSPA_PortRangeHint hintsMono[PortCountMono];
static const char *const portNamesStereo[PortCountStereo];
static const LADSPA_PortDescriptor portsStereo[PortCountStereo];
static const LADSPA_PortRangeHint hintsStereo[PortCountStereo];
static const LADSPA_Properties properties;
static const LADSPA_Descriptor ladspaDescriptorMono;
static const LADSPA_Descriptor ladspaDescriptorStereo;
static LADSPA_Handle instantiate(const LADSPA_Descriptor *, unsigned long);
static void connectPort(LADSPA_Handle, unsigned long, LADSPA_Data *);
static void activate(LADSPA_Handle);
static void run(LADSPA_Handle, unsigned long);
static void deactivate(LADSPA_Handle);
static void cleanup(LADSPA_Handle);
void runImpl(unsigned long);
void updateRatio();
void updateCrispness();
float *m_input[2];
float *m_output[2];
float *m_latency;
float *m_cents;
float *m_semitones;
float *m_octaves;
float *m_crispness;
double m_ratio;
double m_prevRatio;
int m_currentCrispness;
size_t m_extraLatency;
RubberBand::RubberBandStretcher *m_stretcher;
RubberBand::RingBuffer<float> *m_outputBuffer[2];
float *m_scratch[2];
int m_sampleRate;
size_t m_channels;
};
#endif

View file

@ -1,2 +0,0 @@
ladspa:ladspa-rubberband:rubberband-pitchshifter-mono::Frequency > Pitch shifters
ladspa:ladspa-rubberband:rubberband-pitchshifter-stereo::Frequency > Pitch shifters

View file

@ -1,26 +0,0 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band
An audio time-stretching and pitch-shifting library.
Copyright 2007 Chris Cannam.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#include "RubberBandPitchShifter.h"
#include <stdio.h>
extern "C" {
const LADSPA_Descriptor *ladspa_descriptor(unsigned long index)
{
return RubberBandPitchShifter::getDescriptor(index);
}
}

View file

@ -1,475 +0,0 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band
An audio time-stretching and pitch-shifting library.
Copyright 2007 Chris Cannam.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#include "RubberBandStretcher.h"
#include <iostream>
#include <sndfile.h>
#include <cmath>
#include <sys/time.h>
#include <time.h>
#include "sysutils.h"
#include <getopt.h>
// for import and export of FFTW wisdom
#include <fftw3.h>
using namespace std;
using namespace RubberBand;
#ifdef _WIN32
using RubberBand::gettimeofday;
using RubberBand::usleep;
#endif
int main(int argc, char **argv)
{
int c;
double ratio = 1.0;
double pitchshift = 1.0;
double frequencyshift = 1.0;
int debug = 0;
bool realtime = false;
bool precise = false;
int threading = 0;
bool peaklock = true;
bool longwin = false;
bool shortwin = false;
bool softening = true;
int crispness = -1;
bool help = false;
bool quiet = false;
bool haveRatio = false;
enum {
NoTransients,
BandLimitedTransients,
Transients
} transients = Transients;
float fthresh0 = -1.f;
float fthresh1 = -1.f;
float fthresh2 = -1.f;
while (1) {
int optionIndex = 0;
static struct option longOpts[] = {
{ "help", 0, 0, 'h' },
{ "time", 1, 0, 't' },
{ "tempo", 1, 0, 'T' },
{ "pitch", 1, 0, 'p' },
{ "frequency", 1, 0, 'f' },
{ "crisp", 1, 0, 'c' },
{ "crispness", 1, 0, 'c' },
{ "debug", 1, 0, 'd' },
{ "realtime", 0, 0, 'R' },
{ "precise", 0, 0, 'P' },
{ "no-threads", 0, 0, '0' },
{ "no-transients", 0, 0, '1' },
{ "no-peaklock", 0, 0, '2' },
{ "window-long", 0, 0, '3' },
{ "window-short", 0, 0, '4' },
{ "thresh0", 1, 0, '5' },
{ "thresh1", 1, 0, '6' },
{ "thresh2", 1, 0, '7' },
{ "bl-transients", 0, 0, '8' },
{ "no-softening", 0, 0, '9' },
{ "threads", 0, 0, '@' },
{ "quiet", 0, 0, 'q' },
{ 0, 0, 0 }
};
c = getopt_long(argc, argv, "t:p:d:RPc:f:qh", longOpts, &optionIndex);
if (c == -1) break;
switch (c) {
case 'h': help = true; break;
case 't': ratio *= atof(optarg); haveRatio = true; break;
case 'T': { double m = atof(optarg); if (m != 0.0) ratio /= m; }; haveRatio = true; break;
case 'p': pitchshift = atof(optarg); haveRatio = true; break;
case 'f': frequencyshift = atof(optarg); haveRatio = true; break;
case 'd': debug = atoi(optarg); break;
case 'R': realtime = true; break;
case 'P': precise = true; break;
case '0': threading = 1; break;
case '@': threading = 2; break;
case '1': transients = NoTransients; break;
case '2': peaklock = false; break;
case '3': longwin = true; break;
case '4': shortwin = true; break;
case '5': fthresh0 = atof(optarg); break;
case '6': fthresh1 = atof(optarg); break;
case '7': fthresh2 = atof(optarg); break;
case '8': transients = BandLimitedTransients; break;
case '9': softening = false; break;
case 'c': crispness = atoi(optarg); break;
case 'q': quiet = true; break;
default: help = true; break;
}
}
if (help || !haveRatio || optind + 2 != argc) {
cerr << endl;
cerr << "Rubber Band" << endl;
cerr << "An audio time-stretching and pitch-shifting library and utility program." << endl;
cerr << "Copyright 2007 Chris Cannam. Distributed under the GNU General Public License." << endl;
cerr << endl;
cerr << " Usage: " << argv[0] << " [options] <infile.wav> <outfile.wav>" << endl;
cerr << endl;
cerr << "You must specify at least one of the following time and pitch ratio options." << endl;
cerr << endl;
cerr << " -t<X>, --time <X> Stretch to X times original duration, or" << endl;
cerr << " -T<X>, --tempo <X> Change tempo by multiple X (equivalent to --time 1/X)" << endl;
cerr << endl;
cerr << " -p<X>, --pitch <X> Raise pitch by X semitones, or" << endl;
cerr << " -f<X>, --frequency <X> Change frequency by multiple X" << endl;
cerr << endl;
cerr << "The following option provides a simple way to adjust the sound. See below" << endl;
cerr << "for more details." << endl;
cerr << endl;
cerr << " -c<N>, --crisp <N> Crispness (N = 0,1,2,3,4,5); default 4 (see below)" << endl;
cerr << endl;
cerr << "The remaining options fine-tune the processing mode and stretch algorithm." << endl;
cerr << "These are mostly included for test purposes; the default settings and standard" << endl;
cerr << "crispness parameter are intended to provide the best sounding set of options" << endl;
cerr << "for most situations." << endl;
cerr << endl;
cerr << " -P, --precise Aim for minimal time distortion (implied by -R)" << endl;
cerr << " -R, --realtime Select realtime mode (implies -P --no-threads)" << endl;
cerr << " --no-threads No extra threads regardless of CPU and channel count" << endl;
cerr << " --threads Assume multi-CPU even if only one CPU is identified" << endl;
cerr << " --no-transients Disable phase resynchronisation at transients" << endl;
cerr << " --bl-transients Band-limit phase resync to extreme frequencies" << endl;
cerr << " --no-peaklock Disable phase locking to peak frequencies" << endl;
cerr << " --no-softening Disable large-ratio softening of phase locking" << endl;
cerr << " --window-long Use longer processing window (actual size may vary)" << endl;
cerr << " --window-short Use shorter processing window" << endl;
cerr << " --thresh<N> <F> Set internal freq threshold N (N = 0,1,2) to F Hz" << endl;
cerr << endl;
cerr << " -d<N>, --debug <N> Select debug level (N = 0,1,2,3); default 0, full 3" << endl;
cerr << " (N.B. debug level 3 includes audible ticks in output)" << endl;
cerr << " -q, --quiet Suppress progress output" << endl;
cerr << endl;
cerr << " -h, --help Show this help" << endl;
cerr << endl;
cerr << "\"Crispness\" levels:" << endl;
cerr << " -c 0 equivalent to --no-transients --no-peaklock --window-long" << endl;
cerr << " -c 1 equivalent to --no-transients --no-peaklock" << endl;
cerr << " -c 2 equivalent to --no-transients" << endl;
cerr << " -c 3 equivalent to --bl-transients" << endl;
cerr << " -c 4 default processing options" << endl;
cerr << " -c 5 equivalent to --no-peaklock --window-short (may be suitable for drums)" << endl;
cerr << endl;
return 2;
}
switch (crispness) {
case -1: crispness = 4; break;
case 0: transients = NoTransients; peaklock = false; longwin = true; shortwin = false; break;
case 1: transients = NoTransients; peaklock = false; longwin = false; shortwin = false; break;
case 2: transients = NoTransients; peaklock = true; longwin = false; shortwin = false; break;
case 3: transients = BandLimitedTransients; peaklock = true; longwin = false; shortwin = false; break;
case 4: transients = Transients; peaklock = true; longwin = false; shortwin = false; break;
case 5: transients = Transients; peaklock = false; longwin = false; shortwin = true; break;
};
if (!quiet) {
cerr << "Using crispness level: " << crispness << " (";
switch (crispness) {
case 0: cerr << "Mushy"; break;
case 1: cerr << "Smooth"; break;
case 2: cerr << "Balanced multitimbral mixture"; break;
case 3: cerr << "Unpitched percussion with stable notes"; break;
case 4: cerr << "Crisp monophonic instrumental"; break;
case 5: cerr << "Unpitched solo percussion"; break;
}
cerr << ")" << endl;
}
char *fileName = strdup(argv[optind++]);
char *fileNameOut = strdup(argv[optind++]);
SNDFILE *sndfile;
SNDFILE *sndfileOut;
SF_INFO sfinfo;
SF_INFO sfinfoOut;
memset(&sfinfo, 0, sizeof(SF_INFO));
sndfile = sf_open(fileName, SFM_READ, &sfinfo);
if (!sndfile) {
cerr << "ERROR: Failed to open input file \"" << fileName << "\": "
<< sf_strerror(sndfile) << endl;
return 1;
}
sfinfoOut.channels = sfinfo.channels;
sfinfoOut.format = sfinfo.format;
sfinfoOut.frames = int(sfinfo.frames * ratio + 0.1);
sfinfoOut.samplerate = sfinfo.samplerate;
sfinfoOut.sections = sfinfo.sections;
sfinfoOut.seekable = sfinfo.seekable;
sndfileOut = sf_open(fileNameOut, SFM_WRITE, &sfinfoOut) ;
if (!sndfileOut) {
cerr << "ERROR: Failed to open output file \"" << fileName << "\" for writing: "
<< sf_strerror(sndfile) << endl;
return 1;
}
int ibs = 1024;
size_t channels = sfinfo.channels;
RubberBandStretcher::Options options = 0;
if (realtime) options |= RubberBandStretcher::OptionProcessRealTime;
if (precise) options |= RubberBandStretcher::OptionStretchPrecise;
if (!peaklock) options |= RubberBandStretcher::OptionPhaseIndependent;
if (!softening) options |= RubberBandStretcher::OptionPhasePeakLocked;
if (longwin) options |= RubberBandStretcher::OptionWindowLong;
if (shortwin) options |= RubberBandStretcher::OptionWindowShort;
switch (threading) {
case 0:
options |= RubberBandStretcher::OptionThreadingAuto;
break;
case 1:
options |= RubberBandStretcher::OptionThreadingNever;
break;
case 2:
options |= RubberBandStretcher::OptionThreadingAlways;
break;
}
switch (transients) {
case NoTransients:
options |= RubberBandStretcher::OptionTransientsSmooth;
break;
case BandLimitedTransients:
options |= RubberBandStretcher::OptionTransientsMixed;
break;
case Transients:
options |= RubberBandStretcher::OptionTransientsCrisp;
break;
}
if (pitchshift != 1.0) {
frequencyshift *= pow(2.0, pitchshift / 12);
}
#ifdef _WIN32
RubberBand::
#endif
timeval tv;
(void)gettimeofday(&tv, 0);
RubberBandStretcher::setDefaultDebugLevel(debug);
RubberBandStretcher ts(sfinfo.samplerate, channels, options,
ratio, frequencyshift);
ts.setExpectedInputDuration(sfinfo.frames);
float *fbuf = new float[channels * ibs];
float **ibuf = new float *[channels];
for (size_t i = 0; i < channels; ++i) ibuf[i] = new float[ibs];
int frame = 0;
int percent = 0;
sf_seek(sndfile, 0, SEEK_SET);
if (!realtime) {
if (!quiet) {
cerr << "Pass 1: Studying..." << endl;
}
while (frame < sfinfo.frames) {
int count = -1;
if ((count = sf_readf_float(sndfile, fbuf, ibs)) <= 0) break;
for (size_t c = 0; c < channels; ++c) {
for (int i = 0; i < count; ++i) {
float value = fbuf[i * channels + c];
ibuf[c][i] = value;
}
}
bool final = (frame + ibs >= sfinfo.frames);
ts.study(ibuf, count, final);
int p = int((double(frame) * 100.0) / sfinfo.frames);
if (p > percent || frame == 0) {
percent = p;
if (!quiet) {
cerr << "\r" << percent << "% ";
}
}
frame += ibs;
}
if (!quiet) {
cerr << "\rCalculating profile..." << endl;
}
sf_seek(sndfile, 0, SEEK_SET);
}
frame = 0;
percent = 0;
size_t countIn = 0, countOut = 0;
while (frame < sfinfo.frames) {
int count = -1;
if ((count = sf_readf_float(sndfile, fbuf, ibs)) < 0) break;
countIn += count;
for (size_t c = 0; c < channels; ++c) {
for (int i = 0; i < count; ++i) {
float value = fbuf[i * channels + c];
ibuf[c][i] = value;
}
}
bool final = (frame + ibs >= sfinfo.frames);
ts.process(ibuf, count, final);
int avail = ts.available();
if (debug > 1) cerr << "available = " << avail << endl;
if (avail > 0) {
float **obf = new float *[channels];
for (size_t i = 0; i < channels; ++i) {
obf[i] = new float[avail];
}
ts.retrieve(obf, avail);
countOut += avail;
float *fobf = new float[channels * avail];
for (size_t c = 0; c < channels; ++c) {
for (int i = 0; i < avail; ++i) {
float value = obf[c][i];
if (value > 1.f) value = 1.f;
if (value < -1.f) value = -1.f;
fobf[i * channels + c] = value;
}
}
// cout << "fobf mean: ";
// double d = 0;
// for (int i = 0; i < avail; ++i) {
// d += fobf[i];
// }
// d /= avail;
// cout << d << endl;
sf_writef_float(sndfileOut, fobf, avail);
delete[] fobf;
for (size_t i = 0; i < channels; ++i) {
delete[] obf[i];
}
delete[] obf;
}
if (frame == 0 && !realtime && !quiet) {
cerr << "Pass 2: Processing..." << endl;
}
int p = int((double(frame) * 100.0) / sfinfo.frames);
if (p > percent || frame == 0) {
percent = p;
if (!quiet) {
cerr << "\r" << percent << "% ";
}
}
frame += ibs;
}
if (!quiet) {
cerr << "\r " << endl;
}
int avail;
while ((avail = ts.available()) >= 0) {
if (debug > 1) {
cerr << "(completing) available = " << avail << endl;
}
if (avail > 0) {
float **obf = new float *[channels];
for (size_t i = 0; i < channels; ++i) {
obf[i] = new float[avail];
}
ts.retrieve(obf, avail);
countOut += avail;
float *fobf = new float[channels * avail];
for (size_t c = 0; c < channels; ++c) {
for (int i = 0; i < avail; ++i) {
float value = obf[c][i];
if (value > 1.f) value = 1.f;
if (value < -1.f) value = -1.f;
fobf[i * channels + c] = value;
}
}
sf_writef_float(sndfileOut, fobf, avail);
delete[] fobf;
for (size_t i = 0; i < channels; ++i) {
delete[] obf[i];
}
delete[] obf;
} else {
usleep(10000);
}
}
sf_close(sndfile);
sf_close(sndfileOut);
if (!quiet) {
cerr << "in: " << countIn << ", out: " << countOut << ", ratio: " << float(countOut)/float(countIn) << ", ideal output: " << lrint(countIn * ratio) << ", error: " << abs(lrint(countIn * ratio) - int(countOut)) << endl;
#ifdef _WIN32
RubberBand::
#endif
timeval etv;
(void)gettimeofday(&etv, 0);
etv.tv_sec -= tv.tv_sec;
if (etv.tv_usec < tv.tv_usec) {
etv.tv_usec += 1000000;
etv.tv_sec -= 1;
}
etv.tv_usec -= tv.tv_usec;
double sec = double(etv.tv_sec) + (double(etv.tv_usec) / 1000000.0);
cerr << "elapsed time: " << sec << " sec, in frames/sec: " << countIn/sec << ", out frames/sec: " << countOut/sec << endl;
}
return 0;
}

View file

@ -1,106 +0,0 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band
An audio time-stretching and pitch-shifting library.
Copyright 2007 Chris Cannam.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#include "sysutils.h"
#ifdef _WIN32
#include <windows.h>
#else /* !_WIN32 */
#ifdef __APPLE__
#include <sys/sysctl.h>
#else /* !__APPLE__, !_WIN32 */
#include <stdio.h>
#include <string.h>
#endif /* !__APPLE__, !_WIN32 */
#endif /* !_WIN32 */
#include <iostream>
namespace RubberBand {
bool
system_is_multiprocessor()
{
static bool tested = false, mp = false;
if (tested) return mp;
int count = 0;
#ifdef _WIN32
SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo);
count = sysinfo.dwNumberOfProcessors;
#else /* !_WIN32 */
#ifdef __APPLE__
size_t sz = sizeof(count);
if (sysctlbyname("hw.ncpu", &count, &sz, NULL, 0)) {
mp = false;
} else {
mp = (count > 1);
}
#else /* !__APPLE__, !_WIN32 */
//...
FILE *cpuinfo = fopen("/proc/cpuinfo", "r");
if (!cpuinfo) return false;
char buf[256];
while (!feof(cpuinfo)) {
fgets(buf, 256, cpuinfo);
if (!strncmp(buf, "processor", 9)) {
++count;
}
if (count > 1) break;
}
fclose(cpuinfo);
#endif /* !__APPLE__, !_WIN32 */
#endif /* !_WIN32 */
mp = (count > 1);
tested = true;
return mp;
}
#ifdef _WIN32
void gettimeofday(struct timeval *tv, void *tz)
{
union {
long long ns100;
FILETIME ft;
} now;
::GetSystemTimeAsFileTime(&now.ft);
tv->tv_usec = (long)((now.ns100 / 10LL) % 1000000LL);
tv->tv_sec = (long)((now.ns100 - 116444736000000000LL) / 10000000LL);
}
void usleep(unsigned long usec)
{
::Sleep(usec == 0 ? 0 : usec < 1000 ? 1 : usec / 1000);
}
#endif
}

View file

@ -1,30 +0,0 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band
An audio time-stretching and pitch-shifting library.
Copyright 2007 Chris Cannam.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#ifndef _RUBBERBAND_SYSINFO_H_
#define _RUBBERBAND_SYSINFO_H_
namespace RubberBand {
extern bool system_is_multiprocessor();
#ifdef _WIN32
struct timeval { long tv_sec; long tv_usec; };
void gettimeofday(struct timeval *p, void *tz);
void usleep(unsigned long);
#endif
}
#endif

View file

@ -1,647 +0,0 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band
An audio time-stretching and pitch-shifting library.
Copyright 2007 Chris Cannam.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#include "RubberBandVampPlugin.h"
#include "StretchCalculator.h"
#include <cmath>
using std::string;
using std::vector;
using std::cerr;
using std::endl;
class RubberBandVampPlugin::Impl
{
public:
size_t m_stepSize;
size_t m_blockSize;
size_t m_sampleRate;
float m_timeRatio;
float m_pitchRatio;
bool m_realtime;
bool m_elasticTiming;
int m_transientMode;
bool m_phaseIndependent;
int m_windowLength;
RubberBand::RubberBandStretcher *m_stretcher;
int m_incrementsOutput;
int m_aggregateIncrementsOutput;
int m_divergenceOutput;
int m_phaseResetDfOutput;
int m_smoothedPhaseResetDfOutput;
int m_phaseResetPointsOutput;
int m_timeSyncPointsOutput;
size_t m_counter;
size_t m_accumulatedIncrement;
float **m_outputDump;
FeatureSet processOffline(const float *const *inputBuffers,
Vamp::RealTime timestamp);
FeatureSet getRemainingFeaturesOffline();
FeatureSet processRealTime(const float *const *inputBuffers,
Vamp::RealTime timestamp);
FeatureSet getRemainingFeaturesRealTime();
FeatureSet createFeatures(size_t inputIncrement,
std::vector<int> &outputIncrements,
std::vector<float> &phaseResetDf,
std::vector<int> &exactPoints,
std::vector<float> &smoothedDf,
size_t baseCount,
bool includeFinal);
};
RubberBandVampPlugin::RubberBandVampPlugin(float inputSampleRate) :
Plugin(inputSampleRate)
{
m_d = new Impl();
m_d->m_stepSize = 0;
m_d->m_timeRatio = 1.f;
m_d->m_pitchRatio = 1.f;
m_d->m_realtime = false;
m_d->m_elasticTiming = true;
m_d->m_transientMode = 0;
m_d->m_phaseIndependent = false;
m_d->m_windowLength = 0;
m_d->m_stretcher = 0;
m_d->m_sampleRate = lrintf(m_inputSampleRate);
}
RubberBandVampPlugin::~RubberBandVampPlugin()
{
if (m_d->m_outputDump) {
for (size_t i = 0; i < m_d->m_stretcher->getChannelCount(); ++i) {
delete[] m_d->m_outputDump[i];
}
delete[] m_d->m_outputDump;
}
delete m_d->m_stretcher;
delete m_d;
}
string
RubberBandVampPlugin::getIdentifier() const
{
return "rubberband";
}
string
RubberBandVampPlugin::getName() const
{
return "Rubber Band Timestretch Analysis";
}
string
RubberBandVampPlugin::getDescription() const
{
return "Carry out analysis phases of time stretcher process";
}
string
RubberBandVampPlugin::getMaker() const
{
return "Rubber Band"; ///!!!
}
int
RubberBandVampPlugin::getPluginVersion() const
{
return 1;
}
string
RubberBandVampPlugin::getCopyright() const
{
return "";//!!!
}
RubberBandVampPlugin::OutputList
RubberBandVampPlugin::getOutputDescriptors() const
{
OutputList list;
size_t rate = 0;
if (m_d->m_stretcher) {
rate = lrintf(m_inputSampleRate / m_d->m_stretcher->getInputIncrement());
}
OutputDescriptor d;
d.identifier = "increments";
d.name = "Output Increments";
d.description = "Output time increment for each input step";
d.unit = "samples";
d.hasFixedBinCount = true;
d.binCount = 1;
d.hasKnownExtents = false;
d.isQuantized = true;
d.quantizeStep = 1.0;
d.sampleType = OutputDescriptor::VariableSampleRate;
d.sampleRate = rate;
m_d->m_incrementsOutput = list.size();
list.push_back(d);
d.identifier = "aggregate_increments";
d.name = "Accumulated Output Increments";
d.description = "Accumulated output time increments";
d.sampleRate = 0;
m_d->m_aggregateIncrementsOutput = list.size();
list.push_back(d);
d.identifier = "divergence";
d.name = "Divergence from Linear";
d.description = "Difference between actual output time and the output time for a theoretical linear stretch";
d.isQuantized = false;
d.sampleRate = 0;
m_d->m_divergenceOutput = list.size();
list.push_back(d);
d.identifier = "phaseresetdf";
d.name = "Phase Reset Detection Function";
d.description = "Curve whose peaks are used to identify transients for phase reset points";
d.unit = "";
d.sampleRate = rate;
m_d->m_phaseResetDfOutput = list.size();
list.push_back(d);
d.identifier = "smoothedphaseresetdf";
d.name = "Smoothed Phase Reset Detection Function";
d.description = "Phase reset curve smoothed for peak picking";
d.unit = "";
m_d->m_smoothedPhaseResetDfOutput = list.size();
list.push_back(d);
d.identifier = "phaseresetpoints";
d.name = "Phase Reset Points";
d.description = "Points estimated as transients at which phase reset occurs";
d.unit = "";
d.hasFixedBinCount = true;
d.binCount = 0;
d.hasKnownExtents = false;
d.isQuantized = false;
d.sampleRate = 0;
m_d->m_phaseResetPointsOutput = list.size();
list.push_back(d);
d.identifier = "timesyncpoints";
d.name = "Time Sync Points";
d.description = "Salient points which stretcher aims to place with strictly correct timing";
d.unit = "";
d.hasFixedBinCount = true;
d.binCount = 0;
d.hasKnownExtents = false;
d.isQuantized = false;
d.sampleRate = 0;
m_d->m_timeSyncPointsOutput = list.size();
list.push_back(d);
return list;
}
RubberBandVampPlugin::ParameterList
RubberBandVampPlugin::getParameterDescriptors() const
{
ParameterList list;
ParameterDescriptor d;
d.identifier = "timeratio";
d.name = "Time Ratio";
d.description = "Ratio to modify overall duration by";
d.unit = "%";
d.minValue = 1;
d.maxValue = 500;
d.defaultValue = 100;
d.isQuantized = false;
list.push_back(d);
d.identifier = "pitchratio";
d.name = "Pitch Scale Ratio";
d.description = "Frequency ratio to modify pitch by";
d.unit = "%";
d.minValue = 1;
d.maxValue = 500;
d.defaultValue = 100;
d.isQuantized = false;
list.push_back(d);
d.identifier = "mode";
d.name = "Processing Mode";
d.description = ""; //!!!
d.unit = "";
d.minValue = 0;
d.maxValue = 1;
d.defaultValue = 0;
d.isQuantized = true;
d.quantizeStep = 1;
d.valueNames.clear();
d.valueNames.push_back("Offline");
d.valueNames.push_back("Real Time");
list.push_back(d);
d.identifier = "stretchtype";
d.name = "Stretch Flexibility";
d.description = ""; //!!!
d.unit = "";
d.minValue = 0;
d.maxValue = 1;
d.defaultValue = 0;
d.isQuantized = true;
d.quantizeStep = 1;
d.valueNames.clear();
d.valueNames.push_back("Elastic");
d.valueNames.push_back("Precise");
list.push_back(d);
d.identifier = "transientmode";
d.name = "Transient Handling";
d.description = ""; //!!!
d.unit = "";
d.minValue = 0;
d.maxValue = 2;
d.defaultValue = 0;
d.isQuantized = true;
d.quantizeStep = 1;
d.valueNames.clear();
d.valueNames.push_back("Mixed");
d.valueNames.push_back("Smooth");
d.valueNames.push_back("Crisp");
list.push_back(d);
d.identifier = "phasemode";
d.name = "Phase Handling";
d.description = ""; //!!!
d.unit = "";
d.minValue = 0;
d.maxValue = 1;
d.defaultValue = 0;
d.isQuantized = true;
d.quantizeStep = 1;
d.valueNames.clear();
d.valueNames.push_back("Peak Locked");
d.valueNames.push_back("Independent");
list.push_back(d);
d.identifier = "windowmode";
d.name = "Window Length";
d.description = ""; //!!!
d.unit = "";
d.minValue = 0;
d.maxValue = 2;
d.defaultValue = 0;
d.isQuantized = true;
d.quantizeStep = 1;
d.valueNames.clear();
d.valueNames.push_back("Standard");
d.valueNames.push_back("Short");
d.valueNames.push_back("Long");
list.push_back(d);
return list;
}
float
RubberBandVampPlugin::getParameter(std::string id) const
{
if (id == "timeratio") return m_d->m_timeRatio * 100.f;
if (id == "pitchratio") return m_d->m_pitchRatio * 100.f;
if (id == "mode") return m_d->m_realtime ? 1 : 0;
if (id == "stretchtype") return m_d->m_elasticTiming ? 0 : 1;
if (id == "transientmode") return m_d->m_transientMode;
if (id == "phasemode") return m_d->m_phaseIndependent ? 1 : 0;
if (id == "windowmode") return m_d->m_windowLength;
return 0.f;
}
void
RubberBandVampPlugin::setParameter(std::string id, float value)
{
if (id == "timeratio") {
m_d->m_timeRatio = value / 100;
} else if (id == "pitchratio") {
m_d->m_pitchRatio = value / 100;
} else {
bool set = (value > 0.5);
if (id == "mode") m_d->m_realtime = set;
else if (id == "stretchtype") m_d->m_elasticTiming = !set;
else if (id == "transientmode") m_d->m_transientMode = int(value + 0.5);
else if (id == "phasemode") m_d->m_phaseIndependent = set;
else if (id == "windowmode") m_d->m_windowLength = int(value + 0.5);
}
}
bool
RubberBandVampPlugin::initialise(size_t channels, size_t stepSize, size_t blockSize)
{
if (channels < getMinChannelCount() ||
channels > getMaxChannelCount()) return false;
m_d->m_stepSize = std::min(stepSize, blockSize);
m_d->m_blockSize = stepSize;
RubberBand::RubberBandStretcher::Options options = 0;
if (m_d->m_realtime)
options |= RubberBand::RubberBandStretcher::OptionProcessRealTime;
else options |= RubberBand::RubberBandStretcher::OptionProcessOffline;
if (m_d->m_elasticTiming)
options |= RubberBand::RubberBandStretcher::OptionStretchElastic;
else options |= RubberBand::RubberBandStretcher::OptionStretchPrecise;
if (m_d->m_transientMode == 0)
options |= RubberBand::RubberBandStretcher::OptionTransientsMixed;
else if (m_d->m_transientMode == 1)
options |= RubberBand::RubberBandStretcher::OptionTransientsSmooth;
else options |= RubberBand::RubberBandStretcher::OptionTransientsCrisp;
if (m_d->m_phaseIndependent)
options |= RubberBand::RubberBandStretcher::OptionPhaseIndependent;
else options |= RubberBand::RubberBandStretcher::OptionPhasePeakLocked;
if (m_d->m_windowLength == 0)
options |= RubberBand::RubberBandStretcher::OptionWindowStandard;
else if (m_d->m_windowLength == 1)
options |= RubberBand::RubberBandStretcher::OptionWindowShort;
else options |= RubberBand::RubberBandStretcher::OptionWindowLong;
delete m_d->m_stretcher;
m_d->m_stretcher = new RubberBand::RubberBandStretcher
(m_d->m_sampleRate, channels, options);
m_d->m_stretcher->setDebugLevel(1);
m_d->m_stretcher->setTimeRatio(m_d->m_timeRatio);
m_d->m_stretcher->setPitchScale(m_d->m_pitchRatio);
m_d->m_counter = 0;
m_d->m_accumulatedIncrement = 0;
m_d->m_outputDump = 0;
return true;
}
void
RubberBandVampPlugin::reset()
{
// delete m_stretcher; //!!! or just if (m_stretcher) m_stretcher->reset();
// m_stretcher = new RubberBand::RubberBandStretcher(lrintf(m_inputSampleRate), channels);
if (m_d->m_stretcher) m_d->m_stretcher->reset();
}
RubberBandVampPlugin::FeatureSet
RubberBandVampPlugin::process(const float *const *inputBuffers,
Vamp::RealTime timestamp)
{
if (m_d->m_realtime) {
return m_d->processRealTime(inputBuffers, timestamp);
} else {
return m_d->processOffline(inputBuffers, timestamp);
}
}
RubberBandVampPlugin::FeatureSet
RubberBandVampPlugin::getRemainingFeatures()
{
if (m_d->m_realtime) {
return m_d->getRemainingFeaturesRealTime();
} else {
return m_d->getRemainingFeaturesOffline();
}
}
RubberBandVampPlugin::FeatureSet
RubberBandVampPlugin::Impl::processOffline(const float *const *inputBuffers,
Vamp::RealTime timestamp)
{
if (!m_stretcher) {
cerr << "ERROR: RubberBandVampPlugin::processOffline: "
<< "RubberBandVampPlugin has not been initialised"
<< endl;
return FeatureSet();
}
m_stretcher->study(inputBuffers, m_blockSize, false);
return FeatureSet();
}
RubberBandVampPlugin::FeatureSet
RubberBandVampPlugin::Impl::getRemainingFeaturesOffline()
{
m_stretcher->study(0, 0, true);
m_stretcher->calculateStretch();
int rate = m_sampleRate;
RubberBand::StretchCalculator sc(rate,
m_stretcher->getInputIncrement(),
true);
size_t inputIncrement = m_stretcher->getInputIncrement();
std::vector<int> outputIncrements = m_stretcher->getOutputIncrements();
std::vector<float> phaseResetDf = m_stretcher->getPhaseResetCurve();
std::vector<int> peaks = m_stretcher->getExactTimePoints();
std::vector<float> smoothedDf = sc.smoothDF(phaseResetDf);
FeatureSet features = createFeatures
(inputIncrement, outputIncrements, phaseResetDf, peaks, smoothedDf,
0, true);
return features;
}
RubberBandVampPlugin::FeatureSet
RubberBandVampPlugin::Impl::processRealTime(const float *const *inputBuffers,
Vamp::RealTime timestamp)
{
// This function is not in any way a real-time function (i.e. it
// has no requirement to be RT safe); it simply operates the
// stretcher in RT mode.
if (!m_stretcher) {
cerr << "ERROR: RubberBandVampPlugin::processRealTime: "
<< "RubberBandVampPlugin has not been initialised"
<< endl;
return FeatureSet();
}
m_stretcher->process(inputBuffers, m_blockSize, false);
size_t inputIncrement = m_stretcher->getInputIncrement();
std::vector<int> outputIncrements = m_stretcher->getOutputIncrements();
std::vector<float> phaseResetDf = m_stretcher->getPhaseResetCurve();
std::vector<float> smoothedDf; // not meaningful in RT mode
std::vector<int> dummyPoints;
FeatureSet features = createFeatures
(inputIncrement, outputIncrements, phaseResetDf, dummyPoints, smoothedDf,
m_counter, false);
m_counter += outputIncrements.size();
int available = 0;
while ((available = m_stretcher->available()) > 0) {
if (!m_outputDump) {
m_outputDump = new float *[m_stretcher->getChannelCount()];
for (size_t i = 0; i < m_stretcher->getChannelCount(); ++i) {
m_outputDump[i] = new float[m_blockSize];
}
}
m_stretcher->retrieve(m_outputDump,
std::min(int(m_blockSize), available));
}
return features;
}
RubberBandVampPlugin::FeatureSet
RubberBandVampPlugin::Impl::getRemainingFeaturesRealTime()
{
return FeatureSet();
}
RubberBandVampPlugin::FeatureSet
RubberBandVampPlugin::Impl::createFeatures(size_t inputIncrement,
std::vector<int> &outputIncrements,
std::vector<float> &phaseResetDf,
std::vector<int> &exactPoints,
std::vector<float> &smoothedDf,
size_t baseCount,
bool includeFinal)
{
size_t actual = m_accumulatedIncrement;
double overallRatio = m_timeRatio * m_pitchRatio;
char label[200];
FeatureSet features;
int rate = m_sampleRate;
size_t epi = 0;
for (size_t i = 0; i < outputIncrements.size(); ++i) {
size_t frame = (baseCount + i) * inputIncrement;
int oi = outputIncrements[i];
bool hard = false;
bool soft = false;
if (oi < 0) {
oi = -oi;
hard = true;
}
if (epi < exactPoints.size() && int(i) == exactPoints[epi]) {
soft = true;
++epi;
}
double linear = (frame * overallRatio);
Vamp::RealTime t = Vamp::RealTime::frame2RealTime(frame, rate);
Feature feature;
feature.hasTimestamp = true;
feature.timestamp = t;
feature.values.push_back(oi);
feature.label = Vamp::RealTime::frame2RealTime(oi, rate).toText();
features[m_incrementsOutput].push_back(feature);
feature.values.clear();
feature.values.push_back(actual);
feature.label = Vamp::RealTime::frame2RealTime(actual, rate).toText();
features[m_aggregateIncrementsOutput].push_back(feature);
feature.values.clear();
feature.values.push_back(actual - linear);
sprintf(label, "expected %ld, actual %ld, difference %ld (%s ms)",
long(linear), long(actual), long(actual - linear),
// frame2RealTime expects an integer frame number,
// hence our multiplication factor
(Vamp::RealTime::frame2RealTime
(lrintf((actual - linear) * 1000), rate) / 1000)
.toText().c_str());
feature.label = label;
features[m_divergenceOutput].push_back(feature);
actual += oi;
char buf[30];
if (i < phaseResetDf.size()) {
feature.values.clear();
feature.values.push_back(phaseResetDf[i]);
sprintf(buf, "%d", baseCount + i);
feature.label = buf;
features[m_phaseResetDfOutput].push_back(feature);
}
if (i < smoothedDf.size()) {
feature.values.clear();
feature.values.push_back(smoothedDf[i]);
features[m_smoothedPhaseResetDfOutput].push_back(feature);
}
if (hard) {
feature.values.clear();
feature.label = "Phase Reset";
features[m_phaseResetPointsOutput].push_back(feature);
}
if (hard || soft) {
feature.values.clear();
feature.label = "Time Sync";
features[m_timeSyncPointsOutput].push_back(feature);
}
}
if (includeFinal) {
Vamp::RealTime t = Vamp::RealTime::frame2RealTime
(inputIncrement * (baseCount + outputIncrements.size()), rate);
Feature feature;
feature.hasTimestamp = true;
feature.timestamp = t;
feature.label = Vamp::RealTime::frame2RealTime(actual, rate).toText();
feature.values.clear();
feature.values.push_back(actual);
features[m_aggregateIncrementsOutput].push_back(feature);
float linear = ((baseCount + outputIncrements.size())
* inputIncrement * overallRatio);
feature.values.clear();
feature.values.push_back(actual - linear);
feature.label = // see earlier comment
(Vamp::RealTime::frame2RealTime //!!! update this as earlier label
(lrintf((actual - linear) * 1000), rate) / 1000)
.toText();
features[m_divergenceOutput].push_back(feature);
}
m_accumulatedIncrement = actual;
return features;
}

View file

@ -1,56 +0,0 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band
An audio time-stretching and pitch-shifting library.
Copyright 2007 Chris Cannam.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#ifndef _RUBBERBAND_VAMP_PLUGIN_H_
#define _RUBBERBAND_VAMP_PLUGIN_H_
#include <vamp-sdk/Plugin.h>
#include "RubberBandStretcher.h"
class RubberBandVampPlugin : public Vamp::Plugin
{
public:
RubberBandVampPlugin(float inputSampleRate);
virtual ~RubberBandVampPlugin();
bool initialise(size_t channels, size_t stepSize, size_t blockSize);
void reset();
InputDomain getInputDomain() const { return TimeDomain; }
std::string getIdentifier() const;
std::string getName() const;
std::string getDescription() const;
std::string getMaker() const;
int getPluginVersion() const;
std::string getCopyright() const;
ParameterList getParameterDescriptors() const;
float getParameter(std::string id) const;
void setParameter(std::string id, float value);
OutputList getOutputDescriptors() const;
FeatureSet process(const float *const *inputBuffers,
Vamp::RealTime timestamp);
FeatureSet getRemainingFeatures();
protected:
class Impl;
Impl *m_d;
};
#endif

View file

@ -1,32 +0,0 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band
An audio time-stretching and pitch-shifting library.
Copyright 2007 Chris Cannam.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
*/
#include <vamp/vamp.h>
#include <vamp-sdk/PluginAdapter.h>
#include "RubberBandVampPlugin.h"
static Vamp::PluginAdapter<RubberBandVampPlugin> rubberBandAdapter;
const VampPluginDescriptor *vampGetPluginDescriptor(unsigned int version,
unsigned int index)
{
if (version < 1) return 0;
switch (index) {
case 0: return rubberBandAdapter.getDescriptor();
default: return 0;
}
}

View file

@ -1 +0,0 @@
vamp:vamp-rubberband:rubberband::Time > Timestretch Analysis

View file

@ -1,30 +0,0 @@
# -*- python -*-
import os
import os.path
import glob
rubberband_files = glob.glob ('src/*.cpp')
Import('env install_prefix libraries')
rb = env.Copy()
rb.Merge ([libraries['fftw3f'],
libraries['fftw3'],
libraries['vamp'],
libraries['samplerate'],
libraries['sndfile-ardour']
])
rb.Append (CPPATH='#libs/rubberband/rubberband', CXXFLAGS="-Ilibs/rubberband/rubberband")
librb = rb.SharedLibrary('rubberband', rubberband_files)
Default(librb)
env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ardour2'), librb))
env.Alias('tarball', env.Distribute (env['DISTTREE'],
[ 'SConscript'] + rubberband_files + glob.glob('src/*.h')))