Enhetstopologier
API:et DeviceTo pology ger klienter kontroll över en mängd olika interna funktioner för ljudkort som de inte kan komma åt via MMDevice API, WASAPIeller EndpointVolume API.
Som tidigare förklarats MMDevice API, WASAPIoch EndpointVolume API presentera mikrofoner, högtalare, hörlurar och andra enheter för ljudinmatning och utdata till klienter som ljudslutpunktsenheter. Slutpunktsenhetsmodellen ger klienter bekväm åtkomst till volym- och ljudavstängningskontroller på ljudenheter. Klienter som bara behöver dessa enkla kontroller kan undvika att passera de interna topologierna för maskinvaruenheter i ljudkort.
I Windows Vista konfigurerar ljudmotorn automatiskt topologierna för ljudenheter för användning av ljudprogram. Därför behöver program sällan, om någonsin, använda API:et DeviceTopology för detta ändamål. Anta till exempel att ett ljudkort innehåller en indata multiplexer som kan avbilda en ström från antingen en linjeinmatning eller en mikrofon, men som inte kan avbilda strömmar från båda slutpunktsenheterna samtidigt. Anta att användaren har aktiverat program i exklusivt läge för att föregripa användningen av en ljudslutpunktsenhet av program i delat läge, enligt beskrivningen i Exclusive-Mode Streams. Om ett program i delat läge spelar in en ström från radinmatningen när ett program i exklusivt läge börjar spela in en ström från mikrofonen, växlar ljudmotorn automatiskt multiplexern från linjeinmatningen till mikrofonen. I tidigare versioner av Windows, inklusive Windows XP, skulle däremot programmet med exklusivt läge i det här exemplet använda mixerXxx funktioner i Windows multimedia-API:et för att bläddra igenom adapterenheternas topologier, identifiera multiplexern och konfigurera multiplexern för att välja mikrofonindata. I Windows Vista krävs inte längre de här stegen.
Vissa klienter kan dock kräva explicit kontroll över typer av ljudmaskinvarukontroller som inte kan nås via MMDevice API, WASAPI eller EndpointVolume-API:et. För dessa klienter ger Api:et DeviceTopology möjlighet att gå igenom topologierna för adapterenheter för att identifiera och hantera ljudkontrollerna på enheterna. Program som använder API:et DeviceTopology måste utformas med försiktighet för att undvika att störa Windows ljudprincip och störa de interna konfigurationerna av ljudenheter som delas med andra program. Mer information om Windows ljudprincip finns i User-Mode Ljudkomponenter.
Api:et DeviceTopology innehåller gränssnitt för att identifiera och hantera följande typer av ljudkontroller i en enhetstopologi:
- Automatisk vinstkontroll
- Baskontroll
- Indataväljare (multiplexer)
- Högljutt kontroll
- Mellanregisterkontroll
- Stäng av kontroll
- Utdataväljare (demultiplexer)
- Högsta mätare
- Treble-kontroll
- Volymkontroll
Dessutom gör DeviceTopology-API:et det möjligt för klienter att fråga efter kortenheter för information om de strömformat som de stöder. Huvudfilen Devicetopology.h definierar gränssnitten i API:et DeviceTopology.
Följande diagram visar ett exempel på flera anslutna enhetstopologier för den del av ett PCI-kort som samlar in ljud från en mikrofon, linjeindata och CD-spelare.
Föregående diagram visar de datasökvägar som leder från analoga indata till systembussen. Var och en av följande enheter representeras som ett enhetstopologiobjekt med ett IDeviceTopology--gränssnitt:
- Wave Capture-enhet
- Indata multiplexerenhet
- Slutpunktsenhet A
- Slutpunktsenhet B
Observera att topologidiagrammet kombinerar adapterenheter (vågfångst- och indata-multiplexerenheter) med slutpunktsenheter. Via anslutningarna mellan enheter skickas ljuddata från en enhet till en annan. På varje sida av en anslutning finns en anslutningsapp (märkt Con i diagrammet) genom vilken data kommer in eller lämnar en enhet.
På diagrammets vänstra kant kommer signaler från linjeinmatnings- och mikrofonuttagen in på slutpunktsenheterna.
Inuti vågavbildningsenheten och indata multiplexerenheten finns strömbearbetningsfunktioner, som i terminologin för API:et DeviceTopology kallas underenheter. Följande typer av underenheter visas i föregående diagram:
- Volymkontroll (märkt Vol)
- Stäng av kontrollen (märkt Mute)
- Multiplexer (eller indataväljare, märkt MUX)
- Analog-till-digital-konverterare (märkt ADC)
Inställningarna i underenheterna volume, mute och multiplexer kan styras av klienter, och API:et DeviceTopology tillhandahåller kontrollgränssnitt till klienter för att kontrollera dem. I det här exemplet har ADC-underenheten inga kontrollinställningar. Api:et DeviceTopology tillhandahåller därför inget kontrollgränssnitt för ADC.
I terminologin för API:et DeviceTopology tillhör anslutningsappar och underenheter samma allmänna kategori – delar. Alla delar, oavsett om de är anslutningsappar eller underenheter, tillhandahåller en gemensam uppsättning funktioner. API:et DeviceTopology implementerar ett IPart--gränssnitt som representerar de allmänna funktioner som är gemensamma för anslutningsappar och underenheter. API:et implementerar IConnector- och ISubunit--gränssnitt för att representera de specifika aspekterna av anslutningsappar och underenheter.
Api:et DeviceTopology konstruerar topologierna för vågavbildningsenheten och indata multiplexerenheten från KS-filtren (kernel-streaming) som ljuddrivrutinen exponerar för operativsystemet för att representera dessa enheter. (Drivrutinen för ljudkortet implementerar IMiniportWaveXxx och IMiniportTopology gränssnitt för att representera de maskinvaruberoende delarna av dessa filter. Mer information om dessa gränssnitt och om KS-filter finns i Dokumentationen om Windows DDK.)
API:et DeviceTopology konstruerar triviala topologier för att representera slutpunktsenheterna A och B i föregående diagram. Enhetstopologin för en slutpunktsenhet består av en enda anslutningsapp. Den här topologin är bara en platshållare för slutpunktsenheten och innehåller inga underenheter för bearbetning av ljuddata. I själva verket innehåller adapterenheter alla underenheter som klientprogram använder för att styra ljudbearbetning. Enhetstopologin för en slutpunktsenhet fungerar främst som en startpunkt för att utforska enhetens topologier för adapterenheter.
Interna anslutningar mellan två delar i en enhetstopologi kallas länkar. Api:et DeviceTopology innehåller metoder för att bläddra länkar från en del till en annan i en enhetstopologi. API:et innehåller också metoder för att korsa anslutningarna mellan enhetstopologier.
Om du vill börja utforska en uppsättning anslutna enhetstopologier aktiverar ett klientprogram IDeviceTopology gränssnitt för en ljudslutpunktsenhet. Anslutningsappen på en slutpunktsenhet ansluter antingen till en anslutningsapp i ett ljudkort eller till ett nätverk. Om slutpunkten ansluter till en enhet på ett ljudkort gör metoderna i API:et DeviceTopology att programmet kan gå över anslutningen från slutpunkten till adaptern genom att hämta en referens till IDeviceTopology gränssnitt för adapterenheten på andra sidan anslutningen. Ett nätverk har däremot ingen enhetstopologi. En nätverksanslutning skickar en ljudström till en klient som har fjärråtkomst till systemet.
Api:et DeviceTopology ger endast åtkomst till topologierna för maskinvaruenheterna i ett ljudkort. De externa enheterna till vänster i diagrammet och programvarukomponenterna till höger ligger utanför API:ets omfång. De streckade linjerna på vardera sidan av diagrammet representerar gränserna för API:et DeviceTopology. Klienten kan använda API:et för att utforska en datasökväg som sträcker sig från indatauttaget till systembussen, men API:et kan inte tränga in utanför dessa gränser.
Varje anslutningsapp i föregående diagram har en associerad anslutningstyp som anger vilken typ av anslutning anslutningen gör. Anslutningsapparna på de två sidorna av en anslutning har därför alltid identiska anslutningstyper. Anslutningstypen anges av ett ConnectorType- uppräkningsvärde – Physical_External, Physical_Internal, Software_Fixed, Software_IO eller Nätverk. Anslutningarna mellan indata-multiplexerenheten och slutpunktsenheterna A och B är av typen Physical_External, vilket innebär att anslutningen representerar en fysisk anslutning till en extern enhet (med andra ord ett användartillgängligt ljuduttag). Anslutningen till den analoga signalen från den interna CD-spelaren är av typen Physical_Internal, vilket indikerar en fysisk anslutning till en extra enhet som är installerad i systemchassit. Anslutningen mellan vågfångstenheten och indata-multiplexerenheten är av typen Software_Fixed, vilket indikerar en permanent anslutning som är fast och inte kan konfigureras under programvarukontroll. Slutligen är anslutningen till systembussen till höger i diagrammet av typen Software_IO, vilket indikerar att data-I/O för anslutningen implementeras av en DMA-motor under programvarukontroll. (Diagrammet innehåller inte ett exempel på en typ av nätverksanslutning.)
Klienten börjar gå igenom en datasökväg på slutpunktsenheten. Först hämtar klienten ett IMMDevice--gränssnitt som representerar slutpunktsenheten, enligt beskrivningen i Räkna upp ljudenheter. För att hämta IDeviceTopology--gränssnittet för slutpunktsenheten anropar klienten metoden IMMDevice::Activate med parametern iid inställd på REFIID IID_IDeviceTopology.
I exemplet i föregående diagram innehåller indata multiplexerenheten alla maskinvarukontroller (volym, ljudavstängning och multiplexer) för avbildningsströmmarna från linjeindata- och mikrofonuttagen. I följande kodexempel visas hur du hämtar IDeviceTopology--gränssnittet för multixer-indataenheten från IMMDevice--gränssnittet för slutpunktsenheten för linjeindata eller mikrofon:
//-----------------------------------------------------------
// 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;
}
Funktionen GetHardwareDeviceTopology i föregående kodexempel utför följande steg för att hämta IDeviceTopology-gränssnittet för indata-multiplexerenheten:
- Anropa metoden IMMDevice::Aktivera för att hämta IDeviceTopology-gränssnittet för slutpunktsenheten.
- Med IDeviceTopology gränssnitt som hämtades i föregående steg anropar du metoden IDeviceTopology::GetConnector för att hämta IConnector--gränssnittet för den enskilda anslutningsappen (anslutningsnummer 0) på slutpunktsenheten.
- Med gränssnittet IConnector som hämtades i föregående steg anropar du metoden IConnector::GetConnectedTo för att hämta IConnector--gränssnittet för anslutningsappen i indata-multiplexerenheten.
- Fråga gränssnittet IConnector som hämtades i föregående steg för dess IPart--gränssnitt.
- Med gränssnittet IPart som hämtades i föregående steg anropar du metoden IPart::GetTopologyObject för att hämta IDeviceTopology-gränssnittet för indata-multiplexerenheten.
Innan användaren kan spela in från mikrofonen i föregående diagram måste klientprogrammet se till att multiplexern väljer mikrofoninmatningen. I följande kodexempel visas hur en klient kan bläddra igenom datasökvägen från mikrofonen tills den hittar multiplexern, som den sedan programmerar för att välja mikrofonindata:
//-----------------------------------------------------------
// 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;
}
API:et DeviceTopology implementerar ett IAudioInputSelector--gränssnitt för att kapsla in en multiplexer, till exempel det i föregående diagram. (Ett IAudioOutputSelector--gränssnitt kapslar in en demultiplexer.) I föregående kodexempel frågar den inre loopen i funktionen SelectCaptureDevice varje underenhet som den hittar för att identifiera om underenheten är en multiplexer. Om underenheten är en multiplexer anropar funktionen metoden IAudioInputSelector::SetSelection för att välja indata som ansluter till strömmen från slutpunktsenheten.
I föregående kodexempel passerar varje iteration av den yttre loopen en enhetstopologi. När du går igenom enhetens topologier i föregående diagram passerar den första iterationen indata-multiplexerenheten och den andra iterationen passerar vågfångstenheten. Funktionen avslutas när den når anslutningsappen till höger i diagrammet. Avslutning sker när funktionen identifierar en anslutningsapp med en Software_IO anslutningstyp. Den här anslutningstypen identifierar den punkt där adapterenheten ansluter till systembussen.
Anropet till metoden IPart::GetPartType i föregående kodexempel hämtar ett IPartType- uppräkningsvärde som anger om den aktuella delen är en anslutningsapp eller en underenhet för ljudbearbetning.
Den inre loopen i föregående kodexempel stegar över länken från en del till en annan genom att anropa metoden IPart::EnumPartsOutgoing. (Det finns också en IPart::EnumPartsIncoming metod för att stega i motsatt riktning.) Den här metoden hämtar ett IPartsList- objekt som innehåller en lista över alla utgående delar. Alla delar som funktionen SelectCaptureDevice förväntar sig att stöta på på en avbildningsenhet har dock alltid exakt en utgående del. Därför begär det efterföljande anropet till IPartsList::GetPart alltid den första delen i listan, delnummer 0, eftersom funktionen förutsätter att detta är den enda delen i listan.
Om funktionen SelectCaptureDevice påträffar en topologi som det antagandet inte är giltigt för, kan funktionen misslyckas med att konfigurera enheten korrekt. För att undvika ett sådant fel kan en mer generell version av funktionen göra följande:
- Anropa metoden IPartsList::GetCount för att fastställa antalet utgående delar.
- För varje utgående del anropar du IPartsList::GetPart för att börja bläddra igenom datasökvägen som leder från delen.
Vissa, men inte nödvändigtvis alla, delar har associerade maskinvarukontroller som klienter kan ange eller hämta. En viss del kan ha noll, en eller flera maskinvarukontroller. En maskinvarukontroll representeras av följande gränssnittspar:
- Ett allmänt kontrollgränssnitt, IControlInterface, som har metoder som är gemensamma för alla maskinvarukontroller.
- Ett funktionsspecifikt gränssnitt (till exempel IAudioVolumeLevel) som exponerar kontrollparametrarna för en viss typ av maskinvarukontroll (till exempel en volymkontroll).
För att räkna upp maskinvarukontrollerna för en del anropar klienten först metoden IPart::GetControlInterfaceCount för att fastställa antalet maskinvarukontroller som är associerade med delen. Därefter gör klienten en serie anrop till metoden IPart::GetControlInterface för att hämta IControlInterface--gränssnittet för varje maskinvarukontroll. Slutligen hämtar klienten det funktionsspecifika gränssnittet för varje maskinvarukontroll genom att anropa metoden IControlInterface::GetIID för att hämta gränssnitts-ID:t. Klienten anropar metoden IPart::Activate med det här ID:t för att hämta det funktionsspecifika gränssnittet.
En del som är en anslutningsapp kan ha stöd för något av följande funktionsspecifika kontrollgränssnitt:
En del som är en underenhet kan ha stöd för ett eller flera av följande funktionsspecifika kontrollgränssnitt:
- IAudioAutoGainControl
- IAudioBass
- IAudioChannelConfig
- IAudioInputSelector
- IAudioLoudness
- IAudioMidrange
- IAudioMute
- IAudioOutputSelector
- IAudioPeakMeter
- IAudioTreble
- IAudioVolumeLevel
- IDeviceSpecificProperty
En del stöder IDeviceSpecificProperty- gränssnitt endast om den underliggande maskinvarukontrollen har ett enhetsspecifikt kontrollvärde och kontrollen inte kan representeras korrekt av något annat funktionsspecifikt gränssnitt i föregående lista. Vanligtvis är en enhetsspecifik egenskap endast användbar för en klient som kan härleda innebörden av egenskapsvärdet från information som deltyp, delundertyp och delnamn. Klienten kan hämta den här informationen genom att anropa metoderna IPart::GetPartType, IPart::GetSubTypeoch IPart::GetName.
Relaterade ämnen