結合自定義和 Windows ADO

音訊處理物件 (API) ,為 Windows 音訊串流提供可自定義的軟體數位訊號處理。 您可以結合 Microsoft 提供的 API 與合作夥伴開發的程式代碼、包裝和自訂現有的功能。

如需 ADO 的一般資訊,請參閱這些主題。

ADO 最初是在 Windows Vista 中引進,您可能會看到舊版系統 APOS 的參考 - SAPO。 如需詳細資訊,請參閱 Windows Vista 中的自定義音訊效果 白皮書。 本白皮書可能參考較舊的 COMUI 開發 主題。

如何結合自定義和 Windows ADO

本節包含實作自定義音訊系統效果 APO 的指導方針,方法是在對應的 APO 周圍建立精簡包裝函式。 自定義 APO 是指 APO 的 IHV 實作。

SFX (Stream) 和 MFX (Mode) 有兩種類型的 ADO。 在 Windows 8.1 中,SFX 稱為 LFX (本機) ,而 MFX 則稱為 GFX (全域) API。

IHV 可以實作自定義音訊系統效果 API,以取代 Windows SFX 和 MFX 自定義音訊系統效果 ADO 的其中一個或兩者。 大致來說,IHV 或 OEM 有兩種基本策略,可將自定義音訊系統效果 ADO 與 Windows 提供的 ADO 結合。 這些策略可讓 IHV 彈性地將自定義效果與 Windows 的自定義效果整合。

取代

深入瞭解您想要取代的 Windows APO 及其功能。 使用該了解來實作自定義 APO,以最符合 IHV 目標用戶體驗觀點的方式呼叫 Windows APO。 此策略最適合想要:

  • 將其自定義效果與 Windows 效果緊密整合。
  • 實作自己的UI來控制其效果,以及 Windows APOS 所實作的效果。

如需撰寫 APO 的詳細資訊,請參閱 Windows 音訊處理物件

精簡包裝函式

將自定義 APO 寫入為 Windows APO 周圍的精簡包裝函式。 此策略最適合想要:

  • 以最簡單的方式新增其自定義效果。
  • 讓 Windows UI 繼續控制效果。

選擇 [策略精簡包裝函式] 選項的 IHV 或 OEM 仍應該檢閱 Windows 音訊處理物件 ,以徹底瞭解 Windows 自定義音訊系統效果。

注意:使用精簡包裝函式策略 IHV 時,無法新增 UI 來控制其新增的自定義音訊系統效果至 [Windows 增強功能] 索引標籤。只有一個 [增強功能] 索引標籤,而且它必須與 Windows ADO 的屬性頁保持關聯。 IHV 的 UI 必須以其他方式實作,例如個別 控制台 應用程式。

程序設計資訊

本節涵蓋必須解決的一般程式設計問題,這些問題必須實作自定義 API。

SFX (Stream) 和 MFX (模式) 自定義音訊系統效果 ADO 都有下列一般特性:

  • 它們必須註冊為 COM 同進程伺服器物件,才能使用 CoCreateInstance 具現化。
  • CLSID 分別為 CLSID_CWMAudioLFXAPO SFX 和 MFX API 的 與 CLSID_CWMAudioGFXAPO。 CLSID 會在 wmcodecdsp.h 中宣告,並在 wmcodecdspuuid.lib 中定義。
  • 它們必須支援 COM 匯總。 不過,匯總不應該用於自定義音訊系統效果案例,因此它應該不會造成重大問題。

初始化

自定義 APO 必須藉由呼叫其 IAudioSystemEffects::Initialize 方法來初始化視窗 APO。 這通常是從自定義 APO 的 Initialize 方法完成。 任何傳遞至自定義 APO 的 Initialize 方法的自變數都應該直接傳遞至 Windows APO 的 Initialize。 這可讓 APO 從端點擷取其設定,以及 APOInitSystemEffects 結構中的 Fx 屬性存放區。 自定義 APO 可以擷取設定,並選擇性地將它們傳遞至 APO,但這基本上是策略 A。

如果自定義 APO 取代了功能,通常建議您關閉 APO 上的對應功能。 不過,視功能的運作方式而定,關閉此功能可能並非絕對必要。 若要關閉功能,請查詢 APO 的 IPropertyStore 介面,並呼叫 IPropertyStore::SetValue。 本主題稍後的「支援的 IPropertyStore 屬性」會說明 APO 屬性存放區所支援的屬性。

如需如何與 Windows 自定義音訊系統效果 APO 屬性存放區通訊的範例,請參閱 GitHub 上的範例:: https://github.com/Microsoft/Windows-driver-samples/tree/main/audio/sysvad/APO

查詢 APO 的功能狀態

如果自定義 APO 只會取代 Windows 音訊效果功能,而且沒有自己的設定 UI 或設定存放區,則可能必須判斷對應 APO 上已啟用哪些功能。

至少有兩種方式可以取得這項資訊:

  • 選項 A:直接查詢 Fx 屬性存放區。

  • 選項 B:間接方式是具現化 APO,並使用其 IPropertyStore 介面來查詢屬性存放區。

選項 A

此選項的優點是可以完成,而不需要具現化 APO。 此外,如果自定義 APO 想要監視 Fx 屬性存放區,選項 A 是接收即時屬性變更通知的唯一方式。 如需選項 A 的範例,請參閱「compress」 範例。

使用選項 A 時,自定義 APO 會查詢主要端點屬性存放區,而非 Fx,以進行PKEY_AudioEngine_DeviceFormat。 然後,它會使用該格式的通道遮罩作為用來查詢 Fx 屬性存放區之屬性索引鍵的 PID。 用來查詢 Fx 屬性存放區之屬性索引鍵的 GUID (fmtid) 是 wmcodecdsp.h 的其中一個XXX_XXX_KEY_GUID值。 KEY_GUID名稱會以明顯的方式對應至本主題稍早討論的 MFPKEY 名稱。

選項 B

此選項的優點是,如果 Fx 屬性存放區中的對應屬性不存在,Windows APO 最終可能會啟用某些功能。

使用選項 B 時,自定義 APO 只會查詢 APO 的 IPropertyStore 介面,並使用本主題稍早討論的其中一個MFPKEY_XXX密鑰來呼叫 IPropertyStore::GetValue

格式交涉

實作包裝 SFX APO 的自訂 SFX APO 時,請勿在自訂 APO 的註冊屬性中指定 APO_FLAG_FRAMESPERSECOND_MUST_MATCH 。 不論自定義 APO 是否可以變更通道格式,都應該遵循此規則。 如果自定義 SFX APO 指定此旗標,則會防止對應的 SFX 執行說話者填滿、耳機虛擬化或虛擬環繞。

自定義 SFX APO 實作必須實作或覆寫 IAudioProcessingObject::IsInputFormatSupported。 基類 IsInputFormatSupported 實作不太可能精確地反映自定義 SFX APO 和 SFX APO 所實作的一組可能通道轉換。

自定義 SFX APO 的 IsInputFormatSupported 方法應該呼叫對應的 APO IsInputFormatSupported。 這可確保SFX APO會處理自訂SFX APO未處理的任何通道轉換。 請注意,SFX APO 可能會更新為在未來的 Windows 版本中支援更多轉換。 呼叫 APO 的 IsInputFormatSupported 方法是確保自定義 APO 所支援的通道轉換集完全包含 SFX APO 支援的通道轉換集。

自定義 APO 應該使用 SFX APO IsInputFormatSupported 方法的傳回值,取決於自定義 SFX APO 支援哪些通道轉換。

如果自定義 SFX APO 不支援任何自己的通道轉換,其 IsInputFormatSupported 方法可以直接將 SFX APO 的 IsInputFormatSupported 方法所傳回的值傳回給呼叫端。 如需範例,請參閱“swap” 和 “compress” 範例。

如果自定義 SFX APO 支援自己的通道轉換,則來自 SFX APO 的 IsInputFormatSupported 方法的負傳回值 S_FALSE,不一定會轉譯為負傳回值給呼叫端。 例如,自定義SFX APO可以支持對應 APO 不支援的通道轉換。 在此情況下,自定義SFX APO必須將 SFX APO 的 IsInputFormatSupported 方法的傳回值與自己的邏輯結合,以判斷支持的輸入。 請注意,「合併」的最佳意義取決於通道轉換應該優先的類型。 最佳方法取決於自定義實作的確切設計。

SFX APO 上的 IsOutputFormatSupported 方法並不感興趣,因為 SFX APO 的輸出格式是裝置的混合格式。 此格式是以外部考慮為基礎,且不受 SFX APO 或其輸入格式影響。 因此,這些範例不會嘗試為IsOutputFormatSupported實作正確的邏輯。

上述考慮不適用於 MFX APO,因為 MFX APO 不會實作任何需要或隱含變更通道格式的功能。 基於這個理由,MFX 範例對 IsInputFormatSupported 或 IsOutputFormatSupported 沒有任何特殊作用。 自定義 MFX APO 的格式交涉邏輯不受包裝 MFX APO 的事實影響。

LockForProcess/UnlockForProcess

自定義 APO 的 IAudioProcessingObjectConfiguration::LockForProcess 方法應該呼叫 APO 上的對應方法。 LockForProcess () 是決定各種處理階段發生順序的好位置。 例如,它可以決定要先套用自定義 APO 處理還是 APO 的處理。 這三個範例都提供這類決策邏輯的範例,而範例中的批注則提供一些背景。 不過,無法提供本檔中該主題的完整一般指引,因為它需要瞭解自定義 APO 的特定功能,以及它們如何與 APO 功能互動。

GetLatency

自定義 APO 的 IAudioProcessingObject::GetLatency 實作應該在包裝的 APO 上呼叫 GetLatency。 如果自定義 APO 處理產生延遲,它應該會在將值傳回給呼叫端之前,將它新增至 APO 所傳回的結果。

APOProcess

自定義 APO 的 IAudioProcessingObjectRT::APOProcess 方法應該在處理之前、之後或甚至期間呼叫 APO 的 APOProcess 方法。 應該在 LockForProcess 中決定何時呼叫 APOProcess,以便配置任何必要的中繼緩衝區。 每當 API 的輸入和輸出格式相同時,即支援就地處理。 在此情況下,自定義 APO 可以傳遞與 Windows APO 輸入和輸出連接屬性相同的APO_CONNECTION_PROPERTY。 不過,自定義 APO 不應該使用自訂 APO 的輸入連接屬性做為 APO 的輸出連接屬性。 一般而言,APO 不應該修改其輸入緩衝區。

處理 APO 錯誤

如果 APO 傳回對應的自訂 APO 錯誤,則自定義 APO 應該從該點開始運作,就像沒有 APO 一樣。 這些範例會將所有 APO 錯誤視為相當於 CoCreateInstance 無法建立 APO。 或者,自定義 APO 可以將 APO 的 LockForProcess 方法的錯誤效果限製為目前的工作階段。 換句話說,自定義 APO 不會在其 APOProcess 方法的後續呼叫期間使用 APO。 不過,如果稍後有另一個LockForProcess呼叫,則自定義 APO 可以嘗試再次使用 APO,格式不同。

編譯和連結

若要使用 APO CLSID 和屬性金鑰定義,請包含 wmcodecdsp.h,並使用 wmcodecdspuuid.lib 連結。 如需詳細資訊,請參閱 wmcodecdsp.h 標頭

APO 範例

音訊系統效果範例有四個範例。 APO 範例可在 GitHub 上取得,網址為: https://github.com/Microsoft/Windows-driver-samples/tree/main/audio/sysvad/APO

自定義音訊系統效果的一般指導方針

以下是實作自定義音訊系統效果 API 時,IHD 應遵循的一些指導方針。

  • 所有音訊系統效果都應該提供開啟/關閉選項。 用戶不應該強制使用音訊系統效果。
  • SFX 和 MFX APO 中功能之間的互動應該由 API 及其相關 UI 進行媒體處理。
  • 此處指定為 SFX 或 MFX 的功能可以在自定義實作中的 SFX 與 MFX 之間移動。 不過,這應該要了解開啟/關閉選項應該存在,而且不應危害選項的存取範圍和適當性。
  • 實作者應該記住,SFX 可以有不同的輸入和輸出通道遮罩。 MFX APO 必須具有相同的輸入和輸出通道遮罩。

Windows 提供的 API

如需其他 Windows 提供之 API 的資訊,請參閱這些主題。

Bass Boost

Bass Management

適用於膝上型計算機的增強音效

音量等於 DSP

低頻率保護

會議室更正

演講者填滿

說話者虛設

虛擬周圍

透過耳機虛擬化的環繞音效

特定 APO 自定義資訊

SFX APO) (音量相等

音量相等是壓縮 (動態) 處理,由感知音量計量所驅動。 會議室修正 (MFX APO)

會議室校正會使用會議室校正精靈產生的配置檔。 此配置檔會儲存為二進位 Blob。 Blob 的格式目前未發佈。

通道轉換 (SFX APO)

通道轉換 APO 會處理數個工作。

耳機虛擬化

如果播放的內容通道格式 (N.x) 為 2.0 或更大,則啟用此效果,其中 x 可以是 0 或 1。 輸出遮罩必須是立體 (0x3) 。 輸入遮罩僅限於幾個支持的組合,如下表所列

耳機虛擬化通道遮罩

名稱
MASK_STEREO MASK_FRONTLR 0x3
MASK_3_FRONT (SPEAKER_FRONT_CENTER |MASK_FRONTLR) 0x7
MASK_4_SQUARE (MASK_FRONTLR |MASK_BACKLR) 0x33
MASK_4_DIAMOND (MASK_FRONTLR |MASK_FBCENTERS) 0x107
MASK_5_BACK (MASK_FRONTLR |MASK_BACKLR |SPEAKER_FRONT_CENTER) 0x3F
MASK_5_SIDE (MASK_FRONTLR |MASK_SIDELR |SPEAKER_FRONT_CENTER) 0x60F

虛擬周圍

此效果也稱為左/右 (LTRT) 折疊或左/右矩陣編碼。 如果正在播放的內容通道格式 (N.x) 為 2.0 或更大,其中 x 可以是 0 或 1,則會使用它。 LTRT 折疊通常是 4.0 到 2.0。 任何其他輸入格式通常會先將 N.x 套用至 4.0 泛型折疊來處理。 不過,在我們的實作中,LTRT 折迭是原生 5.1 到 2.0。 任何其他輸入會先將 N.x 套用至 5.1 泛型折疊來處理。

輸出通道遮罩必須0x3 (立体) ,以及輸入通道的數目,包括如果有的子聲道,必須不超過 8 個。

演講者填滿

當輸入通道數目 (N) 小於 M) 輸出 (通道數目時,就會使用這個效果。 效果會將 N.x 通道填滿至 M.x 通道,其中 x 可以是 0 或 1。

表格 4 中的通道遮罩會忽略 LFE 通道,支持說話者填滿。 喇叭填滿支援輸入或輸出子聲道存在的任何組合,因此左邊的數位只是範例。 實際的設定可能或可能沒有子機碼。

說話者填滿通道遮罩

名稱
MASK_STEREO MASK_FRONTLR 0x3
MASK_3_FRONT (SPEAKER_FRONT_CENTER |MASK_FRONTLR) 0x7
MASK_4_SQUARE (MASK_FRONTLR |MASK_BACKLR) \ 0x33
MASK_4_DIAMOND (MASK_FRONTLR |MASK_FBCENTERS) 0x107
MASK_5_BACK (MASK_FRONTLR |MASK_BACKLR |SPEAKER_FRONT_CENTER) 0x3F
MASK_5_SIDE (MASK_FRONTLR |MASK_SIDELR |SPEAKER_FRONT_CENTER) 0x60F
MASK_7_SIDE_BACK (MASK_FRONTLR |MASK_BACKLR |SPEAKER_FRONT_CENTER |MASK_SIDELR) 0x63F
MASK_7_FRONT_SIDE (MASK_FRONTLR |MASK_SIDELR |SPEAKER_FRONT_CENTER |MASK_CENTERLR) 0x6CF
MASK_7_FRONT_BACK (MASK_FRONTLR |MASK_BACKLR |SPEAKER_FRONT_CENTER |MASK_CENTERLR) 0xFF

如果下列任一項成立,則不支持說話者填滿:

  • 輸入掩碼等於輸出遮罩。
  • 輸入和輸出之間的唯一差異在於,有側邊/右通道;另一個有左/右通道。
  • 輸入具有比輸出更多的主要通道。
  • 輸出遮罩包含左/右中央喇叭,但輸入遮罩則不包含。
  • 輸出中的通道集合,但輸入中不包含至少一個:前置、左/右或左/右。

清單中的第二個專案有一個例外。 如果輸入和輸出之間的唯一差異在於其中一個有側邊/右通道,而另一個通道有左/右通道,則如果任一格式包含會落在通道遮罩位順序中 sideLR 和 backLR 之間的通道,則支持說話者填滿。 有三個這類通道:

  • SPEAKER_FRONT_LEFT_OF_CENTER
  • SPEAKER_FRONT_RIGHT_OF_CENTER
  • SPEAKER_BACK_CENTER

如果輸入或輸出遮罩包含這三個通道的任一個,即使它不符合清單中的第二個條件,也可能支持說話者填滿,但只有在滿足其他條件時才支援。 例如,喇叭填滿MASK_7_FRONT_BACK或從MASK_7_FRONT_SIDE填滿喇叭,基於這個原因而受到支援。

下表包含通道值的完整清單。

名稱
SPEAKER_FRONT_LEFT 0x1
SPEAKER_FRONT_RIGHT 0x2
SPEAKER_FRONT_CENTER 0x4
SPEAKER_LOW_FREQUENCY 0x8
SPEAKER_BACK_LEFT 0x10
SPEAKER_BACK_RIGHT 0x20
SPEAKER_FRONT_LEFT_OF_CENTER 0x40
SPEAKER_FRONT_RIGHT_OF_CENTER 0x80
SPEAKER_BACK_CENTER 0x100
SPEAKER_SIDE_LEFT 0x200
SPEAKER_SIDE_RIGHT 0x400

延遲會用於輸入組態中「外部」前端範圍的輸出組態中的通道。 相反地,如果輸出組態中的喇叭是「介於」部分說話者位於前方感知的輸入組態中,該喇叭的輸出是藉由在輸出通道任一端混合一些輸入通道來產生。

重複使用 Windows APOs 時的 Run-Time 考慮

本節包含一些額外的資訊,IHV 和 OEM 在實作其自定義音訊系統效果時可能會發現很有用。

自訂 APO 實作:

  • 使用 CoCreateInstance 具現化 Windows 自定義音訊系統效果 ADO 的一或多個實例。
  • 設定每個實例以啟用所需的一組功能。
  • 將每個實例插入自定義 APO 內部管線內的適當位置。

為何有一或多個實例?

為了避免不想要的互動,大部分功能都需要特定的相對順序。 由於 Windows APO 會在單一 APO 內實作多個功能,因此可能需要該 APO 的多個實例,以確保正確排序。 例如,假設三個已啟用的功能 A、B 和 C 都必須排序 ABC。 自定義實作會處理 B,但會將 A 和 C 委派給 Windows APO。 然後,A 和 C 必須位於 Microsoft APO 的個別實例中,才能在 B 的自定義實作之間發生。

Windows 會在 MFX APO 中實作會議室更正,這表示它是與 SFX APO 不同的 COM 物件。 自定義實作可以選擇將會議室更正委派給 Windows 實作,但將它放在自定義 SFX APO 中。 接著,自定義 SFX 實作可能需要將某些處理委派給 Windows SFX APO 實作,並將其他處理委派給 Windows MFX APO 實作。

處理不同輸入輸出格式組合的限制

許多功能,特別是Bass管理,在特定情況下無法運作。 例如,如果bass喇叭設定屬性為 「AllSmall」 或 「AllLarge」,且輸出格式不包含子聲道或已設定 NoSub 旗標,則正向 Bass 管理不會定義。 在 IPropertyStore::SetValue 呼叫期間,不一定可以偵測失敗。 方法會嘗試啟用此功能,但此時不知道輸入和輸出格式,因為LockForProcess必須在所有屬性操作之後發生。 這表示可以啟用功能、看到功能明顯成功,但不會進行對應的處理。

有兩種策略可用來處理這類情況:

  • 請仔細研究本檔的功能特定區段,以精確預測特定特徵是否會成功或無法成功。
  • 呼叫 LockForProcess 之後呼叫 IPropertyStore::GetValue,以檢查重要屬性的狀態。

當 LockForProcess 判斷無法啟用特定功能時,因為輸入和輸出格式或某些其他屬性的值,LockForProcess 會更新屬性存放區中對應屬性的值。

說話者填滿與Bass管理之間的互動

當喇叭填滿開啟且連接子聲道時,在說話者填滿之前,必須先進行正向Bass管理,以避免喇叭填滿的環繞延遲對低頻率訊號進行下拉式篩選。

當啟用說話者填滿且未連接任何子聲道時,可能會有兩種類型的正向Bass管理:

  • 如果左/右喇叭很大,正向Bass管理會將周圍和中央通道的低頻率部分路由傳送到左/右喇叭。 在此案例中,說話者填滿之後,必須有正向Bass管理。
  • 如果所有喇叭都很小,正向Bass管理會變成所有主要喇叭的低頻率保護。

這可能發生在說話者填滿之前或之後。 不過,基於效能考慮,最好在說話者填滿之前進行向前防禦管理。

Windows APO 會實作一些常見的說話者填滿設定,例如 2.0 => 5.1,並在與說話者填滿相同的步驟中處理反向 Bas 管理的特殊優化程式代碼。

折疊與Bass管理之間的互動

耳機虛擬化僅支援反向Bass管理:

  • 順向Bass管理對於耳機虛擬化並不合理。
  • 為了簡化實作,不支援低頻率保護和 Bas 提升。

當任何耳機虛擬化、虛擬環繞編碼或喇叭填滿效果開啟時,會在該步驟期間處理反向Bass管理。 反向 Bas 管理仍會透過 APOs 反向 Bas 管理屬性來控制,就像是個別的功能一樣。 在這些情況下,反向Bass管理只會控制 .1 輸入通道的折疊係數。 其中一個開啟的問題是當 LTRT 開啟時,無法停用反向 Bass 管理。 在此情況下,反向Bass管理會使用非常數子聲道增益。

Windows 音訊系統效果 ADO 會套用一些次要處理—取得和延遲—即使未啟用任何功能亦然。 這類處理的目標是確保在即時啟用功能時,取得和延遲參數不會變更。 原因是延遲是在實作某些功能時固有的,而某些功能會套用 1 的增益 <,以避免在某些情況下輸出過高。 可用的功能集取決於輸入輸出格式和特定屬性,因此累積正規化增益和延遲也一定。

如果功能不會即時開啟或關閉,則可以藉由呼叫 IPropertyStore::SetValueMFPKEY_CORR_NORMALIZATION_GAIN 屬性設定為 FALSE 來停用正規化提升。 根據預設,屬性可能是TRUE。

沒有機制可停用正規化延遲,因為其假設比正規化提升較不令人反感。 如果正規化延遲令人反感,只要略過有問題的 APO 即可。

另請參閱