update soundtouch to svn revision 173

This commit is contained in:
Tetsuo55 2013-06-22 20:19:27 +02:00
parent 88d1195f93
commit d8f5ecf3ce
22 changed files with 6229 additions and 5979 deletions

View File

@ -1,184 +1,184 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// ///
/// FIR low-pass (anti-alias) filter with filter coefficient design routine and /// FIR low-pass (anti-alias) filter with filter coefficient design routine and
/// MMX optimization. /// MMX optimization.
/// ///
/// Anti-alias filter is used to prevent folding of high frequencies when /// Anti-alias filter is used to prevent folding of high frequencies when
/// transposing the sample rate with interpolation. /// transposing the sample rate with interpolation.
/// ///
/// Author : Copyright (c) Olli Parviainen /// Author : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi /// Author e-mail : oparviai 'at' iki.fi
/// SoundTouch WWW: http://www.surina.net/soundtouch /// SoundTouch WWW: http://www.surina.net/soundtouch
/// ///
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// Last changed : $Date: 2009-01-11 13:34:24 +0200 (Sun, 11 Jan 2009) $ // Last changed : $Date: 2009-01-11 11:34:24 +0000 (Sun, 11 Jan 2009) $
// File revision : $Revision: 4 $ // File revision : $Revision: 4 $
// //
// $Id: AAFilter.cpp 45 2009-01-11 11:34:24Z oparviai $ // $Id: AAFilter.cpp 45 2009-01-11 11:34:24Z oparviai $
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// License : // License :
// //
// SoundTouch audio processing library // SoundTouch audio processing library
// Copyright (c) Olli Parviainen // Copyright (c) Olli Parviainen
// //
// This library is free software; you can redistribute it and/or // This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public // modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either // License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version. // 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, // This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details. // Lesser General Public License for more details.
// //
// You should have received a copy of the GNU Lesser General Public // You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software // License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include <memory.h> #include <memory.h>
#include <assert.h> #include <assert.h>
#include <math.h> #include <math.h>
#include <stdlib.h> #include <stdlib.h>
#include "AAFilter.h" #include "AAFilter.h"
#include "FIRFilter.h" #include "FIRFilter.h"
using namespace soundtouch; using namespace soundtouch;
#define PI 3.141592655357989 #define PI 3.141592655357989
#define TWOPI (2 * PI) #define TWOPI (2 * PI)
/***************************************************************************** /*****************************************************************************
* *
* Implementation of the class 'AAFilter' * Implementation of the class 'AAFilter'
* *
*****************************************************************************/ *****************************************************************************/
AAFilter::AAFilter(uint len) AAFilter::AAFilter(uint len)
{ {
pFIR = FIRFilter::newInstance(); pFIR = FIRFilter::newInstance();
cutoffFreq = 0.5; cutoffFreq = 0.5;
setLength(len); setLength(len);
} }
AAFilter::~AAFilter() AAFilter::~AAFilter()
{ {
delete pFIR; delete pFIR;
} }
// Sets new anti-alias filter cut-off edge frequency, scaled to // Sets new anti-alias filter cut-off edge frequency, scaled to
// sampling frequency (nyquist frequency = 0.5). // sampling frequency (nyquist frequency = 0.5).
// The filter will cut frequencies higher than the given frequency. // The filter will cut frequencies higher than the given frequency.
void AAFilter::setCutoffFreq(double newCutoffFreq) void AAFilter::setCutoffFreq(double newCutoffFreq)
{ {
cutoffFreq = newCutoffFreq; cutoffFreq = newCutoffFreq;
calculateCoeffs(); calculateCoeffs();
} }
// Sets number of FIR filter taps // Sets number of FIR filter taps
void AAFilter::setLength(uint newLength) void AAFilter::setLength(uint newLength)
{ {
length = newLength; length = newLength;
calculateCoeffs(); calculateCoeffs();
} }
// Calculates coefficients for a low-pass FIR filter using Hamming window // Calculates coefficients for a low-pass FIR filter using Hamming window
void AAFilter::calculateCoeffs() void AAFilter::calculateCoeffs()
{ {
uint i; uint i;
double cntTemp, temp, tempCoeff,h, w; double cntTemp, temp, tempCoeff,h, w;
double fc2, wc; double fc2, wc;
double scaleCoeff, sum; double scaleCoeff, sum;
double *work; double *work;
SAMPLETYPE *coeffs; SAMPLETYPE *coeffs;
assert(length >= 2); assert(length >= 2);
assert(length % 4 == 0); assert(length % 4 == 0);
assert(cutoffFreq >= 0); assert(cutoffFreq >= 0);
assert(cutoffFreq <= 0.5); assert(cutoffFreq <= 0.5);
work = new double[length]; work = new double[length];
coeffs = new SAMPLETYPE[length]; coeffs = new SAMPLETYPE[length];
fc2 = 2.0 * cutoffFreq; fc2 = 2.0 * cutoffFreq;
wc = PI * fc2; wc = PI * fc2;
tempCoeff = TWOPI / (double)length; tempCoeff = TWOPI / (double)length;
sum = 0; sum = 0;
for (i = 0; i < length; i ++) for (i = 0; i < length; i ++)
{ {
cntTemp = (double)i - (double)(length / 2); cntTemp = (double)i - (double)(length / 2);
temp = cntTemp * wc; temp = cntTemp * wc;
if (temp != 0) if (temp != 0)
{ {
h = fc2 * sin(temp) / temp; // sinc function h = fc2 * sin(temp) / temp; // sinc function
} }
else else
{ {
h = 1.0; h = 1.0;
} }
w = 0.54 + 0.46 * cos(tempCoeff * cntTemp); // hamming window w = 0.54 + 0.46 * cos(tempCoeff * cntTemp); // hamming window
temp = w * h; temp = w * h;
work[i] = temp; work[i] = temp;
// calc net sum of coefficients // calc net sum of coefficients
sum += temp; sum += temp;
} }
// ensure the sum of coefficients is larger than zero // ensure the sum of coefficients is larger than zero
assert(sum > 0); assert(sum > 0);
// ensure we've really designed a lowpass filter... // ensure we've really designed a lowpass filter...
assert(work[length/2] > 0); assert(work[length/2] > 0);
assert(work[length/2 + 1] > -1e-6); assert(work[length/2 + 1] > -1e-6);
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 // Calculate a scaling coefficient in such a way that the result can be
// divided by 16384 // divided by 16384
scaleCoeff = 16384.0f / sum; scaleCoeff = 16384.0f / sum;
for (i = 0; i < length; i ++) for (i = 0; i < length; i ++)
{ {
// scale & round to nearest integer // scale & round to nearest integer
temp = work[i] * scaleCoeff; temp = work[i] * scaleCoeff;
temp += (temp >= 0) ? 0.5 : -0.5; temp += (temp >= 0) ? 0.5 : -0.5;
// ensure no overfloods // ensure no overfloods
assert(temp >= -32768 && temp <= 32767); assert(temp >= -32768 && temp <= 32767);
coeffs[i] = (SAMPLETYPE)temp; coeffs[i] = (SAMPLETYPE)temp;
} }
// Set coefficients. Use divide factor 14 => divide result by 2^14 = 16384 // Set coefficients. Use divide factor 14 => divide result by 2^14 = 16384
pFIR->setCoefficients(coeffs, length, 14); pFIR->setCoefficients(coeffs, length, 14);
delete[] work; delete[] work;
delete[] coeffs; delete[] coeffs;
} }
// Applies the filter to the given sequence of samples. // Applies the filter to the given sequence of samples.
// Note : The amount of outputted samples is by value of 'filter length' // Note : The amount of outputted samples is by value of 'filter length'
// smaller than the amount of input samples. // smaller than the amount of input samples.
uint AAFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels) const uint AAFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels) const
{ {
return pFIR->evaluate(dest, src, numSamples, numChannels); return pFIR->evaluate(dest, src, numSamples, numChannels);
} }
uint AAFilter::getLength() const uint AAFilter::getLength() const
{ {
return pFIR->getLength(); return pFIR->getLength();
} }

View File

@ -1,91 +1,91 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// ///
/// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo /// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo
/// while maintaining the original pitch by using a time domain WSOLA-like method /// while maintaining the original pitch by using a time domain WSOLA-like method
/// with several performance-increasing tweaks. /// with several performance-increasing tweaks.
/// ///
/// Anti-alias filter is used to prevent folding of high frequencies when /// Anti-alias filter is used to prevent folding of high frequencies when
/// transposing the sample rate with interpolation. /// transposing the sample rate with interpolation.
/// ///
/// Author : Copyright (c) Olli Parviainen /// Author : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi /// Author e-mail : oparviai 'at' iki.fi
/// SoundTouch WWW: http://www.surina.net/soundtouch /// SoundTouch WWW: http://www.surina.net/soundtouch
/// ///
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// Last changed : $Date: 2008-02-10 18:26:55 +0200 (Sun, 10 Feb 2008) $ // Last changed : $Date: 2008-02-10 16:26:55 +0000 (Sun, 10 Feb 2008) $
// File revision : $Revision: 4 $ // File revision : $Revision: 4 $
// //
// $Id: AAFilter.h 11 2008-02-10 16:26:55Z oparviai $ // $Id: AAFilter.h 11 2008-02-10 16:26:55Z oparviai $
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// License : // License :
// //
// SoundTouch audio processing library // SoundTouch audio processing library
// Copyright (c) Olli Parviainen // Copyright (c) Olli Parviainen
// //
// This library is free software; you can redistribute it and/or // This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public // modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either // License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version. // 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, // This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details. // Lesser General Public License for more details.
// //
// You should have received a copy of the GNU Lesser General Public // You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software // License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#ifndef AAFilter_H #ifndef AAFilter_H
#define AAFilter_H #define AAFilter_H
#include "STTypes.h" #include "STTypes.h"
namespace soundtouch namespace soundtouch
{ {
class AAFilter class AAFilter
{ {
protected: protected:
class FIRFilter *pFIR; class FIRFilter *pFIR;
/// Low-pass filter cut-off frequency, negative = invalid /// Low-pass filter cut-off frequency, negative = invalid
double cutoffFreq; double cutoffFreq;
/// num of filter taps /// num of filter taps
uint length; uint length;
/// Calculate the FIR coefficients realizing the given cutoff-frequency /// Calculate the FIR coefficients realizing the given cutoff-frequency
void calculateCoeffs(); void calculateCoeffs();
public: public:
AAFilter(uint length); AAFilter(uint length);
~AAFilter(); ~AAFilter();
/// Sets new anti-alias filter cut-off edge frequency, scaled to sampling /// Sets new anti-alias filter cut-off edge frequency, scaled to sampling
/// frequency (nyquist frequency = 0.5). The filter will cut off the /// frequency (nyquist frequency = 0.5). The filter will cut off the
/// frequencies than that. /// frequencies than that.
void setCutoffFreq(double newCutoffFreq); void setCutoffFreq(double newCutoffFreq);
/// Sets number of FIR filter taps, i.e. ~filter complexity /// Sets number of FIR filter taps, i.e. ~filter complexity
void setLength(uint newLength); void setLength(uint newLength);
uint getLength() const; uint getLength() const;
/// Applies the filter to the given sequence of samples. /// Applies the filter to the given sequence of samples.
/// Note : The amount of outputted samples is by value of 'filter length' /// Note : The amount of outputted samples is by value of 'filter length'
/// smaller than the amount of input samples. /// smaller than the amount of input samples.
uint evaluate(SAMPLETYPE *dest, uint evaluate(SAMPLETYPE *dest,
const SAMPLETYPE *src, const SAMPLETYPE *src,
uint numSamples, uint numSamples,
uint numChannels) const; uint numChannels) const;
}; };
} }
#endif #endif

View File

@ -1,370 +1,370 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// ///
/// Beats-per-minute (BPM) detection routine. /// Beats-per-minute (BPM) detection routine.
/// ///
/// The beat detection algorithm works as follows: /// The beat detection algorithm works as follows:
/// - Use function 'inputSamples' to input a chunks of samples to the class for /// - 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 /// 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. /// chunks of around few kilosamples in order not to extinguish too much RAM memory.
/// - Inputted sound data is decimated to approx 500 Hz to reduce calculation burden, /// - Inputted 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. /// 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 /// Simple averaging is used for anti-alias filtering because the resulting signal
/// quality isn't of that high importance. /// quality isn't of that high importance.
/// - Decimated sound data is enveloped, i.e. the amplitude shape is detected by /// - 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 /// 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 /// are below a couple of times the general RMS amplitude level are cut away to
/// leave only notable peaks there. /// leave only notable peaks there.
/// - Repeating sound patterns (e.g. beats) are detected by calculating short-term /// - Repeating sound patterns (e.g. beats) are detected by calculating short-term
/// autocorrelation function of the enveloped signal. /// autocorrelation function of the enveloped signal.
/// - After whole sound data file has been analyzed as above, the bpm level is /// - 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 /// 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. /// function, calculates it's precise location and converts this reading to bpm's.
/// ///
/// Author : Copyright (c) Olli Parviainen /// Author : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi /// Author e-mail : oparviai 'at' iki.fi
/// SoundTouch WWW: http://www.surina.net/soundtouch /// SoundTouch WWW: http://www.surina.net/soundtouch
/// ///
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// Last changed : $Date: 2012-08-30 22:45:25 +0300 (Thu, 30 Aug 2012) $ // Last changed : $Date: 2012-08-30 19:45:25 +0000 (Thu, 30 Aug 2012) $
// File revision : $Revision: 4 $ // File revision : $Revision: 4 $
// //
// $Id: BPMDetect.cpp 149 2012-08-30 19:45:25Z oparviai $ // $Id: BPMDetect.cpp 149 2012-08-30 19:45:25Z oparviai $
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// License : // License :
// //
// SoundTouch audio processing library // SoundTouch audio processing library
// Copyright (c) Olli Parviainen // Copyright (c) Olli Parviainen
// //
// This library is free software; you can redistribute it and/or // This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public // modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either // License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version. // 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, // This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details. // Lesser General Public License for more details.
// //
// You should have received a copy of the GNU Lesser General Public // You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software // License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include <math.h> #include <math.h>
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include "FIFOSampleBuffer.h" #include "FIFOSampleBuffer.h"
#include "PeakFinder.h" #include "PeakFinder.h"
#include "BPMDetect.h" #include "BPMDetect.h"
using namespace soundtouch; using namespace soundtouch;
#define INPUT_BLOCK_SAMPLES 2048 #define INPUT_BLOCK_SAMPLES 2048
#define DECIMATED_BLOCK_SAMPLES 256 #define DECIMATED_BLOCK_SAMPLES 256
/// decay constant for calculating RMS volume sliding average approximation /// decay constant for calculating RMS volume sliding average approximation
/// (time constant is about 10 sec) /// (time constant is about 10 sec)
const float avgdecay = 0.99986f; const float avgdecay = 0.99986f;
/// Normalization coefficient for calculating RMS sliding average approximation. /// Normalization coefficient for calculating RMS sliding average approximation.
const float avgnorm = (1 - avgdecay); const float avgnorm = (1 - avgdecay);
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Enable following define to create bpm analysis file: // Enable following define to create bpm analysis file:
// #define _CREATE_BPM_DEBUG_FILE // #define _CREATE_BPM_DEBUG_FILE
#ifdef _CREATE_BPM_DEBUG_FILE #ifdef _CREATE_BPM_DEBUG_FILE
#define DEBUGFILE_NAME "c:\\temp\\soundtouch-bpm-debug.txt" #define DEBUGFILE_NAME "c:\\temp\\soundtouch-bpm-debug.txt"
static void _SaveDebugData(const float *data, int minpos, int maxpos, double coeff) static void _SaveDebugData(const float *data, int minpos, int maxpos, double coeff)
{ {
FILE *fptr = fopen(DEBUGFILE_NAME, "wt"); FILE *fptr = fopen(DEBUGFILE_NAME, "wt");
int i; int i;
if (fptr) if (fptr)
{ {
printf("\n\nWriting BPM debug data into file " DEBUGFILE_NAME "\n\n"); printf("\n\nWriting BPM debug data into file " DEBUGFILE_NAME "\n\n");
for (i = minpos; i < maxpos; i ++) for (i = minpos; i < maxpos; i ++)
{ {
fprintf(fptr, "%d\t%.1lf\t%f\n", i, coeff / (double)i, data[i]); fprintf(fptr, "%d\t%.1lf\t%f\n", i, coeff / (double)i, data[i]);
} }
fclose(fptr); fclose(fptr);
} }
} }
#else #else
#define _SaveDebugData(a,b,c,d) #define _SaveDebugData(a,b,c,d)
#endif #endif
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
BPMDetect::BPMDetect(int numChannels, int aSampleRate) BPMDetect::BPMDetect(int numChannels, int aSampleRate)
{ {
this->sampleRate = aSampleRate; this->sampleRate = aSampleRate;
this->channels = numChannels; this->channels = numChannels;
decimateSum = 0; decimateSum = 0;
decimateCount = 0; decimateCount = 0;
envelopeAccu = 0; envelopeAccu = 0;
// Initialize RMS volume accumulator to RMS level of 1500 (out of 32768) that's // Initialize RMS volume accumulator to RMS level of 1500 (out of 32768) that's
// safe initial RMS signal level value for song data. This value is then adapted // safe initial RMS signal level value for song data. This value is then adapted
// to the actual level during processing. // to the actual level during processing.
#ifdef SOUNDTOUCH_INTEGER_SAMPLES #ifdef SOUNDTOUCH_INTEGER_SAMPLES
// integer samples // integer samples
RMSVolumeAccu = (1500 * 1500) / avgnorm; RMSVolumeAccu = (1500 * 1500) / avgnorm;
#else #else
// float samples, scaled to range [-1..+1[ // float samples, scaled to range [-1..+1[
RMSVolumeAccu = (0.045f * 0.045f) / avgnorm; RMSVolumeAccu = (0.045f * 0.045f) / avgnorm;
#endif #endif
// choose decimation factor so that result is approx. 1000 Hz // choose decimation factor so that result is approx. 1000 Hz
decimateBy = sampleRate / 1000; decimateBy = sampleRate / 1000;
assert(decimateBy > 0); assert(decimateBy > 0);
assert(INPUT_BLOCK_SAMPLES < decimateBy * DECIMATED_BLOCK_SAMPLES); assert(INPUT_BLOCK_SAMPLES < decimateBy * DECIMATED_BLOCK_SAMPLES);
// Calculate window length & starting item according to desired min & max bpms // Calculate window length & starting item according to desired min & max bpms
windowLen = (60 * sampleRate) / (decimateBy * MIN_BPM); windowLen = (60 * sampleRate) / (decimateBy * MIN_BPM);
windowStart = (60 * sampleRate) / (decimateBy * MAX_BPM); windowStart = (60 * sampleRate) / (decimateBy * MAX_BPM);
assert(windowLen > windowStart); assert(windowLen > windowStart);
// allocate new working objects // allocate new working objects
xcorr = new float[windowLen]; xcorr = new float[windowLen];
memset(xcorr, 0, windowLen * sizeof(float)); memset(xcorr, 0, windowLen * sizeof(float));
// allocate processing buffer // allocate processing buffer
buffer = new FIFOSampleBuffer(); buffer = new FIFOSampleBuffer();
// we do processing in mono mode // we do processing in mono mode
buffer->setChannels(1); buffer->setChannels(1);
buffer->clear(); buffer->clear();
} }
BPMDetect::~BPMDetect() BPMDetect::~BPMDetect()
{ {
delete[] xcorr; delete[] xcorr;
delete buffer; delete buffer;
} }
/// convert to mono, low-pass filter & decimate to about 500 Hz. /// convert to mono, low-pass filter & decimate to about 500 Hz.
/// return number of outputted samples. /// return number of outputted samples.
/// ///
/// Decimation is used to remove the unnecessary frequencies and thus to reduce /// Decimation is used to remove the unnecessary frequencies and thus to reduce
/// the amount of data needed to be processed as calculating autocorrelation /// the amount of data needed to be processed as calculating autocorrelation
/// function is a very-very heavy operation. /// function is a very-very heavy operation.
/// ///
/// Anti-alias filtering is done simply by averaging the samples. This is really a /// Anti-alias filtering is done simply by averaging the samples. This is really a
/// poor-man's anti-alias filtering, but it's not so critical in this kind of application /// poor-man's anti-alias filtering, but it's not so critical in this kind of application
/// (it'd also be difficult to design a high-quality filter with steep cut-off at very /// (it'd also be difficult to design a high-quality filter with steep cut-off at very
/// narrow band) /// narrow band)
int BPMDetect::decimate(SAMPLETYPE *dest, const SAMPLETYPE *src, int numsamples) int BPMDetect::decimate(SAMPLETYPE *dest, const SAMPLETYPE *src, int numsamples)
{ {
int count, outcount; int count, outcount;
LONG_SAMPLETYPE out; LONG_SAMPLETYPE out;
assert(channels > 0); assert(channels > 0);
assert(decimateBy > 0); assert(decimateBy > 0);
outcount = 0; outcount = 0;
for (count = 0; count < numsamples; count ++) for (count = 0; count < numsamples; count ++)
{ {
int j; int j;
// convert to mono and accumulate // convert to mono and accumulate
for (j = 0; j < channels; j ++) for (j = 0; j < channels; j ++)
{ {
decimateSum += src[j]; decimateSum += src[j];
} }
src += j; src += j;
decimateCount ++; decimateCount ++;
if (decimateCount >= decimateBy) if (decimateCount >= decimateBy)
{ {
// Store every Nth sample only // Store every Nth sample only
out = (LONG_SAMPLETYPE)(decimateSum / (decimateBy * channels)); out = (LONG_SAMPLETYPE)(decimateSum / (decimateBy * channels));
decimateSum = 0; decimateSum = 0;
decimateCount = 0; decimateCount = 0;
#ifdef SOUNDTOUCH_INTEGER_SAMPLES #ifdef SOUNDTOUCH_INTEGER_SAMPLES
// check ranges for sure (shouldn't actually be necessary) // check ranges for sure (shouldn't actually be necessary)
if (out > 32767) if (out > 32767)
{ {
out = 32767; out = 32767;
} }
else if (out < -32768) else if (out < -32768)
{ {
out = -32768; out = -32768;
} }
#endif // SOUNDTOUCH_INTEGER_SAMPLES #endif // SOUNDTOUCH_INTEGER_SAMPLES
dest[outcount] = (SAMPLETYPE)out; dest[outcount] = (SAMPLETYPE)out;
outcount ++; outcount ++;
} }
} }
return outcount; return outcount;
} }
// Calculates autocorrelation function of the sample history buffer // Calculates autocorrelation function of the sample history buffer
void BPMDetect::updateXCorr(int process_samples) void BPMDetect::updateXCorr(int process_samples)
{ {
int offs; int offs;
SAMPLETYPE *pBuffer; SAMPLETYPE *pBuffer;
assert(buffer->numSamples() >= (uint)(process_samples + windowLen)); assert(buffer->numSamples() >= (uint)(process_samples + windowLen));
pBuffer = buffer->ptrBegin(); pBuffer = buffer->ptrBegin();
for (offs = windowStart; offs < windowLen; offs ++) for (offs = windowStart; offs < windowLen; offs ++)
{ {
LONG_SAMPLETYPE sum; LONG_SAMPLETYPE sum;
int i; int i;
sum = 0; sum = 0;
for (i = 0; i < process_samples; i ++) for (i = 0; i < process_samples; i ++)
{ {
sum += pBuffer[i] * pBuffer[i + offs]; // scaling the sub-result shouldn't be necessary sum += pBuffer[i] * pBuffer[i + offs]; // scaling the sub-result shouldn't be necessary
} }
// xcorr[offs] *= xcorr_decay; // decay 'xcorr' here with suitable coefficients // xcorr[offs] *= xcorr_decay; // decay 'xcorr' here with suitable coefficients
// if it's desired that the system adapts automatically to // if it's desired that the system adapts automatically to
// various bpms, e.g. in processing continouos music stream. // various bpms, e.g. in processing continouos music stream.
// The 'xcorr_decay' should be a value that's smaller than but // The 'xcorr_decay' should be a value that's smaller than but
// close to one, and should also depend on 'process_samples' value. // close to one, and should also depend on 'process_samples' value.
xcorr[offs] += (float)sum; xcorr[offs] += (float)sum;
} }
} }
// Calculates envelope of the sample data // Calculates envelope of the sample data
void BPMDetect::calcEnvelope(SAMPLETYPE *samples, int numsamples) void BPMDetect::calcEnvelope(SAMPLETYPE *samples, int numsamples)
{ {
const static double decay = 0.7f; // decay constant for smoothing the envelope const static double decay = 0.7f; // decay constant for smoothing the envelope
const static double norm = (1 - decay); const static double norm = (1 - decay);
int i; int i;
LONG_SAMPLETYPE out; LONG_SAMPLETYPE out;
double val; double val;
for (i = 0; i < numsamples; i ++) for (i = 0; i < numsamples; i ++)
{ {
// calc average RMS volume // calc average RMS volume
RMSVolumeAccu *= avgdecay; RMSVolumeAccu *= avgdecay;
val = (float)fabs((float)samples[i]); val = (float)fabs((float)samples[i]);
RMSVolumeAccu += val * val; RMSVolumeAccu += val * val;
// cut amplitudes that are below cutoff ~2 times RMS volume // cut amplitudes that are below cutoff ~2 times RMS volume
// (we're interested in peak values, not the silent moments) // (we're interested in peak values, not the silent moments)
if (val < 0.5 * sqrt(RMSVolumeAccu * avgnorm)) if (val < 0.5 * sqrt(RMSVolumeAccu * avgnorm))
{ {
val = 0; val = 0;
} }
// smooth amplitude envelope // smooth amplitude envelope
envelopeAccu *= decay; envelopeAccu *= decay;
envelopeAccu += val; envelopeAccu += val;
out = (LONG_SAMPLETYPE)(envelopeAccu * norm); out = (LONG_SAMPLETYPE)(envelopeAccu * norm);
#ifdef SOUNDTOUCH_INTEGER_SAMPLES #ifdef SOUNDTOUCH_INTEGER_SAMPLES
// cut peaks (shouldn't be necessary though) // cut peaks (shouldn't be necessary though)
if (out > 32767) out = 32767; if (out > 32767) out = 32767;
#endif // SOUNDTOUCH_INTEGER_SAMPLES #endif // SOUNDTOUCH_INTEGER_SAMPLES
samples[i] = (SAMPLETYPE)out; samples[i] = (SAMPLETYPE)out;
} }
} }
void BPMDetect::inputSamples(const SAMPLETYPE *samples, int numSamples) void BPMDetect::inputSamples(const SAMPLETYPE *samples, int numSamples)
{ {
SAMPLETYPE decimated[DECIMATED_BLOCK_SAMPLES]; SAMPLETYPE decimated[DECIMATED_BLOCK_SAMPLES];
// iterate so that max INPUT_BLOCK_SAMPLES processed per iteration // iterate so that max INPUT_BLOCK_SAMPLES processed per iteration
while (numSamples > 0) while (numSamples > 0)
{ {
int block; int block;
int decSamples; int decSamples;
block = (numSamples > INPUT_BLOCK_SAMPLES) ? INPUT_BLOCK_SAMPLES : numSamples; block = (numSamples > INPUT_BLOCK_SAMPLES) ? INPUT_BLOCK_SAMPLES : numSamples;
// decimate. note that converts to mono at the same time // decimate. note that converts to mono at the same time
decSamples = decimate(decimated, samples, block); decSamples = decimate(decimated, samples, block);
samples += block * channels; samples += block * channels;
numSamples -= block; numSamples -= block;
// envelope new samples and add them to buffer // envelope new samples and add them to buffer
calcEnvelope(decimated, decSamples); calcEnvelope(decimated, decSamples);
buffer->putSamples(decimated, decSamples); buffer->putSamples(decimated, decSamples);
} }
// when the buffer has enought samples for processing... // when the buffer has enought samples for processing...
if ((int)buffer->numSamples() > windowLen) if ((int)buffer->numSamples() > windowLen)
{ {
int processLength; int processLength;
// how many samples are processed // how many samples are processed
processLength = (int)buffer->numSamples() - windowLen; processLength = (int)buffer->numSamples() - windowLen;
// ... calculate autocorrelations for oldest samples... // ... calculate autocorrelations for oldest samples...
updateXCorr(processLength); updateXCorr(processLength);
// ... and remove them from the buffer // ... and remove them from the buffer
buffer->receiveSamples(processLength); buffer->receiveSamples(processLength);
} }
} }
void BPMDetect::removeBias() void BPMDetect::removeBias()
{ {
int i; int i;
float minval = 1e12f; // arbitrary large number float minval = 1e12f; // arbitrary large number
for (i = windowStart; i < windowLen; i ++) for (i = windowStart; i < windowLen; i ++)
{ {
if (xcorr[i] < minval) if (xcorr[i] < minval)
{ {
minval = xcorr[i]; minval = xcorr[i];
} }
} }
for (i = windowStart; i < windowLen; i ++) for (i = windowStart; i < windowLen; i ++)
{ {
xcorr[i] -= minval; xcorr[i] -= minval;
} }
} }
float BPMDetect::getBpm() float BPMDetect::getBpm()
{ {
double peakPos; double peakPos;
double coeff; double coeff;
PeakFinder peakFinder; PeakFinder peakFinder;
coeff = 60.0 * ((double)sampleRate / (double)decimateBy); coeff = 60.0 * ((double)sampleRate / (double)decimateBy);
// save bpm debug analysis data if debug data enabled // save bpm debug analysis data if debug data enabled
_SaveDebugData(xcorr, windowStart, windowLen, coeff); _SaveDebugData(xcorr, windowStart, windowLen, coeff);
// remove bias from xcorr data // remove bias from xcorr data
removeBias(); removeBias();
// find peak position // find peak position
peakPos = peakFinder.detectPeak(xcorr, windowStart, windowLen); peakPos = peakFinder.detectPeak(xcorr, windowStart, windowLen);
assert(decimateBy != 0); assert(decimateBy != 0);
if (peakPos < 1e-9) return 0.0; // detection failed. if (peakPos < 1e-9) return 0.0; // detection failed.
// calculate BPM // calculate BPM
return (float) (coeff / peakPos); return (float) (coeff / peakPos);
} }

View File

@ -1,164 +1,164 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// ///
/// Beats-per-minute (BPM) detection routine. /// Beats-per-minute (BPM) detection routine.
/// ///
/// The beat detection algorithm works as follows: /// The beat detection algorithm works as follows:
/// - Use function 'inputSamples' to input a chunks of samples to the class for /// - 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 /// 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. /// 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, /// - 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. /// 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 /// Simple averaging is used for anti-alias filtering because the resulting signal
/// quality isn't of that high importance. /// quality isn't of that high importance.
/// - Decimated sound data is enveloped, i.e. the amplitude shape is detected by /// - 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 /// 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 /// are below a couple of times the general RMS amplitude level are cut away to
/// leave only notable peaks there. /// leave only notable peaks there.
/// - Repeating sound patterns (e.g. beats) are detected by calculating short-term /// - Repeating sound patterns (e.g. beats) are detected by calculating short-term
/// autocorrelation function of the enveloped signal. /// autocorrelation function of the enveloped signal.
/// - After whole sound data file has been analyzed as above, the bpm level is /// - 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 /// 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. /// function, calculates it's precise location and converts this reading to bpm's.
/// ///
/// Author : Copyright (c) Olli Parviainen /// Author : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi /// Author e-mail : oparviai 'at' iki.fi
/// SoundTouch WWW: http://www.surina.net/soundtouch /// SoundTouch WWW: http://www.surina.net/soundtouch
/// ///
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// Last changed : $Date: 2012-08-30 22:53:44 +0300 (Thu, 30 Aug 2012) $ // Last changed : $Date: 2012-08-30 19:53:44 +0000 (Thu, 30 Aug 2012) $
// File revision : $Revision: 4 $ // File revision : $Revision: 4 $
// //
// $Id: BPMDetect.h 150 2012-08-30 19:53:44Z oparviai $ // $Id: BPMDetect.h 150 2012-08-30 19:53:44Z oparviai $
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// License : // License :
// //
// SoundTouch audio processing library // SoundTouch audio processing library
// Copyright (c) Olli Parviainen // Copyright (c) Olli Parviainen
// //
// This library is free software; you can redistribute it and/or // This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public // modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either // License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version. // 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, // This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details. // Lesser General Public License for more details.
// //
// You should have received a copy of the GNU Lesser General Public // You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software // License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#ifndef _BPMDetect_H_ #ifndef _BPMDetect_H_
#define _BPMDetect_H_ #define _BPMDetect_H_
#include "STTypes.h" #include "STTypes.h"
#include "FIFOSampleBuffer.h" #include "FIFOSampleBuffer.h"
namespace soundtouch namespace soundtouch
{ {
/// Minimum allowed BPM rate. Used to restrict accepted result above a reasonable limit. /// Minimum allowed BPM rate. Used to restrict accepted result above a reasonable limit.
#define MIN_BPM 29 #define MIN_BPM 29
/// Maximum allowed BPM rate. Used to restrict accepted result below a reasonable limit. /// Maximum allowed BPM rate. Used to restrict accepted result below a reasonable limit.
#define MAX_BPM 200 #define MAX_BPM 200
/// Class for calculating BPM rate for audio data. /// Class for calculating BPM rate for audio data.
class BPMDetect class BPMDetect
{ {
protected: protected:
/// Auto-correlation accumulator bins. /// Auto-correlation accumulator bins.
float *xcorr; float *xcorr;
/// Amplitude envelope sliding average approximation level accumulator /// Amplitude envelope sliding average approximation level accumulator
double envelopeAccu; double envelopeAccu;
/// RMS volume sliding average approximation level accumulator /// RMS volume sliding average approximation level accumulator
double RMSVolumeAccu; double RMSVolumeAccu;
/// Sample average counter. /// Sample average counter.
int decimateCount; int decimateCount;
/// Sample average accumulator for FIFO-like decimation. /// Sample average accumulator for FIFO-like decimation.
soundtouch::LONG_SAMPLETYPE decimateSum; soundtouch::LONG_SAMPLETYPE decimateSum;
/// Decimate sound by this coefficient to reach approx. 500 Hz. /// Decimate sound by this coefficient to reach approx. 500 Hz.
int decimateBy; int decimateBy;
/// Auto-correlation window length /// Auto-correlation window length
int windowLen; int windowLen;
/// Number of channels (1 = mono, 2 = stereo) /// Number of channels (1 = mono, 2 = stereo)
int channels; int channels;
/// sample rate /// sample rate
int sampleRate; int sampleRate;
/// Beginning of auto-correlation window: Autocorrelation isn't being updated for /// Beginning of auto-correlation window: Autocorrelation isn't being updated for
/// the first these many correlation bins. /// the first these many correlation bins.
int windowStart; int windowStart;
/// FIFO-buffer for decimated processing samples. /// FIFO-buffer for decimated processing samples.
soundtouch::FIFOSampleBuffer *buffer; soundtouch::FIFOSampleBuffer *buffer;
/// Updates auto-correlation function for given number of decimated samples that /// 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 /// are read from the internal 'buffer' pipe (samples aren't removed from the pipe
/// though). /// though).
void updateXCorr(int process_samples /// How many samples are processed. void updateXCorr(int process_samples /// How many samples are processed.
); );
/// Decimates samples to approx. 500 Hz. /// Decimates samples to approx. 500 Hz.
/// ///
/// \return Number of output samples. /// \return Number of output samples.
int decimate(soundtouch::SAMPLETYPE *dest, ///< Destination buffer int decimate(soundtouch::SAMPLETYPE *dest, ///< Destination buffer
const soundtouch::SAMPLETYPE *src, ///< Source sample buffer const soundtouch::SAMPLETYPE *src, ///< Source sample buffer
int numsamples ///< Number of source samples. int numsamples ///< Number of source samples.
); );
/// Calculates amplitude envelope for the buffer of samples. /// Calculates amplitude envelope for the buffer of samples.
/// Result is output to 'samples'. /// Result is output to 'samples'.
void calcEnvelope(soundtouch::SAMPLETYPE *samples, ///< Pointer to input/output data buffer void calcEnvelope(soundtouch::SAMPLETYPE *samples, ///< Pointer to input/output data buffer
int numsamples ///< Number of samples in buffer int numsamples ///< Number of samples in buffer
); );
/// remove constant bias from xcorr data /// remove constant bias from xcorr data
void removeBias(); void removeBias();
public: public:
/// Constructor. /// Constructor.
BPMDetect(int numChannels, ///< Number of channels in sample data. BPMDetect(int numChannels, ///< Number of channels in sample data.
int sampleRate ///< Sample rate in Hz. int sampleRate ///< Sample rate in Hz.
); );
/// Destructor. /// Destructor.
virtual ~BPMDetect(); virtual ~BPMDetect();
/// Inputs a block of samples for analyzing: Envelopes the samples and then /// Inputs a block of samples for analyzing: Envelopes the samples and then
/// updates the autocorrelation estimation. When whole song data has been input /// updates the autocorrelation estimation. When whole song data has been input
/// in smaller blocks using this function, read the resulting bpm with 'getBpm' /// in smaller blocks using this function, read the resulting bpm with 'getBpm'
/// function. /// function.
/// ///
/// Notice that data in 'samples' array can be disrupted in processing. /// Notice that data in 'samples' array can be disrupted in processing.
void inputSamples(const soundtouch::SAMPLETYPE *samples, ///< Pointer to input/working data buffer void inputSamples(const soundtouch::SAMPLETYPE *samples, ///< Pointer to input/working data buffer
int numSamples ///< Number of samples in buffer int numSamples ///< Number of samples in buffer
); );
/// Analyzes the results and returns the BPM rate. Use this function to read result /// 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 /// after whole song data has been input to the class by consecutive calls of
/// 'inputSamples' function. /// 'inputSamples' function.
/// ///
/// \return Beats-per-minute rate, or zero if detection failed. /// \return Beats-per-minute rate, or zero if detection failed.
float getBpm(); float getBpm();
}; };
} }
#endif // _BPMDetect_H_ #endif // _BPMDetect_H_

View File

@ -1,274 +1,274 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// ///
/// A buffer class for temporarily storaging sound samples, operates as a /// A buffer class for temporarily storaging sound samples, operates as a
/// first-in-first-out pipe. /// first-in-first-out pipe.
/// ///
/// Samples are added to the end of the sample buffer with the 'putSamples' /// 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 /// function, and are received from the beginning of the buffer by calling
/// the 'receiveSamples' function. The class automatically removes the /// the 'receiveSamples' function. The class automatically removes the
/// outputted samples from the buffer, as well as grows the buffer size /// outputted samples from the buffer, as well as grows the buffer size
/// whenever necessary. /// whenever necessary.
/// ///
/// Author : Copyright (c) Olli Parviainen /// Author : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi /// Author e-mail : oparviai 'at' iki.fi
/// SoundTouch WWW: http://www.surina.net/soundtouch /// SoundTouch WWW: http://www.surina.net/soundtouch
/// ///
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// Last changed : $Date: 2012-11-08 20:53:01 +0200 (Thu, 08 Nov 2012) $ // Last changed : $Date: 2012-11-08 18:53:01 +0000 (Thu, 08 Nov 2012) $
// File revision : $Revision: 4 $ // File revision : $Revision: 4 $
// //
// $Id: FIFOSampleBuffer.cpp 160 2012-11-08 18:53:01Z oparviai $ // $Id: FIFOSampleBuffer.cpp 160 2012-11-08 18:53:01Z oparviai $
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// License : // License :
// //
// SoundTouch audio processing library // SoundTouch audio processing library
// Copyright (c) Olli Parviainen // Copyright (c) Olli Parviainen
// //
// This library is free software; you can redistribute it and/or // This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public // modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either // License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version. // 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, // This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details. // Lesser General Public License for more details.
// //
// You should have received a copy of the GNU Lesser General Public // You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software // License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include <stdlib.h> #include <stdlib.h>
#include <memory.h> #include <memory.h>
#include <string.h> #include <string.h>
#include <assert.h> #include <assert.h>
#include "FIFOSampleBuffer.h" #include "FIFOSampleBuffer.h"
using namespace soundtouch; using namespace soundtouch;
// Constructor // Constructor
FIFOSampleBuffer::FIFOSampleBuffer(int numChannels) FIFOSampleBuffer::FIFOSampleBuffer(int numChannels)
{ {
assert(numChannels > 0); assert(numChannels > 0);
sizeInBytes = 0; // reasonable initial value sizeInBytes = 0; // reasonable initial value
buffer = NULL; buffer = NULL;
bufferUnaligned = NULL; bufferUnaligned = NULL;
samplesInBuffer = 0; samplesInBuffer = 0;
bufferPos = 0; bufferPos = 0;
channels = (uint)numChannels; channels = (uint)numChannels;
ensureCapacity(32); // allocate initial capacity ensureCapacity(32); // allocate initial capacity
} }
// destructor // destructor
FIFOSampleBuffer::~FIFOSampleBuffer() FIFOSampleBuffer::~FIFOSampleBuffer()
{ {
delete[] bufferUnaligned; delete[] bufferUnaligned;
bufferUnaligned = NULL; bufferUnaligned = NULL;
buffer = NULL; buffer = NULL;
} }
// Sets number of channels, 1 = mono, 2 = stereo // Sets number of channels, 1 = mono, 2 = stereo
void FIFOSampleBuffer::setChannels(int numChannels) void FIFOSampleBuffer::setChannels(int numChannels)
{ {
uint usedBytes; uint usedBytes;
assert(numChannels > 0); assert(numChannels > 0);
usedBytes = channels * samplesInBuffer; usedBytes = channels * samplesInBuffer;
channels = (uint)numChannels; channels = (uint)numChannels;
samplesInBuffer = usedBytes / channels; samplesInBuffer = usedBytes / channels;
} }
// if output location pointer 'bufferPos' isn't zero, 'rewinds' the buffer and // if output location pointer 'bufferPos' isn't zero, 'rewinds' the buffer and
// zeroes this pointer by copying samples from the 'bufferPos' pointer // zeroes this pointer by copying samples from the 'bufferPos' pointer
// location on to the beginning of the buffer. // location on to the beginning of the buffer.
void FIFOSampleBuffer::rewind() void FIFOSampleBuffer::rewind()
{ {
if (buffer && bufferPos) if (buffer && bufferPos)
{ {
memmove(buffer, ptrBegin(), sizeof(SAMPLETYPE) * channels * samplesInBuffer); memmove(buffer, ptrBegin(), sizeof(SAMPLETYPE) * channels * samplesInBuffer);
bufferPos = 0; bufferPos = 0;
} }
} }
// Adds 'numSamples' pcs of samples from the 'samples' memory position to // Adds 'numSamples' pcs of samples from the 'samples' memory position to
// the sample buffer. // the sample buffer.
void FIFOSampleBuffer::putSamples(const SAMPLETYPE *samples, uint nSamples) void FIFOSampleBuffer::putSamples(const SAMPLETYPE *samples, uint nSamples)
{ {
memcpy(ptrEnd(nSamples), samples, sizeof(SAMPLETYPE) * nSamples * channels); memcpy(ptrEnd(nSamples), samples, sizeof(SAMPLETYPE) * nSamples * channels);
samplesInBuffer += nSamples; samplesInBuffer += nSamples;
} }
// Increases the number of samples in the buffer without copying any actual // Increases the number of samples in the buffer without copying any actual
// samples. // samples.
// //
// This function is used to update the number of samples in the sample buffer // This function is used to update the number of samples in the sample buffer
// when accessing the buffer directly with 'ptrEnd' function. Please be // when accessing the buffer directly with 'ptrEnd' function. Please be
// careful though! // careful though!
void FIFOSampleBuffer::putSamples(uint nSamples) void FIFOSampleBuffer::putSamples(uint nSamples)
{ {
uint req; uint req;
req = samplesInBuffer + nSamples; req = samplesInBuffer + nSamples;
ensureCapacity(req); ensureCapacity(req);
samplesInBuffer += nSamples; samplesInBuffer += nSamples;
} }
// Returns a pointer to the end of the used part of the sample buffer (i.e. // 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 // 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! // inserting new samples into the sample buffer directly. Please be careful!
// //
// Parameter 'slackCapacity' tells the function how much free capacity (in // Parameter 'slackCapacity' tells the function how much free capacity (in
// terms of samples) there _at least_ should be, in order to the caller to // 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, // succesfully insert all the required samples to the buffer. When necessary,
// the function grows the buffer size to comply with this requirement. // the function grows the buffer size to comply with this requirement.
// //
// When using this function as means for inserting new samples, also remember // When using this function as means for inserting new samples, also remember
// to increase the sample count afterwards, by calling the // to increase the sample count afterwards, by calling the
// 'putSamples(numSamples)' function. // 'putSamples(numSamples)' function.
SAMPLETYPE *FIFOSampleBuffer::ptrEnd(uint slackCapacity) SAMPLETYPE *FIFOSampleBuffer::ptrEnd(uint slackCapacity)
{ {
ensureCapacity(samplesInBuffer + slackCapacity); ensureCapacity(samplesInBuffer + slackCapacity);
return buffer + samplesInBuffer * channels; return buffer + samplesInBuffer * channels;
} }
// Returns a pointer to the beginning of the currently non-outputted samples. // Returns a pointer to the beginning of the currently non-outputted samples.
// This function is provided for accessing the output samples directly. // This function is provided for accessing the output samples directly.
// Please be careful! // Please be careful!
// //
// When using this function to output samples, also remember to 'remove' the // When using this function to output samples, also remember to 'remove' the
// outputted samples from the buffer by calling the // outputted samples from the buffer by calling the
// 'receiveSamples(numSamples)' function // 'receiveSamples(numSamples)' function
SAMPLETYPE *FIFOSampleBuffer::ptrBegin() SAMPLETYPE *FIFOSampleBuffer::ptrBegin()
{ {
assert(buffer); assert(buffer);
return buffer + bufferPos * channels; return buffer + bufferPos * channels;
} }
// Ensures that the buffer has enought capacity, i.e. space for _at least_ // Ensures that the buffer has enought capacity, i.e. space for _at least_
// 'capacityRequirement' number of samples. The buffer is grown in steps of // 'capacityRequirement' number of samples. The buffer is grown in steps of
// 4 kilobytes to eliminate the need for frequently growing up the buffer, // 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. // as well as to round the buffer size up to the virtual memory page size.
void FIFOSampleBuffer::ensureCapacity(uint capacityRequirement) void FIFOSampleBuffer::ensureCapacity(uint capacityRequirement)
{ {
SAMPLETYPE *tempUnaligned, *temp; SAMPLETYPE *tempUnaligned, *temp;
if (capacityRequirement > getCapacity()) if (capacityRequirement > getCapacity())
{ {
// enlarge the buffer in 4kbyte steps (round up to next 4k boundary) // enlarge the buffer in 4kbyte steps (round up to next 4k boundary)
sizeInBytes = (capacityRequirement * channels * sizeof(SAMPLETYPE) + 4095) & (uint)-4096; sizeInBytes = (capacityRequirement * channels * sizeof(SAMPLETYPE) + 4095) & (uint)-4096;
assert(sizeInBytes % 2 == 0); assert(sizeInBytes % 2 == 0);
tempUnaligned = new SAMPLETYPE[sizeInBytes / sizeof(SAMPLETYPE) + 16 / sizeof(SAMPLETYPE)]; tempUnaligned = new SAMPLETYPE[sizeInBytes / sizeof(SAMPLETYPE) + 16 / sizeof(SAMPLETYPE)];
if (tempUnaligned == NULL) if (tempUnaligned == NULL)
{ {
ST_THROW_RT_ERROR("Couldn't allocate memory!\n"); ST_THROW_RT_ERROR("Couldn't allocate memory!\n");
} }
// Align the buffer to begin at 16byte cache line boundary for optimal performance // Align the buffer to begin at 16byte cache line boundary for optimal performance
temp = (SAMPLETYPE *)SOUNDTOUCH_ALIGN_POINTER_16(tempUnaligned); temp = (SAMPLETYPE *)SOUNDTOUCH_ALIGN_POINTER_16(tempUnaligned);
if (samplesInBuffer) if (samplesInBuffer)
{ {
memcpy(temp, ptrBegin(), samplesInBuffer * channels * sizeof(SAMPLETYPE)); memcpy(temp, ptrBegin(), samplesInBuffer * channels * sizeof(SAMPLETYPE));
} }
delete[] bufferUnaligned; delete[] bufferUnaligned;
buffer = temp; buffer = temp;
bufferUnaligned = tempUnaligned; bufferUnaligned = tempUnaligned;
bufferPos = 0; bufferPos = 0;
} }
else else
{ {
// simply rewind the buffer (if necessary) // simply rewind the buffer (if necessary)
rewind(); rewind();
} }
} }
// Returns the current buffer capacity in terms of samples // Returns the current buffer capacity in terms of samples
uint FIFOSampleBuffer::getCapacity() const uint FIFOSampleBuffer::getCapacity() const
{ {
return sizeInBytes / (channels * sizeof(SAMPLETYPE)); return sizeInBytes / (channels * sizeof(SAMPLETYPE));
} }
// Returns the number of samples currently in the buffer // Returns the number of samples currently in the buffer
uint FIFOSampleBuffer::numSamples() const uint FIFOSampleBuffer::numSamples() const
{ {
return samplesInBuffer; return samplesInBuffer;
} }
// Output samples from beginning of the sample buffer. Copies demanded number // Output samples from beginning of the sample buffer. Copies demanded number
// of samples to output and removes them from the sample buffer. If there // of samples to output and removes them from the sample buffer. If there
// are less than 'numsample' samples in the buffer, returns all available. // are less than 'numsample' samples in the buffer, returns all available.
// //
// Returns number of samples copied. // Returns number of samples copied.
uint FIFOSampleBuffer::receiveSamples(SAMPLETYPE *output, uint maxSamples) uint FIFOSampleBuffer::receiveSamples(SAMPLETYPE *output, uint maxSamples)
{ {
uint num; uint num;
num = (maxSamples > samplesInBuffer) ? samplesInBuffer : maxSamples; num = (maxSamples > samplesInBuffer) ? samplesInBuffer : maxSamples;
memcpy(output, ptrBegin(), channels * sizeof(SAMPLETYPE) * num); memcpy(output, ptrBegin(), channels * sizeof(SAMPLETYPE) * num);
return receiveSamples(num); return receiveSamples(num);
} }
// Removes samples from the beginning of the sample buffer without copying them // 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 // anywhere. Used to reduce the number of samples in the buffer, when accessing
// the sample buffer with the 'ptrBegin' function. // the sample buffer with the 'ptrBegin' function.
uint FIFOSampleBuffer::receiveSamples(uint maxSamples) uint FIFOSampleBuffer::receiveSamples(uint maxSamples)
{ {
if (maxSamples >= samplesInBuffer) if (maxSamples >= samplesInBuffer)
{ {
uint temp; uint temp;
temp = samplesInBuffer; temp = samplesInBuffer;
samplesInBuffer = 0; samplesInBuffer = 0;
return temp; return temp;
} }
samplesInBuffer -= maxSamples; samplesInBuffer -= maxSamples;
bufferPos += maxSamples; bufferPos += maxSamples;
return maxSamples; return maxSamples;
} }
// Returns nonzero if the sample buffer is empty // Returns nonzero if the sample buffer is empty
int FIFOSampleBuffer::isEmpty() const int FIFOSampleBuffer::isEmpty() const
{ {
return (samplesInBuffer == 0) ? 1 : 0; return (samplesInBuffer == 0) ? 1 : 0;
} }
// Clears the sample buffer // Clears the sample buffer
void FIFOSampleBuffer::clear() void FIFOSampleBuffer::clear()
{ {
samplesInBuffer = 0; samplesInBuffer = 0;
bufferPos = 0; bufferPos = 0;
} }
/// allow trimming (downwards) amount of samples in pipeline. /// allow trimming (downwards) amount of samples in pipeline.
/// Returns adjusted amount of samples /// Returns adjusted amount of samples
uint FIFOSampleBuffer::adjustAmountOfSamples(uint numSamples) uint FIFOSampleBuffer::adjustAmountOfSamples(uint numSamples)
{ {
if (numSamples < samplesInBuffer) if (numSamples < samplesInBuffer)
{ {
samplesInBuffer = numSamples; samplesInBuffer = numSamples;
} }
return samplesInBuffer; return samplesInBuffer;
} }

View File

@ -1,178 +1,178 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// ///
/// A buffer class for temporarily storaging sound samples, operates as a /// A buffer class for temporarily storaging sound samples, operates as a
/// first-in-first-out pipe. /// first-in-first-out pipe.
/// ///
/// Samples are added to the end of the sample buffer with the 'putSamples' /// 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 /// function, and are received from the beginning of the buffer by calling
/// the 'receiveSamples' function. The class automatically removes the /// the 'receiveSamples' function. The class automatically removes the
/// output samples from the buffer as well as grows the storage size /// output samples from the buffer as well as grows the storage size
/// whenever necessary. /// whenever necessary.
/// ///
/// Author : Copyright (c) Olli Parviainen /// Author : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi /// Author e-mail : oparviai 'at' iki.fi
/// SoundTouch WWW: http://www.surina.net/soundtouch /// SoundTouch WWW: http://www.surina.net/soundtouch
/// ///
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// Last changed : $Date: 2012-06-13 22:29:53 +0300 (Wed, 13 Jun 2012) $ // Last changed : $Date: 2012-06-13 19:29:53 +0000 (Wed, 13 Jun 2012) $
// File revision : $Revision: 4 $ // File revision : $Revision: 4 $
// //
// $Id: FIFOSampleBuffer.h 143 2012-06-13 19:29:53Z oparviai $ // $Id: FIFOSampleBuffer.h 143 2012-06-13 19:29:53Z oparviai $
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// License : // License :
// //
// SoundTouch audio processing library // SoundTouch audio processing library
// Copyright (c) Olli Parviainen // Copyright (c) Olli Parviainen
// //
// This library is free software; you can redistribute it and/or // This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public // modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either // License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version. // 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, // This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details. // Lesser General Public License for more details.
// //
// You should have received a copy of the GNU Lesser General Public // You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software // License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#ifndef FIFOSampleBuffer_H #ifndef FIFOSampleBuffer_H
#define FIFOSampleBuffer_H #define FIFOSampleBuffer_H
#include "FIFOSamplePipe.h" #include "FIFOSamplePipe.h"
namespace soundtouch namespace soundtouch
{ {
/// Sample buffer working in FIFO (first-in-first-out) principle. The class takes /// 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. /// 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 /// Notice that in case of stereo audio, one sample is considered to consist of
/// both channel data. /// both channel data.
class FIFOSampleBuffer : public FIFOSamplePipe class FIFOSampleBuffer : public FIFOSamplePipe
{ {
private: private:
/// Sample buffer. /// Sample buffer.
SAMPLETYPE *buffer; SAMPLETYPE *buffer;
// Raw unaligned buffer memory. 'buffer' is made aligned by pointing it to first // Raw unaligned buffer memory. 'buffer' is made aligned by pointing it to first
// 16-byte aligned location of this buffer // 16-byte aligned location of this buffer
SAMPLETYPE *bufferUnaligned; SAMPLETYPE *bufferUnaligned;
/// Sample buffer size in bytes /// Sample buffer size in bytes
uint sizeInBytes; uint sizeInBytes;
/// How many samples are currently in buffer. /// How many samples are currently in buffer.
uint samplesInBuffer; uint samplesInBuffer;
/// Channels, 1=mono, 2=stereo. /// Channels, 1=mono, 2=stereo.
uint channels; uint channels;
/// Current position pointer to the buffer. This pointer is increased when samples are /// 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) /// removed from the pipe so that it's necessary to actually rewind buffer (move data)
/// only new data when is put to the pipe. /// only new data when is put to the pipe.
uint bufferPos; uint bufferPos;
/// Rewind the buffer by moving data from position pointed by 'bufferPos' to real /// Rewind the buffer by moving data from position pointed by 'bufferPos' to real
/// beginning of the buffer. /// beginning of the buffer.
void rewind(); void rewind();
/// Ensures that the buffer has capacity for at least this many samples. /// Ensures that the buffer has capacity for at least this many samples.
void ensureCapacity(uint capacityRequirement); void ensureCapacity(uint capacityRequirement);
/// Returns current capacity. /// Returns current capacity.
uint getCapacity() const; uint getCapacity() const;
public: public:
/// Constructor /// Constructor
FIFOSampleBuffer(int numChannels = 2 ///< Number of channels, 1=mono, 2=stereo. FIFOSampleBuffer(int numChannels = 2 ///< Number of channels, 1=mono, 2=stereo.
///< Default is stereo. ///< Default is stereo.
); );
/// destructor /// destructor
~FIFOSampleBuffer(); ~FIFOSampleBuffer();
/// Returns a pointer to the beginning of the output samples. /// Returns a pointer to the beginning of the output samples.
/// This function is provided for accessing the output samples directly. /// This function is provided for accessing the output samples directly.
/// Please be careful for not to corrupt the book-keeping! /// Please be careful for not to corrupt the book-keeping!
/// ///
/// When using this function to output samples, also remember to 'remove' the /// When using this function to output samples, also remember to 'remove' the
/// output samples from the buffer by calling the /// output samples from the buffer by calling the
/// 'receiveSamples(numSamples)' function /// 'receiveSamples(numSamples)' function
virtual SAMPLETYPE *ptrBegin(); virtual SAMPLETYPE *ptrBegin();
/// Returns a pointer to the end of the used part of the sample buffer (i.e. /// 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 /// 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 /// inserting new samples into the sample buffer directly. Please be careful
/// not corrupt the book-keeping! /// not corrupt the book-keeping!
/// ///
/// When using this function as means for inserting new samples, also remember /// When using this function as means for inserting new samples, also remember
/// to increase the sample count afterwards, by calling the /// to increase the sample count afterwards, by calling the
/// 'putSamples(numSamples)' function. /// 'putSamples(numSamples)' function.
SAMPLETYPE *ptrEnd( SAMPLETYPE *ptrEnd(
uint slackCapacity ///< How much free capacity (in samples) there _at least_ uint slackCapacity ///< How much free capacity (in samples) there _at least_
///< should be so that the caller can succesfully insert the ///< should be so that the caller can succesfully insert the
///< desired samples to the buffer. If necessary, the function ///< desired samples to the buffer. If necessary, the function
///< grows the buffer size to comply with this requirement. ///< grows the buffer size to comply with this requirement.
); );
/// Adds 'numSamples' pcs of samples from the 'samples' memory position to /// Adds 'numSamples' pcs of samples from the 'samples' memory position to
/// the sample buffer. /// the sample buffer.
virtual void putSamples(const SAMPLETYPE *samples, ///< Pointer to samples. virtual void putSamples(const SAMPLETYPE *samples, ///< Pointer to samples.
uint numSamples ///< Number of samples to insert. uint numSamples ///< Number of samples to insert.
); );
/// Adjusts the book-keeping to increase number of samples in the buffer without /// Adjusts the book-keeping to increase number of samples in the buffer without
/// copying any actual samples. /// copying any actual samples.
/// ///
/// This function is used to update the number of samples in the sample buffer /// This function is used to update the number of samples in the sample buffer
/// when accessing the buffer directly with 'ptrEnd' function. Please be /// when accessing the buffer directly with 'ptrEnd' function. Please be
/// careful though! /// careful though!
virtual void putSamples(uint numSamples ///< Number of samples been inserted. virtual void putSamples(uint numSamples ///< Number of samples been inserted.
); );
/// Output samples from beginning of the sample buffer. Copies requested samples to /// 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 /// output buffer and removes them from the sample buffer. If there are less than
/// 'numsample' samples in the buffer, returns all that available. /// 'numsample' samples in the buffer, returns all that available.
/// ///
/// \return Number of samples returned. /// \return Number of samples returned.
virtual uint receiveSamples(SAMPLETYPE *output, ///< Buffer where to copy output samples. virtual uint receiveSamples(SAMPLETYPE *output, ///< Buffer where to copy output samples.
uint maxSamples ///< How many samples to receive at max. uint maxSamples ///< How many samples to receive at max.
); );
/// Adjusts book-keeping so that given number of samples are removed from beginning of the /// Adjusts book-keeping so that given number of samples are removed from beginning of the
/// sample buffer without copying them anywhere. /// sample buffer without copying them anywhere.
/// ///
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly /// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
/// with 'ptrBegin' function. /// with 'ptrBegin' function.
virtual uint receiveSamples(uint maxSamples ///< Remove this many samples from the beginning of pipe. virtual uint receiveSamples(uint maxSamples ///< Remove this many samples from the beginning of pipe.
); );
/// Returns number of samples currently available. /// Returns number of samples currently available.
virtual uint numSamples() const; virtual uint numSamples() const;
/// Sets number of channels, 1 = mono, 2 = stereo. /// Sets number of channels, 1 = mono, 2 = stereo.
void setChannels(int numChannels); void setChannels(int numChannels);
/// Returns nonzero if there aren't any samples available for outputting. /// Returns nonzero if there aren't any samples available for outputting.
virtual int isEmpty() const; virtual int isEmpty() const;
/// Clears all the samples. /// Clears all the samples.
virtual void clear(); virtual void clear();
/// allow trimming (downwards) amount of samples in pipeline. /// allow trimming (downwards) amount of samples in pipeline.
/// Returns adjusted amount of samples /// Returns adjusted amount of samples
uint adjustAmountOfSamples(uint numSamples); uint adjustAmountOfSamples(uint numSamples);
}; };
} }
#endif #endif

View File

@ -1,234 +1,234 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// ///
/// 'FIFOSamplePipe' : An abstract base class for classes that manipulate sound /// 'FIFOSamplePipe' : An abstract base class for classes that manipulate sound
/// samples by operating like a first-in-first-out pipe: New samples are fed /// 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 /// into one end of the pipe with the 'putSamples' function, and the processed
/// samples are received from the other end with the 'receiveSamples' function. /// samples are received from the other end with the 'receiveSamples' function.
/// ///
/// 'FIFOProcessor' : A base class for classes the do signal processing with /// 'FIFOProcessor' : A base class for classes the do signal processing with
/// the samples while operating like a first-in-first-out pipe. When samples /// the samples while operating like a first-in-first-out pipe. When samples
/// are input with the 'putSamples' function, the class processes them /// are input with the 'putSamples' function, the class processes them
/// and moves the processed samples to the given 'output' pipe object, which /// and moves the processed samples to the given 'output' pipe object, which
/// may be either another processing stage, or a fifo sample buffer object. /// may be either another processing stage, or a fifo sample buffer object.
/// ///
/// Author : Copyright (c) Olli Parviainen /// Author : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi /// Author e-mail : oparviai 'at' iki.fi
/// SoundTouch WWW: http://www.surina.net/soundtouch /// SoundTouch WWW: http://www.surina.net/soundtouch
/// ///
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// Last changed : $Date: 2012-06-13 22:29:53 +0300 (Wed, 13 Jun 2012) $ // Last changed : $Date: 2012-06-13 19:29:53 +0000 (Wed, 13 Jun 2012) $
// File revision : $Revision: 4 $ // File revision : $Revision: 4 $
// //
// $Id: FIFOSamplePipe.h 143 2012-06-13 19:29:53Z oparviai $ // $Id: FIFOSamplePipe.h 143 2012-06-13 19:29:53Z oparviai $
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// License : // License :
// //
// SoundTouch audio processing library // SoundTouch audio processing library
// Copyright (c) Olli Parviainen // Copyright (c) Olli Parviainen
// //
// This library is free software; you can redistribute it and/or // This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public // modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either // License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version. // 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, // This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details. // Lesser General Public License for more details.
// //
// You should have received a copy of the GNU Lesser General Public // You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software // License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#ifndef FIFOSamplePipe_H #ifndef FIFOSamplePipe_H
#define FIFOSamplePipe_H #define FIFOSamplePipe_H
#include <assert.h> #include <assert.h>
#include <stdlib.h> #include <stdlib.h>
#include "STTypes.h" #include "STTypes.h"
namespace soundtouch namespace soundtouch
{ {
/// Abstract base class for FIFO (first-in-first-out) sample processing classes. /// Abstract base class for FIFO (first-in-first-out) sample processing classes.
class FIFOSamplePipe class FIFOSamplePipe
{ {
public: public:
// virtual default destructor // virtual default destructor
virtual ~FIFOSamplePipe() {} virtual ~FIFOSamplePipe() {}
/// Returns a pointer to the beginning of the output samples. /// Returns a pointer to the beginning of the output samples.
/// This function is provided for accessing the output samples directly. /// This function is provided for accessing the output samples directly.
/// Please be careful for not to corrupt the book-keeping! /// Please be careful for not to corrupt the book-keeping!
/// ///
/// When using this function to output samples, also remember to 'remove' the /// When using this function to output samples, also remember to 'remove' the
/// output samples from the buffer by calling the /// output samples from the buffer by calling the
/// 'receiveSamples(numSamples)' function /// 'receiveSamples(numSamples)' function
virtual SAMPLETYPE *ptrBegin() = 0; virtual SAMPLETYPE *ptrBegin() = 0;
/// Adds 'numSamples' pcs of samples from the 'samples' memory position to /// Adds 'numSamples' pcs of samples from the 'samples' memory position to
/// the sample buffer. /// the sample buffer.
virtual void putSamples(const SAMPLETYPE *samples, ///< Pointer to samples. virtual void putSamples(const SAMPLETYPE *samples, ///< Pointer to samples.
uint numSamples ///< Number of samples to insert. uint numSamples ///< Number of samples to insert.
) = 0; ) = 0;
// Moves samples from the 'other' pipe instance to this instance. // Moves samples from the 'other' pipe instance to this instance.
void moveSamples(FIFOSamplePipe &other ///< Other pipe instance where from the receive the data. void moveSamples(FIFOSamplePipe &other ///< Other pipe instance where from the receive the data.
) )
{ {
int oNumSamples = other.numSamples(); int oNumSamples = other.numSamples();
putSamples(other.ptrBegin(), oNumSamples); putSamples(other.ptrBegin(), oNumSamples);
other.receiveSamples(oNumSamples); other.receiveSamples(oNumSamples);
}; };
/// Output samples from beginning of the sample buffer. Copies requested samples to /// 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 /// output buffer and removes them from the sample buffer. If there are less than
/// 'numsample' samples in the buffer, returns all that available. /// 'numsample' samples in the buffer, returns all that available.
/// ///
/// \return Number of samples returned. /// \return Number of samples returned.
virtual uint receiveSamples(SAMPLETYPE *output, ///< Buffer where to copy output samples. virtual uint receiveSamples(SAMPLETYPE *output, ///< Buffer where to copy output samples.
uint maxSamples ///< How many samples to receive at max. uint maxSamples ///< How many samples to receive at max.
) = 0; ) = 0;
/// Adjusts book-keeping so that given number of samples are removed from beginning of the /// Adjusts book-keeping so that given number of samples are removed from beginning of the
/// sample buffer without copying them anywhere. /// sample buffer without copying them anywhere.
/// ///
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly /// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
/// with 'ptrBegin' function. /// with 'ptrBegin' function.
virtual uint receiveSamples(uint maxSamples ///< Remove this many samples from the beginning of pipe. virtual uint receiveSamples(uint maxSamples ///< Remove this many samples from the beginning of pipe.
) = 0; ) = 0;
/// Returns number of samples currently available. /// Returns number of samples currently available.
virtual uint numSamples() const = 0; virtual uint numSamples() const = 0;
// Returns nonzero if there aren't any samples available for outputting. // Returns nonzero if there aren't any samples available for outputting.
virtual int isEmpty() const = 0; virtual int isEmpty() const = 0;
/// Clears all the samples. /// Clears all the samples.
virtual void clear() = 0; virtual void clear() = 0;
/// allow trimming (downwards) amount of samples in pipeline. /// allow trimming (downwards) amount of samples in pipeline.
/// Returns adjusted amount of samples /// Returns adjusted amount of samples
virtual uint adjustAmountOfSamples(uint numSamples) = 0; virtual uint adjustAmountOfSamples(uint numSamples) = 0;
}; };
/// Base-class for sound processing routines working in FIFO principle. With this base /// 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, /// 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 /// so that samples that are fed into beginning of the pipe automatically go through
/// all the processing stages. /// all the processing stages.
/// ///
/// When samples are input to this class, they're first processed and then put to /// 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 /// 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. /// either other processing stage or a FIFO sample buffer.
class FIFOProcessor :public FIFOSamplePipe class FIFOProcessor :public FIFOSamplePipe
{ {
protected: protected:
/// Internal pipe where processed samples are put. /// Internal pipe where processed samples are put.
FIFOSamplePipe *output; FIFOSamplePipe *output;
/// Sets output pipe. /// Sets output pipe.
void setOutPipe(FIFOSamplePipe *pOutput) void setOutPipe(FIFOSamplePipe *pOutput)
{ {
assert(output == NULL); assert(output == NULL);
assert(pOutput != NULL); assert(pOutput != NULL);
output = pOutput; output = pOutput;
} }
/// Constructor. Doesn't define output pipe; it has to be set be /// Constructor. Doesn't define output pipe; it has to be set be
/// 'setOutPipe' function. /// 'setOutPipe' function.
FIFOProcessor() FIFOProcessor()
{ {
output = NULL; output = NULL;
} }
/// Constructor. Configures output pipe. /// Constructor. Configures output pipe.
FIFOProcessor(FIFOSamplePipe *pOutput ///< Output pipe. FIFOProcessor(FIFOSamplePipe *pOutput ///< Output pipe.
) )
{ {
output = pOutput; output = pOutput;
} }
/// Destructor. /// Destructor.
virtual ~FIFOProcessor() virtual ~FIFOProcessor()
{ {
} }
/// Returns a pointer to the beginning of the output samples. /// Returns a pointer to the beginning of the output samples.
/// This function is provided for accessing the output samples directly. /// This function is provided for accessing the output samples directly.
/// Please be careful for not to corrupt the book-keeping! /// Please be careful for not to corrupt the book-keeping!
/// ///
/// When using this function to output samples, also remember to 'remove' the /// When using this function to output samples, also remember to 'remove' the
/// output samples from the buffer by calling the /// output samples from the buffer by calling the
/// 'receiveSamples(numSamples)' function /// 'receiveSamples(numSamples)' function
virtual SAMPLETYPE *ptrBegin() virtual SAMPLETYPE *ptrBegin()
{ {
return output->ptrBegin(); return output->ptrBegin();
} }
public: public:
/// Output samples from beginning of the sample buffer. Copies requested samples to /// 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 /// output buffer and removes them from the sample buffer. If there are less than
/// 'numsample' samples in the buffer, returns all that available. /// 'numsample' samples in the buffer, returns all that available.
/// ///
/// \return Number of samples returned. /// \return Number of samples returned.
virtual uint receiveSamples(SAMPLETYPE *outBuffer, ///< Buffer where to copy output samples. virtual uint receiveSamples(SAMPLETYPE *outBuffer, ///< Buffer where to copy output samples.
uint maxSamples ///< How many samples to receive at max. uint maxSamples ///< How many samples to receive at max.
) )
{ {
return output->receiveSamples(outBuffer, maxSamples); return output->receiveSamples(outBuffer, maxSamples);
} }
/// Adjusts book-keeping so that given number of samples are removed from beginning of the /// Adjusts book-keeping so that given number of samples are removed from beginning of the
/// sample buffer without copying them anywhere. /// sample buffer without copying them anywhere.
/// ///
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly /// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
/// with 'ptrBegin' function. /// with 'ptrBegin' function.
virtual uint receiveSamples(uint maxSamples ///< Remove this many samples from the beginning of pipe. virtual uint receiveSamples(uint maxSamples ///< Remove this many samples from the beginning of pipe.
) )
{ {
return output->receiveSamples(maxSamples); return output->receiveSamples(maxSamples);
} }
/// Returns number of samples currently available. /// Returns number of samples currently available.
virtual uint numSamples() const virtual uint numSamples() const
{ {
return output->numSamples(); return output->numSamples();
} }
/// Returns nonzero if there aren't any samples available for outputting. /// Returns nonzero if there aren't any samples available for outputting.
virtual int isEmpty() const virtual int isEmpty() const
{ {
return output->isEmpty(); return output->isEmpty();
} }
/// allow trimming (downwards) amount of samples in pipeline. /// allow trimming (downwards) amount of samples in pipeline.
/// Returns adjusted amount of samples /// Returns adjusted amount of samples
virtual uint adjustAmountOfSamples(uint numSamples) virtual uint adjustAmountOfSamples(uint numSamples)
{ {
return output->adjustAmountOfSamples(numSamples); return output->adjustAmountOfSamples(numSamples);
} }
}; };
} }
#endif #endif

View File

@ -1,259 +1,322 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// ///
/// General FIR digital filter routines with MMX optimization. /// General FIR digital filter routines with MMX optimization.
/// ///
/// Note : MMX optimized functions reside in a separate, platform-specific file, /// Note : MMX optimized functions reside in a separate, platform-specific file,
/// e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp' /// e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'
/// ///
/// Author : Copyright (c) Olli Parviainen /// Author : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi /// Author e-mail : oparviai 'at' iki.fi
/// SoundTouch WWW: http://www.surina.net/soundtouch /// SoundTouch WWW: http://www.surina.net/soundtouch
/// ///
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// Last changed : $Date: 2011-09-02 21:56:11 +0300 (Fri, 02 Sep 2011) $ // Last changed : $Date: 2013-06-12 15:24:44 +0000 (Wed, 12 Jun 2013) $
// File revision : $Revision: 4 $ // File revision : $Revision: 4 $
// //
// $Id: FIRFilter.cpp 131 2011-09-02 18:56:11Z oparviai $ // $Id: FIRFilter.cpp 171 2013-06-12 15:24:44Z oparviai $
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// License : // License :
// //
// SoundTouch audio processing library // SoundTouch audio processing library
// Copyright (c) Olli Parviainen // Copyright (c) Olli Parviainen
// //
// This library is free software; you can redistribute it and/or // This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public // modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either // License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version. // 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, // This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details. // Lesser General Public License for more details.
// //
// You should have received a copy of the GNU Lesser General Public // You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software // License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include <memory.h> #include <memory.h>
#include <assert.h> #include <assert.h>
#include <math.h> #include <math.h>
#include <stdlib.h> #include <stdlib.h>
#include "FIRFilter.h" #include "FIRFilter.h"
#include "cpu_detect.h" #include "cpu_detect.h"
using namespace soundtouch; using namespace soundtouch;
/***************************************************************************** /*****************************************************************************
* *
* Implementation of the class 'FIRFilter' * Implementation of the class 'FIRFilter'
* *
*****************************************************************************/ *****************************************************************************/
FIRFilter::FIRFilter() FIRFilter::FIRFilter()
{ {
resultDivFactor = 0; resultDivFactor = 0;
resultDivider = 0; resultDivider = 0;
length = 0; length = 0;
lengthDiv8 = 0; lengthDiv8 = 0;
filterCoeffs = NULL; filterCoeffs = NULL;
} }
FIRFilter::~FIRFilter() FIRFilter::~FIRFilter()
{ {
delete[] filterCoeffs; delete[] filterCoeffs;
} }
// Usual C-version of the filter routine for stereo sound // Usual C-version of the filter routine for stereo sound
uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const
{ {
uint i, j, end; uint i, j, end;
LONG_SAMPLETYPE suml, sumr; LONG_SAMPLETYPE suml, sumr;
#ifdef SOUNDTOUCH_FLOAT_SAMPLES #ifdef SOUNDTOUCH_FLOAT_SAMPLES
// when using floating point samples, use a scaler instead of a divider // when using floating point samples, use a scaler instead of a divider
// because division is much slower operation than multiplying. // because division is much slower operation than multiplying.
double dScaler = 1.0 / (double)resultDivider; double dScaler = 1.0 / (double)resultDivider;
#endif #endif
assert(length != 0); assert(length != 0);
assert(src != NULL); assert(src != NULL);
assert(dest != NULL); assert(dest != NULL);
assert(filterCoeffs != NULL); assert(filterCoeffs != NULL);
end = 2 * (numSamples - length); end = 2 * (numSamples - length);
for (j = 0; j < end; j += 2) for (j = 0; j < end; j += 2)
{ {
const SAMPLETYPE *ptr; const SAMPLETYPE *ptr;
suml = sumr = 0; suml = sumr = 0;
ptr = src + j; ptr = src + j;
for (i = 0; i < length; i += 4) for (i = 0; i < length; i += 4)
{ {
// loop is unrolled by factor of 4 here for efficiency // loop is unrolled by factor of 4 here for efficiency
suml += ptr[2 * i + 0] * filterCoeffs[i + 0] + suml += ptr[2 * i + 0] * filterCoeffs[i + 0] +
ptr[2 * i + 2] * filterCoeffs[i + 1] + ptr[2 * i + 2] * filterCoeffs[i + 1] +
ptr[2 * i + 4] * filterCoeffs[i + 2] + ptr[2 * i + 4] * filterCoeffs[i + 2] +
ptr[2 * i + 6] * filterCoeffs[i + 3]; ptr[2 * i + 6] * filterCoeffs[i + 3];
sumr += ptr[2 * i + 1] * filterCoeffs[i + 0] + sumr += ptr[2 * i + 1] * filterCoeffs[i + 0] +
ptr[2 * i + 3] * filterCoeffs[i + 1] + ptr[2 * i + 3] * filterCoeffs[i + 1] +
ptr[2 * i + 5] * filterCoeffs[i + 2] + ptr[2 * i + 5] * filterCoeffs[i + 2] +
ptr[2 * i + 7] * filterCoeffs[i + 3]; ptr[2 * i + 7] * filterCoeffs[i + 3];
} }
#ifdef SOUNDTOUCH_INTEGER_SAMPLES #ifdef SOUNDTOUCH_INTEGER_SAMPLES
suml >>= resultDivFactor; suml >>= resultDivFactor;
sumr >>= resultDivFactor; sumr >>= resultDivFactor;
// saturate to 16 bit integer limits // saturate to 16 bit integer limits
suml = (suml < -32768) ? -32768 : (suml > 32767) ? 32767 : suml; suml = (suml < -32768) ? -32768 : (suml > 32767) ? 32767 : suml;
// saturate to 16 bit integer limits // saturate to 16 bit integer limits
sumr = (sumr < -32768) ? -32768 : (sumr > 32767) ? 32767 : sumr; sumr = (sumr < -32768) ? -32768 : (sumr > 32767) ? 32767 : sumr;
#else #else
suml *= dScaler; suml *= dScaler;
sumr *= dScaler; sumr *= dScaler;
#endif // SOUNDTOUCH_INTEGER_SAMPLES #endif // SOUNDTOUCH_INTEGER_SAMPLES
dest[j] = (SAMPLETYPE)suml; dest[j] = (SAMPLETYPE)suml;
dest[j + 1] = (SAMPLETYPE)sumr; dest[j + 1] = (SAMPLETYPE)sumr;
} }
return numSamples - length; return numSamples - length;
} }
// Usual C-version of the filter routine for mono sound // Usual C-version of the filter routine for mono sound
uint FIRFilter::evaluateFilterMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const uint FIRFilter::evaluateFilterMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const
{ {
uint i, j, end; uint i, j, end;
LONG_SAMPLETYPE sum; LONG_SAMPLETYPE sum;
#ifdef SOUNDTOUCH_FLOAT_SAMPLES #ifdef SOUNDTOUCH_FLOAT_SAMPLES
// when using floating point samples, use a scaler instead of a divider // when using floating point samples, use a scaler instead of a divider
// because division is much slower operation than multiplying. // because division is much slower operation than multiplying.
double dScaler = 1.0 / (double)resultDivider; double dScaler = 1.0 / (double)resultDivider;
#endif #endif
assert(length != 0); assert(length != 0);
end = numSamples - length; end = numSamples - length;
for (j = 0; j < end; j ++) for (j = 0; j < end; j ++)
{ {
sum = 0; sum = 0;
for (i = 0; i < length; i += 4) for (i = 0; i < length; i += 4)
{ {
// loop is unrolled by factor of 4 here for efficiency // loop is unrolled by factor of 4 here for efficiency
sum += src[i + 0] * filterCoeffs[i + 0] + sum += src[i + 0] * filterCoeffs[i + 0] +
src[i + 1] * filterCoeffs[i + 1] + src[i + 1] * filterCoeffs[i + 1] +
src[i + 2] * filterCoeffs[i + 2] + src[i + 2] * filterCoeffs[i + 2] +
src[i + 3] * filterCoeffs[i + 3]; src[i + 3] * filterCoeffs[i + 3];
} }
#ifdef SOUNDTOUCH_INTEGER_SAMPLES #ifdef SOUNDTOUCH_INTEGER_SAMPLES
sum >>= resultDivFactor; sum >>= resultDivFactor;
// saturate to 16 bit integer limits // saturate to 16 bit integer limits
sum = (sum < -32768) ? -32768 : (sum > 32767) ? 32767 : sum; sum = (sum < -32768) ? -32768 : (sum > 32767) ? 32767 : sum;
#else #else
sum *= dScaler; sum *= dScaler;
#endif // SOUNDTOUCH_INTEGER_SAMPLES #endif // SOUNDTOUCH_INTEGER_SAMPLES
dest[j] = (SAMPLETYPE)sum; dest[j] = (SAMPLETYPE)sum;
src ++; src ++;
} }
return end; return end;
} }
// Set filter coeffiecients and length. uint FIRFilter::evaluateFilterMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels) const
// {
// Throws an exception if filter length isn't divisible by 8 uint i, j, end, c;
void FIRFilter::setCoefficients(const SAMPLETYPE *coeffs, uint newLength, uint uResultDivFactor) LONG_SAMPLETYPE *sum=(LONG_SAMPLETYPE*)alloca(numChannels*sizeof(*sum));
{ #ifdef SOUNDTOUCH_FLOAT_SAMPLES
assert(newLength > 0); // when using floating point samples, use a scaler instead of a divider
if (newLength % 8) ST_THROW_RT_ERROR("FIR filter length not divisible by 8"); // because division is much slower operation than multiplying.
double dScaler = 1.0 / (double)resultDivider;
lengthDiv8 = newLength / 8; #endif
length = lengthDiv8 * 8;
assert(length == newLength); assert(length != 0);
assert(src != NULL);
resultDivFactor = uResultDivFactor; assert(dest != NULL);
resultDivider = (SAMPLETYPE)::pow(2.0, (int)resultDivFactor); assert(filterCoeffs != NULL);
delete[] filterCoeffs; end = numChannels * (numSamples - length);
filterCoeffs = new SAMPLETYPE[length];
memcpy(filterCoeffs, coeffs, length * sizeof(SAMPLETYPE)); for (c = 0; c < numChannels; c ++)
} {
sum[c] = 0;
}
uint FIRFilter::getLength() const
{ for (j = 0; j < end; j += numChannels)
return length; {
} const SAMPLETYPE *ptr;
ptr = src + j;
// Applies the filter to the given sequence of samples. for (i = 0; i < length; i ++)
// {
// Note : The amount of outputted samples is by value of 'filter_length' SAMPLETYPE coef=filterCoeffs[i];
// smaller than the amount of input samples. for (c = 0; c < numChannels; c ++)
uint FIRFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels) const {
{ sum[c] += ptr[0] * coef;
assert(numChannels == 1 || numChannels == 2); ptr ++;
}
assert(length > 0); }
assert(lengthDiv8 * 8 == length);
if (numSamples < length) return 0; for (c = 0; c < numChannels; c ++)
if (numChannels == 2) {
{ #ifdef SOUNDTOUCH_INTEGER_SAMPLES
return evaluateFilterStereo(dest, src, numSamples); sum[c] >>= resultDivFactor;
} else { #else
return evaluateFilterMono(dest, src, numSamples); sum[c] *= dScaler;
} #endif // SOUNDTOUCH_INTEGER_SAMPLES
} *dest = (SAMPLETYPE)sum[c];
dest++;
sum[c] = 0;
}
// Operator 'new' is overloaded so that it automatically creates a suitable instance }
// depending on if we've a MMX-capable CPU available or not. return numSamples - length;
void * FIRFilter::operator new(size_t s) }
{
// Notice! don't use "new FIRFilter" directly, use "newInstance" to create a new instance instead!
ST_THROW_RT_ERROR("Error in FIRFilter::new: Don't use 'new FIRFilter', use 'newInstance' member instead!"); // Set filter coeffiecients and length.
return newInstance(); //
} // Throws an exception if filter length isn't divisible by 8
void FIRFilter::setCoefficients(const SAMPLETYPE *coeffs, uint newLength, uint uResultDivFactor)
{
FIRFilter * FIRFilter::newInstance() assert(newLength > 0);
{ if (newLength % 8) ST_THROW_RT_ERROR("FIR filter length not divisible by 8");
uint uExtensions;
lengthDiv8 = newLength / 8;
uExtensions = detectCPUextensions(); length = lengthDiv8 * 8;
assert(length == newLength);
// Check if MMX/SSE instruction set extensions supported by CPU
resultDivFactor = uResultDivFactor;
#ifdef SOUNDTOUCH_ALLOW_MMX resultDivider = (SAMPLETYPE)::pow(2.0, (int)resultDivFactor);
// MMX routines available only with integer sample types
if (uExtensions & SUPPORT_MMX) delete[] filterCoeffs;
{ filterCoeffs = new SAMPLETYPE[length];
return ::new FIRFilterMMX; memcpy(filterCoeffs, coeffs, length * sizeof(SAMPLETYPE));
} }
else
#endif // SOUNDTOUCH_ALLOW_MMX
uint FIRFilter::getLength() const
#ifdef SOUNDTOUCH_ALLOW_SSE {
if (uExtensions & SUPPORT_SSE) return length;
{ }
// SSE support
return ::new FIRFilterSSE;
}
else // Applies the filter to the given sequence of samples.
#endif // SOUNDTOUCH_ALLOW_SSE //
// Note : The amount of outputted samples is by value of 'filter_length'
{ // smaller than the amount of input samples.
// ISA optimizations not supported, use plain C version uint FIRFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels) const
return ::new FIRFilter; {
} assert(length > 0);
} assert(lengthDiv8 * 8 == length);
if (numSamples < length) return 0;
#ifndef USE_MULTICH_ALWAYS
if (numChannels == 1)
{
return evaluateFilterMono(dest, src, numSamples);
}
else if (numChannels == 2)
{
return evaluateFilterStereo(dest, src, numSamples);
}
else
#endif // USE_MULTICH_ALWAYS
{
assert(numChannels > 0);
return evaluateFilterMulti(dest, src, numSamples, numChannels);
}
}
// Operator 'new' is overloaded so that it automatically creates a suitable instance
// depending on if we've a MMX-capable CPU available or not.
void * FIRFilter::operator new(size_t s)
{
// Notice! don't use "new FIRFilter" directly, use "newInstance" to create a new instance instead!
ST_THROW_RT_ERROR("Error in FIRFilter::new: Don't use 'new FIRFilter', use 'newInstance' member instead!");
return newInstance();
}
FIRFilter * FIRFilter::newInstance()
{
uint uExtensions;
uExtensions = detectCPUextensions();
// Check if MMX/SSE instruction set extensions supported by CPU
#ifdef SOUNDTOUCH_ALLOW_MMX
// MMX routines available only with integer sample types
if (uExtensions & SUPPORT_MMX)
{
return ::new FIRFilterMMX;
}
else
#endif // SOUNDTOUCH_ALLOW_MMX
#ifdef SOUNDTOUCH_ALLOW_SSE
if (uExtensions & SUPPORT_SSE)
{
// SSE support
return ::new FIRFilterSSE;
}
else
#endif // SOUNDTOUCH_ALLOW_SSE
{
// ISA optimizations not supported, use plain C version
return ::new FIRFilter;
}
}

View File

@ -1,145 +1,146 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// ///
/// General FIR digital filter routines with MMX optimization. /// General FIR digital filter routines with MMX optimization.
/// ///
/// Note : MMX optimized functions reside in a separate, platform-specific file, /// Note : MMX optimized functions reside in a separate, platform-specific file,
/// e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp' /// e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'
/// ///
/// Author : Copyright (c) Olli Parviainen /// Author : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi /// Author e-mail : oparviai 'at' iki.fi
/// SoundTouch WWW: http://www.surina.net/soundtouch /// SoundTouch WWW: http://www.surina.net/soundtouch
/// ///
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// Last changed : $Date: 2011-02-13 21:13:57 +0200 (Sun, 13 Feb 2011) $ // Last changed : $Date: 2013-06-12 15:24:44 +0000 (Wed, 12 Jun 2013) $
// File revision : $Revision: 4 $ // File revision : $Revision: 4 $
// //
// $Id: FIRFilter.h 104 2011-02-13 19:13:57Z oparviai $ // $Id: FIRFilter.h 171 2013-06-12 15:24:44Z oparviai $
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// License : // License :
// //
// SoundTouch audio processing library // SoundTouch audio processing library
// Copyright (c) Olli Parviainen // Copyright (c) Olli Parviainen
// //
// This library is free software; you can redistribute it and/or // This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public // modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either // License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version. // 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, // This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details. // Lesser General Public License for more details.
// //
// You should have received a copy of the GNU Lesser General Public // You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software // License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#ifndef FIRFilter_H #ifndef FIRFilter_H
#define FIRFilter_H #define FIRFilter_H
#include <stddef.h> #include <stddef.h>
#include "STTypes.h" #include "STTypes.h"
namespace soundtouch namespace soundtouch
{ {
class FIRFilter class FIRFilter
{ {
protected: protected:
// Number of FIR filter taps // Number of FIR filter taps
uint length; uint length;
// Number of FIR filter taps divided by 8 // Number of FIR filter taps divided by 8
uint lengthDiv8; uint lengthDiv8;
// Result divider factor in 2^k format // Result divider factor in 2^k format
uint resultDivFactor; uint resultDivFactor;
// Result divider value. // Result divider value.
SAMPLETYPE resultDivider; SAMPLETYPE resultDivider;
// Memory for filter coefficients // Memory for filter coefficients
SAMPLETYPE *filterCoeffs; SAMPLETYPE *filterCoeffs;
virtual uint evaluateFilterStereo(SAMPLETYPE *dest, virtual uint evaluateFilterStereo(SAMPLETYPE *dest,
const SAMPLETYPE *src, const SAMPLETYPE *src,
uint numSamples) const; uint numSamples) const;
virtual uint evaluateFilterMono(SAMPLETYPE *dest, virtual uint evaluateFilterMono(SAMPLETYPE *dest,
const SAMPLETYPE *src, const SAMPLETYPE *src,
uint numSamples) const; uint numSamples) const;
virtual uint evaluateFilterMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels) const;
public:
FIRFilter(); public:
virtual ~FIRFilter(); FIRFilter();
virtual ~FIRFilter();
/// Operator 'new' is overloaded so that it automatically creates a suitable instance
/// depending on if we've a MMX-capable CPU available or not. /// Operator 'new' is overloaded so that it automatically creates a suitable instance
static void * operator new(size_t s); /// depending on if we've a MMX-capable CPU available or not.
static void * operator new(size_t s);
static FIRFilter *newInstance();
static FIRFilter *newInstance();
/// Applies the filter to the given sequence of samples.
/// Note : The amount of outputted samples is by value of 'filter_length' /// Applies the filter to the given sequence of samples.
/// smaller than the amount of input 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, /// \return Number of samples copied to 'dest'.
const SAMPLETYPE *src, uint evaluate(SAMPLETYPE *dest,
uint numSamples, const SAMPLETYPE *src,
uint numChannels) const; uint numSamples,
uint numChannels) const;
uint getLength() const;
uint getLength() const;
virtual void setCoefficients(const SAMPLETYPE *coeffs,
uint newLength, virtual void setCoefficients(const SAMPLETYPE *coeffs,
uint uResultDivFactor); uint newLength,
}; uint uResultDivFactor);
};
// Optional subclasses that implement CPU-specific optimizations:
// Optional subclasses that implement CPU-specific optimizations:
#ifdef SOUNDTOUCH_ALLOW_MMX
#ifdef SOUNDTOUCH_ALLOW_MMX
/// Class that implements MMX optimized functions exclusive for 16bit integer samples type.
class FIRFilterMMX : public FIRFilter /// Class that implements MMX optimized functions exclusive for 16bit integer samples type.
{ class FIRFilterMMX : public FIRFilter
protected: {
short *filterCoeffsUnalign; protected:
short *filterCoeffsAlign; short *filterCoeffsUnalign;
short *filterCoeffsAlign;
virtual uint evaluateFilterStereo(short *dest, const short *src, uint numSamples) const;
public: virtual uint evaluateFilterStereo(short *dest, const short *src, uint numSamples) const;
FIRFilterMMX(); public:
~FIRFilterMMX(); FIRFilterMMX();
~FIRFilterMMX();
virtual void setCoefficients(const short *coeffs, uint newLength, uint uResultDivFactor);
}; virtual void setCoefficients(const short *coeffs, uint newLength, uint uResultDivFactor);
};
#endif // SOUNDTOUCH_ALLOW_MMX
#endif // SOUNDTOUCH_ALLOW_MMX
#ifdef SOUNDTOUCH_ALLOW_SSE
/// Class that implements SSE optimized functions exclusive for floating point samples type. #ifdef SOUNDTOUCH_ALLOW_SSE
class FIRFilterSSE : public FIRFilter /// Class that implements SSE optimized functions exclusive for floating point samples type.
{ class FIRFilterSSE : public FIRFilter
protected: {
float *filterCoeffsUnalign; protected:
float *filterCoeffsAlign; float *filterCoeffsUnalign;
float *filterCoeffsAlign;
virtual uint evaluateFilterStereo(float *dest, const float *src, uint numSamples) const;
public: virtual uint evaluateFilterStereo(float *dest, const float *src, uint numSamples) const;
FIRFilterSSE(); public:
~FIRFilterSSE(); FIRFilterSSE();
~FIRFilterSSE();
virtual void setCoefficients(const float *coeffs, uint newLength, uint uResultDivFactor);
}; virtual void setCoefficients(const float *coeffs, uint newLength, uint uResultDivFactor);
};
#endif // SOUNDTOUCH_ALLOW_SSE
#endif // SOUNDTOUCH_ALLOW_SSE
}
}
#endif // FIRFilter_H
#endif // FIRFilter_H

View File

@ -1,276 +1,276 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// ///
/// Peak detection routine. /// Peak detection routine.
/// ///
/// The routine detects highest value on an array of values and calculates the /// The routine detects highest value on an array of values and calculates the
/// precise peak location as a mass-center of the 'hump' around the peak value. /// precise peak location as a mass-center of the 'hump' around the peak value.
/// ///
/// Author : Copyright (c) Olli Parviainen /// Author : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi /// Author e-mail : oparviai 'at' iki.fi
/// SoundTouch WWW: http://www.surina.net/soundtouch /// SoundTouch WWW: http://www.surina.net/soundtouch
/// ///
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// Last changed : $Date: 2012-12-28 21:52:47 +0200 (Fri, 28 Dec 2012) $ // Last changed : $Date: 2012-12-28 19:52:47 +0000 (Fri, 28 Dec 2012) $
// File revision : $Revision: 4 $ // File revision : $Revision: 4 $
// //
// $Id: PeakFinder.cpp 164 2012-12-28 19:52:47Z oparviai $ // $Id: PeakFinder.cpp 164 2012-12-28 19:52:47Z oparviai $
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// License : // License :
// //
// SoundTouch audio processing library // SoundTouch audio processing library
// Copyright (c) Olli Parviainen // Copyright (c) Olli Parviainen
// //
// This library is free software; you can redistribute it and/or // This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public // modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either // License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version. // 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, // This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details. // Lesser General Public License for more details.
// //
// You should have received a copy of the GNU Lesser General Public // You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software // License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include <math.h> #include <math.h>
#include <assert.h> #include <assert.h>
#include "PeakFinder.h" #include "PeakFinder.h"
using namespace soundtouch; using namespace soundtouch;
#define max(x, y) (((x) > (y)) ? (x) : (y)) #define max(x, y) (((x) > (y)) ? (x) : (y))
PeakFinder::PeakFinder() PeakFinder::PeakFinder()
{ {
minPos = maxPos = 0; minPos = maxPos = 0;
} }
// Finds real 'top' of a peak hump from neighnourhood of the given 'peakpos'. // Finds real 'top' of a peak hump from neighnourhood of the given 'peakpos'.
int PeakFinder::findTop(const float *data, int peakpos) const int PeakFinder::findTop(const float *data, int peakpos) const
{ {
int i; int i;
int start, end; int start, end;
float refvalue; float refvalue;
refvalue = data[peakpos]; refvalue = data[peakpos];
// seek within ±10 points // seek within ±10 points
start = peakpos - 10; start = peakpos - 10;
if (start < minPos) start = minPos; if (start < minPos) start = minPos;
end = peakpos + 10; end = peakpos + 10;
if (end > maxPos) end = maxPos; if (end > maxPos) end = maxPos;
for (i = start; i <= end; i ++) for (i = start; i <= end; i ++)
{ {
if (data[i] > refvalue) if (data[i] > refvalue)
{ {
peakpos = i; peakpos = i;
refvalue = data[i]; refvalue = data[i];
} }
} }
// failure if max value is at edges of seek range => it's not peak, it's at slope. // failure if max value is at edges of seek range => it's not peak, it's at slope.
if ((peakpos == start) || (peakpos == end)) return 0; if ((peakpos == start) || (peakpos == end)) return 0;
return peakpos; return peakpos;
} }
// Finds 'ground level' of a peak hump by starting from 'peakpos' and proceeding // Finds 'ground level' of a peak hump by starting from 'peakpos' and proceeding
// to direction defined by 'direction' until next 'hump' after minimum value will // to direction defined by 'direction' until next 'hump' after minimum value will
// begin // begin
int PeakFinder::findGround(const float *data, int peakpos, int direction) const int PeakFinder::findGround(const float *data, int peakpos, int direction) const
{ {
int lowpos; int lowpos;
int pos; int pos;
int climb_count; int climb_count;
float refvalue; float refvalue;
float delta; float delta;
climb_count = 0; climb_count = 0;
refvalue = data[peakpos]; refvalue = data[peakpos];
lowpos = peakpos; lowpos = peakpos;
pos = peakpos; pos = peakpos;
while ((pos > minPos+1) && (pos < maxPos-1)) while ((pos > minPos+1) && (pos < maxPos-1))
{ {
int prevpos; int prevpos;
prevpos = pos; prevpos = pos;
pos += direction; pos += direction;
// calculate derivate // calculate derivate
delta = data[pos] - data[prevpos]; delta = data[pos] - data[prevpos];
if (delta <= 0) if (delta <= 0)
{ {
// going downhill, ok // going downhill, ok
if (climb_count) if (climb_count)
{ {
climb_count --; // decrease climb count climb_count --; // decrease climb count
} }
// check if new minimum found // check if new minimum found
if (data[pos] < refvalue) if (data[pos] < refvalue)
{ {
// new minimum found // new minimum found
lowpos = pos; lowpos = pos;
refvalue = data[pos]; refvalue = data[pos];
} }
} }
else else
{ {
// going uphill, increase climbing counter // going uphill, increase climbing counter
climb_count ++; climb_count ++;
if (climb_count > 5) break; // we've been climbing too long => it's next uphill => quit if (climb_count > 5) break; // we've been climbing too long => it's next uphill => quit
} }
} }
return lowpos; return lowpos;
} }
// Find offset where the value crosses the given level, when starting from 'peakpos' and // Find offset where the value crosses the given level, when starting from 'peakpos' and
// proceeds to direction defined in 'direction' // proceeds to direction defined in 'direction'
int PeakFinder::findCrossingLevel(const float *data, float level, int peakpos, int direction) const int PeakFinder::findCrossingLevel(const float *data, float level, int peakpos, int direction) const
{ {
float peaklevel; float peaklevel;
int pos; int pos;
peaklevel = data[peakpos]; peaklevel = data[peakpos];
assert(peaklevel >= level); assert(peaklevel >= level);
pos = peakpos; pos = peakpos;
while ((pos >= minPos) && (pos < maxPos)) while ((pos >= minPos) && (pos < maxPos))
{ {
if (data[pos + direction] < level) return pos; // crossing found if (data[pos + direction] < level) return pos; // crossing found
pos += direction; pos += direction;
} }
return -1; // not found return -1; // not found
} }
// Calculates the center of mass location of 'data' array items between 'firstPos' and 'lastPos' // Calculates the center of mass location of 'data' array items between 'firstPos' and 'lastPos'
double PeakFinder::calcMassCenter(const float *data, int firstPos, int lastPos) const double PeakFinder::calcMassCenter(const float *data, int firstPos, int lastPos) const
{ {
int i; int i;
float sum; float sum;
float wsum; float wsum;
sum = 0; sum = 0;
wsum = 0; wsum = 0;
for (i = firstPos; i <= lastPos; i ++) for (i = firstPos; i <= lastPos; i ++)
{ {
sum += (float)i * data[i]; sum += (float)i * data[i];
wsum += data[i]; wsum += data[i];
} }
if (wsum < 1e-6) return 0; if (wsum < 1e-6) return 0;
return sum / wsum; return sum / wsum;
} }
/// get exact center of peak near given position by calculating local mass of center /// get exact center of peak near given position by calculating local mass of center
double PeakFinder::getPeakCenter(const float *data, int peakpos) const double PeakFinder::getPeakCenter(const float *data, int peakpos) const
{ {
float peakLevel; // peak level float peakLevel; // peak level
int crosspos1, crosspos2; // position where the peak 'hump' crosses cutting level int crosspos1, crosspos2; // position where the peak 'hump' crosses cutting level
float cutLevel; // cutting value float cutLevel; // cutting value
float groundLevel; // ground level of the peak float groundLevel; // ground level of the peak
int gp1, gp2; // bottom positions of the peak 'hump' int gp1, gp2; // bottom positions of the peak 'hump'
// find ground positions. // find ground positions.
gp1 = findGround(data, peakpos, -1); gp1 = findGround(data, peakpos, -1);
gp2 = findGround(data, peakpos, 1); gp2 = findGround(data, peakpos, 1);
groundLevel = 0.5f * (data[gp1] + data[gp2]); groundLevel = 0.5f * (data[gp1] + data[gp2]);
peakLevel = data[peakpos]; peakLevel = data[peakpos];
// calculate 70%-level of the peak // calculate 70%-level of the peak
cutLevel = 0.70f * peakLevel + 0.30f * groundLevel; cutLevel = 0.70f * peakLevel + 0.30f * groundLevel;
// find mid-level crossings // find mid-level crossings
crosspos1 = findCrossingLevel(data, cutLevel, peakpos, -1); crosspos1 = findCrossingLevel(data, cutLevel, peakpos, -1);
crosspos2 = findCrossingLevel(data, cutLevel, peakpos, 1); crosspos2 = findCrossingLevel(data, cutLevel, peakpos, 1);
if ((crosspos1 < 0) || (crosspos2 < 0)) return 0; // no crossing, no peak.. if ((crosspos1 < 0) || (crosspos2 < 0)) return 0; // no crossing, no peak..
// calculate mass center of the peak surroundings // calculate mass center of the peak surroundings
return calcMassCenter(data, crosspos1, crosspos2); return calcMassCenter(data, crosspos1, crosspos2);
} }
double PeakFinder::detectPeak(const float *data, int aminPos, int amaxPos) double PeakFinder::detectPeak(const float *data, int aminPos, int amaxPos)
{ {
int i; int i;
int peakpos; // position of peak level int peakpos; // position of peak level
double highPeak, peak; double highPeak, peak;
this->minPos = aminPos; this->minPos = aminPos;
this->maxPos = amaxPos; this->maxPos = amaxPos;
// find absolute peak // find absolute peak
peakpos = minPos; peakpos = minPos;
peak = data[minPos]; peak = data[minPos];
for (i = minPos + 1; i < maxPos; i ++) for (i = minPos + 1; i < maxPos; i ++)
{ {
if (data[i] > peak) if (data[i] > peak)
{ {
peak = data[i]; peak = data[i];
peakpos = i; peakpos = i;
} }
} }
// Calculate exact location of the highest peak mass center // Calculate exact location of the highest peak mass center
highPeak = getPeakCenter(data, peakpos); highPeak = getPeakCenter(data, peakpos);
peak = highPeak; peak = highPeak;
// Now check if the highest peak were in fact harmonic of the true base beat peak // Now check if the highest peak were in fact harmonic of the true base beat peak
// - sometimes the highest peak can be Nth harmonic of the true base peak yet // - sometimes the highest peak can be Nth harmonic of the true base peak yet
// just a slightly higher than the true base // just a slightly higher than the true base
for (i = 3; i < 10; i ++) for (i = 3; i < 10; i ++)
{ {
double peaktmp, harmonic; double peaktmp, harmonic;
int i1,i2; int i1,i2;
harmonic = (double)i * 0.5; harmonic = (double)i * 0.5;
peakpos = (int)(highPeak / harmonic + 0.5f); peakpos = (int)(highPeak / harmonic + 0.5f);
if (peakpos < minPos) break; if (peakpos < minPos) break;
peakpos = findTop(data, peakpos); // seek true local maximum index peakpos = findTop(data, peakpos); // seek true local maximum index
if (peakpos == 0) continue; // no local max here if (peakpos == 0) continue; // no local max here
// calculate mass-center of possible harmonic peak // calculate mass-center of possible harmonic peak
peaktmp = getPeakCenter(data, peakpos); peaktmp = getPeakCenter(data, peakpos);
// accept harmonic peak if // accept harmonic peak if
// (a) it is found // (a) it is found
// (b) is within ±4% of the expected harmonic interval // (b) is within ±4% of the expected harmonic interval
// (c) has at least half x-corr value of the max. peak // (c) has at least half x-corr value of the max. peak
double diff = harmonic * peaktmp / highPeak; double diff = harmonic * peaktmp / highPeak;
if ((diff < 0.96) || (diff > 1.04)) continue; // peak too afar from expected if ((diff < 0.96) || (diff > 1.04)) continue; // peak too afar from expected
// now compare to highest detected peak // now compare to highest detected peak
i1 = (int)(highPeak + 0.5); i1 = (int)(highPeak + 0.5);
i2 = (int)(peaktmp + 0.5); i2 = (int)(peaktmp + 0.5);
if (data[i2] >= 0.4*data[i1]) if (data[i2] >= 0.4*data[i1])
{ {
// The harmonic is at least half as high primary peak, // The harmonic is at least half as high primary peak,
// thus use the harmonic peak instead // thus use the harmonic peak instead
peak = peaktmp; peak = peaktmp;
} }
} }
return peak; return peak;
} }

View File

@ -1,97 +1,97 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// ///
/// The routine detects highest value on an array of values and calculates the /// The routine detects highest value on an array of values and calculates the
/// precise peak location as a mass-center of the 'hump' around the peak value. /// precise peak location as a mass-center of the 'hump' around the peak value.
/// ///
/// Author : Copyright (c) Olli Parviainen /// Author : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi /// Author e-mail : oparviai 'at' iki.fi
/// SoundTouch WWW: http://www.surina.net/soundtouch /// SoundTouch WWW: http://www.surina.net/soundtouch
/// ///
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// Last changed : $Date: 2011-12-30 22:33:46 +0200 (Fri, 30 Dec 2011) $ // Last changed : $Date: 2011-12-30 20:33:46 +0000 (Fri, 30 Dec 2011) $
// File revision : $Revision: 4 $ // File revision : $Revision: 4 $
// //
// $Id: PeakFinder.h 132 2011-12-30 20:33:46Z oparviai $ // $Id: PeakFinder.h 132 2011-12-30 20:33:46Z oparviai $
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// License : // License :
// //
// SoundTouch audio processing library // SoundTouch audio processing library
// Copyright (c) Olli Parviainen // Copyright (c) Olli Parviainen
// //
// This library is free software; you can redistribute it and/or // This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public // modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either // License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version. // 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, // This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details. // Lesser General Public License for more details.
// //
// You should have received a copy of the GNU Lesser General Public // You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software // License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#ifndef _PeakFinder_H_ #ifndef _PeakFinder_H_
#define _PeakFinder_H_ #define _PeakFinder_H_
namespace soundtouch namespace soundtouch
{ {
class PeakFinder class PeakFinder
{ {
protected: protected:
/// Min, max allowed peak positions within the data vector /// Min, max allowed peak positions within the data vector
int minPos, maxPos; int minPos, maxPos;
/// Calculates the mass center between given vector items. /// Calculates the mass center between given vector items.
double calcMassCenter(const float *data, ///< Data vector. double calcMassCenter(const float *data, ///< Data vector.
int firstPos, ///< Index of first vector item beloging to the peak. int firstPos, ///< Index of first vector item beloging to the peak.
int lastPos ///< Index of last vector item beloging to the peak. int lastPos ///< Index of last vector item beloging to the peak.
) const; ) const;
/// Finds the data vector index where the monotoniously decreasing signal crosses the /// Finds the data vector index where the monotoniously decreasing signal crosses the
/// given level. /// given level.
int findCrossingLevel(const float *data, ///< Data vector. int findCrossingLevel(const float *data, ///< Data vector.
float level, ///< Goal crossing level. float level, ///< Goal crossing level.
int peakpos, ///< Peak position index within the data vector. int peakpos, ///< Peak position index within the data vector.
int direction /// Direction where to proceed from the peak: 1 = right, -1 = left. int direction /// Direction where to proceed from the peak: 1 = right, -1 = left.
) const; ) const;
// Finds real 'top' of a peak hump from neighnourhood of the given 'peakpos'. // Finds real 'top' of a peak hump from neighnourhood of the given 'peakpos'.
int findTop(const float *data, int peakpos) const; int findTop(const float *data, int peakpos) const;
/// Finds the 'ground' level, i.e. smallest level between two neighbouring peaks, to right- /// Finds the 'ground' level, i.e. smallest level between two neighbouring peaks, to right-
/// or left-hand side of the given peak position. /// or left-hand side of the given peak position.
int findGround(const float *data, /// Data vector. int findGround(const float *data, /// Data vector.
int peakpos, /// Peak position index within the data vector. int peakpos, /// Peak position index within the data vector.
int direction /// Direction where to proceed from the peak: 1 = right, -1 = left. int direction /// Direction where to proceed from the peak: 1 = right, -1 = left.
) const; ) const;
/// get exact center of peak near given position by calculating local mass of center /// get exact center of peak near given position by calculating local mass of center
double getPeakCenter(const float *data, int peakpos) const; double getPeakCenter(const float *data, int peakpos) const;
public: public:
/// Constructor. /// Constructor.
PeakFinder(); PeakFinder();
/// Detect exact peak position of the data vector by finding the largest peak 'hump' /// Detect exact peak position of the data vector by finding the largest peak 'hump'
/// and calculating the mass-center location of the peak hump. /// and calculating the mass-center location of the peak hump.
/// ///
/// \return The location of the largest base harmonic peak hump. /// \return The location of the largest base harmonic peak hump.
double detectPeak(const float *data, /// Data vector to be analyzed. The data vector has double detectPeak(const float *data, /// Data vector to be analyzed. The data vector has
/// to be at least 'maxPos' items long. /// to be at least 'maxPos' items long.
int minPos, ///< Min allowed peak location within the vector data. int minPos, ///< Min allowed peak location within the vector data.
int maxPos ///< Max allowed peak location within the vector data. int maxPos ///< Max allowed peak location within the vector data.
); );
}; };
} }
#endif // _PeakFinder_H_ #endif // _PeakFinder_H_

File diff suppressed because it is too large Load Diff

View File

@ -1,159 +1,160 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// ///
/// Sample rate transposer. Changes sample rate by using linear interpolation /// Sample rate transposer. Changes sample rate by using linear interpolation
/// together with anti-alias filtering (first order interpolation with anti- /// together with anti-alias filtering (first order interpolation with anti-
/// alias filtering should be quite adequate for this application). /// alias filtering should be quite adequate for this application).
/// ///
/// Use either of the derived classes of 'RateTransposerInteger' or /// Use either of the derived classes of 'RateTransposerInteger' or
/// 'RateTransposerFloat' for corresponding integer/floating point tranposing /// 'RateTransposerFloat' for corresponding integer/floating point tranposing
/// algorithm implementation. /// algorithm implementation.
/// ///
/// Author : Copyright (c) Olli Parviainen /// Author : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi /// Author e-mail : oparviai 'at' iki.fi
/// SoundTouch WWW: http://www.surina.net/soundtouch /// SoundTouch WWW: http://www.surina.net/soundtouch
/// ///
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// Last changed : $Date: 2009-02-21 18:00:14 +0200 (Sat, 21 Feb 2009) $ // Last changed : $Date: 2013-06-12 15:24:44 +0000 (Wed, 12 Jun 2013) $
// File revision : $Revision: 4 $ // File revision : $Revision: 4 $
// //
// $Id: RateTransposer.h 63 2009-02-21 16:00:14Z oparviai $ // $Id: RateTransposer.h 171 2013-06-12 15:24:44Z oparviai $
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// License : // License :
// //
// SoundTouch audio processing library // SoundTouch audio processing library
// Copyright (c) Olli Parviainen // Copyright (c) Olli Parviainen
// //
// This library is free software; you can redistribute it and/or // This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public // modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either // License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version. // 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, // This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details. // Lesser General Public License for more details.
// //
// You should have received a copy of the GNU Lesser General Public // You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software // License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#ifndef RateTransposer_H #ifndef RateTransposer_H
#define RateTransposer_H #define RateTransposer_H
#include <stddef.h> #include <stddef.h>
#include "AAFilter.h" #include "AAFilter.h"
#include "FIFOSamplePipe.h" #include "FIFOSamplePipe.h"
#include "FIFOSampleBuffer.h" #include "FIFOSampleBuffer.h"
#include "STTypes.h" #include "STTypes.h"
namespace soundtouch namespace soundtouch
{ {
/// A common linear samplerate transposer class. /// A common linear samplerate transposer class.
/// ///
/// Note: Use function "RateTransposer::newInstance()" to create a new class /// Note: Use function "RateTransposer::newInstance()" to create a new class
/// instance instead of the "new" operator; that function automatically /// instance instead of the "new" operator; that function automatically
/// chooses a correct implementation depending on if integer or floating /// chooses a correct implementation depending on if integer or floating
/// arithmetics are to be used. /// arithmetics are to be used.
class RateTransposer : public FIFOProcessor class RateTransposer : public FIFOProcessor
{ {
protected: protected:
/// Anti-alias filter object /// Anti-alias filter object
AAFilter *pAAFilter; AAFilter *pAAFilter;
float fRate; float fRate;
int numChannels; int numChannels;
/// Buffer for collecting samples to feed the anti-alias filter between /// Buffer for collecting samples to feed the anti-alias filter between
/// two batches /// two batches
FIFOSampleBuffer storeBuffer; FIFOSampleBuffer storeBuffer;
/// Buffer for keeping samples between transposing & anti-alias filter /// Buffer for keeping samples between transposing & anti-alias filter
FIFOSampleBuffer tempBuffer; FIFOSampleBuffer tempBuffer;
/// Output sample buffer /// Output sample buffer
FIFOSampleBuffer outputBuffer; FIFOSampleBuffer outputBuffer;
BOOL bUseAAFilter; BOOL bUseAAFilter;
virtual void resetRegisters() = 0; virtual void resetRegisters() = 0;
virtual uint transposeStereo(SAMPLETYPE *dest, virtual int transposeStereo(SAMPLETYPE *dest,
const SAMPLETYPE *src, const SAMPLETYPE *src,
uint numSamples) = 0; uint numSamples) = 0;
virtual uint transposeMono(SAMPLETYPE *dest, virtual int transposeMono(SAMPLETYPE *dest,
const SAMPLETYPE *src, const SAMPLETYPE *src,
uint numSamples) = 0; uint numSamples) = 0;
inline uint transpose(SAMPLETYPE *dest, virtual int transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples) = 0;
const SAMPLETYPE *src, inline int transpose(SAMPLETYPE *dest,
uint numSamples); const SAMPLETYPE *src,
uint numSamples);
void downsample(const SAMPLETYPE *src,
uint numSamples); void downsample(const SAMPLETYPE *src,
void upsample(const SAMPLETYPE *src, uint numSamples);
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. /// Transposes sample rate by applying anti-alias filter to prevent folding.
/// The maximum amount of samples that can be returned at a time is set by /// Returns amount of samples returned in the "dest" buffer.
/// the 'set_returnBuffer_size' function. /// The maximum amount of samples that can be returned at a time is set by
void processSamples(const SAMPLETYPE *src, /// the 'set_returnBuffer_size' function.
uint numSamples); void processSamples(const SAMPLETYPE *src,
uint numSamples);
public:
RateTransposer(); public:
virtual ~RateTransposer(); RateTransposer();
virtual ~RateTransposer();
/// Operator 'new' is overloaded so that it automatically creates a suitable instance
/// depending on if we're to use integer or floating point arithmetics. /// Operator 'new' is overloaded so that it automatically creates a suitable instance
static void *operator new(size_t s); /// depending on if we're to use integer or floating point arithmetics.
static void *operator new(size_t s);
/// Use this function instead of "new" operator to create a new instance of this class.
/// This function automatically chooses a correct implementation, depending on if /// Use this function instead of "new" operator to create a new instance of this class.
/// integer ot floating point arithmetics are to be used. /// This function automatically chooses a correct implementation, depending on if
static RateTransposer *newInstance(); /// integer ot floating point arithmetics are to be used.
static RateTransposer *newInstance();
/// Returns the output buffer object
FIFOSamplePipe *getOutput() { return &outputBuffer; }; /// Returns the output buffer object
FIFOSamplePipe *getOutput() { return &outputBuffer; };
/// Returns the store buffer object
FIFOSamplePipe *getStore() { return &storeBuffer; }; /// Returns the store buffer object
FIFOSamplePipe *getStore() { return &storeBuffer; };
/// Return anti-alias filter object
AAFilter *getAAFilter(); /// Return anti-alias filter object
AAFilter *getAAFilter();
/// Enables/disables the anti-alias filter. Zero to disable, nonzero to enable
void enableAAFilter(BOOL newMode); /// 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; /// 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. /// Sets new target rate. Normal rate = 1.0, smaller values represent slower
virtual void setRate(float newRate); /// rate, larger faster rates.
virtual void setRate(float newRate);
/// Sets the number of channels, 1 = mono, 2 = stereo
void setChannels(int channels); /// Sets the number of channels, 1 = mono, 2 = stereo
void setChannels(int channels);
/// Adds 'numSamples' pcs of samples from the 'samples' memory position into
/// the input of the object. /// Adds 'numSamples' pcs of samples from the 'samples' memory position into
void putSamples(const SAMPLETYPE *samples, uint numSamples); /// the input of the object.
void putSamples(const SAMPLETYPE *samples, uint numSamples);
/// Clears all the samples in the object
void clear(); /// Clears all the samples in the object
void clear();
/// Returns nonzero if there aren't any samples available for outputting.
int isEmpty() const; /// Returns nonzero if there aren't any samples available for outputting.
}; int isEmpty() const;
};
}
}
#endif
#endif

View File

@ -1,191 +1,194 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// ///
/// Common type definitions for SoundTouch audio processing library. /// Common type definitions for SoundTouch audio processing library.
/// ///
/// Author : Copyright (c) Olli Parviainen /// Author : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi /// Author e-mail : oparviai 'at' iki.fi
/// SoundTouch WWW: http://www.surina.net/soundtouch /// SoundTouch WWW: http://www.surina.net/soundtouch
/// ///
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// Last changed : $Date: 2012-12-28 16:53:56 +0200 (Fri, 28 Dec 2012) $ // Last changed : $Date: 2013-06-12 15:24:44 +0000 (Wed, 12 Jun 2013) $
// File revision : $Revision: 3 $ // File revision : $Revision: 3 $
// //
// $Id: STTypes.h 162 2012-12-28 14:53:56Z oparviai $ // $Id: STTypes.h 171 2013-06-12 15:24:44Z oparviai $
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// License : // License :
// //
// SoundTouch audio processing library // SoundTouch audio processing library
// Copyright (c) Olli Parviainen // Copyright (c) Olli Parviainen
// //
// This library is free software; you can redistribute it and/or // This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public // modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either // License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version. // 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, // This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details. // Lesser General Public License for more details.
// //
// You should have received a copy of the GNU Lesser General Public // You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software // License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#ifndef STTypes_H #ifndef STTypes_H
#define STTypes_H #define STTypes_H
typedef unsigned int uint; typedef unsigned int uint;
typedef unsigned long ulong; typedef unsigned long ulong;
// Patch for MinGW: on Win64 long is 32-bit // Patch for MinGW: on Win64 long is 32-bit
#ifdef _WIN64 #ifdef _WIN64
typedef unsigned long long ulongptr; typedef unsigned long long ulongptr;
#else #else
typedef ulong ulongptr; typedef ulong ulongptr;
#endif #endif
// Helper macro for aligning pointer up to next 16-byte boundary // Helper macro for aligning pointer up to next 16-byte boundary
#define SOUNDTOUCH_ALIGN_POINTER_16(x) ( ( (ulongptr)(x) + 15 ) & ~(ulongptr)15 ) #define SOUNDTOUCH_ALIGN_POINTER_16(x) ( ( (ulongptr)(x) + 15 ) & ~(ulongptr)15 )
#if (defined(__GNUC__) && !defined(ANDROID)) #if (defined(__GNUC__) && !defined(ANDROID))
// In GCC, include soundtouch_config.h made by config scritps. // In GCC, include soundtouch_config.h made by config scritps.
// Skip this in Android compilation that uses GCC but without configure scripts. // Skip this in Android compilation that uses GCC but without configure scripts.
//#include "soundtouch_config.h" #include "soundtouch_config.h"
#endif #endif
#ifndef _WINDEF_ #ifndef _WINDEF_
// if these aren't defined already by Windows headers, define now // if these aren't defined already by Windows headers, define now
#if defined(__APPLE__)
typedef signed char BOOL; typedef int BOOL;
#else
typedef int BOOL; #define FALSE 0
#endif #define TRUE 1
#define FALSE 0
#define TRUE 1 #endif // _WINDEF_
#endif // _WINDEF_
namespace soundtouch
{
namespace soundtouch /// Activate these undef's to overrule the possible sampletype
{ /// setting inherited from some other header file:
/// Activate these undef's to overrule the possible sampletype //#undef SOUNDTOUCH_INTEGER_SAMPLES
/// setting inherited from some other header file: //#undef SOUNDTOUCH_FLOAT_SAMPLES
#undef SOUNDTOUCH_INTEGER_SAMPLES
#undef SOUNDTOUCH_FLOAT_SAMPLES /// If following flag is defined, always uses multichannel processing
/// routines also for mono and stero sound. This is for routine testing
#if (defined(ANDROID) && defined(__SOFTFP__)) /// purposes; output should be same with either routines, yet disabling
// For Android compilation: Force use of Integer samples in case that /// the dedicated mono/stereo processing routines will result in slower
// compilation uses soft-floating point emulation - soft-fp is way too slow /// runtime performance so recommendation is to keep this off.
#undef SOUNDTOUCH_FLOAT_SAMPLES // #define USE_MULTICH_ALWAYS
#define SOUNDTOUCH_INTEGER_SAMPLES 1
#endif #if (defined(__SOFTFP__))
// For Android compilation: Force use of Integer samples in case that
#if !(SOUNDTOUCH_INTEGER_SAMPLES || SOUNDTOUCH_FLOAT_SAMPLES) // compilation uses soft-floating point emulation - soft-fp is way too slow
#undef SOUNDTOUCH_FLOAT_SAMPLES
/// Choose either 32bit floating point or 16bit integer sampletype #define SOUNDTOUCH_INTEGER_SAMPLES 1
/// by choosing one of the following defines, unless this selection #endif
/// has already been done in some other file.
//// #if !(SOUNDTOUCH_INTEGER_SAMPLES || SOUNDTOUCH_FLOAT_SAMPLES)
/// Notes:
/// - In Windows environment, choose the sample format with the /// Choose either 32bit floating point or 16bit integer sampletype
/// following defines. /// by choosing one of the following defines, unless this selection
/// - In GNU environment, the floating point samples are used by /// has already been done in some other file.
/// default, but integer samples can be chosen by giving the ////
/// following switch to the configure script: /// Notes:
/// ./configure --enable-integer-samples /// - In Windows environment, choose the sample format with the
/// However, if you still prefer to select the sample format here /// following defines.
/// also in GNU environment, then please #undef the INTEGER_SAMPLE /// - In GNU environment, the floating point samples are used by
/// and FLOAT_SAMPLE defines first as in comments above. /// default, but integer samples can be chosen by giving the
//#define SOUNDTOUCH_INTEGER_SAMPLES 1 //< 16bit integer samples /// following switch to the configure script:
#define SOUNDTOUCH_FLOAT_SAMPLES 1 //< 32bit float samples /// ./configure --enable-integer-samples
/// However, if you still prefer to select the sample format here
#endif /// also in GNU environment, then please #undef the INTEGER_SAMPLE
/// and FLOAT_SAMPLE defines first as in comments above.
#if (_M_IX86 || __i386__ || __x86_64__ || _M_X64) //#define SOUNDTOUCH_INTEGER_SAMPLES 1 //< 16bit integer samples
/// Define this to allow X86-specific assembler/intrinsic optimizations. #define SOUNDTOUCH_FLOAT_SAMPLES 1 //< 32bit float samples
/// Notice that library contains also usual C++ versions of each of these
/// these routines, so if you're having difficulties getting the optimized #endif
/// routines compiled for whatever reason, you may disable these optimizations
/// to make the library compile. #if (_M_IX86 || __i386__ || __x86_64__ || _M_X64)
/// Define this to allow X86-specific assembler/intrinsic optimizations.
#define SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS 1 /// Notice that library contains also usual C++ versions of each of these
/// these routines, so if you're having difficulties getting the optimized
/// In GNU environment, allow the user to override this setting by /// routines compiled for whatever reason, you may disable these optimizations
/// giving the following switch to the configure script: /// to make the library compile.
/// ./configure --disable-x86-optimizations
/// ./configure --enable-x86-optimizations=no #define SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS 1
#ifdef SOUNDTOUCH_DISABLE_X86_OPTIMIZATIONS
#undef SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS /// In GNU environment, allow the user to override this setting by
#endif /// giving the following switch to the configure script:
#else /// ./configure --disable-x86-optimizations
/// Always disable optimizations when not using a x86 systems. /// ./configure --enable-x86-optimizations=no
#undef SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS #ifdef SOUNDTOUCH_DISABLE_X86_OPTIMIZATIONS
#undef SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS
#endif #endif
#else
// If defined, allows the SIMD-optimized routines to take minor shortcuts /// Always disable optimizations when not using a x86 systems.
// for improved performance. Undefine to require faithfully similar SIMD #undef SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS
// calculations as in normal C implementation.
#define SOUNDTOUCH_ALLOW_NONEXACT_SIMD_OPTIMIZATION 1 #endif
// If defined, allows the SIMD-optimized routines to take minor shortcuts
#ifdef SOUNDTOUCH_INTEGER_SAMPLES // for improved performance. Undefine to require faithfully similar SIMD
// 16bit integer sample type // calculations as in normal C implementation.
typedef short SAMPLETYPE; #define SOUNDTOUCH_ALLOW_NONEXACT_SIMD_OPTIMIZATION 1
// data type for sample accumulation: Use 32bit integer to prevent overflows
typedef long LONG_SAMPLETYPE;
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
#ifdef SOUNDTOUCH_FLOAT_SAMPLES // 16bit integer sample type
// check that only one sample type is defined typedef short SAMPLETYPE;
#error "conflicting sample types defined" // data type for sample accumulation: Use 32bit integer to prevent overflows
#endif // SOUNDTOUCH_FLOAT_SAMPLES typedef long LONG_SAMPLETYPE;
#ifdef SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS #ifdef SOUNDTOUCH_FLOAT_SAMPLES
// Allow MMX optimizations // check that only one sample type is defined
#ifndef _M_X64 #error "conflicting sample types defined"
#define SOUNDTOUCH_ALLOW_MMX 1 #endif // SOUNDTOUCH_FLOAT_SAMPLES
#endif
#endif #ifdef SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS
// Allow MMX optimizations
#else #define SOUNDTOUCH_ALLOW_MMX 1
#endif
// floating point samples
typedef float SAMPLETYPE; #else
// data type for sample accumulation: Use double to utilize full precision.
typedef double LONG_SAMPLETYPE; // floating point samples
typedef float SAMPLETYPE;
#ifdef SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS // data type for sample accumulation: Use double to utilize full precision.
// Allow SSE optimizations typedef double LONG_SAMPLETYPE;
#define SOUNDTOUCH_ALLOW_SSE 1
#endif #ifdef SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS
// Allow SSE optimizations
#endif // SOUNDTOUCH_INTEGER_SAMPLES #define SOUNDTOUCH_ALLOW_SSE 1
#endif
};
#endif // SOUNDTOUCH_INTEGER_SAMPLES
// define ST_NO_EXCEPTION_HANDLING switch to disable throwing std exceptions:
#define ST_NO_EXCEPTION_HANDLING 1 };
#ifdef ST_NO_EXCEPTION_HANDLING
// Exceptions disabled. Throw asserts instead if enabled. // define ST_NO_EXCEPTION_HANDLING switch to disable throwing std exceptions:
#include <assert.h> // #define ST_NO_EXCEPTION_HANDLING 1
#define ST_THROW_RT_ERROR(x) {assert((const char *)x);} #ifdef ST_NO_EXCEPTION_HANDLING
#else // Exceptions disabled. Throw asserts instead if enabled.
// use c++ standard exceptions #include <assert.h>
#include <stdexcept> #define ST_THROW_RT_ERROR(x) {assert((const char *)x);}
#define ST_THROW_RT_ERROR(x) {throw std::runtime_error(x);} #else
#endif // use c++ standard exceptions
#include <stdexcept>
// When this #define is active, eliminates a clicking sound when the "rate" or "pitch" #define ST_THROW_RT_ERROR(x) {throw std::runtime_error(x);}
// parameter setting crosses from value <1 to >=1 or vice versa during processing. #endif
// Default is off as such crossover is untypical case and involves a slight sound
// quality compromise. // When this #define is active, eliminates a clicking sound when the "rate" or "pitch"
//#define SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER 1 // parameter setting crosses from value <1 to >=1 or vice versa during processing.
// Default is off as such crossover is untypical case and involves a slight sound
#endif // quality compromise.
//#define SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER 1
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,277 +1,277 @@
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
/// ///
/// SoundTouch - main class for tempo/pitch/rate adjusting routines. /// SoundTouch - main class for tempo/pitch/rate adjusting routines.
/// ///
/// Notes: /// Notes:
/// - Initialize the SoundTouch object instance by setting up the sound stream /// - Initialize the SoundTouch object instance by setting up the sound stream
/// parameters with functions 'setSampleRate' and 'setChannels', then set /// parameters with functions 'setSampleRate' and 'setChannels', then set
/// desired tempo/pitch/rate settings with the corresponding functions. /// desired tempo/pitch/rate settings with the corresponding functions.
/// ///
/// - The SoundTouch class behaves like a first-in-first-out pipeline: The /// - 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 /// 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 /// function 'putSamples', while the ready processed samples can be read
/// from the other end of the pipeline with function 'receiveSamples'. /// from the other end of the pipeline with function 'receiveSamples'.
/// ///
/// - The SoundTouch processing classes require certain sized 'batches' of /// - The SoundTouch processing classes require certain sized 'batches' of
/// samples in order to process the sound. For this reason the classes buffer /// samples in order to process the sound. For this reason the classes buffer
/// incoming samples until there are enough of samples available for /// incoming samples until there are enough of samples available for
/// processing, then they carry out the processing step and consequently /// processing, then they carry out the processing step and consequently
/// make the processed samples available for outputting. /// make the processed samples available for outputting.
/// ///
/// - For the above reason, the processing routines introduce a certain /// - For the above reason, the processing routines introduce a certain
/// 'latency' between the input and output, so that the samples input to /// 'latency' between the input and output, so that the samples input to
/// SoundTouch may not be immediately available in the output, and neither /// SoundTouch may not be immediately available in the output, and neither
/// the amount of outputtable samples may not immediately be in direct /// the amount of outputtable samples may not immediately be in direct
/// relationship with the amount of previously input samples. /// relationship with the amount of previously input samples.
/// ///
/// - The tempo/pitch/rate control parameters can be altered during processing. /// - The tempo/pitch/rate control parameters can be altered during processing.
/// Please notice though that they aren't currently protected by semaphores, /// Please notice though that they aren't currently protected by semaphores,
/// so in multi-thread application external semaphore protection may be /// so in multi-thread application external semaphore protection may be
/// required. /// required.
/// ///
/// - This class utilizes classes 'TDStretch' for tempo change (without modifying /// - This class utilizes classes 'TDStretch' for tempo change (without modifying
/// pitch) and 'RateTransposer' for changing the playback rate (that is, both /// 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 /// 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 /// 'pitch' (change pitch but maintain tempo) is produced by a combination of
/// combining the two other controls. /// combining the two other controls.
/// ///
/// Author : Copyright (c) Olli Parviainen /// Author : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi /// Author e-mail : oparviai 'at' iki.fi
/// SoundTouch WWW: http://www.surina.net/soundtouch /// SoundTouch WWW: http://www.surina.net/soundtouch
/// ///
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// Last changed : $Date: 2012-12-28 21:32:59 +0200 (Fri, 28 Dec 2012) $ // Last changed : $Date: 2013-06-12 15:24:44 +0000 (Wed, 12 Jun 2013) $
// File revision : $Revision: 4 $ // File revision : $Revision: 4 $
// //
// $Id: SoundTouch.h 163 2012-12-28 19:32:59Z oparviai $ // $Id: SoundTouch.h 171 2013-06-12 15:24:44Z oparviai $
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// License : // License :
// //
// SoundTouch audio processing library // SoundTouch audio processing library
// Copyright (c) Olli Parviainen // Copyright (c) Olli Parviainen
// //
// This library is free software; you can redistribute it and/or // This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public // modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either // License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version. // 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, // This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details. // Lesser General Public License for more details.
// //
// You should have received a copy of the GNU Lesser General Public // You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software // License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#ifndef SoundTouch_H #ifndef SoundTouch_H
#define SoundTouch_H #define SoundTouch_H
#include "FIFOSamplePipe.h" #include "FIFOSamplePipe.h"
#include "STTypes.h" #include "STTypes.h"
namespace soundtouch namespace soundtouch
{ {
/// Soundtouch library version string /// Soundtouch library version string
#define SOUNDTOUCH_VERSION "1.7.1" #define SOUNDTOUCH_VERSION "1.7.2 (dev)"
/// SoundTouch library version id /// SoundTouch library version id
#define SOUNDTOUCH_VERSION_ID (10701) #define SOUNDTOUCH_VERSION_ID (10702)
// //
// Available setting IDs for the 'setSetting' & 'get_setting' functions: // Available setting IDs for the 'setSetting' & 'get_setting' functions:
/// Enable/disable anti-alias filter in pitch transposer (0 = disable) /// Enable/disable anti-alias filter in pitch transposer (0 = disable)
#define SETTING_USE_AA_FILTER 0 #define SETTING_USE_AA_FILTER 0
/// Pitch transposer anti-alias filter length (8 .. 128 taps, default = 32) /// Pitch transposer anti-alias filter length (8 .. 128 taps, default = 32)
#define SETTING_AA_FILTER_LENGTH 1 #define SETTING_AA_FILTER_LENGTH 1
/// Enable/disable quick seeking algorithm in tempo changer routine /// Enable/disable quick seeking algorithm in tempo changer routine
/// (enabling quick seeking lowers CPU utilization but causes a minor sound /// (enabling quick seeking lowers CPU utilization but causes a minor sound
/// quality compromising) /// quality compromising)
#define SETTING_USE_QUICKSEEK 2 #define SETTING_USE_QUICKSEEK 2
/// Time-stretch algorithm single processing sequence length in milliseconds. This determines /// 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. /// to how long sequences the original sound is chopped in the time-stretch algorithm.
/// See "STTypes.h" or README for more information. /// See "STTypes.h" or README for more information.
#define SETTING_SEQUENCE_MS 3 #define SETTING_SEQUENCE_MS 3
/// Time-stretch algorithm seeking window length in milliseconds for algorithm that finds the /// 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 /// 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. /// may look for an optimal joining location when mixing the sound sequences back together.
/// See "STTypes.h" or README for more information. /// See "STTypes.h" or README for more information.
#define SETTING_SEEKWINDOW_MS 4 #define SETTING_SEEKWINDOW_MS 4
/// Time-stretch algorithm overlap length in milliseconds. When the chopped sound sequences /// 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 /// 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. /// how long period the two consecutive sequences are let to overlap each other.
/// See "STTypes.h" or README for more information. /// See "STTypes.h" or README for more information.
#define SETTING_OVERLAP_MS 5 #define SETTING_OVERLAP_MS 5
/// Call "getSetting" with this ID to query nominal average processing sequence /// Call "getSetting" with this ID to query nominal average processing sequence
/// size in samples. This value tells approcimate value how many input samples /// size in samples. This value tells approcimate value how many input samples
/// SoundTouch needs to gather before it does DSP processing run for the sample batch. /// SoundTouch needs to gather before it does DSP processing run for the sample batch.
/// ///
/// Notices: /// Notices:
/// - This is read-only parameter, i.e. setSetting ignores this parameter /// - This is read-only parameter, i.e. setSetting ignores this parameter
/// - Returned value is approximate average value, exact processing batch /// - Returned value is approximate average value, exact processing batch
/// size may wary from time to time /// size may wary from time to time
/// - This parameter value is not constant but may change depending on /// - This parameter value is not constant but may change depending on
/// tempo/pitch/rate/samplerate settings. /// tempo/pitch/rate/samplerate settings.
#define SETTING_NOMINAL_INPUT_SEQUENCE 6 #define SETTING_NOMINAL_INPUT_SEQUENCE 6
/// Call "getSetting" with this ID to query nominal average processing output /// Call "getSetting" with this ID to query nominal average processing output
/// size in samples. This value tells approcimate value how many output samples /// size in samples. This value tells approcimate value how many output samples
/// SoundTouch outputs once it does DSP processing run for a batch of input samples. /// SoundTouch outputs once it does DSP processing run for a batch of input samples.
/// ///
/// Notices: /// Notices:
/// - This is read-only parameter, i.e. setSetting ignores this parameter /// - This is read-only parameter, i.e. setSetting ignores this parameter
/// - Returned value is approximate average value, exact processing batch /// - Returned value is approximate average value, exact processing batch
/// size may wary from time to time /// size may wary from time to time
/// - This parameter value is not constant but may change depending on /// - This parameter value is not constant but may change depending on
/// tempo/pitch/rate/samplerate settings. /// tempo/pitch/rate/samplerate settings.
#define SETTING_NOMINAL_OUTPUT_SEQUENCE 7 #define SETTING_NOMINAL_OUTPUT_SEQUENCE 7
class SoundTouch : public FIFOProcessor class SoundTouch : public FIFOProcessor
{ {
private: private:
/// Rate transposer class instance /// Rate transposer class instance
class RateTransposer *pRateTransposer; class RateTransposer *pRateTransposer;
/// Time-stretch class instance /// Time-stretch class instance
class TDStretch *pTDStretch; class TDStretch *pTDStretch;
/// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters. /// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.
float virtualRate; float virtualRate;
/// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters. /// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.
float virtualTempo; float virtualTempo;
/// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters. /// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.
float virtualPitch; float virtualPitch;
/// Flag: Has sample rate been set? /// Flag: Has sample rate been set?
BOOL bSrateSet; BOOL bSrateSet;
/// Calculates effective rate & tempo valuescfrom 'virtualRate', 'virtualTempo' and /// Calculates effective rate & tempo valuescfrom 'virtualRate', 'virtualTempo' and
/// 'virtualPitch' parameters. /// 'virtualPitch' parameters.
void calcEffectiveRateAndTempo(); void calcEffectiveRateAndTempo();
protected : protected :
/// Number of channels /// Number of channels
uint channels; uint channels;
/// Effective 'rate' value calculated from 'virtualRate', 'virtualTempo' and 'virtualPitch' /// Effective 'rate' value calculated from 'virtualRate', 'virtualTempo' and 'virtualPitch'
float rate; float rate;
/// Effective 'tempo' value calculated from 'virtualRate', 'virtualTempo' and 'virtualPitch' /// Effective 'tempo' value calculated from 'virtualRate', 'virtualTempo' and 'virtualPitch'
float tempo; float tempo;
public: public:
SoundTouch(); SoundTouch();
virtual ~SoundTouch(); virtual ~SoundTouch();
/// Get SoundTouch library version string /// Get SoundTouch library version string
static const char *getVersionString(); static const char *getVersionString();
/// Get SoundTouch library version Id /// Get SoundTouch library version Id
static uint getVersionId(); static uint getVersionId();
/// Sets new rate control value. Normal rate = 1.0, smaller values /// Sets new rate control value. Normal rate = 1.0, smaller values
/// represent slower rate, larger faster rates. /// represent slower rate, larger faster rates.
void setRate(float newRate); void setRate(float newRate);
/// Sets new tempo control value. Normal tempo = 1.0, smaller values /// Sets new tempo control value. Normal tempo = 1.0, smaller values
/// represent slower tempo, larger faster tempo. /// represent slower tempo, larger faster tempo.
void setTempo(float newTempo); void setTempo(float newTempo);
/// Sets new rate control value as a difference in percents compared /// Sets new rate control value as a difference in percents compared
/// to the original rate (-50 .. +100 %) /// to the original rate (-50 .. +100 %)
void setRateChange(float newRate); void setRateChange(float newRate);
/// Sets new tempo control value as a difference in percents compared /// Sets new tempo control value as a difference in percents compared
/// to the original tempo (-50 .. +100 %) /// to the original tempo (-50 .. +100 %)
void setTempoChange(float newTempo); void setTempoChange(float newTempo);
/// Sets new pitch control value. Original pitch = 1.0, smaller values /// Sets new pitch control value. Original pitch = 1.0, smaller values
/// represent lower pitches, larger values higher pitch. /// represent lower pitches, larger values higher pitch.
void setPitch(float newPitch); void setPitch(float newPitch);
/// Sets pitch change in octaves compared to the original pitch /// Sets pitch change in octaves compared to the original pitch
/// (-1.00 .. +1.00) /// (-1.00 .. +1.00)
void setPitchOctaves(float newPitch); void setPitchOctaves(float newPitch);
/// Sets pitch change in semi-tones compared to the original pitch /// Sets pitch change in semi-tones compared to the original pitch
/// (-12 .. +12) /// (-12 .. +12)
void setPitchSemiTones(int newPitch); void setPitchSemiTones(int newPitch);
void setPitchSemiTones(float newPitch); void setPitchSemiTones(float newPitch);
/// Sets the number of channels, 1 = mono, 2 = stereo /// Sets the number of channels, 1 = mono, 2 = stereo
void setChannels(uint numChannels); void setChannels(uint numChannels);
/// Sets sample rate. /// Sets sample rate.
void setSampleRate(uint srate); void setSampleRate(uint srate);
/// Flushes the last samples from the processing pipeline to the output. /// Flushes the last samples from the processing pipeline to the output.
/// Clears also the internal processing buffers. /// Clears also the internal processing buffers.
// //
/// Note: This function is meant for extracting the last samples of a sound /// Note: This function is meant for extracting the last samples of a sound
/// stream. This function may introduce additional blank samples in the end /// 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 /// of the sound stream, and thus it's not recommended to call this function
/// in the middle of a sound stream. /// in the middle of a sound stream.
void flush(); void flush();
/// Adds 'numSamples' pcs of samples from the 'samples' memory position into /// 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 /// the input of the object. Notice that sample rate _has_to_ be set before
/// calling this function, otherwise throws a runtime_error exception. /// calling this function, otherwise throws a runtime_error exception.
virtual void putSamples( virtual void putSamples(
const SAMPLETYPE *samples, ///< Pointer to sample buffer. const SAMPLETYPE *samples, ///< Pointer to sample buffer.
uint numSamples ///< Number of samples in buffer. Notice uint numSamples ///< Number of samples in buffer. Notice
///< that in case of stereo-sound a single sample ///< that in case of stereo-sound a single sample
///< contains data for both channels. ///< contains data for both channels.
); );
/// Clears all the samples in the object's output and internal processing /// Clears all the samples in the object's output and internal processing
/// buffers. /// buffers.
virtual void clear(); virtual void clear();
/// Changes a setting controlling the processing system behaviour. See the /// Changes a setting controlling the processing system behaviour. See the
/// 'SETTING_...' defines for available setting ID's. /// 'SETTING_...' defines for available setting ID's.
/// ///
/// \return 'TRUE' if the setting was succesfully changed /// \return 'TRUE' if the setting was succesfully changed
BOOL setSetting(int settingId, ///< Setting ID number. see SETTING_... defines. BOOL setSetting(int settingId, ///< Setting ID number. see SETTING_... defines.
int value ///< New setting value. int value ///< New setting value.
); );
/// Reads a setting controlling the processing system behaviour. See the /// Reads a setting controlling the processing system behaviour. See the
/// 'SETTING_...' defines for available setting ID's. /// 'SETTING_...' defines for available setting ID's.
/// ///
/// \return the setting value. /// \return the setting value.
int getSetting(int settingId ///< Setting ID number, see SETTING_... defines. int getSetting(int settingId ///< Setting ID number, see SETTING_... defines.
) const; ) const;
/// Returns number of samples currently unprocessed. /// Returns number of samples currently unprocessed.
virtual uint numUnprocessedSamples() const; virtual uint numUnprocessedSamples() const;
/// Other handy functions that are implemented in the ancestor classes (see /// Other handy functions that are implemented in the ancestor classes (see
/// classes 'FIFOProcessor' and 'FIFOSamplePipe') /// classes 'FIFOProcessor' and 'FIFOSamplePipe')
/// ///
/// - receiveSamples() : Use this function to receive 'ready' processed samples from SoundTouch. /// - receiveSamples() : Use this function to receive 'ready' processed samples from SoundTouch.
/// - numSamples() : Get number of 'ready' samples that can be received with /// - numSamples() : Get number of 'ready' samples that can be received with
/// function 'receiveSamples()' /// function 'receiveSamples()'
/// - isEmpty() : Returns nonzero if there aren't any 'ready' samples. /// - isEmpty() : Returns nonzero if there aren't any 'ready' samples.
/// - clear() : Clears all samples from ready/processing buffers. /// - clear() : Clears all samples from ready/processing buffers.
}; };
} }
#endif #endif

File diff suppressed because it is too large Load Diff

View File

@ -1,268 +1,269 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// ///
/// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo /// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo
/// while maintaining the original pitch by using a time domain WSOLA-like method /// while maintaining the original pitch by using a time domain WSOLA-like method
/// with several performance-increasing tweaks. /// with several performance-increasing tweaks.
/// ///
/// Note : MMX/SSE optimized functions reside in separate, platform-specific files /// Note : MMX/SSE optimized functions reside in separate, platform-specific files
/// 'mmx_optimized.cpp' and 'sse_optimized.cpp' /// 'mmx_optimized.cpp' and 'sse_optimized.cpp'
/// ///
/// Author : Copyright (c) Olli Parviainen /// Author : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi /// Author e-mail : oparviai 'at' iki.fi
/// SoundTouch WWW: http://www.surina.net/soundtouch /// SoundTouch WWW: http://www.surina.net/soundtouch
/// ///
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// Last changed : $Date: 2012-04-01 22:49:30 +0300 (Sun, 01 Apr 2012) $ // Last changed : $Date: 2013-06-12 15:24:44 +0000 (Wed, 12 Jun 2013) $
// File revision : $Revision: 4 $ // File revision : $Revision: 4 $
// //
// $Id: TDStretch.h 137 2012-04-01 19:49:30Z oparviai $ // $Id: TDStretch.h 171 2013-06-12 15:24:44Z oparviai $
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// License : // License :
// //
// SoundTouch audio processing library // SoundTouch audio processing library
// Copyright (c) Olli Parviainen // Copyright (c) Olli Parviainen
// //
// This library is free software; you can redistribute it and/or // This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public // modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either // License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version. // 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, // This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details. // Lesser General Public License for more details.
// //
// You should have received a copy of the GNU Lesser General Public // You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software // License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#ifndef TDStretch_H #ifndef TDStretch_H
#define TDStretch_H #define TDStretch_H
#include <stddef.h> #include <stddef.h>
#include "STTypes.h" #include "STTypes.h"
#include "RateTransposer.h" #include "RateTransposer.h"
#include "FIFOSamplePipe.h" #include "FIFOSamplePipe.h"
namespace soundtouch namespace soundtouch
{ {
/// Default values for sound processing parameters: /// Default values for sound processing parameters:
/// Notice that the default parameters are tuned for contemporary popular music /// Notice that the default parameters are tuned for contemporary popular music
/// processing. For speech processing applications these parameters suit better: /// processing. For speech processing applications these parameters suit better:
/// #define DEFAULT_SEQUENCE_MS 40 /// #define DEFAULT_SEQUENCE_MS 40
/// #define DEFAULT_SEEKWINDOW_MS 15 /// #define DEFAULT_SEEKWINDOW_MS 15
/// #define DEFAULT_OVERLAP_MS 8 /// #define DEFAULT_OVERLAP_MS 8
/// ///
/// Default length of a single processing sequence, in milliseconds. This determines to how /// 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. /// 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 /// 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 /// a bigger value sounds better when slowing down tempo, but worse when increasing tempo
/// and vice versa. /// and vice versa.
/// ///
/// Increasing this value reduces computational burden & vice versa. /// Increasing this value reduces computational burden & vice versa.
//#define DEFAULT_SEQUENCE_MS 40 //#define DEFAULT_SEQUENCE_MS 40
#define DEFAULT_SEQUENCE_MS USE_AUTO_SEQUENCE_LEN #define DEFAULT_SEQUENCE_MS USE_AUTO_SEQUENCE_LEN
/// Giving this value for the sequence length sets automatic parameter value /// Giving this value for the sequence length sets automatic parameter value
/// according to tempo setting (recommended) /// according to tempo setting (recommended)
#define USE_AUTO_SEQUENCE_LEN 0 #define USE_AUTO_SEQUENCE_LEN 0
/// Seeking window default length in milliseconds for algorithm that finds the best possible /// 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 /// overlapping location. This determines from how wide window the algorithm may look for an
/// optimal joining location when mixing the sound sequences back together. /// 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 /// 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 /// 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. /// 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 /// If there's a disturbing artifact that sounds as if a constant frequency was drifting
/// around, try reducing this setting. /// around, try reducing this setting.
/// ///
/// Increasing this value increases computational burden & vice versa. /// Increasing this value increases computational burden & vice versa.
//#define DEFAULT_SEEKWINDOW_MS 15 //#define DEFAULT_SEEKWINDOW_MS 15
#define DEFAULT_SEEKWINDOW_MS USE_AUTO_SEEKWINDOW_LEN #define DEFAULT_SEEKWINDOW_MS USE_AUTO_SEEKWINDOW_LEN
/// Giving this value for the seek window length sets automatic parameter value /// Giving this value for the seek window length sets automatic parameter value
/// according to tempo setting (recommended) /// according to tempo setting (recommended)
#define USE_AUTO_SEEKWINDOW_LEN 0 #define USE_AUTO_SEEKWINDOW_LEN 0
/// Overlap length in milliseconds. When the chopped sound sequences are mixed back together, /// 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 /// to form a continuous sound stream, this parameter defines over how long period the two
/// consecutive sequences are let to overlap each other. /// consecutive sequences are let to overlap each other.
/// ///
/// This shouldn't be that critical parameter. If you reduce the DEFAULT_SEQUENCE_MS setting /// 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. /// by a large amount, you might wish to try a smaller value on this.
/// ///
/// Increasing this value increases computational burden & vice versa. /// Increasing this value increases computational burden & vice versa.
#define DEFAULT_OVERLAP_MS 8 #define DEFAULT_OVERLAP_MS 8
/// Class that does the time-stretch (tempo change) effect for the processed /// Class that does the time-stretch (tempo change) effect for the processed
/// sound. /// sound.
class TDStretch : public FIFOProcessor class TDStretch : public FIFOProcessor
{ {
protected: protected:
int channels; int channels;
int sampleReq; int sampleReq;
float tempo; float tempo;
SAMPLETYPE *pMidBuffer; SAMPLETYPE *pMidBuffer;
SAMPLETYPE *pMidBufferUnaligned; SAMPLETYPE *pMidBufferUnaligned;
int overlapLength; int overlapLength;
int seekLength; int seekLength;
int seekWindowLength; int seekWindowLength;
int overlapDividerBits; int overlapDividerBits;
int slopingDivider; int slopingDivider;
float nominalSkip; float nominalSkip;
float skipFract; float skipFract;
FIFOSampleBuffer outputBuffer; FIFOSampleBuffer outputBuffer;
FIFOSampleBuffer inputBuffer; FIFOSampleBuffer inputBuffer;
BOOL bQuickSeek; BOOL bQuickSeek;
int sampleRate; int sampleRate;
int sequenceMs; int sequenceMs;
int seekWindowMs; int seekWindowMs;
int overlapMs; int overlapMs;
BOOL bAutoSeqSetting; BOOL bAutoSeqSetting;
BOOL bAutoSeekSetting; BOOL bAutoSeekSetting;
void acceptNewOverlapLength(int newOverlapLength); void acceptNewOverlapLength(int newOverlapLength);
virtual void clearCrossCorrState(); virtual void clearCrossCorrState();
void calculateOverlapLength(int overlapMs); void calculateOverlapLength(int overlapMs);
virtual double calcCrossCorr(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare) const; virtual double calcCrossCorr(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare) const;
virtual int seekBestOverlapPositionFull(const SAMPLETYPE *refPos); virtual int seekBestOverlapPositionFull(const SAMPLETYPE *refPos);
virtual int seekBestOverlapPositionQuick(const SAMPLETYPE *refPos); virtual int seekBestOverlapPositionQuick(const SAMPLETYPE *refPos);
int seekBestOverlapPosition(const SAMPLETYPE *refPos); int seekBestOverlapPosition(const SAMPLETYPE *refPos);
virtual void overlapStereo(SAMPLETYPE *output, const SAMPLETYPE *input) const; virtual void overlapStereo(SAMPLETYPE *output, const SAMPLETYPE *input) const;
virtual void overlapMono(SAMPLETYPE *output, const SAMPLETYPE *input) const; virtual void overlapMono(SAMPLETYPE *output, const SAMPLETYPE *input) const;
virtual void overlapMulti(SAMPLETYPE *output, const SAMPLETYPE *input) const;
void clearMidBuffer();
void overlap(SAMPLETYPE *output, const SAMPLETYPE *input, uint ovlPos) const; void clearMidBuffer();
void overlap(SAMPLETYPE *output, const SAMPLETYPE *input, uint ovlPos) const;
void calcSeqParameters();
void calcSeqParameters();
/// Changes the tempo of the given sound samples.
/// Returns amount of samples returned in the "output" buffer. /// Changes the tempo of the given sound samples.
/// The maximum amount of samples that can be returned at a time is set by /// Returns amount of samples returned in the "output" buffer.
/// the 'set_returnBuffer_size' function. /// The maximum amount of samples that can be returned at a time is set by
void processSamples(); /// the 'set_returnBuffer_size' function.
void processSamples();
public:
TDStretch(); public:
virtual ~TDStretch(); TDStretch();
virtual ~TDStretch();
/// Operator 'new' is overloaded so that it automatically creates a suitable instance
/// depending on if we've a MMX/SSE/etc-capable CPU available or not. /// Operator 'new' is overloaded so that it automatically creates a suitable instance
static void *operator new(size_t s); /// depending on if we've a MMX/SSE/etc-capable CPU available or not.
static void *operator new(size_t s);
/// Use this function instead of "new" operator to create a new instance of this class.
/// This function automatically chooses a correct feature set depending on if the CPU /// Use this function instead of "new" operator to create a new instance of this class.
/// supports MMX/SSE/etc extensions. /// This function automatically chooses a correct feature set depending on if the CPU
static TDStretch *newInstance(); /// supports MMX/SSE/etc extensions.
static TDStretch *newInstance();
/// Returns the output buffer object
FIFOSamplePipe *getOutput() { return &outputBuffer; }; /// Returns the output buffer object
FIFOSamplePipe *getOutput() { return &outputBuffer; };
/// Returns the input buffer object
FIFOSamplePipe *getInput() { return &inputBuffer; }; /// Returns the input buffer object
FIFOSamplePipe *getInput() { return &inputBuffer; };
/// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower
/// tempo, larger faster tempo. /// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower
void setTempo(float newTempo); /// tempo, larger faster tempo.
void setTempo(float newTempo);
/// Returns nonzero if there aren't any samples available for outputting.
virtual void clear(); /// Returns nonzero if there aren't any samples available for outputting.
virtual void clear();
/// Clears the input buffer
void clearInput(); /// Clears the input buffer
void clearInput();
/// Sets the number of channels, 1 = mono, 2 = stereo
void setChannels(int numChannels); /// Sets the number of channels, 1 = mono, 2 = stereo
void setChannels(int numChannels);
/// Enables/disables the quick position seeking algorithm. Zero to disable,
/// nonzero to enable /// Enables/disables the quick position seeking algorithm. Zero to disable,
void enableQuickSeek(BOOL enable); /// nonzero to enable
void enableQuickSeek(BOOL enable);
/// Returns nonzero if the quick seeking algorithm is enabled.
BOOL isQuickSeekEnabled() const; /// 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. /// 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 /// 'sampleRate' = sample rate of the sound
/// 'seekwindowMS' = seeking window length for scanning the best overlapping /// 'sequenceMS' = one processing sequence length in milliseconds
/// position /// 'seekwindowMS' = seeking window length for scanning the best overlapping
/// 'overlapMS' = overlapping length /// position
void setParameters(int sampleRate, ///< Samplerate of sound being processed (Hz) /// 'overlapMS' = overlapping length
int sequenceMS = -1, ///< Single processing sequence length (ms) void setParameters(int sampleRate, ///< Samplerate of sound being processed (Hz)
int seekwindowMS = -1, ///< Offset seeking window length (ms) int sequenceMS = -1, ///< Single processing sequence length (ms)
int overlapMS = -1 ///< Sequence overlapping length (ms) int seekwindowMS = -1, ///< Offset seeking window length (ms)
); int overlapMS = -1 ///< Sequence overlapping length (ms)
);
/// Get routine control parameters, see setParameters() function.
/// Any of the parameters to this function can be NULL, in such case corresponding parameter /// Get routine control parameters, see setParameters() function.
/// value isn't returned. /// Any of the parameters to this function can be NULL, in such case corresponding parameter
void getParameters(int *pSampleRate, int *pSequenceMs, int *pSeekWindowMs, int *pOverlapMs) const; /// value isn't returned.
void getParameters(int *pSampleRate, int *pSequenceMs, int *pSeekWindowMs, int *pOverlapMs) const;
/// Adds 'numsamples' pcs of samples from the 'samples' memory position into
/// the input of the object. /// Adds 'numsamples' pcs of samples from the 'samples' memory position into
virtual void putSamples( /// the input of the object.
const SAMPLETYPE *samples, ///< Input sample data virtual void putSamples(
uint numSamples ///< Number of samples in 'samples' so that one sample const SAMPLETYPE *samples, ///< Input sample data
///< contains both channels if stereo uint numSamples ///< Number of samples in 'samples' so that one sample
); ///< contains both channels if stereo
);
/// return nominal input sample requirement for triggering a processing batch
int getInputSampleReq() const /// return nominal input sample requirement for triggering a processing batch
{ int getInputSampleReq() const
return (int)(nominalSkip + 0.5); {
} return (int)(nominalSkip + 0.5);
}
/// return nominal output sample amount when running a processing batch
int getOutputBatchSize() const /// return nominal output sample amount when running a processing batch
{ int getOutputBatchSize() const
return seekWindowLength - overlapLength; {
} return seekWindowLength - overlapLength;
}; }
};
// Implementation-specific class declarations:
// Implementation-specific class declarations:
#ifdef SOUNDTOUCH_ALLOW_MMX
/// Class that implements MMX optimized routines for 16bit integer samples type. #ifdef SOUNDTOUCH_ALLOW_MMX
class TDStretchMMX : public TDStretch /// Class that implements MMX optimized routines for 16bit integer samples type.
{ class TDStretchMMX : public TDStretch
protected: {
double calcCrossCorr(const short *mixingPos, const short *compare) const; protected:
virtual void overlapStereo(short *output, const short *input) const; double calcCrossCorr(const short *mixingPos, const short *compare) const;
virtual void clearCrossCorrState(); virtual void overlapStereo(short *output, const short *input) const;
}; virtual void clearCrossCorrState();
#endif /// SOUNDTOUCH_ALLOW_MMX };
#endif /// SOUNDTOUCH_ALLOW_MMX
#ifdef SOUNDTOUCH_ALLOW_SSE
/// Class that implements SSE optimized routines for floating point samples type. #ifdef SOUNDTOUCH_ALLOW_SSE
class TDStretchSSE : public TDStretch /// Class that implements SSE optimized routines for floating point samples type.
{ class TDStretchSSE : public TDStretch
protected: {
double calcCrossCorr(const float *mixingPos, const float *compare) const; protected:
}; double calcCrossCorr(const float *mixingPos, const float *compare) const;
};
#endif /// SOUNDTOUCH_ALLOW_SSE
#endif /// SOUNDTOUCH_ALLOW_SSE
}
#endif /// TDStretch_H }
#endif /// TDStretch_H

View File

@ -1,62 +1,62 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// ///
/// A header file for detecting the Intel MMX instructions set extension. /// 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 /// 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 /// routine implementations for x86 Windows, x86 gnu version and non-x86
/// platforms, respectively. /// platforms, respectively.
/// ///
/// Author : Copyright (c) Olli Parviainen /// Author : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi /// Author e-mail : oparviai 'at' iki.fi
/// SoundTouch WWW: http://www.surina.net/soundtouch /// SoundTouch WWW: http://www.surina.net/soundtouch
/// ///
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// Last changed : $Date: 2008-02-10 18:26:55 +0200 (Sun, 10 Feb 2008) $ // Last changed : $Date: 2008-02-10 16:26:55 +0000 (Sun, 10 Feb 2008) $
// File revision : $Revision: 4 $ // File revision : $Revision: 4 $
// //
// $Id: cpu_detect.h 11 2008-02-10 16:26:55Z oparviai $ // $Id: cpu_detect.h 11 2008-02-10 16:26:55Z oparviai $
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// License : // License :
// //
// SoundTouch audio processing library // SoundTouch audio processing library
// Copyright (c) Olli Parviainen // Copyright (c) Olli Parviainen
// //
// This library is free software; you can redistribute it and/or // This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public // modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either // License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version. // 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, // This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details. // Lesser General Public License for more details.
// //
// You should have received a copy of the GNU Lesser General Public // You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software // License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#ifndef _CPU_DETECT_H_ #ifndef _CPU_DETECT_H_
#define _CPU_DETECT_H_ #define _CPU_DETECT_H_
#include "STTypes.h" #include "STTypes.h"
#define SUPPORT_MMX 0x0001 #define SUPPORT_MMX 0x0001
#define SUPPORT_3DNOW 0x0002 #define SUPPORT_3DNOW 0x0002
#define SUPPORT_ALTIVEC 0x0004 #define SUPPORT_ALTIVEC 0x0004
#define SUPPORT_SSE 0x0008 #define SUPPORT_SSE 0x0008
#define SUPPORT_SSE2 0x0010 #define SUPPORT_SSE2 0x0010
/// Checks which instruction set extensions are supported by the CPU. /// Checks which instruction set extensions are supported by the CPU.
/// ///
/// \return A bitmask of supported extensions, see SUPPORT_... defines. /// \return A bitmask of supported extensions, see SUPPORT_... defines.
uint detectCPUextensions(void); uint detectCPUextensions(void);
/// Disables given set of instruction extensions. See SUPPORT_... defines. /// Disables given set of instruction extensions. See SUPPORT_... defines.
void disableExtensions(uint wDisableMask); void disableExtensions(uint wDisableMask);
#endif // _CPU_DETECT_H_ #endif // _CPU_DETECT_H_

View File

@ -1,137 +1,137 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// ///
/// Generic version of the x86 CPU extension detection routine. /// Generic version of the x86 CPU extension detection routine.
/// ///
/// This file is for GNU & other non-Windows compilers, see 'cpu_detect_x86_win.cpp' /// This file is for GNU & other non-Windows compilers, see 'cpu_detect_x86_win.cpp'
/// for the Microsoft compiler version. /// for the Microsoft compiler version.
/// ///
/// Author : Copyright (c) Olli Parviainen /// Author : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi /// Author e-mail : oparviai 'at' iki.fi
/// SoundTouch WWW: http://www.surina.net/soundtouch /// SoundTouch WWW: http://www.surina.net/soundtouch
/// ///
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// Last changed : $Date: 2012-11-08 20:44:37 +0200 (Thu, 08 Nov 2012) $ // Last changed : $Date: 2012-11-08 18:44:37 +0000 (Thu, 08 Nov 2012) $
// File revision : $Revision: 4 $ // File revision : $Revision: 4 $
// //
// $Id: cpu_detect_x86.cpp 159 2012-11-08 18:44:37Z oparviai $ // $Id: cpu_detect_x86.cpp 159 2012-11-08 18:44:37Z oparviai $
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// License : // License :
// //
// SoundTouch audio processing library // SoundTouch audio processing library
// Copyright (c) Olli Parviainen // Copyright (c) Olli Parviainen
// //
// This library is free software; you can redistribute it and/or // This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public // modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either // License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version. // 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, // This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details. // Lesser General Public License for more details.
// //
// You should have received a copy of the GNU Lesser General Public // You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software // License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include "cpu_detect.h" #include "cpu_detect.h"
#include "STTypes.h" #include "STTypes.h"
#if defined(SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS) #if defined(SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS)
#if defined(__GNUC__) && defined(__i386__) #if defined(__GNUC__) && defined(__i386__)
// gcc // gcc
#include "cpuid.h" #include "cpuid.h"
#elif defined(_M_IX86) #elif defined(_M_IX86)
// windows non-gcc // windows non-gcc
#include <intrin.h> #include <intrin.h>
#endif #define bit_MMX (1 << 23)
#define bit_SSE (1 << 25)
#define bit_MMX (1 << 23) #define bit_SSE2 (1 << 26)
#define bit_SSE (1 << 25) #endif
#define bit_SSE2 (1 << 26)
#endif #endif
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
// //
// processor instructions extension detection routines // processor instructions extension detection routines
// //
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
// Flag variable indicating whick ISA extensions are disabled (for debugging) // Flag variable indicating whick ISA extensions are disabled (for debugging)
static uint _dwDisabledISA = 0x00; // 0xffffffff; //<- use this to disable all extensions static uint _dwDisabledISA = 0x00; // 0xffffffff; //<- use this to disable all extensions
// Disables given set of instruction extensions. See SUPPORT_... defines. // Disables given set of instruction extensions. See SUPPORT_... defines.
void disableExtensions(uint dwDisableMask) void disableExtensions(uint dwDisableMask)
{ {
_dwDisabledISA = dwDisableMask; _dwDisabledISA = dwDisableMask;
} }
/// Checks which instruction set extensions are supported by the CPU. /// Checks which instruction set extensions are supported by the CPU.
uint detectCPUextensions(void) uint detectCPUextensions(void)
{ {
/// If building for a 64bit system (no Itanium) and the user wants optimizations. /// If building for a 64bit system (no Itanium) and the user wants optimizations.
/// Return the OR of SUPPORT_{MMX,SSE,SSE2}. 11001 or 0x19. /// Return the OR of SUPPORT_{MMX,SSE,SSE2}. 11001 or 0x19.
/// Keep the _dwDisabledISA test (2 more operations, could be eliminated). /// Keep the _dwDisabledISA test (2 more operations, could be eliminated).
#if ((defined(__GNUC__) && defined(__x86_64__)) \ #if ((defined(__GNUC__) && defined(__x86_64__)) \
|| defined(_M_X64)) \ || defined(_M_X64)) \
&& defined(SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS) && defined(SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS)
return 0x19 & ~_dwDisabledISA; return 0x19 & ~_dwDisabledISA;
/// If building for a 32bit system and the user wants optimizations. /// If building for a 32bit system and the user wants optimizations.
/// Keep the _dwDisabledISA test (2 more operations, could be eliminated). /// Keep the _dwDisabledISA test (2 more operations, could be eliminated).
#elif ((defined(__GNUC__) && defined(__i386__)) \ #elif ((defined(__GNUC__) && defined(__i386__)) \
|| defined(_M_IX86)) \ || defined(_M_IX86)) \
&& defined(SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS) && defined(SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS)
if (_dwDisabledISA == 0xffffffff) return 0; if (_dwDisabledISA == 0xffffffff) return 0;
uint res = 0; uint res = 0;
#if defined(__GNUC__) #if defined(__GNUC__)
// GCC version of cpuid. Requires GCC 4.3.0 or later for __cpuid intrinsic support. // GCC version of cpuid. Requires GCC 4.3.0 or later for __cpuid intrinsic support.
uint eax, ebx, ecx, edx; // unsigned int is the standard type. uint is defined by the compiler and not guaranteed to be portable. uint eax, ebx, ecx, edx; // unsigned int is the standard type. uint is defined by the compiler and not guaranteed to be portable.
// Check if no cpuid support. // Check if no cpuid support.
if (!__get_cpuid (1, &eax, &ebx, &ecx, &edx)) return 0; // always disable extensions. if (!__get_cpuid (1, &eax, &ebx, &ecx, &edx)) return 0; // always disable extensions.
if (edx & bit_MMX) res = res | SUPPORT_MMX; if (edx & bit_MMX) res = res | SUPPORT_MMX;
if (edx & bit_SSE) res = res | SUPPORT_SSE; if (edx & bit_SSE) res = res | SUPPORT_SSE;
if (edx & bit_SSE2) res = res | SUPPORT_SSE2; if (edx & bit_SSE2) res = res | SUPPORT_SSE2;
#else #else
// Window / VS version of cpuid. Notice that Visual Studio 2005 or later required // Window / VS version of cpuid. Notice that Visual Studio 2005 or later required
// for __cpuid intrinsic support. // for __cpuid intrinsic support.
int reg[4] = {-1}; int reg[4] = {-1};
// Check if no cpuid support. // Check if no cpuid support.
__cpuid(reg,0); __cpuid(reg,0);
if ((unsigned int)reg[0] == 0) return 0; // always disable extensions. if ((unsigned int)reg[0] == 0) return 0; // always disable extensions.
__cpuid(reg,1); __cpuid(reg,1);
if ((unsigned int)reg[3] & bit_MMX) res = res | SUPPORT_MMX; if ((unsigned int)reg[3] & bit_MMX) res = res | SUPPORT_MMX;
if ((unsigned int)reg[3] & bit_SSE) res = res | SUPPORT_SSE; if ((unsigned int)reg[3] & bit_SSE) res = res | SUPPORT_SSE;
if ((unsigned int)reg[3] & bit_SSE2) res = res | SUPPORT_SSE2; if ((unsigned int)reg[3] & bit_SSE2) res = res | SUPPORT_SSE2;
#endif #endif
return res & ~_dwDisabledISA; return res & ~_dwDisabledISA;
#else #else
/// One of these is true: /// One of these is true:
/// 1) We don't want optimizations. /// 1) We don't want optimizations.
/// 2) Using an unsupported compiler. /// 2) Using an unsupported compiler.
/// 3) Running on a non-x86 platform. /// 3) Running on a non-x86 platform.
return 0; return 0;
#endif #endif
} }

View File

@ -1,317 +1,317 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// ///
/// MMX optimized routines. All MMX optimized functions have been gathered into /// MMX optimized routines. All MMX optimized functions have been gathered into
/// this single source code file, regardless to their class or original source /// 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 /// code file, in order to ease porting the library to other compiler and
/// processor platforms. /// processor platforms.
/// ///
/// The MMX-optimizations are programmed using MMX compiler intrinsics that /// The MMX-optimizations are programmed using MMX compiler intrinsics that
/// are supported both by Microsoft Visual C++ and GCC compilers, so this file /// are supported both by Microsoft Visual C++ and GCC compilers, so this file
/// should compile with both toolsets. /// should compile with both toolsets.
/// ///
/// NOTICE: If using Visual Studio 6.0, you'll need to install the "Visual C++ /// NOTICE: If using Visual Studio 6.0, you'll need to install the "Visual C++
/// 6.0 processor pack" update to support compiler intrinsic syntax. The update /// 6.0 processor pack" update to support compiler intrinsic syntax. The update
/// is available for download at Microsoft Developers Network, see here: /// is available for download at Microsoft Developers Network, see here:
/// http://msdn.microsoft.com/en-us/vstudio/aa718349.aspx /// http://msdn.microsoft.com/en-us/vstudio/aa718349.aspx
/// ///
/// Author : Copyright (c) Olli Parviainen /// Author : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi /// Author e-mail : oparviai 'at' iki.fi
/// SoundTouch WWW: http://www.surina.net/soundtouch /// SoundTouch WWW: http://www.surina.net/soundtouch
/// ///
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// Last changed : $Date: 2012-11-08 20:53:01 +0200 (Thu, 08 Nov 2012) $ // Last changed : $Date: 2012-11-08 18:53:01 +0000 (Thu, 08 Nov 2012) $
// File revision : $Revision: 4 $ // File revision : $Revision: 4 $
// //
// $Id: mmx_optimized.cpp 160 2012-11-08 18:53:01Z oparviai $ // $Id: mmx_optimized.cpp 160 2012-11-08 18:53:01Z oparviai $
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// License : // License :
// //
// SoundTouch audio processing library // SoundTouch audio processing library
// Copyright (c) Olli Parviainen // Copyright (c) Olli Parviainen
// //
// This library is free software; you can redistribute it and/or // This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public // modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either // License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version. // 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, // This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details. // Lesser General Public License for more details.
// //
// You should have received a copy of the GNU Lesser General Public // You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software // License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include "STTypes.h" #include "STTypes.h"
#ifdef SOUNDTOUCH_ALLOW_MMX #ifdef SOUNDTOUCH_ALLOW_MMX
// MMX routines available only with integer sample type // MMX routines available only with integer sample type
using namespace soundtouch; using namespace soundtouch;
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
// //
// implementation of MMX optimized functions of class 'TDStretchMMX' // implementation of MMX optimized functions of class 'TDStretchMMX'
// //
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
#include "TDStretch.h" #include "TDStretch.h"
#include <mmintrin.h> #include <mmintrin.h>
#include <limits.h> #include <limits.h>
#include <math.h> #include <math.h>
// Calculates cross correlation of two buffers // Calculates cross correlation of two buffers
double TDStretchMMX::calcCrossCorr(const short *pV1, const short *pV2) const double TDStretchMMX::calcCrossCorr(const short *pV1, const short *pV2) const
{ {
const __m64 *pVec1, *pVec2; const __m64 *pVec1, *pVec2;
__m64 shifter; __m64 shifter;
__m64 accu, normaccu; __m64 accu, normaccu;
long corr, norm; long corr, norm;
int i; int i;
pVec1 = (__m64*)pV1; pVec1 = (__m64*)pV1;
pVec2 = (__m64*)pV2; pVec2 = (__m64*)pV2;
shifter = _m_from_int(overlapDividerBits); shifter = _m_from_int(overlapDividerBits);
normaccu = accu = _mm_setzero_si64(); normaccu = accu = _mm_setzero_si64();
// Process 4 parallel sets of 2 * stereo samples or 4 * mono samples // Process 4 parallel sets of 2 * stereo samples or 4 * mono samples
// during each round for improved CPU-level parallellization. // during each round for improved CPU-level parallellization.
for (i = 0; i < channels * overlapLength / 16; i ++) for (i = 0; i < channels * overlapLength / 16; i ++)
{ {
__m64 temp, temp2; __m64 temp, temp2;
// dictionary of instructions: // dictionary of instructions:
// _m_pmaddwd : 4*16bit multiply-add, resulting two 32bits = [a0*b0+a1*b1 ; a2*b2+a3*b3] // _m_pmaddwd : 4*16bit multiply-add, resulting two 32bits = [a0*b0+a1*b1 ; a2*b2+a3*b3]
// _mm_add_pi32 : 2*32bit add // _mm_add_pi32 : 2*32bit add
// _m_psrad : 32bit right-shift // _m_psrad : 32bit right-shift
temp = _mm_add_pi32(_mm_madd_pi16(pVec1[0], pVec2[0]), temp = _mm_add_pi32(_mm_madd_pi16(pVec1[0], pVec2[0]),
_mm_madd_pi16(pVec1[1], pVec2[1])); _mm_madd_pi16(pVec1[1], pVec2[1]));
temp2 = _mm_add_pi32(_mm_madd_pi16(pVec1[0], pVec1[0]), temp2 = _mm_add_pi32(_mm_madd_pi16(pVec1[0], pVec1[0]),
_mm_madd_pi16(pVec1[1], pVec1[1])); _mm_madd_pi16(pVec1[1], pVec1[1]));
accu = _mm_add_pi32(accu, _mm_sra_pi32(temp, shifter)); accu = _mm_add_pi32(accu, _mm_sra_pi32(temp, shifter));
normaccu = _mm_add_pi32(normaccu, _mm_sra_pi32(temp2, shifter)); normaccu = _mm_add_pi32(normaccu, _mm_sra_pi32(temp2, shifter));
temp = _mm_add_pi32(_mm_madd_pi16(pVec1[2], pVec2[2]), temp = _mm_add_pi32(_mm_madd_pi16(pVec1[2], pVec2[2]),
_mm_madd_pi16(pVec1[3], pVec2[3])); _mm_madd_pi16(pVec1[3], pVec2[3]));
temp2 = _mm_add_pi32(_mm_madd_pi16(pVec1[2], pVec1[2]), temp2 = _mm_add_pi32(_mm_madd_pi16(pVec1[2], pVec1[2]),
_mm_madd_pi16(pVec1[3], pVec1[3])); _mm_madd_pi16(pVec1[3], pVec1[3]));
accu = _mm_add_pi32(accu, _mm_sra_pi32(temp, shifter)); accu = _mm_add_pi32(accu, _mm_sra_pi32(temp, shifter));
normaccu = _mm_add_pi32(normaccu, _mm_sra_pi32(temp2, shifter)); normaccu = _mm_add_pi32(normaccu, _mm_sra_pi32(temp2, shifter));
pVec1 += 4; pVec1 += 4;
pVec2 += 4; pVec2 += 4;
} }
// copy hi-dword of mm0 to lo-dword of mm1, then sum mmo+mm1 // copy hi-dword of mm0 to lo-dword of mm1, then sum mmo+mm1
// and finally store the result into the variable "corr" // and finally store the result into the variable "corr"
accu = _mm_add_pi32(accu, _mm_srli_si64(accu, 32)); accu = _mm_add_pi32(accu, _mm_srli_si64(accu, 32));
corr = _m_to_int(accu); corr = _m_to_int(accu);
normaccu = _mm_add_pi32(normaccu, _mm_srli_si64(normaccu, 32)); normaccu = _mm_add_pi32(normaccu, _mm_srli_si64(normaccu, 32));
norm = _m_to_int(normaccu); norm = _m_to_int(normaccu);
// Clear MMS state // Clear MMS state
_m_empty(); _m_empty();
// Normalize result by dividing by sqrt(norm) - this step is easiest // Normalize result by dividing by sqrt(norm) - this step is easiest
// done using floating point operation // done using floating point operation
if (norm == 0) norm = 1; // to avoid div by zero if (norm == 0) norm = 1; // to avoid div by zero
return (double)corr / sqrt((double)norm); return (double)corr / sqrt((double)norm);
// Note: Warning about the missing EMMS instruction is harmless // Note: Warning about the missing EMMS instruction is harmless
// as it'll be called elsewhere. // as it'll be called elsewhere.
} }
void TDStretchMMX::clearCrossCorrState() void TDStretchMMX::clearCrossCorrState()
{ {
// Clear MMS state // Clear MMS state
_m_empty(); _m_empty();
//_asm EMMS; //_asm EMMS;
} }
// MMX-optimized version of the function overlapStereo // MMX-optimized version of the function overlapStereo
void TDStretchMMX::overlapStereo(short *output, const short *input) const void TDStretchMMX::overlapStereo(short *output, const short *input) const
{ {
const __m64 *pVinput, *pVMidBuf; const __m64 *pVinput, *pVMidBuf;
__m64 *pVdest; __m64 *pVdest;
__m64 mix1, mix2, adder, shifter; __m64 mix1, mix2, adder, shifter;
int i; int i;
pVinput = (const __m64*)input; pVinput = (const __m64*)input;
pVMidBuf = (const __m64*)pMidBuffer; pVMidBuf = (const __m64*)pMidBuffer;
pVdest = (__m64*)output; pVdest = (__m64*)output;
// mix1 = mixer values for 1st stereo sample // mix1 = mixer values for 1st stereo sample
// mix1 = mixer values for 2nd stereo sample // mix1 = mixer values for 2nd stereo sample
// adder = adder for updating mixer values after each round // adder = adder for updating mixer values after each round
mix1 = _mm_set_pi16(0, overlapLength, 0, overlapLength); mix1 = _mm_set_pi16(0, overlapLength, 0, overlapLength);
adder = _mm_set_pi16(1, -1, 1, -1); adder = _mm_set_pi16(1, -1, 1, -1);
mix2 = _mm_add_pi16(mix1, adder); mix2 = _mm_add_pi16(mix1, adder);
adder = _mm_add_pi16(adder, adder); adder = _mm_add_pi16(adder, adder);
// Overlaplength-division by shifter. "+1" is to account for "-1" deduced in // Overlaplength-division by shifter. "+1" is to account for "-1" deduced in
// overlapDividerBits calculation earlier. // overlapDividerBits calculation earlier.
shifter = _m_from_int(overlapDividerBits + 1); shifter = _m_from_int(overlapDividerBits + 1);
for (i = 0; i < overlapLength / 4; i ++) for (i = 0; i < overlapLength / 4; i ++)
{ {
__m64 temp1, temp2; __m64 temp1, temp2;
// load & shuffle data so that input & mixbuffer data samples are paired // load & shuffle data so that input & mixbuffer data samples are paired
temp1 = _mm_unpacklo_pi16(pVMidBuf[0], pVinput[0]); // = i0l m0l i0r m0r temp1 = _mm_unpacklo_pi16(pVMidBuf[0], pVinput[0]); // = i0l m0l i0r m0r
temp2 = _mm_unpackhi_pi16(pVMidBuf[0], pVinput[0]); // = i1l m1l i1r m1r temp2 = _mm_unpackhi_pi16(pVMidBuf[0], pVinput[0]); // = i1l m1l i1r m1r
// temp = (temp .* mix) >> shifter // temp = (temp .* mix) >> shifter
temp1 = _mm_sra_pi32(_mm_madd_pi16(temp1, mix1), shifter); temp1 = _mm_sra_pi32(_mm_madd_pi16(temp1, mix1), shifter);
temp2 = _mm_sra_pi32(_mm_madd_pi16(temp2, mix2), shifter); temp2 = _mm_sra_pi32(_mm_madd_pi16(temp2, mix2), shifter);
pVdest[0] = _mm_packs_pi32(temp1, temp2); // pack 2*2*32bit => 4*16bit pVdest[0] = _mm_packs_pi32(temp1, temp2); // pack 2*2*32bit => 4*16bit
// update mix += adder // update mix += adder
mix1 = _mm_add_pi16(mix1, adder); mix1 = _mm_add_pi16(mix1, adder);
mix2 = _mm_add_pi16(mix2, adder); mix2 = _mm_add_pi16(mix2, adder);
// --- second round begins here --- // --- second round begins here ---
// load & shuffle data so that input & mixbuffer data samples are paired // load & shuffle data so that input & mixbuffer data samples are paired
temp1 = _mm_unpacklo_pi16(pVMidBuf[1], pVinput[1]); // = i2l m2l i2r m2r temp1 = _mm_unpacklo_pi16(pVMidBuf[1], pVinput[1]); // = i2l m2l i2r m2r
temp2 = _mm_unpackhi_pi16(pVMidBuf[1], pVinput[1]); // = i3l m3l i3r m3r temp2 = _mm_unpackhi_pi16(pVMidBuf[1], pVinput[1]); // = i3l m3l i3r m3r
// temp = (temp .* mix) >> shifter // temp = (temp .* mix) >> shifter
temp1 = _mm_sra_pi32(_mm_madd_pi16(temp1, mix1), shifter); temp1 = _mm_sra_pi32(_mm_madd_pi16(temp1, mix1), shifter);
temp2 = _mm_sra_pi32(_mm_madd_pi16(temp2, mix2), shifter); temp2 = _mm_sra_pi32(_mm_madd_pi16(temp2, mix2), shifter);
pVdest[1] = _mm_packs_pi32(temp1, temp2); // pack 2*2*32bit => 4*16bit pVdest[1] = _mm_packs_pi32(temp1, temp2); // pack 2*2*32bit => 4*16bit
// update mix += adder // update mix += adder
mix1 = _mm_add_pi16(mix1, adder); mix1 = _mm_add_pi16(mix1, adder);
mix2 = _mm_add_pi16(mix2, adder); mix2 = _mm_add_pi16(mix2, adder);
pVinput += 2; pVinput += 2;
pVMidBuf += 2; pVMidBuf += 2;
pVdest += 2; pVdest += 2;
} }
_m_empty(); // clear MMS state _m_empty(); // clear MMS state
} }
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
// //
// implementation of MMX optimized functions of class 'FIRFilter' // implementation of MMX optimized functions of class 'FIRFilter'
// //
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
#include "FIRFilter.h" #include "FIRFilter.h"
FIRFilterMMX::FIRFilterMMX() : FIRFilter() FIRFilterMMX::FIRFilterMMX() : FIRFilter()
{ {
filterCoeffsUnalign = NULL; filterCoeffsUnalign = NULL;
} }
FIRFilterMMX::~FIRFilterMMX() FIRFilterMMX::~FIRFilterMMX()
{ {
delete[] filterCoeffsUnalign; delete[] filterCoeffsUnalign;
} }
// (overloaded) Calculates filter coefficients for MMX routine // (overloaded) Calculates filter coefficients for MMX routine
void FIRFilterMMX::setCoefficients(const short *coeffs, uint newLength, uint uResultDivFactor) void FIRFilterMMX::setCoefficients(const short *coeffs, uint newLength, uint uResultDivFactor)
{ {
uint i; uint i;
FIRFilter::setCoefficients(coeffs, newLength, uResultDivFactor); FIRFilter::setCoefficients(coeffs, newLength, uResultDivFactor);
// Ensure that filter coeffs array is aligned to 16-byte boundary // Ensure that filter coeffs array is aligned to 16-byte boundary
delete[] filterCoeffsUnalign; delete[] filterCoeffsUnalign;
filterCoeffsUnalign = new short[2 * newLength + 8]; filterCoeffsUnalign = new short[2 * newLength + 8];
filterCoeffsAlign = (short *)SOUNDTOUCH_ALIGN_POINTER_16(filterCoeffsUnalign); filterCoeffsAlign = (short *)SOUNDTOUCH_ALIGN_POINTER_16(filterCoeffsUnalign);
// rearrange the filter coefficients for mmx routines // rearrange the filter coefficients for mmx routines
for (i = 0;i < length; i += 4) for (i = 0;i < length; i += 4)
{ {
filterCoeffsAlign[2 * i + 0] = coeffs[i + 0]; filterCoeffsAlign[2 * i + 0] = coeffs[i + 0];
filterCoeffsAlign[2 * i + 1] = coeffs[i + 2]; filterCoeffsAlign[2 * i + 1] = coeffs[i + 2];
filterCoeffsAlign[2 * i + 2] = coeffs[i + 0]; filterCoeffsAlign[2 * i + 2] = coeffs[i + 0];
filterCoeffsAlign[2 * i + 3] = coeffs[i + 2]; filterCoeffsAlign[2 * i + 3] = coeffs[i + 2];
filterCoeffsAlign[2 * i + 4] = coeffs[i + 1]; filterCoeffsAlign[2 * i + 4] = coeffs[i + 1];
filterCoeffsAlign[2 * i + 5] = coeffs[i + 3]; filterCoeffsAlign[2 * i + 5] = coeffs[i + 3];
filterCoeffsAlign[2 * i + 6] = coeffs[i + 1]; filterCoeffsAlign[2 * i + 6] = coeffs[i + 1];
filterCoeffsAlign[2 * i + 7] = coeffs[i + 3]; filterCoeffsAlign[2 * i + 7] = coeffs[i + 3];
} }
} }
// mmx-optimized version of the filter routine for stereo sound // mmx-optimized version of the filter routine for stereo sound
uint FIRFilterMMX::evaluateFilterStereo(short *dest, const short *src, uint numSamples) const uint FIRFilterMMX::evaluateFilterStereo(short *dest, const short *src, uint numSamples) const
{ {
// Create stack copies of the needed member variables for asm routines : // Create stack copies of the needed member variables for asm routines :
uint i, j; uint i, j;
__m64 *pVdest = (__m64*)dest; __m64 *pVdest = (__m64*)dest;
if (length < 2) return 0; if (length < 2) return 0;
for (i = 0; i < (numSamples - length) / 2; i ++) for (i = 0; i < (numSamples - length) / 2; i ++)
{ {
__m64 accu1; __m64 accu1;
__m64 accu2; __m64 accu2;
const __m64 *pVsrc = (const __m64*)src; const __m64 *pVsrc = (const __m64*)src;
const __m64 *pVfilter = (const __m64*)filterCoeffsAlign; const __m64 *pVfilter = (const __m64*)filterCoeffsAlign;
accu1 = accu2 = _mm_setzero_si64(); accu1 = accu2 = _mm_setzero_si64();
for (j = 0; j < lengthDiv8 * 2; j ++) for (j = 0; j < lengthDiv8 * 2; j ++)
{ {
__m64 temp1, temp2; __m64 temp1, temp2;
temp1 = _mm_unpacklo_pi16(pVsrc[0], pVsrc[1]); // = l2 l0 r2 r0 temp1 = _mm_unpacklo_pi16(pVsrc[0], pVsrc[1]); // = l2 l0 r2 r0
temp2 = _mm_unpackhi_pi16(pVsrc[0], pVsrc[1]); // = l3 l1 r3 r1 temp2 = _mm_unpackhi_pi16(pVsrc[0], pVsrc[1]); // = l3 l1 r3 r1
accu1 = _mm_add_pi32(accu1, _mm_madd_pi16(temp1, pVfilter[0])); // += l2*f2+l0*f0 r2*f2+r0*f0 accu1 = _mm_add_pi32(accu1, _mm_madd_pi16(temp1, pVfilter[0])); // += l2*f2+l0*f0 r2*f2+r0*f0
accu1 = _mm_add_pi32(accu1, _mm_madd_pi16(temp2, pVfilter[1])); // += l3*f3+l1*f1 r3*f3+r1*f1 accu1 = _mm_add_pi32(accu1, _mm_madd_pi16(temp2, pVfilter[1])); // += l3*f3+l1*f1 r3*f3+r1*f1
temp1 = _mm_unpacklo_pi16(pVsrc[1], pVsrc[2]); // = l4 l2 r4 r2 temp1 = _mm_unpacklo_pi16(pVsrc[1], pVsrc[2]); // = l4 l2 r4 r2
accu2 = _mm_add_pi32(accu2, _mm_madd_pi16(temp2, pVfilter[0])); // += l3*f2+l1*f0 r3*f2+r1*f0 accu2 = _mm_add_pi32(accu2, _mm_madd_pi16(temp2, pVfilter[0])); // += l3*f2+l1*f0 r3*f2+r1*f0
accu2 = _mm_add_pi32(accu2, _mm_madd_pi16(temp1, pVfilter[1])); // += l4*f3+l2*f1 r4*f3+r2*f1 accu2 = _mm_add_pi32(accu2, _mm_madd_pi16(temp1, pVfilter[1])); // += l4*f3+l2*f1 r4*f3+r2*f1
// accu1 += l2*f2+l0*f0 r2*f2+r0*f0 // accu1 += l2*f2+l0*f0 r2*f2+r0*f0
// += l3*f3+l1*f1 r3*f3+r1*f1 // += l3*f3+l1*f1 r3*f3+r1*f1
// accu2 += l3*f2+l1*f0 r3*f2+r1*f0 // accu2 += l3*f2+l1*f0 r3*f2+r1*f0
// l4*f3+l2*f1 r4*f3+r2*f1 // l4*f3+l2*f1 r4*f3+r2*f1
pVfilter += 2; pVfilter += 2;
pVsrc += 2; pVsrc += 2;
} }
// accu >>= resultDivFactor // accu >>= resultDivFactor
accu1 = _mm_srai_pi32(accu1, resultDivFactor); accu1 = _mm_srai_pi32(accu1, resultDivFactor);
accu2 = _mm_srai_pi32(accu2, resultDivFactor); accu2 = _mm_srai_pi32(accu2, resultDivFactor);
// pack 2*2*32bits => 4*16 bits // pack 2*2*32bits => 4*16 bits
pVdest[0] = _mm_packs_pi32(accu1, accu2); pVdest[0] = _mm_packs_pi32(accu1, accu2);
src += 4; src += 4;
pVdest ++; pVdest ++;
} }
_m_empty(); // clear emms state _m_empty(); // clear emms state
return (numSamples & 0xfffffffe) - length; return (numSamples & 0xfffffffe) - length;
} }
#endif // SOUNDTOUCH_ALLOW_MMX #endif // SOUNDTOUCH_ALLOW_MMX

View File

@ -1,361 +1,361 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// ///
/// SSE optimized routines for Pentium-III, Athlon-XP and later CPUs. All SSE /// SSE optimized routines for Pentium-III, Athlon-XP and later CPUs. All SSE
/// optimized functions have been gathered into this single source /// optimized functions have been gathered into this single source
/// code file, regardless to their class or original source code file, in order /// code file, regardless to their class or original source code file, in order
/// to ease porting the library to other compiler and processor platforms. /// to ease porting the library to other compiler and processor platforms.
/// ///
/// The SSE-optimizations are programmed using SSE compiler intrinsics that /// The SSE-optimizations are programmed using SSE compiler intrinsics that
/// are supported both by Microsoft Visual C++ and GCC compilers, so this file /// are supported both by Microsoft Visual C++ and GCC compilers, so this file
/// should compile with both toolsets. /// should compile with both toolsets.
/// ///
/// NOTICE: If using Visual Studio 6.0, you'll need to install the "Visual C++ /// 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 /// 6.0 processor pack" update to support SSE instruction set. The update is
/// available for download at Microsoft Developers Network, see here: /// available for download at Microsoft Developers Network, see here:
/// http://msdn.microsoft.com/en-us/vstudio/aa718349.aspx /// http://msdn.microsoft.com/en-us/vstudio/aa718349.aspx
/// ///
/// If the above URL is expired or removed, go to "http://msdn.microsoft.com" and /// If the above URL is expired or removed, go to "http://msdn.microsoft.com" and
/// perform a search with keywords "processor pack". /// perform a search with keywords "processor pack".
/// ///
/// Author : Copyright (c) Olli Parviainen /// Author : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi /// Author e-mail : oparviai 'at' iki.fi
/// SoundTouch WWW: http://www.surina.net/soundtouch /// SoundTouch WWW: http://www.surina.net/soundtouch
/// ///
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// Last changed : $Date: 2012-11-08 20:53:01 +0200 (Thu, 08 Nov 2012) $ // Last changed : $Date: 2012-11-08 18:53:01 +0000 (Thu, 08 Nov 2012) $
// File revision : $Revision: 4 $ // File revision : $Revision: 4 $
// //
// $Id: sse_optimized.cpp 160 2012-11-08 18:53:01Z oparviai $ // $Id: sse_optimized.cpp 160 2012-11-08 18:53:01Z oparviai $
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// License : // License :
// //
// SoundTouch audio processing library // SoundTouch audio processing library
// Copyright (c) Olli Parviainen // Copyright (c) Olli Parviainen
// //
// This library is free software; you can redistribute it and/or // This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public // modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either // License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version. // 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, // This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details. // Lesser General Public License for more details.
// //
// You should have received a copy of the GNU Lesser General Public // You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software // License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include "cpu_detect.h" #include "cpu_detect.h"
#include "STTypes.h" #include "STTypes.h"
using namespace soundtouch; using namespace soundtouch;
#ifdef SOUNDTOUCH_ALLOW_SSE #ifdef SOUNDTOUCH_ALLOW_SSE
// SSE routines available only with float sample type // SSE routines available only with float sample type
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
// //
// implementation of SSE optimized functions of class 'TDStretchSSE' // implementation of SSE optimized functions of class 'TDStretchSSE'
// //
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
#include "TDStretch.h" #include "TDStretch.h"
#include <xmmintrin.h> #include <xmmintrin.h>
#include <math.h> #include <math.h>
// Calculates cross correlation of two buffers // Calculates cross correlation of two buffers
double TDStretchSSE::calcCrossCorr(const float *pV1, const float *pV2) const double TDStretchSSE::calcCrossCorr(const float *pV1, const float *pV2) const
{ {
int i; int i;
const float *pVec1; const float *pVec1;
const __m128 *pVec2; const __m128 *pVec2;
__m128 vSum, vNorm; __m128 vSum, vNorm;
// Note. It means a major slow-down if the routine needs to tolerate // Note. It means a major slow-down if the routine needs to tolerate
// unaligned __m128 memory accesses. It's way faster if we can skip // unaligned __m128 memory accesses. It's way faster if we can skip
// unaligned slots and use _mm_load_ps instruction instead of _mm_loadu_ps. // unaligned slots and use _mm_load_ps instruction instead of _mm_loadu_ps.
// This can mean up to ~ 10-fold difference (incl. part of which is // This can mean up to ~ 10-fold difference (incl. part of which is
// due to skipping every second round for stereo sound though). // due to skipping every second round for stereo sound though).
// //
// Compile-time define SOUNDTOUCH_ALLOW_NONEXACT_SIMD_OPTIMIZATION is provided // Compile-time define SOUNDTOUCH_ALLOW_NONEXACT_SIMD_OPTIMIZATION is provided
// for choosing if this little cheating is allowed. // for choosing if this little cheating is allowed.
#ifdef SOUNDTOUCH_ALLOW_NONEXACT_SIMD_OPTIMIZATION #ifdef SOUNDTOUCH_ALLOW_NONEXACT_SIMD_OPTIMIZATION
// Little cheating allowed, return valid correlation only for // Little cheating allowed, return valid correlation only for
// aligned locations, meaning every second round for stereo sound. // aligned locations, meaning every second round for stereo sound.
#define _MM_LOAD _mm_load_ps #define _MM_LOAD _mm_load_ps
if (((ulongptr)pV1) & 15) return -1e50; // skip unaligned locations if (((ulongptr)pV1) & 15) return -1e50; // skip unaligned locations
#else #else
// No cheating allowed, use unaligned load & take the resulting // No cheating allowed, use unaligned load & take the resulting
// performance hit. // performance hit.
#define _MM_LOAD _mm_loadu_ps #define _MM_LOAD _mm_loadu_ps
#endif #endif
// ensure overlapLength is divisible by 8 // ensure overlapLength is divisible by 8
assert((overlapLength % 8) == 0); assert((overlapLength % 8) == 0);
// Calculates the cross-correlation value between 'pV1' and 'pV2' vectors // Calculates the cross-correlation value between 'pV1' and 'pV2' vectors
// Note: pV2 _must_ be aligned to 16-bit boundary, pV1 need not. // Note: pV2 _must_ be aligned to 16-bit boundary, pV1 need not.
pVec1 = (const float*)pV1; pVec1 = (const float*)pV1;
pVec2 = (const __m128*)pV2; pVec2 = (const __m128*)pV2;
vSum = vNorm = _mm_setzero_ps(); vSum = vNorm = _mm_setzero_ps();
// Unroll the loop by factor of 4 * 4 operations. Use same routine for // Unroll the loop by factor of 4 * 4 operations. Use same routine for
// stereo & mono, for mono it just means twice the amount of unrolling. // stereo & mono, for mono it just means twice the amount of unrolling.
for (i = 0; i < channels * overlapLength / 16; i ++) for (i = 0; i < channels * overlapLength / 16; i ++)
{ {
__m128 vTemp; __m128 vTemp;
// vSum += pV1[0..3] * pV2[0..3] // vSum += pV1[0..3] * pV2[0..3]
vTemp = _MM_LOAD(pVec1); vTemp = _MM_LOAD(pVec1);
vSum = _mm_add_ps(vSum, _mm_mul_ps(vTemp ,pVec2[0])); vSum = _mm_add_ps(vSum, _mm_mul_ps(vTemp ,pVec2[0]));
vNorm = _mm_add_ps(vNorm, _mm_mul_ps(vTemp ,vTemp)); vNorm = _mm_add_ps(vNorm, _mm_mul_ps(vTemp ,vTemp));
// vSum += pV1[4..7] * pV2[4..7] // vSum += pV1[4..7] * pV2[4..7]
vTemp = _MM_LOAD(pVec1 + 4); vTemp = _MM_LOAD(pVec1 + 4);
vSum = _mm_add_ps(vSum, _mm_mul_ps(vTemp, pVec2[1])); vSum = _mm_add_ps(vSum, _mm_mul_ps(vTemp, pVec2[1]));
vNorm = _mm_add_ps(vNorm, _mm_mul_ps(vTemp ,vTemp)); vNorm = _mm_add_ps(vNorm, _mm_mul_ps(vTemp ,vTemp));
// vSum += pV1[8..11] * pV2[8..11] // vSum += pV1[8..11] * pV2[8..11]
vTemp = _MM_LOAD(pVec1 + 8); vTemp = _MM_LOAD(pVec1 + 8);
vSum = _mm_add_ps(vSum, _mm_mul_ps(vTemp, pVec2[2])); vSum = _mm_add_ps(vSum, _mm_mul_ps(vTemp, pVec2[2]));
vNorm = _mm_add_ps(vNorm, _mm_mul_ps(vTemp ,vTemp)); vNorm = _mm_add_ps(vNorm, _mm_mul_ps(vTemp ,vTemp));
// vSum += pV1[12..15] * pV2[12..15] // vSum += pV1[12..15] * pV2[12..15]
vTemp = _MM_LOAD(pVec1 + 12); vTemp = _MM_LOAD(pVec1 + 12);
vSum = _mm_add_ps(vSum, _mm_mul_ps(vTemp, pVec2[3])); vSum = _mm_add_ps(vSum, _mm_mul_ps(vTemp, pVec2[3]));
vNorm = _mm_add_ps(vNorm, _mm_mul_ps(vTemp ,vTemp)); vNorm = _mm_add_ps(vNorm, _mm_mul_ps(vTemp ,vTemp));
pVec1 += 16; pVec1 += 16;
pVec2 += 4; pVec2 += 4;
} }
// return value = vSum[0] + vSum[1] + vSum[2] + vSum[3] // return value = vSum[0] + vSum[1] + vSum[2] + vSum[3]
float *pvNorm = (float*)&vNorm; float *pvNorm = (float*)&vNorm;
double norm = sqrt(pvNorm[0] + pvNorm[1] + pvNorm[2] + pvNorm[3]); double norm = sqrt(pvNorm[0] + pvNorm[1] + pvNorm[2] + pvNorm[3]);
if (norm < 1e-9) norm = 1.0; // to avoid div by zero if (norm < 1e-9) norm = 1.0; // to avoid div by zero
float *pvSum = (float*)&vSum; float *pvSum = (float*)&vSum;
return (double)(pvSum[0] + pvSum[1] + pvSum[2] + pvSum[3]) / norm; return (double)(pvSum[0] + pvSum[1] + pvSum[2] + pvSum[3]) / norm;
/* This is approximately corresponding routine in C-language yet without normalization: /* This is approximately corresponding routine in C-language yet without normalization:
double corr, norm; double corr, norm;
uint i; uint i;
// Calculates the cross-correlation value between 'pV1' and 'pV2' vectors // Calculates the cross-correlation value between 'pV1' and 'pV2' vectors
corr = norm = 0.0; corr = norm = 0.0;
for (i = 0; i < channels * overlapLength / 16; i ++) for (i = 0; i < channels * overlapLength / 16; i ++)
{ {
corr += pV1[0] * pV2[0] + corr += pV1[0] * pV2[0] +
pV1[1] * pV2[1] + pV1[1] * pV2[1] +
pV1[2] * pV2[2] + pV1[2] * pV2[2] +
pV1[3] * pV2[3] + pV1[3] * pV2[3] +
pV1[4] * pV2[4] + pV1[4] * pV2[4] +
pV1[5] * pV2[5] + pV1[5] * pV2[5] +
pV1[6] * pV2[6] + pV1[6] * pV2[6] +
pV1[7] * pV2[7] + pV1[7] * pV2[7] +
pV1[8] * pV2[8] + pV1[8] * pV2[8] +
pV1[9] * pV2[9] + pV1[9] * pV2[9] +
pV1[10] * pV2[10] + pV1[10] * pV2[10] +
pV1[11] * pV2[11] + pV1[11] * pV2[11] +
pV1[12] * pV2[12] + pV1[12] * pV2[12] +
pV1[13] * pV2[13] + pV1[13] * pV2[13] +
pV1[14] * pV2[14] + pV1[14] * pV2[14] +
pV1[15] * pV2[15]; pV1[15] * pV2[15];
for (j = 0; j < 15; j ++) norm += pV1[j] * pV1[j]; for (j = 0; j < 15; j ++) norm += pV1[j] * pV1[j];
pV1 += 16; pV1 += 16;
pV2 += 16; pV2 += 16;
} }
return corr / sqrt(norm); return corr / sqrt(norm);
*/ */
} }
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
// //
// implementation of SSE optimized functions of class 'FIRFilter' // implementation of SSE optimized functions of class 'FIRFilter'
// //
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
#include "FIRFilter.h" #include "FIRFilter.h"
FIRFilterSSE::FIRFilterSSE() : FIRFilter() FIRFilterSSE::FIRFilterSSE() : FIRFilter()
{ {
filterCoeffsAlign = NULL; filterCoeffsAlign = NULL;
filterCoeffsUnalign = NULL; filterCoeffsUnalign = NULL;
} }
FIRFilterSSE::~FIRFilterSSE() FIRFilterSSE::~FIRFilterSSE()
{ {
delete[] filterCoeffsUnalign; delete[] filterCoeffsUnalign;
filterCoeffsAlign = NULL; filterCoeffsAlign = NULL;
filterCoeffsUnalign = NULL; filterCoeffsUnalign = NULL;
} }
// (overloaded) Calculates filter coefficients for SSE routine // (overloaded) Calculates filter coefficients for SSE routine
void FIRFilterSSE::setCoefficients(const float *coeffs, uint newLength, uint uResultDivFactor) void FIRFilterSSE::setCoefficients(const float *coeffs, uint newLength, uint uResultDivFactor)
{ {
uint i; uint i;
float fDivider; float fDivider;
FIRFilter::setCoefficients(coeffs, newLength, uResultDivFactor); FIRFilter::setCoefficients(coeffs, newLength, uResultDivFactor);
// Scale the filter coefficients so that it won't be necessary to scale the filtering result // Scale the filter coefficients so that it won't be necessary to scale the filtering result
// also rearrange coefficients suitably for SSE // also rearrange coefficients suitably for SSE
// Ensure that filter coeffs array is aligned to 16-byte boundary // Ensure that filter coeffs array is aligned to 16-byte boundary
delete[] filterCoeffsUnalign; delete[] filterCoeffsUnalign;
filterCoeffsUnalign = new float[2 * newLength + 4]; filterCoeffsUnalign = new float[2 * newLength + 4];
filterCoeffsAlign = (float *)SOUNDTOUCH_ALIGN_POINTER_16(filterCoeffsUnalign); filterCoeffsAlign = (float *)SOUNDTOUCH_ALIGN_POINTER_16(filterCoeffsUnalign);
fDivider = (float)resultDivider; fDivider = (float)resultDivider;
// rearrange the filter coefficients for mmx routines // rearrange the filter coefficients for mmx routines
for (i = 0; i < newLength; i ++) for (i = 0; i < newLength; i ++)
{ {
filterCoeffsAlign[2 * i + 0] = filterCoeffsAlign[2 * i + 0] =
filterCoeffsAlign[2 * i + 1] = coeffs[i + 0] / fDivider; filterCoeffsAlign[2 * i + 1] = coeffs[i + 0] / fDivider;
} }
} }
// SSE-optimized version of the filter routine for stereo sound // SSE-optimized version of the filter routine for stereo sound
uint FIRFilterSSE::evaluateFilterStereo(float *dest, const float *source, uint numSamples) const uint FIRFilterSSE::evaluateFilterStereo(float *dest, const float *source, uint numSamples) const
{ {
int count = (int)((numSamples - length) & (uint)-2); int count = (int)((numSamples - length) & (uint)-2);
int j; int j;
assert(count % 2 == 0); assert(count % 2 == 0);
if (count < 2) return 0; if (count < 2) return 0;
assert(source != NULL); assert(source != NULL);
assert(dest != NULL); assert(dest != NULL);
assert((length % 8) == 0); assert((length % 8) == 0);
assert(filterCoeffsAlign != NULL); assert(filterCoeffsAlign != NULL);
assert(((ulongptr)filterCoeffsAlign) % 16 == 0); assert(((ulongptr)filterCoeffsAlign) % 16 == 0);
// filter is evaluated for two stereo samples with each iteration, thus use of 'j += 2' // filter is evaluated for two stereo samples with each iteration, thus use of 'j += 2'
for (j = 0; j < count; j += 2) for (j = 0; j < count; j += 2)
{ {
const float *pSrc; const float *pSrc;
const __m128 *pFil; const __m128 *pFil;
__m128 sum1, sum2; __m128 sum1, sum2;
uint i; uint i;
pSrc = (const float*)source; // source audio data pSrc = (const float*)source; // source audio data
pFil = (const __m128*)filterCoeffsAlign; // filter coefficients. NOTE: Assumes coefficients pFil = (const __m128*)filterCoeffsAlign; // filter coefficients. NOTE: Assumes coefficients
// are aligned to 16-byte boundary // are aligned to 16-byte boundary
sum1 = sum2 = _mm_setzero_ps(); sum1 = sum2 = _mm_setzero_ps();
for (i = 0; i < length / 8; i ++) for (i = 0; i < length / 8; i ++)
{ {
// Unroll loop for efficiency & calculate filter for 2*2 stereo samples // Unroll loop for efficiency & calculate filter for 2*2 stereo samples
// at each pass // at each pass
// sum1 is accu for 2*2 filtered stereo sound data at the primary sound data offset // sum1 is accu for 2*2 filtered stereo sound data at the primary sound data offset
// sum2 is accu for 2*2 filtered stereo sound data for the next sound sample offset. // sum2 is accu for 2*2 filtered stereo sound data for the next sound sample offset.
sum1 = _mm_add_ps(sum1, _mm_mul_ps(_mm_loadu_ps(pSrc) , pFil[0])); sum1 = _mm_add_ps(sum1, _mm_mul_ps(_mm_loadu_ps(pSrc) , pFil[0]));
sum2 = _mm_add_ps(sum2, _mm_mul_ps(_mm_loadu_ps(pSrc + 2), pFil[0])); sum2 = _mm_add_ps(sum2, _mm_mul_ps(_mm_loadu_ps(pSrc + 2), pFil[0]));
sum1 = _mm_add_ps(sum1, _mm_mul_ps(_mm_loadu_ps(pSrc + 4), pFil[1])); sum1 = _mm_add_ps(sum1, _mm_mul_ps(_mm_loadu_ps(pSrc + 4), pFil[1]));
sum2 = _mm_add_ps(sum2, _mm_mul_ps(_mm_loadu_ps(pSrc + 6), pFil[1])); sum2 = _mm_add_ps(sum2, _mm_mul_ps(_mm_loadu_ps(pSrc + 6), pFil[1]));
sum1 = _mm_add_ps(sum1, _mm_mul_ps(_mm_loadu_ps(pSrc + 8) , pFil[2])); sum1 = _mm_add_ps(sum1, _mm_mul_ps(_mm_loadu_ps(pSrc + 8) , pFil[2]));
sum2 = _mm_add_ps(sum2, _mm_mul_ps(_mm_loadu_ps(pSrc + 10), pFil[2])); sum2 = _mm_add_ps(sum2, _mm_mul_ps(_mm_loadu_ps(pSrc + 10), pFil[2]));
sum1 = _mm_add_ps(sum1, _mm_mul_ps(_mm_loadu_ps(pSrc + 12), pFil[3])); sum1 = _mm_add_ps(sum1, _mm_mul_ps(_mm_loadu_ps(pSrc + 12), pFil[3]));
sum2 = _mm_add_ps(sum2, _mm_mul_ps(_mm_loadu_ps(pSrc + 14), pFil[3])); sum2 = _mm_add_ps(sum2, _mm_mul_ps(_mm_loadu_ps(pSrc + 14), pFil[3]));
pSrc += 16; pSrc += 16;
pFil += 4; pFil += 4;
} }
// Now sum1 and sum2 both have a filtered 2-channel sample each, but we still need // Now sum1 and sum2 both have a filtered 2-channel sample each, but we still need
// to sum the two hi- and lo-floats of these registers together. // to sum the two hi- and lo-floats of these registers together.
// post-shuffle & add the filtered values and store to dest. // post-shuffle & add the filtered values and store to dest.
_mm_storeu_ps(dest, _mm_add_ps( _mm_storeu_ps(dest, _mm_add_ps(
_mm_shuffle_ps(sum1, sum2, _MM_SHUFFLE(1,0,3,2)), // s2_1 s2_0 s1_3 s1_2 _mm_shuffle_ps(sum1, sum2, _MM_SHUFFLE(1,0,3,2)), // s2_1 s2_0 s1_3 s1_2
_mm_shuffle_ps(sum1, sum2, _MM_SHUFFLE(3,2,1,0)) // s2_3 s2_2 s1_1 s1_0 _mm_shuffle_ps(sum1, sum2, _MM_SHUFFLE(3,2,1,0)) // s2_3 s2_2 s1_1 s1_0
)); ));
source += 4; source += 4;
dest += 4; dest += 4;
} }
// Ideas for further improvement: // Ideas for further improvement:
// 1. If it could be guaranteed that 'source' were always aligned to 16-byte // 1. If it could be guaranteed that 'source' were always aligned to 16-byte
// boundary, a faster aligned '_mm_load_ps' instruction could be used. // boundary, a faster aligned '_mm_load_ps' instruction could be used.
// 2. If it could be guaranteed that 'dest' were always aligned to 16-byte // 2. If it could be guaranteed that 'dest' were always aligned to 16-byte
// boundary, a faster '_mm_store_ps' instruction could be used. // boundary, a faster '_mm_store_ps' instruction could be used.
return (uint)count; return (uint)count;
/* original routine in C-language. please notice the C-version has differently /* original routine in C-language. please notice the C-version has differently
organized coefficients though. organized coefficients though.
double suml1, suml2; double suml1, suml2;
double sumr1, sumr2; double sumr1, sumr2;
uint i, j; uint i, j;
for (j = 0; j < count; j += 2) for (j = 0; j < count; j += 2)
{ {
const float *ptr; const float *ptr;
const float *pFil; const float *pFil;
suml1 = sumr1 = 0.0; suml1 = sumr1 = 0.0;
suml2 = sumr2 = 0.0; suml2 = sumr2 = 0.0;
ptr = src; ptr = src;
pFil = filterCoeffs; pFil = filterCoeffs;
for (i = 0; i < lengthLocal; i ++) for (i = 0; i < lengthLocal; i ++)
{ {
// unroll loop for efficiency. // unroll loop for efficiency.
suml1 += ptr[0] * pFil[0] + suml1 += ptr[0] * pFil[0] +
ptr[2] * pFil[2] + ptr[2] * pFil[2] +
ptr[4] * pFil[4] + ptr[4] * pFil[4] +
ptr[6] * pFil[6]; ptr[6] * pFil[6];
sumr1 += ptr[1] * pFil[1] + sumr1 += ptr[1] * pFil[1] +
ptr[3] * pFil[3] + ptr[3] * pFil[3] +
ptr[5] * pFil[5] + ptr[5] * pFil[5] +
ptr[7] * pFil[7]; ptr[7] * pFil[7];
suml2 += ptr[8] * pFil[0] + suml2 += ptr[8] * pFil[0] +
ptr[10] * pFil[2] + ptr[10] * pFil[2] +
ptr[12] * pFil[4] + ptr[12] * pFil[4] +
ptr[14] * pFil[6]; ptr[14] * pFil[6];
sumr2 += ptr[9] * pFil[1] + sumr2 += ptr[9] * pFil[1] +
ptr[11] * pFil[3] + ptr[11] * pFil[3] +
ptr[13] * pFil[5] + ptr[13] * pFil[5] +
ptr[15] * pFil[7]; ptr[15] * pFil[7];
ptr += 16; ptr += 16;
pFil += 8; pFil += 8;
} }
dest[0] = (float)suml1; dest[0] = (float)suml1;
dest[1] = (float)sumr1; dest[1] = (float)sumr1;
dest[2] = (float)suml2; dest[2] = (float)suml2;
dest[3] = (float)sumr2; dest[3] = (float)sumr2;
src += 4; src += 4;
dest += 4; dest += 4;
} }
*/ */
} }
#endif // SOUNDTOUCH_ALLOW_SSE #endif // SOUNDTOUCH_ALLOW_SSE