Leaked source code of windows server 2003
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.

981 lines
29 KiB

  1. #include "precomp.h"
  2. #include <mixer.h>
  3. //
  4. // private APIs
  5. //
  6. #define MAX_MICROPHONE_DEVS 10
  7. static MMRESULT mixerGetControlValue ( HMIXER, MIXVOLUME *, DWORD, UINT );
  8. static MMRESULT mixerSetControlValue ( HMIXER, MIXVOLUME *, DWORD, UINT );
  9. static MMRESULT mixerGetControlId ( HMIXER, DWORD *, DWORD, DWORD );
  10. static MMRESULT mixerGetControlByType ( HMIXER, DWORD, DWORD, MIXERCONTROL *pMixerControl);
  11. // used for AGC detection
  12. static const char *szGain = "gain";
  13. static const char *szBoost = "boost";
  14. static const char *szAGC = "agc";
  15. struct AGCDetails
  16. {
  17. WORD wMID; // manufacturer ID
  18. WORD wPID; // product ID
  19. DWORD dwAGCID; // AGC ID
  20. };
  21. // we shouldn't have to use this table scheme anymore.
  22. // CMixerDevice::DetectAGC will autodetect the control ID
  23. static const AGCDetails AGCList[] =
  24. {
  25. // MID PID AGCID
  26. {2, 409, 27}, // Creative Labs
  27. {21, 42, 13}, // Turtle Beach Tropez
  28. {132, 3, 2072}, // Crystal MMX
  29. {384, 7, 28}, // Xitel Storm 3d PCI
  30. {385, 32, 35} // Aztech PCI-331
  31. };
  32. static BOOL GetAGCID(WORD wMID, WORD wPID, DWORD *pdwAGCID)
  33. {
  34. int nIndex;
  35. int nAGCEntries = sizeof(AGCList) / sizeof(AGCDetails);
  36. for (nIndex = 0; nIndex < nAGCEntries; nIndex++)
  37. {
  38. if ( (AGCList[nIndex].wMID == wMID) &&
  39. (AGCList[nIndex].wPID == wPID))
  40. {
  41. *pdwAGCID = AGCList[nIndex].dwAGCID;
  42. return TRUE;
  43. }
  44. }
  45. return FALSE;
  46. }
  47. /********************************************************************************\
  48. * *
  49. * void NewMixVolume(MIXVOLUME* lpMixVolDest, const MIXVOLUME& mixVolSource, *
  50. * DWORD dwNewVolume) *
  51. * *
  52. * NewMixVolume takes the current mixer value (mixVolSource), and converts it *
  53. * to its adjusted value (lpMixVolDest) using the new volume settings *
  54. * (dwNewVolume) and the proportionality between the left and right volumes of *
  55. * mixVolSource. Note that if mixVolSource's left and right volumes are both *
  56. * equal to zero, then their proportionality is set to 1/1. *
  57. * *
  58. * March 2001 Matthew Maddin (mmaddin@microsoft) *
  59. * *
  60. \********************************************************************************/
  61. void NewMixVolume(MIXVOLUME* lpMixVolDest, const MIXVOLUME& mixVolSource, DWORD dwNewVolume)
  62. {
  63. // If the source's left volume is the greater than the right volume,
  64. // then it is the used to calculate the proportionality.
  65. if(mixVolSource.leftVolume == max(mixVolSource.leftVolume, mixVolSource.rightVolume))
  66. {
  67. // Set the left volume to the new value.
  68. lpMixVolDest->leftVolume = dwNewVolume;
  69. // If the left volume is non-zero, then continue with the proportionality calculation.
  70. if(mixVolSource.leftVolume)
  71. {
  72. // Calculate proportionality using the equation: NewRight = OldRight * (NewLeft/OldLeft)
  73. // where (NewLeft/OldLeft) Is the proportionality.
  74. lpMixVolDest->rightVolume = (mixVolSource.rightVolume*lpMixVolDest->leftVolume)/mixVolSource.leftVolume;
  75. }
  76. // Otherwise we cannot compute the proportionality and reset it to 1/1.
  77. // Note that 1/1 is not necessarily the 'correct' value.
  78. else
  79. {
  80. // To maintain a ratio of 1/1 the right volume must equal the left volume.
  81. lpMixVolDest->rightVolume = lpMixVolDest->leftVolume;
  82. }
  83. }
  84. // Otherwise use the right volume to calculate the proportionality.
  85. else
  86. {
  87. // Set the right volume to the new value.
  88. lpMixVolDest->rightVolume = dwNewVolume;
  89. // If the right volume is non-zero, then continue with the proportionality calculation.
  90. if(mixVolSource.rightVolume)
  91. {
  92. // Calculate proportionality using the equation: NewLeft = OldLeft * (NewRight/OldRight)
  93. // where (NewRight/OldRight) Is the proportionality.
  94. lpMixVolDest->leftVolume = (mixVolSource.leftVolume*lpMixVolDest->rightVolume)/mixVolSource.rightVolume;
  95. }
  96. // Otherwise we cannot compute the proportionality and reset it to 1/1.
  97. // Note that 1/1 is not necessarily the 'correct' value.
  98. else
  99. {
  100. // To maintain a ratio of 1/1 the left volume must equal the right volume.
  101. lpMixVolDest->leftVolume = lpMixVolDest->rightVolume;
  102. }
  103. }
  104. }
  105. //
  106. // Init
  107. //
  108. // Enumerate all existing mixers in the system. For each mixer,
  109. // we enumerate all lines with destination Speaker and WaveIn.
  110. // For each such line, we cache the control id and control value
  111. // of volume control. An invalid flag will be tagged to any control
  112. // not supported by this mixer.
  113. // When an application is finished with all mixers operations,
  114. // it must call ReleaseAllMixers to free all memory resources and
  115. // mixers.
  116. //
  117. // THIS MUST BE THE FIRST API TO CALL TO START MIXER OPERATIONS.
  118. //
  119. // Input: The handle of the window which will handle all callback
  120. // messages MM_MIXM_CONTROL_CHANGE and MM_MIXM_LINE_CHANGE.
  121. //
  122. // Output: TRUE if success; otherwise, FALSE.
  123. //
  124. BOOL CMixerDevice::Init( HWND hWnd, UINT uWaveDevId, DWORD dwFlags)
  125. {
  126. UINT uMixerIdx, uDstIdx, uSrcIdx, uMixerIdCheck;
  127. MMRESULT mmr = MMSYSERR_NOERROR;
  128. MIXERLINE mlDst, mlSrc;
  129. UINT_PTR nMixers, nWaveInDevs, uIndex;
  130. //get the mixer device corresponding to the wave device
  131. #ifndef _WIN64
  132. mmr = mixerGetID((HMIXEROBJ)uWaveDevId, &uMixerIdx, dwFlags);
  133. #else
  134. mmr = MMSYSERR_NODRIVER;
  135. #endif
  136. if ((mmr != MMSYSERR_NOERROR) && (mmr != MMSYSERR_NODRIVER)) {
  137. return FALSE;
  138. }
  139. // a simple fix for cheesy sound cards that don't make a
  140. // direct mapping between waveDevice and mixer device
  141. // e.g. MWAVE cards and newer SB NT 4 drivers
  142. // If there is only ONE mixer device and if no other waveIn device
  143. // uses it, then it is probably valid.
  144. if ((mmr == MMSYSERR_NODRIVER) && (dwFlags == MIXER_OBJECTF_WAVEIN))
  145. {
  146. nMixers = mixerGetNumDevs();
  147. nWaveInDevs = waveInGetNumDevs();
  148. if (nMixers == 1)
  149. {
  150. uMixerIdx = 0;
  151. for (uIndex = 0; uIndex < nWaveInDevs; uIndex++)
  152. {
  153. mmr = mixerGetID((HMIXEROBJ)uIndex, &uMixerIdCheck, dwFlags);
  154. if ((mmr == MMSYSERR_NOERROR) && (uMixerIdCheck == uMixerIdx))
  155. {
  156. return FALSE; // the mixer belongs to another waveIn Device
  157. }
  158. }
  159. }
  160. else
  161. {
  162. return FALSE;
  163. }
  164. }
  165. // open the mixer such that we can get notification messages
  166. mmr = mixerOpen (
  167. &m_hMixer,
  168. uMixerIdx,
  169. (DWORD_PTR) hWnd,
  170. 0,
  171. (hWnd ? CALLBACK_WINDOW : 0) | MIXER_OBJECTF_MIXER);
  172. if (mmr != MMSYSERR_NOERROR) {
  173. return FALSE;
  174. }
  175. // get mixer caps
  176. mmr = mixerGetDevCaps (uMixerIdx, &(m_mixerCaps), sizeof (MIXERCAPS));
  177. if ((mmr != MMSYSERR_NOERROR) || (0 == m_mixerCaps.cDestinations)) {
  178. mixerClose(m_hMixer);
  179. return FALSE;
  180. }
  181. for (uDstIdx = 0; uDstIdx < m_mixerCaps.cDestinations; uDstIdx++)
  182. {
  183. ZeroMemory (&mlDst, sizeof (mlDst));
  184. mlDst.cbStruct = sizeof (mlDst);
  185. mlDst.dwDestination = uDstIdx;
  186. // get the mixer line for this destination
  187. mmr = mixerGetLineInfo ((HMIXEROBJ)m_hMixer, &mlDst,
  188. MIXER_GETLINEINFOF_DESTINATION | MIXER_OBJECTF_HMIXER);
  189. if (mmr != MMSYSERR_NOERROR) continue;
  190. // examine the type of this destination line
  191. if (((MIXER_OBJECTF_WAVEOUT == dwFlags) &&
  192. (MIXERLINE_COMPONENTTYPE_DST_SPEAKERS == mlDst.dwComponentType)) ||
  193. ((MIXER_OBJECTF_WAVEIN == dwFlags) &&
  194. (MIXERLINE_COMPONENTTYPE_DST_WAVEIN == mlDst.dwComponentType)))
  195. {
  196. // fill in more info about DstLine
  197. m_DstLine.ucChannels = mlDst.cChannels;
  198. if (!(mlDst.fdwLine & MIXERLINE_LINEF_DISCONNECTED))
  199. {
  200. // get id and value of volume control
  201. mmr = mixerGetControlId (
  202. m_hMixer,
  203. &m_DstLine.dwControlId,
  204. mlDst.dwLineID,
  205. MIXERCONTROL_CONTROLTYPE_VOLUME);
  206. m_DstLine.fIdValid = (mmr == MMSYSERR_NOERROR);
  207. m_DstLine.dwLineId = mlDst.dwLineID;
  208. m_DstLine.dwCompType = mlDst.dwComponentType;
  209. m_DstLine.dwConnections = mlDst.cConnections;
  210. // -----------------------------------------------------
  211. // enumerate all sources for this destination
  212. for (uSrcIdx = 0; uSrcIdx < mlDst.cConnections; uSrcIdx++)
  213. {
  214. // get the info of the line with specific src and dst...
  215. ZeroMemory (&mlSrc, sizeof (mlSrc));
  216. mlSrc.cbStruct = sizeof (mlSrc);
  217. mlSrc.dwDestination = uDstIdx;
  218. mlSrc.dwSource = uSrcIdx;
  219. mmr = mixerGetLineInfo (
  220. (HMIXEROBJ)m_hMixer,
  221. &mlSrc,
  222. MIXER_GETLINEINFOF_SOURCE | MIXER_OBJECTF_HMIXER);
  223. if (mmr == MMSYSERR_NOERROR)
  224. {
  225. if (((MIXERLINE_COMPONENTTYPE_DST_SPEAKERS == mlDst.dwComponentType) &&
  226. (MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT == mlSrc.dwComponentType)) ||
  227. ((MIXERLINE_COMPONENTTYPE_DST_WAVEIN == mlDst.dwComponentType) &&
  228. (MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE == mlSrc.dwComponentType)))
  229. {
  230. // fill in more info about this source
  231. m_SrcLine.ucChannels = mlSrc.cChannels;
  232. // get id and value of volume control
  233. mmr = mixerGetControlId (
  234. m_hMixer,
  235. &m_SrcLine.dwControlId,
  236. mlSrc.dwLineID,
  237. MIXERCONTROL_CONTROLTYPE_VOLUME);
  238. m_SrcLine.fIdValid = (mmr == MMSYSERR_NOERROR);
  239. m_SrcLine.dwLineId = mlSrc.dwLineID;
  240. m_SrcLine.dwCompType = mlSrc.dwComponentType;
  241. m_SrcLine.dwConnections = mlSrc.cConnections;
  242. m_SrcLine.dwControls = mlSrc.cControls;
  243. DetectAGC();
  244. break;
  245. }
  246. }
  247. }
  248. }
  249. break;
  250. }
  251. }
  252. return TRUE;
  253. }
  254. CMixerDevice* CMixerDevice::GetMixerForWaveDevice( HWND hWnd, UINT uWaveDevId, DWORD dwFlags)
  255. {
  256. CMixerDevice* pMixerDev = new CMixerDevice;
  257. if (NULL != pMixerDev)
  258. {
  259. if (!pMixerDev->Init(hWnd, uWaveDevId, dwFlags))
  260. {
  261. delete pMixerDev;
  262. pMixerDev = NULL;
  263. }
  264. }
  265. return pMixerDev;
  266. }
  267. // general method for setting the volume
  268. // for recording, this will try to set both the master and microphone volume controls
  269. // for playback, it will only set the WaveOut playback line (master line stays put)
  270. BOOL CMixerDevice::SetVolume(MIXVOLUME * pdwVolume)
  271. {
  272. BOOL fSetMain=FALSE, fSetSub;
  273. BOOL fMicrophone;
  274. // is this the microphone channel ?
  275. fMicrophone = ((m_DstLine.dwCompType == MIXERLINE_COMPONENTTYPE_DST_WAVEIN) ||
  276. (m_SrcLine.dwCompType == MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE));
  277. fSetSub = SetSubVolume(pdwVolume);
  278. if ((fMicrophone) || (!fSetSub))
  279. {
  280. fSetMain = SetMainVolume(pdwVolume);
  281. }
  282. return (fSetSub || fSetMain);
  283. }
  284. BOOL CMixerDevice::SetMainVolume(MIXVOLUME * pdwVolume)
  285. {
  286. MMRESULT mmr = MMSYSERR_ERROR;
  287. if (m_DstLine.fIdValid) {
  288. mmr = mixerSetControlValue (
  289. m_hMixer,
  290. pdwVolume,
  291. m_DstLine.dwControlId,
  292. 2);
  293. }
  294. return (mmr == MMSYSERR_NOERROR);
  295. }
  296. BOOL CMixerDevice::SetSubVolume(MIXVOLUME * pdwVolume)
  297. {
  298. MMRESULT mmr = MMSYSERR_ERROR;
  299. if (m_SrcLine.fIdValid)
  300. {
  301. mmr = mixerSetControlValue (
  302. m_hMixer,
  303. pdwVolume,
  304. m_SrcLine.dwControlId,
  305. m_SrcLine.ucChannels);
  306. }
  307. return (mmr == MMSYSERR_NOERROR);
  308. }
  309. //
  310. // Gets the volume (0 - 65535) of the master volume
  311. // returns TRUE if succesful,
  312. // returns FALSE if it fails or if this control is not available
  313. //
  314. BOOL CMixerDevice::GetMainVolume(MIXVOLUME * pdwVolume)
  315. {
  316. BOOL fRet = FALSE;
  317. if (m_DstLine.fIdValid)
  318. {
  319. MMRESULT mmr = ::mixerGetControlValue(
  320. m_hMixer,
  321. pdwVolume,
  322. m_DstLine.dwControlId,
  323. 2);
  324. fRet = (mmr == MMSYSERR_NOERROR);
  325. }
  326. return fRet;
  327. }
  328. //
  329. // Gets the volume (0 - 65535) of the sub volume
  330. // returns TRUE if succesful,
  331. // returns FALSE if it fails or if this control is not available
  332. //
  333. BOOL CMixerDevice::GetSubVolume(MIXVOLUME * pdwVolume)
  334. {
  335. BOOL fRet = FALSE;
  336. if (m_SrcLine.fIdValid)
  337. {
  338. MMRESULT mmr = ::mixerGetControlValue(
  339. m_hMixer,
  340. pdwVolume,
  341. m_SrcLine.dwControlId,
  342. m_SrcLine.ucChannels);
  343. fRet = (mmr == MMSYSERR_NOERROR);
  344. }
  345. return fRet;
  346. }
  347. BOOL CMixerDevice::GetVolume(MIXVOLUME * pdwVol)
  348. {
  349. MIXVOLUME dwSub={0,0}, dwMain={0,0};
  350. BOOL fSubAvail, fMainAvail, fMicrophone;
  351. // is this the microphone channel ?
  352. fMicrophone = ((m_DstLine.dwCompType == MIXERLINE_COMPONENTTYPE_DST_WAVEIN) ||
  353. (m_SrcLine.dwCompType == MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE));
  354. fSubAvail = GetSubVolume(&dwSub);
  355. fMainAvail = GetMainVolume(&dwMain);
  356. if ((!fSubAvail) && (!fMainAvail))
  357. {
  358. pdwVol->leftVolume = 0;
  359. pdwVol->rightVolume = 0;
  360. return FALSE;
  361. }
  362. // don't return an average volume in the case of the speaker mixer
  363. if (fSubAvail && fMainAvail && fMicrophone)
  364. {
  365. pdwVol->leftVolume = dwSub.leftVolume;
  366. pdwVol->rightVolume = dwSub.rightVolume;
  367. }
  368. else if (fSubAvail)
  369. {
  370. pdwVol->leftVolume = dwSub.leftVolume;
  371. pdwVol->rightVolume = dwSub.rightVolume;
  372. }
  373. else
  374. {
  375. pdwVol->leftVolume = dwMain.leftVolume;
  376. pdwVol->rightVolume = dwMain.rightVolume;
  377. }
  378. return TRUE;
  379. }
  380. // Return the value of the Auto Gain Control
  381. // Returns FALSE if the control is not supported.
  382. // pfOn is OUTPUT, OPTIONAL - value of AGC
  383. BOOL CMixerDevice::GetAGC(BOOL *pfOn)
  384. {
  385. MIXVOLUME dwValue;
  386. MMRESULT mmr;
  387. if ((m_SrcLine.fIdValid==FALSE) || (m_SrcLine.fAgcAvailable==FALSE))
  388. {
  389. return FALSE;
  390. }
  391. mmr = mixerGetControlValue(m_hMixer, &dwValue, m_SrcLine.dwAGCID, 1);
  392. if (mmr != MMSYSERR_NOERROR)
  393. {
  394. return FALSE;
  395. }
  396. if (pfOn)
  397. {
  398. *pfOn = dwValue.leftVolume || dwValue.rightVolume;
  399. }
  400. return TRUE;
  401. }
  402. /*
  403. Hack API to turn MIC Auto Gain Control on or off.
  404. Its a hack because it only works on SB16/AWE32 cards.
  405. */
  406. BOOL CMixerDevice::SetAGC(BOOL fOn)
  407. {
  408. MIXVOLUME dwValue;
  409. MMRESULT mmr;
  410. if ((m_SrcLine.fIdValid==FALSE) || (m_SrcLine.fAgcAvailable==FALSE))
  411. {
  412. return FALSE;
  413. }
  414. mmr = mixerGetControlValue(m_hMixer, &dwValue, m_SrcLine.dwAGCID, 1);
  415. if (mmr != MMSYSERR_NOERROR)
  416. {
  417. return FALSE;
  418. }
  419. if (dwValue.leftVolume == (DWORD)fOn && dwValue.rightVolume == (DWORD)fOn)
  420. {
  421. return TRUE;
  422. }
  423. dwValue.leftVolume = fOn;
  424. dwValue.rightVolume = fOn;
  425. mmr = mixerSetControlValue(m_hMixer, &dwValue, m_SrcLine.dwAGCID, 1);
  426. return (mmr == MMSYSERR_NOERROR);
  427. }
  428. // this method tries to determine if an AGC control is available by
  429. // looking for a microphone sub-control that has a corresponding text
  430. // string with the words "gain", "boost", or "agc" in in.
  431. // If it can't auto-detect it through string compares, it falls
  432. // back to a table lookup scheme. (Auto-detect will probably not work
  433. // in localized versions of the driver. But hardly anyone localizes
  434. // their drivers.)
  435. BOOL CMixerDevice::DetectAGC()
  436. {
  437. MIXERLINECONTROLS mlc;
  438. MIXERCONTROL *aMC=NULL;
  439. MIXERCONTROL *pMC;
  440. MMRESULT mmrQuery;
  441. DWORD dwIndex;
  442. if ((m_SrcLine.fIdValid == FALSE) || (m_SrcLine.dwCompType != MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE))
  443. {
  444. return FALSE;
  445. }
  446. // make sure that dwControls isn't set to something outrageous
  447. if ((m_SrcLine.dwControls > 0) && (m_SrcLine.dwControls < 30))
  448. {
  449. aMC = new MIXERCONTROL[m_SrcLine.dwControls];
  450. mlc.cbStruct = sizeof(MIXERLINECONTROLS);
  451. mlc.dwLineID = m_SrcLine.dwLineId;
  452. mlc.dwControlType = 0; // not set ???
  453. mlc.cControls = m_SrcLine.dwControls;
  454. mlc.cbmxctrl = sizeof(MIXERCONTROL); // set to sizeof 1 MC structure
  455. mlc.pamxctrl = aMC;
  456. mmrQuery = mixerGetLineControls((HMIXEROBJ)m_hMixer, &mlc, MIXER_OBJECTF_HMIXER|MIXER_GETLINECONTROLSF_ALL );
  457. if (mmrQuery == MMSYSERR_NOERROR)
  458. {
  459. for (dwIndex = 0; dwIndex < mlc.cControls; dwIndex++)
  460. {
  461. pMC = &aMC[dwIndex];
  462. // make sure we don't get a fader, mute, or some other control
  463. if ( (!(pMC->dwControlType & MIXERCONTROL_CT_CLASS_SWITCH)) ||
  464. (pMC->dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE) )
  465. {
  466. continue;
  467. }
  468. CharLower(pMC->szName); // CharLower is a Win32 string function
  469. CharLower(pMC->szShortName);
  470. if ( _StrStr(pMC->szName, szGain) || _StrStr(pMC->szName, szBoost) || _StrStr(pMC->szName, szAGC) ||
  471. _StrStr(pMC->szShortName, szGain) || _StrStr(pMC->szShortName, szBoost) || _StrStr(pMC->szShortName, szAGC) )
  472. {
  473. m_SrcLine.fAgcAvailable = TRUE;
  474. m_SrcLine.dwAGCID = pMC->dwControlID;
  475. TRACE_OUT(("CMixerDevice::DetectAGC - Autodetected control ID %d as AGC\r\n", m_SrcLine.dwAGCID));
  476. break;
  477. }
  478. }
  479. }
  480. }
  481. // if we didn't find the mixer dynamically, consult our old list
  482. if (!m_SrcLine.fAgcAvailable)
  483. {
  484. m_SrcLine.fAgcAvailable = GetAGCID(m_mixerCaps.wMid, m_mixerCaps.wPid, &(m_SrcLine.dwAGCID));
  485. }
  486. if (aMC)
  487. delete [] aMC;
  488. return m_SrcLine.fAgcAvailable;
  489. }
  490. BOOL CMixerDevice::EnableMicrophone()
  491. {
  492. MIXERLINE mixerLine;
  493. MIXERCONTROL mixerControl;
  494. MIXERCONTROLDETAILS mixerControlDetails, mixerControlDetailsOrig;
  495. UINT uIndex, numItems, numMics, numMicsSet, fMicFound;
  496. UINT uMicIndex = 0;
  497. UINT aMicIndices[MAX_MICROPHONE_DEVS];
  498. MIXERCONTROLDETAILS_LISTTEXT *aListText = NULL;
  499. MIXERCONTROLDETAILS_BOOLEAN *aEnableList = NULL;
  500. MMRESULT mmr;
  501. BOOL fLoopback=FALSE;
  502. UINT uLoopbackIndex=0;
  503. // check to see if component type is valid (which means the line exists!)
  504. // even if the volume control doesn't exist or isn't slidable,
  505. // there may still be a select switch
  506. if ((m_SrcLine.dwCompType != MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE) ||
  507. (m_DstLine.dwCompType != MIXERLINE_COMPONENTTYPE_DST_WAVEIN))
  508. {
  509. return FALSE;
  510. }
  511. // try to find the mixer list
  512. if ( (MMSYSERR_NOERROR != mixerGetControlByType(m_hMixer, m_DstLine.dwLineId, MIXERCONTROL_CT_CLASS_LIST, &mixerControl))
  513. && (MMSYSERR_NOERROR != mixerGetControlByType(m_hMixer, m_DstLine.dwLineId, MIXERCONTROL_CONTROLTYPE_MIXER, &mixerControl))
  514. && (MMSYSERR_NOERROR != mixerGetControlByType(m_hMixer, m_DstLine.dwLineId, MIXERCONTROL_CONTROLTYPE_MULTIPLESELECT, &mixerControl))
  515. && (MMSYSERR_NOERROR != mixerGetControlByType(m_hMixer, m_DstLine.dwLineId, MIXERCONTROL_CONTROLTYPE_MUX, &mixerControl))
  516. && (MMSYSERR_NOERROR != mixerGetControlByType(m_hMixer, m_DstLine.dwLineId, MIXERCONTROL_CONTROLTYPE_SINGLESELECT, &mixerControl))
  517. )
  518. {
  519. TRACE_OUT(("CMixerDevice::EnableMicrophone-Unable to find mixer list!"));
  520. // if no mixer list, see if there is a "mute" control...
  521. return UnMuteVolume();
  522. // note: even though we don't have a mixer mux/select control
  523. // we could still find a way to mute the loopback line
  524. // unfortunately, we don't have the code that finds the line id
  525. // of the loopback control
  526. }
  527. ZeroMemory(&mixerControlDetails, sizeof(MIXERCONTROLDETAILS));
  528. mixerControlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS);
  529. mixerControlDetails.dwControlID = mixerControl.dwControlID;
  530. if (MIXERCONTROL_CONTROLF_UNIFORM & mixerControl.fdwControl)
  531. mixerControlDetails.cChannels = 1;
  532. else
  533. mixerControlDetails.cChannels = m_DstLine.ucChannels;
  534. if (MIXERCONTROL_CONTROLF_MULTIPLE & mixerControl.fdwControl)
  535. mixerControlDetails.cMultipleItems = (UINT)mixerControl.cMultipleItems;
  536. else
  537. mixerControlDetails.cMultipleItems = 1;
  538. // weirdness - you have to set cbDetails to the size of a single LISTTEXT item
  539. // setting it to anything larger will make the call fail
  540. mixerControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXT);
  541. numItems = mixerControlDetails.cMultipleItems;
  542. if (m_DstLine.dwConnections > numItems)
  543. numItems = m_DstLine.dwConnections;
  544. aListText = new MIXERCONTROLDETAILS_LISTTEXT[numItems];
  545. aEnableList = new MIXERCONTROLDETAILS_BOOLEAN[numItems];
  546. if ((aListText == NULL) || (aEnableList == NULL))
  547. {
  548. WARNING_OUT(("CMixerDevice::EnableMicrophone-Out of memory"));
  549. return FALSE;
  550. }
  551. ZeroMemory(aListText, sizeof(MIXERCONTROLDETAILS_LISTTEXT)*numItems);
  552. ZeroMemory(aEnableList, sizeof(MIXERCONTROLDETAILS_BOOLEAN)*numItems);
  553. mixerControlDetails.paDetails = aListText;
  554. // preserve the settings, some values will change after this call
  555. mixerControlDetailsOrig = mixerControlDetails;
  556. // query for the text of the list
  557. mmr = mixerGetControlDetails((HMIXEROBJ)m_hMixer, &mixerControlDetails,
  558. MIXER_GETCONTROLDETAILSF_LISTTEXT
  559. |MIXER_OBJECTF_HMIXER);
  560. // some sound cards don't specify CONTROLF_MULTIPLE
  561. // try doing what sndvol32 does for MUX controls
  562. if (mmr != MMSYSERR_NOERROR)
  563. {
  564. mixerControlDetails = mixerControlDetailsOrig;
  565. mixerControlDetails.cChannels = 1;
  566. mixerControlDetails.cMultipleItems = m_DstLine.dwConnections;
  567. mixerControlDetailsOrig = mixerControlDetails;
  568. mmr = mixerGetControlDetails((HMIXEROBJ)m_hMixer, &mixerControlDetails,
  569. MIXER_GETCONTROLDETAILSF_LISTTEXT
  570. |MIXER_OBJECTF_HMIXER);
  571. }
  572. if (mmr != MMSYSERR_NOERROR)
  573. {
  574. delete [] aListText;
  575. delete [] aEnableList;
  576. return FALSE;
  577. }
  578. // enumerate for the microphone
  579. numMics = 0;
  580. fMicFound = FALSE;
  581. for (uIndex = 0; uIndex < mixerControlDetails.cMultipleItems; uIndex++)
  582. {
  583. // dwParam1 of the listText structure is the LineID of the source
  584. // dwParam2 should be the component type, but unfoturnately not
  585. // all sound cards obey this rule.
  586. ZeroMemory (&mixerLine, sizeof(MIXERLINE));
  587. mixerLine.cbStruct = sizeof(MIXERLINE);
  588. mixerLine.dwLineID = aListText[uIndex].dwParam1;
  589. mmr = mixerGetLineInfo ((HMIXEROBJ)m_hMixer, &mixerLine,
  590. MIXER_GETLINEINFOF_LINEID | MIXER_OBJECTF_HMIXER);
  591. if ((mmr == MMSYSERR_NOERROR) &&
  592. (mixerLine.dwComponentType == m_SrcLine.dwCompType) &&
  593. (numMics < MAX_MICROPHONE_DEVS))
  594. {
  595. aMicIndices[numMics] = uIndex;
  596. numMics++;
  597. }
  598. if (aListText[uIndex].dwParam1 == m_SrcLine.dwLineId)
  599. {
  600. uMicIndex = uIndex;
  601. fMicFound = TRUE; // can't rely on uIndex or uNumMics not zero
  602. }
  603. if ((mmr == MMSYSERR_NOERROR) &&
  604. (mixerLine.dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT))
  605. {
  606. fLoopback = TRUE;
  607. uLoopbackIndex = uIndex;
  608. }
  609. }
  610. if (fMicFound == FALSE)
  611. {
  612. delete [] aListText;
  613. delete [] aEnableList;
  614. return FALSE;
  615. }
  616. // now we know which position in the array to set, let's do it.
  617. mixerControlDetails = mixerControlDetailsOrig;
  618. mixerControlDetails.paDetails = aEnableList;
  619. mixerControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
  620. // find out what's already marked as set.
  621. mmr = mixerGetControlDetails((HMIXEROBJ)m_hMixer, &mixerControlDetails,
  622. MIXER_SETCONTROLDETAILSF_VALUE|MIXER_OBJECTF_HMIXER);
  623. if ( (mmr == MMSYSERR_NOERROR) &&
  624. ( (aEnableList[uMicIndex].fValue != 1) || (fLoopback && aEnableList[uLoopbackIndex].fValue == 1) )
  625. )
  626. {
  627. // how many microphone's are already enabled ?
  628. // if another microphone is already enabled and if the device is MUX type
  629. // we won't attempt to turn one on.
  630. numMicsSet = 0;
  631. for (uIndex = 0; uIndex < numMics; uIndex++)
  632. {
  633. if ((aEnableList[aMicIndices[uIndex]].fValue == 1) &&
  634. (uIndex != uMicIndex))
  635. {
  636. numMicsSet++;
  637. }
  638. }
  639. if ( (mixerControl.dwControlType == MIXERCONTROL_CONTROLTYPE_MUX)
  640. ||(mixerControl.dwControlType == MIXERCONTROL_CONTROLTYPE_SINGLESELECT))
  641. {
  642. ZeroMemory(aEnableList, sizeof(aEnableList)*numItems);
  643. aEnableList[uMicIndex].fValue = 1;
  644. if (numMicsSet == 0)
  645. {
  646. mmr = mixerSetControlDetails((HMIXEROBJ)m_hMixer, &mixerControlDetails,
  647. MIXER_SETCONTROLDETAILSF_VALUE|MIXER_OBJECTF_HMIXER);
  648. }
  649. else
  650. {
  651. mmr = MMSYSERR_ERROR; // a mike has already been enabled
  652. }
  653. }
  654. else
  655. {
  656. mixerControlDetails = mixerControlDetailsOrig;
  657. mixerControlDetails.paDetails = aEnableList;
  658. mixerControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
  659. // enable the microphone
  660. aEnableList[uMicIndex].fValue = 1;
  661. mmr = mixerSetControlDetails((HMIXEROBJ)m_hMixer, &mixerControlDetails,
  662. MIXER_GETCONTROLDETAILSF_VALUE|MIXER_OBJECTF_HMIXER);
  663. // disable the loopback input
  664. if (fLoopback)
  665. {
  666. aEnableList[uLoopbackIndex].fValue = 0;
  667. mixerSetControlDetails((HMIXEROBJ)m_hMixer, &mixerControlDetails,
  668. MIXER_GETCONTROLDETAILSF_VALUE|MIXER_OBJECTF_HMIXER);
  669. }
  670. }
  671. }
  672. delete []aEnableList;
  673. delete []aListText;
  674. return (mmr == MMSYSERR_NOERROR);
  675. }
  676. BOOL CMixerDevice::UnMuteVolume()
  677. {
  678. MIXERCONTROL mixerControl;
  679. MIXERCONTROLDETAILS mixerControlDetails;
  680. MIXERCONTROLDETAILS_BOOLEAN mcdb;
  681. MMRESULT mmrMaster=MMSYSERR_ERROR, mmrSub=MMSYSERR_ERROR;
  682. // try to unmute the master volume (playback only)
  683. if (m_DstLine.dwCompType == MIXERLINE_COMPONENTTYPE_DST_SPEAKERS)
  684. {
  685. mmrMaster = mixerGetControlByType(m_hMixer,
  686. m_DstLine.dwLineId, MIXERCONTROL_CONTROLTYPE_MUTE,
  687. &mixerControl);
  688. if (mmrMaster == MMSYSERR_NOERROR)
  689. {
  690. ZeroMemory(&mixerControlDetails, sizeof(MIXERCONTROLDETAILS));
  691. mixerControlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS);
  692. mixerControlDetails.dwControlID = mixerControl.dwControlID;
  693. mixerControlDetails.cChannels = 1;
  694. mixerControlDetails.cMultipleItems = 0;
  695. mcdb.fValue = 0;
  696. mixerControlDetails.paDetails = &mcdb;
  697. mixerControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
  698. mmrMaster = mixerSetControlDetails((HMIXEROBJ)m_hMixer,
  699. &mixerControlDetails,
  700. MIXER_SETCONTROLDETAILSF_VALUE|MIXER_OBJECTF_HMIXER);
  701. }
  702. }
  703. // Unmute the source line
  704. // this will unmute WaveOut, and possibly the microphone
  705. // (Use EnableMicrophone() above to handle all the potential microphone cases)
  706. if ((m_SrcLine.dwCompType == MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE) ||
  707. (m_SrcLine.dwCompType == MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT))
  708. {
  709. mmrSub = mixerGetControlByType(m_hMixer,
  710. m_SrcLine.dwLineId, MIXERCONTROL_CONTROLTYPE_MUTE,
  711. &mixerControl);
  712. if (mmrSub == MMSYSERR_NOERROR)
  713. {
  714. ZeroMemory(&mixerControlDetails, sizeof(MIXERCONTROLDETAILS));
  715. mixerControlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS);
  716. mixerControlDetails.dwControlID = mixerControl.dwControlID;
  717. mixerControlDetails.cChannels = 1;
  718. mixerControlDetails.cMultipleItems = 0;
  719. mcdb.fValue = 0;
  720. mixerControlDetails.paDetails = &mcdb;
  721. mixerControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
  722. mmrSub = mixerSetControlDetails((HMIXEROBJ)m_hMixer,
  723. &mixerControlDetails,
  724. MIXER_SETCONTROLDETAILSF_VALUE|MIXER_OBJECTF_HMIXER);
  725. }
  726. }
  727. return ((mmrSub == MMSYSERR_NOERROR) || (mmrMaster == MMSYSERR_NOERROR));
  728. }
  729. //////////////////////////////////////////////////
  730. //
  731. // The following are private APIs
  732. //
  733. static MMRESULT mixerGetControlValue ( HMIXER hMixer, MIXVOLUME *pdwValue,
  734. DWORD dwControlId, UINT ucChannels )
  735. {
  736. MIXERCONTROLDETAILS mxcd;
  737. MMRESULT mmr;
  738. ZeroMemory (&mxcd, sizeof (mxcd));
  739. mxcd.cbStruct = sizeof (mxcd);
  740. mxcd.dwControlID = dwControlId;
  741. mxcd.cChannels = ucChannels;
  742. mxcd.cbDetails = sizeof (DWORD);
  743. mxcd.paDetails = (PVOID) pdwValue;
  744. mmr = mixerGetControlDetails ((HMIXEROBJ) hMixer, &mxcd,
  745. MIXER_GETCONTROLDETAILSF_VALUE | MIXER_OBJECTF_HMIXER);
  746. return mmr;
  747. }
  748. static MMRESULT mixerSetControlValue ( HMIXER hMixer, MIXVOLUME * pdwValue,
  749. DWORD dwControlId, UINT ucChannels )
  750. {
  751. MIXERCONTROLDETAILS mxcd;
  752. MMRESULT mmr;
  753. ZeroMemory (&mxcd, sizeof (mxcd));
  754. mxcd.cbStruct = sizeof (mxcd);
  755. mxcd.dwControlID = dwControlId;
  756. mxcd.cChannels = ucChannels;
  757. mxcd.cbDetails = sizeof (DWORD);
  758. mxcd.paDetails = (PVOID) pdwValue;
  759. mmr = mixerSetControlDetails ((HMIXEROBJ) hMixer, &mxcd,
  760. MIXER_SETCONTROLDETAILSF_VALUE | MIXER_OBJECTF_HMIXER);
  761. return mmr;
  762. }
  763. static MMRESULT mixerGetControlId ( HMIXER hMixer, DWORD *pdwControlId,
  764. DWORD dwLineId, DWORD dwControlType )
  765. {
  766. MIXERLINECONTROLS mxlc;
  767. MIXERCONTROL mxc;
  768. MMRESULT mmr;
  769. ZeroMemory (&mxlc, sizeof (mxlc));
  770. ZeroMemory (&mxc, sizeof (mxc));
  771. mxlc.cbStruct = sizeof (mxlc);
  772. mxlc.dwLineID = dwLineId;
  773. mxlc.dwControlType = dwControlType;
  774. mxlc.cControls = 1;
  775. mxlc.cbmxctrl = sizeof (mxc);
  776. mxlc.pamxctrl = &mxc;
  777. mmr = mixerGetLineControls ((HMIXEROBJ) hMixer, &mxlc,
  778. MIXER_GETLINECONTROLSF_ONEBYTYPE | MIXER_OBJECTF_HMIXER);
  779. *pdwControlId = mxc.dwControlID;
  780. return mmr;
  781. }
  782. // similar to above, except returns the whole control
  783. static MMRESULT mixerGetControlByType ( HMIXER hMixer, DWORD dwLineId, DWORD dwControlType, MIXERCONTROL *pMixerControl)
  784. {
  785. MIXERLINECONTROLS mxlc;
  786. MMRESULT mmr;
  787. ZeroMemory (&mxlc, sizeof (mxlc));
  788. ZeroMemory (pMixerControl, sizeof (MIXERCONTROL));
  789. mxlc.cbStruct = sizeof (mxlc);
  790. mxlc.dwLineID = dwLineId;
  791. mxlc.dwControlType = dwControlType;
  792. mxlc.cControls = 1;
  793. mxlc.cbmxctrl = sizeof (MIXERCONTROL);
  794. mxlc.pamxctrl = pMixerControl;
  795. mmr = mixerGetLineControls ((HMIXEROBJ) hMixer, &mxlc,
  796. MIXER_GETLINECONTROLSF_ONEBYTYPE | MIXER_OBJECTF_HMIXER);
  797. return mmr;
  798. }