From a77ba067220b7264e2abe4904eeb7173146900e6 Mon Sep 17 00:00:00 2001 From: Erik Date: Fri, 24 Jul 2026 19:49:25 +0200 Subject: [PATCH] fix(audio): keep unavailable world slots quiescent Construct the retail-sized world voice ledger independently of OpenAL device availability so world reveal suspension is a safe no-op on machines without an audio backend. Co-authored-by: Erik Nilsson --- src/AcDream.App/Audio/OpenAlAudioEngine.cs | 12 ++++++++++-- .../Audio/OpenAlResourceLifetimeTests.cs | 15 +++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/AcDream.App/Audio/OpenAlAudioEngine.cs b/src/AcDream.App/Audio/OpenAlAudioEngine.cs index ba4f249a..959415ea 100644 --- a/src/AcDream.App/Audio/OpenAlAudioEngine.cs +++ b/src/AcDream.App/Audio/OpenAlAudioEngine.cs @@ -82,7 +82,7 @@ public sealed unsafe class OpenAlAudioEngine : IAudioEngine, IWorldAudioQuiescen public bool InUse; public uint PriorityBase; // raw priority from SoundEntry.Priority } - private readonly Slot3D[] _pool3D = new Slot3D[PoolSize3D]; + private readonly Slot3D[] _pool3D = CreateWorldSlots(); private int _pool3DCursor; // round-robin start private bool _worldAudioSuspended; @@ -159,7 +159,7 @@ public sealed unsafe class OpenAlAudioEngine : IAudioEngine, IWorldAudioQuiescen for (int i = 0; i < PoolSize3D; i++) { uint src = _resources.Create3DSource(); - _pool3D[i] = new Slot3D { SourceId = src, InUse = false }; + _pool3D[i].SourceId = src; } // UI sources are source-relative (attached to listener) so they @@ -520,4 +520,12 @@ public sealed unsafe class OpenAlAudioEngine : IAudioEngine, IWorldAudioQuiescen slot.PriorityBase = 0; slot.InUse = false; } + + private static Slot3D[] CreateWorldSlots() + { + var slots = new Slot3D[PoolSize3D]; + for (int i = 0; i < slots.Length; i++) + slots[i] = new Slot3D(); + return slots; + } } diff --git a/tests/AcDream.App.Tests/Audio/OpenAlResourceLifetimeTests.cs b/tests/AcDream.App.Tests/Audio/OpenAlResourceLifetimeTests.cs index f2c4e115..bcf599eb 100644 --- a/tests/AcDream.App.Tests/Audio/OpenAlResourceLifetimeTests.cs +++ b/tests/AcDream.App.Tests/Audio/OpenAlResourceLifetimeTests.cs @@ -89,6 +89,21 @@ public sealed class OpenAlResourceLifetimeTests Assert.Equal(1, api.CloseDeviceCalls); } + [Fact] + public void UnavailableEngineWorldQuiescenceRemainsASafeNoOp() + { + var api = new RecordingApi { ContextResult = 0 }; + var engine = new OpenAlAudioEngine(new Factory(api)); + + engine.SuspendWorldAudio(); + engine.StopAllForOwner(0x50000001u); + engine.ResumeWorldAudio(); + + Assert.False(engine.IsAvailable); + Assert.True(engine.IsDisposalComplete); + Assert.Empty(api.GeneratedSources); + } + private sealed class Factory(IOpenAlResourceApi api) : IOpenAlResourceApiFactory { public IOpenAlResourceApi Create() => api;