Add Equalizer DSP effect
This commit is contained in:
@@ -248,6 +248,8 @@ add_library(XCEngine STATIC
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Audio/FFTFilter.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Audio/FFTFilter.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Audio/Reverbation.h
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Audio/Reverbation.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Audio/Reverbation.cpp
|
${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)
|
# Third-party (KissFFT)
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/third_party/kissfft/kiss_fft.h
|
${CMAKE_CURRENT_SOURCE_DIR}/third_party/kissfft/kiss_fft.h
|
||||||
|
|||||||
65
engine/include/XCEngine/Audio/Equalizer.h
Normal file
65
engine/include/XCEngine/Audio/Equalizer.h
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "IAudioEffect.h"
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
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<float> m_frequencies;
|
||||||
|
std::vector<float> m_gains;
|
||||||
|
std::vector<float> m_qs;
|
||||||
|
std::vector<float> m_a0;
|
||||||
|
std::vector<float> m_a1;
|
||||||
|
std::vector<float> m_a2;
|
||||||
|
std::vector<float> m_b1;
|
||||||
|
std::vector<float> m_b2;
|
||||||
|
|
||||||
|
struct BandState {
|
||||||
|
float x1 = 0.0f;
|
||||||
|
float x2 = 0.0f;
|
||||||
|
float y1 = 0.0f;
|
||||||
|
float y2 = 0.0f;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<BandState> m_bandStates;
|
||||||
|
|
||||||
|
float m_wetMix = 1.0f;
|
||||||
|
bool m_enabled = true;
|
||||||
|
|
||||||
|
uint32 m_sampleRate = 48000;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Audio
|
||||||
|
} // namespace XCEngine
|
||||||
165
engine/src/Audio/Equalizer.cpp
Normal file
165
engine/src/Audio/Equalizer.cpp
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
#include <XCEngine/Audio/Equalizer.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
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
|
||||||
Reference in New Issue
Block a user