Według firmy Microsoft, począwszy od systemu Windows 10, aplikacje korzystające z trybu WASAPI z trybem współużytkowanym mogą żądać rozmiarów buforów mniejszych niż 10 ms (patrz https://msdn.microsoft.com/en-us/library/windows/hardware/mt298187%28v=vs.85%29.aspx).Jak uzyskać mniej niż 10ms opóźnienia przy użyciu wspólnego trybu WASAPI?
Zgodnie z artykułem, osiągnięcie tak niskich opóźnień wymaga pewnych aktualizacji sterowników, co zrobiłem. Korzystając z trybu renderowania i strumienia przechwytywania w trybie wyłączności, zmierzyłem całkowite opóźnienie w czasie podróży (przy użyciu kabla sprzężenia zwrotnego) około 13 ms. Sugeruje to mi, że co najmniej jeden z punktów końcowych pomyślnie osiąga opóźnienie wynoszące < 10 ms. (Czy to założenie jest poprawne?)
W artykule wspomniano, że aplikacje mogą korzystać z nowego interfejsu zapytań IAudioClient3
minimalny rozmiar bufora obsługiwanego przez silnik audio, Windows przy użyciu IAudioClient3::GetSharedModeEnginePeriod()
. Jednak ta funkcja zawsze zwraca 10ms w moim systemie, a każda próba zainicjowania strumienia audio przy użyciu albo IAudioClient::Initialize()
lub IAudioClient3::InitializeSharedAudioStream()
z okresem niższym niż 10 ms zawsze skutkuje AUDCLNT_E_INVALID_DEVICE_PERIOD
.
Dla pewności wyłączyłem także przetwarzanie efektów w sterownikach audio. Czego mi brakuje? Czy możliwe jest uzyskanie niskiego opóźnienia z trybu współdzielonego? Zobacz poniżej przykładowy kod.
#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, ¤t));
// 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, ¤t));
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;
}
Z pewnością jest to problem ze sterownikiem, nigdy nie stanowi problemu w dźwięku. Otrzymuję te same podstawowe wyniki, sterownik Cirrus Logic CS4208 w wersji 6.6001.3.24 i Windows 10.0.10586. Powinieneś wspomnieć o swojej. –
Testowano za pomocą wbudowanego dźwięku HD za pomocą "zaktualizowanych sterowników", jak wspomniano w artykule. Przetestuję ponownie używając innego interfejsu, także nie mogę się już zorientować, skąd pobrać te zaktualizowane sterowniki (ale wiem, że to były sterowniki dla Windows przeznaczone dla dźwięku o niskim opóźnieniu, więc byłoby dziwnie, gdyby tak naprawdę było rzecz kierowcy). Patrzę na niego, aby zobaczyć, co dokładnie jest w moim systemie. –
Mam to. Użyłem sterowników, jak wyjaśniono tutaj: https://msdn.microsoft.com/en-us/library/windows/hardware/mt298187(v=vs.85).aspx#Measurement_Tools. Cytat: "Aby zmierzyć opóźnienie w obie strony dla różnych rozmiarów buforów, użytkownicy muszą zainstalować sterownik, który obsługuje małe bufory. Sterownik HDAudio skrzynki odbiorczej został zaktualizowany w celu obsługi buforów o rozmiarach od 128 próbek ([email protected]) do 480 próbek (10 ms przy 48 kHz) ". Spróbuję dziś skorzystać z innego urządzenia. –