[AUDIO-BRINGUP]
[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 = MixerInfo->MixCaps.cDestinations + DESTINATION_LINE;
206 DestinationLine->Line.fdwLine = MIXERLINE_LINEF_ACTIVE;
207 DestinationLine->Line.dwUser = 0;
208 DestinationLine->Line.dwDestination = MixerInfo->MixCaps.cDestinations;
209 DestinationLine->Line.dwComponentType = (bInputMixer == 0 ? MIXERLINE_COMPONENTTYPE_DST_SPEAKERS : MIXERLINE_COMPONENTTYPE_DST_WAVEIN);
210 DestinationLine->Line.cChannels = 2; /* FIXME */
211
212 if (LineName)
213 {
214 MixerContext->Copy(DestinationLine->Line.szShortName, LineName, (min(MIXER_SHORT_NAME_CHARS, wcslen(LineName)+1)) * sizeof(WCHAR));
215 DestinationLine->Line.szShortName[MIXER_SHORT_NAME_CHARS-1] = L'\0';
216
217 MixerContext->Copy(DestinationLine->Line.szName, LineName, (min(MIXER_LONG_NAME_CHARS, wcslen(LineName)+1)) * sizeof(WCHAR));
218 DestinationLine->Line.szName[MIXER_LONG_NAME_CHARS-1] = L'\0';
219
220 }
221
222 DestinationLine->Line.Target.dwType = (bInputMixer == 0 ? MIXERLINE_TARGETTYPE_WAVEOUT : MIXERLINE_TARGETTYPE_WAVEIN);
223 DestinationLine->Line.Target.dwDeviceID = 0; //FIXME
224 DestinationLine->Line.Target.wMid = MixerInfo->MixCaps.wMid;
225 DestinationLine->Line.Target.wPid = MixerInfo->MixCaps.wPid;
226 DestinationLine->Line.Target.vDriverVersion = MixerInfo->MixCaps.vDriverVersion;
227
228 ASSERT(MixerInfo->MixCaps.szPname[MAXPNAMELEN-1] == 0);
229 wcscpy(DestinationLine->Line.Target.szPname, MixerInfo->MixCaps.szPname);
230
231 /* initialize extra line */
232 InitializeListHead(&DestinationLine->LineControlsExtraData);
233
234 /* insert into mixer info */
235 InsertTailList(&MixerInfo->LineList, &DestinationLine->Entry);
236
237 /* increment destination count */
238 MixerInfo->MixCaps.cDestinations++;
239
240 /* done */
241 return MM_STATUS_SUCCESS;
242 }
243
244 MIXER_STATUS
245 MMixerGetPinName(
246 IN PMIXER_CONTEXT MixerContext,
247 IN LPMIXER_INFO MixerInfo,
248 IN ULONG PinId,
249 IN OUT LPWSTR * OutBuffer)
250 {
251 KSP_PIN Pin;
252 ULONG BytesReturned;
253 LPWSTR Buffer;
254 MIXER_STATUS Status;
255
256 /* prepare pin */
257 Pin.PinId = PinId;
258 Pin.Reserved = 0;
259 Pin.Property.Flags = KSPROPERTY_TYPE_GET;
260 Pin.Property.Set = KSPROPSETID_Pin;
261 Pin.Property.Id = KSPROPERTY_PIN_NAME;
262
263 /* try get pin name size */
264 Status = MixerContext->Control(MixerInfo->hMixer, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSP_PIN), NULL, 0, &BytesReturned);
265
266 /* check if buffer overflowed */
267 if (Status == MM_STATUS_MORE_ENTRIES)
268 {
269 /* allocate buffer */
270 Buffer = (LPWSTR)MixerContext->Alloc(BytesReturned);
271 if (!Buffer)
272 {
273 /* out of memory */
274 return MM_STATUS_NO_MEMORY;
275 }
276
277 /* try get pin name */
278 Status = MixerContext->Control(MixerInfo->hMixer, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSP_PIN), (PVOID)Buffer, BytesReturned, &BytesReturned);
279 if (Status != MM_STATUS_SUCCESS)
280 {
281 /* failed to get pin name */
282 MixerContext->Free((PVOID)Buffer);
283 return Status;
284 }
285
286 /* successfully obtained pin name */
287 *OutBuffer = Buffer;
288 return MM_STATUS_SUCCESS;
289 }
290
291 /* failed to get pin name */
292 return Status;
293 }
294
295 MIXER_STATUS
296 MMixerBuildMixerDestinationLine(
297 IN PMIXER_CONTEXT MixerContext,
298 IN OUT LPMIXER_INFO MixerInfo,
299 IN ULONG PinId,
300 IN ULONG bInput)
301 {
302 LPWSTR PinName;
303 MIXER_STATUS Status;
304
305 /* try get pin name */
306 Status = MMixerGetPinName(MixerContext, MixerInfo, PinId, &PinName);
307 if (Status == MM_STATUS_SUCCESS)
308 {
309 /* create mixer destination line */
310
311 Status = MMixerCreateDestinationLine(MixerContext, MixerInfo, bInput, PinName);
312
313 /* free pin name */
314 MixerContext->Free(PinName);
315 }
316 else
317 {
318 /* create mixer destination line unlocalized */
319 Status = MMixerCreateDestinationLine(MixerContext, MixerInfo, bInput, L"No Name");
320 }
321
322 return Status;
323 }
324
325 MIXER_STATUS
326 MMixerBuildTopology(
327 IN PMIXER_CONTEXT MixerContext,
328 IN LPMIXER_DATA MixerData,
329 OUT PTOPOLOGY * OutTopology)
330 {
331 ULONG PinsCount;
332 PKSMULTIPLE_ITEM NodeTypes = NULL;
333 PKSMULTIPLE_ITEM NodeConnections = NULL;
334 MIXER_STATUS Status;
335
336 if (MixerData->Topology)
337 {
338 /* re-use existing topology */
339 *OutTopology = MixerData->Topology;
340
341 return MM_STATUS_SUCCESS;
342 }
343
344 /* get connected filter pin count */
345 PinsCount = MMixerGetFilterPinCount(MixerContext, MixerData->hDevice);
346
347 if (!PinsCount)
348 {
349 /* referenced filter does not have any pins */
350 return MM_STATUS_UNSUCCESSFUL;
351 }
352
353 /* get topology node types */
354 Status = MMixerGetFilterTopologyProperty(MixerContext, MixerData->hDevice, KSPROPERTY_TOPOLOGY_NODES, &NodeTypes);
355 if (Status != MM_STATUS_SUCCESS)
356 {
357 /* failed to get topology node types */
358 return Status;
359 }
360
361 /* get topology connections */
362 Status = MMixerGetFilterTopologyProperty(MixerContext, MixerData->hDevice, KSPROPERTY_TOPOLOGY_CONNECTIONS, &NodeConnections);
363 if (Status != MM_STATUS_SUCCESS)
364 {
365 /* failed to get topology connections */
366 MixerContext->Free(NodeTypes);
367 return Status;
368 }
369
370 /* create a topology */
371 Status = MMixerCreateTopology(MixerContext, PinsCount, NodeConnections, NodeTypes, OutTopology);
372
373 /* free node types & connections */
374 MixerContext->Free(NodeConnections);
375 MixerContext->Free(NodeTypes);
376
377 if (Status == MM_STATUS_SUCCESS)
378 {
379 /* store topology object */
380 MixerData->Topology = *OutTopology;
381 }
382
383 /* done */
384 return Status;
385 }
386
387 MIXER_STATUS
388 MMixerCountMixerControls(
389 IN PMIXER_CONTEXT MixerContext,
390 IN PTOPOLOGY Topology,
391 IN ULONG PinId,
392 IN ULONG bInputMixer,
393 IN ULONG bUpStream,
394 OUT PULONG OutNodesCount,
395 OUT PULONG OutNodes,
396 OUT PULONG OutLineTerminator)
397 {
398 PULONG Nodes;
399 ULONG NodesCount, NodeIndex, Count, bTerminator;
400 MIXER_STATUS Status;
401
402 /* allocate an array to store all nodes which are upstream of this pin */
403 Status = MMixerAllocateTopologyNodeArray(MixerContext, Topology, &Nodes);
404
405 if (Status != MM_STATUS_SUCCESS)
406 {
407 /* out of memory */
408 return STATUS_NO_MEMORY;
409 }
410
411 /* mark result array as zero */
412 *OutNodesCount = 0;
413
414 /* get next nodes */
415 MMixerGetNextNodesFromPinIndex(MixerContext, Topology, PinId, bUpStream, &NodesCount, Nodes);
416
417 /* assume no topology split before getting line terminator */
418 ASSERT(NodesCount == 1);
419
420 /* get first node */
421 NodeIndex = Nodes[0];
422 Count = 0;
423
424 do
425 {
426 /* check if the node is a terminator */
427 MMixerIsNodeTerminator(Topology, NodeIndex, &bTerminator);
428
429 if (bTerminator)
430 {
431 /* found terminator */
432 if (bInputMixer)
433 {
434 /* add mux source for source destination line */
435 OutNodes[Count] = NodeIndex;
436 Count++;
437 }
438 break;
439 }
440
441 /* store node id */
442 OutNodes[Count] = NodeIndex;
443
444 /* increment node count */
445 Count++;
446
447 /* get next nodes upstream */
448 MMixerGetNextNodesFromNodeIndex(MixerContext, Topology, NodeIndex, bUpStream, &NodesCount, Nodes);
449
450 /* assume there is a node connected */
451 ASSERT(NodesCount != 0);
452 ASSERT(NodesCount == 1);
453
454 /* use first index */
455 NodeIndex = Nodes[0];
456
457 }while(TRUE);
458
459 /* free node index */
460 MixerContext->Free(Nodes);
461
462 /* store nodes count */
463 *OutNodesCount = Count;
464
465 /* store line terminator */
466 *OutLineTerminator = NodeIndex;
467
468 /* done */
469 return MM_STATUS_SUCCESS;
470 }
471
472 MIXER_STATUS
473 MMixerAddMixerControlsToMixerLineByNodeIndexArray(
474 IN PMIXER_CONTEXT MixerContext,
475 IN LPMIXER_INFO MixerInfo,
476 IN PTOPOLOGY Topology,
477 IN OUT LPMIXERLINE_EXT DstLine,
478 IN ULONG NodesCount,
479 IN PULONG Nodes)
480 {
481 ULONG Index, Count, bReserved;
482 MIXER_STATUS Status;
483
484 /* store nodes array */
485 DstLine->NodeIds = Nodes;
486
487 /* allocate MIXERCONTROLSW array */
488 DstLine->LineControls = MixerContext->Alloc(NodesCount * sizeof(MIXERCONTROLW));
489
490 if (!DstLine->LineControls)
491 {
492 /* out of memory */
493 return MM_STATUS_NO_MEMORY;
494 }
495
496 /* initialize control count */
497 Count = 0;
498
499 for(Index = 0; Index < NodesCount; Index++)
500 {
501 /* check if the node has already been reserved to a line */
502 MMixerIsTopologyNodeReserved(Topology, Nodes[Index], &bReserved);
503 #if 0 /* MS lies */
504 if (bReserved)
505 {
506 /* node is already used, skip it */
507 continue;
508 }
509 #endif
510 /* set node status as used */
511 MMixerSetTopologyNodeReserved(Topology, Nodes[Index]);
512
513 /* now add the mixer control */
514 Status = MMixerAddMixerControl(MixerContext, MixerInfo, Topology, Nodes[Index], DstLine, &DstLine->LineControls[Count]);
515
516 if (Status == MM_STATUS_SUCCESS)
517 {
518 /* increment control count */
519 Count++;
520 }
521 }
522
523 /* store control count */
524 DstLine->Line.cControls = Count;
525
526 /* done */
527 return MM_STATUS_SUCCESS;
528 }
529
530 MIXER_STATUS
531 MMixerBuildMixerSourceLine(
532 IN PMIXER_CONTEXT MixerContext,
533 IN OUT LPMIXER_INFO MixerInfo,
534 IN PTOPOLOGY Topology,
535 IN ULONG PinId,
536 IN ULONG NodesCount,
537 IN PULONG Nodes,
538 IN ULONG DestinationLineID,
539 OUT LPMIXERLINE_EXT * OutSrcLine)
540 {
541 LPMIXERLINE_EXT SrcLine, DstLine;
542 LPWSTR PinName;
543 MIXER_STATUS Status;
544
545 /* construct source line */
546 SrcLine = (LPMIXERLINE_EXT)MixerContext->Alloc(sizeof(MIXERLINE_EXT));
547
548 if (!SrcLine)
549 {
550 /* no memory */
551 return MM_STATUS_NO_MEMORY;
552 }
553
554 /* get destination line */
555 DstLine = MMixerGetSourceMixerLineByLineId(MixerInfo, DestinationLineID);
556 ASSERT(DstLine);
557
558 /* initialize mixer src line */
559 SrcLine->hDevice = MixerInfo->hMixer;
560 SrcLine->PinId = PinId;
561 SrcLine->NodeIds = Nodes;
562
563 /* initialize mixer line */
564 SrcLine->Line.cbStruct = sizeof(MIXERLINEW);
565 SrcLine->Line.dwDestination = 0;
566 SrcLine->Line.dwSource = DstLine->Line.cConnections;
567 SrcLine->Line.dwLineID = (DstLine->Line.cConnections * 0x10000)+ (MixerInfo->MixCaps.cDestinations-1);
568 SrcLine->Line.fdwLine = MIXERLINE_LINEF_ACTIVE | MIXERLINE_LINEF_SOURCE;
569 SrcLine->Line.dwUser = 0;
570 SrcLine->Line.cChannels = DstLine->Line.cChannels;
571 SrcLine->Line.cConnections = 0;
572 SrcLine->Line.Target.dwType = 1;
573 SrcLine->Line.Target.dwDeviceID = DstLine->Line.Target.dwDeviceID;
574 SrcLine->Line.Target.wMid = MixerInfo->MixCaps.wMid;
575 SrcLine->Line.Target.wPid = MixerInfo->MixCaps.wPid;
576 SrcLine->Line.Target.vDriverVersion = MixerInfo->MixCaps.vDriverVersion;
577 InitializeListHead(&SrcLine->LineControlsExtraData);
578
579 /* copy name */
580 ASSERT(MixerInfo->MixCaps.szPname[MAXPNAMELEN-1] == L'\0');
581 wcscpy(SrcLine->Line.Target.szPname, MixerInfo->MixCaps.szPname);
582
583 /* get pin name */
584 Status = MMixerGetPinName(MixerContext, MixerInfo, PinId, &PinName);
585
586 if (Status == MM_STATUS_SUCCESS)
587 {
588 /* store pin name as line name */
589 MixerContext->Copy(SrcLine->Line.szShortName, PinName, (min(MIXER_SHORT_NAME_CHARS, wcslen(PinName)+1)) * sizeof(WCHAR));
590 SrcLine->Line.szShortName[MIXER_SHORT_NAME_CHARS-1] = L'\0';
591
592 MixerContext->Copy(SrcLine->Line.szName, PinName, (min(MIXER_LONG_NAME_CHARS, wcslen(PinName)+1)) * sizeof(WCHAR));
593 SrcLine->Line.szName[MIXER_LONG_NAME_CHARS-1] = L'\0';
594
595 /* free pin name buffer */
596 MixerContext->Free(PinName);
597 }
598
599 /* add the controls to mixer line */
600 Status = MMixerAddMixerControlsToMixerLineByNodeIndexArray(MixerContext, MixerInfo, Topology, SrcLine, NodesCount, Nodes);
601 if (Status != MM_STATUS_SUCCESS)
602 {
603 /* failed */
604 return Status;
605 }
606
607 /* store result */
608 *OutSrcLine = SrcLine;
609
610 return MM_STATUS_SUCCESS;
611 }
612
613 MIXER_STATUS
614 MMixerAddMixerSourceLines(
615 IN PMIXER_CONTEXT MixerContext,
616 IN OUT LPMIXER_INFO MixerInfo,
617 IN PTOPOLOGY Topology,
618 IN ULONG DestinationLineID,
619 IN ULONG LineTerminator)
620 {
621 PULONG AllNodes, AllPins, AllPinNodes;
622 ULONG AllNodesCount, AllPinsCount, AllPinNodesCount;
623 ULONG Index, SubIndex, PinId, CurNode, bConnected;
624 MIXER_STATUS Status;
625 LPMIXERLINE_EXT DstLine, SrcLine;
626
627 /* get destination line */
628 DstLine = MMixerGetSourceMixerLineByLineId(MixerInfo, DestinationLineID);
629 ASSERT(DstLine);
630
631 /* allocate an array to store all nodes which are upstream of the line terminator */
632 Status = MMixerAllocateTopologyNodeArray(MixerContext, Topology, &AllNodes);
633
634 /* check for success */
635 if (Status != MM_STATUS_SUCCESS)
636 {
637 /* out of memory */
638 return MM_STATUS_NO_MEMORY;
639 }
640
641 /* allocate an array to store all nodes which are downstream of a particular pin */
642 Status = MMixerAllocateTopologyNodeArray(MixerContext, Topology, &AllPinNodes);
643
644 /* allocate an array to store all pins which are upstream of this pin */
645 Status = MMixerAllocateTopologyPinArray(MixerContext, Topology, &AllPins);
646
647 /* check for success */
648 if (Status != MM_STATUS_SUCCESS)
649 {
650 /* out of memory */
651 MixerContext->Free(AllNodes);
652 return MM_STATUS_NO_MEMORY;
653 }
654
655 /* get all nodes which indirectly / directly connect to this node */
656 AllNodesCount = 0;
657 MMixerGetAllUpOrDownstreamNodesFromNodeIndex(MixerContext, Topology, LineTerminator, TRUE, &AllNodesCount, AllNodes);
658
659 /* get all pins which indirectly / directly connect to this node */
660 AllPinsCount = 0;
661 MMixerGetAllUpOrDownstreamPinsFromNodeIndex(MixerContext, Topology, LineTerminator, TRUE, &AllPinsCount, AllPins);
662
663 DPRINT1("LineTerminator %lu\n", LineTerminator);
664 DPRINT1("PinCount %lu\n", AllPinsCount);
665 DPRINT1("AllNodesCount %lu\n", AllNodesCount);
666
667 /* now construct the source lines which are attached to the destination line */
668 Index = AllPinsCount;
669
670 do
671 {
672 /* get current pin id */
673 PinId = AllPins[Index - 1];
674
675 /* reset nodes count */
676 AllPinNodesCount = 0;
677
678 /* now scan all nodes and add them to AllPinNodes array when they are connected to this pin */
679 for(SubIndex = 0; SubIndex < AllNodesCount; SubIndex++)
680 {
681 /* get current node index */
682 CurNode = AllNodes[SubIndex];
683
684 if (CurNode != MAXULONG && CurNode != LineTerminator)
685 {
686 /* check if that node is connected in some way to the current pin */
687 Status = MMixerIsNodeConnectedToPin(MixerContext, Topology, CurNode, PinId, TRUE, &bConnected);
688
689 if (Status != MM_STATUS_SUCCESS)
690 break;
691
692 if (bConnected)
693 {
694 /* it is connected */
695 AllPinNodes[AllPinNodesCount] = CurNode;
696 AllPinNodesCount++;
697
698 /* clear current index */
699 AllNodes[SubIndex] = MAXULONG;
700 }
701 }
702 }
703
704 /* decrement pin index */
705 Index--;
706
707 if (AllPinNodesCount)
708 {
709 #ifdef MMIXER_DEBUG
710 ULONG TempIndex;
711 #endif
712 /* now build the mixer source line */
713 Status = MMixerBuildMixerSourceLine(MixerContext, MixerInfo, Topology, PinId, AllPinNodesCount, AllPinNodes, DestinationLineID, &SrcLine);
714
715 if (Status == MM_STATUS_SUCCESS)
716 {
717 /* insert into line list */
718 InsertTailList(&MixerInfo->LineList, &SrcLine->Entry);
719
720 /* increment destination line count */
721 DstLine->Line.cConnections++;
722 #ifdef MMIXER_DEBUG
723 DPRINT1("Adding PinId %lu AllPinNodesCount %lu to DestinationLine %lu\n", PinId, AllPinNodesCount, DestinationLineID);
724 for(TempIndex = 0; TempIndex < AllPinNodesCount; TempIndex++)
725 DPRINT1("NodeIndex %lu\n", AllPinNodes[TempIndex]);
726 #endif
727 }
728 }
729 else
730 {
731 #ifdef MMIXER_DEBUG
732 DPRINT1("Discarding DestinationLineID %lu PinId %lu NO NODES!\n", DestinationLineID, PinId);
733 #endif
734 }
735
736 }while(Index != 0);
737
738 return MM_STATUS_SUCCESS;
739 }
740
741
742 MIXER_STATUS
743 MMixerAddMixerControlsToDestinationLine(
744 IN PMIXER_CONTEXT MixerContext,
745 IN OUT LPMIXER_INFO MixerInfo,
746 IN PTOPOLOGY Topology,
747 IN ULONG PinId,
748 IN ULONG bInput,
749 IN ULONG DestinationLineId,
750 OUT PULONG OutLineTerminator)
751 {
752 PULONG Nodes;
753 ULONG NodesCount, LineTerminator;
754 MIXER_STATUS Status;
755 LPMIXERLINE_EXT DstLine;
756
757 /* allocate nodes index array */
758 Status = MMixerAllocateTopologyNodeArray(MixerContext, Topology, &Nodes);
759
760 /* check for success */
761 if (Status != MM_STATUS_SUCCESS)
762 {
763 /* out of memory */
764 return MM_STATUS_NO_MEMORY;
765 }
766
767 /* get all destination line controls */
768 Status = MMixerCountMixerControls(MixerContext, Topology, PinId, bInput, TRUE, &NodesCount, Nodes, &LineTerminator);
769
770 /* check for success */
771 if (Status != MM_STATUS_SUCCESS)
772 {
773 /* failed to count controls */
774 MixerContext->Free(Nodes);
775 return Status;
776 }
777
778 /* get destination mixer line */
779 DstLine = MMixerGetSourceMixerLineByLineId(MixerInfo, DestinationLineId);
780
781 /* sanity check */
782 ASSERT(DstLine);
783
784 if (NodesCount > 0)
785 {
786 /* add all nodes as mixer controls to the destination line */
787 Status = MMixerAddMixerControlsToMixerLineByNodeIndexArray(MixerContext, MixerInfo, Topology, DstLine, NodesCount, Nodes);
788 if (Status != MM_STATUS_SUCCESS)
789 {
790 /* failed to add controls */
791 MixerContext->Free(Nodes);
792 return Status;
793 }
794 }
795
796 /* store result */
797 *OutLineTerminator = LineTerminator;
798
799 /* return result */
800 return Status;
801 }
802
803 VOID
804 MMixerApplyOutputFilterHack(
805 IN PMIXER_CONTEXT MixerContext,
806 IN LPMIXER_DATA MixerData,
807 IN OUT PULONG PinsCount,
808 IN OUT PULONG Pins)
809 {
810 ULONG Count = 0, Index;
811 MIXER_STATUS Status;
812 PKSPIN_PHYSICALCONNECTION Connection;
813
814 for(Index = 0; Index < *PinsCount; Index++)
815 {
816 /* check if it has a physical connection */
817 Status = MMixerGetPhysicalConnection(MixerContext, MixerData->hDevice, Pins[Index], &Connection);
818
819 if (Status == MM_STATUS_SUCCESS)
820 {
821 /* remove pin */
822 MixerContext->Copy(&Pins[Index], &Pins[Index + 1], (*PinsCount - (Index + 1)) * sizeof(ULONG));
823
824 /* free physical connection */
825 MixerContext->Free(Connection);
826
827 /* decrement index */
828 Index--;
829
830 /* decrement pin count */
831 (*PinsCount)--;
832 }
833 else
834 {
835 /* simple pin */
836 Count++;
837 }
838 }
839
840 /* store result */
841 *PinsCount = Count;
842 }
843
844 MIXER_STATUS
845 MMixerHandlePhysicalConnection(
846 IN PMIXER_CONTEXT MixerContext,
847 IN PMIXER_LIST MixerList,
848 IN LPMIXER_DATA MixerData,
849 IN OUT LPMIXER_INFO MixerInfo,
850 IN ULONG bInput,
851 IN PKSPIN_PHYSICALCONNECTION OutConnection)
852 {
853 MIXER_STATUS Status;
854 ULONG PinsCount, LineTerminator, DestinationLineID;
855 PULONG Pins;
856 PTOPOLOGY Topology;
857
858 /* first try to open the connected filter */
859 OutConnection->SymbolicLinkName[1] = L'\\';
860 MixerData = MMixerGetDataByDeviceName(MixerList, OutConnection->SymbolicLinkName);
861
862 /* check if the linked connection is found */
863 if (!MixerData)
864 {
865 /* filter references invalid physical connection */
866 return MM_STATUS_UNSUCCESSFUL;
867 }
868
869 DPRINT1("Name %S, Pin %lu bInput %lu\n", OutConnection->SymbolicLinkName, OutConnection->Pin, bInput);
870
871 if (MixerInfo->hMixer != NULL)
872 {
873 /* dont replace mixer destination handles */
874 DPRINT1("MixerInfo hDevice %p MixerData hDevice %p\n", MixerInfo->hMixer, MixerData->hDevice);
875 ASSERT(MixerInfo->hMixer == MixerData->hDevice);
876 }
877
878 /* store connected mixer handle */
879 MixerInfo->hMixer = MixerData->hDevice;
880
881 if (MixerData->Topology == NULL)
882 {
883 /* construct new topology */
884 Status = MMixerBuildTopology(MixerContext, MixerData, &Topology);
885 if (Status != MM_STATUS_SUCCESS)
886 {
887 /* failed to create topology */
888 return Status;
889 }
890
891 /* store topology */
892 MixerData->Topology = Topology;
893 }
894 else
895 {
896 /* re-use existing topology */
897 Topology = MixerData->Topology;
898 }
899
900 /* allocate pin index array which will hold all referenced pins */
901 Status = MMixerAllocateTopologyPinArray(MixerContext, Topology, &Pins);
902 ASSERT(Status == MM_STATUS_SUCCESS);
903
904 if (!bInput)
905 {
906 /* the mixer is an output mixer
907 * find end pin of the node path
908 */
909 PinsCount = 0;
910 Status = MMixerGetAllUpOrDownstreamPinsFromPinIndex(MixerContext, Topology, OutConnection->Pin, FALSE, &PinsCount, Pins);
911
912 /* check for success */
913 if (Status != MM_STATUS_SUCCESS)
914 {
915 /* failed to get end pin */
916 MixerContext->Free(Pins);
917 //MMixerFreeTopology(Topology);
918
919 /* return error code */
920 return Status;
921 }
922 /* HACK:
923 * some topologies do not have strict boundaries
924 * WorkArround: remove all pin ids which have a physical connection
925 * because bridge pins may belong to different render paths
926 */
927 MMixerApplyOutputFilterHack(MixerContext, MixerData, &PinsCount, Pins);
928
929 /* sanity checks */
930 ASSERT(PinsCount != 0);
931 ASSERT(PinsCount == 1);
932
933 /* create destination line */
934 Status = MMixerBuildMixerDestinationLine(MixerContext, MixerInfo, Pins[0], bInput);
935
936 /* calculate destination line id */
937 DestinationLineID = (DESTINATION_LINE + MixerInfo->MixCaps.cDestinations-1);
938
939 if (Status != MM_STATUS_SUCCESS)
940 {
941 /* failed to build destination line */
942 MixerContext->Free(Pins);
943
944 /* return error code */
945 return Status;
946 }
947
948 /* add mixer controls to destination line */
949 Status = MMixerAddMixerControlsToDestinationLine(MixerContext, MixerInfo, Topology, Pins[0], bInput, DestinationLineID, &LineTerminator);
950
951 if (Status == MM_STATUS_SUCCESS)
952 {
953 /* now add the rest of the source lines */
954 Status = MMixerAddMixerSourceLines(MixerContext, MixerInfo, Topology, DestinationLineID, LineTerminator);
955 }
956 }
957 else
958 {
959 /* calculate destination line id */
960 DestinationLineID = (DESTINATION_LINE + MixerInfo->MixCaps.cDestinations-1);
961
962 /* add mixer controls */
963 Status = MMixerAddMixerControlsToDestinationLine(MixerContext, MixerInfo, Topology, OutConnection->Pin, bInput, DestinationLineID, &LineTerminator);
964
965 if (Status == MM_STATUS_SUCCESS)
966 {
967 /* now add the rest of the source lines */
968 Status = MMixerAddMixerSourceLines(MixerContext, MixerInfo, Topology, DestinationLineID, LineTerminator);
969 }
970 }
971
972 return Status;
973 }
974
975 MIXER_STATUS
976 MMixerInitializeFilter(
977 IN PMIXER_CONTEXT MixerContext,
978 IN PMIXER_LIST MixerList,
979 IN LPMIXER_DATA MixerData,
980 IN LPMIXER_INFO MixerInfo,
981 IN PTOPOLOGY Topology,
982 IN ULONG NodeIndex,
983 IN ULONG bInputMixer,
984 IN OUT LPMIXER_INFO * OutMixerInfo)
985 {
986
987 MIXER_STATUS Status;
988 PKSPIN_PHYSICALCONNECTION OutConnection;
989 ULONG * Pins;
990 ULONG PinsFound;
991 ULONG NewMixerInfo = FALSE;
992
993 if (MixerInfo == NULL)
994 {
995 /* allocate a mixer info struct */
996 MixerInfo = (LPMIXER_INFO) MixerContext->Alloc(sizeof(MIXER_INFO));
997 if (!MixerInfo)
998 {
999 /* no memory */
1000 return MM_STATUS_NO_MEMORY;
1001 }
1002
1003 /* new mixer info */
1004 NewMixerInfo = TRUE;
1005
1006 /* intialize mixer caps */
1007 MixerInfo->MixCaps.wMid = MM_MICROSOFT; /* FIXME */
1008 MixerInfo->MixCaps.wPid = MM_PID_UNMAPPED; /* FIXME */
1009 MixerInfo->MixCaps.vDriverVersion = 1; /* FIXME */
1010 MixerInfo->MixCaps.fdwSupport = 0;
1011 MixerInfo->MixCaps.cDestinations = 0;
1012
1013 /* get mixer name */
1014 MMixerGetDeviceName(MixerContext, MixerInfo->MixCaps.szPname, MixerData->hDeviceInterfaceKey);
1015
1016 /* initialize line list */
1017 InitializeListHead(&MixerInfo->LineList);
1018 InitializeListHead(&MixerInfo->EventList);
1019 }
1020
1021 /* store mixer info */
1022 *OutMixerInfo = MixerInfo;
1023
1024 /* now allocate an array which will receive the indices of the pin
1025 * which has a ADC / DAC nodetype in its path
1026 */
1027 Status = MMixerAllocateTopologyPinArray(MixerContext, Topology, &Pins);
1028 ASSERT(Status == MM_STATUS_SUCCESS);
1029
1030 PinsFound = 0;
1031
1032 /* now get all sink / source pins, which are attached to the ADC / DAC node
1033 * For sink pins (wave out) search up stream
1034 * For source pins (wave in) search down stream
1035 * The search direction is always the opposite of the current mixer type
1036 */
1037 PinsFound = 0;
1038 MMixerGetAllUpOrDownstreamPinsFromNodeIndex(MixerContext, Topology, NodeIndex, !bInputMixer, &PinsFound, Pins);
1039
1040 /* if there is no pin found, we have a broken topology */
1041 ASSERT(PinsFound != 0);
1042
1043 /* now create a wave info struct */
1044 Status = MMixerInitializeWaveInfo(MixerContext, MixerList, MixerData, MixerInfo->MixCaps.szPname, bInputMixer, PinsFound, Pins);
1045 if (Status != MM_STATUS_SUCCESS)
1046 {
1047 /* failed to create wave info struct */
1048 MixerContext->Free(MixerInfo);
1049 MixerContext->Free(Pins);
1050 return Status;
1051 }
1052
1053 if (bInputMixer)
1054 {
1055 /* pre create the mixer destination line for input mixers */
1056 Status = MMixerBuildMixerDestinationLine(MixerContext, MixerInfo, Pins[0], bInputMixer);
1057
1058 if (Status != MM_STATUS_SUCCESS)
1059 {
1060 /* failed to create mixer destination line */
1061 return Status;
1062 }
1063 }
1064
1065
1066 /* now get the bridge pin which is at the end of node path
1067 * For sink pins (wave out) search down stream
1068 * For source pins (wave in) search up stream
1069 */
1070 MixerContext->Free(Pins);
1071 Status = MMixerAllocateTopologyPinArray(MixerContext, Topology, &Pins);
1072 ASSERT(Status == MM_STATUS_SUCCESS);
1073
1074 PinsFound = 0;
1075 MMixerGetAllUpOrDownstreamPinsFromNodeIndex(MixerContext, Topology, NodeIndex, bInputMixer, &PinsFound, Pins);
1076
1077 /* if there is no pin found, we have a broken topology */
1078 ASSERT(PinsFound != 0);
1079
1080 /* there should be exactly one bridge pin */
1081 ASSERT(PinsFound == 1);
1082
1083 DPRINT("BridgePin %lu bInputMixer %lu\n", Pins[0], bInputMixer);
1084
1085 /* does the pin have a physical connection */
1086 Status = MMixerGetPhysicalConnection(MixerContext, MixerData->hDevice, Pins[0], &OutConnection);
1087
1088 if (Status == MM_STATUS_SUCCESS)
1089 {
1090 /* topology on the topoloy filter */
1091 Status = MMixerHandlePhysicalConnection(MixerContext, MixerList, MixerData, MixerInfo, bInputMixer, OutConnection);
1092
1093 /* free physical connection data */
1094 MixerContext->Free(OutConnection);
1095 }
1096 else
1097 {
1098 /* FIXME
1099 * handle drivers which expose their topology on the same filter
1100 */
1101 ASSERT(0);
1102 MixerInfo->hMixer = MixerData->hDevice;
1103 }
1104
1105 /* free pins */
1106 MixerContext->Free(Pins);
1107
1108 if (NewMixerInfo)
1109 {
1110 /* insert mixer */
1111 InsertHeadList(&MixerList->MixerList, &MixerInfo->Entry);
1112 /* increment mixer count */
1113 MixerList->MixerListCount++;
1114 }
1115
1116 /* done */
1117 return Status;
1118 }
1119
1120 MIXER_STATUS
1121 MMixerSetupFilter(
1122 IN PMIXER_CONTEXT MixerContext,
1123 IN PMIXER_LIST MixerList,
1124 IN LPMIXER_DATA MixerData,
1125 IN PULONG DeviceCount)
1126 {
1127 MIXER_STATUS Status;
1128 PTOPOLOGY Topology;
1129 ULONG NodeIndex;
1130 LPMIXER_INFO MixerInfo = NULL;
1131
1132 /* check if topology has already been built */
1133 if (MixerData->Topology == NULL)
1134 {
1135 /* build topology */
1136 Status = MMixerBuildTopology(MixerContext, MixerData, &Topology);
1137
1138 if (Status != MM_STATUS_SUCCESS)
1139 {
1140 /* failed to build topology */
1141 return Status;
1142 }
1143
1144 /* store topology */
1145 MixerData->Topology = Topology;
1146 }
1147 else
1148 {
1149 /* re-use topology */
1150 Topology = MixerData->Topology;
1151 }
1152
1153 /* check if the filter has an wave out node */
1154 NodeIndex = MMixerGetNodeIndexFromGuid(Topology, &KSNODETYPE_DAC);
1155 if (NodeIndex != MAXULONG)
1156 {
1157 /* it has */
1158 Status = MMixerInitializeFilter(MixerContext, MixerList, MixerData, NULL, Topology, NodeIndex, FALSE, &MixerInfo);
1159
1160 /* check for success */
1161 if (Status == MM_STATUS_SUCCESS)
1162 {
1163 /* increment mixer count */
1164 (*DeviceCount)++;
1165 }
1166 else
1167 {
1168 /* reset mixer info in case of error */
1169 MixerInfo = NULL;
1170 }
1171 }
1172
1173 /* check if the filter has an wave in node */
1174 NodeIndex = MMixerGetNodeIndexFromGuid(Topology, &KSNODETYPE_ADC);
1175 if (NodeIndex != MAXULONG)
1176 {
1177 /* it has */
1178 Status = MMixerInitializeFilter(MixerContext, MixerList, MixerData, MixerInfo, Topology, NodeIndex, TRUE, &MixerInfo);
1179
1180 /* check for success */
1181 if (Status == MM_STATUS_SUCCESS)
1182 {
1183 /* increment mixer count */
1184 (*DeviceCount)++;
1185 }
1186
1187 }
1188
1189 /* TODO: handle alternative mixer types + apply hacks for Wave source line */
1190
1191 /* activate midi devices */
1192 MMixerInitializeMidiForFilter(MixerContext, MixerList, MixerData, Topology);
1193
1194 /* done */
1195 return Status;
1196 }
1197
1198
1199 MIXER_STATUS
1200 MMixerAddEvent(
1201 IN PMIXER_CONTEXT MixerContext,
1202 IN OUT LPMIXER_INFO MixerInfo,
1203 IN PVOID MixerEventContext,
1204 IN PMIXER_EVENT MixerEventRoutine)
1205 {
1206 //KSE_NODE Property;
1207 PEVENT_NOTIFICATION_ENTRY EventData;
1208 //ULONG BytesReturned;
1209 //MIXER_STATUS Status;
1210
1211 EventData = (PEVENT_NOTIFICATION_ENTRY)MixerContext->AllocEventData(sizeof(EVENT_NOTIFICATION_ENTRY));
1212 if (!EventData)
1213 {
1214 /* not enough memory */
1215 return MM_STATUS_NO_MEMORY;
1216 }
1217
1218 #if 0
1219 /* setup request */
1220 Property.Event.Set = KSEVENTSETID_AudioControlChange;
1221 Property.Event.Flags = KSEVENT_TYPE_TOPOLOGY|KSEVENT_TYPE_ENABLE;
1222 Property.Event.Id = KSEVENT_CONTROL_CHANGE;
1223
1224 Property.NodeId = NodeId;
1225 Property.Reserved = 0;
1226
1227 Status = MixerContext->Control(MixerInfo->hMixer, IOCTL_KS_ENABLE_EVENT, (PVOID)&Property, sizeof(KSP_NODE), (PVOID)EventData, sizeof(KSEVENTDATA), &BytesReturned);
1228 if (Status != MM_STATUS_SUCCESS)
1229 {
1230 /* failed to add event */
1231 MixerContext->FreeEventData(EventData);
1232 return Status;
1233 }
1234 #endif
1235
1236 /* initialize notification entry */
1237 EventData->MixerEventContext = MixerEventContext;
1238 EventData->MixerEventRoutine;
1239
1240 /* store event */
1241 InsertTailList(&MixerInfo->EventList, &EventData->Entry);
1242 return MM_STATUS_SUCCESS;
1243 }
1244