xref: /reactos/sdk/lib/drivers/sound/mmixer/sup.c (revision 019f21ee)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS Kernel Streaming
4  * FILE:            lib/drivers/sound/mmixer/sup.c
5  * PURPOSE:         Mixer Support Functions
6  * PROGRAMMER:      Johannes Anderwald
7  */
8 
9 #include "precomp.h"
10 
11 #define YDEBUG
12 #include <debug.h>
13 
14 const GUID KSNODETYPE_SUM = {0xDA441A60L, 0xC556, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
15 const GUID KSNODETYPE_DAC = {0x507AE360L, 0xC554, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
16 const GUID KSNODETYPE_ADC = {0x4D837FE0L, 0xC555, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
17 const GUID KSNODETYPE_AGC = {0xE88C9BA0L, 0xC557, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
18 const GUID KSNODETYPE_LOUDNESS = {0x41887440L, 0xC558, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
19 const GUID KSNODETYPE_MUTE =     {0x02B223C0L, 0xC557, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
20 const GUID KSNODETYPE_TONE =     {0x7607E580L, 0xC557, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
21 const GUID KSNODETYPE_VOLUME =   {0x3A5ACC00L, 0xC557, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
22 const GUID KSNODETYPE_PEAKMETER = {0xa085651e, 0x5f0d, 0x4b36, {0xa8, 0x69, 0xd1, 0x95, 0xd6, 0xab, 0x4b, 0x9e}};
23 const GUID KSNODETYPE_MUX =       {0x2CEAF780, 0xC556, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
24 const GUID KSNODETYPE_STEREO_WIDE = {0xA9E69800L, 0xC558, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
25 const GUID KSNODETYPE_CHORUS =      {0x20173F20L, 0xC559, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
26 const GUID KSNODETYPE_REVERB =      {0xEF0328E0L, 0xC558, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
27 const GUID KSNODETYPE_SUPERMIX =    {0xE573ADC0L, 0xC555, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
28 
29 const GUID KSPROPSETID_Audio = {0x45FFAAA0L, 0x6E1B, 0x11D0, {0xBC, 0xF2, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}};
30 const GUID KSPROPSETID_Pin                     = {0x8C134960L, 0x51AD, 0x11CF, {0x87, 0x8A, 0x94, 0xF8, 0x01, 0xC1, 0x00, 0x00}};
31 const GUID KSPROPSETID_General                  = {0x1464EDA5L, 0x6A8F, 0x11D1, {0x9A, 0xA7, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
32 const GUID KSPROPSETID_Topology                 = {0x720D4AC0L, 0x7533, 0x11D0, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}};
33 const GUID KSEVENTSETID_AudioControlChange      = {0xE85E9698L, 0xFA2F, 0x11D1, {0x95, 0xBD, 0x00, 0xC0, 0x4F, 0xB9, 0x25, 0xD3}};
34 
35 const GUID KSDATAFORMAT_TYPE_MUSIC = {0xE725D360L, 0x62CC, 0x11CF, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}};
36 const GUID KSDATAFORMAT_SUBTYPE_MIDI = {0x1D262760L, 0xE957, 0x11CF, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}};
37 const GUID KSDATAFORMAT_SPECIFIER_NONE = {0x0F6417D6L, 0xC318, 0x11D0, {0xA4, 0x3F, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
38 
39 
40 MIXER_STATUS
41 MMixerVerifyContext(
42     IN PMIXER_CONTEXT MixerContext)
43 {
44     if (MixerContext->SizeOfStruct != sizeof(MIXER_CONTEXT))
45         return MM_STATUS_INVALID_PARAMETER;
46 
47     if (!MixerContext->Alloc || !MixerContext->Control || !MixerContext->Free || !MixerContext->Open ||
48         !MixerContext->AllocEventData || !MixerContext->FreeEventData ||
49         !MixerContext->Close || !MixerContext->OpenKey || !MixerContext->QueryKeyValue || !MixerContext->CloseKey)
50         return MM_STATUS_INVALID_PARAMETER;
51 
52     if (!MixerContext->MixerContext)
53         return MM_STATUS_INVALID_PARAMETER;
54 
55     return MM_STATUS_SUCCESS;
56 }
57 
58 LPMIXERLINE_EXT
59 MMixerGetMixerLineContainingNodeId(
60     IN LPMIXER_INFO MixerInfo,
61     IN ULONG NodeID)
62 {
63     PLIST_ENTRY Entry, ControlEntry;
64     LPMIXERLINE_EXT MixerLineSrc;
65     LPMIXERCONTROL_EXT MixerControl;
66 
67     /* get first entry */
68     Entry = MixerInfo->LineList.Flink;
69 
70     while(Entry != &MixerInfo->LineList)
71     {
72         MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry);
73 
74         ControlEntry = MixerLineSrc->ControlsList.Flink;
75         while(ControlEntry != &MixerLineSrc->ControlsList)
76         {
77             MixerControl = (LPMIXERCONTROL_EXT)CONTAINING_RECORD(ControlEntry, MIXERCONTROL_EXT, Entry);
78             if (MixerControl->NodeID == NodeID)
79             {
80                 return MixerLineSrc;
81             }
82             ControlEntry = ControlEntry->Flink;
83         }
84         Entry = Entry->Flink;
85     }
86 
87     return NULL;
88 }
89 
90 VOID
91 MMixerGetLowestLogicalTopologyPinOffsetFromArray(
92     IN ULONG LogicalPinArrayCount,
93     IN PULONG LogicalPinArray,
94     OUT PULONG PinOffset)
95 {
96     ULONG Index;
97     ULONG LowestId = 0;
98 
99     for(Index = 1; Index < LogicalPinArrayCount; Index++)
100     {
101         if (LogicalPinArray[Index] != MAXULONG)
102         {
103             /* sanity check: logical pin id must be unique */
104             ASSERT(LogicalPinArray[Index] != LogicalPinArray[LowestId]);
105         }
106 
107         if (LogicalPinArray[Index] < LogicalPinArray[LowestId])
108             LowestId = Index;
109     }
110 
111     /* store result */
112     *PinOffset = LowestId;
113 }
114 
115 VOID
116 MMixerFreeMixerInfo(
117     IN PMIXER_CONTEXT MixerContext,
118     IN LPMIXER_INFO MixerInfo)
119 {
120     /* UNIMPLEMENTED;
121      * FIXME
122      * free all lines
123      */
124 
125     MixerContext->Free((PVOID)MixerInfo);
126 }
127 
128 
129 LPMIXER_DATA
130 MMixerGetMixerDataByDeviceHandle(
131     IN PMIXER_CONTEXT MixerContext,
132     IN HANDLE hDevice)
133 {
134     LPMIXER_DATA MixerData;
135     PLIST_ENTRY Entry;
136     PMIXER_LIST MixerList;
137 
138     /* get mixer list */
139     MixerList = (PMIXER_LIST)MixerContext->MixerContext;
140 
141     if (!MixerList->MixerDataCount)
142         return NULL;
143 
144     Entry = MixerList->MixerData.Flink;
145 
146     while(Entry != &MixerList->MixerData)
147     {
148         MixerData = (LPMIXER_DATA)CONTAINING_RECORD(Entry, MIXER_DATA, Entry);
149 
150         if (MixerData->hDevice == hDevice)
151             return MixerData;
152 
153         /* move to next mixer entry */
154         Entry = Entry->Flink;
155     }
156     return NULL;
157 }
158 
159 
160 LPMIXER_INFO
161 MMixerGetMixerInfoByIndex(
162     IN PMIXER_CONTEXT MixerContext,
163     IN ULONG MixerIndex)
164 {
165     LPMIXER_INFO MixerInfo;
166     PLIST_ENTRY Entry;
167     PMIXER_LIST MixerList;
168     ULONG Index = 0;
169 
170     /* get mixer list */
171     MixerList = (PMIXER_LIST)MixerContext->MixerContext;
172 
173     if (!MixerList->MixerListCount)
174         return NULL;
175 
176     Entry = MixerList->MixerList.Flink;
177 
178     while(Entry != &MixerList->MixerList)
179     {
180         MixerInfo = (LPMIXER_INFO)CONTAINING_RECORD(Entry, MIXER_INFO, Entry);
181 
182         if (Index == MixerIndex)
183             return MixerInfo;
184 
185         /* move to next mixer entry */
186         Index++;
187         Entry = Entry->Flink;
188     }
189 
190     return NULL;
191 }
192 
193 MIXER_STATUS
194 MMixerGetMixerByName(
195     IN PMIXER_LIST MixerList,
196     IN LPWSTR MixerName,
197     OUT LPMIXER_INFO *OutMixerInfo)
198 {
199     LPMIXER_INFO MixerInfo;
200     PLIST_ENTRY Entry;
201 
202     Entry = MixerList->MixerList.Flink;
203     while(Entry != &MixerList->MixerList)
204     {
205         MixerInfo = (LPMIXER_INFO)CONTAINING_RECORD(Entry, MIXER_INFO, Entry);
206 
207         DPRINT1("MixerName %S MixerName %S\n", MixerInfo->MixCaps.szPname, MixerName);
208         if (wcsicmp(MixerInfo->MixCaps.szPname, MixerName) == 0)
209         {
210             *OutMixerInfo = MixerInfo;
211             return MM_STATUS_SUCCESS;
212         }
213         /* move to next mixer entry */
214         Entry = Entry->Flink;
215     }
216 
217     return MM_STATUS_UNSUCCESSFUL;
218 }
219 
220 LPMIXERLINE_EXT
221 MMixerGetSourceMixerLineByLineId(
222     LPMIXER_INFO MixerInfo,
223     DWORD dwLineID)
224 {
225     PLIST_ENTRY Entry;
226     LPMIXERLINE_EXT MixerLineSrc;
227 
228     /* get first entry */
229     Entry = MixerInfo->LineList.Flink;
230 
231     while(Entry != &MixerInfo->LineList)
232     {
233         MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry);
234         DPRINT("dwLineID %x dwLineID %x MixerLineSrc %p\n", MixerLineSrc->Line.dwLineID, dwLineID, MixerLineSrc);
235         if (MixerLineSrc->Line.dwLineID == dwLineID)
236             return MixerLineSrc;
237 
238         Entry = Entry->Flink;
239     }
240 
241     return NULL;
242 }
243 
244 LPGUID
245 MMixerGetNodeType(
246     IN PKSMULTIPLE_ITEM MultipleItem,
247     IN ULONG Index)
248 {
249     LPGUID NodeType;
250 
251     ASSERT(Index < MultipleItem->Count);
252 
253     NodeType = (LPGUID)(MultipleItem + 1);
254     return &NodeType[Index];
255 }
256 
257 LPMIXERLINE_EXT
258 MMixerGetSourceMixerLineByComponentType(
259     LPMIXER_INFO MixerInfo,
260     DWORD dwComponentType)
261 {
262     PLIST_ENTRY Entry;
263     LPMIXERLINE_EXT MixerLineSrc;
264 
265     /* get first entry */
266     Entry = MixerInfo->LineList.Flink;
267 
268     while(Entry != &MixerInfo->LineList)
269     {
270         MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry);
271         if (MixerLineSrc->Line.dwComponentType == dwComponentType)
272             return MixerLineSrc;
273 
274         Entry = Entry->Flink;
275     }
276 
277     return NULL;
278 }
279 
280 MIXER_STATUS
281 MMixerGetMixerControlById(
282     LPMIXER_INFO MixerInfo,
283     DWORD dwControlID,
284     LPMIXERLINE_EXT *OutMixerLine,
285     LPMIXERCONTROL_EXT *OutMixerControl,
286     PULONG NodeId)
287 {
288     PLIST_ENTRY Entry, ControlEntry;
289     LPMIXERLINE_EXT MixerLineSrc;
290     LPMIXERCONTROL_EXT MixerControl;
291 
292     /* get first entry */
293     Entry = MixerInfo->LineList.Flink;
294 
295     while(Entry != &MixerInfo->LineList)
296     {
297         MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry);
298 
299         ControlEntry = MixerLineSrc->ControlsList.Flink;
300         while(ControlEntry != &MixerLineSrc->ControlsList)
301         {
302             MixerControl = (LPMIXERCONTROL_EXT)CONTAINING_RECORD(ControlEntry, MIXERCONTROL_EXT, Entry);
303             if (MixerControl->Control.dwControlID == dwControlID)
304             {
305                 if (OutMixerLine)
306                     *OutMixerLine = MixerLineSrc;
307                 if (OutMixerControl)
308                     *OutMixerControl = MixerControl;
309                 if (NodeId)
310                     *NodeId = MixerControl->NodeID;
311                 return MM_STATUS_SUCCESS;
312             }
313             ControlEntry = ControlEntry->Flink;
314         }
315         Entry = Entry->Flink;
316     }
317 
318     return MM_STATUS_UNSUCCESSFUL;
319 }
320 
321 ULONG
322 MMixerGetVolumeControlIndex(
323     LPMIXERVOLUME_DATA VolumeData,
324     LONG Value)
325 {
326     ULONG Index;
327 
328     for(Index = 0; Index < VolumeData->ValuesCount; Index++)
329     {
330         if (VolumeData->Values[Index] > Value)
331         {
332             return VolumeData->InputSteppingDelta * Index;
333         }
334     }
335     return VolumeData->InputSteppingDelta * (VolumeData->ValuesCount-1);
336 }
337 
338 VOID
339 MMixerNotifyControlChange(
340     IN PMIXER_CONTEXT MixerContext,
341     IN LPMIXER_INFO MixerInfo,
342     IN ULONG NotificationType,
343     IN ULONG Value)
344 {
345     PLIST_ENTRY Entry;
346     PEVENT_NOTIFICATION_ENTRY NotificationEntry;
347 
348     /* enumerate list and perform notification */
349     Entry = MixerInfo->EventList.Flink;
350     while(Entry != &MixerInfo->EventList)
351     {
352         /* get notification entry offset */
353         NotificationEntry = (PEVENT_NOTIFICATION_ENTRY)CONTAINING_RECORD(Entry, EVENT_NOTIFICATION_ENTRY, Entry);
354 
355         if (NotificationEntry->MixerEventRoutine)
356         {
357             /* now perform the callback */
358             NotificationEntry->MixerEventRoutine(NotificationEntry->MixerEventContext, (HANDLE)MixerInfo, NotificationType, Value);
359         }
360 
361         /* move to next notification entry */
362         Entry = Entry->Flink;
363     }
364 }
365 
366 MIXER_STATUS
367 MMixerSetGetMuteControlDetails(
368     IN PMIXER_CONTEXT MixerContext,
369     IN LPMIXER_INFO MixerInfo,
370     IN LPMIXERCONTROL_EXT MixerControl,
371     IN ULONG dwLineID,
372     IN LPMIXERCONTROLDETAILS MixerControlDetails,
373     IN ULONG bSet)
374 {
375     LPMIXERCONTROLDETAILS_BOOLEAN Input;
376     LONG Value;
377     MIXER_STATUS Status;
378 
379     if (MixerControlDetails->cbDetails != sizeof(MIXERCONTROLDETAILS_BOOLEAN))
380         return MM_STATUS_INVALID_PARAMETER;
381 
382     /* get input */
383     Input = (LPMIXERCONTROLDETAILS_BOOLEAN)MixerControlDetails->paDetails;
384 
385     /* FIXME SEH */
386     if (bSet)
387         Value = Input->fValue;
388 
389     /* set control details */
390     Status = MMixerSetGetControlDetails(MixerContext, MixerControl->hDevice, MixerControl->NodeID, bSet, KSPROPERTY_AUDIO_MUTE, 0, &Value);
391 
392     if (Status != MM_STATUS_SUCCESS)
393         return Status;
394 
395     /* FIXME SEH */
396     if (!bSet)
397     {
398         Input->fValue = Value;
399         return Status;
400     }
401     else
402     {
403         /* notify wdmaud clients MM_MIXM_LINE_CHANGE dwLineID */
404         MMixerNotifyControlChange(MixerContext, MixerInfo, MM_MIXM_LINE_CHANGE, dwLineID);
405     }
406 
407     return Status;
408 }
409 
410 MIXER_STATUS
411 MMixerSetGetMuxControlDetails(
412     IN PMIXER_CONTEXT MixerContext,
413     IN LPMIXER_INFO MixerInfo,
414     IN ULONG NodeId,
415     IN ULONG bSet,
416     IN ULONG Flags,
417     IN LPMIXERCONTROL_EXT MixerControl,
418     IN LPMIXERCONTROLDETAILS MixerControlDetails,
419     IN LPMIXERLINE_EXT MixerLine)
420 {
421     MIXER_STATUS Status;
422     PULONG LogicalNodes, ConnectedNodes;
423     ULONG LogicalNodesCount, ConnectedNodesCount, Index, CurLogicalPinOffset, BytesReturned, OldLogicalPinOffset;
424     LPMIXER_DATA MixerData;
425     LPMIXERCONTROLDETAILS_LISTTEXTW ListText;
426     LPMIXERCONTROLDETAILS_BOOLEAN Values;
427     LPMIXERLINE_EXT SourceLine;
428     KSNODEPROPERTY Request;
429 
430     DPRINT("MixerControlDetails %p\n", MixerControlDetails);
431     DPRINT("bSet %lx\n", bSet);
432     DPRINT("Flags %lx\n", Flags);
433     DPRINT("NodeId %lu\n", MixerControl->NodeID);
434     DPRINT("MixerControlDetails dwControlID %lu\n", MixerControlDetails->dwControlID);
435     DPRINT("MixerControlDetails cChannels %lu\n", MixerControlDetails->cChannels);
436     DPRINT("MixerControlDetails cMultipleItems %lu\n", MixerControlDetails->cMultipleItems);
437     DPRINT("MixerControlDetails cbDetails %lu\n", MixerControlDetails->cbDetails);
438     DPRINT("MixerControlDetails paDetails %p\n", MixerControlDetails->paDetails);
439 
440     if (MixerControl->Control.fdwControl & MIXERCONTROL_CONTROLF_UNIFORM)
441     {
442         /* control acts uniform */
443         if (MixerControlDetails->cChannels != 1)
444         {
445             /* expected 1 channel */
446             DPRINT1("Expected 1 channel but got %lu\n", MixerControlDetails->cChannels);
447             return MM_STATUS_UNSUCCESSFUL;
448         }
449     }
450 
451     /* check if multiple items match */
452     if (MixerControlDetails->cMultipleItems != MixerControl->Control.cMultipleItems)
453     {
454         DPRINT1("MultipleItems mismatch %lu expected %lu\n", MixerControlDetails->cMultipleItems, MixerControl->Control.cMultipleItems);
455         return MM_STATUS_UNSUCCESSFUL;
456     }
457 
458     if (bSet)
459     {
460         if ((Flags & MIXER_SETCONTROLDETAILSF_QUERYMASK) == MIXER_SETCONTROLDETAILSF_CUSTOM)
461         {
462             /* tell me when this is hit */
463             ASSERT(FALSE);
464         }
465         else if ((Flags & (MIXER_SETCONTROLDETAILSF_VALUE | MIXER_SETCONTROLDETAILSF_CUSTOM)) == MIXER_SETCONTROLDETAILSF_VALUE)
466         {
467             /* sanity check */
468             ASSERT(bSet == TRUE);
469             ASSERT(MixerControlDetails->cbDetails == sizeof(MIXERCONTROLDETAILS_BOOLEAN));
470 
471             Values = (LPMIXERCONTROLDETAILS_BOOLEAN)MixerControlDetails->paDetails;
472             CurLogicalPinOffset = MAXULONG;
473             for(Index = 0; Index < MixerControlDetails->cMultipleItems; Index++)
474             {
475                 if (Values[Index].fValue)
476                 {
477                     /* mux can only activate one line at a time */
478                     ASSERT(CurLogicalPinOffset == MAXULONG);
479                     CurLogicalPinOffset = Index;
480                 }
481             }
482 
483             /* setup request */
484             Request.NodeId = NodeId;
485             Request.Reserved = 0;
486             Request.Property.Flags = KSPROPERTY_TYPE_TOPOLOGY | KSPROPERTY_TYPE_GET;
487             Request.Property.Id = KSPROPERTY_AUDIO_MUX_SOURCE;
488             Request.Property.Set = KSPROPSETID_Audio;
489 
490             /* perform getting source */
491             Status = MixerContext->Control(MixerControl->hDevice, IOCTL_KS_PROPERTY, (PVOID)&Request, sizeof(KSNODEPROPERTY), &OldLogicalPinOffset, sizeof(ULONG), &BytesReturned);
492             if (Status != MM_STATUS_SUCCESS)
493             {
494                 /* failed to get source */
495                 return Status;
496             }
497 
498             DPRINT("OldLogicalPinOffset %lu CurLogicalPinOffset %lu\n", OldLogicalPinOffset, CurLogicalPinOffset);
499 
500             if (OldLogicalPinOffset == CurLogicalPinOffset)
501             {
502                 /* cannot be unselected */
503                 return MM_STATUS_UNSUCCESSFUL;
504             }
505 
506             /* perform setting source */
507             Request.Property.Flags = KSPROPERTY_TYPE_TOPOLOGY | KSPROPERTY_TYPE_SET;
508             Status = MixerContext->Control(MixerControl->hDevice, IOCTL_KS_PROPERTY, (PVOID)&Request, sizeof(KSNODEPROPERTY), &CurLogicalPinOffset, sizeof(ULONG), &BytesReturned);
509             if (Status != MM_STATUS_SUCCESS)
510             {
511                 /* failed to set source */
512                 return Status;
513             }
514 
515             /* notify control change */
516             MMixerNotifyControlChange(MixerContext, MixerInfo, MM_MIXM_CONTROL_CHANGE, MixerControl->Control.dwControlID );
517 
518             return Status;
519         }
520     }
521     else
522     {
523         if ((Flags & MIXER_GETCONTROLDETAILSF_QUERYMASK) == MIXER_GETCONTROLDETAILSF_VALUE)
524         {
525             /* setup request */
526             Request.NodeId = NodeId;
527             Request.Reserved = 0;
528             Request.Property.Flags = KSPROPERTY_TYPE_TOPOLOGY | KSPROPERTY_TYPE_GET;
529             Request.Property.Id = KSPROPERTY_AUDIO_MUX_SOURCE;
530             Request.Property.Set = KSPROPSETID_Audio;
531 
532             /* perform getting source */
533             Status = MixerContext->Control(MixerControl->hDevice, IOCTL_KS_PROPERTY, (PVOID)&Request, sizeof(KSNODEPROPERTY), &OldLogicalPinOffset, sizeof(ULONG), &BytesReturned);
534             if (Status != MM_STATUS_SUCCESS)
535             {
536                 /* failed to get source */
537                 return Status;
538             }
539 
540             /* gets the corresponding mixer data */
541             MixerData = MMixerGetMixerDataByDeviceHandle(MixerContext, MixerControl->hDevice);
542 
543             /* sanity check */
544             ASSERT(MixerData);
545             ASSERT(MixerData->Topology);
546             ASSERT(MixerData->MixerInfo == MixerInfo);
547 
548             /* now allocate logical pin array */
549             Status = MMixerAllocateTopologyNodeArray(MixerContext, MixerData->Topology, &LogicalNodes);
550             if (Status != MM_STATUS_SUCCESS)
551             {
552                 /* no memory */
553                 return MM_STATUS_NO_MEMORY;
554             }
555 
556             /* get logical pin nodes */
557             MMixerGetConnectedFromLogicalTopologyPins(MixerData->Topology, MixerControl->NodeID, &LogicalNodesCount, LogicalNodes);
558 
559             /* sanity check */
560             ASSERT(LogicalNodesCount == MixerControlDetails->cMultipleItems);
561             ASSERT(LogicalNodesCount == MixerControl->Control.Metrics.dwReserved[0]);
562 
563             Values = (LPMIXERCONTROLDETAILS_BOOLEAN)MixerControlDetails->paDetails;
564             for(Index = 0; Index < LogicalNodesCount; Index++)
565             {
566                 /* getting logical pin offset */
567                 MMixerGetLowestLogicalTopologyPinOffsetFromArray(LogicalNodesCount, LogicalNodes, &CurLogicalPinOffset);
568 
569                 if (CurLogicalPinOffset == OldLogicalPinOffset)
570                 {
571                     /* mark index as active */
572                     Values[Index].fValue = TRUE;
573                 }
574                 else
575                 {
576                     /* index not active */
577                     Values[Index].fValue = FALSE;
578                 }
579 
580                 /* mark offset as consumed */
581                 LogicalNodes[CurLogicalPinOffset] = MAXULONG;
582             }
583 
584             /* cleanup */
585             MixerContext->Free(LogicalNodes);
586 
587             /* done */
588             return MM_STATUS_SUCCESS;
589         }
590         else if ((Flags & MIXER_GETCONTROLDETAILSF_QUERYMASK) == MIXER_GETCONTROLDETAILSF_LISTTEXT)
591         {
592             /* sanity check */
593             ASSERT(bSet == FALSE);
594 
595             /* gets the corresponding mixer data */
596             MixerData = MMixerGetMixerDataByDeviceHandle(MixerContext, MixerControl->hDevice);
597 
598             /* sanity check */
599             ASSERT(MixerData);
600             ASSERT(MixerData->Topology);
601             ASSERT(MixerData->MixerInfo == MixerInfo);
602 
603             /* now allocate logical pin array */
604             Status = MMixerAllocateTopologyNodeArray(MixerContext, MixerData->Topology, &LogicalNodes);
605             if (Status != MM_STATUS_SUCCESS)
606             {
607                 /* no memory */
608                 return MM_STATUS_NO_MEMORY;
609             }
610 
611             /* allocate connected node array */
612             Status = MMixerAllocateTopologyNodeArray(MixerContext, MixerData->Topology, &ConnectedNodes);
613             if (Status != MM_STATUS_SUCCESS)
614             {
615                 /* no memory */
616                 MixerContext->Free(LogicalNodes);
617                 return MM_STATUS_NO_MEMORY;
618             }
619 
620             /* get logical pin nodes */
621             MMixerGetConnectedFromLogicalTopologyPins(MixerData->Topology, MixerControl->NodeID, &LogicalNodesCount, LogicalNodes);
622 
623             /* get connected nodes */
624             MMixerGetNextNodesFromNodeIndex(MixerContext, MixerData->Topology, MixerControl->NodeID, TRUE, &ConnectedNodesCount, ConnectedNodes);
625 
626             /* sanity check */
627             ASSERT(ConnectedNodesCount == LogicalNodesCount);
628             ASSERT(ConnectedNodesCount == MixerControlDetails->cMultipleItems);
629             ASSERT(ConnectedNodesCount == MixerControl->Control.Metrics.dwReserved[0]);
630 
631             ListText = (LPMIXERCONTROLDETAILS_LISTTEXTW)MixerControlDetails->paDetails;
632 
633             for(Index = 0; Index < ConnectedNodesCount; Index++)
634             {
635                 /* getting logical pin offset */
636                 MMixerGetLowestLogicalTopologyPinOffsetFromArray(LogicalNodesCount, LogicalNodes, &CurLogicalPinOffset);
637 
638                 /* get mixer line with that node */
639                 SourceLine = MMixerGetMixerLineContainingNodeId(MixerInfo, ConnectedNodes[CurLogicalPinOffset]);
640 
641                 /* sanity check */
642                 ASSERT(SourceLine);
643 
644                 DPRINT1("PinOffset %lu LogicalPin %lu NodeId %lu LineName %S\n", CurLogicalPinOffset, LogicalNodes[CurLogicalPinOffset], ConnectedNodes[CurLogicalPinOffset], SourceLine->Line.szName);
645 
646                 /* copy details */
647                 ListText[Index].dwParam1 = SourceLine->Line.dwLineID;
648                 ListText[Index].dwParam2 = SourceLine->Line.dwComponentType;
649                 MixerContext->Copy(ListText[Index].szName, SourceLine->Line.szName, (wcslen(SourceLine->Line.szName) + 1) * sizeof(WCHAR));
650 
651                 /* mark offset as consumed */
652                 LogicalNodes[CurLogicalPinOffset] = MAXULONG;
653             }
654 
655             /* cleanup */
656             MixerContext->Free(LogicalNodes);
657             MixerContext->Free(ConnectedNodes);
658 
659             /* done */
660             return MM_STATUS_SUCCESS;
661         }
662     }
663 
664     return MM_STATUS_NOT_IMPLEMENTED;
665 }
666 
667 MIXER_STATUS
668 MMixerSetGetVolumeControlDetails(
669     IN PMIXER_CONTEXT MixerContext,
670     IN LPMIXER_INFO MixerInfo,
671     IN ULONG NodeId,
672     IN ULONG bSet,
673     LPMIXERCONTROL_EXT MixerControl,
674     IN LPMIXERCONTROLDETAILS MixerControlDetails,
675     LPMIXERLINE_EXT MixerLine)
676 {
677     LPMIXERCONTROLDETAILS_UNSIGNED Input;
678     LONG Value;
679     ULONG Index, Channel = 0;
680     ULONG dwValue;
681     MIXER_STATUS Status;
682     LPMIXERVOLUME_DATA VolumeData;
683 
684     if (MixerControlDetails->cbDetails != sizeof(MIXERCONTROLDETAILS_SIGNED))
685         return MM_STATUS_INVALID_PARAMETER;
686 
687     VolumeData = (LPMIXERVOLUME_DATA)MixerControl->ExtraData;
688     if (!VolumeData)
689         return MM_STATUS_UNSUCCESSFUL;
690 
691     /* get input */
692     Input = (LPMIXERCONTROLDETAILS_UNSIGNED)MixerControlDetails->paDetails;
693 
694     if (bSet)
695     {
696         /* FIXME SEH */
697         Value = Input->dwValue;
698         Index = Value / VolumeData->InputSteppingDelta;
699 
700         if (Index >= VolumeData->ValuesCount)
701         {
702             DPRINT1("Index %u out of bounds %u \n", Index, VolumeData->ValuesCount);
703             return MM_STATUS_INVALID_PARAMETER;
704         }
705 
706         Value = VolumeData->Values[Index];
707     }
708 
709     /* set control details */
710     if (bSet)
711     {
712         /* TODO */
713         Status = MMixerSetGetControlDetails(MixerContext, MixerControl->hDevice, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, 0, &Value);
714         Status = MMixerSetGetControlDetails(MixerContext, MixerControl->hDevice, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, 1, &Value);
715     }
716     else
717     {
718         Status = MMixerSetGetControlDetails(MixerContext, MixerControl->hDevice, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, Channel, &Value);
719     }
720 
721     if (!bSet)
722     {
723         dwValue = MMixerGetVolumeControlIndex(VolumeData, (LONG)Value);
724         /* FIXME SEH */
725         Input->dwValue = dwValue;
726     }
727     else
728     {
729         /* notify clients of a line change  MM_MIXM_CONTROL_CHANGE with MixerControl->dwControlID */
730         MMixerNotifyControlChange(MixerContext, MixerInfo, MM_MIXM_CONTROL_CHANGE, MixerControl->Control.dwControlID);
731     }
732     return Status;
733 }
734 
735 LPMIXER_DATA
736 MMixerGetDataByDeviceId(
737     IN PMIXER_LIST MixerList,
738     IN ULONG DeviceId)
739 {
740     PLIST_ENTRY Entry;
741     LPMIXER_DATA MixerData;
742 
743     Entry = MixerList->MixerData.Flink;
744     while(Entry != &MixerList->MixerData)
745     {
746         MixerData = (LPMIXER_DATA)CONTAINING_RECORD(Entry, MIXER_DATA, Entry);
747         if (MixerData->DeviceId == DeviceId)
748         {
749             return MixerData;
750         }
751         Entry = Entry->Flink;
752     }
753     return NULL;
754 }
755 
756 LPMIXER_DATA
757 MMixerGetDataByDeviceName(
758     IN PMIXER_LIST MixerList,
759     IN LPWSTR DeviceName)
760 {
761     PLIST_ENTRY Entry;
762     LPMIXER_DATA MixerData;
763 
764     Entry = MixerList->MixerData.Flink;
765     while(Entry != &MixerList->MixerData)
766     {
767         MixerData = (LPMIXER_DATA)CONTAINING_RECORD(Entry, MIXER_DATA, Entry);
768         if (wcsicmp(&DeviceName[2], &MixerData->DeviceName[2]) == 0)
769         {
770             /* found entry */
771             return MixerData;
772         }
773         Entry = Entry->Flink;
774     }
775     return NULL;
776 }
777 
778 MIXER_STATUS
779 MMixerCreateMixerData(
780     IN PMIXER_CONTEXT MixerContext,
781     IN PMIXER_LIST MixerList,
782     IN ULONG DeviceId,
783     IN LPWSTR DeviceName,
784     IN HANDLE hDevice,
785     IN HANDLE hKey)
786 {
787     LPMIXER_DATA MixerData;
788 
789     MixerData = (LPMIXER_DATA)MixerContext->Alloc(sizeof(MIXER_DATA));
790     if (!MixerData)
791         return MM_STATUS_NO_MEMORY;
792 
793     MixerData->DeviceId = DeviceId;
794     MixerData->DeviceName = DeviceName;
795     MixerData->hDevice = hDevice;
796     MixerData->hDeviceInterfaceKey = hKey;
797     MixerData->Topology = NULL;
798 
799     InsertTailList(&MixerList->MixerData, &MixerData->Entry);
800     MixerList->MixerDataCount++;
801     return MM_STATUS_SUCCESS;
802 }
803 
804 MIXER_STATUS
805 MMixerGetDeviceNameWithComponentId(
806     IN PMIXER_CONTEXT MixerContext,
807     IN HANDLE hMixer,
808     OUT LPWSTR OutDeviceName)
809 {
810     MIXER_STATUS Status;
811     KSPROPERTY Property;
812     KSCOMPONENTID ComponentId;
813     ULONG Length;
814     UNICODE_STRING GuidString;
815     ULONG ResultLength, KeyType;
816     HANDLE hMediaKey, hGuidKey;
817     LPWSTR DeviceName;
818 
819     /* prepare property */
820     Property.Flags = KSPROPERTY_TYPE_GET;
821     Property.Set = KSPROPSETID_General;
822     Property.Id = KSPROPERTY_GENERAL_COMPONENTID;
823 
824     /* try get component id */
825     Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Property, sizeof(KSPROPERTY), &ComponentId, sizeof(KSCOMPONENTID), &Length);
826 
827     if (Status == MM_STATUS_SUCCESS)
828     {
829         Status = MixerContext->OpenKey(NULL, L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\MediaCategories", KEY_READ, &hMediaKey);
830         if (Status == MM_STATUS_SUCCESS)
831         {
832             RtlStringFromGUID(&ComponentId.Name, &GuidString);
833             Status = MixerContext->OpenKey(hMediaKey, GuidString.Buffer, KEY_READ, &hGuidKey);
834             RtlFreeUnicodeString(&GuidString);
835             if (Status == MM_STATUS_SUCCESS)
836             {
837                 Status = MixerContext->QueryKeyValue(hGuidKey, L"Name", (PVOID*)&DeviceName, &ResultLength, &KeyType);
838                 if (Status == MM_STATUS_SUCCESS)
839                 {
840                     MixerContext->Copy(OutDeviceName, DeviceName, min(ResultLength, (MAXPNAMELEN-1)*2));
841                     MixerContext->Free(DeviceName);
842                 }
843 
844                 MixerContext->CloseKey(hGuidKey);
845             }
846             MixerContext->CloseKey(hMediaKey);
847         }
848     }
849     return Status;
850 }
851 
852 MIXER_STATUS
853 MMixerGetDeviceName(
854     IN PMIXER_CONTEXT MixerContext,
855     OUT LPWSTR DeviceName,
856     IN HANDLE hKey)
857 {
858     LPWSTR Name;
859     HANDLE hTemp;
860     ULONG Length;
861     ULONG Type;
862     MIXER_STATUS Status;
863 
864     Status = MixerContext->QueryKeyValue(hKey, L"FriendlyName", (PVOID*)&Name, &Length, &Type);
865     if (Status == MM_STATUS_SUCCESS)
866     {
867         /* copy device name */
868         MixerContext->Copy(DeviceName, Name, min(wcslen(Name), MAXPNAMELEN-1) * sizeof(WCHAR));
869 
870         /* make sure its null terminated */
871         DeviceName[MAXPNAMELEN-1] = L'\0';
872 
873         /* free device name */
874         MixerContext->Free(Name);
875 
876         /* done */
877         return Status;
878     }
879 
880     Status = MixerContext->OpenKey(hKey, L"Device Parameters", KEY_READ, &hTemp);
881     if (Status != MM_STATUS_SUCCESS)
882         return Status;
883 
884     Status = MixerContext->QueryKeyValue(hTemp, L"FriendlyName", (PVOID*)&Name, &Length, &Type);
885     if (Status == MM_STATUS_SUCCESS)
886     {
887         /* copy device name */
888         MixerContext->Copy(DeviceName, Name, min(wcslen(Name), MAXPNAMELEN-1) * sizeof(WCHAR));
889 
890         /* make sure its null terminated */
891         DeviceName[MAXPNAMELEN-1] = L'\0';
892 
893         /* free device name */
894         MixerContext->Free(Name);
895     }
896 
897     MixerContext->CloseKey(hTemp);
898     return Status;
899 }
900 
901 VOID
902 MMixerInitializePinConnect(
903     IN OUT PKSPIN_CONNECT PinConnect,
904     IN ULONG PinId)
905 {
906     PinConnect->Interface.Set = KSINTERFACESETID_Standard;
907     PinConnect->Interface.Id = KSINTERFACE_STANDARD_STREAMING;
908     PinConnect->Interface.Flags = 0;
909     PinConnect->Medium.Set = KSMEDIUMSETID_Standard;
910     PinConnect->Medium.Id = KSMEDIUM_TYPE_ANYINSTANCE;
911     PinConnect->Medium.Flags = 0;
912     PinConnect->PinToHandle = NULL;
913     PinConnect->PinId = PinId;
914     PinConnect->Priority.PriorityClass = KSPRIORITY_NORMAL;
915     PinConnect->Priority.PrioritySubClass = 1;
916 }
917