mirror of https://github.com/PCSX2/pcsx2.git
Updated the SoundTouch library to version 1.5.
It works fine and without further modifications in Windows, for SPU2-X and ZeroSPU2. Linux is untested, but hopefully also fine :p git-svn-id: http://pcsx2.googlecode.com/svn/trunk@2734 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
parent
70a3940d75
commit
ccfb4e1ea9
|
@ -24,7 +24,7 @@
|
|||
/// NOTICE: If using Visual Studio 6.0, you'll need to install the "Visual C++
|
||||
/// 6.0 processor pack" update to support 3DNow! instruction set. The update is
|
||||
/// available for download at Microsoft Developers Network, see here:
|
||||
/// http://msdn.microsoft.com/vstudio/downloads/tools/ppack/default.aspx
|
||||
/// http://msdn.microsoft.com/en-us/vstudio/aa718349.aspx
|
||||
///
|
||||
/// If the above URL is expired or removed, go to "http://msdn.microsoft.com" and
|
||||
/// perform a search with keywords "processor pack".
|
||||
|
@ -35,10 +35,10 @@
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.10 $
|
||||
// Last changed : $Date: 2009-02-21 18:00:14 +0200 (Sat, 21 Feb 2009) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: 3dnow_win.cpp,v 1.10 2006/02/05 16:44:06 Olli Exp $
|
||||
// $Id: 3dnow_win.cpp 63 2009-02-21 16:00:14Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -66,7 +66,7 @@
|
|||
#include "cpu_detect.h"
|
||||
#include "STTypes.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#ifndef WIN32
|
||||
#error "wrong platform - this source code file is exclusively for Win32 platform"
|
||||
#endif
|
||||
|
||||
|
@ -82,17 +82,13 @@ using namespace soundtouch;
|
|||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "TDStretch.h"
|
||||
#include <limits.h>
|
||||
|
||||
// these are declared in 'TDStretch.cpp'
|
||||
extern int scanOffsets[4][24];
|
||||
|
||||
|
||||
// Calculates cross correlation of two buffers
|
||||
double TDStretch3DNow::calcCrossCorrStereo(const float *pV1, const float *pV2) const
|
||||
{
|
||||
uint overlapLengthLocal = overlapLength;
|
||||
float corr;
|
||||
int overlapLengthLocal = overlapLength;
|
||||
float corr = 0;
|
||||
|
||||
// Calculates the cross-correlation value between 'pV1' and 'pV2' vectors
|
||||
/*
|
||||
|
@ -181,12 +177,15 @@ double TDStretch3DNow::calcCrossCorrStereo(const float *pV1, const float *pV2) c
|
|||
FIRFilter3DNow::FIRFilter3DNow() : FIRFilter()
|
||||
{
|
||||
filterCoeffsUnalign = NULL;
|
||||
filterCoeffsAlign = NULL;
|
||||
}
|
||||
|
||||
|
||||
FIRFilter3DNow::~FIRFilter3DNow()
|
||||
{
|
||||
delete[] filterCoeffsUnalign;
|
||||
filterCoeffsUnalign = NULL;
|
||||
filterCoeffsAlign = NULL;
|
||||
}
|
||||
|
||||
|
||||
|
@ -203,7 +202,7 @@ void FIRFilter3DNow::setCoefficients(const float *coeffs, uint newLength, uint u
|
|||
// Ensure that filter coeffs array is aligned to 16-byte boundary
|
||||
delete[] filterCoeffsUnalign;
|
||||
filterCoeffsUnalign = new float[2 * newLength + 4];
|
||||
filterCoeffsAlign = (float *)(((uint)filterCoeffsUnalign + 15) & -16);
|
||||
filterCoeffsAlign = (float *)(((uint)filterCoeffsUnalign + 15) & (uint)-16);
|
||||
|
||||
fDivider = (float)resultDivider;
|
||||
|
||||
|
@ -217,10 +216,10 @@ void FIRFilter3DNow::setCoefficients(const float *coeffs, uint newLength, uint u
|
|||
|
||||
|
||||
// 3DNow!-optimized version of the filter routine for stereo sound
|
||||
uint FIRFilter3DNow::evaluateFilterStereo(float *dest, const float *src, const uint numSamples) const
|
||||
uint FIRFilter3DNow::evaluateFilterStereo(float *dest, const float *src, uint numSamples) const
|
||||
{
|
||||
float *filterCoeffsLocal = filterCoeffsAlign;
|
||||
uint count = (numSamples - length) & -2;
|
||||
uint count = (numSamples - length) & (uint)-2;
|
||||
uint lengthLocal = length / 4;
|
||||
|
||||
assert(length != 0);
|
||||
|
|
|
@ -12,10 +12,10 @@
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.9 $
|
||||
// Last changed : $Date: 2009-01-11 13:34:24 +0200 (Sun, 11 Jan 2009) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: AAFilter.cpp,v 1.9 2006/02/05 16:44:06 Olli Exp $
|
||||
// $Id: AAFilter.cpp 45 2009-01-11 11:34:24Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -58,11 +58,11 @@ using namespace soundtouch;
|
|||
*
|
||||
*****************************************************************************/
|
||||
|
||||
AAFilter::AAFilter(const uint length)
|
||||
AAFilter::AAFilter(uint len)
|
||||
{
|
||||
pFIR = FIRFilter::newInstance();
|
||||
cutoffFreq = 0.5;
|
||||
setLength(length);
|
||||
setLength(len);
|
||||
}
|
||||
|
||||
|
||||
|
@ -77,7 +77,7 @@ AAFilter::~AAFilter()
|
|||
// Sets new anti-alias filter cut-off edge frequency, scaled to
|
||||
// sampling frequency (nyquist frequency = 0.5).
|
||||
// The filter will cut frequencies higher than the given frequency.
|
||||
void AAFilter::setCutoffFreq(const double newCutoffFreq)
|
||||
void AAFilter::setCutoffFreq(double newCutoffFreq)
|
||||
{
|
||||
cutoffFreq = newCutoffFreq;
|
||||
calculateCoeffs();
|
||||
|
@ -86,7 +86,7 @@ void AAFilter::setCutoffFreq(const double newCutoffFreq)
|
|||
|
||||
|
||||
// Sets number of FIR filter taps
|
||||
void AAFilter::setLength(const uint newLength)
|
||||
void AAFilter::setLength(uint newLength)
|
||||
{
|
||||
length = newLength;
|
||||
calculateCoeffs();
|
||||
|
@ -104,7 +104,7 @@ void AAFilter::calculateCoeffs()
|
|||
double *work;
|
||||
SAMPLETYPE *coeffs;
|
||||
|
||||
assert(length > 0);
|
||||
assert(length >= 2);
|
||||
assert(length % 4 == 0);
|
||||
assert(cutoffFreq >= 0);
|
||||
assert(cutoffFreq <= 0.5);
|
||||
|
|
|
@ -13,10 +13,10 @@
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.10 $
|
||||
// Last changed : $Date: 2008-02-10 18:26:55 +0200 (Sun, 10 Feb 2008) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: AAFilter.h,v 1.10 2006/02/05 16:44:06 Olli Exp $
|
||||
// $Id: AAFilter.h 11 2008-02-10 16:26:55Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
|
|
@ -26,10 +26,10 @@
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.5 $
|
||||
// Last changed : $Date: 2009-02-21 18:00:14 +0200 (Sat, 21 Feb 2009) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: BPMDetect.h,v 1.5 2006/02/05 16:44:06 Olli Exp $
|
||||
// $Id: BPMDetect.h 63 2009-02-21 16:00:14Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -60,8 +60,11 @@
|
|||
#include "STTypes.h"
|
||||
#include "FIFOSampleBuffer.h"
|
||||
|
||||
namespace soundtouch
|
||||
{
|
||||
|
||||
/// Minimum allowed BPM rate. Used to restrict accepted result above a reasonable limit.
|
||||
#define MIN_BPM 45
|
||||
#define MIN_BPM 29
|
||||
|
||||
/// Maximum allowed BPM rate. Used to restrict accepted result below a reasonable limit.
|
||||
#define MAX_BPM 230
|
||||
|
@ -105,9 +108,6 @@ protected:
|
|||
/// FIFO-buffer for decimated processing samples.
|
||||
soundtouch::FIFOSampleBuffer *buffer;
|
||||
|
||||
/// Initialize the class for processing.
|
||||
void init(int numChannels, int sampleRate);
|
||||
|
||||
/// Updates auto-correlation function for given number of decimated samples that
|
||||
/// are read from the internal 'buffer' pipe (samples aren't removed from the pipe
|
||||
/// though).
|
||||
|
@ -143,8 +143,8 @@ public:
|
|||
/// function.
|
||||
///
|
||||
/// Notice that data in 'samples' array can be disrupted in processing.
|
||||
void inputSamples(soundtouch::SAMPLETYPE *samples, ///< Pointer to input/working data buffer
|
||||
int numSamples ///< Number of samples in buffer
|
||||
void inputSamples(const soundtouch::SAMPLETYPE *samples, ///< Pointer to input/working data buffer
|
||||
int numSamples ///< Number of samples in buffer
|
||||
);
|
||||
|
||||
|
||||
|
@ -156,4 +156,6 @@ public:
|
|||
float getBpm();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // _BPMDetect_H_
|
||||
|
|
|
@ -15,10 +15,10 @@
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.11 $
|
||||
// Last changed : $Date: 2009-02-27 19:24:42 +0200 (Fri, 27 Feb 2009) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: FIFOSampleBuffer.cpp,v 1.11 2006/02/05 16:44:06 Olli Exp $
|
||||
// $Id: FIFOSampleBuffer.cpp 68 2009-02-27 17:24:42Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -54,14 +54,16 @@
|
|||
using namespace soundtouch;
|
||||
|
||||
// Constructor
|
||||
FIFOSampleBuffer::FIFOSampleBuffer(uint numChannels)
|
||||
FIFOSampleBuffer::FIFOSampleBuffer(int numChannels)
|
||||
{
|
||||
assert(numChannels > 0);
|
||||
sizeInBytes = 0; // reasonable initial value
|
||||
buffer = NULL; //new SAMPLETYPE[sizeInBytes / sizeof(SAMPLETYPE)];
|
||||
buffer = NULL;
|
||||
bufferUnaligned = NULL;
|
||||
samplesInBuffer = 0;
|
||||
bufferPos = 0;
|
||||
channels = numChannels;
|
||||
channels = (uint)numChannels;
|
||||
ensureCapacity(32); // allocate initial capacity
|
||||
}
|
||||
|
||||
|
||||
|
@ -69,16 +71,19 @@ FIFOSampleBuffer::FIFOSampleBuffer(uint numChannels)
|
|||
FIFOSampleBuffer::~FIFOSampleBuffer()
|
||||
{
|
||||
delete[] bufferUnaligned;
|
||||
bufferUnaligned = NULL;
|
||||
buffer = NULL;
|
||||
}
|
||||
|
||||
|
||||
// Sets number of channels, 1 = mono, 2 = stereo
|
||||
void FIFOSampleBuffer::setChannels(const uint numChannels)
|
||||
void FIFOSampleBuffer::setChannels(int numChannels)
|
||||
{
|
||||
uint usedBytes;
|
||||
|
||||
assert(numChannels > 0);
|
||||
usedBytes = channels * samplesInBuffer;
|
||||
channels = numChannels;
|
||||
channels = (uint)numChannels;
|
||||
samplesInBuffer = usedBytes / channels;
|
||||
}
|
||||
|
||||
|
@ -88,7 +93,7 @@ void FIFOSampleBuffer::setChannels(const uint numChannels)
|
|||
// location on to the beginning of the buffer.
|
||||
void FIFOSampleBuffer::rewind()
|
||||
{
|
||||
if (bufferPos)
|
||||
if (buffer && bufferPos)
|
||||
{
|
||||
memmove(buffer, ptrBegin(), sizeof(SAMPLETYPE) * channels * samplesInBuffer);
|
||||
bufferPos = 0;
|
||||
|
@ -98,10 +103,10 @@ void FIFOSampleBuffer::rewind()
|
|||
|
||||
// Adds 'numSamples' pcs of samples from the 'samples' memory position to
|
||||
// the sample buffer.
|
||||
void FIFOSampleBuffer::putSamples(const SAMPLETYPE *samples, uint numSamples)
|
||||
void FIFOSampleBuffer::putSamples(const SAMPLETYPE *samples, uint nSamples)
|
||||
{
|
||||
memcpy(ptrEnd(numSamples), samples, sizeof(SAMPLETYPE) * numSamples * channels);
|
||||
samplesInBuffer += numSamples;
|
||||
memcpy(ptrEnd(nSamples), samples, sizeof(SAMPLETYPE) * nSamples * channels);
|
||||
samplesInBuffer += nSamples;
|
||||
}
|
||||
|
||||
|
||||
|
@ -111,13 +116,13 @@ void FIFOSampleBuffer::putSamples(const SAMPLETYPE *samples, uint numSamples)
|
|||
// This function is used to update the number of samples in the sample buffer
|
||||
// when accessing the buffer directly with 'ptrEnd' function. Please be
|
||||
// careful though!
|
||||
void FIFOSampleBuffer::putSamples(uint numSamples)
|
||||
void FIFOSampleBuffer::putSamples(uint nSamples)
|
||||
{
|
||||
uint req;
|
||||
|
||||
req = samplesInBuffer + numSamples;
|
||||
req = samplesInBuffer + nSamples;
|
||||
ensureCapacity(req);
|
||||
samplesInBuffer += numSamples;
|
||||
samplesInBuffer += nSamples;
|
||||
}
|
||||
|
||||
|
||||
|
@ -147,8 +152,9 @@ SAMPLETYPE *FIFOSampleBuffer::ptrEnd(uint slackCapacity)
|
|||
// When using this function to output samples, also remember to 'remove' the
|
||||
// outputted samples from the buffer by calling the
|
||||
// 'receiveSamples(numSamples)' function
|
||||
SAMPLETYPE *FIFOSampleBuffer::ptrBegin() const
|
||||
SAMPLETYPE *FIFOSampleBuffer::ptrBegin()
|
||||
{
|
||||
assert(buffer);
|
||||
return buffer + bufferPos * channels;
|
||||
}
|
||||
|
||||
|
@ -164,15 +170,19 @@ void FIFOSampleBuffer::ensureCapacity(uint capacityRequirement)
|
|||
if (capacityRequirement > getCapacity())
|
||||
{
|
||||
// enlarge the buffer in 4kbyte steps (round up to next 4k boundary)
|
||||
sizeInBytes = (capacityRequirement * channels * sizeof(SAMPLETYPE) + 4095) & -4096;
|
||||
sizeInBytes = (capacityRequirement * channels * sizeof(SAMPLETYPE) + 4095) & (uint)-4096;
|
||||
assert(sizeInBytes % 2 == 0);
|
||||
tempUnaligned = new SAMPLETYPE[sizeInBytes / sizeof(SAMPLETYPE) + 16 / sizeof(SAMPLETYPE)];
|
||||
if (tempUnaligned == NULL)
|
||||
{
|
||||
throw std::runtime_error("Couldn't allocate memory!\n");
|
||||
}
|
||||
temp = (SAMPLETYPE *)(((ulongptr)tempUnaligned + 15) & -16);
|
||||
memcpy(temp, ptrBegin(), samplesInBuffer * channels * sizeof(SAMPLETYPE));
|
||||
// Align the buffer to begin at 16byte cache line boundary for optimal performance
|
||||
temp = (SAMPLETYPE *)(((ulong)tempUnaligned + 15) & (ulong)-16);
|
||||
if (samplesInBuffer)
|
||||
{
|
||||
memcpy(temp, ptrBegin(), samplesInBuffer * channels * sizeof(SAMPLETYPE));
|
||||
}
|
||||
delete[] bufferUnaligned;
|
||||
buffer = temp;
|
||||
bufferUnaligned = tempUnaligned;
|
||||
|
|
|
@ -15,10 +15,10 @@
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.9 $
|
||||
// Last changed : $Date: 2009-02-21 18:00:14 +0200 (Sat, 21 Feb 2009) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: FIFOSampleBuffer.h,v 1.9 2006/02/05 16:44:06 Olli Exp $
|
||||
// $Id: FIFOSampleBuffer.h 63 2009-02-21 16:00:14Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -85,7 +85,7 @@ private:
|
|||
void rewind();
|
||||
|
||||
/// Ensures that the buffer has capacity for at least this many samples.
|
||||
void ensureCapacity(const uint capacityRequirement);
|
||||
void ensureCapacity(uint capacityRequirement);
|
||||
|
||||
/// Returns current capacity.
|
||||
uint getCapacity() const;
|
||||
|
@ -93,7 +93,7 @@ private:
|
|||
public:
|
||||
|
||||
/// Constructor
|
||||
FIFOSampleBuffer(uint numChannels = 2 ///< Number of channels, 1=mono, 2=stereo.
|
||||
FIFOSampleBuffer(int numChannels = 2 ///< Number of channels, 1=mono, 2=stereo.
|
||||
///< Default is stereo.
|
||||
);
|
||||
|
||||
|
@ -107,7 +107,7 @@ public:
|
|||
/// When using this function to output samples, also remember to 'remove' the
|
||||
/// output samples from the buffer by calling the
|
||||
/// 'receiveSamples(numSamples)' function
|
||||
virtual SAMPLETYPE *ptrBegin() const;
|
||||
virtual SAMPLETYPE *ptrBegin();
|
||||
|
||||
/// Returns a pointer to the end of the used part of the sample buffer (i.e.
|
||||
/// where the new samples are to be inserted). This function may be used for
|
||||
|
@ -160,7 +160,7 @@ public:
|
|||
virtual uint numSamples() const;
|
||||
|
||||
/// Sets number of channels, 1 = mono, 2 = stereo.
|
||||
void setChannels(uint numChannels);
|
||||
void setChannels(int numChannels);
|
||||
|
||||
/// Returns nonzero if there aren't any samples available for outputting.
|
||||
virtual int isEmpty() const;
|
||||
|
|
|
@ -17,10 +17,10 @@
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.8 $
|
||||
// Last changed : $Date: 2009-04-13 16:18:48 +0300 (Mon, 13 Apr 2009) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: FIFOSamplePipe.h,v 1.8 2006/02/05 16:44:06 Olli Exp $
|
||||
// $Id: FIFOSamplePipe.h 69 2009-04-13 13:18:48Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -59,6 +59,10 @@ namespace soundtouch
|
|||
class FIFOSamplePipe
|
||||
{
|
||||
public:
|
||||
// virtual default destructor
|
||||
virtual ~FIFOSamplePipe() {}
|
||||
|
||||
|
||||
/// Returns a pointer to the beginning of the output samples.
|
||||
/// This function is provided for accessing the output samples directly.
|
||||
/// Please be careful for not to corrupt the book-keeping!
|
||||
|
@ -66,12 +70,12 @@ public:
|
|||
/// When using this function to output samples, also remember to 'remove' the
|
||||
/// output samples from the buffer by calling the
|
||||
/// 'receiveSamples(numSamples)' function
|
||||
virtual SAMPLETYPE *ptrBegin() const = 0;
|
||||
virtual SAMPLETYPE *ptrBegin() = 0;
|
||||
|
||||
/// Adds 'numSamples' pcs of samples from the 'samples' memory position to
|
||||
/// the sample buffer.
|
||||
virtual void putSamples(const SAMPLETYPE *samples, ///< Pointer to samples.
|
||||
uint numSamples ///< Number of samples to insert.
|
||||
uint numSamples ///< Number of samples to insert.
|
||||
) = 0;
|
||||
|
||||
|
||||
|
@ -166,7 +170,7 @@ protected:
|
|||
/// When using this function to output samples, also remember to 'remove' the
|
||||
/// output samples from the buffer by calling the
|
||||
/// 'receiveSamples(numSamples)' function
|
||||
virtual SAMPLETYPE *ptrBegin() const
|
||||
virtual SAMPLETYPE *ptrBegin()
|
||||
{
|
||||
return output->ptrBegin();
|
||||
}
|
||||
|
|
|
@ -11,10 +11,10 @@
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.16 $
|
||||
// Last changed : $Date: 2009-02-25 19:13:51 +0200 (Wed, 25 Feb 2009) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: FIRFilter.cpp,v 1.16 2006/02/05 16:44:06 Olli Exp $
|
||||
// $Id: FIRFilter.cpp 67 2009-02-25 17:13:51Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -58,6 +58,7 @@ using namespace soundtouch;
|
|||
FIRFilter::FIRFilter()
|
||||
{
|
||||
resultDivFactor = 0;
|
||||
resultDivider = 0;
|
||||
length = 0;
|
||||
lengthDiv8 = 0;
|
||||
filterCoeffs = NULL;
|
||||
|
@ -81,6 +82,9 @@ uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, ui
|
|||
#endif
|
||||
|
||||
assert(length != 0);
|
||||
assert(src != NULL);
|
||||
assert(dest != NULL);
|
||||
assert(filterCoeffs != NULL);
|
||||
|
||||
end = 2 * (numSamples - length);
|
||||
|
||||
|
@ -177,11 +181,7 @@ void FIRFilter::setCoefficients(const SAMPLETYPE *coeffs, uint newLength, uint u
|
|||
assert(length == newLength);
|
||||
|
||||
resultDivFactor = uResultDivFactor;
|
||||
#ifdef INTEGER_SAMPLES
|
||||
resultDivider = (SAMPLETYPE)(1<<resultDivFactor);
|
||||
#else
|
||||
resultDivider = (SAMPLETYPE)powf(2, (SAMPLETYPE)resultDivFactor);
|
||||
#endif
|
||||
resultDivider = (SAMPLETYPE)::pow(2.0, (int)resultDivFactor);
|
||||
|
||||
delete[] filterCoeffs;
|
||||
filterCoeffs = new SAMPLETYPE[length];
|
||||
|
@ -207,7 +207,6 @@ uint FIRFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSample
|
|||
assert(length > 0);
|
||||
assert(lengthDiv8 * 8 == length);
|
||||
if (numSamples < length) return 0;
|
||||
assert(resultDivFactor >= 0);
|
||||
if (numChannels == 2)
|
||||
{
|
||||
return evaluateFilterStereo(dest, src, numSamples);
|
||||
|
@ -223,18 +222,16 @@ uint FIRFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSample
|
|||
void * FIRFilter::operator new(size_t s)
|
||||
{
|
||||
// Notice! don't use "new FIRFilter" directly, use "newInstance" to create a new instance instead!
|
||||
throw std::runtime_error("Don't use 'new FIRFilter', use 'newInstance' member instead!");
|
||||
throw std::runtime_error("Error in FIRFilter::new: Don't use 'new FIRFilter', use 'newInstance' member instead!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
FIRFilter * FIRFilter::newInstance()
|
||||
{
|
||||
uint uExtensions = 0;
|
||||
uint uExtensions;
|
||||
|
||||
#if !defined(_MSC_VER) || !defined(__x86_64__)
|
||||
uExtensions = detectCPUextensions();
|
||||
#endif
|
||||
|
||||
// Check if MMX/SSE/3DNow! instruction set extensions supported by CPU
|
||||
|
||||
|
|
|
@ -11,10 +11,10 @@
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.17 $
|
||||
// Last changed : $Date: 2009-02-21 18:00:14 +0200 (Sat, 21 Feb 2009) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: FIRFilter.h,v 1.17 2006/02/05 16:44:06 Olli Exp $
|
||||
// $Id: FIRFilter.h 63 2009-02-21 16:00:14Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -42,6 +42,7 @@
|
|||
#ifndef FIRFilter_H
|
||||
#define FIRFilter_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include "STTypes.h"
|
||||
|
||||
namespace soundtouch
|
||||
|
@ -77,7 +78,7 @@ public:
|
|||
|
||||
/// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
||||
/// depending on if we've a MMX-capable CPU available or not.
|
||||
void * operator new(size_t s);
|
||||
static void * operator new(size_t s);
|
||||
|
||||
static FIRFilter *newInstance();
|
||||
|
||||
|
@ -103,7 +104,7 @@ public:
|
|||
|
||||
#ifdef ALLOW_MMX
|
||||
|
||||
/// Class that implements MMX optimized functions exclusive for 16bit integer samples type.
|
||||
/// Class that implements MMX optimized functions exclusive for 16bit integer samples type.
|
||||
class FIRFilterMMX : public FIRFilter
|
||||
{
|
||||
protected:
|
||||
|
|
|
@ -0,0 +1,752 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type"
|
||||
content="text/html; charset=windows-1252">
|
||||
<meta http-equiv="Content-Language" content="en-us">
|
||||
<meta name="author" content="Olli Parviainen">
|
||||
<meta name="description"
|
||||
content="Readme file for SoundTouch audio processing library">
|
||||
<meta name="GENERATOR" content="Microsoft FrontPage 4.0">
|
||||
<meta name="ProgId" content="FrontPage.Editor.Document">
|
||||
<title>SoundTouch library README</title>
|
||||
<style>
|
||||
<!--
|
||||
.normal { font-family: Arial }
|
||||
-->
|
||||
</style>
|
||||
</head>
|
||||
<body class="normal">
|
||||
<hr>
|
||||
<h1>SoundTouch audio processing library v1.5.0
|
||||
</h1>
|
||||
<p class="normal">SoundTouch library Copyright (c) Olli
|
||||
Parviainen 2002-2009 </p>
|
||||
<hr>
|
||||
<h2>1. Introduction </h2>
|
||||
<p>SoundTouch is an open-source audio
|
||||
processing library that allows changing the sound tempo, pitch
|
||||
and playback rate parameters independently from each other, i.e.:</p>
|
||||
<ul>
|
||||
<li>Sound tempo can be increased or decreased while
|
||||
maintaining the original pitch</li>
|
||||
<li>Sound pitch can be increased or decreased while
|
||||
maintaining the original tempo </li>
|
||||
<li>Change playback rate that affects both tempo
|
||||
and pitch at the same time </li>
|
||||
<li>Choose any combination of tempo/pitch/rate</li>
|
||||
</ul>
|
||||
<h3>1.1 Contact information </h3>
|
||||
<p>Author email: oparviai 'at' iki.fi </p>
|
||||
<p>SoundTouch WWW page: <a href="http://www.surina.net/soundtouch">http://www.surina.net/soundtouch</a></p>
|
||||
<hr>
|
||||
<h2>2. Compiling SoundTouch</h2>
|
||||
<p>Before compiling, notice that you can choose the sample data format
|
||||
if it's desirable to use floating point sample
|
||||
data instead of 16bit integers. See section "sample data format"
|
||||
for more information.</p>
|
||||
<h3>2.1. Building in Microsoft Windows</h3>
|
||||
<p>Project files for Microsoft Visual C++ 6.0 and Visual C++ .NET are
|
||||
supplied with the source code package. </p>
|
||||
<p> Please notice that SoundTouch
|
||||
library uses processor-specific optimizations for Pentium III and AMD
|
||||
processors. Visual Studio .NET and later versions supports the required
|
||||
instructions by default, but Visual Studio 6.0 requires a processor pack upgrade
|
||||
to be installed in order to support these optimizations. The processor pack upgrade can be downloaded from
|
||||
Microsoft site at this URL:</p>
|
||||
<p><a href="http://msdn.microsoft.com/en-us/vstudio/aa718349.aspx">http://msdn.microsoft.com/en-us/vstudio/aa718349.aspx</a></p>
|
||||
<p>If the above URL is unavailable or removed, go
|
||||
to <a href="http://msdn.microsoft.com/">http://msdn.microsoft.com</a>
|
||||
and perform a search with keywords "processor pack". </p>
|
||||
<p>To build the binaries with Visual C++
|
||||
compiler, either run "make-win.bat" script, or open the
|
||||
appropriate project files in source code directories with Visual
|
||||
Studio. The final executable will appear under the "SoundTouch\bin"
|
||||
directory. If using the Visual Studio IDE instead of the make-win.bat script, directories bin and
|
||||
lib may need to be created manually to the SoundTouch
|
||||
package root for the final executables. The make-win.bat script
|
||||
creates these directories automatically.
|
||||
</p>
|
||||
<h3>2.2. Building in Gnu platforms</h3>
|
||||
<p>The SoundTouch library can be compiled in
|
||||
practically any platform supporting GNU compiler (GCC) tools.
|
||||
SoundTouch have been tested with gcc version 3.3.4., but it
|
||||
shouldn't be very specific about the gcc version. Assembler-level
|
||||
performance optimizations for GNU platform are currently available in
|
||||
x86 platforms only, they are automatically disabled and replaced with
|
||||
standard C routines in other processor platforms.</p>
|
||||
<p>To build and install the binaries, run the
|
||||
following commands in the SoundTouch/ directory:</p>
|
||||
<table border="0" cellpadding="0" cellspacing="4">
|
||||
<tbody>
|
||||
<tr valign="top">
|
||||
<td>
|
||||
<pre>./configure -</pre>
|
||||
</td>
|
||||
<td>
|
||||
<p>Configures the SoundTouch package for the local
|
||||
environment.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr valign="top">
|
||||
<td>
|
||||
<pre>make -</pre>
|
||||
</td>
|
||||
<td>
|
||||
<p>Builds the SoundTouch library &
|
||||
SoundStretch utility.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr valign="top">
|
||||
<td>
|
||||
<pre>make install -</pre>
|
||||
</td>
|
||||
<td>
|
||||
<p>Installs the SoundTouch & BPM libraries
|
||||
to <b>/usr/local/lib</b> and SoundStretch utility to <b>/usr/local/bin</b>.
|
||||
Please notice that 'root' privileges may be required to install the
|
||||
binaries to the destination locations.</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h4><b>2.2.1 Required GNU tools</b> </h4>
|
||||
<p> Bash shell, GNU C++ compiler, libtool, autoconf and automake tools are required
|
||||
for compiling
|
||||
the SoundTouch library. These are usually included with the GNU/Linux distribution, but if
|
||||
not, install these packages first. For example, in Ubuntu Linux these can be acquired and
|
||||
installed with the following command:</p>
|
||||
<pre><b>sudo apt-get install <font SIZE="2">automake autoconf libtool build-essential</font></b></pre>
|
||||
<h4><b>2.2.2 Problems with GCC compiler compatibility</b></h4>
|
||||
<p>At the release time the SoundTouch package has been tested to compile in
|
||||
GNU/Linux platform. However, in past it's happened that new gcc versions aren't
|
||||
necessarily compatible with the assembler settings used in the optimized
|
||||
routines. <b>If you have problems getting the
|
||||
SoundTouch library compiled, try the workaround of disabling the optimizations</b>
|
||||
by editing the file "include/STTypes.h" and removing the following
|
||||
definition there:</p>
|
||||
<blockquote>
|
||||
<pre>#define ALLOW_OPTIMIZATIONS 1</pre>
|
||||
</blockquote>
|
||||
<h4><b>2.2.3 Problems with configure script or build process</b> </h4>
|
||||
<p>Incompatibilities between various GNU toolchain versions may cause errors when running the "configure" script or building the source
|
||||
codes, if your GNU tool versions are not compatible with the versions used for
|
||||
preparing the SoundTouch kit. </p>
|
||||
<p>To resolve the issue, regenerate the configure scripts with your local tool
|
||||
set by running
|
||||
the "<b>./bootstrap</b>" script included in the SoundTouch source code
|
||||
kit. After that, run the <b>configure</b> script and <b>make</b> as usually.</p>
|
||||
<h4><b>2.2.4 Compiler issues with non-x86 processors</b></h4>
|
||||
<p>SoundTouch library works also on non-x86 processors.</p>
|
||||
<p>However, in case that you get compiler errors when trying to compile for non-Intel processor, edit the file
|
||||
"<b>source\SoundTouch\Makefile.am</b>" and remove the "<b>-msse2</b>"
|
||||
flag on the <b>AM_CXXFLAGS </b>line:</p>
|
||||
<pre><b>AM_CXXFLAGS=-O3 -fcheck-new -I../../include # Note: -msse2 flag removed!</b></pre>
|
||||
<p>After that, run "<b>./bootstrap</b>" script, and then run <b>configure</b>
|
||||
and <b>make</b> again.</p>
|
||||
<hr>
|
||||
<h2>3. About implementation & Usage tips</h2>
|
||||
<h3>3.1. Supported sample data formats</h3>
|
||||
<p>The sample data format can be chosen
|
||||
between 16bit signed integer and 32bit floating point values, the
|
||||
default is 32bit floating point. </p>
|
||||
|
||||
<p>
|
||||
In Windows environment, the sample data format is chosen
|
||||
in file "STTypes.h" by choosing one of the following
|
||||
defines:</p>
|
||||
<ul>
|
||||
<li><span style="font-weight: bold;">#define INTEGER_SAMPLES</span>
|
||||
for 16bit signed
|
||||
integer</li>
|
||||
<li><span style="font-weight: bold;">#define FLOAT_SAMPLES</span> for
|
||||
32bit floating point</li>
|
||||
</ul>
|
||||
<p>
|
||||
In GNU environment, the floating sample format is used by default, but
|
||||
integer sample format can be chosen by giving the
|
||||
following switch to the configure script:
|
||||
<blockquote>
|
||||
<pre>./configure --enable-integer-samples</pre>
|
||||
</blockquote>
|
||||
|
||||
<p>The sample data can have either single (mono)
|
||||
or double (stereo) audio channel. Stereo data is interleaved so
|
||||
that every other data value is for left channel and every second
|
||||
for right channel. Notice that while it'd be possible in theory
|
||||
to process stereo sound as two separate mono channels, this isn't
|
||||
recommended because processing the channels separately would
|
||||
result in losing the phase coherency between the channels, which
|
||||
consequently would ruin the stereo effect.</p>
|
||||
<p>Sample rates between 8000-48000H are
|
||||
supported.</p>
|
||||
<h3>3.2. Processing latency</h3>
|
||||
<p>The processing and latency constraints of
|
||||
the SoundTouch library are:</p>
|
||||
<ul>
|
||||
<li>Input/output processing latency for the
|
||||
SoundTouch processor is around 100 ms. This is when time-stretching is
|
||||
used. If the rate transposing effect alone is used, the latency
|
||||
requirement
|
||||
is much shorter, see section 'About algorithms'.</li>
|
||||
<li>Processing CD-quality sound (16bit stereo
|
||||
sound with 44100H sample rate) in real-time or faster is possible
|
||||
starting from processors equivalent to Intel Pentium 133Mh or better,
|
||||
if using the "quick" processing algorithm. If not using the "quick"
|
||||
mode or
|
||||
if floating point sample data are being used, several times more CPU
|
||||
power is typically required.</li>
|
||||
</ul>
|
||||
<h3>3.3. About algorithms</h3>
|
||||
<p>SoundTouch provides three seemingly
|
||||
independent effects: tempo, pitch and playback rate control.
|
||||
These three controls are implemented as combination of two primary
|
||||
effects, <em>sample rate transposing</em> and <em>time-stretching</em>.</p>
|
||||
<p><em>Sample rate transposing</em> affects
|
||||
both the audio stream duration and pitch. It's implemented simply
|
||||
by converting the original audio sample stream to the desired
|
||||
duration by interpolating from the original audio samples. In SoundTouch, linear interpolation with anti-alias filtering is
|
||||
used. Theoretically a higher-order interpolation provide better
|
||||
result than 1st order linear interpolation, but in audio
|
||||
application linear interpolation together with anti-alias
|
||||
filtering performs subjectively about as well as higher-order
|
||||
filtering would.</p>
|
||||
<p><em>Time-stretching </em>means changing
|
||||
the audio stream duration without affecting it's pitch. SoundTouch
|
||||
uses WSOLA-like time-stretching routines that operate in the time
|
||||
domain. Compared to sample rate transposing, time-stretching is a
|
||||
much heavier operation and also requires a longer processing
|
||||
"window" of sound samples used by the
|
||||
processing algorithm, thus increasing the algorithm input/output
|
||||
latency. Typical i/o latency for the SoundTouch
|
||||
time-stretch algorithm is around 100 ms.</p>
|
||||
<p>Sample rate transposing and time-stretching
|
||||
are then used together to produce the tempo, pitch and rate
|
||||
controls:</p>
|
||||
<ul>
|
||||
<li><strong>'Tempo'</strong> control is
|
||||
implemented purely by time-stretching.</li>
|
||||
<li><strong>'Rate</strong>' control is implemented
|
||||
purely by sample rate transposing.</li>
|
||||
<li><strong>'Pitch</strong>' control is
|
||||
implemented as a combination of time-stretching and sample rate
|
||||
transposing. For example, to increase pitch the audio stream is first
|
||||
time-stretched to longer duration (without affecting pitch) and then
|
||||
transposed back to original duration by sample rate transposing, which
|
||||
simultaneously reduces duration and increases pitch. The result is
|
||||
original duration but increased pitch.</li>
|
||||
</ul>
|
||||
<h3>3.4 Tuning the algorithm parameters</h3>
|
||||
<p>The time-stretch algorithm has few
|
||||
parameters that can be tuned to optimize sound quality for
|
||||
certain application. The current default parameters have been
|
||||
chosen by iterative if-then analysis (read: "trial and error")
|
||||
to obtain best subjective sound quality in pop/rock music
|
||||
processing, but in applications processing different kind of
|
||||
sound the default parameter set may result into a sub-optimal
|
||||
result.</p>
|
||||
<p>The time-stretch algorithm default
|
||||
parameter values are set by the following #defines in file "TDStretch.h":</p>
|
||||
<blockquote>
|
||||
<pre>#define DEFAULT_SEQUENCE_MS AUTOMATIC
|
||||
#define DEFAULT_SEEKWINDOW_MS AUTOMATIC
|
||||
#define DEFAULT_OVERLAP_MS 8</pre>
|
||||
</blockquote>
|
||||
<p>These parameters affect to the time-stretch
|
||||
algorithm as follows:</p>
|
||||
<ul>
|
||||
<li><strong>DEFAULT_SEQUENCE_MS</strong>: This is
|
||||
the default length of a single processing sequence in milliseconds
|
||||
which determines the how the original sound is chopped in
|
||||
the time-stretch algorithm. Larger values mean fewer sequences
|
||||
are used in processing. In principle a larger value sounds better when
|
||||
slowing down the tempo, but worse when increasing the tempo and vice
|
||||
versa. <br>
|
||||
<br>
|
||||
By default, this setting value is calculated automatically according to
|
||||
tempo value.<br>
|
||||
</li>
|
||||
<li><strong>DEFAULT_SEEKWINDOW_MS</strong>: The seeking window
|
||||
default length in milliseconds is for the algorithm that seeks the best
|
||||
possible overlapping location. This determines from how
|
||||
wide a sample "window" the algorithm can use to find an optimal mixing
|
||||
location when the sound sequences are to be linked back together. <br>
|
||||
<br>
|
||||
The bigger this window setting is, the higher the possibility to find a
|
||||
better mixing position becomes, but at the same time large values may
|
||||
cause a "drifting" sound artifact because neighboring sequences can be
|
||||
chosen at more uneven intervals. If there's a disturbing artifact that
|
||||
sounds as if a constant frequency was drifting around, try reducing
|
||||
this setting.<br>
|
||||
<br>
|
||||
By default, this setting value is calculated automatically according to
|
||||
tempo value.<br>
|
||||
</li>
|
||||
<li><strong>DEFAULT_OVERLAP_MS</strong>: Overlap
|
||||
length in milliseconds. When the sound sequences are mixed back
|
||||
together to form again a continuous sound stream, this parameter
|
||||
defines how much the ends of the consecutive sequences will overlap with each other.<br>
|
||||
<br>
|
||||
This shouldn't be that critical parameter. If you reduce the
|
||||
DEFAULT_SEQUENCE_MS setting by a large amount, you might wish to try a
|
||||
smaller value on this.</li>
|
||||
</ul>
|
||||
<p>Notice that these parameters can also be
|
||||
set during execution time with functions "<strong>TDStretch::setParameters()</strong>"
|
||||
and "<strong>SoundTouch::setSetting()</strong>".</p>
|
||||
<p>The table below summaries how the
|
||||
parameters can be adjusted for different applications:</p>
|
||||
<table border="1">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td valign="top"><strong>Parameter name</strong></td>
|
||||
<td valign="top"><strong>Default value
|
||||
magnitude</strong></td>
|
||||
<td valign="top"><strong>Larger value
|
||||
affects...</strong></td>
|
||||
<td valign="top"><strong>Smaller value
|
||||
affects...</strong></td>
|
||||
<td valign="top"><strong>Effect to CPU burden</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top">
|
||||
<pre>SEQUENCE_MS</pre>
|
||||
</td>
|
||||
<td valign="top">Default value is relatively
|
||||
large, chosen for slowing down music tempo</td>
|
||||
<td valign="top">Larger value is usually
|
||||
better for slowing down tempo. Growing the value decelerates the
|
||||
"echoing" artifact when slowing down the tempo.</td>
|
||||
<td valign="top">Smaller value might be better
|
||||
for speeding up tempo. Reducing the value accelerates the "echoing"
|
||||
artifact when slowing down the tempo </td>
|
||||
<td valign="top">Increasing the parameter
|
||||
value reduces computation burden</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top">
|
||||
<pre>SEEKWINDOW_MS</pre>
|
||||
</td>
|
||||
<td valign="top">Default value is relatively
|
||||
large, chosen for slowing down music tempo</td>
|
||||
<td valign="top">Larger value eases finding a
|
||||
good mixing position, but may cause a "drifting" artifact</td>
|
||||
<td valign="top">Smaller reduce possibility to
|
||||
find a good mixing position, but reduce the "drifting" artifact.</td>
|
||||
<td valign="top">Increasing the parameter
|
||||
value increases computation burden</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top">
|
||||
<pre>OVERLAP_MS</pre>
|
||||
</td>
|
||||
<td valign="top">Default value is relatively
|
||||
large, chosen to suit with above parameters.</td>
|
||||
<td valign="top"> </td>
|
||||
<td valign="top">If you reduce the "sequence
|
||||
ms" setting, you might wish to try a smaller value.</td>
|
||||
<td valign="top">Increasing the parameter
|
||||
value increases computation burden</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h3>3.5 Performance Optimizations </h3>
|
||||
<p><strong>General optimizations:</strong></p>
|
||||
<p>The time-stretch routine has a 'quick' mode
|
||||
that substantially speeds up the algorithm but may degrade the
|
||||
sound quality by a small amount. This mode is activated by
|
||||
calling SoundTouch::setSetting() function with parameter id
|
||||
of SETTING_USE_QUICKSEEK and value "1", i.e. </p>
|
||||
<blockquote>
|
||||
<p>setSetting(SETTING_USE_QUICKSEEK, 1);</p>
|
||||
</blockquote>
|
||||
<p><strong>CPU-specific optimizations:</strong></p>
|
||||
<ul>
|
||||
<li>Intel MMX optimized routines are used with
|
||||
compatible CPUs when 16bit integer sample type is used. MMX optimizations are available both in Win32 and Gnu/x86 platforms.
|
||||
Compatible processors are Intel PentiumMMX and later; AMD K6-2, Athlon
|
||||
and later. </li>
|
||||
<li>Intel SSE optimized routines are used with
|
||||
compatible CPUs when floating point sample type is used. SSE optimizations are currently implemented for Win32 platform only.
|
||||
Processors compatible with SSE extension are Intel processors starting
|
||||
from Pentium-III, and AMD processors starting from Athlon XP. </li>
|
||||
<li>AMD 3DNow! optimized routines are used with
|
||||
compatible CPUs when floating point sample type is used, but SSE
|
||||
extension isn't supported . 3DNow! optimizations are currently
|
||||
implemented for Win32 platform only. These optimizations are used in
|
||||
AMD K6-2 and Athlon (classic) CPU's; better performing SSE routines are
|
||||
used with AMD processor starting from Athlon XP. </li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2><a name="SoundStretch"></a>4. SoundStretch audio processing utility
|
||||
</h2>
|
||||
<p>SoundStretch audio processing utility<br>
|
||||
Copyright (c) Olli Parviainen 2002-2009</p>
|
||||
<p>SoundStretch is a simple command-line
|
||||
application that can change tempo, pitch and playback rates of
|
||||
WAV sound files. This program is intended primarily to
|
||||
demonstrate how the "SoundTouch" library can be used to
|
||||
process sound in your own program, but it can as well be used for
|
||||
processing sound files.</p>
|
||||
<h3>4.1. SoundStretch Usage Instructions</h3>
|
||||
<p>SoundStretch Usage syntax:</p>
|
||||
<blockquote>
|
||||
<pre>soundstretch infilename outfilename [switches]</pre>
|
||||
</blockquote>
|
||||
<p>Where: </p>
|
||||
<table border="0" cellpadding="2" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td valign="top">
|
||||
<pre>"infilename"</pre>
|
||||
</td>
|
||||
<td valign="top">Name of the input sound
|
||||
data file (in .WAV audio file format). Give "stdin" as filename to use
|
||||
standard input pipe. </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top">
|
||||
<pre>"outfilename"</pre>
|
||||
</td>
|
||||
<td valign="top">Name of the output sound
|
||||
file where the resulting sound is saved (in .WAV audio file format).
|
||||
This parameter may be omitted if you don't want to save the
|
||||
output
|
||||
(e.g. when only calculating BPM rate with '-bpm' switch). Give "stdout"
|
||||
as filename to use standard output pipe.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top">
|
||||
<pre> [switches]</pre>
|
||||
</td>
|
||||
<td valign="top">Are one or more control
|
||||
switches.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>Available control switches are:</p>
|
||||
<table border="0" cellpadding="2" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td valign="top">
|
||||
<pre>-tempo=n </pre>
|
||||
</td>
|
||||
<td valign="top">Change the sound tempo by n
|
||||
percents (n = -95.0 .. +5000.0 %) </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top">
|
||||
<pre>-pitch=n</pre>
|
||||
</td>
|
||||
<td valign="top">Change the sound pitch by n
|
||||
semitones (n = -60.0 .. + 60.0 semitones) </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top">
|
||||
<pre>-rate=n</pre>
|
||||
</td>
|
||||
<td valign="top">Change the sound playback rate by
|
||||
n percents (n = -95.0 .. +5000.0 %) </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top">
|
||||
<pre>-bpm=n</pre>
|
||||
</td>
|
||||
<td valign="top">Detect the Beats-Per-Minute (BPM) rate of the sound and adjust the tempo to meet 'n'
|
||||
BPMs. When this switch is
|
||||
applied, the "-tempo" switch is ignored. If "=n" is
|
||||
omitted, i.e. switch "-bpm" is used alone, then the BPM rate is
|
||||
estimated and displayed, but tempo not adjusted according to the BPM
|
||||
value. </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top">
|
||||
<pre>-quick</pre>
|
||||
</td>
|
||||
<td valign="top">Use quicker tempo change
|
||||
algorithm. Gains speed but loses sound quality. </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top">
|
||||
<pre>-naa</pre>
|
||||
</td>
|
||||
<td valign="top">Don't use anti-alias
|
||||
filtering in sample rate transposing. Gains speed but loses sound
|
||||
quality. </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top">
|
||||
<pre>-license</pre>
|
||||
</td>
|
||||
<td valign="top">Displays the program license
|
||||
text (LGPL)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>Notes:</p>
|
||||
<ul>
|
||||
<li>To use standard input/output pipes for processing, give "stdin"
|
||||
and "stdout" as input/output filenames correspondingly. The
|
||||
standard input/output pipes will still carry the audio data in .wav audio
|
||||
file format.</li>
|
||||
<li>The numerical switches allow both integer (e.g. "-tempo=123") and decimal (e.g.
|
||||
"-tempo=123.45") numbers.</li>
|
||||
<li>The "-naa" and/or "-quick" switches can be
|
||||
used to reduce CPU usage while compromising some sound quality </li>
|
||||
<li>The BPM detection algorithm works by detecting
|
||||
repeating bass or drum patterns at low frequencies of <250Hz. A
|
||||
lower-than-expected BPM figure may be reported for music with uneven or
|
||||
complex bass patterns. </li>
|
||||
</ul>
|
||||
<h3>4.2. SoundStretch usage examples </h3>
|
||||
<p><strong>Example 1</strong></p>
|
||||
<p>The following command increases tempo of
|
||||
the sound file "originalfile.wav" by 12.5% and stores result to file "destinationfile.wav":</p>
|
||||
<blockquote>
|
||||
<pre>soundstretch originalfile.wav destinationfile.wav -tempo=12.5</pre>
|
||||
</blockquote>
|
||||
<p><strong>Example 2</strong></p>
|
||||
<p>The following command decreases the sound
|
||||
pitch (key) of the sound file "orig.wav" by two
|
||||
semitones and stores the result to file "dest.wav":</p>
|
||||
<blockquote>
|
||||
<pre>soundstretch orig.wav dest.wav -pitch=-2</pre>
|
||||
</blockquote>
|
||||
<p><strong>Example 3</strong></p>
|
||||
<p>The following command processes the file "orig.wav" by decreasing the sound tempo by 25.3% and
|
||||
increasing the sound pitch (key) by 1.5 semitones. Resulting .wav audio data is
|
||||
directed to standard output pipe:</p>
|
||||
<blockquote>
|
||||
<pre>soundstretch orig.wav stdout -tempo=-25.3 -pitch=1.5</pre>
|
||||
</blockquote>
|
||||
<p><strong>Example 4</strong></p>
|
||||
<p>The following command detects the BPM rate
|
||||
of the file "orig.wav" and adjusts the tempo to match
|
||||
100 beats per minute. Result is stored to file "dest.wav":</p>
|
||||
<blockquote>
|
||||
<pre>soundstretch orig.wav dest.wav -bpm=100</pre>
|
||||
</blockquote>
|
||||
<p><strong>Example 5</strong></p>
|
||||
<p>The following command reads .wav sound data from standard input pipe and
|
||||
estimates the BPM rate:</p>
|
||||
<blockquote>
|
||||
<pre>soundstretch stdin -bpm</pre>
|
||||
</blockquote>
|
||||
<hr>
|
||||
<h2>5. Change History</h2>
|
||||
<h3>5.1. SoundTouch library Change History </h3>
|
||||
|
||||
<p><strong>1.5.0:</strong></p>
|
||||
<ul>
|
||||
<li>Added normalization to correlation calculation and improvement automatic seek/sequence parameter calculation to improve sound quality</li>
|
||||
|
||||
<li>Bugfixes:
|
||||
<ul>
|
||||
<li>Fixed negative array indexing in quick seek algorithm</li>
|
||||
<li>FIR autoalias filter running too far in processing buffer</li>
|
||||
<li>Check against zero sample count in rate transposing</li>
|
||||
<li>Fix for x86-64 support: Removed pop/push instructions from the cpu detection algorithm. </li>
|
||||
<li>Check against empty buffers in FIFOSampleBuffer</li>
|
||||
<li>Other minor fixes & code cleanup</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li>Fixes in compilation scripts for non-Intel platforms</li>
|
||||
<li>Added Dynamic-Link-Library (DLL) version of SoundTouch library build,
|
||||
provided with Delphi/Pascal wrapper for calling the dll routines</li>
|
||||
<li>Added #define PREVENT_CLICK_AT_RATE_CROSSOVER that prevents a click artifact
|
||||
when crossing the nominal pitch from either positive to negative side or vice
|
||||
versa</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<p><strong>1.4.1:</strong></p>
|
||||
<ul>
|
||||
<li>Fixed a buffer overflow bug in BPM detect algorithm routines if processing
|
||||
more than 2048 samples at one call </li>
|
||||
|
||||
</ul>
|
||||
|
||||
<p><strong>1.4.0:</strong></p>
|
||||
<ul>
|
||||
<li>Improved sound quality by automatic calculation of time stretch algorithm
|
||||
processing parameters according to tempo setting</li>
|
||||
<li>Moved BPM detection routines from SoundStretch application into SoundTouch
|
||||
library</li>
|
||||
<li>Bugfixes: Usage of uninitialied variables, GNU build scripts, compiler errors
|
||||
due to 'const' keyword mismatch.</li>
|
||||
<li>Source code cleanup</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<p><strong>v1.3.1:
|
||||
</strong></p>
|
||||
<ul>
|
||||
<li>Changed static class declaration to GCC 4.x compiler compatible syntax.</li>
|
||||
<li>Enabled MMX/SSE-optimized routines also for GCC compilers. Earlier
|
||||
the MMX/SSE-optimized routines were written in compiler-specific inline
|
||||
assembler, now these routines are migrated to use compiler intrinsic
|
||||
syntax which allows compiling the same MMX/SSE-optimized source code with
|
||||
both Visual C++ and GCC compilers. </li>
|
||||
<li>Set floating point as the default sample format and added switch to
|
||||
the GNU configure script for selecting the other sample format.</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<p><strong>v1.3.0:
|
||||
</strong></p>
|
||||
<ul>
|
||||
<li>Fixed tempo routine output duration inaccuracy due to rounding
|
||||
error </li>
|
||||
<li>Implemented separate processing routines for integer and
|
||||
floating arithmetic to allow improvements to floating point routines
|
||||
(earlier used algorithms mostly optimized for integer arithmetic also
|
||||
for floating point samples) </li>
|
||||
<li>Fixed a bug that distorts sound if sample rate changes during the
|
||||
sound stream </li>
|
||||
<li>Fixed a memory leak that appeared in MMX/SSE/3DNow! optimized
|
||||
routines </li>
|
||||
<li>Reduced redundant code pieces in MMX/SSE/3DNow! optimized
|
||||
routines vs. the standard C routines.</li>
|
||||
<li>MMX routine incompatibility with new gcc compiler versions </li>
|
||||
<li>Other miscellaneous bug fixes </li>
|
||||
</ul>
|
||||
<p><strong>v1.2.1: </strong></p>
|
||||
<ul>
|
||||
<li>Added automake/autoconf scripts for GNU
|
||||
platforms (in courtesy of David Durham)</li>
|
||||
<li>Fixed SCALE overflow bug in rate transposer
|
||||
routine.</li>
|
||||
<li>Fixed 64bit address space bugs.</li>
|
||||
<li>Created a 'soundtouch' namespace for
|
||||
SAMPLETYPE definitions.</li>
|
||||
</ul>
|
||||
<p><strong>v1.2.0: </strong></p>
|
||||
<ul>
|
||||
<li>Added support for 32bit floating point sample
|
||||
data type with SSE/3DNow! optimizations for Win32 platform (SSE/3DNow! optimizations currently not supported in GCC environment)</li>
|
||||
<li>Replaced 'make-gcc' script for GNU environment
|
||||
by master Makefile</li>
|
||||
<li>Added time-stretch routine configurability to
|
||||
SoundTouch main class</li>
|
||||
<li>Bugfixes</li>
|
||||
</ul>
|
||||
<p><strong>v1.1.1: </strong></p>
|
||||
<ul>
|
||||
<li>Moved SoundTouch under lesser GPL license (LGPL). This allows using SoundTouch library in programs that aren't
|
||||
released under GPL license. </li>
|
||||
<li>Changed MMX routine organiation so that MMX optimized routines are now implemented in classes that are derived from
|
||||
the basic classes having the standard non-mmx routines. </li>
|
||||
<li>MMX routines to support gcc version 3. </li>
|
||||
<li>Replaced windows makefiles by script using the .dsw files </li>
|
||||
</ul>
|
||||
<p><strong>v1.01: </strong></p>
|
||||
<ul>
|
||||
<li>"mmx_gcc.cpp": Added "using namespace std" and
|
||||
removed "return 0" from a function with void return value to fix
|
||||
compiler errors when compiling the library in Solaris environment. </li>
|
||||
<li>Moved file "FIFOSampleBuffer.h" to "include"
|
||||
directory to allow accessing the FIFOSampleBuffer class from external
|
||||
files. </li>
|
||||
</ul>
|
||||
<p><strong>v1.0: </strong></p>
|
||||
<ul>
|
||||
<li>Initial release </li>
|
||||
</ul>
|
||||
<p> </p>
|
||||
<h3>5.2. SoundStretch application Change
|
||||
History </h3>
|
||||
|
||||
<p><strong>1.4.0:</strong></p>
|
||||
<ul>
|
||||
<li>Moved BPM detection routines from SoundStretch application into SoundTouch
|
||||
library</li>
|
||||
<li>Allow using standard input/output pipes as audio processing input/output
|
||||
streams</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<p><strong>v1.3.0:</strong></p>
|
||||
<ul>
|
||||
<li>Simplified accessing WAV files with floating
|
||||
point sample format.
|
||||
</li>
|
||||
</ul>
|
||||
<p><strong>v1.2.1: </strong></p>
|
||||
<ul>
|
||||
<li>Fixed 64bit address space bugs.</li>
|
||||
</ul>
|
||||
<p><strong>v1.2.0: </strong></p>
|
||||
<ul>
|
||||
<li>Added support for 32bit floating point sample
|
||||
data type</li>
|
||||
<li>Restructured the BPM routines into separate
|
||||
library</li>
|
||||
<li>Fixed big-endian conversion bugs in WAV file
|
||||
routines (hopefully :)</li>
|
||||
</ul>
|
||||
<p><strong>v1.1.1: </strong></p>
|
||||
<ul>
|
||||
<li>Fixed bugs in WAV file reading & added
|
||||
byte-order conversion for big-endian processors. </li>
|
||||
<li>Moved SoundStretch source code under 'example'
|
||||
directory to highlight difference from SoundTouch stuff. </li>
|
||||
<li>Replaced windows makefiles by script using the .dsw files </li>
|
||||
<li>Output file name isn't required if output
|
||||
isn't desired (e.g. if using the switch '-bpm' in plain format only) </li>
|
||||
</ul>
|
||||
<p><strong>v1.1:</strong></p>
|
||||
<ul>
|
||||
<li>Fixed "Release" settings in Microsoft Visual
|
||||
C++ project file (.dsp) </li>
|
||||
<li>Added beats-per-minute (BPM) detection routine
|
||||
and command-line switch "-bpm" </li>
|
||||
</ul>
|
||||
<p><strong>v1.01: </strong></p>
|
||||
<ul>
|
||||
<li>Initial release </li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 >6. Acknowledgements </h2>
|
||||
<p >Kudos for these people who have contributed to development or submitted
|
||||
bugfixes since
|
||||
SoundTouch v1.3.1: </p>
|
||||
<ul>
|
||||
<li>Arthur A</li>
|
||||
<li>Richard Ash</li>
|
||||
<li>Stanislav Brabec</li>
|
||||
<li>Christian Budde</li>
|
||||
<li>Brian Cameron</li>
|
||||
<li>Jason Champion</li>
|
||||
<li>Patrick Colis</li>
|
||||
<li>Justin Frankel</li>
|
||||
<li>Jason Garland</li>
|
||||
<li>Takashi Iwai</li>
|
||||
<li>Paulo Pizarro</li>
|
||||
<li>RJ Ryan</li>
|
||||
<li>John Sheehy</li>
|
||||
</ul>
|
||||
<p >Moral greetings to all other contributors and users also!</p>
|
||||
<hr>
|
||||
<h2 >7. LICENSE </h2>
|
||||
<p>SoundTouch audio processing library<br>
|
||||
Copyright (c) Olli Parviainen</p>
|
||||
<p>This library is free software; you can
|
||||
redistribute it and/or modify it under the terms of the GNU
|
||||
Lesser General Public License version 2.1 as published by the Free Software
|
||||
Foundation.</p>
|
||||
<p>This library is distributed in the hope
|
||||
that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU Lesser General Public License for
|
||||
more details.</p>
|
||||
<p>You should have received a copy of the GNU
|
||||
Lesser General Public License along with this library; if not,
|
||||
write to the Free Software Foundation, Inc., 59 Temple Place,
|
||||
Suite 330, Boston, MA 02111-1307 USA</p>
|
||||
<hr>
|
||||
<!--
|
||||
$Id: README.html 81 2009-12-28 20:51:18Z oparviai $
|
||||
-->
|
||||
</body>
|
||||
</html>
|
|
@ -10,10 +10,10 @@
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/03/19 10:05:49 $
|
||||
// File revision : $Revision: 1.13 $
|
||||
// Last changed : $Date: 2009-10-31 16:37:24 +0200 (Sat, 31 Oct 2009) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: RateTransposer.cpp,v 1.13 2006/03/19 10:05:49 Olli Exp $
|
||||
// $Id: RateTransposer.cpp 74 2009-10-31 14:37:24Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -42,10 +42,11 @@
|
|||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
#include <stdexcept>
|
||||
#include "RateTransposer.h"
|
||||
#include "AAFilter.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace soundtouch;
|
||||
|
||||
|
||||
|
@ -55,7 +56,7 @@ class RateTransposerInteger : public RateTransposer
|
|||
{
|
||||
protected:
|
||||
int iSlopeCount;
|
||||
uint uRate;
|
||||
int iRate;
|
||||
SAMPLETYPE sPrevSampleL, sPrevSampleR;
|
||||
|
||||
virtual void resetRegisters();
|
||||
|
@ -84,7 +85,6 @@ class RateTransposerFloat : public RateTransposer
|
|||
{
|
||||
protected:
|
||||
float fSlopeCount;
|
||||
float fRateStep;
|
||||
SAMPLETYPE sPrevSampleL, sPrevSampleR;
|
||||
|
||||
virtual void resetRegisters();
|
||||
|
@ -103,18 +103,12 @@ public:
|
|||
|
||||
|
||||
|
||||
#ifndef min
|
||||
#define min(a,b) ((a > b) ? b : a)
|
||||
#define max(a,b) ((a < b) ? b : a)
|
||||
#endif
|
||||
|
||||
|
||||
// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
||||
// depending on if we've a MMX/SSE/etc-capable CPU available or not.
|
||||
void * RateTransposer::operator new(size_t s)
|
||||
{
|
||||
// Notice! don't use "new TDStretch" directly, use "newInstance" to create a new instance instead!
|
||||
assert(FALSE);
|
||||
throw runtime_error("Error in RateTransoser::new: don't use \"new TDStretch\" directly, use \"newInstance\" to create a new instance instead!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -132,8 +126,9 @@ RateTransposer *RateTransposer::newInstance()
|
|||
// Constructor
|
||||
RateTransposer::RateTransposer() : FIFOProcessor(&outputBuffer)
|
||||
{
|
||||
uChannels = 2;
|
||||
numChannels = 2;
|
||||
bUseAAFilter = TRUE;
|
||||
fRate = 0;
|
||||
|
||||
// Instantiates the anti-alias filter with default tap length
|
||||
// of 32
|
||||
|
@ -150,7 +145,7 @@ RateTransposer::~RateTransposer()
|
|||
|
||||
|
||||
/// Enables/disables the anti-alias filter. Zero to disable, nonzero to enable
|
||||
void RateTransposer::enableAAFilter(const BOOL newMode)
|
||||
void RateTransposer::enableAAFilter(BOOL newMode)
|
||||
{
|
||||
bUseAAFilter = newMode;
|
||||
}
|
||||
|
@ -163,18 +158,18 @@ BOOL RateTransposer::isAAFilterEnabled() const
|
|||
}
|
||||
|
||||
|
||||
AAFilter *RateTransposer::getAAFilter() const
|
||||
AAFilter *RateTransposer::getAAFilter()
|
||||
{
|
||||
return pAAFilter;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets new target uRate. Normal uRate = 1.0, smaller values represent slower
|
||||
// uRate, larger faster uRates.
|
||||
// Sets new target iRate. Normal iRate = 1.0, smaller values represent slower
|
||||
// iRate, larger faster iRates.
|
||||
void RateTransposer::setRate(float newRate)
|
||||
{
|
||||
float fCutoff;
|
||||
double fCutoff;
|
||||
|
||||
fRate = newRate;
|
||||
|
||||
|
@ -197,45 +192,47 @@ void RateTransposer::setRate(float newRate)
|
|||
//
|
||||
// It's allowed for 'output' and 'input' parameters to point to the same
|
||||
// memory position.
|
||||
/*
|
||||
void RateTransposer::flushStoreBuffer()
|
||||
{
|
||||
if (storeBuffer.isEmpty()) return;
|
||||
|
||||
outputBuffer.moveSamples(storeBuffer);
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
// Adds 'numSamples' pcs of samples from the 'samples' memory position into
|
||||
// Adds 'nSamples' pcs of samples from the 'samples' memory position into
|
||||
// the input of the object.
|
||||
void RateTransposer::putSamples(const SAMPLETYPE *samples, uint numSamples)
|
||||
void RateTransposer::putSamples(const SAMPLETYPE *samples, uint nSamples)
|
||||
{
|
||||
processSamples(samples, numSamples);
|
||||
processSamples(samples, nSamples);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Transposes up the sample rate, causing the observed playback 'rate' of the
|
||||
// sound to decrease
|
||||
void RateTransposer::upsample(const SAMPLETYPE *src, uint numSamples)
|
||||
void RateTransposer::upsample(const SAMPLETYPE *src, uint nSamples)
|
||||
{
|
||||
int count, sizeTemp, num;
|
||||
uint count, sizeTemp, num;
|
||||
|
||||
// If the parameter 'uRate' value is smaller than 'SCALE', first transpose
|
||||
// the samples and then apply the anti-alias filter to remove aliasing.
|
||||
|
||||
// First check that there's enough room in 'storeBuffer'
|
||||
// (+16 is to reserve some slack in the destination buffer)
|
||||
sizeTemp = (int)((float)numSamples / fRate + 16.0f);
|
||||
sizeTemp = (uint)((float)nSamples / fRate + 16.0f);
|
||||
|
||||
// Transpose the samples, store the result into the end of "storeBuffer"
|
||||
count = transpose(storeBuffer.ptrEnd(sizeTemp), src, numSamples);
|
||||
count = transpose(storeBuffer.ptrEnd(sizeTemp), src, nSamples);
|
||||
storeBuffer.putSamples(count);
|
||||
|
||||
// Apply the anti-alias filter to samples in "store output", output the
|
||||
// result to "dest"
|
||||
num = storeBuffer.numSamples();
|
||||
count = pAAFilter->evaluate(outputBuffer.ptrEnd(num),
|
||||
storeBuffer.ptrBegin(), num, uChannels);
|
||||
storeBuffer.ptrBegin(), num, (uint)numChannels);
|
||||
outputBuffer.putSamples(count);
|
||||
|
||||
// Remove the processed samples from "storeBuffer"
|
||||
|
@ -245,16 +242,16 @@ void RateTransposer::upsample(const SAMPLETYPE *src, uint numSamples)
|
|||
|
||||
// Transposes down the sample rate, causing the observed playback 'rate' of the
|
||||
// sound to increase
|
||||
void RateTransposer::downsample(const SAMPLETYPE *src, uint numSamples)
|
||||
void RateTransposer::downsample(const SAMPLETYPE *src, uint nSamples)
|
||||
{
|
||||
int count, sizeTemp;
|
||||
uint count, sizeTemp;
|
||||
|
||||
// If the parameter 'uRate' value is larger than 'SCALE', first apply the
|
||||
// anti-alias filter to remove high frequencies (prevent them from folding
|
||||
// over the lover frequencies), then transpose. */
|
||||
// over the lover frequencies), then transpose.
|
||||
|
||||
// Add the new samples to the end of the storeBuffer */
|
||||
storeBuffer.putSamples(src, numSamples);
|
||||
// Add the new samples to the end of the storeBuffer
|
||||
storeBuffer.putSamples(src, nSamples);
|
||||
|
||||
// Anti-alias filter the samples to prevent folding and output the filtered
|
||||
// data to tempBuffer. Note : because of the FIR filter length, the
|
||||
|
@ -263,13 +260,15 @@ void RateTransposer::downsample(const SAMPLETYPE *src, uint numSamples)
|
|||
sizeTemp = storeBuffer.numSamples();
|
||||
|
||||
count = pAAFilter->evaluate(tempBuffer.ptrEnd(sizeTemp),
|
||||
storeBuffer.ptrBegin(), sizeTemp, uChannels);
|
||||
storeBuffer.ptrBegin(), sizeTemp, (uint)numChannels);
|
||||
|
||||
if (count == 0) return;
|
||||
|
||||
// Remove the filtered samples from 'storeBuffer'
|
||||
storeBuffer.receiveSamples(count);
|
||||
|
||||
// Transpose the samples (+16 is to reserve some slack in the destination buffer)
|
||||
sizeTemp = (int)((float)numSamples / fRate + 16.0f);
|
||||
sizeTemp = (uint)((float)nSamples / fRate + 16.0f);
|
||||
count = transpose(outputBuffer.ptrEnd(sizeTemp), tempBuffer.ptrBegin(), count);
|
||||
outputBuffer.putSamples(count);
|
||||
}
|
||||
|
@ -279,20 +278,20 @@ void RateTransposer::downsample(const SAMPLETYPE *src, uint numSamples)
|
|||
// Returns amount of samples returned in the "dest" buffer.
|
||||
// The maximum amount of samples that can be returned at a time is set by
|
||||
// the 'set_returnBuffer_size' function.
|
||||
void RateTransposer::processSamples(const SAMPLETYPE *src, uint numSamples)
|
||||
void RateTransposer::processSamples(const SAMPLETYPE *src, uint nSamples)
|
||||
{
|
||||
uint count;
|
||||
uint sizeReq;
|
||||
|
||||
if (numSamples == 0) return;
|
||||
if (nSamples == 0) return;
|
||||
assert(pAAFilter);
|
||||
|
||||
// If anti-alias filter is turned off, simply transpose without applying
|
||||
// the filter
|
||||
if (bUseAAFilter == FALSE)
|
||||
{
|
||||
sizeReq = (int)((float)numSamples / fRate + 1.0f);
|
||||
count = transpose(outputBuffer.ptrEnd(sizeReq), src, numSamples);
|
||||
sizeReq = (uint)((float)nSamples / fRate + 1.0f);
|
||||
count = transpose(outputBuffer.ptrEnd(sizeReq), src, nSamples);
|
||||
outputBuffer.putSamples(count);
|
||||
return;
|
||||
}
|
||||
|
@ -300,41 +299,42 @@ void RateTransposer::processSamples(const SAMPLETYPE *src, uint numSamples)
|
|||
// Transpose with anti-alias filter
|
||||
if (fRate < 1.0f)
|
||||
{
|
||||
upsample(src, numSamples);
|
||||
upsample(src, nSamples);
|
||||
}
|
||||
else
|
||||
{
|
||||
downsample(src, numSamples);
|
||||
downsample(src, nSamples);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Transposes the sample rate of the given samples using linear interpolation.
|
||||
// Returns the number of samples returned in the "dest" buffer
|
||||
inline uint RateTransposer::transpose(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples)
|
||||
inline uint RateTransposer::transpose(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples)
|
||||
{
|
||||
if (uChannels == 2)
|
||||
if (numChannels == 2)
|
||||
{
|
||||
return transposeStereo(dest, src, numSamples);
|
||||
return transposeStereo(dest, src, nSamples);
|
||||
}
|
||||
else
|
||||
{
|
||||
return transposeMono(dest, src, numSamples);
|
||||
return transposeMono(dest, src, nSamples);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Sets the number of channels, 1 = mono, 2 = stereo
|
||||
void RateTransposer::setChannels(const uint numchannels)
|
||||
void RateTransposer::setChannels(int nChannels)
|
||||
{
|
||||
if (uChannels == numchannels) return;
|
||||
assert(nChannels > 0);
|
||||
if (numChannels == nChannels) return;
|
||||
|
||||
assert(numchannels == 1 || numchannels == 2);
|
||||
uChannels = numchannels;
|
||||
assert(nChannels == 1 || nChannels == 2);
|
||||
numChannels = nChannels;
|
||||
|
||||
storeBuffer.setChannels(uChannels);
|
||||
tempBuffer.setChannels(uChannels);
|
||||
outputBuffer.setChannels(uChannels);
|
||||
storeBuffer.setChannels(numChannels);
|
||||
tempBuffer.setChannels(numChannels);
|
||||
outputBuffer.setChannels(numChannels);
|
||||
|
||||
// Inits the linear interpolation registers
|
||||
resetRegisters();
|
||||
|
@ -350,7 +350,7 @@ void RateTransposer::clear()
|
|||
|
||||
|
||||
// Returns nonzero if there aren't any samples available for outputting.
|
||||
uint RateTransposer::isEmpty()
|
||||
int RateTransposer::isEmpty() const
|
||||
{
|
||||
int res;
|
||||
|
||||
|
@ -371,11 +371,10 @@ uint RateTransposer::isEmpty()
|
|||
// Constructor
|
||||
RateTransposerInteger::RateTransposerInteger() : RateTransposer()
|
||||
{
|
||||
// call these here as these are virtual functions; calling these
|
||||
// from the base class constructor wouldn't execute the overloaded
|
||||
// versions (<master yoda>peculiar C++ can be</my>).
|
||||
resetRegisters();
|
||||
setRate(1.0f);
|
||||
// Notice: use local function calling syntax for sake of clarity,
|
||||
// to indicate the fact that C++ constructor can't call virtual functions.
|
||||
RateTransposerInteger::resetRegisters();
|
||||
RateTransposerInteger::setRate(1.0f);
|
||||
}
|
||||
|
||||
|
||||
|
@ -396,12 +395,14 @@ void RateTransposerInteger::resetRegisters()
|
|||
// Transposes the sample rate of the given samples using linear interpolation.
|
||||
// 'Mono' version of the routine. Returns the number of samples returned in
|
||||
// the "dest" buffer
|
||||
uint RateTransposerInteger::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples)
|
||||
uint RateTransposerInteger::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples)
|
||||
{
|
||||
unsigned int i, used;
|
||||
LONG_SAMPLETYPE temp, vol1;
|
||||
|
||||
used = 0;
|
||||
if (nSamples == 0) return 0; // no samples, no work
|
||||
|
||||
used = 0;
|
||||
i = 0;
|
||||
|
||||
// Process the last sample saved from the previous call first...
|
||||
|
@ -411,7 +412,7 @@ uint RateTransposerInteger::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *sr
|
|||
temp = vol1 * sPrevSampleL + iSlopeCount * src[0];
|
||||
dest[i] = (SAMPLETYPE)(temp / SCALE);
|
||||
i++;
|
||||
iSlopeCount += uRate;
|
||||
iSlopeCount += iRate;
|
||||
}
|
||||
// now always (iSlopeCount > SCALE)
|
||||
iSlopeCount -= SCALE;
|
||||
|
@ -422,18 +423,18 @@ uint RateTransposerInteger::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *sr
|
|||
{
|
||||
iSlopeCount -= SCALE;
|
||||
used ++;
|
||||
if (used >= numSamples - 1) goto end;
|
||||
if (used >= nSamples - 1) goto end;
|
||||
}
|
||||
vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount);
|
||||
temp = src[used] * vol1 + iSlopeCount * src[used + 1];
|
||||
dest[i] = (SAMPLETYPE)(temp / SCALE);
|
||||
|
||||
i++;
|
||||
iSlopeCount += uRate;
|
||||
iSlopeCount += iRate;
|
||||
}
|
||||
end:
|
||||
// Store the last sample for the next round
|
||||
sPrevSampleL = src[numSamples - 1];
|
||||
sPrevSampleL = src[nSamples - 1];
|
||||
|
||||
return i;
|
||||
}
|
||||
|
@ -442,12 +443,12 @@ end:
|
|||
// Transposes the sample rate of the given samples using linear interpolation.
|
||||
// 'Stereo' version of the routine. Returns the number of samples returned in
|
||||
// the "dest" buffer
|
||||
uint RateTransposerInteger::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples)
|
||||
uint RateTransposerInteger::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples)
|
||||
{
|
||||
unsigned int srcPos, i, used;
|
||||
LONG_SAMPLETYPE temp, vol1;
|
||||
|
||||
if (numSamples == 0) return 0; // no samples, no work
|
||||
if (nSamples == 0) return 0; // no samples, no work
|
||||
|
||||
used = 0;
|
||||
i = 0;
|
||||
|
@ -461,7 +462,7 @@ uint RateTransposerInteger::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *
|
|||
temp = vol1 * sPrevSampleR + iSlopeCount * src[1];
|
||||
dest[2 * i + 1] = (SAMPLETYPE)(temp / SCALE);
|
||||
i++;
|
||||
iSlopeCount += uRate;
|
||||
iSlopeCount += iRate;
|
||||
}
|
||||
// now always (iSlopeCount > SCALE)
|
||||
iSlopeCount -= SCALE;
|
||||
|
@ -472,7 +473,7 @@ uint RateTransposerInteger::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *
|
|||
{
|
||||
iSlopeCount -= SCALE;
|
||||
used ++;
|
||||
if (used >= numSamples - 1) goto end;
|
||||
if (used >= nSamples - 1) goto end;
|
||||
}
|
||||
srcPos = 2 * used;
|
||||
vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount);
|
||||
|
@ -482,22 +483,22 @@ uint RateTransposerInteger::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *
|
|||
dest[2 * i + 1] = (SAMPLETYPE)(temp / SCALE);
|
||||
|
||||
i++;
|
||||
iSlopeCount += uRate;
|
||||
iSlopeCount += iRate;
|
||||
}
|
||||
end:
|
||||
// Store the last sample for the next round
|
||||
sPrevSampleL = src[2 * numSamples - 2];
|
||||
sPrevSampleR = src[2 * numSamples - 1];
|
||||
sPrevSampleL = src[2 * nSamples - 2];
|
||||
sPrevSampleR = src[2 * nSamples - 1];
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
// Sets new target uRate. Normal uRate = 1.0, smaller values represent slower
|
||||
// uRate, larger faster uRates.
|
||||
// Sets new target iRate. Normal iRate = 1.0, smaller values represent slower
|
||||
// iRate, larger faster iRates.
|
||||
void RateTransposerInteger::setRate(float newRate)
|
||||
{
|
||||
uRate = (int)(newRate * SCALE + 0.5f);
|
||||
iRate = (int)(newRate * SCALE + 0.5f);
|
||||
RateTransposer::setRate(newRate);
|
||||
}
|
||||
|
||||
|
@ -511,11 +512,10 @@ void RateTransposerInteger::setRate(float newRate)
|
|||
// Constructor
|
||||
RateTransposerFloat::RateTransposerFloat() : RateTransposer()
|
||||
{
|
||||
// call these here as these are virtual functions; calling these
|
||||
// from the base class constructor wouldn't execute the overloaded
|
||||
// versions (<master yoda>peculiar C++ can be</my>).
|
||||
resetRegisters();
|
||||
setRate(1.0f);
|
||||
// Notice: use local function calling syntax for sake of clarity,
|
||||
// to indicate the fact that C++ constructor can't call virtual functions.
|
||||
RateTransposerFloat::resetRegisters();
|
||||
RateTransposerFloat::setRate(1.0f);
|
||||
}
|
||||
|
||||
|
||||
|
@ -536,7 +536,7 @@ void RateTransposerFloat::resetRegisters()
|
|||
// Transposes the sample rate of the given samples using linear interpolation.
|
||||
// 'Mono' version of the routine. Returns the number of samples returned in
|
||||
// the "dest" buffer
|
||||
uint RateTransposerFloat::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples)
|
||||
uint RateTransposerFloat::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples)
|
||||
{
|
||||
unsigned int i, used;
|
||||
|
||||
|
@ -552,23 +552,24 @@ uint RateTransposerFloat::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src,
|
|||
}
|
||||
fSlopeCount -= 1.0f;
|
||||
|
||||
if (numSamples == 1) goto end;
|
||||
|
||||
while (1)
|
||||
if (nSamples > 1)
|
||||
{
|
||||
while (fSlopeCount > 1.0f)
|
||||
while (1)
|
||||
{
|
||||
fSlopeCount -= 1.0f;
|
||||
used ++;
|
||||
if (used >= numSamples - 1) goto end;
|
||||
while (fSlopeCount > 1.0f)
|
||||
{
|
||||
fSlopeCount -= 1.0f;
|
||||
used ++;
|
||||
if (used >= nSamples - 1) goto end;
|
||||
}
|
||||
dest[i] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[used] + fSlopeCount * src[used + 1]);
|
||||
i++;
|
||||
fSlopeCount += fRate;
|
||||
}
|
||||
dest[i] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[used] + fSlopeCount * src[used + 1]);
|
||||
i++;
|
||||
fSlopeCount += fRate;
|
||||
}
|
||||
end:
|
||||
// Store the last sample for the next round
|
||||
sPrevSampleL = src[numSamples - 1];
|
||||
sPrevSampleL = src[nSamples - 1];
|
||||
|
||||
return i;
|
||||
}
|
||||
|
@ -577,11 +578,11 @@ end:
|
|||
// Transposes the sample rate of the given samples using linear interpolation.
|
||||
// 'Mono' version of the routine. Returns the number of samples returned in
|
||||
// the "dest" buffer
|
||||
uint RateTransposerFloat::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples)
|
||||
uint RateTransposerFloat::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples)
|
||||
{
|
||||
unsigned int srcPos, i, used;
|
||||
|
||||
if (numSamples == 0) return 0; // no samples, no work
|
||||
if (nSamples == 0) return 0; // no samples, no work
|
||||
|
||||
used = 0;
|
||||
i = 0;
|
||||
|
@ -597,30 +598,31 @@ uint RateTransposerFloat::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *sr
|
|||
// now always (iSlopeCount > 1.0f)
|
||||
fSlopeCount -= 1.0f;
|
||||
|
||||
if (numSamples == 1) goto end;
|
||||
|
||||
while (1)
|
||||
if (nSamples > 1)
|
||||
{
|
||||
while (fSlopeCount > 1.0f)
|
||||
while (1)
|
||||
{
|
||||
fSlopeCount -= 1.0f;
|
||||
used ++;
|
||||
if (used >= numSamples - 1) goto end;
|
||||
while (fSlopeCount > 1.0f)
|
||||
{
|
||||
fSlopeCount -= 1.0f;
|
||||
used ++;
|
||||
if (used >= nSamples - 1) goto end;
|
||||
}
|
||||
srcPos = 2 * used;
|
||||
|
||||
dest[2 * i] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[srcPos]
|
||||
+ fSlopeCount * src[srcPos + 2]);
|
||||
dest[2 * i + 1] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[srcPos + 1]
|
||||
+ fSlopeCount * src[srcPos + 3]);
|
||||
|
||||
i++;
|
||||
fSlopeCount += fRate;
|
||||
}
|
||||
srcPos = 2 * used;
|
||||
|
||||
dest[2 * i] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[srcPos]
|
||||
+ fSlopeCount * src[srcPos + 2]);
|
||||
dest[2 * i + 1] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[srcPos + 1]
|
||||
+ fSlopeCount * src[srcPos + 3]);
|
||||
|
||||
i++;
|
||||
fSlopeCount += fRate;
|
||||
}
|
||||
end:
|
||||
// Store the last sample for the next round
|
||||
sPrevSampleL = src[2 * numSamples - 2];
|
||||
sPrevSampleR = src[2 * numSamples - 1];
|
||||
sPrevSampleL = src[2 * nSamples - 2];
|
||||
sPrevSampleR = src[2 * nSamples - 1];
|
||||
|
||||
return i;
|
||||
}
|
||||
|
|
|
@ -14,10 +14,10 @@
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.10 $
|
||||
// Last changed : $Date: 2009-02-21 18:00:14 +0200 (Sat, 21 Feb 2009) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: RateTransposer.h,v 1.10 2006/02/05 16:44:06 Olli Exp $
|
||||
// $Id: RateTransposer.h 63 2009-02-21 16:00:14Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -45,6 +45,7 @@
|
|||
#ifndef RateTransposer_H
|
||||
#define RateTransposer_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include "AAFilter.h"
|
||||
#include "FIFOSamplePipe.h"
|
||||
#include "FIFOSampleBuffer.h"
|
||||
|
@ -68,7 +69,7 @@ protected:
|
|||
|
||||
float fRate;
|
||||
|
||||
uint uChannels;
|
||||
int numChannels;
|
||||
|
||||
/// Buffer for collecting samples to feed the anti-alias filter between
|
||||
/// two batches
|
||||
|
@ -82,8 +83,6 @@ protected:
|
|||
|
||||
BOOL bUseAAFilter;
|
||||
|
||||
void init();
|
||||
|
||||
virtual void resetRegisters() = 0;
|
||||
|
||||
virtual uint transposeStereo(SAMPLETYPE *dest,
|
||||
|
@ -92,12 +91,10 @@ protected:
|
|||
virtual uint transposeMono(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
uint numSamples) = 0;
|
||||
uint transpose(SAMPLETYPE *dest,
|
||||
inline uint transpose(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
uint numSamples);
|
||||
|
||||
void flushStoreBuffer();
|
||||
|
||||
void downsample(const SAMPLETYPE *src,
|
||||
uint numSamples);
|
||||
void upsample(const SAMPLETYPE *src,
|
||||
|
@ -117,7 +114,7 @@ public:
|
|||
|
||||
/// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
||||
/// depending on if we're to use integer or floating point arithmetics.
|
||||
void *operator new(size_t s);
|
||||
static void *operator new(size_t s);
|
||||
|
||||
/// Use this function instead of "new" operator to create a new instance of this class.
|
||||
/// This function automatically chooses a correct implementation, depending on if
|
||||
|
@ -131,7 +128,7 @@ public:
|
|||
FIFOSamplePipe *getStore() { return &storeBuffer; };
|
||||
|
||||
/// Return anti-alias filter object
|
||||
AAFilter *getAAFilter() const;
|
||||
AAFilter *getAAFilter();
|
||||
|
||||
/// Enables/disables the anti-alias filter. Zero to disable, nonzero to enable
|
||||
void enableAAFilter(BOOL newMode);
|
||||
|
@ -144,7 +141,7 @@ public:
|
|||
virtual void setRate(float newRate);
|
||||
|
||||
/// Sets the number of channels, 1 = mono, 2 = stereo
|
||||
void setChannels(uint channels);
|
||||
void setChannels(int channels);
|
||||
|
||||
/// Adds 'numSamples' pcs of samples from the 'samples' memory position into
|
||||
/// the input of the object.
|
||||
|
@ -154,7 +151,7 @@ public:
|
|||
void clear();
|
||||
|
||||
/// Returns nonzero if there aren't any samples available for outputting.
|
||||
uint isEmpty();
|
||||
int isEmpty() const;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -8,10 +8,10 @@
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.16 $
|
||||
// Last changed : $Date: 2009-05-17 14:30:57 +0300 (Sun, 17 May 2009) $
|
||||
// File revision : $Revision: 3 $
|
||||
//
|
||||
// $Id: STTypes.h,v 1.16 2006/02/05 16:44:06 Olli Exp $
|
||||
// $Id: STTypes.h 70 2009-05-17 11:30:57Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -39,63 +39,12 @@
|
|||
#ifndef STTypes_H
|
||||
#define STTypes_H
|
||||
|
||||
//#define INTEGER_SAMPLES 1
|
||||
|
||||
typedef unsigned int uint;
|
||||
typedef unsigned long ulong;
|
||||
|
||||
#ifdef __x86_64__
|
||||
typedef unsigned long long ulongptr;
|
||||
#else
|
||||
typedef unsigned long ulongptr;
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __GNUC__
|
||||
// In GCC, include soundtouch_config.h made by config scritps
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#define HAVE_INTTYPES_H 1
|
||||
|
||||
/* Define to 1 if you have the `m' library (-lm). */
|
||||
#define HAVE_LIBM 1
|
||||
|
||||
/* Define to 1 if your system has a GNU libc compatible `malloc' function, and
|
||||
to 0 otherwise. */
|
||||
#define HAVE_MALLOC 1
|
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
#define HAVE_MEMORY_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#define HAVE_STDINT_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||
#define HAVE_STDLIB_H 1
|
||||
|
||||
/* Define to 1 if you have the <strings.h> header file. */
|
||||
#define HAVE_STRINGS_H 1
|
||||
|
||||
/* Define to 1 if you have the <string.h> header file. */
|
||||
#define HAVE_STRING_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||
#define HAVE_SYS_STAT_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||
#define HAVE_SYS_TYPES_H 1
|
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#define HAVE_UNISTD_H 1
|
||||
|
||||
/* Use Integer as Sample type */
|
||||
//#define INTEGER_SAMPLES 1
|
||||
|
||||
/* Define as the return type of signal handlers (`int' or `void'). */
|
||||
#define RETSIGTYPE void
|
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
#define STDC_HEADERS 1
|
||||
|
||||
#include "soundtouch_config.h"
|
||||
#endif
|
||||
|
||||
#ifndef _WINDEF_
|
||||
|
@ -103,19 +52,15 @@ typedef unsigned long ulongptr;
|
|||
|
||||
typedef int BOOL;
|
||||
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
#endif
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif
|
||||
|
||||
#endif // _WINDEF_
|
||||
|
||||
|
||||
namespace soundtouch
|
||||
{
|
||||
|
||||
/// Activate these undef's to overrule the possible sampletype
|
||||
/// setting inherited from some other header file:
|
||||
//#undef INTEGER_SAMPLES
|
||||
|
@ -142,22 +87,21 @@ namespace soundtouch
|
|||
|
||||
#endif
|
||||
|
||||
/// Define this to allow CPU-specific assembler optimizations. Notice that
|
||||
/// having this enabled on non-x86 platforms doesn't matter; the compiler can
|
||||
/// drop unsupported extensions on different platforms automatically.
|
||||
/// However, if you're having difficulties getting the optimized routines
|
||||
/// compiled with your compler (e.g. some gcc compiler versions may be picky),
|
||||
/// you may wish to disable the optimizations to make the library compile.
|
||||
#if !defined(_MSC_VER) || !defined(__x86_64__)
|
||||
#define ALLOW_OPTIMIZATIONS 1
|
||||
#define ALLOW_NONEXACT_SIMD_OPTIMIZATION 1
|
||||
#endif
|
||||
#if (WIN32 || __i386__ || __x86_64__)
|
||||
/// Define this to allow X86-specific assembler/intrinsic optimizations.
|
||||
/// Notice that library contains also usual C++ versions of each of these
|
||||
/// these routines, so if you're having difficulties getting the optimized
|
||||
/// routines compiled for whatever reason, you may disable these optimizations
|
||||
/// to make the library compile.
|
||||
|
||||
#define ALLOW_X86_OPTIMIZATIONS 1
|
||||
|
||||
#endif
|
||||
|
||||
// If defined, allows the SIMD-optimized routines to take minor shortcuts
|
||||
// for improved performance. Undefine to require faithfully similar SIMD
|
||||
// calculations as in normal C implementation.
|
||||
|
||||
#define ALLOW_NONEXACT_SIMD_OPTIMIZATION 1
|
||||
|
||||
|
||||
#ifdef INTEGER_SAMPLES
|
||||
|
@ -171,11 +115,9 @@ namespace soundtouch
|
|||
#error "conflicting sample types defined"
|
||||
#endif // FLOAT_SAMPLES
|
||||
|
||||
#ifdef ALLOW_OPTIMIZATIONS
|
||||
#if (_WIN32 || __i386__ || __x86_64__)
|
||||
// Allow MMX optimizations
|
||||
#define ALLOW_MMX 1
|
||||
#endif
|
||||
#ifdef ALLOW_X86_OPTIMIZATIONS
|
||||
// Allow MMX optimizations
|
||||
#define ALLOW_MMX 1
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
@ -185,18 +127,23 @@ namespace soundtouch
|
|||
// data type for sample accumulation: Use double to utilize full precision.
|
||||
typedef double LONG_SAMPLETYPE;
|
||||
|
||||
#ifdef ALLOW_OPTIMIZATIONS
|
||||
#ifdef ALLOW_X86_OPTIMIZATIONS
|
||||
// Allow 3DNow! and SSE optimizations
|
||||
#if _WIN32
|
||||
// #define ALLOW_3DNOW 1
|
||||
#if WIN32
|
||||
#define ALLOW_3DNOW 1
|
||||
#endif
|
||||
|
||||
#if (_WIN32 || __i386__ || __x86_64__)
|
||||
#define ALLOW_SSE 1
|
||||
#endif
|
||||
#define ALLOW_SSE 1
|
||||
#endif
|
||||
|
||||
#endif // INTEGER_SAMPLES
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
// When this #define is active, eliminates a clicking sound when the "rate" or "pitch"
|
||||
// parameter setting crosses from value <1 to >=1 or vice versa during processing.
|
||||
// Default is off as such crossover is untypical case and involves a slight sound
|
||||
// quality compromise.
|
||||
//#define PREVENT_CLICK_AT_RATE_CROSSOVER 1
|
||||
|
||||
#endif
|
||||
|
|
|
@ -41,10 +41,10 @@
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.13 $
|
||||
// Last changed : $Date: 2009-05-19 07:57:30 +0300 (Tue, 19 May 2009) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: SoundTouch.cpp,v 1.13 2006/02/05 16:44:06 Olli Exp $
|
||||
// $Id: SoundTouch.cpp 73 2009-05-19 04:57:30Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -82,8 +82,12 @@
|
|||
#include "cpu_detect.h"
|
||||
|
||||
using namespace soundtouch;
|
||||
|
||||
/// test if two floating point numbers are equal
|
||||
#define TEST_FLOAT_EQUAL(a, b) (fabs(a - b) < 1e-10)
|
||||
|
||||
/// Print library version string
|
||||
|
||||
/// Print library version string for autoconf
|
||||
extern "C" void soundtouch_ac_test()
|
||||
{
|
||||
printf("SoundTouch Version: %s\n",SOUNDTOUCH_VERSION);
|
||||
|
@ -145,8 +149,8 @@ void SoundTouch::setChannels(uint numChannels)
|
|||
throw std::runtime_error("Illegal number of channels");
|
||||
}
|
||||
channels = numChannels;
|
||||
pRateTransposer->setChannels(numChannels);
|
||||
pTDStretch->setChannels(numChannels);
|
||||
pRateTransposer->setChannels((int)numChannels);
|
||||
pTDStretch->setChannels((int)numChannels);
|
||||
}
|
||||
|
||||
|
||||
|
@ -236,10 +240,28 @@ void SoundTouch::calcEffectiveRateAndTempo()
|
|||
tempo = virtualTempo / virtualPitch;
|
||||
rate = virtualPitch * virtualRate;
|
||||
|
||||
if (rate != oldRate) pRateTransposer->setRate(rate);
|
||||
if (tempo != oldTempo) pTDStretch->setTempo(tempo);
|
||||
if (!TEST_FLOAT_EQUAL(rate,oldRate)) pRateTransposer->setRate(rate);
|
||||
if (!TEST_FLOAT_EQUAL(tempo, oldTempo)) pTDStretch->setTempo(tempo);
|
||||
|
||||
if (rate > 1.0f)
|
||||
#ifndef PREVENT_CLICK_AT_RATE_CROSSOVER
|
||||
if (rate <= 1.0f)
|
||||
{
|
||||
if (output != pTDStretch)
|
||||
{
|
||||
FIFOSamplePipe *tempoOut;
|
||||
|
||||
assert(output == pRateTransposer);
|
||||
// move samples in the current output buffer to the output of pTDStretch
|
||||
tempoOut = pTDStretch->getOutput();
|
||||
tempoOut->moveSamples(*output);
|
||||
// move samples in pitch transposer's store buffer to tempo changer's input
|
||||
pTDStretch->moveSamples(*pRateTransposer->getStore());
|
||||
|
||||
output = pTDStretch;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if (output != pRateTransposer)
|
||||
{
|
||||
|
@ -255,23 +277,6 @@ void SoundTouch::calcEffectiveRateAndTempo()
|
|||
output = pRateTransposer;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (output != pTDStretch)
|
||||
{
|
||||
FIFOSamplePipe *tempoOut;
|
||||
|
||||
assert(output == pRateTransposer);
|
||||
// move samples in the current output buffer to the output of pTDStretch
|
||||
tempoOut = pTDStretch->getOutput();
|
||||
tempoOut->moveSamples(*output);
|
||||
// move samples in pitch transposer's store buffer to tempo changer's input
|
||||
pTDStretch->moveSamples(*pRateTransposer->getStore());
|
||||
|
||||
output = pTDStretch;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -280,13 +285,13 @@ void SoundTouch::setSampleRate(uint srate)
|
|||
{
|
||||
bSrateSet = TRUE;
|
||||
// set sample rate, leave other tempo changer parameters as they are.
|
||||
pTDStretch->setParameters(srate);
|
||||
pTDStretch->setParameters((int)srate);
|
||||
}
|
||||
|
||||
|
||||
// Adds 'numSamples' pcs of samples from the 'samples' memory position into
|
||||
// the input of the object.
|
||||
void SoundTouch::putSamples(const SAMPLETYPE *samples, uint numSamples)
|
||||
void SoundTouch::putSamples(const SAMPLETYPE *samples, uint nSamples)
|
||||
{
|
||||
if (bSrateSet == FALSE)
|
||||
{
|
||||
|
@ -309,22 +314,23 @@ void SoundTouch::putSamples(const SAMPLETYPE *samples, uint numSamples)
|
|||
// (may happen if 'rate' changes from a non-zero value to zero)
|
||||
pTDStretch->moveSamples(*pRateTransposer);
|
||||
}
|
||||
pTDStretch->putSamples(samples, numSamples);
|
||||
pTDStretch->putSamples(samples, nSamples);
|
||||
}
|
||||
*/
|
||||
#ifndef PREVENT_CLICK_AT_RATE_CROSSOVER
|
||||
else if (rate <= 1.0f)
|
||||
{
|
||||
// transpose the rate down, output the transposed sound to tempo changer buffer
|
||||
assert(output == pTDStretch);
|
||||
pRateTransposer->putSamples(samples, numSamples);
|
||||
pRateTransposer->putSamples(samples, nSamples);
|
||||
pTDStretch->moveSamples(*pRateTransposer);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
assert(rate > 1.0f);
|
||||
// evaluate the tempo changer, then transpose the rate up,
|
||||
assert(output == pRateTransposer);
|
||||
pTDStretch->putSamples(samples, numSamples);
|
||||
pTDStretch->putSamples(samples, nSamples);
|
||||
pRateTransposer->moveSamples(*pTDStretch);
|
||||
}
|
||||
}
|
||||
|
@ -366,9 +372,9 @@ void SoundTouch::flush()
|
|||
|
||||
// Changes a setting controlling the processing system behaviour. See the
|
||||
// 'SETTING_...' defines for available setting ID's.
|
||||
BOOL SoundTouch::setSetting(uint settingId, uint value)
|
||||
BOOL SoundTouch::setSetting(int settingId, int value)
|
||||
{
|
||||
uint sampleRate, sequenceMs, seekWindowMs, overlapMs;
|
||||
int sampleRate, sequenceMs, seekWindowMs, overlapMs;
|
||||
|
||||
// read current tdstretch routine parameters
|
||||
pTDStretch->getParameters(&sampleRate, &sequenceMs, &seekWindowMs, &overlapMs);
|
||||
|
@ -415,20 +421,20 @@ BOOL SoundTouch::setSetting(uint settingId, uint value)
|
|||
// 'SETTING_...' defines for available setting ID's.
|
||||
//
|
||||
// Returns the setting value.
|
||||
uint SoundTouch::getSetting(uint settingId) const
|
||||
int SoundTouch::getSetting(int settingId) const
|
||||
{
|
||||
uint temp;
|
||||
int temp;
|
||||
|
||||
switch (settingId)
|
||||
{
|
||||
case SETTING_USE_AA_FILTER :
|
||||
return pRateTransposer->isAAFilterEnabled();
|
||||
return (uint)pRateTransposer->isAAFilterEnabled();
|
||||
|
||||
case SETTING_AA_FILTER_LENGTH :
|
||||
return pRateTransposer->getAAFilter()->getLength();
|
||||
|
||||
case SETTING_USE_QUICKSEEK :
|
||||
return pTDStretch->isQuickSeekEnabled();
|
||||
return (uint) pTDStretch->isQuickSeekEnabled();
|
||||
|
||||
case SETTING_SEQUENCE_MS:
|
||||
pTDStretch->getParameters(NULL, &temp, NULL, NULL);
|
||||
|
|
|
@ -41,10 +41,10 @@
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.14 $
|
||||
// Last changed : $Date: 2009-12-28 22:10:14 +0200 (Mon, 28 Dec 2009) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: SoundTouch.h,v 1.14 2006/02/05 16:44:06 Olli Exp $
|
||||
// $Id: SoundTouch.h 78 2009-12-28 20:10:14Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -79,10 +79,10 @@ namespace soundtouch
|
|||
{
|
||||
|
||||
/// Soundtouch library version string
|
||||
#define SOUNDTOUCH_VERSION "1.3.1"
|
||||
#define SOUNDTOUCH_VERSION "1.5.0"
|
||||
|
||||
/// SoundTouch library version id
|
||||
#define SOUNDTOUCH_VERSION_ID 010301
|
||||
#define SOUNDTOUCH_VERSION_ID (10500)
|
||||
|
||||
//
|
||||
// Available setting IDs for the 'setSetting' & 'get_setting' functions:
|
||||
|
@ -223,16 +223,16 @@ public:
|
|||
/// 'SETTING_...' defines for available setting ID's.
|
||||
///
|
||||
/// \return 'TRUE' if the setting was succesfully changed
|
||||
BOOL setSetting(uint settingId, ///< Setting ID number. see SETTING_... defines.
|
||||
uint value ///< New setting value.
|
||||
BOOL setSetting(int settingId, ///< Setting ID number. see SETTING_... defines.
|
||||
int value ///< New setting value.
|
||||
);
|
||||
|
||||
/// Reads a setting controlling the processing system behaviour. See the
|
||||
/// 'SETTING_...' defines for available setting ID's.
|
||||
///
|
||||
/// \return the setting value.
|
||||
uint getSetting(uint settingId ///< Setting ID number, see SETTING_... defines.
|
||||
) const;
|
||||
int getSetting(int settingId ///< Setting ID number, see SETTING_... defines.
|
||||
) const;
|
||||
|
||||
/// Returns number of samples currently unprocessed.
|
||||
virtual uint numUnprocessedSamples() const;
|
||||
|
|
|
@ -13,10 +13,10 @@
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.24 $
|
||||
// Last changed : $Date: 2009-12-28 21:27:04 +0200 (Mon, 28 Dec 2009) $
|
||||
// File revision : $Revision: 1.12 $
|
||||
//
|
||||
// $Id: TDStretch.cpp,v 1.24 2006/02/05 16:44:06 Olli Exp $
|
||||
// $Id: TDStretch.cpp 77 2009-12-28 19:27:04Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -42,23 +42,21 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <memory.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "STTypes.h"
|
||||
#include "cpu_detect.h"
|
||||
#include "TDStretch.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
using namespace soundtouch;
|
||||
|
||||
#ifndef min
|
||||
#define min(a,b) ((a > b) ? b : a)
|
||||
#define max(a,b) ((a < b) ? b : a)
|
||||
#endif
|
||||
|
||||
#define max(x, y) (((x) > (y)) ? (x) : (y))
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
|
@ -67,17 +65,18 @@ using namespace soundtouch;
|
|||
*
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
// Table for the hierarchical mixing position seeking algorithm
|
||||
int scanOffsets[4][24]={
|
||||
{ 124, 186, 248, 310, 372, 434, 496, 558, 620, 682, 744, 806,
|
||||
868, 930, 992, 1054, 1116, 1178, 1240, 1302, 1364, 1426, 1488, 0},
|
||||
static const short _scanOffsets[5][24]={
|
||||
{ 124, 186, 248, 310, 372, 434, 496, 558, 620, 682, 744, 806,
|
||||
868, 930, 992, 1054, 1116, 1178, 1240, 1302, 1364, 1426, 1488, 0},
|
||||
{-100, -75, -50, -25, 25, 50, 75, 100, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{ -20, -15, -10, -5, 5, 10, 15, 20, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{ -4, -3, -2, -1, 1, 2, 3, 4, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{ 121, 114, 97, 114, 98, 105, 108, 32, 104, 99, 117, 111,
|
||||
116, 100, 110, 117, 111, 115, 0, 0, 0, 0, 0, 0}};
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
|
@ -88,19 +87,25 @@ int scanOffsets[4][24]={
|
|||
|
||||
TDStretch::TDStretch() : FIFOProcessor(&outputBuffer)
|
||||
{
|
||||
bQuickseek = FALSE;
|
||||
bQuickSeek = FALSE;
|
||||
channels = 2;
|
||||
bMidBufferDirty = FALSE;
|
||||
|
||||
pMidBuffer = NULL;
|
||||
pRefMidBufferUnaligned = NULL;
|
||||
overlapLength = 0;
|
||||
|
||||
bAutoSeqSetting = TRUE;
|
||||
bAutoSeekSetting = TRUE;
|
||||
|
||||
// outDebt = 0;
|
||||
skipFract = 0;
|
||||
|
||||
tempo = 1.0f;
|
||||
setParameters(48000, DEFAULT_SEQUENCE_MS, DEFAULT_SEEKWINDOW_MS, DEFAULT_OVERLAP_MS);
|
||||
|
||||
setTempo(1.0f);
|
||||
}
|
||||
|
||||
clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -111,14 +116,6 @@ TDStretch::~TDStretch()
|
|||
}
|
||||
|
||||
|
||||
|
||||
// Calculates the x having the closest 2^x value for the given value
|
||||
static int _getClosest2Power(double value)
|
||||
{
|
||||
return (int)(log(value) / log(2.0) + 0.5);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets routine control parameters. These control are certain time constants
|
||||
// defining how the sound is stretched to the desired duration.
|
||||
|
@ -129,18 +126,36 @@ static int _getClosest2Power(double value)
|
|||
// position (default = 28 ms)
|
||||
// 'overlapMS' = overlapping length (default = 12 ms)
|
||||
|
||||
void TDStretch::setParameters(uint aSampleRate, uint aSequenceMS,
|
||||
uint aSeekWindowMS, uint aOverlapMS)
|
||||
void TDStretch::setParameters(int aSampleRate, int aSequenceMS,
|
||||
int aSeekWindowMS, int aOverlapMS)
|
||||
{
|
||||
this->sampleRate = aSampleRate;
|
||||
this->sequenceMs = aSequenceMS;
|
||||
this->seekWindowMs = aSeekWindowMS;
|
||||
this->overlapMs = aOverlapMS;
|
||||
// accept only positive parameter values - if zero or negative, use old values instead
|
||||
if (aSampleRate > 0) this->sampleRate = aSampleRate;
|
||||
if (aOverlapMS > 0) this->overlapMs = aOverlapMS;
|
||||
|
||||
seekLength = (sampleRate * seekWindowMs) / 1000;
|
||||
seekWindowLength = (sampleRate * sequenceMs) / 1000;
|
||||
if (aSequenceMS > 0)
|
||||
{
|
||||
this->sequenceMs = aSequenceMS;
|
||||
bAutoSeqSetting = FALSE;
|
||||
}
|
||||
else if (aSequenceMS == 0)
|
||||
{
|
||||
// if zero, use automatic setting
|
||||
bAutoSeqSetting = TRUE;
|
||||
}
|
||||
|
||||
maxOffset = seekLength;
|
||||
if (aSeekWindowMS > 0)
|
||||
{
|
||||
this->seekWindowMs = aSeekWindowMS;
|
||||
bAutoSeekSetting = FALSE;
|
||||
}
|
||||
else if (aSeekWindowMS == 0)
|
||||
{
|
||||
// if zero, use automatic setting
|
||||
bAutoSeekSetting = TRUE;
|
||||
}
|
||||
|
||||
calcSeqParameters();
|
||||
|
||||
calculateOverlapLength(overlapMs);
|
||||
|
||||
|
@ -154,7 +169,7 @@ void TDStretch::setParameters(uint aSampleRate, uint aSequenceMS,
|
|||
/// Get routine control parameters, see setParameters() function.
|
||||
/// Any of the parameters to this function can be NULL, in such case corresponding parameter
|
||||
/// value isn't returned.
|
||||
void TDStretch::getParameters(uint *pSampleRate, uint *pSequenceMs, uint *pSeekWindowMs, uint *pOverlapMs)
|
||||
void TDStretch::getParameters(int *pSampleRate, int *pSequenceMs, int *pSeekWindowMs, int *pOverlapMs) const
|
||||
{
|
||||
if (pSampleRate)
|
||||
{
|
||||
|
@ -163,12 +178,12 @@ void TDStretch::getParameters(uint *pSampleRate, uint *pSequenceMs, uint *pSeekW
|
|||
|
||||
if (pSequenceMs)
|
||||
{
|
||||
*pSequenceMs = sequenceMs;
|
||||
*pSequenceMs = (bAutoSeqSetting) ? (USE_AUTO_SEQUENCE_LEN) : sequenceMs;
|
||||
}
|
||||
|
||||
if (pSeekWindowMs)
|
||||
{
|
||||
*pSeekWindowMs = seekWindowMs;
|
||||
*pSeekWindowMs = (bAutoSeekSetting) ? (USE_AUTO_SEEKWINDOW_LEN) : seekWindowMs;
|
||||
}
|
||||
|
||||
if (pOverlapMs)
|
||||
|
@ -178,15 +193,15 @@ void TDStretch::getParameters(uint *pSampleRate, uint *pSequenceMs, uint *pSeekW
|
|||
}
|
||||
|
||||
|
||||
// Overlaps samples in 'midBuffer' with the samples in 'input'
|
||||
void TDStretch::overlapMono(SAMPLETYPE *output, const SAMPLETYPE *input) const
|
||||
// Overlaps samples in 'midBuffer' with the samples in 'pInput'
|
||||
void TDStretch::overlapMono(SAMPLETYPE *pOutput, const SAMPLETYPE *pInput) const
|
||||
{
|
||||
int i, itemp;
|
||||
|
||||
for (i = 0; i < (int)overlapLength ; i ++)
|
||||
for (i = 0; i < overlapLength ; i ++)
|
||||
{
|
||||
itemp = overlapLength - i;
|
||||
output[i] = (input[i] * i + pMidBuffer[i] * itemp ) / overlapLength; // >> overlapDividerBits;
|
||||
pOutput[i] = (pInput[i] * i + pMidBuffer[i] * itemp ) / overlapLength; // >> overlapDividerBits;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -194,11 +209,7 @@ void TDStretch::overlapMono(SAMPLETYPE *output, const SAMPLETYPE *input) const
|
|||
|
||||
void TDStretch::clearMidBuffer()
|
||||
{
|
||||
if (bMidBufferDirty)
|
||||
{
|
||||
memset(pMidBuffer, 0, 2 * sizeof(SAMPLETYPE) * overlapLength);
|
||||
bMidBufferDirty = FALSE;
|
||||
}
|
||||
memset(pMidBuffer, 0, 2 * sizeof(SAMPLETYPE) * overlapLength);
|
||||
}
|
||||
|
||||
|
||||
|
@ -213,8 +224,7 @@ void TDStretch::clearInput()
|
|||
void TDStretch::clear()
|
||||
{
|
||||
outputBuffer.clear();
|
||||
inputBuffer.clear();
|
||||
clearMidBuffer();
|
||||
clearInput();
|
||||
}
|
||||
|
||||
|
||||
|
@ -223,24 +233,24 @@ void TDStretch::clear()
|
|||
// to enable
|
||||
void TDStretch::enableQuickSeek(BOOL enable)
|
||||
{
|
||||
bQuickseek = enable;
|
||||
bQuickSeek = enable;
|
||||
}
|
||||
|
||||
|
||||
// Returns nonzero if the quick seeking algorithm is enabled.
|
||||
BOOL TDStretch::isQuickSeekEnabled() const
|
||||
{
|
||||
return bQuickseek;
|
||||
return bQuickSeek;
|
||||
}
|
||||
|
||||
|
||||
// Seeks for the optimal overlap-mixing position.
|
||||
uint TDStretch::seekBestOverlapPosition(const SAMPLETYPE *refPos)
|
||||
int TDStretch::seekBestOverlapPosition(const SAMPLETYPE *refPos)
|
||||
{
|
||||
if (channels == 2)
|
||||
{
|
||||
// stereo sound
|
||||
if (bQuickseek)
|
||||
if (bQuickSeek)
|
||||
{
|
||||
return seekBestOverlapPositionStereoQuick(refPos);
|
||||
}
|
||||
|
@ -252,7 +262,7 @@ uint TDStretch::seekBestOverlapPosition(const SAMPLETYPE *refPos)
|
|||
else
|
||||
{
|
||||
// mono sound
|
||||
if (bQuickseek)
|
||||
if (bQuickSeek)
|
||||
{
|
||||
return seekBestOverlapPositionMonoQuick(refPos);
|
||||
}
|
||||
|
@ -266,17 +276,17 @@ uint TDStretch::seekBestOverlapPosition(const SAMPLETYPE *refPos)
|
|||
|
||||
|
||||
|
||||
// Overlaps samples in 'midBuffer' with the samples in 'inputBuffer' at position
|
||||
// Overlaps samples in 'midBuffer' with the samples in 'pInputBuffer' at position
|
||||
// of 'ovlPos'.
|
||||
inline void TDStretch::overlap(SAMPLETYPE *output, const SAMPLETYPE *input, uint ovlPos) const
|
||||
inline void TDStretch::overlap(SAMPLETYPE *pOutput, const SAMPLETYPE *pInput, uint ovlPos) const
|
||||
{
|
||||
if (channels == 2)
|
||||
{
|
||||
// stereo sound
|
||||
overlapStereo(output, input + 2 * ovlPos);
|
||||
overlapStereo(pOutput, pInput + 2 * ovlPos);
|
||||
} else {
|
||||
// mono sound.
|
||||
overlapMono(output, input + ovlPos);
|
||||
overlapMono(pOutput, pInput + ovlPos);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -289,16 +299,16 @@ inline void TDStretch::overlap(SAMPLETYPE *output, const SAMPLETYPE *input, uint
|
|||
// The best position is determined as the position where the two overlapped
|
||||
// sample sequences are 'most alike', in terms of the highest cross-correlation
|
||||
// value over the overlapping period
|
||||
uint TDStretch::seekBestOverlapPositionStereo(const SAMPLETYPE *refPos)
|
||||
int TDStretch::seekBestOverlapPositionStereo(const SAMPLETYPE *refPos)
|
||||
{
|
||||
uint bestOffs;
|
||||
LONG_SAMPLETYPE bestCorr, corr;
|
||||
uint i;
|
||||
int bestOffs;
|
||||
double bestCorr, corr;
|
||||
int i;
|
||||
|
||||
// Slopes the amplitudes of the 'midBuffer' samples
|
||||
precalcCorrReferenceStereo();
|
||||
|
||||
bestCorr = INT_MIN;
|
||||
bestCorr = FLT_MIN;
|
||||
bestOffs = 0;
|
||||
|
||||
// Scans for the best correlation value by testing each possible position
|
||||
|
@ -307,7 +317,10 @@ uint TDStretch::seekBestOverlapPositionStereo(const SAMPLETYPE *refPos)
|
|||
{
|
||||
// Calculates correlation value for the mixing position corresponding
|
||||
// to 'i'
|
||||
corr = calcCrossCorrStereo(refPos + 2 * i, pRefMidBuffer);
|
||||
corr = (double)calcCrossCorrStereo(refPos + 2 * i, pRefMidBuffer);
|
||||
// heuristic rule to slightly favour values close to mid of the range
|
||||
double tmp = (double)(2 * i - seekLength) / (double)seekLength;
|
||||
corr = ((corr + 0.1) * (1.0 - 0.25 * tmp * tmp));
|
||||
|
||||
// Checks for the highest correlation value
|
||||
if (corr > bestCorr)
|
||||
|
@ -329,18 +342,18 @@ uint TDStretch::seekBestOverlapPositionStereo(const SAMPLETYPE *refPos)
|
|||
// The best position is determined as the position where the two overlapped
|
||||
// sample sequences are 'most alike', in terms of the highest cross-correlation
|
||||
// value over the overlapping period
|
||||
uint TDStretch::seekBestOverlapPositionStereoQuick(const SAMPLETYPE *refPos)
|
||||
int TDStretch::seekBestOverlapPositionStereoQuick(const SAMPLETYPE *refPos)
|
||||
{
|
||||
uint j;
|
||||
uint bestOffs;
|
||||
LONG_SAMPLETYPE bestCorr, corr;
|
||||
uint scanCount, corrOffset, tempOffset;
|
||||
int j;
|
||||
int bestOffs;
|
||||
double bestCorr, corr;
|
||||
int scanCount, corrOffset, tempOffset;
|
||||
|
||||
// Slopes the amplitude of the 'midBuffer' samples
|
||||
precalcCorrReferenceStereo();
|
||||
|
||||
bestCorr = INT_MIN;
|
||||
bestOffs = 0;
|
||||
bestCorr = FLT_MIN;
|
||||
bestOffs = _scanOffsets[0][0];
|
||||
corrOffset = 0;
|
||||
tempOffset = 0;
|
||||
|
||||
|
@ -353,14 +366,17 @@ uint TDStretch::seekBestOverlapPositionStereoQuick(const SAMPLETYPE *refPos)
|
|||
for (scanCount = 0;scanCount < 4; scanCount ++)
|
||||
{
|
||||
j = 0;
|
||||
while (scanOffsets[scanCount][j])
|
||||
while (_scanOffsets[scanCount][j])
|
||||
{
|
||||
tempOffset = corrOffset + scanOffsets[scanCount][j];
|
||||
tempOffset = corrOffset + _scanOffsets[scanCount][j];
|
||||
if (tempOffset >= seekLength) break;
|
||||
|
||||
// Calculates correlation value for the mixing position corresponding
|
||||
// to 'tempOffset'
|
||||
corr = calcCrossCorrStereo(refPos + 2 * tempOffset, pRefMidBuffer);
|
||||
corr = (double)calcCrossCorrStereo(refPos + 2 * tempOffset, pRefMidBuffer);
|
||||
// heuristic rule to slightly favour values close to mid of the range
|
||||
double tmp = (double)(2 * tempOffset - seekLength) / seekLength;
|
||||
corr = ((corr + 0.1) * (1.0 - 0.25 * tmp * tmp));
|
||||
|
||||
// Checks for the highest correlation value
|
||||
if (corr > bestCorr)
|
||||
|
@ -386,17 +402,17 @@ uint TDStretch::seekBestOverlapPositionStereoQuick(const SAMPLETYPE *refPos)
|
|||
// The best position is determined as the position where the two overlapped
|
||||
// sample sequences are 'most alike', in terms of the highest cross-correlation
|
||||
// value over the overlapping period
|
||||
uint TDStretch::seekBestOverlapPositionMono(const SAMPLETYPE *refPos)
|
||||
int TDStretch::seekBestOverlapPositionMono(const SAMPLETYPE *refPos)
|
||||
{
|
||||
uint bestOffs;
|
||||
LONG_SAMPLETYPE bestCorr, corr;
|
||||
uint tempOffset;
|
||||
int bestOffs;
|
||||
double bestCorr, corr;
|
||||
int tempOffset;
|
||||
const SAMPLETYPE *compare;
|
||||
|
||||
// Slopes the amplitude of the 'midBuffer' samples
|
||||
precalcCorrReferenceMono();
|
||||
|
||||
bestCorr = INT_MIN;
|
||||
bestCorr = FLT_MIN;
|
||||
bestOffs = 0;
|
||||
|
||||
// Scans for the best correlation value by testing each possible position
|
||||
|
@ -407,7 +423,10 @@ uint TDStretch::seekBestOverlapPositionMono(const SAMPLETYPE *refPos)
|
|||
|
||||
// Calculates correlation value for the mixing position corresponding
|
||||
// to 'tempOffset'
|
||||
corr = calcCrossCorrMono(pRefMidBuffer, compare);
|
||||
corr = (double)calcCrossCorrMono(pRefMidBuffer, compare);
|
||||
// heuristic rule to slightly favour values close to mid of the range
|
||||
double tmp = (double)(2 * tempOffset - seekLength) / seekLength;
|
||||
corr = ((corr + 0.1) * (1.0 - 0.25 * tmp * tmp));
|
||||
|
||||
// Checks for the highest correlation value
|
||||
if (corr > bestCorr)
|
||||
|
@ -429,18 +448,18 @@ uint TDStretch::seekBestOverlapPositionMono(const SAMPLETYPE *refPos)
|
|||
// The best position is determined as the position where the two overlapped
|
||||
// sample sequences are 'most alike', in terms of the highest cross-correlation
|
||||
// value over the overlapping period
|
||||
uint TDStretch::seekBestOverlapPositionMonoQuick(const SAMPLETYPE *refPos)
|
||||
int TDStretch::seekBestOverlapPositionMonoQuick(const SAMPLETYPE *refPos)
|
||||
{
|
||||
uint j;
|
||||
uint bestOffs;
|
||||
LONG_SAMPLETYPE bestCorr, corr;
|
||||
uint scanCount, corrOffset, tempOffset;
|
||||
int j;
|
||||
int bestOffs;
|
||||
double bestCorr, corr;
|
||||
int scanCount, corrOffset, tempOffset;
|
||||
|
||||
// Slopes the amplitude of the 'midBuffer' samples
|
||||
precalcCorrReferenceMono();
|
||||
|
||||
bestCorr = INT_MIN;
|
||||
bestOffs = 0;
|
||||
bestCorr = FLT_MIN;
|
||||
bestOffs = _scanOffsets[0][0];
|
||||
corrOffset = 0;
|
||||
tempOffset = 0;
|
||||
|
||||
|
@ -453,14 +472,17 @@ uint TDStretch::seekBestOverlapPositionMonoQuick(const SAMPLETYPE *refPos)
|
|||
for (scanCount = 0;scanCount < 4; scanCount ++)
|
||||
{
|
||||
j = 0;
|
||||
while (scanOffsets[scanCount][j])
|
||||
while (_scanOffsets[scanCount][j])
|
||||
{
|
||||
tempOffset = corrOffset + scanOffsets[scanCount][j];
|
||||
tempOffset = corrOffset + _scanOffsets[scanCount][j];
|
||||
if (tempOffset >= seekLength) break;
|
||||
|
||||
// Calculates correlation value for the mixing position corresponding
|
||||
// to 'tempOffset'
|
||||
corr = calcCrossCorrMono(refPos + tempOffset, pRefMidBuffer);
|
||||
corr = (double)calcCrossCorrMono(refPos + tempOffset, pRefMidBuffer);
|
||||
// heuristic rule to slightly favour values close to mid of the range
|
||||
double tmp = (double)(2 * tempOffset - seekLength) / seekLength;
|
||||
corr = ((corr + 0.1) * (1.0 - 0.25 * tmp * tmp));
|
||||
|
||||
// Checks for the highest correlation value
|
||||
if (corr > bestCorr)
|
||||
|
@ -486,29 +508,82 @@ void TDStretch::clearCrossCorrState()
|
|||
}
|
||||
|
||||
|
||||
/// Calculates processing sequence length according to tempo setting
|
||||
void TDStretch::calcSeqParameters()
|
||||
{
|
||||
// Adjust tempo param according to tempo, so that variating processing sequence length is used
|
||||
// at varius tempo settings, between the given low...top limits
|
||||
#define AUTOSEQ_TEMPO_LOW 0.5 // auto setting low tempo range (-50%)
|
||||
#define AUTOSEQ_TEMPO_TOP 2.0 // auto setting top tempo range (+100%)
|
||||
|
||||
// sequence-ms setting values at above low & top tempo
|
||||
#define AUTOSEQ_AT_MIN 125.0
|
||||
#define AUTOSEQ_AT_MAX 50.0
|
||||
#define AUTOSEQ_K ((AUTOSEQ_AT_MAX - AUTOSEQ_AT_MIN) / (AUTOSEQ_TEMPO_TOP - AUTOSEQ_TEMPO_LOW))
|
||||
#define AUTOSEQ_C (AUTOSEQ_AT_MIN - (AUTOSEQ_K) * (AUTOSEQ_TEMPO_LOW))
|
||||
|
||||
// seek-window-ms setting values at above low & top tempo
|
||||
#define AUTOSEEK_AT_MIN 25.0
|
||||
#define AUTOSEEK_AT_MAX 15.0
|
||||
#define AUTOSEEK_K ((AUTOSEEK_AT_MAX - AUTOSEEK_AT_MIN) / (AUTOSEQ_TEMPO_TOP - AUTOSEQ_TEMPO_LOW))
|
||||
#define AUTOSEEK_C (AUTOSEEK_AT_MIN - (AUTOSEEK_K) * (AUTOSEQ_TEMPO_LOW))
|
||||
|
||||
#define CHECK_LIMITS(x, mi, ma) (((x) < (mi)) ? (mi) : (((x) > (ma)) ? (ma) : (x)))
|
||||
|
||||
double seq, seek;
|
||||
|
||||
if (bAutoSeqSetting)
|
||||
{
|
||||
seq = AUTOSEQ_C + AUTOSEQ_K * tempo;
|
||||
seq = CHECK_LIMITS(seq, AUTOSEQ_AT_MAX, AUTOSEQ_AT_MIN);
|
||||
sequenceMs = (int)(seq + 0.5);
|
||||
}
|
||||
|
||||
if (bAutoSeekSetting)
|
||||
{
|
||||
seek = AUTOSEEK_C + AUTOSEEK_K * tempo;
|
||||
seek = CHECK_LIMITS(seek, AUTOSEEK_AT_MAX, AUTOSEEK_AT_MIN);
|
||||
seekWindowMs = (int)(seek + 0.5);
|
||||
}
|
||||
|
||||
// Update seek window lengths
|
||||
seekWindowLength = (sampleRate * sequenceMs) / 1000;
|
||||
if (seekWindowLength < 2 * overlapLength)
|
||||
{
|
||||
seekWindowLength = 2 * overlapLength;
|
||||
}
|
||||
seekLength = (sampleRate * seekWindowMs) / 1000;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower
|
||||
// tempo, larger faster tempo.
|
||||
void TDStretch::setTempo(float newTempo)
|
||||
{
|
||||
uint intskip;
|
||||
int intskip;
|
||||
|
||||
tempo = newTempo;
|
||||
|
||||
// Calculate new sequence duration
|
||||
calcSeqParameters();
|
||||
|
||||
// Calculate ideal skip length (according to tempo value)
|
||||
nominalSkip = tempo * (seekWindowLength - overlapLength);
|
||||
skipFract = 0;
|
||||
intskip = (int)(nominalSkip + 0.5f);
|
||||
|
||||
// Calculate how many samples are needed in the 'inputBuffer' to
|
||||
// process another batch of samples
|
||||
sampleReq = max(intskip + overlapLength, seekWindowLength) + maxOffset;
|
||||
//sampleReq = max(intskip + overlapLength, seekWindowLength) + seekLength / 2;
|
||||
sampleReq = max(intskip + overlapLength, seekWindowLength) + seekLength;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets the number of channels, 1 = mono, 2 = stereo
|
||||
void TDStretch::setChannels(uint numChannels)
|
||||
void TDStretch::setChannels(int numChannels)
|
||||
{
|
||||
assert(numChannels > 0);
|
||||
if (channels == numChannels) return;
|
||||
assert(numChannels == 1 || numChannels == 2);
|
||||
|
||||
|
@ -520,6 +595,7 @@ void TDStretch::setChannels(uint numChannels)
|
|||
|
||||
// nominal tempo, no need for processing, just pass the samples through
|
||||
// to outputBuffer
|
||||
/*
|
||||
void TDStretch::processNominalTempo()
|
||||
{
|
||||
assert(tempo == 1.0f);
|
||||
|
@ -547,13 +623,15 @@ void TDStretch::processNominalTempo()
|
|||
// Simply bypass samples from input to output
|
||||
outputBuffer.moveSamples(inputBuffer);
|
||||
}
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
// Processes as many processing frames of the samples 'inputBuffer', store
|
||||
// the result into 'outputBuffer'
|
||||
void TDStretch::processSamples()
|
||||
{
|
||||
uint ovlSkip, offset;
|
||||
int ovlSkip, offset;
|
||||
int temp;
|
||||
|
||||
/* Removed this small optimization - can introduce a click to sound when tempo setting
|
||||
|
@ -566,23 +644,10 @@ void TDStretch::processSamples()
|
|||
}
|
||||
*/
|
||||
|
||||
if (bMidBufferDirty == FALSE)
|
||||
{
|
||||
// if midBuffer is empty, move the first samples of the input stream
|
||||
// into it
|
||||
if (inputBuffer.numSamples() < overlapLength)
|
||||
{
|
||||
// wait until we've got overlapLength samples
|
||||
return;
|
||||
}
|
||||
memcpy(pMidBuffer, inputBuffer.ptrBegin(), channels * overlapLength * sizeof(SAMPLETYPE));
|
||||
inputBuffer.receiveSamples(overlapLength);
|
||||
bMidBufferDirty = TRUE;
|
||||
}
|
||||
|
||||
// Process samples as long as there are enough samples in 'inputBuffer'
|
||||
// to form a processing frame.
|
||||
while (inputBuffer.numSamples() >= sampleReq)
|
||||
// while ((int)inputBuffer.numSamples() >= sampleReq - (outDebt / 4))
|
||||
while ((int)inputBuffer.numSamples() >= sampleReq)
|
||||
{
|
||||
// If tempo differs from the normal ('SCALE'), scan for the best overlapping
|
||||
// position
|
||||
|
@ -592,23 +657,36 @@ void TDStretch::processSamples()
|
|||
// samples in 'midBuffer' using sliding overlapping
|
||||
// ... first partially overlap with the end of the previous sequence
|
||||
// (that's in 'midBuffer')
|
||||
overlap(outputBuffer.ptrEnd(overlapLength), inputBuffer.ptrBegin(), offset);
|
||||
outputBuffer.putSamples(overlapLength);
|
||||
overlap(outputBuffer.ptrEnd((uint)overlapLength), inputBuffer.ptrBegin(), (uint)offset);
|
||||
outputBuffer.putSamples((uint)overlapLength);
|
||||
|
||||
// ... then copy sequence samples from 'inputBuffer' to output
|
||||
temp = (seekWindowLength - 2 * overlapLength);// & 0xfffffffe;
|
||||
if (temp > 0)
|
||||
// ... then copy sequence samples from 'inputBuffer' to output:
|
||||
temp = (seekLength / 2 - offset);
|
||||
|
||||
// compensate cumulated output length diff vs. ideal output
|
||||
// temp -= outDebt / 4;
|
||||
|
||||
// update ideal vs. true output difference
|
||||
// outDebt += temp;
|
||||
|
||||
// length of sequence
|
||||
// temp += (seekWindowLength - 2 * overlapLength);
|
||||
temp = (seekWindowLength - 2 * overlapLength);
|
||||
|
||||
// crosscheck that we don't have buffer overflow...
|
||||
if ((int)inputBuffer.numSamples() < (offset + temp + overlapLength * 2))
|
||||
{
|
||||
outputBuffer.putSamples(inputBuffer.ptrBegin() + channels * (offset + overlapLength), temp);
|
||||
continue; // just in case, shouldn't really happen
|
||||
}
|
||||
|
||||
outputBuffer.putSamples(inputBuffer.ptrBegin() + channels * (offset + overlapLength), (uint)temp);
|
||||
|
||||
// Copies the end of the current sequence from 'inputBuffer' to
|
||||
// 'midBuffer' for being mixed with the beginning of the next
|
||||
// processing sequence and so on
|
||||
assert(offset + seekWindowLength <= inputBuffer.numSamples());
|
||||
memcpy(pMidBuffer, inputBuffer.ptrBegin() + channels * (offset + seekWindowLength - overlapLength),
|
||||
assert((offset + temp + overlapLength * 2) <= (int)inputBuffer.numSamples());
|
||||
memcpy(pMidBuffer, inputBuffer.ptrBegin() + channels * (offset + temp + overlapLength),
|
||||
channels * sizeof(SAMPLETYPE) * overlapLength);
|
||||
bMidBufferDirty = TRUE;
|
||||
|
||||
// Remove the processed samples from the input buffer. Update
|
||||
// the difference between integer & nominal skip step to 'skipFract'
|
||||
|
@ -616,17 +694,17 @@ void TDStretch::processSamples()
|
|||
skipFract += nominalSkip; // real skip size
|
||||
ovlSkip = (int)skipFract; // rounded to integer skip
|
||||
skipFract -= ovlSkip; // maintain the fraction part, i.e. real vs. integer skip
|
||||
inputBuffer.receiveSamples(ovlSkip);
|
||||
inputBuffer.receiveSamples((uint)ovlSkip);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Adds 'numsamples' pcs of samples from the 'samples' memory position into
|
||||
// the input of the object.
|
||||
void TDStretch::putSamples(const SAMPLETYPE *samples, uint numSamples)
|
||||
void TDStretch::putSamples(const SAMPLETYPE *samples, uint nSamples)
|
||||
{
|
||||
// Add the samples into the input buffer
|
||||
inputBuffer.putSamples(samples, numSamples);
|
||||
inputBuffer.putSamples(samples, nSamples);
|
||||
// Process the samples in input buffer
|
||||
processSamples();
|
||||
}
|
||||
|
@ -634,10 +712,11 @@ void TDStretch::putSamples(const SAMPLETYPE *samples, uint numSamples)
|
|||
|
||||
|
||||
/// Set new overlap length parameter & reallocate RefMidBuffer if necessary.
|
||||
void TDStretch::acceptNewOverlapLength(uint newOverlapLength)
|
||||
void TDStretch::acceptNewOverlapLength(int newOverlapLength)
|
||||
{
|
||||
uint prevOvl;
|
||||
int prevOvl;
|
||||
|
||||
assert(newOverlapLength >= 0);
|
||||
prevOvl = overlapLength;
|
||||
overlapLength = newOverlapLength;
|
||||
|
||||
|
@ -647,12 +726,11 @@ void TDStretch::acceptNewOverlapLength(uint newOverlapLength)
|
|||
delete[] pRefMidBufferUnaligned;
|
||||
|
||||
pMidBuffer = new SAMPLETYPE[overlapLength * 2];
|
||||
bMidBufferDirty = TRUE;
|
||||
clearMidBuffer();
|
||||
|
||||
pRefMidBufferUnaligned = new SAMPLETYPE[2 * overlapLength + 16 / sizeof(SAMPLETYPE)];
|
||||
// ensure that 'pRefMidBuffer' is aligned to 16 byte boundary for efficiency
|
||||
pRefMidBuffer = (SAMPLETYPE *)((((ulongptr)pRefMidBufferUnaligned) + 15) & -16);
|
||||
pRefMidBuffer = (SAMPLETYPE *)((((ulong)pRefMidBufferUnaligned) + 15) & (ulong)-16);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -662,18 +740,16 @@ void TDStretch::acceptNewOverlapLength(uint newOverlapLength)
|
|||
void * TDStretch::operator new(size_t s)
|
||||
{
|
||||
// Notice! don't use "new TDStretch" directly, use "newInstance" to create a new instance instead!
|
||||
assert(FALSE);
|
||||
throw std::runtime_error("Error in TDStretch::new: Don't use 'new TDStretch' directly, use 'newInstance' member instead!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
TDStretch * TDStretch::newInstance()
|
||||
{
|
||||
uint uExtensions = 0;
|
||||
uint uExtensions;
|
||||
|
||||
#if !defined(_MSC_VER) || !defined(__x86_64__)
|
||||
uExtensions = detectCPUextensions();
|
||||
#endif
|
||||
|
||||
// Check if MMX/SSE/3DNow! instruction set extensions supported by CPU
|
||||
|
||||
|
@ -760,33 +836,44 @@ void TDStretch::precalcCorrReferenceMono()
|
|||
|
||||
// Overlaps samples in 'midBuffer' with the samples in 'input'. The 'Stereo'
|
||||
// version of the routine.
|
||||
void TDStretch::overlapStereo(short *output, const short *input) const
|
||||
void TDStretch::overlapStereo(short *poutput, const short *input) const
|
||||
{
|
||||
int i;
|
||||
short temp;
|
||||
uint cnt2;
|
||||
int cnt2;
|
||||
|
||||
for (i = 0; i < (int)overlapLength ; i ++)
|
||||
for (i = 0; i < overlapLength ; i ++)
|
||||
{
|
||||
temp = (short)(overlapLength - i);
|
||||
cnt2 = 2 * i;
|
||||
output[cnt2] = (input[cnt2] * i + pMidBuffer[cnt2] * temp ) / overlapLength;
|
||||
output[cnt2 + 1] = (input[cnt2 + 1] * i + pMidBuffer[cnt2 + 1] * temp ) / overlapLength;
|
||||
poutput[cnt2] = (input[cnt2] * i + pMidBuffer[cnt2] * temp ) / overlapLength;
|
||||
poutput[cnt2 + 1] = (input[cnt2 + 1] * i + pMidBuffer[cnt2 + 1] * temp ) / overlapLength;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculates the x having the closest 2^x value for the given value
|
||||
static int _getClosest2Power(double value)
|
||||
{
|
||||
return (int)(log(value) / log(2.0) + 0.5);
|
||||
}
|
||||
|
||||
|
||||
/// Calculates overlap period length in samples.
|
||||
/// Integer version rounds overlap length to closest power of 2
|
||||
/// for a divide scaling operation.
|
||||
void TDStretch::calculateOverlapLength(uint overlapMs)
|
||||
void TDStretch::calculateOverlapLength(int aoverlapMs)
|
||||
{
|
||||
uint newOvl;
|
||||
int newOvl;
|
||||
|
||||
overlapDividerBits = _getClosest2Power((sampleRate * overlapMs) / 1000.0);
|
||||
assert(aoverlapMs >= 0);
|
||||
|
||||
// calculate overlap length so that it's power of 2 - thus it's easy to do
|
||||
// integer division by right-shifting. Term "-1" at end is to account for
|
||||
// the extra most significatnt bit left unused in result by signed multiplication
|
||||
overlapDividerBits = _getClosest2Power((sampleRate * aoverlapMs) / 1000.0) - 1;
|
||||
if (overlapDividerBits > 9) overlapDividerBits = 9;
|
||||
if (overlapDividerBits < 4) overlapDividerBits = 4;
|
||||
newOvl = 1<<overlapDividerBits;
|
||||
if (overlapDividerBits < 3) overlapDividerBits = 3;
|
||||
newOvl = (int)pow(2.0, (int)overlapDividerBits + 1); // +1 => account for -1 above
|
||||
|
||||
acceptNewOverlapLength(newOvl);
|
||||
|
||||
|
@ -800,31 +887,41 @@ void TDStretch::calculateOverlapLength(uint overlapMs)
|
|||
long TDStretch::calcCrossCorrMono(const short *mixingPos, const short *compare) const
|
||||
{
|
||||
long corr;
|
||||
uint i;
|
||||
long norm;
|
||||
int i;
|
||||
|
||||
corr = 0;
|
||||
corr = norm = 0;
|
||||
for (i = 1; i < overlapLength; i ++)
|
||||
{
|
||||
corr += (mixingPos[i] * compare[i]) >> overlapDividerBits;
|
||||
norm += (mixingPos[i] * mixingPos[i]) >> overlapDividerBits;
|
||||
}
|
||||
|
||||
return corr;
|
||||
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
||||
// done using floating point operation
|
||||
if (norm == 0) norm = 1; // to avoid div by zero
|
||||
return (long)((double)corr * SHRT_MAX / sqrt((double)norm));
|
||||
}
|
||||
|
||||
|
||||
long TDStretch::calcCrossCorrStereo(const short *mixingPos, const short *compare) const
|
||||
{
|
||||
long corr;
|
||||
uint i;
|
||||
long norm;
|
||||
int i;
|
||||
|
||||
corr = 0;
|
||||
corr = norm = 0;
|
||||
for (i = 2; i < 2 * overlapLength; i += 2)
|
||||
{
|
||||
corr += (mixingPos[i] * compare[i] +
|
||||
mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBits;
|
||||
norm += (mixingPos[i] * mixingPos[i] + mixingPos[i + 1] * mixingPos[i + 1]) >> overlapDividerBits;
|
||||
}
|
||||
|
||||
return corr;
|
||||
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
||||
// done using floating point operation
|
||||
if (norm == 0) norm = 1; // to avoid div by zero
|
||||
return (long)((double)corr * SHRT_MAX / sqrt((double)norm));
|
||||
}
|
||||
|
||||
#endif // INTEGER_SAMPLES
|
||||
|
@ -869,11 +966,11 @@ void TDStretch::precalcCorrReferenceMono()
|
|||
}
|
||||
|
||||
|
||||
// SSE-optimized version of the function overlapStereo
|
||||
void TDStretch::overlapStereo(float *output, const float *input) const
|
||||
// Overlaps samples in 'midBuffer' with the samples in 'pInput'
|
||||
void TDStretch::overlapStereo(float *pOutput, const float *pInput) const
|
||||
{
|
||||
int i;
|
||||
uint cnt2;
|
||||
int cnt2;
|
||||
float fTemp;
|
||||
float fScale;
|
||||
float fi;
|
||||
|
@ -885,18 +982,19 @@ void TDStretch::overlapStereo(float *output, const float *input) const
|
|||
fTemp = (float)(overlapLength - i) * fScale;
|
||||
fi = (float)i * fScale;
|
||||
cnt2 = 2 * i;
|
||||
output[cnt2 + 0] = input[cnt2 + 0] * fi + pMidBuffer[cnt2 + 0] * fTemp;
|
||||
output[cnt2 + 1] = input[cnt2 + 1] * fi + pMidBuffer[cnt2 + 1] * fTemp;
|
||||
pOutput[cnt2 + 0] = pInput[cnt2 + 0] * fi + pMidBuffer[cnt2 + 0] * fTemp;
|
||||
pOutput[cnt2 + 1] = pInput[cnt2 + 1] * fi + pMidBuffer[cnt2 + 1] * fTemp;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Calculates overlap period length in samples.
|
||||
void TDStretch::calculateOverlapLength(uint overlapMs)
|
||||
/// Calculates overlapInMsec period length in samples.
|
||||
void TDStretch::calculateOverlapLength(int overlapInMsec)
|
||||
{
|
||||
uint newOvl;
|
||||
int newOvl;
|
||||
|
||||
newOvl = (sampleRate * overlapMs) / 1000;
|
||||
assert(overlapInMsec >= 0);
|
||||
newOvl = (sampleRate * overlapInMsec) / 1000;
|
||||
if (newOvl < 16) newOvl = 16;
|
||||
|
||||
// must be divisible by 8
|
||||
|
@ -910,31 +1008,38 @@ void TDStretch::calculateOverlapLength(uint overlapMs)
|
|||
double TDStretch::calcCrossCorrMono(const float *mixingPos, const float *compare) const
|
||||
{
|
||||
double corr;
|
||||
uint i;
|
||||
double norm;
|
||||
int i;
|
||||
|
||||
corr = 0;
|
||||
corr = norm = 0;
|
||||
for (i = 1; i < overlapLength; i ++)
|
||||
{
|
||||
corr += mixingPos[i] * compare[i];
|
||||
norm += mixingPos[i] * mixingPos[i];
|
||||
}
|
||||
|
||||
return corr;
|
||||
if (norm < 1e-9) norm = 1.0; // to avoid div by zero
|
||||
return corr / sqrt(norm);
|
||||
}
|
||||
|
||||
|
||||
double TDStretch::calcCrossCorrStereo(const float *mixingPos, const float *compare) const
|
||||
{
|
||||
double corr;
|
||||
uint i;
|
||||
double norm;
|
||||
int i;
|
||||
|
||||
corr = 0;
|
||||
corr = norm = 0;
|
||||
for (i = 2; i < 2 * overlapLength; i += 2)
|
||||
{
|
||||
corr += mixingPos[i] * compare[i] +
|
||||
mixingPos[i + 1] * compare[i + 1];
|
||||
norm += mixingPos[i] * mixingPos[i] +
|
||||
mixingPos[i + 1] * mixingPos[i + 1];
|
||||
}
|
||||
|
||||
return corr;
|
||||
if (norm < 1e-9) norm = 1.0; // to avoid div by zero
|
||||
return corr / sqrt(norm);
|
||||
}
|
||||
|
||||
#endif // FLOAT_SAMPLES
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
/// while maintaining the original pitch by using a time domain WSOLA-like method
|
||||
/// with several performance-increasing tweaks.
|
||||
///
|
||||
/// Note : MMX optimized functions reside in a separate, platform-specific file,
|
||||
/// e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'
|
||||
/// Note : MMX/SSE optimized functions reside in separate, platform-specific files
|
||||
/// 'mmx_optimized.cpp' and 'sse_optimized.cpp'
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
|
@ -13,10 +13,10 @@
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.16 $
|
||||
// Last changed : $Date: 2009-05-17 14:35:13 +0300 (Sun, 17 May 2009) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: TDStretch.h,v 1.16 2006/02/05 16:44:06 Olli Exp $
|
||||
// $Id: TDStretch.h 71 2009-05-17 11:35:13Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -44,6 +44,7 @@
|
|||
#ifndef TDStretch_H
|
||||
#define TDStretch_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include "STTypes.h"
|
||||
#include "RateTransposer.h"
|
||||
#include "FIFOSamplePipe.h"
|
||||
|
@ -51,7 +52,13 @@
|
|||
namespace soundtouch
|
||||
{
|
||||
|
||||
// Default values for sound processing parameters:
|
||||
/// Default values for sound processing parameters:
|
||||
/// Notice that the default parameters are tuned for contemporary popular music
|
||||
/// processing. For speech processing applications these parameters suit better:
|
||||
/// #define DEFAULT_SEQUENCE_MS 40
|
||||
/// #define DEFAULT_SEEKWINDOW_MS 15
|
||||
/// #define DEFAULT_OVERLAP_MS 8
|
||||
///
|
||||
|
||||
/// Default length of a single processing sequence, in milliseconds. This determines to how
|
||||
/// long sequences the original sound is chopped in the time-stretch algorithm.
|
||||
|
@ -61,11 +68,41 @@ namespace soundtouch
|
|||
/// and vice versa.
|
||||
///
|
||||
/// Increasing this value reduces computational burden & vice versa.
|
||||
#define DEFAULT_SEQUENCE_MS 63
|
||||
//#define DEFAULT_SEQUENCE_MS 40
|
||||
#define DEFAULT_SEQUENCE_MS USE_AUTO_SEQUENCE_LEN
|
||||
|
||||
#define DEFAULT_SEEKWINDOW_MS 17
|
||||
/// Giving this value for the sequence length sets automatic parameter value
|
||||
/// according to tempo setting (recommended)
|
||||
#define USE_AUTO_SEQUENCE_LEN 0
|
||||
|
||||
#define DEFAULT_OVERLAP_MS 7
|
||||
/// Seeking window default length in milliseconds for algorithm that finds the best possible
|
||||
/// overlapping location. This determines from how wide window the algorithm may look for an
|
||||
/// optimal joining location when mixing the sound sequences back together.
|
||||
///
|
||||
/// The bigger this window setting is, the higher the possibility to find a better mixing
|
||||
/// position will become, but at the same time large values may cause a "drifting" artifact
|
||||
/// because consequent sequences will be taken at more uneven intervals.
|
||||
///
|
||||
/// If there's a disturbing artifact that sounds as if a constant frequency was drifting
|
||||
/// around, try reducing this setting.
|
||||
///
|
||||
/// Increasing this value increases computational burden & vice versa.
|
||||
//#define DEFAULT_SEEKWINDOW_MS 15
|
||||
#define DEFAULT_SEEKWINDOW_MS USE_AUTO_SEEKWINDOW_LEN
|
||||
|
||||
/// Giving this value for the seek window length sets automatic parameter value
|
||||
/// according to tempo setting (recommended)
|
||||
#define USE_AUTO_SEEKWINDOW_LEN 0
|
||||
|
||||
/// Overlap length in milliseconds. When the chopped sound sequences are mixed back together,
|
||||
/// to form a continuous sound stream, this parameter defines over how long period the two
|
||||
/// consecutive sequences are let to overlap each other.
|
||||
///
|
||||
/// This shouldn't be that critical parameter. If you reduce the DEFAULT_SEQUENCE_MS setting
|
||||
/// by a large amount, you might wish to try a smaller value on this.
|
||||
///
|
||||
/// Increasing this value increases computational burden & vice versa.
|
||||
#define DEFAULT_OVERLAP_MS 8
|
||||
|
||||
|
||||
/// Class that does the time-stretch (tempo change) effect for the processed
|
||||
|
@ -73,44 +110,46 @@ namespace soundtouch
|
|||
class TDStretch : public FIFOProcessor
|
||||
{
|
||||
protected:
|
||||
uint channels;
|
||||
uint sampleReq;
|
||||
int channels;
|
||||
int sampleReq;
|
||||
float tempo;
|
||||
|
||||
SAMPLETYPE *pMidBuffer;
|
||||
SAMPLETYPE *pRefMidBuffer;
|
||||
SAMPLETYPE *pRefMidBufferUnaligned;
|
||||
uint overlapLength;
|
||||
uint overlapDividerBits;
|
||||
uint slopingDivider;
|
||||
uint seekLength;
|
||||
uint seekWindowLength;
|
||||
uint maxOffset;
|
||||
int overlapLength;
|
||||
int seekLength;
|
||||
int seekWindowLength;
|
||||
int overlapDividerBits;
|
||||
int slopingDivider;
|
||||
float nominalSkip;
|
||||
float skipFract;
|
||||
FIFOSampleBuffer outputBuffer;
|
||||
FIFOSampleBuffer inputBuffer;
|
||||
BOOL bQuickseek;
|
||||
BOOL bMidBufferDirty;
|
||||
BOOL bQuickSeek;
|
||||
// int outDebt;
|
||||
// BOOL bMidBufferDirty;
|
||||
|
||||
uint sampleRate;
|
||||
uint sequenceMs;
|
||||
uint seekWindowMs;
|
||||
uint overlapMs;
|
||||
int sampleRate;
|
||||
int sequenceMs;
|
||||
int seekWindowMs;
|
||||
int overlapMs;
|
||||
BOOL bAutoSeqSetting;
|
||||
BOOL bAutoSeekSetting;
|
||||
|
||||
void acceptNewOverlapLength(uint newOverlapLength);
|
||||
void acceptNewOverlapLength(int newOverlapLength);
|
||||
|
||||
virtual void clearCrossCorrState();
|
||||
void calculateOverlapLength(uint overlapMs);
|
||||
void calculateOverlapLength(int overlapMs);
|
||||
|
||||
virtual LONG_SAMPLETYPE calcCrossCorrStereo(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare) const;
|
||||
virtual LONG_SAMPLETYPE calcCrossCorrMono(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare) const;
|
||||
|
||||
virtual uint seekBestOverlapPositionStereo(const SAMPLETYPE *refPos);
|
||||
virtual uint seekBestOverlapPositionStereoQuick(const SAMPLETYPE *refPos);
|
||||
virtual uint seekBestOverlapPositionMono(const SAMPLETYPE *refPos);
|
||||
virtual uint seekBestOverlapPositionMonoQuick(const SAMPLETYPE *refPos);
|
||||
uint seekBestOverlapPosition(const SAMPLETYPE *refPos);
|
||||
virtual int seekBestOverlapPositionStereo(const SAMPLETYPE *refPos);
|
||||
virtual int seekBestOverlapPositionStereoQuick(const SAMPLETYPE *refPos);
|
||||
virtual int seekBestOverlapPositionMono(const SAMPLETYPE *refPos);
|
||||
virtual int seekBestOverlapPositionMonoQuick(const SAMPLETYPE *refPos);
|
||||
int seekBestOverlapPosition(const SAMPLETYPE *refPos);
|
||||
|
||||
virtual void overlapStereo(SAMPLETYPE *output, const SAMPLETYPE *input) const;
|
||||
virtual void overlapMono(SAMPLETYPE *output, const SAMPLETYPE *input) const;
|
||||
|
@ -121,7 +160,7 @@ protected:
|
|||
void precalcCorrReferenceMono();
|
||||
void precalcCorrReferenceStereo();
|
||||
|
||||
void processNominalTempo();
|
||||
void calcSeqParameters();
|
||||
|
||||
/// Changes the tempo of the given sound samples.
|
||||
/// Returns amount of samples returned in the "output" buffer.
|
||||
|
@ -135,7 +174,7 @@ public:
|
|||
|
||||
/// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
||||
/// depending on if we've a MMX/SSE/etc-capable CPU available or not.
|
||||
void *operator new(size_t s);
|
||||
static void *operator new(size_t s);
|
||||
|
||||
/// Use this function instead of "new" operator to create a new instance of this class.
|
||||
/// This function automatically chooses a correct feature set depending on if the CPU
|
||||
|
@ -159,7 +198,7 @@ public:
|
|||
void clearInput();
|
||||
|
||||
/// Sets the number of channels, 1 = mono, 2 = stereo
|
||||
void setChannels(uint numChannels);
|
||||
void setChannels(int numChannels);
|
||||
|
||||
/// Enables/disables the quick position seeking algorithm. Zero to disable,
|
||||
/// nonzero to enable
|
||||
|
@ -176,16 +215,16 @@ public:
|
|||
/// 'seekwindowMS' = seeking window length for scanning the best overlapping
|
||||
/// position
|
||||
/// 'overlapMS' = overlapping length
|
||||
void setParameters(uint sampleRate, ///< Samplerate of sound being processed (Hz)
|
||||
uint sequenceMS = DEFAULT_SEQUENCE_MS, ///< Single processing sequence length (ms)
|
||||
uint seekwindowMS = DEFAULT_SEEKWINDOW_MS, ///< Offset seeking window length (ms)
|
||||
uint overlapMS = DEFAULT_OVERLAP_MS ///< Sequence overlapping length (ms)
|
||||
void setParameters(int sampleRate, ///< Samplerate of sound being processed (Hz)
|
||||
int sequenceMS = -1, ///< Single processing sequence length (ms)
|
||||
int seekwindowMS = -1, ///< Offset seeking window length (ms)
|
||||
int overlapMS = -1 ///< Sequence overlapping length (ms)
|
||||
);
|
||||
|
||||
/// Get routine control parameters, see setParameters() function.
|
||||
/// Any of the parameters to this function can be NULL, in such case corresponding parameter
|
||||
/// value isn't returned.
|
||||
void getParameters(uint *pSampleRate, uint *pSequenceMs, uint *pSeekWindowMs, uint *pOverlapMs);
|
||||
void getParameters(int *pSampleRate, int *pSequenceMs, int *pSeekWindowMs, int *pOverlapMs) const;
|
||||
|
||||
/// Adds 'numsamples' pcs of samples from the 'samples' memory position into
|
||||
/// the input of the object.
|
||||
|
@ -200,26 +239,26 @@ public:
|
|||
|
||||
// Implementation-specific class declarations:
|
||||
|
||||
//#ifdef ALLOW_MMX
|
||||
// /// Class that implements MMX optimized routines for 16bit integer samples type.
|
||||
// class TDStretchMMX : public TDStretch
|
||||
// {
|
||||
// protected:
|
||||
// long calcCrossCorrStereo(const short *mixingPos, const short *compare) const;
|
||||
// virtual void overlapStereo(short *output, const short *input) const;
|
||||
// virtual void clearCrossCorrState();
|
||||
// };
|
||||
//#endif /// ALLOW_MMX
|
||||
//
|
||||
//
|
||||
//#ifdef ALLOW_3DNOW
|
||||
// /// Class that implements 3DNow! optimized routines for floating point samples type.
|
||||
// class TDStretch3DNow : public TDStretch
|
||||
// {
|
||||
// protected:
|
||||
// double calcCrossCorrStereo(const float *mixingPos, const float *compare) const;
|
||||
// };
|
||||
//#endif /// ALLOW_3DNOW
|
||||
#ifdef ALLOW_MMX
|
||||
/// Class that implements MMX optimized routines for 16bit integer samples type.
|
||||
class TDStretchMMX : public TDStretch
|
||||
{
|
||||
protected:
|
||||
long calcCrossCorrStereo(const short *mixingPos, const short *compare) const;
|
||||
virtual void overlapStereo(short *output, const short *input) const;
|
||||
virtual void clearCrossCorrState();
|
||||
};
|
||||
#endif /// ALLOW_MMX
|
||||
|
||||
|
||||
#ifdef ALLOW_3DNOW
|
||||
/// Class that implements 3DNow! optimized routines for floating point samples type.
|
||||
class TDStretch3DNow : public TDStretch
|
||||
{
|
||||
protected:
|
||||
double calcCrossCorrStereo(const float *mixingPos, const float *compare) const;
|
||||
};
|
||||
#endif /// ALLOW_3DNOW
|
||||
|
||||
|
||||
#ifdef ALLOW_SSE
|
||||
|
|
|
@ -17,10 +17,10 @@
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.15 $
|
||||
// Last changed : $Date: 2009-02-21 18:00:14 +0200 (Sat, 21 Feb 2009) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: WavFile.cpp,v 1.15 2006/02/05 16:44:06 Olli Exp $
|
||||
// $Id: WavFile.cpp 63 2009-02-21 16:00:14Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -48,20 +48,18 @@
|
|||
#include <stdio.h>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include "WavFile.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
const static char riffStr[] = "RIFF";
|
||||
const static char waveStr[] = "WAVE";
|
||||
const static char fmtStr[] = "fmt ";
|
||||
const static char dataStr[] = "data";
|
||||
static const char riffStr[] = "RIFF";
|
||||
static const char waveStr[] = "WAVE";
|
||||
static const char fmtStr[] = "fmt ";
|
||||
static const char dataStr[] = "data";
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -142,8 +140,6 @@ const static char dataStr[] = "data";
|
|||
|
||||
WavInFile::WavInFile(const char *fileName)
|
||||
{
|
||||
int hdrsOk;
|
||||
|
||||
// Try to open the file for reading
|
||||
fptr = fopen(fileName, "rb");
|
||||
if (fptr == NULL)
|
||||
|
@ -155,22 +151,45 @@ WavInFile::WavInFile(const char *fileName)
|
|||
throw runtime_error(msg);
|
||||
}
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
|
||||
WavInFile::WavInFile(FILE *file)
|
||||
{
|
||||
// Try to open the file for reading
|
||||
fptr = file;
|
||||
if (!file)
|
||||
{
|
||||
// didn't succeed
|
||||
string msg = "Error : Unable to access input stream for reading";
|
||||
throw runtime_error(msg);
|
||||
}
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
|
||||
/// Init the WAV file stream
|
||||
void WavInFile::init()
|
||||
{
|
||||
int hdrsOk;
|
||||
|
||||
// assume file stream is already open
|
||||
assert(fptr);
|
||||
|
||||
// Read the file headers
|
||||
hdrsOk = readWavHeaders();
|
||||
if (hdrsOk != 0)
|
||||
{
|
||||
// Something didn't match in the wav file headers
|
||||
string msg = "File \"";
|
||||
msg += fileName;
|
||||
msg += "\" is corrupt or not a WAV file";
|
||||
string msg = "Input file is corrupt or not a WAV file";
|
||||
throw runtime_error(msg);
|
||||
}
|
||||
|
||||
if (header.format.fixed != 1)
|
||||
{
|
||||
string msg = "File \"";
|
||||
msg += fileName;
|
||||
msg += "\" uses unsupported encoding.";
|
||||
string msg = "Input file uses unsupported encoding.";
|
||||
throw runtime_error(msg);
|
||||
}
|
||||
|
||||
|
@ -181,7 +200,8 @@ WavInFile::WavInFile(const char *fileName)
|
|||
|
||||
WavInFile::~WavInFile()
|
||||
{
|
||||
close();
|
||||
if (fptr) fclose(fptr);
|
||||
fptr = NULL;
|
||||
}
|
||||
|
||||
|
||||
|
@ -197,7 +217,7 @@ void WavInFile::rewind()
|
|||
}
|
||||
|
||||
|
||||
int WavInFile::checkCharTags()
|
||||
int WavInFile::checkCharTags() const
|
||||
{
|
||||
// header.format.fmt should equal to 'fmt '
|
||||
if (memcmp(fmtStr, header.format.fmt, 4) != 0) return -1;
|
||||
|
@ -225,10 +245,11 @@ int WavInFile::read(char *buffer, int maxElems)
|
|||
if (afterDataRead > header.data.data_len)
|
||||
{
|
||||
// Don't read more samples than are marked available in header
|
||||
numBytes = header.data.data_len - dataRead;
|
||||
numBytes = (int)header.data.data_len - (int)dataRead;
|
||||
assert(numBytes >= 0);
|
||||
}
|
||||
|
||||
assert(buffer);
|
||||
numBytes = fread(buffer, 1, numBytes, fptr);
|
||||
dataRead += numBytes;
|
||||
|
||||
|
@ -242,6 +263,7 @@ int WavInFile::read(short *buffer, int maxElems)
|
|||
int numBytes;
|
||||
int numElems;
|
||||
|
||||
assert(buffer);
|
||||
if (header.format.bits_per_sample == 8)
|
||||
{
|
||||
// 8 bit format
|
||||
|
@ -267,7 +289,7 @@ int WavInFile::read(short *buffer, int maxElems)
|
|||
if (afterDataRead > header.data.data_len)
|
||||
{
|
||||
// Don't read more samples than are marked available in header
|
||||
numBytes = header.data.data_len - dataRead;
|
||||
numBytes = (int)header.data.data_len - (int)dataRead;
|
||||
assert(numBytes >= 0);
|
||||
}
|
||||
|
||||
|
@ -313,13 +335,6 @@ int WavInFile::eof() const
|
|||
}
|
||||
|
||||
|
||||
void WavInFile::close()
|
||||
{
|
||||
fclose(fptr);
|
||||
fptr = NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// test if character code is between a white space ' ' and little 'z'
|
||||
static int isAlpha(char c)
|
||||
|
@ -329,9 +344,9 @@ static int isAlpha(char c)
|
|||
|
||||
|
||||
// test if all characters are between a white space ' ' and little 'z'
|
||||
static int isAlphaStr(char *str)
|
||||
static int isAlphaStr(const char *str)
|
||||
{
|
||||
int c;
|
||||
char c;
|
||||
|
||||
c = str[0];
|
||||
while (c)
|
||||
|
@ -347,7 +362,7 @@ static int isAlphaStr(char *str)
|
|||
|
||||
int WavInFile::readRIFFBlock()
|
||||
{
|
||||
fread(&(header.riff), sizeof(WavRiff), 1, fptr);
|
||||
if (fread(&(header.riff), sizeof(WavRiff), 1, fptr) != 1) return -1;
|
||||
|
||||
// swap 32bit data byte order if necessary
|
||||
_swap32((unsigned int &)header.riff.package_len);
|
||||
|
@ -369,7 +384,7 @@ int WavInFile::readHeaderBlock()
|
|||
string sLabel;
|
||||
|
||||
// lead label string
|
||||
fread(label, 1, 4, fptr);
|
||||
if (fread(label, 1, 4, fptr) !=4) return -1;
|
||||
label[4] = 0;
|
||||
|
||||
if (isAlphaStr(label) == 0) return -1; // not a valid label
|
||||
|
@ -383,13 +398,13 @@ int WavInFile::readHeaderBlock()
|
|||
memcpy(header.format.fmt, fmtStr, 4);
|
||||
|
||||
// read length of the format field
|
||||
fread(&nLen, sizeof(int), 1, fptr);
|
||||
if (fread(&nLen, sizeof(int), 1, fptr) != 1) return -1;
|
||||
// swap byte order if necessary
|
||||
_swap32((unsigned int &)nLen); // int format_len;
|
||||
header.format.format_len = nLen;
|
||||
|
||||
// calculate how much length differs from expected
|
||||
nDump = nLen - (sizeof(header.format) - 8);
|
||||
nDump = nLen - ((int)sizeof(header.format) - 8);
|
||||
|
||||
// if format_len is larger than expected, read only as much data as we've space for
|
||||
if (nDump > 0)
|
||||
|
@ -398,7 +413,7 @@ int WavInFile::readHeaderBlock()
|
|||
}
|
||||
|
||||
// read data
|
||||
fread(&(header.format.fixed), nLen, 1, fptr);
|
||||
if (fread(&(header.format.fixed), nLen, 1, fptr) != 1) return -1;
|
||||
|
||||
// swap byte order if necessary
|
||||
_swap16((unsigned short &)header.format.fixed); // short int fixed;
|
||||
|
@ -420,7 +435,7 @@ int WavInFile::readHeaderBlock()
|
|||
{
|
||||
// 'data' block
|
||||
memcpy(header.data.data_field, dataStr, 4);
|
||||
fread(&(header.data.data_len), sizeof(uint), 1, fptr);
|
||||
if (fread(&(header.data.data_len), sizeof(uint), 1, fptr) != 1) return -1;
|
||||
|
||||
// swap byte order if necessary
|
||||
_swap32((unsigned int &)header.data.data_len);
|
||||
|
@ -434,11 +449,11 @@ int WavInFile::readHeaderBlock()
|
|||
// unknown block
|
||||
|
||||
// read length
|
||||
fread(&len, sizeof(len), 1, fptr);
|
||||
if (fread(&len, sizeof(len), 1, fptr) != 1) return -1;
|
||||
// scan through the block
|
||||
for (i = 0; i < len; i ++)
|
||||
{
|
||||
fread(&temp, 1, 1, fptr);
|
||||
if (fread(&temp, 1, 1, fptr) != 1) return -1;
|
||||
if (feof(fptr)) return -1; // unexpected eof
|
||||
}
|
||||
}
|
||||
|
@ -499,7 +514,8 @@ uint WavInFile::getDataSizeInBytes() const
|
|||
|
||||
uint WavInFile::getNumSamples() const
|
||||
{
|
||||
return header.data.data_len / header.format.byte_per_sample;
|
||||
if (header.format.byte_per_sample == 0) return 0;
|
||||
return header.data.data_len / (unsigned short)header.format.byte_per_sample;
|
||||
}
|
||||
|
||||
|
||||
|
@ -536,15 +552,30 @@ WavOutFile::WavOutFile(const char *fileName, int sampleRate, int bits, int chann
|
|||
|
||||
fillInHeader(sampleRate, bits, channels);
|
||||
writeHeader();
|
||||
|
||||
flushTime = flushRate;
|
||||
}
|
||||
|
||||
|
||||
WavOutFile::WavOutFile(FILE *file, int sampleRate, int bits, int channels)
|
||||
{
|
||||
bytesWritten = 0;
|
||||
fptr = file;
|
||||
if (fptr == NULL)
|
||||
{
|
||||
string msg = "Error : Unable to access output file stream.";
|
||||
throw runtime_error(msg);
|
||||
}
|
||||
|
||||
fillInHeader(sampleRate, bits, channels);
|
||||
writeHeader();
|
||||
}
|
||||
|
||||
|
||||
|
||||
WavOutFile::~WavOutFile()
|
||||
{
|
||||
close();
|
||||
finishHeader();
|
||||
if (fptr) fclose(fptr);
|
||||
fptr = NULL;
|
||||
}
|
||||
|
||||
|
||||
|
@ -569,11 +600,11 @@ void WavOutFile::fillInHeader(uint sampleRate, uint bits, uint channels)
|
|||
header.format.format_len = 0x10;
|
||||
header.format.fixed = 1;
|
||||
header.format.channel_number = (short)channels;
|
||||
header.format.sample_rate = sampleRate;
|
||||
header.format.sample_rate = (int)sampleRate;
|
||||
header.format.bits_per_sample = (short)bits;
|
||||
header.format.byte_per_sample = (short)(bits * channels / 8);
|
||||
header.format.byte_rate = header.format.byte_per_sample * sampleRate;
|
||||
header.format.sample_rate = sampleRate;
|
||||
header.format.byte_rate = header.format.byte_per_sample * (int)sampleRate;
|
||||
header.format.sample_rate = (int)sampleRate;
|
||||
|
||||
// fill in the 'data' part..
|
||||
|
||||
|
@ -598,6 +629,7 @@ void WavOutFile::finishHeader()
|
|||
void WavOutFile::writeHeader()
|
||||
{
|
||||
WavHeader hdrTemp;
|
||||
int res;
|
||||
|
||||
// swap byte order if necessary
|
||||
hdrTemp = header;
|
||||
|
@ -613,30 +645,18 @@ void WavOutFile::writeHeader()
|
|||
|
||||
// write the supplemented header in the beginning of the file
|
||||
fseek(fptr, 0, SEEK_SET);
|
||||
fwrite(&hdrTemp, sizeof(hdrTemp), 1, fptr);
|
||||
res = fwrite(&hdrTemp, sizeof(hdrTemp), 1, fptr);
|
||||
if (res != 1)
|
||||
{
|
||||
throw runtime_error("Error while writing to a wav file.");
|
||||
}
|
||||
|
||||
// jump back to the end of the file
|
||||
fseek(fptr, 0, SEEK_END);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void WavOutFile::close()
|
||||
{
|
||||
finishHeader();
|
||||
fclose(fptr);
|
||||
fptr = NULL;
|
||||
}
|
||||
|
||||
void WavOutFile::flush( int numElems )
|
||||
{
|
||||
flushTime -= numElems;
|
||||
if( flushTime < 0 )
|
||||
{
|
||||
flushTime += flushRate;
|
||||
finishHeader();
|
||||
}
|
||||
}
|
||||
|
||||
void WavOutFile::write(const char *buffer, int numElems)
|
||||
{
|
||||
int res;
|
||||
|
@ -654,7 +674,6 @@ void WavOutFile::write(const char *buffer, int numElems)
|
|||
}
|
||||
|
||||
bytesWritten += numElems;
|
||||
flush( numElems );
|
||||
}
|
||||
|
||||
|
||||
|
@ -698,7 +717,6 @@ void WavOutFile::write(const short *buffer, int numElems)
|
|||
throw runtime_error("Error while writing to a wav file.");
|
||||
}
|
||||
bytesWritten += 2 * numElems;
|
||||
flush( numElems*2 );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -722,7 +740,6 @@ void WavOutFile::write(const float *buffer, int numElems)
|
|||
}
|
||||
|
||||
write(temp, numElems);
|
||||
flush( numElems );
|
||||
|
||||
delete[] temp;
|
||||
}
|
||||
|
|
|
@ -16,10 +16,10 @@
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.7 $
|
||||
// Last changed : $Date: 2009-02-21 18:00:14 +0200 (Sat, 21 Feb 2009) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: WavFile.h,v 1.7 2006/02/05 16:44:06 Olli Exp $
|
||||
// $Id: WavFile.h 63 2009-02-21 16:00:14Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -105,13 +105,16 @@ private:
|
|||
/// WAV header information
|
||||
WavHeader header;
|
||||
|
||||
/// Init the WAV file stream
|
||||
void init();
|
||||
|
||||
/// Read WAV file headers.
|
||||
/// \return zero if all ok, nonzero if file format is invalid.
|
||||
int readWavHeaders();
|
||||
|
||||
/// Checks WAV file header tags.
|
||||
/// \return zero if all ok, nonzero if file format is invalid.
|
||||
int checkCharTags();
|
||||
int checkCharTags() const;
|
||||
|
||||
/// Reads a single WAV file header block.
|
||||
/// \return zero if all ok, nonzero if file format is invalid.
|
||||
|
@ -125,13 +128,11 @@ public:
|
|||
/// throws 'runtime_error' exception.
|
||||
WavInFile(const char *filename);
|
||||
|
||||
WavInFile(FILE *file);
|
||||
|
||||
/// Destructor: Closes the file.
|
||||
~WavInFile();
|
||||
|
||||
/// Close the file. Notice that file is automatically closed also when the
|
||||
/// class instance is deleted.
|
||||
void close();
|
||||
|
||||
/// Rewind to beginning of the file
|
||||
void rewind();
|
||||
|
||||
|
@ -203,9 +204,6 @@ private:
|
|||
/// Counter of how many bytes have been written to the file so far.
|
||||
int bytesWritten;
|
||||
|
||||
/// number of bytes to be written before next flush.
|
||||
int flushTime;
|
||||
|
||||
/// Fills in WAV file header information.
|
||||
void fillInHeader(const uint sampleRate, const uint bits, const uint channels);
|
||||
|
||||
|
@ -216,14 +214,6 @@ private:
|
|||
/// Writes the WAV file header.
|
||||
void writeHeader();
|
||||
|
||||
/// Flushes the WAV file every so often -- writes header info for the current
|
||||
/// data length and then returns the seek position to the end of the WAV for
|
||||
/// continued writing. This method is called from each write() method.
|
||||
void flush( int numElems );
|
||||
|
||||
/// Flush the WAVheader every 32kb written
|
||||
static const int flushRate = 0x8000;
|
||||
|
||||
public:
|
||||
/// Constructor: Creates a new WAV file. Throws a 'runtime_error' exception
|
||||
/// if file creation fails.
|
||||
|
@ -233,6 +223,8 @@ public:
|
|||
int channels ///< Number of channels (1=mono, 2=stereo)
|
||||
);
|
||||
|
||||
WavOutFile(FILE *file, int sampleRate, int bits, int channels);
|
||||
|
||||
/// Destructor: Finalizes & closes the WAV file.
|
||||
~WavOutFile();
|
||||
|
||||
|
@ -253,12 +245,6 @@ public:
|
|||
void write(const float *buffer, ///< Pointer to sample data buffer.
|
||||
int numElems ///< How many array items are to be written to file.
|
||||
);
|
||||
|
||||
/// Finalize & close the WAV file. Automatically supplements the WAV file header
|
||||
/// information according to written data etc.
|
||||
///
|
||||
/// Notice that file is automatically closed also when the class instance is deleted.
|
||||
void close();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -12,10 +12,10 @@
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.4 $
|
||||
// Last changed : $Date: 2008-02-10 18:26:55 +0200 (Sun, 10 Feb 2008) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: cpu_detect.h,v 1.4 2006/02/05 16:44:06 Olli Exp $
|
||||
// $Id: cpu_detect.h 11 2008-02-10 16:26:55Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// gcc version of the x86 CPU detect routine.
|
||||
/// Generic version of the x86 CPU extension detection routine.
|
||||
///
|
||||
/// This file is to be compiled on any platform with the GNU C compiler.
|
||||
/// Compiler. Please see 'cpu_detect_x86_win.cpp' for the x86 Windows version
|
||||
/// of this file.
|
||||
/// This file is for GNU & other non-Windows compilers, see 'cpu_detect_x86_win.cpp'
|
||||
/// for the Microsoft compiler version.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
|
@ -12,10 +11,10 @@
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.6 $
|
||||
// Last changed : $Date: 2009-02-25 19:13:51 +0200 (Wed, 25 Feb 2009) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: cpu_detect_x86_gcc.cpp,v 1.6 2006/02/05 16:44:06 Olli Exp $
|
||||
// $Id: cpu_detect_x86_gcc.cpp 67 2009-02-25 17:13:51Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -43,21 +42,18 @@
|
|||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include "cpu_detect.h"
|
||||
|
||||
#ifndef __GNUC__
|
||||
#error wrong platform - this source code file is for the GNU C compiler.
|
||||
#endif
|
||||
#include "STTypes.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// processor instructions extension detection routines
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// Flag variable indicating whick ISA extensions are disabled (for debugging)
|
||||
static uint _dwDisabledISA = 0x00; // 0xffffffff; //<- use this to disable all extensions
|
||||
|
||||
|
@ -72,8 +68,10 @@ void disableExtensions(uint dwDisableMask)
|
|||
/// Checks which instruction set extensions are supported by the CPU.
|
||||
uint detectCPUextensions(void)
|
||||
{
|
||||
#ifndef __i386__
|
||||
#if (!(ALLOW_X86_OPTIMIZATIONS) || !(__GNUC__))
|
||||
|
||||
return 0; // always disable extensions on non-x86 platforms.
|
||||
|
||||
#else
|
||||
uint res = 0;
|
||||
|
||||
|
@ -84,22 +82,21 @@ uint detectCPUextensions(void)
|
|||
// check if 'cpuid' instructions is available by toggling eflags bit 21
|
||||
|
||||
"\n\tpushf" // save eflags to stack
|
||||
"\n\tpop %%eax" // load eax from stack (with eflags)
|
||||
"\n\tmovl (%%esp), %%eax" // load eax from stack (with eflags)
|
||||
"\n\tmovl %%eax, %%ecx" // save the original eflags values to ecx
|
||||
"\n\txor $0x00200000, %%eax" // toggle bit 21
|
||||
"\n\tpush %%eax" // store toggled eflags to stack
|
||||
"\n\tmovl %%eax, (%%esp)" // store toggled eflags to stack
|
||||
"\n\tpopf" // load eflags from stack
|
||||
"\n\tpushf" // save updated eflags to stack
|
||||
"\n\tpop %%eax" // load from stack
|
||||
"\n\tmovl (%%esp), %%eax" // load eax from stack
|
||||
"\n\tpopf" // pop stack to restore esp
|
||||
"\n\txor %%edx, %%edx" // clear edx for defaulting no mmx
|
||||
"\n\tcmp %%ecx, %%eax" // compare to original eflags values
|
||||
"\n\tjz end" // jumps to 'end' if cpuid not present
|
||||
|
||||
// cpuid instruction available, test for presence of mmx instructions
|
||||
|
||||
"\n\tmovl $1, %%eax"
|
||||
"\n\tcpuid"
|
||||
// movl $0x00800000, %edx // force enable MMX
|
||||
"\n\ttest $0x00800000, %%edx"
|
||||
"\n\tjz end" // branch if MMX not available
|
||||
|
||||
|
|
|
@ -12,10 +12,10 @@
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.10 $
|
||||
// Last changed : $Date: 2009-02-13 18:22:48 +0200 (Fri, 13 Feb 2009) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: cpu_detect_x86_win.cpp,v 1.10 2006/02/05 16:44:06 Olli Exp $
|
||||
// $Id: cpu_detect_x86_win.cpp 62 2009-02-13 16:22:48Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -42,7 +42,7 @@
|
|||
|
||||
#include "cpu_detect.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#ifndef WIN32
|
||||
#error wrong platform - this source code file is exclusively for Win32 platform
|
||||
#endif
|
||||
|
||||
|
@ -78,13 +78,16 @@ uint detectCPUextensions(void)
|
|||
xor esi, esi ; clear esi = result register
|
||||
|
||||
pushfd ; save eflags to stack
|
||||
pop eax ; load eax from stack (with eflags)
|
||||
mov eax,dword ptr [esp] ; load eax from stack (with eflags)
|
||||
mov ecx, eax ; save the original eflags values to ecx
|
||||
xor eax, 0x00200000 ; toggle bit 21
|
||||
push eax ; store toggled eflags to stack
|
||||
mov dword ptr [esp],eax ; store toggled eflags to stack
|
||||
popfd ; load eflags from stack
|
||||
|
||||
pushfd ; save updated eflags to stack
|
||||
pop eax ; load from stack
|
||||
mov eax,dword ptr [esp] ; load eax from stack
|
||||
popfd ; pop stack to restore stack pointer
|
||||
|
||||
xor edx, edx ; clear edx for defaulting no mmx
|
||||
cmp eax, ecx ; compare to original eflags values
|
||||
jz end ; jumps to 'end' if cpuid not present
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
/// NOTICE: If using Visual Studio 6.0, you'll need to install the "Visual C++
|
||||
/// 6.0 processor pack" update to support compiler intrinsic syntax. The update
|
||||
/// is available for download at Microsoft Developers Network, see here:
|
||||
/// http://msdn.microsoft.com/vstudio/downloads/tools/ppack/default.aspx
|
||||
/// http://msdn.microsoft.com/en-us/vstudio/aa718349.aspx
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
|
@ -20,10 +20,10 @@
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/06 18:52:43 $
|
||||
// File revision : $Revision: 1.1 $
|
||||
// Last changed : $Date: 2009-10-31 16:53:23 +0200 (Sat, 31 Oct 2009) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: mmx_optimized.cpp,v 1.1 2006/02/06 18:52:43 Olli Exp $
|
||||
// $Id: mmx_optimized.cpp 75 2009-10-31 14:53:23Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -53,7 +53,7 @@
|
|||
#ifdef ALLOW_MMX
|
||||
// MMX routines available only with integer sample type
|
||||
|
||||
#if !(_WIN32 || __i386__ || __x86_64__)
|
||||
#if !(WIN32 || __i386__ || __x86_64__)
|
||||
#error "wrong platform - this source code file is exclusively for x86 platforms"
|
||||
#endif
|
||||
|
||||
|
@ -68,6 +68,7 @@ using namespace soundtouch;
|
|||
#include "TDStretch.h"
|
||||
#include <mmintrin.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
|
||||
|
||||
// Calculates cross correlation of two buffers
|
||||
|
@ -75,21 +76,21 @@ long TDStretchMMX::calcCrossCorrStereo(const short *pV1, const short *pV2) const
|
|||
{
|
||||
const __m64 *pVec1, *pVec2;
|
||||
__m64 shifter;
|
||||
__m64 accu;
|
||||
long corr;
|
||||
uint i;
|
||||
__m64 accu, normaccu;
|
||||
long corr, norm;
|
||||
int i;
|
||||
|
||||
pVec1 = (__m64*)pV1;
|
||||
pVec2 = (__m64*)pV2;
|
||||
|
||||
shifter = _m_from_int(overlapDividerBits);
|
||||
accu = _mm_setzero_si64();
|
||||
normaccu = accu = _mm_setzero_si64();
|
||||
|
||||
// Process 4 parallel sets of 2 * stereo samples each during each
|
||||
// round to improve CPU-level parallellization.
|
||||
for (i = 0; i < overlapLength / 8; i ++)
|
||||
{
|
||||
__m64 temp;
|
||||
__m64 temp, temp2;
|
||||
|
||||
// dictionary of instructions:
|
||||
// _m_pmaddwd : 4*16bit multiply-add, resulting two 32bits = [a0*b0+a1*b1 ; a2*b2+a3*b3]
|
||||
|
@ -98,11 +99,17 @@ long TDStretchMMX::calcCrossCorrStereo(const short *pV1, const short *pV2) const
|
|||
|
||||
temp = _mm_add_pi32(_mm_madd_pi16(pVec1[0], pVec2[0]),
|
||||
_mm_madd_pi16(pVec1[1], pVec2[1]));
|
||||
temp2 = _mm_add_pi32(_mm_madd_pi16(pVec1[0], pVec1[0]),
|
||||
_mm_madd_pi16(pVec1[1], pVec1[1]));
|
||||
accu = _mm_add_pi32(accu, _mm_sra_pi32(temp, shifter));
|
||||
normaccu = _mm_add_pi32(normaccu, _mm_sra_pi32(temp2, shifter));
|
||||
|
||||
temp = _mm_add_pi32(_mm_madd_pi16(pVec1[2], pVec2[2]),
|
||||
_mm_madd_pi16(pVec1[3], pVec2[3]));
|
||||
temp2 = _mm_add_pi32(_mm_madd_pi16(pVec1[2], pVec1[2]),
|
||||
_mm_madd_pi16(pVec1[3], pVec1[3]));
|
||||
accu = _mm_add_pi32(accu, _mm_sra_pi32(temp, shifter));
|
||||
normaccu = _mm_add_pi32(normaccu, _mm_sra_pi32(temp2, shifter));
|
||||
|
||||
pVec1 += 4;
|
||||
pVec2 += 4;
|
||||
|
@ -114,10 +121,16 @@ long TDStretchMMX::calcCrossCorrStereo(const short *pV1, const short *pV2) const
|
|||
accu = _mm_add_pi32(accu, _mm_srli_si64(accu, 32));
|
||||
corr = _m_to_int(accu);
|
||||
|
||||
normaccu = _mm_add_pi32(normaccu, _mm_srli_si64(normaccu, 32));
|
||||
norm = _m_to_int(normaccu);
|
||||
|
||||
// Clear MMS state
|
||||
_m_empty();
|
||||
|
||||
return corr;
|
||||
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
||||
// done using floating point operation
|
||||
if (norm == 0) norm = 1; // to avoid div by zero
|
||||
return (long)((double)corr * USHRT_MAX / sqrt((double)norm));
|
||||
// Note: Warning about the missing EMMS instruction is harmless
|
||||
// as it'll be called elsewhere.
|
||||
}
|
||||
|
@ -139,7 +152,7 @@ void TDStretchMMX::overlapStereo(short *output, const short *input) const
|
|||
const __m64 *pVinput, *pVMidBuf;
|
||||
__m64 *pVdest;
|
||||
__m64 mix1, mix2, adder, shifter;
|
||||
uint i;
|
||||
int i;
|
||||
|
||||
pVinput = (const __m64*)input;
|
||||
pVMidBuf = (const __m64*)pMidBuffer;
|
||||
|
@ -154,7 +167,9 @@ void TDStretchMMX::overlapStereo(short *output, const short *input) const
|
|||
mix2 = _mm_add_pi16(mix1, adder);
|
||||
adder = _mm_add_pi16(adder, adder);
|
||||
|
||||
shifter = _m_from_int(overlapDividerBits);
|
||||
// Overlaplength-division by shifter. "+1" is to account for "-1" deduced in
|
||||
// overlapDividerBits calculation earlier.
|
||||
shifter = _m_from_int(overlapDividerBits + 1);
|
||||
|
||||
for (i = 0; i < overlapLength / 4; i ++)
|
||||
{
|
||||
|
@ -227,7 +242,7 @@ void FIRFilterMMX::setCoefficients(const short *coeffs, uint newLength, uint uRe
|
|||
// Ensure that filter coeffs array is aligned to 16-byte boundary
|
||||
delete[] filterCoeffsUnalign;
|
||||
filterCoeffsUnalign = new short[2 * newLength + 8];
|
||||
filterCoeffsAlign = (short *)(((ulongptr)filterCoeffsUnalign + 15) & -16);
|
||||
filterCoeffsAlign = (short *)(((ulong)filterCoeffsUnalign + 15) & -16);
|
||||
|
||||
// rearrange the filter coefficients for mmx routines
|
||||
for (i = 0;i < length; i += 4)
|
||||
|
@ -247,7 +262,7 @@ void FIRFilterMMX::setCoefficients(const short *coeffs, uint newLength, uint uRe
|
|||
|
||||
|
||||
// mmx-optimized version of the filter routine for stereo sound
|
||||
uint FIRFilterMMX::evaluateFilterStereo(short *dest, const short *src, const uint numSamples) const
|
||||
uint FIRFilterMMX::evaluateFilterStereo(short *dest, const short *src, uint numSamples) const
|
||||
{
|
||||
// Create stack copies of the needed member variables for asm routines :
|
||||
uint i, j;
|
||||
|
@ -255,7 +270,7 @@ uint FIRFilterMMX::evaluateFilterStereo(short *dest, const short *src, const uin
|
|||
|
||||
if (length < 2) return 0;
|
||||
|
||||
for (i = 0; i < numSamples / 2; i ++)
|
||||
for (i = 0; i < (numSamples - length) / 2; i ++)
|
||||
{
|
||||
__m64 accu1;
|
||||
__m64 accu2;
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
/// NOTICE: If using Visual Studio 6.0, you'll need to install the "Visual C++
|
||||
/// 6.0 processor pack" update to support SSE instruction set. The update is
|
||||
/// available for download at Microsoft Developers Network, see here:
|
||||
/// http://msdn.microsoft.com/vstudio/downloads/tools/ppack/default.aspx
|
||||
/// http://msdn.microsoft.com/en-us/vstudio/aa718349.aspx
|
||||
///
|
||||
/// If the above URL is expired or removed, go to "http://msdn.microsoft.com" and
|
||||
/// perform a search with keywords "processor pack".
|
||||
|
@ -23,10 +23,10 @@
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.2 $
|
||||
// Last changed : $Date: 2009-12-28 22:32:57 +0200 (Mon, 28 Dec 2009) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: sse_optimized.cpp,v 1.2 2006/02/05 16:44:06 Olli Exp $
|
||||
// $Id: sse_optimized.cpp 80 2009-12-28 20:32:57Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -68,12 +68,15 @@ using namespace soundtouch;
|
|||
|
||||
#include "TDStretch.h"
|
||||
#include <xmmintrin.h>
|
||||
#include <math.h>
|
||||
|
||||
// Calculates cross correlation of two buffers
|
||||
double TDStretchSSE::calcCrossCorrStereo(const float *pV1, const float *pV2) const
|
||||
{
|
||||
uint i;
|
||||
__m128 vSum, *pVec2;
|
||||
int i;
|
||||
const float *pVec1;
|
||||
const __m128 *pVec2;
|
||||
__m128 vSum, vNorm;
|
||||
|
||||
// Note. It means a major slow-down if the routine needs to tolerate
|
||||
// unaligned __m128 memory accesses. It's way faster if we can skip
|
||||
|
@ -103,38 +106,52 @@ double TDStretchSSE::calcCrossCorrStereo(const float *pV1, const float *pV2) con
|
|||
|
||||
// Calculates the cross-correlation value between 'pV1' and 'pV2' vectors
|
||||
// Note: pV2 _must_ be aligned to 16-bit boundary, pV1 need not.
|
||||
pVec2 = (__m128*)pV2;
|
||||
vSum = _mm_setzero_ps();
|
||||
pVec1 = (const float*)pV1;
|
||||
pVec2 = (const __m128*)pV2;
|
||||
vSum = vNorm = _mm_setzero_ps();
|
||||
|
||||
// Unroll the loop by factor of 4 * 4 operations
|
||||
for (i = 0; i < overlapLength / 8; i ++)
|
||||
{
|
||||
__m128 vTemp;
|
||||
// vSum += pV1[0..3] * pV2[0..3]
|
||||
vSum = _mm_add_ps(vSum, _mm_mul_ps(_MM_LOAD(pV1),pVec2[0]));
|
||||
vTemp = _MM_LOAD(pVec1);
|
||||
vSum = _mm_add_ps(vSum, _mm_mul_ps(vTemp ,pVec2[0]));
|
||||
vNorm = _mm_add_ps(vNorm, _mm_mul_ps(vTemp ,vTemp));
|
||||
|
||||
// vSum += pV1[4..7] * pV2[4..7]
|
||||
vSum = _mm_add_ps(vSum, _mm_mul_ps(_MM_LOAD(pV1 + 4), pVec2[1]));
|
||||
vTemp = _MM_LOAD(pVec1 + 4);
|
||||
vSum = _mm_add_ps(vSum, _mm_mul_ps(vTemp, pVec2[1]));
|
||||
vNorm = _mm_add_ps(vNorm, _mm_mul_ps(vTemp ,vTemp));
|
||||
|
||||
// vSum += pV1[8..11] * pV2[8..11]
|
||||
vSum = _mm_add_ps(vSum, _mm_mul_ps(_MM_LOAD(pV1 + 8), pVec2[2]));
|
||||
vTemp = _MM_LOAD(pVec1 + 8);
|
||||
vSum = _mm_add_ps(vSum, _mm_mul_ps(vTemp, pVec2[2]));
|
||||
vNorm = _mm_add_ps(vNorm, _mm_mul_ps(vTemp ,vTemp));
|
||||
|
||||
// vSum += pV1[12..15] * pV2[12..15]
|
||||
vSum = _mm_add_ps(vSum, _mm_mul_ps(_MM_LOAD(pV1 + 12), pVec2[3]));
|
||||
vTemp = _MM_LOAD(pVec1 + 12);
|
||||
vSum = _mm_add_ps(vSum, _mm_mul_ps(vTemp, pVec2[3]));
|
||||
vNorm = _mm_add_ps(vNorm, _mm_mul_ps(vTemp ,vTemp));
|
||||
|
||||
pV1 += 16;
|
||||
pVec1 += 16;
|
||||
pVec2 += 4;
|
||||
}
|
||||
|
||||
// return value = vSum[0] + vSum[1] + vSum[2] + vSum[3]
|
||||
float *pvSum = (float*)&vSum;
|
||||
return (double)(pvSum[0] + pvSum[1] + pvSum[2] + pvSum[3]);
|
||||
float *pvNorm = (float*)&vNorm;
|
||||
double norm = sqrt(pvNorm[0] + pvNorm[1] + pvNorm[2] + pvNorm[3]);
|
||||
if (norm < 1e-9) norm = 1.0; // to avoid div by zero
|
||||
|
||||
/* This is approximately corresponding routine in C-language:
|
||||
double corr;
|
||||
float *pvSum = (float*)&vSum;
|
||||
return (double)(pvSum[0] + pvSum[1] + pvSum[2] + pvSum[3]) / norm;
|
||||
|
||||
/* This is approximately corresponding routine in C-language yet without normalization:
|
||||
double corr, norm;
|
||||
uint i;
|
||||
|
||||
// Calculates the cross-correlation value between 'pV1' and 'pV2' vectors
|
||||
corr = 0.0;
|
||||
corr = norm = 0.0;
|
||||
for (i = 0; i < overlapLength / 8; i ++)
|
||||
{
|
||||
corr += pV1[0] * pV2[0] +
|
||||
|
@ -154,13 +171,16 @@ double TDStretchSSE::calcCrossCorrStereo(const float *pV1, const float *pV2) con
|
|||
pV1[14] * pV2[14] +
|
||||
pV1[15] * pV2[15];
|
||||
|
||||
for (j = 0; j < 15; j ++) norm += pV1[j] * pV1[j];
|
||||
|
||||
pV1 += 16;
|
||||
pV2 += 16;
|
||||
}
|
||||
return corr / sqrt(norm);
|
||||
*/
|
||||
|
||||
/* This is corresponding routine in assembler. This may be teeny-weeny bit faster
|
||||
than intrinsic version, but more difficult to maintain & get compiled on multiple
|
||||
/* This is a bit outdated, corresponding routine in assembler. This may be teeny-weeny bit
|
||||
faster than intrinsic version, but more difficult to maintain & get compiled on multiple
|
||||
platforms.
|
||||
|
||||
uint overlapLengthLocal = overlapLength;
|
||||
|
@ -239,6 +259,7 @@ double TDStretchSSE::calcCrossCorrStereo(const float *pV1, const float *pV2) con
|
|||
|
||||
FIRFilterSSE::FIRFilterSSE() : FIRFilter()
|
||||
{
|
||||
filterCoeffsAlign = NULL;
|
||||
filterCoeffsUnalign = NULL;
|
||||
}
|
||||
|
||||
|
@ -246,6 +267,8 @@ FIRFilterSSE::FIRFilterSSE() : FIRFilter()
|
|||
FIRFilterSSE::~FIRFilterSSE()
|
||||
{
|
||||
delete[] filterCoeffsUnalign;
|
||||
filterCoeffsAlign = NULL;
|
||||
filterCoeffsUnalign = NULL;
|
||||
}
|
||||
|
||||
|
||||
|
@ -262,7 +285,7 @@ void FIRFilterSSE::setCoefficients(const float *coeffs, uint newLength, uint uRe
|
|||
// Ensure that filter coeffs array is aligned to 16-byte boundary
|
||||
delete[] filterCoeffsUnalign;
|
||||
filterCoeffsUnalign = new float[2 * newLength + 4];
|
||||
filterCoeffsAlign = (float *)(((unsigned long)filterCoeffsUnalign + 15) & -16);
|
||||
filterCoeffsAlign = (float *)(((unsigned long)filterCoeffsUnalign + 15) & (ulong)-16);
|
||||
|
||||
fDivider = (float)resultDivider;
|
||||
|
||||
|
@ -279,15 +302,18 @@ void FIRFilterSSE::setCoefficients(const float *coeffs, uint newLength, uint uRe
|
|||
// SSE-optimized version of the filter routine for stereo sound
|
||||
uint FIRFilterSSE::evaluateFilterStereo(float *dest, const float *source, uint numSamples) const
|
||||
{
|
||||
int count = (numSamples - length) & -2;
|
||||
int count = (int)((numSamples - length) & (uint)-2);
|
||||
int j;
|
||||
|
||||
assert(count % 2 == 0);
|
||||
|
||||
if (count < 2) return 0;
|
||||
|
||||
assert(source != NULL);
|
||||
assert(dest != NULL);
|
||||
assert((length % 8) == 0);
|
||||
assert(((unsigned long)filterCoeffsAlign) % 16 == 0);
|
||||
assert(filterCoeffsAlign != NULL);
|
||||
assert(((ulong)filterCoeffsAlign) % 16 == 0);
|
||||
|
||||
// filter is evaluated for two stereo samples with each iteration, thus use of 'j += 2'
|
||||
for (j = 0; j < count; j += 2)
|
||||
|
@ -297,9 +323,9 @@ uint FIRFilterSSE::evaluateFilterStereo(float *dest, const float *source, uint n
|
|||
__m128 sum1, sum2;
|
||||
uint i;
|
||||
|
||||
pSrc = source; // source audio data
|
||||
pFil = (__m128*)filterCoeffsAlign; // filter coefficients. NOTE: Assumes coefficients
|
||||
// are aligned to 16-byte boundary
|
||||
pSrc = (const float*)source; // source audio data
|
||||
pFil = (const __m128*)filterCoeffsAlign; // filter coefficients. NOTE: Assumes coefficients
|
||||
// are aligned to 16-byte boundary
|
||||
sum1 = sum2 = _mm_setzero_ps();
|
||||
|
||||
for (i = 0; i < length / 8; i ++)
|
||||
|
|
|
@ -93,9 +93,9 @@ namespace SoundtouchCfg
|
|||
static const int Overlap_Min = 3;
|
||||
static const int Overlap_Max = 15;
|
||||
|
||||
static int SequenceLenMS = 63;
|
||||
static int SeekWindowMS = 16;
|
||||
static int OverlapMS = 7;
|
||||
static int SequenceLenMS = 50;
|
||||
static int SeekWindowMS = 15;
|
||||
static int OverlapMS = 25;
|
||||
|
||||
void ReadSettings();
|
||||
void WriteSettings();
|
||||
|
|
|
@ -284,11 +284,12 @@ void SndBuffer::timeStretchWrite()
|
|||
{
|
||||
if( progress )
|
||||
{
|
||||
if( ++ts_stats_logcounter > 300 )
|
||||
if( ++ts_stats_logcounter > 150 )
|
||||
{
|
||||
ts_stats_logcounter = 0;
|
||||
ConLog( " * SPU2 > Timestretch Stats > %d%% of packets stretched.\n",
|
||||
( ts_stats_stretchblocks * 100 ) / ( ts_stats_normalblocks + ts_stats_stretchblocks ) );
|
||||
ConLog( " * SPU2 > Timestretch Stats > %d percent stretched. Total stretchblocks = %d.\n",
|
||||
( ts_stats_stretchblocks * 100 ) / ( ts_stats_normalblocks + ts_stats_stretchblocks ),
|
||||
ts_stats_stretchblocks);
|
||||
ts_stats_normalblocks = 0;
|
||||
ts_stats_stretchblocks = 0;
|
||||
}
|
||||
|
|
|
@ -20,9 +20,9 @@
|
|||
|
||||
#include "SoundTouch/SoundTouch.h"
|
||||
|
||||
static int SequenceLenMS = 63;
|
||||
static int SeekWindowMS = 16;
|
||||
static int OverlapMS = 7;
|
||||
static int SequenceLenMS = 50;
|
||||
static int SeekWindowMS = 15;
|
||||
static int OverlapMS = 25;
|
||||
|
||||
// Timestretch Slider Bounds, Min/Max
|
||||
static const int SequenceLen_Min = 50;
|
||||
|
|
Loading…
Reference in New Issue