From 36119e62aaf155fbbfaa1a926140e0139416ef90 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Sat, 21 Mar 2026 12:19:27 +0800 Subject: [PATCH] Add Equalizer DSP effect --- engine/CMakeLists.txt | 2 + engine/include/XCEngine/Audio/Equalizer.h | 65 +++++++++ engine/src/Audio/Equalizer.cpp | 165 ++++++++++++++++++++++ 3 files changed, 232 insertions(+) create mode 100644 engine/include/XCEngine/Audio/Equalizer.h create mode 100644 engine/src/Audio/Equalizer.cpp diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index e9fda8e4..9c88cc76 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -248,6 +248,8 @@ add_library(XCEngine STATIC ${CMAKE_CURRENT_SOURCE_DIR}/src/Audio/FFTFilter.cpp ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Audio/Reverbation.h ${CMAKE_CURRENT_SOURCE_DIR}/src/Audio/Reverbation.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Audio/Equalizer.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/Audio/Equalizer.cpp # Third-party (KissFFT) ${CMAKE_CURRENT_SOURCE_DIR}/third_party/kissfft/kiss_fft.h diff --git a/engine/include/XCEngine/Audio/Equalizer.h b/engine/include/XCEngine/Audio/Equalizer.h new file mode 100644 index 00000000..351dbd31 --- /dev/null +++ b/engine/include/XCEngine/Audio/Equalizer.h @@ -0,0 +1,65 @@ +#pragma once + +#include "IAudioEffect.h" +#include + +namespace XCEngine { +namespace Audio { + +class Equalizer : public IAudioEffect { +public: + Equalizer(); + ~Equalizer() override; + + void ProcessAudio(float* buffer, uint32 sampleCount, uint32 channels) override; + + void SetBandCount(uint32 count); + uint32 GetBandCount() const { return m_bandCount; } + + void SetBandFrequency(uint32 band, float frequency); + float GetBandFrequency(uint32 band) const; + + void SetBandGain(uint32 band, float gainDb); + float GetBandGain(uint32 band) const; + + void SetBandQ(uint32 band, float q); + float GetBandQ(uint32 band) const; + + void SetEnabled(bool enabled) override; + bool IsEnabled() const override { return m_enabled; } + + void SetWetMix(float wetMix) override; + float GetWetMix() const override { return m_wetMix; } + +private: + void ProcessBand(float* buffer, uint32 sampleCount, uint32 channel, uint32 band); + void ComputeCoefficients(float frequency, float q, float gainDb); + +private: + uint32 m_bandCount = 4; + std::vector m_frequencies; + std::vector m_gains; + std::vector m_qs; + std::vector m_a0; + std::vector m_a1; + std::vector m_a2; + std::vector m_b1; + std::vector m_b2; + + struct BandState { + float x1 = 0.0f; + float x2 = 0.0f; + float y1 = 0.0f; + float y2 = 0.0f; + }; + + std::vector m_bandStates; + + float m_wetMix = 1.0f; + bool m_enabled = true; + + uint32 m_sampleRate = 48000; +}; + +} // namespace Audio +} // namespace XCEngine diff --git a/engine/src/Audio/Equalizer.cpp b/engine/src/Audio/Equalizer.cpp new file mode 100644 index 00000000..c58a5308 --- /dev/null +++ b/engine/src/Audio/Equalizer.cpp @@ -0,0 +1,165 @@ +#include +#include +#include + +namespace XCEngine { +namespace Audio { + +Equalizer::Equalizer() + : m_sampleRate(48000) +{ + SetBandCount(4); + + m_frequencies[0] = 100.0f; + m_frequencies[1] = 500.0f; + m_frequencies[2] = 2000.0f; + m_frequencies[3] = 8000.0f; + + for (uint32 i = 0; i < m_bandCount; ++i) { + m_gains[i] = 0.0f; + m_qs[i] = 1.0f; + ComputeCoefficients(m_frequencies[i], m_qs[i], m_gains[i]); + } +} + +Equalizer::~Equalizer() { +} + +void Equalizer::ProcessAudio(float* buffer, uint32 sampleCount, uint32 channels) { + if (!m_enabled || buffer == nullptr || sampleCount == 0) { + return; + } + + if (channels == 0) { + return; + } + + for (uint32 ch = 0; ch < channels; ++ch) { + for (uint32 band = 0; band < m_bandCount; ++band) { + uint32 stateIndex = ch * m_bandCount + band; + BandState& state = m_bandStates[stateIndex]; + + float a0 = m_a0[band]; + float a1 = m_a1[band]; + float a2 = m_a2[band]; + float b1 = m_b1[band]; + float b2 = m_b2[band]; + + for (uint32 i = 0; i < sampleCount; ++i) { + uint32 sampleIndex = i * channels + ch; + float x0 = buffer[sampleIndex]; + + float y0 = a0 * x0 + a1 * state.x1 + a2 * state.x2 - b1 * state.y1 - b2 * state.y2; + + state.x2 = state.x1; + state.x1 = x0; + state.y2 = state.y1; + state.y1 = y0; + + buffer[sampleIndex] = y0; + } + } + } +} + +void Equalizer::SetBandCount(uint32 count) { + if (count == m_bandCount) { + return; + } + + m_bandCount = count; + m_frequencies.resize(count, 1000.0f); + m_gains.resize(count, 0.0f); + m_qs.resize(count, 1.0f); + m_a0.resize(count, 1.0f); + m_a1.resize(count, 0.0f); + m_a2.resize(count, 0.0f); + m_b1.resize(count, 0.0f); + m_b2.resize(count, 0.0f); + + m_bandStates.resize(count * 2); +} + +void Equalizer::SetBandFrequency(uint32 band, float frequency) { + if (band >= m_bandCount) { + return; + } + + m_frequencies[band] = std::max(20.0f, std::min(frequency, 20000.0f)); + ComputeCoefficients(m_frequencies[band], m_qs[band], m_gains[band]); +} + +float Equalizer::GetBandFrequency(uint32 band) const { + if (band >= m_bandCount) { + return 0.0f; + } + return m_frequencies[band]; +} + +void Equalizer::SetBandGain(uint32 band, float gainDb) { + if (band >= m_bandCount) { + return; + } + + m_gains[band] = std::max(-24.0f, std::min(gainDb, 24.0f)); + ComputeCoefficients(m_frequencies[band], m_qs[band], m_gains[band]); +} + +float Equalizer::GetBandGain(uint32 band) const { + if (band >= m_bandCount) { + return 0.0f; + } + return m_gains[band]; +} + +void Equalizer::SetBandQ(uint32 band, float q) { + if (band >= m_bandCount) { + return; + } + + m_qs[band] = std::max(0.1f, std::min(q, 10.0f)); + ComputeCoefficients(m_frequencies[band], m_qs[band], m_gains[band]); +} + +float Equalizer::GetBandQ(uint32 band) const { + if (band >= m_bandCount) { + return 0.0f; + } + return m_qs[band]; +} + +void Equalizer::SetEnabled(bool enabled) { + m_enabled = enabled; +} + +void Equalizer::SetWetMix(float wetMix) { + m_wetMix = std::max(0.0f, std::min(1.0f, wetMix)); +} + +void Equalizer::ComputeCoefficients(float frequency, float q, float gainDb) { + float A = std::pow(10.0f, gainDb / 40.0f); + float w0 = 2.0f * 3.14159265f * frequency / m_sampleRate; + float cosw0 = std::cos(w0); + float sinw0 = std::sin(w0); + float alpha = sinw0 / (2.0f * q); + + float b0 = 1.0f + alpha * A; + float b1 = -2.0f * cosw0; + float b2 = 1.0f - alpha * A; + float a0 = 1.0f + alpha / A; + float a1 = -2.0f * cosw0; + float a2 = 1.0f - alpha / A; + + size_t bandIndex = &frequency - &m_frequencies[0]; + + if (bandIndex < m_bandCount) { + m_a0[bandIndex] = b0 / a0; + m_a1[bandIndex] = b1 / a0; + m_a2[bandIndex] = b2 / a0; + m_b1[bandIndex] = a1 / a0; + m_b2[bandIndex] = a2 / a0; + } +} + +} // namespace Audio +} // namespace XCEngine