-
-
Notifications
You must be signed in to change notification settings - Fork 21.2k
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
Fixed audio output latency inaccuracies #38280
Fixed audio output latency inaccuracies #38280
Conversation
Could you squash commits into one? |
bb52fe2
to
c55e2a2
Compare
I'm not sure how to test this to solve the problem of "scheduled" audio play this was referenced in. |
Are you able to rebase on master? I can also rebase for you. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really like these changes overall. Just a bit of a note about whether the mix buffer needs to always be a power of two or not? I know that's convention. There's even a get_nearest_power_of_two
function (or something with a similar name) somewhere in the audio system to guarantee it.
@@ -83,6 +83,7 @@ class AudioDriver { | |||
|
|||
static const int DEFAULT_MIX_RATE = 44100; | |||
static const int DEFAULT_OUTPUT_LATENCY = 15; | |||
static constexpr int DEFAULT_MIX_BUFFER_SIZE = (DEFAULT_OUTPUT_LATENCY * DEFAULT_MIX_RATE) / 1000; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's normal for Godot's mix buffers to be powers of two from what I've seen. Perhaps someone else with more deep knowledge about the audio system can comment on why or whether that's truly necessary?
I would do it but I'm extremely busy with work at the moment - if you want to go ahead, I would certainly appreciate it.
Good catch, I didn't notice this. I can't personally find a hard requirement for this. I've been developing with Godot using this PR for about 15 months now on a Windows 10 system with a variety of audio drivers that give Godot different buffer lengths to use which are rarely a power of two. This development has mainly been using the Vive Pro and USB audio drivers, for which the drivers typically tell Godot to buffer 441 samples per callback and I have yet to experience any issues. I believe @EIREXE shipped Project Heartbeat with this patch backported to 3.x; maybe he has some insights into any issues this does/could cause.
One possible advantage of the buffer size being a power of two: the audio stream resampling is hard-coded to work in batches of 256 samples (IIRC), so using a power of two would reduce latency between audio streams that resample before handing it over to the audio mixer. If the resampling isn't performed in multiples of the buffer size or lower then this could introduce latency with a worst-case of adding 255 samples of delay. However:
I completely agree with you that someone with more experience on Godot's audio mixer should take a look. |
Interesting. I'm not sure what to make of that to be honest (edit: that sentence sounded kind of rude; just to clarify I'm referring to the fact that buffers of size 441 work correctly). Audio driver buffer sizes and audio server buffer sizes look like they were decoupled for a reason, but I can't quite piece together why. It's possible that it's related to drivers potentially having weird buffer sizes, I suppose. I'm supportive of the driver and the audio server having the same or very similar buffer sizes though. Currently a user can set latency to be a very low value in the driver but the audio server still mixes 1024 samples per batch so the latency doesn't actually go down below that. @reduz would be the only one I can think of who might know more about this. |
Currently it also increases the chances of audio artifacts due to buffer underruns. For example if the Godot audio driver is outputting at 256 samples per batch then every 4th batch will have the audio server mix 1024 frames, and the next 3 audio driver batches will use the remainder of the last audio server mix. This means that some audio driver callbacks take much longer than others, and if the audio driver callback doesn't finish before the audio needs to go to the speaker then we get audio artifacts. When the audio driver and audio server are perfectly in-sync then the CPU time spent in the audio server's mixing is relatively constant across each driver batch and these glitches are less likely to occur. This means that with this PR you can take the latency much lower than you could before with less risk of audio glitches. For me making the callbacks less glitchy under high CPU load was just as important as reducing the latency, as my game has a hefty audio synthesiser node that makes audio server mixes unusually expensive for a game.
I think this is it. Up until this PR Godot couldn't dynamically set the buffer size on Windows, and just had to roll with whatever the system audio driver gave it.
I'm guessing @reduz wrote most of Godot's audio code on account of all the music software he's authored, so he's probably the one to ask. |
I don't think I can offer much more insight, since I am not really an audio professional, I just know that after I shipped the change people said the game felt more responsive, which matched the feel I had when playing with it. |
@benjarmstrong If you're too busy to rebase this, would you mind if I took these changes and made a new PR to get them merged? I'll list you as a co-author on the commit if you'd like. I'm not super familiar with git workflows but I think that's the normal way to salvage old work. I might modify some logic a bit but I really like these changes overall and I want to make sure this gets merged in some form or another. |
If you could I would really appreciate it, I'm really snowed under at the moment. |
Salvaged in #52626. Thank you again Ben for putting this together! I'll try to backport it to 3.x too if it's accepted. |
Superseded by #52626. Thanks for the contribution! |
Fixed issue #38215
A couple of notes on the changes: