audio: clear mixer routes on destruction

This commit is contained in:
2026-04-14 16:48:39 +08:00
parent ee03f7035b
commit a4c48c1b3f
6 changed files with 241 additions and 8 deletions

View File

@@ -1,18 +1,40 @@
#include <XCEngine/Audio/AudioMixer.h>
#include <XCEngine/Audio/AudioSystem.h>
#include <XCEngine/Audio/IAudioEffect.h>
#include <algorithm>
#include <cmath>
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<int>(AudioChannel::SideRight) + 1; ++i) {
m_channelVolumes[static_cast<AudioChannel>(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<size_t>(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);
}
}
}

View File

@@ -1,5 +1,6 @@
#include <XCEngine/Audio/AudioSystem.h>
#include <XCEngine/Audio/WindowsAudioBackend.h>
#include <XCEngine/Components/AudioListenerComponent.h>
#include <XCEngine/Components/AudioSourceComponent.h>
#include <algorithm>
#include <iostream>
@@ -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;