1 /*
2 * PROJECT: ReactOS Multimedia Control Panel
3 * FILE: dll/cpl/mmsys/speakervolume.c
4 * PURPOSE: ReactOS Multimedia Control Panel
5 * PROGRAMMER: Eric Kohl <eric.kohl@reactos.com>
6 */
7
8 #include "mmsys.h"
9
10 typedef struct _PAGE_DATA
11 {
12 HMIXER hMixer;
13 DWORD volumeControlID;
14 DWORD volumeChannels;
15 DWORD volumeMinimum;
16 DWORD volumeMaximum;
17 DWORD volumeStep;
18 PMIXERCONTROLDETAILS_UNSIGNED volumeValues;
19 BOOL volumeSync;
20 } PAGE_DATA, *PPAGE_DATA;
21
22
23 static
24 BOOL
OnInitDialog(PPAGE_DATA pPageData,HWND hwndDlg)25 OnInitDialog(
26 PPAGE_DATA pPageData,
27 HWND hwndDlg)
28 {
29 WCHAR szBuffer[256];
30 MIXERLINEW mxln;
31 MIXERCONTROLW mxc;
32 MIXERLINECONTROLSW mxlctrl;
33 MIXERCONTROLDETAILS mxcd;
34 INT i, j;
35
36 /* Open the mixer */
37 if (mixerOpen(&pPageData->hMixer, 0, PtrToUlong(hwndDlg), 0, MIXER_OBJECTF_MIXER | CALLBACK_WINDOW) != MMSYSERR_NOERROR)
38 {
39 MessageBoxW(hwndDlg, L"Cannot open mixer", NULL, MB_OK);
40 return FALSE;
41 }
42
43 /* Retrieve the mixer information */
44 mxln.cbStruct = sizeof(MIXERLINEW);
45 mxln.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
46
47 if (mixerGetLineInfoW((HMIXEROBJ)pPageData->hMixer, &mxln, MIXER_OBJECTF_HMIXER | MIXER_GETLINEINFOF_COMPONENTTYPE) != MMSYSERR_NOERROR)
48 return FALSE;
49
50 pPageData->volumeChannels = mxln.cChannels;
51
52 /* Retrieve the line information */
53 mxlctrl.cbStruct = sizeof(MIXERLINECONTROLSW);
54 mxlctrl.dwLineID = mxln.dwLineID;
55 mxlctrl.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
56 mxlctrl.cControls = 1;
57 mxlctrl.cbmxctrl = sizeof(MIXERCONTROLW);
58 mxlctrl.pamxctrl = &mxc;
59
60 if (mixerGetLineControlsW((HMIXEROBJ)pPageData->hMixer, &mxlctrl, MIXER_OBJECTF_HMIXER | MIXER_GETLINECONTROLSF_ONEBYTYPE) != MMSYSERR_NOERROR)
61 return FALSE;
62
63 pPageData->volumeControlID = mxc.dwControlID;
64 pPageData->volumeMinimum = mxc.Bounds.dwMinimum;
65 pPageData->volumeMaximum = mxc.Bounds.dwMaximum;
66 pPageData->volumeStep = (pPageData->volumeMaximum - pPageData->volumeMinimum) / (VOLUME_MAX - VOLUME_MIN);
67
68 /* Allocate a buffer for all channel volume values */
69 pPageData->volumeValues = HeapAlloc(GetProcessHeap(),
70 0,
71 mxln.cChannels * sizeof(MIXERCONTROLDETAILS_UNSIGNED));
72 if (pPageData->volumeValues == NULL)
73 return FALSE;
74
75 /* Retrieve the channel volume values */
76 mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
77 mxcd.dwControlID = mxc.dwControlID;
78 mxcd.cChannels = mxln.cChannels;
79 mxcd.cMultipleItems = 0;
80 mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
81 mxcd.paDetails = pPageData->volumeValues;
82
83 if (mixerGetControlDetailsW((HMIXEROBJ)pPageData->hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_GETCONTROLDETAILSF_VALUE) != MMSYSERR_NOERROR)
84 return FALSE;
85
86 /* Initialize the channels */
87 for (i = 0; i < min(mxln.cChannels, 5); i++)
88 {
89 j = i * 4;
90
91 /* Set the channel name */
92 LoadStringW(hApplet, IDS_SPEAKER_LEFT + i, szBuffer, _countof(szBuffer));
93 SetWindowTextW(GetDlgItem(hwndDlg, 9472 + j), szBuffer);
94
95 /* Initialize the channel trackbar */
96 SendDlgItemMessageW(hwndDlg, 9475 + j, TBM_SETRANGE, (WPARAM)TRUE, MAKELPARAM(VOLUME_MIN, VOLUME_MAX));
97 SendDlgItemMessageW(hwndDlg, 9475 + j, TBM_SETTICFREQ, VOLUME_TICFREQ, 0);
98 SendDlgItemMessageW(hwndDlg, 9475 + j, TBM_SETPAGESIZE, 0, VOLUME_PAGESIZE);
99 SendDlgItemMessageW(hwndDlg, 9475 + j, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)(pPageData->volumeValues[i].dwValue - pPageData->volumeMinimum) / pPageData->volumeStep);
100 }
101
102 /* Hide the unused controls */
103 for (i = mxln.cChannels; i < 8; i++)
104 {
105 j = i * 4;
106 ShowWindow(GetDlgItem(hwndDlg, 9472 + j), SW_HIDE);
107 ShowWindow(GetDlgItem(hwndDlg, 9473 + j), SW_HIDE);
108 ShowWindow(GetDlgItem(hwndDlg, 9474 + j), SW_HIDE);
109 ShowWindow(GetDlgItem(hwndDlg, 9475 + j), SW_HIDE);
110 }
111
112 return TRUE;
113 }
114
115
116 static
117 VOID
OnMixerControlChange(PPAGE_DATA pPageData,HWND hwndDlg)118 OnMixerControlChange(
119 PPAGE_DATA pPageData,
120 HWND hwndDlg)
121 {
122 MIXERCONTROLDETAILS mxcd;
123 INT i, j;
124
125 /* Retrieve the channel volume values */
126 mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
127 mxcd.dwControlID = pPageData->volumeControlID;
128 mxcd.cChannels = pPageData->volumeChannels;
129 mxcd.cMultipleItems = 0;
130 mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
131 mxcd.paDetails = pPageData->volumeValues;
132
133 if (mixerGetControlDetailsW((HMIXEROBJ)pPageData->hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_GETCONTROLDETAILSF_VALUE) != MMSYSERR_NOERROR)
134 return;
135
136 for (i = 0; i < pPageData->volumeChannels; i++)
137 {
138 j = i * 4;
139
140 SendDlgItemMessageW(hwndDlg, 9475 + j, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)(pPageData->volumeValues[i].dwValue - pPageData->volumeMinimum) / pPageData->volumeStep);
141 }
142 }
143
144
145 static
146 VOID
OnHScroll(PPAGE_DATA pPageData,HWND hwndDlg,WPARAM wParam,LPARAM lParam)147 OnHScroll(
148 PPAGE_DATA pPageData,
149 HWND hwndDlg,
150 WPARAM wParam,
151 LPARAM lParam)
152 {
153 MIXERCONTROLDETAILS mxcd;
154 DWORD dwValue, dwPosition;
155 INT id, idx, i, j;
156
157 id = (INT)GetWindowLongPtrW((HWND)lParam, GWLP_ID);
158 if (id < 9475 || id > 9503)
159 return;
160
161 if ((id - 9475) % 4 != 0)
162 return;
163
164 dwPosition = (DWORD)SendDlgItemMessageW(hwndDlg, id, TBM_GETPOS, 0, 0);
165
166 if (dwPosition == VOLUME_MIN)
167 dwValue = pPageData->volumeMinimum;
168 else if (dwPosition == VOLUME_MAX)
169 dwValue = pPageData->volumeMaximum;
170 else
171 dwValue = (dwPosition * pPageData->volumeStep) + pPageData->volumeMinimum;
172
173 if (pPageData->volumeSync)
174 {
175 for (i = 0; i < pPageData->volumeChannels; i++)
176 {
177 j = 9475 + (i * 4);
178 if (j != id)
179 SendDlgItemMessageW(hwndDlg, j, TBM_SETPOS, (WPARAM)TRUE, dwPosition);
180
181 pPageData->volumeValues[i].dwValue = dwValue;
182 }
183 }
184 else
185 {
186 idx = (id - 9475) / 4;
187 pPageData->volumeValues[idx].dwValue = dwValue;
188 }
189
190 mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
191 mxcd.dwControlID = pPageData->volumeControlID;
192 mxcd.cChannels = pPageData->volumeChannels;
193 mxcd.cMultipleItems = 0;
194 mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
195 mxcd.paDetails = pPageData->volumeValues;
196
197 if (mixerSetControlDetails((HMIXEROBJ)pPageData->hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE) != MMSYSERR_NOERROR)
198 return;
199 }
200
201
202 static
203 VOID
OnSetDefaults(PPAGE_DATA pPageData,HWND hwndDlg)204 OnSetDefaults(
205 PPAGE_DATA pPageData,
206 HWND hwndDlg)
207 {
208 MIXERCONTROLDETAILS mxcd;
209 DWORD dwValue, i;
210
211 dwValue = ((VOLUME_MAX - VOLUME_MIN) / 2 * pPageData->volumeStep) + pPageData->volumeMinimum;
212
213 for (i = 0; i < pPageData->volumeChannels; i++)
214 pPageData->volumeValues[i].dwValue = dwValue;
215
216 mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
217 mxcd.dwControlID = pPageData->volumeControlID;
218 mxcd.cChannels = pPageData->volumeChannels;
219 mxcd.cMultipleItems = 0;
220 mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
221 mxcd.paDetails = pPageData->volumeValues;
222
223 if (mixerSetControlDetails((HMIXEROBJ)pPageData->hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE) != MMSYSERR_NOERROR)
224 return;
225 }
226
227
228 INT_PTR
229 CALLBACK
SpeakerVolumeDlgProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)230 SpeakerVolumeDlgProc(
231 HWND hwndDlg,
232 UINT uMsg,
233 WPARAM wParam,
234 LPARAM lParam)
235 {
236 PPAGE_DATA pPageData;
237
238 pPageData = (PPAGE_DATA)GetWindowLongPtrW(hwndDlg, DWLP_USER);
239
240 switch (uMsg)
241 {
242 case WM_INITDIALOG:
243 pPageData = (PPAGE_DATA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PAGE_DATA));
244 SetWindowLongPtrW(hwndDlg, DWLP_USER, (LONG_PTR)pPageData);
245
246 if (pPageData)
247 {
248 OnInitDialog(pPageData, hwndDlg);
249 }
250 break;
251
252 case WM_DESTROY:
253 if (pPageData)
254 {
255 if (pPageData->volumeValues)
256 HeapFree(GetProcessHeap(), 0, pPageData->volumeValues);
257
258 if (pPageData->hMixer)
259 mixerClose(pPageData->hMixer);
260
261 HeapFree(GetProcessHeap(), 0, pPageData);
262 pPageData = NULL;
263 SetWindowLongPtrW(hwndDlg, DWLP_USER, (LONG_PTR)NULL);
264 }
265 break;
266
267 case WM_HSCROLL:
268 if (pPageData)
269 {
270 OnHScroll(pPageData, hwndDlg, wParam, lParam);
271 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
272 }
273 break;
274
275 case WM_COMMAND:
276 switch (LOWORD(wParam))
277 {
278 case 9504:
279 if (HIWORD(wParam) == BN_CLICKED)
280 pPageData->volumeSync = (SendDlgItemMessageW(hwndDlg, 9504, BM_GETCHECK, 0, 0) == BST_CHECKED);
281 break;
282
283 case 9505:
284 OnSetDefaults(pPageData, hwndDlg);
285 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
286 break;
287 }
288 break;
289
290 case WM_NOTIFY:
291 return TRUE;
292
293 case MM_MIXM_CONTROL_CHANGE:
294 if (pPageData)
295 OnMixerControlChange(pPageData, hwndDlg);
296 break;
297 }
298
299 return FALSE;
300 }
301
302
303 INT_PTR
SpeakerVolume(HWND hwndDlg)304 SpeakerVolume(
305 HWND hwndDlg)
306 {
307 PROPSHEETPAGEW psp[1];
308 PROPSHEETHEADERW psh;
309
310 ZeroMemory(&psh, sizeof(PROPSHEETHEADERW));
311 psh.dwSize = sizeof(PROPSHEETHEADERW);
312 psh.dwFlags = PSH_PROPSHEETPAGE;
313 psh.hwndParent = hwndDlg;
314 psh.hInstance = hApplet;
315 psh.pszCaption = MAKEINTRESOURCE(IDS_SPEAKER_VOLUME);
316 psh.nPages = _countof(psp);
317 psh.nStartPage = 0;
318 psh.ppsp = psp;
319
320 InitPropSheetPage(&psp[0], IDD_MULTICHANNEL, SpeakerVolumeDlgProc);
321 psp[0].dwFlags |= PSP_USETITLE;
322 psp[0].hInstance = hApplet;
323 psp[0].pszTitle = MAKEINTRESOURCE(IDS_SPEAKER_VOLUME);
324
325 return (LONG)(PropertySheetW(&psh) != -1);
326 }
327