Quick answer: Godot AudioStreamPlayer finished signal firing when you call stop() manually? The signal is shared - check stream_paused and the playback position before treating it as natural end.
VO cue fires its 'subtitle done' handler the moment the player skips it, because stop() emits finished.
Track the stop call
var manual_stop = false
func _on_finished():
if manual_stop:
manual_stop = false
return
on_natural_end()The signal can't tell you why it fired - track the caller's intent.
Or compare playback position
If get_playback_position() is at or beyond stream.get_length(), you ended naturally. Otherwise something stopped you.
Use the AudioStreamPlayback API
For finer control, AudioStreamPlayer.get_stream_playback() exposes a playback object with is_playing() independent of the player's signal flow.
“finished says 'I'm not playing anymore' - that's it. Anything else, you derive.”
Centralize VO/cue completion logic in a coroutine that awaits both the signal and the natural duration, whichever fires first. Easier to debug than chained signal handlers.