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));
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)
{
const SAMPLETYPE *ptr;
@ -98,8 +101,8 @@ uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, ui
for (int i = 0; i < ilength; i ++)
{
suml += ptr[2 * i] * filterCoeffsStereo[2 * i];
sumr += ptr[2 * i + 1] * filterCoeffsStereo[2 * i + 1];
suml += ptr[2 * i] * coeffsStereo[2 * i];
sumr += ptr[2 * i + 1] * coeffsStereo[2 * i + 1];
}
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
@ -133,7 +136,10 @@ uint FIRFilter::evaluateFilterMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint
assert(ilength != 0);
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 ++)
{
const SAMPLETYPE *pSrc = src + j;
@ -143,7 +149,7 @@ uint FIRFilter::evaluateFilterMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint
sum = 0;
for (i = 0; i < ilength; i ++)
{
sum += pSrc[i] * filterCoeffs[i];
sum += pSrc[i] * coeffs[i];
}
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
sum >>= resultDivFactor;
@ -176,8 +182,11 @@ uint FIRFilter::evaluateFilterMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uin
int ilength = length & -8;
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)
{
const SAMPLETYPE *ptr;
@ -194,7 +203,7 @@ uint FIRFilter::evaluateFilterMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uin
for (i = 0; i < ilength; i ++)
{
SAMPLETYPE coef=filterCoeffs[i];
SAMPLETYPE coef=coeffs[i];
for (c = 0; c < numChannels; c ++)
{
sums[c] += ptr[0] * coef;

View file

@ -306,25 +306,31 @@ int TDStretch::seekBestOverlapPositionFull(const SAMPLETYPE *refPos)
// over the permitted range.
bestCorr = calcCrossCorr(refPos, pMidBuffer, norm);
bestCorr = (bestCorr + 0.1) * 0.75;
const int seekLen = seekLength;
const int nChannels = channels;
const SAMPLETYPE *midBuffer = pMidBuffer;
#pragma omp parallel for
for (i = 1; i < seekLength; i ++)
#if defined(_OPENMP)
#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 localNorm;
// Calculates correlation value for the mixing position corresponding to 'i'
#if defined(_OPENMP) || defined(ST_SIMD_AVOID_UNALIGNED)
// in parallel OpenMP mode, can't use norm accumulator version as parallel executor won't
// iterate the loop in sequential order
// 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
// 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);
corr = calcCrossCorrAccumulate(refPos + nChannels * i, midBuffer, norm);
#endif
// 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));
// 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.
// 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 defined(_OPENMP)
#pragma omp critical(soundtouch_bestcorr)
#endif
if (corr > bestCorr)
{
bestCorr = corr;
@ -910,7 +918,9 @@ double TDStretch::calcCrossCorr(const short *mixingPos, const short *compare, do
if (lnorm > maxnorm)
{
// 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)
{
maxnorm = lnorm;

View file

@ -119,7 +119,9 @@ double TDStretchMMX::calcCrossCorr(const short *pV1, const short *pV2, double &d
if (norm > (long)maxnorm)
{
// 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)
{
maxnorm = norm;

View file

@ -41,6 +41,11 @@ else:
# Windows need alloca renamed to _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
# 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(filterCoeffsAlign != NULL);
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'
#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)
{
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
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
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
// at each pass