xref: /reactos/dll/cpl/mmsys/volume.c (revision 305aae75)
1 /*
2  * PROJECT:         ReactOS Multimedia Control Panel
3  * FILE:            dll/cpl/mmsys/volume.c
4  * PURPOSE:         ReactOS Multimedia Control Panel
5  * PROGRAMMER:      Thomas Weidenmueller <w3seek@reactos.com>
6  *                  Johannes Anderwald <janderwald@reactos.com>
7  *                  Dmitry Chapyshev <dmitry@reactos.org>
8  */
9 
10 #include "mmsys.h"
11 
12 #include <shellapi.h>
13 
14 
15 typedef struct _IMGINFO
16 {
17     HBITMAP hBitmap;
18     INT cxSource;
19     INT cySource;
20 } IMGINFO, *PIMGINFO;
21 
22 
23 typedef struct _GLOBAL_DATA
24 {
25     HMIXER hMixer;
26     HICON hIconMuted;
27     HICON hIconUnMuted;
28     HICON hIconNoHW;
29 
30     LONG muteVal;
31     DWORD muteControlID;
32 
33     DWORD volumeControlID;
34     DWORD volumeChannels;
35     DWORD volumeMinimum;
36     DWORD volumeMaximum;
37     DWORD volumeStep;
38 
39     DWORD maxVolume;
40     PMIXERCONTROLDETAILS_UNSIGNED volumeInitialValues;
41     PMIXERCONTROLDETAILS_UNSIGNED volumePreviousValues;
42     PMIXERCONTROLDETAILS_UNSIGNED volumeCurrentValues;
43 
44 } GLOBAL_DATA, *PGLOBAL_DATA;
45 
46 
47 static VOID
48 InitImageInfo(PIMGINFO ImgInfo)
49 {
50     BITMAP bitmap;
51 
52     ZeroMemory(ImgInfo, sizeof(*ImgInfo));
53 
54     ImgInfo->hBitmap = LoadImageW(hApplet,
55                                   MAKEINTRESOURCEW(IDB_SPEAKIMG),
56                                   IMAGE_BITMAP,
57                                   0,
58                                   0,
59                                   LR_DEFAULTCOLOR);
60 
61     if (ImgInfo->hBitmap != NULL)
62     {
63         GetObjectW(ImgInfo->hBitmap, sizeof(BITMAP), &bitmap);
64 
65         ImgInfo->cxSource = bitmap.bmWidth;
66         ImgInfo->cySource = bitmap.bmHeight;
67     }
68 }
69 
70 
71 VOID
72 GetMuteControl(PGLOBAL_DATA pGlobalData)
73 {
74     MIXERLINEW mxln;
75     MIXERCONTROLW mxc;
76     MIXERLINECONTROLSW mxlctrl;
77 
78     if (pGlobalData->hMixer == NULL)
79         return;
80 
81     mxln.cbStruct = sizeof(MIXERLINEW);
82     mxln.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
83 
84     if (mixerGetLineInfoW((HMIXEROBJ)pGlobalData->hMixer, &mxln, MIXER_OBJECTF_HMIXER | MIXER_GETLINEINFOF_COMPONENTTYPE)
85         != MMSYSERR_NOERROR)
86         return;
87 
88     mxlctrl.cbStruct = sizeof(MIXERLINECONTROLSW);
89     mxlctrl.dwLineID = mxln.dwLineID;
90     mxlctrl.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
91     mxlctrl.cControls = 1;
92     mxlctrl.cbmxctrl = sizeof(MIXERCONTROLW);
93     mxlctrl.pamxctrl = &mxc;
94 
95     if (mixerGetLineControlsW((HMIXEROBJ)pGlobalData->hMixer, &mxlctrl, MIXER_OBJECTF_HMIXER | MIXER_GETLINECONTROLSF_ONEBYTYPE)
96         != MMSYSERR_NOERROR)
97         return;
98 
99     pGlobalData->muteControlID = mxc.dwControlID;
100 }
101 
102 
103 VOID
104 GetMuteState(PGLOBAL_DATA pGlobalData)
105 {
106     MIXERCONTROLDETAILS_BOOLEAN mxcdMute;
107     MIXERCONTROLDETAILS mxcd;
108 
109     if (pGlobalData->hMixer == NULL)
110         return;
111 
112     mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
113     mxcd.dwControlID = pGlobalData->muteControlID;
114     mxcd.cChannels = 1;
115     mxcd.cMultipleItems = 0;
116     mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
117     mxcd.paDetails = &mxcdMute;
118 
119     if (mixerGetControlDetailsW((HMIXEROBJ)pGlobalData->hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_GETCONTROLDETAILSF_VALUE)
120         != MMSYSERR_NOERROR)
121         return;
122 
123     pGlobalData->muteVal = mxcdMute.fValue;
124 }
125 
126 
127 VOID
128 SwitchMuteState(PGLOBAL_DATA pGlobalData)
129 {
130     MIXERCONTROLDETAILS_BOOLEAN mxcdMute;
131     MIXERCONTROLDETAILS mxcd;
132 
133     mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
134     mxcd.dwControlID = pGlobalData->muteControlID;
135     mxcd.cChannels = 1;
136     mxcd.cMultipleItems = 0;
137     mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
138     mxcd.paDetails = &mxcdMute;
139 
140     mxcdMute.fValue = !pGlobalData->muteVal;
141     if (mixerSetControlDetails((HMIXEROBJ)pGlobalData->hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE)
142         != MMSYSERR_NOERROR)
143         return;
144 
145     pGlobalData->muteVal = mxcdMute.fValue;
146 }
147 
148 
149 VOID
150 GetVolumeControl(PGLOBAL_DATA pGlobalData)
151 {
152     MIXERLINEW mxln;
153     MIXERCONTROLW mxc;
154     MIXERLINECONTROLSW mxlc;
155 
156     if (pGlobalData->hMixer == NULL)
157         return;
158 
159     mxln.cbStruct = sizeof(MIXERLINEW);
160     mxln.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
161     if (mixerGetLineInfoW((HMIXEROBJ)pGlobalData->hMixer, &mxln, MIXER_OBJECTF_HMIXER | MIXER_GETLINEINFOF_COMPONENTTYPE)
162         != MMSYSERR_NOERROR)
163         return;
164 
165     pGlobalData->volumeChannels = mxln.cChannels;
166 
167     mxlc.cbStruct = sizeof(MIXERLINECONTROLSW);
168     mxlc.dwLineID = mxln.dwLineID;
169     mxlc.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
170     mxlc.cControls = 1;
171     mxlc.cbmxctrl = sizeof(MIXERCONTROLW);
172     mxlc.pamxctrl = &mxc;
173     if (mixerGetLineControlsW((HMIXEROBJ)pGlobalData->hMixer, &mxlc, MIXER_OBJECTF_HMIXER | MIXER_GETLINECONTROLSF_ONEBYTYPE)
174         != MMSYSERR_NOERROR)
175         return;
176 
177     pGlobalData->volumeMinimum = mxc.Bounds.dwMinimum;
178     pGlobalData->volumeMaximum = mxc.Bounds.dwMaximum;
179     pGlobalData->volumeControlID = mxc.dwControlID;
180     pGlobalData->volumeStep = (pGlobalData->volumeMaximum - pGlobalData->volumeMinimum) / (VOLUME_MAX - VOLUME_MIN);
181 
182     pGlobalData->volumeInitialValues = HeapAlloc(GetProcessHeap(),
183                                                  0,
184                                                  mxln.cChannels * sizeof(MIXERCONTROLDETAILS_UNSIGNED));
185     if (pGlobalData->volumeInitialValues == NULL)
186         return;
187 
188     pGlobalData->volumePreviousValues = HeapAlloc(GetProcessHeap(),
189                                                   0,
190                                                   mxln.cChannels * sizeof(MIXERCONTROLDETAILS_UNSIGNED));
191     if (pGlobalData->volumePreviousValues == NULL)
192         return;
193 
194     pGlobalData->volumeCurrentValues = HeapAlloc(GetProcessHeap(),
195                                                  0,
196                                                  mxln.cChannels * sizeof(MIXERCONTROLDETAILS_UNSIGNED));
197     if (pGlobalData->volumeCurrentValues == NULL)
198         return;
199 }
200 
201 
202 VOID
203 GetVolumeValue(
204     PGLOBAL_DATA pGlobalData,
205     BOOL bInit)
206 {
207     MIXERCONTROLDETAILS mxcd;
208     DWORD i;
209 
210     if (pGlobalData->hMixer == NULL)
211         return;
212 
213     mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
214     mxcd.dwControlID = pGlobalData->volumeControlID;
215     mxcd.cChannels = pGlobalData->volumeChannels;
216     mxcd.cMultipleItems = 0;
217     mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
218     mxcd.paDetails = pGlobalData->volumePreviousValues;
219 
220     if (mixerGetControlDetailsW((HMIXEROBJ)pGlobalData->hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_GETCONTROLDETAILSF_VALUE)
221         != MMSYSERR_NOERROR)
222         return;
223 
224     pGlobalData->maxVolume = 0;
225     for (i = 0; i < pGlobalData->volumeChannels; i++)
226     {
227         pGlobalData->volumeCurrentValues[i].dwValue = pGlobalData->volumePreviousValues[i].dwValue;
228 
229         if (pGlobalData->volumePreviousValues[i].dwValue > pGlobalData->maxVolume)
230             pGlobalData->maxVolume = pGlobalData->volumePreviousValues[i].dwValue;
231 
232         if (bInit)
233             pGlobalData->volumeInitialValues[i].dwValue = pGlobalData->volumeCurrentValues[i].dwValue;
234     }
235 }
236 
237 
238 VOID
239 SetVolumeValue(PGLOBAL_DATA pGlobalData,
240                DWORD dwPosition)
241 {
242     MIXERCONTROLDETAILS mxcd;
243     DWORD dwVolume, i;
244 
245     if (pGlobalData->hMixer == NULL)
246         return;
247 
248     if (dwPosition == VOLUME_MIN)
249         dwVolume = pGlobalData->volumeMinimum;
250     else if (dwPosition == VOLUME_MAX)
251         dwVolume = pGlobalData->volumeMaximum;
252     else
253         dwVolume = (dwPosition * pGlobalData->volumeStep) + pGlobalData->volumeMinimum;
254 
255     for (i = 0; i < pGlobalData->volumeChannels; i++)
256     {
257         if (pGlobalData->volumePreviousValues[i].dwValue == pGlobalData->maxVolume)
258         {
259             pGlobalData->volumeCurrentValues[i].dwValue = dwVolume;
260         }
261         else
262         {
263             pGlobalData->volumeCurrentValues[i].dwValue =
264                 pGlobalData->volumePreviousValues[i].dwValue * dwVolume / pGlobalData->maxVolume;
265         }
266     }
267 
268     mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
269     mxcd.dwControlID = pGlobalData->volumeControlID;
270     mxcd.cChannels = pGlobalData->volumeChannels;
271     mxcd.cMultipleItems = 0;
272     mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
273     mxcd.paDetails = pGlobalData->volumeCurrentValues;
274 
275     if (mixerSetControlDetails((HMIXEROBJ)pGlobalData->hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE)
276         != MMSYSERR_NOERROR)
277         return;
278 }
279 
280 
281 static
282 VOID
283 RestoreVolumeValue(
284     PGLOBAL_DATA pGlobalData)
285 {
286     MIXERCONTROLDETAILS mxcd;
287 
288     if (pGlobalData->hMixer == NULL)
289         return;
290 
291     mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
292     mxcd.dwControlID = pGlobalData->volumeControlID;
293     mxcd.cChannels = pGlobalData->volumeChannels;
294     mxcd.cMultipleItems = 0;
295     mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
296     mxcd.paDetails = pGlobalData->volumeInitialValues;
297 
298     mixerSetControlDetails((HMIXEROBJ)pGlobalData->hMixer,
299                            &mxcd,
300                            MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE);
301 }
302 
303 
304 static
305 VOID
306 SetSystrayVolumeIconState(BOOL bEnabled)
307 {
308     HWND hwndTaskBar;
309 
310     hwndTaskBar = FindWindowW(L"SystemTray_Main", NULL);
311     if (hwndTaskBar == NULL)
312         return;
313 
314     SendMessageW(hwndTaskBar, WM_USER + 220, 4, bEnabled);
315 }
316 
317 static
318 BOOL
319 GetSystrayVolumeIconState(VOID)
320 {
321     HWND hwndTaskBar;
322 
323     hwndTaskBar = FindWindowW(L"SystemTray_Main", NULL);
324     if (hwndTaskBar == NULL)
325     {
326         return FALSE;
327     }
328 
329     return (BOOL)SendMessageW(hwndTaskBar, WM_USER + 221, 4, 0);
330 }
331 
332 VOID
333 InitVolumeControls(HWND hwndDlg, PGLOBAL_DATA pGlobalData)
334 {
335     UINT NumMixers;
336     MIXERCAPSW mxc;
337     WCHAR szNoDevices[256];
338 
339     LoadStringW(hApplet, IDS_NO_DEVICES, szNoDevices, _countof(szNoDevices));
340 
341     SendDlgItemMessageW(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_SETRANGE, (WPARAM)TRUE, (LPARAM)MAKELONG(VOLUME_MIN, VOLUME_MAX));
342     SendDlgItemMessageW(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_SETTICFREQ, VOLUME_TICFREQ, 0);
343     SendDlgItemMessageW(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_SETPAGESIZE, 0, VOLUME_PAGESIZE);
344 
345     NumMixers = mixerGetNumDevs();
346     if (!NumMixers)
347     {
348         EnableWindow(GetDlgItem(hwndDlg, IDC_VOLUME_TRACKBAR), FALSE);
349         EnableWindow(GetDlgItem(hwndDlg, IDC_VOLUME_LOW),      FALSE);
350         EnableWindow(GetDlgItem(hwndDlg, IDC_VOLUME_HIGH),     FALSE);
351         EnableWindow(GetDlgItem(hwndDlg, IDC_MUTE_CHECKBOX),   FALSE);
352         EnableWindow(GetDlgItem(hwndDlg, IDC_ICON_IN_TASKBAR), FALSE);
353         EnableWindow(GetDlgItem(hwndDlg, IDC_ADVANCED_BTN),    FALSE);
354         EnableWindow(GetDlgItem(hwndDlg, IDC_SPEAKER_VOL_BTN), FALSE);
355         EnableWindow(GetDlgItem(hwndDlg, IDC_ADVANCED2_BTN),   FALSE);
356         SendDlgItemMessageW(hwndDlg, IDC_MUTE_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)pGlobalData->hIconNoHW);
357         SetDlgItemTextW(hwndDlg, IDC_DEVICE_NAME, szNoDevices);
358         return;
359     }
360 
361     if (mixerOpen(&pGlobalData->hMixer, 0, PtrToUlong(hwndDlg), 0, MIXER_OBJECTF_MIXER | CALLBACK_WINDOW) != MMSYSERR_NOERROR)
362     {
363         MessageBoxW(hwndDlg, L"Cannot open mixer", NULL, MB_OK);
364         return;
365     }
366 
367     ZeroMemory(&mxc, sizeof(MIXERCAPSW));
368     if (mixerGetDevCapsW(PtrToUint(pGlobalData->hMixer), &mxc, sizeof(MIXERCAPSW)) != MMSYSERR_NOERROR)
369     {
370         MessageBoxW(hwndDlg, L"mixerGetDevCaps failed", NULL, MB_OK);
371         return;
372     }
373 
374     CheckDlgButton(hwndDlg,
375                    IDC_ICON_IN_TASKBAR,
376                    GetSystrayVolumeIconState() ? BST_CHECKED : BST_UNCHECKED);
377 
378     GetMuteControl(pGlobalData);
379     GetMuteState(pGlobalData);
380     if (pGlobalData->muteVal)
381     {
382         SendDlgItemMessageW(hwndDlg, IDC_MUTE_CHECKBOX, BM_SETCHECK, (WPARAM)BST_CHECKED, 0);
383         SendDlgItemMessageW(hwndDlg, IDC_MUTE_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)pGlobalData->hIconMuted);
384     }
385     else
386     {
387         SendDlgItemMessageW(hwndDlg, IDC_MUTE_CHECKBOX, BM_SETCHECK, (WPARAM)BST_UNCHECKED, 0);
388         SendDlgItemMessageW(hwndDlg, IDC_MUTE_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)pGlobalData->hIconUnMuted);
389     }
390 
391     GetVolumeControl(pGlobalData);
392     GetVolumeValue(pGlobalData, TRUE);
393 
394     SendDlgItemMessageW(hwndDlg, IDC_DEVICE_NAME, WM_SETTEXT, 0, (LPARAM)mxc.szPname);
395     SendDlgItemMessageW(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)(pGlobalData->maxVolume - pGlobalData->volumeMinimum) / pGlobalData->volumeStep);
396 }
397 
398 VOID
399 SaveData(HWND hwndDlg)
400 {
401     BOOL bShowIcon;
402 
403     bShowIcon = (IsDlgButtonChecked(hwndDlg, IDC_ICON_IN_TASKBAR) == BST_CHECKED);
404 
405     SetSystrayVolumeIconState(bShowIcon);
406 }
407 
408 VOID
409 LaunchSoundControl(HWND hwndDlg)
410 {
411     if ((INT_PTR)ShellExecuteW(NULL, L"open", L"sndvol32.exe", NULL, NULL, SW_SHOWNORMAL) > 32)
412         return;
413     MessageBoxW(hwndDlg, L"Cannot run sndvol32.exe", NULL, MB_OK);
414 }
415 
416 /* Volume property page dialog callback */
417 //static INT_PTR CALLBACK
418 INT_PTR CALLBACK
419 VolumeDlgProc(HWND hwndDlg,
420               UINT uMsg,
421               WPARAM wParam,
422               LPARAM lParam)
423 {
424     static IMGINFO ImgInfo;
425     PGLOBAL_DATA pGlobalData;
426 
427     pGlobalData = (PGLOBAL_DATA)GetWindowLongPtrW(hwndDlg, DWLP_USER);
428 
429     switch (uMsg)
430     {
431         case MM_MIXM_LINE_CHANGE:
432         {
433             GetMuteState(pGlobalData);
434             if (pGlobalData->muteVal)
435             {
436                 SendDlgItemMessageW(hwndDlg, IDC_MUTE_CHECKBOX, BM_SETCHECK, (WPARAM)BST_CHECKED, 0);
437                 SendDlgItemMessageW(hwndDlg, IDC_MUTE_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)pGlobalData->hIconMuted);
438             }
439             else
440             {
441                 SendDlgItemMessageW(hwndDlg, IDC_MUTE_CHECKBOX, BM_SETCHECK, (WPARAM)BST_UNCHECKED, 0);
442                 SendDlgItemMessageW(hwndDlg, IDC_MUTE_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)pGlobalData->hIconUnMuted);
443             }
444             break;
445         }
446         case MM_MIXM_CONTROL_CHANGE:
447         {
448             GetVolumeValue(pGlobalData, FALSE);
449             SendDlgItemMessageW(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)(pGlobalData->maxVolume - pGlobalData->volumeMinimum) / pGlobalData->volumeStep);
450             break;
451         }
452         case WM_INITDIALOG:
453         {
454             pGlobalData = (PGLOBAL_DATA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(GLOBAL_DATA));
455             SetWindowLongPtrW(hwndDlg, DWLP_USER, (LONG_PTR)pGlobalData);
456 
457             pGlobalData->hIconUnMuted = LoadImageW(hApplet, MAKEINTRESOURCEW(IDI_CPLICON), IMAGE_ICON, 32, 32, LR_DEFAULTCOLOR);
458             pGlobalData->hIconMuted = LoadImageW(hApplet, MAKEINTRESOURCEW(IDI_MUTED_ICON), IMAGE_ICON, 32, 32, LR_DEFAULTCOLOR);
459             pGlobalData->hIconNoHW = LoadImageW(hApplet, MAKEINTRESOURCEW(IDI_NO_HW), IMAGE_ICON, 32, 32, LR_DEFAULTCOLOR);
460 
461             InitImageInfo(&ImgInfo);
462             InitVolumeControls(hwndDlg, pGlobalData);
463             break;
464         }
465 
466         case WM_DRAWITEM:
467         {
468             LPDRAWITEMSTRUCT lpDrawItem;
469             lpDrawItem = (LPDRAWITEMSTRUCT)lParam;
470             if (lpDrawItem->CtlID == IDC_SPEAKIMG)
471             {
472                 HDC hdcMem;
473                 LONG left;
474 
475                 /* Position image in centre of dialog */
476                 left = (lpDrawItem->rcItem.right - ImgInfo.cxSource) / 2;
477 
478                 hdcMem = CreateCompatibleDC(lpDrawItem->hDC);
479                 if (hdcMem != NULL)
480                 {
481                     SelectObject(hdcMem, ImgInfo.hBitmap);
482                     BitBlt(lpDrawItem->hDC,
483                            left,
484                            lpDrawItem->rcItem.top,
485                            lpDrawItem->rcItem.right - lpDrawItem->rcItem.left,
486                            lpDrawItem->rcItem.bottom - lpDrawItem->rcItem.top,
487                            hdcMem,
488                            0,
489                            0,
490                            SRCCOPY);
491                     DeleteDC(hdcMem);
492                 }
493             }
494             break;
495         }
496 
497         case WM_COMMAND:
498         {
499             switch (LOWORD(wParam))
500             {
501                 case IDC_MUTE_CHECKBOX:
502                     if (HIWORD(wParam) == BN_CLICKED)
503                     {
504                         SwitchMuteState(pGlobalData);
505                         if (pGlobalData->muteVal)
506                         {
507                             SendDlgItemMessageW(hwndDlg, IDC_MUTE_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)pGlobalData->hIconMuted);
508                         }
509                         else
510                         {
511                             SendDlgItemMessageW(hwndDlg, IDC_MUTE_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)pGlobalData->hIconUnMuted);
512                         }
513 
514                         PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
515                     }
516                     break;
517 
518                 case IDC_ICON_IN_TASKBAR:
519                     if (HIWORD(wParam) == BN_CLICKED)
520                     {
521                         PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
522                     }
523                     break;
524 
525                 case IDC_ADVANCED_BTN:
526                     LaunchSoundControl(hwndDlg);
527                     break;
528 
529                 case IDC_SPEAKER_VOL_BTN:
530                     SpeakerVolume(hwndDlg);
531                     break;
532             }
533             break;
534         }
535 
536         case WM_HSCROLL:
537         {
538             HWND hVolumeTrackbar = GetDlgItem(hwndDlg, IDC_VOLUME_TRACKBAR);
539             if (hVolumeTrackbar == (HWND)lParam)
540             {
541                 switch (LOWORD(wParam))
542                 {
543                     case TB_THUMBPOSITION:
544                         break;
545 
546                     case TB_ENDTRACK:
547                         PlaySoundW((LPCWSTR)SND_ALIAS_SYSTEMDEFAULT, NULL, SND_ALIAS_ID | SND_ASYNC);
548                         break;
549 
550                     default:
551                         SetVolumeValue(pGlobalData,
552                                        (DWORD)SendDlgItemMessageW(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_GETPOS, 0, 0));
553                         break;
554                 }
555             }
556             break;
557         }
558 
559         case WM_DESTROY:
560             if (pGlobalData)
561             {
562                 if (pGlobalData->volumeCurrentValues)
563                     HeapFree(GetProcessHeap(), 0, pGlobalData->volumeCurrentValues);
564 
565                 if (pGlobalData->volumePreviousValues)
566                     HeapFree(GetProcessHeap(), 0, pGlobalData->volumePreviousValues);
567 
568                 if (pGlobalData->volumeInitialValues)
569                     HeapFree(GetProcessHeap(), 0, pGlobalData->volumeInitialValues);
570 
571                 mixerClose(pGlobalData->hMixer);
572                 DestroyIcon(pGlobalData->hIconMuted);
573                 DestroyIcon(pGlobalData->hIconUnMuted);
574                 DestroyIcon(pGlobalData->hIconNoHW);
575                 HeapFree(GetProcessHeap(), 0, pGlobalData);
576             }
577             break;
578 
579         case WM_NOTIFY:
580             switch (((LPNMHDR)lParam)->code)
581             {
582                 case PSN_APPLY:
583                     SaveData(hwndDlg);
584                     break;
585 
586                 case PSN_RESET:
587                     RestoreVolumeValue(pGlobalData);
588                     break;
589             }
590             return TRUE;
591     }
592 
593     return FALSE;
594 }
595