📄 volumeapi.cpp
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
#include "VolumeApi.hpp"
#include "Common.hpp"
#include "Debug.hpp"
#include <regext.h>
#include "VoIPNotify.hpp"
#include "VolumeAPI.hpp"
const DWORD c_MaxVolume = 0xFFFF;
class Mixer_t
{
public:
Mixer_t();
~Mixer_t();
HRESULT
Open(
void
);
HRESULT
SetSourceVolume(
DWORD Volume
);
HRESULT
GetSourceVolume(
__out DWORD* pVolume
);
private:
HRESULT
GetCaptureSource(
UINT* pSourceIndex
);
private:
static const WCHAR sc_DeviceIdPath[];
static const WCHAR sc_DeviceIdValue[];
HMIXEROBJ m_MixerHandle; // mixer handle
UINT m_MuxID; // control ID for the source Mux
UINT m_SourcesCount; // number of capture sources (e.g. CD, Aux, Mic)
DWORD m_VolumeControlIdOfCaptureSource; // Volume control Id of current capture source
};
const WCHAR Mixer_t::sc_DeviceIdPath[] = L"System\\State\\VoIP";
const WCHAR Mixer_t::sc_DeviceIdValue[] = L"Mixer Device Id";
Mixer_t::Mixer_t()
: m_MixerHandle(NULL)
{
;
}
Mixer_t::~Mixer_t()
{
if (m_MixerHandle != NULL)
{
mixerClose((HMIXER) m_MixerHandle);
}
}
/*------------------------------------------------------------------------------
Mixer_t::Open
Description
Parameters:
DeviceId:
------------------------------------------------------------------------------*/
HRESULT
Mixer_t::Open(
void
)
{
DWORD DeviceId = 0;
// trying to get the device Id from registry, if it failed, we use the default device id, which is 0
RegistryGetDWORD(
HKEY_LOCAL_MACHINE,
sc_DeviceIdPath,
sc_DeviceIdValue,
&DeviceId
);
// Start off by opening the device, using the device ID
MMRESULT MMResult = mixerOpen((HMIXER *) &m_MixerHandle, static_cast<UINT>(DeviceId), 0, 0, 0);
if (MMResult != MMSYSERR_NOERROR)
{
COMMON_DEBUGMSG(ZONE_COMMON_ERROR, (L"Failed at opening mixer for device %d", DeviceId));
return CommonUtilities_t::ToHR(MMResult);
}
ASSERT(m_MixerHandle != NULL);
// Mixer devices expose several different lines, one for each input/output source
// Find the line corresponding to PCM capture
MIXERLINE MixerLine;
MixerLine.cbStruct = sizeof(MixerLine);
MixerLine.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
MMResult = mixerGetLineInfo(m_MixerHandle, &MixerLine, MIXER_GETLINEINFOF_COMPONENTTYPE);
if (MMResult != MMSYSERR_NOERROR)
{
return CommonUtilities_t::ToHR(MMResult);
}
m_SourcesCount = MixerLine.cConnections;
// wave-in destinations have a Multiplexer (mux) control to select between input sources
// we need the Control ID for the capture source select mux
MIXERCONTROL MixerControl;
MixerControl.cbStruct = sizeof(MixerControl);
MIXERLINECONTROLS MixerControlQuery;
MixerControlQuery.cbStruct = sizeof(MixerControlQuery);
MixerControlQuery.dwLineID = MixerLine.dwLineID;
MixerControlQuery.cControls = 1;
MixerControlQuery.dwControlType = MIXERCONTROL_CONTROLTYPE_MUX;
MixerControlQuery.cbmxctrl = sizeof(MixerControl);
MixerControlQuery.pamxctrl = &MixerControl;
MMResult = mixerGetLineControls(m_MixerHandle, &MixerControlQuery, MIXER_GETLINECONTROLSF_ONEBYTYPE);
if (MMResult != MMSYSERR_NOERROR)
{
return CommonUtilities_t::ToHR(MMResult);
}
if (MixerControl.dwControlType != MIXERCONTROL_CONTROLTYPE_MUX ||
MixerControl.cMultipleItems != m_SourcesCount)
{
// something's gone terribly wrong
COMMON_DEBUGMSG(ZONE_COMMON_ERROR, (L"The control is not a mux, or doesn't have the right number of entries!\n"));
return E_UNEXPECTED;
}
m_MuxID = MixerControl.dwControlID;
UINT CurrentCaptureSourceId;
HRESULT hr = GetCaptureSource(&CurrentCaptureSourceId);
if (FAILED(hr))
{
return hr;
}
MIXERLINE SourceLine;
// first, retrieve the info for the source line
SourceLine.cbStruct = sizeof(SourceLine);
SourceLine.dwDestination = MixerLine.dwDestination;
SourceLine.dwSource = CurrentCaptureSourceId;
MMResult = mixerGetLineInfo(m_MixerHandle,&SourceLine, MIXER_GETLINEINFOF_SOURCE);
if (MMResult != MMSYSERR_NOERROR)
{
return CommonUtilities_t::ToHR(MMResult);
}
// now, retrieve the info for the volume control for the source line
memset(&MixerControl, 0, sizeof(MIXERCONTROL));
MixerControl.cbStruct = sizeof(MixerControl);
memset(&MixerControlQuery, 0, sizeof(MIXERLINECONTROLS));
MixerControlQuery.cbStruct = sizeof(MixerControlQuery);
MixerControlQuery.dwLineID = SourceLine.dwLineID;
MixerControlQuery.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
MixerControlQuery.cControls = 1;
MixerControlQuery.cbmxctrl = sizeof(MixerControl);
MixerControlQuery.pamxctrl = &MixerControl;
MMResult = mixerGetLineControls(m_MixerHandle, &MixerControlQuery, MIXER_GETLINECONTROLSF_ONEBYTYPE);
if (MMResult != MMSYSERR_NOERROR)
{
return CommonUtilities_t::ToHR(MMResult);
}
m_VolumeControlIdOfCaptureSource = MixerControl.dwControlID;
return S_OK;
}
/*------------------------------------------------------------------------------
Mixer_t::SetSourceVolume
Uses mixerSetControlDetails to set the volume for a particular capture source
------------------------------------------------------------------------------*/
HRESULT
Mixer_t::SetSourceVolume(
DWORD Volume
)
{
if (m_MixerHandle == NULL)
{
return E_UNEXPECTED;
}
Volume = LOWORD(Volume); // the volume should ranges from 0x0000 - 0xffff
MMRESULT MMResult;
MIXERCONTROLDETAILS MixerDetails;
MixerDetails.cbStruct = sizeof(MixerDetails);
MixerDetails.dwControlID = m_VolumeControlIdOfCaptureSource;
MixerDetails.cChannels = 1;
MixerDetails.cMultipleItems = 0;
MixerDetails.cbDetails = sizeof(DWORD);
MixerDetails.paDetails = &Volume;
MMResult = mixerSetControlDetails(m_MixerHandle, &MixerDetails, MIXER_SETCONTROLDETAILSF_VALUE );
if (MMResult != MMSYSERR_NOERROR)
{
COMMON_DEBUGMSG(ZONE_COMMON_ERROR, (L"Failed at setting source volume!"));
return CommonUtilities_t::ToHR(MMResult);
}
return S_OK;
}
/*------------------------------------------------------------------------------
Mixer_t::GetSourceVolume
Uses mixerGetControlDetails to get the volume for a particular capture source
------------------------------------------------------------------------------*/
HRESULT
Mixer_t::GetSourceVolume(
DWORD* pVolume
)
{
if (pVolume == NULL)
{
return E_INVALIDARG;
}
*pVolume = 0;
if (m_MixerHandle == NULL)
{
return E_UNEXPECTED;
}
MMRESULT MMResult;
MIXERCONTROLDETAILS MixerDetails;
MixerDetails.cbStruct = sizeof(MixerDetails);
MixerDetails.dwControlID = m_VolumeControlIdOfCaptureSource;
MixerDetails.cChannels = 1;
MixerDetails.cMultipleItems = 0;
MixerDetails.cbDetails = sizeof(DWORD);
MixerDetails.paDetails = pVolume;
MMResult = mixerGetControlDetails(m_MixerHandle, &MixerDetails, MIXER_GETCONTROLDETAILSF_VALUE );
if (MMResult != MMSYSERR_NOERROR)
{
COMMON_DEBUGMSG(ZONE_COMMON_ERROR, (L"Failed at setting source volume!"));
return CommonUtilities_t::ToHR(MMResult);
}
return S_OK;
}
/*------------------------------------------------------------------------------
Mixer_t::GetCaptureSource
uses mixerGetControlDetails to query the state of the record-select mux
Note that mux controls return their state in a peculiar way. Rather
than return the index of the selected source, the mux control wants
to fill in an array of booleans with a unique entry set to TRUE, corresponding
to the selected source.
------------------------------------------------------------------------------*/
HRESULT
Mixer_t::GetCaptureSource(
UINT* pSourceIndex
)
{
if (pSourceIndex == NULL)
{
return E_INVALIDARG;
}
*pSourceIndex = 0;
if (m_MixerHandle == NULL)
{
return E_UNEXPECTED;
}
// allocate and populate the source table and mux-setting table
BOOL* pMuxTable = new BOOL[m_SourcesCount];
if (pMuxTable == NULL)
{
return E_OUTOFMEMORY;
}
// clear the mux table to be sure we get the real scoop from the driver
memset(pMuxTable, 0, m_SourcesCount * sizeof(pMuxTable[0]));
MMRESULT MMResult;
HRESULT hr = E_FAIL;
MIXERCONTROLDETAILS MuxSetting;
MuxSetting.cbStruct = sizeof(MuxSetting);
MuxSetting.hwndOwner = NULL;
MuxSetting.cChannels = 1;
MuxSetting.cMultipleItems = m_SourcesCount;
MuxSetting.dwControlID = m_MuxID;
MuxSetting.paDetails = pMuxTable;
MuxSetting.cbDetails = m_SourcesCount * sizeof(pMuxTable[0]);
MMResult = mixerGetControlDetails(m_MixerHandle, &MuxSetting, MIXER_GETCONTROLDETAILSF_VALUE );
if (MMResult != MMSYSERR_NOERROR)
{
hr = CommonUtilities_t::ToHR(MMResult);
goto exit;
}
for (UINT i=0; i < m_SourcesCount; i++)
{
if (pMuxTable[i])
{
*pSourceIndex = i;
hr = S_OK;
break;
}
}
exit:
if (pMuxTable != NULL)
{
delete [] pMuxTable;
}
return hr;
}
/*------------------------------------------------------------------------------
UIVolumeToAudioVolume
Convert UI volume to audio volume
------------------------------------------------------------------------------*/
DWORD
UIVolumeToAudioVolume(
DWORD UIVolume,
bool Stereo
)
{
if (UIVolume < 0 || UIVolume > c_MaxUIVolume)
{
return 0;
}
DWORD AudioVolume = (UIVolume * c_MaxVolume / c_MaxUIVolume);
if (Stereo)
{
AudioVolume = LOWORD(AudioVolume) | (LOWORD(AudioVolume) << 16);
}
return AudioVolume;
}
/*------------------------------------------------------------------------------
AudioVolumeToUIVolume
Convert Audio volume to UI volume
------------------------------------------------------------------------------*/
DWORD
AudioVolumeToUIVolume(
DWORD AudioVolume
)
{
//because UI volume and audio volume have different scale, it is possible that 2 adjacent volume
//can fall into same UI volume slot. Therefore, we need to round up a little bit (c_MaxVolume / c_MaxUIVolume / 2)
//to make sure they always fall into different UI slot
return (LOWORD(AudioVolume) + c_MaxVolume / c_MaxUIVolume / 2) * c_MaxUIVolume / c_MaxVolume;
}
/*------------------------------------------------------------------------------
GetWaveOutVolume
Get true volume from waveOut API
------------------------------------------------------------------------------*/
HRESULT
GetWaveOutVolume(
__out DWORD* pVolume
)
{
ASSERT(pVolume != NULL);
*pVolume = 0;
MMRESULT Result = waveOutGetVolume(0, pVolume);
if (Result != MMSYSERR_NOERROR)
{
return E_FAIL;
}
*pVolume = AudioVolumeToUIVolume(*pVolume);
return S_OK;
}
/*------------------------------------------------------------------------------
SetWaveOutVolume
apply the current UI volume to waveOut API
------------------------------------------------------------------------------*/
HRESULT
SetWaveOutVolume(
DWORD UIVolume
)
{
if (UIVolume < 0 || UIVolume > c_MaxUIVolume)
{
return E_INVALIDARG;
}
DWORD Volume = UIVolumeToAudioVolume(UIVolume, true);
MMRESULT Result = waveOutSetVolume(0, Volume);
if (Result != MMSYSERR_NOERROR)
{
return E_FAIL;
}
return S_OK;
}
/*------------------------------------------------------------------------------
GetRingerVolume
Get ringer volume
------------------------------------------------------------------------------*/
HRESULT
GetRingerVolume(
__out DWORD* pVolume
)
{
if (pVolume == NULL)
{
return E_INVALIDARG;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -