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

Add support for variable output latency in WASAPI audio driver #38210

Merged
merged 1 commit into from
Aug 22, 2021

Conversation

benjarmstrong
Copy link
Contributor

@benjarmstrong benjarmstrong commented Apr 25, 2020

Fixed the audio 'output latency' project setting not appearing when using the WASAPI audio driver (see #30562). Added variable output latency support to the WASAPI audio driver for systems that support it.

Bugsquad edit: This closes #30562.

@benjarmstrong
Copy link
Contributor Author

I've also implemented this in my own 3.2 branch for a musc/rhythm based project I am working on that requires this fix. Let me know if you'd like me to create a pull request for that.

@akien-mga
Copy link
Member

Could you squash commits into one? See PR workflow.

@akien-mga akien-mga requested review from marcelofg55 and reduz April 28, 2020 09:37
@benjarmstrong
Copy link
Contributor Author

benjarmstrong commented Apr 28, 2020

@akien-mga

Could you squash commits into one? See PR workflow.

Apologies, will do and I'll review the PR workflow

@benjarmstrong benjarmstrong force-pushed the wasapi-audio-output-latency branch 3 times, most recently from dcc59a8 to 4d40ff3 Compare April 28, 2020 12:51
Calinou
Calinou previously requested changes Jun 5, 2020
drivers/wasapi/audio_driver_wasapi.cpp Outdated Show resolved Hide resolved
@benjarmstrong benjarmstrong force-pushed the wasapi-audio-output-latency branch 3 times, most recently from 0d7cb78 to 30acc29 Compare June 8, 2020 04:28
@benjarmstrong
Copy link
Contributor Author

Removed auto, added support for building via MinGW, fixed conflicts

@EIREXE
Copy link
Contributor

EIREXE commented Dec 18, 2020

GetBufferSize appears to return an incorrect value when using InitializeSharedAudioStream to set a buffer size different than default, this is probably due to window compatibility stuff?

I have a (I believe) fix here: EIRTeam@68e098a

@benjarmstrong
Copy link
Contributor Author

GetBufferSize appears to return an incorrect value when using InitializeSharedAudioStream to set a buffer size different than default, this is probably due to window compatibility stuff?

I have a (I believe) fix here: EIRTeam@68e098a

Thank you, I'll give it a look.

Which Windows version(s) did you test on and what compiler(s) did you use?

@benjarmstrong
Copy link
Contributor Author

@EIREXE

GetBufferSize appears to return an incorrect value when using InitializeSharedAudioStream to set a buffer size different than default, this is probably due to window compatibility stuff?

I can reproduce this, but only sometimes; IAudioClient3 behaves differently for different audio devices.

I have a (I believe) fix here: EIRTeam@68e098a

This works like a charm, thank you! I'll squash that into my PR.

P.S. Sorry about closing/reopening, accidentally hit 'close with comment'

@EIREXE
Copy link
Contributor

EIREXE commented Dec 19, 2020

@EIREXE

GetBufferSize appears to return an incorrect value when using InitializeSharedAudioStream to set a buffer size different than default, this is probably due to window compatibility stuff?

I can reproduce this, but only sometimes; IAudioClient3 behaves differently for different audio devices.

I have a (I believe) fix here: EIRTeam@68e098a

This works like a charm, thank you! I'll squash that into my PR.

P.S. Sorry about closing/reopening, accidentally hit 'close with comment'

You're welcome!

I've also done some testing on linux with pulse and it appears like the reported latency by get_output_latency is way too high (on the order of 50 ms) but it sure doesn't FEEL like it, I wonder what's going on.

@benjarmstrong benjarmstrong force-pushed the wasapi-audio-output-latency branch 2 times, most recently from 544af92 to 4013841 Compare December 21, 2020 00:58
@benjarmstrong
Copy link
Contributor Author

@EIREXE

I've also done some testing on linux with pulse and it appears like the reported latency by get_output_latency is way too high (on the order of 50 ms) but it sure doesn't FEEL like it, I wonder what's going on.

I think I may have found part your problem.

From this page:

If PA_STREAM_INTERPOLATE_TIMING is passed when connecting the stream, pa_stream_get_time() and pa_stream_get_latency() will try to interpolate the current playback time/latency by estimating the number of samples that have been played back by the hardware since the last regular timing update. It is espcially useful to combine this option with PA_STREAM_AUTO_TIMING_UPDATE, which will enable you to monitor the current playback time/latency very precisely and very frequently without requiring a network round trip every time.

PA_STREAM_INTERPOLATE_TIMING and PA_STREAM_AUTO_TIMING_UPDATE are passed in Godot's pulse driver so this applies. PA_STREAM_ADJUST_LATENCY is also passed allowing pulse to adjust the latency of the stream as it sees fit. This means means that pa_stream_get_latency may return different results each call, but in AudioDriverPulseAudio pa_stream_get_latency is called exactly once then that result is used for every subsequent AudioDriverPulseAudio ::get_latency() call (this appears to be for performance reasons).

After realising this could be an issue I modified AudioDriverPulseAudio::get_latency() to not reuse the return value of pa_stream_get_latency and call it every time. Then I ran a project with this script:

extends Node

var latencies = []
var min_latency = null
var max_latency = null

func _process(_delta):
	var latency = AudioServer.get_output_latency()
	if min_latency == null or latency < min_latency:
		min_latency = latency
	if max_latency == null or latency > max_latency:
		max_latency = latency

func _exit_tree():
	print("AudioServer.get_output_latency() ranged between ", min_latency, " and ", max_latency)

And I got the following output:

AudioServer.get_output_latency() ranged between 0.014455 and 0.02272

When this was run the engine audio output latency was set to 5ms (256 audio frames).

So I guess in your case it's plausible that AudioDriverPulseAudio::get_latency() returned the correct latency at the time it was called, but Pulse eventually reduced it which is why it doesn't feel as high as Godot claims it is.

DISCLAIMER: I skimmed the Pulse Audio documentation extremely briefly.

Apologies for getting off topic. I guess this should submitted as an issue if it hasn't been already?

@EIREXE
Copy link
Contributor

EIREXE commented Jan 2, 2021

I'm starting to believe my change isn't entirely a good idea, users have reported pops and desyncs, so maybe we should actually believe getbuffersize after all.

@EIREXE
Copy link
Contributor

EIREXE commented Jan 2, 2021

Reading the documentation:

https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nf-audioclient-iaudioclient-getbuffersize

This method retrieves the length of the endpoint buffer shared between the client application and the audio engine. The length is expressed as the number of audio frames the buffer can hold. The size in bytes of an audio frame is calculated as the number of channels in the stream multiplied by the sample size per channel. For example, the frame size is four bytes for a stereo (2-channel) stream with 16-bit samples.

so from what I understand getbuffersize is the size of the internal buffer that's between the application and WASPI, and wasapi might read this buffer with a periodicity that's higher than the actual buffer size
or something like this I have no idea about audio engineering stuff.

…sing the WASAPI audio driver. Added variable output latency support to the WASAPI audio driver for systems that support it.
@benjarmstrong benjarmstrong force-pushed the wasapi-audio-output-latency branch from ec334e2 to 880d470 Compare May 4, 2021 10:21
@benjarmstrong benjarmstrong requested a review from a team as a code owner May 4, 2021 10:21
@akien-mga akien-mga changed the title Fixed audio output latency not showing using WASAPI Add support for variable output latency in WASAPI audio driver May 4, 2021
@fire
Copy link
Member

fire commented Aug 22, 2021

I polled the audio channel on rocket chat.

This is a lot of Windows specific code and one person said they're not qualified to say if it's a good change or not because of that.

Is there a way to have an easier review?

@EIREXE
Copy link
Contributor

EIREXE commented Aug 22, 2021

I polled the audio channel on rocket chat.

This is a lot of Windows specific code and one person said they're not qualified to say if it's a good change or not because of that.

Is there a way to have an easier review?

I asked around for people who've written similar IAudioClient3 code for rhythm games (particularly project DIVA) and they say it appears to be spot on.
I also reviewed it when the PR came out and it looked correct comparing it to MS's documentation.
I've also been using it in production for a while and people report it's fine on w7 too (which is where the iaudioclient3 interface stops existing).

Copy link
Member

@fire fire left a comment

Choose a reason for hiding this comment

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

I asked around for people who've written similar IAudioClient3 code for rhythm games (particularly project DIVA) and they say it appears to be spot on.

I also reviewed it when the PR came out and it looked correct comparing it to MS's documentation.

I've also been using it in production for a while and people report it's fine on w7 too (which is where the iaudioclient3 interface stops existing).

Approved because of review by @EIREXE.

@fire fire dismissed Calinou’s stale review August 22, 2021 22:35

Calinou marked this conversation as resolved.

@fire fire merged commit 7560ba8 into godotengine:master Aug 22, 2021
@fire
Copy link
Member

fire commented Aug 22, 2021

Thanks.

@benjarmstrong
Copy link
Contributor Author

With this merged we should probably make an issue/PR for the docs that mention the limitations of the audio latency project setting. Depending on the operating system and hardware audio driver Godot may have to pick the nearest latency that the system allows. Examples:

  • Some drivers may have a limited range of buffer sizes they support, or a set of fixed values (e.g. my motherboard audio driver will allow buffer sizes of 128 to 4096 in increments of 32)
  • Some hardware audio drivers may only support one latency (e.g. the Vive pro audio driver on my Windows 10 machine only allows a buffer size of 441 samples)
  • Windows versions prior to 10 will only allow one latency.

Should I go ahead and do this?

@clayjohn
Copy link
Member

@benjarmstrong yes please, doc changes are usually included with PRs as appropriate. It would be great if you could make a docs PR as soon as convenient.

@benjarmstrong
Copy link
Contributor Author

Will do

@akien-mga
Copy link
Member

A backport for 3.x could also be considered if there's interest and it doesn't break compatibility.

@benjarmstrong
Copy link
Contributor Author

Oh absolutely, I've been using my own backport of this for ages now. I'll get on that

@EIREXE
Copy link
Contributor

EIREXE commented Sep 14, 2021

I have a backport to current-ish 3.x here: EIRTeam@7df5185

@akien-mga akien-mga added the cherrypick:3.x Considered for cherry-picking into a future 3.x release label Oct 4, 2021
@akien-mga
Copy link
Member

A PR would be welcome yes :) Be sure to include the fix from #53319 too.

@akien-mga
Copy link
Member

If a cherry-pick PR is made, #54880 should also be included to solve a regression.

@benjarmstrong
Copy link
Contributor Author

Yep I've just confirmed that this PR triggers #54879

#54880 should probably be cherry-picked either way since it fixes a nasty little trip wire where if certain AudioDriverWASAPI methods aren't called in exactly the right order the audio engine explodes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug cherrypick:3.x Considered for cherry-picking into a future 3.x release enhancement platform:windows topic:audio
Projects
None yet
Development

Successfully merging this pull request may close these issues.

No audio output latency option in project settings when using WASAPI
7 participants