From 1d41c952e80a23802ffd9d0bccdde2cbf5f35ddc Mon Sep 17 00:00:00 2001 From: Chris Cannam Date: Thu, 22 May 2008 16:54:27 +0000 Subject: [PATCH] Update from personal repository. * Added an initial "formant preservation" option when pitch shifting * Real-time pitch shifting now uses a faster method by default, with less variation in CPU usage * The code is more amenable to compiler auto-vectorization (through e.g. gcc --ftree-vectorize). --- CHANGELOG | 9 + Makefile.in | 134 ++-- README | 2 +- configure | 22 +- configure.ac | 6 +- rubberband/RubberBandStretcher.h | 115 +++- rubberband/TimeStretcher.h | 2 +- src/AudioCurve.cpp | 15 +- src/AudioCurve.h | 7 +- src/ConstantAudioCurve.cpp | 4 +- src/ConstantAudioCurve.h | 4 +- src/FFT.cpp | 786 ++++++++++++++++++----- src/FFT.h | 28 +- src/HighFrequencyAudioCurve.cpp | 10 +- src/HighFrequencyAudioCurve.h | 4 +- src/PercussiveAudioCurve.cpp | 60 +- src/PercussiveAudioCurve.h | 7 +- src/Profiler.cpp | 176 +++++ src/Profiler.h | 91 +++ src/Resampler.cpp | 148 ++++- src/Resampler.h | 20 +- src/RingBuffer.h | 219 ++++--- src/RubberBandStretcher.cpp | 14 +- src/Scavenger.h | 7 +- src/SpectralDifferenceAudioCurve.cpp | 11 +- src/SpectralDifferenceAudioCurve.h | 6 +- src/StretchCalculator.cpp | 45 +- src/StretchCalculator.h | 9 +- src/StretcherChannelData.cpp | 76 ++- src/StretcherChannelData.h | 19 +- src/StretcherImpl.cpp | 174 +++-- src/StretcherImpl.h | 10 +- src/StretcherProcess.cpp | 435 ++++++++++--- src/Thread.cpp | 107 ++- src/Thread.h | 21 +- src/Window.cpp | 17 + src/Window.h | 34 +- src/bsd-3rdparty/float_cast/float_cast.h | 73 +++ src/bsd-3rdparty/getopt/getopt.c | 122 ++++ src/bsd-3rdparty/getopt/getopt.h | 110 ++++ src/bsd-3rdparty/getopt/getopt_long.c | 547 ++++++++++++++++ src/bsd-3rdparty/getopt/unistd.h | 0 src/ladspa/RubberBandPitchShifter.cpp | 18 +- src/ladspa/RubberBandPitchShifter.h | 12 +- src/ladspa/libmain.cpp | 2 +- src/main.cpp | 39 +- src/sysutils.cpp | 5 +- src/sysutils.h | 19 +- src/vamp/RubberBandVampPlugin.cpp | 28 +- src/vamp/RubberBandVampPlugin.h | 2 +- src/vamp/libmain.cpp | 2 +- 51 files changed, 3157 insertions(+), 676 deletions(-) create mode 100644 CHANGELOG create mode 100644 src/Profiler.cpp create mode 100644 src/Profiler.h create mode 100644 src/Window.cpp create mode 100644 src/bsd-3rdparty/float_cast/float_cast.h create mode 100644 src/bsd-3rdparty/getopt/getopt.c create mode 100644 src/bsd-3rdparty/getopt/getopt.h create mode 100644 src/bsd-3rdparty/getopt/getopt_long.c create mode 100644 src/bsd-3rdparty/getopt/unistd.h diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000..426fff5 --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,9 @@ + +Changes since Rubber Band v1.0.1 + + * Added an initial "formant preservation" option when pitch shifting + * Real-time pitch shifting now uses a faster method by default, with + less variation in CPU usage + * The code is more amenable to compiler auto-vectorization (through + e.g. gcc --ftree-vectorize). + diff --git a/Makefile.in b/Makefile.in index eef0d93..0b1a5f2 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1,6 +1,7 @@ CXX := @CXX@ -CXXFLAGS := -DFFTW_DOUBLE_ONLY @CXXFLAGS@ @SRC_CFLAGS@ @SNDFILE_CFLAGS@ @FFTW_CFLAGS@ @Vamp_CFLAGS@ -Irubberband -Isrc $(OPTFLAGS) +CXXFLAGS := -DHAVE_FFTW3 -DFFTW_DOUBLE_ONLY -DNO_THREAD_CHECKS @CXXFLAGS@ @SRC_CFLAGS@ @SNDFILE_CFLAGS@ @FFTW_CFLAGS@ @Vamp_CFLAGS@ -Irubberband -Isrc $(OPTFLAGS) +CFLAGS := @CFLAGS@ $(OPTFLAGS) LDFLAGS := @LDFLAGS@ -lpthread $(LDFLAGS) LIBRARY_LIBS := @SRC_LIBS@ @FFTW_LIBS@ @@ -29,6 +30,13 @@ INSTALL_PKGDIR := @prefix@/lib/pkgconfig all: bin lib $(PROGRAM_TARGET) $(STATIC_TARGET) $(DYNAMIC_TARGET) $(VAMP_TARGET) $(LADSPA_TARGET) +static: $(STATIC_TARGET) +dynamic: $(DYNAMIC_TARGET) +library: $(STATIC_TARGET) $(DYNAMIC_TARGET) +program: $(PROGRAM_TARGET) +vamp: $(VAMP_TARGET) +ladspa: $(LADSPA_TARGET) + PUBLIC_INCLUDES := \ rubberband/TimeStretcher.h \ rubberband/RubberBandStretcher.h @@ -39,6 +47,7 @@ LIBRARY_INCLUDES := \ src/FFT.h \ src/HighFrequencyAudioCurve.h \ src/PercussiveAudioCurve.h \ + src/Profiler.h \ src/Resampler.h \ src/RingBuffer.h \ src/Scavenger.h \ @@ -51,19 +60,21 @@ LIBRARY_INCLUDES := \ src/sysutils.h LIBRARY_SOURCES := \ - src/RubberBandStretcher.cpp \ + src/AudioCurve.cpp \ src/ConstantAudioCurve.cpp \ + src/FFT.cpp \ src/HighFrequencyAudioCurve.cpp \ src/PercussiveAudioCurve.cpp \ - src/AudioCurve.cpp \ + src/Profiler.cpp \ src/Resampler.cpp \ + src/RubberBandStretcher.cpp \ src/SpectralDifferenceAudioCurve.cpp \ src/StretchCalculator.cpp \ src/StretcherImpl.cpp \ src/StretcherProcess.cpp \ src/StretcherChannelData.cpp \ - src/FFT.cpp \ src/Thread.cpp \ + src/Window.cpp \ src/sysutils.cpp PROGRAM_SOURCES := \ @@ -84,6 +95,8 @@ LADSPA_SOURCES := \ src/ladspa/libmain.cpp LIBRARY_OBJECTS := $(LIBRARY_SOURCES:.cpp=.o) +LIBRARY_OBJECTS := $(LIBRARY_OBJECTS:.c=.o) + PROGRAM_OBJECTS := $(PROGRAM_SOURCES:.cpp=.o) VAMP_OBJECTS := $(VAMP_SOURCES:.cpp=.o) LADSPA_OBJECTS := $(LADSPA_SOURCES:.cpp=.o) @@ -134,57 +147,94 @@ distclean: clean # DO NOT DELETE -src/AudioCurve.o: src/AudioCurve.h +src/AudioCurve.o: src/AudioCurve.h src/sysutils.h src/ConstantAudioCurve.o: src/ConstantAudioCurve.h src/AudioCurve.h -src/FFT.o: src/FFT.h src/Thread.h +src/ConstantAudioCurve.o: src/sysutils.h +src/FFT.o: src/FFT.h src/sysutils.h src/Thread.h src/Profiler.h src/HighFrequencyAudioCurve.o: src/HighFrequencyAudioCurve.h src/AudioCurve.h -src/HighFrequencyAudioCurve.o: src/Window.h -src/main.o: src/sysutils.h +src/HighFrequencyAudioCurve.o: src/sysutils.h src/Window.h +src/main.o: rubberband/RubberBandStretcher.h rubberband/TimeStretcher.h +src/main.o: src/sysutils.h src/Profiler.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/PercussiveAudioCurve.o: src/sysutils.h +src/Profiler.o: src/Profiler.h src/sysutils.h +src/Resampler.o: src/Resampler.h src/sysutils.h src/Profiler.h +src/RingBuffer.o: src/RingBuffer.h src/Scavenger.h src/Thread.h +src/RingBuffer.o: src/sysutils.h src/Profiler.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/sysutils.h src/Thread.h src/RingBuffer.h +src/RubberBandStretcher.o: src/Scavenger.h src/Profiler.h 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/SpectralDifferenceAudioCurve.o: src/AudioCurve.h src/sysutils.h +src/SpectralDifferenceAudioCurve.o: src/Window.h +src/StretchCalculator.o: src/StretchCalculator.h src/sysutils.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: rubberband/RubberBandStretcher.h +src/StretcherChannelData.o: rubberband/TimeStretcher.h src/Window.h +src/StretcherChannelData.o: src/sysutils.h src/Thread.h src/RingBuffer.h +src/StretcherChannelData.o: src/Scavenger.h src/Profiler.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/StretcherImpl.h rubberband/RubberBandStretcher.h +src/StretcherImpl.o: rubberband/TimeStretcher.h src/Window.h src/sysutils.h +src/StretcherImpl.o: src/Thread.h src/RingBuffer.h src/Scavenger.h +src/StretcherImpl.o: src/Profiler.h src/FFT.h src/PercussiveAudioCurve.h +src/StretcherImpl.o: src/AudioCurve.h 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/StretcherImpl.h rubberband/RubberBandStretcher.h +src/StretcherProcess.o: rubberband/TimeStretcher.h src/Window.h +src/StretcherProcess.o: src/sysutils.h src/Thread.h src/RingBuffer.h +src/StretcherProcess.o: src/Scavenger.h src/Profiler.h src/FFT.h +src/StretcherProcess.o: 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/Window.o: src/Window.h src/sysutils.h +rubberband/RubberBandStretcher.o: rubberband/TimeStretcher.h +src/AudioCurve.o: src/sysutils.h +src/ConstantAudioCurve.o: src/AudioCurve.h src/sysutils.h +src/FFT.o: src/sysutils.h +src/HighFrequencyAudioCurve.o: src/AudioCurve.h src/sysutils.h src/Window.h +src/PercussiveAudioCurve.o: src/AudioCurve.h src/sysutils.h +src/Profiler.o: src/sysutils.h +src/Resampler.o: src/sysutils.h +src/RingBuffer.o: src/Scavenger.h src/Thread.h src/sysutils.h src/Profiler.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/SpectralDifferenceAudioCurve.o: src/AudioCurve.h src/sysutils.h +src/SpectralDifferenceAudioCurve.o: src/Window.h +src/StretcherChannelData.o: src/StretcherImpl.h +src/StretcherChannelData.o: rubberband/RubberBandStretcher.h +src/StretcherChannelData.o: rubberband/TimeStretcher.h src/Window.h +src/StretcherChannelData.o: src/sysutils.h src/Thread.h src/RingBuffer.h +src/StretcherChannelData.o: src/Scavenger.h src/Profiler.h src/FFT.h +src/StretcherImpl.o: rubberband/RubberBandStretcher.h +src/StretcherImpl.o: rubberband/TimeStretcher.h src/Window.h src/sysutils.h +src/StretcherImpl.o: src/Thread.h src/RingBuffer.h src/Scavenger.h +src/StretcherImpl.o: src/Profiler.h src/FFT.h +src/Window.o: src/sysutils.h +src/ladspa/libmain.o: src/ladspa/RubberBandPitchShifter.h +src/ladspa/libmain.o: src/RingBuffer.h src/Scavenger.h src/Thread.h +src/ladspa/libmain.o: src/sysutils.h src/Profiler.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 +src/ladspa/RubberBandPitchShifter.o: src/RingBuffer.h +src/ladspa/RubberBandPitchShifter.o: src/Scavenger.h src/Thread.h +src/ladspa/RubberBandPitchShifter.o: src/sysutils.h src/Profiler.h +src/ladspa/RubberBandPitchShifter.o: rubberband/RubberBandStretcher.h +src/ladspa/RubberBandPitchShifter.o: rubberband/TimeStretcher.h +src/vamp/libmain.o: src/vamp/RubberBandVampPlugin.h +src/vamp/libmain.o: rubberband/RubberBandStretcher.h +src/vamp/libmain.o: rubberband/TimeStretcher.h +src/vamp/RubberBandVampPlugin.o: src/vamp/RubberBandVampPlugin.h +src/vamp/RubberBandVampPlugin.o: rubberband/RubberBandStretcher.h +src/vamp/RubberBandVampPlugin.o: rubberband/TimeStretcher.h +src/vamp/RubberBandVampPlugin.o: src/StretchCalculator.h +src/ladspa/RubberBandPitchShifter.o: src/RingBuffer.h +src/ladspa/RubberBandPitchShifter.o: src/Scavenger.h src/Thread.h +src/ladspa/RubberBandPitchShifter.o: src/sysutils.h src/Profiler.h +src/vamp/RubberBandVampPlugin.o: rubberband/RubberBandStretcher.h +src/vamp/RubberBandVampPlugin.o: rubberband/TimeStretcher.h diff --git a/README b/README index 178234e..6002cc5 100644 --- a/README +++ b/README @@ -4,7 +4,7 @@ Rubber Band An audio time-stretching and pitch-shifting library and utility program. -Copyright 2007 Chris Cannam, cannam@all-day-breakfast.com. +Copyright 2008 Chris Cannam, cannam@all-day-breakfast.com. Distributed under the GNU General Public License. diff --git a/configure b/configure index 3ad36a8..0a93d9f 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.61 for RubberBand 0.1. +# Generated by GNU Autoconf 2.61 for RubberBand 1.1. # # Report bugs to . # @@ -574,8 +574,8 @@ SHELL=${CONFIG_SHELL-/bin/sh} # Identity of this package. PACKAGE_NAME='RubberBand' PACKAGE_TARNAME='rubberband' -PACKAGE_VERSION='0.1' -PACKAGE_STRING='RubberBand 0.1' +PACKAGE_VERSION='1.1' +PACKAGE_STRING='RubberBand 1.1' PACKAGE_BUGREPORT='cannam@all-day-breakfast.com' ac_unique_file="src/StretcherImpl.h" @@ -1200,7 +1200,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures RubberBand 0.1 to adapt to many kinds of systems. +\`configure' configures RubberBand 1.1 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1261,7 +1261,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of RubberBand 0.1:";; + short | recursive ) echo "Configuration of RubberBand 1.1:";; esac cat <<\_ACEOF @@ -1352,7 +1352,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -RubberBand configure 0.1 +RubberBand configure 1.1 generated by GNU Autoconf 2.61 Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, @@ -1366,7 +1366,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by RubberBand $as_me 0.1, which was +It was created by RubberBand $as_me 1.1, which was generated by GNU Autoconf 2.61. Invocation command line was $ $0 $@ @@ -4746,6 +4746,10 @@ fi if test "x$GCC" = "xyes"; then + case " $CFLAGS " in + *[\ \ ]-fPIC\ -Wall[\ \ ]*) ;; + *) CFLAGS="$CFLAGS -fPIC -Wall" ;; + esac case " $CXXFLAGS " in *[\ \ ]-fPIC\ -Wall[\ \ ]*) ;; *) CXXFLAGS="$CXXFLAGS -fPIC -Wall" ;; @@ -5180,7 +5184,7 @@ exec 6>&1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by RubberBand $as_me 0.1, which was +This file was extended by RubberBand $as_me 1.1, which was generated by GNU Autoconf 2.61. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -5223,7 +5227,7 @@ Report bugs to ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF ac_cs_version="\\ -RubberBand config.status 0.1 +RubberBand config.status 1.1 configured by $0, generated by GNU Autoconf 2.61, with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" diff --git a/configure.ac b/configure.ac index 83da151..87bb0df 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ -AC_INIT(RubberBand, 0.1, cannam@all-day-breakfast.com) +AC_INIT(RubberBand, 1.1, cannam@all-day-breakfast.com) AC_CONFIG_SRCDIR(src/StretcherImpl.h) AC_PROG_CXX @@ -27,6 +27,10 @@ AC_SUBST(Vamp_LIBS) changequote(,)dnl if test "x$GCC" = "xyes"; then + case " $CFLAGS " in + *[\ \ ]-fPIC\ -Wall[\ \ ]*) ;; + *) CFLAGS="$CFLAGS -fPIC -Wall" ;; + esac case " $CXXFLAGS " in *[\ \ ]-fPIC\ -Wall[\ \ ]*) ;; *) CXXFLAGS="$CXXFLAGS -fPIC -Wall" ;; diff --git a/rubberband/RubberBandStretcher.h b/rubberband/RubberBandStretcher.h index e9dcb2f..9b34714 100644 --- a/rubberband/RubberBandStretcher.h +++ b/rubberband/RubberBandStretcher.h @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + 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 @@ -19,6 +19,13 @@ #include +/** + * @mainpage RubberBand + * + * The Rubber Band API is contained in the single class + * RubberBand::RubberBandStretcher. + */ + namespace RubberBand { @@ -151,34 +158,72 @@ public: * \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. */ + + enum Option { + + OptionProcessOffline = 0x00000000, + OptionProcessRealTime = 0x00000001, + + OptionStretchElastic = 0x00000000, + OptionStretchPrecise = 0x00000010, + + OptionTransientsCrisp = 0x00000000, + OptionTransientsMixed = 0x00000100, + OptionTransientsSmooth = 0x00000200, + + OptionPhaseAdaptive = 0x00000000, + OptionPhasePeakLocked = 0x00001000, + OptionPhaseIndependent = 0x00002000, + + OptionThreadingAuto = 0x00000000, + OptionThreadingNever = 0x00010000, + OptionThreadingAlways = 0x00020000, + + OptionWindowStandard = 0x00000000, + OptionWindowShort = 0x00100000, + OptionWindowLong = 0x00200000, + + OptionFormantShifted = 0x00000000, + OptionFormantPreserved = 0x01000000, + + OptionPitchHighSpeed = 0x00000000, + OptionPitchHighQuality = 0x02000000 + }; + typedef int Options; - - static const int OptionProcessOffline = 0x00000000; - static const int OptionProcessRealTime = 0x00000001; - static const int OptionStretchElastic = 0x00000000; - static const int OptionStretchPrecise = 0x00000010; - - static const int OptionTransientsCrisp = 0x00000000; - static const int OptionTransientsMixed = 0x00000100; - static const int OptionTransientsSmooth = 0x00000200; - - static const int OptionPhaseAdaptive = 0x00000000; - static const int OptionPhasePeakLocked = 0x00001000; - static const int OptionPhaseIndependent = 0x00002000; - - static const int OptionThreadingAuto = 0x00000000; - static const int OptionThreadingNever = 0x00010000; - static const int OptionThreadingAlways = 0x00020000; - - static const int OptionWindowStandard = 0x00000000; - static const int OptionWindowShort = 0x00100000; - static const int OptionWindowLong = 0x00200000; - - static const int DefaultOptions = 0x00000000; - static const int PercussiveOptions = OptionWindowShort | \ - OptionPhaseIndependent; + enum PresetOption { + DefaultOptions = 0x00000000, + PercussiveOptions = 0x00102000 + }; /** * Construct a time and pitch stretcher object to run at the given @@ -293,6 +338,24 @@ public: */ virtual 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. + */ + virtual 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). + */ + virtual 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 diff --git a/rubberband/TimeStretcher.h b/rubberband/TimeStretcher.h index bad916a..99d8810 100644 --- a/rubberband/TimeStretcher.h +++ b/rubberband/TimeStretcher.h @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + 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 diff --git a/src/AudioCurve.cpp b/src/AudioCurve.cpp index c18d134..e7bb1ec 100644 --- a/src/AudioCurve.cpp +++ b/src/AudioCurve.cpp @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + 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 @@ -14,6 +14,9 @@ #include "AudioCurve.h" +#include +using namespace std; + namespace RubberBand { @@ -27,5 +30,15 @@ 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 < m_windowSize; ++i) tmp[i] = float(mag[i]); + float df = process(tmp, increment); + delete[] tmp; + return df; +} } diff --git a/src/AudioCurve.h b/src/AudioCurve.h index e7a57c5..7896308 100644 --- a/src/AudioCurve.h +++ b/src/AudioCurve.h @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + 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 @@ -17,6 +17,8 @@ #include +#include "sysutils.h" + namespace RubberBand { @@ -28,7 +30,8 @@ public: virtual void setWindowSize(size_t newSize) = 0; - virtual float process(float *mag, size_t increment) = 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: diff --git a/src/ConstantAudioCurve.cpp b/src/ConstantAudioCurve.cpp index 85c2c67..3263c53 100644 --- a/src/ConstantAudioCurve.cpp +++ b/src/ConstantAudioCurve.cpp @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + 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 @@ -38,7 +38,7 @@ ConstantAudioCurve::setWindowSize(size_t newSize) } float -ConstantAudioCurve::process(float *, size_t) +ConstantAudioCurve::process(const float *R__, size_t) { return 1.f; } diff --git a/src/ConstantAudioCurve.h b/src/ConstantAudioCurve.h index 87a4f75..d73cabe 100644 --- a/src/ConstantAudioCurve.h +++ b/src/ConstantAudioCurve.h @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + 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 @@ -28,7 +28,7 @@ public: virtual void setWindowSize(size_t newSize); - virtual float process(float *mag, size_t increment); + virtual float process(const float *R__ mag, size_t increment); virtual void reset(); }; diff --git a/src/FFT.cpp b/src/FFT.cpp index f1040bd..49c1a71 100644 --- a/src/FFT.cpp +++ b/src/FFT.cpp @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + 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 @@ -14,14 +14,32 @@ #include "FFT.h" #include "Thread.h" +#include "Profiler.h" + +//#define FFT_MEASUREMENT 1 +#ifdef HAVE_FFTW3 #include +#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 #include #include #include +#include #include namespace RubberBand { @@ -34,26 +52,30 @@ public: 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(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(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 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(double *realIn, double *imagIn, double *realOut) = 0; - virtual void inversePolar(double *magIn, double *phaseIn, double *realOut) = 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(float *realIn, float *imagIn, float *realOut) = 0; - virtual void inversePolar(float *magIn, float *phaseIn, float *realOut) = 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 @@ -79,6 +101,7 @@ public: #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 @@ -91,9 +114,12 @@ public: #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 @@ -105,13 +131,15 @@ public: #define atan2 atan2f #define sqrt sqrtf #define cos cosf -#define sif sinf +#define sin sinf +#else +#define fft_double_type double #endif /* FFTW_FLOAT_ONLY */ class D_FFTW : public FFTImpl { public: - D_FFTW(unsigned int size) : m_fplanf(0) + D_FFTW(int size) : m_fplanf(0) #ifdef FFTW_DOUBLE_ONLY , m_frb(0) #endif @@ -129,7 +157,9 @@ public: 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); @@ -143,7 +173,9 @@ public: 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); @@ -162,11 +194,10 @@ public: 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_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 @@ -183,11 +214,10 @@ public: 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_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 @@ -250,175 +280,279 @@ public: 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 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 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) { + 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]; - im[i] = m_fpacked[i][1]; + } + if (im) { + for (int i = 0; i <= hs; ++i) { + im[i] = m_fpacked[i][1]; + } } } - void unpackDouble(double *re, double *im) { - for (unsigned int i = 0; i <= m_size/2; ++i) { + 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]; - im[i] = m_dpacked[i][1]; + } + if (im) { + for (int i = 0; i <= hs; ++i) { + im[i] = m_dpacked[i][1]; + } } } - void forward(double *realIn, double *realOut, double *imagOut) { + 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 != m_dbuf) + if (realIn != dbuf) #endif - for (unsigned int i = 0; i < m_size; ++i) { - m_dbuf[i] = realIn[i]; + for (int i = 0; i < sz; ++i) { + dbuf[i] = realIn[i]; } fftw_execute(m_dplanf); unpackDouble(realOut, imagOut); } - void forwardPolar(double *realIn, double *magOut, double *phaseOut) { + 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 != m_dbuf) + if (realIn != dbuf) #endif - for (unsigned int i = 0; i < m_size; ++i) { - m_dbuf[i] = realIn[i]; + for (int i = 0; i < sz; ++i) { + dbuf[i] = realIn[i]; } fftw_execute(m_dplanf); - for (unsigned int i = 0; i <= m_size/2; ++i) { + 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 (unsigned int i = 0; i <= m_size/2; ++i) { + for (int i = 0; i <= hs; ++i) { phaseOut[i] = atan2(m_dpacked[i][1], m_dpacked[i][0]); } } - void forwardMagnitude(double *realIn, double *magOut) { + 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 (unsigned int i = 0; i < m_size; ++i) { - m_dbuf[i] = realIn[i]; + for (int i = 0; i < sz; ++i) { + dbuf[i] = realIn[i]; } fftw_execute(m_dplanf); - for (unsigned int i = 0; i <= m_size/2; ++i) { + 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(float *realIn, float *realOut, float *imagOut) { + 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 != m_fbuf) + if (realIn != fbuf) #endif - for (unsigned int i = 0; i < m_size; ++i) { - m_fbuf[i] = realIn[i]; + for (int i = 0; i < sz; ++i) { + fbuf[i] = realIn[i]; } fftwf_execute(m_fplanf); unpackFloat(realOut, imagOut); } - void forwardPolar(float *realIn, float *magOut, float *phaseOut) { + 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 != m_fbuf) + if (realIn != fbuf) #endif - for (unsigned int i = 0; i < m_size; ++i) { - m_fbuf[i] = realIn[i]; + for (int i = 0; i < sz; ++i) { + fbuf[i] = realIn[i]; } fftwf_execute(m_fplanf); - for (unsigned int i = 0; i <= m_size/2; ++i) { + 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 (unsigned int i = 0; i <= m_size/2; ++i) { - phaseOut[i] = atan2f(m_fpacked[i][1], m_fpacked[i][0]) ; + for (int i = 0; i <= hs; ++i) { + phaseOut[i] = atan2f(m_fpacked[i][1], m_fpacked[i][0]) ; } } - void forwardMagnitude(float *realIn, float *magOut) { + 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 != m_fbuf) + if (realIn != fbuf) #endif - for (unsigned int i = 0; i < m_size; ++i) { - m_fbuf[i] = realIn[i]; + for (int i = 0; i < sz; ++i) { + fbuf[i] = realIn[i]; } fftwf_execute(m_fplanf); - for (unsigned int i = 0; i <= m_size/2; ++i) { + 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(double *realIn, double *imagIn, double *realOut) { + 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 != m_dbuf) + if (realOut != dbuf) #endif - for (unsigned int i = 0; i < m_size; ++i) { - realOut[i] = m_dbuf[i]; + for (int i = 0; i < sz; ++i) { + realOut[i] = dbuf[i]; } } - void inversePolar(double *magIn, double *phaseIn, double *realOut) { + void inversePolar(const double *R__ magIn, const double *R__ phaseIn, double *R__ 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]); + 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 != m_dbuf) + if (realOut != dbuf) #endif - for (unsigned int i = 0; i < m_size; ++i) { - realOut[i] = m_dbuf[i]; + for (int i = 0; i < sz; ++i) { + realOut[i] = dbuf[i]; } } - void inverse(float *realIn, float *imagIn, float *realOut) { + void inverseCepstral(const double *R__ magIn, double *R__ cepOut) { + if (!m_dplanf) initDouble(); + fft_double_type *const R__ dbuf = m_dbuf; + fftwf_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 != m_fbuf) + if (realOut != fbuf) #endif - for (unsigned int i = 0; i < m_size; ++i) { - realOut[i] = m_fbuf[i]; + for (int i = 0; i < sz; ++i) { + realOut[i] = fbuf[i]; } } - void inversePolar(float *magIn, float *phaseIn, float *realOut) { + void inversePolar(const float *R__ magIn, const float *R__ phaseIn, float *R__ 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]); + 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 != m_fbuf) + if (realOut != fbuf) #endif - for (unsigned int i = 0; i < m_size; ++i) { - realOut[i] = m_fbuf[i]; + 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; + } + fftw_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]; } } @@ -460,27 +594,278 @@ private: #else double *m_dbuf; #endif - fftw_complex *m_dpacked; - unsigned int m_size; - static unsigned int m_extantf; - static unsigned int m_extantd; + fftw_complex * m_dpacked; + const int m_size; + static int m_extantf; + static int m_extantd; static Mutex m_extantMutex; }; -unsigned int +int D_FFTW::m_extantf = 0; -unsigned int +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(unsigned int size) : m_size(size), m_table(0), m_frb(0), m_drb(0) { + 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]; @@ -489,8 +874,8 @@ public: m_table = new int[m_size]; - unsigned int bits; - unsigned int i, j, k, m; + int bits; + int i, j, k, m; for (i = 0; ; ++i) { if (m_size & (1 << i)) { @@ -525,53 +910,64 @@ public: void initFloat() { } void initDouble() { } - void forward(double *realIn, double *realOut, double *imagOut) { + void forward(const double *R__ realIn, double *R__ realOut, double *R__ 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]; + 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(double *realIn, double *magOut, double *phaseOut) { + void forwardPolar(const double *R__ realIn, double *R__ magOut, double *R__ phaseOut) { basefft(false, realIn, 0, m_c, m_d); - for (unsigned int i = 0; i <= m_size/2; ++i) { + 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(double *realIn, double *magOut) { + void forwardMagnitude(const double *R__ realIn, double *R__ magOut) { basefft(false, realIn, 0, m_c, m_d); - for (unsigned int i = 0; i <= m_size/2; ++i) { + 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(float *realIn, float *realOut, float *imagOut) { - for (size_t i = 0; i < m_size; ++i) m_a[i] = realIn[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); - 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]; + 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(float *realIn, float *magOut, float *phaseOut) { - for (size_t i = 0; i < m_size; ++i) m_a[i] = realIn[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); - for (unsigned int i = 0; i <= m_size/2; ++i) { + 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(float *realIn, float *magOut) { - for (size_t i = 0; i < m_size; ++i) m_a[i] = realIn[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); - for (unsigned int i = 0; i <= m_size/2; ++i) { + 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(double *realIn, double *imagIn, double *realOut) { - for (unsigned int i = 0; i <= m_size/2; ++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; @@ -584,8 +980,9 @@ public: 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) { + 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; @@ -598,8 +995,23 @@ public: 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) { + 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; @@ -610,11 +1022,12 @@ public: } } basefft(true, m_a, m_b, m_c, m_d); - for (unsigned int i = 0; i < m_size; ++i) realOut[i] = m_c[i]; + for (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) { + 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; @@ -625,7 +1038,22 @@ public: } } basefft(true, m_a, m_b, m_c, m_d); - for (unsigned int i = 0; i < m_size; ++i) realOut[i] = m_c[i]; + 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() { @@ -639,7 +1067,7 @@ public: } private: - unsigned int m_size; + const int m_size; int *m_table; float *m_frb; double *m_drb; @@ -647,32 +1075,36 @@ private: double *m_b; double *m_c; double *m_d; - void basefft(bool inverse, double *ri, double *ii, double *ro, double *io); + void basefft(bool inverse, const double *R__ ri, const double *R__ ii, double *R__ ro, double *R__ io); }; void -D_Cross::basefft(bool inverse, double *ri, double *ii, double *ro, double *io) +D_Cross::basefft(bool inverse, const double *R__ ri, const double *R__ ii, double *R__ ro, double *R__ io) { if (!ri || !ro || !io) return; - unsigned int i, j, k, m; - unsigned int blockSize, blockEnd; + int i, j, k, m; + int blockSize, blockEnd; double tr, ti; double angle = 2.0 * M_PI; if (inverse) angle = -angle; - const unsigned int n = m_size; + 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; } } @@ -736,10 +1168,14 @@ D_Cross::basefft(bool inverse, double *ri, double *ii, double *ro, double *io) */ } +#endif /* USE_BUILTIN_FFT */ + +} /* end namespace FFTs */ + int FFT::m_method = -1; -FFT::FFT(unsigned int size) +FFT::FFT(int size, int debugLevel) { if ((size < 2) || (size & (size-1))) { @@ -748,25 +1184,71 @@ FFT::FFT(unsigned int size) } 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: - d = new D_Cross(size); + 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: -// std::cerr << "FFT::FFT(" << size << "): using FFTW3 implementation" -// << std::endl; - d = new D_FFTW(size); +#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: - std::cerr << "FFT::FFT(" << size << "): WARNING: using slow built-in implementation" - << std::endl; - d = new D_Cross(size); +#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; } } @@ -777,65 +1259,77 @@ FFT::~FFT() } void -FFT::forward(double *realIn, double *realOut, double *imagOut) +FFT::forward(const double *R__ realIn, double *R__ realOut, double *R__ imagOut) { d->forward(realIn, realOut, imagOut); } void -FFT::forwardPolar(double *realIn, double *magOut, double *phaseOut) +FFT::forwardPolar(const double *R__ realIn, double *R__ magOut, double *R__ phaseOut) { d->forwardPolar(realIn, magOut, phaseOut); } void -FFT::forwardMagnitude(double *realIn, double *magOut) +FFT::forwardMagnitude(const double *R__ realIn, double *R__ magOut) { d->forwardMagnitude(realIn, magOut); } void -FFT::forward(float *realIn, float *realOut, float *imagOut) +FFT::forward(const float *R__ realIn, float *R__ realOut, float *R__ imagOut) { d->forward(realIn, realOut, imagOut); } void -FFT::forwardPolar(float *realIn, float *magOut, float *phaseOut) +FFT::forwardPolar(const float *R__ realIn, float *R__ magOut, float *R__ phaseOut) { d->forwardPolar(realIn, magOut, phaseOut); } void -FFT::forwardMagnitude(float *realIn, float *magOut) +FFT::forwardMagnitude(const float *R__ realIn, float *R__ magOut) { d->forwardMagnitude(realIn, magOut); } void -FFT::inverse(double *realIn, double *imagIn, double *realOut) +FFT::inverse(const double *R__ realIn, const double *R__ imagIn, double *R__ realOut) { d->inverse(realIn, imagIn, realOut); } void -FFT::inversePolar(double *magIn, double *phaseIn, double *realOut) +FFT::inversePolar(const double *R__ magIn, const double *R__ phaseIn, double *R__ realOut) { d->inversePolar(magIn, phaseIn, realOut); } void -FFT::inverse(float *realIn, float *imagIn, float *realOut) +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(float *magIn, float *phaseIn, float *realOut) +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() { diff --git a/src/FFT.h b/src/FFT.h index 65c185d..b31d925 100644 --- a/src/FFT.h +++ b/src/FFT.h @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + 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 @@ -15,6 +15,8 @@ #ifndef _RUBBERBAND_FFT_H_ #define _RUBBERBAND_FFT_H_ +#include "sysutils.h" + namespace RubberBand { class FFTImpl; @@ -36,22 +38,24 @@ class FFT public: enum Exception { InvalidSize }; - FFT(unsigned int size); // may throw InvalidSize + FFT(int size, int debugLevel = 0); // may throw InvalidSize ~FFT(); - void forward(double *realIn, double *realOut, double *imagOut); - void forwardPolar(double *realIn, double *magOut, double *phaseOut); - void forwardMagnitude(double *realIn, double *magOut); + void forward(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(float *realIn, float *realOut, float *imagOut); - void forwardPolar(float *realIn, float *magOut, float *phaseOut); - void forwardMagnitude(float *realIn, float *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(double *realIn, double *imagIn, double *realOut); - void inversePolar(double *magIn, double *phaseIn, double *realOut); + 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(float *realIn, float *imagIn, float *realOut); - void inversePolar(float *magIn, float *phaseIn, float *realOut); + 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 diff --git a/src/HighFrequencyAudioCurve.cpp b/src/HighFrequencyAudioCurve.cpp index 1bc4399..987cf76 100644 --- a/src/HighFrequencyAudioCurve.cpp +++ b/src/HighFrequencyAudioCurve.cpp @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + 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 @@ -38,12 +38,14 @@ HighFrequencyAudioCurve::setWindowSize(size_t newSize) } float -HighFrequencyAudioCurve::process(float *mag, size_t increment) +HighFrequencyAudioCurve::process(const float *R__ mag, size_t increment) { float result = 0.0; - for (size_t n = 0; n <= m_windowSize / 2; ++n) { - result += mag[n] * n; + const int sz = m_windowSize / 2; + + for (int n = 0; n <= sz; ++n) { + result = result + mag[n] * n; } return result; diff --git a/src/HighFrequencyAudioCurve.h b/src/HighFrequencyAudioCurve.h index e891afa..d42513f 100644 --- a/src/HighFrequencyAudioCurve.h +++ b/src/HighFrequencyAudioCurve.h @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + 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 @@ -30,7 +30,7 @@ public: virtual void setWindowSize(size_t newSize); - virtual float process(float *mag, size_t increment); + virtual float process(const float *R__ mag, size_t increment); virtual void reset(); }; diff --git a/src/PercussiveAudioCurve.cpp b/src/PercussiveAudioCurve.cpp index 98c6508..f892596 100644 --- a/src/PercussiveAudioCurve.cpp +++ b/src/PercussiveAudioCurve.cpp @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + 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 @@ -14,15 +14,18 @@ #include "PercussiveAudioCurve.h" +#include "Profiler.h" + #include + namespace RubberBand { PercussiveAudioCurve::PercussiveAudioCurve(size_t sampleRate, size_t windowSize) : AudioCurve(sampleRate, windowSize) { - m_prevMag = new double[m_windowSize/2 + 1]; + m_prevMag = new float[m_windowSize/2 + 1]; for (size_t i = 0; i <= m_windowSize/2; ++i) { m_prevMag[i] = 0.f; @@ -45,29 +48,60 @@ PercussiveAudioCurve::reset() void PercussiveAudioCurve::setWindowSize(size_t newSize) { - delete[] m_prevMag; m_windowSize = newSize; - - m_prevMag = new double[m_windowSize/2 + 1]; + + delete[] m_prevMag; + m_prevMag = new float[m_windowSize/2 + 1]; reset(); } float -PercussiveAudioCurve::process(float *mag, size_t increment) +PercussiveAudioCurve::process(const float *R__ mag, size_t increment) { - static float threshold = pow(10, 0.3); - static float zeroThresh = pow(10, -16); + 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; - for (size_t n = 1; n <= m_windowSize / 2; ++n) { - float sqrmag = mag[n] * mag[n]; - bool above = ((sqrmag / m_prevMag[n]) >= threshold); + 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 (sqrmag > zeroThresh) ++nonZeroCount; - m_prevMag[n] = sqrmag; + 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; diff --git a/src/PercussiveAudioCurve.h b/src/PercussiveAudioCurve.h index 1d23a50..29c4fb7 100644 --- a/src/PercussiveAudioCurve.h +++ b/src/PercussiveAudioCurve.h @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + 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 @@ -29,11 +29,12 @@ public: virtual void setWindowSize(size_t newSize); - virtual float process(float *mag, size_t increment); + virtual float process(const float *R__ mag, size_t increment); + virtual float process(const double *R__ mag, size_t increment); virtual void reset(); protected: - double *m_prevMag; + float *R__ m_prevMag; }; } diff --git a/src/Profiler.cpp b/src/Profiler.cpp new file mode 100644 index 0000000..df148d4 --- /dev/null +++ b/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 +#include +#include +#include + +#include + +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 > 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 TimeRMap; + typedef std::multimap 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/src/Profiler.h b/src/Profiler.h new file mode 100644 index 0000000..947eb82 --- /dev/null +++ b/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 +#else +#include "sysutils.h" +#ifndef _WIN32 +#include +#endif +#endif +#endif + +#include + +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 TimePair; + typedef std::map ProfileMap; + typedef std::map WorstCallMap; + static ProfileMap m_profiles; + static WorstCallMap m_worstCalls; + static void add(const char *, float); +}; + +#else + +class Profiler +{ +public: + Profiler(const char *, bool = false) { } + ~Profiler() { } + + void update() const { } + void end() { } + static void dump() { } +}; + +#endif + +} + +#endif diff --git a/src/Resampler.cpp b/src/Resampler.cpp index 731ac4e..26633e2 100644 --- a/src/Resampler.cpp +++ b/src/Resampler.cpp @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + 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 @@ -14,6 +14,8 @@ #include "Resampler.h" +#include "Profiler.h" + #include #include @@ -22,16 +24,40 @@ #include + + namespace RubberBand { -class Resampler::D +class ResamplerImpl { public: - D(Quality quality, size_t channels, size_t maxBufferSize); - ~D(); + 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; - size_t resample(float **in, float **out, - size_t incount, float ratio, bool final); + 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(); @@ -39,25 +65,30 @@ protected: SRC_STATE *m_src; float *m_iin; float *m_iout; - size_t m_channels; - size_t m_iinsize; - size_t m_ioutsize; + int m_channels; + int m_iinsize; + int m_ioutsize; + int m_debugLevel; }; -Resampler::D::D(Quality quality, size_t channels, size_t maxBufferSize) : +D_SRC::D_SRC(Resampler::Quality quality, int channels, int maxBufferSize, + int debugLevel) : m_src(0), m_iin(0), m_iout(0), m_channels(channels), m_iinsize(0), - m_ioutsize(0) + m_ioutsize(0), + m_debugLevel(debugLevel) { -// std::cerr << "Resampler::Resampler: using libsamplerate implementation" -// << std::endl; + if (m_debugLevel > 0) { + std::cerr << "Resampler::Resampler: using libsamplerate implementation" + << std::endl; + } int err = 0; - m_src = src_new(quality == Best ? SRC_SINC_BEST_QUALITY : - quality == Fastest ? SRC_LINEAR : + m_src = src_new(quality == Resampler::Best ? SRC_SINC_BEST_QUALITY : + quality == Resampler::Fastest ? SRC_LINEAR : SRC_SINC_FASTEST, channels, &err); @@ -72,7 +103,7 @@ Resampler::D::D(Quality quality, size_t channels, size_t maxBufferSize) : } } -Resampler::D::~D() +D_SRC::~D_SRC() { src_delete(m_src); if (m_iinsize > 0) { @@ -83,16 +114,19 @@ Resampler::D::~D() } } -size_t -Resampler::D::resample(float **in, float **out, size_t incount, float ratio, - bool final) +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; - size_t outcount = lrintf(ceilf(incount * ratio)); + int outcount = lrintf(ceilf(incount * ratio)); if (m_channels == 1) { - data.data_in = *in; + data.data_in = const_cast(*in); //!!!??? data.data_out = *out; } else { if (incount * m_channels > m_iinsize) { @@ -103,8 +137,8 @@ Resampler::D::resample(float **in, float **out, size_t incount, float ratio, m_ioutsize = outcount * m_channels; m_iout = (float *)realloc(m_iout, m_ioutsize * sizeof(float)); } - for (size_t i = 0; i < incount; ++i) { - for (size_t c = 0; c < m_channels; ++c) { + for (int i = 0; i < incount; ++i) { + for (int c = 0; c < m_channels; ++c) { m_iin[i * m_channels + c] = in[c][i]; } } @@ -123,7 +157,7 @@ Resampler::D::resample(float **in, float **out, size_t incount, float ratio, if (m_channels > 1) { for (int i = 0; i < data.output_frames_gen; ++i) { - for (size_t c = 0; c < m_channels; ++c) { + for (int c = 0; c < m_channels; ++c) { out[c][i] = m_iout[i * m_channels + c]; } } @@ -133,37 +167,81 @@ Resampler::D::resample(float **in, float **out, size_t incount, float ratio, } void -Resampler::D::reset() +D_SRC::reset() { src_reset(m_src); } -} // end namespace -namespace RubberBand { +} /* end namespace Resamplers */ -Resampler::Resampler(Quality quality, size_t channels, size_t maxBufferSize) +Resampler::Resampler(Resampler::Quality quality, int channels, + int maxBufferSize, int debugLevel) { - m_d = new D(quality, channels, maxBufferSize); + 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 m_d; + delete d; } -size_t -Resampler::resample(float **in, float **out, - size_t incount, float ratio, bool final) +int +Resampler::resample(const float *const R__ *const R__ in, + float *const R__ *const R__ out, + int incount, float ratio, bool final) { - return m_d->resample(in, out, incount, ratio, final); + Profiler profiler("Resampler::resample"); + return d->resample(in, out, incount, ratio, final); } void Resampler::reset() { - m_d->reset(); + d->reset(); } } diff --git a/src/Resampler.h b/src/Resampler.h index bc07c58..40aad5f 100644 --- a/src/Resampler.h +++ b/src/Resampler.h @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + 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 @@ -17,8 +17,12 @@ #include +#include "sysutils.h" + namespace RubberBand { +class ResamplerImpl; + class Resampler { public: @@ -30,17 +34,21 @@ public: * that may be passed to the resample function before the * resampler needs to reallocate its internal buffers. */ - Resampler(Quality quality, size_t channels, size_t maxBufferSize = 0); + Resampler(Quality quality, int channels, int maxBufferSize = 0, + int debugLevel = 0); ~Resampler(); - size_t resample(float **in, float **out, - size_t incount, float ratio, bool final = false); + 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: - class D; - D *m_d; + ResamplerImpl *d; + int m_method; }; } diff --git a/src/RingBuffer.h b/src/RingBuffer.h index a3673d3..ebc0952 100644 --- a/src/RingBuffer.h +++ b/src/RingBuffer.h @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + 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 @@ -17,11 +17,15 @@ #include +#include + #ifndef _WIN32 #include #endif #include "Scavenger.h" +#include "Profiler.h" + //#define DEBUG_RINGBUFFER 1 @@ -57,7 +61,7 @@ public: * power of two, this means n should ideally be some power of two * minus one. */ - RingBuffer(size_t n); + RingBuffer(int n); virtual ~RingBuffer(); @@ -65,7 +69,7 @@ public: * Return the total capacity of the ring buffer in samples. * (This is the argument n passed to the constructor.) */ - size_t getSize() const; + int getSize() const; /** * Resize the ring buffer. This also empties it; use resized() @@ -73,7 +77,7 @@ public: * new, larger buffer; the old buffer is scavenged after a seemly * delay. Should be called from the write thread. */ - void resize(size_t newSize); + void resize(int newSize); /** * Return a new ring buffer (allocated with "new" -- called must @@ -83,7 +87,7 @@ public: * or inconsistent. If this buffer's data will not fit in the new * size, the contents are undefined. */ - RingBuffer *resized(size_t newSize, int R = 0) const; + RingBuffer *resized(int newSize, int R = 0) const; /** * Lock the ring buffer into physical memory. Returns true @@ -101,19 +105,19 @@ public: * Return the amount of data available for reading by reader R, in * samples. */ - size_t getReadSpace(int R = 0) const; + int getReadSpace(int R = 0) const; /** * Return the amount of space available for writing, in samples. */ - size_t getWriteSpace() const; + 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. */ - size_t read(T *destination, size_t n, int R = 0); + int read(T *R__ destination, int n, int R = 0); /** * Read n samples from the buffer, for reader R, adding them to @@ -121,7 +125,7 @@ public: * will be left alone. Returns the number of samples actually * read. */ - size_t readAdding(T *destination, size_t n, int R = 0); + int readAdding(T *R__ destination, int n, int R = 0); /** * Read one sample from the buffer, for reader R. If no sample is @@ -139,7 +143,7 @@ public: * n are available, the remainder will be zeroed out. Returns the * number of samples actually read. */ - size_t peek(T *destination, size_t n, int R = 0) const; + int peek(T *R__ destination, int n, int R = 0) const; /** * Read one sample from the buffer, if available, without @@ -155,27 +159,27 @@ public: * samples). Returns the number of samples actually available for * discarding. */ - size_t skip(size_t n, int R = 0); + 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. */ - size_t write(const T *source, size_t n); + 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. */ - size_t zero(size_t n); + int zero(int n); protected: - T *m_buffer; - volatile size_t m_writer; - volatile size_t m_readers[N]; - size_t m_size; + T *R__ m_buffer; + volatile int m_writer; + volatile int m_readers[N]; + int m_size; bool m_mlocked; static Scavenger > m_scavenger; @@ -189,7 +193,7 @@ template Scavenger > RingBuffer::m_scavenger; template -RingBuffer::RingBuffer(size_t n) : +RingBuffer::RingBuffer(int n) : m_buffer(new T[n + 1]), m_writer(0), m_size(n + 1), @@ -220,7 +224,7 @@ RingBuffer::~RingBuffer() } template -size_t +int RingBuffer::getSize() const { #ifdef DEBUG_RINGBUFFER @@ -232,7 +236,7 @@ RingBuffer::getSize() const template void -RingBuffer::resize(size_t newSize) +RingBuffer::resize(int newSize) { #ifdef DEBUG_RINGBUFFER std::cerr << "RingBuffer[" << this << "]::resize(" << newSize << ")" << std::endl; @@ -259,12 +263,12 @@ RingBuffer::resize(size_t newSize) template RingBuffer * -RingBuffer::resized(size_t newSize, int R) const +RingBuffer::resized(int newSize, int R) const { RingBuffer *newBuffer = new RingBuffer(newSize); - size_t w = m_writer; - size_t r = m_readers[R]; + int w = m_writer; + int r = m_readers[R]; while (r != w) { T value = m_buffer[r]; @@ -297,12 +301,12 @@ RingBuffer::reset() } template -size_t +int RingBuffer::getReadSpace(int R) const { - size_t writer = m_writer; - size_t reader = m_readers[R]; - size_t space; + int writer = m_writer; + int reader = m_readers[R]; + int space; #ifdef DEBUG_RINGBUFFER std::cerr << "RingBuffer[" << this << "]::getReadSpace(" << R << "): reader " << reader << ", writer " << writer << std::endl; @@ -320,20 +324,20 @@ RingBuffer::getReadSpace(int R) const } template -size_t +int RingBuffer::getWriteSpace() const { - size_t space = 0; + int space = 0; for (int i = 0; i < N; ++i) { - size_t writer = m_writer; - size_t reader = m_readers[i]; - size_t here = (reader + m_size - writer - 1); + 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 - size_t rs(getReadSpace()), rp(m_readers[0]); + 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; @@ -348,39 +352,44 @@ RingBuffer::getWriteSpace() const } template -size_t -RingBuffer::read(T *destination, size_t n, int R) +int +RingBuffer::read(T *R__ destination, int n, int R) { + Profiler profiler("RingBuffer::read"); + #ifdef DEBUG_RINGBUFFER std::cerr << "RingBuffer[" << this << "]::read(dest, " << n << ", " << R << ")" << std::endl; #endif - size_t available = getReadSpace(R); + int available = getReadSpace(R); if (n > available) { #ifdef DEBUG_RINGBUFFER std::cerr << "WARNING: Only " << available << " samples available" << std::endl; #endif - for (size_t i = available; i < n; ++i) { + for (int i = available; i < n; ++i) { destination[i] = 0; } n = available; } if (n == 0) return n; - size_t reader = m_readers[R]; - size_t here = m_size - reader; + int reader = m_readers[R]; + int here = m_size - reader; + T *const R__ bufbase = m_buffer + reader; if (here >= n) { - for (size_t i = 0; i < n; ++i) { - destination[i] = (m_buffer + reader)[i]; + for (int i = 0; i < n; ++i) { + destination[i] = bufbase[i]; } } else { - for (size_t i = 0; i < here; ++i) { - destination[i] = (m_buffer + reader)[i]; + for (int i = 0; i < here; ++i) { + destination[i] = bufbase[i]; } - for (size_t i = 0; i < (n - here); ++i) { - destination[i + here] = m_buffer[i]; + T *const R__ destbase = destination + here; + const int nh = n - here; + for (int i = 0; i < nh; ++i) { + destbase[i] = m_buffer[i]; } } @@ -396,14 +405,16 @@ RingBuffer::read(T *destination, size_t n, int R) } template -size_t -RingBuffer::readAdding(T *destination, size_t n, int R) +int +RingBuffer::readAdding(T *R__ destination, int n, int R) { + Profiler profiler("RingBuffer::readAdding"); + #ifdef DEBUG_RINGBUFFER std::cerr << "RingBuffer[" << this << "]::readAdding(dest, " << n << ", " << R << ")" << std::endl; #endif - size_t available = getReadSpace(R); + int available = getReadSpace(R); if (n > available) { #ifdef DEBUG_RINGBUFFER std::cerr << "WARNING: Only " << available << " samples available" @@ -413,19 +424,22 @@ RingBuffer::readAdding(T *destination, size_t n, int R) } if (n == 0) return n; - size_t reader = m_readers[R]; - size_t here = m_size - reader; + int reader = m_readers[R]; + int here = m_size - reader; + const T *const R__ bufbase = m_buffer + reader; if (here >= n) { - for (size_t i = 0; i < n; ++i) { - destination[i] += (m_buffer + reader)[i]; + for (int i = 0; i < n; ++i) { + destination[i] += bufbase[i]; } } else { - for (size_t i = 0; i < here; ++i) { - destination[i] += (m_buffer + reader)[i]; + for (int i = 0; i < here; ++i) { + destination[i] += bufbase[i]; } - for (size_t i = 0; i < (n - here); ++i) { - destination[i + here] += m_buffer[i]; + T *const R__ destbase = destination + here; + const int nh = n - here; + for (int i = 0; i < nh; ++i) { + destbase[i] += m_buffer[i]; } } @@ -450,7 +464,7 @@ RingBuffer::readOne(int R) #endif return 0; } - size_t reader = m_readers[R]; + int reader = m_readers[R]; T value = m_buffer[reader]; if (++reader == m_size) reader = 0; m_readers[R] = reader; @@ -458,14 +472,16 @@ RingBuffer::readOne(int R) } template -size_t -RingBuffer::peek(T *destination, size_t n, int R) const +int +RingBuffer::peek(T *R__ destination, int n, int R) const { + Profiler profiler("RingBuffer::peek"); + #ifdef DEBUG_RINGBUFFER std::cerr << "RingBuffer[" << this << "]::peek(dest, " << n << ", " << R << ")" << std::endl; #endif - size_t available = getReadSpace(R); + int available = getReadSpace(R); if (n > available) { #ifdef DEBUG_RINGBUFFER std::cerr << "WARNING: Only " << available << " samples available" @@ -476,19 +492,22 @@ RingBuffer::peek(T *destination, size_t n, int R) const } if (n == 0) return n; - size_t reader = m_readers[R]; - size_t here = m_size - reader; + int reader = m_readers[R]; + int here = m_size - reader; + const T *const R__ bufbase = m_buffer + reader; if (here >= n) { - for (size_t i = 0; i < n; ++i) { - destination[i] = (m_buffer + reader)[i]; + for (int i = 0; i < n; ++i) { + destination[i] = bufbase[i]; } } else { - for (size_t i = 0; i < here; ++i) { - destination[i] = (m_buffer + reader)[i]; + for (int i = 0; i < here; ++i) { + destination[i] = bufbase[i]; } - for (size_t i = 0; i < (n - here); ++i) { - destination[i + here] = m_buffer[i]; + T *const R__ destbase = destination + here; + const int nh = n - here; + for (int i = 0; i < nh; ++i) { + destbase[i] = m_buffer[i]; } } @@ -519,14 +538,14 @@ RingBuffer::peekOne(int R) const } template -size_t -RingBuffer::skip(size_t n, int R) +int +RingBuffer::skip(int n, int R) { #ifdef DEBUG_RINGBUFFER std::cerr << "RingBuffer[" << this << "]::skip(" << n << ", " << R << ")" << std::endl; #endif - size_t available = getReadSpace(R); + int available = getReadSpace(R); if (n > available) { #ifdef DEBUG_RINGBUFFER std::cerr << "WARNING: Only " << available << " samples available" @@ -536,7 +555,7 @@ RingBuffer::skip(size_t n, int R) } if (n == 0) return n; - size_t reader = m_readers[R]; + int reader = m_readers[R]; reader += n; while (reader >= m_size) reader -= m_size; m_readers[R] = reader; @@ -544,14 +563,16 @@ RingBuffer::skip(size_t n, int R) } template -size_t -RingBuffer::write(const T *source, size_t n) +int +RingBuffer::write(const T *source, int n) { + Profiler profiler("RingBuffer::write"); + #ifdef DEBUG_RINGBUFFER std::cerr << "RingBuffer[" << this << "]::write(" << n << ")" << std::endl; #endif - size_t available = getWriteSpace(); + int available = getWriteSpace(); if (n > available) { #ifdef DEBUG_RINGBUFFER std::cerr << "WARNING: Only room for " << available << " samples" @@ -561,18 +582,23 @@ RingBuffer::write(const T *source, size_t n) } if (n == 0) return n; - size_t writer = m_writer; - size_t here = m_size - writer; + int writer = m_writer; + int here = m_size - writer; + T *const R__ bufbase = m_buffer + writer; + if (here >= n) { - for (size_t i = 0; i < n; ++i) { - (m_buffer + writer)[i] = source[i]; + for (int i = 0; i < n; ++i) { + bufbase[i] = source[i]; } } else { - for (size_t i = 0; i < here; ++i) { - (m_buffer + writer)[i] = source[i]; + for (int i = 0; i < here; ++i) { + bufbase[i] = source[i]; } - for (size_t i = 0; i < (n - here); ++i) { - m_buffer[i] = (source + here)[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]; } } @@ -588,14 +614,16 @@ RingBuffer::write(const T *source, size_t n) } template -size_t -RingBuffer::zero(size_t n) +int +RingBuffer::zero(int n) { + Profiler profiler("RingBuffer::zero"); + #ifdef DEBUG_RINGBUFFER std::cerr << "RingBuffer[" << this << "]::zero(" << n << ")" << std::endl; #endif - size_t available = getWriteSpace(); + int available = getWriteSpace(); if (n > available) { #ifdef DEBUG_RINGBUFFER std::cerr << "WARNING: Only room for " << available << " samples" @@ -605,17 +633,20 @@ RingBuffer::zero(size_t n) } if (n == 0) return n; - size_t writer = m_writer; - size_t here = m_size - writer; + int writer = m_writer; + int here = m_size - writer; + T *const R__ bufbase = m_buffer + writer; + if (here >= n) { - for (size_t i = 0; i < n; ++i) { - (m_buffer + writer)[i] = 0; + for (int i = 0; i < n; ++i) { + bufbase[i] = 0; } } else { - for (size_t i = 0; i < here; ++i) { - (m_buffer + writer)[i] = 0; + for (int i = 0; i < here; ++i) { + bufbase[i] = 0; } - for (size_t i = 0; i < (n - here); ++i) { + const int nh = n - here; + for (int i = 0; i < nh; ++i) { m_buffer[i] = 0; } } @@ -633,4 +664,6 @@ RingBuffer::zero(size_t n) } +//#include "RingBuffer.cpp" + #endif // _RINGBUFFER_H_ diff --git a/src/RubberBandStretcher.cpp b/src/RubberBandStretcher.cpp index 9a401b4..9236fbd 100644 --- a/src/RubberBandStretcher.cpp +++ b/src/RubberBandStretcher.cpp @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + 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 @@ -81,6 +81,18 @@ 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) { diff --git a/src/Scavenger.h b/src/Scavenger.h index 54af5da..d1b6ca9 100644 --- a/src/Scavenger.h +++ b/src/Scavenger.h @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + 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 @@ -17,9 +17,12 @@ #include #include -#include #include +#ifndef WIN32 +#include +#endif + #include "Thread.h" #include "sysutils.h" diff --git a/src/SpectralDifferenceAudioCurve.cpp b/src/SpectralDifferenceAudioCurve.cpp index fe26e3e..0deec53 100644 --- a/src/SpectralDifferenceAudioCurve.cpp +++ b/src/SpectralDifferenceAudioCurve.cpp @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + 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 @@ -20,7 +20,7 @@ namespace RubberBand SpectralDifferenceAudioCurve::SpectralDifferenceAudioCurve(size_t sampleRate, size_t windowSize) : AudioCurve(sampleRate, windowSize) { - m_prevMag = new double[m_windowSize/2 + 1]; + m_prevMag = new float[m_windowSize/2 + 1]; for (size_t i = 0; i <= m_windowSize/2; ++i) { m_prevMag[i] = 0.f; @@ -43,11 +43,16 @@ SpectralDifferenceAudioCurve::reset() void SpectralDifferenceAudioCurve::setWindowSize(size_t newSize) { + delete[] m_prevMag; m_windowSize = newSize; + + m_prevMag = new float[m_windowSize/2 + 1]; + + reset(); } float -SpectralDifferenceAudioCurve::process(float *mag, size_t increment) +SpectralDifferenceAudioCurve::process(const float *R__ mag, size_t increment) { float result = 0.0; diff --git a/src/SpectralDifferenceAudioCurve.h b/src/SpectralDifferenceAudioCurve.h index c6f4484..6ab0af9 100644 --- a/src/SpectralDifferenceAudioCurve.h +++ b/src/SpectralDifferenceAudioCurve.h @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + 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 @@ -30,11 +30,11 @@ public: virtual void setWindowSize(size_t newSize); - virtual float process(float *mag, size_t increment); + virtual float process(const float *R__ mag, size_t increment); virtual void reset(); protected: - double *m_prevMag; + float *R__ m_prevMag; }; } diff --git a/src/StretchCalculator.cpp b/src/StretchCalculator.cpp index 77d1c50..f212a87 100644 --- a/src/StretchCalculator.cpp +++ b/src/StretchCalculator.cpp @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + 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 @@ -19,6 +19,9 @@ #include #include #include +#include + +#include "sysutils.h" namespace RubberBand { @@ -162,9 +165,11 @@ StretchCalculator::calculate(double ratio, size_t inputDuration, int StretchCalculator::calculateSingle(double ratio, - size_t inputDurationSoFar, - float df) + 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 @@ -176,10 +181,10 @@ StretchCalculator::calculateSingle(double ratio, // from the ratio directly. For the moment we're happy if it // works well in common situations. - float transientThreshold = 0.35; - if (ratio > 1) transientThreshold = 0.25; + float transientThreshold = 0.35f; + if (ratio > 1) transientThreshold = 0.25f; - if (m_useHardPeaks && df > m_prevDf * 1.1 && df > transientThreshold) { + if (m_useHardPeaks && df > m_prevDf * 1.1f && df > transientThreshold) { isTransient = true; } @@ -192,37 +197,37 @@ StretchCalculator::calculateSingle(double ratio, if (isTransient && m_transientAmnesty == 0) { if (m_debugLevel > 1) { - std::cerr << "StretchCalculator::calculateSingle: transient found at " - << inputDurationSoFar << std::endl; + std::cerr << "StretchCalculator::calculateSingle: transient" + << std::endl; } - m_divergence += m_increment - (m_increment * ratio); + 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(m_increment)))); + lrint(ceil(double(m_sampleRate) / (20 * double(increment)))); - m_recovery = m_divergence / ((m_sampleRate / 10.0) / m_increment); - return -m_increment; + m_recovery = m_divergence / ((m_sampleRate / 10.0) / increment); + return -int(increment); } if (m_prevRatio != ratio) { - m_recovery = m_divergence / ((m_sampleRate / 10.0) / m_increment); + m_recovery = m_divergence / ((m_sampleRate / 10.0) / increment); m_prevRatio = ratio; } if (m_transientAmnesty > 0) --m_transientAmnesty; - int incr = lrint(m_increment * ratio - m_recovery); + 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((m_increment * ratio) / 2)) { - incr = lrint((m_increment * ratio) / 2); - } else if (incr > lrint(m_increment * ratio * 2)) { - incr = lrint(m_increment * ratio * 2); + 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 = (m_increment * ratio) - incr; + double divdiff = (increment * ratio) - incr; if (m_debugLevel > 2 || (m_debugLevel > 1 && m_divergence != 0)) { std::cerr << "divdiff = " << divdiff << std::endl; @@ -232,7 +237,7 @@ StretchCalculator::calculateSingle(double ratio, m_divergence -= divdiff; if ((prevDivergence < 0 && m_divergence > 0) || (prevDivergence > 0 && m_divergence < 0)) { - m_recovery = m_divergence / ((m_sampleRate / 10.0) / m_increment); + m_recovery = m_divergence / ((m_sampleRate / 10.0) / increment); } return incr; diff --git a/src/StretchCalculator.h b/src/StretchCalculator.h index f6c3544..e79c8e3 100644 --- a/src/StretchCalculator.h +++ b/src/StretchCalculator.h @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + 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 @@ -45,9 +45,12 @@ public: * 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, size_t inputDurationSoFar, - float curveValue); + virtual int calculateSingle(double ratio, float curveValue, + size_t increment = 0); void setUseHardPeaks(bool use) { m_useHardPeaks = use; } diff --git a/src/StretcherChannelData.cpp b/src/StretcherChannelData.cpp index ecbb9a6..2a8fe40 100644 --- a/src/StretcherChannelData.cpp +++ b/src/StretcherChannelData.cpp @@ -1,22 +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. +*/ + #include "StretcherChannelData.h" #include "Resampler.h" + namespace RubberBand { RubberBandStretcher::Impl::ChannelData::ChannelData(size_t windowSize, - size_t outbufSize) + int overSample, + size_t outbufSize) : + oversample(overSample) { std::set s; construct(s, windowSize, outbufSize); } RubberBandStretcher::Impl::ChannelData::ChannelData(const std::set &windowSizes, + int overSample, size_t initialWindowSize, - size_t outbufSize) + size_t outbufSize) : + oversample(overSample) { construct(windowSizes, initialWindowSize, outbufSize); } @@ -37,7 +54,8 @@ RubberBandStretcher::Impl::ChannelData::construct(const std::set &window if (initialWindowSize > maxSize) maxSize = initialWindowSize; } - size_t realSize = maxSize/2 + 1; // size of the real "half" of freq data + // 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; @@ -51,19 +69,21 @@ RubberBandStretcher::Impl::ChannelData::construct(const std::set &window prevPhase = new double[realSize]; unwrappedPhase = new double[realSize]; freqPeak = new size_t[realSize]; + envelope = new double[realSize]; + + fltbuf = new float[maxSize]; accumulator = new float[maxSize]; windowAccumulator = new float[maxSize]; - fltbuf = new float[maxSize]; for (std::set::const_iterator i = windowSizes.begin(); i != windowSizes.end(); ++i) { - ffts[*i] = new FFT(*i); + ffts[*i] = new FFT(*i * oversample); ffts[*i]->initDouble(); } if (windowSizes.find(initialWindowSize) == windowSizes.end()) { - ffts[initialWindowSize] = new FFT(initialWindowSize); + ffts[initialWindowSize] = new FFT(initialWindowSize * oversample); ffts[initialWindowSize]->initDouble(); } fft = ffts[initialWindowSize]; @@ -82,16 +102,17 @@ RubberBandStretcher::Impl::ChannelData::construct(const std::set &window prevPhase[i] = 0.0; unwrappedPhase[i] = 0.0; freqPeak[i] = 0; + envelope[i] = 0.0; } - for (size_t i = 0; i < initialWindowSize; ++i) { + for (size_t i = 0; i < initialWindowSize * oversample; ++i) { dblbuf[i] = 0.0; } for (size_t i = 0; i < maxSize; ++i) { accumulator[i] = 0.f; windowAccumulator[i] = 0.f; - fltbuf[i] = 0.0; + fltbuf[i] = 0.f; } } @@ -99,7 +120,7 @@ void RubberBandStretcher::Impl::ChannelData::setWindowSize(size_t windowSize) { size_t oldSize = inbuf->getSize(); - size_t realSize = windowSize/2 + 1; + size_t realSize = (windowSize * oversample) / 2 + 1; // std::cerr << "ChannelData::setWindowSize(" << windowSize << ") [from " << oldSize << "]" << std::endl; @@ -114,7 +135,7 @@ RubberBandStretcher::Impl::ChannelData::setWindowSize(size_t windowSize) if (ffts.find(windowSize) == ffts.end()) { //!!! this also requires a lock, but it shouldn't occur in //RT mode with proper initialisation - ffts[windowSize] = new FFT(windowSize); + ffts[windowSize] = new FFT(windowSize * oversample); ffts[windowSize]->initDouble(); } @@ -122,7 +143,7 @@ RubberBandStretcher::Impl::ChannelData::setWindowSize(size_t windowSize) dblbuf = fft->getDoubleTimeBuffer(); - for (size_t i = 0; i < windowSize; ++i) { + for (size_t i = 0; i < windowSize * oversample; ++i) { dblbuf[i] = 0.0; } @@ -155,16 +176,19 @@ RubberBandStretcher::Impl::ChannelData::setWindowSize(size_t windowSize) delete[] prevPhase; delete[] unwrappedPhase; delete[] freqPeak; + delete[] envelope; mag = new double[realSize]; phase = new double[realSize]; prevPhase = new double[realSize]; unwrappedPhase = new double[realSize]; freqPeak = new size_t[realSize]; + envelope = new double[realSize]; delete[] fltbuf; fltbuf = new float[windowSize]; + // But we do want to preserve data in these float *newAcc = new float[windowSize]; @@ -185,10 +209,11 @@ RubberBandStretcher::Impl::ChannelData::setWindowSize(size_t windowSize) prevPhase[i] = 0.0; unwrappedPhase[i] = 0.0; freqPeak[i] = 0; + envelope[i] = 0.0; } for (size_t i = 0; i < windowSize; ++i) { - fltbuf[i] = 0.0; + fltbuf[i] = 0.f; } for (size_t i = oldSize; i < windowSize; ++i) { @@ -197,7 +222,7 @@ RubberBandStretcher::Impl::ChannelData::setWindowSize(size_t windowSize) } if (ffts.find(windowSize) == ffts.end()) { - ffts[windowSize] = new FFT(windowSize); + ffts[windowSize] = new FFT(windowSize * oversample); ffts[windowSize]->initDouble(); } @@ -205,7 +230,7 @@ RubberBandStretcher::Impl::ChannelData::setWindowSize(size_t windowSize) dblbuf = fft->getDoubleTimeBuffer(); - for (size_t i = 0; i < windowSize; ++i) { + for (size_t i = 0; i < windowSize * oversample; ++i) { dblbuf[i] = 0.0; } } @@ -228,13 +253,32 @@ RubberBandStretcher::Impl::ChannelData::setOutbufSize(size_t outbufSize) } } +void +RubberBandStretcher::Impl::ChannelData::setResampleBufSize(size_t sz) +{ + if (!resamplebuf) { + resamplebuf = new float[sz]; + resamplebufSize = sz; + return; + } + + delete[] resamplebuf; + resamplebuf = new float[sz]; + for (size_t i = 0; i < sz; ++i) resamplebuf[i] = 0.f; + resamplebufSize = sz; +} + RubberBandStretcher::Impl::ChannelData::~ChannelData() { delete resampler; - delete[] resamplebuf; + + if (resamplebuf) { + delete[] resamplebuf; + } delete inbuf; delete outbuf; + delete[] mag; delete[] phase; delete[] prevPhase; @@ -243,6 +287,7 @@ RubberBandStretcher::Impl::ChannelData::~ChannelData() delete[] accumulator; delete[] windowAccumulator; delete[] fltbuf; + delete[] envelope; for (std::map::iterator i = ffts.begin(); i != ffts.end(); ++i) { @@ -264,6 +309,7 @@ RubberBandStretcher::Impl::ChannelData::reset() inCount = 0; inputSize = -1; outCount = 0; + unchanged = true; draining = false; outputComplete = false; } diff --git a/src/StretcherChannelData.h b/src/StretcherChannelData.h index ff110d1..5020568 100644 --- a/src/StretcherChannelData.h +++ b/src/StretcherChannelData.h @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + 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 @@ -19,6 +19,8 @@ #include +//#define EXPERIMENT 1 + namespace RubberBand { @@ -39,7 +41,7 @@ public: * the pitch scale factor and any maximum processing block * size specified by the user of the code. */ - ChannelData(size_t windowSize, size_t outbufSize); + ChannelData(size_t windowSize, int overSample, size_t outbufSize); /** * Construct a ChannelData structure that can process at @@ -54,7 +56,7 @@ public: * called subsequently. */ ChannelData(const std::set &windowSizes, - size_t initialWindowSize, size_t outbufSize); + int overSample, size_t initialWindowSize, size_t outbufSize); ~ChannelData(); /** @@ -76,6 +78,12 @@ public: */ 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 *inbuf; RingBuffer *outbuf; @@ -85,6 +93,7 @@ public: double *prevPhase; double *unwrappedPhase; + size_t *freqPeak; float *accumulator; @@ -93,6 +102,8 @@ public: 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 @@ -111,6 +122,8 @@ public: float *resamplebuf; size_t resamplebufSize; + int oversample; + private: void construct(const std::set &windowSizes, size_t initialWindowSize, size_t outbufSize); diff --git a/src/StretcherImpl.cpp b/src/StretcherImpl.cpp index 30bc529..3a0960e 100644 --- a/src/StretcherImpl.cpp +++ b/src/StretcherImpl.cpp @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + 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 @@ -20,6 +20,7 @@ #include "StretchCalculator.h" #include "StretcherChannelData.h" #include "Resampler.h" +#include "Profiler.h" #include #include @@ -34,6 +35,7 @@ using std::set; using std::max; using std::min; + namespace RubberBand { const size_t @@ -46,6 +48,7 @@ int RubberBandStretcher::Impl::m_defaultDebugLevel = 0; + RubberBandStretcher::Impl::Impl(RubberBandStretcher *stretcher, size_t sampleRate, size_t channels, @@ -80,6 +83,7 @@ RubberBandStretcher::Impl::Impl(RubberBandStretcher *stretcher, m_freq2(12000), m_baseWindowSize(m_defaultWindowSize) { + if (m_debugLevel > 0) { cerr << "RubberBandStretcher::Impl::Impl: rate = " << m_stretcher->m_sampleRate << ", options = " << options << endl; } @@ -321,31 +325,62 @@ RubberBandStretcher::Impl::calculateSizes() if (m_realtime) { - // use a fixed input increment - - inputIncrement = roundUp(int(m_defaultIncrement * m_rateMultiple)); - 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)); - if (outputIncrement < 1) { - outputIncrement = 1; - inputIncrement = roundUp(lrint(ceil(outputIncrement / r))); - windowSize = inputIncrement * 4; + + // 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 { - outputIncrement = int(ceil(inputIncrement * r)); - while (outputIncrement > 1024 && inputIncrement > 1) { - inputIncrement /= 2; - outputIncrement = lrint(ceil(inputIncrement * r)); + + 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; } - windowSize = std::max(windowSize, roundUp(outputIncrement * 6)); - if (r > 5) while (windowSize < 8192) windowSize *= 2; } } else { - // use a variable increment - if (r < 1) { inputIncrement = windowSize / 4; while (inputIncrement >= 512) inputIncrement /= 2; @@ -365,7 +400,7 @@ RubberBandStretcher::Impl::calculateSizes() windowSize = std::max(windowSize, roundUp(outputIncrement * 6)); if (r > 5) while (windowSize < 8192) windowSize *= 2; } - } + } if (m_expectedInputDuration > 0) { while (inputIncrement * 4 > m_expectedInputDuration && @@ -450,8 +485,9 @@ RubberBandStretcher::Impl::configure() set 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_baseWindowSize * 4); } windowSizes.insert(m_windowSize); @@ -479,13 +515,13 @@ RubberBandStretcher::Impl::configure() for (size_t c = 0; c < m_channels; ++c) { m_channelData.push_back - (new ChannelData(windowSizes, m_windowSize, m_outbufSize)); + (new ChannelData(windowSizes, 1, m_windowSize, m_outbufSize)); } } if (!m_realtime && windowSizeChanged) { delete m_studyFFT; - m_studyFFT = new FFT(m_windowSize); + m_studyFFT = new FFT(m_windowSize, m_debugLevel); m_studyFFT->initFloat(); } @@ -496,7 +532,8 @@ RubberBandStretcher::Impl::configure() if (m_channelData[c]->resampler) continue; m_channelData[c]->resampler = - new Resampler(Resampler::FastestTolerable, 1, 4096 * 16); + 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 @@ -504,8 +541,7 @@ RubberBandStretcher::Impl::configure() size_t rbs = lrintf(ceil((m_increment * m_timeRatio * 2) / m_pitchScale)); if (rbs < m_increment * 16) rbs = m_increment * 16; - m_channelData[c]->resamplebufSize = rbs; - m_channelData[c]->resamplebuf = new float[rbs]; + m_channelData[c]->setResampleBufSize(rbs); } } @@ -609,12 +645,11 @@ RubberBandStretcher::Impl::reconfigure() std::cerr << "WARNING: reconfigure(): resampler construction required in RT mode" << std::endl; m_channelData[c]->resampler = - new Resampler(Resampler::FastestTolerable, 1, m_windowSize); + new Resampler(Resampler::FastestTolerable, 1, m_windowSize, + m_debugLevel); - m_channelData[c]->resamplebufSize = - lrintf(ceil((m_increment * m_timeRatio * 2) / m_pitchScale)); - m_channelData[c]->resamplebuf = - new float[m_channelData[c]->resamplebufSize]; + m_channelData[c]->setResampleBufSize + (lrintf(ceil((m_increment * m_timeRatio * 2) / m_pitchScale))); } } @@ -637,9 +672,9 @@ RubberBandStretcher::Impl::setTransientsOption(Options options) cerr << "RubberBandStretcher::Impl::setTransientsOption: Not permissible in non-realtime mode" << endl; return; } - m_options &= ~(OptionTransientsMixed | - OptionTransientsSmooth | - OptionTransientsCrisp); + int mask = (OptionTransientsMixed | OptionTransientsSmooth | OptionTransientsCrisp); + m_options &= ~mask; + options &= mask; m_options |= options; m_stretchCalculator->setUseHardPeaks @@ -649,15 +684,44 @@ RubberBandStretcher::Impl::setTransientsOption(Options options) void RubberBandStretcher::Impl::setPhaseOption(Options options) { - m_options &= ~(OptionPhaseAdaptive | - OptionPhasePeakLocked | - OptionPhaseIndependent); + int mask = (OptionPhaseAdaptive | OptionPhasePeakLocked | 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); + 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; @@ -817,6 +881,8 @@ RubberBandStretcher::Impl::getExactTimePoints() const void RubberBandStretcher::Impl::calculateStretch() { + Profiler profiler("RubberBandStretcher::Impl::calculateStretch"); + std::vector increments = m_stretchCalculator->calculate (getEffectiveRatio(), m_inputDuration, @@ -843,6 +909,8 @@ RubberBandStretcher::Impl::setDebugLevel(int 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) { @@ -878,6 +946,8 @@ RubberBandStretcher::Impl::getSamplesRequired() const 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; @@ -935,10 +1005,12 @@ RubberBandStretcher::Impl::process(const float *const *input, size_t samples, bo // 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]); + samples - consumed[c], + final); if (consumed[c] < samples) { allConsumed = false; // cerr << "process: waiting on input consumption for channel " << c << endl; @@ -988,36 +1060,6 @@ RubberBandStretcher::Impl::process(const float *const *input, size_t samples, bo if (final) m_mode = Finished; } -size_t -RubberBandStretcher::Impl::consumeChannel(size_t c, const float *input, size_t samples) -{ - size_t consumed = 0; - - ChannelData &cd = *m_channelData[c]; - RingBuffer &inbuf = *cd.inbuf; - - while (consumed < samples) { - - size_t writable = inbuf.getWriteSpace(); - -// cerr << "channel " << c << ": samples remaining = " << samples - consumed << ", writable space = " << writable << endl; - - writable = min(writable, samples - consumed); - - if (writable == 0) { - // warn -// cerr << "WARNING: writable == 0 for ch " << c << " (consumed = " << consumed << ", samples = " << samples << ")" << endl; - return consumed; - } else { - inbuf.write(input + consumed, writable); - consumed += writable; - cd.inCount += writable; - } - } - - return samples; -} - } diff --git a/src/StretcherImpl.h b/src/StretcherImpl.h index 0dec4aa..f743129 100644 --- a/src/StretcherImpl.h +++ b/src/StretcherImpl.h @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + 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 @@ -50,6 +50,8 @@ public: void setTransientsOption(Options); void setPhaseOption(Options); + void setFormantOption(Options); + void setPitchOption(Options); void setExpectedInputDuration(size_t samples); void setMaxProcessSize(size_t samples); @@ -86,7 +88,8 @@ protected: RubberBandStretcher *m_stretcher; size_t m_channels; - size_t consumeChannel(size_t channel, const float *input, size_t samples); + 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, @@ -98,6 +101,7 @@ protected: 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); @@ -109,6 +113,8 @@ protected: size_t roundUp(size_t value); // to next power of two + bool resampleBeforeStretching() const; + double m_timeRatio; double m_pitchScale; diff --git a/src/StretcherProcess.cpp b/src/StretcherProcess.cpp index f1e5290..eb7bcc6 100644 --- a/src/StretcherProcess.cpp +++ b/src/StretcherProcess.cpp @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + 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 @@ -19,11 +19,14 @@ #include "StretchCalculator.h" #include "StretcherChannelData.h" #include "Resampler.h" +#include "Profiler.h" #include #include #include #include +#include + using std::cerr; using std::endl; @@ -97,9 +100,82 @@ 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 { + 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 &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. @@ -140,6 +216,8 @@ RubberBandStretcher::Impl::processChunks(size_t c, bool &any, bool &last) 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. @@ -173,6 +251,8 @@ RubberBandStretcher::Impl::processOneChunk() bool RubberBandStretcher::Impl::testInbufReadSpace(size_t c) { + Profiler profiler("RubberBandStretcher::Impl::testInbufReadSpace"); + ChannelData &cd = *m_channelData[c]; RingBuffer &inbuf = *cd.inbuf; @@ -223,6 +303,8 @@ RubberBandStretcher::Impl::processChunkForChannel(size_t c, 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 @@ -284,7 +366,9 @@ RubberBandStretcher::Impl::processChunkForChannel(size_t c, } if (m_threaded) { - size_t required = shiftIncrement; + + int required = shiftIncrement; + if (m_pitchScale != 1.0) { required = int(required / m_pitchScale) + 1; } @@ -313,6 +397,8 @@ 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 @@ -342,6 +428,8 @@ RubberBandStretcher::Impl::calculateIncrements(size_t &phaseIncrementRtn, } } + 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 @@ -352,22 +440,30 @@ RubberBandStretcher::Impl::calculateIncrements(size_t &phaseIncrementRtn, // phases to cancel each other, and broadband effects will still // be apparent. - for (size_t i = 0; i <= m_windowSize/2; ++i) { - cd.fltbuf[i] = 0.0; - } + float df = 0.f; - for (size_t c = 0; c < m_channels; ++c) { - for (size_t i = 0; i <= m_windowSize/2; ++i) { - cd.fltbuf[i] += m_channelData[c]->mag[i]; + if (m_channels == 1) { + + df = m_phaseResetAudioCurve->process(cd.mag, m_increment); + + } 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]; + } } - } - float df = m_phaseResetAudioCurve->process(cd.fltbuf, m_increment); + df = m_phaseResetAudioCurve->process(tmp, m_increment); + } int incr = m_stretchCalculator->calculateSingle - (getEffectiveRatio(), - m_inputDuration, //!!! no, totally wrong... fortunately it doesn't matter atm - df); + (getEffectiveRatio(), df, m_increment); m_lastProcessPhaseResetDf.write(&df, 1); m_lastProcessOutputIncrements.write(&incr, 1); @@ -407,6 +503,8 @@ RubberBandStretcher::Impl::getIncrements(size_t channel, size_t &shiftIncrementRtn, bool &phaseReset) { + Profiler profiler("RubberBandStretcher::Impl::getIncrements"); + if (channel >= m_channels) { phaseIncrementRtn = m_increment; shiftIncrementRtn = m_increment; @@ -478,39 +576,78 @@ RubberBandStretcher::Impl::getIncrements(size_t channel, void RubberBandStretcher::Impl::analyseChunk(size_t channel) { - size_t i; + 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(cd.fltbuf); + m_window->cut(fltbuf); - for (i = 0; i < m_windowSize/2; ++i) { - cd.dblbuf[i] = cd.fltbuf[i + m_windowSize/2]; - cd.dblbuf[i + m_windowSize/2] = cd.fltbuf[i]; + 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(cd.dblbuf, cd.mag, cd.phase); + cd.fft->forwardPolar(dblbuf, cd.mag, cd.phase); } -double mod(double x, double y) { return x - (y * floor(x / y)); } -double princarg(double a) { return mod(a + M_PI, -2 * M_PI) + M_PI; } +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; } - size_t count = m_windowSize/2; - size_t pfp = 0; + int pfp = 0; double rate = m_stretcher->m_sampleRate; + int sz = m_windowSize; + + int count = (sz * cd.oversample) / 2; + + bool unchanged = cd.unchanged && (outputIncrement == m_increment); + bool fullReset = phaseReset; + if (!(m_options & OptionPhaseIndependent)) { cd.freqPeak[0] = 0; @@ -539,11 +676,11 @@ RubberBandStretcher::Impl::modifyChunk(size_t channel, size_t outputIncrement, } } - size_t limit0 = lrint((freq0 * m_windowSize) / rate); - size_t limit1 = lrint((freq1 * m_windowSize) / rate); - size_t limit2 = lrint((freq2 * m_windowSize) / rate); + int limit0 = lrint((freq0 * sz * cd.oversample) / rate); + int limit1 = lrint((freq1 * sz * cd.oversample) / rate); + int limit2 = lrint((freq2 * sz * cd.oversample) / rate); - size_t range = 0; + int range = 0; if (limit1 < limit0) limit1 = limit0; if (limit2 < limit1) limit2 = limit1; @@ -552,12 +689,12 @@ RubberBandStretcher::Impl::modifyChunk(size_t channel, size_t outputIncrement, int peakCount = 0; - for (size_t i = 0; i <= count; ++i) { + for (int i = 0; i <= count; ++i) { double mag = cd.mag[i]; bool isPeak = true; - for (size_t j = 1; j <= range; ++j) { + for (int j = 1; j <= range; ++j) { if (mag < cd.mag[i-j]) { isPeak = false; @@ -578,13 +715,13 @@ RubberBandStretcher::Impl::modifyChunk(size_t channel, size_t outputIncrement, // from pfp to half-way between pfp and i point at pfp, and // those from the half-way mark to i point at i. - size_t halfway = (pfp + i) / 2; + int halfway = (pfp + i) / 2; if (halfway == pfp) halfway = pfp + 1; - for (size_t j = pfp + 1; j < halfway; ++j) { + for (int j = pfp + 1; j < halfway; ++j) { cd.freqPeak[j] = pfp; } - for (size_t j = halfway; j <= i; ++j) { + for (int j = halfway; j <= i; ++j) { cd.freqPeak[j] = i; } @@ -607,27 +744,33 @@ RubberBandStretcher::Impl::modifyChunk(size_t channel, size_t outputIncrement, cd.freqPeak[count] = count; } + Profiler profiler2("RubberBandStretcher::Impl::modifyChunk part 2"); + double peakInPhase = 0.0; double peakOutPhase = 0.0; int p = -1, pp = -1; - for (size_t i = 0; i <= count; ++i) { + + for (int i = 0; i <= count; ++i) { if (m_options & OptionPhaseIndependent) { p = i; - pp = int(i)-1; + pp = i-1; } else { p = cd.freqPeak[i]; - if (i > 0) pp = int(cd.freqPeak[i-1]); + if (i > 0) pp = cd.freqPeak[i-1]; } bool resetThis = phaseReset; if (m_options & OptionTransientsMixed) { - size_t low = lrint((150 * m_windowSize) / rate); - size_t high = lrint((1000 * m_windowSize) / rate); + int low = lrint((150 * sz * cd.oversample) / rate); + int high = lrint((1000 * sz * cd.oversample) / rate); if (resetThis) { - if (i > low && i < high) resetThis = false; + if (i > low && i < high) { + resetThis = false; + fullReset = false; + } } } @@ -635,7 +778,9 @@ RubberBandStretcher::Impl::modifyChunk(size_t channel, size_t outputIncrement, if (i == 0 || p != pp) { - double omega = (2 * M_PI * m_increment * p) / m_windowSize; + + double omega = (2 * M_PI * m_increment * p) / + (sz * cd.oversample); double expectedPhase = cd.prevPhase[p] + omega; double phaseError = princarg(cd.phase[p] - expectedPhase); double phaseIncrement = (omega + phaseError) / m_increment; @@ -649,10 +794,13 @@ RubberBandStretcher::Impl::modifyChunk(size_t channel, size_t outputIncrement, peakInPhase = cd.prevPhase[p]; peakOutPhase = unwrappedPhase; + } if (i != p) { +// cd.phase[i] = atan2(cd.imag[i], cd.real[i]);//!!! + double diffToPeak = peakInPhase - cd.phase[i]; double unwrappedPhase = peakOutPhase - diffToPeak; @@ -662,57 +810,186 @@ RubberBandStretcher::Impl::modifyChunk(size_t channel, size_t outputIncrement, } } else { + // resetThis == true cd.prevPhase[i] = cd.phase[i]; cd.unwrappedPhase[i] = cd.phase[i]; } } + + + 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; + + cd.fft->inverseCepstral(mag, dblbuf); + + double denom = sz; + for (int i = 0; i < sz; ++i) { + dblbuf[i] /= denom; + } + + //!!! calculate this value -- the divisor should be the highest fundamental frequency we expect to find, plus a bit + const int cutoff = m_stretcher->m_sampleRate / 700; + + 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 > 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]; - cd.fft->inversePolar(cd.mag, cd.phase, cd.dblbuf); + 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; - for (size_t i = 0; i < m_windowSize/2; ++i) { - cd.fltbuf[i] = cd.dblbuf[i + m_windowSize/2]; - cd.fltbuf[i + m_windowSize/2] = cd.dblbuf[i]; + + 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] / float(sz * cd.oversample); + } +// } else { +// cerr << "unchanged on channel " << channel << endl; } - // our ffts produced unscaled results - for (size_t i = 0; i < m_windowSize; ++i) { - cd.fltbuf[i] = cd.fltbuf[i] / m_windowSize; - } + m_window->cut(fltbuf); - m_window->cut(cd.fltbuf); - - for (size_t i = 0; i < m_windowSize; ++i) { - cd.accumulator[i] += cd.fltbuf[i]; + for (i = 0; i < sz; ++i) { + accumulator[i] += fltbuf[i]; } cd.accumulatorFill = m_windowSize; - float fixed = m_window->getArea() * 1.5; + float fixed = m_window->getArea() * 1.5f; - for (size_t i = 0; i < m_windowSize; ++i) { + for (i = 0; i < sz; ++i) { float val = m_window->getValue(i); - cd.windowAccumulator[i] += val * fixed; + windowAccumulator[i] += val * fixed; } } void RubberBandStretcher::Impl::writeChunk(size_t channel, size_t shiftIncrement, bool last) { - ChannelData &cd = *m_channelData[channel]; + 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 (int i = 0; i < shiftIncrement; ++i) { - if (cd.windowAccumulator[i] > 0.f) { - cd.accumulator[i] /= cd.windowAccumulator[i]; + for (i = 0; i < si; ++i) { + if (windowAccumulator[i] > 0.f) { + accumulator[i] /= windowAccumulator[i]; } } @@ -723,9 +1000,11 @@ RubberBandStretcher::Impl::writeChunk(size_t channel, size_t shiftIncrement, boo theoreticalOut = lrint(cd.inputSize * m_timeRatio); } - if (m_pitchScale != 1.0 && cd.resampler) { + bool resampledAlready = resampleBeforeStretching(); - size_t reqSize = int(ceil(shiftIncrement / m_pitchScale)); + if (!resampledAlready && m_pitchScale != 1.0 && 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 @@ -734,15 +1013,13 @@ RubberBandStretcher::Impl::writeChunk(size_t channel, size_t shiftIncrement, boo // calculator has gone mad, or something. cerr << "WARNING: RubberBandStretcher::Impl::writeChunk: resizing resampler buffer from " << cd.resamplebufSize << " to " << reqSize << endl; - cd.resamplebufSize = reqSize; - if (cd.resamplebuf) delete[] cd.resamplebuf; - cd.resamplebuf = new float[cd.resamplebufSize]; + cd.setResampleBufSize(reqSize); } size_t outframes = cd.resampler->resample(&cd.accumulator, &cd.resamplebuf, - shiftIncrement, + si, 1.0 / m_pitchScale, last); @@ -751,28 +1028,28 @@ RubberBandStretcher::Impl::writeChunk(size_t channel, size_t shiftIncrement, boo outframes, cd.outCount, theoreticalOut); } else { - writeOutput(*cd.outbuf, cd.accumulator, - shiftIncrement, cd.outCount, theoreticalOut); + writeOutput(*cd.outbuf, accumulator, + si, cd.outCount, theoreticalOut); } - for (size_t i = 0; i < m_windowSize - shiftIncrement; ++i) { - cd.accumulator[i] = cd.accumulator[i + shiftIncrement]; + for (i = 0; i < sz - si; ++i) { + accumulator[i] = accumulator[i + si]; } - for (size_t i = m_windowSize - shiftIncrement; i < m_windowSize; ++i) { - cd.accumulator[i] = 0.0f; + for (i = sz - si; i < sz; ++i) { + accumulator[i] = 0.0f; } - for (size_t i = 0; i < m_windowSize - shiftIncrement; ++i) { - cd.windowAccumulator[i] = cd.windowAccumulator[i + shiftIncrement]; + for (i = 0; i < sz - si; ++i) { + windowAccumulator[i] = windowAccumulator[i + si]; } - for (size_t i = m_windowSize - shiftIncrement; i < m_windowSize; ++i) { - cd.windowAccumulator[i] = 0.0f; + for (i = sz - si; i < sz; ++i) { + windowAccumulator[i] = 0.0f; } - if (cd.accumulatorFill > shiftIncrement) { - cd.accumulatorFill -= shiftIncrement; + if (cd.accumulatorFill > si) { + cd.accumulatorFill -= si; } else { cd.accumulatorFill = 0; if (cd.draining) { @@ -787,6 +1064,8 @@ RubberBandStretcher::Impl::writeChunk(size_t channel, size_t shiftIncrement, boo void RubberBandStretcher::Impl::writeOutput(RingBuffer &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 @@ -859,6 +1138,8 @@ RubberBandStretcher::Impl::writeOutput(RingBuffer &to, float *from, size_ int RubberBandStretcher::Impl::available() const { + Profiler profiler("RubberBandStretcher::Impl::available"); + if (m_threaded) { MutexLocker locker(&m_threadSetMutex); if (m_channelData.empty()) return 0; @@ -906,6 +1187,8 @@ RubberBandStretcher::Impl::available() const 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) { diff --git a/src/Thread.cpp b/src/Thread.cpp index 2b37875..83857ea 100644 --- a/src/Thread.cpp +++ b/src/Thread.cpp @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + 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 @@ -15,14 +15,11 @@ #include "Thread.h" #include +#include #include #include -//#define DEBUG_THREAD 1 -//#define DEBUG_MUTEX 1 -//#define DEBUG_CONDITION 1 - using std::cerr; using std::endl; using std::string; @@ -107,8 +104,11 @@ Thread::staticRun(LPVOID arg) return 0; } -Mutex::Mutex() : - m_locked(false) +Mutex::Mutex() +#ifndef NO_THREAD_CHECKS + : + m_lockedBy(-1) +#endif { m_mutex = CreateMutex(NULL, FALSE, NULL); #ifdef DEBUG_MUTEX @@ -127,50 +127,71 @@ Mutex::~Mutex() void Mutex::lock() { - if (m_locked) { +#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 *)GetCurrentThreadId() << ": Want to lock mutex " << &m_mutex << endl; + cerr << "MUTEX DEBUG: " << (void *)tid << ": Want to lock mutex " << &m_mutex << endl; #endif WaitForSingleObject(m_mutex, INFINITE); - m_locked = true; +#ifndef NO_THREAD_CHECKS + m_lockedBy = tid; +#endif #ifdef DEBUG_MUTEX - cerr << "MUTEX DEBUG: " << (void *)GetCurrentThreadId() << ": Locked mutex " << &m_mutex << endl; + cerr << "MUTEX DEBUG: " << (void *)tid << ": Locked mutex " << &m_mutex << endl; #endif } void Mutex::unlock() { -#ifdef DEBUG_MUTEX - cerr << "MUTEX DEBUG: " << (void *)GetCurrentThreadId() << ": Unlocking mutex " << &m_mutex << endl; +#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 - m_locked = false; 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 *)GetCurrentThreadId() << ": Mutex " << &m_mutex << " unavailable" << endl; + cerr << "MUTEX DEBUG: " << (void *)tid << ": Mutex " << &m_mutex << " unavailable" << endl; #endif return false; } else { - m_locked = true; +#ifndef NO_THREAD_CHECKS + m_lockedBy = tid; +#endif #ifdef DEBUG_MUTEX - cerr << "MUTEX DEBUG: " << (void *)GetCurrentThreadId() << ": Locked mutex " << &m_mutex << " (from trylock)" << endl; + cerr << "MUTEX DEBUG: " << (void *)tid << ": Locked mutex " << &m_mutex << " (from trylock)" << endl; #endif return true; } } Condition::Condition(string name) : - m_name(name), m_locked(false) +#ifdef DEBUG_CONDITION + , m_name(name) +#endif { m_mutex = CreateMutex(NULL, FALSE, NULL); m_condition = CreateEvent(NULL, FALSE, FALSE, NULL); @@ -343,8 +364,12 @@ Thread::staticRun(void *arg) return 0; } -Mutex::Mutex() : +Mutex::Mutex() +#ifndef NO_THREAD_CHECKS + : + m_lockedBy(0), m_locked(false) +#endif { pthread_mutex_init(&m_mutex, 0); #ifdef DEBUG_MUTEX @@ -363,49 +388,75 @@ Mutex::~Mutex() void Mutex::lock() { - if (m_locked) { +#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 *)pthread_self() << ": Want to lock mutex " << &m_mutex << endl; + 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 *)pthread_self() << ": Locked mutex " << &m_mutex << endl; + cerr << "MUTEX DEBUG: " << (void *)tid << ": Locked mutex " << &m_mutex << endl; #endif } void Mutex::unlock() { -#ifdef DEBUG_MUTEX - cerr << "MUTEX DEBUG: " << (void *)pthread_self() << ": Unlocking mutex " << &m_mutex << endl; +#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 *)pthread_self() << ": Mutex " << &m_mutex << " unavailable" << endl; + 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 *)pthread_self() << ": Locked mutex " << &m_mutex << " (from trylock)" << endl; + cerr << "MUTEX DEBUG: " << (void *)tid << ": Locked mutex " << &m_mutex << " (from trylock)" << endl; #endif return true; } } Condition::Condition(string name) : - m_locked(false), - m_name(name) + m_locked(false) +#ifdef DEBUG_CONDITION + , m_name(name) +#endif { pthread_mutex_init(&m_mutex, 0); pthread_cond_init(&m_condition, 0); diff --git a/src/Thread.h b/src/Thread.h index dc37f6d..0614692 100644 --- a/src/Thread.h +++ b/src/Thread.h @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + 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 @@ -23,6 +23,10 @@ #include +//#define DEBUG_THREAD 1 +//#define DEBUG_MUTEX 1 +//#define DEBUG_CONDITION 1 + namespace RubberBand { @@ -73,11 +77,16 @@ public: private: #ifdef _WIN32 HANDLE m_mutex; - bool m_locked; +#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 @@ -113,15 +122,17 @@ public: void signal(); private: + #ifdef _WIN32 HANDLE m_mutex; - bool m_locked; HANDLE m_condition; - std::string m_name; + bool m_locked; #else pthread_mutex_t m_mutex; - bool m_locked; pthread_cond_t m_condition; + bool m_locked; +#endif +#ifdef DEBUG_CONDITION std::string m_name; #endif }; diff --git a/src/Window.cpp b/src/Window.cpp new file mode 100644 index 0000000..106faa7 --- /dev/null +++ b/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/src/Window.h b/src/Window.h index 305daa7..6e88083 100644 --- a/src/Window.h +++ b/src/Window.h @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + 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 @@ -17,8 +17,11 @@ #include #include +#include #include +#include "sysutils.h" + namespace RubberBand { enum WindowType { @@ -40,7 +43,7 @@ public: /** * Construct a windower of the given type. */ - Window(WindowType type, size_t size) : m_type(type), m_size(size) { encache(); } + 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; @@ -51,21 +54,34 @@ public: } virtual ~Window() { delete[] m_cache; } - void cut(T *src) const { cut(src, src); } - void cut(T *src, T *dst) const { - for (size_t i = 0; i < m_size; ++i) dst[i] = src[i] * m_cache[i]; + 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(size_t i) { return m_cache[i]; } + T getValue(int i) { return m_cache[i]; } WindowType getType() const { return m_type; } - size_t getSize() const { return m_size; } + int getSize() const { return m_size; } protected: WindowType m_type; - size_t m_size; - T *m_cache; + int m_size; + T *R__ m_cache; T m_area; void encache(); diff --git a/src/bsd-3rdparty/float_cast/float_cast.h b/src/bsd-3rdparty/float_cast/float_cast.h new file mode 100644 index 0000000..1ba0e03 --- /dev/null +++ b/src/bsd-3rdparty/float_cast/float_cast.h @@ -0,0 +1,73 @@ +/* +** Copyright (C) 2001 Erik de Castro Lopo +** +** 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 + + /* 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/src/bsd-3rdparty/getopt/getopt.c b/src/bsd-3rdparty/getopt/getopt.c new file mode 100644 index 0000000..4cfe746 --- /dev/null +++ b/src/bsd-3rdparty/getopt/getopt.c @@ -0,0 +1,122 @@ +/* + * 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. + */ + +/*#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)getopt.c 8.3 (Berkeley) 4/27/95"; +#endif /* LIBC_SCCS and not lint +#include +//__FBSDID("$FreeBSD: src/lib/libc/stdlib/getopt.c,v 1.6 2002/03/29 22:43:42 markm Exp $"); + +#include "namespace.h"*/ +#include +#include +#include +/*#include "un-namespace.h"*/ + +/*#include "libc_private.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/src/bsd-3rdparty/getopt/getopt.h b/src/bsd-3rdparty/getopt/getopt.h new file mode 100644 index 0000000..d95d6cf --- /dev/null +++ b/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 */ +# 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 +#include +#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/src/bsd-3rdparty/getopt/getopt_long.c b/src/bsd-3rdparty/getopt/getopt_long.c new file mode 100644 index 0000000..1f92449 --- /dev/null +++ b/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 +#include + +#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 +#include + +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/src/bsd-3rdparty/getopt/unistd.h b/src/bsd-3rdparty/getopt/unistd.h new file mode 100644 index 0000000..e69de29 diff --git a/src/ladspa/RubberBandPitchShifter.cpp b/src/ladspa/RubberBandPitchShifter.cpp index ca439a4..f85022b 100644 --- a/src/ladspa/RubberBandPitchShifter.cpp +++ b/src/ladspa/RubberBandPitchShifter.cpp @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + 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 @@ -55,6 +55,7 @@ RubberBandPitchShifter::portsMono[PortCountMono] = 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 }; @@ -67,6 +68,7 @@ RubberBandPitchShifter::portsStereo[PortCountStereo] = 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, @@ -96,6 +98,11 @@ RubberBandPitchShifter::hintsMono[PortCountMono] = LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_INTEGER, 0.0, 3.0 }, + { LADSPA_HINT_DEFAULT_0 | + LADSPA_HINT_BOUNDED_BELOW | + LADSPA_HINT_BOUNDED_ABOVE | + LADSPA_HINT_TOGGLED, + 0.0, 1.0 }, { 0, 0, 0 }, { 0, 0, 0 } }; @@ -123,6 +130,11 @@ RubberBandPitchShifter::hintsStereo[PortCountStereo] = LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_INTEGER, 0.0, 3.0 }, + { LADSPA_HINT_DEFAULT_0 | + LADSPA_HINT_BOUNDED_BELOW | + LADSPA_HINT_BOUNDED_ABOVE | + LADSPA_HINT_TOGGLED, + 0.0, 1.0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, @@ -247,7 +259,7 @@ RubberBandPitchShifter::connectPort(LADSPA_Handle handle, &shifter->m_semitones, &shifter->m_octaves, &shifter->m_crispness, - &shifter->m_input[0], + &shifter->m_input[0], &shifter->m_output[0], &shifter->m_input[1], &shifter->m_output[1] @@ -326,7 +338,7 @@ RubberBandPitchShifter::runImpl(unsigned long insamples) } if (m_latency) { - *m_latency = m_stretcher->getLatency() + m_extraLatency; + *m_latency = float(m_stretcher->getLatency() + m_extraLatency); // std::cerr << "latency = " << *m_latency << std::endl; } diff --git a/src/ladspa/RubberBandPitchShifter.h b/src/ladspa/RubberBandPitchShifter.h index 3adfb61..520b14b 100644 --- a/src/ladspa/RubberBandPitchShifter.h +++ b/src/ladspa/RubberBandPitchShifter.h @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + 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 @@ -15,7 +15,7 @@ #ifndef _RUBBERBAND_PITCH_SHIFTER_H_ #define _RUBBERBAND_PITCH_SHIFTER_H_ -#include "ladspa.h" +#include #include "RingBuffer.h" @@ -38,11 +38,11 @@ protected: SemitonesPort = 2, CentsPort = 3, CrispnessPort = 4, - InputPort1 = 5, - OutputPort1 = 6, + InputPort1 = 6, + OutputPort1 = 7, PortCountMono = OutputPort1 + 1, - InputPort2 = 7, - OutputPort2 = 8, + InputPort2 = 8, + OutputPort2 = 9, PortCountStereo = OutputPort2 + 1 }; diff --git a/src/ladspa/libmain.cpp b/src/ladspa/libmain.cpp index afc7ac0..d949e81 100644 --- a/src/ladspa/libmain.cpp +++ b/src/ladspa/libmain.cpp @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + 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 diff --git a/src/main.cpp b/src/main.cpp index db5a218..78d645b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + 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 @@ -17,14 +17,19 @@ #include #include #include -#include #include +#include +#include #include "sysutils.h" +#ifdef _WIN32 +#include "bsd-3rdparty/getopt/getopt.h" +#else #include +#include +#endif -// for import and export of FFTW wisdom -#include +#include "Profiler.h" using namespace std; using namespace RubberBand; @@ -48,7 +53,9 @@ int main(int argc, char **argv) bool peaklock = true; bool longwin = false; bool shortwin = false; + bool hqpitch = false; bool softening = true; + bool formant = false; int crispness = -1; bool help = false; bool quiet = false; @@ -79,6 +86,7 @@ int main(int argc, char **argv) { "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-peaklock", 0, 0, '2' }, @@ -89,12 +97,13 @@ int main(int argc, char **argv) { "thresh2", 1, 0, '7' }, { "bl-transients", 0, 0, '8' }, { "no-softening", 0, 0, '9' }, + { "pitch-hq", 0, 0, '%' }, { "threads", 0, 0, '@' }, { "quiet", 0, 0, 'q' }, { 0, 0, 0 } }; - c = getopt_long(argc, argv, "t:p:d:RPc:f:T:qh", longOpts, &optionIndex); + c = getopt_long(argc, argv, "t:p:d:RPFc:f:T:qh", longOpts, &optionIndex); if (c == -1) break; switch (c) { @@ -106,6 +115,7 @@ int main(int argc, char **argv) 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; break; @@ -117,6 +127,7 @@ int main(int argc, char **argv) case '7': fthresh2 = atof(optarg); break; case '8': transients = BandLimitedTransients; break; case '9': softening = false; break; + case '%': hqpitch = true; break; case 'c': crispness = atoi(optarg); break; case 'q': quiet = true; break; default: help = true; break; @@ -127,7 +138,7 @@ int main(int argc, char **argv) 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 << "Copyright 2008 Chris Cannam. Distributed under the GNU General Public License." << endl; cerr << endl; cerr << " Usage: " << argv[0] << " [options] " << endl; cerr << endl; @@ -139,15 +150,16 @@ int main(int argc, char **argv) cerr << " -p, --pitch Raise pitch by X semitones, or" << endl; cerr << " -f, --frequency Change frequency by multiple X" << endl; cerr << endl; - cerr << "The following option provides a simple way to adjust the sound. See below" << 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, --crisp 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." << 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; @@ -159,6 +171,7 @@ int main(int argc, char **argv) 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 << " --pitch-hq In RT mode, use a slower, higher quality pitch shift" << endl; cerr << " --thresh Set internal freq threshold N (N = 0,1,2) to F Hz" << endl; cerr << endl; cerr << " -d, --debug Select debug level (N = 0,1,2,3); default 0, full 3" << endl; @@ -203,7 +216,7 @@ int main(int argc, char **argv) char *fileName = strdup(argv[optind++]); char *fileNameOut = strdup(argv[optind++]); - + SNDFILE *sndfile; SNDFILE *sndfileOut; SF_INFO sfinfo; @@ -226,8 +239,8 @@ int main(int argc, char **argv) sndfileOut = sf_open(fileNameOut, SFM_WRITE, &sfinfoOut) ; if (!sndfileOut) { - cerr << "ERROR: Failed to open output file \"" << fileName << "\" for writing: " - << sf_strerror(sndfile) << endl; + cerr << "ERROR: Failed to open output file \"" << fileNameOut << "\" for writing: " + << sf_strerror(sndfileOut) << endl; return 1; } @@ -241,6 +254,8 @@ int main(int argc, char **argv) if (!softening) options |= RubberBandStretcher::OptionPhasePeakLocked; if (longwin) options |= RubberBandStretcher::OptionWindowLong; if (shortwin) options |= RubberBandStretcher::OptionWindowShort; + if (formant) options |= RubberBandStretcher::OptionFormantPreserved; + if (hqpitch) options |= RubberBandStretcher::OptionPitchHighQuality; switch (threading) { case 0: @@ -474,6 +489,8 @@ int main(int argc, char **argv) cerr << "elapsed time: " << sec << " sec, in frames/sec: " << countIn/sec << ", out frames/sec: " << countOut/sec << endl; } + Profiler::dump(); + return 0; } diff --git a/src/sysutils.cpp b/src/sysutils.cpp index fc4a17b..ba91d16 100644 --- a/src/sysutils.cpp +++ b/src/sysutils.cpp @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + 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 @@ -81,7 +81,7 @@ system_is_multiprocessor() #ifdef _WIN32 -void gettimeofday(struct timeval *tv, void *tz) +int gettimeofday(struct timeval *tv, void *tz) { union { long long ns100; @@ -91,6 +91,7 @@ void gettimeofday(struct timeval *tv, void *tz) ::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) diff --git a/src/sysutils.h b/src/sysutils.h index b9dd23e..f7a1bfe 100644 --- a/src/sysutils.h +++ b/src/sysutils.h @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + 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 @@ -15,14 +15,29 @@ #ifndef _RUBBERBAND_SYSINFO_H_ #define _RUBBERBAND_SYSINFO_H_ +#ifdef _WIN32 +#include "bsd-3rdparty/float_cast/float_cast.h" +#endif + namespace RubberBand { extern bool system_is_multiprocessor(); #ifdef _WIN32 + +#define R__ __restrict + struct timeval { long tv_sec; long tv_usec; }; -void gettimeofday(struct timeval *p, void *tz); +int gettimeofday(struct timeval *p, void *tz); + void usleep(unsigned long); + +#define alloca _alloca + +#else + +#define R__ __restrict__ + #endif } diff --git a/src/vamp/RubberBandVampPlugin.cpp b/src/vamp/RubberBandVampPlugin.cpp index 1e9227f..9f15843 100644 --- a/src/vamp/RubberBandVampPlugin.cpp +++ b/src/vamp/RubberBandVampPlugin.cpp @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + 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 @@ -18,6 +18,10 @@ #include +#ifdef _WIN32 +#include "bsd-3rdparty/float_cast/float_cast.h" +#endif + using std::string; using std::vector; using std::cerr; @@ -159,7 +163,7 @@ RubberBandVampPlugin::getOutputDescriptors() const d.isQuantized = true; d.quantizeStep = 1.0; d.sampleType = OutputDescriptor::VariableSampleRate; - d.sampleRate = rate; + d.sampleRate = float(rate); m_d->m_incrementsOutput = list.size(); list.push_back(d); @@ -182,7 +186,7 @@ RubberBandVampPlugin::getOutputDescriptors() const d.name = "Phase Reset Detection Function"; d.description = "Curve whose peaks are used to identify transients for phase reset points"; d.unit = ""; - d.sampleRate = rate; + d.sampleRate = float(rate); m_d->m_phaseResetDfOutput = list.size(); list.push_back(d); @@ -326,11 +330,11 @@ RubberBandVampPlugin::getParameter(std::string id) const { if (id == "timeratio") return m_d->m_timeRatio * 100.f; if (id == "pitchratio") return m_d->m_pitchRatio * 100.f; - if (id == "mode") return m_d->m_realtime ? 1 : 0; - if (id == "stretchtype") return m_d->m_elasticTiming ? 0 : 1; - if (id == "transientmode") return m_d->m_transientMode; - if (id == "phasemode") return m_d->m_phaseIndependent ? 1 : 0; - if (id == "windowmode") return m_d->m_windowLength; + 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; } @@ -565,12 +569,12 @@ RubberBandVampPlugin::Impl::createFeatures(size_t inputIncrement, Feature feature; feature.hasTimestamp = true; feature.timestamp = t; - feature.values.push_back(oi); + 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(actual); + feature.values.push_back(float(actual)); feature.label = Vamp::RealTime::frame2RealTime(actual, rate).toText(); features[m_aggregateIncrementsOutput].push_back(feature); @@ -594,7 +598,7 @@ RubberBandVampPlugin::Impl::createFeatures(size_t inputIncrement, if (i < phaseResetDf.size()) { feature.values.clear(); feature.values.push_back(phaseResetDf[i]); - sprintf(buf, "%d", baseCount + i); + sprintf(buf, "%d", int(baseCount + i)); feature.label = buf; features[m_phaseResetDfOutput].push_back(feature); } @@ -626,7 +630,7 @@ RubberBandVampPlugin::Impl::createFeatures(size_t inputIncrement, feature.timestamp = t; feature.label = Vamp::RealTime::frame2RealTime(actual, rate).toText(); feature.values.clear(); - feature.values.push_back(actual); + feature.values.push_back(float(actual)); features[m_aggregateIncrementsOutput].push_back(feature); float linear = ((baseCount + outputIncrements.size()) diff --git a/src/vamp/RubberBandVampPlugin.h b/src/vamp/RubberBandVampPlugin.h index f850a28..f062e35 100644 --- a/src/vamp/RubberBandVampPlugin.h +++ b/src/vamp/RubberBandVampPlugin.h @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + 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 diff --git a/src/vamp/libmain.cpp b/src/vamp/libmain.cpp index a535c20..1b41851 100644 --- a/src/vamp/libmain.cpp +++ b/src/vamp/libmain.cpp @@ -3,7 +3,7 @@ /* Rubber Band An audio time-stretching and pitch-shifting library. - Copyright 2007 Chris Cannam. + 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