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>
15 typedef struct _IMGINFO
23 typedef struct _GLOBAL_DATA
33 DWORD volumeControlID
;
40 PMIXERCONTROLDETAILS_UNSIGNED volumeInitialValues
;
41 PMIXERCONTROLDETAILS_UNSIGNED volumePreviousValues
;
42 PMIXERCONTROLDETAILS_UNSIGNED volumeCurrentValues
;
44 } GLOBAL_DATA
, *PGLOBAL_DATA
;
48 InitImageInfo(PIMGINFO ImgInfo
)
52 ZeroMemory(ImgInfo
, sizeof(*ImgInfo
));
54 ImgInfo
->hBitmap
= LoadImage(hApplet
,
55 MAKEINTRESOURCE(IDB_SPEAKIMG
),
61 if (ImgInfo
->hBitmap
!= NULL
)
63 GetObject(ImgInfo
->hBitmap
, sizeof(BITMAP
), &bitmap
);
65 ImgInfo
->cxSource
= bitmap
.bmWidth
;
66 ImgInfo
->cySource
= bitmap
.bmHeight
;
72 GetMuteControl(PGLOBAL_DATA pGlobalData
)
76 MIXERLINECONTROLS mxlctrl
;
78 if (pGlobalData
->hMixer
== NULL
)
81 mxln
.cbStruct
= sizeof(MIXERLINE
);
82 mxln
.dwComponentType
= MIXERLINE_COMPONENTTYPE_DST_SPEAKERS
;
84 if (mixerGetLineInfo((HMIXEROBJ
)pGlobalData
->hMixer
, &mxln
, MIXER_OBJECTF_HMIXER
| MIXER_GETLINEINFOF_COMPONENTTYPE
)
85 != MMSYSERR_NOERROR
) return;
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
;
94 if (mixerGetLineControls((HMIXEROBJ
)pGlobalData
->hMixer
, &mxlctrl
, MIXER_OBJECTF_HMIXER
| MIXER_GETLINECONTROLSF_ONEBYTYPE
)
95 != MMSYSERR_NOERROR
) return;
97 pGlobalData
->muteControlID
= mxc
.dwControlID
;
102 GetMuteState(PGLOBAL_DATA pGlobalData
)
104 MIXERCONTROLDETAILS_BOOLEAN mxcdMute
;
105 MIXERCONTROLDETAILS mxcd
;
107 if (pGlobalData
->hMixer
== NULL
)
110 mxcd
.cbStruct
= sizeof(MIXERCONTROLDETAILS
);
111 mxcd
.dwControlID
= pGlobalData
->muteControlID
;
113 mxcd
.cMultipleItems
= 0;
114 mxcd
.cbDetails
= sizeof(MIXERCONTROLDETAILS_BOOLEAN
);
115 mxcd
.paDetails
= &mxcdMute
;
117 if (mixerGetControlDetails((HMIXEROBJ
)pGlobalData
->hMixer
, &mxcd
, MIXER_OBJECTF_HMIXER
| MIXER_GETCONTROLDETAILSF_VALUE
)
121 pGlobalData
->muteVal
= mxcdMute
.fValue
;
126 SwitchMuteState(PGLOBAL_DATA pGlobalData
)
128 MIXERCONTROLDETAILS_BOOLEAN mxcdMute
;
129 MIXERCONTROLDETAILS mxcd
;
131 mxcd
.cbStruct
= sizeof(MIXERCONTROLDETAILS
);
132 mxcd
.dwControlID
= pGlobalData
->muteControlID
;
134 mxcd
.cMultipleItems
= 0;
135 mxcd
.cbDetails
= sizeof(MIXERCONTROLDETAILS_BOOLEAN
);
136 mxcd
.paDetails
= &mxcdMute
;
138 mxcdMute
.fValue
= !pGlobalData
->muteVal
;
139 if (mixerSetControlDetails((HMIXEROBJ
)pGlobalData
->hMixer
, &mxcd
, MIXER_OBJECTF_HMIXER
| MIXER_SETCONTROLDETAILSF_VALUE
)
143 pGlobalData
->muteVal
= mxcdMute
.fValue
;
148 GetVolumeControl(PGLOBAL_DATA pGlobalData
)
152 MIXERLINECONTROLS mxlc
;
154 if (pGlobalData
->hMixer
== NULL
)
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
)
163 pGlobalData
->volumeChannels
= mxln
.cChannels
;
165 mxlc
.cbStruct
= sizeof(MIXERLINECONTROLS
);
166 mxlc
.dwLineID
= mxln
.dwLineID
;
167 mxlc
.dwControlType
= MIXERCONTROL_CONTROLTYPE_VOLUME
;
169 mxlc
.cbmxctrl
= sizeof(MIXERCONTROL
);
170 mxlc
.pamxctrl
= &mxc
;
171 if (mixerGetLineControls((HMIXEROBJ
)pGlobalData
->hMixer
, &mxlc
, MIXER_OBJECTF_HMIXER
| MIXER_GETLINECONTROLSF_ONEBYTYPE
)
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
);
180 pGlobalData
->volumeInitialValues
= HeapAlloc(GetProcessHeap(),
182 mxln
.cChannels
* sizeof(MIXERCONTROLDETAILS_UNSIGNED
));
183 if (pGlobalData
->volumeInitialValues
== NULL
)
186 pGlobalData
->volumePreviousValues
= HeapAlloc(GetProcessHeap(),
188 mxln
.cChannels
* sizeof(MIXERCONTROLDETAILS_UNSIGNED
));
189 if (pGlobalData
->volumePreviousValues
== NULL
)
192 pGlobalData
->volumeCurrentValues
= HeapAlloc(GetProcessHeap(),
194 mxln
.cChannels
* sizeof(MIXERCONTROLDETAILS_UNSIGNED
));
195 if (pGlobalData
->volumeCurrentValues
== NULL
)
202 PGLOBAL_DATA pGlobalData
,
205 MIXERCONTROLDETAILS mxcd
;
208 if (pGlobalData
->hMixer
== NULL
)
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
;
218 if (mixerGetControlDetails((HMIXEROBJ
)pGlobalData
->hMixer
, &mxcd
, MIXER_OBJECTF_HMIXER
| MIXER_GETCONTROLDETAILSF_VALUE
)
222 pGlobalData
->maxVolume
= 0;
223 for (i
= 0; i
< pGlobalData
->volumeChannels
; i
++)
225 pGlobalData
->volumeCurrentValues
[i
].dwValue
= pGlobalData
->volumePreviousValues
[i
].dwValue
;
227 if (pGlobalData
->volumePreviousValues
[i
].dwValue
> pGlobalData
->maxVolume
)
228 pGlobalData
->maxVolume
= pGlobalData
->volumePreviousValues
[i
].dwValue
;
231 pGlobalData
->volumeInitialValues
[i
].dwValue
= pGlobalData
->volumeCurrentValues
[i
].dwValue
;
237 SetVolumeValue(PGLOBAL_DATA pGlobalData
,
240 MIXERCONTROLDETAILS mxcd
;
243 if (pGlobalData
->hMixer
== NULL
)
246 if (dwPosition
== VOLUME_MIN
)
247 dwVolume
= pGlobalData
->volumeMinimum
;
248 else if (dwPosition
== VOLUME_MAX
)
249 dwVolume
= pGlobalData
->volumeMaximum
;
251 dwVolume
= (dwPosition
* pGlobalData
->volumeStep
) + pGlobalData
->volumeMinimum
;
253 for (i
= 0; i
< pGlobalData
->volumeChannels
; i
++)
255 if (pGlobalData
->volumePreviousValues
[i
].dwValue
== pGlobalData
->maxVolume
)
257 pGlobalData
->volumeCurrentValues
[i
].dwValue
= dwVolume
;
261 pGlobalData
->volumeCurrentValues
[i
].dwValue
=
262 pGlobalData
->volumePreviousValues
[i
].dwValue
* dwVolume
/ pGlobalData
-> maxVolume
;
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
;
273 if (mixerSetControlDetails((HMIXEROBJ
)pGlobalData
->hMixer
, &mxcd
, MIXER_OBJECTF_HMIXER
| MIXER_SETCONTROLDETAILSF_VALUE
)
282 PGLOBAL_DATA pGlobalData
)
284 MIXERCONTROLDETAILS mxcd
;
286 if (pGlobalData
->hMixer
== NULL
)
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
;
296 mixerSetControlDetails((HMIXEROBJ
)pGlobalData
->hMixer
,
298 MIXER_OBJECTF_HMIXER
| MIXER_SETCONTROLDETAILSF_VALUE
);
304 SetSystrayVolumeIconState(BOOL bEnabled
)
308 hwndTaskBar
= FindWindowW(L
"SystemTray_Main", NULL
);
309 if (hwndTaskBar
== NULL
)
312 SendMessageW(hwndTaskBar
, WM_USER
+ 220, 4, bEnabled
);
317 GetSystrayVolumeIconState(VOID
)
321 hwndTaskBar
= FindWindowW(L
"SystemTray_Main", NULL
);
322 if (hwndTaskBar
== NULL
)
327 return (BOOL
)SendMessageW(hwndTaskBar
, WM_USER
+ 221, 4, 0);
331 InitVolumeControls(HWND hwndDlg
, PGLOBAL_DATA pGlobalData
)
335 TCHAR szNoDevices
[256];
337 LoadString(hApplet
, IDS_NO_DEVICES
, szNoDevices
, _countof(szNoDevices
));
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
);
343 NumMixers
= mixerGetNumDevs();
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
);
359 if (mixerOpen(&pGlobalData
->hMixer
, 0, PtrToUlong(hwndDlg
), 0, MIXER_OBJECTF_MIXER
| CALLBACK_WINDOW
) != MMSYSERR_NOERROR
)
361 MessageBox(hwndDlg
, _T("Cannot open mixer"), NULL
, MB_OK
);
365 ZeroMemory(&mxc
, sizeof(MIXERCAPS
));
366 if (mixerGetDevCaps(PtrToUint(pGlobalData
->hMixer
), &mxc
, sizeof(MIXERCAPS
)) != MMSYSERR_NOERROR
)
368 MessageBox(hwndDlg
, _T("mixerGetDevCaps failed"), NULL
, MB_OK
);
372 CheckDlgButton(hwndDlg
,
374 GetSystrayVolumeIconState() ? BST_CHECKED
: BST_UNCHECKED
);
376 GetMuteControl(pGlobalData
);
377 GetMuteState(pGlobalData
);
378 if (pGlobalData
->muteVal
)
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
);
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
);
389 GetVolumeControl(pGlobalData
);
390 GetVolumeValue(pGlobalData
, TRUE
);
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
);
397 SaveData(HWND hwndDlg
)
401 bShowIcon
= (IsDlgButtonChecked(hwndDlg
, IDC_ICON_IN_TASKBAR
) == BST_CHECKED
);
403 SetSystrayVolumeIconState(bShowIcon
);
407 LaunchSoundControl(HWND hwndDlg
)
409 if ((INT_PTR
)ShellExecuteW(NULL
, L
"open", L
"sndvol32.exe", NULL
, NULL
, SW_SHOWNORMAL
) > 32)
411 MessageBox(hwndDlg
, _T("Cannot run sndvol32.exe"), NULL
, MB_OK
);
414 /* Volume property page dialog callback */
415 //static INT_PTR CALLBACK
417 VolumeDlgProc(HWND hwndDlg
,
422 static IMGINFO ImgInfo
;
423 PGLOBAL_DATA pGlobalData
;
424 UNREFERENCED_PARAMETER(lParam
);
425 UNREFERENCED_PARAMETER(wParam
);
427 pGlobalData
= (PGLOBAL_DATA
)GetWindowLongPtr(hwndDlg
, DWLP_USER
);
431 case MM_MIXM_LINE_CHANGE
:
433 GetMuteState(pGlobalData
);
434 if (pGlobalData
->muteVal
)
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
);
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
);
446 case MM_MIXM_CONTROL_CHANGE
:
448 GetVolumeValue(pGlobalData
, FALSE
);
449 SendDlgItemMessage(hwndDlg
, IDC_VOLUME_TRACKBAR
, TBM_SETPOS
, (WPARAM
)TRUE
, (LPARAM
)(pGlobalData
->maxVolume
- pGlobalData
->volumeMinimum
) / pGlobalData
->volumeStep
);
454 pGlobalData
= (GLOBAL_DATA
*) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(GLOBAL_DATA
));
455 SetWindowLongPtr(hwndDlg
, DWLP_USER
, (LONG_PTR
)pGlobalData
);
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
);
461 InitImageInfo(&ImgInfo
);
462 InitVolumeControls(hwndDlg
, pGlobalData
);
468 LPDRAWITEMSTRUCT lpDrawItem
;
469 lpDrawItem
= (LPDRAWITEMSTRUCT
) lParam
;
470 if(lpDrawItem
->CtlID
== IDC_SPEAKIMG
)
475 /* Position image in centre of dialog */
476 left
= (lpDrawItem
->rcItem
.right
- ImgInfo
.cxSource
) / 2;
478 hdcMem
= CreateCompatibleDC(lpDrawItem
->hDC
);
481 SelectObject(hdcMem
, ImgInfo
.hBitmap
);
482 BitBlt(lpDrawItem
->hDC
,
484 lpDrawItem
->rcItem
.top
,
485 lpDrawItem
->rcItem
.right
- lpDrawItem
->rcItem
.left
,
486 lpDrawItem
->rcItem
.bottom
- lpDrawItem
->rcItem
.top
,
499 switch (LOWORD(wParam
))
501 case IDC_MUTE_CHECKBOX
:
502 if (HIWORD(wParam
) == BN_CLICKED
)
504 SwitchMuteState(pGlobalData
);
505 if (pGlobalData
->muteVal
)
507 SendDlgItemMessage(hwndDlg
, IDC_MUTE_ICON
, STM_SETIMAGE
, IMAGE_ICON
, (LPARAM
)pGlobalData
->hIconMuted
);
511 SendDlgItemMessage(hwndDlg
, IDC_MUTE_ICON
, STM_SETIMAGE
, IMAGE_ICON
, (LPARAM
)pGlobalData
->hIconUnMuted
);
514 PropSheet_Changed(GetParent(hwndDlg
), hwndDlg
);
518 case IDC_ICON_IN_TASKBAR
:
519 if (HIWORD(wParam
) == BN_CLICKED
)
521 PropSheet_Changed(GetParent(hwndDlg
), hwndDlg
);
525 case IDC_ADVANCED_BTN
:
526 LaunchSoundControl(hwndDlg
);
529 case IDC_SPEAKER_VOL_BTN
:
530 SpeakerVolume(hwndDlg
);
538 HWND hVolumeTrackbar
= GetDlgItem(hwndDlg
, IDC_VOLUME_TRACKBAR
);
539 if (hVolumeTrackbar
== (HWND
)lParam
)
541 switch (LOWORD(wParam
))
543 case TB_THUMBPOSITION
:
547 PlaySound((LPCTSTR
)SND_ALIAS_SYSTEMDEFAULT
, NULL
, SND_ALIAS_ID
| SND_ASYNC
);
551 SetVolumeValue(pGlobalData
,
552 (DWORD
)SendDlgItemMessage(hwndDlg
, IDC_VOLUME_TRACKBAR
, TBM_GETPOS
, 0, 0));
562 if (pGlobalData
->volumeCurrentValues
)
563 HeapFree(GetProcessHeap(), 0, pGlobalData
->volumeCurrentValues
);
565 if (pGlobalData
->volumePreviousValues
)
566 HeapFree(GetProcessHeap(), 0, pGlobalData
->volumePreviousValues
);
568 if (pGlobalData
->volumeInitialValues
)
569 HeapFree(GetProcessHeap(), 0, pGlobalData
->volumeInitialValues
);
571 mixerClose(pGlobalData
->hMixer
);
572 DestroyIcon(pGlobalData
->hIconMuted
);
573 DestroyIcon(pGlobalData
->hIconUnMuted
);
574 DestroyIcon(pGlobalData
->hIconNoHW
);
575 HeapFree(GetProcessHeap(), 0, pGlobalData
);
580 switch (((LPNMHDR
)lParam
)->code
)
587 RestoreVolumeValue(pGlobalData
);