From a4c48c1b3fa83110f7566943e29f4e7327fcc7ab Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Tue, 14 Apr 2026 16:48:39 +0800 Subject: [PATCH] audio: clear mixer routes on destruction --- engine/include/XCEngine/Audio/AudioSystem.h | 11 +++ engine/src/Audio/AudioMixer.cpp | 50 +++++++++- engine/src/Audio/AudioSystem.cpp | 96 +++++++++++++++++++ .../src/Components/AudioListenerComponent.cpp | 64 ++++++++++++- .../src/Components/AudioSourceComponent.cpp | 5 +- tests/Components/test_audio_system.cpp | 23 +++++ 6 files changed, 241 insertions(+), 8 deletions(-) diff --git a/engine/include/XCEngine/Audio/AudioSystem.h b/engine/include/XCEngine/Audio/AudioSystem.h index e7a71f03..52ec907b 100644 --- a/engine/include/XCEngine/Audio/AudioSystem.h +++ b/engine/include/XCEngine/Audio/AudioSystem.h @@ -14,6 +14,7 @@ namespace XCEngine { namespace Components { class AudioSourceComponent; +class AudioListenerComponent; } namespace Audio { @@ -63,6 +64,12 @@ public: void RegisterSource(Components::AudioSourceComponent* source); void UnregisterSource(Components::AudioSourceComponent* source); + void RegisterSourceComponent(Components::AudioSourceComponent* source); + void UnregisterSourceComponent(Components::AudioSourceComponent* source); + void RegisterListenerComponent(Components::AudioListenerComponent* listener); + void UnregisterListenerComponent(Components::AudioListenerComponent* listener); + void RegisterMixer(AudioMixer* mixer); + void UnregisterMixer(AudioMixer* mixer); struct Stats { uint32_t activeSources; @@ -81,6 +88,7 @@ private: void ProcessSource(Components::AudioSourceComponent* source, float* buffer, uint32 frameCount, uint32 channels, uint32 sampleRate); void RenderAudioBlock(uint32 frameCount, uint32 channels, uint32 sampleRate); + void DetachMixerReferences(AudioMixer* mixer); private: std::unique_ptr m_backend; @@ -94,6 +102,9 @@ private: Math::Quaternion m_listenerRotation = Math::Quaternion::Identity(); Math::Vector3 m_listenerVelocity = Math::Vector3::Zero(); + std::vector m_registeredMixers; + std::vector m_registeredSourceComponents; + std::vector m_registeredListenerComponents; std::vector m_activeSources; std::vector m_activeSourceSnapshot; std::vector m_mixScratchBuffer; diff --git a/engine/src/Audio/AudioMixer.cpp b/engine/src/Audio/AudioMixer.cpp index 28827990..846f78a4 100644 --- a/engine/src/Audio/AudioMixer.cpp +++ b/engine/src/Audio/AudioMixer.cpp @@ -1,18 +1,40 @@ #include +#include +#include #include #include namespace XCEngine { namespace Audio { +namespace { + +AudioChannel ResolveChannel(uint32 channelIndex) { + switch (channelIndex) { + case 0: return AudioChannel::FrontLeft; + case 1: return AudioChannel::FrontRight; + case 2: return AudioChannel::FrontCenter; + case 3: return AudioChannel::LFE; + case 4: return AudioChannel::BackLeft; + case 5: return AudioChannel::BackRight; + case 6: return AudioChannel::SideLeft; + case 7: return AudioChannel::SideRight; + default: return AudioChannel::FrontLeft; + } +} + +} // namespace + AudioMixer::AudioMixer() { + AudioSystem::Get().RegisterMixer(this); for (int i = 0; i < static_cast(AudioChannel::SideRight) + 1; ++i) { m_channelVolumes[static_cast(i)] = ChannelVolume{1.0f, false}; } } AudioMixer::~AudioMixer() { + AudioSystem::Get().UnregisterMixer(this); } void AudioMixer::SetVolume(float volume) { @@ -24,7 +46,7 @@ void AudioMixer::SetMute(bool mute) { } void AudioMixer::AddEffect(IAudioEffect* effect) { - if (effect) { + if (effect && std::find(m_effects.begin(), m_effects.end(), effect) == m_effects.end()) { m_effects.push_back(effect); } } @@ -41,19 +63,37 @@ void AudioMixer::ClearEffects() { m_effects.clear(); } -void AudioMixer::ProcessAudio(float* buffer, uint32 sampleCount, uint32 channels) { - if (!buffer || sampleCount == 0) { +void AudioMixer::ProcessAudio(float* buffer, uint32 frameCount, uint32 channels, uint32 sampleRate) { + if (!buffer || frameCount == 0 || channels == 0) { return; } float effectiveVolume = m_mute ? 0.0f : m_volume; + const size_t sampleCount = static_cast(frameCount) * channels; if (effectiveVolume < 0.001f) { + std::fill(buffer, buffer + sampleCount, 0.0f); return; } - for (size_t i = 0; i < sampleCount * channels; ++i) { - buffer[i] *= effectiveVolume; + for (uint32 frame = 0; frame < frameCount; ++frame) { + for (uint32 ch = 0; ch < channels; ++ch) { + const AudioChannel channel = ResolveChannel(ch); + const auto it = m_channelVolumes.find(channel); + const ChannelVolume channelVolume = (it != m_channelVolumes.end()) + ? it->second + : ChannelVolume{}; + + const float channelGain = channelVolume.mute ? 0.0f : channelVolume.volume; + buffer[frame * channels + ch] *= effectiveVolume * channelGain; + } + } + + for (IAudioEffect* effect : m_effects) { + if (effect != nullptr && effect->IsEnabled()) { + effect->SetSampleRate(sampleRate); + effect->ProcessAudio(buffer, frameCount, channels); + } } } diff --git a/engine/src/Audio/AudioSystem.cpp b/engine/src/Audio/AudioSystem.cpp index 8136b340..65e6bdcf 100644 --- a/engine/src/Audio/AudioSystem.cpp +++ b/engine/src/Audio/AudioSystem.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -113,6 +114,9 @@ void AudioSystem::Shutdown() { m_backend->Shutdown(); m_backend.reset(); } + m_registeredMixers.clear(); + m_registeredSourceComponents.clear(); + m_registeredListenerComponents.clear(); m_activeSources.clear(); m_listenerReverbMixer = nullptr; m_listenerReverbLevel = 1.0f; @@ -249,6 +253,70 @@ void AudioSystem::RegisterSource(Components::AudioSourceComponent* source) { } } +void AudioSystem::RegisterSourceComponent(Components::AudioSourceComponent* source) { + if (!source) { + return; + } + + if (std::find(m_registeredSourceComponents.begin(), m_registeredSourceComponents.end(), source) == + m_registeredSourceComponents.end()) { + m_registeredSourceComponents.push_back(source); + } +} + +void AudioSystem::UnregisterSourceComponent(Components::AudioSourceComponent* source) { + if (!source) { + return; + } + + UnregisterSource(source); + m_registeredSourceComponents.erase( + std::remove(m_registeredSourceComponents.begin(), m_registeredSourceComponents.end(), source), + m_registeredSourceComponents.end()); +} + +void AudioSystem::RegisterListenerComponent(Components::AudioListenerComponent* listener) { + if (!listener) { + return; + } + + if (std::find(m_registeredListenerComponents.begin(), m_registeredListenerComponents.end(), listener) == + m_registeredListenerComponents.end()) { + m_registeredListenerComponents.push_back(listener); + } +} + +void AudioSystem::UnregisterListenerComponent(Components::AudioListenerComponent* listener) { + if (!listener) { + return; + } + + m_registeredListenerComponents.erase( + std::remove(m_registeredListenerComponents.begin(), m_registeredListenerComponents.end(), listener), + m_registeredListenerComponents.end()); +} + +void AudioSystem::RegisterMixer(AudioMixer* mixer) { + if (!mixer) { + return; + } + + if (std::find(m_registeredMixers.begin(), m_registeredMixers.end(), mixer) == m_registeredMixers.end()) { + m_registeredMixers.push_back(mixer); + } +} + +void AudioSystem::UnregisterMixer(AudioMixer* mixer) { + if (!mixer) { + return; + } + + m_registeredMixers.erase( + std::remove(m_registeredMixers.begin(), m_registeredMixers.end(), mixer), + m_registeredMixers.end()); + DetachMixerReferences(mixer); +} + void AudioSystem::UnregisterSource(Components::AudioSourceComponent* source) { if (!source) { return; @@ -259,6 +327,34 @@ void AudioSystem::UnregisterSource(Components::AudioSourceComponent* source) { m_activeSources.end()); } +void AudioSystem::DetachMixerReferences(AudioMixer* mixer) { + if (!mixer) { + return; + } + + if (m_listenerReverbMixer == mixer) { + m_listenerReverbMixer = nullptr; + } + + for (Components::AudioListenerComponent* listener : m_registeredListenerComponents) { + if (listener != nullptr && listener->GetReverb() == mixer) { + listener->SetReverb(nullptr); + } + } + + for (Components::AudioSourceComponent* source : m_registeredSourceComponents) { + if (source != nullptr && source->GetOutputMixer() == mixer) { + source->SetOutputMixer(nullptr); + } + } + + for (AudioMixer* registeredMixer : m_registeredMixers) { + if (registeredMixer != nullptr && registeredMixer->GetOutputMixer() == mixer) { + registeredMixer->SetOutputMixer(nullptr); + } + } +} + void AudioSystem::ProcessSource(Components::AudioSourceComponent* source, float* buffer, uint32 frameCount, uint32 channels, uint32 sampleRate) { if (!source || !buffer) { return; diff --git a/engine/src/Components/AudioListenerComponent.cpp b/engine/src/Components/AudioListenerComponent.cpp index a1278035..2d2d63fd 100644 --- a/engine/src/Components/AudioListenerComponent.cpp +++ b/engine/src/Components/AudioListenerComponent.cpp @@ -1,15 +1,19 @@ #include #include +#include #include +#include namespace XCEngine { namespace Components { AudioListenerComponent::AudioListenerComponent() { + Audio::AudioSystem::Get().RegisterListenerComponent(this); } AudioListenerComponent::~AudioListenerComponent() { + Audio::AudioSystem::Get().UnregisterListenerComponent(this); } void AudioListenerComponent::SetMasterVolume(float volume) { @@ -24,18 +28,22 @@ void AudioListenerComponent::SetMute(bool mute) { void AudioListenerComponent::SetDopplerLevel(float level) { m_dopplerLevel = std::max(0.0f, std::min(5.0f, level)); + Audio::AudioSystem::Get().SetListenerDopplerLevel(m_dopplerLevel); } void AudioListenerComponent::SetSpeedOfSound(float metersPerSecond) { m_speedOfSound = std::max(1.0f, metersPerSecond); + Audio::AudioSystem::Get().SetSpeedOfSound(m_speedOfSound); } void AudioListenerComponent::SetReverbLevel(float level) { m_reverbLevel = std::max(0.0f, std::min(1.0f, level)); + Audio::AudioSystem::Get().SetListenerReverbLevel(m_reverbLevel); } void AudioListenerComponent::SetReverb(Audio::AudioMixer* reverb) { m_reverb = reverb; + Audio::AudioSystem::Get().SetListenerReverbMixer(m_reverb); } void AudioListenerComponent::Update(float deltaTime) { @@ -43,10 +51,62 @@ void AudioListenerComponent::Update(float deltaTime) { return; } - Math::Vector3 position = transform().GetPosition(); - Math::Quaternion rotation = transform().GetRotation(); + const Math::Vector3 position = transform().GetPosition(); + const Math::Quaternion rotation = transform().GetRotation(); + Math::Vector3 velocity = Math::Vector3::Zero(); + + if (m_hasLastPosition && deltaTime > 0.0f) { + velocity = (position - m_lastPosition) / deltaTime; + } Audio::AudioSystem::Get().SetListenerTransform(position, rotation); + Audio::AudioSystem::Get().SetListenerVelocity(velocity); + + m_lastPosition = position; + m_hasLastPosition = true; +} + +void AudioListenerComponent::Serialize(std::ostream& os) const { + os << "masterVolume=" << m_masterVolume << ";"; + os << "mute=" << (m_mute ? 1 : 0) << ";"; + os << "dopplerLevel=" << m_dopplerLevel << ";"; + os << "speedOfSound=" << m_speedOfSound << ";"; + os << "reverbLevel=" << m_reverbLevel << ";"; +} + +void AudioListenerComponent::Deserialize(std::istream& is) { + SetMasterVolume(1.0f); + SetMute(false); + SetDopplerLevel(1.0f); + SetSpeedOfSound(343.0f); + SetReverbLevel(1.0f); + + std::string token; + while (std::getline(is, token, ';')) { + if (token.empty()) { + continue; + } + + const size_t eqPos = token.find('='); + if (eqPos == std::string::npos) { + continue; + } + + const std::string key = token.substr(0, eqPos); + const std::string value = token.substr(eqPos + 1); + + if (key == "masterVolume") { + SetMasterVolume(std::stof(value)); + } else if (key == "mute") { + SetMute(std::stoi(value) != 0); + } else if (key == "dopplerLevel") { + SetDopplerLevel(std::stof(value)); + } else if (key == "speedOfSound") { + SetSpeedOfSound(std::stof(value)); + } else if (key == "reverbLevel") { + SetReverbLevel(std::stof(value)); + } + } } } // namespace Components diff --git a/engine/src/Components/AudioSourceComponent.cpp b/engine/src/Components/AudioSourceComponent.cpp index 8df7eb71..46340f01 100644 --- a/engine/src/Components/AudioSourceComponent.cpp +++ b/engine/src/Components/AudioSourceComponent.cpp @@ -146,9 +146,12 @@ void ApplyPanToBuffer(float* buffer, Audio::uint32 frameCount, Audio::uint32 cha } // namespace -AudioSourceComponent::AudioSourceComponent() = default; +AudioSourceComponent::AudioSourceComponent() { + Audio::AudioSystem::Get().RegisterSourceComponent(this); +} AudioSourceComponent::~AudioSourceComponent() { + Audio::AudioSystem::Get().UnregisterSourceComponent(this); if (m_playState == Audio::PlayState::Playing) { Audio::AudioSystem::Get().UnregisterSource(this); } diff --git a/tests/Components/test_audio_system.cpp b/tests/Components/test_audio_system.cpp index c7a4b2d1..c15ee080 100644 --- a/tests/Components/test_audio_system.cpp +++ b/tests/Components/test_audio_system.cpp @@ -218,6 +218,29 @@ TEST(AudioSystem, ListenerReverbMixerReceivesSend) { system.Shutdown(); } +TEST(AudioSystem, DestroyingMixerClearsDependentRoutes) { + AudioSystem& system = AudioSystem::Get(); + system.Shutdown(); + + AudioSourceComponent source; + AudioListenerComponent listener; + auto parentMixer = std::make_unique(); + auto childMixer = std::make_unique(); + + source.SetOutputMixer(parentMixer.get()); + listener.SetReverb(parentMixer.get()); + childMixer->SetOutputMixer(parentMixer.get()); + + parentMixer.reset(); + + EXPECT_EQ(source.GetOutputMixer(), nullptr); + EXPECT_EQ(listener.GetReverb(), nullptr); + EXPECT_EQ(system.GetListenerReverbMixer(), nullptr); + EXPECT_EQ(childMixer->GetOutputMixer(), nullptr); + + system.Shutdown(); +} + TEST(AudioSystem, ListenerComponentPublishesVelocityAndDopplerSettings) { AudioSystem& system = AudioSystem::Get(); system.Shutdown();