* Sync with recent trunk (r52637).
[reactos.git] / base / applications / 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19 /* $Id$
20 *
21 * COPYRIGHT: See COPYING in the top level directory
22 * PROJECT: ReactOS Sound Volume Control
23 * FILE: subsys/system/sndvol32/mixer.c
24 * PROGRAMMERS: Thomas Weidenmueller <w3seek@reactos.com>
25 */
26 #include "sndvol32.h"
27
28 #define NO_MIXER_SELECTED ((UINT)(~0))
29
30 static VOID
31 ClearMixerCache(PSND_MIXER Mixer)
32 {
33 PSND_MIXER_DESTINATION Line, NextLine;
34 PSND_MIXER_CONNECTION Con, NextCon;
35
36 for (Line = Mixer->Lines; Line != NULL; Line = NextLine)
37 {
38 if (Line->Controls != NULL)
39 {
40 HeapFree(GetProcessHeap(),
41 0,
42 Line->Controls);
43 }
44
45 for (Con = Line->Connections; Con != NULL; Con = NextCon)
46 {
47 if (Con->Controls != NULL)
48 {
49 HeapFree(GetProcessHeap(),
50 0,
51 Con->Controls);
52 }
53
54 NextCon = Con->Next;
55 HeapFree(GetProcessHeap(),
56 0,
57 Con);
58 }
59
60 NextLine = Line->Next;
61 HeapFree(GetProcessHeap(),
62 0,
63 Line);
64 }
65 Mixer->Lines = NULL;
66 }
67
68 PSND_MIXER
69 SndMixerCreate(HWND hWndNotification)
70 {
71 PSND_MIXER Mixer = (PSND_MIXER) HeapAlloc(GetProcessHeap(),
72 HEAP_ZERO_MEMORY,
73 sizeof(SND_MIXER));
74 if (Mixer != NULL)
75 {
76 Mixer->hWndNotification = hWndNotification;
77 Mixer->MixersCount = mixerGetNumDevs();
78 Mixer->MixerId = NO_MIXER_SELECTED;
79
80 if (Mixer->MixersCount > 0)
81 {
82 /* select the first mixer by default */
83 SndMixerSelect(Mixer, 0);
84 }
85 }
86
87 return Mixer;
88 }
89
90 VOID
91 SndMixerDestroy(PSND_MIXER Mixer)
92 {
93 SndMixerClose(Mixer);
94 HeapFree(GetProcessHeap(),
95 0,
96 Mixer);
97 }
98
99 VOID
100 SndMixerClose(PSND_MIXER Mixer)
101 {
102 if (Mixer->hmx != NULL)
103 {
104 mixerClose(Mixer->hmx);
105 Mixer->hmx = NULL;
106 Mixer->MixerId = NO_MIXER_SELECTED;
107 }
108 }
109
110 BOOL
111 SndMixerQueryControls(PSND_MIXER Mixer,
112 PUINT DisplayControls,
113 LPMIXERLINE LineInfo,
114 LPMIXERCONTROL *Controls)
115 {
116 if (LineInfo->cControls > 0)
117 {
118 *Controls = (MIXERCONTROL*) HeapAlloc(GetProcessHeap(),
119 HEAP_ZERO_MEMORY,
120 LineInfo->cControls * sizeof(MIXERCONTROL));
121 if (*Controls != NULL)
122 {
123 MIXERLINECONTROLS LineControls;
124 MMRESULT Result;
125 UINT j;
126
127 LineControls.cbStruct = sizeof(LineControls);
128 LineControls.dwLineID = LineInfo->dwLineID;
129 LineControls.cControls = LineInfo->cControls;
130 LineControls.cbmxctrl = sizeof(MIXERCONTROL);
131 LineControls.pamxctrl = (MIXERCONTROL*)(*Controls);
132
133 Result = mixerGetLineControls((HMIXEROBJ)Mixer->hmx,
134 &LineControls,
135 MIXER_GETLINECONTROLSF_ALL);
136 if (Result == MMSYSERR_NOERROR)
137 {
138 for (j = 0; j < LineControls.cControls; j++)
139 {
140 if (SndMixerIsDisplayControl(Mixer,
141 &(*Controls)[j]))
142 {
143 (*DisplayControls)++;
144 }
145
146 DPRINT("Line control: %ws (0x%x, 0x%x)\n", (*Controls)[j].szName, (*Controls)[j].fdwControl, (*Controls)[j].dwControlType);
147 }
148
149 return TRUE;
150 }
151 else
152 {
153 HeapFree(GetProcessHeap(),
154 0,
155 *Controls);
156 *Controls = NULL;
157 DPRINT("Failed to get line (ID: 0x%x) controls: %d\n", LineInfo->dwLineID, Result);
158 }
159 }
160 else
161 {
162 DPRINT("Failed to allocate memory for %d line (ID: 0x%x) controls!\n", LineInfo->dwLineID, LineInfo->cControls);
163 }
164
165 return FALSE;
166 }
167 else
168 {
169 return TRUE;
170 }
171 }
172
173 static BOOL
174 SndMixerQueryConnections(PSND_MIXER Mixer,
175 PSND_MIXER_DESTINATION Line)
176 {
177 UINT i, DispControls;
178 MIXERLINE LineInfo;
179 MMRESULT Result;
180 BOOL Ret = TRUE;
181
182 LineInfo.cbStruct = sizeof(LineInfo);
183 for (i = Line->Info.cConnections; i > 0; i--)
184 {
185 LineInfo.dwDestination = Line->Info.dwDestination;
186 LineInfo.dwSource = i - 1;
187 Result = mixerGetLineInfo((HMIXEROBJ)Mixer->hmx,
188 &LineInfo,
189 MIXER_GETLINEINFOF_SOURCE);
190 if (Result == MMSYSERR_NOERROR)
191 {
192 LPMIXERCONTROL Controls = NULL;
193 PSND_MIXER_CONNECTION Con;
194
195 DPRINT("++ Source: %ws\n", LineInfo.szName);
196
197 DispControls = 0;
198
199 if (!SndMixerQueryControls(Mixer,
200 &DispControls,
201 &LineInfo,
202 &Controls))
203 {
204 DPRINT("Failed to query connection controls\n");
205 Ret = FALSE;
206 break;
207 }
208
209 Con = (SND_MIXER_CONNECTION*) HeapAlloc(GetProcessHeap(),
210 HEAP_ZERO_MEMORY,
211 sizeof(SND_MIXER_CONNECTION));
212 if (Con != NULL)
213 {
214 Con->Info = LineInfo;
215 Con->Controls = Controls;
216 Con->DisplayControls = DispControls;
217 Con->Next = Line->Connections;
218 Line->Connections = Con;
219 }
220 else
221 {
222 HeapFree(GetProcessHeap(),
223 0,
224 Controls);
225 }
226 }
227 else
228 {
229 DPRINT("Failed to get connection information: %d\n", Result);
230 Ret = FALSE;
231 break;
232 }
233 }
234
235 return Ret;
236 }
237
238 static BOOL
239 SndMixerQueryDestinations(PSND_MIXER Mixer)
240 {
241 UINT i;
242 BOOL Ret = TRUE;
243
244 for (i = Mixer->Caps.cDestinations; i > 0; i--)
245 {
246 PSND_MIXER_DESTINATION Line;
247
248 Line = (SND_MIXER_DESTINATION*) HeapAlloc(GetProcessHeap(),
249 HEAP_ZERO_MEMORY,
250 sizeof(SND_MIXER_DESTINATION));
251 if (Line != NULL)
252 {
253 Line->Info.cbStruct = sizeof(Line->Info);
254 Line->Info.dwDestination = i - 1;
255 if (mixerGetLineInfo((HMIXEROBJ)Mixer->hmx,
256 &Line->Info,
257 MIXER_GETLINEINFOF_DESTINATION) == MMSYSERR_NOERROR)
258 {
259 DPRINT("+ Destination: %ws (0x%x, %d)\n", Line->Info.szName, Line->Info.dwLineID, Line->Info.dwComponentType);
260
261 if (!SndMixerQueryControls(Mixer,
262 &Line->DisplayControls,
263 &Line->Info,
264 &Line->Controls))
265 {
266 DPRINT("Failed to query mixer controls!\n");
267 Ret = FALSE;
268 break;
269 }
270
271 if (!SndMixerQueryConnections(Mixer, Line))
272 {
273 DPRINT("Failed to query mixer connections!\n");
274 Ret = FALSE;
275 break;
276 }
277
278 Line->Next = Mixer->Lines;
279 Mixer->Lines = Line;
280 }
281 else
282 {
283 DPRINT("Failed to get line information for id %d!\n", i);
284 HeapFree(GetProcessHeap(),
285 0,
286 Line);
287 Ret = FALSE;
288 break;
289 }
290 }
291 else
292 {
293 DPRINT("Allocation of SND_MIXER_DEST structure for id %d failed!\n", i);
294 Ret = FALSE;
295 break;
296 }
297 }
298
299 return Ret;
300 }
301
302 BOOL
303 SndMixerSelect(PSND_MIXER Mixer,
304 UINT MixerId)
305 {
306 if (MixerId >= Mixer->MixersCount)
307 {
308 return FALSE;
309 }
310
311 SndMixerClose(Mixer);
312
313 if (mixerOpen(&Mixer->hmx,
314 MixerId,
315 (DWORD_PTR)Mixer->hWndNotification,
316 0,
317 CALLBACK_WINDOW | MIXER_OBJECTF_MIXER) == MMSYSERR_NOERROR ||
318 mixerOpen(&Mixer->hmx,
319 MixerId,
320 (DWORD_PTR)Mixer->hWndNotification,
321 0,
322 CALLBACK_WINDOW) == MMSYSERR_NOERROR ||
323 mixerOpen(&Mixer->hmx,
324 MixerId,
325 0,
326 0,
327 0) == MMSYSERR_NOERROR)
328 {
329 if (mixerGetDevCaps(MixerId,
330 &Mixer->Caps,
331 sizeof(Mixer->Caps)) == MMSYSERR_NOERROR)
332 {
333 BOOL Ret = FALSE;
334
335 Mixer->MixerId = MixerId;
336
337 ClearMixerCache(Mixer);
338
339 Ret = SndMixerQueryDestinations(Mixer);
340
341 if (!Ret)
342 {
343 ClearMixerCache(Mixer);
344 }
345
346 return Ret;
347 }
348 else
349 {
350 mixerClose(Mixer->hmx);
351 }
352 }
353
354 Mixer->hmx = NULL;
355 Mixer->MixerId = NO_MIXER_SELECTED;
356 return FALSE;
357 }
358
359 UINT
360 SndMixerGetSelection(PSND_MIXER Mixer)
361 {
362 return Mixer->MixerId;
363 }
364
365 INT
366 SndMixerGetProductName(PSND_MIXER Mixer,
367 LPTSTR lpBuffer,
368 UINT uSize)
369 {
370 if (Mixer->hmx)
371 {
372 UINT lnsz = (UINT) lstrlen(Mixer->Caps.szPname);
373 if(lnsz + 1 > uSize)
374 {
375 return lnsz + 1;
376 }
377 else
378 {
379 memcpy(lpBuffer, Mixer->Caps.szPname, lnsz * sizeof(TCHAR));
380 lpBuffer[lnsz] = _T('\0');
381 return lnsz;
382 }
383 }
384
385 return -1;
386 }
387
388 INT
389 SndMixerGetLineName(PSND_MIXER Mixer,
390 DWORD LineID,
391 LPTSTR lpBuffer,
392 UINT uSize,
393 BOOL LongName)
394 {
395 if (Mixer->hmx)
396 {
397 UINT lnsz;
398 PSND_MIXER_DESTINATION Line;
399 LPMIXERLINE lpl = NULL;
400
401 for (Line = Mixer->Lines; Line != NULL; Line = Line->Next)
402 {
403 if (Line->Info.dwLineID == LineID)
404 {
405 lpl = &Line->Info;
406 break;
407 }
408 }
409
410 if (lpl != NULL)
411 {
412 lnsz = (UINT) lstrlen(LongName ? lpl->szName : lpl->szShortName);
413 if(lnsz + 1 > uSize)
414 {
415 return lnsz + 1;
416 }
417 else
418 {
419 memcpy(lpBuffer, LongName ? lpl->szName : lpl->szShortName, lnsz * sizeof(TCHAR));
420 lpBuffer[lnsz] = _T('\0');
421 return lnsz;
422 }
423 }
424 }
425
426 return -1;
427 }
428
429 BOOL
430 SndMixerEnumProducts(PSND_MIXER Mixer,
431 PFNSNDMIXENUMPRODUCTS EnumProc,
432 PVOID Context)
433 {
434 MIXERCAPS Caps;
435 HMIXER hMixer;
436 UINT i;
437 BOOL Ret = TRUE;
438
439 for (i = 0; i < Mixer->MixersCount; i++)
440 {
441 if (mixerOpen(&hMixer,
442 i,
443 0,
444 0,
445 0) == MMSYSERR_NOERROR)
446 {
447 if (mixerGetDevCaps(i,
448 &Caps,
449 sizeof(Caps)) == MMSYSERR_NOERROR)
450 {
451 if (!EnumProc(Mixer,
452 i,
453 Caps.szPname,
454 Context))
455 {
456 mixerClose(hMixer);
457 Ret = FALSE;
458 break;
459 }
460 }
461 else
462 {
463 DPRINT("Failed to get device capabilities for mixer id %d!\n", i);
464 }
465 mixerClose(hMixer);
466 }
467 }
468
469 return Ret;
470 }
471
472 INT
473 SndMixerSetVolumeControlDetails(PSND_MIXER Mixer, DWORD dwControlID, DWORD cbDetails, LPVOID paDetails)
474 {
475 MIXERCONTROLDETAILS MixerDetails;
476
477 if (Mixer->hmx)
478 {
479 MixerDetails.cbStruct = sizeof(MIXERCONTROLDETAILS);
480 MixerDetails.dwControlID = dwControlID;
481 MixerDetails.cChannels = 1; //FIXME
482 MixerDetails.cMultipleItems = 0;
483 MixerDetails.cbDetails = cbDetails;
484 MixerDetails.paDetails = paDetails;
485
486 if (mixerSetControlDetails((HMIXEROBJ)Mixer->hmx, &MixerDetails, MIXER_GETCONTROLDETAILSF_VALUE | MIXER_OBJECTF_HMIXER) == MMSYSERR_NOERROR)
487 {
488 return 1;
489 }
490 }
491
492 return -1;
493 }
494
495
496 INT
497 SndMixerGetVolumeControlDetails(PSND_MIXER Mixer, DWORD dwControlID, DWORD cbDetails, LPVOID paDetails)
498 {
499 MIXERCONTROLDETAILS MixerDetails;
500
501 if (Mixer->hmx)
502 {
503 MixerDetails.cbStruct = sizeof(MIXERCONTROLDETAILS);
504 MixerDetails.dwControlID = dwControlID;
505 MixerDetails.cChannels = 1; //FIXME
506 MixerDetails.cMultipleItems = 0;
507 MixerDetails.cbDetails = cbDetails;
508 MixerDetails.paDetails = paDetails;
509
510 if (mixerGetControlDetails((HMIXEROBJ)Mixer->hmx, &MixerDetails, MIXER_GETCONTROLDETAILSF_VALUE | MIXER_OBJECTF_HMIXER) == MMSYSERR_NOERROR)
511 {
512 return 1;
513 }
514 }
515 return -1;
516 }
517
518 INT
519 SndMixerGetDestinationCount(PSND_MIXER Mixer)
520 {
521 return (Mixer->hmx ? (INT)Mixer->Caps.cDestinations : -1);
522 }
523
524 BOOL
525 SndMixerEnumLines(PSND_MIXER Mixer,
526 PFNSNDMIXENUMLINES EnumProc,
527 PVOID Context)
528 {
529 if (Mixer->hmx)
530 {
531 PSND_MIXER_DESTINATION Line;
532
533 for (Line = Mixer->Lines; Line != NULL; Line = Line->Next)
534 {
535 if (!EnumProc(Mixer,
536 &Line->Info,
537 Line->DisplayControls,
538 Context))
539 {
540 return FALSE;
541 }
542 }
543
544 return TRUE;
545 }
546
547 return FALSE;
548 }
549
550 BOOL
551 SndMixerEnumConnections(PSND_MIXER Mixer,
552 DWORD LineID,
553 PFNSNDMIXENUMCONNECTIONS EnumProc,
554 PVOID Context)
555 {
556 if (Mixer->hmx)
557 {
558 PSND_MIXER_DESTINATION Line;
559
560 for (Line = Mixer->Lines; Line != NULL; Line = Line->Next)
561 {
562 if (Line->Info.dwLineID == LineID)
563 {
564 PSND_MIXER_CONNECTION Connection;
565
566 if (Line->DisplayControls != 0)
567 {
568 if (!EnumProc(Mixer,
569 LineID,
570 &Line->Info,
571 Context))
572 {
573 return FALSE;
574 }
575 }
576
577 for (Connection = Line->Connections; Connection != NULL; Connection = Connection->Next)
578 {
579 if (!EnumProc(Mixer,
580 LineID,
581 &Connection->Info,
582 Context))
583 {
584 return FALSE;
585 }
586 }
587
588 return TRUE;
589 }
590 }
591 }
592
593 return FALSE;
594 }
595
596 BOOL
597 SndMixerIsDisplayControl(PSND_MIXER Mixer,
598 LPMIXERCONTROL Control)
599 {
600 if (Mixer->hmx && !(Control->fdwControl & MIXERCONTROL_CONTROLF_DISABLED))
601 {
602 switch (Control->dwControlType & MIXERCONTROL_CT_CLASS_MASK)
603 {
604 case MIXERCONTROL_CT_CLASS_FADER:
605 case MIXERCONTROL_CT_CLASS_SWITCH:
606 return TRUE;
607 }
608 }
609
610 return FALSE;
611 }
612