From 7630872229f70b42786500171222061082e2e3fd Mon Sep 17 00:00:00 2001 From: DaBultz <27208227+DaBultz@users.noreply.github.com> Date: Sat, 2 May 2026 11:41:10 +0200 Subject: [PATCH 1/8] replace NAudio with SoundFlow # Conflicts: # OofPlugin/OofPlugin.csproj --- OofPlugin/OofPlugin.csproj | 4 +- OofPlugin/SoundManager.cs | 110 ++++++++++++++++------------------- OofPlugin/packages.lock.json | 17 ++---- 3 files changed, 55 insertions(+), 76 deletions(-) diff --git a/OofPlugin/OofPlugin.csproj b/OofPlugin/OofPlugin.csproj index 7dd73a6..cb8a463 100644 --- a/OofPlugin/OofPlugin.csproj +++ b/OofPlugin/OofPlugin.csproj @@ -23,8 +23,8 @@ - - + + diff --git a/OofPlugin/SoundManager.cs b/OofPlugin/SoundManager.cs index 80bbe1b..ceb08ea 100644 --- a/OofPlugin/SoundManager.cs +++ b/OofPlugin/SoundManager.cs @@ -1,9 +1,15 @@ -using NAudio.Wave; +using SoundFlow.Abstracts.Devices; +using SoundFlow.Backends.MiniAudio; +using SoundFlow.Components; +using SoundFlow.Providers; using System; using System.IO; +using System.Linq; using System.Numerics; using System.Threading; using System.Threading.Tasks; +using AudioFormat = SoundFlow.Structs.AudioFormat; +using PlaybackState = SoundFlow.Enums.PlaybackState; namespace OofPlugin; @@ -12,9 +18,13 @@ internal class SoundManager : IDisposable { private readonly DeadPlayersList DeadPlayersList; // sound - public bool isSoundPlaying { get; private set; } = false; - private DirectSoundOut? soundOut; - private string? soundFile; + private string soundFile; + + private MiniAudioEngine engine; + private AudioPlaybackDevice playbackDevice; + private AudioFormat audioFormat; + private StreamDataProvider dataProvider; + private SoundPlayer player; internal CancellationTokenSource CancelToken; @@ -22,17 +32,30 @@ public SoundManager(OofPlugin plugin) { Configuration = plugin.Configuration; DeadPlayersList = plugin.DeadPlayersList; + engine = new MiniAudioEngine(); + engine.UpdateAudioDevicesInfo(); + + // Get the system default playback device + var defaultDevice = engine.PlaybackDevices.FirstOrDefault(x => x.IsDefault); + audioFormat = AudioFormat.DvdHq; + playbackDevice = engine.InitializePlaybackDevice(defaultDevice, audioFormat); + LoadFile(); + dataProvider = new StreamDataProvider(engine, audioFormat, File.OpenRead(soundFile)); + player = new SoundPlayer(engine, audioFormat, dataProvider); + player.PlaybackEnded += (_, _) => player.Stop(); + + playbackDevice.MasterMixer.AddComponent(player); + playbackDevice.Start(); + CancelToken = new CancellationTokenSource(); Task.Run(() => OofAudioPolling(CancelToken.Token)); } public void LoadFile() { if (string.IsNullOrEmpty(Configuration.DefaultSoundImportPath)) { - soundFile = Path.Combine( - Dalamud.PluginInterface.AssemblyLocation.Directory!.FullName, - "oof.wav"); + soundFile = Path.Combine(Dalamud.PluginInterface.AssemblyLocation.Directory!.FullName, "oof.wav"); return; } @@ -40,54 +63,19 @@ public void LoadFile() { } public void Stop() { - soundOut?.Pause(); - soundOut?.Dispose(); - soundOut = null; + player.Stop(); } public void Play(CancellationToken token, float volume = 1f) { _ = Task.Run(() => { - isSoundPlaying = true; - - WaveStream reader; - try { - reader = new MediaFoundationReader(soundFile); - } - catch (Exception ex) { - isSoundPlaying = false; - Dalamud.Log.Error("Failed to read sound file", ex); - return; + // To play a sound, we need to stop it first if it's already playing + if (player.State == PlaybackState.Playing) { + player.Stop(); } - var audioStream = - new WaveChannel32(reader) { - Volume = Configuration.Volume * volume, - PadWithZeroes = false // you need this or else playbackstopped event will not fire - }; - - using (reader) { - soundOut?.Pause(); - soundOut?.Dispose(); - soundOut = new DirectSoundOut(); - - try { - soundOut.Init(audioStream); - soundOut.Play(); - - soundOut.PlaybackStopped += (_, _) => { isSoundPlaying = false; }; - } - catch (Exception ex) { - isSoundPlaying = false; - Dalamud.Log.Error("Failed to play sound", ex); - } - } - }, token).ContinueWith((t) => { - t.Exception?.Handle((e) => { - Dalamud.Log.Error($"Failed to dispose DirectSoundOut {e} "); - - return true; - }); - }); + player.Volume = volume; + player.Play(); + }, token); } private async Task OofAudioPolling(CancellationToken token) { @@ -100,8 +88,7 @@ private async Task OofAudioPolling(CancellationToken token) { // Run on framework thread AND await it so exceptions are observed await Dalamud.Framework.RunOnFrameworkThread(() => { - var localPlayer = - Dalamud.ObjectTable.LocalPlayer; + var localPlayer = Dalamud.ObjectTable.LocalPlayer; if (localPlayer is null) return; @@ -111,10 +98,8 @@ await Dalamud.Framework.RunOnFrameworkThread(() => { float volume = 1f; - if (Configuration.DistanceBasedOof && - player.Distance != Vector3.Zero) { - var dist = - Vector3.Distance(localPlayer.Position, player.Distance); + if (Configuration.DistanceBasedOof && player.Distance != Vector3.Zero) { + var dist = Vector3.Distance(localPlayer.Position, player.Distance); volume = VolumeFromDist(dist); } @@ -139,8 +124,8 @@ public float VolumeFromDist(float dist, float distMax = 30f) { dist = Math.Min(dist, distMax); var falloff = Configuration.DistanceFalloff > 0 - ? 3f - Configuration.DistanceFalloff * 3f - : 2.999f; + ? 3f - Configuration.DistanceFalloff * 3f + : 2.999f; var vol = 1f - ((dist / distMax) * (1f / falloff)); return Math.Max(Configuration.DistanceMinVolume, vol); @@ -164,8 +149,11 @@ async Task PlayTest(float volume) { public void Dispose() { CancelToken.Cancel(); CancelToken.Dispose(); - - soundOut?.Dispose(); - soundOut = null; + + playbackDevice.MasterMixer.RemoveComponent(player); + player.Dispose(); + dataProvider.Dispose(); + playbackDevice.Dispose(); + engine.Dispose(); } -} +} \ No newline at end of file diff --git a/OofPlugin/packages.lock.json b/OofPlugin/packages.lock.json index 725b17e..5797274 100644 --- a/OofPlugin/packages.lock.json +++ b/OofPlugin/packages.lock.json @@ -14,20 +14,11 @@ "resolved": "1.2.39", "contentHash": "fcFN01tDTIQqDuTwr1jUQK/geofiwjG5DycJQOnC72i1SsLAk1ELe+apBOuZ11UMQG8YKFZG1FgvjZPbqHyatg==" }, - "NAudio.Core": { + "SoundFlow": { "type": "Direct", - "requested": "[2.2.1, )", - "resolved": "2.2.1", - "contentHash": "GgkdP6K/7FqXFo7uHvoqGZTJvW4z8g2IffhOO4JHaLzKCdDOUEzVKtveoZkCuUX8eV2HAINqi7VFqlFndrnz/g==" - }, - "NAudio.Wasapi": { - "type": "Direct", - "requested": "[2.2.1, )", - "resolved": "2.2.1", - "contentHash": "lFfXoqacZZe0WqNChJgGYI+XV/n/61LzPHT3C1CJp4khoxeo2sziyX5wzNYWeCMNbsWxFvT3b3iXeY1UYjBhZw==", - "dependencies": { - "NAudio.Core": "2.2.1" - } + "requested": "[1.4.0, )", + "resolved": "1.4.0", + "contentHash": "PVc8NEpwmx16qJdOGRvc7Kpvs/3hNZBuldErmnMQSh9IClAA2UWrafcUIQUxki2GrWB2g71hPSDIUKEo3nRgeA==" } } } From 8b2f84963f24716335d97f6d7b7aee2e4e9be03a Mon Sep 17 00:00:00 2001 From: DaBultz <27208227+DaBultz@users.noreply.github.com> Date: Sat, 2 May 2026 15:38:48 +0200 Subject: [PATCH 2/8] custom audio file now works again --- OofPlugin/SoundManager.cs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/OofPlugin/SoundManager.cs b/OofPlugin/SoundManager.cs index ceb08ea..2cbdd57 100644 --- a/OofPlugin/SoundManager.cs +++ b/OofPlugin/SoundManager.cs @@ -41,13 +41,6 @@ public SoundManager(OofPlugin plugin) { playbackDevice = engine.InitializePlaybackDevice(defaultDevice, audioFormat); LoadFile(); - dataProvider = new StreamDataProvider(engine, audioFormat, File.OpenRead(soundFile)); - player = new SoundPlayer(engine, audioFormat, dataProvider); - - player.PlaybackEnded += (_, _) => player.Stop(); - - playbackDevice.MasterMixer.AddComponent(player); - playbackDevice.Start(); CancelToken = new CancellationTokenSource(); Task.Run(() => OofAudioPolling(CancelToken.Token)); @@ -68,6 +61,18 @@ public void Stop() { public void Play(CancellationToken token, float volume = 1f) { _ = Task.Run(() => { + dataProvider = new StreamDataProvider(engine, audioFormat, File.OpenRead(soundFile)); + player = new SoundPlayer(engine, audioFormat, dataProvider); + playbackDevice.MasterMixer.AddComponent(player); + playbackDevice.Start(); + + player.PlaybackEnded += (_, _) => { + player.Stop(); + playbackDevice.MasterMixer.RemoveComponent(player); + player.Dispose(); + }; + + // To play a sound, we need to stop it first if it's already playing if (player.State == PlaybackState.Playing) { player.Stop(); From b26dcfc26328b87b03cd494fdbc619ca9f3ac118 Mon Sep 17 00:00:00 2001 From: DaBultz <27208227+DaBultz@users.noreply.github.com> Date: Sat, 2 May 2026 15:39:55 +0200 Subject: [PATCH 3/8] allow audio to overlap The audio can now overlap if multiple people die in quick succession --- OofPlugin/SoundManager.cs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/OofPlugin/SoundManager.cs b/OofPlugin/SoundManager.cs index 2cbdd57..9be0cda 100644 --- a/OofPlugin/SoundManager.cs +++ b/OofPlugin/SoundManager.cs @@ -23,8 +23,7 @@ internal class SoundManager : IDisposable { private MiniAudioEngine engine; private AudioPlaybackDevice playbackDevice; private AudioFormat audioFormat; - private StreamDataProvider dataProvider; - private SoundPlayer player; + internal CancellationTokenSource CancelToken; @@ -56,28 +55,29 @@ public void LoadFile() { } public void Stop() { - player.Stop(); + // When an audio plays this will cause a tiny lag spike, but as this is only used in the ConfigWindow, it + // should be fine + playbackDevice.Stop(); } public void Play(CancellationToken token, float volume = 1f) { _ = Task.Run(() => { - dataProvider = new StreamDataProvider(engine, audioFormat, File.OpenRead(soundFile)); - player = new SoundPlayer(engine, audioFormat, dataProvider); + var dataProvider = new StreamDataProvider(engine, audioFormat, File.OpenRead(soundFile)); + var player = new SoundPlayer(engine, audioFormat, dataProvider); playbackDevice.MasterMixer.AddComponent(player); playbackDevice.Start(); + // this cleans up after the playback ends player.PlaybackEnded += (_, _) => { player.Stop(); playbackDevice.MasterMixer.RemoveComponent(player); player.Dispose(); }; - - // To play a sound, we need to stop it first if it's already playing if (player.State == PlaybackState.Playing) { player.Stop(); } - + player.Volume = volume; player.Play(); }, token); @@ -155,9 +155,6 @@ public void Dispose() { CancelToken.Cancel(); CancelToken.Dispose(); - playbackDevice.MasterMixer.RemoveComponent(player); - player.Dispose(); - dataProvider.Dispose(); playbackDevice.Dispose(); engine.Dispose(); } From 41c974d7f0b8cc7a6f0600f8110e453dfc8a3927 Mon Sep 17 00:00:00 2001 From: DaBultz <27208227+DaBultz@users.noreply.github.com> Date: Sun, 3 May 2026 17:29:52 +0200 Subject: [PATCH 4/8] resolve rebase conflcit --- OofPlugin/OofPlugin.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/OofPlugin/OofPlugin.csproj b/OofPlugin/OofPlugin.csproj index cb8a463..4868a88 100644 --- a/OofPlugin/OofPlugin.csproj +++ b/OofPlugin/OofPlugin.csproj @@ -23,7 +23,6 @@ - From fec6598fd900c77fa7c983a16ef435a1e98ab7a8 Mon Sep 17 00:00:00 2001 From: DaBultz <27208227+DaBultz@users.noreply.github.com> Date: Mon, 4 May 2026 13:58:47 +0200 Subject: [PATCH 5/8] audio overlapping can be toggled --- OofPlugin/Configuration.cs | 1 + OofPlugin/SoundManager.cs | 15 ++++++++++++--- OofPlugin/Windows/ConfigWindow.cs | 8 ++++++++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/OofPlugin/Configuration.cs b/OofPlugin/Configuration.cs index 25fd5bf..2fb4e61 100644 --- a/OofPlugin/Configuration.cs +++ b/OofPlugin/Configuration.cs @@ -26,6 +26,7 @@ public class Configuration : IPluginConfiguration { //audio settings public float Volume { get; set; } = 0.5f; public string DefaultSoundImportPath { get; set; } = string.Empty; + public bool AudioOverlap { get; set; } = false; // the below exist just to make saving less cumbersome diff --git a/OofPlugin/SoundManager.cs b/OofPlugin/SoundManager.cs index 9be0cda..53076ba 100644 --- a/OofPlugin/SoundManager.cs +++ b/OofPlugin/SoundManager.cs @@ -23,6 +23,7 @@ internal class SoundManager : IDisposable { private MiniAudioEngine engine; private AudioPlaybackDevice playbackDevice; private AudioFormat audioFormat; + private SoundPlayer? currentPlayer; internal CancellationTokenSource CancelToken; @@ -36,6 +37,7 @@ public SoundManager(OofPlugin plugin) { // Get the system default playback device var defaultDevice = engine.PlaybackDevices.FirstOrDefault(x => x.IsDefault); + audioFormat = AudioFormat.DvdHq; playbackDevice = engine.InitializePlaybackDevice(defaultDevice, audioFormat); @@ -62,8 +64,18 @@ public void Stop() { public void Play(CancellationToken token, float volume = 1f) { _ = Task.Run(() => { + if (!Configuration.AudioOverlap && currentPlayer != null) { + currentPlayer.Stop(); + playbackDevice.MasterMixer.RemoveComponent(currentPlayer); + currentPlayer.Dispose(); + currentPlayer = null; + } + var dataProvider = new StreamDataProvider(engine, audioFormat, File.OpenRead(soundFile)); var player = new SoundPlayer(engine, audioFormat, dataProvider); + + currentPlayer = player; + playbackDevice.MasterMixer.AddComponent(player); playbackDevice.Start(); @@ -74,9 +86,6 @@ public void Play(CancellationToken token, float volume = 1f) { player.Dispose(); }; - if (player.State == PlaybackState.Playing) { - player.Stop(); - } player.Volume = volume; player.Play(); diff --git a/OofPlugin/Windows/ConfigWindow.cs b/OofPlugin/Windows/ConfigWindow.cs index 768234c..19899a2 100644 --- a/OofPlugin/Windows/ConfigWindow.cs +++ b/OofPlugin/Windows/ConfigWindow.cs @@ -59,6 +59,14 @@ public override void Draw() { ImGui.Spacing(); + ImGui.TextColoredWrapped(headingColor, "Settings"); + var audioOverlap = Configuration.AudioOverlap; + if (ImGui.Checkbox("Allow audio overlap###config:overlap", ref audioOverlap)) { + Configuration.AudioOverlap = audioOverlap; + Configuration.Save(); + }; + ImGuiComponents.HelpMarker("Overlap audio instead of stopping an already playing audio"); + ImGui.Spacing(); ImGui.TextColoredWrapped(headingColor, "Play sound on"); // when self falls options From c6782fe13b443894be16d93d491a5765b3efbda7 Mon Sep 17 00:00:00 2001 From: DaBultz <27208227+DaBultz@users.noreply.github.com> Date: Mon, 4 May 2026 14:20:02 +0200 Subject: [PATCH 6/8] stopping audio playback now stops all created players now we keep track of all active players, instead of only 1. all operations modifying the activePlayers is done via a Lock, to ensure multiple tasks doesn't modify the list at the same time --- OofPlugin/SoundManager.cs | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/OofPlugin/SoundManager.cs b/OofPlugin/SoundManager.cs index 53076ba..432c7d0 100644 --- a/OofPlugin/SoundManager.cs +++ b/OofPlugin/SoundManager.cs @@ -3,6 +3,7 @@ using SoundFlow.Components; using SoundFlow.Providers; using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Numerics; @@ -23,7 +24,7 @@ internal class SoundManager : IDisposable { private MiniAudioEngine engine; private AudioPlaybackDevice playbackDevice; private AudioFormat audioFormat; - private SoundPlayer? currentPlayer; + private List activePlayers = new(); internal CancellationTokenSource CancelToken; @@ -43,6 +44,9 @@ public SoundManager(OofPlugin plugin) { LoadFile(); + playbackDevice.Start(); + + CancelToken = new CancellationTokenSource(); Task.Run(() => OofAudioPolling(CancelToken.Token)); } @@ -56,31 +60,37 @@ public void LoadFile() { soundFile = Configuration.DefaultSoundImportPath; } + /// + /// Stops all active sound players and cleans up their resources. + /// public void Stop() { - // When an audio plays this will cause a tiny lag spike, but as this is only used in the ConfigWindow, it - // should be fine - playbackDevice.Stop(); + lock (activePlayers) { + activePlayers.ForEach(x => { + x.Stop(); + playbackDevice.MasterMixer.RemoveComponent(x); + x.Dispose(); + }); + + activePlayers.Clear(); + } } public void Play(CancellationToken token, float volume = 1f) { _ = Task.Run(() => { - if (!Configuration.AudioOverlap && currentPlayer != null) { - currentPlayer.Stop(); - playbackDevice.MasterMixer.RemoveComponent(currentPlayer); - currentPlayer.Dispose(); - currentPlayer = null; + if (!Configuration.AudioOverlap) { + Stop(); } var dataProvider = new StreamDataProvider(engine, audioFormat, File.OpenRead(soundFile)); var player = new SoundPlayer(engine, audioFormat, dataProvider); - currentPlayer = player; + lock(activePlayers) { activePlayers.Add(player); } playbackDevice.MasterMixer.AddComponent(player); - playbackDevice.Start(); // this cleans up after the playback ends player.PlaybackEnded += (_, _) => { + lock(activePlayers) { activePlayers.Remove(player); }; player.Stop(); playbackDevice.MasterMixer.RemoveComponent(player); player.Dispose(); From e605a1f19cba3ff50a2c47ab56abff638a8252f9 Mon Sep 17 00:00:00 2001 From: DaBultz <27208227+DaBultz@users.noreply.github.com> Date: Mon, 4 May 2026 14:51:28 +0200 Subject: [PATCH 7/8] audio device can now be reinitialized Reintiialize/Initializing the audio device, will first safely dispose the old one, before attempting to make a new one. --- OofPlugin/SoundManager.cs | 72 +++++++++++++++++++++---------- OofPlugin/Windows/ConfigWindow.cs | 9 +++- 2 files changed, 57 insertions(+), 24 deletions(-) diff --git a/OofPlugin/SoundManager.cs b/OofPlugin/SoundManager.cs index 432c7d0..bc3268b 100644 --- a/OofPlugin/SoundManager.cs +++ b/OofPlugin/SoundManager.cs @@ -10,7 +10,6 @@ using System.Threading; using System.Threading.Tasks; using AudioFormat = SoundFlow.Structs.AudioFormat; -using PlaybackState = SoundFlow.Enums.PlaybackState; namespace OofPlugin; @@ -19,12 +18,12 @@ internal class SoundManager : IDisposable { private readonly DeadPlayersList DeadPlayersList; // sound - private string soundFile; + private string? soundFile; - private MiniAudioEngine engine; - private AudioPlaybackDevice playbackDevice; + private readonly MiniAudioEngine engine; + private AudioPlaybackDevice? playbackDevice; private AudioFormat audioFormat; - private List activePlayers = new(); + private readonly List activePlayers = new(); internal CancellationTokenSource CancelToken; @@ -33,19 +32,10 @@ public SoundManager(OofPlugin plugin) { Configuration = plugin.Configuration; DeadPlayersList = plugin.DeadPlayersList; - engine = new MiniAudioEngine(); - engine.UpdateAudioDevicesInfo(); - - // Get the system default playback device - var defaultDevice = engine.PlaybackDevices.FirstOrDefault(x => x.IsDefault); - - audioFormat = AudioFormat.DvdHq; - playbackDevice = engine.InitializePlaybackDevice(defaultDevice, audioFormat); - LoadFile(); - playbackDevice.Start(); - + engine = new MiniAudioEngine(); + InitializeAudioDevice(); CancelToken = new CancellationTokenSource(); Task.Run(() => OofAudioPolling(CancelToken.Token)); @@ -60,6 +50,40 @@ public void LoadFile() { soundFile = Configuration.DefaultSoundImportPath; } + /// + /// Create the playback device using the current system default output and starts it, if it's already + /// created it will be destroyed and re-created, safely + /// Falls back to logging an error if device discovery or initialization fails. + /// + public void InitializeAudioDevice() { + DestroyAudioDevice(); + + try { + engine.UpdateAudioDevicesInfo(); + + // Get the system default playback device + var defaultDevice = engine.PlaybackDevices.FirstOrDefault(x => x.IsDefault); + + audioFormat = AudioFormat.DvdHq; + playbackDevice = engine.InitializePlaybackDevice(defaultDevice, audioFormat); + playbackDevice.Start(); + } + catch (Exception ex) { + Dalamud.Log.Error(ex, "OOF: OofAudioRecreateAudioDevice failed"); + } + } + + + private void DestroyAudioDevice() { + Stop(); + + if(playbackDevice != null) { + playbackDevice.Stop(); + playbackDevice.Dispose(); + } + + } + /// /// Stops all active sound players and cleans up their resources. /// @@ -67,7 +91,7 @@ public void Stop() { lock (activePlayers) { activePlayers.ForEach(x => { x.Stop(); - playbackDevice.MasterMixer.RemoveComponent(x); + playbackDevice?.MasterMixer.RemoveComponent(x); x.Dispose(); }); @@ -80,23 +104,27 @@ public void Play(CancellationToken token, float volume = 1f) { if (!Configuration.AudioOverlap) { Stop(); } + + if (soundFile == null) { + Dalamud.Log.Error("OOF: No sound file found to play."); + return; + } var dataProvider = new StreamDataProvider(engine, audioFormat, File.OpenRead(soundFile)); var player = new SoundPlayer(engine, audioFormat, dataProvider); lock(activePlayers) { activePlayers.Add(player); } - playbackDevice.MasterMixer.AddComponent(player); + playbackDevice?.MasterMixer.AddComponent(player); // this cleans up after the playback ends player.PlaybackEnded += (_, _) => { - lock(activePlayers) { activePlayers.Remove(player); }; + lock(activePlayers) { activePlayers.Remove(player); } player.Stop(); - playbackDevice.MasterMixer.RemoveComponent(player); + playbackDevice?.MasterMixer.RemoveComponent(player); player.Dispose(); }; - player.Volume = volume; player.Play(); }, token); @@ -174,7 +202,7 @@ public void Dispose() { CancelToken.Cancel(); CancelToken.Dispose(); - playbackDevice.Dispose(); + playbackDevice?.Dispose(); engine.Dispose(); } } \ No newline at end of file diff --git a/OofPlugin/Windows/ConfigWindow.cs b/OofPlugin/Windows/ConfigWindow.cs index 19899a2..48f2bc3 100644 --- a/OofPlugin/Windows/ConfigWindow.cs +++ b/OofPlugin/Windows/ConfigWindow.cs @@ -66,6 +66,13 @@ public override void Draw() { Configuration.Save(); }; ImGuiComponents.HelpMarker("Overlap audio instead of stopping an already playing audio"); + + ImGui.Spacing(); + if (ImGui.Button("Reload Audio Devices")) { + Plugin.SoundManager.InitializeAudioDevice(); + } + + ImGui.Spacing(); ImGui.TextColoredWrapped(headingColor, "Play sound on"); @@ -329,7 +336,5 @@ public static FileDialogManager SetupFileManager() { return fileManager; } - - } From fd4d0a815ae38bf578b759d642ab217ee56ec915 Mon Sep 17 00:00:00 2001 From: DaBultz <27208227+DaBultz@users.noreply.github.com> Date: Sun, 10 May 2026 21:05:59 +0200 Subject: [PATCH 8/8] only play/stop audio when player is initialized Incase the initilized fails, this should prevent crashes from happending, when it attempts to play an audio/stop one. There might be a better way of handling this --- OofPlugin/SoundManager.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/OofPlugin/SoundManager.cs b/OofPlugin/SoundManager.cs index bc3268b..8a1633c 100644 --- a/OofPlugin/SoundManager.cs +++ b/OofPlugin/SoundManager.cs @@ -25,7 +25,8 @@ internal class SoundManager : IDisposable { private AudioFormat audioFormat; private readonly List activePlayers = new(); - + private bool isInitialized = false; + internal CancellationTokenSource CancelToken; public SoundManager(OofPlugin plugin) { @@ -67,8 +68,11 @@ public void InitializeAudioDevice() { audioFormat = AudioFormat.DvdHq; playbackDevice = engine.InitializePlaybackDevice(defaultDevice, audioFormat); playbackDevice.Start(); + + isInitialized = true; } catch (Exception ex) { + isInitialized = false; Dalamud.Log.Error(ex, "OOF: OofAudioRecreateAudioDevice failed"); } } @@ -88,6 +92,8 @@ private void DestroyAudioDevice() { /// Stops all active sound players and cleans up their resources. /// public void Stop() { + if (!isInitialized) return; + lock (activePlayers) { activePlayers.ForEach(x => { x.Stop(); @@ -100,6 +106,8 @@ public void Stop() { } public void Play(CancellationToken token, float volume = 1f) { + if (!isInitialized) return; + _ = Task.Run(() => { if (!Configuration.AudioOverlap) { Stop();