mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-31 01:59:52 -06:00
Updated SoundTouch library to 1.9.2
This commit is contained in:
307
Externals/soundtouch/TDStretch.cpp
vendored
307
Externals/soundtouch/TDStretch.cpp
vendored
@ -13,10 +13,10 @@
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2014-04-07 01:57:21 +1000 (Mon, 07 Apr 2014) $
|
||||
// Last changed : $Date: 2015-08-09 00:00:15 +0300 (Sun, 09 Aug 2015) $
|
||||
// File revision : $Revision: 1.12 $
|
||||
//
|
||||
// $Id: TDStretch.cpp 195 2014-04-06 15:57:21Z oparviai $
|
||||
// $Id: TDStretch.cpp 226 2015-08-08 21:00:15Z oparviai $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@ -63,7 +63,7 @@ using namespace soundtouch;
|
||||
*****************************************************************************/
|
||||
|
||||
// Table for the hierarchical mixing position seeking algorithm
|
||||
static const short _scanOffsets[5][24]={
|
||||
const short _scanOffsets[5][24]={
|
||||
{ 124, 186, 248, 310, 372, 434, 496, 558, 620, 682, 744, 806,
|
||||
868, 930, 992, 1054, 1116, 1178, 1240, 1302, 1364, 1426, 1488, 0},
|
||||
{-100, -75, -50, -25, 25, 50, 75, 100, 0, 0, 0, 0,
|
||||
@ -94,7 +94,9 @@ TDStretch::TDStretch() : FIFOProcessor(&outputBuffer)
|
||||
bAutoSeqSetting = true;
|
||||
bAutoSeekSetting = true;
|
||||
|
||||
// outDebt = 0;
|
||||
maxnorm = 0;
|
||||
maxnormf = 1e8;
|
||||
|
||||
skipFract = 0;
|
||||
|
||||
tempo = 1.0f;
|
||||
@ -250,7 +252,7 @@ int TDStretch::seekBestOverlapPosition(const SAMPLETYPE *refPos)
|
||||
if (bQuickSeek)
|
||||
{
|
||||
return seekBestOverlapPositionQuick(refPos);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return seekBestOverlapPositionFull(refPos);
|
||||
@ -282,7 +284,6 @@ inline void TDStretch::overlap(SAMPLETYPE *pOutput, const SAMPLETYPE *pInput, ui
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Seeks for the optimal overlap-mixing position. The 'stereo' version of the
|
||||
// routine
|
||||
//
|
||||
@ -292,9 +293,9 @@ inline void TDStretch::overlap(SAMPLETYPE *pOutput, const SAMPLETYPE *pInput, ui
|
||||
int TDStretch::seekBestOverlapPositionFull(const SAMPLETYPE *refPos)
|
||||
{
|
||||
int bestOffs;
|
||||
double bestCorr, corr;
|
||||
double norm;
|
||||
double bestCorr;
|
||||
int i;
|
||||
double norm;
|
||||
|
||||
bestCorr = FLT_MIN;
|
||||
bestOffs = 0;
|
||||
@ -302,14 +303,22 @@ int TDStretch::seekBestOverlapPositionFull(const SAMPLETYPE *refPos)
|
||||
// Scans for the best correlation value by testing each possible position
|
||||
// over the permitted range.
|
||||
bestCorr = calcCrossCorr(refPos, pMidBuffer, norm);
|
||||
|
||||
#pragma omp parallel for
|
||||
for (i = 1; i < seekLength; i ++)
|
||||
{
|
||||
// Calculates correlation value for the mixing position corresponding
|
||||
// to 'i'. Now call "calcCrossCorrAccumulate" that is otherwise same as
|
||||
// "calcCrossCorr", but saves time by reusing & updating previously stored
|
||||
double corr;
|
||||
// Calculates correlation value for the mixing position corresponding to 'i'
|
||||
#ifdef _OPENMP
|
||||
// in parallel OpenMP mode, can't use norm accumulator version as parallel executor won't
|
||||
// iterate the loop in sequential order
|
||||
corr = calcCrossCorr(refPos + channels * i, pMidBuffer, norm);
|
||||
#else
|
||||
// In non-parallel version call "calcCrossCorrAccumulate" that is otherwise same
|
||||
// as "calcCrossCorr", but saves time by reusing & updating previously stored
|
||||
// "norm" value
|
||||
corr = calcCrossCorrAccumulate(refPos + channels * i, pMidBuffer, norm);
|
||||
|
||||
#endif
|
||||
// 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));
|
||||
@ -317,75 +326,184 @@ int TDStretch::seekBestOverlapPositionFull(const SAMPLETYPE *refPos)
|
||||
// Checks for the highest correlation value
|
||||
if (corr > bestCorr)
|
||||
{
|
||||
// For optimal performance, enter critical section only in case that best value found.
|
||||
// in such case repeat 'if' condition as it's possible that parallel execution may have
|
||||
// updated the bestCorr value in the mean time
|
||||
#pragma omp critical
|
||||
if (corr > bestCorr)
|
||||
{
|
||||
bestCorr = corr;
|
||||
bestOffs = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||
adaptNormalizer();
|
||||
#endif
|
||||
|
||||
// clear cross correlation routine state if necessary (is so e.g. in MMX routines).
|
||||
clearCrossCorrState();
|
||||
|
||||
return bestOffs;
|
||||
}
|
||||
|
||||
|
||||
// Quick seek algorithm for improved runtime-performance: First roughly scans through the
|
||||
// correlation area, and then scan surroundings of two best preliminary correlation candidates
|
||||
// with improved precision
|
||||
//
|
||||
// Based on testing:
|
||||
// - This algorithm gives on average 99% as good match as the full algorith
|
||||
// - this quick seek algorithm finds the best match on ~90% of cases
|
||||
// - on those 10% of cases when this algorithm doesn't find best match,
|
||||
// it still finds on average ~90% match vs. the best possible match
|
||||
int TDStretch::seekBestOverlapPositionQuick(const SAMPLETYPE *refPos)
|
||||
{
|
||||
#define _MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#define SCANSTEP 16
|
||||
#define SCANWIND 8
|
||||
|
||||
int bestOffs;
|
||||
int i;
|
||||
int bestOffs2;
|
||||
float bestCorr, corr;
|
||||
float bestCorr2;
|
||||
double norm;
|
||||
|
||||
// note: 'float' types used in this function in case that the platform would need to use software-fp
|
||||
|
||||
bestCorr = FLT_MIN;
|
||||
bestOffs = SCANWIND;
|
||||
bestCorr2 = FLT_MIN;
|
||||
bestOffs2 = 0;
|
||||
|
||||
int best = 0;
|
||||
|
||||
// Scans for the best correlation value by testing each possible position
|
||||
// over the permitted range. Look for two best matches on the first pass to
|
||||
// increase possibility of ideal match.
|
||||
//
|
||||
// Begin from "SCANSTEP" instead of SCANWIND to make the calculation
|
||||
// catch the 'middlepoint' of seekLength vector as that's the a-priori
|
||||
// expected best match position
|
||||
//
|
||||
// Roughly:
|
||||
// - 15% of cases find best result directly on the first round,
|
||||
// - 75% cases find better match on 2nd round around the best match from 1st round
|
||||
// - 10% cases find better match on 2nd round around the 2nd-best-match from 1st round
|
||||
for (i = SCANSTEP; i < seekLength - SCANWIND - 1; i += SCANSTEP)
|
||||
{
|
||||
// Calculates correlation value for the mixing position corresponding
|
||||
// to 'i'
|
||||
corr = (float)calcCrossCorr(refPos + channels*i, pMidBuffer, norm);
|
||||
// heuristic rule to slightly favour values close to mid of the seek range
|
||||
float tmp = (float)(2 * i - seekLength - 1) / (float)seekLength;
|
||||
corr = ((corr + 0.1f) * (1.0f - 0.25f * tmp * tmp));
|
||||
|
||||
// Checks for the highest correlation value
|
||||
if (corr > bestCorr)
|
||||
{
|
||||
// found new best match. keep the previous best as 2nd best match
|
||||
bestCorr2 = bestCorr;
|
||||
bestOffs2 = bestOffs;
|
||||
bestCorr = corr;
|
||||
bestOffs = i;
|
||||
}
|
||||
}
|
||||
// clear cross correlation routine state if necessary (is so e.g. in MMX routines).
|
||||
clearCrossCorrState();
|
||||
|
||||
return bestOffs;
|
||||
}
|
||||
|
||||
|
||||
// Seeks for the optimal overlap-mixing position. The 'stereo' version of the
|
||||
// routine
|
||||
//
|
||||
// The best position is determined as the position where the two overlapped
|
||||
// sample sequences are 'most alike', in terms of the highest cross-correlation
|
||||
// value over the overlapping period
|
||||
int TDStretch::seekBestOverlapPositionQuick(const SAMPLETYPE *refPos)
|
||||
{
|
||||
int j;
|
||||
int bestOffs;
|
||||
double bestCorr, corr;
|
||||
int scanCount, corrOffset, tempOffset;
|
||||
|
||||
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])
|
||||
else if (corr > bestCorr2)
|
||||
{
|
||||
double norm;
|
||||
tempOffset = corrOffset + _scanOffsets[scanCount][j];
|
||||
if (tempOffset >= seekLength) break;
|
||||
|
||||
// Calculates correlation value for the mixing position corresponding
|
||||
// to 'tempOffset'
|
||||
corr = (double)calcCrossCorr(refPos + channels * tempOffset, pMidBuffer, norm);
|
||||
// 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 ++;
|
||||
// not new best, but still new 2nd best match
|
||||
bestCorr2 = corr;
|
||||
bestOffs2 = i;
|
||||
}
|
||||
corrOffset = bestOffs;
|
||||
}
|
||||
|
||||
// Scans surroundings of the found best match with small stepping
|
||||
int end = _MIN(bestOffs + SCANWIND + 1, seekLength);
|
||||
for (i = bestOffs - SCANWIND; i < end; i++)
|
||||
{
|
||||
if (i == bestOffs) continue; // this offset already calculated, thus skip
|
||||
|
||||
// Calculates correlation value for the mixing position corresponding
|
||||
// to 'i'
|
||||
corr = (float)calcCrossCorr(refPos + channels*i, pMidBuffer, norm);
|
||||
// heuristic rule to slightly favour values close to mid of the range
|
||||
float tmp = (float)(2 * i - seekLength - 1) / (float)seekLength;
|
||||
corr = ((corr + 0.1f) * (1.0f - 0.25f * tmp * tmp));
|
||||
|
||||
// Checks for the highest correlation value
|
||||
if (corr > bestCorr)
|
||||
{
|
||||
bestCorr = corr;
|
||||
bestOffs = i;
|
||||
best = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Scans surroundings of the 2nd best match with small stepping
|
||||
end = _MIN(bestOffs2 + SCANWIND + 1, seekLength);
|
||||
for (i = bestOffs2 - SCANWIND; i < end; i++)
|
||||
{
|
||||
if (i == bestOffs2) continue; // this offset already calculated, thus skip
|
||||
|
||||
// Calculates correlation value for the mixing position corresponding
|
||||
// to 'i'
|
||||
corr = (float)calcCrossCorr(refPos + channels*i, pMidBuffer, norm);
|
||||
// heuristic rule to slightly favour values close to mid of the range
|
||||
float tmp = (float)(2 * i - seekLength - 1) / (float)seekLength;
|
||||
corr = ((corr + 0.1f) * (1.0f - 0.25f * tmp * tmp));
|
||||
|
||||
// Checks for the highest correlation value
|
||||
if (corr > bestCorr)
|
||||
{
|
||||
bestCorr = corr;
|
||||
bestOffs = i;
|
||||
best = 2;
|
||||
}
|
||||
}
|
||||
|
||||
// clear cross correlation routine state if necessary (is so e.g. in MMX routines).
|
||||
clearCrossCorrState();
|
||||
|
||||
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||
adaptNormalizer();
|
||||
#endif
|
||||
|
||||
return bestOffs;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// For integer algorithm: adapt normalization factor divider with music so that
|
||||
/// it'll not be pessimistically restrictive that can degrade quality on quieter sections
|
||||
/// yet won't cause integer overflows either
|
||||
void TDStretch::adaptNormalizer()
|
||||
{
|
||||
// Do not adapt normalizer over too silent sequences to avoid averaging filter depleting to
|
||||
// too low values during pauses in music
|
||||
if ((maxnorm > 1000) || (maxnormf > 40000000))
|
||||
{
|
||||
//norm averaging filter
|
||||
maxnormf = 0.9f * maxnormf + 0.1f * (float)maxnorm;
|
||||
|
||||
if ((maxnorm > 800000000) && (overlapDividerBitsNorm < 16))
|
||||
{
|
||||
// large values, so increase divider
|
||||
overlapDividerBitsNorm++;
|
||||
if (maxnorm > 1600000000) overlapDividerBitsNorm++; // extra large value => extra increase
|
||||
}
|
||||
else if ((maxnormf < 1000000) && (overlapDividerBitsNorm > 0))
|
||||
{
|
||||
// extra small values, decrease divider
|
||||
overlapDividerBitsNorm--;
|
||||
}
|
||||
}
|
||||
|
||||
maxnorm = 0;
|
||||
}
|
||||
|
||||
|
||||
/// clear cross correlation routine state if necessary
|
||||
void TDStretch::clearCrossCorrState()
|
||||
{
|
||||
@ -407,7 +525,7 @@ void TDStretch::calcSeqParameters()
|
||||
#define AUTOSEQ_K ((AUTOSEQ_AT_MAX - AUTOSEQ_AT_MIN) / (AUTOSEQ_TEMPO_TOP - AUTOSEQ_TEMPO_LOW))
|
||||
#define AUTOSEQ_C (AUTOSEQ_AT_MIN - (AUTOSEQ_K) * (AUTOSEQ_TEMPO_LOW))
|
||||
|
||||
// seek-window-ms setting values at above low & top tempo
|
||||
// seek-window-ms setting values at above low & top tempoq
|
||||
#define AUTOSEEK_AT_MIN 25.0
|
||||
#define AUTOSEEK_AT_MAX 15.0
|
||||
#define AUTOSEEK_K ((AUTOSEEK_AT_MAX - AUTOSEEK_AT_MIN) / (AUTOSEQ_TEMPO_TOP - AUTOSEQ_TEMPO_LOW))
|
||||
@ -444,7 +562,7 @@ void TDStretch::calcSeqParameters()
|
||||
|
||||
// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower
|
||||
// tempo, larger faster tempo.
|
||||
void TDStretch::setTempo(float newTempo)
|
||||
void TDStretch::setTempo(double newTempo)
|
||||
{
|
||||
int intskip;
|
||||
|
||||
@ -455,7 +573,7 @@ void TDStretch::setTempo(float newTempo)
|
||||
|
||||
// Calculate ideal skip length (according to tempo value)
|
||||
nominalSkip = tempo * (seekWindowLength - overlapLength);
|
||||
intskip = (int)(nominalSkip + 0.5f);
|
||||
intskip = (int)(nominalSkip + 0.5);
|
||||
|
||||
// Calculate how many samples are needed in the 'inputBuffer' to
|
||||
// process another batch of samples
|
||||
@ -721,13 +839,15 @@ void TDStretch::calculateOverlapLength(int aoverlapMs)
|
||||
// calculate overlap length so that it's power of 2 - thus it's easy to do
|
||||
// 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;
|
||||
newOvl = (int)pow(2.0, (int)overlapDividerBits + 1); // +1 => account for -1 above
|
||||
overlapDividerBitsPure = _getClosest2Power((sampleRate * aoverlapMs) / 1000.0) - 1;
|
||||
if (overlapDividerBitsPure > 9) overlapDividerBitsPure = 9;
|
||||
if (overlapDividerBitsPure < 3) overlapDividerBitsPure = 3;
|
||||
newOvl = (int)pow(2.0, (int)overlapDividerBitsPure + 1); // +1 => account for -1 above
|
||||
|
||||
acceptNewOverlapLength(newOvl);
|
||||
|
||||
overlapDividerBitsNorm = overlapDividerBitsPure;
|
||||
|
||||
// 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
|
||||
@ -735,10 +855,10 @@ void TDStretch::calculateOverlapLength(int aoverlapMs)
|
||||
}
|
||||
|
||||
|
||||
double TDStretch::calcCrossCorr(const short *mixingPos, const short *compare, double &norm) const
|
||||
double TDStretch::calcCrossCorr(const short *mixingPos, const short *compare, double &norm)
|
||||
{
|
||||
long corr;
|
||||
long lnorm;
|
||||
unsigned long lnorm;
|
||||
int i;
|
||||
|
||||
corr = lnorm = 0;
|
||||
@ -748,15 +868,19 @@ double TDStretch::calcCrossCorr(const short *mixingPos, const short *compare, do
|
||||
for (i = 0; i < channels * overlapLength; i += 4)
|
||||
{
|
||||
corr += (mixingPos[i] * compare[i] +
|
||||
mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBits; // notice: do intermediate division here to avoid integer overflow
|
||||
mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBitsNorm; // notice: do intermediate division here to avoid integer overflow
|
||||
corr += (mixingPos[i + 2] * compare[i + 2] +
|
||||
mixingPos[i + 3] * compare[i + 3]) >> overlapDividerBits;
|
||||
mixingPos[i + 3] * compare[i + 3]) >> overlapDividerBitsNorm;
|
||||
lnorm += (mixingPos[i] * mixingPos[i] +
|
||||
mixingPos[i + 1] * mixingPos[i + 1]) >> overlapDividerBits; // notice: do intermediate division here to avoid integer overflow
|
||||
mixingPos[i + 1] * mixingPos[i + 1]) >> overlapDividerBitsNorm; // notice: do intermediate division here to avoid integer overflow
|
||||
lnorm += (mixingPos[i + 2] * mixingPos[i + 2] +
|
||||
mixingPos[i + 3] * mixingPos[i + 3]) >> overlapDividerBits;
|
||||
mixingPos[i + 3] * mixingPos[i + 3]) >> overlapDividerBitsNorm;
|
||||
}
|
||||
|
||||
if (lnorm > maxnorm)
|
||||
{
|
||||
maxnorm = lnorm;
|
||||
}
|
||||
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
||||
// done using floating point operation
|
||||
norm = (double)lnorm;
|
||||
@ -765,17 +889,17 @@ double TDStretch::calcCrossCorr(const short *mixingPos, const short *compare, do
|
||||
|
||||
|
||||
/// Update cross-correlation by accumulating "norm" coefficient by previously calculated value
|
||||
double TDStretch::calcCrossCorrAccumulate(const short *mixingPos, const short *compare, double &norm) const
|
||||
double TDStretch::calcCrossCorrAccumulate(const short *mixingPos, const short *compare, double &norm)
|
||||
{
|
||||
long corr;
|
||||
long lnorm;
|
||||
unsigned long lnorm;
|
||||
int i;
|
||||
|
||||
// cancel first normalizer tap from previous round
|
||||
lnorm = 0;
|
||||
for (i = 1; i <= channels; i ++)
|
||||
{
|
||||
lnorm -= (mixingPos[-i] * mixingPos[-i]) >> overlapDividerBits;
|
||||
lnorm -= (mixingPos[-i] * mixingPos[-i]) >> overlapDividerBitsNorm;
|
||||
}
|
||||
|
||||
corr = 0;
|
||||
@ -785,18 +909,23 @@ double TDStretch::calcCrossCorrAccumulate(const short *mixingPos, const short *c
|
||||
for (i = 0; i < channels * overlapLength; i += 4)
|
||||
{
|
||||
corr += (mixingPos[i] * compare[i] +
|
||||
mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBits; // notice: do intermediate division here to avoid integer overflow
|
||||
mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBitsNorm; // notice: do intermediate division here to avoid integer overflow
|
||||
corr += (mixingPos[i + 2] * compare[i + 2] +
|
||||
mixingPos[i + 3] * compare[i + 3]) >> overlapDividerBits;
|
||||
mixingPos[i + 3] * compare[i + 3]) >> overlapDividerBitsNorm;
|
||||
}
|
||||
|
||||
// update normalizer with last samples of this round
|
||||
for (int j = 0; j < channels; j ++)
|
||||
{
|
||||
i --;
|
||||
lnorm += (mixingPos[i] * mixingPos[i]) >> overlapDividerBits;
|
||||
lnorm += (mixingPos[i] * mixingPos[i]) >> overlapDividerBitsNorm;
|
||||
}
|
||||
|
||||
norm += (double)lnorm;
|
||||
if (norm > maxnorm)
|
||||
{
|
||||
maxnorm = (unsigned long)norm;
|
||||
}
|
||||
|
||||
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
||||
// done using floating point operation
|
||||
@ -881,9 +1010,10 @@ void TDStretch::calculateOverlapLength(int overlapInMsec)
|
||||
|
||||
|
||||
/// Calculate cross-correlation
|
||||
double TDStretch::calcCrossCorr(const float *mixingPos, const float *compare, double &norm) const
|
||||
double TDStretch::calcCrossCorr(const float *mixingPos, const float *compare, double &anorm)
|
||||
{
|
||||
double corr;
|
||||
double norm;
|
||||
int i;
|
||||
|
||||
corr = norm = 0;
|
||||
@ -905,12 +1035,13 @@ double TDStretch::calcCrossCorr(const float *mixingPos, const float *compare, do
|
||||
mixingPos[i + 3] * mixingPos[i + 3];
|
||||
}
|
||||
|
||||
anorm = norm;
|
||||
return corr / sqrt((norm < 1e-9 ? 1.0 : norm));
|
||||
}
|
||||
|
||||
|
||||
/// Update cross-correlation by accumulating "norm" coefficient by previously calculated value
|
||||
double TDStretch::calcCrossCorrAccumulate(const float *mixingPos, const float *compare, double &norm) const
|
||||
double TDStretch::calcCrossCorrAccumulate(const float *mixingPos, const float *compare, double &norm)
|
||||
{
|
||||
double corr;
|
||||
int i;
|
||||
|
Reference in New Issue
Block a user