xref: /reactos/dll/cpl/mmsys/volume.c (revision 2196a06f)
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 = LoadImage(hApplet,
55                                  MAKEINTRESOURCE(IDB_SPEAKIMG),
56                                  IMAGE_BITMAP,
57                                  0,
58                                  0,
59                                  LR_DEFAULTCOLOR);
60 
61     if (ImgInfo->hBitmap != NULL)
62     {
63         GetObject(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     MIXERLINE mxln;
75     MIXERCONTROL mxc;
76     MIXERLINECONTROLS mxlctrl;
77 
78     if (pGlobalData->hMixer == NULL)
79         return;
80 
81     mxln.cbStruct = sizeof(MIXERLINE);
82     mxln.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
83 
84     if (mixerGetLineInfo((HMIXEROBJ)pGlobalData->hMixer, &mxln, MIXER_OBJECTF_HMIXER | MIXER_GETLINEINFOF_COMPONENTTYPE)
85         != MMSYSERR_NOERROR) return;
86 
87     mxlctrl.cbStruct = sizeof(MIXERLINECONTROLS);
88     mxlctrl.dwLineID = mxln.dwLineID;
89     mxlctrl.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
90     mxlctrl.cControls = 1;
91     mxlctrl.cbmxctrl = sizeof(MIXERCONTROL);
92     mxlctrl.pamxctrl = &mxc;
93 
94     if (mixerGetLineControls((HMIXEROBJ)pGlobalData->hMixer, &mxlctrl, MIXER_OBJECTF_HMIXER | MIXER_GETLINECONTROLSF_ONEBYTYPE)
95         != MMSYSERR_NOERROR) return;
96 
97     pGlobalData->muteControlID = mxc.dwControlID;
98 }
99 
100 
101 VOID
102 GetMuteState(PGLOBAL_DATA pGlobalData)
103 {
104     MIXERCONTROLDETAILS_BOOLEAN mxcdMute;
105     MIXERCONTROLDETAILS mxcd;
106 
107     if (pGlobalData->hMixer == NULL)
108         return;
109 
110     mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
111     mxcd.dwControlID = pGlobalData->muteControlID;
112     mxcd.cChannels = 1;
113     mxcd.cMultipleItems = 0;
114     mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
115     mxcd.paDetails = &mxcdMute;
116 
117     if (mixerGetControlDetails((HMIXEROBJ)pGlobalData->hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_GETCONTROLDETAILSF_VALUE)
118         != MMSYSERR_NOERROR)
119         return;
120 
121     pGlobalData->muteVal = mxcdMute.fValue;
122 }
123 
124 
125 VOID
126 SwitchMuteState(PGLOBAL_DATA pGlobalData)
127 {
128     MIXERCONTROLDETAILS_BOOLEAN mxcdMute;
129     MIXERCONTROLDETAILS mxcd;
130 
131     mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
132     mxcd.dwControlID = pGlobalData->muteControlID;
133     mxcd.cChannels = 1;
134     mxcd.cMultipleItems = 0;
135     mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
136     mxcd.paDetails = &mxcdMute;
137 
138     mxcdMute.fValue = !pGlobalData->muteVal;
139     if (mixerSetControlDetails((HMIXEROBJ)pGlobalData->hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE)
140         != MMSYSERR_NOERROR)
141         return;
142 
143     pGlobalData->muteVal = mxcdMute.fValue;
144 }
145 
146 
147 VOID
148 GetVolumeControl(PGLOBAL_DATA pGlobalData)
149 {
150     MIXERLINE mxln;
151     MIXERCONTROL mxc;
152     MIXERLINECONTROLS mxlc;
153 
154     if (pGlobalData->hMixer == NULL)
155         return;
156 
157     mxln.cbStruct = sizeof(MIXERLINE);
158     mxln.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
159     if (mixerGetLineInfo((HMIXEROBJ)pGlobalData->hMixer, &mxln, MIXER_OBJECTF_HMIXER | MIXER_GETLINEINFOF_COMPONENTTYPE)
160         != MMSYSERR_NOERROR)
161         return;
162 
163     pGlobalData->volumeChannels = mxln.cChannels;
164 
165     mxlc.cbStruct = sizeof(MIXERLINECONTROLS);
166     mxlc.dwLineID = mxln.dwLineID;
167     mxlc.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
168     mxlc.cControls = 1;
169     mxlc.cbmxctrl = sizeof(MIXERCONTROL);
170     mxlc.pamxctrl = &mxc;
171     if (mixerGetLineControls((HMIXEROBJ)pGlobalData->hMixer, &mxlc, MIXER_OBJECTF_HMIXER | MIXER_GETLINECONTROLSF_ONEBYTYPE)
172         != MMSYSERR_NOERROR)
173         return;
174 
175     pGlobalData->volumeMinimum = mxc.Bounds.dwMinimum;
176     pGlobalData->volumeMaximum = mxc.Bounds.dwMaximum;
177     pGlobalData->volumeControlID = mxc.dwControlID;
178     pGlobalData->volumeStep = (pGlobalData->volumeMaximum - pGlobalData->volumeMinimum) / (VOLUME_MAX - VOLUME_MIN);
179 
180     pGlobalData->volumeInitialValues = HeapAlloc(GetProcessHeap(),
181                                                  0,
182                                                  mxln.cChannels * sizeof(MIXERCONTROLDETAILS_UNSIGNED));
183     if (pGlobalData->volumeInitialValues == NULL)
184         return;
185 
186     pGlobalData->volumePreviousValues = HeapAlloc(GetProcessHeap(),
187                                                   0,
188                                                   mxln.cChannels * sizeof(MIXERCONTROLDETAILS_UNSIGNED));
189     if (pGlobalData->volumePreviousValues == NULL)
190         return;
191 
192     pGlobalData->volumeCurrentValues = HeapAlloc(GetProcessHeap(),
193                                                  0,
194                                                  mxln.cChannels * sizeof(MIXERCONTROLDETAILS_UNSIGNED));
195     if (pGlobalData->volumeCurrentValues == NULL)
196         return;
197 }
198 
199 
200 VOID
201 GetVolumeValue(
202     PGLOBAL_DATA pGlobalData,
203     BOOL bInit)
204 {
205     MIXERCONTROLDETAILS mxcd;
206     DWORD i;
207 
208     if (pGlobalData->hMixer == NULL)
209         return;
210 
211     mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
212     mxcd.dwControlID = pGlobalData->volumeControlID;
213     mxcd.cChannels = pGlobalData->volumeChannels;
214     mxcd.cMultipleItems = 0;
215     mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
216     mxcd.paDetails = pGlobalData->volumePreviousValues;
217 
218     if (mixerGetControlDetails((HMIXEROBJ)pGlobalData->hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_GETCONTROLDETAILSF_VALUE)
219         != MMSYSERR_NOERROR)
220         return;
221 
222     pGlobalData->maxVolume = 0;
223     for (i = 0; i < pGlobalData->volumeChannels; i++)
224     {
225         pGlobalData->volumeCurrentValues[i].dwValue = pGlobalData->volumePreviousValues[i].dwValue;
226 
227         if (pGlobalData->volumePreviousValues[i].dwValue > pGlobalData->maxVolume)
228             pGlobalData->maxVolume = pGlobalData->volumePreviousValues[i].dwValue;
229 
230         if (bInit)
231             pGlobalData->volumeInitialValues[i].dwValue = pGlobalData->volumeCurrentValues[i].dwValue;
232     }
233 }
234 
235 
236 VOID
237 SetVolumeValue(PGLOBAL_DATA pGlobalData,
238                DWORD dwPosition)
239 {
240     MIXERCONTROLDETAILS mxcd;
241     DWORD dwVolume, i;
242 
243     if (pGlobalData->hMixer == NULL)
244         return;
245 
246     if (dwPosition == VOLUME_MIN)
247         dwVolume = pGlobalData->volumeMinimum;
248     else if (dwPosition == VOLUME_MAX)
249         dwVolume = pGlobalData->volumeMaximum;
250     else
251         dwVolume = (dwPosition * pGlobalData->volumeStep) + pGlobalData->volumeMinimum;
252 
253     for (i = 0; i < pGlobalData->volumeChannels; i++)
254     {
255         if (pGlobalData->volumePreviousValues[i].dwValue == pGlobalData->maxVolume)
256         {
257             pGlobalData->volumeCurrentValues[i].dwValue = dwVolume;
258         }
259         else
260         {
261             pGlobalData->volumeCurrentValues[i].dwValue =
262                 pGlobalData->volumePreviousValues[i].dwValue * dwVolume / pGlobalData-> maxVolume;
263         }
264     }
265 
266     mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
267     mxcd.dwControlID = pGlobalData->volumeControlID;
268     mxcd.cChannels = pGlobalData->volumeChannels;
269     mxcd.cMultipleItems = 0;
270     mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
271     mxcd.paDetails = pGlobalData->volumeCurrentValues;
272 
273     if (mixerSetControlDetails((HMIXEROBJ)pGlobalData->hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE)
274         != MMSYSERR_NOERROR)
275         return;
276 }
277 
278 
279 static
280 VOID
281 RestoreVolumeValue(
282     PGLOBAL_DATA pGlobalData)
283 {
284     MIXERCONTROLDETAILS mxcd;
285 
286     if (pGlobalData->hMixer == NULL)
287         return;
288 
289     mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
290     mxcd.dwControlID = pGlobalData->volumeControlID;
291     mxcd.cChannels = pGlobalData->volumeChannels;
292     mxcd.cMultipleItems = 0;
293     mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
294     mxcd.paDetails = pGlobalData->volumeInitialValues;
295 
296     mixerSetControlDetails((HMIXEROBJ)pGlobalData->hMixer,
297                            &mxcd,
298                            MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE);
299 }
300 
301 
302 static
303 VOID
304 SetSystrayVolumeIconState(BOOL bEnabled)
305 {
306     HWND hwndTaskBar;
307 
308     hwndTaskBar = FindWindowW(L"SystemTray_Main", NULL);
309     if (hwndTaskBar == NULL)
310         return;
311 
312     SendMessageW(hwndTaskBar, WM_USER + 220, 4, bEnabled);
313 }
314 
315 static
316 BOOL
317 GetSystrayVolumeIconState(VOID)
318 {
319     HWND hwndTaskBar;
320 
321     hwndTaskBar = FindWindowW(L"SystemTray_Main", NULL);
322     if (hwndTaskBar == NULL)
323     {
324         return FALSE;
325     }
326 
327     return (BOOL)SendMessageW(hwndTaskBar, WM_USER + 221, 4, 0);
328 }
329 
330 VOID
331 InitVolumeControls(HWND hwndDlg, PGLOBAL_DATA pGlobalData)
332 {
333     UINT NumMixers;
334     MIXERCAPS mxc;
335     TCHAR szNoDevices[256];
336 
337     LoadString(hApplet, IDS_NO_DEVICES, szNoDevices, _countof(szNoDevices));
338 
339     SendDlgItemMessage(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_SETRANGE, (WPARAM)TRUE, (LPARAM)MAKELONG(VOLUME_MIN, VOLUME_MAX));
340     SendDlgItemMessage(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_SETTICFREQ, VOLUME_TICFREQ, 0);
341     SendDlgItemMessage(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_SETPAGESIZE, 0, VOLUME_PAGESIZE);
342 
343     NumMixers = mixerGetNumDevs();
344     if (!NumMixers)
345     {
346         EnableWindow(GetDlgItem(hwndDlg, IDC_VOLUME_TRACKBAR), FALSE);
347         EnableWindow(GetDlgItem(hwndDlg, IDC_VOLUME_LOW),      FALSE);
348         EnableWindow(GetDlgItem(hwndDlg, IDC_VOLUME_HIGH),     FALSE);
349         EnableWindow(GetDlgItem(hwndDlg, IDC_MUTE_CHECKBOX),   FALSE);
350         EnableWindow(GetDlgItem(hwndDlg, IDC_ICON_IN_TASKBAR), FALSE);
351         EnableWindow(GetDlgItem(hwndDlg, IDC_ADVANCED_BTN),    FALSE);
352         EnableWindow(GetDlgItem(hwndDlg, IDC_SPEAKER_VOL_BTN), FALSE);
353         EnableWindow(GetDlgItem(hwndDlg, IDC_ADVANCED2_BTN),   FALSE);
354         SendDlgItemMessage(hwndDlg, IDC_MUTE_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)pGlobalData->hIconNoHW);
355         SetDlgItemText(hwndDlg, IDC_DEVICE_NAME, szNoDevices);
356         return;
357     }
358 
359     if (mixerOpen(&pGlobalData->hMixer, 0, PtrToUlong(hwndDlg), 0, MIXER_OBJECTF_MIXER | CALLBACK_WINDOW) != MMSYSERR_NOERROR)
360     {
361         MessageBox(hwndDlg, _T("Cannot open mixer"), NULL, MB_OK);
362         return;
363     }
364 
365     ZeroMemory(&mxc, sizeof(MIXERCAPS));
366     if (mixerGetDevCaps(PtrToUint(pGlobalData->hMixer), &mxc, sizeof(MIXERCAPS)) != MMSYSERR_NOERROR)
367     {
368         MessageBox(hwndDlg, _T("mixerGetDevCaps failed"), NULL, MB_OK);
369         return;
370     }
371 
372     CheckDlgButton(hwndDlg,
373                    IDC_ICON_IN_TASKBAR,
374                    GetSystrayVolumeIconState() ? BST_CHECKED : BST_UNCHECKED);
375 
376     GetMuteControl(pGlobalData);
377     GetMuteState(pGlobalData);
378     if (pGlobalData->muteVal)
379     {
380         SendDlgItemMessage(hwndDlg, IDC_MUTE_CHECKBOX, BM_SETCHECK, (WPARAM)BST_CHECKED, (LPARAM)0);
381         SendDlgItemMessage(hwndDlg, IDC_MUTE_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)pGlobalData->hIconMuted);
382     }
383     else
384     {
385         SendDlgItemMessage(hwndDlg, IDC_MUTE_CHECKBOX, BM_SETCHECK, (WPARAM)BST_UNCHECKED, (LPARAM)0);
386         SendDlgItemMessage(hwndDlg, IDC_MUTE_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)pGlobalData->hIconUnMuted);
387     }
388 
389     GetVolumeControl(pGlobalData);
390     GetVolumeValue(pGlobalData, TRUE);
391 
392     SendDlgItemMessage(hwndDlg, IDC_DEVICE_NAME, WM_SETTEXT, 0, (LPARAM)mxc.szPname);
393     SendDlgItemMessage(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)(pGlobalData->maxVolume - pGlobalData->volumeMinimum) / pGlobalData->volumeStep);
394 }
395 
396 VOID
397 SaveData(HWND hwndDlg)
398 {
399     BOOL bShowIcon;
400 
401     bShowIcon = (IsDlgButtonChecked(hwndDlg, IDC_ICON_IN_TASKBAR) == BST_CHECKED);
402 
403     SetSystrayVolumeIconState(bShowIcon);
404 }
405 
406 VOID
407 LaunchSoundControl(HWND hwndDlg)
408 {
409     if ((INT_PTR)ShellExecuteW(NULL, L"open", L"sndvol32.exe", NULL, NULL, SW_SHOWNORMAL) > 32)
410         return;
411     MessageBox(hwndDlg, _T("Cannot run sndvol32.exe"), NULL, MB_OK);
412 }
413 
414 /* Volume property page dialog callback */
415 //static INT_PTR CALLBACK
416 INT_PTR CALLBACK
417 VolumeDlgProc(HWND hwndDlg,
418               UINT uMsg,
419               WPARAM wParam,
420               LPARAM lParam)
421 {
422     static IMGINFO ImgInfo;
423     PGLOBAL_DATA pGlobalData;
424     UNREFERENCED_PARAMETER(lParam);
425     UNREFERENCED_PARAMETER(wParam);
426 
427     pGlobalData = (PGLOBAL_DATA)GetWindowLongPtr(hwndDlg, DWLP_USER);
428 
429     switch(uMsg)
430     {
431         case MM_MIXM_LINE_CHANGE:
432         {
433             GetMuteState(pGlobalData);
434             if (pGlobalData->muteVal)
435             {
436                 SendDlgItemMessage(hwndDlg, IDC_MUTE_CHECKBOX, BM_SETCHECK, (WPARAM)BST_CHECKED, (LPARAM)0);
437                 SendDlgItemMessage(hwndDlg, IDC_MUTE_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)pGlobalData->hIconMuted);
438             }
439             else
440             {
441                 SendDlgItemMessage(hwndDlg, IDC_MUTE_CHECKBOX, BM_SETCHECK, (WPARAM)BST_UNCHECKED, (LPARAM)0);
442                 SendDlgItemMessage(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             SendDlgItemMessage(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 = (GLOBAL_DATA*) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(GLOBAL_DATA));
455             SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pGlobalData);
456 
457             pGlobalData->hIconUnMuted = LoadImage(hApplet, MAKEINTRESOURCE(IDI_CPLICON), IMAGE_ICON, 32, 32, LR_DEFAULTCOLOR);
458             pGlobalData->hIconMuted = LoadImage(hApplet, MAKEINTRESOURCE(IDI_MUTED_ICON), IMAGE_ICON, 32, 32, LR_DEFAULTCOLOR);
459             pGlobalData->hIconNoHW = LoadImage(hApplet, MAKEINTRESOURCE(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                             SendDlgItemMessage(hwndDlg, IDC_MUTE_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)pGlobalData->hIconMuted);
508                         }
509                         else
510                         {
511                             SendDlgItemMessage(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                         PlaySound((LPCTSTR)SND_ALIAS_SYSTEMDEFAULT, NULL, SND_ALIAS_ID | SND_ASYNC);
548                         break;
549 
550                     default:
551                         SetVolumeValue(pGlobalData,
552                                        (DWORD)SendDlgItemMessage(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