Zdarzenia urządzenia (podstawowe interfejsy API audio)
Zdarzenie urządzenia powiadamia klientów o zmianie stanu urządzenia urządzenia punktu końcowego audio w systemie. Poniżej przedstawiono przykłady zdarzeń urządzenia:
- Użytkownik włącza lub wyłącza urządzenie punktu końcowego audio z Menedżera urządzeń lub z panelu sterowania multimediami systemu Windows, Mmsys.cpl.
- Użytkownik dodaje adapter audio do systemu lub usuwa adapter audio z systemu.
- Użytkownik podłącza urządzenie punktu końcowego audio do gniazda audio z wykrywaniem obecności gniazda lub usuwa urządzenie punktu końcowego audio z takiego gniazda.
- Użytkownik zmienia rolę urządzenia przypisaną do urządzenia.
- Wartość właściwości urządzenia zmienia się.
Dodanie lub usunięcie karty audio generuje zdarzenia urządzenia dla wszystkich urządzeń punktu końcowego audio, które łączą się z kartą. Pierwsze cztery elementy na powyższej liście to przykłady zmian stanu urządzenia. Aby uzyskać więcej informacji na temat stanów urządzeń punktu końcowego audio, zobacz DEVICE_STATE_XXX Stałe. Aby uzyskać więcej informacji na temat wykrywania obecności jacka, zobacz Audio Endpoint Devices.
Klient może zarejestrować się, aby otrzymywać powiadomienia o wystąpieniu zdarzeń urządzenia. W odpowiedzi na te powiadomienia klient może dynamicznie zmieniać sposób korzystania z określonego urządzenia lub wybrać inne urządzenie do użycia w określonym celu.
Jeśli na przykład aplikacja odtwarza ścieżkę dźwiękową przez zestaw głośników USB, a użytkownik odłącza głośniki od łącznika USB, aplikacja otrzymuje powiadomienie o zdarzeniu urządzenia. W odpowiedzi na zdarzenie, jeśli aplikacja wykryje, że zestaw głośników stacjonarnych jest podłączony do zintegrowanej karty audio na płycie głównej systemu, aplikacja może wznowić odtwarzanie ścieżki audio za pośrednictwem głośników stacjonarnych. W tym przykładzie przejście z głośników USB do głośników klasycznych odbywa się automatycznie, bez konieczności interwencji użytkownika przez jawne przekierowanie aplikacji.
Aby zarejestrować się w celu odbierania powiadomień urządzenia, klient wywołuje metodę IMMDeviceEnumerator::RegisterEndpointNotificationCallback. Gdy klient nie wymaga już powiadomień, anuluje je, wywołując metodę IMMDeviceEnumerator::UnregisterEndpointNotificationCallback. Obie metody przyjmują parametr wejściowy o nazwie pClient, który jest wskaźnikiem do wystąpienia interfejsu IMMNotificationClient.
Interfejs IMMNotificationClient jest implementowany przez klienta. Interfejs zawiera kilka metod, z których każda służy jako procedura wywołania zwrotnego dla określonego typu zdarzenia urządzenia. Gdy zdarzenie urządzenia występuje na urządzeniu punktu końcowego audio, moduł MMDevice wywołuje odpowiednią metodę w IMMNotificationClient interfejsu każdego klienta, który jest obecnie zarejestrowany w celu odbierania powiadomień o zdarzeniach urządzenia. Te wywołania przekazują opis zdarzenia do klientów. Aby uzyskać więcej informacji, zobacz interfejsU IMMNotificationClient.
Klient zarejestrowany do odbierania powiadomień o zdarzeniach urządzenia będzie otrzymywać powiadomienia o wszystkich typach zdarzeń urządzeń występujących we wszystkich urządzeniach punktu końcowego audio w systemie. Jeśli klient jest zainteresowany tylko niektórymi typami zdarzeń lub określonymi urządzeniami, metody w jego implementacji IMMNotificationClient powinny odpowiednio filtrować zdarzenia.
Zestaw Windows SDK zawiera przykłady obejmujące kilka implementacji interfejsu IMMNotificationClient. Aby uzyskać więcej informacji, zobacz przykłady zestawu SDK korzystające z podstawowych interfejsów API audio.
Poniższy przykład kodu przedstawia możliwą implementację interfejsu IMMNotificationClient:
//-----------------------------------------------------------
// 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;
}
Klasa CMMNotificationClient w poprzednim przykładzie kodu jest implementacją interfejsu IMMNotificationClient. Ponieważ IMMNotificationClient dziedziczy z IUnknown, definicja klasy zawiera implementacje metod IUnknownAddRef, Releasei QueryInterface. Pozostałe metody publiczne w definicji klasy są specyficzne dla interfejsu IMMNotificationClient. Oto następujące metody:
- OnDefaultDeviceChanged, który jest wywoływany, gdy użytkownik zmienia rolę urządzenia urządzenia punktu końcowego audio.
- OnDeviceAdded, który jest wywoływany, gdy użytkownik dodaje urządzenie punktu końcowego audio do systemu.
- OnDeviceRemoved, który jest wywoływany, gdy użytkownik usuwa urządzenie punktu końcowego audio z systemu.
- OnDeviceStateChanged, który jest wywoływany, gdy zmieni się stan urządzenia punktu końcowego audio. (Aby uzyskać więcej informacji na temat stanów urządzeń, zobacz DEVICE_STATE_ XXX Stałe.)
- OnPropertyValueChanged, który jest wywoływany, gdy wartość właściwości urządzenia punktu końcowego audio ulegnie zmianie.
Każda z tych metod przyjmuje parametr wejściowy, pwstrDeviceId, który jest wskaźnikiem do ciągu identyfikatora punktu końcowego. Ciąg identyfikuje urządzenie punktu końcowego audio, w którym wystąpiło zdarzenie urządzenia.
W poprzednim przykładzie kodu _PrintDeviceName jest prywatną metodą w klasie CMMNotificationClient, która wyświetla przyjazną nazwę urządzenia. _PrintDeviceName przyjmuje ciąg identyfikatora punktu końcowego jako parametr wejściowy. Przekazuje on ciąg do metody IMMDeviceEnumerator::GetDevice. GetDevice tworzy obiekt urządzenia punktu końcowego do reprezentowania urządzenia i udostępnia interfejs IMMDevice dla tego obiektu. Następnie _PrintDeviceName wywołuje metodę IMMDevice::OpenPropertyStore, aby pobrać interfejs IPropertyStore do magazynu właściwości urządzenia. Na koniec _PrintDeviceName wywołuje metodę IPropertyStore::GetItem, aby uzyskać przyjazną nazwę urządzenia. Aby uzyskać więcej informacji na temat IPropertyStore, zobacz dokumentację zestawu Windows SDK.
Oprócz zdarzeń urządzenia klienci mogą rejestrować się w celu odbierania powiadomień o zdarzeniach sesji audio i zdarzeniach woluminu punktu końcowego. Aby uzyskać więcej informacji, zobacz interfejs IAudioSessionEvents Interface i interfejsU IAudioEndpointVolumeCallback.
Tematy pokrewne