xref: /reactos/dll/cpl/mmsys/speakervolume.c (revision f308c6a2)
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
25 OnInitDialog(
26     PPAGE_DATA pPageData,
27     HWND hwndDlg)
28 {
29     TCHAR szBuffer[256];
30     MIXERLINE mxln;
31     MIXERCONTROL mxc;
32     MIXERLINECONTROLS 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         MessageBox(hwndDlg, _T("Cannot open mixer"), NULL, MB_OK);
40         return FALSE;
41     }
42 
43     /* Retrieve the mixer information */
44     mxln.cbStruct = sizeof(MIXERLINE);
45     mxln.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
46 
47     if (mixerGetLineInfo((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(MIXERLINECONTROLS);
54     mxlctrl.dwLineID = mxln.dwLineID;
55     mxlctrl.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
56     mxlctrl.cControls = 1;
57     mxlctrl.cbmxctrl = sizeof(MIXERCONTROL);
58     mxlctrl.pamxctrl = &mxc;
59 
60     if (mixerGetLineControls((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 (mixerGetControlDetails((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         LoadString(hApplet, IDS_SPEAKER_LEFT + i, szBuffer, _countof(szBuffer));
93         SetWindowText(GetDlgItem(hwndDlg, 9472 + j), szBuffer);
94 
95         /* Initialize the channel trackbar */
96         SendDlgItemMessage(hwndDlg, 9475 + j, TBM_SETRANGE, (WPARAM)TRUE, (LPARAM)MAKELONG(VOLUME_MIN, VOLUME_MAX));
97         SendDlgItemMessage(hwndDlg, 9475 + j, TBM_SETTICFREQ, VOLUME_TICFREQ, 0);
98         SendDlgItemMessage(hwndDlg, 9475 + j, TBM_SETPAGESIZE, 0, VOLUME_PAGESIZE);
99         SendDlgItemMessage(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
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 (mixerGetControlDetails((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         SendDlgItemMessage(hwndDlg, 9475 + j, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)(pPageData->volumeValues[i].dwValue - pPageData->volumeMinimum) / pPageData->volumeStep);
141     }
142 }
143 
144 
145 static
146 VOID
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)GetWindowLongPtr((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)SendDlgItemMessage(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                 SendDlgItemMessage(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
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
230 SpeakerVolumeDlgProc(
231     HWND hwndDlg,
232     UINT uMsg,
233     WPARAM wParam,
234     LPARAM lParam)
235 {
236     PPAGE_DATA pPageData;
237 
238     UNREFERENCED_PARAMETER(wParam);
239 
240     pPageData = (PPAGE_DATA)GetWindowLongPtr(hwndDlg, DWLP_USER);
241 
242     switch(uMsg)
243     {
244         case WM_INITDIALOG:
245             pPageData = (PPAGE_DATA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PAGE_DATA));
246             SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pPageData);
247 
248             if (pPageData)
249             {
250                 OnInitDialog(pPageData, hwndDlg);
251             }
252             break;
253 
254         case WM_DESTROY:
255             if (pPageData)
256             {
257                 if (pPageData->volumeValues)
258                     HeapFree(GetProcessHeap(), 0, pPageData->volumeValues);
259 
260                 if (pPageData->hMixer)
261                     mixerClose(pPageData->hMixer);
262 
263                 HeapFree(GetProcessHeap(), 0, pPageData);
264                 pPageData = NULL;
265                 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)NULL);
266             }
267             break;
268 
269         case WM_HSCROLL:
270             if (pPageData)
271             {
272                 OnHScroll(pPageData, hwndDlg, wParam, lParam);
273                 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
274             }
275             break;
276 
277         case WM_COMMAND:
278             switch (LOWORD(wParam))
279             {
280                 case 9504:
281                     if (HIWORD(wParam) == BN_CLICKED)
282                         pPageData->volumeSync = (SendDlgItemMessage(hwndDlg, 9504, BM_GETCHECK, 0, 0) == BST_CHECKED);
283                     break;
284 
285                 case 9505:
286                     OnSetDefaults(pPageData, hwndDlg);
287                     PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
288                     break;
289             }
290             break;
291 
292         case WM_NOTIFY:
293             return TRUE;
294 
295         case MM_MIXM_CONTROL_CHANGE:
296             if (pPageData)
297                 OnMixerControlChange(pPageData, hwndDlg);
298             break;
299     }
300 
301     return FALSE;
302 }
303 
304 
305 INT_PTR
306 SpeakerVolume(
307     HWND hwndDlg)
308 {
309     PROPSHEETPAGE psp[1];
310     PROPSHEETHEADER psh;
311 
312     ZeroMemory(&psh, sizeof(PROPSHEETHEADER));
313     psh.dwSize = sizeof(PROPSHEETHEADER);
314     psh.dwFlags =  PSH_PROPSHEETPAGE;
315     psh.hwndParent = hwndDlg;
316     psh.hInstance = hApplet;
317     psh.pszCaption = MAKEINTRESOURCE(IDS_SPEAKER_VOLUME);
318     psh.nPages = sizeof(psp) / sizeof(PROPSHEETPAGE);
319     psh.nStartPage = 0;
320     psh.ppsp = psp;
321 
322     InitPropSheetPage(&psp[0], IDD_MULTICHANNEL, SpeakerVolumeDlgProc);
323     psp[0].dwFlags |= PSP_USETITLE;
324     psp[0].hInstance = hApplet;
325     psp[0].pszTitle = MAKEINTRESOURCE(IDS_SPEAKER_VOLUME);
326 
327     return (LONG)(PropertySheet(&psh) != -1);
328 }
329