Updated SoundTouch library to 1.8.1 [r198]
This commit is contained in:
parent
7a01effe94
commit
ba2bec1c0a
|
@ -12,10 +12,10 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2009-01-11 11:34:24 +0000 (Sun, 11 Jan 2009) $
|
// Last changed : $Date: 2014-01-06 08:40:22 +1100 (Mon, 06 Jan 2014) $
|
||||||
// File revision : $Revision: 4 $
|
// File revision : $Revision: 4 $
|
||||||
//
|
//
|
||||||
// $Id: AAFilter.cpp 45 2009-01-11 11:34:24Z oparviai $
|
// $Id: AAFilter.cpp 177 2014-01-05 21:40:22Z oparviai $
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
|
@ -52,6 +52,30 @@ using namespace soundtouch;
|
||||||
#define PI 3.141592655357989
|
#define PI 3.141592655357989
|
||||||
#define TWOPI (2 * PI)
|
#define TWOPI (2 * PI)
|
||||||
|
|
||||||
|
// define this to save AA filter coefficients to a file
|
||||||
|
// #define _DEBUG_SAVE_AAFILTER_COEFFICIENTS 1
|
||||||
|
|
||||||
|
#ifdef _DEBUG_SAVE_AAFILTER_COEFFICIENTS
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
static void _DEBUG_SAVE_AAFIR_COEFFS(SAMPLETYPE *coeffs, int len)
|
||||||
|
{
|
||||||
|
FILE *fptr = fopen("aa_filter_coeffs.txt", "wt");
|
||||||
|
if (fptr == NULL) return;
|
||||||
|
|
||||||
|
for (int i = 0; i < len; i ++)
|
||||||
|
{
|
||||||
|
double temp = coeffs[i];
|
||||||
|
fprintf(fptr, "%lf\n", temp);
|
||||||
|
}
|
||||||
|
fclose(fptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
#define _DEBUG_SAVE_AAFIR_COEFFS(x, y)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
*
|
*
|
||||||
* Implementation of the class 'AAFilter'
|
* Implementation of the class 'AAFilter'
|
||||||
|
@ -99,7 +123,7 @@ void AAFilter::calculateCoeffs()
|
||||||
{
|
{
|
||||||
uint i;
|
uint i;
|
||||||
double cntTemp, temp, tempCoeff,h, w;
|
double cntTemp, temp, tempCoeff,h, w;
|
||||||
double fc2, wc;
|
double wc;
|
||||||
double scaleCoeff, sum;
|
double scaleCoeff, sum;
|
||||||
double *work;
|
double *work;
|
||||||
SAMPLETYPE *coeffs;
|
SAMPLETYPE *coeffs;
|
||||||
|
@ -112,8 +136,7 @@ void AAFilter::calculateCoeffs()
|
||||||
work = new double[length];
|
work = new double[length];
|
||||||
coeffs = new SAMPLETYPE[length];
|
coeffs = new SAMPLETYPE[length];
|
||||||
|
|
||||||
fc2 = 2.0 * cutoffFreq;
|
wc = 2.0 * PI * cutoffFreq;
|
||||||
wc = PI * fc2;
|
|
||||||
tempCoeff = TWOPI / (double)length;
|
tempCoeff = TWOPI / (double)length;
|
||||||
|
|
||||||
sum = 0;
|
sum = 0;
|
||||||
|
@ -124,7 +147,7 @@ void AAFilter::calculateCoeffs()
|
||||||
temp = cntTemp * wc;
|
temp = cntTemp * wc;
|
||||||
if (temp != 0)
|
if (temp != 0)
|
||||||
{
|
{
|
||||||
h = fc2 * sin(temp) / temp; // sinc function
|
h = sin(temp) / temp; // sinc function
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -153,17 +176,21 @@ void AAFilter::calculateCoeffs()
|
||||||
|
|
||||||
for (i = 0; i < length; i ++)
|
for (i = 0; i < length; i ++)
|
||||||
{
|
{
|
||||||
// scale & round to nearest integer
|
|
||||||
temp = work[i] * scaleCoeff;
|
temp = work[i] * scaleCoeff;
|
||||||
|
//#if SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
|
// scale & round to nearest integer
|
||||||
temp += (temp >= 0) ? 0.5 : -0.5;
|
temp += (temp >= 0) ? 0.5 : -0.5;
|
||||||
// ensure no overfloods
|
// ensure no overfloods
|
||||||
assert(temp >= -32768 && temp <= 32767);
|
assert(temp >= -32768 && temp <= 32767);
|
||||||
|
//#endif
|
||||||
coeffs[i] = (SAMPLETYPE)temp;
|
coeffs[i] = (SAMPLETYPE)temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set coefficients. Use divide factor 14 => divide result by 2^14 = 16384
|
// Set coefficients. Use divide factor 14 => divide result by 2^14 = 16384
|
||||||
pFIR->setCoefficients(coeffs, length, 14);
|
pFIR->setCoefficients(coeffs, length, 14);
|
||||||
|
|
||||||
|
_DEBUG_SAVE_AAFIR_COEFFS(coeffs, length);
|
||||||
|
|
||||||
delete[] work;
|
delete[] work;
|
||||||
delete[] coeffs;
|
delete[] coeffs;
|
||||||
}
|
}
|
||||||
|
@ -178,6 +205,31 @@ uint AAFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Applies the filter to the given src & dest pipes, so that processed amount of
|
||||||
|
/// samples get removed from src, and produced amount added to dest
|
||||||
|
/// Note : The amount of outputted samples is by value of 'filter length'
|
||||||
|
/// smaller than the amount of input samples.
|
||||||
|
uint AAFilter::evaluate(FIFOSampleBuffer &dest, FIFOSampleBuffer &src) const
|
||||||
|
{
|
||||||
|
SAMPLETYPE *pdest;
|
||||||
|
const SAMPLETYPE *psrc;
|
||||||
|
uint numSrcSamples;
|
||||||
|
uint result;
|
||||||
|
int numChannels = src.getChannels();
|
||||||
|
|
||||||
|
assert(numChannels == dest.getChannels());
|
||||||
|
|
||||||
|
numSrcSamples = src.numSamples();
|
||||||
|
psrc = src.ptrBegin();
|
||||||
|
pdest = dest.ptrEnd(numSrcSamples);
|
||||||
|
result = pFIR->evaluate(pdest, psrc, numSrcSamples, numChannels);
|
||||||
|
src.receiveSamples(result);
|
||||||
|
dest.putSamples(result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
uint AAFilter::getLength() const
|
uint AAFilter::getLength() const
|
||||||
{
|
{
|
||||||
return pFIR->getLength();
|
return pFIR->getLength();
|
||||||
|
|
|
@ -13,10 +13,10 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2008-02-10 16:26:55 +0000 (Sun, 10 Feb 2008) $
|
// Last changed : $Date: 2014-01-08 06:41:23 +1100 (Wed, 08 Jan 2014) $
|
||||||
// File revision : $Revision: 4 $
|
// File revision : $Revision: 4 $
|
||||||
//
|
//
|
||||||
// $Id: AAFilter.h 11 2008-02-10 16:26:55Z oparviai $
|
// $Id: AAFilter.h 187 2014-01-07 19:41:23Z oparviai $
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
|
@ -45,6 +45,7 @@
|
||||||
#define AAFilter_H
|
#define AAFilter_H
|
||||||
|
|
||||||
#include "STTypes.h"
|
#include "STTypes.h"
|
||||||
|
#include "FIFOSampleBuffer.h"
|
||||||
|
|
||||||
namespace soundtouch
|
namespace soundtouch
|
||||||
{
|
{
|
||||||
|
@ -84,6 +85,14 @@ public:
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
uint numSamples,
|
uint numSamples,
|
||||||
uint numChannels) const;
|
uint numChannels) const;
|
||||||
|
|
||||||
|
/// Applies the filter to the given src & dest pipes, so that processed amount of
|
||||||
|
/// samples get removed from src, and produced amount added to dest
|
||||||
|
/// Note : The amount of outputted samples is by value of 'filter length'
|
||||||
|
/// smaller than the amount of input samples.
|
||||||
|
uint evaluate(FIFOSampleBuffer &dest,
|
||||||
|
FIFOSampleBuffer &src) const;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2012-08-30 19:45:25 +0000 (Thu, 30 Aug 2012) $
|
// Last changed : $Date: 2012-08-31 05:45:25 +1000 (Fri, 31 Aug 2012) $
|
||||||
// File revision : $Revision: 4 $
|
// File revision : $Revision: 4 $
|
||||||
//
|
//
|
||||||
// $Id: BPMDetect.cpp 149 2012-08-30 19:45:25Z oparviai $
|
// $Id: BPMDetect.cpp 149 2012-08-30 19:45:25Z oparviai $
|
||||||
|
|
|
@ -4,6 +4,9 @@ set(SRCS
|
||||||
cpu_detect_x86.cpp
|
cpu_detect_x86.cpp
|
||||||
FIFOSampleBuffer.cpp
|
FIFOSampleBuffer.cpp
|
||||||
FIRFilter.cpp
|
FIRFilter.cpp
|
||||||
|
InterpolateCubic.cpp
|
||||||
|
InterpolateLinear.cpp
|
||||||
|
InterpolateShannon.cpp
|
||||||
mmx_optimized.cpp
|
mmx_optimized.cpp
|
||||||
PeakFinder.cpp
|
PeakFinder.cpp
|
||||||
RateTransposer.cpp
|
RateTransposer.cpp
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2012-11-08 18:53:01 +0000 (Thu, 08 Nov 2012) $
|
// Last changed : $Date: 2012-11-09 05:53:01 +1100 (Fri, 09 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,6 +86,10 @@ 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
|
||||||
|
|
|
@ -161,6 +161,7 @@ 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();
|
||||||
|
|
||||||
/// 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;
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2013-06-12 15:24:44 +0000 (Wed, 12 Jun 2013) $
|
// Last changed : $Date: 2013-06-13 01:24:44 +1000 (Thu, 13 Jun 2013) $
|
||||||
// File revision : $Revision: 4 $
|
// File revision : $Revision: 4 $
|
||||||
//
|
//
|
||||||
// $Id: FIRFilter.cpp 171 2013-06-12 15:24:44Z oparviai $
|
// $Id: FIRFilter.cpp 171 2013-06-12 15:24:44Z oparviai $
|
||||||
|
@ -217,7 +217,6 @@ uint FIRFilter::evaluateFilterMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uin
|
||||||
sum[c] = 0;
|
sum[c] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
free(sum);
|
|
||||||
return numSamples - length;
|
return numSamples - length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2013-06-12 15:24:44 +0000 (Wed, 12 Jun 2013) $
|
// Last changed : $Date: 2013-06-13 01:24:44 +1000 (Thu, 13 Jun 2013) $
|
||||||
// File revision : $Revision: 4 $
|
// File revision : $Revision: 4 $
|
||||||
//
|
//
|
||||||
// $Id: FIRFilter.h 171 2013-06-12 15:24:44Z oparviai $
|
// $Id: FIRFilter.h 171 2013-06-12 15:24:44Z oparviai $
|
||||||
|
|
|
@ -0,0 +1,200 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
///
|
||||||
|
/// Cubic interpolation routine.
|
||||||
|
///
|
||||||
|
/// Author : Copyright (c) Olli Parviainen
|
||||||
|
/// Author e-mail : oparviai 'at' iki.fi
|
||||||
|
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// $Id: InterpolateCubic.cpp 179 2014-01-06 18:41:42Z oparviai $
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// License :
|
||||||
|
//
|
||||||
|
// SoundTouch audio processing library
|
||||||
|
// Copyright (c) Olli Parviainen
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU Lesser General Public
|
||||||
|
// License as published by the Free Software Foundation; either
|
||||||
|
// version 2.1 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public
|
||||||
|
// License along with this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include "InterpolateCubic.h"
|
||||||
|
#include "STTypes.h"
|
||||||
|
|
||||||
|
using namespace soundtouch;
|
||||||
|
|
||||||
|
// cubic interpolation coefficients
|
||||||
|
static const float _coeffs[]=
|
||||||
|
{ -0.5f, 1.0f, -0.5f, 0.0f,
|
||||||
|
1.5f, -2.5f, 0.0f, 1.0f,
|
||||||
|
-1.5f, 2.0f, 0.5f, 0.0f,
|
||||||
|
0.5f, -0.5f, 0.0f, 0.0f};
|
||||||
|
|
||||||
|
|
||||||
|
InterpolateCubic::InterpolateCubic()
|
||||||
|
{
|
||||||
|
fract = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void InterpolateCubic::resetRegisters()
|
||||||
|
{
|
||||||
|
fract = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Transpose mono audio. Returns number of produced output samples, and
|
||||||
|
/// updates "srcSamples" to amount of consumed source samples
|
||||||
|
int InterpolateCubic::transposeMono(SAMPLETYPE *pdest,
|
||||||
|
const SAMPLETYPE *psrc,
|
||||||
|
int &srcSamples)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int srcSampleEnd = srcSamples - 4;
|
||||||
|
int srcCount = 0;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while (srcCount < srcSampleEnd)
|
||||||
|
{
|
||||||
|
float out;
|
||||||
|
const float x3 = 1.0f;
|
||||||
|
const float x2 = (float)fract; // x
|
||||||
|
const float x1 = x2*x2; // x^2
|
||||||
|
const float x0 = x1*x2; // x^3
|
||||||
|
float y0, y1, y2, y3;
|
||||||
|
|
||||||
|
assert(fract < 1.0);
|
||||||
|
|
||||||
|
y0 = _coeffs[0] * x0 + _coeffs[1] * x1 + _coeffs[2] * x2 + _coeffs[3] * x3;
|
||||||
|
y1 = _coeffs[4] * x0 + _coeffs[5] * x1 + _coeffs[6] * x2 + _coeffs[7] * x3;
|
||||||
|
y2 = _coeffs[8] * x0 + _coeffs[9] * x1 + _coeffs[10] * x2 + _coeffs[11] * x3;
|
||||||
|
y3 = _coeffs[12] * x0 + _coeffs[13] * x1 + _coeffs[14] * x2 + _coeffs[15] * x3;
|
||||||
|
|
||||||
|
out = y0 * psrc[0] + y1 * psrc[1] + y2 * psrc[2] + y3 * psrc[3];
|
||||||
|
|
||||||
|
pdest[i] = (SAMPLETYPE)out;
|
||||||
|
i ++;
|
||||||
|
|
||||||
|
// update position fraction
|
||||||
|
fract += rate;
|
||||||
|
// update whole positions
|
||||||
|
int whole = (int)fract;
|
||||||
|
fract -= whole;
|
||||||
|
psrc += whole;
|
||||||
|
srcCount += whole;
|
||||||
|
}
|
||||||
|
srcSamples = srcCount;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Transpose stereo audio. Returns number of produced output samples, and
|
||||||
|
/// updates "srcSamples" to amount of consumed source samples
|
||||||
|
int InterpolateCubic::transposeStereo(SAMPLETYPE *pdest,
|
||||||
|
const SAMPLETYPE *psrc,
|
||||||
|
int &srcSamples)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int srcSampleEnd = srcSamples - 4;
|
||||||
|
int srcCount = 0;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while (srcCount < srcSampleEnd)
|
||||||
|
{
|
||||||
|
const float x3 = 1.0f;
|
||||||
|
const float x2 = (float)fract; // x
|
||||||
|
const float x1 = x2*x2; // x^2
|
||||||
|
const float x0 = x1*x2; // x^3
|
||||||
|
float y0, y1, y2, y3;
|
||||||
|
float out0, out1;
|
||||||
|
|
||||||
|
assert(fract < 1.0);
|
||||||
|
|
||||||
|
y0 = _coeffs[0] * x0 + _coeffs[1] * x1 + _coeffs[2] * x2 + _coeffs[3] * x3;
|
||||||
|
y1 = _coeffs[4] * x0 + _coeffs[5] * x1 + _coeffs[6] * x2 + _coeffs[7] * x3;
|
||||||
|
y2 = _coeffs[8] * x0 + _coeffs[9] * x1 + _coeffs[10] * x2 + _coeffs[11] * x3;
|
||||||
|
y3 = _coeffs[12] * x0 + _coeffs[13] * x1 + _coeffs[14] * x2 + _coeffs[15] * x3;
|
||||||
|
|
||||||
|
out0 = y0 * psrc[0] + y1 * psrc[2] + y2 * psrc[4] + y3 * psrc[6];
|
||||||
|
out1 = y0 * psrc[1] + y1 * psrc[3] + y2 * psrc[5] + y3 * psrc[7];
|
||||||
|
|
||||||
|
pdest[2*i] = (SAMPLETYPE)out0;
|
||||||
|
pdest[2*i+1] = (SAMPLETYPE)out1;
|
||||||
|
i ++;
|
||||||
|
|
||||||
|
// update position fraction
|
||||||
|
fract += rate;
|
||||||
|
// update whole positions
|
||||||
|
int whole = (int)fract;
|
||||||
|
fract -= whole;
|
||||||
|
psrc += 2*whole;
|
||||||
|
srcCount += whole;
|
||||||
|
}
|
||||||
|
srcSamples = srcCount;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Transpose multi-channel audio. Returns number of produced output samples, and
|
||||||
|
/// updates "srcSamples" to amount of consumed source samples
|
||||||
|
int InterpolateCubic::transposeMulti(SAMPLETYPE *pdest,
|
||||||
|
const SAMPLETYPE *psrc,
|
||||||
|
int &srcSamples)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int srcSampleEnd = srcSamples - 4;
|
||||||
|
int srcCount = 0;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while (srcCount < srcSampleEnd)
|
||||||
|
{
|
||||||
|
const float x3 = 1.0f;
|
||||||
|
const float x2 = (float)fract; // x
|
||||||
|
const float x1 = x2*x2; // x^2
|
||||||
|
const float x0 = x1*x2; // x^3
|
||||||
|
float y0, y1, y2, y3;
|
||||||
|
|
||||||
|
assert(fract < 1.0);
|
||||||
|
|
||||||
|
y0 = _coeffs[0] * x0 + _coeffs[1] * x1 + _coeffs[2] * x2 + _coeffs[3] * x3;
|
||||||
|
y1 = _coeffs[4] * x0 + _coeffs[5] * x1 + _coeffs[6] * x2 + _coeffs[7] * x3;
|
||||||
|
y2 = _coeffs[8] * x0 + _coeffs[9] * x1 + _coeffs[10] * x2 + _coeffs[11] * x3;
|
||||||
|
y3 = _coeffs[12] * x0 + _coeffs[13] * x1 + _coeffs[14] * x2 + _coeffs[15] * x3;
|
||||||
|
|
||||||
|
for (int c = 0; c < numChannels; c ++)
|
||||||
|
{
|
||||||
|
float out;
|
||||||
|
out = y0 * psrc[c] + y1 * psrc[c + numChannels] + y2 * psrc[c + 2 * numChannels] + y3 * psrc[c + 3 * numChannels];
|
||||||
|
pdest[0] = (SAMPLETYPE)out;
|
||||||
|
pdest ++;
|
||||||
|
}
|
||||||
|
i ++;
|
||||||
|
|
||||||
|
// update position fraction
|
||||||
|
fract += rate;
|
||||||
|
// update whole positions
|
||||||
|
int whole = (int)fract;
|
||||||
|
fract -= whole;
|
||||||
|
psrc += numChannels*whole;
|
||||||
|
srcCount += whole;
|
||||||
|
}
|
||||||
|
srcSamples = srcCount;
|
||||||
|
return i;
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
///
|
||||||
|
/// Cubic interpolation routine.
|
||||||
|
///
|
||||||
|
/// Author : Copyright (c) Olli Parviainen
|
||||||
|
/// Author e-mail : oparviai 'at' iki.fi
|
||||||
|
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// $Id: InterpolateCubic.h 179 2014-01-06 18:41:42Z oparviai $
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// License :
|
||||||
|
//
|
||||||
|
// SoundTouch audio processing library
|
||||||
|
// Copyright (c) Olli Parviainen
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU Lesser General Public
|
||||||
|
// License as published by the Free Software Foundation; either
|
||||||
|
// version 2.1 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public
|
||||||
|
// License along with this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef _InterpolateCubic_H_
|
||||||
|
#define _InterpolateCubic_H_
|
||||||
|
|
||||||
|
#include "RateTransposer.h"
|
||||||
|
#include "STTypes.h"
|
||||||
|
|
||||||
|
namespace soundtouch
|
||||||
|
{
|
||||||
|
|
||||||
|
class InterpolateCubic : public TransposerBase
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
virtual void resetRegisters();
|
||||||
|
virtual int transposeMono(SAMPLETYPE *dest,
|
||||||
|
const SAMPLETYPE *src,
|
||||||
|
int &srcSamples);
|
||||||
|
virtual int transposeStereo(SAMPLETYPE *dest,
|
||||||
|
const SAMPLETYPE *src,
|
||||||
|
int &srcSamples);
|
||||||
|
virtual int transposeMulti(SAMPLETYPE *dest,
|
||||||
|
const SAMPLETYPE *src,
|
||||||
|
int &srcSamples);
|
||||||
|
|
||||||
|
float fract;
|
||||||
|
|
||||||
|
public:
|
||||||
|
InterpolateCubic();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,299 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
///
|
||||||
|
/// Linear interpolation algorithm.
|
||||||
|
///
|
||||||
|
/// Author : Copyright (c) Olli Parviainen
|
||||||
|
/// Author e-mail : oparviai 'at' iki.fi
|
||||||
|
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// $Id: InterpolateLinear.cpp 180 2014-01-06 19:16:02Z oparviai $
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// License :
|
||||||
|
//
|
||||||
|
// SoundTouch audio processing library
|
||||||
|
// Copyright (c) Olli Parviainen
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU Lesser General Public
|
||||||
|
// License as published by the Free Software Foundation; either
|
||||||
|
// version 2.1 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public
|
||||||
|
// License along with this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "InterpolateLinear.h"
|
||||||
|
|
||||||
|
using namespace soundtouch;
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// InterpolateLinearInteger - integer arithmetic implementation
|
||||||
|
//
|
||||||
|
|
||||||
|
/// fixed-point interpolation routine precision
|
||||||
|
#define SCALE 65536
|
||||||
|
|
||||||
|
|
||||||
|
// Constructor
|
||||||
|
InterpolateLinearInteger::InterpolateLinearInteger() : TransposerBase()
|
||||||
|
{
|
||||||
|
// Notice: use local function calling syntax for sake of clarity,
|
||||||
|
// to indicate the fact that C++ constructor can't call virtual functions.
|
||||||
|
resetRegisters();
|
||||||
|
setRate(1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void InterpolateLinearInteger::resetRegisters()
|
||||||
|
{
|
||||||
|
iFract = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Transposes the sample rate of the given samples using linear interpolation.
|
||||||
|
// 'Mono' version of the routine. Returns the number of samples returned in
|
||||||
|
// the "dest" buffer
|
||||||
|
int InterpolateLinearInteger::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int srcSampleEnd = srcSamples - 1;
|
||||||
|
int srcCount = 0;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while (srcCount < srcSampleEnd)
|
||||||
|
{
|
||||||
|
LONG_SAMPLETYPE temp;
|
||||||
|
|
||||||
|
assert(iFract < SCALE);
|
||||||
|
|
||||||
|
temp = (SCALE - iFract) * src[0] + iFract * src[1];
|
||||||
|
dest[i] = (SAMPLETYPE)(temp / SCALE);
|
||||||
|
i++;
|
||||||
|
|
||||||
|
iFract += iRate;
|
||||||
|
|
||||||
|
int iWhole = iFract / SCALE;
|
||||||
|
iFract -= iWhole * SCALE;
|
||||||
|
srcCount += iWhole;
|
||||||
|
src += iWhole;
|
||||||
|
}
|
||||||
|
srcSamples = srcCount;
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Transposes the sample rate of the given samples using linear interpolation.
|
||||||
|
// 'Stereo' version of the routine. Returns the number of samples returned in
|
||||||
|
// the "dest" buffer
|
||||||
|
int InterpolateLinearInteger::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int srcSampleEnd = srcSamples - 1;
|
||||||
|
int srcCount = 0;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while (srcCount < srcSampleEnd)
|
||||||
|
{
|
||||||
|
LONG_SAMPLETYPE temp0;
|
||||||
|
LONG_SAMPLETYPE temp1;
|
||||||
|
|
||||||
|
assert(iFract < SCALE);
|
||||||
|
|
||||||
|
temp0 = (SCALE - iFract) * src[0] + iFract * src[2];
|
||||||
|
temp1 = (SCALE - iFract) * src[1] + iFract * src[3];
|
||||||
|
dest[0] = (SAMPLETYPE)(temp0 / SCALE);
|
||||||
|
dest[1] = (SAMPLETYPE)(temp1 / SCALE);
|
||||||
|
dest += 2;
|
||||||
|
i++;
|
||||||
|
|
||||||
|
iFract += iRate;
|
||||||
|
|
||||||
|
int iWhole = iFract / SCALE;
|
||||||
|
iFract -= iWhole * SCALE;
|
||||||
|
srcCount += iWhole;
|
||||||
|
src += 2*iWhole;
|
||||||
|
}
|
||||||
|
srcSamples = srcCount;
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int InterpolateLinearInteger::transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int srcSampleEnd = srcSamples - 1;
|
||||||
|
int srcCount = 0;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while (srcCount < srcSampleEnd)
|
||||||
|
{
|
||||||
|
LONG_SAMPLETYPE temp, vol1;
|
||||||
|
|
||||||
|
assert(iFract < SCALE);
|
||||||
|
vol1 = (SCALE - iFract);
|
||||||
|
for (int c = 0; c < numChannels; c ++)
|
||||||
|
{
|
||||||
|
temp = vol1 * src[c] + iFract * src[c + numChannels];
|
||||||
|
dest[0] = (SAMPLETYPE)(temp / SCALE);
|
||||||
|
dest ++;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
|
||||||
|
iFract += iRate;
|
||||||
|
|
||||||
|
int iWhole = iFract / SCALE;
|
||||||
|
iFract -= iWhole * SCALE;
|
||||||
|
srcCount += iWhole;
|
||||||
|
src += iWhole * numChannels;
|
||||||
|
}
|
||||||
|
srcSamples = srcCount;
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Sets new target iRate. Normal iRate = 1.0, smaller values represent slower
|
||||||
|
// iRate, larger faster iRates.
|
||||||
|
void InterpolateLinearInteger::setRate(float newRate)
|
||||||
|
{
|
||||||
|
iRate = (int)(newRate * SCALE + 0.5f);
|
||||||
|
TransposerBase::setRate(newRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// InterpolateLinearFloat - floating point arithmetic implementation
|
||||||
|
//
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
// Constructor
|
||||||
|
InterpolateLinearFloat::InterpolateLinearFloat() : TransposerBase()
|
||||||
|
{
|
||||||
|
// Notice: use local function calling syntax for sake of clarity,
|
||||||
|
// to indicate the fact that C++ constructor can't call virtual functions.
|
||||||
|
resetRegisters();
|
||||||
|
setRate(1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void InterpolateLinearFloat::resetRegisters()
|
||||||
|
{
|
||||||
|
fract = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Transposes the sample rate of the given samples using linear interpolation.
|
||||||
|
// 'Mono' version of the routine. Returns the number of samples returned in
|
||||||
|
// the "dest" buffer
|
||||||
|
int InterpolateLinearFloat::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int srcSampleEnd = srcSamples - 1;
|
||||||
|
int srcCount = 0;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while (srcCount < srcSampleEnd)
|
||||||
|
{
|
||||||
|
double out;
|
||||||
|
assert(fract < 1.0);
|
||||||
|
|
||||||
|
out = (1.0 - fract) * src[0] + fract * src[1];
|
||||||
|
dest[i] = (SAMPLETYPE)out;
|
||||||
|
i ++;
|
||||||
|
|
||||||
|
// update position fraction
|
||||||
|
fract += rate;
|
||||||
|
// update whole positions
|
||||||
|
int whole = (int)fract;
|
||||||
|
fract -= whole;
|
||||||
|
src += whole;
|
||||||
|
srcCount += whole;
|
||||||
|
}
|
||||||
|
srcSamples = srcCount;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Transposes the sample rate of the given samples using linear interpolation.
|
||||||
|
// 'Mono' version of the routine. Returns the number of samples returned in
|
||||||
|
// the "dest" buffer
|
||||||
|
int InterpolateLinearFloat::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int srcSampleEnd = srcSamples - 1;
|
||||||
|
int srcCount = 0;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while (srcCount < srcSampleEnd)
|
||||||
|
{
|
||||||
|
double out0, out1;
|
||||||
|
assert(fract < 1.0);
|
||||||
|
|
||||||
|
out0 = (1.0 - fract) * src[0] + fract * src[2];
|
||||||
|
out1 = (1.0 - fract) * src[1] + fract * src[3];
|
||||||
|
dest[2*i] = (SAMPLETYPE)out0;
|
||||||
|
dest[2*i+1] = (SAMPLETYPE)out1;
|
||||||
|
i ++;
|
||||||
|
|
||||||
|
// update position fraction
|
||||||
|
fract += rate;
|
||||||
|
// update whole positions
|
||||||
|
int whole = (int)fract;
|
||||||
|
fract -= whole;
|
||||||
|
src += 2*whole;
|
||||||
|
srcCount += whole;
|
||||||
|
}
|
||||||
|
srcSamples = srcCount;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int InterpolateLinearFloat::transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int srcSampleEnd = srcSamples - 1;
|
||||||
|
int srcCount = 0;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while (srcCount < srcSampleEnd)
|
||||||
|
{
|
||||||
|
float temp, vol1;
|
||||||
|
|
||||||
|
vol1 = (1.0f- fract);
|
||||||
|
for (int c = 0; c < numChannels; c ++)
|
||||||
|
{
|
||||||
|
temp = vol1 * src[c] + fract * src[c + numChannels];
|
||||||
|
*dest = (SAMPLETYPE)temp;
|
||||||
|
dest ++;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
|
||||||
|
fract += rate;
|
||||||
|
|
||||||
|
int iWhole = (int)fract;
|
||||||
|
fract -= iWhole;
|
||||||
|
srcCount += iWhole;
|
||||||
|
src += iWhole * numChannels;
|
||||||
|
}
|
||||||
|
srcSamples = srcCount;
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
///
|
||||||
|
/// Linear interpolation routine.
|
||||||
|
///
|
||||||
|
/// Author : Copyright (c) Olli Parviainen
|
||||||
|
/// Author e-mail : oparviai 'at' iki.fi
|
||||||
|
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// $Id: InterpolateLinear.h 179 2014-01-06 18:41:42Z oparviai $
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// License :
|
||||||
|
//
|
||||||
|
// SoundTouch audio processing library
|
||||||
|
// Copyright (c) Olli Parviainen
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU Lesser General Public
|
||||||
|
// License as published by the Free Software Foundation; either
|
||||||
|
// version 2.1 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public
|
||||||
|
// License along with this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef _InterpolateLinear_H_
|
||||||
|
#define _InterpolateLinear_H_
|
||||||
|
|
||||||
|
#include "RateTransposer.h"
|
||||||
|
#include "STTypes.h"
|
||||||
|
|
||||||
|
namespace soundtouch
|
||||||
|
{
|
||||||
|
|
||||||
|
/// Linear transposer class that uses integer arithmetics
|
||||||
|
class InterpolateLinearInteger : public TransposerBase
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
int iFract;
|
||||||
|
int iRate;
|
||||||
|
|
||||||
|
virtual void resetRegisters();
|
||||||
|
|
||||||
|
virtual int transposeMono(SAMPLETYPE *dest,
|
||||||
|
const SAMPLETYPE *src,
|
||||||
|
int &srcSamples);
|
||||||
|
virtual int transposeStereo(SAMPLETYPE *dest,
|
||||||
|
const SAMPLETYPE *src,
|
||||||
|
int &srcSamples);
|
||||||
|
virtual int transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples);
|
||||||
|
public:
|
||||||
|
InterpolateLinearInteger();
|
||||||
|
|
||||||
|
/// Sets new target rate. Normal rate = 1.0, smaller values represent slower
|
||||||
|
/// rate, larger faster rates.
|
||||||
|
virtual void setRate(float newRate);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/// Linear transposer class that uses floating point arithmetics
|
||||||
|
class InterpolateLinearFloat : public TransposerBase
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
float fract;
|
||||||
|
|
||||||
|
virtual void resetRegisters();
|
||||||
|
|
||||||
|
virtual int transposeMono(SAMPLETYPE *dest,
|
||||||
|
const SAMPLETYPE *src,
|
||||||
|
int &srcSamples);
|
||||||
|
virtual int transposeStereo(SAMPLETYPE *dest,
|
||||||
|
const SAMPLETYPE *src,
|
||||||
|
int &srcSamples);
|
||||||
|
virtual int transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples);
|
||||||
|
|
||||||
|
public:
|
||||||
|
InterpolateLinearFloat();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,185 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
///
|
||||||
|
/// Sample interpolation routine using 8-tap band-limited Shannon interpolation
|
||||||
|
/// with kaiser window.
|
||||||
|
///
|
||||||
|
/// Notice. This algorithm is remarkably much heavier than linear or cubic
|
||||||
|
/// interpolation, and not remarkably better than cubic algorithm. Thus mostly
|
||||||
|
/// for experimental purposes
|
||||||
|
///
|
||||||
|
/// Author : Copyright (c) Olli Parviainen
|
||||||
|
/// Author e-mail : oparviai 'at' iki.fi
|
||||||
|
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// $Id: InterpolateShannon.cpp 195 2014-04-06 15:57:21Z oparviai $
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// License :
|
||||||
|
//
|
||||||
|
// SoundTouch audio processing library
|
||||||
|
// Copyright (c) Olli Parviainen
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU Lesser General Public
|
||||||
|
// License as published by the Free Software Foundation; either
|
||||||
|
// version 2.1 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public
|
||||||
|
// License along with this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include "InterpolateShannon.h"
|
||||||
|
#include "STTypes.h"
|
||||||
|
|
||||||
|
using namespace soundtouch;
|
||||||
|
|
||||||
|
|
||||||
|
/// Kaiser window with beta = 2.0
|
||||||
|
/// Values scaled down by 5% to avoid overflows
|
||||||
|
static const double _kaiser8[8] =
|
||||||
|
{
|
||||||
|
0.41778693317814,
|
||||||
|
0.64888025049173,
|
||||||
|
0.83508562409944,
|
||||||
|
0.93887857733412,
|
||||||
|
0.93887857733412,
|
||||||
|
0.83508562409944,
|
||||||
|
0.64888025049173,
|
||||||
|
0.41778693317814
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
InterpolateShannon::InterpolateShannon()
|
||||||
|
{
|
||||||
|
fract = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void InterpolateShannon::resetRegisters()
|
||||||
|
{
|
||||||
|
fract = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define PI 3.1415926536
|
||||||
|
#define sinc(x) (sin(PI * (x)) / (PI * (x)))
|
||||||
|
|
||||||
|
/// Transpose mono audio. Returns number of produced output samples, and
|
||||||
|
/// updates "srcSamples" to amount of consumed source samples
|
||||||
|
int InterpolateShannon::transposeMono(SAMPLETYPE *pdest,
|
||||||
|
const SAMPLETYPE *psrc,
|
||||||
|
int &srcSamples)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int srcSampleEnd = srcSamples - 8;
|
||||||
|
int srcCount = 0;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while (srcCount < srcSampleEnd)
|
||||||
|
{
|
||||||
|
double out;
|
||||||
|
assert(fract < 1.0);
|
||||||
|
|
||||||
|
out = psrc[0] * sinc(-3.0 - fract) * _kaiser8[0];
|
||||||
|
out += psrc[1] * sinc(-2.0 - fract) * _kaiser8[1];
|
||||||
|
out += psrc[2] * sinc(-1.0 - fract) * _kaiser8[2];
|
||||||
|
if (fract < 1e-6)
|
||||||
|
{
|
||||||
|
out += psrc[3] * _kaiser8[3]; // sinc(0) = 1
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
out += psrc[3] * sinc(- fract) * _kaiser8[3];
|
||||||
|
}
|
||||||
|
out += psrc[4] * sinc( 1.0 - fract) * _kaiser8[4];
|
||||||
|
out += psrc[5] * sinc( 2.0 - fract) * _kaiser8[5];
|
||||||
|
out += psrc[6] * sinc( 3.0 - fract) * _kaiser8[6];
|
||||||
|
out += psrc[7] * sinc( 4.0 - fract) * _kaiser8[7];
|
||||||
|
|
||||||
|
pdest[i] = (SAMPLETYPE)out;
|
||||||
|
i ++;
|
||||||
|
|
||||||
|
// update position fraction
|
||||||
|
fract += rate;
|
||||||
|
// update whole positions
|
||||||
|
int whole = (int)fract;
|
||||||
|
fract -= whole;
|
||||||
|
psrc += whole;
|
||||||
|
srcCount += whole;
|
||||||
|
}
|
||||||
|
srcSamples = srcCount;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Transpose stereo audio. Returns number of produced output samples, and
|
||||||
|
/// updates "srcSamples" to amount of consumed source samples
|
||||||
|
int InterpolateShannon::transposeStereo(SAMPLETYPE *pdest,
|
||||||
|
const SAMPLETYPE *psrc,
|
||||||
|
int &srcSamples)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int srcSampleEnd = srcSamples - 8;
|
||||||
|
int srcCount = 0;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while (srcCount < srcSampleEnd)
|
||||||
|
{
|
||||||
|
double out0, out1, w;
|
||||||
|
assert(fract < 1.0);
|
||||||
|
|
||||||
|
w = sinc(-3.0 - fract) * _kaiser8[0];
|
||||||
|
out0 = psrc[0] * w; out1 = psrc[1] * w;
|
||||||
|
w = sinc(-2.0 - fract) * _kaiser8[1];
|
||||||
|
out0 += psrc[2] * w; out1 += psrc[3] * w;
|
||||||
|
w = sinc(-1.0 - fract) * _kaiser8[2];
|
||||||
|
out0 += psrc[4] * w; out1 += psrc[5] * w;
|
||||||
|
w = _kaiser8[3] * ((fract < 1e-5) ? 1.0 : sinc(- fract)); // sinc(0) = 1
|
||||||
|
out0 += psrc[6] * w; out1 += psrc[7] * w;
|
||||||
|
w = sinc( 1.0 - fract) * _kaiser8[4];
|
||||||
|
out0 += psrc[8] * w; out1 += psrc[9] * w;
|
||||||
|
w = sinc( 2.0 - fract) * _kaiser8[5];
|
||||||
|
out0 += psrc[10] * w; out1 += psrc[11] * w;
|
||||||
|
w = sinc( 3.0 - fract) * _kaiser8[6];
|
||||||
|
out0 += psrc[12] * w; out1 += psrc[13] * w;
|
||||||
|
w = sinc( 4.0 - fract) * _kaiser8[7];
|
||||||
|
out0 += psrc[14] * w; out1 += psrc[15] * w;
|
||||||
|
|
||||||
|
pdest[2*i] = (SAMPLETYPE)out0;
|
||||||
|
pdest[2*i+1] = (SAMPLETYPE)out1;
|
||||||
|
i ++;
|
||||||
|
|
||||||
|
// update position fraction
|
||||||
|
fract += rate;
|
||||||
|
// update whole positions
|
||||||
|
int whole = (int)fract;
|
||||||
|
fract -= whole;
|
||||||
|
psrc += 2*whole;
|
||||||
|
srcCount += whole;
|
||||||
|
}
|
||||||
|
srcSamples = srcCount;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Transpose stereo audio. Returns number of produced output samples, and
|
||||||
|
/// updates "srcSamples" to amount of consumed source samples
|
||||||
|
int InterpolateShannon::transposeMulti(SAMPLETYPE *pdest,
|
||||||
|
const SAMPLETYPE *psrc,
|
||||||
|
int &srcSamples)
|
||||||
|
{
|
||||||
|
// not implemented
|
||||||
|
assert(false);
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
///
|
||||||
|
/// Sample interpolation routine using 8-tap band-limited Shannon interpolation
|
||||||
|
/// with kaiser window.
|
||||||
|
///
|
||||||
|
/// Notice. This algorithm is remarkably much heavier than linear or cubic
|
||||||
|
/// interpolation, and not remarkably better than cubic algorithm. Thus mostly
|
||||||
|
/// for experimental purposes
|
||||||
|
///
|
||||||
|
/// Author : Copyright (c) Olli Parviainen
|
||||||
|
/// Author e-mail : oparviai 'at' iki.fi
|
||||||
|
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// $Id: InterpolateShannon.h 179 2014-01-06 18:41:42Z oparviai $
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// License :
|
||||||
|
//
|
||||||
|
// SoundTouch audio processing library
|
||||||
|
// Copyright (c) Olli Parviainen
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU Lesser General Public
|
||||||
|
// License as published by the Free Software Foundation; either
|
||||||
|
// version 2.1 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public
|
||||||
|
// License along with this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef _InterpolateShannon_H_
|
||||||
|
#define _InterpolateShannon_H_
|
||||||
|
|
||||||
|
#include "RateTransposer.h"
|
||||||
|
#include "STTypes.h"
|
||||||
|
|
||||||
|
namespace soundtouch
|
||||||
|
{
|
||||||
|
|
||||||
|
class InterpolateShannon : public TransposerBase
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
void resetRegisters();
|
||||||
|
int transposeMono(SAMPLETYPE *dest,
|
||||||
|
const SAMPLETYPE *src,
|
||||||
|
int &srcSamples);
|
||||||
|
int transposeStereo(SAMPLETYPE *dest,
|
||||||
|
const SAMPLETYPE *src,
|
||||||
|
int &srcSamples);
|
||||||
|
int transposeMulti(SAMPLETYPE *dest,
|
||||||
|
const SAMPLETYPE *src,
|
||||||
|
int &srcSamples);
|
||||||
|
|
||||||
|
float fract;
|
||||||
|
|
||||||
|
public:
|
||||||
|
InterpolateShannon();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -11,7 +11,7 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2012-12-28 19:52:47 +0000 (Fri, 28 Dec 2012) $
|
// Last changed : $Date: 2012-12-29 06:52:47 +1100 (Sat, 29 Dec 2012) $
|
||||||
// File revision : $Revision: 4 $
|
// File revision : $Revision: 4 $
|
||||||
//
|
//
|
||||||
// $Id: PeakFinder.cpp 164 2012-12-28 19:52:47Z oparviai $
|
// $Id: PeakFinder.cpp 164 2012-12-28 19:52:47Z oparviai $
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2011-12-30 20:33:46 +0000 (Fri, 30 Dec 2011) $
|
// Last changed : $Date: 2011-12-31 07:33:46 +1100 (Sat, 31 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: 2013-06-14 17:34:33 +0000 (Fri, 14 Jun 2013) $
|
// Last changed : $Date: 2014-04-07 01:57:21 +1000 (Mon, 07 Apr 2014) $
|
||||||
// File revision : $Revision: 4 $
|
// File revision : $Revision: 4 $
|
||||||
//
|
//
|
||||||
// $Id: RateTransposer.cpp 172 2013-06-14 17:34:33Z oparviai $
|
// $Id: RateTransposer.cpp 195 2014-04-06 15:57:21Z oparviai $
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
|
@ -43,95 +43,25 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "RateTransposer.h"
|
#include "RateTransposer.h"
|
||||||
|
#include "InterpolateLinear.h"
|
||||||
|
#include "InterpolateCubic.h"
|
||||||
|
#include "InterpolateShannon.h"
|
||||||
#include "AAFilter.h"
|
#include "AAFilter.h"
|
||||||
|
|
||||||
using namespace soundtouch;
|
using namespace soundtouch;
|
||||||
|
|
||||||
|
// Define default interpolation algorithm here
|
||||||
/// A linear samplerate transposer class that uses integer arithmetics.
|
TransposerBase::ALGORITHM TransposerBase::algorithm = TransposerBase::CUBIC;
|
||||||
/// for the transposing.
|
|
||||||
class RateTransposerInteger : public RateTransposer
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
int iSlopeCount;
|
|
||||||
int iRate;
|
|
||||||
SAMPLETYPE *sPrevSample;
|
|
||||||
|
|
||||||
virtual void resetRegisters();
|
|
||||||
|
|
||||||
virtual int transposeStereo(SAMPLETYPE *dest,
|
|
||||||
const SAMPLETYPE *src,
|
|
||||||
uint numSamples);
|
|
||||||
virtual int transposeMono(SAMPLETYPE *dest,
|
|
||||||
const SAMPLETYPE *src,
|
|
||||||
uint numSamples);
|
|
||||||
virtual int transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples);
|
|
||||||
public:
|
|
||||||
RateTransposerInteger();
|
|
||||||
virtual ~RateTransposerInteger();
|
|
||||||
|
|
||||||
/// Sets new target rate. Normal rate = 1.0, smaller values represent slower
|
|
||||||
/// rate, larger faster rates.
|
|
||||||
virtual void setRate(float newRate);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/// A linear samplerate transposer class that uses floating point arithmetics
|
|
||||||
/// for the transposing.
|
|
||||||
class RateTransposerFloat : public RateTransposer
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
float fSlopeCount;
|
|
||||||
SAMPLETYPE *sPrevSample;
|
|
||||||
|
|
||||||
virtual void resetRegisters();
|
|
||||||
|
|
||||||
virtual int transposeStereo(SAMPLETYPE *dest,
|
|
||||||
const SAMPLETYPE *src,
|
|
||||||
uint numSamples);
|
|
||||||
virtual int transposeMono(SAMPLETYPE *dest,
|
|
||||||
const SAMPLETYPE *src,
|
|
||||||
uint numSamples);
|
|
||||||
virtual int transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples);
|
|
||||||
|
|
||||||
public:
|
|
||||||
RateTransposerFloat();
|
|
||||||
virtual ~RateTransposerFloat();
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
|
||||||
// depending on if we've a MMX/SSE/etc-capable CPU available or not.
|
|
||||||
void * RateTransposer::operator new(size_t s)
|
|
||||||
{
|
|
||||||
ST_THROW_RT_ERROR("Error in RateTransoser::new: don't use \"new TDStretch\" directly, use \"newInstance\" to create a new instance instead!");
|
|
||||||
return newInstance();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
RateTransposer *RateTransposer::newInstance()
|
|
||||||
{
|
|
||||||
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
|
||||||
return ::new RateTransposerInteger;
|
|
||||||
#else
|
|
||||||
return ::new RateTransposerFloat;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
RateTransposer::RateTransposer() : FIFOProcessor(&outputBuffer)
|
RateTransposer::RateTransposer() : FIFOProcessor(&outputBuffer)
|
||||||
{
|
{
|
||||||
numChannels = 2;
|
bUseAAFilter = true;
|
||||||
bUseAAFilter = TRUE;
|
|
||||||
fRate = 0;
|
|
||||||
|
|
||||||
// Instantiates the anti-alias filter with default tap length
|
// Instantiates the anti-alias filter
|
||||||
// of 32
|
pAAFilter = new AAFilter(64);
|
||||||
pAAFilter = new AAFilter(32);
|
pTransposer = TransposerBase::newInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -139,19 +69,20 @@ RateTransposer::RateTransposer() : FIFOProcessor(&outputBuffer)
|
||||||
RateTransposer::~RateTransposer()
|
RateTransposer::~RateTransposer()
|
||||||
{
|
{
|
||||||
delete pAAFilter;
|
delete pAAFilter;
|
||||||
|
delete pTransposer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// Enables/disables the anti-alias filter. Zero to disable, nonzero to enable
|
/// Enables/disables the anti-alias filter. Zero to disable, nonzero to enable
|
||||||
void RateTransposer::enableAAFilter(BOOL newMode)
|
void RateTransposer::enableAAFilter(bool newMode)
|
||||||
{
|
{
|
||||||
bUseAAFilter = newMode;
|
bUseAAFilter = newMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Returns nonzero if anti-alias filter is enabled.
|
/// Returns nonzero if anti-alias filter is enabled.
|
||||||
BOOL RateTransposer::isAAFilterEnabled() const
|
bool RateTransposer::isAAFilterEnabled() const
|
||||||
{
|
{
|
||||||
return bUseAAFilter;
|
return bUseAAFilter;
|
||||||
}
|
}
|
||||||
|
@ -170,7 +101,7 @@ void RateTransposer::setRate(float newRate)
|
||||||
{
|
{
|
||||||
double fCutoff;
|
double fCutoff;
|
||||||
|
|
||||||
fRate = newRate;
|
pTransposer->setRate(newRate);
|
||||||
|
|
||||||
// design a new anti-alias filter
|
// design a new anti-alias filter
|
||||||
if (newRate > 1.0f)
|
if (newRate > 1.0f)
|
||||||
|
@ -185,22 +116,6 @@ void RateTransposer::setRate(float newRate)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Outputs as many samples of the 'outputBuffer' as possible, and if there's
|
|
||||||
// any room left, outputs also as many of the incoming samples as possible.
|
|
||||||
// The goal is to drive the outputBuffer empty.
|
|
||||||
//
|
|
||||||
// It's allowed for 'output' and 'input' parameters to point to the same
|
|
||||||
// memory position.
|
|
||||||
/*
|
|
||||||
void RateTransposer::flushStoreBuffer()
|
|
||||||
{
|
|
||||||
if (storeBuffer.isEmpty()) return;
|
|
||||||
|
|
||||||
outputBuffer.moveSamples(storeBuffer);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
// Adds 'nSamples' pcs of samples from the 'samples' memory position into
|
// Adds 'nSamples' pcs of samples from the 'samples' memory position into
|
||||||
// the input of the object.
|
// the input of the object.
|
||||||
void RateTransposer::putSamples(const SAMPLETYPE *samples, uint nSamples)
|
void RateTransposer::putSamples(const SAMPLETYPE *samples, uint nSamples)
|
||||||
|
@ -209,70 +124,6 @@ void RateTransposer::putSamples(const SAMPLETYPE *samples, uint nSamples)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Transposes up the sample rate, causing the observed playback 'rate' of the
|
|
||||||
// sound to decrease
|
|
||||||
void RateTransposer::upsample(const SAMPLETYPE *src, uint nSamples)
|
|
||||||
{
|
|
||||||
uint count, sizeTemp, num;
|
|
||||||
|
|
||||||
// If the parameter 'uRate' value is smaller than 'SCALE', first transpose
|
|
||||||
// the samples and then apply the anti-alias filter to remove aliasing.
|
|
||||||
|
|
||||||
// First check that there's enough room in 'storeBuffer'
|
|
||||||
// (+16 is to reserve some slack in the destination buffer)
|
|
||||||
sizeTemp = (uint)((float)nSamples / fRate + 16.0f);
|
|
||||||
|
|
||||||
// Transpose the samples, store the result into the end of "storeBuffer"
|
|
||||||
count = transpose(storeBuffer.ptrEnd(sizeTemp), src, nSamples);
|
|
||||||
storeBuffer.putSamples(count);
|
|
||||||
|
|
||||||
// Apply the anti-alias filter to samples in "store output", output the
|
|
||||||
// result to "dest"
|
|
||||||
num = storeBuffer.numSamples();
|
|
||||||
count = pAAFilter->evaluate(outputBuffer.ptrEnd(num),
|
|
||||||
storeBuffer.ptrBegin(), num, (uint)numChannels);
|
|
||||||
outputBuffer.putSamples(count);
|
|
||||||
|
|
||||||
// Remove the processed samples from "storeBuffer"
|
|
||||||
storeBuffer.receiveSamples(count);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Transposes down the sample rate, causing the observed playback 'rate' of the
|
|
||||||
// sound to increase
|
|
||||||
void RateTransposer::downsample(const SAMPLETYPE *src, uint nSamples)
|
|
||||||
{
|
|
||||||
uint count, sizeTemp;
|
|
||||||
|
|
||||||
// If the parameter 'uRate' value is larger than 'SCALE', first apply the
|
|
||||||
// anti-alias filter to remove high frequencies (prevent them from folding
|
|
||||||
// over the lover frequencies), then transpose.
|
|
||||||
|
|
||||||
// Add the new samples to the end of the storeBuffer
|
|
||||||
storeBuffer.putSamples(src, nSamples);
|
|
||||||
|
|
||||||
// Anti-alias filter the samples to prevent folding and output the filtered
|
|
||||||
// data to tempBuffer. Note : because of the FIR filter length, the
|
|
||||||
// filtering routine takes in 'filter_length' more samples than it outputs.
|
|
||||||
assert(tempBuffer.isEmpty());
|
|
||||||
sizeTemp = storeBuffer.numSamples();
|
|
||||||
|
|
||||||
count = pAAFilter->evaluate(tempBuffer.ptrEnd(sizeTemp),
|
|
||||||
storeBuffer.ptrBegin(), sizeTemp, (uint)numChannels);
|
|
||||||
|
|
||||||
if (count == 0) return;
|
|
||||||
|
|
||||||
// Remove the filtered samples from 'storeBuffer'
|
|
||||||
storeBuffer.receiveSamples(count);
|
|
||||||
|
|
||||||
// Transpose the samples (+16 is to reserve some slack in the destination buffer)
|
|
||||||
sizeTemp = (uint)((float)nSamples / fRate + 16.0f);
|
|
||||||
count = transpose(outputBuffer.ptrEnd(sizeTemp), tempBuffer.ptrBegin(), count);
|
|
||||||
outputBuffer.putSamples(count);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Transposes sample rate by applying anti-alias filter to prevent folding.
|
// Transposes sample rate by applying anti-alias filter to prevent folding.
|
||||||
// Returns amount of samples returned in the "dest" buffer.
|
// Returns amount of samples returned in the "dest" buffer.
|
||||||
// The maximum amount of samples that can be returned at a time is set by
|
// The maximum amount of samples that can be returned at a time is set by
|
||||||
|
@ -280,51 +131,45 @@ void RateTransposer::downsample(const SAMPLETYPE *src, uint nSamples)
|
||||||
void RateTransposer::processSamples(const SAMPLETYPE *src, uint nSamples)
|
void RateTransposer::processSamples(const SAMPLETYPE *src, uint nSamples)
|
||||||
{
|
{
|
||||||
uint count;
|
uint count;
|
||||||
uint sizeReq;
|
|
||||||
|
|
||||||
if (nSamples == 0) return;
|
if (nSamples == 0) return;
|
||||||
assert(pAAFilter);
|
|
||||||
|
// Store samples to input buffer
|
||||||
|
inputBuffer.putSamples(src, nSamples);
|
||||||
|
|
||||||
// If anti-alias filter is turned off, simply transpose without applying
|
// If anti-alias filter is turned off, simply transpose without applying
|
||||||
// the filter
|
// the filter
|
||||||
if (bUseAAFilter == FALSE)
|
if (bUseAAFilter == false)
|
||||||
{
|
{
|
||||||
sizeReq = (uint)((float)nSamples / fRate + 1.0f);
|
count = pTransposer->transpose(outputBuffer, inputBuffer);
|
||||||
count = transpose(outputBuffer.ptrEnd(sizeReq), src, nSamples);
|
|
||||||
outputBuffer.putSamples(count);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(pAAFilter);
|
||||||
|
|
||||||
// Transpose with anti-alias filter
|
// Transpose with anti-alias filter
|
||||||
if (fRate < 1.0f)
|
if (pTransposer->rate < 1.0f)
|
||||||
{
|
{
|
||||||
upsample(src, nSamples);
|
// If the parameter 'Rate' value is smaller than 1, first transpose
|
||||||
|
// the samples and then apply the anti-alias filter to remove aliasing.
|
||||||
|
|
||||||
|
// Transpose the samples, store the result to end of "midBuffer"
|
||||||
|
pTransposer->transpose(midBuffer, inputBuffer);
|
||||||
|
|
||||||
|
// Apply the anti-alias filter for transposed samples in midBuffer
|
||||||
|
pAAFilter->evaluate(outputBuffer, midBuffer);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
downsample(src, nSamples);
|
// If the parameter 'Rate' value is larger than 1, first apply the
|
||||||
}
|
// anti-alias filter to remove high frequencies (prevent them from folding
|
||||||
}
|
// over the lover frequencies), then transpose.
|
||||||
|
|
||||||
|
// Apply the anti-alias filter for samples in inputBuffer
|
||||||
|
pAAFilter->evaluate(midBuffer, inputBuffer);
|
||||||
|
|
||||||
// Transposes the sample rate of the given samples using linear interpolation.
|
// Transpose the AA-filtered samples in "midBuffer"
|
||||||
// Returns the number of samples returned in the "dest" buffer
|
pTransposer->transpose(outputBuffer, midBuffer);
|
||||||
inline int RateTransposer::transpose(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples)
|
|
||||||
{
|
|
||||||
#ifndef USE_MULTICH_ALWAYS
|
|
||||||
if (numChannels == 1)
|
|
||||||
{
|
|
||||||
return transposeMono(dest, src, nSamples);
|
|
||||||
}
|
|
||||||
else if (numChannels == 2)
|
|
||||||
{
|
|
||||||
return transposeStereo(dest, src, nSamples);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
#endif // USE_MULTICH_ALWAYS
|
|
||||||
{
|
|
||||||
assert(numChannels > 0);
|
|
||||||
return transposeMulti(dest, src, nSamples);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,17 +178,13 @@ inline int RateTransposer::transpose(SAMPLETYPE *dest, const SAMPLETYPE *src, ui
|
||||||
void RateTransposer::setChannels(int nChannels)
|
void RateTransposer::setChannels(int nChannels)
|
||||||
{
|
{
|
||||||
assert(nChannels > 0);
|
assert(nChannels > 0);
|
||||||
if (numChannels == nChannels) return;
|
|
||||||
|
|
||||||
// assert(nChannels == 1 || nChannels == 2);
|
if (pTransposer->numChannels == nChannels) return;
|
||||||
numChannels = nChannels;
|
pTransposer->setChannels(nChannels);
|
||||||
|
|
||||||
storeBuffer.setChannels(numChannels);
|
inputBuffer.setChannels(nChannels);
|
||||||
tempBuffer.setChannels(numChannels);
|
midBuffer.setChannels(nChannels);
|
||||||
outputBuffer.setChannels(numChannels);
|
outputBuffer.setChannels(nChannels);
|
||||||
|
|
||||||
// Inits the linear interpolation registers
|
|
||||||
resetRegisters();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -351,7 +192,8 @@ void RateTransposer::setChannels(int nChannels)
|
||||||
void RateTransposer::clear()
|
void RateTransposer::clear()
|
||||||
{
|
{
|
||||||
outputBuffer.clear();
|
outputBuffer.clear();
|
||||||
storeBuffer.clear();
|
midBuffer.clear();
|
||||||
|
inputBuffer.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -362,387 +204,99 @@ int RateTransposer::isEmpty() const
|
||||||
|
|
||||||
res = FIFOProcessor::isEmpty();
|
res = FIFOProcessor::isEmpty();
|
||||||
if (res == 0) return 0;
|
if (res == 0) return 0;
|
||||||
return storeBuffer.isEmpty();
|
return inputBuffer.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// RateTransposerInteger - integer arithmetic implementation
|
// TransposerBase - Base class for interpolation
|
||||||
//
|
//
|
||||||
|
|
||||||
/// fixed-point interpolation routine precision
|
// static function to set interpolation algorithm
|
||||||
#define SCALE 65536
|
void TransposerBase::setAlgorithm(TransposerBase::ALGORITHM a)
|
||||||
|
|
||||||
// Constructor
|
|
||||||
RateTransposerInteger::RateTransposerInteger() : RateTransposer()
|
|
||||||
{
|
{
|
||||||
// Notice: use local function calling syntax for sake of clarity,
|
TransposerBase::algorithm = a;
|
||||||
// to indicate the fact that C++ constructor can't call virtual functions.
|
|
||||||
sPrevSample=0;
|
|
||||||
RateTransposerInteger::resetRegisters();
|
|
||||||
RateTransposerInteger::setRate(1.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
RateTransposerInteger::~RateTransposerInteger()
|
|
||||||
{
|
|
||||||
if (sPrevSample) delete[] sPrevSample;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void RateTransposerInteger::resetRegisters()
|
|
||||||
{
|
|
||||||
iSlopeCount = 0;
|
|
||||||
delete[] sPrevSample;
|
|
||||||
sPrevSample = new SAMPLETYPE[numChannels];
|
|
||||||
memset(sPrevSample, 0, numChannels * sizeof(SAMPLETYPE));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Transposes the sample rate of the given samples using linear interpolation.
|
|
||||||
// 'Mono' version of the routine. Returns the number of samples returned in
|
|
||||||
// the "dest" buffer
|
|
||||||
int RateTransposerInteger::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples)
|
|
||||||
{
|
|
||||||
int i, remain;
|
|
||||||
LONG_SAMPLETYPE temp, vol1;
|
|
||||||
|
|
||||||
if (nSamples == 0) return 0; // no samples, no work
|
|
||||||
|
|
||||||
remain = nSamples - 1;
|
|
||||||
i = 0;
|
|
||||||
|
|
||||||
// Process the last sample saved from the previous call first...
|
|
||||||
while (iSlopeCount <= SCALE)
|
|
||||||
{
|
|
||||||
vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount);
|
|
||||||
temp = vol1 * sPrevSample[0] + iSlopeCount * src[0];
|
|
||||||
dest[i] = (SAMPLETYPE)(temp / SCALE);
|
|
||||||
i++;
|
|
||||||
iSlopeCount += iRate;
|
|
||||||
}
|
|
||||||
// now always (iSlopeCount > SCALE)
|
|
||||||
iSlopeCount -= SCALE;
|
|
||||||
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
while (iSlopeCount > SCALE)
|
|
||||||
{
|
|
||||||
iSlopeCount -= SCALE;
|
|
||||||
src ++;
|
|
||||||
remain --;
|
|
||||||
if (remain == 0) goto end;
|
|
||||||
}
|
|
||||||
vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount);
|
|
||||||
temp = src[0] * vol1 + iSlopeCount * src[1];
|
|
||||||
dest[i] = (SAMPLETYPE)(temp / SCALE);
|
|
||||||
|
|
||||||
i++;
|
|
||||||
iSlopeCount += iRate;
|
|
||||||
}
|
|
||||||
end:
|
|
||||||
// Store the last sample for the next round
|
|
||||||
sPrevSample[0] = src[0];
|
|
||||||
|
|
||||||
return i;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Transposes the sample rate of the given samples using linear interpolation.
|
// Transposes the sample rate of the given samples using linear interpolation.
|
||||||
// 'Stereo' version of the routine. Returns the number of samples returned in
|
// Returns the number of samples returned in the "dest" buffer
|
||||||
// the "dest" buffer
|
int TransposerBase::transpose(FIFOSampleBuffer &dest, FIFOSampleBuffer &src)
|
||||||
int RateTransposerInteger::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples)
|
|
||||||
{
|
{
|
||||||
int i, remain;
|
int numSrcSamples = src.numSamples();
|
||||||
LONG_SAMPLETYPE temp, vol1;
|
int sizeDemand = (int)((float)numSrcSamples / rate) + 8;
|
||||||
|
int numOutput;
|
||||||
|
SAMPLETYPE *psrc = src.ptrBegin();
|
||||||
|
SAMPLETYPE *pdest = dest.ptrEnd(sizeDemand);
|
||||||
|
|
||||||
if (nSamples == 0) return 0; // no samples, no work
|
#ifndef USE_MULTICH_ALWAYS
|
||||||
|
if (numChannels == 1)
|
||||||
remain = nSamples - 1;
|
|
||||||
i = 0;
|
|
||||||
|
|
||||||
// Process the last sample saved from the sPrevSampleLious call first...
|
|
||||||
while (iSlopeCount <= SCALE)
|
|
||||||
{
|
{
|
||||||
vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount);
|
numOutput = transposeMono(pdest, psrc, numSrcSamples);
|
||||||
temp = vol1 * sPrevSample[0] + iSlopeCount * src[0];
|
|
||||||
dest[2 * i] = (SAMPLETYPE)(temp / SCALE);
|
|
||||||
temp = vol1 * sPrevSample[1] + iSlopeCount * src[1];
|
|
||||||
dest[2 * i + 1] = (SAMPLETYPE)(temp / SCALE);
|
|
||||||
i++;
|
|
||||||
iSlopeCount += iRate;
|
|
||||||
}
|
}
|
||||||
// now always (iSlopeCount > SCALE)
|
else if (numChannels == 2)
|
||||||
iSlopeCount -= SCALE;
|
|
||||||
|
|
||||||
while (1)
|
|
||||||
{
|
{
|
||||||
while (iSlopeCount > SCALE)
|
numOutput = transposeStereo(pdest, psrc, numSrcSamples);
|
||||||
{
|
}
|
||||||
iSlopeCount -= SCALE;
|
else
|
||||||
remain --;
|
#endif // USE_MULTICH_ALWAYS
|
||||||
src += 2;
|
{
|
||||||
if (remain == 0) goto end;
|
assert(numChannels > 0);
|
||||||
}
|
numOutput = transposeMulti(pdest, psrc, numSrcSamples);
|
||||||
vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount);
|
|
||||||
temp = src[0] * vol1 + iSlopeCount * src[2];
|
|
||||||
dest[2 * i] = (SAMPLETYPE)(temp / SCALE);
|
|
||||||
temp = src[1] * vol1 + iSlopeCount * src[3];
|
|
||||||
dest[2 * i + 1] = (SAMPLETYPE)(temp / SCALE);
|
|
||||||
|
|
||||||
i++;
|
|
||||||
iSlopeCount += iRate;
|
|
||||||
}
|
}
|
||||||
end:
|
dest.putSamples(numOutput);
|
||||||
// Store the last sample for the next round
|
src.receiveSamples(numSrcSamples);
|
||||||
sPrevSample[0] = src[0];
|
return numOutput;
|
||||||
sPrevSample[1] = src[1];
|
|
||||||
|
|
||||||
return i;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int RateTransposerInteger::transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples)
|
TransposerBase::TransposerBase()
|
||||||
{
|
{
|
||||||
int i, remaining;
|
numChannels = 0;
|
||||||
LONG_SAMPLETYPE temp, vol1;
|
rate = 1.0f;
|
||||||
|
|
||||||
if (nSamples == 0) return 0; // no samples, no work
|
|
||||||
|
|
||||||
remaining = nSamples - 1;
|
|
||||||
i = 0;
|
|
||||||
|
|
||||||
// Process the last sample saved from the sPrevSampleLious call first...
|
|
||||||
while (iSlopeCount <= SCALE)
|
|
||||||
{
|
|
||||||
for (int c = 0; c < numChannels; c ++)
|
|
||||||
{
|
|
||||||
vol1 = (SCALE - iSlopeCount);
|
|
||||||
temp = vol1 * sPrevSample[c] + iSlopeCount * src[c];
|
|
||||||
*dest = (SAMPLETYPE)(temp / SCALE);
|
|
||||||
dest ++;
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
|
|
||||||
iSlopeCount += iRate;
|
|
||||||
}
|
|
||||||
// now always (iSlopeCount > SCALE)
|
|
||||||
iSlopeCount -= SCALE;
|
|
||||||
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
while (iSlopeCount > SCALE)
|
|
||||||
{
|
|
||||||
iSlopeCount -= SCALE;
|
|
||||||
src += numChannels;
|
|
||||||
remaining --;
|
|
||||||
if (remaining == 0) goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int c = 0; c < numChannels; c ++)
|
|
||||||
{
|
|
||||||
vol1 = (SCALE - iSlopeCount);
|
|
||||||
temp = src[c] * vol1 + iSlopeCount * src[c + numChannels];
|
|
||||||
*dest = (SAMPLETYPE)(temp / SCALE);
|
|
||||||
dest++;
|
|
||||||
}
|
|
||||||
|
|
||||||
i++;
|
|
||||||
iSlopeCount += iRate;
|
|
||||||
}
|
|
||||||
end:
|
|
||||||
// Store the last sample for the next round
|
|
||||||
memcpy(sPrevSample, src, numChannels * sizeof(SAMPLETYPE));
|
|
||||||
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sets new target iRate. Normal iRate = 1.0, smaller values represent slower
|
|
||||||
// iRate, larger faster iRates.
|
|
||||||
void RateTransposerInteger::setRate(float newRate)
|
|
||||||
{
|
|
||||||
iRate = (int)(newRate * SCALE + 0.5f);
|
|
||||||
RateTransposer::setRate(newRate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
TransposerBase::~TransposerBase()
|
||||||
//
|
|
||||||
// RateTransposerFloat - floating point arithmetic implementation
|
|
||||||
//
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// Constructor
|
|
||||||
RateTransposerFloat::RateTransposerFloat() : RateTransposer()
|
|
||||||
{
|
{
|
||||||
// Notice: use local function calling syntax for sake of clarity,
|
|
||||||
// to indicate the fact that C++ constructor can't call virtual functions.
|
|
||||||
sPrevSample = NULL;
|
|
||||||
RateTransposerFloat::resetRegisters();
|
|
||||||
RateTransposerFloat::setRate(1.0f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
RateTransposerFloat::~RateTransposerFloat()
|
void TransposerBase::setChannels(int channels)
|
||||||
{
|
{
|
||||||
delete[] sPrevSample;
|
numChannels = channels;
|
||||||
|
resetRegisters();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void RateTransposerFloat::resetRegisters()
|
void TransposerBase::setRate(float newRate)
|
||||||
{
|
{
|
||||||
fSlopeCount = 0;
|
rate = newRate;
|
||||||
delete[] sPrevSample;
|
|
||||||
sPrevSample = new SAMPLETYPE[numChannels];
|
|
||||||
memset(sPrevSample, 0, numChannels * sizeof(SAMPLETYPE));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// static factory function
|
||||||
// Transposes the sample rate of the given samples using linear interpolation.
|
TransposerBase *TransposerBase::newInstance()
|
||||||
// 'Mono' version of the routine. Returns the number of samples returned in
|
|
||||||
// the "dest" buffer
|
|
||||||
int RateTransposerFloat::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples)
|
|
||||||
{
|
{
|
||||||
int i, remain;
|
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
|
// Notice: For integer arithmetics support only linear algorithm (due to simplest calculus)
|
||||||
remain = 0;
|
return ::new InterpolateLinearInteger;
|
||||||
i = 0;
|
#else
|
||||||
|
switch (algorithm)
|
||||||
// Process the last sample saved from the previous call first...
|
|
||||||
while (fSlopeCount <= 1.0f)
|
|
||||||
{
|
{
|
||||||
dest[i] = (SAMPLETYPE)((1.0f - fSlopeCount) * sPrevSample[0] + fSlopeCount * src[0]);
|
case LINEAR:
|
||||||
i++;
|
return new InterpolateLinearFloat;
|
||||||
fSlopeCount += fRate;
|
|
||||||
}
|
|
||||||
fSlopeCount -= 1.0f;
|
|
||||||
|
|
||||||
if (nSamples > 1)
|
case CUBIC:
|
||||||
{
|
return new InterpolateCubic;
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
while (fSlopeCount > 1.0f)
|
|
||||||
{
|
|
||||||
fSlopeCount -= 1.0f;
|
|
||||||
src ++;
|
|
||||||
remain --;
|
|
||||||
if (remain == 0) goto end;
|
|
||||||
}
|
|
||||||
dest[i] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[0] + fSlopeCount * src[1]);
|
|
||||||
i++;
|
|
||||||
fSlopeCount += fRate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end:
|
|
||||||
// Store the last sample for the next round
|
|
||||||
sPrevSample[0] = src[0];
|
|
||||||
|
|
||||||
return i;
|
case SHANNON:
|
||||||
}
|
return new InterpolateShannon;
|
||||||
|
|
||||||
|
default:
|
||||||
// Transposes the sample rate of the given samples using linear interpolation.
|
assert(false);
|
||||||
// 'Mono' version of the routine. Returns the number of samples returned in
|
return NULL;
|
||||||
// the "dest" buffer
|
}
|
||||||
int RateTransposerFloat::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples)
|
#endif
|
||||||
{
|
|
||||||
int i, remain;
|
|
||||||
|
|
||||||
if (nSamples == 0) return 0; // no samples, no work
|
|
||||||
|
|
||||||
remain = nSamples - 1;
|
|
||||||
i = 0;
|
|
||||||
|
|
||||||
// Process the last sample saved from the sPrevSampleLious call first...
|
|
||||||
while (fSlopeCount <= 1.0f)
|
|
||||||
{
|
|
||||||
dest[2 * i] = (SAMPLETYPE)((1.0f - fSlopeCount) * sPrevSample[0] + fSlopeCount * src[0]);
|
|
||||||
dest[2 * i + 1] = (SAMPLETYPE)((1.0f - fSlopeCount) * sPrevSample[1] + fSlopeCount * src[1]);
|
|
||||||
i++;
|
|
||||||
fSlopeCount += fRate;
|
|
||||||
}
|
|
||||||
// now always (iSlopeCount > 1.0f)
|
|
||||||
fSlopeCount -= 1.0f;
|
|
||||||
|
|
||||||
if (nSamples > 1)
|
|
||||||
{
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
while (fSlopeCount > 1.0f)
|
|
||||||
{
|
|
||||||
fSlopeCount -= 1.0f;
|
|
||||||
remain --;
|
|
||||||
src += 2;
|
|
||||||
if (remain == 0) goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
dest[2 * i] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[0]
|
|
||||||
+ fSlopeCount * src[2]);
|
|
||||||
dest[2 * i + 1] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[1]
|
|
||||||
+ fSlopeCount * src[3]);
|
|
||||||
|
|
||||||
i++;
|
|
||||||
fSlopeCount += fRate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end:
|
|
||||||
// Store the last sample for the next round
|
|
||||||
sPrevSample[0] = src[0];
|
|
||||||
sPrevSample[1] = src[1];
|
|
||||||
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
int RateTransposerFloat::transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples)
|
|
||||||
{
|
|
||||||
int i, remaining;
|
|
||||||
|
|
||||||
if (nSamples == 0) return 0; // no samples, no work
|
|
||||||
|
|
||||||
remaining = nSamples - 1;
|
|
||||||
i = 0;
|
|
||||||
|
|
||||||
// Process the last sample saved from the sPrevSampleLious call first...
|
|
||||||
while (fSlopeCount <= 1.0f)
|
|
||||||
{
|
|
||||||
for (int c = 0; c < numChannels; c ++)
|
|
||||||
{
|
|
||||||
*dest = (SAMPLETYPE)((1.0f - fSlopeCount) * sPrevSample[c] + fSlopeCount * src[c]);
|
|
||||||
dest ++;
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
fSlopeCount += fRate;
|
|
||||||
}
|
|
||||||
// now always (iSlopeCount > 1.0f)
|
|
||||||
fSlopeCount -= 1.0f;
|
|
||||||
|
|
||||||
while (remaining > 0)
|
|
||||||
{
|
|
||||||
while (fSlopeCount > 1.0f)
|
|
||||||
{
|
|
||||||
fSlopeCount -= 1.0f;
|
|
||||||
src += numChannels;
|
|
||||||
remaining --;
|
|
||||||
if (remaining == 0) goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int c = 0; c < numChannels; c ++)
|
|
||||||
{
|
|
||||||
*dest = (SAMPLETYPE)((1.0f - fSlopeCount) * src[c]
|
|
||||||
+ fSlopeCount * src[c + numChannels]);
|
|
||||||
dest++;
|
|
||||||
}
|
|
||||||
|
|
||||||
i++;
|
|
||||||
fSlopeCount += fRate;
|
|
||||||
}
|
|
||||||
|
|
||||||
end:
|
|
||||||
// Store the last sample for the next round
|
|
||||||
memcpy(sPrevSample, src, numChannels * sizeof(SAMPLETYPE));
|
|
||||||
|
|
||||||
return i;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,10 +14,10 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2013-06-12 15:24:44 +0000 (Wed, 12 Jun 2013) $
|
// Last changed : $Date: 2014-04-07 01:57:21 +1000 (Mon, 07 Apr 2014) $
|
||||||
// File revision : $Revision: 4 $
|
// File revision : $Revision: 4 $
|
||||||
//
|
//
|
||||||
// $Id: RateTransposer.h 171 2013-06-12 15:24:44Z oparviai $
|
// $Id: RateTransposer.h 195 2014-04-06 15:57:21Z oparviai $
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
|
@ -55,51 +55,71 @@
|
||||||
namespace soundtouch
|
namespace soundtouch
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/// Abstract base class for transposer implementations (linear, advanced vs integer, float etc)
|
||||||
|
class TransposerBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum ALGORITHM {
|
||||||
|
LINEAR = 0,
|
||||||
|
CUBIC,
|
||||||
|
SHANNON
|
||||||
|
};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void resetRegisters() = 0;
|
||||||
|
|
||||||
|
virtual int transposeMono(SAMPLETYPE *dest,
|
||||||
|
const SAMPLETYPE *src,
|
||||||
|
int &srcSamples) = 0;
|
||||||
|
virtual int transposeStereo(SAMPLETYPE *dest,
|
||||||
|
const SAMPLETYPE *src,
|
||||||
|
int &srcSamples) = 0;
|
||||||
|
virtual int transposeMulti(SAMPLETYPE *dest,
|
||||||
|
const SAMPLETYPE *src,
|
||||||
|
int &srcSamples) = 0;
|
||||||
|
|
||||||
|
static ALGORITHM algorithm;
|
||||||
|
|
||||||
|
public:
|
||||||
|
float rate;
|
||||||
|
int numChannels;
|
||||||
|
|
||||||
|
TransposerBase();
|
||||||
|
virtual ~TransposerBase();
|
||||||
|
|
||||||
|
virtual int transpose(FIFOSampleBuffer &dest, FIFOSampleBuffer &src);
|
||||||
|
virtual void setRate(float newRate);
|
||||||
|
virtual void setChannels(int channels);
|
||||||
|
|
||||||
|
// static factory function
|
||||||
|
static TransposerBase *newInstance();
|
||||||
|
|
||||||
|
// static function to set interpolation algorithm
|
||||||
|
static void setAlgorithm(ALGORITHM a);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/// A common linear samplerate transposer class.
|
/// A common linear samplerate transposer class.
|
||||||
///
|
///
|
||||||
/// Note: Use function "RateTransposer::newInstance()" to create a new class
|
|
||||||
/// instance instead of the "new" operator; that function automatically
|
|
||||||
/// chooses a correct implementation depending on if integer or floating
|
|
||||||
/// arithmetics are to be used.
|
|
||||||
class RateTransposer : public FIFOProcessor
|
class RateTransposer : public FIFOProcessor
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
/// Anti-alias filter object
|
/// Anti-alias filter object
|
||||||
AAFilter *pAAFilter;
|
AAFilter *pAAFilter;
|
||||||
|
TransposerBase *pTransposer;
|
||||||
float fRate;
|
|
||||||
|
|
||||||
int numChannels;
|
|
||||||
|
|
||||||
/// Buffer for collecting samples to feed the anti-alias filter between
|
/// Buffer for collecting samples to feed the anti-alias filter between
|
||||||
/// two batches
|
/// two batches
|
||||||
FIFOSampleBuffer storeBuffer;
|
FIFOSampleBuffer inputBuffer;
|
||||||
|
|
||||||
/// Buffer for keeping samples between transposing & anti-alias filter
|
/// Buffer for keeping samples between transposing & anti-alias filter
|
||||||
FIFOSampleBuffer tempBuffer;
|
FIFOSampleBuffer midBuffer;
|
||||||
|
|
||||||
/// Output sample buffer
|
/// Output sample buffer
|
||||||
FIFOSampleBuffer outputBuffer;
|
FIFOSampleBuffer outputBuffer;
|
||||||
|
|
||||||
BOOL bUseAAFilter;
|
bool bUseAAFilter;
|
||||||
|
|
||||||
virtual void resetRegisters() = 0;
|
|
||||||
|
|
||||||
virtual int transposeStereo(SAMPLETYPE *dest,
|
|
||||||
const SAMPLETYPE *src,
|
|
||||||
uint numSamples) = 0;
|
|
||||||
virtual int transposeMono(SAMPLETYPE *dest,
|
|
||||||
const SAMPLETYPE *src,
|
|
||||||
uint numSamples) = 0;
|
|
||||||
virtual int transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples) = 0;
|
|
||||||
inline int transpose(SAMPLETYPE *dest,
|
|
||||||
const SAMPLETYPE *src,
|
|
||||||
uint numSamples);
|
|
||||||
|
|
||||||
void downsample(const SAMPLETYPE *src,
|
|
||||||
uint numSamples);
|
|
||||||
void upsample(const SAMPLETYPE *src,
|
|
||||||
uint numSamples);
|
|
||||||
|
|
||||||
/// Transposes sample rate by applying anti-alias filter to prevent folding.
|
/// Transposes sample rate by applying anti-alias filter to prevent folding.
|
||||||
/// Returns amount of samples returned in the "dest" buffer.
|
/// Returns amount of samples returned in the "dest" buffer.
|
||||||
|
@ -108,34 +128,33 @@ protected:
|
||||||
void processSamples(const SAMPLETYPE *src,
|
void processSamples(const SAMPLETYPE *src,
|
||||||
uint numSamples);
|
uint numSamples);
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RateTransposer();
|
RateTransposer();
|
||||||
virtual ~RateTransposer();
|
virtual ~RateTransposer();
|
||||||
|
|
||||||
/// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
/// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
||||||
/// depending on if we're to use integer or floating point arithmetics.
|
/// depending on if we're to use integer or floating point arithmetics.
|
||||||
static void *operator new(size_t s);
|
// static void *operator new(size_t s);
|
||||||
|
|
||||||
/// Use this function instead of "new" operator to create a new instance of this class.
|
/// Use this function instead of "new" operator to create a new instance of this class.
|
||||||
/// This function automatically chooses a correct implementation, depending on if
|
/// This function automatically chooses a correct implementation, depending on if
|
||||||
/// integer ot floating point arithmetics are to be used.
|
/// integer ot floating point arithmetics are to be used.
|
||||||
static RateTransposer *newInstance();
|
// static RateTransposer *newInstance();
|
||||||
|
|
||||||
/// Returns the output buffer object
|
/// Returns the output buffer object
|
||||||
FIFOSamplePipe *getOutput() { return &outputBuffer; };
|
FIFOSamplePipe *getOutput() { return &outputBuffer; };
|
||||||
|
|
||||||
/// Returns the store buffer object
|
/// Returns the store buffer object
|
||||||
FIFOSamplePipe *getStore() { return &storeBuffer; };
|
// FIFOSamplePipe *getStore() { return &storeBuffer; };
|
||||||
|
|
||||||
/// Return anti-alias filter object
|
/// Return anti-alias filter object
|
||||||
AAFilter *getAAFilter();
|
AAFilter *getAAFilter();
|
||||||
|
|
||||||
/// Enables/disables the anti-alias filter. Zero to disable, nonzero to enable
|
/// Enables/disables the anti-alias filter. Zero to disable, nonzero to enable
|
||||||
void enableAAFilter(BOOL newMode);
|
void enableAAFilter(bool newMode);
|
||||||
|
|
||||||
/// Returns nonzero if anti-alias filter is enabled.
|
/// Returns nonzero if anti-alias filter is enabled.
|
||||||
BOOL isAAFilterEnabled() const;
|
bool isAAFilterEnabled() const;
|
||||||
|
|
||||||
/// 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.
|
||||||
|
|
|
@ -41,10 +41,10 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2013-06-12 15:24:44 +0000 (Wed, 12 Jun 2013) $
|
// Last changed : $Date: 2014-04-07 01:57:21 +1000 (Mon, 07 Apr 2014) $
|
||||||
// File revision : $Revision: 4 $
|
// File revision : $Revision: 4 $
|
||||||
//
|
//
|
||||||
// $Id: SoundTouch.cpp 171 2013-06-12 15:24:44Z oparviai $
|
// $Id: SoundTouch.cpp 195 2014-04-06 15:57:21Z oparviai $
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
|
@ -97,7 +97,7 @@ SoundTouch::SoundTouch()
|
||||||
{
|
{
|
||||||
// Initialize rate transposer and tempo changer instances
|
// Initialize rate transposer and tempo changer instances
|
||||||
|
|
||||||
pRateTransposer = RateTransposer::newInstance();
|
pRateTransposer = new RateTransposer();
|
||||||
pTDStretch = TDStretch::newInstance();
|
pTDStretch = TDStretch::newInstance();
|
||||||
|
|
||||||
setOutPipe(pTDStretch);
|
setOutPipe(pTDStretch);
|
||||||
|
@ -111,7 +111,7 @@ SoundTouch::SoundTouch()
|
||||||
calcEffectiveRateAndTempo();
|
calcEffectiveRateAndTempo();
|
||||||
|
|
||||||
channels = 0;
|
channels = 0;
|
||||||
bSrateSet = FALSE;
|
bSrateSet = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -255,7 +255,7 @@ void SoundTouch::calcEffectiveRateAndTempo()
|
||||||
tempoOut = pTDStretch->getOutput();
|
tempoOut = pTDStretch->getOutput();
|
||||||
tempoOut->moveSamples(*output);
|
tempoOut->moveSamples(*output);
|
||||||
// move samples in pitch transposer's store buffer to tempo changer's input
|
// move samples in pitch transposer's store buffer to tempo changer's input
|
||||||
pTDStretch->moveSamples(*pRateTransposer->getStore());
|
// deprecated : pTDStretch->moveSamples(*pRateTransposer->getStore());
|
||||||
|
|
||||||
output = pTDStretch;
|
output = pTDStretch;
|
||||||
}
|
}
|
||||||
|
@ -283,7 +283,7 @@ void SoundTouch::calcEffectiveRateAndTempo()
|
||||||
// Sets sample rate.
|
// Sets sample rate.
|
||||||
void SoundTouch::setSampleRate(uint srate)
|
void SoundTouch::setSampleRate(uint srate)
|
||||||
{
|
{
|
||||||
bSrateSet = TRUE;
|
bSrateSet = true;
|
||||||
// set sample rate, leave other tempo changer parameters as they are.
|
// set sample rate, leave other tempo changer parameters as they are.
|
||||||
pTDStretch->setParameters((int)srate);
|
pTDStretch->setParameters((int)srate);
|
||||||
}
|
}
|
||||||
|
@ -293,7 +293,7 @@ void SoundTouch::setSampleRate(uint srate)
|
||||||
// the input of the object.
|
// the input of the object.
|
||||||
void SoundTouch::putSamples(const SAMPLETYPE *samples, uint nSamples)
|
void SoundTouch::putSamples(const SAMPLETYPE *samples, uint nSamples)
|
||||||
{
|
{
|
||||||
if (bSrateSet == FALSE)
|
if (bSrateSet == false)
|
||||||
{
|
{
|
||||||
ST_THROW_RT_ERROR("SoundTouch : Sample rate not defined");
|
ST_THROW_RT_ERROR("SoundTouch : Sample rate not defined");
|
||||||
}
|
}
|
||||||
|
@ -383,13 +383,12 @@ void SoundTouch::flush()
|
||||||
pTDStretch->clearInput();
|
pTDStretch->clearInput();
|
||||||
// yet leave the 'tempoChanger' output intouched as that's where the
|
// yet leave the 'tempoChanger' output intouched as that's where the
|
||||||
// flushed samples are!
|
// flushed samples are!
|
||||||
free(buff);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 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.
|
||||||
BOOL SoundTouch::setSetting(int settingId, int value)
|
bool SoundTouch::setSetting(int settingId, int value)
|
||||||
{
|
{
|
||||||
int sampleRate, sequenceMs, seekWindowMs, overlapMs;
|
int sampleRate, sequenceMs, seekWindowMs, overlapMs;
|
||||||
|
|
||||||
|
@ -400,36 +399,36 @@ BOOL SoundTouch::setSetting(int settingId, int value)
|
||||||
{
|
{
|
||||||
case SETTING_USE_AA_FILTER :
|
case SETTING_USE_AA_FILTER :
|
||||||
// enables / disabless anti-alias filter
|
// enables / disabless anti-alias filter
|
||||||
pRateTransposer->enableAAFilter((value != 0) ? TRUE : FALSE);
|
pRateTransposer->enableAAFilter((value != 0) ? true : false);
|
||||||
return TRUE;
|
return true;
|
||||||
|
|
||||||
case SETTING_AA_FILTER_LENGTH :
|
case SETTING_AA_FILTER_LENGTH :
|
||||||
// sets anti-alias filter length
|
// sets anti-alias filter length
|
||||||
pRateTransposer->getAAFilter()->setLength(value);
|
pRateTransposer->getAAFilter()->setLength(value);
|
||||||
return TRUE;
|
return true;
|
||||||
|
|
||||||
case SETTING_USE_QUICKSEEK :
|
case SETTING_USE_QUICKSEEK :
|
||||||
// enables / disables tempo routine quick seeking algorithm
|
// enables / disables tempo routine quick seeking algorithm
|
||||||
pTDStretch->enableQuickSeek((value != 0) ? TRUE : FALSE);
|
pTDStretch->enableQuickSeek((value != 0) ? true : false);
|
||||||
return TRUE;
|
return true;
|
||||||
|
|
||||||
case SETTING_SEQUENCE_MS:
|
case SETTING_SEQUENCE_MS:
|
||||||
// change time-stretch sequence duration parameter
|
// change time-stretch sequence duration parameter
|
||||||
pTDStretch->setParameters(sampleRate, value, seekWindowMs, overlapMs);
|
pTDStretch->setParameters(sampleRate, value, seekWindowMs, overlapMs);
|
||||||
return TRUE;
|
return true;
|
||||||
|
|
||||||
case SETTING_SEEKWINDOW_MS:
|
case SETTING_SEEKWINDOW_MS:
|
||||||
// change time-stretch seek window length parameter
|
// change time-stretch seek window length parameter
|
||||||
pTDStretch->setParameters(sampleRate, sequenceMs, value, overlapMs);
|
pTDStretch->setParameters(sampleRate, sequenceMs, value, overlapMs);
|
||||||
return TRUE;
|
return true;
|
||||||
|
|
||||||
case SETTING_OVERLAP_MS:
|
case SETTING_OVERLAP_MS:
|
||||||
// change time-stretch overlap length parameter
|
// change time-stretch overlap length parameter
|
||||||
pTDStretch->setParameters(sampleRate, sequenceMs, seekWindowMs, value);
|
pTDStretch->setParameters(sampleRate, sequenceMs, seekWindowMs, value);
|
||||||
return TRUE;
|
return true;
|
||||||
|
|
||||||
default :
|
default :
|
||||||
return FALSE;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -79,10 +79,10 @@ namespace soundtouch
|
||||||
{
|
{
|
||||||
|
|
||||||
/// Soundtouch library version string
|
/// Soundtouch library version string
|
||||||
#define SOUNDTOUCH_VERSION "1.7.2 (dev)"
|
#define SOUNDTOUCH_VERSION "1.8.1 (r198)"
|
||||||
|
|
||||||
/// SoundTouch library version id
|
/// SoundTouch library version id
|
||||||
#define SOUNDTOUCH_VERSION_ID (10702)
|
#define SOUNDTOUCH_VERSION_ID (10801)
|
||||||
|
|
||||||
//
|
//
|
||||||
// Available setting IDs for the 'setSetting' & 'get_setting' functions:
|
// Available setting IDs for the 'setSetting' & 'get_setting' functions:
|
||||||
|
@ -248,7 +248,7 @@ public:
|
||||||
/// '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.
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,9 @@
|
||||||
<ClCompile Include="cpu_detect_x86.cpp" />
|
<ClCompile Include="cpu_detect_x86.cpp" />
|
||||||
<ClCompile Include="FIFOSampleBuffer.cpp" />
|
<ClCompile Include="FIFOSampleBuffer.cpp" />
|
||||||
<ClCompile Include="FIRFilter.cpp" />
|
<ClCompile Include="FIRFilter.cpp" />
|
||||||
|
<ClCompile Include="InterpolateCubic.cpp" />
|
||||||
|
<ClCompile Include="InterpolateLinear.cpp" />
|
||||||
|
<ClCompile Include="InterpolateShannon.cpp" />
|
||||||
<ClCompile Include="mmx_optimized.cpp" />
|
<ClCompile Include="mmx_optimized.cpp" />
|
||||||
<ClCompile Include="PeakFinder.cpp" />
|
<ClCompile Include="PeakFinder.cpp" />
|
||||||
<ClCompile Include="RateTransposer.cpp" />
|
<ClCompile Include="RateTransposer.cpp" />
|
||||||
|
@ -62,6 +65,9 @@
|
||||||
<ClInclude Include="FIFOSampleBuffer.h" />
|
<ClInclude Include="FIFOSampleBuffer.h" />
|
||||||
<ClInclude Include="FIFOSamplePipe.h" />
|
<ClInclude Include="FIFOSamplePipe.h" />
|
||||||
<ClInclude Include="FIRFilter.h" />
|
<ClInclude Include="FIRFilter.h" />
|
||||||
|
<ClInclude Include="InterpolateCubic.h" />
|
||||||
|
<ClInclude Include="InterpolateLinear.h" />
|
||||||
|
<ClInclude Include="InterpolateShannon.h" />
|
||||||
<ClInclude Include="PeakFinder.h" />
|
<ClInclude Include="PeakFinder.h" />
|
||||||
<ClInclude Include="RateTransposer.h" />
|
<ClInclude Include="RateTransposer.h" />
|
||||||
<ClInclude Include="SoundTouch.h" />
|
<ClInclude Include="SoundTouch.h" />
|
||||||
|
@ -74,4 +80,4 @@
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
<ImportGroup Label="ExtensionTargets">
|
<ImportGroup Label="ExtensionTargets">
|
||||||
</ImportGroup>
|
</ImportGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -13,10 +13,10 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2013-06-14 17:34:33 +0000 (Fri, 14 Jun 2013) $
|
// Last changed : $Date: 2014-04-07 01:57:21 +1000 (Mon, 07 Apr 2014) $
|
||||||
// File revision : $Revision: 1.12 $
|
// File revision : $Revision: 1.12 $
|
||||||
//
|
//
|
||||||
// $Id: TDStretch.cpp 172 2013-06-14 17:34:33Z oparviai $
|
// $Id: TDStretch.cpp 195 2014-04-06 15:57:21Z oparviai $
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
|
@ -84,15 +84,15 @@ static const short _scanOffsets[5][24]={
|
||||||
|
|
||||||
TDStretch::TDStretch() : FIFOProcessor(&outputBuffer)
|
TDStretch::TDStretch() : FIFOProcessor(&outputBuffer)
|
||||||
{
|
{
|
||||||
bQuickSeek = FALSE;
|
bQuickSeek = false;
|
||||||
channels = 2;
|
channels = 2;
|
||||||
|
|
||||||
pMidBuffer = NULL;
|
pMidBuffer = NULL;
|
||||||
pMidBufferUnaligned = NULL;
|
pMidBufferUnaligned = NULL;
|
||||||
overlapLength = 0;
|
overlapLength = 0;
|
||||||
|
|
||||||
bAutoSeqSetting = TRUE;
|
bAutoSeqSetting = true;
|
||||||
bAutoSeekSetting = TRUE;
|
bAutoSeekSetting = true;
|
||||||
|
|
||||||
// outDebt = 0;
|
// outDebt = 0;
|
||||||
skipFract = 0;
|
skipFract = 0;
|
||||||
|
@ -132,23 +132,23 @@ void TDStretch::setParameters(int aSampleRate, int aSequenceMS,
|
||||||
if (aSequenceMS > 0)
|
if (aSequenceMS > 0)
|
||||||
{
|
{
|
||||||
this->sequenceMs = aSequenceMS;
|
this->sequenceMs = aSequenceMS;
|
||||||
bAutoSeqSetting = FALSE;
|
bAutoSeqSetting = false;
|
||||||
}
|
}
|
||||||
else if (aSequenceMS == 0)
|
else if (aSequenceMS == 0)
|
||||||
{
|
{
|
||||||
// if zero, use automatic setting
|
// if zero, use automatic setting
|
||||||
bAutoSeqSetting = TRUE;
|
bAutoSeqSetting = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aSeekWindowMS > 0)
|
if (aSeekWindowMS > 0)
|
||||||
{
|
{
|
||||||
this->seekWindowMs = aSeekWindowMS;
|
this->seekWindowMs = aSeekWindowMS;
|
||||||
bAutoSeekSetting = FALSE;
|
bAutoSeekSetting = false;
|
||||||
}
|
}
|
||||||
else if (aSeekWindowMS == 0)
|
else if (aSeekWindowMS == 0)
|
||||||
{
|
{
|
||||||
// if zero, use automatic setting
|
// if zero, use automatic setting
|
||||||
bAutoSeekSetting = TRUE;
|
bAutoSeekSetting = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
calcSeqParameters();
|
calcSeqParameters();
|
||||||
|
@ -231,14 +231,14 @@ void TDStretch::clear()
|
||||||
|
|
||||||
// Enables/disables the quick position seeking algorithm. Zero to disable, nonzero
|
// Enables/disables the quick position seeking algorithm. Zero to disable, nonzero
|
||||||
// to enable
|
// to enable
|
||||||
void TDStretch::enableQuickSeek(BOOL enable)
|
void TDStretch::enableQuickSeek(bool enable)
|
||||||
{
|
{
|
||||||
bQuickSeek = enable;
|
bQuickSeek = enable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Returns nonzero if the quick seeking algorithm is enabled.
|
// Returns nonzero if the quick seeking algorithm is enabled.
|
||||||
BOOL TDStretch::isQuickSeekEnabled() const
|
bool TDStretch::isQuickSeekEnabled() const
|
||||||
{
|
{
|
||||||
return bQuickSeek;
|
return bQuickSeek;
|
||||||
}
|
}
|
||||||
|
@ -293,6 +293,7 @@ int TDStretch::seekBestOverlapPositionFull(const SAMPLETYPE *refPos)
|
||||||
{
|
{
|
||||||
int bestOffs;
|
int bestOffs;
|
||||||
double bestCorr, corr;
|
double bestCorr, corr;
|
||||||
|
double norm;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
bestCorr = FLT_MIN;
|
bestCorr = FLT_MIN;
|
||||||
|
@ -300,11 +301,15 @@ 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.
|
||||||
for (i = 0; i < seekLength; i ++)
|
bestCorr = calcCrossCorr(refPos, pMidBuffer, norm);
|
||||||
|
for (i = 1; i < seekLength; i ++)
|
||||||
{
|
{
|
||||||
// Calculates correlation value for the mixing position corresponding
|
// Calculates correlation value for the mixing position corresponding
|
||||||
// to 'i'
|
// to 'i'. Now call "calcCrossCorrAccumulate" that is otherwise same as
|
||||||
corr = calcCrossCorr(refPos + channels * i, pMidBuffer);
|
// "calcCrossCorr", but saves time by reusing & updating previously stored
|
||||||
|
// "norm" value
|
||||||
|
corr = calcCrossCorrAccumulate(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 * 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));
|
||||||
|
@ -352,12 +357,13 @@ int TDStretch::seekBestOverlapPositionQuick(const SAMPLETYPE *refPos)
|
||||||
j = 0;
|
j = 0;
|
||||||
while (_scanOffsets[scanCount][j])
|
while (_scanOffsets[scanCount][j])
|
||||||
{
|
{
|
||||||
|
double norm;
|
||||||
tempOffset = corrOffset + _scanOffsets[scanCount][j];
|
tempOffset = corrOffset + _scanOffsets[scanCount][j];
|
||||||
if (tempOffset >= seekLength) break;
|
if (tempOffset >= seekLength) break;
|
||||||
|
|
||||||
// Calculates correlation value for the mixing position corresponding
|
// Calculates correlation value for the mixing position corresponding
|
||||||
// to 'tempOffset'
|
// to 'tempOffset'
|
||||||
corr = (double)calcCrossCorr(refPos + channels * tempOffset, pMidBuffer);
|
corr = (double)calcCrossCorr(refPos + channels * tempOffset, 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;
|
double tmp = (double)(2 * tempOffset - seekLength) / seekLength;
|
||||||
corr = ((corr + 0.1) * (1.0 - 0.25 * tmp * tmp));
|
corr = ((corr + 0.1) * (1.0 - 0.25 * tmp * tmp));
|
||||||
|
@ -729,32 +735,72 @@ void TDStretch::calculateOverlapLength(int aoverlapMs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
double TDStretch::calcCrossCorr(const short *mixingPos, const short *compare) const
|
double TDStretch::calcCrossCorr(const short *mixingPos, const short *compare, double &norm) const
|
||||||
{
|
{
|
||||||
long corr;
|
long corr;
|
||||||
long norm;
|
long lnorm;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
corr = norm = 0;
|
corr = lnorm = 0;
|
||||||
// Same routine for stereo and mono. For stereo, unroll loop for better
|
// Same routine for stereo and mono. For stereo, unroll loop for better
|
||||||
// efficiency and gives slightly better resolution against rounding.
|
// efficiency and gives slightly better resolution against rounding.
|
||||||
// For mono it same routine, just unrolls loop by factor of 4
|
// For mono it same routine, just unrolls loop by factor of 4
|
||||||
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] +
|
mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBits; // notice: do intermediate division here to avoid integer overflow
|
||||||
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]) >> overlapDividerBits;
|
||||||
norm += (mixingPos[i] * mixingPos[i] +
|
lnorm += (mixingPos[i] * mixingPos[i] +
|
||||||
mixingPos[i + 1] * mixingPos[i + 1] +
|
mixingPos[i + 1] * mixingPos[i + 1]) >> overlapDividerBits; // notice: do intermediate division here to avoid integer overflow
|
||||||
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]) >> overlapDividerBits;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
if (norm == 0) norm = 1; // to avoid div by zero
|
norm = (double)lnorm;
|
||||||
return (double)corr / sqrt((double)norm);
|
return (double)corr / sqrt((norm < 1e-9) ? 1.0 : norm);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Update cross-correlation by accumulating "norm" coefficient by previously calculated value
|
||||||
|
double TDStretch::calcCrossCorrAccumulate(const short *mixingPos, const short *compare, double &norm) const
|
||||||
|
{
|
||||||
|
long corr;
|
||||||
|
long lnorm;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
// cancel first normalizer tap from previous round
|
||||||
|
lnorm = 0;
|
||||||
|
for (i = 1; i <= channels; i ++)
|
||||||
|
{
|
||||||
|
lnorm -= (mixingPos[-i] * mixingPos[-i]) >> overlapDividerBits;
|
||||||
|
}
|
||||||
|
|
||||||
|
corr = 0;
|
||||||
|
// Same routine for stereo and mono. For stereo, unroll loop for better
|
||||||
|
// efficiency and gives slightly better resolution against rounding.
|
||||||
|
// For mono it same routine, just unrolls loop by factor of 4
|
||||||
|
for (i = 0; i < channels * overlapLength; i += 4)
|
||||||
|
{
|
||||||
|
corr += (mixingPos[i] * compare[i] +
|
||||||
|
mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBits; // notice: do intermediate division here to avoid integer overflow
|
||||||
|
corr += (mixingPos[i + 2] * compare[i + 2] +
|
||||||
|
mixingPos[i + 3] * compare[i + 3]) >> overlapDividerBits;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update normalizer with last samples of this round
|
||||||
|
for (int j = 0; j < channels; j ++)
|
||||||
|
{
|
||||||
|
i --;
|
||||||
|
lnorm += (mixingPos[i] * mixingPos[i]) >> overlapDividerBits;
|
||||||
|
}
|
||||||
|
norm += (double)lnorm;
|
||||||
|
|
||||||
|
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
||||||
|
// done using floating point operation
|
||||||
|
return (double)corr / sqrt((norm < 1e-9) ? 1.0 : norm);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // SOUNDTOUCH_INTEGER_SAMPLES
|
#endif // SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
|
@ -834,10 +880,10 @@ void TDStretch::calculateOverlapLength(int overlapInMsec)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
double TDStretch::calcCrossCorr(const float *mixingPos, const float *compare) const
|
/// Calculate cross-correlation
|
||||||
|
double TDStretch::calcCrossCorr(const float *mixingPos, const float *compare, double &norm) const
|
||||||
{
|
{
|
||||||
double corr;
|
double corr;
|
||||||
double norm;
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
corr = norm = 0;
|
corr = norm = 0;
|
||||||
|
@ -859,8 +905,43 @@ double TDStretch::calcCrossCorr(const float *mixingPos, const float *compare) co
|
||||||
mixingPos[i + 3] * mixingPos[i + 3];
|
mixingPos[i + 3] * mixingPos[i + 3];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (norm < 1e-9) norm = 1.0; // to avoid div by zero
|
return corr / sqrt((norm < 1e-9 ? 1.0 : norm));
|
||||||
return corr / sqrt(norm);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Update cross-correlation by accumulating "norm" coefficient by previously calculated value
|
||||||
|
double TDStretch::calcCrossCorrAccumulate(const float *mixingPos, const float *compare, double &norm) const
|
||||||
|
{
|
||||||
|
double corr;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
corr = 0;
|
||||||
|
|
||||||
|
// cancel first normalizer tap from previous round
|
||||||
|
for (i = 1; i <= channels; i ++)
|
||||||
|
{
|
||||||
|
norm -= mixingPos[-i] * mixingPos[-i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Same routine for stereo and mono. For Stereo, unroll by factor of 2.
|
||||||
|
// For mono it's same routine yet unrollsd by factor of 4.
|
||||||
|
for (i = 0; i < channels * overlapLength; i += 4)
|
||||||
|
{
|
||||||
|
corr += mixingPos[i] * compare[i] +
|
||||||
|
mixingPos[i + 1] * compare[i + 1] +
|
||||||
|
mixingPos[i + 2] * compare[i + 2] +
|
||||||
|
mixingPos[i + 3] * compare[i + 3];
|
||||||
|
}
|
||||||
|
|
||||||
|
// update normalizer with last samples of this round
|
||||||
|
for (int j = 0; j < channels; j ++)
|
||||||
|
{
|
||||||
|
i --;
|
||||||
|
norm += mixingPos[i] * mixingPos[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return corr / sqrt((norm < 1e-9 ? 1.0 : norm));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#endif // SOUNDTOUCH_FLOAT_SAMPLES
|
#endif // SOUNDTOUCH_FLOAT_SAMPLES
|
||||||
|
|
|
@ -13,10 +13,10 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2013-06-12 15:24:44 +0000 (Wed, 12 Jun 2013) $
|
// Last changed : $Date: 2014-04-07 01:57:21 +1000 (Mon, 07 Apr 2014) $
|
||||||
// File revision : $Revision: 4 $
|
// File revision : $Revision: 4 $
|
||||||
//
|
//
|
||||||
// $Id: TDStretch.h 171 2013-06-12 15:24:44Z oparviai $
|
// $Id: TDStretch.h 195 2014-04-06 15:57:21Z oparviai $
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
|
@ -125,21 +125,22 @@ protected:
|
||||||
float skipFract;
|
float skipFract;
|
||||||
FIFOSampleBuffer outputBuffer;
|
FIFOSampleBuffer outputBuffer;
|
||||||
FIFOSampleBuffer inputBuffer;
|
FIFOSampleBuffer inputBuffer;
|
||||||
BOOL bQuickSeek;
|
bool bQuickSeek;
|
||||||
|
|
||||||
int sampleRate;
|
int sampleRate;
|
||||||
int sequenceMs;
|
int sequenceMs;
|
||||||
int seekWindowMs;
|
int seekWindowMs;
|
||||||
int overlapMs;
|
int overlapMs;
|
||||||
BOOL bAutoSeqSetting;
|
bool bAutoSeqSetting;
|
||||||
BOOL bAutoSeekSetting;
|
bool bAutoSeekSetting;
|
||||||
|
|
||||||
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) const;
|
virtual double calcCrossCorr(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare, double &norm) const;
|
||||||
|
virtual double calcCrossCorrAccumulate(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare, double &norm) const;
|
||||||
|
|
||||||
virtual int seekBestOverlapPositionFull(const SAMPLETYPE *refPos);
|
virtual int seekBestOverlapPositionFull(const SAMPLETYPE *refPos);
|
||||||
virtual int seekBestOverlapPositionQuick(const SAMPLETYPE *refPos);
|
virtual int seekBestOverlapPositionQuick(const SAMPLETYPE *refPos);
|
||||||
|
@ -194,10 +195,10 @@ public:
|
||||||
|
|
||||||
/// Enables/disables the quick position seeking algorithm. Zero to disable,
|
/// Enables/disables the quick position seeking algorithm. Zero to disable,
|
||||||
/// nonzero to enable
|
/// nonzero to enable
|
||||||
void enableQuickSeek(BOOL enable);
|
void enableQuickSeek(bool enable);
|
||||||
|
|
||||||
/// Returns nonzero if the quick seeking algorithm is enabled.
|
/// Returns nonzero if the quick seeking algorithm is enabled.
|
||||||
BOOL isQuickSeekEnabled() const;
|
bool isQuickSeekEnabled() const;
|
||||||
|
|
||||||
/// Sets routine control parameters. These control are certain time constants
|
/// Sets routine control parameters. These control are certain time constants
|
||||||
/// defining how the sound is stretched to the desired duration.
|
/// defining how the sound is stretched to the desired duration.
|
||||||
|
@ -248,7 +249,8 @@ public:
|
||||||
class TDStretchMMX : public TDStretch
|
class TDStretchMMX : public TDStretch
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
double calcCrossCorr(const short *mixingPos, const short *compare) const;
|
double calcCrossCorr(const short *mixingPos, const short *compare, double &norm) const;
|
||||||
|
double calcCrossCorrAccumulate(const short *mixingPos, const short *compare, double &norm) const;
|
||||||
virtual void overlapStereo(short *output, const short *input) const;
|
virtual void overlapStereo(short *output, const short *input) const;
|
||||||
virtual void clearCrossCorrState();
|
virtual void clearCrossCorrState();
|
||||||
};
|
};
|
||||||
|
@ -260,7 +262,8 @@ public:
|
||||||
class TDStretchSSE : public TDStretch
|
class TDStretchSSE : public TDStretch
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
double calcCrossCorr(const float *mixingPos, const float *compare) const;
|
double calcCrossCorr(const float *mixingPos, const float *compare, double &norm) const;
|
||||||
|
double calcCrossCorrAccumulate(const float *mixingPos, const float *compare, double &norm) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /// SOUNDTOUCH_ALLOW_SSE
|
#endif /// SOUNDTOUCH_ALLOW_SSE
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2008-02-10 16:26:55 +0000 (Sun, 10 Feb 2008) $
|
// Last changed : $Date: 2008-02-11 03:26:55 +1100 (Mon, 11 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,10 +11,10 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2012-11-08 18:44:37 +0000 (Thu, 08 Nov 2012) $
|
// Last changed : $Date: 2014-01-08 05:24:28 +1100 (Wed, 08 Jan 2014) $
|
||||||
// File revision : $Revision: 4 $
|
// File revision : $Revision: 4 $
|
||||||
//
|
//
|
||||||
// $Id: cpu_detect_x86.cpp 159 2012-11-08 18:44:37Z oparviai $
|
// $Id: cpu_detect_x86.cpp 183 2014-01-07 18:24:28Z oparviai $
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
|
@ -42,22 +42,20 @@
|
||||||
#include "cpu_detect.h"
|
#include "cpu_detect.h"
|
||||||
#include "STTypes.h"
|
#include "STTypes.h"
|
||||||
|
|
||||||
|
|
||||||
#if defined(SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS)
|
#if defined(SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS)
|
||||||
|
|
||||||
#if defined(__GNUC__) && defined(__i386__)
|
#if defined(__GNUC__) && defined(__i386__)
|
||||||
// gcc
|
// gcc
|
||||||
#include "cpuid.h"
|
#include "cpuid.h"
|
||||||
#elif defined(_M_IX86)
|
#elif defined(_M_IX86)
|
||||||
// windows non-gcc
|
// windows non-gcc
|
||||||
#include <intrin.h>
|
#include <intrin.h>
|
||||||
#endif
|
#endif
|
||||||
#ifndef bit_MMX
|
|
||||||
#define bit_MMX (1 << 23)
|
|
||||||
#define bit_MMX (1 << 23)
|
|
||||||
#define bit_SSE (1 << 25)
|
|
||||||
#define bit_SSE2 (1 << 26)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
#define bit_MMX (1 << 23)
|
||||||
|
#define bit_SSE (1 << 25)
|
||||||
|
#define bit_SSE2 (1 << 26)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -20,10 +20,10 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2012-11-08 18:53:01 +0000 (Thu, 08 Nov 2012) $
|
// Last changed : $Date: 2014-01-08 05:25:40 +1100 (Wed, 08 Jan 2014) $
|
||||||
// File revision : $Revision: 4 $
|
// File revision : $Revision: 4 $
|
||||||
//
|
//
|
||||||
// $Id: mmx_optimized.cpp 160 2012-11-08 18:53:01Z oparviai $
|
// $Id: mmx_optimized.cpp 184 2014-01-07 18:25:40Z 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) const
|
double TDStretchMMX::calcCrossCorr(const short *pV1, const short *pV2, double &dnorm) const
|
||||||
{
|
{
|
||||||
const __m64 *pVec1, *pVec2;
|
const __m64 *pVec1, *pVec2;
|
||||||
__m64 shifter;
|
__m64 shifter;
|
||||||
|
@ -93,19 +93,19 @@ double TDStretchMMX::calcCrossCorr(const short *pV1, const short *pV2) const
|
||||||
// _mm_add_pi32 : 2*32bit add
|
// _mm_add_pi32 : 2*32bit add
|
||||||
// _m_psrad : 32bit right-shift
|
// _m_psrad : 32bit right-shift
|
||||||
|
|
||||||
temp = _mm_add_pi32(_mm_madd_pi16(pVec1[0], pVec2[0]),
|
temp = _mm_add_pi32(_mm_sra_pi32(_mm_madd_pi16(pVec1[0], pVec2[0]), shifter),
|
||||||
_mm_madd_pi16(pVec1[1], pVec2[1]));
|
_mm_sra_pi32(_mm_madd_pi16(pVec1[1], pVec2[1]), shifter));
|
||||||
temp2 = _mm_add_pi32(_mm_madd_pi16(pVec1[0], pVec1[0]),
|
temp2 = _mm_add_pi32(_mm_sra_pi32(_mm_madd_pi16(pVec1[0], pVec1[0]), shifter),
|
||||||
_mm_madd_pi16(pVec1[1], pVec1[1]));
|
_mm_sra_pi32(_mm_madd_pi16(pVec1[1], pVec1[1]), shifter));
|
||||||
accu = _mm_add_pi32(accu, _mm_sra_pi32(temp, shifter));
|
accu = _mm_add_pi32(accu, temp);
|
||||||
normaccu = _mm_add_pi32(normaccu, _mm_sra_pi32(temp2, shifter));
|
normaccu = _mm_add_pi32(normaccu, temp2);
|
||||||
|
|
||||||
temp = _mm_add_pi32(_mm_madd_pi16(pVec1[2], pVec2[2]),
|
temp = _mm_add_pi32(_mm_sra_pi32(_mm_madd_pi16(pVec1[2], pVec2[2]), shifter),
|
||||||
_mm_madd_pi16(pVec1[3], pVec2[3]));
|
_mm_sra_pi32(_mm_madd_pi16(pVec1[3], pVec2[3]), shifter));
|
||||||
temp2 = _mm_add_pi32(_mm_madd_pi16(pVec1[2], pVec1[2]),
|
temp2 = _mm_add_pi32(_mm_sra_pi32(_mm_madd_pi16(pVec1[2], pVec1[2]), shifter),
|
||||||
_mm_madd_pi16(pVec1[3], pVec1[3]));
|
_mm_sra_pi32(_mm_madd_pi16(pVec1[3], pVec1[3]), shifter));
|
||||||
accu = _mm_add_pi32(accu, _mm_sra_pi32(temp, shifter));
|
accu = _mm_add_pi32(accu, temp);
|
||||||
normaccu = _mm_add_pi32(normaccu, _mm_sra_pi32(temp2, shifter));
|
normaccu = _mm_add_pi32(normaccu, temp2);
|
||||||
|
|
||||||
pVec1 += 4;
|
pVec1 += 4;
|
||||||
pVec2 += 4;
|
pVec2 += 4;
|
||||||
|
@ -125,14 +125,81 @@ double TDStretchMMX::calcCrossCorr(const short *pV1, const short *pV2) const
|
||||||
|
|
||||||
// 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
|
||||||
if (norm == 0) norm = 1; // to avoid div by zero
|
dnorm = (double)norm;
|
||||||
|
|
||||||
return (double)corr / sqrt((double)norm);
|
return (double)corr / sqrt(dnorm < 1e-9 ? 1.0 : dnorm);
|
||||||
// Note: Warning about the missing EMMS instruction is harmless
|
// Note: Warning about the missing EMMS instruction is harmless
|
||||||
// as it'll be called elsewhere.
|
// as it'll be called elsewhere.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Update cross-correlation by accumulating "norm" coefficient by previously calculated value
|
||||||
|
double TDStretchMMX::calcCrossCorrAccumulate(const short *pV1, const short *pV2, double &dnorm) const
|
||||||
|
{
|
||||||
|
const __m64 *pVec1, *pVec2;
|
||||||
|
__m64 shifter;
|
||||||
|
__m64 accu;
|
||||||
|
long corr, lnorm;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
// cancel first normalizer tap from previous round
|
||||||
|
lnorm = 0;
|
||||||
|
for (i = 1; i <= channels; i ++)
|
||||||
|
{
|
||||||
|
lnorm -= (pV1[-i] * pV1[-i]) >> overlapDividerBits;
|
||||||
|
}
|
||||||
|
|
||||||
|
pVec1 = (__m64*)pV1;
|
||||||
|
pVec2 = (__m64*)pV2;
|
||||||
|
|
||||||
|
shifter = _m_from_int(overlapDividerBits);
|
||||||
|
accu = _mm_setzero_si64();
|
||||||
|
|
||||||
|
// Process 4 parallel sets of 2 * stereo samples or 4 * mono samples
|
||||||
|
// during each round for improved CPU-level parallellization.
|
||||||
|
for (i = 0; i < channels * overlapLength / 16; i ++)
|
||||||
|
{
|
||||||
|
__m64 temp;
|
||||||
|
|
||||||
|
// dictionary of instructions:
|
||||||
|
// _m_pmaddwd : 4*16bit multiply-add, resulting two 32bits = [a0*b0+a1*b1 ; a2*b2+a3*b3]
|
||||||
|
// _mm_add_pi32 : 2*32bit add
|
||||||
|
// _m_psrad : 32bit right-shift
|
||||||
|
|
||||||
|
temp = _mm_add_pi32(_mm_sra_pi32(_mm_madd_pi16(pVec1[0], pVec2[0]), shifter),
|
||||||
|
_mm_sra_pi32(_mm_madd_pi16(pVec1[1], pVec2[1]), shifter));
|
||||||
|
accu = _mm_add_pi32(accu, temp);
|
||||||
|
|
||||||
|
temp = _mm_add_pi32(_mm_sra_pi32(_mm_madd_pi16(pVec1[2], pVec2[2]), shifter),
|
||||||
|
_mm_sra_pi32(_mm_madd_pi16(pVec1[3], pVec2[3]), shifter));
|
||||||
|
accu = _mm_add_pi32(accu, temp);
|
||||||
|
|
||||||
|
pVec1 += 4;
|
||||||
|
pVec2 += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy hi-dword of mm0 to lo-dword of mm1, then sum mmo+mm1
|
||||||
|
// and finally store the result into the variable "corr"
|
||||||
|
|
||||||
|
accu = _mm_add_pi32(accu, _mm_srli_si64(accu, 32));
|
||||||
|
corr = _m_to_int(accu);
|
||||||
|
|
||||||
|
// Clear MMS state
|
||||||
|
_m_empty();
|
||||||
|
|
||||||
|
// update normalizer with last samples of this round
|
||||||
|
pV1 = (short *)pVec1;
|
||||||
|
for (int j = 1; j <= channels; j ++)
|
||||||
|
{
|
||||||
|
lnorm += (pV1[-j] * pV1[-j]) >> overlapDividerBits;
|
||||||
|
}
|
||||||
|
dnorm += (double)lnorm;
|
||||||
|
|
||||||
|
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
||||||
|
// done using floating point operation
|
||||||
|
return (double)corr / sqrt((dnorm < 1e-9) ? 1.0 : dnorm);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void TDStretchMMX::clearCrossCorrState()
|
void TDStretchMMX::clearCrossCorrState()
|
||||||
{
|
{
|
||||||
|
|
|
@ -23,10 +23,10 @@
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Last changed : $Date: 2012-11-08 18:53:01 +0000 (Thu, 08 Nov 2012) $
|
// Last changed : $Date: 2014-01-08 05:25:40 +1100 (Wed, 08 Jan 2014) $
|
||||||
// File revision : $Revision: 4 $
|
// File revision : $Revision: 4 $
|
||||||
//
|
//
|
||||||
// $Id: sse_optimized.cpp 160 2012-11-08 18:53:01Z oparviai $
|
// $Id: sse_optimized.cpp 184 2014-01-07 18:25:40Z 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) const
|
double TDStretchSSE::calcCrossCorr(const float *pV1, const float *pV2, double &norm) const
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
const float *pVec1;
|
const float *pVec1;
|
||||||
|
@ -141,11 +141,10 @@ double TDStretchSSE::calcCrossCorr(const float *pV1, const float *pV2) const
|
||||||
|
|
||||||
// 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;
|
||||||
double norm = sqrt(pvNorm[0] + pvNorm[1] + pvNorm[2] + pvNorm[3]);
|
norm = (pvNorm[0] + pvNorm[1] + pvNorm[2] + pvNorm[3]);
|
||||||
if (norm < 1e-9) norm = 1.0; // to avoid div by zero
|
|
||||||
|
|
||||||
float *pvSum = (float*)&vSum;
|
float *pvSum = (float*)&vSum;
|
||||||
return (double)(pvSum[0] + pvSum[1] + pvSum[2] + pvSum[3]) / norm;
|
return (double)(pvSum[0] + pvSum[1] + pvSum[2] + pvSum[3]) / sqrt(norm < 1e-9 ? 1.0 : norm);
|
||||||
|
|
||||||
/* This is approximately corresponding routine in C-language yet without normalization:
|
/* This is approximately corresponding routine in C-language yet without normalization:
|
||||||
double corr, norm;
|
double corr, norm;
|
||||||
|
@ -182,6 +181,16 @@ double TDStretchSSE::calcCrossCorr(const float *pV1, const float *pV2) const
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
double TDStretchSSE::calcCrossCorrAccumulate(const float *pV1, const float *pV2, double &norm) const
|
||||||
|
{
|
||||||
|
// call usual calcCrossCorr function because SSE does not show big benefit of
|
||||||
|
// accumulating "norm" value, and also the "norm" rolling algorithm would get
|
||||||
|
// complicated due to SSE-specific alignment-vs-nonexact correlation rules.
|
||||||
|
return calcCrossCorr(pV1, pV2, norm);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// implementation of SSE optimized functions of class 'FIRFilter'
|
// implementation of SSE optimized functions of class 'FIRFilter'
|
||||||
|
|
Loading…
Reference in New Issue