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