[CMAKE]
[reactos.git] / lib / drivers / sound / mmixer / controls.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Kernel Streaming
4 * FILE: lib/drivers/sound/mmixer/controls.c
5 * PURPOSE: Mixer Control Iteration Functions
6 * PROGRAMMER: Johannes Anderwald
7 */
8
9 #include "priv.h"
10
11 MIXER_STATUS
12 MMixerAddMixerControl(
13 IN PMIXER_CONTEXT MixerContext,
14 IN LPMIXER_INFO MixerInfo,
15 IN PTOPOLOGY Topology,
16 IN ULONG NodeIndex,
17 IN LPMIXERLINE_EXT MixerLine,
18 OUT LPMIXERCONTROLW MixerControl)
19 {
20 LPGUID NodeType;
21 KSP_NODE Node;
22 ULONG BytesReturned;
23 MIXER_STATUS Status;
24 LPWSTR Name;
25
26 /* initialize mixer control */
27 MixerControl->cbStruct = sizeof(MIXERCONTROLW);
28 MixerControl->dwControlID = MixerInfo->ControlId;
29
30 /* get node type */
31 NodeType = MMixerGetNodeTypeFromTopology(Topology, NodeIndex);
32 /* store control type */
33 MixerControl->dwControlType = MMixerGetControlTypeFromTopologyNode(NodeType);
34
35 MixerControl->fdwControl = MIXERCONTROL_CONTROLF_UNIFORM; /* FIXME */
36 MixerControl->cMultipleItems = 0; /* FIXME */
37
38 if (MixerControl->dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE)
39 {
40 MixerControl->Bounds.dwMinimum = 0;
41 MixerControl->Bounds.dwMaximum = 1;
42 }
43 else if (MixerControl->dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)
44 {
45 MixerControl->Bounds.dwMinimum = 0;
46 MixerControl->Bounds.dwMaximum = 0xFFFF;
47 MixerControl->Metrics.cSteps = 0xC0; /* FIXME */
48 }
49
50 /* setup request to retrieve name */
51 Node.NodeId = NodeIndex;
52 Node.Property.Id = KSPROPERTY_TOPOLOGY_NAME;
53 Node.Property.Flags = KSPROPERTY_TYPE_GET;
54 Node.Property.Set = KSPROPSETID_Topology;
55 Node.Reserved = 0;
56
57 /* get node name size */
58 Status = MixerContext->Control(MixerInfo->hMixer, IOCTL_KS_PROPERTY, (PVOID)&Node, sizeof(KSP_NODE), NULL, 0, &BytesReturned);
59
60 if (Status == MM_STATUS_MORE_ENTRIES)
61 {
62 ASSERT(BytesReturned != 0);
63 Name = (LPWSTR)MixerContext->Alloc(BytesReturned);
64 if (!Name)
65 {
66 /* not enough memory */
67 return MM_STATUS_NO_MEMORY;
68 }
69
70 /* get node name */
71 Status = MixerContext->Control(MixerInfo->hMixer, IOCTL_KS_PROPERTY, (PVOID)&Node, sizeof(KSP_NODE), (LPVOID)Name, BytesReturned, &BytesReturned);
72
73 if (Status == MM_STATUS_SUCCESS)
74 {
75 MixerContext->Copy(MixerControl->szShortName, Name, (min(MIXER_SHORT_NAME_CHARS, wcslen(Name)+1)) * sizeof(WCHAR));
76 MixerControl->szShortName[MIXER_SHORT_NAME_CHARS-1] = L'\0';
77
78 MixerContext->Copy(MixerControl->szName, Name, (min(MIXER_LONG_NAME_CHARS, wcslen(Name)+1)) * sizeof(WCHAR));
79 MixerControl->szName[MIXER_LONG_NAME_CHARS-1] = L'\0';
80 }
81
82 /* free name buffer */
83 MixerContext->Free(Name);
84 }
85
86 MixerInfo->ControlId++;
87 #if 0
88 if (MixerControl->dwControlType == MIXERCONTROL_CONTROLTYPE_MUX)
89 {
90 KSNODEPROPERTY Property;
91 ULONG PinId = 2;
92
93 /* setup the request */
94 RtlZeroMemory(&Property, sizeof(KSNODEPROPERTY));
95
96 Property.NodeId = NodeIndex;
97 Property.Property.Id = KSPROPERTY_AUDIO_MUX_SOURCE;
98 Property.Property.Flags = KSPROPERTY_TYPE_SET;
99 Property.Property.Set = KSPROPSETID_Audio;
100
101 /* get node volume level info */
102 Status = MixerContext->Control(hDevice, IOCTL_KS_PROPERTY, (PVOID)&Property, sizeof(KSNODEPROPERTY), (PVOID)&PinId, sizeof(ULONG), &BytesReturned);
103
104 DPRINT1("Status %x NodeIndex %u PinId %u\n", Status, NodeIndex, PinId);
105 //DbgBreakPoint();
106 }else
107 #endif
108 if (MixerControl->dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)
109 {
110 KSNODEPROPERTY_AUDIO_CHANNEL Property;
111 ULONG Length;
112 PKSPROPERTY_DESCRIPTION Desc;
113 PKSPROPERTY_MEMBERSHEADER Members;
114 PKSPROPERTY_STEPPING_LONG Range;
115
116 Length = sizeof(KSPROPERTY_DESCRIPTION) + sizeof(KSPROPERTY_MEMBERSHEADER) + sizeof(KSPROPERTY_STEPPING_LONG);
117 Desc = (PKSPROPERTY_DESCRIPTION)MixerContext->Alloc(Length);
118 ASSERT(Desc);
119
120 /* setup the request */
121 RtlZeroMemory(&Property, sizeof(KSNODEPROPERTY_AUDIO_CHANNEL));
122
123 Property.NodeProperty.NodeId = NodeIndex;
124 Property.NodeProperty.Property.Id = KSPROPERTY_AUDIO_VOLUMELEVEL;
125 Property.NodeProperty.Property.Flags = KSPROPERTY_TYPE_BASICSUPPORT;
126 Property.NodeProperty.Property.Set = KSPROPSETID_Audio;
127
128 /* get node volume level info */
129 Status = MixerContext->Control(MixerInfo->hMixer, IOCTL_KS_PROPERTY, (PVOID)&Property, sizeof(KSNODEPROPERTY_AUDIO_CHANNEL), Desc, Length, &BytesReturned);
130
131 if (Status == MM_STATUS_SUCCESS)
132 {
133 LPMIXERVOLUME_DATA VolumeData;
134 ULONG Steps, MaxRange, Index;
135 LONG Value;
136
137 Members = (PKSPROPERTY_MEMBERSHEADER)(Desc + 1);
138 Range = (PKSPROPERTY_STEPPING_LONG)(Members + 1);
139
140 DPRINT("NodeIndex %u Range Min %d Max %d Steps %x UMin %x UMax %x\n", NodeIndex, Range->Bounds.SignedMinimum, Range->Bounds.SignedMaximum, Range->SteppingDelta, Range->Bounds.UnsignedMinimum, Range->Bounds.UnsignedMaximum);
141
142 MaxRange = Range->Bounds.UnsignedMaximum - Range->Bounds.UnsignedMinimum;
143
144 if (MaxRange)
145 {
146 ASSERT(MaxRange);
147 VolumeData = (LPMIXERVOLUME_DATA)MixerContext->Alloc(sizeof(MIXERVOLUME_DATA));
148 if (!VolumeData)
149 return MM_STATUS_NO_MEMORY;
150
151 Steps = MaxRange / Range->SteppingDelta + 1;
152
153 /* store mixer control info there */
154 VolumeData->Header.dwControlID = MixerControl->dwControlID;
155 VolumeData->SignedMaximum = Range->Bounds.SignedMaximum;
156 VolumeData->SignedMinimum = Range->Bounds.SignedMinimum;
157 VolumeData->SteppingDelta = Range->SteppingDelta;
158 VolumeData->ValuesCount = Steps;
159 VolumeData->InputSteppingDelta = 0x10000 / Steps;
160
161 VolumeData->Values = (PLONG)MixerContext->Alloc(sizeof(LONG) * Steps);
162 if (!VolumeData->Values)
163 {
164 MixerContext->Free(Desc);
165 MixerContext->Free(VolumeData);
166 return MM_STATUS_NO_MEMORY;
167 }
168
169 Value = Range->Bounds.SignedMinimum;
170 for(Index = 0; Index < Steps; Index++)
171 {
172 VolumeData->Values[Index] = Value;
173 Value += Range->SteppingDelta;
174 }
175 InsertTailList(&MixerLine->LineControlsExtraData, &VolumeData->Header.Entry);
176 }
177 }
178 MixerContext->Free(Desc);
179 }
180
181 DPRINT("Status %x Name %S\n", Status, MixerControl->szName);
182 return MM_STATUS_SUCCESS;
183 }
184
185 MIXER_STATUS
186 MMixerCreateDestinationLine(
187 IN PMIXER_CONTEXT MixerContext,
188 IN LPMIXER_INFO MixerInfo,
189 IN ULONG bInputMixer,
190 IN LPWSTR LineName)
191 {
192 LPMIXERLINE_EXT DestinationLine;
193
194 /* allocate a mixer destination line */
195 DestinationLine = (LPMIXERLINE_EXT) MixerContext->Alloc(sizeof(MIXERLINE_EXT));
196 if (!MixerInfo)
197 {
198 /* no memory */
199 return MM_STATUS_NO_MEMORY;
200 }
201
202 /* initialize mixer destination line */
203 DestinationLine->Line.cbStruct = sizeof(MIXERLINEW);
204 DestinationLine->Line.dwSource = MAXULONG;
205 DestinationLine->Line.dwLineID = DESTINATION_LINE;
206 DestinationLine->Line.fdwLine = MIXERLINE_LINEF_ACTIVE;
207 DestinationLine->Line.dwUser = 0;
208 DestinationLine->Line.dwComponentType = (bInputMixer == 0 ? MIXERLINE_COMPONENTTYPE_DST_SPEAKERS : MIXERLINE_COMPONENTTYPE_DST_WAVEIN);
209 DestinationLine->Line.cChannels = 2; /* FIXME */
210
211 if (LineName)
212 {
213 MixerContext->Copy(DestinationLine->Line.szShortName, LineName, (min(MIXER_SHORT_NAME_CHARS, wcslen(LineName)+1)) * sizeof(WCHAR));
214 DestinationLine->Line.szShortName[MIXER_SHORT_NAME_CHARS-1] = L'\0';
215
216 MixerContext->Copy(DestinationLine->Line.szName, LineName, (min(MIXER_LONG_NAME_CHARS, wcslen(LineName)+1)) * sizeof(WCHAR));
217 DestinationLine->Line.szName[MIXER_LONG_NAME_CHARS-1] = L'\0';
218
219 }
220
221 DestinationLine->Line.Target.dwType = (bInputMixer == 0 ? MIXERLINE_TARGETTYPE_WAVEOUT : MIXERLINE_TARGETTYPE_WAVEIN);
222 DestinationLine->Line.Target.dwDeviceID = !bInputMixer;
223 DestinationLine->Line.Target.wMid = MixerInfo->MixCaps.wMid;
224 DestinationLine->Line.Target.wPid = MixerInfo->MixCaps.wPid;
225 DestinationLine->Line.Target.vDriverVersion = MixerInfo->MixCaps.vDriverVersion;
226
227 ASSERT(MixerInfo->MixCaps.szPname[MAXPNAMELEN-1] == 0);
228 wcscpy(DestinationLine->Line.Target.szPname, MixerInfo->MixCaps.szPname);
229
230 /* initialize extra line */
231 InitializeListHead(&DestinationLine->LineControlsExtraData);
232
233 /* insert into mixer info */
234 InsertHeadList(&MixerInfo->LineList, &DestinationLine->Entry);
235
236 /* done */
237 return MM_STATUS_SUCCESS;
238 }
239
240 MIXER_STATUS
241 MMixerGetPinName(
242 IN PMIXER_CONTEXT MixerContext,
243 IN LPMIXER_INFO MixerInfo,
244 IN ULONG PinId,
245 IN OUT LPWSTR * OutBuffer)
246 {
247 KSP_PIN Pin;
248 ULONG BytesReturned;
249 LPWSTR Buffer;
250 MIXER_STATUS Status;
251
252 /* prepare pin */
253 Pin.PinId = PinId;
254 Pin.Reserved = 0;
255 Pin.Property.Flags = KSPROPERTY_TYPE_GET;
256 Pin.Property.Set = KSPROPSETID_Pin;
257 Pin.Property.Id = KSPROPERTY_PIN_NAME;
258
259 /* try get pin name size */
260 Status = MixerContext->Control(MixerInfo->hMixer, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSP_PIN), NULL, 0, &BytesReturned);
261
262 /* check if buffer overflowed */
263 if (Status == MM_STATUS_MORE_ENTRIES)
264 {
265 /* allocate buffer */
266 Buffer = (LPWSTR)MixerContext->Alloc(BytesReturned);
267 if (!Buffer)
268 {
269 /* out of memory */
270 return MM_STATUS_NO_MEMORY;
271 }
272
273 /* try get pin name */
274 Status = MixerContext->Control(MixerInfo->hMixer, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSP_PIN), (PVOID)Buffer, BytesReturned, &BytesReturned);
275 if (Status != MM_STATUS_SUCCESS)
276 {
277 /* failed to get pin name */
278 MixerContext->Free((PVOID)Buffer);
279 return Status;
280 }
281
282 /* successfully obtained pin name */
283 *OutBuffer = Buffer;
284 return MM_STATUS_SUCCESS;
285 }
286
287 /* failed to get pin name */
288 return Status;
289 }
290
291 MIXER_STATUS
292 MMixerBuildMixerDestinationLine(
293 IN PMIXER_CONTEXT MixerContext,
294 IN OUT LPMIXER_INFO MixerInfo,
295 IN ULONG PinId,
296 IN ULONG bInput)
297 {
298 LPWSTR PinName;
299 MIXER_STATUS Status;
300
301 /* try get pin name */
302 Status = MMixerGetPinName(MixerContext, MixerInfo, PinId, &PinName);
303 if (Status == MM_STATUS_SUCCESS)
304 {
305 /* create mixer destination line */
306
307 Status = MMixerCreateDestinationLine(MixerContext, MixerInfo, bInput, PinName);
308
309 /* free pin name */
310 MixerContext->Free(PinName);
311 }
312 else
313 {
314 /* create mixer destination line unlocalized */
315 Status = MMixerCreateDestinationLine(MixerContext, MixerInfo, bInput, L"No Name");
316 }
317
318 return Status;
319 }
320
321 MIXER_STATUS
322 MMixerBuildTopology(
323 IN PMIXER_CONTEXT MixerContext,
324 IN LPMIXER_DATA MixerData,
325 OUT PTOPOLOGY * OutTopology)
326 {
327 ULONG PinsCount;
328 PKSMULTIPLE_ITEM NodeTypes = NULL;
329 PKSMULTIPLE_ITEM NodeConnections = NULL;
330 MIXER_STATUS Status;
331
332 if (MixerData->Topology)
333 {
334 /* re-use existing topology */
335 *OutTopology = MixerData->Topology;
336
337 return MM_STATUS_SUCCESS;
338 }
339
340 /* get connected filter pin count */
341 PinsCount = MMixerGetFilterPinCount(MixerContext, MixerData->hDevice);
342
343 if (!PinsCount)
344 {
345 /* referenced filter does not have any pins */
346 return MM_STATUS_UNSUCCESSFUL;
347 }
348
349 /* get topology node types */
350 Status = MMixerGetFilterTopologyProperty(MixerContext, MixerData->hDevice, KSPROPERTY_TOPOLOGY_NODES, &NodeTypes);
351 if (Status != MM_STATUS_SUCCESS)
352 {
353 /* failed to get topology node types */
354 return Status;
355 }
356
357 /* get topology connections */
358 Status = MMixerGetFilterTopologyProperty(MixerContext, MixerData->hDevice, KSPROPERTY_TOPOLOGY_CONNECTIONS, &NodeConnections);
359 if (Status != MM_STATUS_SUCCESS)
360 {
361 /* failed to get topology connections */
362 MixerContext->Free(NodeTypes);
363 return Status;
364 }
365
366 /* create a topology */
367 Status = MMixerCreateTopology(MixerContext, PinsCount, NodeConnections, NodeTypes, OutTopology);
368
369 /* free node types & connections */
370 MixerContext->Free(NodeConnections);
371 MixerContext->Free(NodeTypes);
372
373 if (Status == MM_STATUS_SUCCESS)
374 {
375 /* store topology object */
376 MixerData->Topology = *OutTopology;
377 }
378
379 /* done */
380 return Status;
381 }
382
383 MIXER_STATUS
384 MMixerCountMixerControls(
385 IN PMIXER_CONTEXT MixerContext,
386 IN PTOPOLOGY Topology,
387 IN ULONG PinId,
388 IN ULONG bUpStream,
389 OUT PULONG OutNodesCount,
390 OUT PULONG OutNodes,
391 OUT PULONG OutLineTerminator)
392 {
393 PULONG Nodes;
394 ULONG NodesCount, NodeIndex, Count, bTerminator;
395 MIXER_STATUS Status;
396
397 /* allocate an array to store all nodes which are upstream of this pin */
398 Status = MMixerAllocateTopologyNodeArray(MixerContext, Topology, &Nodes);
399
400 if (Status != MM_STATUS_SUCCESS)
401 {
402 /* out of memory */
403 return STATUS_NO_MEMORY;
404 }
405
406 /* mark result array as zero */
407 *OutNodesCount = 0;
408
409 /* get next nodes */
410 MMixerGetNextNodesFromPinIndex(MixerContext, Topology, PinId, bUpStream, &NodesCount, Nodes);
411
412 /* assume no topology split before getting line terminator */
413 ASSERT(NodesCount == 1);
414
415 /* get first node */
416 NodeIndex = Nodes[0];
417 Count = 0;
418
419 do
420 {
421 /* check if the node is a terminator */
422 MMixerIsNodeTerminator(Topology, NodeIndex, &bTerminator);
423
424 if (bTerminator)
425 {
426 /* found terminator */
427 break;
428 }
429
430 /* store node id */
431 OutNodes[Count] = NodeIndex;
432
433 /* increment node count */
434 Count++;
435
436 /* get next nodes upstream */
437 MMixerGetNextNodesFromNodeIndex(MixerContext, Topology, NodeIndex, bUpStream, &NodesCount, Nodes);
438
439 /* assume there is a node connected */
440 ASSERT(NodesCount != 0);
441 ASSERT(NodesCount == 1);
442
443 /* use first index */
444 NodeIndex = Nodes[0];
445
446 }while(TRUE);
447
448 /* free node index */
449 MixerContext->Free(Nodes);
450
451 /* store nodes count */
452 *OutNodesCount = Count;
453
454 /* store line terminator */
455 *OutLineTerminator = NodeIndex;
456
457 /* done */
458 return MM_STATUS_SUCCESS;
459 }
460
461 MIXER_STATUS
462 MMixerAddMixerControlsToMixerLineByNodeIndexArray(
463 IN PMIXER_CONTEXT MixerContext,
464 IN LPMIXER_INFO MixerInfo,
465 IN PTOPOLOGY Topology,
466 IN OUT LPMIXERLINE_EXT DstLine,
467 IN ULONG NodesCount,
468 IN PULONG Nodes)
469 {
470 ULONG Index, Count, bReserved;
471 MIXER_STATUS Status;
472
473 /* store nodes array */
474 DstLine->NodeIds = Nodes;
475
476 /* allocate MIXERCONTROLSW array */
477 DstLine->LineControls = MixerContext->Alloc(NodesCount * sizeof(MIXERCONTROLW));
478
479 if (!DstLine->LineControls)
480 {
481 /* out of memory */
482 return MM_STATUS_NO_MEMORY;
483 }
484
485 /* initialize control count */
486 Count = 0;
487
488 for(Index = 0; Index < NodesCount; Index++)
489 {
490 /* check if the node has already been reserved to a line */
491 MMixerIsTopologyNodeReserved(Topology, Nodes[Index], &bReserved);
492
493 if (bReserved)
494 {
495 /* node is already used, skip it */
496 continue;
497 }
498
499 /* set node status as used */
500 MMixerSetTopologyNodeReserved(Topology, Nodes[Index]);
501
502 /* now add the mixer control */
503 Status = MMixerAddMixerControl(MixerContext, MixerInfo, Topology, Nodes[Index], DstLine, &DstLine->LineControls[Count]);
504
505 if (Status == MM_STATUS_SUCCESS)
506 {
507 /* increment control count */
508 Count++;
509 }
510 }
511
512 /* store control count */
513 DstLine->Line.cControls = Count;
514
515 /* done */
516 return MM_STATUS_SUCCESS;
517 }
518
519 MIXER_STATUS
520 MMixerBuildMixerSourceLine(
521 IN PMIXER_CONTEXT MixerContext,
522 IN OUT LPMIXER_INFO MixerInfo,
523 IN PTOPOLOGY Topology,
524 IN ULONG PinId,
525 IN ULONG NodesCount,
526 IN PULONG Nodes,
527 OUT LPMIXERLINE_EXT * OutSrcLine)
528 {
529 LPMIXERLINE_EXT SrcLine, DstLine;
530 LPWSTR PinName;
531 MIXER_STATUS Status;
532
533 /* construct source line */
534 SrcLine = (LPMIXERLINE_EXT)MixerContext->Alloc(sizeof(MIXERLINE_EXT));
535
536 if (!SrcLine)
537 {
538 /* no memory */
539 return MM_STATUS_NO_MEMORY;
540 }
541
542 /* get destination line */
543 DstLine = MMixerGetSourceMixerLineByLineId(MixerInfo, DESTINATION_LINE);
544 ASSERT(DstLine);
545
546 /* initialize mixer src line */
547 SrcLine->hDevice = MixerInfo->hMixer;
548 SrcLine->PinId = PinId;
549 SrcLine->NodeIds = Nodes;
550
551 /* initialize mixer line */
552 SrcLine->Line.cbStruct = sizeof(MIXERLINEW);
553 SrcLine->Line.dwDestination = 0;
554 SrcLine->Line.dwSource = DstLine->Line.cConnections;
555 SrcLine->Line.dwLineID = (DstLine->Line.cConnections * 0x10000);
556 SrcLine->Line.fdwLine = MIXERLINE_LINEF_ACTIVE | MIXERLINE_LINEF_SOURCE;
557 SrcLine->Line.dwUser = 0;
558 SrcLine->Line.cChannels = DstLine->Line.cChannels;
559 SrcLine->Line.cConnections = 0;
560 SrcLine->Line.Target.dwType = 1;
561 SrcLine->Line.Target.dwDeviceID = DstLine->Line.Target.dwDeviceID;
562 SrcLine->Line.Target.wMid = MixerInfo->MixCaps.wMid;
563 SrcLine->Line.Target.wPid = MixerInfo->MixCaps.wPid;
564 SrcLine->Line.Target.vDriverVersion = MixerInfo->MixCaps.vDriverVersion;
565 InitializeListHead(&SrcLine->LineControlsExtraData);
566
567 /* copy name */
568 ASSERT(MixerInfo->MixCaps.szPname[MAXPNAMELEN-1] == L'\0');
569 wcscpy(SrcLine->Line.Target.szPname, MixerInfo->MixCaps.szPname);
570
571 /* get pin name */
572 Status = MMixerGetPinName(MixerContext, MixerInfo, PinId, &PinName);
573
574 if (Status == MM_STATUS_SUCCESS)
575 {
576 /* store pin name as line name */
577 MixerContext->Copy(SrcLine->Line.szShortName, PinName, (min(MIXER_SHORT_NAME_CHARS, wcslen(PinName)+1)) * sizeof(WCHAR));
578 SrcLine->Line.szShortName[MIXER_SHORT_NAME_CHARS-1] = L'\0';
579
580 MixerContext->Copy(SrcLine->Line.szName, PinName, (min(MIXER_LONG_NAME_CHARS, wcslen(PinName)+1)) * sizeof(WCHAR));
581 SrcLine->Line.szName[MIXER_LONG_NAME_CHARS-1] = L'\0';
582
583 /* free pin name buffer */
584 MixerContext->Free(PinName);
585 }
586
587 /* add the controls to mixer line */
588 Status = MMixerAddMixerControlsToMixerLineByNodeIndexArray(MixerContext, MixerInfo, Topology, SrcLine, NodesCount, Nodes);
589 if (Status != MM_STATUS_SUCCESS)
590 {
591 /* failed */
592 return Status;
593 }
594
595 /* store result */
596 *OutSrcLine = SrcLine;
597
598 return MM_STATUS_SUCCESS;
599 }
600
601 MIXER_STATUS
602 MMixerAddMixerSourceLines(
603 IN PMIXER_CONTEXT MixerContext,
604 IN OUT LPMIXER_INFO MixerInfo,
605 IN PTOPOLOGY Topology,
606 IN ULONG LineTerminator)
607 {
608 PULONG AllNodes, AllPins, AllPinNodes;
609 ULONG AllNodesCount, AllPinsCount, AllPinNodesCount;
610 ULONG Index, SubIndex, PinId, CurNode, bConnected;
611 MIXER_STATUS Status;
612 LPMIXERLINE_EXT DstLine, SrcLine;
613
614 /* get destination line */
615 DstLine = MMixerGetSourceMixerLineByLineId(MixerInfo, DESTINATION_LINE);
616 ASSERT(DstLine);
617
618 /* allocate an array to store all nodes which are upstream of the line terminator */
619 Status = MMixerAllocateTopologyNodeArray(MixerContext, Topology, &AllNodes);
620
621 /* check for success */
622 if (Status != MM_STATUS_SUCCESS)
623 {
624 /* out of memory */
625 return MM_STATUS_NO_MEMORY;
626 }
627
628 /* allocate an array to store all nodes which are downstream of a particular pin */
629 Status = MMixerAllocateTopologyNodeArray(MixerContext, Topology, &AllPinNodes);
630
631 /* allocate an array to store all pins which are upstream of this pin */
632 Status = MMixerAllocateTopologyPinArray(MixerContext, Topology, &AllPins);
633
634 /* check for success */
635 if (Status != MM_STATUS_SUCCESS)
636 {
637 /* out of memory */
638 MixerContext->Free(AllNodes);
639 return MM_STATUS_NO_MEMORY;
640 }
641
642 /* get all nodes which indirectly / directly connect to this node */
643 AllNodesCount = 0;
644 MMixerGetAllUpOrDownstreamNodesFromNodeIndex(MixerContext, Topology, LineTerminator, TRUE, &AllNodesCount, AllNodes);
645
646 /* get all pins which indirectly / directly connect to this node */
647 AllPinsCount = 0;
648 MMixerGetAllUpOrDownstreamPinsFromNodeIndex(MixerContext, Topology, LineTerminator, TRUE, &AllPinsCount, AllPins);
649
650 DPRINT("LineTerminator %lu\n", LineTerminator);
651 DPRINT("PinCount %lu\n", AllPinsCount);
652 DPRINT("AllNodesCount %lu\n", AllNodesCount);
653
654 /* now construct the source lines which are attached to the destination line */
655 Index = AllPinsCount;
656
657 do
658 {
659 /* get current pin id */
660 PinId = AllPins[Index - 1];
661
662 /* reset nodes count */
663 AllPinNodesCount = 0;
664
665 /* now scan all nodes and add them to AllPinNodes array when they are connected to this pin */
666 for(SubIndex = 0; SubIndex < AllNodesCount; SubIndex++)
667 {
668 /* get current node index */
669 CurNode = AllNodes[SubIndex];
670
671 if (CurNode != MAXULONG && CurNode != LineTerminator)
672 {
673 /* check if that node is connected in some way to the current pin */
674 Status = MMixerIsNodeConnectedToPin(MixerContext, Topology, CurNode, PinId, TRUE, &bConnected);
675
676 if (Status != MM_STATUS_SUCCESS)
677 break;
678
679 if (bConnected)
680 {
681 /* it is connected */
682 AllPinNodes[AllPinNodesCount] = CurNode;
683 AllPinNodesCount++;
684
685 /* clear current index */
686 AllNodes[SubIndex] = MAXULONG;
687 }
688 }
689 }
690
691 /* decrement pin index */
692 Index--;
693
694 if (AllPinNodesCount)
695 {
696 /* now build the mixer source line */
697 Status = MMixerBuildMixerSourceLine(MixerContext, MixerInfo, Topology, PinId, AllPinNodesCount, AllPinNodes, &SrcLine);
698
699 if (Status == MM_STATUS_SUCCESS)
700 {
701 /* insert into line list */
702 InsertTailList(&MixerInfo->LineList, &SrcLine->Entry);
703
704 /* increment destination line count */
705 DstLine->Line.cConnections++;
706 }
707 }
708
709 }while(Index != 0);
710
711 return MM_STATUS_SUCCESS;
712 }
713
714
715 MIXER_STATUS
716 MMixerAddMixerControlsToDestinationLine(
717 IN PMIXER_CONTEXT MixerContext,
718 IN OUT LPMIXER_INFO MixerInfo,
719 IN PTOPOLOGY Topology,
720 IN ULONG PinId,
721 IN ULONG bInput,
722 OUT PULONG OutLineTerminator)
723 {
724 PULONG Nodes;
725 ULONG NodesCount, LineTerminator;
726 MIXER_STATUS Status;
727 LPMIXERLINE_EXT DstLine;
728
729 /* allocate nodes index array */
730 Status = MMixerAllocateTopologyNodeArray(MixerContext, Topology, &Nodes);
731
732 /* check for success */
733 if (Status != MM_STATUS_SUCCESS)
734 {
735 /* out of memory */
736 return MM_STATUS_NO_MEMORY;
737 }
738
739 /* get all destination line controls */
740 Status = MMixerCountMixerControls(MixerContext, Topology, PinId, TRUE, &NodesCount, Nodes, &LineTerminator);
741
742 /* check for success */
743 if (Status != MM_STATUS_SUCCESS)
744 {
745 /* failed to count controls */
746 MixerContext->Free(Nodes);
747 return Status;
748 }
749
750 /* get destination mixer line */
751 DstLine = MMixerGetSourceMixerLineByLineId(MixerInfo, DESTINATION_LINE);
752
753 /* sanity check */
754 ASSERT(DstLine);
755
756 if (NodesCount > 0)
757 {
758 /* add all nodes as mixer controls to the destination line */
759 Status = MMixerAddMixerControlsToMixerLineByNodeIndexArray(MixerContext, MixerInfo, Topology, DstLine, NodesCount, Nodes);
760 if (Status != MM_STATUS_SUCCESS)
761 {
762 /* failed to add controls */
763 MixerContext->Free(Nodes);
764 return Status;
765 }
766 }
767
768 /* store result */
769 *OutLineTerminator = LineTerminator;
770
771 /* return result */
772 return Status;
773 }
774
775 VOID
776 MMixerApplyOutputFilterHack(
777 IN PMIXER_CONTEXT MixerContext,
778 IN LPMIXER_DATA MixerData,
779 IN OUT PULONG PinsCount,
780 IN OUT PULONG Pins)
781 {
782 ULONG Count = 0, Index;
783 MIXER_STATUS Status;
784 PKSPIN_PHYSICALCONNECTION Connection;
785
786 for(Index = 0; Index < *PinsCount; Index++)
787 {
788 /* check if it has a physical connection */
789 Status = MMixerGetPhysicalConnection(MixerContext, MixerData->hDevice, Pins[Index], &Connection);
790
791 if (Status == MM_STATUS_SUCCESS)
792 {
793 /* remove pin */
794 MixerContext->Copy(&Pins[Index], &Pins[Index + 1], (*PinsCount - (Index + 1)) * sizeof(ULONG));
795
796 /* free physical connection */
797 MixerContext->Free(Connection);
798
799 /* decrement index */
800 Index--;
801
802 /* decrement pin count */
803 (*PinsCount)--;
804 }
805 else
806 {
807 /* simple pin */
808 Count++;
809 }
810 }
811
812 /* store result */
813 *PinsCount = Count;
814 }
815
816 MIXER_STATUS
817 MMixerHandlePhysicalConnection(
818 IN PMIXER_CONTEXT MixerContext,
819 IN PMIXER_LIST MixerList,
820 IN LPMIXER_DATA MixerData,
821 IN OUT LPMIXER_INFO MixerInfo,
822 IN ULONG bInput,
823 IN PKSPIN_PHYSICALCONNECTION OutConnection)
824 {
825 MIXER_STATUS Status;
826 ULONG PinsCount, LineTerminator;
827 PULONG Pins;
828 PTOPOLOGY Topology;
829
830 /* first try to open the connected filter */
831 OutConnection->SymbolicLinkName[1] = L'\\';
832 MixerData = MMixerGetDataByDeviceName(MixerList, OutConnection->SymbolicLinkName);
833
834 /* check if the linked connection is found */
835 if (!MixerData)
836 {
837 /* filter references invalid physical connection */
838 return MM_STATUS_UNSUCCESSFUL;
839 }
840
841 DPRINT("Name %S, Pin %lu bInput %lu\n", OutConnection->SymbolicLinkName, OutConnection->Pin, bInput);
842
843 /* store connected mixer handle */
844 MixerInfo->hMixer = MixerData->hDevice;
845
846
847 Status = MMixerBuildTopology(MixerContext, MixerData, &Topology);
848 if (Status != MM_STATUS_SUCCESS)
849 {
850 /* failed to create topology */
851 return Status;
852 }
853
854 /* allocate pin index array which will hold all referenced pins */
855 Status = MMixerAllocateTopologyPinArray(MixerContext, Topology, &Pins);
856 ASSERT(Status == MM_STATUS_SUCCESS);
857
858 if (!bInput)
859 {
860 /* the mixer is an output mixer
861 * find end pin of the node path
862 */
863 PinsCount = 0;
864 Status = MMixerGetAllUpOrDownstreamPinsFromPinIndex(MixerContext, Topology, OutConnection->Pin, FALSE, &PinsCount, Pins);
865
866 /* check for success */
867 if (Status != MM_STATUS_SUCCESS)
868 {
869 /* failed to get end pin */
870 MixerContext->Free(Pins);
871 //MMixerFreeTopology(Topology);
872
873 /* return error code */
874 return Status;
875 }
876 /* HACK:
877 * some topologies do not have strict boundaries
878 * WorkArround: remove all pin ids which have a physical connection
879 * because bridge pins may belong to different render paths
880 */
881 MMixerApplyOutputFilterHack(MixerContext, MixerData, &PinsCount, Pins);
882
883 /* sanity checks */
884 ASSERT(PinsCount != 0);
885 ASSERT(PinsCount == 1);
886
887 /* create destination line */
888 Status = MMixerBuildMixerDestinationLine(MixerContext, MixerInfo, Pins[0], bInput);
889
890 if (Status != MM_STATUS_SUCCESS)
891 {
892 MixerContext->Free(Pins);
893 //MMixerFreeTopology(Topology);
894
895 /* return error code */
896 return Status;
897 }
898
899 /* add mixer controls to destination line */
900 Status = MMixerAddMixerControlsToDestinationLine(MixerContext, MixerInfo, Topology, Pins[0], bInput, &LineTerminator);
901
902 if (Status == MM_STATUS_SUCCESS)
903 {
904 /* now add the rest of the source lines */
905 Status = MMixerAddMixerSourceLines(MixerContext, MixerInfo, Topology, LineTerminator);
906 }
907 }
908 else
909 {
910 Status = MMixerAddMixerControlsToDestinationLine(MixerContext, MixerInfo, Topology, OutConnection->Pin, bInput, &LineTerminator);
911
912 if (Status == MM_STATUS_SUCCESS)
913 {
914 /* now add the rest of the source lines */
915 Status = MMixerAddMixerSourceLines(MixerContext, MixerInfo, Topology, LineTerminator);
916 }
917 }
918
919 /* free topology */
920 //MMixerFreeTopology(Topology);
921
922 return Status;
923 }
924
925
926 MIXER_STATUS
927 MMixerInitializeFilter(
928 IN PMIXER_CONTEXT MixerContext,
929 IN PMIXER_LIST MixerList,
930 IN LPMIXER_DATA MixerData,
931 IN PTOPOLOGY Topology,
932 IN ULONG NodeIndex,
933 IN ULONG bInputMixer)
934 {
935 LPMIXER_INFO MixerInfo;
936 MIXER_STATUS Status;
937 PKSPIN_PHYSICALCONNECTION OutConnection;
938 ULONG * Pins;
939 ULONG PinsFound;
940
941 /* allocate a mixer info struct */
942 MixerInfo = (LPMIXER_INFO) MixerContext->Alloc(sizeof(MIXER_INFO));
943 if (!MixerInfo)
944 {
945 /* no memory */
946 return MM_STATUS_NO_MEMORY;
947 }
948
949 /* intialize mixer caps */
950 MixerInfo->MixCaps.wMid = MM_MICROSOFT; /* FIXME */
951 MixerInfo->MixCaps.wPid = MM_PID_UNMAPPED; /* FIXME */
952 MixerInfo->MixCaps.vDriverVersion = 1; /* FIXME */
953 MixerInfo->MixCaps.fdwSupport = 0;
954 MixerInfo->MixCaps.cDestinations = 1;
955 MixerInfo->hMixer = MixerData->hDevice;
956
957 /* get mixer name */
958 MMixerGetDeviceName(MixerContext, MixerInfo->MixCaps.szPname, MixerData->hDeviceInterfaceKey);
959
960 /* initialize line list */
961 InitializeListHead(&MixerInfo->LineList);
962 InitializeListHead(&MixerInfo->EventList);
963
964 /* now allocate an array which will receive the indices of the pin
965 * which has a ADC / DAC nodetype in its path
966 */
967 Status = MMixerAllocateTopologyPinArray(MixerContext, Topology, &Pins);
968 ASSERT(Status == MM_STATUS_SUCCESS);
969
970 PinsFound = 0;
971
972 /* now get all sink / source pins, which are attached to the ADC / DAC node
973 * For sink pins (wave out) search up stream
974 * For source pins (wave in) search down stream
975 * The search direction is always the opposite of the current mixer type
976 */
977 PinsFound = 0;
978 MMixerGetAllUpOrDownstreamPinsFromNodeIndex(MixerContext, Topology, NodeIndex, !bInputMixer, &PinsFound, Pins);
979
980 /* if there is now pin found, we have a broken topology */
981 ASSERT(PinsFound != 0);
982
983 /* now create a wave info struct */
984 Status = MMixerInitializeWaveInfo(MixerContext, MixerList, MixerData, MixerInfo->MixCaps.szPname, bInputMixer, PinsFound, Pins);
985 if (Status != MM_STATUS_SUCCESS)
986 {
987 /* failed to create wave info struct */
988 MixerContext->Free(MixerInfo);
989 MixerContext->Free(Pins);
990 return Status;
991 }
992
993 if (bInputMixer)
994 {
995 /* pre create the mixer destination line for input mixers */
996 Status = MMixerBuildMixerDestinationLine(MixerContext, MixerInfo, Pins[0], bInputMixer);
997
998 if (Status != MM_STATUS_SUCCESS)
999 {
1000 /* failed to create mixer destination line */
1001 return Status;
1002 }
1003 }
1004
1005
1006 /* now get the bridge pin which is at the end of node path
1007 * For sink pins (wave out) search down stream
1008 * For source pins (wave in) search up stream
1009 */
1010 MixerContext->Free(Pins);
1011 Status = MMixerAllocateTopologyPinArray(MixerContext, Topology, &Pins);
1012 ASSERT(Status == MM_STATUS_SUCCESS);
1013
1014 PinsFound = 0;
1015 MMixerGetAllUpOrDownstreamPinsFromNodeIndex(MixerContext, Topology, NodeIndex, bInputMixer, &PinsFound, Pins);
1016
1017 /* if there is no pin found, we have a broken topology */
1018 ASSERT(PinsFound != 0);
1019
1020 /* there should be exactly one bridge pin */
1021 ASSERT(PinsFound == 1);
1022
1023 DPRINT("BridgePin %lu bInputMixer %lu\n", Pins[0], bInputMixer);
1024
1025 /* does the pin have a physical connection */
1026 Status = MMixerGetPhysicalConnection(MixerContext, MixerData->hDevice, Pins[0], &OutConnection);
1027
1028 if (Status == MM_STATUS_SUCCESS)
1029 {
1030 /* topology on the topoloy filter */
1031 Status = MMixerHandlePhysicalConnection(MixerContext, MixerList, MixerData, MixerInfo, bInputMixer, OutConnection);
1032
1033 /* free physical connection data */
1034 MixerContext->Free(OutConnection);
1035 }
1036 else
1037 {
1038 /* FIXME
1039 * handle drivers which expose their topology on the same filter
1040 */
1041 ASSERT(0);
1042 }
1043
1044 /* free pins */
1045 MixerContext->Free(Pins);
1046
1047 if (!bInputMixer && MixerList->MixerListCount == 1)
1048 {
1049 /* FIXME preferred device should be inserted at front
1050 * windows always inserts output mixer in front
1051 */
1052 InsertHeadList(&MixerList->MixerList, &MixerInfo->Entry);
1053 }
1054 else
1055 {
1056 /* insert at back */
1057 InsertTailList(&MixerList->MixerList, &MixerInfo->Entry);
1058 }
1059
1060 /* increment mixer count */
1061 MixerList->MixerListCount++;
1062
1063 /* done */
1064 return Status;
1065 }
1066
1067 MIXER_STATUS
1068 MMixerSetupFilter(
1069 IN PMIXER_CONTEXT MixerContext,
1070 IN PMIXER_LIST MixerList,
1071 IN LPMIXER_DATA MixerData,
1072 IN PULONG DeviceCount)
1073 {
1074 MIXER_STATUS Status;
1075 PTOPOLOGY Topology;
1076 ULONG NodeIndex;
1077
1078 /* check if topology has already been built */
1079 if (MixerData->Topology == NULL)
1080 {
1081 /* build topology */
1082 Status = MMixerBuildTopology(MixerContext, MixerData, &Topology);
1083
1084 if (Status != MM_STATUS_SUCCESS)
1085 {
1086 /* failed to build topology */
1087 return Status;
1088 }
1089
1090 /* store topology */
1091 MixerData->Topology = Topology;
1092 }
1093 else
1094 {
1095 /* re-use topology */
1096 Topology = MixerData->Topology;
1097 }
1098
1099 /* check if the filter has an wave out node */
1100 NodeIndex = MMixerGetNodeIndexFromGuid(Topology, &KSNODETYPE_DAC);
1101 if (NodeIndex != MAXULONG)
1102 {
1103 /* it has */
1104 Status = MMixerInitializeFilter(MixerContext, MixerList, MixerData, Topology, NodeIndex, FALSE);
1105
1106 /* check for success */
1107 if (Status == MM_STATUS_SUCCESS)
1108 {
1109 /* increment mixer count */
1110 (*DeviceCount)++;
1111 }
1112
1113 }
1114
1115 /* check if the filter has an wave in node */
1116 NodeIndex = MMixerGetNodeIndexFromGuid(Topology, &KSNODETYPE_ADC);
1117 if (NodeIndex != MAXULONG)
1118 {
1119 /* it has */
1120 Status = MMixerInitializeFilter(MixerContext, MixerList, MixerData, Topology, NodeIndex, TRUE);
1121
1122 /* check for success */
1123 if (Status == MM_STATUS_SUCCESS)
1124 {
1125 /* increment mixer count */
1126 (*DeviceCount)++;
1127 }
1128
1129 }
1130
1131 /* activate midi devices */
1132 MMixerInitializeMidiForFilter(MixerContext, MixerList, MixerData, Topology);
1133
1134 /* done */
1135 return Status;
1136 }
1137
1138
1139 MIXER_STATUS
1140 MMixerAddEvent(
1141 IN PMIXER_CONTEXT MixerContext,
1142 IN OUT LPMIXER_INFO MixerInfo,
1143 IN PVOID MixerEventContext,
1144 IN PMIXER_EVENT MixerEventRoutine)
1145 {
1146 //KSE_NODE Property;
1147 PEVENT_NOTIFICATION_ENTRY EventData;
1148 //ULONG BytesReturned;
1149 //MIXER_STATUS Status;
1150
1151 EventData = (PEVENT_NOTIFICATION_ENTRY)MixerContext->AllocEventData(sizeof(EVENT_NOTIFICATION_ENTRY));
1152 if (!EventData)
1153 {
1154 /* not enough memory */
1155 return MM_STATUS_NO_MEMORY;
1156 }
1157
1158 #if 0
1159 /* setup request */
1160 Property.Event.Set = KSEVENTSETID_AudioControlChange;
1161 Property.Event.Flags = KSEVENT_TYPE_TOPOLOGY|KSEVENT_TYPE_ENABLE;
1162 Property.Event.Id = KSEVENT_CONTROL_CHANGE;
1163
1164 Property.NodeId = NodeId;
1165 Property.Reserved = 0;
1166
1167 Status = MixerContext->Control(MixerInfo->hMixer, IOCTL_KS_ENABLE_EVENT, (PVOID)&Property, sizeof(KSP_NODE), (PVOID)EventData, sizeof(KSEVENTDATA), &BytesReturned);
1168 if (Status != MM_STATUS_SUCCESS)
1169 {
1170 /* failed to add event */
1171 MixerContext->FreeEventData(EventData);
1172 return Status;
1173 }
1174 #endif
1175
1176 /* initialize notification entry */
1177 EventData->MixerEventContext = MixerEventContext;
1178 EventData->MixerEventRoutine;
1179
1180 /* store event */
1181 InsertTailList(&MixerInfo->EventList, &EventData->Entry);
1182 return MM_STATUS_SUCCESS;
1183 }
1184