123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366 |
- ////////////////////////////////////////////////////////////////////////////////
- ///
- /// 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
- /// 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
- /// perform a search with keywords "processor pack".
- ///
- /// Author : Copyright (c) Olli Parviainen
- /// Author e-mail : oparviai 'at' iki.fi
- /// SoundTouch WWW: http://www.surina.net/soundtouch
- ///
- ////////////////////////////////////////////////////////////////////////////////
- //
- // 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"
- using namespace soundtouch;
- #ifdef SOUNDTOUCH_ALLOW_SSE
- // SSE routines available only with float sample type
- //////////////////////////////////////////////////////////////////////////////
- //
- // implementation of SSE optimized functions of class 'TDStretchSSE'
- //
- //////////////////////////////////////////////////////////////////////////////
- #include "TDStretch.h"
- #include <xmmintrin.h>
- #include <math.h>
- // Calculates cross correlation of two buffers
- double TDStretchSSE::calcCrossCorr(const float *pV1, const float *pV2, double &anorm)
- {
- 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
- // 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 SOUNDTOUCH_ALLOW_NONEXACT_SIMD_OPTIMIZATION is provided
- // for choosing if this little cheating is allowed.
- #ifdef ST_SIMD_AVOID_UNALIGNED
- // Little cheating allowed, return valid correlation only for
- // aligned locations, meaning every second round for stereo sound.
- #define _MM_LOAD _mm_load_ps
- 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
- // ensure overlapLength is divisible by 8
- assert((overlapLength % 8) == 0);
- // Calculates the cross-correlation value between 'pV1' and 'pV2' vectors
- // Note: pV2 _must_ be aligned to 16-bit boundary, pV1 need not.
- pVec1 = (const float*)pV1;
- pVec2 = (const __m128*)pV2;
- vSum = vNorm = _mm_setzero_ps();
- // 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]
- vTemp = _MM_LOAD(pVec1);
- vSum = _mm_add_ps(vSum, _mm_mul_ps(vTemp ,pVec2[0]));
- vNorm = _mm_add_ps(vNorm, _mm_mul_ps(vTemp ,vTemp));
- // vSum += pV1[4..7] * pV2[4..7]
- vTemp = _MM_LOAD(pVec1 + 4);
- vSum = _mm_add_ps(vSum, _mm_mul_ps(vTemp, pVec2[1]));
- vNorm = _mm_add_ps(vNorm, _mm_mul_ps(vTemp ,vTemp));
- // vSum += pV1[8..11] * pV2[8..11]
- vTemp = _MM_LOAD(pVec1 + 8);
- vSum = _mm_add_ps(vSum, _mm_mul_ps(vTemp, pVec2[2]));
- vNorm = _mm_add_ps(vNorm, _mm_mul_ps(vTemp ,vTemp));
- // vSum += pV1[12..15] * pV2[12..15]
- vTemp = _MM_LOAD(pVec1 + 12);
- vSum = _mm_add_ps(vSum, _mm_mul_ps(vTemp, pVec2[3]));
- vNorm = _mm_add_ps(vNorm, _mm_mul_ps(vTemp ,vTemp));
- pVec1 += 16;
- pVec2 += 4;
- }
- // return value = vSum[0] + vSum[1] + vSum[2] + vSum[3]
- float *pvNorm = (float*)&vNorm;
- float norm = (pvNorm[0] + pvNorm[1] + pvNorm[2] + pvNorm[3]);
- anorm = norm;
- float *pvSum = (float*)&vSum;
- return (double)(pvSum[0] + pvSum[1] + pvSum[2] + pvSum[3]) / sqrt(norm < 1e-9 ? 1.0 : norm);
- /* This is approximately corresponding routine in C-language yet without normalization:
- double corr, norm;
- uint i;
- // Calculates the cross-correlation value between 'pV1' and 'pV2' vectors
- corr = norm = 0.0;
- for (i = 0; i < channels * overlapLength / 16; i ++)
- {
- corr += pV1[0] * pV2[0] +
- pV1[1] * pV2[1] +
- pV1[2] * pV2[2] +
- pV1[3] * pV2[3] +
- pV1[4] * pV2[4] +
- pV1[5] * pV2[5] +
- pV1[6] * pV2[6] +
- pV1[7] * pV2[7] +
- pV1[8] * pV2[8] +
- pV1[9] * pV2[9] +
- pV1[10] * pV2[10] +
- pV1[11] * pV2[11] +
- pV1[12] * pV2[12] +
- pV1[13] * pV2[13] +
- pV1[14] * pV2[14] +
- pV1[15] * pV2[15];
- for (j = 0; j < 15; j ++) norm += pV1[j] * pV1[j];
- pV1 += 16;
- pV2 += 16;
- }
- return corr / sqrt(norm);
- */
- }
- double TDStretchSSE::calcCrossCorrAccumulate(const float *pV1, const float *pV2, double &norm)
- {
- // call usual calcCrossCorr function because SSE does not show big benefit of
- // accumulating "norm" value, and also the "norm" rolling algorithm would get
- // complicated due to SSE-specific alignment-vs-nonexact correlation rules.
- return calcCrossCorr(pV1, pV2, norm);
- }
- //////////////////////////////////////////////////////////////////////////////
- //
- // implementation of SSE optimized functions of class 'FIRFilter'
- //
- //////////////////////////////////////////////////////////////////////////////
- #include "FIRFilter.h"
- FIRFilterSSE::FIRFilterSSE() : FIRFilter()
- {
- filterCoeffsAlign = nullptr;
- filterCoeffsUnalign = nullptr;
- }
- FIRFilterSSE::~FIRFilterSSE()
- {
- delete[] filterCoeffsUnalign;
- filterCoeffsAlign = nullptr;
- filterCoeffsUnalign = nullptr;
- }
- // (overloaded) Calculates filter coefficients for SSE routine
- void FIRFilterSSE::setCoefficients(const float *coeffs, uint newLength, uint uResultDivFactor)
- {
- uint i;
- float fDivider;
- 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 SSE
- // Ensure that filter coeffs array is aligned to 16-byte boundary
- delete[] filterCoeffsUnalign;
- filterCoeffsUnalign = new float[2 * newLength + 4];
- filterCoeffsAlign = (float *)SOUNDTOUCH_ALIGN_POINTER_16(filterCoeffsUnalign);
- fDivider = (float)resultDivider;
- // rearrange the filter coefficients for mmx routines
- for (i = 0; i < newLength; i ++)
- {
- filterCoeffsAlign[2 * i + 0] =
- filterCoeffsAlign[2 * i + 1] = coeffs[i + 0] / fDivider;
- }
- }
- // SSE-optimized version of the filter routine for stereo sound
- uint FIRFilterSSE::evaluateFilterStereo(float *dest, const float *source, uint numSamples) const
- {
- int count = (int)((numSamples - length) & (uint)-2);
- int j;
- assert(count % 2 == 0);
- if (count < 2) return 0;
- assert(source != nullptr);
- assert(dest != nullptr);
- assert((length % 8) == 0);
- assert(filterCoeffsAlign != nullptr);
- assert(((ulongptr)filterCoeffsAlign) % 16 == 0);
- // filter is evaluated for two stereo samples with each iteration, thus use of 'j += 2'
- #pragma omp parallel for
- for (j = 0; j < count; j += 2)
- {
- const float *pSrc;
- float *pDest;
- const __m128 *pFil;
- __m128 sum1, sum2;
- uint i;
- 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
- // are aligned to 16-byte boundary
- sum1 = sum2 = _mm_setzero_ps();
- for (i = 0; i < length / 8; i ++)
- {
- // 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
- // sum2 is accu for 2*2 filtered stereo sound data for the next sound sample offset.
- sum1 = _mm_add_ps(sum1, _mm_mul_ps(_mm_loadu_ps(pSrc) , pFil[0]));
- sum2 = _mm_add_ps(sum2, _mm_mul_ps(_mm_loadu_ps(pSrc + 2), pFil[0]));
- sum1 = _mm_add_ps(sum1, _mm_mul_ps(_mm_loadu_ps(pSrc + 4), pFil[1]));
- sum2 = _mm_add_ps(sum2, _mm_mul_ps(_mm_loadu_ps(pSrc + 6), pFil[1]));
- sum1 = _mm_add_ps(sum1, _mm_mul_ps(_mm_loadu_ps(pSrc + 8) , pFil[2]));
- sum2 = _mm_add_ps(sum2, _mm_mul_ps(_mm_loadu_ps(pSrc + 10), pFil[2]));
- sum1 = _mm_add_ps(sum1, _mm_mul_ps(_mm_loadu_ps(pSrc + 12), pFil[3]));
- sum2 = _mm_add_ps(sum2, _mm_mul_ps(_mm_loadu_ps(pSrc + 14), pFil[3]));
- pSrc += 16;
- pFil += 4;
- }
- // Now sum1 and sum2 both have a filtered 2-channel sample each, but we still need
- // to sum the two hi- and lo-floats of these registers together.
- // post-shuffle & add the filtered values and store to dest.
- _mm_storeu_ps(pDest, _mm_add_ps(
- _mm_shuffle_ps(sum1, sum2, _MM_SHUFFLE(1,0,3,2)), // s2_1 s2_0 s1_3 s1_2
- _mm_shuffle_ps(sum1, sum2, _MM_SHUFFLE(3,2,1,0)) // s2_3 s2_2 s1_1 s1_0
- ));
- }
- // Ideas for further improvement:
- // 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
- // 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
- organized coefficients though.
- double suml1, suml2;
- double sumr1, sumr2;
- uint i, j;
- for (j = 0; j < count; j += 2)
- {
- const float *ptr;
- const float *pFil;
- suml1 = sumr1 = 0.0;
- suml2 = sumr2 = 0.0;
- ptr = src;
- pFil = filterCoeffs;
- for (i = 0; i < lengthLocal; i ++)
- {
- // unroll loop for efficiency.
- suml1 += ptr[0] * pFil[0] +
- ptr[2] * pFil[2] +
- ptr[4] * pFil[4] +
- ptr[6] * pFil[6];
- sumr1 += ptr[1] * pFil[1] +
- ptr[3] * pFil[3] +
- ptr[5] * pFil[5] +
- ptr[7] * pFil[7];
- suml2 += ptr[8] * pFil[0] +
- ptr[10] * pFil[2] +
- ptr[12] * pFil[4] +
- ptr[14] * pFil[6];
- sumr2 += ptr[9] * pFil[1] +
- ptr[11] * pFil[3] +
- ptr[13] * pFil[5] +
- ptr[15] * pFil[7];
- ptr += 16;
- pFil += 8;
- }
- dest[0] = (float)suml1;
- dest[1] = (float)sumr1;
- dest[2] = (float)suml2;
- dest[3] = (float)sumr2;
- src += 4;
- dest += 4;
- }
- */
- }
- #endif // SOUNDTOUCH_ALLOW_SSE
|