Add IAudioEffect interface and FFTFilter DSP effect using KissFFT
This commit is contained in:
@@ -226,6 +226,7 @@ add_library(XCEngine STATIC
|
||||
# Audio
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Audio/AudioTypes.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Audio/AudioConfig.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Audio/IAudioEffect.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Audio/IAudioBackend.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Audio/AudioSystem.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Audio/WASAPI/WASAPIBackend.h
|
||||
@@ -242,6 +243,10 @@ add_library(XCEngine STATIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Audio/AudioMixer.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Audio/AudioMixer.cpp
|
||||
|
||||
# Audio DSP Effects
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Audio/FFTFilter.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Audio/FFTFilter.cpp
|
||||
|
||||
# Third-party (KissFFT)
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/third_party/kissfft/kiss_fft.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/third_party/kissfft/kiss_fft.c
|
||||
|
||||
42
engine/include/XCEngine/Audio/FFTFilter.h
Normal file
42
engine/include/XCEngine/Audio/FFTFilter.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include "IAudioEffect.h"
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Audio {
|
||||
|
||||
class FFTFilter : public IAudioEffect {
|
||||
public:
|
||||
FFTFilter();
|
||||
explicit FFTFilter(uint32 fftSize);
|
||||
~FFTFilter() override;
|
||||
|
||||
void ProcessAudio(float* buffer, uint32 sampleCount, uint32 channels) override;
|
||||
|
||||
void SetFFTSize(uint32 size);
|
||||
uint32 GetFFTSize() const { return m_fftSize; }
|
||||
|
||||
void SetSmoothingFactor(float factor);
|
||||
float GetSmoothingFactor() const { return m_smoothingFactor; }
|
||||
|
||||
const float* GetSpectrumData() const { return m_spectrumData.data(); }
|
||||
size_t GetSpectrumSize() const { return m_spectrumData.size(); }
|
||||
|
||||
private:
|
||||
void InitializeFFT(uint32 size);
|
||||
void ComputeFFT(const float* input, uint32 size);
|
||||
|
||||
private:
|
||||
uint32 m_fftSize = 1024;
|
||||
float m_smoothingFactor = 0.8f;
|
||||
|
||||
std::vector<float> m_spectrumData;
|
||||
std::vector<float> m_prevSpectrum;
|
||||
|
||||
void* m_fftConfig = nullptr;
|
||||
void* m_fftrConfig = nullptr;
|
||||
};
|
||||
|
||||
} // namespace Audio
|
||||
} // namespace XCEngine
|
||||
26
engine/include/XCEngine/Audio/IAudioEffect.h
Normal file
26
engine/include/XCEngine/Audio/IAudioEffect.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "AudioTypes.h"
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Audio {
|
||||
|
||||
class IAudioEffect {
|
||||
public:
|
||||
virtual ~IAudioEffect() = default;
|
||||
|
||||
virtual void ProcessAudio(float* buffer, uint32 sampleCount, uint32 channels) = 0;
|
||||
|
||||
virtual void SetEnabled(bool enabled) { m_enabled = enabled; }
|
||||
virtual bool IsEnabled() const { return m_enabled; }
|
||||
|
||||
virtual void SetWetMix(float wetMix) { m_wetMix = wetMix; }
|
||||
virtual float GetWetMix() const { return m_wetMix; }
|
||||
|
||||
protected:
|
||||
bool m_enabled = true;
|
||||
float m_wetMix = 1.0f;
|
||||
};
|
||||
|
||||
} // namespace Audio
|
||||
} // namespace XCEngine
|
||||
119
engine/src/Audio/FFTFilter.cpp
Normal file
119
engine/src/Audio/FFTFilter.cpp
Normal file
@@ -0,0 +1,119 @@
|
||||
#include "FFTFilter.h"
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include "../../third_party/kissfft/kiss_fftr.h"
|
||||
#include "../../third_party/kissfft/kiss_fft.h"
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Audio {
|
||||
|
||||
FFTFilter::FFTFilter()
|
||||
: m_spectrumData(512, 0.0f)
|
||||
, m_prevSpectrum(512, 0.0f)
|
||||
{
|
||||
InitializeFFT(m_fftSize);
|
||||
}
|
||||
|
||||
FFTFilter::FFTFilter(uint32 fftSize)
|
||||
: m_fftSize(fftSize)
|
||||
, m_spectrumData(fftSize / 2, 0.0f)
|
||||
, m_prevSpectrum(fftSize / 2, 0.0f)
|
||||
{
|
||||
InitializeFFT(fftSize);
|
||||
}
|
||||
|
||||
FFTFilter::~FFTFilter() {
|
||||
if (m_fftConfig) {
|
||||
kiss_fft_free(static_cast<kiss_fft_cfg>(m_fftConfig));
|
||||
m_fftConfig = nullptr;
|
||||
}
|
||||
if (m_fftrConfig) {
|
||||
kiss_fftr_free(static_cast<kiss_fftr_cfg>(m_fftrConfig));
|
||||
m_fftrConfig = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void FFTFilter::ProcessAudio(float* buffer, uint32 sampleCount, uint32 channels) {
|
||||
if (!m_enabled || buffer == nullptr || sampleCount == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (channels == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sampleCount < m_fftSize) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32 binCount = m_fftSize / 2;
|
||||
if (m_spectrumData.size() != binCount) {
|
||||
m_spectrumData.resize(binCount);
|
||||
m_prevSpectrum.resize(binCount);
|
||||
}
|
||||
|
||||
std::vector<float> monoBuffer(m_fftSize, 0.0f);
|
||||
for (uint32 i = 0; i < m_fftSize; ++i) {
|
||||
uint32 channelIndex = (channels == 1) ? 0 : (i % channels);
|
||||
monoBuffer[i] = buffer[i * channels + channelIndex];
|
||||
}
|
||||
|
||||
ComputeFFT(monoBuffer.data(), m_fftSize);
|
||||
}
|
||||
|
||||
void FFTFilter::SetFFTSize(uint32 size) {
|
||||
if (size == m_fftSize) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_fftSize = size;
|
||||
m_spectrumData.resize(size / 2);
|
||||
m_prevSpectrum.resize(size / 2);
|
||||
InitializeFFT(size);
|
||||
}
|
||||
|
||||
void FFTFilter::SetSmoothingFactor(float factor) {
|
||||
m_smoothingFactor = std::max(0.0f, std::min(1.0f, factor));
|
||||
}
|
||||
|
||||
void FFTFilter::InitializeFFT(uint32 size) {
|
||||
if (m_fftConfig) {
|
||||
kiss_fft_free(static_cast<kiss_fft_cfg>(m_fftConfig));
|
||||
}
|
||||
if (m_fftrConfig) {
|
||||
kiss_fftr_free(static_cast<kiss_fftr_cfg>(m_fftrConfig));
|
||||
}
|
||||
|
||||
m_fftConfig = kiss_fft_alloc(size, 0, nullptr, nullptr);
|
||||
m_fftrConfig = kiss_fftr_alloc(size, 0, nullptr, nullptr);
|
||||
}
|
||||
|
||||
void FFTFilter::ComputeFFT(const float* input, uint32 size) {
|
||||
if (!m_fftrConfig || !input) {
|
||||
return;
|
||||
}
|
||||
|
||||
kiss_fft_scalar* timedata = new kiss_fft_scalar[size];
|
||||
kiss_fft_cpx* freqdata = new kiss_fft_cpx[size / 2 + 1];
|
||||
|
||||
for (uint32 i = 0; i < size; ++i) {
|
||||
timedata[i] = input[i];
|
||||
}
|
||||
|
||||
kiss_fftr(static_cast<kiss_fftr_cfg>(m_fftrConfig), timedata, freqdata);
|
||||
|
||||
uint32 binCount = size / 2;
|
||||
for (uint32 i = 0; i < binCount; ++i) {
|
||||
float magnitude = std::sqrt(freqdata[i].r * freqdata[i].r + freqdata[i].i * freqdata[i].i);
|
||||
float dbValue = 20.0f * std::log10(magnitude + 1e-10f);
|
||||
dbValue = std::max(0.0f, std::min(1.0f, (dbValue + 80.0f) / 80.0f));
|
||||
|
||||
m_spectrumData[i] = m_spectrumData[i] * m_smoothingFactor + dbValue * (1.0f - m_smoothingFactor);
|
||||
}
|
||||
|
||||
delete[] timedata;
|
||||
delete[] freqdata;
|
||||
}
|
||||
|
||||
} // namespace Audio
|
||||
} // namespace XCEngine
|
||||
Reference in New Issue
Block a user