Apparaattopologieën
De DeviceTopology-API geeft clients controle over verschillende interne functies van audioadapters waartoe ze geen toegang hebben via de MMDevice-API, WASAPI-of de EndpointVolume-API.
Zoals eerder uitgelegd, presenteren de MMDevice-API, WASAPI-en de EndpointVolume-API microfoons, luidsprekers, hoofdtelefoons en andere audio-invoer- en uitvoerapparaten aan clients als audio-eindpuntapparaten. Het eindpuntapparaatmodel biedt clients handige toegang tot volume- en dempingsbesturingselementen in audioapparaten. Clients die alleen deze eenvoudige besturingselementen vereisen, kunnen voorkomen dat de interne topologieën van hardwareapparaten in audioadapters worden doorkruist.
In Windows Vista configureert de audio-engine automatisch de topologieën van audioapparaten voor gebruik door audiotoepassingen. Daarom moeten toepassingen zelden, indien ooit, de DeviceTopology-API gebruiken voor dit doel. Stel dat een audioadapter een invoer-multiplexer bevat waarmee een stroom kan worden vastgelegd vanaf een regelinvoer of een microfoon, maar dat geen streams van beide eindpuntapparaten tegelijk kan vastleggen. Stel dat de gebruiker exclusieve modustoepassingen heeft ingeschakeld om het gebruik van een audio-eindpuntapparaat vooraf te laten gaan door toepassingen in de gedeelde modus, zoals beschreven in Exclusive-Mode Streams. Als een toepassing in de gedeelde modus een stroom van de regelinvoer opneemt op het moment dat een exclusieve-modustoepassing begint met het opnemen van een stream vanaf de microfoon, schakelt de audio-engine automatisch de multiplexer over van de regelinvoer naar de microfoon. In eerdere versies van Windows, met inbegrip van Windows XP, zou de exclusieve-modustoepassing in dit voorbeeld de mixerXxx functies in de Windows-multimedia-API gebruiken om de topologieën van de adapterapparaten te doorlopen, de multiplexer te detecteren en de multiplexer te configureren om de microfooninvoer te selecteren. In Windows Vista zijn deze stappen niet meer vereist.
Voor sommige clients is echter mogelijk expliciete controle vereist over typen audiohardwarebesturingselementen die niet toegankelijk zijn via de MMDevice-API, WASAPI of de EndpointVolume-API. Voor deze clients biedt de DeviceTopology-API de mogelijkheid om de topologieën van adapterapparaten te doorlopen om de audiobesturingselementen op de apparaten te detecteren en te beheren. Toepassingen die gebruikmaken van de DeviceTopology-API moeten zorgvuldig worden ontworpen om te voorkomen dat het windows-audiobeleid wordt verstoord en dat de interne configuraties van audioapparaten die met andere toepassingen worden gedeeld, worden verstoord. Zie User-Mode Audioonderdelenvoor meer informatie over het Windows-audiobeleid.
De DeviceTopology-API biedt interfaces voor het detecteren en beheren van de volgende typen audiobesturingselementen in een apparaattopologie:
- Automatisch controle over verkrijgen
- Basbesturing
- Invoerkiezer (multiplexer)
- Besturingselement voor luidheid
- Besturingselement voor middenbereik
- Besturingselement dempen
- Uitvoerkiezer (demultiplexer)
- Piekmeter
- Besturingselement voor treble
- Volumeregeling
Bovendien kunnen clients met de DeviceTopology-API adapterapparaten opvragen voor informatie over de streamindelingen die ze ondersteunen. Het headerbestand Devicetopology.h definieert de interfaces in de DeviceTopology-API.
In het volgende diagram ziet u een voorbeeld van verschillende verbonden apparaattopologieën voor het gedeelte van een PCI-adapter waarmee audio van een microfoon, lijninvoer en CD-speler wordt vastgelegd.
In het voorgaande diagram ziet u de gegevenspaden die van de analoge invoer naar de systeembus leiden. Elk van de volgende apparaten wordt weergegeven als een apparaattopologieobject met een IDeviceTopology interface:
- Wave capture-apparaat
- Invoer multiplexer-apparaat
- Eindpuntapparaat A
- Eindpuntapparaat B
In het topologiediagram worden adapterapparaten (de wave capture- en invoer-multiplexer-apparaten) gecombineerd met eindpuntapparaten. Via de verbindingen tussen apparaten worden audiogegevens van het ene apparaat naar het andere doorgegeven. Aan elke zijde van een verbinding bevindt zich een connector (gelabelde Con in het diagram) waarmee gegevens een apparaat invoeren of verlaten.
Aan de linkerkant van het diagram worden de eindpuntapparaten ingevoerd door signalen van de lijninvoer- en microfoonaansluitingen.
Binnen het apparaat voor golfopname en invoer-multiplexer-apparaat zijn streamverwerkingsfuncties, die, in de terminologie van de DeviceTopology-API, subeenheden worden genoemd. De volgende typen subeenheden worden weergegeven in het voorgaande diagram:
- Volumeregeling (gelabeld Vol)
- Besturingselement dempen (gelabeld dempen)
- Multiplexer (of invoerkiezer; gelabelde MUX)
- Analog-to-digital converter (gelabeld ADC)
De instellingen in het volume, dempen en multiplexer-subeenheden kunnen worden beheerd door clients en de DeviceTopology-API biedt besturingsinterfaces voor clients voor het beheren ervan. In dit voorbeeld heeft de ADC-subeenheid geen controle-instellingen. De DeviceTopology-API biedt dus geen besturingsinterface voor de ADC.
In de terminologie van de DeviceTopology-API behoren connectors en subeenheden tot dezelfde algemene categorie: onderdelen. Alle onderdelen, ongeacht of het verbindingslijnen of subeenheden zijn, bieden een gemeenschappelijke set functies. De DeviceTopology-API implementeert een IPart--interface om de algemene functies weer te geven die gebruikelijk zijn voor connectors en subeenheden. De API implementeert de IConnector- en ISubunit-interfaces om de specifieke aspecten van connectors en subeenheden aan te geven.
De DeviceTopology-API bouwt de topologieën van het wave capture-apparaat en invoer multiplexer-apparaat van de KS-filters (kernelstreaming) die het audiostuurprogramma beschikbaar maakt voor het besturingssysteem om deze apparaten weer te geven. (Het stuurprogramma voor de audioadapter implementeert IMiniportWaveXxx en IMiniportTopology interfaces om de hardwareafhankelijke gedeelten van deze filters weer te geven. Zie de Windows DDK-documentatie voor meer informatie over deze interfaces en over KS-filters.)
Met de DeviceTopology-API worden triviale topologieën samengesteld die eindpuntapparaten A en B in het voorgaande diagram vertegenwoordigen. De apparaattopologie van een eindpuntapparaat bestaat uit één connector. Deze topologie is slechts een tijdelijke aanduiding voor het eindpuntapparaat en bevat geen subeenheden voor het verwerken van audiogegevens. Adapterapparaten bevatten zelfs alle subeenheden die clienttoepassingen gebruiken om audioverwerking te beheren. De apparaattopologie van een eindpuntapparaat fungeert voornamelijk als uitgangspunt voor het verkennen van de apparaattopologieën van adapterapparaten.
Interne verbindingen tussen twee onderdelen in een apparaattopologie worden koppelingen genoemd. De DeviceTopology-API biedt methoden voor het doorkruisen van koppelingen van het ene naar het andere deel in een apparaattopologie. De API biedt ook methoden voor het doorlopen van de verbindingen tussen apparaattopologieën.
Als u een reeks verbonden apparaattopologieën wilt verkennen, activeert een clienttoepassing de IDeviceTopology interface van een audio-eindpuntapparaat. De connector in een eindpuntapparaat maakt verbinding met een connector in een audioadapter of met een netwerk. Als het eindpunt verbinding maakt met een apparaat op een audioadapter, kunnen met de methoden in de DeviceTopology-API de toepassing stap voor stap over de verbinding van het eindpunt naar de adapter gaan door een verwijzing te verkrijgen naar de IDeviceTopology interface van het adapterapparaat aan de andere kant van de verbinding. Een netwerk heeft daarentegen geen apparaattopologie. Een netwerkverbinding pijpt een audiostream naar een client die extern toegang heeft tot het systeem.
De DeviceTopology-API biedt alleen toegang tot de topologieën van de hardwareapparaten in een audioadapter. De externe apparaten aan de linkerkant van het diagram en de softwareonderdelen aan de rechterkant vallen buiten het bereik van de API. De stippellijnen aan beide zijden van het diagram vertegenwoordigen de limieten van de DeviceTopology-API. De client kan de API gebruiken om een gegevenspad te verkennen dat van de invoeraansluiting naar de systeembus wordt uitgerekt, maar de API kan niet buiten deze grenzen binnendringen.
Elke connector in het voorgaande diagram heeft een gekoppeld verbindingstype dat aangeeft welk type verbinding de connector maakt. De connectors aan de twee zijden van een verbinding hebben dus altijd identieke verbindingstypen. Het verbindingstype wordt aangegeven met een ConnectorType opsommingswaarde: Physical_External, Physical_Internal, Software_Fixed, Software_IO of Network. De verbindingen tussen het invoer-multiplexer-apparaat en eindpuntapparaten A en B zijn van het type Physical_External, wat betekent dat de verbinding een fysieke verbinding met een extern apparaat vertegenwoordigt (met andere woorden, een audioaansluiting die toegankelijk is voor de gebruiker). De verbinding met het analoge signaal van de interne CD-speler is van het type Physical_Internal, wat wijst op een fysieke verbinding met een hulpapparaat dat in het systeemchassis is geïnstalleerd. De verbinding tussen het wave capture-apparaat en het invoer-multiplexer-apparaat is van het type Software_Fixed, wat aangeeft dat er een permanente verbinding is die is opgelost en niet kan worden geconfigureerd onder softwarebeheer. Ten slotte is de verbinding met de systeembus aan de rechterkant van het diagram van het type Software_IO, wat aangeeft dat de gegevens-I/O voor de verbinding worden geïmplementeerd door een DMA-engine onder softwarebeheer. (Het diagram bevat geen voorbeeld van een netwerkverbindingstype.)
De client begint met het doorlopen van een gegevenspad op het eindpuntapparaat. Eerst verkrijgt de client een IMMDevice-interface die het eindpuntapparaat vertegenwoordigt, zoals wordt uitgelegd in Audioapparaten opsommen. Om de IDeviceTopology interface voor het eindpuntapparaat te verkrijgen, roept de client de methode IMMDevice::Activate method with parameter iid set to REFIID IID_IDeviceTopology.
In het voorbeeld in het voorgaande diagram bevat het invoer-multiplexerapparaat alle hardwarebesturingselementen (volume, dempen en multiplexer) voor de opnamestromen van de lijninvoer- en microfoonaansluitingen. In het volgende codevoorbeeld ziet u hoe u de IDeviceTopology interface voor het invoer multiplexer-apparaat kunt verkrijgen via de IMMDevice-interface voor het eindpuntapparaat voor de regelinvoer of microfoon:
//-----------------------------------------------------------
// 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;
}
De functie GetHardwareDeviceTopology in het vorige codevoorbeeld voert de volgende stappen uit om de IDeviceTopology interface voor het invoer-multiplexer-apparaat te verkrijgen:
- Roep de methode IMMDevice::Activate aan om de IDeviceTopology-interface voor het eindpuntapparaat op te halen.
- Roep met de IDeviceTopology interface die u in de vorige stap hebt verkregen, de IDeviceTopology::GetConnector methode aan om de IConnector--interface van de enkele connector (connectornummer 0) op het eindpuntapparaat op te halen.
- Met de IConnector-interface die u in de vorige stap hebt verkregen, roept u de methode IConnector::GetConnectedTo aan om de interface van de connector op te halen in het invoer multiplexer-apparaat.
- Voer een query uit op de IConnector interface die u in de vorige stap hebt verkregen voor de IPart-interface.
- Roep met de IPart-interface die u in de vorige stap hebt verkregen, de methode IPart::GetTopologyObject aan om de IDeviceTopology--interface voor het invoer-multiplexer-apparaat op te halen.
Voordat de gebruiker vanuit de microfoon in het voorgaande diagram kan opnemen, moet de clienttoepassing ervoor zorgen dat de multiplexer de microfooninvoer selecteert. In het volgende codevoorbeeld ziet u hoe een client het gegevenspad van de microfoon kan doorlopen totdat de multiplexer wordt gevonden, waarna de microfooninvoer wordt geselecteerd:
//-----------------------------------------------------------
// 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;
}
De DeviceTopology-API implementeert een IAudioInputSelector interface om een multiplexer in te kapselen, zoals die in het voorgaande diagram. (Een IAudioOutputSelector interface kapselt een demultiplexer in.) In het voorgaande codevoorbeeld wordt met de binnenste lus van de functie SelectCaptureDevice elke subeenheid opgevraagd die wordt gevonden om te detecteren of de subeenheid een multiplexer is. Als de subeenheid een multiplexer is, roept de functie de IAudioInputSelector::SetSelection methode aan om de invoer te selecteren die verbinding maakt met de stream vanaf het eindpuntapparaat.
In het voorgaande codevoorbeeld doorloopt elke iteratie van de buitenste lus één apparaattopologie. Wanneer u de apparaattopologieën in het voorgaande diagram doorkruist, gaat de eerste iteratie door het invoer multiplexer-apparaat en de tweede iteratie door het apparaat voor golfopname. De functie wordt beëindigd wanneer deze de verbindingslijn bereikt aan de rechterkant van het diagram. Beëindiging vindt plaats wanneer de functie een connector detecteert met een Software_IO verbindingstype. Dit verbindingstype identificeert het punt waarop het adapterapparaat verbinding maakt met de systeembus.
De aanroep van de methode IPart::GetPartType in het voorgaande codevoorbeeld verkrijgt een IPartType opsommingswaarde die aangeeft of het huidige deel een connector of een subeenheid voor audioverwerking is.
De binnenste lus in de voorgaande codevoorbeeldstappen op de koppeling van het ene naar het andere deel door de methode IPart::EnumPartsOutgoing aan te roepen. (Er is ook een IPart::EnumPartsIncoming methode om in tegenovergestelde richting te stappen.) Met deze methode wordt een IPartsList-object opgehaald dat een lijst met alle uitgaande onderdelen bevat. Elk onderdeel dat de functie SelectCaptureDevice verwacht te tegenkomen in een capture-apparaat, heeft echter altijd precies één uitgaand onderdeel. Daarom vraagt de volgende aanroep naar IPartsList::GetPart altijd het eerste deel in de lijst aan, onderdeelnummer 0, omdat de functie ervan uitgaat dat dit het enige deel in de lijst is.
Als de functie SelectCaptureDevice een topologie tegenkomt waarvoor deze aanname niet geldig is, kan de functie het apparaat mogelijk niet correct configureren. Om een dergelijke fout te voorkomen, kan een meer algemene versie van de functie het volgende doen:
- Roep de methode IPartsList::GetCount aan om het aantal uitgaande onderdelen te bepalen.
- Roep voor elk uitgaand deel IPartsList::GetPart aan om het gegevenspad te doorlopen dat van het deel leidt.
Sommige, maar niet noodzakelijkerwijs alle onderdelen hebben gekoppelde hardwarebesturingselementen die clients kunnen instellen of ophalen. Een bepaald onderdeel kan nul, één of meer hardwarebesturingselementen hebben. Een hardwarebeheer wordt vertegenwoordigd door het volgende paar interfaces:
- Een algemene besturingsinterface, IControlInterface, die methoden bevat die gebruikelijk zijn voor alle hardwarebesturingselementen.
- Een functiespecifieke interface (bijvoorbeeld IAudioVolumeLevel) waarmee de besturingsparameters voor een bepaald type hardwarebeheer (bijvoorbeeld een volumeregeling) worden weergegeven.
Om de hardwarebesturingselementen voor een onderdeel op te sommen, roept de client eerst de IPart::GetControlInterfaceCount methode aan om het aantal hardwarebesturingselementen te bepalen dat aan het onderdeel is gekoppeld. Vervolgens maakt de client een reeks aanroepen naar het IPart::GetControlInterface methode om de IControlInterface interface te verkrijgen voor elk hardwarebeheer. Ten slotte verkrijgt de client de functiespecifieke interface voor elk hardwarebeheer door de IControlInterface::GetIID methode aan te roepen om de interface-id te verkrijgen. De client roept de methode IPart::Activate aan met deze id om de functiespecifieke interface te verkrijgen.
Een onderdeel dat een connector is, ondersteunt mogelijk een van de volgende functiespecifieke besturingsinterfaces:
Een onderdeel dat een subeenheid is, ondersteunt mogelijk een of meer van de volgende functiespecifieke besturingsinterfaces:
- IAudioAutoGainControl-
- IAudioBass
- IAudioChannelConfig-
- IAudioInputSelector
- IAudioLoudness-
- IAudioMidrange
- IAudioMute-
- IAudioOutputSelector-
- IAudioPeakMeter
- IAudioTreble-
- IAudioVolumeLevel
- IDeviceSpecificProperty-
Een onderdeel ondersteunt de IDeviceSpecificProperty interface alleen als het onderliggende hardwarebesturingselement een apparaatspecifieke besturingswaarde heeft en het besturingselement niet voldoende kan worden vertegenwoordigd door een andere functiespecifieke interface in de voorgaande lijst. Normaal gesproken is een apparaatspecifieke eigenschap alleen nuttig voor een client die de betekenis van de eigenschapswaarde kan afleiden uit informatie zoals het onderdeeltype, het deelsubtype en de naam van het onderdeel. De client kan deze informatie verkrijgen door het IPart::GetPartTypeaan te roepen, IPart::GetSubTypeen IPart::GetName methoden.
Verwante onderwerpen