xref: /reactos/dll/cpl/mmsys/speakervolume.c (revision 781c247b)
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