Godot: No "Finished" signal with looped audio

Created on 21 Jul 2019  路  3Comments  路  Source: godotengine/godot

Using 3.1.1

Audio that is set to loop does not send a "finished" signal when it restarts.
To get around this, I disabled loop, and loop through code (on the finished signal, play)
That is almost good enough, but I have found:
Manually looping like this causes the audio to have a slight delay before it restarts, so the looping audio is not seamless.
When set to automatically loop, there is no (observable) delay. However as I said, with automatic loop set I can't get the finished signal.

discussion enhancement audio

Most helpful comment

They should totally emit a signal, but I think it should be a different one to avoid any compatibility break, something like signal looped

All 3 comments

They should totally emit a signal, but I think it should be a different one to avoid any compatibility break, something like signal looped

For fun, I decided to guess on how to implement this feature

Background: Godot implements scripting and audio on different threads. To avoid avoiding mixing on the main thread, Audioplayer::play() only sets flags in the Audioplayer itself and calls AudioStreamPlayback::play() which holds the state instance for static AudioStream resources. When the Audioplayer ends, Scenetree calls Audioplayer::_notification(int) to emit signals.

The Problem: AudioStreamPlayer::_mix_internal(bool) is managed by the audio thread and should not be emitting signals. Although AudioStreamPlayer mix creates AudioFrame to be generated into the audio buffer, the Class store mostly user settings rather than player state. AudioStreamPlayback::get_loop_count() can only provide count information but cannot discern when the playback happened.

Proposal: Implement play() flag setting in reverse.

  1. Add a signal looped to Audioplayer
    https://github.com/godotengine/godot/blob/17732fe698b835c29f77c84f329b2ed6cab215ce/scene/audio/audio_stream_player.cpp#L422 .

  2. Call AudioStreamPlayback::get_loop_count() in the beginning of _mix_internal() and call it again to the end to see if the player looped and set a flag for later processing

  3. In the NOTIFICATION_INTERNAL_PROCESS state, check the flag and `emit_signal("looped")

    https://github.com/godotengine/godot/blob/17732fe698b835c29f77c84f329b2ed6cab215ce/scene/audio/audio_stream_player.cpp#L150

Like play, this loop will only emit signals in between frames like the rest of the engine.

Somewhat related discussion: #33579.

Was this page helpful?
0 / 5 - 0 ratings