[AUDIO-BRINGUP]
[reactos.git] / lib / drivers / sound / mmixer / controls.c
index 5b5dd18..dfa2b53 100644 (file)
@@ -8,6 +8,26 @@
  
 #include "priv.h"
 
+const GUID KSNODETYPE_DESKTOP_MICROPHONE = {0xDFF21BE2, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
+const GUID KSNODETYPE_LEGACY_AUDIO_CONNECTOR = {0xDFF21FE4, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
+const GUID KSNODETYPE_TELEPHONE = {0xDFF21EE2, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
+const GUID KSNODETYPE_PHONE_LINE = {0xDFF21EE1, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
+const GUID KSNODETYPE_DOWN_LINE_PHONE = {0xDFF21EE3, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
+const GUID KSNODETYPE_DESKTOP_SPEAKER = {0xDFF21CE4, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
+const GUID KSNODETYPE_ROOM_SPEAKER = {0xDFF21CE5, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
+const GUID KSNODETYPE_COMMUNICATION_SPEAKER = {0xDFF21CE6, 0xF70F, 0x11D0, {0xB9,0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
+const GUID KSNODETYPE_HEADPHONES = {0xDFF21CE2, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
+const GUID KSNODETYPE_HEAD_MOUNTED_DISPLAY_AUDIO = {0xDFF21CE3, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
+const GUID KSNODETYPE_MICROPHONE = {0xDFF21BE1, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9,0x22, 0x31, 0x96}};
+const GUID KSCATEGORY_AUDIO = {0x6994AD04L, 0x93EF, 0x11D0, {0xA3, 0xCC, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
+const GUID KSNODETYPE_SPDIF_INTERFACE = {0xDFF21FE5, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
+const GUID KSNODETYPE_ANALOG_CONNECTOR = {0xDFF21FE1, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
+const GUID KSNODETYPE_SPEAKER = {0xDFF21CE1, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
+const GUID KSNODETYPE_CD_PLAYER = {0xDFF220E3, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
+const GUID KSNODETYPE_SYNTHESIZER = {0xDFF220F3, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
+const GUID KSNODETYPE_LINE_CONNECTOR = {0xDFF21FE3, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0,0xC9, 0x22, 0x31, 0x96}};
+const GUID PINNAME_VIDEO_CAPTURE  = {0xfb6c4281, 0x353, 0x11d1, {0x90, 0x5f, 0x0, 0x0, 0xc0, 0xcc, 0x16, 0xba}};
+
 MIXER_STATUS
 MMixerAddMixerControl(
     IN PMIXER_CONTEXT MixerContext,
@@ -33,19 +53,7 @@ MMixerAddMixerControl(
     MixerControl->dwControlType = MMixerGetControlTypeFromTopologyNode(NodeType);
 
     MixerControl->fdwControl = MIXERCONTROL_CONTROLF_UNIFORM; /* FIXME */
-    MixerControl->cMultipleItems = 0; /* FIXME */
-
-    if (MixerControl->dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE)
-    {
-        MixerControl->Bounds.dwMinimum = 0;
-        MixerControl->Bounds.dwMaximum = 1;
-    }
-    else if (MixerControl->dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)
-    {
-        MixerControl->Bounds.dwMinimum = 0;
-        MixerControl->Bounds.dwMaximum = 0xFFFF;
-        MixerControl->Metrics.cSteps = 0xC0; /* FIXME */
-    }
+    MixerControl->cMultipleItems = 0;
 
     /* setup request to retrieve name */
     Node.NodeId = NodeIndex;
@@ -84,28 +92,46 @@ MMixerAddMixerControl(
     }
 
     MixerInfo->ControlId++;
-#if 0
+
     if (MixerControl->dwControlType == MIXERCONTROL_CONTROLTYPE_MUX)
     {
-        KSNODEPROPERTY Property;
-        ULONG PinId = 2;
+        ULONG NodesCount;
+        PULONG Nodes;
 
-        /* setup the request */
-        RtlZeroMemory(&Property, sizeof(KSNODEPROPERTY));
+        /* allocate topology nodes array */
+        Status = MMixerAllocateTopologyNodeArray(MixerContext, Topology, &Nodes);
+
+        if (Status != MM_STATUS_SUCCESS)
+        {
+            /* out of memory */
+            return STATUS_NO_MEMORY;
+        }
 
-        Property.NodeId = NodeIndex;
-        Property.Property.Id = KSPROPERTY_AUDIO_MUX_SOURCE;
-        Property.Property.Flags = KSPROPERTY_TYPE_SET;
-        Property.Property.Set = KSPROPSETID_Audio;
+        /* get connected node count */
+        MMixerGetNextNodesFromNodeIndex(MixerContext, Topology, NodeIndex, TRUE, &NodesCount, Nodes);
 
-        /* get node volume level info */
-        Status = MixerContext->Control(hDevice, IOCTL_KS_PROPERTY, (PVOID)&Property, sizeof(KSNODEPROPERTY), (PVOID)&PinId, sizeof(ULONG), &BytesReturned);
+        /* TODO */
+        MixerContext->Free(Nodes);
 
-        DPRINT1("Status %x NodeIndex %u PinId %u\n", Status, NodeIndex, PinId);
-        //DbgBreakPoint();
-    }else
-#endif
-    if (MixerControl->dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)
+        /* setup mux bounds */
+        MixerControl->Bounds.dwMinimum = 0;
+        MixerControl->Bounds.dwMaximum = NodesCount - 1;
+        MixerControl->Metrics.dwReserved[0] = NodesCount;
+        MixerControl->cMultipleItems = NodesCount;
+        MixerControl->fdwControl |= MIXERCONTROL_CONTROLF_MULTIPLE;
+    }
+    else if (MixerControl->dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE)
+    {
+        MixerControl->Bounds.dwMinimum = 0;
+        MixerControl->Bounds.dwMaximum = 1;
+    }
+    else if (MixerControl->dwControlType == MIXERCONTROL_CONTROLTYPE_ONOFF)
+    {
+        /* only needs to set bounds */
+        MixerControl->Bounds.dwMinimum = 0;
+        MixerControl->Bounds.dwMaximum = 1;
+    }
+    else if (MixerControl->dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)
     {
         KSNODEPROPERTY_AUDIO_CHANNEL Property;
         ULONG Length;
@@ -113,6 +139,10 @@ MMixerAddMixerControl(
         PKSPROPERTY_MEMBERSHEADER Members;
         PKSPROPERTY_STEPPING_LONG Range;
 
+        MixerControl->Bounds.dwMinimum = 0;
+        MixerControl->Bounds.dwMaximum = 0xFFFF;
+        MixerControl->Metrics.cSteps = 0xC0; /* FIXME */
+
         Length = sizeof(KSPROPERTY_DESCRIPTION) + sizeof(KSPROPERTY_MEMBERSHEADER) + sizeof(KSPROPERTY_STEPPING_LONG);
         Desc = (PKSPROPERTY_DESCRIPTION)MixerContext->Alloc(Length);
         ASSERT(Desc);
@@ -122,7 +152,7 @@ MMixerAddMixerControl(
 
         Property.NodeProperty.NodeId = NodeIndex;
         Property.NodeProperty.Property.Id = KSPROPERTY_AUDIO_VOLUMELEVEL;
-        Property.NodeProperty.Property.Flags = KSPROPERTY_TYPE_BASICSUPPORT;
+        Property.NodeProperty.Property.Flags = KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_TOPOLOGY;
         Property.NodeProperty.Property.Set = KSPROPSETID_Audio;
 
         /* get node volume level info */
@@ -202,9 +232,10 @@ MMixerCreateDestinationLine(
     /* initialize mixer destination line */
     DestinationLine->Line.cbStruct = sizeof(MIXERLINEW);
     DestinationLine->Line.dwSource = MAXULONG;
-    DestinationLine->Line.dwLineID = DESTINATION_LINE;
+    DestinationLine->Line.dwLineID = MixerInfo->MixCaps.cDestinations + DESTINATION_LINE;
     DestinationLine->Line.fdwLine = MIXERLINE_LINEF_ACTIVE;
     DestinationLine->Line.dwUser = 0;
+    DestinationLine->Line.dwDestination = MixerInfo->MixCaps.cDestinations;
     DestinationLine->Line.dwComponentType = (bInputMixer == 0 ? MIXERLINE_COMPONENTTYPE_DST_SPEAKERS : MIXERLINE_COMPONENTTYPE_DST_WAVEIN);
     DestinationLine->Line.cChannels = 2; /* FIXME */
 
@@ -219,7 +250,7 @@ MMixerCreateDestinationLine(
     }
 
     DestinationLine->Line.Target.dwType = (bInputMixer == 0 ? MIXERLINE_TARGETTYPE_WAVEOUT : MIXERLINE_TARGETTYPE_WAVEIN);
-    DestinationLine->Line.Target.dwDeviceID = !bInputMixer;
+    DestinationLine->Line.Target.dwDeviceID = 0; //FIXME
     DestinationLine->Line.Target.wMid = MixerInfo->MixCaps.wMid;
     DestinationLine->Line.Target.wPid = MixerInfo->MixCaps.wPid;
     DestinationLine->Line.Target.vDriverVersion = MixerInfo->MixCaps.vDriverVersion;
@@ -231,7 +262,10 @@ MMixerCreateDestinationLine(
     InitializeListHead(&DestinationLine->LineControlsExtraData);
 
     /* insert into mixer info */
-    InsertHeadList(&MixerInfo->LineList, &DestinationLine->Entry);
+    InsertTailList(&MixerInfo->LineList, &DestinationLine->Entry);
+
+    /* increment destination count */
+    MixerInfo->MixCaps.cDestinations++;
 
     /* done */
     return MM_STATUS_SUCCESS;
@@ -385,6 +419,7 @@ MMixerCountMixerControls(
     IN PMIXER_CONTEXT MixerContext,
     IN PTOPOLOGY Topology,
     IN ULONG PinId,
+    IN ULONG bInputMixer,
     IN ULONG bUpStream,
     OUT PULONG OutNodesCount,
     OUT PULONG OutNodes,
@@ -424,6 +459,12 @@ MMixerCountMixerControls(
         if (bTerminator)
         {
             /* found terminator */
+            if (bInputMixer)
+            {
+                /* add mux source for source destination line */
+                OutNodes[Count] = NodeIndex;
+                Count++;
+            }
             break;
         }
 
@@ -458,6 +499,132 @@ MMixerCountMixerControls(
     return MM_STATUS_SUCCESS;
 }
 
+MIXER_STATUS
+MMixerGetChannelCountEnhanced(
+    IN PMIXER_CONTEXT MixerContext,
+    IN LPMIXER_INFO MixerInfo,
+    IN ULONG NodeId,
+    OUT PULONG MaxChannels)
+{
+    KSPROPERTY_DESCRIPTION Description;
+    PKSPROPERTY_DESCRIPTION NewDescription;
+    PKSPROPERTY_MEMBERSHEADER Header;
+    ULONG BytesReturned;
+    KSP_NODE Request;
+    MIXER_STATUS Status;
+
+    /* try #1 obtain it via description */
+    Request.NodeId = NodeId;
+    Request.Reserved = 0;
+    Request.Property.Set = KSPROPSETID_Audio;
+    Request.Property.Flags = KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_TOPOLOGY;
+    Request.Property.Id = KSPROPERTY_AUDIO_VOLUMELEVEL;
+
+
+    /* get description */
+    Status = MixerContext->Control(MixerInfo->hMixer, IOCTL_KS_PROPERTY, (PVOID)&Request, sizeof(KSP_NODE), (PVOID)&Description, sizeof(KSPROPERTY_DESCRIPTION), &BytesReturned);
+    if (Status == MM_STATUS_SUCCESS)
+    {
+        if (Description.DescriptionSize >= sizeof(KSPROPERTY_DESCRIPTION) + sizeof(KSPROPERTY_MEMBERSHEADER) && (Description.MembersListCount > 0))
+        {
+            /* allocate new description */
+            NewDescription = MixerContext->Alloc(Description.DescriptionSize);
+
+            if (!NewDescription)
+            {
+                /* not enough memory */
+                return MM_STATUS_NO_MEMORY;
+            }
+
+            /* get description */
+            Status = MixerContext->Control(MixerInfo->hMixer, IOCTL_KS_PROPERTY, (PVOID)&Request, sizeof(KSP_NODE), (PVOID)NewDescription, Description.DescriptionSize, &BytesReturned);
+            if (Status == MM_STATUS_SUCCESS)
+            {
+                /* get header */
+                Header = (PKSPROPERTY_MEMBERSHEADER)(NewDescription + 1);
+
+                if (Header->Flags & KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_MULTICHANNEL)
+                {
+                    /* found enhanced flag */
+                    ASSERT(Header->MembersCount > 1);
+
+                    /* store channel count */
+                    *MaxChannels = Header->MembersCount;
+
+                    /* free description */
+                    MixerContext->Free(NewDescription);
+
+                    /* done */
+                    return MM_STATUS_SUCCESS;
+                }
+            }
+
+            /* free description */
+            MixerContext->Free(NewDescription);
+        }
+    }
+
+    /* failed to get channel count enhanced */
+    return MM_STATUS_UNSUCCESSFUL;
+}
+
+VOID
+MMixerGetChannelCountLegacy(
+    IN PMIXER_CONTEXT MixerContext,
+    IN LPMIXER_INFO MixerInfo,
+    IN ULONG NodeId,
+    OUT PULONG MaxChannels)
+{
+    ULONG BytesReturned;
+    MIXER_STATUS Status;
+    KSNODEPROPERTY_AUDIO_CHANNEL Channel;
+    LONG Volume;
+
+    /* setup request */
+    Channel.Reserved = 0;
+    Channel.NodeProperty.NodeId = NodeId;
+    Channel.NodeProperty.Reserved = 0;
+    Channel.NodeProperty.Property.Flags = KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_TOPOLOGY;
+    Channel.NodeProperty.Property.Set = KSPROPSETID_Audio;
+    Channel.Channel = 0;
+    Channel.NodeProperty.Property.Id = KSPROPERTY_AUDIO_VOLUMELEVEL;
+
+    do
+    {
+        /* get channel volume */
+        Status = MixerContext->Control(MixerInfo->hMixer, IOCTL_KS_PROPERTY, (PVOID)&Channel, sizeof(KSNODEPROPERTY_AUDIO_CHANNEL), (PVOID)&Volume, sizeof(LONG), &BytesReturned);
+        if (Status != MM_STATUS_SUCCESS)
+            break;
+
+        /* increment channel count */
+        Channel.Channel++;
+
+    }while(TRUE);
+
+    /* store channel count */
+    *MaxChannels = Channel.Channel;
+
+}
+
+VOID
+MMixerGetMaxChannelsForNode(
+    IN PMIXER_CONTEXT MixerContext,
+    IN LPMIXER_INFO MixerInfo,
+    IN ULONG NodeId,
+    OUT PULONG MaxChannels)
+{
+    MIXER_STATUS Status;
+
+    /* try to get it enhanced */
+    Status = MMixerGetChannelCountEnhanced(MixerContext, MixerInfo, NodeId, MaxChannels);
+
+    if (Status != MM_STATUS_SUCCESS)
+    {
+        /* get it old-fashioned way */
+        MMixerGetChannelCountLegacy(MixerContext, MixerInfo, NodeId, MaxChannels);
+    }
+}
+
 MIXER_STATUS
 MMixerAddMixerControlsToMixerLineByNodeIndexArray(
     IN PMIXER_CONTEXT MixerContext,
@@ -469,6 +636,8 @@ MMixerAddMixerControlsToMixerLineByNodeIndexArray(
 {
     ULONG Index, Count, bReserved;
     MIXER_STATUS Status;
+    LPGUID NodeType;
+    ULONG MaxChannels;
 
     /* store nodes array */
     DstLine->NodeIds = Nodes;
@@ -489,16 +658,30 @@ MMixerAddMixerControlsToMixerLineByNodeIndexArray(
     {
         /* check if the node has already been reserved to a line */
         MMixerIsTopologyNodeReserved(Topology, Nodes[Index], &bReserved);
-
+#if 0 /* MS lies */
         if (bReserved)
         {
             /* node is already used, skip it */
             continue;
         }
-
+#endif
         /* set node status as used */
         MMixerSetTopologyNodeReserved(Topology, Nodes[Index]);
 
+        /* query node type */
+        NodeType = MMixerGetNodeTypeFromTopology(Topology, Nodes[Index]);
+
+        if (IsEqualGUIDAligned(NodeType, &KSNODETYPE_VOLUME))
+        {
+            /* calculate maximum channel count for node */
+            MMixerGetMaxChannelsForNode(MixerContext, MixerInfo, Nodes[Index], &MaxChannels);
+
+            DPRINT("NodeId %lu MaxChannels %lu Line %S Id %lu\n", Nodes[Index], MaxChannels, DstLine->Line.szName, DstLine->Line.dwLineID);
+            /* calculate maximum channels */
+            DstLine->Line.cChannels = min(DstLine->Line.cChannels, MaxChannels);
+        }
+
+
         /* now add the mixer control */
         Status = MMixerAddMixerControl(MixerContext, MixerInfo, Topology, Nodes[Index], DstLine, &DstLine->LineControls[Count]);
 
@@ -516,6 +699,204 @@ MMixerAddMixerControlsToMixerLineByNodeIndexArray(
     return MM_STATUS_SUCCESS;
 }
 
+MIXER_STATUS
+MMixerGetComponentAndTargetType(
+    IN PMIXER_CONTEXT MixerContext,
+    IN OUT LPMIXER_INFO MixerInfo,
+    IN ULONG PinId,
+    OUT PULONG ComponentType,
+    OUT PULONG TargetType)
+{
+    KSPIN_DATAFLOW DataFlow;
+    KSPIN_COMMUNICATION Communication;
+    MIXER_STATUS Status;
+    KSP_PIN Request;
+    ULONG BytesReturned;
+    GUID Guid;
+    BOOLEAN BridgePin = FALSE;
+    PKSPIN_PHYSICALCONNECTION Connection;
+
+    /* first dataflow type */
+    Status = MMixerGetPinDataFlowAndCommunication(MixerContext, MixerInfo->hMixer, PinId, &DataFlow, &Communication);
+
+    if (Status != MM_STATUS_SUCCESS)
+    {
+        /* failed to get dataflow */
+        return Status;
+    }
+
+    /* now get pin category guid */
+    Request.PinId = PinId;
+    Request.Reserved = 0;
+    Request.Property.Flags = KSPROPERTY_TYPE_GET;
+    Request.Property.Set = KSPROPSETID_Pin;
+    Request.Property.Id = KSPROPERTY_PIN_CATEGORY;
+
+
+    /* get pin category */
+    Status = MixerContext->Control(MixerInfo->hMixer, IOCTL_KS_PROPERTY, (PVOID)&Request, sizeof(KSP_PIN), &Guid, sizeof(GUID), &BytesReturned);
+    if (Status != MM_STATUS_SUCCESS)
+    {
+        /* failed to get dataflow */
+        return Status;
+    }
+
+    /* check if it has a physical connection */
+    Status = MMixerGetPhysicalConnection(MixerContext, MixerInfo->hMixer, PinId, &Connection);
+    if (Status == MM_STATUS_SUCCESS)
+    {
+        /* pin is a brige pin */
+        BridgePin = TRUE;
+
+        /* free physical connection */
+        MixerContext->Free(Connection);
+    }
+
+    if (DataFlow == KSPIN_DATAFLOW_IN)
+    {
+        if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_MICROPHONE) ||
+            IsEqualGUIDAligned(&Guid, &KSNODETYPE_DESKTOP_MICROPHONE))
+        {
+            /* type microphone */
+            *TargetType = MIXERLINE_TARGETTYPE_WAVEIN;
+            *ComponentType = MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE;
+        }
+        else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_LEGACY_AUDIO_CONNECTOR) ||
+                 IsEqualGUIDAligned(&Guid, &KSCATEGORY_AUDIO) ||
+                 IsEqualGUIDAligned(&Guid, &KSNODETYPE_SPEAKER))
+        {
+            /* type waveout */
+            *TargetType = MIXERLINE_TARGETTYPE_WAVEOUT;
+            *ComponentType = MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT;
+        }
+        else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_CD_PLAYER))
+        {
+            /* type cd player */
+            *TargetType = MIXERLINE_TARGETTYPE_UNDEFINED;
+            *ComponentType = MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC;
+        }
+        else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_SYNTHESIZER))
+        {
+            /* type synthesizer */
+            *TargetType = MIXERLINE_TARGETTYPE_MIDIOUT;
+            *ComponentType = MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER;
+        }
+        else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_LINE_CONNECTOR))
+        {
+            /* type line */
+            *TargetType = MIXERLINE_TARGETTYPE_UNDEFINED;
+            *ComponentType = MIXERLINE_COMPONENTTYPE_SRC_LINE;
+        }
+        else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_TELEPHONE) ||
+                 IsEqualGUIDAligned(&Guid, &KSNODETYPE_PHONE_LINE) ||
+                 IsEqualGUIDAligned(&Guid, &KSNODETYPE_DOWN_LINE_PHONE))
+        {
+            /* type telephone */
+            *TargetType =  MIXERLINE_TARGETTYPE_UNDEFINED;
+            *ComponentType =  MIXERLINE_COMPONENTTYPE_SRC_TELEPHONE;
+        }
+        else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_ANALOG_CONNECTOR))
+        {
+            /* type analog */
+            if (BridgePin)
+                *TargetType = MIXERLINE_TARGETTYPE_WAVEIN;
+            else
+                *TargetType = MIXERLINE_TARGETTYPE_WAVEOUT;
+
+            *ComponentType = MIXERLINE_COMPONENTTYPE_SRC_ANALOG;
+        }
+        else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_SPDIF_INTERFACE))
+        {
+            /* type analog */
+            if (BridgePin)
+                *TargetType = MIXERLINE_TARGETTYPE_WAVEIN;
+            else
+                *TargetType = MIXERLINE_TARGETTYPE_WAVEOUT;
+
+            *ComponentType = MIXERLINE_COMPONENTTYPE_SRC_DIGITAL;
+        }
+        else
+        {
+            /* unknown type */
+            *TargetType = MIXERLINE_TARGETTYPE_UNDEFINED;
+            *ComponentType = MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED;
+            DPRINT1("Unknown Category for PinId %lu BridgePin %lu\n", PinId, BridgePin);
+        }
+    }
+    else
+    {
+        if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_SPEAKER) ||
+                 IsEqualGUIDAligned(&Guid, &KSNODETYPE_DESKTOP_SPEAKER) ||
+                 IsEqualGUIDAligned(&Guid, &KSNODETYPE_ROOM_SPEAKER) ||
+                 IsEqualGUIDAligned(&Guid, &KSNODETYPE_COMMUNICATION_SPEAKER))
+        {
+            /* type waveout */
+            *TargetType =  MIXERLINE_TARGETTYPE_WAVEOUT;
+            *ComponentType =  MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
+        }
+        else if (IsEqualGUIDAligned(&Guid, &KSCATEGORY_AUDIO) ||
+                 IsEqualGUIDAligned(&Guid, &PINNAME_CAPTURE))
+        {
+            /* type wavein */
+            *TargetType =  MIXERLINE_TARGETTYPE_WAVEIN;
+            *ComponentType =  MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
+        }
+        else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_HEADPHONES) ||
+                 IsEqualGUIDAligned(&Guid, &KSNODETYPE_HEAD_MOUNTED_DISPLAY_AUDIO))
+        {
+            /* type head phones */
+            *TargetType =  MIXERLINE_TARGETTYPE_WAVEOUT;
+            *ComponentType =  MIXERLINE_COMPONENTTYPE_DST_HEADPHONES;
+        }
+        else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_TELEPHONE) ||
+                 IsEqualGUIDAligned(&Guid, &KSNODETYPE_PHONE_LINE) ||
+                 IsEqualGUIDAligned(&Guid, &KSNODETYPE_DOWN_LINE_PHONE))
+        {
+            /* type waveout */
+            *TargetType =   MIXERLINE_TARGETTYPE_UNDEFINED;
+            *ComponentType =   MIXERLINE_COMPONENTTYPE_DST_TELEPHONE;
+        }
+        else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_ANALOG_CONNECTOR))
+        {
+            /* type analog */
+            if (BridgePin)
+            {
+                *TargetType =  MIXERLINE_TARGETTYPE_WAVEOUT;
+                *ComponentType =  MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
+            }
+            else
+            {
+                *TargetType =  MIXERLINE_TARGETTYPE_WAVEIN;
+                *ComponentType =  MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
+            }
+        }
+        else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_SPDIF_INTERFACE))
+        {
+            /* type spdif */
+            if (BridgePin)
+            {
+                *TargetType =  MIXERLINE_TARGETTYPE_WAVEOUT;
+                *ComponentType =  MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
+            }
+            else
+            {
+                *TargetType =  MIXERLINE_TARGETTYPE_WAVEIN;
+                *ComponentType =  MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
+            }
+        }
+        else
+        {
+            /* unknown type */
+            *TargetType = MIXERLINE_TARGETTYPE_UNDEFINED;
+            *ComponentType = MIXERLINE_COMPONENTTYPE_DST_UNDEFINED;
+            DPRINT1("Unknown Category for PinId %lu BridgePin %lu\n", PinId, BridgePin);
+        }
+    }
+
+    /* done */
+    return MM_STATUS_SUCCESS;
+}
+
 MIXER_STATUS
 MMixerBuildMixerSourceLine(
     IN PMIXER_CONTEXT MixerContext,
@@ -524,11 +905,22 @@ MMixerBuildMixerSourceLine(
     IN ULONG PinId,
     IN ULONG NodesCount,
     IN PULONG Nodes,
+    IN ULONG DestinationLineID,
     OUT LPMIXERLINE_EXT * OutSrcLine)
 {
     LPMIXERLINE_EXT SrcLine, DstLine;
     LPWSTR PinName;
     MIXER_STATUS Status;
+    ULONG ComponentType, TargetType;
+
+    /* get component and target type */
+    Status = MMixerGetComponentAndTargetType(MixerContext, MixerInfo, PinId, &ComponentType, &TargetType);
+    if (Status != MM_STATUS_SUCCESS)
+    {
+        /* failed to get component status */
+        TargetType = MIXERLINE_TARGETTYPE_UNDEFINED;
+        ComponentType = MIXERLINE_COMPONENTTYPE_DST_UNDEFINED;
+    }
 
     /* construct source line */
     SrcLine = (LPMIXERLINE_EXT)MixerContext->Alloc(sizeof(MIXERLINE_EXT));
@@ -540,7 +932,7 @@ MMixerBuildMixerSourceLine(
     }
 
     /* get destination line */
-    DstLine = MMixerGetSourceMixerLineByLineId(MixerInfo, DESTINATION_LINE);
+    DstLine = MMixerGetSourceMixerLineByLineId(MixerInfo, DestinationLineID);
     ASSERT(DstLine);
 
     /* initialize mixer src line */
@@ -550,14 +942,15 @@ MMixerBuildMixerSourceLine(
 
     /* initialize mixer line */
     SrcLine->Line.cbStruct = sizeof(MIXERLINEW);
-    SrcLine->Line.dwDestination = 0;
+    SrcLine->Line.dwDestination = MixerInfo->MixCaps.cDestinations-1;
     SrcLine->Line.dwSource = DstLine->Line.cConnections;
-    SrcLine->Line.dwLineID = (DstLine->Line.cConnections * 0x10000);
+    SrcLine->Line.dwLineID = (DstLine->Line.cConnections * SOURCE_LINE)+ (MixerInfo->MixCaps.cDestinations-1);
     SrcLine->Line.fdwLine = MIXERLINE_LINEF_ACTIVE | MIXERLINE_LINEF_SOURCE;
+    SrcLine->Line.dwComponentType = ComponentType;
     SrcLine->Line.dwUser = 0;
     SrcLine->Line.cChannels = DstLine->Line.cChannels;
     SrcLine->Line.cConnections = 0;
-    SrcLine->Line.Target.dwType = 1;
+    SrcLine->Line.Target.dwType = TargetType;
     SrcLine->Line.Target.dwDeviceID = DstLine->Line.Target.dwDeviceID;
     SrcLine->Line.Target.wMid = MixerInfo->MixCaps.wMid;
     SrcLine->Line.Target.wPid = MixerInfo->MixCaps.wPid;
@@ -603,6 +996,7 @@ MMixerAddMixerSourceLines(
     IN PMIXER_CONTEXT MixerContext,
     IN OUT LPMIXER_INFO MixerInfo,
     IN PTOPOLOGY Topology,
+    IN ULONG DestinationLineID,
     IN ULONG LineTerminator)
 {
     PULONG AllNodes, AllPins, AllPinNodes;
@@ -612,7 +1006,7 @@ MMixerAddMixerSourceLines(
     LPMIXERLINE_EXT DstLine, SrcLine;
 
     /* get destination line */
-    DstLine = MMixerGetSourceMixerLineByLineId(MixerInfo, DESTINATION_LINE);
+    DstLine = MMixerGetSourceMixerLineByLineId(MixerInfo, DestinationLineID);
     ASSERT(DstLine);
 
     /* allocate an array to store all nodes which are upstream of the line terminator */
@@ -647,9 +1041,9 @@ MMixerAddMixerSourceLines(
     AllPinsCount = 0;
     MMixerGetAllUpOrDownstreamPinsFromNodeIndex(MixerContext, Topology, LineTerminator, TRUE, &AllPinsCount, AllPins);
 
-    DPRINT("LineTerminator %lu\n", LineTerminator);
-    DPRINT("PinCount %lu\n", AllPinsCount);
-    DPRINT("AllNodesCount %lu\n", AllNodesCount);
+    DPRINT1("LineTerminator %lu\n", LineTerminator);
+    DPRINT1("PinCount %lu\n", AllPinsCount);
+    DPRINT1("AllNodesCount %lu\n", AllNodesCount);
 
     /* now construct the source lines which are attached to the destination line */
     Index = AllPinsCount;
@@ -693,8 +1087,11 @@ MMixerAddMixerSourceLines(
 
         if (AllPinNodesCount)
         {
+#ifdef MMIXER_DEBUG
+            ULONG TempIndex;
+#endif
             /* now build the mixer source line */
-            Status = MMixerBuildMixerSourceLine(MixerContext, MixerInfo, Topology, PinId, AllPinNodesCount, AllPinNodes, &SrcLine);
+            Status = MMixerBuildMixerSourceLine(MixerContext, MixerInfo, Topology, PinId, AllPinNodesCount, AllPinNodes, DestinationLineID, &SrcLine);
 
              if (Status == MM_STATUS_SUCCESS)
              {
@@ -703,8 +1100,19 @@ MMixerAddMixerSourceLines(
 
                  /* increment destination line count */
                  DstLine->Line.cConnections++;
+#ifdef MMIXER_DEBUG
+                 DPRINT1("Adding PinId %lu AllPinNodesCount %lu to DestinationLine %lu\n", PinId, AllPinNodesCount, DestinationLineID);
+                 for(TempIndex = 0; TempIndex < AllPinNodesCount; TempIndex++)
+                     DPRINT1("NodeIndex %lu\n", AllPinNodes[TempIndex]);
+#endif
              }
         }
+        else
+        {
+#ifdef MMIXER_DEBUG
+            DPRINT1("Discarding DestinationLineID %lu PinId %lu NO NODES!\n", DestinationLineID, PinId);
+#endif
+        }
 
     }while(Index != 0);
 
@@ -719,6 +1127,7 @@ MMixerAddMixerControlsToDestinationLine(
     IN PTOPOLOGY Topology,
     IN ULONG PinId,
     IN ULONG bInput,
+    IN ULONG DestinationLineId,
     OUT PULONG OutLineTerminator)
 {
     PULONG Nodes;
@@ -737,7 +1146,7 @@ MMixerAddMixerControlsToDestinationLine(
     }
 
     /* get all destination line controls */
-    Status = MMixerCountMixerControls(MixerContext, Topology, PinId, TRUE, &NodesCount, Nodes, &LineTerminator);
+    Status = MMixerCountMixerControls(MixerContext, Topology, PinId, bInput, TRUE, &NodesCount, Nodes, &LineTerminator);
 
     /* check for success */
     if (Status != MM_STATUS_SUCCESS)
@@ -748,7 +1157,7 @@ MMixerAddMixerControlsToDestinationLine(
     }
 
     /* get destination mixer line */
-    DstLine = MMixerGetSourceMixerLineByLineId(MixerInfo, DESTINATION_LINE);
+    DstLine = MMixerGetSourceMixerLineByLineId(MixerInfo, DestinationLineId);
 
     /* sanity check */
     ASSERT(DstLine);
@@ -823,7 +1232,7 @@ MMixerHandlePhysicalConnection(
     IN PKSPIN_PHYSICALCONNECTION OutConnection)
 {
     MIXER_STATUS Status;
-    ULONG PinsCount, LineTerminator;
+    ULONG PinsCount, LineTerminator, DestinationLineID;
     PULONG Pins;
     PTOPOLOGY Topology;
 
@@ -838,17 +1247,35 @@ MMixerHandlePhysicalConnection(
          return MM_STATUS_UNSUCCESSFUL;
      }
 
-    DPRINT("Name %S, Pin %lu bInput %lu\n", OutConnection->SymbolicLinkName, OutConnection->Pin, bInput);
+    DPRINT1("Name %S, Pin %lu bInput %lu\n", OutConnection->SymbolicLinkName, OutConnection->Pin, bInput);
+
+    if (MixerInfo->hMixer != NULL)
+    {
+         /* dont replace mixer destination handles */
+         DPRINT1("MixerInfo hDevice %p MixerData hDevice %p\n", MixerInfo->hMixer, MixerData->hDevice);
+         ASSERT(MixerInfo->hMixer == MixerData->hDevice);
+    }
 
     /* store connected mixer handle */
     MixerInfo->hMixer = MixerData->hDevice;
 
+    if (MixerData->Topology == NULL)
+    {
+        /* construct new topology */
+        Status = MMixerBuildTopology(MixerContext, MixerData, &Topology);
+        if (Status != MM_STATUS_SUCCESS)
+        {
+            /* failed to create topology */
+            return Status;
+        }
 
-    Status = MMixerBuildTopology(MixerContext, MixerData, &Topology);
-    if (Status != MM_STATUS_SUCCESS)
+        /* store topology */
+        MixerData->Topology = Topology;
+    }
+    else
     {
-        /* failed to create topology */
-        return Status;
+        /* re-use existing topology */
+        Topology = MixerData->Topology;
     }
 
     /* allocate pin index array which will hold all referenced pins */
@@ -887,79 +1314,93 @@ MMixerHandlePhysicalConnection(
         /* create destination line */
         Status = MMixerBuildMixerDestinationLine(MixerContext, MixerInfo, Pins[0], bInput);
 
+        /* calculate destination line id */
+        DestinationLineID = (DESTINATION_LINE + MixerInfo->MixCaps.cDestinations-1);
+
         if (Status != MM_STATUS_SUCCESS)
         {
+            /* failed to build destination line */
             MixerContext->Free(Pins);
-            //MMixerFreeTopology(Topology);
 
             /* return error code */
             return Status;
         }
 
         /* add mixer controls to destination line */
-        Status = MMixerAddMixerControlsToDestinationLine(MixerContext, MixerInfo, Topology, Pins[0], bInput, &LineTerminator);
+        Status = MMixerAddMixerControlsToDestinationLine(MixerContext, MixerInfo, Topology, Pins[0], bInput, DestinationLineID,  &LineTerminator);
 
         if (Status == MM_STATUS_SUCCESS)
         {
             /* now add the rest of the source lines */
-            Status = MMixerAddMixerSourceLines(MixerContext, MixerInfo, Topology, LineTerminator);
+            Status = MMixerAddMixerSourceLines(MixerContext, MixerInfo, Topology, DestinationLineID, LineTerminator);
         }
     }
     else
     {
-        Status = MMixerAddMixerControlsToDestinationLine(MixerContext, MixerInfo, Topology, OutConnection->Pin, bInput, &LineTerminator);
+        /* calculate destination line id */
+        DestinationLineID = (DESTINATION_LINE + MixerInfo->MixCaps.cDestinations-1);
+
+        /* add mixer controls */
+        Status = MMixerAddMixerControlsToDestinationLine(MixerContext, MixerInfo, Topology, OutConnection->Pin, bInput, DestinationLineID, &LineTerminator);
 
         if (Status == MM_STATUS_SUCCESS)
         {
             /* now add the rest of the source lines */
-            Status = MMixerAddMixerSourceLines(MixerContext, MixerInfo, Topology, LineTerminator);
+            Status = MMixerAddMixerSourceLines(MixerContext, MixerInfo, Topology, DestinationLineID, LineTerminator);
         }
     }
 
-    /* free topology */
-    //MMixerFreeTopology(Topology);
-
     return Status;
 }
 
-
 MIXER_STATUS
 MMixerInitializeFilter(
     IN PMIXER_CONTEXT MixerContext,
     IN PMIXER_LIST MixerList,
     IN LPMIXER_DATA MixerData,
+    IN LPMIXER_INFO MixerInfo,
     IN PTOPOLOGY Topology,
     IN ULONG NodeIndex,
-    IN ULONG bInputMixer)
+    IN ULONG bInputMixer,
+    IN OUT LPMIXER_INFO * OutMixerInfo)
 {
-    LPMIXER_INFO MixerInfo;
+
     MIXER_STATUS Status;
     PKSPIN_PHYSICALCONNECTION OutConnection;
     ULONG * Pins;
     ULONG PinsFound;
+    ULONG NewMixerInfo = FALSE;
 
-    /* allocate a mixer info struct */
-    MixerInfo = (LPMIXER_INFO) MixerContext->Alloc(sizeof(MIXER_INFO));
-    if (!MixerInfo)
+    if (MixerInfo == NULL)
     {
-        /* no memory */
-        return MM_STATUS_NO_MEMORY;
-    }
+        /* allocate a mixer info struct */
+        MixerInfo = (LPMIXER_INFO) MixerContext->Alloc(sizeof(MIXER_INFO));
+        if (!MixerInfo)
+        {
+            /* no memory */
+            return MM_STATUS_NO_MEMORY;
+        }
 
-    /* intialize mixer caps */
-    MixerInfo->MixCaps.wMid = MM_MICROSOFT; /* FIXME */
-    MixerInfo->MixCaps.wPid = MM_PID_UNMAPPED; /* FIXME */
-    MixerInfo->MixCaps.vDriverVersion = 1; /* FIXME */
-    MixerInfo->MixCaps.fdwSupport = 0;
-    MixerInfo->MixCaps.cDestinations = 1;
-    MixerInfo->hMixer = MixerData->hDevice;
+        /* new mixer info */
+        NewMixerInfo = TRUE;
+
+        /* intialize mixer caps */
+        MixerInfo->MixCaps.wMid = MM_MICROSOFT; /* FIXME */
+        MixerInfo->MixCaps.wPid = MM_PID_UNMAPPED; /* FIXME */
+        MixerInfo->MixCaps.vDriverVersion = 1; /* FIXME */
+        MixerInfo->MixCaps.fdwSupport = 0;
+        MixerInfo->MixCaps.cDestinations = 0;
 
-    /* get mixer name */
-    MMixerGetDeviceName(MixerContext, MixerInfo->MixCaps.szPname, MixerData->hDeviceInterfaceKey);
+        /* get mixer name */
+        MMixerGetDeviceName(MixerContext, MixerInfo->MixCaps.szPname, MixerData->hDeviceInterfaceKey);
+
+        /* initialize line list */
+        InitializeListHead(&MixerInfo->LineList);
+        InitializeListHead(&MixerInfo->EventList);
+    }
 
-    /* initialize line list */
-    InitializeListHead(&MixerInfo->LineList);
-    InitializeListHead(&MixerInfo->EventList);
+    /* store mixer info */
+    *OutMixerInfo = MixerInfo;
 
     /* now allocate an array which will receive the indices of the pin 
      * which has a ADC / DAC nodetype in its path
@@ -977,7 +1418,7 @@ MMixerInitializeFilter(
     PinsFound = 0;
     MMixerGetAllUpOrDownstreamPinsFromNodeIndex(MixerContext, Topology, NodeIndex, !bInputMixer, &PinsFound, Pins);
 
-    /* if there is now pin found, we have a broken topology */
+    /* if there is no pin found, we have a broken topology */
     ASSERT(PinsFound != 0);
 
     /* now create a wave info struct */
@@ -1039,26 +1480,19 @@ MMixerInitializeFilter(
          * handle drivers which expose their topology on the same filter
          */
         ASSERT(0);
+        MixerInfo->hMixer = MixerData->hDevice;
     }
 
     /* free pins */
     MixerContext->Free(Pins);
 
-    if (!bInputMixer && MixerList->MixerListCount == 1)
+    if (NewMixerInfo)
     {
-        /* FIXME preferred device should be inserted at front
-         * windows always inserts output mixer in front
-         */
+        /* insert mixer */
         InsertHeadList(&MixerList->MixerList, &MixerInfo->Entry);
+        /* increment mixer count */
+        MixerList->MixerListCount++;
     }
-    else
-    {
-        /* insert at back */
-        InsertTailList(&MixerList->MixerList, &MixerInfo->Entry);
-    }
-
-    /* increment mixer count */
-    MixerList->MixerListCount++;
 
     /* done */
     return Status;
@@ -1074,6 +1508,7 @@ MMixerSetupFilter(
     MIXER_STATUS Status;
     PTOPOLOGY Topology;
     ULONG NodeIndex;
+    LPMIXER_INFO MixerInfo = NULL;
 
     /* check if topology has already been built */
     if (MixerData->Topology == NULL)
@@ -1101,7 +1536,7 @@ MMixerSetupFilter(
     if (NodeIndex != MAXULONG)
     {
         /* it has */
-        Status = MMixerInitializeFilter(MixerContext, MixerList, MixerData, Topology, NodeIndex, FALSE);
+        Status = MMixerInitializeFilter(MixerContext, MixerList, MixerData, NULL, Topology, NodeIndex, FALSE, &MixerInfo);
 
         /* check for success */
         if (Status == MM_STATUS_SUCCESS)
@@ -1109,7 +1544,11 @@ MMixerSetupFilter(
             /* increment mixer count */
             (*DeviceCount)++;
         }
-
+        else
+        {
+            /* reset mixer info in case of error */
+            MixerInfo = NULL;
+        }
     }
 
     /* check if the filter has an wave in node */
@@ -1117,7 +1556,7 @@ MMixerSetupFilter(
     if (NodeIndex != MAXULONG)
     {
         /* it has */
-        Status = MMixerInitializeFilter(MixerContext, MixerList, MixerData, Topology, NodeIndex, TRUE);
+        Status = MMixerInitializeFilter(MixerContext, MixerList, MixerData, MixerInfo, Topology, NodeIndex, TRUE, &MixerInfo);
 
         /* check for success */
         if (Status == MM_STATUS_SUCCESS)
@@ -1128,6 +1567,8 @@ MMixerSetupFilter(
 
     }
 
+    /* TODO: handle alternative mixer types + apply hacks for Wave source line */
+
     /* activate midi devices */
     MMixerInitializeMidiForFilter(MixerContext, MixerList, MixerData, Topology);