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