[CMAKE]
[reactos.git] / reactos / 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 MMixerHandlePhysicalConnection(
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 PKSPIN_PHYSICALCONNECTION OutConnection)
1279 {
1280 MIXER_STATUS Status;
1281 ULONG PinsCount, LineTerminator, DestinationLineID;
1282 PULONG Pins;
1283 PTOPOLOGY Topology;
1284
1285 /* first try to open the connected filter */
1286 OutConnection->SymbolicLinkName[1] = L'\\';
1287 MixerData = MMixerGetDataByDeviceName(MixerList, OutConnection->SymbolicLinkName);
1288
1289 /* check if the linked connection is found */
1290 if (!MixerData)
1291 {
1292 /* filter references invalid physical connection */
1293 return MM_STATUS_UNSUCCESSFUL;
1294 }
1295
1296 DPRINT("Name %S, Pin %lu bInput %lu\n", OutConnection->SymbolicLinkName, OutConnection->Pin, bInput);
1297
1298 /* sanity check */
1299 ASSERT(MixerData->MixerInfo == NULL || MixerData->MixerInfo == MixerInfo);
1300
1301 /* associate with mixer */
1302 MixerData->MixerInfo = MixerInfo;
1303
1304 if (MixerData->Topology == NULL)
1305 {
1306 /* construct new topology */
1307 Status = MMixerBuildTopology(MixerContext, MixerData, &Topology);
1308 if (Status != MM_STATUS_SUCCESS)
1309 {
1310 /* failed to create topology */
1311 return Status;
1312 }
1313
1314 /* store topology */
1315 MixerData->Topology = Topology;
1316 }
1317 else
1318 {
1319 /* re-use existing topology */
1320 Topology = MixerData->Topology;
1321 }
1322
1323 /* mark pin as consumed */
1324 MMixerSetTopologyPinReserved(Topology, OutConnection->Pin);
1325
1326 if (!bInput)
1327 {
1328 /* allocate pin index array which will hold all referenced pins */
1329 Status = MMixerAllocateTopologyPinArray(MixerContext, Topology, &Pins);
1330 if (Status != MM_STATUS_SUCCESS)
1331 {
1332 /* failed to create topology */
1333 return Status;
1334 }
1335
1336 /* the mixer is an output mixer
1337 * find end pin of the node path
1338 */
1339 PinsCount = 0;
1340 Status = MMixerGetAllUpOrDownstreamPinsFromPinIndex(MixerContext, Topology, OutConnection->Pin, FALSE, &PinsCount, Pins);
1341
1342 /* check for success */
1343 if (Status != MM_STATUS_SUCCESS)
1344 {
1345 /* failed to get end pin */
1346 MixerContext->Free(Pins);
1347 //MMixerFreeTopology(Topology);
1348
1349 /* return error code */
1350 return Status;
1351 }
1352 /* HACK:
1353 * some topologies do not have strict boundaries
1354 * WorkArround: remove all pin ids which have a physical connection
1355 * because bridge pins may belong to different render paths
1356 */
1357 MMixerApplyOutputFilterHack(MixerContext, MixerData, MixerData->hDevice, &PinsCount, Pins);
1358
1359 /* sanity checks */
1360 ASSERT(PinsCount != 0);
1361 if (PinsCount != 1)
1362 {
1363 DPRINT1("MMixerHandlePhysicalConnection Expected 1 pin but got %lu\n", PinsCount);
1364 }
1365
1366 /* create destination line */
1367 Status = MMixerBuildMixerDestinationLine(MixerContext, MixerInfo, MixerData->hDevice, Pins[0], bInput);
1368
1369 /* calculate destination line id */
1370 DestinationLineID = (DESTINATION_LINE + MixerInfo->MixCaps.cDestinations-1);
1371
1372 if (Status != MM_STATUS_SUCCESS)
1373 {
1374 /* failed to build destination line */
1375 MixerContext->Free(Pins);
1376
1377 /* return error code */
1378 return Status;
1379 }
1380
1381 /* add mixer controls to destination line */
1382 Status = MMixerAddMixerControlsToDestinationLine(MixerContext, MixerInfo, MixerData->hDevice, Topology, Pins[0], bInput, DestinationLineID, &LineTerminator);
1383
1384 if (Status == MM_STATUS_SUCCESS)
1385 {
1386 /* now add the rest of the source lines */
1387 Status = MMixerAddMixerSourceLines(MixerContext, MixerInfo, MixerData->hDevice, Topology, DestinationLineID, LineTerminator);
1388 }
1389
1390 /* mark pin as consumed */
1391 MMixerSetTopologyPinReserved(Topology, Pins[0]);
1392
1393 /* free topology pin array */
1394 MixerContext->Free(Pins);
1395 }
1396 else
1397 {
1398 /* calculate destination line id */
1399 DestinationLineID = (DESTINATION_LINE + MixerInfo->MixCaps.cDestinations-1);
1400
1401 /* add mixer controls */
1402 Status = MMixerAddMixerControlsToDestinationLine(MixerContext, MixerInfo, MixerData->hDevice, Topology, OutConnection->Pin, bInput, DestinationLineID, &LineTerminator);
1403
1404 if (Status == MM_STATUS_SUCCESS)
1405 {
1406 /* now add the rest of the source lines */
1407 Status = MMixerAddMixerSourceLines(MixerContext, MixerInfo, MixerData->hDevice, Topology, DestinationLineID, LineTerminator);
1408 }
1409 }
1410
1411 return Status;
1412 }
1413
1414 MIXER_STATUS
1415 MMixerInitializeFilter(
1416 IN PMIXER_CONTEXT MixerContext,
1417 IN PMIXER_LIST MixerList,
1418 IN LPMIXER_DATA MixerData,
1419 IN LPMIXER_INFO MixerInfo,
1420 IN PTOPOLOGY Topology,
1421 IN ULONG NodeIndex,
1422 IN ULONG bInputMixer,
1423 IN OUT LPMIXER_INFO * OutMixerInfo)
1424 {
1425 ULONG Index;
1426 MIXER_STATUS Status;
1427 PKSPIN_PHYSICALCONNECTION OutConnection;
1428 ULONG * Pins;
1429 ULONG PinsFound;
1430 ULONG NewMixerInfo = FALSE;
1431
1432 if (MixerInfo == NULL)
1433 {
1434 /* allocate a mixer info struct */
1435 MixerInfo = (LPMIXER_INFO) MixerContext->Alloc(sizeof(MIXER_INFO));
1436 if (!MixerInfo)
1437 {
1438 /* no memory */
1439 return MM_STATUS_NO_MEMORY;
1440 }
1441
1442 /* new mixer info */
1443 NewMixerInfo = TRUE;
1444
1445 /* intialize mixer caps */
1446 MixerInfo->MixCaps.wMid = MM_MICROSOFT; /* FIXME */
1447 MixerInfo->MixCaps.wPid = MM_PID_UNMAPPED; /* FIXME */
1448 MixerInfo->MixCaps.vDriverVersion = 1; /* FIXME */
1449 MixerInfo->MixCaps.fdwSupport = 0;
1450 MixerInfo->MixCaps.cDestinations = 0;
1451
1452 /* get mixer name */
1453 MMixerGetDeviceName(MixerContext, MixerInfo->MixCaps.szPname, MixerData->hDeviceInterfaceKey);
1454
1455 /* initialize line list */
1456 InitializeListHead(&MixerInfo->LineList);
1457 InitializeListHead(&MixerInfo->EventList);
1458
1459 /* associate with mixer data */
1460 MixerData->MixerInfo = MixerInfo;
1461 }
1462
1463 /* store mixer info */
1464 *OutMixerInfo = MixerInfo;
1465
1466 /* now allocate an array which will receive the indices of the pin
1467 * which has a ADC / DAC nodetype in its path
1468 */
1469 Status = MMixerAllocateTopologyPinArray(MixerContext, Topology, &Pins);
1470 ASSERT(Status == MM_STATUS_SUCCESS);
1471
1472 PinsFound = 0;
1473
1474 /* now get all sink / source pins, which are attached to the ADC / DAC node
1475 * For sink pins (wave out) search up stream
1476 * For source pins (wave in) search down stream
1477 * The search direction is always the opposite of the current mixer type
1478 */
1479 PinsFound = 0;
1480 MMixerGetAllUpOrDownstreamPinsFromNodeIndex(MixerContext, Topology, NodeIndex, !bInputMixer, &PinsFound, Pins);
1481
1482 /* if there is no pin found, we have a broken topology */
1483 ASSERT(PinsFound != 0);
1484
1485 /* now create a wave info struct */
1486 Status = MMixerInitializeWaveInfo(MixerContext, MixerList, MixerData, MixerInfo->MixCaps.szPname, bInputMixer, PinsFound, Pins);
1487 if (Status != MM_STATUS_SUCCESS)
1488 {
1489 /* failed to create wave info struct */
1490 MixerContext->Free(MixerInfo);
1491 MixerContext->Free(Pins);
1492 return Status;
1493 }
1494
1495 /* mark all found pins as reserved */
1496 for(Index = 0; Index < PinsFound; Index++)
1497 {
1498 MMixerSetTopologyPinReserved(Topology, Pins[Index]);
1499 }
1500
1501 if (bInputMixer)
1502 {
1503 /* pre create the mixer destination line for input mixers */
1504 Status = MMixerBuildMixerDestinationLine(MixerContext, MixerInfo, MixerData->hDevice, Pins[0], bInputMixer);
1505
1506 if (Status != MM_STATUS_SUCCESS)
1507 {
1508 /* failed to create mixer destination line */
1509 return Status;
1510 }
1511 }
1512
1513
1514 /* now get the bridge pin which is at the end of node path
1515 * For sink pins (wave out) search down stream
1516 * For source pins (wave in) search up stream
1517 */
1518 MixerContext->Free(Pins);
1519 Status = MMixerAllocateTopologyPinArray(MixerContext, Topology, &Pins);
1520 ASSERT(Status == MM_STATUS_SUCCESS);
1521
1522 PinsFound = 0;
1523 MMixerGetAllUpOrDownstreamPinsFromNodeIndex(MixerContext, Topology, NodeIndex, bInputMixer, &PinsFound, Pins);
1524
1525 /* if there is no pin found, we have a broken topology */
1526 ASSERT(PinsFound != 0);
1527
1528 /* there should be exactly one bridge pin */
1529 ASSERT(PinsFound == 1);
1530
1531 DPRINT("BridgePin %lu bInputMixer %lu\n", Pins[0], bInputMixer);
1532
1533 /* does the pin have a physical connection */
1534 Status = MMixerGetPhysicalConnection(MixerContext, MixerData->hDevice, Pins[0], &OutConnection);
1535
1536 if (Status == MM_STATUS_SUCCESS)
1537 {
1538 /* mark pin as reserved */
1539 MMixerSetTopologyPinReserved(Topology, Pins[0]);
1540
1541 /* topology on the topoloy filter */
1542 Status = MMixerHandlePhysicalConnection(MixerContext, MixerList, MixerData, MixerInfo, bInputMixer, OutConnection);
1543
1544 /* free physical connection data */
1545 MixerContext->Free(OutConnection);
1546 }
1547 else
1548 {
1549 /* FIXME
1550 * handle drivers which expose their topology on the same filter
1551 */
1552 ASSERT(0);
1553 }
1554
1555 /* free pins */
1556 MixerContext->Free(Pins);
1557
1558 if (NewMixerInfo)
1559 {
1560 /* insert mixer */
1561 InsertHeadList(&MixerList->MixerList, &MixerInfo->Entry);
1562 /* increment mixer count */
1563 MixerList->MixerListCount++;
1564 }
1565
1566 /* done */
1567 return Status;
1568 }
1569
1570 VOID
1571 MMixerHandleAlternativeMixers(
1572 IN PMIXER_CONTEXT MixerContext,
1573 IN PMIXER_LIST MixerList,
1574 IN LPMIXER_DATA MixerData,
1575 IN PTOPOLOGY Topology)
1576 {
1577 ULONG Index, PinCount, Reserved;
1578 MIXER_STATUS Status;
1579 ULONG DestinationLineID, LineTerminator;
1580 LPMIXERLINE_EXT DstLine;
1581
1582 DPRINT("DeviceName %S\n", MixerData->DeviceName);
1583
1584 /* get topology pin count */
1585 MMixerGetTopologyPinCount(Topology, &PinCount);
1586
1587 for(Index = 0; Index < PinCount; Index++)
1588 {
1589 MMixerIsTopologyPinReserved(Topology, Index, &Reserved);
1590
1591 /* check if it has already been reserved */
1592 if (Reserved)
1593 {
1594 /* pin has already been reserved */
1595 continue;
1596 }
1597
1598 DPRINT("MixerName %S Available PinID %lu\n", MixerData->DeviceName, Index);
1599
1600 /* sanity check */
1601 //ASSERT(MixerData->MixerInfo);
1602
1603 if (!MixerData->MixerInfo)
1604 {
1605 DPRINT1("Expected mixer info\n");
1606 continue;
1607 }
1608
1609 /* build the destination line */
1610 Status = MMixerBuildMixerDestinationLine(MixerContext, MixerData->MixerInfo, MixerData->hDevice, Index, TRUE);
1611 if (Status != MM_STATUS_SUCCESS)
1612 {
1613 /* failed to build destination line */
1614 continue;
1615 }
1616
1617 /* calculate destination line id */
1618 DestinationLineID = (DESTINATION_LINE + MixerData->MixerInfo->MixCaps.cDestinations-1);
1619
1620 /* add mixer controls to destination line */
1621 Status = MMixerAddMixerControlsToDestinationLine(MixerContext, MixerData->MixerInfo, MixerData->hDevice, MixerData->Topology, Index, TRUE, DestinationLineID, &LineTerminator);
1622 if (Status == MM_STATUS_SUCCESS)
1623 {
1624 /* now add the rest of the source lines */
1625 Status = MMixerAddMixerSourceLines(MixerContext, MixerData->MixerInfo, MixerData->hDevice, MixerData->Topology, DestinationLineID, LineTerminator);
1626 }
1627
1628 /* mark pin as consumed */
1629 MMixerSetTopologyPinReserved(Topology, Index);
1630
1631 /* now grab destination line */
1632 DstLine = MMixerGetSourceMixerLineByLineId(MixerData->MixerInfo, DestinationLineID);
1633
1634 /* set type and target as undefined */
1635 DstLine->Line.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_UNDEFINED;
1636 DstLine->Line.Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
1637 DstLine->Line.Target.vDriverVersion = 0;
1638 DstLine->Line.Target.wMid = 0;
1639 DstLine->Line.Target.wPid = 0;
1640 }
1641 }
1642
1643 MIXER_STATUS
1644 MMixerSetupFilter(
1645 IN PMIXER_CONTEXT MixerContext,
1646 IN PMIXER_LIST MixerList,
1647 IN LPMIXER_DATA MixerData,
1648 IN PULONG DeviceCount)
1649 {
1650 MIXER_STATUS Status = MM_STATUS_SUCCESS;
1651 PTOPOLOGY Topology;
1652 ULONG NodeIndex;
1653 LPMIXER_INFO MixerInfo = NULL;
1654
1655 /* check if topology has already been built */
1656 if (MixerData->Topology == NULL)
1657 {
1658 /* build topology */
1659 Status = MMixerBuildTopology(MixerContext, MixerData, &Topology);
1660
1661 if (Status != MM_STATUS_SUCCESS)
1662 {
1663 /* failed to build topology */
1664 return Status;
1665 }
1666
1667 /* store topology */
1668 MixerData->Topology = Topology;
1669 }
1670 else
1671 {
1672 /* re-use topology */
1673 Topology = MixerData->Topology;
1674 }
1675
1676 /* check if the filter has an wave out node */
1677 NodeIndex = MMixerGetNodeIndexFromGuid(Topology, &KSNODETYPE_DAC);
1678 if (NodeIndex != MAXULONG)
1679 {
1680 /* it has */
1681 Status = MMixerInitializeFilter(MixerContext, MixerList, MixerData, NULL, Topology, NodeIndex, FALSE, &MixerInfo);
1682
1683 /* check for success */
1684 if (Status == MM_STATUS_SUCCESS)
1685 {
1686 /* increment mixer count */
1687 (*DeviceCount)++;
1688 }
1689 else
1690 {
1691 /* reset mixer info in case of error */
1692 MixerInfo = NULL;
1693 }
1694 }
1695
1696 /* check if the filter has an wave in node */
1697 NodeIndex = MMixerGetNodeIndexFromGuid(Topology, &KSNODETYPE_ADC);
1698 if (NodeIndex != MAXULONG)
1699 {
1700 /* it has */
1701 Status = MMixerInitializeFilter(MixerContext, MixerList, MixerData, MixerInfo, Topology, NodeIndex, TRUE, &MixerInfo);
1702
1703 /* check for success */
1704 if (Status == MM_STATUS_SUCCESS)
1705 {
1706 /* increment mixer count */
1707 (*DeviceCount)++;
1708 }
1709
1710 }
1711
1712 /* TODO: apply hacks for Wave source line */
1713
1714 /* activate midi devices */
1715 //MMixerInitializeMidiForFilter(MixerContext, MixerList, MixerData, Topology);
1716
1717 /* done */
1718 return Status;
1719 }
1720
1721
1722 MIXER_STATUS
1723 MMixerAddEvent(
1724 IN PMIXER_CONTEXT MixerContext,
1725 IN OUT LPMIXER_INFO MixerInfo,
1726 IN PVOID MixerEventContext,
1727 IN PMIXER_EVENT MixerEventRoutine)
1728 {
1729 //KSE_NODE Property;
1730 PEVENT_NOTIFICATION_ENTRY EventData;
1731 //ULONG BytesReturned;
1732 //MIXER_STATUS Status;
1733
1734 EventData = (PEVENT_NOTIFICATION_ENTRY)MixerContext->AllocEventData(sizeof(EVENT_NOTIFICATION_ENTRY));
1735 if (!EventData)
1736 {
1737 /* not enough memory */
1738 return MM_STATUS_NO_MEMORY;
1739 }
1740
1741 #if 0
1742 /* setup request */
1743 Property.Event.Set = KSEVENTSETID_AudioControlChange;
1744 Property.Event.Flags = KSEVENT_TYPE_TOPOLOGY|KSEVENT_TYPE_ENABLE;
1745 Property.Event.Id = KSEVENT_CONTROL_CHANGE;
1746
1747 Property.NodeId = NodeId;
1748 Property.Reserved = 0;
1749
1750 Status = MixerContext->Control(MixerInfo->hMixer, IOCTL_KS_ENABLE_EVENT, (PVOID)&Property, sizeof(KSP_NODE), (PVOID)EventData, sizeof(KSEVENTDATA), &BytesReturned);
1751 if (Status != MM_STATUS_SUCCESS)
1752 {
1753 /* failed to add event */
1754 MixerContext->FreeEventData(EventData);
1755 return Status;
1756 }
1757 #endif
1758
1759 /* initialize notification entry */
1760 EventData->MixerEventContext = MixerEventContext;
1761 EventData->MixerEventRoutine = MixerEventRoutine;
1762
1763 /* store event */
1764 InsertTailList(&MixerInfo->EventList, &EventData->Entry);
1765 return MM_STATUS_SUCCESS;
1766 }