[MMIXER]
[reactos.git] / reactos / lib / drivers / sound / mmixer / sup.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Kernel Streaming
4 * FILE: lib/drivers/sound/mmixer/sup.c
5 * PURPOSE: Mixer Support Functions
6 * PROGRAMMER: Johannes Anderwald
7 */
8
9
10
11 #include "priv.h"
12
13 const GUID KSNODETYPE_SUM = {0xDA441A60L, 0xC556, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
14 const GUID KSNODETYPE_DAC = {0x507AE360L, 0xC554, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
15 const GUID KSNODETYPE_ADC = {0x4D837FE0L, 0xC555, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
16 const GUID KSNODETYPE_AGC = {0xE88C9BA0L, 0xC557, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
17 const GUID KSNODETYPE_LOUDNESS = {0x41887440L, 0xC558, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
18 const GUID KSNODETYPE_MUTE = {0x02B223C0L, 0xC557, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
19 const GUID KSNODETYPE_TONE = {0x7607E580L, 0xC557, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
20 const GUID KSNODETYPE_VOLUME = {0x3A5ACC00L, 0xC557, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
21 const GUID KSNODETYPE_PEAKMETER = {0xa085651e, 0x5f0d, 0x4b36, {0xa8, 0x69, 0xd1, 0x95, 0xd6, 0xab, 0x4b, 0x9e}};
22 const GUID KSNODETYPE_MUX = {0x2CEAF780, 0xC556, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
23 const GUID KSNODETYPE_STEREO_WIDE = {0xA9E69800L, 0xC558, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
24 const GUID KSNODETYPE_CHORUS = {0x20173F20L, 0xC559, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
25 const GUID KSNODETYPE_REVERB = {0xEF0328E0L, 0xC558, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
26 const GUID KSNODETYPE_SUPERMIX = {0xE573ADC0L, 0xC555, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
27
28 const GUID KSPROPSETID_Audio = {0x45FFAAA0L, 0x6E1B, 0x11D0, {0xBC, 0xF2, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}};
29 const GUID KSPROPSETID_Pin = {0x8C134960L, 0x51AD, 0x11CF, {0x87, 0x8A, 0x94, 0xF8, 0x01, 0xC1, 0x00, 0x00}};
30 const GUID KSPROPSETID_General = {0x1464EDA5L, 0x6A8F, 0x11D1, {0x9A, 0xA7, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
31 const GUID KSPROPSETID_Topology = {0x720D4AC0L, 0x7533, 0x11D0, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}};
32
33 MIXER_STATUS
34 MMixerVerifyContext(
35 IN PMIXER_CONTEXT MixerContext)
36 {
37 if (MixerContext->SizeOfStruct != sizeof(MIXER_CONTEXT))
38 return MM_STATUS_INVALID_PARAMETER;
39
40 if (!MixerContext->Alloc || !MixerContext->Control || !MixerContext->Free || !MixerContext->Open || !MixerContext->Close)
41 return MM_STATUS_INVALID_PARAMETER;
42
43 if (!MixerContext->MixerContext)
44 return MM_STATUS_INVALID_PARAMETER;
45
46 return MM_STATUS_SUCCESS;
47 }
48
49 VOID
50 MMixerFreeMixerInfo(
51 IN PMIXER_CONTEXT MixerContext,
52 IN LPMIXER_INFO MixerInfo)
53 {
54 //UNIMPLEMENTED
55 // FIXME
56 // free all lines
57
58 MixerContext->Free((PVOID)MixerInfo);
59 }
60
61 LPMIXER_INFO
62 MMixerGetMixerInfoByIndex(
63 IN PMIXER_CONTEXT MixerContext,
64 IN ULONG MixerIndex)
65 {
66 LPMIXER_INFO MixerInfo;
67 PLIST_ENTRY Entry;
68 PMIXER_LIST MixerList;
69 ULONG Index = 0;
70
71 // get mixer list
72 MixerList = (PMIXER_LIST)MixerContext->MixerContext;
73
74 if (!MixerList->MixerListCount)
75 return NULL;
76
77 Entry = MixerList->MixerList.Flink;
78
79 while(Entry != &MixerList->MixerList)
80 {
81 MixerInfo = (LPMIXER_INFO)CONTAINING_RECORD(Entry, MIXER_INFO, Entry);
82
83 if (Index == MixerIndex)
84 return MixerInfo;
85
86 // move to next mixer entry
87 Index++;
88 Entry = Entry->Flink;
89 }
90
91 return NULL;
92 }
93
94 LPMIXERCONTROL_DATA
95 MMixerGetMixerControlDataById(
96 PLIST_ENTRY ListHead,
97 DWORD dwControlId)
98 {
99 PLIST_ENTRY Entry;
100 LPMIXERCONTROL_DATA Control;
101
102 /* get first entry */
103 Entry = ListHead->Flink;
104
105 while(Entry != ListHead)
106 {
107 Control = (LPMIXERCONTROL_DATA)CONTAINING_RECORD(Entry, MIXERCONTROL_DATA, Entry);
108 DPRINT("dwSource %x dwSource %x\n", Control->dwControlID, dwControlId);
109 if (Control->dwControlID == dwControlId)
110 return Control;
111
112 Entry = Entry->Flink;
113 }
114 return NULL;
115 }
116
117 LPMIXERLINE_EXT
118 MMixerGetSourceMixerLineByLineId(
119 LPMIXER_INFO MixerInfo,
120 DWORD dwLineID)
121 {
122 PLIST_ENTRY Entry;
123 LPMIXERLINE_EXT MixerLineSrc;
124
125 /* get first entry */
126 Entry = MixerInfo->LineList.Flink;
127
128 while(Entry != &MixerInfo->LineList)
129 {
130 MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry);
131 DPRINT("dwLineID %x dwLineID %x\n", MixerLineSrc->Line.dwLineID, dwLineID);
132 if (MixerLineSrc->Line.dwLineID == dwLineID)
133 return MixerLineSrc;
134
135 Entry = Entry->Flink;
136 }
137
138 return NULL;
139 }
140
141 ULONG
142 MMixerGetIndexOfGuid(
143 PKSMULTIPLE_ITEM MultipleItem,
144 LPCGUID NodeType)
145 {
146 ULONG Index;
147 LPGUID Guid;
148
149 Guid = (LPGUID)(MultipleItem+1);
150
151 /* iterate through node type array */
152 for(Index = 0; Index < MultipleItem->Count; Index++)
153 {
154 if (IsEqualGUIDAligned(NodeType, Guid))
155 {
156 /* found matching guid */
157 return Index;
158 }
159 Guid++;
160 }
161 return MAXULONG;
162 }
163
164 PKSTOPOLOGY_CONNECTION
165 MMixerGetConnectionByIndex(
166 IN PKSMULTIPLE_ITEM MultipleItem,
167 IN ULONG Index)
168 {
169 PKSTOPOLOGY_CONNECTION Descriptor;
170
171 ASSERT(Index < MultipleItem->Count);
172
173 Descriptor = (PKSTOPOLOGY_CONNECTION)(MultipleItem + 1);
174 return &Descriptor[Index];
175 }
176
177 LPGUID
178 MMixerGetNodeType(
179 IN PKSMULTIPLE_ITEM MultipleItem,
180 IN ULONG Index)
181 {
182 LPGUID NodeType;
183
184 ASSERT(Index < MultipleItem->Count);
185
186 NodeType = (LPGUID)(MultipleItem + 1);
187 return &NodeType[Index];
188 }
189
190 MIXER_STATUS
191 MMixerGetNodeIndexes(
192 IN PMIXER_CONTEXT MixerContext,
193 IN PKSMULTIPLE_ITEM MultipleItem,
194 IN ULONG NodeIndex,
195 IN ULONG bNode,
196 IN ULONG bFrom,
197 OUT PULONG NodeReferenceCount,
198 OUT PULONG *NodeReference)
199 {
200 ULONG Index, Count = 0;
201 PKSTOPOLOGY_CONNECTION Connection;
202 PULONG Refs;
203
204 // KSMULTIPLE_ITEM is followed by several KSTOPOLOGY_CONNECTION
205 Connection = (PKSTOPOLOGY_CONNECTION)(MultipleItem + 1);
206
207 // first count all referenced nodes
208 for(Index = 0; Index < MultipleItem->Count; Index++)
209 {
210 if (bNode)
211 {
212 if (bFrom)
213 {
214 if (Connection->FromNode == NodeIndex)
215 {
216 // node id has a connection
217 Count++;
218 }
219 }
220 else
221 {
222 if (Connection->ToNode == NodeIndex)
223 {
224 // node id has a connection
225 Count++;
226 }
227 }
228 }
229 else
230 {
231 if (bFrom)
232 {
233 if (Connection->FromNodePin == NodeIndex && Connection->FromNode == KSFILTER_NODE)
234 {
235 // node id has a connection
236 Count++;
237 }
238 }
239 else
240 {
241 if (Connection->ToNodePin == NodeIndex && Connection->ToNode == KSFILTER_NODE)
242 {
243 // node id has a connection
244 Count++;
245 }
246 }
247 }
248
249
250 // move to next connection
251 Connection++;
252 }
253
254 ASSERT(Count != 0);
255
256 /* now allocate node index array */
257 Refs = (PULONG)MixerContext->Alloc(sizeof(ULONG) * Count);
258 if (!Refs)
259 {
260 // not enough memory
261 return MM_STATUS_NO_MEMORY;
262 }
263
264 Count = 0;
265 Connection = (PKSTOPOLOGY_CONNECTION)(MultipleItem + 1);
266 for(Index = 0; Index < MultipleItem->Count; Index++)
267 {
268 if (bNode)
269 {
270 if (bFrom)
271 {
272 if (Connection->FromNode == NodeIndex)
273 {
274 /* node id has a connection */
275 Refs[Count] = Index;
276 Count++;
277 }
278 }
279 else
280 {
281 if (Connection->ToNode == NodeIndex)
282 {
283 /* node id has a connection */
284 Refs[Count] = Index;
285 Count++;
286 }
287 }
288 }
289 else
290 {
291 if (bFrom)
292 {
293 if (Connection->FromNodePin == NodeIndex && Connection->FromNode == KSFILTER_NODE)
294 {
295 /* node id has a connection */
296 Refs[Count] = Index;
297 Count++;
298 }
299 }
300 else
301 {
302 if (Connection->ToNodePin == NodeIndex && Connection->ToNode == KSFILTER_NODE)
303 {
304 /* node id has a connection */
305 Refs[Count] = Index;
306 Count++;
307 }
308 }
309 }
310
311 /* move to next connection */
312 Connection++;
313 }
314
315 /* store result */
316 *NodeReference = Refs;
317 *NodeReferenceCount = Count;
318
319 return MM_STATUS_SUCCESS;
320 }
321
322 MIXER_STATUS
323 MMixerGetTargetPins(
324 IN PMIXER_CONTEXT MixerContext,
325 IN PKSMULTIPLE_ITEM NodeTypes,
326 IN PKSMULTIPLE_ITEM NodeConnections,
327 IN ULONG NodeIndex,
328 IN ULONG bUpDirection,
329 OUT PULONG Pins,
330 IN ULONG PinCount)
331 {
332 ULONG NodeConnectionCount, Index;
333 MIXER_STATUS Status;
334 PULONG NodeConnection;
335
336 // sanity check */
337 ASSERT(NodeIndex != (ULONG)-1);
338
339 /* get all node indexes referenced by that pin */
340 if (bUpDirection)
341 Status = MMixerGetNodeIndexes(MixerContext, NodeConnections, NodeIndex, TRUE, FALSE, &NodeConnectionCount, &NodeConnection);
342 else
343 Status = MMixerGetNodeIndexes(MixerContext, NodeConnections, NodeIndex, TRUE, TRUE, &NodeConnectionCount, &NodeConnection);
344
345 //DPRINT("NodeIndex %u Status %x Count %u\n", NodeIndex, Status, NodeConnectionCount);
346
347 if (Status == MM_STATUS_SUCCESS)
348 {
349 for(Index = 0; Index < NodeConnectionCount; Index++)
350 {
351 Status = MMixerGetTargetPinsByNodeConnectionIndex(MixerContext, NodeConnections, NodeTypes, bUpDirection, NodeConnection[Index], Pins);
352 ASSERT(Status == STATUS_SUCCESS);
353 }
354 MixerContext->Free((PVOID)NodeConnection);
355 }
356
357 return Status;
358 }
359
360 LPMIXERLINE_EXT
361 MMixerGetSourceMixerLineByComponentType(
362 LPMIXER_INFO MixerInfo,
363 DWORD dwComponentType)
364 {
365 PLIST_ENTRY Entry;
366 LPMIXERLINE_EXT MixerLineSrc;
367
368 /* get first entry */
369 Entry = MixerInfo->LineList.Flink;
370
371 while(Entry != &MixerInfo->LineList)
372 {
373 MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry);
374 if (MixerLineSrc->Line.dwComponentType == dwComponentType)
375 return MixerLineSrc;
376
377 Entry = Entry->Flink;
378 }
379
380 return NULL;
381 }
382
383 MIXER_STATUS
384 MMixerGetMixerControlById(
385 LPMIXER_INFO MixerInfo,
386 DWORD dwControlID,
387 LPMIXERLINE_EXT *MixerLine,
388 LPMIXERCONTROLW *MixerControl,
389 PULONG NodeId)
390 {
391 PLIST_ENTRY Entry;
392 LPMIXERLINE_EXT MixerLineSrc;
393 ULONG Index;
394
395 /* get first entry */
396 Entry = MixerInfo->LineList.Flink;
397
398 while(Entry != &MixerInfo->LineList)
399 {
400 MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry);
401
402 for(Index = 0; Index < MixerLineSrc->Line.cControls; Index++)
403 {
404 if (MixerLineSrc->LineControls[Index].dwControlID == dwControlID)
405 {
406 if (MixerLine)
407 *MixerLine = MixerLineSrc;
408 if (MixerControl)
409 *MixerControl = &MixerLineSrc->LineControls[Index];
410 if (NodeId)
411 *NodeId = MixerLineSrc->NodeIds[Index];
412 return MM_STATUS_SUCCESS;
413 }
414 }
415 Entry = Entry->Flink;
416 }
417
418 return MM_STATUS_UNSUCCESSFUL;
419 }
420
421 ULONG
422 MMixerGetVolumeControlIndex(
423 LPMIXERVOLUME_DATA VolumeData,
424 LONG Value)
425 {
426 ULONG Index;
427
428 for(Index = 0; Index < VolumeData->ValuesCount; Index++)
429 {
430 if (VolumeData->Values[Index] > Value)
431 {
432 return VolumeData->InputSteppingDelta * Index;
433 }
434 }
435 return VolumeData->InputSteppingDelta * (VolumeData->ValuesCount-1);
436 }
437
438 MIXER_STATUS
439 MMixerSetGetMuteControlDetails(
440 IN PMIXER_CONTEXT MixerContext,
441 IN HANDLE hMixer,
442 IN ULONG NodeId,
443 IN ULONG dwLineID,
444 IN LPMIXERCONTROLDETAILS MixerControlDetails,
445 IN ULONG bSet)
446 {
447 LPMIXERCONTROLDETAILS_BOOLEAN Input;
448 LONG Value;
449 MIXER_STATUS Status;
450
451 if (MixerControlDetails->cbDetails != sizeof(MIXERCONTROLDETAILS_BOOLEAN))
452 return MM_STATUS_INVALID_PARAMETER;
453
454 /* get input */
455 Input = (LPMIXERCONTROLDETAILS_BOOLEAN)MixerControlDetails->paDetails;
456
457 /* FIXME SEH */
458 if (bSet)
459 Value = Input->fValue;
460
461 /* set control details */
462 Status = MMixerSetGetControlDetails(MixerContext, hMixer, NodeId, bSet, KSPROPERTY_AUDIO_MUTE, 0, &Value);
463
464 if (Status != MM_STATUS_SUCCESS)
465 return Status;
466
467 /* FIXME SEH */
468 if (!bSet)
469 {
470 Input->fValue = Value;
471 return Status;
472 }
473 else
474 {
475 // FIXME notify wdmaud clients MM_MIXM_LINE_CHANGE dwLineID
476 }
477
478 return Status;
479 }
480
481 MIXER_STATUS
482 MMixerSetGetVolumeControlDetails(
483 IN PMIXER_CONTEXT MixerContext,
484 IN HANDLE hMixer,
485 IN ULONG NodeId,
486 IN ULONG bSet,
487 LPMIXERCONTROLW MixerControl,
488 IN LPMIXERCONTROLDETAILS MixerControlDetails,
489 LPMIXERLINE_EXT MixerLine)
490 {
491 LPMIXERCONTROLDETAILS_UNSIGNED Input;
492 LONG Value, Index, Channel = 0;
493 ULONG dwValue;
494 MIXER_STATUS Status;
495 LPMIXERVOLUME_DATA VolumeData;
496
497 if (MixerControlDetails->cbDetails != sizeof(MIXERCONTROLDETAILS_SIGNED))
498 return MM_STATUS_INVALID_PARAMETER;
499
500 VolumeData = (LPMIXERVOLUME_DATA)MMixerGetMixerControlDataById(&MixerLine->LineControlsExtraData, MixerControl->dwControlID);
501 if (!VolumeData)
502 return MM_STATUS_UNSUCCESSFUL;
503
504 /* get input */
505 Input = (LPMIXERCONTROLDETAILS_UNSIGNED)MixerControlDetails->paDetails;
506
507 if (bSet)
508 {
509 /* FIXME SEH */
510 Value = Input->dwValue;
511 Index = Value / VolumeData->InputSteppingDelta;
512
513 if (Index >= VolumeData->ValuesCount)
514 {
515 DPRINT1("Index %u out of bounds %u \n", Index, VolumeData->ValuesCount);
516 DbgBreakPoint();
517 return MM_STATUS_INVALID_PARAMETER;
518 }
519
520 Value = VolumeData->Values[Index];
521 }
522
523 /* set control details */
524 if (bSet)
525 {
526 Status = MMixerSetGetControlDetails(MixerContext, hMixer, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, 0, &Value);
527 Status = MMixerSetGetControlDetails(MixerContext, hMixer, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, 1, &Value);
528 }
529 else
530 {
531 Status = MMixerSetGetControlDetails(MixerContext, hMixer, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, Channel, &Value);
532 }
533
534 if (!bSet)
535 {
536 dwValue = MMixerGetVolumeControlIndex(VolumeData, (LONG)Value);
537 /* FIXME SEH */
538 Input->dwValue = dwValue;
539 }
540 else
541 {
542 /* notify clients of a line change MM_MIXM_CONTROL_CHANGE with MixerControl->dwControlID */
543 }
544 return Status;
545 }