Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
O DeviceTopology API dá aos clientes controle sobre uma variedade de funções internas de adaptadores de áudio que eles não podem acessar por meio do da API MMDevice, WASAPI ou da API EndpointVolume .
Como explicado anteriormente, o MMDevice API, WASAPIe o EndpointVolume API apresentar microfones, alto-falantes, fones de ouvido e outros dispositivos de entrada e saída de áudio para clientes como dispositivos de ponto final de áudio. O modelo de dispositivo de ponto final fornece aos clientes acesso conveniente aos controles de volume e silenciamento em dispositivos de áudio. Os clientes que exigem apenas esses controles simples podem evitar atravessar as topologias internas de dispositivos de hardware em adaptadores de áudio.
No Windows Vista, o mecanismo de áudio configura automaticamente as topologias de dispositivos de áudio para uso por aplicativos de áudio. Assim, os aplicativos raramente, ou nunca, precisam usar a API DeviceTopology para essa finalidade. Por exemplo, suponha que um adaptador de áudio contém um multiplexador de entrada que pode capturar um fluxo de uma entrada de linha ou de um microfone, mas que não pode capturar fluxos de ambos os dispositivos de ponto de extremidade ao mesmo tempo. Suponha que o usuário tenha habilitado aplicativos de modo exclusivo para antecipar o uso de um dispositivo de ponto de extremidade de áudio por aplicativos de modo compartilhado, conforme descrito em Exclusive-Mode Streams. Se um aplicativo de modo compartilhado estiver gravando um fluxo da entrada de linha no momento em que um aplicativo de modo exclusivo começar a gravar um fluxo do microfone, o mecanismo de áudio alternará automaticamente o multiplexador da entrada de linha para o microfone. Em contraste, em versões anteriores do Windows, incluindo o Windows XP, o aplicativo de modo exclusivo neste exemplo usaria as funções de mixerXxx na API multimídia do Windows para percorrer as topologias dos dispositivos adaptadores, descobrir o multiplexador e configurar o multiplexador para selecionar a entrada do microfone. No Windows Vista, essas etapas não são mais necessárias.
No entanto, alguns clientes podem exigir controle explícito sobre tipos de controles de hardware de áudio que não podem ser acessados por meio da API MMDevice, WASAPI ou EndpointVolume API. Para esses clientes, a API DeviceTopology fornece a capacidade de percorrer as topologias de dispositivos adaptadores para descobrir e gerenciar os controles de áudio nos dispositivos. Os aplicativos que usam a API DeviceTopology devem ser projetados com cuidado para evitar interferir na política de áudio do Windows e perturbar as configurações internas de dispositivos de áudio compartilhados com outros aplicativos. Para obter mais informações sobre a política de áudio do Windows, consulte User-Mode Audio Components.
A API DeviceTopology fornece interfaces para descobrir e gerenciar os seguintes tipos de controles de áudio em uma topologia de dispositivo:
- Controle automático de ganho
- Controlo de graves
- Seletor de entrada (multiplexador)
- Controlo de volume
- Controlo de gama média
- Controle de silenciamento
- Seletor de saída (desmultiplexador)
- Medidor de pico
- Controlo de agudos
- Controlo de volume
Além disso, a API DeviceTopology permite que os clientes consultem dispositivos adaptadores para obter informações sobre os formatos de fluxo suportados. O arquivo de cabeçalho Devicetopology.h define as interfaces na API DeviceTopology.
O diagrama a seguir mostra um exemplo de várias topologias de dispositivos conectados para a parte de um adaptador PCI que captura áudio de um microfone, entrada de linha e CD player.
O diagrama anterior mostra os caminhos de dados que levam das entradas analógicas para o barramento do sistema. Cada um dos seguintes dispositivos é representado como um objeto de topologia de dispositivo com uma interfaceIDeviceTopology:
- Dispositivo de captura de ondas
- Dispositivo multiplexador de entrada
- Dispositivo de ponto final A
- Dispositivo de ponto final B
Observe que o diagrama de topologia combina dispositivos adaptadores (os dispositivos multiplexadores de captura e entrada de ondas) com dispositivos de ponto final. Através das conexões entre dispositivos, os dados de áudio passam de um dispositivo para o próximo. Em cada lado de uma conexão há um conector (rotulado Con no diagrama) através do qual os dados entram ou saem de um dispositivo.
Na borda esquerda do diagrama, os sinais das entradas de linha e conectores de microfone entram nos dispositivos de ponto final.
Dentro do dispositivo de captura de onda e do dispositivo multiplexador de entrada estão funções de processamento de fluxo, que, na terminologia da API DeviceTopology, são chamadas de subunidades. Os seguintes tipos de subunidades aparecem no diagrama anterior:
- Controle de volume (rotulado Vol)
- Controle de silenciamento (rotulado Mudo)
- Multiplexador (ou seletor de entrada; rotulado MUX)
- Conversor analógico-digital (rotulado ADC)
As configurações nas subunidades de volume, mudo e multiplexador podem ser controladas por clientes, e a API DeviceTopology fornece interfaces de controle aos clientes para controlá-las. Neste exemplo, a subunidade ADC não tem configurações de controle. Assim, a API DeviceTopology não fornece nenhuma interface de controle para o ADC.
Na terminologia da API DeviceTopology, conectores e subunidades pertencem à mesma categoria geral — partes. Todas as peças, independentemente de serem conectores ou subunidades, fornecem um conjunto comum de funções. A API DeviceTopology implementa uma interface IPartpara representar as funções genéricas que são comuns a conectores e subunidades. A API implementa o IConnector e interfaces ISubunit para representar os aspetos específicos de conectores e subunidades.
A API DeviceTopology constrói as topologias do dispositivo de captura de onda e do dispositivo multiplexador de entrada a partir dos filtros KS (kernel-streaming) que o driver de áudio expõe ao sistema operacional para representar esses dispositivos. (O driver do adaptador de áudio implementa IMiniportWaveXxx e interfaces IMiniportTopology para representar as partes dependentes de hardware desses filtros; para obter mais informações sobre essas interfaces e sobre filtros KS, consulte a documentação do Windows DDK.)
A API DeviceTopology constrói topologias triviais para representar os dispositivos de ponto de extremidade A e B no diagrama anterior. A topologia de dispositivo de um dispositivo de ponto de extremidade consiste em um único conector. Essa topologia é apenas um espaço reservado para o dispositivo de ponto de extremidade e não contém subunidades para processar dados de áudio. Na verdade, os dispositivos adaptadores contêm todas as subunidades que os aplicativos cliente usam para controlar o processamento de áudio. A topologia de dispositivo de um dispositivo de ponto de extremidade serve principalmente como um ponto de partida para explorar as topologias de dispositivo de dispositivos adaptadores.
As conexões internas entre duas partes em uma topologia de dispositivo são chamadas de links. A API DeviceTopology fornece métodos para percorrer links de uma parte para outra em uma topologia de dispositivo. A API também fornece métodos para percorrer as conexões entre topologias de dispositivo.
Para iniciar a exploração de um conjunto de topologias de dispositivo conectado, um aplicativo cliente ativa a interface IDeviceTopology de um dispositivo de ponto de extremidade de áudio. O conector em um dispositivo de ponto de extremidade se conecta a um conector em um adaptador de áudio ou a uma rede. Se o ponto de extremidade se conectar a um dispositivo em um adaptador de áudio, os métodos na API DeviceTopology permitirão que o aplicativo percorra a conexão do ponto de extremidade para o adaptador obtendo uma referência à interface IDeviceTopology do dispositivo adaptador do outro lado da conexão. Uma rede, por outro lado, não tem topologia de dispositivo. Uma conexão de rede canaliza um fluxo de áudio para um cliente que está acessando o sistema remotamente.
A API DeviceTopology fornece acesso somente às topologias dos dispositivos de hardware em um adaptador de áudio. Os dispositivos externos na borda esquerda do diagrama e os componentes de software na borda direita estão além do escopo da API. As linhas tracejadas em ambos os lados do diagrama representam os limites da API DeviceTopology. O cliente pode usar a API para explorar um caminho de dados que se estende da tomada de entrada até o barramento do sistema, mas a API não pode penetrar além desses limites.
Cada conector no diagrama anterior tem um tipo de conexão associado que indica o tipo de conexão que o conector faz. Assim, os conectores nos dois lados de uma conexão sempre têm tipos de conexão idênticos. O tipo de conexão é indicado por um ConnectorType valor de enumeração — Physical_External, Physical_Internal, Software_Fixed, Software_IO ou Network. As conexões entre o dispositivo multiplexador de entrada e dispositivos de ponto final A e B são do tipo Physical_External, o que significa que a conexão representa uma conexão física com um dispositivo externo (em outras palavras, uma tomada de áudio acessível ao usuário). A ligação ao sinal analógico do leitor de CD interno é do tipo Physical_Internal, o que indica uma ligação física a um dispositivo auxiliar instalado dentro do chassis do sistema. A conexão entre o dispositivo de captura de onda e o dispositivo multiplexador de entrada é do tipo Software_Fixed, o que indica uma conexão permanente que é fixa e não pode ser configurada sob controle de software. Finalmente, a conexão com o barramento do sistema no lado direito do diagrama é do tipo Software_IO, o que indica que a E/S de dados para a conexão é implementada por um mecanismo DMA sob controle de software. (O diagrama não inclui um exemplo de um tipo de conexão de rede.)
O cliente começa a percorrer um caminho de dados no dispositivo de ponto de extremidade. Primeiro, o cliente obtém um interface IMMDevice que representa o dispositivo de ponto final, conforme explicado em Enumerating Audio Devices. Para obter o IDeviceTopology interface para o dispositivo de ponto de extremidade, o cliente chama o IMMDevice::Activate método com parâmetro iid definido como REFIID IID_IDeviceTopology.
No exemplo no diagrama anterior, o dispositivo multiplexador de entrada contém todos os controles de hardware (volume, mudo e multiplexador) para os fluxos de captura das entradas de linha e conectores de microfone. O exemplo de código a seguir mostra como obter o IDeviceTopology interface para o dispositivo multiplexador de entrada a partir do IMMDevice interface para o dispositivo de ponto de extremidade para a entrada de linha ou microfone:
//-----------------------------------------------------------
// 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;
}
A função GetHardwareDeviceTopology no exemplo de código anterior executa as seguintes etapas para obter o IDeviceTopology interface para o dispositivo multiplexador de entrada:
- Chame o IMMDevice::Activate método para obter o IDeviceTopology interface para o dispositivo de ponto final.
- Com o IDeviceTopology interface obtida na etapa anterior, chame o IDeviceTopology::GetConnector método para obter o IConnector interface do conector único (conector número 0) no dispositivo de ponto final.
- Com o IConnector interface obtida na etapa anterior, chame o IConnector::GetConnectedTo método para obter o IConnector interface do conector no dispositivo multiplexador de entrada.
- Consulte a interface IConnector obtida na etapa anterior para sua interface IPart .
- Com o IPart interface obtida na etapa anterior, chame o IPart::GetTopologyObject método para obter o IDeviceTopology interface para o dispositivo multiplexador de entrada.
Antes que o usuário possa gravar a partir do microfone no diagrama anterior, o aplicativo cliente deve certificar-se de que o multiplexador seleciona a entrada do microfone. O exemplo de código a seguir mostra como um cliente pode percorrer o caminho de dados do microfone até encontrar o multiplexador, que ele então programa para selecionar a entrada do microfone:
//-----------------------------------------------------------
// 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;
}
A API DeviceTopology implementa um interface IAudioInputSelector para encapsular um multiplexador, como o do diagrama anterior. (Um interface IAudioOutputSelector encapsula um desmultiplexador.) No exemplo de código anterior, o loop interno da função SelectCaptureDevice consulta cada subunidade que encontra para descobrir se a subunidade é um multiplexador. Se a subunidade for um multiplexador, a função chamará o método IAudioInputSelector::SetSelection para selecionar a entrada que se conecta ao fluxo a partir do dispositivo de ponto final.
No exemplo de código anterior, cada iteração do loop externo atravessa uma topologia de dispositivo. Ao percorrer as topologias do dispositivo no diagrama anterior, a primeira iteração atravessa o dispositivo multiplexador de entrada e a segunda iteração atravessa o dispositivo de captura de ondas. A função terminará quando atingir o conector na borda direita do diagrama. A terminação ocorre quando a função deteta um conector com um tipo de conexão Software_IO. Esse tipo de conexão identifica o ponto no qual o dispositivo adaptador se conecta ao barramento do sistema.
A chamada para o método IPart::GetPartType no exemplo de código anterior obtém um IPartType valor de enumeração que indica se a parte atual é um conector ou uma subunidade de processamento de áudio.
O loop interno no exemplo de código anterior percorre o link de uma parte para a próxima chamando o IPart::EnumPartsOutgoing método. (Há também um IPart::EnumPartsIncoming método para pisar na direção oposta.) Esse método recupera um IPartsList objeto que contém uma lista de todas as partes de saída. No entanto, qualquer parte que a função SelectCaptureDevice espera encontrar em um dispositivo de captura sempre terá exatamente uma parte de saída. Assim, a chamada subsequente para IPartsList::GetPart sempre solicita a primeira parte da lista, número de peça 0, porque a função assume que esta é a única parte na lista.
Se a função SelectCaptureDevice encontrar uma topologia para a qual essa suposição não é válida, a função pode falhar ao configurar o dispositivo corretamente. Para evitar tal falha, uma versão mais geral da função pode fazer o seguinte:
- Chame o método IPartsList::GetCount para determinar o número de partes de saída.
- Para cada parte de saída, chame IPartsList::GetPart para começar a percorrer o caminho de dados que leva da parte.
Algumas, mas não necessariamente todas, as peças têm controles de hardware associados que os clientes podem definir ou obter. Uma peça específica pode ter zero, um ou mais controles de hardware. Um controle de hardware é representado pelo seguinte par de interfaces:
- Uma interface de controle genérica, IControlInterface, que tem métodos que são comuns a todos os controles de hardware.
- Uma interface específica da função (por exemplo, IAudioVolumeLevel) que expõe os parâmetros de controle para um tipo específico de controle de hardware (por exemplo, um controle de volume).
Para enumerar os controles de hardware de uma peça, o cliente primeiro chama o métodoIPart::GetControlInterfaceCountpara determinar o número de controles de hardware associados à peça. Em seguida, o cliente faz uma série de chamadas para o IPart::GetControlInterface método para obter o IControlInterface interface para cada controle de hardware. Finalmente, o cliente obtém a interface específica da função para cada controle de hardware chamando o IControlInterface::GetIID método para obter o ID da interface. O cliente chama o método IPart::Activate com essa ID para obter a interface específica da função.
Uma parte que é um conector pode suportar uma das seguintes interfaces de controle específicas da função:
Uma parte que é uma subunidade pode suportar uma ou mais das seguintes interfaces de controle específicas da função:
- IAudioAutoGainControl
- IAudioBass
- IAudioChannelConfig
- IAudioInputSelector
- IAudioLoudness
- IAudioMidrange
- IAudioMute
- IAudioOutputSelector
- IAudioPeakMeter
- IAudioTreble
- IAudioVolumeLevel
- IDeviceSpecificProperty
Uma parte suporta a interface IDeviceSpecificProperty somente se o controle de hardware subjacente tiver um valor de controle específico do dispositivo e o controle não puder ser representado adequadamente por qualquer outra interface específica da função na lista anterior. Normalmente, uma propriedade específica do dispositivo é útil apenas para um cliente que pode inferir o significado do valor da propriedade a partir de informações como o tipo de peça, subtipo de peça e nome da peça. O cliente pode obter essas informações chamando o IPart::GetPartType, IPart::GetSubTypee IPart::GetName métodos.
Tópicos relacionados