Topologies d’appareils
L’API DeviceTopology permet aux clients de contrôler diverses fonctions internes des adaptateurs audio auxquels ils ne peuvent pas accéder via l’API MMDevice, WASAPIou l’API EndpointVolume .
Comme expliqué précédemment, l’API MMDevice, WASAPIet l’API EndpointVolume présentent des microphones, des haut-parleurs, des casques et d’autres périphériques d’entrée et de sortie audio aux clients en tant qu’appareils de point de terminaison audio . Le modèle d’appareil de point de terminaison fournit aux clients un accès pratique aux contrôles de volume et de désactivation du son dans les appareils audio. Les clients qui nécessitent uniquement ces contrôles simples peuvent éviter de parcourir les topologies internes des périphériques matériels dans les adaptateurs audio.
Dans Windows Vista, le moteur audio configure automatiquement les topologies d’appareils audio à utiliser par les applications audio. Ainsi, les applications doivent rarement, si jamais, utiliser l’API DeviceTopology à cet effet. Par exemple, supposons qu’une carte audio contient un multiplexeur d’entrée qui peut capturer un flux à partir d’une entrée de ligne ou d’un microphone, mais qui ne peut pas capturer les flux des deux appareils de point de terminaison en même temps. Supposons que l’utilisateur a activé les applications en mode exclusif pour préempter l’utilisation d’un appareil de point de terminaison audio par des applications en mode partagé, comme décrit dans Exclusive-Mode Flux. Si une application en mode partagé enregistre un flux à partir de l’entrée de ligne au moment où une application en mode exclusif commence à enregistrer un flux à partir du microphone, le moteur audio bascule automatiquement le multiplexeur de l’entrée de ligne vers le microphone. En revanche, dans les versions antérieures de Windows, y compris Windows XP, l’application en mode exclusif de cet exemple utilise les fonctions mixerXxx dans l’API multimédia Windows pour parcourir les topologies des périphériques d’adaptateur, découvrir le multiplexeur et configurer le multiplexeur pour sélectionner l’entrée du microphone. Dans Windows Vista, ces étapes ne sont plus nécessaires.
Toutefois, certains clients peuvent nécessiter un contrôle explicite sur les types de contrôles matériels audio qui ne sont pas accessibles via l’API MMDevice, WASAPI ou l’API EndpointVolume. Pour ces clients, l’API DeviceTopology permet de parcourir les topologies des appareils adaptateurs pour découvrir et gérer les contrôles audio dans les appareils. Les applications qui utilisent l’API DeviceTopology doivent être conçues avec soin pour éviter d’interférer avec la stratégie audio Windows et déranger les configurations internes des appareils audio partagés avec d’autres applications. Pour plus d’informations sur la stratégie audio Windows, consultez User-Mode composants audio.
L’API DeviceTopology fournit des interfaces pour découvrir et gérer les types de contrôles audio suivants dans une topologie d’appareil :
- Contrôle de gain automatique
- Contrôle Bass
- Sélecteur d’entrée (multiplexeur)
- Contrôle de l’intensité
- Contrôle Midrange
- Désactiver le contrôle
- Sélecteur de sortie (demultiplexer)
- Compteur de pointe
- Contrôle treble
- Contrôle de volume
En outre, l’API DeviceTopology permet aux clients d’interroger les appareils d’adaptateur pour obtenir des informations sur les formats de flux qu’ils prennent en charge. Le fichier d’en-tête Devicetopology.h définit les interfaces dans l’API DeviceTopology.
Le diagramme suivant montre un exemple de plusieurs topologies d’appareils connectés pour la partie d’une carte PCI qui capture l’audio à partir d’un microphone, d’une entrée de ligne et d’un lecteur CD.
Le diagramme précédent montre les chemins de données qui mènent des entrées analogiques au bus système. Chacun des appareils suivants est représenté en tant qu’objet de topologie d’appareil avec une interface IDeviceTopology :
- Appareil de capture d’ondes
- Appareil multiplexeur d’entrée
- Appareil de point de terminaison A
- Appareil de point de terminaison B
Notez que le diagramme de topologie combine les périphériques d’adaptateur (les appareils de capture d’onde et de multiplexeur d’entrée) avec les appareils de point de terminaison. Grâce aux connexions entre les appareils, les données audio passent d’un appareil à l’autre. Sur chaque côté d’une connexion est un connecteur (étiqueté Con dans le diagramme) par le biais duquel les données entrent ou quittent un appareil.
Sur le bord gauche du diagramme, les signaux provenant des jacks d’entrée de ligne et de microphone entrent dans les appareils de point de terminaison.
À l’intérieur de l’appareil de capture d’ondes et le multiplexeur d’entrée sont des fonctions de traitement de flux, qui, dans la terminologie de l’API DeviceTopology, sont appelées sous-unités. Les types de sous-unités suivants apparaissent dans le diagramme précédent :
- Contrôle de volume (intitulé Vol)
- Contrôle Mute (intitulé Mute)
- Multiplexeur (ou sélecteur d’entrée ; étiqueté MUX)
- Convertisseur analogique à numérique (étiqueté ADC)
Les paramètres dans le volume, le son et les sous-unités multiplexeurs peuvent être contrôlés par les clients, et l’API DeviceTopology fournit des interfaces de contrôle aux clients pour les contrôler. Dans cet exemple, la sous-unité ADC n’a aucun paramètre de contrôle. Ainsi, l’API DeviceTopology ne fournit aucune interface de contrôle pour l’ADC.
Dans la terminologie de l’API DeviceTopology, les connecteurs et les sous-unités appartiennent à la même catégorie générale : parties. Toutes les parties, qu’elles soient des connecteurs ou des sous-unités, fournissent un ensemble commun de fonctions. L’API DeviceTopology implémente une interface IPart pour représenter les fonctions génériques communes aux connecteurs et aux sous-unités. L’API implémente les interfaces IConnector et ISubunit pour représenter les aspects spécifiques des connecteurs et des sous-unités.
L’API DeviceTopology construit les topologies de l’appareil de capture d’ondes et du multiplexeur d’entrée à partir des filtres KS (kernel-streaming) que le pilote audio expose au système d’exploitation pour représenter ces appareils. (Le pilote de carte audio implémente IMiniportWaveXxx et interfaces iMiniportTopology pour représenter les parties dépendantes du matériel de ces filtres ; pour plus d’informations sur ces interfaces et sur les filtres KS, consultez la documentation windows DDK.)
L’API DeviceTopology construit des topologies triviales pour représenter les appareils de point de terminaison A et B dans le diagramme précédent. La topologie d’appareil d’un appareil de point de terminaison se compose d’un seul connecteur. Cette topologie est simplement un espace réservé pour l’appareil de point de terminaison et ne contient aucune sous-unité pour le traitement des données audio. En fait, les périphériques d’adaptateur contiennent toutes les sous-unités utilisées par les applications clientes pour contrôler le traitement audio. La topologie d’appareil d’un appareil de point de terminaison sert principalement de point de départ pour explorer les topologies d’appareils d’adaptateur.
Les connexions internes entre deux parties d’une topologie d’appareil sont appelées liens. L’API DeviceTopology fournit des méthodes pour parcourir les liens d’une partie à l’autre dans une topologie d’appareil. L’API fournit également des méthodes pour parcourir les connexions entre les topologies d’appareil.
Pour commencer l’exploration d’un ensemble de topologies d’appareils connectés, une application cliente active l’interface IDeviceTopology d’un appareil de point de terminaison audio. Le connecteur d’un appareil de point de terminaison se connecte à un connecteur d’une carte audio ou à un réseau. Si le point de terminaison se connecte à un appareil sur une carte audio, les méthodes de l’API DeviceTopology permettent à l’application de parcourir la connexion du point de terminaison à l’adaptateur en obtenant une référence à l’interface IDeviceTopology de l’appareil adaptateur sur l’autre côté de la connexion. Un réseau, d’autre part, n’a pas de topologie d’appareil. Une connexion réseau canalise un flux audio vers un client qui accède au système à distance.
L’API DeviceTopology fournit un accès uniquement aux topologies des périphériques matériels d’une carte audio. Les appareils externes sur le bord gauche du diagramme et les composants logiciels sur le bord droit dépassent la portée de l’API. Les lignes en pointillés de chaque côté du diagramme représentent les limites de l’API DeviceTopology. Le client peut utiliser l’API pour explorer un chemin de données qui s’étend du jack d’entrée au bus système, mais l’API ne peut pas pénétrer au-delà de ces limites.
Chaque connecteur du diagramme précédent a un type de connexion associé qui indique le type de connexion que le connecteur effectue. Ainsi, les connecteurs sur les deux côtés d’une connexion ont toujours des types de connexion identiques. Le type de connexion est indiqué par une valeur d’énumération ConnectorType : Physical_External, Physical_Internal, Software_Fixed, Software_IO ou Réseau. Les connexions entre l’appareil multiplexeur d’entrée et les appareils de point de terminaison A et B sont de type Physical_External, ce qui signifie que la connexion représente une connexion physique à un appareil externe (en d’autres termes, une prise audio accessible par l’utilisateur). La connexion au signal analogique à partir du lecteur CD interne est de type Physical_Internal, ce qui indique une connexion physique à un appareil auxiliaire installé à l’intérieur du châssis système. La connexion entre l’appareil de capture d’ondes et l’appareil multiplexeur d’entrée est de type Software_Fixed, ce qui indique une connexion permanente qui est fixe et ne peut pas être configurée sous contrôle logiciel. Enfin, la connexion au bus système sur le côté droit du diagramme est de type Software_IO, ce qui indique que les E/S de données pour la connexion sont implémentées par un moteur DMA sous contrôle logiciel. (Le diagramme n’inclut pas d’exemple de type de connexion réseau.)
Le client commence à parcourir un chemin de données sur l’appareil de point de terminaison. Tout d’abord, le client obtient une interfaceIMMDevicequi représente l’appareil de point de terminaison, comme expliqué dans énumération des appareils audio. Pour obtenir l’interface IDeviceTopology pour l’appareil de point de terminaison, le client appelle la méthode IMMDevice ::Activate avec le paramètre iid défini sur REFIID IID_IDeviceTopology.
Dans l’exemple du diagramme précédent, l’appareil de multiplexeur d’entrée contient tous les contrôles matériels (volume, muet et multiplexeur) pour les flux de capture à partir des jacks d’entrée de ligne et de microphone. L’exemple de code suivant montre comment obtenir l’interface IDeviceTopology pour l’appareil multiplexeur d’entrée à partir de l’interface IMMDevice pour l’appareil de point de terminaison pour l’entrée de ligne ou le microphone :
//-----------------------------------------------------------
// The input argument to this function is a pointer to the
// IMMDevice interface of an endpoint device. The function
// outputs a pointer (counted reference) to the
// IDeviceTopology interface of the adapter device that
// connects to the endpoint device.
//-----------------------------------------------------------
#define EXIT_ON_ERROR(hres) \
if (FAILED(hres)) { goto Exit; }
#define SAFE_RELEASE(punk) \
if ((punk) != NULL) \
{ (punk)->Release(); (punk) = NULL; }
const IID IID_IDeviceTopology = __uuidof(IDeviceTopology);
const IID IID_IPart = __uuidof(IPart);
HRESULT GetHardwareDeviceTopology(
IMMDevice *pEndptDev,
IDeviceTopology **ppDevTopo)
{
HRESULT hr = S_OK;
IDeviceTopology *pDevTopoEndpt = NULL;
IConnector *pConnEndpt = NULL;
IConnector *pConnHWDev = NULL;
IPart *pPartConn = NULL;
// Get the endpoint device's IDeviceTopology interface.
hr = pEndptDev->Activate(
IID_IDeviceTopology, CLSCTX_ALL,
NULL, (void**)&pDevTopoEndpt);
EXIT_ON_ERROR(hr)
// The device topology for an endpoint device always
// contains just one connector (connector number 0).
hr = pDevTopoEndpt->GetConnector(0, &pConnEndpt);
EXIT_ON_ERROR(hr)
// Use the connector in the endpoint device to get the
// connector in the adapter device.
hr = pConnEndpt->GetConnectedTo(&pConnHWDev);
EXIT_ON_ERROR(hr)
// Query the connector in the adapter device for
// its IPart interface.
hr = pConnHWDev->QueryInterface(
IID_IPart, (void**)&pPartConn);
EXIT_ON_ERROR(hr)
// Use the connector's IPart interface to get the
// IDeviceTopology interface for the adapter device.
hr = pPartConn->GetTopologyObject(ppDevTopo);
Exit:
SAFE_RELEASE(pDevTopoEndpt)
SAFE_RELEASE(pConnEndpt)
SAFE_RELEASE(pConnHWDev)
SAFE_RELEASE(pPartConn)
return hr;
}
La fonction GetHardwareDeviceTopology dans l’exemple de code précédent effectue les étapes suivantes pour obtenir l’interface IDeviceTopology pour l’appareil multiplexeur d’entrée :
- Appelez la méthode IMMDevice ::Activate pour obtenir l’interface IDeviceTopology pour l’appareil de point de terminaison.
- Avec l’interface IDeviceTopology obtenue à l’étape précédente, appelez la méthode IDeviceTopology ::GetConnector pour obtenir l’interface IConnector du connecteur unique (numéro de connecteur 0) dans l’appareil de point de terminaison.
- Avec l’interface IConnector obtenue à l’étape précédente, appelez la méthode IConnector ::GetConnectedTo pour obtenir l’interface IConnector du connecteur dans le multiplexeur d’entrée.
- Interrogez l’interface IConnector obtenue à l’étape précédente pour son interface IPart.
- Avec l’interface IPart obtenue à l’étape précédente, appelez la méthode IPart ::GetTopologyObject pour obtenir l’interface IDeviceTopology pour l’appareil multiplexeur d’entrée.
Avant que l’utilisateur puisse enregistrer à partir du microphone dans le diagramme précédent, l’application cliente doit s’assurer que le multiplexeur sélectionne l’entrée du microphone. L’exemple de code suivant montre comment un client peut parcourir le chemin des données à partir du microphone jusqu’à ce qu’il trouve le multiplexeur, qu’il programme ensuite pour sélectionner l’entrée du microphone :
//-----------------------------------------------------------
// The input argument to this function is a pointer to the
// IMMDevice interface for a capture endpoint device. The
// function traverses the data path that extends from the
// endpoint device to the system bus (for example, PCI)
// or external bus (USB). If the function discovers a MUX
// (input selector) in the path, it selects the MUX input
// that connects to the stream from the endpoint device.
//-----------------------------------------------------------
#define EXIT_ON_ERROR(hres) \
if (FAILED(hres)) { goto Exit; }
#define SAFE_RELEASE(punk) \
if ((punk) != NULL) \
{ (punk)->Release(); (punk) = NULL; }
const IID IID_IDeviceTopology = __uuidof(IDeviceTopology);
const IID IID_IPart = __uuidof(IPart);
const IID IID_IConnector = __uuidof(IConnector);
const IID IID_IAudioInputSelector = __uuidof(IAudioInputSelector);
HRESULT SelectCaptureDevice(IMMDevice *pEndptDev)
{
HRESULT hr = S_OK;
DataFlow flow;
IDeviceTopology *pDeviceTopology = NULL;
IConnector *pConnFrom = NULL;
IConnector *pConnTo = NULL;
IPart *pPartPrev = NULL;
IPart *pPartNext = NULL;
IAudioInputSelector *pSelector = NULL;
if (pEndptDev == NULL)
{
EXIT_ON_ERROR(hr = E_POINTER)
}
// Get the endpoint device's IDeviceTopology interface.
hr = pEndptDev->Activate(
IID_IDeviceTopology, CLSCTX_ALL, NULL,
(void**)&pDeviceTopology);
EXIT_ON_ERROR(hr)
// The device topology for an endpoint device always
// contains just one connector (connector number 0).
hr = pDeviceTopology->GetConnector(0, &pConnFrom);
SAFE_RELEASE(pDeviceTopology)
EXIT_ON_ERROR(hr)
// Make sure that this is a capture device.
hr = pConnFrom->GetDataFlow(&flow);
EXIT_ON_ERROR(hr)
if (flow != Out)
{
// Error -- this is a rendering device.
EXIT_ON_ERROR(hr = AUDCLNT_E_WRONG_ENDPOINT_TYPE)
}
// Outer loop: Each iteration traverses the data path
// through a device topology starting at the input
// connector and ending at the output connector.
while (TRUE)
{
BOOL bConnected;
hr = pConnFrom->IsConnected(&bConnected);
EXIT_ON_ERROR(hr)
// Does this connector connect to another device?
if (bConnected == FALSE)
{
// This is the end of the data path that
// stretches from the endpoint device to the
// system bus or external bus. Verify that
// the connection type is Software_IO.
ConnectorType connType;
hr = pConnFrom->GetType(&connType);
EXIT_ON_ERROR(hr)
if (connType == Software_IO)
{
break; // finished
}
EXIT_ON_ERROR(hr = E_FAIL)
}
// Get the connector in the next device topology,
// which lies on the other side of the connection.
hr = pConnFrom->GetConnectedTo(&pConnTo);
EXIT_ON_ERROR(hr)
SAFE_RELEASE(pConnFrom)
// Get the connector's IPart interface.
hr = pConnTo->QueryInterface(
IID_IPart, (void**)&pPartPrev);
EXIT_ON_ERROR(hr)
SAFE_RELEASE(pConnTo)
// Inner loop: Each iteration traverses one link in a
// device topology and looks for input multiplexers.
while (TRUE)
{
PartType parttype;
UINT localId;
IPartsList *pParts;
// Follow downstream link to next part.
hr = pPartPrev->EnumPartsOutgoing(&pParts);
EXIT_ON_ERROR(hr)
hr = pParts->GetPart(0, &pPartNext);
pParts->Release();
EXIT_ON_ERROR(hr)
hr = pPartNext->GetPartType(&parttype);
EXIT_ON_ERROR(hr)
if (parttype == Connector)
{
// We've reached the output connector that
// lies at the end of this device topology.
hr = pPartNext->QueryInterface(
IID_IConnector,
(void**)&pConnFrom);
EXIT_ON_ERROR(hr)
SAFE_RELEASE(pPartPrev)
SAFE_RELEASE(pPartNext)
break;
}
// Failure of the following call means only that
// the part is not a MUX (input selector).
hr = pPartNext->Activate(
CLSCTX_ALL,
IID_IAudioInputSelector,
(void**)&pSelector);
if (hr == S_OK)
{
// We found a MUX (input selector), so select
// the input from our endpoint device.
hr = pPartPrev->GetLocalId(&localId);
EXIT_ON_ERROR(hr)
hr = pSelector->SetSelection(localId, NULL);
EXIT_ON_ERROR(hr)
SAFE_RELEASE(pSelector)
}
SAFE_RELEASE(pPartPrev)
pPartPrev = pPartNext;
pPartNext = NULL;
}
}
Exit:
SAFE_RELEASE(pConnFrom)
SAFE_RELEASE(pConnTo)
SAFE_RELEASE(pPartPrev)
SAFE_RELEASE(pPartNext)
SAFE_RELEASE(pSelector)
return hr;
}
L’API DeviceTopology implémente une interface IAudioInputSelector pour encapsuler un multiplexeur, tel que celui du diagramme précédent. (Une interface IAudioOutputSelector encapsule un démultiplexeur.) Dans l’exemple de code précédent, la boucle interne de la fonction SelectCaptureDevice interroge chaque sous-unité qu’elle trouve pour déterminer si la sous-unité est un multiplexeur. Si la sous-unité est un multiplexeur, la fonction appelle la méthode IAudioInputSelector ::SetSelection pour sélectionner l’entrée qui se connecte au flux à partir de l’appareil de point de terminaison.
Dans l’exemple de code précédent, chaque itération de la boucle externe traverse une topologie d’appareil. Lors de la traversée des topologies d’appareil dans le diagramme précédent, la première itération traverse l’appareil multiplexeur d’entrée et la deuxième itération traverse l’appareil de capture d’ondes. La fonction se termine lorsqu’elle atteint le connecteur au bord droit du diagramme. L’arrêt se produit lorsque la fonction détecte un connecteur avec un type de connexion Software_IO. Ce type de connexion identifie le point auquel l’appareil adaptateur se connecte au bus système.
L’appel à la méthode IPart ::GetPartType dans l’exemple de code précédent obtient une valeur d’énumération IPartType qui indique si le composant actuel est un connecteur ou une sous-unité de traitement audio.
La boucle interne dans l’exemple de code précédent effectue des étapes dans le lien d’une partie à l’autre en appelant la méthode IPart ::EnumPartsOutgoing. (Il existe également une méthode IPart ::EnumPartsIncoming pour effectuer un pas à pas dans la direction opposée.) Cette méthode récupère un objet IPartsList qui contient une liste de toutes les parties sortantes. Toutefois, toute partie que la fonction SelectCaptureDevice s’attend à rencontrer dans un appareil de capture aura toujours une partie sortante exactement. Ainsi, l’appel suivant à IPartsList ::GetPart demande toujours la première partie de la liste, numéro de partie 0, car la fonction suppose qu’il s’agit de la seule partie de la liste.
Si la fonction SelectCaptureDevice rencontre une topologie pour laquelle cette hypothèse n’est pas valide, la fonction peut échouer à configurer l’appareil correctement. Pour éviter un tel échec, une version plus à usage général de la fonction peut effectuer les opérations suivantes :
- Appelez la méthode IPartsList ::GetCount pour déterminer le nombre de parties sortantes.
- Pour chaque partie sortante, appelez IPartsList ::GetPart pour commencer à parcourir le chemin de données qui mène à partir du composant.
Certaines parties, mais pas nécessairement toutes, ont des contrôles matériels associés que les clients peuvent définir ou obtenir. Une partie particulière peut avoir zéro, un ou plusieurs contrôles matériels. Un contrôle matériel est représenté par la paire d’interfaces suivante :
- Interface de contrôle générique, IControlInterface, qui a des méthodes communes à tous les contrôles matériels.
- Interface spécifique à une fonction (par exemple, IAudioVolumeLevel) qui expose les paramètres de contrôle pour un type particulier de contrôle matériel (par exemple, un contrôle de volume).
Pour énumérer les contrôles matériels d’une partie, le client appelle d’abord la méthode IPart ::GetControlInterfaceCount pour déterminer le nombre de contrôles matériels associés au composant. Ensuite, le client effectue une série d’appels à la méthode IPart ::GetControlInterface pour obtenir l’interface IControlInterface pour chaque contrôle matériel. Enfin, le client obtient l’interface spécifique à la fonction pour chaque contrôle matériel en appelant la méthode IControlInterface ::GetIID pour obtenir l’ID d’interface. Le client appelle la méthode IPart ::Activate avec cet ID pour obtenir l’interface spécifique à la fonction.
Une partie qui est un connecteur peut prendre en charge l’une des interfaces de contrôle spécifiques à la fonction suivantes :
Une partie qui est une sous-unité peut prendre en charge une ou plusieurs des interfaces de contrôle spécifiques à la fonction suivantes :
- IAudioAutoGainControl
- IAudioBass
- IAudioChannelConfig
- IAudioInputSelector
- IAudioLoudness
- IAudioMidrange
- IAudioMute
- IAudioOutputSelector
- IAudioPeakMeter
- IAudioTreble
- IAudioVolumeLevel
- IDeviceSpecificProperty
Une partie prend en charge l’interface IDeviceSpecificProperty uniquement si le contrôle matériel sous-jacent a une valeur de contrôle spécifique à l’appareil et que le contrôle ne peut pas être correctement représenté par une autre interface spécifique à une fonction dans la liste précédente. En règle générale, une propriété spécifique à l’appareil est utile uniquement à un client qui peut déduire la signification de la valeur de propriété à partir d’informations telles que le type de composant, le sous-type de composant et le nom de partie. Le client peut obtenir ces informations en appelant les méthodes IPart ::GetPartType, IPart ::GetSubTypeet méthodes IPart ::GetName.
Rubriques connexes