Udostępnij za pośrednictwem


Dźwięk o małym opóźnieniu

W tym artykule omówiono zmiany opóźnienia dźwięku w systemie Windows 10. Obejmuje ona opcje interfejsu API dla deweloperów aplikacji i zmiany sterowników, które można wprowadzić w celu obsługi dźwięku o małych opóźnieniach. Opóźnienie dźwięku to opóźnienie między tym czasem utworzenia dźwięku a czasem jego usłyszenia. Małe opóźnienie dźwięku jest ważne w przypadku kilku kluczowych scenariuszy, takich jak:

  • Dźwięk w wersji Pro
  • Tworzenie muzyki
  • Komunikacja
  • Rzeczywistość wirtualna
  • Igrzyska

Cele tego dokumentu to:

  1. Opis źródeł opóźnienia dźwięku w systemie Windows.
  2. Wyjaśnij zmiany, które zmniejszają opóźnienie dźwięku w stosie audio systemu Windows 10.
  3. Podaj informacje na temat sposobu, w jaki deweloperzy aplikacji i producenci sprzętu mogą korzystać z nowej infrastruktury, aby tworzyć aplikacje i sterowniki z małym opóźnieniem audio.

W tym artykule opisano:

  1. API AudioGraph do scenariuszy interaktywnych i twórczych z zakresu multimediów.
  2. Zmiany w interfejsie WASAPI do obsługi małych opóźnień.
  3. Ulepszenia w DDI sterowników.

Terminologia

Termin Opis
Opóźnienie renderowania Opóźnienie między czasem przesyłania przez aplikację buforu danych dźwiękowych do interfejsów renderowania API, do momentu, gdy będzie on słyszany z głośników.
Opóźnienie przechwytywania Opóźnienie między momentem, w którym dźwięk jest przechwytywany z mikrofonu, a chwilą, gdy jest wysyłany do interfejsów API przechwytywania używanych przez aplikację.
Opóźnienie dwukierunkowe Opóźnienie między momentem, gdy dźwięk jest przechwycony z mikrofonu, przetworzony przez aplikację i przesłany przez aplikację do renderowania na głośniki. Jest ona w przybliżeniu równa opóźnieniu renderowania i opóźnieniu przechwytywania.
Opóźnienie funkcji Touch-to-app Opóźnienie między czasem naciśnięcia ekranu przez użytkownika do momentu wysłania sygnału do aplikacji.
Opóźnienie dotykowo-dźwiękowe Opóźnienie pomiędzy naciśnięciem ekranu przez użytkownika, przesłaniem zdarzenia do aplikacji, a odtwarzaniem dźwięku przez głośniki. Jest to równe opóźnieniu renderowania + opóźnieniu od dotyku do aplikacji.

Stos audio systemu Windows

Na poniższym diagramie przedstawiono uproszczoną wersję stosu audio systemu Windows.

Diagram przedstawiający stos audio o małym opóźnieniu z aplikacjami, sterownikiem aparatu audio i sprzętem.

Oto podsumowanie opóźnień w ścieżce renderowania: obiekty przetwarzania audio

  1. Aplikacja zapisuje dane w buforze

  2. Aparat audio odczytuje dane z buforu i przetwarza je. Ładuje również efekty dźwiękowe w postaci obiektów przetwarzania audio (APOs). Aby uzyskać więcej informacji na temat APOs, zapoznaj się z obiektami przetwarzania audio systemu Windows.

  3. Opóźnienie obiektów APO różni się w zależności od przetwarzania sygnału w ramach APO.

  4. Przed systemem Windows 10 opóźnienie aparatu audio było równe ok. 12 ms dla aplikacji korzystających z danych zmiennoprzecinkowych i ~6 ms dla aplikacji korzystających z danych całkowitych

  5. W systemie Windows 10 lub nowszym opóźnienie zostało zmniejszone do 1,3 ms dla wszystkich aplikacji

  6. Aparat audio zapisuje przetworzone dane w buforze.

  7. Przed systemem Windows 10 bufor był zawsze ustawiony na ~10 ms.

  8. Począwszy od systemu Windows 10, rozmiar buforu jest definiowany przez sterownik audio (więcej szczegółów na temat buforu opisano w dalszej części tego artykułu).

  9. Sterownik audio odczytuje dane z buforu i zapisuje je na sprzęcie.

  10. Sprzęt może również ponownie przetwarzać dane w postaci większej liczby efektów dźwiękowych.

  11. Użytkownik słyszy dźwięk od głośnika.

Oto podsumowanie opóźnienia w ścieżce przechwytywania:

  1. Dźwięk jest przechwytywany z mikrofonu.

  2. Sprzęt może przetwarzać dane. Aby na przykład dodać efekty dźwiękowe.

  3. Sterownik odczytuje dane ze sprzętu i zapisuje dane w buforze.

  4. Przed systemem Windows 10 ten bufor był zawsze ustawiony na 10 ms.

  5. Począwszy od systemu Windows 10, rozmiar buforu jest definiowany przez sterownik audio (więcej szczegółów poniżej).

  6. Aparat audio odczytuje dane z buforu i przetwarza je. Ładuje również efekty dźwiękowe w postaci obiektów przetwarzania audio (APOs).

  7. Opóźnienie APOs różni się w zależności od przetwarzania sygnału w APOs.

  8. Przed systemem Windows 10 opóźnienie aparatu audio było równe ok. 6 ms dla aplikacji korzystających z danych zmiennoprzecinkowych i ~0 ms dla aplikacji korzystających z danych całkowitych.

  9. W systemie Windows 10 lub nowszym opóźnienie zostało zmniejszone do ~0 ms dla wszystkich aplikacji.

  10. Gdy tylko silnik audio zakończy przetwarzanie, aplikacja zostaje powiadomiona, że dane są dostępne do odczytu. Stos audio zapewnia również opcję trybu wyłącznego. W takim przypadku dane pomijają aparat audio i przechodzą bezpośrednio z aplikacji do buforu, z którego sterownik go odczytuje. Jeśli jednak aplikacja otworzy punkt końcowy w trybie wyłącznym, nie ma inną aplikację, która może używać tego punktu końcowego do renderowania lub przechwytywania dźwięku.

Kolejną popularną alternatywą dla aplikacji wymagających małych opóźnień jest użycie modelu ASIO (wejście/wyjście strumienia audio), który korzysta z trybu wyłącznego. Po zainstalowaniu sterownika ASIO innej firmy aplikacje mogą wysyłać dane bezpośrednio z aplikacji do sterownika ASIO. Jednak aplikacja musi być napisana w taki sposób, aby rozmawiała bezpośrednio z kierowcą ASIO.

Obie alternatywy (tryb wyłączny i ASIO) mają własne ograniczenia. Zapewniają małe opóźnienia, ale mają własne ograniczenia (niektóre z nich zostały opisane powyżej). W związku z tym aparat audio został zmodyfikowany, aby zmniejszyć opóźnienie przy zachowaniu elastyczności.

Ulepszenia systemu audio

System Windows 10 i nowsze zostały ulepszone w trzech obszarach, aby zmniejszyć opóźnienie:

  1. Wszystkie aplikacje korzystające z dźwięku będą widzieć zmniejszenie opóźnienia 4,5–16 ms (jak wyjaśniono w powyższej sekcji) bez żadnych zmian kodu lub aktualizacji sterowników w porównaniu z systemem Windows 8.1.
    1. Aplikacje korzystające z danych zmiennoprzecinkowych będą miały mniejsze opóźnienie 16 ms.
    2. Aplikacje korzystające z danych całkowitych będą miały mniejsze opóźnienie 4,5 ms.
  2. Systemy ze zaktualizowanymi sterownikami zapewniają jeszcze mniejsze opóźnienie w obie strony:
    1. Sterowniki mogą używać interfejsów DDI o małych opóźnieniach do raportowania obsługiwanych rozmiarów buforu wykorzystywanego do transferu danych między systemem Windows a sprzętem. Transfery danych nie muszą zawsze używać buforów 10 ms, tak jak w poprzednich wersjach systemu Windows. Zamiast tego sterownik może określić, czy może używać małych buforów, na przykład 5 ms, 3 ms, 1 ms itp.
    2. Aplikacje, które wymagają niskiego opóźnienia, mogą używać interfejsów API audio o niskim opóźnieniu (AudioGraph lub WASAPI), aby sprawdzać rozmiary buforów obsługiwane przez sterownik i wybierać ten, który będzie używany do przesyłania danych do i ze sprzętu.
  3. Gdy aplikacja używa rozmiarów buforów poniżej określonego progu do renderowania i przechwytywania dźwięku, system Windows wchodzi w tryb specjalny, w którym zarządza zasobami, aby uniknąć zakłóceń między przesyłaniem strumieniowym audio a innymi podsystemami. Spowoduje to zmniejszenie przerw w wykonywaniu podsystemu audio i zminimalizowanie prawdopodobieństwa awarii dźwięku. Gdy aplikacja przestanie przesyłać strumieniowo, system Windows powraca do normalnego trybu wykonywania. Podsystem audio składa się z następujących zasobów:
    1. Wątek silnika audio, który przetwarza dźwięk o małych opóźnieniach.
    2. Wszystkie wątki i przerwania zarejestrowane przez sterownik (przy użyciu identyfikatorów DDI o małych opóźnieniach opisanych w sekcji dotyczącej rejestracji zasobów sterownika).
    3. Niektóre lub wszystkie wątki audio z aplikacji, które żądają małych buforów, oraz ze wszystkich aplikacji, które współużytkują ten sam graf urządzenia audio (na przykład ten sam tryb przetwarzania sygnału) z dowolną aplikacją, która żądała małych buforów.
  4. Wywołania zwrotne AudioGraph na ścieżce przesyłania strumieniowego.
  5. Jeśli aplikacja używa interfejsu WASAPI, tylko elementy robocze przesłane do interfejsu API kolejki pracy Real-Time work lub MFCreateMFByteStreamOnStreamEx i zostały oznaczone jako "Audio" lub "ProAudio".

Ulepszenia interfejsu API

Następujące dwa interfejsy API systemu Windows 10 zapewniają możliwości małych opóźnień:

Aby określić, które z dwóch interfejsów API mają być używane:

  • Preferuj aplikację AudioGraph, wszędzie tam, gdzie jest to możliwe na potrzeby tworzenia nowych aplikacji.
  • Użyj tylko interfejsu WASAPI, jeśli:
    • Potrzebna jest większa kontrola niż zapewniana przez audiograph.
    • Potrzebujesz mniejszego opóźnienia niż podane przez audiograph.

W sekcji narzędzi pomiarowych tego artykułu przedstawiono konkretne pomiary z systemu Haswell przy użyciu wbudowanego sterownika HDAudio.

W poniższych sekcjach opisano możliwości małych opóźnień w każdym interfejsie API. Jak wspomniano w poprzedniej sekcji, aby system osiągnął minimalne opóźnienie, musi mieć zaktualizowane sterowniki, które obsługują małe rozmiary buforów.

AudioGraph

AudioGraph to API uniwersalnej platformy Windows w systemie Windows 10 i nowszych, które ma na celu łatwe realizowanie scenariuszy interaktywnych i muzycznych. AudioGraph jest dostępny w kilku językach programowania (C++, C#, JavaScript) i ma prosty i bogaty w funkcje model programowania.

Aby sprostać scenariuszom o niskim opóźnieniu, AudioGraph udostępnia właściwość AudioGraphSettings::QuantumSizeSelectionMode. Ta właściwość może być dowolną wartością pokazaną w poniższej tabeli:

Wartość Opis
SystemDefault Ustawia bufor na domyślny rozmiar buforu (~10 ms)
Najniższe opóźnienie Ustawia bufor na minimalną wartość obsługiwaną przez sterownik
Najbliżej Pożądanego Ustawia rozmiar buforu na wartość równą wartości zdefiniowanej przez właściwość DesiredSamplesPerQuantum lub wartość, która jest tak samo zbliżona do DesiredSamplesPerQuantum, jak jest obsługiwana przez sterownik.

W przykładzie AudioCreation pokazano, jak używać usługi AudioGraph do małych opóźnień. Poniższy fragment kodu pokazuje, jak ustawić minimalny rozmiar buforu:

AudioGraphSettings settings = new AudioGraphSettings(AudioRenderCategory.Media);
settings.QuantumSizeSelectionMode = QuantumSizeSelectionMode.LowestLatency;
CreateAudioGraphResult result = await AudioGraph.CreateAsync(settings);

Interfejs API sesji audio systemu Windows (WASAPI)

Począwszy od systemu Windows 10, interfejs WASAPI został ulepszony do:

  • Zezwól aplikacji na odkrywanie zakresu rozmiarów bufora (czyli wartości cykliczności), które są obsługiwane przez sterownik audio konkretnego urządzenia audio. Dzięki temu aplikacja może wybrać domyślny rozmiar buforu (10 ms) lub mały bufor (mniej niż 10 ms) podczas otwierania strumienia w trybie udostępnionym. Jeśli aplikacja nie określi rozmiaru buforu, użyje domyślnego rozmiaru buforu.
  • Pozwól aplikacji odkrywać bieżący format i okresowość silnika audio. Umożliwia to aplikacjom dostosowanie się do bieżących ustawień silnika audio.
  • Pozwól aplikacji określić, że chce renderować lub przechwytywać w określonym formacie bez konieczności ponownego próbkowania przez silnik audio.

Powyższe funkcje będą dostępne na wszystkich urządzeniach z systemem Windows. Jednak niektóre urządzenia z wystarczającą ilością zasobów i zaktualizowanymi sterownikami zapewnią lepsze środowisko użytkownika niż inne.

Powyższe funkcje są udostępniane przez interfejs o nazwie IAudioClient3, który pochodzi z IAudioClient2.

IAudioClient3 definiuje następujące 3 metody:

Metoda Opis
GetCurrentSharedModeEnginePeriod Zwraca bieżący format i okresowość silnika audio
GetSharedModeEnginePeriod Zwraca zakres okresowości obsługiwanych przez silnik dla określonego formatu strumienia
InitializeSharedAudioStream Inicjuje udostępniony strumień z określoną okresowością

Przykład WASAPIAudio pokazuje, jak używać IAudioClient3 dla niskich opóźnień.

Poniższy fragment kodu pokazuje, jak aplikacja do tworzenia muzyki może działać w najniższym ustawieniu opóźnienia obsługiwanym przez system.

// 1. Activation

// Get a string representing the Default Audio (Render|Capture) Device
m_DeviceIdString = MediaDevice::GetDefaultAudio(Render|Capture)Id(
Windows::Media::Devices::AudioDeviceRole::Default );

// This call must be made on the main UI thread.  Async operation will call back to
// IActivateAudioInterfaceCompletionHandler::ActivateCompleted, which must be an agile // interface implementation
hr = ActivateAudioInterfaceAsync( m_DeviceIdString->Data(), __uuidof(IAudioClient3),
nullptr, this, &asyncOp );

// 2. Setting the audio client properties – note that low latency offload is not supported

AudioClientProperties audioProps = {0};
audioProps.cbSize = sizeof( AudioClientProperties );
audioProps.eCategory = AudioCategory_Media;

// if the device has System.Devices.AudioDevice.RawProcessingSupported set to true and you want to use raw mode
// audioProps.Options |= AUDCLNT_STREAMOPTIONS_RAW;
//
// if it is important to avoid resampling in the audio engine, set this flag
// audioProps.Options |= AUDCLNT_STREAMOPTIONS_MATCH_FORMAT;


hr = m_AudioClient->SetClientProperties( &audioProps ); if (FAILED(hr)) { ... }

// 3. Querying the legal periods

hr = m_AudioClient->GetMixFormat( &mixFormat ); if (FAILED(hr)) { ... }

hr = m_AudioClient->GetSharedModeEnginePeriod(wfx, &defaultPeriodInFrames, &fundamentalPeriodInFrames, &minPeriodInFrames, &maxPeriodInFrames); if (FAILED(hr)) { ... }

// legal periods are any multiple of fundamentalPeriodInFrames between
// minPeriodInFrames and maxPeriodInFrames, inclusive
// the Windows shared-mode engine uses defaultPeriodInFrames unless an audio client // has specifically requested otherwise

// 4. Initializing a low-latency client

hr = m_AudioClient->InitializeSharedAudioStream(
         AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
         desiredPeriodInFrames,
         mixFormat,
         nullptr); // audio session GUID
         if (AUDCLNT_E_ENGINE_PERIODICITY_LOCKED == hr) {
         /* engine is already running at a different period; call m_AudioClient->GetSharedModeEnginePeriod to see what it is */
         } else if (FAILED(hr)) {
             ...
         }

// 5. Initializing a client with a specific format (if the format needs to be different than the default format)

AudioClientProperties audioProps = {0};
audioProps.cbSize = sizeof( AudioClientProperties );
audioProps.eCategory = AudioCategory_Media;
audioProps.Options |= AUDCLNT_STREAMOPTIONS_MATCH_FORMAT;

hr = m_AudioClient->SetClientProperties( &audioProps );
if (FAILED(hr)) { ... }

hr = m_AudioClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, appFormat, &closest);
if (S_OK == hr) {
       /* device supports the app format */
} else if (S_FALSE == hr) {
       /* device DOES NOT support the app format; closest supported format is in the "closest" output variable */
} else {
       /* device DOES NOT support the app format, and Windows could not find a close supported format */
}

hr = m_AudioClient->InitializeSharedAudioStream(
       AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
       defaultPeriodInFrames,
       appFormat,
       nullptr); // audio session GUID
if (AUDCLNT_E_ENGINE_FORMAT_LOCKED == hr) {
       /* engine is already running at a different format */
} else if (FAILED(hr)) {
       ...
}

Ponadto firma Microsoft zaleca, aby aplikacje korzystające z interfejsu WASAPI używały również interfejsu API kolejki pracy Real-Time lub MFCreateMFByteStreamOnStreamEx do tworzenia elementów pracy i oznaczania ich jako dźwięk lub Pro Audio, zamiast używać własnych wątków. Pozwoli to systemowi Windows zarządzać nimi w taki sposób, aby uniknąć zakłóceń podsystemów innych niż audio. Z kolei wszystkie wątki AudioGraph są automatycznie zarządzane prawidłowo przez system Windows. Poniższy fragment kodu z przykładu WASAPIAudio pokazuje, jak używać API kolejki roboczej MF.

// Specify Source Reader Attributes
Attributes->SetUnknown( MF_SOURCE_READER_ASYNC_CALLBACK, static_cast<IMFSourceReaderCallback *>(this) );
    if (FAILED( hr ))
    {
        goto exit;
    }
    Attributes->SetString( MF_READWRITE_MMCSS_CLASS_AUDIO, L"Audio" );
    if (FAILED( hr ))
    {
        goto exit;
    }
    Attributes->SetUINT32( MF_READWRITE_MMCSS_PRIORITY_AUDIO, 0 );
    if (FAILED( hr ))
    {
        goto exit;
    }
    // Create a stream from IRandomAccessStream
    hr = MFCreateMFByteStreamOnStreamEx (reinterpret_cast<IUnknown*>(m_ContentStream), &ByteStream );
    if ( FAILED( hr ) )
    {
        goto exit;
    }
    // Create source reader
    hr = MFCreateSourceReaderFromByteStream( ByteStream, Attributes, &m_MFSourceReader );

Alternatywnie, oto fragment kodu, który pokazuje, jak korzystać z interfejsów API kolejki pracy RT.

#define INVALID_WORK_QUEUE_ID 0xffffffff
DWORD g_WorkQueueId = INVALID_WORK_QUEUE_ID;
//#define MMCSS_AUDIO_CLASS    L"Audio"
//#define MMCSS_PROAUDIO_CLASS L"ProAudio"

STDMETHODIMP TestClass::GetParameters(DWORD* pdwFlags, DWORD* pdwQueue)
{
       HRESULT hr = S_OK;
       *pdwFlags = 0;
       *pdwQueue = g_WorkQueueId;
       return hr;
}

//-------------------------------------------------------
STDMETHODIMP TestClass::Invoke(IRtwqAsyncResult* pAsyncResult)
{
       HRESULT hr = S_OK;
       IUnknown *pState = NULL;
       WCHAR className[20];
       DWORD  bufferLength = 20;
       DWORD taskID = 0;
       LONG priority = 0;

       printf("Callback is invoked pAsyncResult(0x%0x)  Current process id :0x%0x Current thread id :0x%0x\n", (INT64)pAsyncResult, GetCurrentProcessId(), GetCurrentThreadId());

       hr = RtwqGetWorkQueueMMCSSClass(g_WorkQueueId, className, &bufferLength);
       IF_FAIL_EXIT(hr, Exit);

       if (className[0])
       {
              hr = RtwqGetWorkQueueMMCSSTaskId(g_WorkQueueId, &taskID);
              IF_FAIL_EXIT(hr, Exit);

              hr = RtwqGetWorkQueueMMCSSPriority(g_WorkQueueId, &priority);
              IF_FAIL_EXIT(hr, Exit);
              printf("MMCSS: [%ws] taskID (%d) priority(%d)\n", className, taskID, priority);
       }
       else
       {
              printf("non-MMCSS\n");
       }
       hr = pAsyncResult->GetState(&pState);
       IF_FAIL_EXIT(hr, Exit);

Exit:
       return S_OK;
}
//-------------------------------------------------------

int _tmain(int argc, _TCHAR* argv[])
{
       HRESULT hr = S_OK;
       HANDLE signalEvent;
       LONG Priority = 1;
       IRtwqAsyncResult *pAsyncResult = NULL;
       RTWQWORKITEM_KEY workItemKey = NULL;;
       IRtwqAsyncCallback *callback = NULL;
       IUnknown *appObject = NULL;
       IUnknown *appState = NULL;
       DWORD taskId = 0;
       TestClass cbClass;
       NTSTATUS status;

       hr = RtwqStartup();
       IF_FAIL_EXIT(hr, Exit);

       signalEvent = CreateEvent(NULL, true, FALSE, NULL);
       IF_TRUE_ACTION_EXIT(signalEvent == NULL, hr = E_OUTOFMEMORY, Exit);

       g_WorkQueueId = RTWQ_MULTITHREADED_WORKQUEUE;

       hr = RtwqLockSharedWorkQueue(L"Audio", 0, &taskId, &g_WorkQueueId);
       IF_FAIL_EXIT(hr, Exit);

       hr = RtwqCreateAsyncResult(NULL, reinterpret_cast<IRtwqAsyncCallback*>(&cbClass), NULL, &pAsyncResult);
       IF_FAIL_EXIT(hr, Exit);

       hr = RtwqPutWaitingWorkItem(signalEvent, Priority, pAsyncResult, &workItemKey);
       IF_FAIL_EXIT(hr, Exit);

       for (int i = 0; i < 5; i++)
       {
              SetEvent(signalEvent);
              Sleep(30);
              hr = RtwqPutWaitingWorkItem(signalEvent, Priority, pAsyncResult, &workItemKey);
              IF_FAIL_EXIT(hr, Exit);
    }

Exit:
       if (pAsyncResult)
       {
              pAsyncResult->Release();
       }

      if (INVALID_WORK_QUEUE_ID != g_WorkQueueId)
      {
        hr = RtwqUnlockWorkQueue(g_WorkQueueId);
        if (FAILED(hr))
        {
            printf("Failed with RtwqUnlockWorkQueue 0x%x\n", hr);
        }

        hr = RtwqShutdown();
        if (FAILED(hr))
        {
            printf("Failed with RtwqShutdown 0x%x\n", hr);
        }
      }

       if (FAILED(hr))
       {
          printf("Failed with error code 0x%x\n", hr);
       }
       return 0;
}

Na koniec deweloperzy aplikacji korzystający z interfejsu WASAPI muszą oznaczyć strumienie kategorią audio i określić, czy używać nieprzetworzonego trybu przetwarzania sygnału na podstawie funkcjonalności każdego strumienia. Microsoft zaleca, aby wszystkie strumienie audio nie używały trybu przetwarzania surowego sygnału, chyba że zostaną zrozumiane jego konsekwencje. Tryb pierwotny pomija wszystkie operacje przetwarzania sygnału wybrane przez producenta OEM, więc:

  • Sygnał renderowania dla określonego punktu końcowego może być nieoptymalny.
  • Sygnał przechwytywania może mieć format, którego aplikacja nie może zrozumieć.
  • Opóźnienie może zostać zmniejszone.

Ulepszenia sterowników

Aby sterowniki audio obsługiwały małe opóźnienia, system Windows 10 i nowsze udostępnia następujące funkcje:

  1. [Obowiązkowe] Zadeklaruj minimalny rozmiar buforu obsługiwany w każdym trybie.
  2. [Opcjonalne, ale zalecane] Popraw koordynację przepływu danych między sterownikiem a systemem Windows.
  3. [Opcjonalne, ale zalecane] Zarejestruj zasoby sterowników (przerwania, wątki), aby mogły być chronione przez system Windows w scenariuszach o małych opóźnieniach. Sterowniki funkcji miniportu HDAudio, które są wyliczane przez wbudowany sterownik magistrali HDAudio hdaudbus.sys, nie muszą rejestrować przerwań HDAudio, ponieważ jest to już wykonywane przez hdaudbus.sys. Jeśli jednak sterownik miniportu tworzy własne wątki, musi je zarejestrować.

W poniższych trzech sekcjach opisano szczegółowo każdą funkcję.

Deklarowanie minimalnego rozmiaru buforu

Sterownik działa w różnych ograniczeniach podczas przenoszenia danych audio między systemem Windows, sterownikiem i sprzętem. Te ograniczenia mogą być spowodowane fizycznym transportem sprzętowym, który przenosi dane między pamięcią a sprzętem lub z powodu modułów przetwarzania sygnałów w ramach sprzętu lub skojarzonej dsP.

Począwszy od systemu Windows 10 w wersji 1607, sterownik może wyrazić możliwości rozmiaru buforu przy użyciu właściwości urządzenia DEVPKEY_KsAudio_PacketSize_Constraints2. Ta właściwość umożliwia użytkownikowi zdefiniowanie bezwzględnego minimalnego rozmiaru buforu obsługiwanego przez sterownik oraz określone ograniczenia rozmiaru buforu dla każdego trybu przetwarzania sygnału. Ograniczenia specyficzne dla danego trybu muszą przekraczać minimalny rozmiar bufora sterowników, w przeciwnym razie są ignorowane przez stos audio.

Na przykład poniższy fragment kodu pokazuje, jak sterownik może zadeklarować, że bezwzględny minimalny obsługiwany rozmiar buforu wynosi 2 ms, ale tryb domyślny obsługuje 128 ramek, co odpowiada 3 ms, jeśli zakładamy częstotliwość próbkowania 48 kHz.

 
//
// Describe buffer size constraints for WaveRT buffers
//
static struct
{
    KSAUDIO_PACKETSIZE_CONSTRAINTS2 TransportPacketConstraints;
    KSAUDIO_PACKETSIZE_PROCESSINGMODE_CONSTRAINT AdditionalProcessingConstraints[1];
} SysvadWaveRtPacketSizeConstraintsRender =
{
    {
        2 * HNSTIME_PER_MILLISECOND,                // 2 ms minimum processing interval
        FILE_BYTE_ALIGNMENT,                        // 1 byte packet size alignment
        0,                                          // no maximum packet size constraint
        2,                                          // 2 processing constraints follow
        {
            STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT,          // constraint for default processing mode
            128,                                                // 128 samples per processing frame
            0,                                                  // NA hns per processing frame
        },
    },
    {
        {
            STATIC_AUDIO_SIGNALPROCESSINGMODE_MOVIE,            // constraint for movie processing mode
            1024,                                               // 1024 samples per processing frame
            0,                                                  // NA hns per processing frame
        },
    }
};

Aby uzyskać bardziej szczegółowe informacje dotyczące tych struktur, zobacz następujące artykuły:

Ponadto przykład sysvad pokazuje, jak używać tych właściwości, aby sterownik zadeklarował minimalny bufor dla każdego trybu.

Ulepszanie koordynacji między sterownikiem a systemem operacyjnym

Identyfikatory DDI opisane w tej sekcji umożliwiają sterownikowi:

  • Wyraźnie wskaż, która połowa (pakiet) buforu jest dostępna dla systemu Windows, zamiast pozwalać systemowi operacyjnemu zgadywać na podstawie pozycji łącza kodeka. Ułatwia to systemowi Windows szybsze odzyskiwanie po błędach dźwiękowych.
  • Opcjonalnie zoptymalizuj lub uprość transfer danych w buforze WaveRT i poza nią. Ilość korzyści tutaj zależy od konstrukcji silnika DMA lub innego mechanizmu przesyłu danych między buforem WaveRT a sprzętem (być może DSP).
  • "Burst" przechwytywał dane szybciej niż w czasie rzeczywistym, jeśli sterownik wewnętrznie przechwycił dane. Jest to przeznaczone głównie dla scenariuszy aktywacji głosowej, ale może być również stosowane podczas normalnego przesyłania strumieniowego.
  • Zapewnij informacje o bieżącej pozycji strumienia zamiast polegać na domysłach systemu Windows, co może potencjalnie pozwolić na dokładniejsze informacje o pozycji.

Ta funkcja DDI jest przydatna w przypadku, gdy jest używana platforma DSP. Jednak standardowy sterownik audio HD lub inne proste cykliczne projekty buforów DMA mogą nie znaleźć wielu korzyści w interfejsach DDI wymienionych tutaj.

  • IMiniportWaveRTInputStream
  • IMiniportWaveRTOutputStream

Niektóre procedury sterowników zwracają znaczniki czasu liczników wydajności systemu Windows, odzwierciedlające moment, w którym próbki są przechwytywane lub prezentowane przez urządzenie.

W przypadku urządzeń, które mają złożone potoki DSP i przetwarzanie sygnałów, obliczanie dokładnego znacznika czasu może być trudne i powinno być przeprowadzone z rozwagą. Znaczniki czasu nie powinny odzwierciedlać czasu, w którym próbki zostały przeniesione do systemu Windows lub z systemu Windows do dsP.

Aby obliczyć wartości licznika wydajności, sterownik i DSP mogą stosować niektóre z następujących metod.

  • W ramach dsP śledź przykładowe znaczniki czasu przy użyciu wewnętrznego zegara dsP.
  • Między sterownikiem a DSP oblicz korelację między licznikiem wydajności systemu Windows a zegarem dsp. Procedury dla tego zakresu mogą wahać się od prostych (ale mniej precyzyjnych) do dość złożonych lub nowatorskich (ale bardziej precyzyjnych).
  • Uwzględnianie jakichkolwiek stałych opóźnień z powodu algorytmów przetwarzania sygnałów lub potoków lub transportu sprzętowego, chyba że te opóźnienia zostaną uwzględnione w inny sposób.

W przykładzie sysvad pokazano, jak używać powyższych DDI.

Rejestrowanie zasobów sterowników

Aby ułatwić zapewnienie bezawaryjnego działania, sterowniki audio muszą zarejestrować swoje zasoby przesyłu strumieniowego za pomocą Portcls. Dzięki temu system Windows może zarządzać zasobami, aby uniknąć interferencji między przesyłaniem strumieniowym audio a innymi podsystemami.

Zasoby strumienia to wszystkie zasoby używane przez sterownik audio do przetwarzania strumieni audio lub zapewnienia przepływu danych audio. Obsługiwane są tylko dwa typy zasobów strumienia: przerwania i wątki należące do sterownika. Sterowniki audio powinny zarejestrować zasób po utworzeniu zasobu i wyrejestrować zasób przed jego usunięciem.

Sterowniki audio mogą rejestrować zasoby w czasie inicjowania, gdy sterownik jest ładowany, lub podczas działania, na przykład podczas ponownego równoważenia zasobów wejścia/wyjścia. Portcls używa stanu globalnego do śledzenia wszystkich zasobów przesyłania strumieniowego audio.

W niektórych przypadkach użycia, takich jak te wymagające bardzo małych opóźnień audio, system Windows próbuje odizolować zarejestrowane zasoby sterownika audio od interferencji od innych systemów operacyjnych, aplikacji i działań sprzętowych. System operacyjny i podsystem audio wykonują to zgodnie z potrzebami bez interakcji ze sterownikiem audio, z wyjątkiem rejestracji zasobów przez sterownik audio.

Wymóg rejestracji zasobów strumieniowych oznacza, że wszystkie sterowniki znajdujące się na ścieżce przepływu strumieniowego muszą zarejestrować swoje zasoby bezpośrednio lub pośrednio w Portcls. Sterownik miniportu audio ma następujące opcje:

  • Sterownik miniportu audio jest dolnym sterownikiem stosu (bezpośrednio współpracującym z h/w), w tym przypadku sterownik wie, że swoje zasoby strumieniowe i że może je zarejestrować w Portcls.
  • Sterownik miniportu audio przesyła dźwięk strumieniowo z pomocą innych sterowników, takich jak sterowniki magistrali audio. Inne sterowniki również używają zasobów, które muszą być zarejestrowane w Portcls. Te stosy sterowników równoległych/magistrali mogą uwidocznić publiczny (lub prywatny interfejs, jeśli jeden dostawca jest właścicielem wszystkich sterowników), który sterowniki miniportów audio używają do zbierania tych informacji.
  • Sterownik miniportu audio strumieniuje audio przy pomocy innych sterowników, na przykład hdaudbus. Te inne sterowniki również używają zasobów, które muszą być zarejestrowane w Portcls. Te sterowniki równoległe/magistrali mogą łączyć się z Portcls i bezpośrednio rejestrować swoje zasoby. Sterowniki miniportów audio muszą poinformować Portcls, że są zależne od zasobów innych urządzeń równoległych/magistrali (PDO). Infrastruktura audio HD korzysta z tej opcji, to znaczy, że sterownik magistrali audio HD łączy się z Portcls i automatycznie wykonuje następujące kroki:
    • rejestruje zasoby kierowcy swojego autobusu i
    • Powiadamia Portcls, że zasoby podrzędne zależą od zasobów rodzica. W architekturze audio HD sterownik miniportu audio musi jedynie zarejestrować własne zasoby wątków sterowników.

Notatki:

  • Sterowniki miniportu HDAudio, które są enumerowane przez sterownik magistrali HDAudio hdaudbus.sys, nie muszą rejestrować przerwań HDAudio, ponieważ jest to już wykonywane przez hdaudbus.sys. Jeśli jednak sterownik miniportu tworzy własne wątki, musi je zarejestrować.
  • Sterowniki łączące się tylko z Portcls w celu rejestrowania zasobów streamingu muszą zaktualizować swoje pliki INF, aby uwzględnić wdmaudio.inf i skopiować portcls.sys (oraz pliki zależne). Nowa sekcja kopiowania INF jest zdefiniowana w pliku wdmaudio.inf, aby skopiować tylko te pliki.
  • Sterowniki audio, które działają tylko w systemie Windows 10 lub nowszych, mogą ustawiać trwałe odnośniki do:
  • Sterowniki audio, które muszą działać w systemie operacyjnym na poziomie dół, mogą używać następującego interfejsu (miniport może wywołać QueryInterface dla interfejsu IID_IPortClsStreamResourceManager i zarejestrować swoje zasoby tylko wtedy, gdy portCls obsługuje interfejs).
  • Te identyfikatory DDI używają tej enumeracji i struktury.

Na koniec sterowniki, które używają PortCls w celu wyłącznego rejestrowania zasobów, muszą dodać następujące dwa wiersze w sekcji DDInstall pliku INF. Sterowniki miniportu audio nie potrzebują tego, ponieważ mają już pliki include/needs w wdmaudio.inf.

[<install-section-name>]
Include=wdmaudio.inf
Needs=WDMPORTCLS.CopyFilesOnly

Powyższe wiersze zapewniają, że PortCls i jego pliki zależne są zainstalowane.

Narzędzia do pomiaru

Aby zmierzyć opóźnienie w obie strony, użytkownik może korzystać z narzędzi, które odtwarzają impulsy za pośrednictwem głośników i przechwytują je za pośrednictwem mikrofonu. Mierzy opóźnienie następującej ścieżki:

  1. Aplikacja wywołuje interfejs API renderowania (AudioGraph lub WASAPI), aby odtworzyć impuls
  2. Dźwięk jest odtwarzany za pośrednictwem głośników
  3. Dźwięk jest przechwytywany z mikrofonu
  4. Impuls jest wykrywany przez interfejs API przechwytywania (AudioGraph lub WASAPI). W celu zmierzenia opóźnienia okrężnego dla różnych rozmiarów bufora, użytkownicy muszą zainstalować sterownik obsługujący małe bufory. Sterownik wbudowany HDAudio został zaktualizowany, aby obsługiwać rozmiary bufora od 128 próbek (2.66ms@48kHz) do 480 próbek (10ms@48kHz). W poniższych krokach pokazano, jak zainstalować sterownik HDAudio skrzynki odbiorczej (który jest częścią wszystkich jednostek SKU systemu Windows 10 i nowszych):
  • Uruchom Menedżera urządzeń.
  • W sekcji Kontrolery dźwięku, wideo i gier, kliknij dwukrotnie urządzenie odpowiadające wbudowanym głośnikom.
  • W następnym oknie przejdź do karty sterownika.
  • Wybierz pozycję Aktualizuj sterownik —>Przeglądaj mój komputer w poszukiwaniu oprogramowania sterownika —>Pozwól mi wybrać z listy sterowników na tym komputerze —>Wybierz urządzenie audio o wysokiej rozdzielczości i wybierz pozycję Dalej.
  • Jeśli zostanie wyświetlone okno o nazwie "Ostrzeżenie dotyczące aktualizacji sterownika", wybierz pozycję Tak.
  • Wybierz , zamknij.
  • Jeśli zostanie wyświetlony monit o ponowne uruchomienie systemu, wybierz pozycję Tak do ponownego uruchomienia.
  • Po ponownym uruchomieniu system będzie używać wbudowanego sterownika Microsoft HDAudio, a nie sterownika kodera innej firmy. Pamiętaj, który sterownik był używany wcześniej, aby można było wrócić do tego sterownika, jeśli chcesz użyć optymalnych ustawień dla kodera audio.

Wykres ilustrujący różnice opóźnienia dwukierunkowego między interfejsem WASAPI oraz AudioGraph dla różnych wielkości buforów.

Różnice w opóźnieniu między WASAPI i AudioGraph są spowodowane następującymi przyczynami:

  • Funkcja AudioGraph dodaje jeden bufor opóźnienia po stronie przechwytywania, aby zsynchronizować renderowanie i przechwytywanie, które nie jest udostępniane przez interfejs WASAPI. Ten dodatek upraszcza kod dla aplikacji napisanych przy użyciu biblioteki AudioGraph.
  • Istnieje inny bufor opóźnienia po stronie renderowania w AudioGraph, gdy system używa buforów dłuższych niż 6 ms.
  • Aplikacja AudioGraph nie ma opcji wyłączenia efektów audio podczas przechwytywania.

Próbki

FAQ

Czy nie byłoby lepiej, gdyby wszystkie aplikacje używały nowych API dla niskiej latencji? Czy małe opóźnienie zawsze gwarantuje lepsze środowisko użytkownika?

Niekoniecznie. Niskie opóźnienia mają swoje konsekwencje.

  • Małe opóźnienie oznacza większe zużycie energii. Jeśli system używa 10-ms buforów, oznacza to, że procesor będzie budził się co 10 ms, wypełniać bufor danych i przechodzić do uśpienia. Jeśli jednak system używa buforów 1-ms, oznacza to, że procesor będzie się wybudzać co 1 ms. W drugim scenariuszu oznacza to, że procesor będzie wznawiać się częściej, a zużycie energii wzrośnie. Zmniejszy to żywotność baterii.
  • Większość aplikacji korzysta z efektów dźwiękowych, aby zapewnić najlepsze środowisko użytkownika. Na przykład odtwarzacze multimedialne chcą zapewnić dźwięk o wysokiej wierności. Aplikacje komunikacyjne chcą minimalnego echa i szumu. Dodanie tych typów efektów dźwiękowych do strumienia zwiększa opóźnienie. Te aplikacje są bardziej zainteresowane jakością dźwięku niż opóźnieniem dźwięku.

Podsumowując, każdy typ aplikacji ma różne potrzeby dotyczące opóźnienia dźwięku. Jeśli aplikacja nie wymaga małych opóźnień, nie powinna używać nowych interfejsów API do małych opóźnień.

Czy wszystkie systemy, które zostaną zaktualizowane do systemu Windows 10 i nowszych, będą obsługiwać małe bufory automatycznie? Czy wszystkie systemy będą obsługiwać ten sam minimalny rozmiar buforu?

Nie, aby system obsługiwał małe bufory, musi mieć zaktualizowane sterowniki. To OEM-ów należy zdecydowanie, które systemy zostaną zaktualizowane w celu obsługi małych buforów. Ponadto nowsze systemy prawdopodobnie lepiej obsługują mniejsze bufory niż starsze systemy. Opóźnienie w nowych systemach będzie najprawdopodobniej niższe niż starsze systemy.

Jeśli sterownik obsługuje małe rozmiary buforów, czy wszystkie aplikacje w systemie Windows 10 i nowszych wersjach automatycznie używają małych buforów do renderowania i przechwytywania dźwięku?

Nie, domyślnie wszystkie aplikacje w systemie Windows 10 i nowszych będą używać 10 ms do renderowania i przechwytywania dźwięku. Jeśli aplikacja musi używać małych buforów, musi skorzystać z nowych ustawień AudioGraph lub interfejsu WASAPI IAudioClient3, aby to zrobić. Jeśli jednak jedna aplikacja żąda użycia małych buforów, silnik audio zacznie przesyłać dźwięk za pomocą tego określonego rozmiaru buforu. W takim przypadku wszystkie aplikacje korzystające z tego samego punktu końcowego i trybu będą automatycznie przełączać się do tego małego rozmiaru buforu. Gdy aplikacja o małym opóźnieniu zakończy działanie, aparat audio przełączy się z powrotem na bufor o rozmiarze 10 ms.