-
Notifications
You must be signed in to change notification settings - Fork 323
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
DirectSound full duplex produces a single glitch shortly after stream starts #770
Comments
Actually, closer investigation of the recordings reveal that there is only a single glitch in each stream, happening exactly 20 ms after the start of the test signal. After that single glitch, a 10-second stream appears to be glitch-free: This likely explains why no-one is complaining about this - a single glitch at the beginning of the stream is arguably benign, although this is still a bug that should be fixed (if only so that we can get proper results from |
I was wondering if this could be related to the use of |
If I record the audio going through the loopback device using Audacity at the same time as the paloopback test is running, the glitch appears in the separately recorded audio. This indicates that the glitch is produced in the PortAudio → device (playback) direction. |
Hi Etienne, please update your issue report to specify Windows version and hardware setup. Can you consistently reproduce this on multiple systems with differing audio hardware? I have never observed such behavior myself, nor do I recall anyone report in over the long history of PortAudio (DirectSound was one of the first host API implementations). This makes me think that it is possibly a regression in recent Windows versions, or with specific drivers. |
I already did, see bottom of my first post.
I only tried Virtual Audio Cable so far. I guess I could try other VAC KS modes or some real hardware. The fact that both MME and DS half duplex are working fine would seem to point away from the hardware, though.
It's possible it was always there but that no-one noticed it - a glitch happening in the first 20 ms could be quite hard to notice in real usage, especially given that the first 20 ms of streamed audio would probably be silence in many scenarios. Also an half-duplex application would not trigger it. I only noticed this problem when I saw |
I'm not saying it's not a bug, just that from my point of view PortAudio is not the most likely culprit. Please at least test with real hardware. |
I'm still investigating but so far I am getting very suspicious there is a problem in the interaction between the DS host API code and the portaudio/src/hostapi/dsound/pa_win_ds.c Lines 2617 to 2626 in d07842c
The return value of The buffer processor business logic that is used here, specifically The problem is, I believe I have found a scenario in which that isn't the case. Here's how things actually unfold in reality, assuming
I believe this explains the symptoms. I believe the reason why this only happens at the end of the first user buffer likely has to do with the fact that this initial priming phase where Next step is to come up with a fix. For that I need to understand how that code was originally intended to work. This buffer adaptation stuff is proving to be quite headache-inducing! |
Currently the DirectSound host API uses the buffer processor in the "paUtilVariableHostBufferSizePartialUsageAllowed" buffer size mode. What's special about this mode is that it allows the buffer processor to leave input frames in the host buffer if there are not enough frames available to fire the stream callback. In practice the buffer processor will only do this in full duplex mode. However, just because we *can* leave these frames in the host buffer, doesn't mean we *should*. It is not clear to me why the DS code was set up to leave data in the host input buffer. The reason stated in a code comment, "because DS can split the host buffer when it wraps around" doesn't make sense to me - the buffer processor seems perfectly capable of handling buffer split and wraparound (through functions such as PaUtil_Set2ndInputFrameCount()) regardless of the buffer size mode. While it is not clear what could be the upside of leaving data in the host buffer, the downsides seem evident: the longer data stays in the input buffer, the more likely it is to get trampled on by the capture cursor (buffer overflow). Copying the data out of the host input buffer and into the buffer processor temp buffer at the earliest opportunity seems like a safer option with no downside. Another downside of this mode is that currently, the behavior with respect to the host *output* buffer seems... confused, with the buffer processor writing a number of frames to the output host buffer that is inconsistent with the number of "frames processed" it returns to the DS code, leading to PortAudio#770. Arguably we could try to fix the buffer processor, but why fix a mode whose reason to exist is unclear in the first place? This commit changes the mode to paUtilBoundedHostBufferSize. Not only does this fix PortAudio#770, it also has the nice side effect of having the buffer processor provision a temp buffer to match the host buffer size (if paFramesPerBufferUnspecified is used), avoiding unnecessary user buffer splitting. This change was tested using paloopback to test for glitches with various framesPerBuffer and suggested latency parameters, both in half duplex and full duplex mode. No regressions were observed, and paloopback results show that PortAudio#770 is fixed by this change. Fixes PortAudio#770
DirectSound was the only host API using this mode, and its usage was removed in the previous commit, making this mode dead code. As discussed in that commit the value proposition of this mode is unclear, and it does not behave correctly around stream start (priming) as shown in PortAudio#770.
Currently the DirectSound host API uses the buffer processor in the "paUtilVariableHostBufferSizePartialUsageAllowed" buffer size mode. What's special about this mode is that it allows the buffer processor to leave input frames in the host buffer if there are not enough frames available to fire the stream callback. In practice the buffer processor will only do this in full duplex mode. However, just because we *can* leave these frames in the host buffer, doesn't mean we *should*. It is not clear to me why the DS code was set up to leave data in the host input buffer. The reason stated in a code comment, "because DS can split the host buffer when it wraps around" doesn't make sense to me - the buffer processor seems perfectly capable of handling buffer split and wraparound (through functions such as PaUtil_Set2ndInputFrameCount()) regardless of the buffer size mode. While it is not clear what could be the upside of leaving data in the host buffer, the downsides seem evident: the longer data stays in the input buffer, the more likely it is to get trampled on by the capture cursor (buffer overflow). Copying the data out of the host input buffer and into the buffer processor temp buffer at the earliest opportunity seems like a safer option with no downside. Another downside of this mode is that currently, the behavior with respect to the host *output* buffer seems... confused, with the buffer processor writing a number of frames to the output host buffer that is inconsistent with the number of "frames processed" it returns to the DS code, leading to PortAudio#770. Arguably we could try to fix the buffer processor, but why fix a mode whose reason to exist is unclear in the first place? Another oddity of this mode is that it leads to the DS code locking the DS input and output buffers for read/write, but the buffer processor might then decide to do nothing with them. These DS Lock/Unlock calls are therefore wasted, reducing efficiency. This commit changes the mode to paUtilBoundedHostBufferSize. Not only does this fix PortAudio#770, it also has the nice side effect of having the buffer processor provision a temp buffer to match the host buffer size (if paFramesPerBufferUnspecified is used), avoiding unnecessary user buffer splitting. This change was tested using paloopback to test for glitches with various framesPerBuffer and suggested latency parameters, both in half duplex and full duplex mode. No regressions were observed, and paloopback results show that PortAudio#770 is fixed by this change. Fixes PortAudio#770
DirectSound was the only host API using this mode, and its usage was removed in the previous commit, making this mode dead code. As discussed in that commit the value proposition of this mode is unclear, and it does not behave correctly around stream start (priming) as shown in PortAudio#770.
Currently the DirectSound host API uses the buffer processor in the "paUtilVariableHostBufferSizePartialUsageAllowed" buffer size mode. What's special about this mode is that it allows the buffer processor to leave input frames in the host buffer if there are not enough frames available to fire the stream callback. In practice the buffer processor will only do this in full duplex mode. However, just because we *can* leave these frames in the host buffer, doesn't mean we *should*. It is not clear to me why the DS code was set up to leave data in the host input buffer. The reason stated in a code comment, "because DS can split the host buffer when it wraps around" doesn't make sense to me - the buffer processor seems perfectly capable of handling buffer split and wraparound (through functions such as PaUtil_Set2ndInputFrameCount()) regardless of the buffer size mode. While it is not clear what could be the upside of leaving data in the host buffer, the downsides seem evident: the longer data stays in the input buffer, the more likely it is to get trampled on by the capture cursor (buffer overflow). Copying the data out of the host input buffer and into the buffer processor temp buffer at the earliest opportunity seems like a safer option with no downside. Another downside of this mode is that currently, the behavior with respect to the host *output* buffer seems... confused, with the buffer processor writing a number of frames to the output host buffer that is inconsistent with the number of "frames processed" it returns to the DS code, leading to PortAudio#770. Arguably we could try to fix the buffer processor, but why fix a mode whose reason to exist is unclear in the first place? Another oddity of this mode is that it leads to the DS code locking the DS input and output buffers for read/write, but the buffer processor might then decide to do nothing with them. These DS Lock/Unlock calls are therefore wasted, reducing efficiency. This commit changes the mode to paUtilBoundedHostBufferSize. Not only does this fix PortAudio#770, it also has the nice side effect of having the buffer processor provision a temp buffer to match the host buffer size (if paFramesPerBufferUnspecified is used), avoiding unnecessary user buffer splitting. This change was tested using paloopback to test for glitches with various framesPerBuffer and suggested latency parameters, both in half duplex and full duplex mode. No regressions were observed, and paloopback results show that PortAudio#770 is fixed by this change. Fixes PortAudio#770
DirectSound was the only host API using this mode, and its usage was removed in the previous commit, making this mode dead code. As discussed in that commit the value proposition of this mode is unclear, and it does not behave correctly around stream start (priming) as shown in PortAudio#770.
The DirectSound Host API is glitchy when used in full duplex mode (i.e. when both input and output devices are used in a single PortAudio stream) when using reasonable parameters (default frames per buffer, default high suggested latency).
Half duplex is fine.
Example output from
paloopback -r48000 -s1024 --inputLatency 240 --outputLatency 240 -w
:A quick look at the recorded test signal confirms the issue:
MME passes the same test with flying colors.
The text was updated successfully, but these errors were encountered: