xref: /reactos/dll/cpl/mmsys/speakervolume.c (revision 58aee30e)
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, dwPos;
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     dwPos = (DWORD)SendDlgItemMessage(hwndDlg, id, TBM_GETPOS, 0, 0);
165     dwValue = (dwPos * pPageData->volumeStep) + pPageData->volumeMinimum;
166 
167     if (pPageData->volumeSync)
168     {
169         for (i = 0; i < pPageData->volumeChannels; i++)
170         {
171             j = 9475 + (i * 4);
172             if (j != id)
173                 SendDlgItemMessage(hwndDlg, j, TBM_SETPOS, (WPARAM)TRUE, dwPos);
174 
175             pPageData->volumeValues[i].dwValue = dwValue;
176         }
177     }
178     else
179     {
180         idx = (id - 9475) / 4;
181         pPageData->volumeValues[idx].dwValue = dwValue;
182     }
183 
184     mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
185     mxcd.dwControlID = pPageData->volumeControlID;
186     mxcd.cChannels = pPageData->volumeChannels;
187     mxcd.cMultipleItems = 0;
188     mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
189     mxcd.paDetails = pPageData->volumeValues;
190 
191     if (mixerSetControlDetails((HMIXEROBJ)pPageData->hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE) != MMSYSERR_NOERROR)
192         return;
193 }
194 
195 
196 static
197 VOID
198 OnSetDefaults(
199     PPAGE_DATA pPageData,
200     HWND hwndDlg)
201 {
202     MIXERCONTROLDETAILS mxcd;
203     DWORD dwValue, i;
204 
205     dwValue = ((VOLUME_MAX - VOLUME_MIN) / 2 * pPageData->volumeStep) + pPageData->volumeMinimum;
206 
207     for (i = 0; i < pPageData->volumeChannels; i++)
208         pPageData->volumeValues[i].dwValue = dwValue;
209 
210     mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
211     mxcd.dwControlID = pPageData->volumeControlID;
212     mxcd.cChannels = pPageData->volumeChannels;
213     mxcd.cMultipleItems = 0;
214     mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
215     mxcd.paDetails = pPageData->volumeValues;
216 
217     if (mixerSetControlDetails((HMIXEROBJ)pPageData->hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE) != MMSYSERR_NOERROR)
218         return;
219 }
220 
221 
222 INT_PTR
223 CALLBACK
224 SpeakerVolumeDlgProc(
225     HWND hwndDlg,
226     UINT uMsg,
227     WPARAM wParam,
228     LPARAM lParam)
229 {
230     PPAGE_DATA pPageData;
231 
232     UNREFERENCED_PARAMETER(wParam);
233 
234     pPageData = (PPAGE_DATA)GetWindowLongPtr(hwndDlg, DWLP_USER);
235 
236     switch(uMsg)
237     {
238         case WM_INITDIALOG:
239             pPageData = (PPAGE_DATA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PAGE_DATA));
240             SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pPageData);
241 
242             if (pPageData)
243             {
244                 OnInitDialog(pPageData, hwndDlg);
245             }
246             break;
247 
248         case WM_DESTROY:
249             if (pPageData)
250             {
251                 if (pPageData->volumeValues)
252                     HeapFree(GetProcessHeap(), 0, pPageData->volumeValues);
253 
254                 if (pPageData->hMixer)
255                     mixerClose(pPageData->hMixer);
256 
257                 HeapFree(GetProcessHeap(), 0, pPageData);
258                 pPageData = NULL;
259                 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)NULL);
260             }
261             break;
262 
263         case WM_HSCROLL:
264             if (pPageData)
265             {
266                 OnHScroll(pPageData, hwndDlg, wParam, lParam);
267                 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
268             }
269             break;
270 
271         case WM_COMMAND:
272             switch (LOWORD(wParam))
273             {
274                 case 9504:
275                     if (HIWORD(wParam) == BN_CLICKED)
276                         pPageData->volumeSync = (SendDlgItemMessage(hwndDlg, 9504, BM_GETCHECK, 0, 0) == BST_CHECKED);
277                     break;
278 
279                 case 9505:
280                     OnSetDefaults(pPageData, hwndDlg);
281                     PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
282                     break;
283             }
284             break;
285 
286         case WM_NOTIFY:
287             return TRUE;
288 
289         case MM_MIXM_CONTROL_CHANGE:
290             if (pPageData)
291                 OnMixerControlChange(pPageData, hwndDlg);
292             break;
293     }
294 
295     return FALSE;
296 }
297 
298 
299 INT_PTR
300 SpeakerVolume(
301     HWND hwndDlg)
302 {
303     PROPSHEETPAGE psp[1];
304     PROPSHEETHEADER psh;
305     TCHAR Caption[256];
306 
307     LoadString(hApplet, IDS_SPEAKER_VOLUME, Caption, _countof(Caption));
308 
309     ZeroMemory(&psh, sizeof(PROPSHEETHEADER));
310     psh.dwSize = sizeof(PROPSHEETHEADER);
311     psh.dwFlags =  PSH_PROPSHEETPAGE;
312     psh.hwndParent = hwndDlg;
313     psh.hInstance = hApplet;
314     psh.pszCaption = Caption;
315     psh.nPages = sizeof(psp) / sizeof(PROPSHEETPAGE);
316     psh.nStartPage = 0;
317     psh.ppsp = psp;
318 
319     InitPropSheetPage(&psp[0], IDD_MULTICHANNEL, SpeakerVolumeDlgProc);
320     psp[0].dwFlags |= PSP_USETITLE;
321     psp[0].pszTitle = Caption;
322 
323     return (LONG)(PropertySheet(&psh) != -1);
324 }
325