//////////////////////////////////////////////////////////////////////////////// /// /// Win32 version of the AMD 3DNow! optimized routines for AMD K6-2/Athlon /// processors. All 3DNow! 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. /// /// By the way; the performance gain depends heavily on the CPU generation: On /// K6-2 these routines provided speed-up of even 2.4 times, while on Athlon the /// difference to the original routines stayed at unremarkable 8%! Such a small /// improvement on Athlon is due to 3DNow can perform only two operations in /// parallel, and obviously also the Athlon FPU is doing a very good job with /// the standard C floating point routines! Here these routines are anyway, /// although it might not be worth the effort to convert these to GCC platform, /// for Athlon CPU at least. The situation is different regarding the SSE /// optimizations though, thanks to the four parallel operations of SSE that /// already make a difference. /// /// This file is to be compiled in Windows platform with Microsoft Visual C++ /// Compiler. Please see '3dnow_gcc.cpp' for the gcc compiler version for all /// GNU platforms (if file supplied). /// /// NOTICE: If using Visual Studio 6.0, you'll need to install the "Visual C++ /// 6.0 processor pack" update to support 3DNow! instruction set. The update is /// available for download at Microsoft Developers Network, see here: /// http://msdn.microsoft.com/vstudio/downloads/tools/ppack/default.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 @ iki.fi /// SoundTouch WWW: http://www.iki.fi/oparviai/soundtouch /// //////////////////////////////////////////////////////////////////////////////// // // Last changed : $Date$ // File revision : $Revision$ // // $Id$ // //////////////////////////////////////////////////////////////////////////////// // // 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" #ifndef WIN32 #error "wrong platform - this source code file is exclusively for Win32 platform" #endif using namespace soundtouch; #ifdef ALLOW_3DNOW // 3DNow! routines available only with float sample type ////////////////////////////////////////////////////////////////////////////// // // implementation of 3DNow! optimized functions of class 'TDStretch3DNow' // ////////////////////////////////////////////////////////////////////////////// #include "TDStretch.h" #include // these are declared in 'TDStretch.cpp' extern int scanOffsets[4][24]; // Calculates cross correlation of two buffers double TDStretch3DNow::calcCrossCorrStereo(const float *pV1, const float *pV2) const { uint overlapLengthLocal = overlapLength; float corr; // Calculates the cross-correlation value between 'pV1' and 'pV2' vectors /* c-pseudocode: corr = 0; for (i = 0; i < overlapLength / 4; 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; } */ _asm { // give prefetch hints to CPU of what data are to be needed soonish. // give more aggressive hints on pV1 as that changes more between different calls // while pV2 stays the same. prefetch [pV1] prefetch [pV2] prefetch [pV1 + 32] mov eax, dword ptr pV2 mov ebx, dword ptr pV1 pxor mm0, mm0 mov ecx, overlapLengthLocal shr ecx, 2 // div by four loop1: movq mm1, [eax] prefetch [eax + 32] // give a prefetch hint to CPU what data are to be needed soonish pfmul mm1, [ebx] prefetch [ebx + 64] // give a prefetch hint to CPU what data are to be needed soonish movq mm2, [eax + 8] pfadd mm0, mm1 pfmul mm2, [ebx + 8] movq mm3, [eax + 16] pfadd mm0, mm2 pfmul mm3, [ebx + 16] movq mm4, [eax + 24] pfadd mm0, mm3 pfmul mm4, [ebx + 24] add eax, 32 pfadd mm0, mm4 add ebx, 32 dec ecx jnz loop1 // add halfs of mm0 together and return the result. // note: mm1 is used as a dummy parameter only, we actually don't care about it's value pfacc mm0, mm1 movd corr, mm0 femms } return corr; } ////////////////////////////////////////////////////////////////////////////// // // implementation of 3DNow! optimized functions of class 'FIRFilter' // ////////////////////////////////////////////////////////////////////////////// #include "FIRFilter.h" FIRFilter3DNow::FIRFilter3DNow() : FIRFilter() { filterCoeffsUnalign = NULL; } FIRFilter3DNow::~FIRFilter3DNow() { delete[] filterCoeffsUnalign; } // (overloaded) Calculates filter coefficients for 3DNow! routine void FIRFilter3DNow::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 3DNow! // Ensure that filter coeffs array is aligned to 16-byte boundary delete[] filterCoeffsUnalign; filterCoeffsUnalign = new float[2 * newLength + 4]; filterCoeffsAlign = (float *)(((uint)filterCoeffsUnalign + 15) & -16); 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; } } // 3DNow!-optimized version of the filter routine for stereo sound uint FIRFilter3DNow::evaluateFilterStereo(float *dest, const float *src, const uint numSamples) const { float *filterCoeffsLocal = filterCoeffsAlign; uint count = (numSamples - length) & -2; uint lengthLocal = length / 4; assert(length != 0); assert(count % 2 == 0); /* original code: double suml1, suml2; double sumr1, sumr2; uint i, j; for (j = 0; j < count; j += 2) { const float *ptr; suml1 = sumr1 = 0.0; suml2 = sumr2 = 0.0; ptr = src; filterCoeffsLocal = filterCoeffs; for (i = 0; i < lengthLocal; i ++) { // unroll loop for efficiency. suml1 += ptr[0] * filterCoeffsLocal[0] + ptr[2] * filterCoeffsLocal[2] + ptr[4] * filterCoeffsLocal[4] + ptr[6] * filterCoeffsLocal[6]; sumr1 += ptr[1] * filterCoeffsLocal[1] + ptr[3] * filterCoeffsLocal[3] + ptr[5] * filterCoeffsLocal[5] + ptr[7] * filterCoeffsLocal[7]; suml2 += ptr[8] * filterCoeffsLocal[0] + ptr[10] * filterCoeffsLocal[2] + ptr[12] * filterCoeffsLocal[4] + ptr[14] * filterCoeffsLocal[6]; sumr2 += ptr[9] * filterCoeffsLocal[1] + ptr[11] * filterCoeffsLocal[3] + ptr[13] * filterCoeffsLocal[5] + ptr[15] * filterCoeffsLocal[7]; ptr += 16; filterCoeffsLocal += 8; } dest[0] = (float)suml1; dest[1] = (float)sumr1; dest[2] = (float)suml2; dest[3] = (float)sumr2; src += 4; dest += 4; } */ _asm { mov eax, dword ptr dest mov ebx, dword ptr src mov edx, count shr edx, 1 loop1: // "outer loop" : during each round 2*2 output samples are calculated prefetch [ebx] // give a prefetch hint to CPU what data are to be needed soonish prefetch [filterCoeffsLocal] // give a prefetch hint to CPU what data are to be needed soonish mov esi, ebx mov edi, filterCoeffsLocal pxor mm0, mm0 pxor mm1, mm1 mov ecx, lengthLocal loop2: // "inner loop" : during each round four FIR filter taps are evaluated for 2*2 output samples movq mm2, [edi] movq mm3, mm2 prefetch [edi + 32] // give a prefetch hint to CPU what data are to be needed soonish pfmul mm2, [esi] prefetch [esi + 32] // give a prefetch hint to CPU what data are to be needed soonish pfmul mm3, [esi + 8] movq mm4, [edi + 8] movq mm5, mm4 pfadd mm0, mm2 pfmul mm4, [esi + 8] pfadd mm1, mm3 pfmul mm5, [esi + 16] movq mm2, [edi + 16] movq mm6, mm2 pfadd mm0, mm4 pfmul mm2, [esi + 16] pfadd mm1, mm5 pfmul mm6, [esi + 24] movq mm3, [edi + 24] movq mm7, mm3 pfadd mm0, mm2 pfmul mm3, [esi + 24] pfadd mm1, mm6 pfmul mm7, [esi + 32] add esi, 32 pfadd mm0, mm3 add edi, 32 pfadd mm1, mm7 dec ecx jnz loop2 movq [eax], mm0 add ebx, 16 movq [eax + 8], mm1 add eax, 16 dec edx jnz loop1 femms } return count; } #endif // ALLOW_3DNOW