Let audio stop and free its players before the application quits

AudioService handles ApplicationExit.Preparing: it stops playback and keeps the
main loop alive briefly for the mixer fade and deferred player destruction.
This commit is contained in:
cthangover committed 2026-10-29 13:21:39 +01:00
1 parent 09b6d437e5
commit 7b0a57fea7
2 files changed
+62

No files matched your search

+34
View File
@@ -52,6 +52,7 @@ namespace Cthangover.Mods.Audio
/// <summary>Creates the three audio buses (Music/SFX/Ambient) if missing, the music and ambient players, and applies saved settings.</summary> /// <summary>Creates the three audio buses (Music/SFX/Ambient) if missing, the music and ambient players, and applies saved settings.</summary>
protected override void OnReady() protected override void OnReady()
{ {
ApplicationExit.Preparing += PrepareExit;
AddToGroup("AudioService"); AddToGroup("AudioService");
SetupBuses(); SetupBuses();
SetupMusicPlayer(); SetupMusicPlayer();
@@ -77,6 +78,39 @@ namespace Cthangover.Mods.Audio
} }
} }
/// <summary>Stops and detaches streams while the audio server still exists, including pooled sound players.</summary>
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 lastSoundsVolume = -1;
private int lastMusicsVolume = -1; private int lastMusicsVolume = -1;
private int lastAmbientVolume = -1; private int lastAmbientVolume = -1;
+28
View File
@@ -46,6 +46,33 @@ namespace Cthangover.Mods.Audio
/// </summary> /// </summary>
public bool IsCanAutoPlay { get; set; } = true; public bool IsCanAutoPlay { get; set; } = true;
/// <summary>Releases the pending fade and player reference when the audio mod leaves the tree.</summary>
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 Tween fadeTween;
private bool playlistsInited; private bool playlistsInited;
private double autoAdvanceCooldown; private double autoAdvanceCooldown;
@@ -53,6 +80,7 @@ namespace Cthangover.Mods.Audio
/// <summary>Registers in the <c>music_player</c> group and locates the AudioService music player.</summary> /// <summary>Registers in the <c>music_player</c> group and locates the AudioService music player.</summary>
protected override void OnReady() protected override void OnReady()
{ {
ApplicationExit.Preparing += PrepareExit;
AddToGroup("music_player"); AddToGroup("music_player");
TryFindAudioPlayer(); TryFindAudioPlayer();
} }