低延遲音訊
本文討論Windows 10的音訊延遲變更。 它涵蓋應用程式開發人員的 API 選項,以及可支援低延遲音訊的驅動程式變更。 音訊延遲是建立音效的時間和聽到音效的時間之間的延遲。 對於數個主要案例而言,低音訊延遲很重要,例如:
- Pro 音訊
- 音樂建立
- 通訊
- 虛擬實境
- 遊戲
本檔的目標是:
- 描述 Windows 中的音訊延遲來源。
- 說明減少Windows 10音訊堆疊中音訊延遲的變更。
- 提供應用程式開發人員和硬體製造商如何利用新基礎結構的參考,以開發低音訊延遲的應用程式和驅動程式。
本文將說明:
- 互動式和媒體建立案例的新 AudioGraph API。
- WASAPI 中的變更以支援低延遲。
- 驅動程式 DIS 中的增強功能。
詞彙
詞彙 | 描述 |
---|---|
轉譯延遲 | 在應用程式將音訊資料的緩衝區提交至轉譯 API 的時間之間延遲,直到從說話者聽到音訊資料的時間為止。 |
擷取延遲 | 從麥克風擷取音效的時間延遲,直到傳送到應用程式所使用的擷取 API 為止。 |
往返延遲 | 從麥克風擷取音效的時間,由應用程式處理,並由應用程式提交以轉譯給喇叭之間的延遲。 大致上等於轉譯延遲 + 擷取延遲。 |
觸控到應用程式延遲 | 延遲使用者點選螢幕的時間,直到訊號傳送至應用程式的時間。 |
觸控到音效延遲 | 在使用者點選螢幕的時間之間延遲,事件會移至應用程式,並透過喇叭聽到音效。 它等於轉譯延遲 + 觸控到應用程式延遲。 |
Windows 音訊堆疊
下圖顯示簡化版本的 Windows 音訊堆疊。
以下是轉譯路徑中延遲的摘要:音訊處理物件
應用程式會將資料寫入緩衝區
音訊引擎會從緩衝區讀取資料並加以處理。 它也會以音訊處理物件的形式載入音訊效果, (API) 。 如需 ADO 的詳細資訊,請參閱 Windows 音訊處理物件。
ADO 的延遲會根據 ADO 內的訊號處理而有所不同。
在Windows 10之前,音訊引擎的延遲等於 ~12 毫秒,適用于使用浮點數據的應用程式,而針對使用整數資料的應用程式則等於 ~6 毫秒
在Windows 10和更新版本中,所有應用程式的延遲已縮減為 1.3 毫秒
音訊引擎會將已處理的資料寫入緩衝區。
在Windows 10之前,緩衝區一律設定為 ~10 毫秒。
從Windows 10開始,音訊驅動程式會定義緩衝區大小, (本文稍後會說明緩衝區的詳細資料) 。
音訊驅動程式會從緩衝區讀取資料,並將其寫入硬體。
硬體也可以以更多音訊效果的形式再次處理資料。
使用者聽到來自喇叭的音訊。
以下是擷取路徑中的延遲摘要:
音訊會從麥克風擷取。
硬體可以處理資料。 例如,若要新增音訊效果。
驅動程式會從硬體讀取資料,並將資料寫入緩衝區。
在Windows 10之前,此緩衝區一律設定為 10 毫秒。
從Windows 10開始,音訊驅動程式會定義緩衝區大小, (以下) 的詳細資料。
音訊引擎會從緩衝區讀取資料並加以處理。 它也會以音訊處理物件的形式載入音訊效果, (API) 。
ADO 的延遲會根據 ADO 內的訊號處理而有所不同。
在Windows 10之前,音訊引擎的延遲等於 ~6 毫秒,適用于使用浮點數據的應用程式,而針對使用整數資料的應用程式則等於 ~0 毫秒。
在Windows 10和更新版本中,所有應用程式的延遲已縮減為 ~0 毫秒。
當音訊引擎完成其處理之後,應用程式就會收到可讀取資料的訊號。 音訊堆疊也會提供獨佔模式的選項。 在此情況下,資料會略過音訊引擎,並直接從應用程式移至驅動程式從中讀取它的緩衝區。 不過,如果應用程式以獨佔模式開啟端點,則沒有任何其他應用程式可以使用該端點來轉譯或擷取音訊。
另一個需要低延遲之應用程式的熱門替代方案是使用 ASIO (音訊串流輸入/輸出) 模型,其利用獨佔模式。 使用者安裝協力廠商 ASIO 驅動程式之後,應用程式可以直接將資料從應用程式傳送至 ASIO 驅動程式。 不過,應用程式必須以直接與 ASIO 驅動程式交談的方式撰寫。
(獨佔模式和 ASIO) 兩種替代方案都有自己的限制。 它們提供低延遲,但其本身的限制 (上述部分) 所述。 因此,音訊引擎已經過修改,以降低延遲,同時保留彈性。
音訊堆疊改善
Windows 10和更新版本已在三個區域中增強,以減少延遲:
- 相較于Windows 8.1,所有使用音訊的應用程式都會看到 4.5-16 毫秒的來回延遲減少 (,如上一節所述,) 沒有任何程式碼變更或驅動程式更新。
- 使用浮點數據的應用程式會有 16 毫秒較低的延遲。
- 使用整數資料的應用程式會有 4.5 毫秒較低的延遲。
- 具有更新驅動程式的系統可提供較低的來回延遲:
- 驅動程式可以使用新的 DIS 來報告用來在 Windows 與硬體之間傳輸資料的緩衝區支援大小。 資料傳輸不需要一律使用 10 毫秒的緩衝區,如同在舊版 Windows 中所做的一樣。 相反地,驅動程式可以指定它是否可以使用小型緩衝區,例如 5 毫秒、3 毫秒、1 毫秒等等。
- 需要低延遲的應用程式可以使用新的音訊 API (AudioGraph 或 WASAPI) ,來查詢驅動程式支援的緩衝區大小,並選取要用於硬體之間資料傳輸的緩衝區大小。
- 當應用程式使用低於特定閾值的緩衝區大小來轉譯和擷取音訊時,Windows 會進入特殊模式,以避免音訊串流和其他子系統之間的干擾的方式來管理其資源。 這可減少音訊子系統執行中斷,並將音訊問題的可能性降到最低。 當應用程式停止串流處理時,Windows 會返回其正常執行模式。 音訊子系統包含下列資源:
- 正在處理低延遲音訊的音訊引擎執行緒。
- 驅動程式 (使用驅動程式資源註冊) 一節中所述的新 DIS,來 (所有線程和中斷。
- 例如,來自要求小型緩衝區之應用程式的部分或所有音訊執行緒,以及從共用相同音訊裝置圖形的所有應用程式 (,相同的訊號處理模式會與任何要求小型緩衝區的應用程式) :
- 串流路徑上的 AudioGraph 回呼。
- 如果應用程式使用 WASAPI,則只有提交至 即時工作佇列 API 或 MFCreateMFByteStreamOnStreamEx 的工作專案,且標記為「音訊」或「ProAudio」。
API 改善
下列兩個Windows 10 API 提供低延遲功能:
若要判斷要使用哪兩個 API:
- 盡可能支援 AudioGraph,以便進行新的應用程式開發。
- 只有在下列專案時,才使用 WASAPI:
- 您需要比 AudioGraph 所提供的更多控制權。
- 您需要比 AudioGraph 所提供的延遲低。
本文的 度量工具 一節會使用收件匣 HDAudio 驅動程式,顯示 Haswell 系統的特定度量。
下列各節將說明每個 API 中的低延遲功能。 如上一節所述,為了讓系統達到最低延遲,它必須有支援小型緩衝區大小的更新驅動程式。
AudioGraph
AudioGraph 是Windows 10和更新版本中的新通用 Windows 平臺 API,旨在輕鬆實現互動式和音樂建立案例。 AudioGraph 提供數種程式設計語言, (C++、C#、JavaScript) ,而且具有簡單且功能豐富的程式設計模型。
為了以低延遲案例為目標, AudioGraph 會提供 AudioGraphSettings::QuantumSizeSelectionMode 屬性。 此屬性可以是下表所示的任何值:
值 | Description |
---|---|
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 種方法:
方法 | Description |
---|---|
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 音訊,而不是自己的執行緒。 這可讓 Windows 以避免干擾非音訊子系統的方式加以管理。 相反地,Windows 會自動管理所有 AudioGraph 執行緒。 WASAPIAudio 範例中的下列程式碼片段示範如何使用 MF 工作佇列 API。
// 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 );
或者,下列程式碼片段會示範如何使用 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<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;
}
最後,使用 WASAPI 的應用程式開發人員必須以音訊類別標記其串流,以及是否根據每個資料流程的功能使用原始訊號處理模式。 除非瞭解含意,否則 Microsoft 建議所有音訊串流都不會使用原始訊號處理模式。 原始模式會略過 OEM 選擇的所有訊號處理,因此:
- 特定端點的轉譯訊號可能是次佳。
- 擷取訊號的格式可能是應用程式無法理解的格式。
- 延遲可能會改善。
驅動程式改善
為了讓音訊驅動程式支援低延遲,Windows 10和更新版本提供下列功能:
- [強制]宣告每個模式中支援的最小緩衝區大小。
- [選擇性,但建議]改善驅動程式與 Windows 之間的資料流程協調。
- [選擇性,但建議]註冊驅動程式資源 (中斷、執行緒) ,以便在低延遲案例中受到 Windows 保護。 由收件匣 HDAudio 匯流排驅動程式列舉的 HDAudio 迷你埠函式驅動程式 hdaudbus.sys 不需要註冊 HDAudio 中斷,因為此動作已經由 hdaudbus.sys 來完成。 不過,如果迷你埠驅動程式建立自己的執行緒,則需要註冊它們。
下列三節將更深入地說明每個新功能。
宣告緩衝區大小下限
在 Windows、驅動程式和硬體之間移動音訊資料時,驅動程式會在各種條件約束下運作。 這些條件約束可能是由於在記憶體與硬體之間移動資料的實體硬體傳輸,或因為硬體或相關聯的 DSP 內的訊號處理模組所造成。
從 1607 版Windows 10開始,驅動程式可以使用 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 之間的協調
本節所述的 DIS 可讓驅動程式:
- 清楚指出 Windows 可以使用緩衝區的半 (封包) ,而不是根據編解碼器連結位置進行 OS 猜測。 這可協助 Windows 更快速地從音訊問題復原。
- 選擇性地優化或簡化其在 WaveRT 緩衝區內和移出的資料傳輸。 這裡的權益數量取決於 DMA 引擎設計或其他 WaveRT 緩衝區與 (可能是 DSP) 硬體之間的資料傳輸機制。
- 如果驅動程式內部累積的擷取資料,「高載」擷取的資料比即時快。 這主要適用于語音啟用案例,但也可以套用在一般串流期間。
- 提供其目前資料流程位置的時間戳記資訊,而不是 Windows 猜測,可能允許精確的位置資訊。
在使用 DSP 的情況下,這個 DDI 很有用。 不過,標準 HD 音訊驅動程式或其他簡單的迴圈 DMA 緩衝區設計可能無法在此列出的新 DDI 中找到許多好處。
數個驅動程式常式會傳回 Windows 效能計數器時間戳記,反映裝置擷取或呈現樣本的時間。
在具有複雜 DSP 管線和訊號處理的裝置中,計算精確的時間戳記可能很困難,而且應該謹慎完成。 時間戳記不應該反映樣本傳送至 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 音訊匯流排驅動程式與 Portcls 連結,並自動執行下列步驟:
- 註冊其匯流排驅動程式的資源,以及
- 通知 Portcls 子系的資源相依于父系的資源。 在 HD 音訊架構中,音訊迷你埠驅動程式只需要註冊自己的驅動程式擁有線程資源。
注意:
- 由收件匣 HDAudio 匯流排驅動程式列舉的 HDAudio 迷你埠函式驅動程式 hdaudbus.sys 不需要註冊 HDAudio 中斷,因為此動作已經由 hdaudbus.sys 來完成。 不過,如果迷你埠驅動程式建立自己的執行緒,則需要註冊它們。
- 只有註冊串流資源的 Portcls 連結的驅動程式必須更新其 INF,以包含 wdmaudio.inf,並複製 portcls.sys (和相依檔案) 。 新的 INF copy 區段定義于 wdmaudio.inf 中,只複製這些檔案。
- 只在 Windows 10 和更新版本中執行的音訊驅動程式可以硬式連結至:
- 必須在下層 OS 上執行的音訊驅動程式可以使用下列介面, (迷你埠可以呼叫 IID_IPortClsStreamResourceManager 介面的 QueryInterface,並且只有在 PortCls 支援介面) 時,才註冊其資源。
- 這些 DIS,請使用此列舉和結構:
最後,為了註冊資源的唯一目的連結 PortCls 的驅動程式,必須在其 inf 的 DDInstall 區段中新增下列兩行。 音訊迷你埠驅動程式不需要此專案,因為它們已在 wdmaudio.inf 中包含/需求。
[<install-section-name>]
Include=wdmaudio.inf
Needs=WDMPORTCLS.CopyFilesOnly
上述幾行可確保已安裝 PortCls 及其相依檔案。
測量工具
為了測量往返延遲,使用者可以利用透過喇叭播放脈衝的工具,並透過麥克風擷取它們。 它們會測量下列路徑的延遲:
- 應用程式會呼叫轉譯 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 沒有選項可停用擷取音訊效果。
範例
常見問題集
如果所有應用程式都針對低延遲使用新的 API,是否更好? 低延遲不一定會保證更好的使用者體驗?
不一定。 低延遲有其取捨:
- 低延遲表示較高的耗電量。 如果系統使用 10 毫秒的緩衝區,表示 CPU 會每隔 10 毫秒喚醒一次,填滿資料緩衝區並進入睡眠狀態。 不過,如果系統使用 1 毫秒的緩衝區,表示 CPU 每 1 毫秒就會喚醒一次。 第二個案例中,這表示 CPU 會更頻繁地喚醒,而耗電量也會增加。 這會降低電池使用時間。
- 大部分的應用程式都依賴音訊效果來提供最佳使用者體驗。 例如,媒體播放機想要提供高逼真度音訊。 通訊應用程式想要最小回應和雜訊。 將這些類型的音訊效果新增至資料流程會增加其延遲。 這些應用程式比音訊延遲更有興趣音訊品質。
總而言之,每個應用程式類型對於音訊延遲有不同的需求。 如果應用程式不需要低延遲,則不應該使用新的 API 進行低延遲。
更新為 Windows 10 和更新版本的所有系統都會自動更新以支援小型緩衝區嗎? 所有系統都支援相同的緩衝區大小下限嗎?
否,為了讓系統支援小型緩衝區,它必須有更新的驅動程式。 由 OEM 決定要更新哪些系統以支援小型緩衝區。 此外,較新的系統更可能支援比舊系統較小的緩衝區。 新系統中的延遲很可能低於較舊的系統。
如果驅動程式支援小型緩衝區大小,則Windows 10和更新版本中的所有應用程式都會自動使用小型緩衝區來轉譯和擷取音訊嗎?
否,根據預設,Windows 10和更新版本中的所有應用程式都會使用 10 毫秒的緩衝區來轉譯和擷取音訊。 如果應用程式需要使用小型緩衝區,則必須使用新的 AudioGraph 設定或 WASAPI IAudioClient3 介面,才能這麼做。 不過,如果有一個應用程式要求使用小型緩衝區,音訊引擎會開始使用該特定緩衝區大小來傳輸音訊。 在此情況下,使用相同端點和模式的所有應用程式都會自動切換至該小型緩衝區大小。 當低延遲應用程式結束時,音訊引擎會再次切換至 10 毫秒的緩衝區。