|
|
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "audio_pch.h"
#include "voice_mixer_controls.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
// NOTE: Vista deprecated these APIs
// Under vista these settings are per-session (not persistent)
// The correct method is to use the AudioEndpoint COM objects to manage controls like this
// The interface is not 1:1 so for now we'll just back the state with convars and reapply it
// on init of the mixer controls. In the future when XP is no longer the majority of our user base
// we should revisit this and move to the new API.
// http://msdn.microsoft.com/en-us/library/aa964574(VS.85).aspx
class CMixerControls : public IMixerControls { public: CMixerControls(); virtual ~CMixerControls();
virtual bool GetValue_Float(Control iControl, float &value); virtual bool SetValue_Float(Control iControl, float value); virtual bool SelectMicrophoneForWaveInput();
private: bool Init(); void Term();
void Clear();
bool GetControlOption_Bool(DWORD dwControlID, DWORD cMultipleItems, bool &bValue); bool SetControlOption_Bool(DWORD dwControlID, DWORD cMultipleItems, const bool bValue);
bool GetControlOption_Unsigned(DWORD dwControlID, DWORD cMultipleItems, DWORD &value); bool SetControlOption_Unsigned(DWORD dwControlID, DWORD cMultipleItems, const DWORD value);
bool GetLineControls( DWORD dwLineID, MIXERCONTROL *controls, DWORD nControls ); void FindMicSelectControl( DWORD dwLineID, DWORD nControls );
private: HMIXER m_hMixer;
class ControlInfo { public: DWORD m_dwControlID; DWORD m_cMultipleItems; bool m_bFound; };
DWORD m_dwMicSelectControlID; DWORD m_dwMicSelectMultipleItems; DWORD m_dwMicSelectControlType; DWORD m_dwMicSelectIndex; // Info about the controls we found.
ControlInfo m_ControlInfos[NumControls]; };
CMixerControls::CMixerControls() { m_dwMicSelectControlID = 0xFFFFFFFF;
Clear(); Init(); }
CMixerControls::~CMixerControls() { Term(); }
bool CMixerControls::Init() { Term();
MMRESULT mmr;
bool bFoundMixer = false; bool bFoundConnectionWithMicVolume = false; CUtlVectorFixedGrowable<MIXERCONTROL, 64> controls; // Iterate over all the devices
// This is done in reverse so the 0th device is our fallback if none of them had the correct MicVolume control
for ( int iDevice = static_cast<int>( mixerGetNumDevs() ) - 1; iDevice >= 0 && !bFoundConnectionWithMicVolume; --iDevice ) { // Open the mixer.
mmr = mixerOpen(&m_hMixer, (DWORD)iDevice, 0, 0, 0 ); if(mmr != MMSYSERR_NOERROR) { continue; }
// Iterate over each destination line, looking for Play Controls.
MIXERCAPS mxcaps; mmr = mixerGetDevCaps((UINT)m_hMixer, &mxcaps, sizeof(mxcaps)); if(mmr != MMSYSERR_NOERROR) { continue; }
bFoundMixer = true;
for(UINT u = 0; u < mxcaps.cDestinations; u++) { MIXERLINE recordLine; recordLine.cbStruct = sizeof(recordLine); recordLine.dwDestination = u; mmr = mixerGetLineInfo((HMIXEROBJ)m_hMixer, &recordLine, MIXER_GETLINEINFOF_DESTINATION); if(mmr != MMSYSERR_NOERROR) continue;
// Go through the controls that aren't attached to a specific src connection.
// We're looking for the checkbox that enables the user's microphone for waveIn.
if( recordLine.dwComponentType == MIXERLINE_COMPONENTTYPE_DST_WAVEIN ) { FindMicSelectControl( recordLine.dwLineID, recordLine.cControls ); }
// Now iterate over each connection (things like wave out, microphone, speaker, CD audio), looking for Microphone.
UINT cConnections = (UINT)recordLine.cConnections; for (UINT v = 0; v < cConnections; v++) { MIXERLINE micLine; micLine.cbStruct = sizeof(micLine); micLine.dwDestination = u; micLine.dwSource = v;
mmr = mixerGetLineInfo((HMIXEROBJ)m_hMixer, &micLine, MIXER_GETLINEINFOF_SOURCE); if(mmr != MMSYSERR_NOERROR) continue;
// Now look at all the controls (volume, mute, boost, etc).
controls.RemoveAll(); controls.SetCount(micLine.cControls); if( !GetLineControls( micLine.dwLineID, controls.Base(), micLine.cControls ) ) continue;
for(UINT i=0; i < micLine.cControls; i++) { MIXERCONTROL *pControl = &controls[i];
if(micLine.dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE) { if( pControl->dwControlType == MIXERCONTROL_CONTROLTYPE_ONOFF && ( strstr(pControl->szShortName, "Gain") || strstr(pControl->szShortName, "Boos") || strstr(pControl->szShortName, "+20d") ) ) { // This is the (record) boost option.
m_ControlInfos[MicBoost].m_bFound = true; m_ControlInfos[MicBoost].m_dwControlID = pControl->dwControlID; m_ControlInfos[MicBoost].m_cMultipleItems = pControl->cMultipleItems; }
if(recordLine.dwComponentType == MIXERLINE_COMPONENTTYPE_DST_SPEAKERS && pControl->dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE) { // This is the mute button.
m_ControlInfos[MicMute].m_bFound = true; m_ControlInfos[MicMute].m_dwControlID = pControl->dwControlID; m_ControlInfos[MicMute].m_cMultipleItems = pControl->cMultipleItems; } if(recordLine.dwComponentType == MIXERLINE_COMPONENTTYPE_DST_WAVEIN && pControl->dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME) { // This is the mic input level.
m_ControlInfos[MicVolume].m_bFound = true; m_ControlInfos[MicVolume].m_dwControlID = pControl->dwControlID; m_ControlInfos[MicVolume].m_cMultipleItems = pControl->cMultipleItems;
// We found a good recording device and can stop looking throught the available devices
bFoundConnectionWithMicVolume = true; } } } } } }
if ( !bFoundMixer ) { // Failed to find any mixer (MixVolume or not)
Term(); return false; }
return true; }
void CMixerControls::Term() { if(m_hMixer) { mixerClose(m_hMixer); m_hMixer = 0; }
Clear(); }
bool CMixerControls::GetValue_Float( Control iControl, float &flValue ) { if( iControl < 0 || iControl >= NumControls || !m_ControlInfos[iControl].m_bFound ) return false;
if(iControl == MicBoost || iControl == MicMute) { bool bValue = false; bool ret = GetControlOption_Bool(m_ControlInfos[iControl].m_dwControlID, m_ControlInfos[iControl].m_cMultipleItems, bValue); flValue = (float)bValue; return ret; } else if(iControl == MicVolume) { DWORD dwValue = (DWORD)0; if(GetControlOption_Unsigned(m_ControlInfos[iControl].m_dwControlID, m_ControlInfos[iControl].m_cMultipleItems, dwValue)) { flValue = dwValue / 65535.0f; return true; } }
return false; }
bool CMixerControls::SetValue_Float(Control iControl, float flValue ) { if(iControl < 0 || iControl >= NumControls || !m_ControlInfos[iControl].m_bFound) return false;
if(iControl == MicBoost || iControl == MicMute) { bool bValue = !!flValue; return SetControlOption_Bool(m_ControlInfos[iControl].m_dwControlID, m_ControlInfos[iControl].m_cMultipleItems, bValue); } else if(iControl == MicVolume) { DWORD dwValue = (DWORD)(flValue * 65535.0f); return SetControlOption_Unsigned(m_ControlInfos[iControl].m_dwControlID, m_ControlInfos[iControl].m_cMultipleItems, dwValue); } return false; }
bool CMixerControls::SelectMicrophoneForWaveInput() { if( m_dwMicSelectControlID == 0xFFFFFFFF ) return false;
MIXERCONTROLDETAILS_BOOLEAN *pmxcdSelectValue = (MIXERCONTROLDETAILS_BOOLEAN*)_alloca( sizeof(MIXERCONTROLDETAILS_BOOLEAN) * m_dwMicSelectMultipleItems );
MIXERCONTROLDETAILS mxcd; mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS); mxcd.dwControlID = m_dwMicSelectControlID; mxcd.cChannels = 1; mxcd.cMultipleItems = m_dwMicSelectMultipleItems; mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN); mxcd.paDetails = pmxcdSelectValue; if (mixerGetControlDetails(reinterpret_cast<HMIXEROBJ>(m_hMixer), &mxcd, MIXER_OBJECTF_HMIXER | MIXER_GETCONTROLDETAILSF_VALUE) == MMSYSERR_NOERROR) { // MUX restricts the line selection to one source line at a time.
if( m_dwMicSelectControlType == MIXERCONTROL_CONTROLTYPE_MUX ) { ZeroMemory(pmxcdSelectValue, m_dwMicSelectMultipleItems * sizeof(MIXERCONTROLDETAILS_BOOLEAN)); }
// set the Microphone value
pmxcdSelectValue[m_dwMicSelectIndex].fValue = 1;
mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS); mxcd.dwControlID = m_dwMicSelectControlID; mxcd.cChannels = 1; mxcd.cMultipleItems = m_dwMicSelectMultipleItems; mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN); mxcd.paDetails = pmxcdSelectValue; if (mixerSetControlDetails(reinterpret_cast<HMIXEROBJ>(m_hMixer), &mxcd, MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE) == MMSYSERR_NOERROR) { return true; } }
return false; }
void CMixerControls::Clear() { m_hMixer = 0; memset(m_ControlInfos, 0, sizeof(m_ControlInfos)); }
bool CMixerControls::GetControlOption_Bool(DWORD dwControlID, DWORD cMultipleItems, bool &bValue) { MIXERCONTROLDETAILS details; MIXERCONTROLDETAILS_BOOLEAN controlValue;
details.cbStruct = sizeof(details); details.dwControlID = dwControlID; details.cChannels = 1; // uniform..
details.cMultipleItems = cMultipleItems; details.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN); details.paDetails = &controlValue; MMRESULT mmr = mixerGetControlDetails((HMIXEROBJ)m_hMixer, &details, 0L); if(mmr == MMSYSERR_NOERROR) { bValue = !!controlValue.fValue; return true; } else { return false; } }
bool CMixerControls::SetControlOption_Bool(DWORD dwControlID, DWORD cMultipleItems, const bool bValue) { MIXERCONTROLDETAILS details; MIXERCONTROLDETAILS_BOOLEAN controlValue;
details.cbStruct = sizeof(details); details.dwControlID = dwControlID; details.cChannels = 1; // uniform..
details.cMultipleItems = cMultipleItems; details.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN); details.paDetails = &controlValue;
controlValue.fValue = bValue; MMRESULT mmr = mixerSetControlDetails((HMIXEROBJ)m_hMixer, &details, 0L); return mmr == MMSYSERR_NOERROR; }
bool CMixerControls::GetControlOption_Unsigned(DWORD dwControlID, DWORD cMultipleItems, DWORD &value) { MIXERCONTROLDETAILS details; MIXERCONTROLDETAILS_UNSIGNED controlValue;
details.cbStruct = sizeof(details); details.dwControlID = dwControlID; details.cChannels = 1; // uniform..
details.cMultipleItems = cMultipleItems; details.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED); details.paDetails = &controlValue; MMRESULT mmr = mixerGetControlDetails((HMIXEROBJ)m_hMixer, &details, 0L); if(mmr == MMSYSERR_NOERROR) { value = controlValue.dwValue; return true; } else { return false; } }
bool CMixerControls::SetControlOption_Unsigned(DWORD dwControlID, DWORD cMultipleItems, const DWORD value) { MIXERCONTROLDETAILS details; MIXERCONTROLDETAILS_UNSIGNED controlValue;
details.cbStruct = sizeof(details); details.dwControlID = dwControlID; details.cChannels = 1; // uniform..
details.cMultipleItems = cMultipleItems; details.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED); details.paDetails = &controlValue;
controlValue.dwValue = value; MMRESULT mmr = mixerSetControlDetails((HMIXEROBJ)m_hMixer, &details, 0L); return mmr == MMSYSERR_NOERROR; }
bool CMixerControls::GetLineControls( DWORD dwLineID, MIXERCONTROL *controls, DWORD nControls ) { MIXERLINECONTROLS mxlc;
mxlc.cbStruct = sizeof(mxlc); mxlc.dwLineID = dwLineID; mxlc.cControls = nControls; mxlc.cbmxctrl = sizeof(MIXERCONTROL); mxlc.pamxctrl = controls; MMRESULT mmr = mixerGetLineControls((HMIXEROBJ)m_hMixer, &mxlc, MIXER_GETLINECONTROLSF_ALL); return mmr == MMSYSERR_NOERROR; }
void CMixerControls::FindMicSelectControl( DWORD dwLineID, DWORD nControls ) { m_dwMicSelectControlID = 0xFFFFFFFF;
MIXERCONTROL *recControls = (MIXERCONTROL*)_alloca( sizeof(MIXERCONTROL) * nControls ); if( !GetLineControls( dwLineID, recControls, nControls ) ) return;
for( UINT iRecControl=0; iRecControl < nControls; iRecControl++ ) { if( recControls[iRecControl].dwControlType == MIXERCONTROL_CONTROLTYPE_MIXER || recControls[iRecControl].dwControlType == MIXERCONTROL_CONTROLTYPE_MUX ) { m_dwMicSelectControlID = recControls[iRecControl].dwControlID; m_dwMicSelectControlType = recControls[iRecControl].dwControlType; m_dwMicSelectMultipleItems = recControls[iRecControl].cMultipleItems; m_dwMicSelectIndex = iRecControl;
// Get the index of the one that selects the mic.
MIXERCONTROLDETAILS_LISTTEXT *pmxcdSelectText = (MIXERCONTROLDETAILS_LISTTEXT*)_alloca( sizeof(MIXERCONTROLDETAILS_LISTTEXT) * m_dwMicSelectMultipleItems );
MIXERCONTROLDETAILS mxcd; mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS); mxcd.dwControlID = m_dwMicSelectControlID; mxcd.cChannels = 1; mxcd.cMultipleItems = m_dwMicSelectMultipleItems; mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXT); mxcd.paDetails = pmxcdSelectText; if (mixerGetControlDetails((HMIXEROBJ)m_hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_GETCONTROLDETAILSF_LISTTEXT) == MMSYSERR_NOERROR) { // determine which controls the Microphone source line
for (DWORD dwi = 0; dwi < m_dwMicSelectMultipleItems; dwi++) { // get the line information
MIXERLINE mxl; mxl.cbStruct = sizeof(MIXERLINE); mxl.dwLineID = pmxcdSelectText[dwi].dwParam1; if (mixerGetLineInfo((HMIXEROBJ)m_hMixer, &mxl, MIXER_OBJECTF_HMIXER | MIXER_GETLINEINFOF_LINEID) == MMSYSERR_NOERROR && mxl.dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE) { // found, dwi is the index.
m_dwMicSelectIndex = dwi; break; } } }
break; } } }
IMixerControls* g_pMixerControls = NULL; void InitMixerControls() { if ( !g_pMixerControls ) { g_pMixerControls = new CMixerControls; } }
void ShutdownMixerControls() { delete g_pMixerControls; g_pMixerControls = NULL; }
|