Source code of Windows XP (NT5)
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.

2611 lines
77 KiB

  1. /*****************************************************************************
  2. *
  3. * Component: sndvol32.exe
  4. * File: mixer.c
  5. * Purpose: mixer api specific implementations
  6. *
  7. * Copyright (c) 1985-1999 Microsoft Corporation
  8. *
  9. *****************************************************************************/
  10. #include <windows.h>
  11. #include <windowsx.h>
  12. #include <mmsystem.h>
  13. #include <commctrl.h>
  14. #include <math.h>
  15. #include "volumei.h"
  16. #include "volids.h"
  17. #include "vu.h"
  18. extern void Mixer_Multichannel(PMIXUIDIALOG pmxud, DWORD dwVolumeID);
  19. extern void Mixer_Advanced(PMIXUIDIALOG pmxud, DWORD dwLineID, LPTSTR szName);
  20. extern HRESULT GetDestination(DWORD mxid, int *piDest);
  21. extern BOOL DeviceChange_Init(HWND hWnd, DWORD dwMixerID);
  22. /*****************************************************************************
  23. *
  24. * INIT SPECIFIC CODE
  25. *
  26. *****************************************************************************/
  27. /*
  28. * Mixer_GetNumDevs
  29. *
  30. * */
  31. int Mixer_GetNumDevs()
  32. {
  33. return mixerGetNumDevs();
  34. }
  35. /*
  36. * Mixer_GetDeviceName()
  37. *
  38. * */
  39. BOOL Mixer_GetDeviceName(
  40. PMIXUIDIALOG pmxud)
  41. {
  42. MIXERCAPS mxcaps;
  43. MMRESULT mmr;
  44. mmr = mixerGetDevCaps( pmxud->mxid, &mxcaps, sizeof(mxcaps));
  45. if (mmr != MMSYSERR_NOERROR)
  46. return FALSE;
  47. lstrcpy(pmxud->szMixer, mxcaps.szPname);
  48. return TRUE;
  49. }
  50. BOOL Mixer_AreChannelsAtMinimum(MIXERCONTROLDETAILS_UNSIGNED* pmcdVolume,
  51. DWORD cChannels)
  52. {
  53. UINT uiIndx;
  54. if (pmcdVolume && cChannels > 0)
  55. {
  56. for (uiIndx = 0; uiIndx < cChannels; uiIndx++)
  57. {
  58. if ((pmcdVolume + uiIndx) -> dwValue != 0)
  59. {
  60. return (FALSE);
  61. }
  62. }
  63. return (TRUE); // Volume of all channels equals zero since we haven't returned yet.
  64. }
  65. else return (FALSE);
  66. }
  67. void Mixer_RefreshMixCache (PVOLCTRLDESC pvcd,
  68. MIXERCONTROLDETAILS_UNSIGNED* pmcdVolume,
  69. DWORD cChannels)
  70. {
  71. if (pmcdVolume && cChannels > 0)
  72. {
  73. // Create cache if necessary
  74. if (!pvcd->pdblCacheMix)
  75. pvcd->pdblCacheMix = (double*) GlobalAllocPtr(GHND, sizeof (double) * cChannels);
  76. // Refresh cache
  77. if (pvcd->pdblCacheMix)
  78. {
  79. UINT uiIndx;
  80. double* pdblMixPercent;
  81. DWORD dwVolume;
  82. // Get the maximum volume
  83. DWORD dwMaxVol = 0;
  84. for (uiIndx = 0; uiIndx < cChannels; uiIndx++)
  85. dwMaxVol = max (dwMaxVol, (pmcdVolume + uiIndx) -> dwValue);
  86. // Caculate the percentage distance each channel is away from the max
  87. // value. Creating this cache allows us to maintain the relative distance
  88. // of the channel levels from each other as the user adjusts the master
  89. // volume level.
  90. for (uiIndx = 0; uiIndx < cChannels; uiIndx++)
  91. {
  92. dwVolume = (pmcdVolume + uiIndx) -> dwValue;
  93. pdblMixPercent = (pvcd->pdblCacheMix + uiIndx);
  94. // Caculate the percentage this value is from the max ...
  95. if (dwMaxVol == dwVolume)
  96. {
  97. *pdblMixPercent = 1.0F;
  98. }
  99. else
  100. {
  101. // Note: if 0 == dwMaxVol all values would be zero and this part
  102. // of the "if" statement will never execute.
  103. *pdblMixPercent = ((double) dwVolume / (double) dwMaxVol);
  104. }
  105. }
  106. }
  107. }
  108. }
  109. /*
  110. * Mixer_SetLines
  111. *
  112. * Locate mixer/mux relationships. Fix up uninitialized volume description
  113. * information.
  114. *
  115. * */
  116. static void Mixer_SetLines(
  117. HMIXEROBJ hmx,
  118. PVOLCTRLDESC pvcd,
  119. UINT cPvcd)
  120. {
  121. LPMIXERCONTROLDETAILS_LISTTEXT pmcd_lt;
  122. PMIXERCONTROLDETAILS_BOOLEAN pmcd_b;
  123. MIXERCONTROLDETAILS mxd;
  124. MMRESULT mmr;
  125. UINT i,j;
  126. MIXERLINE mxl;
  127. DWORD dwDst;
  128. //
  129. // Another test for drivers. Some drivers (Mediavision)
  130. // don't return the proper destination / source index in the
  131. // mixerGetLineInfo call. Tag a workaround.
  132. //
  133. mxl.cbStruct = sizeof(mxl);
  134. mxl.dwLineID = pvcd[0].dwLineID;
  135. mmr = mixerGetLineInfo(hmx
  136. , &mxl
  137. , MIXER_GETLINEINFOF_LINEID);
  138. if (mmr == MMSYSERR_NOERROR)
  139. {
  140. dwDst = mxl.dwDestination;
  141. for (i = 1; i < cPvcd; i++)
  142. {
  143. mxl.cbStruct = sizeof(mxl);
  144. mxl.dwLineID = pvcd[i].dwLineID;
  145. mmr = mixerGetLineInfo(hmx
  146. , &mxl
  147. , MIXER_GETLINEINFOF_LINEID);
  148. if (mmr != MMSYSERR_NOERROR)
  149. {
  150. pvcd[0].dwSupport |= VCD_SUPPORTF_BADDRIVER;
  151. break;
  152. }
  153. if (mxl.dwDestination != dwDst)
  154. {
  155. pvcd[0].dwSupport |= VCD_SUPPORTF_BADDRIVER;
  156. break;
  157. }
  158. }
  159. }
  160. //
  161. // for the first pvcd (destination), propogate the mixer/mux control
  162. // id's to those controls that are part of the list. 0 out the rest.
  163. // The UI can just do a mixerXXXControlDetails on the control ID to
  164. // locate the state information
  165. //
  166. if (pvcd->dwSupport & VCD_SUPPORTF_MIXER_MIXER)
  167. {
  168. pmcd_lt = GlobalAllocPtr(GHND, sizeof(MIXERCONTROLDETAILS_LISTTEXT)
  169. * pvcd->cMixer);
  170. pmcd_b = GlobalAllocPtr(GHND, sizeof(MIXERCONTROLDETAILS_BOOLEAN)
  171. * pvcd->cMixer);
  172. if (!pmcd_lt || !pmcd_b)
  173. return;
  174. mxd.cbStruct = sizeof(mxd);
  175. mxd.dwControlID = pvcd->dwMixerID;
  176. mxd.cChannels = 1;
  177. mxd.cMultipleItems = pvcd->cMixer;
  178. mxd.cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXT);
  179. mxd.paDetails = pmcd_lt;
  180. mmr = mixerGetControlDetails(hmx
  181. , &mxd
  182. , MIXER_GETCONTROLDETAILSF_LISTTEXT);
  183. if (mmr == MMSYSERR_NOERROR)
  184. {
  185. //
  186. // iterate over all source lines s.t. dwMixerID points to the
  187. // correct control id on the destination and iMixer is the
  188. // correct index into the value list
  189. //
  190. pvcd[0].amcd_bMixer = pmcd_b;
  191. for (i = 1; i < cPvcd; i++)
  192. {
  193. for (j = 0; j < pvcd->cMixer; j++)
  194. {
  195. if (!lstrcmp(pmcd_lt[j].szName,pvcd[i].szName))
  196. {
  197. pvcd[i].dwMixerID = pvcd->dwMixerID;
  198. pvcd[i].iMixer = j;
  199. pvcd[i].cMixer = pvcd->cMixer;
  200. pvcd[i].dwSupport |= VCD_SUPPORTF_MIXER_MIXER;
  201. pvcd[i].dwVisible |= VCD_VISIBLEF_MIXER_MIXER;
  202. pvcd[i].dwVisible &= ~VCD_VISIBLEF_MIXER_MUTE;
  203. pvcd[i].amcd_bMixer = pmcd_b;
  204. }
  205. }
  206. }
  207. }
  208. GlobalFreePtr(pmcd_lt);
  209. }
  210. if (pvcd->dwSupport & VCD_SUPPORTF_MIXER_MUX)
  211. {
  212. pmcd_lt = GlobalAllocPtr(GHND, sizeof(MIXERCONTROLDETAILS_LISTTEXT)
  213. * pvcd->cMux);
  214. pmcd_b = GlobalAllocPtr(GHND, sizeof(MIXERCONTROLDETAILS_BOOLEAN)
  215. * pvcd->cMux);
  216. if (!pmcd_lt || !pmcd_b)
  217. return;
  218. mxd.cbStruct = sizeof(mxd);
  219. mxd.dwControlID = pvcd->dwMuxID;
  220. mxd.cChannels = 1;
  221. mxd.cMultipleItems = pvcd->cMux;
  222. mxd.cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXT);
  223. mxd.paDetails = pmcd_lt;
  224. mmr = mixerGetControlDetails(hmx
  225. , &mxd
  226. , MIXER_GETCONTROLDETAILSF_LISTTEXT);
  227. if (mmr == MMSYSERR_NOERROR)
  228. {
  229. //
  230. // iterate over all source lines s.t. dwMuxID points to the
  231. // correct control id on the destination and iMux is the
  232. // correct index into the value list
  233. //
  234. pvcd[0].amcd_bMux = pmcd_b;
  235. for (i = 1; i < cPvcd; i++)
  236. {
  237. for (j = 0; j < pvcd->cMux; j++)
  238. {
  239. if (!lstrcmp(pmcd_lt[j].szName,pvcd[i].szName))
  240. {
  241. pvcd[i].dwMuxID = pvcd->dwMuxID;
  242. pvcd[i].iMux = j;
  243. pvcd[i].cMux = pvcd->cMux;
  244. pvcd[i].dwSupport |= VCD_SUPPORTF_MIXER_MUX;
  245. pvcd[i].dwVisible |= VCD_VISIBLEF_MIXER_MUX;
  246. pvcd[i].dwVisible &= ~VCD_VISIBLEF_MIXER_MUTE;
  247. pvcd[i].amcd_bMux = pmcd_b;
  248. }
  249. }
  250. }
  251. }
  252. GlobalFreePtr(pmcd_lt);
  253. }
  254. }
  255. /*
  256. * Mixer_CheckdDriver
  257. *
  258. * Consistency check for bad mixer drivers.
  259. * */
  260. static DWORD Mixer_CheckBadDriver(
  261. HMIXEROBJ hmx,
  262. PMIXERLINECONTROLS pmxlc,
  263. PMIXERCONTROL pmxc,
  264. DWORD dwControlID,
  265. DWORD dwLineID)
  266. {
  267. MMRESULT mmr;
  268. pmxlc->cbStruct = sizeof(MIXERLINECONTROLS);
  269. pmxlc->dwControlID = dwControlID;
  270. pmxlc->cControls = 1;
  271. pmxlc->cbmxctrl = sizeof(MIXERCONTROL);
  272. pmxlc->pamxctrl = pmxc;
  273. mmr = mixerGetLineControls(hmx
  274. , pmxlc
  275. , MIXER_GETLINECONTROLSF_ONEBYID);
  276. if (mmr != MMSYSERR_NOERROR)
  277. return VCD_SUPPORTF_BADDRIVER;
  278. if (pmxlc->dwLineID != dwLineID)
  279. return VCD_SUPPORTF_BADDRIVER;
  280. return 0L;
  281. }
  282. /*
  283. * IsDestinationMux
  284. *
  285. * Helper function to determine if a source line has a mux on its associated
  286. * destination line
  287. *
  288. * */
  289. BOOL IsDestinationMux(
  290. HMIXEROBJ hmx,
  291. DWORD dwLineID
  292. )
  293. {
  294. MIXERLINE mxl;
  295. MIXERLINECONTROLS mxlc;
  296. MIXERCONTROL mxc;
  297. MMRESULT mmr;
  298. DWORD dwDst;
  299. mxl.cbStruct = sizeof(mxl);
  300. mxl.dwLineID = dwLineID;
  301. // Get the destination number for this line
  302. mmr = mixerGetLineInfo(hmx
  303. , &mxl
  304. , MIXER_GETLINEINFOF_LINEID);
  305. if (mmr != MMSYSERR_NOERROR)
  306. {
  307. return FALSE;
  308. }
  309. //
  310. // Get the LineId for this destination number
  311. //
  312. // mxl.dwDestination will been filled in by the last
  313. // call to mixerGetLineInfo
  314. //
  315. mmr = mixerGetLineInfo(hmx
  316. , &mxl
  317. , MIXER_GETLINEINFOF_DESTINATION);
  318. if (mmr != MMSYSERR_NOERROR)
  319. {
  320. return FALSE;
  321. }
  322. mxlc.cbStruct = sizeof(mxlc);
  323. mxlc.dwLineID = mxl.dwLineID; // use the dwLineId obtained from mixerGetLinInfo
  324. mxlc.dwControlType = MIXERCONTROL_CONTROLTYPE_MUX;
  325. mxlc.cControls = 1;
  326. mxlc.cbmxctrl = sizeof(mxc);
  327. mxlc.pamxctrl = &mxc;
  328. mmr = mixerGetLineControls(hmx
  329. , &mxlc
  330. , MIXER_GETLINECONTROLSF_ONEBYTYPE);
  331. if (mmr == MMSYSERR_NOERROR)
  332. {
  333. return TRUE;
  334. }
  335. return FALSE;
  336. }
  337. /*
  338. * Mixer_InitLineControls
  339. *
  340. * Initialize the mixer api specific part of the volume control description
  341. * mark hidden lines.
  342. * */
  343. static void Mixer_InitLineControls(
  344. HMIXEROBJ hmx,
  345. PVOLCTRLDESC pvcd,
  346. DWORD dwLineID)
  347. {
  348. MIXERLINECONTROLS mxlc;
  349. MIXERCONTROL mxc;
  350. MMRESULT mmr;
  351. int iType;
  352. const DWORD dwAdvTypes[] = {
  353. MIXERCONTROL_CONTROLTYPE_BOOLEAN,
  354. MIXERCONTROL_CONTROLTYPE_ONOFF,
  355. MIXERCONTROL_CONTROLTYPE_MONO,
  356. MIXERCONTROL_CONTROLTYPE_LOUDNESS,
  357. MIXERCONTROL_CONTROLTYPE_STEREOENH,
  358. MIXERCONTROL_CONTROLTYPE_BASS,
  359. MIXERCONTROL_CONTROLTYPE_TREBLE
  360. };
  361. pvcd->dwLineID = dwLineID;
  362. pvcd->dwMeterID = 0;
  363. pvcd->dwVolumeID = 0;
  364. pvcd->dwMuteID = 0;
  365. pvcd->dwMixerID = 0;
  366. pvcd->dwMuxID = 0;
  367. //
  368. // advanced controls
  369. //
  370. for (iType = 0;
  371. iType < SIZEOF(dwAdvTypes);
  372. iType++)
  373. {
  374. mxlc.cbStruct = sizeof(mxlc);
  375. mxlc.dwLineID = dwLineID;
  376. mxlc.dwControlType = dwAdvTypes[iType];
  377. mxlc.cControls = 1;
  378. mxlc.cbmxctrl = sizeof(mxc);
  379. mxlc.pamxctrl = &mxc;
  380. mmr = mixerGetLineControls(hmx
  381. , &mxlc
  382. , MIXER_GETLINECONTROLSF_ONEBYTYPE);
  383. if (mmr == MMSYSERR_NOERROR)
  384. {
  385. pvcd->dwSupport |= VCD_SUPPORTF_MIXER_ADVANCED;
  386. break;
  387. }
  388. }
  389. //
  390. // stock controls
  391. //
  392. //
  393. // peakmeter
  394. //
  395. mxlc.cbStruct = sizeof(mxlc);
  396. mxlc.dwLineID = dwLineID;
  397. mxlc.dwControlType = MIXERCONTROL_CONTROLTYPE_PEAKMETER;
  398. mxlc.cControls = 1;
  399. mxlc.cbmxctrl = sizeof(mxc);
  400. mxlc.pamxctrl = &mxc;
  401. mmr = mixerGetLineControls(hmx
  402. , &mxlc
  403. , MIXER_GETLINECONTROLSF_ONEBYTYPE);
  404. if (mmr == MMSYSERR_NOERROR)
  405. {
  406. pvcd->dwMeterID = mxc.dwControlID;
  407. pvcd->dwSupport |= VCD_SUPPORTF_MIXER_METER;
  408. pvcd->dwSupport |= Mixer_CheckBadDriver(hmx
  409. , &mxlc
  410. , &mxc
  411. , mxc.dwControlID
  412. , dwLineID);
  413. }
  414. //
  415. // mute
  416. //
  417. mxlc.cbStruct = sizeof(mxlc);
  418. mxlc.dwLineID = dwLineID;
  419. mxlc.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
  420. mxlc.cControls = 1;
  421. mxlc.cbmxctrl = sizeof(mxc);
  422. mxlc.pamxctrl = &mxc;
  423. mmr = mixerGetLineControls(hmx
  424. , &mxlc
  425. , MIXER_GETLINECONTROLSF_ONEBYTYPE);
  426. if (mmr == MMSYSERR_NOERROR)
  427. {
  428. pvcd->fdwMuteControl = mxc.fdwControl;
  429. pvcd->dwMuteID = mxc.dwControlID;
  430. pvcd->dwSupport |= VCD_SUPPORTF_MIXER_MUTE;
  431. pvcd->dwVisible |= VCD_VISIBLEF_MIXER_MUTE;
  432. pvcd->dwSupport |= Mixer_CheckBadDriver(hmx
  433. , &mxlc
  434. , &mxc
  435. , mxc.dwControlID
  436. , dwLineID);
  437. }
  438. //
  439. // volume
  440. //
  441. mxlc.cbStruct = sizeof(mxlc);
  442. mxlc.dwLineID = dwLineID;
  443. mxlc.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
  444. mxlc.cControls = 1;
  445. mxlc.cbmxctrl = sizeof(mxc);
  446. mxlc.pamxctrl = &mxc;
  447. mmr = mixerGetLineControls(hmx
  448. , &mxlc
  449. , MIXER_GETLINECONTROLSF_ONEBYTYPE);
  450. if (mmr == MMSYSERR_NOERROR)
  451. {
  452. pvcd->fdwVolumeControl = mxc.fdwControl;
  453. pvcd->dwVolumeID = mxc.dwControlID;
  454. pvcd->dwSupport |= VCD_SUPPORTF_MIXER_VOLUME;
  455. pvcd->dwSupport |= Mixer_CheckBadDriver(hmx
  456. , &mxlc
  457. , &mxc
  458. , mxc.dwControlID
  459. , dwLineID);
  460. }
  461. //
  462. // mixer
  463. //
  464. mxlc.cbStruct = sizeof(mxlc);
  465. mxlc.dwLineID = dwLineID;
  466. mxlc.dwControlType = MIXERCONTROL_CONTROLTYPE_MIXER;
  467. mxlc.cControls = 1;
  468. mxlc.cbmxctrl = sizeof(mxc);
  469. mxlc.pamxctrl = &mxc;
  470. mmr = mixerGetLineControls(hmx
  471. , &mxlc
  472. , MIXER_GETLINECONTROLSF_ONEBYTYPE);
  473. if (mmr == MMSYSERR_NOERROR)
  474. {
  475. pvcd->dwMixerID = mxc.dwControlID;
  476. pvcd->cMixer = mxc.cMultipleItems;
  477. pvcd->dwSupport |= VCD_SUPPORTF_MIXER_MIXER;
  478. pvcd->dwSupport |= Mixer_CheckBadDriver(hmx
  479. , &mxlc
  480. , &mxc
  481. , mxc.dwControlID
  482. , dwLineID);
  483. }
  484. //
  485. // mux
  486. //
  487. mxlc.cbStruct = sizeof(mxlc);
  488. mxlc.dwLineID = dwLineID;
  489. mxlc.dwControlType = MIXERCONTROL_CONTROLTYPE_MUX;
  490. mxlc.cControls = 1;
  491. mxlc.cbmxctrl = sizeof(mxc);
  492. mxlc.pamxctrl = &mxc;
  493. mmr = mixerGetLineControls(hmx
  494. , &mxlc
  495. , MIXER_GETLINECONTROLSF_ONEBYTYPE);
  496. if (mmr == MMSYSERR_NOERROR)
  497. {
  498. pvcd->dwMuxID = mxc.dwControlID;
  499. pvcd->cMux = mxc.cMultipleItems;
  500. pvcd->dwSupport |= VCD_SUPPORTF_MIXER_MUX;
  501. pvcd->dwSupport |= Mixer_CheckBadDriver(hmx
  502. , &mxlc
  503. , &mxc
  504. , mxc.dwControlID
  505. , dwLineID);
  506. }
  507. if (!(pvcd->dwSupport & ( VCD_SUPPORTF_MIXER_MUTE
  508. | VCD_SUPPORTF_MIXER_METER
  509. | VCD_SUPPORTF_MIXER_VOLUME)))
  510. {
  511. if (IsDestinationMux(hmx, dwLineID) &&
  512. !(pvcd->dwSupport & VCD_SUPPORTF_MIXER_MUX))
  513. {
  514. //
  515. // Visible, and not hidden
  516. //
  517. pvcd->dwSupport |= VCD_SUPPORTF_VISIBLE;
  518. pvcd->dwSupport &= ~VCD_SUPPORTF_DEFAULT;
  519. }
  520. else
  521. {
  522. //
  523. // make it invisible in the UI.
  524. //
  525. pvcd->dwSupport |= VCD_SUPPORTF_HIDDEN;
  526. }
  527. }
  528. else
  529. {
  530. //
  531. // Visible, and not hidden
  532. //
  533. pvcd->dwSupport |= VCD_SUPPORTF_VISIBLE;
  534. }
  535. }
  536. /*
  537. * Mixer_CreateVolumeDescription
  538. *
  539. * */
  540. PVOLCTRLDESC Mixer_CreateVolumeDescription(
  541. HMIXEROBJ hmx,
  542. int iDest,
  543. DWORD* pcvd )
  544. {
  545. MMRESULT mmr;
  546. PVOLCTRLDESC pvcdPrev = NULL, pvcd = NULL;
  547. MIXERLINE mlDst;
  548. DWORD cLines = 0;
  549. DWORD dwSupport = 0L;
  550. UINT iSrc;
  551. int newDest=0;
  552. ZeroMemory(&mlDst, sizeof(mlDst));
  553. mlDst.cbStruct = sizeof(mlDst);
  554. mlDst.dwDestination = iDest;
  555. mmr = mixerGetLineInfo(hmx
  556. , &mlDst
  557. , MIXER_GETLINEINFOF_DESTINATION);
  558. if(!mlDst.cConnections)
  559. {
  560. //No lines to list. Try with a different mixer ID.
  561. GetDestination(0, &newDest);
  562. mlDst.dwDestination = newDest;
  563. mmr = mixerGetLineInfo(hmx
  564. , &mlDst
  565. , MIXER_GETLINEINFOF_DESTINATION);
  566. //Even if we do not get any connections here lets continue. Nothing more we can do.
  567. //This will be taken care of before opening the dialog.
  568. }
  569. if (mmr == MMSYSERR_NOERROR)
  570. {
  571. if (mlDst.cChannels == 1L)
  572. dwSupport |= VCD_SUPPORTF_MONO;
  573. if (mlDst.fdwLine & MIXERLINE_LINEF_DISCONNECTED)
  574. dwSupport |= VCD_SUPPORTF_DISABLED;
  575. //
  576. // a default type
  577. //
  578. dwSupport |= VCD_SUPPORTF_DEFAULT;
  579. }
  580. else
  581. {
  582. //
  583. // we need to add it anyway s.t. a UI comes up
  584. //
  585. dwSupport = VCD_SUPPORTF_DISABLED;
  586. }
  587. pvcd = PVCD_AddLine(NULL
  588. , iDest
  589. , VCD_TYPE_MIXER
  590. , mlDst.szShortName
  591. , mlDst.szName
  592. , dwSupport
  593. , &cLines );
  594. if (!pvcd)
  595. return NULL;
  596. Mixer_InitLineControls( hmx, pvcd, mlDst.dwLineID );
  597. pvcdPrev = pvcd;
  598. for (iSrc = 0; iSrc < mlDst.cConnections; iSrc++)
  599. {
  600. MIXERLINE mlSrc;
  601. mlSrc.cbStruct = sizeof(mlSrc);
  602. mlSrc.dwDestination = iDest;
  603. mlSrc.dwSource = iSrc;
  604. mmr = mixerGetLineInfo(hmx
  605. , &mlSrc
  606. , MIXER_GETLINEINFOF_SOURCE);
  607. dwSupport = 0L;
  608. if (mmr == MMSYSERR_NOERROR)
  609. {
  610. if (mlSrc.cChannels == 1L)
  611. {
  612. dwSupport |= VCD_SUPPORTF_MONO;
  613. }
  614. if (mlSrc.fdwLine & MIXERLINE_LINEF_DISCONNECTED)
  615. dwSupport |= VCD_SUPPORTF_DISABLED;
  616. //
  617. // Mark these types as "default" just to lessen the shock on
  618. // some advanced sound cards.
  619. //
  620. if (mlDst.dwComponentType == MIXERLINE_COMPONENTTYPE_DST_SPEAKERS
  621. || mlDst.dwComponentType == MIXERLINE_COMPONENTTYPE_DST_HEADPHONES)
  622. {
  623. switch (mlSrc.dwComponentType)
  624. {
  625. case MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT:
  626. case MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC:
  627. case MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER:
  628. case MIXERLINE_COMPONENTTYPE_SRC_LINE:
  629. dwSupport |= VCD_SUPPORTF_DEFAULT;
  630. break;
  631. }
  632. }
  633. else if (mlDst.dwComponentType == MIXERLINE_COMPONENTTYPE_DST_WAVEIN
  634. || mlDst.dwComponentType == MIXERLINE_COMPONENTTYPE_DST_VOICEIN)
  635. {
  636. switch (mlSrc.dwComponentType)
  637. {
  638. case MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE:
  639. case MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC:
  640. case MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER:
  641. case MIXERLINE_COMPONENTTYPE_SRC_LINE:
  642. case MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED:
  643. dwSupport |= VCD_SUPPORTF_DEFAULT;
  644. break;
  645. }
  646. }
  647. }
  648. else
  649. {
  650. //
  651. // we need to add it anyway s.t. lookups aren't under counted
  652. //
  653. dwSupport = VCD_SUPPORTF_DISABLED;
  654. }
  655. pvcd = PVCD_AddLine(pvcdPrev
  656. , iDest
  657. , VCD_TYPE_MIXER
  658. , mlSrc.szShortName
  659. , mlSrc.szName
  660. , dwSupport
  661. , &cLines );
  662. if (pvcd)
  663. {
  664. Mixer_InitLineControls( hmx, &pvcd[cLines-1], mlSrc.dwLineID );
  665. pvcdPrev = pvcd;
  666. }
  667. }
  668. //
  669. // Fixup dependencies
  670. //
  671. Mixer_SetLines(hmx, pvcdPrev, cLines);
  672. *pcvd = cLines;
  673. return pvcdPrev;
  674. }
  675. /*
  676. * Mixer_IsValidRecordingDestination
  677. *
  678. * */
  679. BOOL Mixer_IsValidRecordingDestination (HMIXEROBJ hmx, MIXERLINE* pmlDst)
  680. {
  681. BOOL fReturn = FALSE;
  682. if (pmlDst && MIXERLINE_COMPONENTTYPE_DST_WAVEIN == pmlDst -> dwComponentType)
  683. {
  684. UINT uiSrc;
  685. MIXERLINE mlSrc;
  686. for (uiSrc = 0; uiSrc < pmlDst -> cConnections; uiSrc++)
  687. {
  688. mlSrc.cbStruct = sizeof (mlSrc);
  689. mlSrc.dwDestination = pmlDst -> dwDestination;
  690. mlSrc.dwSource = uiSrc;
  691. if (SUCCEEDED (mixerGetLineInfo (hmx, &mlSrc, MIXER_GETLINEINFOF_SOURCE)))
  692. {
  693. switch (mlSrc.dwComponentType)
  694. {
  695. case MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE:
  696. case MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC:
  697. case MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER:
  698. case MIXERLINE_COMPONENTTYPE_SRC_LINE:
  699. case MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED:
  700. fReturn = TRUE;
  701. break;
  702. }
  703. }
  704. }
  705. }
  706. return fReturn;
  707. }
  708. /*
  709. * Mixer_CleanupVolumeDescription
  710. *
  711. * */
  712. void Mixer_CleanupVolumeDescription(
  713. PVOLCTRLDESC avcd,
  714. DWORD cvcd)
  715. {
  716. if (cvcd == 0)
  717. return;
  718. if (avcd[0].pdblCacheMix)
  719. {
  720. GlobalFreePtr(avcd[0].pdblCacheMix);
  721. }
  722. if (avcd[0].dwSupport & VCD_SUPPORTF_MIXER_MIXER)
  723. {
  724. if (avcd[0].amcd_bMixer)
  725. GlobalFreePtr(avcd[0].amcd_bMixer);
  726. }
  727. if (avcd[0].dwSupport & VCD_SUPPORTF_MIXER_MUX)
  728. {
  729. if (avcd[0].amcd_bMux)
  730. GlobalFreePtr(avcd[0].amcd_bMux);
  731. }
  732. }
  733. /*****************************************************************************
  734. *
  735. * ACTIVE GET/SET CODE
  736. *
  737. *****************************************************************************/
  738. static
  739. MMRESULT
  740. Mixer_GetMixerLineInfo(
  741. HMIXEROBJ hmx, // handle to mixer
  742. LPMIXERLINE pml, // Returns destination info
  743. DWORD dwLineID //
  744. )
  745. {
  746. if (!pml || !hmx)
  747. return MMSYSERR_INVALPARAM;
  748. // Get mixerline info
  749. ZeroMemory( pml, sizeof(*pml) );
  750. pml->cbStruct = sizeof(*pml);
  751. pml->dwLineID = dwLineID;
  752. return (mixerGetLineInfo (hmx, pml, MIXER_GETLINEINFOF_LINEID));
  753. }
  754. /*
  755. * Mixer_GetMixerVolume
  756. *
  757. * */
  758. static MMRESULT Mixer_GetMixerVolume(
  759. PMIXUIDIALOG pmxud, // app instance
  760. PVOLCTRLDESC pvcd, // volume to change
  761. MIXERCONTROLDETAILS_UNSIGNED* pmcdVolume, // array for volume levels
  762. LPDWORD lpSize // size of array (or return size needed)
  763. )
  764. {
  765. MMRESULT mmr;
  766. MIXERLINE ml;
  767. MIXERCONTROLDETAILS mxcd;
  768. DWORD cChannels;
  769. if (!lpSize || !pmxud)
  770. return MMSYSERR_INVALPARAM;
  771. // Get mixerline info
  772. if (pvcd->fdwVolumeControl & MIXERCONTROL_CONTROLF_UNIFORM)
  773. {
  774. cChannels = 1;
  775. }
  776. else
  777. {
  778. mmr = Mixer_GetMixerLineInfo ((HMIXEROBJ)(pmxud->hmx), &ml, pvcd->dwLineID);
  779. if (MMSYSERR_NOERROR != mmr)
  780. {
  781. return mmr;
  782. }
  783. cChannels = ml.cChannels;
  784. }
  785. if (!pmcdVolume)
  786. {
  787. // Just return size needed
  788. *lpSize = cChannels * sizeof (MIXERCONTROLDETAILS_UNSIGNED);
  789. return MMSYSERR_NOERROR;
  790. }
  791. else
  792. {
  793. // Verify passed array size
  794. if (*lpSize < cChannels * sizeof (MIXERCONTROLDETAILS_UNSIGNED))
  795. return MMSYSERR_INVALPARAM;
  796. }
  797. // Get volume levels
  798. ZeroMemory (&mxcd, sizeof (mxcd));
  799. mxcd.cbStruct = sizeof(mxcd);
  800. mxcd.dwControlID = pvcd->dwVolumeID;
  801. mxcd.cChannels = cChannels;
  802. mxcd.cbDetails = sizeof (MIXERCONTROLDETAILS_UNSIGNED);
  803. mxcd.paDetails = (LPVOID) pmcdVolume;
  804. mmr = mixerGetControlDetails((HMIXEROBJ)(pmxud->hmx),
  805. &mxcd,
  806. MIXER_OBJECTF_HANDLE | MIXER_GETCONTROLDETAILSF_VALUE);
  807. return mmr;
  808. }
  809. static MMRESULT Mixer_Mute(
  810. HMIXEROBJ hmx,
  811. PVOLCTRLDESC pvcd,
  812. PMIXERCONTROLDETAILS pmxcd,
  813. DWORD fMute)
  814. {
  815. MIXERLINE ml;
  816. DWORD cChannels;
  817. DWORD dwSize;
  818. LPDWORD lpdwCurrent;
  819. UINT uiIndx;
  820. MMRESULT mmr;
  821. // Check the parameters
  822. if (!hmx || !pvcd || !pmxcd)
  823. return MMSYSERR_INVALPARAM;
  824. // Get mixerline info
  825. if (pvcd->fdwMuteControl & MIXERCONTROL_CONTROLF_UNIFORM)
  826. {
  827. cChannels = 1;
  828. }
  829. else
  830. {
  831. mmr = Mixer_GetMixerLineInfo(hmx, &ml, pvcd->dwLineID);
  832. if (MMSYSERR_NOERROR != mmr)
  833. {
  834. return mmr;
  835. }
  836. cChannels = ml.cChannels;
  837. }
  838. dwSize = (DWORD)(cChannels * sizeof(DWORD));
  839. pmxcd->paDetails = LocalAlloc (LPTR, dwSize);
  840. if (!pmxcd->paDetails)
  841. return MMSYSERR_NOMEM;
  842. for (uiIndx = 0; uiIndx < cChannels; uiIndx++)
  843. {
  844. lpdwCurrent = ((LPDWORD)pmxcd->paDetails + uiIndx);
  845. *lpdwCurrent = fMute;
  846. }
  847. pmxcd->cbStruct = sizeof(*pmxcd);
  848. pmxcd->dwControlID = pvcd->dwMuteID ;
  849. pmxcd->cChannels = cChannels;
  850. pmxcd->cMultipleItems = 0;
  851. pmxcd->cbDetails = sizeof(DWORD);
  852. mmr = mixerSetControlDetails(hmx
  853. , pmxcd
  854. , MIXER_SETCONTROLDETAILSF_VALUE);
  855. LocalFree (pmxcd->paDetails);
  856. return mmr;
  857. }
  858. /*
  859. * Mixer_GetControl
  860. *
  861. * Change a UI control in response to a device or initialization event
  862. *
  863. * */
  864. void Mixer_GetControl(
  865. PMIXUIDIALOG pmxud,
  866. HWND hctl,
  867. int imxul,
  868. int itype)
  869. {
  870. PMIXUILINE pmxul = &pmxud->amxul[imxul];
  871. PVOLCTRLDESC pvcd = pmxul->pvcd;
  872. DWORD dwID = 0L;
  873. BOOL fSet = FALSE;
  874. switch (itype)
  875. {
  876. case MIXUI_VUMETER:
  877. fSet = (pmxul->pvcd->dwSupport & VCD_SUPPORTF_MIXER_METER);
  878. if (fSet)
  879. dwID = pmxul->pvcd->dwMeterID;
  880. break;
  881. case MIXUI_SWITCH:
  882. fSet = (pmxul->pvcd->dwSupport & VCD_SUPPORTF_MIXER_MUTE)
  883. && (pmxul->pvcd->dwVisible & VCD_VISIBLEF_MIXER_MUTE);
  884. if (fSet)
  885. {
  886. dwID = pmxul->pvcd->dwMuteID;
  887. break;
  888. }
  889. fSet = (pmxul->pvcd->dwSupport & VCD_SUPPORTF_MIXER_MUX)
  890. && (pmxul->pvcd->dwVisible & VCD_VISIBLEF_MIXER_MUX);
  891. if (fSet)
  892. {
  893. dwID = pmxul->pvcd->dwMuxID;
  894. break;
  895. }
  896. fSet = (pmxul->pvcd->dwSupport & VCD_SUPPORTF_MIXER_MIXER)
  897. && (pmxul->pvcd->dwVisible & VCD_VISIBLEF_MIXER_MIXER);
  898. if (fSet)
  899. {
  900. dwID = pmxul->pvcd->dwMixerID;
  901. break;
  902. }
  903. break;
  904. case MIXUI_VOLUME:
  905. case MIXUI_BALANCE:
  906. fSet = (pmxul->pvcd->dwSupport & VCD_SUPPORTF_MIXER_VOLUME);
  907. if (fSet)
  908. dwID = pmxul->pvcd->dwVolumeID;
  909. break;
  910. default:
  911. return;
  912. }
  913. if (fSet)
  914. Mixer_GetControlFromID(pmxud, dwID);
  915. }
  916. /*
  917. * Mixer_SetVolume
  918. *
  919. * - Change a mixerControl in response to a user event
  920. * */
  921. MMRESULT Mixer_SetVolume (
  922. PMIXUIDIALOG pmxud, // app instance
  923. PVOLCTRLDESC pvcd, // volume to change
  924. DWORD dwVolume, // volume value VOLUME_MAX to VOLUME_MIN
  925. LPDWORD lpdwBalance // Balance desired (NULL == No Balance) 0 to 64
  926. )
  927. {
  928. MIXERLINE ml;
  929. DWORD cChannels;
  930. DWORD dwSize;
  931. MIXERCONTROLDETAILS_UNSIGNED* pmcdVolume;
  932. MMRESULT mmr;
  933. // Check the parameters
  934. if ( !pvcd || !pmxud || (dwVolume > VOLUME_MAX) )
  935. return MMSYSERR_INVALPARAM;
  936. // Find needed buffer size for volumes
  937. if (pvcd->fdwVolumeControl & MIXERCONTROL_CONTROLF_UNIFORM)
  938. {
  939. cChannels = 1;
  940. }
  941. else
  942. {
  943. mmr = Mixer_GetMixerLineInfo ((HMIXEROBJ)(pmxud->hmx), &ml, pvcd->dwLineID);
  944. if (MMSYSERR_NOERROR != mmr)
  945. {
  946. return mmr;
  947. }
  948. cChannels = ml.cChannels;
  949. }
  950. dwSize = (DWORD)(cChannels * sizeof (MIXERCONTROLDETAILS_UNSIGNED));
  951. // Create volume buffer
  952. pmcdVolume = LocalAlloc (LPTR, dwSize);
  953. if (!pmcdVolume)
  954. return MMSYSERR_NOMEM;
  955. // Note: From here on, do not return without releasing 'pmcdVolume'.
  956. mmr = Mixer_GetMixerVolume (pmxud, pvcd, pmcdVolume, &dwSize);
  957. if (MMSYSERR_NOERROR == mmr)
  958. {
  959. MIXERCONTROLDETAILS mcd;
  960. ZeroMemory (&mcd, sizeof (mcd));
  961. // Create volume mix cache if necessary
  962. // if we have no cache we make one of course
  963. // other wise we first check that not all the volumes of the channels are equal to zero
  964. if (!pvcd->pdblCacheMix || !Mixer_AreChannelsAtMinimum(pmcdVolume,cChannels))
  965. {
  966. Mixer_RefreshMixCache (pvcd, pmcdVolume, cChannels);
  967. }
  968. // Create volume buffer for new values
  969. mcd.paDetails = LocalAlloc (LPTR, dwSize);
  970. if (!mcd.paDetails || !pvcd->pdblCacheMix)
  971. mmr = MMSYSERR_NOMEM;
  972. // Caculate the new volume & balance
  973. if (MMSYSERR_NOERROR == mmr)
  974. {
  975. UINT uiIndx;
  976. MIXERCONTROLDETAILS_UNSIGNED* pmcdCurrent;
  977. // Account for Balance (only for Stereo)
  978. if ( lpdwBalance && (cChannels == 2) && (*lpdwBalance <= 64) )
  979. {
  980. MIXERCONTROLDETAILS_UNSIGNED* pmcdLeft = ((MIXERCONTROLDETAILS_UNSIGNED*)mcd.paDetails);
  981. MIXERCONTROLDETAILS_UNSIGNED* pmcdRight = ((MIXERCONTROLDETAILS_UNSIGNED*)mcd.paDetails + 1);
  982. long lBalance = *lpdwBalance;
  983. lBalance -= 32; // -32 to 32 range
  984. // Caculate volume based on balance and refresh mix cache
  985. if (lBalance > 0) // Balance Right
  986. {
  987. // Left
  988. if (lBalance == 32) // Pegged Right
  989. pmcdLeft -> dwValue = 0;
  990. else
  991. pmcdLeft -> dwValue = dwVolume - (lBalance * (dwVolume - VOLUME_MIN))/32;
  992. // Right
  993. pmcdRight -> dwValue = dwVolume;
  994. }
  995. if (lBalance < 0) // Balance Left
  996. {
  997. // Left
  998. pmcdLeft -> dwValue = dwVolume;
  999. // Right
  1000. if (lBalance == -32) // Pegged Left
  1001. pmcdRight -> dwValue = 0;
  1002. else
  1003. pmcdRight -> dwValue = dwVolume - (-lBalance * (dwVolume - VOLUME_MIN))/32;
  1004. }
  1005. if (lBalance == 0) // Balance Centered
  1006. {
  1007. // Left
  1008. pmcdLeft -> dwValue = dwVolume;
  1009. // Right
  1010. pmcdRight -> dwValue = dwVolume;
  1011. }
  1012. Mixer_RefreshMixCache (pvcd, mcd.paDetails, cChannels);
  1013. }
  1014. else
  1015. {
  1016. // Caculate the new volume level for each of the channels. For volume levels
  1017. // at the current max, we simply set the newly requested level (in this case
  1018. // the cache value is 1.0). For those less than the max, we set a value that
  1019. // is a percentage of the max. This maintains the relative distance of the
  1020. // channel levels from each other.
  1021. for (uiIndx = 0; uiIndx < cChannels; uiIndx++)
  1022. {
  1023. pmcdCurrent = ((MIXERCONTROLDETAILS_UNSIGNED*)mcd.paDetails + uiIndx);
  1024. // The 0.5f forces rounding (instead of truncation)
  1025. pmcdCurrent -> dwValue = (DWORD)(*(pvcd->pdblCacheMix + uiIndx) * (double) dwVolume + 0.5f);
  1026. }
  1027. }
  1028. mcd.cbStruct = sizeof (mcd);
  1029. mcd.dwControlID = pvcd -> dwVolumeID;
  1030. mcd.cChannels = cChannels;
  1031. mcd.cbDetails = sizeof (MIXERCONTROLDETAILS_UNSIGNED);
  1032. // seems like it would be sizeof(mcd.paDetails),
  1033. // but actually, it is the size of a single value
  1034. // and is multiplied by channel in the driver.
  1035. // Apply new value only if it is different. This prevents unessary calls to
  1036. // mixerSetControlDetails() when we are pegged.
  1037. if (memcmp (pmcdVolume, mcd.paDetails, dwSize))
  1038. {
  1039. mixerSetControlDetails ((HMIXEROBJ)(pmxud->hmx), &mcd, MIXER_SETCONTROLDETAILSF_VALUE);
  1040. }
  1041. }
  1042. // Free new volume array
  1043. if (mcd.paDetails)
  1044. LocalFree (mcd.paDetails);
  1045. }
  1046. // Free volume array
  1047. LocalFree (pmcdVolume);
  1048. return mmr;
  1049. }
  1050. /*
  1051. * Mixer_GetControlFromID
  1052. *
  1053. * */
  1054. void Mixer_GetControlFromID(
  1055. PMIXUIDIALOG pmxud,
  1056. DWORD dwControlID)
  1057. {
  1058. MIXERLINE mxl;
  1059. MIXERLINECONTROLS mxlc;
  1060. MIXERCONTROL mxc;
  1061. MIXERCONTROLDETAILS mxcd;
  1062. PMIXUILINE pmxul;
  1063. PMIXUICTRL pmxuc;
  1064. PVOLCTRLDESC pvcd;
  1065. DWORD ivcd;
  1066. BOOL fBarf = FALSE;
  1067. MMRESULT mmr;
  1068. //
  1069. // Retrieve the control information
  1070. //
  1071. mxlc.cbStruct = sizeof(mxlc);
  1072. mxlc.dwControlID = dwControlID;
  1073. mxlc.cControls = 1;
  1074. mxlc.cbmxctrl = sizeof(mxc);
  1075. mxlc.pamxctrl = &mxc;
  1076. mmr = mixerGetLineControls((HMIXEROBJ)(pmxud->hmx)
  1077. , &mxlc
  1078. , MIXER_GETLINECONTROLSF_ONEBYID);
  1079. if (mmr != MMSYSERR_NOERROR)
  1080. return;
  1081. if (!(pmxud->dwFlags & MXUD_FLAGSF_BADDRIVER))
  1082. {
  1083. //
  1084. // The *correct* code for this lookup using the mixer API.
  1085. //
  1086. // Is this our current destination line?
  1087. //
  1088. mxl.cbStruct = sizeof(mxl);
  1089. mxl.dwLineID = mxlc.dwLineID;
  1090. mmr = mixerGetLineInfo((HMIXEROBJ)(pmxud->hmx)
  1091. , &mxl
  1092. , MIXER_GETLINEINFOF_LINEID);
  1093. if (mmr != MMSYSERR_NOERROR)
  1094. return;
  1095. if (mxl.dwDestination != pmxud->iDest)
  1096. return;
  1097. //
  1098. // Is this a source line or a destination line?
  1099. //
  1100. ivcd = (mxl.fdwLine & MIXERLINE_LINEF_SOURCE)? 1 + mxl.dwSource : 0;
  1101. pvcd = &pmxud->avcd[ivcd];
  1102. //
  1103. // a bad driver was detected!
  1104. //
  1105. if (pvcd->dwLineID != mxlc.dwLineID)
  1106. {
  1107. pmxud->dwFlags |= MXUD_FLAGSF_BADDRIVER;
  1108. }
  1109. }
  1110. if (pmxud->dwFlags & MXUD_FLAGSF_BADDRIVER)
  1111. {
  1112. PVOLCTRLDESC pvcdTmp;
  1113. //
  1114. // take evasive action if this was a bad driver by doing a brute force
  1115. // search.
  1116. //
  1117. pvcd = NULL;
  1118. for (ivcd = 0; ivcd < pmxud->cvcd; ivcd ++)
  1119. {
  1120. pvcdTmp = &pmxud->avcd[ivcd];
  1121. if ( ( (pvcdTmp->dwSupport & VCD_SUPPORTF_MIXER_VOLUME)
  1122. && pvcdTmp->dwVolumeID == dwControlID )
  1123. || ( (pvcdTmp->dwSupport & VCD_SUPPORTF_MIXER_MUTE)
  1124. && pvcdTmp->dwMuteID == dwControlID )
  1125. || ( (pvcdTmp->dwSupport & VCD_SUPPORTF_MIXER_MIXER)
  1126. && pvcdTmp->dwMixerID == dwControlID )
  1127. || ( (pvcdTmp->dwSupport & VCD_SUPPORTF_MIXER_MUX)
  1128. && pvcdTmp->dwMuxID == dwControlID )
  1129. || ( (pvcdTmp->dwSupport & VCD_SUPPORTF_MIXER_METER)
  1130. && pvcdTmp->dwMeterID == dwControlID ) )
  1131. {
  1132. pvcd = pvcdTmp;
  1133. break;
  1134. }
  1135. }
  1136. if (pvcd == NULL)
  1137. return;
  1138. }
  1139. pmxul = pvcd->pmxul;
  1140. //
  1141. // Go through our visible lines to determine if this control affects
  1142. // any visible control and change them.
  1143. //
  1144. switch (mxc.dwControlType)
  1145. {
  1146. case MIXERCONTROL_CONTROLTYPE_VOLUME:
  1147. {
  1148. MIXERCONTROLDETAILS_UNSIGNED* pmcdVolume;
  1149. DWORD cChannels;
  1150. DWORD dwSize;
  1151. MIXERLINE ml;
  1152. //
  1153. // A nonvisible line should be shunned
  1154. //
  1155. if (pmxul == NULL)
  1156. return;
  1157. // Find needed buffer size for volumes
  1158. if (pvcd->fdwVolumeControl & MIXERCONTROL_CONTROLF_UNIFORM)
  1159. {
  1160. cChannels = 1;
  1161. }
  1162. else
  1163. {
  1164. mmr = Mixer_GetMixerLineInfo ((HMIXEROBJ)(pmxud->hmx), &ml, pvcd->dwLineID);
  1165. if (MMSYSERR_NOERROR != mmr)
  1166. {
  1167. return;
  1168. }
  1169. cChannels = ml.cChannels;
  1170. }
  1171. dwSize = (DWORD)(cChannels * sizeof (MIXERCONTROLDETAILS_UNSIGNED));
  1172. // Create volume buffer
  1173. pmcdVolume = LocalAlloc (LPTR, dwSize);
  1174. if (!pmcdVolume)
  1175. return;
  1176. // Note : Do not return without releasing 'pmcdVolume'.
  1177. if (Mixer_GetMixerVolume (pmxud, pvcd, pmcdVolume, &dwSize)
  1178. == MMSYSERR_NOERROR)
  1179. {
  1180. UINT uindx;
  1181. DWORD dwVolume;
  1182. DWORD dwMax = 0;
  1183. // Set Volume slider
  1184. for (uindx = 0; uindx < cChannels; uindx++)
  1185. dwMax = max (dwMax, (pmcdVolume + uindx) -> dwValue);
  1186. dwVolume = VOLUME_TO_SLIDER(dwMax);
  1187. dwVolume = VOLUME_TICS - dwVolume;
  1188. pmxuc = &pmxul->acr[MIXUI_VOLUME];
  1189. if (pmxuc->state)
  1190. {
  1191. SendMessage(pmxuc->hwnd, TBM_SETPOS, TRUE, dwVolume);
  1192. }
  1193. // Set Balance Slider
  1194. pmxuc = &pmxul->acr[MIXUI_BALANCE];
  1195. if (dwVolume < VOLUME_TICS && pmxuc->state && 2 >= cChannels)
  1196. {
  1197. long lBalance;
  1198. double dblBalance;
  1199. if (1 >= cChannels)
  1200. lBalance = 0;
  1201. else
  1202. {
  1203. // Stereo
  1204. dblBalance = (double)(32 * (long)(pmcdVolume -> dwValue - (pmcdVolume + 1) -> dwValue))
  1205. / (double)(dwMax - VOLUME_MIN);
  1206. lBalance = (long)((32.0F - dblBalance) + 0.5F); // 0.5 forces rounding
  1207. }
  1208. SendMessage(pmxuc->hwnd, TBM_SETPOS, TRUE, lBalance);
  1209. }
  1210. }
  1211. LocalFree (pmcdVolume);
  1212. break;
  1213. }
  1214. case MIXERCONTROL_CONTROLTYPE_MIXER:
  1215. {
  1216. DWORD i;
  1217. mxcd.cbStruct = sizeof(mxcd);
  1218. mxcd.dwControlID = pvcd->dwMixerID ;
  1219. mxcd.cChannels = 1;
  1220. mxcd.cMultipleItems = pvcd->cMixer;
  1221. mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
  1222. mxcd.paDetails = (LPVOID)pvcd->amcd_bMixer;
  1223. mmr = mixerGetControlDetails((HMIXEROBJ)(pmxud->hmx)
  1224. , &mxcd
  1225. , MIXER_GETCONTROLDETAILSF_VALUE);
  1226. if (mmr == MMSYSERR_NOERROR)
  1227. {
  1228. for (i = 0; i < pmxud->cvcd; i++)
  1229. {
  1230. pvcd = &pmxud->avcd[i];
  1231. if ( (pvcd->dwSupport & VCD_SUPPORTF_MIXER_MIXER)
  1232. && (pvcd->dwVisible & VCD_VISIBLEF_MIXER_MIXER)
  1233. && pvcd->pmxul)
  1234. {
  1235. pmxuc = &pvcd->pmxul->acr[MIXUI_SWITCH];
  1236. if (pmxuc->state == MIXUI_CONTROL_INITIALIZED)
  1237. {
  1238. SendMessage(pmxuc->hwnd
  1239. , BM_SETCHECK
  1240. , pvcd->amcd_bMixer[pvcd->iMixer].fValue, 0);
  1241. }
  1242. }
  1243. }
  1244. }
  1245. break;
  1246. }
  1247. case MIXERCONTROL_CONTROLTYPE_MUX:
  1248. {
  1249. DWORD i;
  1250. mxcd.cbStruct = sizeof(mxcd);
  1251. mxcd.dwControlID = pvcd->dwMuxID ;
  1252. mxcd.cChannels = 1;
  1253. mxcd.cMultipleItems = pvcd->cMux;
  1254. mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
  1255. mxcd.paDetails = (LPVOID)pvcd->amcd_bMux;
  1256. mmr = mixerGetControlDetails((HMIXEROBJ)(pmxud->hmx)
  1257. , &mxcd
  1258. , MIXER_GETCONTROLDETAILSF_VALUE);
  1259. if (mmr == MMSYSERR_NOERROR)
  1260. {
  1261. for (i = 0; i < pmxud->cvcd; i++)
  1262. {
  1263. pvcd = &pmxud->avcd[i];
  1264. if ( (pvcd->dwSupport & VCD_SUPPORTF_MIXER_MUX)
  1265. && (pvcd->dwVisible & VCD_VISIBLEF_MIXER_MUX)
  1266. && pvcd->pmxul)
  1267. {
  1268. pmxuc = &pvcd->pmxul->acr[MIXUI_SWITCH];
  1269. if (pmxuc->state == MIXUI_CONTROL_INITIALIZED)
  1270. SendMessage(pmxuc->hwnd
  1271. , BM_SETCHECK
  1272. , pvcd->amcd_bMux[pvcd->iMux].fValue, 0);
  1273. }
  1274. }
  1275. }
  1276. break;
  1277. }
  1278. case MIXERCONTROL_CONTROLTYPE_MUTE:
  1279. {
  1280. DWORD fChecked;
  1281. //
  1282. // A nonvisible line should be shunned
  1283. //
  1284. if (pmxul == NULL)
  1285. return;
  1286. if (! (pvcd->dwSupport & VCD_SUPPORTF_MIXER_MUTE
  1287. && pvcd->dwVisible & VCD_VISIBLEF_MIXER_MUTE))
  1288. return;
  1289. pmxuc = &pmxul->acr[MIXUI_SWITCH];
  1290. if (pmxuc->state != MIXUI_CONTROL_INITIALIZED)
  1291. break;
  1292. mxcd.cbStruct = sizeof(mxcd);
  1293. mxcd.dwControlID = pvcd->dwMuteID;
  1294. mxcd.cChannels = 1;
  1295. mxcd.cMultipleItems = 0;
  1296. mxcd.cbDetails = sizeof(DWORD);
  1297. mxcd.paDetails = (LPVOID)&fChecked;
  1298. mmr = mixerGetControlDetails((HMIXEROBJ)(pmxud->hmx)
  1299. , &mxcd
  1300. , MIXER_GETCONTROLDETAILSF_VALUE);
  1301. if (mmr != MMSYSERR_NOERROR)
  1302. break;
  1303. SendMessage(pmxuc->hwnd, BM_SETCHECK, fChecked, 0);
  1304. break;
  1305. }
  1306. case MIXERCONTROL_CONTROLTYPE_PEAKMETER:
  1307. {
  1308. LONG lVol;
  1309. DWORD dwVol;
  1310. //
  1311. // A nonvisible line should be shunned
  1312. //
  1313. if (pmxul == NULL)
  1314. return;
  1315. pmxuc = &pmxul->acr[MIXUI_VUMETER];
  1316. if (pmxuc->state != MIXUI_CONTROL_INITIALIZED)
  1317. break;
  1318. mxcd.cbStruct = sizeof(mxcd);
  1319. mxcd.dwControlID = pvcd->dwMeterID;
  1320. mxcd.cChannels = 1;
  1321. mxcd.cMultipleItems = 0;
  1322. mxcd.cbDetails = sizeof(DWORD);
  1323. mxcd.paDetails = (LPVOID)&lVol;
  1324. mmr = mixerGetControlDetails((HMIXEROBJ)(pmxud->hmx)
  1325. , &mxcd
  1326. , MIXER_GETCONTROLDETAILSF_VALUE);
  1327. if (mmr != MMSYSERR_NOERROR)
  1328. break;
  1329. dwVol = (DWORD)abs(lVol);
  1330. dwVol = (VOLUME_TICS * dwVol) / 32768;
  1331. SendMessage(pmxuc->hwnd, VU_SETPOS, 0, dwVol);
  1332. break;
  1333. }
  1334. default:
  1335. return;
  1336. }
  1337. }
  1338. /*
  1339. * Mixer_SetControl
  1340. *
  1341. * - Change a mixerControl in response to a user event
  1342. * */
  1343. void Mixer_SetControl(
  1344. PMIXUIDIALOG pmxud, // app instance
  1345. HWND hctl, // hwnd of control that changed
  1346. int iLine, // visible line index of control that changed
  1347. int iCtl) // control id%line of control that changed
  1348. {
  1349. MMRESULT mmr;
  1350. MIXERCONTROLDETAILS mxcd;
  1351. PMIXUILINE pmxul;
  1352. PMIXUICTRL pmxuc;
  1353. PVOLCTRLDESC pvcd = NULL;
  1354. if ((DWORD)iLine >= pmxud->cmxul)
  1355. return;
  1356. pmxul = &pmxud->amxul[iLine];
  1357. pvcd = pmxul->pvcd;
  1358. if (iCtl <= MIXUI_LAST)
  1359. {
  1360. pmxuc = &pmxul->acr[iCtl];
  1361. }
  1362. switch (iCtl)
  1363. {
  1364. case MIXUI_ADVANCED:
  1365. Mixer_Advanced(pmxud, pvcd->dwLineID, pvcd->szName);
  1366. break;
  1367. case MIXUI_MULTICHANNEL:
  1368. // Note: This will always be true:
  1369. // (MXUL_STYLEF_DESTINATION & pmxul->dwStyle)
  1370. Mixer_Multichannel(pmxud, pvcd->dwVolumeID);
  1371. break;
  1372. case MIXUI_VOLUME:
  1373. case MIXUI_BALANCE:
  1374. {
  1375. // Make sure we have a volume slider
  1376. if ( pmxul->acr[MIXUI_VOLUME].state != MIXUI_CONTROL_UNINITIALIZED)
  1377. {
  1378. DWORD dwVolume;
  1379. DWORD dwBalance;
  1380. LPDWORD lpdwBalance = NULL;
  1381. dwVolume = (DWORD)SendMessage( pmxul->acr[MIXUI_VOLUME].hwnd
  1382. , TBM_GETPOS
  1383. , 0
  1384. , 0 );
  1385. dwVolume = VOLUME_TICS - dwVolume;
  1386. dwVolume = SLIDER_TO_VOLUME(dwVolume);
  1387. // See if we have a balance slider as well
  1388. if ( pmxul->acr[MIXUI_BALANCE].state != MIXUI_CONTROL_UNINITIALIZED)
  1389. {
  1390. dwBalance = (DWORD)SendMessage(pmxul->acr[MIXUI_BALANCE].hwnd
  1391. , TBM_GETPOS
  1392. , 0
  1393. , 0);
  1394. lpdwBalance = &dwBalance;
  1395. }
  1396. Mixer_SetVolume (pmxud, pvcd, dwVolume, lpdwBalance);
  1397. }
  1398. break;
  1399. }
  1400. case MIXUI_SWITCH:
  1401. {
  1402. LONG fChecked;
  1403. if (pmxuc->state != MIXUI_CONTROL_INITIALIZED)
  1404. break;
  1405. fChecked = (LONG)SendMessage(pmxuc->hwnd, BM_GETCHECK, 0, 0);
  1406. //
  1407. // it's unlikely that there is a mixer and a mux and a mute
  1408. // representing the same line. It's most important that when a line
  1409. // is selected that the user gets a response. If there is a mute
  1410. // but no mux, then mute and mixer should be OFF and ON
  1411. // respectively and vice versa. If there is a mux and a mute the
  1412. // same is true.
  1413. // If there is a mux and a mixer... then the mux select should
  1414. // correspond.
  1415. //
  1416. if ( pvcd->dwSupport & VCD_SUPPORTF_MIXER_MUTE
  1417. && pvcd->dwVisible & VCD_VISIBLEF_MIXER_MUTE )
  1418. {
  1419. mmr = Mixer_Mute((HMIXEROBJ)(pmxud->hmx),
  1420. pvcd, &mxcd, fChecked);
  1421. }
  1422. if (pvcd->dwSupport & VCD_SUPPORTF_MIXER_MIXER
  1423. && pvcd->dwVisible & VCD_VISIBLEF_MIXER_MIXER )
  1424. {
  1425. //
  1426. // get all other mixer settings, make sure this one is checked
  1427. //
  1428. mxcd.cbStruct = sizeof(mxcd);
  1429. mxcd.dwControlID = pvcd->dwMixerID ;
  1430. mxcd.cChannels = 1;
  1431. mxcd.cMultipleItems = pvcd->cMixer;
  1432. mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
  1433. mxcd.paDetails = (LPVOID)pvcd->amcd_bMixer;
  1434. mmr = mixerGetControlDetails((HMIXEROBJ)(pmxud->hmx)
  1435. , &mxcd
  1436. , MIXER_GETCONTROLDETAILSF_VALUE);
  1437. if (mmr == MMSYSERR_NOERROR)
  1438. {
  1439. pvcd->amcd_bMixer[pvcd->iMixer].fValue = fChecked;
  1440. mmr = mixerSetControlDetails((HMIXEROBJ)(pmxud->hmx)
  1441. , &mxcd
  1442. , MIXER_SETCONTROLDETAILSF_VALUE);
  1443. }
  1444. if (fChecked && pvcd->dwSupport & VCD_SUPPORTF_MIXER_MUTE)
  1445. {
  1446. mmr = Mixer_Mute((HMIXEROBJ)(pmxud->hmx), pvcd, &mxcd, FALSE);
  1447. }
  1448. }
  1449. if (pvcd->dwSupport & VCD_SUPPORTF_MIXER_MUX
  1450. && pvcd->dwVisible & VCD_VISIBLEF_MIXER_MUX )
  1451. {
  1452. DWORD i;
  1453. //
  1454. // get all other mux settings, make sure this one is checked
  1455. // or unchecked and all others are not.
  1456. //
  1457. for (i = 0; i < pvcd->cMux; i++)
  1458. pvcd->amcd_bMux[i].fValue = FALSE;
  1459. pvcd->amcd_bMux[pvcd->iMux].fValue = TRUE;
  1460. mxcd.cbStruct = sizeof(mxcd);
  1461. mxcd.dwControlID = pvcd->dwMuxID ;
  1462. mxcd.cChannels = 1;
  1463. mxcd.cMultipleItems = pvcd->cMux;
  1464. mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
  1465. mxcd.paDetails = (LPVOID)pvcd->amcd_bMux;
  1466. mmr = mixerSetControlDetails((HMIXEROBJ)(pmxud->hmx)
  1467. , &mxcd
  1468. , MIXER_SETCONTROLDETAILSF_VALUE);
  1469. if (fChecked && pvcd->dwSupport & VCD_SUPPORTF_MIXER_MUTE)
  1470. {
  1471. mmr = Mixer_Mute((HMIXEROBJ)(pmxud->hmx), pvcd, &mxcd, FALSE);
  1472. }
  1473. }
  1474. break;
  1475. }
  1476. default:
  1477. break;
  1478. }
  1479. }
  1480. /*
  1481. * Mixer_PollingUpdate
  1482. *
  1483. * Controls that need to be updated by a timer.
  1484. *
  1485. * */
  1486. void Mixer_PollingUpdate(
  1487. PMIXUIDIALOG pmxud)
  1488. {
  1489. DWORD i;
  1490. MMRESULT mmr;
  1491. MIXERLINE mxl;
  1492. //
  1493. // For all visible mixer lines, locate the control id's that need to be
  1494. // updated.
  1495. //
  1496. for (i = 0; i < pmxud->cmxul; i++)
  1497. {
  1498. PMIXUICTRL pmxuc = &pmxud->amxul[i].acr[MIXUI_VUMETER];
  1499. PVOLCTRLDESC pvcd = pmxud->amxul[i].pvcd;
  1500. if (pmxuc->state == MIXUI_CONTROL_UNINITIALIZED)
  1501. continue;
  1502. if (!(pvcd->dwSupport & VCD_SUPPORTF_MIXER_METER))
  1503. continue;
  1504. //
  1505. // Is the line active?
  1506. //
  1507. mxl.cbStruct = sizeof(MIXERLINE);
  1508. mxl.dwLineID = pvcd->dwLineID;
  1509. mmr = mixerGetLineInfo((HMIXEROBJ)(pmxud->hmx)
  1510. , &mxl
  1511. , MIXER_GETLINEINFOF_LINEID);
  1512. //
  1513. // Force non active or invalid lines to 0
  1514. //
  1515. if (mmr != MMSYSERR_NOERROR || !(mxl.fdwLine & MIXERLINE_LINEF_ACTIVE))
  1516. {
  1517. SendMessage(pmxuc->hwnd, VU_SETPOS, 0, 0L);
  1518. continue;
  1519. }
  1520. //
  1521. // Force a visible update
  1522. //
  1523. Mixer_GetControlFromID(pmxud, pvcd->dwMeterID);
  1524. }
  1525. }
  1526. void ShowAndEnableWindow (HWND hWnd, BOOL fEnable)
  1527. {
  1528. ShowWindow (hWnd, fEnable ? SW_SHOW : SW_HIDE);
  1529. EnableWindow (hWnd, fEnable);
  1530. }
  1531. /*
  1532. * Mixer_Init
  1533. *
  1534. * Control initialization
  1535. * */
  1536. BOOL Mixer_Init(
  1537. PMIXUIDIALOG pmxud)
  1538. {
  1539. MMRESULT mmr;
  1540. MIXERLINE mlDst;
  1541. DWORD iline;
  1542. TCHAR achFmt[256];
  1543. TCHAR achTitle[256];
  1544. TCHAR achAccessible[256];
  1545. int x;
  1546. ZeroMemory (achFmt, sizeof (achFmt)); // Inital value for prefix
  1547. mmr = mixerOpen((LPHMIXER)&pmxud->hmx
  1548. , pmxud->mxid
  1549. , (DWORD_PTR)pmxud->hwnd
  1550. , 0
  1551. , CALLBACK_WINDOW);
  1552. if (mmr != MMSYSERR_NOERROR)
  1553. {
  1554. return FALSE;
  1555. }
  1556. else
  1557. {
  1558. DeviceChange_Init(pmxud->hwnd, pmxud->mxid);
  1559. }
  1560. if (mixerMessage((HMIXER)ULongToPtr(pmxud->mxid), DRV_QUERYDEVNODE, (DWORD_PTR)&pmxud->dwDevNode, 0L))
  1561. pmxud->dwDevNode = 0L;
  1562. LoadString(pmxud->hInstance, IDS_APPTITLE, achFmt, SIZEOF(achFmt));
  1563. mlDst.cbStruct = sizeof ( mlDst );
  1564. mlDst.dwDestination = pmxud->iDest;
  1565. mmr = mixerGetLineInfo((HMIXEROBJ)ULongToPtr(pmxud->mxid)
  1566. , &mlDst
  1567. , MIXER_GETLINEINFOF_DESTINATION);
  1568. if (mmr == MMSYSERR_NOERROR)
  1569. {
  1570. lstrcpy(achTitle, mlDst.szName);
  1571. }
  1572. else
  1573. {
  1574. LoadString(pmxud->hInstance, IDS_APPBASE, achTitle, SIZEOF(achTitle));
  1575. }
  1576. SetWindowText(pmxud->hwnd, achTitle);
  1577. //
  1578. // since we cannot get a WM_PARENTNOTIFY, we need to run through
  1579. // all controls and make appropriate modifications.
  1580. //
  1581. for ( iline = 0 ; iline < pmxud->cmxul ; iline++ )
  1582. {
  1583. PMIXUILINE pmxul = &pmxud->amxul[iline];
  1584. PMIXUICTRL amxuc = pmxul->acr;
  1585. HWND ctrl;
  1586. ctrl = Volume_GetLineItem(pmxud->hwnd, iline, IDC_LINELABEL);
  1587. if (ctrl)
  1588. {
  1589. if (pmxud->dwStyle & MXUD_STYLEF_SMALL)
  1590. Static_SetText(ctrl, pmxul->pvcd->szShortName);
  1591. else
  1592. Static_SetText(ctrl, pmxul->pvcd->szName);
  1593. }
  1594. // for MSAA (accessibility), we need to put the control name on the sliders
  1595. for (x = IDC_ACCESS_BALANCE; x <= IDC_ACCESS_VOLUME; x++)
  1596. {
  1597. ctrl = Volume_GetLineItem(pmxud->hwnd, iline, x);
  1598. if (ctrl)
  1599. {
  1600. Static_GetText(ctrl, achFmt, sizeof(achFmt)/sizeof(TCHAR));
  1601. if (pmxud->dwStyle & MXUD_STYLEF_SMALL)
  1602. {
  1603. wsprintf(achAccessible,achFmt,pmxul->pvcd->szShortName);
  1604. Static_SetText(ctrl, achAccessible);
  1605. }
  1606. else
  1607. {
  1608. wsprintf(achAccessible,achFmt,pmxul->pvcd->szName);
  1609. Static_SetText(ctrl, achAccessible);
  1610. }
  1611. }
  1612. }
  1613. //
  1614. // Master Control Multichannel Support
  1615. //
  1616. // Init multichannel support for master control if available. Note that if a master
  1617. // control exisits on the dialog, it is currently in the first position, but we do
  1618. // NOT rely on that fact here.
  1619. // Note: Not only must there be multiple channels, but Volume must also be
  1620. // Supported to manipulate the channels.
  1621. if (mlDst.cChannels > 2L &&
  1622. MXUL_STYLEF_DESTINATION & pmxul->dwStyle &&
  1623. pmxul->pvcd->dwSupport & VCD_SUPPORTF_MIXER_VOLUME)
  1624. {
  1625. int idc;
  1626. for (idc = IDC_MASTER_BALANCE_ICON_2; idc >= IDC_MULTICHANNEL; idc--)
  1627. {
  1628. ctrl = Volume_GetLineItem (pmxud->hwnd, iline, idc);
  1629. if (ctrl)
  1630. ShowAndEnableWindow (ctrl, (IDC_MULTICHANNEL == idc));
  1631. }
  1632. ctrl = Volume_GetLineItem (pmxud->hwnd, iline, IDC_BALANCE);
  1633. if (ctrl)
  1634. ShowAndEnableWindow (ctrl, FALSE);
  1635. switch (mlDst.dwComponentType)
  1636. {
  1637. case MIXERLINE_COMPONENTTYPE_DST_SPEAKERS:
  1638. // No Change
  1639. break;
  1640. case MIXERLINE_COMPONENTTYPE_DST_WAVEIN:
  1641. case MIXERLINE_COMPONENTTYPE_DST_VOICEIN:
  1642. // Recording
  1643. LoadString(pmxud->hInstance, IDS_MC_RECORDING, achFmt, SIZEOF(achFmt));
  1644. SetWindowText (ctrl, achFmt);
  1645. break;
  1646. default:
  1647. // Anything else...
  1648. LoadString(pmxud->hInstance, IDS_MC_LEVEL, achFmt, SIZEOF(achFmt));
  1649. SetWindowText (ctrl, achFmt);
  1650. break;
  1651. }
  1652. }
  1653. //
  1654. // Advanced escape
  1655. //
  1656. if (MXUD_ADVANCED(pmxud) &&
  1657. !(pmxud->dwStyle & MXUD_STYLEF_SMALL))
  1658. {
  1659. HWND hadv = Volume_GetLineItem(pmxud->hwnd, iline, IDC_ADVANCED);
  1660. if (hadv)
  1661. {
  1662. ShowWindow(hadv,(pmxul->pvcd->dwSupport & VCD_SUPPORTF_MIXER_ADVANCED)?SW_SHOW:SW_HIDE);
  1663. EnableWindow(hadv,
  1664. (pmxul->pvcd->dwSupport & VCD_SUPPORTF_MIXER_ADVANCED)?TRUE:FALSE);
  1665. }
  1666. }
  1667. if (pmxul->pvcd->dwSupport & VCD_SUPPORTF_DISABLED)
  1668. continue;
  1669. //
  1670. // allow init of control structures
  1671. //
  1672. if (pmxul->pvcd->dwSupport & VCD_SUPPORTF_MIXER_VOLUME)
  1673. {
  1674. amxuc[MIXUI_VOLUME].state = MIXUI_CONTROL_ENABLED;
  1675. if (pmxul->pvcd->dwSupport & VCD_SUPPORTF_MONO)
  1676. {
  1677. amxuc[MIXUI_BALANCE].state = MIXUI_CONTROL_UNINITIALIZED;
  1678. }
  1679. else
  1680. amxuc[MIXUI_BALANCE].state = MIXUI_CONTROL_ENABLED;
  1681. }
  1682. if (pmxul->pvcd->dwSupport & VCD_SUPPORTF_MIXER_METER)
  1683. amxuc[MIXUI_VUMETER].state = MIXUI_CONTROL_ENABLED;
  1684. if (pmxul->pvcd->dwSupport & VCD_SUPPORTF_MIXER_MUTE)
  1685. amxuc[MIXUI_SWITCH].state = MIXUI_CONTROL_ENABLED;
  1686. if ((pmxul->pvcd->dwSupport & ( VCD_SUPPORTF_MIXER_MIXER
  1687. | VCD_SUPPORTF_MIXER_MUX))
  1688. && (pmxul->pvcd->dwVisible & ( VCD_VISIBLEF_MIXER_MIXER
  1689. | VCD_VISIBLEF_MIXER_MUX)))
  1690. {
  1691. //
  1692. // No longer make the mute visible
  1693. //
  1694. pmxul->pvcd->dwVisible &= ~VCD_VISIBLEF_MIXER_MUTE;
  1695. amxuc[MIXUI_SWITCH].state = MIXUI_CONTROL_ENABLED;
  1696. ctrl = Volume_GetLineItem(pmxud->hwnd, iline, IDC_SWITCH);
  1697. if (ctrl)
  1698. {
  1699. TCHAR ach[256];
  1700. if (LoadString(pmxud->hInstance, IDS_SELECT, ach, SIZEOF(ach)))
  1701. Button_SetText(ctrl, ach);
  1702. }
  1703. }
  1704. }
  1705. return TRUE;
  1706. }
  1707. /*
  1708. * Mixer_Shutdown
  1709. *
  1710. * Close handles, etc..
  1711. * */
  1712. void Mixer_Shutdown(
  1713. PMIXUIDIALOG pmxud)
  1714. {
  1715. if (pmxud->hmx)
  1716. {
  1717. mixerClose(pmxud->hmx);
  1718. pmxud->hmx = NULL;
  1719. }
  1720. Mixer_CleanupVolumeDescription(pmxud->avcd, pmxud->cvcd);
  1721. }
  1722. /* - - - - - - - - - */
  1723. typedef struct tagAdv {
  1724. PMIXUIDIALOG pmxud; // IN
  1725. DWORD dwLineID; // IN
  1726. HMIXER hmx; // IN
  1727. LPTSTR szName; // IN
  1728. DWORD dwSupport;
  1729. DWORD dwBassID;
  1730. DWORD dwTrebleID;
  1731. DWORD dwSwitch1ID;
  1732. DWORD dwSwitch2ID;
  1733. } ADVPARAM, *PADVPARAM;
  1734. #define GETPADVPARAM(x) (ADVPARAM *)GetWindowLongPtr(x, DWLP_USER)
  1735. #define SETPADVPARAM(x,y) SetWindowLongPtr(x, DWLP_USER, y)
  1736. #define ADV_HAS_BASS 0x00000001
  1737. #define ADV_HAS_TREBLE 0x00000002
  1738. #define ADV_HAS_SWITCH1 0x00000004
  1739. #define ADV_HAS_SWITCH2 0x00000008
  1740. void Mixer_Advanced_Update(
  1741. PADVPARAM pap,
  1742. HWND hwnd)
  1743. {
  1744. MIXERCONTROLDETAILS mxcd;
  1745. DWORD dwValue = 0;
  1746. MMRESULT mmr;
  1747. if (pap->dwSupport & ADV_HAS_TREBLE)
  1748. {
  1749. mxcd.cbStruct = sizeof(mxcd);
  1750. mxcd.dwControlID = pap->dwTrebleID ;
  1751. mxcd.cChannels = 1;
  1752. mxcd.cMultipleItems = 0;
  1753. mxcd.cbDetails = sizeof(DWORD);
  1754. mxcd.paDetails = (LPVOID)&dwValue;
  1755. mmr = mixerGetControlDetails((HMIXEROBJ)(pap->hmx)
  1756. , &mxcd
  1757. , MIXER_GETCONTROLDETAILSF_VALUE);
  1758. if (mmr == MMSYSERR_NOERROR)
  1759. {
  1760. dwValue = VOLUME_TO_SLIDER(dwValue);
  1761. SendMessage(GetDlgItem(hwnd, IDC_TREBLE), TBM_SETPOS, TRUE, dwValue);
  1762. }
  1763. }
  1764. if (pap->dwSupport & ADV_HAS_BASS)
  1765. {
  1766. mxcd.cbStruct = sizeof(mxcd);
  1767. mxcd.dwControlID = pap->dwBassID;
  1768. mxcd.cChannels = 1;
  1769. mxcd.cMultipleItems = 0;
  1770. mxcd.cbDetails = sizeof(DWORD);
  1771. mxcd.paDetails = (LPVOID)&dwValue;
  1772. mmr = mixerGetControlDetails((HMIXEROBJ)(pap->hmx)
  1773. , &mxcd
  1774. , MIXER_GETCONTROLDETAILSF_VALUE);
  1775. if (mmr == MMSYSERR_NOERROR)
  1776. {
  1777. dwValue = VOLUME_TO_SLIDER(dwValue);
  1778. SendMessage(GetDlgItem(hwnd, IDC_BASS), TBM_SETPOS, TRUE, dwValue);
  1779. }
  1780. }
  1781. if (pap->dwSupport & ADV_HAS_SWITCH1)
  1782. {
  1783. mxcd.cbStruct = sizeof(mxcd);
  1784. mxcd.dwControlID = pap->dwSwitch1ID;
  1785. mxcd.cChannels = 1;
  1786. mxcd.cMultipleItems = 0;
  1787. mxcd.cbDetails = sizeof(DWORD);
  1788. mxcd.paDetails = (LPVOID)&dwValue;
  1789. mmr = mixerGetControlDetails((HMIXEROBJ)(pap->hmx)
  1790. , &mxcd
  1791. , MIXER_GETCONTROLDETAILSF_VALUE);
  1792. if (mmr == MMSYSERR_NOERROR)
  1793. {
  1794. Button_SetCheck(GetDlgItem(hwnd,IDC_SWITCH1),dwValue);
  1795. }
  1796. }
  1797. if (pap->dwSupport & ADV_HAS_SWITCH2)
  1798. {
  1799. mxcd.cbStruct = sizeof(mxcd);
  1800. mxcd.dwControlID = pap->dwSwitch2ID;
  1801. mxcd.cChannels = 1;
  1802. mxcd.cMultipleItems = 0;
  1803. mxcd.cbDetails = sizeof(DWORD);
  1804. mxcd.paDetails = (LPVOID)&dwValue;
  1805. mmr = mixerGetControlDetails((HMIXEROBJ)(pap->hmx)
  1806. , &mxcd
  1807. , MIXER_GETCONTROLDETAILSF_VALUE);
  1808. if (mmr == MMSYSERR_NOERROR)
  1809. {
  1810. Button_SetCheck(GetDlgItem(hwnd,IDC_SWITCH2),dwValue);
  1811. }
  1812. }
  1813. }
  1814. void Mixer_Advanced_OnMixmControlChange(
  1815. HWND hwnd,
  1816. HMIXER hmx,
  1817. DWORD dwControlID)
  1818. {
  1819. PADVPARAM pap = GETPADVPARAM(hwnd);
  1820. if (!pap)
  1821. return;
  1822. if ( ((pap->dwSupport & ADV_HAS_BASS)
  1823. && dwControlID == pap->dwBassID)
  1824. || ((pap->dwSupport & ADV_HAS_TREBLE)
  1825. && dwControlID == pap->dwTrebleID)
  1826. || ((pap->dwSupport & ADV_HAS_SWITCH1)
  1827. && dwControlID == pap->dwSwitch1ID)
  1828. || ((pap->dwSupport & ADV_HAS_SWITCH2)
  1829. && dwControlID == pap->dwSwitch2ID) )
  1830. {
  1831. Mixer_Advanced_Update(pap,hwnd);
  1832. }
  1833. }
  1834. BOOL Mixer_Advanced_OnInitDialog(
  1835. HWND hwnd,
  1836. HWND hwndFocus,
  1837. LPARAM lParam)
  1838. {
  1839. PADVPARAM pap;
  1840. MIXERLINECONTROLS mxlc;
  1841. MIXERCONTROL *pmxc;
  1842. MIXERLINE ml;
  1843. MMRESULT mmr;
  1844. DWORD iCtrl, iSwitch1, iSwitch2;
  1845. TCHAR ach[MIXER_LONG_NAME_CHARS + 24];
  1846. TCHAR achFmt[256];
  1847. HWND hBass,hTreble,hSwitch1,hSwitch2;
  1848. SETPADVPARAM(hwnd, lParam);
  1849. pap = GETPADVPARAM(hwnd);
  1850. if (!pap)
  1851. EndDialog(hwnd, FALSE);
  1852. //
  1853. // clone the mixer handle to catch callbacks
  1854. //
  1855. #ifndef _WIN64
  1856. mmr = mixerOpen((LPHMIXER)&pap->hmx
  1857. , (UINT)pap->pmxud->hmx
  1858. , (DWORD_PTR)hwnd
  1859. , 0
  1860. , CALLBACK_WINDOW | MIXER_OBJECTF_HMIXER );
  1861. #else
  1862. mmr = mixerOpen((LPHMIXER)&pap->hmx
  1863. , (UINT)pap->pmxud->mxid
  1864. , (DWORD_PTR)hwnd
  1865. , 0
  1866. , CALLBACK_WINDOW | MIXER_OBJECTF_HMIXER );
  1867. #endif
  1868. if (mmr != MMSYSERR_NOERROR)
  1869. EndDialog(hwnd, FALSE);
  1870. //
  1871. // Get all controls.
  1872. //
  1873. ml.cbStruct = sizeof(ml);
  1874. ml.dwLineID = pap->dwLineID;
  1875. mmr = mixerGetLineInfo((HMIXEROBJ)pap->hmx
  1876. , &ml
  1877. , MIXER_GETLINEINFOF_LINEID);
  1878. if (mmr != MMSYSERR_NOERROR || ml.cControls == 0L)
  1879. EndDialog(hwnd, FALSE);
  1880. pmxc = (MIXERCONTROL *)GlobalAllocPtr(GHND,
  1881. sizeof(MIXERCONTROL) * ml.cControls);
  1882. if (!pmxc)
  1883. {
  1884. EndDialog(hwnd, FALSE);
  1885. return FALSE; // Bail on error
  1886. }
  1887. mxlc.cbStruct = sizeof(mxlc);
  1888. mxlc.dwLineID = pap->dwLineID;
  1889. mxlc.cControls = ml.cControls;
  1890. mxlc.cbmxctrl = sizeof(MIXERCONTROL);
  1891. mxlc.pamxctrl = pmxc;
  1892. mmr = mixerGetLineControls((HMIXEROBJ)(pap->hmx)
  1893. , &mxlc
  1894. , MIXER_GETLINECONTROLSF_ALL);
  1895. if (mmr != MMSYSERR_NOERROR)
  1896. {
  1897. GlobalFreePtr(pmxc);
  1898. EndDialog(hwnd, FALSE);
  1899. }
  1900. pap->dwSupport = 0L;
  1901. for (iCtrl = 0; iCtrl < ml.cControls; iCtrl++)
  1902. {
  1903. switch (pmxc[iCtrl].dwControlType)
  1904. {
  1905. case MIXERCONTROL_CONTROLTYPE_BASS:
  1906. if (!(pap->dwSupport & ADV_HAS_BASS))
  1907. {
  1908. pap->dwBassID = pmxc[iCtrl].dwControlID;
  1909. pap->dwSupport |= ADV_HAS_BASS;
  1910. }
  1911. break;
  1912. case MIXERCONTROL_CONTROLTYPE_TREBLE:
  1913. if (!(pap->dwSupport & ADV_HAS_TREBLE))
  1914. {
  1915. pap->dwTrebleID = pmxc[iCtrl].dwControlID;
  1916. pap->dwSupport |= ADV_HAS_TREBLE;
  1917. }
  1918. break;
  1919. case MIXERCONTROL_CONTROLTYPE_BOOLEAN:
  1920. case MIXERCONTROL_CONTROLTYPE_MONO:
  1921. case MIXERCONTROL_CONTROLTYPE_STEREOENH:
  1922. case MIXERCONTROL_CONTROLTYPE_ONOFF:
  1923. case MIXERCONTROL_CONTROLTYPE_LOUDNESS:
  1924. if (!(pap->dwSupport & ADV_HAS_SWITCH1))
  1925. {
  1926. pap->dwSwitch1ID = pmxc[iCtrl].dwControlID;
  1927. pap->dwSupport |= ADV_HAS_SWITCH1;
  1928. iSwitch1 = iCtrl;
  1929. }
  1930. else if (!(pap->dwSupport & ADV_HAS_SWITCH2))
  1931. {
  1932. pap->dwSwitch2ID = pmxc[iCtrl].dwControlID;
  1933. pap->dwSupport |= ADV_HAS_SWITCH2;
  1934. iSwitch2 = iCtrl;
  1935. }
  1936. break;
  1937. }
  1938. }
  1939. //
  1940. //
  1941. //
  1942. hBass = GetDlgItem(hwnd, IDC_BASS);
  1943. hTreble = GetDlgItem(hwnd, IDC_TREBLE);
  1944. hSwitch1 = GetDlgItem(hwnd, IDC_SWITCH1);
  1945. hSwitch2 = GetDlgItem(hwnd, IDC_SWITCH2);
  1946. SendMessage(hBass, TBM_SETRANGE, 0, MAKELONG(0, VOLUME_TICS));
  1947. SendMessage(hBass, TBM_SETTICFREQ, (VOLUME_TICS + 5)/6, 0 );
  1948. SendMessage(hTreble, TBM_SETRANGE, 0, MAKELONG(0, VOLUME_TICS));
  1949. SendMessage(hTreble, TBM_SETTICFREQ, (VOLUME_TICS + 5)/6, 0 );
  1950. if (!(pap->dwSupport & ADV_HAS_BASS))
  1951. {
  1952. SendMessage(hBass, TBM_SETPOS, 64, 0 );
  1953. EnableWindow(GetDlgItem(hwnd, IDC_TXT_LOW1), FALSE);
  1954. EnableWindow(GetDlgItem(hwnd, IDC_TXT_HI1), FALSE);
  1955. }
  1956. EnableWindow(hBass, (pap->dwSupport & ADV_HAS_BASS));
  1957. if (!(pap->dwSupport & ADV_HAS_TREBLE))
  1958. {
  1959. SendMessage(hTreble, TBM_SETPOS, 64, 0 );
  1960. EnableWindow(GetDlgItem(hwnd, IDC_TXT_LOW2), FALSE);
  1961. EnableWindow(GetDlgItem(hwnd, IDC_TXT_HI2), FALSE);
  1962. }
  1963. EnableWindow(hTreble, (pap->dwSupport & ADV_HAS_TREBLE));
  1964. if (pap->dwSupport & ADV_HAS_SWITCH1)
  1965. {
  1966. LoadString(pap->pmxud->hInstance, IDS_ADV_SWITCH1, achFmt,
  1967. SIZEOF(achFmt));
  1968. wsprintf(ach, achFmt, pmxc[iSwitch1].szName);
  1969. SetWindowText(hSwitch1, ach);
  1970. ShowWindow(hSwitch1, SW_SHOW);
  1971. ShowWindow(GetDlgItem(hwnd, IDC_TXT_SWITCHES), SW_SHOW);
  1972. ShowWindow(GetDlgItem(hwnd, IDC_GRP_OTHER), SW_SHOW);
  1973. }
  1974. EnableWindow(hSwitch1, (pap->dwSupport & ADV_HAS_SWITCH1));
  1975. if (pap->dwSupport & ADV_HAS_SWITCH2)
  1976. {
  1977. LoadString(pap->pmxud->hInstance, IDS_ADV_SWITCH2, achFmt,
  1978. SIZEOF(achFmt));
  1979. wsprintf(ach, achFmt, pmxc[iSwitch2].szName);
  1980. SetWindowText(hSwitch2, ach);
  1981. ShowWindow(hSwitch2, SW_SHOW);
  1982. }
  1983. EnableWindow(hSwitch2, (pap->dwSupport & ADV_HAS_SWITCH2));
  1984. if (pap->dwSupport & (ADV_HAS_SWITCH1 | ADV_HAS_SWITCH2))
  1985. {
  1986. RECT rcGrp,rcGrp2,rcClose,rcWnd;
  1987. DWORD dwDY=0L;
  1988. POINT pos;
  1989. HWND hClose = GetDlgItem(hwnd, IDOK);
  1990. HWND hGrp2 = GetDlgItem(hwnd, IDC_GRP_OTHER);
  1991. GetWindowRect(GetDlgItem(hwnd, IDC_GRP_TONE), &rcGrp);
  1992. GetWindowRect(GetDlgItem(hwnd, IDC_GRP_OTHER), &rcGrp2);
  1993. GetWindowRect(hClose, &rcClose);
  1994. GetWindowRect(hwnd, &rcWnd);
  1995. if (pap->dwSupport & ADV_HAS_SWITCH2)
  1996. {
  1997. RECT rc1, rc2;
  1998. GetWindowRect(hSwitch1,&rc1);
  1999. GetWindowRect(hSwitch2,&rc2);
  2000. rcGrp2.bottom += rc2.bottom - rc1.bottom;
  2001. }
  2002. dwDY = rcGrp2.bottom - rcGrp.bottom;
  2003. //
  2004. // resize our main window
  2005. //
  2006. MoveWindow(hwnd, rcWnd.left
  2007. , rcWnd.top
  2008. , rcWnd.right - rcWnd.left
  2009. , (rcWnd.bottom - rcWnd.top) + dwDY
  2010. , FALSE);
  2011. //
  2012. // move the close button
  2013. //
  2014. MapWindowPoints(NULL, hwnd, (LPPOINT)&rcClose, 2);
  2015. pos.x = rcClose.left;
  2016. pos.y = rcClose.top;
  2017. MoveWindow(hClose, pos.x
  2018. , pos.y + dwDY
  2019. , rcClose.right - rcClose.left
  2020. , rcClose.bottom - rcClose.top
  2021. , FALSE);
  2022. //
  2023. // resize our group box if necessary
  2024. //
  2025. if (pap->dwSupport & ADV_HAS_SWITCH2)
  2026. {
  2027. MapWindowPoints(NULL, hwnd, (LPPOINT)&rcGrp2, 2);
  2028. pos.x = rcGrp2.left;
  2029. pos.y = rcGrp2.top;
  2030. MoveWindow(hGrp2, pos.x
  2031. , pos.y
  2032. , rcGrp2.right - rcGrp2.left
  2033. , rcGrp2.bottom - rcGrp2.top
  2034. , FALSE);
  2035. }
  2036. }
  2037. GlobalFreePtr(pmxc);
  2038. {
  2039. TCHAR achTitle[MIXER_LONG_NAME_CHARS+256];
  2040. LoadString(pap->pmxud->hInstance, IDS_ADV_TITLE, achFmt,
  2041. SIZEOF(achFmt));
  2042. wsprintf(achTitle, achFmt, pap->szName);
  2043. SetWindowText(hwnd, achTitle);
  2044. }
  2045. Mixer_Advanced_Update(pap, hwnd);
  2046. return TRUE;
  2047. }
  2048. void Mixer_Advanced_OnXScroll(
  2049. HWND hwnd,
  2050. HWND hwndCtl,
  2051. UINT code,
  2052. int pos)
  2053. {
  2054. PADVPARAM pap;
  2055. MIXERCONTROLDETAILS mxcd;
  2056. DWORD dwVol;
  2057. MMRESULT mmr;
  2058. pap = GETPADVPARAM(hwnd);
  2059. if (!pap)
  2060. return;
  2061. if (pap->dwSupport & ADV_HAS_TREBLE)
  2062. {
  2063. dwVol = (DWORD)SendMessage( GetDlgItem(hwnd, IDC_TREBLE)
  2064. , TBM_GETPOS
  2065. , 0
  2066. , 0 );
  2067. dwVol = SLIDER_TO_VOLUME(dwVol);
  2068. mxcd.cbStruct = sizeof(mxcd);
  2069. mxcd.dwControlID = pap->dwTrebleID ;
  2070. mxcd.cChannels = 1;
  2071. mxcd.cMultipleItems = 0;
  2072. mxcd.cbDetails = sizeof(DWORD);
  2073. mxcd.paDetails = (LPVOID)&dwVol;
  2074. mixerSetControlDetails((HMIXEROBJ)(pap->hmx)
  2075. , &mxcd
  2076. , MIXER_SETCONTROLDETAILSF_VALUE);
  2077. }
  2078. if (pap->dwSupport & ADV_HAS_BASS)
  2079. {
  2080. dwVol = (DWORD)SendMessage( GetDlgItem(hwnd, IDC_BASS)
  2081. , TBM_GETPOS
  2082. , 0
  2083. , 0 );
  2084. dwVol = SLIDER_TO_VOLUME(dwVol);
  2085. mxcd.cbStruct = sizeof(mxcd);
  2086. mxcd.dwControlID = pap->dwBassID;
  2087. mxcd.cChannels = 1;
  2088. mxcd.cMultipleItems = 0;
  2089. mxcd.cbDetails = sizeof(DWORD);
  2090. mxcd.paDetails = (LPVOID)&dwVol;
  2091. mmr = mixerSetControlDetails((HMIXEROBJ)(pap->hmx)
  2092. , &mxcd
  2093. , MIXER_SETCONTROLDETAILSF_VALUE);
  2094. }
  2095. }
  2096. void Mixer_Advanced_OnSwitch(
  2097. HWND hwnd,
  2098. int id,
  2099. HWND hwndCtl)
  2100. {
  2101. PADVPARAM pap;
  2102. MIXERCONTROLDETAILS mxcd;
  2103. DWORD dwValue;
  2104. MMRESULT mmr;
  2105. pap = GETPADVPARAM(hwnd);
  2106. if (!pap)
  2107. return;
  2108. dwValue = Button_GetCheck(hwndCtl);
  2109. mxcd.cbStruct = sizeof(mxcd);
  2110. mxcd.dwControlID = (id == IDC_SWITCH1)?pap->dwSwitch1ID:pap->dwSwitch2ID;
  2111. mxcd.cChannels = 1;
  2112. mxcd.cMultipleItems = 0;
  2113. mxcd.cbDetails = sizeof(DWORD);
  2114. mxcd.paDetails = (LPVOID)&dwValue;
  2115. mmr = mixerSetControlDetails((HMIXEROBJ)(pap->hmx)
  2116. , &mxcd
  2117. , MIXER_SETCONTROLDETAILSF_VALUE);
  2118. }
  2119. BOOL Mixer_Advanced_OnCommand(
  2120. HWND hwnd,
  2121. int id,
  2122. HWND hwndCtl,
  2123. UINT codeNotify)
  2124. {
  2125. switch (id)
  2126. {
  2127. case IDOK:
  2128. EndDialog(hwnd, TRUE);
  2129. break;
  2130. case IDCANCEL:
  2131. EndDialog(hwnd, FALSE);
  2132. break;
  2133. case IDC_SWITCH1:
  2134. Mixer_Advanced_OnSwitch(hwnd, id, hwndCtl);
  2135. break;
  2136. case IDC_SWITCH2:
  2137. Mixer_Advanced_OnSwitch(hwnd, id, hwndCtl);
  2138. break;
  2139. }
  2140. return FALSE;
  2141. }
  2142. INT_PTR CALLBACK Mixer_Advanced_Proc(
  2143. HWND hwnd,
  2144. UINT msg,
  2145. WPARAM wparam,
  2146. LPARAM lparam)
  2147. {
  2148. switch (msg)
  2149. {
  2150. case WM_INITDIALOG:
  2151. HANDLE_WM_INITDIALOG(hwnd, wparam, lparam, Mixer_Advanced_OnInitDialog);
  2152. return TRUE;
  2153. case MM_MIXM_CONTROL_CHANGE:
  2154. HANDLE_MM_MIXM_CONTROL_CHANGE(hwnd
  2155. , wparam
  2156. , lparam
  2157. , Mixer_Advanced_OnMixmControlChange);
  2158. break;
  2159. case WM_CLOSE:
  2160. EndDialog(hwnd, FALSE);
  2161. break;
  2162. case WM_HSCROLL:
  2163. HANDLE_WM_XSCROLL(hwnd, wparam, lparam, Mixer_Advanced_OnXScroll);
  2164. break;
  2165. case WM_COMMAND:
  2166. HANDLE_WM_COMMAND(hwnd, wparam, lparam, Mixer_Advanced_OnCommand);
  2167. break;
  2168. case WM_DESTROY:
  2169. {
  2170. PADVPARAM pap = GETPADVPARAM(hwnd);
  2171. if (pap)
  2172. {
  2173. if (pap->hmx)
  2174. mixerClose(pap->hmx);
  2175. }
  2176. break;
  2177. }
  2178. default:
  2179. break;
  2180. }
  2181. return FALSE;
  2182. }
  2183. /*
  2184. * Advanced Features for specific mixer lines.
  2185. */
  2186. void Mixer_Advanced(
  2187. PMIXUIDIALOG pmxud,
  2188. DWORD dwLineID,
  2189. LPTSTR szName)
  2190. {
  2191. ADVPARAM advp;
  2192. ZeroMemory(&advp, sizeof(ADVPARAM));
  2193. advp.pmxud = pmxud;
  2194. advp.dwLineID = dwLineID;
  2195. advp.szName = szName;
  2196. DialogBoxParam(pmxud->hInstance
  2197. , MAKEINTRESOURCE(IDD_ADVANCED)
  2198. , pmxud->hwnd
  2199. , Mixer_Advanced_Proc
  2200. , (LPARAM)(LPVOID)&advp);
  2201. }
  2202. typedef void (*MULTICHANNELFUNC)(HWND, UINT, DWORD, DWORD);
  2203. void Mixer_Multichannel (PMIXUIDIALOG pmxud, DWORD dwVolumeID)
  2204. {
  2205. HMODULE hModule;
  2206. MULTICHANNELFUNC fnMultiChannel;
  2207. if (pmxud)
  2208. {
  2209. hModule = (HMODULE) LoadLibrary (TEXT ("mmsys.cpl"));
  2210. if (hModule)
  2211. {
  2212. fnMultiChannel = (MULTICHANNELFUNC) GetProcAddress (hModule, "Multichannel");
  2213. if (fnMultiChannel)
  2214. {
  2215. (*fnMultiChannel)(pmxud->hwnd, pmxud->mxid, pmxud->iDest, dwVolumeID);
  2216. }
  2217. FreeLibrary (hModule);
  2218. }
  2219. }
  2220. }