add (copy of 2.0-ongoing) rubberband to 3.0

git-svn-id: svn://localhost/ardour2/branches/3.0@3713 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Paul Davis 2008-09-10 21:35:32 +00:00
parent c86210a9d5
commit 7da75446b8
65 changed files with 17770 additions and 0 deletions

280
libs/rubberband/COPYING Normal file
View file

@ -0,0 +1,280 @@
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

189
libs/rubberband/Makefile.in Normal file
View file

@ -0,0 +1,189 @@
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

158
libs/rubberband/README Normal file
View file

@ -0,0 +1,158 @@
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.

View file

@ -0,0 +1,33 @@
# -*- python -*-
import os
import os.path
import glob
rubberband_files = glob.glob ('src/*.cpp')
Import('env install_prefix libraries')
rb = env.Clone()
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('rubberband/*.h') +
glob.glob('src/*.h')))

View file

@ -0,0 +1,38 @@
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

@ -0,0 +1,144 @@
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

@ -0,0 +1,10 @@
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

@ -0,0 +1,562 @@
/* -*- 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-2008 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_
#define RUBBERBAND_VERSION "1.2.0-gpl"
#define RUBBERBAND_API_MAJOR_VERSION 2
#define RUBBERBAND_API_MINOR_VERSION 0
#include <vector>
/**
* @mainpage RubberBand
*
* The Rubber Band API is contained in the single class
* RubberBand::RubberBandStretcher.
*
* Threading notes for real-time applications:
*
* Multiple instances of RubberBandStretcher may be created and used
* in separate threads concurrently. However, for any single instance
* of RubberBandStretcher, you may not call process() more than once
* concurrently, and you may not change the time or pitch ratio while
* a process() call is being executed (if the stretcher was created in
* "real-time mode"; in "offline mode" you can't change the ratios
* during use anyway).
*
* So you can run process() in its own thread if you like, but if you
* want to change ratios dynamically from a different thread, you will
* need some form of mutex in your code. Changing the time or pitch
* ratio is real-time safe except in extreme circumstances, so for
* most applications that may change these dynamically it probably
* makes most sense to do so from the same thread as calls process(),
* even if that is a real-time thread.
*/
namespace RubberBand
{
class RubberBandStretcher
{
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 OptionPhaseLaminar - Adjust phases when stretching in
* such a way as to try to retain the continuity of phase
* relationships between adjacent frequency bins whose phases
* are behaving in similar ways. This, the default setting,
* should give good results in most situations.
*
* \li \c OptionPhaseIndependent - Adjust the phase in each
* frequency bin independently from its neighbours. This
* usually results in a slightly 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.
*
* 7. Flags prefixed \c OptionFormant control the handling of
* formant shape (spectral envelope) when pitch-shifting. These
* options may be changed at any time.
*
* \li \c OptionFormantShifted - Apply no special formant
* processing. The spectral envelope will be pitch shifted as
* normal.
*
* \li \c OptionFormantPreserved - Preserve the spectral
* envelope of the unshifted signal. This permits shifting the
* note frequency without so substantially affecting the
* perceived pitch profile of the voice or instrument.
*
* 8. Flags prefixed \c OptionPitch control the method used for
* pitch shifting. These options may be changed at any time.
* They are only effective in realtime mode; in offline mode, the
* pitch-shift method is fixed.
*
* \li \c OptionPitchHighSpeed - Use a method with a CPU cost
* that is relatively moderate and predictable. This may
* sound less clear than OptionPitchHighQuality, especially
* for large pitch shifts.
* \li \c OptionPitchHighQuality - Use the highest quality
* method for pitch shifting. This method has a CPU cost
* approximately proportional to the required frequency shift.
* \li \c OptionPitchHighConsistency - Use the method that gives
* greatest consistency when used to create small variations in
* pitch around the 1.0-ratio level. Unlike the previous two
* options, this avoids discontinuities when moving across the
* 1.0 pitch scale in real-time; it also consumes more CPU than
* the others in the case where the pitch scale is exactly 1.0.
*/
enum Option {
OptionProcessOffline = 0x00000000,
OptionProcessRealTime = 0x00000001,
OptionStretchElastic = 0x00000000,
OptionStretchPrecise = 0x00000010,
OptionTransientsCrisp = 0x00000000,
OptionTransientsMixed = 0x00000100,
OptionTransientsSmooth = 0x00000200,
OptionPhaseLaminar = 0x00000000,
OptionPhaseIndependent = 0x00002000,
OptionThreadingAuto = 0x00000000,
OptionThreadingNever = 0x00010000,
OptionThreadingAlways = 0x00020000,
OptionWindowStandard = 0x00000000,
OptionWindowShort = 0x00100000,
OptionWindowLong = 0x00200000,
OptionFormantShifted = 0x00000000,
OptionFormantPreserved = 0x01000000,
OptionPitchHighSpeed = 0x00000000,
OptionPitchHighQuality = 0x02000000,
OptionPitchHighConsistency = 0x04000000
};
typedef int Options;
enum PresetOption {
DefaultOptions = 0x00000000,
PercussiveOptions = 0x00102000
};
/**
* 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);
~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).
*/
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).
*/
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).
*/
void setPitchScale(double scale);
/**
* Return the last time ratio value that was set (either on
* construction or with setTimeRatio()).
*/
double getTimeRatio() const;
/**
* Return the last pitch scaling ratio value that was set (either
* on construction or with setPitchScale()).
*/
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.
*/
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).
*/
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.
*/
void setPhaseOption(Options options);
/**
* Change an OptionFormant 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.
*/
void setFormantOption(Options options);
/**
* Change an OptionPitch 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).
*/
void setPitchOption(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.
*/
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).
*/
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).
*/
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.
*/
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.
*/
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.
*/
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.
*/
size_t retrieve(float *const *output, size_t samples) const;
/**
* Return the value of internal frequency cutoff value n.
*
* This function is not for general use.
*/
float getFrequencyCutoff(int n) const;
/**
* Set the value of internal frequency cutoff n to f Hz.
*
* This function is not for general use.
*/
void setFrequencyCutoff(int n, float f);
/**
* Retrieve the value of the internal input block increment value.
*
* This function is provided for diagnostic purposes only.
*/
size_t getInputIncrement() const;
/**
* In offline mode, retrieve the sequence of internal block
* increments for output, for the entire audio data, provided the
* stretch profile has been calculated. In realtime mode,
* retrieve any output increments that have accumulated since the
* last call to getOutputIncrements, to a limit of 16.
*
* This function is provided for diagnostic purposes only.
*/
std::vector<int> getOutputIncrements() const;
/**
* In offline mode, retrieve the sequence of internal phase reset
* detection function values, for the entire audio data, provided
* the stretch profile has been calculated. In realtime mode,
* retrieve any phase reset points that have accumulated since the
* last call to getPhaseResetCurve, to a limit of 16.
*
* This function is provided for diagnostic purposes only.
*/
std::vector<float> getPhaseResetCurve() const;
/**
* In offline mode, retrieve the sequence of internal frames for
* which exact timing has been sought, for the entire audio data,
* provided the stretch profile has been calculated. In realtime
* mode, return an empty sequence.
*
* This function is provided for diagnostic purposes only.
*/
std::vector<int> getExactTimePoints() const;
/**
* Return the number of channels this stretcher was constructed
* with.
*/
size_t getChannelCount() const;
/**
* Force the stretcher to calculate a stretch profile. Normally
* this happens automatically for the first process() call in
* offline mode.
*
* This function is provided for diagnostic purposes only.
*/
void calculateStretch();
/**
* Set the level of debug output. The value may be from 0 (errors
* only) to 3 (very verbose, with audible ticks in the output at
* phase reset points). The default is whatever has been set
* using setDefaultDebugLevel, or 0 if that function has not been
* called.
*/
void setDebugLevel(int level);
/**
* Set the default level of debug output for subsequently
* constructed stretchers.
*
* @see setDebugLevel
*/
static void setDefaultDebugLevel(int level);
protected:
class Impl;
Impl *m_d;
};
}
#endif

View file

@ -0,0 +1,58 @@
/* -*- 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

@ -0,0 +1,121 @@
/* -*- 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-2008 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_C_API_H_
#define _RUBBERBAND_C_API_H_
#ifdef __cplusplus
extern "C" {
#endif
#define RUBBERBAND_VERSION "1.2.0-gpl"
#define RUBBERBAND_API_MAJOR_VERSION 2
#define RUBBERBAND_API_MINOR_VERSION 0
/**
* This is a C-linkage interface to the Rubber Band time stretcher.
*
* This is a wrapper interface: the primary interface is in C++ and is
* defined and documented in RubberBandStretcher.h. The library
* itself is implemented in C++, and requires C++ standard library
* support even when using the C-linkage API.
*
* Please see RubberBandStretcher.h for documentation.
*
* If you are writing to the C++ API, do not include this header.
*/
enum RubberBandOption {
RubberBandOptionProcessOffline = 0x00000000,
RubberBandOptionProcessRealTime = 0x00000001,
RubberBandOptionStretchElastic = 0x00000000,
RubberBandOptionStretchPrecise = 0x00000010,
RubberBandOptionTransientsCrisp = 0x00000000,
RubberBandOptionTransientsMixed = 0x00000100,
RubberBandOptionTransientsSmooth = 0x00000200,
RubberBandOptionPhaseLaminar = 0x00000000,
RubberBandOptionPhaseIndependent = 0x00002000,
RubberBandOptionThreadingAuto = 0x00000000,
RubberBandOptionThreadingNever = 0x00010000,
RubberBandOptionThreadingAlways = 0x00020000,
RubberBandOptionWindowStandard = 0x00000000,
RubberBandOptionWindowShort = 0x00100000,
RubberBandOptionWindowLong = 0x00200000,
RubberBandOptionFormantShifted = 0x00000000,
RubberBandOptionFormantPreserved = 0x01000000,
RubberBandOptionPitchHighQuality = 0x00000000,
RubberBandOptionPitchHighSpeed = 0x02000000,
RubberBandOptionPitchHighConsistency = 0x04000000
};
typedef int RubberBandOptions;
struct RubberBandState_;
typedef struct RubberBandState_ *RubberBandState;
extern RubberBandState rubberband_new(unsigned int sampleRate,
unsigned int channels,
RubberBandOptions options,
double initialTimeRatio,
double initialPitchScale);
extern void rubberband_delete(RubberBandState);
extern void rubberband_reset(RubberBandState);
extern void rubberband_set_time_ratio(RubberBandState, double ratio);
extern void rubberband_set_pitch_scale(RubberBandState, double scale);
extern double rubberband_get_time_ratio(const RubberBandState);
extern double rubberband_get_pitch_scale(const RubberBandState);
extern unsigned int rubberband_get_latency(const RubberBandState);
extern void rubberband_set_transients_option(RubberBandState, RubberBandOptions options);
extern void rubberband_set_phase_option(RubberBandState, RubberBandOptions options);
extern void rubberband_set_formant_option(RubberBandState, RubberBandOptions options);
extern void rubberband_set_pitch_option(RubberBandState, RubberBandOptions options);
extern void rubberband_set_expected_input_duration(RubberBandState, unsigned int samples);
extern unsigned int rubberband_get_samples_required(const RubberBandState);
extern void rubberband_set_max_process_size(RubberBandState, unsigned int samples);
extern void rubberband_study(RubberBandState, const float *const *input, unsigned int samples, int final);
extern void rubberband_process(RubberBandState, const float *const *input, unsigned int samples, int final);
extern int rubberband_available(const RubberBandState);
extern unsigned int rubberband_retrieve(const RubberBandState, float *const *output, unsigned int samples);
extern unsigned int rubberband_get_channel_count(const RubberBandState);
extern void rubberband_calculate_stretch(RubberBandState);
extern void rubberband_set_debug_level(RubberBandState, int level);
extern void rubberband_set_default_debug_level(int level);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,44 @@
/* -*- 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-2008 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"
#include <iostream>
using namespace std;
namespace RubberBand
{
AudioCurve::AudioCurve(size_t sampleRate, size_t windowSize) :
m_sampleRate(sampleRate),
m_windowSize(windowSize)
{
}
AudioCurve::~AudioCurve()
{
}
float
AudioCurve::process(const double *R__ mag, size_t increment)
{
cerr << "WARNING: Using inefficient AudioCurve::process(double)" << endl;
float *tmp = new float[m_windowSize];
for (int i = 0; i < int(m_windowSize); ++i) tmp[i] = float(mag[i]);
float df = process(tmp, increment);
delete[] tmp;
return df;
}
}

View file

@ -0,0 +1,45 @@
/* -*- 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-2008 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>
#include "sysutils.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(const float *R__ mag, size_t increment) = 0;
virtual float process(const double *R__ mag, size_t increment);
virtual void reset() = 0;
protected:
size_t m_sampleRate;
size_t m_windowSize;
};
}
#endif

View file

@ -0,0 +1,47 @@
/* -*- 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-2008 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(const float *R__, size_t)
{
return 1.f;
}
}

View file

@ -0,0 +1,37 @@
/* -*- 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-2008 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(const float *R__ mag, size_t increment);
virtual void reset();
};
}
#endif

1367
libs/rubberband/src/FFT.cpp Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,870 @@
/* -*- 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 <cstdlib>
#include <cmath>
#include <iostream>
#include <map>
#include <cstdio>
#include <cstdlib>
#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
#error Building for FFTW-DOUBLE BOTH
// 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 */
#endif
#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;
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

@ -0,0 +1,869 @@
/* -*- 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 <cstdlib>
#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
#error Building for FFTW-DOUBLE BOTH
// 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 */
#endif
#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;
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()
{
}
}

File diff suppressed because it is too large Load diff

80
libs/rubberband/src/FFT.h Normal file
View file

@ -0,0 +1,80 @@
/* -*- 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-2008 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_
#include "sysutils.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(int size, int debugLevel = 0); // may throw InvalidSize
~FFT();
void forward(const double *R__ realIn, double *R__ realOut, double *R__ imagOut);
void forwardPolar(const double *R__ realIn, double *R__ magOut, double *R__ phaseOut);
void forwardMagnitude(const double *R__ realIn, double *R__ magOut);
void forward(const float *R__ realIn, float *R__ realOut, float *R__ imagOut);
void forwardPolar(const float *R__ realIn, float *R__ magOut, float *R__ phaseOut);
void forwardMagnitude(const float *R__ realIn, float *R__ magOut);
void inverse(const double *R__ realIn, const double *R__ imagIn, double *R__ realOut);
void inversePolar(const double *R__ magIn, const double *R__ phaseIn, double *R__ realOut);
void inverseCepstral(const double *R__ magIn, double *R__ cepOut);
void inverse(const float *R__ realIn, const float *R__ imagIn, float *R__ realOut);
void inversePolar(const float *R__ magIn, const float *R__ phaseIn, float *R__ realOut);
void inverseCepstral(const float *R__ magIn, float *R__ cepOut);
// 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

@ -0,0 +1,55 @@
/* -*- 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-2008 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(const float *R__ mag, size_t increment)
{
float result = 0.0;
const int sz = m_windowSize / 2;
for (int n = 0; n <= sz; ++n) {
result = result + mag[n] * n;
}
return result;
}
}

View file

@ -0,0 +1,39 @@
/* -*- 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-2008 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(const float *R__ mag, size_t increment);
virtual void reset();
};
}
#endif

View file

@ -0,0 +1,112 @@
/* -*- 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-2008 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 "Profiler.h"
#include <cmath>
namespace RubberBand
{
PercussiveAudioCurve::PercussiveAudioCurve(size_t sampleRate, size_t windowSize) :
AudioCurve(sampleRate, windowSize)
{
m_prevMag = new float[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)
{
m_windowSize = newSize;
delete[] m_prevMag;
m_prevMag = new float[m_windowSize/2 + 1];
reset();
}
float
PercussiveAudioCurve::process(const float *R__ mag, size_t increment)
{
static float threshold = powf(10.f, 0.15f); // 3dB rise in square of magnitude
static float zeroThresh = powf(10.f, -8);
size_t count = 0;
size_t nonZeroCount = 0;
const int sz = m_windowSize / 2;
for (int n = 1; n <= sz; ++n) {
bool above = ((mag[n] / m_prevMag[n]) >= threshold);
if (above) ++count;
if (mag[n] > zeroThresh) ++nonZeroCount;
}
for (int n = 1; n <= sz; ++n) {
m_prevMag[n] = mag[n];
}
if (nonZeroCount == 0) return 0;
else return float(count) / float(nonZeroCount);
}
float
PercussiveAudioCurve::process(const double *R__ mag, size_t increment)
{
Profiler profiler("PercussiveAudioCurve::process");
static double threshold = pow(10.0, 0.15); // 3dB rise in square of magnitude
static double zeroThresh = pow(10.0, -8);
size_t count = 0;
size_t nonZeroCount = 0;
const int sz = m_windowSize / 2;
for (int n = 1; n <= sz; ++n) {
bool above = ((mag[n] / m_prevMag[n]) >= threshold);
if (above) ++count;
if (mag[n] > zeroThresh) ++nonZeroCount;
}
for (int n = 1; n <= sz; ++n) {
m_prevMag[n] = mag[n];
}
if (nonZeroCount == 0) return 0;
else return float(count) / float(nonZeroCount);
}
}

View file

@ -0,0 +1,42 @@
/* -*- 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-2008 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(const float *R__ mag, size_t increment);
virtual float process(const double *R__ mag, size_t increment);
virtual void reset();
protected:
float *R__ m_prevMag;
};
}
#endif

View file

@ -0,0 +1,176 @@
/* -*- 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-2008 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 "Profiler.h"
#include <algorithm>
#include <set>
#include <string>
#include <map>
#include <cstdio>
namespace RubberBand {
#ifndef NO_TIMING
Profiler::ProfileMap
Profiler::m_profiles;
Profiler::WorstCallMap
Profiler::m_worstCalls;
void
Profiler::add(const char *id, float ms)
{
ProfileMap::iterator pmi = m_profiles.find(id);
if (pmi != m_profiles.end()) {
++pmi->second.first;
pmi->second.second += ms;
} else {
m_profiles[id] = TimePair(1, ms);
}
WorstCallMap::iterator wci = m_worstCalls.find(id);
if (wci != m_worstCalls.end()) {
if (ms > wci->second) wci->second = ms;
} else {
m_worstCalls[id] = ms;
}
}
void
Profiler::dump()
{
#ifdef PROFILE_CLOCKS
fprintf(stderr, "Profiling points [CPU time]:\n");
#else
fprintf(stderr, "Profiling points [Wall time]:\n");
#endif
fprintf(stderr, "\nBy name:\n");
typedef std::set<const char *, std::less<std::string> > StringSet;
StringSet profileNames;
for (ProfileMap::const_iterator i = m_profiles.begin();
i != m_profiles.end(); ++i) {
profileNames.insert(i->first);
}
for (StringSet::const_iterator i = profileNames.begin();
i != profileNames.end(); ++i) {
ProfileMap::const_iterator j = m_profiles.find(*i);
if (j == m_profiles.end()) continue;
const TimePair &pp(j->second);
fprintf(stderr, "%s(%d):\n", *i, pp.first);
fprintf(stderr, "\tReal: \t%f ms \t[%f ms total]\n",
(pp.second / pp.first),
(pp.second));
WorstCallMap::const_iterator k = m_worstCalls.find(*i);
if (k == m_worstCalls.end()) continue;
fprintf(stderr, "\tWorst:\t%f ms/call\n", k->second);
}
typedef std::multimap<float, const char *> TimeRMap;
typedef std::multimap<int, const char *> IntRMap;
TimeRMap totmap, avgmap, worstmap;
IntRMap ncallmap;
for (ProfileMap::const_iterator i = m_profiles.begin();
i != m_profiles.end(); ++i) {
totmap.insert(TimeRMap::value_type(i->second.second, i->first));
avgmap.insert(TimeRMap::value_type(i->second.second /
i->second.first, i->first));
ncallmap.insert(IntRMap::value_type(i->second.first, i->first));
}
for (WorstCallMap::const_iterator i = m_worstCalls.begin();
i != m_worstCalls.end(); ++i) {
worstmap.insert(TimeRMap::value_type(i->second, i->first));
}
fprintf(stderr, "\nBy total:\n");
for (TimeRMap::const_iterator i = totmap.end(); i != totmap.begin(); ) {
--i;
fprintf(stderr, "%-40s %f ms\n", i->second, i->first);
}
fprintf(stderr, "\nBy average:\n");
for (TimeRMap::const_iterator i = avgmap.end(); i != avgmap.begin(); ) {
--i;
fprintf(stderr, "%-40s %f ms\n", i->second, i->first);
}
fprintf(stderr, "\nBy worst case:\n");
for (TimeRMap::const_iterator i = worstmap.end(); i != worstmap.begin(); ) {
--i;
fprintf(stderr, "%-40s %f ms\n", i->second, i->first);
}
fprintf(stderr, "\nBy number of calls:\n");
for (IntRMap::const_iterator i = ncallmap.end(); i != ncallmap.begin(); ) {
--i;
fprintf(stderr, "%-40s %d\n", i->second, i->first);
}
}
Profiler::Profiler(const char* c) :
m_c(c),
m_ended(false)
{
#ifdef PROFILE_CLOCKS
m_start = clock();
#else
(void)gettimeofday(&m_start, 0);
#endif
}
Profiler::~Profiler()
{
if (!m_ended) end();
}
void
Profiler::end()
{
#ifdef PROFILE_CLOCKS
clock_t end = clock();
clock_t elapsed = end - m_start;
float ms = float((double(elapsed) / double(CLOCKS_PER_SEC)) * 1000.0);
#else
struct timeval tv;
(void)gettimeofday(&tv, 0);
tv.tv_sec -= m_start.tv_sec;
if (tv.tv_usec < m_start.tv_usec) {
tv.tv_usec += 1000000;
tv.tv_sec -= 1;
}
tv.tv_usec -= m_start.tv_usec;
float ms = float((double(tv.tv_sec) + (double(tv.tv_usec) / 1000000.0)) * 1000.0);
#endif
add(m_c, ms);
m_ended = true;
}
#endif
}

View file

@ -0,0 +1,91 @@
/* -*- 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-2008 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 _PROFILER_H_
#define _PROFILER_H_
#define NO_TIMING 1
//#define WANT_TIMING 1
//#define PROFILE_CLOCKS 1
#ifdef NDEBUG
#ifndef WANT_TIMING
#define NO_TIMING 1
#endif
#endif
#ifndef NO_TIMING
#ifdef PROFILE_CLOCKS
#include <time.h>
#else
#include "sysutils.h"
#ifndef _WIN32
#include <sys/time.h>
#endif
#endif
#endif
#include <map>
namespace RubberBand {
#ifndef NO_TIMING
class Profiler
{
public:
Profiler(const char *name);
~Profiler();
void end(); // same action as dtor
static void dump();
protected:
const char* m_c;
#ifdef PROFILE_CLOCKS
clock_t m_start;
#else
struct timeval m_start;
#endif
bool m_showOnDestruct;
bool m_ended;
typedef std::pair<int, float> TimePair;
typedef std::map<const char *, TimePair> ProfileMap;
typedef std::map<const char *, float> WorstCallMap;
static ProfileMap m_profiles;
static WorstCallMap m_worstCalls;
static void add(const char *, float);
};
#else
class Profiler
{
public:
Profiler(const char *) { }
~Profiler() { }
void update() const { }
void end() { }
static void dump() { }
};
#endif
}
#endif

View file

@ -0,0 +1,259 @@
/* -*- 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-2008 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 "Profiler.h"
#include <cstdlib>
#include <cmath>
#include <iostream>
#include <samplerate.h>
namespace RubberBand {
class ResamplerImpl
{
public:
virtual ~ResamplerImpl() { }
virtual int resample(const float *const R__ *const R__ in,
float *const R__ *const R__ out,
int incount,
float ratio,
bool final) = 0;
virtual void reset() = 0;
};
namespace Resamplers {
class D_SRC : public ResamplerImpl
{
public:
D_SRC(Resampler::Quality quality, int channels, int maxBufferSize,
int m_debugLevel);
~D_SRC();
int resample(const float *const R__ *const R__ in,
float *const R__ *const R__ out,
int incount,
float ratio,
bool final);
void reset();
protected:
SRC_STATE *m_src;
float *m_iin;
float *m_iout;
float m_lastRatio;
int m_channels;
int m_iinsize;
int m_ioutsize;
int m_debugLevel;
};
D_SRC::D_SRC(Resampler::Quality quality, int channels, int maxBufferSize,
int debugLevel) :
m_src(0),
m_iin(0),
m_iout(0),
m_lastRatio(1.f),
m_channels(channels),
m_iinsize(0),
m_ioutsize(0),
m_debugLevel(debugLevel)
{
if (m_debugLevel > 0) {
std::cerr << "Resampler::Resampler: using libsamplerate implementation"
<< std::endl;
}
int err = 0;
m_src = src_new(quality == Resampler::Best ? SRC_SINC_BEST_QUALITY :
quality == Resampler::Fastest ? SRC_LINEAR :
SRC_SINC_FASTEST,
channels, &err);
if (err) {
std::cerr << "Resampler::Resampler: failed to create libsamplerate resampler: "
<< src_strerror(err) << std::endl;
throw Resampler::ImplementationError; //!!! of course, need to catch this!
}
if (maxBufferSize > 0 && m_channels > 1) {
m_iinsize = maxBufferSize * m_channels;
m_ioutsize = maxBufferSize * m_channels * 2;
m_iin = allocFloat(m_iinsize);
m_iout = allocFloat(m_ioutsize);
}
reset();
}
D_SRC::~D_SRC()
{
src_delete(m_src);
if (m_iinsize > 0) {
free(m_iin);
}
if (m_ioutsize > 0) {
free(m_iout);
}
}
int
D_SRC::resample(const float *const R__ *const R__ in,
float *const R__ *const R__ out,
int incount,
float ratio,
bool final)
{
SRC_DATA data;
int outcount = lrintf(ceilf(incount * ratio));
if (m_channels == 1) {
data.data_in = const_cast<float *>(*in); //!!!???
data.data_out = *out;
} else {
if (incount * m_channels > m_iinsize) {
m_iin = allocFloat(m_iin, m_iinsize);
}
if (outcount * m_channels > m_ioutsize) {
m_iout = allocFloat(m_iout, m_ioutsize);
}
for (int i = 0; i < incount; ++i) {
for (int 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 = 0;
err = src_process(m_src, &data);
if (err) {
std::cerr << "Resampler::process: libsamplerate error: "
<< src_strerror(err) << std::endl;
throw Resampler::ImplementationError; //!!! of course, need to catch this!
}
if (m_channels > 1) {
for (int i = 0; i < data.output_frames_gen; ++i) {
for (int c = 0; c < m_channels; ++c) {
out[c][i] = m_iout[i * m_channels + c];
}
}
}
m_lastRatio = ratio;
return data.output_frames_gen;
}
void
D_SRC::reset()
{
src_reset(m_src);
}
} /* end namespace Resamplers */
Resampler::Resampler(Resampler::Quality quality, int channels,
int maxBufferSize, int debugLevel)
{
m_method = -1;
switch (quality) {
case Resampler::Best:
m_method = 1;
break;
case Resampler::FastestTolerable:
m_method = 1;
break;
case Resampler::Fastest:
m_method = 1;
break;
}
if (m_method == -1) {
std::cerr << "Resampler::Resampler(" << quality << ", " << channels
<< ", " << maxBufferSize << "): No implementation available!"
<< std::endl;
abort();
}
switch (m_method) {
case 0:
std::cerr << "Resampler::Resampler(" << quality << ", " << channels
<< ", " << maxBufferSize << "): No implementation available!"
<< std::endl;
abort();
break;
case 1:
d = new Resamplers::D_SRC(quality, channels, maxBufferSize, debugLevel);
break;
case 2:
std::cerr << "Resampler::Resampler(" << quality << ", " << channels
<< ", " << maxBufferSize << "): No implementation available!"
<< std::endl;
abort();
break;
}
}
Resampler::~Resampler()
{
delete d;
}
int
Resampler::resample(const float *const R__ *const R__ in,
float *const R__ *const R__ out,
int incount, float ratio, bool final)
{
Profiler profiler("Resampler::resample");
return d->resample(in, out, incount, ratio, final);
}
void
Resampler::reset()
{
d->reset();
}
}

View file

@ -0,0 +1,57 @@
/* -*- 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-2008 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>
#include "sysutils.h"
namespace RubberBand {
class ResamplerImpl;
class Resampler
{
public:
enum Quality { Best, FastestTolerable, Fastest };
enum Exception { ImplementationError };
/**
* 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, int channels, int maxBufferSize = 0,
int debugLevel = 0);
~Resampler();
int resample(const float *const R__ *const R__ in,
float *const R__ *const R__ out,
int incount,
float ratio,
bool final = false);
void reset();
protected:
ResamplerImpl *d;
int m_method;
};
}
#endif

View file

@ -0,0 +1,670 @@
/* -*- 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-2008 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 <cstring>
#include <sys/types.h>
#include <cstring>
#ifndef _WIN32
#include <sys/mman.h>
#endif
#include "Scavenger.h"
#include "Profiler.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(int n);
virtual ~RingBuffer();
/**
* Return the total capacity of the ring buffer in samples.
* (This is the argument n passed to the constructor.)
*/
int 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(int 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(int 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.
*/
int getReadSpace(int R = 0) const;
/**
* Return the amount of space available for writing, in samples.
*/
int 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.
*/
int read(T *R__ destination, int 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.
*/
int readAdding(T *R__ destination, int 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.
*/
int peek(T *R__ destination, int 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.
*/
int skip(int 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.
*/
int write(const T *source, int 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.
*/
int zero(int n);
protected:
T *R__ m_buffer;
volatile int m_writer;
volatile int m_readers[N];
int 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(int 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>
int
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(int 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(int newSize, int R) const
{
RingBuffer<T, N> *newBuffer = new RingBuffer<T, N>(newSize);
int w = m_writer;
int 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>
int
RingBuffer<T, N>::getReadSpace(int R) const
{
int writer = m_writer;
int reader = m_readers[R];
int 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>
int
RingBuffer<T, N>::getWriteSpace() const
{
int space = 0;
for (int i = 0; i < N; ++i) {
int writer = m_writer;
int reader = m_readers[i];
int here = (reader + m_size - writer - 1);
if (here >= m_size) here -= m_size;
if (i == 0 || here < space) space = here;
}
#ifdef DEBUG_RINGBUFFER
int 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>
int
RingBuffer<T, N>::read(T *R__ destination, int n, int R)
{
Profiler profiler("RingBuffer::read");
#ifdef DEBUG_RINGBUFFER
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::read(dest, " << n << ", " << R << ")" << std::endl;
#endif
int available = getReadSpace(R);
if (n > available) {
#ifdef DEBUG_RINGBUFFER
std::cerr << "WARNING: Only " << available << " samples available"
<< std::endl;
#endif
for (int i = available; i < n; ++i) {
destination[i] = 0;
}
n = available;
}
if (n == 0) return n;
int reader = m_readers[R];
int here = m_size - reader;
T *const R__ bufbase = m_buffer + reader;
if (here >= n) {
for (int i = 0; i < n; ++i) {
destination[i] = bufbase[i];
}
} else {
for (int i = 0; i < here; ++i) {
destination[i] = bufbase[i];
}
T *const R__ destbase = destination + here;
const int nh = n - here;
for (int i = 0; i < nh; ++i) {
destbase[i] = 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>
int
RingBuffer<T, N>::readAdding(T *R__ destination, int n, int R)
{
Profiler profiler("RingBuffer::readAdding");
#ifdef DEBUG_RINGBUFFER
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::readAdding(dest, " << n << ", " << R << ")" << std::endl;
#endif
int 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;
int reader = m_readers[R];
int here = m_size - reader;
const T *const R__ bufbase = m_buffer + reader;
if (here >= n) {
for (int i = 0; i < n; ++i) {
destination[i] += bufbase[i];
}
} else {
for (int i = 0; i < here; ++i) {
destination[i] += bufbase[i];
}
T *const R__ destbase = destination + here;
const int nh = n - here;
for (int i = 0; i < nh; ++i) {
destbase[i] += 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;
}
int 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>
int
RingBuffer<T, N>::peek(T *R__ destination, int n, int R) const
{
Profiler profiler("RingBuffer::peek");
#ifdef DEBUG_RINGBUFFER
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::peek(dest, " << n << ", " << R << ")" << std::endl;
#endif
int 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;
int reader = m_readers[R];
int here = m_size - reader;
const T *const R__ bufbase = m_buffer + reader;
if (here >= n) {
for (int i = 0; i < n; ++i) {
destination[i] = bufbase[i];
}
} else {
for (int i = 0; i < here; ++i) {
destination[i] = bufbase[i];
}
T *const R__ destbase = destination + here;
const int nh = n - here;
for (int i = 0; i < nh; ++i) {
destbase[i] = 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>
int
RingBuffer<T, N>::skip(int n, int R)
{
#ifdef DEBUG_RINGBUFFER
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::skip(" << n << ", " << R << ")" << std::endl;
#endif
int 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;
int reader = m_readers[R];
reader += n;
while (reader >= m_size) reader -= m_size;
m_readers[R] = reader;
return n;
}
template <typename T, int N>
int
RingBuffer<T, N>::write(const T *source, int n)
{
Profiler profiler("RingBuffer::write");
#ifdef DEBUG_RINGBUFFER
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::write(" << n << ")" << std::endl;
#endif
int 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;
int writer = m_writer;
int here = m_size - writer;
T *const R__ bufbase = m_buffer + writer;
if (here >= n) {
for (int i = 0; i < n; ++i) {
bufbase[i] = source[i];
}
} else {
for (int i = 0; i < here; ++i) {
bufbase[i] = source[i];
}
const int nh = n - here;
const T *const R__ srcbase = source + here;
T *const R__ buf = m_buffer;
for (int i = 0; i < nh; ++i) {
buf[i] = srcbase[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>
int
RingBuffer<T, N>::zero(int n)
{
Profiler profiler("RingBuffer::zero");
#ifdef DEBUG_RINGBUFFER
std::cerr << "RingBuffer<T," << N << ">[" << this << "]::zero(" << n << ")" << std::endl;
#endif
int 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;
int writer = m_writer;
int here = m_size - writer;
T *const R__ bufbase = m_buffer + writer;
if (here >= n) {
for (int i = 0; i < n; ++i) {
bufbase[i] = 0;
}
} else {
for (int i = 0; i < here; ++i) {
bufbase[i] = 0;
}
const int nh = n - here;
for (int i = 0; i < nh; ++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;
}
}
//#include "RingBuffer.cpp"
#endif // _RINGBUFFER_H_

View file

@ -0,0 +1,200 @@
/* -*- 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-2008 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) :
m_d(new Impl(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::setFormantOption(Options options)
{
m_d->setFormantOption(options);
}
void
RubberBandStretcher::setPitchOption(Options options)
{
m_d->setPitchOption(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

@ -0,0 +1,202 @@
/* -*- 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-2008 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 <iostream>
#ifndef WIN32
#include <sys/time.h>
#endif
#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

@ -0,0 +1,69 @@
/* -*- 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-2008 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 "SilentAudioCurve.h"
#include <cmath>
namespace RubberBand
{
SilentAudioCurve::SilentAudioCurve(size_t sampleRate, size_t windowSize) :
AudioCurve(sampleRate, windowSize)
{
}
SilentAudioCurve::~SilentAudioCurve()
{
}
void
SilentAudioCurve::reset()
{
}
void
SilentAudioCurve::setWindowSize(size_t newSize)
{
m_windowSize = newSize;
}
float
SilentAudioCurve::process(const float *R__ mag, size_t)
{
const int hs = m_windowSize / 2;
static float threshold = powf(10.f, -6);
for (int i = 0; i <= hs; ++i) {
if (mag[i] > threshold) return 0.f;
}
return 1.f;
}
float
SilentAudioCurve::process(const double *R__ mag, size_t)
{
const int hs = m_windowSize / 2;
static double threshold = pow(10.0, -6);
for (int i = 0; i <= hs; ++i) {
if (mag[i] > threshold) return 0.f;
}
return 1.f;
}
}

View file

@ -0,0 +1,38 @@
/* -*- 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-2008 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 _SILENT_AUDIO_CURVE_H_
#define _SILENT_AUDIO_CURVE_H_
#include "AudioCurve.h"
namespace RubberBand
{
class SilentAudioCurve : public AudioCurve
{
public:
SilentAudioCurve(size_t sampleRate, size_t windowSize);
virtual ~SilentAudioCurve();
virtual void setWindowSize(size_t newSize);
virtual float process(const float *R__ mag, size_t increment);
virtual float process(const double *R__ mag, size_t increment);
virtual void reset();
};
}
#endif

View file

@ -0,0 +1,69 @@
/* -*- 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-2008 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 float[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)
{
delete[] m_prevMag;
m_windowSize = newSize;
m_prevMag = new float[m_windowSize/2 + 1];
reset();
}
float
SpectralDifferenceAudioCurve::process(const float *R__ 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

@ -0,0 +1,42 @@
/* -*- 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-2008 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(const float *R__ mag, size_t increment);
virtual void reset();
protected:
float *R__ m_prevMag;
};
}
#endif

View file

@ -0,0 +1,799 @@
/* -*- 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-2008 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 <algorithm>
#include <math.h>
#include <algorithm>
#include <iostream>
#include <deque>
#include <set>
#include <cassert>
#include <algorithm>
#include "sysutils.h"
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,
float df,
size_t increment)
{
if (increment == 0) increment = m_increment;
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.35f;
if (ratio > 1) transientThreshold = 0.25f;
if (m_useHardPeaks && df > m_prevDf * 1.1f && df > transientThreshold) {
isTransient = true;
}
if (m_debugLevel > 2) {
std::cerr << "df = " << df << ", prevDf = " << m_prevDf
<< ", thresh = " << transientThreshold << std::endl;
}
m_prevDf = df;
bool ratioChanged = (ratio != m_prevRatio);
m_prevRatio = ratio;
if (isTransient && m_transientAmnesty == 0) {
if (m_debugLevel > 1) {
std::cerr << "StretchCalculator::calculateSingle: transient"
<< std::endl;
}
m_divergence += increment - (increment * ratio);
// as in offline mode, 0.05 sec approx min between transients
m_transientAmnesty =
lrint(ceil(double(m_sampleRate) / (20 * double(increment))));
m_recovery = m_divergence / ((m_sampleRate / 10.0) / increment);
return -int(increment);
}
if (ratioChanged) {
m_recovery = m_divergence / ((m_sampleRate / 10.0) / increment);
}
if (m_transientAmnesty > 0) --m_transientAmnesty;
int incr = lrint(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((increment * ratio) / 2)) {
incr = lrint((increment * ratio) / 2);
} else if (incr > lrint(increment * ratio * 2)) {
incr = lrint(increment * ratio * 2);
}
double divdiff = (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) / 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

@ -0,0 +1,98 @@
/* -*- 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-2008 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().
*
* If increment is non-zero, use it for the input increment for
* this block in preference to m_increment.
*/
virtual int calculateSingle(double ratio, float curveValue,
size_t increment = 0);
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

@ -0,0 +1,287 @@
/* -*- 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-2008 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 "StretcherChannelData.h"
#include "Resampler.h"
namespace RubberBand
{
RubberBandStretcher::Impl::ChannelData::ChannelData(size_t windowSize,
int overSample,
size_t outbufSize) :
oversample(overSample)
{
std::set<size_t> s;
construct(s, windowSize, outbufSize);
}
RubberBandStretcher::Impl::ChannelData::ChannelData(const std::set<size_t> &windowSizes,
int overSample,
size_t initialWindowSize,
size_t outbufSize) :
oversample(overSample)
{
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;
}
// max size of the real "half" of freq data
size_t realSize = (maxSize * oversample)/2 + 1;
// 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 = allocDouble(realSize);
phase = allocDouble(realSize);
prevPhase = allocDouble(realSize);
prevError = allocDouble(realSize);
unwrappedPhase = allocDouble(realSize);
envelope = allocDouble(realSize);
freqPeak = new size_t[realSize];
fltbuf = allocFloat(maxSize);
accumulator = allocFloat(maxSize);
windowAccumulator = allocFloat(maxSize);
for (std::set<size_t>::const_iterator i = windowSizes.begin();
i != windowSizes.end(); ++i) {
ffts[*i] = new FFT(*i * oversample);
ffts[*i]->initDouble();
}
if (windowSizes.find(initialWindowSize) == windowSizes.end()) {
ffts[initialWindowSize] = new FFT(initialWindowSize * oversample);
ffts[initialWindowSize]->initDouble();
}
fft = ffts[initialWindowSize];
dblbuf = fft->getDoubleTimeBuffer();
resampler = 0;
resamplebuf = 0;
resamplebufSize = 0;
reset();
for (size_t i = 0; i < realSize; ++i) {
freqPeak[i] = 0;
}
for (size_t i = 0; i < initialWindowSize * oversample; ++i) {
dblbuf[i] = 0.0;
}
}
void
RubberBandStretcher::Impl::ChannelData::setWindowSize(size_t windowSize)
{
size_t oldSize = inbuf->getSize();
size_t realSize = (windowSize * oversample) / 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 * oversample);
ffts[windowSize]->initDouble();
}
fft = ffts[windowSize];
dblbuf = fft->getDoubleTimeBuffer();
for (size_t i = 0; i < windowSize * oversample; ++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;
prevError[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
mag = allocDouble(mag, realSize);
phase = allocDouble(phase, realSize);
prevPhase = allocDouble(prevPhase, realSize);
prevError = allocDouble(prevError, realSize);
unwrappedPhase = allocDouble(unwrappedPhase, realSize);
envelope = allocDouble(envelope, realSize);
delete[] freqPeak;
freqPeak = new size_t[realSize];
fltbuf = allocFloat(fltbuf, windowSize);
// But we do want to preserve data in these
float *newAcc = allocFloat(windowSize);
for (size_t i = 0; i < oldSize; ++i) newAcc[i] = accumulator[i];
freeFloat(accumulator);
accumulator = newAcc;
newAcc = allocFloat(windowSize);
for (size_t i = 0; i < oldSize; ++i) newAcc[i] = windowAccumulator[i];
freeFloat(windowAccumulator);
windowAccumulator = newAcc;
//!!! and resampler?
for (size_t i = 0; i < realSize; ++i) {
freqPeak[i] = 0;
}
for (size_t i = 0; i < windowSize; ++i) {
fltbuf[i] = 0.f;
}
if (ffts.find(windowSize) == ffts.end()) {
ffts[windowSize] = new FFT(windowSize * oversample);
ffts[windowSize]->initDouble();
}
fft = ffts[windowSize];
dblbuf = fft->getDoubleTimeBuffer();
for (size_t i = 0; i < windowSize * oversample; ++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;
}
}
void
RubberBandStretcher::Impl::ChannelData::setResampleBufSize(size_t sz)
{
resamplebuf = allocFloat(resamplebuf, sz);
resamplebufSize = sz;
}
RubberBandStretcher::Impl::ChannelData::~ChannelData()
{
delete resampler;
freeFloat(resamplebuf);
delete inbuf;
delete outbuf;
freeDouble(mag);
freeDouble(phase);
freeDouble(prevPhase);
freeDouble(prevError);
freeDouble(unwrappedPhase);
freeDouble(envelope);
delete[] freqPeak;
freeFloat(accumulator);
freeFloat(windowAccumulator);
freeFloat(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;
unchanged = true;
draining = false;
outputComplete = false;
}
}

View file

@ -0,0 +1,135 @@
/* -*- 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-2008 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>
//#define EXPERIMENT 1
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, int overSample, 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,
int overSample, 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);
/**
* Set the resampler buffer size. Default if not called is no
* buffer allocated at all.
*/
void setResampleBufSize(size_t resamplebufSize);
RingBuffer<float> *inbuf;
RingBuffer<float> *outbuf;
double *mag;
double *phase;
double *prevPhase;
double *prevError;
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
double *envelope; // for cepstral formant shift
bool unchanged;
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;
int oversample;
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

@ -0,0 +1,202 @@
/* -*- 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-2008 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(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 setFormantOption(Options);
void setPitchOption(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:
size_t m_sampleRate;
size_t m_channels;
size_t consumeChannel(size_t channel, const float *input,
size_t samples, bool final);
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 formantShiftChunk(size_t channel);
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
bool resampleBeforeStretching() const;
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;
std::vector<bool> m_silence;
int m_silentHistory;
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;
AudioCurve *m_silentAudioCurve;
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

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,583 @@
/* -*- 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-2008 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 <cstdlib>
#include <iostream>
#include <cstdlib>
#include <cstdlib>
#include <sys/time.h>
#include <time.h>
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()
#ifndef NO_THREAD_CHECKS
:
m_lockedBy(-1)
#endif
{
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()
{
#ifndef NO_THREAD_CHECKS
DWORD tid = GetCurrentThreadId();
if (m_lockedBy == tid) {
cerr << "ERROR: Deadlock on mutex " << &m_mutex << endl;
}
#endif
#ifdef DEBUG_MUTEX
cerr << "MUTEX DEBUG: " << (void *)tid << ": Want to lock mutex " << &m_mutex << endl;
#endif
WaitForSingleObject(m_mutex, INFINITE);
#ifndef NO_THREAD_CHECKS
m_lockedBy = tid;
#endif
#ifdef DEBUG_MUTEX
cerr << "MUTEX DEBUG: " << (void *)tid << ": Locked mutex " << &m_mutex << endl;
#endif
}
void
Mutex::unlock()
{
#ifndef NO_THREAD_CHECKS
DWORD tid = GetCurrentThreadId();
if (m_lockedBy != tid) {
cerr << "ERROR: Mutex " << &m_mutex << " not owned by unlocking thread" << endl;
return;
}
#endif
#ifdef DEBUG_MUTEX
cerr << "MUTEX DEBUG: " << (void *)tid << ": Unlocking mutex " << &m_mutex << endl;
#endif
#ifndef NO_THREAD_CHECKS
m_lockedBy = -1;
#endif
ReleaseMutex(m_mutex);
}
bool
Mutex::trylock()
{
#ifndef NO_THREAD_CHECKS
DWORD tid = GetCurrentThreadId();
#endif
DWORD result = WaitForSingleObject(m_mutex, 0);
if (result == WAIT_TIMEOUT || result == WAIT_FAILED) {
#ifdef DEBUG_MUTEX
cerr << "MUTEX DEBUG: " << (void *)tid << ": Mutex " << &m_mutex << " unavailable" << endl;
#endif
return false;
} else {
#ifndef NO_THREAD_CHECKS
m_lockedBy = tid;
#endif
#ifdef DEBUG_MUTEX
cerr << "MUTEX DEBUG: " << (void *)tid << ": Locked mutex " << &m_mutex << " (from trylock)" << endl;
#endif
return true;
}
}
Condition::Condition(string name) :
m_locked(false)
#ifdef DEBUG_CONDITION
, m_name(name)
#endif
{
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()
#ifndef NO_THREAD_CHECKS
:
m_lockedBy(0),
m_locked(false)
#endif
{
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()
{
#ifndef NO_THREAD_CHECKS
pthread_t tid = pthread_self();
if (m_locked && m_lockedBy == tid) {
cerr << "ERROR: Deadlock on mutex " << &m_mutex << endl;
}
#endif
#ifdef DEBUG_MUTEX
cerr << "MUTEX DEBUG: " << (void *)tid << ": Want to lock mutex " << &m_mutex << endl;
#endif
pthread_mutex_lock(&m_mutex);
#ifndef NO_THREAD_CHECKS
m_lockedBy = tid;
m_locked = true;
#endif
#ifdef DEBUG_MUTEX
cerr << "MUTEX DEBUG: " << (void *)tid << ": Locked mutex " << &m_mutex << endl;
#endif
}
void
Mutex::unlock()
{
#ifndef NO_THREAD_CHECKS
pthread_t tid = pthread_self();
if (!m_locked) {
cerr << "ERROR: Mutex " << &m_mutex << " not locked in unlock" << endl;
return;
} else if (m_lockedBy != tid) {
cerr << "ERROR: Mutex " << &m_mutex << " not owned by unlocking thread" << endl;
return;
}
#endif
#ifdef DEBUG_MUTEX
cerr << "MUTEX DEBUG: " << (void *)tid << ": Unlocking mutex " << &m_mutex << endl;
#endif
#ifndef NO_THREAD_CHECKS
m_locked = false;
#endif
pthread_mutex_unlock(&m_mutex);
}
bool
Mutex::trylock()
{
#ifndef NO_THREAD_CHECKS
pthread_t tid = pthread_self();
#endif
if (pthread_mutex_trylock(&m_mutex)) {
#ifdef DEBUG_MUTEX
cerr << "MUTEX DEBUG: " << (void *)tid << ": Mutex " << &m_mutex << " unavailable" << endl;
#endif
return false;
} else {
#ifndef NO_THREAD_CHECKS
m_lockedBy = tid;
m_locked = true;
#endif
#ifdef DEBUG_MUTEX
cerr << "MUTEX DEBUG: " << (void *)tid << ": Locked mutex " << &m_mutex << " (from trylock)" << endl;
#endif
return true;
}
}
Condition::Condition(string name) :
m_locked(false)
#ifdef DEBUG_CONDITION
, m_name(name)
#endif
{
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

@ -0,0 +1,142 @@
/* -*- 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-2008 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>
//#define DEBUG_THREAD 1
//#define DEBUG_MUTEX 1
//#define DEBUG_CONDITION 1
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;
#ifndef NO_THREAD_CHECKS
DWORD m_lockedBy;
#endif
#else
pthread_mutex_t m_mutex;
#ifndef NO_THREAD_CHECKS
pthread_t m_lockedBy;
bool m_locked;
#endif
#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;
HANDLE m_condition;
bool m_locked;
#else
pthread_mutex_t m_mutex;
pthread_cond_t m_condition;
bool m_locked;
#endif
#ifdef DEBUG_CONDITION
std::string m_name;
#endif
};
}
#endif

View file

@ -0,0 +1,17 @@
/* -*- 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-2008 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 "Window.h"

View file

@ -0,0 +1,183 @@
/* -*- 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-2008 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 <cstdlib>
#include <cmath>
#include <cstdlib>
#include <iostream>
#include <cstdlib>
#include <map>
#include "sysutils.h"
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, int 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 *R__ src) const
{
const int sz = m_size;
for (int i = 0; i < sz; ++i) {
src[i] *= m_cache[i];
}
}
void cut(T *R__ src, T *dst) const {
const int sz = m_size;
for (int i = 0; i < sz; ++i) {
dst[i] = src[i];
}
for (int i = 0; i < sz; ++i) {
dst[i] *= m_cache[i];
}
}
T getArea() { return m_area; }
T getValue(int i) { return m_cache[i]; }
WindowType getType() const { return m_type; }
int getSize() const { return m_size; }
protected:
WindowType m_type;
int m_size;
T *R__ 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

@ -0,0 +1,73 @@
/*
** Copyright (C) 2001 Erik de Castro Lopo <erikd AT mega-nerd DOT com>
**
** Permission to use, copy, modify, distribute, and sell this file for any
** purpose is hereby granted without fee, provided that the above copyright
** and this permission notice appear in all copies. No representations are
** made about the suitability of this software for any purpose. It is
** provided "as is" without express or implied warranty.
*/
/* Version 1.1 */
/*============================================================================
** On Intel Pentium processors (especially PIII and probably P4), converting
** from float to int is very slow. To meet the C specs, the code produced by
** most C compilers targeting Pentium needs to change the FPU rounding mode
** before the float to int conversion is performed.
**
** Changing the FPU rounding mode causes the FPU pipeline to be flushed. It
** is this flushing of the pipeline which is so slow.
**
** Fortunately the ISO C99 specifications define the functions lrint, lrintf,
** llrint and llrintf which fix this problem as a side effect.
**
** On Unix-like systems, the configure process should have detected the
** presence of these functions. If they weren't found we have to replace them
** here with a standard C cast.
*/
/*
** The C99 prototypes for lrint and lrintf are as follows:
**
** long int lrintf (float x) ;
** long int lrint (double x) ;
*/
#if (defined (WIN32) || defined (_WIN32))
#include <math.h>
/* Win32 doesn't seem to have these functions.
** Therefore implement inline versions of these functions here.
*/
__inline long int
lrint (double flt)
{ int intgr;
_asm
{ fld flt
fistp intgr
} ;
return intgr ;
}
__inline long int
lrintf (float flt)
{ int intgr;
_asm
{ fld flt
fistp intgr
} ;
return intgr ;
}
#endif

View file

@ -0,0 +1,112 @@
/*
* Copyright (c) 1987, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int opterr = 1, /* if error message should be printed */
optind = 1, /* index into parent argv vector */
optopt, /* character checked for validity */
optreset; /* reset getopt */
char *optarg; /* argument associated with option */
#define BADCH (int)'?'
#define BADARG (int)':'
#define EMSG ""
/*
* getopt --
* Parse argc/argv argument vector.
*/
int
getopt(nargc, nargv, ostr)
int nargc;
char * const *nargv;
const char *ostr;
{
static char *place = EMSG; /* option letter processing */
char *oli; /* option letter list index */
if (optreset || !*place) { /* update scanning pointer */
optreset = 0;
if (optind >= nargc || *(place = nargv[optind]) != '-') {
place = EMSG;
return (-1);
}
if (place[1] && *++place == '-') { /* found "--" */
++optind;
place = EMSG;
return (-1);
}
} /* option letter okay? */
if ((optopt = (int)*place++) == (int)':' ||
!(oli = strchr(ostr, optopt))) {
/*
* if the user didn't specify '-' as an option,
* assume it means -1.
*/
if (optopt == (int)'-')
return (-1);
if (!*place)
++optind;
if (opterr && *ostr != ':' && optopt != BADCH)
(void)fprintf(stderr, "%s: illegal option -- %c\n",
"progname", optopt);
return (BADCH);
}
if (*++oli != ':') { /* don't need argument */
optarg = NULL;
if (!*place)
++optind;
}
else { /* need an argument */
if (*place) /* no white space */
optarg = place;
else if (nargc <= ++optind) { /* no arg */
place = EMSG;
if (*ostr == ':')
return (BADARG);
if (opterr)
(void)fprintf(stderr,
"%s: option requires an argument -- %c\n",
"progname", optopt);
return (BADCH);
}
else /* white space */
optarg = nargv[optind];
place = EMSG;
++optind;
}
return (optopt); /* dump back option letter */
}

View file

@ -0,0 +1,110 @@
/* $NetBSD: getopt.h,v 1.4 2000/07/07 10:43:54 ad Exp $ */
/* $FreeBSD: src/include/getopt.h,v 1.1 2002/09/29 04:14:30 eric Exp $ */
/*-
* Copyright (c) 2000 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Dieter Baron and Thomas Klausner.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the NetBSD
* Foundation, Inc. and its contributors.
* 4. Neither the name of The NetBSD Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _GETOPT_H_
#define _GETOPT_H_
#ifdef _WIN32
/* from <sys/cdefs.h> */
# ifdef __cplusplus
# define __BEGIN_DECLS extern "C" {
# define __END_DECLS }
# else
# define __BEGIN_DECLS
# define __END_DECLS
# endif
# define __P(args) args
#endif
/*#ifndef _WIN32
#include <sys/cdefs.h>
#include <unistd.h>
#endif*/
#ifdef _WIN32
# if !defined(GETOPT_API)
# define GETOPT_API __declspec(dllimport)
# endif
#endif
/*
* Gnu like getopt_long() and BSD4.4 getsubopt()/optreset extensions
*/
#if !defined(_POSIX_SOURCE) && !defined(_XOPEN_SOURCE)
#define no_argument 0
#define required_argument 1
#define optional_argument 2
struct option {
/* name of long option */
const char *name;
/*
* one of no_argument, required_argument, and optional_argument:
* whether option takes an argument
*/
int has_arg;
/* if not NULL, set *flag to val when option found */
int *flag;
/* if flag not NULL, value to set *flag to; else return value */
int val;
};
__BEGIN_DECLS
GETOPT_API int getopt_long __P((int, char * const *, const char *,
const struct option *, int *));
__END_DECLS
#endif
#ifdef _WIN32
/* These are global getopt variables */
__BEGIN_DECLS
GETOPT_API extern int opterr, /* if error message should be printed */
optind, /* index into parent argv vector */
optopt, /* character checked for validity */
optreset; /* reset getopt */
GETOPT_API extern char* optarg; /* argument associated with option */
/* Original getopt */
GETOPT_API int getopt __P((int, char * const *, const char *));
__END_DECLS
#endif
#endif /* !_GETOPT_H_ */

View file

@ -0,0 +1,547 @@
/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */
/* $FreeBSD: src/lib/libc/stdlib/getopt_long.c,v 1.2 2002/10/16 22:18:42 alfred Exp $ */
/*-
* Copyright (c) 2000 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Dieter Baron and Thomas Klausner.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the NetBSD
* Foundation, Inc. and its contributors.
* 4. Neither the name of The NetBSD Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
/* Windows needs warnx(). We change the definition though:
* 1. (another) global is defined, opterrmsg, which holds the error message
* 2. errors are always printed out on stderr w/o the program name
* Note that opterrmsg always gets set no matter what opterr is set to. The
* error message will not be printed if opterr is 0 as usual.
*/
#include "getopt.h"
#include <stdio.h>
#include <stdarg.h>
GETOPT_API extern char opterrmsg[128];
char opterrmsg[128]; /* last error message is stored here */
static void warnx(int print_error, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (fmt != NULL)
_vsnprintf(opterrmsg, 128, fmt, ap);
else
opterrmsg[0]='\0';
va_end(ap);
if (print_error) {
fprintf(stderr, opterrmsg);
fprintf(stderr, "\n");
}
}
#endif /*_WIN32*/
/* not part of the original file */
#ifndef _DIAGASSERT
#define _DIAGASSERT(X)
#endif
#if HAVE_CONFIG_H && !HAVE_GETOPT_LONG && !HAVE_DECL_OPTIND
#define REPLACE_GETOPT
#endif
#ifdef REPLACE_GETOPT
#ifdef __weak_alias
__weak_alias(getopt,_getopt)
#endif
int opterr = 1; /* if error message should be printed */
int optind = 1; /* index into parent argv vector */
int optopt = '?'; /* character checked for validity */
int optreset; /* reset getopt */
char *optarg; /* argument associated with option */
#elif HAVE_CONFIG_H && !HAVE_DECL_OPTRESET
static int optreset;
#endif
#ifdef __weak_alias
__weak_alias(getopt_long,_getopt_long)
#endif
#if !HAVE_GETOPT_LONG
#define IGNORE_FIRST (*options == '-' || *options == '+')
#define PRINT_ERROR ((opterr) && ((*options != ':') \
|| (IGNORE_FIRST && options[1] != ':')))
#define IS_POSIXLY_CORRECT (getenv("POSIXLY_CORRECT") != NULL)
#define PERMUTE (!IS_POSIXLY_CORRECT && !IGNORE_FIRST)
/* XXX: GNU ignores PC if *options == '-' */
#define IN_ORDER (!IS_POSIXLY_CORRECT && *options == '-')
/* return values */
#define BADCH (int)'?'
#define BADARG ((IGNORE_FIRST && options[1] == ':') \
|| (*options == ':') ? (int)':' : (int)'?')
#define INORDER (int)1
#define EMSG ""
static int getopt_internal(int, char * const *, const char *);
static int gcd(int, int);
static void permute_args(int, int, int, char * const *);
static char *place = EMSG; /* option letter processing */
/* XXX: set optreset to 1 rather than these two */
static int nonopt_start = -1; /* first non option argument (for permute) */
static int nonopt_end = -1; /* first option after non options (for permute) */
/* Error messages */
static const char recargchar[] = "option requires an argument -- %c";
static const char recargstring[] = "option requires an argument -- %s";
static const char ambig[] = "ambiguous option -- %.*s";
static const char noarg[] = "option doesn't take an argument -- %.*s";
static const char illoptchar[] = "unknown option -- %c";
static const char illoptstring[] = "unknown option -- %s";
/*
* Compute the greatest common divisor of a and b.
*/
static int
gcd(a, b)
int a;
int b;
{
int c;
c = a % b;
while (c != 0) {
a = b;
b = c;
c = a % b;
}
return b;
}
/*
* Exchange the block from nonopt_start to nonopt_end with the block
* from nonopt_end to opt_end (keeping the same order of arguments
* in each block).
*/
static void
permute_args(panonopt_start, panonopt_end, opt_end, nargv)
int panonopt_start;
int panonopt_end;
int opt_end;
char * const *nargv;
{
int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
char *swap;
_DIAGASSERT(nargv != NULL);
/*
* compute lengths of blocks and number and size of cycles
*/
nnonopts = panonopt_end - panonopt_start;
nopts = opt_end - panonopt_end;
ncycle = gcd(nnonopts, nopts);
cyclelen = (opt_end - panonopt_start) / ncycle;
for (i = 0; i < ncycle; i++) {
cstart = panonopt_end+i;
pos = cstart;
for (j = 0; j < cyclelen; j++) {
if (pos >= panonopt_end)
pos -= nnonopts;
else
pos += nopts;
swap = nargv[pos];
/* LINTED const cast */
((char **) nargv)[pos] = nargv[cstart];
/* LINTED const cast */
((char **)nargv)[cstart] = swap;
}
}
}
/*
* getopt_internal --
* Parse argc/argv argument vector. Called by user level routines.
* Returns -2 if -- is found (can be long option or end of options marker).
*/
static int
getopt_internal(nargc, nargv, options)
int nargc;
char * const *nargv;
const char *options;
{
char *oli; /* option letter list index */
int optchar;
_DIAGASSERT(nargv != NULL);
_DIAGASSERT(options != NULL);
optarg = NULL;
/*
* XXX Some programs (like rsyncd) expect to be able to
* XXX re-initialize optind to 0 and have getopt_long(3)
* XXX properly function again. Work around this braindamage.
*/
if (optind == 0)
optind = 1;
if (optreset)
nonopt_start = nonopt_end = -1;
start:
if (optreset || !*place) { /* update scanning pointer */
optreset = 0;
if (optind >= nargc) { /* end of argument vector */
place = EMSG;
if (nonopt_end != -1) {
/* do permutation, if we have to */
permute_args(nonopt_start, nonopt_end,
optind, nargv);
optind -= nonopt_end - nonopt_start;
}
else if (nonopt_start != -1) {
/*
* If we skipped non-options, set optind
* to the first of them.
*/
optind = nonopt_start;
}
nonopt_start = nonopt_end = -1;
return -1;
}
if ((*(place = nargv[optind]) != '-')
|| (place[1] == '\0')) { /* found non-option */
place = EMSG;
if (IN_ORDER) {
/*
* GNU extension:
* return non-option as argument to option 1
*/
optarg = nargv[optind++];
return INORDER;
}
if (!PERMUTE) {
/*
* if no permutation wanted, stop parsing
* at first non-option
*/
return -1;
}
/* do permutation */
if (nonopt_start == -1)
nonopt_start = optind;
else if (nonopt_end != -1) {
permute_args(nonopt_start, nonopt_end,
optind, nargv);
nonopt_start = optind -
(nonopt_end - nonopt_start);
nonopt_end = -1;
}
optind++;
/* process next argument */
goto start;
}
if (nonopt_start != -1 && nonopt_end == -1)
nonopt_end = optind;
if (place[1] && *++place == '-') { /* found "--" */
place++;
return -2;
}
}
if ((optchar = (int)*place++) == (int)':' ||
(oli = strchr(options + (IGNORE_FIRST ? 1 : 0), optchar)) == NULL) {
/* option letter unknown or ':' */
if (!*place)
++optind;
#ifndef _WIN32
if (PRINT_ERROR)
warnx(illoptchar, optchar);
#else
warnx(PRINT_ERROR, illoptchar, optchar);
#endif
optopt = optchar;
return BADCH;
}
if (optchar == 'W' && oli[1] == ';') { /* -W long-option */
/* XXX: what if no long options provided (called by getopt)? */
if (*place)
return -2;
if (++optind >= nargc) { /* no arg */
place = EMSG;
#ifndef _WIN32
if (PRINT_ERROR)
warnx(recargchar, optchar);
#else
warnx(PRINT_ERROR, recargchar, optchar);
#endif
optopt = optchar;
return BADARG;
} else /* white space */
place = nargv[optind];
/*
* Handle -W arg the same as --arg (which causes getopt to
* stop parsing).
*/
return -2;
}
if (*++oli != ':') { /* doesn't take argument */
if (!*place)
++optind;
} else { /* takes (optional) argument */
optarg = NULL;
if (*place) /* no white space */
optarg = place;
/* XXX: disable test for :: if PC? (GNU doesn't) */
else if (oli[1] != ':') { /* arg not optional */
if (++optind >= nargc) { /* no arg */
place = EMSG;
#ifndef _WIN32
if (PRINT_ERROR)
warnx(recargchar, optchar);
#else
warnx(PRINT_ERROR, recargchar, optchar);
#endif
optopt = optchar;
return BADARG;
} else
optarg = nargv[optind];
}
place = EMSG;
++optind;
}
/* dump back option letter */
return optchar;
}
#ifdef REPLACE_GETOPT
/*
* getopt --
* Parse argc/argv argument vector.
*
* [eventually this will replace the real getopt]
*/
int
getopt(nargc, nargv, options)
int nargc;
char * const *nargv;
const char *options;
{
int retval;
_DIAGASSERT(nargv != NULL);
_DIAGASSERT(options != NULL);
if ((retval = getopt_internal(nargc, nargv, options)) == -2) {
++optind;
/*
* We found an option (--), so if we skipped non-options,
* we have to permute.
*/
if (nonopt_end != -1) {
permute_args(nonopt_start, nonopt_end, optind,
nargv);
optind -= nonopt_end - nonopt_start;
}
nonopt_start = nonopt_end = -1;
retval = -1;
}
return retval;
}
#endif
/*
* getopt_long --
* Parse argc/argv argument vector.
*/
int
getopt_long(nargc, nargv, options, long_options, idx)
int nargc;
char * const *nargv;
const char *options;
const struct option *long_options;
int *idx;
{
int retval;
_DIAGASSERT(nargv != NULL);
_DIAGASSERT(options != NULL);
_DIAGASSERT(long_options != NULL);
/* idx may be NULL */
if ((retval = getopt_internal(nargc, nargv, options)) == -2) {
char *current_argv, *has_equal;
size_t current_argv_len;
int i, match;
current_argv = place;
match = -1;
optind++;
place = EMSG;
if (*current_argv == '\0') { /* found "--" */
/*
* We found an option (--), so if we skipped
* non-options, we have to permute.
*/
if (nonopt_end != -1) {
permute_args(nonopt_start, nonopt_end,
optind, nargv);
optind -= nonopt_end - nonopt_start;
}
nonopt_start = nonopt_end = -1;
return -1;
}
if ((has_equal = strchr(current_argv, '=')) != NULL) {
/* argument found (--option=arg) */
current_argv_len = has_equal - current_argv;
has_equal++;
} else
current_argv_len = strlen(current_argv);
for (i = 0; long_options[i].name; i++) {
/* find matching long option */
if (strncmp(current_argv, long_options[i].name,
current_argv_len))
continue;
if (strlen(long_options[i].name) ==
(unsigned)current_argv_len) {
/* exact match */
match = i;
break;
}
if (match == -1) /* partial match */
match = i;
else {
/* ambiguous abbreviation */
#ifndef _WIN32
if (PRINT_ERROR)
warnx(ambig, (int)current_argv_len,
current_argv);
#else
warnx(PRINT_ERROR, ambig, (int)current_argv_len,
current_argv);
#endif
optopt = 0;
return BADCH;
}
}
if (match != -1) { /* option found */
if (long_options[match].has_arg == no_argument
&& has_equal) {
#ifndef _WIN32
if (PRINT_ERROR)
warnx(noarg, (int)current_argv_len,
current_argv);
#else
warnx(PRINT_ERROR, noarg, (int)current_argv_len,
current_argv);
#endif
/*
* XXX: GNU sets optopt to val regardless of
* flag
*/
if (long_options[match].flag == NULL)
optopt = long_options[match].val;
else
optopt = 0;
return BADARG;
}
if (long_options[match].has_arg == required_argument ||
long_options[match].has_arg == optional_argument) {
if (has_equal)
optarg = has_equal;
else if (long_options[match].has_arg ==
required_argument) {
/*
* optional argument doesn't use
* next nargv
*/
optarg = nargv[optind++];
}
}
if ((long_options[match].has_arg == required_argument)
&& (optarg == NULL)) {
/*
* Missing argument; leading ':'
* indicates no error should be generated
*/
#ifndef _WIN32
if (PRINT_ERROR)
warnx(recargstring, current_argv);
#else
warnx(PRINT_ERROR, recargstring, current_argv);
#endif
/*
* XXX: GNU sets optopt to val regardless
* of flag
*/
if (long_options[match].flag == NULL)
optopt = long_options[match].val;
else
optopt = 0;
--optind;
return BADARG;
}
} else { /* unknown option */
#ifndef _WIN32
if (PRINT_ERROR)
warnx(illoptstring, current_argv);
#else
warnx(PRINT_ERROR, illoptstring, current_argv);
#endif
optopt = 0;
return BADCH;
}
if (long_options[match].flag) {
*long_options[match].flag = long_options[match].val;
retval = 0;
} else
retval = long_options[match].val;
if (idx)
*idx = match;
}
return retval;
}
#endif /* !GETOPT_LONG */

View file

View file

@ -0,0 +1,554 @@
/* -*- 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-2008 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;
using std::cout;
using std::cerr;
using std::endl;
using std::min;
const char *const
RubberBandPitchShifter::portNamesMono[PortCountMono] =
{
"latency",
"Cents",
"Semitones",
"Octaves",
"Crispness",
"Formant Preserving",
"Faster",
"Input",
"Output"
};
const char *const
RubberBandPitchShifter::portNamesStereo[PortCountStereo] =
{
"latency",
"Cents",
"Semitones",
"Octaves",
"Crispness",
"Formant Preserving",
"Faster",
"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_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_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 }, // latency
{ LADSPA_HINT_DEFAULT_0 | // cents
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE,
-100.0, 100.0 },
{ LADSPA_HINT_DEFAULT_0 | // semitones
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_INTEGER,
-12.0, 12.0 },
{ LADSPA_HINT_DEFAULT_0 | // octaves
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_INTEGER,
-3.0, 3.0 },
{ LADSPA_HINT_DEFAULT_MAXIMUM | // crispness
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_INTEGER,
0.0, 3.0 },
{ LADSPA_HINT_DEFAULT_0 | // formant preserving
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_TOGGLED,
0.0, 1.0 },
{ LADSPA_HINT_DEFAULT_0 | // fast
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_TOGGLED,
0.0, 1.0 },
{ 0, 0, 0 },
{ 0, 0, 0 }
};
const LADSPA_PortRangeHint
RubberBandPitchShifter::hintsStereo[PortCountStereo] =
{
{ 0, 0, 0 }, // latency
{ LADSPA_HINT_DEFAULT_0 | // cents
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE,
-100.0, 100.0 },
{ LADSPA_HINT_DEFAULT_0 | // semitones
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_INTEGER,
-12.0, 12.0 },
{ LADSPA_HINT_DEFAULT_0 | // octaves
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_INTEGER,
-3.0, 3.0 },
{ LADSPA_HINT_DEFAULT_MAXIMUM | // crispness
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_INTEGER,
0.0, 3.0 },
{ LADSPA_HINT_DEFAULT_0 | // formant preserving
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_TOGGLED,
0.0, 1.0 },
{ LADSPA_HINT_DEFAULT_0 | // fast
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_TOGGLED,
0.0, 1.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
"Breakfast Quay",
"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
"Breakfast Quay",
"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_formant(0),
m_fast(0),
m_ratio(1.0),
m_prevRatio(1.0),
m_currentCrispness(-1),
m_currentFormant(false),
m_currentFast(false),
m_blockSize(1024),
m_reserve(1024),
m_minfill(0),
m_stretcher(new RubberBandStretcher
(sampleRate, channels,
RubberBandStretcher::OptionProcessRealTime |
RubberBandStretcher::OptionPitchHighConsistency)),
m_sampleRate(sampleRate),
m_channels(channels)
{
for (size_t c = 0; c < m_channels; ++c) {
m_input[c] = 0;
m_output[c] = 0;
int bufsize = m_blockSize + m_reserve + 8192;
m_outputBuffer[c] = new RingBuffer<float>(bufsize);
m_scratch[c] = new float[bufsize];
for (int i = 0; i < bufsize; ++i) m_scratch[c][i] = 0.f;
}
activateImpl();
}
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_formant,
&shifter->m_fast,
&shifter->m_input[0],
&shifter->m_output[0],
&shifter->m_input[1],
&shifter->m_output[1]
};
if (shifter->m_channels == 1) {
if (port >= PortCountMono) return;
} else {
if (port >= PortCountStereo) return;
}
*ports[port] = (float *)location;
if (shifter->m_latency) {
*(shifter->m_latency) =
float(shifter->m_stretcher->getLatency() + shifter->m_reserve);
}
}
void
RubberBandPitchShifter::activate(LADSPA_Handle handle)
{
RubberBandPitchShifter *shifter = (RubberBandPitchShifter *)handle;
shifter->activateImpl();
}
void
RubberBandPitchShifter::activateImpl()
{
updateRatio();
m_prevRatio = m_ratio;
m_stretcher->reset();
m_stretcher->setPitchScale(m_ratio);
for (size_t c = 0; c < m_channels; ++c) {
m_outputBuffer[c]->reset();
m_outputBuffer[c]->zero(m_reserve);
}
m_minfill = 0;
// prime stretcher
// for (int i = 0; i < 8; ++i) {
// int reqd = m_stretcher->getSamplesRequired();
// m_stretcher->process(m_scratch, reqd, false);
// int avail = m_stretcher->available();
// if (avail > 0) {
// m_stretcher->retrieve(m_scratch, avail);
// }
// }
}
void
RubberBandPitchShifter::run(LADSPA_Handle handle, unsigned long samples)
{
RubberBandPitchShifter *shifter = (RubberBandPitchShifter *)handle;
shifter->runImpl(samples);
}
void
RubberBandPitchShifter::updateRatio()
{
double oct = (m_octaves ? *m_octaves : 0.0);
oct += (m_semitones ? *m_semitones : 0.0) / 12;
oct += (m_cents ? *m_cents : 0.0) / 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::OptionPhaseLaminar);
s->setTransientsOption(RubberBandStretcher::OptionTransientsSmooth);
break;
case 2:
s->setPhaseOption(RubberBandStretcher::OptionPhaseLaminar);
s->setTransientsOption(RubberBandStretcher::OptionTransientsMixed);
break;
case 3:
s->setPhaseOption(RubberBandStretcher::OptionPhaseLaminar);
s->setTransientsOption(RubberBandStretcher::OptionTransientsCrisp);
break;
}
m_currentCrispness = c;
}
void
RubberBandPitchShifter::updateFormant()
{
if (!m_formant) return;
bool f = (*m_formant > 0.5f);
if (f == m_currentFormant) return;
RubberBandStretcher *s = m_stretcher;
s->setFormantOption(f ?
RubberBandStretcher::OptionFormantPreserved :
RubberBandStretcher::OptionFormantShifted);
m_currentFormant = f;
}
void
RubberBandPitchShifter::updateFast()
{
if (!m_fast) return;
bool f = (*m_fast > 0.5f);
if (f == m_currentFast) return;
RubberBandStretcher *s = m_stretcher;
s->setPitchOption(f ?
RubberBandStretcher::OptionPitchHighSpeed :
RubberBandStretcher::OptionPitchHighConsistency);
m_currentFast = f;
}
void
RubberBandPitchShifter::runImpl(unsigned long insamples)
{
unsigned long offset = 0;
// We have to break up the input into chunks like this because
// insamples could be arbitrarily large and our output buffer is
// of limited size
while (offset < insamples) {
unsigned long block = (unsigned long)m_blockSize;
if (block + offset > insamples) block = insamples - offset;
runImpl(block, offset);
offset += block;
}
}
void
RubberBandPitchShifter::runImpl(unsigned long insamples, unsigned long offset)
{
// cerr << "RubberBandPitchShifter::runImpl(" << insamples << ")" << endl;
// static int incount = 0, outcount = 0;
updateRatio();
if (m_ratio != m_prevRatio) {
m_stretcher->setPitchScale(m_ratio);
m_prevRatio = m_ratio;
}
if (m_latency) {
*m_latency = float(m_stretcher->getLatency() + m_reserve);
// cerr << "latency = " << *m_latency << endl;
}
updateCrispness();
updateFormant();
updateFast();
const int samples = insamples;
int processed = 0;
size_t outTotal = 0;
float *ptrs[2];
int rs = m_outputBuffer[0]->getReadSpace();
if (rs < int(m_minfill)) {
// cerr << "temporary expansion (have " << rs << ", want " << m_reserve << ")" << endl;
m_stretcher->setTimeRatio(1.1); // fill up temporarily
} else if (rs > 8192) {
// cerr << "temporary reduction (have " << rs << ", want " << m_reserve << ")" << endl;
m_stretcher->setTimeRatio(0.9); // reduce temporarily
} else {
m_stretcher->setTimeRatio(1.0);
}
while (processed < samples) {
// never feed more than the minimum necessary number of
// samples at a time; ensures nothing will overflow internally
// and we don't need to call setMaxProcessSize
int toCauseProcessing = m_stretcher->getSamplesRequired();
int inchunk = min(samples - processed, toCauseProcessing);
for (size_t c = 0; c < m_channels; ++c) {
ptrs[c] = &(m_input[c][offset + processed]);
}
m_stretcher->process(ptrs, inchunk, false);
processed += inchunk;
int avail = m_stretcher->available();
int writable = m_outputBuffer[0]->getWriteSpace();
int outchunk = min(avail, writable);
size_t actual = m_stretcher->retrieve(m_scratch, outchunk);
outTotal += actual;
// incount += inchunk;
// outcount += actual;
// cout << "avail: " << avail << ", outchunk = " << outchunk;
// if (actual != outchunk) cout << " (" << actual << ")";
// cout << endl;
outchunk = actual;
for (size_t c = 0; c < m_channels; ++c) {
if (int(m_outputBuffer[c]->getWriteSpace()) < outchunk) {
cerr << "RubberBandPitchShifter::runImpl: buffer overrun: chunk = " << outchunk << ", space = " << m_outputBuffer[c]->getWriteSpace() << endl;
}
m_outputBuffer[c]->write(m_scratch[c], outchunk);
}
}
for (size_t c = 0; c < m_channels; ++c) {
int toRead = m_outputBuffer[c]->getReadSpace();
if (toRead < samples && c == 0) {
cerr << "RubberBandPitchShifter::runImpl: buffer underrun: required = " << samples << ", available = " << toRead << endl;
}
int chunk = min(toRead, samples);
m_outputBuffer[c]->read(&(m_output[c][offset]), chunk);
}
if (m_minfill == 0) {
m_minfill = m_outputBuffer[0]->getReadSpace();
// cerr << "minfill = " << m_minfill << endl;
}
}
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

@ -0,0 +1,107 @@
/* -*- 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-2008 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,
FormantPort = 5,
FastPort = 6,
InputPort1 = 7,
OutputPort1 = 8,
PortCountMono = OutputPort1 + 1,
InputPort2 = 9,
OutputPort2 = 10,
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 activateImpl();
void runImpl(unsigned long);
void runImpl(unsigned long, unsigned long offset);
void updateRatio();
void updateCrispness();
void updateFormant();
void updateFast();
float *m_input[2];
float *m_output[2];
float *m_latency;
float *m_cents;
float *m_semitones;
float *m_octaves;
float *m_crispness;
float *m_formant;
float *m_fast;
double m_ratio;
double m_prevRatio;
int m_currentCrispness;
bool m_currentFormant;
bool m_currentFast;
size_t m_blockSize;
size_t m_reserve;
size_t m_minfill;
RubberBand::RubberBandStretcher *m_stretcher;
RubberBand::RingBuffer<float> *m_outputBuffer[2];
float *m_scratch[2];
int m_sampleRate;
size_t m_channels;
};
#endif

View file

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

View file

@ -0,0 +1,26 @@
/* -*- 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-2008 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

@ -0,0 +1,530 @@
/* -*- 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-2008 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 <cstring>
#include <iostream>
#include <sndfile.h>
#include <cmath>
#include <time.h>
#include <cstdlib>
#include <cstring>
#include "sysutils.h"
#ifdef __MSVC__
#include "bsd-3rdparty/getopt/getopt.h"
#else
#include <getopt.h>
#include <sys/time.h>
#endif
#include "Profiler.h"
using namespace std;
using namespace RubberBand;
#ifdef _WIN32
using RubberBand::gettimeofday;
using RubberBand::usleep;
#endif
double tempo_convert(const char *str)
{
char *d = strchr((char *)str, ':');
if (!d || !*d) {
double m = atof(str);
if (m != 0.0) return 1.0 / m;
else return 1.0;
}
char *a = strdup(str);
char *b = strdup(d+1);
a[d-str] = '\0';
double m = atof(a);
double n = atof(b);
free(a);
free(b);
if (n != 0.0 && m != 0.0) return m / n;
else return 1.0;
}
int main(int argc, char **argv)
{
int c;
double ratio = 1.0;
double duration = 0.0;
double pitchshift = 0.0;
double frequencyshift = 1.0;
int debug = 0;
bool realtime = false;
bool precise = false;
int threading = 0;
bool lamination = true;
bool longwin = false;
bool shortwin = false;
bool hqpitch = false;
bool formant = false;
bool crispchanged = false;
int crispness = -1;
bool help = false;
bool version = false;
bool quiet = false;
bool haveRatio = false;
enum {
NoTransients,
BandLimitedTransients,
Transients
} transients = Transients;
while (1) {
int optionIndex = 0;
static struct option longOpts[] = {
{ "help", 0, 0, 'h' },
{ "version", 0, 0, 'V' },
{ "time", 1, 0, 't' },
{ "tempo", 1, 0, 'T' },
{ "duration", 1, 0, 'D' },
{ "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' },
{ "formant", 0, 0, 'F' },
{ "no-threads", 0, 0, '0' },
{ "no-transients", 0, 0, '1' },
{ "no-lamination", 0, 0, '2' },
{ "window-long", 0, 0, '3' },
{ "window-short", 0, 0, '4' },
{ "bl-transients", 0, 0, '8' },
{ "pitch-hq", 0, 0, '%' },
{ "threads", 0, 0, '@' },
{ "quiet", 0, 0, 'q' },
{ 0, 0, 0 }
};
c = getopt_long(argc, argv, "t:p:d:RPFc:f:T:D:qhV", longOpts, &optionIndex);
if (c == -1) break;
switch (c) {
case 'h': help = true; break;
case 'V': version = true; break;
case 't': ratio *= atof(optarg); haveRatio = true; break;
case 'T': ratio *= tempo_convert(optarg); haveRatio = true; break;
case 'D': duration = atof(optarg); 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 'F': formant = true; break;
case '0': threading = 1; break;
case '@': threading = 2; break;
case '1': transients = NoTransients; crispchanged = true; break;
case '2': lamination = false; crispchanged = true; break;
case '3': longwin = true; crispchanged = true; break;
case '4': shortwin = true; crispchanged = true; break;
case '8': transients = BandLimitedTransients; crispchanged = true; break;
case '%': hqpitch = true; break;
case 'c': crispness = atoi(optarg); break;
case 'q': quiet = true; break;
default: help = true; break;
}
}
if (version) {
cerr << RUBBERBAND_VERSION << endl;
return 0;
}
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 2008 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 (same as --time 1/X), or" << endl;
cerr << " -T<X>, --tempo <X>:<Y> Change tempo from X to Y (same as --time X/Y), or" << endl;
cerr << " -D<X>, --duration <X> Stretch or squash to make output file X seconds long" << 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 options provide 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 << " -F, --formant Enable formant preservation when pitch shifting" << 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. The default is to use none of these options." << 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-lamination Disable phase lamination" << endl;
cerr << " --window-long Use longer processing window (actual size may vary)" << endl;
cerr << " --window-short Use shorter processing window" << endl;
cerr << " --pitch-hq In RT mode, use a slower, higher quality pitch shift" << 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 << " -V, --version Show version number and exit" << endl;
cerr << " -h, --help Show this help" << endl;
cerr << endl;
cerr << "\"Crispness\" levels:" << endl;
cerr << " -c 0 equivalent to --no-transients --no-lamination --window-long" << endl;
cerr << " -c 1 equivalent to --no-transients --no-lamination" << 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-lamination --window-short (may be good for drums)" << endl;
cerr << endl;
return 2;
}
if (crispness >= 0 && crispchanged) {
cerr << "WARNING: Both crispness option and transients, lamination or window options" << endl;
cerr << " provided -- crispness will override these other options" << endl;
}
switch (crispness) {
case -1: crispness = 4; break;
case 0: transients = NoTransients; lamination = false; longwin = true; shortwin = false; break;
case 1: transients = NoTransients; lamination = false; longwin = false; shortwin = false; break;
case 2: transients = NoTransients; lamination = true; longwin = false; shortwin = false; break;
case 3: transients = BandLimitedTransients; lamination = true; longwin = false; shortwin = false; break;
case 4: transients = Transients; lamination = true; longwin = false; shortwin = false; break;
case 5: transients = Transients; lamination = 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;
}
if (duration != 0.0) {
if (sfinfo.frames == 0 || sfinfo.samplerate == 0) {
cerr << "ERROR: File lacks frame count or sample rate in header, cannot use --duration" << endl;
return 1;
}
double induration = double(sfinfo.frames) / double(sfinfo.samplerate);
if (induration != 0.0) ratio = duration / induration;
}
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 \"" << fileNameOut << "\" for writing: "
<< sf_strerror(sndfileOut) << 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 (!lamination) options |= RubberBandStretcher::OptionPhaseIndependent;
if (longwin) options |= RubberBandStretcher::OptionWindowLong;
if (shortwin) options |= RubberBandStretcher::OptionWindowShort;
if (formant) options |= RubberBandStretcher::OptionFormantPreserved;
if (hqpitch) options |= RubberBandStretcher::OptionPitchHighQuality;
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 != 0.0) {
frequencyshift *= pow(2.0, pitchshift / 12);
}
cerr << "Using time ratio " << ratio;
cerr << " and frequency ratio " << frequencyshift << endl;
#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;
}
Profiler::dump();
return 0;
}

View file

@ -0,0 +1,477 @@
/* -*- 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 <cstdlib>
#include <cstring>
#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

@ -0,0 +1,475 @@
/* -*- 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

@ -0,0 +1,477 @@
/* -*- 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 <cstring>
#include <iostream>
#include <sndfile.h>
#include <cmath>
#include <cstdlib>
#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

@ -0,0 +1,146 @@
/* -*- 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-2008 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 "rubberband-c.h"
#include "RubberBandStretcher.h"
struct RubberBandState_
{
RubberBand::RubberBandStretcher *m_s;
};
RubberBandState rubberband_new(unsigned int sampleRate,
unsigned int channels,
RubberBandOptions options,
double initialTimeRatio,
double initialPitchScale)
{
RubberBandState_ *state = new RubberBandState_();
state->m_s = new RubberBand::RubberBandStretcher
(sampleRate, channels, options,
initialTimeRatio, initialPitchScale);
return state;
}
void rubberband_delete(RubberBandState state)
{
delete state->m_s;
delete state;
}
void rubberband_reset(RubberBandState state)
{
state->m_s->reset();
}
void rubberband_set_time_ratio(RubberBandState state, double ratio)
{
state->m_s->setTimeRatio(ratio);
}
void rubberband_set_pitch_scale(RubberBandState state, double scale)
{
state->m_s->setPitchScale(scale);
}
double rubberband_get_time_ratio(const RubberBandState state)
{
return state->m_s->getTimeRatio();
}
double rubberband_get_pitch_scale(const RubberBandState state)
{
return state->m_s->getPitchScale();
}
unsigned int rubberband_get_latency(const RubberBandState state)
{
return state->m_s->getLatency();
}
void rubberband_set_transients_option(RubberBandState state, RubberBandOptions options)
{
state->m_s->setTransientsOption(options);
}
void rubberband_set_phase_option(RubberBandState state, RubberBandOptions options)
{
state->m_s->setPhaseOption(options);
}
void rubberband_set_formant_option(RubberBandState state, RubberBandOptions options)
{
state->m_s->setFormantOption(options);
}
void rubberband_set_pitch_option(RubberBandState state, RubberBandOptions options)
{
state->m_s->setPitchOption(options);
}
void rubberband_set_expected_input_duration(RubberBandState state, unsigned int samples)
{
state->m_s->setExpectedInputDuration(samples);
}
unsigned int rubberband_get_samples_required(const RubberBandState state)
{
return state->m_s->getSamplesRequired();
}
void rubberband_set_max_process_size(RubberBandState state, unsigned int samples)
{
state->m_s->setMaxProcessSize(samples);
}
void rubberband_study(RubberBandState state, const float *const *input, unsigned int samples, int final)
{
state->m_s->study(input, samples, final != 0);
}
void rubberband_process(RubberBandState state, const float *const *input, unsigned int samples, int final)
{
state->m_s->process(input, samples, final != 0);
}
int rubberband_available(const RubberBandState state)
{
return state->m_s->available();
}
unsigned int rubberband_retrieve(const RubberBandState state, float *const *output, unsigned int samples)
{
return state->m_s->retrieve(output, samples);
}
unsigned int rubberband_get_channel_count(const RubberBandState state)
{
return state->m_s->getChannelCount();
}
void rubberband_calculate_stretch(RubberBandState state)
{
state->m_s->calculateStretch();
}
void rubberband_set_debug_level(RubberBandState state, int level)
{
state->m_s->setDebugLevel(level);
}
void rubberband_set_default_debug_level(int level)
{
RubberBand::RubberBandStretcher::setDefaultDebugLevel(level);
}

View file

@ -0,0 +1,158 @@
/* -*- 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-2008 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 <cstdio>
#include <cstring>
#endif /* !__APPLE__, !_WIN32 */
#endif /* !_WIN32 */
#include <cstdlib>
#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
int 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);
return 0;
}
void usleep(unsigned long usec)
{
::Sleep(usec == 0 ? 0 : usec < 1000 ? 1 : usec / 1000);
}
#endif
float *allocFloat(float *ptr, int count)
{
if (ptr) free((void *)ptr);
void *allocated;
#ifndef _WIN32
#ifndef __APPLE__
if (!posix_memalign(&allocated, 16, count * sizeof(float)))
#endif
#endif
allocated = malloc(count * sizeof(float));
for (int i = 0; i < count; ++i) ((float *)allocated)[i] = 0.f;
return (float *)allocated;
}
float *allocFloat(int count)
{
return allocFloat(0, count);
}
void freeFloat(float *ptr)
{
if (ptr) free(ptr);
}
double *allocDouble(double *ptr, int count)
{
if (ptr) free((void *)ptr);
void *allocated;
#ifndef _WIN32
#ifndef __APPLE__
if (!posix_memalign(&allocated, 16, count * sizeof(double)))
#endif
#endif
allocated = malloc(count * sizeof(double));
for (int i = 0; i < count; ++i) ((double *)allocated)[i] = 0.f;
return (double *)allocated;
}
double *allocDouble(int count)
{
return allocDouble(0, count);
}
void freeDouble(double *ptr)
{
if (ptr) free(ptr);
}
}

View file

@ -0,0 +1,62 @@
/* -*- 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-2008 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_
#ifdef __MSVC__
#include "bsd-3rdparty/float_cast/float_cast.h"
#define R__ __restrict
#endif
#ifdef __GNUC__
#define R__ __restrict__
#endif
#ifndef R__
#define R__
#endif
#ifdef __MINGW32__
#include <malloc.h>
#endif
#ifdef __MSVC__
#define alloca _alloca
#endif
namespace RubberBand {
extern bool system_is_multiprocessor();
#ifdef _WIN32
struct timeval { long tv_sec; long tv_usec; };
int gettimeofday(struct timeval *p, void *tz);
void usleep(unsigned long);
#endif
extern float *allocFloat(int);
extern float *allocFloat(float *, int);
extern void freeFloat(float *);
extern double *allocDouble(int);
extern double *allocDouble(double *, int);
extern void freeDouble(double *);
}
#endif

View file

@ -0,0 +1,648 @@
/* -*- 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-2008 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 "sysutils.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 "Breakfast Quay";
}
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 = float(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 = float(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.f : 0.f;
if (id == "stretchtype") return m_d->m_elasticTiming ? 0.f : 1.f;
if (id == "transientmode") return float(m_d->m_transientMode);
if (id == "phasemode") return m_d->m_phaseIndependent ? 1.f : 0.f;
if (id == "windowmode") return float(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::OptionPhaseLaminar;
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(float(oi));
feature.label = Vamp::RealTime::frame2RealTime(oi, rate).toText();
features[m_incrementsOutput].push_back(feature);
feature.values.clear();
feature.values.push_back(float(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", int(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(float(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

@ -0,0 +1,56 @@
/* -*- 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-2008 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

@ -0,0 +1,32 @@
/* -*- 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-2008 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

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