Updated to soundtouch-1.3 (plus modifications)
git-svn-id: svn://localhost/trunk/ardour2@13 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
parent
90dff30cf4
commit
fa7c141813
350
libs/soundtouch/3dnow_win.cpp
Normal file
350
libs/soundtouch/3dnow_win.cpp
Normal file
@ -0,0 +1,350 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Win32 version of the AMD 3DNow! optimized routines for AMD K6-2/Athlon
|
||||
/// processors. All 3DNow! optimized functions have been gathered into this
|
||||
/// single source code file, regardless to their class or original source code
|
||||
/// file, in order to ease porting the library to other compiler and processor
|
||||
/// platforms.
|
||||
///
|
||||
/// By the way; the performance gain depends heavily on the CPU generation: On
|
||||
/// K6-2 these routines provided speed-up of even 2.4 times, while on Athlon the
|
||||
/// difference to the original routines stayed at unremarkable 8%! Such a small
|
||||
/// improvement on Athlon is due to 3DNow can perform only two operations in
|
||||
/// parallel, and obviously also the Athlon FPU is doing a very good job with
|
||||
/// the standard C floating point routines! Here these routines are anyway,
|
||||
/// although it might not be worth the effort to convert these to GCC platform,
|
||||
/// for Athlon CPU at least. The situation is different regarding the SSE
|
||||
/// optimizations though, thanks to the four parallel operations of SSE that
|
||||
/// already make a difference.
|
||||
///
|
||||
/// This file is to be compiled in Windows platform with Microsoft Visual C++
|
||||
/// Compiler. Please see '3dnow_gcc.cpp' for the gcc compiler version for all
|
||||
/// GNU platforms (if file supplied).
|
||||
///
|
||||
/// NOTICE: If using Visual Studio 6.0, you'll need to install the "Visual C++
|
||||
/// 6.0 processor pack" update to support 3DNow! instruction set. The update is
|
||||
/// available for download at Microsoft Developers Network, see here:
|
||||
/// http://msdn.microsoft.com/vstudio/downloads/tools/ppack/default.aspx
|
||||
///
|
||||
/// If the above URL is expired or removed, go to "http://msdn.microsoft.com" and
|
||||
/// perform a search with keywords "processor pack".
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai @ iki.fi
|
||||
/// SoundTouch WWW: http://www.iki.fi/oparviai/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date$
|
||||
// File revision : $Revision$
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "cpu_detect.h"
|
||||
#include "STTypes.h"
|
||||
|
||||
#ifndef WIN32
|
||||
#error "wrong platform - this source code file is exclusively for Win32 platform"
|
||||
#endif
|
||||
|
||||
using namespace soundtouch;
|
||||
|
||||
#ifdef ALLOW_3DNOW
|
||||
// 3DNow! routines available only with float sample type
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// implementation of 3DNow! optimized functions of class 'TDStretch3DNow'
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "TDStretch.h"
|
||||
#include <limits.h>
|
||||
|
||||
// these are declared in 'TDStretch.cpp'
|
||||
extern int scanOffsets[4][24];
|
||||
|
||||
|
||||
// Calculates cross correlation of two buffers
|
||||
double TDStretch3DNow::calcCrossCorrStereo(const float *pV1, const float *pV2) const
|
||||
{
|
||||
uint overlapLengthLocal = overlapLength;
|
||||
float corr;
|
||||
|
||||
// Calculates the cross-correlation value between 'pV1' and 'pV2' vectors
|
||||
/*
|
||||
c-pseudocode:
|
||||
|
||||
corr = 0;
|
||||
for (i = 0; i < overlapLength / 4; i ++)
|
||||
{
|
||||
corr += pV1[0] * pV2[0];
|
||||
pV1[1] * pV2[1];
|
||||
pV1[2] * pV2[2];
|
||||
pV1[3] * pV2[3];
|
||||
pV1[4] * pV2[4];
|
||||
pV1[5] * pV2[5];
|
||||
pV1[6] * pV2[6];
|
||||
pV1[7] * pV2[7];
|
||||
|
||||
pV1 += 8;
|
||||
pV2 += 8;
|
||||
}
|
||||
*/
|
||||
|
||||
_asm
|
||||
{
|
||||
// give prefetch hints to CPU of what data are to be needed soonish.
|
||||
// give more aggressive hints on pV1 as that changes more between different calls
|
||||
// while pV2 stays the same.
|
||||
prefetch [pV1]
|
||||
prefetch [pV2]
|
||||
prefetch [pV1 + 32]
|
||||
|
||||
mov eax, dword ptr pV2
|
||||
mov ebx, dword ptr pV1
|
||||
|
||||
pxor mm0, mm0
|
||||
|
||||
mov ecx, overlapLengthLocal
|
||||
shr ecx, 2 // div by four
|
||||
|
||||
loop1:
|
||||
movq mm1, [eax]
|
||||
prefetch [eax + 32] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
pfmul mm1, [ebx]
|
||||
prefetch [ebx + 64] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
|
||||
movq mm2, [eax + 8]
|
||||
pfadd mm0, mm1
|
||||
pfmul mm2, [ebx + 8]
|
||||
|
||||
movq mm3, [eax + 16]
|
||||
pfadd mm0, mm2
|
||||
pfmul mm3, [ebx + 16]
|
||||
|
||||
movq mm4, [eax + 24]
|
||||
pfadd mm0, mm3
|
||||
pfmul mm4, [ebx + 24]
|
||||
|
||||
add eax, 32
|
||||
pfadd mm0, mm4
|
||||
add ebx, 32
|
||||
|
||||
dec ecx
|
||||
jnz loop1
|
||||
|
||||
// add halfs of mm0 together and return the result.
|
||||
// note: mm1 is used as a dummy parameter only, we actually don't care about it's value
|
||||
pfacc mm0, mm1
|
||||
movd corr, mm0
|
||||
femms
|
||||
}
|
||||
|
||||
return corr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// implementation of 3DNow! optimized functions of class 'FIRFilter'
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "FIRFilter.h"
|
||||
|
||||
FIRFilter3DNow::FIRFilter3DNow() : FIRFilter()
|
||||
{
|
||||
filterCoeffsUnalign = NULL;
|
||||
}
|
||||
|
||||
|
||||
FIRFilter3DNow::~FIRFilter3DNow()
|
||||
{
|
||||
delete[] filterCoeffsUnalign;
|
||||
}
|
||||
|
||||
|
||||
// (overloaded) Calculates filter coefficients for 3DNow! routine
|
||||
void FIRFilter3DNow::setCoefficients(const float *coeffs, uint newLength, uint uResultDivFactor)
|
||||
{
|
||||
uint i;
|
||||
float fDivider;
|
||||
|
||||
FIRFilter::setCoefficients(coeffs, newLength, uResultDivFactor);
|
||||
|
||||
// Scale the filter coefficients so that it won't be necessary to scale the filtering result
|
||||
// also rearrange coefficients suitably for 3DNow!
|
||||
// Ensure that filter coeffs array is aligned to 16-byte boundary
|
||||
delete[] filterCoeffsUnalign;
|
||||
filterCoeffsUnalign = new float[2 * newLength + 4];
|
||||
filterCoeffsAlign = (float *)(((uint)filterCoeffsUnalign + 15) & -16);
|
||||
|
||||
fDivider = (float)resultDivider;
|
||||
|
||||
// rearrange the filter coefficients for mmx routines
|
||||
for (i = 0; i < newLength; i ++)
|
||||
{
|
||||
filterCoeffsAlign[2 * i + 0] =
|
||||
filterCoeffsAlign[2 * i + 1] = coeffs[i + 0] / fDivider;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 3DNow!-optimized version of the filter routine for stereo sound
|
||||
uint FIRFilter3DNow::evaluateFilterStereo(float *dest, const float *src, const uint numSamples) const
|
||||
{
|
||||
float *filterCoeffsLocal = filterCoeffsAlign;
|
||||
uint count = (numSamples - length) & -2;
|
||||
uint lengthLocal = length / 4;
|
||||
|
||||
assert(length != 0);
|
||||
assert(count % 2 == 0);
|
||||
|
||||
/* original code:
|
||||
|
||||
double suml1, suml2;
|
||||
double sumr1, sumr2;
|
||||
uint i, j;
|
||||
|
||||
for (j = 0; j < count; j += 2)
|
||||
{
|
||||
const float *ptr;
|
||||
|
||||
suml1 = sumr1 = 0.0;
|
||||
suml2 = sumr2 = 0.0;
|
||||
ptr = src;
|
||||
filterCoeffsLocal = filterCoeffs;
|
||||
for (i = 0; i < lengthLocal; i ++)
|
||||
{
|
||||
// unroll loop for efficiency.
|
||||
|
||||
suml1 += ptr[0] * filterCoeffsLocal[0] +
|
||||
ptr[2] * filterCoeffsLocal[2] +
|
||||
ptr[4] * filterCoeffsLocal[4] +
|
||||
ptr[6] * filterCoeffsLocal[6];
|
||||
|
||||
sumr1 += ptr[1] * filterCoeffsLocal[1] +
|
||||
ptr[3] * filterCoeffsLocal[3] +
|
||||
ptr[5] * filterCoeffsLocal[5] +
|
||||
ptr[7] * filterCoeffsLocal[7];
|
||||
|
||||
suml2 += ptr[8] * filterCoeffsLocal[0] +
|
||||
ptr[10] * filterCoeffsLocal[2] +
|
||||
ptr[12] * filterCoeffsLocal[4] +
|
||||
ptr[14] * filterCoeffsLocal[6];
|
||||
|
||||
sumr2 += ptr[9] * filterCoeffsLocal[1] +
|
||||
ptr[11] * filterCoeffsLocal[3] +
|
||||
ptr[13] * filterCoeffsLocal[5] +
|
||||
ptr[15] * filterCoeffsLocal[7];
|
||||
|
||||
ptr += 16;
|
||||
filterCoeffsLocal += 8;
|
||||
}
|
||||
dest[0] = (float)suml1;
|
||||
dest[1] = (float)sumr1;
|
||||
dest[2] = (float)suml2;
|
||||
dest[3] = (float)sumr2;
|
||||
|
||||
src += 4;
|
||||
dest += 4;
|
||||
}
|
||||
|
||||
*/
|
||||
_asm
|
||||
{
|
||||
mov eax, dword ptr dest
|
||||
mov ebx, dword ptr src
|
||||
mov edx, count
|
||||
shr edx, 1
|
||||
|
||||
loop1:
|
||||
// "outer loop" : during each round 2*2 output samples are calculated
|
||||
prefetch [ebx] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
prefetch [filterCoeffsLocal] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
|
||||
mov esi, ebx
|
||||
mov edi, filterCoeffsLocal
|
||||
pxor mm0, mm0
|
||||
pxor mm1, mm1
|
||||
mov ecx, lengthLocal
|
||||
|
||||
loop2:
|
||||
// "inner loop" : during each round four FIR filter taps are evaluated for 2*2 output samples
|
||||
movq mm2, [edi]
|
||||
movq mm3, mm2
|
||||
prefetch [edi + 32] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
pfmul mm2, [esi]
|
||||
prefetch [esi + 32] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
pfmul mm3, [esi + 8]
|
||||
|
||||
movq mm4, [edi + 8]
|
||||
movq mm5, mm4
|
||||
pfadd mm0, mm2
|
||||
pfmul mm4, [esi + 8]
|
||||
pfadd mm1, mm3
|
||||
pfmul mm5, [esi + 16]
|
||||
|
||||
movq mm2, [edi + 16]
|
||||
movq mm6, mm2
|
||||
pfadd mm0, mm4
|
||||
pfmul mm2, [esi + 16]
|
||||
pfadd mm1, mm5
|
||||
pfmul mm6, [esi + 24]
|
||||
|
||||
movq mm3, [edi + 24]
|
||||
movq mm7, mm3
|
||||
pfadd mm0, mm2
|
||||
pfmul mm3, [esi + 24]
|
||||
pfadd mm1, mm6
|
||||
pfmul mm7, [esi + 32]
|
||||
add esi, 32
|
||||
pfadd mm0, mm3
|
||||
add edi, 32
|
||||
pfadd mm1, mm7
|
||||
|
||||
dec ecx
|
||||
jnz loop2
|
||||
|
||||
movq [eax], mm0
|
||||
add ebx, 16
|
||||
movq [eax + 8], mm1
|
||||
add eax, 16
|
||||
|
||||
dec edx
|
||||
jnz loop1
|
||||
|
||||
femms
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
#endif // ALLOW_3DNOW
|
184
libs/soundtouch/AAFilter.cpp
Normal file
184
libs/soundtouch/AAFilter.cpp
Normal file
@ -0,0 +1,184 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// FIR low-pass (anti-alias) filter with filter coefficient design routine and
|
||||
/// MMX optimization.
|
||||
///
|
||||
/// Anti-alias filter is used to prevent folding of high frequencies when
|
||||
/// transposing the sample rate with interpolation.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai @ iki.fi
|
||||
/// SoundTouch WWW: http://www.iki.fi/oparviai/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date$
|
||||
// File revision : $Revision$
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <memory.h>
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include "AAFilter.h"
|
||||
#include "FIRFilter.h"
|
||||
|
||||
using namespace soundtouch;
|
||||
|
||||
#define PI 3.141592655357989
|
||||
#define TWOPI (2 * PI)
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* Implementation of the class 'AAFilter'
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
AAFilter::AAFilter(const uint length)
|
||||
{
|
||||
pFIR = FIRFilter::newInstance();
|
||||
cutoffFreq = 0.5;
|
||||
setLength(length);
|
||||
}
|
||||
|
||||
|
||||
|
||||
AAFilter::~AAFilter()
|
||||
{
|
||||
delete pFIR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets new anti-alias filter cut-off edge frequency, scaled to
|
||||
// sampling frequency (nyquist frequency = 0.5).
|
||||
// The filter will cut frequencies higher than the given frequency.
|
||||
void AAFilter::setCutoffFreq(const double newCutoffFreq)
|
||||
{
|
||||
cutoffFreq = newCutoffFreq;
|
||||
calculateCoeffs();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets number of FIR filter taps
|
||||
void AAFilter::setLength(const uint newLength)
|
||||
{
|
||||
length = newLength;
|
||||
calculateCoeffs();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Calculates coefficients for a low-pass FIR filter using Hamming window
|
||||
void AAFilter::calculateCoeffs()
|
||||
{
|
||||
uint i;
|
||||
double cntTemp, temp, tempCoeff,h, w;
|
||||
double fc2, wc;
|
||||
double scaleCoeff, sum;
|
||||
double *work;
|
||||
SAMPLETYPE *coeffs;
|
||||
|
||||
assert(length > 0);
|
||||
assert(length % 4 == 0);
|
||||
assert(cutoffFreq >= 0);
|
||||
assert(cutoffFreq <= 0.5);
|
||||
|
||||
work = new double[length];
|
||||
coeffs = new SAMPLETYPE[length];
|
||||
|
||||
fc2 = 2.0 * cutoffFreq;
|
||||
wc = PI * fc2;
|
||||
tempCoeff = TWOPI / (double)length;
|
||||
|
||||
sum = 0;
|
||||
for (i = 0; i < length; i ++)
|
||||
{
|
||||
cntTemp = (double)i - (double)(length / 2);
|
||||
|
||||
temp = cntTemp * wc;
|
||||
if (temp != 0)
|
||||
{
|
||||
h = fc2 * sin(temp) / temp; // sinc function
|
||||
}
|
||||
else
|
||||
{
|
||||
h = 1.0;
|
||||
}
|
||||
w = 0.54 + 0.46 * cos(tempCoeff * cntTemp); // hamming window
|
||||
|
||||
temp = w * h;
|
||||
work[i] = temp;
|
||||
|
||||
// calc net sum of coefficients
|
||||
sum += temp;
|
||||
}
|
||||
|
||||
// ensure the sum of coefficients is larger than zero
|
||||
assert(sum > 0);
|
||||
|
||||
// ensure we've really designed a lowpass filter...
|
||||
assert(work[length/2] > 0);
|
||||
assert(work[length/2 + 1] > -1e-6);
|
||||
assert(work[length/2 - 1] > -1e-6);
|
||||
|
||||
// Calculate a scaling coefficient in such a way that the result can be
|
||||
// divided by 16384
|
||||
scaleCoeff = 16384.0f / sum;
|
||||
|
||||
for (i = 0; i < length; i ++)
|
||||
{
|
||||
// scale & round to nearest integer
|
||||
temp = work[i] * scaleCoeff;
|
||||
temp += (temp >= 0) ? 0.5 : -0.5;
|
||||
// ensure no overfloods
|
||||
assert(temp >= -32768 && temp <= 32767);
|
||||
coeffs[i] = (SAMPLETYPE)temp;
|
||||
}
|
||||
|
||||
// Set coefficients. Use divide factor 14 => divide result by 2^14 = 16384
|
||||
pFIR->setCoefficients(coeffs, length, 14);
|
||||
|
||||
delete[] work;
|
||||
delete[] coeffs;
|
||||
}
|
||||
|
||||
|
||||
// Applies the filter to the given sequence of samples.
|
||||
// Note : The amount of outputted samples is by value of 'filter length'
|
||||
// smaller than the amount of input samples.
|
||||
uint AAFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels) const
|
||||
{
|
||||
return pFIR->evaluate(dest, src, numSamples, numChannels);
|
||||
}
|
||||
|
||||
|
||||
uint AAFilter::getLength() const
|
||||
{
|
||||
return pFIR->getLength();
|
||||
}
|
91
libs/soundtouch/AAFilter.h
Normal file
91
libs/soundtouch/AAFilter.h
Normal file
@ -0,0 +1,91 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo
|
||||
/// while maintaining the original pitch by using a time domain WSOLA-like method
|
||||
/// with several performance-increasing tweaks.
|
||||
///
|
||||
/// Anti-alias filter is used to prevent folding of high frequencies when
|
||||
/// transposing the sample rate with interpolation.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai @ iki.fi
|
||||
/// SoundTouch WWW: http://www.iki.fi/oparviai/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date$
|
||||
// File revision : $Revision$
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 AAFilter_H
|
||||
#define AAFilter_H
|
||||
|
||||
#include "STTypes.h"
|
||||
|
||||
namespace soundtouch
|
||||
{
|
||||
|
||||
class AAFilter
|
||||
{
|
||||
protected:
|
||||
class FIRFilter *pFIR;
|
||||
|
||||
/// Low-pass filter cut-off frequency, negative = invalid
|
||||
double cutoffFreq;
|
||||
|
||||
/// num of filter taps
|
||||
uint length;
|
||||
|
||||
/// Calculate the FIR coefficients realizing the given cutoff-frequency
|
||||
void calculateCoeffs();
|
||||
public:
|
||||
AAFilter(uint length);
|
||||
|
||||
~AAFilter();
|
||||
|
||||
/// Sets new anti-alias filter cut-off edge frequency, scaled to sampling
|
||||
/// frequency (nyquist frequency = 0.5). The filter will cut off the
|
||||
/// frequencies than that.
|
||||
void setCutoffFreq(double newCutoffFreq);
|
||||
|
||||
/// Sets number of FIR filter taps, i.e. ~filter complexity
|
||||
void setLength(uint newLength);
|
||||
|
||||
uint getLength() const;
|
||||
|
||||
/// Applies the filter to the given sequence of samples.
|
||||
/// Note : The amount of outputted samples is by value of 'filter length'
|
||||
/// smaller than the amount of input samples.
|
||||
uint evaluate(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
uint numSamples,
|
||||
uint numChannels) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
159
libs/soundtouch/BPMDetect.h
Normal file
159
libs/soundtouch/BPMDetect.h
Normal file
@ -0,0 +1,159 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Beats-per-minute (BPM) detection routine.
|
||||
///
|
||||
/// The beat detection algorithm works as follows:
|
||||
/// - Use function 'inputSamples' to input a chunks of samples to the class for
|
||||
/// analysis. It's a good idea to enter a large sound file or stream in smallish
|
||||
/// chunks of around few kilosamples in order not to extinguish too much RAM memory.
|
||||
/// - Input sound data is decimated to approx 500 Hz to reduce calculation burden,
|
||||
/// which is basically ok as low (bass) frequencies mostly determine the beat rate.
|
||||
/// Simple averaging is used for anti-alias filtering because the resulting signal
|
||||
/// quality isn't of that high importance.
|
||||
/// - Decimated sound data is enveloped, i.e. the amplitude shape is detected by
|
||||
/// taking absolute value that's smoothed by sliding average. Signal levels that
|
||||
/// are below a couple of times the general RMS amplitude level are cut away to
|
||||
/// leave only notable peaks there.
|
||||
/// - Repeating sound patterns (e.g. beats) are detected by calculating short-term
|
||||
/// autocorrelation function of the enveloped signal.
|
||||
/// - After whole sound data file has been analyzed as above, the bpm level is
|
||||
/// detected by function 'getBpm' that finds the highest peak of the autocorrelation
|
||||
/// function, calculates it's precise location and converts this reading to bpm's.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai @ iki.fi
|
||||
/// SoundTouch WWW: http://www.iki.fi/oparviai/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date$
|
||||
// File revision : $Revision$
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 _BPMDetect_H_
|
||||
#define _BPMDetect_H_
|
||||
|
||||
#include "STTypes.h"
|
||||
#include "FIFOSampleBuffer.h"
|
||||
|
||||
/// Minimum allowed BPM rate. Used to restrict accepted result above a reasonable limit.
|
||||
#define MIN_BPM 45
|
||||
|
||||
/// Maximum allowed BPM rate. Used to restrict accepted result below a reasonable limit.
|
||||
#define MAX_BPM 230
|
||||
|
||||
|
||||
/// Class for calculating BPM rate for audio data.
|
||||
class BPMDetect
|
||||
{
|
||||
protected:
|
||||
/// Auto-correlation accumulator bins.
|
||||
float *xcorr;
|
||||
|
||||
/// Amplitude envelope sliding average approximation level accumulator
|
||||
float envelopeAccu;
|
||||
|
||||
/// RMS volume sliding average approximation level accumulator
|
||||
float RMSVolumeAccu;
|
||||
|
||||
/// Sample average counter.
|
||||
int decimateCount;
|
||||
|
||||
/// Sample average accumulator for FIFO-like decimation.
|
||||
soundtouch::LONG_SAMPLETYPE decimateSum;
|
||||
|
||||
/// Decimate sound by this coefficient to reach approx. 500 Hz.
|
||||
int decimateBy;
|
||||
|
||||
/// Auto-correlation window length
|
||||
int windowLen;
|
||||
|
||||
/// Number of channels (1 = mono, 2 = stereo)
|
||||
int channels;
|
||||
|
||||
/// sample rate
|
||||
int sampleRate;
|
||||
|
||||
/// Beginning of auto-correlation window: Autocorrelation isn't being updated for
|
||||
/// the first these many correlation bins.
|
||||
int windowStart;
|
||||
|
||||
/// FIFO-buffer for decimated processing samples.
|
||||
soundtouch::FIFOSampleBuffer *buffer;
|
||||
|
||||
/// Initialize the class for processing.
|
||||
void init(int numChannels, int sampleRate);
|
||||
|
||||
/// Updates auto-correlation function for given number of decimated samples that
|
||||
/// are read from the internal 'buffer' pipe (samples aren't removed from the pipe
|
||||
/// though).
|
||||
void updateXCorr(int process_samples /// How many samples are processed.
|
||||
);
|
||||
|
||||
/// Decimates samples to approx. 500 Hz.
|
||||
///
|
||||
/// \return Number of output samples.
|
||||
int decimate(soundtouch::SAMPLETYPE *dest, ///< Destination buffer
|
||||
const soundtouch::SAMPLETYPE *src, ///< Source sample buffer
|
||||
int numsamples ///< Number of source samples.
|
||||
);
|
||||
|
||||
/// Calculates amplitude envelope for the buffer of samples.
|
||||
/// Result is output to 'samples'.
|
||||
void calcEnvelope(soundtouch::SAMPLETYPE *samples, ///< Pointer to input/output data buffer
|
||||
int numsamples ///< Number of samples in buffer
|
||||
);
|
||||
|
||||
public:
|
||||
/// Constructor.
|
||||
BPMDetect(int numChannels, ///< Number of channels in sample data.
|
||||
int sampleRate ///< Sample rate in Hz.
|
||||
);
|
||||
|
||||
/// Destructor.
|
||||
virtual ~BPMDetect();
|
||||
|
||||
/// Inputs a block of samples for analyzing: Envelopes the samples and then
|
||||
/// updates the autocorrelation estimation. When whole song data has been input
|
||||
/// in smaller blocks using this function, read the resulting bpm with 'getBpm'
|
||||
/// function.
|
||||
///
|
||||
/// Notice that data in 'samples' array can be disrupted in processing.
|
||||
void inputSamples(soundtouch::SAMPLETYPE *samples, ///< Pointer to input/working data buffer
|
||||
int numSamples ///< Number of samples in buffer
|
||||
);
|
||||
|
||||
|
||||
/// Analyzes the results and returns the BPM rate. Use this function to read result
|
||||
/// after whole song data has been input to the class by consecutive calls of
|
||||
/// 'inputSamples' function.
|
||||
///
|
||||
/// \return Beats-per-minute rate, or zero if detection failed.
|
||||
float getBpm();
|
||||
};
|
||||
|
||||
#endif // _BPMDetect_H_
|
340
libs/soundtouch/COPYING
Normal file
340
libs/soundtouch/COPYING
Normal file
@ -0,0 +1,340 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) 19yy <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) 19yy name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
252
libs/soundtouch/FIFOSampleBuffer.cpp
Normal file
252
libs/soundtouch/FIFOSampleBuffer.cpp
Normal file
@ -0,0 +1,252 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// A buffer class for temporarily storaging sound samples, operates as a
|
||||
/// first-in-first-out pipe.
|
||||
///
|
||||
/// Samples are added to the end of the sample buffer with the 'putSamples'
|
||||
/// function, and are received from the beginning of the buffer by calling
|
||||
/// the 'receiveSamples' function. The class automatically removes the
|
||||
/// outputted samples from the buffer, as well as grows the buffer size
|
||||
/// whenever necessary.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai @ iki.fi
|
||||
/// SoundTouch WWW: http://www.iki.fi/oparviai/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date$
|
||||
// File revision : $Revision$
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <stdlib.h>
|
||||
#include <memory.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "FIFOSampleBuffer.h"
|
||||
|
||||
using namespace soundtouch;
|
||||
|
||||
// Constructor
|
||||
FIFOSampleBuffer::FIFOSampleBuffer(uint numChannels)
|
||||
{
|
||||
sizeInBytes = 0; // reasonable initial value
|
||||
buffer = NULL; //new SAMPLETYPE[sizeInBytes / sizeof(SAMPLETYPE)];
|
||||
bufferUnaligned = NULL;
|
||||
samplesInBuffer = 0;
|
||||
bufferPos = 0;
|
||||
channels = numChannels;
|
||||
}
|
||||
|
||||
|
||||
// destructor
|
||||
FIFOSampleBuffer::~FIFOSampleBuffer()
|
||||
{
|
||||
delete[] bufferUnaligned;
|
||||
}
|
||||
|
||||
|
||||
// Sets number of channels, 1 = mono, 2 = stereo
|
||||
void FIFOSampleBuffer::setChannels(const uint numChannels)
|
||||
{
|
||||
uint usedBytes;
|
||||
|
||||
usedBytes = channels * samplesInBuffer;
|
||||
channels = numChannels;
|
||||
samplesInBuffer = usedBytes / channels;
|
||||
}
|
||||
|
||||
|
||||
// if output location pointer 'bufferPos' isn't zero, 'rewinds' the buffer and
|
||||
// zeroes this pointer by copying samples from the 'bufferPos' pointer
|
||||
// location on to the beginning of the buffer.
|
||||
void FIFOSampleBuffer::rewind()
|
||||
{
|
||||
if (bufferPos)
|
||||
{
|
||||
memmove(buffer, ptrBegin(), sizeof(SAMPLETYPE) * channels * samplesInBuffer);
|
||||
bufferPos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Adds 'numSamples' pcs of samples from the 'samples' memory position to
|
||||
// the sample buffer.
|
||||
void FIFOSampleBuffer::putSamples(const SAMPLETYPE *samples, uint numSamples)
|
||||
{
|
||||
memcpy(ptrEnd(numSamples), samples, sizeof(SAMPLETYPE) * numSamples * channels);
|
||||
samplesInBuffer += numSamples;
|
||||
}
|
||||
|
||||
|
||||
// Increases the number of samples in the buffer without copying any actual
|
||||
// samples.
|
||||
//
|
||||
// This function is used to update the number of samples in the sample buffer
|
||||
// when accessing the buffer directly with 'ptrEnd' function. Please be
|
||||
// careful though!
|
||||
void FIFOSampleBuffer::putSamples(uint numSamples)
|
||||
{
|
||||
uint req;
|
||||
|
||||
req = samplesInBuffer + numSamples;
|
||||
ensureCapacity(req);
|
||||
samplesInBuffer += numSamples;
|
||||
}
|
||||
|
||||
|
||||
// Returns a pointer to the end of the used part of the sample buffer (i.e.
|
||||
// where the new samples are to be inserted). This function may be used for
|
||||
// inserting new samples into the sample buffer directly. Please be careful!
|
||||
//
|
||||
// Parameter 'slackCapacity' tells the function how much free capacity (in
|
||||
// terms of samples) there _at least_ should be, in order to the caller to
|
||||
// succesfully insert all the required samples to the buffer. When necessary,
|
||||
// the function grows the buffer size to comply with this requirement.
|
||||
//
|
||||
// When using this function as means for inserting new samples, also remember
|
||||
// to increase the sample count afterwards, by calling the
|
||||
// 'putSamples(numSamples)' function.
|
||||
SAMPLETYPE *FIFOSampleBuffer::ptrEnd(uint slackCapacity)
|
||||
{
|
||||
ensureCapacity(samplesInBuffer + slackCapacity);
|
||||
return buffer + samplesInBuffer * channels;
|
||||
}
|
||||
|
||||
|
||||
// Returns a pointer to the beginning of the currently non-outputted samples.
|
||||
// This function is provided for accessing the output samples directly.
|
||||
// Please be careful!
|
||||
//
|
||||
// When using this function to output samples, also remember to 'remove' the
|
||||
// outputted samples from the buffer by calling the
|
||||
// 'receiveSamples(numSamples)' function
|
||||
SAMPLETYPE *FIFOSampleBuffer::ptrBegin() const
|
||||
{
|
||||
return buffer + bufferPos * channels;
|
||||
}
|
||||
|
||||
|
||||
// Ensures that the buffer has enought capacity, i.e. space for _at least_
|
||||
// 'capacityRequirement' number of samples. The buffer is grown in steps of
|
||||
// 4 kilobytes to eliminate the need for frequently growing up the buffer,
|
||||
// as well as to round the buffer size up to the virtual memory page size.
|
||||
void FIFOSampleBuffer::ensureCapacity(uint capacityRequirement)
|
||||
{
|
||||
SAMPLETYPE *tempUnaligned, *temp;
|
||||
|
||||
if (capacityRequirement > getCapacity())
|
||||
{
|
||||
// enlarge the buffer in 4kbyte steps (round up to next 4k boundary)
|
||||
sizeInBytes = (capacityRequirement * channels * sizeof(SAMPLETYPE) + 4095) & -4096;
|
||||
assert(sizeInBytes % 2 == 0);
|
||||
tempUnaligned = new SAMPLETYPE[sizeInBytes / sizeof(SAMPLETYPE) + 16 / sizeof(SAMPLETYPE)];
|
||||
if (tempUnaligned == NULL)
|
||||
{
|
||||
throw std::runtime_error("Couldn't allocate memory!\n");
|
||||
}
|
||||
temp = (SAMPLETYPE *)(((ulong)tempUnaligned + 15) & -16);
|
||||
memcpy(temp, ptrBegin(), samplesInBuffer * channels * sizeof(SAMPLETYPE));
|
||||
delete[] bufferUnaligned;
|
||||
buffer = temp;
|
||||
bufferUnaligned = tempUnaligned;
|
||||
bufferPos = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// simply rewind the buffer (if necessary)
|
||||
rewind();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Returns the current buffer capacity in terms of samples
|
||||
uint FIFOSampleBuffer::getCapacity() const
|
||||
{
|
||||
return sizeInBytes / (channels * sizeof(SAMPLETYPE));
|
||||
}
|
||||
|
||||
|
||||
// Returns the number of samples currently in the buffer
|
||||
uint FIFOSampleBuffer::numSamples() const
|
||||
{
|
||||
return samplesInBuffer;
|
||||
}
|
||||
|
||||
|
||||
// Output samples from beginning of the sample buffer. Copies demanded number
|
||||
// of samples to output and removes them from the sample buffer. If there
|
||||
// are less than 'numsample' samples in the buffer, returns all available.
|
||||
//
|
||||
// Returns number of samples copied.
|
||||
uint FIFOSampleBuffer::receiveSamples(SAMPLETYPE *output, uint maxSamples)
|
||||
{
|
||||
uint num;
|
||||
|
||||
num = (maxSamples > samplesInBuffer) ? samplesInBuffer : maxSamples;
|
||||
|
||||
memcpy(output, ptrBegin(), channels * sizeof(SAMPLETYPE) * num);
|
||||
return receiveSamples(num);
|
||||
}
|
||||
|
||||
|
||||
// Removes samples from the beginning of the sample buffer without copying them
|
||||
// anywhere. Used to reduce the number of samples in the buffer, when accessing
|
||||
// the sample buffer with the 'ptrBegin' function.
|
||||
uint FIFOSampleBuffer::receiveSamples(uint maxSamples)
|
||||
{
|
||||
if (maxSamples >= samplesInBuffer)
|
||||
{
|
||||
uint temp;
|
||||
|
||||
temp = samplesInBuffer;
|
||||
samplesInBuffer = 0;
|
||||
return temp;
|
||||
}
|
||||
|
||||
samplesInBuffer -= maxSamples;
|
||||
bufferPos += maxSamples;
|
||||
|
||||
return maxSamples;
|
||||
}
|
||||
|
||||
|
||||
// Returns nonzero if the sample buffer is empty
|
||||
int FIFOSampleBuffer::isEmpty() const
|
||||
{
|
||||
return (samplesInBuffer == 0) ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
// Clears the sample buffer
|
||||
void FIFOSampleBuffer::clear()
|
||||
{
|
||||
samplesInBuffer = 0;
|
||||
bufferPos = 0;
|
||||
}
|
174
libs/soundtouch/FIFOSampleBuffer.h
Normal file
174
libs/soundtouch/FIFOSampleBuffer.h
Normal file
@ -0,0 +1,174 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// A buffer class for temporarily storaging sound samples, operates as a
|
||||
/// first-in-first-out pipe.
|
||||
///
|
||||
/// Samples are added to the end of the sample buffer with the 'putSamples'
|
||||
/// function, and are received from the beginning of the buffer by calling
|
||||
/// the 'receiveSamples' function. The class automatically removes the
|
||||
/// output samples from the buffer as well as grows the storage size
|
||||
/// whenever necessary.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai @ iki.fi
|
||||
/// SoundTouch WWW: http://www.iki.fi/oparviai/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date$
|
||||
// File revision : $Revision$
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 FIFOSampleBuffer_H
|
||||
#define FIFOSampleBuffer_H
|
||||
|
||||
#include "FIFOSamplePipe.h"
|
||||
|
||||
namespace soundtouch
|
||||
{
|
||||
|
||||
/// Sample buffer working in FIFO (first-in-first-out) principle. The class takes
|
||||
/// care of storage size adjustment and data moving during input/output operations.
|
||||
///
|
||||
/// Notice that in case of stereo audio, one sample is considered to consist of
|
||||
/// both channel data.
|
||||
class FIFOSampleBuffer : public FIFOSamplePipe
|
||||
{
|
||||
private:
|
||||
/// Sample buffer.
|
||||
SAMPLETYPE *buffer;
|
||||
|
||||
// Raw unaligned buffer memory. 'buffer' is made aligned by pointing it to first
|
||||
// 16-byte aligned location of this buffer
|
||||
SAMPLETYPE *bufferUnaligned;
|
||||
|
||||
/// Sample buffer size in bytes
|
||||
uint sizeInBytes;
|
||||
|
||||
/// How many samples are currently in buffer.
|
||||
uint samplesInBuffer;
|
||||
|
||||
/// Channels, 1=mono, 2=stereo.
|
||||
uint channels;
|
||||
|
||||
/// Current position pointer to the buffer. This pointer is increased when samples are
|
||||
/// removed from the pipe so that it's necessary to actually rewind buffer (move data)
|
||||
/// only new data when is put to the pipe.
|
||||
uint bufferPos;
|
||||
|
||||
/// Rewind the buffer by moving data from position pointed by 'bufferPos' to real
|
||||
/// beginning of the buffer.
|
||||
void rewind();
|
||||
|
||||
/// Ensures that the buffer has capacity for at least this many samples.
|
||||
void ensureCapacity(const uint capacityRequirement);
|
||||
|
||||
/// Returns current capacity.
|
||||
uint getCapacity() const;
|
||||
|
||||
public:
|
||||
|
||||
/// Constructor
|
||||
FIFOSampleBuffer(uint numChannels = 2 ///< Number of channels, 1=mono, 2=stereo.
|
||||
///< Default is stereo.
|
||||
);
|
||||
|
||||
/// destructor
|
||||
virtual ~FIFOSampleBuffer();
|
||||
|
||||
/// Returns a pointer to the beginning of the output samples.
|
||||
/// This function is provided for accessing the output samples directly.
|
||||
/// Please be careful for not to corrupt the book-keeping!
|
||||
///
|
||||
/// When using this function to output samples, also remember to 'remove' the
|
||||
/// output samples from the buffer by calling the
|
||||
/// 'receiveSamples(numSamples)' function
|
||||
virtual SAMPLETYPE *ptrBegin() const;
|
||||
|
||||
/// Returns a pointer to the end of the used part of the sample buffer (i.e.
|
||||
/// where the new samples are to be inserted). This function may be used for
|
||||
/// inserting new samples into the sample buffer directly. Please be careful
|
||||
/// not corrupt the book-keeping!
|
||||
///
|
||||
/// When using this function as means for inserting new samples, also remember
|
||||
/// to increase the sample count afterwards, by calling the
|
||||
/// 'putSamples(numSamples)' function.
|
||||
SAMPLETYPE *ptrEnd(
|
||||
uint slackCapacity ///< How much free capacity (in samples) there _at least_
|
||||
///< should be so that the caller can succesfully insert the
|
||||
///< desired samples to the buffer. If necessary, the function
|
||||
///< grows the buffer size to comply with this requirement.
|
||||
);
|
||||
|
||||
/// Adds 'numSamples' pcs of samples from the 'samples' memory position to
|
||||
/// the sample buffer.
|
||||
virtual void putSamples(const SAMPLETYPE *samples, ///< Pointer to samples.
|
||||
uint numSamples ///< Number of samples to insert.
|
||||
);
|
||||
|
||||
/// Adjusts the book-keeping to increase number of samples in the buffer without
|
||||
/// copying any actual samples.
|
||||
///
|
||||
/// This function is used to update the number of samples in the sample buffer
|
||||
/// when accessing the buffer directly with 'ptrEnd' function. Please be
|
||||
/// careful though!
|
||||
virtual void putSamples(uint numSamples ///< Number of samples been inserted.
|
||||
);
|
||||
|
||||
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
||||
/// output buffer and removes them from the sample buffer. If there are less than
|
||||
/// 'numsample' samples in the buffer, returns all that available.
|
||||
///
|
||||
/// \return Number of samples returned.
|
||||
virtual uint receiveSamples(SAMPLETYPE *output, ///< Buffer where to copy output samples.
|
||||
uint maxSamples ///< How many samples to receive at max.
|
||||
);
|
||||
|
||||
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
||||
/// sample buffer without copying them anywhere.
|
||||
///
|
||||
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
|
||||
/// with 'ptrBegin' function.
|
||||
virtual uint receiveSamples(uint maxSamples ///< Remove this many samples from the beginning of pipe.
|
||||
);
|
||||
|
||||
/// Returns number of samples currently available.
|
||||
virtual uint numSamples() const;
|
||||
|
||||
/// Sets number of channels, 1 = mono, 2 = stereo.
|
||||
void setChannels(uint numChannels);
|
||||
|
||||
/// Returns nonzero if there aren't any samples available for outputting.
|
||||
virtual int isEmpty() const;
|
||||
|
||||
/// Clears all the samples.
|
||||
virtual void clear();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
217
libs/soundtouch/FIFOSamplePipe.h
Normal file
217
libs/soundtouch/FIFOSamplePipe.h
Normal file
@ -0,0 +1,217 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// 'FIFOSamplePipe' : An abstract base class for classes that manipulate sound
|
||||
/// samples by operating like a first-in-first-out pipe: New samples are fed
|
||||
/// into one end of the pipe with the 'putSamples' function, and the processed
|
||||
/// samples are received from the other end with the 'receiveSamples' function.
|
||||
///
|
||||
/// 'FIFOProcessor' : A base class for classes the do signal processing with
|
||||
/// the samples while operating like a first-in-first-out pipe. When samples
|
||||
/// are input with the 'putSamples' function, the class processes them
|
||||
/// and moves the processed samples to the given 'output' pipe object, which
|
||||
/// may be either another processing stage, or a fifo sample buffer object.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai @ iki.fi
|
||||
/// SoundTouch WWW: http://www.iki.fi/oparviai/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date$
|
||||
// File revision : $Revision$
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 FIFOSamplePipe_H
|
||||
#define FIFOSamplePipe_H
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include "STTypes.h"
|
||||
|
||||
namespace soundtouch
|
||||
{
|
||||
|
||||
/// Abstract base class for FIFO (first-in-first-out) sample processing classes.
|
||||
class FIFOSamplePipe
|
||||
{
|
||||
public:
|
||||
/// Returns a pointer to the beginning of the output samples.
|
||||
/// This function is provided for accessing the output samples directly.
|
||||
/// Please be careful for not to corrupt the book-keeping!
|
||||
///
|
||||
/// When using this function to output samples, also remember to 'remove' the
|
||||
/// output samples from the buffer by calling the
|
||||
/// 'receiveSamples(numSamples)' function
|
||||
virtual SAMPLETYPE *ptrBegin() const = 0;
|
||||
|
||||
/// Adds 'numSamples' pcs of samples from the 'samples' memory position to
|
||||
/// the sample buffer.
|
||||
virtual void putSamples(const SAMPLETYPE *samples, ///< Pointer to samples.
|
||||
uint numSamples ///< Number of samples to insert.
|
||||
) = 0;
|
||||
|
||||
|
||||
// Moves samples from the 'other' pipe instance to this instance.
|
||||
void moveSamples(FIFOSamplePipe &other ///< Other pipe instance where from the receive the data.
|
||||
)
|
||||
{
|
||||
int oNumSamples = other.numSamples();
|
||||
|
||||
putSamples(other.ptrBegin(), oNumSamples);
|
||||
other.receiveSamples(oNumSamples);
|
||||
};
|
||||
|
||||
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
||||
/// output buffer and removes them from the sample buffer. If there are less than
|
||||
/// 'numsample' samples in the buffer, returns all that available.
|
||||
///
|
||||
/// \return Number of samples returned.
|
||||
virtual uint receiveSamples(SAMPLETYPE *output, ///< Buffer where to copy output samples.
|
||||
uint maxSamples ///< How many samples to receive at max.
|
||||
) = 0;
|
||||
|
||||
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
||||
/// sample buffer without copying them anywhere.
|
||||
///
|
||||
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
|
||||
/// with 'ptrBegin' function.
|
||||
virtual uint receiveSamples(uint maxSamples ///< Remove this many samples from the beginning of pipe.
|
||||
) = 0;
|
||||
|
||||
/// Returns number of samples currently available.
|
||||
virtual uint numSamples() const = 0;
|
||||
|
||||
// Returns nonzero if there aren't any samples available for outputting.
|
||||
virtual int isEmpty() const = 0;
|
||||
|
||||
/// Clears all the samples.
|
||||
virtual void clear() = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// Base-class for sound processing routines working in FIFO principle. With this base
|
||||
/// class it's easy to implement sound processing stages that can be chained together,
|
||||
/// so that samples that are fed into beginning of the pipe automatically go through
|
||||
/// all the processing stages.
|
||||
///
|
||||
/// When samples are input to this class, they're first processed and then put to
|
||||
/// the FIFO pipe that's defined as output of this class. This output pipe can be
|
||||
/// either other processing stage or a FIFO sample buffer.
|
||||
class FIFOProcessor :public FIFOSamplePipe
|
||||
{
|
||||
protected:
|
||||
/// Internal pipe where processed samples are put.
|
||||
FIFOSamplePipe *output;
|
||||
|
||||
/// Sets output pipe.
|
||||
void setOutPipe(FIFOSamplePipe *pOutput)
|
||||
{
|
||||
assert(output == NULL);
|
||||
assert(pOutput != NULL);
|
||||
output = pOutput;
|
||||
}
|
||||
|
||||
|
||||
/// Constructor. Doesn't define output pipe; it has to be set be
|
||||
/// 'setOutPipe' function.
|
||||
FIFOProcessor()
|
||||
{
|
||||
output = NULL;
|
||||
}
|
||||
|
||||
|
||||
/// Constructor. Configures output pipe.
|
||||
FIFOProcessor(FIFOSamplePipe *pOutput ///< Output pipe.
|
||||
)
|
||||
{
|
||||
output = pOutput;
|
||||
}
|
||||
|
||||
|
||||
/// Destructor.
|
||||
virtual ~FIFOProcessor()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// Returns a pointer to the beginning of the output samples.
|
||||
/// This function is provided for accessing the output samples directly.
|
||||
/// Please be careful for not to corrupt the book-keeping!
|
||||
///
|
||||
/// When using this function to output samples, also remember to 'remove' the
|
||||
/// output samples from the buffer by calling the
|
||||
/// 'receiveSamples(numSamples)' function
|
||||
virtual SAMPLETYPE *ptrBegin() const
|
||||
{
|
||||
return output->ptrBegin();
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
||||
/// output buffer and removes them from the sample buffer. If there are less than
|
||||
/// 'numsample' samples in the buffer, returns all that available.
|
||||
///
|
||||
/// \return Number of samples returned.
|
||||
virtual uint receiveSamples(SAMPLETYPE *outBuffer, ///< Buffer where to copy output samples.
|
||||
uint maxSamples ///< How many samples to receive at max.
|
||||
)
|
||||
{
|
||||
return output->receiveSamples(outBuffer, maxSamples);
|
||||
}
|
||||
|
||||
|
||||
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
||||
/// sample buffer without copying them anywhere.
|
||||
///
|
||||
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
|
||||
/// with 'ptrBegin' function.
|
||||
virtual uint receiveSamples(uint maxSamples ///< Remove this many samples from the beginning of pipe.
|
||||
)
|
||||
{
|
||||
return output->receiveSamples(maxSamples);
|
||||
}
|
||||
|
||||
|
||||
/// Returns number of samples currently available.
|
||||
virtual uint numSamples() const
|
||||
{
|
||||
return output->numSamples();
|
||||
}
|
||||
|
||||
|
||||
/// Returns nonzero if there aren't any samples available for outputting.
|
||||
virtual int isEmpty() const
|
||||
{
|
||||
return output->isEmpty();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
254
libs/soundtouch/FIRFilter.cpp
Normal file
254
libs/soundtouch/FIRFilter.cpp
Normal file
@ -0,0 +1,254 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// General FIR digital filter routines with MMX optimization.
|
||||
///
|
||||
/// Note : MMX optimized functions reside in a separate, platform-specific file,
|
||||
/// e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai @ iki.fi
|
||||
/// SoundTouch WWW: http://www.iki.fi/oparviai/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date$
|
||||
// File revision : $Revision$
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <memory.h>
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdexcept>
|
||||
#include "FIRFilter.h"
|
||||
#include "cpu_detect.h"
|
||||
|
||||
using namespace soundtouch;
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* Implementation of the class 'FIRFilter'
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
FIRFilter::FIRFilter()
|
||||
{
|
||||
resultDivFactor = 0;
|
||||
length = 0;
|
||||
lengthDiv8 = 0;
|
||||
filterCoeffs = NULL;
|
||||
}
|
||||
|
||||
|
||||
FIRFilter::~FIRFilter()
|
||||
{
|
||||
delete[] filterCoeffs;
|
||||
}
|
||||
|
||||
// Usual C-version of the filter routine for stereo sound
|
||||
uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const
|
||||
{
|
||||
uint i, j, end;
|
||||
LONG_SAMPLETYPE suml, sumr;
|
||||
#ifdef FLOAT_SAMPLES
|
||||
// when using floating point samples, use a scaler instead of a divider
|
||||
// because division is much slower operation than multiplying.
|
||||
double dScaler = 1.0 / (double)resultDivider;
|
||||
#endif
|
||||
|
||||
assert(length != 0);
|
||||
|
||||
end = 2 * (numSamples - length);
|
||||
|
||||
for (j = 0; j < end; j += 2)
|
||||
{
|
||||
const SAMPLETYPE *ptr;
|
||||
|
||||
suml = sumr = 0;
|
||||
ptr = src + j;
|
||||
|
||||
for (i = 0; i < length; i += 4)
|
||||
{
|
||||
// loop is unrolled by factor of 4 here for efficiency
|
||||
suml += ptr[2 * i + 0] * filterCoeffs[i + 0] +
|
||||
ptr[2 * i + 2] * filterCoeffs[i + 1] +
|
||||
ptr[2 * i + 4] * filterCoeffs[i + 2] +
|
||||
ptr[2 * i + 6] * filterCoeffs[i + 3];
|
||||
sumr += ptr[2 * i + 1] * filterCoeffs[i + 0] +
|
||||
ptr[2 * i + 3] * filterCoeffs[i + 1] +
|
||||
ptr[2 * i + 5] * filterCoeffs[i + 2] +
|
||||
ptr[2 * i + 7] * filterCoeffs[i + 3];
|
||||
}
|
||||
|
||||
#ifdef INTEGER_SAMPLES
|
||||
suml >>= resultDivFactor;
|
||||
sumr >>= resultDivFactor;
|
||||
// saturate to 16 bit integer limits
|
||||
suml = (suml < -32768) ? -32768 : (suml > 32767) ? 32767 : suml;
|
||||
// saturate to 16 bit integer limits
|
||||
sumr = (sumr < -32768) ? -32768 : (sumr > 32767) ? 32767 : sumr;
|
||||
#else
|
||||
suml *= dScaler;
|
||||
sumr *= dScaler;
|
||||
#endif // INTEGER_SAMPLES
|
||||
dest[j] = (SAMPLETYPE)suml;
|
||||
dest[j + 1] = (SAMPLETYPE)sumr;
|
||||
}
|
||||
return numSamples - length;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Usual C-version of the filter routine for mono sound
|
||||
uint FIRFilter::evaluateFilterMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const
|
||||
{
|
||||
uint i, j, end;
|
||||
LONG_SAMPLETYPE sum;
|
||||
#ifdef FLOAT_SAMPLES
|
||||
// when using floating point samples, use a scaler instead of a divider
|
||||
// because division is much slower operation than multiplying.
|
||||
double dScaler = 1.0 / (double)resultDivider;
|
||||
#endif
|
||||
|
||||
|
||||
assert(length != 0);
|
||||
|
||||
end = numSamples - length;
|
||||
for (j = 0; j < end; j ++)
|
||||
{
|
||||
sum = 0;
|
||||
for (i = 0; i < length; i += 4)
|
||||
{
|
||||
// loop is unrolled by factor of 4 here for efficiency
|
||||
sum += src[i + 0] * filterCoeffs[i + 0] +
|
||||
src[i + 1] * filterCoeffs[i + 1] +
|
||||
src[i + 2] * filterCoeffs[i + 2] +
|
||||
src[i + 3] * filterCoeffs[i + 3];
|
||||
}
|
||||
#ifdef INTEGER_SAMPLES
|
||||
sum >>= resultDivFactor;
|
||||
// saturate to 16 bit integer limits
|
||||
sum = (sum < -32768) ? -32768 : (sum > 32767) ? 32767 : sum;
|
||||
#else
|
||||
sum *= dScaler;
|
||||
#endif // INTEGER_SAMPLES
|
||||
dest[j] = (SAMPLETYPE)sum;
|
||||
src ++;
|
||||
}
|
||||
return end;
|
||||
}
|
||||
|
||||
|
||||
// Set filter coeffiecients and length.
|
||||
//
|
||||
// Throws an exception if filter length isn't divisible by 8
|
||||
void FIRFilter::setCoefficients(const SAMPLETYPE *coeffs, uint newLength, uint uResultDivFactor)
|
||||
{
|
||||
assert(newLength > 0);
|
||||
if (newLength % 8) throw std::runtime_error("FIR filter length not divisible by 8");
|
||||
|
||||
lengthDiv8 = newLength / 8;
|
||||
length = lengthDiv8 * 8;
|
||||
assert(length == newLength);
|
||||
|
||||
resultDivFactor = uResultDivFactor;
|
||||
resultDivider = (uint)pow(2, resultDivFactor);
|
||||
|
||||
delete[] filterCoeffs;
|
||||
filterCoeffs = new SAMPLETYPE[length];
|
||||
memcpy(filterCoeffs, coeffs, length * sizeof(SAMPLETYPE));
|
||||
}
|
||||
|
||||
|
||||
uint FIRFilter::getLength() const
|
||||
{
|
||||
return length;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Applies the filter to the given sequence of samples.
|
||||
//
|
||||
// Note : The amount of outputted samples is by value of 'filter_length'
|
||||
// smaller than the amount of input samples.
|
||||
uint FIRFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels) const
|
||||
{
|
||||
assert(numChannels == 1 || numChannels == 2);
|
||||
|
||||
assert(length > 0);
|
||||
assert(lengthDiv8 * 8 == length);
|
||||
if (numSamples < length) return 0;
|
||||
assert(resultDivFactor >= 0);
|
||||
if (numChannels == 2)
|
||||
{
|
||||
return evaluateFilterStereo(dest, src, numSamples);
|
||||
} else {
|
||||
return evaluateFilterMono(dest, src, numSamples);
|
||||
}
|
||||
}
|
||||
|
||||
FIRFilter * FIRFilter::newInstance()
|
||||
{
|
||||
uint uExtensions;
|
||||
|
||||
uExtensions = detectCPUextensions();
|
||||
|
||||
// Check if MMX/SSE/3DNow! instruction set extensions supported by CPU
|
||||
|
||||
#ifdef ALLOW_MMX
|
||||
// MMX routines available only with integer sample types
|
||||
if (uExtensions & SUPPORT_MMX)
|
||||
{
|
||||
return ::new FIRFilterMMX;
|
||||
}
|
||||
else
|
||||
#endif // ALLOW_MMX
|
||||
|
||||
#ifdef ALLOW_SSE
|
||||
if (uExtensions & SUPPORT_SSE)
|
||||
{
|
||||
// SSE support
|
||||
return ::new FIRFilterSSE;
|
||||
}
|
||||
else
|
||||
#endif // ALLOW_SSE
|
||||
|
||||
#ifdef ALLOW_3DNOW
|
||||
if (uExtensions & SUPPORT_3DNOW)
|
||||
{
|
||||
// 3DNow! support
|
||||
return ::new FIRFilter3DNow;
|
||||
}
|
||||
else
|
||||
#endif // ALLOW_3DNOW
|
||||
|
||||
{
|
||||
// ISA optimizations not supported, use plain C version
|
||||
return ::new FIRFilter;
|
||||
}
|
||||
}
|
160
libs/soundtouch/FIRFilter.h
Normal file
160
libs/soundtouch/FIRFilter.h
Normal file
@ -0,0 +1,160 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// General FIR digital filter routines with MMX optimization.
|
||||
///
|
||||
/// Note : MMX optimized functions reside in a separate, platform-specific file,
|
||||
/// e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai @ iki.fi
|
||||
/// SoundTouch WWW: http://www.iki.fi/oparviai/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date$
|
||||
// File revision : $Revision$
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 FIRFilter_H
|
||||
#define FIRFilter_H
|
||||
|
||||
#include "STTypes.h"
|
||||
|
||||
namespace soundtouch
|
||||
{
|
||||
|
||||
class FIRFilter
|
||||
{
|
||||
protected:
|
||||
// Number of FIR filter taps
|
||||
uint length;
|
||||
// Number of FIR filter taps divided by 8
|
||||
uint lengthDiv8;
|
||||
|
||||
// Result divider factor in 2^k format
|
||||
uint resultDivFactor;
|
||||
|
||||
// Result divider value.
|
||||
SAMPLETYPE resultDivider;
|
||||
|
||||
// Memory for filter coefficients
|
||||
SAMPLETYPE *filterCoeffs;
|
||||
|
||||
virtual uint evaluateFilterStereo(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
uint numSamples) const;
|
||||
virtual uint evaluateFilterMono(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
uint numSamples) const;
|
||||
|
||||
FIRFilter();
|
||||
|
||||
public:
|
||||
virtual ~FIRFilter();
|
||||
|
||||
static FIRFilter *newInstance();
|
||||
|
||||
/// Applies the filter to the given sequence of samples.
|
||||
/// Note : The amount of outputted samples is by value of 'filter_length'
|
||||
/// smaller than the amount of input samples.
|
||||
///
|
||||
/// \return Number of samples copied to 'dest'.
|
||||
uint evaluate(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
uint numSamples,
|
||||
uint numChannels) const;
|
||||
|
||||
uint getLength() const;
|
||||
|
||||
virtual void setCoefficients(const SAMPLETYPE *coeffs,
|
||||
uint newLength,
|
||||
uint uResultDivFactor);
|
||||
};
|
||||
|
||||
|
||||
// Optional subclasses that implement CPU-specific optimizations:
|
||||
|
||||
#ifdef ALLOW_MMX
|
||||
|
||||
/// Class that implements MMX optimized functions exclusive for 16bit integer samples type.
|
||||
class FIRFilterMMX : public FIRFilter
|
||||
{
|
||||
protected:
|
||||
short *filterCoeffsUnalign;
|
||||
short *filterCoeffsAlign;
|
||||
|
||||
virtual uint evaluateFilterStereo(short *dest, const short *src, uint numSamples) const;
|
||||
public:
|
||||
FIRFilterMMX();
|
||||
~FIRFilterMMX();
|
||||
|
||||
virtual void setCoefficients(const short *coeffs, uint newLength, uint uResultDivFactor);
|
||||
};
|
||||
|
||||
#endif // ALLOW_MMX
|
||||
|
||||
|
||||
#ifdef ALLOW_3DNOW
|
||||
|
||||
/// Class that implements 3DNow! optimized functions exclusive for floating point samples type.
|
||||
class FIRFilter3DNow : public FIRFilter
|
||||
{
|
||||
protected:
|
||||
float *filterCoeffsUnalign;
|
||||
float *filterCoeffsAlign;
|
||||
|
||||
virtual uint evaluateFilterStereo(float *dest, const float *src, uint numSamples) const;
|
||||
public:
|
||||
FIRFilter3DNow();
|
||||
~FIRFilter3DNow();
|
||||
virtual void setCoefficients(const float *coeffs, uint newLength, uint uResultDivFactor);
|
||||
};
|
||||
|
||||
#endif // ALLOW_3DNOW
|
||||
|
||||
|
||||
#ifdef ALLOW_SSE
|
||||
/// Class that implements SSE optimized functions exclusive for floating point samples type.
|
||||
class FIRFilterSSE : public FIRFilter
|
||||
{
|
||||
protected:
|
||||
float *filterCoeffsUnalign;
|
||||
float *filterCoeffsAlign;
|
||||
|
||||
virtual uint evaluateFilterStereo(float *dest, const float *src, uint numSamples) const;
|
||||
public:
|
||||
FIRFilterSSE();
|
||||
~FIRFilterSSE();
|
||||
|
||||
virtual void setCoefficients(const float *coeffs, uint newLength, uint uResultDivFactor);
|
||||
};
|
||||
|
||||
#endif // ALLOW_SSE
|
||||
|
||||
}
|
||||
|
||||
#endif // FIRFilter_H
|
191
libs/soundtouch/README
Normal file
191
libs/soundtouch/README
Normal file
@ -0,0 +1,191 @@
|
||||
SoundTouch sound processing library v1.01
|
||||
=========================================
|
||||
Copyright (c) Olli Parviainen 2002
|
||||
|
||||
A library for changing tempo, pitch and playback rate of digital sound.
|
||||
|
||||
|
||||
SoundStretch sound processing application v1.1
|
||||
==============================================
|
||||
Copyright (c) Olli Parviainen 2002-2003
|
||||
|
||||
A command-line application for changing tempo, pitch and playback rates
|
||||
of WAV sound files. This program also demonstrates how the "SoundTouch"
|
||||
library can be used to process sound in own programs.
|
||||
|
||||
|
||||
SoundStretch Usage Instructions
|
||||
===============================
|
||||
|
||||
SoundStretch Usage syntax:
|
||||
soundstretch infile.wav outfile.wav [switches]
|
||||
|
||||
Where:
|
||||
|
||||
"infile.wav" is the name of the input sound data file (in .WAV audio
|
||||
file format).
|
||||
|
||||
"outfile.wav" is the name of the output sound file where the resulting
|
||||
sound is saved (in .WAV audio file format).
|
||||
|
||||
[switches] are one or more control switches.
|
||||
|
||||
Available control switches are:
|
||||
|
||||
-tempo=n : Change sound tempo by n percents (n = -95.0 .. +5000.0 %)
|
||||
|
||||
-pitch=n : Change sound pitch by n semitones (n = -60.0 .. + 60.0 semitones)
|
||||
|
||||
-rate=n : Change sound playback rate by n percents (n = -95.0 .. +5000.0 %)
|
||||
|
||||
-bpm=n : Detect the Beats-Per-Minute (BPM) rate of the sound and adjust the
|
||||
tempo to meet 'n' BPMs. If this switch is defined, the "-tempo=n"
|
||||
switch value is ignored.
|
||||
|
||||
If "=n" is omitted, i.e. switch "-bpm" is used alone, the
|
||||
program just calculates and displays the BPM rate but doesn't
|
||||
adjust tempo according to the BPM value.
|
||||
|
||||
-quick : Use quicker tempo change algorithm. Gains speed but loses sound
|
||||
quality.
|
||||
|
||||
-naa : Don't use anti-alias filtering in samplerate transposing. Gains
|
||||
speed but loses sound quality.
|
||||
|
||||
-license : Displays the program license text (GPL)
|
||||
|
||||
Notes:
|
||||
* The numerical switch values can be entered using either integer (e.g.
|
||||
"-tempo=123") or decimal (e.g. "-tempo=123.45") numbers.
|
||||
|
||||
* The "-naa" and/or "-quick" switches can be used to reduce CPU usage
|
||||
while compromising some sound quality
|
||||
|
||||
* The BPM detection algorithm works by detecting repeating low-frequency
|
||||
(<250Hz) sound patterns and thus works mostly with most rock/pop music
|
||||
with bass or drum beat. The BPM detection doesn't work on pieces such
|
||||
as classical music without distinct, repeating bass frequency patterns.
|
||||
Also pieces with varying tempo, varying bass patterns or very complex
|
||||
bass patterns (jazz, hiphop) may produce odd BPM readings.
|
||||
|
||||
In cases when the bass pattern drifts a bit around a nominal beat rate
|
||||
(e.g. drummer is again drunken :), the BPM algorithm may report incorrect
|
||||
harmonic one-halft of one-thirdth of the correct BPM value; in such case
|
||||
the system could for example report BPM value of 50 or 100 instead of
|
||||
correct BPM value of 150.
|
||||
|
||||
|
||||
Usage examples:
|
||||
===============
|
||||
|
||||
Example 1
|
||||
=========
|
||||
|
||||
The following command increases tempo of the sound file "originalfile.wav"
|
||||
by 12.5% and saves result to file "destinationfile.wav":
|
||||
|
||||
soundstretch originalfile.wav destinationfile.wav -tempo=12.5
|
||||
|
||||
|
||||
Example 2
|
||||
=========
|
||||
|
||||
The following command decreases the sound pitch (key) of the sound file
|
||||
"orig.wav" by two semitones and saves the result to file "dest.wav":
|
||||
|
||||
soundstretch orig.wav dest.wav -pitch=-2
|
||||
|
||||
|
||||
Example 3
|
||||
=========
|
||||
|
||||
The following command processes the file "orig.wav" by decreasing the
|
||||
sound tempo by 25.3% and increasing the sound pitch (key) by 1.5 semitones.
|
||||
Result is saved to file "dest.wav":
|
||||
|
||||
soundstretch orig.wav dest.wav -tempo=-25.3 -pitch=1.5
|
||||
|
||||
|
||||
Example 4
|
||||
=========
|
||||
|
||||
The following command detects the BPM rate of the file "orig.wav" and
|
||||
adjusts the tempo to match 100 beats per minute. Result is saved to
|
||||
file "dest.wav":
|
||||
|
||||
soundstretch orig.wav dest.wav -bpm=100
|
||||
|
||||
|
||||
|
||||
Building Instructions
|
||||
=====================
|
||||
|
||||
The package contains executable binaries for Win32 platform in the "bin"
|
||||
directory.
|
||||
|
||||
To build the library and application executable for other platforms or to
|
||||
re-build the delivered binaries, run either of the scripts in the package
|
||||
root directory:
|
||||
|
||||
"make-win.bat" for Microsoft Windows environment, or
|
||||
"make-gcc" for GNU/Linux or Unix environment with a gcc compiler.
|
||||
|
||||
|
||||
|
||||
Change History
|
||||
==============
|
||||
|
||||
|
||||
SoundTouch library Change History
|
||||
=================================
|
||||
|
||||
v1.01:
|
||||
- "mmx_gcc.cpp": Added "using namespace std" and removed "return 0" from a
|
||||
function with void return value to fix compiler errors when compiling
|
||||
the library in Solaris environment.
|
||||
|
||||
- Moved file "FIFOSampleBuffer.h" to "include" directory to allow accessing
|
||||
the FIFOSampleBuffer class from external files.
|
||||
|
||||
v1.0: Initial release
|
||||
|
||||
|
||||
SoundStretch application Change History
|
||||
=======================================
|
||||
|
||||
v1.1:
|
||||
- Fixed "Release" settings in Microsoft Visual C++ project file (.dsp)
|
||||
|
||||
- Added beats-per-minute (BPM) detection routine and command-line switch
|
||||
"-bpm"
|
||||
|
||||
v1.01: Initial release
|
||||
|
||||
|
||||
Acknowledgements
|
||||
================
|
||||
|
||||
Many thanks to Stuart Lamble for translating the MMX optimizations from
|
||||
MS Visual C++ syntax into gcc syntax for joy of all Linux users.
|
||||
|
||||
Thanks also to Manish Bajpai, whose WAV file reading routines I've used
|
||||
as base of the WavInFile & WavOutFile classes, that are being used in
|
||||
the soundstrecth program for accessing WAV audio files.
|
||||
|
||||
|
||||
LICENSE:
|
||||
========
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the
|
||||
Free Software Foundation; either version 2 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
This program 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 General Public License
|
||||
for more details.\n"
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
611
libs/soundtouch/RateTransposer.cpp
Normal file
611
libs/soundtouch/RateTransposer.cpp
Normal file
@ -0,0 +1,611 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Sample rate transposer. Changes sample rate by using linear interpolation
|
||||
/// together with anti-alias filtering (first order interpolation with anti-
|
||||
/// alias filtering should be quite adequate for this application)
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai @ iki.fi
|
||||
/// SoundTouch WWW: http://www.iki.fi/oparviai/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date$
|
||||
// File revision : $Revision$
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <memory.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
#include "RateTransposer.h"
|
||||
#include "AAFilter.h"
|
||||
|
||||
using namespace soundtouch;
|
||||
|
||||
|
||||
/// A linear samplerate transposer class that uses integer arithmetics.
|
||||
/// for the transposing.
|
||||
class RateTransposerInteger : public RateTransposer
|
||||
{
|
||||
protected:
|
||||
int iSlopeCount;
|
||||
uint uRate;
|
||||
SAMPLETYPE sPrevSampleL, sPrevSampleR;
|
||||
|
||||
virtual void resetRegisters();
|
||||
|
||||
virtual uint transposeStereo(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
uint numSamples);
|
||||
virtual uint transposeMono(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;
|
||||
float fRateStep;
|
||||
SAMPLETYPE sPrevSampleL, sPrevSampleR;
|
||||
|
||||
virtual void resetRegisters();
|
||||
|
||||
virtual uint transposeStereo(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
uint numSamples);
|
||||
virtual uint transposeMono(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
uint numSamples);
|
||||
|
||||
public:
|
||||
RateTransposerFloat();
|
||||
virtual ~RateTransposerFloat();
|
||||
};
|
||||
|
||||
|
||||
|
||||
#ifndef min
|
||||
#define min(a,b) ((a > b) ? b : a)
|
||||
#define max(a,b) ((a < b) ? b : a)
|
||||
#endif
|
||||
|
||||
RateTransposer *RateTransposer::newInstance()
|
||||
{
|
||||
#ifdef INTEGER_SAMPLES
|
||||
return ::new RateTransposerInteger;
|
||||
#else
|
||||
return ::new RateTransposerFloat;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Constructor
|
||||
RateTransposer::RateTransposer() : FIFOProcessor(&outputBuffer)
|
||||
{
|
||||
uChannels = 2;
|
||||
bUseAAFilter = TRUE;
|
||||
|
||||
// Instantiates the anti-alias filter with default tap length
|
||||
// of 32
|
||||
pAAFilter = new AAFilter(32);
|
||||
}
|
||||
|
||||
|
||||
|
||||
RateTransposer::~RateTransposer()
|
||||
{
|
||||
delete pAAFilter;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Enables/disables the anti-alias filter. Zero to disable, nonzero to enable
|
||||
void RateTransposer::enableAAFilter(const BOOL newMode)
|
||||
{
|
||||
bUseAAFilter = newMode;
|
||||
}
|
||||
|
||||
|
||||
/// Returns nonzero if anti-alias filter is enabled.
|
||||
BOOL RateTransposer::isAAFilterEnabled() const
|
||||
{
|
||||
return bUseAAFilter;
|
||||
}
|
||||
|
||||
|
||||
AAFilter *RateTransposer::getAAFilter() const
|
||||
{
|
||||
return pAAFilter;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets new target uRate. Normal uRate = 1.0, smaller values represent slower
|
||||
// uRate, larger faster uRates.
|
||||
void RateTransposer::setRate(float newRate)
|
||||
{
|
||||
float fCutoff;
|
||||
|
||||
fRate = newRate;
|
||||
|
||||
// design a new anti-alias filter
|
||||
if (newRate > 1.0f)
|
||||
{
|
||||
fCutoff = 0.5f / newRate;
|
||||
}
|
||||
else
|
||||
{
|
||||
fCutoff = 0.5f * newRate;
|
||||
}
|
||||
pAAFilter->setCutoffFreq(fCutoff);
|
||||
}
|
||||
|
||||
|
||||
// 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 'numSamples' pcs of samples from the 'samples' memory position into
|
||||
// the input of the object.
|
||||
void RateTransposer::putSamples(const SAMPLETYPE *samples, uint numSamples)
|
||||
{
|
||||
processSamples(samples, numSamples);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Transposes up the sample rate, causing the observed playback 'rate' of the
|
||||
// sound to decrease
|
||||
void RateTransposer::upsample(const SAMPLETYPE *src, uint numSamples)
|
||||
{
|
||||
int count, sizeTemp, num;
|
||||
|
||||
// If the parameter 'uRate' value is smaller than 'SCALE', first transpose
|
||||
// the samples and then apply the anti-alias filter to remove aliasing.
|
||||
|
||||
// First check that there's enough room in 'storeBuffer'
|
||||
// (+16 is to reserve some slack in the destination buffer)
|
||||
sizeTemp = (int)((float)numSamples / fRate + 16.0f);
|
||||
|
||||
// Transpose the samples, store the result into the end of "storeBuffer"
|
||||
count = transpose(storeBuffer.ptrEnd(sizeTemp), src, numSamples);
|
||||
storeBuffer.putSamples(count);
|
||||
|
||||
// Apply the anti-alias filter to samples in "store output", output the
|
||||
// result to "dest"
|
||||
num = storeBuffer.numSamples();
|
||||
count = pAAFilter->evaluate(outputBuffer.ptrEnd(num),
|
||||
storeBuffer.ptrBegin(), num, uChannels);
|
||||
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 numSamples)
|
||||
{
|
||||
int 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, numSamples);
|
||||
|
||||
// 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, uChannels);
|
||||
|
||||
// Remove the filtered samples from 'storeBuffer'
|
||||
storeBuffer.receiveSamples(count);
|
||||
|
||||
// Transpose the samples (+16 is to reserve some slack in the destination buffer)
|
||||
sizeTemp = (int)((float)numSamples / fRate + 16.0f);
|
||||
count = transpose(outputBuffer.ptrEnd(sizeTemp), tempBuffer.ptrBegin(), count);
|
||||
outputBuffer.putSamples(count);
|
||||
}
|
||||
|
||||
|
||||
// Transposes sample rate by applying anti-alias filter to prevent folding.
|
||||
// Returns amount of samples returned in the "dest" buffer.
|
||||
// The maximum amount of samples that can be returned at a time is set by
|
||||
// the 'set_returnBuffer_size' function.
|
||||
void RateTransposer::processSamples(const SAMPLETYPE *src, uint numSamples)
|
||||
{
|
||||
uint count;
|
||||
uint sizeReq;
|
||||
|
||||
if (numSamples == 0) return;
|
||||
assert(pAAFilter);
|
||||
|
||||
// If anti-alias filter is turned off, simply transpose without applying
|
||||
// the filter
|
||||
if (bUseAAFilter == FALSE)
|
||||
{
|
||||
sizeReq = (int)((float)numSamples / fRate + 1.0f);
|
||||
count = transpose(outputBuffer.ptrEnd(sizeReq), src, numSamples);
|
||||
outputBuffer.putSamples(count);
|
||||
return;
|
||||
}
|
||||
|
||||
// Transpose with anti-alias filter
|
||||
if (fRate < 1.0f)
|
||||
{
|
||||
upsample(src, numSamples);
|
||||
}
|
||||
else
|
||||
{
|
||||
downsample(src, numSamples);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Transposes the sample rate of the given samples using linear interpolation.
|
||||
// Returns the number of samples returned in the "dest" buffer
|
||||
inline uint RateTransposer::transpose(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples)
|
||||
{
|
||||
if (uChannels == 2)
|
||||
{
|
||||
return transposeStereo(dest, src, numSamples);
|
||||
}
|
||||
else
|
||||
{
|
||||
return transposeMono(dest, src, numSamples);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Sets the number of channels, 1 = mono, 2 = stereo
|
||||
void RateTransposer::setChannels(const uint numchannels)
|
||||
{
|
||||
if (uChannels == numchannels) return;
|
||||
|
||||
assert(numchannels == 1 || numchannels == 2);
|
||||
uChannels = numchannels;
|
||||
|
||||
storeBuffer.setChannels(uChannels);
|
||||
tempBuffer.setChannels(uChannels);
|
||||
outputBuffer.setChannels(uChannels);
|
||||
|
||||
// Inits the linear interpolation registers
|
||||
resetRegisters();
|
||||
}
|
||||
|
||||
|
||||
// Clears all the samples in the object
|
||||
void RateTransposer::clear()
|
||||
{
|
||||
outputBuffer.clear();
|
||||
storeBuffer.clear();
|
||||
}
|
||||
|
||||
|
||||
// Returns nonzero if there aren't any samples available for outputting.
|
||||
uint RateTransposer::isEmpty()
|
||||
{
|
||||
int res;
|
||||
|
||||
res = FIFOProcessor::isEmpty();
|
||||
if (res == 0) return 0;
|
||||
return storeBuffer.isEmpty();
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// RateTransposerInteger - integer arithmetic implementation
|
||||
//
|
||||
|
||||
/// fixed-point interpolation routine precision
|
||||
#define SCALE 65536
|
||||
|
||||
// Constructor
|
||||
RateTransposerInteger::RateTransposerInteger() : RateTransposer()
|
||||
{
|
||||
// call these here as these are virtual functions; calling these
|
||||
// from the base class constructor wouldn't execute the overloaded
|
||||
// versions (<master yoda>peculiar C++ can be</my>).
|
||||
resetRegisters();
|
||||
setRate(1.0f);
|
||||
}
|
||||
|
||||
|
||||
RateTransposerInteger::~RateTransposerInteger()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void RateTransposerInteger::resetRegisters()
|
||||
{
|
||||
iSlopeCount = 0;
|
||||
sPrevSampleL =
|
||||
sPrevSampleR = 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
|
||||
uint RateTransposerInteger::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples)
|
||||
{
|
||||
unsigned int i, used;
|
||||
LONG_SAMPLETYPE temp, vol1;
|
||||
|
||||
used = 0;
|
||||
i = 0;
|
||||
|
||||
// Process the last sample saved from the previous call first...
|
||||
while (iSlopeCount <= SCALE)
|
||||
{
|
||||
vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount);
|
||||
temp = vol1 * sPrevSampleL + iSlopeCount * src[0];
|
||||
dest[i] = (SAMPLETYPE)(temp / SCALE);
|
||||
i++;
|
||||
iSlopeCount += uRate;
|
||||
}
|
||||
// now always (iSlopeCount > SCALE)
|
||||
iSlopeCount -= SCALE;
|
||||
|
||||
while (1)
|
||||
{
|
||||
while (iSlopeCount > SCALE)
|
||||
{
|
||||
iSlopeCount -= SCALE;
|
||||
used ++;
|
||||
if (used >= numSamples - 1) goto end;
|
||||
}
|
||||
vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount);
|
||||
temp = src[used] * vol1 + iSlopeCount * src[used + 1];
|
||||
dest[i] = (SAMPLETYPE)(temp / SCALE);
|
||||
|
||||
i++;
|
||||
iSlopeCount += uRate;
|
||||
}
|
||||
end:
|
||||
// Store the last sample for the next round
|
||||
sPrevSampleL = src[numSamples - 1];
|
||||
|
||||
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
|
||||
uint RateTransposerInteger::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples)
|
||||
{
|
||||
unsigned int srcPos, i, used;
|
||||
LONG_SAMPLETYPE temp, vol1;
|
||||
|
||||
if (numSamples == 0) return 0; // no samples, no work
|
||||
|
||||
used = 0;
|
||||
i = 0;
|
||||
|
||||
// Process the last sample saved from the sPrevSampleLious call first...
|
||||
while (iSlopeCount <= SCALE)
|
||||
{
|
||||
vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount);
|
||||
temp = vol1 * sPrevSampleL + iSlopeCount * src[0];
|
||||
dest[2 * i] = (SAMPLETYPE)(temp / SCALE);
|
||||
temp = vol1 * sPrevSampleR + iSlopeCount * src[1];
|
||||
dest[2 * i + 1] = (SAMPLETYPE)(temp / SCALE);
|
||||
i++;
|
||||
iSlopeCount += uRate;
|
||||
}
|
||||
// now always (iSlopeCount > SCALE)
|
||||
iSlopeCount -= SCALE;
|
||||
|
||||
while (1)
|
||||
{
|
||||
while (iSlopeCount > SCALE)
|
||||
{
|
||||
iSlopeCount -= SCALE;
|
||||
used ++;
|
||||
if (used >= numSamples - 1) goto end;
|
||||
}
|
||||
srcPos = 2 * used;
|
||||
vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount);
|
||||
temp = src[srcPos] * vol1 + iSlopeCount * src[srcPos + 2];
|
||||
dest[2 * i] = (SAMPLETYPE)(temp / SCALE);
|
||||
temp = src[srcPos + 1] * vol1 + iSlopeCount * src[srcPos + 3];
|
||||
dest[2 * i + 1] = (SAMPLETYPE)(temp / SCALE);
|
||||
|
||||
i++;
|
||||
iSlopeCount += uRate;
|
||||
}
|
||||
end:
|
||||
// Store the last sample for the next round
|
||||
sPrevSampleL = src[2 * numSamples - 2];
|
||||
sPrevSampleR = src[2 * numSamples - 1];
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
// Sets new target uRate. Normal uRate = 1.0, smaller values represent slower
|
||||
// uRate, larger faster uRates.
|
||||
void RateTransposerInteger::setRate(float newRate)
|
||||
{
|
||||
uRate = (int)(newRate * SCALE + 0.5f);
|
||||
RateTransposer::setRate(newRate);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// RateTransposerFloat - floating point arithmetic implementation
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Constructor
|
||||
RateTransposerFloat::RateTransposerFloat() : RateTransposer()
|
||||
{
|
||||
// call these here as these are virtual functions; calling these
|
||||
// from the base class constructor wouldn't execute the overloaded
|
||||
// versions (<master yoda>peculiar C++ can be</my>).
|
||||
resetRegisters();
|
||||
setRate(1.0f);
|
||||
}
|
||||
|
||||
|
||||
RateTransposerFloat::~RateTransposerFloat()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void RateTransposerFloat::resetRegisters()
|
||||
{
|
||||
fSlopeCount = 0;
|
||||
sPrevSampleL =
|
||||
sPrevSampleR = 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
|
||||
uint RateTransposerFloat::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples)
|
||||
{
|
||||
unsigned int i, used;
|
||||
|
||||
used = 0;
|
||||
i = 0;
|
||||
|
||||
// Process the last sample saved from the previous call first...
|
||||
while (fSlopeCount <= 1.0f)
|
||||
{
|
||||
dest[i] = (SAMPLETYPE)((1.0f - fSlopeCount) * sPrevSampleL + fSlopeCount * src[0]);
|
||||
i++;
|
||||
fSlopeCount += fRate;
|
||||
}
|
||||
fSlopeCount -= 1.0f;
|
||||
|
||||
while (1)
|
||||
{
|
||||
while (fSlopeCount > 1.0f)
|
||||
{
|
||||
fSlopeCount -= 1.0f;
|
||||
used ++;
|
||||
if (used >= numSamples - 1) goto end;
|
||||
}
|
||||
dest[i] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[used] + fSlopeCount * src[used + 1]);
|
||||
i++;
|
||||
fSlopeCount += fRate;
|
||||
}
|
||||
end:
|
||||
// Store the last sample for the next round
|
||||
sPrevSampleL = src[numSamples - 1];
|
||||
|
||||
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
|
||||
uint RateTransposerFloat::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples)
|
||||
{
|
||||
unsigned int srcPos, i, used;
|
||||
|
||||
if (numSamples == 0) return 0; // no samples, no work
|
||||
|
||||
used = 0;
|
||||
i = 0;
|
||||
|
||||
// Process the last sample saved from the sPrevSampleLious call first...
|
||||
while (fSlopeCount <= 1.0f)
|
||||
{
|
||||
dest[2 * i] = (SAMPLETYPE)((1.0f - fSlopeCount) * sPrevSampleL + fSlopeCount * src[0]);
|
||||
dest[2 * i + 1] = (SAMPLETYPE)((1.0f - fSlopeCount) * sPrevSampleR + fSlopeCount * src[1]);
|
||||
i++;
|
||||
fSlopeCount += fRate;
|
||||
}
|
||||
// now always (iSlopeCount > 1.0f)
|
||||
fSlopeCount -= 1.0f;
|
||||
|
||||
while (1)
|
||||
{
|
||||
while (fSlopeCount > 1.0f)
|
||||
{
|
||||
fSlopeCount -= 1.0f;
|
||||
used ++;
|
||||
if (used >= numSamples - 1) goto end;
|
||||
}
|
||||
srcPos = 2 * used;
|
||||
|
||||
dest[2 * i] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[srcPos]
|
||||
+ fSlopeCount * src[srcPos + 2]);
|
||||
dest[2 * i + 1] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[srcPos + 1]
|
||||
+ fSlopeCount * src[srcPos + 3]);
|
||||
|
||||
i++;
|
||||
fSlopeCount += fRate;
|
||||
}
|
||||
end:
|
||||
// Store the last sample for the next round
|
||||
sPrevSampleL = src[2 * numSamples - 2];
|
||||
sPrevSampleR = src[2 * numSamples - 1];
|
||||
|
||||
return i;
|
||||
}
|
158
libs/soundtouch/RateTransposer.h
Normal file
158
libs/soundtouch/RateTransposer.h
Normal file
@ -0,0 +1,158 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Sample rate transposer. Changes sample rate by using linear interpolation
|
||||
/// together with anti-alias filtering (first order interpolation with anti-
|
||||
/// alias filtering should be quite adequate for this application).
|
||||
///
|
||||
/// Use either of the derived classes of 'RateTransposerInteger' or
|
||||
/// 'RateTransposerFloat' for corresponding integer/floating point tranposing
|
||||
/// algorithm implementation.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai @ iki.fi
|
||||
/// SoundTouch WWW: http://www.iki.fi/oparviai/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date$
|
||||
// File revision : $Revision$
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 RateTransposer_H
|
||||
#define RateTransposer_H
|
||||
|
||||
#include "AAFilter.h"
|
||||
#include "FIFOSamplePipe.h"
|
||||
#include "FIFOSampleBuffer.h"
|
||||
|
||||
#include "STTypes.h"
|
||||
|
||||
namespace soundtouch
|
||||
{
|
||||
|
||||
/// 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
|
||||
{
|
||||
protected:
|
||||
/// Anti-alias filter object
|
||||
AAFilter *pAAFilter;
|
||||
|
||||
float fRate;
|
||||
|
||||
uint uChannels;
|
||||
|
||||
/// Buffer for collecting samples to feed the anti-alias filter between
|
||||
/// two batches
|
||||
FIFOSampleBuffer storeBuffer;
|
||||
|
||||
/// Buffer for keeping samples between transposing & anti-alias filter
|
||||
FIFOSampleBuffer tempBuffer;
|
||||
|
||||
/// Output sample buffer
|
||||
FIFOSampleBuffer outputBuffer;
|
||||
|
||||
BOOL bUseAAFilter;
|
||||
|
||||
void init();
|
||||
|
||||
virtual void resetRegisters() = 0;
|
||||
|
||||
virtual uint transposeStereo(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
uint numSamples) = 0;
|
||||
virtual uint transposeMono(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
uint numSamples) = 0;
|
||||
uint transpose(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
uint numSamples);
|
||||
|
||||
void flushStoreBuffer();
|
||||
|
||||
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.
|
||||
/// Returns amount of samples returned in the "dest" buffer.
|
||||
/// The maximum amount of samples that can be returned at a time is set by
|
||||
/// the 'set_returnBuffer_size' function.
|
||||
void processSamples(const SAMPLETYPE *src,
|
||||
uint numSamples);
|
||||
|
||||
RateTransposer();
|
||||
|
||||
public:
|
||||
virtual ~RateTransposer();
|
||||
|
||||
/// 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
|
||||
/// integer ot floating point arithmetics are to be used.
|
||||
static RateTransposer *newInstance();
|
||||
|
||||
/// Returns the output buffer object
|
||||
FIFOSamplePipe *getOutput() { return &outputBuffer; };
|
||||
|
||||
/// Returns the store buffer object
|
||||
FIFOSamplePipe *getStore() { return &storeBuffer; };
|
||||
|
||||
/// Return anti-alias filter object
|
||||
AAFilter *getAAFilter() const;
|
||||
|
||||
/// Enables/disables the anti-alias filter. Zero to disable, nonzero to enable
|
||||
void enableAAFilter(BOOL newMode);
|
||||
|
||||
/// Returns nonzero if anti-alias filter is enabled.
|
||||
BOOL isAAFilterEnabled() const;
|
||||
|
||||
/// Sets new target rate. Normal rate = 1.0, smaller values represent slower
|
||||
/// rate, larger faster rates.
|
||||
virtual void setRate(float newRate);
|
||||
|
||||
/// Sets the number of channels, 1 = mono, 2 = stereo
|
||||
void setChannels(uint channels);
|
||||
|
||||
/// Adds 'numSamples' pcs of samples from the 'samples' memory position into
|
||||
/// the input of the object.
|
||||
void putSamples(const SAMPLETYPE *samples, uint numSamples);
|
||||
|
||||
/// Clears all the samples in the object
|
||||
void clear();
|
||||
|
||||
/// Returns nonzero if there aren't any samples available for outputting.
|
||||
uint isEmpty();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
23
libs/soundtouch/SConscript
Normal file
23
libs/soundtouch/SConscript
Normal file
@ -0,0 +1,23 @@
|
||||
# -*- python -*-
|
||||
|
||||
import glob
|
||||
|
||||
soundtouch_files = Split("""
|
||||
AAFilter.cpp
|
||||
FIFOSampleBuffer.cpp
|
||||
FIRFilter.cpp
|
||||
RateTransposer.cpp
|
||||
SoundTouch.cpp
|
||||
TDStretch.cpp
|
||||
mmx_gcc.cpp
|
||||
cpu_detect_x86_gcc.cpp
|
||||
""")
|
||||
|
||||
Import('env')
|
||||
st = env.Copy()
|
||||
st.Append(CCFLAGS="-DHAVE_CONFIG_H -D_REENTRANT -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE")
|
||||
libst = st.StaticLibrary('soundtouch', soundtouch_files)
|
||||
Default(libst)
|
||||
|
||||
env.Alias('tarball', env.Distribute (env['DISTTREE'],
|
||||
[ 'SConscript'] + soundtouch_files + glob.glob('*.h')))
|
110
libs/soundtouch/STTypes.h
Normal file
110
libs/soundtouch/STTypes.h
Normal file
@ -0,0 +1,110 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Common type definitions for SoundTouch audio processing library.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai @ iki.fi
|
||||
/// SoundTouch WWW: http://www.iki.fi/oparviai/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date$
|
||||
// File revision : $Revision$
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 STTypes_H
|
||||
#define STTypes_H
|
||||
|
||||
typedef unsigned int uint;
|
||||
typedef unsigned long ulong;
|
||||
|
||||
#ifndef _WINDEF_
|
||||
// if these aren't defined already by Windows headers, define now
|
||||
|
||||
typedef unsigned int BOOL;
|
||||
|
||||
#define FALSE 0
|
||||
#define TRUE 1
|
||||
|
||||
#endif // _WINDEF_
|
||||
|
||||
|
||||
namespace soundtouch
|
||||
{
|
||||
/// Enable one of the following defines to choose either 16bit integer or
|
||||
/// 32bit float sample type. If you don't have opinion, using integer samples
|
||||
/// is generally faster.
|
||||
/// #define INTEGER_SAMPLES //< 16bit integer samples
|
||||
#define FLOAT_SAMPLES //< 32bit float samples
|
||||
|
||||
|
||||
/// Define this to allow CPU-specific assembler optimizations. Notice that
|
||||
/// having this enabled on non-x86 platforms doesn't matter; the compiler can
|
||||
/// drop unsupported extensions on different platforms automatically.
|
||||
/// However, if you're having difficulties getting the optimized routines
|
||||
/// compiled with your compler (e.g. some gcc compiler versions may be picky),
|
||||
/// you may wish to disable the optimizations to make the library compile.
|
||||
#define ALLOW_OPTIMIZATIONS 1
|
||||
|
||||
|
||||
#ifdef INTEGER_SAMPLES
|
||||
// 16bit integer sample type
|
||||
typedef short SAMPLETYPE;
|
||||
// data type for sample accumulation: Use 32bit integer to prevent overflows
|
||||
typedef long LONG_SAMPLETYPE;
|
||||
|
||||
#ifdef FLOAT_SAMPLES
|
||||
// check that only one sample type is defined
|
||||
#error "conflicting sample types defined"
|
||||
#endif // FLOAT_SAMPLES
|
||||
|
||||
#ifdef ALLOW_OPTIMIZATIONS
|
||||
#if WIN32 || __i386__
|
||||
// Allow MMX optimizations
|
||||
#define ALLOW_MMX 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
// floating point samples
|
||||
typedef float SAMPLETYPE;
|
||||
// data type for sample accumulation: Use double to utilize full precision.
|
||||
typedef double LONG_SAMPLETYPE;
|
||||
|
||||
#ifdef ALLOW_OPTIMIZATIONS
|
||||
#ifdef WIN32
|
||||
// Allow 3DNow! and SSE optimizations
|
||||
#define ALLOW_3DNOW 1
|
||||
#define ALLOW_SSE 1
|
||||
#endif // WIN32
|
||||
#endif
|
||||
|
||||
#endif // INTEGER_SAMPLES
|
||||
};
|
||||
|
||||
#endif
|
472
libs/soundtouch/SoundTouch.cpp
Normal file
472
libs/soundtouch/SoundTouch.cpp
Normal file
@ -0,0 +1,472 @@
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// SoundTouch - main class for tempo/pitch/rate adjusting routines.
|
||||
///
|
||||
/// Notes:
|
||||
/// - Initialize the SoundTouch object instance by setting up the sound stream
|
||||
/// parameters with functions 'setSampleRate' and 'setChannels', then set
|
||||
/// desired tempo/pitch/rate settings with the corresponding functions.
|
||||
///
|
||||
/// - The SoundTouch class behaves like a first-in-first-out pipeline: The
|
||||
/// samples that are to be processed are fed into one of the pipe by calling
|
||||
/// function 'putSamples', while the ready processed samples can be read
|
||||
/// from the other end of the pipeline with function 'receiveSamples'.
|
||||
///
|
||||
/// - The SoundTouch processing classes require certain sized 'batches' of
|
||||
/// samples in order to process the sound. For this reason the classes buffer
|
||||
/// incoming samples until there are enough of samples available for
|
||||
/// processing, then they carry out the processing step and consequently
|
||||
/// make the processed samples available for outputting.
|
||||
///
|
||||
/// - For the above reason, the processing routines introduce a certain
|
||||
/// 'latency' between the input and output, so that the samples input to
|
||||
/// SoundTouch may not be immediately available in the output, and neither
|
||||
/// the amount of outputtable samples may not immediately be in direct
|
||||
/// relationship with the amount of previously input samples.
|
||||
///
|
||||
/// - The tempo/pitch/rate control parameters can be altered during processing.
|
||||
/// Please notice though that they aren't currently protected by semaphores,
|
||||
/// so in multi-thread application external semaphore protection may be
|
||||
/// required.
|
||||
///
|
||||
/// - This class utilizes classes 'TDStretch' for tempo change (without modifying
|
||||
/// pitch) and 'RateTransposer' for changing the playback rate (that is, both
|
||||
/// tempo and pitch in the same ratio) of the sound. The third available control
|
||||
/// 'pitch' (change pitch but maintain tempo) is produced by a combination of
|
||||
/// combining the two other controls.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai @ iki.fi
|
||||
/// SoundTouch WWW: http://www.iki.fi/oparviai/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date$
|
||||
// File revision : $Revision$
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <memory.h>
|
||||
#include <math.h>
|
||||
#include <stdexcept>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "SoundTouch.h"
|
||||
#include "TDStretch.h"
|
||||
#include "RateTransposer.h"
|
||||
#include "cpu_detect.h"
|
||||
|
||||
using namespace soundtouch;
|
||||
|
||||
/// Print library version string
|
||||
extern "C" void soundtouch_ac_test()
|
||||
{
|
||||
printf("SoundTouch Version: %s\n",SOUNDTOUCH_VERSION);
|
||||
}
|
||||
|
||||
|
||||
SoundTouch::SoundTouch()
|
||||
{
|
||||
// Initialize rate transposer and tempo changer instances
|
||||
|
||||
pRateTransposer = RateTransposer::newInstance();
|
||||
pTDStretch = TDStretch::newInstance();
|
||||
|
||||
setOutPipe(pTDStretch);
|
||||
|
||||
rate = tempo = 0;
|
||||
|
||||
virtualPitch =
|
||||
virtualRate =
|
||||
virtualTempo = 1.0;
|
||||
|
||||
calcEffectiveRateAndTempo();
|
||||
|
||||
channels = 0;
|
||||
bSrateSet = FALSE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
SoundTouch::~SoundTouch()
|
||||
{
|
||||
delete pRateTransposer;
|
||||
delete pTDStretch;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Get SoundTouch library version string
|
||||
const char *SoundTouch::getVersionString()
|
||||
{
|
||||
static const char *_version = SOUNDTOUCH_VERSION;
|
||||
|
||||
return _version;
|
||||
}
|
||||
|
||||
|
||||
/// Get SoundTouch library version Id
|
||||
uint SoundTouch::getVersionId()
|
||||
{
|
||||
return SOUNDTOUCH_VERSION_ID;
|
||||
}
|
||||
|
||||
|
||||
// Sets the number of channels, 1 = mono, 2 = stereo
|
||||
void SoundTouch::setChannels(uint numChannels)
|
||||
{
|
||||
if (numChannels != 1 && numChannels != 2)
|
||||
{
|
||||
throw std::runtime_error("Illegal number of channels");
|
||||
}
|
||||
channels = numChannels;
|
||||
pRateTransposer->setChannels(numChannels);
|
||||
pTDStretch->setChannels(numChannels);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets new rate control value. Normal rate = 1.0, smaller values
|
||||
// represent slower rate, larger faster rates.
|
||||
void SoundTouch::setRate(float newRate)
|
||||
{
|
||||
virtualRate = newRate;
|
||||
calcEffectiveRateAndTempo();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets new rate control value as a difference in percents compared
|
||||
// to the original rate (-50 .. +100 %)
|
||||
void SoundTouch::setRateChange(float newRate)
|
||||
{
|
||||
virtualRate = 1.0f + 0.01f * newRate;
|
||||
calcEffectiveRateAndTempo();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets new tempo control value. Normal tempo = 1.0, smaller values
|
||||
// represent slower tempo, larger faster tempo.
|
||||
void SoundTouch::setTempo(float newTempo)
|
||||
{
|
||||
virtualTempo = newTempo;
|
||||
calcEffectiveRateAndTempo();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets new tempo control value as a difference in percents compared
|
||||
// to the original tempo (-50 .. +100 %)
|
||||
void SoundTouch::setTempoChange(float newTempo)
|
||||
{
|
||||
virtualTempo = 1.0f + 0.01f * newTempo;
|
||||
calcEffectiveRateAndTempo();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets new pitch control value. Original pitch = 1.0, smaller values
|
||||
// represent lower pitches, larger values higher pitch.
|
||||
void SoundTouch::setPitch(float newPitch)
|
||||
{
|
||||
virtualPitch = newPitch;
|
||||
calcEffectiveRateAndTempo();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets pitch change in octaves compared to the original pitch
|
||||
// (-1.00 .. +1.00)
|
||||
void SoundTouch::setPitchOctaves(float newPitch)
|
||||
{
|
||||
virtualPitch = (float)exp(0.69314718056f * newPitch);
|
||||
calcEffectiveRateAndTempo();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets pitch change in semi-tones compared to the original pitch
|
||||
// (-12 .. +12)
|
||||
void SoundTouch::setPitchSemiTones(int newPitch)
|
||||
{
|
||||
setPitchOctaves((float)newPitch / 12.0f);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void SoundTouch::setPitchSemiTones(float newPitch)
|
||||
{
|
||||
setPitchOctaves(newPitch / 12.0f);
|
||||
}
|
||||
|
||||
|
||||
// Calculates 'effective' rate and tempo values from the
|
||||
// nominal control values.
|
||||
void SoundTouch::calcEffectiveRateAndTempo()
|
||||
{
|
||||
float oldTempo = tempo;
|
||||
float oldRate = rate;
|
||||
|
||||
tempo = virtualTempo / virtualPitch;
|
||||
rate = virtualPitch * virtualRate;
|
||||
|
||||
if (rate != oldRate) pRateTransposer->setRate(rate);
|
||||
if (tempo != oldTempo) pTDStretch->setTempo(tempo);
|
||||
|
||||
if (rate > 1.0f)
|
||||
{
|
||||
if (output != pRateTransposer)
|
||||
{
|
||||
FIFOSamplePipe *transOut;
|
||||
|
||||
assert(output == pTDStretch);
|
||||
// move samples in the current output buffer to the output of pRateTransposer
|
||||
transOut = pRateTransposer->getOutput();
|
||||
transOut->moveSamples(*output);
|
||||
// move samples in tempo changer's input to pitch transposer's input
|
||||
pRateTransposer->moveSamples(*pTDStretch->getInput());
|
||||
|
||||
output = pRateTransposer;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (output != pTDStretch)
|
||||
{
|
||||
FIFOSamplePipe *tempoOut;
|
||||
|
||||
assert(output == pRateTransposer);
|
||||
// move samples in the current output buffer to the output of pTDStretch
|
||||
tempoOut = pTDStretch->getOutput();
|
||||
tempoOut->moveSamples(*output);
|
||||
// move samples in pitch transposer's store buffer to tempo changer's input
|
||||
pTDStretch->moveSamples(*pRateTransposer->getStore());
|
||||
|
||||
output = pTDStretch;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Sets sample rate.
|
||||
void SoundTouch::setSampleRate(uint srate)
|
||||
{
|
||||
bSrateSet = TRUE;
|
||||
// set sample rate, leave other tempo changer parameters as they are.
|
||||
pTDStretch->setParameters(srate);
|
||||
}
|
||||
|
||||
|
||||
// Adds 'numSamples' pcs of samples from the 'samples' memory position into
|
||||
// the input of the object.
|
||||
void SoundTouch::putSamples(const SAMPLETYPE *samples, uint numSamples)
|
||||
{
|
||||
if (bSrateSet == FALSE)
|
||||
{
|
||||
throw std::runtime_error("SoundTouch : Sample rate not defined");
|
||||
}
|
||||
else if (channels == 0)
|
||||
{
|
||||
throw std::runtime_error("SoundTouch : Number of channels not defined");
|
||||
}
|
||||
|
||||
// Transpose the rate of the new samples if necessary
|
||||
if (rate == 1.0f)
|
||||
{
|
||||
// The rate value is same as the original, simply evaluate the tempo changer.
|
||||
assert(output == pTDStretch);
|
||||
if (pRateTransposer->isEmpty() == 0)
|
||||
{
|
||||
// yet flush the last samples in the pitch transposer buffer
|
||||
// (may happen if 'rate' changes from a non-zero value to zero)
|
||||
pTDStretch->moveSamples(*pRateTransposer);
|
||||
}
|
||||
pTDStretch->putSamples(samples, numSamples);
|
||||
}
|
||||
else if (rate < 1.0f)
|
||||
{
|
||||
// transpose the rate down, output the transposed sound to tempo changer buffer
|
||||
assert(output == pTDStretch);
|
||||
pRateTransposer->putSamples(samples, numSamples);
|
||||
pTDStretch->moveSamples(*pRateTransposer);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(rate > 1.0f);
|
||||
// evaluate the tempo changer, then transpose the rate up,
|
||||
assert(output == pRateTransposer);
|
||||
pTDStretch->putSamples(samples, numSamples);
|
||||
pRateTransposer->moveSamples(*pTDStretch);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Flushes the last samples from the processing pipeline to the output.
|
||||
// Clears also the internal processing buffers.
|
||||
//
|
||||
// Note: This function is meant for extracting the last samples of a sound
|
||||
// stream. This function may introduce additional blank samples in the end
|
||||
// of the sound stream, and thus it's not recommended to call this function
|
||||
// in the middle of a sound stream.
|
||||
void SoundTouch::flush()
|
||||
{
|
||||
int i;
|
||||
uint nOut;
|
||||
SAMPLETYPE buff[128];
|
||||
|
||||
nOut = numSamples();
|
||||
|
||||
memset(buff, 0, 128 * sizeof(SAMPLETYPE));
|
||||
// "Push" the last active samples out from the processing pipeline by
|
||||
// feeding blank samples into the processing pipeline until new,
|
||||
// processed samples appear in the output (not however, more than
|
||||
// 8ksamples in any case)
|
||||
for (i = 0; i < 128; i ++)
|
||||
{
|
||||
putSamples(buff, 64);
|
||||
if (numSamples() != nOut) break; // new samples have appeared in the output!
|
||||
}
|
||||
|
||||
// Clear working buffers
|
||||
pRateTransposer->clear();
|
||||
pTDStretch->clearInput();
|
||||
// yet leave the 'tempoChanger' output intouched as that's where the
|
||||
// flushed samples are!
|
||||
}
|
||||
|
||||
|
||||
// Changes a setting controlling the processing system behaviour. See the
|
||||
// 'SETTING_...' defines for available setting ID's.
|
||||
BOOL SoundTouch::setSetting(uint settingId, uint value)
|
||||
{
|
||||
uint sampleRate, sequenceMs, seekWindowMs, overlapMs;
|
||||
|
||||
// read current tdstretch routine parameters
|
||||
pTDStretch->getParameters(&sampleRate, &sequenceMs, &seekWindowMs, &overlapMs);
|
||||
|
||||
switch (settingId)
|
||||
{
|
||||
case SETTING_USE_AA_FILTER :
|
||||
// enables / disabless anti-alias filter
|
||||
pRateTransposer->enableAAFilter((value != 0) ? TRUE : FALSE);
|
||||
return TRUE;
|
||||
|
||||
case SETTING_AA_FILTER_LENGTH :
|
||||
// sets anti-alias filter length
|
||||
pRateTransposer->getAAFilter()->setLength(value);
|
||||
return TRUE;
|
||||
|
||||
case SETTING_USE_QUICKSEEK :
|
||||
// enables / disables tempo routine quick seeking algorithm
|
||||
pTDStretch->enableQuickSeek((value != 0) ? TRUE : FALSE);
|
||||
return TRUE;
|
||||
|
||||
case SETTING_SEQUENCE_MS:
|
||||
// change time-stretch sequence duration parameter
|
||||
pTDStretch->setParameters(sampleRate, value, seekWindowMs, overlapMs);
|
||||
return TRUE;
|
||||
|
||||
case SETTING_SEEKWINDOW_MS:
|
||||
// change time-stretch seek window length parameter
|
||||
pTDStretch->setParameters(sampleRate, sequenceMs, value, overlapMs);
|
||||
return TRUE;
|
||||
|
||||
case SETTING_OVERLAP_MS:
|
||||
// change time-stretch overlap length parameter
|
||||
pTDStretch->setParameters(sampleRate, sequenceMs, seekWindowMs, value);
|
||||
return TRUE;
|
||||
|
||||
default :
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Reads a setting controlling the processing system behaviour. See the
|
||||
// 'SETTING_...' defines for available setting ID's.
|
||||
//
|
||||
// Returns the setting value.
|
||||
uint SoundTouch::getSetting(uint settingId) const
|
||||
{
|
||||
uint temp;
|
||||
|
||||
switch (settingId)
|
||||
{
|
||||
case SETTING_USE_AA_FILTER :
|
||||
return pRateTransposer->isAAFilterEnabled();
|
||||
|
||||
case SETTING_AA_FILTER_LENGTH :
|
||||
return pRateTransposer->getAAFilter()->getLength();
|
||||
|
||||
case SETTING_USE_QUICKSEEK :
|
||||
return pTDStretch->isQuickSeekEnabled();
|
||||
|
||||
case SETTING_SEQUENCE_MS:
|
||||
pTDStretch->getParameters(NULL, &temp, NULL, NULL);
|
||||
return temp;
|
||||
|
||||
case SETTING_SEEKWINDOW_MS:
|
||||
pTDStretch->getParameters(NULL, NULL, &temp, NULL);
|
||||
return temp;
|
||||
|
||||
case SETTING_OVERLAP_MS:
|
||||
pTDStretch->getParameters(NULL, NULL, NULL, &temp);
|
||||
return temp;
|
||||
|
||||
default :
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Clears all the samples in the object's output and internal processing
|
||||
// buffers.
|
||||
void SoundTouch::clear()
|
||||
{
|
||||
pRateTransposer->clear();
|
||||
pTDStretch->clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Returns number of samples currently unprocessed.
|
||||
uint SoundTouch::numUnprocessedSamples() const
|
||||
{
|
||||
FIFOSamplePipe * psp;
|
||||
if (pTDStretch)
|
||||
{
|
||||
psp = pTDStretch->getInput();
|
||||
if (psp)
|
||||
{
|
||||
return psp->numSamples();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
252
libs/soundtouch/SoundTouch.h
Normal file
252
libs/soundtouch/SoundTouch.h
Normal file
@ -0,0 +1,252 @@
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// SoundTouch - main class for tempo/pitch/rate adjusting routines.
|
||||
///
|
||||
/// Notes:
|
||||
/// - Initialize the SoundTouch object instance by setting up the sound stream
|
||||
/// parameters with functions 'setSampleRate' and 'setChannels', then set
|
||||
/// desired tempo/pitch/rate settings with the corresponding functions.
|
||||
///
|
||||
/// - The SoundTouch class behaves like a first-in-first-out pipeline: The
|
||||
/// samples that are to be processed are fed into one of the pipe by calling
|
||||
/// function 'putSamples', while the ready processed samples can be read
|
||||
/// from the other end of the pipeline with function 'receiveSamples'.
|
||||
///
|
||||
/// - The SoundTouch processing classes require certain sized 'batches' of
|
||||
/// samples in order to process the sound. For this reason the classes buffer
|
||||
/// incoming samples until there are enough of samples available for
|
||||
/// processing, then they carry out the processing step and consequently
|
||||
/// make the processed samples available for outputting.
|
||||
///
|
||||
/// - For the above reason, the processing routines introduce a certain
|
||||
/// 'latency' between the input and output, so that the samples input to
|
||||
/// SoundTouch may not be immediately available in the output, and neither
|
||||
/// the amount of outputtable samples may not immediately be in direct
|
||||
/// relationship with the amount of previously input samples.
|
||||
///
|
||||
/// - The tempo/pitch/rate control parameters can be altered during processing.
|
||||
/// Please notice though that they aren't currently protected by semaphores,
|
||||
/// so in multi-thread application external semaphore protection may be
|
||||
/// required.
|
||||
///
|
||||
/// - This class utilizes classes 'TDStretch' for tempo change (without modifying
|
||||
/// pitch) and 'RateTransposer' for changing the playback rate (that is, both
|
||||
/// tempo and pitch in the same ratio) of the sound. The third available control
|
||||
/// 'pitch' (change pitch but maintain tempo) is produced by a combination of
|
||||
/// combining the two other controls.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai @ iki.fi
|
||||
/// SoundTouch WWW: http://www.iki.fi/oparviai/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date$
|
||||
// File revision : $Revision$
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 SoundTouch_H
|
||||
#define SoundTouch_H
|
||||
|
||||
#include "FIFOSamplePipe.h"
|
||||
#include "STTypes.h"
|
||||
|
||||
namespace soundtouch
|
||||
{
|
||||
|
||||
/// Soundtouch library version string
|
||||
#define SOUNDTOUCH_VERSION "1.3.0"
|
||||
|
||||
/// SoundTouch library version id
|
||||
#define SOUNDTOUCH_VERSION_ID 010300
|
||||
|
||||
//
|
||||
// Available setting IDs for the 'setSetting' & 'get_setting' functions:
|
||||
|
||||
/// Enable/disable anti-alias filter in pitch transposer (0 = disable)
|
||||
#define SETTING_USE_AA_FILTER 0
|
||||
|
||||
/// Pitch transposer anti-alias filter length (8 .. 128 taps, default = 32)
|
||||
#define SETTING_AA_FILTER_LENGTH 1
|
||||
|
||||
/// Enable/disable quick seeking algorithm in tempo changer routine
|
||||
/// (enabling quick seeking lowers CPU utilization but causes a minor sound
|
||||
/// quality compromising)
|
||||
#define SETTING_USE_QUICKSEEK 2
|
||||
|
||||
/// Time-stretch algorithm single processing sequence length in milliseconds. This determines
|
||||
/// to how long sequences the original sound is chopped in the time-stretch algorithm.
|
||||
/// See "STTypes.h" or README for more information.
|
||||
#define SETTING_SEQUENCE_MS 3
|
||||
|
||||
/// Time-stretch algorithm seeking window length in milliseconds for algorithm that finds the
|
||||
/// best possible overlapping location. This determines from how wide window the algorithm
|
||||
/// may look for an optimal joining location when mixing the sound sequences back together.
|
||||
/// See "STTypes.h" or README for more information.
|
||||
#define SETTING_SEEKWINDOW_MS 4
|
||||
|
||||
/// Time-stretch algorithm overlap length in milliseconds. When the chopped sound sequences
|
||||
/// are mixed back together, to form a continuous sound stream, this parameter defines over
|
||||
/// how long period the two consecutive sequences are let to overlap each other.
|
||||
/// See "STTypes.h" or README for more information.
|
||||
#define SETTING_OVERLAP_MS 5
|
||||
|
||||
|
||||
class SoundTouch : public FIFOProcessor
|
||||
{
|
||||
private:
|
||||
/// Rate transposer class instance
|
||||
class RateTransposer *pRateTransposer;
|
||||
|
||||
/// Time-stretch class instance
|
||||
class TDStretch *pTDStretch;
|
||||
|
||||
/// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.
|
||||
float virtualRate;
|
||||
|
||||
/// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.
|
||||
float virtualTempo;
|
||||
|
||||
/// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.
|
||||
float virtualPitch;
|
||||
|
||||
/// Flag: Has sample rate been set?
|
||||
BOOL bSrateSet;
|
||||
|
||||
/// Calculates effective rate & tempo valuescfrom 'virtualRate', 'virtualTempo' and
|
||||
/// 'virtualPitch' parameters.
|
||||
void calcEffectiveRateAndTempo();
|
||||
|
||||
protected :
|
||||
/// Number of channels
|
||||
uint channels;
|
||||
|
||||
/// Effective 'rate' value calculated from 'virtualRate', 'virtualTempo' and 'virtualPitch'
|
||||
float rate;
|
||||
|
||||
/// Effective 'tempo' value calculated from 'virtualRate', 'virtualTempo' and 'virtualPitch'
|
||||
float tempo;
|
||||
|
||||
public:
|
||||
SoundTouch();
|
||||
virtual ~SoundTouch();
|
||||
|
||||
/// Get SoundTouch library version string
|
||||
static const char *getVersionString();
|
||||
|
||||
/// Get SoundTouch library version Id
|
||||
static uint SoundTouch::getVersionId();
|
||||
|
||||
/// Sets new rate control value. Normal rate = 1.0, smaller values
|
||||
/// represent slower rate, larger faster rates.
|
||||
void setRate(float newRate);
|
||||
|
||||
/// Sets new tempo control value. Normal tempo = 1.0, smaller values
|
||||
/// represent slower tempo, larger faster tempo.
|
||||
void setTempo(float newTempo);
|
||||
|
||||
/// Sets new rate control value as a difference in percents compared
|
||||
/// to the original rate (-50 .. +100 %)
|
||||
void setRateChange(float newRate);
|
||||
|
||||
/// Sets new tempo control value as a difference in percents compared
|
||||
/// to the original tempo (-50 .. +100 %)
|
||||
void setTempoChange(float newTempo);
|
||||
|
||||
/// Sets new pitch control value. Original pitch = 1.0, smaller values
|
||||
/// represent lower pitches, larger values higher pitch.
|
||||
void setPitch(float newPitch);
|
||||
|
||||
/// Sets pitch change in octaves compared to the original pitch
|
||||
/// (-1.00 .. +1.00)
|
||||
void setPitchOctaves(float newPitch);
|
||||
|
||||
/// Sets pitch change in semi-tones compared to the original pitch
|
||||
/// (-12 .. +12)
|
||||
void setPitchSemiTones(int newPitch);
|
||||
void setPitchSemiTones(float newPitch);
|
||||
|
||||
/// Sets the number of channels, 1 = mono, 2 = stereo
|
||||
void setChannels(uint numChannels);
|
||||
|
||||
/// Sets sample rate.
|
||||
void setSampleRate(uint srate);
|
||||
|
||||
/// Flushes the last samples from the processing pipeline to the output.
|
||||
/// Clears also the internal processing buffers.
|
||||
//
|
||||
/// Note: This function is meant for extracting the last samples of a sound
|
||||
/// stream. This function may introduce additional blank samples in the end
|
||||
/// of the sound stream, and thus it's not recommended to call this function
|
||||
/// in the middle of a sound stream.
|
||||
void flush();
|
||||
|
||||
/// Adds 'numSamples' pcs of samples from the 'samples' memory position into
|
||||
/// the input of the object. Notice that sample rate _has_to_ be set before
|
||||
/// calling this function, otherwise throws a runtime_error exception.
|
||||
virtual void putSamples(
|
||||
const SAMPLETYPE *samples, ///< Pointer to sample buffer.
|
||||
uint numSamples ///< Number of samples in buffer. Notice
|
||||
///< that in case of stereo-sound a single sample
|
||||
///< contains data for both channels.
|
||||
);
|
||||
|
||||
/// Clears all the samples in the object's output and internal processing
|
||||
/// buffers.
|
||||
virtual void clear();
|
||||
|
||||
/// Changes a setting controlling the processing system behaviour. See the
|
||||
/// 'SETTING_...' defines for available setting ID's.
|
||||
///
|
||||
/// \return 'TRUE' if the setting was succesfully changed
|
||||
BOOL setSetting(uint settingId, ///< Setting ID number. see SETTING_... defines.
|
||||
uint value ///< New setting value.
|
||||
);
|
||||
|
||||
/// Reads a setting controlling the processing system behaviour. See the
|
||||
/// 'SETTING_...' defines for available setting ID's.
|
||||
///
|
||||
/// \return the setting value.
|
||||
uint getSetting(uint settingId ///< Setting ID number, see SETTING_... defines.
|
||||
) const;
|
||||
|
||||
/// Returns number of samples currently unprocessed.
|
||||
virtual uint numUnprocessedSamples() const;
|
||||
|
||||
|
||||
/// Other handy functions that are implemented in the ancestor classes (see
|
||||
/// classes 'FIFOProcessor' and 'FIFOSamplePipe')
|
||||
///
|
||||
/// - receiveSamples() : Use this function to receive 'ready' processed samples from SoundTouch.
|
||||
/// - numSamples() : Get number of 'ready' samples that can be received with
|
||||
/// function 'receiveSamples()'
|
||||
/// - isEmpty() : Returns nonzero if there aren't any 'ready' samples.
|
||||
/// - clear() : Clears all samples from ready/processing buffers.
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
923
libs/soundtouch/TDStretch.cpp
Normal file
923
libs/soundtouch/TDStretch.cpp
Normal file
@ -0,0 +1,923 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo
|
||||
/// while maintaining the original pitch by using a time domain WSOLA-like
|
||||
/// method with several performance-increasing tweaks.
|
||||
///
|
||||
/// Note : MMX optimized functions reside in a separate, platform-specific
|
||||
/// file, e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai @ iki.fi
|
||||
/// SoundTouch WWW: http://www.iki.fi/oparviai/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date$
|
||||
// File revision : $Revision$
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <memory.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "STTypes.h"
|
||||
#include "cpu_detect.h"
|
||||
#include "TDStretch.h"
|
||||
|
||||
using namespace soundtouch;
|
||||
|
||||
#ifndef min
|
||||
#define min(a,b) ((a > b) ? b : a)
|
||||
#define max(a,b) ((a < b) ? b : a)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* Constant definitions
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#define MAX_SCAN_DELTA 124
|
||||
|
||||
// Table for the hierarchical mixing position seeking algorithm
|
||||
int scanOffsets[4][24]={
|
||||
{ 124, 186, 248, 310, 372, 434, 496, 558, 620, 682, 744, 806,
|
||||
868, 930, 992, 1054, 1116, 1178, 1240, 1302, 1364, 1426, 1488, 0},
|
||||
{-100, -75, -50, -25, 25, 50, 75, 100, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{ -20, -15, -10, -5, 5, 10, 15, 20, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{ -4, -3, -2, -1, 1, 2, 3, 4, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* Implementation of the class 'TDStretch'
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
TDStretch::TDStretch() : FIFOProcessor(&outputBuffer)
|
||||
{
|
||||
bQuickseek = FALSE;
|
||||
channels = 2;
|
||||
bMidBufferDirty = FALSE;
|
||||
|
||||
pMidBuffer = NULL;
|
||||
pRefMidBufferUnaligned = NULL;
|
||||
overlapLength = 0;
|
||||
|
||||
setParameters(44100, DEFAULT_SEQUENCE_MS, DEFAULT_SEEKWINDOW_MS, DEFAULT_OVERLAP_MS);
|
||||
|
||||
setTempo(1.0f);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
TDStretch::~TDStretch()
|
||||
{
|
||||
delete[] pMidBuffer;
|
||||
delete[] pRefMidBufferUnaligned;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Calculates the x having the closest 2^x value for the given value
|
||||
static int _getClosest2Power(double value)
|
||||
{
|
||||
return (int)(log(value) / log(2.0) + 0.5);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets routine control parameters. These control are certain time constants
|
||||
// defining how the sound is stretched to the desired duration.
|
||||
//
|
||||
// 'sampleRate' = sample rate of the sound
|
||||
// 'sequenceMS' = one processing sequence length in milliseconds (default = 82 ms)
|
||||
// 'seekwindowMS' = seeking window length for scanning the best overlapping
|
||||
// position (default = 28 ms)
|
||||
// 'overlapMS' = overlapping length (default = 12 ms)
|
||||
|
||||
void TDStretch::setParameters(uint aSampleRate, uint aSequenceMS,
|
||||
uint aSeekWindowMS, uint aOverlapMS)
|
||||
{
|
||||
this->sampleRate = aSampleRate;
|
||||
this->sequenceMs = aSequenceMS;
|
||||
this->seekWindowMs = aSeekWindowMS;
|
||||
this->overlapMs = aOverlapMS;
|
||||
|
||||
seekLength = (sampleRate * seekWindowMs) / 1000;
|
||||
seekWindowLength = (sampleRate * sequenceMs) / 1000;
|
||||
|
||||
maxOffset = seekLength;
|
||||
|
||||
calculateOverlapLength(overlapMs);
|
||||
|
||||
// set tempo to recalculate 'sampleReq'
|
||||
setTempo(tempo);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Get routine control parameters, see setParameters() function.
|
||||
/// Any of the parameters to this function can be NULL, in such case corresponding parameter
|
||||
/// value isn't returned.
|
||||
void TDStretch::getParameters(uint *pSampleRate, uint *pSequenceMs, uint *pSeekWindowMs, uint *pOverlapMs)
|
||||
{
|
||||
if (pSampleRate)
|
||||
{
|
||||
*pSampleRate = sampleRate;
|
||||
}
|
||||
|
||||
if (pSequenceMs)
|
||||
{
|
||||
*pSequenceMs = sequenceMs;
|
||||
}
|
||||
|
||||
if (pSeekWindowMs)
|
||||
{
|
||||
*pSeekWindowMs = seekWindowMs;
|
||||
}
|
||||
|
||||
if (pOverlapMs)
|
||||
{
|
||||
*pOverlapMs = overlapMs;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Overlaps samples in 'midBuffer' with the samples in 'input'
|
||||
void TDStretch::overlapMono(SAMPLETYPE *output, const SAMPLETYPE *input) const
|
||||
{
|
||||
int i, itemp;
|
||||
|
||||
for (i = 0; i < (int)overlapLength ; i ++)
|
||||
{
|
||||
itemp = overlapLength - i;
|
||||
output[i] = (input[i] * i + pMidBuffer[i] * itemp ) / overlapLength; // >> overlapDividerBits;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void TDStretch::clearMidBuffer()
|
||||
{
|
||||
if (bMidBufferDirty)
|
||||
{
|
||||
memset(pMidBuffer, 0, 2 * sizeof(SAMPLETYPE) * overlapLength);
|
||||
bMidBufferDirty = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void TDStretch::clearInput()
|
||||
{
|
||||
inputBuffer.clear();
|
||||
clearMidBuffer();
|
||||
}
|
||||
|
||||
|
||||
// Clears the sample buffers
|
||||
void TDStretch::clear()
|
||||
{
|
||||
outputBuffer.clear();
|
||||
inputBuffer.clear();
|
||||
clearMidBuffer();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Enables/disables the quick position seeking algorithm. Zero to disable, nonzero
|
||||
// to enable
|
||||
void TDStretch::enableQuickSeek(BOOL enable)
|
||||
{
|
||||
bQuickseek = enable;
|
||||
}
|
||||
|
||||
|
||||
// Returns nonzero if the quick seeking algorithm is enabled.
|
||||
BOOL TDStretch::isQuickSeekEnabled() const
|
||||
{
|
||||
return bQuickseek;
|
||||
}
|
||||
|
||||
|
||||
// Seeks for the optimal overlap-mixing position.
|
||||
uint TDStretch::seekBestOverlapPosition(const SAMPLETYPE *refPos)
|
||||
{
|
||||
if (channels == 2)
|
||||
{
|
||||
// stereo sound
|
||||
if (bQuickseek)
|
||||
{
|
||||
return seekBestOverlapPositionStereoQuick(refPos);
|
||||
}
|
||||
else
|
||||
{
|
||||
return seekBestOverlapPositionStereo(refPos);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// mono sound
|
||||
if (bQuickseek)
|
||||
{
|
||||
return seekBestOverlapPositionMonoQuick(refPos);
|
||||
}
|
||||
else
|
||||
{
|
||||
return seekBestOverlapPositionMono(refPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Overlaps samples in 'midBuffer' with the samples in 'inputBuffer' at position
|
||||
// of 'ovlPos'.
|
||||
inline void TDStretch::overlap(SAMPLETYPE *output, const SAMPLETYPE *input, uint ovlPos) const
|
||||
{
|
||||
if (channels == 2)
|
||||
{
|
||||
// stereo sound
|
||||
overlapStereo(output, input + 2 * ovlPos);
|
||||
} else {
|
||||
// mono sound.
|
||||
overlapMono(output, input + ovlPos);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Seeks for the optimal overlap-mixing position. The 'stereo' version of the
|
||||
// routine
|
||||
//
|
||||
// The best position is determined as the position where the two overlapped
|
||||
// sample sequences are 'most alike', in terms of the highest cross-correlation
|
||||
// value over the overlapping period
|
||||
uint TDStretch::seekBestOverlapPositionStereo(const SAMPLETYPE *refPos)
|
||||
{
|
||||
uint bestOffs;
|
||||
LONG_SAMPLETYPE bestCorr, corr;
|
||||
uint i;
|
||||
|
||||
// Slopes the amplitudes of the 'midBuffer' samples
|
||||
precalcCorrReferenceStereo();
|
||||
|
||||
bestCorr = INT_MIN;
|
||||
bestOffs = 0;
|
||||
|
||||
// Scans for the best correlation value by testing each possible position
|
||||
// over the permitted range.
|
||||
for (i = 0; i < seekLength; i ++)
|
||||
{
|
||||
// Calculates correlation value for the mixing position corresponding
|
||||
// to 'i'
|
||||
corr = calcCrossCorrStereo(refPos + 2 * i, pRefMidBuffer);
|
||||
|
||||
// Checks for the highest correlation value
|
||||
if (corr > bestCorr)
|
||||
{
|
||||
bestCorr = corr;
|
||||
bestOffs = i;
|
||||
}
|
||||
}
|
||||
// clear cross correlation routine state if necessary (is so e.g. in MMX routines).
|
||||
clearCrossCorrState();
|
||||
|
||||
return bestOffs;
|
||||
}
|
||||
|
||||
|
||||
// Seeks for the optimal overlap-mixing position. The 'stereo' version of the
|
||||
// routine
|
||||
//
|
||||
// The best position is determined as the position where the two overlapped
|
||||
// sample sequences are 'most alike', in terms of the highest cross-correlation
|
||||
// value over the overlapping period
|
||||
uint TDStretch::seekBestOverlapPositionStereoQuick(const SAMPLETYPE *refPos)
|
||||
{
|
||||
uint j;
|
||||
uint bestOffs;
|
||||
LONG_SAMPLETYPE bestCorr, corr;
|
||||
uint scanCount, corrOffset, tempOffset;
|
||||
|
||||
// Slopes the amplitude of the 'midBuffer' samples
|
||||
precalcCorrReferenceStereo();
|
||||
|
||||
bestCorr = INT_MIN;
|
||||
bestOffs = 0;
|
||||
corrOffset = 0;
|
||||
tempOffset = 0;
|
||||
|
||||
// Scans for the best correlation value using four-pass hierarchical search.
|
||||
//
|
||||
// The look-up table 'scans' has hierarchical position adjusting steps.
|
||||
// In first pass the routine searhes for the highest correlation with
|
||||
// relatively coarse steps, then rescans the neighbourhood of the highest
|
||||
// correlation with better resolution and so on.
|
||||
for (scanCount = 0;scanCount < 4; scanCount ++)
|
||||
{
|
||||
j = 0;
|
||||
while (scanOffsets[scanCount][j])
|
||||
{
|
||||
tempOffset = corrOffset + scanOffsets[scanCount][j];
|
||||
if (tempOffset >= seekLength) break;
|
||||
|
||||
// Calculates correlation value for the mixing position corresponding
|
||||
// to 'tempOffset'
|
||||
corr = calcCrossCorrStereo(refPos + 2 * tempOffset, pRefMidBuffer);
|
||||
|
||||
// Checks for the highest correlation value
|
||||
if (corr > bestCorr)
|
||||
{
|
||||
bestCorr = corr;
|
||||
bestOffs = tempOffset;
|
||||
}
|
||||
j ++;
|
||||
}
|
||||
corrOffset = bestOffs;
|
||||
}
|
||||
// clear cross correlation routine state if necessary (is so e.g. in MMX routines).
|
||||
clearCrossCorrState();
|
||||
|
||||
return bestOffs;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Seeks for the optimal overlap-mixing position. The 'mono' version of the
|
||||
// routine
|
||||
//
|
||||
// The best position is determined as the position where the two overlapped
|
||||
// sample sequences are 'most alike', in terms of the highest cross-correlation
|
||||
// value over the overlapping period
|
||||
uint TDStretch::seekBestOverlapPositionMono(const SAMPLETYPE *refPos)
|
||||
{
|
||||
uint bestOffs;
|
||||
LONG_SAMPLETYPE bestCorr, corr;
|
||||
uint tempOffset;
|
||||
const SAMPLETYPE *compare;
|
||||
|
||||
// Slopes the amplitude of the 'midBuffer' samples
|
||||
precalcCorrReferenceMono();
|
||||
|
||||
bestCorr = INT_MIN;
|
||||
bestOffs = 0;
|
||||
|
||||
// Scans for the best correlation value by testing each possible position
|
||||
// over the permitted range.
|
||||
for (tempOffset = 0; tempOffset < seekLength; tempOffset ++)
|
||||
{
|
||||
compare = refPos + tempOffset;
|
||||
|
||||
// Calculates correlation value for the mixing position corresponding
|
||||
// to 'tempOffset'
|
||||
corr = calcCrossCorrMono(pRefMidBuffer, compare);
|
||||
|
||||
// Checks for the highest correlation value
|
||||
if (corr > bestCorr)
|
||||
{
|
||||
bestCorr = corr;
|
||||
bestOffs = tempOffset;
|
||||
}
|
||||
}
|
||||
// clear cross correlation routine state if necessary (is so e.g. in MMX routines).
|
||||
clearCrossCorrState();
|
||||
|
||||
return bestOffs;
|
||||
}
|
||||
|
||||
|
||||
// Seeks for the optimal overlap-mixing position. The 'mono' version of the
|
||||
// routine
|
||||
//
|
||||
// The best position is determined as the position where the two overlapped
|
||||
// sample sequences are 'most alike', in terms of the highest cross-correlation
|
||||
// value over the overlapping period
|
||||
uint TDStretch::seekBestOverlapPositionMonoQuick(const SAMPLETYPE *refPos)
|
||||
{
|
||||
uint j;
|
||||
uint bestOffs;
|
||||
LONG_SAMPLETYPE bestCorr, corr;
|
||||
uint scanCount, corrOffset, tempOffset;
|
||||
|
||||
// Slopes the amplitude of the 'midBuffer' samples
|
||||
precalcCorrReferenceMono();
|
||||
|
||||
bestCorr = INT_MIN;
|
||||
bestOffs = 0;
|
||||
corrOffset = 0;
|
||||
tempOffset = 0;
|
||||
|
||||
// Scans for the best correlation value using four-pass hierarchical search.
|
||||
//
|
||||
// The look-up table 'scans' has hierarchical position adjusting steps.
|
||||
// In first pass the routine searhes for the highest correlation with
|
||||
// relatively coarse steps, then rescans the neighbourhood of the highest
|
||||
// correlation with better resolution and so on.
|
||||
for (scanCount = 0;scanCount < 4; scanCount ++)
|
||||
{
|
||||
j = 0;
|
||||
while (scanOffsets[scanCount][j])
|
||||
{
|
||||
tempOffset = corrOffset + scanOffsets[scanCount][j];
|
||||
if (tempOffset >= seekLength) break;
|
||||
|
||||
// Calculates correlation value for the mixing position corresponding
|
||||
// to 'tempOffset'
|
||||
corr = calcCrossCorrMono(refPos + tempOffset, pRefMidBuffer);
|
||||
|
||||
// Checks for the highest correlation value
|
||||
if (corr > bestCorr)
|
||||
{
|
||||
bestCorr = corr;
|
||||
bestOffs = tempOffset;
|
||||
}
|
||||
j ++;
|
||||
}
|
||||
corrOffset = bestOffs;
|
||||
}
|
||||
// clear cross correlation routine state if necessary (is so e.g. in MMX routines).
|
||||
clearCrossCorrState();
|
||||
|
||||
return bestOffs;
|
||||
}
|
||||
|
||||
|
||||
/// clear cross correlation routine state if necessary
|
||||
void TDStretch::clearCrossCorrState()
|
||||
{
|
||||
// default implementation is empty.
|
||||
}
|
||||
|
||||
|
||||
// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower
|
||||
// tempo, larger faster tempo.
|
||||
void TDStretch::setTempo(float newTempo)
|
||||
{
|
||||
uint intskip;
|
||||
|
||||
tempo = newTempo;
|
||||
|
||||
// Calculate ideal skip length (according to tempo value)
|
||||
nominalSkip = tempo * (seekWindowLength - overlapLength);
|
||||
skipFract = 0;
|
||||
intskip = (int)(nominalSkip + 0.5f);
|
||||
|
||||
// Calculate how many samples are needed in the 'inputBuffer' to
|
||||
// process another batch of samples
|
||||
sampleReq = max(intskip + overlapLength, seekWindowLength) + maxOffset;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets the number of channels, 1 = mono, 2 = stereo
|
||||
void TDStretch::setChannels(uint numChannels)
|
||||
{
|
||||
if (channels == numChannels) return;
|
||||
assert(numChannels == 1 || numChannels == 2);
|
||||
|
||||
channels = numChannels;
|
||||
inputBuffer.setChannels(channels);
|
||||
outputBuffer.setChannels(channels);
|
||||
}
|
||||
|
||||
|
||||
// nominal tempo, no need for processing, just pass the samples through
|
||||
// to outputBuffer
|
||||
void TDStretch::processNominalTempo()
|
||||
{
|
||||
assert(tempo == 1.0f);
|
||||
|
||||
if (bMidBufferDirty)
|
||||
{
|
||||
// If there are samples in pMidBuffer waiting for overlapping,
|
||||
// do a single sliding overlapping with them in order to prevent a
|
||||
// clicking distortion in the output sound
|
||||
if (inputBuffer.numSamples() < overlapLength)
|
||||
{
|
||||
// wait until we've got overlapLength input samples
|
||||
return;
|
||||
}
|
||||
// Mix the samples in the beginning of 'inputBuffer' with the
|
||||
// samples in 'midBuffer' using sliding overlapping
|
||||
overlap(outputBuffer.ptrEnd(overlapLength), inputBuffer.ptrBegin(), 0);
|
||||
outputBuffer.putSamples(overlapLength);
|
||||
inputBuffer.receiveSamples(overlapLength);
|
||||
clearMidBuffer();
|
||||
// now we've caught the nominal sample flow and may switch to
|
||||
// bypass mode
|
||||
}
|
||||
|
||||
// Simply bypass samples from input to output
|
||||
outputBuffer.moveSamples(inputBuffer);
|
||||
}
|
||||
|
||||
|
||||
// Processes as many processing frames of the samples 'inputBuffer', store
|
||||
// the result into 'outputBuffer'
|
||||
void TDStretch::processSamples()
|
||||
{
|
||||
uint ovlSkip, offset;
|
||||
int temp;
|
||||
|
||||
if (tempo == 1.0f)
|
||||
{
|
||||
// tempo not changed from the original, so bypass the processing
|
||||
processNominalTempo();
|
||||
return;
|
||||
}
|
||||
|
||||
if (bMidBufferDirty == FALSE)
|
||||
{
|
||||
// if midBuffer is empty, move the first samples of the input stream
|
||||
// into it
|
||||
if (inputBuffer.numSamples() < overlapLength)
|
||||
{
|
||||
// wait until we've got overlapLength samples
|
||||
return;
|
||||
}
|
||||
memcpy(pMidBuffer, inputBuffer.ptrBegin(), channels * overlapLength * sizeof(SAMPLETYPE));
|
||||
inputBuffer.receiveSamples(overlapLength);
|
||||
bMidBufferDirty = TRUE;
|
||||
}
|
||||
|
||||
// Process samples as long as there are enough samples in 'inputBuffer'
|
||||
// to form a processing frame.
|
||||
while (inputBuffer.numSamples() >= sampleReq)
|
||||
{
|
||||
// If tempo differs from the normal ('SCALE'), scan for the best overlapping
|
||||
// position
|
||||
offset = seekBestOverlapPosition(inputBuffer.ptrBegin());
|
||||
|
||||
// Mix the samples in the 'inputBuffer' at position of 'offset' with the
|
||||
// samples in 'midBuffer' using sliding overlapping
|
||||
// ... first partially overlap with the end of the previous sequence
|
||||
// (that's in 'midBuffer')
|
||||
overlap(outputBuffer.ptrEnd(overlapLength), inputBuffer.ptrBegin(), offset);
|
||||
outputBuffer.putSamples(overlapLength);
|
||||
|
||||
// ... then copy sequence samples from 'inputBuffer' to output
|
||||
temp = (seekWindowLength - 2 * overlapLength);// & 0xfffffffe;
|
||||
if (temp > 0)
|
||||
{
|
||||
outputBuffer.putSamples(inputBuffer.ptrBegin() + channels * (offset + overlapLength), temp);
|
||||
}
|
||||
|
||||
// Copies the end of the current sequence from 'inputBuffer' to
|
||||
// 'midBuffer' for being mixed with the beginning of the next
|
||||
// processing sequence and so on
|
||||
assert(offset + seekWindowLength <= inputBuffer.numSamples());
|
||||
memcpy(pMidBuffer, inputBuffer.ptrBegin() + channels * (offset + seekWindowLength - overlapLength),
|
||||
channels * sizeof(SAMPLETYPE) * overlapLength);
|
||||
bMidBufferDirty = TRUE;
|
||||
|
||||
// Remove the processed samples from the input buffer. Update
|
||||
// the difference between integer & nominal skip step to 'skipFract'
|
||||
// in order to prevent the error from accumulating over time.
|
||||
skipFract += nominalSkip; // real skip size
|
||||
ovlSkip = (int)skipFract; // rounded to integer skip
|
||||
skipFract -= ovlSkip; // maintain the fraction part, i.e. real vs. integer skip
|
||||
inputBuffer.receiveSamples(ovlSkip);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Adds 'numsamples' pcs of samples from the 'samples' memory position into
|
||||
// the input of the object.
|
||||
void TDStretch::putSamples(const SAMPLETYPE *samples, uint numSamples)
|
||||
{
|
||||
// Add the samples into the input buffer
|
||||
inputBuffer.putSamples(samples, numSamples);
|
||||
// Process the samples in input buffer
|
||||
processSamples();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Set new overlap length parameter & reallocate RefMidBuffer if necessary.
|
||||
void TDStretch::acceptNewOverlapLength(uint newOverlapLength)
|
||||
{
|
||||
uint prevOvl;
|
||||
|
||||
prevOvl = overlapLength;
|
||||
overlapLength = newOverlapLength;
|
||||
|
||||
if (overlapLength > prevOvl)
|
||||
{
|
||||
delete[] pMidBuffer;
|
||||
delete[] pRefMidBufferUnaligned;
|
||||
|
||||
pMidBuffer = new SAMPLETYPE[overlapLength * 2];
|
||||
bMidBufferDirty = TRUE;
|
||||
clearMidBuffer();
|
||||
|
||||
pRefMidBufferUnaligned = new SAMPLETYPE[2 * overlapLength + 16 / sizeof(SAMPLETYPE)];
|
||||
// ensure that 'pRefMidBuffer' is aligned to 16 byte boundary for efficiency
|
||||
pRefMidBuffer = (SAMPLETYPE *)((((ulong)pRefMidBufferUnaligned) + 15) & -16);
|
||||
}
|
||||
}
|
||||
|
||||
TDStretch * TDStretch::newInstance()
|
||||
{
|
||||
uint uExtensions;
|
||||
|
||||
uExtensions = detectCPUextensions();
|
||||
|
||||
// Check if MMX/SSE/3DNow! instruction set extensions supported by CPU
|
||||
|
||||
#ifdef ALLOW_MMX
|
||||
// MMX routines available only with integer sample types
|
||||
if (uExtensions & SUPPORT_MMX)
|
||||
{
|
||||
return ::new TDStretchMMX;
|
||||
}
|
||||
else
|
||||
#endif // ALLOW_MMX
|
||||
|
||||
|
||||
#ifdef ALLOW_SSE
|
||||
if (uExtensions & SUPPORT_SSE)
|
||||
{
|
||||
// SSE support
|
||||
return ::new TDStretchSSE;
|
||||
}
|
||||
else
|
||||
#endif // ALLOW_SSE
|
||||
|
||||
|
||||
#ifdef ALLOW_3DNOW
|
||||
if (uExtensions & SUPPORT_3DNOW)
|
||||
{
|
||||
// 3DNow! support
|
||||
return ::new TDStretch3DNow;
|
||||
}
|
||||
else
|
||||
#endif // ALLOW_3DNOW
|
||||
|
||||
{
|
||||
// ISA optimizations not supported, use plain C version
|
||||
return ::new TDStretch;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Integer arithmetics specific algorithm implementations.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifdef INTEGER_SAMPLES
|
||||
|
||||
// Slopes the amplitude of the 'midBuffer' samples so that cross correlation
|
||||
// is faster to calculate
|
||||
void TDStretch::precalcCorrReferenceStereo()
|
||||
{
|
||||
int i, cnt2;
|
||||
int temp, temp2;
|
||||
|
||||
for (i=0 ; i < (int)overlapLength ;i ++)
|
||||
{
|
||||
temp = i * (overlapLength - i);
|
||||
cnt2 = i * 2;
|
||||
|
||||
temp2 = (pMidBuffer[cnt2] * temp) / slopingDivider;
|
||||
pRefMidBuffer[cnt2] = (short)(temp2);
|
||||
temp2 = (pMidBuffer[cnt2 + 1] * temp) / slopingDivider;
|
||||
pRefMidBuffer[cnt2 + 1] = (short)(temp2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Slopes the amplitude of the 'midBuffer' samples so that cross correlation
|
||||
// is faster to calculate
|
||||
void TDStretch::precalcCorrReferenceMono()
|
||||
{
|
||||
int i;
|
||||
long temp;
|
||||
long temp2;
|
||||
|
||||
for (i=0 ; i < (int)overlapLength ;i ++)
|
||||
{
|
||||
temp = i * (overlapLength - i);
|
||||
temp2 = (pMidBuffer[i] * temp) / slopingDivider;
|
||||
pRefMidBuffer[i] = (short)temp2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Overlaps samples in 'midBuffer' with the samples in 'input'. The 'Stereo'
|
||||
// version of the routine.
|
||||
void TDStretch::overlapStereo(short *output, const short *input) const
|
||||
{
|
||||
int i;
|
||||
short temp;
|
||||
uint cnt2;
|
||||
|
||||
for (i = 0; i < (int)overlapLength ; i ++)
|
||||
{
|
||||
temp = (short)(overlapLength - i);
|
||||
cnt2 = 2 * i;
|
||||
output[cnt2] = (input[cnt2] * i + pMidBuffer[cnt2] * temp ) / overlapLength;
|
||||
output[cnt2 + 1] = (input[cnt2 + 1] * i + pMidBuffer[cnt2 + 1] * temp ) / overlapLength;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Calculates overlap period length in samples.
|
||||
/// Integer version rounds overlap length to closest power of 2
|
||||
/// for a divide scaling operation.
|
||||
void TDStretch::calculateOverlapLength(uint overlapMs)
|
||||
{
|
||||
uint newOvl;
|
||||
|
||||
overlapDividerBits = _getClosest2Power((sampleRate * overlapMs) / 1000.0);
|
||||
if (overlapDividerBits > 9) overlapDividerBits = 9;
|
||||
if (overlapDividerBits < 4) overlapDividerBits = 4;
|
||||
newOvl = (uint)pow(2, overlapDividerBits);
|
||||
|
||||
acceptNewOverlapLength(newOvl);
|
||||
|
||||
// calculate sloping divider so that crosscorrelation operation won't
|
||||
// overflow 32-bit register. Max. sum of the crosscorrelation sum without
|
||||
// divider would be 2^30*(N^3-N)/3, where N = overlap length
|
||||
slopingDivider = (newOvl * newOvl - 1) / 3;
|
||||
}
|
||||
|
||||
|
||||
long TDStretch::calcCrossCorrMono(const short *mixingPos, const short *compare) const
|
||||
{
|
||||
long corr;
|
||||
uint i;
|
||||
|
||||
corr = 0;
|
||||
for (i = 1; i < overlapLength; i ++)
|
||||
{
|
||||
corr += (mixingPos[i] * compare[i]) >> overlapDividerBits;
|
||||
}
|
||||
|
||||
return corr;
|
||||
}
|
||||
|
||||
|
||||
long TDStretch::calcCrossCorrStereo(const short *mixingPos, const short *compare) const
|
||||
{
|
||||
long corr;
|
||||
uint i;
|
||||
|
||||
corr = 0;
|
||||
for (i = 2; i < 2 * overlapLength; i += 2)
|
||||
{
|
||||
corr += (mixingPos[i] * compare[i] +
|
||||
mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBits;
|
||||
}
|
||||
|
||||
return corr;
|
||||
}
|
||||
|
||||
#endif // INTEGER_SAMPLES
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Floating point arithmetics specific algorithm implementations.
|
||||
//
|
||||
|
||||
#ifdef FLOAT_SAMPLES
|
||||
|
||||
|
||||
// Slopes the amplitude of the 'midBuffer' samples so that cross correlation
|
||||
// is faster to calculate
|
||||
void TDStretch::precalcCorrReferenceStereo()
|
||||
{
|
||||
int i, cnt2;
|
||||
float temp;
|
||||
|
||||
for (i=0 ; i < (int)overlapLength ;i ++)
|
||||
{
|
||||
temp = (float)i * (float)(overlapLength - i);
|
||||
cnt2 = i * 2;
|
||||
pRefMidBuffer[cnt2] = (float)(pMidBuffer[cnt2] * temp);
|
||||
pRefMidBuffer[cnt2 + 1] = (float)(pMidBuffer[cnt2 + 1] * temp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Slopes the amplitude of the 'midBuffer' samples so that cross correlation
|
||||
// is faster to calculate
|
||||
void TDStretch::precalcCorrReferenceMono()
|
||||
{
|
||||
int i;
|
||||
float temp;
|
||||
|
||||
for (i=0 ; i < (int)overlapLength ;i ++)
|
||||
{
|
||||
temp = (float)i * (float)(overlapLength - i);
|
||||
pRefMidBuffer[i] = (float)(pMidBuffer[i] * temp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// SSE-optimized version of the function overlapStereo
|
||||
void TDStretch::overlapStereo(float *output, const float *input) const
|
||||
{
|
||||
int i;
|
||||
uint cnt2;
|
||||
float fTemp;
|
||||
float fScale;
|
||||
float fi;
|
||||
|
||||
fScale = 1.0f / (float)overlapLength;
|
||||
|
||||
for (i = 0; i < (int)overlapLength ; i ++)
|
||||
{
|
||||
fTemp = (float)(overlapLength - i) * fScale;
|
||||
fi = (float)i * fScale;
|
||||
cnt2 = 2 * i;
|
||||
output[cnt2 + 0] = input[cnt2 + 0] * fi + pMidBuffer[cnt2 + 0] * fTemp;
|
||||
output[cnt2 + 1] = input[cnt2 + 1] * fi + pMidBuffer[cnt2 + 1] * fTemp;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Calculates overlap period length in samples.
|
||||
void TDStretch::calculateOverlapLength(uint overlapMs)
|
||||
{
|
||||
uint newOvl;
|
||||
|
||||
newOvl = (sampleRate * overlapMs) / 1000;
|
||||
if (newOvl < 16) newOvl = 16;
|
||||
|
||||
acceptNewOverlapLength(newOvl);
|
||||
}
|
||||
|
||||
|
||||
|
||||
double TDStretch::calcCrossCorrMono(const float *mixingPos, const float *compare) const
|
||||
{
|
||||
double corr;
|
||||
uint i;
|
||||
|
||||
corr = 0;
|
||||
for (i = 1; i < overlapLength; i ++)
|
||||
{
|
||||
corr += mixingPos[i] * compare[i];
|
||||
}
|
||||
|
||||
return corr;
|
||||
}
|
||||
|
||||
|
||||
double TDStretch::calcCrossCorrStereo(const float *mixingPos, const float *compare) const
|
||||
{
|
||||
double corr;
|
||||
uint i;
|
||||
|
||||
corr = 0;
|
||||
for (i = 2; i < 2 * overlapLength; i += 2)
|
||||
{
|
||||
corr += mixingPos[i] * compare[i] +
|
||||
mixingPos[i + 1] * compare[i + 1];
|
||||
}
|
||||
|
||||
return corr;
|
||||
}
|
||||
|
||||
#endif // FLOAT_SAMPLES
|
253
libs/soundtouch/TDStretch.h
Normal file
253
libs/soundtouch/TDStretch.h
Normal file
@ -0,0 +1,253 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo
|
||||
/// while maintaining the original pitch by using a time domain WSOLA-like method
|
||||
/// with several performance-increasing tweaks.
|
||||
///
|
||||
/// Note : MMX optimized functions reside in a separate, platform-specific file,
|
||||
/// e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai @ iki.fi
|
||||
/// SoundTouch WWW: http://www.iki.fi/oparviai/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date$
|
||||
// File revision : $Revision$
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 TDStretch_H
|
||||
#define TDStretch_H
|
||||
|
||||
#include "STTypes.h"
|
||||
#include "RateTransposer.h"
|
||||
#include "FIFOSamplePipe.h"
|
||||
|
||||
namespace soundtouch
|
||||
{
|
||||
|
||||
// Default values for sound processing parameters:
|
||||
|
||||
/// Default length of a single processing sequence, in milliseconds. This determines to how
|
||||
/// long sequences the original sound is chopped in the time-stretch algorithm.
|
||||
///
|
||||
/// The larger this value is, the lesser sequences are used in processing. In principle
|
||||
/// a bigger value sounds better when slowing down tempo, but worse when increasing tempo
|
||||
/// and vice versa.
|
||||
///
|
||||
/// Increasing this value reduces computational burden & vice versa.
|
||||
#define DEFAULT_SEQUENCE_MS 82
|
||||
|
||||
/// Seeking window default length in milliseconds for algorithm that finds the best possible
|
||||
/// overlapping location. This determines from how wide window the algorithm may look for an
|
||||
/// optimal joining location when mixing the sound sequences back together.
|
||||
///
|
||||
/// The bigger this window setting is, the higher the possibility to find a better mixing
|
||||
/// position will become, but at the same time large values may cause a "drifting" artifact
|
||||
/// because consequent sequences will be taken at more uneven intervals.
|
||||
///
|
||||
/// If there's a disturbing artifact that sounds as if a constant frequency was drifting
|
||||
/// around, try reducing this setting.
|
||||
///
|
||||
/// Increasing this value increases computational burden & vice versa.
|
||||
#define DEFAULT_SEEKWINDOW_MS 14
|
||||
|
||||
/// Overlap length in milliseconds. When the chopped sound sequences are mixed back together,
|
||||
/// to form a continuous sound stream, this parameter defines over how long period the two
|
||||
/// consecutive sequences are let to overlap each other.
|
||||
///
|
||||
/// This shouldn't be that critical parameter. If you reduce the DEFAULT_SEQUENCE_MS setting
|
||||
/// by a large amount, you might wish to try a smaller value on this.
|
||||
///
|
||||
/// Increasing this value increases computational burden & vice versa.
|
||||
#define DEFAULT_OVERLAP_MS 12
|
||||
|
||||
|
||||
/// Class that does the time-stretch (tempo change) effect for the processed
|
||||
/// sound.
|
||||
class TDStretch : public FIFOProcessor
|
||||
{
|
||||
protected:
|
||||
uint channels;
|
||||
uint sampleReq;
|
||||
float tempo;
|
||||
|
||||
SAMPLETYPE *pMidBuffer;
|
||||
SAMPLETYPE *pRefMidBuffer;
|
||||
SAMPLETYPE *pRefMidBufferUnaligned;
|
||||
uint overlapLength;
|
||||
uint overlapDividerBits;
|
||||
uint slopingDivider;
|
||||
uint seekLength;
|
||||
uint seekWindowLength;
|
||||
uint maxOffset;
|
||||
float nominalSkip;
|
||||
float skipFract;
|
||||
FIFOSampleBuffer outputBuffer;
|
||||
FIFOSampleBuffer inputBuffer;
|
||||
BOOL bQuickseek;
|
||||
BOOL bMidBufferDirty;
|
||||
|
||||
uint sampleRate;
|
||||
uint sequenceMs;
|
||||
uint seekWindowMs;
|
||||
uint overlapMs;
|
||||
|
||||
void acceptNewOverlapLength(uint newOverlapLength);
|
||||
|
||||
virtual void clearCrossCorrState();
|
||||
void calculateOverlapLength(uint overlapMs);
|
||||
|
||||
virtual LONG_SAMPLETYPE calcCrossCorrStereo(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare) const;
|
||||
virtual LONG_SAMPLETYPE calcCrossCorrMono(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare) const;
|
||||
|
||||
virtual uint seekBestOverlapPositionStereo(const SAMPLETYPE *refPos);
|
||||
virtual uint seekBestOverlapPositionStereoQuick(const SAMPLETYPE *refPos);
|
||||
virtual uint seekBestOverlapPositionMono(const SAMPLETYPE *refPos);
|
||||
virtual uint seekBestOverlapPositionMonoQuick(const SAMPLETYPE *refPos);
|
||||
uint seekBestOverlapPosition(const SAMPLETYPE *refPos);
|
||||
|
||||
virtual void overlapStereo(SAMPLETYPE *output, const SAMPLETYPE *input) const;
|
||||
virtual void overlapMono(SAMPLETYPE *output, const SAMPLETYPE *input) const;
|
||||
|
||||
void clearMidBuffer();
|
||||
void overlap(SAMPLETYPE *output, const SAMPLETYPE *input, uint ovlPos) const;
|
||||
|
||||
void precalcCorrReferenceMono();
|
||||
void precalcCorrReferenceStereo();
|
||||
|
||||
void processNominalTempo();
|
||||
|
||||
/// Changes the tempo of the given sound samples.
|
||||
/// Returns amount of samples returned in the "output" buffer.
|
||||
/// The maximum amount of samples that can be returned at a time is set by
|
||||
/// the 'set_returnBuffer_size' function.
|
||||
void processSamples();
|
||||
|
||||
TDStretch();
|
||||
|
||||
public:
|
||||
virtual ~TDStretch();
|
||||
|
||||
/// Use this function instead of "new" operator to create a new instance of this class.
|
||||
/// This function automatically chooses a correct feature set depending on if the CPU
|
||||
/// supports MMX/SSE/etc extensions.
|
||||
static TDStretch *newInstance();
|
||||
|
||||
/// Returns the output buffer object
|
||||
FIFOSamplePipe *getOutput() { return &outputBuffer; };
|
||||
|
||||
/// Returns the input buffer object
|
||||
FIFOSamplePipe *getInput() { return &inputBuffer; };
|
||||
|
||||
/// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower
|
||||
/// tempo, larger faster tempo.
|
||||
void setTempo(float newTempo);
|
||||
|
||||
/// Returns nonzero if there aren't any samples available for outputting.
|
||||
virtual void clear();
|
||||
|
||||
/// Clears the input buffer
|
||||
void clearInput();
|
||||
|
||||
/// Sets the number of channels, 1 = mono, 2 = stereo
|
||||
void setChannels(uint numChannels);
|
||||
|
||||
/// Enables/disables the quick position seeking algorithm. Zero to disable,
|
||||
/// nonzero to enable
|
||||
void enableQuickSeek(BOOL enable);
|
||||
|
||||
/// Returns nonzero if the quick seeking algorithm is enabled.
|
||||
BOOL isQuickSeekEnabled() const;
|
||||
|
||||
/// Sets routine control parameters. These control are certain time constants
|
||||
/// defining how the sound is stretched to the desired duration.
|
||||
//
|
||||
/// 'sampleRate' = sample rate of the sound
|
||||
/// 'sequenceMS' = one processing sequence length in milliseconds
|
||||
/// 'seekwindowMS' = seeking window length for scanning the best overlapping
|
||||
/// position
|
||||
/// 'overlapMS' = overlapping length
|
||||
void setParameters(uint sampleRate, ///< Samplerate of sound being processed (Hz)
|
||||
uint sequenceMS = DEFAULT_SEQUENCE_MS, ///< Single processing sequence length (ms)
|
||||
uint seekwindowMS = DEFAULT_SEEKWINDOW_MS, ///< Offset seeking window length (ms)
|
||||
uint overlapMS = DEFAULT_OVERLAP_MS ///< Sequence overlapping length (ms)
|
||||
);
|
||||
|
||||
/// Get routine control parameters, see setParameters() function.
|
||||
/// Any of the parameters to this function can be NULL, in such case corresponding parameter
|
||||
/// value isn't returned.
|
||||
void getParameters(uint *pSampleRate, uint *pSequenceMs, uint *pSeekWindowMs, uint *pOverlapMs);
|
||||
|
||||
/// Adds 'numsamples' pcs of samples from the 'samples' memory position into
|
||||
/// the input of the object.
|
||||
virtual void putSamples(
|
||||
const SAMPLETYPE *samples, ///< Input sample data
|
||||
uint numSamples ///< Number of samples in 'samples' so that one sample
|
||||
///< contains both channels if stereo
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Implementation-specific class declarations:
|
||||
|
||||
#ifdef ALLOW_MMX
|
||||
/// Class that implements MMX optimized routines for 16bit integer samples type.
|
||||
class TDStretchMMX : public TDStretch
|
||||
{
|
||||
protected:
|
||||
long calcCrossCorrStereo(const short *mixingPos, const short *compare) const;
|
||||
virtual void overlapStereo(short *output, const short *input) const;
|
||||
virtual void clearCrossCorrState();
|
||||
};
|
||||
#endif /// ALLOW_MMX
|
||||
|
||||
|
||||
#ifdef ALLOW_3DNOW
|
||||
/// Class that implements 3DNow! optimized routines for floating point samples type.
|
||||
class TDStretch3DNow : public TDStretch
|
||||
{
|
||||
protected:
|
||||
double calcCrossCorrStereo(const float *mixingPos, const float *compare) const;
|
||||
};
|
||||
#endif /// ALLOW_3DNOW
|
||||
|
||||
|
||||
#ifdef ALLOW_SSE
|
||||
/// Class that implements SSE optimized routines for floating point samples type.
|
||||
class TDStretchSSE : public TDStretch
|
||||
{
|
||||
protected:
|
||||
double calcCrossCorrStereo(const float *mixingPos, const float *compare) const;
|
||||
};
|
||||
|
||||
#endif /// ALLOW_SSE
|
||||
|
||||
}
|
||||
#endif /// TDStretch_H
|
62
libs/soundtouch/cpu_detect.h
Normal file
62
libs/soundtouch/cpu_detect.h
Normal file
@ -0,0 +1,62 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// A header file for detecting the Intel MMX instructions set extension.
|
||||
///
|
||||
/// Please see 'mmx_win.cpp', 'mmx_cpp.cpp' and 'mmx_non_x86.cpp' for the
|
||||
/// routine implementations for x86 Windows, x86 gnu version and non-x86
|
||||
/// platforms, respectively.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai @ iki.fi
|
||||
/// SoundTouch WWW: http://www.iki.fi/oparviai/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date$
|
||||
// File revision : $Revision$
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 _CPU_DETECT_H_
|
||||
#define _CPU_DETECT_H_
|
||||
|
||||
#include "STTypes.h"
|
||||
|
||||
#define SUPPORT_MMX 0x0001
|
||||
#define SUPPORT_3DNOW 0x0002
|
||||
#define SUPPORT_ALTIVEC 0x0004
|
||||
#define SUPPORT_SSE 0x0008
|
||||
#define SUPPORT_SSE2 0x0010
|
||||
|
||||
/// Checks which instruction set extensions are supported by the CPU.
|
||||
///
|
||||
/// \return A bitmask of supported extensions, see SUPPORT_... defines.
|
||||
uint detectCPUextensions(void);
|
||||
|
||||
/// Disables given set of instruction extensions. See SUPPORT_... defines.
|
||||
void disableExtensions(uint wDisableMask);
|
||||
|
||||
#endif // _CPU_DETECT_H_
|
138
libs/soundtouch/cpu_detect_x86_gcc.cpp
Normal file
138
libs/soundtouch/cpu_detect_x86_gcc.cpp
Normal file
@ -0,0 +1,138 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// gcc version of the x86 CPU detect routine.
|
||||
///
|
||||
/// This file is to be compiled on any platform with the GNU C compiler.
|
||||
/// Compiler. Please see 'cpu_detect_x86_win.cpp' for the x86 Windows version
|
||||
/// of this file.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai @ iki.fi
|
||||
/// SoundTouch WWW: http://www.iki.fi/oparviai/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date$
|
||||
// File revision : $Revision$
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <stdexcept>
|
||||
#include <string>
|
||||
#include "cpu_detect.h"
|
||||
|
||||
#ifndef __GNUC__
|
||||
#error wrong platform - this source code file is for the GNU C compiler.
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
|
||||
#include <stdio.h>
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// processor instructions extension detection routines
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// Flag variable indicating whick ISA extensions are disabled (for debugging)
|
||||
static uint _dwDisabledISA = 0x00; // 0xffffffff; //<- use this to disable all extensions
|
||||
|
||||
// Disables given set of instruction extensions. See SUPPORT_... defines.
|
||||
void disableExtensions(uint dwDisableMask)
|
||||
{
|
||||
_dwDisabledISA = dwDisableMask;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Checks which instruction set extensions are supported by the CPU.
|
||||
uint detectCPUextensions(void)
|
||||
{
|
||||
#ifndef __i386__
|
||||
return 0; // always disable extensions on non-x86 platforms.
|
||||
#else
|
||||
uint res = 0;
|
||||
|
||||
if (_dwDisabledISA == 0xffffffff) return 0;
|
||||
|
||||
asm volatile(
|
||||
"\n\txor %%esi, %%esi" // clear %%esi = result register
|
||||
// check if 'cpuid' instructions is available by toggling eflags bit 21
|
||||
|
||||
"\n\tpushf" // save eflags to stack
|
||||
"\n\tpop %%eax" // load eax from stack (with eflags)
|
||||
"\n\tmovl %%eax, %%ecx" // save the original eflags values to ecx
|
||||
"\n\txor $0x00200000, %%eax" // toggle bit 21
|
||||
"\n\tpush %%eax" // store toggled eflags to stack
|
||||
"\n\tpopf" // load eflags from stack
|
||||
"\n\tpushf" // save updated eflags to stack
|
||||
"\n\tpop %%eax" // load from stack
|
||||
"\n\txor %%edx, %%edx" // clear edx for defaulting no mmx
|
||||
"\n\tcmp %%ecx, %%eax" // compare to original eflags values
|
||||
"\n\tjz end" // jumps to 'end' if cpuid not present
|
||||
|
||||
// cpuid instruction available, test for presence of mmx instructions
|
||||
|
||||
"\n\tmovl $1, %%eax"
|
||||
"\n\tcpuid"
|
||||
// movl $0x00800000, %edx // force enable MMX
|
||||
"\n\ttest $0x00800000, %%edx"
|
||||
"\n\tjz end" // branch if MMX not available
|
||||
|
||||
"\n\tor $0x01, %%esi" // otherwise add MMX support bit
|
||||
|
||||
"\n\ttest $0x02000000, %%edx"
|
||||
"\n\tjz test3DNow" // branch if SSE not available
|
||||
|
||||
"\n\tor $0x08, %%esi" // otherwise add SSE support bit
|
||||
|
||||
"\n\ttest3DNow:"
|
||||
// test for precense of AMD extensions
|
||||
"\n\tmov $0x80000000, %%eax"
|
||||
"\n\tcpuid"
|
||||
"\n\tcmp $0x80000000, %%eax"
|
||||
"\n\tjbe end" // branch if no AMD extensions detected
|
||||
|
||||
// test for precense of 3DNow! extension
|
||||
"\n\tmov $0x80000001, %%eax"
|
||||
"\n\tcpuid"
|
||||
"\n\ttest $0x80000000, %%edx"
|
||||
"\n\tjz end" // branch if 3DNow! not detected
|
||||
|
||||
"\n\tor $0x02, %%esi" // otherwise add 3DNow support bit
|
||||
|
||||
"\n\tend:"
|
||||
|
||||
"\n\tmov %%esi, %0"
|
||||
|
||||
: "=r" (res)
|
||||
: /* no inputs */
|
||||
: "%edx", "%eax", "%ecx", "%esi" );
|
||||
|
||||
return res & ~_dwDisabledISA;
|
||||
#endif
|
||||
}
|
126
libs/soundtouch/cpu_detect_x86_win.cpp
Normal file
126
libs/soundtouch/cpu_detect_x86_win.cpp
Normal file
@ -0,0 +1,126 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Win32 version of the x86 CPU detect routine.
|
||||
///
|
||||
/// This file is to be compiled in Windows platform with Microsoft Visual C++
|
||||
/// Compiler. Please see 'cpu_detect_x86_gcc.cpp' for the gcc compiler version
|
||||
/// for all GNU platforms.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai @ iki.fi
|
||||
/// SoundTouch WWW: http://www.iki.fi/oparviai/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date$
|
||||
// File revision : $Revision$
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "cpu_detect.h"
|
||||
|
||||
#ifndef WIN32
|
||||
#error wrong platform - this source code file is exclusively for Win32 platform
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// processor instructions extension detection routines
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Flag variable indicating whick ISA extensions are disabled (for debugging)
|
||||
static uint _dwDisabledISA = 0x00; // 0xffffffff; //<- use this to disable all extensions
|
||||
|
||||
|
||||
// Disables given set of instruction extensions. See SUPPORT_... defines.
|
||||
void disableExtensions(uint dwDisableMask)
|
||||
{
|
||||
_dwDisabledISA = dwDisableMask;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Checks which instruction set extensions are supported by the CPU.
|
||||
uint detectCPUextensions(void)
|
||||
{
|
||||
uint res = 0;
|
||||
|
||||
if (_dwDisabledISA == 0xffffffff) return 0;
|
||||
|
||||
_asm
|
||||
{
|
||||
; check if 'cpuid' instructions is available by toggling eflags bit 21
|
||||
;
|
||||
xor esi, esi ; clear esi = result register
|
||||
|
||||
pushfd ; save eflags to stack
|
||||
pop eax ; load eax from stack (with eflags)
|
||||
mov ecx, eax ; save the original eflags values to ecx
|
||||
xor eax, 0x00200000 ; toggle bit 21
|
||||
push eax ; store toggled eflags to stack
|
||||
popfd ; load eflags from stack
|
||||
pushfd ; save updated eflags to stack
|
||||
pop eax ; load from stack
|
||||
xor edx, edx ; clear edx for defaulting no mmx
|
||||
cmp eax, ecx ; compare to original eflags values
|
||||
jz end ; jumps to 'end' if cpuid not present
|
||||
|
||||
; cpuid instruction available, test for presence of mmx instructions
|
||||
mov eax, 1
|
||||
cpuid
|
||||
test edx, 0x00800000
|
||||
jz end ; branch if MMX not available
|
||||
|
||||
or esi, SUPPORT_MMX ; otherwise add MMX support bit
|
||||
|
||||
test edx, 0x02000000
|
||||
jz test3DNow ; branch if SSE not available
|
||||
|
||||
or esi, SUPPORT_SSE ; otherwise add SSE support bit
|
||||
|
||||
test3DNow:
|
||||
; test for precense of AMD extensions
|
||||
mov eax, 0x80000000
|
||||
cpuid
|
||||
cmp eax, 0x80000000
|
||||
jbe end ; branch if no AMD extensions detected
|
||||
|
||||
; test for precense of 3DNow! extension
|
||||
mov eax, 0x80000001
|
||||
cpuid
|
||||
test edx, 0x80000000
|
||||
jz end ; branch if 3DNow! not detected
|
||||
|
||||
or esi, SUPPORT_3DNOW ; otherwise add 3DNow support bit
|
||||
|
||||
end:
|
||||
|
||||
mov res, esi
|
||||
}
|
||||
|
||||
return res & ~_dwDisabledISA;
|
||||
}
|
534
libs/soundtouch/mmx_gcc.cpp
Normal file
534
libs/soundtouch/mmx_gcc.cpp
Normal file
@ -0,0 +1,534 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// gcc version of the MMX optimized routines. All MMX optimized functions
|
||||
/// have been gathered into this single source code file, regardless to their
|
||||
/// class or original source code file, in order to ease porting the library
|
||||
/// to other compiler and processor platforms.
|
||||
///
|
||||
/// This file is to be compiled on any platform with the GNU C compiler.
|
||||
/// Compiler. Please see 'mmx_win.cpp' for the x86 Windows version of this
|
||||
/// file.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai @ iki.fi
|
||||
/// SoundTouch WWW: http://www.iki.fi/oparviai/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date$
|
||||
// File revision : $Revision$
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 <stdexcept>
|
||||
#include <string>
|
||||
#include "cpu_detect.h"
|
||||
|
||||
#ifndef __GNUC__
|
||||
#error "wrong platform - this source code file is for the GNU C compiler."
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
using namespace soundtouch;
|
||||
|
||||
|
||||
#ifdef ALLOW_MMX
|
||||
// MMX routines available only with integer sample type
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// implementation of MMX optimized functions of class 'TDStretch'
|
||||
//
|
||||
// NOTE: ebx in gcc 3.x is not preserved if -fPIC and -DPIC
|
||||
// gcc-3.4 correctly flags this error and wont let you continue.
|
||||
// gcc-2.95 preserves esi correctly
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "TDStretch.h"
|
||||
#include <limits.h>
|
||||
|
||||
// these are declared in 'TDStretch.cpp'
|
||||
extern int scanOffsets[4][24];
|
||||
|
||||
// Calculates cross correlation of two buffers
|
||||
long TDStretchMMX::calcCrossCorrStereo(const short *pV1, const short *pV2) const
|
||||
{
|
||||
#ifdef __i386__
|
||||
int corr;
|
||||
uint local_overlapLength = overlapLength;
|
||||
uint local_overlapDividerBits = overlapDividerBits;
|
||||
|
||||
asm volatile(
|
||||
// Calculate cross-correlation between the tempOffset and tmpbid_buffer.
|
||||
|
||||
// Process 4 parallel batches of 2 * stereo samples each during one
|
||||
// round to improve CPU-level parallellization.
|
||||
|
||||
// load address of sloped pV2 buffer to eax
|
||||
// load address of mixing point of the sample data buffer to edi
|
||||
// load counter to ecx = overlapLength / 8 - 1
|
||||
// empty the mm0
|
||||
|
||||
// prepare to the first round by loading
|
||||
// load mm1 = eax[0]
|
||||
// load mm2 = eax[1];
|
||||
|
||||
"\n\tmovl %1, %%eax"
|
||||
"\n\tmovl %2, %%edi"
|
||||
|
||||
"\n\tmovq (%%eax), %%mm1"
|
||||
"\n\tmovl %3, %%ecx"
|
||||
|
||||
"\n\tmovq 8(%%eax), %%mm2"
|
||||
"\n\tshr $3, %%ecx"
|
||||
|
||||
"\n\tpxor %%mm0, %%mm0"
|
||||
"\n\tsub $1, %%ecx"
|
||||
|
||||
"\n\tmovd %4, %%mm5"
|
||||
|
||||
"\n1:"
|
||||
// multiply-add mm1 = mm1 * edi[0]
|
||||
// multiply-add mm2 = mm2 * edi[1]
|
||||
//
|
||||
// add mm2 += mm1
|
||||
// mm2 >>= mm5 (=overlapDividerBits)
|
||||
// add mm0 += mm2
|
||||
//
|
||||
// load mm3 = eax[2]
|
||||
// multiply-add mm3 = mm3 * edi[2]
|
||||
//
|
||||
// load mm4 = eax[3]
|
||||
// multiply-add mm4 = mm4 * edi[3]
|
||||
//
|
||||
// add mm3 += mm4
|
||||
// mm3 >>= mm5 (=overlapDividerBits)
|
||||
// add mm0 += mm3
|
||||
//
|
||||
// add eax += 4
|
||||
// add edi += 4
|
||||
// load mm1 = eax[0] (~eax[4])
|
||||
// load mm2 = eax[1] (~eax[5])
|
||||
//
|
||||
// loop
|
||||
|
||||
"\n\tpmaddwd (%%edi), %%mm1" // qword ptr [edi]
|
||||
"\n\tmovq 16(%%eax), %%mm3" // qword ptr [eax+16]
|
||||
|
||||
"\n\tpmaddwd 8(%%edi), %%mm2" // qword ptr [edi+8]
|
||||
"\n\tmovq 24(%%eax), %%mm4" // qword ptr [eax+24]
|
||||
|
||||
"\n\tpmaddwd 16(%%edi), %%mm3" // qword ptr [edi+16]
|
||||
"\n\tpaddd %%mm1, %%mm2"
|
||||
|
||||
"\n\tpmaddwd 24(%%edi), %%mm4" // qword ptr [edi+24]
|
||||
"\n\tmovq 32(%%eax), %%mm1" // qword ptr [eax+32]
|
||||
|
||||
"\n\tpsrad %%mm5, %%mm2"
|
||||
"\n\tadd $32, %%eax"
|
||||
|
||||
"\n\tpaddd %%mm4, %%mm3"
|
||||
"\n\tpaddd %%mm2, %%mm0"
|
||||
|
||||
"\n\tmovq 8(%%eax), %%mm2" // qword ptr [eax+8]
|
||||
"\n\tpsrad %%mm5, %%mm3"
|
||||
|
||||
"\n\tadd $32, %%edi"
|
||||
"\n\tpaddd %%mm3, %%mm0"
|
||||
|
||||
"\n\tdec %%ecx"
|
||||
"\n\tjnz 1b"
|
||||
|
||||
// Finalize the last partial loop:
|
||||
|
||||
"\n\tmovq 16(%%eax), %%mm3" // qword ptr [eax+16]
|
||||
"\n\tpmaddwd (%%edi), %%mm1" // qword ptr [edi]
|
||||
|
||||
"\n\tmovq 24(%%eax), %%mm4" // qword ptr [eax+24]
|
||||
"\n\tpmaddwd 8(%%edi), %%mm2" // qword ptr [edi+8]
|
||||
|
||||
"\n\tpmaddwd 16(%%edi), %%mm3" // qword ptr [edi+16]
|
||||
"\n\tpaddd %%mm1, %%mm2"
|
||||
|
||||
"\n\tpmaddwd 24(%%edi), %%mm4" // qword ptr [edi+24]
|
||||
"\n\tpsrad %%mm5, %%mm2"
|
||||
|
||||
"\n\tpaddd %%mm4, %%mm3"
|
||||
"\n\tpaddd %%mm2, %%mm0"
|
||||
|
||||
"\n\tpsrad %%mm5, %%mm3"
|
||||
"\n\tpaddd %%mm3, %%mm0"
|
||||
|
||||
// copy hi-dword of mm0 to lo-dword of mm1, then sum mmo+mm1
|
||||
// and finally store the result into the variable "corr"
|
||||
|
||||
"\n\tmovq %%mm0, %%mm1"
|
||||
"\n\tpsrlq $32, %%mm1"
|
||||
"\n\tpaddd %%mm1, %%mm0"
|
||||
"\n\tmovd %%mm0, %0"
|
||||
: "=rm" (corr)
|
||||
: "rim" (pV1), "rim" (pV2), "rim" (local_overlapLength),
|
||||
"rim" (local_overlapDividerBits)
|
||||
: "%ecx", "%eax", "%edi"
|
||||
);
|
||||
return corr;
|
||||
|
||||
// Note: Warning about the missing EMMS instruction is harmless
|
||||
// as it'll be called elsewhere.
|
||||
#else
|
||||
throw runtime_error("MMX not supported");
|
||||
#endif
|
||||
}
|
||||
|
||||
void TDStretchMMX::clearCrossCorrState()
|
||||
{
|
||||
#ifdef __i386__
|
||||
asm volatile("EMMS");
|
||||
#endif
|
||||
}
|
||||
|
||||
// MMX-optimized version of the function overlapStereo
|
||||
void TDStretchMMX::overlapStereo(short *output, const short *input) const
|
||||
{
|
||||
#ifdef __i386__
|
||||
short *local_midBuffer = pMidBuffer;
|
||||
uint local_overlapLength = overlapLength;
|
||||
uint local_overlapDividerBits = overlapDividerBits;
|
||||
|
||||
asm volatile(
|
||||
"\n\t"
|
||||
// load sliding mixing value counter to mm6 and mm7
|
||||
// load counter value to ecx = overlapLength / 4
|
||||
// load divider-shifter value to esi
|
||||
// load mixing value adder to mm5
|
||||
// load address of midBuffer to eax
|
||||
// load address of inputBuffer added with ovlOffset to edi
|
||||
// load address of end of the outputBuffer to edx
|
||||
//
|
||||
// We need to preserve esi, since gcc uses it for the
|
||||
// stack frame.
|
||||
|
||||
"movl %0, %%eax\n\t" // ecx = 0x0000 OVL_
|
||||
"movl $0x0002fffe, %%edi\n\t" // ecx = 0x0002 fffe
|
||||
|
||||
"movl %1, %%esi\n\t"
|
||||
"movd %%eax, %%mm6\n\t" // mm6 = 0x0000 0000 0000 OVL_
|
||||
|
||||
"movl %%eax, %%ecx\n\t"
|
||||
"sub $1, %%eax\n\t"
|
||||
|
||||
"punpckldq %%mm6, %%mm6\n\t" // mm6 = 0x0000 OVL_ 0000 OVL_
|
||||
|
||||
"or $0x00010000, %%eax\n\t" // eax = 0x0001 overlapLength-1
|
||||
|
||||
"movd %%edi, %%mm5\n\t" // mm5 = 0x0000 0000 0002 fffe
|
||||
"movd %%eax, %%mm7\n\t" // mm7 = 0x0000 0000 0001 01ff
|
||||
|
||||
"movl %3, %%edi\n\t"
|
||||
|
||||
"movl %4, %%eax\n\t" // dword ptr local_midBuffer
|
||||
"punpckldq %%mm5, %%mm5\n\t" // mm5 = 0x0002 fffe 0002 fffe
|
||||
|
||||
"shr $2, %%ecx\n\t" // ecx = overlapLength / 2
|
||||
"punpckldq %%mm7, %%mm7\n\t" // mm7 = 0x0001 01ff 0001 01ff
|
||||
|
||||
"movl %2, %%edx\n"
|
||||
|
||||
"2:\n\t"
|
||||
// Process two parallel batches of 2+2 stereo samples during each round
|
||||
// to improve CPU-level parallellization.
|
||||
//
|
||||
// Load [eax] into mm0 and mm1
|
||||
// Load [edi] into mm3
|
||||
// unpack words of mm0, mm1 and mm3 into mm0 and mm1
|
||||
// multiply-add mm0*mm6 and mm1*mm7, store results into mm0 and mm1
|
||||
// divide mm0 and mm1 by 512 (=right-shift by overlapDividerBits)
|
||||
// pack the result into mm0 and store into [edx]
|
||||
//
|
||||
// Load [eax+8] into mm2 and mm3
|
||||
// Load [edi+8] into mm4
|
||||
// unpack words of mm2, mm3 and mm4 into mm2 and mm3
|
||||
// multiply-add mm2*mm6 and mm3*mm7, store results into mm2 and mm3
|
||||
// divide mm2 and mm3 by 512 (=right-shift by overlapDividerBits)
|
||||
// pack the result into mm2 and store into [edx+8]
|
||||
|
||||
|
||||
"movq (%%eax), %%mm0\n\t" // mm0 = m1l m1r m0l m0r
|
||||
"add $16, %%edx\n\t"
|
||||
|
||||
"movq (%%edi), %%mm3\n\t" // mm3 = i1l i1r i0l i0r
|
||||
"movq %%mm0, %%mm1\n\t" // mm1 = m1l m1r m0l m0r
|
||||
|
||||
"movq 8(%%eax), %%mm2\n\t" // mm2 = m3l m3r m2l m2r
|
||||
"punpcklwd %%mm3, %%mm0\n\t" // mm0 = i0l m0l i0r m0r
|
||||
|
||||
"movq 8(%%edi), %%mm4\n\t" // mm4 = i3l i3r i2l i2r
|
||||
"punpckhwd %%mm3, %%mm1\n\t" // mm1 = i1l m1l i1r m1r
|
||||
|
||||
"movq %%mm2, %%mm3\n\t" // mm3 = m3l m3r m2l m2r
|
||||
"punpcklwd %%mm4, %%mm2\n\t" // mm2 = i2l m2l i2r m2r
|
||||
|
||||
"pmaddwd %%mm6, %%mm0\n\t" // mm0 = i0l*m63+m0l*m62 i0r*m61+m0r*m60
|
||||
"punpckhwd %%mm4, %%mm3\n\t" // mm3 = i3l m3l i3r m3r
|
||||
|
||||
"movd %%esi, %%mm4\n\t" // mm4 = overlapDividerBits
|
||||
|
||||
"pmaddwd %%mm7, %%mm1\n\t" // mm1 = i1l*m73+m1l*m72 i1r*m71+m1r*m70
|
||||
"paddw %%mm5, %%mm6\n\t"
|
||||
|
||||
"paddw %%mm5, %%mm7\n\t"
|
||||
"psrad %%mm4, %%mm0\n\t" // mmo >>= overlapDividerBits
|
||||
|
||||
"pmaddwd %%mm6, %%mm2\n\t" // mm2 = i2l*m63+m2l*m62 i2r*m61+m2r*m60
|
||||
"psrad %%mm4, %%mm1\n\t" // mm1 >>= overlapDividerBits
|
||||
|
||||
"pmaddwd %%mm7, %%mm3\n\t" // mm3 = i3l*m73+m3l*m72 i3r*m71+m3r*m70
|
||||
"psrad %%mm4, %%mm2\n\t" // mm2 >>= overlapDividerBits
|
||||
|
||||
"packssdw %%mm1, %%mm0\n\t" // mm0 = mm1h mm1l mm0h mm0l
|
||||
"psrad %%mm4, %%mm3\n\t" // mm3 >>= overlapDividerBits
|
||||
|
||||
"add $16, %%eax\n\t"
|
||||
"paddw %%mm5, %%mm6\n\t"
|
||||
|
||||
"packssdw %%mm3, %%mm2\n\t" // mm2 = mm2h mm2l mm3h mm3l
|
||||
"paddw %%mm5, %%mm7\n\t"
|
||||
|
||||
"movq %%mm0, -16(%%edx)\n\t"
|
||||
"add $16, %%edi\n\t"
|
||||
|
||||
"movq %%mm2, -8(%%edx)\n\t"
|
||||
"dec %%ecx\n\t"
|
||||
|
||||
"jnz 2b\n\t"
|
||||
|
||||
"emms\n\t"
|
||||
|
||||
:
|
||||
: "rim" (local_overlapLength),
|
||||
"rim" (local_overlapDividerBits),
|
||||
"rim" (output),
|
||||
"rim" (input),
|
||||
"rim" (local_midBuffer)
|
||||
/* input */
|
||||
: "%edi", "%ecx", "%edx", "%eax", "%esi" /* regs */
|
||||
);
|
||||
#else
|
||||
throw runtime_error("MMX not supported");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// implementation of MMX optimized functions of class 'FIRFilter'
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "FIRFilter.h"
|
||||
|
||||
FIRFilterMMX::FIRFilterMMX() : FIRFilter()
|
||||
{
|
||||
filterCoeffsUnalign = NULL;
|
||||
}
|
||||
|
||||
|
||||
FIRFilterMMX::~FIRFilterMMX()
|
||||
{
|
||||
delete[] filterCoeffsUnalign;
|
||||
}
|
||||
|
||||
|
||||
#if 1
|
||||
// (overloaded) Calculates filter coefficients for MMX routine
|
||||
void FIRFilterMMX::setCoefficients(const short *coeffs, uint newLength, uint uResultDivFactor)
|
||||
{
|
||||
#ifdef __i386__
|
||||
uint i;
|
||||
FIRFilter::setCoefficients(coeffs, newLength, uResultDivFactor);
|
||||
|
||||
// Ensure that filter coeffs array is aligned to 16-byte boundary
|
||||
delete[] filterCoeffsUnalign;
|
||||
filterCoeffsUnalign = new short[2 * newLength + 8];
|
||||
filterCoeffsAlign = (short *)(((uint)filterCoeffsUnalign + 15) & -16);
|
||||
|
||||
// rearrange the filter coefficients for mmx routines
|
||||
for (i = 0;i < length; i += 4)
|
||||
{
|
||||
filterCoeffsAlign[2 * i + 0] = coeffs[i + 0];
|
||||
filterCoeffsAlign[2 * i + 1] = coeffs[i + 2];
|
||||
filterCoeffsAlign[2 * i + 2] = coeffs[i + 0];
|
||||
filterCoeffsAlign[2 * i + 3] = coeffs[i + 2];
|
||||
|
||||
filterCoeffsAlign[2 * i + 4] = coeffs[i + 1];
|
||||
filterCoeffsAlign[2 * i + 5] = coeffs[i + 3];
|
||||
filterCoeffsAlign[2 * i + 6] = coeffs[i + 1];
|
||||
filterCoeffsAlign[2 * i + 7] = coeffs[i + 3];
|
||||
}
|
||||
#else
|
||||
throw runtime_error("MMX not supported");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
// mmx-optimized version of the filter routine for stereo sound
|
||||
uint FIRFilterMMX::evaluateFilterStereo(short *dest, const short *src, const uint numSamples) const
|
||||
{
|
||||
#ifdef __i386__
|
||||
// Create stack copies of the needed member variables for asm routines :
|
||||
uint local_length = length;
|
||||
uint local_lengthDiv8 = lengthDiv8;
|
||||
uint local_resultDivider = resultDivFactor;
|
||||
short *local_filterCoeffs = (short*)filterCoeffsAlign;
|
||||
short *local_src = (short *)src;
|
||||
|
||||
asm volatile(
|
||||
"\n\t"
|
||||
// Load (num_samples-aa_filter_length)/2 to edi as a i
|
||||
// Load a pointer to samples to esi
|
||||
// Load a pointer to destination to edx
|
||||
|
||||
"movl %0, %%edi\n\t"
|
||||
"subl %2, %%edi\n\t"
|
||||
"movl %3, %%edx\n\t"
|
||||
"sar $1, %%edi\n"
|
||||
|
||||
// Load filter length/8 to ecx
|
||||
// Load pointer to samples from esi to ebx
|
||||
// Load counter from edi to ecx
|
||||
// Load [ebx] to mm3
|
||||
// Load pointer to filter coefficients to eax
|
||||
"3:\n\t"
|
||||
"movl %1, %%esi\n\t"
|
||||
"pxor %%mm0, %%mm0\n\t"
|
||||
|
||||
"movl %4, %%ecx\n\t"
|
||||
"pxor %%mm7, %%mm7\n\t"
|
||||
|
||||
"movq (%%esi), %%mm1\n\t" // mm1 = l1 r1 l0 r0
|
||||
"movl %5, %%eax\n"
|
||||
"4:\n\t"
|
||||
|
||||
"movq 8(%%esi), %%mm2\n\t" // mm2 = l3 r3 l2 r2
|
||||
"movq %%mm1, %%mm4\n\t" // mm4 = l1 r1 l0 r0
|
||||
|
||||
"movq 16(%%esi), %%mm3\n\t" // mm3 = l5 r5 l4 r4
|
||||
"punpckhwd %%mm2, %%mm1\n\t" // mm1 = l3 l1 r3 r1
|
||||
|
||||
"movq %%mm2, %%mm6\n\t" // mm6 = l3 r3 l2 r2
|
||||
"punpcklwd %%mm2, %%mm4\n\t" // mm4 = l2 l0 r2 r0
|
||||
|
||||
"movq (%%eax), %%mm2\n\t" // mm2 = f2 f0 f2 f0
|
||||
"movq %%mm1, %%mm5\n\t" // mm5 = l3 l1 r3 r1
|
||||
|
||||
"punpcklwd %%mm3, %%mm6\n\t" // mm6 = l4 l2 r4 r2
|
||||
"pmaddwd %%mm2, %%mm4\n\t" // mm4 = l2*f2+l0*f0 r2*f2+r0*f0
|
||||
|
||||
"pmaddwd %%mm2, %%mm5\n\t" // mm5 = l3*f2+l1*f0 r3*f2+l1*f0
|
||||
"movq 8(%%eax), %%mm2\n\t" // mm2 = f3 f1 f3 f1
|
||||
|
||||
"paddd %%mm4, %%mm0\n\t" // mm0 += s02*f02
|
||||
"movq %%mm3, %%mm4\n\t" // mm4 = l1 r1 l0 r0
|
||||
|
||||
"pmaddwd %%mm2, %%mm1\n\t" // mm1 = l3*f3+l1*f1 r3*f3+l1*f1
|
||||
"paddd %%mm5, %%mm7\n\t" // mm7 += s13*f02
|
||||
|
||||
"pmaddwd %%mm2, %%mm6\n\t" // mm6 = l4*f3+l2*f1 r4*f3+f4*f1
|
||||
"movq 24(%%esi), %%mm2\n\t" // mm2 = l3 r3 l2 r2
|
||||
|
||||
"paddd %%mm1, %%mm0\n\t" // mm0 += s31*f31
|
||||
"movq 32(%%esi), %%mm1\n\t" // mm1 = l5 r5 l4 r4
|
||||
|
||||
"paddd %%mm6, %%mm7\n\t" // mm7 += s42*f31
|
||||
"punpckhwd %%mm2, %%mm3\n\t" // mm3 = l3 l1 r3 r1
|
||||
|
||||
"movq %%mm2, %%mm6\n\t" // mm6 = l3 r3 l2 r2
|
||||
"punpcklwd %%mm2, %%mm4\n\t" // mm4 = l2 l0 r2 r0
|
||||
|
||||
"movq 16(%%eax), %%mm2\n\t" // mm2 = f2 f0 f2 f0
|
||||
"movq %%mm3, %%mm5\n\t" // mm5 = l3 l1 r3 r1
|
||||
|
||||
"punpcklwd %%mm1, %%mm6\n\t" // mm6 = l4 l2 r4 r2
|
||||
"add $32, %%eax\n\t"
|
||||
|
||||
"pmaddwd %%mm2, %%mm4\n\t" // mm4 = l2*f2+l0*f0 r2*f2+r0*f0
|
||||
"add $32, %%esi\n\t"
|
||||
|
||||
"pmaddwd %%mm2, %%mm5\n\t" // mm5 = l3*f2+l1*f0 r3*f2+l1*f0
|
||||
"movq -8(%%eax), %%mm2\n\t" // mm2 = f3 f1 f3 f1
|
||||
|
||||
"paddd %%mm4, %%mm0\n\t" // mm0 += s02*f02
|
||||
"pmaddwd %%mm2, %%mm3\n\t" // mm3 = l3*f3+l1*f1 r3*f3+l1*f1
|
||||
|
||||
"paddd %%mm5, %%mm7\n\t" // mm7 += s13*f02
|
||||
"pmaddwd %%mm2, %%mm6\n\t" // mm6 = l4*f3+l2*f1 r4*f3+f4*f1
|
||||
|
||||
"paddd %%mm3, %%mm0\n\t" // mm0 += s31*f31
|
||||
"paddd %%mm6, %%mm7\n\t" // mm7 += s42*f31
|
||||
|
||||
"dec %%ecx\n\t"
|
||||
"jnz 4b\n\t"
|
||||
|
||||
// Divide mm0 and mm7 by 8192 (= right-shift by 13),
|
||||
// pack and store to [edx]
|
||||
"movd %6, %%mm4\n\t"
|
||||
|
||||
"psrad %%mm4, %%mm0\n\t" // divide the result
|
||||
|
||||
"add $8, %%edx\n\t"
|
||||
"psrad %%mm4, %%mm7\n\t" // divide the result
|
||||
|
||||
"add $8, %1\n\t"
|
||||
"packssdw %%mm7, %%mm0\n\t"
|
||||
|
||||
"movq %%mm0, -8(%%edx)\n\t"
|
||||
"dec %%edi\n\t"
|
||||
|
||||
"jnz 3b\n\t"
|
||||
|
||||
"emms\n\t"
|
||||
|
||||
:
|
||||
: "rim" (numSamples),
|
||||
"rim" (local_src),
|
||||
"rim" (local_length),
|
||||
"rim" (dest),
|
||||
"rim" (local_lengthDiv8),
|
||||
"rim" (local_filterCoeffs),
|
||||
"rim" (local_resultDivider) /* input */
|
||||
: "%eax", "%ecx", "%edx", "%edi", "%esi" /* regs */
|
||||
);
|
||||
return (numSamples & 0xfffffffe) - local_length;
|
||||
#else
|
||||
throw runtime_error("MMX not supported");
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // ALLOW_MMX
|
487
libs/soundtouch/mmx_win.cpp
Normal file
487
libs/soundtouch/mmx_win.cpp
Normal file
@ -0,0 +1,487 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Win32 version of the MMX optimized routines. All MMX optimized functions
|
||||
/// have been gathered into this single source code file, regardless to their
|
||||
/// class or original source code file, in order to ease porting the library
|
||||
/// to other compiler and processor platforms.
|
||||
///
|
||||
/// This file is to be compiled in Windows platform with Microsoft Visual C++
|
||||
/// Compiler. Please see 'mmx_gcc.cpp' for the gcc compiler version for all
|
||||
/// GNU platforms.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai @ iki.fi
|
||||
/// SoundTouch WWW: http://www.iki.fi/oparviai/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date$
|
||||
// File revision : $Revision$
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "STTypes.h"
|
||||
|
||||
#ifndef WIN32
|
||||
#error "wrong platform - this source code file is exclusively for Win32 platform"
|
||||
#endif
|
||||
|
||||
using namespace soundtouch;
|
||||
|
||||
#ifdef ALLOW_MMX
|
||||
// MMX routines available only with integer sample type
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// implementation of MMX optimized functions of class 'TDStretchMMX'
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "TDStretch.h"
|
||||
#include <limits.h>
|
||||
|
||||
// these are declared in 'TDStretch.cpp'
|
||||
extern int scanOffsets[4][24];
|
||||
|
||||
// Calculates cross correlation of two buffers
|
||||
long TDStretchMMX::calcCrossCorrStereo(const short *pV1, const short *pV2) const
|
||||
{
|
||||
long corr;
|
||||
uint local_overlapLength = overlapLength;
|
||||
uint local_overlapDividerBits = overlapDividerBits;
|
||||
|
||||
_asm
|
||||
{
|
||||
; Calculate cross-correlation between the tempOffset and tmpbid_buffer.
|
||||
;
|
||||
; Process 4 parallel batches of 2 * stereo samples each during one
|
||||
; round to improve CPU-level parallellization.
|
||||
;
|
||||
; load address of sloped pV2 buffer to eax
|
||||
; load address of mixing point of the sample data buffer to ebx
|
||||
; load counter to ecx = overlapLength / 8 - 1
|
||||
; empty the mm0
|
||||
;
|
||||
; prepare to the first round by loading
|
||||
; load mm1 = eax[0]
|
||||
; load mm2 = eax[1];
|
||||
|
||||
mov eax, dword ptr pV1
|
||||
mov ebx, dword ptr pV2
|
||||
|
||||
movq mm1, qword ptr [eax]
|
||||
mov ecx, local_overlapLength
|
||||
|
||||
movq mm2, qword ptr [eax+8]
|
||||
shr ecx, 3
|
||||
|
||||
pxor mm0, mm0
|
||||
sub ecx, 1
|
||||
|
||||
movd mm5, local_overlapDividerBits
|
||||
|
||||
loop1:
|
||||
; multiply-add mm1 = mm1 * ebx[0]
|
||||
; multiply-add mm2 = mm2 * ebx[1]
|
||||
;
|
||||
; add mm2 += mm1
|
||||
; mm2 >>= mm5 (=overlapDividerBits)
|
||||
; add mm0 += mm2
|
||||
;
|
||||
; load mm3 = eax[2]
|
||||
; multiply-add mm3 = mm3 * ebx[2]
|
||||
;
|
||||
; load mm4 = eax[3]
|
||||
; multiply-add mm4 = mm4 * ebx[3]
|
||||
;
|
||||
; add mm3 += mm4
|
||||
; mm3 >>= mm5 (=overlapDividerBits)
|
||||
; add mm0 += mm3
|
||||
;
|
||||
; add eax += 4;
|
||||
; add ebx += 4
|
||||
; load mm1 = eax[0] (~eax[4])
|
||||
; load mm2 = eax[1] (~eax[5])
|
||||
;
|
||||
; loop
|
||||
|
||||
pmaddwd mm1, qword ptr [ebx]
|
||||
movq mm3, qword ptr [eax+16]
|
||||
|
||||
pmaddwd mm2, qword ptr [ebx+8]
|
||||
movq mm4, qword ptr [eax+24]
|
||||
|
||||
pmaddwd mm3, qword ptr [ebx+16]
|
||||
paddd mm2, mm1
|
||||
|
||||
pmaddwd mm4, qword ptr [ebx+24]
|
||||
movq mm1, qword ptr [eax+32]
|
||||
|
||||
psrad mm2, mm5
|
||||
add eax, 32
|
||||
|
||||
paddd mm3, mm4
|
||||
paddd mm0, mm2
|
||||
|
||||
movq mm2, qword ptr [eax+8]
|
||||
psrad mm3, mm5
|
||||
|
||||
add ebx, 32
|
||||
paddd mm0, mm3
|
||||
|
||||
dec ecx
|
||||
jnz loop1
|
||||
|
||||
; Finalize the last partial loop:
|
||||
|
||||
movq mm3, qword ptr [eax+16]
|
||||
pmaddwd mm1, qword ptr [ebx]
|
||||
|
||||
movq mm4, qword ptr [eax+24]
|
||||
pmaddwd mm2, qword ptr [ebx+8]
|
||||
|
||||
pmaddwd mm3, qword ptr [ebx+16]
|
||||
paddd mm2, mm1
|
||||
|
||||
pmaddwd mm4, qword ptr [ebx+24]
|
||||
psrad mm2, mm5
|
||||
|
||||
paddd mm3, mm4
|
||||
paddd mm0, mm2
|
||||
|
||||
psrad mm3, mm5
|
||||
paddd mm0, mm3
|
||||
|
||||
; copy hi-dword of mm0 to lo-dword of mm1, then sum mmo+mm1
|
||||
; and finally store the result into the variable "corr"
|
||||
|
||||
movq mm1, mm0
|
||||
psrlq mm1, 32
|
||||
paddd mm0, mm1
|
||||
movd corr, mm0
|
||||
}
|
||||
return corr;
|
||||
|
||||
// Note: Warning about the missing EMMS instruction is harmless
|
||||
// as it'll be called elsewhere.
|
||||
}
|
||||
|
||||
|
||||
|
||||
void TDStretchMMX::clearCrossCorrState()
|
||||
{
|
||||
_asm EMMS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// MMX-optimized version of the function overlapStereo
|
||||
void TDStretchMMX::overlapStereo(short *output, const short *input) const
|
||||
{
|
||||
short *local_midBuffer = pMidBuffer;
|
||||
uint local_overlapLength = overlapLength;
|
||||
uint local_overlapDividerBits = overlapDividerBits;
|
||||
|
||||
_asm
|
||||
{
|
||||
; load sliding mixing value counter to mm6 and mm7
|
||||
; load counter value to ecx = overlapLength / 4
|
||||
; load divider-shifter value to esi
|
||||
; load mixing value adder to mm5
|
||||
; load address of midBuffer to eax
|
||||
; load address of inputBuffer added with ovlOffset to ebx
|
||||
; load address of end of the outputBuffer to edx
|
||||
|
||||
mov eax, local_overlapLength ; ecx = 0x0000 OVL_
|
||||
mov edi, 0x0002fffe ; ecx = 0x0002 fffe
|
||||
|
||||
mov esi, local_overlapDividerBits
|
||||
movd mm6, eax ; mm6 = 0x0000 0000 0000 OVL_
|
||||
|
||||
mov ecx, eax;
|
||||
sub eax, 1
|
||||
|
||||
punpckldq mm6, mm6 ; mm6 = 0x0000 OVL_ 0000 OVL_
|
||||
mov edx, output
|
||||
|
||||
or eax, 0x00010000 ; eax = 0x0001 overlapLength-1
|
||||
mov ebx, dword ptr input
|
||||
|
||||
movd mm5, edi ; mm5 = 0x0000 0000 0002 fffe
|
||||
movd mm7, eax ; mm7 = 0x0000 0000 0001 01ff
|
||||
|
||||
mov eax, dword ptr local_midBuffer
|
||||
punpckldq mm5, mm5 ; mm5 = 0x0002 fffe 0002 fffe
|
||||
|
||||
shr ecx, 2 ; ecx = overlapLength / 2
|
||||
punpckldq mm7, mm7 ; mm7 = 0x0001 01ff 0001 01ff
|
||||
|
||||
loop1:
|
||||
; Process two parallel batches of 2+2 stereo samples during each round
|
||||
; to improve CPU-level parallellization.
|
||||
;
|
||||
; Load [eax] into mm0 and mm1
|
||||
; Load [ebx] into mm3
|
||||
; unpack words of mm0, mm1 and mm3 into mm0 and mm1
|
||||
; multiply-add mm0*mm6 and mm1*mm7, store results into mm0 and mm1
|
||||
; divide mm0 and mm1 by 512 (=right-shift by overlapDividerBits)
|
||||
; pack the result into mm0 and store into [edx]
|
||||
;
|
||||
; Load [eax+8] into mm2 and mm3
|
||||
; Load [ebx+8] into mm4
|
||||
; unpack words of mm2, mm3 and mm4 into mm2 and mm3
|
||||
; multiply-add mm2*mm6 and mm3*mm7, store results into mm2 and mm3
|
||||
; divide mm2 and mm3 by 512 (=right-shift by overlapDividerBits)
|
||||
; pack the result into mm2 and store into [edx+8]
|
||||
|
||||
|
||||
movq mm0, qword ptr [eax] ; mm0 = m1l m1r m0l m0r
|
||||
add edx, 16
|
||||
|
||||
movq mm3, qword ptr [ebx] ; mm3 = i1l i1r i0l i0r
|
||||
movq mm1, mm0 ; mm1 = m1l m1r m0l m0r
|
||||
|
||||
movq mm2, qword ptr [eax+8] ; mm2 = m3l m3r m2l m2r
|
||||
punpcklwd mm0, mm3 ; mm0 = i0l m0l i0r m0r
|
||||
|
||||
movq mm4, qword ptr [ebx+8] ; mm4 = i3l i3r i2l i2r
|
||||
punpckhwd mm1, mm3 ; mm1 = i1l m1l i1r m1r
|
||||
|
||||
movq mm3, mm2 ; mm3 = m3l m3r m2l m2r
|
||||
punpcklwd mm2, mm4 ; mm2 = i2l m2l i2r m2r
|
||||
|
||||
pmaddwd mm0, mm6 ; mm0 = i0l*m63+m0l*m62 i0r*m61+m0r*m60
|
||||
punpckhwd mm3, mm4 ; mm3 = i3l m3l i3r m3r
|
||||
|
||||
movd mm4, esi ; mm4 = overlapDividerBits
|
||||
|
||||
pmaddwd mm1, mm7 ; mm1 = i1l*m73+m1l*m72 i1r*m71+m1r*m70
|
||||
paddw mm6, mm5
|
||||
|
||||
paddw mm7, mm5
|
||||
psrad mm0, mm4 ; mmo >>= overlapDividerBits
|
||||
|
||||
pmaddwd mm2, mm6 ; mm2 = i2l*m63+m2l*m62 i2r*m61+m2r*m60
|
||||
psrad mm1, mm4 ; mm1 >>= overlapDividerBits
|
||||
|
||||
pmaddwd mm3, mm7 ; mm3 = i3l*m73+m3l*m72 i3r*m71+m3r*m70
|
||||
psrad mm2, mm4 ; mm2 >>= overlapDividerBits
|
||||
|
||||
packssdw mm0, mm1 ; mm0 = mm1h mm1l mm0h mm0l
|
||||
psrad mm3, mm4 ; mm3 >>= overlapDividerBits
|
||||
|
||||
add eax, 16
|
||||
paddw mm6, mm5
|
||||
|
||||
packssdw mm2, mm3 ; mm2 = mm2h mm2l mm3h mm3l
|
||||
paddw mm7, mm5
|
||||
|
||||
movq qword ptr [edx-16], mm0
|
||||
add ebx, 16
|
||||
|
||||
movq qword ptr [edx-8], mm2
|
||||
dec ecx
|
||||
|
||||
jnz loop1
|
||||
|
||||
emms
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// implementation of MMX optimized functions of class 'FIRFilter'
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "FIRFilter.h"
|
||||
|
||||
|
||||
FIRFilterMMX::FIRFilterMMX() : FIRFilter()
|
||||
{
|
||||
filterCoeffsUnalign = NULL;
|
||||
}
|
||||
|
||||
|
||||
FIRFilterMMX::~FIRFilterMMX()
|
||||
{
|
||||
delete[] filterCoeffsUnalign;
|
||||
}
|
||||
|
||||
|
||||
// (overloaded) Calculates filter coefficients for MMX routine
|
||||
void FIRFilterMMX::setCoefficients(const short *coeffs, uint newLength, uint uResultDivFactor)
|
||||
{
|
||||
uint i;
|
||||
FIRFilter::setCoefficients(coeffs, newLength, uResultDivFactor);
|
||||
|
||||
// Ensure that filter coeffs array is aligned to 16-byte boundary
|
||||
delete[] filterCoeffsUnalign;
|
||||
filterCoeffsUnalign = new short[2 * newLength + 8];
|
||||
filterCoeffsAlign = (short *)(((uint)filterCoeffsUnalign + 15) & -16);
|
||||
|
||||
// rearrange the filter coefficients for mmx routines
|
||||
for (i = 0;i < length; i += 4)
|
||||
{
|
||||
filterCoeffsAlign[2 * i + 0] = coeffs[i + 0];
|
||||
filterCoeffsAlign[2 * i + 1] = coeffs[i + 2];
|
||||
filterCoeffsAlign[2 * i + 2] = coeffs[i + 0];
|
||||
filterCoeffsAlign[2 * i + 3] = coeffs[i + 2];
|
||||
|
||||
filterCoeffsAlign[2 * i + 4] = coeffs[i + 1];
|
||||
filterCoeffsAlign[2 * i + 5] = coeffs[i + 3];
|
||||
filterCoeffsAlign[2 * i + 6] = coeffs[i + 1];
|
||||
filterCoeffsAlign[2 * i + 7] = coeffs[i + 3];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// mmx-optimized version of the filter routine for stereo sound
|
||||
uint FIRFilterMMX::evaluateFilterStereo(short *dest, const short *src, const uint numSamples) const
|
||||
{
|
||||
// Create stack copies of the needed member variables for asm routines :
|
||||
uint local_length = length;
|
||||
uint local_lengthDiv8 = lengthDiv8;
|
||||
uint local_resultDivider = resultDivFactor;
|
||||
short *local_filterCoeffs = (short*)filterCoeffsAlign;
|
||||
|
||||
if (local_length < 2) return 0;
|
||||
|
||||
_asm
|
||||
{
|
||||
; Load (num_samples-aa_filter_length)/2 to edi as a i
|
||||
; Load a pointer to samples to esi
|
||||
; Load a pointer to destination to edx
|
||||
|
||||
mov edi, numSamples
|
||||
mov esi, dword ptr src
|
||||
sub edi, local_length
|
||||
mov edx, dword ptr dest
|
||||
sar edi, 1
|
||||
|
||||
; Load filter length/8 to ecx
|
||||
; Load pointer to samples from esi to ebx
|
||||
; Load counter from edi to ecx
|
||||
; Load [ebx] to mm3
|
||||
; Load pointer to filter coefficients to eax
|
||||
loop1:
|
||||
mov ebx, esi
|
||||
pxor mm0, mm0
|
||||
|
||||
mov ecx, local_lengthDiv8
|
||||
pxor mm7, mm7
|
||||
|
||||
movq mm1, [ebx] ; mm1 = l1 r1 l0 r0
|
||||
mov eax, local_filterCoeffs
|
||||
loop2:
|
||||
|
||||
movq mm2, [ebx+8] ; mm2 = l3 r3 l2 r2
|
||||
movq mm4, mm1 ; mm4 = l1 r1 l0 r0
|
||||
|
||||
movq mm3, [ebx+16] ; mm3 = l5 r5 l4 r4
|
||||
punpckhwd mm1, mm2 ; mm1 = l3 l1 r3 r1
|
||||
|
||||
movq mm6, mm2 ; mm6 = l3 r3 l2 r2
|
||||
punpcklwd mm4, mm2 ; mm4 = l2 l0 r2 r0
|
||||
|
||||
movq mm2, qword ptr [eax] ; mm2 = f2 f0 f2 f0
|
||||
movq mm5, mm1 ; mm5 = l3 l1 r3 r1
|
||||
|
||||
punpcklwd mm6, mm3 ; mm6 = l4 l2 r4 r2
|
||||
pmaddwd mm4, mm2 ; mm4 = l2*f2+l0*f0 r2*f2+r0*f0
|
||||
|
||||
pmaddwd mm5, mm2 ; mm5 = l3*f2+l1*f0 r3*f2+l1*f0
|
||||
movq mm2, qword ptr [eax+8] ; mm2 = f3 f1 f3 f1
|
||||
|
||||
paddd mm0, mm4 ; mm0 += s02*f02
|
||||
movq mm4, mm3 ; mm4 = l1 r1 l0 r0
|
||||
|
||||
pmaddwd mm1, mm2 ; mm1 = l3*f3+l1*f1 r3*f3+l1*f1
|
||||
paddd mm7, mm5 ; mm7 += s13*f02
|
||||
|
||||
pmaddwd mm6, mm2 ; mm6 = l4*f3+l2*f1 r4*f3+f4*f1
|
||||
movq mm2, [ebx+24] ; mm2 = l3 r3 l2 r2
|
||||
|
||||
paddd mm0, mm1 ; mm0 += s31*f31
|
||||
movq mm1, [ebx+32] ; mm1 = l5 r5 l4 r4
|
||||
|
||||
paddd mm7, mm6 ; mm7 += s42*f31
|
||||
punpckhwd mm3, mm2 ; mm3 = l3 l1 r3 r1
|
||||
|
||||
movq mm6, mm2 ; mm6 = l3 r3 l2 r2
|
||||
punpcklwd mm4, mm2 ; mm4 = l2 l0 r2 r0
|
||||
|
||||
movq mm2, qword ptr [eax+16] ; mm2 = f2 f0 f2 f0
|
||||
movq mm5, mm3 ; mm5 = l3 l1 r3 r1
|
||||
|
||||
punpcklwd mm6, mm1 ; mm6 = l4 l2 r4 r2
|
||||
add eax, 32
|
||||
|
||||
pmaddwd mm4, mm2 ; mm4 = l2*f2+l0*f0 r2*f2+r0*f0
|
||||
add ebx, 32
|
||||
|
||||
pmaddwd mm5, mm2 ; mm5 = l3*f2+l1*f0 r3*f2+l1*f0
|
||||
movq mm2, qword ptr [eax-8] ; mm2 = f3 f1 f3 f1
|
||||
|
||||
paddd mm0, mm4 ; mm0 += s02*f02
|
||||
pmaddwd mm3, mm2 ; mm3 = l3*f3+l1*f1 r3*f3+l1*f1
|
||||
|
||||
paddd mm7, mm5 ; mm7 += s13*f02
|
||||
pmaddwd mm6, mm2 ; mm6 = l4*f3+l2*f1 r4*f3+f4*f1
|
||||
|
||||
paddd mm0, mm3 ; mm0 += s31*f31
|
||||
paddd mm7, mm6 ; mm7 += s42*f31
|
||||
|
||||
dec ecx
|
||||
jnz loop2
|
||||
|
||||
; Divide mm0 and mm7 by 8192 (= right-shift by 13),
|
||||
; pack and store to [edx]
|
||||
movd mm4, local_resultDivider;
|
||||
|
||||
psrad mm0, mm4 ; divider the result
|
||||
|
||||
add edx, 8
|
||||
psrad mm7, mm4 ; divider the result
|
||||
|
||||
add esi, 8
|
||||
packssdw mm0, mm7
|
||||
|
||||
movq qword ptr [edx-8], mm0
|
||||
dec edi
|
||||
|
||||
jnz loop1
|
||||
|
||||
emms
|
||||
}
|
||||
return (numSamples & 0xfffffffe) - local_length;
|
||||
}
|
||||
|
||||
#endif // ALLOW_MMX
|
367
libs/soundtouch/sse_win.cpp
Normal file
367
libs/soundtouch/sse_win.cpp
Normal file
@ -0,0 +1,367 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Win32 version of the SSE optimized routines for Pentium-III, Athlon-XP and
|
||||
/// later. All SSE optimized functions have been gathered into this single source
|
||||
/// code file, regardless to their class or original source code file, in order
|
||||
/// to ease porting the library to other compiler and processor platforms.
|
||||
///
|
||||
/// NOTICE: If using Visual Studio 6.0, you'll need to install the "Visual C++
|
||||
/// 6.0 processor pack" update to support SSE instruction set. The update is
|
||||
/// available for download at Microsoft Developers Network, see here:
|
||||
/// http://msdn.microsoft.com/vstudio/downloads/tools/ppack/default.aspx
|
||||
///
|
||||
/// If the above URL is expired or removed, go to "http://msdn.microsoft.com" and
|
||||
/// perform a search with keywords "processor pack".
|
||||
///
|
||||
/// This file is to be compiled in Windows platform with Microsoft Visual C++
|
||||
/// Compiler. Please see 'sse_gcc.cpp' for the gcc compiler version for all
|
||||
/// GNU platforms (if file supplied).
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai @ iki.fi
|
||||
/// SoundTouch WWW: http://www.iki.fi/oparviai/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date$
|
||||
// File revision : $Revision$
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 "cpu_detect.h"
|
||||
#include "STTypes.h"
|
||||
|
||||
#ifndef WIN32
|
||||
#error "wrong platform - this source code file is exclusively for Win32 platform"
|
||||
#endif
|
||||
|
||||
using namespace soundtouch;
|
||||
|
||||
#ifdef ALLOW_SSE
|
||||
// SSE routines available only with float sample type
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// implementation of SSE optimized functions of class 'TDStretchSSE'
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "TDStretch.h"
|
||||
#include <limits.h>
|
||||
|
||||
// these are declared in 'TDStretch.cpp'
|
||||
extern int scanOffsets[4][24];
|
||||
|
||||
// Calculates cross correlation of two buffers
|
||||
double TDStretchSSE::calcCrossCorrStereo(const float *pV1, const float *pV2) const
|
||||
{
|
||||
uint overlapLengthLocal = overlapLength;
|
||||
float corr;
|
||||
|
||||
/*
|
||||
double corr;
|
||||
uint i;
|
||||
|
||||
// Calculates the cross-correlation value between 'pV1' and 'pV2' vectors
|
||||
corr = 0.0;
|
||||
for (i = 0; i < overlapLength / 8; i ++)
|
||||
{
|
||||
corr += pV1[0] * pV2[0] +
|
||||
pV1[1] * pV2[1] +
|
||||
pV1[2] * pV2[2] +
|
||||
pV1[3] * pV2[3] +
|
||||
pV1[4] * pV2[4] +
|
||||
pV1[5] * pV2[5] +
|
||||
pV1[6] * pV2[6] +
|
||||
pV1[7] * pV2[7] +
|
||||
pV1[8] * pV2[8] +
|
||||
pV1[9] * pV2[9] +
|
||||
pV1[10] * pV2[10] +
|
||||
pV1[11] * pV2[11] +
|
||||
pV1[12] * pV2[12] +
|
||||
pV1[13] * pV2[13] +
|
||||
pV1[14] * pV2[14] +
|
||||
pV1[15] * pV2[15];
|
||||
|
||||
pV1 += 16;
|
||||
pV2 += 16;
|
||||
}
|
||||
*/
|
||||
|
||||
_asm
|
||||
{
|
||||
// Very important note: data in 'pV2' _must_ be aligned to
|
||||
// 16-byte boundary!
|
||||
|
||||
// give prefetch hints to CPU of what data are to be needed soonish
|
||||
// give more aggressive hints on pV1 as that changes while pV2 stays
|
||||
// same between runs
|
||||
prefetcht0 [pV1]
|
||||
prefetcht0 [pV2]
|
||||
prefetcht0 [pV1 + 32]
|
||||
|
||||
mov eax, dword ptr pV1
|
||||
mov ebx, dword ptr pV2
|
||||
|
||||
xorps xmm0, xmm0
|
||||
|
||||
mov ecx, overlapLengthLocal
|
||||
shr ecx, 3 // div by eight
|
||||
|
||||
loop1:
|
||||
prefetcht0 [eax + 64] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
prefetcht0 [ebx + 32] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
movups xmm1, [eax]
|
||||
mulps xmm1, [ebx]
|
||||
addps xmm0, xmm1
|
||||
|
||||
movups xmm2, [eax + 16]
|
||||
mulps xmm2, [ebx + 16]
|
||||
addps xmm0, xmm2
|
||||
|
||||
prefetcht0 [eax + 96] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
prefetcht0 [ebx + 64] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
|
||||
movups xmm3, [eax + 32]
|
||||
mulps xmm3, [ebx + 32]
|
||||
addps xmm0, xmm3
|
||||
|
||||
movups xmm4, [eax + 48]
|
||||
mulps xmm4, [ebx + 48]
|
||||
addps xmm0, xmm4
|
||||
|
||||
add eax, 64
|
||||
add ebx, 64
|
||||
|
||||
dec ecx
|
||||
jnz loop1
|
||||
|
||||
// add the four floats of xmm0 together and return the result.
|
||||
|
||||
movhlps xmm1, xmm0 // move 3 & 4 of xmm0 to 1 & 2 of xmm1
|
||||
addps xmm1, xmm0
|
||||
movaps xmm2, xmm1
|
||||
shufps xmm2, xmm2, 0x01 // move 2 of xmm2 as 1 of xmm2
|
||||
addss xmm2, xmm1
|
||||
movss corr, xmm2
|
||||
}
|
||||
|
||||
return (double)corr;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// implementation of SSE optimized functions of class 'FIRFilter'
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "FIRFilter.h"
|
||||
|
||||
FIRFilterSSE::FIRFilterSSE() : FIRFilter()
|
||||
{
|
||||
filterCoeffsUnalign = NULL;
|
||||
}
|
||||
|
||||
|
||||
FIRFilterSSE::~FIRFilterSSE()
|
||||
{
|
||||
delete[] filterCoeffsUnalign;
|
||||
}
|
||||
|
||||
|
||||
// (overloaded) Calculates filter coefficients for SSE routine
|
||||
void FIRFilterSSE::setCoefficients(const float *coeffs, uint newLength, uint uResultDivFactor)
|
||||
{
|
||||
uint i;
|
||||
float fDivider;
|
||||
|
||||
FIRFilter::setCoefficients(coeffs, newLength, uResultDivFactor);
|
||||
|
||||
// Scale the filter coefficients so that it won't be necessary to scale the filtering result
|
||||
// also rearrange coefficients suitably for 3DNow!
|
||||
// Ensure that filter coeffs array is aligned to 16-byte boundary
|
||||
delete[] filterCoeffsUnalign;
|
||||
filterCoeffsUnalign = new float[2 * newLength + 4];
|
||||
filterCoeffsAlign = (float *)(((uint)filterCoeffsUnalign + 15) & -16);
|
||||
|
||||
fDivider = (float)resultDivider;
|
||||
|
||||
// rearrange the filter coefficients for mmx routines
|
||||
for (i = 0; i < newLength; i ++)
|
||||
{
|
||||
filterCoeffsAlign[2 * i + 0] =
|
||||
filterCoeffsAlign[2 * i + 1] = coeffs[i + 0] / fDivider;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// SSE-optimized version of the filter routine for stereo sound
|
||||
uint FIRFilterSSE::evaluateFilterStereo(float *dest, const float *src, const uint numSamples) const
|
||||
{
|
||||
int count = (numSamples - length) & -2;
|
||||
uint lengthLocal = length / 8;
|
||||
float *filterCoeffsLocal = filterCoeffsAlign;
|
||||
|
||||
assert(count % 2 == 0);
|
||||
|
||||
if (count < 2) return 0;
|
||||
|
||||
/*
|
||||
double suml1, suml2;
|
||||
double sumr1, sumr2;
|
||||
uint i, j;
|
||||
|
||||
for (j = 0; j < count; j += 2)
|
||||
{
|
||||
const float *ptr;
|
||||
const float *pFil;
|
||||
|
||||
suml1 = sumr1 = 0.0;
|
||||
suml2 = sumr2 = 0.0;
|
||||
ptr = src;
|
||||
pFil = filterCoeffs;
|
||||
for (i = 0; i < lengthLocal; i ++)
|
||||
{
|
||||
// unroll loop for efficiency.
|
||||
|
||||
suml1 += ptr[0] * pFil[0] +
|
||||
ptr[2] * pFil[2] +
|
||||
ptr[4] * pFil[4] +
|
||||
ptr[6] * pFil[6];
|
||||
|
||||
sumr1 += ptr[1] * pFil[1] +
|
||||
ptr[3] * pFil[3] +
|
||||
ptr[5] * pFil[5] +
|
||||
ptr[7] * pFil[7];
|
||||
|
||||
suml2 += ptr[8] * pFil[0] +
|
||||
ptr[10] * pFil[2] +
|
||||
ptr[12] * pFil[4] +
|
||||
ptr[14] * pFil[6];
|
||||
|
||||
sumr2 += ptr[9] * pFil[1] +
|
||||
ptr[11] * pFil[3] +
|
||||
ptr[13] * pFil[5] +
|
||||
ptr[15] * pFil[7];
|
||||
|
||||
ptr += 16;
|
||||
pFil += 8;
|
||||
}
|
||||
dest[0] = (float)suml1;
|
||||
dest[1] = (float)sumr1;
|
||||
dest[2] = (float)suml2;
|
||||
dest[3] = (float)sumr2;
|
||||
|
||||
src += 4;
|
||||
dest += 4;
|
||||
}
|
||||
*/
|
||||
|
||||
_asm
|
||||
{
|
||||
// Very important note: data in 'src' _must_ be aligned to
|
||||
// 16-byte boundary!
|
||||
mov edx, count
|
||||
mov ebx, dword ptr src
|
||||
mov eax, dword ptr dest
|
||||
shr edx, 1
|
||||
|
||||
loop1:
|
||||
// "outer loop" : during each round 2*2 output samples are calculated
|
||||
|
||||
// give prefetch hints to CPU of what data are to be needed soonish
|
||||
prefetcht0 [ebx]
|
||||
prefetcht0 [filterCoeffsLocal]
|
||||
|
||||
mov esi, ebx
|
||||
mov edi, filterCoeffsLocal
|
||||
xorps xmm0, xmm0
|
||||
xorps xmm1, xmm1
|
||||
mov ecx, lengthLocal
|
||||
|
||||
loop2:
|
||||
// "inner loop" : during each round eight FIR filter taps are evaluated for 2*2 samples
|
||||
prefetcht0 [esi + 32] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
prefetcht0 [edi + 32] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
|
||||
movups xmm2, [esi] // possibly unaligned load
|
||||
movups xmm3, [esi + 8] // possibly unaligned load
|
||||
mulps xmm2, [edi]
|
||||
mulps xmm3, [edi]
|
||||
addps xmm0, xmm2
|
||||
addps xmm1, xmm3
|
||||
|
||||
movups xmm4, [esi + 16] // possibly unaligned load
|
||||
movups xmm5, [esi + 24] // possibly unaligned load
|
||||
mulps xmm4, [edi + 16]
|
||||
mulps xmm5, [edi + 16]
|
||||
addps xmm0, xmm4
|
||||
addps xmm1, xmm5
|
||||
|
||||
prefetcht0 [esi + 64] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
prefetcht0 [edi + 64] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
|
||||
movups xmm6, [esi + 32] // possibly unaligned load
|
||||
movups xmm7, [esi + 40] // possibly unaligned load
|
||||
mulps xmm6, [edi + 32]
|
||||
mulps xmm7, [edi + 32]
|
||||
addps xmm0, xmm6
|
||||
addps xmm1, xmm7
|
||||
|
||||
movups xmm4, [esi + 48] // possibly unaligned load
|
||||
movups xmm5, [esi + 56] // possibly unaligned load
|
||||
mulps xmm4, [edi + 48]
|
||||
mulps xmm5, [edi + 48]
|
||||
addps xmm0, xmm4
|
||||
addps xmm1, xmm5
|
||||
|
||||
add esi, 64
|
||||
add edi, 64
|
||||
dec ecx
|
||||
jnz loop2
|
||||
|
||||
// Now xmm0 and xmm1 both have a filtered 2-channel sample each, but we still need
|
||||
// to sum the two hi- and lo-floats of these registers together.
|
||||
|
||||
movhlps xmm2, xmm0 // xmm2 = xmm2_3 xmm2_2 xmm0_3 xmm0_2
|
||||
movlhps xmm2, xmm1 // xmm2 = xmm1_1 xmm1_0 xmm0_3 xmm0_2
|
||||
shufps xmm0, xmm1, 0xe4 // xmm0 = xmm1_3 xmm1_2 xmm0_1 xmm0_0
|
||||
addps xmm0, xmm2
|
||||
|
||||
movaps [eax], xmm0
|
||||
add ebx, 16
|
||||
add eax, 16
|
||||
|
||||
dec edx
|
||||
jnz loop1
|
||||
}
|
||||
|
||||
return (uint)count;
|
||||
}
|
||||
|
||||
#endif // ALLOW_SSE
|
Loading…
Reference in New Issue
Block a user