Add Reverbation DSP effect and fix FFTFilter include paths
This commit is contained in:
@@ -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
|
||||
|
||||
74
engine/include/XCEngine/Audio/Reverbation.h
Normal file
74
engine/include/XCEngine/Audio/Reverbation.h
Normal file
@@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
#include "IAudioEffect.h"
|
||||
#include <vector>
|
||||
|
||||
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<float> 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<float> 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
|
||||
@@ -1,8 +1,8 @@
|
||||
#include "FFTFilter.h"
|
||||
#include <XCEngine/Audio/FFTFilter.h>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include "../../third_party/kissfft/kiss_fftr.h"
|
||||
#include "../../third_party/kissfft/kiss_fft.h"
|
||||
#include <kissfft/kiss_fftr.h>
|
||||
#include <kissfft/kiss_fft.h>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Audio {
|
||||
|
||||
123
engine/src/Audio/Reverbation.cpp
Normal file
123
engine/src/Audio/Reverbation.cpp
Normal file
@@ -0,0 +1,123 @@
|
||||
#include <XCEngine/Audio/Reverbation.h>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
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
|
||||
Reference in New Issue
Block a user