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