migrate substitution keywords to SVN
[reactos.git] / reactos / subsys / system / sndvol32 / mixer.c
1 /*
2 * ReactOS Sound Volume Control
3 * Copyright (C) 2004 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(), 0, Line->Controls);
43 }
44
45 for(Con = Line->Connections; Con != NULL; Con = NextCon)
46 {
47 if(Con->Controls != NULL)
48 {
49 HeapFree(GetProcessHeap(), 0, Con->Controls);
50 }
51
52 NextCon = Con->Next;
53 HeapFree(GetProcessHeap(), 0, Con);
54 }
55
56 NextLine = Line->Next;
57 HeapFree(GetProcessHeap(), 0, Line);
58 }
59 Mixer->Lines = NULL;
60 }
61
62 PSND_MIXER
63 SndMixerCreate(HWND hWndNotification)
64 {
65 PSND_MIXER Mixer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SND_MIXER));
66 if(Mixer != NULL)
67 {
68 Mixer->hWndNotification = hWndNotification;
69 Mixer->MixersCount = mixerGetNumDevs();
70 Mixer->MixerId = NO_MIXER_SELECTED;
71
72 if(Mixer->MixersCount > 0)
73 {
74 /* select the first mixer by default */
75 SndMixerSelect(Mixer, 0);
76 }
77 }
78
79 return Mixer;
80 }
81
82 VOID
83 SndMixerDestroy(PSND_MIXER Mixer)
84 {
85 SndMixerClose(Mixer);
86 HeapFree(GetProcessHeap(), 0, Mixer);
87 }
88
89 VOID
90 SndMixerClose(PSND_MIXER Mixer)
91 {
92 if(Mixer->hmx != NULL)
93 {
94 mixerClose(Mixer->hmx);
95 Mixer->hmx = NULL;
96 Mixer->MixerId = NO_MIXER_SELECTED;
97 }
98 }
99
100 static BOOL
101 SndMixerQueryControls(PSND_MIXER Mixer, LPMIXERLINE LineInfo, LPMIXERCONTROL *Controls)
102 {
103 if(LineInfo->cControls > 0)
104 {
105 *Controls = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, LineInfo->cControls * sizeof(MIXERCONTROL));
106 if(*Controls != NULL)
107 {
108 MIXERLINECONTROLS LineControls;
109 UINT j;
110
111 LineControls.cbStruct = sizeof(LineControls);
112 LineControls.dwLineID = LineInfo->dwLineID;
113 LineControls.cControls = LineInfo->cControls;
114 LineControls.cbmxctrl = sizeof(MIXERCONTROL);
115 LineControls.pamxctrl = (PVOID)(*Controls);
116
117 for(j = 0; j < LineInfo->cControls; j++)
118 {
119 (*Controls)[j].cbStruct = sizeof(MIXERCONTROL);
120 }
121
122 if(mixerGetLineControls((HMIXEROBJ)Mixer->hmx, &LineControls, MIXER_GETLINECONTROLSF_ALL) == MMSYSERR_NOERROR)
123 {
124 for(j = 0; j < LineInfo->cControls; j++)
125 {
126 DBG("Line control: %ws", (*Controls)[j].szName);
127 }
128
129 return TRUE;
130 }
131 else
132 {
133 HeapFree(GetProcessHeap(), 0, *Controls);
134 *Controls = NULL;
135 DBG("Failed to get line controls!\n");
136 }
137 }
138 else
139 {
140 DBG("Failed to allocate memory for %d line controls!\n", LineInfo->cControls);
141 }
142
143 return FALSE;
144 }
145 else
146 {
147 return TRUE;
148 }
149 }
150
151 static BOOL
152 SndMixerQueryConnections(PSND_MIXER Mixer, PSND_MIXER_DESTINATION Line)
153 {
154 UINT i;
155 MIXERLINE LineInfo;
156 BOOL Ret = TRUE;
157
158 LineInfo.cbStruct = sizeof(LineInfo);
159 LineInfo.dwDestination = Line->Info.dwDestination;
160 for(i = Line->Info.cConnections; i > 0; i--)
161 {
162 LineInfo.dwSource = i - 1;
163 if(mixerGetLineInfo((HMIXEROBJ)Mixer->hmx, &LineInfo, MIXER_GETLINEINFOF_SOURCE) == MMSYSERR_NOERROR)
164 {
165 LPMIXERCONTROL Controls;
166 PSND_MIXER_CONNECTION Con;
167
168 if(!SndMixerQueryControls(Mixer, &LineInfo, &Controls))
169 {
170 DBG("Failed to query connection controls\n");
171 Ret = FALSE;
172 break;
173 }
174
175 Con = HeapAlloc(GetProcessHeap(), 0, sizeof(SND_MIXER_CONNECTION));
176 if(Con != NULL)
177 {
178 Con->Info = LineInfo;
179 Con->Controls = Controls;
180 Con->Next = Line->Connections;
181 Line->Connections = Con;
182 }
183 else
184 {
185 HeapFree(GetProcessHeap(), 0, Controls);
186 }
187 }
188 else
189 {
190 DBG("Failed to get connection information!\n");
191 Ret = FALSE;
192 break;
193 }
194 }
195
196 return Ret;
197 }
198
199 static BOOL
200 SndMixerQueryDestinations(PSND_MIXER Mixer)
201 {
202 UINT i;
203 BOOL Ret = TRUE;
204
205 for(i = Mixer->Caps.cDestinations; i > 0; i--)
206 {
207 PSND_MIXER_DESTINATION Line;
208
209 Line = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SND_MIXER_DESTINATION));
210 if(Line != NULL)
211 {
212 Line->Info.cbStruct = sizeof(Line->Info);
213 Line->Info.dwDestination = i - 1;
214 if(mixerGetLineInfo((HMIXEROBJ)Mixer->hmx, &Line->Info, MIXER_GETLINEINFOF_DESTINATION) == MMSYSERR_NOERROR)
215 {
216 if(!SndMixerQueryConnections(Mixer, Line))
217 {
218 DBG("Failed to query mixer connections!\n");
219 Ret = FALSE;
220 break;
221 }
222 if(!SndMixerQueryControls(Mixer, &Line->Info, &Line->Controls))
223 {
224 DBG("Failed to query mixer controls!\n");
225 Ret = FALSE;
226 break;
227 }
228
229 Line->Next = Mixer->Lines;
230 Mixer->Lines = Line;
231 }
232 else
233 {
234 DBG("Failed to get line information for id %d!\n", i);
235 HeapFree(GetProcessHeap(), 0, Line);
236 Ret = FALSE;
237 break;
238 }
239 }
240 else
241 {
242 DBG("Allocation of SND_MIXER_DEST structure for id %d failed!\n", i);
243 Ret = FALSE;
244 break;
245 }
246 }
247
248 return Ret;
249 }
250
251 BOOL
252 SndMixerSelect(PSND_MIXER Mixer, UINT MixerId)
253 {
254 if(MixerId >= Mixer->MixersCount)
255 {
256 return FALSE;
257 }
258
259 SndMixerClose(Mixer);
260
261 if(mixerOpen(&Mixer->hmx, MixerId, (DWORD_PTR)Mixer->hWndNotification, 0, CALLBACK_WINDOW | MIXER_OBJECTF_MIXER) == MMSYSERR_NOERROR ||
262 mixerOpen(&Mixer->hmx, MixerId, (DWORD_PTR)Mixer->hWndNotification, 0, CALLBACK_WINDOW) == MMSYSERR_NOERROR ||
263 mixerOpen(&Mixer->hmx, MixerId, 0, 0, 0) == MMSYSERR_NOERROR)
264 {
265 if(mixerGetDevCaps(MixerId, &Mixer->Caps, sizeof(Mixer->Caps)) == MMSYSERR_NOERROR)
266 {
267 BOOL Ret = FALSE;
268
269 Mixer->MixerId = MixerId;
270
271 ClearMixerCache(Mixer);
272
273 Ret = SndMixerQueryDestinations(Mixer);
274
275 if(!Ret)
276 {
277 ClearMixerCache(Mixer);
278 }
279
280 return Ret;
281 }
282 else
283 {
284 mixerClose(Mixer->hmx);
285 }
286 }
287
288 Mixer->hmx = NULL;
289 Mixer->MixerId = NO_MIXER_SELECTED;
290 return FALSE;
291 }
292
293 UINT
294 SndMixerGetSelection(PSND_MIXER Mixer)
295 {
296 return Mixer->MixerId;
297 }
298
299 INT
300 SndMixerGetProductName(PSND_MIXER Mixer, LPTSTR lpBuffer, UINT uSize)
301 {
302 if(Mixer->hmx)
303 {
304 int lnsz = lstrlen(Mixer->Caps.szPname);
305 if(lnsz + 1 > uSize)
306 {
307 return lnsz + 1;
308 }
309 else
310 {
311 memcpy(lpBuffer, Mixer->Caps.szPname, lnsz * sizeof(TCHAR));
312 lpBuffer[lnsz] = _T('\0');
313 return lnsz;
314 }
315 }
316
317 return -1;
318 }
319
320 BOOL
321 SndMixerEnumProducts(PSND_MIXER Mixer, PFNSNDMIXENUMPRODUCTS EnumProc, PVOID Context)
322 {
323 MIXERCAPS Caps;
324 HMIXER hMixer;
325 UINT i;
326 BOOL Ret = TRUE;
327
328 for(i = 0; i < Mixer->MixersCount; i++)
329 {
330 if(mixerOpen(&hMixer, i, 0, 0, 0) == MMSYSERR_NOERROR)
331 {
332 if(mixerGetDevCaps(i, &Caps, sizeof(Caps)) == MMSYSERR_NOERROR)
333 {
334 if(!EnumProc(Mixer, i, Caps.szPname, Context))
335 {
336 mixerClose(hMixer);
337 Ret = FALSE;
338 break;
339 }
340 }
341 else
342 {
343 DBG("Failed to get device capabilities for mixer id %d!\n", i);
344 }
345 mixerClose(hMixer);
346 }
347 }
348
349 return Ret;
350 }
351
352 INT
353 SndMixerGetDestinationCount(PSND_MIXER Mixer)
354 {
355 return (Mixer->hmx ? Mixer->Caps.cDestinations : -1);
356 }
357
358 BOOL
359 SndMixerEnumLines(PSND_MIXER Mixer, PFNSNDMIXENUMLINES EnumProc, PVOID Context)
360 {
361 if(Mixer->hmx)
362 {
363 PSND_MIXER_DESTINATION Line;
364
365 for(Line = Mixer->Lines; Line != NULL; Line = Line->Next)
366 {
367 if(!EnumProc(Mixer, &Line->Info, Context))
368 {
369 return FALSE;
370 }
371 }
372
373 return TRUE;
374 }
375
376 return FALSE;
377 }
378