From 5460b5c668988c92fc2647e306702170fe6cac87 Mon Sep 17 00:00:00 2001 From: "v.audacity" Date: Sat, 3 Nov 2012 02:06:07 +0000 Subject: [PATCH] This commit is partial effort toward more libsoxr integration, that implements Rob Sykes's latest patch. I have no idea whether the Linux-specific stuff from the patch (e.g., M4 file) is still correct in this. For Resample.* and QualityPrefs.cpp, this commit has my restructuring for distinguishing constant-rate vs variable-rate resamplers more generally. I think it's complete and ready for const-rate, but I have more review and testing to do for the var-rate cases. Variable-rate resampling is not implemented here, so Time Tracks are still broken, but this is a milestone in getting to a more general and correct structure. Also I think this fixes AboutDialog issues Steve noticed. --- m4/audacity_checklib_libresample.m4 | 46 +++++ src/AboutDialog.cpp | 21 +- src/AudioIO.cpp | 4 +- src/AudioIO.h | 4 +- src/Mix.cpp | 4 +- src/Resample.cpp | 269 +++++++++++++------------- src/Resample.h | 154 ++++++++------- src/WaveClip.cpp | 2 +- src/effects/ChangeSpeed.cpp | 2 +- src/prefs/QualityPrefs.cpp | 18 +- win/Projects/Audacity/Audacity.vcproj | 4 +- win/audacity.sln | 2 + win/configwin.h | 10 +- 13 files changed, 306 insertions(+), 234 deletions(-) create mode 100644 m4/audacity_checklib_libresample.m4 diff --git a/m4/audacity_checklib_libresample.m4 b/m4/audacity_checklib_libresample.m4 new file mode 100644 index 000000000..bcb187339 --- /dev/null +++ b/m4/audacity_checklib_libresample.m4 @@ -0,0 +1,46 @@ +dnl Add Audacity license? +dnl Please increment the serial number below whenever you alter this macro +dnl for the benefit of automatic macro update systems +# audacity_checklib_libresample.m4 serial 1 + +AC_DEFUN([AUDACITY_CHECKLIB_LIBRESAMPLE], [ + + AC_ARG_WITH(libresample, + [AS_HELP_STRING([--with-libresample], + [use libresample for sample rate conversion: [yes,no]])], + LIBRESAMPLE_ARGUMENT=$withval, + LIBRESAMPLE_ARGUMENT="unspecified") + + if false ; then + AC_DEFINE(USE_LIBRESAMPLE, 1, + [Define if libresample support should be enabled]) + fi + + dnl see if libresample is installed on the system + + dnl ... but libresample isn't generally installed as a system library... + + LIBRESAMPLE_SYSTEM_AVAILABLE="no" + + dnl see if libresample is available locally + + AC_CHECK_FILE(${srcdir}/lib-src/libresample/include/libresample.h, + resample_h_found="yes", + resample_h_found="no") + + if test "x$resample_h_found" = "xyes" ; then + LIBRESAMPLE_LOCAL_AVAILABLE="yes" + LIBRESAMPLE_LOCAL_LIBS="libresample.a" + LIBRESAMPLE_LOCAL_CXXFLAGS='-I$(top_srcdir)/lib-src/libresample/include' + LIBRESAMPLE_LOCAL_CPPSYMBOLS="USE_LIBRESAMPLE" + + if test ! -f lib-src/libresample/Makefile ; then + LIBRESAMPLE_LOCAL_CONFIG_SUBDIRS="lib-src/libresample" + fi + AC_MSG_NOTICE([libresample libraries are available in the local tree]) + else + LIBRESAMPLE_LOCAL_AVAILABLE="no" + AC_MSG_NOTICE([libresample libraries are NOT available in the local tree]) + fi +]) + diff --git a/src/AboutDialog.cpp b/src/AboutDialog.cpp index bbecd274f..1720d1de4 100644 --- a/src/AboutDialog.cpp +++ b/src/AboutDialog.cpp @@ -414,16 +414,19 @@ void AboutDialog::PopulateInformationPage( ShuttleGui & S ) #elif USE_LIBSAMPLERATE AddBuildinfoRow(&informationStr, wxT("libsamplerate"), _("Sample rate conversion"), enabled); - #elif USE_LIBSOXR - AddBuildinfoRow(&informationStr, wxT("libsoxr"), - _("Sample rate conversion"), enabled); #else - AddBuildinfoRow(&informationStr, wxT("libresample"), - _("Sample rate conversion"), disabled); - AddBuildinfoRow(&informationStr, wxT("libsamplerate"), - _("Sample rate conversion"), disabled); - AddBuildinfoRow(&informationStr, wxT("libsoxr"), - _("Sample rate conversion"), disabled); + AddBuildinfoRow(&informationStr, wxT("libresample"), + _("Sample rate conversion"), disabled); + AddBuildinfoRow(&informationStr, wxT("libsamplerate"), + _("Sample rate conversion"), disabled); + #endif + + #if USE_LIBSOXR + AddBuildinfoRow(&informationStr, wxT("libsoxr"), + _("Sample rate conversion"), enabled); + #else + AddBuildinfoRow(&informationStr, wxT("libsoxr"), + _("Sample rate conversion"), disabled); #endif AddBuildinfoRow(&informationStr, wxT("PortAudio"), diff --git a/src/AudioIO.cpp b/src/AudioIO.cpp index 113fc2b80..8453def93 100644 --- a/src/AudioIO.cpp +++ b/src/AudioIO.cpp @@ -1314,7 +1314,7 @@ int AudioIO::StartStream(WaveTrackArray playbackTracks, } mCaptureBuffers = new RingBuffer* [mCaptureTracks.GetCount()]; - mResample = new Resample* [mCaptureTracks.GetCount()]; + mResample = new ConstRateResample* [mCaptureTracks.GetCount()]; mFactor = sampleRate / mRate; // Set everything to zero in case we have to delete these due to a memory exception. @@ -1325,7 +1325,7 @@ int AudioIO::StartStream(WaveTrackArray playbackTracks, { mCaptureBuffers[i] = new RingBuffer( mCaptureTracks[i]->GetSampleFormat(), captureBufferSize ); - mResample[i] = new Resample( true, mFactor, mFactor ); + mResample[i] = new ConstRateResample(true, mFactor); } } } diff --git a/src/AudioIO.h b/src/AudioIO.h index 64f3a7e57..afc068830 100644 --- a/src/AudioIO.h +++ b/src/AudioIO.h @@ -38,7 +38,7 @@ class AudioIO; class RingBuffer; class Mixer; -class Resample; +class ConstRateResample; class TimeTrack; class AudioThread; class Meter; @@ -474,7 +474,7 @@ private: #ifdef EXPERIMENTAL_MIDI_OUT AudioThread *mMidiThread; #endif - Resample **mResample; + ConstRateResample **mResample; RingBuffer **mCaptureBuffers; WaveTrackArray mCaptureTracks; RingBuffer **mPlaybackBuffers; diff --git a/src/Mix.cpp b/src/Mix.cpp index 7b8b2ea27..cb1a7ccc4 100644 --- a/src/Mix.cpp +++ b/src/Mix.cpp @@ -281,7 +281,7 @@ Mixer::Mixer(int numInputTracks, WaveTrack **inputTracks, mQueueStart = new int[mNumInputTracks]; mQueueLen = new int[mNumInputTracks]; mSampleQueue = new float *[mNumInputTracks]; - mSRC = new Resample*[mNumInputTracks]; + mSRC = new Resample*[mNumInputTracks]; //vvvvv for(i=0; iGetRate()); double lowFactor = factor, highFactor = factor; @@ -289,7 +289,7 @@ Mixer::Mixer(int numInputTracks, WaveTrack **inputTracks, highFactor /= timeTrack->GetRangeLower() / 100.0; lowFactor /= timeTrack->GetRangeUpper() / 100.0; } - mSRC[i] = new Resample(highQuality, lowFactor, highFactor); + mSRC[i] = new Resample(); //vvvvv mSampleQueue[i] = new float[mQueueMaxLen]; mQueueStart[i] = 0; mQueueLen[i] = 0; diff --git a/src/Resample.cpp b/src/Resample.cpp index fa037583d..5c3d4e686 100644 --- a/src/Resample.cpp +++ b/src/Resample.cpp @@ -1,54 +1,139 @@ /********************************************************************** - Audacity: A Digital Audio Editor + Audacity: A Digital Audio Editor + Audacity(R) is copyright (c) 1999-2012 Audacity Team. + License: GPL v2. See License.txt. - Dominic Mazzoni + Resample.cpp + Dominic Mazzoni, Rob Sykes, Vaughan Johnson -*******************************************************************//*! +******************************************************************//** \class Resample -\brief Combined interface to libresample and libsamplerate (which libsoxr emulates). +\brief Combined interface to libresample, libsamplerate, and libsoxr. - This class abstracts the interface to two different resampling - libraries: + This class abstracts the interface to two different variable-rate + resampling libraries: - libresample, written by Dominic Mazzoni based on Resample-1.7 - by Julius Smith. LGPL. + libresample, written by Dominic Mazzoni based on Resample-1.7 + by Julius Smith. LGPL. - libsamplerate, written by Erik de Castro Lopo. GPL. The author - of libsamplerate requests that you not distribute a binary version - of Audacity that links to libsamplerate and also has plug-in support. + libsamplerate, written by Erik de Castro Lopo. GPL. The author + of libsamplerate requests that you not distribute a binary version + of Audacity that links to libsamplerate and also has plug-in support. + Since Audacity always does resampling on mono streams that are + contiguous in memory, this class doesn't support multiple channels + or some of the other optional features of some of these resamplers. + + and the fixed-rate resampling library: + + libsoxr, written by Rob Sykes. LGPL. *//*******************************************************************/ #include "Resample.h" -#include "Prefs.h" #include -int Resample::GetFastMethod() -{ - return gPrefs->Read(GetFastMethodKey(), GetFastMethodDefault()); -} +//v Currently unused. +//void Resample::SetFastMethod(int index) +//{ +// gPrefs->Write(GetFastMethodKey(), (long)index); +// gPrefs->Flush(); +//} +// +//void Resample::SetBestMethod(int index) +//{ +// gPrefs->Write(GetBestMethodKey(), (long)index); +// gPrefs->Flush(); +//} -int Resample::GetBestMethod() -{ - return gPrefs->Read(GetBestMethodKey(), GetBestMethodDefault()); -} +// constant-rate resampler(s) +#ifdef USE_LIBSOXR -void Resample::SetFastMethod(int index) -{ - gPrefs->Write(GetFastMethodKey(), (long)index); - gPrefs->Flush(); -} + #include -void Resample::SetBestMethod(int index) -{ - gPrefs->Write(GetBestMethodKey(), (long)index); - gPrefs->Flush(); -} + ConstRateResample::ConstRateResample(const bool useBestMethod, const double dFactor) + : Resample() + { + soxr_quality_spec_t q_spec = soxr_quality_spec("\0\1\4\6"[mMethod], 0); + mHandle = (void *)soxr_create(1, dFactor, 1, 0, 0, &q_spec, 0); + } + + ConstRateResample::~ConstRateResample() + { + delete mHandle; + mHandle = NULL; + soxr_delete((soxr_t)mHandle); + } + + //v Currently unused. + //wxString ConstRateResample::GetResamplingLibraryName() + //{ + // return _("Libsoxr by Rob Sykes"); + //} + + int ConstRateResample::GetNumMethods() { return 4; } + + static char const * const soxr_method_names[] = { + "Quick & dirty", "Basic quality", "High quality", "Very high quality" + }; + + wxString ConstRateResample::GetMethodName(int index) + { + return wxString(wxString::FromAscii(soxr_method_names[index])); + } + + const wxString ConstRateResample::GetFastMethodKey() + { + return wxT("/Quality/SampleRateConverter"); + } + + const wxString ConstRateResample::GetBestMethodKey() + { + return wxT("/Quality/HQSampleRateConverter"); + } + + int ConstRateResample::GetFastMethodDefault() {return 1;} + int ConstRateResample::GetBestMethodDefault() {return 3;} + + int ConstRateResample::Process(double factor, + float *inBuffer, + int inBufferLen, + bool lastFlag, + int *inBufferUsed, + float *outBuffer, + int outBufferLen) + { + size_t idone , odone; + soxr_set_oi_ratio((soxr_t)mHandle, factor); + soxr_process((soxr_t)mHandle, + inBuffer , (size_t)(lastFlag? ~inBufferLen : inBufferLen), &idone, + outBuffer, (size_t) outBufferLen, &odone); + *inBufferUsed = (int)idone; + return (int)odone; + } + +#else // no const-rate resampler + ConstRateResample::ConstRateResample(const bool useBestMethod, const double dFactor) + : Resample() + { + } + + ConstRateResample::~ConstRateResample() + { + } +#endif + + + + + + + +//vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv #if USE_LIBRESAMPLE @@ -59,10 +144,11 @@ bool Resample::ResamplingEnabled() return true; } -wxString Resample::GetResamplingLibraryName() -{ - return _("Libresample by Dominic Mazzoni and Julius Smith"); -} +//v Currently unused. +//wxString Resample::GetResamplingLibraryName() +//{ +// return _("Libresample by Dominic Mazzoni and Julius Smith"); +//} int Resample::GetNumMethods() { @@ -129,7 +215,7 @@ Resample::~Resample() resample_close(mHandle); } -#elif USE_LIBSAMPLERATE || USE_LIBSOXR +#elif USE_LIBSAMPLERATE #include @@ -138,16 +224,17 @@ bool Resample::ResamplingEnabled() return true; } -wxString Resample::GetResamplingLibraryName() -{ -#ifdef USE_LIBSAMPLERATE - return _("Libsamplerate by Erik de Castro Lopo"); -#elif USE_LIBSOXR - return _("Libsoxr by Rob Sykes"); -#else - return _("Unknown"); -#endif -} +//v Currently unused. +//wxString Resample::GetResamplingLibraryName() +//{ +//#ifdef USE_LIBSAMPLERATE +// return _("Libsamplerate by Erik de Castro Lopo"); +//#elif USE_LIBSOXR +// return _("Libsoxr by Rob Sykes"); +//#else +// return _("Unknown"); +//#endif +//} int Resample::GetNumMethods() { @@ -248,96 +335,4 @@ Resample::~Resample() src_delete((SRC_STATE *)mHandle); } -#else // No resampling support - -bool Resample::ResamplingEnabled() -{ - return false; -} - -wxString Resample::GetResamplingLibraryName() -{ - return _("Resampling disabled."); -} - -int Resample::GetNumMethods() -{ - return 1; -} - -wxString Resample::GetMethodName(int index) -{ - return _("Resampling disabled."); -} - -const wxString Resample::GetFastMethodKey() -{ - return wxT("/Quality/DisabledConverter"); -} - -const wxString Resample::GetBestMethodKey() -{ - return wxT("/Quality/DisabledConverter"); -} - -int Resample::GetFastMethodDefault() -{ - return 0; -} - -int Resample::GetBestMethodDefault() -{ - return 0; -} - -Resample::Resample(bool, double, double) -{ -} - -bool Resample::Ok() -{ - return false; -} - -int Resample::Process(double factor, - float *inBuffer, - int inBufferLen, - bool lastFlag, - int *inBufferUsed, - float *outBuffer, - int outBufferLen) -{ - int i; - int len = inBufferLen; - - if (len > outBufferLen) - len = outBufferLen; - - for(i=0; i +#include "Prefs.h" #include "SampleFormat.h" class Resample { public: - - /// This will return true if Audacity is being compiled with - /// resampling support. - static bool ResamplingEnabled(); + /// The first parameter lets you select either the best method or + /// the fast method. + //v (The particular method used was previously set by + /// SetFastMethod or SetBestMethod.) + // minFactor and maxFactor + /// specify the range of factors that will be used, if you plan + /// to vary the factor over time. Otherwise set minFactor and + /// maxFactor to the same value for optimized performance. + Resample() + { + mMethod = 0; + mHandle = NULL; + mInitial = false; + }; + virtual ~Resample() {}; /// Returns the name of the library used for resampling /// (long format, may include author name and version number). - static wxString GetResamplingLibraryName(); + //v Currently unused. + // virtual wxString GetResamplingLibraryName() { return _("Resampling disabled."); }; /// Resamplers may have more than one method, offering a /// tradeoff between speed and quality. This lets you query /// the various methods available. - static int GetNumMethods(); - static wxString GetMethodName(int index); + static int GetNumMethods() { return 1; }; + static wxString GetMethodName(int index) { return _("Resampling disabled."); }; /// Audacity identifies two methods out of all of the choices: /// a Fast method intended for real-time audio I/O, and a Best - /// method intended for mixing and exporting. These are saved - /// in the preferences when you call Set[Best,Fast]Method. - static int GetFastMethod(); - static int GetBestMethod(); - static void SetFastMethod(int index); - static void SetBestMethod(int index); + /// method intended for mixing and exporting. + //v (These were previously saved + /// in the preferences when you call Set[Best,Fast]Method.) + int GetFastMethod() { return gPrefs->Read(GetFastMethodKey(), GetFastMethodDefault()); }; + int GetBestMethod() { return gPrefs->Read(GetBestMethodKey(), GetBestMethodDefault()); }; + //v Currently unused. + //static void SetFastMethod(int index); + //static void SetBestMethod(int index); - static const wxString GetFastMethodKey(); - static const wxString GetBestMethodKey(); - static int GetFastMethodDefault(); - static int GetBestMethodDefault(); - - /// Constructor. - /// The first parameter lets you select either the best method or - /// the fast method - the particular method used was set by - /// SetFastMethod or SetBestMethod, above. minFactor and maxFactor - /// specify the range of factors that will be used, if you plan - /// to vary the factor over time. Otherwise set minFactor and - /// maxFactor to the same value for optimized performance. - Resample(bool useBestMethod, double minFactor, double maxFactor); - - /// Returns true if the constructor succeeded. - bool Ok(); + static const wxString GetFastMethodKey() { return wxT("/Quality/DisabledConverter"); }; + static const wxString GetBestMethodKey() { return wxT("/Quality/DisabledConverter"); }; + static int GetFastMethodDefault() { return 0; }; + static int GetBestMethodDefault() { return 0; }; /** @brief Main processing function. Resamples from the input buffer to the * output buffer. @@ -95,33 +87,57 @@ class Resample @param outBufferLen How big outBuffer is. @return Number of output samples created by this call */ - int Process(double factor, - float *inBuffer, - int inBufferLen, - bool lastFlag, - int *inBufferUsed, - float *outBuffer, - int outBufferLen); + virtual int Process(double factor, + float *inBuffer, + int inBufferLen, + bool lastFlag, + int *inBufferUsed, + float *outBuffer, + int outBufferLen) + { + // Base class method just copies data with no change. + int i; + int len = inBufferLen; - // Destructor - ~Resample(); + if (len > outBufferLen) + len = outBufferLen; - private: - int mMethod; - void *mHandle; + for(i=0; i