设备事件 (核心音频 API)

您所在的位置:网站首页 windows录音设备功能 设备事件 (核心音频 API)

设备事件 (核心音频 API)

2023-07-29 23:56| 来源: 网络整理| 查看: 265

设备事件 (核心音频 API) 项目 06/13/2023

设备事件通知客户端系统中 音频终结点设备 状态发生更改。 下面是设备事件的示例:

用户从设备管理器或 Windows 多媒体控制面板启用或禁用音频终结点设备,Mmsys.cpl。 用户向系统添加音频适配器或从系统中删除音频适配器。 用户通过插孔存在检测将音频终结点设备插入音频插孔,或者从此类插孔中删除音频终结点设备。 用户更改分配给 设备的设备角色 。 设备属性的值更改。

添加或删除音频适配器会为所有连接到该适配器的音频终结点设备生成设备事件。 上述列表中的前四项是设备状态更改的示例。 有关音频终结点设备的设备状态的详细信息,请参阅 DEVICE_STATE_XXX常量。 有关插孔状态检测的详细信息,请参阅 音频终结点设备。

客户端可以注册,以在发生设备事件时收到通知。 为了响应这些通知,客户端可以动态更改其使用特定设备的方式,或选择要用于特定用途的不同设备。

例如,如果应用程序正在通过一组 USB 扬声器播放音轨,并且用户断开了扬声器与 USB 连接器的连接,则应用程序会收到设备事件通知。 为响应事件,如果应用程序检测到一组桌面扬声器已连接到系统主板上的集成音频适配器,则应用程序可以通过桌面扬声器继续播放音轨。 在此示例中,从 USB 扬声器到桌面扬声器的转换会自动进行,而无需用户通过显式重定向应用程序进行干预。

为了注册以接收设备通知,客户端调用 IMMDeviceEnumerator::RegisterEndpointNotificationCallback 方法。 当客户端不再需要通知时,它会通过调用 IMMDeviceEnumerator::UnregisterEndpointNotificationCallback 方法取消通知。 这两种方法都采用名为 pClient 的输入参数,该参数是指向 IMMNotificationClient 接口实例的指针。

IMMNotificationClient 接口由客户端实现。 接口包含多个方法,每种方法都用作特定类型的设备事件的回调例程。 当音频终结点设备中发生设备事件时,MMDevice 模块在当前注册的每个客户端的 IMMNotificationClient 接口中调用相应的方法,以接收设备事件通知。 这些调用将事件的说明传递给客户端。 有关详细信息,请参阅 IMMNotificationClient 接口。

注册为接收设备事件通知的客户端将接收系统中所有音频终结点设备中发生的所有类型的设备事件的通知。 如果客户端只对某些事件类型或某些设备感兴趣,则其 IMMNotificationClient 实现中的方法应相应地筛选事件。

Windows SDK 提供的示例包括 IMMNotificationClient 接口的多个实现。 有关详细信息,请参阅 使用核心音频 API 的 SDK 示例。

下面的代码示例演示 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; }

上述代码示例中的 CMMNotificationClient 类是 IMMNotificationClient 接口的实现。 由于 IMMNotificationClient 继承自 IUnknown,因此类定义包含 IUnknown 方法 AddRef、 Release 和 QueryInterface 的实现。 类定义中的其余公共方法特定于 IMMNotificationClient 接口。 这些方法包括:

OnDefaultDeviceChanged,当用户更改音频终结点设备的设备角色时调用。 OnDeviceAdded,当用户将音频终结点设备添加到系统时调用。 OnDeviceRemoved,当用户从系统中删除音频终结点设备时调用。 OnDeviceStateChanged,当音频终结点设备的设备状态更改时调用。 (有关设备状态的详细信息,请参阅 DEVICE_STATE_ XXX Constants.) OnPropertyValueChanged,在音频终结点设备的属性值更改时调用。

其中每种方法都采用一个输入参数 pwstrDeviceId,它是指向终结点 ID 字符串的指针。 字符串标识发生设备事件的音频终结点设备。

在前面的代码示例中,_PrintDeviceName 是 CMMNotificationClient 类中的一个私有方法,用于打印设备的友好名称。 _PrintDeviceName采用终结点 ID 字符串作为输入参数。 它将字符串传递给 IMMDeviceEnumerator::GetDevice 方法。 GetDevice 创建一个终结点设备对象来表示设备,并为该对象提供 IMMDevice 接口。 接下来,_PrintDeviceName调用 IMMDevice::OpenPropertyStore 方法以检索设备属性存储的 IPropertyStore 接口。 最后,_PrintDeviceName调用 IPropertyStore::GetItem 方法以获取设备的友好名称属性。 有关 IPropertyStore 的详细信息,请参阅 Windows SDK 文档。

除了设备事件,客户端还可以注册以接收音频会话事件和终结点卷事件的通知。 有关详细信息,请参阅 IAudioSessionEvents 接口 和 IAudioEndpointVolumeCallback 接口。

相关主题

音频终结点设备

 

 



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3