Issue #451 part 1 - use OpenMP for SoundTouch

This commit is contained in:
ownedbywuigi 2026-04-25 09:40:24 +01:00
commit b66a74daf6
5 changed files with 48 additions and 18 deletions

View file

@ -86,8 +86,11 @@ uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, ui
assert((length != 0) && (length == ilength) && (src != NULL) && (dest != NULL) && (filterCoeffs != NULL)); assert((length != 0) && (length == ilength) && (src != NULL) && (dest != NULL) && (filterCoeffs != NULL));
end = 2 * (numSamples - ilength); end = 2 * (numSamples - ilength);
const SAMPLETYPE *coeffsStereo = filterCoeffsStereo;
#pragma omp parallel for #if defined(_OPENMP)
#pragma omp parallel for default(none) shared(src, dest, end, ilength, coeffsStereo) private(j) schedule(static)
#endif
for (j = 0; j < end; j += 2) for (j = 0; j < end; j += 2)
{ {
const SAMPLETYPE *ptr; const SAMPLETYPE *ptr;
@ -98,8 +101,8 @@ uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, ui
for (int i = 0; i < ilength; i ++) for (int i = 0; i < ilength; i ++)
{ {
suml += ptr[2 * i] * filterCoeffsStereo[2 * i]; suml += ptr[2 * i] * coeffsStereo[2 * i];
sumr += ptr[2 * i + 1] * filterCoeffsStereo[2 * i + 1]; sumr += ptr[2 * i + 1] * coeffsStereo[2 * i + 1];
} }
#ifdef SOUNDTOUCH_INTEGER_SAMPLES #ifdef SOUNDTOUCH_INTEGER_SAMPLES
@ -133,7 +136,10 @@ uint FIRFilter::evaluateFilterMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint
assert(ilength != 0); assert(ilength != 0);
end = numSamples - ilength; end = numSamples - ilength;
#pragma omp parallel for const SAMPLETYPE *coeffs = filterCoeffs;
#if defined(_OPENMP)
#pragma omp parallel for default(none) shared(src, dest, end, ilength, coeffs) private(j) schedule(static)
#endif
for (j = 0; j < end; j ++) for (j = 0; j < end; j ++)
{ {
const SAMPLETYPE *pSrc = src + j; const SAMPLETYPE *pSrc = src + j;
@ -143,7 +149,7 @@ uint FIRFilter::evaluateFilterMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint
sum = 0; sum = 0;
for (i = 0; i < ilength; i ++) for (i = 0; i < ilength; i ++)
{ {
sum += pSrc[i] * filterCoeffs[i]; sum += pSrc[i] * coeffs[i];
} }
#ifdef SOUNDTOUCH_INTEGER_SAMPLES #ifdef SOUNDTOUCH_INTEGER_SAMPLES
sum >>= resultDivFactor; sum >>= resultDivFactor;
@ -176,8 +182,11 @@ uint FIRFilter::evaluateFilterMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uin
int ilength = length & -8; int ilength = length & -8;
end = numChannels * (numSamples - ilength); end = numChannels * (numSamples - ilength);
const SAMPLETYPE *coeffs = filterCoeffs;
#pragma omp parallel for #if defined(_OPENMP)
#pragma omp parallel for default(none) shared(src, dest, end, ilength, numChannels, coeffs) private(j) schedule(static)
#endif
for (j = 0; j < end; j += numChannels) for (j = 0; j < end; j += numChannels)
{ {
const SAMPLETYPE *ptr; const SAMPLETYPE *ptr;
@ -194,7 +203,7 @@ uint FIRFilter::evaluateFilterMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uin
for (i = 0; i < ilength; i ++) for (i = 0; i < ilength; i ++)
{ {
SAMPLETYPE coef=filterCoeffs[i]; SAMPLETYPE coef=coeffs[i];
for (c = 0; c < numChannels; c ++) for (c = 0; c < numChannels; c ++)
{ {
sums[c] += ptr[0] * coef; sums[c] += ptr[0] * coef;

View file

@ -306,25 +306,31 @@ int TDStretch::seekBestOverlapPositionFull(const SAMPLETYPE *refPos)
// over the permitted range. // over the permitted range.
bestCorr = calcCrossCorr(refPos, pMidBuffer, norm); bestCorr = calcCrossCorr(refPos, pMidBuffer, norm);
bestCorr = (bestCorr + 0.1) * 0.75; bestCorr = (bestCorr + 0.1) * 0.75;
const int seekLen = seekLength;
const int nChannels = channels;
const SAMPLETYPE *midBuffer = pMidBuffer;
#pragma omp parallel for #if defined(_OPENMP)
for (i = 1; i < seekLength; i ++) #pragma omp parallel for default(none) shared(refPos, bestCorr, bestOffs, seekLen, nChannels, midBuffer) private(i) schedule(static)
#endif
for (i = 1; i < seekLen; i ++)
{ {
double corr; double corr;
double localNorm;
// Calculates correlation value for the mixing position corresponding to 'i' // Calculates correlation value for the mixing position corresponding to 'i'
#if defined(_OPENMP) || defined(ST_SIMD_AVOID_UNALIGNED) #if defined(_OPENMP) || defined(ST_SIMD_AVOID_UNALIGNED)
// in parallel OpenMP mode, can't use norm accumulator version as parallel executor won't // in parallel OpenMP mode, can't use norm accumulator version as parallel executor won't
// iterate the loop in sequential order // iterate the loop in sequential order
// in SIMD mode, avoid accumulator version to allow avoiding unaligned positions // in SIMD mode, avoid accumulator version to allow avoiding unaligned positions
corr = calcCrossCorr(refPos + channels * i, pMidBuffer, norm); corr = calcCrossCorr(refPos + nChannels * i, midBuffer, localNorm);
#else #else
// In non-parallel version call "calcCrossCorrAccumulate" that is otherwise same // In non-parallel version call "calcCrossCorrAccumulate" that is otherwise same
// as "calcCrossCorr", but saves time by reusing & updating previously stored // as "calcCrossCorr", but saves time by reusing & updating previously stored
// "norm" value // "norm" value
corr = calcCrossCorrAccumulate(refPos + channels * i, pMidBuffer, norm); corr = calcCrossCorrAccumulate(refPos + nChannels * i, midBuffer, norm);
#endif #endif
// heuristic rule to slightly favour values close to mid of the range // heuristic rule to slightly favour values close to mid of the range
double tmp = (double)(2 * i - seekLength) / (double)seekLength; double tmp = (double)(2 * i - seekLen) / (double)seekLen;
corr = ((corr + 0.1) * (1.0 - 0.25 * tmp * tmp)); corr = ((corr + 0.1) * (1.0 - 0.25 * tmp * tmp));
// Checks for the highest correlation value // Checks for the highest correlation value
@ -333,7 +339,9 @@ int TDStretch::seekBestOverlapPositionFull(const SAMPLETYPE *refPos)
// For optimal performance, enter critical section only in case that best value found. // 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 // in such case repeat 'if' condition as it's possible that parallel execution may have
// updated the bestCorr value in the mean time // updated the bestCorr value in the mean time
#pragma omp critical #if defined(_OPENMP)
#pragma omp critical(soundtouch_bestcorr)
#endif
if (corr > bestCorr) if (corr > bestCorr)
{ {
bestCorr = corr; bestCorr = corr;
@ -910,7 +918,9 @@ double TDStretch::calcCrossCorr(const short *mixingPos, const short *compare, do
if (lnorm > maxnorm) if (lnorm > maxnorm)
{ {
// modify 'maxnorm' inside critical section to avoid multi-access conflict if in OpenMP mode // modify 'maxnorm' inside critical section to avoid multi-access conflict if in OpenMP mode
#pragma omp critical #if defined(_OPENMP)
#pragma omp critical(soundtouch_maxnorm)
#endif
if (lnorm > maxnorm) if (lnorm > maxnorm)
{ {
maxnorm = lnorm; maxnorm = lnorm;

View file

@ -119,7 +119,9 @@ double TDStretchMMX::calcCrossCorr(const short *pV1, const short *pV2, double &d
if (norm > (long)maxnorm) if (norm > (long)maxnorm)
{ {
// modify 'maxnorm' inside critical section to avoid multi-access conflict if in OpenMP mode // modify 'maxnorm' inside critical section to avoid multi-access conflict if in OpenMP mode
#pragma omp critical #if defined(_OPENMP)
#pragma omp critical(soundtouch_maxnorm)
#endif
if (norm > (long)maxnorm) if (norm > (long)maxnorm)
{ {
maxnorm = norm; maxnorm = norm;

View file

@ -41,6 +41,11 @@ else:
# Windows need alloca renamed to _alloca # Windows need alloca renamed to _alloca
DEFINES['alloca'] = '_alloca' DEFINES['alloca'] = '_alloca'
# Use standardized OpenMP parallelization for SoundTouch hot loops.
if CONFIG['CC_TYPE'] == 'gcc':
CXXFLAGS += ['-fopenmp']
OS_LIBS += ['gomp']
DEFINES['BUILDING_SOUNDTOUCH'] = 1 DEFINES['BUILDING_SOUNDTOUCH'] = 1
# We allow warnings for third-party code that can be updated from upstream. # We allow warnings for third-party code that can be updated from upstream.

View file

@ -250,9 +250,13 @@ uint FIRFilterSSE::evaluateFilterStereo(float *dest, const float *source, uint n
assert((length % 8) == 0); assert((length % 8) == 0);
assert(filterCoeffsAlign != NULL); assert(filterCoeffsAlign != NULL);
assert(((ulongptr)filterCoeffsAlign) % 16 == 0); assert(((ulongptr)filterCoeffsAlign) % 16 == 0);
const uint filterLength = length;
const float *coeffsAlign = filterCoeffsAlign;
// filter is evaluated for two stereo samples with each iteration, thus use of 'j += 2' // filter is evaluated for two stereo samples with each iteration, thus use of 'j += 2'
#pragma omp parallel for #if defined(_OPENMP)
#pragma omp parallel for default(none) shared(source, dest, count, filterLength, coeffsAlign) private(j) schedule(static)
#endif
for (j = 0; j < count; j += 2) for (j = 0; j < count; j += 2)
{ {
const float *pSrc; const float *pSrc;
@ -263,11 +267,11 @@ uint FIRFilterSSE::evaluateFilterStereo(float *dest, const float *source, uint n
pSrc = (const float*)source + j * 2; // source audio data pSrc = (const float*)source + j * 2; // source audio data
pDest = dest + j * 2; // destination audio data pDest = dest + j * 2; // destination audio data
pFil = (const __m128*)filterCoeffsAlign; // filter coefficients. NOTE: Assumes coefficients pFil = (const __m128*)coeffsAlign; // filter coefficients. NOTE: Assumes coefficients
// are aligned to 16-byte boundary // are aligned to 16-byte boundary
sum1 = sum2 = _mm_setzero_ps(); sum1 = sum2 = _mm_setzero_ps();
for (i = 0; i < length / 8; i ++) for (i = 0; i < filterLength / 8; i ++)
{ {
// Unroll loop for efficiency & calculate filter for 2*2 stereo samples // Unroll loop for efficiency & calculate filter for 2*2 stereo samples
// at each pass // at each pass