/* * 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 "EQ.h" #include "mpt/audio/span.hpp" #include "mpt/base/numbers.hpp" #include "openmpt/base/Types.hpp" #include "openmpt/soundbase/MixSample.hpp" #include "openmpt/soundbase/MixSampleConvert.hpp" #ifndef NO_EQ #include "../misc/mptCPU.h" #endif #include #include #include #if defined(MPT_ENABLE_ARCH_INTRINSICS_SSE) #include #endif OPENMPT_NAMESPACE_BEGIN #ifndef NO_EQ static constexpr float EQ_BANDWIDTH = 2.0f; static constexpr std::array gEqLinearToDB = { 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 std::array gEQDefaults = {{ // Default: Flat EQ {0,0,0,0,0, 1, 120}, {0,0,0,0,0, 1, 600}, {0,0,0,0,0, 1, 1200}, {0,0,0,0,0, 1, 3000}, {0,0,0,0,0, 1, 6000}, {0,0,0,0,0, 1, 10000} }}; template static void EQFilter(Tbuf & buf, const std::array &bands, std::array, MAX_EQ_CHANNELS> &states) { for(std::size_t frame = 0; frame < buf.size_frames(); ++frame) { for(std::size_t channel = 0; channel < channels; ++channel) { float sample = mix_sample_cast(buf(channel, frame)); for(std::size_t b = 0; b < std::size(bands); ++b) { const EQBANDSETTINGS &band = bands[b]; if(band.Gain != 1.0f) { EQBANDSTATE &bandState = states[channel][b]; float x = sample; float y = band.a1 * bandState.x1 + band.a2 * bandState.x2 + band.a0 * x + band.b1 * bandState.y1 + band.b2 * bandState.y2; bandState.x2 = bandState.x1; bandState.y2 = bandState.y1; bandState.x1 = x; bandState.y1 = y; sample = y; } } buf(channel, frame) = mix_sample_cast(sample); } } } template void CEQ::ProcessTemplate(TMixSample *frontBuffer, TMixSample *rearBuffer, std::size_t countFrames, std::size_t numChannels) { #if defined(MPT_ENABLE_ARCH_INTRINSICS_SSE) unsigned int old_csr = 0; if(CPU::HasFeatureSet(CPU::feature::sse)) { old_csr = _mm_getcsr(); _mm_setcsr((old_csr & ~(_MM_DENORMALS_ZERO_MASK | _MM_FLUSH_ZERO_MASK)) | _MM_DENORMALS_ZERO_ON | _MM_FLUSH_ZERO_ON); } #endif if(numChannels == 1) { mpt::audio_span_interleaved buf{ frontBuffer, 1, countFrames }; EQFilter<1>(buf, m_Bands, m_ChannelState); } else if(numChannels == 2) { mpt::audio_span_interleaved buf{ frontBuffer, 2, countFrames }; EQFilter<2>(buf, m_Bands, m_ChannelState); } else if(numChannels == 4) { std::array buffers = { &frontBuffer[0], &frontBuffer[1], &rearBuffer[0], &rearBuffer[1] }; mpt::audio_span_planar_strided buf{ buffers.data(), 4, countFrames, 2 }; EQFilter<4>(buf, m_Bands, m_ChannelState); } #if defined(MPT_ENABLE_ARCH_INTRINSICS_SSE) if(CPU::HasFeatureSet(CPU::feature::sse)) { _mm_setcsr(old_csr); } #endif } void CEQ::Process(MixSampleInt *frontBuffer, MixSampleInt *rearBuffer, std::size_t countFrames, std::size_t numChannels) { ProcessTemplate(frontBuffer, rearBuffer, countFrames, numChannels); } void CEQ::Process(MixSampleFloat *frontBuffer, MixSampleFloat *rearBuffer, std::size_t countFrames, std::size_t numChannels) { ProcessTemplate(frontBuffer, rearBuffer, countFrames, numChannels); } CEQ::CEQ() : m_Bands(gEQDefaults) { return; } void CEQ::Initialize(bool bReset, uint32 MixingFreq) { float fMixingFreq = static_cast(MixingFreq); // Gain = 0.5 (-6dB) .. 2 (+6dB) for(std::size_t band = 0; band < MAX_EQ_BANDS; ++band) { float k, k2, r, f; float v0, v1; bool b = bReset; f = m_Bands[band].CenterFrequency / fMixingFreq; if(f > 0.45f) { m_Bands[band].Gain = 1.0f; } k = f * mpt::numbers::pi_v; k = k + k*f; k2 = k*k; v0 = m_Bands[band].Gain; v1 = 1; if(m_Bands[band].Gain < 1.0f) { 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 != m_Bands[band].a0) { m_Bands[band].a0 = r; b = true; } r = 2 * (k2 - 1) / (1 + v1*k + k2); if(r != m_Bands[band].a1) { m_Bands[band].a1 = r; b = true; } r = (1 - v0*k + k2) / (1 + v1*k + k2); if(r != m_Bands[band].a2) { m_Bands[band].a2 = r; b = true; } r = - 2 * (k2 - 1) / (1 + v1*k + k2); if(r != m_Bands[band].b1) { m_Bands[band].b1 = r; b = true; } r = - (1 - v1*k + k2) / (1 + v1*k + k2); if(r != m_Bands[band].b2) { m_Bands[band].b2 = r; b = true; } if(b) { for(std::size_t channel = 0; channel < MAX_EQ_CHANNELS; ++channel) { m_ChannelState[channel][band] = EQBANDSTATE{}; } } } } void CEQ::SetEQGains(const uint32 *pGains, const uint32 *pFreqs, bool bReset, uint32 MixingFreq) { for(std::size_t i = 0; i < MAX_EQ_BANDS; ++i) { m_Bands[i].Gain = static_cast(gEqLinearToDB[std::clamp(pGains[i], static_cast(0), static_cast(std::size(gEqLinearToDB) - 1))]) / 64.0f; m_Bands[i].CenterFrequency = static_cast(pFreqs[i]); } Initialize(bReset, MixingFreq); } #else MPT_MSVC_WORKAROUND_LNK4221(EQ) #endif // !NO_EQ OPENMPT_NAMESPACE_END