Ses İşleme Nesneleri için Windows 11 API'leri

Bu konu, ses sürücüsüyle birlikte gönderilen Ses İşleme Nesneleri (API' ler) için bir dizi yeni Windows 11 API'sini tanıtır.

Windows, üçüncü taraf ses donanımı üreticilerinin özel konak tabanlı dijital sinyal işleme efektleri eklemesine olanak tanır. Bu efektler kullanıcı modu Ses İşleme Nesneleri (API) olarak paketlenir. Daha fazla bilgi için bkz. Windows Ses İşleme Nesneleri.

Burada açıklanan API'lerden bazıları Bağımsız Donanım Satıcıları (IHV) ve Bağımsız Yazılım Satıcıları (ISV) için yeni senaryolara olanak tanırken, diğer API'lerin genel ses güvenilirliğini ve hata ayıklama özelliklerini geliştiren alternatifler sağlaması amaçlanmıştır.

  • Akustik Yankı İptali (AEC) çerçevesi, APO'ların kendisini AEC APO olarak tanımlamasına olanak tanıyarak bir başvuru akışına ve ek denetimlere erişim sağlar.
  • Ayarlar çerçevesi, APO'ların bir ses uç noktasında ses efektleri ("FX özellik deposu") için özellik deposunu sorgulama ve değiştirme yöntemlerini kullanıma sunmasına olanak sağlar. Bu yöntemler bir APO tarafından uygulandığında, bu APO ile ilişkili Donanım Destek Uygulamaları (HSA) tarafından çağrılabilir.
  • Bildirimler çerçevesi ses efektlerinin (API' ler) birim, uç nokta ve ses efektleri özellik deposu değişikliklerini işlemek için bildirim istemesine olanak tanır.
  • Günlükleme çerçevesi, APO'ların geliştirilmesine ve hata ayıklamasına yardımcı olur.
  • İş Parçacığı Oluşturma çerçevesi, APO'ların işletim sistemi tarafından yönetilen ve MMCSS tarafından kaydedilen bir iş parçacığı havuzu kullanılarak çok iş parçacıklı olmasını sağlar.
  • Ses Efektleri Bulma ve Denetim API'leri, işletim sisteminin bir akışta işlenebilecek efektleri algılamasına, etkinleştirmesine ve devre dışı bırakmasına olanak tanır.

Bu yeni API'lerden yararlanmak için, APO'ların yeni IAudioSystemEffects3 arabirimini kullanmaları beklenir. Bir APO bu arabirimi uyguladığında, işletim sistemi bunu APO Ayarları çerçevesini desteklediğine dair örtük bir sinyal olarak yorumlar ve APO'ya ses altyapısından gelen yaygın sesle ilgili bildirimler için abone olmasına izin verir.

Windows 11 APO CAPX geliştirme gereksinimleri

Windows 11 için bir cihaza yüklenen tüm yeni APO'ların, bu konuda listelenmiş olan ve HLK aracılığıyla doğrulanan API'lerle uyumlu olması gerekir. Ayrıca, AEC'den yararlanan tüm APO'ların bu başlıkta özetlenen ve HLK aracılığıyla doğrulanan uygulamayı takip etmesi beklendik. Bu çekirdek ses işleme uzantıları (Ayarlar, Günlüğe Kaydetme, Bildirimler, İş Parçacığı Yönetimi, AEC) için özel uygulamaların CAPX API'leri kullanılarak geliştirilmesi beklenir. Bu, Windows 11 HLK testleri aracılığıyla doğrulanır. Örneğin, bir APO Ayarlar Çerçevesi yerine ayarları kaydetmek için kayıt defteri verilerini kullanıyorsa, ilişkili HLK testi başarısız olur.

Windows sürüm gereksinimleri

Bu konuda açıklanan API'ler Windows 11 işletim sistemi, WDK ve SDK'nın 22000 derlemesinde kullanıma sunulmuştur. Windows 10 bu API'ler için destek almayacaktır. APO, hem Windows 10 hem de Windows 11 üzerinde çalışmak istiyorsa, CAPX API'lerini destekleyen bir işletim sisteminde çalışıp çalışmadığını belirlemek için APOInitSystemEffects2 veya APOInitSystemEffects3 yapısıyla başlatılıp başlatılmadığını inceleyebilir.

Windows'un, WDK'nin ve SDK'nın en son sürümleri Windows Insider Programı aracılığıyla aşağıdan indirilebilir. İş Ortağı Merkezi aracılığıyla Microsoft ile etkileşime geçen iş ortakları da Collaborate aracılığıyla bu içeriğe erişebilir. collaborate hakkında daha fazla bilgi için bkz. Microsoft Collaborate'e Giriş.

Windows 11 WHCP İçeriği, iş ortaklarına bu API'leri doğrulama araçları sağlayacak şekilde güncelleştirildi.

Bu konuda özetlenen içeriğin örnek koduna şuradan ulaşabilirsiniz: Ses/SYSVAD/APO - github

Akustik Yankı İptali (AEC)

Akustik Yankı İptali (AEC), Bağımsız Donanım Satıcıları (IHV'ler) ve Bağımsız Yazılım Satıcıları (ISV) tarafından mikrofon yakalama işlem hattında Ses İşleme Nesnesi (APO) olarak uygulanan yaygın bir ses efektidir. Bu etki, genellikle IHD'ler ve ISV'ler tarafından uygulanan diğer efektlerden farklıdır ve 2 giriş gerektirir: mikrofondan bir ses akışı ve başvuru sinyali işlevi gören bir işleme cihazından ses akışı.

Bu yeni arabirim kümesi, bir AEC APO'sunun kendisini ses altyapısına göre tanımlamasına olanak tanır. Bunun yapılması, ses altyapısının APO'ya birden çok giriş ve tek bir çıkışla uygun şekilde yapılandırılmasını sağlar.

Yeni AEC arabirimleri bir APO tarafından uygulandığında ses altyapısı şunları yapacaktır:

  • APO'yu uygun bir işleme uç noktasından APO'ya başvuru akışı sağlayan ek bir girişle yapılandırın.
  • Render cihazı değiştiğinde referans akışlarını değiştirin.
  • Bir APO'nun giriş mikrofonunun ve başvuru akışının biçimini denetlemesine izin verin.
  • Bir APO'nun mikrofon ve referans akışlarında zaman damgalarını elde etmesine izin verin.

Önceki yaklaşım - Windows 10

API'ler tek giriş - tek çıkış nesneleridir. Ses altyapısı, girişinde mikrofonun uç noktasından gelen sesi bir AEC APO'ya iletir. Bir APO, başvuru akışını elde etmek için işleme uç noktasından başvuru sesini almak için özel arabirimler kullanarak sürücüyle etkileşimde bulunabilir veya işleme uç noktasında bir geri döngü akışı açmak için WASAPI kullanabilir.

Yukarıdaki yaklaşımların her ikisinin de dezavantajları vardır:

  • Sürücüden bir referans akışı almak için özel kanallar kullanan bir AEC APO, genellikle bunu yalnızca tümleşik ses oynatma cihazından yapabilir. Sonuç olarak, kullanıcı USB veya Bluetooth ses cihazı gibi tümleşik olmayan cihazdan ses çalıyorsa yankı iptali çalışmaz. Yalnızca işletim sistemi, başvuru uç noktaları olarak görev yapabilecek doğru işleme uç noktalarının farkındadır.

  • APO, yankı iptali gerçekleştirmek üzere varsayılan işleme uç noktasını seçmek için WASAPI kullanabilir. Ancak, audiodg.exe işleminden (APO'nun barındırıldığı yer) bir geri döngü akışı açarken dikkat edilmesi gereken bazı tuzaklar vardır.

    • Ses altyapısı ana APO yöntemlerini çağırdığında geri döngü akışı açılamaz/yok edilemez, bu da kilitlenmeye neden olabilir.
    • Yakalama APO'su, istemcilerinin akışlarının durumunu bilmez. Örneğin, bir yakalama uygulaması 'STOP' durumunda bir yakalama akışına sahip olabilir, ancak APO bu durumun farkında değildir ve bu nedenle geri döngü akışını güç tüketimi açısından verimsiz olan 'RUN' durumunda açık tutar.

API tanımı - AEC

AEC çerçevesi, API'lerin yararlanabileceği yeni yapılar ve arabirimler sağlar. Bu yeni yapılar ve arabirimler aşağıda açıklanmıştır.

APO_CONNECTION_PROPERTY_V2 yapısı

IApoAcousticEchoCancellation arabirimini uygulayan API'ler, IAudioProcessingObjectRT::APOProcess çağrısında bir APO_CONNECTION_PROPERTY_V2 yapısı geçirilir. APO_CONNECTION_PROPERTY yapısındaki tüm alanlara ek olarak, yapının 2. sürümü de ses arabellekleri için zaman damgası bilgileri sağlar.

APO, ses altyapısından aldığı yapının APO_CONNECTION_PROPERTY veya APO_CONNECTION_PROPERTY_V2 türünde olup olmadığını belirlemek için APO_CONNECTION_PROPERTY.u32Signature alanını inceleyebilir. APO_CONNECTION_PROPERTY yapıların imzası APO_CONNECTION_PROPERTY_SIGNATURE olduğu gibi, APO_CONNECTION_PROPERTY_V2 yapıların imzası APO_CONNECTION_PROPERTY_V2_SIGNATURE eşittir. İmzanın APO_CONNECTION_PROPERTY_V2_SIGNATURE eşit bir değeri varsa, APO_CONNECTION_PROPERTY yapısının işaretçisi güvenli bir şekilde APO_CONNECTION_PROPERTY_V2 işaretçisine yazılabilir.

Aşağıdaki kod, Aec APO MFX örneği - AecApoMfx.cpp içinden alınmış olup yeniden dönüştürmeyi gösterir.

    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

IApoAcousticEchoCancellation arabiriminde açık bir yöntem yoktur. Amacı, ses altyapısına bir AEC APO'sunu tanımlamaktır. Bu arabirim yalnızca yakalama uç noktalarındaki mod efektleri (MFX) tarafından uygulanabilir. Bu arabirimin başka bir APO'ya uygulanması, bu APO'ya yüklemede hataya neden olur. MFX hakkında genel bilgi için bkz. Ses İşleme Nesneleri Mimarisi.

Yakalama uç noktası üzerindeki mod etkisi bir dizi zincirlenmiş API olarak uygulanırsa, bu arabirimi yalnızca cihaza en yakın APO uygulayabilir. Bu arabirimi uygulayan API'lere IAudioProcessingobjectRT::APOProcess çağrısında APO_CONNECTION_PROPERTY_V2 yapısı sunulur. APO, bağlantı özelliğinde APO_CONNECTION_PROPERTY_V2_SIGNATURE imzası olup olmadığını denetleyebilir ve gelen APO_CONNECTION_PROPERTY yapısını APO_CONNECTION_PROPERTY_V2 yapısına dönüştürebilir.

AEC API'lerinin algoritmalarını genellikle belirli bir örnekleme hızında/kanal sayısında çalıştırması nedeniyle ses altyapısı, IApoAcousticEchoCancellation arabirimini uygulayan API'lere yeniden örnekleme desteği sağlar.

AEC APO, IAudioProcessingObject::OutInputFormatSupported çağrısında APOERR_FORMAT_NOT_SUPPORTED döndürdüğünde, ses motoru APO'nun önerilen biçimini elde etmek için APO'da NULL çıkış biçimi ve NULL olmayan giriş biçimiyle IAudioProcessingObject::IsInputFormatSupported'ı yeniden çağırarak çalışır. Ardından ses altyapısı, mikrofon sesini AEC APO'ya göndermeden önce önerilen biçimde yeniden örnekleme yapacaktır. Bu, AEC APO'nun örnekleme oranını ve kanal sayısını dönüştürme gereğini ortadan kaldırır.

IApoAuxiliaryInputConfiguration

IApoAuxiliaryInputConfiguration arabirimi, ses motorunun yardımcı giriş akışları ekleyip kaldırabilmesi için APO'ların uygulayabileceği yöntemler sağlar.

Bu arabirim AEC APO tarafından uygulanır ve ses motoru tarafından referans girişini başlatmak için kullanılır. Windows 11'de, AEC APO yalnızca yankı iptali için başvuru ses akışını içeren bir yardımcı girişle başlatılacak. AddAuxiliaryInput yöntemi, APO'ya başvuru girişi eklemek için kullanılır. Başlatma parametreleri, geri döngü akışının alındığı işleme uç noktasına bir başvuru içerir.

IsInputFormatSupported yöntemi, yardımcı girişte biçimler üzerinde anlaşmak için ses altyapısı tarafından çağrılır. AEC APO belirli bir biçimi tercih ederse, IsInputFormatSupported çağrısında S_FALSE döndürebilir ve önerilen bir biçim belirtebilir. Ses motoru, referans sesini önerilen biçime göre yeniden örnekleyecek ve AEC APO'nun yardımcı girişinde sağlayacak.

IApoAuxiliaryInputRT

IApoAuxiliaryInputRT arabirimi, bir APO'nun yardımcı girişlerini yönlendirmek için kullanılan gerçek zamanlı güvenli arabirimdir.

Bu arabirim, APO'ya yardımcı girişte ses verileri sağlamak için kullanılır. Yardımcı ses girişlerinin IAudioProcessingObjectRT::APOProcess çağrılarıyla eşitlenmediğini unutmayın. İşlem sonu noktasında ses çıktısı olmadığında, yardımcı girişte geri döngü verileri kullanılamaz. Yani, IApoAuxiliaryInputRT::AcceptInput çağrısı yapılmaz.

AEC CAPX API'lerinin özeti

Daha fazla bilgi için aşağıdaki sayfalarda ek bilgi bulabilirsiniz.

Örnek kod - AEC

Aşağıdaki Sysvad Audio AecApo kod örneklerine bakın.

Aec APO örnek üst bilgisi olan AecAPO.h'dan alınan aşağıdaki kod, eklenen üç yeni genel yöntemi gösterir.

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

Aşağıdaki kod , Aec APO MFX örneğinden alınıp AecApoMfx.cpp ve APO'nun yalnızca bir yardımcı girişi işleyebildiği durumlarda AddAuxiliaryInput uygulamasını gösterir.

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;

Ayrıca CAecApoMFX::IsInputFormatSupported ve CAecApoMFX::AcceptInput implementasyonunu ile APO_CONNECTION_PROPERTY_V2 kullanımını gösteren örnek kodu da gözden geçirin.

İşlem sırası - AEC

Başlatmada:

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

Render cihazı değişikliğinde:

  1. IAudioProcessingObject::Initialize
  2. IApoYardımcıGirişYapılandırması::YardımcıGirişEkle
  3. IAudioProcessingObjectConfiguration::LockForProcess
  4. Varsayılan cihaz değişiklikleri
  5. IAudioProcessingObjectConfiguration::UnlockForProcess
  6. IApoAuxiliaryInputConfiguration::RemoveAuxiliaryInput
  7. IApoAuxiliaryInputConfiguration::AddAuxiliaryInput
  8. IAudioProcessingObjectConfiguration::LockForProcess

Bu, AEC için önerilen arabellek davranışıdır.

  • IApoAuxiliaryInputRT::AcceptInput çağrısında elde edilen arabellekler, ana iş parçacığını kilitlemeden döngüsel arabelleğe yazılmalıdır.
  • IAudioProcessingObjectRT::APOProcess çağrısında, döngüsel arabellekten en son ses paketini almak için başvuru akışı okunmalıdır ve bu paket, yankı iptal algoritması ile işlenmelidir.
  • Konuşmacı ve mikrofon verilerinin hizalanması için başvuru ve mikrofon verilerindeki zaman damgaları kullanılabilir.

Referans Geri Döngü Akışı

Varsayılan olarak, geri döngü akışı herhangi bir ses düzeyi veya sessize alınmadan önce ses akışına "dokunur" (dinler). Ses düzeyi uygulanmadan önce alınan geri döngü akışı, ses düzeyi öncesi geri döngü akışı olarak bilinir. Ses seviyesi öncesi geri döngü akışına sahip olmanın avantajlarından biri, geçerli ses ayarından bağımsız olarak net ve tekdüzen bir ses akışıdır.

Bazı AEC algoritmaları, ses seviyesinin işlenmesinden (sesi kapatmak dahil) sonra bağlanan bir geri döngü akışını almayı tercih edebilir. Bu yapılandırma, birim sonrası geri döngü olarak bilinir.

Windows AEC API'lerinin sonraki ana sürümünde desteklenen uç noktalarda birim sonrası geri döngü isteyebilir.

Sınırlamalar

Tüm işleme uç noktaları için kullanılabilen birim öncesi geri döngü akışlarından farklı olarak, birim sonrası geri döngü akışları tüm uç noktalarda kullanılamayabilir.

Birim Sonrası Geri Döngü İsteme

Ses seviyesi sonrası geri döngü kullanmak isteyen AEC APO'lar IApoAcousticEchoCancellation2 arabirimini uygulamalıdır.

AEC APO, IApoAcousticEchoCancellation2::GetDesiredReferenceStreamProperties uygulamasında Properties parametresi aracılığıyla APO_REFERENCE_STREAM_PROPERTIES_POST_VOLUME_LOOPBACK bayrağını döndürerek birim sonrası geri döngü isteğinde bulunabilir.

Kullanılmakta olan işleme uç noktasına bağlı olarak, ses sonrası geri döngü kullanılamayabilir. IApoAuxiliaryInputConfiguration::AddAuxiliaryInput yöntemi çağrıldığında ses seviyesi sonrası geri bağlantı kullanılıyorsa AEC APO'ya bildirim gönderilir. AcousticEchoCanceller_Reference_Input streamProperties alanı APO_REFERENCE_STREAM_PROPERTIES_POST_VOLUME_LOOPBACK içeriyorsa, ses seviyesi sonrası geri döngü kullanımdadır.

AEC APO örnek üst bilgisi olan AecAPO.h'den alınan aşağıdaki kod, eklenen üç yeni genel yöntemi gösterir.

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;

Aşağıdaki kod parçacığı AEC APO MFX örneğindendir - AecApoMfx.cpp ve GetDesiredReferenceStreamProperties uygulamasını ve AddAuxiliaryInput'un ilgili bölümünü gösterir.

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.

Ayarlar Çerçevesi

Ayarlar Çerçevesi, APO'ların bir ses uç noktasında ses efektleri ("FX Özellik Deposu") için özellik deposunu sorgulama ve değiştirme yöntemlerini kullanıma sunmasına olanak tanır. Bu çerçeve, ayarları bu APO'ya iletmek isteyen API'ler ve Donanım Destek Uygulamaları (HSA) tarafından kullanılabilir. HSA'lar Evrensel Windows Platformu (UWP) uygulamaları olabilir ve Ayarlar Çerçevesi'nde API'leri çağırmak için özel bir özellik gerektirir. HSA uygulamaları hakkında daha fazla bilgi için bkz. UWP cihaz uygulamaları.

FxProperty Store Yapısı

Yeni FxProperty deposunun üç alt deposu vardır: Default, User ve Volatile.

"Varsayılan" alt anahtarı özel efektler özellikleri içerir ve INF dosyasından doldurulur. Bu özellikler işletim sistemi yükseltmelerinde kalıcı olmaz. Örneğin, genellikle bir INF'de tanımlanan özellikler buraya sığar. Daha sonra bu öğeler INF dosyasından yeniden doldurulacaktır.

"User" alt anahtarı, efekt özellikleriyle ilgili kullanıcı ayarlarını içerir. Bu ayarlar, yükseltmeler ve geçişler arasında işletim sistemi tarafından kalıcı hale taşınır. Örneğin, kullanıcının yapılandırabileceği ve yükseltme boyunca kalıcı olması beklenen tüm ön ayarlar.

"Volatile" alt anahtarı geçici efektler özellikleri içerir. Cihaz yeniden başlatıldığında bu özellikler kaybolur ve uç nokta her aktif duruma geçtiğinde temizlenir. Bunların zaman değişken özellikleri içermesi beklenir (örneğin, geçerli çalışan uygulamalara, cihaz duruşuna vb.) Örneğin, geçerli ortama bağımlı olan tüm ayarlar.

Kullanıcı ile varsayılan arasında düşünmenin yolu, özelliklerin işletim sistemi ve sürücü yükseltmelerinde kalıcı olmasını isteyip istemediğinizdir. Kullanıcı özellikleri kalıcı hale gelecek. Varsayılan özellikler, INF dosyasından yeniden ayarlanır.

APO Bağlamları

CAPX ayarları çerçevesi, APO yazarının APO özelliklerini bağlamlara göre gruplandırmasını sağlar. Her APO kendi bağlamını tanımlayabilir ve özellikleri kendi bağlamlarına göre güncelleştirebilir. Ses uç noktası için efektler özellik deposu sıfır veya daha fazla bağlam içerebilir. Satıcılar, ister SFX/MFX/EFX ister moda göre olsun, istedikleri gibi bağlamlar oluşturabilir. Satıcı, satıcı tarafından gönderilen tüm API'ler için tek bir bağlam olmasını da seçebilir.

Ayarlar Kısıtlanmış Yetkinlik

Ayarlar API'sinin amacı, bir ses cihazıyla ilişkili ses efektleri ayarlarını sorgulamak ve değiştirmek isteyen tüm OEM'leri ve HSA geliştiricileri desteklemektir. Bu API, manifestte belirtilmesi gereken "audioDeviceConfiguration" adlı kısıtlanmış yetenek aracılığıyla özellik deposuna erişim sağlamak amacıyla HSA ve Win32 uygulamalarına sunulur. Buna ek olarak, karşılık gelen bir ad alanı aşağıdaki gibi bildirilmelidir:

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

IAudioSystemEffectsPropertyStore bir ISV/IHV hizmeti, UWP depolama uygulaması, yönetici olmayan masaüstü uygulamaları ve API'ler tarafından okunabilir ve yazılabilir. Ayrıca bu, APO'lar için iletileri bir hizmete veya UWP mağaza uygulamasına geri iletme mekanizması olarak hareket edebilir.

Uyarı

Bu kısıtlı bir özelliktir: Microsoft Store'a bu özellik ile bir uygulama gönderilirse, yakın inceleme tetikler. Uygulamanın bir Donanım Destek Uygulaması (HSA) olması gerekir ve gönderim onay almadan önce gerçekten bir HSA olduğunu değerlendirmek için incelenir.

API tanımı - Ayarlar Çerçevesi

Yeni IAudioSystemEffectsPropertyStore arabirimi, HSA'nın ses sistemi efektleri özellik depolarına erişmesine ve özellik değişikliği bildirimlerine kaydolmasına olanak tanır.

ActiveAudioInterfaceAsync işlevi, IAudioSystemEffectsPropertyStore arabirimini zaman uyumsuz olarak almak için bir yöntem sağlar.

Bir uygulama, yeni IAudioSystemEffectsPropertyChangeNotificationClient geri çağırma arabirimini kullanarak sistem efektleri özellik deposu değiştiğinde bildirim alabilir.

IMMDevice::Activate kullanarak IAudioSystemEffectsPropertyStore'u almaya çalışan uygulama

Örnek, bir Donanım Destek Uygulamasının IAudioSystemEffectsPropertyStore'u etkinleştirmek için IMMDevice::Activate'ı nasıl kullanabileceğini gösterir. Örnek, kullanıcı ayarlarına sahip bir IPropertyStore açmak için IAudioSystemEffectsPropertyStore'nun nasıl kullanılacağını gösterir.

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

ActivateAudioInterfaceAsync kullanan örnek

Bu örnek önceki örnekle aynı işlemi yapar, ancak IMMDevice yerine ActivateAudioInterfaceAsync API'sini kullanarak IAudioSystemEffectsPropertyStore arabirimini zaman uyumsuz olarak alır.

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::IAudioSystemEffectsPropertyStore kullanarak kodu başlatma

Örnek, APO'nun başlatılması sırasında APO'nun kullanıcı, varsayılan ve geçici IPropertyStore arabirimlerini almak için APO'nun APOInitSystemEffects3 yapısını kullanabileceğini gösterir.

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

Özellik değişikliği bildirimleri için uygulamanın kaydedilmesi

Örnek, özellik değişikliği bildirimleri için kayıt işlemlerinin kullanımını gösterir. APO ile kullanılmamalıdır ve yalnızca Win32 uygulamaları tarafından kullanılmalıdır.

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

Örnek kod - Ayarlar Çerçevesi

Bu örnek kod sysvad SFX Swap APO örneğindendir - 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);
    }
}

INF bölümü - Ayarlar Çerçevesi

Yeni CAPX ayarları çerçevesini kullanarak efekt özelliklerini bildirmek için INF dosyası söz dizimi aşağıdaki gibidir:

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

Bu, efekt özelliklerini bildirmek için eski söz diziminin yerini aşağıdaki gibi alır:

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

INF, aynı ses uç noktası için hem IAudioSystemEffectsPropertyStore girdisine hem de IPropertyStore girdisine sahip olamaz. Bu desteklenmez.

Yeni özellik deposunun kullanımını gösteren örnek:

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}"

Bildirimler Çerçevesi

Bildirimler çerçevesi ses efektlerinin (API' ler) ses düzeyi, uç nokta ve ses efektleri özellik deposu değişiklik bildirimlerini istemesine ve işlemesine olanak tanır. Mevcut API'lerin yerini almak üzere tasarlanan bu çerçeve, APO'lar tarafından bildirimler için kaydolmak ve kaydı kaldırmak amacıyla kullanılan API'lerin yerine geçecektir.

Yeni API, APO'ların ilgilendikleri bildirim türlerini beyan etmek için kullanabilecekleri bir arabirim sunar. Windows ilgilendiği bildirimleri APO'ya sorgular ve bildirimi API'lere iletir. APO'lar artık kayıt veya kayıt kaldırma API'lerini açıkça çağırmak zorunda değil.

Bildirimler, seri kuyruk kullanılarak bir APO'ya teslim edilir. Uygun olduğunda, ilk bildirim istenen değerin ilk durumunu (örneğin ses uç noktası birimi) yayınlar. Bildirimler, audiodg.exe, akış için bir APO kullanmayı amaçlamaktan vazgeçtiğinde durur. APO'lar, UnlockForProcess sonrasında bildirim almayı durdurur. UnlockForProcess ve tüm uçuş içi bildirimleri eşitlemek yine de gereklidir.

Uygulama - Bildirimler Çerçevesi

Bir APO, bildirimler çerçevesinden faydalanmak için hangi bildirimlerle ilgilendiğini belirtir. Açık kayıt/kayıt kaldırma çağrısı yok. APO'ya yapılan tüm bildirimler serileştirilir ve bildirim geri çağırma iş parçacığını çok uzun süre engellememek önemlidir.

API tanımı - Notifications Framework

Bildirim çerçevesi, APO uç noktası ve sistem efekti bildirimleri için sesle ilgili yaygın bildirimleri kaydetmek ve almak üzere istemciler tarafından uygulanabilen yeni bir IAudioProcessingObjectNotifications arabirimi uygular.

Daha fazla bilgi için aşağıdaki sayfalarda ek içerik bulun:

Örnek kod - Notifications Framework

Örnek, bir APO'nun IAudioProcessingObjectNotifications arabirimini nasıl uygulayabileceğini gösterir. GetApoNotificationRegistrationInfo yönteminde örnek APO, sistem efektleri özellik depolarındaki değişikliklere yönelik bildirimler için kaydolmaktadır.
HandleNotification yöntemi, APO'nun kaydettiğiyle eşleşen değişiklikleri APO'ya bildirmek için işletim sistemi tarafından çağrılır.

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

Aşağıdaki kod, Swap APO MFX örneğinden - swapapomfx.cpp alınmış olup, bir dizi APO_NOTIFICATION_DESCRIPTORs döndürerek olaylara kaydolmayı gösterir.

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

Aşağıdaki kod SwapAPO MFX HandleNotifications örneğinden alınıp swapapomfx.cpp ve bildirimlerin nasıl işleneceğini gösterir.

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

Loglama Çerçevesi

Loglama çerçevesi, APO geliştiricilerine geliştirme ve hata ayıklamaya katkıda bulunmak için ek veri toplama yöntemleri sağlar. Bu çerçeve, farklı satıcılar tarafından kullanılan farklı günlüğe kaydetme yöntemlerini bir arada sunar ve daha anlamlı günlük kaydı oluşturmak için bunu ses izleme günlüğü sağlayıcılarına bağlar. Yeni çerçeve, loglama API'si sağlayarak işin geri kalanını işletim sistemine bırakır.

Sağlayıcı şu şekilde tanımlanır:

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

Her APO'nun kendi Aktivite Kimliği vardır. Bu, mevcut izleme günlüğü mekanizmasını kullandığından, mevcut konsol araçları bu olayları filtrelemek ve bunları gerçek zamanlı olarak görüntülemek için kullanılabilir. Yazılım İzleme Araçları - Windows sürücüleri bölümünde açıklandığı gibi tracelog ve tracefmt gibi mevcut araçları kullanabilirsiniz. İzleme oturumları hakkında daha fazla bilgi için bkz. Denetim GUID'i ile İzleme Oturumu Oluşturma.

İzleme günlüğü olayları telemetri olarak işaretlenmez ve xperf gibi araçlarda telemetri sağlayıcısı olarak görüntülenmez.

Uygulama - Kayıt Altyapısı

Günlüğe kaydetme framework'ü, ETW izlemesi tarafından sağlanan günlüğe kaydetme mekanizmalarına dayanır. ETW hakkında daha fazla bilgi için bkz. Olay İzleme. Bu, ses verilerini günlüğe kaydetmek amaçlanmamıştır, bunun yerine genellikle üretimde günlüğe kaydedilen olayları kaydetmek için kullanılır. Günlük API'leri gerçek zamanlı akış iş parçacığında kullanılmamalıdır çünkü bunlar, pompa iş parçacığının işletim sistemi CPU zamanlayıcısı tarafından kesintiye uğrama olasılığına sahiptir. Kayıt tutma, öncelikle sıklıkla sahada karşılaşılan sorunları ayıklamaya yardımcı olacak olaylar için kullanılmalıdır.

API tanımı - Kayıt Çerçevesi

Günlükleme çerçevesi, APO'lar için yeni bir günlükleme hizmeti sağlayan IAudioProcessingObjectLoggingService arabirimini tanıtır.

Daha fazla bilgi için bkz. IAudioProcessingObjectLoggingService.

Örnek kod - Kayıt Tutma Çatısı

Örnek, IAudioProcessingObjectLoggingService::ApoLog yönteminin kullanımını ve bu arabirim işaretçisinin IAudioProcessingObject::Initialize içinde nasıl elde edilir olduğunu gösterir.

AecApoMfx Loglama Örneği.

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

İş Parçacığı Oluşturma Çerçevesi

İş parçacığı oluşturma çerçevesi, uygun bir Multimedya Sınıf Zamanlayıcı Hizmeti (MMCSS) görevinin iş kuyruklarını kullanarak basit bir API aracılığıyla efektlerin çoklu iş parçacığı desteğine sahip olmasını sağlar. Gerçek zamanlı seri iş kuyruklarının oluşturulması ve bunların ana pompa iş parçacığıyla ilişkisi işletim sistemi tarafından işlenir. Bu çerçeve, API'lerin kısa süre çalışan iş öğelerini kuyruğa almalarını sağlar. Görevler arasındaki eşitleme, APO'nun sorumluluğunda olmaya devam eder. MMCSS iş parçacığı oluşturma hakkında daha fazla bilgi için Multimedya Sınıf Zamanlayıcı Hizmeti ve Real-Time İş Kuyruğu API'si'ne bakınız.

API tanımları - İş Parçacığı Oluşturma Çerçevesi

İş Parçacığı Oluşturma çerçevesi, APO'lar için gerçek zamanlı iş kuyruğuna erişim sağlayan IAudioProcessingObjectQueueService arabirimini tanıtır.

Daha fazla bilgi için aşağıdaki sayfalarda ek içerik bulun:

Örnek kod - İş Parçacıkları Çerçevesi

Bu örnek, IAudioProcessingObjectRTQueueService::GetRealTimeWorkQueue yönteminin kullanımını ve IAudioProcessingObjectRTQueueService arabirim işaretçisinin IAudioProcessingObject::Initialize içinde nasıl alınduğunu gösterir.

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

Bu arabirimi kullanma hakkında daha fazla örnek için lütfen aşağıdaki örnek koda bakın:

Ses Efektleri Bulma ve Efektler için Denetim

Bulma çerçevesi, işletim sisteminin kendi akışındaki ses efektlerini denetlemesine olanak tanır. Bu API'ler, bir uygulamanın kullanıcısının akışlar üzerindeki belirli etkileri (örneğin, derin gürültü engelleme) denetlemesi gereken senaryolar için destek sağlar. Bunu başarmak için bu çerçeve aşağıdakileri ekler:

  • Ses efektinin etkinleştirilip etkinleştirilmediğini veya devre dışı bırakılıp devre dışı bırakılamayacağını belirlemek için APO'dan sorgulanacak yeni bir API.
  • Ses efektinin durumunu açık/kapalı olarak ayarlamak için yeni bir API.
  • Ses efektleri listesinde bir değişiklik olduğunda veya kaynaklar kullanılabilir olduğunda bir ses efektinin etkinleştirilebileceği/devre dışı bırakılacağı bir bildirim.

Uygulama - Ses Efektleri Bulma

Bir APO,dinamik olarak etkinleştirilebilen ve devre dışı bırakılabilir efektleri kullanıma sunma amaçlıysa IAudioSystemEffects3 arabirimini uygulaması gerekir. APO, ses efektlerini IAudioSystemEffects3::GetControllableSystemEffectsList işlevi aracılığıyla kullanıma sunar ve IAudioSystemEffects3::SetAudioSystemEffectState işlevi aracılığıyla ses efektlerini etkinleştirir ve devre dışı bırakır.

Örnek kod - Ses Efekti Keşfi

Ses Efekti Bulma örnek kodu SwapAPOSFX örneği - swapaposfx.cpp içinde bulunabilir.

Aşağıdaki örnek kod, yapılandırılabilir efektler listesinin nasıl alınacaklarını gösterir. GetControllableSystemEffectsList örneği - 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;
}

Aşağıdaki örnek kodda efektlerin nasıl etkinleştirileceği ve devre dışı bırakileceği gösterilmektedir. SetAudioSystemEffectState örneği - 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;
}

Windows 11, sürüm 22H2'de WM SFX ve MFX API'lerinin yeniden kullanımı

Windows 11, sürüm 22H2'den itibaren, dahili WM SFX ve MFX APO'larını yeniden kullanan INF yapılandırma dosyaları artık CAPX SFX ve MFX APO'larını yeniden kullanabilir. Bu bölümde bunu yapmanın üç yolu açıklanmaktadır.

APO'lar için üç ekleme noktası vardır: ön karışık işleme, karışım sonrası işleme ve yakalama. Her bir mantıksal cihazın ses altyapısı, akış başına bir ön karışım işleme APO'sunun (işleme SFX) ve bir karışım sonrası işleme APO'sunun (MFX) bir örneğini destekler. Ses altyapısı, her yakalama akışına eklenen bir yakalama APO'sunun (yakalama SFX) bir örneğini de destekler. Gelen kutusu API'lerini yeniden kullanma veya sarmalama hakkında daha fazla bilgi için bkz. Özel ve Windows API'lerini birleştirme.

CAPX SFX ve MFX API'leri aşağıdaki üç yoldan biriyle yeniden kullanılabilir.

INF DDInstall Bölümünü Kullanma

wdmaudio.inf dosyasından mssysfx.CopyFilesAndRegisterCapX'i aşağıdaki girdileri ekleyerek kullanın.

   Include=wdmaudio.inf
   Needs=mssysfx.CopyFilesAndRegisterCapX

INF dosyası uzantısı kullanılması

wdmaudioapo.inf, AudioProcessingObject sınıf uzantısı inf'dir. SFX ve MFX API'lerinin cihaza özgü kaydını içerir.

Akış ve mod efektleri için WM SFX ve MFX API'lerine doğrudan başvurma

Akış ve mod efektleri için bu API'lere doğrudan başvurmak için aşağıdaki GUID değerlerini kullanın.

  • {C9453E73-8C5C-4463-9984-AF8BAB2F5447} öğesini WM SFX APO olarak kullanın
  • WM MFX APO olarak {13AB3EBD-137E-4903-9D89-60BE8277FD17} kullanın.

SFX (Stream) ve MFX (Mod), Windows 8.1'de sırasıyla LFX (yerel) ve GFX (genel) olarak adlandırılmıştı. Bu kayıt defteri girdileri önceki adları kullanmaya devam eder.

Cihaza özgü kayıt, HKCR yerine HKR kullanır.

INF dosyasında aşağıdaki girdilerin eklenmesi gerekir.

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

Bu INF dosyası girdileri, yeni APO'lar için Windows 11 API'leri tarafından kullanılacak bir özellik deposu oluşturur.

INF örneğinde PKEY_FX_Association. HKR,"FX\\0",%PKEY_FX_Association%,,%KSNODETYPE_ANY%ile değiştirilmelidir HKR,"FX\\0\\%WMALFXGFXAPO_Context%",%PKEY_FX_Association%,,%KSNODETYPE_ANY%.

Ayrıca bakınız

Windows Ses İşleme Nesneleri.