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

dsound: clamp input buffer size at 62.5 ms #782

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

dechamps
Copy link
Contributor

@dechamps dechamps commented Mar 6, 2023

This PR works around a DirectSound limitation where input host buffer sizes smaller than 31.25 ms are basically unworkable and make PortAudio hang. The workaround is to impose a minimal buffer size of 2*31.25 ms on input-only and full-duplex streams. This is enough for the read cursor to advance twice around the buffer, basically resulting in de facto double buffering.

This change was tested with paloopback under a wide variety of half/full-duplex, framesPerBuffer, and suggested latency parameters. (Note the testing was done on top of #772 as otherwise paloopback is not usable.)

In addition, this PR includes a small refactoring of the DirectSound buffer calculation code which was giving me headaches as I was working on the fix.

Fixes #775

src/hostapi/dsound/pa_win_ds.c Outdated Show resolved Hide resolved
src/hostapi/dsound/pa_win_ds.c Outdated Show resolved Hide resolved
@philburk
Copy link
Collaborator

philburk commented Mar 7, 2023

@dechamps - Thanks for doing this. Handling quirks in the underlying host APIs is an important feature for PortAudio.

@dechamps dechamps force-pushed the dsoundhangfix-pub branch from 1b1c30b to 0ed250c Compare March 7, 2023 17:14
@dechamps dechamps requested a review from philburk March 7, 2023 17:16
@RossBencina RossBencina added the src-dsound MS DirectSound Host API /src/hostapi/dsound label Mar 14, 2023
@RossBencina RossBencina self-requested a review March 21, 2023 00:31
@RossBencina
Copy link
Collaborator

This change was tested ...

@dechamps please could you update the PR description with information about which OS version(s) and audio devices were used for your testing. This gives us traceability if someone wants to compare your results later.

@RossBencina
Copy link
Collaborator

RossBencina commented Mar 21, 2023

Based on my testing reported at #775, I can get stable bidirectional audio with a 5.3ms input buffer size no problems with standard Realtek motherboard sound. I do get stalling with low output buffer sizes, so maybe there is a bug, but I don't think we have characterized it correctly yet. In any case with the available information, I don't think it's appropriate to put a blanket clamp to 31ms. I think we should continue the root-cause analysis at #775.

If it were the case that there are no systems where lower input buffer size works, or there was a way to detect that we were on such a system where the buffer limit applied, then the clamp would make total sense.

While I'm all for quirk workarounds, my take is that there will always be "low" latency/buffer settings which will produce unreliable output on some or all systems with some or all host APIs. It's up to the end user to tweak the buffering for best performance on their system. And that includes discovering the system limits. In other words, we should allow for parameter tuning beyond functioning limits. This PR prevents that.

If your goal is to provide a "foolproof" configuration that never fails due to quirks of some devices (by clamping even if it degrades latency on some system), then that logic should be put in the application code. (i.e. the application implements the clamp). PortAudio already provides some infrastructure to support this in PaDeviceInfo:

/** Default latency values for interactive performance. */
PaTime defaultLowInputLatency;
PaTime defaultLowOutputLatency;
/** Default latency values for robust non-interactive applications (eg. playing sound files). */
PaTime defaultHighInputLatency;
PaTime defaultHighOutputLatency;

It might make sense to update these default latency values to reflect the current findings especially for the non-interactive case.

@dechamps
Copy link
Contributor Author

Based on my testing reported at #775, I can get stable bidirectional audio with a 5.3ms input buffer size no problems with standard Realtek motherboard sound. I do get stalling with low output buffer sizes, so maybe there is a bug, but I don't think we have characterized it correctly yet. In any case with the available information, I don't think it's appropriate to put a blanket clamp to 31ms. I think we should continue the root-cause analysis at #775.

I just commented on #775 to show you that you did precisely reproduce the problem I characterized. Given the various indications that this is a widespread problem, and no evidence that there exists systems that aren't affected, I would say the blanket clamp is appropriate.

If it were the case that there are no systems where lower input buffer size works

It certainly looks like it. And you can add your own system to that list since you did reproduce it.

It's up to the end user to tweak the buffering for best performance on their system. And that includes discovering the system limits. In other words, we should allow for parameter tuning beyond functioning limits. This PR prevents that.

If your goal is to provide a "foolproof" configuration that never fails due to quirks of some devices (by clamping even if it degrades latency on some system), then that logic should be put in the application code.

If this failure mode merely resulted in glitchy audio, then I would agree (for example this is why I closed #788 instead of trying to implement a clamp in MME). However the failure mode we're dealing with here is much worse - it doesn't just cause glitchy audio, instead it results in a stall which is impossible to recover from and can easily compromise the stability of the application (deadlock), forcing the user to kill the app and restart it. I don't think this is acceptable behaviour from any library under any circumstances, regardless of parameters.

@mirh
Copy link

mirh commented Mar 22, 2023

Isn't there somebody you forgot to ask?
(cue XP, especially given that's presumably the major use case for dsound)

@dechamps
Copy link
Contributor Author

dechamps commented Mar 22, 2023

Isn't there somebody you forgot to ask?
(cue XP, especially given that's presumably the major use case for dsound)

Do you have evidence that #775 does not apply to XP? If so, I would be happy to add some kind of version check for that.

Also, to be clear: even if this new code runs on systems that are not subject to #775, that still doesn't mean these systems would break. The worst that can happen is that there might be some increase to the minimum achievable DS input latency, and even that is not certain (it depends on various parameters and on how DS relates input read cursor granularity to buffer size).

On the other hand, #775 does break modern systems in a pretty bad way.

(cue XP, especially given that's presumably the major use case for dsound)

My project does not even support XP, yet it uses dsound by default. This is because, when that decision was made, dsound looked like the most "modern" PortAudio Windows Host API that seemed reliable enough (WASAPI had lots of problems back then, and I suspect it still does, but I need to look into it).

I don't think it's fair to assume that users are using DS primarily to be compatible with XP. If a user cares deeply about compatibility with old platforms, then I would expect them to use MME which is the default and is more reliable and more widely supported than DS.

@mirh
Copy link

mirh commented Mar 22, 2023

Do you have evidence that #775 does not apply to XP?

Not really.
But it would still seem almost impossible tbf?
It's not just that some functions may have been rewritten (or abstracted to wasapi), in your usual XP machine a lot of of the processing was up to the audio driver itself.
So even going from a realtek to an nforce to a xfi could give you pretty different "low level" results.

I don't think it's fair to assume that users are using DS primarily to be compatible with XP.

My judgement was just theoretical, idk about history or implementation quirks.
Of course not all shortcomings may matter to certain use case, but it seems pretty obvious that it is a deprecated second-tier api.

@dechamps dechamps force-pushed the dsoundhangfix-pub branch 2 times, most recently from 4180493 to d0290d4 Compare March 22, 2023 23:21
@dechamps
Copy link
Contributor Author

Do you have evidence that #775 does not apply to XP?

Not really.
But it would still seem almost impossible tbf?

Okay, fair enough. Given that #775 affecting pre-Vista Windows seems unlikely, and given that the buffer size clamp could plausibly hurt low-latency DS users who are still on XP, I have updated this PR to do a version check so that it is a no-op on pre-Vista Windows.

I think all concerns with this PR have been addressed now?

@RossBencina
Copy link
Collaborator

My personal status: I want to do more local testing before I comment further.

@RossBencina RossBencina added this to the V19.8 milestone May 24, 2024
src/hostapi/dsound/pa_win_ds.c Outdated Show resolved Hide resolved
@philburk
Copy link
Collaborator

We are considering this PR for the next release, milestone 19.8.
We need to do a more through review and resume the conversation.

@RossBencina
Copy link
Collaborator

As I understand it, #772 is blocking this, and I will look into that one.

@dechamps
Copy link
Contributor Author

dechamps commented May 25, 2024

As I understand it, #772 is blocking this, and I will look into that one.

No, this PR does not depend on #772. #772 is only required if you want to test this PR using paloopback. This is because paloopback shows DirectSound as glitchy - regardless of this PR - due to #770, and #772 is the fix for that.

dechamps added 2 commits May 25, 2024 20:20
This commit rewrites CalculateBufferSettings() in an attempt to make it
a bit less headache-inducing. The basic idea is to reduce branching,
reduce code duplication, and share code paths between the various cases
(fixed/variable user buffer, half/full duplex) as much as possible.
Also, min()/max() are easier to read than if statements.

This is a pure refactoring - there should be no change in observable
behaviour. The math stays the same, it is merely reshuffled around in
place.
This works around a DirectSound limitation where input host buffer sizes
smaller than 31.25 ms are basically unworkable and make PortAudio hang.
The workaround is to impose a minimal buffer size of 2*31.25 ms on
input-only and full-duplex streams. This is enough for the read cursor
to advance twice around the buffer, basically resulting in de facto
double buffering.

This change was tested with `paloopback` under a wide variety of
half/full-duplex, framesPerBuffer, and suggested latency parameters.
(Note the testing was done on top of PortAudio#772 as otherwise paloopback is not
usable.)

Fixes PortAudio#775
@dechamps dechamps force-pushed the dsoundhangfix-pub branch from d0290d4 to 4a658fa Compare May 25, 2024 19:26
@dechamps dechamps requested a review from philburk May 25, 2024 19:35
@RossBencina RossBencina added the P1 Priority: Highest label Jul 5, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
P1 Priority: Highest src-dsound MS DirectSound Host API /src/hostapi/dsound
Projects
None yet
Development

Successfully merging this pull request may close these issues.

DirectSound hangs/glitches when using small input host buffers
4 participants