xref: /reactos/base/applications/sndvol32/mixer.c (revision 53221834)
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
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, 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
91 SndMixerDestroy(PSND_MIXER Mixer)
92 {
93     ClearMixerCache(Mixer);
94     SndMixerClose(Mixer);
95     HeapFree(GetProcessHeap(),
96              0,
97              Mixer);
98 }
99 
100 VOID
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
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
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
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
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
361 SndMixerGetSelection(PSND_MIXER Mixer)
362 {
363     return Mixer->MixerId;
364 }
365 
366 INT
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
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
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
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
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
520 SndMixerGetDestinationCount(PSND_MIXER Mixer)
521 {
522     return (Mixer->hmx ? (INT)Mixer->Caps.cDestinations : -1);
523 }
524 
525 BOOL
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
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
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
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 }