196 lines
5.5 KiB
C++
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
|