WASAPI ~2-3s delays on creating audio devices

Experiencing long delays when creating WASAPI audio devices. For example, when changing audio devices in DemoRunner.

Firstly, a few seconds are spent repeatedly trying formats where each time calling IAuidioClient::IsFormatSupported takes ~10ms:

In profiler:

After that a few hundreds of milliseconds are spent inside a call to IAuidioClient::Initialize:

In profiler:

Also, with defined JUCE_WASAPI_LOGGING=1 a lot of “WASAPI error: AUDCLNT_E_UNSUPPORTED_FORMAT” show up during creation of devices.

Could someone help with this issue or maybe point out in which direction to look?I


Are there some comments from JUCE? Is this a known issue? Will this be addressed? Is it just me? Thanks!

I haven’t taken a look at this but my first reaction is that I’m not entirely surprised. I don’t recall all the details but I’m aware of cases in which a WASAPI device may support hundreds of channels in which both JUCE and non JUCE code can take minutes (10+) to fully initialise a device causing the CPU to be running at max the whole time!. Talking to driver developers my understanding was that this was just the dance you have to go through for WASAPI. I of course could be wrong and if you have any suggestions for improvements we could potentially take a look.

Thanks @anthony-nicholls, in this case there is a maximum of 32 channels, and I don’t think WASAPI devices can support more channels than a PortClass audio device.
Couldn’t the format interrogation happen in parallel?

Maybe, I haven’t taken much of a look.

If memory serves me well (it rarely does :laughing:) I think there is some background service that gets started by Microsoft that ends up doing a lot of work and in my expereicne this is what I’ve seen takes up the majority of the time.

I worked for Focusrite and I’m sure we would occasionally see issues similar to this with some of our devices, but I also recall seeing it with things such as the Dante Virtual Soundcard, which claims to be 64 channels.

Are other applications managing to initialise the device in a much shorter time period? If not, are other JUCE based apps affected, such as Traction Waveform for example?

Looking at the link mentioned in the comments of the code you shared above there seems to be a lot in the remarks, I haven’t read it all yet but this section seems relevant.

Starting with Windows 7, Initialize can return AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED for a render or a capture device. This indicates that the buffer size, specified by the caller in the hnsBufferDuration parameter, is not aligned. This error code is returned only if the caller requested an exclusive-mode stream (AUDCLNT_SHAREMODE_EXCLUSIVE) and event-driven buffering (AUDCLNT_STREAMFLAGS_EVENTCALLBACK).

If Initialize returns AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED, the caller must call Initialize again and specify the aligned buffer size. Use the following steps:

  1. Call IAudioClient::GetBufferSize and receive the next-highest-aligned buffer size (in frames).
  2. Call IAudioClient::Release to release the audio client used in the previous call that returned AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED.
  3. Calculate the aligned buffer size in 100-nanosecond units (hns). The buffer size is (REFERENCE_TIME)((10000.0 * 1000 / WAVEFORMATEX.nSamplesPerSecond * nFrames) + 0.5). In this formula, nFrames is the buffer size retrieved by GetBufferSize.
  4. Call the IMMDevice::Activate method with parameter iid set to REFIID IID_IAudioClient to create a new audio client.
  5. Call Initialize again on the created audio client and specify the new buffer size and periodicity.

I’m probably missing something but I doubt we could run this in parallel as we just need to wait and find out the result of each call before we try anything else.

Thanks again!
Yes, it looks like the Initialize sequence can’t be de-sequentialized. But the repeated interrogative calls to IAuidioClient::IsFormatSupported() maybe could(?!) - which seems also to add up to the more time consuming requests.

Cross reference to Microsoft Q&A:

1 Like