Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix audio stream generators getting freed accidentally #81508

Merged

Conversation

bluenote10
Copy link
Contributor

Closes #65155

This is an alternative to #73162 following @reduz suggestion there #73162 (comment)

To recap: In Godot 4.x audio stream generators have a fundamental bug that they get killed by the audio server in case of a buffer underrun. This leads to a very ugly race condition already when trying to set them up: If the very first "buffer fill" comes too late, the audio server will kill the playback generator even before it got its first chance to fill the buffer! Taking my minimal debug project as an example, I just had to launch this small app 53 times (!) to get one working instance of an audio stream generator. This means that in Godot 4.x, generators are almost completely unusable right now. (I have a music project that I'd love to migrate to Godot 4.x, but this bug is a complete showstopper.)

Possible minimal bugfixes: There are a few ways to fix this issue without the need for a big refactoring. #73162 had proposed to fix it on the level of the audio server, by changing the logic when to kill a playback to incorporate the .is_playing() information. This PR here basically solves it on the other end -- in AudioStreamGeneratorPlayback itself. In my opinion both fixes are a big improvement over the more or less completely broken state in 4.x / master. There are a few minor aspects why the approach taken in this PR may be preferable:

  1. It is a more local change, not having to touch audio server logic itself, thus affecting only the anyway broken AudioStreamGeneratorPlayback.

  2. The change makes the behavior consistent with the other playback's _mix_internal implementations (AudioStreamPlaybackMP3::_mix_internal, AudioStreamPlaybackOggVorbis::_mix_internal), which all have the semantics: Return 0 if inactive/stopped, otherwise return either the number of requested frames, or the number of available frames due to their finite nature. A generator conceptually falls into the infinite category though. Therefore, it should always simply return the number of requested frames. In particular, a buffer underrun should not become a signal to the audio server to kill the playback -- a buffer underrun is always an accident!

  3. There is currently an inconsistency between how mixed gets updated vs the return value:

    mixed += p_frames / generator->get_mix_rate();
    return read_amount < p_frames ? read_amount : p_frames;

    mixed already gets incremented by an amount corresponding to p_frames (the number of requested frames), but the return value can indicate something else. This PR would make the update to mixed and the return value consistent.


In the long term I fully agree with @reduz that it is probably best to partially revert #51296 and do things like ramp up/down rather on the playback/stream level, because they have better capabilities of doing lookaheads e.g. in case of file based playback. I've collected a few relevant parts of the discussion:

However, this would obviously require a bigger refactoring.

For the time being, I'd really appreciate if we could either merge (and release) the bugfix in #73162 or this PR here. Over the course over the last half year I've tried numerous hacks on user side trying to work around this bug, but I don't think there is anything that's really practical. Either of the two PRs would change the situation from "more or less unusable" to "basically fully working", so I hope we can go for a temporary fix now, and look into this bigger refactoring as a second step.

@bluenote10 bluenote10 requested a review from a team as a code owner September 10, 2023 07:32
@@ -181,7 +184,7 @@ void AudioStreamGeneratorPlayback::stop() {
}

bool AudioStreamGeneratorPlayback::is_playing() const {
return active; //always playing, can't be stopped
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment seemed to be outdated. Or, in fact slightly ironic, because in Godot 4.x the generator playbacks get stopped all the time 😉

@ellenhp
Copy link
Contributor

ellenhp commented Sep 10, 2023

In the long term I fully agree with @reduz that it is probably best to partially revert #51296 and do things like ramp up/down rather on the playback/stream level, because they have better capabilities of doing lookaheads e.g. in case of file based playback.

As long as we don't fully revert to mixing in the nodes, because that was an absolute nightmare and at some point i just stopped being interested in solving any audio bugs in 3.x because of it. Mixing in the playbacks doesn't make any sense to me either though because it closes the door to having one player send to multiple audio buses, and without that feature it's not possible to change the bus a player is on at runtime without a loud pop. Given that we point to the bus system whenever anyone wants to do something complex with audio in godot I think we should strive to support changing sends at runtime eventually. I know that reduz disagrees with me here so I'm going to get overruled but I think there's a very strong argument for keeping mixing in the audioserver.

@Calinou Calinou added bug topic:audio regression cherrypick:4.1 Considered for cherry-picking into a future 4.1.x release labels Sep 10, 2023
@Calinou Calinou added this to the 4.2 milestone Sep 10, 2023
@ellenhp
Copy link
Contributor

ellenhp commented Sep 15, 2023

@akien-mga I'm a bit out of the loop with regard to release timing because I haven't logged into rocketchat recently, but this would be good to merge when convenient.

@akien-mga
Copy link
Member

Thanks! I asked reduz to have a look, hopefully he should in coming days. If he can't, then I'm confident merging anyway with just your approval.

@akien-mga akien-mga merged commit cc7227c into godotengine:master Sep 20, 2023
16 checks passed
@akien-mga
Copy link
Member

Thanks!

@YuriSizov
Copy link
Contributor

Cherry-picked for 4.1.2.

@YuriSizov YuriSizov removed the cherrypick:4.1 Considered for cherry-picking into a future 4.1.x release label Sep 20, 2023
bend-n added a commit to bend-n/godot-builds that referenced this pull request Sep 21, 2023
@YuriSizov YuriSizov changed the title Alternative minimal fix for audio stream generators Fix audio stream generators getting freed accidentally Sep 21, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Audio Stream Playback Generator Broken
6 participants