[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 if (!Count)
255 {
256 *NodeReferenceCount = 0;
257 *NodeReference = NULL;
258 return MM_STATUS_SUCCESS;
259 }
260
261 ASSERT(Count != 0);
262
263 /* now allocate node index array */
264 Refs = (PULONG)MixerContext->Alloc(sizeof(ULONG) * Count);
265 if (!Refs)
266 {
267 // not enough memory
268 return MM_STATUS_NO_MEMORY;
269 }
270
271 Count = 0;
272 Connection = (PKSTOPOLOGY_CONNECTION)(MultipleItem + 1);
273 for(Index = 0; Index < MultipleItem->Count; Index++)
274 {
275 if (bNode)
276 {
277 if (bFrom)
278 {
279 if (Connection->FromNode == NodeIndex)
280 {
281 /* node id has a connection */
282 Refs[Count] = Index;
283 Count++;
284 }
285 }
286 else
287 {
288 if (Connection->ToNode == NodeIndex)
289 {
290 /* node id has a connection */
291 Refs[Count] = Index;
292 Count++;
293 }
294 }
295 }
296 else
297 {
298 if (bFrom)
299 {
300 if (Connection->FromNodePin == NodeIndex && Connection->FromNode == KSFILTER_NODE)
301 {
302 /* node id has a connection */
303 Refs[Count] = Index;
304 Count++;
305 }
306 }
307 else
308 {
309 if (Connection->ToNodePin == NodeIndex && Connection->ToNode == KSFILTER_NODE)
310 {
311 /* node id has a connection */
312 Refs[Count] = Index;
313 Count++;
314 }
315 }
316 }
317
318 /* move to next connection */
319 Connection++;
320 }
321
322 /* store result */
323 *NodeReference = Refs;
324 *NodeReferenceCount = Count;
325
326 return MM_STATUS_SUCCESS;
327 }
328
329 MIXER_STATUS
330 MMixerGetTargetPins(
331 IN PMIXER_CONTEXT MixerContext,
332 IN PKSMULTIPLE_ITEM NodeTypes,
333 IN PKSMULTIPLE_ITEM NodeConnections,
334 IN ULONG NodeIndex,
335 IN ULONG bUpDirection,
336 OUT PULONG Pins,
337 IN ULONG PinCount)
338 {
339 ULONG NodeConnectionCount, Index;
340 MIXER_STATUS Status;
341 PULONG NodeConnection;
342
343 // sanity check */
344 ASSERT(NodeIndex != (ULONG)-1);
345
346 /* get all node indexes referenced by that pin */
347 if (bUpDirection)
348 Status = MMixerGetNodeIndexes(MixerContext, NodeConnections, NodeIndex, TRUE, FALSE, &NodeConnectionCount, &NodeConnection);
349 else
350 Status = MMixerGetNodeIndexes(MixerContext, NodeConnections, NodeIndex, TRUE, TRUE, &NodeConnectionCount, &NodeConnection);
351
352 //DPRINT("NodeIndex %u Status %x Count %u\n", NodeIndex, Status, NodeConnectionCount);
353
354 if (Status == MM_STATUS_SUCCESS)
355 {
356 for(Index = 0; Index < NodeConnectionCount; Index++)
357 {
358 Status = MMixerGetTargetPinsByNodeConnectionIndex(MixerContext, NodeConnections, NodeTypes, bUpDirection, NodeConnection[Index], Pins);
359 ASSERT(Status == STATUS_SUCCESS);
360 }
361 MixerContext->Free((PVOID)NodeConnection);
362 }
363
364 return Status;
365 }
366
367 LPMIXERLINE_EXT
368 MMixerGetSourceMixerLineByComponentType(
369 LPMIXER_INFO MixerInfo,
370 DWORD dwComponentType)
371 {
372 PLIST_ENTRY Entry;
373 LPMIXERLINE_EXT MixerLineSrc;
374
375 /* get first entry */
376 Entry = MixerInfo->LineList.Flink;
377
378 while(Entry != &MixerInfo->LineList)
379 {
380 MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry);
381 if (MixerLineSrc->Line.dwComponentType == dwComponentType)
382 return MixerLineSrc;
383
384 Entry = Entry->Flink;
385 }
386
387 return NULL;
388 }
389
390 MIXER_STATUS
391 MMixerGetMixerControlById(
392 LPMIXER_INFO MixerInfo,
393 DWORD dwControlID,
394 LPMIXERLINE_EXT *MixerLine,
395 LPMIXERCONTROLW *MixerControl,
396 PULONG NodeId)
397 {
398 PLIST_ENTRY Entry;
399 LPMIXERLINE_EXT MixerLineSrc;
400 ULONG Index;
401
402 /* get first entry */
403 Entry = MixerInfo->LineList.Flink;
404
405 while(Entry != &MixerInfo->LineList)
406 {
407 MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry);
408
409 for(Index = 0; Index < MixerLineSrc->Line.cControls; Index++)
410 {
411 if (MixerLineSrc->LineControls[Index].dwControlID == dwControlID)
412 {
413 if (MixerLine)
414 *MixerLine = MixerLineSrc;
415 if (MixerControl)
416 *MixerControl = &MixerLineSrc->LineControls[Index];
417 if (NodeId)
418 *NodeId = MixerLineSrc->NodeIds[Index];
419 return MM_STATUS_SUCCESS;
420 }
421 }
422 Entry = Entry->Flink;
423 }
424
425 return MM_STATUS_UNSUCCESSFUL;
426 }
427
428 ULONG
429 MMixerGetVolumeControlIndex(
430 LPMIXERVOLUME_DATA VolumeData,
431 LONG Value)
432 {
433 ULONG Index;
434
435 for(Index = 0; Index < VolumeData->ValuesCount; Index++)
436 {
437 if (VolumeData->Values[Index] > Value)
438 {
439 return VolumeData->InputSteppingDelta * Index;
440 }
441 }
442 return VolumeData->InputSteppingDelta * (VolumeData->ValuesCount-1);
443 }
444
445 MIXER_STATUS
446 MMixerSetGetMuteControlDetails(
447 IN PMIXER_CONTEXT MixerContext,
448 IN HANDLE hMixer,
449 IN ULONG NodeId,
450 IN ULONG dwLineID,
451 IN LPMIXERCONTROLDETAILS MixerControlDetails,
452 IN ULONG bSet)
453 {
454 LPMIXERCONTROLDETAILS_BOOLEAN Input;
455 LONG Value;
456 MIXER_STATUS Status;
457
458 if (MixerControlDetails->cbDetails != sizeof(MIXERCONTROLDETAILS_BOOLEAN))
459 return MM_STATUS_INVALID_PARAMETER;
460
461 /* get input */
462 Input = (LPMIXERCONTROLDETAILS_BOOLEAN)MixerControlDetails->paDetails;
463
464 /* FIXME SEH */
465 if (bSet)
466 Value = Input->fValue;
467
468 /* set control details */
469 Status = MMixerSetGetControlDetails(MixerContext, hMixer, NodeId, bSet, KSPROPERTY_AUDIO_MUTE, 0, &Value);
470
471 if (Status != MM_STATUS_SUCCESS)
472 return Status;
473
474 /* FIXME SEH */
475 if (!bSet)
476 {
477 Input->fValue = Value;
478 return Status;
479 }
480 else
481 {
482 // FIXME notify wdmaud clients MM_MIXM_LINE_CHANGE dwLineID
483 }
484
485 return Status;
486 }
487
488 MIXER_STATUS
489 MMixerSetGetVolumeControlDetails(
490 IN PMIXER_CONTEXT MixerContext,
491 IN HANDLE hMixer,
492 IN ULONG NodeId,
493 IN ULONG bSet,
494 LPMIXERCONTROLW MixerControl,
495 IN LPMIXERCONTROLDETAILS MixerControlDetails,
496 LPMIXERLINE_EXT MixerLine)
497 {
498 LPMIXERCONTROLDETAILS_UNSIGNED Input;
499 LONG Value, Index, Channel = 0;
500 ULONG dwValue;
501 MIXER_STATUS Status;
502 LPMIXERVOLUME_DATA VolumeData;
503
504 if (MixerControlDetails->cbDetails != sizeof(MIXERCONTROLDETAILS_SIGNED))
505 return MM_STATUS_INVALID_PARAMETER;
506
507 VolumeData = (LPMIXERVOLUME_DATA)MMixerGetMixerControlDataById(&MixerLine->LineControlsExtraData, MixerControl->dwControlID);
508 if (!VolumeData)
509 return MM_STATUS_UNSUCCESSFUL;
510
511 /* get input */
512 Input = (LPMIXERCONTROLDETAILS_UNSIGNED)MixerControlDetails->paDetails;
513
514 if (bSet)
515 {
516 /* FIXME SEH */
517 Value = Input->dwValue;
518 Index = Value / VolumeData->InputSteppingDelta;
519
520 if (Index >= VolumeData->ValuesCount)
521 {
522 DPRINT1("Index %u out of bounds %u \n", Index, VolumeData->ValuesCount);
523 DbgBreakPoint();
524 return MM_STATUS_INVALID_PARAMETER;
525 }
526
527 Value = VolumeData->Values[Index];
528 }
529
530 /* set control details */
531 if (bSet)
532 {
533 Status = MMixerSetGetControlDetails(MixerContext, hMixer, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, 0, &Value);
534 Status = MMixerSetGetControlDetails(MixerContext, hMixer, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, 1, &Value);
535 }
536 else
537 {
538 Status = MMixerSetGetControlDetails(MixerContext, hMixer, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, Channel, &Value);
539 }
540
541 if (!bSet)
542 {
543 dwValue = MMixerGetVolumeControlIndex(VolumeData, (LONG)Value);
544 /* FIXME SEH */
545 Input->dwValue = dwValue;
546 }
547 else
548 {
549 /* notify clients of a line change MM_MIXM_CONTROL_CHANGE with MixerControl->dwControlID */
550 }
551 return Status;
552 }