Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

503 lines
14 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "audio_pch.h"
  8. #include "voice_mixer_controls.h"
  9. // memdbgon must be the last include file in a .cpp file!!!
  10. #include "tier0/memdbgon.h"
  11. // NOTE: Vista deprecated these APIs
  12. // Under vista these settings are per-session (not persistent)
  13. // The correct method is to use the AudioEndpoint COM objects to manage controls like this
  14. // The interface is not 1:1 so for now we'll just back the state with convars and reapply it
  15. // on init of the mixer controls. In the future when XP is no longer the majority of our user base
  16. // we should revisit this and move to the new API.
  17. // http://msdn.microsoft.com/en-us/library/aa964574(VS.85).aspx
  18. class CMixerControls : public IMixerControls
  19. {
  20. public:
  21. CMixerControls();
  22. virtual ~CMixerControls();
  23. virtual bool GetValue_Float(Control iControl, float &value);
  24. virtual bool SetValue_Float(Control iControl, float value);
  25. virtual bool SelectMicrophoneForWaveInput();
  26. private:
  27. bool Init();
  28. void Term();
  29. void Clear();
  30. bool GetControlOption_Bool(DWORD dwControlID, DWORD cMultipleItems, bool &bValue);
  31. bool SetControlOption_Bool(DWORD dwControlID, DWORD cMultipleItems, const bool bValue);
  32. bool GetControlOption_Unsigned(DWORD dwControlID, DWORD cMultipleItems, DWORD &value);
  33. bool SetControlOption_Unsigned(DWORD dwControlID, DWORD cMultipleItems, const DWORD value);
  34. bool GetLineControls( DWORD dwLineID, MIXERCONTROL *controls, DWORD nControls );
  35. void FindMicSelectControl( DWORD dwLineID, DWORD nControls );
  36. private:
  37. HMIXER m_hMixer;
  38. class ControlInfo
  39. {
  40. public:
  41. DWORD m_dwControlID;
  42. DWORD m_cMultipleItems;
  43. bool m_bFound;
  44. };
  45. DWORD m_dwMicSelectControlID;
  46. DWORD m_dwMicSelectMultipleItems;
  47. DWORD m_dwMicSelectControlType;
  48. DWORD m_dwMicSelectIndex;
  49. // Info about the controls we found.
  50. ControlInfo m_ControlInfos[NumControls];
  51. };
  52. CMixerControls::CMixerControls()
  53. {
  54. m_dwMicSelectControlID = 0xFFFFFFFF;
  55. Clear();
  56. Init();
  57. }
  58. CMixerControls::~CMixerControls()
  59. {
  60. Term();
  61. }
  62. bool CMixerControls::Init()
  63. {
  64. Term();
  65. MMRESULT mmr;
  66. bool bFoundMixer = false;
  67. bool bFoundConnectionWithMicVolume = false;
  68. CUtlVectorFixedGrowable<MIXERCONTROL, 64> controls;
  69. // Iterate over all the devices
  70. // This is done in reverse so the 0th device is our fallback if none of them had the correct MicVolume control
  71. for ( int iDevice = static_cast<int>( mixerGetNumDevs() ) - 1; iDevice >= 0 && !bFoundConnectionWithMicVolume; --iDevice )
  72. {
  73. // Open the mixer.
  74. mmr = mixerOpen(&m_hMixer, (DWORD)iDevice, 0, 0, 0 );
  75. if(mmr != MMSYSERR_NOERROR)
  76. {
  77. continue;
  78. }
  79. // Iterate over each destination line, looking for Play Controls.
  80. MIXERCAPS mxcaps;
  81. mmr = mixerGetDevCaps((UINT)m_hMixer, &mxcaps, sizeof(mxcaps));
  82. if(mmr != MMSYSERR_NOERROR)
  83. {
  84. continue;
  85. }
  86. bFoundMixer = true;
  87. for(UINT u = 0; u < mxcaps.cDestinations; u++)
  88. {
  89. MIXERLINE recordLine;
  90. recordLine.cbStruct = sizeof(recordLine);
  91. recordLine.dwDestination = u;
  92. mmr = mixerGetLineInfo((HMIXEROBJ)m_hMixer, &recordLine, MIXER_GETLINEINFOF_DESTINATION);
  93. if(mmr != MMSYSERR_NOERROR)
  94. continue;
  95. // Go through the controls that aren't attached to a specific src connection.
  96. // We're looking for the checkbox that enables the user's microphone for waveIn.
  97. if( recordLine.dwComponentType == MIXERLINE_COMPONENTTYPE_DST_WAVEIN )
  98. {
  99. FindMicSelectControl( recordLine.dwLineID, recordLine.cControls );
  100. }
  101. // Now iterate over each connection (things like wave out, microphone, speaker, CD audio), looking for Microphone.
  102. UINT cConnections = (UINT)recordLine.cConnections;
  103. for (UINT v = 0; v < cConnections; v++)
  104. {
  105. MIXERLINE micLine;
  106. micLine.cbStruct = sizeof(micLine);
  107. micLine.dwDestination = u;
  108. micLine.dwSource = v;
  109. mmr = mixerGetLineInfo((HMIXEROBJ)m_hMixer, &micLine, MIXER_GETLINEINFOF_SOURCE);
  110. if(mmr != MMSYSERR_NOERROR)
  111. continue;
  112. // Now look at all the controls (volume, mute, boost, etc).
  113. controls.RemoveAll();
  114. controls.SetCount(micLine.cControls);
  115. if( !GetLineControls( micLine.dwLineID, controls.Base(), micLine.cControls ) )
  116. continue;
  117. for(UINT i=0; i < micLine.cControls; i++)
  118. {
  119. MIXERCONTROL *pControl = &controls[i];
  120. if(micLine.dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE)
  121. {
  122. if( pControl->dwControlType == MIXERCONTROL_CONTROLTYPE_ONOFF &&
  123. (
  124. strstr(pControl->szShortName, "Gain") ||
  125. strstr(pControl->szShortName, "Boos") ||
  126. strstr(pControl->szShortName, "+20d")
  127. )
  128. )
  129. {
  130. // This is the (record) boost option.
  131. m_ControlInfos[MicBoost].m_bFound = true;
  132. m_ControlInfos[MicBoost].m_dwControlID = pControl->dwControlID;
  133. m_ControlInfos[MicBoost].m_cMultipleItems = pControl->cMultipleItems;
  134. }
  135. if(recordLine.dwComponentType == MIXERLINE_COMPONENTTYPE_DST_SPEAKERS &&
  136. pControl->dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE)
  137. {
  138. // This is the mute button.
  139. m_ControlInfos[MicMute].m_bFound = true;
  140. m_ControlInfos[MicMute].m_dwControlID = pControl->dwControlID;
  141. m_ControlInfos[MicMute].m_cMultipleItems = pControl->cMultipleItems;
  142. }
  143. if(recordLine.dwComponentType == MIXERLINE_COMPONENTTYPE_DST_WAVEIN &&
  144. pControl->dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)
  145. {
  146. // This is the mic input level.
  147. m_ControlInfos[MicVolume].m_bFound = true;
  148. m_ControlInfos[MicVolume].m_dwControlID = pControl->dwControlID;
  149. m_ControlInfos[MicVolume].m_cMultipleItems = pControl->cMultipleItems;
  150. // We found a good recording device and can stop looking throught the available devices
  151. bFoundConnectionWithMicVolume = true;
  152. }
  153. }
  154. }
  155. }
  156. }
  157. }
  158. if ( !bFoundMixer )
  159. {
  160. // Failed to find any mixer (MixVolume or not)
  161. Term();
  162. return false;
  163. }
  164. return true;
  165. }
  166. void CMixerControls::Term()
  167. {
  168. if(m_hMixer)
  169. {
  170. mixerClose(m_hMixer);
  171. m_hMixer = 0;
  172. }
  173. Clear();
  174. }
  175. bool CMixerControls::GetValue_Float( Control iControl, float &flValue )
  176. {
  177. if( iControl < 0 || iControl >= NumControls || !m_ControlInfos[iControl].m_bFound )
  178. return false;
  179. if(iControl == MicBoost || iControl == MicMute)
  180. {
  181. bool bValue = false;
  182. bool ret = GetControlOption_Bool(m_ControlInfos[iControl].m_dwControlID, m_ControlInfos[iControl].m_cMultipleItems, bValue);
  183. flValue = (float)bValue;
  184. return ret;
  185. }
  186. else if(iControl == MicVolume)
  187. {
  188. DWORD dwValue = (DWORD)0;
  189. if(GetControlOption_Unsigned(m_ControlInfos[iControl].m_dwControlID, m_ControlInfos[iControl].m_cMultipleItems, dwValue))
  190. {
  191. flValue = dwValue / 65535.0f;
  192. return true;
  193. }
  194. }
  195. return false;
  196. }
  197. bool CMixerControls::SetValue_Float(Control iControl, float flValue )
  198. {
  199. if(iControl < 0 || iControl >= NumControls || !m_ControlInfos[iControl].m_bFound)
  200. return false;
  201. if(iControl == MicBoost || iControl == MicMute)
  202. {
  203. bool bValue = !!flValue;
  204. return SetControlOption_Bool(m_ControlInfos[iControl].m_dwControlID, m_ControlInfos[iControl].m_cMultipleItems, bValue);
  205. }
  206. else if(iControl == MicVolume)
  207. {
  208. DWORD dwValue = (DWORD)(flValue * 65535.0f);
  209. return SetControlOption_Unsigned(m_ControlInfos[iControl].m_dwControlID, m_ControlInfos[iControl].m_cMultipleItems, dwValue);
  210. }
  211. return false;
  212. }
  213. bool CMixerControls::SelectMicrophoneForWaveInput()
  214. {
  215. if( m_dwMicSelectControlID == 0xFFFFFFFF )
  216. return false;
  217. MIXERCONTROLDETAILS_BOOLEAN *pmxcdSelectValue =
  218. (MIXERCONTROLDETAILS_BOOLEAN*)_alloca( sizeof(MIXERCONTROLDETAILS_BOOLEAN) * m_dwMicSelectMultipleItems );
  219. MIXERCONTROLDETAILS mxcd;
  220. mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
  221. mxcd.dwControlID = m_dwMicSelectControlID;
  222. mxcd.cChannels = 1;
  223. mxcd.cMultipleItems = m_dwMicSelectMultipleItems;
  224. mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
  225. mxcd.paDetails = pmxcdSelectValue;
  226. if (mixerGetControlDetails(reinterpret_cast<HMIXEROBJ>(m_hMixer),
  227. &mxcd,
  228. MIXER_OBJECTF_HMIXER |
  229. MIXER_GETCONTROLDETAILSF_VALUE)
  230. == MMSYSERR_NOERROR)
  231. {
  232. // MUX restricts the line selection to one source line at a time.
  233. if( m_dwMicSelectControlType == MIXERCONTROL_CONTROLTYPE_MUX )
  234. {
  235. ZeroMemory(pmxcdSelectValue,
  236. m_dwMicSelectMultipleItems * sizeof(MIXERCONTROLDETAILS_BOOLEAN));
  237. }
  238. // set the Microphone value
  239. pmxcdSelectValue[m_dwMicSelectIndex].fValue = 1;
  240. mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
  241. mxcd.dwControlID = m_dwMicSelectControlID;
  242. mxcd.cChannels = 1;
  243. mxcd.cMultipleItems = m_dwMicSelectMultipleItems;
  244. mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
  245. mxcd.paDetails = pmxcdSelectValue;
  246. if (mixerSetControlDetails(reinterpret_cast<HMIXEROBJ>(m_hMixer),
  247. &mxcd,
  248. MIXER_OBJECTF_HMIXER |
  249. MIXER_SETCONTROLDETAILSF_VALUE) == MMSYSERR_NOERROR)
  250. {
  251. return true;
  252. }
  253. }
  254. return false;
  255. }
  256. void CMixerControls::Clear()
  257. {
  258. m_hMixer = 0;
  259. memset(m_ControlInfos, 0, sizeof(m_ControlInfos));
  260. }
  261. bool CMixerControls::GetControlOption_Bool(DWORD dwControlID, DWORD cMultipleItems, bool &bValue)
  262. {
  263. MIXERCONTROLDETAILS details;
  264. MIXERCONTROLDETAILS_BOOLEAN controlValue;
  265. details.cbStruct = sizeof(details);
  266. details.dwControlID = dwControlID;
  267. details.cChannels = 1; // uniform..
  268. details.cMultipleItems = cMultipleItems;
  269. details.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
  270. details.paDetails = &controlValue;
  271. MMRESULT mmr = mixerGetControlDetails((HMIXEROBJ)m_hMixer, &details, 0L);
  272. if(mmr == MMSYSERR_NOERROR)
  273. {
  274. bValue = !!controlValue.fValue;
  275. return true;
  276. }
  277. else
  278. {
  279. return false;
  280. }
  281. }
  282. bool CMixerControls::SetControlOption_Bool(DWORD dwControlID, DWORD cMultipleItems, const bool bValue)
  283. {
  284. MIXERCONTROLDETAILS details;
  285. MIXERCONTROLDETAILS_BOOLEAN controlValue;
  286. details.cbStruct = sizeof(details);
  287. details.dwControlID = dwControlID;
  288. details.cChannels = 1; // uniform..
  289. details.cMultipleItems = cMultipleItems;
  290. details.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
  291. details.paDetails = &controlValue;
  292. controlValue.fValue = bValue;
  293. MMRESULT mmr = mixerSetControlDetails((HMIXEROBJ)m_hMixer, &details, 0L);
  294. return mmr == MMSYSERR_NOERROR;
  295. }
  296. bool CMixerControls::GetControlOption_Unsigned(DWORD dwControlID, DWORD cMultipleItems, DWORD &value)
  297. {
  298. MIXERCONTROLDETAILS details;
  299. MIXERCONTROLDETAILS_UNSIGNED controlValue;
  300. details.cbStruct = sizeof(details);
  301. details.dwControlID = dwControlID;
  302. details.cChannels = 1; // uniform..
  303. details.cMultipleItems = cMultipleItems;
  304. details.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
  305. details.paDetails = &controlValue;
  306. MMRESULT mmr = mixerGetControlDetails((HMIXEROBJ)m_hMixer, &details, 0L);
  307. if(mmr == MMSYSERR_NOERROR)
  308. {
  309. value = controlValue.dwValue;
  310. return true;
  311. }
  312. else
  313. {
  314. return false;
  315. }
  316. }
  317. bool CMixerControls::SetControlOption_Unsigned(DWORD dwControlID, DWORD cMultipleItems, const DWORD value)
  318. {
  319. MIXERCONTROLDETAILS details;
  320. MIXERCONTROLDETAILS_UNSIGNED controlValue;
  321. details.cbStruct = sizeof(details);
  322. details.dwControlID = dwControlID;
  323. details.cChannels = 1; // uniform..
  324. details.cMultipleItems = cMultipleItems;
  325. details.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
  326. details.paDetails = &controlValue;
  327. controlValue.dwValue = value;
  328. MMRESULT mmr = mixerSetControlDetails((HMIXEROBJ)m_hMixer, &details, 0L);
  329. return mmr == MMSYSERR_NOERROR;
  330. }
  331. bool CMixerControls::GetLineControls( DWORD dwLineID, MIXERCONTROL *controls, DWORD nControls )
  332. {
  333. MIXERLINECONTROLS mxlc;
  334. mxlc.cbStruct = sizeof(mxlc);
  335. mxlc.dwLineID = dwLineID;
  336. mxlc.cControls = nControls;
  337. mxlc.cbmxctrl = sizeof(MIXERCONTROL);
  338. mxlc.pamxctrl = controls;
  339. MMRESULT mmr = mixerGetLineControls((HMIXEROBJ)m_hMixer, &mxlc, MIXER_GETLINECONTROLSF_ALL);
  340. return mmr == MMSYSERR_NOERROR;
  341. }
  342. void CMixerControls::FindMicSelectControl( DWORD dwLineID, DWORD nControls )
  343. {
  344. m_dwMicSelectControlID = 0xFFFFFFFF;
  345. MIXERCONTROL *recControls = (MIXERCONTROL*)_alloca( sizeof(MIXERCONTROL) * nControls );
  346. if( !GetLineControls( dwLineID, recControls, nControls ) )
  347. return;
  348. for( UINT iRecControl=0; iRecControl < nControls; iRecControl++ )
  349. {
  350. if( recControls[iRecControl].dwControlType == MIXERCONTROL_CONTROLTYPE_MIXER ||
  351. recControls[iRecControl].dwControlType == MIXERCONTROL_CONTROLTYPE_MUX )
  352. {
  353. m_dwMicSelectControlID = recControls[iRecControl].dwControlID;
  354. m_dwMicSelectControlType = recControls[iRecControl].dwControlType;
  355. m_dwMicSelectMultipleItems = recControls[iRecControl].cMultipleItems;
  356. m_dwMicSelectIndex = iRecControl;
  357. // Get the index of the one that selects the mic.
  358. MIXERCONTROLDETAILS_LISTTEXT *pmxcdSelectText =
  359. (MIXERCONTROLDETAILS_LISTTEXT*)_alloca( sizeof(MIXERCONTROLDETAILS_LISTTEXT) * m_dwMicSelectMultipleItems );
  360. MIXERCONTROLDETAILS mxcd;
  361. mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
  362. mxcd.dwControlID = m_dwMicSelectControlID;
  363. mxcd.cChannels = 1;
  364. mxcd.cMultipleItems = m_dwMicSelectMultipleItems;
  365. mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXT);
  366. mxcd.paDetails = pmxcdSelectText;
  367. if (mixerGetControlDetails((HMIXEROBJ)m_hMixer,
  368. &mxcd,
  369. MIXER_OBJECTF_HMIXER |
  370. MIXER_GETCONTROLDETAILSF_LISTTEXT) == MMSYSERR_NOERROR)
  371. {
  372. // determine which controls the Microphone source line
  373. for (DWORD dwi = 0; dwi < m_dwMicSelectMultipleItems; dwi++)
  374. {
  375. // get the line information
  376. MIXERLINE mxl;
  377. mxl.cbStruct = sizeof(MIXERLINE);
  378. mxl.dwLineID = pmxcdSelectText[dwi].dwParam1;
  379. if (mixerGetLineInfo((HMIXEROBJ)m_hMixer,
  380. &mxl,
  381. MIXER_OBJECTF_HMIXER |
  382. MIXER_GETLINEINFOF_LINEID) == MMSYSERR_NOERROR &&
  383. mxl.dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE)
  384. {
  385. // found, dwi is the index.
  386. m_dwMicSelectIndex = dwi;
  387. break;
  388. }
  389. }
  390. }
  391. break;
  392. }
  393. }
  394. }
  395. IMixerControls* g_pMixerControls = NULL;
  396. void InitMixerControls()
  397. {
  398. if ( !g_pMixerControls )
  399. {
  400. g_pMixerControls = new CMixerControls;
  401. }
  402. }
  403. void ShutdownMixerControls()
  404. {
  405. delete g_pMixerControls;
  406. g_pMixerControls = NULL;
  407. }