Files
XCEngine/tests/Components/test_audio_mixer.cpp

196 lines
5.5 KiB
C++

#include <gtest/gtest.h>
#include <XCEngine/Audio/AudioMixer.h>
#include <XCEngine/Audio/Equalizer.h>
#include <XCEngine/Audio/FFTFilter.h>
#include <XCEngine/Audio/IAudioEffect.h>
#include <XCEngine/Audio/Reverbation.h>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace XCEngine::Audio;
namespace {
class OffsetEffect final : public IAudioEffect {
public:
explicit OffsetEffect(float offset) : m_offset(offset) {}
void ProcessAudio(float* buffer, uint32 frameCount, uint32 channels) override {
++callCount;
const size_t totalSamples = static_cast<size_t>(frameCount) * channels;
for (size_t i = 0; i < totalSamples; ++i) {
buffer[i] += m_offset;
}
}
int callCount = 0;
private:
float m_offset = 0.0f;
};
TEST(AudioMixer, AppliesMasterAndChannelVolume) {
AudioMixer mixer;
mixer.SetVolume(0.5f);
mixer.SetChannelVolume(AudioChannel::FrontLeft, 0.25f);
mixer.SetChannelVolume(AudioChannel::FrontRight, 1.0f);
float buffer[] = {1.0f, 1.0f, 1.0f, 1.0f};
mixer.ProcessAudio(buffer, 2, 2);
EXPECT_FLOAT_EQ(buffer[0], 0.125f);
EXPECT_FLOAT_EQ(buffer[1], 0.5f);
EXPECT_FLOAT_EQ(buffer[2], 0.125f);
EXPECT_FLOAT_EQ(buffer[3], 0.5f);
}
TEST(AudioMixer, MuteSilencesBuffer) {
AudioMixer mixer;
mixer.SetMute(true);
float buffer[] = {0.5f, -0.5f};
mixer.ProcessAudio(buffer, 1, 2);
EXPECT_FLOAT_EQ(buffer[0], 0.0f);
EXPECT_FLOAT_EQ(buffer[1], 0.0f);
}
TEST(AudioMixer, EffectsRunAfterGain) {
AudioMixer mixer;
mixer.SetVolume(0.5f);
OffsetEffect effect(0.25f);
mixer.AddEffect(&effect);
float buffer[] = {1.0f, -1.0f};
mixer.ProcessAudio(buffer, 1, 2);
EXPECT_EQ(effect.callCount, 1);
EXPECT_FLOAT_EQ(buffer[0], 0.75f);
EXPECT_FLOAT_EQ(buffer[1], -0.25f);
}
TEST(AudioMixer, EqualizerWetMixControlsProcessedSignal) {
AudioMixer mixer;
Equalizer equalizer;
equalizer.SetBandCount(1);
equalizer.SetBandFrequency(0, 2000.0f);
equalizer.SetBandGain(0, 12.0f);
equalizer.SetBandQ(0, 0.7f);
mixer.AddEffect(&equalizer);
std::vector<float> dryBuffer(64, 0.0f);
dryBuffer[0] = 1.0f;
equalizer.SetWetMix(0.0f);
mixer.ProcessAudio(dryBuffer.data(), 64, 1, 48000);
EXPECT_FLOAT_EQ(dryBuffer[0], 1.0f);
EXPECT_TRUE(std::all_of(
dryBuffer.begin() + 1,
dryBuffer.end(),
[](float sample) { return sample == 0.0f; }));
std::vector<float> wetBuffer(64, 0.0f);
wetBuffer[0] = 1.0f;
equalizer.ResetState();
equalizer.SetWetMix(1.0f);
mixer.ProcessAudio(wetBuffer.data(), 64, 1, 48000);
const bool changed = std::any_of(
wetBuffer.begin(),
wetBuffer.end(),
[](float sample) { return std::abs(sample) > 1e-4f && sample != 1.0f; }) ||
std::any_of(
wetBuffer.begin() + 1,
wetBuffer.end(),
[](float sample) { return std::abs(sample) > 1e-5f; });
EXPECT_TRUE(changed);
}
TEST(AudioMixer, ReverbationProducesAudibleTail) {
AudioMixer mixer;
Reverbation reverb;
reverb.SetWetMix(1.0f);
reverb.SetDryMix(0.0f);
reverb.SetRoomSize(1.0f);
reverb.SetDamping(0.0f);
mixer.AddEffect(&reverb);
std::vector<float> buffer(2000, 0.0f);
buffer[0] = 1.0f;
mixer.ProcessAudio(buffer.data(), 2000, 1, 48000);
EXPECT_FLOAT_EQ(buffer[0], 0.0f);
const bool hasTail = std::any_of(
buffer.begin() + 1000,
buffer.end(),
[](float sample) { return std::abs(sample) > 1e-6f; });
EXPECT_TRUE(hasTail);
}
TEST(AudioMixer, ReverbationWidthControlsStereoCrossfeed) {
AudioMixer narrowMixer;
Reverbation narrowReverb;
narrowReverb.SetWetMix(1.0f);
narrowReverb.SetDryMix(0.0f);
narrowReverb.SetRoomSize(1.0f);
narrowReverb.SetDamping(0.0f);
narrowReverb.SetWidth(0.0f);
narrowMixer.AddEffect(&narrowReverb);
std::vector<float> narrowBuffer(2000 * 2, 0.0f);
narrowBuffer[0] = 1.0f;
narrowMixer.ProcessAudio(narrowBuffer.data(), 2000, 2, 48000);
float narrowMaxDiff = 0.0f;
for (size_t i = 1100; i < 1150; ++i) {
narrowMaxDiff = std::max(
narrowMaxDiff,
std::abs(narrowBuffer[i * 2] - narrowBuffer[i * 2 + 1]));
}
EXPECT_LT(narrowMaxDiff, 1e-6f);
AudioMixer wideMixer;
Reverbation wideReverb;
wideReverb.SetWetMix(1.0f);
wideReverb.SetDryMix(0.0f);
wideReverb.SetRoomSize(1.0f);
wideReverb.SetDamping(0.0f);
wideReverb.SetWidth(1.0f);
wideMixer.AddEffect(&wideReverb);
std::vector<float> wideBuffer(2000 * 2, 0.0f);
wideBuffer[0] = 1.0f;
wideMixer.ProcessAudio(wideBuffer.data(), 2000, 2, 48000);
float wideMaxDiff = 0.0f;
for (size_t i = 1100; i < 1150; ++i) {
wideMaxDiff = std::max(
wideMaxDiff,
std::abs(wideBuffer[i * 2] - wideBuffer[i * 2 + 1]));
}
EXPECT_GT(wideMaxDiff, 1e-6f);
}
TEST(AudioMixer, FFTFilterAnalyzesWithoutMutatingBuffer) {
AudioMixer mixer;
FFTFilter fft(8);
fft.SetSmoothingFactor(0.0f);
mixer.AddEffect(&fft);
std::vector<float> buffer = {1.0f, 0.5f, 0.0f, -0.5f};
const std::vector<float> original = buffer;
mixer.ProcessAudio(buffer.data(), 4, 1, 48000);
EXPECT_EQ(buffer, original);
ASSERT_EQ(fft.GetSpectrumSize(), 4u);
const bool hasSpectrum = std::any_of(
fft.GetSpectrumData(),
fft.GetSpectrumData() + fft.GetSpectrumSize(),
[](float value) { return value > 0.0f; });
EXPECT_TRUE(hasSpectrum);
}
} // namespace