summaryrefslogtreecommitdiff
path: root/engine/audio/private/voice_mixer_controls.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /engine/audio/private/voice_mixer_controls.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'engine/audio/private/voice_mixer_controls.cpp')
-rw-r--r--engine/audio/private/voice_mixer_controls.cpp503
1 files changed, 503 insertions, 0 deletions
diff --git a/engine/audio/private/voice_mixer_controls.cpp b/engine/audio/private/voice_mixer_controls.cpp
new file mode 100644
index 0000000..44c6a94
--- /dev/null
+++ b/engine/audio/private/voice_mixer_controls.cpp
@@ -0,0 +1,503 @@
+//========= 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;
+}
+