Bagikan melalui


API Windows 11 untuk Objek Pemrosesan Audio

Topik ini memperkenalkan sekumpulan API Windows 11 baru untuk Objek Pemrosesan Audio (API) yang dikirim dengan driver audio.

Windows memungkinkan produsen perangkat keras audio pihak ketiga untuk menyertakan efek pemrosesan sinyal digital berbasis host kustom. Efek ini di kemas sebagai Objek Pemrosesan Audio (API) mode pengguna. Untuk informasi selengkapnya, lihat Objek Pemrosesan Audio Windows.

Beberapa API yang dijelaskan di sini memungkinkan skenario baru untuk Vendor Perangkat Keras Independen (IHV) dan Vendor Perangkat Lunak Independen (ISV), sementara API lain dimaksudkan untuk memberikan alternatif yang meningkatkan keandalan audio secara keseluruhan dan kemampuan penelusuran kesalahan.

  • Kerangka kerja Pembatalan Gema Akustik (AEC) memungkinkan APO mengidentifikasi dirinya sebagai AEC APO, memberikan akses ke aliran referensi dan kontrol tambahan.
  • Kerangka kerja Pengaturan akan memungkinkan API mengekspos metode untuk mengkueri dan memodifikasi penyimpanan properti untuk efek audio ("penyimpanan properti FX") pada titik akhir audio. Ketika metode ini diimplementasikan oleh APO, metode ini dapat dipanggil oleh Hardware Support Apps (HSA) yang terkait dengan APO tersebut.
  • Kerangka kerja Pemberitahuan memungkinkan efek audio (API) untuk meminta pemberitahuan untuk menangani perubahan penyimpanan properti efek volume, titik akhir, dan audio.
  • Kerangka kerja Pengelogan membantu dalam pengembangan dan penelusuran kesalahan API.
  • Kerangka kerja Threading memungkinkan API untuk di-multithread dengan menggunakan kumpulan utas yang dikelola OS yang didaftarkan MMCSS.
  • API Penemuan dan Kontrol Efek Audio memungkinkan OS mendeteksi, mengaktifkan, dan menonaktifkan efek yang tersedia untuk diproses pada aliran.

Untuk memanfaatkan API baru ini, API diharapkan menggunakan antarmuka IAudioSystemEffects3 baru. Ketika APO mengimplementasikan antarmuka ini, OS menafsirkan ini sebagai sinyal implisit bahwa APO mendukung kerangka kerja Pengaturan APO dan memungkinkan APO untuk berlangganan pemberitahuan terkait audio umum dari mesin audio.

Persyaratan pengembangan WINDOWS 11 APO CAPX

SETIAP API baru yang dikirim pada perangkat untuk Windows 11 harus mematuhi API yang tercantum dalam topik ini, divalidasi melalui HLK. Selain itu, SETIAP API yang memanfaatkan AEC diharapkan mengikuti implementasi yang diuraikan dalam topik ini, divalidasi melalui HLK. Implementasi kustom untuk ekstensi pemrosesan audio inti ini (Pengaturan, Pengelogan, Pemberitahuan, Threading, AEC) diharapkan dapat memanfaatkan API CAPX. Ini akan divalidasi melalui tes HLK Windows 11. Misalnya, jika APO menggunakan data registri untuk menyimpan pengaturan alih-alih menggunakan Pengaturan Framework, pengujian HLK terkait akan gagal.

Persyaratan versi Windows

API yang dijelaskan dalam topik ini tersedia mulai dari build 22000 OS Windows 11, WDK, dan SDK. Windows 10 tidak akan memiliki dukungan untuk API ini. Jika APO berniat untuk berfungsi pada Windows 10 dan Windows 11, APO dapat memeriksa apakah APOInitSystemEffects2 atau struktur APOInitSystemEffects3 untuk menentukan apakah APOInitSystemEffects3 dijalankan pada OS yang mendukung CAPX API.

Versi terbaru Windows, WDK, dan SDK dapat diunduh di bawah ini melalui Program Windows Insider. Mitra yang terlibat dengan Microsoft melalui Pusat Mitra juga dapat mengakses konten ini melalui Kolaborasi. Untuk informasi selengkapnya tentang Kolaborasi, lihat Pengenalan Microsoft Collaborate.

Konten WHCP Windows 11 telah diperbarui untuk menyediakan sarana kepada mitra untuk memvalidasi API ini.

Kode sampel untuk konten yang diuraikan dalam topik ini dapat ditemukan di sini: Audio/SYSVAD/APO - github

Pembatalan Gema Akustik (AEC)

Pembatalan Gema Akustik (AEC) adalah efek audio umum yang diterapkan oleh Vendor Perangkat Keras Independen (IHV) dan Vendor Perangkat Lunak Independen (ISV) sebagai Objek Pemrosesan Audio (APO) dalam alur pengambilan mikrofon. Efek ini berbeda dari efek lain yang biasanya diimplementasikan oleh IHV dan ISV karena membutuhkan 2 input – aliran audio dari mikrofon dan aliran audio dari perangkat render yang bertindak sebagai sinyal referensi.

Serangkaian antarmuka baru ini memungkinkan AEC APO mengidentifikasi dirinya seperti mesin audio. Melakukannya menghasilkan mesin audio yang mengonfigurasi APO dengan tepat dengan beberapa input dan satu output.

Ketika antarmuka AEC baru diimplementasikan oleh APO, mesin audio akan:

  • Konfigurasikan APO dengan input tambahan yang menyediakan APO dengan aliran referensi dari titik akhir render yang sesuai.
  • Alihkan aliran referensi saat perangkat render berubah.
  • Izinkan APO mengontrol format mikrofon input dan aliran referensi.
  • Izinkan APO untuk mendapatkan tanda waktu pada mikrofon dan aliran referensi.

Pendekatan sebelumnya - Windows 10

API adalah input tunggal – objek output tunggal. Mesin audio menyediakan AEC APO audio dari titik akhir mikrofon pada inputnya. Untuk mendapatkan aliran referensi, APO dapat berinteraksi dengan driver menggunakan antarmuka kepemilikan untuk mengambil audio referensi dari titik akhir render atau menggunakan WASAPI untuk membuka aliran loopback pada titik akhir render.

Kedua pendekatan di atas memiliki kelemahan:

  • APO AEC yang menggunakan saluran privat untuk mendapatkan aliran referensi dari driver, biasanya hanya dapat melakukannya dari perangkat render audio terintegrasi. Akibatnya, pembatalan gema tidak akan berfungsi jika pengguna memutar audio dari perangkat yang tidak terintegrasi seperti usb atau perangkat audio Bluetooth. Hanya OS yang mengetahui titik akhir render yang tepat yang dapat berfungsi sebagai titik akhir referensi.

  • APO dapat menggunakan WASAPI untuk memilih titik akhir render default untuk melakukan pembatalan gema. Namun, ada beberapa jebakan yang harus diperhatikan saat membuka aliran loopback dari proses audiodg.exe (yang merupakan tempat APO dihosting).

    • Aliran loopback tidak dapat dibuka/dihancurkan ketika mesin audio memanggil ke metode APO utama, karena ini dapat mengakibatkan kebuntuan.
    • APO penangkapan tidak mengetahui status aliran kliennya. yaitu aplikasi pengambilan dapat memiliki aliran pengambilan dalam status 'STOP', namun APO tidak menyadari status ini, dan karenanya menjaga aliran loopback terbuka dalam status 'RUN', yang tidak efisien dalam hal konsumsi daya.

Definisi API - AEC

Kerangka kerja AEC menyediakan struktur dan antarmuka baru yang dapat dimanfaat OLEH API. Struktur dan antarmuka baru ini dijelaskan di bawah ini.

struktur APO_CONNECTION_PROPERTY_V2

API yang mengimplementasikan antarmuka IApoAcousticEchoCancellation akan diteruskan struktur APO_CONNECTION_PROPERTY_V2 dalam panggilannya ke IAudioProcessingObjectRT::APOProcess. Selain semua bidang dalam struktur APO_CONNECTION_PROPERTY , versi 2 struktur juga menyediakan informasi tanda waktu untuk buffer audio.

APO dapat memeriksa bidang APO_CONNECTION_PROPERTY.u32Signature untuk menentukan apakah struktur yang diterimanya dari mesin audio berjenis APO_CONNECTION_PROPERTY atau APO_CONNECTION_PROPERTY_V2. APO_CONNECTION_PROPERTY struktur memiliki tanda tangan APO_CONNECTION_PROPERTY_SIGNATURE, sementara APO_CONNECTION_PROPERTY_V2 memiliki tanda tangan yang sama dengan APO_CONNECTION_PROPERTY_V2_SIGNATURE. Jika tanda tangan memiliki nilai yang sama dengan APO_CONNECTION_PROPERTY_V2_SIGNATURE, penunjuk ke struktur APO_CONNECTION_PROPERTY mungkin dititik dengan aman ke penunjuk APO_CONNECTION_PROPERTY_V2.

Kode berikut berasal dari sampel Aec APO MFX - AecApoMfx.cpp dan menunjukkan prakiraan ulang.

    if (ppInputConnections[0]->u32Signature == APO_CONNECTION_PROPERTY_V2_SIGNATURE)
    {
        const APO_CONNECTION_PROPERTY_V2* connectionV2 = reinterpret_cast<const APO_CONNECTION_PROPERTY_V2*>(ppInputConnections[0]);
    }

IApoAcousticEchoCancellation

Antarmuka IApoAcousticEchoCancellation tidak memiliki metode eksplisit di dalamnya. Tujuannya adalah untuk mengidentifikasi AEC APO ke mesin audio. Antarmuka ini hanya dapat diimplementasikan oleh efek mode (MFX) pada titik akhir pengambilan. Menerapkan antarmuka ini pada APO lain akan menyebabkan kegagalan dalam memuat APO tersebut. Untuk informasi umum tentang MFX, lihat Arsitektur Objek Pemrosesan Audio.

Jika efek mode pada titik akhir pengambilan diimplementasikan sebagai serangkaian API berantai, hanya APO yang paling dekat dengan perangkat yang dapat mengimplementasikan antarmuka ini. API yang mengimplementasikan antarmuka ini akan ditawarkan struktur APO_CONNECTION_PROPERTY_V2 dalam panggilannya ke IAudioProcessingobjectRT::APOProcess. APO dapat memeriksa tanda tangan APO_CONNECTION_PROPERTY_V2_SIGNATURE pada properti koneksi dan mengetik struktur APO_CONNECTION_PROPERTY masuk ke struktur APO_CONNECTION_PROPERTY_V2.

Sebagai pengenalan fakta bahwa AEC API biasanya menjalankan algoritma mereka pada tingkat pengambilan sampel/jumlah saluran tertentu, mesin audio menyediakan dukungan pengambilan sampel ulang ke API yang mengimplementasikan antarmuka IApoAcousticEchoCancellation.

Saat AEC APO mengembalikan APOERR_FORMAT_NOT_SUPPORTED dalam panggilan ke IAudioProcessingObject::OutInputFormatSupported, mesin audio akan memanggil IAudioProcessingObject::IsInputFormatSupported pada APO lagi dengan format output NULL dan format input non-null, untuk mendapatkan format yang disarankan APO. Mesin audio kemudian akan mengubah sampel audio mikrofon ke format yang disarankan sebelum mengirimkannya ke AEC APO. Ini menghilangkan kebutuhan AEC APO untuk menerapkan laju pengambilan sampel dan konversi jumlah saluran.

IApoAuxiliaryInputConfiguration

Antarmuka IApoAuxiliaryInputConfiguration menyediakan metode yang dapat diterapkan API sehingga mesin audio dapat menambahkan dan menghapus aliran input tambahan.

Antarmuka ini diimplementasikan oleh AEC APO dan digunakan oleh mesin audio untuk menginisialisasi input referensi. Di Windows 11, AEC APO hanya akan diinisialisasi dengan satu input tambahan - yang memiliki aliran audio referensi untuk pembatalan gema. Metode AddAuxiliaryInput akan digunakan untuk menambahkan input referensi ke APO. Parameter inisialisasi akan berisi referensi ke titik akhir render tempat aliran loopback diperoleh.

Metode IsInputFormatSupported dipanggil oleh mesin audio untuk menegosiasikan format pada input tambahan. Jika AEC APO lebih memilih format tertentu, AEC APO dapat mengembalikan S_FALSE dalam panggilan ke IsInputFormatSupported, dan menentukan format yang disarankan. Mesin audio akan mengubah sampel audio referensi ke format yang disarankan dan menyediakannya pada input tambahan AEC APO.

IApoAuxiliaryInputRT

Antarmuka IApoAuxiliaryInputRT adalah antarmuka realtime-safe yang digunakan untuk mendorong input tambahan APO.

Antarmuka ini digunakan untuk menyediakan data audio pada input tambahan ke APO. Perhatikan bahwa input audio tambahan tidak disinkronkan dengan panggilan ke IAudioProcessingObjectRT::APOProcess. Ketika tidak ada audio yang dirender keluar dari titik akhir render, data loopback tidak akan tersedia di input tambahan. yaitu tidak akan ada panggilan ke IApoAuxiliaryInputRT::AcceptInput

Ringkasan API AEC CAPX

Untuk informasi selengkapnya, temukan informasi tambahan di halaman berikut.

Kode sampel - AEC

Lihat sampel kode Sysvad Audio AecApo berikut.

Kode berikut dari header sampel Aec APO- AecAPO.h menunjukkan tiga metode publik baru yang ditambahkan.

 public IApoAcousticEchoCancellation,
 public IApoAuxiliaryInputConfiguration,
 public IApoAuxiliaryInputRT

...

 COM_INTERFACE_ENTRY(IApoAcousticEchoCancellation)
 COM_INTERFACE_ENTRY(IApoAuxiliaryInputConfiguration)
 COM_INTERFACE_ENTRY(IApoAuxiliaryInputRT)

...


    // IAPOAuxiliaryInputConfiguration
    STDMETHOD(AddAuxiliaryInput)(
        DWORD dwInputId,
        UINT32 cbDataSize,
        BYTE *pbyData,
        APO_CONNECTION_DESCRIPTOR *pInputConnection
        ) override;
    STDMETHOD(RemoveAuxiliaryInput)(
        DWORD dwInputId
        ) override;
    STDMETHOD(IsInputFormatSupported)(
        IAudioMediaType* pRequestedInputFormat,
        IAudioMediaType** ppSupportedInputFormat
        ) override;
...

    // IAPOAuxiliaryInputRT
    STDMETHOD_(void, AcceptInput)(
        DWORD dwInputId,
        const APO_CONNECTION_PROPERTY *pInputConnection
        ) override;

    // IAudioSystemEffects3
    STDMETHODIMP GetControllableSystemEffectsList(_Outptr_result_buffer_maybenull_(*numEffects) AUDIO_SYSTEMEFFECT** effects, _Out_ UINT* numEffects, _In_opt_ HANDLE event) override
    {
        UNREFERENCED_PARAMETER(effects);
        UNREFERENCED_PARAMETER(numEffects);
        UNREFERENCED_PARAMETER(event);
        return S_OK; 
    }

Kode berikut berasal dari sampel Aec APO MFX - AecApoMfx.cpp dan menunjukkan implementasi AddAuxiliaryInput, ketika APO hanya dapat menangani satu input tambahan.

STDMETHODIMP
CAecApoMFX::AddAuxiliaryInput(
    DWORD dwInputId,
    UINT32 cbDataSize,
    BYTE *pbyData,
    APO_CONNECTION_DESCRIPTOR * pInputConnection
)
{
    HRESULT hResult = S_OK;

    CComPtr<IAudioMediaType> spSupportedType;
    ASSERT_NONREALTIME();

    IF_TRUE_ACTION_JUMP(m_bIsLocked, hResult = APOERR_APO_LOCKED, Exit);
    IF_TRUE_ACTION_JUMP(!m_bIsInitialized, hResult = APOERR_NOT_INITIALIZED, Exit);

    BOOL bSupported = FALSE;
    hResult = IsInputFormatSupportedForAec(pInputConnection->pFormat, &bSupported);
    IF_FAILED_JUMP(hResult, Exit);
    IF_TRUE_ACTION_JUMP(!bSupported, hResult = APOERR_FORMAT_NOT_SUPPORTED, Exit);

    // This APO can only handle 1 auxiliary input
    IF_TRUE_ACTION_JUMP(m_auxiliaryInputId != 0, hResult = APOERR_NUM_CONNECTIONS_INVALID, Exit);

    m_auxiliaryInputId = dwInputId;

Tinjau juga kode sampel yang menunjukkan implementasi CAecApoMFX::IsInputFormatSupported dan CAecApoMFX::AcceptInput serta penanganan APO_CONNECTION_PROPERTY_V2.

Urutan operasi - AEC

Pada inisialisasi:

  1. IAudioProcessingObject::Initialize
  2. IApoAuxiliaryInputConfiguration::AddAuxiliaryInput
  3. IAudioProcessingObjectConfiguration:: LockForProcess
  4. IAudioProcessingObjectConfiguration ::UnlockForProcess
  5. IApoAuxiliaryInputConfiguration::RemoveAuxiliaryInput

Pada render perubahan perangkat:

  1. IAudioProcessingObject::Initialize
  2. IApoAuxiliaryInputConfiguration::AddAuxiliaryInput
  3. IAudioProcessingObjectConfiguration::LockForProcess
  4. Perubahan perangkat default
  5. IAudioProcessingObjectConfiguration::UnlockForProcess
  6. IApoAuxiliaryInputConfiguration::RemoveAuxiliaryInput
  7. IApoAuxiliaryInputConfiguration::AddAuxiliaryInput
  8. IAudioProcessingObjectConfiguration::LockForProcess

Ini adalah perilaku buffer yang direkomendasikan untuk AEC.

  • Buffer yang diperoleh dalam panggilan ke IApoAuxiliaryInputRT::AcceptInput harus ditulis ke buffer melingkar tanpa mengunci utas utama.
  • Pada panggilan ke IAudioProcessingObjectRT::APOProcess, buffer melingkar harus dibaca untuk paket audio terbaru dari aliran referensi, dan paket ini harus digunakan untuk menjalankan algoritma pembatalan gema.
  • Tanda waktu pada referensi dan data mikrofon dapat digunakan untuk melapisi data speaker dan mikrofon.

Aliran Loopback Referensi

Secara default, aliran loopback "mengetuk ke" (mendengarkan) aliran audio sebelum volume apa pun atau dibisukan. Aliran loopback yang diketuk sebelum volume diterapkan dikenal sebagai aliran loopback pra-volume. Keuntungan memiliki aliran loopback pra-volume adalah aliran audio yang jelas dan seragam, terlepas dari pengaturan volume saat ini.

Beberapa algoritma AEC mungkin lebih suka mendapatkan aliran loopback yang telah terhubung setelah pemrosesan volume apa pun (termasuk dibisukan). Konfigurasi ini dikenal sebagai loopback pasca volume.

Dalam versi utama BERIKUTNYA dari WINDOWS AEC API dapat meminta loopback pasca-volume pada titik akhir yang didukung.

Batasan

Tidak seperti aliran loopback pra-volume, yang tersedia untuk semua titik akhir render, aliran loopback pasca volume mungkin tidak tersedia di semua titik akhir.

Meminta Loopback Pasca Volume

AEC API yang ingin menggunakan loopback pasca-volume harus mengimplementasikan antarmuka IApoAcousticEchoCancellation2 .

AEC APO dapat meminta loopback pasca-volume dengan mengembalikan bendera APO_REFERENCE_STREAM_PROPERTIES_POST_VOLUME_LOOPBACK melalui parameter Properti dalam implementasi IApoAcousticEchoCancellation2::GetDesiredReferenceStreamProperties.

Bergantung pada titik akhir render yang saat ini digunakan, loopback pasca volume mungkin tidak tersedia. APO AEC diberi tahu jika loopback pasca-volume digunakan ketika metode IApoAuxiliaryInputConfiguration::AddAuxiliaryInput dipanggil. Jika bidang streamProperties AcousticEchoCanceller_Reference_Input berisi APO_REFERENCE_STREAM_PROPERTIES_POST_VOLUME_LOOPBACK, loopback pasca volume sedang digunakan.

Kode berikut dari header sampel AEC APO- AecAPO.h menunjukkan tiga metode publik baru yang ditambahkan.

public:
  // IApoAcousticEchoCancellation2
  STDMETHOD(GetDesiredReferenceStreamProperties)(
    _Out_ APO_REFERENCE_STREAM_PROPERTIES * properties) override;

  // IApoAuxiliaryInputConfiguration
  STDMETHOD(AddAuxiliaryInput)(
    DWORD dwInputId,
    UINT32 cbDataSize,
    _In_ BYTE* pbyData,
    _In_ APO_CONNECTION_DESCRIPTOR *pInputConnection
    ) override;

Cuplikan kode berikut berasal dari sampel AEC APO MFX - AecApoMfx.cpp dan menunjukkan implementasi GetDesiredReferenceStreamProperties, dan bagian addAuxiliaryInput yang relevan.

STDMETHODIMP SampleApo::GetDesiredReferenceStreamProperties(
  _Out_ APO_REFERENCE_STREAM_PROPERTIES * properties)
{
  RETURN_HR_IF_NULL(E_INVALIDARG, properties);

  // Always request that a post-volume loopback stream be used, if
  // available. We will find out which type of stream was actually
  // created when AddAuxiliaryInput is invoked.
  *properties = APO_REFERENCE_STREAM_PROPERTIES_POST_VOLUME_LOOPBACK;
  return S_OK;
}

STDMETHODIMP
CAecApoMFX::AddAuxiliaryInput(
    DWORD dwInputId,
    UINT32 cbDataSize,
    BYTE *pbyData,
    APO_CONNECTION_DESCRIPTOR * pInputConnection
)
{
   // Parameter checking skipped for brevity, please see sample for 
   // full implementation.

  AcousticEchoCanceller_Reference_Input* referenceInput = nullptr;
  APOInitSystemEffects3* papoSysFxInit3 = nullptr;

  if (cbDataSize == sizeof(AcousticEchoCanceller_Reference_Input))
  {
    referenceInput = 
      reinterpret_cast<AcousticEchoCanceller_Reference_Input*>(pbyData);

    if (WI_IsFlagSet(
          referenceInput->streamProperties,
          APO_REFERENCE_STREAM_PROPERTIES_POST_VOLUME_LOOPBACK))
    {
      // Post-volume loopback is being used.
      m_bUsingPostVolumeLoopback = TRUE;
        
      // Note that we can get to the APOInitSystemEffects3 from     
      // AcousticEchoCanceller_Reference_Input.
      papoSysFxInit3 = (APOInitSystemEffects3*)pbyData;
    }
    else  if (cbDataSize == sizeof(APOInitSystemEffects3))
    {
      // Post-volume loopback is not supported.
      papoSysFxInit3 = (APOInitSystemEffects3*)pbyData;
    }

    // Remainder of method skipped for brevity.

kerangka kerja Pengaturan

Pengaturan Framework memungkinkan API mengekspos metode untuk mengkueri dan memodifikasi penyimpanan properti untuk efek audio ("Penyimpanan Properti FX") pada titik akhir audio. Kerangka kerja ini dapat digunakan oleh API dan oleh Hardware Support Apps (HSA) yang ingin mengomunikasikan pengaturan ke APO tersebut. HSA dapat berupa aplikasi Platform Windows Universal (UWP) dan memerlukan kemampuan khusus untuk memanggil API dalam Pengaturan Framework. Untuk informasi selengkapnya tentang aplikasi HSA, lihat aplikasi perangkat UWP.

Struktur Penyimpanan FxProperty

Penyimpanan FxProperty baru memiliki tiga substores: Default, User, dan Volatile.

Subkunjuk "Default" berisi properti efek kustom dan diisi dari file INF. Properti ini tidak bertahan di seluruh peningkatan OS. Misalnya, properti yang biasanya didefinisikan dalam INF akan cocok di sini. Ini kemudian akan diisi ulang dari INF.

Subkunjuk "Pengguna" berisi pengaturan pengguna yang berkaitan dengan properti efek. Pengaturan ini dipertahankan oleh OS di seluruh peningkatan dan migrasi. Misalnya, preset apa pun yang dapat dikonfigurasi pengguna yang diharapkan bertahan di seluruh peningkatan.

Subkunjuk "Volatil" mengandung sifat efek volatil. Properti ini hilang setelah boot ulang perangkat dan dibersihkan setiap kali transisi titik akhir aktif. Ini diharapkan berisi properti varian waktu (misalnya berdasarkan aplikasi yang sedang berjalan, postur perangkat, dll.) Misalnya, pengaturan apa pun yang bergantung pada lingkungan saat ini.

Cara untuk memikirkan pengguna versus default adalah apakah Anda ingin properti bertahan di seluruh peningkatan OS dan driver. Properti pengguna akan dipertahankan. Properti default akan diisi ulang dari INF.

Konteks APO

Kerangka kerja pengaturan CAPX memungkinkan penulis APO mengelompokkan properti APO berdasarkan konteks. Setiap APO dapat menentukan konteksnya sendiri dan memperbarui properti relatif terhadap konteksnya sendiri. Penyimpanan properti efek untuk titik akhir audio mungkin memiliki nol atau lebih konteks. Vendor bebas untuk membuat konteks namun mereka memilih, apakah itu oleh SFX/MFX/EFX atau berdasarkan mode. Vendor juga dapat memilih untuk memiliki satu konteks untuk semua API yang dikirim oleh vendor tersebut.

Pengaturan Kemampuan Terbatas

API pengaturan dimaksudkan untuk mendukung semua OEM dan pengembang HSA yang tertarik untuk mengkueri dan memodifikasi pengaturan efek audio yang terkait dengan perangkat audio. API ini diekspos ke aplikasi HSA dan Win32 untuk menyediakan akses ke penyimpanan properti melalui kemampuan terbatas "audioDeviceConfiguration" yang harus dideklarasikan dalam manifes. Selain itu, namespace yang sesuai harus dinyatakan sebagai berikut:

<Package
  xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
  IgnorableNamespaces="uap mp rescap">
  ...
 
  <Capabilities>
    <rescap:Capability Name="audioDeviceConfiguration" />
  </Capabilities>
</Package>

IAudioSystemEffectsPropertyStore dapat dibaca dan dapat ditulis oleh layanan ISV/IHV, aplikasi penyimpanan UWP, aplikasi desktop non-admin, dan API. Selain itu, ini dapat bertindak sebagai mekanisme bagi API untuk mengirimkan pesan kembali ke layanan atau aplikasi penyimpanan UWP.

Catatan

Ini adalah kemampuan terbatas: Jika aplikasi dikirimkan dengan kemampuan ini ke Microsoft Store, aplikasi akan memicu pengamatan ketat. Aplikasi harus berupa Aplikasi Dukungan Perangkat Keras (HSA), dan akan diperiksa untuk mengevaluasi bahwa itu memang HSA sebelum pengiriman disetujui.

Definisi API - Pengaturan Framework

Antarmuka IAudioSystemEffectsPropertyStore baru memungkinkan HSA untuk mengakses penyimpanan properti efek sistem audio dan mendaftar untuk pemberitahuan perubahan properti.

Fungsi ActiveAudioInterfaceAsync menyediakan metode untuk mendapatkan antarmuka IAudioSystemEffectsPropertyStore secara asinkron.

Aplikasi dapat menerima pemberitahuan ketika penyimpanan properti efek sistem berubah, menggunakan antarmuka panggilan balik IAudioSystemEffectsPropertyChangeNotificationClient baru.

Aplikasi yang mencoba mendapatkan IAudioSystemEffectsPropertyStore menggunakan IMMDevice::Activate

Sampel menunjukkan bagaimana Aplikasi Dukungan Perangkat Keras dapat menggunakan IMMDevice::Activate untuk mengaktifkan IAudioSystemEffectsPropertyStore. Sampel menunjukkan cara menggunakan IAudioSystemEffectsPropertyStore untuk membuka IPropertyStore yang memiliki pengaturan pengguna.

#include <mmdeviceapi.h>

// This function opens an IPropertyStore with user settings on the specified IMMDevice.
// Input parameters:
// device - IMMDevice object that identifies the audio endpoint.
// propertyStoreContext - GUID that identifies the property store. Each APO can have its own GUID. These 
// GUIDs are chosen by the audio driver at installation time.
HRESULT GetPropertyStoreFromMMDevice(_In_ IMMDevice* device,
    REFGUID propertyStoreContext,
    _COM_Outptr_ IPropertyStore** userPropertyStore)
{
    *userPropertyStore = nullptr;

    wil::unique_prop_variant activationParam;
    RETURN_IF_FAILED(InitPropVariantFromCLSID(propertyStoreContext, &activationParam));

    wil::com_ptr_nothrow<IAudioSystemEffectsPropertyStore> effectsPropertyStore;
    RETURN_IF_FAILED(device->Activate(__uuidof(effectsPropertyStore), CLSCTX_INPROC_SERVER, activationParam.addressof(), effectsPropertyStore.put_void()));

    RETURN_IF_FAILED(effectsPropertyStore->OpenUserPropertyStore(STGM_READWRITE, userPropertyStore));
    return S_OK;
}

Sampel menggunakan ActivateAudioInterfaceAsync

Sampel ini melakukan hal yang sama seperti sampel sebelumnya, tetapi alih-alih menggunakan IMMDevice, ia menggunakan API ActivateAudioInterfaceAsync untuk mendapatkan antarmuka IAudioSystemEffectsPropertyStore secara asinkron.

include <mmdeviceapi.h>

class PropertyStoreHelper : 
    public winrt::implements<PropertyStoreHelper, IActivateAudioInterfaceCompletionHandler>
{
public:
    wil::unique_event_nothrow m_asyncOpCompletedEvent;

    HRESULT GetPropertyStoreAsync(
        _In_ PCWSTR deviceInterfacePath,
        REFGUID propertyStoreContext,
        _COM_Outptr_ IActivateAudioInterfaceAsyncOperation** operation);

    HRESULT GetPropertyStoreResult(_COM_Outptr_ IPropertyStore** userPropertyStore);

    // IActivateAudioInterfaceCompletionHandler
    STDMETHOD(ActivateCompleted)(_In_ IActivateAudioInterfaceAsyncOperation *activateOperation);

private:
    wil::com_ptr_nothrow<IPropertyStore> m_userPropertyStore;
    HRESULT m_hrAsyncOperationResult = E_FAIL;

    HRESULT GetUserPropertyStore(
        _In_ IActivateAudioInterfaceAsyncOperation* operation,
        _COM_Outptr_ IPropertyStore** userPropertyStore);
};

// This function opens an IPropertyStore with user settings asynchronously on the specified audio endpoint.
// Input parameters:
// deviceInterfacePath - the Device Interface Path string that identifies the audio endpoint. Can be 
// obtained from Windows.Devices.Enumeration.DeviceInformation.
// propertyStoreContext - GUID that identifies the property store. Each APO can have its own GUID. These 
// GUIDs are chosen by the audio driver at installation time.
//
// The function returns an IActivateAudioInterfaceAsyncOperation, which can be used to check the result of
// the asynchronous operation.
HRESULT PropertyStoreHelper::GetPropertyStoreAsync(
    _In_ PCWSTR deviceInterfacePath,
    REFGUID propertyStoreContext,
    _COM_Outptr_ IActivateAudioInterfaceAsyncOperation** operation)
{
    *operation = nullptr;

    wil::unique_prop_variant activationParam;
    RETURN_IF_FAILED(InitPropVariantFromCLSID(propertyStoreContext, &activationParam));

    RETURN_IF_FAILED(ActivateAudioInterfaceAsync(deviceInterfacePath,
        __uuidof(IAudioSystemEffectsPropertyStore),
        activationParam.addressof(),
        this,
        operation));
    return S_OK;
}

// Once the IPropertyStore is available, the app can call this function to retrieve it.
// (The m_asyncOpCompletedEvent event is signaled when the asynchronous operation to retrieve
// the IPropertyStore has completed.)
HRESULT PropertyStoreHelper::GetPropertyStoreResult(_COM_Outptr_ IPropertyStore** userPropertyStore)
{
    *userPropertyStore = nullptr;

    // First check if the asynchronous operation completed. If it failed, the error code
    // is stored in the m_hrAsyncOperationResult variable.
    RETURN_IF_FAILED(m_hrAsyncOperationResult);

    RETURN_IF_FAILED(m_userPropertyStore.copy_to(userPropertyStore));
    return S_OK;
}

// Implementation of IActivateAudioInterfaceCompletionHandler::ActivateCompleted.
STDMETHODIMP PropertyStoreHelper::ActivateCompleted(_In_ IActivateAudioInterfaceAsyncOperation* operation)
{
    m_hrAsyncOperationResult = GetUserPropertyStore(operation, m_userPropertyStore.put());

    // Always signal the event that our caller might be waiting on before we exit,
    // even in case of failure.
    m_asyncOpCompletedEvent.SetEvent();
    return S_OK;
}

HRESULT PropertyStoreHelper::GetUserPropertyStore(
    _In_ IActivateAudioInterfaceAsyncOperation* operation,
    _COM_Outptr_ IPropertyStore** userPropertyStore)
{
    *userPropertyStore = nullptr;

    // Check if the asynchronous operation completed successfully, and retrieve an
    // IUnknown pointer to the result.
    HRESULT hrActivateResult;
    wil::com_ptr_nothrow<IUnknown> audioInterfaceUnknown;
    RETURN_IF_FAILED(operation->GetActivateResult(&hrActivateResult, audioInterfaceUnknown.put()));
    RETURN_IF_FAILED(hrActivateResult);

    // Convert the result to IAudioSystemEffectsPropertyStore
    wil::com_ptr_nothrow<IAudioSystemEffectsPropertyStore> effctsPropertyStore;
    RETURN_IF_FAILED(audioInterfaceUnknown.query_to(&effectsPropertyStore));

    // Open an IPropertyStore with the user settings.
    RETURN_IF_FAILED(effectsPropertyStore->OpenUserPropertyStore(STGM_READWRITE, userPropertyStore));
    return S_OK;
}

IAudioProcessingObject::Inisialisasi kode menggunakan IAudioSystemEffectsPropertyStore

Sampel menunjukkan implementasi APO dapat menggunakan struktur APOInitSystemEffects3 untuk mengambil antarmuka IPropertyStore default dan volatil untuk APO, selama inisialisasi APO.

#include <audioenginebaseapo.h>

// Partial implementation of APO to show how an APO that implements IAudioSystemEffects3 can handle
// being initialized with the APOInitSystemEffects3 structure.
class SampleApo : public winrt::implements<SampleApo, IAudioProcessingObject,
    IAudioSystemEffects, IAudioSystemEffects2, IAudioSystemEffects3>
{
public:
    // IAudioProcessingObject
    STDMETHOD(Initialize)(UINT32 cbDataSize, BYTE* pbyData);

    // Implementation of IAudioSystemEffects2, IAudioSystemEffects3 has been omitted from this sample for brevity.  

private:

    wil::com_ptr_nothrow<IPropertyStore> m_defaultStore;
    wil::com_ptr_nothrow<IPropertyStore> m_userStore;
    wil::com_ptr_nothrow<IPropertyStore> m_volatileStore;

    // Each APO has its own private collection of properties. The collection is identified through a
    // a property store context GUID, which is defined below and in the audio driver INF file.
    const GUID m_propertyStoreContext = ...;
};

// Implementation of IAudioProcessingObject::Initialize
STDMETHODIMP SampleApo::Initialize(UINT32 cbDataSize, BYTE* pbyData)
{
    if (cbDataSize == sizeof(APOInitSystemEffects3))
    {
        // SampleApo supports the new IAudioSystemEffects3 interface so it will receive APOInitSystemEffects3
        // in pbyData if the audio driver has declared support for this.

        // Use IMMDevice to activate IAudioSystemEffectsPropertyStore that contains the default, user and
        // volatile settings.
        IMMDeviceCollection* deviceCollection = 
            reinterpret_cast<APOInitSystemEffects3*>(pbyData)->pDeviceCollection;
        if (deviceCollection != nullptr)
        {
            UINT32 numDevices;
            wil::com_ptr_nothrow<IMMDevice> endpoint;

            // Get the endpoint on which this APO has been created
            // (It is the last device in the device collection)
            if (SUCCEEDED(deviceCollection->GetCount(&numDevices)) &&
                numDevices > 0 &&
                SUCCEEDED(deviceCollection->Item(numDevices - 1, &endpoint)))
            {
                wil::unique_prop_variant activationParam;
                RETURN_IF_FAILED(InitPropVariantFromCLSID(m_propertyStoreContext, &activationParam));

                wil::com_ptr_nothrow<IAudioSystemEffectsPropertyStore> effectsPropertyStore;
                RETURN_IF_FAILED(endpoint->Activate(__uuidof(effectsPropertyStore), CLSCTX_ALL, &activationParam, effectsPropertyStore.put_void()));

                // Read default, user and volatile property values to set up initial operation of the APO
                RETURN_IF_FAILED(effectsPropertyStore->OpenDefaultPropertyStore(STGM_READWRITE, m_defaultStore.put()));
                RETURN_IF_FAILED(effectsPropertyStore->OpenUserPropertyStore(STGM_READWRITE, m_userStore.put()));
                RETURN_IF_FAILED(effectsPropertyStore->OpenVolatilePropertyStore(STGM_READWRITE, m_volatileStore.put()));

                // At this point the APO can read and write settings in the various property stores,
                // as appropriate. (Not shown.)
                // Note that APOInitSystemEffects3 contains all the members of APOInitSystemEffects2,
                // so an APO that knows how to initialize from APOInitSystemEffects2 can use the same
                // code to continue its initialization here.
            }
        }
    }
    else if (cbDataSize == sizeof(APOInitSystemEffects2))
    {
        // Use APOInitSystemEffects2 for the initialization of the APO.
        // If we get here, the audio driver did not declare support for IAudioSystemEffects3.
    }
    else if (cbDataSize == sizeof(APOInitSystemEffects))
    {
        // Use APOInitSystemEffects for the initialization of the APO.
    }

    return S_OK;
}

Pendaftaran aplikasi untuk pemberitahuan perubahan properti

Sampel menunjukkan penggunaan pendaftaran untuk pemberitahuan perubahan properti. Ini tidak boleh digunakan dari dengan APO, dan harus digunakan oleh aplikasi Win32.

class PropertyChangeNotificationClient : public 
    winrt::implements<PropertyChangeNotificationClient, IAudioSystemEffectsPropertyChangeNotificationClient>
{
private:
    wil::com_ptr_nothrow<IAudioSystemEffectsPropertyStore> m_propertyStore;
    bool m_isListening = false;

public:
    HRESULT OpenPropertyStoreOnDefaultRenderEndpoint(REFGUID propertyStoreContext);
    HRESULT StartListeningForPropertyStoreChanges();
    HRESULT StopListeningForPropertyStoreChanges();

    // IAudioSystemEffectsPropertyChangeNotificationClient
    STDMETHOD(OnPropertyChanged)(AUDIO_SYSTEMEFFECTS_PROPERTYSTORE_TYPE type, const PROPERTYKEY key);
};

// Open the IAudioSystemEffectsPropertyStore. This should be the first method invoked on this class.
HRESULT PropertyChangeNotificationClient::OpenPropertyStoreOnDefaultRenderEndpoint(
    REFGUID propertyStoreContext)
{
    wil::com_ptr_nothrow<IMMDeviceEnumerator> deviceEnumerator;
    RETURN_IF_FAILED(CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&deviceEnumerator)));

    wil::com_ptr_nothrow<IMMDevice> device;
    RETURN_IF_FAILED(deviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, device.put()));

    wil::unique_prop_variant activationParam;
    RETURN_IF_FAILED(InitPropVariantFromCLSID(propertyStoreContext, &activationParam));

    RETURN_IF_FAILED(device->Activate(__uuidof(m_propertyStore), CLSCTX_INPROC_SERVER,
        &activationParam, m_propertyStore.put_void()));
    return S_OK;
}

// Start subscribing to callbacks that are invoked when there are changes to any of the IPropertyStores
// that are managed by IAudioSystemEffectsPropertyStore.
// The OpenPropertyStoreOnDefaultRenderEndpoint should have been invoked prior to invoking this function.
HRESULT PropertyChangeNotificationClient::StartListeningForPropertyStoreChanges()
{
    RETURN_HR_IF(E_FAIL, !m_propertyStore);
    RETURN_IF_FAILED(m_propertyStore->RegisterPropertyChangeNotification(this));
    m_isListening = true;
    return S_OK;
}

// Unsubscribe to event callbacks. Since IAudioSystemEffectsPropertyStore takes a reference on our
// PropertyChangeNotificationClient class, it is important that this method is invoked prior to cleanup,
// to break the circular reference.
HRESULT PropertyChangeNotificationClient::StopListeningForPropertyStoreChanges()
{
    if (m_propertyStore != nullptr && m_isListening)
    {
        RETURN_IF_FAILED(m_propertyStore->UnregisterPropertyChangeNotification(this));
        m_isListening = false;
    }
    return S_OK;
}

// Callback method that gets invoked when there have been changes to any of the IPropertyStores
// that are managed by IAudioSystemEffectsPropertyStore. Note that calls to 
// IAudioSystemEffectsPropertyChangeNotificationClient are not marshalled across COM apartments.
// Therefore, the OnPropertyChanged is most likely invoked on a different thread than the one used when
// invoking RegisterPropertyChangeNotification. If necessary, concurrent access to shared state should be
// protected with a critical section. 
STDMETHODIMP PropertyChangeNotificationClient::OnPropertyChanged(AUDIO_SYSTEMEFFECTS_PROPERTYSTORE_TYPE type, const PROPERTYKEY key)
{
    if (type == AUDIO_SYSTEMEFFECTS_PROPERTYSTORE_TYPE_USER)
    {
        // Handle changes to the User property store.

        wil::com_ptr_nothrow<IPropertyStore> userPropertyStore;
        RETURN_IF_FAILED(m_propertyStore->OpenUserPropertyStore(STGM_READ, userPropertyStore.put()));

        // Here we can call IPropertyStore::GetValue to read the current value of PROPERTYKEYs that we are
        // interested in.
    }
    else if (type == AUDIO_SYSTEMEFFECTS_PROPERTYSTORE_TYPE_VOLATILE)
    {
        // Handle changes to the Volatile property store, if desired
    }

    return S_OK;
}

Kode sampel - Pengaturan Framework

Kode sampel ini berasal dari sampel Sysvad SFX Swap APO - SwapAPOSFX.cpp.

// SampleApo supports the new IAudioSystemEffects3 interface so it will receive APOInitSystemEffects3
// in pbyData if the audio driver has declared support for this.

// Use IMMDevice to activate IAudioSystemEffectsPropertyStore that contains the default, user and
// volatile settings.
IMMDeviceCollection* deviceCollection = reinterpret_cast<APOInitSystemEffects3*>(pbyData)->pDeviceCollection;
if (deviceCollection != nullptr)
{
    UINT32 numDevices;
    wil::com_ptr_nothrow<IMMDevice> endpoint;

    // Get the endpoint on which this APO has been created
    // (It is the last device in the device collection)
    if (SUCCEEDED(deviceCollection->GetCount(&numDevices)) && numDevices > 0 &&
        SUCCEEDED(deviceCollection->Item(numDevices - 1, &endpoint)))
    {
        wil::unique_prop_variant activationParam;
        hr = InitPropVariantFromCLSID(SWAP_APO_SFX_CONTEXT, &activationParam);
        IF_FAILED_JUMP(hr, Exit);

        wil::com_ptr_nothrow<IAudioSystemEffectsPropertyStore> effectsPropertyStore;
        hr = endpoint->Activate(__uuidof(effectsPropertyStore), CLSCTX_ALL, &activationParam, effectsPropertyStore.put_void());
        IF_FAILED_JUMP(hr, Exit);

        // This is where an APO might want to open the volatile or default property stores as well 
        // Use STGM_READWRITE if IPropertyStore::SetValue is needed.
        hr = effectsPropertyStore->OpenUserPropertyStore(STGM_READ, m_userStore.put());
        IF_FAILED_JUMP(hr, Exit);
    }
}

Bagian INF - Pengaturan Framework

Sintaks file INF untuk mendeklarasikan properti efek menggunakan kerangka kerja pengaturan CAPX baru adalah sebagai berikut:

HKR, FX\0\{ApoContext}\{Default|User}, %CUSTOM_PROPERTY_KEY%,,,

Ini menggantikan sintaks yang lebih lama untuk mendeklarasikan properti efek sebagai berikut:

# Old way of declaring FX properties
HKR, FX\0, %CUSTOM_PROPERTY_KEY_1%,,,

INF tidak dapat memiliki entri IAudioSystemEffectsPropertyStore dan entri IPropertyStore untuk titik akhir audio yang sama. Itu tidak didukung.

Contoh yang menunjukkan penggunaan penyimpanan properti baru:

HKR,FX\0\%SWAP_APO_CONTEXT%,%PKEY_FX_Association%,,%KSNODETYPE_ANY%
; Enable the channel swap in the APO
HKR,FX\0\%SWAP_APO_CONTEXT%\User,%PKEY_Endpoint_Enable_Channel_Swap_SFX%,REG_DWORD,0x1

PKEY_Endpoint_Enable_Channel_Swap_SFX = "{A44531EF-5377-4944-AE15-53789A9629C7},2"
REG_DWORD = 0x00010001 ; FLG_ADDREG_TYPE_DWORD
SWAP_APO_CONTEXT = "{24E7F619-5B33-4084-9607-878DA8722417}"
PKEY_FX_Association  = "{D04E05A6-594B-4FB6-A80D-01AF5EED7D1D},0"
KSNODETYPE_ANY   = "{00000000-0000-0000-0000-000000000000}"

Kerangka Kerja Pemberitahuan

Kerangka kerja Pemberitahuan memungkinkan efek audio (API) untuk meminta dan menangani volume, titik akhir, dan pemberitahuan perubahan penyimpanan properti efek audio. Kerangka kerja ini dimaksudkan untuk menggantiKAN API yang ada yang digunakan oleh API untuk mendaftar dan membatalkan pendaftaran pemberitahuan.

API baru memperkenalkan antarmuka yang dapat digunakan API untuk mendeklarasikan jenis pemberitahuan yang diminati APO. Windows akan mengkueri APO untuk pemberitahuan yang diminatinya, dan meneruskan pemberitahuan ke API. API tidak perlu lagi secara eksplisit memanggil API pendaftaran atau unregistration.

Pemberitahuan dikirimkan ke APO menggunakan antrean serial. Jika berlaku, pemberitahuan pertama menyiarkan status awal dari nilai yang diminta (misalnya, volume titik akhir audio). Pemberitahuan berhenti setelah audiodg.exe berhenti berniat menggunakan APO untuk streaming. API akan berhenti menerima pemberitahuan setelah UnlockForProcess. Masih perlu untuk menyinkronkan UnlockForProcess dan pemberitahuan dalam penerbangan apa pun.

Implementasi - Kerangka Kerja Pemberitahuan

Untuk memanfaatkan kerangka kerja pemberitahuan, APO menyatakan pemberitahuan apa yang diminatinya. Tidak ada panggilan pendaftaran/unregistrasi eksplisit. Semua pemberitahuan ke APO diserialisasikan, dan penting untuk tidak memblokir utas panggilan balik pemberitahuan terlalu lama.

Definisi API - Kerangka Kerja Pemberitahuan

Kerangka kerja pemberitahuan mengimplementasikan antarmuka IAudioProcessingObjectNotifications baru yang dapat diimplementasikan oleh klien untuk mendaftar dan menerima pemberitahuan umum terkait audio untuk titik akhir APO dan pemberitahuan efek sistem.

Untuk informasi selengkapnya, temukan konten tambahan di halaman berikut:

Kode sampel - Kerangka Kerja Pemberitahuan

Sampel menunjukkan bagaimana APO dapat mengimplementasikan antarmuka IAudioProcessingObjectNotifications. Dalam metode GetApoNotificationRegistrationInfo, sampel APO mendaftar untuk pemberitahuan untuk perubahan pada penyimpanan properti efek sistem.
Metode HandleNotification dipanggil oleh OS untuk memberi tahu APO perubahan yang cocok dengan apa yang telah didaftarkan oleh APO.

class SampleApo : public winrt::implements<SampleApo, IAudioProcessingObject,
    IAudioSystemEffects, IAudioSystemEffects2, IAudioSystemEffects3,
    IAudioProcessingObjectNotifications>
{
public:
    // IAudioProcessingObjectNotifications
    STDMETHOD(GetApoNotificationRegistrationInfo)(
        _Out_writes_(count) APO_NOTIFICATION_DESCRIPTOR** apoNotifications, _Out_ DWORD* count);
    STDMETHOD_(void, HandleNotification)(_In_ APO_NOTIFICATION *apoNotification);

    // Implementation of IAudioSystemEffects2, IAudioSystemEffects3 has been omitted from this sample for brevity. 

private:
    wil::com_ptr_nothrow<IMMDevice> m_device;

    // Each APO has its own private collection of properties. The collection is identified through a
    // a property store context GUID, which is defined below and in the audio driver INF file.
    const GUID m_propertyStoreContext = ...;

    float m_masterVolume = 1.0f;
    BOOL m_isMuted = FALSE;
    BOOL m_allowOffloading = FALSE;

    // The rest of the implementation of IAudioProcessingObject is omitted for brevity
};

// The OS invokes this method on the APO to find out what notifications the APO is interested in.
STDMETHODIMP SampleApo::GetApoNotificationRegistrationInfo(
    _Out_writes_(count) APO_NOTIFICATION_DESCRIPTOR** apoNotificationDescriptorsReturned,
    _Out_ DWORD* count)
{
    *apoNotificationDescriptorsReturned = nullptr;
    *count = 0;

    // Before this function can be called, our m_device member variable should already have been initialized.
    // This would typically be done in our implementation of IAudioProcessingObject::Initialize, by using
    // APOInitSystemEffects3::pDeviceCollection to obtain the last IMMDevice in the collection.
    RETURN_HR_IF_NULL(E_FAIL, m_device);

    // Let the OS know what notifications we are interested in by returning an array of
    // APO_NOTIFICATION_DESCRIPTORs.
    constexpr DWORD numDescriptors = 3;
    wil::unique_cotaskmem_ptr<APO_NOTIFICATION_DESCRIPTOR[]> apoNotificationDescriptors;

    apoNotificationDescriptors.reset(static_cast<APO_NOTIFICATION_DESCRIPTOR*>(
        CoTaskMemAlloc(sizeof(APO_NOTIFICATION_DESCRIPTOR) * numDescriptors)));
    RETURN_IF_NULL_ALLOC(apoNotificationDescriptors);

    // Our APO wants to get notified when any change occurs on the user property store on the audio endpoint
    // identified by m_device.
    // The user property store is different for each APO. Ours is identified by m_propertyStoreContext.
    apoNotificationDescriptors[0].type = APO_NOTIFICATION_TYPE_AUDIO_SYSTEM_EFFECTS_PROPERTY_CHANGE;
    (void)m_device.query_to(&apoNotificationDescriptors[0].audioSystemEffectsPropertyChange.device);
    apoNotificationDescriptors[0].audioSystemEffectsPropertyChange.propertyStoreContext =   m_propertyStoreContext;

    // Our APO wants to get notified when an endpoint property changes on the audio endpoint.
    apoNotificationDescriptors[1].type = APO_NOTIFICATION_TYPE_ENDPOINT_PROPERTY_CHANGE;
    (void)m_device.query_to(&apoNotificationDescriptors[1].audioEndpointPropertyChange.device);


    // Our APO also wants to get notified when the volume level changes on the audio endpoint.
    apoNotificationDescriptors   [2].type = APO_NOTIFICATION_TYPE_ENDPOINT_VOLUME;
    (void)m_device.query_to(&apoNotificationDescriptors[2].audioEndpointVolume.device);

    *apoNotificationDescriptorsReturned = apoNotificationDescriptors.release();
    *count = numDescriptors;
    return S_OK;
}

static bool IsSameEndpointId(IMMDevice* device1, IMMDevice* device2)
{
    bool isSameEndpointId = false;

    wil::unique_cotaskmem_string deviceId1;
    if (SUCCEEDED(device1->GetId(&deviceId1)))
    {
        wil::unique_cotaskmem_string deviceId2;
        if (SUCCEEDED(device2->GetId(&deviceId2)))
        {
            isSameEndpointId = (CompareStringOrdinal(deviceId1.get(), -1, deviceId2.get(), -1, TRUE) == CSTR_EQUAL);
        }
    }
    return isSameEndpointId;
}

// HandleNotification is called whenever there is a change that matches any of the
// APO_NOTIFICATION_DESCRIPTOR elements in the array that was returned by GetApoNotificationRegistrationInfo.
// Note that the APO will have to query each property once to get its initial value because this method is
// only invoked when any of the properties have changed.
STDMETHODIMP_(void) SampleApo::HandleNotification(_In_ APO_NOTIFICATION* apoNotification)
{
    // Check if a property in the user property store has changed.
    if (apoNotification->type == APO_NOTIFICATION_TYPE_AUDIO_SYSTEM_EFFECTS_PROPERTY_CHANGE
        && IsSameEndpointId(apoNotification->audioSystemEffectsPropertyChange.endpoint, m_device.get())
        && apoNotification->audioSystemEffectsPropertyChange.propertyStoreContext == m_propertyStoreContext
        && apoNotification->audioSystemEffectsPropertyChange.propertyStoreType == AUDIO_SYSTEMEFFECTS_PROPERTYSTORE_TYPE_USER)
    {
        // Check if one of the properties that we are interested in has changed.
        // As an example, we check for "PKEY_Endpoint_Enable_Channel_Swap_SFX" which is a fictitious
        // PROPERTYKEY that could be set on our user property store.
        if (apoNotification->audioSystemEffectsPropertyChange.propertyKey ==
            PKEY_Endpoint_Enable_Channel_Swap_SFX)
        {
            wil::unique_prop_variant var;
            if (SUCCEEDED(apoNotification->audioSystemEffectsPropertyChange.propertyStore->GetValue(
                    PKEY_Endpoint_Enable_Channel_Swap_SFX, &var)) &&
                var.vt != VT_EMPTY)
            {
                // We have retrieved the property value. Now we can do something interesting with it.
            }
        }
    }
    else if (apoNotification->type == APO_NOTIFICATION_TYPE_ENDPOINT_PROPERTY_CHANGE
        
        && IsSameEndpointId(apoNotification->audioEndpointPropertyChange.endpoint, m_device.get())
    {
        // Handle changes to PROPERTYKEYs in the audio endpoint's own property store.
        // In this example, we are interested in a property called "PKEY_Endpoint_AllowOffloading" that the
        // user might change in the audio control panel, and we update our member variable if this
        // property changes.
        if (apoNotification->audioEndpointPropertyChange.propertyKey == PKEY_Endpoint_AllowOffloading)
        {
            wil::unique_prop_variant var;
            if (SUCCEEDED(propertyStore->GetValue(PKEY_Endpoint_AllowOffloading, &var)) && var.vt == VT_BOOL)
            {
                m_allowOffloading = var.boolVal;
            }
        }    
    }
    else if (apoNotification->type == APO_NOTIFICATION_TYPE_ENDPOINT_VOLUME
        
        && IsSameEndpointId(apoNotification->audioEndpointVolumeChange.endpoint, m_device.get())
    {
        // Handle endpoint volume change
        m_masterVolume = apoNotification->audioEndpointVolumeChange.volume->fMasterVolume;
        m_isMuted = apoNotification->audioEndpointVolumeChange.volume->bMuted;
    }
}

Kode berikut berasal dari sampel Swap APO MFX - swapapomfx.cpp dan menunjukkan pendaftaran untuk peristiwa, dengan mengembalikan array APO_NOTIFICATION_DESCRIPTORs.

HRESULT CSwapAPOMFX::GetApoNotificationRegistrationInfo(_Out_writes_(*count) APO_NOTIFICATION_DESCRIPTOR **apoNotifications, _Out_ DWORD *count)
{
    *apoNotifications = nullptr;
    *count = 0;

    RETURN_HR_IF_NULL(E_FAIL, m_device);

    // Let the OS know what notifications we are interested in by returning an array of
    // APO_NOTIFICATION_DESCRIPTORs.
    constexpr DWORD numDescriptors = 1;
    wil::unique_cotaskmem_ptr<APO_NOTIFICATION_DESCRIPTOR[]> apoNotificationDescriptors;

    apoNotificationDescriptors.reset(static_cast<APO_NOTIFICATION_DESCRIPTOR*>(
        CoTaskMemAlloc(sizeof(APO_NOTIFICATION_DESCRIPTOR) * numDescriptors)));
    RETURN_IF_NULL_ALLOC(apoNotificationDescriptors);

    // Our APO wants to get notified when an endpoint property changes on the audio endpoint.
    apoNotificationDescriptors[0].type = APO_NOTIFICATION_TYPE_ENDPOINT_PROPERTY_CHANGE;
    (void)m_device.query_to(&apoNotificationDescriptors[0].audioEndpointPropertyChange.device);

    *apoNotifications = apoNotificationDescriptors.release();
    *count = numDescriptors;

    return S_OK;
}

Kode berikut berasal dari sampel HandleNotifications SwapAPO MFX - swapapomfx.cpp dan menunjukkan cara menangani pemberitahuan.

void CSwapAPOMFX::HandleNotification(APO_NOTIFICATION *apoNotification)
{
    if (apoNotification->type == APO_NOTIFICATION_TYPE_ENDPOINT_PROPERTY_CHANGE)
    {
        // If either the master disable or our APO's enable properties changed...
        if (PK_EQUAL(apoNotification->audioEndpointPropertyChange.propertyKey, PKEY_Endpoint_Enable_Channel_Swap_MFX) ||
            PK_EQUAL(apoNotification->audioEndpointPropertyChange.propertyKey, PKEY_AudioEndpoint_Disable_SysFx))
        {
            struct KeyControl
            {
                PROPERTYKEY key;
                LONG* value;
            };

            KeyControl controls[] = {
                {PKEY_Endpoint_Enable_Channel_Swap_MFX, &m_fEnableSwapMFX},
            };

            m_apoLoggingService->ApoLog(APO_LOG_LEVEL_INFO, L"HandleNotification - pkey: " GUID_FORMAT_STRING L" %d", GUID_FORMAT_ARGS(apoNotification->audioEndpointPropertyChange.propertyKey.fmtid), apoNotification->audioEndpointPropertyChange.propertyKey.pid);

            for (int i = 0; i < ARRAYSIZE(controls); i++)
            {
                LONG fNewValue = true;

                // Get the state of whether channel swap MFX is enabled or not
                fNewValue = GetCurrentEffectsSetting(m_userStore.get(), controls[i].key, m_AudioProcessingMode);

                SetAudioSystemEffectState(m_effectInfos[i].id, fNewValue ? AUDIO_SYSTEMEFFECT_STATE_ON : AUDIO_SYSTEMEFFECT_STATE_OFF);
            }
        }
    }
}

Kerangka Kerja Pengelogan

Kerangka kerja Pengelogan memberi pengembang APO cara tambahan untuk mengumpulkan data untuk meningkatkan pengembangan dan penelusuran kesalahan. Kerangka kerja ini menyatukan berbagai metode pengelogan yang digunakan oleh vendor yang berbeda dan mengikatnya dengan penyedia pengelogan jejak audio untuk membuat pengelogan yang lebih bermakna. Kerangka kerja baru menyediakan API pengelogan, meninggalkan sisa pekerjaan yang akan dilakukan oleh OS.

Penyedia didefinisikan sebagai:

IMPLEMENT_TRACELOGGING_CLASS(ApoTelemetryProvider, "Microsoft.Windows.Audio.ApoTrace",
    // {8b4a0b51-5dcf-5a9c-2817-95d0ec876a87}
    (0x8b4a0b51, 0x5dcf, 0x5a9c, 0x28, 0x17, 0x95, 0xd0, 0xec, 0x87, 0x6a, 0x87));

Setiap APO memiliki ID aktivitasnya sendiri. Karena ini menggunakan mekanisme pencatatan jejak yang ada, alat konsol yang ada dapat digunakan untuk memfilter peristiwa ini dan menampilkannya secara real time. Anda dapat menggunakan alat yang ada seperti tracelog dan tracefmt seperti yang dijelaskan dalam Alat untuk Pelacakan Perangkat Lunak - driver Windows. Untuk informasi selengkapnya tentang sesi pelacakan, lihat Membuat Sesi Pelacakan dengan GUID Kontrol.

Peristiwa pencatatan jejak tidak ditandai sebagai telemetri dan tidak akan ditampilkan sebagai penyedia telemetri dalam alat seperti xperf.

Implementasi - Kerangka Kerja Pengelogan

Kerangka kerja pengelogan didasarkan pada mekanisme pengelogan yang disediakan oleh pelacakan ETW. Untuk informasi selengkapnya tentang ETW, lihat Pelacakan Peristiwa. Ini tidak dimaksudkan untuk mencatat data audio, melainkan untuk mencatat peristiwa yang biasanya dicatat dalam produksi. API pengelogan tidak boleh digunakan dari utas streaming real-time karena ini berpotensi menyebabkan utas pompa dikosongkan sebelumnya oleh penjadwal CPU OS. Pengelogan terutama harus digunakan untuk peristiwa yang akan membantu masalah penelusuran kesalahan yang sering ditemukan di bidang .

Definisi API - Kerangka Kerja Pengelogan

Kerangka kerja Pengelogan memperkenalkan antarmuka IAudioProcessingObjectLoggingService yang menyediakan layanan pengelogan baru untuk API.

Untuk informasi selengkapnya, lihat IAudioProcessingObjectLoggingService.

Kode sampel - Kerangka Kerja Pengelogan

Sampel menunjukkan penggunaan metode IAudioProcessingObjectLoggingService::ApoLog dan bagaimana penunjuk antarmuka ini diperoleh di IAudioProcessingObject::Initialize.

Contoh Pengelogan AecApoMfx.

class SampleApo : public winrt::implements<SampleApo, IAudioProcessingObject,
    IAudioSystemEffects, IAudioSystemEffects2, IAudioSystemEffects3>
{
private:
    wil::com_ptr_nothrow<IAudioProcessingObjectLoggingService> m_apoLoggingService;

public:
    // IAudioProcessingObject
    STDMETHOD(Initialize)(UINT32 cbDataSize, BYTE* pbyData);

    // Implementation of IAudioProcessingObject, IAudioSystemEffects2 andIAudioSystemEffects3 has been omitted for brevity.
};

// Implementation of IAudioProcessingObject::Initialize
STDMETHODIMP SampleApo::Initialize(UINT32 cbDataSize, BYTE* pbyData)
{
    if (cbDataSize == sizeof(APOInitSystemEffects3))
    {
        APOInitSystemEffects3* apoInitSystemEffects3 = reinterpret_cast<APOInitSystemEffects3*>(pbyData);

        // Try to get the logging service, but ignore errors as failure to do logging it is not fatal.
        (void)apoInitSystemEffects3->pServiceProvider->QueryService(SID_AudioProcessingObjectLoggingService, 
            __uuidof(IAudioProcessingObjectLoggingService), IID_PPV_ARGS(&m_apoLoggingService));
    }

    // Do other APO initialization work

    if (m_apoLoggingService != nullptr)
    {
        m_apoLoggingService->ApoLog(APO_LOG_LEVEL_INFO, L"APO Initialization completed");
    }
    return S_OK;
}

Kerangka Kerja Utas

Kerangka kerja utas yang memungkinkan efek untuk di-multithread dengan menggunakan antrean kerja dari tugas Multimedia Class Scheduler Service (MMCSS) yang sesuai melalui API sederhana. Pembuatan antrean kerja serial real time dan asosiasinya dengan utas pompa utama ditangani oleh OS. Kerangka kerja ini memungkinkan API untuk mengantre item kerja yang berjalan pendek. Sinkronisasi antar tugas terus menjadi tanggung jawab APO. Untuk informasi selengkapnya tentang utas MMCSS, lihat Layanan Penjadwal Kelas Multimedia dan API Antrean Kerja Real Time.

Definisi API - Kerangka Kerja Utas

Kerangka kerja Threading memperkenalkan antarmuka IAudioProcessingObjectQueueService yang menyediakan akses ke antrean kerja real time untuk API.

Untuk informasi selengkapnya, temukan konten tambahan di halaman berikut:

Kode sampel - Kerangka Kerja Utas

Sampel ini menunjukkan penggunaan metode IAudioProcessingObjectRTQueueService::GetRealTimeWorkQueue dan bagaimana penunjuk antarmuka IAudioProcessingObjectRTQueueService diperoleh di IAudioProcessingObject::Initialize.

#include <rtworkq.h>

class SampleApo3 :
    public winrt::implements<SampleApo3, IAudioProcessingObject, IAudioProcessingObjectConfiguration,
        IAudioSystemEffects, IAudioSystemEffects2, IAudioSystemEffects3>
{
private:
    DWORD m_queueId = 0;
    wil::com_ptr_nothrow<SampleApo3AsyncCallback> m_asyncCallback;

public:
    // IAudioProcessingObject
    STDMETHOD(Initialize)(UINT32 cbDataSize, BYTE* pbyData);

    // IAudioProcessingObjectConfiguration
    STDMETHOD(LockForProcess)(
        _In_ UINT32 u32NumInputConnections,
        _In_reads_(u32NumInputConnections) APO_CONNECTION_DESCRIPTOR** ppInputConnections,
        _In_ UINT32 u32NumOutputConnections,
        _In_reads_(u32NumOutputConnections) APO_CONNECTION_DESCRIPTOR** ppOutputConnections);

    // Non-interface methods called by the SampleApo3AsyncCallback helper class.
    HRESULT DoWorkOnRealTimeThread()
    {
        // Do the actual work here
        return S_OK;
    }
    void HandleWorkItemCompleted(_In_ IRtwqAsyncResult* asyncResult);

    // Implementation of IAudioProcessingObject, IAudioSystemEffects2, IAudioSystemEffects3   and IAudioProcessingObjectConfiguration is omitted
    // for brevity.
};

// Implementation of IAudioProcessingObject::Initialize
STDMETHODIMP SampleApo3::Initialize(UINT32 cbDataSize, BYTE* pbyData)
{
    if (cbDataSize == sizeof(APOInitSystemEffects3))
    {
        APOInitSystemEffects3* apoInitSystemEffects3 = reinterpret_cast<APOInitSystemEffects3*>(pbyData);

        wil::com_ptr_nothrow<IAudioProcessingObjectRTQueueService> apoRtQueueService;
        RETURN_IF_FAILED(apoInitSystemEffects3->pServiceProvider->QueryService(
            SID_AudioProcessingObjectRTQueue, IID_PPV_ARGS(&apoRtQueueService)));

        // Call the GetRealTimeWorkQueue to get the ID of a work queue that can be used for scheduling tasks
        // that need to run at a real-time priority. The work queue ID is used with the Rtwq APIs.
        RETURN_IF_FAILED(apoRtQueueService->GetRealTimeWorkQueue(&m_queueId));
    }

    // Do other initialization here
    return S_OK;
}

STDMETHODIMP SampleApo3::LockForProcess(
    _In_ UINT32 u32NumInputConnections,
    _In_reads_(u32NumInputConnections) APO_CONNECTION_DESCRIPTOR** ppInputConnections,
    _In_ UINT32 u32NumOutputConnections,
    _In_reads_(u32NumOutputConnections) APO_CONNECTION_DESCRIPTOR** ppOutputConnections)
{
    // Implementation details of LockForProcess omitted for brevity
    m_asyncCallback = winrt::make<SampleApo3AsyncCallback>(m_queueId).get();
    RETURN_IF_NULL_ALLOC(m_asyncCallback);

    wil::com_ptr_nothrow<IRtwqAsyncResult> asyncResult;	
    RETURN_IF_FAILED(RtwqCreateAsyncResult(this, m_asyncCallback.get(), nullptr, &asyncResult));

    RETURN_IF_FAILED(RtwqPutWorkItem(m_queueId, 0, asyncResult.get())); 
    return S_OK;
}

void SampleApo3::HandleWorkItemCompleted(_In_ IRtwqAsyncResult* asyncResult)
{
    // check the status of the result
    if (FAILED(asyncResult->GetStatus()))
    {
        // Handle failure
    }

    // Here the app could call RtwqPutWorkItem again with m_queueId if it has more work that needs to
    // execute on a real-time thread.
}


class SampleApo3AsyncCallback :
    public winrt::implements<SampleApo3AsyncCallback, IRtwqAsyncCallback>
{
private:
    DWORD m_queueId;

public:
    SampleApo3AsyncCallback(DWORD queueId) : m_queueId(queueId) {}

    // IRtwqAsyncCallback
    STDMETHOD(GetParameters)(_Out_ DWORD* pdwFlags, _Out_ DWORD* pdwQueue)
    {
        *pdwFlags = 0;
        *pdwQueue = m_queueId;
        return S_OK;
    }
    STDMETHOD(Invoke)(_In_ IRtwqAsyncResult* asyncResult);
};


STDMETHODIMP SampleApo3AsyncCallback::Invoke(_In_ IRtwqAsyncResult* asyncResult)
{
    // We are now executing on the real-time thread. Invoke the APO and let it execute the work.
    wil::com_ptr_nothrow<IUnknown> objectUnknown;
    RETURN_IF_FAILED(asyncResult->GetObject(objectUnknown.put_unknown()));

    wil::com_ptr_nothrow<SampleApo3> sampleApo3 = static_cast<SampleApo3*>(objectUnknown.get());
    HRESULT hr = sampleApo3->DoWorkOnRealTimeThread();
    RETURN_IF_FAILED(asyncResult->SetStatus(hr));

    sampleApo3->HandleWorkItemCompleted(asyncResult);
    return S_OK;
}

Untuk contoh selengkapnya tentang cara menggunakan antarmuka ini, silakan lihat kode sampel berikut:

Penemuan dan Kontrol Efek Audio untuk Efek

Kerangka kerja penemuan memungkinkan OS untuk mengontrol efek audio pada alirannya. API ini memberikan dukungan untuk skenario di mana pengguna aplikasi perlu mengontrol efek tertentu pada aliran (misalnya, penekanan kebisingan yang dalam). Untuk mencapai hal ini, kerangka kerja ini menambahkan hal berikut:

  • API baru untuk dikueri dari APO untuk menentukan apakah efek audio dapat diaktifkan atau dinonaktifkan.
  • API baru untuk mengatur status efek audio ke aktif/nonaktif.
  • Pemberitahuan ketika ada perubahan dalam daftar efek audio atau ketika sumber daya tersedia sehingga efek audio sekarang dapat diaktifkan/dinonaktifkan.

Implementasi - Penemuan Efek Audio

APO perlu mengimplementasikan antarmuka IAudioSystemEffects3 jika ingin mengekspos efek yang dapat diaktifkan dan dinonaktifkan secara dinamis. APO mengekspos efek audionya melalui fungsi IAudioSystemEffects3::GetControllableSystemEffectsList dan mengaktifkan dan menonaktifkan efek audionya melalui fungsi IAudioSystemEffects3::SetAudioSystemEffectState .

Kode sampel - Penemuan Efek Audio

Kode sampel Penemuan Efek Audio dapat ditemukan dalam sampel SwapAPOSFX - swapaposfx.cpp.

Kode sampel berikut mengilustrasikan cara mengambil daftar efek yang dapat dikonfigurasi. Sampel GetControllableSystemEffectsList - swapaposfx.cpp

HRESULT CSwapAPOSFX::GetControllableSystemEffectsList(_Outptr_result_buffer_maybenull_(*numEffects) AUDIO_SYSTEMEFFECT** effects, _Out_ UINT* numEffects, _In_opt_ HANDLE event)
{
    RETURN_HR_IF_NULL(E_POINTER, effects);
    RETURN_HR_IF_NULL(E_POINTER, numEffects);

    *effects = nullptr;
    *numEffects = 0;

    // Always close existing effects change event handle
    if (m_hEffectsChangedEvent != NULL)
    {
        CloseHandle(m_hEffectsChangedEvent);
        m_hEffectsChangedEvent = NULL;
    }

    // If an event handle was specified, save it here (duplicated to control lifetime)
    if (event != NULL)
    {
        if (!DuplicateHandle(GetCurrentProcess(), event, GetCurrentProcess(), &m_hEffectsChangedEvent, EVENT_MODIFY_STATE, FALSE, 0))
        {
            RETURN_IF_FAILED(HRESULT_FROM_WIN32(GetLastError()));
        }
    }

    if (!IsEqualGUID(m_AudioProcessingMode, AUDIO_SIGNALPROCESSINGMODE_RAW))
    {
        wil::unique_cotaskmem_array_ptr<AUDIO_SYSTEMEFFECT> audioEffects(
            static_cast<AUDIO_SYSTEMEFFECT*>(CoTaskMemAlloc(NUM_OF_EFFECTS * sizeof(AUDIO_SYSTEMEFFECT))), NUM_OF_EFFECTS);
        RETURN_IF_NULL_ALLOC(audioEffects.get());

        for (UINT i = 0; i < NUM_OF_EFFECTS; i++)
        {
            audioEffects[i].id = m_effectInfos[i].id;
            audioEffects[i].state = m_effectInfos[i].state;
            audioEffects[i].canSetState = m_effectInfos[i].canSetState;
        }

        *numEffects = (UINT)audioEffects.size();
        *effects = audioEffects.release();
    }

    return S_OK;
}

Contoh kode berikut mengilustrasikan cara mengaktifkan dan menonaktifkan efek. Sampel SetAudioSystemEffectState - swapaposfx.cpp

HRESULT CSwapAPOSFX::SetAudioSystemEffectState(GUID effectId, AUDIO_SYSTEMEFFECT_STATE state)
{
    for (auto effectInfo : m_effectInfos)
    {
        if (effectId == effectInfo.id)
        {
            AUDIO_SYSTEMEFFECT_STATE oldState = effectInfo.state;
            effectInfo.state = state;

            // Synchronize access to the effects list and effects changed event
            m_EffectsLock.Enter();

            // If anything changed and a change event handle exists
            if (oldState != effectInfo.state)
            {
                SetEvent(m_hEffectsChangedEvent);
                m_apoLoggingService->ApoLog(APO_LOG_LEVEL_INFO, L"SetAudioSystemEffectState - effect: " GUID_FORMAT_STRING L", state: %i", effectInfo.id, effectInfo.state);
            }

            m_EffectsLock.Leave();
            
            return S_OK;
        }
    }

    return E_NOTFOUND;
}

Penggunaan kembali WM SFX dan MFX API di Windows 11, versi 22H2

Dimulai dengan Windows 11, versi 22H2, file konfigurasi INF yang menggunakan kembali API WM SFX dan MFX kotak masuk, sekarang dapat menggunakan kembali CAPX SFX dan MFX API. Bagian ini menjelaskan tiga cara untuk melakukan ini.

Ada tiga titik penyisipan untuk API: render pra-campuran, render pasca-campuran, dan pengambilan. Setiap mesin audio perangkat logis mendukung satu instans APO render pra-campuran per aliran (render SFX) dan satu APO render pasca-campuran (MFX). Mesin audio juga mendukung satu instans capture APO (capture SFX) yang disisipkan di setiap aliran pengambilan. Untuk informasi selengkapnya tentang cara menggunakan kembali atau membungkus API kotak masuk, lihat Menggabungkan API kustom dan Windows.

CAPX SFX dan MFX API dapat digunakan kembali dengan salah satu dari tiga cara berikut.

Menggunakan bagian INF DDInstall

Gunakan mssysfx. CopyFilesAndRegisterCapX dari wdmaudio.inf dengan menambahkan entri berikut.

   Include=wdmaudio.inf
   Needs=mssysfx.CopyFilesAndRegisterCapX

Menggunakan file INF ekstensi

wdmaudioapo.inf adalah inf ekstensi kelas AudioProcessingObject. Ini berisi pendaftaran khusus perangkat dari API SFX dan MFX.

Secara langsung mereferensikan WM SFX dan MFX API untuk efek streaming dan mode

Untuk mereferensikan API ini secara langsung untuk efek streaming dan mode, gunakan nilai GUID berikut.

  • Gunakan {C9453E73-8C5C-4463-9984-AF8BAB2F5447} sebagai WM SFX APO
  • Gunakan {13AB3EBD-137E-4903-9D89-60BE8277FD17} sebagai WM MFX APO.

SFX (Stream) dan MFX (Mode) disebut dalam Windows 8.1 ke LFX (lokal) dan MFX disebut sebagai GFX (global). Entri registri ini terus menggunakan nama sebelumnya.

Pendaftaran khusus perangkat menggunakan HKR alih-alih HKCR.

File INF harus menambahkan entri berikut.

  HKR,"FX\\0\\%WMALFXGFXAPO_Context%",%PKEY_FX_Association%,,%KSNODETYPE_ANY%
  HKR,"FX\\0\\%WMALFXGFXAPO_Context%\\User",,,
  WMALFXGFXAPO_Context = "{B13412EE-07AF-4C57-B08B-E327F8DB085B}"

Entri file INF ini akan membuat penyimpanan properti yang akan digunakan oleh API Windows 11 untuk API baru.

PKEY_FX_Association di inf ex. HKR,"FX\\0",%PKEY_FX_Association%,,%KSNODETYPE_ANY%, harus diganti dengan HKR,"FX\\0\\%WMALFXGFXAPO_Context%",%PKEY_FX_Association%,,%KSNODETYPE_ANY%.

Lihat juga

Objek Pemrosesan Audio Windows.