Missing data when recording with Windows Audio

We are having a problem with silence and corrupted waveforms in data recorded with Windows Audio. If there is a way to fix this, please let me know. Or is this a bug?

[Reproduction method]

  • Use DemoRunner.exe of Juce to reproduce the problem.
  • Select “Windows Audio” for Audio Device Type.
  • Record a sine wave for about 10 minutes.

[Result]

  • The waveform is disordered, and short waveforms are missing (silent parts).
    (See attached capture image)

[Additional information]

  • The problem occurs even if I change the Audio buffer size.
  • The problem tends to occur with external devices such as USB.
  • The problem does not seem to depend on whether it is set as the system default device or not.
  • It is almost impossible to reproduce the problem in exclusive mode, but the frequency seems to increase in debug build.
  • The problem occurred even when the CPU load was low on a high-spec PC.
  • When I wrote out the read/write stream of the “reservoir” memory of the “WASAPIInputDevice” class to a file, I found that there was no missing data when writing, but the missing data occurred when reading.
    -We’re checking on Windows 10.

I would be grateful for any useful information.


waveform2

It looks like there are two different kinds of glitches.

  • One sort of glitch, where the waveform has a discontinuity, has uncertain origins. If I open two instances of the DemoRunner, set one to use DirectSound and the other to use Windows Audio, and then record a sinewave input with both simultaneously, these glitches line up exactly in the recorded files. This suggests that the glitches are happening in the OS or driver before they reach the JUCE apps (it would be very surprising for two different app instances to introduce identical glitches using different audio engines). As this glitch doesn’t seem to be under our control, I don’t think there’s much we can do about it.
  • The other kind of glitch manifests as a short period of silence in the recorded output, which seems to be due to the user’s audio callback being called before there is a full buffer of input available. In this case, any missing samples are replaced with zeros. At the moment I’m not sure of the best way to fix this - it’s possible to avoid the problem by delaying the audio callback until there is a full buffer of input available, but this has drawbackss. Waiting for new input may mean that we miss the deadline for sending the next output buffer, which will lead to glitches in the output instead (i.e. not the recorded output, but at the output of the audio device). I’m still mulling this over, but at the moment I can’t think of an approach which won’t cause breakage elsewhere.

For me, the second kind of glitch only seems to be present when my input and output devices are running at different sampling rates. It looks like in this case, the input and output devices are given slightly different block sizes, making it difficult/impossible to process both at an appropriate rate. The periods of silence seemed to disappear when I updated the device settings to force the system mic and speakers to use matching sample rates (Settings → Sound → Device Properties → Additional Device Properties → Advanced). If you haven’t checked this already, it might be worth seeing whether it reduces the amount of glitching for you.

Hopefully that helps. Let me know if you have any further questions and I’ll do my best to answer.

Thank you for your reply.

There was no missing data in the stream read from “IAudioCaptureClient”, so I think the problem is in the JUCE implementation.

To confirm this, I modified the source code to directly write the byte data read by “WASAPIInputDevice::handleDeviceBuffer()” to a file.

The source file is “/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp”.
The modified version is attached.

There is a comment called “DBGWRITER” in three places in the source." There are three comments in the source, one before the “reservoir” write, one after, and one after the read. You can choose which one you want to use by commenting it out.
When writing out, only the first channel is written out.

Try the source for this change and you will see where the problem is.

juce_win32_WASAPI.cpp.zip (15.1 KB)

I just tried running with your patch, writing the debug file from the pre-reservoir write position. It looks like there is an issue with the reservoir logic which introduces some unnecessary discontinuities. I’ve put together a fix for this issue, but this still doesn’t completely solve the problem:

The top waveform/spectrogram shows the file recorded by the recording demo. The bottom waveform/spectrogram shows the output of the pre-reservoir write (the offset is because this recording started before I was able to hit the “record” button in the demo).

This is consistent with my previous post - I think the (remaining) discontinuities are produced by the driver/OS before the input reaches the JUCE app, and the periods of silence are due to the way that WASAPI is wrapped in JUCE. I don’t think there’s a way we can avoid the occasional drop-outs completely, but running the input and output devices at the same samplerate makes these much less likely.

I’ll update this thread once the reservoir tweak is on develop, which I expect to happen early next week.

1 Like

Thanks for the detailed confirmation.
We wait for the fix.

We are not sure about the issue before “reservoir”.
But in order to do input/output with WASAPI, you need to wait for two events, one for reading and one for writing. Isn’t it possible that the timing of these events is causing problems because of the loss of time?

If the input and output blocks have different sizes, then they will need to be processed at different rates. At the moment, if there’s an input and an output device, the output event will be used to set the rate of the processing callback.

Presumably the input might end up dropping samples if it isn’t read quickly enough. I think I would expect the xruns count to be incremented in this case, but in testing the xruns count is generally lower than the number of discontinuities in the recorded output so I’m not sure that’s the cause of this issue.

I also tried setting a much larger input buffer when calling Initialize on the input device to see whether that would reduce the amount of glitching (I think a larger buffer should reduce the liklihood of the buffer filling up and dropping samples), but this didn’t seem to have any effect.

Thank you for your reply and explanation.

How did the fix work after that?

Thanks for your patience. We’ve now merged some improvements to the WASAPI compatibility layer:

This should go some way to resolving the glitches you were seeing. Please try it out and let us know if you encounter any new issues.

As I mentioned above, I’m not expecting this to completely eliminate glitches, but it should at least reduce their frequency.

Thank you. I’ll give it a try.