Partager via


API Windows 11 pour les objets de traitement audio

Cette rubrique présente un ensemble de nouvelles API Windows 11 pour les objets de traitement audio (API) fournis avec un pilote audio.

Windows permet aux fabricants de matériel audio tiers d’inclure des effets personnalisés de traitement des signaux numériques basés sur l’hôte. Ces effets sont empaquetés en tant qu’objets de traitement audio en mode utilisateur (API). Pour plus d’informations, veuillez consulter la section Objets de traitement audio Windows.

Certaines API décrites ici permettent de nouveaux scénarios pour les fournisseurs de matériel indépendant (IHV) et les fournisseurs de logiciels indépendants (ISV), tandis que d’autres API sont destinées à fournir des alternatives qui améliorent la fiabilité et le débogage audio globaux.

  • L’infrastructure Acoustic Echo Cancellation (AEC) permet à une APO de s’identifier en tant qu’APO AEC, en lui accordant l’accès à un flux de référence et à des contrôles supplémentaires.
  • L’infrastructure Paramètres permet aux API d’exposer des méthodes d’interrogation et de modification du magasin de propriétés pour les effets audio (« magasin de propriétés FX ») sur un point de terminaison audio. Lorsque ces méthodes sont implémentées par une APO, elles peuvent être appelées par des applications de support matériel (HSA) associées à cette APO.
  • L’infrastructure Notifications permet aux effets audio (APO) de demander des notifications afin de gérer les modifications du volume, du point de terminaison et du magasin de propriétés des effets audio.
  • L’infrastructure de journalisation permet de développer et de déboguer des API.
  • L’infrastructure Threading permet aux APO d’être multi-threads à l’aide d’un pool de threads géré par le système d’exploitation, enregistré auprès de MMCSS.
  • Les API de détection et de contrôle des effets audio permettent au système d’exploitation de détecter, d’activer et de désactiver les effets disponibles pour le traitement sur un flux.

Pour tirer parti de ces nouvelles API, les APOs sont supposées utiliser la nouvelle interface IAudioSystemEffects3. Lorsqu’une APO implémente cette interface, le système d’exploitation l’interprète comme un signal implicite que l’APO prend en charge l’infrastructure des paramètres APO et permet à l’APO de s’abonner à des notifications audio courantes à partir du moteur audio.

Exigences de développement pour APO CAPX sous Windows 11

Les nouveaux APO fournis sur un appareil pour Windows 11 doivent être conformes aux API répertoriées dans cette rubrique, validées via HLK. En outre, toutes les API tirant parti de l’AEC sont censées suivre l’implémentation décrite dans cette rubrique, validée via HLK. Les implémentations personnalisées pour ces extensions de traitement audio de base (paramètres, journalisation, notifications, threading, AEC) sont censées tirer parti des API CAPX. Cela sera validé par le biais des tests HLK Windows 11. Par exemple, si une APO utilise des données de Registre pour enregistrer les paramètres au lieu d’utiliser l’infrastructure de paramètres, le test HLK associé échoue.

Configuration requise pour la version de Windows

Les API décrites dans cette rubrique sont disponibles à partir de la build 22000 du système d’exploitation Windows 11, wdK et sdk. Windows 10 n’aura pas de prise en charge de ces API. Si une APO a l’intention de fonctionner sur Windows 10 et Windows 11, elle peut examiner s’il est initialisé avec l’APOInitSystemEffects2 ou la structure APOInitSystemEffects3 pour déterminer s’il s’exécute sur un système d’exploitation qui prend en charge les API CAPX.

Les dernières versions de Windows, de WDK et du Kit de développement logiciel (SDK) peuvent être téléchargées ci-dessous via le programme Windows Insider. Les partenaires engagés auprès de Microsoft via l’Espace partenaires peuvent également accéder à ce contenu via Collaboration. Pour plus d’informations sur La collaboration, consultez Présentation de Microsoft Collaborate.

Le contenu WHCP Windows 11 a été mis à jour pour fournir aux partenaires les moyens de valider ces API.

Vous trouverez ici l’exemple de code du contenu décrit dans cette rubrique : Audio/SYSVAD/APO - github

Annulation de l'écho acoustique (AEC)

Acoustic Echo Cancellation (AEC) est un effet audio courant implémenté par les éditeurs de matériel indépendants (IHD) et les éditeurs de logiciels indépendants (ISV) en tant qu’objet de traitement audio (APO) dans le pipeline de capture du microphone. Cet effet est différent des autres effets généralement implémentés par les IHVS et les éditeurs de logiciels indépendants dans le sens où il nécessite 2 entrées : un flux audio à partir du microphone et un flux audio à partir d’un appareil de rendu qui agit comme le signal de référence.

Ce nouvel ensemble d’interfaces permet à une APO AEC de s’identifier comme tel au moteur audio. Cela entraîne la configuration appropriée du moteur audio de l’APO avec plusieurs entrées et une seule sortie.

Lorsque les nouvelles interfaces AEC sont implémentées par une APO, le moteur audio fera :

  • Configurez l’APO avec une entrée supplémentaire qui fournit à l’APO le flux de référence à partir d’un point de terminaison de rendu approprié.
  • Basculez les flux de données de référence au fur et à mesure que le dispositif de rendu change.
  • Autorisez une APO à contrôler le format du microphone d’entrée et du flux de référence.
  • Autorisez une APO à obtenir des horodatages sur le microphone et des flux de référence.

Approche précédente - Windows 10

Les APO sont des objets à entrée unique - sortie unique. Le moteur audio fournit un AEC APO l'audio provenant du point de terminaison du microphone en entrée. Pour obtenir le flux de référence, une APO peut interagir avec le pilote à l’aide d’interfaces propriétaires pour récupérer l’audio de référence à partir du point de terminaison de rendu, ou utiliser WASAPI pour ouvrir un flux de bouclage sur le point de terminaison de rendu.

Les deux approches ci-dessus présentent des inconvénients :

  • Un APO AEC qui utilise des canaux privés pour obtenir un flux de référence à partir du pilote, peut généralement le faire uniquement à partir du périphérique de rendu audio intégré. Par conséquent, l’annulation de l’écho ne fonctionnera pas si l’utilisateur joue de l’audio hors de l’appareil non intégré, tel que usb ou périphérique audio Bluetooth. Seul le système d’exploitation connaît les points de terminaison de rendu appropriés qui peuvent servir de points de terminaison de référence.

  • Une APO peut utiliser WASAPI pour sélectionner le point de terminaison de rendu par défaut pour effectuer une annulation d’écho. Toutefois, il faut être conscient des pièges lors de l’ouverture d’un flux de bouclage à partir du processus de audiodg.exe (là où l’APO est hébergée).

    • Le flux de bouclage ne peut pas être ouvert/détruit lorsque le moteur audio appelle les méthodes APO principales, car cela peut entraîner un blocage.
    • Un APO de capture ne connaît pas l’état des flux de ses clients. C’est-à-dire qu’une application de capture peut avoir un flux de capture dans l’état « STOP », mais l’APO n’est pas au courant de cet état et conserve donc le flux de bouclage ouvert dans l’état « RUN », ce qui est inefficace en termes de consommation d’énergie.

Définition de l’API - AEC

L’infrastructure AEC fournit de nouvelles structures et interfaces que les API peuvent tirer parti. Ces nouvelles structures et interfaces sont décrites ci-dessous.

structure de APO_CONNECTION_PROPERTY_V2

Les APOs qui implémentent l’interface IApoAcousticEchoCancellation recevront une structure APO_CONNECTION_PROPERTY_V2 lors de l'appel à IAudioProcessingObjectRT::APOProcess. Outre tous les champs de la structure APO_CONNECTION_PROPERTY , la version 2 de la structure fournit également des informations d’horodatage pour les mémoires tampons audio.

Une APO peut examiner le champ APO_CONNECTION_PROPERTY.u32Signature pour déterminer si la structure qu’elle reçoit du moteur audio est de type APO_CONNECTION_PROPERTY ou APO_CONNECTION_PROPERTY_V2. APO_CONNECTION_PROPERTY structures ont une signature de APO_CONNECTION_PROPERTY_SIGNATURE, tandis que APO_CONNECTION_PROPERTY_V2 ont une signature qui est égale à APO_CONNECTION_PROPERTY_V2_SIGNATURE. Si la signature a une valeur égale à APO_CONNECTION_PROPERTY_V2_SIGNATURE, le pointeur vers la structure APO_CONNECTION_PROPERTY peut être converti en toute sécurité en un pointeur APO_CONNECTION_PROPERTY_V2.

Le code suivant provient de l’exemple Aec APO MFX - AecApoMfx.cpp et montre la refonte.

    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

L’interface IApoAcousticEchoCancellation n’a aucune méthode explicite sur celle-ci. Son objectif est d’identifier une APO AEC au moteur audio. Cette interface peut uniquement être implémentée par des effets de mode (MFX) sur les points de terminaison de capture. L’implémentation de cette interface sur une autre APO entraîne un échec lors du chargement de cette APO. Pour obtenir des informations générales sur MFX, consultez Architecture des objets de traitement audio.

Si l’effet de mode sur un point de terminaison de capture est implémenté en tant que série d’API chaînées, seule l’APO la plus proche de l’appareil peut implémenter cette interface. Les APO qui implémentent cette interface recevront la structure APO_CONNECTION_PROPERTY_V2 lors de son appel à IAudioProcessingobjectRT::APOProcess. L'APO peut rechercher une signature APO_CONNECTION_PROPERTY_V2_SIGNATURE sur la propriété de connexion et convertir la structure APO_CONNECTION_PROPERTY entrante en une structure APO_CONNECTION_PROPERTY_V2.

En reconnaissance du fait que les API AEC exécutent généralement leurs algorithmes à un taux d’échantillonnage/nombre de canaux spécifique, le moteur audio fournit une prise en charge de rééchantillonnement aux API qui implémentent l’interface IApoAcousticEchoCancellation.

Lorsqu’une APO AEC retourne APOERR_FORMAT_NOT_SUPPORTED dans l’appel à IAudioProcessingObject ::OutInputFormatSupported, le moteur audio appellera À nouveau IAudioProcessingObject ::IsInputFormatSupported sur l’APO avec un format de sortie NULL et un format d’entrée non null, pour obtenir le format suggéré par l’APO. Le moteur audio rééchantille ensuite l’audio du microphone au format suggéré avant de l’envoyer à l’APO AEC. Cela élimine la nécessité pour l’APO AEC d’implémenter la conversion du taux d’échantillonnage et du nombre de canaux.

IApoAuxiliaryInputConfiguration

L’interface IApoAuxiliaryInputConfiguration fournit des méthodes que les API peuvent implémenter afin que le moteur audio puisse ajouter et supprimer des flux d’entrée auxiliaires.

Cette interface est implémentée par l’APO AEC et utilisée par le moteur audio pour initialiser l’entrée de référence. Dans Windows 11, l’APO AEC n’est initialisée qu’avec une seule entrée auxiliaire contenant le flux audio de référence pour l’annulation de l’écho. La méthode AddAuxiliaryInput sera utilisée pour ajouter l’entrée de référence à l’APO. Les paramètres d’initialisation contiennent une référence au point d'accès de rendu à partir duquel le flux retour est obtenu.

La méthode IsInputFormatSupported est appelée par le moteur audio pour négocier des formats sur l’entrée auxiliaire. Si l’APO AEC préfère un format spécifique, il peut retourner S_FALSE dans l’appel à IsInputFormatSupported et spécifier un format suggéré. Le moteur audio va rééchantillonner l'audio de référence au format suggéré et le fournir à l'entrée auxiliaire de l'APO AEC.

IApoAuxiliaryInputRT

L’interface IApoAuxiliaryInputRT est l’interface sécurisée en temps réel utilisée pour piloter les entrées auxiliaires d’une APO.

Cette interface est utilisée pour fournir des données audio sur l’entrée auxiliaire à l’APO. Notez que les entrées audio auxiliaires ne sont pas synchronisées avec les appels à IAudioProcessingObjectRT ::APOProcess. Lorsqu’il n’y a pas d’audio rendu sur le point de rendu, les données de bouclage ne sont pas disponibles à l’entrée auxiliaire. c’est-à-dire qu’il n’y aura aucun appel à IApoAuxiliaryInputRT ::AcceptInput

Résumé des API AEC CAPX

Pour plus d’informations, recherchez des informations supplémentaires sur les pages suivantes.

Exemple de code - AEC

Reportez-vous aux exemples de code Sysvad Audio AecApo suivants.

Le code suivant de l’exemple d’en-tête Aec APO - AecAPO.h montre les trois nouvelles méthodes publiques ajoutées.

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

Le code suivant provient de l’exemple Aec APO MFX - AecApoMfx.cpp et montre l’implémentation d’AddAuxiliaryInput, lorsque l’APO ne peut gérer qu’une seule entrée auxiliaire.

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;

Passez également en revue l’exemple de code qui montre l’implémentation de CAecApoMFX::IsInputFormatSupported et CAecApoMFX::AcceptInput, ainsi que la gestion de APO_CONNECTION_PROPERTY_V2.

Séquence d’opérations - AEC

Lors de l’initialisation :

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

Lors du changement du dispositif de rendu :

  1. IAudioProcessingObject::Initialiser
  2. IApoAuxiliaryInputConfiguration::AddAuxiliaryInput
  3. IAudioProcessingObjectConfiguration ::LockForProcess
  4. Modifications d’appareil par défaut
  5. IAudioProcessingObjectConfiguration ::UnlockForProcess
  6. IApoAuxiliaryInputConfiguration ::RemoveAuxiliaryInput
  7. IApoAuxiliaryInputConfiguration::AddAuxiliaryInput
  8. IAudioProcessingObjectConfiguration ::LockForProcess

Il s’agit du comportement de mémoire tampon recommandé pour AEC.

  • Les mémoires tampons obtenues dans l’appel à IApoAuxiliaryInputRT ::AcceptInput doivent être écrites dans une mémoire tampon circulaire sans verrouiller le thread principal.
  • Lors de l’appel à IAudioProcessingObjectRT ::APOProcess, la mémoire tampon circulaire doit être lue pour le dernier paquet audio du flux de référence, et ce paquet doit être utilisé pour l’exécution via l’algorithme d’annulation d’écho.
  • Les horodatages sur les données de référence et de microphone peuvent être utilisés pour aligner les données du haut-parleur et de micro.

Flux de bouclage de référence

Par défaut, le flux de bouclage « intercepte » (écoute) le flux audio avant l'application d'un réglage de volume ou d'une mise en sourdine. Un flux de bouclage intercepté avant l'application du volume est appelé flux de bouclage de pré-volume. Un avantage d’avoir un flux de bouclage avant le réglage du volume est un flux audio clair et uniforme, quel que soit le niveau de volume actuel.

Certains algorithmes AEC peuvent préférer obtenir un flux de retour ayant été connecté après tout traitement de volume, ce traitement incluant également la désactivation du son. Cette configuration est appelée bouclage post-volume.

Dans la prochaine version majeure des API Windows AEC, vous pouvez demander une bouclage post-volume sur des points de terminaison pris en charge.

Limites

Contrairement aux flux de bouclage de pré-volume, qui sont disponibles pour tous les points de terminaison de rendu, les flux de bouclage post-volume peuvent ne pas être disponibles sur tous les points de terminaison.

Demande de bouclage après volume

Les API AEC qui souhaitent utiliser le bouclage post-volume doivent implémenter l’interface IApoAcousticEchoCancellation2 .

Un AEC APO peut demander un bouclage post-volume en retournant l’indicateur APO_REFERENCE_STREAM_PROPERTIES_POST_VOLUME_LOOPBACK via le paramètre Properties dans le cadre de l'implémentation de l’interface IApoAcousticEchoCancellation2 ::GetDesiredReferenceStreamProperties.

Selon le point de terminaison de rendu actuellement utilisé, la boucle post-volume pourrait ne pas être disponible. Une APO AEC est avertie si la bouclage post-volume est utilisée lorsque sa méthode IApoAuxiliaryInputConfiguration ::AddAuxiliaryInput est appelée. Si le champ AcousticEchoCanceller_Reference_Input streamProperties contient APO_REFERENCE_STREAM_PROPERTIES_POST_VOLUME_LOOPBACK, le bouclage post-volume est utilisé.

Le code suivant de l'en-tête AecAPO.h de l'échantillon AEC APO montre les trois nouvelles méthodes publiques qui sont en cours d'ajout.

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;

L’extrait de code suivant provient de l’exemple AEC APO MFX : AecApoMfx.cpp et montre l’implémentation de GetDesiredReferenceStreamProperties et la partie pertinente de 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.

Framework de paramètres

L’infrastructure de paramètres permet aux API d’exposer des méthodes d’interrogation et de modification du magasin de propriétés pour les effets audio (« FX Property Store ») sur un point de terminaison audio. Cette infrastructure peut être utilisée par les API et par les applications de support matériel (HSA) qui souhaitent communiquer les paramètres à cette APO. Les HSA peuvent être des applications de plateforme Windows universelle (UWP) et nécessitent une fonctionnalité spéciale pour appeler les API dans l’infrastructure de paramètres. Pour plus d’informations sur les applications HSA, consultez les applications d’appareil UWP.

Structure de magasin de propriété Fx

Le nouveau magasin FxProperty a trois sous-magasins : Default, User et Volatile.

La sous-clé « Default » contient des propriétés d’effets personnalisés et est remplie à partir du fichier INF. Ces propriétés ne persistent pas entre les mises à niveau du système d’exploitation. Par exemple, les propriétés qui sont généralement définies dans un INF tiennent ici. Celles-ci seraient ensuite remplies à nouveau à partir de l'INF.

La sous-clé « Utilisateur » contient les paramètres utilisateur relatifs aux propriétés d’effets. Ces paramètres sont conservés par le système d’exploitation entre les mises à niveau et les migrations. Par exemple, toutes les présélections que l’utilisateur peut configurer sont censées conserver entre les mises à niveau.

La sous-clé « Volatile » contient des propriétés d’effets volatiles. Ces propriétés sont perdues lors du redémarrage de l’appareil et sont effacées chaque fois que le point de terminaison passe à actif. Celles-ci doivent contenir des propriétés de variante de temps (par exemple, en fonction des applications en cours d’exécution, de la posture de l’appareil, etc.) Par exemple, tous les paramètres qui dépendent de l’environnement actuel.

Pour réfléchir à la différence entre les paramètres utilisateur et les paramètres par défaut, il s'agit de déterminer si vous souhaitez que les propriétés soient conservées lors des mises à niveau du système d'exploitation et des pilotes. Les propriétés utilisateur sont conservées. Les propriétés par défaut seront remplies à nouveau à partir du fichier INF.

Contextes APO

L’infrastructure de paramètres CAPX permet à un auteur APO de regrouper les propriétés APO par contextes. Chaque APO peut définir son propre contexte et mettre à jour les propriétés par rapport à son propre contexte. Le magasin de propriétés d’effets pour un point de terminaison audio peut avoir zéro ou plusieurs contextes. Les fournisseurs sont libres de créer des contextes selon leur choix, que ce soit par SFX/MFX/EFX ou par mode. Un fournisseur peut également choisir d’avoir un contexte unique pour toutes les API fournies par ce fournisseur.

Fonctionnalité restreinte des paramètres

L’API de paramètres est destinée à prendre en charge tous les fabricants OEM et les développeurs HSA intéressés par l’interrogation et la modification des paramètres d’effets audio associés à un appareil audio. Cette API est exposée aux applications HSA et Win32 pour permettre l'accès au magasin de propriétés via la fonctionnalité restreinte « audioDeviceConfiguration » qui doit être déclarée dans le manifeste. En outre, un espace de noms correspondant doit être déclaré comme suit :

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

IAudioSystemEffectsPropertyStore est lisible et accessible en écriture par un service ISV/IHV, une application du Windows Store UWP, des applications de bureau sans privilèges administratifs et des APOs. En outre, cela peut servir de mécanisme permettant aux API de remettre des messages à un service ou à une application de magasin UWP.

Remarque

Il s’agit d’une fonctionnalité restreinte : si une application est soumise avec cette fonctionnalité au Microsoft Store, elle déclenche un examen étroit. L’application doit être une application de support matériel (HSA), et elle sera examinée pour évaluer qu’elle est en effet un HSA avant l’approbation de la soumission.

Définition de l’API - Framework de paramètres

La nouvelle interface IAudioSystemEffectsPropertyStore permet à un HSA d’accéder aux magasins de propriétés d’effets du système audio et de s’inscrire aux notifications de modification de propriété.

La fonction ActiveAudioInterfaceAsync fournit une méthode pour obtenir l’interface IAudioSystemEffectsPropertyStore de manière asynchrone.

Une application peut recevoir des notifications lorsque le système enregistre des modifications apportées au stockage des propriétés, en utilisant la nouvelle interface de rappel IAudioSystemEffectsPropertyChangeNotificationClient.

Application qui tente d’obtenir IAudioSystemEffectsPropertyStore à l’aide de IMMDevice ::Activate

L’exemple montre comment une application de support matériel peut utiliser IMMDevice ::Activate pour activer IAudioSystemEffectsPropertyStore. L’exemple montre comment utiliser IAudioSystemEffectsPropertyStore pour ouvrir un IPropertyStore qui a des paramètres utilisateur.

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

Exemple utilisant ActivateAudioInterfaceAsync

Cet exemple fait la même chose que l’exemple précédent, mais au lieu d’utiliser IMMDevice, il utilise l’API ActivateAudioInterfaceAsync pour obtenir l’interface IAudioSystemEffectsPropertyStore de manière asynchrone.

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 ::Initialize code using the IAudioSystemEffectsPropertyStore

L'exemple montre comment l'implémentation d'une APO peut utiliser la structure APOInitSystemEffects3 pour récupérer les interfaces utilisateur, par défaut et volatiles de IPropertyStore pour l'APO, lors de l'initialisation de l'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;
}

Enregistrement d’applications pour les notifications de modification de propriété

L’exemple illustre l’utilisation de l’inscription pour les notifications de modification de propriété. Cela ne doit pas être utilisé avec l’APO et doit être employé par les applications 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;
}

Exemple de code - Framework de paramètres

Cet exemple de code provient de l’exemple 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);
    }
}

Section INF - Cadre des paramètres

La syntaxe du fichier INF pour déclarer des propriétés d’effet à l’aide de la nouvelle infrastructure de paramètres CAPX est la suivante :

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

Cela remplace l’ancienne syntaxe pour déclarer les propriétés d’effet comme suit :

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

L’inf ne peut pas avoir à la fois l’entrée IAudioSystemEffectsPropertyStore et l’entrée IPropertyStore pour le même point de terminaison audio. Cela n’est pas pris en charge.

Exemple montrant l’utilisation du nouveau magasin de propriétés :

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

Infrastructure de notifications

L'infrastructure Notifications permet aux effets audio (APO) de demander et de gérer les notifications de changement du volume, du point de terminaison et du magasin des propriétés des effets audio. Cette infrastructure vise à remplacer les API existantes utilisées par les APO pour s'inscrire et se désinscrire des notifications.

La nouvelle API introduit une interface que les APO peuvent utiliser pour déclarer le type de notifications qui intéresse l'APO. Windows interroge l’APO pour les notifications qui lui intéressent et transfère la notification aux API. Les APO n’ont plus besoin d’appeler explicitement les API d’inscription ou de désinscription.

Les notifications sont remises à une APO à l’aide d’une file d’attente série. Le cas échéant, la première notification diffuse l’état initial de la valeur demandée (par exemple, le volume de point de terminaison audio). Les notifications s’arrêtent une fois que audiodg.exe cesse d’utiliser une APO pour la diffusion en continu. Les APO cesseront de recevoir des notifications après UnlockForProcess. Il est toujours nécessaire de synchroniser UnlockForProcess et toutes les notifications en cours.

Implémentation - Infrastructure de notifications

Pour tirer parti de l’infrastructure de notifications, une APO déclare les notifications qui lui intéressent. Il n’existe aucun appel d’inscription/annulation d’inscription explicite. Toutes les notifications envoyées à l’APO sont sérialisées et il est important de ne pas bloquer le thread de rappel de notification pendant trop longtemps.

Définition de l’API - Infrastructure de notifications

L’infrastructure de notification implémente une nouvelle interface IAudioProcessingObjectNotifications qui peut être implémentée par les clients pour inscrire et recevoir des notifications audio courantes pour les notifications de point de terminaison APO et d’effet système.

Pour plus d’informations, recherchez du contenu supplémentaire sur les pages suivantes :

Exemple de code - Infrastructure de notifications

L’exemple montre comment une APO peut implémenter l’interface IAudioProcessingObjectNotifications. Dans la méthode GetApoNotificationRegistrationInfo, l'exemple APO s'enregistre pour recevoir des notifications de modifications des magasins de propriétés des effets système.
La méthode HandleNotification est appelée par le système d’exploitation pour notifier l’APO des modifications qui correspondent à ce pour quoi l'APO s'était enregistré.

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

Le code suivant provient de l’exemple SWAP APO MFX : swapapomfx.cpp et affiche l’inscription aux événements, en retournant un tableau de 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;
}

Le code suivant provient de l’exemple SwapAPO MFX HandleNotifications - swapapomfx.cpp et montre comment gérer les notifications.

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 de journalisation

L’infrastructure de journalisation fournit aux développeurs APO des moyens supplémentaires de collecter des données pour améliorer le développement et le débogage. Ce framework unifie les différentes méthodes de journalisation utilisées par différents fournisseurs et les lie aux fournisseurs de journalisation de trace audio pour créer une journalisation plus significative. La nouvelle infrastructure fournit une API de journalisation, laissant le reste du travail à effectuer par le système d’exploitation.

Le fournisseur est défini comme suit :

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

Chaque APO a son propre identifiant d’activité. Étant donné que cela utilise le mécanisme de journalisation de trace existant, les outils de console existants peuvent être utilisés pour filtrer ces événements et les afficher en temps réel. Vous pouvez utiliser des outils existants tels que tracelog et tracefmt, comme décrit dans Tools for Software Tracing - Windows Drivers. Pour plus d’informations sur les sessions de suivi, consultez Création d’une session de trace avec un GUID de contrôle.

Les événements de journalisation des traces ne sont pas marqués comme des données de télémétrie et ne sont pas affichés en tant que fournisseur de données de télémétrie dans des outils tels que xperf.

Implémentation - Framework de journalisation

L'infrastructure de journalisation est basée sur les mécanismes de journalisation fournis par ETW tracing. Pour plus d’informations sur ETW, consultez Suivi d’événements. Cela n’est pas destiné à la journalisation des données audio, mais plutôt à journaliser les événements qui sont généralement journalisés en production. Les API de journalisation ne doivent pas être utilisées à partir du thread de streaming en temps réel, car elles peuvent entraîner la préemption du thread de pompe par le planificateur du processeur du système d’exploitation. La journalisation doit principalement être utilisée pour les événements qui aideront à résoudre les problèmes de débogage souvent trouvés dans le champ.

Définition de l’API - Framework de journalisation

L’infrastructure de journalisation introduit l’interface IAudioProcessingObjectLoggingService qui fournit un nouveau service de journalisation pour les API.

Pour plus d’informations, consultez IAudioProcessingObjectLoggingService.

Exemple de code - Framework de journalisation

L’exemple illustre l’utilisation de la méthode IAudioProcessingObjectLoggingService ::ApoLog et la façon dont ce pointeur d’interface est obtenu dans IAudioProcessingObject ::Initialize.

Exemple de journalisation 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

Le cadre de threading permettant aux effets d'être exécutés de manière parallèle utilise des files d'attente de travail via une tâche appropriée du service de planification de classe multimédia (MMCSS) par le biais d'une API simple. La création de files d’attente de travail série en temps réel et leur association avec le thread de pompe principal sont gérées par le système d’exploitation. Cette infrastructure permet aux API de mettre en file d’attente des éléments de travail courts. La responsabilité de la synchronisation entre les tâches incombe toujours à l’APO. Pour plus d’informations sur le thread MMCSS, consultez le service Planificateur de classes multimédias et l’API file d’attente de travailReal-Time.

Définitions d’API - Threading Framework

L’infrastructure Threading introduit l’interface IAudioProcessingObjectQueueService qui fournit l’accès à la file d’attente de travail en temps réel pour les API.

Pour plus d’informations, recherchez du contenu supplémentaire sur les pages suivantes :

Exemple de code - Threading Framework

Cet exemple illustre l’utilisation de la méthode IAudioProcessingObjectRTQueueService ::GetRealTimeWorkQueue et la façon dont le pointeur d’interface IAudioProcessingObjectRTQueueService est obtenu dans 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;
}

Pour obtenir d’autres exemples d’utilisation de cette interface, consultez l’exemple de code suivant :

Détection et contrôle des effets audio

Le cadre de découverte permet au système d'exploitation de contrôler les effets audio sur son flux. Ces API prennent en charge les scénarios dans lesquels l’utilisateur d’une application doit contrôler certains effets sur les flux (par exemple, la suppression profonde du bruit). Pour ce faire, cette infrastructure ajoute les éléments suivants :

  • Nouvelle API à interroger à partir d’une APO pour déterminer si un effet audio peut être activé ou désactivé.
  • Nouvelle API pour définir l’état d’un effet audio activé/désactivé.
  • Notification lorsqu’il existe une modification dans la liste des effets audio ou lorsque les ressources deviennent disponibles afin qu’un effet audio puisse désormais être activé/désactivé.

Implémentation - Découverte d’effets audio

Une APO doit implémenter l’interface IAudioSystemEffects3 si elle a l’intention d’exposer des effets qui peuvent être activés dynamiquement et désactivés. Une APO expose ses effets audio via la fonction IAudioSystemEffects3 ::GetControllableSystemEffectsList et active et désactive ses effets audio via la fonction IAudioSystemEffects3 ::SetAudioSystemEffectState .

Exemple de code - Détection d’effets audio

L’exemple de code de découverte d’effet audio se trouve dans l’exemple SwapAPOSFX - swapaposfx.cpp.

L’exemple de code suivant montre comment récupérer la liste des effets configurables. Exemple 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;
}

L’exemple de code suivant montre comment activer et désactiver des effets. Exemple de 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;
}

Réutilisation des API WM SFX et MFX dans Windows 11, version 22H2

À compter de Windows 11, version 22H2, les fichiers de configuration INF qui réutilisent les API WM SFX et MFX de boîte de réception, peuvent désormais réutiliser les API CAPX SFX et MFX. Cette section décrit les trois façons de procéder.

Il existe trois points d’insertion pour les API : le rendu pré-mix, le rendu post-mix et la capture. Le moteur audio de chaque appareil logique prend en charge une instance d'une APO de rendu pré-mixage par flux (effets sonores de rendu) et une APO de rendu post-mixage (MFX). Le moteur audio prend également en charge une instance d'APO de capture (capture SFX) insérée dans chaque flux de capture. Pour plus d’informations sur la réutilisation ou l’habillage des API de boîte de réception, consultez Combiner des API personnalisées et Windows.

Les API CAPX SFX et MFX peuvent être réutilisées de l’une des trois manières suivantes.

Utilisation de la section DDInstall INF

Utilisez mssysfx. CopyFilesAndRegisterCapX de wdmaudio.inf en ajoutant les entrées suivantes.

   Include=wdmaudio.inf
   Needs=mssysfx.CopyFilesAndRegisterCapX

Utilisation d’un fichier INF d’extension

Wdmaudioapo.inf est l’extension de classe AudioProcessingObject inf. Il contient l’inscription spécifique à l’appareil des API SFX et MFX.

Référencer directement les API WM SFX et MFX pour les effets de flux et de mode

Pour référencer directement ces API pour les effets de flux et de mode, utilisez les valeurs GUID suivantes.

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

SFX (Stream) et MFX (mode) ont été référencés dans Windows 8.1 à LFX (local) et MFX a été appelé GFX (global). Ces entrées de Registre continuent d’utiliser les noms précédents.

L'enregistrement spécifique à l'appareil utilise HKR au lieu de HKCR.

Le fichier INF doit avoir les entrées suivantes ajoutées.

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

Ces entrées de fichier INF créeront un magasin de propriétés qui sera utilisé par les API Windows 11 pour les nouveaux APO.

PKEY_FX_Association dans l’exemple INF. HKR,"FX\\0",%PKEY_FX_Association%,,%KSNODETYPE_ANY%, doit être remplacé par HKR,"FX\\0\\%WMALFXGFXAPO_Context%",%PKEY_FX_Association%,,%KSNODETYPE_ANY%.

Voir aussi

Objets de traitement audio Windows.