From 00c2699542c5ae76874db79dae5890bbb35c26a7 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Sat, 21 Mar 2026 12:16:19 +0800 Subject: [PATCH] Add Reverbation DSP effect and fix FFTFilter include paths --- engine/CMakeLists.txt | 2 + engine/include/XCEngine/Audio/Reverbation.h | 74 ++++++++++++ engine/src/Audio/FFTFilter.cpp | 6 +- engine/src/Audio/Reverbation.cpp | 123 ++++++++++++++++++++ 4 files changed, 202 insertions(+), 3 deletions(-) create mode 100644 engine/include/XCEngine/Audio/Reverbation.h create mode 100644 engine/src/Audio/Reverbation.cpp diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 4f1dce5f..e9fda8e4 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -246,6 +246,8 @@ add_library(XCEngine STATIC # Audio DSP Effects ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Audio/FFTFilter.h ${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 # Third-party (KissFFT) ${CMAKE_CURRENT_SOURCE_DIR}/third_party/kissfft/kiss_fft.h diff --git a/engine/include/XCEngine/Audio/Reverbation.h b/engine/include/XCEngine/Audio/Reverbation.h new file mode 100644 index 00000000..601f07dc --- /dev/null +++ b/engine/include/XCEngine/Audio/Reverbation.h @@ -0,0 +1,74 @@ +#pragma once + +#include "IAudioEffect.h" +#include + +namespace XCEngine { +namespace Audio { + +class Reverbation : public IAudioEffect { +public: + Reverbation(); + ~Reverbation() override; + + void ProcessAudio(float* buffer, uint32 sampleCount, uint32 channels) override; + + void SetRoomSize(float size); + float GetRoomSize() const { return m_roomSize; } + + void SetDamping(float damping); + float GetDamping() const { return m_damping; } + + void SetWetMix(float wetMix) override; + float GetWetMix() const override { return m_wetMix; } + + void SetDryMix(float dryMix); + float GetDryMix() const { return m_dryMix; } + + void SetWidth(float width); + float GetWidth() const { return m_width; } + + void SetFreeze(bool freeze); + bool IsFreeze() const { return m_freeze; } + +private: + void ProcessCombFilter(float* buffer, uint32 sampleCount, uint32 channel, uint32 combIndex); + void ProcessAllPassFilter(float* buffer, uint32 sampleCount, uint32 channel); + +private: + float m_roomSize = 0.5f; + float m_damping = 0.5f; + float m_wetMix = 0.3f; + float m_dryMix = 0.7f; + float m_width = 1.0f; + bool m_freeze = false; + + static constexpr uint32 CombCount = 8; + static constexpr uint32 AllPassCount = 4; + static constexpr uint32 MaxDelayLength = 2000; + + struct CombFilter { + std::vector buffer; + uint32 bufferSize = 0; + uint32 writeIndex = 0; + float feedback = 0.0f; + float damp1 = 0.0f; + float damp2 = 0.0f; + float filterStore = 0.0f; + }; + + struct AllPassFilter { + std::vector buffer; + uint32 bufferSize = 0; + uint32 writeIndex = 0; + float feedback = 0.0f; + }; + + CombFilter m_combFilters[CombCount]; + AllPassFilter m_allPassFilters[AllPassCount]; + + uint32 m_sampleRate = 48000; +}; + +} // namespace Audio +} // namespace XCEngine diff --git a/engine/src/Audio/FFTFilter.cpp b/engine/src/Audio/FFTFilter.cpp index 7f38a3ee..de359984 100644 --- a/engine/src/Audio/FFTFilter.cpp +++ b/engine/src/Audio/FFTFilter.cpp @@ -1,8 +1,8 @@ -#include "FFTFilter.h" +#include #include #include -#include "../../third_party/kissfft/kiss_fftr.h" -#include "../../third_party/kissfft/kiss_fft.h" +#include +#include namespace XCEngine { namespace Audio { diff --git a/engine/src/Audio/Reverbation.cpp b/engine/src/Audio/Reverbation.cpp new file mode 100644 index 00000000..72b269ed --- /dev/null +++ b/engine/src/Audio/Reverbation.cpp @@ -0,0 +1,123 @@ +#include +#include +#include + +namespace XCEngine { +namespace Audio { + +static const uint32 CombTuning[] = { 1116, 1188, 1277, 1356, 1422, 1491, 1557, 1617 }; +static const uint32 AllPassTuning[] = { 556, 441, 341, 225 }; + +Reverbation::Reverbation() + : m_sampleRate(48000) +{ + for (uint32 i = 0; i < CombCount; ++i) { + m_combFilters[i].bufferSize = CombTuning[i]; + m_combFilters[i].buffer.resize(m_combFilters[i].bufferSize, 0.0f); + m_combFilters[i].writeIndex = 0; + m_combFilters[i].feedback = 0.0f; + m_combFilters[i].damp1 = 0.0f; + m_combFilters[i].damp2 = 0.0f; + m_combFilters[i].filterStore = 0.0f; + } + + for (uint32 i = 0; i < AllPassCount; ++i) { + m_allPassFilters[i].bufferSize = AllPassTuning[i]; + m_allPassFilters[i].buffer.resize(m_allPassFilters[i].bufferSize, 0.0f); + m_allPassFilters[i].writeIndex = 0; + m_allPassFilters[i].feedback = 0.5f; + } + + SetRoomSize(0.5f); + SetDamping(0.5f); +} + +Reverbation::~Reverbation() { +} + +void Reverbation::ProcessAudio(float* buffer, uint32 sampleCount, uint32 channels) { + if (!m_enabled || buffer == nullptr || sampleCount == 0) { + return; + } + + if (channels == 0) { + return; + } + + for (uint32 i = 0; i < sampleCount; ++i) { + float input = buffer[i * channels]; + + float wet = 0.0f; + + for (uint32 c = 0; c < CombCount; ++c) { + float output = m_combFilters[c].buffer[m_combFilters[c].writeIndex]; + + m_combFilters[c].filterStore = output * m_combFilters[c].damp2 + m_combFilters[c].filterStore * m_combFilters[c].damp1; + + m_combFilters[c].buffer[m_combFilters[c].writeIndex] = input + m_combFilters[c].filterStore * m_combFilters[c].feedback; + + m_combFilters[c].writeIndex++; + if (m_combFilters[c].writeIndex >= m_combFilters[c].bufferSize) { + m_combFilters[c].writeIndex = 0; + } + + wet += output; + } + + for (uint32 a = 0; a < AllPassCount; ++a) { + float output = m_allPassFilters[a].buffer[m_allPassFilters[a].writeIndex]; + float temp = output; + + m_allPassFilters[a].buffer[m_allPassFilters[a].writeIndex] = wet + output * m_allPassFilters[a].feedback; + + wet = -wet + output; + wet += temp; + + m_allPassFilters[a].writeIndex++; + if (m_allPassFilters[a].writeIndex >= m_allPassFilters[a].bufferSize) { + m_allPassFilters[a].writeIndex = 0; + } + } + + float outSample = input * m_dryMix + wet * m_wetMix; + + for (uint32 ch = 0; ch < channels; ++ch) { + buffer[i * channels + ch] = outSample; + } + } +} + +void Reverbation::SetRoomSize(float size) { + m_roomSize = std::max(0.0f, std::min(1.0f, size)); + float roomScale = 0.28f + 0.7f * m_roomSize; + for (uint32 i = 0; i < CombCount; ++i) { + m_combFilters[i].feedback = roomScale; + } +} + +void Reverbation::SetDamping(float damping) { + m_damping = std::max(0.0f, std::min(1.0f, damping)); + for (uint32 i = 0; i < CombCount; ++i) { + m_combFilters[i].damp1 = m_damping * 0.4f; + m_combFilters[i].damp2 = 1.0f - m_damping * 0.4f; + } +} + +void Reverbation::SetWetMix(float wetMix) { + m_wetMix = std::max(0.0f, std::min(1.0f, wetMix)); +} + +void Reverbation::SetDryMix(float dryMix) { + m_dryMix = std::max(0.0f, std::min(1.0f, dryMix)); +} + +void Reverbation::SetWidth(float width) { + m_width = std::max(0.0f, std::min(1.0f, width)); +} + +void Reverbation::SetFreeze(bool freeze) { + m_freeze = freeze; +} + +} // namespace Audio +} // namespace XCEngine