#include #include #include #include #include #include #include #include #include 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(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 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 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 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 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 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 buffer = {1.0f, 0.5f, 0.0f, -0.5f}; const std::vector 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