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.

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