Geräteereignisse (Core Audio APIs)
Ein Geräteereignis benachrichtigt Clients über eine Änderung des Status eines Audioendpunktgeräts im System. Im Folgenden sind Beispiele für Geräteereignisse aufgeführt:
- Der Benutzer aktiviert oder deaktiviert ein Audioendpunktgerät vom Geräte-Manager oder aus der Windows-Multimedia-Systemsteuerung, Mmsys.cpl.
- Der Benutzer fügt dem System einen Audioadapter hinzu oder entfernt einen Audioadapter aus dem System.
- Der Benutzer steckert ein Audioendpunktgerät an eine Audiobuchse mit Erkennung von Jack-Presence oder entfernt ein Audioendpunktgerät aus einer solchen Buchse.
- Der Benutzer ändert die Geräterolle, die einem Gerät zugewiesen ist.
- Der Wert einer Eigenschaft eines Geräts geändert.
Das Hinzufügen oder Entfernen eines Audioadapters generiert Geräteereignisse für alle Audioendpunktgeräte, die eine Verbindung mit dem Adapter herstellen. Die ersten vier Elemente in der vorherigen Liste sind Beispiele für Gerätestatusänderungen. Weitere Informationen zu den Gerätezuständen von Audioendpunktgeräten finden Sie unter DEVICE_STATE_XXX Konstanten. Weitere Informationen zur Erkennung von Jack-Presence finden Sie unter Audio-Endpunktgeräte.
Ein Client kann sich registrieren, um benachrichtigt zu werden, wenn Geräteereignisse auftreten. Als Reaktion auf diese Benachrichtigungen kann der Client dynamisch die Art und Weise ändern, wie es ein bestimmtes Gerät verwendet, oder ein anderes Gerät auswählen, das für einen bestimmten Zweck verwendet werden soll.
Wenn beispielsweise eine Anwendung eine Audiospur über eine Reihe von USB-Lautsprechern abspielt und der Benutzer die Lautsprecher vom USB-Anschluss trennt, empfängt die Anwendung eine Geräteereignisbenachrichtigung. Wenn die Anwendung erkennt, dass eine Reihe von Desktoplautsprechern an den integrierten Audioadapter auf der Hauptplatine des Systems angeschlossen ist, kann die Anwendung die Wiedergabe der Audiospur über die Desktoplautsprecher fortsetzen. In diesem Beispiel erfolgt der Übergang von USB-Lautsprechern zu Desktoplautsprechern automatisch, ohne dass der Benutzer eingreifen muss, indem die Anwendung explizit umgeleitet wird.
Um gerätebenachrichtigungen zu empfangen, ruft ein Client die IMMDeviceEnumerator::RegisterEndpointNotificationCallback-Methode auf. Wenn der Client keine Benachrichtigungen mehr benötigt, wird er durch Aufrufen der IMMDeviceEnumerator::UnregisterEndpointNotificationCallback-Methode abgebrochen. Beide Methoden verwenden einen Eingabeparameter namens pClient-, d. h. einen Zeiger auf eine IMMNotificationClient- Schnittstelleninstanz.
Die IMMNotificationClient Schnittstelle wird von einem Client implementiert. Die Schnittstelle enthält mehrere Methoden, die jeweils als Rückrufroutine für einen bestimmten Geräteereignistyp dienen. Wenn ein Geräteereignis in einem Audioendpunktgerät auftritt, ruft das MMDevice-Modul die entsprechende Methode in der IMMNotificationClient- Schnittstelle jedes Clients auf, der derzeit für den Empfang von Geräteereignisbenachrichtigungen registriert ist. Diese Aufrufe übergeben eine Beschreibung des Ereignisses an die Clients. Weitere Informationen finden Sie unter IMMNotificationClient Interface.
Ein Client, der für den Empfang von Geräteereignisbenachrichtigungen registriert ist, empfängt Benachrichtigungen über alle Arten von Geräteereignissen, die in allen Audioendpunktgeräten im System auftreten. Wenn ein Client nur an bestimmten Ereignistypen oder an bestimmten Geräten interessiert ist, sollten die Methoden in der IMMNotificationClient Implementierung die Ereignisse entsprechend filtern.
Das Windows SDK enthält Beispiele, die mehrere Implementierungen für die IMMNotificationClient Interfaceenthalten. Weitere Informationen finden Sie unter SDK-Beispiele, in denen die core Audio-APIsverwendet werden.
Das folgende Codebeispiel zeigt eine mögliche Implementierung der IMMNotificationClient Schnittstelle:
//-----------------------------------------------------------
// Example implementation of IMMNotificationClient interface.
// When the status of audio endpoint devices change, the
// MMDevice module calls these methods to notify the client.
//-----------------------------------------------------------
#define SAFE_RELEASE(punk) \
if ((punk) != NULL) \
{ (punk)->Release(); (punk) = NULL; }
class CMMNotificationClient : public IMMNotificationClient
{
LONG _cRef;
IMMDeviceEnumerator *_pEnumerator;
// Private function to print device-friendly name
HRESULT _PrintDeviceName(LPCWSTR pwstrId);
public:
CMMNotificationClient() :
_cRef(1),
_pEnumerator(NULL)
{
}
~CMMNotificationClient()
{
SAFE_RELEASE(_pEnumerator)
}
// IUnknown methods -- AddRef, Release, and QueryInterface
ULONG STDMETHODCALLTYPE AddRef()
{
return InterlockedIncrement(&_cRef);
}
ULONG STDMETHODCALLTYPE Release()
{
ULONG ulRef = InterlockedDecrement(&_cRef);
if (0 == ulRef)
{
delete this;
}
return ulRef;
}
HRESULT STDMETHODCALLTYPE QueryInterface(
REFIID riid, VOID **ppvInterface)
{
if (IID_IUnknown == riid)
{
AddRef();
*ppvInterface = (IUnknown*)this;
}
else if (__uuidof(IMMNotificationClient) == riid)
{
AddRef();
*ppvInterface = (IMMNotificationClient*)this;
}
else
{
*ppvInterface = NULL;
return E_NOINTERFACE;
}
return S_OK;
}
// Callback methods for device-event notifications.
HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(
EDataFlow flow, ERole role,
LPCWSTR pwstrDeviceId)
{
char *pszFlow = "?????";
char *pszRole = "?????";
_PrintDeviceName(pwstrDeviceId);
switch (flow)
{
case eRender:
pszFlow = "eRender";
break;
case eCapture:
pszFlow = "eCapture";
break;
}
switch (role)
{
case eConsole:
pszRole = "eConsole";
break;
case eMultimedia:
pszRole = "eMultimedia";
break;
case eCommunications:
pszRole = "eCommunications";
break;
}
printf(" -->New default device: flow = %s, role = %s\n",
pszFlow, pszRole);
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR pwstrDeviceId)
{
_PrintDeviceName(pwstrDeviceId);
printf(" -->Added device\n");
return S_OK;
};
HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR pwstrDeviceId)
{
_PrintDeviceName(pwstrDeviceId);
printf(" -->Removed device\n");
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(
LPCWSTR pwstrDeviceId,
DWORD dwNewState)
{
char *pszState = "?????";
_PrintDeviceName(pwstrDeviceId);
switch (dwNewState)
{
case DEVICE_STATE_ACTIVE:
pszState = "ACTIVE";
break;
case DEVICE_STATE_DISABLED:
pszState = "DISABLED";
break;
case DEVICE_STATE_NOTPRESENT:
pszState = "NOTPRESENT";
break;
case DEVICE_STATE_UNPLUGGED:
pszState = "UNPLUGGED";
break;
}
printf(" -->New device state is DEVICE_STATE_%s (0x%8.8x)\n",
pszState, dwNewState);
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(
LPCWSTR pwstrDeviceId,
const PROPERTYKEY key)
{
_PrintDeviceName(pwstrDeviceId);
printf(" -->Changed device property "
"{%8.8x-%4.4x-%4.4x-%2.2x%2.2x-%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x}#%d\n",
key.fmtid.Data1, key.fmtid.Data2, key.fmtid.Data3,
key.fmtid.Data4[0], key.fmtid.Data4[1],
key.fmtid.Data4[2], key.fmtid.Data4[3],
key.fmtid.Data4[4], key.fmtid.Data4[5],
key.fmtid.Data4[6], key.fmtid.Data4[7],
key.pid);
return S_OK;
}
};
// Given an endpoint ID string, print the friendly device name.
HRESULT CMMNotificationClient::_PrintDeviceName(LPCWSTR pwstrId)
{
HRESULT hr = S_OK;
IMMDevice *pDevice = NULL;
IPropertyStore *pProps = NULL;
PROPVARIANT varString;
CoInitialize(NULL);
PropVariantInit(&varString);
if (_pEnumerator == NULL)
{
// Get enumerator for audio endpoint devices.
hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
NULL, CLSCTX_INPROC_SERVER,
__uuidof(IMMDeviceEnumerator),
(void**)&_pEnumerator);
}
if (hr == S_OK)
{
hr = _pEnumerator->GetDevice(pwstrId, &pDevice);
}
if (hr == S_OK)
{
hr = pDevice->OpenPropertyStore(STGM_READ, &pProps);
}
if (hr == S_OK)
{
// Get the endpoint device's friendly-name property.
hr = pProps->GetValue(PKEY_Device_FriendlyName, &varString);
}
printf("----------------------\nDevice name: \"%S\"\n"
" Endpoint ID string: \"%S\"\n",
(hr == S_OK) ? varString.pwszVal : L"null device",
(pwstrId != NULL) ? pwstrId : L"null ID");
PropVariantClear(&varString);
SAFE_RELEASE(pProps)
SAFE_RELEASE(pDevice)
CoUninitialize();
return hr;
}
Die CMMNotificationClient-Klasse im vorherigen Codebeispiel ist eine Implementierung der IMMNotificationClient--Schnittstelle. Da IMMNotificationClient von IUnknownerbt, enthält die Klassendefinition Implementierungen der IUnknown- Methoden AddRef, Releaseund QueryInterface. Die verbleibenden öffentlichen Methoden in der Klassendefinition sind spezifisch für die IMMNotificationClient--Schnittstelle. Diese Methoden sind:
- OnDefaultDeviceChanged, der aufgerufen wird, wenn der Benutzer die Geräterolle eines Audioendpunktgeräts ändert.
- OnDeviceAdded, das aufgerufen wird, wenn der Benutzer dem System ein Audioendpunktgerät hinzufügt.
- OnDeviceRemoved, der aufgerufen wird, wenn der Benutzer ein Audioendpunktgerät aus dem System entfernt.
- OnDeviceStateChanged, der aufgerufen wird, wenn sich der Gerätestatus eines Audioendpunktgeräts ändert. (Weitere Informationen zu Gerätezuständen finden Sie unter DEVICE_STATE_ XXX-Konstanten.)
- OnPropertyValueChanged, der aufgerufen wird, wenn sich der Wert einer Eigenschaft eines Audioendpunktgeräts ändert.
Jede dieser Methoden verwendet einen Eingabeparameter, pwstrDeviceId, d. h. einen Zeiger auf eine Endpunkt-ID-Zeichenfolge. Die Zeichenfolge gibt das Audioendpunktgerät an, auf dem das Geräteereignis aufgetreten ist.
Im vorherigen Codebeispiel ist _PrintDeviceName eine private Methode in der CMMNotificationClient-Klasse, die den Anzeigenamen des Geräts druckt. _PrintDeviceName verwendet die Endpunkt-ID-Zeichenfolge als Eingabeparameter. Sie übergibt die Zeichenfolge an die IMMDeviceEnumerator::GetDevice-Methode. GetDevice erstellt ein Endpunktgerätobjekt, das das Gerät darstellt, und stellt die IMMDevice- Schnittstelle zu diesem Objekt bereit. Als Nächstes ruft _PrintDeviceName die IMMDevice::OpenPropertyStore--Methode auf, um die IPropertyStore- Schnittstelle für den Eigenschaftenspeicher des Geräts abzurufen. Schließlich ruft _PrintDeviceName die IPropertyStore::GetItem-Methode auf, um die Eigenschaft "Anzeigename" des Geräts abzurufen. Weitere Informationen zu IPropertyStore-finden Sie in der Windows SDK-Dokumentation.
Zusätzlich zu Geräteereignissen können Clients sich registrieren, um Benachrichtigungen über Audiositzungsereignisse und Endpunktvolumeereignisse zu empfangen. Weitere Informationen finden Sie unter IAudioSessionEvents Interface und IAudioEndpointVolumeCallback Interface.
Verwandte Themen