add SkipMode enum (UseReverbTail / CrossFade / Jump)

advance_to_next_song / advance_to_track now branch on the new SkipMode export:

- UseReverbTail (default): existing behavior. Accelerate the outgoing song so
  remaining time hits ReverbTail, both songs play during the overlap, outgoing
  stops naturally.
- CrossFade: programmatic volume crossfade. Outgoing fades from current volume
  to silent over CrossFadeDuration seconds (then stops), incoming starts silent
  and fades up to full over the same duration. Independent of any global fade.
- Jump: hard cut. Outgoing song's streams are stopped immediately and removed
  from the active list before the incoming starts.

For CrossFade we needed per-song fade state (the global _fade_db can't drive
two songs in opposite directions at once), so _ActiveSong gained fade_db /
fade_from_db / fade_to_db / fade_duration / fade_elapsed / fading /
stop_after_fade fields. _apply_volumes_to_song now adds both the global and
per-song fade_db to volume_db; process() advances each song's fade per-frame
and stops songs whose fades reach the silent target with stop_after_fade set.

CrossFadeDuration is hidden in the inspector via _validate_property unless
SkipMode == CrossFade. The natural end-of-song advance is unchanged — it still
uses the song's reverb tail. SkipMode only affects programmatic skips.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Daniel Samson
2026-04-29 16:31:37 +01:00
co-authored by Claude Opus 4.7
parent e792572635
commit df3e73b32e
6 changed files with 116 additions and 12 deletions
+14
View File
@@ -23,6 +23,16 @@ extends AudioStreamPlayer2D
## through the playlist and wraps from the last song back to the first.
@export var LoopMode : DynamicSoundConstants.LoopMode = DynamicSoundConstants.LoopMode.All;
## How [method advance_to_next_song] and [method advance_to_track] transition between songs.
## Does not affect the natural end-of-song advance, which always uses the song's reverb tail.
@export var SkipMode : DynamicSoundConstants.SkipMode = DynamicSoundConstants.SkipMode.UseReverbTail:
set(value):
SkipMode = value;
notify_property_list_changed();
## Crossfade duration in seconds when [member SkipMode] is [code]CrossFade[/code].
@export var CrossFadeDuration : float = 1.0;
var _core : DynamicSoundPlayerCore;
func _set(property: StringName, value: Variant) -> bool:
@@ -39,6 +49,10 @@ func _enter_tree() -> void:
if bus == &"Master" and AudioServer.get_bus_index("Music") != -1:
bus = "Music";
func _validate_property(property: Dictionary) -> void:
if property.name == "CrossFadeDuration" and SkipMode != DynamicSoundConstants.SkipMode.CrossFade:
property.usage &= ~PROPERTY_USAGE_EDITOR;
func _ready() -> void:
_core = DynamicSoundPlayerCore.new(self);
_core.ready();