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