[AUDIO-BRINGUP]
[reactos.git] / lib / drivers / sound / mmixer / sup.c
index e864d1d..9052e94 100644 (file)
@@ -31,6 +31,11 @@ const GUID KSPROPSETID_General                  = {0x1464EDA5L, 0x6A8F, 0x11D1,
 const GUID KSPROPSETID_Topology                 = {0x720D4AC0L, 0x7533, 0x11D0, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}};
 const GUID KSEVENTSETID_AudioControlChange      = {0xE85E9698L, 0xFA2F, 0x11D1, {0x95, 0xBD, 0x00, 0xC0, 0x4F, 0xB9, 0x25, 0xD3}};
 
+const GUID KSDATAFORMAT_TYPE_MUSIC = {0xE725D360L, 0x62CC, 0x11CF, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}};
+const GUID KSDATAFORMAT_SUBTYPE_MIDI = {0x1D262760L, 0xE957, 0x11CF, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}};
+const GUID KSDATAFORMAT_SPECIFIER_NONE = {0x0F6417D6L, 0xC318, 0x11D0, {0xA4, 0x3F, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
+
+
 MIXER_STATUS
 MMixerVerifyContext(
     IN PMIXER_CONTEXT MixerContext)
@@ -49,18 +54,108 @@ MMixerVerifyContext(
     return MM_STATUS_SUCCESS;
 }
 
+LPMIXERLINE_EXT
+MMixerGetMixerLineContainingNodeId(
+    IN LPMIXER_INFO MixerInfo,
+    IN ULONG NodeID)
+{
+    PLIST_ENTRY Entry, ControlEntry;
+    LPMIXERLINE_EXT MixerLineSrc;
+    LPMIXERCONTROL_EXT MixerControl;
+
+    /* get first entry */
+    Entry = MixerInfo->LineList.Flink;
+
+    while(Entry != &MixerInfo->LineList)
+    {
+        MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry);
+
+        ControlEntry = MixerLineSrc->ControlsList.Flink;
+        while(ControlEntry != &MixerLineSrc->ControlsList)
+        {
+            MixerControl = (LPMIXERCONTROL_EXT)CONTAINING_RECORD(ControlEntry, MIXERCONTROL_EXT, Entry);
+            if (MixerControl->NodeID == NodeID)
+            {
+                return MixerLineSrc;
+            }
+            ControlEntry = ControlEntry->Flink;
+        }
+        Entry = Entry->Flink;
+    }
+
+    return NULL;
+}
+
+VOID
+MMixerGetLowestLogicalTopologyPinOffsetFromArray(
+    IN ULONG LogicalPinArrayCount,
+    IN PULONG LogicalPinArray,
+    OUT PULONG PinOffset)
+{
+    ULONG Index;
+    ULONG LowestId = 0;
+
+    for(Index = 1; Index < LogicalPinArrayCount; Index++)
+    {
+        if (LogicalPinArray[Index] != MAXULONG)
+        {
+            /* sanity check: logical pin id must be unique */
+            ASSERT(LogicalPinArray[Index] != LogicalPinArray[LowestId]);
+        }
+
+        if (LogicalPinArray[Index] < LogicalPinArray[LowestId])
+            LowestId = Index;
+    }
+
+    /* store result */
+    *PinOffset = LowestId;
+}
+
 VOID
 MMixerFreeMixerInfo(
     IN PMIXER_CONTEXT MixerContext,
     IN LPMIXER_INFO MixerInfo)
 {
-    //UNIMPLEMENTED
-    // FIXME
-    // free all lines
+    /* UNIMPLEMENTED
+     * FIXME
+     * free all lines
+     */
 
     MixerContext->Free((PVOID)MixerInfo);
 }
 
+
+LPMIXER_DATA
+MMixerGetMixerDataByDeviceHandle(
+    IN PMIXER_CONTEXT MixerContext,
+    IN HANDLE hDevice)
+{
+    LPMIXER_DATA MixerData;
+    PLIST_ENTRY Entry;
+    PMIXER_LIST MixerList;
+
+    /* get mixer list */
+    MixerList = (PMIXER_LIST)MixerContext->MixerContext;
+
+    if (!MixerList->MixerDataCount)
+        return NULL;
+
+    Entry = MixerList->MixerData.Flink;
+
+    while(Entry != &MixerList->MixerData)
+    {
+        MixerData = (LPMIXER_DATA)CONTAINING_RECORD(Entry, MIXER_DATA, Entry);
+
+        if (MixerData->hDevice == hDevice)
+            return MixerData;
+
+        /* move to next mixer entry */
+        Entry = Entry->Flink;
+    }
+    return NULL;
+}
+
+
 LPMIXER_INFO
 MMixerGetMixerInfoByIndex(
     IN PMIXER_CONTEXT MixerContext,
@@ -71,7 +166,7 @@ MMixerGetMixerInfoByIndex(
     PMIXER_LIST MixerList;
     ULONG Index = 0;
 
-    // get mixer list
+    /* get mixer list */
     MixerList = (PMIXER_LIST)MixerContext->MixerContext;
 
     if (!MixerList->MixerListCount)
@@ -86,7 +181,7 @@ MMixerGetMixerInfoByIndex(
         if (Index == MixerIndex)
             return MixerInfo;
 
-        // move to next mixer entry
+        /* move to next mixer entry */
         Index++;
         Entry = Entry->Flink;
     }
@@ -94,27 +189,31 @@ MMixerGetMixerInfoByIndex(
     return NULL;
 }
 
-LPMIXERCONTROL_DATA
-MMixerGetMixerControlDataById(
-    PLIST_ENTRY ListHead,
-    DWORD dwControlId)
+MIXER_STATUS
+MMixerGetMixerByName(
+    IN PMIXER_LIST MixerList,
+    IN LPWSTR MixerName,
+    OUT LPMIXER_INFO *OutMixerInfo)
 {
+    LPMIXER_INFO MixerInfo;
     PLIST_ENTRY Entry;
-    LPMIXERCONTROL_DATA Control;
 
-    /* get first entry */
-    Entry = ListHead->Flink;
-
-    while(Entry != ListHead)
+    Entry = MixerList->MixerList.Flink;
+    while(Entry != &MixerList->MixerList)
     {
-        Control = (LPMIXERCONTROL_DATA)CONTAINING_RECORD(Entry, MIXERCONTROL_DATA, Entry);
-        DPRINT("dwSource %x dwSource %x\n", Control->dwControlID, dwControlId);
-        if (Control->dwControlID == dwControlId)
-            return Control;
+        MixerInfo = (LPMIXER_INFO)CONTAINING_RECORD(Entry, MIXER_INFO, Entry);
 
+        DPRINT1("MixerName %S MixerName %S\n", MixerInfo->MixCaps.szPname, MixerName);
+        if (wcsicmp(MixerInfo->MixCaps.szPname, MixerName) == 0)
+        {
+            *OutMixerInfo = MixerInfo;
+            return MM_STATUS_SUCCESS;
+        }
+        /* move to next mixer entry */
         Entry = Entry->Flink;
     }
-    return NULL;
+
+    return MM_STATUS_UNSUCCESSFUL;
 }
 
 LPMIXERLINE_EXT
@@ -141,42 +240,6 @@ MMixerGetSourceMixerLineByLineId(
     return NULL;
 }
 
-ULONG
-MMixerGetIndexOfGuid(
-    PKSMULTIPLE_ITEM MultipleItem,
-    LPCGUID NodeType)
-{
-    ULONG Index;
-    LPGUID Guid;
-
-    Guid = (LPGUID)(MultipleItem+1);
-
-    /* iterate through node type array */
-    for(Index = 0; Index < MultipleItem->Count; Index++)
-    {
-        if (IsEqualGUIDAligned(NodeType, Guid))
-        {
-            /* found matching guid */
-            return Index;
-        }
-        Guid++;
-    }
-    return MAXULONG;
-}
-
-PKSTOPOLOGY_CONNECTION
-MMixerGetConnectionByIndex(
-    IN PKSMULTIPLE_ITEM MultipleItem,
-    IN ULONG Index)
-{
-    PKSTOPOLOGY_CONNECTION Descriptor;
-
-    ASSERT(Index < MultipleItem->Count);
-
-    Descriptor = (PKSTOPOLOGY_CONNECTION)(MultipleItem + 1);
-    return &Descriptor[Index];
-}
-
 LPGUID
 MMixerGetNodeType(
     IN PKSMULTIPLE_ITEM MultipleItem,
@@ -190,183 +253,6 @@ MMixerGetNodeType(
     return &NodeType[Index];
 }
 
-MIXER_STATUS
-MMixerGetNodeIndexes(
-    IN PMIXER_CONTEXT MixerContext,
-    IN PKSMULTIPLE_ITEM MultipleItem,
-    IN ULONG NodeIndex,
-    IN ULONG bNode,
-    IN ULONG bFrom,
-    OUT PULONG NodeReferenceCount,
-    OUT PULONG *NodeReference)
-{
-    ULONG Index, Count = 0;
-    PKSTOPOLOGY_CONNECTION Connection;
-    PULONG Refs;
-
-    // KSMULTIPLE_ITEM is followed by several KSTOPOLOGY_CONNECTION
-    Connection = (PKSTOPOLOGY_CONNECTION)(MultipleItem + 1);
-
-    // first count all referenced nodes
-    for(Index = 0; Index < MultipleItem->Count; Index++)
-    {
-        if (bNode)
-        {
-            if (bFrom)
-            {
-                if (Connection->FromNode == NodeIndex)
-                {
-                    // node id has a connection
-                    Count++;
-                }
-            }
-            else
-            {
-                if (Connection->ToNode == NodeIndex)
-                {
-                    // node id has a connection
-                    Count++;
-                }
-            }
-        }
-        else
-        {
-            if (bFrom)
-            {
-                if (Connection->FromNodePin == NodeIndex && Connection->FromNode == KSFILTER_NODE)
-                {
-                    // node id has a connection
-                    Count++;
-                }
-            }
-            else
-            {
-                if (Connection->ToNodePin == NodeIndex && Connection->ToNode == KSFILTER_NODE)
-                {
-                    // node id has a connection
-                    Count++;
-                }
-            }
-        }
-
-
-        // move to next connection
-        Connection++;
-    }
-
-    if (!Count)
-    {
-        *NodeReferenceCount = 0;
-        *NodeReference = NULL;
-        return MM_STATUS_SUCCESS;
-    }
-
-    ASSERT(Count != 0);
-
-    /* now allocate node index array */
-    Refs = (PULONG)MixerContext->Alloc(sizeof(ULONG) * Count);
-    if (!Refs)
-    {
-        // not enough memory
-        return MM_STATUS_NO_MEMORY;
-    }
-
-    Count = 0;
-    Connection = (PKSTOPOLOGY_CONNECTION)(MultipleItem + 1);
-    for(Index = 0; Index < MultipleItem->Count; Index++)
-    {
-        if (bNode)
-        {
-            if (bFrom)
-            {
-                if (Connection->FromNode == NodeIndex)
-                {
-                    /* node id has a connection */
-                    Refs[Count] = Index;
-                    Count++;
-                }
-            }
-            else
-            {
-                if (Connection->ToNode == NodeIndex)
-                {
-                    /* node id has a connection */
-                    Refs[Count] = Index;
-                    Count++;
-                }
-            }
-        }
-        else
-        {
-            if (bFrom)
-            {
-                if (Connection->FromNodePin == NodeIndex && Connection->FromNode == KSFILTER_NODE)
-                {
-                    /* node id has a connection */
-                    Refs[Count] = Index;
-                    Count++;
-                }
-            }
-            else
-            {
-                if (Connection->ToNodePin == NodeIndex && Connection->ToNode == KSFILTER_NODE)
-                {
-                    /* node id has a connection */
-                    Refs[Count] = Index;
-                    Count++;
-                }
-            }
-        }
-
-        /* move to next connection */
-        Connection++;
-    }
-
-    /* store result */
-    *NodeReference = Refs;
-    *NodeReferenceCount = Count;
-
-    return MM_STATUS_SUCCESS;
-}
-
-MIXER_STATUS
-MMixerGetTargetPins(
-    IN PMIXER_CONTEXT MixerContext,
-    IN PKSMULTIPLE_ITEM NodeTypes,
-    IN PKSMULTIPLE_ITEM NodeConnections,
-    IN ULONG NodeIndex,
-    IN ULONG bUpDirection,
-    OUT PULONG Pins,
-    IN ULONG PinCount)
-{
-    ULONG NodeConnectionCount, Index;
-    MIXER_STATUS Status;
-    PULONG NodeConnection;
-
-    // sanity check */
-    ASSERT(NodeIndex != (ULONG)-1);
-
-    /* get all node indexes referenced by that pin */
-    if (bUpDirection)
-        Status = MMixerGetNodeIndexes(MixerContext, NodeConnections, NodeIndex, TRUE, FALSE, &NodeConnectionCount, &NodeConnection);
-    else
-        Status = MMixerGetNodeIndexes(MixerContext, NodeConnections, NodeIndex, TRUE, TRUE, &NodeConnectionCount, &NodeConnection);
-
-    //DPRINT("NodeIndex %u Status %x Count %u\n", NodeIndex, Status, NodeConnectionCount);
-
-    if (Status == MM_STATUS_SUCCESS)
-    {
-        for(Index = 0; Index < NodeConnectionCount; Index++)
-        {
-            Status = MMixerGetTargetPinsByNodeConnectionIndex(MixerContext, NodeConnections, NodeTypes, bUpDirection, NodeConnection[Index], PinCount, Pins);
-            ASSERT(Status == STATUS_SUCCESS);
-        }
-        MixerContext->Free((PVOID)NodeConnection);
-    }
-
-    return Status;
-}
-
 LPMIXERLINE_EXT
 MMixerGetSourceMixerLineByComponentType(
     LPMIXER_INFO MixerInfo,
@@ -394,13 +280,13 @@ MIXER_STATUS
 MMixerGetMixerControlById(
     LPMIXER_INFO MixerInfo,
     DWORD dwControlID,
-    LPMIXERLINE_EXT *MixerLine,
-    LPMIXERCONTROLW *MixerControl,
+    LPMIXERLINE_EXT *OutMixerLine,
+    LPMIXERCONTROL_EXT *OutMixerControl,
     PULONG NodeId)
 {
-    PLIST_ENTRY Entry;
+    PLIST_ENTRY Entry, ControlEntry;
     LPMIXERLINE_EXT MixerLineSrc;
-    ULONG Index;
+    LPMIXERCONTROL_EXT MixerControl;
 
     /* get first entry */
     Entry = MixerInfo->LineList.Flink;
@@ -409,18 +295,21 @@ MMixerGetMixerControlById(
     {
         MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry);
 
-        for(Index = 0; Index < MixerLineSrc->Line.cControls; Index++)
+        ControlEntry = MixerLineSrc->ControlsList.Flink;
+        while(ControlEntry != &MixerLineSrc->ControlsList)
         {
-            if (MixerLineSrc->LineControls[Index].dwControlID == dwControlID)
+            MixerControl = (LPMIXERCONTROL_EXT)CONTAINING_RECORD(ControlEntry, MIXERCONTROL_EXT, Entry);
+            if (MixerControl->Control.dwControlID == dwControlID)
             {
-                if (MixerLine)
-                    *MixerLine = MixerLineSrc;
-                if (MixerControl)
-                    *MixerControl = &MixerLineSrc->LineControls[Index];
+                if (OutMixerLine)
+                    *OutMixerLine = MixerLineSrc;
+                if (OutMixerControl)
+                    *OutMixerControl = MixerControl;
                 if (NodeId)
-                    *NodeId = MixerLineSrc->NodeIds[Index];
+                    *NodeId = MixerControl->NodeID;
                 return MM_STATUS_SUCCESS;
             }
+            ControlEntry = ControlEntry->Flink;
         }
         Entry = Entry->Flink;
     }
@@ -445,11 +334,39 @@ MMixerGetVolumeControlIndex(
     return VolumeData->InputSteppingDelta * (VolumeData->ValuesCount-1);
 }
 
+VOID
+MMixerNotifyControlChange(
+    IN PMIXER_CONTEXT MixerContext,
+    IN LPMIXER_INFO MixerInfo,
+    IN ULONG NotificationType,
+    IN ULONG Value)
+{
+    PLIST_ENTRY Entry;
+    PEVENT_NOTIFICATION_ENTRY NotificationEntry;
+
+    /* enumerate list and perform notification */
+    Entry = MixerInfo->EventList.Flink;
+    while(Entry != &MixerInfo->EventList)
+    {
+        /* get notification entry offset */
+        NotificationEntry = (PEVENT_NOTIFICATION_ENTRY)CONTAINING_RECORD(Entry, EVENT_NOTIFICATION_ENTRY, Entry);
+
+        if (NotificationEntry->MixerEventRoutine)
+        {
+            /* now perform the callback */
+            NotificationEntry->MixerEventRoutine(NotificationEntry->MixerEventContext, (HANDLE)MixerInfo, NotificationType, Value);
+        }
+
+        /* move to next notification entry */
+        Entry = Entry->Flink;
+    }
+}
+
 MIXER_STATUS
 MMixerSetGetMuteControlDetails(
     IN PMIXER_CONTEXT MixerContext,
-    IN HANDLE hMixer,
-    IN ULONG NodeId,
+    IN LPMIXER_INFO MixerInfo,
+    IN LPMIXERCONTROL_EXT MixerControl,
     IN ULONG dwLineID,
     IN LPMIXERCONTROLDETAILS MixerControlDetails,
     IN ULONG bSet)
@@ -469,7 +386,7 @@ MMixerSetGetMuteControlDetails(
         Value = Input->fValue;
 
     /* set control details */
-    Status = MMixerSetGetControlDetails(MixerContext, hMixer, NodeId, bSet, KSPROPERTY_AUDIO_MUTE, 0, &Value);
+    Status = MMixerSetGetControlDetails(MixerContext, MixerControl->hDevice, MixerControl->NodeID, bSet, KSPROPERTY_AUDIO_MUTE, 0, &Value);
 
     if (Status != MM_STATUS_SUCCESS)
         return Status;
@@ -482,19 +399,269 @@ MMixerSetGetMuteControlDetails(
     }
     else
     {
-        // FIXME notify wdmaud clients MM_MIXM_LINE_CHANGE dwLineID
+        /* notify wdmaud clients MM_MIXM_LINE_CHANGE dwLineID */
+        MMixerNotifyControlChange(MixerContext, MixerInfo, MM_MIXM_LINE_CHANGE, dwLineID);
     }
 
     return Status;
 }
 
+MIXER_STATUS
+MMixerSetGetMuxControlDetails(
+    IN PMIXER_CONTEXT MixerContext,
+    IN LPMIXER_INFO MixerInfo,
+    IN ULONG NodeId,
+    IN ULONG bSet,
+    IN ULONG Flags,
+    IN LPMIXERCONTROL_EXT MixerControl,
+    IN LPMIXERCONTROLDETAILS MixerControlDetails,
+    IN LPMIXERLINE_EXT MixerLine)
+{
+    MIXER_STATUS Status;
+    PULONG LogicalNodes, ConnectedNodes;
+    ULONG LogicalNodesCount, ConnectedNodesCount, Index, CurLogicalPinOffset, BytesReturned, OldLogicalPinOffset;
+    LPMIXER_DATA MixerData;
+    LPMIXERCONTROLDETAILS_LISTTEXTW ListText;
+    LPMIXERCONTROLDETAILS_BOOLEAN Values;
+    LPMIXERLINE_EXT SourceLine;
+    KSNODEPROPERTY Request;
+
+    DPRINT("MixerControlDetails %p\n", MixerControlDetails);
+    DPRINT("bSet %lx\n", bSet);
+    DPRINT("Flags %lx\n", Flags);
+    DPRINT("NodeId %lu\n", MixerControl->NodeID);
+    DPRINT("MixerControlDetails dwControlID %lu\n", MixerControlDetails->dwControlID);
+    DPRINT("MixerControlDetails cChannels %lu\n", MixerControlDetails->cChannels);
+    DPRINT("MixerControlDetails cMultipleItems %lu\n", MixerControlDetails->cMultipleItems);
+    DPRINT("MixerControlDetails cbDetails %lu\n", MixerControlDetails->cbDetails);
+    DPRINT("MixerControlDetails paDetails %p\n", MixerControlDetails->paDetails);
+
+    if (MixerControl->Control.fdwControl & MIXERCONTROL_CONTROLF_UNIFORM)
+    {
+        /* control acts uniform */
+        if (MixerControlDetails->cChannels != 1)
+        {
+            /* expected 1 channel */
+            DPRINT1("Expected 1 channel but got %lu\n", MixerControlDetails->cChannels);
+            return MM_STATUS_UNSUCCESSFUL;
+        }
+    }
+
+    /* check if multiple items match */
+    if (MixerControlDetails->cMultipleItems != MixerControl->Control.cMultipleItems)
+    {
+        DPRINT1("MultipleItems mismatch %lu expected %lu\n", MixerControlDetails->cMultipleItems, MixerControl->Control.cMultipleItems);
+        return MM_STATUS_UNSUCCESSFUL;
+    }
+
+    if (bSet)
+    {
+        if ((Flags & MIXER_SETCONTROLDETAILSF_QUERYMASK) == MIXER_SETCONTROLDETAILSF_CUSTOM)
+        {
+            /* tell me when this is hit */
+            ASSERT(FALSE);
+        }
+        else if ((Flags & (MIXER_SETCONTROLDETAILSF_VALUE | MIXER_SETCONTROLDETAILSF_CUSTOM)) == MIXER_SETCONTROLDETAILSF_VALUE)
+        {
+            /* sanity check */
+            ASSERT(bSet == TRUE);
+            ASSERT(MixerControlDetails->cbDetails == sizeof(MIXERCONTROLDETAILS_BOOLEAN));
+
+            Values = (LPMIXERCONTROLDETAILS_BOOLEAN)MixerControlDetails->paDetails;
+            CurLogicalPinOffset = MAXULONG;
+            for(Index = 0; Index < MixerControlDetails->cMultipleItems; Index++)
+            {
+                if (Values[Index].fValue)
+                {
+                    /* mux can only activate one line at a time */
+                    ASSERT(CurLogicalPinOffset == MAXULONG);
+                    CurLogicalPinOffset = Index;
+                }
+            }
+
+            /* setup request */
+            Request.NodeId = NodeId;
+            Request.Reserved = 0;
+            Request.Property.Flags = KSPROPERTY_TYPE_TOPOLOGY | KSPROPERTY_TYPE_GET;
+            Request.Property.Id = KSPROPERTY_AUDIO_MUX_SOURCE;
+            Request.Property.Set = KSPROPSETID_Audio;
+
+            /* perform getting source */
+            Status = MixerContext->Control(MixerControl->hDevice, IOCTL_KS_PROPERTY, (PVOID)&Request, sizeof(KSNODEPROPERTY), &OldLogicalPinOffset, sizeof(ULONG), &BytesReturned);
+            if (Status != MM_STATUS_SUCCESS)
+            {
+                /* failed to get source */
+                return Status;
+            }
+
+            DPRINT("OldLogicalPinOffset %lu CurLogicalPinOffset %lu\n", OldLogicalPinOffset, CurLogicalPinOffset);
+
+            if (OldLogicalPinOffset == CurLogicalPinOffset)
+            {
+                /* cannot be unselected */
+                return MM_STATUS_UNSUCCESSFUL;
+            }
+
+            /* perform setting source */
+            Request.Property.Flags = KSPROPERTY_TYPE_TOPOLOGY | KSPROPERTY_TYPE_SET;
+            Status = MixerContext->Control(MixerControl->hDevice, IOCTL_KS_PROPERTY, (PVOID)&Request, sizeof(KSNODEPROPERTY), &CurLogicalPinOffset, sizeof(ULONG), &BytesReturned);
+            if (Status != MM_STATUS_SUCCESS)
+            {
+                /* failed to set source */
+                return Status;
+            }
+
+            /* notify control change */
+            MMixerNotifyControlChange(MixerContext, MixerInfo, MM_MIXM_CONTROL_CHANGE, MixerControl->Control.dwControlID );
+
+            return Status;
+        }
+    }
+    else
+    {
+        if ((Flags & MIXER_GETCONTROLDETAILSF_QUERYMASK) == MIXER_GETCONTROLDETAILSF_VALUE)
+        {
+            /* setup request */
+            Request.NodeId = NodeId;
+            Request.Reserved = 0;
+            Request.Property.Flags = KSPROPERTY_TYPE_TOPOLOGY | KSPROPERTY_TYPE_GET;
+            Request.Property.Id = KSPROPERTY_AUDIO_MUX_SOURCE;
+            Request.Property.Set = KSPROPSETID_Audio;
+
+            /* perform getting source */
+            Status = MixerContext->Control(MixerControl->hDevice, IOCTL_KS_PROPERTY, (PVOID)&Request, sizeof(KSNODEPROPERTY), &OldLogicalPinOffset, sizeof(ULONG), &BytesReturned);
+            if (Status != MM_STATUS_SUCCESS)
+            {
+                /* failed to get source */
+                return Status;
+            }
+
+            /* gets the corresponding mixer data */
+            MixerData = MMixerGetMixerDataByDeviceHandle(MixerContext, MixerControl->hDevice);
+
+            /* sanity check */
+            ASSERT(MixerData);
+            ASSERT(MixerData->Topology);
+            ASSERT(MixerData->MixerInfo == MixerInfo);
+
+            /* get logical pin nodes */
+            MMixerGetConnectedFromLogicalTopologyPins(MixerData->Topology, MixerControl->NodeID, &LogicalNodesCount, LogicalNodes);
+
+            /* sanity check */
+            ASSERT(LogicalNodesCount == MixerControlDetails->cMultipleItems);
+            ASSERT(LogicalNodesCount == MixerControl->Control.Metrics.dwReserved[0]);
+
+            Values = (LPMIXERCONTROLDETAILS_BOOLEAN)MixerControlDetails->paDetails;
+            for(Index = 0; Index < ConnectedNodesCount; Index++)
+            {
+                /* getting logical pin offset */
+                MMixerGetLowestLogicalTopologyPinOffsetFromArray(LogicalNodesCount, LogicalNodes, &CurLogicalPinOffset);
+
+                if (CurLogicalPinOffset == OldLogicalPinOffset)
+                {
+                    /* mark index as active */
+                    Values[Index].fValue = TRUE;
+                }
+                else
+                {
+                    /* index not active */
+                    Values[Index].fValue = FALSE;
+                }
+
+                /* mark offset as consumed */
+                LogicalNodes[CurLogicalPinOffset] = MAXULONG;
+            }
+
+            /* cleanup */
+            MixerContext->Free(LogicalNodes);
+
+            /* done */
+            return MM_STATUS_SUCCESS;
+        }
+        else if ((Flags & MIXER_GETCONTROLDETAILSF_QUERYMASK) == MIXER_GETCONTROLDETAILSF_LISTTEXT)
+        {
+            /* sanity check */
+            ASSERT(bSet == FALSE);
+
+            /* gets the corresponding mixer data */
+            MixerData = MMixerGetMixerDataByDeviceHandle(MixerContext, MixerControl->hDevice);
+
+            /* sanity check */
+            ASSERT(MixerData);
+            ASSERT(MixerData->Topology);
+            ASSERT(MixerData->MixerInfo == MixerInfo);
+
+            /* now allocate logical pin array */
+            Status = MMixerAllocateTopologyNodeArray(MixerContext, MixerData->Topology, &LogicalNodes);
+            if (Status != MM_STATUS_SUCCESS)
+            {
+                /* no memory */
+                return MM_STATUS_NO_MEMORY;
+            }
+
+            /* allocate connected node array */
+            Status = MMixerAllocateTopologyNodeArray(MixerContext, MixerData->Topology, &ConnectedNodes);
+            if (Status != MM_STATUS_SUCCESS)
+            {
+                /* no memory */
+                MixerContext->Free(LogicalNodes);
+                return MM_STATUS_NO_MEMORY;
+            }
+
+            /* get logical pin nodes */
+            MMixerGetConnectedFromLogicalTopologyPins(MixerData->Topology, MixerControl->NodeID, &LogicalNodesCount, LogicalNodes);
+
+            /* get connected nodes */
+            MMixerGetNextNodesFromNodeIndex(MixerContext, MixerData->Topology, MixerControl->NodeID, TRUE, &ConnectedNodesCount, ConnectedNodes);
+
+            /* sanity check */
+            ASSERT(ConnectedNodesCount == LogicalNodesCount);
+            ASSERT(ConnectedNodesCount == MixerControlDetails->cMultipleItems);
+            ASSERT(ConnectedNodesCount == MixerControl->Control.Metrics.dwReserved[0]);
+
+            ListText = (LPMIXERCONTROLDETAILS_LISTTEXTW)MixerControlDetails->paDetails;
+
+            for(Index = 0; Index < ConnectedNodesCount; Index++)
+            {
+                /* getting logical pin offset */
+                MMixerGetLowestLogicalTopologyPinOffsetFromArray(LogicalNodesCount, LogicalNodes, &CurLogicalPinOffset);
+
+                /* get mixer line with that node */
+                SourceLine = MMixerGetMixerLineContainingNodeId(MixerInfo, ConnectedNodes[CurLogicalPinOffset]);
+
+                /* sanity check */
+                ASSERT(SourceLine);
+
+                DPRINT1("PinOffset %lu LogicalPin %lu NodeId %lu LineName %S\n", CurLogicalPinOffset, LogicalNodes[CurLogicalPinOffset], ConnectedNodes[CurLogicalPinOffset], SourceLine->Line.szName);
+
+                /* copy details */
+                ListText[Index].dwParam1 = SourceLine->Line.dwLineID;
+                ListText[Index].dwParam2 = SourceLine->Line.dwComponentType;
+                MixerContext->Copy(ListText[Index].szName, SourceLine->Line.szName, (wcslen(SourceLine->Line.szName) + 1) * sizeof(WCHAR));
+
+                /* mark offset as consumed */
+                LogicalNodes[CurLogicalPinOffset] = MAXULONG;
+            }
+
+            /* cleanup */
+            MixerContext->Free(LogicalNodes);
+            MixerContext->Free(ConnectedNodes);
+
+            /* done */
+            return MM_STATUS_SUCCESS;
+        }
+    }
+
+    return MM_STATUS_NOT_IMPLEMENTED;
+}
+
 MIXER_STATUS
 MMixerSetGetVolumeControlDetails(
     IN PMIXER_CONTEXT MixerContext,
-    IN HANDLE hMixer,
+    IN LPMIXER_INFO MixerInfo,
     IN ULONG NodeId,
     IN ULONG bSet,
-    LPMIXERCONTROLW MixerControl,
+    LPMIXERCONTROL_EXT MixerControl,
     IN LPMIXERCONTROLDETAILS MixerControlDetails,
     LPMIXERLINE_EXT MixerLine)
 {
@@ -507,7 +674,7 @@ MMixerSetGetVolumeControlDetails(
     if (MixerControlDetails->cbDetails != sizeof(MIXERCONTROLDETAILS_SIGNED))
         return MM_STATUS_INVALID_PARAMETER;
 
-    VolumeData = (LPMIXERVOLUME_DATA)MMixerGetMixerControlDataById(&MixerLine->LineControlsExtraData, MixerControl->dwControlID);
+    VolumeData = (LPMIXERVOLUME_DATA)MixerControl->ExtraData;
     if (!VolumeData)
         return MM_STATUS_UNSUCCESSFUL;
 
@@ -523,7 +690,6 @@ MMixerSetGetVolumeControlDetails(
         if (Index >= VolumeData->ValuesCount)
         {
             DPRINT1("Index %u out of bounds %u \n", Index, VolumeData->ValuesCount);
-            DbgBreakPoint();
             return MM_STATUS_INVALID_PARAMETER;
         }
 
@@ -534,12 +700,12 @@ MMixerSetGetVolumeControlDetails(
     if (bSet)
     {
         /* TODO */
-        Status = MMixerSetGetControlDetails(MixerContext, hMixer, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, 0, &Value);
-        Status = MMixerSetGetControlDetails(MixerContext, hMixer, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, 1, &Value);
+        Status = MMixerSetGetControlDetails(MixerContext, MixerControl->hDevice, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, 0, &Value);
+        Status = MMixerSetGetControlDetails(MixerContext, MixerControl->hDevice, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, 1, &Value);
     }
     else
     {
-        Status = MMixerSetGetControlDetails(MixerContext, hMixer, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, Channel, &Value);
+        Status = MMixerSetGetControlDetails(MixerContext, MixerControl->hDevice, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, Channel, &Value);
     }
 
     if (!bSet)
@@ -551,6 +717,7 @@ MMixerSetGetVolumeControlDetails(
     else
     {
         /* notify clients of a line change  MM_MIXM_CONTROL_CHANGE with MixerControl->dwControlID */
+        MMixerNotifyControlChange(MixerContext, MixerInfo, MM_MIXM_CONTROL_CHANGE, MixerControl->Control.dwControlID);
     }
     return Status;
 }
@@ -590,7 +757,7 @@ MMixerGetDataByDeviceName(
         MixerData = (LPMIXER_DATA)CONTAINING_RECORD(Entry, MIXER_DATA, Entry);
         if (wcsicmp(&DeviceName[2], &MixerData->DeviceName[2]) == 0)
         {
-            // found entry
+            /* found entry */
             return MixerData;
         }
         Entry = Entry->Flink;
@@ -617,6 +784,7 @@ MMixerCreateMixerData(
     MixerData->DeviceName = DeviceName;
     MixerData->hDevice = hDevice;
     MixerData->hDeviceInterfaceKey = hKey;
+    MixerData->Topology = NULL;
 
     InsertTailList(&MixerList->MixerData, &MixerData->Entry);
     MixerList->MixerDataCount++;
@@ -626,7 +794,7 @@ MMixerCreateMixerData(
 MIXER_STATUS
 MMixerGetDeviceName(
     IN PMIXER_CONTEXT MixerContext,
-    IN LPMIXER_INFO MixerInfo,
+    OUT LPWSTR DeviceName,
     IN HANDLE hKey)
 {
     LPWSTR Name;
@@ -638,16 +806,16 @@ MMixerGetDeviceName(
     Status = MixerContext->QueryKeyValue(hKey, L"FriendlyName", (PVOID*)&Name, &Length, &Type);
     if (Status == MM_STATUS_SUCCESS)
     {
-        // copy device name
-        MixerContext->Copy(MixerInfo->MixCaps.szPname, Name, min(wcslen(Name), MAXPNAMELEN-1) * sizeof(WCHAR));
+        /* copy device name */
+        MixerContext->Copy(DeviceName, Name, min(wcslen(Name), MAXPNAMELEN-1) * sizeof(WCHAR));
 
-        // make sure its null terminated
-        MixerInfo->MixCaps.szPname[MAXPNAMELEN-1] = L'\0';
+        /* make sure its null terminated */
+        DeviceName[MAXPNAMELEN-1] = L'\0';
 
-        // free device name
+        /* free device name */
         MixerContext->Free(Name);
 
-        // done
+        /* done */
         return Status;
     }
 
@@ -655,19 +823,37 @@ MMixerGetDeviceName(
     if (Status != MM_STATUS_SUCCESS)
         return Status;
 
-    Status = MixerContext->QueryKeyValue(hKey, L"FriendlyName", (PVOID*)&Name, &Length, &Type);
+    Status = MixerContext->QueryKeyValue(hTemp, L"FriendlyName", (PVOID*)&Name, &Length, &Type);
     if (Status == MM_STATUS_SUCCESS)
     {
-        // copy device name
-        MixerContext->Copy(MixerInfo->MixCaps.szPname, Name, min(wcslen(Name), MAXPNAMELEN-1) * sizeof(WCHAR));
+        /* copy device name */
+        MixerContext->Copy(DeviceName, Name, min(wcslen(Name), MAXPNAMELEN-1) * sizeof(WCHAR));
 
-        // make sure its null terminated
-        MixerInfo->MixCaps.szPname[MAXPNAMELEN-1] = L'\0';
+        /* make sure its null terminated */
+        DeviceName[MAXPNAMELEN-1] = L'\0';
 
-        // free device name
+        /* free device name */
         MixerContext->Free(Name);
     }
 
     MixerContext->CloseKey(hTemp);
     return Status;
 }
+
+VOID
+MMixerInitializePinConnect(
+    IN OUT PKSPIN_CONNECT PinConnect,
+    IN ULONG PinId)
+{
+    PinConnect->Interface.Set = KSINTERFACESETID_Standard;
+    PinConnect->Interface.Id = KSINTERFACE_STANDARD_STREAMING;
+    PinConnect->Interface.Flags = 0;
+    PinConnect->Medium.Set = KSMEDIUMSETID_Standard;
+    PinConnect->Medium.Id = KSMEDIUM_TYPE_ANYINSTANCE;
+    PinConnect->Medium.Flags = 0;
+    PinConnect->PinToHandle = NULL;
+    PinConnect->PinId = PinId;
+    PinConnect->Priority.PriorityClass = KSPRIORITY_NORMAL;
+    PinConnect->Priority.PrioritySubClass = 1;
+}
+