低延迟音频

您所在的位置:网站首页 win10麦克风声音大小 低延迟音频

低延迟音频

2023-12-02 16:37| 来源: 网络整理| 查看: 265

低延迟音频 项目 06/15/2023

本文讨论Windows 10中的音频延迟更改。 它涵盖了面向应用程序开发人员的 API 选项以及驱动程序中的更改,这些更改可以支持低延迟音频。 音频延迟是创建声音的时间和听到声音的时间之间的延迟。 对于几个关键方案,低音频延迟非常重要,例如:

专业音频 音乐创作 通信 虚拟现实 游戏

本文档的目标是:

描述 Windows 中音频延迟的来源。 说明在Windows 10音频堆栈中减少音频延迟的更改。 提供有关应用程序开发人员和硬件制造商如何利用新基础结构以开发低音频延迟的应用程序和驱动程序的参考。

本文介绍:

用于交互式和媒体创建方案的新 AudioGraph API。 更改 WASAPI 以支持低延迟。 驱动程序 DDI 中的增强功能。 术语 术语 说明 呈现延迟 从应用程序将音频数据缓冲区提交到呈现 API 之间的延迟时间,直到从扬声器听到该缓冲区为止。 捕获延迟 从麦克风捕获声音之间的时间延迟,直到将声音发送到应用程序正在使用的捕获 API。 往返延迟 从麦克风捕获声音(由应用程序处理)和应用程序提交以呈现到扬声器之间的时间延迟。 这大致等于呈现延迟 + 捕获延迟。 触摸到应用的延迟 从用户点击屏幕到将信号发送到应用程序之间的延迟。 触摸到声音延迟 用户点击屏幕、事件转到应用程序以及通过扬声器听到声音之间的延迟。 这等于呈现延迟 + 触摸到应用延迟。 Windows 音频堆栈

下图显示了 Windows 音频堆栈的简化版本。

下面是呈现路径中延迟的摘要:音频处理对象

应用程序将数据写入缓冲区

音频引擎从缓冲区读取数据并对其进行处理。 它还以音频处理对象的形式加载音频效果, (ADO) 。 有关 ADO 的详细信息,请参阅 Windows 音频处理对象。

APOs 的延迟因 ADO 中的信号处理而异。

在Windows 10之前,对于使用浮点数据的应用程序,音频引擎的延迟等于约 12 毫秒,对于使用整数数据的应用程序,音频引擎的延迟等于约 6 毫秒

在 Windows 10 及更高版本中,所有应用程序的延迟已降低到 1.3 毫秒

音频引擎将处理的数据写入缓冲区。

在Windows 10之前,缓冲区始终设置为约 10 毫秒。

从Windows 10开始,缓冲区大小由音频驱动程序定义, (有关缓冲区的更多详细信息将在本文) 后面部分介绍。

音频驱动程序从缓冲区读取数据并将其写入硬件。

硬件还可以以更多音频效果的形式再次处理数据。

用户从扬声器听到音频。

下面是捕获路径中延迟的摘要:

音频是从麦克风捕获的。

硬件可以处理数据。 例如,添加音频效果。

驱动程序从硬件读取数据,并将数据写入缓冲区。

在Windows 10之前,此缓冲区始终设置为 10 毫秒。

从Windows 10开始,缓冲区大小由音频驱动程序定义, (下面) 的更多详细信息。

音频引擎从缓冲区读取数据并对其进行处理。 它还以音频处理对象的形式加载音频效果, (ADO) 。

APOs 的延迟因 ADO 中的信号处理而异。

在Windows 10之前,对于使用浮点数据的应用程序,音频引擎的延迟等于约 6 毫秒,对于使用整数数据的应用程序,则等于大约 0 毫秒。

在 Windows 10 及更高版本中,所有应用程序的延迟已降低到 ~0 毫秒。

一旦音频引擎完成数据处理,应用程序就会发出信号,表明数据可供读取。 音频堆栈还提供独占模式选项。 在这种情况下,数据会绕过音频引擎,直接从应用程序转到驱动程序从中读取数据的缓冲区。 但是,如果应用程序以独占模式打开终结点,则没有其他应用程序可以使用该终结点来呈现或捕获音频。

对于需要低延迟的应用程序,另一种常用的替代方法是使用 ASIO (音频流输入/输出) 模型,该模型使用独占模式。 用户安装第三方 ASIO 驱动程序后,应用程序可以直接将数据从应用程序发送到 ASIO 驱动程序。 但是,应用程序必须以与 ASIO 驱动程序直接通信的方式编写。

(独占模式和 ASIO) 的两种替代方法都有自己的限制。 它们提供较低的延迟,但它们在上述) (有其自身的限制。 因此,已修改音频引擎,以降低延迟,同时保持灵活性。

音频堆栈改进

Windows 10 及更高版本在三个方面进行了增强,以减少延迟:

与Windows 8.1相比,使用音频的所有应用程序都将看到往返延迟 (减少 4.5-16 毫秒,如上述) 部分所述。 使用浮点数据的应用程序的延迟将降低 16 毫秒。 使用整数数据的应用程序的延迟将降低 4.5 毫秒。 具有更新驱动程序的系统将提供更低的往返延迟: 驱动程序可以使用新的 DDI 来报告用于在 Windows 和硬件之间传输数据的受支持缓冲区大小。 数据传输不必像在以前的 Windows 版本中那样始终使用 10 毫秒缓冲区。 相反,驱动程序可以指定它是否可以使用较小的缓冲区,例如 5 ms、3 ms、1 ms 等。 需要低延迟的应用程序可以使用新的音频 API (AudioGraph 或 WASAPI) 来查询驱动程序支持的缓冲区大小,并选择用于向/从硬件进行数据传输的缓冲区大小。 当应用程序使用低于特定阈值的缓冲区大小来呈现和捕获音频时,Windows 将进入一种特殊模式,其中它以避免音频流式处理和其他子系统之间的干扰的方式管理其资源。 这将减少音频子系统的执行中断,并最大程度地降低音频故障的概率。 当应用程序停止流式处理时,Windows 将返回到其正常执行模式。 音频子系统包含以下资源: 正在处理低延迟音频的音频引擎线程。 驱动程序已注册的所有线程和中断 (使用有关驱动程序资源注册) 部分中介绍的新 DDI。 例如,请求小缓冲区的应用程序以及共享同一音频设备图的所有应用程序中的部分或全部音频线程 (,与请求小缓冲区的任何应用程序) 相同的信号处理模式: 流式处理路径上的 AudioGraph 回调。 如果应用程序使用 WASAPI,则仅提交到 实时工作队列 API 或 MFCreateMFByteStreamOnStreamEx 并标记为“Audio”或“ProAudio”的工作项。 API 改进

以下两个Windows 10 API 提供低延迟功能:

AudioGraph Windows 音频会话 API (WASAPI)

若要确定要使用这两个 API 中的哪一个::

尽可能使用 AudioGraph 进行新应用程序开发。 仅在以下的情况下使用 WASAPI: 你需要比 AudioGraph 提供的更多控制。 需要低于 AudioGraph 提供的延迟。

本文的 度量工具 部分显示使用内置 HDAudio 驱动程序从 Haswell 系统进行的特定度量。

以下部分将介绍每个 API 中的低延迟功能。 如上一部分所述,为了使系统实现最小延迟,它需要更新支持较小缓冲区大小的驱动程序。

AudioGraph

AudioGraph 是 Windows 10 及更高版本中的新通用 Windows 平台 API,旨在轻松实现交互式和音乐创作方案。 AudioGraph 提供多种编程语言 (C++、C#、JavaScript) ,并且具有简单且功能丰富的编程模型。

为了面向低延迟方案,AudioGraph 提供了 AudioGraphSettings::QuantumSizeSelectionMode 属性。 此属性可以是下表中显示的任何值:

值 说明 SystemDefault 将缓冲区设置为默认缓冲区大小 (~10 毫秒) LowestLatency 将缓冲区设置为驱动程序支持的最小值 ClosestToDesired 将缓冲区大小设置为等于 DesiredSamplesPerQuantum 属性定义的值或与驱动程序支持的 DesiredSamplesPerQuantum 一样接近的值。

AudioCreation 示例演示如何使用 AudioGraph 实现低延迟。 以下代码片段演示如何设置最小缓冲区大小:

AudioGraphSettings settings = new AudioGraphSettings(AudioRenderCategory.Media); settings.QuantumSizeSelectionMode = QuantumSizeSelectionMode.LowestLatency; CreateAudioGraphResult result = await AudioGraph.CreateAsync(settings); Windows 音频会话 API (WASAPI)

从Windows 10开始,WASAPI 已增强为:

允许应用程序发现缓冲区大小的范围 (即给定音频设备的音频驱动程序支持的周期值) 。 这样,应用程序可以在默认缓冲区大小 (10 毫秒) 或小型缓冲区 (小于 10 毫秒) 时选择在共享模式下打开流。 如果应用程序未指定缓冲区大小,它将使用默认缓冲区大小。 允许应用程序发现音频引擎的当前格式和周期。 这样,应用程序就可以捕捉到音频引擎的当前设置。 允许应用指定它希望以它指定的格式呈现/捕获,而无需音频引擎重新采样

上述功能将在所有 Windows 设备上可用。 但是,某些具有足够资源和更新驱动程序的设备将比其他设备提供更好的用户体验。

上述功能由一个名为 IAudioClient3 的新接口提供,该接口派生自 IAudioClient2。

IAudioClient3 定义以下 3 种方法:

方法 说明 GetCurrentSharedModeEnginePeriod 返回音频引擎的当前格式和周期 GetSharedModeEnginePeriod 返回引擎支持的指定流格式的周期范围 InitializeSharedAudioStream 使用指定的周期初始化共享流

WASAPIAudio 示例演示如何使用 IAudioClient3 实现低延迟。

以下代码片段演示了音乐创建应用如何在系统支持的最低延迟设置中运行。

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

此外,Microsoft 建议使用 WASAPI 的应用程序也使用 实时工作队列 API 或 MFCreateMFByteStreamOnStreamEx 来创建工作项并将其标记为音频或 Pro Audio,而不是他们自己的线程。 这将允许 Windows 以避免干扰非音频子系统的方式管理它们。 相比之下,Windows 会自动正确管理所有 AudioGraph 线程。 WASAPIAudio 示例中的以下代码片段演示如何使用 MF 工作队列 API。

// Specify Source Reader Attributes Attributes->SetUnknown( MF_SOURCE_READER_ASYNC_CALLBACK, static_cast(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(m_ContentStream), &ByteStream ); if ( FAILED( hr ) ) { goto exit; } // Create source reader hr = MFCreateSourceReaderFromByteStream( ByteStream, Attributes, &m_MFSourceReader );

或者,以下代码片段演示如何使用 RT 工作队列 API。

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

最后,使用 WASAPI 的应用程序开发人员需要根据每个流的功能,使用音频类别标记其流,以及是否使用原始信号处理模式。 Microsoft 建议所有音频流不要使用原始信号处理模式,除非了解其含义。 原始模式绕过 OEM 选择的所有信号处理,因此:

特定终结点的呈现信号可能不理想。 捕获信号可能采用应用程序无法理解的格式。 延迟可能会有所改善。 驱动程序改进

为了使音频驱动程序支持低延迟,Windows 10 及更高版本提供以下功能:

[必需]声明每个模式支持的最小缓冲区大小。 [可选,但建议]改进驱动程序与 Windows 之间数据流的协调。 [可选,但建议] (中断、线程) 注册驱动程序资源,以便在低延迟方案中由 Windows 保护它们。 由内置 HDAudio 总线驱动程序枚举的 HDAudio 微型端口函数驱动程序 hdaudbus.sys 不需要注册 HDAudio 中断,因为 hdaudbus.sys 已完成此操作。 但是,如果微型端口驱动程序创建自己的线程,则需要注册它们。

以下三个部分将更深入地介绍每个新功能。

声明最小缓冲区大小

在 Windows、驱动程序和硬件之间移动音频数据时,驱动程序在各种约束下运行。 这些限制可能是由于在内存和硬件之间移动数据的物理硬件传输,或者由于硬件或关联的DSP中的信号处理模块。

从 Windows 10 版本 1607 开始,驱动程序可以使用 DEVPKEY_KsAudio_PacketSize_Constraints2 设备属性来表达其缓冲区大小功能。 此属性允许用户定义驱动程序支持的绝对最小缓冲区大小,以及每个信号处理模式的特定缓冲区大小约束。 特定于模式的约束需要高于驱动程序的最小缓冲区大小,否则音频堆栈会忽略它们。

例如,以下代码片段演示了驱动程序如何声明支持的绝对最小缓冲区大小为 2 毫秒,但默认模式支持 128 帧,如果我们假定为 48 kHz 采样率,则这相当于 3 毫秒。

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

有关这些结构的更深入信息,请参阅以下文章:

KSAUDIO_PACKETSIZE_CONSTRAINTS 结构 KSAUDIO_PACKETSIZE_CONSTRAINTS2结构 KSAUDIO_PACKETSIZE_PROCESSINGMODE_CONSTRAINT 结构

此外, sysvad 示例 演示如何使用这些属性,以便驱动程序声明每个模式的最小缓冲区。

改进驱动程序与 OS 之间的协调

本部分中介绍的 DDI 允许驱动程序:

明确指示缓冲区的哪个半 (数据包) 可供 Windows 使用,而不是基于编解码器链接位置的 OS 猜测。 这有助于 Windows 更快地从音频故障中恢复。 (可选)优化或简化其传入和流出 WaveRT 缓冲区的数据传输。 此处的好处程度取决于 DMA 引擎设计或 WaveRT 缓冲区与 (可能为 DSP) 硬件之间的其他数据传输机制。 如果驱动程序具有内部累积的捕获数据,则“突发”捕获数据的速度比实时快。 这主要用于语音激活方案,但在正常流式处理期间也可以应用。 提供有关其当前流位置的时间戳信息,而不是 Windows 猜测,从而可能提供准确的位置信息。

在使用 DSP 的情况下,此 DDI 非常有用。 但是,标准 HD 音频驱动程序或其他简单的圆形 DMA 缓冲区设计在此处列出的这些新 DDI 中可能没有太大优势。

IMiniportWaveRTInputStream IMiniportWaveRTOutputStream

一些驱动程序例程返回 Windows 性能计数器时间戳,反映设备捕获或显示示例的时间。

在具有复杂 DSP 管道和信号处理的设备中,计算准确的时间戳可能具有挑战性,应深思熟虑地完成。 时间戳不应反映将样本传输到 Windows 或从 Windows 传输到 DSP 的时间。

若要计算性能计数器值,驱动程序和 DSP 可能使用以下一些方法。

在 DSP 中,使用一些内部的 DSP 时钟跟踪示例时间戳。 在驱动程序和 DSP 之间,计算 Windows 性能计数器与 DSP 挂钟之间的关联。 此过程的范围从简单 (但不太精确的) 到相当复杂或新颖 (但更精确的) 。 考虑信号处理算法或管道或硬件传输导致的任何持续延迟,除非另行考虑这些延迟。

sysvad 示例演示如何使用上述 DDI。

注册驱动程序资源

为了帮助确保无故障操作,音频驱动程序必须向 Portcls 注册其流式处理资源。 这允许 Windows 管理资源,以避免音频流式处理和其他子系统之间的干扰。

流资源是音频驱动程序用于处理音频流或确保音频数据流的任何资源。 仅支持两种类型的流资源:中断和驱动程序拥有的线程。 音频驱动程序应在创建资源后注册资源,并在删除资源之前注销该资源。

音频驱动程序可以在加载驱动程序时初始化时注册资源,或者在运行时注册资源,例如,在进行 I/O 资源重新均衡时。 Portcls 使用全局状态来跟踪所有音频流式处理资源。

在某些用例(例如需要非常低延迟音频的用例)中,Windows 会尝试隔离音频驱动程序的注册资源免受其他 OS、应用程序和硬件活动的干扰。 OS 和音频子系统根据需要执行此操作,而无需与音频驱动程序交互,音频驱动程序注册资源除外。

此注册流资源的要求意味着流式处理管道路径中的所有驱动程序都必须直接或间接使用 Portcls 注册其资源。 音频微型端口驱动程序具有以下选项:

音频微型端口驱动程序是其堆栈的底部驱动程序, (将 h/w 直接) 接口,在这种情况下,驱动程序知道其流资源,并且可以将其注册到 Portcls。 音频微型端口驱动程序在其他驱动程序的帮助下流式传输音频 (示例音频总线驱动程序) 。 这些其他驱动程序还使用必须向 Portcls 注册的资源。 如果单个供应商拥有音频微型端口驱动程序用于收集此信息的所有驱动程序) ,则这些并行/总线驱动程序堆栈可以公开公共 (或专用接口。 音频微型端口驱动程序正在其他驱动程序的帮助下流式传输音频 (示例 hdaudbus) 。 这些其他驱动程序还使用必须向 Portcls 注册的资源。 这些并行/总线驱动程序可以与 Portcl 链接并直接注册其资源。 音频微型端口驱动程序必须让 Portcl 知道它们依赖于这些其他并行/总线设备的资源, (PDO) 。 HD 音频基础结构使用此选项,即 HD 音频总线驱动程序与 Portcl 链接,并自动执行以下步骤: 注册其总线驱动程序的资源,并 通知 Portcls 子级资源依赖于父级的资源。 在 HD 音频体系结构中,音频微型端口驱动程序只需注册其自己的驱动程序拥有的线程资源。

注意:

由内置 HDAudio 总线驱动程序枚举的 HDAudio 微型端口函数驱动程序 hdaudbus.sys 不需要注册 HDAudio 中断,因为 hdaudbus.sys 已完成此操作。 但是,如果微型端口驱动程序创建自己的线程,则需要注册它们。 仅与 Portcls 链接以注册流式处理资源的驱动程序必须更新其 INF 以包括 wdmaudio.inf,并将 portcls.sys (和依赖文件复制) 。 wdmaudio.inf 中定义了一个新的 INF 复制部分,以仅复制这些文件。 仅在 Windows 10 及更高版本中运行的音频驱动程序可以硬链接到: PcAddStreamResource PcRemoveStreamResource 必须在下级 OS 上运行的音频驱动程序可以使用以下接口 (微型端口可以调用查询接口作为IID_IPortClsStreamResourceManager接口,并且仅在 PortCls 支持接口) 时才注册其资源。 IPortClsStreamResourceManager - AddStreamResource - RemoveStreamResource 这些 DDI 使用此枚举和结构: PcStreamResourceType PCSTREAMRESOURCE_DESCRIPTOR

最后,为了注册资源而链接 PortCl 的驱动程序必须在其 inf 的 DDInstall 节中添加以下两行。 音频微型端口驱动程序不需要此功能,因为它们已在 wdmaudio.inf 中包含/需要。

[] Include=wdmaudio.inf Needs=WDMPORTCLS.CopyFilesOnly

上述行确保已安装 PortCl 及其依赖文件。

度量工具

为了测量往返延迟,用户可以利用通过扬声器播放脉冲的工具,并通过麦克风捕获它们。 它们测量以下路径的延迟:

应用程序调用呈现 API (AudioGraph 或 WASAPI) 来播放脉冲 音频通过扬声器播放 从麦克风捕获音频 脉冲由捕获 API (AudioGraph 或 WASAPI) 为了测量不同缓冲区大小的往返延迟,用户需要安装支持小缓冲区的驱动程序。 收件箱 HDAudio 驱动程序已更新,支持 128 个样本 (2.66ms@48kHz) 到 480 个样本 (10ms@48kHz) 的缓冲区大小。 以下步骤演示如何安装收件箱 HDAudio 驱动程序 (这是所有Windows 10及更高版本 SKU) 的一部分: 启动“设备管理器”。 在 “声音视频和游戏控制器”下,双击与内部扬声器对应的设备。 在下一个窗口中,转到“ 驱动程序 ”选项卡。 选择“ 更新驱动程序 ”->“浏览我的计算机中的驱动程序软件 ”->“让我从此计算机中的设备驱动程序列表中选择 ”->选择“高清音频设备 ”,然后选择“ 下一步”。 如果出现标题为“更新驱动程序警告”的窗口,请选择“ 是”。 选择 “关闭”。 如果系统要求重启,请选择“ 是 ”以重新启动。 重新启动后,系统将使用收件箱 Microsoft HDAudio 驱动程序,而不是第三方编解码器驱动程序。 请记住你以前使用的驱动程序,以便在想要使用音频编解码器的最佳设置时回退到该驱动程序。

WASAPI 和 AudioGraph 之间的延迟差异是由于以下原因造成的:

AudioGraph 在捕获端添加一个延迟缓冲区,以便同步呈现和捕获,WASAPI 不提供。 此添加简化了使用 AudioGraph 编写的应用程序的代码。 当系统使用的缓冲区大于 6 毫秒时,AudioGraph 的呈现端存在另一个延迟缓冲区。 AudioGraph 没有禁用捕获音频效果的选项。 示例 WASAPI 音频示例 AudioCreation 示例 (AudioGraph) Sysvad 驱动程序示例 常见问题解答

如果所有应用程序都使用新 API 实现低延迟,这难道不是更好吗? 低延迟并不总是保证更好的用户体验吗?

不一定。 低延迟有其利弊:

低延迟意味着更高的功耗。 如果系统使用 10 毫秒的缓冲区,则意味着 CPU 将每隔 10 毫秒唤醒一次,填充数据缓冲区并进入睡眠状态。 但是,如果系统使用 1 毫秒的缓冲区,则意味着 CPU 将每 1 毫秒唤醒一次。 第二种情况意味着 CPU 将更频繁地唤醒,并且功耗会增加。 这会缩短电池使用时间。 大多数应用程序依赖于音频效果来提供最佳用户体验。 例如,媒体播放器希望提供高保真音频。 通信应用程序希望将回声和噪音降到最低。 将这些类型的音频效果添加到流会增加其延迟。 这些应用程序对音频质量比对音频延迟更感兴趣。

总之,每种应用程序类型对音频延迟有不同的需求。 如果应用程序不需要低延迟,则不应使用新 API 实现低延迟。

更新到 Windows 10 以后的所有系统是否都会自动更新以支持小型缓冲区? 所有系统是否都支持相同的最小缓冲区大小?

否,为了使系统支持小型缓冲区,它需要更新驱动程序。 由 OEM 决定将更新哪些系统以支持小型缓冲区。 此外,较新的系统更有可能支持比旧系统更小的缓冲区。 新系统中的延迟很可能低于旧系统。

如果驱动程序支持较小的缓冲区大小,Windows 10 及更高版本中的所有应用程序是否会自动使用小型缓冲区来呈现和捕获音频?

否,默认情况下,Windows 10 及更高版本中的所有应用程序都将使用 10 毫秒的缓冲区来呈现和捕获音频。 如果应用程序需要使用小型缓冲区,则需要使用新的 AudioGraph 设置或 WASAPI IAudioClient3 接口才能执行此操作。 但是,如果某个应用程序请求使用小型缓冲区,则音频引擎将开始使用该特定缓冲区大小传输音频。 在这种情况下,使用相同终结点和模式的所有应用程序将自动切换到该较小的缓冲区大小。 当低延迟应用程序退出时,音频引擎将再次切换到 10 毫秒的缓冲区。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3