Merge pull request #3406 from skidau/soundtouch-192
Updated SoundTouch library to 1.9.2
This commit is contained in:
commit
fb55544021
|
@ -12,7 +12,7 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2014-01-06 08:40:22 +1100 (Mon, 06 Jan 2014) $
|
// Last changed : $Date: 2014-01-05 23:40:22 +0200 (Sun, 05 Jan 2014) $
|
||||||
// File revision : $Revision: 4 $
|
// File revision : $Revision: 4 $
|
||||||
//
|
//
|
||||||
// $Id: AAFilter.cpp 177 2014-01-05 21:40:22Z oparviai $
|
// $Id: AAFilter.cpp 177 2014-01-05 21:40:22Z oparviai $
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2014-01-08 06:41:23 +1100 (Wed, 08 Jan 2014) $
|
// Last changed : $Date: 2014-01-07 21:41:23 +0200 (Tue, 07 Jan 2014) $
|
||||||
// File revision : $Revision: 4 $
|
// File revision : $Revision: 4 $
|
||||||
//
|
//
|
||||||
// $Id: AAFilter.h 187 2014-01-07 19:41:23Z oparviai $
|
// $Id: AAFilter.h 187 2014-01-07 19:41:23Z oparviai $
|
||||||
|
|
|
@ -26,10 +26,10 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2012-08-31 05:45:25 +1000 (Fri, 31 Aug 2012) $
|
// Last changed : $Date: 2015-02-21 23:24:29 +0200 (Sat, 21 Feb 2015) $
|
||||||
// File revision : $Revision: 4 $
|
// File revision : $Revision: 4 $
|
||||||
//
|
//
|
||||||
// $Id: BPMDetect.cpp 149 2012-08-30 19:45:25Z oparviai $
|
// $Id: BPMDetect.cpp 202 2015-02-21 21:24:29Z oparviai $
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
|
@ -226,6 +226,7 @@ void BPMDetect::updateXCorr(int process_samples)
|
||||||
assert(buffer->numSamples() >= (uint)(process_samples + windowLen));
|
assert(buffer->numSamples() >= (uint)(process_samples + windowLen));
|
||||||
|
|
||||||
pBuffer = buffer->ptrBegin();
|
pBuffer = buffer->ptrBegin();
|
||||||
|
#pragma omp parallel for
|
||||||
for (offs = windowStart; offs < windowLen; offs ++)
|
for (offs = windowStart; offs < windowLen; offs ++)
|
||||||
{
|
{
|
||||||
LONG_SAMPLETYPE sum;
|
LONG_SAMPLETYPE sum;
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2012-08-30 19:53:44 +0000 (Thu, 30 Aug 2012) $
|
// Last changed : $Date: 2012-08-30 22:53:44 +0300 (Thu, 30 Aug 2012) $
|
||||||
// File revision : $Revision: 4 $
|
// File revision : $Revision: 4 $
|
||||||
//
|
//
|
||||||
// $Id: BPMDetect.h 150 2012-08-30 19:53:44Z oparviai $
|
// $Id: BPMDetect.h 150 2012-08-30 19:53:44Z oparviai $
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2012-11-09 05:53:01 +1100 (Fri, 09 Nov 2012) $
|
// Last changed : $Date: 2012-11-08 20:53:01 +0200 (Thu, 08 Nov 2012) $
|
||||||
// File revision : $Revision: 4 $
|
// File revision : $Revision: 4 $
|
||||||
//
|
//
|
||||||
// $Id: FIFOSampleBuffer.cpp 160 2012-11-08 18:53:01Z oparviai $
|
// $Id: FIFOSampleBuffer.cpp 160 2012-11-08 18:53:01Z oparviai $
|
||||||
|
@ -86,10 +86,6 @@ void FIFOSampleBuffer::setChannels(int numChannels)
|
||||||
samplesInBuffer = usedBytes / channels;
|
samplesInBuffer = usedBytes / channels;
|
||||||
}
|
}
|
||||||
|
|
||||||
int FIFOSampleBuffer::getChannels()
|
|
||||||
{
|
|
||||||
return channels;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if output location pointer 'bufferPos' isn't zero, 'rewinds' the buffer and
|
// if output location pointer 'bufferPos' isn't zero, 'rewinds' the buffer and
|
||||||
// zeroes this pointer by copying samples from the 'bufferPos' pointer
|
// zeroes this pointer by copying samples from the 'bufferPos' pointer
|
||||||
|
|
|
@ -15,10 +15,10 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2012-06-13 19:29:53 +0000 (Wed, 13 Jun 2012) $
|
// Last changed : $Date: 2014-01-05 23:40:22 +0200 (Sun, 05 Jan 2014) $
|
||||||
// File revision : $Revision: 4 $
|
// File revision : $Revision: 4 $
|
||||||
//
|
//
|
||||||
// $Id: FIFOSampleBuffer.h 143 2012-06-13 19:29:53Z oparviai $
|
// $Id: FIFOSampleBuffer.h 177 2014-01-05 21:40:22Z oparviai $
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
|
@ -161,7 +161,12 @@ public:
|
||||||
|
|
||||||
/// Sets number of channels, 1 = mono, 2 = stereo.
|
/// Sets number of channels, 1 = mono, 2 = stereo.
|
||||||
void setChannels(int numChannels);
|
void setChannels(int numChannels);
|
||||||
int getChannels();
|
|
||||||
|
/// Get number of channels
|
||||||
|
int getChannels()
|
||||||
|
{
|
||||||
|
return channels;
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns nonzero if there aren't any samples available for outputting.
|
/// Returns nonzero if there aren't any samples available for outputting.
|
||||||
virtual int isEmpty() const;
|
virtual int isEmpty() const;
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2012-06-13 19:29:53 +0000 (Wed, 13 Jun 2012) $
|
// Last changed : $Date: 2012-06-13 22:29:53 +0300 (Wed, 13 Jun 2012) $
|
||||||
// File revision : $Revision: 4 $
|
// File revision : $Revision: 4 $
|
||||||
//
|
//
|
||||||
// $Id: FIFOSamplePipe.h 143 2012-06-13 19:29:53Z oparviai $
|
// $Id: FIFOSamplePipe.h 143 2012-06-13 19:29:53Z oparviai $
|
||||||
|
|
|
@ -11,10 +11,10 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2013-06-13 01:24:44 +1000 (Thu, 13 Jun 2013) $
|
// Last changed : $Date: 2015-02-21 23:24:29 +0200 (Sat, 21 Feb 2015) $
|
||||||
// File revision : $Revision: 4 $
|
// File revision : $Revision: 4 $
|
||||||
//
|
//
|
||||||
// $Id: FIRFilter.cpp 171 2013-06-12 15:24:44Z oparviai $
|
// $Id: FIRFilter.cpp 202 2015-02-21 21:24:29Z oparviai $
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
|
@ -72,8 +72,7 @@ FIRFilter::~FIRFilter()
|
||||||
// Usual C-version of the filter routine for stereo sound
|
// Usual C-version of the filter routine for stereo sound
|
||||||
uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const
|
uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const
|
||||||
{
|
{
|
||||||
uint i, j, end;
|
int j, end;
|
||||||
LONG_SAMPLETYPE suml, sumr;
|
|
||||||
#ifdef SOUNDTOUCH_FLOAT_SAMPLES
|
#ifdef SOUNDTOUCH_FLOAT_SAMPLES
|
||||||
// when using floating point samples, use a scaler instead of a divider
|
// when using floating point samples, use a scaler instead of a divider
|
||||||
// because division is much slower operation than multiplying.
|
// because division is much slower operation than multiplying.
|
||||||
|
@ -87,9 +86,12 @@ uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, ui
|
||||||
|
|
||||||
end = 2 * (numSamples - length);
|
end = 2 * (numSamples - length);
|
||||||
|
|
||||||
|
#pragma omp parallel for
|
||||||
for (j = 0; j < end; j += 2)
|
for (j = 0; j < end; j += 2)
|
||||||
{
|
{
|
||||||
const SAMPLETYPE *ptr;
|
const SAMPLETYPE *ptr;
|
||||||
|
LONG_SAMPLETYPE suml, sumr;
|
||||||
|
uint i;
|
||||||
|
|
||||||
suml = sumr = 0;
|
suml = sumr = 0;
|
||||||
ptr = src + j;
|
ptr = src + j;
|
||||||
|
@ -130,28 +132,31 @@ uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, ui
|
||||||
// Usual C-version of the filter routine for mono sound
|
// Usual C-version of the filter routine for mono sound
|
||||||
uint FIRFilter::evaluateFilterMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const
|
uint FIRFilter::evaluateFilterMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const
|
||||||
{
|
{
|
||||||
uint i, j, end;
|
int j, end;
|
||||||
LONG_SAMPLETYPE sum;
|
|
||||||
#ifdef SOUNDTOUCH_FLOAT_SAMPLES
|
#ifdef SOUNDTOUCH_FLOAT_SAMPLES
|
||||||
// when using floating point samples, use a scaler instead of a divider
|
// when using floating point samples, use a scaler instead of a divider
|
||||||
// because division is much slower operation than multiplying.
|
// because division is much slower operation than multiplying.
|
||||||
double dScaler = 1.0 / (double)resultDivider;
|
double dScaler = 1.0 / (double)resultDivider;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
assert(length != 0);
|
assert(length != 0);
|
||||||
|
|
||||||
end = numSamples - length;
|
end = numSamples - length;
|
||||||
|
#pragma omp parallel for
|
||||||
for (j = 0; j < end; j ++)
|
for (j = 0; j < end; j ++)
|
||||||
{
|
{
|
||||||
|
const SAMPLETYPE *pSrc = src + j;
|
||||||
|
LONG_SAMPLETYPE sum;
|
||||||
|
uint i;
|
||||||
|
|
||||||
sum = 0;
|
sum = 0;
|
||||||
for (i = 0; i < length; i += 4)
|
for (i = 0; i < length; i += 4)
|
||||||
{
|
{
|
||||||
// loop is unrolled by factor of 4 here for efficiency
|
// loop is unrolled by factor of 4 here for efficiency
|
||||||
sum += src[i + 0] * filterCoeffs[i + 0] +
|
sum += pSrc[i + 0] * filterCoeffs[i + 0] +
|
||||||
src[i + 1] * filterCoeffs[i + 1] +
|
pSrc[i + 1] * filterCoeffs[i + 1] +
|
||||||
src[i + 2] * filterCoeffs[i + 2] +
|
pSrc[i + 2] * filterCoeffs[i + 2] +
|
||||||
src[i + 3] * filterCoeffs[i + 3];
|
pSrc[i + 3] * filterCoeffs[i + 3];
|
||||||
}
|
}
|
||||||
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
sum >>= resultDivFactor;
|
sum >>= resultDivFactor;
|
||||||
|
@ -161,16 +166,15 @@ uint FIRFilter::evaluateFilterMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint
|
||||||
sum *= dScaler;
|
sum *= dScaler;
|
||||||
#endif // SOUNDTOUCH_INTEGER_SAMPLES
|
#endif // SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
dest[j] = (SAMPLETYPE)sum;
|
dest[j] = (SAMPLETYPE)sum;
|
||||||
src ++;
|
|
||||||
}
|
}
|
||||||
return end;
|
return end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
uint FIRFilter::evaluateFilterMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels) const
|
uint FIRFilter::evaluateFilterMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels)
|
||||||
{
|
{
|
||||||
uint i, j, end, c;
|
int j, end;
|
||||||
LONG_SAMPLETYPE *sum=(LONG_SAMPLETYPE*)malloc(numChannels*sizeof(*sum));
|
|
||||||
#ifdef SOUNDTOUCH_FLOAT_SAMPLES
|
#ifdef SOUNDTOUCH_FLOAT_SAMPLES
|
||||||
// when using floating point samples, use a scaler instead of a divider
|
// when using floating point samples, use a scaler instead of a divider
|
||||||
// because division is much slower operation than multiplying.
|
// because division is much slower operation than multiplying.
|
||||||
|
@ -181,17 +185,21 @@ uint FIRFilter::evaluateFilterMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uin
|
||||||
assert(src != NULL);
|
assert(src != NULL);
|
||||||
assert(dest != NULL);
|
assert(dest != NULL);
|
||||||
assert(filterCoeffs != NULL);
|
assert(filterCoeffs != NULL);
|
||||||
|
assert(numChannels < 16);
|
||||||
|
|
||||||
end = numChannels * (numSamples - length);
|
end = numChannels * (numSamples - length);
|
||||||
|
|
||||||
for (c = 0; c < numChannels; c ++)
|
#pragma omp parallel for
|
||||||
{
|
|
||||||
sum[c] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (j = 0; j < end; j += numChannels)
|
for (j = 0; j < end; j += numChannels)
|
||||||
{
|
{
|
||||||
const SAMPLETYPE *ptr;
|
const SAMPLETYPE *ptr;
|
||||||
|
LONG_SAMPLETYPE sums[16];
|
||||||
|
uint c, i;
|
||||||
|
|
||||||
|
for (c = 0; c < numChannels; c ++)
|
||||||
|
{
|
||||||
|
sums[c] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
ptr = src + j;
|
ptr = src + j;
|
||||||
|
|
||||||
|
@ -200,7 +208,7 @@ uint FIRFilter::evaluateFilterMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uin
|
||||||
SAMPLETYPE coef=filterCoeffs[i];
|
SAMPLETYPE coef=filterCoeffs[i];
|
||||||
for (c = 0; c < numChannels; c ++)
|
for (c = 0; c < numChannels; c ++)
|
||||||
{
|
{
|
||||||
sum[c] += ptr[0] * coef;
|
sums[c] += ptr[0] * coef;
|
||||||
ptr ++;
|
ptr ++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -208,13 +216,11 @@ uint FIRFilter::evaluateFilterMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uin
|
||||||
for (c = 0; c < numChannels; c ++)
|
for (c = 0; c < numChannels; c ++)
|
||||||
{
|
{
|
||||||
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
sum[c] >>= resultDivFactor;
|
sums[c] >>= resultDivFactor;
|
||||||
#else
|
#else
|
||||||
sum[c] *= dScaler;
|
sums[c] *= dScaler;
|
||||||
#endif // SOUNDTOUCH_INTEGER_SAMPLES
|
#endif // SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
*dest = (SAMPLETYPE)sum[c];
|
dest[j+c] = (SAMPLETYPE)sums[c];
|
||||||
dest++;
|
|
||||||
sum[c] = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return numSamples - length;
|
return numSamples - length;
|
||||||
|
@ -253,7 +259,7 @@ uint FIRFilter::getLength() const
|
||||||
//
|
//
|
||||||
// Note : The amount of outputted samples is by value of 'filter_length'
|
// Note : The amount of outputted samples is by value of 'filter_length'
|
||||||
// smaller than the amount of input samples.
|
// smaller than the amount of input samples.
|
||||||
uint FIRFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels) const
|
uint FIRFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels)
|
||||||
{
|
{
|
||||||
assert(length > 0);
|
assert(length > 0);
|
||||||
assert(lengthDiv8 * 8 == length);
|
assert(lengthDiv8 * 8 == length);
|
||||||
|
|
|
@ -11,10 +11,10 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2013-06-13 01:24:44 +1000 (Thu, 13 Jun 2013) $
|
// Last changed : $Date: 2015-02-21 23:24:29 +0200 (Sat, 21 Feb 2015) $
|
||||||
// File revision : $Revision: 4 $
|
// File revision : $Revision: 4 $
|
||||||
//
|
//
|
||||||
// $Id: FIRFilter.h 171 2013-06-12 15:24:44Z oparviai $
|
// $Id: FIRFilter.h 202 2015-02-21 21:24:29Z oparviai $
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
|
@ -71,7 +71,7 @@ protected:
|
||||||
virtual uint evaluateFilterMono(SAMPLETYPE *dest,
|
virtual uint evaluateFilterMono(SAMPLETYPE *dest,
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
uint numSamples) const;
|
uint numSamples) const;
|
||||||
virtual uint evaluateFilterMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels) const;
|
virtual uint evaluateFilterMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FIRFilter();
|
FIRFilter();
|
||||||
|
@ -91,7 +91,7 @@ public:
|
||||||
uint evaluate(SAMPLETYPE *dest,
|
uint evaluate(SAMPLETYPE *dest,
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
uint numSamples,
|
uint numSamples,
|
||||||
uint numChannels) const;
|
uint numChannels);
|
||||||
|
|
||||||
uint getLength() const;
|
uint getLength() const;
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// $Id: InterpolateCubic.h 179 2014-01-06 18:41:42Z oparviai $
|
// $Id: InterpolateCubic.h 225 2015-07-26 14:45:48Z oparviai $
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
|
@ -56,7 +56,7 @@ protected:
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
int &srcSamples);
|
int &srcSamples);
|
||||||
|
|
||||||
float fract;
|
double fract;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
InterpolateCubic();
|
InterpolateCubic();
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// $Id: InterpolateLinear.cpp 180 2014-01-06 19:16:02Z oparviai $
|
// $Id: InterpolateLinear.cpp 225 2015-07-26 14:45:48Z oparviai $
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
|
@ -170,9 +170,9 @@ int InterpolateLinearInteger::transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE
|
||||||
|
|
||||||
// Sets new target iRate. Normal iRate = 1.0, smaller values represent slower
|
// Sets new target iRate. Normal iRate = 1.0, smaller values represent slower
|
||||||
// iRate, larger faster iRates.
|
// iRate, larger faster iRates.
|
||||||
void InterpolateLinearInteger::setRate(float newRate)
|
void InterpolateLinearInteger::setRate(double newRate)
|
||||||
{
|
{
|
||||||
iRate = (int)(newRate * SCALE + 0.5f);
|
iRate = (int)(newRate * SCALE + 0.5);
|
||||||
TransposerBase::setRate(newRate);
|
TransposerBase::setRate(newRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,7 +190,7 @@ InterpolateLinearFloat::InterpolateLinearFloat() : TransposerBase()
|
||||||
// Notice: use local function calling syntax for sake of clarity,
|
// Notice: use local function calling syntax for sake of clarity,
|
||||||
// to indicate the fact that C++ constructor can't call virtual functions.
|
// to indicate the fact that C++ constructor can't call virtual functions.
|
||||||
resetRegisters();
|
resetRegisters();
|
||||||
setRate(1.0f);
|
setRate(1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -275,12 +275,13 @@ int InterpolateLinearFloat::transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE *s
|
||||||
i = 0;
|
i = 0;
|
||||||
while (srcCount < srcSampleEnd)
|
while (srcCount < srcSampleEnd)
|
||||||
{
|
{
|
||||||
float temp, vol1;
|
float temp, vol1, fract_float;
|
||||||
|
|
||||||
vol1 = (1.0f- fract);
|
vol1 = (float)(1.0 - fract);
|
||||||
|
fract_float = (float)fract;
|
||||||
for (int c = 0; c < numChannels; c ++)
|
for (int c = 0; c < numChannels; c ++)
|
||||||
{
|
{
|
||||||
temp = vol1 * src[c] + fract * src[c + numChannels];
|
temp = vol1 * src[c] + fract_float * src[c + numChannels];
|
||||||
*dest = (SAMPLETYPE)temp;
|
*dest = (SAMPLETYPE)temp;
|
||||||
dest ++;
|
dest ++;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// $Id: InterpolateLinear.h 179 2014-01-06 18:41:42Z oparviai $
|
// $Id: InterpolateLinear.h 225 2015-07-26 14:45:48Z oparviai $
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
|
@ -63,7 +63,7 @@ public:
|
||||||
|
|
||||||
/// Sets new target rate. Normal rate = 1.0, smaller values represent slower
|
/// Sets new target rate. Normal rate = 1.0, smaller values represent slower
|
||||||
/// rate, larger faster rates.
|
/// rate, larger faster rates.
|
||||||
virtual void setRate(float newRate);
|
virtual void setRate(double newRate);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ public:
|
||||||
class InterpolateLinearFloat : public TransposerBase
|
class InterpolateLinearFloat : public TransposerBase
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
float fract;
|
double fract;
|
||||||
|
|
||||||
virtual void resetRegisters();
|
virtual void resetRegisters();
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// $Id: InterpolateShannon.h 179 2014-01-06 18:41:42Z oparviai $
|
// $Id: InterpolateShannon.h 225 2015-07-26 14:45:48Z oparviai $
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
|
@ -61,7 +61,7 @@ protected:
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
int &srcSamples);
|
int &srcSamples);
|
||||||
|
|
||||||
float fract;
|
double fract;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
InterpolateShannon();
|
InterpolateShannon();
|
||||||
|
|
|
@ -11,10 +11,10 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2012-12-29 06:52:47 +1100 (Sat, 29 Dec 2012) $
|
// Last changed : $Date: 2015-05-18 18:22:02 +0300 (Mon, 18 May 2015) $
|
||||||
// File revision : $Revision: 4 $
|
// File revision : $Revision: 4 $
|
||||||
//
|
//
|
||||||
// $Id: PeakFinder.cpp 164 2012-12-28 19:52:47Z oparviai $
|
// $Id: PeakFinder.cpp 213 2015-05-18 15:22:02Z oparviai $
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
|
@ -192,11 +192,21 @@ double PeakFinder::getPeakCenter(const float *data, int peakpos) const
|
||||||
gp1 = findGround(data, peakpos, -1);
|
gp1 = findGround(data, peakpos, -1);
|
||||||
gp2 = findGround(data, peakpos, 1);
|
gp2 = findGround(data, peakpos, 1);
|
||||||
|
|
||||||
groundLevel = 0.5f * (data[gp1] + data[gp2]);
|
|
||||||
peakLevel = data[peakpos];
|
peakLevel = data[peakpos];
|
||||||
|
|
||||||
|
if (gp1 == gp2)
|
||||||
|
{
|
||||||
|
// avoid rounding errors when all are equal
|
||||||
|
assert(gp1 == peakpos);
|
||||||
|
cutLevel = groundLevel = peakLevel;
|
||||||
|
} else {
|
||||||
|
// get average of the ground levels
|
||||||
|
groundLevel = 0.5f * (data[gp1] + data[gp2]);
|
||||||
|
|
||||||
// calculate 70%-level of the peak
|
// calculate 70%-level of the peak
|
||||||
cutLevel = 0.70f * peakLevel + 0.30f * groundLevel;
|
cutLevel = 0.70f * peakLevel + 0.30f * groundLevel;
|
||||||
|
}
|
||||||
|
|
||||||
// find mid-level crossings
|
// find mid-level crossings
|
||||||
crosspos1 = findCrossingLevel(data, cutLevel, peakpos, -1);
|
crosspos1 = findCrossingLevel(data, cutLevel, peakpos, -1);
|
||||||
crosspos2 = findCrossingLevel(data, cutLevel, peakpos, 1);
|
crosspos2 = findCrossingLevel(data, cutLevel, peakpos, 1);
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2011-12-31 07:33:46 +1100 (Sat, 31 Dec 2011) $
|
// Last changed : $Date: 2011-12-30 22:33:46 +0200 (Fri, 30 Dec 2011) $
|
||||||
// File revision : $Revision: 4 $
|
// File revision : $Revision: 4 $
|
||||||
//
|
//
|
||||||
// $Id: PeakFinder.h 132 2011-12-30 20:33:46Z oparviai $
|
// $Id: PeakFinder.h 132 2011-12-30 20:33:46Z oparviai $
|
||||||
|
|
|
@ -10,10 +10,10 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2014-04-07 01:57:21 +1000 (Mon, 07 Apr 2014) $
|
// Last changed : $Date: 2015-07-26 17:45:48 +0300 (Sun, 26 Jul 2015) $
|
||||||
// File revision : $Revision: 4 $
|
// File revision : $Revision: 4 $
|
||||||
//
|
//
|
||||||
// $Id: RateTransposer.cpp 195 2014-04-06 15:57:21Z oparviai $
|
// $Id: RateTransposer.cpp 225 2015-07-26 14:45:48Z oparviai $
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
|
@ -97,20 +97,20 @@ AAFilter *RateTransposer::getAAFilter()
|
||||||
|
|
||||||
// Sets new target iRate. Normal iRate = 1.0, smaller values represent slower
|
// Sets new target iRate. Normal iRate = 1.0, smaller values represent slower
|
||||||
// iRate, larger faster iRates.
|
// iRate, larger faster iRates.
|
||||||
void RateTransposer::setRate(float newRate)
|
void RateTransposer::setRate(double newRate)
|
||||||
{
|
{
|
||||||
double fCutoff;
|
double fCutoff;
|
||||||
|
|
||||||
pTransposer->setRate(newRate);
|
pTransposer->setRate(newRate);
|
||||||
|
|
||||||
// design a new anti-alias filter
|
// design a new anti-alias filter
|
||||||
if (newRate > 1.0f)
|
if (newRate > 1.0)
|
||||||
{
|
{
|
||||||
fCutoff = 0.5f / newRate;
|
fCutoff = 0.5 / newRate;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fCutoff = 0.5f * newRate;
|
fCutoff = 0.5 * newRate;
|
||||||
}
|
}
|
||||||
pAAFilter->setCutoffFreq(fCutoff);
|
pAAFilter->setCutoffFreq(fCutoff);
|
||||||
}
|
}
|
||||||
|
@ -225,7 +225,7 @@ void TransposerBase::setAlgorithm(TransposerBase::ALGORITHM a)
|
||||||
int TransposerBase::transpose(FIFOSampleBuffer &dest, FIFOSampleBuffer &src)
|
int TransposerBase::transpose(FIFOSampleBuffer &dest, FIFOSampleBuffer &src)
|
||||||
{
|
{
|
||||||
int numSrcSamples = src.numSamples();
|
int numSrcSamples = src.numSamples();
|
||||||
int sizeDemand = (int)((float)numSrcSamples / rate) + 8;
|
int sizeDemand = (int)((double)numSrcSamples / rate) + 8;
|
||||||
int numOutput;
|
int numOutput;
|
||||||
SAMPLETYPE *psrc = src.ptrBegin();
|
SAMPLETYPE *psrc = src.ptrBegin();
|
||||||
SAMPLETYPE *pdest = dest.ptrEnd(sizeDemand);
|
SAMPLETYPE *pdest = dest.ptrEnd(sizeDemand);
|
||||||
|
@ -270,7 +270,7 @@ void TransposerBase::setChannels(int channels)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void TransposerBase::setRate(float newRate)
|
void TransposerBase::setRate(double newRate)
|
||||||
{
|
{
|
||||||
rate = newRate;
|
rate = newRate;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,10 +14,10 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2014-04-07 01:57:21 +1000 (Mon, 07 Apr 2014) $
|
// Last changed : $Date: 2015-07-26 17:45:48 +0300 (Sun, 26 Jul 2015) $
|
||||||
// File revision : $Revision: 4 $
|
// File revision : $Revision: 4 $
|
||||||
//
|
//
|
||||||
// $Id: RateTransposer.h 195 2014-04-06 15:57:21Z oparviai $
|
// $Id: RateTransposer.h 225 2015-07-26 14:45:48Z oparviai $
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
|
@ -81,14 +81,14 @@ protected:
|
||||||
static ALGORITHM algorithm;
|
static ALGORITHM algorithm;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
float rate;
|
double rate;
|
||||||
int numChannels;
|
int numChannels;
|
||||||
|
|
||||||
TransposerBase();
|
TransposerBase();
|
||||||
virtual ~TransposerBase();
|
virtual ~TransposerBase();
|
||||||
|
|
||||||
virtual int transpose(FIFOSampleBuffer &dest, FIFOSampleBuffer &src);
|
virtual int transpose(FIFOSampleBuffer &dest, FIFOSampleBuffer &src);
|
||||||
virtual void setRate(float newRate);
|
virtual void setRate(double newRate);
|
||||||
virtual void setChannels(int channels);
|
virtual void setChannels(int channels);
|
||||||
|
|
||||||
// static factory function
|
// static factory function
|
||||||
|
@ -158,7 +158,7 @@ public:
|
||||||
|
|
||||||
/// Sets new target rate. Normal rate = 1.0, smaller values represent slower
|
/// Sets new target rate. Normal rate = 1.0, smaller values represent slower
|
||||||
/// rate, larger faster rates.
|
/// rate, larger faster rates.
|
||||||
virtual void setRate(float newRate);
|
virtual void setRate(double newRate);
|
||||||
|
|
||||||
/// Sets the number of channels, 1 = mono, 2 = stereo
|
/// Sets the number of channels, 1 = mono, 2 = stereo
|
||||||
void setChannels(int channels);
|
void setChannels(int channels);
|
||||||
|
|
|
@ -8,10 +8,10 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2013-06-12 15:24:44 +0000 (Wed, 12 Jun 2013) $
|
// Last changed : $Date: 2015-05-18 18:25:07 +0300 (Mon, 18 May 2015) $
|
||||||
// File revision : $Revision: 3 $
|
// File revision : $Revision: 3 $
|
||||||
//
|
//
|
||||||
// $Id: STTypes.h 171 2013-06-12 15:24:44Z oparviai $
|
// $Id: STTypes.h 215 2015-05-18 15:25:07Z oparviai $
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
|
@ -60,16 +60,6 @@ typedef unsigned long ulong;
|
||||||
//#include "soundtouch_config.h"
|
//#include "soundtouch_config.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef _WINDEF_
|
|
||||||
// if these aren't defined already by Windows headers, define now
|
|
||||||
|
|
||||||
typedef int BOOL;
|
|
||||||
|
|
||||||
#define FALSE 0
|
|
||||||
#define TRUE 1
|
|
||||||
|
|
||||||
#endif // _WINDEF_
|
|
||||||
|
|
||||||
|
|
||||||
namespace soundtouch
|
namespace soundtouch
|
||||||
{
|
{
|
||||||
|
@ -182,6 +172,7 @@ namespace soundtouch
|
||||||
#else
|
#else
|
||||||
// use c++ standard exceptions
|
// use c++ standard exceptions
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
#define ST_THROW_RT_ERROR(x) {throw std::runtime_error(x);}
|
#define ST_THROW_RT_ERROR(x) {throw std::runtime_error(x);}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -41,10 +41,10 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2014-04-07 01:57:21 +1000 (Mon, 07 Apr 2014) $
|
// Last changed : $Date: 2015-07-26 17:45:48 +0300 (Sun, 26 Jul 2015) $
|
||||||
// File revision : $Revision: 4 $
|
// File revision : $Revision: 4 $
|
||||||
//
|
//
|
||||||
// $Id: SoundTouch.cpp 195 2014-04-06 15:57:21Z oparviai $
|
// $Id: SoundTouch.cpp 225 2015-07-26 14:45:48Z oparviai $
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
|
@ -110,6 +110,9 @@ SoundTouch::SoundTouch()
|
||||||
|
|
||||||
calcEffectiveRateAndTempo();
|
calcEffectiveRateAndTempo();
|
||||||
|
|
||||||
|
samplesExpectedOut = 0;
|
||||||
|
samplesOutput = 0;
|
||||||
|
|
||||||
channels = 0;
|
channels = 0;
|
||||||
bSrateSet = false;
|
bSrateSet = false;
|
||||||
}
|
}
|
||||||
|
@ -157,7 +160,7 @@ void SoundTouch::setChannels(uint numChannels)
|
||||||
|
|
||||||
// Sets new rate control value. Normal rate = 1.0, smaller values
|
// Sets new rate control value. Normal rate = 1.0, smaller values
|
||||||
// represent slower rate, larger faster rates.
|
// represent slower rate, larger faster rates.
|
||||||
void SoundTouch::setRate(float newRate)
|
void SoundTouch::setRate(double newRate)
|
||||||
{
|
{
|
||||||
virtualRate = newRate;
|
virtualRate = newRate;
|
||||||
calcEffectiveRateAndTempo();
|
calcEffectiveRateAndTempo();
|
||||||
|
@ -167,9 +170,9 @@ void SoundTouch::setRate(float newRate)
|
||||||
|
|
||||||
// Sets new rate control value as a difference in percents compared
|
// Sets new rate control value as a difference in percents compared
|
||||||
// to the original rate (-50 .. +100 %)
|
// to the original rate (-50 .. +100 %)
|
||||||
void SoundTouch::setRateChange(float newRate)
|
void SoundTouch::setRateChange(double newRate)
|
||||||
{
|
{
|
||||||
virtualRate = 1.0f + 0.01f * newRate;
|
virtualRate = 1.0 + 0.01 * newRate;
|
||||||
calcEffectiveRateAndTempo();
|
calcEffectiveRateAndTempo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,7 +180,7 @@ void SoundTouch::setRateChange(float newRate)
|
||||||
|
|
||||||
// Sets new tempo control value. Normal tempo = 1.0, smaller values
|
// Sets new tempo control value. Normal tempo = 1.0, smaller values
|
||||||
// represent slower tempo, larger faster tempo.
|
// represent slower tempo, larger faster tempo.
|
||||||
void SoundTouch::setTempo(float newTempo)
|
void SoundTouch::setTempo(double newTempo)
|
||||||
{
|
{
|
||||||
virtualTempo = newTempo;
|
virtualTempo = newTempo;
|
||||||
calcEffectiveRateAndTempo();
|
calcEffectiveRateAndTempo();
|
||||||
|
@ -187,9 +190,9 @@ void SoundTouch::setTempo(float newTempo)
|
||||||
|
|
||||||
// Sets new tempo control value as a difference in percents compared
|
// Sets new tempo control value as a difference in percents compared
|
||||||
// to the original tempo (-50 .. +100 %)
|
// to the original tempo (-50 .. +100 %)
|
||||||
void SoundTouch::setTempoChange(float newTempo)
|
void SoundTouch::setTempoChange(double newTempo)
|
||||||
{
|
{
|
||||||
virtualTempo = 1.0f + 0.01f * newTempo;
|
virtualTempo = 1.0 + 0.01 * newTempo;
|
||||||
calcEffectiveRateAndTempo();
|
calcEffectiveRateAndTempo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,7 +200,7 @@ void SoundTouch::setTempoChange(float newTempo)
|
||||||
|
|
||||||
// Sets new pitch control value. Original pitch = 1.0, smaller values
|
// Sets new pitch control value. Original pitch = 1.0, smaller values
|
||||||
// represent lower pitches, larger values higher pitch.
|
// represent lower pitches, larger values higher pitch.
|
||||||
void SoundTouch::setPitch(float newPitch)
|
void SoundTouch::setPitch(double newPitch)
|
||||||
{
|
{
|
||||||
virtualPitch = newPitch;
|
virtualPitch = newPitch;
|
||||||
calcEffectiveRateAndTempo();
|
calcEffectiveRateAndTempo();
|
||||||
|
@ -207,9 +210,9 @@ void SoundTouch::setPitch(float newPitch)
|
||||||
|
|
||||||
// Sets pitch change in octaves compared to the original pitch
|
// Sets pitch change in octaves compared to the original pitch
|
||||||
// (-1.00 .. +1.00)
|
// (-1.00 .. +1.00)
|
||||||
void SoundTouch::setPitchOctaves(float newPitch)
|
void SoundTouch::setPitchOctaves(double newPitch)
|
||||||
{
|
{
|
||||||
virtualPitch = (float)exp(0.69314718056f * newPitch);
|
virtualPitch = exp(0.69314718056 * newPitch);
|
||||||
calcEffectiveRateAndTempo();
|
calcEffectiveRateAndTempo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,14 +222,14 @@ void SoundTouch::setPitchOctaves(float newPitch)
|
||||||
// (-12 .. +12)
|
// (-12 .. +12)
|
||||||
void SoundTouch::setPitchSemiTones(int newPitch)
|
void SoundTouch::setPitchSemiTones(int newPitch)
|
||||||
{
|
{
|
||||||
setPitchOctaves((float)newPitch / 12.0f);
|
setPitchOctaves((double)newPitch / 12.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void SoundTouch::setPitchSemiTones(float newPitch)
|
void SoundTouch::setPitchSemiTones(double newPitch)
|
||||||
{
|
{
|
||||||
setPitchOctaves(newPitch / 12.0f);
|
setPitchOctaves(newPitch / 12.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -234,8 +237,8 @@ void SoundTouch::setPitchSemiTones(float newPitch)
|
||||||
// nominal control values.
|
// nominal control values.
|
||||||
void SoundTouch::calcEffectiveRateAndTempo()
|
void SoundTouch::calcEffectiveRateAndTempo()
|
||||||
{
|
{
|
||||||
float oldTempo = tempo;
|
double oldTempo = tempo;
|
||||||
float oldRate = rate;
|
double oldRate = rate;
|
||||||
|
|
||||||
tempo = virtualTempo / virtualPitch;
|
tempo = virtualTempo / virtualPitch;
|
||||||
rate = virtualPitch * virtualRate;
|
rate = virtualPitch * virtualRate;
|
||||||
|
@ -317,8 +320,13 @@ void SoundTouch::putSamples(const SAMPLETYPE *samples, uint nSamples)
|
||||||
pTDStretch->putSamples(samples, nSamples);
|
pTDStretch->putSamples(samples, nSamples);
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// accumulate how many samples are expected out from processing, given the current
|
||||||
|
// processing setting
|
||||||
|
samplesExpectedOut += (double)nSamples / ((double)rate * (double)tempo);
|
||||||
|
|
||||||
#ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
|
#ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
|
||||||
else if (rate <= 1.0f)
|
if (rate <= 1.0f)
|
||||||
{
|
{
|
||||||
// transpose the rate down, output the transposed sound to tempo changer buffer
|
// transpose the rate down, output the transposed sound to tempo changer buffer
|
||||||
assert(output == pTDStretch);
|
assert(output == pTDStretch);
|
||||||
|
@ -346,42 +354,30 @@ void SoundTouch::putSamples(const SAMPLETYPE *samples, uint nSamples)
|
||||||
void SoundTouch::flush()
|
void SoundTouch::flush()
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int nUnprocessed;
|
int numStillExpected;
|
||||||
int nOut;
|
SAMPLETYPE *buff = new SAMPLETYPE[128 * channels];
|
||||||
SAMPLETYPE *buff=(SAMPLETYPE*)malloc(64*channels*sizeof(SAMPLETYPE));
|
|
||||||
|
|
||||||
// check how many samples still await processing, and scale
|
// how many samples are still expected to output
|
||||||
// that by tempo & rate to get expected output sample count
|
numStillExpected = (int)((long)(samplesExpectedOut + 0.5) - samplesOutput);
|
||||||
nUnprocessed = numUnprocessedSamples();
|
|
||||||
nUnprocessed = (int)((double)nUnprocessed / (tempo * rate) + 0.5);
|
|
||||||
|
|
||||||
nOut = numSamples(); // ready samples currently in buffer ...
|
memset(buff, 0, 128 * channels * sizeof(SAMPLETYPE));
|
||||||
nOut += nUnprocessed; // ... and how many we expect there to be in the end
|
|
||||||
|
|
||||||
memset(buff, 0, 64 * channels * sizeof(SAMPLETYPE));
|
|
||||||
// "Push" the last active samples out from the processing pipeline by
|
// "Push" the last active samples out from the processing pipeline by
|
||||||
// feeding blank samples into the processing pipeline until new,
|
// feeding blank samples into the processing pipeline until new,
|
||||||
// processed samples appear in the output (not however, more than
|
// processed samples appear in the output (not however, more than
|
||||||
// 8ksamples in any case)
|
// 24ksamples in any case)
|
||||||
for (i = 0; i < 128; i ++)
|
for (i = 0; (numStillExpected > (int)numSamples()) && (i < 200); i ++)
|
||||||
{
|
{
|
||||||
putSamples(buff, 64);
|
putSamples(buff, 128);
|
||||||
if ((int)numSamples() >= nOut)
|
|
||||||
{
|
|
||||||
// Enough new samples have appeared into the output!
|
|
||||||
// As samples come from processing with bigger chunks, now truncate it
|
|
||||||
// back to maximum "nOut" samples to improve duration accuracy
|
|
||||||
adjustAmountOfSamples(nOut);
|
|
||||||
|
|
||||||
// finish
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear working buffers
|
adjustAmountOfSamples(numStillExpected);
|
||||||
pRateTransposer->clear();
|
|
||||||
|
delete[] buff;
|
||||||
|
|
||||||
|
// Clear input buffers
|
||||||
|
// pRateTransposer->clearInput();
|
||||||
pTDStretch->clearInput();
|
pTDStretch->clearInput();
|
||||||
// yet leave the 'tempoChanger' output intouched as that's where the
|
// yet leave the output intouched as that's where the
|
||||||
// flushed samples are!
|
// flushed samples are!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -480,6 +476,7 @@ int SoundTouch::getSetting(int settingId) const
|
||||||
// buffers.
|
// buffers.
|
||||||
void SoundTouch::clear()
|
void SoundTouch::clear()
|
||||||
{
|
{
|
||||||
|
samplesExpectedOut = 0;
|
||||||
pRateTransposer->clear();
|
pRateTransposer->clear();
|
||||||
pTDStretch->clear();
|
pTDStretch->clear();
|
||||||
}
|
}
|
||||||
|
@ -500,3 +497,30 @@ uint SoundTouch::numUnprocessedSamples() const
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
||||||
|
/// output buffer and removes them from the sample buffer. If there are less than
|
||||||
|
/// 'numsample' samples in the buffer, returns all that available.
|
||||||
|
///
|
||||||
|
/// \return Number of samples returned.
|
||||||
|
uint SoundTouch::receiveSamples(SAMPLETYPE *output, uint maxSamples)
|
||||||
|
{
|
||||||
|
uint ret = FIFOProcessor::receiveSamples(output, maxSamples);
|
||||||
|
samplesOutput += (long)ret;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
||||||
|
/// sample buffer without copying them anywhere.
|
||||||
|
///
|
||||||
|
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
|
||||||
|
/// with 'ptrBegin' function.
|
||||||
|
uint SoundTouch::receiveSamples(uint maxSamples)
|
||||||
|
{
|
||||||
|
uint ret = FIFOProcessor::receiveSamples(maxSamples);
|
||||||
|
samplesOutput += (long)ret;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
|
@ -41,10 +41,10 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2013-06-12 15:24:44 +0000 (Wed, 12 Jun 2013) $
|
// Last changed : $Date: 2015-09-20 10:38:32 +0300 (Sun, 20 Sep 2015) $
|
||||||
// File revision : $Revision: 4 $
|
// File revision : $Revision: 4 $
|
||||||
//
|
//
|
||||||
// $Id: SoundTouch.h 171 2013-06-12 15:24:44Z oparviai $
|
// $Id: SoundTouch.h 230 2015-09-20 07:38:32Z oparviai $
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
|
@ -79,10 +79,10 @@ namespace soundtouch
|
||||||
{
|
{
|
||||||
|
|
||||||
/// Soundtouch library version string
|
/// Soundtouch library version string
|
||||||
#define SOUNDTOUCH_VERSION "1.8.1 (r198)"
|
#define SOUNDTOUCH_VERSION "1.9.2"
|
||||||
|
|
||||||
/// SoundTouch library version id
|
/// SoundTouch library version id
|
||||||
#define SOUNDTOUCH_VERSION_ID (10801)
|
#define SOUNDTOUCH_VERSION_ID (10902)
|
||||||
|
|
||||||
//
|
//
|
||||||
// Available setting IDs for the 'setSetting' & 'get_setting' functions:
|
// Available setting IDs for the 'setSetting' & 'get_setting' functions:
|
||||||
|
@ -151,16 +151,23 @@ private:
|
||||||
class TDStretch *pTDStretch;
|
class TDStretch *pTDStretch;
|
||||||
|
|
||||||
/// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.
|
/// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.
|
||||||
float virtualRate;
|
double virtualRate;
|
||||||
|
|
||||||
/// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.
|
/// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.
|
||||||
float virtualTempo;
|
double virtualTempo;
|
||||||
|
|
||||||
/// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.
|
/// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.
|
||||||
float virtualPitch;
|
double virtualPitch;
|
||||||
|
|
||||||
/// Flag: Has sample rate been set?
|
/// Flag: Has sample rate been set?
|
||||||
BOOL bSrateSet;
|
bool bSrateSet;
|
||||||
|
|
||||||
|
/// Accumulator for how many samples in total will be expected as output vs. samples put in,
|
||||||
|
/// considering current processing settings.
|
||||||
|
double samplesExpectedOut;
|
||||||
|
|
||||||
|
/// Accumulator for how many samples in total have been read out from the processing so far
|
||||||
|
long samplesOutput;
|
||||||
|
|
||||||
/// Calculates effective rate & tempo valuescfrom 'virtualRate', 'virtualTempo' and
|
/// Calculates effective rate & tempo valuescfrom 'virtualRate', 'virtualTempo' and
|
||||||
/// 'virtualPitch' parameters.
|
/// 'virtualPitch' parameters.
|
||||||
|
@ -171,10 +178,10 @@ protected :
|
||||||
uint channels;
|
uint channels;
|
||||||
|
|
||||||
/// Effective 'rate' value calculated from 'virtualRate', 'virtualTempo' and 'virtualPitch'
|
/// Effective 'rate' value calculated from 'virtualRate', 'virtualTempo' and 'virtualPitch'
|
||||||
float rate;
|
double rate;
|
||||||
|
|
||||||
/// Effective 'tempo' value calculated from 'virtualRate', 'virtualTempo' and 'virtualPitch'
|
/// Effective 'tempo' value calculated from 'virtualRate', 'virtualTempo' and 'virtualPitch'
|
||||||
float tempo;
|
double tempo;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SoundTouch();
|
SoundTouch();
|
||||||
|
@ -188,32 +195,32 @@ public:
|
||||||
|
|
||||||
/// Sets new rate control value. Normal rate = 1.0, smaller values
|
/// Sets new rate control value. Normal rate = 1.0, smaller values
|
||||||
/// represent slower rate, larger faster rates.
|
/// represent slower rate, larger faster rates.
|
||||||
void setRate(float newRate);
|
void setRate(double newRate);
|
||||||
|
|
||||||
/// Sets new tempo control value. Normal tempo = 1.0, smaller values
|
/// Sets new tempo control value. Normal tempo = 1.0, smaller values
|
||||||
/// represent slower tempo, larger faster tempo.
|
/// represent slower tempo, larger faster tempo.
|
||||||
void setTempo(float newTempo);
|
void setTempo(double newTempo);
|
||||||
|
|
||||||
/// Sets new rate control value as a difference in percents compared
|
/// Sets new rate control value as a difference in percents compared
|
||||||
/// to the original rate (-50 .. +100 %)
|
/// to the original rate (-50 .. +100 %)
|
||||||
void setRateChange(float newRate);
|
void setRateChange(double newRate);
|
||||||
|
|
||||||
/// Sets new tempo control value as a difference in percents compared
|
/// Sets new tempo control value as a difference in percents compared
|
||||||
/// to the original tempo (-50 .. +100 %)
|
/// to the original tempo (-50 .. +100 %)
|
||||||
void setTempoChange(float newTempo);
|
void setTempoChange(double newTempo);
|
||||||
|
|
||||||
/// Sets new pitch control value. Original pitch = 1.0, smaller values
|
/// Sets new pitch control value. Original pitch = 1.0, smaller values
|
||||||
/// represent lower pitches, larger values higher pitch.
|
/// represent lower pitches, larger values higher pitch.
|
||||||
void setPitch(float newPitch);
|
void setPitch(double newPitch);
|
||||||
|
|
||||||
/// Sets pitch change in octaves compared to the original pitch
|
/// Sets pitch change in octaves compared to the original pitch
|
||||||
/// (-1.00 .. +1.00)
|
/// (-1.00 .. +1.00)
|
||||||
void setPitchOctaves(float newPitch);
|
void setPitchOctaves(double newPitch);
|
||||||
|
|
||||||
/// Sets pitch change in semi-tones compared to the original pitch
|
/// Sets pitch change in semi-tones compared to the original pitch
|
||||||
/// (-12 .. +12)
|
/// (-12 .. +12)
|
||||||
void setPitchSemiTones(int newPitch);
|
void setPitchSemiTones(int newPitch);
|
||||||
void setPitchSemiTones(float newPitch);
|
void setPitchSemiTones(double newPitch);
|
||||||
|
|
||||||
/// Sets the number of channels, 1 = mono, 2 = stereo
|
/// Sets the number of channels, 1 = mono, 2 = stereo
|
||||||
void setChannels(uint numChannels);
|
void setChannels(uint numChannels);
|
||||||
|
@ -240,6 +247,23 @@ public:
|
||||||
///< contains data for both channels.
|
///< contains data for both channels.
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
||||||
|
/// output buffer and removes them from the sample buffer. If there are less than
|
||||||
|
/// 'numsample' samples in the buffer, returns all that available.
|
||||||
|
///
|
||||||
|
/// \return Number of samples returned.
|
||||||
|
virtual uint receiveSamples(SAMPLETYPE *output, ///< Buffer where to copy output samples.
|
||||||
|
uint maxSamples ///< How many samples to receive at max.
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
||||||
|
/// sample buffer without copying them anywhere.
|
||||||
|
///
|
||||||
|
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
|
||||||
|
/// with 'ptrBegin' function.
|
||||||
|
virtual uint receiveSamples(uint maxSamples ///< Remove this many samples from the beginning of pipe.
|
||||||
|
);
|
||||||
|
|
||||||
/// Clears all the samples in the object's output and internal processing
|
/// Clears all the samples in the object's output and internal processing
|
||||||
/// buffers.
|
/// buffers.
|
||||||
virtual void clear();
|
virtual void clear();
|
||||||
|
@ -247,7 +271,7 @@ public:
|
||||||
/// Changes a setting controlling the processing system behaviour. See the
|
/// Changes a setting controlling the processing system behaviour. See the
|
||||||
/// 'SETTING_...' defines for available setting ID's.
|
/// 'SETTING_...' defines for available setting ID's.
|
||||||
///
|
///
|
||||||
/// \return 'TRUE' if the setting was succesfully changed
|
/// \return 'true' if the setting was succesfully changed
|
||||||
bool setSetting(int settingId, ///< Setting ID number. see SETTING_... defines.
|
bool setSetting(int settingId, ///< Setting ID number. see SETTING_... defines.
|
||||||
int value ///< New setting value.
|
int value ///< New setting value.
|
||||||
);
|
);
|
||||||
|
|
|
@ -13,10 +13,10 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2014-04-07 01:57:21 +1000 (Mon, 07 Apr 2014) $
|
// Last changed : $Date: 2015-08-09 00:00:15 +0300 (Sun, 09 Aug 2015) $
|
||||||
// File revision : $Revision: 1.12 $
|
// File revision : $Revision: 1.12 $
|
||||||
//
|
//
|
||||||
// $Id: TDStretch.cpp 195 2014-04-06 15:57:21Z oparviai $
|
// $Id: TDStretch.cpp 226 2015-08-08 21:00:15Z oparviai $
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
|
@ -63,7 +63,7 @@ using namespace soundtouch;
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
// Table for the hierarchical mixing position seeking algorithm
|
// Table for the hierarchical mixing position seeking algorithm
|
||||||
static const short _scanOffsets[5][24]={
|
const short _scanOffsets[5][24]={
|
||||||
{ 124, 186, 248, 310, 372, 434, 496, 558, 620, 682, 744, 806,
|
{ 124, 186, 248, 310, 372, 434, 496, 558, 620, 682, 744, 806,
|
||||||
868, 930, 992, 1054, 1116, 1178, 1240, 1302, 1364, 1426, 1488, 0},
|
868, 930, 992, 1054, 1116, 1178, 1240, 1302, 1364, 1426, 1488, 0},
|
||||||
{-100, -75, -50, -25, 25, 50, 75, 100, 0, 0, 0, 0,
|
{-100, -75, -50, -25, 25, 50, 75, 100, 0, 0, 0, 0,
|
||||||
|
@ -94,7 +94,9 @@ TDStretch::TDStretch() : FIFOProcessor(&outputBuffer)
|
||||||
bAutoSeqSetting = true;
|
bAutoSeqSetting = true;
|
||||||
bAutoSeekSetting = true;
|
bAutoSeekSetting = true;
|
||||||
|
|
||||||
// outDebt = 0;
|
maxnorm = 0;
|
||||||
|
maxnormf = 1e8;
|
||||||
|
|
||||||
skipFract = 0;
|
skipFract = 0;
|
||||||
|
|
||||||
tempo = 1.0f;
|
tempo = 1.0f;
|
||||||
|
@ -282,7 +284,6 @@ inline void TDStretch::overlap(SAMPLETYPE *pOutput, const SAMPLETYPE *pInput, ui
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Seeks for the optimal overlap-mixing position. The 'stereo' version of the
|
// Seeks for the optimal overlap-mixing position. The 'stereo' version of the
|
||||||
// routine
|
// routine
|
||||||
//
|
//
|
||||||
|
@ -292,9 +293,9 @@ inline void TDStretch::overlap(SAMPLETYPE *pOutput, const SAMPLETYPE *pInput, ui
|
||||||
int TDStretch::seekBestOverlapPositionFull(const SAMPLETYPE *refPos)
|
int TDStretch::seekBestOverlapPositionFull(const SAMPLETYPE *refPos)
|
||||||
{
|
{
|
||||||
int bestOffs;
|
int bestOffs;
|
||||||
double bestCorr, corr;
|
double bestCorr;
|
||||||
double norm;
|
|
||||||
int i;
|
int i;
|
||||||
|
double norm;
|
||||||
|
|
||||||
bestCorr = FLT_MIN;
|
bestCorr = FLT_MIN;
|
||||||
bestOffs = 0;
|
bestOffs = 0;
|
||||||
|
@ -302,25 +303,45 @@ int TDStretch::seekBestOverlapPositionFull(const SAMPLETYPE *refPos)
|
||||||
// Scans for the best correlation value by testing each possible position
|
// Scans for the best correlation value by testing each possible position
|
||||||
// over the permitted range.
|
// over the permitted range.
|
||||||
bestCorr = calcCrossCorr(refPos, pMidBuffer, norm);
|
bestCorr = calcCrossCorr(refPos, pMidBuffer, norm);
|
||||||
|
|
||||||
|
#pragma omp parallel for
|
||||||
for (i = 1; i < seekLength; i ++)
|
for (i = 1; i < seekLength; i ++)
|
||||||
{
|
{
|
||||||
// Calculates correlation value for the mixing position corresponding
|
double corr;
|
||||||
// to 'i'. Now call "calcCrossCorrAccumulate" that is otherwise same as
|
// Calculates correlation value for the mixing position corresponding to 'i'
|
||||||
// "calcCrossCorr", but saves time by reusing & updating previously stored
|
#ifdef _OPENMP
|
||||||
|
// in parallel OpenMP mode, can't use norm accumulator version as parallel executor won't
|
||||||
|
// iterate the loop in sequential order
|
||||||
|
corr = calcCrossCorr(refPos + channels * i, pMidBuffer, norm);
|
||||||
|
#else
|
||||||
|
// In non-parallel version call "calcCrossCorrAccumulate" that is otherwise same
|
||||||
|
// as "calcCrossCorr", but saves time by reusing & updating previously stored
|
||||||
// "norm" value
|
// "norm" value
|
||||||
corr = calcCrossCorrAccumulate(refPos + channels * i, pMidBuffer, norm);
|
corr = calcCrossCorrAccumulate(refPos + channels * i, pMidBuffer, norm);
|
||||||
|
#endif
|
||||||
// heuristic rule to slightly favour values close to mid of the range
|
// heuristic rule to slightly favour values close to mid of the range
|
||||||
double tmp = (double)(2 * i - seekLength) / (double)seekLength;
|
double tmp = (double)(2 * i - seekLength) / (double)seekLength;
|
||||||
corr = ((corr + 0.1) * (1.0 - 0.25 * tmp * tmp));
|
corr = ((corr + 0.1) * (1.0 - 0.25 * tmp * tmp));
|
||||||
|
|
||||||
// Checks for the highest correlation value
|
// Checks for the highest correlation value
|
||||||
if (corr > bestCorr)
|
if (corr > bestCorr)
|
||||||
|
{
|
||||||
|
// For optimal performance, enter critical section only in case that best value found.
|
||||||
|
// in such case repeat 'if' condition as it's possible that parallel execution may have
|
||||||
|
// updated the bestCorr value in the mean time
|
||||||
|
#pragma omp critical
|
||||||
|
if (corr > bestCorr)
|
||||||
{
|
{
|
||||||
bestCorr = corr;
|
bestCorr = corr;
|
||||||
bestOffs = i;
|
bestOffs = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
|
adaptNormalizer();
|
||||||
|
#endif
|
||||||
|
|
||||||
// clear cross correlation routine state if necessary (is so e.g. in MMX routines).
|
// clear cross correlation routine state if necessary (is so e.g. in MMX routines).
|
||||||
clearCrossCorrState();
|
clearCrossCorrState();
|
||||||
|
|
||||||
|
@ -328,64 +349,161 @@ int TDStretch::seekBestOverlapPositionFull(const SAMPLETYPE *refPos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Seeks for the optimal overlap-mixing position. The 'stereo' version of the
|
// Quick seek algorithm for improved runtime-performance: First roughly scans through the
|
||||||
// routine
|
// correlation area, and then scan surroundings of two best preliminary correlation candidates
|
||||||
|
// with improved precision
|
||||||
//
|
//
|
||||||
// The best position is determined as the position where the two overlapped
|
// Based on testing:
|
||||||
// sample sequences are 'most alike', in terms of the highest cross-correlation
|
// - This algorithm gives on average 99% as good match as the full algorith
|
||||||
// value over the overlapping period
|
// - this quick seek algorithm finds the best match on ~90% of cases
|
||||||
|
// - on those 10% of cases when this algorithm doesn't find best match,
|
||||||
|
// it still finds on average ~90% match vs. the best possible match
|
||||||
int TDStretch::seekBestOverlapPositionQuick(const SAMPLETYPE *refPos)
|
int TDStretch::seekBestOverlapPositionQuick(const SAMPLETYPE *refPos)
|
||||||
{
|
{
|
||||||
int j;
|
#define _MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||||
|
#define SCANSTEP 16
|
||||||
|
#define SCANWIND 8
|
||||||
|
|
||||||
int bestOffs;
|
int bestOffs;
|
||||||
double bestCorr, corr;
|
int i;
|
||||||
int scanCount, corrOffset, tempOffset;
|
int bestOffs2;
|
||||||
|
float bestCorr, corr;
|
||||||
|
float bestCorr2;
|
||||||
|
double norm;
|
||||||
|
|
||||||
|
// note: 'float' types used in this function in case that the platform would need to use software-fp
|
||||||
|
|
||||||
bestCorr = FLT_MIN;
|
bestCorr = FLT_MIN;
|
||||||
bestOffs = _scanOffsets[0][0];
|
bestOffs = SCANWIND;
|
||||||
corrOffset = 0;
|
bestCorr2 = FLT_MIN;
|
||||||
tempOffset = 0;
|
bestOffs2 = 0;
|
||||||
|
|
||||||
// Scans for the best correlation value using four-pass hierarchical search.
|
int best = 0;
|
||||||
|
|
||||||
|
// Scans for the best correlation value by testing each possible position
|
||||||
|
// over the permitted range. Look for two best matches on the first pass to
|
||||||
|
// increase possibility of ideal match.
|
||||||
//
|
//
|
||||||
// The look-up table 'scans' has hierarchical position adjusting steps.
|
// Begin from "SCANSTEP" instead of SCANWIND to make the calculation
|
||||||
// In first pass the routine searhes for the highest correlation with
|
// catch the 'middlepoint' of seekLength vector as that's the a-priori
|
||||||
// relatively coarse steps, then rescans the neighbourhood of the highest
|
// expected best match position
|
||||||
// correlation with better resolution and so on.
|
//
|
||||||
for (scanCount = 0;scanCount < 4; scanCount ++)
|
// Roughly:
|
||||||
|
// - 15% of cases find best result directly on the first round,
|
||||||
|
// - 75% cases find better match on 2nd round around the best match from 1st round
|
||||||
|
// - 10% cases find better match on 2nd round around the 2nd-best-match from 1st round
|
||||||
|
for (i = SCANSTEP; i < seekLength - SCANWIND - 1; i += SCANSTEP)
|
||||||
{
|
{
|
||||||
j = 0;
|
// Calculates correlation value for the mixing position corresponding
|
||||||
while (_scanOffsets[scanCount][j])
|
// to 'i'
|
||||||
|
corr = (float)calcCrossCorr(refPos + channels*i, pMidBuffer, norm);
|
||||||
|
// heuristic rule to slightly favour values close to mid of the seek range
|
||||||
|
float tmp = (float)(2 * i - seekLength - 1) / (float)seekLength;
|
||||||
|
corr = ((corr + 0.1f) * (1.0f - 0.25f * tmp * tmp));
|
||||||
|
|
||||||
|
// Checks for the highest correlation value
|
||||||
|
if (corr > bestCorr)
|
||||||
{
|
{
|
||||||
double norm;
|
// found new best match. keep the previous best as 2nd best match
|
||||||
tempOffset = corrOffset + _scanOffsets[scanCount][j];
|
bestCorr2 = bestCorr;
|
||||||
if (tempOffset >= seekLength) break;
|
bestOffs2 = bestOffs;
|
||||||
|
bestCorr = corr;
|
||||||
|
bestOffs = i;
|
||||||
|
}
|
||||||
|
else if (corr > bestCorr2)
|
||||||
|
{
|
||||||
|
// not new best, but still new 2nd best match
|
||||||
|
bestCorr2 = corr;
|
||||||
|
bestOffs2 = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scans surroundings of the found best match with small stepping
|
||||||
|
int end = _MIN(bestOffs + SCANWIND + 1, seekLength);
|
||||||
|
for (i = bestOffs - SCANWIND; i < end; i++)
|
||||||
|
{
|
||||||
|
if (i == bestOffs) continue; // this offset already calculated, thus skip
|
||||||
|
|
||||||
// Calculates correlation value for the mixing position corresponding
|
// Calculates correlation value for the mixing position corresponding
|
||||||
// to 'tempOffset'
|
// to 'i'
|
||||||
corr = (double)calcCrossCorr(refPos + channels * tempOffset, pMidBuffer, norm);
|
corr = (float)calcCrossCorr(refPos + channels*i, pMidBuffer, norm);
|
||||||
// heuristic rule to slightly favour values close to mid of the range
|
// heuristic rule to slightly favour values close to mid of the range
|
||||||
double tmp = (double)(2 * tempOffset - seekLength) / seekLength;
|
float tmp = (float)(2 * i - seekLength - 1) / (float)seekLength;
|
||||||
corr = ((corr + 0.1) * (1.0 - 0.25 * tmp * tmp));
|
corr = ((corr + 0.1f) * (1.0f - 0.25f * tmp * tmp));
|
||||||
|
|
||||||
// Checks for the highest correlation value
|
// Checks for the highest correlation value
|
||||||
if (corr > bestCorr)
|
if (corr > bestCorr)
|
||||||
{
|
{
|
||||||
bestCorr = corr;
|
bestCorr = corr;
|
||||||
bestOffs = tempOffset;
|
bestOffs = i;
|
||||||
|
best = 1;
|
||||||
}
|
}
|
||||||
j ++;
|
|
||||||
}
|
}
|
||||||
corrOffset = bestOffs;
|
|
||||||
|
// Scans surroundings of the 2nd best match with small stepping
|
||||||
|
end = _MIN(bestOffs2 + SCANWIND + 1, seekLength);
|
||||||
|
for (i = bestOffs2 - SCANWIND; i < end; i++)
|
||||||
|
{
|
||||||
|
if (i == bestOffs2) continue; // this offset already calculated, thus skip
|
||||||
|
|
||||||
|
// Calculates correlation value for the mixing position corresponding
|
||||||
|
// to 'i'
|
||||||
|
corr = (float)calcCrossCorr(refPos + channels*i, pMidBuffer, norm);
|
||||||
|
// heuristic rule to slightly favour values close to mid of the range
|
||||||
|
float tmp = (float)(2 * i - seekLength - 1) / (float)seekLength;
|
||||||
|
corr = ((corr + 0.1f) * (1.0f - 0.25f * tmp * tmp));
|
||||||
|
|
||||||
|
// Checks for the highest correlation value
|
||||||
|
if (corr > bestCorr)
|
||||||
|
{
|
||||||
|
bestCorr = corr;
|
||||||
|
bestOffs = i;
|
||||||
|
best = 2;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// clear cross correlation routine state if necessary (is so e.g. in MMX routines).
|
// clear cross correlation routine state if necessary (is so e.g. in MMX routines).
|
||||||
clearCrossCorrState();
|
clearCrossCorrState();
|
||||||
|
|
||||||
|
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
|
adaptNormalizer();
|
||||||
|
#endif
|
||||||
|
|
||||||
return bestOffs;
|
return bestOffs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// For integer algorithm: adapt normalization factor divider with music so that
|
||||||
|
/// it'll not be pessimistically restrictive that can degrade quality on quieter sections
|
||||||
|
/// yet won't cause integer overflows either
|
||||||
|
void TDStretch::adaptNormalizer()
|
||||||
|
{
|
||||||
|
// Do not adapt normalizer over too silent sequences to avoid averaging filter depleting to
|
||||||
|
// too low values during pauses in music
|
||||||
|
if ((maxnorm > 1000) || (maxnormf > 40000000))
|
||||||
|
{
|
||||||
|
//norm averaging filter
|
||||||
|
maxnormf = 0.9f * maxnormf + 0.1f * (float)maxnorm;
|
||||||
|
|
||||||
|
if ((maxnorm > 800000000) && (overlapDividerBitsNorm < 16))
|
||||||
|
{
|
||||||
|
// large values, so increase divider
|
||||||
|
overlapDividerBitsNorm++;
|
||||||
|
if (maxnorm > 1600000000) overlapDividerBitsNorm++; // extra large value => extra increase
|
||||||
|
}
|
||||||
|
else if ((maxnormf < 1000000) && (overlapDividerBitsNorm > 0))
|
||||||
|
{
|
||||||
|
// extra small values, decrease divider
|
||||||
|
overlapDividerBitsNorm--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
maxnorm = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// clear cross correlation routine state if necessary
|
/// clear cross correlation routine state if necessary
|
||||||
void TDStretch::clearCrossCorrState()
|
void TDStretch::clearCrossCorrState()
|
||||||
{
|
{
|
||||||
|
@ -407,7 +525,7 @@ void TDStretch::calcSeqParameters()
|
||||||
#define AUTOSEQ_K ((AUTOSEQ_AT_MAX - AUTOSEQ_AT_MIN) / (AUTOSEQ_TEMPO_TOP - AUTOSEQ_TEMPO_LOW))
|
#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))
|
#define AUTOSEQ_C (AUTOSEQ_AT_MIN - (AUTOSEQ_K) * (AUTOSEQ_TEMPO_LOW))
|
||||||
|
|
||||||
// seek-window-ms setting values at above low & top tempo
|
// seek-window-ms setting values at above low & top tempoq
|
||||||
#define AUTOSEEK_AT_MIN 25.0
|
#define AUTOSEEK_AT_MIN 25.0
|
||||||
#define AUTOSEEK_AT_MAX 15.0
|
#define AUTOSEEK_AT_MAX 15.0
|
||||||
#define AUTOSEEK_K ((AUTOSEEK_AT_MAX - AUTOSEEK_AT_MIN) / (AUTOSEQ_TEMPO_TOP - AUTOSEQ_TEMPO_LOW))
|
#define AUTOSEEK_K ((AUTOSEEK_AT_MAX - AUTOSEEK_AT_MIN) / (AUTOSEQ_TEMPO_TOP - AUTOSEQ_TEMPO_LOW))
|
||||||
|
@ -444,7 +562,7 @@ void TDStretch::calcSeqParameters()
|
||||||
|
|
||||||
// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower
|
// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower
|
||||||
// tempo, larger faster tempo.
|
// tempo, larger faster tempo.
|
||||||
void TDStretch::setTempo(float newTempo)
|
void TDStretch::setTempo(double newTempo)
|
||||||
{
|
{
|
||||||
int intskip;
|
int intskip;
|
||||||
|
|
||||||
|
@ -455,7 +573,7 @@ void TDStretch::setTempo(float newTempo)
|
||||||
|
|
||||||
// Calculate ideal skip length (according to tempo value)
|
// Calculate ideal skip length (according to tempo value)
|
||||||
nominalSkip = tempo * (seekWindowLength - overlapLength);
|
nominalSkip = tempo * (seekWindowLength - overlapLength);
|
||||||
intskip = (int)(nominalSkip + 0.5f);
|
intskip = (int)(nominalSkip + 0.5);
|
||||||
|
|
||||||
// Calculate how many samples are needed in the 'inputBuffer' to
|
// Calculate how many samples are needed in the 'inputBuffer' to
|
||||||
// process another batch of samples
|
// process another batch of samples
|
||||||
|
@ -721,13 +839,15 @@ void TDStretch::calculateOverlapLength(int aoverlapMs)
|
||||||
// calculate overlap length so that it's power of 2 - thus it's easy to do
|
// 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
|
// 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
|
// the extra most significatnt bit left unused in result by signed multiplication
|
||||||
overlapDividerBits = _getClosest2Power((sampleRate * aoverlapMs) / 1000.0) - 1;
|
overlapDividerBitsPure = _getClosest2Power((sampleRate * aoverlapMs) / 1000.0) - 1;
|
||||||
if (overlapDividerBits > 9) overlapDividerBits = 9;
|
if (overlapDividerBitsPure > 9) overlapDividerBitsPure = 9;
|
||||||
if (overlapDividerBits < 3) overlapDividerBits = 3;
|
if (overlapDividerBitsPure < 3) overlapDividerBitsPure = 3;
|
||||||
newOvl = (int)pow(2.0, (int)overlapDividerBits + 1); // +1 => account for -1 above
|
newOvl = (int)pow(2.0, (int)overlapDividerBitsPure + 1); // +1 => account for -1 above
|
||||||
|
|
||||||
acceptNewOverlapLength(newOvl);
|
acceptNewOverlapLength(newOvl);
|
||||||
|
|
||||||
|
overlapDividerBitsNorm = overlapDividerBitsPure;
|
||||||
|
|
||||||
// calculate sloping divider so that crosscorrelation operation won't
|
// calculate sloping divider so that crosscorrelation operation won't
|
||||||
// overflow 32-bit register. Max. sum of the crosscorrelation sum without
|
// overflow 32-bit register. Max. sum of the crosscorrelation sum without
|
||||||
// divider would be 2^30*(N^3-N)/3, where N = overlap length
|
// divider would be 2^30*(N^3-N)/3, where N = overlap length
|
||||||
|
@ -735,10 +855,10 @@ void TDStretch::calculateOverlapLength(int aoverlapMs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
double TDStretch::calcCrossCorr(const short *mixingPos, const short *compare, double &norm) const
|
double TDStretch::calcCrossCorr(const short *mixingPos, const short *compare, double &norm)
|
||||||
{
|
{
|
||||||
long corr;
|
long corr;
|
||||||
long lnorm;
|
unsigned long lnorm;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
corr = lnorm = 0;
|
corr = lnorm = 0;
|
||||||
|
@ -748,15 +868,19 @@ double TDStretch::calcCrossCorr(const short *mixingPos, const short *compare, do
|
||||||
for (i = 0; i < channels * overlapLength; i += 4)
|
for (i = 0; i < channels * overlapLength; i += 4)
|
||||||
{
|
{
|
||||||
corr += (mixingPos[i] * compare[i] +
|
corr += (mixingPos[i] * compare[i] +
|
||||||
mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBits; // notice: do intermediate division here to avoid integer overflow
|
mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBitsNorm; // notice: do intermediate division here to avoid integer overflow
|
||||||
corr += (mixingPos[i + 2] * compare[i + 2] +
|
corr += (mixingPos[i + 2] * compare[i + 2] +
|
||||||
mixingPos[i + 3] * compare[i + 3]) >> overlapDividerBits;
|
mixingPos[i + 3] * compare[i + 3]) >> overlapDividerBitsNorm;
|
||||||
lnorm += (mixingPos[i] * mixingPos[i] +
|
lnorm += (mixingPos[i] * mixingPos[i] +
|
||||||
mixingPos[i + 1] * mixingPos[i + 1]) >> overlapDividerBits; // notice: do intermediate division here to avoid integer overflow
|
mixingPos[i + 1] * mixingPos[i + 1]) >> overlapDividerBitsNorm; // notice: do intermediate division here to avoid integer overflow
|
||||||
lnorm += (mixingPos[i + 2] * mixingPos[i + 2] +
|
lnorm += (mixingPos[i + 2] * mixingPos[i + 2] +
|
||||||
mixingPos[i + 3] * mixingPos[i + 3]) >> overlapDividerBits;
|
mixingPos[i + 3] * mixingPos[i + 3]) >> overlapDividerBitsNorm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lnorm > maxnorm)
|
||||||
|
{
|
||||||
|
maxnorm = lnorm;
|
||||||
|
}
|
||||||
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
||||||
// done using floating point operation
|
// done using floating point operation
|
||||||
norm = (double)lnorm;
|
norm = (double)lnorm;
|
||||||
|
@ -765,17 +889,17 @@ double TDStretch::calcCrossCorr(const short *mixingPos, const short *compare, do
|
||||||
|
|
||||||
|
|
||||||
/// Update cross-correlation by accumulating "norm" coefficient by previously calculated value
|
/// Update cross-correlation by accumulating "norm" coefficient by previously calculated value
|
||||||
double TDStretch::calcCrossCorrAccumulate(const short *mixingPos, const short *compare, double &norm) const
|
double TDStretch::calcCrossCorrAccumulate(const short *mixingPos, const short *compare, double &norm)
|
||||||
{
|
{
|
||||||
long corr;
|
long corr;
|
||||||
long lnorm;
|
unsigned long lnorm;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
// cancel first normalizer tap from previous round
|
// cancel first normalizer tap from previous round
|
||||||
lnorm = 0;
|
lnorm = 0;
|
||||||
for (i = 1; i <= channels; i ++)
|
for (i = 1; i <= channels; i ++)
|
||||||
{
|
{
|
||||||
lnorm -= (mixingPos[-i] * mixingPos[-i]) >> overlapDividerBits;
|
lnorm -= (mixingPos[-i] * mixingPos[-i]) >> overlapDividerBitsNorm;
|
||||||
}
|
}
|
||||||
|
|
||||||
corr = 0;
|
corr = 0;
|
||||||
|
@ -785,18 +909,23 @@ double TDStretch::calcCrossCorrAccumulate(const short *mixingPos, const short *c
|
||||||
for (i = 0; i < channels * overlapLength; i += 4)
|
for (i = 0; i < channels * overlapLength; i += 4)
|
||||||
{
|
{
|
||||||
corr += (mixingPos[i] * compare[i] +
|
corr += (mixingPos[i] * compare[i] +
|
||||||
mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBits; // notice: do intermediate division here to avoid integer overflow
|
mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBitsNorm; // notice: do intermediate division here to avoid integer overflow
|
||||||
corr += (mixingPos[i + 2] * compare[i + 2] +
|
corr += (mixingPos[i + 2] * compare[i + 2] +
|
||||||
mixingPos[i + 3] * compare[i + 3]) >> overlapDividerBits;
|
mixingPos[i + 3] * compare[i + 3]) >> overlapDividerBitsNorm;
|
||||||
}
|
}
|
||||||
|
|
||||||
// update normalizer with last samples of this round
|
// update normalizer with last samples of this round
|
||||||
for (int j = 0; j < channels; j ++)
|
for (int j = 0; j < channels; j ++)
|
||||||
{
|
{
|
||||||
i --;
|
i --;
|
||||||
lnorm += (mixingPos[i] * mixingPos[i]) >> overlapDividerBits;
|
lnorm += (mixingPos[i] * mixingPos[i]) >> overlapDividerBitsNorm;
|
||||||
}
|
}
|
||||||
|
|
||||||
norm += (double)lnorm;
|
norm += (double)lnorm;
|
||||||
|
if (norm > maxnorm)
|
||||||
|
{
|
||||||
|
maxnorm = (unsigned long)norm;
|
||||||
|
}
|
||||||
|
|
||||||
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
||||||
// done using floating point operation
|
// done using floating point operation
|
||||||
|
@ -881,9 +1010,10 @@ void TDStretch::calculateOverlapLength(int overlapInMsec)
|
||||||
|
|
||||||
|
|
||||||
/// Calculate cross-correlation
|
/// Calculate cross-correlation
|
||||||
double TDStretch::calcCrossCorr(const float *mixingPos, const float *compare, double &norm) const
|
double TDStretch::calcCrossCorr(const float *mixingPos, const float *compare, double &anorm)
|
||||||
{
|
{
|
||||||
double corr;
|
double corr;
|
||||||
|
double norm;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
corr = norm = 0;
|
corr = norm = 0;
|
||||||
|
@ -905,12 +1035,13 @@ double TDStretch::calcCrossCorr(const float *mixingPos, const float *compare, do
|
||||||
mixingPos[i + 3] * mixingPos[i + 3];
|
mixingPos[i + 3] * mixingPos[i + 3];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
anorm = norm;
|
||||||
return corr / sqrt((norm < 1e-9 ? 1.0 : norm));
|
return corr / sqrt((norm < 1e-9 ? 1.0 : norm));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Update cross-correlation by accumulating "norm" coefficient by previously calculated value
|
/// Update cross-correlation by accumulating "norm" coefficient by previously calculated value
|
||||||
double TDStretch::calcCrossCorrAccumulate(const float *mixingPos, const float *compare, double &norm) const
|
double TDStretch::calcCrossCorrAccumulate(const float *mixingPos, const float *compare, double &norm)
|
||||||
{
|
{
|
||||||
double corr;
|
double corr;
|
||||||
int i;
|
int i;
|
||||||
|
|
|
@ -13,10 +13,10 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2014-04-07 01:57:21 +1000 (Mon, 07 Apr 2014) $
|
// Last changed : $Date: 2015-08-09 00:00:15 +0300 (Sun, 09 Aug 2015) $
|
||||||
// File revision : $Revision: 4 $
|
// File revision : $Revision: 4 $
|
||||||
//
|
//
|
||||||
// $Id: TDStretch.h 195 2014-04-06 15:57:21Z oparviai $
|
// $Id: TDStretch.h 226 2015-08-08 21:00:15Z oparviai $
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
|
@ -112,39 +112,46 @@ class TDStretch : public FIFOProcessor
|
||||||
protected:
|
protected:
|
||||||
int channels;
|
int channels;
|
||||||
int sampleReq;
|
int sampleReq;
|
||||||
float tempo;
|
|
||||||
|
|
||||||
SAMPLETYPE *pMidBuffer;
|
|
||||||
SAMPLETYPE *pMidBufferUnaligned;
|
|
||||||
int overlapLength;
|
int overlapLength;
|
||||||
int seekLength;
|
int seekLength;
|
||||||
int seekWindowLength;
|
int seekWindowLength;
|
||||||
int overlapDividerBits;
|
int overlapDividerBitsNorm;
|
||||||
|
int overlapDividerBitsPure;
|
||||||
int slopingDivider;
|
int slopingDivider;
|
||||||
float nominalSkip;
|
|
||||||
float skipFract;
|
|
||||||
FIFOSampleBuffer outputBuffer;
|
|
||||||
FIFOSampleBuffer inputBuffer;
|
|
||||||
bool bQuickSeek;
|
|
||||||
|
|
||||||
int sampleRate;
|
int sampleRate;
|
||||||
int sequenceMs;
|
int sequenceMs;
|
||||||
int seekWindowMs;
|
int seekWindowMs;
|
||||||
int overlapMs;
|
int overlapMs;
|
||||||
|
|
||||||
|
unsigned long maxnorm;
|
||||||
|
float maxnormf;
|
||||||
|
|
||||||
|
double tempo;
|
||||||
|
double nominalSkip;
|
||||||
|
double skipFract;
|
||||||
|
|
||||||
|
bool bQuickSeek;
|
||||||
bool bAutoSeqSetting;
|
bool bAutoSeqSetting;
|
||||||
bool bAutoSeekSetting;
|
bool bAutoSeekSetting;
|
||||||
|
|
||||||
|
SAMPLETYPE *pMidBuffer;
|
||||||
|
SAMPLETYPE *pMidBufferUnaligned;
|
||||||
|
|
||||||
|
FIFOSampleBuffer outputBuffer;
|
||||||
|
FIFOSampleBuffer inputBuffer;
|
||||||
|
|
||||||
void acceptNewOverlapLength(int newOverlapLength);
|
void acceptNewOverlapLength(int newOverlapLength);
|
||||||
|
|
||||||
virtual void clearCrossCorrState();
|
virtual void clearCrossCorrState();
|
||||||
void calculateOverlapLength(int overlapMs);
|
void calculateOverlapLength(int overlapMs);
|
||||||
|
|
||||||
virtual double calcCrossCorr(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare, double &norm) const;
|
virtual double calcCrossCorr(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare, double &norm);
|
||||||
virtual double calcCrossCorrAccumulate(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare, double &norm) const;
|
virtual double calcCrossCorrAccumulate(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare, double &norm);
|
||||||
|
|
||||||
virtual int seekBestOverlapPositionFull(const SAMPLETYPE *refPos);
|
virtual int seekBestOverlapPositionFull(const SAMPLETYPE *refPos);
|
||||||
virtual int seekBestOverlapPositionQuick(const SAMPLETYPE *refPos);
|
virtual int seekBestOverlapPositionQuick(const SAMPLETYPE *refPos);
|
||||||
int seekBestOverlapPosition(const SAMPLETYPE *refPos);
|
virtual int seekBestOverlapPosition(const SAMPLETYPE *refPos);
|
||||||
|
|
||||||
virtual void overlapStereo(SAMPLETYPE *output, const SAMPLETYPE *input) const;
|
virtual void overlapStereo(SAMPLETYPE *output, const SAMPLETYPE *input) const;
|
||||||
virtual void overlapMono(SAMPLETYPE *output, const SAMPLETYPE *input) const;
|
virtual void overlapMono(SAMPLETYPE *output, const SAMPLETYPE *input) const;
|
||||||
|
@ -154,6 +161,8 @@ protected:
|
||||||
void overlap(SAMPLETYPE *output, const SAMPLETYPE *input, uint ovlPos) const;
|
void overlap(SAMPLETYPE *output, const SAMPLETYPE *input, uint ovlPos) const;
|
||||||
|
|
||||||
void calcSeqParameters();
|
void calcSeqParameters();
|
||||||
|
void adaptNormalizer();
|
||||||
|
|
||||||
|
|
||||||
/// Changes the tempo of the given sound samples.
|
/// Changes the tempo of the given sound samples.
|
||||||
/// Returns amount of samples returned in the "output" buffer.
|
/// Returns amount of samples returned in the "output" buffer.
|
||||||
|
@ -182,7 +191,7 @@ public:
|
||||||
|
|
||||||
/// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower
|
/// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower
|
||||||
/// tempo, larger faster tempo.
|
/// tempo, larger faster tempo.
|
||||||
void setTempo(float newTempo);
|
void setTempo(double newTempo);
|
||||||
|
|
||||||
/// Returns nonzero if there aren't any samples available for outputting.
|
/// Returns nonzero if there aren't any samples available for outputting.
|
||||||
virtual void clear();
|
virtual void clear();
|
||||||
|
@ -249,8 +258,8 @@ public:
|
||||||
class TDStretchMMX : public TDStretch
|
class TDStretchMMX : public TDStretch
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
double calcCrossCorr(const short *mixingPos, const short *compare, double &norm) const;
|
double calcCrossCorr(const short *mixingPos, const short *compare, double &norm);
|
||||||
double calcCrossCorrAccumulate(const short *mixingPos, const short *compare, double &norm) const;
|
double calcCrossCorrAccumulate(const short *mixingPos, const short *compare, double &norm);
|
||||||
virtual void overlapStereo(short *output, const short *input) const;
|
virtual void overlapStereo(short *output, const short *input) const;
|
||||||
virtual void clearCrossCorrState();
|
virtual void clearCrossCorrState();
|
||||||
};
|
};
|
||||||
|
@ -262,8 +271,8 @@ public:
|
||||||
class TDStretchSSE : public TDStretch
|
class TDStretchSSE : public TDStretch
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
double calcCrossCorr(const float *mixingPos, const float *compare, double &norm) const;
|
double calcCrossCorr(const float *mixingPos, const float *compare, double &norm);
|
||||||
double calcCrossCorrAccumulate(const float *mixingPos, const float *compare, double &norm) const;
|
double calcCrossCorrAccumulate(const float *mixingPos, const float *compare, double &norm);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /// SOUNDTOUCH_ALLOW_SSE
|
#endif /// SOUNDTOUCH_ALLOW_SSE
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2008-02-11 03:26:55 +1100 (Mon, 11 Feb 2008) $
|
// Last changed : $Date: 2008-02-10 18:26:55 +0200 (Sun, 10 Feb 2008) $
|
||||||
// File revision : $Revision: 4 $
|
// File revision : $Revision: 4 $
|
||||||
//
|
//
|
||||||
// $Id: cpu_detect.h 11 2008-02-10 16:26:55Z oparviai $
|
// $Id: cpu_detect.h 11 2008-02-10 16:26:55Z oparviai $
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2014-01-08 05:24:28 +1100 (Wed, 08 Jan 2014) $
|
// Last changed : $Date: 2014-01-07 20:24:28 +0200 (Tue, 07 Jan 2014) $
|
||||||
// File revision : $Revision: 4 $
|
// File revision : $Revision: 4 $
|
||||||
//
|
//
|
||||||
// $Id: cpu_detect_x86.cpp 183 2014-01-07 18:24:28Z oparviai $
|
// $Id: cpu_detect_x86.cpp 183 2014-01-07 18:24:28Z oparviai $
|
||||||
|
|
|
@ -20,10 +20,10 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2014-01-08 05:25:40 +1100 (Wed, 08 Jan 2014) $
|
// Last changed : $Date: 2015-08-09 00:00:15 +0300 (Sun, 09 Aug 2015) $
|
||||||
// File revision : $Revision: 4 $
|
// File revision : $Revision: 4 $
|
||||||
//
|
//
|
||||||
// $Id: mmx_optimized.cpp 184 2014-01-07 18:25:40Z oparviai $
|
// $Id: mmx_optimized.cpp 226 2015-08-08 21:00:15Z oparviai $
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
|
@ -68,7 +68,7 @@ using namespace soundtouch;
|
||||||
|
|
||||||
|
|
||||||
// Calculates cross correlation of two buffers
|
// Calculates cross correlation of two buffers
|
||||||
double TDStretchMMX::calcCrossCorr(const short *pV1, const short *pV2, double &dnorm) const
|
double TDStretchMMX::calcCrossCorr(const short *pV1, const short *pV2, double &dnorm)
|
||||||
{
|
{
|
||||||
const __m64 *pVec1, *pVec2;
|
const __m64 *pVec1, *pVec2;
|
||||||
__m64 shifter;
|
__m64 shifter;
|
||||||
|
@ -79,7 +79,7 @@ double TDStretchMMX::calcCrossCorr(const short *pV1, const short *pV2, double &d
|
||||||
pVec1 = (__m64*)pV1;
|
pVec1 = (__m64*)pV1;
|
||||||
pVec2 = (__m64*)pV2;
|
pVec2 = (__m64*)pV2;
|
||||||
|
|
||||||
shifter = _m_from_int(overlapDividerBits);
|
shifter = _m_from_int(overlapDividerBitsNorm);
|
||||||
normaccu = accu = _mm_setzero_si64();
|
normaccu = accu = _mm_setzero_si64();
|
||||||
|
|
||||||
// Process 4 parallel sets of 2 * stereo samples or 4 * mono samples
|
// Process 4 parallel sets of 2 * stereo samples or 4 * mono samples
|
||||||
|
@ -123,6 +123,11 @@ double TDStretchMMX::calcCrossCorr(const short *pV1, const short *pV2, double &d
|
||||||
// Clear MMS state
|
// Clear MMS state
|
||||||
_m_empty();
|
_m_empty();
|
||||||
|
|
||||||
|
if (norm > (long)maxnorm)
|
||||||
|
{
|
||||||
|
maxnorm = norm;
|
||||||
|
}
|
||||||
|
|
||||||
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
||||||
// done using floating point operation
|
// done using floating point operation
|
||||||
dnorm = (double)norm;
|
dnorm = (double)norm;
|
||||||
|
@ -134,7 +139,7 @@ double TDStretchMMX::calcCrossCorr(const short *pV1, const short *pV2, double &d
|
||||||
|
|
||||||
|
|
||||||
/// Update cross-correlation by accumulating "norm" coefficient by previously calculated value
|
/// Update cross-correlation by accumulating "norm" coefficient by previously calculated value
|
||||||
double TDStretchMMX::calcCrossCorrAccumulate(const short *pV1, const short *pV2, double &dnorm) const
|
double TDStretchMMX::calcCrossCorrAccumulate(const short *pV1, const short *pV2, double &dnorm)
|
||||||
{
|
{
|
||||||
const __m64 *pVec1, *pVec2;
|
const __m64 *pVec1, *pVec2;
|
||||||
__m64 shifter;
|
__m64 shifter;
|
||||||
|
@ -146,13 +151,13 @@ double TDStretchMMX::calcCrossCorrAccumulate(const short *pV1, const short *pV2,
|
||||||
lnorm = 0;
|
lnorm = 0;
|
||||||
for (i = 1; i <= channels; i ++)
|
for (i = 1; i <= channels; i ++)
|
||||||
{
|
{
|
||||||
lnorm -= (pV1[-i] * pV1[-i]) >> overlapDividerBits;
|
lnorm -= (pV1[-i] * pV1[-i]) >> overlapDividerBitsNorm;
|
||||||
}
|
}
|
||||||
|
|
||||||
pVec1 = (__m64*)pV1;
|
pVec1 = (__m64*)pV1;
|
||||||
pVec2 = (__m64*)pV2;
|
pVec2 = (__m64*)pV2;
|
||||||
|
|
||||||
shifter = _m_from_int(overlapDividerBits);
|
shifter = _m_from_int(overlapDividerBitsNorm);
|
||||||
accu = _mm_setzero_si64();
|
accu = _mm_setzero_si64();
|
||||||
|
|
||||||
// Process 4 parallel sets of 2 * stereo samples or 4 * mono samples
|
// Process 4 parallel sets of 2 * stereo samples or 4 * mono samples
|
||||||
|
@ -191,10 +196,15 @@ double TDStretchMMX::calcCrossCorrAccumulate(const short *pV1, const short *pV2,
|
||||||
pV1 = (short *)pVec1;
|
pV1 = (short *)pVec1;
|
||||||
for (int j = 1; j <= channels; j ++)
|
for (int j = 1; j <= channels; j ++)
|
||||||
{
|
{
|
||||||
lnorm += (pV1[-j] * pV1[-j]) >> overlapDividerBits;
|
lnorm += (pV1[-j] * pV1[-j]) >> overlapDividerBitsNorm;
|
||||||
}
|
}
|
||||||
dnorm += (double)lnorm;
|
dnorm += (double)lnorm;
|
||||||
|
|
||||||
|
if (lnorm > (long)maxnorm)
|
||||||
|
{
|
||||||
|
maxnorm = lnorm;
|
||||||
|
}
|
||||||
|
|
||||||
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
||||||
// done using floating point operation
|
// done using floating point operation
|
||||||
return (double)corr / sqrt((dnorm < 1e-9) ? 1.0 : dnorm);
|
return (double)corr / sqrt((dnorm < 1e-9) ? 1.0 : dnorm);
|
||||||
|
@ -233,7 +243,7 @@ void TDStretchMMX::overlapStereo(short *output, const short *input) const
|
||||||
|
|
||||||
// Overlaplength-division by shifter. "+1" is to account for "-1" deduced in
|
// Overlaplength-division by shifter. "+1" is to account for "-1" deduced in
|
||||||
// overlapDividerBits calculation earlier.
|
// overlapDividerBits calculation earlier.
|
||||||
shifter = _m_from_int(overlapDividerBits + 1);
|
shifter = _m_from_int(overlapDividerBitsPure + 1);
|
||||||
|
|
||||||
for (i = 0; i < overlapLength / 4; i ++)
|
for (i = 0; i < overlapLength / 4; i ++)
|
||||||
{
|
{
|
||||||
|
@ -287,6 +297,7 @@ void TDStretchMMX::overlapStereo(short *output, const short *input) const
|
||||||
|
|
||||||
FIRFilterMMX::FIRFilterMMX() : FIRFilter()
|
FIRFilterMMX::FIRFilterMMX() : FIRFilter()
|
||||||
{
|
{
|
||||||
|
filterCoeffsAlign = NULL;
|
||||||
filterCoeffsUnalign = NULL;
|
filterCoeffsUnalign = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,10 +23,10 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2014-01-08 05:25:40 +1100 (Wed, 08 Jan 2014) $
|
// Last changed : $Date: 2015-08-09 00:00:15 +0300 (Sun, 09 Aug 2015) $
|
||||||
// File revision : $Revision: 4 $
|
// File revision : $Revision: 4 $
|
||||||
//
|
//
|
||||||
// $Id: sse_optimized.cpp 184 2014-01-07 18:25:40Z oparviai $
|
// $Id: sse_optimized.cpp 226 2015-08-08 21:00:15Z oparviai $
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
|
@ -71,7 +71,7 @@ using namespace soundtouch;
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
// Calculates cross correlation of two buffers
|
// Calculates cross correlation of two buffers
|
||||||
double TDStretchSSE::calcCrossCorr(const float *pV1, const float *pV2, double &norm) const
|
double TDStretchSSE::calcCrossCorr(const float *pV1, const float *pV2, double &anorm)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
const float *pVec1;
|
const float *pVec1;
|
||||||
|
@ -141,7 +141,8 @@ double TDStretchSSE::calcCrossCorr(const float *pV1, const float *pV2, double &n
|
||||||
|
|
||||||
// return value = vSum[0] + vSum[1] + vSum[2] + vSum[3]
|
// return value = vSum[0] + vSum[1] + vSum[2] + vSum[3]
|
||||||
float *pvNorm = (float*)&vNorm;
|
float *pvNorm = (float*)&vNorm;
|
||||||
norm = (pvNorm[0] + pvNorm[1] + pvNorm[2] + pvNorm[3]);
|
float norm = (pvNorm[0] + pvNorm[1] + pvNorm[2] + pvNorm[3]);
|
||||||
|
anorm = norm;
|
||||||
|
|
||||||
float *pvSum = (float*)&vSum;
|
float *pvSum = (float*)&vSum;
|
||||||
return (double)(pvSum[0] + pvSum[1] + pvSum[2] + pvSum[3]) / sqrt(norm < 1e-9 ? 1.0 : norm);
|
return (double)(pvSum[0] + pvSum[1] + pvSum[2] + pvSum[3]) / sqrt(norm < 1e-9 ? 1.0 : norm);
|
||||||
|
@ -182,7 +183,7 @@ double TDStretchSSE::calcCrossCorr(const float *pV1, const float *pV2, double &n
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
double TDStretchSSE::calcCrossCorrAccumulate(const float *pV1, const float *pV2, double &norm) const
|
double TDStretchSSE::calcCrossCorrAccumulate(const float *pV1, const float *pV2, double &norm)
|
||||||
{
|
{
|
||||||
// call usual calcCrossCorr function because SSE does not show big benefit of
|
// call usual calcCrossCorr function because SSE does not show big benefit of
|
||||||
// accumulating "norm" value, and also the "norm" rolling algorithm would get
|
// accumulating "norm" value, and also the "norm" rolling algorithm would get
|
||||||
|
@ -258,14 +259,17 @@ uint FIRFilterSSE::evaluateFilterStereo(float *dest, const float *source, uint n
|
||||||
assert(((ulongptr)filterCoeffsAlign) % 16 == 0);
|
assert(((ulongptr)filterCoeffsAlign) % 16 == 0);
|
||||||
|
|
||||||
// filter is evaluated for two stereo samples with each iteration, thus use of 'j += 2'
|
// filter is evaluated for two stereo samples with each iteration, thus use of 'j += 2'
|
||||||
|
#pragma omp parallel for
|
||||||
for (j = 0; j < count; j += 2)
|
for (j = 0; j < count; j += 2)
|
||||||
{
|
{
|
||||||
const float *pSrc;
|
const float *pSrc;
|
||||||
|
float *pDest;
|
||||||
const __m128 *pFil;
|
const __m128 *pFil;
|
||||||
__m128 sum1, sum2;
|
__m128 sum1, sum2;
|
||||||
uint i;
|
uint i;
|
||||||
|
|
||||||
pSrc = (const float*)source; // source audio data
|
pSrc = (const float*)source + j * 2; // source audio data
|
||||||
|
pDest = dest + j * 2; // destination audio data
|
||||||
pFil = (const __m128*)filterCoeffsAlign; // filter coefficients. NOTE: Assumes coefficients
|
pFil = (const __m128*)filterCoeffsAlign; // filter coefficients. NOTE: Assumes coefficients
|
||||||
// are aligned to 16-byte boundary
|
// are aligned to 16-byte boundary
|
||||||
sum1 = sum2 = _mm_setzero_ps();
|
sum1 = sum2 = _mm_setzero_ps();
|
||||||
|
@ -298,12 +302,10 @@ uint FIRFilterSSE::evaluateFilterStereo(float *dest, const float *source, uint n
|
||||||
// to sum the two hi- and lo-floats of these registers together.
|
// to sum the two hi- and lo-floats of these registers together.
|
||||||
|
|
||||||
// post-shuffle & add the filtered values and store to dest.
|
// post-shuffle & add the filtered values and store to dest.
|
||||||
_mm_storeu_ps(dest, _mm_add_ps(
|
_mm_storeu_ps(pDest, _mm_add_ps(
|
||||||
_mm_shuffle_ps(sum1, sum2, _MM_SHUFFLE(1,0,3,2)), // s2_1 s2_0 s1_3 s1_2
|
_mm_shuffle_ps(sum1, sum2, _MM_SHUFFLE(1,0,3,2)), // s2_1 s2_0 s1_3 s1_2
|
||||||
_mm_shuffle_ps(sum1, sum2, _MM_SHUFFLE(3,2,1,0)) // s2_3 s2_2 s1_1 s1_0
|
_mm_shuffle_ps(sum1, sum2, _MM_SHUFFLE(3,2,1,0)) // s2_3 s2_2 s1_1 s1_0
|
||||||
));
|
));
|
||||||
source += 4;
|
|
||||||
dest += 4;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ideas for further improvement:
|
// Ideas for further improvement:
|
||||||
|
|
|
@ -233,11 +233,11 @@ void OpenALStream::SoundLoop()
|
||||||
|
|
||||||
if (iBuffersProcessed)
|
if (iBuffersProcessed)
|
||||||
{
|
{
|
||||||
float rate = m_mixer->GetCurrentSpeed();
|
double rate = (double)m_mixer->GetCurrentSpeed();
|
||||||
if (rate <= 0)
|
if (rate <= 0)
|
||||||
{
|
{
|
||||||
Core::RequestRefreshInfo();
|
Core::RequestRefreshInfo();
|
||||||
rate = m_mixer->GetCurrentSpeed();
|
rate = (double)m_mixer->GetCurrentSpeed();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Place a lower limit of 10% speed. When a game boots up, there will be
|
// Place a lower limit of 10% speed. When a game boots up, there will be
|
||||||
|
|
Loading…
Reference in New Issue