diff options
author | Paul Davis <paul@linuxaudiosystems.com> | 2008-09-10 21:35:32 +0000 |
---|---|---|
committer | Paul Davis <paul@linuxaudiosystems.com> | 2008-09-10 21:35:32 +0000 |
commit | 7da75446b878f9af817a400ade13d3caa40416ef (patch) | |
tree | f337f99d28860abcd22ee9f59121bce25e408d1d /libs | |
parent | c86210a9d5bdb7b36ad58552a1f99f53d48781b3 (diff) |
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
Diffstat (limited to 'libs')
65 files changed, 17770 insertions, 0 deletions
diff --git a/libs/rubberband/COPYING b/libs/rubberband/COPYING new file mode 100644 index 0000000000..c7aea1896f --- /dev/null +++ b/libs/rubberband/COPYING @@ -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 diff --git a/libs/rubberband/Makefile.in b/libs/rubberband/Makefile.in new file mode 100644 index 0000000000..9d4666ffaf --- /dev/null +++ b/libs/rubberband/Makefile.in @@ -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 diff --git a/libs/rubberband/README b/libs/rubberband/README new file mode 100644 index 0000000000..178234e9b5 --- /dev/null +++ b/libs/rubberband/README @@ -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. + diff --git a/libs/rubberband/SConscript b/libs/rubberband/SConscript new file mode 100644 index 0000000000..82a9f1fa5c --- /dev/null +++ b/libs/rubberband/SConscript @@ -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'))) + + diff --git a/libs/rubberband/configure.ac b/libs/rubberband/configure.ac new file mode 100644 index 0000000000..83da151ba3 --- /dev/null +++ b/libs/rubberband/configure.ac @@ -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]) + diff --git a/libs/rubberband/misc/Makefile.osx b/libs/rubberband/misc/Makefile.osx new file mode 100644 index 0000000000..ecef7ab164 --- /dev/null +++ b/libs/rubberband/misc/Makefile.osx @@ -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 diff --git a/libs/rubberband/rubberband.pc.in b/libs/rubberband/rubberband.pc.in new file mode 100644 index 0000000000..580fea38f2 --- /dev/null +++ b/libs/rubberband/rubberband.pc.in @@ -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} diff --git a/libs/rubberband/rubberband/RubberBandStretcher.h b/libs/rubberband/rubberband/RubberBandStretcher.h new file mode 100644 index 0000000000..ff12bafe8a --- /dev/null +++ b/libs/rubberband/rubberband/RubberBandStretcher.h @@ -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 diff --git a/libs/rubberband/rubberband/TimeStretcher.h b/libs/rubberband/rubberband/TimeStretcher.h new file mode 100644 index 0000000000..bad916a75c --- /dev/null +++ b/libs/rubberband/rubberband/TimeStretcher.h @@ -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 + diff --git a/libs/rubberband/rubberband/rubberband-c.h b/libs/rubberband/rubberband/rubberband-c.h new file mode 100644 index 0000000000..78fd129684 --- /dev/null +++ b/libs/rubberband/rubberband/rubberband-c.h @@ -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 diff --git a/libs/rubberband/src/AudioCurve.cpp b/libs/rubberband/src/AudioCurve.cpp new file mode 100644 index 0000000000..118caf4bdc --- /dev/null +++ b/libs/rubberband/src/AudioCurve.cpp @@ -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; +} + +} diff --git a/libs/rubberband/src/AudioCurve.h b/libs/rubberband/src/AudioCurve.h new file mode 100644 index 0000000000..7896308013 --- /dev/null +++ b/libs/rubberband/src/AudioCurve.h @@ -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 + diff --git a/libs/rubberband/src/ConstantAudioCurve.cpp b/libs/rubberband/src/ConstantAudioCurve.cpp new file mode 100644 index 0000000000..3263c53c65 --- /dev/null +++ b/libs/rubberband/src/ConstantAudioCurve.cpp @@ -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; +} + +} + diff --git a/libs/rubberband/src/ConstantAudioCurve.h b/libs/rubberband/src/ConstantAudioCurve.h new file mode 100644 index 0000000000..d73cabe943 --- /dev/null +++ b/libs/rubberband/src/ConstantAudioCurve.h @@ -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 diff --git a/libs/rubberband/src/FFT.cpp b/libs/rubberband/src/FFT.cpp new file mode 100644 index 0000000000..98201350a2 --- /dev/null +++ b/libs/rubberband/src/FFT.cpp @@ -0,0 +1,1367 @@ +/* -*- 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 "FFT.h" +#include "Thread.h" +#include "Profiler.h" + +//#define FFT_MEASUREMENT 1 + +#define HAVE_FFTW3 // for Ardour + +#ifdef HAVE_FFTW3 +#include <fftw3.h> +#endif + +#include <cstdlib> + +#ifdef USE_KISSFFT +#include "bsd-3rdparty/kissfft/kiss_fftr.h" +#endif + +#ifndef HAVE_FFTW3 +#ifndef USE_KISSFFT +#ifndef USE_BUILTIN_FFT +#error No FFT implementation selected! +#endif +#endif +#endif + +#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(const double *R__ realIn, double *R__ realOut, double *R__ imagOut) = 0; + virtual void forwardPolar(const double *R__ realIn, double *R__ magOut, double *R__ phaseOut) = 0; + virtual void forwardMagnitude(const double *R__ realIn, double *R__ magOut) = 0; + + virtual void forward(const float *R__ realIn, float *R__ realOut, float *R__ imagOut) = 0; + virtual void forwardPolar(const float *R__ realIn, float *R__ magOut, float *R__ phaseOut) = 0; + virtual void forwardMagnitude(const float *R__ realIn, float *R__ magOut) = 0; + + virtual void inverse(const double *R__ realIn, const double *R__ imagIn, double *R__ realOut) = 0; + virtual void inversePolar(const double *R__ magIn, const double *R__ phaseIn, double *R__ realOut) = 0; + virtual void inverseCepstral(const double *R__ magIn, double *R__ cepOut) = 0; + + virtual void inverse(const float *R__ realIn, const float *R__ imagIn, float *R__ realOut) = 0; + virtual void inversePolar(const float *R__ magIn, const float *R__ phaseIn, float *R__ realOut) = 0; + virtual void inverseCepstral(const float *R__ magIn, float *R__ cepOut) = 0; + + virtual float *getFloatTimeBuffer() = 0; + virtual double *getDoubleTimeBuffer() = 0; +}; + +namespace FFTs { + + +#ifdef HAVE_FFTW3 + +// 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 + +#if defined(FFTW_DOUBLE_ONLY) && defined(FFTW_FLOAT_ONLY) +// Can't meaningfully define both +#undef FFTW_DOUBLE_ONLY +#undef FFTW_FLOAT_ONLY +#endif + +#ifdef FFTW_DOUBLE_ONLY +#define fft_float_type double +#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 +#else +#define fft_float_type float +#endif /* FFTW_DOUBLE_ONLY */ + +#ifdef FFTW_FLOAT_ONLY +#define fft_double_type float +#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 sin sinf +#else +#define fft_double_type double +#endif /* FFTW_FLOAT_ONLY */ + +class D_FFTW : public FFTImpl +{ +public: + D_FFTW(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(); +#ifndef FFTW_DOUBLE_ONLY + if (save) saveWisdom('f'); +#endif + 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(); +#ifndef FFTW_FLOAT_ONLY + if (save) saveWisdom('d'); +#endif + 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'); +#else + if (load) loadWisdom('f'); +#endif + m_fbuf = (fft_float_type *)fftw_malloc(m_size * sizeof(fft_float_type)); + 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'); +#else + if (load) loadWisdom('d'); +#endif + m_dbuf = (fft_double_type *)fftw_malloc(m_size * sizeof(fft_double_type)); + 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(const float *R__ re, const float *R__ im) { + const int hs = m_size/2; + fftwf_complex *const R__ fpacked = m_fpacked; + for (int i = 0; i <= hs; ++i) { + fpacked[i][0] = re[i]; + } + if (im) { + for (int i = 0; i <= hs; ++i) { + fpacked[i][1] = im[i]; + } + } else { + for (int i = 0; i <= hs; ++i) { + fpacked[i][1] = 0.f; + } + } + } + + void packDouble(const double *R__ re, const double *R__ im) { + const int hs = m_size/2; + fftw_complex *const R__ dpacked = m_dpacked; + for (int i = 0; i <= hs; ++i) { + dpacked[i][0] = re[i]; + } + if (im) { + for (int i = 0; i <= hs; ++i) { + dpacked[i][1] = im[i]; + } + } else { + for (int i = 0; i <= hs; ++i) { + dpacked[i][1] = 0.0; + } + } + } + + void unpackFloat(float *R__ re, float *R__ im) { + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { + re[i] = m_fpacked[i][0]; + } + if (im) { + for (int i = 0; i <= hs; ++i) { + im[i] = m_fpacked[i][1]; + } + } + } + + void unpackDouble(double *R__ re, double *R__ im) { + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { + re[i] = m_dpacked[i][0]; + } + if (im) { + for (int i = 0; i <= hs; ++i) { + im[i] = m_dpacked[i][1]; + } + } + } + + void forward(const double *R__ realIn, double *R__ realOut, double *R__ imagOut) { + if (!m_dplanf) initDouble(); + const int sz = m_size; + fft_double_type *const R__ dbuf = m_dbuf; +#ifndef FFTW_FLOAT_ONLY + if (realIn != dbuf) +#endif + for (int i = 0; i < sz; ++i) { + dbuf[i] = realIn[i]; + } + fftw_execute(m_dplanf); + unpackDouble(realOut, imagOut); + } + + void forwardPolar(const double *R__ realIn, double *R__ magOut, double *R__ phaseOut) { + if (!m_dplanf) initDouble(); + fft_double_type *const R__ dbuf = m_dbuf; + const int sz = m_size; +#ifndef FFTW_FLOAT_ONLY + if (realIn != dbuf) +#endif + for (int i = 0; i < sz; ++i) { + dbuf[i] = realIn[i]; + } + fftw_execute(m_dplanf); + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { + magOut[i] = sqrt(m_dpacked[i][0] * m_dpacked[i][0] + + m_dpacked[i][1] * m_dpacked[i][1]); + } + for (int i = 0; i <= hs; ++i) { + phaseOut[i] = atan2(m_dpacked[i][1], m_dpacked[i][0]); + } + } + + void forwardMagnitude(const double *R__ realIn, double *R__ magOut) { + if (!m_dplanf) initDouble(); + fft_double_type *const R__ dbuf = m_dbuf; + const int sz = m_size; +#ifndef FFTW_FLOAT_ONLY + if (realIn != m_dbuf) +#endif + for (int i = 0; i < sz; ++i) { + dbuf[i] = realIn[i]; + } + fftw_execute(m_dplanf); + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { + magOut[i] = sqrt(m_dpacked[i][0] * m_dpacked[i][0] + + m_dpacked[i][1] * m_dpacked[i][1]); + } + } + + void forward(const float *R__ realIn, float *R__ realOut, float *R__ imagOut) { + if (!m_fplanf) initFloat(); + fft_float_type *const R__ fbuf = m_fbuf; + const int sz = m_size; +#ifndef FFTW_DOUBLE_ONLY + if (realIn != fbuf) +#endif + for (int i = 0; i < sz; ++i) { + fbuf[i] = realIn[i]; + } + fftwf_execute(m_fplanf); + unpackFloat(realOut, imagOut); + } + + void forwardPolar(const float *R__ realIn, float *R__ magOut, float *R__ phaseOut) { + if (!m_fplanf) initFloat(); + fft_float_type *const R__ fbuf = m_fbuf; + const int sz = m_size; +#ifndef FFTW_DOUBLE_ONLY + if (realIn != fbuf) +#endif + for (int i = 0; i < sz; ++i) { + fbuf[i] = realIn[i]; + } + fftwf_execute(m_fplanf); + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { + magOut[i] = sqrtf(m_fpacked[i][0] * m_fpacked[i][0] + + m_fpacked[i][1] * m_fpacked[i][1]); + } + for (int i = 0; i <= hs; ++i) { + phaseOut[i] = atan2f(m_fpacked[i][1], m_fpacked[i][0]) ; + } + } + + void forwardMagnitude(const float *R__ realIn, float *R__ magOut) { + if (!m_fplanf) initFloat(); + fft_float_type *const R__ fbuf = m_fbuf; + const int sz = m_size; +#ifndef FFTW_DOUBLE_ONLY + if (realIn != fbuf) +#endif + for (int i = 0; i < sz; ++i) { + fbuf[i] = realIn[i]; + } + fftwf_execute(m_fplanf); + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { + magOut[i] = sqrtf(m_fpacked[i][0] * m_fpacked[i][0] + + m_fpacked[i][1] * m_fpacked[i][1]); + } + } + + void inverse(const double *R__ realIn, const double *R__ imagIn, double *R__ realOut) { + if (!m_dplanf) initDouble(); + packDouble(realIn, imagIn); + fftw_execute(m_dplani); + const int sz = m_size; + fft_double_type *const R__ dbuf = m_dbuf; +#ifndef FFTW_FLOAT_ONLY + if (realOut != dbuf) +#endif + for (int i = 0; i < sz; ++i) { + realOut[i] = dbuf[i]; + } + } + + void inversePolar(const double *R__ magIn, const double *R__ phaseIn, double *R__ realOut) { + if (!m_dplanf) initDouble(); + const int hs = m_size/2; + fftw_complex *const R__ dpacked = m_dpacked; + for (int i = 0; i <= hs; ++i) { + dpacked[i][0] = magIn[i] * cos(phaseIn[i]); + } + for (int i = 0; i <= hs; ++i) { + dpacked[i][1] = magIn[i] * sin(phaseIn[i]); + } + fftw_execute(m_dplani); + const int sz = m_size; + fft_double_type *const R__ dbuf = m_dbuf; +#ifndef FFTW_FLOAT_ONLY + if (realOut != dbuf) +#endif + for (int i = 0; i < sz; ++i) { + realOut[i] = dbuf[i]; + } + } + + void inverseCepstral(const double *R__ magIn, double *R__ cepOut) { + if (!m_dplanf) initDouble(); + fft_double_type *const R__ dbuf = m_dbuf; + fftw_complex *const R__ dpacked = m_dpacked; + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { + dpacked[i][0] = log(magIn[i] + 0.000001); + } + for (int i = 0; i <= hs; ++i) { + dpacked[i][1] = 0.0; + } + fftw_execute(m_dplani); + const int sz = m_size; +#ifndef FFTW_FLOAT_ONLY + if (cepOut != dbuf) +#endif + for (int i = 0; i < sz; ++i) { + cepOut[i] = dbuf[i]; + } + } + + void inverse(const float *R__ realIn, const float *R__ imagIn, float *R__ realOut) { + if (!m_fplanf) initFloat(); + packFloat(realIn, imagIn); + fftwf_execute(m_fplani); + const int sz = m_size; + fft_float_type *const R__ fbuf = m_fbuf; +#ifndef FFTW_DOUBLE_ONLY + if (realOut != fbuf) +#endif + for (int i = 0; i < sz; ++i) { + realOut[i] = fbuf[i]; + } + } + + void inversePolar(const float *R__ magIn, const float *R__ phaseIn, float *R__ realOut) { + if (!m_fplanf) initFloat(); + const int hs = m_size/2; + fftwf_complex *const R__ fpacked = m_fpacked; + for (int i = 0; i <= hs; ++i) { + fpacked[i][0] = magIn[i] * cosf(phaseIn[i]); + } + for (int i = 0; i <= hs; ++i) { + fpacked[i][1] = magIn[i] * sinf(phaseIn[i]); + } + fftwf_execute(m_fplani); + const int sz = m_size; + fft_float_type *const R__ fbuf = m_fbuf; +#ifndef FFTW_DOUBLE_ONLY + if (realOut != fbuf) +#endif + for (int i = 0; i < sz; ++i) { + realOut[i] = fbuf[i]; + } + } + + void inverseCepstral(const float *R__ magIn, float *R__ cepOut) { + if (!m_fplanf) initFloat(); + const int hs = m_size/2; + fftwf_complex *const R__ fpacked = m_fpacked; + for (int i = 0; i <= hs; ++i) { + fpacked[i][0] = logf(magIn[i] + 0.000001f); + } + for (int i = 0; i <= hs; ++i) { + fpacked[i][1] = 0.f; + } + fftwf_execute(m_fplani); + const int sz = m_size; + fft_float_type *const R__ fbuf = m_fbuf; +#ifndef FFTW_DOUBLE_ONLY + if (cepOut != fbuf) +#endif + for (int i = 0; i < sz; ++i) { + cepOut[i] = 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; + const int m_size; + static int m_extantf; + static int m_extantd; + static Mutex m_extantMutex; +}; + +int +D_FFTW::m_extantf = 0; + +int +D_FFTW::m_extantd = 0; + +Mutex +D_FFTW::m_extantMutex; + +#endif /* HAVE_FFTW3 */ + +#ifdef USE_KISSFFT + +class D_KISSFFT : public FFTImpl +{ +public: + D_KISSFFT(int size) : + m_size(size), + m_frb(0), + m_drb(0), + m_fplanf(0), + m_fplani(0) + { +#ifdef FIXED_POINT +#error KISSFFT is not configured for float values +#endif + if (sizeof(kiss_fft_scalar) != sizeof(float)) { + std::cerr << "ERROR: KISSFFT is not configured for float values" + << std::endl; + } + + m_fbuf = new kiss_fft_scalar[m_size + 2]; + m_fpacked = new kiss_fft_cpx[m_size + 2]; + m_fplanf = kiss_fftr_alloc(m_size, 0, NULL, NULL); + m_fplani = kiss_fftr_alloc(m_size, 1, NULL, NULL); + } + + ~D_KISSFFT() { + kiss_fftr_free(m_fplanf); + kiss_fftr_free(m_fplani); + kiss_fft_cleanup(); + + delete[] m_fbuf; + delete[] m_fpacked; + + if (m_frb) delete[] m_frb; + if (m_drb) delete[] m_drb; + } + + void initFloat() { } + void initDouble() { } + + void packFloat(const float *R__ re, const float *R__ im) { + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { + m_fpacked[i].r = re[i]; + m_fpacked[i].i = im[i]; + } + } + + void unpackFloat(float *R__ re, float *R__ im) { + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { + re[i] = m_fpacked[i].r; + im[i] = m_fpacked[i].i; + } + } + + void packDouble(const double *R__ re, const double *R__ im) { + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { + m_fpacked[i].r = float(re[i]); + m_fpacked[i].i = float(im[i]); + } + } + + void unpackDouble(double *R__ re, double *R__ im) { + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { + re[i] = double(m_fpacked[i].r); + im[i] = double(m_fpacked[i].i); + } + } + + void forward(const double *R__ realIn, double *R__ realOut, double *R__ imagOut) { + + for (int i = 0; i < m_size; ++i) { + m_fbuf[i] = float(realIn[i]); + } + + kiss_fftr(m_fplanf, m_fbuf, m_fpacked); + unpackDouble(realOut, imagOut); + } + + void forwardPolar(const double *R__ realIn, double *R__ magOut, double *R__ phaseOut) { + + for (int i = 0; i < m_size; ++i) { + m_fbuf[i] = float(realIn[i]); + } + + kiss_fftr(m_fplanf, m_fbuf, m_fpacked); + + const int hs = m_size/2; + + for (int i = 0; i <= hs; ++i) { + magOut[i] = sqrt(double(m_fpacked[i].r) * double(m_fpacked[i].r) + + double(m_fpacked[i].i) * double(m_fpacked[i].i)); + } + + for (int i = 0; i <= hs; ++i) { + phaseOut[i] = atan2(double(m_fpacked[i].i), double(m_fpacked[i].r)); + } + } + + void forwardMagnitude(const double *R__ realIn, double *R__ magOut) { + + for (int i = 0; i < m_size; ++i) { + m_fbuf[i] = float(realIn[i]); + } + + kiss_fftr(m_fplanf, m_fbuf, m_fpacked); + + const int hs = m_size/2; + + for (int i = 0; i <= hs; ++i) { + magOut[i] = sqrt(double(m_fpacked[i].r) * double(m_fpacked[i].r) + + double(m_fpacked[i].i) * double(m_fpacked[i].i)); + } + } + + void forward(const float *R__ realIn, float *R__ realOut, float *R__ imagOut) { + + kiss_fftr(m_fplanf, realIn, m_fpacked); + unpackFloat(realOut, imagOut); + } + + void forwardPolar(const float *R__ realIn, float *R__ magOut, float *R__ phaseOut) { + + kiss_fftr(m_fplanf, realIn, m_fpacked); + + const int hs = m_size/2; + + for (int i = 0; i <= hs; ++i) { + magOut[i] = sqrtf(m_fpacked[i].r * m_fpacked[i].r + + m_fpacked[i].i * m_fpacked[i].i); + } + + for (int i = 0; i <= hs; ++i) { + phaseOut[i] = atan2f(m_fpacked[i].i, m_fpacked[i].r); + } + } + + void forwardMagnitude(const float *R__ realIn, float *R__ magOut) { + + kiss_fftr(m_fplanf, realIn, m_fpacked); + + const int hs = m_size/2; + + for (int i = 0; i <= hs; ++i) { + magOut[i] = sqrtf(m_fpacked[i].r * m_fpacked[i].r + + m_fpacked[i].i * m_fpacked[i].i); + } + } + + void inverse(const double *R__ realIn, const double *R__ imagIn, double *R__ realOut) { + + packDouble(realIn, imagIn); + + kiss_fftri(m_fplani, m_fpacked, m_fbuf); + + for (int i = 0; i < m_size; ++i) { + realOut[i] = m_fbuf[i]; + } + } + + void inversePolar(const double *R__ magIn, const double *R__ phaseIn, double *R__ realOut) { + + const int hs = m_size/2; + + for (int i = 0; i <= hs; ++i) { + m_fpacked[i].r = float(magIn[i] * cos(phaseIn[i])); + m_fpacked[i].i = float(magIn[i] * sin(phaseIn[i])); + } + + kiss_fftri(m_fplani, m_fpacked, m_fbuf); + + for (int i = 0; i < m_size; ++i) { + realOut[i] = m_fbuf[i]; + } + } + + void inverseCepstral(const double *R__ magIn, double *R__ cepOut) { + + const int hs = m_size/2; + + for (int i = 0; i <= hs; ++i) { + m_fpacked[i].r = float(log(magIn[i] + 0.000001)); + m_fpacked[i].i = 0.0f; + } + + kiss_fftri(m_fplani, m_fpacked, m_fbuf); + + for (int i = 0; i < m_size; ++i) { + cepOut[i] = m_fbuf[i]; + } + } + + void inverse(const float *R__ realIn, const float *R__ imagIn, float *R__ realOut) { + + packFloat(realIn, imagIn); + kiss_fftri(m_fplani, m_fpacked, realOut); + } + + void inversePolar(const float *R__ magIn, const float *R__ phaseIn, float *R__ realOut) { + + const int hs = m_size/2; + + for (int i = 0; i <= hs; ++i) { + m_fpacked[i].r = magIn[i] * cosf(phaseIn[i]); + m_fpacked[i].i = magIn[i] * sinf(phaseIn[i]); + } + + kiss_fftri(m_fplani, m_fpacked, realOut); + } + + void inverseCepstral(const float *R__ magIn, float *R__ cepOut) { + + const int hs = m_size/2; + + for (int i = 0; i <= hs; ++i) { + m_fpacked[i].r = logf(magIn[i] + 0.000001f); + m_fpacked[i].i = 0.0f; + } + + kiss_fftri(m_fplani, m_fpacked, cepOut); + } + + 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: + const int m_size; + float* m_frb; + double* m_drb; + kiss_fftr_cfg m_fplanf; + kiss_fftr_cfg m_fplani; + kiss_fft_scalar *m_fbuf; + kiss_fft_cpx *m_fpacked; +}; + +#endif /* USE_KISSFFT */ + +#ifdef USE_BUILTIN_FFT + +class D_Cross : public FFTImpl +{ +public: + D_Cross(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]; + + int bits; + 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(const double *R__ realIn, double *R__ realOut, double *R__ imagOut) { + basefft(false, realIn, 0, m_c, m_d); + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) realOut[i] = m_c[i]; + if (imagOut) { + for (int i = 0; i <= hs; ++i) imagOut[i] = m_d[i]; + } + } + + void forwardPolar(const double *R__ realIn, double *R__ magOut, double *R__ phaseOut) { + basefft(false, realIn, 0, m_c, m_d); + const int hs = m_size/2; + for (int i = 0; i <= hs; ++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(const double *R__ realIn, double *R__ magOut) { + basefft(false, realIn, 0, m_c, m_d); + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { + magOut[i] = sqrt(m_c[i] * m_c[i] + m_d[i] * m_d[i]); + } + } + + void forward(const float *R__ realIn, float *R__ realOut, float *R__ imagOut) { + for (int i = 0; i < m_size; ++i) m_a[i] = realIn[i]; + basefft(false, m_a, 0, m_c, m_d); + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) realOut[i] = m_c[i]; + if (imagOut) { + for (int i = 0; i <= hs; ++i) imagOut[i] = m_d[i]; + } + } + + void forwardPolar(const float *R__ realIn, float *R__ magOut, float *R__ phaseOut) { + for (int i = 0; i < m_size; ++i) m_a[i] = realIn[i]; + basefft(false, m_a, 0, m_c, m_d); + const int hs = m_size/2; + for (int i = 0; i <= hs; ++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(const float *R__ realIn, float *R__ magOut) { + for (int i = 0; i < m_size; ++i) m_a[i] = realIn[i]; + basefft(false, m_a, 0, m_c, m_d); + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { + magOut[i] = sqrt(m_c[i] * m_c[i] + m_d[i] * m_d[i]); + } + } + + void inverse(const double *R__ realIn, const double *R__ imagIn, double *R__ realOut) { + const int hs = m_size/2; + for (int i = 0; i <= hs; ++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(const double *R__ magIn, const double *R__ phaseIn, double *R__ realOut) { + const int hs = m_size/2; + for (int i = 0; i <= hs; ++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 inverseCepstral(const double *R__ magIn, double *R__ cepOut) { + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { + double real = log(magIn[i] + 0.000001); + m_a[i] = real; + m_b[i] = 0.0; + if (i > 0) { + m_a[m_size-i] = real; + m_b[m_size-i] = 0.0; + } + } + basefft(true, m_a, m_b, cepOut, m_d); + } + + void inverse(const float *R__ realIn, const float *R__ imagIn, float *R__ realOut) { + const int hs = m_size/2; + for (int i = 0; i <= hs; ++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 (int i = 0; i < m_size; ++i) realOut[i] = m_c[i]; + } + + void inversePolar(const float *R__ magIn, const float *R__ phaseIn, float *R__ realOut) { + const int hs = m_size/2; + for (int i = 0; i <= hs; ++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 (int i = 0; i < m_size; ++i) realOut[i] = m_c[i]; + } + + void inverseCepstral(const float *R__ magIn, float *R__ cepOut) { + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { + float real = logf(magIn[i] + 0.000001); + m_a[i] = real; + m_b[i] = 0.0; + if (i > 0) { + m_a[m_size-i] = real; + m_b[m_size-i] = 0.0; + } + } + basefft(true, m_a, m_b, m_c, m_d); + for (int i = 0; i < m_size; ++i) cepOut[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: + const 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, const double *R__ ri, const double *R__ ii, double *R__ ro, double *R__ io); +}; + +void +D_Cross::basefft(bool inverse, const double *R__ ri, const double *R__ ii, double *R__ ro, double *R__ io) +{ + if (!ri || !ro || !io) return; + + int i, j, k, m; + int blockSize, blockEnd; + + double tr, ti; + + double angle = 2.0 * M_PI; + if (inverse) angle = -angle; + + const int n = m_size; + + if (ii) { + for (i = 0; i < n; ++i) { + ro[m_table[i]] = ri[i]; + } + for (i = 0; i < n; ++i) { + io[m_table[i]] = ii[i]; + } + } else { + for (i = 0; i < n; ++i) { + ro[m_table[i]] = ri[i]; + } + for (i = 0; i < n; ++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; + } + } +*/ +} + +#endif /* USE_BUILTIN_FFT */ + +} /* end namespace FFTs */ + +int +FFT::m_method = -1; + +FFT::FFT(int size, int debugLevel) +{ + if ((size < 2) || + (size & (size-1))) { + std::cerr << "FFT::FFT(" << size << "): power-of-two sizes only supported, minimum size 2" << std::endl; + throw InvalidSize; + } + + if (m_method == -1) { + m_method = 3; +#ifdef USE_KISSFFT + m_method = 2; +#endif +#ifdef HAVE_FFTW3 + m_method = 1; +#endif + } + + switch (m_method) { + + case 0: + std::cerr << "FFT::FFT(" << size << "): WARNING: Selected implemention not available" << std::endl; +#ifdef USE_BUILTIN_FFT + d = new FFTs::D_Cross(size); +#else + std::cerr << "FFT::FFT(" << size << "): ERROR: Fallback implementation not available!" << std::endl; + abort(); +#endif + break; + + case 1: +#ifdef HAVE_FFTW3 + if (debugLevel > 0) { + std::cerr << "FFT::FFT(" << size << "): using FFTW3 implementation" + << std::endl; + } + d = new FFTs::D_FFTW(size); +#else + std::cerr << "FFT::FFT(" << size << "): WARNING: Selected implemention not available" << std::endl; +#ifdef USE_BUILTIN_FFT + d = new FFTs::D_Cross(size); +#else + std::cerr << "FFT::FFT(" << size << "): ERROR: Fallback implementation not available!" << std::endl; + abort(); +#endif +#endif + break; + + case 2: +#ifdef USE_KISSFFT + if (debugLevel > 0) { + std::cerr << "FFT::FFT(" << size << "): using KISSFFT implementation" + << std::endl; + } + d = new FFTs::D_KISSFFT(size); +#else + std::cerr << "FFT::FFT(" << size << "): WARNING: Selected implemention not available" << std::endl; +#ifdef USE_BUILTIN_FFT + d = new FFTs::D_Cross(size); +#else + std::cerr << "FFT::FFT(" << size << "): ERROR: Fallback implementation not available!" << std::endl; + abort(); +#endif +#endif + break; + + default: +#ifdef USE_BUILTIN_FFT + std::cerr << "FFT::FFT(" << size << "): WARNING: using slow built-in implementation" << std::endl; + d = new FFTs::D_Cross(size); +#else + std::cerr << "FFT::FFT(" << size << "): ERROR: Fallback implementation not available!" << std::endl; + abort(); +#endif + break; + } +} + +FFT::~FFT() +{ + delete d; +} + +void +FFT::forward(const double *R__ realIn, double *R__ realOut, double *R__ imagOut) +{ + d->forward(realIn, realOut, imagOut); +} + +void +FFT::forwardPolar(const double *R__ realIn, double *R__ magOut, double *R__ phaseOut) +{ + d->forwardPolar(realIn, magOut, phaseOut); +} + +void +FFT::forwardMagnitude(const double *R__ realIn, double *R__ magOut) +{ + d->forwardMagnitude(realIn, magOut); +} + +void +FFT::forward(const float *R__ realIn, float *R__ realOut, float *R__ imagOut) +{ + d->forward(realIn, realOut, imagOut); +} + +void +FFT::forwardPolar(const float *R__ realIn, float *R__ magOut, float *R__ phaseOut) +{ + d->forwardPolar(realIn, magOut, phaseOut); +} + +void +FFT::forwardMagnitude(const float *R__ realIn, float *R__ magOut) +{ + d->forwardMagnitude(realIn, magOut); +} + +void +FFT::inverse(const double *R__ realIn, const double *R__ imagIn, double *R__ realOut) +{ + d->inverse(realIn, imagIn, realOut); +} + +void +FFT::inversePolar(const double *R__ magIn, const double *R__ phaseIn, double *R__ realOut) +{ + d->inversePolar(magIn, phaseIn, realOut); +} + +void +FFT::inverseCepstral(const double *R__ magIn, double *R__ cepOut) +{ + d->inverseCepstral(magIn, cepOut); +} + +void +FFT::inverse(const float *R__ realIn, const float *R__ imagIn, float *R__ realOut) +{ + d->inverse(realIn, imagIn, realOut); +} + +void +FFT::inversePolar(const float *R__ magIn, const float *R__ phaseIn, float *R__ realOut) +{ + d->inversePolar(magIn, phaseIn, realOut); +} + +void +FFT::inverseCepstral(const float *R__ magIn, float *R__ cepOut) +{ + d->inverseCepstral(magIn, cepOut); +} + +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() +{ +} + + +} diff --git a/libs/rubberband/src/FFT.cpp.mine b/libs/rubberband/src/FFT.cpp.mine new file mode 100644 index 0000000000..9066e0a5d8 --- /dev/null +++ b/libs/rubberband/src/FFT.cpp.mine @@ -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() +{ +} + + +} diff --git a/libs/rubberband/src/FFT.cpp.r3502 b/libs/rubberband/src/FFT.cpp.r3502 new file mode 100644 index 0000000000..1177d1dde4 --- /dev/null +++ b/libs/rubberband/src/FFT.cpp.r3502 @@ -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() +{ +} + + +} diff --git a/libs/rubberband/src/FFT.cpp.r3708 b/libs/rubberband/src/FFT.cpp.r3708 new file mode 100644 index 0000000000..5a655efc55 --- /dev/null +++ b/libs/rubberband/src/FFT.cpp.r3708 @@ -0,0 +1,1365 @@ +/* -*- 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 "FFT.h" +#include "Thread.h" +#include "Profiler.h" + +//#define FFT_MEASUREMENT 1 + +#define HAVE_FFTW3 // for Ardour + +#ifdef HAVE_FFTW3 +#include <fftw3.h> +#endif + +#ifdef USE_KISSFFT +#include "bsd-3rdparty/kissfft/kiss_fftr.h" +#endif + +#ifndef HAVE_FFTW3 +#ifndef USE_KISSFFT +#ifndef USE_BUILTIN_FFT +#error No FFT implementation selected! +#endif +#endif +#endif + +#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(const double *R__ realIn, double *R__ realOut, double *R__ imagOut) = 0; + virtual void forwardPolar(const double *R__ realIn, double *R__ magOut, double *R__ phaseOut) = 0; + virtual void forwardMagnitude(const double *R__ realIn, double *R__ magOut) = 0; + + virtual void forward(const float *R__ realIn, float *R__ realOut, float *R__ imagOut) = 0; + virtual void forwardPolar(const float *R__ realIn, float *R__ magOut, float *R__ phaseOut) = 0; + virtual void forwardMagnitude(const float *R__ realIn, float *R__ magOut) = 0; + + virtual void inverse(const double *R__ realIn, const double *R__ imagIn, double *R__ realOut) = 0; + virtual void inversePolar(const double *R__ magIn, const double *R__ phaseIn, double *R__ realOut) = 0; + virtual void inverseCepstral(const double *R__ magIn, double *R__ cepOut) = 0; + + virtual void inverse(const float *R__ realIn, const float *R__ imagIn, float *R__ realOut) = 0; + virtual void inversePolar(const float *R__ magIn, const float *R__ phaseIn, float *R__ realOut) = 0; + virtual void inverseCepstral(const float *R__ magIn, float *R__ cepOut) = 0; + + virtual float *getFloatTimeBuffer() = 0; + virtual double *getDoubleTimeBuffer() = 0; +}; + +namespace FFTs { + + +#ifdef HAVE_FFTW3 + +// 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 + +#if defined(FFTW_DOUBLE_ONLY) && defined(FFTW_FLOAT_ONLY) +// Can't meaningfully define both +#undef FFTW_DOUBLE_ONLY +#undef FFTW_FLOAT_ONLY +#endif + +#ifdef FFTW_DOUBLE_ONLY +#define fft_float_type double +#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 +#else +#define fft_float_type float +#endif /* FFTW_DOUBLE_ONLY */ + +#ifdef FFTW_FLOAT_ONLY +#define fft_double_type float +#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 sin sinf +#else +#define fft_double_type double +#endif /* FFTW_FLOAT_ONLY */ + +class D_FFTW : public FFTImpl +{ +public: + D_FFTW(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(); +#ifndef FFTW_DOUBLE_ONLY + if (save) saveWisdom('f'); +#endif + 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(); +#ifndef FFTW_FLOAT_ONLY + if (save) saveWisdom('d'); +#endif + 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'); +#else + if (load) loadWisdom('f'); +#endif + m_fbuf = (fft_float_type *)fftw_malloc(m_size * sizeof(fft_float_type)); + 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'); +#else + if (load) loadWisdom('d'); +#endif + m_dbuf = (fft_double_type *)fftw_malloc(m_size * sizeof(fft_double_type)); + 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(const float *R__ re, const float *R__ im) { + const int hs = m_size/2; + fftwf_complex *const R__ fpacked = m_fpacked; + for (int i = 0; i <= hs; ++i) { + fpacked[i][0] = re[i]; + } + if (im) { + for (int i = 0; i <= hs; ++i) { + fpacked[i][1] = im[i]; + } + } else { + for (int i = 0; i <= hs; ++i) { + fpacked[i][1] = 0.f; + } + } + } + + void packDouble(const double *R__ re, const double *R__ im) { + const int hs = m_size/2; + fftw_complex *const R__ dpacked = m_dpacked; + for (int i = 0; i <= hs; ++i) { + dpacked[i][0] = re[i]; + } + if (im) { + for (int i = 0; i <= hs; ++i) { + dpacked[i][1] = im[i]; + } + } else { + for (int i = 0; i <= hs; ++i) { + dpacked[i][1] = 0.0; + } + } + } + + void unpackFloat(float *R__ re, float *R__ im) { + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { + re[i] = m_fpacked[i][0]; + } + if (im) { + for (int i = 0; i <= hs; ++i) { + im[i] = m_fpacked[i][1]; + } + } + } + + void unpackDouble(double *R__ re, double *R__ im) { + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { + re[i] = m_dpacked[i][0]; + } + if (im) { + for (int i = 0; i <= hs; ++i) { + im[i] = m_dpacked[i][1]; + } + } + } + + void forward(const double *R__ realIn, double *R__ realOut, double *R__ imagOut) { + if (!m_dplanf) initDouble(); + const int sz = m_size; + fft_double_type *const R__ dbuf = m_dbuf; +#ifndef FFTW_FLOAT_ONLY + if (realIn != dbuf) +#endif + for (int i = 0; i < sz; ++i) { + dbuf[i] = realIn[i]; + } + fftw_execute(m_dplanf); + unpackDouble(realOut, imagOut); + } + + void forwardPolar(const double *R__ realIn, double *R__ magOut, double *R__ phaseOut) { + if (!m_dplanf) initDouble(); + fft_double_type *const R__ dbuf = m_dbuf; + const int sz = m_size; +#ifndef FFTW_FLOAT_ONLY + if (realIn != dbuf) +#endif + for (int i = 0; i < sz; ++i) { + dbuf[i] = realIn[i]; + } + fftw_execute(m_dplanf); + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { + magOut[i] = sqrt(m_dpacked[i][0] * m_dpacked[i][0] + + m_dpacked[i][1] * m_dpacked[i][1]); + } + for (int i = 0; i <= hs; ++i) { + phaseOut[i] = atan2(m_dpacked[i][1], m_dpacked[i][0]); + } + } + + void forwardMagnitude(const double *R__ realIn, double *R__ magOut) { + if (!m_dplanf) initDouble(); + fft_double_type *const R__ dbuf = m_dbuf; + const int sz = m_size; +#ifndef FFTW_FLOAT_ONLY + if (realIn != m_dbuf) +#endif + for (int i = 0; i < sz; ++i) { + dbuf[i] = realIn[i]; + } + fftw_execute(m_dplanf); + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { + magOut[i] = sqrt(m_dpacked[i][0] * m_dpacked[i][0] + + m_dpacked[i][1] * m_dpacked[i][1]); + } + } + + void forward(const float *R__ realIn, float *R__ realOut, float *R__ imagOut) { + if (!m_fplanf) initFloat(); + fft_float_type *const R__ fbuf = m_fbuf; + const int sz = m_size; +#ifndef FFTW_DOUBLE_ONLY + if (realIn != fbuf) +#endif + for (int i = 0; i < sz; ++i) { + fbuf[i] = realIn[i]; + } + fftwf_execute(m_fplanf); + unpackFloat(realOut, imagOut); + } + + void forwardPolar(const float *R__ realIn, float *R__ magOut, float *R__ phaseOut) { + if (!m_fplanf) initFloat(); + fft_float_type *const R__ fbuf = m_fbuf; + const int sz = m_size; +#ifndef FFTW_DOUBLE_ONLY + if (realIn != fbuf) +#endif + for (int i = 0; i < sz; ++i) { + fbuf[i] = realIn[i]; + } + fftwf_execute(m_fplanf); + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { + magOut[i] = sqrtf(m_fpacked[i][0] * m_fpacked[i][0] + + m_fpacked[i][1] * m_fpacked[i][1]); + } + for (int i = 0; i <= hs; ++i) { + phaseOut[i] = atan2f(m_fpacked[i][1], m_fpacked[i][0]) ; + } + } + + void forwardMagnitude(const float *R__ realIn, float *R__ magOut) { + if (!m_fplanf) initFloat(); + fft_float_type *const R__ fbuf = m_fbuf; + const int sz = m_size; +#ifndef FFTW_DOUBLE_ONLY + if (realIn != fbuf) +#endif + for (int i = 0; i < sz; ++i) { + fbuf[i] = realIn[i]; + } + fftwf_execute(m_fplanf); + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { + magOut[i] = sqrtf(m_fpacked[i][0] * m_fpacked[i][0] + + m_fpacked[i][1] * m_fpacked[i][1]); + } + } + + void inverse(const double *R__ realIn, const double *R__ imagIn, double *R__ realOut) { + if (!m_dplanf) initDouble(); + packDouble(realIn, imagIn); + fftw_execute(m_dplani); + const int sz = m_size; + fft_double_type *const R__ dbuf = m_dbuf; +#ifndef FFTW_FLOAT_ONLY + if (realOut != dbuf) +#endif + for (int i = 0; i < sz; ++i) { + realOut[i] = dbuf[i]; + } + } + + void inversePolar(const double *R__ magIn, const double *R__ phaseIn, double *R__ realOut) { + if (!m_dplanf) initDouble(); + const int hs = m_size/2; + fftw_complex *const R__ dpacked = m_dpacked; + for (int i = 0; i <= hs; ++i) { + dpacked[i][0] = magIn[i] * cos(phaseIn[i]); + } + for (int i = 0; i <= hs; ++i) { + dpacked[i][1] = magIn[i] * sin(phaseIn[i]); + } + fftw_execute(m_dplani); + const int sz = m_size; + fft_double_type *const R__ dbuf = m_dbuf; +#ifndef FFTW_FLOAT_ONLY + if (realOut != dbuf) +#endif + for (int i = 0; i < sz; ++i) { + realOut[i] = dbuf[i]; + } + } + + void inverseCepstral(const double *R__ magIn, double *R__ cepOut) { + if (!m_dplanf) initDouble(); + fft_double_type *const R__ dbuf = m_dbuf; + fftw_complex *const R__ dpacked = m_dpacked; + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { + dpacked[i][0] = log(magIn[i] + 0.000001); + } + for (int i = 0; i <= hs; ++i) { + dpacked[i][1] = 0.0; + } + fftw_execute(m_dplani); + const int sz = m_size; +#ifndef FFTW_FLOAT_ONLY + if (cepOut != dbuf) +#endif + for (int i = 0; i < sz; ++i) { + cepOut[i] = dbuf[i]; + } + } + + void inverse(const float *R__ realIn, const float *R__ imagIn, float *R__ realOut) { + if (!m_fplanf) initFloat(); + packFloat(realIn, imagIn); + fftwf_execute(m_fplani); + const int sz = m_size; + fft_float_type *const R__ fbuf = m_fbuf; +#ifndef FFTW_DOUBLE_ONLY + if (realOut != fbuf) +#endif + for (int i = 0; i < sz; ++i) { + realOut[i] = fbuf[i]; + } + } + + void inversePolar(const float *R__ magIn, const float *R__ phaseIn, float *R__ realOut) { + if (!m_fplanf) initFloat(); + const int hs = m_size/2; + fftwf_complex *const R__ fpacked = m_fpacked; + for (int i = 0; i <= hs; ++i) { + fpacked[i][0] = magIn[i] * cosf(phaseIn[i]); + } + for (int i = 0; i <= hs; ++i) { + fpacked[i][1] = magIn[i] * sinf(phaseIn[i]); + } + fftwf_execute(m_fplani); + const int sz = m_size; + fft_float_type *const R__ fbuf = m_fbuf; +#ifndef FFTW_DOUBLE_ONLY + if (realOut != fbuf) +#endif + for (int i = 0; i < sz; ++i) { + realOut[i] = fbuf[i]; + } + } + + void inverseCepstral(const float *R__ magIn, float *R__ cepOut) { + if (!m_fplanf) initFloat(); + const int hs = m_size/2; + fftwf_complex *const R__ fpacked = m_fpacked; + for (int i = 0; i <= hs; ++i) { + fpacked[i][0] = logf(magIn[i] + 0.000001f); + } + for (int i = 0; i <= hs; ++i) { + fpacked[i][1] = 0.f; + } + fftwf_execute(m_fplani); + const int sz = m_size; + fft_float_type *const R__ fbuf = m_fbuf; +#ifndef FFTW_DOUBLE_ONLY + if (cepOut != fbuf) +#endif + for (int i = 0; i < sz; ++i) { + cepOut[i] = 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; + const int m_size; + static int m_extantf; + static int m_extantd; + static Mutex m_extantMutex; +}; + +int +D_FFTW::m_extantf = 0; + +int +D_FFTW::m_extantd = 0; + +Mutex +D_FFTW::m_extantMutex; + +#endif /* HAVE_FFTW3 */ + +#ifdef USE_KISSFFT + +class D_KISSFFT : public FFTImpl +{ +public: + D_KISSFFT(int size) : + m_size(size), + m_frb(0), + m_drb(0), + m_fplanf(0), + m_fplani(0) + { +#ifdef FIXED_POINT +#error KISSFFT is not configured for float values +#endif + if (sizeof(kiss_fft_scalar) != sizeof(float)) { + std::cerr << "ERROR: KISSFFT is not configured for float values" + << std::endl; + } + + m_fbuf = new kiss_fft_scalar[m_size + 2]; + m_fpacked = new kiss_fft_cpx[m_size + 2]; + m_fplanf = kiss_fftr_alloc(m_size, 0, NULL, NULL); + m_fplani = kiss_fftr_alloc(m_size, 1, NULL, NULL); + } + + ~D_KISSFFT() { + kiss_fftr_free(m_fplanf); + kiss_fftr_free(m_fplani); + kiss_fft_cleanup(); + + delete[] m_fbuf; + delete[] m_fpacked; + + if (m_frb) delete[] m_frb; + if (m_drb) delete[] m_drb; + } + + void initFloat() { } + void initDouble() { } + + void packFloat(const float *R__ re, const float *R__ im) { + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { + m_fpacked[i].r = re[i]; + m_fpacked[i].i = im[i]; + } + } + + void unpackFloat(float *R__ re, float *R__ im) { + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { + re[i] = m_fpacked[i].r; + im[i] = m_fpacked[i].i; + } + } + + void packDouble(const double *R__ re, const double *R__ im) { + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { + m_fpacked[i].r = float(re[i]); + m_fpacked[i].i = float(im[i]); + } + } + + void unpackDouble(double *R__ re, double *R__ im) { + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { + re[i] = double(m_fpacked[i].r); + im[i] = double(m_fpacked[i].i); + } + } + + void forward(const double *R__ realIn, double *R__ realOut, double *R__ imagOut) { + + for (int i = 0; i < m_size; ++i) { + m_fbuf[i] = float(realIn[i]); + } + + kiss_fftr(m_fplanf, m_fbuf, m_fpacked); + unpackDouble(realOut, imagOut); + } + + void forwardPolar(const double *R__ realIn, double *R__ magOut, double *R__ phaseOut) { + + for (int i = 0; i < m_size; ++i) { + m_fbuf[i] = float(realIn[i]); + } + + kiss_fftr(m_fplanf, m_fbuf, m_fpacked); + + const int hs = m_size/2; + + for (int i = 0; i <= hs; ++i) { + magOut[i] = sqrt(double(m_fpacked[i].r) * double(m_fpacked[i].r) + + double(m_fpacked[i].i) * double(m_fpacked[i].i)); + } + + for (int i = 0; i <= hs; ++i) { + phaseOut[i] = atan2(double(m_fpacked[i].i), double(m_fpacked[i].r)); + } + } + + void forwardMagnitude(const double *R__ realIn, double *R__ magOut) { + + for (int i = 0; i < m_size; ++i) { + m_fbuf[i] = float(realIn[i]); + } + + kiss_fftr(m_fplanf, m_fbuf, m_fpacked); + + const int hs = m_size/2; + + for (int i = 0; i <= hs; ++i) { + magOut[i] = sqrt(double(m_fpacked[i].r) * double(m_fpacked[i].r) + + double(m_fpacked[i].i) * double(m_fpacked[i].i)); + } + } + + void forward(const float *R__ realIn, float *R__ realOut, float *R__ imagOut) { + + kiss_fftr(m_fplanf, realIn, m_fpacked); + unpackFloat(realOut, imagOut); + } + + void forwardPolar(const float *R__ realIn, float *R__ magOut, float *R__ phaseOut) { + + kiss_fftr(m_fplanf, realIn, m_fpacked); + + const int hs = m_size/2; + + for (int i = 0; i <= hs; ++i) { + magOut[i] = sqrtf(m_fpacked[i].r * m_fpacked[i].r + + m_fpacked[i].i * m_fpacked[i].i); + } + + for (int i = 0; i <= hs; ++i) { + phaseOut[i] = atan2f(m_fpacked[i].i, m_fpacked[i].r); + } + } + + void forwardMagnitude(const float *R__ realIn, float *R__ magOut) { + + kiss_fftr(m_fplanf, realIn, m_fpacked); + + const int hs = m_size/2; + + for (int i = 0; i <= hs; ++i) { + magOut[i] = sqrtf(m_fpacked[i].r * m_fpacked[i].r + + m_fpacked[i].i * m_fpacked[i].i); + } + } + + void inverse(const double *R__ realIn, const double *R__ imagIn, double *R__ realOut) { + + packDouble(realIn, imagIn); + + kiss_fftri(m_fplani, m_fpacked, m_fbuf); + + for (int i = 0; i < m_size; ++i) { + realOut[i] = m_fbuf[i]; + } + } + + void inversePolar(const double *R__ magIn, const double *R__ phaseIn, double *R__ realOut) { + + const int hs = m_size/2; + + for (int i = 0; i <= hs; ++i) { + m_fpacked[i].r = float(magIn[i] * cos(phaseIn[i])); + m_fpacked[i].i = float(magIn[i] * sin(phaseIn[i])); + } + + kiss_fftri(m_fplani, m_fpacked, m_fbuf); + + for (int i = 0; i < m_size; ++i) { + realOut[i] = m_fbuf[i]; + } + } + + void inverseCepstral(const double *R__ magIn, double *R__ cepOut) { + + const int hs = m_size/2; + + for (int i = 0; i <= hs; ++i) { + m_fpacked[i].r = float(log(magIn[i] + 0.000001)); + m_fpacked[i].i = 0.0f; + } + + kiss_fftri(m_fplani, m_fpacked, m_fbuf); + + for (int i = 0; i < m_size; ++i) { + cepOut[i] = m_fbuf[i]; + } + } + + void inverse(const float *R__ realIn, const float *R__ imagIn, float *R__ realOut) { + + packFloat(realIn, imagIn); + kiss_fftri(m_fplani, m_fpacked, realOut); + } + + void inversePolar(const float *R__ magIn, const float *R__ phaseIn, float *R__ realOut) { + + const int hs = m_size/2; + + for (int i = 0; i <= hs; ++i) { + m_fpacked[i].r = magIn[i] * cosf(phaseIn[i]); + m_fpacked[i].i = magIn[i] * sinf(phaseIn[i]); + } + + kiss_fftri(m_fplani, m_fpacked, realOut); + } + + void inverseCepstral(const float *R__ magIn, float *R__ cepOut) { + + const int hs = m_size/2; + + for (int i = 0; i <= hs; ++i) { + m_fpacked[i].r = logf(magIn[i] + 0.000001f); + m_fpacked[i].i = 0.0f; + } + + kiss_fftri(m_fplani, m_fpacked, cepOut); + } + + 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: + const int m_size; + float* m_frb; + double* m_drb; + kiss_fftr_cfg m_fplanf; + kiss_fftr_cfg m_fplani; + kiss_fft_scalar *m_fbuf; + kiss_fft_cpx *m_fpacked; +}; + +#endif /* USE_KISSFFT */ + +#ifdef USE_BUILTIN_FFT + +class D_Cross : public FFTImpl +{ +public: + D_Cross(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]; + + int bits; + 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(const double *R__ realIn, double *R__ realOut, double *R__ imagOut) { + basefft(false, realIn, 0, m_c, m_d); + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) realOut[i] = m_c[i]; + if (imagOut) { + for (int i = 0; i <= hs; ++i) imagOut[i] = m_d[i]; + } + } + + void forwardPolar(const double *R__ realIn, double *R__ magOut, double *R__ phaseOut) { + basefft(false, realIn, 0, m_c, m_d); + const int hs = m_size/2; + for (int i = 0; i <= hs; ++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(const double *R__ realIn, double *R__ magOut) { + basefft(false, realIn, 0, m_c, m_d); + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { + magOut[i] = sqrt(m_c[i] * m_c[i] + m_d[i] * m_d[i]); + } + } + + void forward(const float *R__ realIn, float *R__ realOut, float *R__ imagOut) { + for (int i = 0; i < m_size; ++i) m_a[i] = realIn[i]; + basefft(false, m_a, 0, m_c, m_d); + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) realOut[i] = m_c[i]; + if (imagOut) { + for (int i = 0; i <= hs; ++i) imagOut[i] = m_d[i]; + } + } + + void forwardPolar(const float *R__ realIn, float *R__ magOut, float *R__ phaseOut) { + for (int i = 0; i < m_size; ++i) m_a[i] = realIn[i]; + basefft(false, m_a, 0, m_c, m_d); + const int hs = m_size/2; + for (int i = 0; i <= hs; ++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(const float *R__ realIn, float *R__ magOut) { + for (int i = 0; i < m_size; ++i) m_a[i] = realIn[i]; + basefft(false, m_a, 0, m_c, m_d); + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { + magOut[i] = sqrt(m_c[i] * m_c[i] + m_d[i] * m_d[i]); + } + } + + void inverse(const double *R__ realIn, const double *R__ imagIn, double *R__ realOut) { + const int hs = m_size/2; + for (int i = 0; i <= hs; ++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(const double *R__ magIn, const double *R__ phaseIn, double *R__ realOut) { + const int hs = m_size/2; + for (int i = 0; i <= hs; ++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 inverseCepstral(const double *R__ magIn, double *R__ cepOut) { + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { + double real = log(magIn[i] + 0.000001); + m_a[i] = real; + m_b[i] = 0.0; + if (i > 0) { + m_a[m_size-i] = real; + m_b[m_size-i] = 0.0; + } + } + basefft(true, m_a, m_b, cepOut, m_d); + } + + void inverse(const float *R__ realIn, const float *R__ imagIn, float *R__ realOut) { + const int hs = m_size/2; + for (int i = 0; i <= hs; ++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 (int i = 0; i < m_size; ++i) realOut[i] = m_c[i]; + } + + void inversePolar(const float *R__ magIn, const float *R__ phaseIn, float *R__ realOut) { + const int hs = m_size/2; + for (int i = 0; i <= hs; ++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 (int i = 0; i < m_size; ++i) realOut[i] = m_c[i]; + } + + void inverseCepstral(const float *R__ magIn, float *R__ cepOut) { + const int hs = m_size/2; + for (int i = 0; i <= hs; ++i) { + float real = logf(magIn[i] + 0.000001); + m_a[i] = real; + m_b[i] = 0.0; + if (i > 0) { + m_a[m_size-i] = real; + m_b[m_size-i] = 0.0; + } + } + basefft(true, m_a, m_b, m_c, m_d); + for (int i = 0; i < m_size; ++i) cepOut[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: + const 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, const double *R__ ri, const double *R__ ii, double *R__ ro, double *R__ io); +}; + +void +D_Cross::basefft(bool inverse, const double *R__ ri, const double *R__ ii, double *R__ ro, double *R__ io) +{ + if (!ri || !ro || !io) return; + + int i, j, k, m; + int blockSize, blockEnd; + + double tr, ti; + + double angle = 2.0 * M_PI; + if (inverse) angle = -angle; + + const int n = m_size; + + if (ii) { + for (i = 0; i < n; ++i) { + ro[m_table[i]] = ri[i]; + } + for (i = 0; i < n; ++i) { + io[m_table[i]] = ii[i]; + } + } else { + for (i = 0; i < n; ++i) { + ro[m_table[i]] = ri[i]; + } + for (i = 0; i < n; ++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; + } + } +*/ +} + +#endif /* USE_BUILTIN_FFT */ + +} /* end namespace FFTs */ + +int +FFT::m_method = -1; + +FFT::FFT(int size, int debugLevel) +{ + if ((size < 2) || + (size & (size-1))) { + std::cerr << "FFT::FFT(" << size << "): power-of-two sizes only supported, minimum size 2" << std::endl; + throw InvalidSize; + } + + if (m_method == -1) { + m_method = 3; +#ifdef USE_KISSFFT + m_method = 2; +#endif +#ifdef HAVE_FFTW3 + m_method = 1; +#endif + } + + switch (m_method) { + + case 0: + std::cerr << "FFT::FFT(" << size << "): WARNING: Selected implemention not available" << std::endl; +#ifdef USE_BUILTIN_FFT + d = new FFTs::D_Cross(size); +#else + std::cerr << "FFT::FFT(" << size << "): ERROR: Fallback implementation not available!" << std::endl; + abort(); +#endif + break; + + case 1: +#ifdef HAVE_FFTW3 + if (debugLevel > 0) { + std::cerr << "FFT::FFT(" << size << "): using FFTW3 implementation" + << std::endl; + } + d = new FFTs::D_FFTW(size); +#else + std::cerr << "FFT::FFT(" << size << "): WARNING: Selected implemention not available" << std::endl; +#ifdef USE_BUILTIN_FFT + d = new FFTs::D_Cross(size); +#else + std::cerr << "FFT::FFT(" << size << "): ERROR: Fallback implementation not available!" << std::endl; + abort(); +#endif +#endif + break; + + case 2: +#ifdef USE_KISSFFT + if (debugLevel > 0) { + std::cerr << "FFT::FFT(" << size << "): using KISSFFT implementation" + << std::endl; + } + d = new FFTs::D_KISSFFT(size); +#else + std::cerr << "FFT::FFT(" << size << "): WARNING: Selected implemention not available" << std::endl; +#ifdef USE_BUILTIN_FFT + d = new FFTs::D_Cross(size); +#else + std::cerr << "FFT::FFT(" << size << "): ERROR: Fallback implementation not available!" << std::endl; + abort(); +#endif +#endif + break; + + default: +#ifdef USE_BUILTIN_FFT + std::cerr << "FFT::FFT(" << size << "): WARNING: using slow built-in implementation" << std::endl; + d = new FFTs::D_Cross(size); +#else + std::cerr << "FFT::FFT(" << size << "): ERROR: Fallback implementation not available!" << std::endl; + abort(); +#endif + break; + } +} + +FFT::~FFT() +{ + delete d; +} + +void +FFT::forward(const double *R__ realIn, double *R__ realOut, double *R__ imagOut) +{ + d->forward(realIn, realOut, imagOut); +} + +void +FFT::forwardPolar(const double *R__ realIn, double *R__ magOut, double *R__ phaseOut) +{ + d->forwardPolar(realIn, magOut, phaseOut); +} + +void +FFT::forwardMagnitude(const double *R__ realIn, double *R__ magOut) +{ + d->forwardMagnitude(realIn, magOut); +} + +void +FFT::forward(const float *R__ realIn, float *R__ realOut, float *R__ imagOut) +{ + d->forward(realIn, realOut, imagOut); +} + +void +FFT::forwardPolar(const float *R__ realIn, float *R__ magOut, float *R__ phaseOut) +{ + d->forwardPolar(realIn, magOut, phaseOut); +} + +void +FFT::forwardMagnitude(const float *R__ realIn, float *R__ magOut) +{ + d->forwardMagnitude(realIn, magOut); +} + +void +FFT::inverse(const double *R__ realIn, const double *R__ imagIn, double *R__ realOut) +{ + d->inverse(realIn, imagIn, realOut); +} + +void +FFT::inversePolar(const double *R__ magIn, const double *R__ phaseIn, double *R__ realOut) +{ + d->inversePolar(magIn, phaseIn, realOut); +} + +void +FFT::inverseCepstral(const double *R__ magIn, double *R__ cepOut) +{ + d->inverseCepstral(magIn, cepOut); +} + +void +FFT::inverse(const float *R__ realIn, const float *R__ imagIn, float *R__ realOut) +{ + d->inverse(realIn, imagIn, realOut); +} + +void +FFT::inversePolar(const float *R__ magIn, const float *R__ phaseIn, float *R__ realOut) +{ + d->inversePolar(magIn, phaseIn, realOut); +} + +void +FFT::inverseCepstral(const float *R__ magIn, float *R__ cepOut) +{ + d->inverseCepstral(magIn, cepOut); +} + +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() +{ +} + + +} diff --git a/libs/rubberband/src/FFT.h b/libs/rubberband/src/FFT.h new file mode 100644 index 0000000000..b31d925d36 --- /dev/null +++ b/libs/rubberband/src/FFT.h @@ -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 + diff --git a/libs/rubberband/src/HighFrequencyAudioCurve.cpp b/libs/rubberband/src/HighFrequencyAudioCurve.cpp new file mode 100644 index 0000000000..987cf76a66 --- /dev/null +++ b/libs/rubberband/src/HighFrequencyAudioCurve.cpp @@ -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; +} + +} + diff --git a/libs/rubberband/src/HighFrequencyAudioCurve.h b/libs/rubberband/src/HighFrequencyAudioCurve.h new file mode 100644 index 0000000000..d42513f9b7 --- /dev/null +++ b/libs/rubberband/src/HighFrequencyAudioCurve.h @@ -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 diff --git a/libs/rubberband/src/PercussiveAudioCurve.cpp b/libs/rubberband/src/PercussiveAudioCurve.cpp new file mode 100644 index 0000000000..f8925961f3 --- /dev/null +++ b/libs/rubberband/src/PercussiveAudioCurve.cpp @@ -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); +} + +} + diff --git a/libs/rubberband/src/PercussiveAudioCurve.h b/libs/rubberband/src/PercussiveAudioCurve.h new file mode 100644 index 0000000000..29c4fb7fd9 --- /dev/null +++ b/libs/rubberband/src/PercussiveAudioCurve.h @@ -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 diff --git a/libs/rubberband/src/Profiler.cpp b/libs/rubberband/src/Profiler.cpp new file mode 100644 index 0000000000..df148d48e3 --- /dev/null +++ b/libs/rubberband/src/Profiler.cpp @@ -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 + +} diff --git a/libs/rubberband/src/Profiler.h b/libs/rubberband/src/Profiler.h new file mode 100644 index 0000000000..616a553ecb --- /dev/null +++ b/libs/rubberband/src/Profiler.h @@ -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 diff --git a/libs/rubberband/src/Resampler.cpp b/libs/rubberband/src/Resampler.cpp new file mode 100644 index 0000000000..296537f085 --- /dev/null +++ b/libs/rubberband/src/Resampler.cpp @@ -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(); +} + +} diff --git a/libs/rubberband/src/Resampler.h b/libs/rubberband/src/Resampler.h new file mode 100644 index 0000000000..3c4af40e8e --- /dev/null +++ b/libs/rubberband/src/Resampler.h @@ -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 diff --git a/libs/rubberband/src/RingBuffer.h b/libs/rubberband/src/RingBuffer.h new file mode 100644 index 0000000000..07312169a6 --- /dev/null +++ b/libs/rubberband/src/RingBuffer.h @@ -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_ diff --git a/libs/rubberband/src/RubberBandStretcher.cpp b/libs/rubberband/src/RubberBandStretcher.cpp new file mode 100644 index 0000000000..7e249c6633 --- /dev/null +++ b/libs/rubberband/src/RubberBandStretcher.cpp @@ -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); +} + +} + diff --git a/libs/rubberband/src/Scavenger.h b/libs/rubberband/src/Scavenger.h new file mode 100644 index 0000000000..d1b6ca9ffa --- /dev/null +++ b/libs/rubberband/src/Scavenger.h @@ -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 diff --git a/libs/rubberband/src/SilentAudioCurve.cpp b/libs/rubberband/src/SilentAudioCurve.cpp new file mode 100644 index 0000000000..b44564671c --- /dev/null +++ b/libs/rubberband/src/SilentAudioCurve.cpp @@ -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; +} + +} + diff --git a/libs/rubberband/src/SilentAudioCurve.h b/libs/rubberband/src/SilentAudioCurve.h new file mode 100644 index 0000000000..ec7009a871 --- /dev/null +++ b/libs/rubberband/src/SilentAudioCurve.h @@ -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 diff --git a/libs/rubberband/src/SpectralDifferenceAudioCurve.cpp b/libs/rubberband/src/SpectralDifferenceAudioCurve.cpp new file mode 100644 index 0000000000..0deec53c87 --- /dev/null +++ b/libs/rubberband/src/SpectralDifferenceAudioCurve.cpp @@ -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; +} + +} + diff --git a/libs/rubberband/src/SpectralDifferenceAudioCurve.h b/libs/rubberband/src/SpectralDifferenceAudioCurve.h new file mode 100644 index 0000000000..6ab0af9c02 --- /dev/null +++ b/libs/rubberband/src/SpectralDifferenceAudioCurve.h @@ -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 diff --git a/libs/rubberband/src/StretchCalculator.cpp b/libs/rubberband/src/StretchCalculator.cpp new file mode 100644 index 0000000000..1541759762 --- /dev/null +++ b/libs/rubberband/src/StretchCalculator.cpp @@ -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; + } + } +} + +} + diff --git a/libs/rubberband/src/StretchCalculator.h b/libs/rubberband/src/StretchCalculator.h new file mode 100644 index 0000000000..e79c8e3c1e --- /dev/null +++ b/libs/rubberband/src/StretchCalculator.h @@ -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> ®ionCurve, + 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 diff --git a/libs/rubberband/src/StretcherChannelData.cpp b/libs/rubberband/src/StretcherChannelData.cpp new file mode 100644 index 0000000000..8378975cbd --- /dev/null +++ b/libs/rubberband/src/StretcherChannelData.cpp @@ -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; +} + +} diff --git a/libs/rubberband/src/StretcherChannelData.h b/libs/rubberband/src/StretcherChannelData.h new file mode 100644 index 0000000000..b56a6e07dc --- /dev/null +++ b/libs/rubberband/src/StretcherChannelData.h @@ -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 diff --git a/libs/rubberband/src/StretcherImpl.cpp b/libs/rubberband/src/StretcherImpl.cpp new file mode 100644 index 0000000000..126b001b83 --- /dev/null +++ b/libs/rubberband/src/StretcherImpl.cpp @@ -0,0 +1,1118 @@ +/* -*- 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" +#include "PercussiveAudioCurve.h" +#include "HighFrequencyAudioCurve.h" +#include "SpectralDifferenceAudioCurve.h" +#include "SilentAudioCurve.h" +#include "ConstantAudioCurve.h" +#include "StretchCalculator.h" +#include "StretcherChannelData.h" +#include "Resampler.h" +#include "Profiler.h" + +#include <cassert> +#include <cmath> +#include <set> +#include <map> + +using std::cerr; +using std::endl; +using std::vector; +using std::map; +using std::set; +using std::max; +using std::min; + + +namespace RubberBand { + +const size_t +RubberBandStretcher::Impl::m_defaultIncrement = 256; + +const size_t +RubberBandStretcher::Impl::m_defaultWindowSize = 2048; + +int +RubberBandStretcher::Impl::m_defaultDebugLevel = 0; + + + +RubberBandStretcher::Impl::Impl(size_t sampleRate, + size_t channels, + Options options, + double initialTimeRatio, + double initialPitchScale) : + m_sampleRate(sampleRate), + m_channels(channels), + m_timeRatio(initialTimeRatio), + m_pitchScale(initialPitchScale), + m_windowSize(m_defaultWindowSize), + m_increment(m_defaultIncrement), + m_outbufSize(m_defaultWindowSize * 2), + m_maxProcessSize(m_defaultWindowSize), + m_expectedInputDuration(0), + m_threaded(false), + m_realtime(false), + m_options(options), + m_debugLevel(m_defaultDebugLevel), + m_mode(JustCreated), + m_window(0), + m_studyFFT(0), + m_spaceAvailable("space"), + m_inputDuration(0), + m_silentHistory(0), + m_lastProcessOutputIncrements(16), + m_lastProcessPhaseResetDf(16), + m_phaseResetAudioCurve(0), + m_stretchAudioCurve(0), + m_silentAudioCurve(0), + m_stretchCalculator(0), + m_freq0(600), + m_freq1(1200), + m_freq2(12000), + m_baseWindowSize(m_defaultWindowSize) +{ + + if (m_debugLevel > 0) { + cerr << "RubberBandStretcher::Impl::Impl: rate = " << m_sampleRate << ", options = " << options << endl; + } + + // Window size will vary according to the audio sample rate, but + // we don't let it drop below the 48k default + m_rateMultiple = float(m_sampleRate) / 48000.f; + if (m_rateMultiple < 1.f) m_rateMultiple = 1.f; + m_baseWindowSize = roundUp(int(m_defaultWindowSize * m_rateMultiple)); + + if ((options & OptionWindowShort) || (options & OptionWindowLong)) { + if ((options & OptionWindowShort) && (options & OptionWindowLong)) { + cerr << "RubberBandStretcher::Impl::Impl: Cannot specify OptionWindowLong and OptionWindowShort together; falling back to OptionWindowStandard" << endl; + } else if (options & OptionWindowShort) { + m_baseWindowSize = m_baseWindowSize / 2; + if (m_debugLevel > 0) { + cerr << "setting baseWindowSize to " << m_baseWindowSize << endl; + } + } else if (options & OptionWindowLong) { + m_baseWindowSize = m_baseWindowSize * 2; + if (m_debugLevel > 0) { + cerr << "setting baseWindowSize to " << m_baseWindowSize << endl; + } + } + m_windowSize = m_baseWindowSize; + m_outbufSize = m_baseWindowSize * 2; + m_maxProcessSize = m_baseWindowSize; + } + + if (m_options & OptionProcessRealTime) { + + m_realtime = true; + + if (!(m_options & OptionStretchPrecise)) { + m_options |= OptionStretchPrecise; + } + } + + if (m_channels > 1) { + + m_threaded = true; + + if (m_realtime) { + m_threaded = false; + } else if (m_options & OptionThreadingNever) { + m_threaded = false; + } else if (!(m_options & OptionThreadingAlways) && + !system_is_multiprocessor()) { + m_threaded = false; + } + + if (m_threaded && m_debugLevel > 0) { + cerr << "Going multithreaded..." << endl; + } + } + + configure(); +} + +RubberBandStretcher::Impl::~Impl() +{ + if (m_threaded) { + MutexLocker locker(&m_threadSetMutex); + for (set<ProcessThread *>::iterator i = m_threadSet.begin(); + i != m_threadSet.end(); ++i) { + if (m_debugLevel > 0) { + cerr << "RubberBandStretcher::~RubberBandStretcher: joining (channel " << *i << ")" << endl; + } + (*i)->abandon(); + (*i)->wait(); + delete *i; + } + } + + for (size_t c = 0; c < m_channels; ++c) { + delete m_channelData[c]; + } + + delete m_phaseResetAudioCurve; + delete m_stretchAudioCurve; + delete m_silentAudioCurve; + delete m_stretchCalculator; + delete m_studyFFT; + + for (map<size_t, Window<float> *>::iterator i = m_windows.begin(); + i != m_windows.end(); ++i) { + delete i->second; + } +} + +void +RubberBandStretcher::Impl::reset() +{ + if (m_threaded) { + m_threadSetMutex.lock(); + for (set<ProcessThread *>::iterator i = m_threadSet.begin(); + i != m_threadSet.end(); ++i) { + if (m_debugLevel > 0) { + cerr << "RubberBandStretcher::~RubberBandStretcher: joining (channel " << *i << ")" << endl; + } + (*i)->abandon(); + (*i)->wait(); + delete *i; + } + m_threadSet.clear(); + } + + for (size_t c = 0; c < m_channels; ++c) { + m_channelData[c]->reset(); + } + + m_mode = JustCreated; + if (m_phaseResetAudioCurve) m_phaseResetAudioCurve->reset(); + if (m_stretchAudioCurve) m_stretchAudioCurve->reset(); + if (m_silentAudioCurve) m_silentAudioCurve->reset(); + m_inputDuration = 0; + m_silentHistory = 0; + + if (m_threaded) m_threadSetMutex.unlock(); + + reconfigure(); +} + +void +RubberBandStretcher::Impl::setTimeRatio(double ratio) +{ + if (!m_realtime) { + if (m_mode == Studying || m_mode == Processing) { + cerr << "RubberBandStretcher::Impl::setTimeRatio: Cannot set ratio while studying or processing in non-RT mode" << endl; + return; + } + } + + if (ratio == m_timeRatio) return; + m_timeRatio = ratio; + + reconfigure(); +} + +void +RubberBandStretcher::Impl::setPitchScale(double fs) +{ + if (!m_realtime) { + if (m_mode == Studying || m_mode == Processing) { + cerr << "RubberBandStretcher::Impl::setPitchScale: Cannot set ratio while studying or processing in non-RT mode" << endl; + return; + } + } + + if (fs == m_pitchScale) return; + + bool was1 = (m_pitchScale == 1.f); + bool rbs = resampleBeforeStretching(); + + m_pitchScale = fs; + + reconfigure(); + + if (!(m_options & OptionPitchHighConsistency) && + (was1 || resampleBeforeStretching() != rbs) && + m_pitchScale != 1.f) { + + // resampling mode has changed + for (int c = 0; c < int(m_channels); ++c) { + if (m_channelData[c]->resampler) { + m_channelData[c]->resampler->reset(); + } + } + } +} + +double +RubberBandStretcher::Impl::getTimeRatio() const +{ + return m_timeRatio; +} + +double +RubberBandStretcher::Impl::getPitchScale() const +{ + return m_pitchScale; +} + +void +RubberBandStretcher::Impl::setExpectedInputDuration(size_t samples) +{ + if (samples == m_expectedInputDuration) return; + m_expectedInputDuration = samples; + + reconfigure(); +} + +void +RubberBandStretcher::Impl::setMaxProcessSize(size_t samples) +{ + if (samples <= m_maxProcessSize) return; + m_maxProcessSize = samples; + + reconfigure(); +} + +float +RubberBandStretcher::Impl::getFrequencyCutoff(int n) const +{ + switch (n) { + case 0: return m_freq0; + case 1: return m_freq1; + case 2: return m_freq2; + } + return 0.f; +} + +void +RubberBandStretcher::Impl::setFrequencyCutoff(int n, float f) +{ + switch (n) { + case 0: m_freq0 = f; break; + case 1: m_freq1 = f; break; + case 2: m_freq2 = f; break; + } +} + +double +RubberBandStretcher::Impl::getEffectiveRatio() const +{ + // Returns the ratio that the internal time stretcher needs to + // achieve, not the resulting duration ratio of the output (which + // is simply m_timeRatio). + + // A frequency shift is achieved using an additional time shift, + // followed by resampling back to the original time shift to + // change the pitch. Note that the resulting frequency change is + // fixed, as it is effected by the resampler -- in contrast to + // time shifting, which is variable aiming to place the majority + // of the stretch or squash in low-interest regions of audio. + + return m_timeRatio * m_pitchScale; +} + +size_t +RubberBandStretcher::Impl::roundUp(size_t value) +{ + if (!(value & (value - 1))) return value; + int bits = 0; + while (value) { ++bits; value >>= 1; } + value = 1 << bits; + return value; +} + +void +RubberBandStretcher::Impl::calculateSizes() +{ + size_t inputIncrement = m_defaultIncrement; + size_t windowSize = m_baseWindowSize; + size_t outputIncrement; + + double r = getEffectiveRatio(); + + if (m_realtime) { + + if (r < 1) { + + bool rsb = (m_pitchScale < 1.0 && !resampleBeforeStretching()); + float windowIncrRatio = 4.5; + if (r == 1.0) windowIncrRatio = 4; + else if (rsb) windowIncrRatio = 4.5; + else windowIncrRatio = 6; + + inputIncrement = int(windowSize / windowIncrRatio); + outputIncrement = int(floor(inputIncrement * r)); + + // Very long stretch or very low pitch shift + if (outputIncrement < m_defaultIncrement / 4) { + if (outputIncrement < 1) outputIncrement = 1; + while (outputIncrement < m_defaultIncrement / 4 && + windowSize < m_baseWindowSize * 4) { + outputIncrement *= 2; + inputIncrement = lrint(ceil(outputIncrement / r)); + windowSize = roundUp(lrint(ceil(inputIncrement * windowIncrRatio))); + } + } + + } else { + + bool rsb = (m_pitchScale > 1.0 && resampleBeforeStretching()); + float windowIncrRatio = 4.5; + if (r == 1.0) windowIncrRatio = 4; + else if (rsb) windowIncrRatio = 4.5; + else windowIncrRatio = 6; + + outputIncrement = int(windowSize / windowIncrRatio); + inputIncrement = int(outputIncrement / r); + while (outputIncrement > 1024 * m_rateMultiple && + inputIncrement > 1) { + outputIncrement /= 2; + inputIncrement = int(outputIncrement / r); + } + size_t minwin = roundUp(lrint(outputIncrement * windowIncrRatio)); + if (windowSize < minwin) windowSize = minwin; + + if (rsb) { +// cerr << "adjusting window size from " << windowSize; + size_t newWindowSize = roundUp(lrint(windowSize / m_pitchScale)); + if (newWindowSize < 512) newWindowSize = 512; + size_t div = windowSize / newWindowSize; + if (inputIncrement > div && outputIncrement > div) { + inputIncrement /= div; + outputIncrement /= div; + windowSize /= div; + } +// cerr << " to " << windowSize << " (inputIncrement = " << inputIncrement << ", outputIncrement = " << outputIncrement << ")" << endl; + } + } + + } else { + + if (r < 1) { + inputIncrement = windowSize / 4; + while (inputIncrement >= 512) inputIncrement /= 2; + outputIncrement = int(floor(inputIncrement * r)); + if (outputIncrement < 1) { + outputIncrement = 1; + inputIncrement = roundUp(lrint(ceil(outputIncrement / r))); + windowSize = inputIncrement * 4; + } + } else { + outputIncrement = windowSize / 6; + inputIncrement = int(outputIncrement / r); + while (outputIncrement > 1024 && inputIncrement > 1) { + outputIncrement /= 2; + inputIncrement = int(outputIncrement / r); + } + windowSize = std::max(windowSize, roundUp(outputIncrement * 6)); + if (r > 5) while (windowSize < 8192) windowSize *= 2; + } + } + + if (m_expectedInputDuration > 0) { + while (inputIncrement * 4 > m_expectedInputDuration && + inputIncrement > 1) { + inputIncrement /= 2; + } + } + + // windowSize can be almost anything, but it can't be greater than + // 4 * m_baseWindowSize unless ratio is less than 1/1024. + + m_windowSize = windowSize; + m_increment = inputIncrement; + + // When squashing, the greatest theoretically possible output + // increment is the input increment. When stretching adaptively + // the sky's the limit in principle, but we expect + // StretchCalculator to restrict itself to using no more than + // twice the basic output increment (i.e. input increment times + // ratio) for any chunk. + + if (m_debugLevel > 0) { + cerr << "configure: effective ratio = " << getEffectiveRatio() << endl; + cerr << "configure: window size = " << m_windowSize << ", increment = " << m_increment << " (approx output increment = " << int(lrint(m_increment * getEffectiveRatio())) << ")" << endl; + } + + if (m_windowSize > m_maxProcessSize) { + m_maxProcessSize = m_windowSize; + } + + m_outbufSize = + size_t + (ceil(max + (m_maxProcessSize / m_pitchScale, + m_windowSize * 2 * (m_timeRatio > 1.f ? m_timeRatio : 1.f)))); + + if (m_realtime) { + // This headroom is so as to try to avoid reallocation when + // the pitch scale changes + m_outbufSize = m_outbufSize * 16; + } else { + if (m_threaded) { + // This headroom is to permit the processing threads to + // run ahead of the buffer output drainage; the exact + // amount of headroom is a question of tuning rather than + // results + m_outbufSize = m_outbufSize * 16; + } + } + + if (m_debugLevel > 0) { + cerr << "configure: outbuf size = " << m_outbufSize << endl; + } +} + +void +RubberBandStretcher::Impl::configure() +{ +// std::cerr << "configure[" << this << "]: realtime = " << m_realtime << ", pitch scale = " +// << m_pitchScale << ", channels = " << m_channels << std::endl; + + size_t prevWindowSize = m_windowSize; + size_t prevOutbufSize = m_outbufSize; + if (m_windows.empty()) { + prevWindowSize = 0; + prevOutbufSize = 0; + } + + calculateSizes(); + + bool windowSizeChanged = (prevWindowSize != m_windowSize); + bool outbufSizeChanged = (prevOutbufSize != m_outbufSize); + + // This function may be called at any time in non-RT mode, after a + // parameter has changed. It shouldn't be legal to call it after + // processing has already begun. + + // This function is only called once (on construction) in RT + // mode. After that reconfigure() does the work in a hopefully + // RT-safe way. + + set<size_t> windowSizes; + if (m_realtime) { + windowSizes.insert(m_baseWindowSize); + windowSizes.insert(m_baseWindowSize / 2); + windowSizes.insert(m_baseWindowSize * 2); +// windowSizes.insert(m_baseWindowSize * 4); + } + windowSizes.insert(m_windowSize); + + if (windowSizeChanged) { + + for (set<size_t>::const_iterator i = windowSizes.begin(); + i != windowSizes.end(); ++i) { + if (m_windows.find(*i) == m_windows.end()) { + m_windows[*i] = new Window<float>(HanningWindow, *i); + } + } + m_window = m_windows[m_windowSize]; + + if (m_debugLevel > 0) { + cerr << "Window area: " << m_window->getArea() << "; synthesis window area: " << m_window->getArea() << endl; + } + } + + if (windowSizeChanged || outbufSizeChanged) { + + for (size_t c = 0; c < m_channelData.size(); ++c) { + delete m_channelData[c]; + } + m_channelData.clear(); + + for (size_t c = 0; c < m_channels; ++c) { + m_channelData.push_back + (new ChannelData(windowSizes, 1, m_windowSize, m_outbufSize)); + } + } + + if (!m_realtime && windowSizeChanged) { + delete m_studyFFT; + m_studyFFT = new FFT(m_windowSize, m_debugLevel); + m_studyFFT->initFloat(); + } + + if (m_pitchScale != 1.0 || + (m_options & OptionPitchHighConsistency) || + m_realtime) { + + for (size_t c = 0; c < m_channels; ++c) { + + if (m_channelData[c]->resampler) continue; + + m_channelData[c]->resampler = + new Resampler(Resampler::FastestTolerable, 1, 4096 * 16, + m_debugLevel); + + // rbs is the amount of buffer space we think we'll need + // for resampling; but allocate a sensible amount in case + // the pitch scale changes during use + size_t rbs = + lrintf(ceil((m_increment * m_timeRatio * 2) / m_pitchScale)); + if (rbs < m_increment * 16) rbs = m_increment * 16; + m_channelData[c]->setResampleBufSize(rbs); + } + } + + // stretchAudioCurve is unused in RT mode; phaseResetAudioCurve, + // silentAudioCurve and stretchCalculator however are used in all + // modes + + delete m_phaseResetAudioCurve; + m_phaseResetAudioCurve = new PercussiveAudioCurve + (m_sampleRate, m_windowSize); + + delete m_silentAudioCurve; + m_silentAudioCurve = new SilentAudioCurve + (m_sampleRate, m_windowSize); + + if (!m_realtime) { + delete m_stretchAudioCurve; + if (!(m_options & OptionStretchPrecise)) { + m_stretchAudioCurve = new SpectralDifferenceAudioCurve + (m_sampleRate, m_windowSize); + } else { + m_stretchAudioCurve = new ConstantAudioCurve + (m_sampleRate, m_windowSize); + } + } + + delete m_stretchCalculator; + m_stretchCalculator = new StretchCalculator + (m_sampleRate, m_increment, + !(m_options & OptionTransientsSmooth)); + + m_stretchCalculator->setDebugLevel(m_debugLevel); + m_inputDuration = 0; + + // Prepare the inbufs with half a chunk of emptiness. The centre + // point of the first processing chunk for the onset detector + // should be the first sample of the audio, and we continue until + // we can no longer centre a chunk within the input audio. The + // number of onset detector chunks will be the number of audio + // samples input, divided by the input increment, plus one. + + // In real-time mode, we don't do this prefill -- it's better to + // start with a swoosh than introduce more latency, and we don't + // want gaps when the ratio changes. + + if (!m_realtime) { + for (size_t c = 0; c < m_channels; ++c) { + m_channelData[c]->reset(); + m_channelData[c]->inbuf->zero(m_windowSize/2); + } + } +} + + +void +RubberBandStretcher::Impl::reconfigure() +{ + if (!m_realtime) { + if (m_mode == Studying) { + // stop and calculate the stretch curve so far, then reset + // the df vectors + calculateStretch(); + m_phaseResetDf.clear(); + m_stretchDf.clear(); + m_silence.clear(); + m_inputDuration = 0; + } + configure(); + } + + size_t prevWindowSize = m_windowSize; + size_t prevOutbufSize = m_outbufSize; + + calculateSizes(); + + // There are various allocations in this function, but they should + // never happen in normal use -- they just recover from the case + // where not all of the things we need were correctly created when + // we first configured (for whatever reason). This is intended to + // be "effectively" realtime safe. The same goes for + // ChannelData::setOutbufSize and setWindowSize. + + if (m_windowSize != prevWindowSize) { + + if (m_windows.find(m_windowSize) == m_windows.end()) { + std::cerr << "WARNING: reconfigure(): window allocation (size " << m_windowSize << ") required in RT mode" << std::endl; + m_windows[m_windowSize] = new Window<float>(HanningWindow, m_windowSize); + } + m_window = m_windows[m_windowSize]; + + for (size_t c = 0; c < m_channels; ++c) { + m_channelData[c]->setWindowSize(m_windowSize); + } + } + + if (m_outbufSize != prevOutbufSize) { + for (size_t c = 0; c < m_channels; ++c) { + m_channelData[c]->setOutbufSize(m_outbufSize); + } + } + + if (m_pitchScale != 1.0) { + for (size_t c = 0; c < m_channels; ++c) { + + if (m_channelData[c]->resampler) continue; + + std::cerr << "WARNING: reconfigure(): resampler construction required in RT mode" << std::endl; + + m_channelData[c]->resampler = + new Resampler(Resampler::FastestTolerable, 1, m_windowSize, + m_debugLevel); + + m_channelData[c]->setResampleBufSize + (lrintf(ceil((m_increment * m_timeRatio * 2) / m_pitchScale))); + } + } + + if (m_windowSize != prevWindowSize) { + m_phaseResetAudioCurve->setWindowSize(m_windowSize); + } +} + +size_t +RubberBandStretcher::Impl::getLatency() const +{ + if (!m_realtime) return 0; + return int((m_windowSize/2) / m_pitchScale + 1); +} + +void +RubberBandStretcher::Impl::setTransientsOption(Options options) +{ + if (!m_realtime) { + cerr << "RubberBandStretcher::Impl::setTransientsOption: Not permissible in non-realtime mode" << endl; + return; + } + int mask = (OptionTransientsMixed | OptionTransientsSmooth | OptionTransientsCrisp); + m_options &= ~mask; + options &= mask; + m_options |= options; + + m_stretchCalculator->setUseHardPeaks + (!(m_options & OptionTransientsSmooth)); +} + +void +RubberBandStretcher::Impl::setPhaseOption(Options options) +{ + int mask = (OptionPhaseLaminar | OptionPhaseIndependent); + m_options &= ~mask; + options &= mask; + m_options |= options; +} + +void +RubberBandStretcher::Impl::setFormantOption(Options options) +{ + int mask = (OptionFormantShifted | OptionFormantPreserved); + m_options &= ~mask; + options &= mask; + m_options |= options; +} + +void +RubberBandStretcher::Impl::setPitchOption(Options options) +{ + if (!m_realtime) { + cerr << "RubberBandStretcher::Impl::setPitchOption: Pitch option is not used in non-RT mode" << endl; + return; + } + + Options prior = m_options; + + int mask = (OptionPitchHighQuality | + OptionPitchHighSpeed | + OptionPitchHighConsistency); + m_options &= ~mask; + options &= mask; + m_options |= options; + + if (prior != m_options) reconfigure(); +} + +void +RubberBandStretcher::Impl::study(const float *const *input, size_t samples, bool final) +{ + Profiler profiler("RubberBandStretcher::Impl::study"); + + if (m_realtime) { + if (m_debugLevel > 1) { + cerr << "RubberBandStretcher::Impl::study: Not meaningful in realtime mode" << endl; + } + return; + } + + if (m_mode == Processing || m_mode == Finished) { + cerr << "RubberBandStretcher::Impl::study: Cannot study after processing" << endl; + return; + } + m_mode = Studying; + + size_t consumed = 0; + + ChannelData &cd = *m_channelData[0]; + RingBuffer<float> &inbuf = *cd.inbuf; + + const float *mixdown; + float *mdalloc = 0; + + if (m_channels > 1 || final) { + // mix down into a single channel for analysis + mdalloc = new float[samples]; + for (size_t i = 0; i < samples; ++i) { + if (i < samples) { + mdalloc[i] = input[0][i]; + } else { + mdalloc[i] = 0.f; + } + } + for (size_t c = 1; c < m_channels; ++c) { + for (size_t i = 0; i < samples; ++i) { + mdalloc[i] += input[c][i]; + } + } + for (size_t i = 0; i < samples; ++i) { + mdalloc[i] /= m_channels; + } + mixdown = mdalloc; + } else { + mixdown = input[0]; + } + + while (consumed < samples) { + + size_t writable = inbuf.getWriteSpace(); + writable = min(writable, samples - consumed); + + if (writable == 0) { + // warn + cerr << "WARNING: writable == 0 (consumed = " << consumed << ", samples = " << samples << ")" << endl; + } else { + inbuf.write(mixdown + consumed, writable); + consumed += writable; + } + + while ((inbuf.getReadSpace() >= int(m_windowSize)) || + (final && (inbuf.getReadSpace() >= int(m_windowSize/2)))) { + + // We know we have at least m_windowSize samples available + // in m_inbuf. We need to peek m_windowSize of them for + // processing, and then skip m_increment to advance the + // read pointer. + + // cd.accumulator is not otherwise used during studying, + // so we can use it as a temporary buffer here + + size_t got = inbuf.peek(cd.accumulator, m_windowSize); + assert(final || got == m_windowSize); + + m_window->cut(cd.accumulator); + + // We don't need the fftshift for studying, as we're only + // interested in magnitude + + m_studyFFT->forwardMagnitude(cd.accumulator, cd.fltbuf); + + float df = m_phaseResetAudioCurve->process(cd.fltbuf, m_increment); + m_phaseResetDf.push_back(df); + +// cout << m_phaseResetDf.size() << " [" << final << "] -> " << df << " \t: "; + + df = m_stretchAudioCurve->process(cd.fltbuf, m_increment); + m_stretchDf.push_back(df); + + df = m_silentAudioCurve->process(cd.fltbuf, m_increment); + bool silent = (df > 0.f); + if (silent && m_debugLevel > 1) { + cerr << "silence found at " << m_inputDuration << endl; + } + m_silence.push_back(silent); + +// cout << df << endl; + + // We have augmented the input by m_windowSize/2 so + // that the first chunk is centred on the first audio + // sample. We want to ensure that m_inputDuration + // contains the exact input duration without including + // this extra bit. We just add up all the increments + // here, and deduct the extra afterwards. + + m_inputDuration += m_increment; +// cerr << "incr input duration by increment: " << m_increment << " -> " << m_inputDuration << endl; + inbuf.skip(m_increment); + } + } + + if (final) { + int rs = inbuf.getReadSpace(); + m_inputDuration += rs; +// cerr << "incr input duration by read space: " << rs << " -> " << m_inputDuration << endl; + + if (m_inputDuration > m_windowSize/2) { // deducting the extra + m_inputDuration -= m_windowSize/2; + } + } + + if (m_channels > 1) delete[] mdalloc; +} + +vector<int> +RubberBandStretcher::Impl::getOutputIncrements() const +{ + if (!m_realtime) { + return m_outputIncrements; + } else { + vector<int> increments; + while (m_lastProcessOutputIncrements.getReadSpace() > 0) { + increments.push_back(m_lastProcessOutputIncrements.readOne()); + } + return increments; + } +} + +vector<float> +RubberBandStretcher::Impl::getPhaseResetCurve() const +{ + if (!m_realtime) { + return m_phaseResetDf; + } else { + vector<float> df; + while (m_lastProcessPhaseResetDf.getReadSpace() > 0) { + df.push_back(m_lastProcessPhaseResetDf.readOne()); + } + return df; + } +} + +vector<int> +RubberBandStretcher::Impl::getExactTimePoints() const +{ + std::vector<int> points; + if (!m_realtime) { + std::vector<StretchCalculator::Peak> peaks = + m_stretchCalculator->getLastCalculatedPeaks(); + for (size_t i = 0; i < peaks.size(); ++i) { + points.push_back(peaks[i].chunk); + } + } + return points; +} + +void +RubberBandStretcher::Impl::calculateStretch() +{ + Profiler profiler("RubberBandStretcher::Impl::calculateStretch"); + + std::vector<int> increments = m_stretchCalculator->calculate + (getEffectiveRatio(), + m_inputDuration, + m_phaseResetDf, + m_stretchDf); + + int history = 0; + for (size_t i = 0; i < increments.size(); ++i) { + if (i >= m_silence.size()) break; + if (m_silence[i]) ++history; + else history = 0; + if (history >= int(m_windowSize / m_increment) && increments[i] >= 0) { + increments[i] = -increments[i]; + if (m_debugLevel > 1) { + std::cerr << "phase reset on silence (silent history == " + << history << ")" << std::endl; + } + } + } + + if (m_outputIncrements.empty()) m_outputIncrements = increments; + else { + for (size_t i = 0; i < increments.size(); ++i) { + m_outputIncrements.push_back(increments[i]); + } + } + + return; +} + +void +RubberBandStretcher::Impl::setDebugLevel(int level) +{ + m_debugLevel = level; + if (m_stretchCalculator) m_stretchCalculator->setDebugLevel(level); +} + +size_t +RubberBandStretcher::Impl::getSamplesRequired() const +{ + Profiler profiler("RubberBandStretcher::Impl::getSamplesRequired"); + + size_t reqd = 0; + + for (size_t c = 0; c < m_channels; ++c) { + + size_t reqdHere = 0; + + ChannelData &cd = *m_channelData[c]; + RingBuffer<float> &inbuf = *cd.inbuf; + + size_t rs = inbuf.getReadSpace(); + + // See notes in testInbufReadSpace + + if (rs < m_windowSize && !cd.draining) { + + if (cd.inputSize == -1) { + reqdHere = m_windowSize - rs; + if (reqdHere > reqd) reqd = reqdHere; + continue; + } + + if (rs == 0) { + reqdHere = m_windowSize; + if (reqdHere > reqd) reqd = reqdHere; + continue; + } + } + } + + return reqd; +} + +void +RubberBandStretcher::Impl::process(const float *const *input, size_t samples, bool final) +{ + Profiler profiler("RubberBandStretcher::Impl::process"); + + if (m_mode == Finished) { + cerr << "RubberBandStretcher::Impl::process: Cannot process again after final chunk" << endl; + return; + } + + if (m_mode == JustCreated || m_mode == Studying) { + + if (m_mode == Studying) { + calculateStretch(); + } + + for (size_t c = 0; c < m_channels; ++c) { + m_channelData[c]->reset(); + m_channelData[c]->inbuf->zero(m_windowSize/2); + } + + if (m_threaded) { + MutexLocker locker(&m_threadSetMutex); + + for (size_t c = 0; c < m_channels; ++c) { + ProcessThread *thread = new ProcessThread(this, c); + m_threadSet.insert(thread); + thread->start(); + } + + if (m_debugLevel > 0) { + cerr << m_channels << " threads created" << endl; + } + } + + m_mode = Processing; + } + + bool allConsumed = false; + + size_t *consumed = (size_t *)alloca(m_channels * sizeof(size_t)); + for (size_t c = 0; c < m_channels; ++c) { + consumed[c] = 0; + } + + while (!allConsumed) { + +//#ifndef NO_THREADING +// if (m_threaded) { +// pthread_mutex_lock(&m_inputProcessedMutex); +// } +//#endif + + // In a threaded mode, our "consumed" counters only indicate + // the number of samples that have been taken into the input + // ring buffers waiting to be processed by the process thread. + // In non-threaded mode, "consumed" counts the number that + // have actually been processed. + + allConsumed = true; + + for (size_t c = 0; c < m_channels; ++c) { + consumed[c] += consumeChannel(c, + input[c] + consumed[c], + samples - consumed[c], + final); + if (consumed[c] < samples) { + allConsumed = false; +// cerr << "process: waiting on input consumption for channel " << c << endl; + } else { + if (final) { + m_channelData[c]->inputSize = m_channelData[c]->inCount; + } +// cerr << "process: happy with channel " << c << endl; + } + if (!m_threaded && !m_realtime) { + bool any = false, last = false; + processChunks(c, any, last); + } + } + + if (m_realtime) { + // When running in real time, we need to process both + // channels in step because we will need to use the sum of + // their frequency domain representations as the input to + // the realtime onset detector + processOneChunk(); + } + + if (m_threaded) { + for (ThreadSet::iterator i = m_threadSet.begin(); + i != m_threadSet.end(); ++i) { + (*i)->signalDataAvailable(); + } + if (!allConsumed) { + m_spaceAvailable.wait(500); + } +/* + } else { + if (!allConsumed) { + cerr << "RubberBandStretcher::Impl::process: ERROR: Too much data provided to process() call -- either call setMaxProcessSize() beforehand, or provide only getSamplesRequired() frames at a time" << endl; + for (size_t c = 0; c < m_channels; ++c) { + cerr << "channel " << c << ": " << samples << " provided, " << consumed[c] << " consumed" << endl; + } +// break; + } +*/ + } + +// if (!allConsumed) cerr << "process looping" << endl; + + } + +// cerr << "process returning" << endl; + + if (final) m_mode = Finished; +} + + +} + diff --git a/libs/rubberband/src/StretcherImpl.h b/libs/rubberband/src/StretcherImpl.h new file mode 100644 index 0000000000..996c61b7ef --- /dev/null +++ b/libs/rubberband/src/StretcherImpl.h @@ -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 diff --git a/libs/rubberband/src/StretcherProcess.cpp b/libs/rubberband/src/StretcherProcess.cpp new file mode 100644 index 0000000000..59f678bf6e --- /dev/null +++ b/libs/rubberband/src/StretcherProcess.cpp @@ -0,0 +1,1175 @@ +/* -*- 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" +#include "PercussiveAudioCurve.h" +#include "HighFrequencyAudioCurve.h" +#include "ConstantAudioCurve.h" +#include "StretchCalculator.h" +#include "StretcherChannelData.h" +#include "Resampler.h" +#include "Profiler.h" + +#include <cstring> +#include <cassert> +#include <cmath> +#include <set> +#include <map> +#include <deque> + + +using std::cerr; +using std::endl; + +namespace RubberBand { + +RubberBandStretcher::Impl::ProcessThread::ProcessThread(Impl *s, size_t c) : + m_s(s), + m_channel(c), + m_dataAvailable(std::string("data ") + char('A' + c)), + m_abandoning(false) +{ } + +void +RubberBandStretcher::Impl::ProcessThread::run() +{ + if (m_s->m_debugLevel > 1) { + cerr << "thread " << m_channel << " getting going" << endl; + } + + ChannelData &cd = *m_s->m_channelData[m_channel]; + + while (cd.inputSize == -1 || + cd.inbuf->getReadSpace() > 0) { + +// if (cd.inputSize != -1) { +// cerr << "inputSize == " << cd.inputSize +// << ", readSpace == " << cd.inbuf->getReadSpace() << endl; +// } + + bool any = false, last = false; + m_s->processChunks(m_channel, any, last); + + if (last) break; + + if (any) m_s->m_spaceAvailable.signal(); + + m_dataAvailable.lock(); + if (!m_s->testInbufReadSpace(m_channel) && !m_abandoning) { + m_dataAvailable.wait(); + } else { + m_dataAvailable.unlock(); + } + + if (m_abandoning) { + if (m_s->m_debugLevel > 1) { + cerr << "thread " << m_channel << " abandoning" << endl; + } + return; + } + } + + bool any = false, last = false; + m_s->processChunks(m_channel, any, last); + m_s->m_spaceAvailable.signal(); + + if (m_s->m_debugLevel > 1) { + cerr << "thread " << m_channel << " done" << endl; + } +} + +void +RubberBandStretcher::Impl::ProcessThread::signalDataAvailable() +{ + m_dataAvailable.signal(); +} + +void +RubberBandStretcher::Impl::ProcessThread::abandon() +{ + m_abandoning = true; +} + +bool +RubberBandStretcher::Impl::resampleBeforeStretching() const +{ + // We can't resample before stretching in offline mode, because + // the stretch calculation is based on doing it the other way + // around. It would take more work (and testing) to enable this. + if (!m_realtime) return false; + + if (m_options & OptionPitchHighQuality) { + return (m_pitchScale < 1.0); // better sound + } else if (m_options & OptionPitchHighConsistency) { + return false; + } else { + return (m_pitchScale > 1.0); // better performance + } +} + +size_t +RubberBandStretcher::Impl::consumeChannel(size_t c, const float *input, + size_t samples, bool final) +{ + Profiler profiler("RubberBandStretcher::Impl::consumeChannel"); + + ChannelData &cd = *m_channelData[c]; + RingBuffer<float> &inbuf = *cd.inbuf; + + size_t toWrite = samples; + size_t writable = inbuf.getWriteSpace(); + + bool resampling = resampleBeforeStretching(); + + if (resampling) { + + toWrite = int(ceil(samples / m_pitchScale)); + if (writable < toWrite) { + samples = int(floor(writable * m_pitchScale)); + if (samples == 0) return 0; + } + + size_t reqSize = int(ceil(samples / m_pitchScale)); + if (reqSize > cd.resamplebufSize) { + cerr << "WARNING: RubberBandStretcher::Impl::consumeChannel: resizing resampler buffer from " + << cd.resamplebufSize << " to " << reqSize << endl; + cd.setResampleBufSize(reqSize); + } + + + toWrite = cd.resampler->resample(&input, + &cd.resamplebuf, + samples, + 1.0 / m_pitchScale, + final); + + } + + if (writable < toWrite) { + if (resampling) { + return 0; + } + toWrite = writable; + } + + if (resampling) { + inbuf.write(cd.resamplebuf, toWrite); + cd.inCount += samples; + return samples; + } else { + inbuf.write(input, toWrite); + cd.inCount += toWrite; + return toWrite; + } +} + +void +RubberBandStretcher::Impl::processChunks(size_t c, bool &any, bool &last) +{ + Profiler profiler("RubberBandStretcher::Impl::processChunks"); + + // Process as many chunks as there are available on the input + // buffer for channel c. This requires that the increments have + // already been calculated. + + ChannelData &cd = *m_channelData[c]; + + last = false; + any = false; + + while (!last) { + + if (!testInbufReadSpace(c)) { +// cerr << "not enough input" << endl; + break; + } + + any = true; + + if (!cd.draining) { + size_t got = cd.inbuf->peek(cd.fltbuf, m_windowSize); + assert(got == m_windowSize || cd.inputSize >= 0); + cd.inbuf->skip(m_increment); + analyseChunk(c); + } + + bool phaseReset = false; + size_t phaseIncrement, shiftIncrement; + getIncrements(c, phaseIncrement, shiftIncrement, phaseReset); + + last = processChunkForChannel(c, phaseIncrement, shiftIncrement, phaseReset); + cd.chunkCount++; + if (m_debugLevel > 2) { + cerr << "channel " << c << ": last = " << last << ", chunkCount = " << cd.chunkCount << endl; + } + } +} + +bool +RubberBandStretcher::Impl::processOneChunk() +{ + Profiler profiler("RubberBandStretcher::Impl::processOneChunk"); + + // Process a single chunk for all channels, provided there is + // enough data on each channel for at least one chunk. This is + // able to calculate increments as it goes along. + + for (size_t c = 0; c < m_channels; ++c) { + if (!testInbufReadSpace(c)) return false; + ChannelData &cd = *m_channelData[c]; + if (!cd.draining) { + size_t got = cd.inbuf->peek(cd.fltbuf, m_windowSize); + assert(got == m_windowSize || cd.inputSize >= 0); + cd.inbuf->skip(m_increment); + analyseChunk(c); + } + } + + bool phaseReset = false; + size_t phaseIncrement, shiftIncrement; + if (!getIncrements(0, phaseIncrement, shiftIncrement, phaseReset)) { + calculateIncrements(phaseIncrement, shiftIncrement, phaseReset); + } + + bool last = false; + for (size_t c = 0; c < m_channels; ++c) { + last = processChunkForChannel(c, phaseIncrement, shiftIncrement, phaseReset); + m_channelData[c]->chunkCount++; + } + + return last; +} + +bool +RubberBandStretcher::Impl::testInbufReadSpace(size_t c) +{ + Profiler profiler("RubberBandStretcher::Impl::testInbufReadSpace"); + + ChannelData &cd = *m_channelData[c]; + RingBuffer<float> &inbuf = *cd.inbuf; + + size_t rs = inbuf.getReadSpace(); + + if (rs < m_windowSize && !cd.draining) { + + if (cd.inputSize == -1) { + + // Not all the input data has been written to the inbuf + // (that's why the input size is not yet set). We can't + // process, because we don't have a full chunk of data, so + // our process chunk would contain some empty padding in + // its input -- and that would give incorrect output, as + // we know there is more input to come. + + if (!m_threaded) { +// cerr << "WARNING: RubberBandStretcher: read space < chunk size (" +// << inbuf.getReadSpace() << " < " << m_windowSize +// << ") when not all input written, on processChunks for channel " << c << endl; + } + return false; + } + + if (rs == 0) { + + if (m_debugLevel > 1) { + cerr << "read space = 0, giving up" << endl; + } + return false; + + } else if (rs < m_windowSize/2) { + + if (m_debugLevel > 1) { + cerr << "read space = " << rs << ", setting draining true" << endl; + } + + cd.draining = true; + } + } + + return true; +} + +bool +RubberBandStretcher::Impl::processChunkForChannel(size_t c, + size_t phaseIncrement, + size_t shiftIncrement, + bool phaseReset) +{ + Profiler profiler("RubberBandStretcher::Impl::processChunkForChannel"); + + // Process a single chunk on a single channel. This assumes + // enough input data is available; caller must have tested this + // using e.g. testInbufReadSpace first. Return true if this is + // the last chunk on the channel. + + if (phaseReset && (m_debugLevel > 1)) { + cerr << "processChunkForChannel: phase reset found, incrs " + << phaseIncrement << ":" << shiftIncrement << endl; + } + + ChannelData &cd = *m_channelData[c]; + + if (!cd.draining) { + + // This is the normal processing case -- draining is only + // set when all the input has been used and we only need + // to write from the existing accumulator into the output. + + // We know we have enough samples available in m_inbuf -- + // this is usually m_windowSize, but we know that if fewer + // are available, it's OK to use zeroes for the rest + // (which the ring buffer will provide) because we've + // reached the true end of the data. + + // We need to peek m_windowSize samples for processing, and + // then skip m_increment to advance the read pointer. + + modifyChunk(c, phaseIncrement, phaseReset); + synthesiseChunk(c); // reads from cd.mag, cd.phase + + if (m_debugLevel > 2) { + if (phaseReset) { + for (int i = 0; i < 10; ++i) { + cd.accumulator[i] = 1.2f - (i % 3) * 1.2f; + } + } + } + } + + bool last = false; + + if (cd.draining) { + if (m_debugLevel > 1) { + cerr << "draining: accumulator fill = " << cd.accumulatorFill << " (shiftIncrement = " << shiftIncrement << ")" << endl; + } + if (shiftIncrement == 0) { + cerr << "WARNING: draining: shiftIncrement == 0, can't handle that in this context: setting to " << m_increment << endl; + shiftIncrement = m_increment; + } + if (cd.accumulatorFill <= shiftIncrement) { + if (m_debugLevel > 1) { + cerr << "reducing shift increment from " << shiftIncrement + << " to " << cd.accumulatorFill + << " and marking as last" << endl; + } + shiftIncrement = cd.accumulatorFill; + last = true; + } + } + + if (m_threaded) { + + int required = shiftIncrement; + + if (m_pitchScale != 1.0) { + required = int(required / m_pitchScale) + 1; + } + + if (cd.outbuf->getWriteSpace() < required) { + if (m_debugLevel > 0) { + cerr << "Buffer overrun on output for channel " << c << endl; + } + + //!!! The only correct thing we can do here is resize the + // buffer. We can't wait for the client thread to read + // some data out from the buffer so as to make more space, + // because the client thread is probably stuck in a + // process() call waiting for us to stow away enough input + // increments to allow the process() call to complete. + + } + } + + writeChunk(c, shiftIncrement, last); + return last; +} + +void +RubberBandStretcher::Impl::calculateIncrements(size_t &phaseIncrementRtn, + size_t &shiftIncrementRtn, + bool &phaseReset) +{ + Profiler profiler("RubberBandStretcher::Impl::calculateIncrements"); + +// cerr << "calculateIncrements" << endl; + + // Calculate the next upcoming phase and shift increment, on the + // basis that both channels are in sync. This is in contrast to + // getIncrements, which requires that all the increments have been + // calculated in advance but can then return increments + // corresponding to different chunks in different channels. + + // Requires frequency domain representations of channel data in + // the mag and phase buffers in the channel. + + // This function is only used in real-time mode. + + phaseIncrementRtn = m_increment; + shiftIncrementRtn = m_increment; + phaseReset = false; + + if (m_channels == 0) return; + + ChannelData &cd = *m_channelData[0]; + + size_t bc = cd.chunkCount; + for (size_t c = 1; c < m_channels; ++c) { + if (m_channelData[c]->chunkCount != bc) { + cerr << "ERROR: RubberBandStretcher::Impl::calculateIncrements: Channels are not in sync" << endl; + return; + } + } + + const int hs = m_windowSize/2 + 1; + + // Normally we would mix down the time-domain signal and apply a + // single FFT, or else mix down the Cartesian form of the + // frequency-domain signal. Both of those would be inefficient + // from this position. Fortunately, the onset detectors should + // work reasonably well (maybe even better?) if we just sum the + // magnitudes of the frequency-domain channel signals and forget + // about phase entirely. Normally we don't expect the channel + // phases to cancel each other, and broadband effects will still + // be apparent. + + float df = 0.f; + bool silent = false; + + if (m_channels == 1) { + + df = m_phaseResetAudioCurve->process(cd.mag, m_increment); + silent = (m_silentAudioCurve->process(cd.mag, m_increment) > 0.f); + + } else { + + double *tmp = (double *)alloca(hs * sizeof(double)); + + for (int i = 0; i < hs; ++i) { + tmp[i] = 0.0; + } + for (size_t c = 0; c < m_channels; ++c) { + for (int i = 0; i < hs; ++i) { + tmp[i] += m_channelData[c]->mag[i]; + } + } + + df = m_phaseResetAudioCurve->process(tmp, m_increment); + silent = (m_silentAudioCurve->process(tmp, m_increment) > 0.f); + } + + int incr = m_stretchCalculator->calculateSingle + (getEffectiveRatio(), df, m_increment); + + m_lastProcessPhaseResetDf.write(&df, 1); + m_lastProcessOutputIncrements.write(&incr, 1); + + if (incr < 0) { + phaseReset = true; + incr = -incr; + } + + // The returned increment is the phase increment. The shift + // increment for one chunk is the same as the phase increment for + // the following chunk (see comment below). This means we don't + // actually know the shift increment until we see the following + // phase increment... which is a bit of a problem. + + // This implies we should use this increment for the shift + // increment, and make the following phase increment the same as + // it. This means in RT mode we'll be one chunk later with our + // phase reset than we would be in non-RT mode. The sensitivity + // of the broadband onset detector may mean that this isn't a + // problem -- test it and see. + + shiftIncrementRtn = incr; + + if (cd.prevIncrement == 0) { + phaseIncrementRtn = shiftIncrementRtn; + } else { + phaseIncrementRtn = cd.prevIncrement; + } + + cd.prevIncrement = shiftIncrementRtn; + + if (silent) ++m_silentHistory; + else m_silentHistory = 0; + + if (m_silentHistory >= int(m_windowSize / m_increment) && !phaseReset) { + phaseReset = true; + if (m_debugLevel > 1) { + cerr << "calculateIncrements: phase reset on silence (silent history == " + << m_silentHistory << ")" << endl; + } + } +} + +bool +RubberBandStretcher::Impl::getIncrements(size_t channel, + size_t &phaseIncrementRtn, + size_t &shiftIncrementRtn, + bool &phaseReset) +{ + Profiler profiler("RubberBandStretcher::Impl::getIncrements"); + + if (channel >= m_channels) { + phaseIncrementRtn = m_increment; + shiftIncrementRtn = m_increment; + phaseReset = false; + return false; + } + + // There are two relevant output increments here. The first is + // the phase increment which we use when recalculating the phases + // for the current chunk; the second is the shift increment used + // to determine how far to shift the processing buffer after + // writing the chunk. The shift increment for one chunk is the + // same as the phase increment for the following chunk. + + // When an onset occurs for which we need to reset phases, the + // increment given will be negative. + + // When we reset phases, the previous shift increment (and so + // current phase increments) must have been m_increment to ensure + // consistency. + + // m_outputIncrements stores phase increments. + + ChannelData &cd = *m_channelData[channel]; + bool gotData = true; + + if (cd.chunkCount >= m_outputIncrements.size()) { +// cerr << "WARNING: RubberBandStretcher::Impl::getIncrements:" +// << " chunk count " << cd.chunkCount << " >= " +// << m_outputIncrements.size() << endl; + if (m_outputIncrements.size() == 0) { + phaseIncrementRtn = m_increment; + shiftIncrementRtn = m_increment; + phaseReset = false; + return false; + } else { + cd.chunkCount = m_outputIncrements.size()-1; + gotData = false; + } + } + + int phaseIncrement = m_outputIncrements[cd.chunkCount]; + + int shiftIncrement = phaseIncrement; + if (cd.chunkCount + 1 < m_outputIncrements.size()) { + shiftIncrement = m_outputIncrements[cd.chunkCount + 1]; + } + + if (phaseIncrement < 0) { + phaseIncrement = -phaseIncrement; + phaseReset = true; + } + + if (shiftIncrement < 0) { + shiftIncrement = -shiftIncrement; + } + + if (shiftIncrement >= int(m_windowSize)) { + cerr << "*** ERROR: RubberBandStretcher::Impl::processChunks: shiftIncrement " << shiftIncrement << " >= windowSize " << m_windowSize << " at " << cd.chunkCount << " (of " << m_outputIncrements.size() << ")" << endl; + shiftIncrement = m_windowSize; + } + + phaseIncrementRtn = phaseIncrement; + shiftIncrementRtn = shiftIncrement; + if (cd.chunkCount == 0) phaseReset = true; // don't mess with the first chunk + return gotData; +} + +void +RubberBandStretcher::Impl::analyseChunk(size_t channel) +{ + Profiler profiler("RubberBandStretcher::Impl::analyseChunk"); + + int i; + + ChannelData &cd = *m_channelData[channel]; + + double *const R__ dblbuf = cd.dblbuf; + float *const R__ fltbuf = cd.fltbuf; + + int sz = m_windowSize; + int hs = m_windowSize/2; + + // cd.fltbuf is known to contain m_windowSize samples + + m_window->cut(fltbuf); + + if (cd.oversample > 1) { + + int bufsiz = sz * cd.oversample; + int offset = (bufsiz - sz) / 2; + + // eek + + for (i = 0; i < offset; ++i) { + dblbuf[i] = 0.0; + } + for (i = 0; i < offset; ++i) { + dblbuf[bufsiz - i - 1] = 0.0; + } + for (i = 0; i < sz; ++i) { + dblbuf[offset + i] = fltbuf[i]; + } + for (i = 0; i < bufsiz / 2; ++i) { + double tmp = dblbuf[i]; + dblbuf[i] = dblbuf[i + bufsiz/2]; + dblbuf[i + bufsiz/2] = tmp; + } + } else { + for (i = 0; i < hs; ++i) { + dblbuf[i] = fltbuf[i + hs]; + dblbuf[i + hs] = fltbuf[i]; + } + } + + cd.fft->forwardPolar(dblbuf, cd.mag, cd.phase); +} + +static inline double mod(double x, double y) { return x - (y * floor(x / y)); } +static inline double princarg(double a) { return mod(a + M_PI, -2.0 * M_PI) + M_PI; } + +void +RubberBandStretcher::Impl::modifyChunk(size_t channel, + size_t outputIncrement, + bool phaseReset) +{ + Profiler profiler("RubberBandStretcher::Impl::modifyChunk"); + + ChannelData &cd = *m_channelData[channel]; + + if (phaseReset && m_debugLevel > 1) { + cerr << "phase reset: leaving phases unmodified" << endl; + } + + const double rate = m_sampleRate; + const int sz = m_windowSize; + const int count = (sz * cd.oversample) / 2; + + bool unchanged = cd.unchanged && (outputIncrement == m_increment); + bool fullReset = phaseReset; + bool laminar = !(m_options & OptionPhaseIndependent); + bool bandlimited = (m_options & OptionTransientsMixed); + int bandlow = lrint((150 * sz * cd.oversample) / rate); + int bandhigh = lrint((1000 * sz * cd.oversample) / rate); + + float freq0 = m_freq0; + float freq1 = m_freq1; + float freq2 = m_freq2; + + if (laminar) { + float r = getEffectiveRatio(); + if (r > 1) { + float rf0 = 600 + (600 * ((r-1)*(r-1)*(r-1)*2)); + float f1ratio = freq1 / freq0; + float f2ratio = freq2 / freq0; + freq0 = std::max(freq0, rf0); + freq1 = freq0 * f1ratio; + freq2 = freq0 * f2ratio; + } + } + + int limit0 = lrint((freq0 * sz * cd.oversample) / rate); + int limit1 = lrint((freq1 * sz * cd.oversample) / rate); + int limit2 = lrint((freq2 * sz * cd.oversample) / rate); + + if (limit1 < limit0) limit1 = limit0; + if (limit2 < limit1) limit2 = limit1; + + double prevInstability = 0.0; + bool prevDirection = false; + + double distance = 0.0; + const double maxdist = 8.0; + + const int lookback = 1; + + double distacc = 0.0; + + for (int i = count; i >= 0; i -= lookback) { + + bool resetThis = phaseReset; + + if (bandlimited) { + if (resetThis) { + if (i > bandlow && i < bandhigh) { + resetThis = false; + fullReset = false; + } + } + } + + double p = cd.phase[i]; + double perr = 0.0; + double outphase = p; + + double mi = maxdist; + if (i <= limit0) mi = 0.0; + else if (i <= limit1) mi = 1.0; + else if (i <= limit2) mi = 3.0; + + if (!resetThis) { + + double omega = (2 * M_PI * m_increment * i) / (sz * cd.oversample); + + double pp = cd.prevPhase[i]; + double ep = pp + omega; + perr = princarg(p - ep); + + double instability = fabs(perr - cd.prevError[i]); + bool direction = (perr > cd.prevError[i]); + + bool inherit = false; + + if (laminar) { + if (distance >= mi) { + inherit = false; + } else if (bandlimited && (i == bandhigh || i == bandlow)) { + inherit = false; + } else if (instability > prevInstability && + direction == prevDirection) { + inherit = true; + } + } + + double advance = outputIncrement * ((omega + perr) / m_increment); + + if (inherit) { + double inherited = + cd.unwrappedPhase[i + lookback] - cd.prevPhase[i + lookback]; + advance = ((advance * distance) + + (inherited * (maxdist - distance))) + / maxdist; + outphase = p + advance; + distacc += distance; + distance += 1.0; + } else { + outphase = cd.unwrappedPhase[i] + advance; + distance = 0.0; + } + + prevInstability = instability; + prevDirection = direction; + + } else { + distance = 0.0; + } + + cd.prevError[i] = perr; + cd.prevPhase[i] = p; + cd.phase[i] = outphase; + cd.unwrappedPhase[i] = outphase; + } + + if (m_debugLevel > 1) { + cerr << "mean inheritance distance = " << distacc / count << endl; + } + + if (fullReset) unchanged = true; + cd.unchanged = unchanged; + + if (unchanged && m_debugLevel > 1) { + cerr << "frame unchanged on channel " << channel << endl; + } +} + + +void +RubberBandStretcher::Impl::formantShiftChunk(size_t channel) +{ + Profiler profiler("RubberBandStretcher::Impl::formantShiftChunk"); + + ChannelData &cd = *m_channelData[channel]; + + double *const R__ mag = cd.mag; + double *const R__ envelope = cd.envelope; + double *const R__ dblbuf = cd.dblbuf; + + const int sz = m_windowSize; + const int hs = m_windowSize/2; + const double denom = sz; + + + cd.fft->inverseCepstral(mag, dblbuf); + + for (int i = 0; i < sz; ++i) { + dblbuf[i] /= denom; + } + + const int cutoff = m_sampleRate / 700; + +// cerr <<"cutoff = "<< cutoff << ", m_sampleRate/cutoff = " << m_sampleRate/cutoff << endl; + + dblbuf[0] /= 2; + dblbuf[cutoff-1] /= 2; + + for (int i = cutoff; i < sz; ++i) { + dblbuf[i] = 0.0; + } + + cd.fft->forward(dblbuf, envelope, 0); + + + for (int i = 0; i <= hs; ++i) { + envelope[i] = exp(envelope[i]); + } + for (int i = 0; i <= hs; ++i) { + mag[i] /= envelope[i]; + } + + if (m_pitchScale > 1.0) { + // scaling up, we want a new envelope that is lower by the pitch factor + for (int target = 0; target <= hs; ++target) { + int source = lrint(target * m_pitchScale); + if (source > int(m_windowSize)) { + envelope[target] = 0.0; + } else { + envelope[target] = envelope[source]; + } + } + } else { + // scaling down, we want a new envelope that is higher by the pitch factor + for (int target = hs; target > 0; ) { + --target; + int source = lrint(target * m_pitchScale); + envelope[target] = envelope[source]; + } + } + + for (int i = 0; i <= hs; ++i) { + mag[i] *= envelope[i]; + } + + cd.unchanged = false; +} + +void +RubberBandStretcher::Impl::synthesiseChunk(size_t channel) +{ + Profiler profiler("RubberBandStretcher::Impl::synthesiseChunk"); + + + if ((m_options & OptionFormantPreserved) && + (m_pitchScale != 1.0)) { + formantShiftChunk(channel); + } + + ChannelData &cd = *m_channelData[channel]; + + double *const R__ dblbuf = cd.dblbuf; + float *const R__ fltbuf = cd.fltbuf; + float *const R__ accumulator = cd.accumulator; + float *const R__ windowAccumulator = cd.windowAccumulator; + + int sz = m_windowSize; + int hs = m_windowSize/2; + int i; + + + if (!cd.unchanged) { + + cd.fft->inversePolar(cd.mag, cd.phase, cd.dblbuf); + + if (cd.oversample > 1) { + + int bufsiz = sz * cd.oversample; + int hbs = hs * cd.oversample; + int offset = (bufsiz - sz) / 2; + + for (i = 0; i < hbs; ++i) { + double tmp = dblbuf[i]; + dblbuf[i] = dblbuf[i + hbs]; + dblbuf[i + hbs] = tmp; + } + for (i = 0; i < sz; ++i) { + fltbuf[i] = float(dblbuf[i + offset]); + } + } else { + for (i = 0; i < hs; ++i) { + fltbuf[i] = float(dblbuf[i + hs]); + } + for (i = 0; i < hs; ++i) { + fltbuf[i + hs] = float(dblbuf[i]); + } + } + + float denom = float(sz * cd.oversample); + + // our ffts produced unscaled results + for (i = 0; i < sz; ++i) { + fltbuf[i] = fltbuf[i] / denom; + } + } + + m_window->cut(fltbuf); + + for (i = 0; i < sz; ++i) { + accumulator[i] += fltbuf[i]; + } + + cd.accumulatorFill = m_windowSize; + + float fixed = m_window->getArea() * 1.5f; + + for (i = 0; i < sz; ++i) { + float val = m_window->getValue(i); + windowAccumulator[i] += val * fixed; + } +} + +void +RubberBandStretcher::Impl::writeChunk(size_t channel, size_t shiftIncrement, bool last) +{ + Profiler profiler("RubberBandStretcher::Impl::writeChunk"); + + ChannelData &cd = *m_channelData[channel]; + + float *const R__ accumulator = cd.accumulator; + float *const R__ windowAccumulator = cd.windowAccumulator; + + const int sz = m_windowSize; + const int si = shiftIncrement; + + int i; + + if (m_debugLevel > 2) { + cerr << "writeChunk(" << channel << ", " << shiftIncrement << ", " << last << ")" << endl; + } + + for (i = 0; i < si; ++i) { + if (windowAccumulator[i] > 0.f) { + accumulator[i] /= windowAccumulator[i]; + } + } + + // for exact sample scaling (probably not meaningful if we + // were running in RT mode) + size_t theoreticalOut = 0; + if (cd.inputSize >= 0) { + theoreticalOut = lrint(cd.inputSize * m_timeRatio); + } + + bool resampledAlready = resampleBeforeStretching(); + + if (!resampledAlready && + (m_pitchScale != 1.0 || m_options & OptionPitchHighConsistency) && + cd.resampler) { + + size_t reqSize = int(ceil(si / m_pitchScale)); + if (reqSize > cd.resamplebufSize) { + // This shouldn't normally happen -- the buffer is + // supposed to be initialised with enough space in the + // first place. But we retain this check in case the + // pitch scale has changed since then, or the stretch + // calculator has gone mad, or something. + cerr << "WARNING: RubberBandStretcher::Impl::writeChunk: resizing resampler buffer from " + << cd.resamplebufSize << " to " << reqSize << endl; + cd.setResampleBufSize(reqSize); + } + + + size_t outframes = cd.resampler->resample(&cd.accumulator, + &cd.resamplebuf, + si, + 1.0 / m_pitchScale, + last); + + + writeOutput(*cd.outbuf, cd.resamplebuf, + outframes, cd.outCount, theoreticalOut); + + } else { + writeOutput(*cd.outbuf, accumulator, + si, cd.outCount, theoreticalOut); + } + + for (i = 0; i < sz - si; ++i) { + accumulator[i] = accumulator[i + si]; + } + + for (i = sz - si; i < sz; ++i) { + accumulator[i] = 0.0f; + } + + for (i = 0; i < sz - si; ++i) { + windowAccumulator[i] = windowAccumulator[i + si]; + } + + for (i = sz - si; i < sz; ++i) { + windowAccumulator[i] = 0.0f; + } + + if (int(cd.accumulatorFill) > si) { + cd.accumulatorFill -= si; + } else { + cd.accumulatorFill = 0; + if (cd.draining) { + if (m_debugLevel > 1) { + cerr << "RubberBandStretcher::Impl::processChunks: setting outputComplete to true" << endl; + } + cd.outputComplete = true; + } + } +} + +void +RubberBandStretcher::Impl::writeOutput(RingBuffer<float> &to, float *from, size_t qty, size_t &outCount, size_t theoreticalOut) +{ + Profiler profiler("RubberBandStretcher::Impl::writeOutput"); + + // In non-RT mode, we don't want to write the first startSkip + // samples, because the first chunk is centred on the start of the + // output. In RT mode we didn't apply any pre-padding in + // configure(), so we don't want to remove any here. + + size_t startSkip = 0; + if (!m_realtime) { + startSkip = lrintf((m_windowSize/2) / m_pitchScale); + } + + if (outCount > startSkip) { + + // this is the normal case + + if (theoreticalOut > 0) { + if (m_debugLevel > 1) { + cerr << "theoreticalOut = " << theoreticalOut + << ", outCount = " << outCount + << ", startSkip = " << startSkip + << ", qty = " << qty << endl; + } + if (outCount - startSkip <= theoreticalOut && + outCount - startSkip + qty > theoreticalOut) { + qty = theoreticalOut - (outCount - startSkip); + if (m_debugLevel > 1) { + cerr << "reduce qty to " << qty << endl; + } + } + } + + if (m_debugLevel > 2) { + cerr << "writing " << qty << endl; + } + + size_t written = to.write(from, qty); + + if (written < qty) { + cerr << "WARNING: RubberBandStretcher::Impl::writeOutput: " + << "Buffer overrun on output: wrote " << written + << " of " << qty << " samples" << endl; + } + + outCount += written; + return; + } + + // the rest of this is only used during the first startSkip samples + + if (outCount + qty <= startSkip) { + if (m_debugLevel > 1) { + cerr << "qty = " << qty << ", startSkip = " + << startSkip << ", outCount = " << outCount + << ", discarding" << endl; + } + outCount += qty; + return; + } + + size_t off = startSkip - outCount; + if (m_debugLevel > 1) { + cerr << "qty = " << qty << ", startSkip = " + << startSkip << ", outCount = " << outCount + << ", writing " << qty - off + << " from start offset " << off << endl; + } + to.write(from + off, qty - off); + outCount += qty; +} + +int +RubberBandStretcher::Impl::available() const +{ + Profiler profiler("RubberBandStretcher::Impl::available"); + + if (m_threaded) { + MutexLocker locker(&m_threadSetMutex); + if (m_channelData.empty()) return 0; + } else { + if (m_channelData.empty()) return 0; + } + + if (!m_threaded) { + for (size_t c = 0; c < m_channels; ++c) { + if (m_channelData[c]->inputSize >= 0) { +// cerr << "available: m_done true" << endl; + if (m_channelData[c]->inbuf->getReadSpace() > 0) { +// cerr << "calling processChunks(" << c << ") from available" << endl; + //!!! do we ever actually do this? if so, this method should not be const + // ^^^ yes, we do sometimes -- e.g. when fed a very short file + bool any = false, last = false; + ((RubberBandStretcher::Impl *)this)->processChunks(c, any, last); + } + } + } + } + + size_t min = 0; + bool consumed = true; + bool haveResamplers = false; + + for (size_t i = 0; i < m_channels; ++i) { + size_t availIn = m_channelData[i]->inbuf->getReadSpace(); + size_t availOut = m_channelData[i]->outbuf->getReadSpace(); + if (m_debugLevel > 2) { + cerr << "available on channel " << i << ": " << availOut << " (waiting: " << availIn << ")" << endl; + } + if (i == 0 || availOut < min) min = availOut; + if (!m_channelData[i]->outputComplete) consumed = false; + if (m_channelData[i]->resampler) haveResamplers = true; + } + + if (min == 0 && consumed) return -1; + if (m_pitchScale == 1.0) return min; + + if (haveResamplers) return min; // resampling has already happened + return int(floor(min / m_pitchScale)); +} + +size_t +RubberBandStretcher::Impl::retrieve(float *const *output, size_t samples) const +{ + Profiler profiler("RubberBandStretcher::Impl::retrieve"); + + size_t got = samples; + + for (size_t c = 0; c < m_channels; ++c) { + size_t gotHere = m_channelData[c]->outbuf->read(output[c], got); + if (gotHere < got) { + if (c > 0) { + if (m_debugLevel > 0) { + cerr << "RubberBandStretcher::Impl::retrieve: WARNING: channel imbalance detected" << endl; + } + } + got = gotHere; + } + } + + return got; +} + +} + diff --git a/libs/rubberband/src/Thread.cpp b/libs/rubberband/src/Thread.cpp new file mode 100644 index 0000000000..679115eb52 --- /dev/null +++ b/libs/rubberband/src/Thread.cpp @@ -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(); + } +} + +} + diff --git a/libs/rubberband/src/Thread.h b/libs/rubberband/src/Thread.h new file mode 100644 index 0000000000..061469297e --- /dev/null +++ b/libs/rubberband/src/Thread.h @@ -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 diff --git a/libs/rubberband/src/Window.cpp b/libs/rubberband/src/Window.cpp new file mode 100644 index 0000000000..106faa7b62 --- /dev/null +++ b/libs/rubberband/src/Window.cpp @@ -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" + + diff --git a/libs/rubberband/src/Window.h b/libs/rubberband/src/Window.h new file mode 100644 index 0000000000..6916b6fb5f --- /dev/null +++ b/libs/rubberband/src/Window.h @@ -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 diff --git a/libs/rubberband/src/bsd-3rdparty/float_cast/float_cast.h b/libs/rubberband/src/bsd-3rdparty/float_cast/float_cast.h new file mode 100644 index 0000000000..1ba0e03bdc --- /dev/null +++ b/libs/rubberband/src/bsd-3rdparty/float_cast/float_cast.h @@ -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 + + + diff --git a/libs/rubberband/src/bsd-3rdparty/getopt/getopt.c b/libs/rubberband/src/bsd-3rdparty/getopt/getopt.c new file mode 100644 index 0000000000..ce9abb3cce --- /dev/null +++ b/libs/rubberband/src/bsd-3rdparty/getopt/getopt.c @@ -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 */ +} diff --git a/libs/rubberband/src/bsd-3rdparty/getopt/getopt.h b/libs/rubberband/src/bsd-3rdparty/getopt/getopt.h new file mode 100644 index 0000000000..d95d6cf8f8 --- /dev/null +++ b/libs/rubberband/src/bsd-3rdparty/getopt/getopt.h @@ -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_ */ diff --git a/libs/rubberband/src/bsd-3rdparty/getopt/getopt_long.c b/libs/rubberband/src/bsd-3rdparty/getopt/getopt_long.c new file mode 100644 index 0000000000..1f92449a06 --- /dev/null +++ b/libs/rubberband/src/bsd-3rdparty/getopt/getopt_long.c @@ -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 */ diff --git a/libs/rubberband/src/bsd-3rdparty/getopt/unistd.h b/libs/rubberband/src/bsd-3rdparty/getopt/unistd.h new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/libs/rubberband/src/bsd-3rdparty/getopt/unistd.h diff --git a/libs/rubberband/src/ladspa/RubberBandPitchShifter.cpp b/libs/rubberband/src/ladspa/RubberBandPitchShifter.cpp new file mode 100644 index 0000000000..6839124921 --- /dev/null +++ b/libs/rubberband/src/ladspa/RubberBandPitchShifter.cpp @@ -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; +} + diff --git a/libs/rubberband/src/ladspa/RubberBandPitchShifter.h b/libs/rubberband/src/ladspa/RubberBandPitchShifter.h new file mode 100644 index 0000000000..f2f351bff6 --- /dev/null +++ b/libs/rubberband/src/ladspa/RubberBandPitchShifter.h @@ -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 diff --git a/libs/rubberband/src/ladspa/ladspa-rubberband.cat b/libs/rubberband/src/ladspa/ladspa-rubberband.cat new file mode 100644 index 0000000000..438e9a3909 --- /dev/null +++ b/libs/rubberband/src/ladspa/ladspa-rubberband.cat @@ -0,0 +1,2 @@ +ladspa:ladspa-rubberband:rubberband-pitchshifter-mono::Frequency > Pitch shifters +ladspa:ladspa-rubberband:rubberband-pitchshifter-stereo::Frequency > Pitch shifters diff --git a/libs/rubberband/src/ladspa/libmain.cpp b/libs/rubberband/src/ladspa/libmain.cpp new file mode 100644 index 0000000000..d949e81898 --- /dev/null +++ b/libs/rubberband/src/ladspa/libmain.cpp @@ -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); +} + +} diff --git a/libs/rubberband/src/main.cpp b/libs/rubberband/src/main.cpp new file mode 100644 index 0000000000..370ced2c7d --- /dev/null +++ b/libs/rubberband/src/main.cpp @@ -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; +} + + diff --git a/libs/rubberband/src/main.cpp.mine b/libs/rubberband/src/main.cpp.mine new file mode 100644 index 0000000000..569d07f4e6 --- /dev/null +++ b/libs/rubberband/src/main.cpp.mine @@ -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; +} + + diff --git a/libs/rubberband/src/main.cpp.r3257 b/libs/rubberband/src/main.cpp.r3257 new file mode 100644 index 0000000000..c4f9259ae6 --- /dev/null +++ b/libs/rubberband/src/main.cpp.r3257 @@ -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; +} + + diff --git a/libs/rubberband/src/main.cpp.r3502 b/libs/rubberband/src/main.cpp.r3502 new file mode 100644 index 0000000000..2f8b386d51 --- /dev/null +++ b/libs/rubberband/src/main.cpp.r3502 @@ -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; +} + + diff --git a/libs/rubberband/src/rubberband-c.cpp b/libs/rubberband/src/rubberband-c.cpp new file mode 100644 index 0000000000..7bdd701ddf --- /dev/null +++ b/libs/rubberband/src/rubberband-c.cpp @@ -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); +} + diff --git a/libs/rubberband/src/sysutils.cpp b/libs/rubberband/src/sysutils.cpp new file mode 100644 index 0000000000..d52eee9511 --- /dev/null +++ b/libs/rubberband/src/sysutils.cpp @@ -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); +} + + +} + + + diff --git a/libs/rubberband/src/sysutils.h b/libs/rubberband/src/sysutils.h new file mode 100644 index 0000000000..a529afde0d --- /dev/null +++ b/libs/rubberband/src/sysutils.h @@ -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 diff --git a/libs/rubberband/src/vamp/RubberBandVampPlugin.cpp b/libs/rubberband/src/vamp/RubberBandVampPlugin.cpp new file mode 100644 index 0000000000..feb5bfa6bb --- /dev/null +++ b/libs/rubberband/src/vamp/RubberBandVampPlugin.cpp @@ -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; +} + diff --git a/libs/rubberband/src/vamp/RubberBandVampPlugin.h b/libs/rubberband/src/vamp/RubberBandVampPlugin.h new file mode 100644 index 0000000000..f062e35eea --- /dev/null +++ b/libs/rubberband/src/vamp/RubberBandVampPlugin.h @@ -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 diff --git a/libs/rubberband/src/vamp/libmain.cpp b/libs/rubberband/src/vamp/libmain.cpp new file mode 100644 index 0000000000..1b4185130d --- /dev/null +++ b/libs/rubberband/src/vamp/libmain.cpp @@ -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; + } +} + diff --git a/libs/rubberband/src/vamp/vamp-rubberband.cat b/libs/rubberband/src/vamp/vamp-rubberband.cat new file mode 100644 index 0000000000..d1ef2caba8 --- /dev/null +++ b/libs/rubberband/src/vamp/vamp-rubberband.cat @@ -0,0 +1 @@ +vamp:vamp-rubberband:rubberband::Time > Timestretch Analysis |