Audio mit geringer Latenz

In diesem Artikel werden Änderungen der Audiolatenz in Windows 10 erläutert. Es behandelt API-Optionen für Anwendungsentwickler und Änderungen an Treibern, die vorgenommen werden können, um Audio mit niedriger Latenz zu unterstützen. Audiolatenz ist die Verzögerung zwischen dem Zeitpunkt, zu dem der Sound erstellt wird, und dem Zeitpunkt, zu dem er gehört wird. Eine geringe Audiolatenz ist für mehrere wichtige Szenarien wichtig, z. B.:

  • Pro-Audio
  • Musikschaffen
  • Kommunikation
  • Virtual Reality
  • Spiele

Dieses Dokument hat folgende Ziele:

  1. Beschreiben der Quellen der Audiolatenz in Windows
  2. Erläutern der Änderungen, die die Audiolatenz im Windows 10 Audiostapel reduzieren.
  3. Stellen Sie eine Referenz bereit, wie Anwendungsentwickler und Hardwarehersteller die vorteile der neuen Infrastruktur nutzen können, um Anwendungen und Treiber mit geringer Audiolatenz zu entwickeln.

In diesem Artikel wird Folgendes behandelt:

  1. Die neue AudioGraph-API für interaktive Szenarien und Medienerstellungsszenarien.
  2. Änderungen in WASAPI zur Unterstützung einer geringen Latenz.
  3. Verbesserungen in den Treiber-DDIs.

Begriff

Begriff BESCHREIBUNG
Renderlatenz Verzögerung zwischen dem Zeitpunkt, zu dem eine Anwendung einen Puffer von Audiodaten an die Render-APIs übermittelt, bis zu dem Zeitpunkt, zu dem er von den Lautsprechern gehört wird.
Erfassungslatenz Verzögerung zwischen dem Zeitpunkt, zu dem ein Sound vom Mikrofon erfasst wird, bis zu dem Zeitpunkt, zu dem er an die Aufnahme-APIs gesendet wird, die von der Anwendung verwendet werden.
Roundtriplatenz Verzögerung zwischen dem Zeitpunkt, zu dem ein Sound vom Mikrofon erfasst, von der Anwendung verarbeitet und von der Anwendung zum Rendern an die Lautsprecher übermittelt wird. Dies entspricht ungefähr Renderlatenz + Aufzeichnungslatenz.
Wartezeit bei Toucheingabe zu App Verzögerung zwischen der Zeit, zu der ein Benutzer auf den Bildschirm tippt, bis zu dem Zeitpunkt, zu dem das Signal an die Anwendung gesendet wird.
Touch-to-Sound-Latenz Verzögerung zwischen der Zeit, zu der ein Benutzer auf den Bildschirm tippt, wird das Ereignis an die Anwendung weitergeleitet, und ein Sound wird über die Lautsprecher gehört. Es entspricht Renderlatenz + Touch-to-App-Latenz.

Windows-Audiostapel

Das folgende Diagramm zeigt eine vereinfachte Version des Windows-Audiostapels.

Diagramm des Audiostapels mit niedriger Latenz mit Apps, Audio-Engine-Treibern und Hardware.

Hier ist eine Zusammenfassung der Wartezeiten im Renderpfad: Audioverarbeitungsobjekte

  1. Die Anwendung schreibt die Daten in einen Puffer.

  2. Die Audio-Engine liest die Daten aus dem Puffer und verarbeitet sie. Außerdem werden Audioeffekte in Form von Audioverarbeitungsobjekten (AUDIO Processing Objects, APOs) geladen. Weitere Informationen zu APOs finden Sie unter Windows-Audioverarbeitungsobjekte.

  3. Die Latenz der APOs variiert je nach Signalverarbeitung innerhalb der APOs.

  4. Vor Windows 10 lag die Latenz der Audio-Engine bei Anwendungen, die Gleitkommadaten verwenden, bei ~12 ms und bei Anwendungen, die ganzzahlige Daten verwenden, bei ca. 6 ms.

  5. In Windows 10 und später wurde die Latenz für alle Anwendungen auf 1,3 ms reduziert.

  6. Die Audio-Engine schreibt die verarbeiteten Daten in einen Puffer.

  7. Vor Windows 10 wurde der Puffer immer auf ~10 ms festgelegt.

  8. Ab Windows 10 wird die Puffergröße vom Audiotreiber definiert (weitere Details zum Puffer werden weiter unten in diesem Artikel beschrieben).

  9. Der Audiotreiber liest die Daten aus dem Puffer und schreibt sie in die Hardware.

  10. Die Hardware kann die Daten auch wieder in Form von mehr Audioeffekten verarbeiten.

  11. Der Benutzer hört Audio vom Lautsprecher.

Im Folgenden finden Sie eine Zusammenfassung der Latenz im Erfassungspfad:

  1. Die Audioaufzeichnung erfolgt über das Mikrofon.

  2. Die Hardware kann die Daten verarbeiten. Beispielsweise, um Audioeffekte hinzuzufügen.

  3. Der Treiber liest die Daten von der Hardware und schreibt die Daten in einen Puffer.

  4. Vor Windows 10 wurde dieser Puffer immer auf 10 ms festgelegt.

  5. Ab Windows 10 wird die Puffergröße vom Audiotreiber definiert (weitere Details unten).

  6. Die Audio-Engine liest die Daten aus dem Puffer und verarbeitet sie. Außerdem werden Audioeffekte in Form von Audioverarbeitungsobjekten (AUDIO Processing Objects, APOs) geladen.

  7. Die Latenz der APOs variiert je nach Signalverarbeitung innerhalb der APOs.

  8. Vor Windows 10 lag die Latenz der Audio-Engine bei Anwendungen, die Gleitkommadaten verwenden, bei ~6 ms und bei Anwendungen, die ganzzahlige Daten verwenden, ~0 ms.

  9. In Windows 10 und später wurde die Latenz für alle Anwendungen auf ~0 ms reduziert.

  10. Der Anwendung wird signalisiert, dass Daten zum Lesen verfügbar sind, sobald die Verarbeitung der Audio-Engine abgeschlossen ist. Der Audiostapel bietet auch die Option des exklusiven Modus. In diesem Fall umgehen die Daten die Audio-Engine und gelangen direkt von der Anwendung in den Puffer, aus dem der Treiber sie liest. Wenn eine Anwendung jedoch einen Endpunkt im exklusiven Modus öffnet, gibt es keine andere Anwendung, die diesen Endpunkt zum Rendern oder Aufzeichnen von Audio verwenden kann.

Eine weitere beliebte Alternative für Anwendungen, die eine geringe Latenz benötigen, ist die Verwendung des ASIO-Modells (AudioStream Input/Output), das den exklusiven Modus verwendet. Nachdem ein Benutzer einen ASIO-Treiber eines Drittanbieters installiert hat, können Anwendungen Daten direkt von der Anwendung an den ASIO-Treiber senden. Die Anwendung muss jedoch so geschrieben werden, dass sie direkt mit dem ASIO-Treiber kommuniziert.

Beide Alternativen (exklusiver Modus und ASIO) haben ihre eigenen Einschränkungen. Sie bieten eine geringe Latenz, haben aber ihre eigenen Einschränkungen (von denen einige oben beschrieben wurden). Daher wurde die Audio-Engine geändert, um die Latenz zu verringern und gleichzeitig die Flexibilität beizubehalten.

Verbesserungen des Audiostapels

Windows 10 und später wurden in drei Bereichen verbessert, um die Latenz zu verringern:

  1. Alle Anwendungen, die Audio verwenden, sehen eine Verringerung der Roundtriplatenz um 4,5 bis 16 ms (wie im abschnitt oben erläutert) ohne Codeänderungen oder Treiberupdates im Vergleich zu Windows 8.1.
    1. Anwendungen, die Gleitkommadaten verwenden, weisen eine um 16 ms geringere Latenz auf.
    2. Anwendungen, die ganzzahlige Daten verwenden, weisen eine um 4,5 ms geringere Latenz auf.
  2. Systeme mit aktualisierten Treibern bieten eine noch geringere Roundtriplatenz:
    1. Treiber können neue DDIs verwenden, um die unterstützten Größen des Puffers zu melden, der zum Übertragen von Daten zwischen Windows und der Hardware verwendet wird. Datenübertragungen müssen nicht immer 10-ms-Puffer verwenden, wie in früheren Windows-Versionen. Stattdessen kann der Treiber angeben, ob er kleine Puffer verwenden kann, z. B. 5 ms, 3 ms, 1 ms usw.
    2. Anwendungen, die eine geringe Latenz erfordern, können neue Audio-APIs (AudioGraph oder WASAPI) verwenden, um die vom Treiber unterstützten Puffergrößen abzufragen und die für die Datenübertragung zu/von der Hardware zu verwendende Auszuwählen.
  3. Wenn eine Anwendung Puffergrößen unter einem bestimmten Schwellenwert zum Rendern und Erfassen von Audio verwendet, wechselt Windows in einen speziellen Modus, in dem die Ressourcen so verwaltet werden, dass Störungen zwischen dem Audiostreaming und anderen Subsystemen vermieden werden. Dies reduziert die Unterbrechungen bei der Ausführung des Audiosubsystems und minimiert die Wahrscheinlichkeit von Audiostörungen. Wenn die Anwendung das Streaming beendet, kehrt Windows in den normalen Ausführungsmodus zurück. Das Audiosubsystem besteht aus den folgenden Ressourcen:
    1. Der Audio-Engine-Thread, der Audiodaten mit niedriger Latenz verarbeitet.
    2. Alle Threads und Interrupts, die vom Treiber registriert wurden (unter Verwendung der neuen DDIs, die im Abschnitt zur Treiberressourcenregistrierung beschrieben werden).
    3. Einige oder alle Audiothreads aus den Anwendungen, die kleine Puffer anfordern, und von allen Anwendungen, die dasselbe Audiogerätediagramm (z. B. denselben Signalverarbeitungsmodus) mit jeder Anwendung verwenden, die kleine Puffer angefordert hat:
  4. AudioGraph-Rückrufe im Streamingpfad.
  5. Wenn die Anwendung WASAPI verwendet, wurden nur die Arbeitselemente, die an die Echtzeitarbeitswarteschlangen-API oder MFCreateMFByteStreamOnStreamEx übermittelt und als "Audio" oder "ProAudio" gekennzeichnet wurden.

API-Verbesserungen

Die folgenden beiden Windows 10-APIs bieten Funktionen mit geringer Latenz:

So bestimmen Sie, welche der beiden APIs verwendet werden soll:

  • Bevorzugen Sie AudioGraph, wo immer möglich, für die Entwicklung neuer Anwendungen.
  • Verwenden Sie WASAPI nur, wenn:
    • Sie benötigen mehr Kontrolle als die von AudioGraph bereitgestellte.
    • Sie benötigen eine geringere Latenz als die von AudioGraph bereitgestellte.

Im Abschnitt "Messtools " in diesem Artikel werden spezifische Messungen eines Haswell-Systems mithilfe des HDAudio-Treibers für den Posteingang angezeigt.

In den folgenden Abschnitten werden die Funktionen mit geringer Latenz in jeder API erläutert. Wie im vorherigen Abschnitt erwähnt, muss das System aktualisierte Treiber haben, die kleine Puffergrößen unterstützen, damit das System die minimale Latenz erreichen kann.

AudioGraph

AudioGraph ist eine neue Universelle Windows-Plattform-API in Windows 10 und später, die darauf abzielt, interaktive Szenarien und Musikerstellungsszenarien mühelos zu realisieren. AudioGraph ist in mehreren Programmiersprachen (C++, C#, JavaScript) verfügbar und verfügt über ein einfaches und funktionsreiches Programmiermodell.

Um Szenarien mit geringer Latenz zu erreichen, stellt AudioGraph die Eigenschaft AudioGraphSettings::QuantumSizeSelectionMode bereit. Bei dieser Eigenschaft kann es sich um einen der in der folgenden Tabelle gezeigten Werte handeln:

Wert BESCHREIBUNG
SystemDefault Legt den Puffer auf die Standardpuffergröße (~10 ms) fest.
LowestLatency Legt den Puffer auf den Mindestwert fest, der vom Treiber unterstützt wird.
ClosestToDesired Legt fest, dass die Puffergröße entweder dem von der DesiredSamplesPerQuantum-Eigenschaft definierten Wert oder einem Wert entspricht, der genauso nahe an DesiredSamplesPerQuantum liegt, wie vom Treiber unterstützt wird.

Das AudioCreation-Beispiel zeigt, wie Sie AudioGraph für geringe Latenz verwenden. Der folgende Codeausschnitt zeigt, wie die minimale Puffergröße festgelegt wird:

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

Windows-Audiositzungs-API (WASAPI)

Ab Windows 10 wurde WASAPI auf Folgendes erweitert:

  • Zulassen, dass eine Anwendung den Bereich der Puffergrößen (d. h. Periodizitätswerte) ermitteln kann, die vom Audiotreiber eines bestimmten Audiogeräts unterstützt werden. Dadurch kann eine Anwendung beim Öffnen eines Datenstroms im freigegebenen Modus zwischen der Standardpuffergröße (10 ms) oder einem kleinen Puffer (weniger als 10 ms) wählen. Wenn eine Anwendung keine Puffergröße angibt, verwendet sie die Standardpuffergröße.
  • Ermöglichen Sie es einer Anwendung, das aktuelle Format und die Periodizität der Audio-Engine zu ermitteln. Dadurch können Anwendungen an den aktuellen Einstellungen der Audio-Engine andocken.
  • Zulassen, dass eine App angeben kann, dass sie in dem von ihr angegebenen Format rendern/erfassen möchte, ohne dass die Audio-Engine eine Neustempelung vornehmen muss.

Die oben genannten Features sind auf allen Windows-Geräten verfügbar. Bestimmte Geräte mit genügend Ressourcen und aktualisierten Treibern bieten jedoch eine bessere Benutzererfahrung als andere.

Die oben genannte Funktionalität wird von einer neuen Schnittstelle namens IAudioClient3 bereitgestellt, die von IAudioClient2 abgeleitet wird.

IAudioClient3 definiert die folgenden drei Methoden:

Methode BESCHREIBUNG
GetCurrentSharedModeEnginePeriod Gibt das aktuelle Format und die Periodizität der Audio-Engine zurück.
GetSharedModeEnginePeriod Gibt den Bereich der Periodizitäten zurück, die von der Engine für das angegebene Datenstromformat unterstützt werden.
InitializeSharedAudioStream Initialisiert einen freigegebenen Stream mit der angegebenen Periodizität.

Das WASAPIAudio-Beispiel zeigt, wie IAudioClient3 für geringe Latenz verwendet wird.

Der folgende Codeausschnitt zeigt, wie eine Musikerstellungs-App mit der niedrigsten Latenzeinstellung arbeiten kann, die vom System unterstützt wird.

// 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)) {
       ...
}

Darüber hinaus empfiehlt Microsoft für Anwendungen, die WASAPI verwenden, auch die Echtzeitarbeitswarteschlangen-API oder mfCreateMFByteStreamOnStreamOnStreamEx zu verwenden, um Arbeitselemente zu erstellen und diese als Audio oder Pro Audio zu markieren, anstelle ihrer eigenen Threads. Dadurch kann Windows sie so verwalten, dass Störungen von Nicht-Audiosubsystemen vermieden werden. Im Gegensatz dazu werden alle AudioGraph-Threads automatisch ordnungsgemäß von Windows verwaltet. Der folgende Codeausschnitt aus dem WASAPIAudio-Beispiel zeigt die Verwendung der MF-Arbeitswarteschlangen-APIs.

// 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 );

Alternativ zeigt der folgende Codeausschnitt die Verwendung der RT-Arbeitswarteschlangen-APIs.

#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;
}

Schließlich müssen Anwendungsentwickler, die WASAPI verwenden, ihre Datenströme basierend auf der Funktionalität jedes Streams mit der Audiokategorie und der Verwendung des Rohsignalverarbeitungsmodus markieren. Microsoft empfiehlt, dass alle Audiostreams nicht den Rohdatenverarbeitungsmodus für Signale verwenden, es sei denn, die Auswirkungen sind verstanden. Der Rohmodus umgeht die gesamte Signalverarbeitung, die vom OEM ausgewählt wurde, also:

  • Das Rendersignal für einen bestimmten Endpunkt kann suboptimal sein.
  • Das Aufnahmesignal kann in einem Format vorliegen, das die Anwendung nicht verstehen kann.
  • Die Latenz kann verbessert werden.

Treiberverbesserungen

Damit Audiotreiber niedrige Latenzen unterstützen, Windows 10 und höher die folgenden Features bereitstellen:

  1. [Obligatorisch] Deklarieren Sie die minimale Puffergröße, die in jedem Modus unterstützt wird.
  2. [Optional, aber empfohlen] Verbessern Sie die Koordination für den Datenfluss zwischen dem Treiber und Windows.
  3. [Optional, aber empfohlen] Registrieren Sie die Treiberressourcen (Interrupts, Threads), damit sie in Szenarien mit geringer Latenz durch Windows geschützt werden können. HDAudio Miniport-Funktionstreiber, die vom HDAudio-Bustreiber im Posteingang aufgezählt werden hdaudbus.sys müssen die HDAudio-Interrupts nicht registrieren, da dies bereits von hdaudbus.sys erfolgt. Wenn der Miniporttreiber jedoch eigene Threads erstellt, muss er diese registrieren.

In den folgenden drei Abschnitten werden die neuen Features ausführlicher erläutert.

Deklarieren der minimalen Puffergröße

Ein Treiber arbeitet unter verschiedenen Einschränkungen, wenn Audiodaten zwischen Windows, dem Treiber und der Hardware verschoben werden. Diese Einschränkungen können auf den physischen Hardwaretransport zurückzuführen sein, der Daten zwischen Arbeitsspeicher und Hardware verschiebt, oder auf die Signalverarbeitungsmodule innerhalb der Hardware oder des zugehörigen DSP.

Ab Windows 10 Version 1607 kann der Treiber seine Puffergrößenfunktionen mithilfe der geräteeigenschaft DEVPKEY_KsAudio_PacketSize_Constraints2 ausdrücken. Mit dieser Eigenschaft kann der Benutzer die absolute Mindestpuffergröße definieren, die vom Treiber unterstützt wird, und bestimmte Puffergrößeneinschränkungen für jeden Signalverarbeitungsmodus. Die modusspezifischen Einschränkungen müssen höher als die minimale Puffergröße des Treibers sein, andernfalls werden sie vom Audiostapel ignoriert.

Der folgende Codeausschnitt zeigt beispielsweise, wie ein Treiber deklarieren kann, dass die absolute unterstützte Puffergröße 2 ms beträgt, der Standardmodus jedoch 128 Frames unterstützt, was 3 ms entspricht, wenn wir von einer Abtastrate von 48 kHz ausgehen.

 
//
// 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
        },
    }
};

Ausführlichere Informationen zu diesen Strukturen finden Sie in den folgenden Artikeln:

Außerdem zeigt das sysvad-Beispiel , wie diese Eigenschaften verwendet werden, damit ein Treiber den Mindestpuffer für jeden Modus deklariert.

Verbessern der Koordination zwischen Treiber und Betriebssystem

Die in diesem Abschnitt beschriebenen DDIs ermöglichen dem Treiber Folgendes:

  • Geben Sie eindeutig an, welche Hälfte (Paket) des Puffers Windows zur Verfügung steht, anstatt das Betriebssystem basierend auf einer Codeclinkposition zu erraten. Dies hilft Windows bei der schnelleren Wiederherstellung nach Audiostörungen.
  • Optimieren oder vereinfachen Sie optional die Datenübertragungen in und aus dem WaveRT-Puffer. Der Nutzen hängt hier vom DMA-Engine-Design oder einem anderen Datenübertragungsmechanismus zwischen dem WaveRT-Puffer und (möglicherweise DSP)-Hardware ab.
  • "Burst" erfasste Daten schneller als in Echtzeit, wenn der Treiber intern erfasste Daten gesammelt hat. Dies ist in erster Linie für Sprachaktivierungsszenarien vorgesehen, kann aber auch während des normalen Streamings gelten.
  • Geben Sie Zeitstempelinformationen zur aktuellen Streamposition anstelle von Windows-Erraten an, sodass möglicherweise genaue Positionsinformationen möglich sind.

Dieser DDI ist nützlich, wenn ein DSP verwendet wird. Ein Standard-HD-Audiotreiber oder andere einfache kreisförmige DMA-Pufferdesigns können jedoch in diesen neuen DDIs, die hier aufgeführt sind, nicht viel nutzen.

Einige der Treiberroutinen geben Zeitstempel des Windows-Leistungsindikators zurück, die den Zeitpunkt widerspiegeln, zu dem Die Beispiele vom Gerät erfasst oder präsentiert werden.

Bei Geräten, die über komplexe DSP-Pipelines und Signalverarbeitung verfügen, kann die Berechnung eines genauen Zeitstempels eine Herausforderung darstellen und sollte mit Bedacht durchgeführt werden. Die Zeitstempel sollten nicht den Zeitpunkt widerspiegeln, zu dem Stichproben an oder von Windows auf den DSP übertragen wurden.

Um die Leistungsindikatorwerte zu berechnen, verwenden treiber und DSP möglicherweise einige der folgenden Methoden.

  • Verfolgen Sie im DSP Beispielzeitstempel mithilfe einer internen DSP-Wanduhr.
  • Berechnen Sie zwischen dem Treiber und DSP eine Korrelation zwischen dem Windows-Leistungsindikator und der DSP-Wanduhr. Verfahren hierfür können von einfachen (aber weniger präzisen) bis hin zu recht komplexen oder neuartigen (aber genaueren) Verfahren reichen.
  • Berücksichtigen Sie konstante Verzögerungen aufgrund von Signalverarbeitungsalgorithmen oder Pipeline- oder Hardwaretransporten, es sei denn, diese Verzögerungen werden anderweitig berücksichtigt.

Das sysvad-Beispiel zeigt die Verwendung der oben genannten DDIs.

Registrieren von Treiberressourcen

Um einen störungsfreien Betrieb sicherzustellen, müssen Audiotreiber ihre Streamingressourcen bei Portcls registrieren. Dadurch kann Windows Ressourcen verwalten, um Störungen zwischen Audiostreaming und anderen Subsystemen zu vermeiden.

Streamressourcen sind alle Ressourcen, die vom Audiotreiber zum Verarbeiten von Audiodatenströmen oder zum Sicherstellen des Audiodatenflusses verwendet werden. Es werden nur zwei Arten von Streamressourcen unterstützt: Interrupts und treibereigene Threads. Audiotreiber sollten eine Ressource nach dem Erstellen der Ressource registrieren und die Registrierung der Ressource aufheben, bevor sie gelöscht wurde.

Audiotreiber können Ressourcen zum Zeitpunkt der Initialisierung registrieren, wenn der Treiber geladen wird, oder zur Laufzeit, z. B. wenn ein E/A-Ressourcenausgleich erfolgt. Portcls verwendet einen globalen Zustand, um alle Audiostreamingressourcen nachzuverfolgen.

In einigen Anwendungsfällen, z. B. solchen, die eine sehr geringe Latenz erfordern, versucht Windows, die registrierten Ressourcen des Audiotreibers vor Störungen durch andere Betriebssystem-, Anwendungs- und Hardwareaktivitäten zu isolieren. Das Betriebssystem- und Audiosubsystem erledigen dies bei Bedarf, ohne mit dem Audiotreiber zu interagieren, mit Ausnahme der Registrierung der Ressourcen durch den Audiotreiber.

Diese Anforderung zum Registrieren von Streamressourcen impliziert, dass alle Treiber, die sich im Streamingpipelinepfad befinden, ihre Ressourcen direkt oder indirekt bei Portcls registrieren müssen. Der Audio-Miniporttreiber verfügt über folgende Optionen:

  • Der Audio-Miniporttreiber ist der untere Treiber seines Stapels (der die h/w direkt miteinander verknüpft). In diesem Fall kennt der Treiber seine Streamressourcen und kann sie bei Portcls registrieren.
  • Der Audio-Miniporttreiber streamt Audio mithilfe anderer Treiber (z. B. Audiobustreiber). Diese anderen Treiber verwenden auch Ressourcen, die bei Portcls registriert werden müssen. Diese Parallel-/Bustreiberstapel können eine öffentliche (oder private Schnittstelle, wenn ein einzelner Anbieter alle Treiber besitzt) verfügbar machen, die Audio-Miniporttreiber verwenden, um diese Informationen zu sammeln.
  • Der Audio-Miniporttreiber streamt Audio mithilfe anderer Treiber (z. B. hdaudbus). Diese anderen Treiber verwenden auch Ressourcen, die bei Portcls registriert werden müssen. Diese Parallel-/Bustreiber können eine Verbindung mit Portcls herstellen und ihre Ressourcen direkt registrieren. Die Audio-Miniporttreiber müssen Portcls mitteilen, dass sie von den Ressourcen dieser anderen Parallel/Bus-Geräte (PDOs) abhängig sind. Die HD-Audioinfrastruktur verwendet diese Option, d. h. der HD-Audiobustreiber verbindet sich mit Portcls und führt automatisch die folgenden Schritte aus:
    • registriert die Ressourcen des Busfahrers und
    • benachrichtigt Portcls, dass die Ressourcen der untergeordneten Elemente von den Ressourcen des übergeordneten Elements abhängen. In der HD-Audioarchitektur muss der Audio-Miniporttreiber lediglich seine eigenen treibereigenen Threadressourcen registrieren.

Hinweise:

  • HDAudio Miniport-Funktionstreiber, die vom HDAudio-Bustreiber im Posteingang aufgezählt werden hdaudbus.sys müssen die HDAudio-Interrupts nicht registrieren, da dies bereits von hdaudbus.sys erfolgt. Wenn der Miniporttreiber jedoch eigene Threads erstellt, muss er diese registrieren.
  • Treiber, die nur zum Registrieren von Streamingressourcen eine Verbindung mit Portcls herstellen, müssen ihre INFs aktualisieren, um wdmaudio.inf einzuschließen und portcls.sys (und abhängige Dateien) zu kopieren. Ein neuer INF-Kopierabschnitt ist in wdmaudio.inf definiert, um nur diese Dateien zu kopieren.
  • Audiotreiber, die nur in Windows 10 und höher ausgeführt werden, können eine feste Verknüpfung mit:
  • Audiotreiber, die auf einem betriebssystembasierten Betriebssystem ausgeführt werden müssen, können die folgende Schnittstelle verwenden (der Miniport kann QueryInterface für die IID_IPortClsStreamResourceManager-Schnittstelle aufrufen und deren Ressourcen nur registrieren, wenn PortCls die Schnittstelle unterstützt).
  • Diese DDIs verwenden diese Enumeration und Struktur:

Schließlich müssen Treiber, die PortCls zum alleinigen Zweck der Registrierung von Ressourcen verknüpfen, die folgenden beiden Zeilen im DDInstall-Abschnitt ihres inf hinzufügen. Audio-Miniporttreiber benötigen dies nicht, da sie bereits include/needs in wdmaudio.inf haben.

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

In den obigen Zeilen wird sichergestellt, dass PortCls und die zugehörigen abhängigen Dateien installiert sind.

Messwerkzeuge

Um die Roundtriplatenz zu messen, kann der Benutzer Tools verwenden, die Pulse über die Lautsprecher wiedergeben und über das Mikrofon erfassen. Sie messen die Verzögerung des folgenden Pfads:

  1. Die Anwendung ruft die Render-API (AudioGraph oder WASAPI) auf, um den Puls wiederzugeben.
  2. Die Audiowiedergabe erfolgt über die Lautsprecher
  3. Die Audioaufnahme erfolgt über das Mikrofon.
  4. Der Impuls wird von der Aufnahme-API (AudioGraph oder WASAPI) erkannt. Um die Roundtriplatenz für verschiedene Puffergrößen zu messen, müssen Benutzer einen Treiber installieren, der kleine Puffer unterstützt. Der HDAudio-Treiber für den Posteingang wurde aktualisiert, um Puffergrößen zwischen 128 Beispielen (2.66ms@48kHz) und 480 Beispielen (10ms@48kHz) zu unterstützen. Die folgenden Schritte zeigen, wie Sie den HDAudio-Treiber für den Posteingang installieren (der Teil aller Windows 10 und höherer SKUs ist):
  • Starten Sie den Geräte-Manager.
  • Doppelklicken Sie unter Audiovideo- und Gamecontroller auf das Gerät, das Ihren internen Lautsprechern entspricht.
  • Wechseln Sie im nächsten Fenster zur Registerkarte Treiber .
  • Wählen Sie Treiber aktualisieren –>Meinen Computer nach Treibersoftware durchsuchen aus>einer Liste der Gerätetreiber auf diesem Computer> auswählen – Wählen Sie High Definition Audio Device (High Definition Audio Device) aus, und wählen Sie Weiter aus.
  • Wenn ein Fenster mit dem Titel "Treiberwarnung aktualisieren" angezeigt wird, wählen Sie Ja aus.
  • Wählen Sie Schließen aus.
  • Wenn Sie aufgefordert werden, das System neu zu starten, wählen Sie Ja aus, um das System neu zu starten.
  • Nach dem Neustart verwendet das System den Microsoft HDAudio-Treiber für den Posteingang und nicht den Codectreiber des Drittanbieters. Denken Sie daran, welchen Treiber Sie zuvor verwendet haben, damit Sie auf diesen Treiber zurückgreifen können, wenn Sie die optimalen Einstellungen für Ihren Audiocodec verwenden möchten.

Graph, der die Unterschiede bei der Roundtriplatenz zwischen WASAPI und AudioGraph für verschiedene Puffergrößen veranschaulicht.

Die Unterschiede in der Latenz zwischen WASAPI und AudioGraph sind auf die folgenden Gründe zurückzuführen:

  • AudioGraph fügt einen Latenzpuffer auf der Aufzeichnungsseite hinzu, um Render- und Aufzeichnungsvorgänge zu synchronisieren, die nicht von WASAPI bereitgestellt werden. Diese Ergänzung vereinfacht den Code für Anwendungen, die mit AudioGraph geschrieben wurden.
  • Es gibt einen weiteren Latenzpuffer auf der Renderseite von AudioGraph, wenn das System puffert, die größer als 6 ms sind.
  • AudioGraph verfügt nicht über die Möglichkeit, Audioeffekte zu deaktivieren.

Beispiele

Häufig gestellte Fragen

Wäre es nicht besser, wenn alle Anwendungen die neuen APIs für geringe Latenz verwenden würden? Garantiert niedrige Latenz nicht immer eine bessere Benutzererfahrung?

Nicht unbedingt. Geringe Latenz hat ihre Nachteile:

  • Geringe Latenz bedeutet einen höheren Stromverbrauch. Wenn das System 10-ms-Puffer verwendet, bedeutet dies, dass die CPU alle 10 ms aktiviert wird, den Datenpuffer auffüllt und in den Standbymodus wechselt. Wenn das System jedoch 1-ms-Puffer verwendet, bedeutet dies, dass die CPU alle 1 ms aktiviert wird. Im zweiten Szenario bedeutet dies, dass die CPU häufiger aktiviert wird und der Stromverbrauch steigt. Dadurch wird die Akkulaufzeit verringert.
  • Die meisten Anwendungen basieren auf Audioeffekten, um die beste Benutzererfahrung zu bieten. Media Player möchten beispielsweise Audio mit hoher Genauigkeit bereitstellen. Kommunikationsanwendungen möchten Echo und Rauschen so gering wie möglich halten. Das Hinzufügen dieser Arten von Audioeffekten zu einem Stream erhöht die Latenz. Diese Anwendungen sind mehr an der Audioqualität als an der Audiolatenz interessiert.

Zusammenfassend lässt sich sagen, dass jeder Anwendungstyp unterschiedliche Anforderungen hinsichtlich der Audiolatenz hat. Wenn eine Anwendung keine geringe Latenz benötigt, sollte sie die neuen APIs nicht für geringe Latenz verwenden.

Werden alle Systeme, die auf Windows 10 und später aktualisiert werden, automatisch aktualisiert, um kleine Puffer zu unterstützen? Unterstützen alle Systeme die gleiche Mindestpuffergröße?

Nein, damit ein System kleine Puffer unterstützen kann, muss es über aktualisierte Treiber verfügen. Es liegt an den OEMs, zu entscheiden, welche Systeme aktualisiert werden, um kleine Puffer zu unterstützen. Außerdem unterstützen neuere Systeme eher kleinere Puffer als ältere Systeme. Die Latenz in neuen Systemen ist wahrscheinlich niedriger als bei älteren Systemen.

Wenn ein Treiber kleine Puffergrößen unterstützt, werden alle Anwendungen in Windows 10 und später automatisch kleine Puffer zum Rendern und Aufzeichnen von Audio verwenden?

Nein, standardmäßig verwenden alle Anwendungen in Windows 10 und höher 10 ms Puffer zum Rendern und Aufzeichnen von Audiodaten. Wenn eine Anwendung kleine Puffer verwenden muss, muss sie dazu die neuen AudioGraph-Einstellungen oder die WASAPI IAudioClient3-Schnittstelle verwenden. Wenn jedoch eine Anwendung die Verwendung kleiner Puffer anfordert, beginnt die Audio-Engine mit der Übertragung von Audiodaten mit dieser bestimmten Puffergröße. In diesem Fall werden alle Anwendungen, die denselben Endpunkt und Modus verwenden, automatisch auf diese kleine Puffergröße umgestellt. Wenn die Anwendung mit geringer Latenz beendet wird, wechselt die Audio-Engine erneut zu Puffern mit 10 ms.