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.

927 lines
29 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. // File: multchan.c
  4. //
  5. // This file defines the functions that drive the multichannel
  6. // volume tab of the Sounds & Multimedia control panel.
  7. //
  8. // History:
  9. // 13 March 2000 RogerW
  10. // Created.
  11. //
  12. // Copyright (C) 2000 Microsoft Corporation All Rights Reserved.
  13. //
  14. // Microsoft Confidential
  15. //
  16. ///////////////////////////////////////////////////////////////////////////////
  17. //=============================================================================
  18. // Include files
  19. //=============================================================================
  20. #include <windows.h>
  21. #include <windowsx.h>
  22. #include <tchar.h>
  23. #include <regstr.h>
  24. #include <dbt.h>
  25. #include "medhelp.h"
  26. #include "mmcpl.h"
  27. #include "multchan.h"
  28. #include "speakers.h"
  29. #include "dslevel.h"
  30. // Externals
  31. extern BOOL DeviceChange_GetHandle(DWORD dwMixerID, HANDLE *phDevice);
  32. extern HRESULT DSGetGuidFromName(LPTSTR szName, BOOL fRecord, LPGUID pGuid);
  33. extern HRESULT DSGetCplValues(GUID guid, BOOL fRecord, LPCPLDATA pData);
  34. // Globals
  35. UINT g_uiMCMixID = 0;
  36. HMIXER g_hMCMixer = NULL;
  37. UINT g_uiMCPageStringID = 0;
  38. UINT g_uiMCDescStringID = 0;
  39. LPVOID g_paPrevious = NULL;
  40. BOOL g_fInternalMCGenerated = FALSE;
  41. BOOL g_fMCChanged = FALSE;
  42. MIXERCONTROLDETAILS g_mcdMC;
  43. MIXERLINE g_mlMCDst;
  44. WNDPROC g_fnMCPSProc = NULL;
  45. UINT g_uiMCDevChange = 0;
  46. HWND g_hWndMC = NULL;
  47. static HDEVNOTIFY g_hMCDeviceEventContext= NULL;
  48. // Constants
  49. #define VOLUME_TICS (500) // VOLUME_TICS * VOLUME_MAX must be less than 0xFFFFFFFF
  50. #define VOLUME_MAX (0xFFFF)
  51. #define VOLUME_MIN (0)
  52. #define MC_SLIDER_COUNT (8) // Update Code & Dialog Template if change this value!
  53. static INTCODE aKeyWordIds[] =
  54. {
  55. IDC_MC_DESCRIPTION, NO_HELP,
  56. IDC_MC_ZERO_LOW, IDH_MC_ALL_SLIDERS,
  57. IDC_MC_ZERO, IDH_MC_ALL_SLIDERS,
  58. IDC_MC_ZERO_VOLUME, IDH_MC_ALL_SLIDERS,
  59. IDC_MC_ZERO_HIGH, IDH_MC_ALL_SLIDERS,
  60. IDC_MC_ONE_LOW, IDH_MC_ALL_SLIDERS,
  61. IDC_MC_ONE, IDH_MC_ALL_SLIDERS,
  62. IDC_MC_ONE_VOLUME, IDH_MC_ALL_SLIDERS,
  63. IDC_MC_ONE_HIGH, IDH_MC_ALL_SLIDERS,
  64. IDC_MC_TWO_LOW, IDH_MC_ALL_SLIDERS,
  65. IDC_MC_TWO, IDH_MC_ALL_SLIDERS,
  66. IDC_MC_TWO_VOLUME, IDH_MC_ALL_SLIDERS,
  67. IDC_MC_TWO_HIGH, IDH_MC_ALL_SLIDERS,
  68. IDC_MC_THREE_LOW, IDH_MC_ALL_SLIDERS,
  69. IDC_MC_THREE, IDH_MC_ALL_SLIDERS,
  70. IDC_MC_THREE_VOLUME, IDH_MC_ALL_SLIDERS,
  71. IDC_MC_THREE_HIGH, IDH_MC_ALL_SLIDERS,
  72. IDC_MC_FOUR_LOW, IDH_MC_ALL_SLIDERS,
  73. IDC_MC_FOUR, IDH_MC_ALL_SLIDERS,
  74. IDC_MC_FOUR_VOLUME, IDH_MC_ALL_SLIDERS,
  75. IDC_MC_FOUR_HIGH, IDH_MC_ALL_SLIDERS,
  76. IDC_MC_FIVE_LOW, IDH_MC_ALL_SLIDERS,
  77. IDC_MC_FIVE, IDH_MC_ALL_SLIDERS,
  78. IDC_MC_FIVE_VOLUME, IDH_MC_ALL_SLIDERS,
  79. IDC_MC_FIVE_HIGH, IDH_MC_ALL_SLIDERS,
  80. IDC_MC_SIX_LOW, IDH_MC_ALL_SLIDERS,
  81. IDC_MC_SIX, IDH_MC_ALL_SLIDERS,
  82. IDC_MC_SIX_VOLUME, IDH_MC_ALL_SLIDERS,
  83. IDC_MC_SIX_HIGH, IDH_MC_ALL_SLIDERS,
  84. IDC_MC_SEVEN_LOW, IDH_MC_ALL_SLIDERS,
  85. IDC_MC_SEVEN, IDH_MC_ALL_SLIDERS,
  86. IDC_MC_SEVEN_VOLUME, IDH_MC_ALL_SLIDERS,
  87. IDC_MC_SEVEN_HIGH, IDH_MC_ALL_SLIDERS,
  88. IDC_MC_MOVE_TOGETHER, IDH_MC_MOVE_TOGETHER,
  89. IDC_MC_RESTORE, IDH_MC_RESTORE,
  90. 0,0
  91. };
  92. ///////////////////////////////////////////////////////////////////////////////
  93. //
  94. // %%Function: MCTabProc
  95. //
  96. // Parameters: hDlg = window handle of dialog window.
  97. // uiMessage = message ID.
  98. // wParam = message-dependent.
  99. // lParam = message-dependent.
  100. //
  101. // Returns: TRUE if message has been processed, else FALSE
  102. //
  103. // Description: Dialog proc for multichannel control panel page device change
  104. // message.
  105. //
  106. //
  107. ///////////////////////////////////////////////////////////////////////////////
  108. LRESULT CALLBACK MCTabProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
  109. {
  110. if (iMsg == g_uiMCDevChange)
  111. {
  112. InitMCVolume (g_hWndMC);
  113. }
  114. return CallWindowProc (g_fnMCPSProc, hwnd, iMsg, wParam, lParam);
  115. }
  116. ///////////////////////////////////////////////////////////////////////////////
  117. //
  118. // %%Function: MultichannelDlg
  119. //
  120. // Parameters: hDlg = window handle of dialog window.
  121. // uiMessage = message ID.
  122. // wParam = message-dependent.
  123. // lParam = message-dependent.
  124. //
  125. // Returns: TRUE if message has been processed, else FALSE
  126. //
  127. // Description: Dialog proc for multichannel volume control panel page.
  128. //
  129. //
  130. ///////////////////////////////////////////////////////////////////////////////
  131. INT_PTR CALLBACK MultichannelDlg (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  132. {
  133. switch (uMsg)
  134. {
  135. case WM_NOTIFY:
  136. {
  137. OnNotifyMC (hDlg, (LPNMHDR) lParam);
  138. }
  139. break;
  140. case WM_INITDIALOG:
  141. {
  142. HANDLE_WM_INITDIALOG (hDlg, wParam, lParam, OnInitDialogMC);
  143. }
  144. break;
  145. case WM_DESTROY:
  146. {
  147. HANDLE_WM_DESTROY (hDlg, wParam, lParam, OnDestroyMC);
  148. }
  149. break;
  150. case WM_COMMAND:
  151. {
  152. HANDLE_WM_COMMAND (hDlg, wParam, lParam, OnCommandMC);
  153. }
  154. break;
  155. case WM_HSCROLL:
  156. {
  157. HANDLE_WM_HSCROLL (hDlg, wParam, lParam, MCVolumeScroll);
  158. }
  159. break;
  160. case WM_POWERBROADCAST:
  161. {
  162. HandleMCPowerBroadcast (hDlg, wParam, lParam);
  163. }
  164. break;
  165. case MM_MIXM_LINE_CHANGE:
  166. case MM_MIXM_CONTROL_CHANGE:
  167. {
  168. if (!g_fInternalMCGenerated)
  169. {
  170. DisplayMCVolumeControl (hDlg);
  171. }
  172. g_fInternalMCGenerated = FALSE;
  173. }
  174. break;
  175. case WM_CONTEXTMENU:
  176. {
  177. WinHelp ((HWND)wParam, NULL, HELP_CONTEXTMENU,
  178. (UINT_PTR)(LPTSTR)aKeyWordIds);
  179. }
  180. break;
  181. case WM_HELP:
  182. {
  183. WinHelp (((LPHELPINFO)lParam)->hItemHandle, NULL, HELP_WM_HELP
  184. , (UINT_PTR)(LPTSTR)aKeyWordIds);
  185. }
  186. break;
  187. case WM_DEVICECHANGE:
  188. {
  189. MCDeviceChange_Change (hDlg, wParam, lParam);
  190. }
  191. break;
  192. case WM_WININICHANGE:
  193. case WM_DISPLAYCHANGE :
  194. {
  195. int iLastSliderID = IDC_MC_ZERO_VOLUME + (MC_SLIDER_COUNT - 1) * 4;
  196. int indx = IDC_MC_ZERO_VOLUME;
  197. for (; indx <= iLastSliderID; indx += 4)
  198. SendDlgItemMessage (hDlg, indx, uMsg, wParam, lParam);
  199. }
  200. break;
  201. }
  202. return FALSE;
  203. }
  204. void InitMCVolume (HWND hDlg)
  205. {
  206. FreeMCMixer ();
  207. if (MMSYSERR_NOERROR == mixerOpen (&g_hMCMixer, g_uiMCMixID, (DWORD_PTR) hDlg, 0L, CALLBACK_WINDOW))
  208. {
  209. if (SUCCEEDED (GetMCVolume ()) && g_paPrevious && g_mcdMC.paDetails)
  210. {
  211. // Copy data so can undo volume changes
  212. memcpy (g_paPrevious, g_mcdMC.paDetails, sizeof (MIXERCONTROLDETAILS_UNSIGNED) * g_mlMCDst.cChannels);
  213. DisplayMCVolumeControl (hDlg);
  214. }
  215. MCDeviceChange_Init (hDlg, g_uiMCMixID);
  216. }
  217. }
  218. BOOL OnInitDialogMC (HWND hDlg, HWND hwndFocus, LPARAM lParam)
  219. {
  220. TCHAR szDescription [255];
  221. LoadString (ghInstance, g_uiMCDescStringID, szDescription, sizeof (szDescription)/sizeof (TCHAR));
  222. SetWindowText (GetDlgItem (hDlg, IDC_MC_DESCRIPTION), szDescription);
  223. // Init Globals
  224. g_fInternalMCGenerated = FALSE;
  225. g_fMCChanged = FALSE;
  226. g_hWndMC = hDlg;
  227. // Set Device Change Notification
  228. g_fnMCPSProc = (WNDPROC) SetWindowLongPtr (GetParent (hDlg), GWLP_WNDPROC, (LONG_PTR) MCTabProc);
  229. g_uiMCDevChange = RegisterWindowMessage (_T("winmm_devicechange"));
  230. // Init Volume
  231. InitMCVolume (hDlg);
  232. return FALSE;
  233. }
  234. void OnDestroyMC (HWND hDlg)
  235. {
  236. // Unregister from notifications
  237. MCDeviceChange_Cleanup ();
  238. SetWindowLongPtr (GetParent (hDlg), GWLP_WNDPROC, (LONG_PTR) g_fnMCPSProc);
  239. FreeAll ();
  240. }
  241. void OnNotifyMC (HWND hDlg, LPNMHDR pnmh)
  242. {
  243. if (!pnmh)
  244. {
  245. DPF ("bad WM_NOTIFY pointer\n");
  246. return;
  247. }
  248. switch (pnmh->code)
  249. {
  250. case PSN_KILLACTIVE:
  251. FORWARD_WM_COMMAND (hDlg, IDOK, 0, 0, SendMessage);
  252. break;
  253. case PSN_APPLY:
  254. FORWARD_WM_COMMAND (hDlg, ID_APPLY, 0, 0, SendMessage);
  255. break;
  256. case PSN_RESET:
  257. FORWARD_WM_COMMAND (hDlg, IDCANCEL, 0, 0, SendMessage);
  258. break;
  259. }
  260. }
  261. BOOL PASCAL OnCommandMC (HWND hDlg, int id, HWND hwndCtl, UINT codeNotify)
  262. {
  263. switch (id)
  264. {
  265. case IDC_MC_RESTORE:
  266. {
  267. // Move all sliders to center
  268. UINT uiIndx;
  269. for (uiIndx = 0; uiIndx < g_mlMCDst.cChannels; uiIndx++)
  270. {
  271. ((MIXERCONTROLDETAILS_UNSIGNED*)g_mcdMC.paDetails + uiIndx) -> dwValue = VOLUME_MAX/2;
  272. }
  273. g_fInternalMCGenerated = FALSE;
  274. mixerSetControlDetails ((HMIXEROBJ) g_hMCMixer, &g_mcdMC, MIXER_SETCONTROLDETAILSF_VALUE);
  275. if (!g_fMCChanged)
  276. {
  277. g_fMCChanged = TRUE;
  278. PropSheet_Changed (GetParent (hDlg), hDlg);
  279. }
  280. }
  281. break;
  282. case ID_APPLY:
  283. {
  284. if (SUCCEEDED (GetMCVolume ()) && g_paPrevious && g_mcdMC.paDetails)
  285. {
  286. // Copy data so can undo volume changes
  287. memcpy (g_paPrevious, g_mcdMC.paDetails, sizeof (MIXERCONTROLDETAILS_UNSIGNED) * g_mlMCDst.cChannels);
  288. DisplayMCVolumeControl (hDlg);
  289. }
  290. g_fMCChanged = FALSE;
  291. return TRUE;
  292. }
  293. break;
  294. case IDOK:
  295. {
  296. }
  297. break;
  298. case IDCANCEL:
  299. {
  300. if (g_paPrevious && g_mcdMC.paDetails)
  301. {
  302. // Undo volume changes
  303. memcpy (g_mcdMC.paDetails, g_paPrevious, sizeof (MIXERCONTROLDETAILS_UNSIGNED) * g_mlMCDst.cChannels);
  304. g_fInternalMCGenerated = TRUE;
  305. mixerSetControlDetails ((HMIXEROBJ) g_hMCMixer, &g_mcdMC, MIXER_SETCONTROLDETAILSF_VALUE);
  306. }
  307. WinHelp (hDlg, gszWindowsHlp, HELP_QUIT, 0L);
  308. }
  309. break;
  310. }
  311. return FALSE;
  312. }
  313. void HandleMCPowerBroadcast (HWND hWnd, WPARAM wParam, LPARAM lParam)
  314. {
  315. switch (wParam)
  316. {
  317. case PBT_APMQUERYSUSPEND:
  318. {
  319. FreeMCMixer ();
  320. }
  321. break;
  322. case PBT_APMQUERYSUSPENDFAILED:
  323. case PBT_APMRESUMESUSPEND:
  324. {
  325. InitMCVolume (hWnd);
  326. }
  327. break;
  328. }
  329. }
  330. BOOL SliderIDtoChannel (UINT uiSliderID, DWORD* pdwChannel)
  331. {
  332. if (!pdwChannel)
  333. return FALSE;
  334. // Determine channel number (index)
  335. switch (uiSliderID)
  336. {
  337. case IDC_MC_ZERO_VOLUME: // Left
  338. *pdwChannel = 0;
  339. break;
  340. case IDC_MC_ONE_VOLUME: // Right
  341. *pdwChannel = 1;
  342. break;
  343. case IDC_MC_TWO_VOLUME: // Center
  344. *pdwChannel = 2;
  345. break;
  346. case IDC_MC_THREE_VOLUME: // Back Left
  347. *pdwChannel = 3;
  348. break;
  349. case IDC_MC_FOUR_VOLUME: // Back Right
  350. *pdwChannel = 4;
  351. break;
  352. case IDC_MC_FIVE_VOLUME: // Low Frequency
  353. *pdwChannel = 5;
  354. break;
  355. case IDC_MC_SIX_VOLUME: // Left of Center
  356. *pdwChannel = 6;
  357. break;
  358. case IDC_MC_SEVEN_VOLUME: // Right of Center
  359. *pdwChannel = 7;
  360. break;
  361. default:
  362. return FALSE;
  363. }
  364. return ((*pdwChannel) < g_mlMCDst.cChannels);
  365. }
  366. // Called in response to slider movement, computes new volume level and sets it
  367. // it also controls the apply state (changed or not)
  368. void MCVolumeScroll (HWND hwnd, HWND hwndCtl, UINT code, int pos)
  369. {
  370. BOOL fSet;
  371. BOOL fMoveTogether;
  372. DWORD dwChannel;
  373. DWORD dwSliderVol;
  374. DWORD dwNewMixerVol;
  375. DWORD dwOldMixerVol;
  376. DWORD dwVolume;
  377. fMoveTogether = IsDlgButtonChecked (hwnd, IDC_MC_MOVE_TOGETHER);
  378. if (SliderIDtoChannel (GetDlgCtrlID (hwndCtl), &dwChannel))
  379. {
  380. // Set the new volume
  381. dwSliderVol = (DWORD) SendMessage (hwndCtl, TBM_GETPOS, 0, 0);
  382. dwNewMixerVol = (VOLUME_MAX * dwSliderVol + VOLUME_TICS / 2) / VOLUME_TICS;
  383. dwOldMixerVol = (g_paPrevious ? ((MIXERCONTROLDETAILS_UNSIGNED*)g_paPrevious + dwChannel) -> dwValue : 0);
  384. fSet = SetMCVolume (dwChannel, dwNewMixerVol, fMoveTogether);
  385. if (!fSet)
  386. {
  387. // Restore the correct thumb position.
  388. dwVolume = (VOLUME_TICS * ((MIXERCONTROLDETAILS_UNSIGNED*)g_mcdMC.paDetails + dwChannel) -> dwValue + VOLUME_MAX / 2) / VOLUME_MAX;
  389. SendMessage (hwndCtl, TBM_SETPOS, TRUE, dwVolume);
  390. }
  391. if ((fMoveTogether || dwOldMixerVol != dwNewMixerVol) && !g_fMCChanged)
  392. {
  393. g_fMCChanged = TRUE;
  394. PropSheet_Changed (GetParent (hwnd), hwnd);
  395. }
  396. }
  397. }
  398. // Sets the volume level
  399. //
  400. BOOL SetMCVolume (DWORD dwChannel, DWORD dwVol, BOOL fMoveTogether)
  401. {
  402. BOOL fReturn;
  403. UINT uiIndx;
  404. DWORD dwValue;
  405. long lMoveValue;
  406. long lMoveValueActual;
  407. fReturn = TRUE;
  408. if (dwChannel < g_mlMCDst.cChannels && g_mcdMC.paDetails && g_hMCMixer)
  409. {
  410. if (fMoveTogether)
  411. {
  412. // Note: Do not set g_fInternalMCGenerated = TRUE here because we are relying
  413. // on the change notification to update the other sliders.
  414. lMoveValue = (long)((double)dwVol - (double)(((MIXERCONTROLDETAILS_UNSIGNED*)g_mcdMC.paDetails + dwChannel) -> dwValue));
  415. lMoveValueActual = lMoveValue;
  416. // Don't bother if no move requested.
  417. if (lMoveValue == 0)
  418. return TRUE; // Already Set
  419. // Ensure that the new value is within the range of all the sliders that are
  420. // being used. This will ensure that we maintain the distance between sliders
  421. // as they are being moved.
  422. for (uiIndx = 0; uiIndx < g_mlMCDst.cChannels; uiIndx++)
  423. {
  424. dwValue = ((MIXERCONTROLDETAILS_UNSIGNED*)g_mcdMC.paDetails + uiIndx) -> dwValue;
  425. if (VOLUME_MIN > ((long)dwValue+lMoveValueActual))
  426. {
  427. lMoveValueActual = VOLUME_MIN - dwValue;
  428. }
  429. else
  430. {
  431. if (VOLUME_MAX < ((long)dwValue+lMoveValueActual))
  432. lMoveValueActual = VOLUME_MAX - dwValue;
  433. }
  434. }
  435. if (lMoveValueActual != 0)
  436. {
  437. // Update the values
  438. for (uiIndx = 0; uiIndx < g_mlMCDst.cChannels; uiIndx++)
  439. {
  440. dwValue = ((MIXERCONTROLDETAILS_UNSIGNED*)g_mcdMC.paDetails + uiIndx) -> dwValue;
  441. ((MIXERCONTROLDETAILS_UNSIGNED*)g_mcdMC.paDetails + uiIndx) -> dwValue = (DWORD)((long) dwValue + lMoveValueActual);
  442. }
  443. }
  444. else
  445. {
  446. // Let user know that they can move no farther in the current direction.
  447. // Note: We use the PC Speaker instead of the mixer because this is an
  448. // indicator that they are at either min or max volume for one of
  449. // the sliders. Since these are channel volume sliders, if we used
  450. // the mixer, the user would either not hear anything (min volume)
  451. // or get blown away (max volume).
  452. MessageBeep (-1 /*PC Speaker*/);
  453. fReturn = FALSE;
  454. }
  455. }
  456. else
  457. {
  458. g_fInternalMCGenerated = TRUE;
  459. ((MIXERCONTROLDETAILS_UNSIGNED*)g_mcdMC.paDetails + dwChannel) -> dwValue = dwVol;
  460. }
  461. mixerSetControlDetails ((HMIXEROBJ) g_hMCMixer, &g_mcdMC, MIXER_SETCONTROLDETAILSF_VALUE);
  462. }
  463. return fReturn;
  464. }
  465. void ShowAndEnableWindow (HWND hwnd, BOOL fEnable)
  466. {
  467. EnableWindow (hwnd, fEnable);
  468. ShowWindow (hwnd, fEnable ? SW_SHOW : SW_HIDE);
  469. }
  470. void DisplayMCVolumeControl (HWND hDlg)
  471. {
  472. HWND hwndVol = NULL;
  473. HWND hwndLabel = NULL;
  474. WCHAR szLabel[MAX_PATH];
  475. BOOL fEnabled;
  476. UINT uiIndx;
  477. DWORD dwSpeakerType = TYPE_STEREODESKTOP;
  478. BOOL fPlayback = (MIXERLINE_COMPONENTTYPE_DST_SPEAKERS == g_mlMCDst.dwComponentType);
  479. ZeroMemory (szLabel, sizeof (szLabel));
  480. // Get Speaker Configuration Type
  481. if (fPlayback)
  482. GetSpeakerType (&dwSpeakerType);
  483. for (uiIndx = 0; uiIndx < MC_SLIDER_COUNT; uiIndx++)
  484. {
  485. fEnabled = (uiIndx < g_mlMCDst.cChannels);
  486. // Set up the volume slider
  487. hwndVol = GetDlgItem (hDlg, IDC_MC_ZERO_VOLUME + uiIndx * 4);
  488. SendMessage (hwndVol, TBM_SETTICFREQ, VOLUME_TICS / 10, 0);
  489. SendMessage (hwndVol, TBM_SETRANGE, FALSE, MAKELONG (0, VOLUME_TICS));
  490. // Enable/Disable sliders
  491. hwndLabel = GetDlgItem (hDlg, IDC_MC_ZERO + uiIndx * 4);
  492. ShowAndEnableWindow (hwndVol, fEnabled);
  493. ShowAndEnableWindow (hwndLabel, fEnabled);
  494. ShowAndEnableWindow (GetDlgItem (hDlg, IDC_MC_ZERO_LOW + uiIndx * 4), fEnabled);
  495. ShowAndEnableWindow (GetDlgItem (hDlg, IDC_MC_ZERO_HIGH + uiIndx * 4), fEnabled);
  496. if (fPlayback)
  497. {
  498. GetSpeakerLabel (dwSpeakerType, uiIndx, szLabel, sizeof(szLabel)/sizeof(TCHAR));
  499. }
  500. else
  501. {
  502. LoadString (ghInstance, IDS_MC_CHANNEL_ZERO + uiIndx, szLabel, MAX_PATH);
  503. }
  504. SetWindowText (hwndLabel, szLabel);
  505. }
  506. if (0 < g_mlMCDst.cChannels)
  507. {
  508. UpdateMCVolumeSliders (hDlg);
  509. }
  510. }
  511. BOOL GetSpeakerType (DWORD* pdwSpeakerType)
  512. {
  513. BOOL fReturn = FALSE;
  514. if (pdwSpeakerType)
  515. {
  516. MIXERCAPS mc;
  517. *pdwSpeakerType = TYPE_STEREODESKTOP; // Init Value
  518. if (MMSYSERR_NOERROR == mixerGetDevCaps (g_uiMCMixID, &mc, sizeof (mc)))
  519. {
  520. GUID guid;
  521. if (SUCCEEDED (DSGetGuidFromName (mc.szPname, FALSE, &guid)))
  522. {
  523. CPLDATA cpldata;
  524. if (SUCCEEDED (DSGetCplValues (guid, FALSE, &cpldata)))
  525. {
  526. *pdwSpeakerType = cpldata.dwSpeakerType;
  527. fReturn = TRUE;
  528. }
  529. }
  530. }
  531. }
  532. return fReturn;
  533. }
  534. BOOL GetSpeakerLabel (DWORD dwSpeakerType, UINT uiSliderIndx, WCHAR* szLabel, int nSize)
  535. {
  536. if (uiSliderIndx >= MC_SLIDER_COUNT || !szLabel || nSize <= 0)
  537. // Invalid
  538. return FALSE;
  539. switch (dwSpeakerType)
  540. {
  541. //
  542. // Mono
  543. //
  544. case TYPE_NOSPEAKERS:
  545. case TYPE_MONOLAPTOP:
  546. if (0 == uiSliderIndx)
  547. return (LoadString (ghInstance, IDS_MC_SPEAKER_CENTER, szLabel, nSize));
  548. break;
  549. //
  550. // Stereo
  551. //
  552. case TYPE_HEADPHONES:
  553. case TYPE_STEREODESKTOP:
  554. case TYPE_STEREOLAPTOP:
  555. case TYPE_STEREOMONITOR:
  556. case TYPE_STEREOCPU:
  557. case TYPE_MOUNTEDSTEREO:
  558. case TYPE_STEREOKEYBOARD:
  559. // Left & Right Channel ...
  560. if (0 == uiSliderIndx)
  561. return (LoadString (ghInstance, IDS_MC_SPEAKER_LEFT, szLabel, nSize));
  562. else if (1 == uiSliderIndx)
  563. return (LoadString (ghInstance, IDS_MC_SPEAKER_RIGHT, szLabel, nSize));
  564. break;
  565. //
  566. // Greater than Stereo
  567. //
  568. case TYPE_SURROUND:
  569. // Left & Right Channel ...
  570. if (0 == uiSliderIndx)
  571. return (LoadString (ghInstance, IDS_MC_SPEAKER_LEFT, szLabel, nSize));
  572. else if (1 == uiSliderIndx)
  573. return (LoadString (ghInstance, IDS_MC_SPEAKER_RIGHT, szLabel, nSize));
  574. // Center Front & Back
  575. else if (2 == uiSliderIndx)
  576. return (LoadString (ghInstance, IDS_MC_SPEAKER_CENTER, szLabel, nSize));
  577. else if (3 == uiSliderIndx)
  578. return (LoadString (ghInstance, IDS_MC_SPEAKER_BACKCENTER, szLabel, nSize));
  579. break;
  580. case TYPE_QUADRAPHONIC:
  581. // Left & Right Channel ...
  582. if (0 == uiSliderIndx)
  583. return (LoadString (ghInstance, IDS_MC_SPEAKER_LEFT, szLabel, nSize));
  584. else if (1 == uiSliderIndx)
  585. return (LoadString (ghInstance, IDS_MC_SPEAKER_RIGHT, szLabel, nSize));
  586. // Back Left & Back Right Channel ...
  587. else if (2 == uiSliderIndx)
  588. return (LoadString (ghInstance, IDS_MC_SPEAKER_BACKLEFT, szLabel, nSize));
  589. else if (3 == uiSliderIndx)
  590. return (LoadString (ghInstance, IDS_MC_SPEAKER_BACKRIGHT, szLabel, nSize));
  591. break;
  592. case TYPE_SURROUND_5_1:
  593. case TYPE_SURROUND_7_1:
  594. // Left & Right Channel ...
  595. if (0 == uiSliderIndx)
  596. return (LoadString (ghInstance, IDS_MC_SPEAKER_LEFT, szLabel, nSize));
  597. else if (1 == uiSliderIndx)
  598. return (LoadString (ghInstance, IDS_MC_SPEAKER_RIGHT, szLabel, nSize));
  599. // Center Channel ...
  600. if (2 == uiSliderIndx)
  601. return (LoadString (ghInstance, IDS_MC_SPEAKER_CENTER, szLabel, nSize));
  602. // Low Frequency ...
  603. if (3 == uiSliderIndx)
  604. return (LoadString (ghInstance, IDS_MC_SPEAKER_LOWFREQUENCY, szLabel, nSize));
  605. // Back Left & Back Right Channel ...
  606. if (4 == uiSliderIndx)
  607. return (LoadString (ghInstance, IDS_MC_SPEAKER_BACKLEFT, szLabel, nSize));
  608. else if (5 == uiSliderIndx)
  609. return (LoadString (ghInstance, IDS_MC_SPEAKER_BACKRIGHT, szLabel, nSize));
  610. if (TYPE_SURROUND_5_1 == dwSpeakerType)
  611. break;
  612. // Left of Center & Right of Center Channel ...
  613. if (6 == uiSliderIndx)
  614. return (LoadString (ghInstance, IDS_MC_SPEAKER_LEFT_OF_CENTER, szLabel, nSize));
  615. else if (7 == uiSliderIndx)
  616. return (LoadString (ghInstance, IDS_MC_SPEAKER_RIGHT_OF_CENTER, szLabel, nSize));
  617. break;
  618. }
  619. // If we are here, we don't know the speaker type or we have too many
  620. // channels for a known type, just use the generic channel text ...
  621. return (LoadString (ghInstance, IDS_MC_CHANNEL_ZERO + uiSliderIndx, szLabel, nSize));
  622. }
  623. // Called to update the slider when the volume is changed externally
  624. //
  625. void UpdateMCVolumeSliders (HWND hDlg)
  626. {
  627. if (g_hMCMixer && g_mcdMC.paDetails && SUCCEEDED (GetMCVolume ()))
  628. {
  629. UINT uiIndx;
  630. DWORD dwVolume;
  631. MIXERCONTROLDETAILS_UNSIGNED mcuVolume;
  632. for (uiIndx = 0; uiIndx < g_mlMCDst.cChannels; uiIndx++)
  633. {
  634. mcuVolume = *((MIXERCONTROLDETAILS_UNSIGNED*)g_mcdMC.paDetails + uiIndx);
  635. dwVolume = (VOLUME_TICS * mcuVolume.dwValue + VOLUME_MAX / 2) / VOLUME_MAX;
  636. SendMessage (GetDlgItem (hDlg, IDC_MC_ZERO_VOLUME + uiIndx * 4), TBM_SETPOS, TRUE, dwVolume);
  637. }
  638. }
  639. }
  640. void FreeAll ()
  641. {
  642. FreeMCMixer ();
  643. if (g_mcdMC.paDetails)
  644. {
  645. LocalFree (g_mcdMC.paDetails);
  646. g_mcdMC.paDetails = NULL;
  647. }
  648. if (g_paPrevious)
  649. {
  650. LocalFree (g_paPrevious);
  651. g_paPrevious = NULL;
  652. }
  653. ZeroMemory (&g_mcdMC, sizeof (g_mcdMC));
  654. ZeroMemory (&g_mlMCDst, sizeof (g_mlMCDst));
  655. }
  656. void FreeMCMixer ()
  657. {
  658. if (g_hMCMixer)
  659. {
  660. mixerClose (g_hMCMixer);
  661. g_hMCMixer = NULL;
  662. }
  663. }
  664. HRESULT SetDevice (UINT uiMixID, DWORD dwDest, DWORD dwVolID)
  665. {
  666. HMIXER hMixer = NULL;
  667. HRESULT hr = E_FAIL;
  668. // Free any current mixer stuff
  669. FreeAll ();
  670. if (MMSYSERR_NOERROR == mixerOpen (&hMixer, uiMixID, 0, 0, MIXER_OBJECTF_MIXER))
  671. {
  672. g_mlMCDst.cbStruct = sizeof (g_mlMCDst);
  673. g_mlMCDst.dwDestination = dwDest;
  674. if (MMSYSERR_NOERROR == mixerGetLineInfo ((HMIXEROBJ) hMixer, &g_mlMCDst, MIXER_GETLINEINFOF_DESTINATION))
  675. {
  676. g_mcdMC.cbStruct = sizeof (g_mcdMC);
  677. g_mcdMC.dwControlID = dwVolID;
  678. g_mcdMC.cChannels = g_mlMCDst.cChannels;
  679. g_mcdMC.hwndOwner = 0;
  680. g_mcdMC.cMultipleItems = 0;
  681. g_mcdMC.cbDetails = sizeof (DWORD); // seems like it would be sizeof(g_mcd),
  682. // but actually, it is the size of a single value
  683. // and is multiplied by channel in the driver.
  684. g_mcdMC.paDetails = (MIXERCONTROLDETAILS_UNSIGNED*) LocalAlloc (LPTR, sizeof (MIXERCONTROLDETAILS_UNSIGNED) * g_mlMCDst.cChannels);
  685. g_paPrevious = (MIXERCONTROLDETAILS_UNSIGNED*) LocalAlloc (LPTR, sizeof (MIXERCONTROLDETAILS_UNSIGNED) * g_mlMCDst.cChannels);
  686. if (g_mcdMC.paDetails && g_paPrevious)
  687. {
  688. hr = S_OK;
  689. // Init our other globals
  690. g_uiMCMixID = uiMixID;
  691. switch (g_mlMCDst.dwComponentType)
  692. {
  693. case MIXERLINE_COMPONENTTYPE_DST_SPEAKERS:
  694. g_uiMCPageStringID = IDS_MC_PLAYBACK;
  695. g_uiMCDescStringID = IDS_MC_PLAYBACK_DESC;
  696. break;
  697. case MIXERLINE_COMPONENTTYPE_DST_WAVEIN:
  698. case MIXERLINE_COMPONENTTYPE_DST_VOICEIN:
  699. g_uiMCPageStringID = IDS_MC_RECORDING;
  700. g_uiMCDescStringID = IDS_MC_RECORDING_DESC;
  701. break;
  702. default:
  703. g_uiMCPageStringID = IDS_MC_OTHER;
  704. g_uiMCDescStringID = IDS_MC_OTHER_DESC;
  705. break;
  706. }
  707. }
  708. }
  709. mixerClose (hMixer);
  710. }
  711. return hr;
  712. }
  713. HRESULT GetMCVolume ()
  714. {
  715. HRESULT hr = E_FAIL;
  716. if (g_hMCMixer && g_mcdMC.paDetails)
  717. {
  718. ZeroMemory (g_mcdMC.paDetails, sizeof (MIXERCONTROLDETAILS_UNSIGNED) * g_mlMCDst.cChannels);
  719. hr = mixerGetControlDetails ((HMIXEROBJ)g_hMCMixer, &g_mcdMC, MIXER_GETCONTROLDETAILSF_VALUE);
  720. }
  721. return hr;
  722. }
  723. UINT GetPageStringID ()
  724. {
  725. return g_uiMCPageStringID;
  726. }
  727. void MCDeviceChange_Cleanup ()
  728. {
  729. if (g_hMCDeviceEventContext)
  730. {
  731. UnregisterDeviceNotification (g_hMCDeviceEventContext);
  732. g_hMCDeviceEventContext = NULL;
  733. }
  734. }
  735. void MCDeviceChange_Init (HWND hWnd, DWORD dwMixerID)
  736. {
  737. DEV_BROADCAST_HANDLE DevBrodHandle;
  738. HANDLE hMixerDevice=NULL;
  739. //If we had registered already for device notifications, unregister ourselves.
  740. MCDeviceChange_Cleanup();
  741. //If we get the device handle register for device notifications on it.
  742. if(DeviceChange_GetHandle(dwMixerID, &hMixerDevice))
  743. {
  744. memset(&DevBrodHandle, 0, sizeof(DEV_BROADCAST_HANDLE));
  745. DevBrodHandle.dbch_size = sizeof(DEV_BROADCAST_HANDLE);
  746. DevBrodHandle.dbch_devicetype = DBT_DEVTYP_HANDLE;
  747. DevBrodHandle.dbch_handle = hMixerDevice;
  748. g_hMCDeviceEventContext = RegisterDeviceNotification(hWnd, &DevBrodHandle, DEVICE_NOTIFY_WINDOW_HANDLE);
  749. if(hMixerDevice)
  750. {
  751. CloseHandle(hMixerDevice);
  752. hMixerDevice = NULL;
  753. }
  754. }
  755. }
  756. // Handle the case where we need to dump mixer handle so PnP can get rid of a device
  757. // We assume we will get the WINMM_DEVICECHANGE handle when the dust settles after a remove or add
  758. // except for DEVICEQUERYREMOVEFAILED which will not generate that message.
  759. //
  760. void MCDeviceChange_Change (HWND hDlg, WPARAM wParam, LPARAM lParam)
  761. {
  762. PDEV_BROADCAST_HANDLE bh = (PDEV_BROADCAST_HANDLE)lParam;
  763. if(!g_hMCDeviceEventContext || !bh || bh->dbch_devicetype != DBT_DEVTYP_HANDLE)
  764. {
  765. return;
  766. }
  767. switch (wParam)
  768. {
  769. case DBT_DEVICEQUERYREMOVE: // Must free up Mixer if they are trying to remove the device
  770. {
  771. FreeMCMixer ();
  772. }
  773. break;
  774. case DBT_DEVICEQUERYREMOVEFAILED: // Didn't happen, need to re-acquire mixer
  775. {
  776. InitMCVolume (hDlg);
  777. }
  778. break;
  779. }
  780. }