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 du signal numérique basés sur l’hôte. Ces effets sont empaquetés sous forme d’objets de traitement audio (APO) en mode utilisateur. Pour plus d’informations, consultez Objets de traitement audio Windows.

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

  • L’infrastructure AEC (Acoustic Echo Cancellation) permet à un 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 pour gérer les modifications de magasin de propriétés de volume, de point de terminaison et d’effets audio.
  • L’infrastructure de journalisation facilite le développement et le débogage d’API.
  • L’infrastructure Threading permet aux APO d’être multithread à l’aide d’un pool de threads géré par le système d’exploitation, inscrit à MMCSS.
  • Les API de découverte 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 API doivent utiliser la nouvelle interface IAudioSystemEffects3 . Lorsqu’un APO implémente cette interface, le système d’exploitation l’interprète comme un signal implicite indiquant que l’APO prend en charge l’infrastructure APO Settings et permet à APO de s’abonner aux notifications audio courantes à partir du moteur audio.

Windows 11 exigences de développement APO CAPX

Toutes les nouvelles API livrées 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 d’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 principales (Paramètres, Journalisation, Notifications, Threading, AEC) sont censées tirer parti des API CAPX. Cela sera validé par le biais des tests Windows 11 HLK. Par exemple, si un APO utilise les données du Registre pour enregistrer les paramètres au lieu d’utiliser Settings Framework, 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 ne prend pas en charge ces API. Si un APO a l’intention de fonctionner à la fois sur Windows 10 et sur Windows 11, il peut déterminer s’il est initialisé avec la structure APOInitSystemEffects2 ou 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, du 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 avec Microsoft via l’Espace partenaires peuvent également accéder à ce contenu via Collaborer. Pour plus d’informations sur Collaborer, consultez Présentation de Microsoft Collaborate.

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

L’exemple de code pour le contenu décrit dans cette rubrique est disponible ici : Audio/SYSVAD/APO - github

Annulation de l’écho acoustique (AEC)

L’annulation de l’écho acoustique (AEC) est un effet audio courant implémenté par les fournisseurs de matériel indépendant (IHVs) et les éditeurs de logiciels indépendants (ISV) en tant qu’objet de traitement audio (APO) dans le pipeline de capture de 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 en ce qu’il nécessite 2 entrées : un flux audio à partir du microphone et un flux audio à partir d’un périphérique de rendu qui fait office de signal de référence.

Ce nouvel ensemble d’interfaces permet à AEC APO de s’identifier comme tel au moteur audio. Ainsi, le moteur audio configure correctement l’APO avec plusieurs entrées et une seule sortie.

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

  • 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é.
  • Désactiver les flux de référence à mesure que l’appareil de rendu change.
  • Autoriser un APO à contrôler le format du microphone d’entrée et du flux de référence.
  • Autoriser un APO à obtenir des horodatages sur le microphone et les flux de référence.

Approche précédente - Windows 10

Les API sont des objets d’entrée unique: un seul objet de sortie. Le moteur audio fournit une APO AEC l’audio du point de terminaison du microphone à son entrée. Pour obtenir le flux de référence, un 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 :

  • Une APO AEC qui utilise des canaux privés pour obtenir un flux de référence à partir du pilote ne peut généralement le faire qu’à partir du périphérique de rendu audio intégré. Par conséquent, l’annulation de l’écho ne fonctionne pas si l’utilisateur lit l’audio à partir de l’appareil non intégré, tel qu’un périphérique audio USB ou 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.

  • Un APO peut utiliser WASAPI pour choisir le point de terminaison de rendu par défaut pour effectuer l’annulation de l’écho. Toutefois, il existe certains pièges à connaître lors de l’ouverture d’un flux de bouclage à partir du processus audiodg.exe (où l’APO est hébergé).

    • Le flux de bouclage ne peut pas être ouvert/détruit lorsque le moteur audio appelle les méthodes APO main, car cela peut entraîner un blocage.
    • Une 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 à l’état « STOP », mais l’APO ignore cet état et maintient donc le flux de bouclage ouvert à l’état « RUN », ce qui est inefficace en termes de consommation d’énergie.

Définition d’API - AEC

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

structure APO_CONNECTION_PROPERTY_V2

Les API qui implémentent l’interface IApoAcousticEchoCancellation se verront transmettre une structure APO_CONNECTION_PROPERTY_V2 dans son appel à IAudioProcessingObjectRT::APOProcess. En plus de 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 é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 tapé en toute sécurité vers 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 ne contient aucune méthode explicite. Son objectif est d’identifier une APO AEC au moteur audio. Cette interface peut uniquement être implémentée par les effets de mode (MFX) sur les points de terminaison de capture. L’implémentation de cette interface sur n’importe quelle autre APO entraîne un échec dans le 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é sous la forme d’une série d’API chaînées, seul l’APO le plus proche de l’appareil peut implémenter cette interface. Les API qui implémentent cette interface se voient offrir la structure APO_CONNECTION_PROPERTY_V2 dans son appel à IAudioProcessingobjectRT::APOProcess. L’APO peut case activée pour une signature APO_CONNECTION_PROPERTY_V2_SIGNATURE sur la propriété de connexion et taper la structure de APO_CONNECTION_PROPERTY entrante dans une structure de 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 la prise en charge du rééchantillonnage des APO qui implémentent l’interface IApoAcousticEchoCancellation.

Lorsqu’une APO AEC retourne APOERR_FORMAT_NOT_SUPPORTED dans l’appel à IAudioProcessingObject::OutInputFormatSupported, le moteur audio appelle À 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 APO. Le moteur audio rééchantillonne 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 le taux d’échantillonnage et la conversion 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é qu’avec une seule entrée auxiliaire, qui contient 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 de terminaison de rendu à partir duquel le flux de bouclage 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 rééchantillonne l’audio de référence au format suggéré et le fournit à 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’aucun audio n’est rendu dans le point de terminaison 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, consultez 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 APO Aec - 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 de 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 de la modification de l’appareil de rendu :

  1. IAudioProcessingObject::Initialize
  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 lors de l’appel à IApoAuxiliaryInputRT::AcceptInput doivent être écrites dans une mémoire tampon circulaire sans verrouiller le thread main.
  • 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 des données de référence et du microphone peuvent être utilisés pour aligner les données du haut-parleur et du micro.

Flux de bouclage de référence

Par défaut, le flux de bouclage « appuie » (écoute) le flux audio avant l’application d’un volume ou d’un muting. Un flux de bouclage appuyé avant l’application du volume est appelé flux de bouclage pré-volume. L’avantage d’avoir un flux de bouclage avant le volume est un flux audio clair et uniforme, quel que soit le paramètre de volume actuel.

Certains algorithmes AEC peuvent préférer obtenir un flux de bouclage qui a été connecté après tout traitement de volume (y compris en sourdine). Cette configuration est appelée bouclage post-volume.

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

Limites

Contrairement aux flux de bouclage 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 post-volume

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

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

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

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

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 d’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 des paramètres

Le Framework de paramètres permet aux APO 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. 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 plateforme Windows universelle applications (UWP) et nécessitent une fonctionnalité spéciale pour appeler les API dans l’infrastructure des paramètres. Pour plus d’informations sur les applications HSA, consultez Applications d’appareil UWP.

Structure du magasin FxProperty

Le nouveau magasin FxProperty a trois sous-magasins : Par défaut, Utilisateur et Volatile.

La sous-clé « Default » contient des propriétés d’effets personnalisés et est renseignée à partir du fichier INF. Ces propriétés ne sont pas conservées 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 s’adapteraient ici. Celles-ci sont ensuite réappréplies à partir de l’INF.

La sous-clé « Utilisateur » contient les paramètres utilisateur relatifs aux propriétés des 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 et qui sont censées persister au cours de la mise à 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. Ceux-ci sont censés 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.

La façon de considérer l’utilisateur par rapport à la valeur par défaut est de savoir si vous souhaitez que les propriétés persistent entre les mises à niveau du système d’exploitation et des pilotes. Les propriétés utilisateur seront conservées. Les propriétés par défaut seront renseignées à nouveau à partir de l’INF.

Contextes APO

Le framwork des 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 comme ils le souhaitent, que ce soit par SFX/MFX/EFX ou par mode. Un fournisseur peut également choisir d’avoir un seul contexte pour toutes les API livrées par ce fournisseur.

Fonctionnalité restreinte des paramètres

L’API de paramètres est destinée à prendre en charge tous les oem et les développeurs HSA intéressés par l’interrogation et la modification des paramètres d’effets audio associés à un périphérique audio. Cette API est exposée à des applications HSA et Win32 pour fournir 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 de magasin UWP, des applications de bureau non-administrateur et des API. En outre, cela peut servir de mécanisme permettant aux API de remettre des messages à un service ou à une application de magasin UWP.

Notes

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

Définition d’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 magasin de propriétés des effets système change, à l’aide de la nouvelle interface de rappel IAudioSystemEffectsPropertyChangeNotificationClient .

Application tentant d’obtenir le IAudioSystemEffectsPropertyStore à l’aide d’IMMDevice::Activate

L’exemple montre comment une application de prise en charge matérielle 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 à l’aide de IAudioSystemEffectsPropertyStore

L’exemple illustre l’implémentation d’une APO peut utiliser la structure APOInitSystemEffects3 pour récupérer les interfaces IPropertyStore utilisateur, par défaut et volatile pour l’APO, pendant 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 dentified 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;
}

Inscription de l’application pour les notifications de modification de propriété

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

// Unsubscrbe 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 d’APO d’échange SFX sysvad - 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 - Framework des paramètres

La syntaxe de 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 la déclaration des 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 des notifications de modification de magasin de propriétés de volume, de point de terminaison et d’effets audio. Cette infrastructure est destinée à remplacer les API existantes utilisées par les API pour s’inscrire et annuler l’inscription aux notifications.

La nouvelle API introduit une interface que les API peuvent utiliser pour déclarer le type de notifications qui intéresse APO. Windows interroge l’APO pour connaître les notifications qui l’intéressent et transfère la notification aux API. Les API 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 du point de terminaison audio). Les notifications s’arrêtent une fois audiodg.exe l’intention d’utiliser une apo pour la diffusion en continu. Les API cesseront de recevoir des notifications après UnlockForProcess. Il est toujours nécessaire de synchroniser UnlockForProcess et toutes les notifications en cours d’exécution.

Implémentation - Infrastructure de notifications

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

Définition d’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 dans les pages suivantes :

Exemple de code - Infrastructure de notifications

L’exemple montre comment un APO peut implémenter l’interface IAudioProcessingObjectNotifications. Dans la méthode GetApoNotificationRegistrationInfo, l’exemple APO s’inscrit pour recevoir des notifications aux modifications apportées aux magasins de propriétés d’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 que l’APO s’était inscrit.

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 dentified 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 a 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 ficticious
        // 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 montre 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 a 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. Cette infrastructure unifie les différentes méthodes de journalisation utilisées par différents fournisseurs et la lie aux fournisseurs de journalisation des traces audio pour créer une journalisation plus significative. La nouvelle infrastructure fournit une API de journalisation, ce qui laisse 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 ID d’activité. Étant donné que cela utilise le mécanisme de journalisation des traces 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 Outils pour le suivi logiciel - Pilotes Windows. 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 de trace 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 - Logging Framework

L’infrastructure de journalisation est basée sur les mécanismes de journalisation fournis par le suivi ETW. 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 enregistrés en production. Les API de journalisation ne doivent pas être utilisées à partir du thread de streaming en temps réel, car elles ont le potentiel d’empêcher le thread de pompe d’être préempté par le planificateur du processeur du système d’exploitation. La journalisation doit principalement être utilisée pour les événements qui facilitent le débogage des problèmes qui se trouvent souvent dans le champ.

Définition d’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 comment 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 Initializion completed");
    }
    return S_OK;
}

Infrastructure de thread

Infrastructure de thread permettant aux effets d’être multithread à l’aide de files d’attente de travail à partir d’une tâche MMCSS (Multimedia Class Scheduler Service) appropriée via 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 main 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 à court terme. La synchronisation entre les tâches continue d’être de la responsabilité de l’APO. Pour plus d’informations sur les threads MMCSS, consultez Service planificateur de classes multimédia et API de file d’attente de travail en temps réel.

Définitions d’API - Infrastructure threading

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 dans les pages suivantes :

Exemple de code - Infrastructure threading

Cet exemple illustre l’utilisation de la méthode IAudioProcessingObjectRTQueueService::GetRealTimeWorkQueue et comment 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 pour les effets

L’infrastructure de découverte permet au système d’exploitation de contrôler les effets audio sur leur 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, suppression de bruit profond). Pour ce faire, cette infrastructure ajoute les éléments suivants :

  • Nouvelle API permettant d’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 sur activé/désactivé.
  • Notification en cas de modification de la liste des effets audio ou lorsque des ressources deviennent disponibles afin qu’un effet audio puisse désormais être activé/désactivé.

Implémentation - Découverte des effets audio

Une APO doit implémenter l’interface IAudioSystemEffects3 si elle a l’intention d’exposer des effets qui peuvent être activés et désactivés dynamiquement. 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écouverte 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 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 périphérique logique prend en charge une instance d’une APO de rendu pré-mix par flux (render SFX) et d’une apo de rendu post-mix (MFX). Le moteur audio prend également en charge un instance d’une apo de capture (capture SFX) insérée dans chaque flux de capture. Pour plus d’informations sur la façon de réutiliser ou d’encapsuler les 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 façons suivantes.

Utilisation de la section INF DDInstall

Utilisez mssysfx. CopyFilesAndRegisterCapX à partir 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érence directe aux 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 APO SFX WM
  • Utilisez {13AB3EBD-137E-4903-9D89-60BE8277FD17} comme apo WM MFX.

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

L’inscription spécifique à l’appareil utilise HKR au lieu de HKCR.

Les éléments suivants doivent être ajoutés au fichier INF.

  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éent un magasin de propriétés qui sera utilisé par les API Windows 11 pour les nouvelles API.

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.