/* * EQ.cpp * ------ * Purpose: Mixing code for equalizer. * Notes : Ugh... This should really be removed at some point. * Authors: Olivier Lapicque * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "../soundlib/MixerLoops.h" #include "../sounddsp/EQ.h" #include OPENMPT_NAMESPACE_BEGIN #ifndef NO_EQ #define EQ_BANDWIDTH 2.0 #define EQ_ZERO 0.000001 static constexpr UINT gEqLinearToDB[33] = { 16, 19, 22, 25, 28, 31, 34, 37, 40, 43, 46, 49, 52, 55, 58, 61, 64, 76, 88, 100, 112, 124, 136, 148, 160, 172, 184, 196, 208, 220, 232, 244, 256 }; static constexpr EQBANDSTRUCT gEQDefaults[MAX_EQ_BANDS*2] = { // Default: Flat EQ {0,0,0,0,0, 0,0,0,0, 1, 120, false}, {0,0,0,0,0, 0,0,0,0, 1, 600, false}, {0,0,0,0,0, 0,0,0,0, 1, 1200, false}, {0,0,0,0,0, 0,0,0,0, 1, 3000, false}, {0,0,0,0,0, 0,0,0,0, 1, 6000, false}, {0,0,0,0,0, 0,0,0,0, 1, 10000, false}, {0,0,0,0,0, 0,0,0,0, 1, 120, false}, {0,0,0,0,0, 0,0,0,0, 1, 600, false}, {0,0,0,0,0, 0,0,0,0, 1, 1200, false}, {0,0,0,0,0, 0,0,0,0, 1, 3000, false}, {0,0,0,0,0, 0,0,0,0, 1, 6000, false}, {0,0,0,0,0, 0,0,0,0, 1, 10000, false}, }; #ifdef ENABLE_X86 #if MPT_COMPILER_MSVC #pragma warning(push) #pragma warning(disable:4100) #endif // MPT_COMPILER_MSVC #define PBS_A0 DWORD PTR [eax + EQBANDSTRUCT.a0] #define PBS_A1 DWORD PTR [eax + EQBANDSTRUCT.a1] #define PBS_A2 DWORD PTR [eax + EQBANDSTRUCT.a2] #define PBS_B1 DWORD PTR [eax + EQBANDSTRUCT.b1] #define PBS_B2 DWORD PTR [eax + EQBANDSTRUCT.b2] #define PBS_X1 DWORD PTR [eax + EQBANDSTRUCT.x1] #define PBS_X2 DWORD PTR [eax + EQBANDSTRUCT.x2] #define PBS_Y1 DWORD PTR [eax + EQBANDSTRUCT.y1] #define PBS_Y2 DWORD PTR [eax + EQBANDSTRUCT.y2] static void X86_EQFilter(EQBANDSTRUCT *pbs, float32 *pbuffer, UINT nCount) { _asm { mov eax, pbs // eax = pbs mov edx, nCount // edx = nCount mov ecx, pbuffer // ecx = pbuffer fld PBS_Y2 // ST(3)=y2 fld PBS_Y1 // ST(2)=y1 fld PBS_X2 // ST(1)=x2 fld PBS_X1 // ST(0)=x1 EQ_Loop: fld DWORD PTR [ecx] // ST(0):x ST(1):x1 ST(2):x2 ST(3):y1 ST(4):y2 fld PBS_A0 // ST(0):a0 ST(1):x ST(2):x1 ST(3):x2 ST(4):y1 ST(5):y2 fmul ST(0), ST(1) // ST(0):a0*x fld PBS_A1 // ST(0):a1 ST(1):a0*x ST(2):x ST(3):x1 ST(4):x2 ST(5):y1 ST(6):y2 fmul ST(0), ST(3) // ST(0):a1*x1 add ecx, 4 faddp ST(1), ST(0) fld PBS_A2 fmul ST(0), ST(4) faddp ST(1), ST(0) fld PBS_B1 fmul ST(0), ST(5) faddp ST(1), ST(0) fld PBS_B2 fmul ST(0), ST(6) sub edx, 1 faddp ST(1), ST(0) fst DWORD PTR [ecx-4] // *pbuffer = a0*x+a1*x1+a2*x2+b1*y1+b2*y2 // Here, ST(0)=y ST(1)=x ST(2)=x1 ST(3)=x2 ST(4)=y1 ST(5)=y2 fxch ST(4) // y1=y fstp ST(5) // y2=y1 // Here, ST(0)=x ST(1)=x1 ST(2)=x2 ST(3)=y1 ST(4)=y2 fxch ST(1) // x1=x fstp ST(2) // x2=x1 jnz EQ_Loop // Store x1,y1,x2,y2 and pop FPU stack fstp PBS_X1 fstp PBS_X2 fstp PBS_Y1 fstp PBS_Y2 } } #if defined(ENABLE_X86) && defined(ENABLE_SSE) static void SSE_StereoEQ(EQBANDSTRUCT *pbl, EQBANDSTRUCT *pbr, float32 *pbuffer, UINT nCount) { static constexpr float gk1 = 1.0f; _asm { mov eax, pbl mov edx, pbr mov ebx, pbuffer mov ecx, nCount movss xmm0, [eax+EQBANDSTRUCT.Gain] movss xmm1, gk1 comiss xmm0, xmm1 jne doeq movss xmm0, [edx+EQBANDSTRUCT.Gain] comiss xmm0, xmm1 je done doeq: test ecx, ecx jz done movss xmm6, [eax+EQBANDSTRUCT.a1] movss xmm7, [eax+EQBANDSTRUCT.a2] movss xmm4, [eax+EQBANDSTRUCT.x1] movss xmm5, [eax+EQBANDSTRUCT.x2] movlhps xmm6, xmm7 // xmm6 = [ 0 | a2 | 0 | a1 ] movlhps xmm4, xmm5 // xmm4 = [ 0 | x2 | 0 | x1 ] movss xmm2, [edx+EQBANDSTRUCT.a1] movss xmm3, [edx+EQBANDSTRUCT.a2] movss xmm0, [edx+EQBANDSTRUCT.x1] movss xmm1, [edx+EQBANDSTRUCT.x2] movlhps xmm2, xmm3 // xmm2 = [ 0 | a'2 | 0 | a'1 ] movlhps xmm0, xmm1 // xmm0 = [ 0 | x'2 | 0 | x'1 ] shufps xmm6, xmm2, 0x88 // xmm6 = [ a'2 | a'1 | a2 | a1 ] shufps xmm4, xmm0, 0x88 // xmm4 = [ x'2 | x'1 | x2 | x1 ] shufps xmm6, xmm6, 0xD8 // xmm6 = [ a'2 | a2 | a'1 | a1 ] shufps xmm4, xmm4, 0xD8 // xmm4 = [ x'2 | x2 | x'1 | x1 ] movss xmm7, [eax+EQBANDSTRUCT.b1] movss xmm0, [eax+EQBANDSTRUCT.b2] movss xmm2, [edx+EQBANDSTRUCT.b1] movss xmm1, [edx+EQBANDSTRUCT.b2] movlhps xmm7, xmm0 // xmm7 = [ 0 | b2 | 0 | b1 ] movlhps xmm2, xmm1 // xmm2 = [ 0 | b'2 | 0 | b'1 ] shufps xmm7, xmm2, 0x88 // xmm7 = [ b'2 | b'1 | b2 | b1 ] shufps xmm7, xmm7, 0xD8 // xmm7 = [ b'2 | b2 | b'1 | b1 ] movss xmm5, [eax+EQBANDSTRUCT.y1] movss xmm1, [eax+EQBANDSTRUCT.y2] movss xmm3, [edx+EQBANDSTRUCT.y1] movss xmm0, [edx+EQBANDSTRUCT.y2] movlhps xmm5, xmm1 // xmm5 = [ 0 | y2 | 0 | y1 ] movlhps xmm3, xmm0 // xmm3 = [ 0 | y'2 | 0 | y'1 ] shufps xmm5, xmm3, 0x88 // xmm5 = [ y'2 | y'1 | y2 | y1 ] shufps xmm5, xmm5, 0xD8 // xmm5 = [ y'2 | y2 | y'1 | y1 ] movss xmm3, [eax+EQBANDSTRUCT.a0] movss xmm2, [edx+EQBANDSTRUCT.a0] shufps xmm3, xmm2, 0x88 shufps xmm3, xmm3, 0xD8 // xmm3 = [ 0 | 0 | a'0 | a0 ] mainloop: movlps xmm0, qword ptr [ebx] add ebx, 8 movaps xmm1, xmm5 // xmm1 = [ y2r | y2l | y1r | y1l ] mulps xmm1, xmm7 // xmm1 = [b2r*y2r|b2l*y2l|b1r*y1r|b1l*y1l] movaps xmm2, xmm4 // xmm2 = [ x2r | x2l | x1r | x1l ] mulps xmm2, xmm6 // xmm6 = [a2r*x2r|a2l*x2l|a1r*x1r|a1l*x1l] shufps xmm4, xmm0, 0x44 // xmm4 = [ xr | xl | x1r | x1l ] mulps xmm0, xmm3 // xmm0 = [ 0 | 0 |a0r*xr|a0l*xl] shufps xmm4, xmm4, 0x4E // xmm4 = [ x1r | x1l | xr | xl ] addps xmm1, xmm2 // xmm1 = [b2r*y2r+a2r*x2r|b2l*y2l+a2l*x2l|b1r*y1r+a1r*x1r|b1l*y1l+a1l*x1l] addps xmm1, xmm0 // xmm1 = [b2r*y2r+a2r*x2r|b2l*y2l+a2l*x2l|b1r*y1r+a1r*x1r+a0r*xr|b1l*y1l+a1l*x1l+a0l*xl] sub ecx, 1 movhlps xmm0, xmm1 addps xmm0, xmm1 // xmm0 = [ ? | ? | yr(n) | yl(n) ] movlps [ebx-8], xmm0 shufps xmm0, xmm5, 0x44 movaps xmm5, xmm0 jnz mainloop movhlps xmm0, xmm4 movhlps xmm1, xmm5 movss [eax+EQBANDSTRUCT.x1], xmm4 movss [eax+EQBANDSTRUCT.x2], xmm0 movss [eax+EQBANDSTRUCT.y1], xmm5 movss [eax+EQBANDSTRUCT.y2], xmm1 shufps xmm4, xmm4, 0x01 shufps xmm0, xmm0, 0x01 shufps xmm5, xmm5, 0x01 shufps xmm1, xmm1, 0x01 movss [edx+EQBANDSTRUCT.x1], xmm4 movss [edx+EQBANDSTRUCT.x2], xmm0 movss [edx+EQBANDSTRUCT.y1], xmm5 movss [edx+EQBANDSTRUCT.y2], xmm1 done:; } } #endif // ENABLE_X86 && ENABLE_SSE #if MPT_COMPILER_MSVC #pragma warning(pop) #endif // MPT_COMPILER_MSVC #endif static void EQFilter(EQBANDSTRUCT *pbs, float32 *pbuffer, UINT nCount) { for (UINT i=0; ia1 * pbs->x1 + pbs->a2 * pbs->x2 + pbs->a0 * x + pbs->b1 * pbs->y1 + pbs->b2 * pbs->y2; pbs->x2 = pbs->x1; pbs->y2 = pbs->y1; pbs->x1 = x; pbuffer[i] = y; pbs->y1 = y; } } void CEQ::ProcessMono(int *pbuffer, float *MixFloatBuffer, UINT nCount) { MonoMixToFloat(pbuffer, MixFloatBuffer, nCount, 1.0f/MIXING_SCALEF); for (UINT b=0; b 0.45f) gEQ[band].Gain = 1; // if (f > 0.25) f = 0.25; // k = tan(PI*f); k = f * 3.141592654f; k = k + k*f; // if (k > (float32)0.707) k = (float32)0.707; k2 = k*k; v0 = gEQ[band].Gain; v1 = 1; if (gEQ[band].Gain < 1.0) { v0 *= (0.5f/EQ_BANDWIDTH); v1 *= (0.5f/EQ_BANDWIDTH); } else { v0 *= (1.0f/EQ_BANDWIDTH); v1 *= (1.0f/EQ_BANDWIDTH); } r = (1 + v0*k + k2) / (1 + v1*k + k2); if (r != gEQ[band].a0) { gEQ[band].a0 = r; b = true; } r = 2 * (k2 - 1) / (1 + v1*k + k2); if (r != gEQ[band].a1) { gEQ[band].a1 = r; b = true; } r = (1 - v0*k + k2) / (1 + v1*k + k2); if (r != gEQ[band].a2) { gEQ[band].a2 = r; b = true; } r = - 2 * (k2 - 1) / (1 + v1*k + k2); if (r != gEQ[band].b1) { gEQ[band].b1 = r; b = true; } r = - (1 - v1*k + k2) / (1 + v1*k + k2); if (r != gEQ[band].b2) { gEQ[band].b2 = r; b = true; } if (b) { gEQ[band].x1 = 0; gEQ[band].x2 = 0; gEQ[band].y1 = 0; gEQ[band].y2 = 0; } } else { gEQ[band].a0 = 0; gEQ[band].a1 = 0; gEQ[band].a2 = 0; gEQ[band].b1 = 0; gEQ[band].b2 = 0; gEQ[band].x1 = 0; gEQ[band].x2 = 0; gEQ[band].y1 = 0; gEQ[band].y2 = 0; } } void CEQ::SetEQGains(const UINT *pGains, UINT nGains, const UINT *pFreqs, bool bReset, DWORD MixingFreq) { for (UINT i=0; i 32) n = 32; g = ((float32)gEqLinearToDB[n]) / 64.0f; if (pFreqs) f = (float32)(int)pFreqs[i]; } else { g = 1; } gEQ[i].Gain = g; gEQ[i].CenterFrequency = f; gEQ[i+MAX_EQ_BANDS].Gain = g; gEQ[i+MAX_EQ_BANDS].CenterFrequency = f; if (f > 20.0f) { gEQ[i].bEnable = true; gEQ[i+MAX_EQ_BANDS].bEnable = true; } else { gEQ[i].bEnable = false; gEQ[i+MAX_EQ_BANDS].bEnable = false; } } Initialize(bReset, MixingFreq); } void CQuadEQ::Initialize(bool bReset, DWORD MixingFreq) { front.Initialize(bReset, MixingFreq); rear.Initialize(bReset, MixingFreq); } void CQuadEQ::SetEQGains(const UINT *pGains, UINT nGains, const UINT *pFreqs, bool bReset, DWORD MixingFreq) { front.SetEQGains(pGains, nGains, pFreqs, bReset, MixingFreq); rear.SetEQGains(pGains, nGains, pFreqs, bReset, MixingFreq); } void CQuadEQ::Process(int *frontBuffer, int *rearBuffer, UINT nCount, UINT nChannels) { if(nChannels == 1) { front.ProcessMono(frontBuffer, EQTempFloatBuffer, nCount); } else if(nChannels == 2) { front.ProcessStereo(frontBuffer, EQTempFloatBuffer, nCount); } else if(nChannels == 4) { front.ProcessStereo(frontBuffer, EQTempFloatBuffer, nCount); rear.ProcessStereo(rearBuffer, EQTempFloatBuffer, nCount); } } #else MPT_MSVC_WORKAROUND_LNK4221(EQ) #endif // !NO_EQ OPENMPT_NAMESPACE_END