Secondo Microsoft, a partire da Windows 10, le applicazioni che utilizzano WASAPI in modalità condivisa possono richiedere dimensioni del buffer inferiori a 10 ms (vedere https://msdn.microsoft.com/en-us/library/windows/hardware/mt298187%28v=vs.85%29.aspx).Come ottenere una latenza inferiore a 10 ms utilizzando la modalità condivisa WASAPI?
Secondo l'articolo, il raggiungimento di tali latenze richiede alcuni aggiornamenti del driver, cosa che ho fatto. Usando un rendering in modalità esclusiva e un flusso di acquisizione, ho misurato una latenza totale di andata e ritorno (utilizzando un cavo loopback hardware) di circa 13 ms. Questo mi suggerisce che almeno uno degli endpoint raggiunge con successo una latenza di < 10 ms. (Questa supposizione è corretta?)
L'articolo indica che le applicazioni possono utilizzare la nuova interfaccia IAudioClient3
per richiedere la dimensione minima del buffer supportata dal motore audio di Windows utilizzando IAudioClient3::GetSharedModeEnginePeriod()
. Tuttavia, questa funzione restituisce sempre 10 ms sul mio sistema e qualsiasi tentativo di inizializzare un flusso audio utilizzando IAudioClient::Initialize()
o IAudioClient3::InitializeSharedAudioStream()
con un periodo inferiore a 10 ms risulta sempre in AUDCLNT_E_INVALID_DEVICE_PERIOD
.
Per sicurezza, ho disabilitato anche l'elaborazione degli effetti nei driver audio. Cosa mi manca? È anche possibile ottenere una bassa latenza dalla modalità condivisa? Vedi sotto per alcuni esempi di codice.
#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;
}
Sicuramente questo è un problema di driver, mai un problema nell'audio. Ottengo gli stessi risultati di base, Cirrus Logic CS4208 versione driver 6.6001.3.24 e Windows 10.0.10586. Dovresti menzionare il tuo. –
Testato utilizzando l'audio HD integrato utilizzando "driver aggiornati" come indicato nell'articolo. Testerà nuovamente utilizzando un'altra interfaccia, inoltre non riesco a trovare dove trovare i driver aggiornati più (ma so per certo che erano driver specifici per Windows per l'audio a bassa latenza, quindi sarebbe strano se fosse davvero una cosa del guidatore). Lo esaminerò per vedere cosa è esattamente sul mio sistema. –
Capito. Ho usato i driver come spiegato qui: https://msdn.microsoft.com/en-us/library/windows/hardware/mt298187(v=vs.85).aspx#Measurement_Tools. Citazione: "Per misurare la latenza di roundtrip per diverse dimensioni del buffer, gli utenti devono installare un driver che supporti piccoli buffer. Il driver di posta in arrivo HDAudio è stato aggiornato per supportare le dimensioni del buffer tra 128 campioni (2,[email protected]) e 480 campioni (10ms @ 48kHz)". Proverò a usare un altro dispositivo stasera. –