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