implement reverb tail, LoopMode, and playlist navigation

DynamicSound.ReverbTail now drives a seamless transition: when the current
song is within ReverbTail seconds of its full length, the next song is started
so its opening covers the outgoing tail. Both songs are audible during the
overlap; the outgoing one stops when its full length has elapsed.

API changes (continuous players only):
- Replaces `Loop : bool` with `LoopMode : enum { Single, All }`. Single keeps
  repeating the current song; All advances through the playlist and wraps
  back to index 0. There is no longer a "play once and stop" mode.
- The playlist resource is no longer mutated. The player tracks an internal
  index instead of popping songs off the front.
- Adds `get_current_song_index() -> int`, `advance_to_next_song()`, and
  `advance_to_track(index: int)` on each continuous wrapper. Both advance
  methods accelerate the outgoing song so its remaining time hits ReverbTail
  immediately, giving a smooth transition rather than a hard cut.

Internals: DynamicSoundPlayerCore now keeps an `_active_songs` array of an
inner _ActiveSong record (ids, start_time, song_length, reverb_tail,
started_next). _process advances time globally and runs the start-next /
stop-old conditions per active song, mirroring the OvaniPlayer reference.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Daniel Samson
2026-04-29 16:07:06 +01:00
co-authored by Claude Opus 4.7
parent e8224e3913
commit c989fc4eea
6 changed files with 204 additions and 90 deletions
+16 -2
View File
@@ -18,8 +18,10 @@ extends AudioStreamPlayer2D
Intensity = value;
if _core: _core.apply_stream_volumes(volume_db);
## When true, finished songs are re-appended to the back of the queue so the playlist cycles forever.
@export var Loop : bool;
## How the playlist loops when the current song ends.
## [code]Single[/code] keeps playing the same song; [code]All[/code] advances
## through the playlist and wraps from the last song back to the first.
@export var LoopMode : DynamicSoundConstants.LoopMode = DynamicSoundConstants.LoopMode.All;
var _core : DynamicSoundPlayerCore;
@@ -48,6 +50,18 @@ func _process(delta: float) -> void:
func play_playlist() -> void:
if _core: _core.play_playlist();
## Index of the song most recently started in the playlist.
func get_current_song_index() -> int:
return _core.get_current_song_index() if _core else 0;
## Skip to the next song (wraps to index 0). Smooth via the current song's reverb tail.
func advance_to_next_song() -> void:
if _core: _core.advance_to_next_song();
## Jump to the song at [param index]. Smooth via the current song's reverb tail.
func advance_to_track(index: int) -> void:
if _core: _core.advance_to_track(index);
## Pause playback. Call [method resume] to continue.
func pause() -> void:
if _core: _core.pause();