122198a58f015ceb11dc23346be080b122927ecc
[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 HEAP_ZERO_MEMORY,
121 LineInfo->cControls * sizeof(MIXERCONTROL));
122 if (*Controls != NULL)
123 {
124 MIXERLINECONTROLS LineControls;
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 = (PVOID)(*Controls);
132
133 for (j = 0; j < LineInfo->cControls; j++)
134 {
135 (*Controls)[j].cbStruct = sizeof(MIXERCONTROL);
136 }
137
138 if (mixerGetLineControls((HMIXEROBJ)Mixer->hmx,
139 &LineControls,
140 MIXER_GETLINECONTROLSF_ALL) == MMSYSERR_NOERROR)
141 {
142 for (j = 0; j < LineInfo->cControls; j++)
143 {
144 DPRINT("Line control: %ws", (*Controls)[j].szName);
145 }
146
147 return TRUE;
148 }
149 else
150 {
151 HeapFree(GetProcessHeap(),
152 0,
153 *Controls);
154 *Controls = NULL;
155 DPRINT("Failed to get line controls!\n");
156 }
157 }
158 else
159 {
160 DPRINT("Failed to allocate memory for %d line controls!\n", LineInfo->cControls);
161 }
162
163 return FALSE;
164 }
165 else
166 {
167 return TRUE;
168 }
169 }
170
171 static BOOL
172 SndMixerQueryConnections(PSND_MIXER Mixer,
173 PSND_MIXER_DESTINATION Line)
174 {
175 UINT i;
176 MIXERLINE LineInfo;
177 BOOL Ret = TRUE;
178
179 LineInfo.cbStruct = sizeof(LineInfo);
180 LineInfo.dwDestination = Line->Info.dwDestination;
181 for (i = Line->Info.cConnections; i > 0; i--)
182 {
183 LineInfo.dwSource = i - 1;
184 if (mixerGetLineInfo((HMIXEROBJ)Mixer->hmx,
185 &LineInfo,
186 MIXER_GETLINEINFOF_SOURCE) == MMSYSERR_NOERROR)
187 {
188 LPMIXERCONTROL Controls;
189 PSND_MIXER_CONNECTION Con;
190
191 if (!SndMixerQueryControls(Mixer,
192 &LineInfo,
193 &Controls))
194 {
195 DPRINT("Failed to query connection controls\n");
196 Ret = FALSE;
197 break;
198 }
199
200 Con = HeapAlloc(GetProcessHeap(),
201 0,
202 sizeof(SND_MIXER_CONNECTION));
203 if (Con != NULL)
204 {
205 Con->Info = LineInfo;
206 Con->Controls = Controls;
207 Con->Next = Line->Connections;
208 Line->Connections = Con;
209 }
210 else
211 {
212 HeapFree(GetProcessHeap(),
213 0,
214 Controls);
215 }
216 }
217 else
218 {
219 DPRINT("Failed to get connection information!\n");
220 Ret = FALSE;
221 break;
222 }
223 }
224
225 return Ret;
226 }
227
228 static BOOL
229 SndMixerQueryDestinations(PSND_MIXER Mixer)
230 {
231 UINT i;
232 BOOL Ret = TRUE;
233
234 for (i = Mixer->Caps.cDestinations; i > 0; i--)
235 {
236 PSND_MIXER_DESTINATION Line;
237
238 Line = HeapAlloc(GetProcessHeap(),
239 HEAP_ZERO_MEMORY,
240 sizeof(SND_MIXER_DESTINATION));
241 if (Line != NULL)
242 {
243 Line->Info.cbStruct = sizeof(Line->Info);
244 Line->Info.dwDestination = i - 1;
245 if (mixerGetLineInfo((HMIXEROBJ)Mixer->hmx,
246 &Line->Info,
247 MIXER_GETLINEINFOF_DESTINATION) == MMSYSERR_NOERROR)
248 {
249 if (!SndMixerQueryConnections(Mixer, Line))
250 {
251 DPRINT("Failed to query mixer connections!\n");
252 Ret = FALSE;
253 break;
254 }
255 if (!SndMixerQueryControls(Mixer,
256 &Line->Info,
257 &Line->Controls))
258 {
259 DPRINT("Failed to query mixer controls!\n");
260 Ret = FALSE;
261 break;
262 }
263
264 Line->Next = Mixer->Lines;
265 Mixer->Lines = Line;
266 }
267 else
268 {
269 DPRINT("Failed to get line information for id %d!\n", i);
270 HeapFree(GetProcessHeap(),
271 0,
272 Line);
273 Ret = FALSE;
274 break;
275 }
276 }
277 else
278 {
279 DPRINT("Allocation of SND_MIXER_DEST structure for id %d failed!\n", i);
280 Ret = FALSE;
281 break;
282 }
283 }
284
285 return Ret;
286 }
287
288 BOOL
289 SndMixerSelect(PSND_MIXER Mixer,
290 UINT MixerId)
291 {
292 if (MixerId >= Mixer->MixersCount)
293 {
294 return FALSE;
295 }
296
297 SndMixerClose(Mixer);
298
299 if (mixerOpen(&Mixer->hmx,
300 MixerId,
301 (DWORD_PTR)Mixer->hWndNotification,
302 0,
303 CALLBACK_WINDOW | MIXER_OBJECTF_MIXER) == MMSYSERR_NOERROR ||
304 mixerOpen(&Mixer->hmx,
305 MixerId,
306 (DWORD_PTR)Mixer->hWndNotification,
307 0,
308 CALLBACK_WINDOW) == MMSYSERR_NOERROR ||
309 mixerOpen(&Mixer->hmx,
310 MixerId,
311 0,
312 0,
313 0) == MMSYSERR_NOERROR)
314 {
315 if (mixerGetDevCaps(MixerId,
316 &Mixer->Caps,
317 sizeof(Mixer->Caps)) == MMSYSERR_NOERROR)
318 {
319 BOOL Ret = FALSE;
320
321 Mixer->MixerId = MixerId;
322
323 ClearMixerCache(Mixer);
324
325 Ret = SndMixerQueryDestinations(Mixer);
326
327 if (!Ret)
328 {
329 ClearMixerCache(Mixer);
330 }
331
332 return Ret;
333 }
334 else
335 {
336 mixerClose(Mixer->hmx);
337 }
338 }
339
340 Mixer->hmx = NULL;
341 Mixer->MixerId = NO_MIXER_SELECTED;
342 return FALSE;
343 }
344
345 UINT
346 SndMixerGetSelection(PSND_MIXER Mixer)
347 {
348 return Mixer->MixerId;
349 }
350
351 INT
352 SndMixerGetProductName(PSND_MIXER Mixer,
353 LPTSTR lpBuffer,
354 UINT uSize)
355 {
356 if (Mixer->hmx)
357 {
358 int lnsz = lstrlen(Mixer->Caps.szPname);
359 if(lnsz + 1 > uSize)
360 {
361 return lnsz + 1;
362 }
363 else
364 {
365 memcpy(lpBuffer, Mixer->Caps.szPname, lnsz * sizeof(TCHAR));
366 lpBuffer[lnsz] = _T('\0');
367 return lnsz;
368 }
369 }
370
371 return -1;
372 }
373
374 BOOL
375 SndMixerEnumProducts(PSND_MIXER Mixer,
376 PFNSNDMIXENUMPRODUCTS EnumProc,
377 PVOID Context)
378 {
379 MIXERCAPS Caps;
380 HMIXER hMixer;
381 UINT i;
382 BOOL Ret = TRUE;
383
384 for (i = 0; i < Mixer->MixersCount; i++)
385 {
386 if (mixerOpen(&hMixer,
387 i,
388 0,
389 0,
390 0) == MMSYSERR_NOERROR)
391 {
392 if (mixerGetDevCaps(i,
393 &Caps,
394 sizeof(Caps)) == MMSYSERR_NOERROR)
395 {
396 if (!EnumProc(Mixer,
397 i,
398 Caps.szPname,
399 Context))
400 {
401 mixerClose(hMixer);
402 Ret = FALSE;
403 break;
404 }
405 }
406 else
407 {
408 DPRINT("Failed to get device capabilities for mixer id %d!\n", i);
409 }
410 mixerClose(hMixer);
411 }
412 }
413
414 return Ret;
415 }
416
417 INT
418 SndMixerGetDestinationCount(PSND_MIXER Mixer)
419 {
420 return (Mixer->hmx ? Mixer->Caps.cDestinations : -1);
421 }
422
423 BOOL
424 SndMixerEnumLines(PSND_MIXER Mixer,
425 PFNSNDMIXENUMLINES EnumProc,
426 PVOID Context)
427 {
428 if (Mixer->hmx)
429 {
430 PSND_MIXER_DESTINATION Line;
431
432 for (Line = Mixer->Lines; Line != NULL; Line = Line->Next)
433 {
434 if (!EnumProc(Mixer,
435 &Line->Info,
436 Context))
437 {
438 return FALSE;
439 }
440 }
441
442 return TRUE;
443 }
444
445 return FALSE;
446 }
447