Add IAudioEffect interface and FFTFilter DSP effect using KissFFT
This commit is contained in:
@@ -226,6 +226,7 @@ add_library(XCEngine STATIC
|
|||||||
# Audio
|
# Audio
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Audio/AudioTypes.h
|
${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/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/IAudioBackend.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Audio/AudioSystem.h
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Audio/AudioSystem.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Audio/WASAPI/WASAPIBackend.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}/include/XCEngine/Audio/AudioMixer.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Audio/AudioMixer.cpp
|
${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)
|
# Third-party (KissFFT)
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/third_party/kissfft/kiss_fft.h
|
${CMAKE_CURRENT_SOURCE_DIR}/third_party/kissfft/kiss_fft.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/third_party/kissfft/kiss_fft.c
|
${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