[MMIXER]
authorJohannes Anderwald <johannes.anderwald@reactos.org>
Wed, 9 Dec 2009 16:00:28 +0000 (16:00 +0000)
committerJohannes Anderwald <johannes.anderwald@reactos.org>
Wed, 9 Dec 2009 16:00:28 +0000 (16:00 +0000)
- Implement MMixerGetLineInfo, MMixerGetLineControls, MMixerSetControlDetails, MMixerGetControlDetails, MMixerOpen
- MMixer library is now ready for testing

svn path=/trunk/; revision=44482

reactos/lib/drivers/sound/mmixer/controls.c
reactos/lib/drivers/sound/mmixer/filter.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

index 5788a43..62e0ade 100644 (file)
@@ -203,10 +203,10 @@ MMixerAddMixerControl(
         Status = MixerContext->Control(hDevice, IOCTL_KS_PROPERTY, (PVOID)&Node, sizeof(KSP_NODE), (LPVOID)Name, BytesReturned, &BytesReturned);
         if (Status != MM_STATUS_SUCCESS)
         {
-            RtlMoveMemory(MixerControl->szShortName, Name, (min(MIXER_SHORT_NAME_CHARS, wcslen(Name)+1)) * sizeof(WCHAR));
+            MixerContext->Copy(MixerControl->szShortName, Name, (min(MIXER_SHORT_NAME_CHARS, wcslen(Name)+1)) * sizeof(WCHAR));
             MixerControl->szShortName[MIXER_SHORT_NAME_CHARS-1] = L'\0';
 
-            RtlMoveMemory(MixerControl->szName, Name, (min(MIXER_LONG_NAME_CHARS, wcslen(Name)+1)) * sizeof(WCHAR));
+            MixerContext->Copy(MixerControl->szName, Name, (min(MIXER_LONG_NAME_CHARS, wcslen(Name)+1)) * sizeof(WCHAR));
             MixerControl->szName[MIXER_LONG_NAME_CHARS-1] = L'\0';
         }
 
@@ -512,10 +512,10 @@ MMixerAddMixerSourceLine(
 
             if (Status != MM_STATUS_SUCCESS)
             {
-                RtlMoveMemory(SrcLine->Line.szShortName, PinName, (min(MIXER_SHORT_NAME_CHARS, wcslen(PinName)+1)) * sizeof(WCHAR));
+                MixerContext->Copy(SrcLine->Line.szShortName, PinName, (min(MIXER_SHORT_NAME_CHARS, wcslen(PinName)+1)) * sizeof(WCHAR));
                 SrcLine->Line.szShortName[MIXER_SHORT_NAME_CHARS-1] = L'\0';
 
-                RtlMoveMemory(SrcLine->Line.szName, PinName, (min(MIXER_LONG_NAME_CHARS, wcslen(PinName)+1)) * sizeof(WCHAR));
+                MixerContext->Copy(SrcLine->Line.szName, PinName, (min(MIXER_LONG_NAME_CHARS, wcslen(PinName)+1)) * sizeof(WCHAR));
                 SrcLine->Line.szName[MIXER_LONG_NAME_CHARS-1] = L'\0';
             }
             MixerContext->Free(PinName);
index 64e19d1..b576a4c 100644 (file)
@@ -205,3 +205,48 @@ MMixerGetControlTypeFromTopologyNode(
     UNIMPLEMENTED
     return 0;
 }
+
+MIXER_STATUS
+MMixerSetGetControlDetails(
+    IN PMIXER_CONTEXT MixerContext,
+    IN HANDLE hMixer,
+    IN ULONG NodeId,
+    IN ULONG bSet,
+    IN ULONG PropertyId,
+    IN ULONG Channel,
+    IN PLONG InputValue)
+{
+    KSNODEPROPERTY_AUDIO_CHANNEL Property;
+    MIXER_STATUS Status;
+    LONG Value;
+    ULONG BytesReturned;
+
+    if (bSet)
+        Value = *InputValue;
+
+    /* setup the request */
+    RtlZeroMemory(&Property, sizeof(KSNODEPROPERTY_AUDIO_CHANNEL));
+
+    Property.NodeProperty.NodeId = NodeId;
+    Property.NodeProperty.Property.Id = PropertyId;
+    Property.NodeProperty.Property.Flags = KSPROPERTY_TYPE_TOPOLOGY;
+    Property.NodeProperty.Property.Set = KSPROPSETID_Audio;
+    Property.Channel = Channel;
+    Property.Reserved = 0;
+
+    if (bSet)
+        Property.NodeProperty.Property.Flags |= KSPROPERTY_TYPE_SET;
+    else
+        Property.NodeProperty.Property.Flags |= KSPROPERTY_TYPE_GET;
+
+    /* send the request */
+    Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Property, sizeof(KSNODEPROPERTY_AUDIO_CHANNEL), (PVOID)&Value, sizeof(LONG), &BytesReturned);
+
+    if (!bSet && Status == MM_STATUS_SUCCESS)
+    {
+        *InputValue = Value;
+    }
+
+    DPRINT("Status %x bSet %u NodeId %u Value %d PropertyId %u\n", Status, bSet, NodeId, Value, PropertyId);
+    return Status;
+}
index 279551a..303e043 100644 (file)
@@ -73,11 +73,13 @@ MMixerGetCapabilities(
 MIXER_STATUS
 MMixerOpen(
     IN PMIXER_CONTEXT MixerContext,
+    IN ULONG MixerId,
     IN PVOID MixerEvent,
     IN PMIXER_EVENT MixerEventRoutine,
     OUT PHANDLE MixerHandle)
 {
     MIXER_STATUS Status;
+    LPMIXER_INFO MixerInfo;
 
     // verify mixer context
     Status = MMixerVerifyContext(MixerContext);
@@ -88,7 +90,20 @@ MMixerOpen(
         return Status;
     }
 
-    return MM_STATUS_NOT_IMPLEMENTED;
+    MixerInfo = (LPMIXER_INFO)MMixerGetMixerInfoByIndex(MixerContext, MixerId);
+    if (!MixerInfo)
+    {
+        // invalid mixer id
+        return MM_STATUS_INVALID_PARAMETER;
+    }
+
+    // FIXME
+    // handle event notification
+
+    // store result
+    *MixerHandle = (HANDLE)MixerInfo;
+
+    return MM_STATUS_SUCCESS;
 }
 
 MIXER_STATUS
@@ -99,6 +114,8 @@ MMixerGetLineInfo(
     OUT LPMIXERLINEW MixerLine)
 {
     MIXER_STATUS Status;
+    LPMIXER_INFO MixerInfo;
+    LPMIXERLINE_EXT MixerLineSrc;
 
     // verify mixer context
     Status = MMixerVerifyContext(MixerContext);
@@ -108,6 +125,88 @@ MMixerGetLineInfo(
         // invalid context passed
         return Status;
     }
+
+    // clear hmixer from flags
+    Flags &=~MIXER_OBJECTF_HMIXER;
+
+    if (Flags == MIXER_GETLINEINFOF_DESTINATION)
+    {
+        // cast to mixer info
+        MixerInfo = (LPMIXER_INFO)MixerHandle;
+
+        if (MixerLine->dwDestination != 0)
+        {
+            // destination line member must be zero
+            return MM_STATUS_INVALID_PARAMETER;
+        }
+
+        MixerLineSrc = MMixerGetSourceMixerLineByLineId(MixerInfo, DESTINATION_LINE);
+        ASSERT(MixerLineSrc);
+        MixerContext->Copy(MixerLine, &MixerLineSrc->Line, sizeof(MIXERLINEW));
+
+        return MM_STATUS_SUCCESS;
+    }
+    else if (Flags == MIXER_GETLINEINFOF_SOURCE)
+    {
+        // cast to mixer info
+        MixerInfo = (LPMIXER_INFO)MixerHandle;
+
+
+        MixerLineSrc = MMixerGetSourceMixerLineByLineId(MixerInfo, DESTINATION_LINE);
+        ASSERT(MixerLineSrc);
+
+        if (MixerLine->dwSource >= MixerLineSrc->Line.cConnections)
+        {
+            DPRINT1("dwSource %u > Destinations %u\n", MixerLine->dwSource, MixerLineSrc->Line.cConnections);
+
+            // invalid parameter
+            return MM_STATUS_INVALID_PARAMETER;
+        }
+
+        MixerLineSrc = MMixerGetSourceMixerLineByLineId(MixerInfo, MixerLine->dwSource);
+        if (MixerLineSrc)
+        {
+            DPRINT("Line %u Name %S\n", MixerLineSrc->Line.dwSource, MixerLineSrc->Line.szName);
+            MixerContext->Copy(MixerLine, &MixerLineSrc->Line, sizeof(MIXERLINEW));
+            return MM_STATUS_SUCCESS;
+        }
+        return MM_STATUS_UNSUCCESSFUL;
+    }
+    else if (Flags == MIXER_GETLINEINFOF_LINEID)
+    {
+        // cast to mixer info
+        MixerInfo = (LPMIXER_INFO)MixerHandle;
+
+        MixerLineSrc = MMixerGetSourceMixerLineByLineId(MixerInfo, MixerLine->dwLineID);
+        if (!MixerLineSrc)
+        {
+            // invalid parameter
+            return MM_STATUS_INVALID_PARAMETER;
+        }
+
+        /* copy cached data */
+        MixerContext->Copy(MixerLine, &MixerLineSrc->Line, sizeof(MIXERLINEW));
+        return MM_STATUS_SUCCESS;
+    }
+    else if (Flags == MIXER_GETLINEINFOF_COMPONENTTYPE)
+    {
+        // cast to mixer info
+        MixerInfo = (LPMIXER_INFO)MixerHandle;
+
+        MixerLineSrc = MMixerGetSourceMixerLineByComponentType(MixerInfo, MixerLine->dwComponentType);
+        if (!MixerLineSrc)
+        {
+            DPRINT1("Failed to find component type %x\n", MixerLine->dwComponentType);
+            return MM_STATUS_UNSUCCESSFUL;
+        }
+
+        ASSERT(MixerLineSrc);
+
+        /* copy cached data */
+        MixerContext->Copy(MixerLine, &MixerLineSrc->Line, sizeof(MIXERLINEW));
+        return MM_STATUS_SUCCESS;
+    }
+
     return MM_STATUS_NOT_IMPLEMENTED;
 }
 
@@ -118,7 +217,11 @@ MMixerGetLineControls(
     IN ULONG Flags,
     OUT LPMIXERLINECONTROLS MixerLineControls)
 {
+    LPMIXER_INFO MixerInfo;
+    LPMIXERLINE_EXT MixerLineSrc;
+    LPMIXERCONTROLW MixerControl;
     MIXER_STATUS Status;
+    ULONG Index;
 
     // verify mixer context
     Status = MMixerVerifyContext(MixerContext);
@@ -129,6 +232,73 @@ MMixerGetLineControls(
         return Status;
     }
 
+    Flags &= ~MIXER_OBJECTF_HMIXER;
+
+    if (Flags == MIXER_GETLINECONTROLSF_ALL)
+    {
+        // cast to mixer info
+        MixerInfo = (LPMIXER_INFO)MixerHandle;
+
+        MixerLineSrc = MMixerGetSourceMixerLineByLineId(MixerInfo, MixerLineControls->dwLineID);
+
+        if (!MixerLineSrc)
+        {
+            // invalid line id
+            return MM_STATUS_INVALID_PARAMETER;
+        }
+        // copy line control(s)
+        MixerContext->Copy(MixerLineControls->pamxctrl, MixerLineSrc->LineControls, min(MixerLineSrc->Line.cControls, MixerLineControls->cControls) * sizeof(MIXERCONTROLW));
+
+        return MM_STATUS_SUCCESS;
+    }
+    else if (Flags == MIXER_GETLINECONTROLSF_ONEBYTYPE)
+    {
+        // cast to mixer info
+        MixerInfo = (LPMIXER_INFO)MixerHandle;
+
+        MixerLineSrc = MMixerGetSourceMixerLineByLineId(MixerInfo, MixerLineControls->dwLineID);
+
+        if (!MixerLineSrc)
+        {
+            // invalid line id
+            return MM_STATUS_INVALID_PARAMETER;
+        }
+
+        ASSERT(MixerLineSrc);
+
+        Index = 0;
+        for(Index = 0; Index < MixerLineSrc->Line.cControls; Index++)
+        {
+            DPRINT("dwControlType %x\n", MixerLineSrc->LineControls[Index].dwControlType);
+            if (MixerLineControls->dwControlType == MixerLineSrc->LineControls[Index].dwControlType)
+            {
+                // found a control with that type
+                MixerContext->Copy(MixerLineControls->pamxctrl, &MixerLineSrc->LineControls[Index], 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;
+    }
+    else if (Flags == MIXER_GETLINECONTROLSF_ONEBYID)
+    {
+        // cast to mixer info
+        MixerInfo = (LPMIXER_INFO)MixerHandle;
+
+        Status = MMixerGetMixerControlById(MixerInfo, MixerLineControls->dwControlID, NULL, &MixerControl, NULL);
+
+        if (Status != MM_STATUS_SUCCESS)
+        {
+            // invalid parameter
+            return MM_STATUS_INVALID_PARAMETER;
+        }
+
+        // copy the controls
+        MixerContext->Copy(MixerLineControls->pamxctrl, MixerControl, sizeof(MIXERCONTROLW));
+        return MM_STATUS_SUCCESS;
+    }
+
+
     return MM_STATUS_NOT_IMPLEMENTED;
 }
 
@@ -140,6 +310,10 @@ MMixerSetControlDetails(
     OUT LPMIXERCONTROLDETAILS MixerControlDetails)
 {
     MIXER_STATUS Status;
+    ULONG NodeId;
+    LPMIXER_INFO MixerInfo;
+    LPMIXERLINE_EXT MixerLine;
+    LPMIXERCONTROLW MixerControl;
 
     // verify mixer context
     Status = MMixerVerifyContext(MixerContext);
@@ -149,7 +323,33 @@ MMixerSetControlDetails(
         // invalid context passed
         return Status;
     }
-    return MM_STATUS_NOT_IMPLEMENTED;
+
+    // get mixer info
+    MixerInfo = (LPMIXER_INFO)MixerHandle;
+
+    // get mixer control
+     Status = MMixerGetMixerControlById(MixerInfo, MixerControlDetails->dwControlID, &MixerLine, &MixerControl, &NodeId);
+
+    // check for success
+    if (Status != MM_STATUS_SUCCESS)
+    {
+        // failed to find control id
+        return MM_STATUS_INVALID_PARAMETER;
+    }
+
+    switch(MixerControl->dwControlType)
+    {
+        case MIXERCONTROL_CONTROLTYPE_MUTE:
+            Status = MMixerSetGetMuteControlDetails(MixerContext, MixerInfo->hMixer, NodeId, MixerLine->Line.dwLineID, MixerControlDetails, TRUE);
+            break;
+        case MIXERCONTROL_CONTROLTYPE_VOLUME:
+            Status = MMixerSetGetVolumeControlDetails(MixerContext, MixerInfo->hMixer, NodeId, TRUE, MixerControl, MixerControlDetails, MixerLine);
+            break;
+        default:
+            Status = MM_STATUS_NOT_IMPLEMENTED;
+    }
+
+    return Status;
 }
 
 MIXER_STATUS
@@ -160,6 +360,10 @@ MMixerGetControlDetails(
     OUT LPMIXERCONTROLDETAILS MixerControlDetails)
 {
     MIXER_STATUS Status;
+    ULONG NodeId;
+    LPMIXER_INFO MixerInfo;
+    LPMIXERLINE_EXT MixerLine;
+    LPMIXERCONTROLW MixerControl;
 
     // verify mixer context
     Status = MMixerVerifyContext(MixerContext);
@@ -170,7 +374,32 @@ MMixerGetControlDetails(
         return Status;
     }
 
-    return MM_STATUS_NOT_IMPLEMENTED;
+    // get mixer info
+    MixerInfo = (LPMIXER_INFO)MixerHandle;
+
+    // get mixer control
+     Status = MMixerGetMixerControlById(MixerInfo, MixerControlDetails->dwControlID, &MixerLine, &MixerControl, &NodeId);
+
+    // check for success
+    if (Status != MM_STATUS_SUCCESS)
+    {
+        // failed to find control id
+        return MM_STATUS_INVALID_PARAMETER;
+    }
+
+    switch(MixerControl->dwControlType)
+    {
+        case MIXERCONTROL_CONTROLTYPE_MUTE:
+            Status = MMixerSetGetMuteControlDetails(MixerContext, MixerInfo->hMixer, NodeId, MixerLine->Line.dwLineID, MixerControlDetails, FALSE);
+            break;
+        case MIXERCONTROL_CONTROLTYPE_VOLUME:
+            Status = MMixerSetGetVolumeControlDetails(MixerContext, MixerInfo->hMixer, NodeId, FALSE, MixerControl, MixerControlDetails, MixerLine);
+            break;
+        default:
+            Status = MM_STATUS_NOT_IMPLEMENTED;
+    }
+
+    return Status;
 }
 
 MIXER_STATUS
index 3f94eaf..51d9772 100644 (file)
@@ -47,6 +47,11 @@ typedef MIXER_STATUS(*PMIXER_CLOSE)(
 typedef VOID (*PMIXER_EVENT)(
     IN PVOID MixerEvent);
 
+typedef VOID (*PMIXER_COPY)(
+    IN PVOID Dst,
+    IN PVOID Src,
+    IN ULONG Length);
+
 
 typedef struct
 {
@@ -58,6 +63,7 @@ typedef struct
      PMIXER_FREE  Free;
      PMIXER_OPEN Open;
      PMIXER_CLOSE Close;
+     PMIXER_COPY Copy;
 }MIXER_CONTEXT, *PMIXER_CONTEXT;
 
 MIXER_STATUS
@@ -79,6 +85,7 @@ MMixerGetCapabilities(
 MIXER_STATUS
 MMixerOpen(
     IN PMIXER_CONTEXT MixerContext,
+    IN ULONG MixerId,
     IN PVOID MixerEvent,
     IN PMIXER_EVENT MixerEventRoutine,
     OUT PHANDLE MixerHandle);
index 430f509..3c6e086 100644 (file)
@@ -164,5 +164,46 @@ MMixerGetMixerInfoByIndex(
     IN PMIXER_CONTEXT MixerContext,
     IN ULONG MixerIndex);
 
+LPMIXERLINE_EXT
+MMixerGetSourceMixerLineByComponentType(
+    LPMIXER_INFO MixerInfo,
+    DWORD dwComponentType);
+
+MIXER_STATUS
+MMixerGetMixerControlById(
+    LPMIXER_INFO MixerInfo,
+    DWORD dwControlID,
+    LPMIXERLINE_EXT *MixerLine,
+    LPMIXERCONTROLW *MixerControl,
+    PULONG NodeId);
+
+MIXER_STATUS
+MMixerSetGetMuteControlDetails(
+    IN PMIXER_CONTEXT MixerContext,
+    IN HANDLE hMixer,
+    IN ULONG NodeId,
+    IN ULONG dwLineID,
+    IN LPMIXERCONTROLDETAILS MixerControlDetails,
+    IN ULONG bSet);
+
+MIXER_STATUS
+MMixerSetGetVolumeControlDetails(
+    IN PMIXER_CONTEXT MixerContext,
+    IN HANDLE hMixer,
+    IN ULONG NodeId,
+    IN ULONG bSet,
+    LPMIXERCONTROLW MixerControl,
+    IN LPMIXERCONTROLDETAILS MixerControlDetails,
+    LPMIXERLINE_EXT MixerLine);
+
+MIXER_STATUS
+MMixerSetGetControlDetails(
+    IN PMIXER_CONTEXT MixerContext,
+    IN HANDLE hMixer,
+    IN ULONG NodeId,
+    IN ULONG bSet,
+    IN ULONG PropertyId,
+    IN ULONG Channel,
+    IN PLONG InputValue);
 
 #endif
index b1cd58d..e2e0d84 100644 (file)
@@ -71,6 +71,28 @@ MMixerGetMixerInfoByIndex(
     return NULL;
 }
 
+LPMIXERCONTROL_DATA
+MMixerGetMixerControlDataById(
+    PLIST_ENTRY ListHead,
+    DWORD dwControlId)
+{
+    PLIST_ENTRY Entry;
+    LPMIXERCONTROL_DATA Control;
+
+    /* get first entry */
+    Entry = ListHead->Flink;
+
+    while(Entry != ListHead)
+    {
+        Control = (LPMIXERCONTROL_DATA)CONTAINING_RECORD(Entry, MIXERCONTROL_DATA, Entry);
+        DPRINT("dwSource %x dwSource %x\n", Control->dwControlID, dwControlId);
+        if (Control->dwControlID == dwControlId)
+            return Control;
+
+        Entry = Entry->Flink;
+    }
+    return NULL;
+}
 
 LPMIXERLINE_EXT
 MMixerGetSourceMixerLineByLineId(
@@ -314,3 +336,190 @@ MMixerGetTargetPins(
 
     return Status;
 }
+
+LPMIXERLINE_EXT
+MMixerGetSourceMixerLineByComponentType(
+    LPMIXER_INFO MixerInfo,
+    DWORD dwComponentType)
+{
+    PLIST_ENTRY Entry;
+    LPMIXERLINE_EXT MixerLineSrc;
+
+    /* get first entry */
+    Entry = MixerInfo->LineList.Flink;
+
+    while(Entry != &MixerInfo->LineList)
+    {
+        MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry);
+        if (MixerLineSrc->Line.dwComponentType == dwComponentType)
+            return MixerLineSrc;
+
+        Entry = Entry->Flink;
+    }
+
+    return NULL;
+}
+
+MIXER_STATUS
+MMixerGetMixerControlById(
+    LPMIXER_INFO MixerInfo,
+    DWORD dwControlID,
+    LPMIXERLINE_EXT *MixerLine,
+    LPMIXERCONTROLW *MixerControl,
+    PULONG NodeId)
+{
+    PLIST_ENTRY Entry;
+    LPMIXERLINE_EXT MixerLineSrc;
+    ULONG Index;
+
+    /* get first entry */
+    Entry = MixerInfo->LineList.Flink;
+
+    while(Entry != &MixerInfo->LineList)
+    {
+        MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry);
+
+        for(Index = 0; Index < MixerLineSrc->Line.cControls; Index++)
+        {
+            if (MixerLineSrc->LineControls[Index].dwControlID == dwControlID)
+            {
+                if (MixerLine)
+                    *MixerLine = MixerLineSrc;
+                if (MixerControl)
+                    *MixerControl = &MixerLineSrc->LineControls[Index];
+                if (NodeId)
+                    *NodeId = MixerLineSrc->NodeIds[Index];
+                return MM_STATUS_SUCCESS;
+            }
+        }
+        Entry = Entry->Flink;
+    }
+
+    return MM_STATUS_UNSUCCESSFUL;
+}
+
+ULONG
+MMixerGetVolumeControlIndex(
+    LPMIXERVOLUME_DATA VolumeData,
+    LONG Value)
+{
+    ULONG Index;
+
+    for(Index = 0; Index < VolumeData->ValuesCount; Index++)
+    {
+        if (VolumeData->Values[Index] > Value)
+        {
+            return VolumeData->InputSteppingDelta * Index;
+        }
+    }
+    return VolumeData->InputSteppingDelta * (VolumeData->ValuesCount-1);
+}
+
+MIXER_STATUS
+MMixerSetGetMuteControlDetails(
+    IN PMIXER_CONTEXT MixerContext,
+    IN HANDLE hMixer,
+    IN ULONG NodeId,
+    IN ULONG dwLineID,
+    IN LPMIXERCONTROLDETAILS MixerControlDetails,
+    IN ULONG bSet)
+{
+    LPMIXERCONTROLDETAILS_BOOLEAN Input;
+    LONG Value;
+    MIXER_STATUS Status;
+
+    if (MixerControlDetails->cbDetails != sizeof(MIXERCONTROLDETAILS_BOOLEAN))
+        return MM_STATUS_INVALID_PARAMETER;
+
+    /* get input */
+    Input = (LPMIXERCONTROLDETAILS_BOOLEAN)MixerControlDetails->paDetails;
+
+    /* FIXME SEH */
+    if (bSet)
+        Value = Input->fValue;
+
+    /* set control details */
+    Status = MMixerSetGetControlDetails(MixerContext, hMixer, NodeId, bSet, KSPROPERTY_AUDIO_MUTE, 0, &Value);
+
+    if (Status != MM_STATUS_SUCCESS)
+        return Status;
+
+    /* FIXME SEH */
+    if (!bSet)
+    {
+        Input->fValue = Value;
+        return Status;
+    }
+    else
+    {
+        // FIXME notify wdmaud clients MM_MIXM_LINE_CHANGE dwLineID
+    }
+
+    return Status;
+}
+
+MIXER_STATUS
+MMixerSetGetVolumeControlDetails(
+    IN PMIXER_CONTEXT MixerContext,
+    IN HANDLE hMixer,
+    IN ULONG NodeId,
+    IN ULONG bSet,
+    LPMIXERCONTROLW MixerControl,
+    IN LPMIXERCONTROLDETAILS MixerControlDetails,
+    LPMIXERLINE_EXT MixerLine)
+{
+    LPMIXERCONTROLDETAILS_UNSIGNED Input;
+    LONG Value, Index, Channel = 0;
+    ULONG dwValue;
+    MIXER_STATUS Status;
+    LPMIXERVOLUME_DATA VolumeData;
+
+    if (MixerControlDetails->cbDetails != sizeof(MIXERCONTROLDETAILS_SIGNED))
+        return MM_STATUS_INVALID_PARAMETER;
+
+    VolumeData = (LPMIXERVOLUME_DATA)MMixerGetMixerControlDataById(&MixerLine->LineControlsExtraData, MixerControl->dwControlID);
+    if (!VolumeData)
+        return MM_STATUS_UNSUCCESSFUL;
+
+    /* get input */
+    Input = (LPMIXERCONTROLDETAILS_UNSIGNED)MixerControlDetails->paDetails;
+
+    if (bSet)
+    {
+        /* FIXME SEH */
+        Value = Input->dwValue;
+        Index = Value / VolumeData->InputSteppingDelta;
+
+        if (Index >= VolumeData->ValuesCount)
+        {
+            DPRINT1("Index %u out of bounds %u \n", Index, VolumeData->ValuesCount);
+            DbgBreakPoint();
+            return MM_STATUS_INVALID_PARAMETER;
+        }
+
+        Value = VolumeData->Values[Index];
+    }
+
+    /* set control details */
+    if (bSet)
+    {
+        Status = MMixerSetGetControlDetails(MixerContext, hMixer, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, 0, &Value);
+        Status = MMixerSetGetControlDetails(MixerContext, hMixer, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, 1, &Value);
+    }
+    else
+    {
+        Status = MMixerSetGetControlDetails(MixerContext, hMixer, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, Channel, &Value);
+    }
+
+    if (!bSet)
+    {
+        dwValue = MMixerGetVolumeControlIndex(VolumeData, (LONG)Value);
+        /* FIXME SEH */
+        Input->dwValue = dwValue;
+    }
+    else
+    {
+        /* notify clients of a line change  MM_MIXM_CONTROL_CHANGE with MixerControl->dwControlID */
+    }
+    return Status;
+}