partly implemented the mixer selection dialog
[reactos.git] / reactos / subsys / system / sndvol32 / mixer.c
1 /*
2 * ReactOS Sound Volume Control
3 * Copyright (C) 2004-2005 Thomas Weidenmueller
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 * VMware is a registered trademark of VMware, Inc.
20 */
21 /* $Id$
22 *
23 * COPYRIGHT: See COPYING in the top level directory
24 * PROJECT: ReactOS Sound Volume Control
25 * FILE: subsys/system/sndvol32/mixer.c
26 * PROGRAMMERS: Thomas Weidenmueller <w3seek@reactos.com>
27 */
28 #include <sndvol32.h>
29
30 #define NO_MIXER_SELECTED (~0)
31
32 static VOID
33 ClearMixerCache(PSND_MIXER Mixer)
34 {
35 PSND_MIXER_DESTINATION Line, NextLine;
36 PSND_MIXER_CONNECTION Con, NextCon;
37
38 for (Line = Mixer->Lines; Line != NULL; Line = NextLine)
39 {
40 if (Line->Controls != NULL)
41 {
42 HeapFree(GetProcessHeap(),
43 0,
44 Line->Controls);
45 }
46
47 for (Con = Line->Connections; Con != NULL; Con = NextCon)
48 {
49 if (Con->Controls != NULL)
50 {
51 HeapFree(GetProcessHeap(),
52 0,
53 Con->Controls);
54 }
55
56 NextCon = Con->Next;
57 HeapFree(GetProcessHeap(),
58 0,
59 Con);
60 }
61
62 NextLine = Line->Next;
63 HeapFree(GetProcessHeap(),
64 0,
65 Line);
66 }
67 Mixer->Lines = NULL;
68 }
69
70 PSND_MIXER
71 SndMixerCreate(HWND hWndNotification)
72 {
73 PSND_MIXER Mixer = HeapAlloc(GetProcessHeap(),
74 HEAP_ZERO_MEMORY,
75 sizeof(SND_MIXER));
76 if (Mixer != NULL)
77 {
78 Mixer->hWndNotification = hWndNotification;
79 Mixer->MixersCount = mixerGetNumDevs();
80 Mixer->MixerId = NO_MIXER_SELECTED;
81
82 if (Mixer->MixersCount > 0)
83 {
84 /* select the first mixer by default */
85 SndMixerSelect(Mixer, 0);
86 }
87 }
88
89 return Mixer;
90 }
91
92 VOID
93 SndMixerDestroy(PSND_MIXER Mixer)
94 {
95 SndMixerClose(Mixer);
96 HeapFree(GetProcessHeap(),
97 0,
98 Mixer);
99 }
100
101 VOID
102 SndMixerClose(PSND_MIXER Mixer)
103 {
104 if (Mixer->hmx != NULL)
105 {
106 mixerClose(Mixer->hmx);
107 Mixer->hmx = NULL;
108 Mixer->MixerId = NO_MIXER_SELECTED;
109 }
110 }
111
112 static BOOL
113 SndMixerQueryControls(PSND_MIXER Mixer,
114 LPMIXERLINE LineInfo,
115 LPMIXERCONTROL *Controls)
116 {
117 if (LineInfo->cControls > 0)
118 {
119 *Controls = HeapAlloc(GetProcessHeap(),
120 0,
121 LineInfo->cControls * sizeof(MIXERCONTROL));
122 if (*Controls != NULL)
123 {
124 MIXERLINECONTROLS LineControls;
125 MMRESULT Result;
126 UINT j;
127
128 LineControls.cbStruct = sizeof(LineControls);
129 LineControls.dwLineID = LineInfo->dwLineID;
130 LineControls.cControls = LineInfo->cControls;
131 LineControls.cbmxctrl = sizeof(MIXERCONTROL);
132 LineControls.pamxctrl = (PVOID)(*Controls);
133
134 Result = mixerGetLineControls((HMIXEROBJ)Mixer->hmx,
135 &LineControls,
136 MIXER_GETLINECONTROLSF_ALL);
137 if (Result == MMSYSERR_NOERROR)
138 {
139 for (j = 0; j < LineControls.cControls; j++)
140 {
141 DPRINT("Line control: %ws\n", (*Controls)[j].szName);
142 }
143
144 return TRUE;
145 }
146 else
147 {
148 HeapFree(GetProcessHeap(),
149 0,
150 *Controls);
151 *Controls = NULL;
152 DPRINT("Failed to get line (ID: 0x%x) controls: %d\n", LineInfo->dwLineID, Result);
153 }
154 }
155 else
156 {
157 DPRINT("Failed to allocate memory for %d line (ID: 0x%x) controls!\n", LineInfo->dwLineID, LineInfo->cControls);
158 }
159
160 return FALSE;
161 }
162 else
163 {
164 return TRUE;
165 }
166 }
167
168 static BOOL
169 SndMixerQueryConnections(PSND_MIXER Mixer,
170 PSND_MIXER_DESTINATION Line)
171 {
172 UINT i;
173 MIXERLINE LineInfo;
174 MMRESULT Result;
175 BOOL Ret = TRUE;
176
177 LineInfo.cbStruct = sizeof(LineInfo);
178 for (i = Line->Info.cConnections; i > 0; i--)
179 {
180 LineInfo.dwDestination = Line->Info.dwDestination;
181 LineInfo.dwSource = i - 1;
182 Result = mixerGetLineInfo((HMIXEROBJ)Mixer->hmx,
183 &LineInfo,
184 MIXER_GETLINEINFOF_SOURCE);
185 if (Result == MMSYSERR_NOERROR)
186 {
187 LPMIXERCONTROL Controls;
188 PSND_MIXER_CONNECTION Con;
189
190 DPRINT("++ Source: %ws\n", LineInfo.szName);
191
192 if (!SndMixerQueryControls(Mixer,
193 &LineInfo,
194 &Controls))
195 {
196 DPRINT("Failed to query connection controls\n");
197 Ret = FALSE;
198 break;
199 }
200
201 Con = HeapAlloc(GetProcessHeap(),
202 0,
203 sizeof(SND_MIXER_CONNECTION));
204 if (Con != NULL)
205 {
206 Con->Info = LineInfo;
207 Con->Controls = Controls;
208 Con->Next = Line->Connections;
209 Line->Connections = Con;
210 }
211 else
212 {
213 HeapFree(GetProcessHeap(),
214 0,
215 Controls);
216 }
217 }
218 else
219 {
220 DPRINT("Failed to get connection information: %d\n", Result);
221 Ret = FALSE;
222 break;
223 }
224 }
225
226 return Ret;
227 }
228
229 static BOOL
230 SndMixerQueryDestinations(PSND_MIXER Mixer)
231 {
232 UINT i;
233 BOOL Ret = TRUE;
234
235 for (i = Mixer->Caps.cDestinations; i > 0; i--)
236 {
237 PSND_MIXER_DESTINATION Line;
238
239 Line = HeapAlloc(GetProcessHeap(),
240 HEAP_ZERO_MEMORY,
241 sizeof(SND_MIXER_DESTINATION));
242 if (Line != NULL)
243 {
244 Line->Info.cbStruct = sizeof(Line->Info);
245 Line->Info.dwDestination = i - 1;
246 if (mixerGetLineInfo((HMIXEROBJ)Mixer->hmx,
247 &Line->Info,
248 MIXER_GETLINEINFOF_DESTINATION) == MMSYSERR_NOERROR)
249 {
250 DPRINT("+ Destination: %ws (%d)\n", Line->Info.szName, Line->Info.dwComponentType);
251
252 if (!SndMixerQueryConnections(Mixer, Line))
253 {
254 DPRINT("Failed to query mixer connections!\n");
255 Ret = FALSE;
256 break;
257 }
258 if (!SndMixerQueryControls(Mixer,
259 &Line->Info,
260 &Line->Controls))
261 {
262 DPRINT("Failed to query mixer controls!\n");
263 Ret = FALSE;
264 break;
265 }
266
267 Line->Next = Mixer->Lines;
268 Mixer->Lines = Line;
269 }
270 else
271 {
272 DPRINT("Failed to get line information for id %d!\n", i);
273 HeapFree(GetProcessHeap(),
274 0,
275 Line);
276 Ret = FALSE;
277 break;
278 }
279 }
280 else
281 {
282 DPRINT("Allocation of SND_MIXER_DEST structure for id %d failed!\n", i);
283 Ret = FALSE;
284 break;
285 }
286 }
287
288 return Ret;
289 }
290
291 BOOL
292 SndMixerSelect(PSND_MIXER Mixer,
293 UINT MixerId)
294 {
295 if (MixerId >= Mixer->MixersCount)
296 {
297 return FALSE;
298 }
299
300 SndMixerClose(Mixer);
301
302 if (mixerOpen(&Mixer->hmx,
303 MixerId,
304 (DWORD_PTR)Mixer->hWndNotification,
305 0,
306 CALLBACK_WINDOW | MIXER_OBJECTF_MIXER) == MMSYSERR_NOERROR ||
307 mixerOpen(&Mixer->hmx,
308 MixerId,
309 (DWORD_PTR)Mixer->hWndNotification,
310 0,
311 CALLBACK_WINDOW) == MMSYSERR_NOERROR ||
312 mixerOpen(&Mixer->hmx,
313 MixerId,
314 0,
315 0,
316 0) == MMSYSERR_NOERROR)
317 {
318 if (mixerGetDevCaps(MixerId,
319 &Mixer->Caps,
320 sizeof(Mixer->Caps)) == MMSYSERR_NOERROR)
321 {
322 BOOL Ret = FALSE;
323
324 Mixer->MixerId = MixerId;
325
326 ClearMixerCache(Mixer);
327
328 Ret = SndMixerQueryDestinations(Mixer);
329
330 if (!Ret)
331 {
332 ClearMixerCache(Mixer);
333 }
334
335 return Ret;
336 }
337 else
338 {
339 mixerClose(Mixer->hmx);
340 }
341 }
342
343 Mixer->hmx = NULL;
344 Mixer->MixerId = NO_MIXER_SELECTED;
345 return FALSE;
346 }
347
348 UINT
349 SndMixerGetSelection(PSND_MIXER Mixer)
350 {
351 return Mixer->MixerId;
352 }
353
354 INT
355 SndMixerGetProductName(PSND_MIXER Mixer,
356 LPTSTR lpBuffer,
357 UINT uSize)
358 {
359 if (Mixer->hmx)
360 {
361 int lnsz = lstrlen(Mixer->Caps.szPname);
362 if(lnsz + 1 > uSize)
363 {
364 return lnsz + 1;
365 }
366 else
367 {
368 memcpy(lpBuffer, Mixer->Caps.szPname, lnsz * sizeof(TCHAR));
369 lpBuffer[lnsz] = _T('\0');
370 return lnsz;
371 }
372 }
373
374 return -1;
375 }
376
377 BOOL
378 SndMixerEnumProducts(PSND_MIXER Mixer,
379 PFNSNDMIXENUMPRODUCTS EnumProc,
380 PVOID Context)
381 {
382 MIXERCAPS Caps;
383 HMIXER hMixer;
384 UINT i;
385 BOOL Ret = TRUE;
386
387 for (i = 0; i < Mixer->MixersCount; i++)
388 {
389 if (mixerOpen(&hMixer,
390 i,
391 0,
392 0,
393 0) == MMSYSERR_NOERROR)
394 {
395 if (mixerGetDevCaps(i,
396 &Caps,
397 sizeof(Caps)) == MMSYSERR_NOERROR)
398 {
399 if (!EnumProc(Mixer,
400 i,
401 Caps.szPname,
402 Context))
403 {
404 mixerClose(hMixer);
405 Ret = FALSE;
406 break;
407 }
408 }
409 else
410 {
411 DPRINT("Failed to get device capabilities for mixer id %d!\n", i);
412 }
413 mixerClose(hMixer);
414 }
415 }
416
417 return Ret;
418 }
419
420 INT
421 SndMixerGetDestinationCount(PSND_MIXER Mixer)
422 {
423 return (Mixer->hmx ? Mixer->Caps.cDestinations : -1);
424 }
425
426 BOOL
427 SndMixerEnumLines(PSND_MIXER Mixer,
428 PFNSNDMIXENUMLINES EnumProc,
429 PVOID Context)
430 {
431 if (Mixer->hmx)
432 {
433 PSND_MIXER_DESTINATION Line;
434
435 for (Line = Mixer->Lines; Line != NULL; Line = Line->Next)
436 {
437 if (!EnumProc(Mixer,
438 &Line->Info,
439 Context))
440 {
441 return FALSE;
442 }
443 }
444
445 return TRUE;
446 }
447
448 return FALSE;
449 }
450
451 BOOL
452 SndMixerEnumConnections(PSND_MIXER Mixer,
453 DWORD LineID,
454 PFNSNDMIXENUMCONNECTIONS EnumProc,
455 PVOID Context)
456 {
457 if (Mixer->hmx)
458 {
459 PSND_MIXER_DESTINATION Line;
460
461 for (Line = Mixer->Lines; Line != NULL; Line = Line->Next)
462 {
463 if (Line->Info.dwLineID == LineID)
464 {
465 PSND_MIXER_CONNECTION Connection;
466
467 for (Connection = Line->Connections; Connection != NULL; Connection = Connection->Next)
468 {
469 if (!EnumProc(Mixer,
470 LineID,
471 &Connection->Info,
472 Context))
473 {
474 return FALSE;
475 }
476 }
477
478 return TRUE;
479 }
480 }
481 }
482
483 return FALSE;
484 }
485