[MMEBUDDY]
authorJohannes Anderwald <johannes.anderwald@reactos.org>
Fri, 28 Jan 2011 10:17:01 +0000 (10:17 +0000)
committerJohannes Anderwald <johannes.anderwald@reactos.org>
Fri, 28 Jan 2011 10:17:01 +0000 (10:17 +0000)
- Merge from audio branch
- Handle mixers identified by id, not by handle
- Fix opening of mixer devices
- Waveformat struct is only provided when type is wave device
- Implement wave reset routine, fixes audio recording on ReactOS SndRec, AudaCity etc. Also fixes random hang in WinAmp when skipping audio bytes
- Implement wave pausing / restarting, should lead to smoother playback
[MMIXER]
- Merge from audio branch
- Tons of fixes to enumeration of mixerlines, controls / etc
- Fix mixer event notification callbacks
- For more info, read audio-bringup log

svn path=/trunk/; revision=50528

14 files changed:
reactos/lib/drivers/sound/mmebuddy/mixer/mxdMessage.c
reactos/lib/drivers/sound/mmebuddy/mmewrap.c
reactos/lib/drivers/sound/mmebuddy/wave/format.c
reactos/lib/drivers/sound/mmebuddy/wave/streaming.c
reactos/lib/drivers/sound/mmebuddy/wave/wodMessage.c
reactos/lib/drivers/sound/mmixer/TODO [new file with mode: 0644]
reactos/lib/drivers/sound/mmixer/controls.c
reactos/lib/drivers/sound/mmixer/midi.c
reactos/lib/drivers/sound/mmixer/mixer.c
reactos/lib/drivers/sound/mmixer/mmixer.h
reactos/lib/drivers/sound/mmixer/priv.h
reactos/lib/drivers/sound/mmixer/sup.c
reactos/lib/drivers/sound/mmixer/topology.c
reactos/lib/drivers/sound/mmixer/wave.c

index 08815ba..7652d9e 100644 (file)
@@ -20,6 +20,7 @@
 
 MMRESULT
 MmeGetLineInfo(
+    IN UINT DeviceId,
     IN  UINT Message,
     IN  DWORD_PTR PrivateHandle,
     IN  DWORD_PTR Parameter1,
@@ -32,6 +33,21 @@ MmeGetLineInfo(
 
     //SND_TRACE(L"Getting mixer info %u\n", Message);
 
+    if ( PrivateHandle == 0 )
+    {
+        Result = GetSoundDevice(MIXER_DEVICE_TYPE, DeviceId, &SoundDevice);
+
+        if ( ! MMSUCCESS(Result) )
+            return TranslateInternalMmResult(Result);
+
+         Result = GetSoundDeviceFunctionTable(SoundDevice, &FunctionTable);
+         if ( ! MMSUCCESS(Result) )
+            return TranslateInternalMmResult(Result);
+
+         Result = FunctionTable->QueryMixerInfo(NULL, DeviceId, Message, (LPVOID)Parameter1, Parameter2);
+         return Result;
+    }
+
     VALIDATE_MMSYS_PARAMETER( PrivateHandle );
     SoundDeviceInstance = (PSOUND_DEVICE_INSTANCE) PrivateHandle;
 
@@ -46,7 +62,7 @@ MmeGetLineInfo(
     if ( ! FunctionTable->QueryMixerInfo )
         return MMSYSERR_NOTSUPPORTED;
 
-    Result = FunctionTable->QueryMixerInfo(SoundDeviceInstance, Message, (LPVOID)Parameter1, Parameter2);
+    Result = FunctionTable->QueryMixerInfo(SoundDeviceInstance, DeviceId, Message, (LPVOID)Parameter1, Parameter2);
 
     return Result;
 }
@@ -100,7 +116,7 @@ mxdMessage(
                                    (LPWAVEOPENDESC) Parameter1, /* unused */
                                    Parameter2,
                                    (DWORD*) PrivateHandle);
-
+            VALIDATE_MMSYS_PARAMETER(*(DWORD_PTR*)PrivateHandle);
             break;
         }
 
@@ -113,7 +129,8 @@ mxdMessage(
 
         case MXDM_GETCONTROLDETAILS :
         {
-            Result = MmeGetLineInfo(Message,
+            Result = MmeGetLineInfo(DeviceId,
+                                    Message,
                                     PrivateHandle,
                                     Parameter1,
                                     Parameter2);
@@ -123,7 +140,8 @@ mxdMessage(
 
         case MXDM_SETCONTROLDETAILS :
         {
-            Result = MmeGetLineInfo(Message,
+            Result = MmeGetLineInfo(DeviceId,
+                                    Message,
                                     PrivateHandle,
                                     Parameter1,
                                     Parameter2);
@@ -133,7 +151,8 @@ mxdMessage(
 
         case MXDM_GETLINECONTROLS :
         {
-            Result = MmeGetLineInfo(Message,
+            Result = MmeGetLineInfo(DeviceId,
+                                    Message,
                                     PrivateHandle,
                                     Parameter1,
                                     Parameter2);
@@ -143,7 +162,8 @@ mxdMessage(
 
         case MXDM_GETLINEINFO :
         {
-            Result = MmeGetLineInfo(Message,
+            Result = MmeGetLineInfo(DeviceId,
+                                    Message,
                                     PrivateHandle,
                                     Parameter1,
                                     Parameter2);
index 583d198..9e03770 100644 (file)
@@ -130,11 +130,11 @@ MmeOpenDevice(
     UINT Message;
     PSOUND_DEVICE SoundDevice;
     PSOUND_DEVICE_INSTANCE SoundDeviceInstance;
-    LPWAVEFORMATEX Format;
+    LPWAVEFORMATEX Format = NULL;
 
     SND_TRACE(L"Opening device");
 
-    VALIDATE_MMSYS_PARAMETER( IS_WAVE_DEVICE_TYPE(DeviceType) );    /* FIXME? wave in too? */
+    VALIDATE_MMSYS_PARAMETER( IS_WAVE_DEVICE_TYPE(DeviceType) || IS_MIXER_DEVICE_TYPE(DeviceType) || IS_MIDI_DEVICE_TYPE(DeviceType) );    /* FIXME? wave in too? */
     VALIDATE_MMSYS_PARAMETER( OpenParameters );
 
     Result = GetSoundDevice(DeviceType, DeviceId, &SoundDevice);
index ecc5bbc..34541c4 100644 (file)
@@ -65,8 +65,6 @@ SetWaveDeviceFormat(
     SND_TRACE(L"Setting wave format\n");
 
     VALIDATE_MMSYS_PARAMETER( IsValidSoundDeviceInstance(SoundDeviceInstance) );
-    VALIDATE_MMSYS_PARAMETER( Format );
-    VALIDATE_MMSYS_PARAMETER( FormatSize >= sizeof(WAVEFORMATEX) );
 
     Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
     if ( ! MMSUCCESS(Result) )
@@ -74,9 +72,14 @@ SetWaveDeviceFormat(
 
     Result = GetSoundDeviceType(SoundDevice, &DeviceType);
     SND_ASSERT( Result == MMSYSERR_NOERROR );
+    if (DeviceType == WAVE_IN_DEVICE_TYPE || DeviceType == WAVE_OUT_DEVICE_TYPE)
+    {
+        VALIDATE_MMSYS_PARAMETER( Format );
+        VALIDATE_MMSYS_PARAMETER( FormatSize >= sizeof(WAVEFORMATEX) );
+    }
 
     /* Ensure we have a wave device (TODO: check if this applies to wavein as well) */
-    VALIDATE_MMSYS_PARAMETER( IS_WAVE_DEVICE_TYPE(DeviceType) );
+    VALIDATE_MMSYS_PARAMETER( IS_WAVE_DEVICE_TYPE(DeviceType) || IS_MIDI_DEVICE_TYPE(DeviceType) || IS_MIXER_DEVICE_TYPE(DeviceType));
 
     /* Obtain the function table */
     Result = GetSoundDeviceFunctionTable(SoundDevice, &FunctionTable);
index a280933..63ac5e0 100644 (file)
@@ -232,6 +232,8 @@ CompleteIO(
 
        }while(dwNumberOfBytesTransferred);
 
+    // AUDIO-BRANCH DIFF
+    // completion callback is performed in a thread
     DoWaveStreaming(SoundDeviceInstance);
 
     //CompleteWavePortion(SoundDeviceInstance, dwNumberOfBytesTransferred);
@@ -277,8 +279,62 @@ StopStreamingInSoundThread(
     IN  PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
     IN  PVOID Parameter)
 {
-    /* TODO */
-    return MMSYSERR_NOTSUPPORTED;
+    MMDEVICE_TYPE DeviceType;
+    PMMFUNCTION_TABLE FunctionTable;
+    MMRESULT Result;
+    PSOUND_DEVICE SoundDevice;
+
+    /* set state reset in progress */
+    SoundDeviceInstance->ResetInProgress = TRUE;
+
+    /* Get sound device */
+    Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
+    SND_ASSERT( Result == MMSYSERR_NOERROR );
+
+    /* Obtain the function table */
+    Result = GetSoundDeviceFunctionTable(SoundDevice, &FunctionTable);
+    SND_ASSERT( Result == MMSYSERR_NOERROR );
+
+    /* Obtain device instance type */
+    Result = GetSoundDeviceType(SoundDevice, &DeviceType);
+    SND_ASSERT( Result == MMSYSERR_NOERROR );
+
+    /* Check if reset function is supported */
+    if (FunctionTable->ResetStream)
+    {
+         /* cancel all current audio buffers */
+         FunctionTable->ResetStream(SoundDeviceInstance, DeviceType, TRUE);
+    }
+
+    /* complete all current headers */
+    while( SoundDeviceInstance->HeadWaveHeader )
+    {
+        SND_TRACE(L"StopStreamingInSoundThread: Completing Header %p\n", SoundDeviceInstance->HeadWaveHeader);
+        CompleteWaveHeader( SoundDeviceInstance, SoundDeviceInstance->HeadWaveHeader );
+    }
+
+    /* there should be no oustanding buffers now */
+    SND_ASSERT(SoundDeviceInstance->OutstandingBuffers == 0);
+
+    while(SoundDeviceInstance->OutstandingBuffers)
+    {
+        SND_ERR("StopStreamingInSoundThread OutStandingBufferCount %lu\n", SoundDeviceInstance->OutstandingBuffers);
+        /* my hack of doom */
+        Sleep(10);
+    }
+
+    /* Check if reset function is supported */
+    if (FunctionTable->ResetStream)
+    {
+        /* finish the reset */
+        FunctionTable->ResetStream(SoundDeviceInstance, DeviceType, FALSE);
+    }
+
+    /* clear state reset in progress */
+    SoundDeviceInstance->ResetInProgress = FALSE;
+
+
+    return MMSYSERR_NOERROR;
 }
 
 MMRESULT
index 330c5f2..a1202a8 100644 (file)
@@ -109,6 +109,13 @@ wodMessage(
         case WODM_RESTART :
         {
             /* Continue playback when paused */
+            Result = MmeSetState(PrivateHandle, TRUE);
+            break;
+        }
+        case WODM_PAUSE :
+        {
+            /* pause playback */
+            Result = MmeSetState(PrivateHandle, FALSE);
             break;
         }
 
diff --git a/reactos/lib/drivers/sound/mmixer/TODO b/reactos/lib/drivers/sound/mmixer/TODO
new file mode 100644 (file)
index 0000000..05cf9c2
--- /dev/null
@@ -0,0 +1,6 @@
+=== MMIXER TASKS ===\r
+\r
+- Add hacks for source lines, such that Wave Mixer line always has a volume control\r
+- Support custom mixer controls\r
+- Assign mixer controls after all controls have been assigned (starting on the destination lines)\r
+- TESTING & BUGFIXING
\ No newline at end of file
index 5b5dd18..e7bd288 100644 (file)
@@ -8,44 +8,67 @@
  
 #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,
     IN LPMIXER_INFO MixerInfo,
+    IN HANDLE hMixer,
     IN PTOPOLOGY Topology,
     IN ULONG NodeIndex,
     IN LPMIXERLINE_EXT MixerLine,
-    OUT LPMIXERCONTROLW MixerControl)
+    IN ULONG MaxChannels)
 {
     LPGUID NodeType;
     KSP_NODE Node;
     ULONG BytesReturned;
     MIXER_STATUS Status;
     LPWSTR Name;
+    LPMIXERCONTROL_EXT MixerControl;
+
+    /* allocate mixer control */
+    MixerControl = MixerContext->Alloc(sizeof(MIXERCONTROL_EXT));
+    if (!MixerControl)
+    {
+        /* no memory */
+        return MM_STATUS_NO_MEMORY;
+    }
+
 
     /* initialize mixer control */
-    MixerControl->cbStruct = sizeof(MIXERCONTROLW);
-    MixerControl->dwControlID = MixerInfo->ControlId;
+    MixerControl->hDevice = hMixer;
+    MixerControl->NodeID = NodeIndex;
+    MixerControl->ExtraData = NULL;
+
+    MixerControl->Control.cbStruct = sizeof(MIXERCONTROLW);
+    MixerControl->Control.dwControlID = MixerInfo->ControlId;
 
     /* get node type */
     NodeType = MMixerGetNodeTypeFromTopology(Topology, NodeIndex);
     /* store control type */
-    MixerControl->dwControlType = MMixerGetControlTypeFromTopologyNode(NodeType);
+    MixerControl->Control.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->Control.fdwControl = (MaxChannels > 1 ? 0 : MIXERCONTROL_CONTROLF_UNIFORM);
+    MixerControl->Control.cMultipleItems = 0;
 
     /* setup request to retrieve name */
     Node.NodeId = NodeIndex;
@@ -55,7 +78,7 @@ MMixerAddMixerControl(
     Node.Reserved = 0;
 
     /* get node name size */
-    Status = MixerContext->Control(MixerInfo->hMixer, IOCTL_KS_PROPERTY, (PVOID)&Node, sizeof(KSP_NODE), NULL, 0, &BytesReturned);
+    Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Node, sizeof(KSP_NODE), NULL, 0, &BytesReturned);
 
     if (Status == MM_STATUS_MORE_ENTRIES)
     {
@@ -68,44 +91,66 @@ MMixerAddMixerControl(
         }
 
         /* get node name */
-        Status = MixerContext->Control(MixerInfo->hMixer, IOCTL_KS_PROPERTY, (PVOID)&Node, sizeof(KSP_NODE), (LPVOID)Name, BytesReturned, &BytesReturned);
+        Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Node, sizeof(KSP_NODE), (LPVOID)Name, BytesReturned, &BytesReturned);
 
         if (Status == MM_STATUS_SUCCESS)
         {
-            MixerContext->Copy(MixerControl->szShortName, Name, (min(MIXER_SHORT_NAME_CHARS, wcslen(Name)+1)) * sizeof(WCHAR));
-            MixerControl->szShortName[MIXER_SHORT_NAME_CHARS-1] = L'\0';
+            MixerContext->Copy(MixerControl->Control.szShortName, Name, (min(MIXER_SHORT_NAME_CHARS, wcslen(Name)+1)) * sizeof(WCHAR));
+            MixerControl->Control.szShortName[MIXER_SHORT_NAME_CHARS-1] = L'\0';
 
-            MixerContext->Copy(MixerControl->szName, Name, (min(MIXER_LONG_NAME_CHARS, wcslen(Name)+1)) * sizeof(WCHAR));
-            MixerControl->szName[MIXER_LONG_NAME_CHARS-1] = L'\0';
+            MixerContext->Copy(MixerControl->Control.szName, Name, (min(MIXER_LONG_NAME_CHARS, wcslen(Name)+1)) * sizeof(WCHAR));
+            MixerControl->Control.szName[MIXER_LONG_NAME_CHARS-1] = L'\0';
         }
 
         /* free name buffer */
         MixerContext->Free(Name);
     }
 
+    /* increment control count */
     MixerInfo->ControlId++;
-#if 0
-    if (MixerControl->dwControlType == MIXERCONTROL_CONTROLTYPE_MUX)
+
+    /* insert control */
+    InsertTailList(&MixerLine->ControlsList, &MixerControl->Entry);
+
+    if (MixerControl->Control.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->Control.Bounds.dwMinimum = 0;
+        MixerControl->Control.Bounds.dwMaximum = NodesCount - 1;
+        MixerControl->Control.Metrics.dwReserved[0] = NodesCount;
+        MixerControl->Control.cMultipleItems = NodesCount;
+        MixerControl->Control.fdwControl |= MIXERCONTROL_CONTROLF_UNIFORM | MIXERCONTROL_CONTROLF_MULTIPLE;
+    }
+    else if (MixerControl->Control.dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE)
+    {
+        MixerControl->Control.Bounds.dwMinimum = 0;
+        MixerControl->Control.Bounds.dwMaximum = 1;
+    }
+    else if (MixerControl->Control.dwControlType == MIXERCONTROL_CONTROLTYPE_ONOFF)
+    {
+        /* only needs to set bounds */
+        MixerControl->Control.Bounds.dwMinimum = 0;
+        MixerControl->Control.Bounds.dwMaximum = 1;
+    }
+    else if (MixerControl->Control.dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)
     {
         KSNODEPROPERTY_AUDIO_CHANNEL Property;
         ULONG Length;
@@ -113,6 +158,10 @@ MMixerAddMixerControl(
         PKSPROPERTY_MEMBERSHEADER Members;
         PKSPROPERTY_STEPPING_LONG Range;
 
+        MixerControl->Control.Bounds.dwMinimum = 0;
+        MixerControl->Control.Bounds.dwMaximum = 0xFFFF;
+        MixerControl->Control.Metrics.cSteps = 0xC0; /* FIXME */
+
         Length = sizeof(KSPROPERTY_DESCRIPTION) + sizeof(KSPROPERTY_MEMBERSHEADER) + sizeof(KSPROPERTY_STEPPING_LONG);
         Desc = (PKSPROPERTY_DESCRIPTION)MixerContext->Alloc(Length);
         ASSERT(Desc);
@@ -122,11 +171,11 @@ 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 */
-        Status = MixerContext->Control(MixerInfo->hMixer, IOCTL_KS_PROPERTY, (PVOID)&Property, sizeof(KSNODEPROPERTY_AUDIO_CHANNEL), Desc, Length, &BytesReturned);
+        Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Property, sizeof(KSNODEPROPERTY_AUDIO_CHANNEL), Desc, Length, &BytesReturned);
 
         if (Status == MM_STATUS_SUCCESS)
         {
@@ -151,7 +200,7 @@ MMixerAddMixerControl(
                 Steps = MaxRange / Range->SteppingDelta + 1;
 
                 /* store mixer control info there */
-                VolumeData->Header.dwControlID = MixerControl->dwControlID;
+                VolumeData->Header.dwControlID = MixerControl->Control.dwControlID;
                 VolumeData->SignedMaximum = Range->Bounds.SignedMaximum;
                 VolumeData->SignedMinimum = Range->Bounds.SignedMinimum;
                 VolumeData->SteppingDelta = Range->SteppingDelta;
@@ -172,13 +221,13 @@ MMixerAddMixerControl(
                     VolumeData->Values[Index] = Value;
                     Value += Range->SteppingDelta;
                 }
-                InsertTailList(&MixerLine->LineControlsExtraData, &VolumeData->Header.Entry);
+                MixerControl->ExtraData = VolumeData;
            }
        }
        MixerContext->Free(Desc);
     }
 
-    DPRINT("Status %x Name %S\n", Status, MixerControl->szName);
+    DPRINT("Status %x Name %S\n", Status, MixerControl->Control.szName);
     return MM_STATUS_SUCCESS;
 }
 
@@ -201,12 +250,16 @@ MMixerCreateDestinationLine(
 
     /* initialize mixer destination line */
     DestinationLine->Line.cbStruct = sizeof(MIXERLINEW);
+    DestinationLine->Line.cChannels = 2; /* FIXME */
+    DestinationLine->Line.cConnections = 0;
+    DestinationLine->Line.cControls = 0;
+    DestinationLine->Line.dwComponentType = (bInputMixer == 0 ? MIXERLINE_COMPONENTTYPE_DST_SPEAKERS : MIXERLINE_COMPONENTTYPE_DST_WAVEIN);
+    DestinationLine->Line.dwDestination = MixerInfo->MixCaps.cDestinations;
+    DestinationLine->Line.dwLineID = MixerInfo->MixCaps.cDestinations + DESTINATION_LINE;
     DestinationLine->Line.dwSource = MAXULONG;
-    DestinationLine->Line.dwLineID = DESTINATION_LINE;
-    DestinationLine->Line.fdwLine = MIXERLINE_LINEF_ACTIVE;
     DestinationLine->Line.dwUser = 0;
-    DestinationLine->Line.dwComponentType = (bInputMixer == 0 ? MIXERLINE_COMPONENTTYPE_DST_SPEAKERS : MIXERLINE_COMPONENTTYPE_DST_WAVEIN);
-    DestinationLine->Line.cChannels = 2; /* FIXME */
+    DestinationLine->Line.fdwLine = MIXERLINE_LINEF_ACTIVE;
+
 
     if (LineName)
     {
@@ -219,7 +272,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;
@@ -228,10 +281,13 @@ MMixerCreateDestinationLine(
     wcscpy(DestinationLine->Line.Target.szPname, MixerInfo->MixCaps.szPname);
 
     /* initialize extra line */
-    InitializeListHead(&DestinationLine->LineControlsExtraData);
+    InitializeListHead(&DestinationLine->ControlsList);
 
     /* 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;
@@ -241,6 +297,7 @@ MIXER_STATUS
 MMixerGetPinName(
     IN PMIXER_CONTEXT MixerContext,
     IN LPMIXER_INFO MixerInfo,
+    IN HANDLE hMixer,
     IN ULONG PinId,
     IN OUT LPWSTR * OutBuffer)
 {
@@ -257,7 +314,7 @@ MMixerGetPinName(
     Pin.Property.Id = KSPROPERTY_PIN_NAME;
 
     /* try get pin name size */
-    Status = MixerContext->Control(MixerInfo->hMixer, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSP_PIN), NULL, 0, &BytesReturned);
+    Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSP_PIN), NULL, 0, &BytesReturned);
 
     /* check if buffer overflowed */
     if (Status == MM_STATUS_MORE_ENTRIES)
@@ -271,7 +328,7 @@ MMixerGetPinName(
         }
 
         /* try get pin name */
-        Status = MixerContext->Control(MixerInfo->hMixer, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSP_PIN), (PVOID)Buffer, BytesReturned, &BytesReturned);
+        Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSP_PIN), (PVOID)Buffer, BytesReturned, &BytesReturned);
         if (Status != MM_STATUS_SUCCESS)
         {
             /* failed to get pin name */
@@ -292,6 +349,7 @@ MIXER_STATUS
 MMixerBuildMixerDestinationLine(
     IN PMIXER_CONTEXT MixerContext,
     IN OUT LPMIXER_INFO MixerInfo,
+    IN HANDLE hMixer,
     IN ULONG PinId,
     IN ULONG bInput)
 {
@@ -299,7 +357,7 @@ MMixerBuildMixerDestinationLine(
     MIXER_STATUS Status;
 
     /* try get pin name */
-    Status = MMixerGetPinName(MixerContext, MixerInfo, PinId, &PinName);
+    Status = MMixerGetPinName(MixerContext, MixerInfo, hMixer, PinId, &PinName);
     if (Status == MM_STATUS_SUCCESS)
     {
         /* create mixer destination line */
@@ -385,6 +443,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 +483,12 @@ MMixerCountMixerControls(
         if (bTerminator)
         {
             /* found terminator */
+            if (bInputMixer)
+            {
+                /* add mux source for source destination line */
+                OutNodes[Count] = NodeIndex;
+                Count++;
+            }
             break;
         }
 
@@ -458,10 +523,140 @@ MMixerCountMixerControls(
     return MM_STATUS_SUCCESS;
 }
 
+MIXER_STATUS
+MMixerGetChannelCountEnhanced(
+    IN PMIXER_CONTEXT MixerContext,
+    IN LPMIXER_INFO MixerInfo,
+    IN HANDLE hMixer,
+    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(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(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 HANDLE hMixer,
+    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(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 HANDLE hMixer,
+    IN ULONG NodeId,
+    OUT PULONG MaxChannels)
+{
+    MIXER_STATUS Status;
+
+    /* try to get it enhanced */
+    Status = MMixerGetChannelCountEnhanced(MixerContext, MixerInfo, hMixer, NodeId, MaxChannels);
+
+    if (Status != MM_STATUS_SUCCESS)
+    {
+        /* get it old-fashioned way */
+        MMixerGetChannelCountLegacy(MixerContext, MixerInfo, hMixer, NodeId, MaxChannels);
+    }
+}
+
 MIXER_STATUS
 MMixerAddMixerControlsToMixerLineByNodeIndexArray(
     IN PMIXER_CONTEXT MixerContext,
     IN LPMIXER_INFO MixerInfo,
+    IN HANDLE hMixer,
     IN PTOPOLOGY Topology,
     IN OUT LPMIXERLINE_EXT DstLine,
     IN ULONG NodesCount,
@@ -469,18 +664,8 @@ MMixerAddMixerControlsToMixerLineByNodeIndexArray(
 {
     ULONG Index, Count, bReserved;
     MIXER_STATUS Status;
-
-    /* store nodes array */
-    DstLine->NodeIds = Nodes;
-
-    /* allocate MIXERCONTROLSW array */
-    DstLine->LineControls = MixerContext->Alloc(NodesCount * sizeof(MIXERCONTROLW));
-
-    if (!DstLine->LineControls)
-    {
-        /* out of memory */
-        return MM_STATUS_NO_MEMORY;
-    }
+    LPGUID NodeType;
+    ULONG MaxChannels;
 
     /* initialize control count */
     Count = 0;
@@ -489,18 +674,36 @@ 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, hMixer, 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);
+        }
+        else
+        {
+            /* use default of one channel */
+            MaxChannels = 1;
+        }
+
         /* now add the mixer control */
-        Status = MMixerAddMixerControl(MixerContext, MixerInfo, Topology, Nodes[Index], DstLine, &DstLine->LineControls[Count]);
+        Status = MMixerAddMixerControl(MixerContext, MixerInfo, hMixer, Topology, Nodes[Index], DstLine, MaxChannels);
 
         if (Status == MM_STATUS_SUCCESS)
         {
@@ -516,19 +719,230 @@ MMixerAddMixerControlsToMixerLineByNodeIndexArray(
     return MM_STATUS_SUCCESS;
 }
 
+MIXER_STATUS
+MMixerGetComponentAndTargetType(
+    IN PMIXER_CONTEXT MixerContext,
+    IN OUT LPMIXER_INFO MixerInfo,
+    IN HANDLE hMixer,
+    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, 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(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, 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,
     IN OUT LPMIXER_INFO MixerInfo,
+    IN HANDLE hMixer,
     IN PTOPOLOGY Topology,
     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, hMixer, 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,36 +954,35 @@ MMixerBuildMixerSourceLine(
     }
 
     /* get destination line */
-    DstLine = MMixerGetSourceMixerLineByLineId(MixerInfo, DESTINATION_LINE);
+    DstLine = MMixerGetSourceMixerLineByLineId(MixerInfo, DestinationLineID);
     ASSERT(DstLine);
 
     /* initialize mixer src line */
-    SrcLine->hDevice = MixerInfo->hMixer;
     SrcLine->PinId = PinId;
-    SrcLine->NodeIds = Nodes;
 
     /* 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;
     SrcLine->Line.Target.vDriverVersion = MixerInfo->MixCaps.vDriverVersion;
-    InitializeListHead(&SrcLine->LineControlsExtraData);
+    InitializeListHead(&SrcLine->ControlsList);
 
     /* copy name */
     ASSERT(MixerInfo->MixCaps.szPname[MAXPNAMELEN-1] == L'\0');
     wcscpy(SrcLine->Line.Target.szPname, MixerInfo->MixCaps.szPname);
 
     /* get pin name */
-    Status = MMixerGetPinName(MixerContext, MixerInfo, PinId, &PinName);
+    Status = MMixerGetPinName(MixerContext, MixerInfo, hMixer, PinId, &PinName);
 
     if (Status == MM_STATUS_SUCCESS)
     {
@@ -585,7 +998,7 @@ MMixerBuildMixerSourceLine(
     }
 
     /* add the controls to mixer line */
-    Status = MMixerAddMixerControlsToMixerLineByNodeIndexArray(MixerContext, MixerInfo, Topology, SrcLine, NodesCount, Nodes);
+    Status = MMixerAddMixerControlsToMixerLineByNodeIndexArray(MixerContext, MixerInfo, hMixer, Topology, SrcLine, NodesCount, Nodes);
     if (Status != MM_STATUS_SUCCESS)
     {
         /* failed */
@@ -602,7 +1015,9 @@ MIXER_STATUS
 MMixerAddMixerSourceLines(
     IN PMIXER_CONTEXT MixerContext,
     IN OUT LPMIXER_INFO MixerInfo,
+    IN HANDLE hMixer,
     IN PTOPOLOGY Topology,
+    IN ULONG DestinationLineID,
     IN ULONG LineTerminator)
 {
     PULONG AllNodes, AllPins, AllPinNodes;
@@ -612,7 +1027,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 */
@@ -693,8 +1108,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, hMixer, Topology, PinId, AllPinNodesCount, AllPinNodes, DestinationLineID, &SrcLine);
 
              if (Status == MM_STATUS_SUCCESS)
              {
@@ -703,8 +1121,23 @@ MMixerAddMixerSourceLines(
 
                  /* increment destination line count */
                  DstLine->Line.cConnections++;
+
+                 /* mark pin as reserved */
+                 MMixerSetTopologyPinReserved(Topology, PinId);
+
+#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);
 
@@ -716,9 +1149,11 @@ MIXER_STATUS
 MMixerAddMixerControlsToDestinationLine(
     IN PMIXER_CONTEXT MixerContext,
     IN OUT LPMIXER_INFO MixerInfo,
+    IN HANDLE hMixer,
     IN PTOPOLOGY Topology,
     IN ULONG PinId,
     IN ULONG bInput,
+    IN ULONG DestinationLineId,
     OUT PULONG OutLineTerminator)
 {
     PULONG Nodes;
@@ -737,7 +1172,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 +1183,7 @@ MMixerAddMixerControlsToDestinationLine(
     }
 
     /* get destination mixer line */
-    DstLine = MMixerGetSourceMixerLineByLineId(MixerInfo, DESTINATION_LINE);
+    DstLine = MMixerGetSourceMixerLineByLineId(MixerInfo, DestinationLineId);
 
     /* sanity check */
     ASSERT(DstLine);
@@ -756,7 +1191,7 @@ MMixerAddMixerControlsToDestinationLine(
     if (NodesCount > 0)
     {
         /* add all nodes as mixer controls to the destination line */
-        Status = MMixerAddMixerControlsToMixerLineByNodeIndexArray(MixerContext, MixerInfo, Topology, DstLine, NodesCount, Nodes);
+        Status = MMixerAddMixerControlsToMixerLineByNodeIndexArray(MixerContext, MixerInfo, hMixer, Topology, DstLine, NodesCount, Nodes);
         if (Status != MM_STATUS_SUCCESS)
         {
             /* failed to add controls */
@@ -776,6 +1211,7 @@ VOID
 MMixerApplyOutputFilterHack(
     IN PMIXER_CONTEXT MixerContext,
     IN LPMIXER_DATA MixerData,
+    IN HANDLE hMixer,
     IN OUT PULONG PinsCount,
     IN OUT PULONG Pins)
 {
@@ -786,7 +1222,7 @@ MMixerApplyOutputFilterHack(
     for(Index = 0; Index < *PinsCount; Index++)
     {
         /* check if it has a physical connection */
-        Status = MMixerGetPhysicalConnection(MixerContext, MixerData->hDevice, Pins[Index], &Connection);
+        Status = MMixerGetPhysicalConnection(MixerContext, hMixer, Pins[Index], &Connection);
 
         if (Status == MM_STATUS_SUCCESS)
         {
@@ -823,7 +1259,7 @@ MMixerHandlePhysicalConnection(
     IN PKSPIN_PHYSICALCONNECTION OutConnection)
 {
     MIXER_STATUS Status;
-    ULONG PinsCount, LineTerminator;
+    ULONG PinsCount, LineTerminator, DestinationLineID;
     PULONG Pins;
     PTOPOLOGY Topology;
 
@@ -840,23 +1276,44 @@ MMixerHandlePhysicalConnection(
 
     DPRINT("Name %S, Pin %lu bInput %lu\n", OutConnection->SymbolicLinkName, OutConnection->Pin, bInput);
 
-    /* store connected mixer handle */
-    MixerInfo->hMixer = MixerData->hDevice;
+    /* sanity check */
+    ASSERT(MixerData->MixerInfo == NULL || MixerData->MixerInfo == MixerInfo);
 
+    /* associate with mixer */
+    MixerData->MixerInfo = MixerInfo;
 
-    Status = MMixerBuildTopology(MixerContext, MixerData, &Topology);
-    if (Status != MM_STATUS_SUCCESS)
+    if (MixerData->Topology == NULL)
     {
-        /* failed to create topology */
-        return Status;
+        /* construct new topology */
+        Status = MMixerBuildTopology(MixerContext, MixerData, &Topology);
+        if (Status != MM_STATUS_SUCCESS)
+        {
+            /* failed to create topology */
+            return Status;
+        }
+
+        /* store topology */
+        MixerData->Topology = Topology;
+    }
+    else
+    {
+        /* re-use existing topology */
+        Topology = MixerData->Topology;
     }
 
-    /* allocate pin index array which will hold all referenced pins */
-    Status = MMixerAllocateTopologyPinArray(MixerContext, Topology, &Pins);
-    ASSERT(Status == MM_STATUS_SUCCESS);
+    /* mark pin as consumed */
+    MMixerSetTopologyPinReserved(Topology, OutConnection->Pin);
 
     if (!bInput)
     {
+        /* allocate pin index array which will hold all referenced pins */
+        Status = MMixerAllocateTopologyPinArray(MixerContext, Topology, &Pins);
+        if (Status != MM_STATUS_SUCCESS)
+        {
+            /* failed to create topology */
+            return Status;
+        }
+
         /* the mixer is an output mixer
          * find end pin of the node path
          */
@@ -878,88 +1335,111 @@ MMixerHandlePhysicalConnection(
          * WorkArround: remove all pin ids which have a physical connection
          * because bridge pins may belong to different render paths
          */
-        MMixerApplyOutputFilterHack(MixerContext, MixerData, &PinsCount, Pins);
+        MMixerApplyOutputFilterHack(MixerContext, MixerData, MixerData->hDevice, &PinsCount, Pins);
 
         /* sanity checks */
         ASSERT(PinsCount != 0);
         ASSERT(PinsCount == 1);
 
         /* create destination line */
-        Status = MMixerBuildMixerDestinationLine(MixerContext, MixerInfo, Pins[0], bInput);
+        Status = MMixerBuildMixerDestinationLine(MixerContext, MixerInfo, MixerData->hDevice, 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, MixerData->hDevice, 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, MixerData->hDevice, Topology, DestinationLineID, LineTerminator);
         }
+
+        /* mark pin as consumed */
+        MMixerSetTopologyPinReserved(Topology, Pins[0]);
+
+        /* free topology pin array */
+        MixerContext->Free(Pins);
     }
     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, MixerData->hDevice, 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, MixerData->hDevice, 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;
+    ULONG Index;
     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;
 
-    /* get mixer name */
-    MMixerGetDeviceName(MixerContext, MixerInfo->MixCaps.szPname, MixerData->hDeviceInterfaceKey);
+        /* 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;
 
-    /* initialize line list */
-    InitializeListHead(&MixerInfo->LineList);
-    InitializeListHead(&MixerInfo->EventList);
+        /* get mixer name */
+        MMixerGetDeviceName(MixerContext, MixerInfo->MixCaps.szPname, MixerData->hDeviceInterfaceKey);
+
+        /* initialize line list */
+        InitializeListHead(&MixerInfo->LineList);
+        InitializeListHead(&MixerInfo->EventList);
+
+        /* associate with mixer data */
+        MixerData->MixerInfo = MixerInfo;
+    }
+
+    /* 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 +1457,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 */
@@ -990,10 +1470,16 @@ MMixerInitializeFilter(
         return Status;
     }
 
+    /* mark all found pins as reserved */
+    for(Index = 0; Index < PinsFound; Index++)
+    {
+        MMixerSetTopologyPinReserved(Topology, Pins[Index]);
+    }
+
     if (bInputMixer)
     {
         /* pre create the mixer destination line for input mixers */
-        Status = MMixerBuildMixerDestinationLine(MixerContext, MixerInfo, Pins[0], bInputMixer);
+        Status = MMixerBuildMixerDestinationLine(MixerContext, MixerInfo, MixerData->hDevice, Pins[0], bInputMixer);
 
         if (Status != MM_STATUS_SUCCESS)
         {
@@ -1027,6 +1513,9 @@ MMixerInitializeFilter(
 
     if (Status == MM_STATUS_SUCCESS)
     {
+        /* mark pin as reserved */
+        MMixerSetTopologyPinReserved(Topology, Pins[0]);
+
         /* topology on the topoloy filter */
         Status = MMixerHandlePhysicalConnection(MixerContext, MixerList, MixerData, MixerInfo, bInputMixer, OutConnection);
 
@@ -1044,26 +1533,91 @@ MMixerInitializeFilter(
     /* 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);
-    }
-    else
-    {
-        /* insert at back */
-        InsertTailList(&MixerList->MixerList, &MixerInfo->Entry);
+        /* increment mixer count */
+        MixerList->MixerListCount++;
     }
 
-    /* increment mixer count */
-    MixerList->MixerListCount++;
-
     /* done */
     return Status;
 }
 
+VOID
+MMixerHandleAlternativeMixers(
+    IN PMIXER_CONTEXT MixerContext,
+    IN PMIXER_LIST MixerList,
+    IN LPMIXER_DATA MixerData,
+    IN PTOPOLOGY Topology)
+{
+    ULONG Index, PinCount, Reserved;
+    MIXER_STATUS Status;
+    ULONG DestinationLineID, LineTerminator;
+    LPMIXERLINE_EXT DstLine;
+
+    DPRINT("DeviceName %S\n", MixerData->DeviceName);
+
+    /* get topology pin count */
+    MMixerGetTopologyPinCount(Topology, &PinCount);
+
+    for(Index = 0; Index < PinCount; Index++)
+    {
+        MMixerIsTopologyPinReserved(Topology, Index, &Reserved);
+
+        /* check if it has already been reserved */
+        if (Reserved == TRUE)
+        {
+            /* pin has already been reserved */
+            continue;
+        }
+
+        DPRINT("MixerName %S Available PinID %lu\n", MixerData->DeviceName, Index);
+
+        /* sanity check */
+        //ASSERT(MixerData->MixerInfo);
+
+        if (!MixerData->MixerInfo)
+        {
+            DPRINT1("Expected mixer info\n");
+            continue;
+        }
+
+        /* build the destination line */
+        Status = MMixerBuildMixerDestinationLine(MixerContext, MixerData->MixerInfo, MixerData->hDevice, Index, TRUE);
+        if (Status != MM_STATUS_SUCCESS)
+        {
+            /* failed to build destination line */
+            continue;
+        }
+
+        /* calculate destination line id */
+        DestinationLineID = (DESTINATION_LINE + MixerData->MixerInfo->MixCaps.cDestinations-1);
+
+        /* add mixer controls to destination line */
+        Status = MMixerAddMixerControlsToDestinationLine(MixerContext, MixerData->MixerInfo, MixerData->hDevice, MixerData->Topology, Index, TRUE, DestinationLineID,  &LineTerminator);
+        if (Status == MM_STATUS_SUCCESS)
+        {
+            /* now add the rest of the source lines */
+            Status = MMixerAddMixerSourceLines(MixerContext, MixerData->MixerInfo, MixerData->hDevice, MixerData->Topology, DestinationLineID, LineTerminator);
+        }
+
+        /* mark pin as consumed */
+        MMixerSetTopologyPinReserved(Topology, Index);
+
+        /* now grab destination line */
+        DstLine = MMixerGetSourceMixerLineByLineId(MixerData->MixerInfo, DestinationLineID);
+
+        /* set type and target as undefined */
+        DstLine->Line.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_UNDEFINED;
+        DstLine->Line.Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
+        DstLine->Line.Target.vDriverVersion = 0;
+        DstLine->Line.Target.wMid = 0;
+        DstLine->Line.Target.wPid = 0;
+    }
+}
+
 MIXER_STATUS
 MMixerSetupFilter(
     IN PMIXER_CONTEXT MixerContext,
@@ -1074,6 +1628,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 +1656,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 +1664,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 +1676,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,8 +1687,10 @@ MMixerSetupFilter(
 
     }
 
+    /* TODO: apply hacks for Wave source line */
+
     /* activate midi devices */
-    MMixerInitializeMidiForFilter(MixerContext, MixerList, MixerData, Topology);
+    //MMixerInitializeMidiForFilter(MixerContext, MixerList, MixerData, Topology);
 
     /* done */
     return Status;
@@ -1175,7 +1736,7 @@ MMixerAddEvent(
 
     /* initialize notification entry */
     EventData->MixerEventContext = MixerEventContext;
-    EventData->MixerEventRoutine;
+    EventData->MixerEventRoutine = MixerEventRoutine;
 
     /* store event */
     InsertTailList(&MixerInfo->EventList, &EventData->Entry);
index e60d149..4809c91 100644 (file)
@@ -11,7 +11,7 @@
 MIXER_STATUS
 MMixerGetPinDataFlowAndCommunication(
     IN PMIXER_CONTEXT MixerContext,
-    IN LPMIXER_DATA MixerData,
+    IN HANDLE hDevice,
     IN ULONG PinId,
     OUT PKSPIN_DATAFLOW DataFlow,
     OUT PKSPIN_COMMUNICATION Communication)
@@ -28,7 +28,7 @@ MMixerGetPinDataFlowAndCommunication(
     Pin.Property.Set = KSPROPSETID_Pin;
 
     /* get pin dataflow */
-    Status = MixerContext->Control(MixerData->hDevice, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSP_PIN), (PVOID)DataFlow, sizeof(KSPIN_DATAFLOW), &BytesReturned);
+    Status = MixerContext->Control(hDevice, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSP_PIN), (PVOID)DataFlow, sizeof(KSPIN_DATAFLOW), &BytesReturned);
     if (Status != MM_STATUS_SUCCESS)
     {
         /* failed to retrieve dataflow */
@@ -39,7 +39,7 @@ MMixerGetPinDataFlowAndCommunication(
     Pin.Property.Id = KSPROPERTY_PIN_COMMUNICATION;
 
     /* get pin communication */
-    Status = MixerContext->Control(MixerData->hDevice, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSP_PIN), (PVOID)Communication, sizeof(KSPIN_COMMUNICATION), &BytesReturned);
+    Status = MixerContext->Control(hDevice, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSP_PIN), (PVOID)Communication, sizeof(KSPIN_COMMUNICATION), &BytesReturned);
 
     return Status;
 }
@@ -142,7 +142,7 @@ MMixerCheckFilterPinMidiSupport(
             IsEqualGUIDAligned(&DataRange->Specifier, &KSDATAFORMAT_SPECIFIER_NONE))
         {
             /* pin supports midi datarange */
-            if (MMixerGetPinDataFlowAndCommunication(MixerContext, MixerData, PinId, &DataFlow, &Communication) == MM_STATUS_SUCCESS)
+            if (MMixerGetPinDataFlowAndCommunication(MixerContext, MixerData->hDevice, PinId, &DataFlow, &Communication) == MM_STATUS_SUCCESS)
             {
                 if (DataFlow == KSPIN_DATAFLOW_IN && Communication == KSPIN_COMMUNICATION_SINK)
                 {
index b04b50c..980a49e 100644 (file)
@@ -89,6 +89,7 @@ MMixerOpen(
     if (Status != MM_STATUS_SUCCESS)
     {
         /* invalid context passed */
+        DPRINT1("invalid context\n");
         return Status;
     }
 
@@ -97,6 +98,7 @@ MMixerOpen(
     if (!MixerInfo)
     {
         /* invalid mixer id */
+        DPRINT1("invalid mixer id %lu\n", MixerId);
         return MM_STATUS_INVALID_PARAMETER;
     }
 
@@ -106,20 +108,21 @@ MMixerOpen(
 
     /* store result */
     *MixerHandle = (HANDLE)MixerInfo;
-
     return MM_STATUS_SUCCESS;
 }
 
 MIXER_STATUS
 MMixerGetLineInfo(
     IN PMIXER_CONTEXT MixerContext,
-    IN  HANDLE MixerHandle,
-    IN  ULONG Flags,
+    IN HANDLE MixerHandle,
+    IN ULONG MixerId,
+    IN ULONG Flags,
     OUT LPMIXERLINEW MixerLine)
 {
     MIXER_STATUS Status;
     LPMIXER_INFO MixerInfo;
     LPMIXERLINE_EXT MixerLineSrc;
+    ULONG DestinationLineID;
 
     /* verify mixer context */
     Status = MMixerVerifyContext(MixerContext);
@@ -129,25 +132,54 @@ MMixerGetLineInfo(
         /* invalid context passed */
         return Status;
     }
+    if ((Flags & (MIXER_OBJECTF_MIXER | MIXER_OBJECTF_HMIXER)) == MIXER_OBJECTF_MIXER)
+    {
+        /* caller passed mixer id */
+        MixerHandle = (HANDLE)MMixerGetMixerInfoByIndex(MixerContext, MixerId);
+
+        if (!MixerHandle)
+        {
+            /* invalid parameter */
+            return MM_STATUS_INVALID_PARAMETER;
+        }
+    }
+
+    if (MixerLine->cbStruct != sizeof(MIXERLINEW))
+    {
+        DPRINT1("MixerLine Expected %lu but got %lu\n", sizeof(MIXERLINEW), MixerLine->cbStruct);
+        return MM_STATUS_INVALID_PARAMETER;
+    }
 
     /* clear hmixer from flags */
     Flags &=~MIXER_OBJECTF_HMIXER;
 
+    DPRINT("MMixerGetLineInfo MixerId %lu Flags %lu\n", MixerId, Flags);
+
     if (Flags == MIXER_GETLINEINFOF_DESTINATION)
     {
         /* cast to mixer info */
         MixerInfo = (LPMIXER_INFO)MixerHandle;
 
-        if (MixerLine->dwDestination != 0)
+        /* calculate destination line id */
+        DestinationLineID = (MixerLine->dwDestination + DESTINATION_LINE);
+
+        /* get destination line */
+        MixerLineSrc = MMixerGetSourceMixerLineByLineId(MixerInfo, DestinationLineID);
+
+        if (MixerLineSrc == NULL)
         {
-            /* destination line member must be zero */
-            return MM_STATUS_INVALID_PARAMETER;
+            DPRINT1("MixerCaps Name %S DestinationLineCount %lu dwDestination %lu not found\n", MixerInfo->MixCaps.szPname, MixerInfo->MixCaps.cDestinations, MixerLine->dwDestination);
+            return MM_STATUS_UNSUCCESSFUL;
         }
-
-        MixerLineSrc = MMixerGetSourceMixerLineByLineId(MixerInfo, DESTINATION_LINE);
-        ASSERT(MixerLineSrc);
+        /* copy mixer line */
         MixerContext->Copy(MixerLine, &MixerLineSrc->Line, sizeof(MIXERLINEW));
 
+        /* make sure it is null terminated */
+        MixerLine->szName[MIXER_LONG_NAME_CHARS-1] = L'\0';
+        MixerLine->szShortName[MIXER_SHORT_NAME_CHARS-1] = L'\0';
+        MixerLine->Target.szPname[MAXPNAMELEN-1] = L'\0';
+
+        /* done */
         return MM_STATUS_SUCCESS;
     }
     else if (Flags == MIXER_GETLINEINFOF_SOURCE)
@@ -155,41 +187,75 @@ MMixerGetLineInfo(
         /* cast to mixer info */
         MixerInfo = (LPMIXER_INFO)MixerHandle;
 
+        /* calculate destination line id */
+        DestinationLineID = (MixerLine->dwDestination + DESTINATION_LINE);
 
-        MixerLineSrc = MMixerGetSourceMixerLineByLineId(MixerInfo, DESTINATION_LINE);
-        ASSERT(MixerLineSrc);
+        /* get destination line */
+        MixerLineSrc = MMixerGetSourceMixerLineByLineId(MixerInfo, DestinationLineID);
 
-        if (MixerLine->dwSource >= MixerLineSrc->Line.cConnections)
+        if (MixerLineSrc == NULL)
         {
-            DPRINT("dwSource %u > Destinations %u\n", MixerLine->dwSource, MixerLineSrc->Line.cConnections);
-
-            /* invalid parameter */
-            return MM_STATUS_INVALID_PARAMETER;
+            DPRINT1("MixerCaps Name %S DestinationLineCount %lu dwDestination %lu not found\n", MixerInfo->MixCaps.szPname, MixerInfo->MixCaps.cDestinations, MixerLine->dwDestination);
+            return MM_STATUS_UNSUCCESSFUL;
         }
 
-        MixerLineSrc = MMixerGetSourceMixerLineByLineId(MixerInfo, MixerLine->dwSource * 0x10000);
-        if (MixerLineSrc)
+        /* check if dwSource is out of bounds */
+        if (MixerLine->dwSource >= MixerLineSrc->Line.cConnections)
         {
-            DPRINT("Line %u Name %S\n", MixerLineSrc->Line.dwSource, MixerLineSrc->Line.szName);
-            MixerContext->Copy(MixerLine, &MixerLineSrc->Line, sizeof(MIXERLINEW));
-            return MM_STATUS_SUCCESS;
+            DPRINT1("MixerCaps Name %S MixerLineName %S Connections %lu dwSource %lu not found\n", MixerInfo->MixCaps.szPname, MixerLineSrc->Line.szName, MixerLineSrc->Line.cConnections, MixerLine->dwSource);
+            return MM_STATUS_UNSUCCESSFUL;
         }
-        return MM_STATUS_UNSUCCESSFUL;
+
+        /* calculate destination line id */
+        DestinationLineID = (MixerLine->dwSource * SOURCE_LINE) + MixerLine->dwDestination;
+
+        DPRINT("MixerName %S cDestinations %lu MixerLineName %S cConnections %lu dwSource %lu dwDestination %lu ID %lx\n", MixerInfo->MixCaps.szPname, MixerInfo->MixCaps.cDestinations,
+                                                                                                                            MixerLineSrc->Line.szName, MixerLineSrc->Line.cConnections,
+                                                                                                                            MixerLine->dwSource, MixerLine->dwDestination,
+                                                                                                                            DestinationLineID);
+        /* get target destination line id */
+        MixerLineSrc = MMixerGetSourceMixerLineByLineId(MixerInfo, DestinationLineID);
+
+        /* sanity check */
+        ASSERT(MixerLineSrc);
+
+        DPRINT("Line %u Name %S\n", MixerLineSrc->Line.dwSource, MixerLineSrc->Line.szName);
+
+        /* copy mixer line */
+        MixerContext->Copy(MixerLine, &MixerLineSrc->Line, sizeof(MIXERLINEW));
+
+        /* make sure it is null terminated */
+        MixerLine->szName[MIXER_LONG_NAME_CHARS-1] = L'\0';
+        MixerLine->szShortName[MIXER_SHORT_NAME_CHARS-1] = L'\0';
+        MixerLine->Target.szPname[MAXPNAMELEN-1] = L'\0';
+
+        /* done */
+        return MM_STATUS_SUCCESS;
     }
     else if (Flags == MIXER_GETLINEINFOF_LINEID)
     {
         /* cast to mixer info */
         MixerInfo = (LPMIXER_INFO)MixerHandle;
 
+        /* try to find line */
         MixerLineSrc = MMixerGetSourceMixerLineByLineId(MixerInfo, MixerLine->dwLineID);
         if (!MixerLineSrc)
         {
             /* invalid parameter */
+            DPRINT1("MMixerGetLineInfo: MixerName %S Line not found 0x%lx\n", MixerInfo->MixCaps.szPname, MixerLine->dwLineID);
             return MM_STATUS_INVALID_PARAMETER;
         }
 
-        /* copy cached data */
+        DPRINT("Line %u Name %S\n", MixerLineSrc->Line.dwSource, MixerLineSrc->Line.szName);
+
+        /* copy mixer line*/
         MixerContext->Copy(MixerLine, &MixerLineSrc->Line, sizeof(MIXERLINEW));
+
+        /* make sure it is null terminated */
+        MixerLine->szName[MIXER_LONG_NAME_CHARS-1] = L'\0';
+        MixerLine->szShortName[MIXER_SHORT_NAME_CHARS-1] = L'\0';
+        MixerLine->Target.szPname[MAXPNAMELEN-1] = L'\0';
+
         return MM_STATUS_SUCCESS;
     }
     else if (Flags == MIXER_GETLINEINFOF_COMPONENTTYPE)
@@ -197,6 +263,7 @@ MMixerGetLineInfo(
         /* cast to mixer info */
         MixerInfo = (LPMIXER_INFO)MixerHandle;
 
+        /* find mixer line by component type */
         MixerLineSrc = MMixerGetSourceMixerLineByComponentType(MixerInfo, MixerLine->dwComponentType);
         if (!MixerLineSrc)
         {
@@ -204,12 +271,25 @@ MMixerGetLineInfo(
             return MM_STATUS_UNSUCCESSFUL;
         }
 
-        ASSERT(MixerLineSrc);
-
-        /* copy cached data */
+        /* copy mixer line */
         MixerContext->Copy(MixerLine, &MixerLineSrc->Line, sizeof(MIXERLINEW));
+
+        /* make sure it is null terminated */
+        MixerLine->szName[MIXER_LONG_NAME_CHARS-1] = L'\0';
+        MixerLine->szShortName[MIXER_SHORT_NAME_CHARS-1] = L'\0';
+        MixerLine->Target.szPname[MAXPNAMELEN-1] = L'\0';
+
+        /* done */
         return MM_STATUS_SUCCESS;
     }
+    else if (Flags == MIXER_GETLINEINFOF_TARGETTYPE)
+    {
+        DPRINT1("MIXER_GETLINEINFOF_TARGETTYPE handling is unimplemented\n");
+    }
+    else
+    {
+        DPRINT1("Unknown Flags %lx handling is unimplemented\n", Flags);
+    }
 
     return MM_STATUS_NOT_IMPLEMENTED;
 }
@@ -218,13 +298,15 @@ MIXER_STATUS
 MMixerGetLineControls(
     IN PMIXER_CONTEXT MixerContext,
     IN HANDLE MixerHandle,
+    IN ULONG MixerId,
     IN ULONG Flags,
     OUT LPMIXERLINECONTROLSW MixerLineControls)
 {
     LPMIXER_INFO MixerInfo;
     LPMIXERLINE_EXT MixerLineSrc;
-    LPMIXERCONTROLW MixerControl;
+    LPMIXERCONTROL_EXT MixerControl;
     MIXER_STATUS Status;
+    PLIST_ENTRY Entry;
     ULONG Index;
 
     /* verify mixer context */
@@ -236,23 +318,75 @@ MMixerGetLineControls(
         return Status;
     }
 
+    if (MixerLineControls->cbStruct != sizeof(MIXERLINECONTROLSW))
+    {
+        DPRINT1("Invalid MixerLineControls cbStruct passed %lu expected %lu\n", MixerLineControls->cbStruct, sizeof(MIXERLINECONTROLSW));
+        /* invalid parameter */
+        return MM_STATUS_INVALID_PARAMETER;
+    }
+
+    if (MixerLineControls->cbmxctrl != sizeof(MIXERCONTROLW))
+    {
+        DPRINT1("Invalid MixerLineControls cbmxctrl passed %lu expected %lu\n", MixerLineControls->cbStruct, sizeof(MIXERLINECONTROLSW));
+        /* invalid parameter */
+        return MM_STATUS_INVALID_PARAMETER;
+    }
+
+    if ((Flags & (MIXER_OBJECTF_MIXER | MIXER_OBJECTF_HMIXER)) == MIXER_OBJECTF_MIXER)
+    {
+        /* caller passed mixer id */
+        MixerHandle = (HANDLE)MMixerGetMixerInfoByIndex(MixerContext, MixerId);
+
+        if (!MixerHandle)
+        {
+            /* invalid parameter */
+            return MM_STATUS_INVALID_PARAMETER;
+        }
+    }
+
     Flags &= ~MIXER_OBJECTF_HMIXER;
 
+    DPRINT("MMixerGetLineControls MixerId %lu Flags %lu\n", MixerId, Flags);
+
     if (Flags == MIXER_GETLINECONTROLSF_ALL)
     {
         /* cast to mixer info */
         MixerInfo = (LPMIXER_INFO)MixerHandle;
 
+        /* get mixer line */
         MixerLineSrc = MMixerGetSourceMixerLineByLineId(MixerInfo, MixerLineControls->dwLineID);
 
         if (!MixerLineSrc)
         {
             /* invalid line id */
+            DPRINT("MMixerGetLineControls Line not found %lx\n", MixerLineControls->dwLineID);
+            return MM_STATUS_INVALID_PARAMETER;
+        }
+
+        if (MixerLineSrc->Line.cControls != MixerLineControls->cControls)
+        {
+            /* invalid parameter */
+            DPRINT1("Invalid control count %lu expected %lu\n", MixerLineControls->cControls, MixerLineSrc->Line.cControls);
             return MM_STATUS_INVALID_PARAMETER;
         }
+
         /* copy line control(s) */
-        MixerContext->Copy(MixerLineControls->pamxctrl, MixerLineSrc->LineControls, min(MixerLineSrc->Line.cControls, MixerLineControls->cControls) * sizeof(MIXERCONTROLW));
+        Entry = MixerLineSrc->ControlsList.Flink;
+        Index = 0;
+        while(Entry != &MixerLineSrc->ControlsList)
+        {
+            /* get mixer control */
+            MixerControl = (LPMIXERCONTROL_EXT)CONTAINING_RECORD(Entry, MIXERCONTROL_EXT, Entry);
+
+            /* copy mixer control */
+            MixerContext->Copy(&MixerLineControls->pamxctrl[Index], &MixerControl->Control, sizeof(MIXERCONTROLW));
 
+            /* move to next */
+            Entry = Entry->Flink;
+
+            /* increment mixer control offset */
+            Index++;
+        }
         return MM_STATUS_SUCCESS;
     }
     else if (Flags == MIXER_GETLINECONTROLSF_ONEBYTYPE)
@@ -260,29 +394,38 @@ MMixerGetLineControls(
         /* cast to mixer info */
         MixerInfo = (LPMIXER_INFO)MixerHandle;
 
+        /* get mixer line */
         MixerLineSrc = MMixerGetSourceMixerLineByLineId(MixerInfo, MixerLineControls->dwLineID);
 
         if (!MixerLineSrc)
         {
             /* invalid line id */
+            DPRINT1("MMixerGetLineControls Line not found %lx\n", MixerLineControls->dwLineID);
             return MM_STATUS_INVALID_PARAMETER;
         }
 
-        ASSERT(MixerLineSrc);
+        /* sanity checks */
+        ASSERT(MixerLineControls->cControls == 1);
+        ASSERT(MixerLineControls->cbmxctrl == sizeof(MIXERCONTROLW));
+        ASSERT(MixerLineControls->pamxctrl != NULL);
 
-        Index = 0;
-        for(Index = 0; Index < MixerLineSrc->Line.cControls; Index++)
+        Entry = MixerLineSrc->ControlsList.Flink;
+        while(Entry != &MixerLineSrc->ControlsList)
         {
-            DPRINT("dwControlType %x\n", MixerLineSrc->LineControls[Index].dwControlType);
-            if (MixerLineControls->dwControlType == MixerLineSrc->LineControls[Index].dwControlType)
+            MixerControl = (LPMIXERCONTROL_EXT)CONTAINING_RECORD(Entry, MIXERCONTROL_EXT, Entry);
+            if (MixerLineControls->dwControlType == MixerControl->Control.dwControlType)
             {
                 /* found a control with that type */
-                MixerContext->Copy(MixerLineControls->pamxctrl, &MixerLineSrc->LineControls[Index], sizeof(MIXERCONTROLW));
+                MixerContext->Copy(MixerLineControls->pamxctrl, &MixerControl->Control, sizeof(MIXERCONTROLW));
                 return MM_STATUS_SUCCESS;
             }
-        }
-        DPRINT("DeviceInfo->u.MixControls.dwControlType %x not found in Line %x cControls %u \n", MixerLineControls->dwControlType, MixerLineControls->dwLineID, MixerLineSrc->Line.cControls);
-        return MM_STATUS_UNSUCCESSFUL;
+
+            /* move to next entry */
+            Entry = Entry->Flink;
+         }
+
+         DPRINT("DeviceInfo->u.MixControls.dwControlType %x not found in Line %x cControls %u \n", MixerLineControls->dwControlType, MixerLineControls->dwLineID, MixerLineSrc->Line.cControls);
+         return MM_STATUS_UNSUCCESSFUL;
     }
     else if (Flags == MIXER_GETLINECONTROLSF_ONEBYID)
     {
@@ -294,15 +437,24 @@ MMixerGetLineControls(
         if (Status != MM_STATUS_SUCCESS)
         {
             /* invalid parameter */
+            DPRINT("MMixerGetLineControls ControlID not found %lx\n", MixerLineControls->dwLineID);
             return MM_STATUS_INVALID_PARAMETER;
         }
 
+        ASSERT(MixerLineControls->cControls == 1);
+        ASSERT(MixerLineControls->cbmxctrl == sizeof(MIXERCONTROLW));
+        ASSERT(MixerLineControls->pamxctrl != NULL);
+
+       DPRINT("MMixerGetLineControls ControlID %lx ControlType %lx Name %S\n", MixerControl->Control.dwControlID, MixerControl->Control.dwControlType, MixerControl->Control.szName);
+
         /* copy the controls */
-        MixerContext->Copy(MixerLineControls->pamxctrl, MixerControl, sizeof(MIXERCONTROLW));
+        MixerContext->Copy(MixerLineControls->pamxctrl, &MixerControl->Control, sizeof(MIXERCONTROLW));
+        MixerLineControls->pamxctrl->szName[MIXER_LONG_NAME_CHARS-1] = L'\0';
+        MixerLineControls->pamxctrl->szShortName[MIXER_SHORT_NAME_CHARS-1] = L'\0';
+
         return MM_STATUS_SUCCESS;
     }
-
-
+    UNIMPLEMENTED
     return MM_STATUS_NOT_IMPLEMENTED;
 }
 
@@ -310,6 +462,7 @@ MIXER_STATUS
 MMixerSetControlDetails(
     IN PMIXER_CONTEXT MixerContext,
     IN HANDLE MixerHandle,
+    IN ULONG MixerId,
     IN ULONG Flags,
     OUT LPMIXERCONTROLDETAILS MixerControlDetails)
 {
@@ -317,7 +470,7 @@ MMixerSetControlDetails(
     ULONG NodeId;
     LPMIXER_INFO MixerInfo;
     LPMIXERLINE_EXT MixerLine;
-    LPMIXERCONTROLW MixerControl;
+    LPMIXERCONTROL_EXT MixerControl;
 
     /* verify mixer context */
     Status = MMixerVerifyContext(MixerContext);
@@ -325,9 +478,23 @@ MMixerSetControlDetails(
     if (Status != MM_STATUS_SUCCESS)
     {
         /* invalid context passed */
+        DPRINT1("invalid context\n");
         return Status;
     }
 
+    if ((Flags & (MIXER_OBJECTF_MIXER | MIXER_OBJECTF_HMIXER)) == MIXER_OBJECTF_MIXER)
+    {
+        /* caller passed mixer id */
+        MixerHandle = (HANDLE)MMixerGetMixerInfoByIndex(MixerContext, MixerId);
+
+        if (!MixerHandle)
+        {
+            /* invalid parameter */
+            DPRINT1("invalid handle\n");
+            return MM_STATUS_INVALID_PARAMETER;
+        }
+    }
+
     /* get mixer info */
     MixerInfo = (LPMIXER_INFO)MixerHandle;
 
@@ -338,16 +505,21 @@ MMixerSetControlDetails(
     if (Status != MM_STATUS_SUCCESS)
     {
         /* failed to find control id */
+        DPRINT1("invalid control id %lu\n", MixerControlDetails->dwControlID);
         return MM_STATUS_INVALID_PARAMETER;
     }
 
-    switch(MixerControl->dwControlType)
+    DPRINT("MMixerSetControlDetails ControlType %lx MixerControlName %S MixerLineName %S NodeID %lu\n", MixerControl->Control.dwControlType, MixerControl->Control.szName, MixerLine->Line.szName, NodeId);
+    switch(MixerControl->Control.dwControlType)
     {
         case MIXERCONTROL_CONTROLTYPE_MUTE:
-            Status = MMixerSetGetMuteControlDetails(MixerContext, MixerInfo->hMixer, NodeId, MixerLine->Line.dwLineID, MixerControlDetails, TRUE);
+            Status = MMixerSetGetMuteControlDetails(MixerContext, MixerInfo, MixerControl, MixerLine->Line.dwLineID, MixerControlDetails, TRUE);
             break;
         case MIXERCONTROL_CONTROLTYPE_VOLUME:
-            Status = MMixerSetGetVolumeControlDetails(MixerContext, MixerInfo->hMixer, NodeId, TRUE, MixerControl, MixerControlDetails, MixerLine);
+            Status = MMixerSetGetVolumeControlDetails(MixerContext, MixerInfo, NodeId, TRUE, MixerControl, MixerControlDetails, MixerLine);
+            break;
+        case MIXERCONTROL_CONTROLTYPE_MUX:
+            Status = MMixerSetGetMuxControlDetails(MixerContext, MixerInfo, NodeId, TRUE, Flags, MixerControl, MixerControlDetails, MixerLine);
             break;
         default:
             Status = MM_STATUS_NOT_IMPLEMENTED;
@@ -360,6 +532,7 @@ MIXER_STATUS
 MMixerGetControlDetails(
     IN PMIXER_CONTEXT MixerContext,
     IN HANDLE MixerHandle,
+    IN ULONG MixerId,
     IN ULONG Flags,
     OUT LPMIXERCONTROLDETAILS MixerControlDetails)
 {
@@ -367,7 +540,7 @@ MMixerGetControlDetails(
     ULONG NodeId;
     LPMIXER_INFO MixerInfo;
     LPMIXERLINE_EXT MixerLine;
-    LPMIXERCONTROLW MixerControl;
+    LPMIXERCONTROL_EXT MixerControl;
 
     /* verify mixer context */
     Status = MMixerVerifyContext(MixerContext);
@@ -378,6 +551,18 @@ MMixerGetControlDetails(
         return Status;
     }
 
+    if ((Flags & (MIXER_OBJECTF_MIXER | MIXER_OBJECTF_HMIXER)) == MIXER_OBJECTF_MIXER)
+    {
+        /* caller passed mixer id */
+        MixerHandle = (HANDLE)MMixerGetMixerInfoByIndex(MixerContext, MixerId);
+
+        if (!MixerHandle)
+        {
+            /* invalid parameter */
+            return MM_STATUS_INVALID_PARAMETER;
+        }
+    }
+
     /* get mixer info */
     MixerInfo = (LPMIXER_INFO)MixerHandle;
 
@@ -391,21 +576,162 @@ MMixerGetControlDetails(
         return MM_STATUS_INVALID_PARAMETER;
     }
 
-    switch(MixerControl->dwControlType)
+    switch(MixerControl->Control.dwControlType)
     {
         case MIXERCONTROL_CONTROLTYPE_MUTE:
-            Status = MMixerSetGetMuteControlDetails(MixerContext, MixerInfo, NodeId, MixerLine->Line.dwLineID, MixerControlDetails, FALSE);
+            Status = MMixerSetGetMuteControlDetails(MixerContext, MixerInfo, MixerControl, MixerLine->Line.dwLineID, MixerControlDetails, FALSE);
             break;
         case MIXERCONTROL_CONTROLTYPE_VOLUME:
             Status = MMixerSetGetVolumeControlDetails(MixerContext, MixerInfo, NodeId, FALSE, MixerControl, MixerControlDetails, MixerLine);
             break;
+        case MIXERCONTROL_CONTROLTYPE_ONOFF:
+            DPRINT1("Not Implemented MIXERCONTROL_CONTROLTYPE_ONOFF\n");
+            break;
+        case MIXERCONTROL_CONTROLTYPE_MUX:
+            Status = MMixerSetGetMuxControlDetails(MixerContext, MixerInfo, NodeId, FALSE, Flags, MixerControl, MixerControlDetails, MixerLine);
+            break;
+
         default:
             Status = MM_STATUS_NOT_IMPLEMENTED;
+            DPRINT1("ControlType %lx not implemented\n", MixerControl->Control.dwControlType);
     }
 
     return Status;
 }
 
+VOID
+MMixerPrintMixerLineControls(
+    IN LPMIXERLINE_EXT MixerLine)
+{
+    PLIST_ENTRY Entry;
+    LPMIXERCONTROL_EXT MixerControl;
+    ULONG Index = 0;
+
+    Entry = MixerLine->ControlsList.Flink;
+    while(Entry != &MixerLine->ControlsList)
+    {
+        MixerControl = (LPMIXERCONTROL_EXT)CONTAINING_RECORD(Entry, MIXERCONTROL_EXT, Entry);
+
+        DPRINT1("\n");
+        DPRINT1("Control Index: %lu\n", Index);
+        DPRINT("\n");
+        DPRINT1("cbStruct %u\n", MixerControl->Control.cbStruct);
+        DPRINT1("dwControlID %lu\n", MixerControl->Control.dwControlID);
+        DPRINT1("dwControlType %lx\n", MixerControl->Control.dwControlType);
+        DPRINT1("fdwControl %lu\n", MixerControl->Control.fdwControl);
+        DPRINT1("cMultipleItems %lu\n", MixerControl->Control.cMultipleItems);
+        DPRINT1("szShortName %S\n", MixerControl->Control.szShortName);
+        DPRINT1("szName %S\n", MixerControl->Control.szName);
+        DPRINT1("Bounds.dwMinimum %lu\n", MixerControl->Control.Bounds.dwMinimum);
+        DPRINT1("Bounds.dwMaximum %lu\n", MixerControl->Control.Bounds.dwMaximum);
+
+        DPRINT1("Metrics.Reserved[0] %lu\n", MixerControl->Control.Metrics.dwReserved[0]);
+        DPRINT1("Metrics.Reserved[1] %lu\n", MixerControl->Control.Metrics.dwReserved[1]);
+        DPRINT1("Metrics.Reserved[2] %lu\n", MixerControl->Control.Metrics.dwReserved[2]);
+        DPRINT1("Metrics.Reserved[3] %lu\n", MixerControl->Control.Metrics.dwReserved[3]);
+        DPRINT1("Metrics.Reserved[4] %lu\n", MixerControl->Control.Metrics.dwReserved[4]);
+        DPRINT1("Metrics.Reserved[5] %lu\n", MixerControl->Control.Metrics.dwReserved[5]);
+
+        Entry = Entry->Flink;
+        Index++;
+    }
+}
+
+VOID
+MMixerPrintMixers(
+    IN PMIXER_CONTEXT MixerContext,
+    IN PMIXER_LIST MixerList)
+{
+    ULONG Index, SubIndex, DestinationLineID, SrcIndex;
+    LPMIXER_INFO MixerInfo;
+    LPMIXERLINE_EXT DstMixerLine, SrcMixerLine;
+
+    DPRINT1("MixerList %p\n", MixerList);
+    DPRINT1("MidiInCount %lu\n", MixerList->MidiInListCount);
+    DPRINT1("MidiOutCount %lu\n", MixerList->MidiOutListCount);
+    DPRINT1("WaveInCount %lu\n", MixerList->WaveInListCount);
+    DPRINT1("WaveOutCount %lu\n", MixerList->WaveOutListCount);
+    DPRINT1("MixerCount %p\n", MixerList->MixerListCount);
+
+
+    for(Index = 0; Index < MixerList->MixerListCount; Index++)
+    {
+        /* get mixer info */
+        MixerInfo = MMixerGetMixerInfoByIndex(MixerContext, Index);
+
+        ASSERT(MixerInfo);
+        DPRINT1("\n");
+        DPRINT1("Name :%S\n", MixerInfo->MixCaps.szPname);
+        DPRINT1("cDestinations: %lu\n", MixerInfo->MixCaps.cDestinations);
+        DPRINT1("fdwSupport %lu\n", MixerInfo->MixCaps.fdwSupport);
+        DPRINT1("vDriverVersion %lx\n", MixerInfo->MixCaps.vDriverVersion);
+        DPRINT1("wMid %lx\n", MixerInfo->MixCaps.wMid);
+        DPRINT1("wPid %lx\n", MixerInfo->MixCaps.wPid);
+
+        for(SubIndex = 0; SubIndex < MixerInfo->MixCaps.cDestinations; SubIndex++)
+        {
+            /* calculate destination line id */
+            DestinationLineID = (SubIndex + DESTINATION_LINE);
+
+            /* get destination line */
+            DstMixerLine = MMixerGetSourceMixerLineByLineId(MixerInfo, DestinationLineID);
+            DPRINT1("//----------------------------------------------------------------------------------------------\n");
+            DPRINT1("\n");
+            DPRINT1("Destination Index %lu\n", SubIndex);
+            DPRINT1("\n");
+            DPRINT1("cChannels %lu\n", DstMixerLine->Line.cChannels);
+            DPRINT1("cConnections %lu\n", DstMixerLine->Line.cConnections);
+            DPRINT1("cControls %lu\n", DstMixerLine->Line.cControls);
+            DPRINT1("dwComponentType %lx\n", DstMixerLine->Line.dwComponentType);
+            DPRINT1("dwDestination %lu\n", DstMixerLine->Line.dwDestination);
+            DPRINT1("dwLineID %lx\n", DstMixerLine->Line.dwLineID);
+            DPRINT1("dwSource %lx\n", DstMixerLine->Line.dwSource);
+            DPRINT1("dwUser %lu\n", DstMixerLine->Line.dwUser);
+            DPRINT1("fdwLine %lu\n", DstMixerLine->Line.fdwLine);
+            DPRINT1("szName %S\n", DstMixerLine->Line.szName);
+            DPRINT1("szShortName %S\n", DstMixerLine->Line.szShortName);
+            DPRINT1("Target.dwDeviceId %lu\n", DstMixerLine->Line.Target.dwDeviceID);
+            DPRINT1("Target.dwType %lu\n", DstMixerLine->Line.Target.dwType);
+            DPRINT1("Target.szName %S\n", DstMixerLine->Line.Target.szPname);
+            DPRINT1("Target.vDriverVersion %lx\n", DstMixerLine->Line.Target.vDriverVersion);
+            DPRINT1("Target.wMid %lx\n", DstMixerLine->Line.Target.wMid );
+            DPRINT1("Target.wPid %lx\n", DstMixerLine->Line.Target.wPid);
+            MMixerPrintMixerLineControls(DstMixerLine);
+
+            for(SrcIndex = 0; SrcIndex < DstMixerLine->Line.cConnections; SrcIndex++)
+            {
+                /* calculate destination line id */
+                DestinationLineID = (SOURCE_LINE * SrcIndex) + SubIndex;
+
+                /* get source line */
+                SrcMixerLine = MMixerGetSourceMixerLineByLineId(MixerInfo, DestinationLineID);
+                DPRINT1("//==============================================================================================\n");
+                DPRINT1("\n");
+                DPRINT1("SrcLineIndex : %lu\n", SrcIndex);
+                DPRINT1("\n");
+                DPRINT1("cChannels %lu\n", SrcMixerLine->Line.cChannels);
+                DPRINT1("cConnections %lu\n", SrcMixerLine->Line.cConnections);
+                DPRINT1("cControls %lu\n", SrcMixerLine->Line.cControls);
+                DPRINT1("dwComponentType %lx\n", SrcMixerLine->Line.dwComponentType);
+                DPRINT1("dwDestination %lu\n", SrcMixerLine->Line.dwDestination);
+                DPRINT1("dwLineID %lx\n", SrcMixerLine->Line.dwLineID);
+                DPRINT1("dwSource %lx\n", SrcMixerLine->Line.dwSource);
+                DPRINT1("dwUser %lu\n", SrcMixerLine->Line.dwUser);
+                DPRINT1("fdwLine %lu\n", SrcMixerLine->Line.fdwLine);
+                DPRINT1("szName %S\n", SrcMixerLine->Line.szName);
+                DPRINT1("szShortName %S\n", SrcMixerLine->Line.szShortName);
+                DPRINT1("Target.dwDeviceId %lu\n", SrcMixerLine->Line.Target.dwDeviceID);
+                DPRINT1("Target.dwType %lu\n", SrcMixerLine->Line.Target.dwType);
+                DPRINT1("Target.szName %S\n", SrcMixerLine->Line.Target.szPname);
+                DPRINT1("Target.vDriverVersion %lx\n", SrcMixerLine->Line.Target.vDriverVersion);
+                DPRINT1("Target.wMid %lx\n", SrcMixerLine->Line.Target.wMid );
+                DPRINT1("Target.wPid %lx\n", SrcMixerLine->Line.Target.wPid);
+                MMixerPrintMixerLineControls(SrcMixerLine);
+            }
+        }
+    }
+}
+
 MIXER_STATUS
 MMixerInitialize(
     IN PMIXER_CONTEXT MixerContext,
@@ -507,6 +833,18 @@ MMixerInitialize(
         Entry = Entry->Flink;
     }
 
+    Entry = MixerList->MixerData.Flink;
+    while(Entry != &MixerList->MixerData)
+    {
+        MixerData = (LPMIXER_DATA)CONTAINING_RECORD(Entry, MIXER_DATA, Entry);
+
+        /* now handle alternative mixer types */
+        MMixerHandleAlternativeMixers(MixerContext, MixerList, MixerData, MixerData->Topology);
+        Entry = Entry->Flink;
+    }
+
+    //MMixerPrintMixers(MixerContext, MixerList);
+
     /* done */
     return MM_STATUS_SUCCESS;
 }
index 846d4e9..5cd530a 100644 (file)
@@ -47,7 +47,7 @@ typedef MIXER_STATUS(*PMIXER_CLOSE)(
 typedef MIXER_STATUS(*PMIXER_CLOSEKEY)(
     IN HANDLE hKey);
 
-typedef VOID (*PMIXER_EVENT)(
+typedef VOID (CALLBACK *PMIXER_EVENT)(
     IN PVOID MixerEventContext,
     IN HANDLE hMixer,
     IN ULONG NotificationType,
@@ -149,7 +149,8 @@ MMixerOpen(
 MIXER_STATUS
 MMixerGetLineInfo(
     IN PMIXER_CONTEXT MixerContext,
-    IN  HANDLE MixerHandle,
+    IN HANDLE MixerHandle,
+    IN ULONG MixerId,
     IN  ULONG Flags,
     OUT LPMIXERLINEW MixerLine);
 
@@ -157,6 +158,7 @@ MIXER_STATUS
 MMixerGetLineControls(
     IN PMIXER_CONTEXT MixerContext,
     IN HANDLE MixerHandle,
+    IN ULONG MixerId,
     IN ULONG Flags,
     OUT LPMIXERLINECONTROLSW MixerLineControls);
 
@@ -164,6 +166,7 @@ MIXER_STATUS
 MMixerSetControlDetails(
     IN PMIXER_CONTEXT MixerContext,
     IN HANDLE MixerHandle,
+    IN ULONG MixerId,
     IN ULONG Flags,
     OUT LPMIXERCONTROLDETAILS MixerControlDetails);
 
@@ -171,6 +174,7 @@ MIXER_STATUS
 MMixerGetControlDetails(
     IN PMIXER_CONTEXT MixerContext,
     IN HANDLE MixerHandle,
+    IN ULONG MixerId,
     IN ULONG Flags,
     OUT LPMIXERCONTROLDETAILS MixerControlDetails);
 
@@ -202,6 +206,12 @@ MMixerSetWaveStatus(
     IN HANDLE PinHandle,
     IN KSSTATE State);
 
+MIXER_STATUS
+MMixerSetWaveResetState(
+    IN PMIXER_CONTEXT MixerContext,
+    IN HANDLE PinHandle,
+    IN ULONG bBegin);
+
 MIXER_STATUS
 MMixerGetWaveDevicePath(
     IN PMIXER_CONTEXT MixerContext,
index 8a4855f..3746f6d 100644 (file)
@@ -27,6 +27,7 @@ typedef struct __TOPOLOGY_NODE__
 
     ULONG NodeConnectedFromCount;
     struct __TOPOLOGY_NODE__ ** NodeConnectedFrom;
+    PULONG LogicalPinNodeConnectedFrom;
 
     ULONG PinConnectedFromCount;
     PULONG PinConnectedFrom;
@@ -55,6 +56,7 @@ typedef struct
     PULONG PinConnectedTo;
 
     ULONG Visited;
+    ULONG Reserved;
 }PIN, *PPIN;
 
 
@@ -72,7 +74,6 @@ typedef struct
 {
     LIST_ENTRY    Entry;
     MIXERCAPSW    MixCaps;
-    HANDLE        hMixer;
     LIST_ENTRY    LineList;
     ULONG         ControlId;
     LIST_ENTRY    EventList;
@@ -81,12 +82,19 @@ typedef struct
 typedef struct
 {
     LIST_ENTRY Entry;
-    ULONG PinId;
+    MIXERCONTROLW Control;
+    ULONG NodeID;
     HANDLE hDevice;
+    PVOID ExtraData;
+}MIXERCONTROL_EXT, *LPMIXERCONTROL_EXT;
+
+typedef struct
+{
+    LIST_ENTRY Entry;
+    ULONG PinId;
     MIXERLINEW Line;
-    LPMIXERCONTROLW LineControls;
-    PULONG          NodeIds;
-    LIST_ENTRY LineControlsExtraData;
+    LIST_ENTRY ControlsList;
+
 }MIXERLINE_EXT, *LPMIXERLINE_EXT;
 
 typedef struct
@@ -114,6 +122,7 @@ typedef struct
     HANDLE hDeviceInterfaceKey;
     LPWSTR DeviceName;
     PTOPOLOGY Topology;
+    LPMIXER_INFO MixerInfo;
 }MIXER_DATA, *LPMIXER_DATA;
 
 typedef struct
@@ -170,8 +179,8 @@ typedef struct
 
 }EVENT_NOTIFICATION_ENTRY, *PEVENT_NOTIFICATION_ENTRY;
 
-#define DESTINATION_LINE 0xFFFF0000
-
+#define DESTINATION_LINE (0xFFFF0000)
+#define SOURCE_LINE (0x10000)
 ULONG
 MMixerGetFilterPinCount(
     IN PMIXER_CONTEXT MixerContext,
@@ -270,14 +279,14 @@ MMixerGetMixerControlById(
     LPMIXER_INFO MixerInfo,
     DWORD dwControlID,
     LPMIXERLINE_EXT *MixerLine,
-    LPMIXERCONTROLW *MixerControl,
+    LPMIXERCONTROL_EXT *MixerControl,
     PULONG NodeId);
 
 MIXER_STATUS
 MMixerSetGetMuteControlDetails(
     IN PMIXER_CONTEXT MixerContext,
     IN LPMIXER_INFO MixerInfo,
-    IN ULONG NodeId,
+    IN LPMIXERCONTROL_EXT MixerControl,
     IN ULONG dwLineID,
     IN LPMIXERCONTROLDETAILS MixerControlDetails,
     IN ULONG bSet);
@@ -288,10 +297,22 @@ MMixerSetGetVolumeControlDetails(
     IN LPMIXER_INFO MixerInfo,
     IN ULONG NodeId,
     IN ULONG bSet,
-    LPMIXERCONTROLW MixerControl,
+    LPMIXERCONTROL_EXT MixerControl,
     IN LPMIXERCONTROLDETAILS MixerControlDetails,
     LPMIXERLINE_EXT MixerLine);
 
+MIXER_STATUS
+MMixerSetGetMuxControlDetails(
+    IN PMIXER_CONTEXT MixerContext,
+    IN LPMIXER_INFO MixerInfo,
+    IN ULONG NodeId,
+    IN ULONG bSet,
+    IN ULONG Flags,
+    LPMIXERCONTROL_EXT MixerControl,
+    IN LPMIXERCONTROLDETAILS MixerControlDetails,
+    LPMIXERLINE_EXT MixerLine);
+
+
 MIXER_STATUS
 MMixerSetGetControlDetails(
     IN PMIXER_CONTEXT MixerContext,
@@ -349,6 +370,26 @@ MMixerInitializePinConnect(
     IN OUT PKSPIN_CONNECT PinConnect,
     IN ULONG PinId);
 
+MIXER_STATUS
+MMixerGetPinDataFlowAndCommunication(
+    IN PMIXER_CONTEXT MixerContext,
+    IN HANDLE hDevice,
+    IN ULONG PinId,
+    OUT PKSPIN_DATAFLOW DataFlow,
+    OUT PKSPIN_COMMUNICATION Communication);
+
+VOID
+MMixerHandleAlternativeMixers(
+    IN PMIXER_CONTEXT MixerContext,
+    IN PMIXER_LIST MixerList,
+    IN LPMIXER_DATA MixerData,
+    IN PTOPOLOGY Topology);
+
+MIXER_STATUS
+MMixerGetMixerByName(
+    IN PMIXER_LIST MixerList,
+    IN LPWSTR MixerName,
+    OUT LPMIXER_INFO *MixerInfo);
 
 /* topology.c */
 
@@ -462,8 +503,25 @@ MMixerIsTopologyNodeReserved(
     IN ULONG NodeIndex,
     OUT PULONG bReserved);
 
+VOID
+MMixerSetTopologyPinReserved(
+    IN PTOPOLOGY Topology,
+    IN ULONG PinId);
+
+VOID
+MMixerIsTopologyPinReserved(
+    IN PTOPOLOGY Topology,
+    IN ULONG PinId,
+    OUT PULONG bReserved);
+
 VOID
 MMixerGetTopologyPinCount(
     IN PTOPOLOGY Topology,
     OUT PULONG PinCount);
 
+VOID
+MMixerGetConnectedFromLogicalTopologyPins(
+    IN PTOPOLOGY Topology,
+    IN ULONG NodeIndex,
+    OUT PULONG OutPinCount,
+    OUT PULONG OutPins);
index 126bcb5..9052e94 100644 (file)
@@ -54,6 +54,63 @@ 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,
@@ -67,6 +124,38 @@ MMixerFreeMixerInfo(
     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,
@@ -100,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
@@ -187,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;
@@ -202,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;
     }
@@ -248,8 +344,8 @@ MMixerNotifyControlChange(
     PLIST_ENTRY Entry;
     PEVENT_NOTIFICATION_ENTRY NotificationEntry;
 
-    /* enumerate list and add a notification entry */
-    Entry = MixerInfo->LineList.Flink;
+    /* enumerate list and perform notification */
+    Entry = MixerInfo->EventList.Flink;
     while(Entry != &MixerInfo->EventList)
     {
         /* get notification entry offset */
@@ -270,7 +366,7 @@ MIXER_STATUS
 MMixerSetGetMuteControlDetails(
     IN PMIXER_CONTEXT MixerContext,
     IN LPMIXER_INFO MixerInfo,
-    IN ULONG NodeId,
+    IN LPMIXERCONTROL_EXT MixerControl,
     IN ULONG dwLineID,
     IN LPMIXERCONTROLDETAILS MixerControlDetails,
     IN ULONG bSet)
@@ -290,7 +386,7 @@ MMixerSetGetMuteControlDetails(
         Value = Input->fValue;
 
     /* set control details */
-    Status = MMixerSetGetControlDetails(MixerContext, MixerInfo->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;
@@ -310,13 +406,262 @@ MMixerSetGetMuteControlDetails(
     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 LPMIXER_INFO MixerInfo,
     IN ULONG NodeId,
     IN ULONG bSet,
-    LPMIXERCONTROLW MixerControl,
+    LPMIXERCONTROL_EXT MixerControl,
     IN LPMIXERCONTROLDETAILS MixerControlDetails,
     LPMIXERLINE_EXT MixerLine)
 {
@@ -329,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;
 
@@ -355,12 +700,12 @@ MMixerSetGetVolumeControlDetails(
     if (bSet)
     {
         /* TODO */
-        Status = MMixerSetGetControlDetails(MixerContext, MixerInfo->hMixer, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, 0, &Value);
-        Status = MMixerSetGetControlDetails(MixerContext, MixerInfo->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, MixerInfo->hMixer, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, Channel, &Value);
+        Status = MMixerSetGetControlDetails(MixerContext, MixerControl->hDevice, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, Channel, &Value);
     }
 
     if (!bSet)
@@ -372,7 +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->dwControlID);
+        MMixerNotifyControlChange(MixerContext, MixerInfo, MM_MIXM_CONTROL_CHANGE, MixerControl->Control.dwControlID);
     }
     return Status;
 }
index 44e85da..c1d4c3d 100644 (file)
@@ -264,7 +264,9 @@ MMixerHandleNodeToNodeConnection(
 {
     PTOPOLOGY_NODE InNode, OutNode;
     PTOPOLOGY_NODE * NewNodes;
+    PULONG NewLogicalPinNodeConnectedFrom;
     ULONG Count;
+    ULONG LogicalPinId;
 
     /* sanity checks */
     ASSERT(Topology->TopologyNodesCount > Connection->ToNode);
@@ -274,6 +276,9 @@ MMixerHandleNodeToNodeConnection(
     InNode = &Topology->TopologyNodes[Connection->FromNode];
     OutNode = &Topology->TopologyNodes[Connection->ToNode];
 
+    /* get logical pin node id */
+    LogicalPinId = Connection->ToNodePin;
+
     /* get existing count */
     Count = OutNode->NodeConnectedFromCount;
 
@@ -286,21 +291,42 @@ MMixerHandleNodeToNodeConnection(
         return MM_STATUS_NO_MEMORY;
     }
 
+    /* allocate logical pin nodes array */
+    NewLogicalPinNodeConnectedFrom = MixerContext->Alloc((Count + 1) * sizeof(ULONG));
+    if (!NewLogicalPinNodeConnectedFrom)
+    {
+        /* out of memory */
+        MixerContext->Free(NewNodes);
+        return MM_STATUS_NO_MEMORY;
+    }
+
     if (Count)
     {
         /* copy existing nodes */
         MixerContext->Copy(NewNodes, OutNode->NodeConnectedFrom, sizeof(PTOPOLOGY) * Count);
 
+        /* copy existing logical pin node array */
+        MixerContext->Copy(NewLogicalPinNodeConnectedFrom, OutNode->LogicalPinNodeConnectedFrom, sizeof(ULONG) * Count);
+
         /* release old nodes array */
         MixerContext->Free(OutNode->NodeConnectedFrom);
+
+        /* release old logical pin node array */
+        MixerContext->Free(OutNode->LogicalPinNodeConnectedFrom);
     }
 
     /* add new topology node */
     NewNodes[OutNode->NodeConnectedFromCount] = InNode;
 
+    /* add logical node id */
+    NewLogicalPinNodeConnectedFrom[OutNode->NodeConnectedFromCount] = LogicalPinId;
+
     /* replace old nodes array */
     OutNode->NodeConnectedFrom = NewNodes;
 
+    /* replace old logical pin node array */
+    OutNode->LogicalPinNodeConnectedFrom = NewLogicalPinNodeConnectedFrom;
+
     /* increment nodes count */
     OutNode->NodeConnectedFromCount++;
 
@@ -745,16 +771,15 @@ MMixerGetUpOrDownstreamNodes(
         /* node should not have been visited */
         ASSERT(Node->Visited == FALSE);
 
+        /* mark node as visited */
+        TopologyNode->Visited = TRUE;
+
         /* add them to node array */
         MMixerAddPinIndexToArray(MixerContext, Node->NodeIndex, Topology->TopologyNodesCount, OutNodeCount, OutNodes);
 
         /* recursively visit them */
         MMixerGetUpOrDownstreamNodes(MixerContext, Topology, TopologyNodes[Index], bUpStream, OutNodeCount, OutNodes);
     }
-
-    /* mark node as visited */
-    TopologyNode->Visited = TRUE;
-
 }
 
 MIXER_STATUS
@@ -1137,6 +1162,32 @@ MMixerIsNodeConnectedToPin(
     return MM_STATUS_SUCCESS;
 }
 
+VOID
+MMixerGetConnectedFromLogicalTopologyPins(
+    IN PTOPOLOGY Topology,
+    IN ULONG NodeIndex,
+    OUT PULONG OutPinCount,
+    OUT PULONG OutPins)
+{
+    ULONG Index;
+    PTOPOLOGY_NODE Node;
+
+    /* sanity check */
+    ASSERT(NodeIndex < Topology->TopologyNodesCount);
+
+    /* get node */
+    Node = &Topology->TopologyNodes[NodeIndex];
+
+    for(Index = 0; Index < Node->NodeConnectedFromCount; Index++)
+    {
+        /* copy logical pin id */
+        OutPins[Index] = Node->LogicalPinNodeConnectedFrom[Index];
+    }
+
+    /* store pin count */
+    *OutPinCount = Node->NodeConnectedFromCount;
+}
+
 LPGUID
 MMixerGetNodeTypeFromTopology(
     IN PTOPOLOGY Topology,
@@ -1148,6 +1199,31 @@ MMixerGetNodeTypeFromTopology(
     return &Topology->TopologyNodes[NodeIndex].NodeType;
 }
 
+VOID
+MMixerSetTopologyPinReserved(
+    IN PTOPOLOGY Topology,
+    IN ULONG PinId)
+{
+    /* sanity check */
+    ASSERT(PinId < Topology->TopologyPinsCount);
+
+    /* set reserved */
+    Topology->TopologyPins[PinId].Reserved = TRUE;
+}
+
+VOID
+MMixerIsTopologyPinReserved(
+    IN PTOPOLOGY Topology,
+    IN ULONG PinId,
+    OUT PULONG bReserved)
+{
+    /* sanity check */
+    ASSERT(PinId < Topology->TopologyPinsCount);
+
+    /* get reserved status */
+    *bReserved = Topology->TopologyPins[PinId].Reserved;
+}
+
 VOID
 MMixerSetTopologyNodeReserved(
     IN PTOPOLOGY Topology,
index c48f03b..1d39f37 100644 (file)
@@ -608,6 +608,16 @@ MMixerSetWaveStatus(
 {
     KSPROPERTY Property;
     ULONG Length;
+    MIXER_STATUS Status;
+
+    /* verify mixer context */
+    Status = MMixerVerifyContext(MixerContext);
+
+    if (Status != MM_STATUS_SUCCESS)
+    {
+        /* invalid context passed */
+        return Status;
+    }
 
     /* setup property request */
     Property.Set = KSPROPSETID_Connection;
@@ -617,6 +627,31 @@ MMixerSetWaveStatus(
     return MixerContext->Control(PinHandle, IOCTL_KS_PROPERTY, &Property, sizeof(KSPROPERTY), &State, sizeof(KSSTATE), &Length);
 }
 
+MIXER_STATUS
+MMixerSetWaveResetState(
+    IN PMIXER_CONTEXT MixerContext,
+    IN HANDLE PinHandle,
+    IN ULONG bBegin)
+{
+    ULONG Length;
+    MIXER_STATUS Status;
+    KSRESET Reset;
+
+    /* verify mixer context */
+    Status = MMixerVerifyContext(MixerContext);
+
+    if (Status != MM_STATUS_SUCCESS)
+    {
+        /* invalid context passed */
+        return Status;
+    }
+
+    /* begin / stop reset */
+    Reset = (bBegin ? KSRESET_BEGIN : KSRESET_END);
+
+    return MixerContext->Control(PinHandle, IOCTL_KS_RESET_STATE, &Reset, sizeof(KSRESET), NULL, 0, &Length);
+}
+
 MIXER_STATUS
 MMixerGetWaveDevicePath(
     IN PMIXER_CONTEXT MixerContext,