diff --git a/src/AudioService.cs b/src/AudioService.cs index 0607b0c..4a7b199 100644 --- a/src/AudioService.cs +++ b/src/AudioService.cs @@ -52,6 +52,7 @@ namespace Cthangover.Mods.Audio /// Creates the three audio buses (Music/SFX/Ambient) if missing, the music and ambient players, and applies saved settings. protected override void OnReady() { + ApplicationExit.Preparing += PrepareExit; AddToGroup("AudioService"); SetupBuses(); SetupMusicPlayer(); @@ -77,6 +78,39 @@ namespace Cthangover.Mods.Audio } } + /// Stops and detaches streams while the audio server still exists, including pooled sound players. + protected override void OnExitTree() + { + ApplicationExit.Preparing -= PrepareExit; + ReleasePlayer(musicPlayer); + ReleasePlayer(ambientPlayer); + foreach (var player in soundPlayers.Values) ReleasePlayer(player); + soundPlayers.Clear(); + musicPlayer = null; + ambientPlayer = null; + } + + private async System.Threading.Tasks.Task PrepareExit(SceneTree tree) + { + SetProcess(false); + ambientPlayer.Finished -= OnAmbientFinished; + ReleasePlayer(musicPlayer); + ReleasePlayer(ambientPlayer); + foreach (var player in soundPlayers.Values) ReleasePlayer(player); + // Stop is queued to the mixer. Keep the main loop alive for its fade + // and deferred playback destruction before SceneTree.Quit shuts the server down. + var deadline = Godot.Time.GetTicksMsec() + 150; + while (Godot.Time.GetTicksMsec() < deadline) + await ToSignal(tree, SceneTree.SignalName.ProcessFrame); + } + + private static void ReleasePlayer(AudioStreamPlayer player) + { + if (!IsInstanceValid(player)) return; + player.Stop(); + player.Stream = null; + } + private int lastSoundsVolume = -1; private int lastMusicsVolume = -1; private int lastAmbientVolume = -1; diff --git a/src/MusicPlayerBehaviour.cs b/src/MusicPlayerBehaviour.cs index 20518a7..06ed7e6 100644 --- a/src/MusicPlayerBehaviour.cs +++ b/src/MusicPlayerBehaviour.cs @@ -46,6 +46,33 @@ namespace Cthangover.Mods.Audio /// public bool IsCanAutoPlay { get; set; } = true; + /// Releases the pending fade and player reference when the audio mod leaves the tree. + protected override void OnExitTree() + { + ApplicationExit.Preparing -= PrepareExit; + StopForExit(); + } + + private System.Threading.Tasks.Task PrepareExit(SceneTree tree) + { + StopForExit(); + return System.Threading.Tasks.Task.CompletedTask; + } + + private void StopForExit() + { + IsCanAutoPlay = false; + SetProcess(false); + if (IsInstanceValid(fadeTween)) fadeTween.Kill(); + fadeTween = null; + if (IsInstanceValid(audioPlayer)) + { + audioPlayer.Stop(); + audioPlayer.Stream = null; + } + audioPlayer = null; + } + private Tween fadeTween; private bool playlistsInited; private double autoAdvanceCooldown; @@ -53,6 +80,7 @@ namespace Cthangover.Mods.Audio /// Registers in the music_player group and locates the AudioService music player. protected override void OnReady() { + ApplicationExit.Preparing += PrepareExit; AddToGroup("music_player"); TryFindAudioPlayer(); }