API di Windows 11 per oggetti di elaborazione audio

Questo argomento presenta un set di nuove API di Windows 11 per gli oggetti di elaborazione audio (APO) forniti con un driver audio.

Windows consente ai produttori di hardware audio di terze parti di includere effetti di elaborazione dei segnali digitali personalizzati basati su host. Questi effetti vengono inseriti in un pacchetto come OGGETTI di elaborazione audio (APO) in modalità utente. Per altre informazioni, vedere Oggetti di elaborazione audio di Windows.

Alcune delle API descritte in questo articolo consentono nuovi scenari per fornitori di hardware indipendenti (IHV) e fornitori di software indipendenti (ISV), mentre altre API sono progettate per fornire alternative che migliorano l'affidabilità audio complessiva e le funzionalità di debug.

  • Il framework Acoustic Echo Cancellation (AEC) consente a un'APO di identificarsi come APO di AEC , concedendo l'accesso a un flusso di riferimento e a controlli aggiuntivi.
  • Il framework Impostazioni consentirà alle API di esporre metodi per l'esecuzione di query e la modifica dell'archivio delle proprietà per gli effetti audio ("archivio proprietà FX") in un endpoint audio. Quando questi metodi vengono implementati da un'apo, possono essere richiamati da Hardware Support Apps (HSA) associati a tale APO.
  • Il framework delle notifiche consente agli effetti audio (API) di richiedere notifiche per la gestione delle modifiche all'archivio delle proprietà di volume, endpoint ed effetti audio.
  • Il framework di registrazione facilita lo sviluppo e il debug delle API.
  • Il framework di threading consente di eseguire il multithreading delle API usando un pool di thread registrato da MMCSS gestito dal sistema operativo.
  • Le API di individuazione e controllo degli effetti audio consentono al sistema operativo di rilevare, abilitare e disabilitare gli effetti disponibili per l'elaborazione in un flusso.

Per sfruttare queste nuove API, è previsto che le API usino la nuova interfaccia IAudioSystemEffects3 . Quando un APO implementa questa interfaccia, il sistema operativo lo interpreta come un segnale implicito che l'APO supporta il framework apo Impostazioni e consente all'APO di sottoscrivere le notifiche comuni relative all'audio dal motore audio.

Requisiti di sviluppo DI WINDOWS 11 APO CAPX

Tutte le nuove API fornite in un dispositivo per Windows 11 devono essere conformi alle API elencate in questo argomento, convalidate tramite HLK. Inoltre, qualsiasi API che sfrutta AEC deve seguire l'implementazione descritta in questo argomento, convalidata tramite HLK. Le implementazioni personalizzate per queste estensioni di elaborazione audio di base (Impostazioni, registrazione, notifiche, threading, AEC) dovrebbero sfruttare le API CAPX. Questa operazione verrà convalidata tramite i test HLK di Windows 11. Ad esempio, se un apo usa i dati del Registro di sistema per salvare le impostazioni anziché usare il framework Impostazioni, il test HLK associato avrà esito negativo.

Requisiti della versione di Windows

Le API descritte in questo argomento sono disponibili a partire dalla build 22000 del sistema operativo Windows 11, WDK e SDK. Windows 10 non avrà il supporto per queste API. Se un apo intende funzionare sia in Windows 10 che in Windows 11, può esaminare se viene inizializzato con APOInitSystemEffects2 o la struttura APOInitSystemEffects3 per determinare se è in esecuzione in un sistema operativo che supporta le API CAPX.

Le versioni più recenti di Windows, WDK e SDK possono essere scaricate di seguito tramite il Programma Windows Insider. I partner coinvolti con Microsoft tramite il Centro per i partner possono accedere a questo contenuto anche tramite Collaborazione. Per altre informazioni su Collaborazione, vedere Introduzione a Microsoft Collaborate.

Il contenuto WHCP di Windows 11 è stato aggiornato per fornire ai partner i mezzi per convalidare queste API.

Il codice di esempio per il contenuto descritto in questo argomento è disponibile qui: Audio/SYSVAD/APO - github

Annullamento dell'eco acustico (AEC)

Acoustic Echo Cancellation (AEC) è un effetto audio comune implementato da fornitori di hardware indipendenti (IHV) e fornitori di software indipendenti (ISV) come oggetto di elaborazione audio (APO) nella pipeline di acquisizione del microfono. Questo effetto è diverso da altri effetti in genere implementati da IHV e ISV in quanto richiede 2 input: un flusso audio dal microfono e un flusso audio da un dispositivo di rendering che funge da segnale di riferimento.

Questo nuovo set di interfacce consente a un AEC APO di identificarsi come tale per il motore audio. In questo modo, il motore audio configura l'APO in modo appropriato con più input e un singolo output.

Quando le nuove interfacce AEC vengono implementate da un apo, il motore audio:

  • Configurare l'APO con un input aggiuntivo che fornisce all'APO il flusso di riferimento da un endpoint di rendering appropriato.
  • Disattivare i flussi di riferimento quando il dispositivo di rendering cambia.
  • Consentire a un apo di controllare il formato del microfono di input e del flusso di riferimento.
  • Consentire a un'APO di ottenere timestamp sul microfono e sui flussi di riferimento.

Approccio precedente - Windows 10

Le API sono un singolo input, ovvero oggetti di output singolo. Il motore audio fornisce un AEC APO l'audio dall'endpoint del microfono all'input. Per ottenere il flusso di riferimento, un apo può interagire con il driver usando interfacce proprietarie per recuperare l'audio di riferimento dall'endpoint di rendering o usare WASAPI per aprire un flusso di loopback nell'endpoint di rendering.

Entrambi gli approcci precedenti presentano svantaggi:

  • Un AEC APO che usa canali privati per ottenere un flusso di riferimento dal driver, in genere può farlo solo dal dispositivo di rendering audio integrato. Di conseguenza, l'annullamento dell'eco non funzionerà se l'utente sta riproducendo l'audio dal dispositivo non integrato, ad esempio usb o bluetooth. Solo il sistema operativo è a conoscenza degli endpoint di rendering corretti che possono fungere da endpoint di riferimento.

  • Un APO può usare WASAPI per selezionare l'endpoint di rendering predefinito per eseguire l'annullamento dell'eco. Tuttavia, ci sono alcune insidie da tenere presente quando si apre un flusso di loopback dal processo di audiodg.exe (dove è ospitato l'APO).

    • Il flusso di loopback non può essere aperto/distrutto quando il motore audio chiama i metodi APO principali, in quanto ciò può comportare un deadlock.
    • Un apo di acquisizione non conosce lo stato dei flussi dei client. Ad esempio, un'app di acquisizione potrebbe avere un flusso di acquisizione nello stato 'STOP', ma l'APO non è a conoscenza di questo stato e quindi mantiene aperto il flusso di loopback nello stato "RUN", che è inefficiente in termini di consumo energetico.

Definizione DELL'API - AEC

Il framework AEC fornisce nuove strutture e interfacce che le API possono sfruttare. Queste nuove strutture e interfacce sono descritte di seguito.

struttura APO_CONNECTION_PROPERTY_V2

Le API che implementano l'interfaccia IApoAcousticEchoMachition verranno passate una struttura APO_CONNECTION_PROPERTY_V2 nella chiamata a IAudioProcessingObjectRT::APOProcess. Oltre a tutti i campi nella struttura APO_CONNECTION_PROPERTY , la versione 2 della struttura fornisce anche informazioni sul timestamp per i buffer audio.

Un apo può esaminare il campo APO_CONNECTION_PROPERTY.u32Signature per determinare se la struttura ricevuta dal motore audio è di tipo APO_CONNECTION_PROPERTY o APO_CONNECTION_PROPERTY_V2. APO_CONNECTION_PROPERTY strutture hanno una firma di APO_CONNECTION_PROPERTY_SIGNATURE, mentre APO_CONNECTION_PROPERTY_V2 hanno una firma uguale a APO_CONNECTION_PROPERTY_V2_SIGNATURE. Se la firma ha un valore uguale a APO_CONNECTION_PROPERTY_V2_SIGNATURE, il puntatore alla struttura APO_CONNECTION_PROPERTY può essere tipizzato in modo sicuro a un puntatore APO_CONNECTION_PROPERTY_V2.

Il codice seguente proviene dall'esempio apo MFX di Aec, AecApoMfx.cpp e mostra la ricasting.

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

IApoAcousticEchoEreition

L'interfaccia IApoAcousticEchoCancellation non dispone di metodi espliciti su di esso. Il suo scopo è identificare un apo AEC al motore audio. Questa interfaccia può essere implementata solo dagli effetti della modalità (MFX) sugli endpoint di acquisizione. L'implementazione di questa interfaccia in qualsiasi altra APO causerà un errore durante il caricamento dell'APO. Per informazioni generali su MFX, vedere Architettura degli oggetti di elaborazione audio.

Se l'effetto della modalità su un endpoint di acquisizione viene implementato come una serie di API concatenati, solo l'APO più vicino al dispositivo può implementare questa interfaccia. Alle API che implementano questa interfaccia verrà offerta la struttura APO_CONNECTION_PROPERTY_V2 nella chiamata a IAudioProcessingobjectRT::APOProcess. L'APO può verificare la presenza di una firma APO_CONNECTION_PROPERTY_V2_SIGNATURE nella proprietà di connessione e digitare il typecast della struttura APO_CONNECTION_PROPERTY in ingresso in una struttura APO_CONNECTION_PROPERTY_V2.

Nel riconoscimento del fatto che le API di AEC eseguono in genere i propri algoritmi a una frequenza di campionamento/conteggio dei canali specifici, il motore audio fornisce il supporto di ricampionamento alle API che implementano l'interfaccia IApoAcousticEchoMachition.

Quando un AEC APO restituisce APOERR_FORMAT_NOT_SUPPORTED nella chiamata a IAudioProcessingObject::OutInputFormatSupported, il motore audio chiamerà IAudioProcessingObject::IsInputFormatSupported nell'APO con un formato di output NULL e un formato di input non Null, per ottenere il formato suggerito dall'APO. Il motore audio ricampionerà quindi l'audio del microfono al formato suggerito prima di inviarlo all'APO AEC. In questo modo si elimina la necessità di AEC APO per implementare la frequenza di campionamento e la conversione del numero di canali.

IApoAuxiliaryInputConfiguration

L'interfaccia IApoAuxiliaryInputConfiguration fornisce metodi che le API possono implementare in modo che il motore audio possa aggiungere e rimuovere flussi di input ausiliari.

Questa interfaccia viene implementata dall'APO di AEC e usata dal motore audio per inizializzare l'input di riferimento. In Windows 11, l'AEC APO verrà inizializzato solo con un singolo input ausiliario, uno con il flusso audio di riferimento per l'annullamento dell'eco. Il metodo AddAuxiliaryInput verrà usato per aggiungere l'input di riferimento all'APO. I parametri di inizializzazione conterranno un riferimento all'endpoint di rendering da cui viene ottenuto il flusso di loopback.

Il metodo IsInputFormatSupported viene chiamato dal motore audio per negoziare i formati sull'input ausiliario. Se l'APO AEC preferisce un formato specifico, può restituire S_FAL edizione Standard nella chiamata a IsInputFormatSupported e specificare un formato suggerito. Il motore audio ricampionerà l'audio di riferimento al formato suggerito e lo fornirà all'input ausiliario dell'APO AEC.

IApoAuxiliaryInputRT

L'interfaccia IApoAuxiliaryInputRT è l'interfaccia sicura in tempo reale usata per guidare gli input ausiliari di un apo.

Questa interfaccia viene usata per fornire dati audio sull'input ausiliario all'APO. Si noti che gli input audio ausiliari non vengono sincronizzati con le chiamate a IAudioProcessingObjectRT::APOProcess. Quando non viene eseguito il rendering dell'endpoint di rendering, i dati di loopback non saranno disponibili nell'input ausiliario. cioè non ci saranno chiamate a IApoAuxiliaryInputRT::AcceptInput

Riepilogo delle API CAPX di AEC

Per altre informazioni, vedere altre informazioni nelle pagine seguenti.

Codice di esempio - AEC

Fare riferimento agli esempi di codice Sysvad Audio AecApo seguenti.

Il codice seguente dell'intestazione di esempio Aec APO- AecAPO.h mostra i tre nuovi metodi pubblici da aggiungere.

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

Il codice seguente proviene dall'esempio Aec APO MFX- AecApoMfx.cpp e mostra l'implementazione di AddAuxiliaryInput, quando l'APO può gestire un solo input ausiliario.

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;

Esaminare anche il codice di esempio che mostra l'implementazione di CAecApoMFX::IsInputFormatSupported e CAecApoMFX::AcceptInput nonché la gestione di APO_CONNECTION_PROPERTY_V2.

Sequenza di operazioni - AEC

All'inizializzazione:

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

In caso di modifica del dispositivo di rendering:

  1. IAudioProcessingObject::Initialize
  2. IApoAuxiliaryInputConfiguration::AddAuxiliaryInput
  3. IAudioProcessingObjectConfiguration::LockForProcess
  4. Modifiche predefinite del dispositivo
  5. IAudioProcessingObjectConfiguration::UnlockForProcess
  6. IApoAuxiliaryInputConfiguration::RemoveAuxiliaryInput
  7. IApoAuxiliaryInputConfiguration::AddAuxiliaryInput
  8. IAudioProcessingObjectConfiguration::LockForProcess

Si tratta del comportamento consigliato del buffer per AEC.

  • I buffer ottenuti nella chiamata a IApoAuxiliaryInputRT::AcceptInput devono essere scritti in un buffer circolare senza bloccare il thread principale.
  • Nella chiamata a IAudioProcessingObjectRT::APOProcess, il buffer circolare deve essere letto per il pacchetto audio più recente dal flusso di riferimento e questo pacchetto deve essere usato per l'esecuzione tramite l'algoritmo di annullamento echo.
  • I timestamp sui dati di riferimento e microfono possono essere usati per allineare i dati dell'altoparlante e del microfono.

Flusso di loopback di riferimento

Per impostazione predefinita, il flusso di loopback "tocca" (ascolta) il flusso audio prima dell'applicazione di qualsiasi volume o disattivazione. Un flusso di loopback toccato prima dell'applicazione del volume è noto come flusso di loopback di pre-volume. Un vantaggio della presenza di un flusso di loopback di pre-volume è un flusso audio chiaro e uniforme, indipendentemente dall'impostazione corrente del volume.

Alcuni algoritmi AEC possono preferire l'acquisizione di un flusso di loopback connesso dopo qualsiasi elaborazione del volume(incluso l'audio disattivato). Questa configurazione è nota come loopback post-volume.

Nella prossima versione principale delle API di Windows AEC può richiedere il loopback post-volume negli endpoint supportati.

Limiti

A differenza dei flussi di loopback di pre-volume, disponibili per tutti gli endpoint di rendering, i flussi di loopback post-volume potrebbero non essere disponibili in tutti gli endpoint.

Richiesta di loopback post-volume

Le API AEC che desiderano usare il loopback post-volume devono implementare l'interfaccia IApoAcousticEchoMachition2 .

Un AEC APO può richiedere un loopback post-volume restituendo il flag APO_REFERENCE_STREAM_PROPERTIES_POST_VOLUME_LOOPBACK tramite il parametro Properties nell'implementazione di IApoAcousticEchoMachition2::GetDesiredReferenceStreamProperties.

A seconda dell'endpoint di rendering attualmente in uso, il loopback post-volume potrebbe non essere disponibile. Quando viene chiamato il metodo IApoAuxiliaryInputConfiguration::AddAuxiliaryInputConfiguration,:AddAuxiliaryInput , viene visualizzato un messaggio APO di AEC. Se il campo AcousticEchoCanceller_Reference_Input streamProperties contiene APO_REFERENCE_STREAM_PROPERTIES_POST_VOLUME_LOOPBACK, è in uso il loopback post-volume.

Il codice seguente dell'intestazione di esempio APO AEC: AecAPO.h mostra i tre nuovi metodi pubblici da aggiungere.

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;

Il frammento di codice seguente proviene dall'esempio APO MFX di AEC, AecApoMfx.cpp e mostra l'implementazione di GetDesiredReferenceStreamProperties e la parte pertinente di AddAuxiliaryInput.

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.

Impostazioni Framework

Il framework di Impostazioni consente alle API di esporre metodi per eseguire query e modificare l'archivio delle proprietà per gli effetti audio ("FX Property Store") in un endpoint audio. Questo framework può essere usato dalle API e dalle app di supporto hardware (HSA) che desiderano comunicare le impostazioni a tale APO. Gli HSA possono essere app piattaforma UWP (Universal Windows Platform) (UWP) e richiedono una funzionalità speciale per richiamare le API in Impostazioni Framework. Per altre informazioni sulle app HSA, vedi App per dispositivi UWP.

Struttura di FxProperty Store

Il nuovo negozio FxProperty ha tre sottostore: Default, User e Volatile.

La sottochiave "Default" contiene proprietà degli effetti personalizzati e viene popolata dal file INF. Queste proprietà non vengono mantenute tra gli aggiornamenti del sistema operativo. Ad esempio, le proprietà in genere definite in un inF si adattano qui. Questi verranno quindi ripopolato dall'INF.

La sottochiave "User" contiene le impostazioni utente relative alle proprietà degli effetti. Queste impostazioni vengono mantenute dal sistema operativo tra gli aggiornamenti e le migrazioni. Ad esempio, tutti i set di impostazioni che l'utente può configurare che devono essere mantenuti durante l'aggiornamento.

La sottochiave "Volatile" contiene proprietà degli effetti volatili. Queste proprietà vengono perse al riavvio del dispositivo e vengono cancellate ogni volta che l'endpoint passa ad attivo. Si prevede che contengano proprietà varianti temporali (ad esempio, in base alle applicazioni in esecuzione correnti, al comportamento del dispositivo e così via) Ad esempio, tutte le impostazioni dipendenti dall'ambiente corrente.

Il modo in cui considerare l'utente rispetto all'impostazione predefinita è se si vuole che le proprietà vengano mantenute tra gli aggiornamenti del sistema operativo e dei driver. Le proprietà utente verranno mantenute. Le proprietà predefinite verranno popolate nuovamente da INF.

Contesti APO

Il framework delle impostazioni CAPX consente a un autore di APO di raggruppare le proprietà APO in base ai contesti. Ogni APO può definire il proprio contesto e aggiornare le proprietà relative al proprio contesto. L'archivio delle proprietà degli effetti per un endpoint audio può avere zero o più contesti. I fornitori sono liberi di creare contesti, indipendentemente dal fatto che si tratti di SFX/MFX/EFX o in modalità. Un fornitore può anche scegliere di avere un singolo contesto per tutte le API spedite da tale fornitore.

Impostazioni funzionalità con restrizioni

L'API delle impostazioni è progettata per supportare tutti gli sviluppatori oem e HSA interessati a eseguire query e modificare le impostazioni degli effetti audio associati a un dispositivo audio. Questa API viene esposta a un modulo di protezione hardware e alle applicazioni Win32 per fornire l'accesso all'archivio delle proprietà tramite la funzionalità limitata "audioDeviceConfiguration" che deve essere dichiarata nel manifesto. Inoltre, uno spazio dei nomi corrispondente deve essere dichiarato come segue:

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

IAudioSystemEffectsPropertyStore è leggibile e scrivibile da un servizio ISV/IHV, un'applicazione di archivio UWP, applicazioni desktop non amministrative e API. Inoltre, questo può fungere da meccanismo per le API per recapitare i messaggi a un servizio o a un'applicazione di archiviazione UWP.

Nota

Si tratta di una funzionalità con restrizioni: se un'applicazione viene inviata con questa funzionalità a Microsoft Store, verrà attivata una verifica di chiusura. L'app deve essere un'app di supporto hardware (HSA) e verrà esaminata per valutare che sia effettivamente un modulo di protezione hardware prima dell'approvazione dell'invio.

Definizione DELL'API - framework di Impostazioni

La nuova interfaccia IAudioSystemEffectsPropertyStore consente a un modulo HSA di accedere agli archivi delle proprietà degli effetti del sistema audio e registrarsi per le notifiche di modifica delle proprietà.

La funzione ActiveAudioInterfaceAsync fornisce un metodo per ottenere l'interfaccia IAudioSystemEffectsPropertyStore in modo asincrono.

Un'app può ricevere notifiche quando cambia l'archivio delle proprietà degli effetti di sistema, usando la nuova interfaccia di callback IAudioSystemEffectsPropertyChangeNotificationClient .

Applicazione che tenta di ottenere IAudioSystemEffectsPropertyStore usando IMMDevice::Activate

L'esempio illustra come un'app di supporto hardware può usare IMMDevice::Activate per attivare IAudioSystemEffectsPropertyStore. L'esempio mostra come usare IAudioSystemEffectsPropertyStore per aprire un IPropertyStore con impostazioni utente.

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

Esempio con ActivateAudioInterfaceAsync

Questo esempio esegue la stessa operazione dell'esempio precedente, ma invece di usare IMMDevice, usa l'API ActivateAudioInterfaceAsync per ottenere l'interfaccia IAudioSystemEffectsPropertyStore in modo asincrono.

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

Codice IAudioProcessingObject::Initialize con IAudioSystemEffectsPropertyStore

L'esempio illustra l'implementazione di un oggetto APO può usare la struttura APOInitSystemEffects3 per recuperare le interfacce IPropertyStore predefinite e volatili per l'APO, durante l'inizializzazione dell'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;
}

Registrazione dell'applicazione per le notifiche di modifica delle proprietà

L'esempio illustra l'uso della registrazione per le notifiche di modifica delle proprietà. Questo non deve essere usato da con l'APO e deve essere usato dalle applicazioni 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;
}

Codice di esempio - Impostazioni Framework

Questo codice di esempio proviene dall'esempio 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);
    }
}

Sezione INF - Impostazioni Framework

La sintassi del file INF per dichiarare le proprietà dell'effetto usando il nuovo framework delle impostazioni CAPX è la seguente:

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

In questo modo viene sostituita la sintassi precedente per dichiarare le proprietà degli effetti come indicato di seguito:

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

InF non può avere sia la voce IAudioSystemEffectsPropertyStore che la voce IPropertyStore per lo stesso endpoint audio. Questo non è supportato.

Esempio che mostra l'uso del nuovo archivio di proprietà:

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

Framework notifiche

Il framework notifiche consente agli effetti audio (API) di richiedere e gestire le notifiche di modifica dell'archivio delle proprietà di volume, endpoint ed effetti audio. Questo framework è progettato per sostituire le API esistenti usate dalle API per registrare e annullare la registrazione per le notifiche.

La nuova API introduce un'interfaccia che le API possono usare per dichiarare il tipo di notifiche a cui è interessato l'APO. Windows eseguirà una query sull'APO per le notifiche a cui è interessato e inoltra la notifica alle API. Le API non devono più chiamare in modo esplicito le API di registrazione o annullamento della registrazione.

Le notifiche vengono recapitate a un apo usando una coda seriale. Se applicabile, la prima notifica trasmette lo stato iniziale del valore richiesto, ad esempio il volume dell'endpoint audio. Le notifiche vengono arrestate una volta audiodg.exe smette di usare un apo per lo streaming. Le API smetteranno di ricevere notifiche dopo UnlockForProcess. È comunque necessario sincronizzare UnlockForProcess ed eventuali notifiche in anteprima.

Implementazione - Notifications Framework

Per sfruttare il framework di notifiche, un'apo dichiara le notifiche a cui è interessato. Non sono presenti chiamate esplicite di registrazione/annullamento della registrazione. Tutte le notifiche all'APO vengono serializzate ed è importante non bloccare il thread di callback di notifica per troppo tempo.

Definizione API - Notifications Framework

Il framework di notifica implementa una nuova interfaccia IAudioProcessingObjectNotifications che può essere implementata dai client per registrare e ricevere notifiche comuni relative all'audio per gli endpoint APO e le notifiche degli effetti di sistema.

Per altre informazioni, trovare contenuto aggiuntivo nelle pagine seguenti:

Codice di esempio - Notifications Framework

L'esempio illustra come un apo può implementare l'interfaccia IAudioProcessingObjectNotifications. Nel metodo GetApoNotificationRegistrationInfo l'esempio APO registra le notifiche relative alle modifiche apportate agli archivi delle proprietà degli effetti di sistema.
Il metodo HandleNotification viene richiamato dal sistema operativo per notificare all'APO le modifiche corrispondenti a quanto registrato dall'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;
    }
}

Il codice seguente proviene dall'esempio swap APO MFX, swapapomfx.cpp e mostra la registrazione per gli eventi restituendo una matrice di 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;
}

Il codice seguente proviene dall'esempio SwapAPO MFX HandleNotifications, swapapomfx.cpp e illustra come gestire le notifiche.

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

Framework di registrazione

Il framework di registrazione fornisce agli sviluppatori APO strumenti aggiuntivi per raccogliere dati per migliorare lo sviluppo e il debug. Questo framework unifica i diversi metodi di registrazione usati da fornitori diversi e lo collega ai provider di registrazione di traccia audio per creare una registrazione più significativa. Il nuovo framework fornisce un'API di registrazione, lasciando il resto del lavoro da eseguire dal sistema operativo.

Il provider è definito come:

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

Ogni APO ha il proprio ID attività. Poiché usa il meccanismo di registrazione delle tracce esistente, è possibile usare gli strumenti della console esistenti per filtrare questi eventi e visualizzarli in tempo reale. È possibile usare strumenti esistenti come tracelog e tracefmt come descritto in Strumenti per traccia software - Driver Windows. Per altre informazioni sulle sessioni di traccia, vedere Creazione di una sessione di traccia con un GUID di controllo.

Gli eventi di registrazione della traccia non vengono contrassegnati come dati di telemetria e non verranno visualizzati come provider di telemetria in strumenti come xperf.

Implementazione - Framework di registrazione

Il framework di registrazione si basa sui meccanismi di registrazione forniti dalla traccia ETW. Per altre informazioni su ETW, vedere Event Tracing.For more information about ETW, see Event Tracing. Questo non è progettato per registrare i dati audio, ma piuttosto per registrare gli eventi che vengono in genere registrati nell'ambiente di produzione. Le API di registrazione non devono essere usate dal thread di streaming in tempo reale, perché queste possono causare il mancato empting del thread pump da parte dell'utilità di pianificazione della CPU del sistema operativo. La registrazione deve essere usata principalmente per gli eventi che consentono di eseguire il debug dei problemi che vengono spesso rilevati nel campo.

Definizione DELL'API - Framework di registrazione

Il framework di registrazione introduce l'interfaccia IAudioProcessingObjectLoggingService che fornisce un nuovo servizio di registrazione per le API.

Per altre informazioni, vedere IAudioProcessingObjectLoggingService.

Codice di esempio - Framework di registrazione

L'esempio illustra l'uso del metodo IAudioProcessingObjectLoggingService::ApoLog e il modo in cui questo puntatore di interfaccia viene ottenuto in IAudioProcessingObject::Initialize.

Esempio di registrazione di 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;
}

Threading Framework

Il framework di threading che consente il multithreading degli effetti usando code di lavoro da un'attività MMCSS (Multimedia Class Scheduler Service) appropriata tramite un'API semplice. La creazione di code di lavoro seriali in tempo reale e la relativa associazione al thread di pompa principale vengono gestite dal sistema operativo. Questo framework consente alle API di accodare elementi di lavoro con esecuzione breve. La sincronizzazione tra le attività continua a essere responsabilità dell'APO. Per altre informazioni sul threading MMCSS, vedere Servizio utilità di pianificazione classi multimediali e API code di lavoro in tempo reale.

Definizioni api - Framework di threading

Il framework threading introduce l'interfaccia IAudioProcessingObjectQueueService che fornisce l'accesso alla coda di lavoro in tempo reale per le API.

Per altre informazioni, trovare contenuto aggiuntivo nelle pagine seguenti:

Codice di esempio - Framework di threading

Questo esempio illustra l'uso del metodo IAudioProcessingObjectRTQueueService::GetRealTimeWorkQueue e il modo in cui il puntatore all'interfaccia IAudioProcessingObjectRTQueueService viene ottenuto in 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;
}

Per altri esempi di utilizzo di questa interfaccia, vedere il codice di esempio seguente:

Individuazione e controllo degli effetti audio per gli effetti

Il framework di individuazione consente al sistema operativo di controllare gli effetti audio nel flusso. Queste API forniscono supporto per gli scenari in cui l'utente di un'applicazione deve controllare determinati effetti sui flussi (ad esempio, eliminazione del rumore profondo). A tale scopo, questo framework aggiunge quanto segue:

  • Nuova API per eseguire una query da un apo per determinare se un effetto audio può essere abilitato o disabilitato.
  • Nuova API per impostare lo stato di un effetto audio su on/off.
  • Una notifica quando si verifica una modifica nell'elenco di effetti audio o quando le risorse diventano disponibili in modo che un effetto audio possa ora essere abilitato/disabilitato.

Implementazione - Individuazione effetti audio

Un APO deve implementare l'interfaccia IAudioSystemEffects3 se intende esporre effetti che possono essere abilitati e disabilitati in modo dinamico. Un APO espone i suoi effetti audio tramite la funzione IAudioSystemEffects3::GetControllableSystemEffectsList e abilita e disabilita i relativi effetti audio tramite la funzione IAudioSystemEffects3::SetAudioSystemEffectState .

Codice di esempio - Individuazione effetti audio

Il codice di esempio di individuazione degli effetti audio è disponibile nell'esempio SwapAPOSFX - swapaposfx.cpp.

Il codice di esempio seguente illustra come recuperare l'elenco di effetti configurabili. Esempio 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;
}

Il codice di esempio seguente illustra come abilitare e disabilitare gli effetti. Esempio 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;
}

Riutilizzo delle API WM SFX e MFX in Windows 11, versione 22H2

A partire da Windows 11 versione 22H2, i file di configurazione INF che riutilizzano le API WM SFX e MFX della posta in arrivo possono ora riutilizzare le API CAPX SFX e MFX. Questa sezione descrive i tre modi per eseguire questa operazione.

Esistono tre punti di inserimento per le API: rendering pre-mix, rendering post-mix e acquisizione. Ogni motore audio di ogni dispositivo logico supporta un'istanza di apo di rendering pre-mix per flusso (rendering SFX) e un post-mix rendering APO (MFX). Il motore audio supporta anche un'istanza di apo di acquisizione (acquisizione SFX) inserita in ogni flusso di acquisizione. Per altre informazioni su come riutilizzare o eseguire il wrapping delle API posta in arrivo, vedere Combinare API personalizzate e Windows.

Le API CAPX SFX e MFX possono essere riutilizzate in uno dei tre modi seguenti.

Uso della sezione INF DDInstall

Usare mssysfx. CopyFilesAndRegisterCapX da wdmaudio.inf aggiungendo le voci seguenti.

   Include=wdmaudio.inf
   Needs=mssysfx.CopyFilesAndRegisterCapX

Uso di un file INF di estensione

Wdmaudioapo.inf è l'estensione della classe AudioProcessingObject inf. Contiene la registrazione specifica del dispositivo delle API SFX e MFX.

Fare riferimento direttamente alle API WM SFX e MFX per gli effetti di flusso e modalità

Per fare riferimento direttamente a queste API per gli effetti di flusso e modalità, usare i valori GUID seguenti.

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

SFX (Stream) e MFX (Mode) sono stati indicati in Windows 8.1 a LFX (locale) e MFX è stato definito GFX (globale). Queste voci del Registro di sistema continuano a usare i nomi precedenti.

La registrazione specifica del dispositivo usa HKR anziché HKCR.

Il file INF dovrà avere le voci seguenti aggiunte.

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

Queste voci di file INF creeranno un archivio proprietà che verrà usato dalle API di Windows 11 per le nuove API.

PKEY_FX_Association in INF ex. HKR,"FX\\0",%PKEY_FX_Association%,,%KSNODETYPE_ANY%, deve essere sostituito con HKR,"FX\\0\\%WMALFXGFXAPO_Context%",%PKEY_FX_Association%,,%KSNODETYPE_ANY%.

Vedi anche

Oggetti di elaborazione audio di Windows.