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