mirror of https://github.com/PCSX2/pcsx2.git
Soundtouch update from 1.5 to 1.71 as per patch from lincolnh_br.
There's been changes in the VS2008 project file which we may want to look at and port to 2010/2012 separately but it builds like this in 2010 here. I want to wait and see if there's any issues with Linux first, too. Thanks to lincolnh_br :) git-svn-id: http://pcsx2.googlecode.com/svn/trunk@5619 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
parent
86a2f5a8ec
commit
796bdb8f37
|
@ -1,9 +1,9 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// FIR low-pass (anti-alias) filter with filter coefficient design routine and
|
||||
/// MMX optimization.
|
||||
///
|
||||
/// Anti-alias filter is used to prevent folding of high frequencies when
|
||||
/// MMX optimization.
|
||||
///
|
||||
/// Anti-alias filter is used to prevent folding of high frequencies when
|
||||
/// transposing the sample rate with interpolation.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
|
@ -12,7 +12,7 @@
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2009-01-11 13:34:24 +0200 (Sun, 11 Jan 2009) $
|
||||
// Last changed : $Date: 2009-01-11 09:34:24 -0200 (dom, 11 jan 2009) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: AAFilter.cpp 45 2009-01-11 11:34:24Z oparviai $
|
||||
|
@ -112,21 +112,21 @@ void AAFilter::calculateCoeffs()
|
|||
work = new double[length];
|
||||
coeffs = new SAMPLETYPE[length];
|
||||
|
||||
fc2 = 2.0 * cutoffFreq;
|
||||
fc2 = 2.0 * cutoffFreq;
|
||||
wc = PI * fc2;
|
||||
tempCoeff = TWOPI / (double)length;
|
||||
|
||||
sum = 0;
|
||||
for (i = 0; i < length; i ++)
|
||||
for (i = 0; i < length; i ++)
|
||||
{
|
||||
cntTemp = (double)i - (double)(length / 2);
|
||||
|
||||
temp = cntTemp * wc;
|
||||
if (temp != 0)
|
||||
if (temp != 0)
|
||||
{
|
||||
h = fc2 * sin(temp) / temp; // sinc function
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
{
|
||||
h = 1.0;
|
||||
}
|
||||
|
@ -135,7 +135,7 @@ void AAFilter::calculateCoeffs()
|
|||
temp = w * h;
|
||||
work[i] = temp;
|
||||
|
||||
// calc net sum of coefficients
|
||||
// calc net sum of coefficients
|
||||
sum += temp;
|
||||
}
|
||||
|
||||
|
@ -151,7 +151,7 @@ void AAFilter::calculateCoeffs()
|
|||
// divided by 16384
|
||||
scaleCoeff = 16384.0f / sum;
|
||||
|
||||
for (i = 0; i < length; i ++)
|
||||
for (i = 0; i < length; i ++)
|
||||
{
|
||||
// scale & round to nearest integer
|
||||
temp = work[i] * scaleCoeff;
|
||||
|
@ -169,8 +169,8 @@ void AAFilter::calculateCoeffs()
|
|||
}
|
||||
|
||||
|
||||
// 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.
|
||||
// Note : The amount of outputted samples is by value of 'filter length'
|
||||
// smaller than the amount of input samples.
|
||||
uint AAFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels) const
|
||||
{
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo
|
||||
/// while maintaining the original pitch by using a time domain WSOLA-like method
|
||||
/// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo
|
||||
/// while maintaining the original pitch by using a time domain WSOLA-like method
|
||||
/// with several performance-increasing tweaks.
|
||||
///
|
||||
/// Anti-alias filter is used to prevent folding of high frequencies when
|
||||
/// Anti-alias filter is used to prevent folding of high frequencies when
|
||||
/// transposing the sample rate with interpolation.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
|
@ -13,7 +13,7 @@
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2008-02-10 18:26:55 +0200 (Sun, 10 Feb 2008) $
|
||||
// Last changed : $Date: 2008-02-10 14:26:55 -0200 (dom, 10 fev 2008) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: AAFilter.h 11 2008-02-10 16:26:55Z oparviai $
|
||||
|
@ -67,8 +67,8 @@ public:
|
|||
|
||||
~AAFilter();
|
||||
|
||||
/// Sets new anti-alias filter cut-off edge frequency, scaled to sampling
|
||||
/// frequency (nyquist frequency = 0.5). The filter will cut off the
|
||||
/// Sets new anti-alias filter cut-off edge frequency, scaled to sampling
|
||||
/// frequency (nyquist frequency = 0.5). The filter will cut off the
|
||||
/// frequencies than that.
|
||||
void setCutoffFreq(double newCutoffFreq);
|
||||
|
||||
|
@ -77,12 +77,12 @@ public:
|
|||
|
||||
uint getLength() const;
|
||||
|
||||
/// 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.
|
||||
/// Note : The amount of outputted samples is by value of 'filter length'
|
||||
/// smaller than the amount of input samples.
|
||||
uint evaluate(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
uint numSamples,
|
||||
uint evaluate(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
uint numSamples,
|
||||
uint numChannels) const;
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,370 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Beats-per-minute (BPM) detection routine.
|
||||
///
|
||||
/// The beat detection algorithm works as follows:
|
||||
/// - Use function 'inputSamples' to input a chunks of samples to the class for
|
||||
/// analysis. It's a good idea to enter a large sound file or stream in smallish
|
||||
/// chunks of around few kilosamples in order not to extinguish too much RAM memory.
|
||||
/// - 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.
|
||||
/// Simple averaging is used for anti-alias filtering because the resulting signal
|
||||
/// quality isn't of that high importance.
|
||||
/// - Decimated sound data is enveloped, i.e. the amplitude shape is detected by
|
||||
/// taking absolute value that's smoothed by sliding average. Signal levels that
|
||||
/// are below a couple of times the general RMS amplitude level are cut away to
|
||||
/// leave only notable peaks there.
|
||||
/// - Repeating sound patterns (e.g. beats) are detected by calculating short-term
|
||||
/// autocorrelation function of the enveloped signal.
|
||||
/// - After whole sound data file has been analyzed as above, the bpm level is
|
||||
/// detected by function 'getBpm' that finds the highest peak of the autocorrelation
|
||||
/// function, calculates it's precise location and converts this reading to bpm's.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2012-08-30 16:45:25 -0300 (qui, 30 ago 2012) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: BPMDetect.cpp 149 2012-08-30 19:45:25Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "FIFOSampleBuffer.h"
|
||||
#include "PeakFinder.h"
|
||||
#include "BPMDetect.h"
|
||||
|
||||
using namespace soundtouch;
|
||||
|
||||
#define INPUT_BLOCK_SAMPLES 2048
|
||||
#define DECIMATED_BLOCK_SAMPLES 256
|
||||
|
||||
/// decay constant for calculating RMS volume sliding average approximation
|
||||
/// (time constant is about 10 sec)
|
||||
const float avgdecay = 0.99986f;
|
||||
|
||||
/// Normalization coefficient for calculating RMS sliding average approximation.
|
||||
const float avgnorm = (1 - avgdecay);
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Enable following define to create bpm analysis file:
|
||||
|
||||
// #define _CREATE_BPM_DEBUG_FILE
|
||||
|
||||
#ifdef _CREATE_BPM_DEBUG_FILE
|
||||
|
||||
#define DEBUGFILE_NAME "c:\\temp\\soundtouch-bpm-debug.txt"
|
||||
|
||||
static void _SaveDebugData(const float *data, int minpos, int maxpos, double coeff)
|
||||
{
|
||||
FILE *fptr = fopen(DEBUGFILE_NAME, "wt");
|
||||
int i;
|
||||
|
||||
if (fptr)
|
||||
{
|
||||
printf("\n\nWriting BPM debug data into file " DEBUGFILE_NAME "\n\n");
|
||||
for (i = minpos; i < maxpos; i ++)
|
||||
{
|
||||
fprintf(fptr, "%d\t%.1lf\t%f\n", i, coeff / (double)i, data[i]);
|
||||
}
|
||||
fclose(fptr);
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define _SaveDebugData(a,b,c,d)
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
BPMDetect::BPMDetect(int numChannels, int aSampleRate)
|
||||
{
|
||||
this->sampleRate = aSampleRate;
|
||||
this->channels = numChannels;
|
||||
|
||||
decimateSum = 0;
|
||||
decimateCount = 0;
|
||||
|
||||
envelopeAccu = 0;
|
||||
|
||||
// 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
|
||||
// to the actual level during processing.
|
||||
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||
// integer samples
|
||||
RMSVolumeAccu = (1500 * 1500) / avgnorm;
|
||||
#else
|
||||
// float samples, scaled to range [-1..+1[
|
||||
RMSVolumeAccu = (0.045f * 0.045f) / avgnorm;
|
||||
#endif
|
||||
|
||||
// choose decimation factor so that result is approx. 1000 Hz
|
||||
decimateBy = sampleRate / 1000;
|
||||
assert(decimateBy > 0);
|
||||
assert(INPUT_BLOCK_SAMPLES < decimateBy * DECIMATED_BLOCK_SAMPLES);
|
||||
|
||||
// Calculate window length & starting item according to desired min & max bpms
|
||||
windowLen = (60 * sampleRate) / (decimateBy * MIN_BPM);
|
||||
windowStart = (60 * sampleRate) / (decimateBy * MAX_BPM);
|
||||
|
||||
assert(windowLen > windowStart);
|
||||
|
||||
// allocate new working objects
|
||||
xcorr = new float[windowLen];
|
||||
memset(xcorr, 0, windowLen * sizeof(float));
|
||||
|
||||
// allocate processing buffer
|
||||
buffer = new FIFOSampleBuffer();
|
||||
// we do processing in mono mode
|
||||
buffer->setChannels(1);
|
||||
buffer->clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
BPMDetect::~BPMDetect()
|
||||
{
|
||||
delete[] xcorr;
|
||||
delete buffer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// convert to mono, low-pass filter & decimate to about 500 Hz.
|
||||
/// return number of outputted samples.
|
||||
///
|
||||
/// Decimation is used to remove the unnecessary frequencies and thus to reduce
|
||||
/// the amount of data needed to be processed as calculating autocorrelation
|
||||
/// function is a very-very heavy operation.
|
||||
///
|
||||
/// 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
|
||||
/// (it'd also be difficult to design a high-quality filter with steep cut-off at very
|
||||
/// narrow band)
|
||||
int BPMDetect::decimate(SAMPLETYPE *dest, const SAMPLETYPE *src, int numsamples)
|
||||
{
|
||||
int count, outcount;
|
||||
LONG_SAMPLETYPE out;
|
||||
|
||||
assert(channels > 0);
|
||||
assert(decimateBy > 0);
|
||||
outcount = 0;
|
||||
for (count = 0; count < numsamples; count ++)
|
||||
{
|
||||
int j;
|
||||
|
||||
// convert to mono and accumulate
|
||||
for (j = 0; j < channels; j ++)
|
||||
{
|
||||
decimateSum += src[j];
|
||||
}
|
||||
src += j;
|
||||
|
||||
decimateCount ++;
|
||||
if (decimateCount >= decimateBy)
|
||||
{
|
||||
// Store every Nth sample only
|
||||
out = (LONG_SAMPLETYPE)(decimateSum / (decimateBy * channels));
|
||||
decimateSum = 0;
|
||||
decimateCount = 0;
|
||||
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||
// check ranges for sure (shouldn't actually be necessary)
|
||||
if (out > 32767)
|
||||
{
|
||||
out = 32767;
|
||||
}
|
||||
else if (out < -32768)
|
||||
{
|
||||
out = -32768;
|
||||
}
|
||||
#endif // SOUNDTOUCH_INTEGER_SAMPLES
|
||||
dest[outcount] = (SAMPLETYPE)out;
|
||||
outcount ++;
|
||||
}
|
||||
}
|
||||
return outcount;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Calculates autocorrelation function of the sample history buffer
|
||||
void BPMDetect::updateXCorr(int process_samples)
|
||||
{
|
||||
int offs;
|
||||
SAMPLETYPE *pBuffer;
|
||||
|
||||
assert(buffer->numSamples() >= (uint)(process_samples + windowLen));
|
||||
|
||||
pBuffer = buffer->ptrBegin();
|
||||
for (offs = windowStart; offs < windowLen; offs ++)
|
||||
{
|
||||
LONG_SAMPLETYPE sum;
|
||||
int i;
|
||||
|
||||
sum = 0;
|
||||
for (i = 0; i < process_samples; i ++)
|
||||
{
|
||||
sum += pBuffer[i] * pBuffer[i + offs]; // scaling the sub-result shouldn't be necessary
|
||||
}
|
||||
// xcorr[offs] *= xcorr_decay; // decay 'xcorr' here with suitable coefficients
|
||||
// if it's desired that the system adapts automatically to
|
||||
// various bpms, e.g. in processing continouos music stream.
|
||||
// The 'xcorr_decay' should be a value that's smaller than but
|
||||
// close to one, and should also depend on 'process_samples' value.
|
||||
|
||||
xcorr[offs] += (float)sum;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Calculates envelope of the sample data
|
||||
void BPMDetect::calcEnvelope(SAMPLETYPE *samples, int numsamples)
|
||||
{
|
||||
const static double decay = 0.7f; // decay constant for smoothing the envelope
|
||||
const static double norm = (1 - decay);
|
||||
|
||||
int i;
|
||||
LONG_SAMPLETYPE out;
|
||||
double val;
|
||||
|
||||
for (i = 0; i < numsamples; i ++)
|
||||
{
|
||||
// calc average RMS volume
|
||||
RMSVolumeAccu *= avgdecay;
|
||||
val = (float)fabs((float)samples[i]);
|
||||
RMSVolumeAccu += val * val;
|
||||
|
||||
// cut amplitudes that are below cutoff ~2 times RMS volume
|
||||
// (we're interested in peak values, not the silent moments)
|
||||
if (val < 0.5 * sqrt(RMSVolumeAccu * avgnorm))
|
||||
{
|
||||
val = 0;
|
||||
}
|
||||
|
||||
// smooth amplitude envelope
|
||||
envelopeAccu *= decay;
|
||||
envelopeAccu += val;
|
||||
out = (LONG_SAMPLETYPE)(envelopeAccu * norm);
|
||||
|
||||
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||
// cut peaks (shouldn't be necessary though)
|
||||
if (out > 32767) out = 32767;
|
||||
#endif // SOUNDTOUCH_INTEGER_SAMPLES
|
||||
samples[i] = (SAMPLETYPE)out;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void BPMDetect::inputSamples(const SAMPLETYPE *samples, int numSamples)
|
||||
{
|
||||
SAMPLETYPE decimated[DECIMATED_BLOCK_SAMPLES];
|
||||
|
||||
// iterate so that max INPUT_BLOCK_SAMPLES processed per iteration
|
||||
while (numSamples > 0)
|
||||
{
|
||||
int block;
|
||||
int decSamples;
|
||||
|
||||
block = (numSamples > INPUT_BLOCK_SAMPLES) ? INPUT_BLOCK_SAMPLES : numSamples;
|
||||
|
||||
// decimate. note that converts to mono at the same time
|
||||
decSamples = decimate(decimated, samples, block);
|
||||
samples += block * channels;
|
||||
numSamples -= block;
|
||||
|
||||
// envelope new samples and add them to buffer
|
||||
calcEnvelope(decimated, decSamples);
|
||||
buffer->putSamples(decimated, decSamples);
|
||||
}
|
||||
|
||||
// when the buffer has enought samples for processing...
|
||||
if ((int)buffer->numSamples() > windowLen)
|
||||
{
|
||||
int processLength;
|
||||
|
||||
// how many samples are processed
|
||||
processLength = (int)buffer->numSamples() - windowLen;
|
||||
|
||||
// ... calculate autocorrelations for oldest samples...
|
||||
updateXCorr(processLength);
|
||||
// ... and remove them from the buffer
|
||||
buffer->receiveSamples(processLength);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void BPMDetect::removeBias()
|
||||
{
|
||||
int i;
|
||||
float minval = 1e12f; // arbitrary large number
|
||||
|
||||
for (i = windowStart; i < windowLen; i ++)
|
||||
{
|
||||
if (xcorr[i] < minval)
|
||||
{
|
||||
minval = xcorr[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (i = windowStart; i < windowLen; i ++)
|
||||
{
|
||||
xcorr[i] -= minval;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
float BPMDetect::getBpm()
|
||||
{
|
||||
double peakPos;
|
||||
double coeff;
|
||||
PeakFinder peakFinder;
|
||||
|
||||
coeff = 60.0 * ((double)sampleRate / (double)decimateBy);
|
||||
|
||||
// save bpm debug analysis data if debug data enabled
|
||||
_SaveDebugData(xcorr, windowStart, windowLen, coeff);
|
||||
|
||||
// remove bias from xcorr data
|
||||
removeBias();
|
||||
|
||||
// find peak position
|
||||
peakPos = peakFinder.detectPeak(xcorr, windowStart, windowLen);
|
||||
|
||||
assert(decimateBy != 0);
|
||||
if (peakPos < 1e-9) return 0.0; // detection failed.
|
||||
|
||||
// calculate BPM
|
||||
return (float) (coeff / peakPos);
|
||||
}
|
|
@ -14,10 +14,10 @@
|
|||
/// taking absolute value that's smoothed by sliding average. Signal levels that
|
||||
/// are below a couple of times the general RMS amplitude level are cut away to
|
||||
/// leave only notable peaks there.
|
||||
/// - Repeating sound patterns (e.g. beats) are detected by calculating short-term
|
||||
/// - Repeating sound patterns (e.g. beats) are detected by calculating short-term
|
||||
/// autocorrelation function of the enveloped signal.
|
||||
/// - After whole sound data file has been analyzed as above, the bpm level is
|
||||
/// detected by function 'getBpm' that finds the highest peak of the autocorrelation
|
||||
/// - After whole sound data file has been analyzed as above, the bpm level is
|
||||
/// detected by function 'getBpm' that finds the highest peak of the autocorrelation
|
||||
/// function, calculates it's precise location and converts this reading to bpm's.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
|
@ -26,10 +26,10 @@
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2009-02-21 18:00:14 +0200 (Sat, 21 Feb 2009) $
|
||||
// Last changed : $Date: 2012-08-30 16:53:44 -0300 (qui, 30 ago 2012) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: BPMDetect.h 63 2009-02-21 16:00:14Z oparviai $
|
||||
// $Id: BPMDetect.h 150 2012-08-30 19:53:44Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -67,7 +67,7 @@ namespace soundtouch
|
|||
#define MIN_BPM 29
|
||||
|
||||
/// Maximum allowed BPM rate. Used to restrict accepted result below a reasonable limit.
|
||||
#define MAX_BPM 230
|
||||
#define MAX_BPM 200
|
||||
|
||||
|
||||
/// Class for calculating BPM rate for audio data.
|
||||
|
@ -76,12 +76,12 @@ class BPMDetect
|
|||
protected:
|
||||
/// Auto-correlation accumulator bins.
|
||||
float *xcorr;
|
||||
|
||||
|
||||
/// Amplitude envelope sliding average approximation level accumulator
|
||||
float envelopeAccu;
|
||||
double envelopeAccu;
|
||||
|
||||
/// RMS volume sliding average approximation level accumulator
|
||||
float RMSVolumeAccu;
|
||||
double RMSVolumeAccu;
|
||||
|
||||
/// Sample average counter.
|
||||
int decimateCount;
|
||||
|
@ -104,12 +104,12 @@ protected:
|
|||
/// Beginning of auto-correlation window: Autocorrelation isn't being updated for
|
||||
/// the first these many correlation bins.
|
||||
int windowStart;
|
||||
|
||||
|
||||
/// FIFO-buffer for decimated processing samples.
|
||||
soundtouch::FIFOSampleBuffer *buffer;
|
||||
|
||||
/// 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
|
||||
/// Updates auto-correlation function for given number of decimated samples that
|
||||
/// are read from the internal 'buffer' pipe (samples aren't removed from the pipe
|
||||
/// though).
|
||||
void updateXCorr(int process_samples /// How many samples are processed.
|
||||
);
|
||||
|
@ -128,6 +128,9 @@ protected:
|
|||
int numsamples ///< Number of samples in buffer
|
||||
);
|
||||
|
||||
/// remove constant bias from xcorr data
|
||||
void removeBias();
|
||||
|
||||
public:
|
||||
/// Constructor.
|
||||
BPMDetect(int numChannels, ///< Number of channels in sample data.
|
||||
|
@ -139,9 +142,9 @@ public:
|
|||
|
||||
/// Inputs a block of samples for analyzing: Envelopes the samples and then
|
||||
/// updates the autocorrelation estimation. When whole song data has been input
|
||||
/// in smaller blocks using this function, read the resulting bpm with 'getBpm'
|
||||
/// function.
|
||||
///
|
||||
/// in smaller blocks using this function, read the resulting bpm with 'getBpm'
|
||||
/// function.
|
||||
///
|
||||
/// Notice that data in 'samples' array can be disrupted in processing.
|
||||
void inputSamples(const soundtouch::SAMPLETYPE *samples, ///< Pointer to input/working data buffer
|
||||
int numSamples ///< Number of samples in buffer
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
/// 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
|
||||
/// the 'receiveSamples' function. The class automatically removes the
|
||||
/// outputted samples from the buffer, as well as grows the buffer size
|
||||
/// the 'receiveSamples' function. The class automatically removes the
|
||||
/// outputted samples from the buffer, as well as grows the buffer size
|
||||
/// whenever necessary.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
|
@ -15,10 +15,10 @@
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2009-02-27 19:24:42 +0200 (Fri, 27 Feb 2009) $
|
||||
// Last changed : $Date: 2012-11-08 16:53:01 -0200 (qui, 08 nov 2012) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: FIFOSampleBuffer.cpp 68 2009-02-27 17:24:42Z oparviai $
|
||||
// $Id: FIFOSampleBuffer.cpp 160 2012-11-08 18:53:01Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -47,7 +47,6 @@
|
|||
#include <memory.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "FIFOSampleBuffer.h"
|
||||
|
||||
|
@ -63,7 +62,7 @@ FIFOSampleBuffer::FIFOSampleBuffer(int numChannels)
|
|||
samplesInBuffer = 0;
|
||||
bufferPos = 0;
|
||||
channels = (uint)numChannels;
|
||||
ensureCapacity(32); // allocate initial capacity
|
||||
ensureCapacity(32); // allocate initial capacity
|
||||
}
|
||||
|
||||
|
||||
|
@ -89,11 +88,11 @@ void FIFOSampleBuffer::setChannels(int numChannels)
|
|||
|
||||
|
||||
// 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.
|
||||
void FIFOSampleBuffer::rewind()
|
||||
{
|
||||
if (buffer && bufferPos)
|
||||
if (buffer && bufferPos)
|
||||
{
|
||||
memmove(buffer, ptrBegin(), sizeof(SAMPLETYPE) * channels * samplesInBuffer);
|
||||
bufferPos = 0;
|
||||
|
@ -101,7 +100,7 @@ void FIFOSampleBuffer::rewind()
|
|||
}
|
||||
|
||||
|
||||
// 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.
|
||||
void FIFOSampleBuffer::putSamples(const SAMPLETYPE *samples, uint nSamples)
|
||||
{
|
||||
|
@ -114,7 +113,7 @@ void FIFOSampleBuffer::putSamples(const SAMPLETYPE *samples, uint nSamples)
|
|||
// samples.
|
||||
//
|
||||
// This function is used to update the number of samples in the sample buffer
|
||||
// when accessing the buffer directly with 'ptrEnd' function. Please be
|
||||
// when accessing the buffer directly with 'ptrEnd' function. Please be
|
||||
// careful though!
|
||||
void FIFOSampleBuffer::putSamples(uint nSamples)
|
||||
{
|
||||
|
@ -126,31 +125,31 @@ void FIFOSampleBuffer::putSamples(uint nSamples)
|
|||
}
|
||||
|
||||
|
||||
// Returns a pointer to the end of the used part of the sample buffer (i.e.
|
||||
// where the new samples are to be inserted). This function may be used for
|
||||
// inserting new samples into the sample buffer directly. Please be careful!
|
||||
// Returns a pointer to the end of the used part of the sample buffer (i.e.
|
||||
// where the new samples are to be inserted). This function may be used for
|
||||
// inserting new samples into the sample buffer directly. Please be careful!
|
||||
//
|
||||
// Parameter 'slackCapacity' tells the function how much free capacity (in
|
||||
// terms of samples) there _at least_ should be, in order to the caller to
|
||||
// succesfully insert all the required samples to the buffer. When necessary,
|
||||
// succesfully insert all the required samples to the buffer. When necessary,
|
||||
// the function grows the buffer size to comply with this requirement.
|
||||
//
|
||||
// When using this function as means for inserting new samples, also remember
|
||||
// to increase the sample count afterwards, by calling the
|
||||
// When using this function as means for inserting new samples, also remember
|
||||
// to increase the sample count afterwards, by calling the
|
||||
// 'putSamples(numSamples)' function.
|
||||
SAMPLETYPE *FIFOSampleBuffer::ptrEnd(uint slackCapacity)
|
||||
SAMPLETYPE *FIFOSampleBuffer::ptrEnd(uint slackCapacity)
|
||||
{
|
||||
ensureCapacity(samplesInBuffer + slackCapacity);
|
||||
return buffer + samplesInBuffer * channels;
|
||||
}
|
||||
|
||||
|
||||
// Returns a pointer to the beginning of the currently non-outputted samples.
|
||||
// This function is provided for accessing the output samples directly.
|
||||
// Returns a pointer to the beginning of the currently non-outputted samples.
|
||||
// This function is provided for accessing the output samples directly.
|
||||
// Please be careful!
|
||||
//
|
||||
// When using this function to output samples, also remember to 'remove' the
|
||||
// outputted samples from the buffer by calling the
|
||||
// outputted samples from the buffer by calling the
|
||||
// 'receiveSamples(numSamples)' function
|
||||
SAMPLETYPE *FIFOSampleBuffer::ptrBegin()
|
||||
{
|
||||
|
@ -167,7 +166,7 @@ void FIFOSampleBuffer::ensureCapacity(uint capacityRequirement)
|
|||
{
|
||||
SAMPLETYPE *tempUnaligned, *temp;
|
||||
|
||||
if (capacityRequirement > getCapacity())
|
||||
if (capacityRequirement > getCapacity())
|
||||
{
|
||||
// enlarge the buffer in 4kbyte steps (round up to next 4k boundary)
|
||||
sizeInBytes = (capacityRequirement * channels * sizeof(SAMPLETYPE) + 4095) & (uint)-4096;
|
||||
|
@ -175,10 +174,10 @@ void FIFOSampleBuffer::ensureCapacity(uint capacityRequirement)
|
|||
tempUnaligned = new SAMPLETYPE[sizeInBytes / sizeof(SAMPLETYPE) + 16 / sizeof(SAMPLETYPE)];
|
||||
if (tempUnaligned == NULL)
|
||||
{
|
||||
throw std::runtime_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
|
||||
temp = (SAMPLETYPE *)(((ulong)tempUnaligned + 15) & (ulong)-16);
|
||||
temp = (SAMPLETYPE *)SOUNDTOUCH_ALIGN_POINTER_16(tempUnaligned);
|
||||
if (samplesInBuffer)
|
||||
{
|
||||
memcpy(temp, ptrBegin(), samplesInBuffer * channels * sizeof(SAMPLETYPE));
|
||||
|
@ -187,8 +186,8 @@ void FIFOSampleBuffer::ensureCapacity(uint capacityRequirement)
|
|||
buffer = temp;
|
||||
bufferUnaligned = tempUnaligned;
|
||||
bufferPos = 0;
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
{
|
||||
// simply rewind the buffer (if necessary)
|
||||
rewind();
|
||||
|
@ -260,3 +259,16 @@ void FIFOSampleBuffer::clear()
|
|||
samplesInBuffer = 0;
|
||||
bufferPos = 0;
|
||||
}
|
||||
|
||||
|
||||
/// allow trimming (downwards) amount of samples in pipeline.
|
||||
/// Returns adjusted amount of samples
|
||||
uint FIFOSampleBuffer::adjustAmountOfSamples(uint numSamples)
|
||||
{
|
||||
if (numSamples < samplesInBuffer)
|
||||
{
|
||||
samplesInBuffer = numSamples;
|
||||
}
|
||||
return samplesInBuffer;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
/// 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
|
||||
/// the 'receiveSamples' function. The class automatically removes the
|
||||
/// output samples from the buffer as well as grows the storage size
|
||||
/// the 'receiveSamples' function. The class automatically removes the
|
||||
/// output samples from the buffer as well as grows the storage size
|
||||
/// whenever necessary.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
|
@ -15,10 +15,10 @@
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2009-02-21 18:00:14 +0200 (Sat, 21 Feb 2009) $
|
||||
// Last changed : $Date: 2012-06-13 16:29:53 -0300 (qua, 13 jun 2012) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: FIFOSampleBuffer.h 63 2009-02-21 16:00:14Z oparviai $
|
||||
// $Id: FIFOSampleBuffer.h 143 2012-06-13 19:29:53Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -54,7 +54,7 @@ namespace soundtouch
|
|||
/// Sample buffer working in FIFO (first-in-first-out) principle. The class takes
|
||||
/// care of storage size adjustment and data moving during input/output operations.
|
||||
///
|
||||
/// Notice that in case of stereo audio, one sample is considered to consist of
|
||||
/// Notice that in case of stereo audio, one sample is considered to consist of
|
||||
/// both channel data.
|
||||
class FIFOSampleBuffer : public FIFOSamplePipe
|
||||
{
|
||||
|
@ -75,12 +75,12 @@ private:
|
|||
/// Channels, 1=mono, 2=stereo.
|
||||
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)
|
||||
/// only new data when is put to the pipe.
|
||||
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.
|
||||
void rewind();
|
||||
|
||||
|
@ -100,27 +100,27 @@ public:
|
|||
/// destructor
|
||||
~FIFOSampleBuffer();
|
||||
|
||||
/// Returns a pointer to the beginning of the output samples.
|
||||
/// This function is provided for accessing the output samples directly.
|
||||
/// Returns a pointer to the beginning of the output samples.
|
||||
/// This function is provided for accessing the output samples directly.
|
||||
/// Please be careful for not to corrupt the book-keeping!
|
||||
///
|
||||
/// When using this function to output samples, also remember to 'remove' the
|
||||
/// output samples from the buffer by calling the
|
||||
/// output samples from the buffer by calling the
|
||||
/// 'receiveSamples(numSamples)' function
|
||||
virtual SAMPLETYPE *ptrBegin();
|
||||
|
||||
/// Returns a pointer to the end of the used part of the sample buffer (i.e.
|
||||
/// where the new samples are to be inserted). This function may be used for
|
||||
/// Returns a pointer to the end of the used part of the sample buffer (i.e.
|
||||
/// where the new samples are to be inserted). This function may be used for
|
||||
/// inserting new samples into the sample buffer directly. Please be careful
|
||||
/// not corrupt the book-keeping!
|
||||
///
|
||||
/// When using this function as means for inserting new samples, also remember
|
||||
/// to increase the sample count afterwards, by calling the
|
||||
/// When using this function as means for inserting new samples, also remember
|
||||
/// to increase the sample count afterwards, by calling the
|
||||
/// 'putSamples(numSamples)' function.
|
||||
SAMPLETYPE *ptrEnd(
|
||||
uint slackCapacity ///< How much free capacity (in samples) there _at least_
|
||||
///< should be so that the caller can succesfully insert the
|
||||
///< desired samples to the buffer. If necessary, the function
|
||||
uint slackCapacity ///< How much free capacity (in samples) there _at least_
|
||||
///< should be so that the caller can succesfully insert the
|
||||
///< desired samples to the buffer. If necessary, the function
|
||||
///< grows the buffer size to comply with this requirement.
|
||||
);
|
||||
|
||||
|
@ -130,17 +130,17 @@ public:
|
|||
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.
|
||||
///
|
||||
/// 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!
|
||||
virtual void putSamples(uint numSamples ///< Number of samples been inserted.
|
||||
);
|
||||
|
||||
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
||||
/// output buffer and removes them from the sample buffer. If there are less than
|
||||
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
||||
/// output buffer and removes them from the sample buffer. If there are less than
|
||||
/// 'numsample' samples in the buffer, returns all that available.
|
||||
///
|
||||
/// \return Number of samples returned.
|
||||
|
@ -148,8 +148,8 @@ public:
|
|||
uint maxSamples ///< How many samples to receive at max.
|
||||
);
|
||||
|
||||
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
||||
/// sample buffer without copying them anywhere.
|
||||
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
||||
/// sample buffer without copying them anywhere.
|
||||
///
|
||||
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
|
||||
/// with 'ptrBegin' function.
|
||||
|
@ -167,6 +167,10 @@ public:
|
|||
|
||||
/// Clears all the samples.
|
||||
virtual void clear();
|
||||
|
||||
/// allow trimming (downwards) amount of samples in pipeline.
|
||||
/// Returns adjusted amount of samples
|
||||
uint adjustAmountOfSamples(uint numSamples);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
/// into one end of the pipe with the 'putSamples' function, and the processed
|
||||
/// samples are received from the other end with the 'receiveSamples' function.
|
||||
///
|
||||
/// 'FIFOProcessor' : A base class for classes the do signal processing with
|
||||
/// 'FIFOProcessor' : A base class for classes the do signal processing with
|
||||
/// the samples while operating like a first-in-first-out pipe. When samples
|
||||
/// are input with the 'putSamples' function, the class processes them
|
||||
/// and moves the processed samples to the given 'output' pipe object, which
|
||||
|
@ -17,10 +17,10 @@
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2009-04-13 16:18:48 +0300 (Mon, 13 Apr 2009) $
|
||||
// Last changed : $Date: 2012-06-13 16:29:53 -0300 (qua, 13 jun 2012) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: FIFOSamplePipe.h 69 2009-04-13 13:18:48Z oparviai $
|
||||
// $Id: FIFOSamplePipe.h 143 2012-06-13 19:29:53Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -63,12 +63,12 @@ public:
|
|||
virtual ~FIFOSamplePipe() {}
|
||||
|
||||
|
||||
/// Returns a pointer to the beginning of the output samples.
|
||||
/// This function is provided for accessing the output samples directly.
|
||||
/// Returns a pointer to the beginning of the output samples.
|
||||
/// This function is provided for accessing the output samples directly.
|
||||
/// Please be careful for not to corrupt the book-keeping!
|
||||
///
|
||||
/// When using this function to output samples, also remember to 'remove' the
|
||||
/// output samples from the buffer by calling the
|
||||
/// output samples from the buffer by calling the
|
||||
/// 'receiveSamples(numSamples)' function
|
||||
virtual SAMPLETYPE *ptrBegin() = 0;
|
||||
|
||||
|
@ -89,8 +89,8 @@ public:
|
|||
other.receiveSamples(oNumSamples);
|
||||
};
|
||||
|
||||
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
||||
/// output buffer and removes them from the sample buffer. If there are less than
|
||||
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
||||
/// output buffer and removes them from the sample buffer. If there are less than
|
||||
/// 'numsample' samples in the buffer, returns all that available.
|
||||
///
|
||||
/// \return Number of samples returned.
|
||||
|
@ -98,8 +98,8 @@ public:
|
|||
uint maxSamples ///< How many samples to receive at max.
|
||||
) = 0;
|
||||
|
||||
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
||||
/// sample buffer without copying them anywhere.
|
||||
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
||||
/// sample buffer without copying them anywhere.
|
||||
///
|
||||
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
|
||||
/// with 'ptrBegin' function.
|
||||
|
@ -114,16 +114,21 @@ public:
|
|||
|
||||
/// Clears all the samples.
|
||||
virtual void clear() = 0;
|
||||
|
||||
/// allow trimming (downwards) amount of samples in pipeline.
|
||||
/// Returns adjusted amount of samples
|
||||
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,
|
||||
/// 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.
|
||||
///
|
||||
/// 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
|
||||
/// either other processing stage or a FIFO sample buffer.
|
||||
class FIFOProcessor :public FIFOSamplePipe
|
||||
|
@ -141,7 +146,7 @@ protected:
|
|||
}
|
||||
|
||||
|
||||
/// 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.
|
||||
FIFOProcessor()
|
||||
{
|
||||
|
@ -163,12 +168,12 @@ protected:
|
|||
}
|
||||
|
||||
|
||||
/// Returns a pointer to the beginning of the output samples.
|
||||
/// This function is provided for accessing the output samples directly.
|
||||
/// Returns a pointer to the beginning of the output samples.
|
||||
/// This function is provided for accessing the output samples directly.
|
||||
/// Please be careful for not to corrupt the book-keeping!
|
||||
///
|
||||
/// When using this function to output samples, also remember to 'remove' the
|
||||
/// output samples from the buffer by calling the
|
||||
/// output samples from the buffer by calling the
|
||||
/// 'receiveSamples(numSamples)' function
|
||||
virtual SAMPLETYPE *ptrBegin()
|
||||
{
|
||||
|
@ -177,8 +182,8 @@ protected:
|
|||
|
||||
public:
|
||||
|
||||
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
||||
/// output buffer and removes them from the sample buffer. If there are less than
|
||||
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
||||
/// output buffer and removes them from the sample buffer. If there are less than
|
||||
/// 'numsample' samples in the buffer, returns all that available.
|
||||
///
|
||||
/// \return Number of samples returned.
|
||||
|
@ -190,8 +195,8 @@ public:
|
|||
}
|
||||
|
||||
|
||||
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
||||
/// sample buffer without copying them anywhere.
|
||||
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
||||
/// sample buffer without copying them anywhere.
|
||||
///
|
||||
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
|
||||
/// with 'ptrBegin' function.
|
||||
|
@ -214,6 +219,14 @@ public:
|
|||
{
|
||||
return output->isEmpty();
|
||||
}
|
||||
|
||||
/// allow trimming (downwards) amount of samples in pipeline.
|
||||
/// Returns adjusted amount of samples
|
||||
virtual uint adjustAmountOfSamples(uint numSamples)
|
||||
{
|
||||
return output->adjustAmountOfSamples(numSamples);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// 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'
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
|
@ -11,10 +11,10 @@
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2009-02-25 19:13:51 +0200 (Wed, 25 Feb 2009) $
|
||||
// Last changed : $Date: 2011-09-02 15:56:11 -0300 (sex, 02 set 2011) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: FIRFilter.cpp 67 2009-02-25 17:13:51Z oparviai $
|
||||
// $Id: FIRFilter.cpp 131 2011-09-02 18:56:11Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -43,7 +43,6 @@
|
|||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdexcept>
|
||||
#include "FIRFilter.h"
|
||||
#include "cpu_detect.h"
|
||||
|
||||
|
@ -75,7 +74,7 @@ uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, ui
|
|||
{
|
||||
uint i, j, end;
|
||||
LONG_SAMPLETYPE suml, sumr;
|
||||
#ifdef FLOAT_SAMPLES
|
||||
#ifdef SOUNDTOUCH_FLOAT_SAMPLES
|
||||
// when using floating point samples, use a scaler instead of a divider
|
||||
// because division is much slower operation than multiplying.
|
||||
double dScaler = 1.0 / (double)resultDivider;
|
||||
|
@ -88,14 +87,14 @@ uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, ui
|
|||
|
||||
end = 2 * (numSamples - length);
|
||||
|
||||
for (j = 0; j < end; j += 2)
|
||||
for (j = 0; j < end; j += 2)
|
||||
{
|
||||
const SAMPLETYPE *ptr;
|
||||
|
||||
suml = sumr = 0;
|
||||
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
|
||||
suml += ptr[2 * i + 0] * filterCoeffs[i + 0] +
|
||||
|
@ -108,7 +107,7 @@ uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, ui
|
|||
ptr[2 * i + 7] * filterCoeffs[i + 3];
|
||||
}
|
||||
|
||||
#ifdef INTEGER_SAMPLES
|
||||
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||
suml >>= resultDivFactor;
|
||||
sumr >>= resultDivFactor;
|
||||
// saturate to 16 bit integer limits
|
||||
|
@ -118,7 +117,7 @@ uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, ui
|
|||
#else
|
||||
suml *= dScaler;
|
||||
sumr *= dScaler;
|
||||
#endif // INTEGER_SAMPLES
|
||||
#endif // SOUNDTOUCH_INTEGER_SAMPLES
|
||||
dest[j] = (SAMPLETYPE)suml;
|
||||
dest[j + 1] = (SAMPLETYPE)sumr;
|
||||
}
|
||||
|
@ -133,7 +132,7 @@ uint FIRFilter::evaluateFilterMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint
|
|||
{
|
||||
uint i, j, end;
|
||||
LONG_SAMPLETYPE sum;
|
||||
#ifdef FLOAT_SAMPLES
|
||||
#ifdef SOUNDTOUCH_FLOAT_SAMPLES
|
||||
// when using floating point samples, use a scaler instead of a divider
|
||||
// because division is much slower operation than multiplying.
|
||||
double dScaler = 1.0 / (double)resultDivider;
|
||||
|
@ -143,24 +142,24 @@ uint FIRFilter::evaluateFilterMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint
|
|||
assert(length != 0);
|
||||
|
||||
end = numSamples - length;
|
||||
for (j = 0; j < end; j ++)
|
||||
for (j = 0; j < end; j ++)
|
||||
{
|
||||
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
|
||||
sum += src[i + 0] * filterCoeffs[i + 0] +
|
||||
src[i + 1] * filterCoeffs[i + 1] +
|
||||
src[i + 2] * filterCoeffs[i + 2] +
|
||||
sum += src[i + 0] * filterCoeffs[i + 0] +
|
||||
src[i + 1] * filterCoeffs[i + 1] +
|
||||
src[i + 2] * filterCoeffs[i + 2] +
|
||||
src[i + 3] * filterCoeffs[i + 3];
|
||||
}
|
||||
#ifdef INTEGER_SAMPLES
|
||||
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||
sum >>= resultDivFactor;
|
||||
// saturate to 16 bit integer limits
|
||||
sum = (sum < -32768) ? -32768 : (sum > 32767) ? 32767 : sum;
|
||||
#else
|
||||
sum *= dScaler;
|
||||
#endif // INTEGER_SAMPLES
|
||||
#endif // SOUNDTOUCH_INTEGER_SAMPLES
|
||||
dest[j] = (SAMPLETYPE)sum;
|
||||
src ++;
|
||||
}
|
||||
|
@ -174,7 +173,7 @@ uint FIRFilter::evaluateFilterMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint
|
|||
void FIRFilter::setCoefficients(const SAMPLETYPE *coeffs, uint newLength, uint uResultDivFactor)
|
||||
{
|
||||
assert(newLength > 0);
|
||||
if (newLength % 8) throw std::runtime_error("FIR filter length not divisible by 8");
|
||||
if (newLength % 8) ST_THROW_RT_ERROR("FIR filter length not divisible by 8");
|
||||
|
||||
lengthDiv8 = newLength / 8;
|
||||
length = lengthDiv8 * 8;
|
||||
|
@ -196,9 +195,9 @@ uint FIRFilter::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.
|
||||
uint FIRFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels) const
|
||||
{
|
||||
|
@ -207,7 +206,7 @@ uint FIRFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSample
|
|||
assert(length > 0);
|
||||
assert(lengthDiv8 * 8 == length);
|
||||
if (numSamples < length) return 0;
|
||||
if (numChannels == 2)
|
||||
if (numChannels == 2)
|
||||
{
|
||||
return evaluateFilterStereo(dest, src, numSamples);
|
||||
} else {
|
||||
|
@ -217,13 +216,13 @@ uint FIRFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSample
|
|||
|
||||
|
||||
|
||||
// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
||||
// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
||||
// depending on if we'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!
|
||||
throw std::runtime_error("Error in FIRFilter::new: Don't use 'new FIRFilter', use 'newInstance' member instead!");
|
||||
return NULL;
|
||||
ST_THROW_RT_ERROR("Error in FIRFilter::new: Don't use 'new FIRFilter', use 'newInstance' member instead!");
|
||||
return newInstance();
|
||||
}
|
||||
|
||||
|
||||
|
@ -233,34 +232,25 @@ FIRFilter * FIRFilter::newInstance()
|
|||
|
||||
uExtensions = detectCPUextensions();
|
||||
|
||||
// Check if MMX/SSE/3DNow! instruction set extensions supported by CPU
|
||||
// Check if MMX/SSE instruction set extensions supported by CPU
|
||||
|
||||
#ifdef ALLOW_MMX
|
||||
#ifdef SOUNDTOUCH_ALLOW_MMX
|
||||
// MMX routines available only with integer sample types
|
||||
if (uExtensions & SUPPORT_MMX)
|
||||
{
|
||||
return ::new FIRFilterMMX;
|
||||
}
|
||||
else
|
||||
#endif // ALLOW_MMX
|
||||
#endif // SOUNDTOUCH_ALLOW_MMX
|
||||
|
||||
#ifdef ALLOW_SSE
|
||||
#ifdef SOUNDTOUCH_ALLOW_SSE
|
||||
if (uExtensions & SUPPORT_SSE)
|
||||
{
|
||||
// SSE support
|
||||
return ::new FIRFilterSSE;
|
||||
}
|
||||
else
|
||||
#endif // ALLOW_SSE
|
||||
|
||||
#ifdef ALLOW_3DNOW
|
||||
if (uExtensions & SUPPORT_3DNOW)
|
||||
{
|
||||
// 3DNow! support
|
||||
return ::new FIRFilter3DNow;
|
||||
}
|
||||
else
|
||||
#endif // ALLOW_3DNOW
|
||||
#endif // SOUNDTOUCH_ALLOW_SSE
|
||||
|
||||
{
|
||||
// ISA optimizations not supported, use plain C version
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// 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'
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
|
@ -11,10 +11,10 @@
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2009-02-21 18:00:14 +0200 (Sat, 21 Feb 2009) $
|
||||
// Last changed : $Date: 2011-02-13 17:13:57 -0200 (dom, 13 fev 2011) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: FIRFilter.h 63 2009-02-21 16:00:14Z oparviai $
|
||||
// $Id: FIRFilter.h 104 2011-02-13 19:13:57Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -48,11 +48,11 @@
|
|||
namespace soundtouch
|
||||
{
|
||||
|
||||
class FIRFilter
|
||||
class FIRFilter
|
||||
{
|
||||
protected:
|
||||
// Number of FIR filter taps
|
||||
uint length;
|
||||
uint length;
|
||||
// Number of FIR filter taps divided by 8
|
||||
uint lengthDiv8;
|
||||
|
||||
|
@ -65,44 +65,44 @@ protected:
|
|||
// Memory for filter coefficients
|
||||
SAMPLETYPE *filterCoeffs;
|
||||
|
||||
virtual uint evaluateFilterStereo(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
virtual uint evaluateFilterStereo(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
uint numSamples) const;
|
||||
virtual uint evaluateFilterMono(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
virtual uint evaluateFilterMono(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
uint numSamples) const;
|
||||
|
||||
public:
|
||||
FIRFilter();
|
||||
virtual ~FIRFilter();
|
||||
|
||||
/// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
||||
/// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
||||
/// depending on if we've a MMX-capable CPU available or not.
|
||||
static void * operator new(size_t s);
|
||||
|
||||
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.
|
||||
/// Note : The amount of outputted samples is by value of 'filter_length'
|
||||
/// smaller than the amount of input samples.
|
||||
///
|
||||
/// \return Number of samples copied to 'dest'.
|
||||
uint evaluate(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
uint numSamples,
|
||||
uint evaluate(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
uint numSamples,
|
||||
uint numChannels) const;
|
||||
|
||||
uint getLength() const;
|
||||
|
||||
virtual void setCoefficients(const SAMPLETYPE *coeffs,
|
||||
uint newLength,
|
||||
virtual void setCoefficients(const SAMPLETYPE *coeffs,
|
||||
uint newLength,
|
||||
uint uResultDivFactor);
|
||||
};
|
||||
|
||||
|
||||
// Optional subclasses that implement CPU-specific optimizations:
|
||||
|
||||
#ifdef ALLOW_MMX
|
||||
#ifdef SOUNDTOUCH_ALLOW_MMX
|
||||
|
||||
/// Class that implements MMX optimized functions exclusive for 16bit integer samples type.
|
||||
class FIRFilterMMX : public FIRFilter
|
||||
|
@ -119,29 +119,10 @@ public:
|
|||
virtual void setCoefficients(const short *coeffs, uint newLength, uint uResultDivFactor);
|
||||
};
|
||||
|
||||
#endif // ALLOW_MMX
|
||||
#endif // SOUNDTOUCH_ALLOW_MMX
|
||||
|
||||
|
||||
#ifdef ALLOW_3DNOW
|
||||
|
||||
/// Class that implements 3DNow! optimized functions exclusive for floating point samples type.
|
||||
class FIRFilter3DNow : public FIRFilter
|
||||
{
|
||||
protected:
|
||||
float *filterCoeffsUnalign;
|
||||
float *filterCoeffsAlign;
|
||||
|
||||
virtual uint evaluateFilterStereo(float *dest, const float *src, uint numSamples) const;
|
||||
public:
|
||||
FIRFilter3DNow();
|
||||
~FIRFilter3DNow();
|
||||
virtual void setCoefficients(const float *coeffs, uint newLength, uint uResultDivFactor);
|
||||
};
|
||||
|
||||
#endif // ALLOW_3DNOW
|
||||
|
||||
|
||||
#ifdef ALLOW_SSE
|
||||
#ifdef SOUNDTOUCH_ALLOW_SSE
|
||||
/// Class that implements SSE optimized functions exclusive for floating point samples type.
|
||||
class FIRFilterSSE : public FIRFilter
|
||||
{
|
||||
|
@ -157,7 +138,7 @@ public:
|
|||
virtual void setCoefficients(const float *coeffs, uint newLength, uint uResultDivFactor);
|
||||
};
|
||||
|
||||
#endif // ALLOW_SSE
|
||||
#endif // SOUNDTOUCH_ALLOW_SSE
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1,42 +1,71 @@
|
|||
## Process this file with automake to create Makefile.in
|
||||
##
|
||||
## $Id: Makefile.am,v 1.3 2006/02/05 18:33:34 Olli Exp $
|
||||
##
|
||||
## Copyright (C) 2003 - David W. Durham
|
||||
## $Id: Makefile.am 138 2012-04-01 20:00:09Z oparviai $
|
||||
##
|
||||
## This file is part of SoundTouch, an audio processing library for pitch/time adjustments
|
||||
##
|
||||
##
|
||||
## SoundTouch is free software; you can redistribute it and/or modify it under the
|
||||
## terms of the GNU General Public License as published by the Free Software
|
||||
## Foundation; either version 2 of the License, or (at your option) any later
|
||||
## version.
|
||||
##
|
||||
##
|
||||
## SoundTouch is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
## WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
## A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
##
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License along with
|
||||
## this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
## Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
|
||||
AUTOMAKE_OPTIONS = foreign
|
||||
|
||||
noinst_HEADERS=AAFilter.h cpu_detect.h FIRFilter.h RateTransposer.h TDStretch.h cpu_detect_x86_gcc.cpp
|
||||
noinst_LIBRARIES = libSoundTouch.a
|
||||
include $(top_srcdir)/config/am_include.mk
|
||||
|
||||
libSoundTouch_a_CXXFLAGS = -msse -mmmx
|
||||
libSoundTouch_a_CFLAGS = -msse -mmmx
|
||||
|
||||
#lib_LTLIBRARIES=libSoundTouch.la
|
||||
# the mmx_gcc.cpp and cpu_detect_x86_gcc.cpp may need to be conditionally included here from things discovered in configure.ac
|
||||
libSoundTouch_a_SOURCES=AAFilter.cpp FIRFilter.cpp FIFOSampleBuffer.cpp mmx_optimized.cpp sse_optimized.cpp \
|
||||
RateTransposer.cpp SoundTouch.cpp TDStretch.cpp WavFile.cpp cpu_detect_x86_gcc.cpp
|
||||
# set to something if you want other stuff to be included in the distribution tarball
|
||||
EXTRA_DIST=SoundTouch.dsp SoundTouch.dsw SoundTouch.sln SoundTouch.vcproj
|
||||
|
||||
noinst_HEADERS=AAFilter.h cpu_detect.h cpu_detect_x86.cpp FIRFilter.h RateTransposer.h TDStretch.h PeakFinder.h
|
||||
|
||||
lib_LTLIBRARIES=libSoundTouch.la
|
||||
#
|
||||
libSoundTouch_la_SOURCES=AAFilter.cpp FIRFilter.cpp FIFOSampleBuffer.cpp RateTransposer.cpp SoundTouch.cpp TDStretch.cpp cpu_detect_x86.cpp BPMDetect.cpp PeakFinder.cpp
|
||||
|
||||
|
||||
# Compiler flags
|
||||
AM_CXXFLAGS=-O3 -fcheck-new -I../../include
|
||||
|
||||
# Compile the files that need MMX and SSE individually.
|
||||
libSoundTouch_la_LIBADD=libSoundTouchMMX.la libSoundTouchSSE.la
|
||||
noinst_LTLIBRARIES=libSoundTouchMMX.la libSoundTouchSSE.la
|
||||
libSoundTouchMMX_la_SOURCES=mmx_optimized.cpp
|
||||
libSoundTouchSSE_la_SOURCES=sse_optimized.cpp
|
||||
|
||||
# We enable optimizations by default.
|
||||
# If MMX is supported compile with -mmmx.
|
||||
# Do not assume -msse is also supported.
|
||||
if HAVE_MMX
|
||||
libSoundTouchMMX_la_CXXFLAGS = -mmmx $(AM_CXXFLAGS)
|
||||
else
|
||||
libSoundTouchMMX_la_CXXFLAGS = $(AM_CXXFLAGS)
|
||||
endif
|
||||
|
||||
# We enable optimizations by default.
|
||||
# If SSE is supported compile with -msse.
|
||||
if HAVE_SSE
|
||||
libSoundTouchSSE_la_CXXFLAGS = -msse $(AM_CXXFLAGS)
|
||||
else
|
||||
libSoundTouchSSE_la_CXXFLAGS = $(AM_CXXFLAGS)
|
||||
endif
|
||||
|
||||
# Let the user disable optimizations if he wishes to.
|
||||
if !X86_OPTIMIZATIONS
|
||||
libSoundTouchMMX_la_CXXFLAGS = $(AM_CXXFLAGS)
|
||||
libSoundTouchSSE_la_CXXFLAGS = $(AM_CXXFLAGS)
|
||||
endif
|
||||
|
||||
# ??? test for -fcheck-new in configure.ac
|
||||
# other compiler flags to add
|
||||
AM_CXXFLAGS=-O3 -msse -fcheck-new
|
||||
#-I../../include
|
||||
|
||||
# other linking flags to add
|
||||
#libSoundTouch_la_LIBADD=
|
||||
|
||||
# noinst_LTLIBRARIES = libSoundTouchOpt.la
|
||||
# libSoundTouch_la_LIBADD = libSoundTouchOpt.la
|
||||
# libSoundTouchOpt_la_SOURCES = mmx_optimized.cpp sse_optimized.cpp
|
||||
# libSoundTouchOpt_la_CXXFLAGS = -O3 -msse -fcheck-new -I../../include
|
||||
|
|
|
@ -0,0 +1,276 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Peak detection routine.
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2012-12-28 17:52:47 -0200 (sex, 28 dez 2012) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: PeakFinder.cpp 164 2012-12-28 19:52:47Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "PeakFinder.h"
|
||||
|
||||
using namespace soundtouch;
|
||||
|
||||
#define max(x, y) (((x) > (y)) ? (x) : (y))
|
||||
|
||||
|
||||
PeakFinder::PeakFinder()
|
||||
{
|
||||
minPos = maxPos = 0;
|
||||
}
|
||||
|
||||
|
||||
// Finds real 'top' of a peak hump from neighnourhood of the given 'peakpos'.
|
||||
int PeakFinder::findTop(const float *data, int peakpos) const
|
||||
{
|
||||
int i;
|
||||
int start, end;
|
||||
float refvalue;
|
||||
|
||||
refvalue = data[peakpos];
|
||||
|
||||
// seek within ±10 points
|
||||
start = peakpos - 10;
|
||||
if (start < minPos) start = minPos;
|
||||
end = peakpos + 10;
|
||||
if (end > maxPos) end = maxPos;
|
||||
|
||||
for (i = start; i <= end; i ++)
|
||||
{
|
||||
if (data[i] > refvalue)
|
||||
{
|
||||
peakpos = i;
|
||||
refvalue = data[i];
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
return peakpos;
|
||||
}
|
||||
|
||||
|
||||
// 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
|
||||
// begin
|
||||
int PeakFinder::findGround(const float *data, int peakpos, int direction) const
|
||||
{
|
||||
int lowpos;
|
||||
int pos;
|
||||
int climb_count;
|
||||
float refvalue;
|
||||
float delta;
|
||||
|
||||
climb_count = 0;
|
||||
refvalue = data[peakpos];
|
||||
lowpos = peakpos;
|
||||
|
||||
pos = peakpos;
|
||||
|
||||
while ((pos > minPos+1) && (pos < maxPos-1))
|
||||
{
|
||||
int prevpos;
|
||||
|
||||
prevpos = pos;
|
||||
pos += direction;
|
||||
|
||||
// calculate derivate
|
||||
delta = data[pos] - data[prevpos];
|
||||
if (delta <= 0)
|
||||
{
|
||||
// going downhill, ok
|
||||
if (climb_count)
|
||||
{
|
||||
climb_count --; // decrease climb count
|
||||
}
|
||||
|
||||
// check if new minimum found
|
||||
if (data[pos] < refvalue)
|
||||
{
|
||||
// new minimum found
|
||||
lowpos = pos;
|
||||
refvalue = data[pos];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// going uphill, increase climbing counter
|
||||
climb_count ++;
|
||||
if (climb_count > 5) break; // we've been climbing too long => it's next uphill => quit
|
||||
}
|
||||
}
|
||||
return lowpos;
|
||||
}
|
||||
|
||||
|
||||
// Find offset where the value crosses the given level, when starting from 'peakpos' and
|
||||
// proceeds to direction defined in 'direction'
|
||||
int PeakFinder::findCrossingLevel(const float *data, float level, int peakpos, int direction) const
|
||||
{
|
||||
float peaklevel;
|
||||
int pos;
|
||||
|
||||
peaklevel = data[peakpos];
|
||||
assert(peaklevel >= level);
|
||||
pos = peakpos;
|
||||
while ((pos >= minPos) && (pos < maxPos))
|
||||
{
|
||||
if (data[pos + direction] < level) return pos; // crossing found
|
||||
pos += direction;
|
||||
}
|
||||
return -1; // not found
|
||||
}
|
||||
|
||||
|
||||
// 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
|
||||
{
|
||||
int i;
|
||||
float sum;
|
||||
float wsum;
|
||||
|
||||
sum = 0;
|
||||
wsum = 0;
|
||||
for (i = firstPos; i <= lastPos; i ++)
|
||||
{
|
||||
sum += (float)i * data[i];
|
||||
wsum += data[i];
|
||||
}
|
||||
|
||||
if (wsum < 1e-6) return 0;
|
||||
return sum / wsum;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// get exact center of peak near given position by calculating local mass of center
|
||||
double PeakFinder::getPeakCenter(const float *data, int peakpos) const
|
||||
{
|
||||
float peakLevel; // peak level
|
||||
int crosspos1, crosspos2; // position where the peak 'hump' crosses cutting level
|
||||
float cutLevel; // cutting value
|
||||
float groundLevel; // ground level of the peak
|
||||
int gp1, gp2; // bottom positions of the peak 'hump'
|
||||
|
||||
// find ground positions.
|
||||
gp1 = findGround(data, peakpos, -1);
|
||||
gp2 = findGround(data, peakpos, 1);
|
||||
|
||||
groundLevel = 0.5f * (data[gp1] + data[gp2]);
|
||||
peakLevel = data[peakpos];
|
||||
|
||||
// calculate 70%-level of the peak
|
||||
cutLevel = 0.70f * peakLevel + 0.30f * groundLevel;
|
||||
// find mid-level crossings
|
||||
crosspos1 = findCrossingLevel(data, cutLevel, peakpos, -1);
|
||||
crosspos2 = findCrossingLevel(data, cutLevel, peakpos, 1);
|
||||
|
||||
if ((crosspos1 < 0) || (crosspos2 < 0)) return 0; // no crossing, no peak..
|
||||
|
||||
// calculate mass center of the peak surroundings
|
||||
return calcMassCenter(data, crosspos1, crosspos2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
double PeakFinder::detectPeak(const float *data, int aminPos, int amaxPos)
|
||||
{
|
||||
|
||||
int i;
|
||||
int peakpos; // position of peak level
|
||||
double highPeak, peak;
|
||||
|
||||
this->minPos = aminPos;
|
||||
this->maxPos = amaxPos;
|
||||
|
||||
// find absolute peak
|
||||
peakpos = minPos;
|
||||
peak = data[minPos];
|
||||
for (i = minPos + 1; i < maxPos; i ++)
|
||||
{
|
||||
if (data[i] > peak)
|
||||
{
|
||||
peak = data[i];
|
||||
peakpos = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate exact location of the highest peak mass center
|
||||
highPeak = getPeakCenter(data, peakpos);
|
||||
peak = highPeak;
|
||||
|
||||
// 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
|
||||
// just a slightly higher than the true base
|
||||
|
||||
for (i = 3; i < 10; i ++)
|
||||
{
|
||||
double peaktmp, harmonic;
|
||||
int i1,i2;
|
||||
|
||||
harmonic = (double)i * 0.5;
|
||||
peakpos = (int)(highPeak / harmonic + 0.5f);
|
||||
if (peakpos < minPos) break;
|
||||
peakpos = findTop(data, peakpos); // seek true local maximum index
|
||||
if (peakpos == 0) continue; // no local max here
|
||||
|
||||
// calculate mass-center of possible harmonic peak
|
||||
peaktmp = getPeakCenter(data, peakpos);
|
||||
|
||||
// accept harmonic peak if
|
||||
// (a) it is found
|
||||
// (b) is within ±4% of the expected harmonic interval
|
||||
// (c) has at least half x-corr value of the max. peak
|
||||
|
||||
double diff = harmonic * peaktmp / highPeak;
|
||||
if ((diff < 0.96) || (diff > 1.04)) continue; // peak too afar from expected
|
||||
|
||||
// now compare to highest detected peak
|
||||
i1 = (int)(highPeak + 0.5);
|
||||
i2 = (int)(peaktmp + 0.5);
|
||||
if (data[i2] >= 0.4*data[i1])
|
||||
{
|
||||
// The harmonic is at least half as high primary peak,
|
||||
// thus use the harmonic peak instead
|
||||
peak = peaktmp;
|
||||
}
|
||||
}
|
||||
|
||||
return peak;
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2011-12-30 18:33:46 -0200 (sex, 30 dez 2011) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: PeakFinder.h 132 2011-12-30 20:33:46Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _PeakFinder_H_
|
||||
#define _PeakFinder_H_
|
||||
|
||||
namespace soundtouch
|
||||
{
|
||||
|
||||
class PeakFinder
|
||||
{
|
||||
protected:
|
||||
/// Min, max allowed peak positions within the data vector
|
||||
int minPos, maxPos;
|
||||
|
||||
/// Calculates the mass center between given vector items.
|
||||
double calcMassCenter(const float *data, ///< Data vector.
|
||||
int firstPos, ///< Index of first vector item beloging to the peak.
|
||||
int lastPos ///< Index of last vector item beloging to the peak.
|
||||
) const;
|
||||
|
||||
/// Finds the data vector index where the monotoniously decreasing signal crosses the
|
||||
/// given level.
|
||||
int findCrossingLevel(const float *data, ///< Data vector.
|
||||
float level, ///< Goal crossing level.
|
||||
int peakpos, ///< Peak position index within the data vector.
|
||||
int direction /// Direction where to proceed from the peak: 1 = right, -1 = left.
|
||||
) const;
|
||||
|
||||
// Finds real 'top' of a peak hump from neighnourhood of the given 'peakpos'.
|
||||
int findTop(const float *data, int peakpos) const;
|
||||
|
||||
|
||||
/// Finds the 'ground' level, i.e. smallest level between two neighbouring peaks, to right-
|
||||
/// or left-hand side of the given peak position.
|
||||
int findGround(const float *data, /// Data vector.
|
||||
int peakpos, /// Peak position index within the data vector.
|
||||
int direction /// Direction where to proceed from the peak: 1 = right, -1 = left.
|
||||
) const;
|
||||
|
||||
/// get exact center of peak near given position by calculating local mass of center
|
||||
double getPeakCenter(const float *data, int peakpos) const;
|
||||
|
||||
public:
|
||||
/// Constructor.
|
||||
PeakFinder();
|
||||
|
||||
/// Detect exact peak position of the data vector by finding the largest peak 'hump'
|
||||
/// and calculating the mass-center location of the 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
|
||||
/// to be at least 'maxPos' items long.
|
||||
int minPos, ///< Min allowed peak location within the vector data.
|
||||
int maxPos ///< Max allowed peak location within the vector data.
|
||||
);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // _PeakFinder_H_
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,6 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// 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-
|
||||
/// alias filtering should be quite adequate for this application)
|
||||
///
|
||||
|
@ -10,10 +10,10 @@
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2009-10-31 16:37:24 +0200 (Sat, 31 Oct 2009) $
|
||||
// Last changed : $Date: 2011-09-02 15:56:11 -0300 (sex, 02 set 2011) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: RateTransposer.cpp 74 2009-10-31 14:37:24Z oparviai $
|
||||
// $Id: RateTransposer.cpp 131 2011-09-02 18:56:11Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -42,11 +42,9 @@
|
|||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdexcept>
|
||||
#include "RateTransposer.h"
|
||||
#include "AAFilter.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace soundtouch;
|
||||
|
||||
|
||||
|
@ -61,18 +59,18 @@ protected:
|
|||
|
||||
virtual void resetRegisters();
|
||||
|
||||
virtual uint transposeStereo(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
virtual uint transposeStereo(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
uint numSamples);
|
||||
virtual uint transposeMono(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
virtual uint transposeMono(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
uint numSamples);
|
||||
|
||||
public:
|
||||
RateTransposerInteger();
|
||||
virtual ~RateTransposerInteger();
|
||||
|
||||
/// Sets new target rate. Normal rate = 1.0, smaller values represent slower
|
||||
/// Sets new target rate. Normal rate = 1.0, smaller values represent slower
|
||||
/// rate, larger faster rates.
|
||||
virtual void setRate(float newRate);
|
||||
|
||||
|
@ -89,11 +87,11 @@ protected:
|
|||
|
||||
virtual void resetRegisters();
|
||||
|
||||
virtual uint transposeStereo(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
virtual uint transposeStereo(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
uint numSamples);
|
||||
virtual uint transposeMono(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
virtual uint transposeMono(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
uint numSamples);
|
||||
|
||||
public:
|
||||
|
@ -104,18 +102,18 @@ public:
|
|||
|
||||
|
||||
|
||||
// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
||||
// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
||||
// depending on if we've a MMX/SSE/etc-capable CPU available or not.
|
||||
void * RateTransposer::operator new(size_t s)
|
||||
{
|
||||
throw runtime_error("Error in RateTransoser::new: don't use \"new TDStretch\" directly, use \"newInstance\" to create a new instance instead!");
|
||||
return NULL;
|
||||
ST_THROW_RT_ERROR("Error in RateTransoser::new: don't use \"new TDStretch\" directly, use \"newInstance\" to create a new instance instead!");
|
||||
return newInstance();
|
||||
}
|
||||
|
||||
|
||||
RateTransposer *RateTransposer::newInstance()
|
||||
{
|
||||
#ifdef INTEGER_SAMPLES
|
||||
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||
return ::new RateTransposerInteger;
|
||||
#else
|
||||
return ::new RateTransposerFloat;
|
||||
|
@ -165,7 +163,7 @@ AAFilter *RateTransposer::getAAFilter()
|
|||
|
||||
|
||||
|
||||
// Sets new target iRate. Normal iRate = 1.0, smaller values represent slower
|
||||
// Sets new target iRate. Normal iRate = 1.0, smaller values represent slower
|
||||
// iRate, larger faster iRates.
|
||||
void RateTransposer::setRate(float newRate)
|
||||
{
|
||||
|
@ -174,11 +172,11 @@ void RateTransposer::setRate(float newRate)
|
|||
fRate = newRate;
|
||||
|
||||
// design a new anti-alias filter
|
||||
if (newRate > 1.0f)
|
||||
if (newRate > 1.0f)
|
||||
{
|
||||
fCutoff = 0.5f / newRate;
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
{
|
||||
fCutoff = 0.5f * newRate;
|
||||
}
|
||||
|
@ -220,7 +218,7 @@ void RateTransposer::upsample(const SAMPLETYPE *src, uint nSamples)
|
|||
// If the parameter 'uRate' value is smaller than 'SCALE', first transpose
|
||||
// the samples and then apply the anti-alias filter to remove aliasing.
|
||||
|
||||
// First check that there's enough room in 'storeBuffer'
|
||||
// First check that there's enough room in 'storeBuffer'
|
||||
// (+16 is to reserve some slack in the destination buffer)
|
||||
sizeTemp = (uint)((float)nSamples / fRate + 16.0f);
|
||||
|
||||
|
@ -231,7 +229,7 @@ void RateTransposer::upsample(const SAMPLETYPE *src, uint nSamples)
|
|||
// Apply the anti-alias filter to samples in "store output", output the
|
||||
// result to "dest"
|
||||
num = storeBuffer.numSamples();
|
||||
count = pAAFilter->evaluate(outputBuffer.ptrEnd(num),
|
||||
count = pAAFilter->evaluate(outputBuffer.ptrEnd(num),
|
||||
storeBuffer.ptrBegin(), num, (uint)numChannels);
|
||||
outputBuffer.putSamples(count);
|
||||
|
||||
|
@ -253,13 +251,13 @@ void RateTransposer::downsample(const SAMPLETYPE *src, uint nSamples)
|
|||
// Add the new samples to the end of the storeBuffer
|
||||
storeBuffer.putSamples(src, nSamples);
|
||||
|
||||
// Anti-alias filter the samples to prevent folding and output the filtered
|
||||
// Anti-alias filter the samples to prevent folding and output the filtered
|
||||
// data to tempBuffer. Note : because of the FIR filter length, the
|
||||
// filtering routine takes in 'filter_length' more samples than it outputs.
|
||||
assert(tempBuffer.isEmpty());
|
||||
sizeTemp = storeBuffer.numSamples();
|
||||
|
||||
count = pAAFilter->evaluate(tempBuffer.ptrEnd(sizeTemp),
|
||||
count = pAAFilter->evaluate(tempBuffer.ptrEnd(sizeTemp),
|
||||
storeBuffer.ptrBegin(), sizeTemp, (uint)numChannels);
|
||||
|
||||
if (count == 0) return;
|
||||
|
@ -274,7 +272,7 @@ void RateTransposer::downsample(const SAMPLETYPE *src, uint nSamples)
|
|||
}
|
||||
|
||||
|
||||
// Transposes sample rate by applying anti-alias filter to prevent folding.
|
||||
// Transposes sample rate by applying anti-alias filter to prevent folding.
|
||||
// Returns amount of samples returned in the "dest" buffer.
|
||||
// The maximum amount of samples that can be returned at a time is set by
|
||||
// the 'set_returnBuffer_size' function.
|
||||
|
@ -288,7 +286,7 @@ void RateTransposer::processSamples(const SAMPLETYPE *src, uint nSamples)
|
|||
|
||||
// If anti-alias filter is turned off, simply transpose without applying
|
||||
// the filter
|
||||
if (bUseAAFilter == FALSE)
|
||||
if (bUseAAFilter == FALSE)
|
||||
{
|
||||
sizeReq = (uint)((float)nSamples / fRate + 1.0f);
|
||||
count = transpose(outputBuffer.ptrEnd(sizeReq), src, nSamples);
|
||||
|
@ -297,26 +295,26 @@ void RateTransposer::processSamples(const SAMPLETYPE *src, uint nSamples)
|
|||
}
|
||||
|
||||
// Transpose with anti-alias filter
|
||||
if (fRate < 1.0f)
|
||||
if (fRate < 1.0f)
|
||||
{
|
||||
upsample(src, nSamples);
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
{
|
||||
downsample(src, nSamples);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Transposes the sample rate of the given samples using linear interpolation.
|
||||
// Transposes the sample rate of the given samples using linear interpolation.
|
||||
// Returns the number of samples returned in the "dest" buffer
|
||||
inline uint RateTransposer::transpose(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples)
|
||||
{
|
||||
if (numChannels == 2)
|
||||
if (numChannels == 2)
|
||||
{
|
||||
return transposeStereo(dest, src, nSamples);
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
{
|
||||
return transposeMono(dest, src, nSamples);
|
||||
}
|
||||
|
@ -363,7 +361,7 @@ int RateTransposer::isEmpty() const
|
|||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// RateTransposerInteger - integer arithmetic implementation
|
||||
//
|
||||
//
|
||||
|
||||
/// fixed-point interpolation routine precision
|
||||
#define SCALE 65536
|
||||
|
@ -371,7 +369,7 @@ int RateTransposer::isEmpty() const
|
|||
// Constructor
|
||||
RateTransposerInteger::RateTransposerInteger() : RateTransposer()
|
||||
{
|
||||
// Notice: use local function calling syntax for sake of clarity,
|
||||
// Notice: use local function calling syntax for sake of clarity,
|
||||
// to indicate the fact that C++ constructor can't call virtual functions.
|
||||
RateTransposerInteger::resetRegisters();
|
||||
RateTransposerInteger::setRate(1.0f);
|
||||
|
@ -386,14 +384,14 @@ RateTransposerInteger::~RateTransposerInteger()
|
|||
void RateTransposerInteger::resetRegisters()
|
||||
{
|
||||
iSlopeCount = 0;
|
||||
sPrevSampleL =
|
||||
sPrevSampleL =
|
||||
sPrevSampleR = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Transposes the sample rate of the given samples using linear interpolation.
|
||||
// 'Mono' version of the routine. Returns the number of samples returned in
|
||||
// Transposes the sample rate of the given samples using linear interpolation.
|
||||
// 'Mono' version of the routine. Returns the number of samples returned in
|
||||
// the "dest" buffer
|
||||
uint RateTransposerInteger::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples)
|
||||
{
|
||||
|
@ -402,11 +400,11 @@ uint RateTransposerInteger::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *sr
|
|||
|
||||
if (nSamples == 0) return 0; // no samples, no work
|
||||
|
||||
used = 0;
|
||||
used = 0;
|
||||
i = 0;
|
||||
|
||||
// Process the last sample saved from the previous call first...
|
||||
while (iSlopeCount <= SCALE)
|
||||
while (iSlopeCount <= SCALE)
|
||||
{
|
||||
vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount);
|
||||
temp = vol1 * sPrevSampleL + iSlopeCount * src[0];
|
||||
|
@ -419,7 +417,7 @@ uint RateTransposerInteger::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *sr
|
|||
|
||||
while (1)
|
||||
{
|
||||
while (iSlopeCount > SCALE)
|
||||
while (iSlopeCount > SCALE)
|
||||
{
|
||||
iSlopeCount -= SCALE;
|
||||
used ++;
|
||||
|
@ -440,8 +438,8 @@ end:
|
|||
}
|
||||
|
||||
|
||||
// Transposes the sample rate of the given samples using linear interpolation.
|
||||
// 'Stereo' version of the routine. Returns the number of samples returned in
|
||||
// Transposes the sample rate of the given samples using linear interpolation.
|
||||
// 'Stereo' version of the routine. Returns the number of samples returned in
|
||||
// the "dest" buffer
|
||||
uint RateTransposerInteger::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples)
|
||||
{
|
||||
|
@ -450,11 +448,11 @@ uint RateTransposerInteger::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *
|
|||
|
||||
if (nSamples == 0) return 0; // no samples, no work
|
||||
|
||||
used = 0;
|
||||
used = 0;
|
||||
i = 0;
|
||||
|
||||
// Process the last sample saved from the sPrevSampleLious call first...
|
||||
while (iSlopeCount <= SCALE)
|
||||
while (iSlopeCount <= SCALE)
|
||||
{
|
||||
vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount);
|
||||
temp = vol1 * sPrevSampleL + iSlopeCount * src[0];
|
||||
|
@ -469,7 +467,7 @@ uint RateTransposerInteger::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *
|
|||
|
||||
while (1)
|
||||
{
|
||||
while (iSlopeCount > SCALE)
|
||||
while (iSlopeCount > SCALE)
|
||||
{
|
||||
iSlopeCount -= SCALE;
|
||||
used ++;
|
||||
|
@ -494,7 +492,7 @@ end:
|
|||
}
|
||||
|
||||
|
||||
// Sets new target iRate. Normal iRate = 1.0, smaller values represent slower
|
||||
// Sets new target iRate. Normal iRate = 1.0, smaller values represent slower
|
||||
// iRate, larger faster iRates.
|
||||
void RateTransposerInteger::setRate(float newRate)
|
||||
{
|
||||
|
@ -506,13 +504,13 @@ void RateTransposerInteger::setRate(float newRate)
|
|||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// RateTransposerFloat - floating point arithmetic implementation
|
||||
//
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Constructor
|
||||
RateTransposerFloat::RateTransposerFloat() : RateTransposer()
|
||||
{
|
||||
// Notice: use local function calling syntax for sake of clarity,
|
||||
// Notice: use local function calling syntax for sake of clarity,
|
||||
// to indicate the fact that C++ constructor can't call virtual functions.
|
||||
RateTransposerFloat::resetRegisters();
|
||||
RateTransposerFloat::setRate(1.0f);
|
||||
|
@ -527,24 +525,24 @@ RateTransposerFloat::~RateTransposerFloat()
|
|||
void RateTransposerFloat::resetRegisters()
|
||||
{
|
||||
fSlopeCount = 0;
|
||||
sPrevSampleL =
|
||||
sPrevSampleL =
|
||||
sPrevSampleR = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Transposes the sample rate of the given samples using linear interpolation.
|
||||
// 'Mono' version of the routine. Returns the number of samples returned in
|
||||
// Transposes the sample rate of the given samples using linear interpolation.
|
||||
// 'Mono' version of the routine. Returns the number of samples returned in
|
||||
// the "dest" buffer
|
||||
uint RateTransposerFloat::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples)
|
||||
{
|
||||
unsigned int i, used;
|
||||
|
||||
used = 0;
|
||||
used = 0;
|
||||
i = 0;
|
||||
|
||||
// Process the last sample saved from the previous call first...
|
||||
while (fSlopeCount <= 1.0f)
|
||||
while (fSlopeCount <= 1.0f)
|
||||
{
|
||||
dest[i] = (SAMPLETYPE)((1.0f - fSlopeCount) * sPrevSampleL + fSlopeCount * src[0]);
|
||||
i++;
|
||||
|
@ -556,7 +554,7 @@ uint RateTransposerFloat::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src,
|
|||
{
|
||||
while (1)
|
||||
{
|
||||
while (fSlopeCount > 1.0f)
|
||||
while (fSlopeCount > 1.0f)
|
||||
{
|
||||
fSlopeCount -= 1.0f;
|
||||
used ++;
|
||||
|
@ -575,8 +573,8 @@ end:
|
|||
}
|
||||
|
||||
|
||||
// Transposes the sample rate of the given samples using linear interpolation.
|
||||
// 'Mono' version of the routine. Returns the number of samples returned in
|
||||
// Transposes the sample rate of the given samples using linear interpolation.
|
||||
// 'Mono' version of the routine. Returns the number of samples returned in
|
||||
// the "dest" buffer
|
||||
uint RateTransposerFloat::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples)
|
||||
{
|
||||
|
@ -584,11 +582,11 @@ uint RateTransposerFloat::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *sr
|
|||
|
||||
if (nSamples == 0) return 0; // no samples, no work
|
||||
|
||||
used = 0;
|
||||
used = 0;
|
||||
i = 0;
|
||||
|
||||
// Process the last sample saved from the sPrevSampleLious call first...
|
||||
while (fSlopeCount <= 1.0f)
|
||||
while (fSlopeCount <= 1.0f)
|
||||
{
|
||||
dest[2 * i] = (SAMPLETYPE)((1.0f - fSlopeCount) * sPrevSampleL + fSlopeCount * src[0]);
|
||||
dest[2 * i + 1] = (SAMPLETYPE)((1.0f - fSlopeCount) * sPrevSampleR + fSlopeCount * src[1]);
|
||||
|
@ -602,7 +600,7 @@ uint RateTransposerFloat::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *sr
|
|||
{
|
||||
while (1)
|
||||
{
|
||||
while (fSlopeCount > 1.0f)
|
||||
while (fSlopeCount > 1.0f)
|
||||
{
|
||||
fSlopeCount -= 1.0f;
|
||||
used ++;
|
||||
|
@ -610,9 +608,9 @@ uint RateTransposerFloat::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *sr
|
|||
}
|
||||
srcPos = 2 * used;
|
||||
|
||||
dest[2 * i] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[srcPos]
|
||||
dest[2 * i] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[srcPos]
|
||||
+ fSlopeCount * src[srcPos + 2]);
|
||||
dest[2 * i + 1] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[srcPos + 1]
|
||||
dest[2 * i + 1] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[srcPos + 1]
|
||||
+ fSlopeCount * src[srcPos + 3]);
|
||||
|
||||
i++;
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// 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-
|
||||
/// 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
|
||||
/// algorithm implementation.
|
||||
///
|
||||
|
@ -14,7 +14,7 @@
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2009-02-21 18:00:14 +0200 (Sat, 21 Feb 2009) $
|
||||
// Last changed : $Date: 2009-02-21 13:00:14 -0300 (sáb, 21 fev 2009) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: RateTransposer.h 63 2009-02-21 16:00:14Z oparviai $
|
||||
|
@ -57,9 +57,9 @@ namespace soundtouch
|
|||
|
||||
/// A common linear samplerate transposer class.
|
||||
///
|
||||
/// Note: Use function "RateTransposer::newInstance()" to create a new class
|
||||
/// instance instead of the "new" operator; that function automatically
|
||||
/// chooses a correct implementation depending on if integer or floating
|
||||
/// Note: Use function "RateTransposer::newInstance()" to create a new class
|
||||
/// instance instead of the "new" operator; that function automatically
|
||||
/// chooses a correct implementation depending on if integer or floating
|
||||
/// arithmetics are to be used.
|
||||
class RateTransposer : public FIFOProcessor
|
||||
{
|
||||
|
@ -85,26 +85,26 @@ protected:
|
|||
|
||||
virtual void resetRegisters() = 0;
|
||||
|
||||
virtual uint transposeStereo(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
virtual uint transposeStereo(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
uint numSamples) = 0;
|
||||
virtual uint transposeMono(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
virtual uint transposeMono(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
uint numSamples) = 0;
|
||||
inline uint transpose(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
inline uint transpose(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
uint numSamples);
|
||||
|
||||
void downsample(const SAMPLETYPE *src,
|
||||
void downsample(const SAMPLETYPE *src,
|
||||
uint numSamples);
|
||||
void upsample(const SAMPLETYPE *src,
|
||||
void upsample(const SAMPLETYPE *src,
|
||||
uint numSamples);
|
||||
|
||||
/// Transposes sample rate by applying anti-alias filter to prevent folding.
|
||||
/// Transposes sample rate by applying anti-alias filter to prevent folding.
|
||||
/// Returns amount of samples returned in the "dest" buffer.
|
||||
/// The maximum amount of samples that can be returned at a time is set by
|
||||
/// the 'set_returnBuffer_size' function.
|
||||
void processSamples(const SAMPLETYPE *src,
|
||||
void processSamples(const SAMPLETYPE *src,
|
||||
uint numSamples);
|
||||
|
||||
|
||||
|
@ -112,12 +112,12 @@ public:
|
|||
RateTransposer();
|
||||
virtual ~RateTransposer();
|
||||
|
||||
/// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
||||
/// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
||||
/// depending on if we're to use integer or floating point arithmetics.
|
||||
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.
|
||||
/// This function automatically chooses a correct implementation, depending on if
|
||||
/// integer ot floating point arithmetics are to be used.
|
||||
static RateTransposer *newInstance();
|
||||
|
||||
|
@ -136,7 +136,7 @@ public:
|
|||
/// Returns nonzero if anti-alias filter is enabled.
|
||||
BOOL isAAFilterEnabled() const;
|
||||
|
||||
/// Sets new target rate. Normal rate = 1.0, smaller values represent slower
|
||||
/// Sets new target rate. Normal rate = 1.0, smaller values represent slower
|
||||
/// rate, larger faster rates.
|
||||
virtual void setRate(float newRate);
|
||||
|
||||
|
|
|
@ -8,10 +8,10 @@
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2009-05-17 14:30:57 +0300 (Sun, 17 May 2009) $
|
||||
// Last changed : $Date: 2012-12-28 12:53:56 -0200 (sex, 28 dez 2012) $
|
||||
// File revision : $Revision: 3 $
|
||||
//
|
||||
// $Id: STTypes.h 70 2009-05-17 11:30:57Z oparviai $
|
||||
// $Id: STTypes.h 162 2012-12-28 14:53:56Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -42,8 +42,21 @@
|
|||
typedef unsigned int uint;
|
||||
typedef unsigned long ulong;
|
||||
|
||||
#ifdef __GNUC__
|
||||
// In GCC, include soundtouch_config.h made by config scritps
|
||||
// Patch for MinGW: on Win64 long is 32-bit
|
||||
#ifdef _WIN64
|
||||
typedef unsigned long long ulongptr;
|
||||
#else
|
||||
typedef ulong ulongptr;
|
||||
#endif
|
||||
|
||||
|
||||
// Helper macro for aligning pointer up to next 16-byte boundary
|
||||
#define SOUNDTOUCH_ALIGN_POINTER_16(x) ( ( (ulongptr)(x) + 15 ) & ~(ulongptr)15 )
|
||||
|
||||
|
||||
#if (defined(__GNUC__) && !defined(ANDROID))
|
||||
// In GCC, include soundtouch_config.h made by config scritps.
|
||||
// Skip this in Android compilation that uses GCC but without configure scripts.
|
||||
#include "soundtouch_config.h"
|
||||
#endif
|
||||
|
||||
|
@ -60,64 +73,81 @@ typedef unsigned long ulong;
|
|||
|
||||
namespace soundtouch
|
||||
{
|
||||
/// Activate these undef's to overrule the possible sampletype
|
||||
/// setting inherited from some other header file:
|
||||
//#undef SOUNDTOUCH_INTEGER_SAMPLES
|
||||
//#undef SOUNDTOUCH_FLOAT_SAMPLES
|
||||
|
||||
/// Activate these undef's to overrule the possible sampletype
|
||||
/// setting inherited from some other header file:
|
||||
//#undef INTEGER_SAMPLES
|
||||
//#undef FLOAT_SAMPLES
|
||||
#if (defined(__SOFTFP__))
|
||||
// For Android compilation: Force use of Integer samples in case that
|
||||
// compilation uses soft-floating point emulation - soft-fp is way too slow
|
||||
#undef SOUNDTOUCH_FLOAT_SAMPLES
|
||||
#define SOUNDTOUCH_INTEGER_SAMPLES 1
|
||||
#endif
|
||||
|
||||
#if !(INTEGER_SAMPLES || FLOAT_SAMPLES)
|
||||
#if !(SOUNDTOUCH_INTEGER_SAMPLES || SOUNDTOUCH_FLOAT_SAMPLES)
|
||||
|
||||
/// Choose either 32bit floating point or 16bit integer sampletype
|
||||
/// by choosing one of the following defines, unless this selection
|
||||
/// has already been done in some other file.
|
||||
////
|
||||
/// Notes:
|
||||
/// - In Windows environment, choose the sample format with the
|
||||
/// following defines.
|
||||
/// - In GNU environment, the floating point samples are used by
|
||||
/// default, but integer samples can be chosen by giving the
|
||||
/// following switch to the configure script:
|
||||
/// ./configure --enable-integer-samples
|
||||
/// However, if you still prefer to select the sample format here
|
||||
/// also in GNU environment, then please #undef the INTEGER_SAMPLE
|
||||
/// and FLOAT_SAMPLE defines first as in comments above.
|
||||
//#define SOUNDTOUCH_INTEGER_SAMPLES 1 //< 16bit integer samples
|
||||
#define SOUNDTOUCH_FLOAT_SAMPLES 1 //< 32bit float samples
|
||||
|
||||
#endif
|
||||
|
||||
/// Choose either 32bit floating point or 16bit integer sampletype
|
||||
/// by choosing one of the following defines, unless this selection
|
||||
/// has already been done in some other file.
|
||||
////
|
||||
/// Notes:
|
||||
/// - In Windows environment, choose the sample format with the
|
||||
/// following defines.
|
||||
/// - In GNU environment, the floating point samples are used by
|
||||
/// default, but integer samples can be chosen by giving the
|
||||
/// following switch to the configure script:
|
||||
/// ./configure --enable-integer-samples
|
||||
/// However, if you still prefer to select the sample format here
|
||||
/// also in GNU environment, then please #undef the INTEGER_SAMPLE
|
||||
/// and FLOAT_SAMPLE defines first as in comments above.
|
||||
//#define INTEGER_SAMPLES 1 //< 16bit integer samples
|
||||
#define FLOAT_SAMPLES 1 //< 32bit float samples
|
||||
|
||||
#endif
|
||||
|
||||
#if (WIN32 || __i386__ || __x86_64__)
|
||||
/// Define this to allow X86-specific assembler/intrinsic optimizations.
|
||||
#if (_M_IX86 || __i386__ || __x86_64__ || _M_X64)
|
||||
/// Define this to allow X86-specific assembler/intrinsic optimizations.
|
||||
/// Notice that library contains also usual C++ versions of each of these
|
||||
/// these routines, so if you're having difficulties getting the optimized
|
||||
/// routines compiled for whatever reason, you may disable these optimizations
|
||||
/// these routines, so if you're having difficulties getting the optimized
|
||||
/// routines compiled for whatever reason, you may disable these optimizations
|
||||
/// to make the library compile.
|
||||
|
||||
#define ALLOW_X86_OPTIMIZATIONS 1
|
||||
#define SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS 1
|
||||
|
||||
/// In GNU environment, allow the user to override this setting by
|
||||
/// giving the following switch to the configure script:
|
||||
/// ./configure --disable-x86-optimizations
|
||||
/// ./configure --enable-x86-optimizations=no
|
||||
#ifdef SOUNDTOUCH_DISABLE_X86_OPTIMIZATIONS
|
||||
#undef SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS
|
||||
#endif
|
||||
#else
|
||||
/// Always disable optimizations when not using a x86 systems.
|
||||
#undef SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS
|
||||
|
||||
#endif
|
||||
|
||||
// If defined, allows the SIMD-optimized routines to take minor shortcuts
|
||||
// for improved performance. Undefine to require faithfully similar SIMD
|
||||
// If defined, allows the SIMD-optimized routines to take minor shortcuts
|
||||
// for improved performance. Undefine to require faithfully similar SIMD
|
||||
// calculations as in normal C implementation.
|
||||
#define ALLOW_NONEXACT_SIMD_OPTIMIZATION 1
|
||||
#define SOUNDTOUCH_ALLOW_NONEXACT_SIMD_OPTIMIZATION 1
|
||||
|
||||
|
||||
#ifdef INTEGER_SAMPLES
|
||||
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||
// 16bit integer sample type
|
||||
typedef short SAMPLETYPE;
|
||||
// data type for sample accumulation: Use 32bit integer to prevent overflows
|
||||
typedef long LONG_SAMPLETYPE;
|
||||
|
||||
#ifdef FLOAT_SAMPLES
|
||||
#ifdef SOUNDTOUCH_FLOAT_SAMPLES
|
||||
// check that only one sample type is defined
|
||||
#error "conflicting sample types defined"
|
||||
#endif // FLOAT_SAMPLES
|
||||
#endif // SOUNDTOUCH_FLOAT_SAMPLES
|
||||
|
||||
#ifdef ALLOW_X86_OPTIMIZATIONS
|
||||
#ifdef SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS
|
||||
// Allow MMX optimizations
|
||||
#define ALLOW_MMX 1
|
||||
#define SOUNDTOUCH_ALLOW_MMX 1
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
@ -127,23 +157,31 @@ namespace soundtouch
|
|||
// data type for sample accumulation: Use double to utilize full precision.
|
||||
typedef double LONG_SAMPLETYPE;
|
||||
|
||||
#ifdef ALLOW_X86_OPTIMIZATIONS
|
||||
// Allow 3DNow! and SSE optimizations
|
||||
#if WIN32
|
||||
#define ALLOW_3DNOW 1
|
||||
#endif
|
||||
|
||||
#define ALLOW_SSE 1
|
||||
#ifdef SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS
|
||||
// Allow SSE optimizations
|
||||
#define SOUNDTOUCH_ALLOW_SSE 1
|
||||
#endif
|
||||
|
||||
#endif // INTEGER_SAMPLES
|
||||
#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.
|
||||
#include <assert.h>
|
||||
#define ST_THROW_RT_ERROR(x) {assert((const char *)x);}
|
||||
#else
|
||||
// use c++ standard exceptions
|
||||
#include <stdexcept>
|
||||
#define ST_THROW_RT_ERROR(x) {throw std::runtime_error(x);}
|
||||
#endif
|
||||
|
||||
// When this #define is active, eliminates a clicking sound when the "rate" or "pitch"
|
||||
// parameter setting crosses from value <1 to >=1 or vice versa during processing.
|
||||
// Default is off as such crossover is untypical case and involves a slight sound
|
||||
// When this #define is active, eliminates a clicking sound when the "rate" or "pitch"
|
||||
// parameter setting crosses from value <1 to >=1 or vice versa during processing.
|
||||
// Default is off as such crossover is untypical case and involves a slight sound
|
||||
// quality compromise.
|
||||
//#define PREVENT_CLICK_AT_RATE_CROSSOVER 1
|
||||
//#define SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER 1
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,27 +1,27 @@
|
|||
//////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// SoundTouch - main class for tempo/pitch/rate adjusting routines.
|
||||
/// SoundTouch - main class for tempo/pitch/rate adjusting routines.
|
||||
///
|
||||
/// Notes:
|
||||
/// - Initialize the SoundTouch object instance by setting up the sound stream
|
||||
/// parameters with functions 'setSampleRate' and 'setChannels', then set
|
||||
/// - Initialize the SoundTouch object instance by setting up the sound stream
|
||||
/// parameters with functions 'setSampleRate' and 'setChannels', then set
|
||||
/// desired tempo/pitch/rate settings with the corresponding functions.
|
||||
///
|
||||
/// - The SoundTouch class behaves like a first-in-first-out pipeline: The
|
||||
/// - The SoundTouch class behaves like a first-in-first-out pipeline: The
|
||||
/// samples that are to be processed are fed into one of the pipe by calling
|
||||
/// function 'putSamples', while the ready processed samples can be read
|
||||
/// function 'putSamples', while the ready processed samples can be read
|
||||
/// from the other end of the pipeline with function 'receiveSamples'.
|
||||
///
|
||||
/// - The SoundTouch processing classes require certain sized 'batches' of
|
||||
/// samples in order to process the sound. For this reason the classes buffer
|
||||
/// incoming samples until there are enough of samples available for
|
||||
///
|
||||
/// - The SoundTouch processing classes require certain sized 'batches' of
|
||||
/// samples in order to process the sound. For this reason the classes buffer
|
||||
/// incoming samples until there are enough of samples available for
|
||||
/// processing, then they carry out the processing step and consequently
|
||||
/// make the processed samples available for outputting.
|
||||
///
|
||||
/// - For the above reason, the processing routines introduce a certain
|
||||
///
|
||||
/// - For the above reason, the processing routines introduce a certain
|
||||
/// 'latency' between the input and output, so that the samples input to
|
||||
/// SoundTouch may not be immediately available in the output, and neither
|
||||
/// the amount of outputtable samples may not immediately be in direct
|
||||
/// SoundTouch may not be immediately available in the output, and neither
|
||||
/// the amount of outputtable samples may not immediately be in direct
|
||||
/// relationship with the amount of previously input samples.
|
||||
///
|
||||
/// - The tempo/pitch/rate control parameters can be altered during processing.
|
||||
|
@ -30,8 +30,8 @@
|
|||
/// required.
|
||||
///
|
||||
/// - This class utilizes classes 'TDStretch' for tempo change (without modifying
|
||||
/// pitch) and 'RateTransposer' for changing the playback rate (that is, both
|
||||
/// tempo and pitch in the same ratio) of the sound. The third available control
|
||||
/// pitch) and 'RateTransposer' for changing the playback rate (that is, both
|
||||
/// tempo and pitch in the same ratio) of the sound. The third available control
|
||||
/// 'pitch' (change pitch but maintain tempo) is produced by a combination of
|
||||
/// combining the two other controls.
|
||||
///
|
||||
|
@ -41,10 +41,10 @@
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2009-05-19 07:57:30 +0300 (Tue, 19 May 2009) $
|
||||
// Last changed : $Date: 2012-06-13 16:29:53 -0300 (qua, 13 jun 2012) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: SoundTouch.cpp 73 2009-05-19 04:57:30Z oparviai $
|
||||
// $Id: SoundTouch.cpp 143 2012-06-13 19:29:53Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -73,7 +73,6 @@
|
|||
#include <stdlib.h>
|
||||
#include <memory.h>
|
||||
#include <math.h>
|
||||
#include <stdexcept>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "SoundTouch.h"
|
||||
|
@ -82,7 +81,7 @@
|
|||
#include "cpu_detect.h"
|
||||
|
||||
using namespace soundtouch;
|
||||
|
||||
|
||||
/// test if two floating point numbers are equal
|
||||
#define TEST_FLOAT_EQUAL(a, b) (fabs(a - b) < 1e-10)
|
||||
|
||||
|
@ -91,7 +90,7 @@ using namespace soundtouch;
|
|||
extern "C" void soundtouch_ac_test()
|
||||
{
|
||||
printf("SoundTouch Version: %s\n",SOUNDTOUCH_VERSION);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SoundTouch::SoundTouch()
|
||||
|
@ -105,8 +104,8 @@ SoundTouch::SoundTouch()
|
|||
|
||||
rate = tempo = 0;
|
||||
|
||||
virtualPitch =
|
||||
virtualRate =
|
||||
virtualPitch =
|
||||
virtualRate =
|
||||
virtualTempo = 1.0;
|
||||
|
||||
calcEffectiveRateAndTempo();
|
||||
|
@ -144,9 +143,9 @@ uint SoundTouch::getVersionId()
|
|||
// Sets the number of channels, 1 = mono, 2 = stereo
|
||||
void SoundTouch::setChannels(uint numChannels)
|
||||
{
|
||||
if (numChannels != 1 && numChannels != 2)
|
||||
if (numChannels != 1 && numChannels != 2)
|
||||
{
|
||||
throw std::runtime_error("Illegal number of channels");
|
||||
ST_THROW_RT_ERROR("Illegal number of channels");
|
||||
}
|
||||
channels = numChannels;
|
||||
pRateTransposer->setChannels((int)numChannels);
|
||||
|
@ -243,10 +242,10 @@ void SoundTouch::calcEffectiveRateAndTempo()
|
|||
if (!TEST_FLOAT_EQUAL(rate,oldRate)) pRateTransposer->setRate(rate);
|
||||
if (!TEST_FLOAT_EQUAL(tempo, oldTempo)) pTDStretch->setTempo(tempo);
|
||||
|
||||
#ifndef PREVENT_CLICK_AT_RATE_CROSSOVER
|
||||
if (rate <= 1.0f)
|
||||
#ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
|
||||
if (rate <= 1.0f)
|
||||
{
|
||||
if (output != pTDStretch)
|
||||
if (output != pTDStretch)
|
||||
{
|
||||
FIFOSamplePipe *tempoOut;
|
||||
|
||||
|
@ -263,7 +262,7 @@ void SoundTouch::calcEffectiveRateAndTempo()
|
|||
else
|
||||
#endif
|
||||
{
|
||||
if (output != pRateTransposer)
|
||||
if (output != pRateTransposer)
|
||||
{
|
||||
FIFOSamplePipe *transOut;
|
||||
|
||||
|
@ -276,7 +275,7 @@ void SoundTouch::calcEffectiveRateAndTempo()
|
|||
|
||||
output = pRateTransposer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -293,42 +292,42 @@ void SoundTouch::setSampleRate(uint srate)
|
|||
// the input of the object.
|
||||
void SoundTouch::putSamples(const SAMPLETYPE *samples, uint nSamples)
|
||||
{
|
||||
if (bSrateSet == FALSE)
|
||||
if (bSrateSet == FALSE)
|
||||
{
|
||||
throw std::runtime_error("SoundTouch : Sample rate not defined");
|
||||
}
|
||||
else if (channels == 0)
|
||||
ST_THROW_RT_ERROR("SoundTouch : Sample rate not defined");
|
||||
}
|
||||
else if (channels == 0)
|
||||
{
|
||||
throw std::runtime_error("SoundTouch : Number of channels not defined");
|
||||
ST_THROW_RT_ERROR("SoundTouch : Number of channels not defined");
|
||||
}
|
||||
|
||||
// Transpose the rate of the new samples if necessary
|
||||
/* Bypass the nominal setting - can introduce a click in sound when tempo/pitch control crosses the nominal value...
|
||||
if (rate == 1.0f)
|
||||
if (rate == 1.0f)
|
||||
{
|
||||
// The rate value is same as the original, simply evaluate the tempo changer.
|
||||
// The rate value is same as the original, simply evaluate the tempo changer.
|
||||
assert(output == pTDStretch);
|
||||
if (pRateTransposer->isEmpty() == 0)
|
||||
if (pRateTransposer->isEmpty() == 0)
|
||||
{
|
||||
// yet flush the last samples in the pitch transposer buffer
|
||||
// (may happen if 'rate' changes from a non-zero value to zero)
|
||||
pTDStretch->moveSamples(*pRateTransposer);
|
||||
}
|
||||
pTDStretch->putSamples(samples, nSamples);
|
||||
}
|
||||
}
|
||||
*/
|
||||
#ifndef PREVENT_CLICK_AT_RATE_CROSSOVER
|
||||
else if (rate <= 1.0f)
|
||||
#ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
|
||||
else if (rate <= 1.0f)
|
||||
{
|
||||
// transpose the rate down, output the transposed sound to tempo changer buffer
|
||||
assert(output == pTDStretch);
|
||||
pRateTransposer->putSamples(samples, nSamples);
|
||||
pTDStretch->moveSamples(*pRateTransposer);
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
// evaluate the tempo changer, then transpose the rate up,
|
||||
// evaluate the tempo changer, then transpose the rate up,
|
||||
assert(output == pRateTransposer);
|
||||
pTDStretch->putSamples(samples, nSamples);
|
||||
pRateTransposer->moveSamples(*pTDStretch);
|
||||
|
@ -346,20 +345,36 @@ void SoundTouch::putSamples(const SAMPLETYPE *samples, uint nSamples)
|
|||
void SoundTouch::flush()
|
||||
{
|
||||
int i;
|
||||
uint nOut;
|
||||
SAMPLETYPE buff[128];
|
||||
int nUnprocessed;
|
||||
int nOut;
|
||||
SAMPLETYPE buff[64*2]; // note: allocate 2*64 to cater 64 sample frames of stereo sound
|
||||
|
||||
nOut = numSamples();
|
||||
// check how many samples still await processing, and scale
|
||||
// that by tempo & rate to get expected output sample count
|
||||
nUnprocessed = numUnprocessedSamples();
|
||||
nUnprocessed = (int)((double)nUnprocessed / (tempo * rate) + 0.5);
|
||||
|
||||
memset(buff, 0, 128 * sizeof(SAMPLETYPE));
|
||||
nOut = numSamples(); // ready samples currently in buffer ...
|
||||
nOut += nUnprocessed; // ... and how many we expect there to be in the end
|
||||
|
||||
memset(buff, 0, 64 * channels * sizeof(SAMPLETYPE));
|
||||
// "Push" the last active samples out from the processing pipeline by
|
||||
// feeding blank samples into the processing pipeline until new,
|
||||
// processed samples appear in the output (not however, more than
|
||||
// feeding blank samples into the processing pipeline until new,
|
||||
// processed samples appear in the output (not however, more than
|
||||
// 8ksamples in any case)
|
||||
for (i = 0; i < 128; i ++)
|
||||
for (i = 0; i < 128; i ++)
|
||||
{
|
||||
putSamples(buff, 64);
|
||||
if (numSamples() != nOut) break; // new samples have appeared in the output!
|
||||
if ((int)numSamples() >= nOut)
|
||||
{
|
||||
// Enough new samples have appeared into the output!
|
||||
// As samples come from processing with bigger chunks, now truncate it
|
||||
// back to maximum "nOut" samples to improve duration accuracy
|
||||
adjustAmountOfSamples(nOut);
|
||||
|
||||
// finish
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Clear working buffers
|
||||
|
@ -379,7 +394,7 @@ BOOL SoundTouch::setSetting(int settingId, int value)
|
|||
// read current tdstretch routine parameters
|
||||
pTDStretch->getParameters(&sampleRate, &sequenceMs, &seekWindowMs, &overlapMs);
|
||||
|
||||
switch (settingId)
|
||||
switch (settingId)
|
||||
{
|
||||
case SETTING_USE_AA_FILTER :
|
||||
// enables / disabless anti-alias filter
|
||||
|
@ -425,7 +440,7 @@ int SoundTouch::getSetting(int settingId) const
|
|||
{
|
||||
int temp;
|
||||
|
||||
switch (settingId)
|
||||
switch (settingId)
|
||||
{
|
||||
case SETTING_USE_AA_FILTER :
|
||||
return (uint)pRateTransposer->isAAFilterEnabled();
|
||||
|
@ -448,7 +463,13 @@ int SoundTouch::getSetting(int settingId) const
|
|||
pTDStretch->getParameters(NULL, NULL, NULL, &temp);
|
||||
return temp;
|
||||
|
||||
default :
|
||||
case SETTING_NOMINAL_INPUT_SEQUENCE :
|
||||
return pTDStretch->getInputSampleReq();
|
||||
|
||||
case SETTING_NOMINAL_OUTPUT_SEQUENCE :
|
||||
return pTDStretch->getOutputBatchSize();
|
||||
|
||||
default :
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,27 +1,27 @@
|
|||
//////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// SoundTouch - main class for tempo/pitch/rate adjusting routines.
|
||||
/// SoundTouch - main class for tempo/pitch/rate adjusting routines.
|
||||
///
|
||||
/// Notes:
|
||||
/// - Initialize the SoundTouch object instance by setting up the sound stream
|
||||
/// parameters with functions 'setSampleRate' and 'setChannels', then set
|
||||
/// - Initialize the SoundTouch object instance by setting up the sound stream
|
||||
/// parameters with functions 'setSampleRate' and 'setChannels', then set
|
||||
/// desired tempo/pitch/rate settings with the corresponding functions.
|
||||
///
|
||||
/// - The SoundTouch class behaves like a first-in-first-out pipeline: The
|
||||
/// - The SoundTouch class behaves like a first-in-first-out pipeline: The
|
||||
/// samples that are to be processed are fed into one of the pipe by calling
|
||||
/// function 'putSamples', while the ready processed samples can be read
|
||||
/// function 'putSamples', while the ready processed samples can be read
|
||||
/// from the other end of the pipeline with function 'receiveSamples'.
|
||||
///
|
||||
/// - The SoundTouch processing classes require certain sized 'batches' of
|
||||
/// samples in order to process the sound. For this reason the classes buffer
|
||||
/// incoming samples until there are enough of samples available for
|
||||
///
|
||||
/// - The SoundTouch processing classes require certain sized 'batches' of
|
||||
/// samples in order to process the sound. For this reason the classes buffer
|
||||
/// incoming samples until there are enough of samples available for
|
||||
/// processing, then they carry out the processing step and consequently
|
||||
/// make the processed samples available for outputting.
|
||||
///
|
||||
/// - For the above reason, the processing routines introduce a certain
|
||||
///
|
||||
/// - For the above reason, the processing routines introduce a certain
|
||||
/// 'latency' between the input and output, so that the samples input to
|
||||
/// SoundTouch may not be immediately available in the output, and neither
|
||||
/// the amount of outputtable samples may not immediately be in direct
|
||||
/// SoundTouch may not be immediately available in the output, and neither
|
||||
/// the amount of outputtable samples may not immediately be in direct
|
||||
/// relationship with the amount of previously input samples.
|
||||
///
|
||||
/// - The tempo/pitch/rate control parameters can be altered during processing.
|
||||
|
@ -30,8 +30,8 @@
|
|||
/// required.
|
||||
///
|
||||
/// - This class utilizes classes 'TDStretch' for tempo change (without modifying
|
||||
/// pitch) and 'RateTransposer' for changing the playback rate (that is, both
|
||||
/// tempo and pitch in the same ratio) of the sound. The third available control
|
||||
/// pitch) and 'RateTransposer' for changing the playback rate (that is, both
|
||||
/// tempo and pitch in the same ratio) of the sound. The third available control
|
||||
/// 'pitch' (change pitch but maintain tempo) is produced by a combination of
|
||||
/// combining the two other controls.
|
||||
///
|
||||
|
@ -41,10 +41,10 @@
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2009-12-28 22:10:14 +0200 (Mon, 28 Dec 2009) $
|
||||
// Last changed : $Date: 2012-12-28 17:32:59 -0200 (sex, 28 dez 2012) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: SoundTouch.h 78 2009-12-28 20:10:14Z oparviai $
|
||||
// $Id: SoundTouch.h 163 2012-12-28 19:32:59Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -79,10 +79,10 @@ namespace soundtouch
|
|||
{
|
||||
|
||||
/// Soundtouch library version string
|
||||
#define SOUNDTOUCH_VERSION "1.5.0"
|
||||
#define SOUNDTOUCH_VERSION "1.7.1"
|
||||
|
||||
/// SoundTouch library version id
|
||||
#define SOUNDTOUCH_VERSION_ID (10500)
|
||||
#define SOUNDTOUCH_VERSION_ID (10701)
|
||||
|
||||
//
|
||||
// Available setting IDs for the 'setSetting' & 'get_setting' functions:
|
||||
|
@ -98,24 +98,49 @@ namespace soundtouch
|
|||
/// quality compromising)
|
||||
#define SETTING_USE_QUICKSEEK 2
|
||||
|
||||
/// Time-stretch algorithm single processing sequence length in milliseconds. This determines
|
||||
/// to how long sequences the original sound is chopped in the time-stretch algorithm.
|
||||
/// Time-stretch algorithm single processing sequence length in milliseconds. This determines
|
||||
/// to how long sequences the original sound is chopped in the time-stretch algorithm.
|
||||
/// See "STTypes.h" or README for more information.
|
||||
#define SETTING_SEQUENCE_MS 3
|
||||
|
||||
/// Time-stretch algorithm seeking window length in milliseconds for algorithm that finds the
|
||||
/// best possible overlapping location. This determines from how wide window the algorithm
|
||||
/// may look for an optimal joining location when mixing the sound sequences back together.
|
||||
/// Time-stretch algorithm seeking window length in milliseconds for algorithm that finds the
|
||||
/// best possible overlapping location. This determines from how wide window the algorithm
|
||||
/// may look for an optimal joining location when mixing the sound sequences back together.
|
||||
/// See "STTypes.h" or README for more information.
|
||||
#define SETTING_SEEKWINDOW_MS 4
|
||||
|
||||
/// Time-stretch algorithm overlap length in milliseconds. When the chopped sound sequences
|
||||
/// are mixed back together, to form a continuous sound stream, this parameter defines over
|
||||
/// how long period the two consecutive sequences are let to overlap each other.
|
||||
/// Time-stretch algorithm overlap length in milliseconds. When the chopped sound sequences
|
||||
/// are mixed back together, to form a continuous sound stream, this parameter defines over
|
||||
/// how long period the two consecutive sequences are let to overlap each other.
|
||||
/// See "STTypes.h" or README for more information.
|
||||
#define SETTING_OVERLAP_MS 5
|
||||
|
||||
|
||||
/// Call "getSetting" with this ID to query nominal average processing sequence
|
||||
/// 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.
|
||||
///
|
||||
/// Notices:
|
||||
/// - This is read-only parameter, i.e. setSetting ignores this parameter
|
||||
/// - Returned value is approximate average value, exact processing batch
|
||||
/// size may wary from time to time
|
||||
/// - This parameter value is not constant but may change depending on
|
||||
/// tempo/pitch/rate/samplerate settings.
|
||||
#define SETTING_NOMINAL_INPUT_SEQUENCE 6
|
||||
|
||||
|
||||
/// Call "getSetting" with this ID to query nominal average processing output
|
||||
/// 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.
|
||||
///
|
||||
/// Notices:
|
||||
/// - This is read-only parameter, i.e. setSetting ignores this parameter
|
||||
/// - Returned value is approximate average value, exact processing batch
|
||||
/// size may wary from time to time
|
||||
/// - This parameter value is not constant but may change depending on
|
||||
/// tempo/pitch/rate/samplerate settings.
|
||||
#define SETTING_NOMINAL_OUTPUT_SEQUENCE 7
|
||||
|
||||
class SoundTouch : public FIFOProcessor
|
||||
{
|
||||
private:
|
||||
|
@ -137,7 +162,7 @@ private:
|
|||
/// Flag: Has sample rate been set?
|
||||
BOOL bSrateSet;
|
||||
|
||||
/// Calculates effective rate & tempo valuescfrom 'virtualRate', 'virtualTempo' and
|
||||
/// Calculates effective rate & tempo valuescfrom 'virtualRate', 'virtualTempo' and
|
||||
/// 'virtualPitch' parameters.
|
||||
void calcEffectiveRateAndTempo();
|
||||
|
||||
|
@ -181,7 +206,7 @@ public:
|
|||
/// represent lower pitches, larger values higher pitch.
|
||||
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)
|
||||
void setPitchOctaves(float newPitch);
|
||||
|
||||
|
@ -221,7 +246,7 @@ public:
|
|||
|
||||
/// Changes a setting controlling the processing system behaviour. See the
|
||||
/// 'SETTING_...' defines for available setting ID's.
|
||||
///
|
||||
///
|
||||
/// \return 'TRUE' if the setting was succesfully changed
|
||||
BOOL setSetting(int settingId, ///< Setting ID number. see SETTING_... defines.
|
||||
int value ///< New setting value.
|
||||
|
@ -242,7 +267,7 @@ public:
|
|||
/// classes 'FIFOProcessor' and 'FIFOSamplePipe')
|
||||
///
|
||||
/// - receiveSamples() : Use this function to receive 'ready' processed samples from SoundTouch.
|
||||
/// - numSamples() : Get number of 'ready' samples that can be received with
|
||||
/// - numSamples() : Get number of 'ready' samples that can be received with
|
||||
/// function 'receiveSamples()'
|
||||
/// - isEmpty() : Returns nonzero if there aren't any 'ready' samples.
|
||||
/// - clear() : Clears all samples from ready/processing buffers.
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo
|
||||
/// while maintaining the original pitch by using a time domain WSOLA-like
|
||||
///
|
||||
/// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo
|
||||
/// while maintaining the original pitch by using a time domain WSOLA-like
|
||||
/// method with several performance-increasing tweaks.
|
||||
///
|
||||
/// Note : MMX optimized functions reside in a separate, platform-specific
|
||||
/// Note : MMX optimized functions reside in a separate, platform-specific
|
||||
/// file, e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
|
@ -13,10 +13,10 @@
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2009-12-28 21:27:04 +0200 (Mon, 28 Dec 2009) $
|
||||
// Last changed : $Date: 2012-11-08 16:53:01 -0200 (qui, 08 nov 2012) $
|
||||
// File revision : $Revision: 1.12 $
|
||||
//
|
||||
// $Id: TDStretch.cpp 77 2009-12-28 19:27:04Z oparviai $
|
||||
// $Id: TDStretch.cpp 160 2012-11-08 18:53:01Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -46,7 +46,6 @@
|
|||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "STTypes.h"
|
||||
#include "cpu_detect.h"
|
||||
|
@ -91,7 +90,7 @@ TDStretch::TDStretch() : FIFOProcessor(&outputBuffer)
|
|||
channels = 2;
|
||||
|
||||
pMidBuffer = NULL;
|
||||
pRefMidBufferUnaligned = NULL;
|
||||
pMidBufferUnaligned = NULL;
|
||||
overlapLength = 0;
|
||||
|
||||
bAutoSeqSetting = TRUE;
|
||||
|
@ -101,7 +100,7 @@ TDStretch::TDStretch() : FIFOProcessor(&outputBuffer)
|
|||
skipFract = 0;
|
||||
|
||||
tempo = 1.0f;
|
||||
setParameters(48000, DEFAULT_SEQUENCE_MS, DEFAULT_SEEKWINDOW_MS, DEFAULT_OVERLAP_MS);
|
||||
setParameters(44100, DEFAULT_SEQUENCE_MS, DEFAULT_SEEKWINDOW_MS, DEFAULT_OVERLAP_MS);
|
||||
setTempo(1.0f);
|
||||
|
||||
clear();
|
||||
|
@ -111,8 +110,7 @@ TDStretch::TDStretch() : FIFOProcessor(&outputBuffer)
|
|||
|
||||
TDStretch::~TDStretch()
|
||||
{
|
||||
delete[] pMidBuffer;
|
||||
delete[] pRefMidBufferUnaligned;
|
||||
delete[] pMidBufferUnaligned;
|
||||
}
|
||||
|
||||
|
||||
|
@ -122,11 +120,11 @@ TDStretch::~TDStretch()
|
|||
//
|
||||
// 'sampleRate' = sample rate of the sound
|
||||
// 'sequenceMS' = one processing sequence length in milliseconds (default = 82 ms)
|
||||
// 'seekwindowMS' = seeking window length for scanning the best overlapping
|
||||
// 'seekwindowMS' = seeking window length for scanning the best overlapping
|
||||
// position (default = 28 ms)
|
||||
// 'overlapMS' = overlapping length (default = 12 ms)
|
||||
|
||||
void TDStretch::setParameters(int aSampleRate, int aSequenceMS,
|
||||
void TDStretch::setParameters(int aSampleRate, int aSequenceMS,
|
||||
int aSeekWindowMS, int aOverlapMS)
|
||||
{
|
||||
// accept only positive parameter values - if zero or negative, use old values instead
|
||||
|
@ -137,19 +135,19 @@ void TDStretch::setParameters(int aSampleRate, int aSequenceMS,
|
|||
{
|
||||
this->sequenceMs = aSequenceMS;
|
||||
bAutoSeqSetting = FALSE;
|
||||
}
|
||||
}
|
||||
else if (aSequenceMS == 0)
|
||||
{
|
||||
// if zero, use automatic setting
|
||||
bAutoSeqSetting = TRUE;
|
||||
}
|
||||
|
||||
if (aSeekWindowMS > 0)
|
||||
if (aSeekWindowMS > 0)
|
||||
{
|
||||
this->seekWindowMs = aSeekWindowMS;
|
||||
bAutoSeekSetting = FALSE;
|
||||
}
|
||||
else if (aSeekWindowMS == 0)
|
||||
}
|
||||
else if (aSeekWindowMS == 0)
|
||||
{
|
||||
// if zero, use automatic setting
|
||||
bAutoSeekSetting = TRUE;
|
||||
|
@ -196,12 +194,17 @@ void TDStretch::getParameters(int *pSampleRate, int *pSequenceMs, int *pSeekWind
|
|||
// Overlaps samples in 'midBuffer' with the samples in 'pInput'
|
||||
void TDStretch::overlapMono(SAMPLETYPE *pOutput, const SAMPLETYPE *pInput) const
|
||||
{
|
||||
int i, itemp;
|
||||
int i;
|
||||
SAMPLETYPE m1, m2;
|
||||
|
||||
for (i = 0; i < overlapLength ; i ++)
|
||||
m1 = (SAMPLETYPE)0;
|
||||
m2 = (SAMPLETYPE)overlapLength;
|
||||
|
||||
for (i = 0; i < overlapLength ; i ++)
|
||||
{
|
||||
itemp = overlapLength - i;
|
||||
pOutput[i] = (pInput[i] * i + pMidBuffer[i] * itemp ) / overlapLength; // >> overlapDividerBits;
|
||||
pOutput[i] = (pInput[i] * m1 + pMidBuffer[i] * m2 ) / overlapLength;
|
||||
m1 += 1;
|
||||
m2 -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -247,40 +250,22 @@ BOOL TDStretch::isQuickSeekEnabled() const
|
|||
// Seeks for the optimal overlap-mixing position.
|
||||
int TDStretch::seekBestOverlapPosition(const SAMPLETYPE *refPos)
|
||||
{
|
||||
if (channels == 2)
|
||||
if (bQuickSeek)
|
||||
{
|
||||
// stereo sound
|
||||
if (bQuickSeek)
|
||||
{
|
||||
return seekBestOverlapPositionStereoQuick(refPos);
|
||||
}
|
||||
else
|
||||
{
|
||||
return seekBestOverlapPositionStereo(refPos);
|
||||
}
|
||||
}
|
||||
else
|
||||
return seekBestOverlapPositionQuick(refPos);
|
||||
}
|
||||
else
|
||||
{
|
||||
// mono sound
|
||||
if (bQuickSeek)
|
||||
{
|
||||
return seekBestOverlapPositionMonoQuick(refPos);
|
||||
}
|
||||
else
|
||||
{
|
||||
return seekBestOverlapPositionMono(refPos);
|
||||
}
|
||||
return seekBestOverlapPositionFull(refPos);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Overlaps samples in 'midBuffer' with the samples in 'pInputBuffer' at position
|
||||
// of 'ovlPos'.
|
||||
inline void TDStretch::overlap(SAMPLETYPE *pOutput, const SAMPLETYPE *pInput, uint ovlPos) const
|
||||
{
|
||||
if (channels == 2)
|
||||
if (channels == 2)
|
||||
{
|
||||
// stereo sound
|
||||
overlapStereo(pOutput, pInput + 2 * ovlPos);
|
||||
|
@ -292,38 +277,34 @@ inline void TDStretch::overlap(SAMPLETYPE *pOutput, const SAMPLETYPE *pInput, ui
|
|||
|
||||
|
||||
|
||||
|
||||
// Seeks for the optimal overlap-mixing position. The 'stereo' version of the
|
||||
// routine
|
||||
//
|
||||
// The best position is determined as the position where the two overlapped
|
||||
// sample sequences are 'most alike', in terms of the highest cross-correlation
|
||||
// value over the overlapping period
|
||||
int TDStretch::seekBestOverlapPositionStereo(const SAMPLETYPE *refPos)
|
||||
int TDStretch::seekBestOverlapPositionFull(const SAMPLETYPE *refPos)
|
||||
{
|
||||
int bestOffs;
|
||||
double bestCorr, corr;
|
||||
int i;
|
||||
|
||||
// Slopes the amplitudes of the 'midBuffer' samples
|
||||
precalcCorrReferenceStereo();
|
||||
|
||||
bestCorr = FLT_MIN;
|
||||
bestOffs = 0;
|
||||
|
||||
// Scans for the best correlation value by testing each possible position
|
||||
// over the permitted range.
|
||||
for (i = 0; i < seekLength; i ++)
|
||||
for (i = 0; i < seekLength; i ++)
|
||||
{
|
||||
// Calculates correlation value for the mixing position corresponding
|
||||
// to 'i'
|
||||
corr = (double)calcCrossCorrStereo(refPos + 2 * i, pRefMidBuffer);
|
||||
corr = calcCrossCorr(refPos + channels * i, pMidBuffer);
|
||||
// heuristic rule to slightly favour values close to mid of the range
|
||||
double tmp = (double)(2 * i - seekLength) / (double)seekLength;
|
||||
corr = ((corr + 0.1) * (1.0 - 0.25 * tmp * tmp));
|
||||
|
||||
// Checks for the highest correlation value
|
||||
if (corr > bestCorr)
|
||||
if (corr > bestCorr)
|
||||
{
|
||||
bestCorr = corr;
|
||||
bestOffs = i;
|
||||
|
@ -342,16 +323,13 @@ int TDStretch::seekBestOverlapPositionStereo(const SAMPLETYPE *refPos)
|
|||
// The best position is determined as the position where the two overlapped
|
||||
// sample sequences are 'most alike', in terms of the highest cross-correlation
|
||||
// value over the overlapping period
|
||||
int TDStretch::seekBestOverlapPositionStereoQuick(const SAMPLETYPE *refPos)
|
||||
int TDStretch::seekBestOverlapPositionQuick(const SAMPLETYPE *refPos)
|
||||
{
|
||||
int j;
|
||||
int bestOffs;
|
||||
double bestCorr, corr;
|
||||
int scanCount, corrOffset, tempOffset;
|
||||
|
||||
// Slopes the amplitude of the 'midBuffer' samples
|
||||
precalcCorrReferenceStereo();
|
||||
|
||||
bestCorr = FLT_MIN;
|
||||
bestOffs = _scanOffsets[0][0];
|
||||
corrOffset = 0;
|
||||
|
@ -360,26 +338,26 @@ int TDStretch::seekBestOverlapPositionStereoQuick(const SAMPLETYPE *refPos)
|
|||
// Scans for the best correlation value using four-pass hierarchical search.
|
||||
//
|
||||
// The look-up table 'scans' has hierarchical position adjusting steps.
|
||||
// In first pass the routine searhes for the highest correlation with
|
||||
// In first pass the routine searhes for the highest correlation with
|
||||
// relatively coarse steps, then rescans the neighbourhood of the highest
|
||||
// correlation with better resolution and so on.
|
||||
for (scanCount = 0;scanCount < 4; scanCount ++)
|
||||
for (scanCount = 0;scanCount < 4; scanCount ++)
|
||||
{
|
||||
j = 0;
|
||||
while (_scanOffsets[scanCount][j])
|
||||
while (_scanOffsets[scanCount][j])
|
||||
{
|
||||
tempOffset = corrOffset + _scanOffsets[scanCount][j];
|
||||
if (tempOffset >= seekLength) break;
|
||||
|
||||
// Calculates correlation value for the mixing position corresponding
|
||||
// to 'tempOffset'
|
||||
corr = (double)calcCrossCorrStereo(refPos + 2 * tempOffset, pRefMidBuffer);
|
||||
corr = (double)calcCrossCorr(refPos + channels * tempOffset, pMidBuffer);
|
||||
// heuristic rule to slightly favour values close to mid of the range
|
||||
double tmp = (double)(2 * tempOffset - seekLength) / seekLength;
|
||||
corr = ((corr + 0.1) * (1.0 - 0.25 * tmp * tmp));
|
||||
|
||||
// Checks for the highest correlation value
|
||||
if (corr > bestCorr)
|
||||
if (corr > bestCorr)
|
||||
{
|
||||
bestCorr = corr;
|
||||
bestOffs = tempOffset;
|
||||
|
@ -396,112 +374,7 @@ int TDStretch::seekBestOverlapPositionStereoQuick(const SAMPLETYPE *refPos)
|
|||
|
||||
|
||||
|
||||
// Seeks for the optimal overlap-mixing position. The 'mono' version of the
|
||||
// routine
|
||||
//
|
||||
// The best position is determined as the position where the two overlapped
|
||||
// sample sequences are 'most alike', in terms of the highest cross-correlation
|
||||
// value over the overlapping period
|
||||
int TDStretch::seekBestOverlapPositionMono(const SAMPLETYPE *refPos)
|
||||
{
|
||||
int bestOffs;
|
||||
double bestCorr, corr;
|
||||
int tempOffset;
|
||||
const SAMPLETYPE *compare;
|
||||
|
||||
// Slopes the amplitude of the 'midBuffer' samples
|
||||
precalcCorrReferenceMono();
|
||||
|
||||
bestCorr = FLT_MIN;
|
||||
bestOffs = 0;
|
||||
|
||||
// Scans for the best correlation value by testing each possible position
|
||||
// over the permitted range.
|
||||
for (tempOffset = 0; tempOffset < seekLength; tempOffset ++)
|
||||
{
|
||||
compare = refPos + tempOffset;
|
||||
|
||||
// Calculates correlation value for the mixing position corresponding
|
||||
// to 'tempOffset'
|
||||
corr = (double)calcCrossCorrMono(pRefMidBuffer, compare);
|
||||
// heuristic rule to slightly favour values close to mid of the range
|
||||
double tmp = (double)(2 * tempOffset - seekLength) / seekLength;
|
||||
corr = ((corr + 0.1) * (1.0 - 0.25 * tmp * tmp));
|
||||
|
||||
// Checks for the highest correlation value
|
||||
if (corr > bestCorr)
|
||||
{
|
||||
bestCorr = corr;
|
||||
bestOffs = tempOffset;
|
||||
}
|
||||
}
|
||||
// clear cross correlation routine state if necessary (is so e.g. in MMX routines).
|
||||
clearCrossCorrState();
|
||||
|
||||
return bestOffs;
|
||||
}
|
||||
|
||||
|
||||
// Seeks for the optimal overlap-mixing position. The 'mono' version of the
|
||||
// routine
|
||||
//
|
||||
// The best position is determined as the position where the two overlapped
|
||||
// sample sequences are 'most alike', in terms of the highest cross-correlation
|
||||
// value over the overlapping period
|
||||
int TDStretch::seekBestOverlapPositionMonoQuick(const SAMPLETYPE *refPos)
|
||||
{
|
||||
int j;
|
||||
int bestOffs;
|
||||
double bestCorr, corr;
|
||||
int scanCount, corrOffset, tempOffset;
|
||||
|
||||
// Slopes the amplitude of the 'midBuffer' samples
|
||||
precalcCorrReferenceMono();
|
||||
|
||||
bestCorr = FLT_MIN;
|
||||
bestOffs = _scanOffsets[0][0];
|
||||
corrOffset = 0;
|
||||
tempOffset = 0;
|
||||
|
||||
// Scans for the best correlation value using four-pass hierarchical search.
|
||||
//
|
||||
// The look-up table 'scans' has hierarchical position adjusting steps.
|
||||
// In first pass the routine searhes for the highest correlation with
|
||||
// relatively coarse steps, then rescans the neighbourhood of the highest
|
||||
// correlation with better resolution and so on.
|
||||
for (scanCount = 0;scanCount < 4; scanCount ++)
|
||||
{
|
||||
j = 0;
|
||||
while (_scanOffsets[scanCount][j])
|
||||
{
|
||||
tempOffset = corrOffset + _scanOffsets[scanCount][j];
|
||||
if (tempOffset >= seekLength) break;
|
||||
|
||||
// Calculates correlation value for the mixing position corresponding
|
||||
// to 'tempOffset'
|
||||
corr = (double)calcCrossCorrMono(refPos + tempOffset, pRefMidBuffer);
|
||||
// heuristic rule to slightly favour values close to mid of the range
|
||||
double tmp = (double)(2 * tempOffset - seekLength) / seekLength;
|
||||
corr = ((corr + 0.1) * (1.0 - 0.25 * tmp * tmp));
|
||||
|
||||
// Checks for the highest correlation value
|
||||
if (corr > bestCorr)
|
||||
{
|
||||
bestCorr = corr;
|
||||
bestOffs = tempOffset;
|
||||
}
|
||||
j ++;
|
||||
}
|
||||
corrOffset = bestOffs;
|
||||
}
|
||||
// clear cross correlation routine state if necessary (is so e.g. in MMX routines).
|
||||
clearCrossCorrState();
|
||||
|
||||
return bestOffs;
|
||||
}
|
||||
|
||||
|
||||
/// clear cross correlation routine state if necessary
|
||||
/// clear cross correlation routine state if necessary
|
||||
void TDStretch::clearCrossCorrState()
|
||||
{
|
||||
// default implementation is empty.
|
||||
|
@ -531,7 +404,7 @@ void TDStretch::calcSeqParameters()
|
|||
#define CHECK_LIMITS(x, mi, ma) (((x) < (mi)) ? (mi) : (((x) > (ma)) ? (ma) : (x)))
|
||||
|
||||
double seq, seek;
|
||||
|
||||
|
||||
if (bAutoSeqSetting)
|
||||
{
|
||||
seq = AUTOSEQ_C + AUTOSEQ_K * tempo;
|
||||
|
@ -548,7 +421,7 @@ void TDStretch::calcSeqParameters()
|
|||
|
||||
// Update seek window lengths
|
||||
seekWindowLength = (sampleRate * sequenceMs) / 1000;
|
||||
if (seekWindowLength < 2 * overlapLength)
|
||||
if (seekWindowLength < 2 * overlapLength)
|
||||
{
|
||||
seekWindowLength = 2 * overlapLength;
|
||||
}
|
||||
|
@ -557,7 +430,7 @@ void TDStretch::calcSeqParameters()
|
|||
|
||||
|
||||
|
||||
// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower
|
||||
// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower
|
||||
// tempo, larger faster tempo.
|
||||
void TDStretch::setTempo(float newTempo)
|
||||
{
|
||||
|
@ -568,11 +441,11 @@ void TDStretch::setTempo(float newTempo)
|
|||
// Calculate new sequence duration
|
||||
calcSeqParameters();
|
||||
|
||||
// Calculate ideal skip length (according to tempo value)
|
||||
// Calculate ideal skip length (according to tempo value)
|
||||
nominalSkip = tempo * (seekWindowLength - overlapLength);
|
||||
intskip = (int)(nominalSkip + 0.5f);
|
||||
|
||||
// Calculate how many samples are needed in the 'inputBuffer' to
|
||||
// Calculate how many samples are needed in the 'inputBuffer' to
|
||||
// process another batch of samples
|
||||
//sampleReq = max(intskip + overlapLength, seekWindowLength) + seekLength / 2;
|
||||
sampleReq = max(intskip + overlapLength, seekWindowLength) + seekLength;
|
||||
|
@ -600,18 +473,18 @@ void TDStretch::processNominalTempo()
|
|||
{
|
||||
assert(tempo == 1.0f);
|
||||
|
||||
if (bMidBufferDirty)
|
||||
if (bMidBufferDirty)
|
||||
{
|
||||
// If there are samples in pMidBuffer waiting for overlapping,
|
||||
// do a single sliding overlapping with them in order to prevent a
|
||||
// do a single sliding overlapping with them in order to prevent a
|
||||
// clicking distortion in the output sound
|
||||
if (inputBuffer.numSamples() < overlapLength)
|
||||
if (inputBuffer.numSamples() < overlapLength)
|
||||
{
|
||||
// wait until we've got overlapLength input samples
|
||||
return;
|
||||
}
|
||||
// Mix the samples in the beginning of 'inputBuffer' with the
|
||||
// samples in 'midBuffer' using sliding overlapping
|
||||
// Mix the samples in the beginning of 'inputBuffer' with the
|
||||
// samples in 'midBuffer' using sliding overlapping
|
||||
overlap(outputBuffer.ptrEnd(overlapLength), inputBuffer.ptrBegin(), 0);
|
||||
outputBuffer.putSamples(overlapLength);
|
||||
inputBuffer.receiveSamples(overlapLength);
|
||||
|
@ -636,7 +509,7 @@ void TDStretch::processSamples()
|
|||
|
||||
/* Removed this small optimization - can introduce a click to sound when tempo setting
|
||||
crosses the nominal value
|
||||
if (tempo == 1.0f)
|
||||
if (tempo == 1.0f)
|
||||
{
|
||||
// tempo not changed from the original, so bypass the processing
|
||||
processNominalTempo();
|
||||
|
@ -646,14 +519,13 @@ void TDStretch::processSamples()
|
|||
|
||||
// Process samples as long as there are enough samples in 'inputBuffer'
|
||||
// to form a processing frame.
|
||||
// while ((int)inputBuffer.numSamples() >= sampleReq - (outDebt / 4))
|
||||
while ((int)inputBuffer.numSamples() >= sampleReq)
|
||||
while ((int)inputBuffer.numSamples() >= sampleReq)
|
||||
{
|
||||
// If tempo differs from the normal ('SCALE'), scan for the best overlapping
|
||||
// position
|
||||
offset = seekBestOverlapPosition(inputBuffer.ptrBegin());
|
||||
|
||||
// Mix the samples in the 'inputBuffer' at position of 'offset' with the
|
||||
// Mix the samples in the 'inputBuffer' at position of 'offset' with the
|
||||
// samples in 'midBuffer' using sliding overlapping
|
||||
// ... first partially overlap with the end of the previous sequence
|
||||
// (that's in 'midBuffer')
|
||||
|
@ -661,16 +533,8 @@ void TDStretch::processSamples()
|
|||
outputBuffer.putSamples((uint)overlapLength);
|
||||
|
||||
// ... then copy sequence samples from 'inputBuffer' to output:
|
||||
temp = (seekLength / 2 - offset);
|
||||
|
||||
// compensate cumulated output length diff vs. ideal output
|
||||
// temp -= outDebt / 4;
|
||||
|
||||
// update ideal vs. true output difference
|
||||
// outDebt += temp;
|
||||
|
||||
// length of sequence
|
||||
// temp += (seekWindowLength - 2 * overlapLength);
|
||||
temp = (seekWindowLength - 2 * overlapLength);
|
||||
|
||||
// crosscheck that we don't have buffer overflow...
|
||||
|
@ -681,11 +545,11 @@ void TDStretch::processSamples()
|
|||
|
||||
outputBuffer.putSamples(inputBuffer.ptrBegin() + channels * (offset + overlapLength), (uint)temp);
|
||||
|
||||
// Copies the end of the current sequence from 'inputBuffer' to
|
||||
// 'midBuffer' for being mixed with the beginning of the next
|
||||
// Copies the end of the current sequence from 'inputBuffer' to
|
||||
// 'midBuffer' for being mixed with the beginning of the next
|
||||
// processing sequence and so on
|
||||
assert((offset + temp + overlapLength * 2) <= (int)inputBuffer.numSamples());
|
||||
memcpy(pMidBuffer, inputBuffer.ptrBegin() + channels * (offset + temp + overlapLength),
|
||||
memcpy(pMidBuffer, inputBuffer.ptrBegin() + channels * (offset + temp + overlapLength),
|
||||
channels * sizeof(SAMPLETYPE) * overlapLength);
|
||||
|
||||
// Remove the processed samples from the input buffer. Update
|
||||
|
@ -722,26 +586,24 @@ void TDStretch::acceptNewOverlapLength(int newOverlapLength)
|
|||
|
||||
if (overlapLength > prevOvl)
|
||||
{
|
||||
delete[] pMidBuffer;
|
||||
delete[] pRefMidBufferUnaligned;
|
||||
delete[] pMidBufferUnaligned;
|
||||
|
||||
pMidBufferUnaligned = new SAMPLETYPE[overlapLength * 2 + 16 / sizeof(SAMPLETYPE)];
|
||||
// ensure that 'pMidBuffer' is aligned to 16 byte boundary for efficiency
|
||||
pMidBuffer = (SAMPLETYPE *)SOUNDTOUCH_ALIGN_POINTER_16(pMidBufferUnaligned);
|
||||
|
||||
pMidBuffer = new SAMPLETYPE[overlapLength * 2];
|
||||
clearMidBuffer();
|
||||
|
||||
pRefMidBufferUnaligned = new SAMPLETYPE[2 * overlapLength + 16 / sizeof(SAMPLETYPE)];
|
||||
// ensure that 'pRefMidBuffer' is aligned to 16 byte boundary for efficiency
|
||||
pRefMidBuffer = (SAMPLETYPE *)((((ulong)pRefMidBufferUnaligned) + 15) & (ulong)-16);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
||||
// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
||||
// depending on if we've a MMX/SSE/etc-capable CPU available or not.
|
||||
void * TDStretch::operator new(size_t s)
|
||||
{
|
||||
// Notice! don't use "new TDStretch" directly, use "newInstance" to create a new instance instead!
|
||||
throw std::runtime_error("Error in TDStretch::new: Don't use 'new TDStretch' directly, use 'newInstance' member instead!");
|
||||
return NULL;
|
||||
ST_THROW_RT_ERROR("Error in TDStretch::new: Don't use 'new TDStretch' directly, use 'newInstance' member instead!");
|
||||
return newInstance();
|
||||
}
|
||||
|
||||
|
||||
|
@ -751,36 +613,26 @@ TDStretch * TDStretch::newInstance()
|
|||
|
||||
uExtensions = detectCPUextensions();
|
||||
|
||||
// Check if MMX/SSE/3DNow! instruction set extensions supported by CPU
|
||||
// Check if MMX/SSE instruction set extensions supported by CPU
|
||||
|
||||
#ifdef ALLOW_MMX
|
||||
#ifdef SOUNDTOUCH_ALLOW_MMX
|
||||
// MMX routines available only with integer sample types
|
||||
if (uExtensions & SUPPORT_MMX)
|
||||
{
|
||||
return ::new TDStretchMMX;
|
||||
}
|
||||
else
|
||||
#endif // ALLOW_MMX
|
||||
#endif // SOUNDTOUCH_ALLOW_MMX
|
||||
|
||||
|
||||
#ifdef ALLOW_SSE
|
||||
#ifdef SOUNDTOUCH_ALLOW_SSE
|
||||
if (uExtensions & SUPPORT_SSE)
|
||||
{
|
||||
// SSE support
|
||||
return ::new TDStretchSSE;
|
||||
}
|
||||
else
|
||||
#endif // ALLOW_SSE
|
||||
|
||||
|
||||
#ifdef ALLOW_3DNOW
|
||||
if (uExtensions & SUPPORT_3DNOW)
|
||||
{
|
||||
// 3DNow! support
|
||||
return ::new TDStretch3DNow;
|
||||
}
|
||||
else
|
||||
#endif // ALLOW_3DNOW
|
||||
#endif // SOUNDTOUCH_ALLOW_SSE
|
||||
|
||||
{
|
||||
// ISA optimizations not supported, use plain C version
|
||||
|
@ -795,46 +647,9 @@ TDStretch * TDStretch::newInstance()
|
|||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifdef INTEGER_SAMPLES
|
||||
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||
|
||||
// Slopes the amplitude of the 'midBuffer' samples so that cross correlation
|
||||
// is faster to calculate
|
||||
void TDStretch::precalcCorrReferenceStereo()
|
||||
{
|
||||
int i, cnt2;
|
||||
int temp, temp2;
|
||||
|
||||
for (i=0 ; i < (int)overlapLength ;i ++)
|
||||
{
|
||||
temp = i * (overlapLength - i);
|
||||
cnt2 = i * 2;
|
||||
|
||||
temp2 = (pMidBuffer[cnt2] * temp) / slopingDivider;
|
||||
pRefMidBuffer[cnt2] = (short)(temp2);
|
||||
temp2 = (pMidBuffer[cnt2 + 1] * temp) / slopingDivider;
|
||||
pRefMidBuffer[cnt2 + 1] = (short)(temp2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Slopes the amplitude of the 'midBuffer' samples so that cross correlation
|
||||
// is faster to calculate
|
||||
void TDStretch::precalcCorrReferenceMono()
|
||||
{
|
||||
int i;
|
||||
long temp;
|
||||
long temp2;
|
||||
|
||||
for (i=0 ; i < (int)overlapLength ;i ++)
|
||||
{
|
||||
temp = i * (overlapLength - i);
|
||||
temp2 = (pMidBuffer[i] * temp) / slopingDivider;
|
||||
pRefMidBuffer[i] = (short)temp2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Overlaps samples in 'midBuffer' with the samples in 'input'. The 'Stereo'
|
||||
// Overlaps samples in 'midBuffer' with the samples in 'input'. The 'Stereo'
|
||||
// version of the routine.
|
||||
void TDStretch::overlapStereo(short *poutput, const short *input) const
|
||||
{
|
||||
|
@ -842,7 +657,7 @@ void TDStretch::overlapStereo(short *poutput, const short *input) const
|
|||
short temp;
|
||||
int cnt2;
|
||||
|
||||
for (i = 0; i < overlapLength ; i ++)
|
||||
for (i = 0; i < overlapLength ; i ++)
|
||||
{
|
||||
temp = (short)(overlapLength - i);
|
||||
cnt2 = 2 * i;
|
||||
|
@ -868,8 +683,8 @@ void TDStretch::calculateOverlapLength(int aoverlapMs)
|
|||
assert(aoverlapMs >= 0);
|
||||
|
||||
// calculate overlap length so that it's power of 2 - thus it's easy to do
|
||||
// integer division by right-shifting. Term "-1" at end is to account for
|
||||
// the extra most significatnt bit left unused in result by signed multiplication
|
||||
// integer division by right-shifting. Term "-1" at end is to account for
|
||||
// the extra most significatnt bit left unused in result by signed multiplication
|
||||
overlapDividerBits = _getClosest2Power((sampleRate * aoverlapMs) / 1000.0) - 1;
|
||||
if (overlapDividerBits > 9) overlapDividerBits = 9;
|
||||
if (overlapDividerBits < 3) overlapDividerBits = 3;
|
||||
|
@ -877,113 +692,70 @@ void TDStretch::calculateOverlapLength(int aoverlapMs)
|
|||
|
||||
acceptNewOverlapLength(newOvl);
|
||||
|
||||
// calculate sloping divider so that crosscorrelation operation won't
|
||||
// overflow 32-bit register. Max. sum of the crosscorrelation sum without
|
||||
// calculate sloping divider so that crosscorrelation operation won't
|
||||
// overflow 32-bit register. Max. sum of the crosscorrelation sum without
|
||||
// divider would be 2^30*(N^3-N)/3, where N = overlap length
|
||||
slopingDivider = (newOvl * newOvl - 1) / 3;
|
||||
}
|
||||
|
||||
|
||||
long TDStretch::calcCrossCorrMono(const short *mixingPos, const short *compare) const
|
||||
double TDStretch::calcCrossCorr(const short *mixingPos, const short *compare) const
|
||||
{
|
||||
long corr;
|
||||
long norm;
|
||||
int i;
|
||||
|
||||
corr = norm = 0;
|
||||
for (i = 1; i < overlapLength; i ++)
|
||||
// Same routine for stereo and mono. For stereo, unroll loop for better
|
||||
// efficiency and gives slightly better resolution against rounding.
|
||||
// For mono it same routine, just unrolls loop by factor of 4
|
||||
for (i = 0; i < channels * overlapLength; i += 4)
|
||||
{
|
||||
corr += (mixingPos[i] * compare[i]) >> overlapDividerBits;
|
||||
norm += (mixingPos[i] * mixingPos[i]) >> overlapDividerBits;
|
||||
corr += (mixingPos[i] * compare[i] +
|
||||
mixingPos[i + 1] * compare[i + 1] +
|
||||
mixingPos[i + 2] * compare[i + 2] +
|
||||
mixingPos[i + 3] * compare[i + 3]) >> overlapDividerBits;
|
||||
norm += (mixingPos[i] * mixingPos[i] +
|
||||
mixingPos[i + 1] * mixingPos[i + 1] +
|
||||
mixingPos[i + 2] * mixingPos[i + 2] +
|
||||
mixingPos[i + 3] * mixingPos[i + 3]) >> overlapDividerBits;
|
||||
}
|
||||
|
||||
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
||||
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
||||
// done using floating point operation
|
||||
if (norm == 0) norm = 1; // to avoid div by zero
|
||||
return (long)((double)corr * SHRT_MAX / sqrt((double)norm));
|
||||
return (double)corr / sqrt((double)norm);
|
||||
}
|
||||
|
||||
|
||||
long TDStretch::calcCrossCorrStereo(const short *mixingPos, const short *compare) const
|
||||
{
|
||||
long corr;
|
||||
long norm;
|
||||
int i;
|
||||
|
||||
corr = norm = 0;
|
||||
for (i = 2; i < 2 * overlapLength; i += 2)
|
||||
{
|
||||
corr += (mixingPos[i] * compare[i] +
|
||||
mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBits;
|
||||
norm += (mixingPos[i] * mixingPos[i] + mixingPos[i + 1] * mixingPos[i + 1]) >> overlapDividerBits;
|
||||
}
|
||||
|
||||
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
||||
// done using floating point operation
|
||||
if (norm == 0) norm = 1; // to avoid div by zero
|
||||
return (long)((double)corr * SHRT_MAX / sqrt((double)norm));
|
||||
}
|
||||
|
||||
#endif // INTEGER_SAMPLES
|
||||
#endif // SOUNDTOUCH_INTEGER_SAMPLES
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Floating point arithmetics specific algorithm implementations.
|
||||
//
|
||||
|
||||
#ifdef FLOAT_SAMPLES
|
||||
|
||||
|
||||
// Slopes the amplitude of the 'midBuffer' samples so that cross correlation
|
||||
// is faster to calculate
|
||||
void TDStretch::precalcCorrReferenceStereo()
|
||||
{
|
||||
int i, cnt2;
|
||||
float temp;
|
||||
|
||||
for (i=0 ; i < (int)overlapLength ;i ++)
|
||||
{
|
||||
temp = (float)i * (float)(overlapLength - i);
|
||||
cnt2 = i * 2;
|
||||
pRefMidBuffer[cnt2] = (float)(pMidBuffer[cnt2] * temp);
|
||||
pRefMidBuffer[cnt2 + 1] = (float)(pMidBuffer[cnt2 + 1] * temp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Slopes the amplitude of the 'midBuffer' samples so that cross correlation
|
||||
// is faster to calculate
|
||||
void TDStretch::precalcCorrReferenceMono()
|
||||
{
|
||||
int i;
|
||||
float temp;
|
||||
|
||||
for (i=0 ; i < (int)overlapLength ;i ++)
|
||||
{
|
||||
temp = (float)i * (float)(overlapLength - i);
|
||||
pRefMidBuffer[i] = (float)(pMidBuffer[i] * temp);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SOUNDTOUCH_FLOAT_SAMPLES
|
||||
|
||||
// Overlaps samples in 'midBuffer' with the samples in 'pInput'
|
||||
void TDStretch::overlapStereo(float *pOutput, const float *pInput) const
|
||||
{
|
||||
int i;
|
||||
int cnt2;
|
||||
float fTemp;
|
||||
float fScale;
|
||||
float fi;
|
||||
float f1;
|
||||
float f2;
|
||||
|
||||
fScale = 1.0f / (float)overlapLength;
|
||||
|
||||
for (i = 0; i < (int)overlapLength ; i ++)
|
||||
f1 = 0;
|
||||
f2 = 1.0f;
|
||||
|
||||
for (i = 0; i < 2 * (int)overlapLength ; i += 2)
|
||||
{
|
||||
fTemp = (float)(overlapLength - i) * fScale;
|
||||
fi = (float)i * fScale;
|
||||
cnt2 = 2 * i;
|
||||
pOutput[cnt2 + 0] = pInput[cnt2 + 0] * fi + pMidBuffer[cnt2 + 0] * fTemp;
|
||||
pOutput[cnt2 + 1] = pInput[cnt2 + 1] * fi + pMidBuffer[cnt2 + 1] * fTemp;
|
||||
pOutput[i + 0] = pInput[i + 0] * f1 + pMidBuffer[i + 0] * f2;
|
||||
pOutput[i + 1] = pInput[i + 1] * f1 + pMidBuffer[i + 1] * f2;
|
||||
|
||||
f1 += fScale;
|
||||
f2 -= fScale;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1004,42 +776,33 @@ void TDStretch::calculateOverlapLength(int overlapInMsec)
|
|||
}
|
||||
|
||||
|
||||
|
||||
double TDStretch::calcCrossCorrMono(const float *mixingPos, const float *compare) const
|
||||
double TDStretch::calcCrossCorr(const float *mixingPos, const float *compare) const
|
||||
{
|
||||
double corr;
|
||||
double norm;
|
||||
int i;
|
||||
|
||||
corr = norm = 0;
|
||||
for (i = 1; i < overlapLength; i ++)
|
||||
{
|
||||
corr += mixingPos[i] * compare[i];
|
||||
norm += mixingPos[i] * mixingPos[i];
|
||||
}
|
||||
|
||||
if (norm < 1e-9) norm = 1.0; // to avoid div by zero
|
||||
return corr / sqrt(norm);
|
||||
}
|
||||
|
||||
|
||||
double TDStretch::calcCrossCorrStereo(const float *mixingPos, const float *compare) const
|
||||
{
|
||||
double corr;
|
||||
double norm;
|
||||
int i;
|
||||
|
||||
corr = norm = 0;
|
||||
for (i = 2; i < 2 * overlapLength; i += 2)
|
||||
// Same routine for stereo and mono. For Stereo, unroll by factor of 2.
|
||||
// For mono it's same routine yet unrollsd by factor of 4.
|
||||
for (i = 0; i < channels * overlapLength; i += 4)
|
||||
{
|
||||
corr += mixingPos[i] * compare[i] +
|
||||
mixingPos[i + 1] * compare[i + 1];
|
||||
norm += mixingPos[i] * mixingPos[i] +
|
||||
|
||||
norm += mixingPos[i] * mixingPos[i] +
|
||||
mixingPos[i + 1] * mixingPos[i + 1];
|
||||
|
||||
// unroll the loop for better CPU efficiency:
|
||||
corr += mixingPos[i + 2] * compare[i + 2] +
|
||||
mixingPos[i + 3] * compare[i + 3];
|
||||
|
||||
norm += mixingPos[i + 2] * mixingPos[i + 2] +
|
||||
mixingPos[i + 3] * mixingPos[i + 3];
|
||||
}
|
||||
|
||||
if (norm < 1e-9) norm = 1.0; // to avoid div by zero
|
||||
return corr / sqrt(norm);
|
||||
}
|
||||
|
||||
#endif // FLOAT_SAMPLES
|
||||
#endif // SOUNDTOUCH_FLOAT_SAMPLES
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo
|
||||
/// while maintaining the original pitch by using a time domain WSOLA-like method
|
||||
///
|
||||
/// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo
|
||||
/// while maintaining the original pitch by using a time domain WSOLA-like method
|
||||
/// with several performance-increasing tweaks.
|
||||
///
|
||||
/// Note : MMX/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'
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
|
@ -13,10 +13,10 @@
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2009-05-17 14:35:13 +0300 (Sun, 17 May 2009) $
|
||||
// Last changed : $Date: 2012-04-01 16:49:30 -0300 (dom, 01 abr 2012) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: TDStretch.h 71 2009-05-17 11:35:13Z oparviai $
|
||||
// $Id: TDStretch.h 137 2012-04-01 19:49:30Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -53,14 +53,14 @@ namespace soundtouch
|
|||
{
|
||||
|
||||
/// 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:
|
||||
/// #define DEFAULT_SEQUENCE_MS 40
|
||||
/// #define DEFAULT_SEEKWINDOW_MS 15
|
||||
/// #define DEFAULT_OVERLAP_MS 8
|
||||
///
|
||||
|
||||
/// Default length of a single processing sequence, in milliseconds. This determines to how
|
||||
/// Default length of a single processing sequence, in milliseconds. This determines to how
|
||||
/// long sequences the original sound is chopped in the time-stretch algorithm.
|
||||
///
|
||||
/// The larger this value is, the lesser sequences are used in processing. In principle
|
||||
|
@ -75,15 +75,15 @@ namespace soundtouch
|
|||
/// according to tempo setting (recommended)
|
||||
#define USE_AUTO_SEQUENCE_LEN 0
|
||||
|
||||
/// Seeking window default length in milliseconds for algorithm that finds the best possible
|
||||
/// overlapping location. This determines from how wide window the algorithm may look for an
|
||||
/// optimal joining location when mixing the sound sequences back together.
|
||||
/// Seeking window default length in milliseconds for algorithm that finds the best possible
|
||||
/// overlapping location. This determines from how wide window the algorithm may look for an
|
||||
/// optimal joining location when mixing the sound sequences back together.
|
||||
///
|
||||
/// The bigger this window setting is, the higher the possibility to find a better mixing
|
||||
/// position will become, but at the same time large values may cause a "drifting" artifact
|
||||
/// because consequent sequences will be taken at more uneven intervals.
|
||||
///
|
||||
/// If there's a disturbing artifact that sounds as if a constant frequency was drifting
|
||||
/// If there's a disturbing artifact that sounds as if a constant frequency was drifting
|
||||
/// around, try reducing this setting.
|
||||
///
|
||||
/// Increasing this value increases computational burden & vice versa.
|
||||
|
@ -94,11 +94,11 @@ namespace soundtouch
|
|||
/// according to tempo setting (recommended)
|
||||
#define USE_AUTO_SEEKWINDOW_LEN 0
|
||||
|
||||
/// Overlap length in milliseconds. When the chopped sound sequences are mixed back together,
|
||||
/// to form a continuous sound stream, this parameter defines over how long period the two
|
||||
/// consecutive sequences are let to overlap each other.
|
||||
/// Overlap length in milliseconds. When the chopped sound sequences are mixed back together,
|
||||
/// to form a continuous sound stream, this parameter defines over how long period the two
|
||||
/// consecutive sequences are let to overlap each other.
|
||||
///
|
||||
/// This shouldn't be that critical parameter. If you reduce the DEFAULT_SEQUENCE_MS setting
|
||||
/// This shouldn't be that critical parameter. If you reduce the DEFAULT_SEQUENCE_MS setting
|
||||
/// by a large amount, you might wish to try a smaller value on this.
|
||||
///
|
||||
/// Increasing this value increases computational burden & vice versa.
|
||||
|
@ -115,8 +115,7 @@ protected:
|
|||
float tempo;
|
||||
|
||||
SAMPLETYPE *pMidBuffer;
|
||||
SAMPLETYPE *pRefMidBuffer;
|
||||
SAMPLETYPE *pRefMidBufferUnaligned;
|
||||
SAMPLETYPE *pMidBufferUnaligned;
|
||||
int overlapLength;
|
||||
int seekLength;
|
||||
int seekWindowLength;
|
||||
|
@ -127,8 +126,6 @@ protected:
|
|||
FIFOSampleBuffer outputBuffer;
|
||||
FIFOSampleBuffer inputBuffer;
|
||||
BOOL bQuickSeek;
|
||||
// int outDebt;
|
||||
// BOOL bMidBufferDirty;
|
||||
|
||||
int sampleRate;
|
||||
int sequenceMs;
|
||||
|
@ -142,13 +139,10 @@ protected:
|
|||
virtual void clearCrossCorrState();
|
||||
void calculateOverlapLength(int overlapMs);
|
||||
|
||||
virtual LONG_SAMPLETYPE calcCrossCorrStereo(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare) const;
|
||||
virtual LONG_SAMPLETYPE calcCrossCorrMono(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare) const;
|
||||
virtual double calcCrossCorr(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare) const;
|
||||
|
||||
virtual int seekBestOverlapPositionStereo(const SAMPLETYPE *refPos);
|
||||
virtual int seekBestOverlapPositionStereoQuick(const SAMPLETYPE *refPos);
|
||||
virtual int seekBestOverlapPositionMono(const SAMPLETYPE *refPos);
|
||||
virtual int seekBestOverlapPositionMonoQuick(const SAMPLETYPE *refPos);
|
||||
virtual int seekBestOverlapPositionFull(const SAMPLETYPE *refPos);
|
||||
virtual int seekBestOverlapPositionQuick(const SAMPLETYPE *refPos);
|
||||
int seekBestOverlapPosition(const SAMPLETYPE *refPos);
|
||||
|
||||
virtual void overlapStereo(SAMPLETYPE *output, const SAMPLETYPE *input) const;
|
||||
|
@ -157,9 +151,6 @@ protected:
|
|||
void clearMidBuffer();
|
||||
void overlap(SAMPLETYPE *output, const SAMPLETYPE *input, uint ovlPos) const;
|
||||
|
||||
void precalcCorrReferenceMono();
|
||||
void precalcCorrReferenceStereo();
|
||||
|
||||
void calcSeqParameters();
|
||||
|
||||
/// Changes the tempo of the given sound samples.
|
||||
|
@ -167,27 +158,27 @@ protected:
|
|||
/// The maximum amount of samples that can be returned at a time is set by
|
||||
/// the 'set_returnBuffer_size' function.
|
||||
void processSamples();
|
||||
|
||||
|
||||
public:
|
||||
TDStretch();
|
||||
virtual ~TDStretch();
|
||||
|
||||
/// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
||||
/// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
||||
/// depending on if we'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.
|
||||
/// Use this function instead of "new" operator to create a new instance of this class.
|
||||
/// This function automatically chooses a correct feature set depending on if the CPU
|
||||
/// supports MMX/SSE/etc extensions.
|
||||
static TDStretch *newInstance();
|
||||
|
||||
|
||||
/// Returns the output buffer object
|
||||
FIFOSamplePipe *getOutput() { return &outputBuffer; };
|
||||
|
||||
/// Returns the input buffer object
|
||||
FIFOSamplePipe *getInput() { return &inputBuffer; };
|
||||
|
||||
/// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower
|
||||
/// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower
|
||||
/// tempo, larger faster tempo.
|
||||
void setTempo(float newTempo);
|
||||
|
||||
|
@ -200,7 +191,7 @@ public:
|
|||
/// Sets the number of channels, 1 = mono, 2 = stereo
|
||||
void setChannels(int numChannels);
|
||||
|
||||
/// Enables/disables the quick position seeking algorithm. Zero to disable,
|
||||
/// Enables/disables the quick position seeking algorithm. Zero to disable,
|
||||
/// nonzero to enable
|
||||
void enableQuickSeek(BOOL enable);
|
||||
|
||||
|
@ -212,7 +203,7 @@ public:
|
|||
//
|
||||
/// 'sampleRate' = sample rate of the sound
|
||||
/// 'sequenceMS' = one processing sequence length in milliseconds
|
||||
/// 'seekwindowMS' = seeking window length for scanning the best overlapping
|
||||
/// 'seekwindowMS' = seeking window length for scanning the best overlapping
|
||||
/// position
|
||||
/// 'overlapMS' = overlapping length
|
||||
void setParameters(int sampleRate, ///< Samplerate of sound being processed (Hz)
|
||||
|
@ -233,43 +224,45 @@ public:
|
|||
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 (int)(nominalSkip + 0.5);
|
||||
}
|
||||
|
||||
/// return nominal output sample amount when running a processing batch
|
||||
int getOutputBatchSize() const
|
||||
{
|
||||
return seekWindowLength - overlapLength;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Implementation-specific class declarations:
|
||||
|
||||
#ifdef ALLOW_MMX
|
||||
#ifdef SOUNDTOUCH_ALLOW_MMX
|
||||
/// Class that implements MMX optimized routines for 16bit integer samples type.
|
||||
class TDStretchMMX : public TDStretch
|
||||
{
|
||||
protected:
|
||||
long calcCrossCorrStereo(const short *mixingPos, const short *compare) const;
|
||||
double calcCrossCorr(const short *mixingPos, const short *compare) const;
|
||||
virtual void overlapStereo(short *output, const short *input) const;
|
||||
virtual void clearCrossCorrState();
|
||||
};
|
||||
#endif /// ALLOW_MMX
|
||||
#endif /// SOUNDTOUCH_ALLOW_MMX
|
||||
|
||||
|
||||
#ifdef ALLOW_3DNOW
|
||||
/// Class that implements 3DNow! optimized routines for floating point samples type.
|
||||
class TDStretch3DNow : public TDStretch
|
||||
{
|
||||
protected:
|
||||
double calcCrossCorrStereo(const float *mixingPos, const float *compare) const;
|
||||
};
|
||||
#endif /// ALLOW_3DNOW
|
||||
|
||||
|
||||
#ifdef ALLOW_SSE
|
||||
#ifdef SOUNDTOUCH_ALLOW_SSE
|
||||
/// Class that implements SSE optimized routines for floating point samples type.
|
||||
class TDStretchSSE : public TDStretch
|
||||
{
|
||||
protected:
|
||||
double calcCrossCorrStereo(const float *mixingPos, const float *compare) const;
|
||||
double calcCrossCorr(const float *mixingPos, const float *compare) const;
|
||||
};
|
||||
|
||||
#endif /// ALLOW_SSE
|
||||
#endif /// SOUNDTOUCH_ALLOW_SSE
|
||||
|
||||
}
|
||||
#endif /// TDStretch_H
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
///
|
||||
/// A header file for detecting the Intel MMX instructions set extension.
|
||||
///
|
||||
/// Please see 'mmx_win.cpp', 'mmx_cpp.cpp' and 'mmx_non_x86.cpp' for the
|
||||
/// routine implementations for x86 Windows, x86 gnu version and non-x86
|
||||
/// Please see 'mmx_win.cpp', 'mmx_cpp.cpp' and 'mmx_non_x86.cpp' for the
|
||||
/// routine implementations for x86 Windows, x86 gnu version and non-x86
|
||||
/// platforms, respectively.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
|
@ -12,7 +12,7 @@
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2008-02-10 18:26:55 +0200 (Sun, 10 Feb 2008) $
|
||||
// Last changed : $Date: 2008-02-10 14:26:55 -0200 (dom, 10 fev 2008) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: cpu_detect.h 11 2008-02-10 16:26:55Z oparviai $
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Generic version of the x86 CPU extension detection routine.
|
||||
///
|
||||
/// This file is for GNU & other non-Windows compilers, see 'cpu_detect_x86_win.cpp'
|
||||
/// for the Microsoft compiler version.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2012-11-08 16:44:37 -0200 (qui, 08 nov 2012) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: cpu_detect_x86.cpp 159 2012-11-08 18:44:37Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "cpu_detect.h"
|
||||
#include "STTypes.h"
|
||||
|
||||
#if defined(SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS)
|
||||
|
||||
#if defined(__GNUC__) && defined(__i386__)
|
||||
// gcc
|
||||
#include "cpuid.h"
|
||||
#elif defined(_M_IX86)
|
||||
// windows non-gcc
|
||||
#include <intrin.h>
|
||||
#define bit_MMX (1 << 23)
|
||||
#define bit_SSE (1 << 25)
|
||||
#define bit_SSE2 (1 << 26)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// processor instructions extension detection routines
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Flag variable indicating whick ISA extensions are disabled (for debugging)
|
||||
static uint _dwDisabledISA = 0x00; // 0xffffffff; //<- use this to disable all extensions
|
||||
|
||||
// Disables given set of instruction extensions. See SUPPORT_... defines.
|
||||
void disableExtensions(uint dwDisableMask)
|
||||
{
|
||||
_dwDisabledISA = dwDisableMask;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Checks which instruction set extensions are supported by the CPU.
|
||||
uint detectCPUextensions(void)
|
||||
{
|
||||
/// If building for a 64bit system (no Itanium) and the user wants optimizations.
|
||||
/// Return the OR of SUPPORT_{MMX,SSE,SSE2}. 11001 or 0x19.
|
||||
/// Keep the _dwDisabledISA test (2 more operations, could be eliminated).
|
||||
#if ((defined(__GNUC__) && defined(__x86_64__)) \
|
||||
|| defined(_M_X64)) \
|
||||
&& defined(SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS)
|
||||
return 0x19 & ~_dwDisabledISA;
|
||||
|
||||
/// If building for a 32bit system and the user wants optimizations.
|
||||
/// Keep the _dwDisabledISA test (2 more operations, could be eliminated).
|
||||
#elif ((defined(__GNUC__) && defined(__i386__)) \
|
||||
|| defined(_M_IX86)) \
|
||||
&& defined(SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS)
|
||||
|
||||
if (_dwDisabledISA == 0xffffffff) return 0;
|
||||
|
||||
uint res = 0;
|
||||
|
||||
#if defined(__GNUC__)
|
||||
// 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.
|
||||
|
||||
// Check if no cpuid support.
|
||||
if (!__get_cpuid (1, &eax, &ebx, &ecx, &edx)) return 0; // always disable extensions.
|
||||
|
||||
if (edx & bit_MMX) res = res | SUPPORT_MMX;
|
||||
if (edx & bit_SSE) res = res | SUPPORT_SSE;
|
||||
if (edx & bit_SSE2) res = res | SUPPORT_SSE2;
|
||||
|
||||
#else
|
||||
// Window / VS version of cpuid. Notice that Visual Studio 2005 or later required
|
||||
// for __cpuid intrinsic support.
|
||||
int reg[4] = {-1};
|
||||
|
||||
// Check if no cpuid support.
|
||||
__cpuid(reg,0);
|
||||
if ((unsigned int)reg[0] == 0) return 0; // always disable extensions.
|
||||
|
||||
__cpuid(reg,1);
|
||||
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_SSE2) res = res | SUPPORT_SSE2;
|
||||
|
||||
#endif
|
||||
|
||||
return res & ~_dwDisabledISA;
|
||||
|
||||
#else
|
||||
|
||||
/// One of these is true:
|
||||
/// 1) We don't want optimizations.
|
||||
/// 2) Using an unsupported compiler.
|
||||
/// 3) Running on a non-x86 platform.
|
||||
return 0;
|
||||
|
||||
#endif
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
///
|
||||
/// 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.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
|
@ -11,10 +11,10 @@
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2009-02-25 19:13:51 +0200 (Wed, 25 Feb 2009) $
|
||||
// Last changed : $Date: 2011-09-02 15:56:11 -0300 (sex, 02 set 2011) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: cpu_detect_x86_gcc.cpp 67 2009-02-25 17:13:51Z oparviai $
|
||||
// $Id: cpu_detect_x86_gcc.cpp 131 2011-09-02 18:56:11Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -39,15 +39,9 @@
|
|||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include "cpu_detect.h"
|
||||
#include "STTypes.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// processor instructions extension detection routines
|
||||
|
@ -68,7 +62,7 @@ void disableExtensions(uint dwDisableMask)
|
|||
/// Checks which instruction set extensions are supported by the CPU.
|
||||
uint detectCPUextensions(void)
|
||||
{
|
||||
#if (!(ALLOW_X86_OPTIMIZATIONS) || !(__GNUC__))
|
||||
#if (!(SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS) || !(__GNUC__))
|
||||
|
||||
return 0; // always disable extensions on non-x86 platforms.
|
||||
|
||||
|
@ -78,8 +72,11 @@ uint detectCPUextensions(void)
|
|||
if (_dwDisabledISA == 0xffffffff) return 0;
|
||||
|
||||
asm volatile(
|
||||
#ifndef __x86_64__
|
||||
// Check if 'cpuid' instructions is available by toggling eflags bit 21.
|
||||
// Skip this for x86-64 as they always have cpuid while stack manipulation
|
||||
// differs from 16/32bit ISA.
|
||||
"\n\txor %%esi, %%esi" // clear %%esi = result register
|
||||
// check if 'cpuid' instructions is available by toggling eflags bit 21
|
||||
|
||||
"\n\tpushf" // save eflags to stack
|
||||
"\n\tmovl (%%esp), %%eax" // load eax from stack (with eflags)
|
||||
|
@ -93,6 +90,8 @@ uint detectCPUextensions(void)
|
|||
"\n\txor %%edx, %%edx" // clear edx for defaulting no mmx
|
||||
"\n\tcmp %%ecx, %%eax" // compare to original eflags values
|
||||
"\n\tjz end" // jumps to 'end' if cpuid not present
|
||||
#endif // __x86_64__
|
||||
|
||||
// cpuid instruction available, test for presence of mmx instructions
|
||||
|
||||
"\n\tmovl $1, %%eax"
|
||||
|
@ -129,7 +128,7 @@ uint detectCPUextensions(void)
|
|||
: "=r" (res)
|
||||
: /* no inputs */
|
||||
: "%edx", "%eax", "%ecx", "%esi" );
|
||||
|
||||
|
||||
return res & ~_dwDisabledISA;
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
///
|
||||
/// Win32 version of the x86 CPU detect routine.
|
||||
///
|
||||
/// This file is to be compiled in Windows platform with Microsoft Visual C++
|
||||
/// Compiler. Please see 'cpu_detect_x86_gcc.cpp' for the gcc compiler version
|
||||
/// This file is to be compiled in Windows platform with Microsoft Visual C++
|
||||
/// Compiler. Please see 'cpu_detect_x86_gcc.cpp' for the gcc compiler version
|
||||
/// for all GNU platforms.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
|
@ -12,10 +12,10 @@
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2009-02-13 18:22:48 +0200 (Fri, 13 Feb 2009) $
|
||||
// Last changed : $Date: 2011-07-17 07:58:40 -0300 (dom, 17 jul 2011) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: cpu_detect_x86_win.cpp 62 2009-02-13 16:22:48Z oparviai $
|
||||
// $Id: cpu_detect_x86_win.cpp 127 2011-07-17 10:58:40Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -42,9 +42,7 @@
|
|||
|
||||
#include "cpu_detect.h"
|
||||
|
||||
#ifndef WIN32
|
||||
#error wrong platform - this source code file is exclusively for Win32 platform
|
||||
#endif
|
||||
#include "STTypes.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -71,7 +69,9 @@ uint detectCPUextensions(void)
|
|||
|
||||
if (_dwDisabledISA == 0xffffffff) return 0;
|
||||
|
||||
_asm
|
||||
#ifndef _M_X64
|
||||
// 32bit compilation, detect CPU capabilities with inline assembler.
|
||||
__asm
|
||||
{
|
||||
; check if 'cpuid' instructions is available by toggling eflags bit 21
|
||||
;
|
||||
|
@ -92,7 +92,7 @@ uint detectCPUextensions(void)
|
|||
cmp eax, ecx ; compare to original eflags values
|
||||
jz end ; jumps to 'end' if cpuid not present
|
||||
|
||||
; cpuid instruction available, test for presence of mmx instructions
|
||||
; cpuid instruction available, test for presence of mmx instructions
|
||||
mov eax, 1
|
||||
cpuid
|
||||
test edx, 0x00800000
|
||||
|
@ -125,5 +125,13 @@ uint detectCPUextensions(void)
|
|||
mov res, esi
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// Visual C++ 64bit compilation doesn't support inline assembler. However,
|
||||
// all x64 compatible CPUs support MMX & SSE extensions.
|
||||
res = SUPPORT_MMX | SUPPORT_SSE | SUPPORT_SSE2;
|
||||
|
||||
#endif
|
||||
|
||||
return res & ~_dwDisabledISA;
|
||||
}
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// MMX optimized routines. All MMX optimized functions have been gathered into
|
||||
/// this single source code file, regardless to their class or original source
|
||||
/// code file, in order to ease porting the library to other compiler and
|
||||
/// MMX optimized routines. All MMX optimized functions have been gathered into
|
||||
/// this single source code file, regardless to their class or original source
|
||||
/// code file, in order to ease porting the library to other compiler and
|
||||
/// processor platforms.
|
||||
///
|
||||
/// The MMX-optimizations are programmed using MMX compiler intrinsics that
|
||||
/// are supported both by Microsoft Visual C++ and GCC compilers, so this file
|
||||
/// 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
|
||||
/// is available for download at Microsoft Developers Network, see here:
|
||||
/// http://msdn.microsoft.com/en-us/vstudio/aa718349.aspx
|
||||
|
@ -20,10 +20,10 @@
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2009-10-31 16:53:23 +0200 (Sat, 31 Oct 2009) $
|
||||
// Last changed : $Date: 2012-11-08 16:53:01 -0200 (qui, 08 nov 2012) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: mmx_optimized.cpp 75 2009-10-31 14:53:23Z oparviai $
|
||||
// $Id: mmx_optimized.cpp 160 2012-11-08 18:53:01Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -50,13 +50,9 @@
|
|||
|
||||
#include "STTypes.h"
|
||||
|
||||
#ifdef ALLOW_MMX
|
||||
#ifdef SOUNDTOUCH_ALLOW_MMX
|
||||
// MMX routines available only with integer sample type
|
||||
|
||||
#if !(WIN32 || __i386__ || __x86_64__)
|
||||
#error "wrong platform - this source code file is exclusively for x86 platforms"
|
||||
#endif
|
||||
|
||||
using namespace soundtouch;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -72,23 +68,23 @@ using namespace soundtouch;
|
|||
|
||||
|
||||
// Calculates cross correlation of two buffers
|
||||
long TDStretchMMX::calcCrossCorrStereo(const short *pV1, const short *pV2) const
|
||||
double TDStretchMMX::calcCrossCorr(const short *pV1, const short *pV2) const
|
||||
{
|
||||
const __m64 *pVec1, *pVec2;
|
||||
__m64 shifter;
|
||||
__m64 accu, normaccu;
|
||||
long corr, norm;
|
||||
int i;
|
||||
|
||||
|
||||
pVec1 = (__m64*)pV1;
|
||||
pVec2 = (__m64*)pV2;
|
||||
|
||||
shifter = _m_from_int(overlapDividerBits);
|
||||
normaccu = accu = _mm_setzero_si64();
|
||||
|
||||
// Process 4 parallel sets of 2 * stereo samples each during each
|
||||
// round to improve CPU-level parallellization.
|
||||
for (i = 0; i < overlapLength / 8; i ++)
|
||||
// Process 4 parallel sets of 2 * stereo samples or 4 * mono samples
|
||||
// during each round for improved CPU-level parallellization.
|
||||
for (i = 0; i < channels * overlapLength / 16; i ++)
|
||||
{
|
||||
__m64 temp, temp2;
|
||||
|
||||
|
@ -127,10 +123,11 @@ long TDStretchMMX::calcCrossCorrStereo(const short *pV1, const short *pV2) const
|
|||
// Clear MMS state
|
||||
_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
|
||||
if (norm == 0) norm = 1; // to avoid div by zero
|
||||
return (long)((double)corr * USHRT_MAX / sqrt((double)norm));
|
||||
|
||||
return (double)corr / sqrt((double)norm);
|
||||
// Note: Warning about the missing EMMS instruction is harmless
|
||||
// as it'll be called elsewhere.
|
||||
}
|
||||
|
@ -161,7 +158,7 @@ void TDStretchMMX::overlapStereo(short *output, const short *input) const
|
|||
// mix1 = mixer values for 1st stereo sample
|
||||
// mix1 = mixer values for 2nd stereo sample
|
||||
// adder = adder for updating mixer values after each round
|
||||
|
||||
|
||||
mix1 = _mm_set_pi16(0, overlapLength, 0, overlapLength);
|
||||
adder = _mm_set_pi16(1, -1, 1, -1);
|
||||
mix2 = _mm_add_pi16(mix1, adder);
|
||||
|
@ -174,7 +171,7 @@ void TDStretchMMX::overlapStereo(short *output, const short *input) const
|
|||
for (i = 0; i < overlapLength / 4; i ++)
|
||||
{
|
||||
__m64 temp1, temp2;
|
||||
|
||||
|
||||
// load & shuffle data so that input & mixbuffer data samples are paired
|
||||
temp1 = _mm_unpacklo_pi16(pVMidBuf[0], pVinput[0]); // = i0l m0l i0r m0r
|
||||
temp2 = _mm_unpackhi_pi16(pVMidBuf[0], pVinput[0]); // = i1l m1l i1r m1r
|
||||
|
@ -242,10 +239,10 @@ void FIRFilterMMX::setCoefficients(const short *coeffs, uint newLength, uint uRe
|
|||
// Ensure that filter coeffs array is aligned to 16-byte boundary
|
||||
delete[] filterCoeffsUnalign;
|
||||
filterCoeffsUnalign = new short[2 * newLength + 8];
|
||||
filterCoeffsAlign = (short *)(((ulong)filterCoeffsUnalign + 15) & -16);
|
||||
filterCoeffsAlign = (short *)SOUNDTOUCH_ALIGN_POINTER_16(filterCoeffsUnalign);
|
||||
|
||||
// rearrange the filter coefficients for mmx routines
|
||||
for (i = 0;i < length; i += 4)
|
||||
// rearrange the filter coefficients for mmx routines
|
||||
for (i = 0;i < length; i += 4)
|
||||
{
|
||||
filterCoeffsAlign[2 * i + 0] = coeffs[i + 0];
|
||||
filterCoeffsAlign[2 * i + 1] = coeffs[i + 2];
|
||||
|
@ -317,4 +314,4 @@ uint FIRFilterMMX::evaluateFilterStereo(short *dest, const short *src, uint numS
|
|||
return (numSamples & 0xfffffffe) - length;
|
||||
}
|
||||
|
||||
#endif // ALLOW_MMX
|
||||
#endif // SOUNDTOUCH_ALLOW_MMX
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// SSE optimized routines for Pentium-III, Athlon-XP and later CPUs. All SSE
|
||||
/// optimized functions have been gathered into this single source
|
||||
/// code file, regardless to their class or original source code file, in order
|
||||
/// SSE optimized routines for Pentium-III, Athlon-XP and later CPUs. All SSE
|
||||
/// optimized functions have been gathered into this single source
|
||||
/// code file, regardless to their class or original source code file, in order
|
||||
/// to ease porting the library to other compiler and processor platforms.
|
||||
///
|
||||
/// The SSE-optimizations are programmed using SSE compiler intrinsics that
|
||||
/// are supported both by Microsoft Visual C++ and GCC compilers, so this file
|
||||
/// should compile with both toolsets.
|
||||
///
|
||||
/// 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
|
||||
/// NOTICE: If using Visual Studio 6.0, you'll need to install the "Visual C++
|
||||
/// 6.0 processor pack" update to support SSE instruction set. The update is
|
||||
/// available for download at Microsoft Developers Network, see here:
|
||||
/// http://msdn.microsoft.com/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".
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
|
@ -23,10 +23,10 @@
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2009-12-28 22:32:57 +0200 (Mon, 28 Dec 2009) $
|
||||
// Last changed : $Date: 2012-11-08 16:53:01 -0200 (qui, 08 nov 2012) $
|
||||
// File revision : $Revision: 4 $
|
||||
//
|
||||
// $Id: sse_optimized.cpp 80 2009-12-28 20:32:57Z oparviai $
|
||||
// $Id: sse_optimized.cpp 160 2012-11-08 18:53:01Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -56,9 +56,9 @@
|
|||
|
||||
using namespace soundtouch;
|
||||
|
||||
#ifdef ALLOW_SSE
|
||||
#ifdef SOUNDTOUCH_ALLOW_SSE
|
||||
|
||||
// SSE routines available only with float sample type
|
||||
// SSE routines available only with float sample type
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -71,35 +71,35 @@ using namespace soundtouch;
|
|||
#include <math.h>
|
||||
|
||||
// Calculates cross correlation of two buffers
|
||||
double TDStretchSSE::calcCrossCorrStereo(const float *pV1, const float *pV2) const
|
||||
double TDStretchSSE::calcCrossCorr(const float *pV1, const float *pV2) const
|
||||
{
|
||||
int i;
|
||||
const float *pVec1;
|
||||
const __m128 *pVec2;
|
||||
__m128 vSum, vNorm;
|
||||
|
||||
// Note. It means a major slow-down if the routine needs to tolerate
|
||||
// unaligned __m128 memory accesses. It's way faster if we can skip
|
||||
// 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 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
|
||||
// due to skipping every second round for stereo sound though).
|
||||
//
|
||||
// Compile-time define ALLOW_NONEXACT_SIMD_OPTIMIZATION is provided
|
||||
// Compile-time define SOUNDTOUCH_ALLOW_NONEXACT_SIMD_OPTIMIZATION is provided
|
||||
// for choosing if this little cheating is allowed.
|
||||
|
||||
#ifdef ALLOW_NONEXACT_SIMD_OPTIMIZATION
|
||||
// Little cheating allowed, return valid correlation only for
|
||||
#ifdef SOUNDTOUCH_ALLOW_NONEXACT_SIMD_OPTIMIZATION
|
||||
// Little cheating allowed, return valid correlation only for
|
||||
// aligned locations, meaning every second round for stereo sound.
|
||||
|
||||
#define _MM_LOAD _mm_load_ps
|
||||
|
||||
if (((ulong)pV1) & 15) return -1e50; // skip unaligned locations
|
||||
if (((ulongptr)pV1) & 15) return -1e50; // skip unaligned locations
|
||||
|
||||
#else
|
||||
// No cheating allowed, use unaligned load & take the resulting
|
||||
// performance hit.
|
||||
#define _MM_LOAD _mm_loadu_ps
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// ensure overlapLength is divisible by 8
|
||||
assert((overlapLength % 8) == 0);
|
||||
|
@ -110,8 +110,9 @@ double TDStretchSSE::calcCrossCorrStereo(const float *pV1, const float *pV2) con
|
|||
pVec2 = (const __m128*)pV2;
|
||||
vSum = vNorm = _mm_setzero_ps();
|
||||
|
||||
// Unroll the loop by factor of 4 * 4 operations
|
||||
for (i = 0; i < overlapLength / 8; i ++)
|
||||
// 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.
|
||||
for (i = 0; i < channels * overlapLength / 16; i ++)
|
||||
{
|
||||
__m128 vTemp;
|
||||
// vSum += pV1[0..3] * pV2[0..3]
|
||||
|
@ -152,7 +153,7 @@ double TDStretchSSE::calcCrossCorrStereo(const float *pV1, const float *pV2) con
|
|||
|
||||
// Calculates the cross-correlation value between 'pV1' and 'pV2' vectors
|
||||
corr = norm = 0.0;
|
||||
for (i = 0; i < overlapLength / 8; i ++)
|
||||
for (i = 0; i < channels * overlapLength / 16; i ++)
|
||||
{
|
||||
corr += pV1[0] * pV2[0] +
|
||||
pV1[1] * pV2[1] +
|
||||
|
@ -171,81 +172,13 @@ double TDStretchSSE::calcCrossCorrStereo(const float *pV1, const float *pV2) con
|
|||
pV1[14] * pV2[14] +
|
||||
pV1[15] * pV2[15];
|
||||
|
||||
for (j = 0; j < 15; j ++) norm += pV1[j] * pV1[j];
|
||||
for (j = 0; j < 15; j ++) norm += pV1[j] * pV1[j];
|
||||
|
||||
pV1 += 16;
|
||||
pV2 += 16;
|
||||
}
|
||||
return corr / sqrt(norm);
|
||||
*/
|
||||
|
||||
/* This is a bit outdated, corresponding routine in assembler. This may be teeny-weeny bit
|
||||
faster than intrinsic version, but more difficult to maintain & get compiled on multiple
|
||||
platforms.
|
||||
|
||||
uint overlapLengthLocal = overlapLength;
|
||||
float corr;
|
||||
|
||||
_asm
|
||||
{
|
||||
// Very important note: data in 'pV2' _must_ be aligned to
|
||||
// 16-byte boundary!
|
||||
|
||||
// give prefetch hints to CPU of what data are to be needed soonish
|
||||
// give more aggressive hints on pV1 as that changes while pV2 stays
|
||||
// same between runs
|
||||
prefetcht0 [pV1]
|
||||
prefetcht0 [pV2]
|
||||
prefetcht0 [pV1 + 32]
|
||||
|
||||
mov eax, dword ptr pV1
|
||||
mov ebx, dword ptr pV2
|
||||
|
||||
xorps xmm0, xmm0
|
||||
|
||||
mov ecx, overlapLengthLocal
|
||||
shr ecx, 3 // div by eight
|
||||
|
||||
loop1:
|
||||
prefetcht0 [eax + 64] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
prefetcht0 [ebx + 32] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
movups xmm1, [eax]
|
||||
mulps xmm1, [ebx]
|
||||
addps xmm0, xmm1
|
||||
|
||||
movups xmm2, [eax + 16]
|
||||
mulps xmm2, [ebx + 16]
|
||||
addps xmm0, xmm2
|
||||
|
||||
prefetcht0 [eax + 96] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
prefetcht0 [ebx + 64] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
|
||||
movups xmm3, [eax + 32]
|
||||
mulps xmm3, [ebx + 32]
|
||||
addps xmm0, xmm3
|
||||
|
||||
movups xmm4, [eax + 48]
|
||||
mulps xmm4, [ebx + 48]
|
||||
addps xmm0, xmm4
|
||||
|
||||
add eax, 64
|
||||
add ebx, 64
|
||||
|
||||
dec ecx
|
||||
jnz loop1
|
||||
|
||||
// add the four floats of xmm0 together and return the result.
|
||||
|
||||
movhlps xmm1, xmm0 // move 3 & 4 of xmm0 to 1 & 2 of xmm1
|
||||
addps xmm1, xmm0
|
||||
movaps xmm2, xmm1
|
||||
shufps xmm2, xmm2, 0x01 // move 2 of xmm2 as 1 of xmm2
|
||||
addss xmm2, xmm1
|
||||
movss corr, xmm2
|
||||
}
|
||||
|
||||
return (double)corr;
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
|
@ -281,15 +214,15 @@ void FIRFilterSSE::setCoefficients(const float *coeffs, uint newLength, uint uRe
|
|||
FIRFilter::setCoefficients(coeffs, newLength, uResultDivFactor);
|
||||
|
||||
// Scale the filter coefficients so that it won't be necessary to scale the filtering result
|
||||
// also rearrange coefficients suitably for 3DNow!
|
||||
// also rearrange coefficients suitably for SSE
|
||||
// Ensure that filter coeffs array is aligned to 16-byte boundary
|
||||
delete[] filterCoeffsUnalign;
|
||||
filterCoeffsUnalign = new float[2 * newLength + 4];
|
||||
filterCoeffsAlign = (float *)(((unsigned long)filterCoeffsUnalign + 15) & (ulong)-16);
|
||||
filterCoeffsAlign = (float *)SOUNDTOUCH_ALIGN_POINTER_16(filterCoeffsUnalign);
|
||||
|
||||
fDivider = (float)resultDivider;
|
||||
|
||||
// rearrange the filter coefficients for mmx routines
|
||||
// rearrange the filter coefficients for mmx routines
|
||||
for (i = 0; i < newLength; i ++)
|
||||
{
|
||||
filterCoeffsAlign[2 * i + 0] =
|
||||
|
@ -313,7 +246,7 @@ uint FIRFilterSSE::evaluateFilterStereo(float *dest, const float *source, uint n
|
|||
assert(dest != NULL);
|
||||
assert((length % 8) == 0);
|
||||
assert(filterCoeffsAlign != NULL);
|
||||
assert(((ulong)filterCoeffsAlign) % 16 == 0);
|
||||
assert(((ulongptr)filterCoeffsAlign) % 16 == 0);
|
||||
|
||||
// filter is evaluated for two stereo samples with each iteration, thus use of 'j += 2'
|
||||
for (j = 0; j < count; j += 2)
|
||||
|
@ -324,13 +257,13 @@ uint FIRFilterSSE::evaluateFilterStereo(float *dest, const float *source, uint n
|
|||
uint i;
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
// sum1 is accu for 2*2 filtered stereo sound data at the primary sound data offset
|
||||
|
@ -365,14 +298,14 @@ uint FIRFilterSSE::evaluateFilterStereo(float *dest, const float *source, uint n
|
|||
}
|
||||
|
||||
// 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.
|
||||
// 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.
|
||||
|
||||
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.
|
||||
double suml1, suml2;
|
||||
double sumr1, sumr2;
|
||||
|
@ -387,26 +320,26 @@ uint FIRFilterSSE::evaluateFilterStereo(float *dest, const float *source, uint n
|
|||
suml2 = sumr2 = 0.0;
|
||||
ptr = src;
|
||||
pFil = filterCoeffs;
|
||||
for (i = 0; i < lengthLocal; i ++)
|
||||
for (i = 0; i < lengthLocal; i ++)
|
||||
{
|
||||
// unroll loop for efficiency.
|
||||
|
||||
suml1 += ptr[0] * pFil[0] +
|
||||
suml1 += ptr[0] * pFil[0] +
|
||||
ptr[2] * pFil[2] +
|
||||
ptr[4] * pFil[4] +
|
||||
ptr[6] * pFil[6];
|
||||
|
||||
sumr1 += ptr[1] * pFil[1] +
|
||||
sumr1 += ptr[1] * pFil[1] +
|
||||
ptr[3] * pFil[3] +
|
||||
ptr[5] * pFil[5] +
|
||||
ptr[7] * pFil[7];
|
||||
|
||||
suml2 += ptr[8] * pFil[0] +
|
||||
suml2 += ptr[8] * pFil[0] +
|
||||
ptr[10] * pFil[2] +
|
||||
ptr[12] * pFil[4] +
|
||||
ptr[14] * pFil[6];
|
||||
|
||||
sumr2 += ptr[9] * pFil[1] +
|
||||
sumr2 += ptr[9] * pFil[1] +
|
||||
ptr[11] * pFil[3] +
|
||||
ptr[13] * pFil[5] +
|
||||
ptr[15] * pFil[7];
|
||||
|
@ -423,88 +356,6 @@ uint FIRFilterSSE::evaluateFilterStereo(float *dest, const float *source, uint n
|
|||
dest += 4;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
/* Similar routine in assembly, again obsoleted due to maintainability
|
||||
_asm
|
||||
{
|
||||
// Very important note: data in 'src' _must_ be aligned to
|
||||
// 16-byte boundary!
|
||||
mov edx, count
|
||||
mov ebx, dword ptr src
|
||||
mov eax, dword ptr dest
|
||||
shr edx, 1
|
||||
|
||||
loop1:
|
||||
// "outer loop" : during each round 2*2 output samples are calculated
|
||||
|
||||
// give prefetch hints to CPU of what data are to be needed soonish
|
||||
prefetcht0 [ebx]
|
||||
prefetcht0 [filterCoeffsLocal]
|
||||
|
||||
mov esi, ebx
|
||||
mov edi, filterCoeffsLocal
|
||||
xorps xmm0, xmm0
|
||||
xorps xmm1, xmm1
|
||||
mov ecx, lengthLocal
|
||||
|
||||
loop2:
|
||||
// "inner loop" : during each round eight FIR filter taps are evaluated for 2*2 samples
|
||||
prefetcht0 [esi + 32] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
prefetcht0 [edi + 32] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
|
||||
movups xmm2, [esi] // possibly unaligned load
|
||||
movups xmm3, [esi + 8] // possibly unaligned load
|
||||
mulps xmm2, [edi]
|
||||
mulps xmm3, [edi]
|
||||
addps xmm0, xmm2
|
||||
addps xmm1, xmm3
|
||||
|
||||
movups xmm4, [esi + 16] // possibly unaligned load
|
||||
movups xmm5, [esi + 24] // possibly unaligned load
|
||||
mulps xmm4, [edi + 16]
|
||||
mulps xmm5, [edi + 16]
|
||||
addps xmm0, xmm4
|
||||
addps xmm1, xmm5
|
||||
|
||||
prefetcht0 [esi + 64] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
prefetcht0 [edi + 64] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
|
||||
movups xmm6, [esi + 32] // possibly unaligned load
|
||||
movups xmm7, [esi + 40] // possibly unaligned load
|
||||
mulps xmm6, [edi + 32]
|
||||
mulps xmm7, [edi + 32]
|
||||
addps xmm0, xmm6
|
||||
addps xmm1, xmm7
|
||||
|
||||
movups xmm4, [esi + 48] // possibly unaligned load
|
||||
movups xmm5, [esi + 56] // possibly unaligned load
|
||||
mulps xmm4, [edi + 48]
|
||||
mulps xmm5, [edi + 48]
|
||||
addps xmm0, xmm4
|
||||
addps xmm1, xmm5
|
||||
|
||||
add esi, 64
|
||||
add edi, 64
|
||||
dec ecx
|
||||
jnz loop2
|
||||
|
||||
// Now xmm0 and xmm1 both have a filtered 2-channel sample each, but we still need
|
||||
// to sum the two hi- and lo-floats of these registers together.
|
||||
|
||||
movhlps xmm2, xmm0 // xmm2 = xmm2_3 xmm2_2 xmm0_3 xmm0_2
|
||||
movlhps xmm2, xmm1 // xmm2 = xmm1_1 xmm1_0 xmm0_3 xmm0_2
|
||||
shufps xmm0, xmm1, 0xe4 // xmm0 = xmm1_3 xmm1_2 xmm0_1 xmm0_0
|
||||
addps xmm0, xmm2
|
||||
|
||||
movaps [eax], xmm0
|
||||
add ebx, 16
|
||||
add eax, 16
|
||||
|
||||
dec edx
|
||||
jnz loop1
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
#endif // ALLOW_SSE
|
||||
#endif // SOUNDTOUCH_ALLOW_SSE
|
||||
|
|
Loading…
Reference in New Issue