24

According to Microsoft, starting with Windows 10, applications using shared-mode WASAPI can request buffer sizes smaller than 10ms (see https://msdn.microsoft.com/en-us/library/windows/hardware/mt298187%28v=vs.85%29.aspx).

According to the article, achieving such low latencies requires some driver updates, which I did. Using an exclusive-mode render and capture stream, I measured a total round-trip latency (using a hardware loopback cable) of around 13ms. This suggests to me that at least one of the endpoints successfully achieves a latency of < 10ms. (Is this assumption correct?)

The article mentions that applications can use the new IAudioClient3 interface to query the minimum buffer size supported by the Windows audio engine using IAudioClient3::GetSharedModeEnginePeriod(). However, this function always returns 10ms on my system, and any attempt to initialize an audio stream using either IAudioClient::Initialize() or IAudioClient3::InitializeSharedAudioStream() with a period lower than 10ms always results in AUDCLNT_E_INVALID_DEVICE_PERIOD.

Just to be sure, I also disabled any effects processing in the audio drivers. What am I missing? Is it even possible to get low latency from shared mode? See below for some sample code.

#include <windows.h>
#include <atlbase.h>
#include <mmdeviceapi.h>
#include <audioclient.h>
#include <iostream>

#define VERIFY(hr) do {                                    \
  auto temp = (hr);                                        \
  if(FAILED(temp)) {                                       \
    std::cout << "Error: " << #hr << ": " << temp << "\n"; \
    goto error;                                            \
  }                                                        \
} while(0)


int main(int argc, char** argv) {

  HRESULT hr;
  CComPtr<IMMDevice> device;
  AudioClientProperties props;
  CComPtr<IAudioClient> client;
  CComPtr<IAudioClient2> client2;
  CComPtr<IAudioClient3> client3;
  CComHeapPtr<WAVEFORMATEX> format;
  CComPtr<IMMDeviceEnumerator> enumerator; 

  REFERENCE_TIME minTime, maxTime, engineTime;
  UINT32 min, max, fundamental, default_, current;

  VERIFY(CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED));
  VERIFY(enumerator.CoCreateInstance(__uuidof(MMDeviceEnumerator)));
  VERIFY(enumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, &device));
  VERIFY(device->Activate(__uuidof(IAudioClient), CLSCTX_ALL, nullptr, reinterpret_cast<void**>(&client)));
  VERIFY(client->QueryInterface(&client2));
  VERIFY(client->QueryInterface(&client3));

  VERIFY(client3->GetCurrentSharedModeEnginePeriod(&format, &current));

  // Always fails with AUDCLNT_E_OFFLOAD_MODE_ONLY.
  hr = client2->GetBufferSizeLimits(format, TRUE, &minTime, &maxTime);
  if(hr == AUDCLNT_E_OFFLOAD_MODE_ONLY)
    std::cout << "GetBufferSizeLimits returned AUDCLNT_E_OFFLOAD_MODE_ONLY.\n";
  else if(SUCCEEDED(hr))
    std::cout << "hw min = " << (minTime / 10000.0) << " hw max = " << (maxTime / 10000.0) << "\n";
  else
    VERIFY(hr);

  // Correctly? reports a minimum hardware period of 3ms and audio engine period of 10ms.
  VERIFY(client->GetDevicePeriod(&engineTime, &minTime));
  std::cout << "hw min = " << (minTime / 10000.0) << " engine = " << (engineTime / 10000.0) << "\n";

  // All values are set to a number of frames corresponding to 10ms.
  // This does not change if i change the device's sampling rate in the control panel.
  VERIFY(client3->GetSharedModeEnginePeriod(format, &default_, &fundamental, &min, &max));
  std::cout << "default = " << default_ 
            << " fundamental = " << fundamental 
            << " min = " << min 
            << " max = " << max 
            << " current = " << current << "\n";

  props.bIsOffload = FALSE;
  props.cbSize = sizeof(props);
  props.eCategory = AudioCategory_ForegroundOnlyMedia;
  props.Options = AUDCLNT_STREAMOPTIONS_RAW | AUDCLNT_STREAMOPTIONS_MATCH_FORMAT;

  // Doesn't seem to have any effect regardless of category/options values.
  VERIFY(client2->SetClientProperties(&props));

  format.Free();
  VERIFY(client3->GetCurrentSharedModeEnginePeriod(&format, &current));
  VERIFY(client3->GetSharedModeEnginePeriod(format, &default_, &fundamental, &min, &max));
  std::cout << "default = " << default_ 
            << " fundamental = " << fundamental 
            << " min = " << min 
            << " max = " << max 
            << " current = " << current << "\n";

error:
  CoUninitialize();
  return 0;
}
TriskalJM
  • 2,393
  • 1
  • 19
  • 20
Sjoerd van Kreel
  • 1,000
  • 6
  • 19
  • Surely this is a driver issue, never not a problem in audio. I'm getting the same basic results, Cirrus Logic CS4208 driver version 6.6001.3.24 and Windows 10.0.10586. You ought to mention yours. – Hans Passant Aug 19 '16 at 07:55
  • Tested using onboard HD audio using "updated drivers" as mentioned in the article. Will test again using another interface, also i cant seem to find where to get this updated drivers anymore (but i know for a fact that it was windows-supplied drivers specific for low latency audio, so it'd be weird if it really is a driver thing). Ill look into it to see what exactly is on my system. – Sjoerd van Kreel Aug 19 '16 at 10:19
  • Got it. I used the drivers as explained here: https://msdn.microsoft.com/en-us/library/windows/hardware/mt298187(v=vs.85).aspx#Measurement_Tools. Quote: "In order to measure the roundtrip latency for different buffer sizes, users need to install a driver that supports small buffers. The inbox HDAudio driver has been updated to support buffer sizes between 128 samples (2.66ms@48kHz) and 480 samples (10ms@48kHz)". Will try using another device tonight. – Sjoerd van Kreel Aug 19 '16 at 11:32
  • Turns out it was a driver issue after all. Just repeated the steps in the link above, now I get a minimum shared mode period of 128 samples (2.67 msec @ 48KHz) reported by GetSharedModeEnginePeriod. Windows 10 10586.545 using an onboard realtek hd audio device (cant find out exactly which version). The microsoft-supplied driver which is now working correctly is called high definition audio device, version 10.0.586.0. Honestly don't know how i missed this while testing last time =) @Hans passant care to make an answer out of your comment so i can award the bounty? – Sjoerd van Kreel Aug 21 '16 at 11:33
  • Why don't you use ASIO ? – Dinaiz Nov 26 '16 at 18:38
  • 1
    because he asked for shared access – user1050755 Apr 29 '17 at 22:13
  • 1
    More importantly, because most cards don't come with asio drivers. Wasapi is there out of the box. – Sjoerd van Kreel Apr 30 '17 at 07:29
  • I'll add the answer, if no one minds, just so this won't show up on the Unanswered list. What to do with the bounty is up to you, @SjoerdvanKreel. :) – TriskalJM Jun 16 '17 at 13:19

1 Answers1

0

Per Hans in the comment above, double-check that you've followed the instructions for Low Latency Audio here.

I'd reboot the machine just to be sure; Windows can be a bit finicky with that kind of thing.

TriskalJM
  • 2,393
  • 1
  • 19
  • 20