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