Fix missing audio on OpenAL when changing active device

#3621
This commit is contained in:
Wojtek Figat
2026-02-04 23:43:06 +01:00
parent f733611213
commit 7e9ee0610a
5 changed files with 86 additions and 11 deletions

View File

@@ -132,9 +132,7 @@ int32 AudioClip::GetFirstBufferIndex(float time, float& offset) const
if (_buffersStartTimes[i + 1] > time)
{
offset = time - _buffersStartTimes[i];
#if BUILD_DEBUG
ASSERT(Math::Abs(GetBufferStartTime(i) + offset - time) < 0.001f);
#endif
ASSERT_LOW_LAYER(Math::Abs(GetBufferStartTime(i) + offset - time) < 0.001f);
return i;
}
}

View File

@@ -18,6 +18,7 @@ class AudioSource;
API_CLASS(NoSpawn) class FLAXENGINE_API AudioClip : public BinaryAsset, public StreamableResource
{
DECLARE_BINARY_ASSET_HEADER(AudioClip, 2);
friend class AudioBackendOAL;
public:
/// <summary>

View File

@@ -14,6 +14,9 @@
#include "Engine/Audio/AudioListener.h"
#include "Engine/Audio/AudioSource.h"
#include "Engine/Audio/AudioSettings.h"
#include "Engine/Content/Content.h"
#include "Engine/Level/Level.h"
#include "Engine/Video/VideoPlayer.h"
// Include OpenAL library
// Source: https://github.com/kcat/openal-soft
@@ -73,6 +76,7 @@ namespace ALC
ALCdevice* Device = nullptr;
ALCcontext* Context = nullptr;
AudioBackend::FeatureFlags Features = AudioBackend::FeatureFlags::None;
bool Inited = false;
CriticalSection Locker;
Dictionary<uint32, SourceData> SourcesData;
@@ -164,12 +168,9 @@ namespace ALC
float Time;
};
void RebuildContext(const Array<AudioSourceState>& states)
void RebuildContext()
{
LOG(Info, "Rebuilding audio contexts");
ClearContext();
if (Device == nullptr)
return;
@@ -182,10 +183,16 @@ namespace ALC
Context = alcCreateContext(Device, attrList);
alcMakeContextCurrent(Context);
}
void RebuildListeners()
{
for (AudioListener* listener : Audio::Listeners)
Listener::Rebuild(listener);
}
void RebuildSources(const Array<AudioSourceState>& states)
{
for (int32 i = 0; i < states.Count(); i++)
{
AudioSource* source = Audio::Sources[i];
@@ -205,6 +212,13 @@ namespace ALC
}
}
void RebuildContext(const Array<AudioSourceState>& states)
{
RebuildContext();
RebuildListeners();
RebuildSources(states);
}
void RebuildContext(bool isChangingDevice)
{
Array<AudioSourceState> states;
@@ -629,6 +643,7 @@ AudioBackend::FeatureFlags AudioBackendOAL::Base_Features()
void AudioBackendOAL::Base_OnActiveDeviceChanged()
{
PROFILE_CPU();
PROFILE_MEM(Audio);
// Cleanup
@@ -659,9 +674,55 @@ void AudioBackendOAL::Base_OnActiveDeviceChanged()
LOG(Fatal, "Failed to open OpenAL device ({0}).", String(name));
return;
}
if (ALC::Inited)
LOG(Info, "Changed audio device to: {}", String(Audio::GetActiveDevice()->Name));
// Setup
ALC::RebuildContext(states);
// Rebuild context
ALC::RebuildContext();
if (ALC::Inited)
{
// Reload all audio clips to recreate their buffers
for (Asset* asset : Content::GetAssets())
{
if (auto* audioClip = ScriptingObject::Cast<AudioClip>(asset))
{
ScopeLock lock(audioClip->Locker);
// Clear old buffer IDs
for (uint32& bufferID : audioClip->Buffers)
bufferID = 0;
if (audioClip->IsStreamable())
{
// Let the streaming recreate missing buffers
audioClip->RequestStreamingUpdate();
}
else
{
// Reload audio clip
auto assetLock = audioClip->Storage->Lock();
audioClip->LoadChunk(0);
audioClip->Buffers[0] = AudioBackend::Buffer::Create();
audioClip->WriteBuffer(0);
}
}
}
// Reload all videos to recreate their buffers
for (VideoPlayer* videoPlayer : Level::GetActors<VideoPlayer>(true))
{
VideoBackendPlayer& player = videoPlayer->_player;
// Clear audio state
for (uint32& bufferID : player.AudioBuffers)
bufferID = 0;
player.NextAudioBuffer = 0;
player.AudioSource = 0;
}
}
ALC::RebuildListeners();
ALC::RebuildSources(states);
}
void AudioBackendOAL::Base_SetDopplerFactor(float value)
@@ -782,6 +843,7 @@ bool AudioBackendOAL::Base_Init()
if (ALC::IsExtensionSupported("AL_SOFT_source_spatialize"))
ALC::Features = EnumAddFlags(ALC::Features, FeatureFlags::SpatialMultiChannel);
#endif
ALC::Inited = true;
// Log service info
LOG(Info, "{0} ({1})", String(alGetString(AL_RENDERER)), String(alGetString(AL_VERSION)));

View File

@@ -473,6 +473,19 @@ public:
/// <returns>Found actors list.</returns>
API_FUNCTION() static Array<Actor*> GetActors(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type, bool activeOnly = false);
/// <summary>
/// Finds all the actors of the given type in all the loaded scenes.
/// </summary>
/// <typeparam name="T">Type of the object.</typeparam>
/// <param name="activeOnly">Finds only active actors.</param>
/// <returns>Found actors list.</returns>
template<typename T>
static Array<T*> GetActors(bool activeOnly = false)
{
Array<Actor*> actors = GetActors(T::GetStaticClass(), activeOnly);
return *(Array<T*>*)&actors;
}
/// <summary>
/// Finds all the scripts of the given type in an actor or all the loaded scenes.
/// </summary>

View File

@@ -15,6 +15,7 @@ class FLAXENGINE_API VideoPlayer : public Actor
{
DECLARE_SCENE_OBJECT(VideoPlayer);
API_AUTO_SERIALIZATION();
friend class AudioBackendOAL;
public:
/// <summary>