最近在做winsows上音频方面的程序,用到Core Audio APIs系列API来设置相关音频设备参数,所以对用到这方面的知识做一个总结。Core Audio APIs是Windows Vista家族后提供一套新的底层API,主要有以下几个内容
Multimedia Device (MMDevice) API:用来枚举操作系统自带的音频终端设备对象。
Windows Audio Session API (WASAPI):根据第一步枚举处理终端设备对象创建和管理音频流。
DeviceTopology API:使用此类API可以直接访问音频适配器中的硬件数据通路的拓扑特性(如音量控制,多路复用器等)。
EndpointVolume API:使用此类API可以直接访问音频设备的声音控制,这类API主要是给那些毒战模式管理音频流的应用程序。
下面让我们用代码事例设置麦克风和扬声器音量大小,以及设置麦克风加强来说明相关用法:
1. 首先我们定义相关COM接口如下,后面要用到:
CComPtr<IMMDeviceEnumerator > m_pIMMEnumerator; //主要用于枚举设备接口 CComPtr<IAudioEndpointVolume> m_pRenderEndptVol; //扬声器音量控制接口 CComPtr<ISimpleAudioVolume> m_pRenderSimpleVol; //扬声器的会话音量控制接口
2.创建IMMDeviceEnumerator接口,IMMDeviceEnumerator接口是获取后面设备对象的根本,所以先要创建这个接口,用常用创建COM接口的CoCreateInstance()来创建,指定相应的参数,很基本用法,不做过多说明。
HRESULT hr = S_OK; hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, IID_IMMDeviceEnumerator, (void**)&m_pIMMEnumerator);
pIMMEnumerator对象创建好,我们可以看看这个接口可以用的方法:
class IMMDeviceEnumerator : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE EnumAudioEndpoints(EDataFlow dataFlow, DWORD dwStateMask, IMMDeviceCollection **ppDevices) = 0; virtual HRESULT STDMETHODCALLTYPE GetDefaultAudioEndpoint(EDataFlow dataFlow, ERole role, IMMDevice **ppEndpoint) = 0; virtual HRESULT STDMETHODCALLTYPE GetDevice(LPCWSTR pwstrId, IMMDevice **ppDevice) = 0; virtual HRESULT STDMETHODCALLTYPE RegisterEndpointNotificationCallback(IMMNotificationClient *pClient) = 0; virtual HRESULT STDMETHODCALLTYPE UnregisterEndpointNotificationCallback(IMMNotificationClient *pClient) = 0; };
可以看到除过IUnKonwn接口方法外有属于自己的五个方法,分别如下:
//枚举系统所有设备 EnumAudioEndpoints(EDataFlow dataFlow, DWORD dwStateMask, IMMDeviceCollection **ppDevices); //获取系统默认音频设备,类似主音频设备 GetDefaultAudioEndpoint(EDataFlow dataFlow, ERole role, IMMDevice **ppEndpoint); //根据设备ID获取指定的设备,设备ID可以通过EnumAudioEndpoints()获取,也可以通过DSound函数DirectSoundEnumerate()等获取。 GetDevice(LPCWSTR pwstrId, IMMDevice **ppDevice); //注册给终端设备一些通知事件,注册后比如当设备拨出、插入、有变化时会收到相应的通知事件。 RegisterEndpointNotificationCallback(IMMNotificationClient *pClient); //注销掉之前注册的通知回调。 UnregisterEndpointNotificationCallback();
本次我们主要是用GetDefaultAudioEndpoint()和GetDevice()这两个接口方法。
4.获取IAudioEndpointVolume扬声器m_pRenderEndptVol接口,首先我们要定义一个IMMDevice对象,根据刚刚创建的m_pIMMEnumerator对象获取相应的IMMDevice对象,然后用获取的IMMDevice对象激活m_pRenderEndptVol接口。
如果之前没有获取终端ID则可以使用
//EDataFlow/有三个常用的变量: //eAll : 会列举出系统中所有的音频设备包括Render --- 扬声器, Capture --- Microphone, Stereo Mixer; //eRender: 会列举出系统中所有的音频播放设备; //eCapture: 会列举出系统中所有音频采集设备; //ERole 设备角色,假如系统中有多个音频设备,那么一个设备可能用户是用来播放电影的,另一个可能是用来玩游戏的,因此就引入了角色的概念。 //eConsole : 与计算机交互; eCommunications : 与他人的声音交流; eMultimedia : 播放或者录制电影和音乐,一般默认使用eConsole。 //ppEndpoint:需要接收获取设备对象指针。 HRESULT GetDefaultAudioEndpoint(EDataFlow dataFlow, ERole role, IMMDevice **ppEndpoint);
如果之前已经枚举出设备ID,想要根据设备ID过去指定的设备对象,可以使用
//pwstrId: 已经获取的设备ID; //ppDevice:需要接收获取设备对象二重指针; HRESULT GetDevice(LPCWSTR pwstrId, IMMDevice **ppDevice)
示例代码如下:
HRESULT hr = S_OK; CComPtr<IMMDevice> pIMMDeivce = NULL; if(strEndPointID.empty()) { hr = m_pIMMEnumerator->GetDefaultAudioEndpoint(eRender,eConsole,&pIMMDeivce); } else { hr = m_pIMMEnumerator->GetDevice(strEndPointID.c_s(),&pIMMDeivce); }获取pIMMDeivce对象后,就可以用这个对象接口激活自己想要控制的相应音量设备的接口了,先看下IMMDevice接口拥有属于自己的方法:
class IMMDevice : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE Activate(REFIID iid, DWORD dwClsCtx, PROPVARIANT *pActivationParams, void **ppInterface) = 0; virtual HRESULT STDMETHODCALLTYPE OpenPropertyStore(DWORD stgmAccess,IPropertyStore **ppProperties) = 0; virtual HRESULT STDMETHODCALLTYPE GetId(LPWSTR *ppstrId) = 0; virtual HRESULT STDMETHODCALLTYPE GetState(DWORD *pdwState) = 0; };简单注释下这个几个函数意义:
//激活这个设备下指定接口的COM对象; //iid:指定获取接口的GUID; //dwClsCtx:执行上下文,一般指定CLSCTX_ALL获取所有信息; //pActivationParams:对于IID_IAudioEndpointVolume接口设置为NULL。 //ppInterface:需要接收获取设备对象二重指针; HRESULT Activate(REFIID iid, DWORD dwClsCtx, PROPVARIANT *pActivationParams, void **ppInterface) //打开一个设备的属性存储接口; HRESULT OpenPropertyStore(DWORD stgmAccess,IPropertyStore **ppProperties) //获取设备ID; HRESULT GetId(LPWSTR *ppstrId) //获取设备状态信息; HRESULT GetState(DWORD *pdwState)我们主要用的是Activate()方法,首先我们获取扬声器音量控制接口m_pRenderEndptVol,如下
//主声音设备 hr = pIMMDeivce->Activate(IID_IAudioEndpointVolume, CLSCTX_ALL, NULL, (void**)&m_pRenderEndptVol);获取到m_pRenderEndptVol接口后我们就可以用这个接口设置系统扬声器音量了。
5.设置系统扬声器音量,也就是主音量,先看看刚刚我们获取m_pRenderEndptVol接口拥有属于自己的方法:
class IAudioEndpointVolume : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE RegisterControlChangeNotify(IAudioEndpointVolumeCallback *pNotify) = 0; virtual HRESULT STDMETHODCALLTYPE UnregisterControlChangeNotify(IAudioEndpointVolumeCallback *pNotify) = 0; virtual HRESULT STDMETHODCALLTYPE GetChannelCount(UINT *pnChannelCount) = 0; virtual HRESULT STDMETHODCALLTYPE SetMasterVolumeLevel(float fLevelDB,LPCGUID pguidEventContext) = 0; virtual HRESULT STDMETHODCALLTYPE SetMasterVolumeLevelScalar(float fLevel,LPCGUID pguidEventContext) = 0; virtual HRESULT STDMETHODCALLTYPE GetMasterVolumeLevel(float *pfLevelDB) = 0; virtual HRESULT STDMETHODCALLTYPE GetMasterVolumeLevelScalar(float *pfLevel) = 0; virtual HRESULT STDMETHODCALLTYPE SetChannelVolumeLevel(UINT nChannel,float fLevelDB,LPCGUID pguidEventContext) = 0; virtual HRESULT STDMETHODCALLTYPE SetChannelVolumeLevelScalar(UINT nChannel,float fLevel,LPCGUID pguidEventContext) = 0; virtual HRESULT STDMETHODCALLTYPE GetChannelVolumeLevel(UINT nChannel,float *pfLevelDB) = 0; virtual HRESULT STDMETHODCALLTYPE GetChannelVolumeLevelScalar(UINT nChannel,float *pfLevel) = 0; virtual HRESULT STDMETHODCALLTYPE SetMute(BOOL bMute,LPCGUID pguidEventContext) = 0; virtual HRESULT STDMETHODCALLTYPE GetMute(BOOL *pbMute) = 0; virtual HRESULT STDMETHODCALLTYPE GetVolumeStepInfo(UINT *pnStep,UINT *pnStepCount) = 0; virtual HRESULT STDMETHODCALLTYPE VolumeStepUp(LPCGUID pguidEventContext) = 0; virtual HRESULT STDMETHODCALLTYPE VolumeStepDown(LPCGUID pguidEventContext) = 0; virtual HRESULT STDMETHODCALLTYPE QueryHardwareSupport(DWORD *pdwHardwareSupportMask) = 0; virtual HRESULT STDMETHODCALLTYPE GetVolumeRange(float *pflVolumeMindB,float *pflVolumeMaxdB,float *pflVolumeIncrementdB) = 0; };这个IAudioEndpointVolume接口方法很多,不仔细介绍每一个了,只介绍我们需要用到的几个方法,大家可以根据自己的需要,顾名思义找到自己需要用的方法。
/* 设置音频终端的主音量电; fLevel:主音量数值级别,这个值范围在0到1之间; pguidEventContext):此参数指向一个事件上下文的GUID,如果SetMasterVolumeLevelScalar调用更改端点的音量,所有已注册客户IAudioEndpointVolumeCallback接口与端点将接收通知。 */ HRESULT SetMasterVolumeLevelScalar(float fLevel,LPCGUID pguidEventContext); //获取音频终端的主音量电平; HRESULT GetMasterVolumeLevelScalar(float *pfLevel); //设置音频终端是否为静音状态; HRESULT SetMute(BOOL bMute,LPCGUID pguidEventContext); //获取音频终端是静音状态; HRESULT GetMute(BOOL *pbMute);其余几个参数很简单,看名字就知道什么意思,不作过多介绍。
设置系统扬声器音量代码示例:
float level = 0.5f;//这个值可以跟进相应比例换算所得 hr = m_pRenderEndptVol->SetMasterVolumeLevelScalar(level, NULL);获取系统扬声器音量代码示例:
float level = 0.0f; hr = m_pRenderEndptVol->GetMasterVolumeLevelScalar(&level);注意level这个值一般根据界面滑动条最大值按比例换算所得。
设置系统扬声器静音示例:
hr = m_pRenderSimpleVol->SetMute(TRUE, NULL);
获取系统扬声器静音状态示例:
BOOL bMute = 0; hr = m_pRenderSimpleVol->GetMute(&bMute);这只是简单的几种参数设置,大家可以根据自己的需要来选择不同方法达到自己目的。
6.设置应用程序会话音量
上面设置的都是系统扬声器的音量,即相应的设置会影响系统上所有软件的音量,下面我们来设置属于应用程序本身的会话音量,所谓会话音量即时这个应用程序本身的音量,设置会话音量的大小只会影响应用程序本身的音量大小,不会影响系统其它程序音量的大小。首先我们要获取IAudioSessionManager接口对象,方法是通过已经获取IMMDevice接口对象来激活IAudioSessionManager接口对象,然后就可以通过IAudioSessionManager接口来获取ISimpleAudioVolume接口对象了,最后通过ISimpleAudioVolume接口对象就可以操作会话音量的大小了。示例代码如下:
//合成器界面 CComPtr<IAudioSessionManager> pSessionManager = NULL; hr = pIMMDeivce->Activate(IID_IAudioSessionManager, CLSCTX_INPROC_SERVER, NULL, (void **)(&pSessionManager)); if(FAILED(hr)) return; hr = pSessionManager->GetSimpleAudioVolume(NULL, FALSE, &m_pRenderSimpleVol);
获取到ISimpleAudioVolume接口对象后就可以使用其具有的方法操作具体的功能了,看下这个接口有的方法:
class ISimpleAudioVolume : public IUnknown { public: virtual HRESULT STDMETHODCALLTYPE SetMasterVolume(float fLevel, LPCGUID EventContext) = 0; virtual HRESULT STDMETHODCALLTYPE GetMasterVolume(float *pfLevel) = 0; virtual HRESULT STDMETHODCALLTYPE SetMute(const BOOL bMute, LPCGUID EventContext) = 0; virtual HRESULT STDMETHODCALLTYPE GetMute(BOOL *pbMute) = 0; };所有方法和上面IAudioEndpointVolume接口方法基本一样,直接贴代码说明
设置和获取会话音量的大小:
//设置扬声器会话音量大小 float fVol = 0.5f; hr = m_pRenderSimpleVol->SetMasterVolume(fVol, NULL); //获取扬声器会话音量大小 float fVol = 0.0f; hr = m_pRenderSimpleVol->GetMasterVolume(&fVol);设置和获取会话音量的静音状态:
//设置扬声器会话音量静音状态 BOOL bMute = TRUE; hr = m_pRenderSimpleVol->SetMute(bMute , NULL); //获取扬声器静音状态 hr = m_pRenderSimpleVol->GetMute(&bMute);扬声器里面的功能设置还有很多方法,介绍只是最基本的方法,大家可以根据自己需要挖掘更深层的设置方法,这一节就先介绍到这里吧,下一节介绍系统麦克风方面一些简单的设置。