[MMIXER] Cleanup mixer notifications opened by an application when it is closed.
[reactos.git] / reactos / sdk / 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 "precomp.h"
10
11 #define YDEBUG
12 #include <debug.h>
13
14 const GUID KSNODETYPE_DESKTOP_MICROPHONE = {0xDFF21BE2, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
15 const GUID KSNODETYPE_LEGACY_AUDIO_CONNECTOR = {0xDFF21FE4, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
16 const GUID KSNODETYPE_TELEPHONE = {0xDFF21EE2, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
17 const GUID KSNODETYPE_PHONE_LINE = {0xDFF21EE1, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
18 const GUID KSNODETYPE_DOWN_LINE_PHONE = {0xDFF21EE3, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
19 const GUID KSNODETYPE_DESKTOP_SPEAKER = {0xDFF21CE4, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
20 const GUID KSNODETYPE_ROOM_SPEAKER = {0xDFF21CE5, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
21 const GUID KSNODETYPE_COMMUNICATION_SPEAKER = {0xDFF21CE6, 0xF70F, 0x11D0, {0xB9,0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
22 const GUID KSNODETYPE_HEADPHONES = {0xDFF21CE2, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
23 const GUID KSNODETYPE_HEAD_MOUNTED_DISPLAY_AUDIO = {0xDFF21CE3, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
24 const GUID KSNODETYPE_MICROPHONE = {0xDFF21BE1, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9,0x22, 0x31, 0x96}};
25 const GUID KSCATEGORY_AUDIO = {0x6994AD04L, 0x93EF, 0x11D0, {0xA3, 0xCC, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
26 const GUID KSNODETYPE_SPDIF_INTERFACE = {0xDFF21FE5, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
27 const GUID KSNODETYPE_ANALOG_CONNECTOR = {0xDFF21FE1, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
28 const GUID KSNODETYPE_SPEAKER = {0xDFF21CE1, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
29 const GUID KSNODETYPE_CD_PLAYER = {0xDFF220E3, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
30 const GUID KSNODETYPE_SYNTHESIZER = {0xDFF220F3, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
31 const GUID KSNODETYPE_LINE_CONNECTOR = {0xDFF21FE3, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0,0xC9, 0x22, 0x31, 0x96}};
32 const GUID PINNAME_VIDEO_CAPTURE = {0xfb6c4281, 0x353, 0x11d1, {0x90, 0x5f, 0x0, 0x0, 0xc0, 0xcc, 0x16, 0xba}};
33
34 MIXER_STATUS
35 MMixerAddMixerControl(
36 IN PMIXER_CONTEXT MixerContext,
37 IN LPMIXER_INFO MixerInfo,
38 IN HANDLE hMixer,
39 IN PTOPOLOGY Topology,
40 IN ULONG NodeIndex,
41 IN LPMIXERLINE_EXT MixerLine,
42 IN ULONG MaxChannels)
43 {
44 LPGUID NodeType;
45 KSP_NODE Node;
46 ULONG BytesReturned;
47 MIXER_STATUS Status;
48 LPWSTR Name;
49 LPMIXERCONTROL_EXT MixerControl;
50
51 /* allocate mixer control */
52 MixerControl = MixerContext->Alloc(sizeof(MIXERCONTROL_EXT));
53 if (!MixerControl)
54 {
55 /* no memory */
56 return MM_STATUS_NO_MEMORY;
57 }
58
59
60 /* initialize mixer control */
61 MixerControl->hDevice = hMixer;
62 MixerControl->NodeID = NodeIndex;
63 MixerControl->ExtraData = NULL;
64
65 MixerControl->Control.cbStruct = sizeof(MIXERCONTROLW);
66 MixerControl->Control.dwControlID = MixerInfo->ControlId;
67
68 /* get node type */
69 NodeType = MMixerGetNodeTypeFromTopology(Topology, NodeIndex);
70 /* store control type */
71 MixerControl->Control.dwControlType = MMixerGetControlTypeFromTopologyNode(NodeType);
72
73 MixerControl->Control.fdwControl = (MaxChannels > 1 ? 0 : MIXERCONTROL_CONTROLF_UNIFORM);
74 MixerControl->Control.cMultipleItems = 0;
75
76 /* setup request to retrieve name */
77 Node.NodeId = NodeIndex;
78 Node.Property.Id = KSPROPERTY_TOPOLOGY_NAME;
79 Node.Property.Flags = KSPROPERTY_TYPE_GET;
80 Node.Property.Set = KSPROPSETID_Topology;
81 Node.Reserved = 0;
82
83 /* get node name size */
84 Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Node, sizeof(KSP_NODE), NULL, 0, &BytesReturned);
85
86 if (Status == MM_STATUS_MORE_ENTRIES)
87 {
88 ASSERT(BytesReturned != 0);
89 Name = (LPWSTR)MixerContext->Alloc(BytesReturned);
90 if (!Name)
91 {
92 /* not enough memory */
93 return MM_STATUS_NO_MEMORY;
94 }
95
96 /* get node name */
97 Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Node, sizeof(KSP_NODE), (LPVOID)Name, BytesReturned, &BytesReturned);
98
99 if (Status == MM_STATUS_SUCCESS)
100 {
101 MixerContext->Copy(MixerControl->Control.szShortName, Name, (min(MIXER_SHORT_NAME_CHARS, wcslen(Name)+1)) * sizeof(WCHAR));
102 MixerControl->Control.szShortName[MIXER_SHORT_NAME_CHARS-1] = L'\0';
103
104 MixerContext->Copy(MixerControl->Control.szName, Name, (min(MIXER_LONG_NAME_CHARS, wcslen(Name)+1)) * sizeof(WCHAR));
105 MixerControl->Control.szName[MIXER_LONG_NAME_CHARS-1] = L'\0';
106 }
107
108 /* free name buffer */
109 MixerContext->Free(Name);
110 }
111
112 /* increment control count */
113 MixerInfo->ControlId++;
114
115 /* insert control */
116 InsertTailList(&MixerLine->ControlsList, &MixerControl->Entry);
117
118 if (MixerControl->Control.dwControlType == MIXERCONTROL_CONTROLTYPE_MUX)
119 {
120 ULONG NodesCount;
121 PULONG Nodes;
122
123 /* allocate topology nodes array */
124 Status = MMixerAllocateTopologyNodeArray(MixerContext, Topology, &Nodes);
125
126 if (Status != MM_STATUS_SUCCESS)
127 {
128 /* out of memory */
129 return STATUS_NO_MEMORY;
130 }
131
132 /* get connected node count */
133 MMixerGetNextNodesFromNodeIndex(MixerContext, Topology, NodeIndex, TRUE, &NodesCount, Nodes);
134
135 /* TODO */
136 MixerContext->Free(Nodes);
137
138 /* setup mux bounds */
139 MixerControl->Control.Bounds.dwMinimum = 0;
140 MixerControl->Control.Bounds.dwMaximum = NodesCount - 1;
141 MixerControl->Control.Metrics.dwReserved[0] = NodesCount;
142 MixerControl->Control.cMultipleItems = NodesCount;
143 MixerControl->Control.fdwControl |= MIXERCONTROL_CONTROLF_UNIFORM | MIXERCONTROL_CONTROLF_MULTIPLE;
144 }
145 else if (MixerControl->Control.dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE)
146 {
147 MixerControl->Control.Bounds.dwMinimum = 0;
148 MixerControl->Control.Bounds.dwMaximum = 1;
149 }
150 else if (MixerControl->Control.dwControlType == MIXERCONTROL_CONTROLTYPE_ONOFF)
151 {
152 /* only needs to set bounds */
153 MixerControl->Control.Bounds.dwMinimum = 0;
154 MixerControl->Control.Bounds.dwMaximum = 1;
155 }
156 else if (MixerControl->Control.dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)
157 {
158 KSNODEPROPERTY_AUDIO_CHANNEL Property;
159 ULONG Length;
160 PKSPROPERTY_DESCRIPTION Desc;
161 PKSPROPERTY_MEMBERSHEADER Members;
162 PKSPROPERTY_STEPPING_LONG Range;
163
164 MixerControl->Control.Bounds.dwMinimum = 0;
165 MixerControl->Control.Bounds.dwMaximum = 0xFFFF;
166 MixerControl->Control.Metrics.cSteps = 0xC0; /* FIXME */
167
168 Length = sizeof(KSPROPERTY_DESCRIPTION) + sizeof(KSPROPERTY_MEMBERSHEADER) + sizeof(KSPROPERTY_STEPPING_LONG);
169 Desc = (PKSPROPERTY_DESCRIPTION)MixerContext->Alloc(Length);
170 ASSERT(Desc);
171
172 /* setup the request */
173 RtlZeroMemory(&Property, sizeof(KSNODEPROPERTY_AUDIO_CHANNEL));
174
175 Property.NodeProperty.NodeId = NodeIndex;
176 Property.NodeProperty.Property.Id = KSPROPERTY_AUDIO_VOLUMELEVEL;
177 Property.NodeProperty.Property.Flags = KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_TOPOLOGY;
178 Property.NodeProperty.Property.Set = KSPROPSETID_Audio;
179
180 /* get node volume level info */
181 Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Property, sizeof(KSNODEPROPERTY_AUDIO_CHANNEL), Desc, Length, &BytesReturned);
182
183 if (Status == MM_STATUS_SUCCESS)
184 {
185 LPMIXERVOLUME_DATA VolumeData;
186 ULONG Steps, MaxRange, Index;
187 LONG Value;
188
189 Members = (PKSPROPERTY_MEMBERSHEADER)(Desc + 1);
190 Range = (PKSPROPERTY_STEPPING_LONG)(Members + 1);
191
192 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);
193
194 MaxRange = Range->Bounds.UnsignedMaximum - Range->Bounds.UnsignedMinimum;
195
196 if (MaxRange)
197 {
198 ASSERT(MaxRange);
199 VolumeData = (LPMIXERVOLUME_DATA)MixerContext->Alloc(sizeof(MIXERVOLUME_DATA));
200 if (!VolumeData)
201 return MM_STATUS_NO_MEMORY;
202
203 Steps = MaxRange / Range->SteppingDelta + 1;
204
205 /* store mixer control info there */
206 VolumeData->Header.dwControlID = MixerControl->Control.dwControlID;
207 VolumeData->SignedMaximum = Range->Bounds.SignedMaximum;
208 VolumeData->SignedMinimum = Range->Bounds.SignedMinimum;
209 VolumeData->SteppingDelta = Range->SteppingDelta;
210 VolumeData->ValuesCount = Steps;
211 VolumeData->InputSteppingDelta = 0x10000 / Steps;
212
213 VolumeData->Values = (PLONG)MixerContext->Alloc(sizeof(LONG) * Steps);
214 if (!VolumeData->Values)
215 {
216 MixerContext->Free(Desc);
217 MixerContext->Free(VolumeData);
218 return MM_STATUS_NO_MEMORY;
219 }
220
221 Value = Range->Bounds.SignedMinimum;
222 for(Index = 0; Index < Steps; Index++)
223 {
224 VolumeData->Values[Index] = Value;
225 Value += Range->SteppingDelta;
226 }
227 MixerControl->ExtraData = VolumeData;
228 }
229 }
230 MixerContext->Free(Desc);
231 }
232
233 DPRINT("Status %x Name %S\n", Status, MixerControl->Control.szName);
234 return MM_STATUS_SUCCESS;
235 }
236
237 MIXER_STATUS
238 MMixerCreateDestinationLine(
239 IN PMIXER_CONTEXT MixerContext,
240 IN LPMIXER_INFO MixerInfo,
241 IN ULONG bInputMixer,
242 IN LPWSTR LineName)
243 {
244 LPMIXERLINE_EXT DestinationLine;
245
246 /* allocate a mixer destination line */
247 DestinationLine = (LPMIXERLINE_EXT) MixerContext->Alloc(sizeof(MIXERLINE_EXT));
248 if (!MixerInfo)
249 {
250 /* no memory */
251 return MM_STATUS_NO_MEMORY;
252 }
253
254 /* initialize mixer destination line */
255 DestinationLine->Line.cbStruct = sizeof(MIXERLINEW);
256 DestinationLine->Line.cChannels = 2; /* FIXME */
257 DestinationLine->Line.cConnections = 0;
258 DestinationLine->Line.cControls = 0;
259 DestinationLine->Line.dwComponentType = (bInputMixer == 0 ? MIXERLINE_COMPONENTTYPE_DST_SPEAKERS : MIXERLINE_COMPONENTTYPE_DST_WAVEIN);
260 DestinationLine->Line.dwDestination = MixerInfo->MixCaps.cDestinations;
261 DestinationLine->Line.dwLineID = MixerInfo->MixCaps.cDestinations + DESTINATION_LINE;
262 DestinationLine->Line.dwSource = MAXULONG;
263 DestinationLine->Line.dwUser = 0;
264 DestinationLine->Line.fdwLine = MIXERLINE_LINEF_ACTIVE;
265
266
267 if (LineName)
268 {
269 MixerContext->Copy(DestinationLine->Line.szShortName, LineName, (min(MIXER_SHORT_NAME_CHARS, wcslen(LineName)+1)) * sizeof(WCHAR));
270 DestinationLine->Line.szShortName[MIXER_SHORT_NAME_CHARS-1] = L'\0';
271
272 MixerContext->Copy(DestinationLine->Line.szName, LineName, (min(MIXER_LONG_NAME_CHARS, wcslen(LineName)+1)) * sizeof(WCHAR));
273 DestinationLine->Line.szName[MIXER_LONG_NAME_CHARS-1] = L'\0';
274
275 }
276
277 DestinationLine->Line.Target.dwType = (bInputMixer == 0 ? MIXERLINE_TARGETTYPE_WAVEOUT : MIXERLINE_TARGETTYPE_WAVEIN);
278 DestinationLine->Line.Target.dwDeviceID = 0; //FIXME
279 DestinationLine->Line.Target.wMid = MixerInfo->MixCaps.wMid;
280 DestinationLine->Line.Target.wPid = MixerInfo->MixCaps.wPid;
281 DestinationLine->Line.Target.vDriverVersion = MixerInfo->MixCaps.vDriverVersion;
282
283 ASSERT(MixerInfo->MixCaps.szPname[MAXPNAMELEN-1] == 0);
284 wcscpy(DestinationLine->Line.Target.szPname, MixerInfo->MixCaps.szPname);
285
286 /* initialize extra line */
287 InitializeListHead(&DestinationLine->ControlsList);
288
289 /* insert into mixer info */
290 InsertTailList(&MixerInfo->LineList, &DestinationLine->Entry);
291
292 /* increment destination count */
293 MixerInfo->MixCaps.cDestinations++;
294
295 /* done */
296 return MM_STATUS_SUCCESS;
297 }
298
299 MIXER_STATUS
300 MMixerGetPinName(
301 IN PMIXER_CONTEXT MixerContext,
302 IN LPMIXER_INFO MixerInfo,
303 IN HANDLE hMixer,
304 IN ULONG PinId,
305 IN OUT LPWSTR * OutBuffer)
306 {
307 KSP_PIN Pin;
308 ULONG BytesReturned;
309 LPWSTR Buffer;
310 MIXER_STATUS Status;
311
312 /* prepare pin */
313 Pin.PinId = PinId;
314 Pin.Reserved = 0;
315 Pin.Property.Flags = KSPROPERTY_TYPE_GET;
316 Pin.Property.Set = KSPROPSETID_Pin;
317 Pin.Property.Id = KSPROPERTY_PIN_NAME;
318
319 /* try get pin name size */
320 Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSP_PIN), NULL, 0, &BytesReturned);
321
322 /* check if buffer overflowed */
323 if (Status == MM_STATUS_MORE_ENTRIES)
324 {
325 /* allocate buffer */
326 Buffer = (LPWSTR)MixerContext->Alloc(BytesReturned);
327 if (!Buffer)
328 {
329 /* out of memory */
330 return MM_STATUS_NO_MEMORY;
331 }
332
333 /* try get pin name */
334 Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSP_PIN), (PVOID)Buffer, BytesReturned, &BytesReturned);
335 if (Status != MM_STATUS_SUCCESS)
336 {
337 /* failed to get pin name */
338 MixerContext->Free((PVOID)Buffer);
339 return Status;
340 }
341
342 /* successfully obtained pin name */
343 *OutBuffer = Buffer;
344 return MM_STATUS_SUCCESS;
345 }
346
347 /* failed to get pin name */
348 return Status;
349 }
350
351 MIXER_STATUS
352 MMixerBuildMixerDestinationLine(
353 IN PMIXER_CONTEXT MixerContext,
354 IN OUT LPMIXER_INFO MixerInfo,
355 IN HANDLE hMixer,
356 IN ULONG PinId,
357 IN ULONG bInput)
358 {
359 LPWSTR PinName;
360 MIXER_STATUS Status;
361
362 /* try get pin name */
363 Status = MMixerGetPinName(MixerContext, MixerInfo, hMixer, PinId, &PinName);
364 if (Status == MM_STATUS_SUCCESS)
365 {
366 /* create mixer destination line */
367
368 Status = MMixerCreateDestinationLine(MixerContext, MixerInfo, bInput, PinName);
369
370 /* free pin name */
371 MixerContext->Free(PinName);
372 }
373 else
374 {
375 /* create mixer destination line unlocalized */
376 Status = MMixerCreateDestinationLine(MixerContext, MixerInfo, bInput, L"No Name");
377 }
378
379 return Status;
380 }
381
382 MIXER_STATUS
383 MMixerBuildTopology(
384 IN PMIXER_CONTEXT MixerContext,
385 IN LPMIXER_DATA MixerData,
386 OUT PTOPOLOGY * OutTopology)
387 {
388 ULONG PinsCount;
389 PKSMULTIPLE_ITEM NodeTypes = NULL;
390 PKSMULTIPLE_ITEM NodeConnections = NULL;
391 MIXER_STATUS Status;
392
393 if (MixerData->Topology)
394 {
395 /* re-use existing topology */
396 *OutTopology = MixerData->Topology;
397
398 return MM_STATUS_SUCCESS;
399 }
400
401 /* get connected filter pin count */
402 PinsCount = MMixerGetFilterPinCount(MixerContext, MixerData->hDevice);
403
404 if (!PinsCount)
405 {
406 /* referenced filter does not have any pins */
407 return MM_STATUS_UNSUCCESSFUL;
408 }
409
410 /* get topology node types */
411 Status = MMixerGetFilterTopologyProperty(MixerContext, MixerData->hDevice, KSPROPERTY_TOPOLOGY_NODES, &NodeTypes);
412 if (Status != MM_STATUS_SUCCESS)
413 {
414 /* failed to get topology node types */
415 return Status;
416 }
417
418 /* get topology connections */
419 Status = MMixerGetFilterTopologyProperty(MixerContext, MixerData->hDevice, KSPROPERTY_TOPOLOGY_CONNECTIONS, &NodeConnections);
420 if (Status != MM_STATUS_SUCCESS)
421 {
422 /* failed to get topology connections */
423 MixerContext->Free(NodeTypes);
424 return Status;
425 }
426
427 /* create a topology */
428 Status = MMixerCreateTopology(MixerContext, PinsCount, NodeConnections, NodeTypes, OutTopology);
429
430 /* free node types & connections */
431 MixerContext->Free(NodeConnections);
432 MixerContext->Free(NodeTypes);
433
434 if (Status == MM_STATUS_SUCCESS)
435 {
436 /* store topology object */
437 MixerData->Topology = *OutTopology;
438 }
439
440 /* done */
441 return Status;
442 }
443
444 MIXER_STATUS
445 MMixerCountMixerControls(
446 IN PMIXER_CONTEXT MixerContext,
447 IN PTOPOLOGY Topology,
448 IN ULONG PinId,
449 IN ULONG bInputMixer,
450 IN ULONG bUpStream,
451 OUT PULONG OutNodesCount,
452 OUT PULONG OutNodes,
453 OUT PULONG OutLineTerminator)
454 {
455 PULONG Nodes;
456 ULONG NodesCount, NodeIndex, Count, bTerminator;
457 MIXER_STATUS Status;
458
459 /* allocate an array to store all nodes which are upstream of this pin */
460 Status = MMixerAllocateTopologyNodeArray(MixerContext, Topology, &Nodes);
461
462 if (Status != MM_STATUS_SUCCESS)
463 {
464 /* out of memory */
465 return STATUS_NO_MEMORY;
466 }
467
468 /* mark result array as zero */
469 *OutNodesCount = 0;
470
471 /* get next nodes */
472 MMixerGetNextNodesFromPinIndex(MixerContext, Topology, PinId, bUpStream, &NodesCount, Nodes);
473
474 if (NodesCount == 0)
475 {
476 /* a pin which is not connected from any node
477 * a) it is a topology bug (driver bug)
478 * b) the request is from an alternative mixer
479 alternative mixer code scans all pins which have not been used and tries to build lines
480 */
481 DPRINT1("MMixerCountMixerControls PinId %lu is not connected by any node\n", PinId);
482 MMixerPrintTopology(Topology);
483 return MM_STATUS_UNSUCCESSFUL;
484 }
485
486 /* assume no topology split before getting line terminator */
487 ASSERT(NodesCount == 1);
488
489 /* get first node */
490 NodeIndex = Nodes[0];
491 Count = 0;
492
493 do
494 {
495 /* check if the node is a terminator */
496 MMixerIsNodeTerminator(Topology, NodeIndex, &bTerminator);
497
498 if (bTerminator)
499 {
500 /* found terminator */
501 if (bInputMixer)
502 {
503 /* add mux source for source destination line */
504 OutNodes[Count] = NodeIndex;
505 Count++;
506 }
507 break;
508 }
509
510 /* store node id */
511 OutNodes[Count] = NodeIndex;
512
513 /* increment node count */
514 Count++;
515
516 /* get next nodes upstream */
517 MMixerGetNextNodesFromNodeIndex(MixerContext, Topology, NodeIndex, bUpStream, &NodesCount, Nodes);
518
519 if (NodesCount != 1)
520 {
521 DPRINT("PinId %lu bInputMixer %lu bUpStream %lu NodeIndex %lu is not connected", PinId, bInputMixer, bUpStream, NodeIndex);
522 break;
523 }
524
525 ASSERT(NodesCount == 1);
526
527 /* use first index */
528 NodeIndex = Nodes[0];
529
530 }while(TRUE);
531
532 /* free node index */
533 MixerContext->Free(Nodes);
534
535 /* store nodes count */
536 *OutNodesCount = Count;
537
538 /* store line terminator */
539 *OutLineTerminator = NodeIndex;
540
541 /* done */
542 return MM_STATUS_SUCCESS;
543 }
544
545 MIXER_STATUS
546 MMixerGetChannelCountEnhanced(
547 IN PMIXER_CONTEXT MixerContext,
548 IN LPMIXER_INFO MixerInfo,
549 IN HANDLE hMixer,
550 IN ULONG NodeId,
551 OUT PULONG MaxChannels)
552 {
553 KSPROPERTY_DESCRIPTION Description;
554 PKSPROPERTY_DESCRIPTION NewDescription;
555 PKSPROPERTY_MEMBERSHEADER Header;
556 ULONG BytesReturned;
557 KSP_NODE Request;
558 MIXER_STATUS Status;
559
560 /* try #1 obtain it via description */
561 Request.NodeId = NodeId;
562 Request.Reserved = 0;
563 Request.Property.Set = KSPROPSETID_Audio;
564 Request.Property.Flags = KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_TOPOLOGY;
565 Request.Property.Id = KSPROPERTY_AUDIO_VOLUMELEVEL;
566
567
568 /* get description */
569 Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Request, sizeof(KSP_NODE), (PVOID)&Description, sizeof(KSPROPERTY_DESCRIPTION), &BytesReturned);
570 if (Status == MM_STATUS_SUCCESS)
571 {
572 if (Description.DescriptionSize >= sizeof(KSPROPERTY_DESCRIPTION) + sizeof(KSPROPERTY_MEMBERSHEADER) && (Description.MembersListCount > 0))
573 {
574 /* allocate new description */
575 NewDescription = MixerContext->Alloc(Description.DescriptionSize);
576
577 if (!NewDescription)
578 {
579 /* not enough memory */
580 return MM_STATUS_NO_MEMORY;
581 }
582
583 /* get description */
584 Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Request, sizeof(KSP_NODE), (PVOID)NewDescription, Description.DescriptionSize, &BytesReturned);
585 if (Status == MM_STATUS_SUCCESS)
586 {
587 /* get header */
588 Header = (PKSPROPERTY_MEMBERSHEADER)(NewDescription + 1);
589
590 if (Header->Flags & KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_MULTICHANNEL)
591 {
592 /* found enhanced flag */
593 ASSERT(Header->MembersCount > 1);
594
595 /* store channel count */
596 *MaxChannels = Header->MembersCount;
597
598 /* free description */
599 MixerContext->Free(NewDescription);
600
601 /* done */
602 return MM_STATUS_SUCCESS;
603 }
604 }
605
606 /* free description */
607 MixerContext->Free(NewDescription);
608 }
609 }
610
611 /* failed to get channel count enhanced */
612 return MM_STATUS_UNSUCCESSFUL;
613 }
614
615 VOID
616 MMixerGetChannelCountLegacy(
617 IN PMIXER_CONTEXT MixerContext,
618 IN LPMIXER_INFO MixerInfo,
619 IN HANDLE hMixer,
620 IN ULONG NodeId,
621 OUT PULONG MaxChannels)
622 {
623 ULONG BytesReturned;
624 MIXER_STATUS Status;
625 KSNODEPROPERTY_AUDIO_CHANNEL Channel;
626 LONG Volume;
627
628 /* setup request */
629 Channel.Reserved = 0;
630 Channel.NodeProperty.NodeId = NodeId;
631 Channel.NodeProperty.Reserved = 0;
632 Channel.NodeProperty.Property.Flags = KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_TOPOLOGY;
633 Channel.NodeProperty.Property.Set = KSPROPSETID_Audio;
634 Channel.Channel = 0;
635 Channel.NodeProperty.Property.Id = KSPROPERTY_AUDIO_VOLUMELEVEL;
636
637 do
638 {
639 /* get channel volume */
640 Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Channel, sizeof(KSNODEPROPERTY_AUDIO_CHANNEL), (PVOID)&Volume, sizeof(LONG), &BytesReturned);
641 if (Status != MM_STATUS_SUCCESS)
642 break;
643
644 /* increment channel count */
645 Channel.Channel++;
646
647 }while(TRUE);
648
649 /* store channel count */
650 *MaxChannels = Channel.Channel;
651
652 }
653
654 VOID
655 MMixerGetMaxChannelsForNode(
656 IN PMIXER_CONTEXT MixerContext,
657 IN LPMIXER_INFO MixerInfo,
658 IN HANDLE hMixer,
659 IN ULONG NodeId,
660 OUT PULONG MaxChannels)
661 {
662 MIXER_STATUS Status;
663
664 /* try to get it enhanced */
665 Status = MMixerGetChannelCountEnhanced(MixerContext, MixerInfo, hMixer, NodeId, MaxChannels);
666
667 if (Status != MM_STATUS_SUCCESS)
668 {
669 /* get it old-fashioned way */
670 MMixerGetChannelCountLegacy(MixerContext, MixerInfo, hMixer, NodeId, MaxChannels);
671 }
672 }
673
674 MIXER_STATUS
675 MMixerAddMixerControlsToMixerLineByNodeIndexArray(
676 IN PMIXER_CONTEXT MixerContext,
677 IN LPMIXER_INFO MixerInfo,
678 IN HANDLE hMixer,
679 IN PTOPOLOGY Topology,
680 IN OUT LPMIXERLINE_EXT DstLine,
681 IN ULONG NodesCount,
682 IN PULONG Nodes)
683 {
684 ULONG Index, Count, bReserved;
685 MIXER_STATUS Status;
686 LPGUID NodeType;
687 ULONG MaxChannels;
688
689 /* initialize control count */
690 Count = 0;
691
692 for(Index = 0; Index < NodesCount; Index++)
693 {
694 /* check if the node has already been reserved to a line */
695 MMixerIsTopologyNodeReserved(Topology, Nodes[Index], &bReserved);
696 #if 0 /* MS lies */
697 if (bReserved)
698 {
699 /* node is already used, skip it */
700 continue;
701 }
702 #endif
703 /* set node status as used */
704 MMixerSetTopologyNodeReserved(Topology, Nodes[Index]);
705
706 /* query node type */
707 NodeType = MMixerGetNodeTypeFromTopology(Topology, Nodes[Index]);
708
709 if (IsEqualGUIDAligned(NodeType, &KSNODETYPE_VOLUME))
710 {
711 /* calculate maximum channel count for node */
712 MMixerGetMaxChannelsForNode(MixerContext, MixerInfo, hMixer, Nodes[Index], &MaxChannels);
713
714 DPRINT("NodeId %lu MaxChannels %lu Line %S Id %lu\n", Nodes[Index], MaxChannels, DstLine->Line.szName, DstLine->Line.dwLineID);
715 /* calculate maximum channels */
716 DstLine->Line.cChannels = min(DstLine->Line.cChannels, MaxChannels);
717 }
718 else
719 {
720 /* use default of one channel */
721 MaxChannels = 1;
722 }
723
724 /* now add the mixer control */
725 Status = MMixerAddMixerControl(MixerContext, MixerInfo, hMixer, Topology, Nodes[Index], DstLine, MaxChannels);
726
727 if (Status == MM_STATUS_SUCCESS)
728 {
729 /* increment control count */
730 Count++;
731 }
732 }
733
734 /* store control count */
735 DstLine->Line.cControls = Count;
736
737 /* done */
738 return MM_STATUS_SUCCESS;
739 }
740
741 MIXER_STATUS
742 MMixerGetComponentAndTargetType(
743 IN PMIXER_CONTEXT MixerContext,
744 IN OUT LPMIXER_INFO MixerInfo,
745 IN HANDLE hMixer,
746 IN ULONG PinId,
747 OUT PULONG ComponentType,
748 OUT PULONG TargetType)
749 {
750 KSPIN_DATAFLOW DataFlow;
751 KSPIN_COMMUNICATION Communication;
752 MIXER_STATUS Status;
753 KSP_PIN Request;
754 ULONG BytesReturned;
755 GUID Guid;
756 BOOLEAN BridgePin = FALSE;
757 PKSPIN_PHYSICALCONNECTION Connection;
758
759 /* first dataflow type */
760 Status = MMixerGetPinDataFlowAndCommunication(MixerContext, hMixer, PinId, &DataFlow, &Communication);
761
762 if (Status != MM_STATUS_SUCCESS)
763 {
764 /* failed to get dataflow */
765 return Status;
766 }
767
768 /* now get pin category guid */
769 Request.PinId = PinId;
770 Request.Reserved = 0;
771 Request.Property.Flags = KSPROPERTY_TYPE_GET;
772 Request.Property.Set = KSPROPSETID_Pin;
773 Request.Property.Id = KSPROPERTY_PIN_CATEGORY;
774
775
776 /* get pin category */
777 Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Request, sizeof(KSP_PIN), &Guid, sizeof(GUID), &BytesReturned);
778 if (Status != MM_STATUS_SUCCESS)
779 {
780 /* failed to get dataflow */
781 return Status;
782 }
783
784 /* check if it has a physical connection */
785 Status = MMixerGetPhysicalConnection(MixerContext, hMixer, PinId, &Connection);
786 if (Status == MM_STATUS_SUCCESS)
787 {
788 /* pin is a brige pin */
789 BridgePin = TRUE;
790
791 /* free physical connection */
792 MixerContext->Free(Connection);
793 }
794
795 if (DataFlow == KSPIN_DATAFLOW_IN)
796 {
797 if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_MICROPHONE) ||
798 IsEqualGUIDAligned(&Guid, &KSNODETYPE_DESKTOP_MICROPHONE))
799 {
800 /* type microphone */
801 *TargetType = MIXERLINE_TARGETTYPE_WAVEIN;
802 *ComponentType = MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE;
803 }
804 else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_LEGACY_AUDIO_CONNECTOR) ||
805 IsEqualGUIDAligned(&Guid, &KSCATEGORY_AUDIO) ||
806 IsEqualGUIDAligned(&Guid, &KSNODETYPE_SPEAKER))
807 {
808 /* type waveout */
809 *TargetType = MIXERLINE_TARGETTYPE_WAVEOUT;
810 *ComponentType = MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT;
811 }
812 else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_CD_PLAYER))
813 {
814 /* type cd player */
815 *TargetType = MIXERLINE_TARGETTYPE_UNDEFINED;
816 *ComponentType = MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC;
817 }
818 else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_SYNTHESIZER))
819 {
820 /* type synthesizer */
821 *TargetType = MIXERLINE_TARGETTYPE_MIDIOUT;
822 *ComponentType = MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER;
823 }
824 else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_LINE_CONNECTOR))
825 {
826 /* type line */
827 *TargetType = MIXERLINE_TARGETTYPE_UNDEFINED;
828 *ComponentType = MIXERLINE_COMPONENTTYPE_SRC_LINE;
829 }
830 else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_TELEPHONE) ||
831 IsEqualGUIDAligned(&Guid, &KSNODETYPE_PHONE_LINE) ||
832 IsEqualGUIDAligned(&Guid, &KSNODETYPE_DOWN_LINE_PHONE))
833 {
834 /* type telephone */
835 *TargetType = MIXERLINE_TARGETTYPE_UNDEFINED;
836 *ComponentType = MIXERLINE_COMPONENTTYPE_SRC_TELEPHONE;
837 }
838 else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_ANALOG_CONNECTOR))
839 {
840 /* type analog */
841 if (BridgePin)
842 *TargetType = MIXERLINE_TARGETTYPE_WAVEIN;
843 else
844 *TargetType = MIXERLINE_TARGETTYPE_WAVEOUT;
845
846 *ComponentType = MIXERLINE_COMPONENTTYPE_SRC_ANALOG;
847 }
848 else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_SPDIF_INTERFACE))
849 {
850 /* type analog */
851 if (BridgePin)
852 *TargetType = MIXERLINE_TARGETTYPE_WAVEIN;
853 else
854 *TargetType = MIXERLINE_TARGETTYPE_WAVEOUT;
855
856 *ComponentType = MIXERLINE_COMPONENTTYPE_SRC_DIGITAL;
857 }
858 else
859 {
860 /* unknown type */
861 *TargetType = MIXERLINE_TARGETTYPE_UNDEFINED;
862 *ComponentType = MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED;
863 DPRINT1("Unknown Category for PinId %lu BridgePin %lu\n", PinId, BridgePin);
864 }
865 }
866 else
867 {
868 if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_SPEAKER) ||
869 IsEqualGUIDAligned(&Guid, &KSNODETYPE_DESKTOP_SPEAKER) ||
870 IsEqualGUIDAligned(&Guid, &KSNODETYPE_ROOM_SPEAKER) ||
871 IsEqualGUIDAligned(&Guid, &KSNODETYPE_COMMUNICATION_SPEAKER))
872 {
873 /* type waveout */
874 *TargetType = MIXERLINE_TARGETTYPE_WAVEOUT;
875 *ComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
876 }
877 else if (IsEqualGUIDAligned(&Guid, &KSCATEGORY_AUDIO) ||
878 IsEqualGUIDAligned(&Guid, &PINNAME_CAPTURE))
879 {
880 /* type wavein */
881 *TargetType = MIXERLINE_TARGETTYPE_WAVEIN;
882 *ComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
883 }
884 else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_HEADPHONES) ||
885 IsEqualGUIDAligned(&Guid, &KSNODETYPE_HEAD_MOUNTED_DISPLAY_AUDIO))
886 {
887 /* type head phones */
888 *TargetType = MIXERLINE_TARGETTYPE_WAVEOUT;
889 *ComponentType = MIXERLINE_COMPONENTTYPE_DST_HEADPHONES;
890 }
891 else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_TELEPHONE) ||
892 IsEqualGUIDAligned(&Guid, &KSNODETYPE_PHONE_LINE) ||
893 IsEqualGUIDAligned(&Guid, &KSNODETYPE_DOWN_LINE_PHONE))
894 {
895 /* type waveout */
896 *TargetType = MIXERLINE_TARGETTYPE_UNDEFINED;
897 *ComponentType = MIXERLINE_COMPONENTTYPE_DST_TELEPHONE;
898 }
899 else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_ANALOG_CONNECTOR))
900 {
901 /* type analog */
902 if (BridgePin)
903 {
904 *TargetType = MIXERLINE_TARGETTYPE_WAVEOUT;
905 *ComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
906 }
907 else
908 {
909 *TargetType = MIXERLINE_TARGETTYPE_WAVEIN;
910 *ComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
911 }
912 }
913 else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_SPDIF_INTERFACE))
914 {
915 /* type spdif */
916 if (BridgePin)
917 {
918 *TargetType = MIXERLINE_TARGETTYPE_WAVEOUT;
919 *ComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
920 }
921 else
922 {
923 *TargetType = MIXERLINE_TARGETTYPE_WAVEIN;
924 *ComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
925 }
926 }
927 else
928 {
929 /* unknown type */
930 *TargetType = MIXERLINE_TARGETTYPE_UNDEFINED;
931 *ComponentType = MIXERLINE_COMPONENTTYPE_DST_UNDEFINED;
932 DPRINT1("Unknown Category for PinId %lu BridgePin %lu\n", PinId, BridgePin);
933 }
934 }
935
936 /* done */
937 return MM_STATUS_SUCCESS;
938 }
939
940 MIXER_STATUS
941 MMixerBuildMixerSourceLine(
942 IN PMIXER_CONTEXT MixerContext,
943 IN OUT LPMIXER_INFO MixerInfo,
944 IN HANDLE hMixer,
945 IN PTOPOLOGY Topology,
946 IN ULONG PinId,
947 IN ULONG NodesCount,
948 IN PULONG Nodes,
949 IN ULONG DestinationLineID,
950 OUT LPMIXERLINE_EXT * OutSrcLine)
951 {
952 LPMIXERLINE_EXT SrcLine, DstLine;
953 LPWSTR PinName;
954 MIXER_STATUS Status;
955 ULONG ComponentType, TargetType;
956
957 /* get component and target type */
958 Status = MMixerGetComponentAndTargetType(MixerContext, MixerInfo, hMixer, PinId, &ComponentType, &TargetType);
959 if (Status != MM_STATUS_SUCCESS)
960 {
961 /* failed to get component status */
962 TargetType = MIXERLINE_TARGETTYPE_UNDEFINED;
963 ComponentType = MIXERLINE_COMPONENTTYPE_DST_UNDEFINED;
964 }
965
966 /* construct source line */
967 SrcLine = (LPMIXERLINE_EXT)MixerContext->Alloc(sizeof(MIXERLINE_EXT));
968
969 if (!SrcLine)
970 {
971 /* no memory */
972 return MM_STATUS_NO_MEMORY;
973 }
974
975 /* get destination line */
976 DstLine = MMixerGetSourceMixerLineByLineId(MixerInfo, DestinationLineID);
977 ASSERT(DstLine);
978
979 /* initialize mixer src line */
980 SrcLine->PinId = PinId;
981
982 /* initialize mixer line */
983 SrcLine->Line.cbStruct = sizeof(MIXERLINEW);
984 SrcLine->Line.dwDestination = MixerInfo->MixCaps.cDestinations-1;
985 SrcLine->Line.dwSource = DstLine->Line.cConnections;
986 SrcLine->Line.dwLineID = (DstLine->Line.cConnections * SOURCE_LINE)+ (MixerInfo->MixCaps.cDestinations-1);
987 SrcLine->Line.fdwLine = MIXERLINE_LINEF_ACTIVE | MIXERLINE_LINEF_SOURCE;
988 SrcLine->Line.dwComponentType = ComponentType;
989 SrcLine->Line.dwUser = 0;
990 SrcLine->Line.cChannels = DstLine->Line.cChannels;
991 SrcLine->Line.cConnections = 0;
992 SrcLine->Line.Target.dwType = TargetType;
993 SrcLine->Line.Target.dwDeviceID = DstLine->Line.Target.dwDeviceID;
994 SrcLine->Line.Target.wMid = MixerInfo->MixCaps.wMid;
995 SrcLine->Line.Target.wPid = MixerInfo->MixCaps.wPid;
996 SrcLine->Line.Target.vDriverVersion = MixerInfo->MixCaps.vDriverVersion;
997 InitializeListHead(&SrcLine->ControlsList);
998
999 /* copy name */
1000 ASSERT(MixerInfo->MixCaps.szPname[MAXPNAMELEN-1] == L'\0');
1001 wcscpy(SrcLine->Line.Target.szPname, MixerInfo->MixCaps.szPname);
1002
1003 /* get pin name */
1004 Status = MMixerGetPinName(MixerContext, MixerInfo, hMixer, PinId, &PinName);
1005
1006 if (Status == MM_STATUS_SUCCESS)
1007 {
1008 /* store pin name as line name */
1009 MixerContext->Copy(SrcLine->Line.szShortName, PinName, (min(MIXER_SHORT_NAME_CHARS, wcslen(PinName)+1)) * sizeof(WCHAR));
1010 SrcLine->Line.szShortName[MIXER_SHORT_NAME_CHARS-1] = L'\0';
1011
1012 MixerContext->Copy(SrcLine->Line.szName, PinName, (min(MIXER_LONG_NAME_CHARS, wcslen(PinName)+1)) * sizeof(WCHAR));
1013 SrcLine->Line.szName[MIXER_LONG_NAME_CHARS-1] = L'\0';
1014
1015 /* free pin name buffer */
1016 MixerContext->Free(PinName);
1017 }
1018
1019 /* add the controls to mixer line */
1020 Status = MMixerAddMixerControlsToMixerLineByNodeIndexArray(MixerContext, MixerInfo, hMixer, Topology, SrcLine, NodesCount, Nodes);
1021 if (Status != MM_STATUS_SUCCESS)
1022 {
1023 /* failed */
1024 return Status;
1025 }
1026
1027 /* store result */
1028 *OutSrcLine = SrcLine;
1029
1030 return MM_STATUS_SUCCESS;
1031 }
1032
1033 MIXER_STATUS
1034 MMixerAddMixerSourceLines(
1035 IN PMIXER_CONTEXT MixerContext,
1036 IN OUT LPMIXER_INFO MixerInfo,
1037 IN HANDLE hMixer,
1038 IN PTOPOLOGY Topology,
1039 IN ULONG DestinationLineID,
1040 IN ULONG LineTerminator)
1041 {
1042 PULONG AllNodes, AllPins, AllPinNodes;
1043 ULONG AllNodesCount, AllPinsCount, AllPinNodesCount;
1044 ULONG Index, SubIndex, PinId, CurNode, bConnected;
1045 MIXER_STATUS Status;
1046 LPMIXERLINE_EXT DstLine, SrcLine;
1047
1048 /* get destination line */
1049 DstLine = MMixerGetSourceMixerLineByLineId(MixerInfo, DestinationLineID);
1050 ASSERT(DstLine);
1051
1052 /* allocate an array to store all nodes which are upstream of the line terminator */
1053 Status = MMixerAllocateTopologyNodeArray(MixerContext, Topology, &AllNodes);
1054
1055 /* check for success */
1056 if (Status != MM_STATUS_SUCCESS)
1057 {
1058 /* out of memory */
1059 return MM_STATUS_NO_MEMORY;
1060 }
1061
1062 /* allocate an array to store all nodes which are downstream of a particular pin */
1063 Status = MMixerAllocateTopologyNodeArray(MixerContext, Topology, &AllPinNodes);
1064
1065 /* allocate an array to store all pins which are upstream of this pin */
1066 Status = MMixerAllocateTopologyPinArray(MixerContext, Topology, &AllPins);
1067
1068 /* check for success */
1069 if (Status != MM_STATUS_SUCCESS)
1070 {
1071 /* out of memory */
1072 MixerContext->Free(AllNodes);
1073 return MM_STATUS_NO_MEMORY;
1074 }
1075
1076 /* get all nodes which indirectly / directly connect to this node */
1077 AllNodesCount = 0;
1078 MMixerGetAllUpOrDownstreamNodesFromNodeIndex(MixerContext, Topology, LineTerminator, TRUE, &AllNodesCount, AllNodes);
1079
1080 /* get all pins which indirectly / directly connect to this node */
1081 AllPinsCount = 0;
1082 MMixerGetAllUpOrDownstreamPinsFromNodeIndex(MixerContext, Topology, LineTerminator, TRUE, &AllPinsCount, AllPins);
1083
1084 DPRINT("LineTerminator %lu\n", LineTerminator);
1085 DPRINT("PinCount %lu\n", AllPinsCount);
1086 DPRINT("AllNodesCount %lu\n", AllNodesCount);
1087
1088 /* now construct the source lines which are attached to the destination line */
1089 Index = AllPinsCount;
1090
1091 do
1092 {
1093 /* get current pin id */
1094 PinId = AllPins[Index - 1];
1095
1096 /* reset nodes count */
1097 AllPinNodesCount = 0;
1098
1099 /* now scan all nodes and add them to AllPinNodes array when they are connected to this pin */
1100 for(SubIndex = 0; SubIndex < AllNodesCount; SubIndex++)
1101 {
1102 /* get current node index */
1103 CurNode = AllNodes[SubIndex];
1104
1105 if (CurNode != MAXULONG && CurNode != LineTerminator)
1106 {
1107 /* check if that node is connected in some way to the current pin */
1108 Status = MMixerIsNodeConnectedToPin(MixerContext, Topology, CurNode, PinId, TRUE, &bConnected);
1109
1110 if (Status != MM_STATUS_SUCCESS)
1111 break;
1112
1113 if (bConnected)
1114 {
1115 /* it is connected */
1116 AllPinNodes[AllPinNodesCount] = CurNode;
1117 AllPinNodesCount++;
1118
1119 /* clear current index */
1120 AllNodes[SubIndex] = MAXULONG;
1121 }
1122 }
1123 }
1124
1125 /* decrement pin index */
1126 Index--;
1127
1128 if (AllPinNodesCount)
1129 {
1130 #ifdef MMIXER_DEBUG
1131 ULONG TempIndex;
1132 #endif
1133 /* now build the mixer source line */
1134 Status = MMixerBuildMixerSourceLine(MixerContext, MixerInfo, hMixer, Topology, PinId, AllPinNodesCount, AllPinNodes, DestinationLineID, &SrcLine);
1135
1136 if (Status == MM_STATUS_SUCCESS)
1137 {
1138 /* insert into line list */
1139 InsertTailList(&MixerInfo->LineList, &SrcLine->Entry);
1140
1141 /* increment destination line count */
1142 DstLine->Line.cConnections++;
1143
1144 /* mark pin as reserved */
1145 MMixerSetTopologyPinReserved(Topology, PinId);
1146
1147 #ifdef MMIXER_DEBUG
1148 DPRINT1("Adding PinId %lu AllPinNodesCount %lu to DestinationLine %lu\n", PinId, AllPinNodesCount, DestinationLineID);
1149 for(TempIndex = 0; TempIndex < AllPinNodesCount; TempIndex++)
1150 DPRINT1("NodeIndex %lu\n", AllPinNodes[TempIndex]);
1151 #endif
1152 }
1153 }
1154 else
1155 {
1156 #ifdef MMIXER_DEBUG
1157 DPRINT1("Discarding DestinationLineID %lu PinId %lu NO NODES!\n", DestinationLineID, PinId);
1158 #endif
1159 }
1160
1161 }while(Index != 0);
1162
1163 return MM_STATUS_SUCCESS;
1164 }
1165
1166
1167 MIXER_STATUS
1168 MMixerAddMixerControlsToDestinationLine(
1169 IN PMIXER_CONTEXT MixerContext,
1170 IN OUT LPMIXER_INFO MixerInfo,
1171 IN HANDLE hMixer,
1172 IN PTOPOLOGY Topology,
1173 IN ULONG PinId,
1174 IN ULONG bInput,
1175 IN ULONG DestinationLineId,
1176 OUT PULONG OutLineTerminator)
1177 {
1178 PULONG Nodes;
1179 ULONG NodesCount, LineTerminator;
1180 MIXER_STATUS Status;
1181 LPMIXERLINE_EXT DstLine;
1182
1183 /* allocate nodes index array */
1184 Status = MMixerAllocateTopologyNodeArray(MixerContext, Topology, &Nodes);
1185
1186 /* check for success */
1187 if (Status != MM_STATUS_SUCCESS)
1188 {
1189 /* out of memory */
1190 return Status;
1191 }
1192
1193 /* get all destination line controls */
1194 Status = MMixerCountMixerControls(MixerContext, Topology, PinId, bInput, TRUE, &NodesCount, Nodes, &LineTerminator);
1195
1196 /* check for success */
1197 if (Status != MM_STATUS_SUCCESS)
1198 {
1199 /* failed to count controls */
1200 MixerContext->Free(Nodes);
1201 return Status;
1202 }
1203
1204 /* get destination mixer line */
1205 DstLine = MMixerGetSourceMixerLineByLineId(MixerInfo, DestinationLineId);
1206
1207 /* sanity check */
1208 ASSERT(DstLine);
1209
1210 if (NodesCount > 0)
1211 {
1212 /* add all nodes as mixer controls to the destination line */
1213 Status = MMixerAddMixerControlsToMixerLineByNodeIndexArray(MixerContext, MixerInfo, hMixer, Topology, DstLine, NodesCount, Nodes);
1214 if (Status != MM_STATUS_SUCCESS)
1215 {
1216 /* failed to add controls */
1217 MixerContext->Free(Nodes);
1218 return Status;
1219 }
1220 }
1221
1222 /* store result */
1223 *OutLineTerminator = LineTerminator;
1224
1225 /* return result */
1226 return Status;
1227 }
1228
1229 VOID
1230 MMixerApplyOutputFilterHack(
1231 IN PMIXER_CONTEXT MixerContext,
1232 IN LPMIXER_DATA MixerData,
1233 IN HANDLE hMixer,
1234 IN OUT PULONG PinsCount,
1235 IN OUT PULONG Pins)
1236 {
1237 ULONG Count = 0, Index;
1238 MIXER_STATUS Status;
1239 PKSPIN_PHYSICALCONNECTION Connection;
1240
1241 for(Index = 0; Index < *PinsCount; Index++)
1242 {
1243 /* check if it has a physical connection */
1244 Status = MMixerGetPhysicalConnection(MixerContext, hMixer, Pins[Index], &Connection);
1245
1246 if (Status == MM_STATUS_SUCCESS)
1247 {
1248 /* remove pin */
1249 MixerContext->Copy(&Pins[Index], &Pins[Index + 1], (*PinsCount - (Index + 1)) * sizeof(ULONG));
1250
1251 /* free physical connection */
1252 MixerContext->Free(Connection);
1253
1254 /* decrement index */
1255 Index--;
1256
1257 /* decrement pin count */
1258 (*PinsCount)--;
1259 }
1260 else
1261 {
1262 /* simple pin */
1263 Count++;
1264 }
1265 }
1266
1267 /* store result */
1268 *PinsCount = Count;
1269 }
1270
1271 MIXER_STATUS
1272 MMixerHandleTopologyFilter(
1273 IN PMIXER_CONTEXT MixerContext,
1274 IN PMIXER_LIST MixerList,
1275 IN LPMIXER_DATA MixerData,
1276 IN OUT LPMIXER_INFO MixerInfo,
1277 IN ULONG bInput,
1278 IN ULONG Pin)
1279 {
1280 MIXER_STATUS Status;
1281 ULONG PinsCount, LineTerminator, DestinationLineID;
1282 PULONG Pins;
1283 PTOPOLOGY Topology;
1284
1285 /* re-use existing topology */
1286 Topology = MixerData->Topology;
1287
1288 if (!bInput)
1289 {
1290 /* allocate pin index array which will hold all referenced pins */
1291 Status = MMixerAllocateTopologyPinArray(MixerContext, Topology, &Pins);
1292 if (Status != MM_STATUS_SUCCESS)
1293 {
1294 /* failed to create topology */
1295 return Status;
1296 }
1297
1298 /* the mixer is an output mixer
1299 * find end pin of the node path
1300 */
1301 PinsCount = 0;
1302 Status = MMixerGetAllUpOrDownstreamPinsFromPinIndex(MixerContext, Topology, Pin, FALSE, &PinsCount, Pins);
1303
1304 /* check for success */
1305 if (Status != MM_STATUS_SUCCESS)
1306 {
1307 /* failed to get end pin */
1308 MixerContext->Free(Pins);
1309 //MMixerFreeTopology(Topology);
1310
1311 /* return error code */
1312 return Status;
1313 }
1314 /* HACK:
1315 * some topologies do not have strict boundaries
1316 * WorkArround: remove all pin ids which have a physical connection
1317 * because bridge pins may belong to different render paths
1318 */
1319 MMixerApplyOutputFilterHack(MixerContext, MixerData, MixerData->hDevice, &PinsCount, Pins);
1320
1321 /* sanity checks */
1322 ASSERT(PinsCount != 0);
1323 if (PinsCount != 1)
1324 {
1325 DPRINT1("MMixerHandlePhysicalConnection Expected 1 pin but got %lu\n", PinsCount);
1326 }
1327
1328 /* create destination line */
1329 Status = MMixerBuildMixerDestinationLine(MixerContext, MixerInfo, MixerData->hDevice, Pins[0], bInput);
1330
1331 /* calculate destination line id */
1332 DestinationLineID = (DESTINATION_LINE + MixerInfo->MixCaps.cDestinations - 1);
1333
1334 if (Status != MM_STATUS_SUCCESS)
1335 {
1336 /* failed to build destination line */
1337 MixerContext->Free(Pins);
1338
1339 /* return error code */
1340 return Status;
1341 }
1342
1343 /* add mixer controls to destination line */
1344 Status = MMixerAddMixerControlsToDestinationLine(MixerContext, MixerInfo, MixerData->hDevice, Topology, Pins[0], bInput, DestinationLineID, &LineTerminator);
1345
1346 if (Status == MM_STATUS_SUCCESS)
1347 {
1348 /* now add the rest of the source lines */
1349 Status = MMixerAddMixerSourceLines(MixerContext, MixerInfo, MixerData->hDevice, Topology, DestinationLineID, LineTerminator);
1350 }
1351
1352 /* mark pin as consumed */
1353 MMixerSetTopologyPinReserved(Topology, Pins[0]);
1354
1355 /* free topology pin array */
1356 MixerContext->Free(Pins);
1357 }
1358 else
1359 {
1360 /* calculate destination line id */
1361 DestinationLineID = (DESTINATION_LINE + MixerInfo->MixCaps.cDestinations - 1);
1362
1363 /* add mixer controls */
1364 Status = MMixerAddMixerControlsToDestinationLine(MixerContext, MixerInfo, MixerData->hDevice, Topology, Pin, bInput, DestinationLineID, &LineTerminator);
1365
1366 if (Status == MM_STATUS_SUCCESS)
1367 {
1368 /* now add the rest of the source lines */
1369 Status = MMixerAddMixerSourceLines(MixerContext, MixerInfo, MixerData->hDevice, Topology, DestinationLineID, LineTerminator);
1370 }
1371 }
1372 return Status;
1373 }
1374
1375 MIXER_STATUS
1376 MMixerHandlePhysicalConnection(
1377 IN PMIXER_CONTEXT MixerContext,
1378 IN PMIXER_LIST MixerList,
1379 IN LPMIXER_DATA MixerData,
1380 IN OUT LPMIXER_INFO MixerInfo,
1381 IN ULONG bInput,
1382 IN PKSPIN_PHYSICALCONNECTION OutConnection)
1383 {
1384 MIXER_STATUS Status;
1385 ULONG PinsCount, LineTerminator, DestinationLineID;
1386 PULONG Pins;
1387 PTOPOLOGY Topology;
1388
1389 /* first try to open the connected filter */
1390 OutConnection->SymbolicLinkName[1] = L'\\';
1391 MixerData = MMixerGetDataByDeviceName(MixerList, OutConnection->SymbolicLinkName);
1392
1393 /* check if the linked connection is found */
1394 if (!MixerData)
1395 {
1396 /* filter references invalid physical connection */
1397 return MM_STATUS_UNSUCCESSFUL;
1398 }
1399
1400 DPRINT("Name %S, Pin %lu bInput %lu\n", OutConnection->SymbolicLinkName, OutConnection->Pin, bInput);
1401
1402 /* sanity check */
1403 ASSERT(MixerData->MixerInfo == NULL || MixerData->MixerInfo == MixerInfo);
1404
1405 /* associate with mixer */
1406 MixerData->MixerInfo = MixerInfo;
1407
1408 if (MixerData->Topology == NULL)
1409 {
1410 /* construct new topology */
1411 Status = MMixerBuildTopology(MixerContext, MixerData, &Topology);
1412 if (Status != MM_STATUS_SUCCESS)
1413 {
1414 /* failed to create topology */
1415 return Status;
1416 }
1417
1418 /* store topology */
1419 MixerData->Topology = Topology;
1420 }
1421 else
1422 {
1423 /* re-use existing topology */
1424 Topology = MixerData->Topology;
1425 }
1426
1427 /* mark pin as consumed */
1428 MMixerSetTopologyPinReserved(Topology, OutConnection->Pin);
1429
1430 if (!bInput)
1431 {
1432 /* allocate pin index array which will hold all referenced pins */
1433 Status = MMixerAllocateTopologyPinArray(MixerContext, Topology, &Pins);
1434 if (Status != MM_STATUS_SUCCESS)
1435 {
1436 /* failed to create topology */
1437 return Status;
1438 }
1439
1440 /* the mixer is an output mixer
1441 * find end pin of the node path
1442 */
1443 PinsCount = 0;
1444 Status = MMixerGetAllUpOrDownstreamPinsFromPinIndex(MixerContext, Topology, OutConnection->Pin, FALSE, &PinsCount, Pins);
1445
1446 /* check for success */
1447 if (Status != MM_STATUS_SUCCESS)
1448 {
1449 /* failed to get end pin */
1450 MixerContext->Free(Pins);
1451 //MMixerFreeTopology(Topology);
1452
1453 /* return error code */
1454 return Status;
1455 }
1456 /* HACK:
1457 * some topologies do not have strict boundaries
1458 * WorkArround: remove all pin ids which have a physical connection
1459 * because bridge pins may belong to different render paths
1460 */
1461 MMixerApplyOutputFilterHack(MixerContext, MixerData, MixerData->hDevice, &PinsCount, Pins);
1462
1463 /* sanity checks */
1464 ASSERT(PinsCount != 0);
1465 if (PinsCount != 1)
1466 {
1467 DPRINT1("MMixerHandlePhysicalConnection Expected 1 pin but got %lu\n", PinsCount);
1468 }
1469
1470 /* create destination line */
1471 Status = MMixerBuildMixerDestinationLine(MixerContext, MixerInfo, MixerData->hDevice, Pins[0], bInput);
1472
1473 /* calculate destination line id */
1474 DestinationLineID = (DESTINATION_LINE + MixerInfo->MixCaps.cDestinations-1);
1475
1476 if (Status != MM_STATUS_SUCCESS)
1477 {
1478 /* failed to build destination line */
1479 MixerContext->Free(Pins);
1480
1481 /* return error code */
1482 return Status;
1483 }
1484
1485 /* add mixer controls to destination line */
1486 Status = MMixerAddMixerControlsToDestinationLine(MixerContext, MixerInfo, MixerData->hDevice, Topology, Pins[0], bInput, DestinationLineID, &LineTerminator);
1487
1488 if (Status == MM_STATUS_SUCCESS)
1489 {
1490 /* now add the rest of the source lines */
1491 Status = MMixerAddMixerSourceLines(MixerContext, MixerInfo, MixerData->hDevice, Topology, DestinationLineID, LineTerminator);
1492 }
1493
1494 /* mark pin as consumed */
1495 MMixerSetTopologyPinReserved(Topology, Pins[0]);
1496
1497 /* free topology pin array */
1498 MixerContext->Free(Pins);
1499 }
1500 else
1501 {
1502 /* calculate destination line id */
1503 DestinationLineID = (DESTINATION_LINE + MixerInfo->MixCaps.cDestinations-1);
1504
1505 /* add mixer controls */
1506 Status = MMixerAddMixerControlsToDestinationLine(MixerContext, MixerInfo, MixerData->hDevice, Topology, OutConnection->Pin, bInput, DestinationLineID, &LineTerminator);
1507
1508 if (Status == MM_STATUS_SUCCESS)
1509 {
1510 /* now add the rest of the source lines */
1511 Status = MMixerAddMixerSourceLines(MixerContext, MixerInfo, MixerData->hDevice, Topology, DestinationLineID, LineTerminator);
1512 }
1513 }
1514
1515 return Status;
1516 }
1517
1518 MIXER_STATUS
1519 MMixerInitializeFilter(
1520 IN PMIXER_CONTEXT MixerContext,
1521 IN PMIXER_LIST MixerList,
1522 IN LPMIXER_DATA MixerData,
1523 IN LPMIXER_INFO MixerInfo,
1524 IN PTOPOLOGY Topology,
1525 IN ULONG NodeIndex,
1526 IN ULONG bInputMixer,
1527 IN OUT LPMIXER_INFO * OutMixerInfo)
1528 {
1529 ULONG Index;
1530 MIXER_STATUS Status;
1531 PKSPIN_PHYSICALCONNECTION OutConnection;
1532 ULONG * Pins;
1533 ULONG PinsFound;
1534 ULONG NewMixerInfo = FALSE;
1535
1536 if (MixerInfo == NULL)
1537 {
1538 /* allocate a mixer info struct */
1539 MixerInfo = (LPMIXER_INFO) MixerContext->Alloc(sizeof(MIXER_INFO));
1540 if (!MixerInfo)
1541 {
1542 /* no memory */
1543 return MM_STATUS_NO_MEMORY;
1544 }
1545
1546 /* new mixer info */
1547 NewMixerInfo = TRUE;
1548
1549 /* intialize mixer caps */
1550 MixerInfo->MixCaps.wMid = MM_MICROSOFT; /* FIXME */
1551 MixerInfo->MixCaps.wPid = MM_PID_UNMAPPED; /* FIXME */
1552 MixerInfo->MixCaps.vDriverVersion = 1; /* FIXME */
1553 MixerInfo->MixCaps.fdwSupport = 0;
1554 MixerInfo->MixCaps.cDestinations = 0;
1555
1556 /* get mixer name */
1557 Status = MMixerGetDeviceName(MixerContext, MixerInfo->MixCaps.szPname, MixerData->hDeviceInterfaceKey);
1558 if (Status != MM_STATUS_SUCCESS)
1559 {
1560 /* try get name with component id */
1561 Status = MMixerGetDeviceNameWithComponentId(MixerContext, MixerData->hDevice, MixerInfo->MixCaps.szPname);
1562 }
1563
1564 /* initialize line list */
1565 InitializeListHead(&MixerInfo->LineList);
1566 InitializeListHead(&MixerInfo->EventList);
1567
1568 /* associate with mixer data */
1569 MixerData->MixerInfo = MixerInfo;
1570 }
1571
1572 /* store mixer info */
1573 *OutMixerInfo = MixerInfo;
1574
1575 /* now allocate an array which will receive the indices of the pin
1576 * which has a ADC / DAC nodetype in its path
1577 */
1578 Status = MMixerAllocateTopologyPinArray(MixerContext, Topology, &Pins);
1579 ASSERT(Status == MM_STATUS_SUCCESS);
1580
1581 PinsFound = 0;
1582
1583 /* now get all sink / source pins, which are attached to the ADC / DAC node
1584 * For sink pins (wave out) search up stream
1585 * For source pins (wave in) search down stream
1586 * The search direction is always the opposite of the current mixer type
1587 */
1588 PinsFound = 0;
1589 MMixerGetAllUpOrDownstreamPinsFromNodeIndex(MixerContext, Topology, NodeIndex, !bInputMixer, &PinsFound, Pins);
1590
1591 /* if there is no pin found, we have a broken topology */
1592 ASSERT(PinsFound != 0);
1593
1594 /* now create a wave info struct */
1595 Status = MMixerInitializeWaveInfo(MixerContext, MixerList, MixerData, MixerInfo->MixCaps.szPname, bInputMixer, PinsFound, Pins);
1596 if (Status != MM_STATUS_SUCCESS)
1597 {
1598 /* failed to create wave info struct */
1599 MixerContext->Free(MixerInfo);
1600 MixerContext->Free(Pins);
1601 return Status;
1602 }
1603
1604 /* mark all found pins as reserved */
1605 for(Index = 0; Index < PinsFound; Index++)
1606 {
1607 MMixerSetTopologyPinReserved(Topology, Pins[Index]);
1608 }
1609
1610 if (bInputMixer)
1611 {
1612 /* pre create the mixer destination line for input mixers */
1613 Status = MMixerBuildMixerDestinationLine(MixerContext, MixerInfo, MixerData->hDevice, Pins[0], bInputMixer);
1614
1615 if (Status != MM_STATUS_SUCCESS)
1616 {
1617 /* failed to create mixer destination line */
1618 return Status;
1619 }
1620 }
1621
1622
1623 /* now get the bridge pin which is at the end of node path
1624 * For sink pins (wave out) search down stream
1625 * For source pins (wave in) search up stream
1626 */
1627 MixerContext->Free(Pins);
1628 Status = MMixerAllocateTopologyPinArray(MixerContext, Topology, &Pins);
1629 ASSERT(Status == MM_STATUS_SUCCESS);
1630
1631 PinsFound = 0;
1632 MMixerGetAllUpOrDownstreamPinsFromNodeIndex(MixerContext, Topology, NodeIndex, bInputMixer, &PinsFound, Pins);
1633
1634 /* if there is no pin found, we have a broken topology */
1635 ASSERT(PinsFound != 0);
1636
1637 /* there should be exactly one bridge pin */
1638 ASSERT(PinsFound == 1);
1639
1640 DPRINT("BridgePin %lu bInputMixer %lu\n", Pins[0], bInputMixer);
1641
1642 /* does the pin have a physical connection */
1643 Status = MMixerGetPhysicalConnection(MixerContext, MixerData->hDevice, Pins[0], &OutConnection);
1644
1645 if (Status == MM_STATUS_SUCCESS)
1646 {
1647 /* mark pin as reserved */
1648 MMixerSetTopologyPinReserved(Topology, Pins[0]);
1649
1650 /* topology on the topoloy filter */
1651 Status = MMixerHandlePhysicalConnection(MixerContext, MixerList, MixerData, MixerInfo, bInputMixer, OutConnection);
1652
1653 /* free physical connection data */
1654 MixerContext->Free(OutConnection);
1655 }
1656 else
1657 {
1658 /* topology is on the same filter */
1659 Status = MMixerHandleTopologyFilter(MixerContext, MixerList, MixerData, MixerInfo, bInputMixer, Pins[0]);
1660 }
1661
1662 /* free pins */
1663 MixerContext->Free(Pins);
1664
1665 if (NewMixerInfo)
1666 {
1667 /* insert mixer */
1668 InsertHeadList(&MixerList->MixerList, &MixerInfo->Entry);
1669 /* increment mixer count */
1670 MixerList->MixerListCount++;
1671 }
1672
1673 /* done */
1674 return Status;
1675 }
1676
1677 VOID
1678 MMixerHandleAlternativeMixers(
1679 IN PMIXER_CONTEXT MixerContext,
1680 IN PMIXER_LIST MixerList,
1681 IN LPMIXER_DATA MixerData,
1682 IN PTOPOLOGY Topology)
1683 {
1684 ULONG Index, PinCount, Reserved;
1685 MIXER_STATUS Status;
1686 ULONG DestinationLineID, LineTerminator;
1687 LPMIXERLINE_EXT DstLine;
1688
1689 DPRINT("DeviceName %S\n", MixerData->DeviceName);
1690
1691 /* get topology pin count */
1692 MMixerGetTopologyPinCount(Topology, &PinCount);
1693
1694 for(Index = 0; Index < PinCount; Index++)
1695 {
1696 MMixerIsTopologyPinReserved(Topology, Index, &Reserved);
1697
1698 /* check if it has already been reserved */
1699 if (Reserved)
1700 {
1701 /* pin has already been reserved */
1702 continue;
1703 }
1704
1705 DPRINT("MixerName %S Available PinID %lu\n", MixerData->DeviceName, Index);
1706
1707 /* sanity check */
1708 //ASSERT(MixerData->MixerInfo);
1709
1710 if (!MixerData->MixerInfo)
1711 {
1712 DPRINT1("Expected mixer info\n");
1713 continue;
1714 }
1715
1716 /* build the destination line */
1717 Status = MMixerBuildMixerDestinationLine(MixerContext, MixerData->MixerInfo, MixerData->hDevice, Index, TRUE);
1718 if (Status != MM_STATUS_SUCCESS)
1719 {
1720 /* failed to build destination line */
1721 continue;
1722 }
1723
1724 /* calculate destination line id */
1725 DestinationLineID = (DESTINATION_LINE + MixerData->MixerInfo->MixCaps.cDestinations-1);
1726
1727 /* add mixer controls to destination line */
1728 Status = MMixerAddMixerControlsToDestinationLine(MixerContext, MixerData->MixerInfo, MixerData->hDevice, MixerData->Topology, Index, TRUE, DestinationLineID, &LineTerminator);
1729 if (Status == MM_STATUS_SUCCESS)
1730 {
1731 /* now add the rest of the source lines */
1732 Status = MMixerAddMixerSourceLines(MixerContext, MixerData->MixerInfo, MixerData->hDevice, MixerData->Topology, DestinationLineID, LineTerminator);
1733 }
1734
1735 /* mark pin as consumed */
1736 MMixerSetTopologyPinReserved(Topology, Index);
1737
1738 /* now grab destination line */
1739 DstLine = MMixerGetSourceMixerLineByLineId(MixerData->MixerInfo, DestinationLineID);
1740
1741 /* set type and target as undefined */
1742 DstLine->Line.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_UNDEFINED;
1743 DstLine->Line.Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
1744 DstLine->Line.Target.vDriverVersion = 0;
1745 DstLine->Line.Target.wMid = 0;
1746 DstLine->Line.Target.wPid = 0;
1747 }
1748 }
1749
1750 MIXER_STATUS
1751 MMixerSetupFilter(
1752 IN PMIXER_CONTEXT MixerContext,
1753 IN PMIXER_LIST MixerList,
1754 IN LPMIXER_DATA MixerData,
1755 IN PULONG DeviceCount)
1756 {
1757 MIXER_STATUS Status = MM_STATUS_SUCCESS;
1758 PTOPOLOGY Topology;
1759 ULONG NodeIndex;
1760 LPMIXER_INFO MixerInfo = NULL;
1761
1762 /* check if topology has already been built */
1763 if (MixerData->Topology == NULL)
1764 {
1765 /* build topology */
1766 Status = MMixerBuildTopology(MixerContext, MixerData, &Topology);
1767
1768 if (Status != MM_STATUS_SUCCESS)
1769 {
1770 /* failed to build topology */
1771 return Status;
1772 }
1773
1774 /* store topology */
1775 MixerData->Topology = Topology;
1776 }
1777 else
1778 {
1779 /* re-use topology */
1780 Topology = MixerData->Topology;
1781 }
1782
1783 /* check if the filter has an wave out node */
1784 NodeIndex = MMixerGetNodeIndexFromGuid(Topology, &KSNODETYPE_DAC);
1785 if (NodeIndex != MAXULONG)
1786 {
1787 /* it has */
1788 Status = MMixerInitializeFilter(MixerContext, MixerList, MixerData, NULL, Topology, NodeIndex, FALSE, &MixerInfo);
1789
1790 /* check for success */
1791 if (Status == MM_STATUS_SUCCESS)
1792 {
1793 /* increment mixer count */
1794 (*DeviceCount)++;
1795 }
1796 else
1797 {
1798 /* reset mixer info in case of error */
1799 MixerInfo = NULL;
1800 }
1801 }
1802
1803 /* check if the filter has an wave in node */
1804 NodeIndex = MMixerGetNodeIndexFromGuid(Topology, &KSNODETYPE_ADC);
1805 if (NodeIndex != MAXULONG)
1806 {
1807 /* it has */
1808 Status = MMixerInitializeFilter(MixerContext, MixerList, MixerData, MixerInfo, Topology, NodeIndex, TRUE, &MixerInfo);
1809
1810 /* check for success */
1811 if (Status == MM_STATUS_SUCCESS)
1812 {
1813 /* increment mixer count */
1814 (*DeviceCount)++;
1815 }
1816
1817 }
1818
1819 /* TODO: apply hacks for Wave source line */
1820
1821 /* activate midi devices */
1822 //MMixerInitializeMidiForFilter(MixerContext, MixerList, MixerData, Topology);
1823
1824 /* done */
1825 return Status;
1826 }
1827
1828
1829 MIXER_STATUS
1830 MMixerAddEvent(
1831 IN PMIXER_CONTEXT MixerContext,
1832 IN OUT LPMIXER_INFO MixerInfo,
1833 IN PVOID MixerEventContext,
1834 IN PMIXER_EVENT MixerEventRoutine)
1835 {
1836 //KSE_NODE Property;
1837 //KSEVENTDATA EventData
1838 //ULONG BytesReturned;
1839 //MIXER_STATUS Status;
1840 PEVENT_NOTIFICATION_ENTRY EventNotification;
1841
1842 EventNotification = (PEVENT_NOTIFICATION_ENTRY)MixerContext->Alloc(sizeof(EVENT_NOTIFICATION_ENTRY));
1843 if (!EventNotification)
1844 {
1845 /* not enough memory */
1846 return MM_STATUS_NO_MEMORY;
1847 }
1848
1849 /* FIXME: what is it supposed to happen with KSEVENTDATA ? */
1850 #if 0
1851 /* setup request */
1852 Property.Event.Set = KSEVENTSETID_AudioControlChange;
1853 Property.Event.Flags = KSEVENT_TYPE_TOPOLOGY|KSEVENT_TYPE_ENABLE;
1854 Property.Event.Id = KSEVENT_CONTROL_CHANGE;
1855
1856 Property.NodeId = NodeId;
1857 Property.Reserved = 0;
1858
1859 Status = MixerContext->Control(MixerInfo->hMixer, IOCTL_KS_ENABLE_EVENT, (PVOID)&Property, sizeof(KSP_NODE), (PVOID)EventData, sizeof(KSEVENTDATA), &BytesReturned);
1860 if (Status != MM_STATUS_SUCCESS)
1861 {
1862 /* failed to add event */
1863 MixerContext->FreeEventData(EventData);
1864 return Status;
1865 }
1866 #endif
1867
1868 /* initialize notification entry */
1869 EventNotification->MixerEventContext = MixerEventContext;
1870 EventNotification->MixerEventRoutine = MixerEventRoutine;
1871
1872 /* store event */
1873 InsertTailList(&MixerInfo->EventList, &EventNotification->Entry);
1874 return MM_STATUS_SUCCESS;
1875 }
1876
1877 MIXER_STATUS
1878 MMixerRemoveEvent(
1879 IN PMIXER_CONTEXT MixerContext,
1880 IN OUT LPMIXER_INFO MixerInfo,
1881 IN PVOID MixerEventContext,
1882 IN PMIXER_EVENT MixerEventRoutine)
1883 {
1884 PLIST_ENTRY EventList;
1885 PEVENT_NOTIFICATION_ENTRY NotificationEntry;
1886
1887 /* Lookup through mixers */
1888 EventList = MixerInfo->EventList.Flink;
1889 while(EventList != &MixerInfo->EventList)
1890 {
1891 NotificationEntry = CONTAINING_RECORD(EventList, EVENT_NOTIFICATION_ENTRY, Entry);
1892 EventList = EventList->Flink;
1893 /* TODO: find a better way to identify an event ? */
1894 if(NotificationEntry->MixerEventRoutine == MixerEventRoutine &&
1895 NotificationEntry->MixerEventContext == MixerEventContext)
1896 {
1897 DPRINT1("Freeing entry %p\n", NotificationEntry);
1898 /* We found the event to remove */
1899 RemoveEntryList(&NotificationEntry->Entry);
1900 MixerContext->Free(NotificationEntry);
1901 }
1902 }
1903 return MM_STATUS_SUCCESS;
1904 }